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

Merge pull request #82 from chiniforooshan/fixes

Fixes
Nuwan Goonasekera 8 лет назад
Родитель
Сommit
ddd5fad029

+ 33 - 24
cloudbridge/cloud/providers/gce/provider.py

@@ -10,11 +10,8 @@ from string import Template
 
 
 from cloudbridge.cloud.base import BaseCloudProvider
 from cloudbridge.cloud.base import BaseCloudProvider
 
 
-import googleapiclient.http
 from googleapiclient import discovery
 from googleapiclient import discovery
 
 
-import httplib2
-
 from oauth2client.client import GoogleCredentials
 from oauth2client.client import GoogleCredentials
 from oauth2client.service_account import ServiceAccountCredentials
 from oauth2client.service_account import ServiceAccountCredentials
 
 
@@ -32,7 +29,26 @@ class GCPResourceUrl(object):
         self._connection = connection
         self._connection = connection
         self.parameters = {}
         self.parameters = {}
 
 
-    def get(self):
+    def get_resource(self):
+        """
+        The format of the returned resource is explained in details in
+        https://cloud.google.com/compute/docs/reference/latest/ and
+        https://cloud.google.com/storage/docs/json_api/v1/.
+
+        Example:
+            When requesting a subnetwork resource the output looks like:
+
+            {'kind': 'compute#subnetwork',
+             'id': '6662746501848591938',
+             'creationTimestamp': '2017-10-13T12:53:17.445-07:00',
+             'name': 'testsubnet-2',
+             'network': 'https://www.googleapis.com/compute/v1/projects/galaxy-on-gcp/global/networks/testnet',
+             'ipCidrRange': '10.128.0.0/20',
+             'gatewayAddress': '10.128.0.1',
+             'region': 'https://www.googleapis.com/compute/v1/projects/galaxy-on-gcp/regions/us-central1',
+             'selfLink': 'https://www.googleapis.com/compute/v1/projects/galaxy-on-gcp/regions/us-central1/subnetworks/testsubnet-2',
+             'privateIpGoogleAccess': false}
+        """
         discovery_object = getattr(self._connection, self._resource)()
         discovery_object = getattr(self._connection, self._resource)()
         return discovery_object.get(**self.parameters).execute()
         return discovery_object.get(**self.parameters).execute()
 
 
@@ -45,7 +61,7 @@ class GCPResources(object):
         # Resource descriptions are already pulled into the internal
         # Resource descriptions are already pulled into the internal
         # _resourceDesc field of the connection.
         # _resourceDesc field of the connection.
         #
         #
-        # TODO: We could fetch compute resource descriptions from
+        # FIX_IF_NEEDED: We could fetch compute resource descriptions from
         # https://www.googleapis.com/discovery/v1/apis/compute/v1/rest and
         # https://www.googleapis.com/discovery/v1/apis/compute/v1/rest and
         # storage resource descriptions from
         # storage resource descriptions from
         # https://www.googleapis.com/discovery/v1/apis/storage/v1/rest
         # https://www.googleapis.com/discovery/v1/apis/storage/v1/rest
@@ -113,6 +129,18 @@ class GCPResources(object):
         Build a GCPResourceUrl from a resource's URL string. One can then call
         Build a GCPResourceUrl from a resource's URL string. One can then call
         the get() method on the returned object to fetch resource details from
         the get() method on the returned object to fetch resource details from
         GCP servers.
         GCP servers.
+
+        Example:
+            If the input url is the following
+            
+            https://www.googleapis.com/compute/v1/projects/galaxy-on-gcp/regions/us-central1/subnetworks/testsubnet-2
+            
+            then parse_url will return a GCPResourceURL and the parameters
+            field of the returned object will look like:
+
+            {'project': 'galaxy-on-gcp',
+             'region': 'us-central1',
+             'subnetwork': 'testsubnet-2'}
         """
         """
         url = url.strip()
         url = url.strip()
         if url.startswith(self._root_url):
         if url.startswith(self._root_url):
@@ -213,25 +241,6 @@ class GCECloudProvider(BaseCloudProvider):
         else:
         else:
             return GoogleCredentials.get_application_default()
             return GoogleCredentials.get_application_default()
 
 
