X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2Fssh.py;h=fe4404b053c6b553aea0eb1a65da30e374592c52;hb=refs%2Fchanges%2F87%2F12187%2F7;hp=87fc02d3dd763d679b213f385f168a3ae6d824e2;hpb=4d2e646f21659a6540312779190b03aed91395d5;p=csit.git diff --git a/resources/libraries/python/ssh.py b/resources/libraries/python/ssh.py index 87fc02d3dd..fe4404b053 100644 --- a/resources/libraries/python/ssh.py +++ b/resources/libraries/python/ssh.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2018 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: @@ -50,7 +50,7 @@ class SSH(object): :param node: Node in topology. :type node: dict - :return: IP address and port for the specified node. + :returns: IP address and port for the specified node. :rtype: int """ @@ -92,11 +92,11 @@ class SSH(object): format(self._ssh.get_transport().getpeername())) logger.debug('Connections: {0}'. format(str(SSH.__existing_connections))) - except: + except RuntimeError as exc: if attempts > 0: self._reconnect(attempts-1) else: - raise + raise exc def disconnect(self, node): """Close SSH connection to the node. @@ -132,21 +132,25 @@ class SSH(object): :rtype: tuple(int, str, str) :raise SSHTimeout: If command is not finished in timeout time. """ - start = time() stdout = StringIO.StringIO() stderr = StringIO.StringIO() try: chan = self._ssh.get_transport().open_session(timeout=5) + peer = self._ssh.get_transport().getpeername() except AttributeError: self._reconnect() chan = self._ssh.get_transport().open_session(timeout=5) + peer = self._ssh.get_transport().getpeername() except SSHException: self._reconnect() chan = self._ssh.get_transport().open_session(timeout=5) + peer = self._ssh.get_transport().getpeername() chan.settimeout(timeout) - logger.trace('exec_command on {0}: {1}' - .format(self._ssh.get_transport().getpeername(), cmd)) + logger.trace('exec_command on {peer} with timeout {timeout}: {cmd}' + .format(peer=peer, timeout=timeout, cmd=cmd)) + + start = time() chan.exec_command(cmd) while not chan.exit_status_ready() and timeout is not None: if chan.recv_ready(): @@ -157,10 +161,11 @@ class SSH(object): if time() - start > timeout: raise SSHTimeout( - 'Timeout exception during execution of command: {0}\n' - 'Current contents of stdout buffer: {1}\n' - 'Current contents of stderr buffer: {2}\n' - .format(cmd, stdout.getvalue(), stderr.getvalue()) + 'Timeout exception during execution of command: {cmd}\n' + 'Current contents of stdout buffer: {stdout}\n' + 'Current contents of stderr buffer: {stderr}\n' + .format(cmd=cmd, stdout=stdout.getvalue(), + stderr=stderr.getvalue()) ) sleep(0.1) @@ -173,14 +178,12 @@ class SSH(object): stderr.write(chan.recv_stderr(self.__MAX_RECV_BUF)) end = time() - logger.trace('exec_command on {0} took {1} seconds'.format( - self._ssh.get_transport().getpeername(), end-start)) - - logger.trace('chan_recv/_stderr took {} seconds'.format(time()-end)) + logger.trace('exec_command on {peer} took {total} seconds'. + format(peer=peer, total=end-start)) - logger.trace('return RC {}'.format(return_code)) - logger.trace('return STDOUT {}'.format(stdout.getvalue())) - logger.trace('return STDERR {}'.format(stderr.getvalue())) + logger.trace('return RC {rc}'.format(rc=return_code)) + logger.trace('return STDOUT {stdout}'.format(stdout=stdout.getvalue())) + logger.trace('return STDERR {stderr}'.format(stderr=stderr.getvalue())) return return_code, stdout.getvalue(), stderr.getvalue() def exec_command_sudo(self, cmd, cmd_input=None, timeout=30): @@ -189,7 +192,7 @@ class SSH(object): :param cmd: Command to be executed. :param cmd_input: Input redirected to the command. :param timeout: Timeout. - :return: return_code, stdout, stderr + :returns: return_code, stdout, stderr :Example: @@ -221,7 +224,7 @@ class SSH(object): :type lxc_params: str :type sudo: bool :type timeout: int - :return: return_code, stdout, stderr + :returns: return_code, stdout, stderr """ command = "lxc-attach {p} --name {n} -- /bin/sh -c '{c}'"\ .format(p=lxc_params, n=lxc_name, c=lxc_cmd) @@ -233,8 +236,8 @@ class SSH(object): def interactive_terminal_open(self, time_out=30): """Open interactive terminal on a new channel on the connected Node. - :param time_out: Timeout in seconds. - :return: SSH channel with opened terminal. + FIXME: Convert or document other possible exceptions, such as + socket.error or SSHException. .. warning:: Interruptingcow is used here, and it uses signal(SIGALRM) to let the operating system interrupt program @@ -242,6 +245,10 @@ class SSH(object): handlers only apply to the main thread, so you cannot use this from other threads. You must not use this in a program that uses SIGALRM itself (this includes certain profilers) + + :param time_out: Timeout in seconds. + :returns: SSH channel with opened terminal. + :raise IOError: If receive attempt results in socket.timeout. """ chan = self._ssh.get_transport().open_session() chan.get_pty() @@ -250,7 +257,7 @@ class SSH(object): chan.set_combine_stderr(True) buf = '' - while not buf.endswith((":~$ ", "~]$ ")): + while not buf.endswith((":~$ ", "~]$ ", "~]# ")): try: chunk = chan.recv(self.__MAX_RECV_BUF) if not chunk: @@ -261,7 +268,8 @@ class SSH(object): break except socket.timeout: logger.error('Socket timeout: {0}'.format(buf)) - raise Exception('Socket timeout: {0}'.format(buf)) + # TODO: Find out which exception would callers appreciate here. + raise IOError('Socket timeout: {0}'.format(buf)) return chan def interactive_terminal_exec_command(self, chan, cmd, prompt): @@ -269,18 +277,19 @@ class SSH(object): interactive_terminal_open() method has to be called first! - :param chan: SSH channel with opened terminal. - :param cmd: Command to be executed. - :param prompt: Command prompt, sequence of characters used to - indicate readiness to accept commands. - :return: Command output. - .. warning:: Interruptingcow is used here, and it uses signal(SIGALRM) to let the operating system interrupt program execution. This has the following limitations: Python signal handlers only apply to the main thread, so you cannot use this from other threads. You must not use this in a program that uses SIGALRM itself (this includes certain profilers) + + :param chan: SSH channel with opened terminal. + :param cmd: Command to be executed. + :param prompt: Command prompt, sequence of characters used to + indicate readiness to accept commands. + :returns: Command output. + :raise IOError: If receive attempt results in socket.timeout. """ chan.sendall('{c}\n'.format(c=cmd)) buf = '' @@ -296,8 +305,9 @@ class SSH(object): except socket.timeout: logger.error('Socket timeout during execution of command: ' '{0}\nBuffer content:\n{1}'.format(cmd, buf)) - raise Exception('Socket timeout during execution of command: ' - '{0}\nBuffer content:\n{1}'.format(cmd, buf)) + # TODO: Find out which exception would callers appreciate here. + raise IOError('Socket timeout during execution of command: ' + '{0}\nBuffer content:\n{1}'.format(cmd, buf)) tmp = buf.replace(cmd.replace('\n', ''), '') for item in prompt: tmp.replace(item, '') @@ -350,6 +360,7 @@ class SSH(object): def exec_cmd(node, cmd, timeout=600, sudo=False): """Convenience function to ssh/exec/return rc, out & err. + FIXME: Document :param, :type, :raise and similar. Returns (rc, stdout, stderr). """ if node is None: