nuwan_ag 10 лет назад
Родитель
Сommit
3eac3f1935

+ 23 - 0
cloudbridge/providers/aws/resources.py

@@ -11,6 +11,7 @@ from cloudbridge.providers.base import BaseInstance
 from cloudbridge.providers.base import BaseInstanceType
 from cloudbridge.providers.base import BaseKeyPair
 from cloudbridge.providers.base import BaseMachineImage
+from cloudbridge.providers.base import BaseRegion
 from cloudbridge.providers.base import BaseSecurityGroup
 from cloudbridge.providers.base import BaseSecurityGroupRule
 from cloudbridge.providers.base import BaseSnapshot
@@ -656,3 +657,25 @@ class AWSContainer(Container):
 
     def __repr__(self):
         return "<CB-AWSContainer: {0}>".format(self.name)
+
+
+class AWSRegion(BaseRegion):
+
+    def __init__(self, provider, aws_region):
+        self._provider = provider
+        self._aws_region = aws_region
+
+    @property
+    def id(self):
+        return self._aws_region.name
+
+    @property
+    def name(self):
+        return self._aws_region.name
+
+    @property
+    def zones(self):
+        """
+        Accesss information about placement zones within this region.
+        """
+        pass

+ 26 - 10
cloudbridge/providers/aws/services.py

@@ -11,6 +11,7 @@ from cloudbridge.providers.base import BaseInstanceService
 from cloudbridge.providers.base import BaseInstanceTypesService
 from cloudbridge.providers.base import BaseKeyPairService
 from cloudbridge.providers.base import BaseObjectStoreService
+from cloudbridge.providers.base import BaseRegionService
 from cloudbridge.providers.base import BaseSecurityGroupService
 from cloudbridge.providers.base import BaseSecurityService
 from cloudbridge.providers.base import BaseSnapshotService
@@ -26,6 +27,7 @@ from .resources import AWSInstance
 from .resources import AWSInstanceType
 from .resources import AWSKeyPair
 from .resources import AWSMachineImage
+from .resources import AWSRegion
 from .resources import AWSSecurityGroup
 from .resources import AWSSnapshot
 from .resources import AWSVolume
@@ -381,6 +383,7 @@ class AWSComputeService(BaseComputeService):
         super(AWSComputeService, self).__init__(provider)
         self._instance_type_svc = AWSInstanceTypesService(self.provider)
         self._instance_svc = AWSInstanceService(self.provider)
+        self._region_svc = AWSRegionService(self.provider)
 
     @property
     def instance_types(self):
@@ -390,15 +393,9 @@ class AWSComputeService(BaseComputeService):
     def instances(self):
         return self._instance_svc
 
-    def list_regions(self):
-        """
-        List all data center regions for this provider.
-
-        :rtype: ``list`` of :class:`.Region`
-        :return: list of Region objects
-        """
-        raise NotImplementedError(
-            'list_regions not implemented by this provider')
+    @property
+    def regions(self):
+        return self._region_svc
 
 
 class AWSInstanceService(BaseInstanceService):
@@ -483,7 +480,7 @@ class AWSInstanceTypesService(BaseInstanceTypesService):
     @property
     def instance_data(self):
         """
-        TODO: Neeeds a caching function with timeout
+        TODO: Needs a caching function with timeout
         """
         r = requests.get(self.provider.config.get(
             "aws_instance_info_url", AWS_INSTANCE_DATA_DEFAULT_URL))
@@ -496,3 +493,22 @@ class AWSInstanceTypesService(BaseInstanceTypesService):
     def find_by_name(self, name):
         return next(
             (itype for itype in self.list() if itype.name == name), None)
