Sfoglia il codice sorgente

Added subservices files, moved and renamed subservices, fixed imports

almahmoud 7 anni fa
parent
commit
62b279f9ca

+ 4 - 92
cloudbridge/cloud/base/resources.py

@@ -12,21 +12,17 @@ import uuid
 
 import six
 
-import cloudbridge.cloud.base.helpers as cb_helpers
-from cloudbridge.cloud.interfaces.exceptions \
-    import InvalidConfigurationException
+from cloudbridge.cloud.interfaces.exceptions import \
+    InvalidConfigurationException
 from cloudbridge.cloud.interfaces.exceptions import InvalidLabelException
 from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
 from cloudbridge.cloud.interfaces.exceptions import WaitStateException
 from cloudbridge.cloud.interfaces.resources import AttachmentInfo
 from cloudbridge.cloud.interfaces.resources import Bucket
-from cloudbridge.cloud.interfaces.resources import BucketContainer
 from cloudbridge.cloud.interfaces.resources import BucketObject
 from cloudbridge.cloud.interfaces.resources import CloudResource
 from cloudbridge.cloud.interfaces.resources import FloatingIP
-from cloudbridge.cloud.interfaces.resources import FloatingIPContainer
 from cloudbridge.cloud.interfaces.resources import FloatingIpState
-from cloudbridge.cloud.interfaces.resources import GatewayContainer
 from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import Instance
 from cloudbridge.cloud.interfaces.resources import InstanceState
@@ -49,11 +45,12 @@ from cloudbridge.cloud.interfaces.resources import Subnet
 from cloudbridge.cloud.interfaces.resources import SubnetState
 from cloudbridge.cloud.interfaces.resources import VMFirewall
 from cloudbridge.cloud.interfaces.resources import VMFirewallRule
-from cloudbridge.cloud.interfaces.resources import VMFirewallRuleContainer
 from cloudbridge.cloud.interfaces.resources import VMType
 from cloudbridge.cloud.interfaces.resources import Volume
 from cloudbridge.cloud.interfaces.resources import VolumeState
 
+from . import helpers as cb_helpers
+
 log = logging.getLogger(__name__)
 
 
@@ -589,37 +586,6 @@ class BaseVMFirewall(BaseCloudResource, VMFirewall):
         return self._provider.security.vm_firewalls.delete(self)
 
 
-class BaseVMFirewallRuleContainer(BasePageableObjectMixin,
-                                  VMFirewallRuleContainer):
-
-    def __init__(self, provider, firewall):
-        self.__provider = provider
-        self.firewall = firewall
-
-    @property
-    def _provider(self):
-        return self.__provider
-
-    def get(self, rule_id):
-        matches = [rule for rule in self if rule.id == rule_id]
-        if matches:
-            return matches[0]
-        else:
-            return None
-
-    def find(self, **kwargs):
-        obj_list = self
-        filters = ['name', 'direction', 'protocol', 'from_port', 'to_port',
-                   'cidr', 'src_dest_fw', 'src_dest_fw_id']
-        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
-        return ClientPagedResultList(self._provider, list(matches))
-
-    def delete(self, rule_id):
-        rule = self.get(rule_id)
-        if rule:
-            rule.delete()
-
-
 class BaseVMFirewallRule(BaseCloudResource, VMFirewallRule):
 
     def __init__(self, parent_fw, rule):
@@ -768,38 +734,6 @@ class BaseBucket(BaseCloudResource, Bucket):
     # TODO: Discuss creating `create_object` method, or change docs
 
 
