Return-Path: X-Original-To: apmail-libcloud-commits-archive@www.apache.org Delivered-To: apmail-libcloud-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 298EC10733 for ; Fri, 3 Jan 2014 15:00:56 +0000 (UTC) Received: (qmail 5873 invoked by uid 500); 3 Jan 2014 14:59:55 -0000 Delivered-To: apmail-libcloud-commits-archive@libcloud.apache.org Received: (qmail 5732 invoked by uid 500); 3 Jan 2014 14:59:42 -0000 Mailing-List: contact commits-help@libcloud.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@libcloud.apache.org Delivered-To: mailing list commits@libcloud.apache.org Received: (qmail 5115 invoked by uid 99); 3 Jan 2014 14:59:16 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 03 Jan 2014 14:59:16 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 0E527915B8A; Fri, 3 Jan 2014 14:59:15 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: tomaz@apache.org To: commits@libcloud.apache.org Date: Fri, 03 Jan 2014 14:59:54 -0000 Message-Id: <8c7a24f2ba744ccca30c5be8ecccf24b@git.apache.org> In-Reply-To: <07fff5edc729485281906e9c199f5937@git.apache.org> References: <07fff5edc729485281906e9c199f5937@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [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 Authored: Mon Dec 30 22:58:36 2013 -0800 Committer: Rick Wright 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 '' % ( - 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 '' % ( + 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 '' % ( - 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 '' % ( + 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 '' % ( - 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 '' % ( + 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 '' % ( - 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 '' % ( + 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 '' % ( - 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 '' % ( + 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 '' % ( - 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 '' % ( + 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 '' % (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.