Repository: libcloud Updated Branches: refs/heads/trunk 4ffa70380 -> 1ad8e0396
Add "timeout" argument to ParamikoSSHClient.run method. If this argument is specified, run method will throw if the command doesn't finish in the provded time period. Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/1ad8e039 Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/1ad8e039 Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/1ad8e039 Branch: refs/heads/trunk Commit: 1ad8e03967e15e93c7fe72ee75f0fdce29f73999 Parents: 4ffa703 Author: Tomaz Muraus <[email protected]> Authored: Wed May 28 16:29:17 2014 +0200 Committer: Tomaz Muraus <[email protected]> Committed: Wed May 28 16:30:40 2014 +0200 ---------------------------------------------------------------------- CHANGES.rst | 12 +++++++++++ libcloud/compute/ssh.py | 47 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/1ad8e039/CHANGES.rst ---------------------------------------------------------------------- diff --git a/CHANGES.rst b/CHANGES.rst index e1e0a39..3d97fee 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -162,6 +162,18 @@ Compute - Add new driver for Kili public cloud (http://kili.io/) [Tomaz Muraus] +- Add "timeout" argument to the ParamikoSSHClient.run method. If this argument + is specified and the command passed to run method doesn't finish in the + defined timeout, `SSHCommandTimeoutError` is throw and the connection to the + remote server is closed. + + Note #1: If timed out happens, this functionality doesn't guarantee that the + underlying command will be stopped / killed. The way it works it simply + closes a connect to the remote server. + [Tomaz Muraus] + + Note #2: "timeout" argument is only available in the Paramiko SSH client. + Storage ~~~~~~~ http://git-wip-us.apache.org/repos/asf/libcloud/blob/1ad8e039/libcloud/compute/ssh.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/ssh.py b/libcloud/compute/ssh.py index 68a4da4..fe9d856 100644 --- a/libcloud/compute/ssh.py +++ b/libcloud/compute/ssh.py @@ -41,11 +41,30 @@ from os.path import join as pjoin from libcloud.utils.logging import ExtraLogFormatter from libcloud.utils.py3 import StringIO +__all__ = [ + 'BaseSSHClient', + 'ParamikoSSHClient', + 'ShellOutSSHClient', + + 'SSHCommandTimeoutError' +] + # Maximum number of bytes to read at once from a socket CHUNK_SIZE = 1024 +class SSHCommandTimeoutError(Exception): + """ + Exception which is raised when an SSH command times out. + """ + def __init__(self, cmd, timeout): + self.cmd = cmd + self.timeout = timeout + message = 'Command didn\'t finish in %s seconds' % (timeout) + super(SSHCommandTimeoutError, self).__init__(message) + + class BaseSSHClient(object): """ Base class representing a connection over SSH/SCP to a remote node. @@ -92,8 +111,8 @@ class BaseSSHClient(object): """ Connect to the remote node over SSH. - :return: True if the connection has been successfully established, - False otherwise. + :return: True if the connection has been successfuly established, False + otherwise. :rtype: ``bool`` """ raise NotImplementedError( @@ -128,8 +147,8 @@ class BaseSSHClient(object): :type path: ``str`` :keyword path: File path on the remote node. - :return: True if the file has been successfully deleted, - False otherwise. + :return: True if the file has been successfuly deleted, False + otherwise. :rtype: ``bool`` """ raise NotImplementedError( @@ -151,8 +170,8 @@ class BaseSSHClient(object): """ Shutdown connection to the remote node. - :return: True if the connection has been successfully closed, - False otherwise. + :return: True if the connection has been successfuly closed, False + otherwise. :rtype: ``bool`` """ raise NotImplementedError( @@ -285,10 +304,14 @@ class ParamikoSSHClient(BaseSSHClient): sftp.close() return True - def run(self, cmd): + def run(self, cmd, timeout=None): """ Note: This function is based on paramiko's exec_command() method. + + :param timeout: How long to wait (in seconds) for the command to + finish (optional). + :type timeout: ``float`` """ extra = {'_cmd': cmd} self.logger.debug('Executing command', extra=extra) @@ -299,6 +322,7 @@ class ParamikoSSHClient(BaseSSHClient): transport = self.client.get_transport() chan = transport.open_session() + start_time = time.time() chan.exec_command(cmd) stdout = StringIO() @@ -320,6 +344,15 @@ class ParamikoSSHClient(BaseSSHClient): exit_status_ready = chan.exit_status_ready() while not exit_status_ready: + current_time = time.time() + elapsed_time = (current_time - start_time) + + if timeout and (elapsed_time > timeout): + # TODO: Is this the right way to clean up? + chan.close() + + raise SSHCommandTimeoutError(cmd=cmd, timeout=timeout) + if chan.recv_ready(): data = chan.recv(CHUNK_SIZE)
