nuwan_ag 10 лет назад
Родитель
Сommit
84f660ad64

+ 5 - 0
cloudbridge/providers/aws/impl.py

@@ -49,6 +49,11 @@ class AWSCloudProviderV1(BaseCloudProvider):
         self._block_store = AWSBlockStoreService(self)
         self._object_store = AWSObjectStoreService(self)
 
+    @property
+    def account(self):
+        raise NotImplementedError(
+            'account not implemented by this provider')
+
     @property
     def compute(self):
         return self._compute

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

@@ -463,6 +463,16 @@ class AWSSnapshot(BaseSnapshot):
         """
         self._snapshot.delete()
 
+    def create_volume(self, placement, size=None, volume_type=None, iops=None):
+        raise NotImplementedError(
+            'create_volume not implemented by this provider')
+
+    def share(self, user_ids=None):
+        raise NotImplementedError('share not implemented by this provider')
+
+    def unshare(self, user_ids=None):
+        raise NotImplementedError('share not implemented by this provider')
+
     def __repr__(self):
         return "<CB-AWSSnapshot: {0} ({1})>".format(self.snapshot_id,
                                                     self.name)

+ 85 - 78
cloudbridge/providers/aws/services.py

@@ -4,21 +4,22 @@ Services implemented by the AWS provider.
 from boto.exception import EC2ResponseError
 import requests
 
-from cloudbridge.providers.interfaces import BlockStoreService
-from cloudbridge.providers.interfaces import ComputeService
-from cloudbridge.providers.interfaces import ImageService
+from cloudbridge.providers.base import BaseBlockStoreService
+from cloudbridge.providers.base import BaseComputeService
+from cloudbridge.providers.base import BaseImageService
+from cloudbridge.providers.base import BaseInstanceTypesService
+from cloudbridge.providers.base import BaseKeyPairService
+from cloudbridge.providers.base import BaseObjectStoreService
+from cloudbridge.providers.base import BaseSecurityGroupService
+from cloudbridge.providers.base import BaseSecurityService
+from cloudbridge.providers.base import BaseSnapshotService
+from cloudbridge.providers.base import BaseVolumeService
+
 from cloudbridge.providers.interfaces import InstanceType
-from cloudbridge.providers.interfaces import InstanceTypesService
 from cloudbridge.providers.interfaces import KeyPair
-from cloudbridge.providers.interfaces import KeyPairService
 from cloudbridge.providers.interfaces import MachineImage
-from cloudbridge.providers.interfaces import ObjectStoreService
 from cloudbridge.providers.interfaces import PlacementZone
 from cloudbridge.providers.interfaces import SecurityGroup
-from cloudbridge.providers.interfaces import SecurityGroupService
-from cloudbridge.providers.interfaces import SecurityService
-from cloudbridge.providers.interfaces import SnapshotService
-from cloudbridge.providers.interfaces import VolumeService
 
 from .resources import AWSContainer
 from .resources import AWSInstance
@@ -30,10 +31,10 @@ from .resources import AWSSnapshot
 from .resources import AWSVolume
 
 
-class AWSSecurityService(SecurityService):
+class AWSSecurityService(BaseSecurityService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSSecurityService, self).__init__(provider)
 
         # Initialize provider services
         self._key_pairs = AWSKeyPairService(provider)
@@ -60,10 +61,10 @@ class AWSSecurityService(SecurityService):
         return self._security_groups
 
 
-class AWSKeyPairService(KeyPairService):
+class AWSKeyPairService(BaseKeyPairService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSKeyPairService, self).__init__(provider)
 
     def list(self):
         """
@@ -72,8 +73,8 @@ class AWSKeyPairService(KeyPairService):
         :rtype: ``list`` of :class:`.KeyPair`
         :return:  list of KeyPair objects
         """
-        key_pairs = self._provider.ec2_conn.get_all_key_pairs()
-        return [AWSKeyPair(self._provider, kp) for kp in key_pairs]
+        key_pairs = self.provider.ec2_conn.get_all_key_pairs()
+        return [AWSKeyPair(self.provider, kp) for kp in key_pairs]
 
     def create(self, name):
         """
@@ -85,9 +86,9 @@ class AWSKeyPairService(KeyPairService):
         :rtype: ``object`` of :class:`.KeyPair`
         :return:  A keypair instance or None if one was not be created.
         """
-        kp = self._provider.ec2_conn.create_key_pair(name)
+        kp = self.provider.ec2_conn.create_key_pair(name)
         if kp:
-            return AWSKeyPair(self._provider, kp)
+            return AWSKeyPair(self.provider, kp)
         return None
 
     def delete(self, name):
@@ -102,17 +103,17 @@ class AWSKeyPairService(KeyPairService):
                   that this implies that the key may not have been deleted by
                   this method but instead has not existed in the first place.
         """
-        for kp in self._provider.ec2_conn.get_all_key_pairs():
+        for kp in self.provider.ec2_conn.get_all_key_pairs():
             if kp.name == name:
                 kp.delete()
                 return True
         return True
 
 
-class AWSSecurityGroupService(SecurityGroupService):
+class AWSSecurityGroupService(BaseSecurityGroupService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSSecurityGroupService, self).__init__(provider)
 
     def list(self):
         """
@@ -121,8 +122,8 @@ class AWSSecurityGroupService(SecurityGroupService):
         :rtype: ``list`` of :class:`.SecurityGroup`
         :return:  list of SecurityGroup objects
         """
-        security_groups = self._provider.ec2_conn.get_all_security_groups()
-        return [AWSSecurityGroup(self._provider, sg) for sg in security_groups]
+        security_groups = self.provider.ec2_conn.get_all_security_groups()
+        return [AWSSecurityGroup(self.provider, sg) for sg in security_groups]
 
     def create(self, name, description):
         """
@@ -137,9 +138,9 @@ class AWSSecurityGroupService(SecurityGroupService):
         :rtype: ``object`` of :class:`.SecurityGroup`
         :return:  A SecurityGroup instance or ``None`` if one was not created.
         """
-        sg = self._provider.ec2_conn.create_security_group(name, description)
+        sg = self.provider.ec2_conn.create_security_group(name, description)
         if sg:
-            return AWSSecurityGroup(self._provider, sg)
+            return AWSSecurityGroup(self.provider, sg)
         return None
 
     def get(self, group_names=None, group_ids=None):
@@ -161,11 +162,11 @@ class AWSSecurityGroupService(SecurityGroupService):
         found.
         """
         try:
-            security_groups = self._provider.ec2_conn.get_all_security_groups(
+            security_groups = self.provider.ec2_conn.get_all_security_groups(
                 groupnames=group_names, group_ids=group_ids)
         except EC2ResponseError:
             security_groups = []
-        return [AWSSecurityGroup(self._provider, sg) for sg in security_groups]
+        return [AWSSecurityGroup(self.provider, sg) for sg in security_groups]
 
     def delete(self, group_id):
         """
@@ -181,7 +182,7 @@ class AWSSecurityGroupService(SecurityGroupService):
                   the first place.
         """
         try:
-            for sg in self._provider.ec2_conn.get_all_security_groups(
+            for sg in self.provider.ec2_conn.get_all_security_groups(
                     group_ids=[group_id]):
                 try:
                     sg.delete()
@@ -192,14 +193,14 @@ class AWSSecurityGroupService(SecurityGroupService):
         return True
 
 
-class AWSBlockStoreService(BlockStoreService):
+class AWSBlockStoreService(BaseBlockStoreService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSBlockStoreService, self).__init__(provider)
 
         # Initialize provider services
-        self._volumes = AWSVolumeService(self._provider)
-        self._snapshots = AWSSnapshotService(self._provider)
+        self._volumes = AWSVolumeService(self.provider)
+        self._snapshots = AWSSnapshotService(self.provider)
 
     @property
     def volumes(self):
@@ -210,17 +211,17 @@ class AWSBlockStoreService(BlockStoreService):
         return self._snapshots
 
 
-class AWSVolumeService(VolumeService):
+class AWSVolumeService(BaseVolumeService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSVolumeService, self).__init__(provider)
 
     def get_volume(self, volume_id):
         """
         Returns a volume given its id.
         """
-        vols = self._provider.ec2_conn.get_all_volumes(volume_ids=[volume_id])
-        return AWSVolume(self._provider, vols[0]) if vols else None
+        vols = self.provider.ec2_conn.get_all_volumes(volume_ids=[volume_id])
+        return AWSVolume(self.provider, vols[0]) if vols else None
 
     def find_volume(self, name):
         """
