You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2014/05/28 16:40:20 UTC

git commit: Add "timeout" argument to ParamikoSSHClient.run method.

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 <to...@apache.org>
Authored: Wed May 28 16:29:17 2014 +0200
Committer: Tomaz Muraus <to...@apache.org>
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)