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

Added subservices files, moved and renamed subservices, fixed imports

almahmoud 7 лет назад
Родитель
Сommit
62b279f9ca

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

@@ -12,21 +12,17 @@ import uuid
 
 
 import six
 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 InvalidLabelException
 from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
 from cloudbridge.cloud.interfaces.exceptions import InvalidNameException
 from cloudbridge.cloud.interfaces.exceptions import WaitStateException
 from cloudbridge.cloud.interfaces.exceptions import WaitStateException
 from cloudbridge.cloud.interfaces.resources import AttachmentInfo
 from cloudbridge.cloud.interfaces.resources import AttachmentInfo
 from cloudbridge.cloud.interfaces.resources import Bucket
 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 BucketObject
 from cloudbridge.cloud.interfaces.resources import CloudResource
 from cloudbridge.cloud.interfaces.resources import CloudResource
 from cloudbridge.cloud.interfaces.resources import FloatingIP
 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 FloatingIpState
-from cloudbridge.cloud.interfaces.resources import GatewayContainer
 from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import GatewayState
 from cloudbridge.cloud.interfaces.resources import Instance
 from cloudbridge.cloud.interfaces.resources import Instance
 from cloudbridge.cloud.interfaces.resources import InstanceState
 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 SubnetState
 from cloudbridge.cloud.interfaces.resources import VMFirewall
 from cloudbridge.cloud.interfaces.resources import VMFirewall
 from cloudbridge.cloud.interfaces.resources import VMFirewallRule
 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 VMType
 from cloudbridge.cloud.interfaces.resources import Volume
 from cloudbridge.cloud.interfaces.resources import Volume
 from cloudbridge.cloud.interfaces.resources import VolumeState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 
 
+from . import helpers as cb_helpers
+
 log = logging.getLogger(__name__)
 log = logging.getLogger(__name__)
 
 
 
 
@@ -589,37 +586,6 @@ class BaseVMFirewall(BaseCloudResource, VMFirewall):
         return self._provider.security.vm_firewalls.delete(self)
         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):
 class BaseVMFirewallRule(BaseCloudResource, VMFirewallRule):
 
 
     def __init__(self, parent_fw, rule):
     def __init__(self, parent_fw, rule):
@@ -768,38 +734,6 @@ class BaseBucket(BaseCloudResource, Bucket):
     # TODO: Discuss creating `create_object` method, or change docs
     # 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):
 class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
 
 
     CB_DEFAULT_NETWORK_LABEL = os.environ.get('CB_DEFAULT_NETWORK_LABEL',
     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)
         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):
 class BaseFloatingIP(BaseCloudResource, BaseObjectLifeCycleMixin, FloatingIP):
 
 
     def __init__(self, provider):
     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 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.exceptions import InvalidParamException
 from cloudbridge.cloud.interfaces.resources import Network
 from cloudbridge.cloud.interfaces.resources import Network
 from cloudbridge.cloud.interfaces.services import BucketObjectService
 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 VMTypeService
 from cloudbridge.cloud.interfaces.services import VolumeService
 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 BasePageableObjectMixin
+from .resources import BaseRouter
+from .resources import BaseSubnet
 from .resources import ClientPagedResultList
 from .resources import ClientPagedResultList
 
 
 log = logging.getLogger(__name__)
 log = logging.getLogger(__name__)
@@ -143,40 +140,13 @@ class BaseBucketService(
                                      matches if matches else [])
                                      matches if matches else [])
 
 
 
 
-class BaseBucketObjectService(
-        BasePageableObjectMixin, BucketObjectService, BaseCloudService):
+class BaseBucketObjectService(BucketObjectService, BaseCloudService):
 
 
     def __init__(self, provider):
     def __init__(self, provider):
         super(BaseBucketObjectService, self).__init__(provider)
         super(BaseBucketObjectService, self).__init__(provider)
-        self._service_event_pattern += ".storage.bucket_objects"
+        self._service_event_pattern += ".storage._bucket_objects"
         self._bucket = None
         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):
 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.
         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
         pass
 
 