+
+
+class AWSRegionService(BaseRegionService):
+
+    def __init__(self, provider):
+        super(AWSRegionService, self).__init__(provider)
+
+    def get(self, region_id):
+        region = self.provider.ec2_conn.get_all_regions(
+            region_names=[region_id])
+        if region:
+            return AWSRegion(self.provider, region[0])
+        else:
+            return None
+
+    def list(self):
+        regions = self.provider.ec2_conn.get_all_regions()
+        return [AWSRegion(self.provider, region)
+                for region in regions]

+ 29 - 5
cloudbridge/providers/base.py

@@ -13,6 +13,7 @@ from cloudbridge.providers.interfaces import KeyPair
 from cloudbridge.providers.interfaces import MachineImage
 from cloudbridge.providers.interfaces import MachineImageState
 from cloudbridge.providers.interfaces import ObjectLifeCycleMixin
+from cloudbridge.providers.interfaces import Region
 from cloudbridge.providers.interfaces import SecurityGroup
 from cloudbridge.providers.interfaces import SecurityGroupRule
 from cloudbridge.providers.interfaces import Snapshot
@@ -20,16 +21,17 @@ from cloudbridge.providers.interfaces import SnapshotState
 from cloudbridge.providers.interfaces import Volume
 from cloudbridge.providers.interfaces import VolumeState
 from cloudbridge.providers.interfaces import WaitStateException
-from cloudbridge.providers.interfaces.services import InstanceTypesService
-from cloudbridge.providers.interfaces.services import KeyPairService
-from cloudbridge.providers.interfaces.services import ObjectStoreService
-from cloudbridge.providers.interfaces.services import SecurityGroupService
-from cloudbridge.providers.interfaces.services import SecurityService
 from cloudbridge.providers.interfaces.services import BlockStoreService
 from cloudbridge.providers.interfaces.services import ComputeService
 from cloudbridge.providers.interfaces.services import ImageService
 from cloudbridge.providers.interfaces.services import InstanceService
+from cloudbridge.providers.interfaces.services import InstanceTypesService
+from cloudbridge.providers.interfaces.services import KeyPairService
+from cloudbridge.providers.interfaces.services import ObjectStoreService
 from cloudbridge.providers.interfaces.services import ProviderService
+from cloudbridge.providers.interfaces.services import RegionService
+from cloudbridge.providers.interfaces.services import SecurityGroupService
+from cloudbridge.providers.interfaces.services import SecurityService
 from cloudbridge.providers.interfaces.services import SnapshotService
 from cloudbridge.providers.interfaces.services import VolumeService
 
@@ -289,6 +291,22 @@ class BaseSecurityGroupRule(SecurityGroupRule):
             self.ip_protocol, self.from_port, self.to_port)
 
 
+class BaseRegion(Region):
+
+    def __init__(self, provider, region):
+        self._provider = provider
+        self._region = region
+
+    def __repr__(self):
+        return "<CB-{0}: {1}>".format(self.__class__.__name__,
+                                      self.name)
+
+    def __eq__(self, other):
+        if isinstance(other, Region):
+            return self._provider == other._provider and \
+                self.id == other.id
+
+
 class BaseProviderService(ProviderService):
 
     def __init__(self, provider):
@@ -363,3 +381,9 @@ class BaseInstanceService(InstanceService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseInstanceService, self).__init__(provider)
+
+
+class BaseRegionService(RegionService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseRegionService, self).__init__(provider)

+ 1 - 0
cloudbridge/providers/interfaces/__init__.py

@@ -28,6 +28,7 @@ from .services import InstanceTypesService
 from .services import KeyPairService
 from .services import ObjectStoreService
 from .services import ProviderService
+from .services import RegionService
 from .services import SecurityGroupService
 from .services import SecurityService
 from .services import SnapshotService

+ 15 - 5
cloudbridge/providers/interfaces/resources.py

@@ -556,6 +556,16 @@ class Region(object):
     """
     __metaclass__ = ABCMeta
 
+    @abstractproperty
+    def id(self):
+        """
+        The id for this region
+
+        :rtype: str
+        :return: Id of the region.
+        """
+        pass
+
     @abstractproperty
     def name(self):
         """
