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

Merge branch 'gce' of https://github.com/baizhang/cloudbridge into
baizhang-gce

# Conflicts:
# cloudbridge/cloud/providers/gce/resources.py

Nuwan Goonasekera 9 лет назад
Родитель
Сommit
e67a69a674

+ 3 - 0
cloudbridge/__init__.py

@@ -19,3 +19,6 @@ def init_logging():
     """
     """
     logging.basicConfig(stream=sys.stdout)
     logging.basicConfig(stream=sys.stdout)
     logging.getLogger(__name__).setLevel(logging.DEBUG)
     logging.getLogger(__name__).setLevel(logging.DEBUG)
+
+log = logging.getLogger('cloudbridge')
+log.addHandler(logging.StreamHandler())

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

@@ -3,6 +3,7 @@ DataTypes used by this provider
 """
 """
 from cloudbridge.cloud.base.resources import BaseInstanceType
 from cloudbridge.cloud.base.resources import BaseInstanceType
 from cloudbridge.cloud.base.resources import BaseKeyPair
 from cloudbridge.cloud.base.resources import BaseKeyPair
+from cloudbridge.cloud.base.resources import BaseMachineImage
 from cloudbridge.cloud.base.resources import BasePlacementZone
 from cloudbridge.cloud.base.resources import BasePlacementZone
 from cloudbridge.cloud.base.resources import BaseRegion
 from cloudbridge.cloud.base.resources import BaseRegion
 from cloudbridge.cloud.base.resources import BaseSecurityGroup
 from cloudbridge.cloud.base.resources import BaseSecurityGroup
@@ -595,3 +596,88 @@ class GCESecurityGroupRule(BaseSecurityGroupRule):
 
 
     def delete(self):
     def delete(self):
         self._delegate.delete_firewall_id(self._rule)
         self._delegate.delete_firewall_id(self._rule)
+
+
+class GCEMachineImage(BaseMachineImage):
+
+    IMAGE_STATE_MAP = {
+        'PENDING': MachineImageState.PENDING,
+        'READY': MachineImageState.AVAILABLE,
+        'FAILED': MachineImageState.ERROR
+    }
+
+    def __init__(self, provider, image):
+        super(GCEMachineImage, self).__init__(provider)
+        if isinstance(image, GCEMachineImage):
+            # pylint:disable=protected-access
+            self._gce_image = image._gce_image
+        else:
+            self._gce_image = image
+
+    @property
+    def id(self):
+        """
+        Get the image identifier.
+        :rtype: ``str``
+        :return: ID for this instance as returned by the cloud middleware.
+        """
+        return self._gce_image['name']
+
+    @property
+    def name(self):
+        """
+        Get the image name.
+        :rtype: ``str``
+        :return: Name for this image as returned by the cloud middleware.
+        """
+        return self._gce_image['name']
+
+    @property
+    def description(self):
+        """
+        Get the image description.
+        :rtype: ``str``
+        :return: Description for this image as returned by the cloud middleware
+        """
+        return self._gce_image.get('description', '')
+
+    def delete(self):
+        """
+        Delete this image
+        """
+        request = self._provider.gce_compute.images().delete(
+            project=self._provider.project_name, image=self.name)
+        request.execute()
+
+    @property
+    def state(self):
+        return GCEMachineImage.IMAGE_STATE_MAP.get(
+            self._gce_image['status'], MachineImageState.UNKNOWN)
+
+    def refresh(self):
+        """
+        Refreshes the state of this instance by re-querying the cloud provider
+        for its latest state.
+        """
+        resource_link = self._gce_image['selfLink']
+        project_pattern = 'projects/(.*?)/'
+        match = re.search(project_pattern, resource_link)
+        if match:
+            project = match.group(1)
+        else:
+            cb.log.warning("Project name is not found.")
+            return
+        try:
+            response = self._provider.gce_compute \
+                                  .images() \
+                                  .get(project=project,
+                                       image=self.name) \
+                                  .execute()
+            if response:
+                # pylint:disable=protected-access
+                self._gce_image = response
+        except googleapiclient.errors.HttpError as http_error:
+            # image no longer exists
+            cb.log.warning(
+                "googleapiclient.errors.HttpError: {0}".format(http_error))
+            self._gce_image['status'] = "unknown"

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

@@ -1,17 +1,22 @@
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.services import BaseComputeService
 from cloudbridge.cloud.base.services import BaseComputeService
+from cloudbridge.cloud.base.services import BaseImageService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
 from cloudbridge.cloud.base.services import BaseInstanceTypesService
 from cloudbridge.cloud.base.services import BaseKeyPairService
 from cloudbridge.cloud.base.services import BaseKeyPairService
 from cloudbridge.cloud.base.services import BaseRegionService
 from cloudbridge.cloud.base.services import BaseRegionService
 from cloudbridge.cloud.base.services import BaseSecurityGroupService
 from cloudbridge.cloud.base.services import BaseSecurityGroupService
 from cloudbridge.cloud.base.services import BaseSecurityService
 from cloudbridge.cloud.base.services import BaseSecurityService
 from cloudbridge.cloud.providers.gce import helpers
 from cloudbridge.cloud.providers.gce import helpers
