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

Merge pull request #16 from baizhang/gce

Add GCEInstanceTypesService for GCE branch.
Nuwan Goonasekera 10 лет назад
Родитель
Сommit
22f68b2da0

+ 3 - 3
cloudbridge/cloud/interfaces/services.py

@@ -960,10 +960,10 @@ class InstanceTypesService(PageableObjectMixin, CloudService):
     @abstractmethod
     def find(self, **kwargs):
         """
-        Searches for an instance by a given list of attributes.
+        Searches for instances by a given list of attributes.
 
-        :rtype: ``object`` of :class:`.InstanceType`
-        :return: an Instance object
+        :rtype: ``list`` of :class:`.InstanceType`
+        :return: list of InstanceType objects
         """
         pass
 

+ 9 - 17
cloudbridge/cloud/providers/gce/provider.py

@@ -10,9 +10,10 @@ import os
 import time
 
 from googleapiclient import discovery
-import httplib2
-from oauth2client.client import SignedJwtAssertionCredentials
+from oauth2client.client import GoogleCredentials
+from oauth2client.service_account import ServiceAccountCredentials
 
+from .services import GCEComputeService
 from .services import GCESecurityService
 
 
@@ -26,8 +27,6 @@ class GCECloudProvider(BaseCloudProvider):
         # Initialize cloud connection fields
         self.client_email = self._get_config_value(
             'gce_client_email', os.environ.get('GCE_CLIENT_EMAIL'))