-    def get_gce_resource_data(self, uri):
-        """
-        Retrieves GCE resoure data given its resource URI.
-        """
-        http = httplib2.Http()
-        http = self._credentials.authorize(http)
-
-        def _postproc(*kwargs):
-            if len(kwargs) >= 2:
-                # The first argument is request, and the second is response.
-                resource_dict = json.loads(kwargs[1])
-                return resource_dict
-        request = googleapiclient.http.HttpRequest(http=http,
-                                                   postproc=_postproc,
-                                                   uri=uri)
-        # The response is a dict representing the GCE resource data.
-        response = request.execute()
-        return response
-
     def _connect_gcp_storage(self):
     def _connect_gcp_storage(self):
         return discovery.build('storage', 'v1', credentials=self._credentials)
         return discovery.build('storage', 'v1', credentials=self._credentials)
 
 

+ 42 - 32
cloudbridge/cloud/providers/gce/resources.py

@@ -828,8 +828,8 @@ class GCEInstance(BaseInstance):
         machine_type_uri = self._gce_instance.get('machineType')
         machine_type_uri = self._gce_instance.get('machineType')
         if machine_type_uri is None:
         if machine_type_uri is None:
             return None
             return None
-        instance_type = self._provider.get_gce_resource_data(machine_type_uri)
-        return instance_type.get('name', None)
+        parsed_uri = self._provider.parse_url(machine_type_uri)
+        return parsed_uri.parameters['machineType']
 
 
     @property
     @property
     def instance_type(self):
     def instance_type(self):
@@ -839,8 +839,8 @@ class GCEInstance(BaseInstance):
         machine_type_uri = self._gce_instance.get('machineType')
         machine_type_uri = self._gce_instance.get('machineType')
         if machine_type_uri is None:
         if machine_type_uri is None:
             return None
             return None
-        instance_type = self._provider.get_gce_resource_data(machine_type_uri)
-        return GCEInstanceType(self._provider, instance_type)
+        parsed_uri = self._provider.parse_url(machine_type_uri)
+        return GCEInstanceType(self._provider, parsed_uri.get_resource())
 
 
     def reboot(self):
     def reboot(self):
         """
         """
@@ -896,8 +896,8 @@ class GCEInstance(BaseInstance):
             return None
             return None
         for disk in self._gce_instance['disks']:
         for disk in self._gce_instance['disks']:
             if 'boot' in disk and disk['boot']:
             if 'boot' in disk and disk['boot']:
-                return self._provider.get_gce_resource_data(
-                    disk['source']).get('sourceImage')
+                disk_url = self._provider.parse_url(disk['source'])
+                return disk_url.get_resource().get('sourceImage')
         return None
         return None
 
 
     @property
     @property
@@ -908,8 +908,7 @@ class GCEInstance(BaseInstance):
         zone_uri = self._gce_instance.get('zone')
         zone_uri = self._gce_instance.get('zone')
         if zone_uri is None:
         if zone_uri is None:
             return None
             return None
-        zone = self._provider.get_gce_resource_data(zone_uri)
-        return zone.get('name', None)
+        return self._provider.parse_url(zone_uri).parameters['zone']
 
 
     @property
     @property
     def security_groups(self):
     def security_groups(self):