@@ -1082,76 +1082,6 @@ class Subnet(ObjectLifeCycleMixin, LabeledCloudResource):
         pass
         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):
 class FloatingIpState(object):
 
 
     """
     """
@@ -1357,49 +1287,6 @@ class GatewayState(object):
     ERROR = "error"
     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):
 class Gateway(CloudResource):
     """
     """
     Represents a gateway resource.
     Represents a gateway resource.
@@ -1429,8 +1316,8 @@ class Gateway(CloudResource):
         """
         """
         Provides access to floating IPs connected to this internet gateway.
         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
         pass
 
 
@@ -2027,143 +1914,8 @@ class VMFirewall(LabeledCloudResource):
         This object can be used for further operations on rules, such as get,
         This object can be used for further operations on rules, such as get,
         list, create, etc.
         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
         pass
 
 
@@ -2435,71 +2187,3 @@ class Bucket(CloudResource):
         :return: ``True`` if successful.
         :return: ``True`` if successful.
         """
         """
         pass
         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
         pass
 
 
 
 
-class BucketObjectService(PageableObjectMixin, CloudService):
+class BucketObjectService(CloudService):
 
 
     """
     """
     The Bucket Object Service interface provides access to the underlying
     The Bucket Object Service interface provides access to the underlying
@@ -988,7 +988,7 @@ class BucketObjectService(PageableObjectMixin, CloudService):
         .. code-block:: python
         .. code-block:: python
 
 
             bucket = provider.storage.buckets.get('my_bucket_id')
             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)
                                                            bucket)
             print(buck_obj.id, buck_obj.name)
             print(buck_obj.id, buck_obj.name)
 
 
@@ -1009,7 +1009,7 @@ class BucketObjectService(PageableObjectMixin, CloudService):
         .. code-block:: python
         .. code-block:: python
 
 
             bucket = provider.storage.buckets.get('my_bucket_id')
             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')
                                                         name='my_obj_name')
             for buck_obj in objs:
             for buck_obj in objs:
                 print(buck_obj.id, buck_obj.name)
                 print(buck_obj.id, buck_obj.name)
@@ -1029,7 +1029,7 @@ class BucketObjectService(PageableObjectMixin, CloudService):
         .. code-block:: python
         .. code-block:: python
 
 
             bucket = provider.storage.buckets.get('my_bucket_id')
             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:
             for buck_obj in objs:
                 print(buck_obj.id, buck_obj.name)
                 print(buck_obj.id, buck_obj.name)
 
 
@@ -1048,7 +1048,7 @@ class BucketObjectService(PageableObjectMixin, CloudService):
         .. code-block:: python
         .. code-block:: python
 
 
             bucket = provider.storage.buckets.get('my_bucket_id')
             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)
                                                               bucket)
             print(buck_obj.name)
             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 BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseBucket
 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 BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
 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 BaseInstance
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
 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 BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewallRule
 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 BaseVMType
 from cloudbridge.cloud.base.resources import BaseVolume
 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 GatewayState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 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 TrafficDirection
 from cloudbridge.cloud.interfaces.resources import VolumeState
 from cloudbridge.cloud.interfaces.resources import VolumeState
 
 
-from .helpers import BotoEC2Service
 from .helpers import find_tag_value
 from .helpers import find_tag_value
 from .helpers import trim_empty_params
 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__)
 log = logging.getLogger(__name__)
 
 
@@ -615,7 +612,7 @@ class AWSVMFirewall(BaseVMFirewall):
 
 
     def __init__(self, provider, _vm_firewall):
     def __init__(self, provider, _vm_firewall):
         super(AWSVMFirewall, self).__init__(provider, _vm_firewall)
         super(AWSVMFirewall, self).__init__(provider, _vm_firewall)
-        self._rule_container = AWSVMFirewallRuleContainer(provider, self)
+        self._rule_container = AWSVMFirewallRuleSubService(provider, self)
 
 
     @property
     @property
     def name(self):
     def name(self):
