Răsfoiți Sursa

Merge pull request #82 from chiniforooshan/fixes

Fixes
Nuwan Goonasekera 8 ani în urmă
părinte
comite
ddd5fad029

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

@@ -10,11 +10,8 @@ from string import Template
 
 from cloudbridge.cloud.base import BaseCloudProvider
 
-import googleapiclient.http
 from googleapiclient import discovery
 
-import httplib2
-
 from oauth2client.client import GoogleCredentials
 from oauth2client.service_account import ServiceAccountCredentials
 
@@ -32,7 +29,26 @@ class GCPResourceUrl(object):
         self._connection = connection
         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)()
         return discovery_object.get(**self.parameters).execute()
 
@@ -45,7 +61,7 @@ class GCPResources(object):
         # Resource descriptions are already pulled into the internal
         # _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
         # storage resource descriptions from
         # 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
         the get() method on the returned object to fetch resource details from
         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()
         if url.startswith(self._root_url):
@@ -213,25 +241,6 @@ class GCECloudProvider(BaseCloudProvider):
         else:
             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):
         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')
         if machine_type_uri is 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
     def instance_type(self):
@@ -839,8 +839,8 @@ class GCEInstance(BaseInstance):
         machine_type_uri = self._gce_instance.get('machineType')
         if machine_type_uri is 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):
         """
@@ -896,8 +896,8 @@ class GCEInstance(BaseInstance):
             return None
         for disk in self._gce_instance['disks']:
             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
 
     @property
@@ -908,8 +908,7 @@ class GCEInstance(BaseInstance):
         zone_uri = self._gce_instance.get('zone')
         if zone_uri is 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
     def security_groups(self):
@@ -958,8 +957,7 @@ class GCEInstance(BaseInstance):
             if 'boot' in disk and disk['boot']:
                 image_body = {
                     'name': name,
-                    'sourceDisk': self._provider.get_gce_resource_data(
-                        disk['source']).get('selfLink')
+                    'sourceDisk': disk['source']
                 }
                 operation = (self._provider
                              .gce_compute
@@ -1183,8 +1181,8 @@ class GCEInstance(BaseInstance):
         Refreshes the state of this instance by re-querying the cloud provider
         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):
         raise NotImplementedError('To be implemented.')
@@ -1213,11 +1211,18 @@ class GCENetwork(BaseNetwork):
 
     @property
     def external(self):
-        raise NotImplementedError("To be implemented")
+        """
+        All GCP networks can be connected to the Internet.
+        """
+        return True
 
     @property
     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
     def cidr_block(self):
@@ -1268,14 +1273,15 @@ class GCEFloatingIP(BaseFloatingIP):
             if len(floating_ip['users']) > 1:
                 cb.log.warning('Address "%s" in use by more than one resource',
                                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':
                 self._rule = resource
-                target = provider.parse_url(resource['target']).get()
+                target = provider.parse_url(resource['target']).get_resource()
                 if target['kind'] == 'compute#targetInstance':
                     url = provider.parse_url(target['instance'])
                     try:
-                        self._target_instance = url.get()
+                        self._target_instance = url.get_resource()
                     except:
                         self._target_instance = GCEFloatingIP._DEAD_INSTANCE
                 else:
@@ -1346,7 +1352,8 @@ class GCERouter(BaseRouter):
         return self._router['name']
 
     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
     def state(self):
@@ -1355,7 +1362,8 @@ class GCERouter(BaseRouter):
 
     @property
     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']
 
     def delete(self):
@@ -1414,7 +1422,7 @@ class GCESubnet(BaseSubnet):
 
     @property
     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
     def region(self):
@@ -1505,13 +1513,15 @@ class GCEVolume(BaseVolume):
     @property
     def source(self):
         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:
-            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
 
     @property
@@ -1563,8 +1573,8 @@ class GCEVolume(BaseVolume):
         # Check whether this volume is attached to an instance.
         if not self.attachments:
             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.
         if 'disks' not in instance_data:
             return
@@ -1613,8 +1623,8 @@ class GCEVolume(BaseVolume):
         Refreshes the state of this volume by re-querying the cloud provider
         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):
@@ -1677,8 +1687,8 @@ class GCESnapshot(BaseSnapshot):
         Refreshes the state of this snapshot by re-querying the cloud provider
         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):
         """

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

@@ -498,9 +498,10 @@ class GCEInstanceService(BaseInstanceService):
                          .execute())
         if 'zone' not in operation:
             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')
-        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)
 
     def get(self, instance_id):
@@ -512,9 +513,9 @@ class GCEInstanceService(BaseInstanceService):
         as its id.
         """
         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:
             # If the instance is not found, the API will raise
             # googleapiclient.errors.HttpError.
@@ -870,9 +871,8 @@ class GCEVolumeService(BaseVolumeService):
         Returns a volume given its id.
         """
         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:
             # If the volume is not found, the API will raise
             # googleapiclient.errors.HttpError.
@@ -978,9 +978,9 @@ class GCESnapshotService(BaseSnapshotService):
         Returns a snapshot given its id.
         """
         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:
             # If the volume is not found, the API will raise
             # googleapiclient.errors.HttpError.
@@ -1055,8 +1055,9 @@ class GCESnapshotService(BaseSnapshotService):
                          .execute())
         if 'zone' not in operation:
             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)
         if snapshots:
             return snapshots[0]