@@ -233,8 +234,8 @@ class AWSVolumeService(VolumeService):
         """
         List all volumes.
         """
-        return [AWSVolume(self._provider, vol)
-                for vol in self._provider.ec2_conn.get_all_volumes()]
+        return [AWSVolume(self.provider, vol)
+                for vol in self.provider.ec2_conn.get_all_volumes()]
 
     def create_volume(self, name, size, zone, snapshot=None):
         """
@@ -244,28 +245,28 @@ class AWSVolumeService(VolumeService):
         snapshot_id = snapshot.snapshot_id if isinstance(
             zone, AWSSnapshot) and snapshot else snapshot
 
-        ec2_vol = self._provider.ec2_conn.create_volume(
+        ec2_vol = self.provider.ec2_conn.create_volume(
             size,
             zone_name,
             snapshot=snapshot_id)
-        cb_vol = AWSVolume(self._provider, ec2_vol)
+        cb_vol = AWSVolume(self.provider, ec2_vol)
         cb_vol.name = name
         return cb_vol
 
 
-class AWSObjectStoreService(ObjectStoreService):
+class AWSObjectStoreService(BaseObjectStoreService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSObjectStoreService, self).__init__(provider)
 
     def get_container(self, container_id):
         """
         Returns a container given its id. Returns None if the container
         does not exist.
         """
-        bucket = self._provider.s3_conn.lookup(container_id)
+        bucket = self.provider.s3_conn.lookup(container_id)
         if bucket:
-            return AWSContainer(self._provider, bucket)
+            return AWSContainer(self.provider, bucket)
         else:
             return None
 
@@ -280,31 +281,31 @@ class AWSObjectStoreService(ObjectStoreService):
         """
         List all containers.
         """
-        buckets = self._provider.s3_conn.get_all_buckets()
-        return [AWSContainer(self._provider, bucket) for bucket in buckets]
+        buckets = self.provider.s3_conn.get_all_buckets()
+        return [AWSContainer(self.provider, bucket) for bucket in buckets]
 
     def create_container(self, name, location=None):
         """
         Create a new container.
         """
-        bucket = self._provider.s3_conn.create_bucket(
+        bucket = self.provider.s3_conn.create_bucket(
             name,
             location=location if location else '')
-        return AWSContainer(self._provider, bucket)
+        return AWSContainer(self.provider, bucket)
 
 
-class AWSSnapshotService(SnapshotService):
+class AWSSnapshotService(BaseSnapshotService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSSnapshotService, self).__init__(provider)
 
     def get_snapshot(self, snapshot_id):
         """
         Returns a snapshot given its id.
         """
-        snaps = self._provider.ec2_conn.get_all_snapshots(
+        snaps = self.provider.ec2_conn.get_all_snapshots(
             snapshot_ids=[snapshot_id])
-        return AWSSnapshot(self._provider, snaps[0]) if snaps else None
+        return AWSSnapshot(self.provider, snaps[0]) if snaps else None
 
     def find_snapshot(self, name):
         """
@@ -319,9 +320,9 @@ class AWSSnapshotService(SnapshotService):
         """
         # TODO: get_all_images returns too many images - some kind of filtering
         # abilities are needed. Forced to "self" for now
-        return [AWSSnapshot(self._provider, snap)
+        return [AWSSnapshot(self.provider, snap)
                 for snap in
-                self._provider.ec2_conn.get_all_snapshots(owner="self")]
+                self.provider.ec2_conn.get_all_snapshots(owner="self")]
 
     def create_snapshot(self, name, volume, description=None):
         """
@@ -331,27 +332,27 @@ class AWSSnapshotService(SnapshotService):
             volume,
             AWSVolume) else volume
 