@@ -659,56 +656,6 @@ class AWSVMFirewall(BaseVMFirewall):
         return js
         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):
 class AWSVMFirewallRule(BaseVMFirewallRule):
 
 
     def __init__(self, parent_fw, direction, rule):
     def __init__(self, parent_fw, direction, rule):
@@ -865,7 +812,7 @@ class AWSBucket(BaseBucket):
     def __init__(self, provider, bucket):
     def __init__(self, provider, bucket):
         super(AWSBucket, self).__init__(provider)
         super(AWSBucket, self).__init__(provider)
         self._bucket = bucket
         self._bucket = bucket
-        self._object_container = AWSBucketContainer(provider, self)
+        self._object_container = AWSBucketObjectSubService(provider, self)
 
 
     @property
     @property
     def id(self):
     def id(self):
@@ -880,12 +827,6 @@ class AWSBucket(BaseBucket):
         return self._object_container
         return self._object_container
 
 
 
 
-class AWSBucketContainer(BaseBucketContainer):
-
-    def __init__(self, provider, bucket):
-        super(AWSBucketContainer, self).__init__(provider, bucket)
-
-
 class AWSRegion(BaseRegion):
 class AWSRegion(BaseRegion):
 
 
     def __init__(self, provider, aws_region):
     def __init__(self, provider, aws_region):
@@ -927,7 +868,7 @@ class AWSNetwork(BaseNetwork):
     def __init__(self, provider, network):
     def __init__(self, provider, network):
         super(AWSNetwork, self).__init__(provider)
         super(AWSNetwork, self).__init__(provider)
         self._vpc = network
         self._vpc = network
-        self._gtw_container = AWSGatewayContainer(provider, self)
+        self._gtw_container = AWSGatewaySubService(provider, self)
         self._unknown_state = False
         self._unknown_state = False
 
 
     @property
     @property
@@ -1058,31 +999,6 @@ class AWSSubnet(BaseSubnet):
             self._unknown_state = True
             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):
 class AWSFloatingIP(BaseFloatingIP):
 
 
     def __init__(self, provider, floating_ip):
     def __init__(self, provider, floating_ip):
@@ -1186,57 +1102,13 @@ class AWSRouter(BaseRouter):
             InternetGatewayId=gw_id, VpcId=self._route_table.vpc_id)
             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):
 class AWSInternetGateway(BaseInternetGateway):
 
 
     def __init__(self, provider, gateway):
     def __init__(self, provider, gateway):
         super(AWSInternetGateway, self).__init__(provider)
         super(AWSInternetGateway, self).__init__(provider)
         self._gateway = gateway
         self._gateway = gateway
         self._gateway.state = ''
         self._gateway.state = ''
-        self._fips_container = AWSFloatingIPContainer(provider, self)
+        self._fips_container = AWSFloatingIPSubService(provider, self)
 
 
     @property
     @property
     def id(self):
     def id(self):

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

@@ -220,7 +220,7 @@ class AWSStorageService(BaseStorageService):
         return self._bucket_svc
         return self._bucket_svc
 
 
     @property
     @property
-    def bucket_objects(self):
+    def _bucket_objects(self):
         return self._bucket_obj_svc
         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 collections
 import logging
 import logging
-from uuid import uuid4
 
 
 from azure.common import AzureException
 from azure.common import AzureException
 from azure.mgmt.devtestlabs.models import GalleryImageReference
 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 BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseBucket
 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 BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
 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 BaseInstance
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
 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 BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewallRule
 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 BaseVMType
 from cloudbridge.cloud.base.resources import BaseVolume
 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 InstanceState
 from cloudbridge.cloud.interfaces import VolumeState
 from cloudbridge.cloud.interfaces import VolumeState
 from cloudbridge.cloud.interfaces.resources import Instance
 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 cloudbridge.cloud.interfaces.resources import TrafficDirection
 
 
 from . import helpers as azure_helpers
 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__)
 log = logging.getLogger(__name__)
 
 