-        self.private_key = self._get_config_value(
-            'gce_private_key', os.environ.get('GCE_PRIVATE_KEY'))
         self.project_name = self._get_config_value(
             'gce_project_name', os.environ.get('GCE_PROJECT_NAME'))
         self.credentials_file = self._get_config_value(
@@ -39,12 +38,12 @@ class GCECloudProvider(BaseCloudProvider):
         self._gce_compute = None
 
         # Initialize provider services
+        self._compute = GCEComputeService(self)
         self._security = GCESecurityService(self)
 
     @property
     def compute(self):
-        raise NotImplementedError(
-            "GCECloudProvider does not implement this service")
+        return self._compute
 
     @property
     def network(self):
@@ -73,18 +72,11 @@ class GCECloudProvider(BaseCloudProvider):
 
     def _connect_gce_compute(self):
         if self.credentials_file:
-            with open(self.credentials_file) as f:
-                data = json.load(f)
-                credentials = SignedJwtAssertionCredentials(
-                    data['client_email'], data['private_key'],
-                    'https://www.googleapis.com/auth/compute')
+            credentials = ServiceAccountCredentials.from_json_keyfile_name(
+                self.credentials_file)
         else:
-            credentials = SignedJwtAssertionCredentials(
-                self.client_email, self.private_key,
-                'https://www.googleapis.com/auth/compute')
-        http = httplib2.Http()
-        http = credentials.authorize(http)
-        return discovery.build('compute', 'v1', http=http)
+            credentials = GoogleCredentials.get_application_default()
+        return discovery.build('compute', 'v1', credentials=credentials)
 
     def wait_for_global_operation(self, operation):
         while True:

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

@@ -1,6 +1,7 @@
 """
 DataTypes used by this provider
 """
+from cloudbridge.cloud.base.resources import BaseInstanceType
 from cloudbridge.cloud.base.resources import BaseKeyPair
 
 
@@ -42,3 +43,48 @@ class GCEKeyPair(BaseKeyPair):
     @material.setter
     def material(self, value):
         self._kp_material = value
+
+
+class GCEInstanceType(BaseInstanceType):
+    def __init__(self, provider, instance_dict):
+        super(GCEInstanceType, self).__init__(provider)
+        self._inst_dict = instance_dict
+
+    @property
+    def id(self):
+        return str(self._inst_dict.get('id'))
+
+    @property
+    def name(self):
+        return self._inst_dict.get('name')
+
+    @property
+    def family(self):
+        return self._inst_dict.get('kind')
+
+    @property
+    def vcpus(self):
+        return self._inst_dict.get('guestCpus')
+
+    @property
+    def ram(self):
+        return self._inst_dict.get('memoryMb')
+
+    @property
+    def size_root_disk(self):
+        return 0
+
+    @property
+    def size_ephemeral_disks(self):
+        return int(self._inst_dict.get('maximumPersistentDisksSizeGb'))
+
+    @property
+    def num_ephemeral_disks(self):
+        return self._inst_dict.get('maximumPersistentDisks')
+
+    @property
+    def extra_data(self):
+        return {key: val for key, val in self._inst_dict.items()
+                if key not in ['id', 'name', 'kind', 'guestCpus', 'memoryMb',
+                               'maximumPersistentDisksSizeGb',
+                               'maximumPersistentDisks']}

+ 68 - 0
cloudbridge/cloud/providers/gce/services.py

@@ -1,4 +1,6 @@
 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 BaseSecurityGroupService
 from cloudbridge.cloud.base.services import BaseSecurityService
@@ -9,6 +11,7 @@ import hashlib
 
 from retrying import retry
 
+from .resources import GCEInstanceType
 from .resources import GCEKeyPair
 
 
@@ -185,3 +188,68 @@ class GCESecurityGroupService(BaseSecurityGroupService):
 
     def __init__(self, provider):
         super(GCESecurityGroupService, self).__init__(provider)
+
+
+class GCEInstanceTypesService(BaseInstanceTypesService):
+
+    def __init__(self, provider):
+        super(GCEInstanceTypesService, self).__init__(provider)
+
+    @property
+    def instance_data(self):
+        response = self.provider.gce_compute \
+                                .machineTypes() \
+                                .list(project=self.provider.project_name,
+                                      zone=self.provider.default_zone) \
+                                .execute()
+        return response['items']
+
+    def get(self, instance_type_id):
+        for inst_type in self.instance_data:
+            if inst_type.get('id') == instance_type_id:
+                return GCEInstanceType(self.provider, inst_type)
+        return None
+
+    def find(self, **kwargs):
+        matched_inst_types = []
+        for inst_type in self.instance_data:
+            is_match = True
+            for key, value in kwargs.iteritems():
+                if key not in inst_type:
+                    raise TypeError("The attribute key is not valid.")
+                if inst_type.get(key) != value:
+                    is_match = False
+                    break
+            if is_match:
+                matched_inst_types.append(
+                    GCEInstanceType(self.provider, inst_type))
+        return matched_inst_types
+
+    def list(self, limit=None, marker=None):
+        inst_types = [GCEInstanceType(self.provider, inst_type)
+                      for inst_type in self.instance_data]
+        return ClientPagedResultList(self.provider, inst_types,
+                                     limit=limit, marker=marker)
+
+
+class GCEComputeService(BaseComputeService):
+    # TODO: implement GCEComputeService
+    def __init__(self, provider):
+        super(GCEComputeService, self).__init__(provider)
+        self._instance_type_svc = GCEInstanceTypesService(self.provider)
+
+    @property
+    def images(self):
+        raise NotImplementedError("To be implemented")
+
+    @property
+    def instance_types(self):
+        return self._instance_type_svc
+
+    @property
+    def instances(self):
+        raise NotImplementedError("To be implemented")
+
+    @property
+    def regions(self):
+        raise NotImplementedError("To be implemented")

+ 10 - 10
setup.py

@@ -13,18 +13,18 @@ with open(os.path.join('cloudbridge', '__init__.py')) as f:
             version = ast.literal_eval(m.group(1))
             break
 
-base_reqs = ['bunch==1.0.1', 'six==1.10.0', 'retrying==1.3.3']
-openstack_reqs = ['python-novaclient==2.33.0',
+base_reqs = ['bunch>=1.0.1', 'six>=1.10.0', 'retrying>=1.3.3']
+openstack_reqs = ['python-novaclient>=2.33.0',
                   'python-glanceclient',
-                  'python-cinderclient==1.4.0',
-                  'python-swiftclient==2.6.0',
-                  'python-neutronclient==3.1.0',
-                  'python-keystoneclient==2.0.0']
-aws_reqs = ['boto==2.38.0']
-gce_reqs = ['google-api-python-client==1.4.2', "pycrypto"]
+                  'python-cinderclient>=1.4.0',
+                  'python-swiftclient>=2.6.0',
+                  'python-neutronclient>=3.1.0',
+                  'python-keystoneclient>=2.0.0']
+aws_reqs = ['boto>=2.38.0']
+gce_reqs = ['google-api-python-client>=1.4.2', "pycrypto"]
 full_reqs = base_reqs + aws_reqs + openstack_reqs + gce_reqs
-dev_reqs = (['httpretty==0.8.10', 'tox==2.1.1', 'moto==0.4.18',
-             'sphinx==1.3.1'] + full_reqs)
+dev_reqs = (['httpretty>=0.8.10', 'tox>=2.1.1', 'moto>=0.4.18',
+             'sphinx>=1.3.1'] + full_reqs)
 
 setup(name='cloudbridge',
       version=version,

+ 5 - 0
test/helpers.py

@@ -58,6 +58,9 @@ TEST_DATA_CONFIG = {
                                 'a471339a-bd0e-41e2-9406-4f308267ed0f'),
         "instance_type": os.environ.get('CB_INSTANCE_TYPE_OS', 'm1.tiny'),
         "placement": os.environ.get('CB_PLACEMENT_OS', 'nova'),
+    },
+    "GCECloudProvider": {
+        "instance_type": os.environ.get('CB_INSTANCE_TYPE_OS', 'f1-micro'),
     }
 }
 