@@ -566,13 +576,13 @@ class Region(object):
         """
         pass
 
-    @abstractmethod
-    def list_zones(self):
+    @abstractproperty
+    def zones(self):
         """
-        List all available placement zones within this region.
+        Accesss information about placement zones within this region.
 
-        :rtype: list
-        :return: List of all the available placement zones.
+        :rtype: iterable
+        :return: Iterable of  available placement zones in this region.
         """
         pass
 

+ 34 - 5
cloudbridge/providers/interfaces/services.py

@@ -49,13 +49,13 @@ class ComputeService(ProviderService):
         """
         pass
 
-    @abstractmethod
-    def list_regions(self):
+    @abstractproperty
+    def regions(self):
         """
-        List all data center regions for this provider.
+        Provides access to all Region related services in this provider.
 
-        :rtype: ``list`` of :class:`.Region`
-        :return: list of Region objects
+        :rtype: ``object`` of :class:`.RegionService`
+        :return:  a RegionService object
         """
         pass
 
@@ -570,3 +570,32 @@ class InstanceTypesService(object):
         :return: an Instance object
         """
         pass
+
+
+class RegionService(ProviderService):
+
+    """
+    Base interface for a Region service
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def get(self, region_id):
+        """
+        Returns a region given its id. Returns None if the region
+        does not exist.
+
+        :rtype: ``object`` of :class:`.Region`
+        :return:  a Region instance
+        """
+        pass
+
+    @abstractmethod
+    def list(self):
+        """
+        List all regions.
+
+        :rtype: ``list`` of :class:`.Region`
+        :return:  list of region objects
+        """
+        pass

+ 12 - 5
cloudbridge/providers/openstack/resources.py

@@ -10,6 +10,7 @@ from cloudbridge.providers.base import BaseInstance
 from cloudbridge.providers.base import BaseInstanceType
 from cloudbridge.providers.base import BaseKeyPair
 from cloudbridge.providers.base import BaseMachineImage
+from cloudbridge.providers.base import BaseRegion
 from cloudbridge.providers.base import BaseSecurityGroup
 from cloudbridge.providers.base import BaseSecurityGroupRule
 from cloudbridge.providers.base import BaseSnapshot
@@ -19,7 +20,6 @@ from cloudbridge.providers.interfaces import ContainerObject
 from cloudbridge.providers.interfaces import InstanceState
 from cloudbridge.providers.interfaces import MachineImageState
 from cloudbridge.providers.interfaces import PlacementZone
-from cloudbridge.providers.interfaces import Region
 from cloudbridge.providers.interfaces import SnapshotState
 from cloudbridge.providers.interfaces import VolumeState
 
@@ -333,18 +333,25 @@ class OpenStackInstance(BaseInstance):
         return "<CB-OSInstance: {0} ({1})>".format(self.name, self.instance_id)
 
 
-class OpenStackRegion(Region):
+class OpenStackRegion(BaseRegion):
 
     def __init__(self, provider, os_region):
         self._provider = provider
         self._os_region = os_region
 
+    @property
+    def id(self):
+        return self._os_region
+
     @property
     def name(self):
-        return self._os_region.zoneName
+        return self._os_region
 
-    def __repr__(self):
-        return "<CB-OSRegion: {0}>".format(self.name)
+    @property
+    def zones(self):
+        # detailed must be set to ``False`` because the (default) ``True``
+        # value requires Admin privileges
+        return self._provider.nova.availability_zones.list(detailed=False)
 
 
 class OpenStackVolume(BaseVolume):

+ 30 - 8
cloudbridge/providers/openstack/services.py

@@ -11,6 +11,7 @@ from cloudbridge.providers.base import BaseInstanceService
 from cloudbridge.providers.base import BaseInstanceTypesService
 from cloudbridge.providers.base import BaseKeyPairService
 from cloudbridge.providers.base import BaseObjectStoreService