@@ -57,7 +55,7 @@ class AzureVMFirewall(BaseVMFirewall):
         super(AzureVMFirewall, self).__init__(provider, vm_firewall)
         super(AzureVMFirewall, self).__init__(provider, vm_firewall)
         self._vm_firewall = vm_firewall
         self._vm_firewall = vm_firewall
         self._vm_firewall.tags = self._vm_firewall.tags or {}
         self._vm_firewall.tags = self._vm_firewall.tags or {}
-        self._rule_container = AzureVMFirewallRuleContainer(provider, self)
+        self._rule_container = AzureVMFirewallRuleSubService(provider, self)
 
 
     @property
     @property
     def network_id(self):
     def network_id(self):
@@ -123,70 +121,6 @@ class AzureVMFirewall(BaseVMFirewall):
         return js
         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
 # Tuple for port range
 PortRange = collections.namedtuple('PortRange', ['from_port', 'to_port'])
 PortRange = collections.namedtuple('PortRange', ['from_port', 'to_port'])
 
 
@@ -342,7 +276,7 @@ class AzureBucket(BaseBucket):
     def __init__(self, provider, bucket):
     def __init__(self, provider, bucket):
         super(AzureBucket, self).__init__(provider)
         super(AzureBucket, self).__init__(provider)
         self._bucket = bucket
         self._bucket = bucket
-        self._object_container = AzureBucketContainer(provider, self)
+        self._object_container = AzureBucketObjectSubService(provider, self)
 
 
     @property
     @property
     def id(self):
     def id(self):
@@ -366,12 +300,6 @@ class AzureBucket(BaseBucket):
         return self._object_container
         return self._object_container
 
 
 
 
-class AzureBucketContainer(BaseBucketContainer):
-
-    def __init__(self, provider, bucket):
-        super(AzureBucketContainer, self).__init__(provider, bucket)
-
-
 class AzureVolume(BaseVolume):
 class AzureVolume(BaseVolume):
     VOLUME_STATE_MAP = {
     VOLUME_STATE_MAP = {
         'InProgress': VolumeState.CREATING,
         'InProgress': VolumeState.CREATING,
@@ -785,28 +713,6 @@ class AzureMachineImage(BaseMachineImage):
                 self._state = "unknown"
                 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):
 class AzureNetwork(BaseNetwork):
     NETWORK_STATE_MAP = {
     NETWORK_STATE_MAP = {
         'InProgress': NetworkState.PENDING,
         'InProgress': NetworkState.PENDING,
@@ -819,7 +725,7 @@ class AzureNetwork(BaseNetwork):
         self._state = self._network.provisioning_state
         self._state = self._network.provisioning_state
         if not self._network.tags:
         if not self._network.tags:
             self._network.tags = {}
             self._network.tags = {}
-        self._gateway_service = AzureGatewayContainer(provider, self)
+        self._gateway_service = AzureGatewaySubService(provider, self)
 
 
     @property
     @property
     def id(self):
     def id(self):
@@ -920,39 +826,6 @@ class AzureNetwork(BaseNetwork):
         return self._gateway_service
         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):
 class AzureFloatingIP(BaseFloatingIP):
 
 
     def __init__(self, provider, floating_ip, network_id):
     def __init__(self, provider, floating_ip, network_id):
@@ -1651,7 +1524,7 @@ class AzureInternetGateway(BaseInternetGateway):
         self._network_id = gateway_net.id if isinstance(
         self._network_id = gateway_net.id if isinstance(
             gateway_net, AzureNetwork) else gateway_net
             gateway_net, AzureNetwork) else gateway_net
         self._state = ''
         self._state = ''
-        self._fips_container = AzureFloatingIPContainer(
+        self._fips_container = AzureFloatingIPSubService(
             provider, self, self._network_id)
             provider, self, self._network_id)
 
 
     @property
     @property

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

@@ -256,7 +256,7 @@ class AzureStorageService(BaseStorageService):
         return self._bucket_svc
         return self._bucket_svc
 
 
     @property
     @property
-    def bucket_objects(self):
+    def _bucket_objects(self):
         return self._bucket_obj_svc
         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
 import cloudbridge as cb
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseBucket
 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 BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
 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 BaseInstance
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
 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 BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewallRule
 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 BaseVMType
 from cloudbridge.cloud.base.resources import BaseVolume
 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 GatewayState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 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 SubnetState
 from cloudbridge.cloud.interfaces.resources import TrafficDirection
 from cloudbridge.cloud.interfaces.resources import TrafficDirection
 from cloudbridge.cloud.interfaces.resources import VolumeState
 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.
 # Older versions of Python do not have a built-in set data-structure.
+
 try:
 try:
     set
     set
 except NameError:
 except NameError:
@@ -454,7 +454,7 @@ class GCEVMFirewall(BaseVMFirewall):
                              .get_or_create_default())
                              .get_or_create_default())
         else:
         else:
             self._network = network
             self._network = network