@@ -958,8 +957,7 @@ class GCEInstance(BaseInstance):
             if 'boot' in disk and disk['boot']:
             if 'boot' in disk and disk['boot']:
                 image_body = {
                 image_body = {
                     'name': name,
                     'name': name,
-                    'sourceDisk': self._provider.get_gce_resource_data(
-                        disk['source']).get('selfLink')
+                    'sourceDisk': disk['source']
                 }
                 }
                 operation = (self._provider
                 operation = (self._provider
                              .gce_compute
                              .gce_compute
@@ -1183,8 +1181,8 @@ class GCEInstance(BaseInstance):
         Refreshes the state of this instance by re-querying the cloud provider
         Refreshes the state of this instance by re-querying the cloud provider
         for its latest state.
         for its latest state.
         """
         """
-        self._gce_instance = self._provider.get_gce_resource_data(
-            self._gce_instance.get('selfLink'))
+        self_link = self._gce_instance.get('selfLink')
+        self._gce_instance = self._provider.parse_url(self_link).get_resource()
 
 
     def add_security_group(self, sg):
     def add_security_group(self, sg):
         raise NotImplementedError('To be implemented.')
         raise NotImplementedError('To be implemented.')
@@ -1213,11 +1211,18 @@ class GCENetwork(BaseNetwork):
 
 
     @property
     @property
     def external(self):
     def external(self):
-        raise NotImplementedError("To be implemented")
+        """
+        All GCP networks can be connected to the Internet.
+        """
+        return True
 
 
     @property
     @property
     def state(self):
     def state(self):
-        raise NotImplementedError("To be implemented")
+        """
+        When a GCP network created by the CloudBridge API, we wait until the
+        network is ready.
+        """
+        return NetworkState.AVAILABLE
 
 
     @property
     @property
     def cidr_block(self):
     def cidr_block(self):
@@ -1268,14 +1273,15 @@ class GCEFloatingIP(BaseFloatingIP):
             if len(floating_ip['users']) > 1:
             if len(floating_ip['users']) > 1:
                 cb.log.warning('Address "%s" in use by more than one resource',
                 cb.log.warning('Address "%s" in use by more than one resource',
                                floating_ip['address'])
                                floating_ip['address'])
-            resource = provider.parse_url(floating_ip['users'][0]).get()
+            resource_parsed_url = provider.parse_url(floating_ip['users'][0])
+            resource = resource_parsed_url.get_resource()
             if resource['kind'] == 'compute#forwardingRule':
             if resource['kind'] == 'compute#forwardingRule':
                 self._rule = resource
                 self._rule = resource
-                target = provider.parse_url(resource['target']).get()
+                target = provider.parse_url(resource['target']).get_resource()
                 if target['kind'] == 'compute#targetInstance':
                 if target['kind'] == 'compute#targetInstance':
                     url = provider.parse_url(target['instance'])
                     url = provider.parse_url(target['instance'])
                     try:
                     try:
-                        self._target_instance = url.get()
+                        self._target_instance = url.get_resource()
                     except:
                     except:
                         self._target_instance = GCEFloatingIP._DEAD_INSTANCE
                         self._target_instance = GCEFloatingIP._DEAD_INSTANCE
                 else:
                 else:
@@ -1346,7 +1352,8 @@ class GCERouter(BaseRouter):
         return self._router['name']
         return self._router['name']
 
 
     def refresh(self):
     def refresh(self):
-        self._router = self._provider.parse_url(self._router['selfLink']).get()
+        parsed_url = self._provider.parse_url(self._router['selfLink'])
+        self._router = parsed_url.get_resource()
 
 
     @property
     @property
     def state(self):
     def state(self):
@@ -1355,7 +1362,8 @@ class GCERouter(BaseRouter):
 
 
     @property
     @property
     def network_id(self):
     def network_id(self):
-        network = self._provider.parse_url(self._router['network']).get()
+        parsed_url = self._provider.parse_url(self._router['network'])
+        network = parsed_url.get_resource()
         return network['id']
         return network['id']
 
 
     def delete(self):
     def delete(self):
@@ -1414,7 +1422,7 @@ class GCESubnet(BaseSubnet):
 
 
     @property
     @property
     def network_id(self):
     def network_id(self):
-        return self._provider.parse_url(self.network_url).get()['id']
+        return self._provider.parse_url(self.network_url).get_resource()['id']
 
 
     @property
     @property
     def region(self):
     def region(self):
@@ -1505,13 +1513,15 @@ class GCEVolume(BaseVolume):
     @property
     @property
     def source(self):
     def source(self):
         if 'sourceSnapshot' in self._volume:
         if 'sourceSnapshot' in self._volume:
-            return GCESnapshot(self._provider,
-                               self._provider.get_gce_resource_data(
-                                   self._volume.get('sourceSnapshot')))
+            snapshot_uri = self._volume.get('sourceSnapshot')
+            return GCESnapshot(
+                    self._provider,
+                    self._provider.parse_url(snapshot_uri).get_resource())
         if 'sourceImage' in self._volume:
         if 'sourceImage' in self._volume:
-            return GCEMachineImage(self._provider,
-                                   self._provider.get_gce_resource_data(
-                                       self._volume.get('sourceImage')))
+            image_uri = self._volume.get('sourceImage')
+            return GCEMachineImage(
+                    self._provider,
+                    self._provider.parse_url(image_uri).get_resource())
         return None
         return None
 
 
     @property
     @property
@@ -1563,8 +1573,8 @@ class GCEVolume(BaseVolume):
         # Check whether this volume is attached to an instance.
         # Check whether this volume is attached to an instance.
         if not self.attachments:
         if not self.attachments:
             return
             return
-        instance_data = self._provider.get_gce_resource_data(
-            self.attachments.instance_id)
+        parsed_uri = self._provider.parse_url(self.attachments.instance_id)
+        instance_data = parsed_uri.get_resource()
         # Check whether the instance has this volume attached.
         # Check whether the instance has this volume attached.
         if 'disks' not in instance_data:
         if 'disks' not in instance_data:
             return
             return
@@ -1613,8 +1623,8 @@ class GCEVolume(BaseVolume):
         Refreshes the state of this volume by re-querying the cloud provider
         Refreshes the state of this volume by re-querying the cloud provider
         for its latest state.
         for its latest state.
         """
         """
-        self._volume = self._provider.get_gce_resource_data(
-            self._volume.get('selfLink'))
+        self_link = self._volume.get('selfLink')
+        self._volume = self._provider.parse_url(self_link).get_resource()
 
 
 
 
 class GCESnapshot(BaseSnapshot):
 class GCESnapshot(BaseSnapshot):
@@ -1677,8 +1687,8 @@ class GCESnapshot(BaseSnapshot):
         Refreshes the state of this snapshot by re-querying the cloud provider
         Refreshes the state of this snapshot by re-querying the cloud provider
         for its latest state.
         for its latest state.
         """
         """
-        self._snapshot = self._provider.get_gce_resource_data(
-            self._snapshot.get('selfLink'))
+        self_link = self._snapshot.get('selfLink')
+        self._snapshot = self._provider.parse_url(self_link).get_resource()
 
 
     def delete(self):
     def delete(self):
         """
         """

+ 14 - 13
cloudbridge/cloud/providers/gce/services.py

@@ -498,9 +498,10 @@ class GCEInstanceService(BaseInstanceService):
                          .execute())
                          .execute())
         if 'zone' not in operation:
         if 'zone' not in operation:
             return None
             return None