+import cloudbridge as cb
+
 from collections import namedtuple
 from collections import namedtuple
 import hashlib
 import hashlib
 import googleapiclient
 import googleapiclient
 
 
 from retrying import retry
 from retrying import retry
+import sys
 
 
+from .resources import GCEMachineImage
 from .resources import GCEInstanceType
 from .resources import GCEInstanceType
 from .resources import GCEKeyPair
 from .resources import GCEKeyPair
 from .resources import GCERegion
 from .resources import GCERegion
@@ -19,7 +24,6 @@ from .resources import GCEFirewallsDelegate
 from .resources import GCESecurityGroup
 from .resources import GCESecurityGroup
 from .resources import GCESecurityGroupRule
 from .resources import GCESecurityGroupRule
 
 
-
 class GCESecurityService(BaseSecurityService):
 class GCESecurityService(BaseSecurityService):
 
 
     def __init__(self, provider):
     def __init__(self, provider):
@@ -301,16 +305,106 @@ class GCERegionService(BaseRegionService):
         return self.get(self.provider.region_name)
         return self.get(self.provider.region_name)
 
 
 
 
+class GCEImageService(BaseImageService):
+
+    def __init__(self, provider):
+        super(GCEImageService, self).__init__(provider)
+        self._public_images = None
+
+    _PUBLIC_IMAGE_PROJECTS = ['centos-cloud', 'coreos-cloud', 'debian-cloud',
+                             'opensuse-cloud', 'ubuntu-os-cloud']
+
+    def _retrieve_public_images(self):
+        if self._public_images is not None:
+            return
+        self._public_images = []
+        for project in GCEImageService._PUBLIC_IMAGE_PROJECTS:
+            try:
+                response = self.provider.gce_compute \
+                                        .images() \
+                                        .list(project=project) \
+                                        .execute()
+            except googleapiclient.errors.HttpError as http_error:
+                cb.log.warning("googleapiclient.errors.HttpError: {0}".format(
+                    http_error))
+            if 'items' in response:
+                self._public_images.extend(
+                    [GCEMachineImage(self.provider, image) for image
+                     in response['items']])
+
+    def get(self, image_id):
+        """
+        Returns an Image given its id
+        """
+        try:
+            image = self.provider.gce_compute \
+                                  .images() \
+                                  .get(project=self.provider.project_name,
+                                       image=image_id) \
+                                  .execute()
+            if image:
+                return GCEMachineImage(self.provider, image)
+        except TypeError as type_error:
+            # The API will throw an TypeError, if parameter `image` does not
+            # match the pattern "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?".
+            cb.log.warning("TypeError: {0}".format(type_error))
+        except googleapiclient.errors.HttpError as http_error:
+            # If the image is not found in project-specific private images,
+            # look for this image in public images.
+            self._retrieve_public_images()
+            for public_image in self._public_images:
+                if public_image.id == image_id:
+                    return public_image
+            cb.log.warning(
+                "googleapiclient.errors.HttpError: {0}".format(http_error))
+        return None
+
+    def find(self, name, limit=None, marker=None):
+        """
+        Searches for an image by a given list of attributes
+        """
+        filters = {'name': name}
+        # Retrieve all available images by setting limit to sys.maxsize
+        images = [image for image in self if image.name == filters['name']]
+        return ClientPagedResultList(self.provider, images,
+                                     limit=limit, marker=marker)
+
+    def list(self, limit=None, marker=None):
+        """
+        List all images.
+        """
+        self._retrieve_public_images()
+        images = []
+        if (self.provider.project_name not in
+            GCEImageService._PUBLIC_IMAGE_PROJECTS):
+            try:
+                response = self.provider \
+                               .gce_compute \
+                               .images() \
+                               .list(project=self.provider.project_name) \
+                               .execute()
+                if 'items' in response:
+                    images = [GCEMachineImage(self.provider, image) for image
+                              in response['items']]
+            except googleapiclient.errors.HttpError as http_error:
+                cb.log.warning(
+                    "googleapiclient.errors.HttpError: {0}".format(http_error))
+        images.extend(self._public_images)
+        return ClientPagedResultList(self.provider, images,
+                                     limit=limit, marker=marker)
+
+
 class GCEComputeService(BaseComputeService):
 class GCEComputeService(BaseComputeService):
     # TODO: implement GCEComputeService
     # TODO: implement GCEComputeService
     def __init__(self, provider):
     def __init__(self, provider):
         super(GCEComputeService, self).__init__(provider)
         super(GCEComputeService, self).__init__(provider)
         self._instance_type_svc = GCEInstanceTypesService(self.provider)
         self._instance_type_svc = GCEInstanceTypesService(self.provider)
         self._region_svc = GCERegionService(self.provider)
         self._region_svc = GCERegionService(self.provider)
+        self._images_svc = GCEImageService(self.provider)
 
 
     @property
     @property
     def images(self):
     def images(self):
-        raise NotImplementedError("To be implemented")
+        return self._images_svc
 
 
     @property
     @property
     def instance_types(self):
     def instance_types(self):