-        self._rule_container = GCEVMFirewallRuleContainer(self)
+        self._rule_container = GCEVMFirewallRuleSubService(self)
 
 
     @property
     @property
     def id(self):
     def id(self):
@@ -537,67 +537,6 @@ class GCEVMFirewall(BaseVMFirewall):
         return self._delegate
         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):
 class GCEVMFirewallRule(BaseVMFirewallRule):
 
 
     def __init__(self, parent_fw, rule):
     def __init__(self, parent_fw, rule):
@@ -1323,7 +1262,7 @@ class GCENetwork(BaseNetwork):
     def __init__(self, provider, network):
     def __init__(self, provider, network):
         super(GCENetwork, self).__init__(provider)
         super(GCENetwork, self).__init__(provider)
         self._network = network
         self._network = network
-        self._gateway_container = GCEGatewayContainer(provider, self)
+        self._gateway_container = GCEGatewaySubService(provider, self)
 
 
     @property
     @property
     def resource_url(self):
     def resource_url(self):
@@ -1394,49 +1333,6 @@ class GCENetwork(BaseNetwork):
         return self._gateway_container
         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):
 class GCEFloatingIP(BaseFloatingIP):
     _DEAD_INSTANCE = 'dead instance'
     _DEAD_INSTANCE = 'dead instance'
 
 
@@ -1611,36 +1507,12 @@ class GCERouter(BaseRouter):
         pass
         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):
 class GCEInternetGateway(BaseInternetGateway):
 
 
     def __init__(self, provider, gateway):
     def __init__(self, provider, gateway):
         super(GCEInternetGateway, self).__init__(provider)
         super(GCEInternetGateway, self).__init__(provider)
         self._gateway = gateway
         self._gateway = gateway
-        self._fip_container = GCEFloatingIPContainer(provider, self)
+        self._fip_container = GCEFloatingIPSubService(provider, self)
 
 
     @property
     @property
     def id(self):
     def id(self):
@@ -2088,7 +1960,7 @@ class GCSObject(BaseBucketObject):
         media_body = googleapiclient.http.MediaIoBaseUpload(
         media_body = googleapiclient.http.MediaIoBaseUpload(
                 io.BytesIO(data), mimetype='plain/text')
                 io.BytesIO(data), mimetype='plain/text')
         response = (self._provider
         response = (self._provider
-                        .storage.bucket_objects
+                        .storage._bucket_objects
                         ._create_object_with_media_body(self._bucket,
                         ._create_object_with_media_body(self._bucket,
                                                         self.name,
                                                         self.name,
                                                         media_body))
                                                         media_body))
@@ -2103,7 +1975,7 @@ class GCSObject(BaseBucketObject):
             media_body = googleapiclient.http.MediaIoBaseUpload(
             media_body = googleapiclient.http.MediaIoBaseUpload(
                     f, 'application/octet-stream')
                     f, 'application/octet-stream')
             response = (self._provider
             response = (self._provider
-                        .storage.bucket_objects
+                        .storage._bucket_objects
                         ._create_object_with_media_body(self._bucket,
                         ._create_object_with_media_body(self._bucket,
                                                         self.name,
                                                         self.name,
                                                         media_body))
                                                         media_body))
