Просмотр исходного кода

Initial updates to support name and label + misc fixes

Nuwan Goonasekera 7 лет назад
Родитель
Сommit
04b77f69aa

+ 1 - 1
cloudbridge/cloud/providers/gce/provider.py

@@ -199,7 +199,7 @@ class GCECloudProvider(BaseCloudProvider):
 
         # Disable warnings about file_cache not being available when using
         # oauth2client >= 4.0.0.
-        logging.getLogger('googleapicliet.discovery_cache').setLevel(
+        logging.getLogger('googleapiclient.discovery_cache').setLevel(
                 logging.ERROR)
 
         # Initialize cloud connection fields

+ 238 - 44
cloudbridge/cloud/providers/gce/resources.py

@@ -740,6 +740,35 @@ class GCEMachineImage(BaseMachineImage):
         """
         return self._gce_image['name']
 
+    @property
+    def label(self):
+        labels = self._gce_image.get('labels')
+        return labels.get('cblabel', '') if labels else ''
+
+    @label.setter
+    # pylint:disable=arguments-differ
+    def label(self, value):
+        self.assert_valid_resource_label(value)
+        request_body = {
+            'labels': {'cblabel': value.replace(' ', '_').lower()},
+            'labelFingerprint': self._gce_image.get('labelFingerprint'),
+        }
+        try:
+            (self._provider
+                 .gce_compute
+                 .images()
+                 .setLabels(project=self._provider.project_name,
+                            zone=self._provider.default_zone,
+                            image=self.name,
+                            body=request_body)
+                 .execute())
+        except Exception as e:
+            cb.log.warning('Exception while setting image label: %s. '
+                           'Check for invalid characters in label. '
+                           'Should conform to RFC1035.', e)
+            raise e
+        self.refresh()
+
     @property
     def description(self):
         """
@@ -827,18 +856,34 @@ class GCEInstance(BaseInstance):
         """
         return self._gce_instance['name']
 
-    @name.setter
+    @property
+    def label(self):
+        labels = self._gce_instance.get('labels')
+        return labels.get('cblabel', '') if labels else ''
+
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        """
-        Set the instance name.
-        """
-        GCEInstance.assert_valid_resource_name(value)
-        # In GCE, the name of the instance is provided by the client when
-        # initially creating the resource. The name cannot be changed after
-        # the instance is created.
-        cb.log.warning("Setting instance name after it is created is not "
-                       "supported by this provider: %s", value)
+    def label(self, value):
+        self.assert_valid_resource_label(value)
+        request_body = {
+            'labels': {'cblabel': value.replace(' ', '_').lower()},
+            'labelFingerprint': self._gce_instance.get('labelFingerprint'),
+        }
+        try:
+            (self._provider
+                 .gce_compute
+                 .instances()
+                 .setLabels(project=self._provider.project_name,
+                            zone=self._provider.default_zone,
+                            instance=self.name,
+                            body=request_body)
+                 .execute())
+        except Exception as e:
+            cb.log.warning('Exception while setting instance label: %s. '
+                           'Check for invalid characters in label. '
+                           'Should conform to RFC1035.', e)
+            raise e
+        self.refresh()
 
     @property
     def public_ips(self):
@@ -895,6 +940,14 @@ class GCEInstance(BaseInstance):
         parsed_uri = self._provider.parse_url(machine_type_uri)
         return GCEVMType(self._provider, parsed_uri.get_resource())
 
+    @property
+    def subnet_id(self):
+        """
+        Get the zone for this instance.
+        """
+        return (self._gce_instance.get('networkInterfaces', [{}])[0]
+                .get('subnetwork'))
+
     def reboot(self):
         """
         Reboot this instance.
@@ -1000,12 +1053,14 @@ class GCEInstance(BaseInstance):
         return sg_ids
 
     @property
-    def key_pair_name(self):
+    def key_pair_id(self):
         """
-        Get the name of the key pair associated with this instance.
+        Get the id of the key pair associated with this instance.
+        For GCE, since keys apply to all instances, return first
+        key in metadata.
         """
         try:
-            return next(self._provider.security.key_pairs)
+            return next(iter(self._provider.security.key_pairs))
         except StopIteration:
             return None
 
@@ -1019,11 +1074,12 @@ class GCEInstance(BaseInstance):
         self._inet_gateway = network.gateways.get_or_create_inet_gateway()
         return self._inet_gateway
 
-    def create_image(self, name):
+    def create_image(self, label):
         """
         Create a new image based on this instance.
         """
-        self.assert_valid_resource_name(name)
+        self.assert_valid_resource_label(label)
+        name = self._generate_name_from_label(label, 'cb-img')
         if 'disks' not in self._gce_instance:
             cb.log.error('Failed to create image: no disks found.')
             return
@@ -1031,7 +1087,8 @@ class GCEInstance(BaseInstance):
             if 'boot' in disk and disk['boot']:
                 image_body = {
                     'name': name,
-                    'sourceDisk': disk['source']
+                    'sourceDisk': disk['source'],
+                    'labels': {'cblabel': label.replace(' ', '_').lower()},
                 }
                 operation = (self._provider
                              .gce_compute
@@ -1302,6 +1359,53 @@ class GCENetwork(BaseNetwork):
     def name(self):
         return self._network['name']
 
+    @property
+    def label(self):
+        return self._network.get('description')
+
+    @label.setter
+    # pylint:disable=arguments-differ
+    def label(self, value):
+        self.assert_valid_resource_label(value)
+        body = {'description': value}
+        response = (self._provider
+                    .gce_compute
+                    .networks()
+                    .patch(project=self._provider.project_name,
+                           network=self.name,
+                           body=body)
+                    .execute())
+        self._provider.wait_for_operation(response)
+        self._network['description'] = value
+
+#     @property
+#     def label(self):
+#         labels = self._network.get('labels')
+#         return labels.get('cblabel', '') if labels else ''
+#
+#     @label.setter
+#     # pylint:disable=arguments-differ
+#     def label(self, value):
+#         request_body = {
+#             'labels': {'cblabel': value.replace(' ', '_').lower()},
+#             'labelFingerprint': self._network.get('labelFingerprint'),
+#         }
+#         try:
+#             (self._provider
+#                  .gce_compute
+#                  .networks()
+#                  .setLabels(project=self._provider.project_name,
+#                             zone=self._provider.default_zone,
+#                             resource=self.name,
+#                             body=request_body)
+#                  .execute())
+#         except Exception as e:
+#             cb.log.warning('Exception while setting network label: %s. '
+#                            'Check for invalid characters in label. '
+#                            'Should conform to RFC1035.', e)
+#             raise e
+#         self.refresh()
+
     @property
     def external(self):
         """
@@ -1346,9 +1450,9 @@ class GCENetwork(BaseNetwork):
             return False
         return True
 
-    def create_subnet(self, cidr_block, name=None, zone=None):
+    def create_subnet(self, label, cidr_block, zone):
         return self._provider.networking.subnets.create(
-            self, cidr_block, name, zone)
+            label, self, cidr_block, zone)
 
     def refresh(self):
         self._network = self._provider.get_resource('networks', self.id)
@@ -1520,6 +1624,32 @@ class GCERouter(BaseRouter):
     def name(self):
         return self._router['name']
 
+    @property
+    def label(self):
+        return self._router.get('description')
+
+    @label.setter
+    # pylint:disable=arguments-differ
+    def label(self, value):
+        self.assert_valid_resource_label(value)
+        request_body = {
+            'description': value.replace(' ', '_').lower()
+        }
+        try:
+            (self._provider
+                 .gce_compute
+                 .routers()
+                 .patch(project=self._provider.project_name,
+                        router=self.name,
+                        body=request_body)
+                 .execute())
+        except Exception as e:
+            cb.log.warning('Exception while setting router label: %s. '
+                           'Check for invalid characters in label. '
+                           'Should conform to RFC1035.', e)
+            raise e
+        self.refresh()
+
     @property
     def region_name(self):
         parsed_url = self._provider.parse_url(self.id)
@@ -1545,6 +1675,11 @@ class GCERouter(BaseRouter):
         network = parsed_url.get_resource()
         return network['selfLink']
 
+    @property
+    def subnets(self):
+        network = self._provider.networking.networks.get(self.network_id)
+        return network.subnets
+
     def delete(self):
         response = (self._provider
                         .gce_compute
@@ -1651,12 +1786,32 @@ class GCESubnet(BaseSubnet):
     def name(self):
         return self._subnet['name']
 
-    @name.setter
-    def name(self, value):
-        GCESubnet.assert_valid_resource_name(value)
-        if value == self.name:
-            return
-        cb.log.warning('Cannot change the name of a GCE subnetwork')
+    @property
+    def label(self):
+        return self._subnet.get('description')
+
+    @label.setter
+    def label(self, value):
+        self.assert_valid_resource_label(value)
+        request_body = {
+            'description': value.replace(' ', '_').lower(),
+            'fingerprint': self._subnet.get('fingerprint')
+        }
+        try:
+            (self._provider
+                 .gce_compute
+                 .subnetworks()
+                 .patch(project=self._provider.project_name,
+                        region=self.region_name,
+                        subnetwork=self.name,
+                        body=request_body)
+                 .execute())
+        except Exception as e:
+            cb.log.warning('Exception while setting subnet label: %s. '
+                           'Check for invalid characters in label. '
+                           'Should conform to RFC1035.', e)
+            raise e
+        self.refresh()
 
     @property
     def cidr_block(self):
@@ -1722,15 +1877,33 @@ class GCEVolume(BaseVolume):
         """
         return self._volume.get('name')
 
-    @name.setter
-    # pylint:disable=arguments-differ
-    def name(self, value):
-        GCEVolume.assert_valid_resource_name(value)
-        # In GCE, the name of the volume is provided by the client when
-        # initially creating the resource. The name cannot be changed after
-        # the volume is created.
-        cb.log.warning("Setting volume name after it is created is not "
-                       "supported by this provider.")
+    @property
+    def label(self):
+        labels = self._volume.get('labels')
+        return labels.get('cblabel', '') if labels else ''
+
+    @label.setter
+    def label(self, value):
+        self.assert_valid_resource_label(value)
+        request_body = {
+            'labels': {'cblabel': value.replace(' ', '_').lower()},
+            'labelFingerprint': self._volume.get('labelFingerprint'),
+        }
+        try:
+            (self._provider
+                 .gce_compute
+                 .disks()
+                 .setLabels(project=self._provider.project_name,
+                            zone=self._provider.default_zone,
+                            resource=self.name,
+                            body=request_body)
+                 .execute())
+        except Exception as e:
+            cb.log.warning('Exception while setting volume name: %s. '
+                           'Check for invalid characters in name. '
+                           'Should conform to RFC1035.', e)
+            raise e
+        self.refresh()
 
     @property
     def description(self):
@@ -1856,12 +2029,12 @@ class GCEVolume(BaseVolume):
                          deviceName=device_name)
              .execute())
 