-        gce_zone = self.provider.get_gce_resource_data(operation['zone'])
+        zone_url = self.provider.parse_url(operation['zone'])
         instance_id = operation.get('targetLink')
         instance_id = operation.get('targetLink')
-        self.provider.wait_for_operation(operation, zone=gce_zone.get('name'))
+        self.provider.wait_for_operation(operation,
+                                         zone=zone_url.parameters['zone'])
         return self.get(instance_id)
         return self.get(instance_id)
 
 
     def get(self, instance_id):
     def get(self, instance_id):
@@ -512,9 +513,9 @@ class GCEInstanceService(BaseInstanceService):
         as its id.
         as its id.
         """
         """
         try:
         try:
-            response = self.provider.get_gce_resource_data(instance_id)
-            if response:
-                return GCEInstance(self.provider, response)
+            return GCEInstance(
+                    self.provider,
+                    self.provider.parse_url(instance_id).get_resource())
         except googleapiclient.errors.HttpError as http_error:
         except googleapiclient.errors.HttpError as http_error:
             # If the instance is not found, the API will raise
             # If the instance is not found, the API will raise
             # googleapiclient.errors.HttpError.
             # googleapiclient.errors.HttpError.
@@ -870,9 +871,8 @@ class GCEVolumeService(BaseVolumeService):
         Returns a volume given its id.
         Returns a volume given its id.
         """
         """
         try:
         try:
-            response = self.provider.get_gce_resource_data(volume_id)
-            if response:
-                return GCEVolume(self.provider, response)
+            return GCEVolume(self.provider,
+                             self.provider.parse_url(volume_id).get_resource())
         except googleapiclient.errors.HttpError as http_error:
         except googleapiclient.errors.HttpError as http_error:
             # If the volume is not found, the API will raise
             # If the volume is not found, the API will raise
             # googleapiclient.errors.HttpError.
             # googleapiclient.errors.HttpError.
@@ -978,9 +978,9 @@ class GCESnapshotService(BaseSnapshotService):
         Returns a snapshot given its id.
         Returns a snapshot given its id.
         """
         """
         try:
         try:
-            response = self.provider.get_gce_resource_data(snapshot_id)
-            if response:
-                return GCESnapshot(self.provider, response)
+            return GCESnapshot(
+                    self.provider,
+                    self.provider.parse_url(snapshot_id).get_resource())
         except googleapiclient.errors.HttpError as http_error:
         except googleapiclient.errors.HttpError as http_error:
             # If the volume is not found, the API will raise
             # If the volume is not found, the API will raise
             # googleapiclient.errors.HttpError.
             # googleapiclient.errors.HttpError.
@@ -1055,8 +1055,9 @@ class GCESnapshotService(BaseSnapshotService):
                          .execute())
                          .execute())
         if 'zone' not in operation:
         if 'zone' not in operation:
             return None
             return None
-        gce_zone = self.provider.get_gce_resource_data(operation['zone'])
-        self.provider.wait_for_operation(operation, zone=gce_zone.get('name'))
+        zone_url = self.provider.parse_url(operation['zone'])
+        self.provider.wait_for_operation(operation,
+                                         zone=zone_url.parameters['zone'])
         snapshots = self.provider.block_store.snapshots.find(name=name)
         snapshots = self.provider.block_store.snapshots.find(name=name)
         if snapshots:
         if snapshots:
             return snapshots[0]
             return snapshots[0]