@@ -2139,18 +2011,12 @@ class GCSObject(BaseBucketObject):
         self._obj = self.bucket.objects.get(self.id)._obj
         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):
 class GCSBucket(BaseBucket):
 
 
     def __init__(self, provider, bucket):
     def __init__(self, provider, bucket):
         super(GCSBucket, self).__init__(provider)
         super(GCSBucket, self).__init__(provider)
         self._bucket = bucket
         self._bucket = bucket
-        self._object_container = GCSBucketContainer(provider, self)
+        self._object_container = GCSBucketObjectSubService(provider, self)
 
 
     @property
     @property
     def id(self):
     def id(self):

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

@@ -1042,7 +1042,7 @@ class GCPStorageService(BaseStorageService):
         return self._bucket_svc
         return self._bucket_svc
 
 
     @property
     @property
-    def bucket_objects(self):
+    def _bucket_objects(self):
         return self._bucket_obj_svc
         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 ipaddress
 import logging
 import logging
 import os
 import os
+
 try:
 try:
     from urllib.parse import urlparse
     from urllib.parse import urlparse
     from urllib.parse import urljoin
     from urllib.parse import urljoin
@@ -16,23 +17,15 @@ from keystoneclient.v3.regions import Region
 
 
 import novaclient.exceptions as novaex
 import novaclient.exceptions as novaex
 
 
-from openstack.exceptions import HttpException
-from openstack.exceptions import NotFoundException
-from openstack.exceptions import ResourceNotFound
-
 import swiftclient
 import swiftclient
 from swiftclient.service import SwiftService
 from swiftclient.service import SwiftService
 from swiftclient.service import SwiftUploadObject
 from swiftclient.service import SwiftUploadObject
 from swiftclient.utils import generate_temp_url
 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 BaseAttachmentInfo
 from cloudbridge.cloud.base.resources import BaseBucket
 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 BaseBucketObject
 from cloudbridge.cloud.base.resources import BaseFloatingIP
 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 BaseInstance
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseInternetGateway
 from cloudbridge.cloud.base.resources import BaseKeyPair
 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 BaseSubnet
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewall
 from cloudbridge.cloud.base.resources import BaseVMFirewallRule
 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 BaseVMType
 from cloudbridge.cloud.base.resources import BaseVolume
 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 GatewayState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 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 TrafficDirection
 from cloudbridge.cloud.interfaces.resources import VolumeState
 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
 ONE_GIG = 1048576000  # in bytes
 FIVE_GIG = ONE_GIG * 5  # in bytes
 FIVE_GIG = ONE_GIG * 5  # in bytes
 
 
@@ -767,50 +762,6 @@ class OpenStackSnapshot(BaseSnapshot):
         return cb_vol
         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):
 class OpenStackNetwork(BaseNetwork):
 
 
     # Ref: https://github.com/openstack/neutron/blob/master/neutron/plugins/
     # Ref: https://github.com/openstack/neutron/blob/master/neutron/plugins/
@@ -829,7 +780,7 @@ class OpenStackNetwork(BaseNetwork):
     def __init__(self, provider, network):
     def __init__(self, provider, network):
         super(OpenStackNetwork, self).__init__(provider)
         super(OpenStackNetwork, self).__init__(provider)
         self._network = network
         self._network = network
-        self._gateway_service = OpenStackGatewayContainer(provider, self)
+        self._gateway_service = OpenStackGatewaySubService(provider, self)
 
 
     @property
     @property
     def id(self):
     def id(self):
@@ -952,33 +903,6 @@ class OpenStackSubnet(BaseSubnet):
             self._state = SubnetState.UNKNOWN
             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):
 class OpenStackFloatingIP(BaseFloatingIP):
 
 
     def __init__(self, provider, floating_ip):
     def __init__(self, provider, floating_ip):
@@ -1109,7 +1033,7 @@ class OpenStackInternetGateway(BaseInternetGateway):
             # pylint:disable=protected-access
             # pylint:disable=protected-access
             gateway_net = gateway_net._network
             gateway_net = gateway_net._network
         self._gateway_net = gateway_net
         self._gateway_net = gateway_net
