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/01/03 15:59:39 UTC

[27/44] git commit: Update API to v1 and remove traces of ephemeral disks. The primary change here is updating ex_create_multiple_nodes to create disks and nodes in parallel.

Update API to v1 and remove traces of ephemeral disks.
The primary change here is updating ex_create_multiple_nodes to create disks and
nodes in parallel.


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/e58e3231
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/e58e3231
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/e58e3231

Branch: refs/heads/trunk
Commit: e58e3231c6adb799a7845048bce39a1424547ac3
Parents: b7f709f
Author: Rick Wright <ri...@google.com>
Authored: Wed Dec 4 13:17:25 2013 -0800
Committer: Rick Wright <ri...@google.com>
Committed: Thu Jan 2 23:30:02 2014 -0800

----------------------------------------------------------------------
 libcloud/compute/drivers/gce.py | 196 ++++++++++++++++++++++++-----------
 1 file changed, 137 insertions(+), 59 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/e58e3231/libcloud/compute/drivers/gce.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py
index 6d74ee3..f2417cf 100644
--- a/libcloud/compute/drivers/gce.py
+++ b/libcloud/compute/drivers/gce.py
@@ -30,7 +30,7 @@ from libcloud.compute.base import NodeSize, StorageVolume, UuidMixin
 from libcloud.compute.providers import Provider
 from libcloud.compute.types import NodeState
 
-API_VERSION = 'v1beta16'
+API_VERSION = 'v1'
 DEFAULT_TASK_COMPLETION_TIMEOUT = 180
 
 
@@ -99,6 +99,18 @@ class GCEAddress(UuidMixin):
         return self.driver.ex_destroy_address(address=self)
 
 
+class GCEFailedDisk(object):
+    """Dummy Node object for disks that are not created."""
+    def __init__(self, name, error, code):
+        self.name = name
+        self.error = error
+        self.code = code
+
+    def __repr__(self):
+        return '<GCEFailedDisk name="%s" error_code="%s">' % (
+            self.name, self.code)
+
+
 class GCEFailedNode(object):
     """Dummy Node object for nodes that are not created."""
     def __init__(self, name, error, code):
@@ -1232,8 +1244,7 @@ class GCENodeDriver(NodeDriver):
         return self.ex_get_network(name)
 
     def _create_node_req(self, name, size, image, location, network,
-                         tags=None, metadata=None, boot_disk=None,
-                         persistent_disk=False):
+                         tags=None, metadata=None, boot_disk=None):
         """
         Returns a request and body to create a new node.  This is a helper
         method to suppor both :class:`create_node` and
@@ -1264,11 +1275,6 @@ class GCENodeDriver(NodeDriver):
         :keyword  boot_disk:  Persistent boot disk to attach.
         :type     :class:`StorageVolume`
 
-        :keyword  persistent_disk: If True, create a persistent disk instead of
-                                   an ephemeral one.  Has no effect if
-                                   boot_disk is specified.
-        :type     persistent_disk: ``bool``
-
         :return:  A tuple containing a request string and a node_data dict.
         :rtype:   ``tuple`` of ``str`` and ``dict``
         """
@@ -1279,9 +1285,7 @@ class GCENodeDriver(NodeDriver):
             node_data['tags'] = {'items': tags}
         if metadata:
             node_data['metadata'] = metadata
-        if (not boot_disk) and persistent_disk:
-            boot_disk = self.create_volume(None, name, location=location,
-                                           image=image)
+
         if boot_disk:
             disks = [{'kind': 'compute#attachedDisk',
                       'boot': True,
@@ -1291,7 +1295,6 @@ class GCENodeDriver(NodeDriver):
                       'zone': boot_disk.extra['zone'].extra['selfLink'],
                       'source': boot_disk.extra['selfLink']}]
             node_data['disks'] = disks
-            node_data['kernel'] = image.extra['preferredKernel']
         else:
             node_data['image'] = image.extra['selfLink']
 
