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:54 UTC

[42/44] git commit: Reorder methods to put them in public/private/internal order.

Reorder methods to put them in public/private/internal order.


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

Branch: refs/heads/trunk
Commit: 00c30bc457bbda07dbc7cebedb520a3edf0a9722
Parents: 9e6caa7
Author: Rick Wright <ri...@google.com>
Authored: Mon Dec 30 22:58:36 2013 -0800
Committer: Rick Wright <ri...@google.com>
Committed: Thu Jan 2 23:30:03 2014 -0800

----------------------------------------------------------------------
 libcloud/compute/drivers/gce.py | 938 +++++++++++++++++------------------
 1 file changed, 469 insertions(+), 469 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/00c30bc4/libcloud/compute/drivers/gce.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/gce.py b/libcloud/compute/drivers/gce.py
index 71d2d1f..f056774 100644
--- a/libcloud/compute/drivers/gce.py
+++ b/libcloud/compute/drivers/gce.py
@@ -88,10 +88,6 @@ class GCEAddress(UuidMixin):
         self.extra = extra
         UuidMixin.__init__(self)
 
-    def __repr__(self):
-        return '<GCEAddress id="%s" name="%s" address="%s">' % (
-            self.id, self.name, self.address)
-
     def destroy(self):
         """
         Destroy this address.
@@ -101,6 +97,10 @@ class GCEAddress(UuidMixin):
         """
         return self.driver.ex_destroy_address(address=self)
 
+    def __repr__(self):
+        return '<GCEAddress id="%s" name="%s" address="%s">' % (
+            self.id, self.name, self.address)
+
 
 class GCEFailedDisk(object):
     """Dummy Node object for disks that are not created."""
@@ -142,10 +142,6 @@ class GCEHealthCheck(UuidMixin):
         self.extra = extra
         UuidMixin.__init__(self)
 
-    def __repr__(self):
-        return '<GCEHealthCheck id="%s" name="%s" path="%s" port="%s">' % (
-            self.id, self.name, self.path, self.port)
-
     def destroy(self):
         """
         Destroy this Health Check.
@@ -164,6 +160,10 @@ class GCEHealthCheck(UuidMixin):
         """
         return self.driver.ex_update_healthcheck(healthcheck=self)
 
+    def __repr__(self):
+        return '<GCEHealthCheck id="%s" name="%s" path="%s" port="%s">' % (
+            self.id, self.name, self.path, self.port)
+
 
 class GCEFirewall(UuidMixin):
     """A GCE Firewall rule class."""
@@ -179,10 +179,6 @@ class GCEFirewall(UuidMixin):
         self.extra = extra
         UuidMixin.__init__(self)
 
-    def __repr__(self):
-        return '<GCEFirewall id="%s" name="%s" network="%s">' % (
-            self.id, self.name, self.network.name)
-
     def destroy(self):
         """
         Destroy this firewall.
@@ -201,6 +197,10 @@ class GCEFirewall(UuidMixin):
         """
         return self.driver.ex_update_firewall(firewall=self)
 