-class BaseBucketContainer(BasePageableObjectMixin, BucketContainer):
-
-    def __init__(self, provider, bucket):
-        self.__provider = provider
-        self.bucket = bucket
-
-    @property
-    def _provider(self):
-        return self.__provider
-
-    def get(self, name):
-        return self._provider.storage.bucket_objects.get(self.bucket, name)
-
-    def list(self, limit=None, marker=None, prefix=None):
-        return self._provider.storage.bucket_objects.list(self.bucket, limit,
-                                                          marker, prefix)
-
-    def find(self, **kwargs):
-        return self._provider.storage.bucket_objects.find(self.bucket,
-                                                          **kwargs)
-
-    def create(self, name):
-        return self._provider.storage.bucket_objects.create(self.bucket, name)
-
-
-class BaseGatewayContainer(GatewayContainer, BasePageableObjectMixin):
-
-    def __init__(self, provider, network):
-        self._network = network
-        self._provider = provider
-
-
 class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
 
     CB_DEFAULT_NETWORK_LABEL = os.environ.get('CB_DEFAULT_NETWORK_LABEL',
@@ -875,28 +809,6 @@ class BaseSubnet(BaseCloudResource, BaseObjectLifeCycleMixin, Subnet):
         self._provider.networking.subnets.delete(self)
 
 
-class BaseFloatingIPContainer(FloatingIPContainer, BasePageableObjectMixin):
-
-    def __init__(self, provider, gateway):
-        self.__provider = provider
-        self.gateway = gateway
-
-    @property
-    def _provider(self):
-        return self.__provider
-
-    def find(self, **kwargs):
-        obj_list = self
-        filters = ['name', 'public_ip']
-        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
-        return ClientPagedResultList(self._provider, list(matches))
-
-    def delete(self, fip_id):
-        floating_ip = self.get(fip_id)
-        if floating_ip:
-            floating_ip.delete()
-
-
 class BaseFloatingIP(BaseCloudResource, BaseObjectLifeCycleMixin, FloatingIP):
 
     def __init__(self, provider):

+ 7 - 37
cloudbridge/cloud/base/services.py

@@ -3,14 +3,6 @@ Base implementation for services available through a provider
 """
 import logging
 
-import cloudbridge.cloud.base.helpers as cb_helpers
-from cloudbridge.cloud.base.middleware import dispatch
-from cloudbridge.cloud.base.resources import BaseBucket
-from cloudbridge.cloud.base.resources import BaseNetwork
-from cloudbridge.cloud.base.resources import BaseRouter
-from cloudbridge.cloud.base.resources import BaseSubnet
-from cloudbridge.cloud.interfaces.exceptions import \
-    InvalidConfigurationException
 from cloudbridge.cloud.interfaces.exceptions import InvalidParamException
 from cloudbridge.cloud.interfaces.resources import Network
 from cloudbridge.cloud.interfaces.services import BucketObjectService
@@ -32,7 +24,12 @@ from cloudbridge.cloud.interfaces.services import VMFirewallService
 from cloudbridge.cloud.interfaces.services import VMTypeService
 from cloudbridge.cloud.interfaces.services import VolumeService
 
+from . import helpers as cb_helpers
+from .middleware import dispatch
+from .resources import BaseNetwork
 from .resources import BasePageableObjectMixin
+from .resources import BaseRouter
+from .resources import BaseSubnet
 from .resources import ClientPagedResultList
 
 log = logging.getLogger(__name__)
@@ -143,40 +140,13 @@ class BaseBucketService(
                                      matches if matches else [])
 
 
-class BaseBucketObjectService(
-        BasePageableObjectMixin, BucketObjectService, BaseCloudService):
+class BaseBucketObjectService(BucketObjectService, BaseCloudService):
 
     def __init__(self, provider):
         super(BaseBucketObjectService, self).__init__(provider)
-        self._service_event_pattern += ".storage.bucket_objects"
+        self._service_event_pattern += ".storage._bucket_objects"
         self._bucket = None
 
-    # Default bucket needs to be set in order for the service to be iterable
-    def set_bucket(self, bucket):
-        bucket = bucket if isinstance(bucket, BaseBucket) \
-                 else self.provider.storage.buckets.get(bucket)
-        self._bucket = bucket
-
-    def __iter__(self):
-        if not self._bucket:
-            message = "You must set a bucket before iterating through its " \
-                      "objects. We do not allow iterating through all " \
-                      "buckets at this time. In order to set a bucket, use: " \
-                      "`provider.storage.bucket_objects.set_bucket(my_bucket)`"
-            raise InvalidConfigurationException(message)
-        result_list = self.list(bucket=self._bucket)
-        if result_list.supports_server_paging:
-            for result in result_list:
-                yield result
-            while result_list.is_truncated:
-                result_list = self.list(bucket=self._bucket,
-                                        marker=result_list.marker)
-                for result in result_list:
-                    yield result
-        else:
-            for result in result_list.data:
-                yield result
-
 
 class BaseComputeService(ComputeService, BaseCloudService):
 

+ 98 - 0
cloudbridge/cloud/base/subservices.py

@@ -0,0 +1,98 @@
+import logging
+
+from cloudbridge.cloud.interfaces.subservices import BucketObjectSubService
+from cloudbridge.cloud.interfaces.subservices import FloatingIPSubService
+from cloudbridge.cloud.interfaces.subservices import GatewaySubService
+from cloudbridge.cloud.interfaces.subservices import VMFirewallRuleSubService
+
+from . import helpers as cb_helpers
+from .resources import BasePageableObjectMixin
+from .resources import ClientPagedResultList
+
+log = logging.getLogger(__name__)
+
+
+class BaseBucketObjectSubService(BasePageableObjectMixin,
+                                 BucketObjectSubService):
+
+    def __init__(self, provider, bucket):
+        self.__provider = provider
+        self.bucket = bucket
+
+    @property
+    def _provider(self):
+        return self.__provider
+
+    def get(self, name):
+        return self._provider.storage._bucket_objects.get(self.bucket, name)
+
+    def list(self, limit=None, marker=None, prefix=None):
+        return self._provider.storage._bucket_objects.list(self.bucket, limit,
+                                                           marker, prefix)
+
+    def find(self, **kwargs):
+        return self._provider.storage._bucket_objects.find(self.bucket,
+                                                           **kwargs)
+
+    def create(self, name):
+        return self._provider.storage._bucket_objects.create(self.bucket, name)
+
+
+class BaseGatewaySubService(GatewaySubService, BasePageableObjectMixin):
+
+    def __init__(self, provider, network):
+        self._network = network
+        self._provider = provider
+
+
+class BaseVMFirewallRuleSubService(BasePageableObjectMixin,
+                                   VMFirewallRuleSubService):
+
+    def __init__(self, provider, firewall):
+        self.__provider = provider
+        self.firewall = firewall
+
+    @property
+    def _provider(self):
+        return self.__provider
+
+    def get(self, rule_id):
+        matches = [rule for rule in self if rule.id == rule_id]
+        if matches:
+            return matches[0]
+        else:
+            return None
+
+    def find(self, **kwargs):
+        obj_list = self
+        filters = ['name', 'direction', 'protocol', 'from_port', 'to_port',
+                   'cidr', 'src_dest_fw', 'src_dest_fw_id']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
+        return ClientPagedResultList(self._provider, list(matches))
+
+    def delete(self, rule_id):
+        rule = self.get(rule_id)
+        if rule:
+            rule.delete()
+
+
+class BaseFloatingIPSubService(FloatingIPSubService, BasePageableObjectMixin):
+
+    def __init__(self, provider, gateway):
+        self.__provider = provider
+        self.gateway = gateway
+
+    @property
+    def _provider(self):
+        return self.__provider
+
+    def find(self, **kwargs):
+        obj_list = self
+        filters = ['name', 'public_ip']
+        matches = cb_helpers.generic_find(filters, kwargs, obj_list)
+        return ClientPagedResultList(self._provider, list(matches))
+
+    def delete(self, fip_id):
+        floating_ip = self.get(fip_id)
+        if floating_ip:
+            floating_ip.delete()

+ 6 - 322
cloudbridge/cloud/interfaces/resources.py

@@ -989,8 +989,8 @@ class Network(ObjectLifeCycleMixin, LabeledCloudResource):
         """
         Provides access to the internet gateways attached to this network.
 
-        :rtype: :class:`.GatewayContainer`
-        :return: A GatewayContainer object
+        :rtype: :class:`.GatewaySubService`
+        :return: A GatewaySubService object
         """
         pass
 
@@ -1082,76 +1082,6 @@ class Subnet(ObjectLifeCycleMixin, LabeledCloudResource):
         pass
 
 
-class FloatingIPContainer(PageableObjectMixin):
-    """
-    Base interface for a FloatingIP Service.
-    """
-    __metaclass__ = ABCMeta
-
-    @abstractmethod
-    def get(self, fip_id):
-        """
-        Returns a FloatingIP given its ID or ``None`` if not found.
-
-        :type fip_id: ``str``
-        :param fip_id: The ID of the FloatingIP to retrieve.
-
-        :rtype: ``object`` of :class:`.FloatingIP`
-        :return: a FloatingIP object
-        """
-        pass
-
-    @abstractmethod
-    def list(self, limit=None, marker=None):
-        """
-        List floating (i.e., static) IP addresses.
-
-        :rtype: ``list`` of :class:`.FloatingIP`
-        :return: list of FloatingIP objects
-        """
-        pass
-
-    @abstractmethod
-    def find(self, **kwargs):
-        """
-        Searches for a FloatingIP by a given list of attributes.
-
-        Supported attributes: name, public_ip
-
-        Example:
-
-        .. code-block:: python
-
-            fip = provider.networking.gateways.get('id').floating_ips.find(
-                        public_ip='public_ip')
-
-
-        :rtype: List of ``object`` of :class:`.FloatingIP`
-        :return: A list of FloatingIP objects matching the supplied attributes.
-        """
-        pass
-
-    @abstractmethod
-    def create(self):
-        """
-        Allocate a new floating (i.e., static) IP address.
-
-        :rtype: ``object`` of :class:`.FloatingIP`
-        :return:  A FloatingIP object
-        """
-        pass
-
-    @abstractmethod
-    def delete(self, fip_id):
-        """
-        Delete an existing FloatingIP.
-
-        :type fip_id: ``str``
-        :param fip_id: The ID of the FloatingIP to be deleted.
-        """
-        pass
-
-
 class FloatingIpState(object):
 
     """
@@ -1357,49 +1287,6 @@ class GatewayState(object):
     ERROR = "error"
 
 
-class GatewayContainer(PageableObjectMixin):
-    """
-    Manage internet gateway resources.
-    """
-    __metaclass__ = ABCMeta
-
-    @abstractmethod
-    def get_or_create_inet_gateway(self):
-        """
-        Creates new or returns an existing internet gateway for a network.
-
-        The returned gateway object can subsequently be attached to a router to
-        provide internet routing to a network.
-
-        :type  name: ``str``
-        :param name: The gateway label.
-
-        :rtype: ``object``  of :class:`.InternetGateway` or ``None``
-        :return: an InternetGateway object of ``None`` if not found.
-        """
-        pass
-
-    @abstractmethod
-    def delete(self, gateway):
-        """
-        Delete a gateway.
-
-        :type gateway: :class:`.Gateway` object
-        :param gateway: Gateway object to delete.
-        """
-        pass
-
-    @abstractmethod
-    def list(self, limit=None, marker=None):
-        """
-        List all available internet gateways.
-
-        :rtype: ``list`` of :class:`.InternetGateway` or ``None``
-        :return: Current list of internet gateways.
-        """
-        pass
-
-
 class Gateway(CloudResource):
     """
     Represents a gateway resource.
@@ -1429,8 +1316,8 @@ class Gateway(CloudResource):
         """
         Provides access to floating IPs connected to this internet gateway.
 
-        :rtype: :class:`.FloatingIPContainer`
-        :return: A FloatingIPContainer object
+        :rtype: :class:`.FloatingIPSubService`
+        :return: A FloatingIPSubService object
         """
         pass
 
@@ -2027,143 +1914,8 @@ class VMFirewall(LabeledCloudResource):
         This object can be used for further operations on rules, such as get,
         list, create, etc.
 
-        :rtype: An object of :class:`.VMFirewallRuleContainer`
-        :return: A VMFirewallRuleContainer for further operations
-        """
-        pass
-
-
-class VMFirewallRuleContainer(PageableObjectMixin):
-    """
-    Base interface for Firewall rules.
-    """
-    __metaclass__ = ABCMeta
-
-    @abstractmethod
-    def get(self, rule_id):
-        """
-        Return a firewall rule given its ID.
-
-        Returns ``None`` if the rule does not exist.
-
-        Example:
-
-        .. code-block:: python
-
-            fw = provider.security.vm_firewalls.get('my_fw_id')
-            rule = fw.rules.get('rule_id')
-            print(rule.id, rule.label)
-
-        :rtype: :class:`.FirewallRule`
-        :return:  a FirewallRule instance
-        """
-        pass
-
-    @abstractmethod
-    def list(self, limit=None, marker=None):
-        """
-        List all firewall rules associated with this firewall.
-
-        :rtype: ``list`` of :class:`.FirewallRule`
-        :return:  list of Firewall rule objects
-        """
-        pass
-
-    @abstractmethod
-    def create(self,  direction, protocol=None, from_port=None,
-               to_port=None, cidr=None, src_dest_fw=None):
-        """
-        Create a VM firewall rule.
-
-        If a matching rule already exists, return it.
-
-        Example:
-
-        .. code-block:: python
-            from cloudbridge.cloud.interfaces.resources import TrafficDirection
-            from cloudbridge.cloud.interfaces.resources import BaseNetwork
-
-            fw = provider.security.vm_firewalls.get('my_fw_id')
-            fw.rules.create(TrafficDirection.INBOUND, protocol='tcp',
-                            from_port=80, to_port=80,
-                            cidr=BaseNetwork.CB_DEFAULT_IPV4RANGE)
-            fw.rules.create(TrafficDirection.INBOUND, src_dest_fw=fw)
-            fw.rules.create(TrafficDirection.OUTBOUND, src_dest_fw=fw)
-
-        You need to pass in either ``src_dest_fw`` OR ``protocol`` AND
-        ``from_port``, ``to_port``, ``cidr``. In other words, either
-        you are authorizing another group or you are authorizing some
-        IP-based rule.
-
-        :type direction: :class:``.TrafficDirection``
-        :param direction: Either ``TrafficDirection.INBOUND`` |
-                          ``TrafficDirection.OUTBOUND``
-
-        :type protocol: ``str``
-        :param protocol: Either ``tcp`` | ``udp`` | ``icmp``.
-
-        :type from_port: ``int``
-        :param from_port: The beginning port number you are enabling.
-
-        :type to_port: ``int``
-        :param to_port: The ending port number you are enabling.
-
-        :type cidr: ``str`` or list of ``str``
-        :param cidr: The CIDR block you are providing access to.
-
-        :type src_dest_fw: :class:`.VMFirewall`
-        :param src_dest_fw: The VM firewall object which is the
-                            source/destination of the traffic, depending on
-                            whether it's ingress/egress traffic.
-
-        :rtype: :class:`.VMFirewallRule`
-        :return: Rule object if successful or ``None``.
-        """
-        pass
-
-    @abstractmethod
-    def find(self, **kwargs):
-        """
-        Find a firewall rule filtered by the given parameters.
-
-        :type label: str
-        :param label: The label of the VM firewall to retrieve.
-
-        :type protocol: ``str``
-        :param protocol: Either ``tcp`` | ``udp`` | ``icmp``.
-
-        :type from_port: ``int``
-        :param from_port: The beginning port number you are enabling.
-
-        :type to_port: ``int``
-        :param to_port: The ending port number you are enabling.
-
-        :type cidr: ``str`` or list of ``str``
-        :param cidr: The CIDR block you are providing access to.
-
-        :type src_dest_fw: :class:`.VMFirewall`
-        :param src_dest_fw: The VM firewall object which is the
-                            source/destination of the traffic, depending on
-                            whether it's ingress/egress traffic.
-
-        :type src_dest_fw_id: :class:`.str`
-        :param src_dest_fw_id: The VM firewall id which is the
-                               source/destination of the traffic, depending on
-                               whether it's ingress/egress traffic.
-
-        :rtype: list of :class:`VMFirewallRule`
-        :return: A list of VMFirewall objects or an empty list if none
-                 found.
-        """
-        pass
-
-    @abstractmethod
-    def delete(self, rule_id):
-        """
-        Delete an existing VMFirewall rule.
-
-        :type rule_id: str
-        :param rule_id: The VM firewall rule to be deleted.
+        :rtype: An object of :class:`.VMFirewallRuleSubService`
+        :return: A VMFirewallRuleSubService for further operations
         """
         pass
 
@@ -2435,71 +2187,3 @@ class Bucket(CloudResource):
         :return: ``True`` if successful.
         """
         pass
-
-
-class BucketContainer(PageableObjectMixin):
-    """
-    A container service for objects within a bucket.
-    """
-    __metaclass__ = ABCMeta
-
-    @abstractmethod
-    def get(self, name):
-        """
-        Retrieve a given object from this bucket.
-
-        :type name: ``str``
-        :param name: The identifier of the object to retrieve
-
-        :rtype: :class:``.BucketObject``
-        :return: The BucketObject or ``None`` if it cannot be found.
-        """
-        pass
-
-    @abstractmethod
-    # pylint:disable=arguments-differ
-    def list(self, limit=None, marker=None, prefix=None):
-        """
-        List objects in this bucket.
-
-        :type limit: ``int``
-        :param limit: Maximum number of elements to return.
-
-        :type marker: ``int``
-        :param marker: Fetch results after this offset.
-
-        :type prefix: ``str``
-        :param prefix: Prefix criteria by which to filter listed objects.
-
-        :rtype: List of ``objects`` of :class:``.BucketObject``
-        :return: List of all available BucketObjects within this bucket.
-        """
-        pass
-
-    @abstractmethod
-    def find(self, **kwargs):
-        """
-        Search for an object by a given list of attributes.
-
-        Supported attributes: ``name``
-
-        :rtype: List of ``objects`` of :class:`.BucketObject`
-        :return: A list of BucketObjects matching the supplied attributes.
-
-        :type limit: ``int``
-        :param limit: Maximum number of elements to return.
-
-        :type marker: ``int``
-        :param marker: Fetch results after this offset.
-        """
-        pass
-
-    @abstractmethod
-    def create(self, name):
-        """
-        Create a new object within this bucket.
-
-        :rtype: :class:``.BucketObject``
-        :return: The newly created bucket object
-        """
-        pass

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

@@ -965,7 +965,7 @@ class BucketService(PageableObjectMixin, CloudService):
         pass
 
 
-class BucketObjectService(PageableObjectMixin, CloudService):
+class BucketObjectService(CloudService):
 
     """
     The Bucket Object Service interface provides access to the underlying
@@ -988,7 +988,7 @@ class BucketObjectService(PageableObjectMixin, CloudService):
         .. code-block:: python
 
             bucket = provider.storage.buckets.get('my_bucket_id')
-            buck_obj = provider.storage.bucket_objects.get('my_object_id',
+            buck_obj = provider.storage._bucket_objects.get('my_object_id',
                                                            bucket)
             print(buck_obj.id, buck_obj.name)
 
@@ -1009,7 +1009,7 @@ class BucketObjectService(PageableObjectMixin, CloudService):
         .. code-block:: python
 
             bucket = provider.storage.buckets.get('my_bucket_id')
-            objs = provider.storage.bucket_objects.find(bucket,
+            objs = provider.storage._bucket_objects.find(bucket,
                                                         name='my_obj_name')
             for buck_obj in objs:
                 print(buck_obj.id, buck_obj.name)
@@ -1029,7 +1029,7 @@ class BucketObjectService(PageableObjectMixin, CloudService):
         .. code-block:: python
 
             bucket = provider.storage.buckets.get('my_bucket_id')
-            objs = provider.storage.bucket_objects.list(bucket)
+            objs = provider.storage._bucket_objects.list(bucket)
             for buck_obj in objs:
                 print(buck_obj.id, buck_obj.name)
 
@@ -1048,7 +1048,7 @@ class BucketObjectService(PageableObjectMixin, CloudService):
         .. code-block:: python
 
             bucket = provider.storage.buckets.get('my_bucket_id')
-            buck_obj = provider.storage.bucket_objects.create('my_name',
+            buck_obj = provider.storage._bucket_objects.create('my_name',
                                                               bucket)
             print(buck_obj.name)
 

+ 320 - 0
cloudbridge/cloud/interfaces/subservices.py

@@ -0,0 +1,320 @@
+from abc import ABCMeta
+from abc import abstractmethod
+
+from cloudbridge.cloud.interfaces.resources import PageableObjectMixin
+
+
+class BucketObjectSubService(PageableObjectMixin):
+    """
+    A container service for objects within a bucket.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def get(self, name):
+        """
+        Retrieve a given object from this bucket.
+
+        :type name: ``str``
+        :param name: The identifier of the object to retrieve
+
+        :rtype: :class:``.BucketObject``
+        :return: The BucketObject or ``None`` if it cannot be found.
+        """
+        pass
+
+    @abstractmethod
+    # pylint:disable=arguments-differ
+    def list(self, limit=None, marker=None, prefix=None):
+        """
+        List objects in this bucket.
+
+        :type limit: ``int``
+        :param limit: Maximum number of elements to return.
+
+        :type marker: ``int``
+        :param marker: Fetch results after this offset.
+
+        :type prefix: ``str``
+        :param prefix: Prefix criteria by which to filter listed objects.
+
+        :rtype: List of ``objects`` of :class:``.BucketObject``
+        :return: List of all available BucketObjects within this bucket.
+        """
+        pass
+
+    @abstractmethod
+    def find(self, **kwargs):
+        """
+        Search for an object by a given list of attributes.
+
+        Supported attributes: ``name``
+
+        :rtype: List of ``objects`` of :class:`.BucketObject`
+        :return: A list of BucketObjects matching the supplied attributes.
+
+        :type limit: ``int``
+        :param limit: Maximum number of elements to return.
+
+        :type marker: ``int``
+        :param marker: Fetch results after this offset.
+        """
+        pass
+
+    @abstractmethod
+    def create(self, name):
+        """
+        Create a new object within this bucket.
+
+        :rtype: :class:``.BucketObject``
+        :return: The newly created bucket object
+        """
+        pass
+
+
+class GatewaySubService(PageableObjectMixin):
+    """
+    Manage internet gateway resources.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def get_or_create_inet_gateway(self):
+        """
+        Creates new or returns an existing internet gateway for a network.
+
+        The returned gateway object can subsequently be attached to a router to
+        provide internet routing to a network.
+
+        :type  name: ``str``
+        :param name: The gateway label.
+
+        :rtype: ``object``  of :class:`.InternetGateway` or ``None``
+        :return: an InternetGateway object of ``None`` if not found.
+        """
+        pass
+
+    @abstractmethod
+    def delete(self, gateway):
+        """
+        Delete a gateway.
+
+        :type gateway: :class:`.Gateway` object
+        :param gateway: Gateway object to delete.
+        """
+        pass
+
+    @abstractmethod
+    def list(self, limit=None, marker=None):
+        """
+        List all available internet gateways.
+
+        :rtype: ``list`` of :class:`.InternetGateway` or ``None``
+        :return: Current list of internet gateways.
+        """
+        pass
+
+
+class FloatingIPSubService(PageableObjectMixin):
+    """
+    Base interface for a FloatingIP Service.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def get(self, fip_id):
+        """
+        Returns a FloatingIP given its ID or ``None`` if not found.
+
+        :type fip_id: ``str``
+        :param fip_id: The ID of the FloatingIP to retrieve.
+
+        :rtype: ``object`` of :class:`.FloatingIP`
+        :return: a FloatingIP object
+        """
+        pass
+
+    @abstractmethod
+    def list(self, limit=None, marker=None):
+        """
+        List floating (i.e., static) IP addresses.
+
+        :rtype: ``list`` of :class:`.FloatingIP`
+        :return: list of FloatingIP objects
+        """
+        pass
+
+    @abstractmethod
+    def find(self, **kwargs):
+        """
+        Searches for a FloatingIP by a given list of attributes.
+
+        Supported attributes: name, public_ip
+
+        Example:
+
+        .. code-block:: python
+
+            fip = provider.networking.gateways.get('id').floating_ips.find(
+                        public_ip='public_ip')
+
+
+        :rtype: List of ``object`` of :class:`.FloatingIP`
+        :return: A list of FloatingIP objects matching the supplied attributes.
+        """
+        pass
+
+    @abstractmethod
+    def create(self):
+        """
+        Allocate a new floating (i.e., static) IP address.
+
+        :rtype: ``object`` of :class:`.FloatingIP`
+        :return:  A FloatingIP object
+        """
+        pass
+
+    @abstractmethod
+    def delete(self, fip_id):
+        """
+        Delete an existing FloatingIP.
+
+        :type fip_id: ``str``
+        :param fip_id: The ID of the FloatingIP to be deleted.
+        """
+        pass
+
+
+class VMFirewallRuleSubService(PageableObjectMixin):
+    """
+    Base interface for Firewall rules.
+    """
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def get(self, rule_id):
+        """
+        Return a firewall rule given its ID.
+
+        Returns ``None`` if the rule does not exist.
+
+        Example:
+
+        .. code-block:: python
+
+            fw = provider.security.vm_firewalls.get('my_fw_id')
+            rule = fw.rules.get('rule_id')
+            print(rule.id, rule.label)
+
+        :rtype: :class:`.FirewallRule`
+        :return:  a FirewallRule instance
+        """
+        pass
+
+    @abstractmethod
+    def list(self, limit=None, marker=None):
+        """
+        List all firewall rules associated with this firewall.
+
+        :rtype: ``list`` of :class:`.FirewallRule`
+        :return:  list of Firewall rule objects
+        """
+        pass
+
+    @abstractmethod
+    def create(self,  direction, protocol=None, from_port=None,
+               to_port=None, cidr=None, src_dest_fw=None):
+        """
+        Create a VM firewall rule.
+
+        If a matching rule already exists, return it.
+
+        Example:
+
+        .. code-block:: python
+            from cloudbridge.cloud.interfaces.resources import TrafficDirection
+            from cloudbridge.cloud.interfaces.resources import BaseNetwork
+
+            fw = provider.security.vm_firewalls.get('my_fw_id')
+            fw.rules.create(TrafficDirection.INBOUND, protocol='tcp',
+                            from_port=80, to_port=80,
+                            cidr=BaseNetwork.CB_DEFAULT_IPV4RANGE)
+            fw.rules.create(TrafficDirection.INBOUND, src_dest_fw=fw)
+            fw.rules.create(TrafficDirection.OUTBOUND, src_dest_fw=fw)
+
+        You need to pass in either ``src_dest_fw`` OR ``protocol`` AND
+        ``from_port``, ``to_port``, ``cidr``. In other words, either
+        you are authorizing another group or you are authorizing some
+        IP-based rule.
+
+        :type direction: :class:``.TrafficDirection``
+        :param direction: Either ``TrafficDirection.INBOUND`` |
+                          ``TrafficDirection.OUTBOUND``
+
+        :type protocol: ``str``
+        :param protocol: Either ``tcp`` | ``udp`` | ``icmp``.
+
+        :type from_port: ``int``
+        :param from_port: The beginning port number you are enabling.
+
+        :type to_port: ``int``
+        :param to_port: The ending port number you are enabling.
+
+        :type cidr: ``str`` or list of ``str``
+        :param cidr: The CIDR block you are providing access to.
+
+        :type src_dest_fw: :class:`.VMFirewall`
+        :param src_dest_fw: The VM firewall object which is the
+                            source/destination of the traffic, depending on
+                            whether it's ingress/egress traffic.
+
+        :rtype: :class:`.VMFirewallRule`
+        :return: Rule object if successful or ``None``.
+        """
+        pass
+
+    @abstractmethod
+    def find(self, **kwargs):
+        """
+        Find a firewall rule filtered by the given parameters.
+
+        :type label: str
+        :param label: The label of the VM firewall to retrieve.
+
+        :type protocol: ``str``
+        :param protocol: Either ``tcp`` | ``udp`` | ``icmp``.
+
+        :type from_port: ``int``
+        :param from_port: The beginning port number you are enabling.
+
+        :type to_port: ``int``
+        :param to_port: The ending port number you are enabling.
+
+        :type cidr: ``str`` or list of ``str``
+        :param cidr: The CIDR block you are providing access to.
+
+        :type src_dest_fw: :class:`.VMFirewall`
+        :param src_dest_fw: The VM firewall object which is the
+                            source/destination of the traffic, depending on
+                            whether it's ingress/egress traffic.
+
+        :type src_dest_fw_id: :class:`.str`
+        :param src_dest_fw_id: The VM firewall id which is the
+                               source/destination of the traffic, depending on
+                               whether it's ingress/egress traffic.
+
+        :rtype: list of :class:`VMFirewallRule`
+        :return: A list of VMFirewall objects or an empty list if none
+                 found.
+        """
+        pass
+
+    @abstractmethod
+    def delete(self, rule_id):
+        """
+        Delete an existing VMFirewall rule.
+
+        :type rule_id: str
+        :param rule_id: The VM firewall rule to be deleted.
+        """
+        pass

+ 8 - 136
cloudbridge/cloud/providers/aws/resources.py

@@ -9,11 +9,8 @@ from botocore.exceptions import ClientError
 
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseBucket
-from cloudbridge.cloud.base.resources import BaseBucketContainer
 from cloudbridge.cloud.base.resources import BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
-from cloudbridge.cloud.base.resources import BaseFloatingIPContainer
-from cloudbridge.cloud.base.resources import BaseGatewayContainer
 from cloudbridge.cloud.base.resources import BaseInstance
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
@@ -27,11 +24,8 @@ from cloudbridge.cloud.base.resources import BaseSnapshot
 from cloudbridge.cloud.base.resources import BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewallRule
-from cloudbridge.cloud.base.resources import BaseVMFirewallRuleContainer
 from cloudbridge.cloud.base.resources import BaseVMType
 from cloudbridge.cloud.base.resources import BaseVolume
-from cloudbridge.cloud.base.resources import ClientPagedResultList
-from cloudbridge.cloud.interfaces.exceptions import InvalidValueException
 from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
@@ -42,9 +36,12 @@ from cloudbridge.cloud.interfaces.resources import SubnetState
 from cloudbridge.cloud.interfaces.resources import TrafficDirection
 from cloudbridge.cloud.interfaces.resources import VolumeState
 
-from .helpers import BotoEC2Service
 from .helpers import find_tag_value
 from .helpers import trim_empty_params
+from .subservices import AWSBucketObjectSubService
+from .subservices import AWSFloatingIPSubService
+from .subservices import AWSGatewaySubService
+from .subservices import AWSVMFirewallRuleSubService
 
 log = logging.getLogger(__name__)
 
@@ -615,7 +612,7 @@ class AWSVMFirewall(BaseVMFirewall):
 
     def __init__(self, provider, _vm_firewall):
         super(AWSVMFirewall, self).__init__(provider, _vm_firewall)
-        self._rule_container = AWSVMFirewallRuleContainer(provider, self)
+        self._rule_container = AWSVMFirewallRuleSubService(provider, self)
 
     @property
     def name(self):
@@ -659,56 +656,6 @@ class AWSVMFirewall(BaseVMFirewall):
         return js
 
 
-class AWSVMFirewallRuleContainer(BaseVMFirewallRuleContainer):
-
-    def __init__(self, provider, firewall):
-        super(AWSVMFirewallRuleContainer, self).__init__(provider, firewall)
-
-    def list(self, limit=None, marker=None):
-        # pylint:disable=protected-access
-        rules = [AWSVMFirewallRule(self.firewall,
-                                   TrafficDirection.INBOUND, r)
-                 for r in self.firewall._vm_firewall.ip_permissions]
-        rules = rules + [
-            AWSVMFirewallRule(
-                self.firewall, TrafficDirection.OUTBOUND, r)
-            for r in self.firewall._vm_firewall.ip_permissions_egress]
-        return ClientPagedResultList(self._provider, rules,
-                                     limit=limit, marker=marker)
-
-    def create(self,  direction, protocol=None, from_port=None,
-               to_port=None, cidr=None, src_dest_fw=None):
-        src_dest_fw_id = (
-            src_dest_fw.id if isinstance(src_dest_fw, AWSVMFirewall)
-            else src_dest_fw)
-
-        # pylint:disable=protected-access
-        ip_perm_entry = AWSVMFirewallRule._construct_ip_perms(
-            protocol, from_port, to_port, cidr, src_dest_fw_id)
-        # Filter out empty values to please Boto
-        ip_perms = [trim_empty_params(ip_perm_entry)]
-
-        try:
-            if direction == TrafficDirection.INBOUND:
-                # pylint:disable=protected-access
-                self.firewall._vm_firewall.authorize_ingress(
-                    IpPermissions=ip_perms)
-            elif direction == TrafficDirection.OUTBOUND:
-                # pylint:disable=protected-access
-                self.firewall._vm_firewall.authorize_egress(
-                    IpPermissions=ip_perms)
-            else:
-                raise InvalidValueException("direction", direction)
-            self.firewall.refresh()
-            return AWSVMFirewallRule(self.firewall, direction, ip_perm_entry)
-        except ClientError as ec2e:
-            if ec2e.response['Error']['Code'] == "InvalidPermission.Duplicate":
-                return AWSVMFirewallRule(
-                    self.firewall, direction, ip_perm_entry)
-            else:
-                raise ec2e
-
-
 class AWSVMFirewallRule(BaseVMFirewallRule):
 
     def __init__(self, parent_fw, direction, rule):
@@ -865,7 +812,7 @@ class AWSBucket(BaseBucket):
     def __init__(self, provider, bucket):
         super(AWSBucket, self).__init__(provider)
         self._bucket = bucket
-        self._object_container = AWSBucketContainer(provider, self)
+        self._object_container = AWSBucketObjectSubService(provider, self)
 
     @property
     def id(self):
@@ -880,12 +827,6 @@ class AWSBucket(BaseBucket):
         return self._object_container
 
 
-class AWSBucketContainer(BaseBucketContainer):
-
-    def __init__(self, provider, bucket):
-        super(AWSBucketContainer, self).__init__(provider, bucket)
-
-
 class AWSRegion(BaseRegion):
 
     def __init__(self, provider, aws_region):
@@ -927,7 +868,7 @@ class AWSNetwork(BaseNetwork):
     def __init__(self, provider, network):
         super(AWSNetwork, self).__init__(provider)
         self._vpc = network
-        self._gtw_container = AWSGatewayContainer(provider, self)
+        self._gtw_container = AWSGatewaySubService(provider, self)
         self._unknown_state = False
 
     @property
@@ -1058,31 +999,6 @@ class AWSSubnet(BaseSubnet):
             self._unknown_state = True
 
 
-class AWSFloatingIPContainer(BaseFloatingIPContainer):
-
-    def __init__(self, provider, gateway):
-        super(AWSFloatingIPContainer, self).__init__(provider, gateway)
-        self.svc = BotoEC2Service(provider=self._provider,
-                                  cb_resource=AWSFloatingIP,
-                                  boto_collection_name='vpc_addresses')
-
-    def get(self, fip_id):
-        log.debug("Getting AWS Floating IP Service with the id: %s", fip_id)
-        return self.svc.get(fip_id)
-
-    def list(self, limit=None, marker=None):
-        log.debug("Listing all floating IPs under gateway %s", self.gateway)
-        return self.svc.list(limit=limit, marker=marker)
-
-    def create(self):
-        log.debug("Creating a floating IP under gateway %s", self.gateway)
-        ip = self._provider.ec2_conn.meta.client.allocate_address(
-            Domain='vpc')
-        return AWSFloatingIP(
-            self._provider,
-            self._provider.ec2_conn.VpcAddress(ip.get('AllocationId')))
-
-
 class AWSFloatingIP(BaseFloatingIP):
 
     def __init__(self, provider, floating_ip):
@@ -1186,57 +1102,13 @@ class AWSRouter(BaseRouter):
             InternetGatewayId=gw_id, VpcId=self._route_table.vpc_id)
 
 
-class AWSGatewayContainer(BaseGatewayContainer):
-
-    def __init__(self, provider, network):
-        super(AWSGatewayContainer, self).__init__(provider, network)
-        self.svc = BotoEC2Service(provider=provider,
-                                  cb_resource=AWSInternetGateway,
-                                  boto_collection_name='internet_gateways')
-
-    def get_or_create_inet_gateway(self):
-        log.debug("Get or create inet gateway on net %s",
-                  self._network)
-        network_id = self._network.id if isinstance(
-            self._network, AWSNetwork) else self._network
-        # Don't filter by label because it may conflict with at least the
-        # default VPC that most accounts have but that network is typically
-        # without a name.
-        gtw = self.svc.find(filter_name='attachment.vpc-id',
-                            filter_value=network_id)
-        if gtw:
-            return gtw[0]  # There can be only one gtw attached to a VPC
-        # Gateway does not exist so create one and attach to the supplied net
-        cb_gateway = self.svc.create('create_internet_gateway')
-        cb_gateway._gateway.create_tags(
-            Tags=[{'Key': 'Name',
-                   'Value': AWSInternetGateway.CB_DEFAULT_INET_GATEWAY_NAME
-                   }])
-        cb_gateway._gateway.attach_to_vpc(VpcId=network_id)
-        return cb_gateway
-
-    def delete(self, gateway):
-        log.debug("Service deleting AWS Gateway %s", gateway)
-        gateway_id = gateway.id if isinstance(
-            gateway, AWSInternetGateway) else gateway
-        gateway = self.svc.get(gateway_id)
-        if gateway:
-            gateway.delete()
-
-    def list(self, limit=None, marker=None):
-        log.debug("Listing current AWS internet gateways for net %s.",
-                  self._network.id)
-        fltr = [{'Name': 'attachment.vpc-id', 'Values': [self._network.id]}]
-        return self.svc.list(limit=None, marker=None, Filters=fltr)
-
-
 class AWSInternetGateway(BaseInternetGateway):
 
     def __init__(self, provider, gateway):
         super(AWSInternetGateway, self).__init__(provider)
         self._gateway = gateway
         self._gateway.state = ''
-        self._fips_container = AWSFloatingIPContainer(provider, self)
+        self._fips_container = AWSFloatingIPSubService(provider, self)
 
     @property
     def id(self):

+ 1 - 1
cloudbridge/cloud/providers/aws/services.py

@@ -220,7 +220,7 @@ class AWSStorageService(BaseStorageService):
         return self._bucket_svc
 
     @property
-    def bucket_objects(self):
+    def _bucket_objects(self):
         return self._bucket_obj_svc
 
 

+ 147 - 0
cloudbridge/cloud/providers/aws/subservices.py

@@ -0,0 +1,147 @@
+import logging
+
+from botocore.exceptions import ClientError
+
+from cloudbridge.cloud.base.resources import ClientPagedResultList
+from cloudbridge.cloud.base.subservices import BaseBucketObjectSubService
+from cloudbridge.cloud.base.subservices import BaseFloatingIPSubService
+from cloudbridge.cloud.base.subservices import \
+    BaseGatewaySubService
+from cloudbridge.cloud.base.subservices import BaseVMFirewallRuleSubService
+from cloudbridge.cloud.interfaces.exceptions import InvalidValueException
+from cloudbridge.cloud.interfaces.resources import TrafficDirection
+
+from .helpers import BotoEC2Service
+from .helpers import trim_empty_params
+from .resources import AWSFloatingIP
+from .resources import AWSInternetGateway
+from .resources import AWSNetwork
+from .resources import AWSVMFirewall
+from .resources import AWSVMFirewallRule
+
+log = logging.getLogger(__name__)
+
+
+class AWSBucketObjectSubService(BaseBucketObjectSubService):
+
+    def __init__(self, provider, bucket):
+        super(AWSBucketObjectSubService, self).__init__(provider, bucket)
+
+
+class AWSVMFirewallRuleSubService(BaseVMFirewallRuleSubService):
+
+    def __init__(self, provider, firewall):
+        super(AWSVMFirewallRuleSubService, self).__init__(provider, firewall)
+
+    def list(self, limit=None, marker=None):
+        # pylint:disable=protected-access
+        rules = [AWSVMFirewallRule(self.firewall,
+                                   TrafficDirection.INBOUND, r)
+                 for r in self.firewall._vm_firewall.ip_permissions]
+        rules = rules + [
+            AWSVMFirewallRule(
+                self.firewall, TrafficDirection.OUTBOUND, r)
+            for r in self.firewall._vm_firewall.ip_permissions_egress]
+        return ClientPagedResultList(self._provider, rules,
+                                     limit=limit, marker=marker)
+
+    def create(self,  direction, protocol=None, from_port=None,
+               to_port=None, cidr=None, src_dest_fw=None):
+        src_dest_fw_id = (
+            src_dest_fw.id if isinstance(src_dest_fw, AWSVMFirewall)
+            else src_dest_fw)
+
+        # pylint:disable=protected-access
+        ip_perm_entry = AWSVMFirewallRule._construct_ip_perms(
+            protocol, from_port, to_port, cidr, src_dest_fw_id)
+        # Filter out empty values to please Boto
+        ip_perms = [trim_empty_params(ip_perm_entry)]
+
+        try:
+            if direction == TrafficDirection.INBOUND:
+                # pylint:disable=protected-access
+                self.firewall._vm_firewall.authorize_ingress(
+                    IpPermissions=ip_perms)
+            elif direction == TrafficDirection.OUTBOUND:
+                # pylint:disable=protected-access
+                self.firewall._vm_firewall.authorize_egress(
+                    IpPermissions=ip_perms)
+            else:
+                raise InvalidValueException("direction", direction)
+            self.firewall.refresh()
+            return AWSVMFirewallRule(self.firewall, direction, ip_perm_entry)
+        except ClientError as ec2e:
+            if ec2e.response['Error']['Code'] == "InvalidPermission.Duplicate":
+                return AWSVMFirewallRule(
+                    self.firewall, direction, ip_perm_entry)
+            else:
+                raise ec2e
+
+
+class AWSFloatingIPSubService(BaseFloatingIPSubService):
+
+    def __init__(self, provider, gateway):
+        super(AWSFloatingIPSubService, self).__init__(provider, gateway)
+        self.svc = BotoEC2Service(provider=self._provider,
+                                  cb_resource=AWSFloatingIP,
+                                  boto_collection_name='vpc_addresses')
+
+    def get(self, fip_id):
+        log.debug("Getting AWS Floating IP Service with the id: %s", fip_id)
+        return self.svc.get(fip_id)
+
+    def list(self, limit=None, marker=None):
+        log.debug("Listing all floating IPs under gateway %s", self.gateway)
+        return self.svc.list(limit=limit, marker=marker)
+
+    def create(self):
+        log.debug("Creating a floating IP under gateway %s", self.gateway)
+        ip = self._provider.ec2_conn.meta.client.allocate_address(
+            Domain='vpc')
+        return AWSFloatingIP(
+            self._provider,
+            self._provider.ec2_conn.VpcAddress(ip.get('AllocationId')))
+
+
+class AWSGatewaySubService(BaseGatewaySubService):
+
+    def __init__(self, provider, network):
+        super(AWSGatewaySubService, self).__init__(provider, network)
+        self.svc = BotoEC2Service(provider=provider,
+                                  cb_resource=AWSInternetGateway,
+                                  boto_collection_name='internet_gateways')
+
+    def get_or_create_inet_gateway(self):
+        log.debug("Get or create inet gateway on net %s",
+                  self._network)
+        network_id = self._network.id if isinstance(
+            self._network, AWSNetwork) else self._network
+        # Don't filter by label because it may conflict with at least the
+        # default VPC that most accounts have but that network is typically
+        # without a name.
+        gtw = self.svc.find(filter_name='attachment.vpc-id',
+                            filter_value=network_id)
+        if gtw:
+            return gtw[0]  # There can be only one gtw attached to a VPC
+        # Gateway does not exist so create one and attach to the supplied net
+        cb_gateway = self.svc.create('create_internet_gateway')
+        cb_gateway._gateway.create_tags(
+            Tags=[{'Key': 'Name',
+                   'Value': AWSInternetGateway.CB_DEFAULT_INET_GATEWAY_NAME
+                   }])
+        cb_gateway._gateway.attach_to_vpc(VpcId=network_id)
+        return cb_gateway
+
+    def delete(self, gateway):
+        log.debug("Service deleting AWS Gateway %s", gateway)
+        gateway_id = gateway.id if isinstance(
+            gateway, AWSInternetGateway) else gateway
+        gateway = self.svc.get(gateway_id)
+        if gateway:
+            gateway.delete()
+
+    def list(self, limit=None, marker=None):
+        log.debug("Listing current AWS internet gateways for net %s.",
+                  self._network.id)
+        fltr = [{'Name': 'attachment.vpc-id', 'Values': [self._network.id]}]
+        return self.svc.list(limit=None, marker=None, Filters=fltr)

+ 8 - 135
cloudbridge/cloud/providers/azure/resources.py

@@ -3,7 +3,6 @@ DataTypes used by this provider
 """
 import collections
 import logging
-from uuid import uuid4
 
 from azure.common import AzureException
 from azure.mgmt.devtestlabs.models import GalleryImageReference
@@ -15,11 +14,8 @@ import pysftp
 
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseBucket
-from cloudbridge.cloud.base.resources import BaseBucketContainer
 from cloudbridge.cloud.base.resources import BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
-from cloudbridge.cloud.base.resources import BaseFloatingIPContainer
-from cloudbridge.cloud.base.resources import BaseGatewayContainer
 from cloudbridge.cloud.base.resources import BaseInstance
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
@@ -33,10 +29,8 @@ from cloudbridge.cloud.base.resources import BaseSnapshot
 from cloudbridge.cloud.base.resources import BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewallRule
-from cloudbridge.cloud.base.resources import BaseVMFirewallRuleContainer
 from cloudbridge.cloud.base.resources import BaseVMType
 from cloudbridge.cloud.base.resources import BaseVolume
-from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.interfaces import InstanceState
 from cloudbridge.cloud.interfaces import VolumeState
 from cloudbridge.cloud.interfaces.resources import Instance
@@ -48,6 +42,10 @@ from cloudbridge.cloud.interfaces.resources import SubnetState
 from cloudbridge.cloud.interfaces.resources import TrafficDirection
 
 from . import helpers as azure_helpers
+from .subservices import AzureBucketObjectSubService
+from .subservices import AzureFloatingIPSubService
+from .subservices import AzureGatewaySubService
+from .subservices import AzureVMFirewallRuleSubService
 
 log = logging.getLogger(__name__)
 
@@ -57,7 +55,7 @@ class AzureVMFirewall(BaseVMFirewall):
         super(AzureVMFirewall, self).__init__(provider, vm_firewall)
         self._vm_firewall = vm_firewall
         self._vm_firewall.tags = self._vm_firewall.tags or {}
-        self._rule_container = AzureVMFirewallRuleContainer(provider, self)
+        self._rule_container = AzureVMFirewallRuleSubService(provider, self)
 
     @property
     def network_id(self):
@@ -123,70 +121,6 @@ class AzureVMFirewall(BaseVMFirewall):
         return js
 
 
-class AzureVMFirewallRuleContainer(BaseVMFirewallRuleContainer):
-
-    def __init__(self, provider, firewall):
-        super(AzureVMFirewallRuleContainer, self).__init__(provider, firewall)
-
-    def list(self, limit=None, marker=None):
-        # Filter out firewall rules with priority < 3500 because values
-        # between 3500 and 4096 are assumed to be owned by cloudbridge
-        # default rules.
-        # pylint:disable=protected-access
-        rules = [AzureVMFirewallRule(self.firewall, rule) for rule
-                 in self.firewall._vm_firewall.security_rules
-                 if rule.priority < 3500]
-        return ClientPagedResultList(self._provider, rules,
-                                     limit=limit, marker=marker)
-
-    def create(self, direction, protocol=None, from_port=None, to_port=None,
-               cidr=None, src_dest_fw=None):
-        if protocol and from_port and to_port:
-            return self._create_rule(direction, protocol, from_port,
-                                     to_port, cidr)
-        elif src_dest_fw:
-            result = None
-            fw = (self._provider.security.vm_firewalls.get(src_dest_fw)
-                  if isinstance(src_dest_fw, str) else src_dest_fw)
-            for rule in fw.rules:
-                result = self._create_rule(
-                    rule.direction, rule.protocol, rule.from_port,
-                    rule.to_port, rule.cidr)
-            return result
-        else:
-            return None
-
-    def _create_rule(self, direction, protocol, from_port, to_port, cidr):
-
-        # If cidr is None, default values is set as 0.0.0.0/0
-        if not cidr:
-            cidr = '0.0.0.0/0'
-
-        count = len(self.firewall._vm_firewall.security_rules) + 1
-        rule_name = "cb-rule-" + str(count)
-        priority = 1000 + count
-        destination_port_range = str(from_port) + "-" + str(to_port)
-        source_port_range = '*'
-        destination_address_prefix = "*"
-        access = "Allow"
-        direction = ("Inbound" if direction == TrafficDirection.INBOUND
-                     else "Outbound")
-        parameters = {"priority": priority,
-                      "protocol": protocol,
-                      "source_port_range": source_port_range,
-                      "source_address_prefix": cidr,
-                      "destination_port_range": destination_port_range,
-                      "destination_address_prefix": destination_address_prefix,
-                      "access": access,
-                      "direction": direction}
-        result = self._provider.azure_client. \
-            create_vm_firewall_rule(self.firewall.id,
-                                    rule_name, parameters)
-        # pylint:disable=protected-access
-        self.firewall._vm_firewall.security_rules.append(result)
-        return AzureVMFirewallRule(self.firewall, result)
-
-
 # Tuple for port range
 PortRange = collections.namedtuple('PortRange', ['from_port', 'to_port'])
 
@@ -342,7 +276,7 @@ class AzureBucket(BaseBucket):
     def __init__(self, provider, bucket):
         super(AzureBucket, self).__init__(provider)
         self._bucket = bucket
-        self._object_container = AzureBucketContainer(provider, self)
+        self._object_container = AzureBucketObjectSubService(provider, self)
 
     @property
     def id(self):
@@ -366,12 +300,6 @@ class AzureBucket(BaseBucket):
         return self._object_container
 
 
-class AzureBucketContainer(BaseBucketContainer):
-
-    def __init__(self, provider, bucket):
-        super(AzureBucketContainer, self).__init__(provider, bucket)
-
-
 class AzureVolume(BaseVolume):
     VOLUME_STATE_MAP = {
         'InProgress': VolumeState.CREATING,
@@ -785,28 +713,6 @@ class AzureMachineImage(BaseMachineImage):
                 self._state = "unknown"
 
 
-class AzureGatewayContainer(BaseGatewayContainer):
-    def __init__(self, provider, network):
-        super(AzureGatewayContainer, self).__init__(provider, network)
-        # Azure doesn't have a notion of a route table or an internet
-        # gateway as OS and AWS so create placeholder objects of the
-        # AzureInternetGateway here.
-        # http://bit.ly/2BqGdVh
-        # Singleton returned by the list method
-        self.gateway_singleton = AzureInternetGateway(self._provider, None,
-                                                      network)
-
-    def get_or_create_inet_gateway(self):
-        gateway = AzureInternetGateway(self._provider, None, self._network)
-        return gateway
-
-    def list(self, limit=None, marker=None):
-        return [self.gateway_singleton]
-
-    def delete(self, gateway):
-        pass
-
-
 class AzureNetwork(BaseNetwork):
     NETWORK_STATE_MAP = {
         'InProgress': NetworkState.PENDING,
@@ -819,7 +725,7 @@ class AzureNetwork(BaseNetwork):
         self._state = self._network.provisioning_state
         if not self._network.tags:
             self._network.tags = {}
-        self._gateway_service = AzureGatewayContainer(provider, self)
+        self._gateway_service = AzureGatewaySubService(provider, self)
 
     @property
     def id(self):
@@ -920,39 +826,6 @@ class AzureNetwork(BaseNetwork):
         return self._gateway_service
 
 
-class AzureFloatingIPContainer(BaseFloatingIPContainer):
-
-    def __init__(self, provider, gateway, network_id):
-        super(AzureFloatingIPContainer, self).__init__(provider, gateway)
-        self._network_id = network_id
-
-    def get(self, fip_id):
-        log.debug("Getting Azure Floating IP container with the id: %s",
-                  fip_id)
-        fip = [fip for fip in self if fip.id == fip_id]
-        return fip[0] if fip else None
-
-    def list(self, limit=None, marker=None):
-        floating_ips = [AzureFloatingIP(self._provider, floating_ip,
-                                        self._network_id)
-                        for floating_ip in self._provider.azure_client.
-                        list_floating_ips()]
-        return ClientPagedResultList(self._provider, floating_ips,
-                                     limit=limit, marker=marker)
-
-    def create(self):
-        public_ip_parameters = {
-            'location': self._provider.azure_client.region_name,
-            'public_ip_allocation_method': 'Static'
-        }
-
-        public_ip_name = 'cb-fip-' + uuid4().hex[:6]
-
-        floating_ip = self._provider.azure_client.\
-            create_floating_ip(public_ip_name, public_ip_parameters)
-        return AzureFloatingIP(self._provider, floating_ip, self._network_id)
-
-
 class AzureFloatingIP(BaseFloatingIP):
 
     def __init__(self, provider, floating_ip, network_id):
@@ -1651,7 +1524,7 @@ class AzureInternetGateway(BaseInternetGateway):
         self._network_id = gateway_net.id if isinstance(
             gateway_net, AzureNetwork) else gateway_net
         self._state = ''
-        self._fips_container = AzureFloatingIPContainer(
+        self._fips_container = AzureFloatingIPSubService(
             provider, self, self._network_id)
 
     @property

+ 1 - 1
cloudbridge/cloud/providers/azure/services.py

@@ -256,7 +256,7 @@ class AzureStorageService(BaseStorageService):
         return self._bucket_svc
 
     @property
-    def bucket_objects(self):
+    def _bucket_objects(self):
         return self._bucket_obj_svc
 
 

+ 141 - 0
cloudbridge/cloud/providers/azure/subservices.py

@@ -0,0 +1,141 @@
+import logging
+from uuid import uuid4
+
+from cloudbridge.cloud.base.resources import ClientPagedResultList
+from cloudbridge.cloud.base.subservices import BaseBucketObjectSubService
+from cloudbridge.cloud.base.subservices import \
+    BaseFloatingIPSubService
+from cloudbridge.cloud.base.subservices import BaseGatewaySubService
+from cloudbridge.cloud.base.subservices import BaseVMFirewallRuleSubService
+from cloudbridge.cloud.interfaces.resources import TrafficDirection
+
+from .resources import AzureFloatingIP
+from .resources import AzureInternetGateway
+from .resources import AzureVMFirewallRule
+
+log = logging.getLogger(__name__)
+
+
+class AzureBucketObjectSubService(BaseBucketObjectSubService):
+
+    def __init__(self, provider, bucket):
+        super(AzureBucketObjectSubService, self).__init__(provider, bucket)
+
+
+class AzureVMFirewallRuleSubService(BaseVMFirewallRuleSubService):
+
+    def __init__(self, provider, firewall):
+        super(AzureVMFirewallRuleSubService, self).__init__(provider, firewall)
+
+    def list(self, limit=None, marker=None):
+        # Filter out firewall rules with priority < 3500 because values
+        # between 3500 and 4096 are assumed to be owned by cloudbridge
+        # default rules.
+        # pylint:disable=protected-access
+        rules = [AzureVMFirewallRule(self.firewall, rule) for rule
+                 in self.firewall._vm_firewall.security_rules
+                 if rule.priority < 3500]
+        return ClientPagedResultList(self._provider, rules,
+                                     limit=limit, marker=marker)
+
+    def create(self, direction, protocol=None, from_port=None, to_port=None,
+               cidr=None, src_dest_fw=None):
+        if protocol and from_port and to_port:
+            return self._create_rule(direction, protocol, from_port,
+                                     to_port, cidr)
+        elif src_dest_fw:
+            result = None
+            fw = (self._provider.security.vm_firewalls.get(src_dest_fw)
+                  if isinstance(src_dest_fw, str) else src_dest_fw)
+            for rule in fw.rules:
+                result = self._create_rule(
+                    rule.direction, rule.protocol, rule.from_port,
+                    rule.to_port, rule.cidr)
+            return result
+        else:
+            return None
+
+    def _create_rule(self, direction, protocol, from_port, to_port, cidr):
+
+        # If cidr is None, default values is set as 0.0.0.0/0
+        if not cidr:
+            cidr = '0.0.0.0/0'
+
+        count = len(self.firewall._vm_firewall.security_rules) + 1
+        rule_name = "cb-rule-" + str(count)
+        priority = 1000 + count
+        destination_port_range = str(from_port) + "-" + str(to_port)
+        source_port_range = '*'
+        destination_address_prefix = "*"
+        access = "Allow"
+        direction = ("Inbound" if direction == TrafficDirection.INBOUND
+                     else "Outbound")
+        parameters = {"priority": priority,
+                      "protocol": protocol,
+                      "source_port_range": source_port_range,
+                      "source_address_prefix": cidr,
+                      "destination_port_range": destination_port_range,
+                      "destination_address_prefix": destination_address_prefix,
+                      "access": access,
+                      "direction": direction}
+        result = self._provider.azure_client. \
+            create_vm_firewall_rule(self.firewall.id,
+                                    rule_name, parameters)
+        # pylint:disable=protected-access
+        self.firewall._vm_firewall.security_rules.append(result)
+        return AzureVMFirewallRule(self.firewall, result)
+
+
+class AzureGatewaySubService(BaseGatewaySubService):
+    def __init__(self, provider, network):
+        super(AzureGatewaySubService, self).__init__(provider, network)
+        # Azure doesn't have a notion of a route table or an internet
+        # gateway as OS and AWS so create placeholder objects of the
+        # AzureInternetGateway here.
+        # http://bit.ly/2BqGdVh
+        # Singleton returned by the list method
+        self.gateway_singleton = AzureInternetGateway(self._provider, None,
+                                                      network)
+
+    def get_or_create_inet_gateway(self):
+        gateway = AzureInternetGateway(self._provider, None, self._network)
+        return gateway
+
+    def list(self, limit=None, marker=None):
+        return [self.gateway_singleton]
+
+    def delete(self, gateway):
+        pass
+
+
+class AzureFloatingIPSubService(BaseFloatingIPSubService):
+
+    def __init__(self, provider, gateway, network_id):
+        super(AzureFloatingIPSubService, self).__init__(provider, gateway)
+        self._network_id = network_id
+
+    def get(self, fip_id):
+        log.debug("Getting Azure Floating IP container with the id: %s",
+                  fip_id)
+        fip = [fip for fip in self if fip.id == fip_id]
+        return fip[0] if fip else None
+
+    def list(self, limit=None, marker=None):
+        floating_ips = [AzureFloatingIP(self._provider, floating_ip,
+                                        self._network_id)
+                        for floating_ip in self._provider.azure_client.
+                        list_floating_ips()]
+        return ClientPagedResultList(self._provider, floating_ips,
+                                     limit=limit, marker=marker)
+
+    def create(self):
+        public_ip_parameters = {
+            'location': self._provider.azure_client.region_name,
+            'public_ip_allocation_method': 'Static'
+        }
+
+        public_ip_name = 'cb-fip-' + uuid4().hex[:6]
+
+        floating_ip = self._provider.azure_client.\
+            create_floating_ip(public_ip_name, public_ip_parameters)
+        return AzureFloatingIP(self._provider, floating_ip, self._network_id)

+ 13 - 147
cloudbridge/cloud/providers/gce/resources.py

@@ -18,11 +18,8 @@ import googleapiclient
 import cloudbridge as cb
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseBucket
-from cloudbridge.cloud.base.resources import BaseBucketContainer
 from cloudbridge.cloud.base.resources import BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
-from cloudbridge.cloud.base.resources import BaseFloatingIPContainer
-from cloudbridge.cloud.base.resources import BaseGatewayContainer
 from cloudbridge.cloud.base.resources import BaseInstance
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
@@ -36,11 +33,8 @@ from cloudbridge.cloud.base.resources import BaseSnapshot
 from cloudbridge.cloud.base.resources import BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewallRule
-from cloudbridge.cloud.base.resources import BaseVMFirewallRuleContainer
 from cloudbridge.cloud.base.resources import BaseVMType
 from cloudbridge.cloud.base.resources import BaseVolume
-from cloudbridge.cloud.base.resources import ClientPagedResultList
-from cloudbridge.cloud.base.resources import ServerPagedResultList
 from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
@@ -50,9 +44,15 @@ from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import SubnetState
 from cloudbridge.cloud.interfaces.resources import TrafficDirection
 from cloudbridge.cloud.interfaces.resources import VolumeState
-from cloudbridge.cloud.providers.gce import helpers
+
+from . import helpers
+from .subservices import GCEFloatingIPSubService
+from .subservices import GCEGatewaySubService
+from .subservices import GCEVMFirewallRuleSubService
+from .subservices import GCSBucketObjectSubService
 
 # Older versions of Python do not have a built-in set data-structure.
+
 try:
     set
 except NameError:
@@ -454,7 +454,7 @@ class GCEVMFirewall(BaseVMFirewall):
                              .get_or_create_default())
         else:
             self._network = network
-        self._rule_container = GCEVMFirewallRuleContainer(self)
+        self._rule_container = GCEVMFirewallRuleSubService(self)
 
     @property
     def id(self):
@@ -537,67 +537,6 @@ class GCEVMFirewall(BaseVMFirewall):
         return self._delegate
 
 
-class GCEVMFirewallRuleContainer(BaseVMFirewallRuleContainer):
-
-    def __init__(self, firewall):
-        super(GCEVMFirewallRuleContainer, self).__init__(
-                firewall.delegate.provider, firewall)
-        self._dummy_rule = None
-
-    def list(self, limit=None, marker=None):
-        rules = []
-        for firewall in self.firewall.delegate.iter_firewalls(
-                self.firewall.name, self.firewall.network.name):
-            rule = GCEVMFirewallRule(self.firewall, firewall['id'])
-            if rule.is_dummy_rule():
-                self._dummy_rule = rule
-            else:
-                rules.append(rule)
-        return ClientPagedResultList(self._provider, rules,
-                                     limit=limit, marker=marker)
-
-    @property
-    def dummy_rule(self):
-        if not self._dummy_rule:
-            self.list()
-        return self._dummy_rule
-
-    @staticmethod
-    def to_port_range(from_port, to_port):
-        if from_port is not None and to_port is not None:
-            return '%d-%d' % (from_port, to_port)
-        elif from_port is not None:
-            return from_port
-        else:
-            return to_port
-
-    def create_with_priority(self, direction, protocol, priority,
-                             from_port=None, to_port=None, cidr=None,
-                             src_dest_fw=None):
-        port = GCEVMFirewallRuleContainer.to_port_range(from_port, to_port)
-        src_dest_tag = None
-        src_dest_fw_id = None
-        if src_dest_fw:
-            src_dest_tag = src_dest_fw.name
-            src_dest_fw_id = src_dest_fw.id
-        if not self.firewall.delegate.add_firewall(
-                self.firewall.name, direction, protocol, priority, port, cidr,
-                src_dest_tag, self.firewall.description,
-                self.firewall.network.name):
-            return None
-        rules = self.find(direction=direction, protocol=protocol,
-                          from_port=from_port, to_port=to_port, cidr=cidr,
-                          src_dest_fw_id=src_dest_fw_id)
-        if len(rules) < 1:
-            return None
-        return rules[0]
-
-    def create(self, direction, protocol, from_port=None, to_port=None,
-               cidr=None, src_dest_fw=None):
-        return self.create_with_priority(direction, protocol, 1000, from_port,
-                                         to_port, cidr, src_dest_fw)
-
-
 class GCEVMFirewallRule(BaseVMFirewallRule):
 
     def __init__(self, parent_fw, rule):
@@ -1323,7 +1262,7 @@ class GCENetwork(BaseNetwork):
     def __init__(self, provider, network):
         super(GCENetwork, self).__init__(provider)
         self._network = network
-        self._gateway_container = GCEGatewayContainer(provider, self)
+        self._gateway_container = GCEGatewaySubService(provider, self)
 
     @property
     def resource_url(self):
@@ -1394,49 +1333,6 @@ class GCENetwork(BaseNetwork):
         return self._gateway_container
 
 
-class GCEFloatingIPContainer(BaseFloatingIPContainer):
-
-    def __init__(self, provider, gateway):
-        super(GCEFloatingIPContainer, self).__init__(provider, gateway)
-
-    def get(self, floating_ip_id):
-        fip = self._provider.get_resource('addresses', floating_ip_id)
-        return (GCEFloatingIP(self._provider, self.gateway, fip)
-                if fip else None)
-
-    def list(self, limit=None, marker=None):
-        max_result = limit if limit is not None and limit < 500 else 500
-        response = (self._provider
-                        .gce_compute
-                        .addresses()
-                        .list(project=self._provider.project_name,
-                              region=self._provider.region_name,
-                              maxResults=max_result,
-                              pageToken=marker)
-                        .execute())
-        ips = [GCEFloatingIP(self._provider, self.gateway, ip)
-               for ip in response.get('items', [])]
-        if len(ips) > max_result:
-            cb.log.warning('Expected at most %d results; got %d',
-                           max_result, len(ips))
-        return ServerPagedResultList('nextPageToken' in response,
-                                     response.get('nextPageToken'),
-                                     False, data=ips)
-
-    def create(self):
-        region_name = self._provider.region_name
-        ip_name = 'ip-{0}'.format(uuid.uuid4())
-        response = (self._provider
-                    .gce_compute
-                    .addresses()
-                    .insert(project=self._provider.project_name,
-                            region=region_name,
-                            body={'name': ip_name})
-                    .execute())
-        self._provider.wait_for_operation(response, region=region_name)
-        return self.get(ip_name)
-
-
 class GCEFloatingIP(BaseFloatingIP):
     _DEAD_INSTANCE = 'dead instance'
 
@@ -1611,36 +1507,12 @@ class GCERouter(BaseRouter):
         pass
 
 
-class GCEGatewayContainer(BaseGatewayContainer):
-    _DEFAULT_GATEWAY_NAME = 'default-internet-gateway'
-    _GATEWAY_URL_PREFIX = 'global/gateways/'
-
-    def __init__(self, provider, network):
-        super(GCEGatewayContainer, self).__init__(provider, network)
-        self._default_internet_gateway = GCEInternetGateway(
-            provider,
-            {'id': (GCEGatewayContainer._GATEWAY_URL_PREFIX +
-                    GCEGatewayContainer._DEFAULT_GATEWAY_NAME),
-             'name': GCEGatewayContainer._DEFAULT_GATEWAY_NAME})
-
-    def get_or_create_inet_gateway(self, name=None):
-        return self._default_internet_gateway
-
-    def delete(self, gateway):
-        pass
-
-    def list(self, limit=None, marker=None):
-        return ClientPagedResultList(self._provider,
-                                     [self._default_internet_gateway],
-                                     limit=limit, marker=marker)
-
-
 class GCEInternetGateway(BaseInternetGateway):
 
     def __init__(self, provider, gateway):
         super(GCEInternetGateway, self).__init__(provider)
         self._gateway = gateway
-        self._fip_container = GCEFloatingIPContainer(provider, self)
+        self._fip_container = GCEFloatingIPSubService(provider, self)
 
     @property
     def id(self):
@@ -2088,7 +1960,7 @@ class GCSObject(BaseBucketObject):
         media_body = googleapiclient.http.MediaIoBaseUpload(
                 io.BytesIO(data), mimetype='plain/text')
         response = (self._provider
-                        .storage.bucket_objects
+                        .storage._bucket_objects
                         ._create_object_with_media_body(self._bucket,
                                                         self.name,
                                                         media_body))
@@ -2103,7 +1975,7 @@ class GCSObject(BaseBucketObject):
             media_body = googleapiclient.http.MediaIoBaseUpload(
                     f, 'application/octet-stream')
             response = (self._provider
-                        .storage.bucket_objects
+                        .storage._bucket_objects
                         ._create_object_with_media_body(self._bucket,
                                                         self.name,
                                                         media_body))
@@ -2139,18 +2011,12 @@ class GCSObject(BaseBucketObject):
         self._obj = self.bucket.objects.get(self.id)._obj
 
 
-class GCSBucketContainer(BaseBucketContainer):
-
-    def __init__(self, provider, bucket):
-        super(GCSBucketContainer, self).__init__(provider, bucket)
-
-
 class GCSBucket(BaseBucket):
 
     def __init__(self, provider, bucket):
         super(GCSBucket, self).__init__(provider)
         self._bucket = bucket
-        self._object_container = GCSBucketContainer(provider, self)
+        self._object_container = GCSBucketObjectSubService(provider, self)
 
     @property
     def id(self):

+ 1 - 1
cloudbridge/cloud/providers/gce/services.py

@@ -1042,7 +1042,7 @@ class GCPStorageService(BaseStorageService):
         return self._bucket_svc
 
     @property
-    def bucket_objects(self):
+    def _bucket_objects(self):
         return self._bucket_obj_svc
 
 

+ 150 - 0
cloudbridge/cloud/providers/gce/subservices.py

@@ -0,0 +1,150 @@
+import logging
+import uuid
+
+from cloudbridge.cloud.base.resources import ClientPagedResultList
+from cloudbridge.cloud.base.resources import ServerPagedResultList
+from cloudbridge.cloud.base.subservices import BaseBucketObjectSubService
+from cloudbridge.cloud.base.subservices import BaseFloatingIPSubService
+from cloudbridge.cloud.base.subservices import \
+    BaseGatewaySubService
+from cloudbridge.cloud.base.subservices import BaseVMFirewallRuleSubService
+
+from .resources import GCEFloatingIP
+from .resources import GCEInternetGateway
+from .resources import GCEVMFirewallRule
+
+log = logging.getLogger(__name__)
+
+
+class GCSBucketObjectSubService(BaseBucketObjectSubService):
+
+    def __init__(self, provider, bucket):
+        super(GCSBucketObjectSubService, self).__init__(provider, bucket)
+
+
+class GCEVMFirewallRuleSubService(BaseVMFirewallRuleSubService):
+
+    def __init__(self, firewall):
+        super(GCEVMFirewallRuleSubService, self).__init__(
+                firewall.delegate.provider, firewall)
+        self._dummy_rule = None
+
+    def list(self, limit=None, marker=None):
+        rules = []
+        for firewall in self.firewall.delegate.iter_firewalls(
+                self.firewall.name, self.firewall.network.name):
+            rule = GCEVMFirewallRule(self.firewall, firewall['id'])
+            if rule.is_dummy_rule():
+                self._dummy_rule = rule
+            else:
+                rules.append(rule)
+        return ClientPagedResultList(self._provider, rules,
+                                     limit=limit, marker=marker)
+
+    @property
+    def dummy_rule(self):
+        if not self._dummy_rule:
+            self.list()
+        return self._dummy_rule
+
+    @staticmethod
+    def to_port_range(from_port, to_port):
+        if from_port is not None and to_port is not None:
+            return '%d-%d' % (from_port, to_port)
+        elif from_port is not None:
+            return from_port
+        else:
+            return to_port
+
+    def create_with_priority(self, direction, protocol, priority,
+                             from_port=None, to_port=None, cidr=None,
+                             src_dest_fw=None):
+        port = GCEVMFirewallRuleSubService.to_port_range(from_port, to_port)
+        src_dest_tag = None
+        src_dest_fw_id = None
+        if src_dest_fw:
+            src_dest_tag = src_dest_fw.name
+            src_dest_fw_id = src_dest_fw.id
+        if not self.firewall.delegate.add_firewall(
+                self.firewall.name, direction, protocol, priority, port, cidr,
+                src_dest_tag, self.firewall.description,
+                self.firewall.network.name):
+            return None
+        rules = self.find(direction=direction, protocol=protocol,
+                          from_port=from_port, to_port=to_port, cidr=cidr,
+                          src_dest_fw_id=src_dest_fw_id)
+        if len(rules) < 1:
+            return None
+        return rules[0]
+
+    def create(self, direction, protocol, from_port=None, to_port=None,
+               cidr=None, src_dest_fw=None):
+        return self.create_with_priority(direction, protocol, 1000, from_port,
+                                         to_port, cidr, src_dest_fw)
+
+
+class GCEFloatingIPSubService(BaseFloatingIPSubService):
+
+    def __init__(self, provider, gateway):
+        super(GCEFloatingIPSubService, self).__init__(provider, gateway)
+
+    def get(self, floating_ip_id):
+        fip = self._provider.get_resource('addresses', floating_ip_id)
+        return (GCEFloatingIP(self._provider, self.gateway, fip)
+                if fip else None)
+
+    def list(self, limit=None, marker=None):
+        max_result = limit if limit is not None and limit < 500 else 500
+        response = (self._provider
+                        .gce_compute
+                        .addresses()
+                        .list(project=self._provider.project_name,
+                              region=self._provider.region_name,
+                              maxResults=max_result,
+                              pageToken=marker)
+                        .execute())
+        ips = [GCEFloatingIP(self._provider, self.gateway, ip)
+               for ip in response.get('items', [])]
+        if len(ips) > max_result:
+            log.warning('Expected at most %d results; got %d',
+                        max_result, len(ips))
+        return ServerPagedResultList('nextPageToken' in response,
+                                     response.get('nextPageToken'),
+                                     False, data=ips)
+
+    def create(self):
+        region_name = self._provider.region_name
+        ip_name = 'ip-{0}'.format(uuid.uuid4())
+        response = (self._provider
+                    .gce_compute
+                    .addresses()
+                    .insert(project=self._provider.project_name,
+                            region=region_name,
+                            body={'name': ip_name})
+                    .execute())
+        self._provider.wait_for_operation(response, region=region_name)
+        return self.get(ip_name)
+
+
+class GCEGatewaySubService(BaseGatewaySubService):
+    _DEFAULT_GATEWAY_NAME = 'default-internet-gateway'
+    _GATEWAY_URL_PREFIX = 'global/gateways/'
+
+    def __init__(self, provider, network):
+        super(GCEGatewaySubService, self).__init__(provider, network)
+        self._default_internet_gateway = GCEInternetGateway(
+            provider,
+            {'id': (GCEGatewaySubService._GATEWAY_URL_PREFIX +
+                    GCEGatewaySubService._DEFAULT_GATEWAY_NAME),
+             'name': GCEGatewaySubService._DEFAULT_GATEWAY_NAME})
+
+    def get_or_create_inet_gateway(self, name=None):
+        return self._default_internet_gateway
+
+    def delete(self, gateway):
+        pass
+
+    def list(self, limit=None, marker=None):
+        return ClientPagedResultList(self._provider,
+                                     [self._default_internet_gateway],
+                                     limit=limit, marker=marker)

+ 11 - 141
cloudbridge/cloud/providers/openstack/resources.py

@@ -5,6 +5,7 @@ import inspect
 import ipaddress
 import logging
 import os
+
 try:
     from urllib.parse import urlparse
     from urllib.parse import urljoin
@@ -16,23 +17,15 @@ from keystoneclient.v3.regions import Region
 
 import novaclient.exceptions as novaex
 
-from openstack.exceptions import HttpException
-from openstack.exceptions import NotFoundException
-from openstack.exceptions import ResourceNotFound
-
 import swiftclient
 from swiftclient.service import SwiftService
 from swiftclient.service import SwiftUploadObject
 from swiftclient.utils import generate_temp_url
 
-import cloudbridge.cloud.base.helpers as cb_helpers
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseBucket
-from cloudbridge.cloud.base.resources import BaseBucketContainer
 from cloudbridge.cloud.base.resources import BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
-from cloudbridge.cloud.base.resources import BaseFloatingIPContainer
-from cloudbridge.cloud.base.resources import BaseGatewayContainer
 from cloudbridge.cloud.base.resources import BaseInstance
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
@@ -45,11 +38,8 @@ from cloudbridge.cloud.base.resources import BaseSnapshot
 from cloudbridge.cloud.base.resources import BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewallRule
-from cloudbridge.cloud.base.resources import BaseVMFirewallRuleContainer
 from cloudbridge.cloud.base.resources import BaseVMType
 from cloudbridge.cloud.base.resources import BaseVolume
-from cloudbridge.cloud.base.resources import ClientPagedResultList
-from cloudbridge.cloud.interfaces.exceptions import InvalidValueException
 from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
@@ -60,6 +50,11 @@ from cloudbridge.cloud.interfaces.resources import SubnetState
 from cloudbridge.cloud.interfaces.resources import TrafficDirection
 from cloudbridge.cloud.interfaces.resources import VolumeState
 
+from .subservices import OpenStackBucketObjectSubService
+from .subservices import OpenStackFloatingIPSubService
+from .subservices import OpenStackGatewaySubService
+from .subservices import OpenStackVMFirewallRuleSubService
+
 ONE_GIG = 1048576000  # in bytes
 FIVE_GIG = ONE_GIG * 5  # in bytes
 
@@ -767,50 +762,6 @@ class OpenStackSnapshot(BaseSnapshot):
         return cb_vol
 
 
-class OpenStackGatewayContainer(BaseGatewayContainer):
-    """For OpenStack, an internet gateway is a just an 'external' network."""
-
-    def __init__(self, provider, network):
-        super(OpenStackGatewayContainer, self).__init__(provider, network)
-
-    def _check_fip_connectivity(self, external_net):
-        # Due to current limitations in OpenStack:
-        # https://bugs.launchpad.net/neutron/+bug/1743480, it's not
-        # possible to differentiate between floating ip networks and provider
-        # external networks. Therefore, we systematically step through
-        # all available networks and perform an assignment test to infer valid
-        # floating ip nets.
-        dummy_router = self._provider.networking.routers.create(
-            label='cb-conn-test-router', network=self._network)
-        with cb_helpers.cleanup_action(lambda: dummy_router.delete()):
-            try:
-                dummy_router.attach_gateway(external_net)
-                return True
-            except Exception:
-                return False
-
-    def get_or_create_inet_gateway(self):
-        """For OS, inet gtw is any net that has `external` property set."""
-        external_nets = (n for n in self._provider.networking.networks
-                         if n.external)
-        for net in external_nets:
-            if self._check_fip_connectivity(net):
-                return OpenStackInternetGateway(self._provider, net)
-        return None
-
-    def delete(self, gateway):
-        log.debug("Deleting OpenStack Gateway: %s", gateway)
-        gateway.delete()
-
-    def list(self, limit=None, marker=None):
-        log.debug("OpenStack listing of all current internet gateways")
-        igl = [OpenStackInternetGateway(self._provider, n)
-               for n in self._provider.networking.networks
-               if n.external and self._check_fip_connectivity(n)]
-        return ClientPagedResultList(self._provider, igl, limit=limit,
-                                     marker=marker)
-
-
 class OpenStackNetwork(BaseNetwork):
 
     # Ref: https://github.com/openstack/neutron/blob/master/neutron/plugins/
@@ -829,7 +780,7 @@ class OpenStackNetwork(BaseNetwork):
     def __init__(self, provider, network):
         super(OpenStackNetwork, self).__init__(provider)
         self._network = network
-        self._gateway_service = OpenStackGatewayContainer(provider, self)
+        self._gateway_service = OpenStackGatewaySubService(provider, self)
 
     @property
     def id(self):
@@ -952,33 +903,6 @@ class OpenStackSubnet(BaseSubnet):
             self._state = SubnetState.UNKNOWN
 
 
-class OpenStackFloatingIPContainer(BaseFloatingIPContainer):
-
-    def __init__(self, provider, gateway):
-        super(OpenStackFloatingIPContainer, self).__init__(provider, gateway)
-
-    def get(self, fip_id):
-        try:
-            return OpenStackFloatingIP(
-                self._provider, self._provider.os_conn.network.get_ip(fip_id))
-        except (ResourceNotFound, NotFoundException):
-            log.debug("Floating IP %s not found.", fip_id)
-            return None
-
-    def list(self, limit=None, marker=None):
-        fips = [OpenStackFloatingIP(self._provider, fip)
-                for fip in self._provider.os_conn.network.ips(
-                    floating_network_id=self.gateway.id
-                )]
-        return ClientPagedResultList(self._provider, fips,
-                                     limit=limit, marker=marker)
-
-    def create(self):
-        return OpenStackFloatingIP(
-            self._provider, self._provider.os_conn.network.create_ip(
-                floating_network_id=self.gateway.id))
-
-
 class OpenStackFloatingIP(BaseFloatingIP):
 
     def __init__(self, provider, floating_ip):
@@ -1109,7 +1033,7 @@ class OpenStackInternetGateway(BaseInternetGateway):
             # pylint:disable=protected-access
             gateway_net = gateway_net._network
         self._gateway_net = gateway_net
-        self._fips_container = OpenStackFloatingIPContainer(provider, self)
+        self._fips_container = OpenStackFloatingIPSubService(provider, self)
 
     @property
     def id(self):
@@ -1158,7 +1082,7 @@ class OpenStackVMFirewall(BaseVMFirewall):
 
     def __init__(self, provider, vm_firewall):
         super(OpenStackVMFirewall, self).__init__(provider, vm_firewall)
-        self._rule_svc = OpenStackVMFirewallRuleContainer(provider, self)
+        self._rule_svc = OpenStackVMFirewallRuleSubService(provider, self)
 
     @property
     def network_id(self):
@@ -1231,55 +1155,6 @@ class OpenStackVMFirewall(BaseVMFirewall):
         return js
 
 
-class OpenStackVMFirewallRuleContainer(BaseVMFirewallRuleContainer):
-
-    def __init__(self, provider, firewall):
-        super(OpenStackVMFirewallRuleContainer, self).__init__(
-            provider, firewall)
-
-    def list(self, limit=None, marker=None):
-        # pylint:disable=protected-access
-        rules = [OpenStackVMFirewallRule(self.firewall, r)
-                 for r in self.firewall._vm_firewall.security_group_rules]
-        return ClientPagedResultList(self._provider, rules,
-                                     limit=limit, marker=marker)
-
-    def create(self,  direction, protocol=None, from_port=None,
-               to_port=None, cidr=None, src_dest_fw=None):
-        src_dest_fw_id = (src_dest_fw.id if isinstance(src_dest_fw,
-                                                       OpenStackVMFirewall)
-                          else src_dest_fw)
-
-        try:
-            if direction == TrafficDirection.INBOUND:
-                os_direction = 'ingress'
-            elif direction == TrafficDirection.OUTBOUND:
-                os_direction = 'egress'
-            else:
-                raise InvalidValueException("direction", direction)
-            # pylint:disable=protected-access
-            rule = self._provider.os_conn.network.create_security_group_rule(
-                security_group_id=self.firewall.id,
-                direction=os_direction,
-                port_range_max=to_port,
-                port_range_min=from_port,
-                protocol=protocol,
-                remote_ip_prefix=cidr,
-                remote_group_id=src_dest_fw_id)
-            self.firewall.refresh()
-            return OpenStackVMFirewallRule(self.firewall, rule.to_dict())
-        except HttpException as e:
-            self.firewall.refresh()
-            # 409=Conflict, raised for duplicate rule
-            if e.status_code == 409:
-                existing = self.find(direction=direction, protocol=protocol,
-                                     from_port=from_port, to_port=to_port,
-                                     cidr=cidr, src_dest_fw_id=src_dest_fw_id)
-                return existing[0]
-            else:
-                raise e
-
-
 class OpenStackVMFirewallRule(BaseVMFirewallRule):
 
     def __init__(self, parent_fw, rule):
@@ -1453,7 +1328,8 @@ class OpenStackBucket(BaseBucket):
     def __init__(self, provider, bucket):
         super(OpenStackBucket, self).__init__(provider)
         self._bucket = bucket
-        self._object_container = OpenStackBucketContainer(provider, self)
+        self._object_container = OpenStackBucketObjectSubService(provider,
+                                                                 self)
 
     @property
     def id(self):
@@ -1466,9 +1342,3 @@ class OpenStackBucket(BaseBucket):
     @property
     def objects(self):
         return self._object_container
-
-
-class OpenStackBucketContainer(BaseBucketContainer):
-
-    def __init__(self, provider, bucket):
-        super(OpenStackBucketContainer, self).__init__(provider, bucket)

+ 1 - 1
cloudbridge/cloud/providers/openstack/services.py

@@ -290,7 +290,7 @@ class OpenStackStorageService(BaseStorageService):
         return self._bucket_svc
 
     @property
-    def bucket_objects(self):
+    def _bucket_objects(self):
         return self._bucket_obj_svc
 
 

+ 148 - 0
cloudbridge/cloud/providers/openstack/subservices.py

@@ -0,0 +1,148 @@
+import logging
+
+from openstack.exceptions import HttpException
+from openstack.exceptions import NotFoundException
+from openstack.exceptions import ResourceNotFound
+
+from cloudbridge.cloud.base import helpers as cb_helpers
+from cloudbridge.cloud.base.resources import ClientPagedResultList
+from cloudbridge.cloud.base.subservices import BaseBucketObjectSubService
+from cloudbridge.cloud.base.subservices import BaseFloatingIPSubService
+from cloudbridge.cloud.base.subservices import BaseGatewaySubService
+from cloudbridge.cloud.base.subservices import \
+    BaseVMFirewallRuleSubService
+from cloudbridge.cloud.interfaces.exceptions import InvalidValueException
+from cloudbridge.cloud.interfaces.resources import TrafficDirection
+
+from .resources import OpenStackFloatingIP
+from .resources import OpenStackInternetGateway
+from .resources import OpenStackVMFirewall
+from .resources import OpenStackVMFirewallRule
+
+log = logging.getLogger(__name__)
+
+
+class OpenStackBucketObjectSubService(BaseBucketObjectSubService):
+
+    def __init__(self, provider, bucket):
+        super(OpenStackBucketObjectSubService, self).__init__(provider, bucket)
+
+
+class OpenStackGatewaySubService(BaseGatewaySubService):
+    """For OpenStack, an internet gateway is a just an 'external' network."""
+
+    def __init__(self, provider, network):
+        super(OpenStackGatewaySubService, self).__init__(provider, network)
+
+    def _check_fip_connectivity(self, external_net):
+        # Due to current limitations in OpenStack:
+        # https://bugs.launchpad.net/neutron/+bug/1743480, it's not
+        # possible to differentiate between floating ip networks and provider
+        # external networks. Therefore, we systematically step through
+        # all available networks and perform an assignment test to infer valid
+        # floating ip nets.
+        dummy_router = self._provider.networking.routers.create(
+            label='cb-conn-test-router', network=self._network)
+        with cb_helpers.cleanup_action(lambda: dummy_router.delete()):
+            try:
+                dummy_router.attach_gateway(external_net)
+                return True
+            except Exception:
+                return False
+
+    def get_or_create_inet_gateway(self):
+        """For OS, inet gtw is any net that has `external` property set."""
+        external_nets = (n for n in self._provider.networking.networks
+                         if n.external)
+        for net in external_nets:
+            if self._check_fip_connectivity(net):
+                return OpenStackInternetGateway(self._provider, net)
+        return None
+
+    def delete(self, gateway):
+        log.debug("Deleting OpenStack Gateway: %s", gateway)
+        gateway.delete()
+
+    def list(self, limit=None, marker=None):
+        log.debug("OpenStack listing of all current internet gateways")
+        igl = [OpenStackInternetGateway(self._provider, n)
+               for n in self._provider.networking.networks
+               if n.external and self._check_fip_connectivity(n)]
+        return ClientPagedResultList(self._provider, igl, limit=limit,
+                                     marker=marker)
+
+
+class OpenStackFloatingIPSubService(BaseFloatingIPSubService):
+
+    def __init__(self, provider, gateway):
+        super(OpenStackFloatingIPSubService, self).__init__(provider, gateway)
+
+    def get(self, fip_id):
+        try:
+            return OpenStackFloatingIP(
+                self._provider, self._provider.os_conn.network.get_ip(fip_id))
+        except (ResourceNotFound, NotFoundException):
+            log.debug("Floating IP %s not found.", fip_id)
+            return None
+
+    def list(self, limit=None, marker=None):
+        fips = [OpenStackFloatingIP(self._provider, fip)
+                for fip in self._provider.os_conn.network.ips(
+                    floating_network_id=self.gateway.id
+                )]
+        return ClientPagedResultList(self._provider, fips,
+                                     limit=limit, marker=marker)
+
+    def create(self):
+        return OpenStackFloatingIP(
+            self._provider, self._provider.os_conn.network.create_ip(
+                floating_network_id=self.gateway.id))
+
+
+class OpenStackVMFirewallRuleSubService(BaseVMFirewallRuleSubService):
+
+    def __init__(self, provider, firewall):
+        super(OpenStackVMFirewallRuleSubService, self).__init__(
+            provider, firewall)
+
+    def list(self, limit=None, marker=None):
+        # pylint:disable=protected-access
+        rules = [OpenStackVMFirewallRule(self.firewall, r)
+                 for r in self.firewall._vm_firewall.security_group_rules]
+        return ClientPagedResultList(self._provider, rules,
+                                     limit=limit, marker=marker)
+
+    def create(self,  direction, protocol=None, from_port=None,
+               to_port=None, cidr=None, src_dest_fw=None):
+        src_dest_fw_id = (src_dest_fw.id if isinstance(src_dest_fw,
+                                                       OpenStackVMFirewall)
+                          else src_dest_fw)
+
+        try:
+            if direction == TrafficDirection.INBOUND:
+                os_direction = 'ingress'
+            elif direction == TrafficDirection.OUTBOUND:
+                os_direction = 'egress'
+            else:
+                raise InvalidValueException("direction", direction)
+            # pylint:disable=protected-access
+            rule = self._provider.os_conn.network.create_security_group_rule(
+                security_group_id=self.firewall.id,
+                direction=os_direction,
+                port_range_max=to_port,
+                port_range_min=from_port,
+                protocol=protocol,
+                remote_ip_prefix=cidr,
+                remote_group_id=src_dest_fw_id)
+            self.firewall.refresh()
+            return OpenStackVMFirewallRule(self.firewall, rule.to_dict())
+        except HttpException as e:
+            self.firewall.refresh()
+            # 409=Conflict, raised for duplicate rule
+            if e.status_code == 409:
+                existing = self.find(direction=direction, protocol=protocol,
+                                     from_port=from_port, to_port=to_port,
+                                     cidr=cidr, src_dest_fw_id=src_dest_fw_id)
+                return existing[0]
+            else:
+                raise e

+ 2 - 2
docs/api_docs/cloud/services.rst

@@ -55,7 +55,7 @@ SubnetService
 
 FloatingIPService
 -----------------
-.. autoclass:: cloudbridge.cloud.interfaces.resources.FloatingIPContainer
+.. autoclass:: cloudbridge.cloud.interfaces.resources.FloatingIPSubService
     :members:
 
 RouterService
@@ -65,7 +65,7 @@ RouterService
 
 GatewayService
 -----------------
-.. autoclass:: cloudbridge.cloud.interfaces.resources.GatewayContainer
+.. autoclass:: cloudbridge.cloud.interfaces.resources.GatewaySubService
     :members:
 
 BucketService

+ 1 - 1
docs/topics/object_storage.rst

@@ -66,5 +66,5 @@ Once a provider is obtained, you can access the container as usual:
 .. code-block:: python
 
     bucket = provider.storage.buckets.get(container)
-    obj = bucket.create_object('my_object.txt')
+    obj = bucket.objects.create('my_object.txt')
     obj.upload_from_file(source)

+ 2 - 1
setup.py

@@ -6,7 +6,8 @@ import ast
 import os
 import re
 
-from setuptools import find_packages, setup
+from setuptools import find_packages
+from setuptools import setup
 
 # Cannot use "from cloudbridge import get_version" because that would try to
 # import the six package which may not be installed yet.

+ 5 - 5
test/test_object_store_service.py

@@ -21,7 +21,7 @@ class CloudObjectStoreServiceTestCase(ProviderTestBase):
 
     _multiprocess_can_split_ = True
 
-    @helpers.skipIfNoService(['storage.bucket_objects', 'storage.buckets'])
+    @helpers.skipIfNoService(['storage._bucket_objects', 'storage.buckets'])
     def test_storage_services_event_pattern(self):
         self.assertEqual(
             self.provider.storage.buckets._service_event_pattern,
@@ -32,12 +32,12 @@ class CloudObjectStoreServiceTestCase(ProviderTestBase):
                                      self.provider.storage.buckets.
                                      _service_event_pattern))
         self.assertEqual(
-            self.provider.storage.bucket_objects._service_event_pattern,
-            "provider.storage.bucket_objects",
+            self.provider.storage._bucket_objects._service_event_pattern,
+            "provider.storage._bucket_objects",
             "Event pattern for {} service should be '{}', "
             "but found '{}'.".format("bucket_objects",
-                                     "provider.storage.bucket_objects",
-                                     self.provider.storage.bucket_objects.
+                                     "provider.storage._bucket_objects",
+                                     self.provider.storage._bucket_objects.
                                      _service_event_pattern))
 
     @helpers.skipIfNoService(['storage.buckets'])