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

Merge pull request #17 from baizhang/gce

Add GCERegionService for GCE branch
Nuwan Goonasekera 10 лет назад
Родитель
Сommit
73543bc6c2

+ 13 - 3
cloudbridge/cloud/providers/gce/provider.py

@@ -31,8 +31,18 @@ class GCECloudProvider(BaseCloudProvider):
             'gce_project_name', os.environ.get('GCE_PROJECT_NAME'))
         self.credentials_file = self._get_config_value(
             'gce_service_creds_file', os.environ.get('GCE_SERVICE_CREDS_FILE'))
+        self.credentials_dict = self._get_config_value(
+            'gce_service_creds_dict', {})
+        # If 'gce_service_creds_dict' is not passed in from config and
+        # self.credentials_file is available, read and parse the json file to
+        # self.credentials_dict.
+        if self.credentials_file and not self.credentials_dict:
+            with open(self.credentials_file) as creds_file:
+                self.credentials_dict = json.load(creds_file)
         self.default_zone = self._get_config_value(
             'gce_default_zone', os.environ.get('GCE_DEFAULT_ZONE'))
+        self.region_name = self._get_config_value(
+            'gce_region_name', 'us-central1')
 
         # service connections, lazily initialized
         self._gce_compute = None
@@ -71,9 +81,9 @@ class GCECloudProvider(BaseCloudProvider):
         return self._gce_compute
 
     def _connect_gce_compute(self):
-        if self.credentials_file:
-            credentials = ServiceAccountCredentials.from_json_keyfile_name(
-                self.credentials_file)
+        if self.credentials_dict:
+            credentials = ServiceAccountCredentials.from_json_keyfile_dict(
+                self.credentials_dict)
         else:
             credentials = GoogleCredentials.get_application_default()
         return discovery.build('compute', 'v1', credentials=credentials)

+ 74 - 0
cloudbridge/cloud/providers/gce/resources.py

@@ -3,6 +3,8 @@ DataTypes used by this provider
 """
 from cloudbridge.cloud.base.resources import BaseInstanceType
 from cloudbridge.cloud.base.resources import BaseKeyPair
+from cloudbridge.cloud.base.resources import BasePlacementZone
+from cloudbridge.cloud.base.resources import BaseRegion
 
 
 class GCEKeyPair(BaseKeyPair):
@@ -88,3 +90,75 @@ class GCEInstanceType(BaseInstanceType):
                 if key not in ['id', 'name', 'kind', 'guestCpus', 'memoryMb',
                                'maximumPersistentDisksSizeGb',
                                'maximumPersistentDisks']}
+
+
+class GCEPlacementZone(BasePlacementZone):
+
+    def __init__(self, provider, zone, region):
+        super(GCEPlacementZone, self).__init__(provider)
+        if isinstance(zone, GCEPlacementZone):
+            # pylint:disable=protected-access
+            self._gce_zone = zone._gce_zone
+            self._gce_region = zone._gce_region
+        else:
+            self._gce_zone = zone
+            self._gce_region = region
+
+    @property
+    def id(self):
+        """
+        Get the zone id
+        :rtype: ``str``
+        :return: ID for this zone as returned by the cloud middleware.
+        """
+        return self._gce_zone
+
+    @property
+    def name(self):
+        """
+        Get the zone name.
+        :rtype: ``str``
+        :return: Name for this zone as returned by the cloud middleware.
+        """
+        return self._gce_zone
+
+    @property
+    def region_name(self):
+        """
+        Get the region that this zone belongs to.
+        :rtype: ``str``
+        :return: Name of this zone's region as returned by the cloud middleware
+        """
+        return self._gce_region
+
+
+class GCERegion(BaseRegion):
+
+    def __init__(self, provider, gce_region):
+        super(GCERegion, self).__init__(provider)
+        self._gce_region = gce_region
+
+    @property
+    def id(self):
+        # In GCE API, region has an 'id' property, whose values are '1220',
+        # '1100', '1000', '1230', etc. Here we use 'name' property (such
+        # as 'asia-east1', 'europe-west1', 'us-central1', 'us-east1') as
+        # 'id' to represent the region for the consistency with AWS
+        # implementation and ease of use.
+        return self._gce_region['name']
+
+    @property
+    def name(self):
+        return self._gce_region['name']
+
+    @property
+    def zones(self):
+        """
+        Accesss information about placement zones within this region.
+        """
+        zones_response = self._provider.gce_compute.zones().list(
+            project=self._provider.project_name).execute()
+        zones = [zone for zone in zones_response['items']
+                 if zone['region'] == self._gce_region['selfLink']]
+        return [GCEPlacementZone(self._provider, zone['name'], self.name)
+                for zone in zones]

+ 38 - 2
cloudbridge/cloud/providers/gce/services.py

@@ -2,17 +2,19 @@ from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.services import BaseComputeService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
 from cloudbridge.cloud.base.services import BaseKeyPairService
+from cloudbridge.cloud.base.services import BaseRegionService
 from cloudbridge.cloud.base.services import BaseSecurityGroupService
 from cloudbridge.cloud.base.services import BaseSecurityService
 from cloudbridge.cloud.providers.gce import helpers
 from collections import namedtuple
 import hashlib
-
+import googleapiclient
 
 from retrying import retry
 
 from .resources import GCEInstanceType
 from .resources import GCEKeyPair
+from .resources import GCERegion
 
 
 class GCESecurityService(BaseSecurityService):
@@ -232,11 +234,45 @@ class GCEInstanceTypesService(BaseInstanceTypesService):
                                      limit=limit, marker=marker)
 
 
+class GCERegionService(BaseRegionService):
+
+    def __init__(self, provider):
+        super(GCERegionService, self).__init__(provider)
+
+    def get(self, region_id):
+        try:
+            region = self.provider.gce_compute \
+                                  .regions() \
+                                  .get(project=self.provider.project_name,
+                                       region=region_id) \
+                                  .execute()
+        # Handle the case when region_id is not valid
+        except googleapiclient.errors.HttpError:
+            return None
+        if region:
+            return GCERegion(self.provider, region)
+        else:
+            return None
+
+    def list(self, limit=None, marker=None):
+        regions_response = self.provider.gce_compute.regions().list(
+            project=self.provider.project_name).execute()
+        regions = [GCERegion(self.provider, region)
+                   for region in regions_response['items']]
+        return ClientPagedResultList(self.provider, regions,
+                                     limit=limit, marker=marker)
+
+    @property
+    def current(self):
+        return self.get(self.provider.region_name)
+
+
 class GCEComputeService(BaseComputeService):
     # TODO: implement GCEComputeService
     def __init__(self, provider):
         super(GCEComputeService, self).__init__(provider)
         self._instance_type_svc = GCEInstanceTypesService(self.provider)
+        self._region_svc = GCERegionService(self.provider)
 
     @property
     def images(self):
@@ -252,4 +288,4 @@ class GCEComputeService(BaseComputeService):
 
     @property
     def regions(self):
-        raise NotImplementedError("To be implemented")
+        return self._region_svc

+ 1 - 0
test/helpers.py

@@ -61,6 +61,7 @@ TEST_DATA_CONFIG = {
     },
     "GCECloudProvider": {
         "instance_type": os.environ.get('CB_INSTANCE_TYPE_OS', 'f1-micro'),
+        "placement": os.environ.get('CB_PLACEMENT_GCE', 'us-central1-a'),
     }
 }