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

Make floating ips a standalone service

Nuwan Goonasekera 8 лет назад
Родитель
Сommit
22f5a152fe

+ 2 - 1
cloudbridge/cloud/base/resources.py

@@ -952,11 +952,12 @@ class BaseFloatingIP(BaseCloudResource, FloatingIP):
     def __init__(self, provider):
         super(BaseFloatingIP, self).__init__(provider)
 
+    @property
     def name(self):
         """
         VM firewall rules don't support names, so pass
         """
-        pass
+        return self.public_ip
 
     def __repr__(self):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,

+ 22 - 1
cloudbridge/cloud/base/services.py

@@ -6,6 +6,7 @@ from cloudbridge.cloud.interfaces.resources import Router
 from cloudbridge.cloud.interfaces.services import BlockStoreService
 from cloudbridge.cloud.interfaces.services import CloudService
 from cloudbridge.cloud.interfaces.services import ComputeService
+from cloudbridge.cloud.interfaces.services import FloatingIPService
 from cloudbridge.cloud.interfaces.services import GatewayService
 from cloudbridge.cloud.interfaces.services import ImageService
 from cloudbridge.cloud.interfaces.services import InstanceService
@@ -169,7 +170,6 @@ class BaseNetworkService(
         network = self.get(network_id)
         if network:
             network.delete()
-        return True
 
 
 class BaseSubnetService(
@@ -187,6 +187,27 @@ class BaseSubnetService(
                 "Invalid parameters for search. Supported attributes: {name}")
 
 
+class BaseFloatingIPService(
+        BasePageableObjectMixin, FloatingIPService, BaseCloudService):
+
+    def __init__(self, provider):
+        super(BaseFloatingIPService, self).__init__(provider)
+
+    def find(self, **kwargs):
+        if 'name' in kwargs:
+            name = kwargs.get('name')
+            if name:
+                return [fip for fip in self if fip.name == name]
+        else:
+            raise TypeError(
+                "Invalid parameters for search. Supported attributes: {name}")
+
+    def delete(self, fip_id):
+        floating_ip = self.get(fip_id)
+        if floating_ip:
+            floating_ip.delete()
+
+
 class BaseRouterService(
         BasePageableObjectMixin, RouterService, BaseCloudService):
 

+ 60 - 36
cloudbridge/cloud/interfaces/services.py

@@ -623,11 +623,6 @@ class NetworkService(PageableObjectMixin, CloudService):
 
         :type network_id: ``str``
         :param network_id: The ID of the network to be deleted.
-
-        :rtype: ``bool``
-        :return:  ``True`` if the network does not exist, ``False`` otherwise.
-                  Note that this implies that the network may not have been
-                  deleted by this method but instead has not existed at all.
         """
         pass
 
@@ -653,33 +648,6 @@ class NetworkService(PageableObjectMixin, CloudService):
         """
         pass
 
-    @abstractproperty
-    def floating_ips(self):
-        """
-        List floating (i.e., static) IP addresses.
-
-        :type network_id: ``str``
-        :param network_id: The ID of the network by which to filter the IPs.
-
-        :rtype: ``list`` of :class:`FloatingIP`
-        :return: list of floating IP objects
-        """
-        pass
-
-    @abstractmethod
-    def create_floating_ip(self):
-        """
-        Allocate a new floating (i.e., static) IP address.
-
-        :type network_id: ``str``
-        :param network_id: The ID of the network with which to associate the
-                           new IP address.
-
-        :rtype: :class:`FloatingIP`
-        :return: floating IP object
-        """
-        pass
-
 
 class SubnetService(PageableObjectMixin, CloudService):
 
@@ -768,11 +736,67 @@ class SubnetService(PageableObjectMixin, CloudService):
 
         :type subnet: :class:`.Subnet` object or ``str``
         :param subnet: Subnet object or ID of the subnet to delete.
+        """
+        pass
 
-        :rtype: ``bool``
-        :return:  ``True`` if the subnet does not exist, ``False`` otherwise.
-                  Note that this implies that the subnet may not have been
-                  deleted by this method but instead has not existed at all.
+
+class FloatingIPService(PageableObjectMixin, CloudService):
+
+    """
+    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, name):
+        """
+        Searches for a FloatingIP by a given list of attributes.
+
+        :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
 

+ 3 - 3
cloudbridge/cloud/providers/aws/resources.py

@@ -290,7 +290,7 @@ class AWSInstance(BaseInstance):
         allocation_id = (
             None if not self._ec2_instance.vpc_id else
             ip_address.id if isinstance(ip_address, AWSFloatingIP) else
-            [x for x in self._provider.networking.networks.floating_ips
+            [x for x in self._provider.networking.floating_ips
              if x.public_ip == ip_address][0].id)
         params = trim_empty_params({
             'InstanceId': self.id,
@@ -895,7 +895,7 @@ class AWSNetwork(BaseNetwork):
         return self._vpc.cidr_block
 
     def delete(self):
-        return self._vpc.delete()
+        self._vpc.delete()
 
     @property
     def subnets(self):
@@ -997,7 +997,7 @@ class AWSFloatingIP(BaseFloatingIP):
         return True if self._ip.instance_id else False
 
     def delete(self):
-        return self._ip.release()
+        self._ip.release()
 
 
 class AWSRouter(BaseRouter):

+ 29 - 15
cloudbridge/cloud/providers/aws/services.py

@@ -6,6 +6,7 @@ from botocore.exceptions import ClientError
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.services import BaseBlockStoreService
 from cloudbridge.cloud.base.services import BaseComputeService
+from cloudbridge.cloud.base.services import BaseFloatingIPService
 from cloudbridge.cloud.base.services import BaseGatewayService
 from cloudbridge.cloud.base.services import BaseImageService
 from cloudbridge.cloud.base.services import BaseInstanceService
@@ -526,6 +527,7 @@ class AWSNetworkingService(BaseNetworkingService):
         super(AWSNetworkingService, self).__init__(provider)
         self._network_service = AWSNetworkService(self.provider)
         self._subnet_service = AWSSubnetService(self.provider)
+        self._fip_service = AWSFloatingIPService(self.provider)
         self._router_service = AWSRouterService(self.provider)
         self._gateway_service = AWSGatewayService(self.provider)
 
@@ -537,6 +539,10 @@ class AWSNetworkingService(BaseNetworkingService):
     def subnets(self):
         return self._subnet_service
 
+    @property
+    def floating_ips(self):
+        return self._fip_service
+
     @property
     def routers(self):
         return self._router_service
@@ -574,20 +580,6 @@ class AWSNetworkService(BaseNetworkService):
             cb_net.name = name
         return cb_net
 
-    @property
-    def floating_ips(self):
-        self.svc_fip = BotoEC2Service(provider=self.provider,
-                                      cb_resource=AWSFloatingIP,
-                                      boto_collection_name='vpc_addresses')
-        return self.svc_fip.list()
-
-    def create_floating_ip(self):
-        ip = self.provider.ec2_conn.meta.client.allocate_address(
-            Domain='vpc')
-        return AWSFloatingIP(
-            self.provider,
-            self.provider.ec2_conn.VpcAddress(ip.get('AllocationId')))
-
 
 class AWSSubnetService(BaseSubnetService):
 
@@ -660,7 +652,29 @@ class AWSSubnetService(BaseSubnetService):
 
     def delete(self, subnet):
         subnet_id = subnet.id if isinstance(subnet, AWSSubnet) else subnet
-        return self.svc.delete(subnet_id)
+        self.svc.delete(subnet_id)
+
+
+class AWSFloatingIPService(BaseFloatingIPService):
+
+    def __init__(self, provider):
+        super(AWSFloatingIPService, self).__init__(provider)
+        self.svc = BotoEC2Service(provider=self.provider,
+                                  cb_resource=AWSFloatingIP,
+                                  boto_collection_name='vpc_addresses')
+
+    def get(self, router_id):
+        return self.svc.get(router_id)
+
+    def list(self, limit=None, marker=None):
+        return self.svc.list(limit=limit, marker=marker)
+
+    def create(self):
+        ip = self.provider.ec2_conn.meta.client.allocate_address(
+            Domain='vpc')
+        return AWSFloatingIP(
+            self.provider,
+            self.provider.ec2_conn.VpcAddress(ip.get('AllocationId')))
 
 
 class AWSRouterService(BaseRouterService):

+ 0 - 9
cloudbridge/cloud/providers/openstack/resources.py

@@ -754,9 +754,6 @@ class OpenStackNetwork(BaseNetwork):
                     # are terminated etc. so exceptions can be safely ignored
                     pass
             self._provider.neutron.delete_network(self.id)
-        # Adhere to the interface docs
-        if self.id not in str(self._provider.neutron.list_networks()):
-            return True
 
     @property
     def subnets(self):
@@ -820,9 +817,6 @@ class OpenStackSubnet(BaseSubnet):
     def delete(self):
         if self.id in str(self._provider.neutron.list_subnets()):
             self._provider.neutron.delete_subnet(self.id)
-        # Adhere to the interface docs
-        if self.id not in str(self._provider.neutron.list_subnets()):
-            return True
 
     @property
     def state(self):
@@ -863,9 +857,6 @@ class OpenStackFloatingIP(BaseFloatingIP):
 
     def delete(self):
         self._provider.neutron.delete_floatingip(self.id)
-        # Adhere to the interface docs
-        if self.id not in str(self._provider.neutron.list_floatingips()):
-            return True
 
 
 class OpenStackRouter(BaseRouter):

+ 32 - 17
cloudbridge/cloud/providers/openstack/services.py

@@ -11,6 +11,7 @@ from cloudbridge.cloud.base.resources import BaseLaunchConfig
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.services import BaseBlockStoreService
 from cloudbridge.cloud.base.services import BaseComputeService
+from cloudbridge.cloud.base.services import BaseFloatingIPService
 from cloudbridge.cloud.base.services import BaseGatewayService
 from cloudbridge.cloud.base.services import BaseImageService
 from cloudbridge.cloud.base.services import BaseInstanceService
@@ -705,6 +706,7 @@ class OpenStackNetworkingService(BaseNetworkingService):
         super(OpenStackNetworkingService, self).__init__(provider)
         self._network_service = OpenStackNetworkService(self.provider)
         self._subnet_service = OpenStackSubnetService(self.provider)
+        self._fip_service = OpenStackFloatingIPService(self.provider)
         self._router_service = OpenStackRouterService(self.provider)
         self._gateway_service = OpenStackGatewayService(self.provider)
 
@@ -716,6 +718,10 @@ class OpenStackNetworkingService(BaseNetworkingService):
     def subnets(self):
         return self._subnet_service
 
+    @property
+    def floating_ips(self):
+        return self._fip_service
+
     @property
     def routers(self):
         return self._router_service
@@ -756,23 +762,6 @@ class OpenStackNetworkService(BaseNetworkService):
         network = self.provider.neutron.create_network({'network': net_info})
         return OpenStackNetwork(self.provider, network.get('network'))
 
-    @property
-    def floating_ips(self):
-        # if network_id:
-        #    al = self.provider.neutron.list_floatingips(
-        #        floating_network_id=network_id)['floatingips']
-        al = self.provider.neutron.list_floatingips()['floatingips']
-        return [OpenStackFloatingIP(self.provider, a) for a in al]
-
-    def create_floating_ip(self):
-        # OpenStack requires a floating IP to be associated with a pool,
-        # so just choose the first one available...
-        ip_pool_name = self.provider.nova.floating_ip_pools.list()[0].name
-        ip = self.provider.nova.floating_ips.create(ip_pool_name)
-        # Nova returns a different object than Neutron so fetch the Neutron one
-        ip = self.provider.neutron.list_floatingips(id=ip.id)['floatingips'][0]
-        return OpenStackFloatingIP(self.provider, ip)
-
 
 class OpenStackSubnetService(BaseSubnetService):
 
@@ -843,6 +832,32 @@ class OpenStackSubnetService(BaseSubnetService):
         return False
 
 
+class OpenStackFloatingIPService(BaseFloatingIPService):
+
+    def __init__(self, provider):
+        super(OpenStackFloatingIPService, self).__init__(provider)
+
+    def get(self, fip_id):
+        floating_ip = (fip for fip in self if fip.id == fip_id)
+        return next(floating_ip, None)
+
+    def list(self, limit=None, marker=None):
+        fips = [OpenStackFloatingIP(self.provider, fip)
+                for fip in self.provider.neutron.list_floatingips()
+                .get('floatingips') if fip]
+        return ClientPagedResultList(self.provider, fips,
+                                     limit=limit, marker=marker)
+
+    def create(self):
+        # OpenStack requires a floating IP to be associated with a pool,
+        # so just choose the first one available...
+        ip_pool_name = self.provider.nova.floating_ip_pools.list()[0].name
+        ip = self.provider.nova.floating_ips.create(ip_pool_name)
+        # Nova returns a different object than Neutron so fetch the Neutron one
+        ip = self.provider.neutron.list_floatingips(id=ip.id)['floatingips'][0]
+        return OpenStackFloatingIP(self.provider, ip)
+
+
 class OpenStackRouterService(BaseRouterService):
 
     def __init__(self, provider):

+ 2 - 2
test/test_compute_service.py

@@ -289,6 +289,7 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                         # correspond to requested mappings
 
     @helpers.skipIfNoService(['compute.instances', 'networking.networks',
+                              'networking.floating_ips',
                               'security.vm_firewalls'])
     def test_instance_methods(self):
         name = "cb_instmethods-{0}".format(helpers.get_uuid())
@@ -339,8 +340,7 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                            .get_or_create_inet_gateway(name))
                 router.attach_gateway(gateway)
                 # check whether adding an elastic ip works
-                fip = (self.provider.networking.networks
-                       .create_floating_ip())
+                fip = self.provider.networking.floating_ips.create()
                 with helpers.cleanup_action(lambda: fip.delete()):
                     with helpers.cleanup_action(
                             lambda: test_inst.remove_floating_ip(

+ 21 - 20
test/test_network_service.py

@@ -3,6 +3,7 @@ import test.helpers as helpers
 from test.helpers import ProviderTestBase
 from test.helpers import standard_interface_tests as sit
 
+from cloudbridge.cloud.interfaces.resources import FloatingIP
 from cloudbridge.cloud.interfaces.resources import Network
 from cloudbridge.cloud.interfaces.resources import RouterState
 from cloudbridge.cloud.interfaces.resources import Subnet
@@ -90,38 +91,38 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
             sit.check_crud(self, self.provider.networking.subnets, Subnet,
                            "cb_crudsubnet", create_subnet, cleanup_subnet)
 
+    @helpers.skipIfNoService(['networking.floating_ips'])
+    def test_crud_floating_ip(self):
+
+        def create_fip(name):
+            return self.provider.networking.floating_ips.create()
+
+        def cleanup_fip(fip):
+            self.provider.networking.floating_ips.delete(fip.id)
+
+        sit.check_crud(self, self.provider.networking.floating_ips, FloatingIP,
+                       "cb_crudfip", create_fip, cleanup_fip,
+                       skip_name_check=True)
+
     def test_floating_ip_properties(self):
         # Check floating IP address
-        ip = self.provider.networking.networks.create_floating_ip()
-        ip_id = ip.id
-        with helpers.cleanup_action(lambda: ip.delete()):
-            ipl = self.provider.networking.networks.floating_ips
-            self.assertTrue(
-                ip in ipl,
-                "Floating IP address {0} should exist in the list {1}"
-                .format(ip.id, ipl))
+        fip = self.provider.networking.floating_ips.create()
+        with helpers.cleanup_action(lambda: fip.delete()):
+            fipl = list(self.provider.networking.floating_ips)
+            self.assertIn(fip, fipl)
             # 2016-08: address filtering not implemented in moto
             # empty_ipl = self.provider.network.floating_ips('dummy-net')
             # self.assertFalse(
             #     empty_ipl,
             #     "Bogus network should not have any floating IPs: {0}"
             #     .format(empty_ipl))
-            self.assertIn(
-                ip.public_ip, repr(ip),
-                "repr(obj) should contain the address public IP value.")
             self.assertFalse(
-                ip.private_ip,
+                fip.private_ip,
                 "Floating IP should not have a private IP value ({0})."
-                .format(ip.private_ip))
+                .format(fip.private_ip))
             self.assertFalse(
-                ip.in_use(),
+                fip.in_use(),
                 "Newly created floating IP address should not be in use.")
-        ipl = self.provider.networking.networks.floating_ips
-        found_ip = [a for a in ipl if a.id == ip_id]
-        self.assertTrue(
-            len(found_ip) == 0,
-            "Floating IP {0} should have been deleted but still exists."
-            .format(ip_id))
 
     @helpers.skipIfNoService(['networking.routers'])
     def test_crud_router(self):