libcloud-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From to...@apache.org
Subject svn commit: r1454972 - in /libcloud/branches/0.12.x: ./ libcloud/compute/ libcloud/compute/drivers/ libcloud/test/ libcloud/test/compute/ libcloud/test/compute/fixtures/digitalocean/
Date Mon, 11 Mar 2013 01:58:14 GMT
Author: tomaz
Date: Mon Mar 11 01:58:13 2013
New Revision: 1454972

URL: http://svn.apache.org/r1454972
Log:
Backport commits from trunk.

Added:
    libcloud/branches/0.12.x/libcloud/compute/drivers/digitalocean.py
      - copied unchanged from r1454971, libcloud/trunk/libcloud/compute/drivers/digitalocean.py
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/
      - copied from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/create_node.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/create_node.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/destroy_node.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/destroy_node.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/error.txt
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/error.txt
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/ex_create_ssh_key.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/ex_create_ssh_key.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/ex_destroy_ssh_key.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/ex_destroy_ssh_key.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/ex_list_ssh_keys.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/ex_list_ssh_keys.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/list_images.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/list_images.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/list_locations.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/list_locations.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/list_nodes.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/list_nodes.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/list_nodes_empty.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/list_nodes_empty.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/list_sizes.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/list_sizes.json
    libcloud/branches/0.12.x/libcloud/test/compute/fixtures/digitalocean/reboot_node.json
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/fixtures/digitalocean/reboot_node.json
    libcloud/branches/0.12.x/libcloud/test/compute/test_digitalocean.py
      - copied unchanged from r1454971, libcloud/trunk/libcloud/test/compute/test_digitalocean.py
Modified:
    libcloud/branches/0.12.x/   (props changed)
    libcloud/branches/0.12.x/CHANGES
    libcloud/branches/0.12.x/libcloud/compute/deployment.py
    libcloud/branches/0.12.x/libcloud/compute/providers.py
    libcloud/branches/0.12.x/libcloud/compute/ssh.py
    libcloud/branches/0.12.x/libcloud/compute/types.py
    libcloud/branches/0.12.x/libcloud/test/compute/test_deployment.py
    libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py
    libcloud/branches/0.12.x/libcloud/test/secrets.py-dist

Propchange: libcloud/branches/0.12.x/
------------------------------------------------------------------------------
  Merged /libcloud/trunk:r1454172-1454971

Modified: libcloud/branches/0.12.x/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/CHANGES?rev=1454972&r1=1454971&r2=1454972&view=diff
==============================================================================
--- libcloud/branches/0.12.x/CHANGES (original)
+++ libcloud/branches/0.12.x/CHANGES Mon Mar 11 01:58:13 2013
@@ -1,6 +1,6 @@
                                    -*- coding: utf-8 -*-
 
-Changes with Apache Libcloud 0.12.2:
+Changes with Apache Libcloud in development:
 
   *) General
 
@@ -25,7 +25,16 @@ Changes with Apache Libcloud 0.12.2:
       is private. (LIBCLOUD-297)
       [Grischa Meyer, Tomaz Muraus]
 
-  *) DNS 
+    - Add new driver for DigitalOcean provider - https://www.digitalocean.com/.
+      (LIBCLOUD-304)
+      [Tomaz Muraus]
+
+    - Fix a regression in ParamikoSSHClient.run method which caused this methid
+      to only work as expected if you passed an absolute or a relative path to
+      the script to it. (LIBCLOUD-278)
+      [Tomaz Muraus]
+
+  *) DNS
 
     - Allow user to specify 'priority' extra argument when creating a MX or SRV
       record.