-    def create_snapshot(self, name, description=None):
+    def create_snapshot(self, label, description=None):
         """
         Create a snapshot of this Volume.
         """
         return self._provider.storage.snapshots.create(
-            name, self, description)
+            label, self, description)
 
     def delete(self):
         """
@@ -1916,15 +2089,33 @@ class GCESnapshot(BaseSnapshot):
         """
         return self._snapshot.get('name')
 
-    @name.setter
+    @property
+    def label(self):
+        labels = self._snapshot.get('labels')
+        return labels.get('cblabel', '') if labels else ''
+
+    @label.setter
     # pylint:disable=arguments-differ
-    def name(self, value):
-        GCESnapshot.assert_valid_resource_name(value)
-        # In GCE, the name of the snapshot is provided by the client when
-        # initially creating the resource. The name cannot be changed after
-        # the snapshot is created.
-        cb.log.warning("Setting snapshot name after it is created is not "
-                       "supported by this provider.")
+    def label(self, value):
+        self.assert_valid_resource_label(value)
+        request_body = {
+            'labels': {'cblabel': value.replace(' ', '_').lower()},
+            'labelFingerprint': self._snapshot.get('labelFingerprint'),
+        }
+        try:
+            (self._provider
+                 .gce_compute
+                 .snapshots()
+                 .setLabels(project=self._provider.project_name,
+                            resource=self.name,
+                            body=request_body)
+                 .execute())
+        except Exception as e:
+            cb.log.warning('Exception while setting snapshot label: %s. '
+                           'Check for invalid characters in label. '
+                           'Should conform to RFC1035.', e)
+            raise e
+        self.refresh()
 
     @property
     def description(self):