-        ec2_snap = self._provider.ec2_conn.create_snapshot(
+        ec2_snap = self.provider.ec2_conn.create_snapshot(
             volume_id,
             description=description)
-        cb_snap = AWSSnapshot(self._provider, ec2_snap)
+        cb_snap = AWSSnapshot(self.provider, ec2_snap)
         cb_snap.name = name
         return cb_snap
 
 
-class AWSImageService(ImageService):
+class AWSImageService(BaseImageService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSImageService, self).__init__(provider)
 
     def get_image(self, image_id):
         """
         Returns an Image given its id
         """
         try:
-            image = self._provider.ec2_conn.get_image(image_id)
+            image = self.provider.ec2_conn.get_image(image_id)
             if image:
-                return AWSMachineImage(self._provider, image)
+                return AWSMachineImage(self.provider, image)
         except EC2ResponseError:
             pass
 
@@ -370,20 +371,16 @@ class AWSImageService(ImageService):
         """
         # TODO: get_all_images returns too many images - some kind of filtering
         # abilities are needed. Forced to "self" for now
-        images = self._provider.ec2_conn.get_all_images(owners="self")
-        return [AWSMachineImage(self._provider, image) for image in images]
+        images = self.provider.ec2_conn.get_all_images(owners="self")
+        return [AWSMachineImage(self.provider, image) for image in images]
 
 
-class AWSComputeService(ComputeService):
+class AWSComputeService(BaseComputeService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSComputeService, self).__init__(provider)
         self._instance_types = AWSInstanceTypesService(self.provider)
 
-    @property
-    def provider(self):
-        return self._provider
-
     @property
     def instance_types(self):
         return self._instance_types
@@ -411,14 +408,14 @@ class AWSComputeService(ComputeService):
         else:
             security_groups_list = None
 
-        reservation = self._provider.ec2_conn.run_instances(
+        reservation = self.provider.ec2_conn.run_instances(
             image_id=image_id, instance_type=instance_size,
             min_count=1, max_count=1, placement=zone_name,
             key_name=keypair_name, security_groups=security_groups_list,
             user_data=user_data
         )
         if reservation:
-            instance = AWSInstance(self._provider, reservation.instances[0])
+            instance = AWSInstance(self.provider, reservation.instances[0])
             instance.name = name
         return instance
 
@@ -427,10 +424,10 @@ class AWSComputeService(ComputeService):
         Returns an instance given its id. Returns None
         if the object does not exist.
         """
-        reservation = self._provider.ec2_conn.get_all_reservations(
+        reservation = self.provider.ec2_conn.get_all_reservations(
             instance_ids=[instance_id])
         if reservation:
-            return AWSInstance(self._provider, reservation[0].instances[0])
+            return AWSInstance(self.provider, reservation[0].instances[0])
         else:
             return None
 
@@ -448,32 +445,42 @@ class AWSComputeService(ComputeService):
         """
         List all instances.
         """
-        reservations = self._provider.ec2_conn.get_all_reservations()
-        return [AWSInstance(self._provider, inst)
+        reservations = self.provider.ec2_conn.get_all_reservations()
+        return [AWSInstance(self.provider, inst)
                 for res in reservations
                 for inst in res.instances]
 
+    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')
+
 
 AWS_INSTANCE_DATA_DEFAULT_URL = "https://swift.rc.nectar.org.au:8888/v1/" \
                                 "AUTH_377/cloud-bridge/aws/instance_data.json"
 
 
-class AWSInstanceTypesService(InstanceTypesService):
+class AWSInstanceTypesService(BaseInstanceTypesService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(AWSInstanceTypesService, self).__init__(provider)
 
     @property
     def instance_data(self):
         """
         TODO: Neeeds a caching function with timeout
         """
-        r = requests.get(self._provider.config.get(
+        r = requests.get(self.provider.config.get(
             "aws_instance_info_url", AWS_INSTANCE_DATA_DEFAULT_URL))
         return r.json()
 
     def list(self):
-        return [AWSInstanceType(self._provider, inst_data)
+        return [AWSInstanceType(self.provider, inst_data)
                 for inst_data in self.instance_data]
 
     def find_by_name(self, name):

+ 74 - 0
cloudbridge/providers/base.py

@@ -20,6 +20,10 @@ 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 ImageService,\
+    ProviderService, ComputeService, VolumeService, SnapshotService,\
+    BlockStoreService, ObjectStoreService, SecurityService, KeyPairService,\
+    SecurityGroupService, InstanceTypesService
 
 
 log = logging.getLogger(__name__)
@@ -275,3 +279,73 @@ class BaseSecurityGroupRule(SecurityGroupRule):
     def __repr__(self):
         return "<CBSecurityGroupRule: IP: {0}; from: {1}; to: {2}>".format(
             self.ip_protocol, self.from_port, self.to_port)
+
+
+class BaseProviderService(ProviderService):
+
+    def __init__(self, provider):
+        self._provider = provider
+
+    @property
+    def provider(self):
+        return self._provider
+
+
+class BaseComputeService(ComputeService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseComputeService, self).__init__(provider)
+
+
+class BaseVolumeService(VolumeService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseVolumeService, self).__init__(provider)
+
+
+class BaseSnapshotService(SnapshotService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseSnapshotService, self).__init__(provider)
+
+
+class BaseBlockStoreService(BlockStoreService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseBlockStoreService, self).__init__(provider)
+
+
+class BaseImageService(ImageService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseImageService, self).__init__(provider)
+
+
+class BaseObjectStoreService(ObjectStoreService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseObjectStoreService, self).__init__(provider)
+
+
+class BaseSecurityService(SecurityService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseSecurityService, self).__init__(provider)
+
+
+class BaseKeyPairService(KeyPairService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseKeyPairService, self).__init__(provider)
+
+
+class BaseSecurityGroupService(SecurityGroupService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseSecurityGroupService, self).__init__(provider)
+
+
+class BaseInstanceTypesService(InstanceTypesService, BaseProviderService):
+
+    def __init__(self, provider):
+        super(BaseInstanceTypesService, self).__init__(provider)

+ 28 - 31
cloudbridge/providers/interfaces/impl.py

@@ -1,14 +1,18 @@
 """
 Specification for a provider interface
 """
+from abc import ABCMeta, abstractmethod, abstractproperty
 
 
 class CloudProvider(object):
 
+    __metaclass__ = ABCMeta
+
     """
     Base interface for a cloud provider
     """
 
+    @abstractmethod
     def __init__(self, config):
         """
         Create a new provider implementation given a dictionary of
@@ -22,10 +26,9 @@ class CloudProvider(object):
         :rtype: ``object`` of :class:`.CloudProvider`
         :return:  a concrete provider instance
         """
-        raise NotImplementedError(
-            '__init__ not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def config(self):
         """
         Returns the config object associated with this provider.
@@ -33,9 +36,8 @@ class CloudProvider(object):
         :rtype: ``object``
         :return:  The config object used to initialize this provider
         """
-        raise NotImplementedError(
-            'CloudProvider.config not implemented by this provider')
 
+    @abstractmethod
     def has_service(self, service_type):
         """
         Checks whether this provider supports a given service.
@@ -46,10 +48,9 @@ class CloudProvider(object):
         :rtype: bool
         :return: ``True`` if the service type is supported.
         """
-        raise NotImplementedError(
-            'CloudProvider.has_service not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def account(self):
         """
         Provides access to all user account related services in this provider.
@@ -58,10 +59,9 @@ class CloudProvider(object):
         :rtype: ``object`` of :class:`.ComputeService`
         :return:  a ComputeService object
         """
-        raise NotImplementedError(
-            'CloudProvider.Compute not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def compute(self):
         """
         Provides access to all compute related services in this provider.
@@ -69,10 +69,9 @@ class CloudProvider(object):
         :rtype: ``object`` of :class:`.ComputeService`
         :return:  a ComputeService object
         """
-        raise NotImplementedError(
-            'CloudProvider.compute not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def images(self):
         """
         Provides access to all Image related services in this provider.
@@ -81,10 +80,9 @@ class CloudProvider(object):
         :rtype: ``object`` of :class:`.ImageService`
         :return: an ImageService object
         """
-        raise NotImplementedError(
-            'CloudProvider.images not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def security(self):
         """
         Provides access to keypair management and firewall control
@@ -92,10 +90,9 @@ class CloudProvider(object):
         :rtype: ``object`` of :class:`.SecurityService`
         :return: a SecurityService object
         """
-        raise NotImplementedError(
-            'CloudProvider.security not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def block_store(self):
         """
         Provides access to the volume and snapshot services in this
@@ -104,10 +101,9 @@ class CloudProvider(object):
         :rtype: ``object`` of :class:`.BlockStoreService`
         :return: a BlockStoreService object
         """
-        raise NotImplementedError(
-            'CloudProvider.block_store not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def object_store(self):
         """
         Provides access to object storage services in this provider.
@@ -115,8 +111,7 @@ class CloudProvider(object):
         :rtype: ``object`` of :class:`.ObjectStoreService`
         :return: an ObjectStoreService object
         """
-        raise NotImplementedError(
-            'CloudProvider.object_store not implemented by this provider')
+        pass
 
 
 class ContainerProvider(object):
@@ -124,14 +119,15 @@ class ContainerProvider(object):
     """
     Represents a container instance, such as Docker or LXC
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def create_container(self):
-        raise NotImplementedError(
-            'create_container not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete_container(self):
-        raise NotImplementedError(
-            'delete_container not implemented by this provider')
+        pass
 
 
 class DeploymentProvider(object):
@@ -139,10 +135,11 @@ class DeploymentProvider(object):
     """
     Represents a deployment provider, such as Ansible or Shell script provider
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def deploy(self, target):
         """
         Deploys on given target, where target is an Instance or Container
         """
-        raise NotImplementedError(
-            'deploy not implemented by this provider')
+        pass

+ 160 - 176
cloudbridge/providers/interfaces/resources.py

@@ -1,6 +1,7 @@
 """
 Specifications for data objects exposed through a provider or service
 """
+from abc import ABCMeta, abstractmethod, abstractproperty
 
 
 class CloudProviderServiceType(object):
@@ -48,7 +49,9 @@ class ObjectLifeCycleMixin(object):
     A refresh operation allows the object to synchronise its state with the
     service provider.
     """
-    @property
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
     def state(self):
         """
         Get the current state of this object.
@@ -56,17 +59,17 @@ class ObjectLifeCycleMixin(object):
         :rtype: ``str``
         :return: The current state as a string
         """
-        raise NotImplementedError(
-            'LifeCycleObject.state not implemented by this service')
+        pass
 
+    @abstractmethod
     def refresh(self):
         """
         Refreshs this object's state and synchronize it with the underlying
         service provider.
         """
-        raise NotImplementedError(
-            'LifeCycleObject.refresh not implemented by this service')
+        pass
 
+    @abstractmethod
     def wait_till_ready(self, timeout, interval):
         """
         Wait till the current object is in a ready state, which is any
@@ -87,8 +90,7 @@ class ObjectLifeCycleMixin(object):
         be thrown by the underlying service if the object cannot get into a
         ready state (e.g. If the object is in an error state)
         """
-        raise NotImplementedError(
-            'LifeCycleObject.wait_till_ready not implemented by this service')
+        pass
 
 
 class InstanceState(object):
@@ -118,7 +120,9 @@ class InstanceState(object):
 
 class Instance(ObjectLifeCycleMixin):
 
-    @property
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
     def instance_id(self):
         """
         Get the instance identifier.
@@ -126,10 +130,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: str
         :return: ID for this instance as returned by the cloud middleware.
         """
-        raise NotImplementedError(
-            'Instance.instance_id not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def name(self):
         """
         Get the instance name.
@@ -137,10 +140,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: str
         :return: Name for this instance as returned by the cloud middleware.
         """
-        raise NotImplementedError(
-            'Instance.name not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def public_ips(self):
         """
         Get all the public IP addresses for this instance.
@@ -148,10 +150,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: list
         :return: A list of public IP addresses associated with this instance.
         """
-        raise NotImplementedError(
-            'Instance.public_ips not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def private_ips(self):
         """
         Get all the private IP addresses for this instance.
@@ -159,10 +160,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: list
         :return: A list of private IP addresses associated with this instance.
         """
-        raise NotImplementedError(
-            'Instance.private_ips not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def instance_type(self):
         """
         Get the instance type.
@@ -170,9 +170,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: str
         :return: API type of this instance (e.g., ``m1.large``)
         """
-        raise NotImplementedError(
-            'Instance.instance_type not implemented by this provider')
+        pass
 
+    @abstractmethod
     def reboot(self):
         """
         Reboot this instance (using the cloud middleware API).
@@ -180,9 +180,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: bool
         :return: ``True`` if the reboot was succesful; ``False`` otherwise.
         """
-        raise NotImplementedError(
-            'Instance.reboot not implemented by this provider')
+        pass
 
+    @abstractmethod
     def terminate(self):
         """
         Permanently terminate this instance.
@@ -191,10 +191,9 @@ class Instance(ObjectLifeCycleMixin):
         :return: ``True`` if the termination of the instance was succesfully
                  initiated; ``False`` otherwise.
         """
-        raise NotImplementedError(
-            'Instance.terminate not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def image_id(self):
         """
         Get the image ID for this insance.
@@ -202,10 +201,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: str
         :return: Image ID (i.e., AMI) this instance is using.
         """
-        raise NotImplementedError(
-            'Instance.image_id not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def placement_zone(self):
         """
         Get the placement zone where this instance is running.
@@ -213,10 +211,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: str
         :return: Region/zone/placement where this instance is running.
         """
-        raise NotImplementedError(
-            'Instance.placement not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def mac_address(self):
         """
         Get the MAC address for this instance.
@@ -224,10 +221,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: str
         :return: MAC address for ths instance.
         """
-        raise NotImplementedError(
-            'Instance.mac_address not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def security_groups(self):
         """
         Get the security groups associated with this instance.
@@ -235,10 +231,9 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: list or :class:``SecurityGroup`` objects
         :return: A list of SecurityGroup objects associated with this instance.
         """
-        raise NotImplementedError(
-            'Instance.security_groups not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def key_pair_name(self):
         """
         Get the name of the key pair associated with this instance.
@@ -246,17 +241,16 @@ class Instance(ObjectLifeCycleMixin):
         :rtype: str
         :return: Name of the ssh key pair associated with this instance.
         """
-        raise NotImplementedError(
-            'Instance.key_pair_name not implemented by this provider')
+        pass
 
+    @abstractmethod
     def create_image(self, name):
         """
         Create a new image based on this instance.
         :return:  an Image object
         :rtype: ``object`` of :class:`.Image`
         """
-        raise NotImplementedError(
-            'Instance.create_image not implemented by this provider')
+        pass
 
 
 class MachineImageState(object):
@@ -278,7 +272,9 @@ class MachineImageState(object):
 
 class MachineImage(ObjectLifeCycleMixin):
 
-    @property
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
     def image_id(self):
         """
         Get the image identifier.
@@ -286,10 +282,9 @@ class MachineImage(ObjectLifeCycleMixin):
         :rtype: ``str``
         :return: ID for this instance as returned by the cloud middleware.
         """
-        raise NotImplementedError(
-            'MachineImage.image_id not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def name(self):
         """
         Get the image name.
@@ -297,10 +292,9 @@ class MachineImage(ObjectLifeCycleMixin):
         :rtype: ``str``
         :return: Name for this image as returned by the cloud middleware.
         """
-        raise NotImplementedError(
-            'MachineImage.name not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def description(self):
         """
         Get the image description.
@@ -308,9 +302,9 @@ class MachineImage(ObjectLifeCycleMixin):
         :rtype: ``str``
         :return: Description for this image as returned by the cloud middleware
         """
-        raise NotImplementedError(
-            'MachineImage.description not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self):
         """
         Delete this image
@@ -318,8 +312,7 @@ class MachineImage(ObjectLifeCycleMixin):
         :rtype: ``bool``
         :return: True if the operation succeeded
         """
-        raise NotImplementedError(
-            'MachineImage.delete not implemented by this provider')
+        pass
 
 
 class VolumeState(object):
@@ -347,7 +340,9 @@ class VolumeState(object):
 
 class Volume(ObjectLifeCycleMixin):
 
-    @property
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
     def volume_id(self):
         """
         Get the volume identifier.
@@ -355,10 +350,9 @@ class Volume(ObjectLifeCycleMixin):
         :rtype: ``str``
         :return: ID for this instance as returned by the cloud middleware.
         """
-        raise NotImplementedError(
-            'Volume.volume_id not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def name(self):
         """
         Get the volume name.
@@ -366,9 +360,9 @@ class Volume(ObjectLifeCycleMixin):
         :rtype: ``str``
         :return: Name for this volume as returned by the cloud middleware.
         """
-        raise NotImplementedError(
-            'Volume.name not implemented by this provider')
+        pass
 
+    @abstractmethod
     def attach(self, instance_id, device):
         """
         Attach this volume to an instance.
@@ -384,9 +378,9 @@ class Volume(ObjectLifeCycleMixin):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError(
-            'Volume.attach not implemented by this provider')
+        pass
 
+    @abstractmethod
     def detach(self, force=False):
         """
         Detach this volume from an instance.
@@ -404,9 +398,9 @@ class Volume(ObjectLifeCycleMixin):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError(
-            'Volume.detach not implemented by this provider')
+        pass
 
+    @abstractmethod
     def create_snapshot(self, description=None):
         """
         Create a snapshot of this Volume.
@@ -418,9 +412,9 @@ class Volume(ObjectLifeCycleMixin):
         :rtype: :class:`.Snapshot`
         :return: The created Snapshot object
         """
-        raise NotImplementedError(
-            'Volume.snapshot not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self):
         """
         Delete this volume.
@@ -428,8 +422,7 @@ class Volume(ObjectLifeCycleMixin):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError(
-            'Volume.delete not implemented by this provider')
+        pass
 
 
 class SnapshotState(object):
@@ -453,6 +446,9 @@ class SnapshotState(object):
 
 class Snapshot(ObjectLifeCycleMixin):
 
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
     def create_volume(self, placement, size=None, volume_type=None, iops=None):
         """
         Create a new Volume from this Snapshot.
@@ -476,9 +472,9 @@ class Snapshot(ObjectLifeCycleMixin):
         :rtype: :class:`.Volume`
         :return: An instance of the created Volume
         """
-        raise NotImplementedError(
-            'create_volume not implemented by this provider')
+        pass
 
+    @abstractmethod
     def share(self, user_ids=None):
         """
         Share this Snapshot.
@@ -490,8 +486,9 @@ class Snapshot(ObjectLifeCycleMixin):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError('share not implemented by this provider')
+        pass
 
+    @abstractmethod
     def unshare(self, user_ids=None):
         """
         Unshare this Snapshot.
@@ -503,8 +500,9 @@ class Snapshot(ObjectLifeCycleMixin):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError('unshare not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self):
         """
         Delete this snapshot.
@@ -512,12 +510,14 @@ class Snapshot(ObjectLifeCycleMixin):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError('delete not implemented by this provider')
+        pass
 
 
 class KeyPair(object):
 
-    @property
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
     def name(self):
         """
         Return the name of this key pair.
@@ -525,10 +525,9 @@ class KeyPair(object):
         :rtype: str
         :return: A name of this ssh key pair
         """
-        raise NotImplementedError(
-            'name not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def material(self):
         """
         Unencrypted private key.
@@ -536,9 +535,9 @@ class KeyPair(object):
         :rtype: str
         :return: Unencrypted private key or ``None`` if not available.
         """
-        raise NotImplementedError(
-            'material not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self):
         """
         Delete this key pair.
@@ -546,8 +545,7 @@ class KeyPair(object):
         :rtype: bool
         :return: ``True`` is successful.
         """
-        raise NotImplementedError(
-            'delete not implemented by this provider')
+        pass
 
 
 class Region(object):
@@ -556,8 +554,9 @@ class Region(object):
     Represents a cloud region, typically a separate geographic area and will
     contain at least one placement zone.
     """
+    __metaclass__ = ABCMeta
 
-    @property
+    @abstractproperty
     def name(self):
         """
         Name of the region.
@@ -565,9 +564,9 @@ class Region(object):
         :rtype: str
         :return: Name of the region.
         """
-        raise NotImplementedError(
-            'name not implemented by this provider')
+        pass
 
+    @abstractmethod
     def list_zones(self):
         """
         List all available placement zones within this region.
@@ -575,8 +574,7 @@ class Region(object):
         :rtype: list
         :return: List of all the available placement zones.
         """
-        raise NotImplementedError(
-            'list_zones not implemented by this provider')
+        pass
 
 
 class PlacementZone(object):
@@ -584,8 +582,9 @@ class PlacementZone(object):
     """
     Represents a placement zone. A placement zone is contained within a Region.
     """
+    __metaclass__ = ABCMeta
 
-    @property
+    @abstractproperty
     def name(self):
         """
         Name of the placement zone.
@@ -593,10 +592,9 @@ class PlacementZone(object):
         :rtype: str
         :return: Name of the placement zone.
         """
-        raise NotImplementedError(
-            'PlacementZone.name not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def region(self):
         """
         A region this placement zone is associated with.
@@ -604,8 +602,7 @@ class PlacementZone(object):
         :rtype: str
         :return: The name of the region the zone is associated with.
         """
-        raise NotImplementedError(
-            'region_name not implemented by this provider')
+        pass
 
 
 class InstanceType(object):
@@ -613,18 +610,17 @@ class InstanceType(object):
     """
     An instance type object.
     """
+    __metaclass__ = ABCMeta
 
-    @property
+    @abstractproperty
     def id(self):
-        raise NotImplementedError(
-            'InstanceType.id not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def name(self):
-        raise NotImplementedError(
-            'InstanceType.name not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def family(self):
         """
         The family/group that this instance type belongs to. For example,
@@ -634,10 +630,9 @@ class InstanceType(object):
         :rtype: str
         :return: Name of the instance family.
         """
-        raise NotImplementedError(
-            'InstanceType.family not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def vcpus(self):
         """
         The number of VCPUs supported by this instance type.
@@ -645,10 +640,9 @@ class InstanceType(object):
         :rtype: int
         :return: Number of VCPUs
         """
-        raise NotImplementedError(
-            'InstanceType.vcpus not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def ram(self):
         """
         The amount of RAM (in mb) supported by this instance type.
@@ -656,10 +650,9 @@ class InstanceType(object):
         :rtype: int
         :return: Total RAM (in MB).
         """
-        raise NotImplementedError(
-            'InstanceType.ram not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def root_disk(self):
         """
         The size of this instance types's root disk (in GB).
@@ -667,10 +660,9 @@ class InstanceType(object):
         :rtype: int
         :return: Size of root disk (in GB).
         """
-        raise NotImplementedError(
-            'InstanceType.root_disk not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def ephemeral_disk(self):
         """
         The size of this instance types's total ephemeral storage (in GB).
@@ -678,10 +670,9 @@ class InstanceType(object):
         :rtype: int
         :return: Size of ephemeral disks (in GB).
         """
-        raise NotImplementedError(
-            'InstanceType.ephemeral_disk not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def total_disk(self):
         """
         The total disk space available on this instance type.
@@ -690,10 +681,9 @@ class InstanceType(object):
         :rtype: int
         :return: Size of total disk space (in GB).
         """
-        raise NotImplementedError(
-            'InstanceType.total_disk not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def extra_data(self):
         """
         A dictionary of extra data about this instance. May contain
@@ -702,13 +692,24 @@ class InstanceType(object):
         :rtype: dict
         :return: Extra attributes for this instance type
         """
-        raise NotImplementedError(
-            'InstanceType.extra_data not implemented by this provider')
+        pass
 
 
 class SecurityGroup(object):
 
-    @property
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
+    def id(self):
+        """
+        Get the ID of this security group.
+
+        :rtype: str
+        :return: Security group ID
+        """
+        pass
+
+    @abstractproperty
     def name(self):
         """
         Return the name of this security group.
@@ -716,10 +717,9 @@ class SecurityGroup(object):
         :rtype: str
         :return: A name of this security group.
         """
-        raise NotImplementedError(
-            'name not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def description(self):
         """
         Return the description of this security group.
@@ -727,21 +727,9 @@ class SecurityGroup(object):
         :rtype: str
         :return: A description of this security group.
         """
-        raise NotImplementedError(
-            'description not implemented by this provider')
-
-    @property
-    def id(self):
-        """
-        Get the ID of this security group.
-
-        :rtype: str
-        :return: Security group ID
-        """
-        raise NotImplementedError(
-            'id not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def rules(self):
         """
         Get the list of rules for this security group.
@@ -749,9 +737,9 @@ class SecurityGroup(object):
         :rtype: list of :class:``.SecurityGroupRule``
         :return: A list of security group rule objects
         """
-        raise NotImplementedError(
-            'rules not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self):
         """
         Delete this security group.
@@ -759,9 +747,9 @@ class SecurityGroup(object):
         :rtype: bool
         :return: ``True`` is successful.
         """
-        raise NotImplementedError(
-            'delete not implemented by this provider')
+        pass
 
+    @abstractmethod
     def add_rule(self, ip_protocol=None, from_port=None, to_port=None,
                  cidr_ip=None, src_group=None):
         """
@@ -790,8 +778,7 @@ class SecurityGroup(object):
         :rtype: bool
         :return: True if successful.
         """
-        raise NotImplementedError(
-            'add_rule not implemented by this provider')
+        pass
 
 
 class SecurityGroupRule(object):
@@ -799,40 +786,37 @@ class SecurityGroupRule(object):
     """
     Represents a security group rule.
     """
+    __metaclass__ = ABCMeta
 
-    @property
+    @abstractproperty
     def ip_protocol(self):
         """
         IP protocol used. Either ``tcp`` | ``udp`` | ``icmp``.
         """
-        raise NotImplementedError(
-            'ip_protocol not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def from_port(self):
         """
         Lowest port number opened as part of this rule.
         """
-        raise NotImplementedError(
-            'from_port not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def to_port(self):
         """
         Highest port number opened as part of this rule.
         """
-        raise NotImplementedError(
-            'to_port not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def cidr_ip(self):
         """
         CIDR block this security group is providing access to.
         """
-        raise NotImplementedError(
-            'cidr_ip not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def group(self):
         """
         Security group given access permissions by this rule.
@@ -840,8 +824,7 @@ class SecurityGroupRule(object):
         :rtype: ``object`` of :class:`.SecurityGroup`
         :return: The Security Group with granting access.
         """
-        raise NotImplementedError(
-            'group not implemented by this provider')
+        pass
 
 
 class ContainerObject(object):
@@ -849,8 +832,9 @@ class ContainerObject(object):
     """
     Represents an object stored within a container.
     """
+    __metaclass__ = ABCMeta
 
-    @property
+    @abstractproperty
     def name(self):
         """
         Get this object's name.
@@ -858,9 +842,9 @@ class ContainerObject(object):
         :rtype: ``str``
         :return: Name of this object as returned by the cloud middleware.
         """
-        raise NotImplementedError(
-            'ContainerObject.name not implemented by this provider')
+        pass
 
+    @abstractmethod
     def download(self, target_stream):
         """
         Download this object and write its
@@ -869,9 +853,9 @@ class ContainerObject(object):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError(
-            'ContainerObject.download not implemented by this provider')
+        pass
 
+    @abstractmethod
     def upload(self, source_stream):
         """
         Set the contents of this object to the data read from the source
@@ -880,9 +864,9 @@ class ContainerObject(object):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError(
-            'ContainerObject.upload not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self):
         """
         Delete this object.
@@ -890,13 +874,14 @@ class ContainerObject(object):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError(
-            'ContainerObject.delete not implemented by this provider')
+        pass
 
 
 class Container(object):
 
-    @property
+    __metaclass__ = ABCMeta
+
+    @abstractproperty
     def name(self):
         """
         Get this container's name.
@@ -904,9 +889,9 @@ class Container(object):
         :rtype: ``str``
         :return: Name of this container as returned by the cloud middleware.
         """
-        raise NotImplementedError(
-            'Container.name not implemented by this provider')
+        pass
 
+    @abstractmethod
     def get(self, key):
         """
         Retrieve a given object from this container.
@@ -917,9 +902,9 @@ class Container(object):
         :rtype: ContainerObject
         :return: The ContainerObject or None if it cannot be found.
         """
-        raise NotImplementedError(
-            'Container.list not implemented by this provider')
+        pass
 
+    @abstractmethod
     def list(self):
         """
         List all objects within this container.
@@ -927,9 +912,9 @@ class Container(object):
         :rtype: ContainerObject
         :return: List of all available ContainerObjects within this container
         """
-        raise NotImplementedError(
-            'Container.list not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self, delete_contents=False):
         """
         Delete this container.
@@ -941,5 +926,4 @@ class Container(object):
         :rtype: bool
         :return: True if successful
         """
-        raise NotImplementedError(
-            'Container.delete not implemented by this provider')
+        pass

+ 90 - 74
cloudbridge/providers/interfaces/services.py

@@ -1,6 +1,7 @@
 """
 Specifications for services available through a provider
 """
+from abc import ABCMeta, abstractmethod, abstractproperty
 
 
 class ProviderService(object):
@@ -8,7 +9,9 @@ class ProviderService(object):
     """
     Base interface for any service supported by a provider
     """
+    __metaclass__ = ABCMeta
 
+    @abstractproperty
     def provider(self):
         """
         Returns the provider instance associated with this service.
@@ -16,8 +19,7 @@ class ProviderService(object):
         :rtype: ``object`` of :class:`.CloudProvider`
         :return: a Provider object
         """
-        raise NotImplementedError(
-            'ComputeService.Provider not implemented by this provider')
+        pass
 
 
 class ComputeService(ProviderService):
@@ -25,7 +27,9 @@ class ComputeService(ProviderService):
     """
     Base interface for compute service supported by a provider
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def get_instance(self, instance_id):
         """
         Returns an instance given its id. Returns None
@@ -34,9 +38,9 @@ class ComputeService(ProviderService):
         :rtype: ``object`` of :class:`.Instance`
         :return:  an Instance object
         """
-        raise NotImplementedError(
-            'get_instance not implemented by this provider')
+        pass
 
+    @abstractmethod
     def find_instance(self, name):
         """
         Searches for an instance by a given list of attributes.
@@ -44,9 +48,9 @@ class ComputeService(ProviderService):
         :rtype: ``object`` of :class:`.Instance`
         :return: an Instance object
         """
-        raise NotImplementedError(
-            'find_instance not implemented by this provider')
+        pass
 
+    @abstractmethod
     def list_instances(self):
         """
         List all instances.
@@ -54,10 +58,9 @@ class ComputeService(ProviderService):
         :rtype: ``list`` of :class:`.Instance`
         :return: list of Instance objects
         """
-        raise NotImplementedError(
-            'list_instances not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def instance_types(self):
         """
         Provides access to all Instance type related services in this provider.
@@ -65,9 +68,9 @@ class ComputeService(ProviderService):
         :rtype: ``object`` of :class:`.InstanceTypeService`
         :return:  an InstanceTypeService object
         """
-        raise NotImplementedError(
-            'instance_types not implemented by this provider')
+        pass
 
+    @abstractmethod
     def list_regions(self):
         """
         List all data center regions for this provider.
@@ -75,12 +78,13 @@ class ComputeService(ProviderService):
         :rtype: ``list`` of :class:`.Region`
         :return: list of Region objects
         """
-        raise NotImplementedError(
-            'list_regions not implemented by this provider')
+        pass
 
+    @abstractmethod
     def create_instance(self, name, image, instance_type, zone=None,
                         keypair=None, security_groups=None, user_data=None,
                         block_device_mapping=None, network_interfaces=None,
+                        launch_configuration=None,
                         **kwargs):
         """
         Creates a new virtual machine instance.
@@ -123,11 +127,17 @@ class ComputeService(ProviderService):
                                    describes network interfaces for this
                                    instance.
 
+        :type  launch_configuration: ``LaunchConfiguration`` object
+        :param launch_configuration: A ``LaunchConfiguration`` object which
+        describes advanced launch configuration options for an instance. This
+        include blck_device_mappings and network_interfaces. To construct a
+        launch configuration object, call
+        provider.compute.instances.create_launch_configuration()
+
         :rtype: `object`` of :class:`.Instance`
         :return:  an instance of Instance class
         """
-        raise NotImplementedError(
-            'create_instance not implemented by this provider')
+        pass
 
 
 class VolumeService(ProviderService):
@@ -135,7 +145,9 @@ class VolumeService(ProviderService):
     """
     Base interface for a Volume Service
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def get_volume(self, volume_id):
         """
         Returns a volume given its id. Returns None if the volume
@@ -144,9 +156,9 @@ class VolumeService(ProviderService):
         :rtype: ``object`` of :class:`.Volume`
         :return: a Volume object
         """
-        raise NotImplementedError(
-            'get_volume not implemented by this provider')
+        pass
 
+    @abstractmethod
     def find_volume(self, name):
         """
         Searches for a volume by a given list of attributes.
@@ -154,9 +166,9 @@ class VolumeService(ProviderService):
         :rtype: ``object`` of :class:`.Volume`
         :return: a Volume object or ``None`` if not found
         """
-        raise NotImplementedError(
-            'find_volume not implemented by this provider')
+        pass
 
+    @abstractmethod
     def list_volumes(self):
         """
         List all volumes.
@@ -164,9 +176,9 @@ class VolumeService(ProviderService):
         :rtype: ``list`` of :class:`.Volume`
         :return: a list of Volume objects
         """
-        raise NotImplementedError(
-            'list_volumes not implemented by this provider')
+        pass
 
+    @abstractmethod
     def create_volume(self, name, size, zone, snapshot=None, description=None):
         """
         Creates a new volume.
@@ -188,8 +200,7 @@ class VolumeService(ProviderService):
         :rtype: ``object`` of :class:`.Volume`
         :return: a newly created Volume object
         """
-        raise NotImplementedError(
-            'create_volume not implemented by this provider')
+        pass
 
 
 class SnapshotService(ProviderService):
@@ -197,7 +208,9 @@ class SnapshotService(ProviderService):
     """
     Base interface for a Snapshot Service
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def get_snapshot(self, volume_id):
         """
         Returns a snapshot given its id. Returns None if the snapshot
@@ -206,9 +219,9 @@ class SnapshotService(ProviderService):
         :rtype: ``object`` of :class:`.Snapshot`
         :return: a Snapshot object
         """
-        raise NotImplementedError(
-            'get_snapshot not implemented by this provider')
+        pass
 
+    @abstractmethod
     def find_snapshot(self, name):
         """
         Searches for a snapshot by a given list of attributes.
@@ -216,9 +229,9 @@ class SnapshotService(ProviderService):
         :rtype: ``object`` of :class:`.Snapshot`
         :return: a Snapshot object or ``None`` if not found
         """
-        raise NotImplementedError(
-            'find_snapshot not implemented by this provider')
+        pass
 
+    @abstractmethod
     def list_snapshots(self):
         """
         List all snapshots.
@@ -226,9 +239,9 @@ class SnapshotService(ProviderService):
         :rtype: ``list`` of :class:`.Snapshot`
         :return: a list of Snapshot objects
         """
-        raise NotImplementedError(
-            'list_snapshots not implemented by this provider')
+        pass
 
+    @abstractmethod
     def create_snapshot(self, name, volume, description=None):
         """
         Creates a new snapshot off a volume.
@@ -247,8 +260,7 @@ class SnapshotService(ProviderService):
         :rtype: ``object`` of :class:`.Snapshot`
         :return: a newly created Snapshot object
         """
-        raise NotImplementedError(
-            'create_snapshot not implemented by this provider')
+        pass
 
 
 class BlockStoreService(ProviderService):
@@ -256,8 +268,9 @@ class BlockStoreService(ProviderService):
     """
     Base interface for a Block Store Service
     """
+    __metaclass__ = ABCMeta
 
-    @property
+    @abstractproperty
     def volumes(self):
         """
         Provides access to the volumes (i.e., block storage) for this provider.
@@ -265,10 +278,9 @@ class BlockStoreService(ProviderService):
         :rtype: ``object`` of :class:`.VolumeService`
         :return: a VolumeService object
         """
-        raise NotImplementedError(
-            'CloudProvider.block_store not implemented by this provider')
+        pass
 
-    @property
+    @abstractproperty
     def snapshots(self):
         """
         Provides access to volume snapshots for this provider.
@@ -276,8 +288,7 @@ class BlockStoreService(ProviderService):
         :rtype: ``object`` of :class:`.SnapshotService`
         :return: an SnapshotService object
         """
-        raise NotImplementedError(
-            'CloudProvider.object_store not implemented by this provider')
+        pass
 
 
 class ImageService(ProviderService):
@@ -285,7 +296,9 @@ class ImageService(ProviderService):
     """
     Base interface for an Image Service
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def get_image(self, image_id):
         """
         Returns an Image given its id. Returns None if the Image does not
@@ -294,9 +307,9 @@ class ImageService(ProviderService):
         :rtype: ``object`` of :class:`.Image`
         :return:  an Image instance
         """
-        raise NotImplementedError(
-            'get_image implemented by this provider')
+        pass
 
+    @abstractmethod
     def find_image(self, name):
         """
         Searches for an image by a given list of attributes
@@ -304,9 +317,9 @@ class ImageService(ProviderService):
         :rtype: ``object`` of :class:`.Image`
         :return:  an Image instance
         """
-        raise NotImplementedError(
-            'find_image not implemented by this provider')
+        pass
 
+    @abstractmethod
     def list_images(self):
         """
         List all images.
@@ -314,8 +327,7 @@ class ImageService(ProviderService):
         :rtype: ``list`` of :class:`.Image`
         :return:  list of image objects
         """
-        raise NotImplementedError(
-            'list_images not implemented by this provider')
+        pass
 
 
 class ObjectStoreService(ProviderService):
@@ -323,7 +335,9 @@ class ObjectStoreService(ProviderService):
     """
     Base interface for an Object Storage Service
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def get_container(self, container_id):
         """
         Returns a container given its id. Returns None if the container
@@ -332,9 +346,9 @@ class ObjectStoreService(ProviderService):
         :rtype: ``object`` of :class:`.Container`
         :return:  a Container instance
         """
-        raise NotImplementedError(
-            'get_container implemented by this provider')
+        pass
 
+    @abstractmethod
     def find_container(self, name):
         """
         Searches for a container by a given list of attributes
@@ -342,9 +356,9 @@ class ObjectStoreService(ProviderService):
         :rtype: ``object`` of :class:`.Container`
         :return:  a Container instance
         """
-        raise NotImplementedError(
-            'find_container not implemented by this provider')
+        pass
 
+    @abstractmethod
     def list_containers(self):
         """
         List all containers.
@@ -352,9 +366,9 @@ class ObjectStoreService(ProviderService):
         :rtype: ``list`` of :class:`.Container`
         :return:  list of container objects
         """
-        raise NotImplementedError(
-            'list_containers not implemented by this provider')
+        pass
 
+    @abstractmethod
     def create_container(self, name, location=None):
         """
         Create a new container.
@@ -368,8 +382,7 @@ class ObjectStoreService(ProviderService):
         :return:  a Container object
         :rtype: ``object`` of :class:`.Container`
         """
-        raise NotImplementedError(
-            'create_container not implemented by this provider')
+        pass
 
 
 class SecurityService(ProviderService):
@@ -377,8 +390,9 @@ class SecurityService(ProviderService):
     """
     Base interface for a Security Service.
     """
+    __metaclass__ = ABCMeta
 
-    @property
+    @abstractproperty
     def key_pairs(self):
         """
         Provides access to key pairs for this provider.
@@ -386,9 +400,9 @@ class SecurityService(ProviderService):
         :rtype: ``object`` of :class:`.KeyPairService`
         :return: a KeyPairService object
         """
-        return self._key_pairs
+        pass
 
-    @property
+    @abstractproperty
     def security_groups(self):
         """
         Provides access to security groups for this provider.
@@ -396,7 +410,7 @@ class SecurityService(ProviderService):
         :rtype: ``object`` of :class:`.SecurityGroupService`
         :return: a SecurityGroupService object
         """
-        return self._security_groups
+        pass
 
 
 class KeyPairService(ProviderService):
@@ -404,7 +418,9 @@ class KeyPairService(ProviderService):
     """
     Base interface for key pairs.
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def list(self):
         """
         List all key pairs associated with this account.
@@ -412,9 +428,9 @@ class KeyPairService(ProviderService):
         :rtype: ``list`` of :class:`.KeyPair`
         :return:  list of KeyPair objects
         """
-        raise NotImplementedError(
-            'list_key_pairs not implemented by this provider')
+        pass
 
+    @abstractmethod
     def create(self, name):
         """
         Create a new keypair.
@@ -425,9 +441,9 @@ class KeyPairService(ProviderService):
         :rtype: ``object`` of :class:`.KeyPair`
         :return:  A keypair instance
         """
-        raise NotImplementedError(
-            'create_key_pair not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self, name):
         """
         Delete an existing SecurityGroup.
@@ -440,8 +456,7 @@ class KeyPairService(ProviderService):
                   that this implies that the key may not have been deleted by
                   this method but instead has not existed at all.
         """
-        raise NotImplementedError(
-            'delete not implemented by this provider')
+        pass
 
 
 class SecurityGroupService(ProviderService):
@@ -449,7 +464,9 @@ class SecurityGroupService(ProviderService):
     """
     Base interface for security groups.
     """
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def list(self):
         """
         List all security groups associated with this account.
@@ -457,9 +474,9 @@ class SecurityGroupService(ProviderService):
         :rtype: ``list`` of :class:`.SecurityGroup`
         :return:  list of SecurityGroup objects
         """
-        raise NotImplementedError(
-            'list not implemented by this provider')
+        pass
 
+    @abstractmethod
     def create(self, name, description):
         """
         Create a new SecurityGroup.
@@ -473,9 +490,9 @@ class SecurityGroupService(ProviderService):
         :rtype: ``object`` of :class:`.SecurityGroup`
         :return:  A SecurityGroup instance or ``None`` if one was not created.
         """
-        raise NotImplementedError(
-            'create not implemented by this provider')
+        pass
 
+    @abstractmethod
     def get(self, group_names=None, group_ids=None):
         """
         Get all security groups associated with your account.
@@ -494,9 +511,9 @@ class SecurityGroupService(ProviderService):
         :return: A list of SecurityGroup objects or an empty list if none
         found.
         """
-        raise NotImplementedError(
-            'get not implemented by this provider')
+        pass
 
+    @abstractmethod
     def delete(self, group_id):
         """
         Delete an existing SecurityGroup.
@@ -510,12 +527,13 @@ class SecurityGroupService(ProviderService):
                   been deleted by this method but instead has not existed in
                   the first place.
         """
-        raise NotImplementedError(
-            'delete not implemented by this provider')
+        pass
 
 
 class InstanceTypesService(object):
+    __metaclass__ = ABCMeta
 
+    @abstractmethod
     def list(self):
         """
         List all instance types.
@@ -523,9 +541,9 @@ class InstanceTypesService(object):
         :rtype: ``list`` of :class:`.InstanceType`
         :return: list of InstanceType objects
         """
-        raise NotImplementedError(
-            'InstanceTypesService.list not implemented by this provider')
+        pass
 
+    @abstractmethod
     def find_by_name(self, name):
         """
         Searches for an instance by a given list of attributes.
@@ -533,6 +551,4 @@ class InstanceTypesService(object):
         :rtype: ``object`` of :class:`.InstanceType`
         :return: an Instance object
         """
-        raise NotImplementedError(
-            'InstanceTypesService.find_instance not implemented by this'
-            'provider')
+        pass

+ 5 - 0
cloudbridge/providers/openstack/impl.py

@@ -48,6 +48,11 @@ class OpenStackCloudProviderV1(BaseCloudProvider):
         self._block_store = OpenStackBlockStoreService(self)
         self._object_store = OpenStackObjectStoreService(self)
 
+    @property
+    def account(self):
+        raise NotImplementedError(
+            'account not implemented by this provider')
+
     @property
     def compute(self):
         return self._compute

+ 10 - 0
cloudbridge/providers/openstack/resources.py

@@ -497,6 +497,16 @@ class OpenStackSnapshot(BaseSnapshot):
         """
         self._snapshot.delete()
 
+    def create_volume(self, placement, size=None, volume_type=None, iops=None):
+        raise NotImplementedError(
+            'create_volume not implemented by this provider')
+
+    def share(self, user_ids=None):
+        raise NotImplementedError('share not implemented by this provider')
+
+    def unshare(self, user_ids=None):
+        raise NotImplementedError('share not implemented by this provider')
+
     def __repr__(self):
         return "<CB-OSSnapshot: {0} ({1}>".format(self.snapshot_id, self.name)
 

+ 40 - 35
cloudbridge/providers/openstack/services.py

@@ -2,22 +2,24 @@
 Services implemented by the OpenStack provider.
 """
 from cinderclient.exceptions import NotFound as CinderNotFound
-from cloudbridge.providers.interfaces import BlockStoreService
-from cloudbridge.providers.interfaces import ComputeService
-from cloudbridge.providers.interfaces import ImageService
+from novaclient.exceptions import NotFound as NovaNotFound
+
+from cloudbridge.providers.base import BaseBlockStoreService
+from cloudbridge.providers.base import BaseComputeService
+from cloudbridge.providers.base import BaseImageService
+from cloudbridge.providers.base import BaseInstanceTypesService
+from cloudbridge.providers.base import BaseKeyPairService
+from cloudbridge.providers.base import BaseObjectStoreService
+from cloudbridge.providers.base import BaseSecurityGroupService
+from cloudbridge.providers.base import BaseSecurityService
+from cloudbridge.providers.base import BaseSnapshotService
+from cloudbridge.providers.base import BaseVolumeService
+
 from cloudbridge.providers.interfaces import InstanceType
-from cloudbridge.providers.interfaces import InstanceTypesService
 from cloudbridge.providers.interfaces import KeyPair
-from cloudbridge.providers.interfaces import KeyPairService
 from cloudbridge.providers.interfaces import MachineImage
-from cloudbridge.providers.interfaces import ObjectStoreService
 from cloudbridge.providers.interfaces import PlacementZone
 from cloudbridge.providers.interfaces import SecurityGroup
-from cloudbridge.providers.interfaces import SecurityGroupService
-from cloudbridge.providers.interfaces import SecurityService
-from cloudbridge.providers.interfaces import SnapshotService
-from cloudbridge.providers.interfaces import VolumeService
-from novaclient.exceptions import NotFound as NovaNotFound
 
 from .resources import OpenStackContainer
 from .resources import OpenStackInstance
@@ -30,10 +32,10 @@ from .resources import OpenStackSnapshot
 from .resources import OpenStackVolume
 
 
-class OpenStackSecurityService(SecurityService):
+class OpenStackSecurityService(BaseSecurityService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackSecurityService, self).__init__(provider)
 
         # Initialize provider services
         self._key_pairs = OpenStackKeyPairService(provider)
@@ -60,10 +62,10 @@ class OpenStackSecurityService(SecurityService):
         return self._security_groups
 
 
-class OpenStackKeyPairService(KeyPairService):
+class OpenStackKeyPairService(BaseKeyPairService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackKeyPairService, self).__init__(provider)
 
     def list(self):
         """
@@ -110,10 +112,10 @@ class OpenStackKeyPairService(KeyPairService):
             return True
 
 
-class OpenStackSecurityGroupService(SecurityGroupService):
+class OpenStackSecurityGroupService(BaseSecurityGroupService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackSecurityGroupService, self).__init__(provider)
 
     def list(self):
         """
@@ -197,10 +199,10 @@ class OpenStackSecurityGroupService(SecurityGroupService):
         return True
 
 
-class OpenStackImageService(ImageService):
+class OpenStackImageService(BaseImageService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackImageService, self).__init__(provider)
 
     def get_image(self, image_id):
         """
@@ -228,10 +230,10 @@ class OpenStackImageService(ImageService):
                 for image in images]
 
 
-class OpenStackInstanceTypesService(InstanceTypesService):
+class OpenStackInstanceTypesService(BaseInstanceTypesService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackInstanceTypesService, self).__init__(provider)
 
     def list(self):
         return [OpenStackInstanceType(f)
@@ -242,10 +244,10 @@ class OpenStackInstanceTypesService(InstanceTypesService):
             (itype for itype in self.list() if itype.name == name), None)
 
 
-class OpenStackBlockStoreService(BlockStoreService):
+class OpenStackBlockStoreService(BaseBlockStoreService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackBlockStoreService, self).__init__(provider)
 
         # Initialize provider services
         self._volumes = OpenStackVolumeService(self._provider)
@@ -260,10 +262,10 @@ class OpenStackBlockStoreService(BlockStoreService):
         return self._snapshots
 
 
-class OpenStackVolumeService(VolumeService):
+class OpenStackVolumeService(BaseVolumeService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackVolumeService, self).__init__(provider)
 
     def get_volume(self, volume_id):
         """
@@ -303,10 +305,10 @@ class OpenStackVolumeService(VolumeService):
         return OpenStackVolume(self._provider, os_vol)
 
 
-class OpenStackSnapshotService(SnapshotService):
+class OpenStackSnapshotService(BaseSnapshotService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackSnapshotService, self).__init__(provider)
 
     def get_snapshot(self, snapshot_id):
         """
@@ -346,10 +348,10 @@ class OpenStackSnapshotService(SnapshotService):
         return OpenStackSnapshot(self._provider, os_snap)
 
 
-class OpenStackObjectStoreService(ObjectStoreService):
+class OpenStackObjectStoreService(BaseObjectStoreService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackObjectStoreService, self).__init__(provider)
 
     def get_container(self, container_id):
         """
@@ -386,16 +388,12 @@ class OpenStackObjectStoreService(ObjectStoreService):
         return self.get_container(name)
 
 
-class OpenStackComputeService(ComputeService):
+class OpenStackComputeService(BaseComputeService):
 
     def __init__(self, provider):
-        self._provider = provider
+        super(OpenStackComputeService, self).__init__(provider)
         self._instance_types = OpenStackInstanceTypesService(self._provider)
 
-    @property
-    def provider(self):
-        return self._provider
-
     @property
     def instance_types(self):
         return self._instance_types
@@ -435,6 +433,13 @@ class OpenStackComputeService(ComputeService):
             userdata=user_data)
         return OpenStackInstance(self._provider, os_instance)
 
+    def find_instance(self, name):
+        """
+        Searches for an instance by a given list of attributes.
+        """
+        raise NotImplementedError(
+            'find_instance not implemented by this provider')
+
     def list_instances(self):
         """
         List all instances.