Modified: libcloud/branches/0.12.x/libcloud/compute/deployment.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/compute/deployment.py?rev=1454972&r1=1454971&r2=1454972&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/compute/deployment.py (original)
+++ libcloud/branches/0.12.x/libcloud/compute/deployment.py Mon Mar 11 01:58:13 2013
@@ -149,9 +149,17 @@ class ScriptDeployment(Deployment):
 
         See also L{Deployment.run}
         """
+        file_path = client.put(path=self.name, chmod=int('755', 8),
+                               contents=self.script)
 
-        client.put(path=self.name, chmod=int('755', 8), contents=self.script)
-        self.stdout, self.stderr, self.exit_status = client.run(self.name)
+        # Pre-pend cwd if user specified a relative path
+        if self.name[0] != '/':
+            base_path = os.path.dirname(file_path)
+            name = os.path.join(base_path, self.name)
+        else:
+            name = self.name
+
+        self.stdout, self.stderr, self.exit_status = client.run(name)
 
         if self.delete:
             client.delete(self.name)

Modified: libcloud/branches/0.12.x/libcloud/compute/providers.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/compute/providers.py?rev=1454972&r1=1454971&r2=1454972&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/compute/providers.py (original)
+++ libcloud/branches/0.12.x/libcloud/compute/providers.py Mon Mar 11 01:58:13 2013
@@ -129,7 +129,9 @@ DRIVERS = {
     Provider.HOSTVIRTUAL:
         ('libcloud.compute.drivers.hostvirtual', 'HostVirtualNodeDriver'),
     Provider.ABIQUO:
-        ('libcloud.compute.drivers.abiquo', 'AbiquoNodeDriver')
+        ('libcloud.compute.drivers.abiquo', 'AbiquoNodeDriver'),
+    Provider.DIGITAL_OCEAN:
+        ('libcloud.compute.drivers.digitalocean', 'DigitalOceanNodeDriver')
 }
 
 

Modified: libcloud/branches/0.12.x/libcloud/compute/ssh.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/compute/ssh.py?rev=1454972&r1=1454971&r2=1454972&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/compute/ssh.py (original)
+++ libcloud/branches/0.12.x/libcloud/compute/ssh.py Mon Mar 11 01:58:13 2013
@@ -28,6 +28,10 @@ except ImportError:
 # warning on Python 2.6.
 # Ref: https://bugs.launchpad.net/paramiko/+bug/392973
 
+import os
+import subprocess
+import logging
+
 from os.path import split as psplit
 from os.path import join as pjoin
 
@@ -66,7 +70,9 @@ class BaseSSHClient(object):
         """
         Connect to the remote node over SSH.
 
-        @return: C{bool}
+        @return: True if the connection has been successfuly established, False
+                 otherwise.
+        @rtype: C{bool}
         """
         raise NotImplementedError(
             'connect not implemented for this ssh client')
@@ -86,6 +92,9 @@ class BaseSSHClient(object):
 
         @type mode: C{str}
         @keyword mode: Mode in which the file is opened.
+
+        @return: Full path to the location where a file has been saved.
+        @rtype: C{str}
         """
         raise NotImplementedError(
             'put not implemented for this ssh client')
@@ -96,6 +105,10 @@ class BaseSSHClient(object):
 
         @type path: C{str}
         @keyword path: File path on the remote node.
+
+        @return: True if the file has been successfuly deleted, False
+                 otherwise.
+        @rtype: C{bool}
         """
         raise NotImplementedError(
             'delete not implemented for this ssh client')
@@ -115,6 +128,10 @@ class BaseSSHClient(object):
     def close(self):
         """
         Shutdown connection to the remote node.