@@ -2106,6 +2297,9 @@ class GCSObject(BaseBucketObject):
                                               expiration,
                                               url_encoded_signature))
 
+    def refresh(self):
+        self._obj = self.bucket.objects.get(self.id)._obj
+
 
 class GCSBucketContainer(BaseBucketContainer):
 

+ 59 - 42
cloudbridge/cloud/providers/gce/services.py

@@ -1,4 +1,5 @@
 import hashlib
+import logging
 import time
 import uuid
 from collections import namedtuple
@@ -8,7 +9,6 @@ import googleapiclient
 from retrying import retry
 
 import cloudbridge as cb
-from cloudbridge.cloud.base.resources import BaseNetwork
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ServerPagedResultList
 from cloudbridge.cloud.base.services import BaseBucketService
@@ -48,6 +48,8 @@ from .resources import GCEVMType
 from .resources import GCEVolume
 from .resources import GCSBucket
 
+log = logging.getLogger(__name__)
+
 
 class GCESecurityService(BaseSecurityService):
 
@@ -258,10 +260,11 @@ class GCEVMFirewallService(BaseVMFirewallService):
         return ClientPagedResultList(self.provider, vm_firewalls,
                                      limit=limit, marker=marker)
 
-    def create(self, name, description, network_id=None):
-        GCEVMFirewall.assert_valid_resource_name(name)
-        network = self.provider.networking.networks.get(network_id)
-        fw = GCEVMFirewall(self._delegate, name, network, description)
+    def create(self, label, description, network=None):
+        GCEVMFirewall.assert_valid_resource_label(label)
+        network = (network if isinstance(network, GCENetwork)
+                   else self.provider.networking.networks.get(network))
+        fw = GCEVMFirewall(self._delegate, label, network, description)
         # This rule exists implicitly. Add it explicitly so that the firewall
         # is not empty and the rule is shown by list/get/find methods.
         fw.rules.create_with_priority(
@@ -712,7 +715,7 @@ class GCENetworkService(BaseNetworkService):
         return ClientPagedResultList(self.provider, networks,
                                      limit=limit, marker=marker)
 
-    def _create(self, name, cidr_block, create_subnetworks):
+    def _create(self, label, cidr_block, create_subnetworks):
         """
         Possible values for 'create_subnetworks' are:
 
@@ -729,10 +732,10 @@ class GCENetworkService(BaseNetworkService):
                            'in each region with explicit CIDR blocks',
                            GCENetwork.DEFAULT_IPV4RANGE)
             cidr_block = None
-        networks = self.list(filter='name eq %s' % name)
-        if len(networks) > 0:
-            return networks[0]
-        body = {'name': name}
+        name = GCENetwork._generate_name_from_label(label, 'cbnet')
+        body = {'name': name,
+                'labels': {'name': label}
+                }
         if cidr_block:
             body['IPv4Range'] = cidr_block
         else:
@@ -753,13 +756,13 @@ class GCENetworkService(BaseNetworkService):
             cb.log.warning('googleapiclient.errors.HttpError: %s', http_error)
             return None
 
-    def create(self, name, cidr_block):
+    def create(self, label, cidr_block):
         """
         Creates an auto mode VPC network with default subnets. It is possible
         to add additional subnets later.
         """
-        GCENetwork.assert_valid_resource_name(name)
-        return self._create(name, cidr_block, False)
+        GCENetwork.assert_valid_resource_label(label)
+        return self._create(label, cidr_block, False)
 
     def get_or_create_default(self):
         return self._create(GCEFirewallsDelegate.DEFAULT_NETWORK, None, True)
@@ -803,9 +806,11 @@ class GCERouterService(BaseRouterService):
                                      response.get('nextPageToken'),
                                      False, data=routers)
 
-    def create(self, name, network):
-        name = name if name else 'router-{0}'.format(uuid.uuid4())
-        GCERouter.assert_valid_resource_name(name)
+    def create(self, label, network):
+        log.debug("Creating GCE Router Service with params "
+                  "[label: %s network: %s]", label, network)
+        GCERouter.assert_valid_resource_label(label)
+        name = GCERouter._generate_name_from_label(label, 'cb-router')
 
         if not isinstance(network, GCENetwork):
             network = self.provider.networking.networks.get(network)
@@ -818,7 +823,8 @@ class GCERouterService(BaseRouterService):
                             .insert(project=self.provider.project_name,
                                     region=region_name,
                                     body={'name': name,
-                                          'network': network_url})
+                                          'network': network_url,
+                                          'description': label})
                             .execute())
             if 'error' in response:
                 return None
@@ -866,6 +872,8 @@ class GCESubnetService(BaseSubnetService):
         """
         filter = None
         if network is not None:
+            network = (network if isinstance(network, GCENetwork)
+                       else self.provider.networking.networks.get(network))
             filter = 'network eq %s' % network.resource_url
         region_names = []
         if zone:
@@ -887,36 +895,37 @@ class GCESubnetService(BaseSubnetService):
         return ClientPagedResultList(self.provider, subnets,
                                      limit=limit, marker=marker)
 
-    def create(self, network, cidr_block, name=None, zone=None):
+    def create(self, label, network, cidr_block, zone):
         """
-        GCE subnets are regional. The region is inferred from the zone if a
-        zone is provided; otherwise, the default region, as set in the
+        GCE subnets are regional. The region is inferred from the zone;
+        otherwise, the default region, as set in the
         provider, is used.
 
         If a subnet with overlapping IP range exists already, we return that
         instead of creating a new subnet. In this case, other parameters, i.e.
         the name and the zone, are ignored.
         """
-        if not name:
-            name = 'subnet-{0}'.format(uuid.uuid4())
-        GCESubnet.assert_valid_resource_name(name)
+        GCESubnet.assert_valid_resource_label(label)
+        name = GCESubnet._generate_name_from_label(label, 'cbsubnet')
         region_name = self._zone_to_region_name(zone)
-        for subnet in self.iter(network=network):
-            if BaseNetwork.cidr_blocks_overlap(subnet.cidr_block, cidr_block):
-                if subnet.region_name != region_name:
-                    cb.log.error('Failed to create subnetwork in region %s: '
-                                 'the given IP range %s overlaps with a '
-                                 'subnetwork in a different region %s',
-                                 region_name, cidr_block, subnet.region_name)
-                    return None
-                return subnet
-            if subnet.name == name and subnet.region_name == region_name:
-                return subnet
+#         for subnet in self.iter(network=network):
+#            if BaseNetwork.cidr_blocks_overlap(subnet.cidr_block, cidr_block):
+#                 if subnet.region_name != region_name:
+#                     cb.log.error('Failed to create subnetwork in region %s: '
+#                                  'the given IP range %s overlaps with a '
+#                                  'subnetwork in a different region %s',
+#                                  region_name, cidr_block, subnet.region_name)
+#                     return None
+#                 return subnet
+#             if subnet.label == label and subnet.region_name == region_name:
+#                 return subnet
 
         body = {'ipCidrRange': cidr_block,
                 'name': name,
                 'network': network.resource_url,
-                'region': region_name}
+                'region': region_name,
+                'labels': {'cblabel': label.replace(' ', '_').lower()}
+                }
         try:
             response = (self.provider
                             .gce_compute
@@ -1011,11 +1020,11 @@ class GCEVolumeService(BaseVolumeService):
         vol = self.provider.get_resource('disks', volume_id)
         return GCEVolume(self.provider, vol) if vol else None
 
-    def find(self, name, limit=None, marker=None):
+    def find(self, label, limit=None, marker=None):
         """
         Searches for a volume by a given list of attributes.
         """
-        filtr = 'name eq ' + name
+        filtr = 'label.cblabel eq ' + label
         max_result = limit if limit is not None and limit < 500 else 500
         response = (self.provider
                         .gce_compute
@@ -1063,7 +1072,7 @@ class GCEVolumeService(BaseVolumeService):
                                      response.get('nextPageToken'),
                                      False, data=gce_vols)
 
-    def create(self, name, size, zone, snapshot=None, description=None):
+    def create(self, label, size, zone, snapshot=None, description=None):
         """
         Creates a new volume.
 
@@ -1074,7 +1083,12 @@ class GCEVolumeService(BaseVolumeService):
         be a dash, lowercase letter, or digit, except the last character, which
         cannot be a dash.
         """
-        GCEVolume.assert_valid_resource_name(name)
+        log.debug("Creating GCE Volume with parameters "
+                  "[label: %s size: %s zone: %s snapshot: %s "
+                  "description: %s]", label, size, zone, snapshot,
+                  description)
+        GCEVolume.assert_valid_resource_label(label)
+        name = GCEVolume._generate_name_from_label(label, 'cb-vol')
         if not isinstance(zone, GCEPlacementZone):
             zone = GCEPlacementZone(
                 self.provider,
@@ -1088,6 +1102,7 @@ class GCEVolumeService(BaseVolumeService):
             'type': 'zones/{0}/diskTypes/{1}'.format(zone_name, 'pd-standard'),
             'sourceSnapshot': snapshot_id,
             'description': description,
+            'labels': {'cblabel': label.replace(' ', '_').lower()}
         }
         operation = (self.provider
                          .gce_compute
@@ -1156,15 +1171,17 @@ class GCESnapshotService(BaseSnapshotService):
                                      response.get('nextPageToken'),
                                      False, data=snapshots)
 
-    def create(self, name, volume, description=None):
+    def create(self, label, volume, description=None):
         """
         Creates a new snapshot of a given volume.
         """
-        GCESnapshot.assert_valid_resource_name(name)
+        GCESnapshot.assert_valid_resource_label(label)
+        name = GCESnapshot._generate_name_from_label(label, 'cbsnap')
         volume_name = volume.name if isinstance(volume, GCEVolume) else volume
         snapshot_body = {
             "name": name,
-            "description": description
+            "description": description,
+            "labels": {"cblabel": label}
         }
         operation = (self.provider
                          .gce_compute