From 85a341d645b57b7cd88a26ed2ea0a314704240ea Mon Sep 17 00:00:00 2001 From: Jordan Augé Date: Fri, 24 Feb 2017 14:58:01 +0100 Subject: Initial commit: vICN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I7ce66c4e84a6a1921c63442f858b49e083adc7a7 Signed-off-by: Jordan Augé --- vicn/resource/linux/physical.py | 144 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 vicn/resource/linux/physical.py (limited to 'vicn/resource/linux/physical.py') diff --git a/vicn/resource/linux/physical.py b/vicn/resource/linux/physical.py new file mode 100644 index 00000000..e5eba2d3 --- /dev/null +++ b/vicn/resource/linux/physical.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import stat +import logging +import subprocess +import shlex + +from netmodel.model.type import String, Integer +from netmodel.util.misc import is_local_host +from netmodel.util.socket import check_port +from vicn.core.attribute import Attribute +from vicn.core.commands import Command, ReturnValue +from vicn.core.exception import ResourceNotFound +from vicn.core.task import Task, task +from vicn.resource.node import Node, DEFAULT_USERNAME +from vicn.resource.node import DEFAULT_SSH_PUBLIC_KEY +from vicn.resource.node import DEFAULT_SSH_PRIVATE_KEY + +log = logging.getLogger(__name__) + +CMD_SSH_COPY_ID = 'ssh-copy-id {ssh_options} -i {public_key} -p {port} ' \ + '{user}@{host}' +CMD_SSH = 'ssh {ssh_options} -i {private_key} -p {port} ' \ + '{user}@{host} {command}' +CMD_SSH_NF = 'ssh -n -f {ssh_options} -i {private_key} -p {port} ' \ + '{user}@{host} {command}' + +class Physical(Node): + """ + Resource: Physical + + Physical node + """ + + hostname = Attribute(String, description = 'Hostname or IP address', + mandatory = True) + ssh_port = Attribute(Integer, description = 'SSH port number', + default = 22) + + #-------------------------------------------------------------------------- + # Constructor and Accessors + #-------------------------------------------------------------------------- + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.node_with_kernel = self + + #-------------------------------------------------------------------------- + # Resource lifecycle + #-------------------------------------------------------------------------- + + @task + def __get__(self, attributes=None): + if not check_port(self.hostname, self.ssh_port): + raise ResourceNotFound + + def __create__(self): + tasks = list() + modes = (True, False) if DEFAULT_USERNAME != 'root' else (True,) + for as_root in modes: + tasks.append(self._setup_ssh_key(as_root)) + return Task.__concurrent__(*tasks) + + __delete__ = None + + #-------------------------------------------------------------------------- + # Internal methods + #-------------------------------------------------------------------------- + + @task + def _setup_ssh_key(self, as_root): + os.chmod(DEFAULT_SSH_PUBLIC_KEY, stat.S_IRUSR | stat.S_IWUSR) + os.chmod(DEFAULT_SSH_PRIVATE_KEY, stat.S_IRUSR | stat.S_IWUSR) + cmd_params = { + 'public_key' : DEFAULT_SSH_PUBLIC_KEY, + 'ssh_options' : '', + 'port' : self.ssh_port, + 'user' : 'root' if as_root else DEFAULT_USERNAME, + 'host' : self.hostname, + } + + c = Command(CMD_SSH_COPY_ID, parameters = cmd_params) + + return self._do_execute_process(c.full_commandline, output=False) + + #-------------------------------------------------------------------------- + # Public API + #-------------------------------------------------------------------------- + + def _do_execute_process(self, command, output = False): + p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE) + if output: + out, err = p.communicate() + return ReturnValue(p.returncode, stdout=out, stderr=err) + + p.wait() + return ReturnValue(p.returncode) + + def _do_execute_ssh(self, command, output=False, as_root=False, + ssh_options=None): + if not ssh_options: + ssh_options = dict() + cmd_params = { + 'private_key' : DEFAULT_SSH_PRIVATE_KEY, + 'ssh_options' : ' '.join(['-o {}={}'.format(k, v) + for k, v in ssh_options.items()]), + 'port' : self.ssh_port, + 'user' : 'root' if as_root else DEFAULT_USERNAME, + 'host' : self.hostname, + 'command' : shlex.quote(command), + } + + if (command[-1] != '&'): + c = Command(CMD_SSH, parameters = cmd_params) + else: + c = Command(CMD_SSH_NF, parameters = cmd_params) + + return self._do_execute_process(c.full_commandline_nobashize, output) + + def execute(self, command, output=False, as_root=False): + if is_local_host(self.hostname): + rv = self._do_execute_process(command, output = output) + else: + rv = self._do_execute_ssh(command, output = output, + as_root = as_root) + return rv -- cgit 1.2.3-korg