+
+        @return: True if the connection has been successfuly closed, False
+                 otherwise.
+        @rtype: C{bool}
         """
         raise NotImplementedError(
             'close not implemented for this ssh client')
@@ -174,6 +191,8 @@ class ParamikoSSHClient(BaseSSHClient):
                     pass
                 sftp.chdir(part)
 
+        cwd = sftp.getcwd()
+
         ak = sftp.file(tail, mode=mode)
         ak.write(contents)
         if chmod is not None:
@@ -181,25 +200,20 @@ class ParamikoSSHClient(BaseSSHClient):
         ak.close()
         sftp.close()
 
+        if path[0] == '/':
+            file_path = path
+        else:
+            file_path = pjoin(cwd, path)
+
+        return file_path
+
     def delete(self, path):
         sftp = self.client.open_sftp()
         sftp.unlink(path)
         sftp.close()
+        return True
 
     def run(self, cmd):
-        if cmd[0] != '/':
-            # If 'cmd' based on relative path,
-            # set the absoute path joining the HOME path
-            sftp = self.client.open_sftp()
-            # Chdir to its own directory is mandatory because otherwise
-            # the 'getcwd()' method returns None
-            sftp.chdir('.')
-            cwd = sftp.getcwd()
-            sftp.close()
-
-            # Join the command to the current path
-            cmd = pjoin(cwd, cmd)
-
         # based on exec_command()
         bufsize = -1
         t = self.client.get_transport()
@@ -217,12 +231,112 @@ class ParamikoSSHClient(BaseSSHClient):
 
     def close(self):
         self.client.close()
+        return True
 
 
 class ShellOutSSHClient(BaseSSHClient):
-    # TODO: write this one
+    """
+    This client shells out to "ssh" binary to run commands on the remote
+    server.
+
+    Note: This client should not be used in production.
+    """
+
+    def __init__(self, hostname, port=22, username='root', password=None,
+                 key=None, timeout=None):
+        super(ShellOutSSHClient, self).__init__(hostname, port, username,
+                                                password, key, timeout)
+        if self.password:
+            raise ValueError('ShellOutSSHClient only supports key auth')
+
+        child = subprocess.Popen(['ssh'], stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+        child.communicate()
+
+        if child.returncode == 127:
+            raise ValueError('ssh client is not available')
+
+        self.logger = self._get_and_setup_logger()
+
+    def connect(self):
+        """
+        This client doesn't support persistent connections establish a new
+        connection every time "run" method is called.
+        """
+        return True
+
+    def run(self, cmd):
+        return self._run_remote_shell_command([cmd])
+
+    def put(self, path, contents=None, chmod=None, mode='w'):
+        if mode == 'w':
+            redirect = '>'
+        elif mode == 'a':
+            redirect = '>>'
+        else:
+            raise ValueError('Invalid mode: ' + mode)
+
+        cmd = ['echo "%s" %s %s' % (contents, redirect, path)]
+        self._run_remote_shell_command(cmd)
+        return path
+
+    def delete(self, path):
+        cmd = ['rm', '-rf', path]
+        self._run_remote_shell_command(cmd)
+        return True
+
+    def close(self):
+        return True
+
+    def _get_and_setup_logger(self):
+        logger = logging.getLogger('libcloud.compute.ssh')
+        path = os.getenv('LIBCLOUD_DEBUG')
+
+        if path:
+            handler = logging.FileHandler(path)
+            logger.addHandler(handler)
+            logger.setLevel(logging.DEBUG)
+
+        return logger
+
+    def _get_base_ssh_command(self):
+        cmd = ['ssh']
+
+        if self.key:
+            cmd += ['-i', self.key]
+
+        if self.timeout:
+            cmd += ['-oConnectTimeout=%s' % (self.timeout)]
+
+        cmd += ['%s@%s' % (self.username, self.hostname)]
+
+        return cmd
+
+    def _run_remote_shell_command(self, cmd):
+        """
+        Run a command on a remote server.
+
+        @param      cmd: Command to run.
+        @type       cmd: C{list} of C{str}
+
+        @return: Command stdout, stderr and status code.
+        @rtype: C{tuple}
+        """
+        base_cmd = self._get_base_ssh_command()
+        full_cmd = base_cmd + [' '.join(cmd)]
+
+        self.logger.debug('Executing command: "%s"' % (' '.join(full_cmd)))
+
+        child = subprocess.Popen(full_cmd, stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+        stdout, stderr = child.communicate()
+        return (stdout, stderr, child.returncode)
+
+
+class MockSSHClient(BaseSSHClient):
     pass
 
+
 SSHClient = ParamikoSSHClient
 if not have_paramiko:
-    SSHClient = ShellOutSSHClient
+    SSHClient = MockSSHClient

Modified: libcloud/branches/0.12.x/libcloud/compute/types.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/compute/types.py?rev=1454972&r1=1454971&r2=1454972&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/compute/types.py (original)
+++ libcloud/branches/0.12.x/libcloud/compute/types.py Mon Mar 11 01:58:13 2013
@@ -110,6 +110,7 @@ class Provider(object):
     GRIDSPOT = 'gridspot'
     HOSTVIRTUAL = 'hostvirtual'
     ABIQUO = 'abiquo'
+    DIGITAL_OCEAN = 'digitalocean'
 
     EC2_US_EAST = 'ec2_us_east'
     EC2_EU = 'ec2_eu_west'  # deprecated name

Modified: libcloud/branches/0.12.x/libcloud/test/compute/test_deployment.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/test/compute/test_deployment.py?rev=1454972&r1=1454971&r2=1454972&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/test/compute/test_deployment.py (original)
+++ libcloud/branches/0.12.x/libcloud/test/compute/test_deployment.py Mon Mar 11 01:58:13
2013
@@ -111,6 +111,26 @@ class DeploymentTests(unittest.TestCase)
         self.assertEqual(self.node, sd2.run(node=self.node,
                         client=MockClient(hostname='localhost')))
 
+    def test_script_deployment_relative_path(self):
+        client = Mock()
+        client.put.return_value = '/home/ubuntu/relative.sh'
+        client.run.return_value = ('', '', 0)
+
+        sd = ScriptDeployment(script='echo "foo"', name='relative.sh')
+        sd.run(self.node, client)
+
+        client.run.assert_called_once_with('/home/ubuntu/relative.sh')
+
+    def test_script_deployment_absolute_path(self):
+        client = Mock()
+        client.put.return_value = '/home/ubuntu/relative.sh'
+        client.run.return_value = ('', '', 0)
+
+        sd = ScriptDeployment(script='echo "foo"', name='/root/relative.sh')
+        sd.run(self.node, client)
+
+        client.run.assert_called_once_with('/root/relative.sh')
+
     def test_script_deployment_and_sshkey_deployment_argument_types(self):
         class FileObject(object):
             def __init__(self, name):

Modified: libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py?rev=1454972&r1=1454971&r2=1454972&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py (original)
+++ libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py Mon Mar 11 01:58:13
2013
@@ -14,10 +14,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import absolute_import
+
 import sys
 import unittest
 
 from libcloud.compute.ssh import ParamikoSSHClient
+from libcloud.compute.ssh import ShellOutSSHClient
 from libcloud.compute.ssh import have_paramiko
 
 from mock import patch, Mock
@@ -114,40 +117,6 @@ class ParamikoSSHClientTests(unittest.Te
 
         mock.close()
 
-    def test_run_script_with_relative_path(self):
-        """
-        Execute script with relative path.
-        """
-        mock = self.ssh_cli
-
-        # Define behaviour then ask for 'current directory'
-        mock.client.open_sftp().getcwd.return_value = '/home/ubuntu/'
-
-        # Script without full path
-        sd = 'random_script.sh'
-
-        # Without assertions because they are the same than the previous
-        # 'test_basic_usage' method
-        mock.connect()
-
-        mock_cli = mock.client  # The actual mocked object: SSHClient
-
-        mock.put(sd, chmod=600)
-        # Make assertions over 'put' method
-        mock_cli.open_sftp().file.assert_called_once_with('random_script.sh',
-                                                          mode='w')
-        mock_cli.open_sftp().file().chmod.assert_called_once_with(600)
-
-        mock.run(sd)
-        # Make assertions over the 'run' method
-        mock_cli.open_sftp().chdir.assert_called_with(".")
-        mock_cli.open_sftp().getcwd.assert_called_once()
-        full_sd = '/home/ubuntu/random_script.sh'
-        mock_cli.get_transport().open_session().exec_command \
-                .assert_called_once_with(full_sd)
-
-        mock.close()
-
     def test_delete_script(self):
         """
         Provide a basic test with 'delete' action.