@@ -67,6 +70,8 @@ def get_provider_test_data(provider, key):
         return TEST_DATA_CONFIG.get("AWSCloudProvider").get(key)
     elif "OpenStackCloudProvider" in provider.name:
         return TEST_DATA_CONFIG.get("OpenStackCloudProvider").get(key)
+    elif "GCECloudProvider" in provider.name:
+        return TEST_DATA_CONFIG.get("GCECloudProvider").get(key)
     return None
 
 

+ 1 - 1
tox.ini

@@ -8,7 +8,7 @@ envlist = py27, py35, pypy
 
 [testenv]
 commands = {envpython} -m coverage run --branch --source=cloudbridge --omit=cloudbridge/cloud/interfaces/* setup.py test
-passenv = AWS_ACCESS_KEY AWS_SECRET_KEY GCE_CLIENT_EMAIL GCE_PRIVATE_KEY GCE_PROJECT_NAME GCE_DEFAULT_ZONE GCE_SERVICE_CREDS_FILE OS_AUTH_URL OS_PASSWORD OS_TENANT_NAME OS_USERNAME OS_REGION_NAME NOVA_SERVICE_NAME CB_IMAGE_AWS CB_INSTANCE_TYPE_AWS CB_PLACEMENT_AWS CB_IMAGE_OS CB_INSTANCE_TYPE_OS CB_PLACEMENT_OS CB_TEST_PROVIDER CB_USE_MOCK_PROVIDERS
+passenv = AWS_ACCESS_KEY AWS_SECRET_KEY GCE_CLIENT_EMAIL GCE_PROJECT_NAME GCE_DEFAULT_ZONE GCE_SERVICE_CREDS_FILE OS_AUTH_URL OS_PASSWORD OS_TENANT_NAME OS_USERNAME OS_REGION_NAME NOVA_SERVICE_NAME CB_IMAGE_AWS CB_INSTANCE_TYPE_AWS CB_PLACEMENT_AWS CB_IMAGE_OS CB_INSTANCE_TYPE_OS CB_PLACEMENT_OS CB_TEST_PROVIDER CB_USE_MOCK_PROVIDERS
 deps =
     -rrequirements.txt
     coverage