+from cloudbridge.providers.base import BaseRegionService
 from cloudbridge.providers.base import BaseSecurityGroupService
 from cloudbridge.providers.base import BaseSecurityService
 from cloudbridge.providers.base import BaseSnapshotService
@@ -388,12 +389,38 @@ class OpenStackObjectStoreService(BaseObjectStoreService):
         return self.get(name)
 
 
+class OpenStackRegionService(BaseRegionService):
+
+    def __init__(self, provider):
+        super(OpenStackRegionService, self).__init__(provider)
+
+    def get(self, region_id):
+        regions = [endpoint.get('region') or endpoint.get('region_id')
+                   for svc in self.provider.keystone.service_catalog.get_data()
+                   for endpoint in svc.get('endpoints', [])]
+        region = [region for region in regions
+                  if region == region_id]
+        if region:
+            return OpenStackRegion(self.provider, region[0])
+        else:
+            return None
+
+    def list(self):
+        regions = [endpoint.get('region') or endpoint.get('region_id')
+                   for svc in self.provider.keystone.service_catalog.get_data()
+                   for endpoint in svc.get('endpoints', [])]
+        regions = [region for region in regions if region]
+        return [OpenStackRegion(self.provider, region)
+                for region in regions]
+
+
 class OpenStackComputeService(BaseComputeService):
 
     def __init__(self, provider):
         super(OpenStackComputeService, self).__init__(provider)
         self._instance_type_svc = OpenStackInstanceTypesService(self._provider)
         self._instance_svc = OpenStackInstanceService(self._provider)
+        self._region_svc = OpenStackRegionService(self.provider)
 
     @property
     def instance_types(self):
@@ -403,14 +430,9 @@ class OpenStackComputeService(BaseComputeService):
     def instances(self):
         return self._instance_svc
 
-    def list_regions(self):
-        """
-        List all data center regions.
-        """
-        # detailed must be set to ``False`` because the (default) ``True``
-        # value requires Admin priviledges
-        regions = self._provider.nova.availability_zones.list(detailed=False)
-        return [OpenStackRegion(self._provider, region) for region in regions]
+    @property
+    def regions(self):
+        return self._region_svc
 
 
 class OpenStackInstanceService(BaseInstanceService):

+ 2 - 0
test/__init__.py

@@ -36,6 +36,7 @@ from test.test_provider_image_service import ProviderImageServiceTestCase
 from test.test_provider_interface import ProviderInterfaceTestCase
 from test.test_provider_object_store_service import \
     ProviderObjectStoreServiceTestCase
+from test.test_provider_region_service import ProviderRegionServiceTestCase
 from test.test_provider_security_service import ProviderSecurityServiceTestCase
 
 
@@ -44,6 +45,7 @@ PROVIDER_TESTS = [
     ProviderSecurityServiceTestCase,
     ProviderInstanceTypesServiceTestCase,
     ProviderComputeServiceTestCase,
+    ProviderRegionServiceTestCase,
     ProviderImageServiceTestCase,
     ProviderBlockStoreServiceTestCase,
     ProviderObjectStoreServiceTestCase

+ 2 - 2
test/test_provider_object_store_service.py

@@ -93,13 +93,13 @@ class ProviderObjectStoreServiceTestCase(ProviderTestBase):
             obj = test_container.create_object(obj_name)
 
             with helpers.exception_action(lambda: obj.delete()):
-                content = "Hello World. Here's some content"
+                content = b"Hello World. Here's some content"
                 # TODO: Upload and download methods accept different parameter
                 # types. Need to make this consistent - possibly provider
                 # multiple methods like upload_from_file, from_stream etc.
                 obj.upload(content)
                 target_stream = BytesIO()
                 obj.download(target_stream)
-                self.assertEqual(str(target_stream.getvalue()), content)
+                self.assertEqual(target_stream.getvalue(), content)
                 obj.delete()
             test_container.delete()