@@ -1307,7 +1310,7 @@ class GCENodeDriver(NodeDriver):
 
     def create_node(self, name, size, image, location=None,
                     ex_network='default', ex_tags=None, ex_metadata=None,
-                    ex_boot_disk=None, ex_persistent_disk=False):
+                    ex_boot_disk=None):
         """
         Create a new node and return a node object for the node.
 
@@ -1337,11 +1340,6 @@ class GCENodeDriver(NodeDriver):
         :keyword  ex_boot_disk: The boot disk to attach to the instance.
         :type     ex_boot_disk: :class:`StorageVolume` or ``str``
 
-        :keyword  ex_persistent_disk: If True, create a persistent_disk instead
-                                      of a ephemeral one.  Has no effect if
-                                      ex_boot_disk is specified.
-        :type     ex_persistent_disk: ``bool``
-
         :return:  A Node object for the new node.
         :rtype:   :class:`Node`
         """
@@ -1355,19 +1353,23 @@ class GCENodeDriver(NodeDriver):
         if not hasattr(image, 'name'):
             image = self.ex_get_image(image)
 
+        if not ex_boot_disk:
+            ex_boot_disk = self.create_volume(None, name, location=location,
+                                              image=image)
+
         request, node_data = self._create_node_req(name, size, image,
                                                    location, ex_network,
                                                    ex_tags, ex_metadata,
-                                                   ex_boot_disk,
-                                                   ex_persistent_disk)
+                                                   ex_boot_disk)
         self.connection.async_request(request, method='POST', data=node_data)
 
         return self.ex_get_node(name, location.name)
 
+
     def ex_create_multiple_nodes(self, base_name, size, image, number,
                                  location=None, ex_network='default',
                                  ex_tags=None, ex_metadata=None,
-                                 ignore_errors=True, ex_persistent_disk=False,
+                                 ignore_errors=True,
                                  timeout=DEFAULT_TASK_COMPLETION_TIMEOUT):
         """
         Create multiple nodes and return a list of Node objects.
@@ -1408,12 +1410,9 @@ class GCENodeDriver(NodeDriver):
                                  more nodes fails.
         :type     ignore_errors: ``bool``
 
-        :keyword  persistent_disk: If True, create persistent boot disks
-                                   instead of ephemeral ones.
-        :type     persistent_disk: ``bool``
-
         :keyword  timeout: The number of seconds to wait for all nodes to be
                            created before timing out.
+        :type     timeout: ``int``
 
         :return:  A list of Node objects for the new nodes.
         :rtype:   ``list`` of :class:`Node`
@@ -1429,16 +1428,23 @@ class GCENodeDriver(NodeDriver):
         if not hasattr(image, 'name'):
             image = self.ex_get_image(image)
 
-        node_list = [None] * number
-        responses = []
+        # List for holding the status information for disk/node creation.
+        status_list = [None] * number
+
         for i in range(number):
             name = '%s-%03d' % (base_name, i)
-            request, node_data = self._create_node_req(
-                name, size, image, location, ex_network, ex_tags, ex_metadata,
-                persistent_disk=ex_persistent_disk)
-            response = self.connection.request(request, method='POST',
-                                               data=node_data)
-            responses.append(response.object)
+
+            # Create disks for nodes
+            disk_req, disk_data, disk_params = self._create_vol_req(
+                None, name, location=location, image=image)
+            disk_res = self.connection.request(disk_req, method='POST',
+                                               data=disk_data,
+                                               params=disk_params).object
+            status_list[i] = {'name': name,
+                              'node_response': None,
+                              'node': None,
+                              'disk_response': disk_res,
+                              'disk': None}
 
         start_time = time.time()
         complete = False
@@ -1447,27 +1453,66 @@ class GCENodeDriver(NodeDriver):
                 raise Exception("Timeout (%s sec) while waiting for multiple "
                                 "instances")
             complete = True
-            for i, operation in enumerate(responses):
-                if operation is None:
-                    continue
-                error = None
-                try:
-                    response = self.connection.request(
-                        operation['selfLink']).object
-                except:
-                    e = self._catch_error(ignore_errors=ignore_errors)
-                    error = e.value
-                    code = e.code
-                if response['status'] == 'DONE':
-                    responses[i] = None
-                    name = '%s-%03d' % (base_name, i)
-                    if error:
-                        node_list[i] = GCEFailedNode(name, error, code)
+            time.sleep(2)
+            for i, status in enumerate(status_list):
+                # If disk does not yet exist, check on its status
+                if not status['disk']:
+                    error = None
+                    try:
+                        response = self.connection.request(
+                            status['disk_response']['selfLink']).object
+                    except:
+                        e = self._catch_error(ignore_errors=ignore_errors)
+                        error = e.value
+                        code = e.code
+                    if response['status'] == 'DONE':
+                        status['disk_response'] = None
+                        if error:
+                            status['disk'] = GCEFailedDisk(status['name'],
+                                                           error, code)
+                        else:
+                            status['disk'] = self.ex_get_volume(status['name'], location)
+
+                # If disk exists, but node does not, create the node or check on
+                # its status if already in progress.
+                if status['disk'] and not status['node']:
+                    if not status['node_response']:
+                        request, node_data = self._create_node_req(status['name'], size,
+                                                                   image,
+                                                                   location,
+                                                                   ex_network,
+                                                                   ex_tags,
+                                                                   ex_metadata,
+                                                                   boot_disk=status['disk'])
+                        node_res = self.connection.request(request,
+                                                           method='POST',
+                                                           data=node_data).object
+                        status['node_response'] = node_res
                     else:
-                        node_list[i] = self.ex_get_node(name, location.name)
-                else:
+                        error = None
+                        try:
+                            response = self.connection.request(
+                                status['node_response']['selfLink']).object
+                        except:
+                            e = self._catch_error(ignore_errors=ignore_errors)
+                            error = e.value
+                            code = e.code
+                        if response['status'] == 'DONE':
+                            status['node_response'] = None
+                        if error:
+                            status['node'] = GCEFailedNode(status['name'],
+                                                           error, code)
+                        else:
+                            status['node'] = self.ex_get_node(status['name'], location)
+
+                # If any of the nodes have not been created (or failed) we are
+                # not done yet.
+                if not status['node']:
                     complete = False
-                    time.sleep(2)
+
+        node_list = []
+        for status in status_list:
+            node_list.append(status['node'])
         return node_list
 
     def ex_create_targetpool(self, name, region=None, healthchecks=None,
@@ -1520,10 +1565,12 @@ class GCENodeDriver(NodeDriver):
 
         return self.ex_get_targetpool(name, region)
 
-    def create_volume(self, size, name, location=None, image=None,
-                      snapshot=None):
+    def _create_vol_req(self, size, name, location=None, image=None,
+                        snapshot=None):
         """
