diff options
Diffstat (limited to 'resources/libraries/python')
-rw-r--r-- | resources/libraries/python/L2Util.py | 2 | ||||
-rw-r--r-- | resources/libraries/python/QemuUtils.py | 7 | ||||
-rw-r--r-- | resources/libraries/python/ssh.py | 62 |
3 files changed, 57 insertions, 14 deletions
diff --git a/resources/libraries/python/L2Util.py b/resources/libraries/python/L2Util.py index 97cef611c5..52d138552b 100644 --- a/resources/libraries/python/L2Util.py +++ b/resources/libraries/python/L2Util.py @@ -283,7 +283,7 @@ class L2Util(object): :param br_name: Bridge name. :param set_down: Change bridge interface state to down before delbr command. Optional. Default: True. - :type node: str + :type node: dict :type br_name: str :type set_down: bool """ diff --git a/resources/libraries/python/QemuUtils.py b/resources/libraries/python/QemuUtils.py index ff3a00f730..037d0de58c 100644 --- a/resources/libraries/python/QemuUtils.py +++ b/resources/libraries/python/QemuUtils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018 Cisco and/or its affiliates. +# Copyright (c) 2019 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: @@ -213,7 +213,10 @@ class QemuUtils(object): self._ssh = SSH() self._ssh.connect(node) self._vm_info['host'] = node['host'] - + if node['port'] != 22: + self._vm_info['host_port'] = node['port'] + self._vm_info['host_username'] = node['username'] + self._vm_info['host_password'] = node['password'] arch = Topology.get_node_arch(node) self._qemu_opt['qemu_bin'] = 'qemu-system-{arch}'.format(arch=arch) diff --git a/resources/libraries/python/ssh.py b/resources/libraries/python/ssh.py index 108c1e4e1e..4beec5aa83 100644 --- a/resources/libraries/python/ssh.py +++ b/resources/libraries/python/ssh.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018 Cisco and/or its affiliates. +# Copyright (c) 2019 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: @@ -13,15 +13,15 @@ """Library for SSH connection management.""" +import paramiko +import socket import StringIO -from time import time, sleep -import socket -import paramiko from paramiko import RSAKey -from paramiko.ssh_exception import SSHException -from scp import SCPClient +from paramiko.ssh_exception import SSHException, NoValidConnectionsError from robot.api import logger +from scp import SCPClient +from time import time, sleep __all__ = ["exec_cmd", "exec_cmd_no_error"] @@ -104,6 +104,10 @@ class SSH(object): except SSHException: raise IOError('Cannot connect to {host}'. format(host=node['host'])) + except NoValidConnectionsError as err: + logger.error(repr(err)) + raise IOError('Unable to connect to port {port} on {host}'. + format(port=node['port'], host=node['host'])) def disconnect(self, node): """Close SSH connection to the node. @@ -320,7 +324,7 @@ class SSH(object): def interactive_terminal_close(chan): """Close interactive terminal SSH channel. - :param: chan: SSH channel to be closed. + :param chan: SSH channel to be closed. """ chan.close() @@ -371,6 +375,17 @@ def exec_cmd(node, cmd, timeout=600, sudo=False): """Convenience function to ssh/exec/return rc, out & err. Returns (rc, stdout, stderr). + + :param node: The node to execute command on. + :param cmd: Command to execute. + :param timeout: Timeout value in seconds. Default: 600. + :param sudo: Sudo privilege execution flag. Default: False. + :type node: dict + :type cmd: str + :type timeout: int + :type sudo: bool + :returns: RC, Stdout, Stderr. + :rtype: tuple(int, str, str) """ if node is None: raise TypeError('Node parameter is None') @@ -380,10 +395,35 @@ def exec_cmd(node, cmd, timeout=600, sudo=False): raise ValueError('Empty command parameter') ssh = SSH() + + if node.get('host_port') is not None: + ssh_node = dict() + ssh_node['host'] = '127.0.0.1' + ssh_node['port'] = node['port'] + ssh_node['username'] = node['username'] + ssh_node['password'] = node['password'] + import pexpect + options = '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' + tnl = '-L {port}:127.0.0.1:{port}'.format(port=node['port']) + ssh_cmd = 'ssh {tnl} {op} {user}@{host} -p {host_port}'.\ + format(tnl=tnl, op=options, user=node['host_username'], + host=node['host'], host_port=node['host_port']) + logger.trace('Initializing local port forwarding:\n{ssh_cmd}'. + format(ssh_cmd=ssh_cmd)) + child = pexpect.spawn(ssh_cmd) + child.expect('.* password: ') + logger.trace(child.after) + child.sendline(node['host_password']) + child.expect('Welcome .*') + logger.trace(child.after) + logger.trace('Local port forwarding finished.') + else: + ssh_node = node + try: - ssh.connect(node) + ssh.connect(ssh_node) except SSHException as err: - logger.error("Failed to connect to node" + str(err)) + logger.error("Failed to connect to node" + repr(err)) return None, None, None try: @@ -393,7 +433,7 @@ def exec_cmd(node, cmd, timeout=600, sudo=False): (ret_code, stdout, stderr) = ssh.exec_command_sudo(cmd, timeout=timeout) except SSHException as err: - logger.error(err) + logger.error(repr(err)) return None, None, None return ret_code, stdout, stderr @@ -416,7 +456,7 @@ def exec_cmd_no_error(node, cmd, timeout=600, sudo=False, message=None): :type message: str :returns: Stdout, Stderr. :rtype: tuple(str, str) - :raise RuntimeError: If bash return code is not 0. + :raises RuntimeError: If bash return code is not 0. """ ret_code, stdout, stderr = exec_cmd(node, cmd, timeout=timeout, sudo=sudo) msg = ('Command execution failed: "{cmd}"\n{stderr}'. |