-        self._fips_container = OpenStackFloatingIPContainer(provider, self)
+        self._fips_container = OpenStackFloatingIPSubService(provider, self)
 
 
     @property
     @property
     def id(self):
     def id(self):
@@ -1158,7 +1082,7 @@ class OpenStackVMFirewall(BaseVMFirewall):
 
 
     def __init__(self, provider, vm_firewall):
     def __init__(self, provider, vm_firewall):
         super(OpenStackVMFirewall, self).__init__(provider, vm_firewall)
         super(OpenStackVMFirewall, self).__init__(provider, vm_firewall)
-        self._rule_svc = OpenStackVMFirewallRuleContainer(provider, self)
+        self._rule_svc = OpenStackVMFirewallRuleSubService(provider, self)
 
 
     @property
     @property
     def network_id(self):
     def network_id(self):
@@ -1231,55 +1155,6 @@ class OpenStackVMFirewall(BaseVMFirewall):
         return js
         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):
 class OpenStackVMFirewallRule(BaseVMFirewallRule):
 
 
     def __init__(self, parent_fw, rule):
     def __init__(self, parent_fw, rule):
@@ -1453,7 +1328,8 @@ class OpenStackBucket(BaseBucket):
     def __init__(self, provider, bucket):
     def __init__(self, provider, bucket):
         super(OpenStackBucket, self).__init__(provider)
         super(OpenStackBucket, self).__init__(provider)
         self._bucket = bucket
         self._bucket = bucket
-        self._object_container = OpenStackBucketContainer(provider, self)
+        self._object_container = OpenStackBucketObjectSubService(provider,
+                                                                 self)
 
 
     @property
     @property
     def id(self):
     def id(self):
@@ -1466,9 +1342,3 @@ class OpenStackBucket(BaseBucket):
     @property
     @property
     def objects(self):
     def objects(self):
         return self._object_container
         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
         return self._bucket_svc
 
 
     @property
     @property
-    def bucket_objects(self):
+    def _bucket_objects(self):
         return self._bucket_obj_svc
         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
 FloatingIPService
 -----------------
 -----------------
-.. autoclass:: cloudbridge.cloud.interfaces.resources.FloatingIPContainer
+.. autoclass:: cloudbridge.cloud.interfaces.resources.FloatingIPSubService
     :members:
     :members:
 
 
 RouterService
 RouterService
@@ -65,7 +65,7 @@ RouterService
 
 
 GatewayService
 GatewayService
 -----------------
 -----------------
-.. autoclass:: cloudbridge.cloud.interfaces.resources.GatewayContainer
+.. autoclass:: cloudbridge.cloud.interfaces.resources.GatewaySubService
     :members:
     :members:
 
 
 BucketService
 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
 .. code-block:: python
 
 
     bucket = provider.storage.buckets.get(container)
     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)
     obj.upload_from_file(source)

+ 2 - 1
setup.py

@@ -6,7 +6,8 @@ import ast
 import os
 import os
 import re
 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
 # Cannot use "from cloudbridge import get_version" because that would try to
 # import the six package which may not be installed yet.
 # 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
     _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):
     def test_storage_services_event_pattern(self):
         self.assertEqual(
         self.assertEqual(
             self.provider.storage.buckets._service_event_pattern,
             self.provider.storage.buckets._service_event_pattern,
@@ -32,12 +32,12 @@ class CloudObjectStoreServiceTestCase(ProviderTestBase):
                                      self.provider.storage.buckets.
                                      self.provider.storage.buckets.
                                      _service_event_pattern))
                                      _service_event_pattern))
         self.assertEqual(
         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 '{}', "
             "Event pattern for {} service should be '{}', "
             "but found '{}'.".format("bucket_objects",
             "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))
                                      _service_event_pattern))
 
 
     @helpers.skipIfNoService(['storage.buckets'])
     @helpers.skipIfNoService(['storage.buckets'])