@@ -164,10 +133,69 @@ class ParamikoSSHClientTests(unittest.Te
 
         mock.close()
 
+
 if not ParamikoSSHClient:
     class ParamikoSSHClientTests(unittest.TestCase):
         pass
 
 
+class ShellOutSSHClientTests(unittest.TestCase):
+    def test_password_auth_not_supported(self):
+        try:
+            ShellOutSSHClient(hostname='localhost', username='foo',
+                              password='bar')
+        except ValueError:
+            e = sys.exc_info()[1]
+            msg = str(e)
+            self.assertTrue('ShellOutSSHClient only supports key auth' in msg)
+        else:
+            self.fail('Exception was not thrown')
+
+    def test_ssh_executable_not_available(self):
+        class MockChild(object):
+            returncode = 127
+
+            def communicate(*args, **kwargs):
+                pass
+
+        def mock_popen(*args, **kwargs):
+            return MockChild()
+
+        with patch('subprocess.Popen', mock_popen):
+            try:
+                ShellOutSSHClient(hostname='localhost', username='foo')
+            except ValueError:
+                e = sys.exc_info()[1]
+                msg = str(e)
+                self.assertTrue('ssh client is not available' in msg)
+            else:
+                self.fail('Exception was not thrown')
+
+    def test_connect_success(self):
+        client = ShellOutSSHClient(hostname='localhost', username='root')
+        self.assertTrue(client.connect())
+
+    def test_close_success(self):
+        client = ShellOutSSHClient(hostname='localhost', username='root')
+        self.assertTrue(client.close())
+
+    def test_get_base_ssh_command(self):
+        client1 = ShellOutSSHClient(hostname='localhost', username='root')
+        client2 = ShellOutSSHClient(hostname='localhost', username='root',
+                                    key='/home/my.key')
+        client3 = ShellOutSSHClient(hostname='localhost', username='root',
+                                    key='/home/my.key', timeout=5)
+
+        cmd1 = client1._get_base_ssh_command()
+        cmd2 = client2._get_base_ssh_command()
+        cmd3 = client3._get_base_ssh_command()
+
+        self.assertEquals(cmd1, ['ssh', 'root@localhost'])
+        self.assertEquals(cmd2, ['ssh', '-i', '/home/my.key',
+                                 'root@localhost'])
+        self.assertEquals(cmd3, ['ssh', '-i', '/home/my.key',
+                                 '-oConnectTimeout=5', 'root@localhost'])
+
+
 if __name__ == '__main__':
     sys.exit(unittest.main())

Modified: libcloud/branches/0.12.x/libcloud/test/secrets.py-dist
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/test/secrets.py-dist?rev=1454972&r1=1454971&r2=1454972&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/test/secrets.py-dist (original)
+++ libcloud/branches/0.12.x/libcloud/test/secrets.py-dist Mon Mar 11 01:58:13 2013
@@ -39,6 +39,7 @@ JOYENT_PARAMS = ('user', 'key')
 VCL_PARAMS = ('user', 'pass', True, 'foo.bar.com')
 GRIDSPOT_PARAMS = ('key',)
 HOSTVIRTUAL_PARAMS = ('key',)
+DIGITAL_OCEAN_PARAMS = ('user', 'key')
 
 # Storage
 STORAGE_S3_PARAMS = ('key', 'secret')



Mime
View raw message