-        Create a volume (disk).
+        Assemble the request/data for creating a volume.
+
+        Used by create_volume and ex_create_multiple_nodes
 
         :param  size: Size of volume to create (in GB). Can be None if image
                       or snapshot is supplied.
@@ -1542,8 +1589,9 @@ class GCENodeDriver(NodeDriver):
         :keyword  snapshot: Snapshot to create image from (needs full URI)
         :type     snapshot: ``str``
 
-        :return:  Storage Volume object
-        :rtype:   :class:`StorageVolume`
+        :return:  Tuple containg the request string, the data dictionary and the
+                  URL parameters
+        :rtype:   ``tuple``
         """
         volume_data = {}
         params = None
@@ -1563,8 +1611,38 @@ class GCENodeDriver(NodeDriver):
         if not hasattr(location, 'name'):
             location = self.ex_get_zone(location)
         request = '/zones/%s/disks' % (location.name)
-        self.connection.async_request(request, method='POST',
-                                      data=volume_data,
+
+        return request, volume_data, params
+
+
+    def create_volume(self, size, name, location=None, image=None,
+                      snapshot=None):
+        """
+        Create a volume (disk).
+
+        :param  size: Size of volume to create (in GB). Can be None if image
+                      or snapshot is supplied.
+        :type   size: ``int`` or ``str`` or ``None``
+
+        :param  name: Name of volume to create
+        :type   name: ``str``
+
+        :keyword  location: Location (zone) to create the volume in
+        :type     location: ``str`` or :class:`GCEZone` or
+                            :class:`NodeLocation` or ``None``
+
+        :keyword  image: Image to create disk from.
+        :type     image: :class:`NodeImage` or ``str`` or ``None``
+
+        :keyword  snapshot: Snapshot to create image from (needs full URI)
+        :type     snapshot: ``str``
+
+        :return:  Storage Volume object
+        :rtype:   :class:`StorageVolume`
+        """
+        request, volume_data, params = self._create_vol_req(
+            size, name, location, image, snapshot)
+        self.connection.async_request(request, method='POST', data=volume_data,
                                       params=params)
 
         return self.ex_get_volume(name, location)