+    def __repr__(self):
+        return '<GCEFirewall id="%s" name="%s" network="%s">' % (
+            self.id, self.name, self.network.name)
+
 
 class GCEForwardingRule(UuidMixin):
     def __init__(self, id, name, region, address, protocol, targetpool, driver,
@@ -215,10 +215,6 @@ class GCEForwardingRule(UuidMixin):
         self.extra = extra
         UuidMixin.__init__(self)
 
-    def __repr__(self):
-        return '<GCEForwardingRule id="%s" name="%s" address="%s">' % (
-            self.id, self.name, self.address)
-
     def destroy(self):
         """
         Destroy this Forwarding Rule
@@ -228,6 +224,10 @@ class GCEForwardingRule(UuidMixin):
         """
         return self.driver.ex_destroy_forwarding_rule(forwarding_rule=self)
 
+    def __repr__(self):
+        return '<GCEForwardingRule id="%s" name="%s" address="%s">' % (
+            self.id, self.name, self.address)
+
 
 class GCENetwork(UuidMixin):
     """A GCE Network object class."""
@@ -239,10 +239,6 @@ class GCENetwork(UuidMixin):
         self.extra = extra
         UuidMixin.__init__(self)
 
-    def __repr__(self):
-        return '<GCENetwork id="%s" name="%s" cidr="%s">' % (
-            self.id, self.name, self.cidr)
-
     def destroy(self):
         """
         Destroy this newtwork
@@ -252,6 +248,10 @@ class GCENetwork(UuidMixin):
         """
         return self.driver.ex_destroy_network(network=self)
 
+    def __repr__(self):
+        return '<GCENetwork id="%s" name="%s" cidr="%s">' % (
+            self.id, self.name, self.cidr)
+
 
 class GCENodeSize(NodeSize):
     """A GCE Node Size (MachineType) class."""
@@ -314,10 +314,6 @@ class GCETargetPool(UuidMixin):
         self.extra = extra
         UuidMixin.__init__(self)
 
-    def __repr__(self):
-        return '<GCETargetPool id="%s" name="%s" region="%s">' % (
-            self.id, self.name, self.region.name)
-
     def add_node(self, node):
         """
         Add a node to this target pool.
@@ -378,6 +374,10 @@ class GCETargetPool(UuidMixin):
         """
         return self.driver.ex_destroy_targetpool(targetpool=self)
 
+    def __repr__(self):
+        return '<GCETargetPool id="%s" name="%s" region="%s">' % (
+            self.id, self.name, self.region.name)
+
 
 class GCEZone(NodeLocation):
     """Subclass of NodeLocation to provide additional information."""
@@ -391,6 +391,22 @@ class GCEZone(NodeLocation):
         super(GCEZone, self).__init__(id=str(id), name=name, country=country,
                                       driver=driver)
 
+    @property
+    def time_until_mw(self):
+        """
+        Returns the time until the next Maintenance Window as a
+        datetime.timedelta object.
+        """
+        return self._get_time_until_mw()
+
+    @property
+    def next_mw_duration(self):
+        """
+        Returns the duration of the next Maintenance Window as a
+        datetime.timedelta object.
+        """
+        return self._get_next_mw_duration()
+
     def _now(self):
         """
         Returns current UTC time.
@@ -455,22 +471,6 @@ class GCEZone(NodeLocation):
         next_end = timestamp_to_datetime(next_window['endTime'])
         return next_end - next_begin
 
-    @property
-    def time_until_mw(self):
-        """
-        Returns the time until the next Maintenance Window as a
-        datetime.timedelta object.
-        """
-        return self._get_time_until_mw()
-
-    @property
-    def next_mw_duration(self):
-        """
-        Returns the duration of the next Maintenance Window as a
-        datetime.timedelta object.
-        """
-        return self._get_next_mw_duration()
-
     def __repr__(self):
         return '<GCEZone id="%s" name="%s" status="%s">' % (self.id, self.name,
                                                             self.status)
@@ -558,182 +558,6 @@ class GCENodeDriver(NodeDriver):
         else:
             self.region = None
 
-    def _ex_connection_class_kwargs(self):
-        return {'auth_type': self.auth_type,
-                'project': self.project}
-
-    def _catch_error(self, ignore_errors=False):
-        """
-        Catch an exception and raise it unless asked to ignore it.
-
-        :keyword  ignore_errors: If true, just return the error.  Otherwise,
-                                 raise the error.
-        :type     ignore_errors: ``bool``
-
-        :return:  The exception that was raised.
-        :rtype:   :class:`Exception`
-        """
-        e = sys.exc_info()[1]
-        if ignore_errors:
-            return e
-        else:
-            raise e
-
-    def _get_components_from_path(self, path):
-        """
-        Return a dictionary containing name & zone/region from a request path.
-
-        :param  path: HTTP request path (e.g.
-                      '/project/pjt-name/zones/us-central1-a/instances/mynode')
-        :type   path: ``str``
-
-        :return:  Dictionary containing name and zone/region of resource
-        :rtype    ``dict``
-        """
-        region = None
-        zone = None
-        glob = False
-        components = path.split('/')
-        name = components[-1]
-        if components[-4] == 'regions':
-            region = components[-3]
-        elif components[-4] == 'zones':
-            zone = components[-3]
-        elif components[-3] == 'global':
-            glob = True
-
-        return {'name': name, 'region': region, 'zone': zone, 'global': glob}
-
-    def _get_region_from_zone(self, zone):
-        """
-        Return the Region object that contains the given Zone object.
-
-        :param  zone: Zone object
-        :type   zone: :class:`GCEZone`
-
-        :return:  Region object that contains the zone
-        :rtype:   :class:`GCERegion`
-        """
-        for region in self.region_list:
-            zones = [z.name for z in region.zones]
-            if zone.name in zones:
-                return region
-
-    def _find_zone_or_region(self, name, res_type, region=False,
-                             res_name=None):
-        """
-        Find the zone or region for a named resource.
-
-        :param  name: Name of resource to find
-        :type   name: ``str``
-
-        :param  res_type: Type of resource to find.
-                          Examples include: 'disks', 'instances' or 'addresses'
-        :type   res_type: ``str``
-
-        :keyword  region: If True, search regions instead of zones
-        :type     region: ``bool``
-
-        :keyword  res_name: The name of the resource type for error messages.
-                            Examples: 'Volume', 'Node', 'Address'
-        :keyword  res_name: ``str``
-
-        :return:  Zone/Region object for the zone/region for the resource.
-        :rtype:   :class:`GCEZone` or :class:`GCERegion`
-        """
-        if region:
-            rz = 'region'
-        else:
-            rz = 'zone'
-        rz_name = None
-        res_name = res_name or res_type
-        request = '/aggregated/%s' % (res_type)
-        res_list = self.connection.request(request).object
-        for k, v in res_list['items'].items():
-            for res in v.get(res_type, []):
-                if res['name'] == name:
-                    rz_name = k.replace('%ss/' % (rz), '')
-                    break
-        if not rz_name:
-            raise ResourceNotFoundError(
-                '%s \'%s\' not found in any %s.' % (res_name, name, rz),
-                None, None)
-        else:
-            getrz = getattr(self, 'ex_get_%s' % (rz))
-            return getrz(rz_name)
-
-    def _match_images(self, project, partial_name):
-        """
-        Find the latest image, given a partial name.
-
-        For example, providing 'debian-7' will return the image object for the
-        most recent image with a name that starts with 'debian-7' in the
-        supplied project.  If no project is given, it will search your own
-        project.
-
-        :param  project:  The name of the project to search for images.
-                          Examples include: 'debian-cloud' and 'centos-cloud'.
-        :type   project:  ``str`` or ``None``
-
-        :param  partial_name: The full name or beginning of a name for an
-                              image.
-        :type   partial_name: ``str``
-
-        :return:  The latest image object that maches the partial name or None
-                  if no matching image is found.
-        :rtype:   :class:`NodeImage` or ``None``
-        """
-        project_images = self.list_images(project)
-        partial_match = []
-        for image in project_images:
-            if image.name == partial_name:
-                return image
-            if image.name.startswith(partial_name):
-                ts = timestamp_to_datetime(image.extra['creationTimestamp'])
-                if not partial_match or partial_match[0] < ts:
-                    partial_match = [ts, image]
-
-        if partial_match:
-            return partial_match[1]
-
-    def _set_region(self, region):
-        """
-        Return the region to use for listing resources.
-
-        :param  region: A name, region object, None, or 'all'
-        :type   region: ``str`` or :class:`GCERegion` or ``None``
-
-        :return:  A region object or None if all regions should be considered
-        :rtype:   :class:`GCERegion` or ``None``
-        """
-        region = region or self.region
-
-        if region == 'all' or region is None:
-            return None
-
-        if not hasattr(region, 'name'):
-            region = self.ex_get_region(region)
-        return region
-
-    def _set_zone(self, zone):
-        """
-        Return the zone to use for listing resources.
-
-        :param  zone: A name, zone object, None, or 'all'
-        :type   region: ``str`` or :class:`GCEZone` or ``None``
-
-        :return:  A zone object or None if all zones should be considered
-        :rtype:   :class:`GCEZone` or ``None``
-        """
-        zone = zone or self.zone
-
-        if zone == 'all' or zone is None:
-            return None
-
-        if not hasattr(zone, 'name'):
-            zone = self.ex_get_zone(zone)
-        return zone
-
     def ex_list_addresses(self, region=None):
         """
         Return a list of static addreses for a region or all.
@@ -1267,71 +1091,6 @@ 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):
-        """
-        Returns a request and body to create a new node.  This is a helper
-        method to suppor both :class:`create_node` and
-        :class:`ex_create_multiple_nodes`.
-
-        :param  name: The name of the node to create.
-        :type   name: ``str``
-
-        :param  size: The machine type to use.
-        :type   size: :class:`GCENodeSize`
-
-        :param  image: The image to use to create the node (or, if using a
-                       persistent disk, the image the disk was created from).
-        :type   image: :class:`NodeImage`
-
-        :param  location: The location (zone) to create the node in.
-        :type   location: :class:`NodeLocation` or :class:`GCEZone`
-
-        :param  network: The network to associate with the node.
-        :type   network: :class:`GCENetwork`
-
-        :keyword  tags: A list of tags to assiciate with the node.
-        :type     tags: ``list`` of ``str``
-
-        :keyword  metadata: Metadata dictionary for instance.
-        :type     metadata: ``dict``
-
-        :keyword  boot_disk:  Persistent boot disk to attach.
-        :type     :class:`StorageVolume`
-
-        :return:  A tuple containing a request string and a node_data dict.
-        :rtype:   ``tuple`` of ``str`` and ``dict``
-        """
-        node_data = {}
-        node_data['machineType'] = size.extra['selfLink']
-        node_data['name'] = name
-        if tags:
-            node_data['tags'] = {'items': tags}
-        if metadata:
-            node_data['metadata'] = metadata
-
-        if boot_disk:
-            disks = [{'kind': 'compute#attachedDisk',
-                      'boot': True,
-                      'type': 'PERSISTENT',
-                      'mode': 'READ_WRITE',
-                      'deviceName': boot_disk.name,
-                      'zone': boot_disk.extra['zone'].extra['selfLink'],
-                      'source': boot_disk.extra['selfLink']}]
-            node_data['disks'] = disks
-        else:
-            node_data['image'] = image.extra['selfLink']
-
-        ni = [{'kind': 'compute#instanceNetworkInterface',
-               'accessConfigs': [{'name': 'External NAT',
-                                  'type': 'ONE_TO_ONE_NAT'}],
-               'network': network.extra['selfLink']}]
-        node_data['networkInterfaces'] = ni
-
-        request = '/zones/%s/instances' % (location.name)
-
-        return request, node_data
-
     def create_node(self, name, size, image, location=None,
                     ex_network='default', ex_tags=None, ex_metadata=None,
                     ex_boot_disk=None, use_existing_disk=True):
@@ -1395,139 +1154,6 @@ class GCENodeDriver(NodeDriver):
 
         return self.ex_get_node(name, location.name)
 
-    def _multi_create_disk(self, status, node_attrs):
-        """Create disk for ex_create_multiple_nodes.
-
-        :param  status: Dictionary for holding node/disk creation status.
-                        (This dictionary is modified by this method)
-        :type   status: ``dict``
-
-        :param  node_attrs: Dictionary for holding node attribute information.
-                            (size, image, location, etc.)
-        :type   node_attrs: ``dict``
-        """
-        disk = None
-        # Check for existing disk
-        if node_attrs['use_existing_disk']:
-            try:
-                disk = self.ex_get_volume(status['name'],
-                                          node_attrs['location'])
-            except ResourceNotFoundError:
-                pass
-
-        if disk:
-            status['disk'] = disk
-        else:
-            # Create disk and return response object back in the status dict.
-            # Or, if there is an error, mark as failed.
-            disk_req, disk_data, disk_params = self._create_vol_req(
-                None, status['name'], location=node_attrs['location'],
-                image=node_attrs['image'])
-            try:
-                disk_res = self.connection.request(
-                    disk_req, method='POST', data=disk_data,
-                    params=disk_params).object
-            except GoogleBaseError:
-                e = self._catch_error(
-                    ignore_errors=node_attrs['ignore_errors'])
-                error = e.value
-                code = e.code
-                disk_res = None
-                status['disk'] = GCEFailedDisk(status['name'],
-                                               error, code)
-            status['disk_response'] = disk_res
-
-    def _multi_check_disk(self, status, node_attrs):
-        """Check disk status for ex_create_multiple_nodes.
-
-        :param  status: Dictionary for holding node/disk creation status.
-                        (This dictionary is modified by this method)
-        :type   status: ``dict``
-
-        :param  node_attrs: Dictionary for holding node attribute information.
-                            (size, image, location, etc.)
-        :type   node_attrs: ``dict``
-        """
-        error = None
-        try:
-            response = self.connection.request(
-                status['disk_response']['selfLink']).object
-        except GoogleBaseError:
-            e = self._catch_error(ignore_errors=node_attrs['ignore_errors'])
-            error = e.value
-            code = e.code
-            response = {'status': 'DONE'}
-        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'],
-                                                    node_attrs['location'])
-
-    def _multi_create_node(self, status, node_attrs):
-        """Create node for ex_create_multiple_nodes.
-
-        :param  status: Dictionary for holding node/disk creation status.
-                        (This dictionary is modified by this method)
-        :type   status: ``dict``
-
-        :param  node_attrs: Dictionary for holding node attribute information.
-                            (size, image, location, etc.)
-        :type   node_attrs: ``dict``
-        """
-        # If disk has an error, set the node as failed and return
-        if hasattr(status['disk'], 'error'):
-            status['node'] = status['disk']
-            return
-
-        # Create node and return response object in status dictionary.
-        # Or, if there is an error, mark as failed.
-        request, node_data = self._create_node_req(
-            status['name'], node_attrs['size'], node_attrs['image'],
-            node_attrs['location'], node_attrs['network'], node_attrs['tags'],
-            node_attrs['metadata'], boot_disk=status['disk'])
-        try:
-            node_res = self.connection.request(
-                request, method='POST', data=node_data).object
-        except GoogleBaseError:
-            e = self._catch_error(ignore_errors=node_attrs['ignore_errors'])
-            error = e.value
-            code = e.code
-            node_res = None
-            status['node'] = GCEFailedNode(status['name'],
-                                           error, code)
-        status['node_response'] = node_res
-
-    def _multi_check_node(self, status, node_attrs):
-        """Check node status for ex_create_multiple_nodes.
-
-        :param  status: Dictionary for holding node/disk creation status.
-                        (This dictionary is modified by this method)
-        :type   status: ``dict``
-
-        :param  node_attrs: Dictionary for holding node attribute information.
-                            (size, image, location, etc.)
-        :type   node_attrs: ``dict``
-        """
-        error = None
-        try:
-            response = self.connection.request(
-                status['node_response']['selfLink']).object
-        except GoogleBaseError:
-            e = self._catch_error(ignore_errors=node_attrs['ignore_errors'])
-            error = e.value
-            code = e.code
-            response = {'status': 'DONE'}
-        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'],
-                                              node_attrs['location'])
-
     def ex_create_multiple_nodes(self, base_name, size, image, number,
                                  location=None, ex_network='default',
                                  ex_tags=None, ex_metadata=None,
@@ -1706,61 +1332,6 @@ class GCENodeDriver(NodeDriver):
 
         return self.ex_get_targetpool(name, region)
 
-    def _create_vol_req(self, size, name, location=None, snapshot=None,
-                        image=None):
-        """
-        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.
-        :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  snapshot: Snapshot to create image from
-        :type     snapshot: :class:`GCESnapshot` or ``str`` or ``None``
-
-        :keyword  image: Image to create disk from.
-        :type     image: :class:`NodeImage` or ``str`` or ``None``
-
-        :return:  Tuple containg the request string, the data dictionary and
-                  the URL parameters
-        :rtype:   ``tuple``
-        """
-        volume_data = {}
-        params = None
-        volume_data['name'] = name
-        if size:
-            volume_data['sizeGb'] = str(size)
-        if image:
-            if not hasattr(image, 'name'):
-                image = self.ex_get_image(image)
-            params = {'sourceImage': image.extra['selfLink']}
-            volume_data['description'] = 'Image: %s' % (
-                image.extra['selfLink'])
-        if snapshot:
-            if not hasattr(snapshot, 'name'):
-                # Check for full URI to not break backward-compatibility
-                if snapshot.startswith('https'):
-                    snapshot = self._get_components_from_path(snapshot)['name']
-                snapshot = self.ex_get_snapshot(snapshot)
-            snapshot_link = snapshot.extra['selfLink']
-            volume_data['sourceSnapshot'] = snapshot_link
-            volume_data['description'] = 'Snapshot: %s' % (snapshot_link)
-        location = location or self.zone
-        if not hasattr(location, 'name'):
-            location = self.ex_get_zone(location)
-        request = '/zones/%s/disks' % (location.name)
-
-        return request, volume_data, params
-
     def create_volume(self, size, name, location=None, snapshot=None,
                       image=None, use_existing=True):
         """
@@ -2734,6 +2305,435 @@ class GCENodeDriver(NodeDriver):
             return None
         return self._to_zone(response)
 
+    def _ex_connection_class_kwargs(self):
+        return {'auth_type': self.auth_type,
+                'project': self.project}
+
+    def _catch_error(self, ignore_errors=False):
+        """
+        Catch an exception and raise it unless asked to ignore it.
+
+        :keyword  ignore_errors: If true, just return the error.  Otherwise,
+                                 raise the error.
+        :type     ignore_errors: ``bool``
+
+        :return:  The exception that was raised.
+        :rtype:   :class:`Exception`
+        """
+        e = sys.exc_info()[1]
+        if ignore_errors:
+            return e
+        else:
+            raise e
+
+    def _get_components_from_path(self, path):
+        """
+        Return a dictionary containing name & zone/region from a request path.
+
+        :param  path: HTTP request path (e.g.
+                      '/project/pjt-name/zones/us-central1-a/instances/mynode')
+        :type   path: ``str``
+
+        :return:  Dictionary containing name and zone/region of resource
+        :rtype    ``dict``
+        """
+        region = None
+        zone = None
+        glob = False
+        components = path.split('/')
+        name = components[-1]
+        if components[-4] == 'regions':
+            region = components[-3]
+        elif components[-4] == 'zones':
+            zone = components[-3]
+        elif components[-3] == 'global':
+            glob = True
+
+        return {'name': name, 'region': region, 'zone': zone, 'global': glob}
+
+    def _get_region_from_zone(self, zone):
+        """
+        Return the Region object that contains the given Zone object.
+
+        :param  zone: Zone object
+        :type   zone: :class:`GCEZone`
+
+        :return:  Region object that contains the zone
+        :rtype:   :class:`GCERegion`
+        """
+        for region in self.region_list:
+            zones = [z.name for z in region.zones]
+            if zone.name in zones:
+                return region
+
+    def _find_zone_or_region(self, name, res_type, region=False,
+                             res_name=None):
+        """
+        Find the zone or region for a named resource.
+
+        :param  name: Name of resource to find
+        :type   name: ``str``
+
+        :param  res_type: Type of resource to find.
+                          Examples include: 'disks', 'instances' or 'addresses'
+        :type   res_type: ``str``
+
+        :keyword  region: If True, search regions instead of zones
+        :type     region: ``bool``
+
+        :keyword  res_name: The name of the resource type for error messages.
+                            Examples: 'Volume', 'Node', 'Address'
+        :keyword  res_name: ``str``
+
+        :return:  Zone/Region object for the zone/region for the resource.
+        :rtype:   :class:`GCEZone` or :class:`GCERegion`
+        """
+        if region:
+            rz = 'region'
+        else:
+            rz = 'zone'
+        rz_name = None
+        res_name = res_name or res_type
+        request = '/aggregated/%s' % (res_type)
+        res_list = self.connection.request(request).object
+        for k, v in res_list['items'].items():
+            for res in v.get(res_type, []):
+                if res['name'] == name:
+                    rz_name = k.replace('%ss/' % (rz), '')
+                    break
+        if not rz_name:
+            raise ResourceNotFoundError(
+                '%s \'%s\' not found in any %s.' % (res_name, name, rz),
+                None, None)
+        else:
+            getrz = getattr(self, 'ex_get_%s' % (rz))
+            return getrz(rz_name)
+
+    def _match_images(self, project, partial_name):
+        """
+        Find the latest image, given a partial name.
+
+        For example, providing 'debian-7' will return the image object for the
+        most recent image with a name that starts with 'debian-7' in the
+        supplied project.  If no project is given, it will search your own
+        project.
+
+        :param  project:  The name of the project to search for images.
+                          Examples include: 'debian-cloud' and 'centos-cloud'.
+        :type   project:  ``str`` or ``None``
+
+        :param  partial_name: The full name or beginning of a name for an
+                              image.
+        :type   partial_name: ``str``
+
+        :return:  The latest image object that maches the partial name or None
+                  if no matching image is found.
+        :rtype:   :class:`NodeImage` or ``None``
+        """
+        project_images = self.list_images(project)
+        partial_match = []
+        for image in project_images:
+            if image.name == partial_name:
+                return image
+            if image.name.startswith(partial_name):
+                ts = timestamp_to_datetime(image.extra['creationTimestamp'])
+                if not partial_match or partial_match[0] < ts:
+                    partial_match = [ts, image]
+
+        if partial_match:
+            return partial_match[1]
+
+    def _set_region(self, region):
+        """
+        Return the region to use for listing resources.
+
+        :param  region: A name, region object, None, or 'all'
+        :type   region: ``str`` or :class:`GCERegion` or ``None``
+
+        :return:  A region object or None if all regions should be considered
+        :rtype:   :class:`GCERegion` or ``None``
+        """
+        region = region or self.region
+
+        if region == 'all' or region is None:
+            return None
+
+        if not hasattr(region, 'name'):
+            region = self.ex_get_region(region)
+        return region
+
+    def _set_zone(self, zone):
+        """
+        Return the zone to use for listing resources.
+
+        :param  zone: A name, zone object, None, or 'all'
+        :type   region: ``str`` or :class:`GCEZone` or ``None``
+
+        :return:  A zone object or None if all zones should be considered
+        :rtype:   :class:`GCEZone` or ``None``
+        """
+        zone = zone or self.zone
+
+        if zone == 'all' or zone is None:
+            return None
+
+        if not hasattr(zone, 'name'):
+            zone = self.ex_get_zone(zone)
+        return zone
+
+    def _create_node_req(self, name, size, image, location, network,
+                         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
+        :class:`ex_create_multiple_nodes`.
+
+        :param  name: The name of the node to create.
+        :type   name: ``str``
+
+        :param  size: The machine type to use.
+        :type   size: :class:`GCENodeSize`
+
+        :param  image: The image to use to create the node (or, if using a
+                       persistent disk, the image the disk was created from).
+        :type   image: :class:`NodeImage`
+
+        :param  location: The location (zone) to create the node in.
+        :type   location: :class:`NodeLocation` or :class:`GCEZone`
+
+        :param  network: The network to associate with the node.
+        :type   network: :class:`GCENetwork`
+
+        :keyword  tags: A list of tags to assiciate with the node.
+        :type     tags: ``list`` of ``str``
+
+        :keyword  metadata: Metadata dictionary for instance.
+        :type     metadata: ``dict``
+
+        :keyword  boot_disk:  Persistent boot disk to attach.
+        :type     :class:`StorageVolume`
+
+        :return:  A tuple containing a request string and a node_data dict.
+        :rtype:   ``tuple`` of ``str`` and ``dict``
+        """
+        node_data = {}
+        node_data['machineType'] = size.extra['selfLink']
+        node_data['name'] = name
+        if tags:
+            node_data['tags'] = {'items': tags}
+        if metadata:
+            node_data['metadata'] = metadata
+
+        if boot_disk:
+            disks = [{'kind': 'compute#attachedDisk',
+                      'boot': True,
+                      'type': 'PERSISTENT',
+                      'mode': 'READ_WRITE',
+                      'deviceName': boot_disk.name,
+                      'zone': boot_disk.extra['zone'].extra['selfLink'],
+                      'source': boot_disk.extra['selfLink']}]
+            node_data['disks'] = disks
+        else:
+            node_data['image'] = image.extra['selfLink']
+
+        ni = [{'kind': 'compute#instanceNetworkInterface',
+               'accessConfigs': [{'name': 'External NAT',
+                                  'type': 'ONE_TO_ONE_NAT'}],
+               'network': network.extra['selfLink']}]
+        node_data['networkInterfaces'] = ni
+
+        request = '/zones/%s/instances' % (location.name)
+
+        return request, node_data
+
+    def _multi_create_disk(self, status, node_attrs):
+        """Create disk for ex_create_multiple_nodes.
+
+        :param  status: Dictionary for holding node/disk creation status.
+                        (This dictionary is modified by this method)
+        :type   status: ``dict``
+
+        :param  node_attrs: Dictionary for holding node attribute information.
+                            (size, image, location, etc.)
+        :type   node_attrs: ``dict``
+        """
+        disk = None
+        # Check for existing disk
+        if node_attrs['use_existing_disk']:
+            try:
+                disk = self.ex_get_volume(status['name'],
+                                          node_attrs['location'])
+            except ResourceNotFoundError:
+                pass
+
+        if disk:
+            status['disk'] = disk
+        else:
+            # Create disk and return response object back in the status dict.
+            # Or, if there is an error, mark as failed.
+            disk_req, disk_data, disk_params = self._create_vol_req(
+                None, status['name'], location=node_attrs['location'],
+                image=node_attrs['image'])
+            try:
+                disk_res = self.connection.request(
+                    disk_req, method='POST', data=disk_data,
+                    params=disk_params).object
+            except GoogleBaseError:
+                e = self._catch_error(
+                    ignore_errors=node_attrs['ignore_errors'])
+                error = e.value
+                code = e.code
+                disk_res = None
+                status['disk'] = GCEFailedDisk(status['name'],
+                                               error, code)
+            status['disk_response'] = disk_res
+
+    def _multi_check_disk(self, status, node_attrs):
+        """Check disk status for ex_create_multiple_nodes.
+
+        :param  status: Dictionary for holding node/disk creation status.
+                        (This dictionary is modified by this method)
+        :type   status: ``dict``
+
+        :param  node_attrs: Dictionary for holding node attribute information.
+                            (size, image, location, etc.)
+        :type   node_attrs: ``dict``
+        """
+        error = None
+        try:
+            response = self.connection.request(
+                status['disk_response']['selfLink']).object
+        except GoogleBaseError:
+            e = self._catch_error(ignore_errors=node_attrs['ignore_errors'])
+            error = e.value
+            code = e.code
+            response = {'status': 'DONE'}
+        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'],
+                                                    node_attrs['location'])
+
+    def _multi_create_node(self, status, node_attrs):
+        """Create node for ex_create_multiple_nodes.
+
+        :param  status: Dictionary for holding node/disk creation status.
+                        (This dictionary is modified by this method)
+        :type   status: ``dict``
+
+        :param  node_attrs: Dictionary for holding node attribute information.
+                            (size, image, location, etc.)
+        :type   node_attrs: ``dict``
+        """
+        # If disk has an error, set the node as failed and return
+        if hasattr(status['disk'], 'error'):
+            status['node'] = status['disk']
+            return
+
+        # Create node and return response object in status dictionary.
+        # Or, if there is an error, mark as failed.
+        request, node_data = self._create_node_req(
+            status['name'], node_attrs['size'], node_attrs['image'],
+            node_attrs['location'], node_attrs['network'], node_attrs['tags'],
+            node_attrs['metadata'], boot_disk=status['disk'])
+        try:
+            node_res = self.connection.request(
+                request, method='POST', data=node_data).object
+        except GoogleBaseError:
+            e = self._catch_error(ignore_errors=node_attrs['ignore_errors'])
+            error = e.value
+            code = e.code
+            node_res = None
+            status['node'] = GCEFailedNode(status['name'],
+                                           error, code)
+        status['node_response'] = node_res
+
+    def _multi_check_node(self, status, node_attrs):
+        """Check node status for ex_create_multiple_nodes.
+
+        :param  status: Dictionary for holding node/disk creation status.
+                        (This dictionary is modified by this method)
+        :type   status: ``dict``
+
+        :param  node_attrs: Dictionary for holding node attribute information.
+                            (size, image, location, etc.)
+        :type   node_attrs: ``dict``
+        """
+        error = None
+        try:
+            response = self.connection.request(
+                status['node_response']['selfLink']).object
+        except GoogleBaseError:
+            e = self._catch_error(ignore_errors=node_attrs['ignore_errors'])
+            error = e.value
+            code = e.code
+            response = {'status': 'DONE'}
+        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'],
+                                              node_attrs['location'])
+
+    def _create_vol_req(self, size, name, location=None, snapshot=None,
+                        image=None):
+        """
+        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.
+        :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  snapshot: Snapshot to create image from
+        :type     snapshot: :class:`GCESnapshot` or ``str`` or ``None``
+
+        :keyword  image: Image to create disk from.
+        :type     image: :class:`NodeImage` or ``str`` or ``None``
+
+        :return:  Tuple containg the request string, the data dictionary and
+                  the URL parameters
+        :rtype:   ``tuple``
+        """
+        volume_data = {}
+        params = None
+        volume_data['name'] = name
+        if size:
+            volume_data['sizeGb'] = str(size)
+        if image:
+            if not hasattr(image, 'name'):
+                image = self.ex_get_image(image)
+            params = {'sourceImage': image.extra['selfLink']}
+            volume_data['description'] = 'Image: %s' % (
+                image.extra['selfLink'])
+        if snapshot:
+            if not hasattr(snapshot, 'name'):
+                # Check for full URI to not break backward-compatibility
+                if snapshot.startswith('https'):
+                    snapshot = self._get_components_from_path(snapshot)['name']
+                snapshot = self.ex_get_snapshot(snapshot)
+            snapshot_link = snapshot.extra['selfLink']
+            volume_data['sourceSnapshot'] = snapshot_link
+            volume_data['description'] = 'Snapshot: %s' % (snapshot_link)
+        location = location or self.zone
+        if not hasattr(location, 'name'):
+            location = self.ex_get_zone(location)
+        request = '/zones/%s/disks' % (location.name)
+
+        return request, volume_data, params
+
     def _to_address(self, address):
         """
         Return an Address object from the json-response dictionary.