Browse Source

Made floating ips more consistent and made openstack use sdk for it

Nuwan Goonasekera 8 years ago
parent
commit
18305a38da

+ 9 - 0
cloudbridge/cloud/interfaces/exceptions.py

@@ -28,6 +28,15 @@ class InvalidConfigurationException(CloudBridgeBaseException):
     pass
 
 
+class ProviderInternalException(CloudBridgeBaseException):
+    """
+    Marker interface for provider specific errors.
+    Thrown when CloudBridge encounters an error internal to a
+    provider.
+    """
+    pass
+
+
 class ProviderConnectionException(CloudBridgeBaseException):
     """
     Marker interface for connection errors to a cloud provider.

+ 8 - 8
cloudbridge/cloud/interfaces/resources.py

@@ -579,22 +579,22 @@ class Instance(ObjectLifeCycleMixin, CloudResource):
         pass
 
     @abstractmethod
-    def add_floating_ip(self, ip_address):
+    def add_floating_ip(self, floating_ip):
         """
         Add a public IP address to this instance.
 
-        :type ip_address: ``str``
-        :param ip_address: The IP address to associate with the instance.
+        :type floating_ip: :class:``.FloatingIP``
+        :param floating_ip: The FloatingIP to associate with the instance.
         """
         pass
 
     @abstractmethod
-    def remove_floating_ip(self, ip_address):
+    def remove_floating_ip(self, floating_ip):
         """
         Remove a public IP address from this instance.
 
-        :type ip_address: ``str``
-        :param ip_address: The IP address to remove from the instance.
+        :type floating_ip: :class:``.FloatingIP``
+        :param floating_ip: The IP address to remove from the instance.
         """
         pass
 
@@ -603,7 +603,7 @@ class Instance(ObjectLifeCycleMixin, CloudResource):
         """
         Add a VM firewall to this instance
 
-        :type firewall: ``VMFirewall``
+        :type firewall: :class:``.VMFirewall``
         :param firewall: The VMFirewall to associate with the instance.
         """
         pass
@@ -976,7 +976,7 @@ class FloatingIP(CloudResource):
         """
         pass
 
-    @abstractmethod
+    @abstractproperty
     def in_use(self):
         """
         Whether the address is in use or not.

+ 13 - 17
cloudbridge/cloud/providers/aws/resources.py

@@ -288,29 +288,24 @@ class AWSInstance(BaseInstance):
         image.refresh()
         return image
 
-    def add_floating_ip(self, ip_address):
-        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.floating_ips
-             if x.public_ip == ip_address][0].id)
+    def add_floating_ip(self, floating_ip):
+        fip = (
+            floating_ip if isinstance(floating_ip, AWSFloatingIP) else
+            self._provider.networking.floating_ips.get(floating_ip))
         params = trim_empty_params({
             'InstanceId': self.id,
-            'PublicIp': None if self._ec2_instance.vpc_id else ip_address,
-            'AllocationId': allocation_id})
+            'PublicIp': None if self._ec2_instance.vpc_id else fip.public_ip,
+            'AllocationId': fip._ip.allocation_id})
         self._provider.ec2_conn.meta.client.associate_address(**params)
         self.refresh()
 
-    def remove_floating_ip(self, ip_address):
-        association_id = (
-            None if not self._ec2_instance.vpc_id else
-            ip_address._ip.association_id
-            if isinstance(ip_address, AWSFloatingIP) else
-            [x for x in self._ec2_instance.vpc_addresses.all()
-             if x.public_ip == ip_address][0].association_id)
+    def remove_floating_ip(self, floating_ip):
+        fip = (
+            floating_ip if isinstance(floating_ip, AWSFloatingIP) else
+            self._provider.networking.floating_ips.get(floating_ip))
         params = trim_empty_params({
-            'PublicIp': None if self._ec2_instance.vpc_id else ip_address,
-            'AssociationId': association_id})
+            'PublicIp': None if self._ec2_instance.vpc_id else fip.public_ip,
+            'AssociationId': fip._ip.association_id})
         self._provider.ec2_conn.meta.client.disassociate_address(**params)
         self.refresh()
 
@@ -1011,6 +1006,7 @@ class AWSFloatingIP(BaseFloatingIP):
     def private_ip(self):
         return self._ip.private_ip_address
 
+    @property
     def in_use(self):
         return True if self._ip.instance_id else False
 

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

@@ -388,17 +388,17 @@ class OpenStackInstance(BaseInstance):
         return OpenStackMachineImage(
             self._provider, self._provider.compute.images.get(image_id))
 
-    def add_floating_ip(self, ip_address):
+    def add_floating_ip(self, floating_ip):
         """
         Add a floating IP address to this instance.
         """
-        self._os_instance.add_floating_ip(ip_address)
+        self._os_instance.add_floating_ip(floating_ip.public_ip)
 
-    def remove_floating_ip(self, ip_address):
+    def remove_floating_ip(self, floating_ip):
         """
         Remove a floating IP address from this instance.
         """
-        self._os_instance.remove_floating_ip(ip_address)
+        self._os_instance.remove_floating_ip(floating_ip.public_ip)
 
     def add_vm_firewall(self, firewall):
         """
@@ -844,21 +844,22 @@ class OpenStackFloatingIP(BaseFloatingIP):
 
     @property
     def id(self):
-        return self._ip.get('id', None)
+        return self._ip.id
 
     @property
     def public_ip(self):
-        return self._ip.get('floating_ip_address', None)
+        return self._ip.floating_ip_address
 
     @property
     def private_ip(self):
-        return self._ip.get('fixed_ip_address', None)
+        return self._ip.fixed_ip_address
 
+    @property
     def in_use(self):
-        return bool(self._ip.get('port_id', None))
+        return bool(self._ip.port_id)
 
     def delete(self):
-        self._provider.neutron.delete_floatingip(self.id)
+        self._ip.delete(self._provider.os_conn.session)
 
 
 class OpenStackRouter(BaseRouter):

+ 16 - 11
cloudbridge/cloud/providers/openstack/services.py

@@ -27,6 +27,7 @@ from cloudbridge.cloud.base.services import BaseSubnetService
 from cloudbridge.cloud.base.services import BaseVMFirewallService
 from cloudbridge.cloud.base.services import BaseVMTypeService
 from cloudbridge.cloud.base.services import BaseVolumeService
+from cloudbridge.cloud.interfaces.exceptions import ProviderInternalException
 from cloudbridge.cloud.interfaces.resources import KeyPair
 from cloudbridge.cloud.interfaces.resources import MachineImage
 from cloudbridge.cloud.interfaces.resources import PlacementZone
@@ -843,24 +844,28 @@ class OpenStackFloatingIPService(BaseFloatingIPService):
         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)
+        try:
+            return OpenStackFloatingIP(
+                self.provider, self.provider.os_conn.network.get_ip(fip_id))
+        except ResourceNotFound:
+            return 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]
+                for fip in self.provider.os_conn.network.ips()]
         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)
+        # OpenStack requires a floating IP to be associated with an external,
+        # network, so choose the first external network found
+        for n in self.provider.networking.networks:
+            if n.external:
+                return OpenStackFloatingIP(
+                    self.provider, self.provider.os_conn.network.create_ip(
+                        floating_network_id=n.id))
+        raise ProviderInternalException(
+            "This OpenStack cloud has no designated external network")
 
 
 class OpenStackRouterService(BaseRouterService):

+ 4 - 4
setup.py

@@ -27,10 +27,10 @@ REQS_AWS = ['boto3']
 REQS_OPENSTACK = [
     'openstacksdk',
     'python-novaclient>=7.0.0',
-    'python-glanceclient>=2.5.0,<=2.6.0',
-    'python-cinderclient>=1.9.0,<=2.0.1',
-    'python-swiftclient>=3.2.0,<=3.3.0',
-    'python-neutronclient>=6.0.0,<=6.1.0',
+    'python-glanceclient>=2.5.0',
+    'python-cinderclient>=1.9.0',
+    'python-swiftclient>=3.2.0',
+    'python-neutronclient>=6.0.0',
     'python-keystoneclient>=3.13.0'
 ]
 REQS_FULL = REQS_BASE + REQS_AWS + REQS_OPENSTACK

+ 2 - 3
test/test_compute_service.py

@@ -343,9 +343,8 @@ class CloudComputeServiceTestCase(ProviderTestBase):
                 fip = self.provider.networking.floating_ips.create()
                 with helpers.cleanup_action(lambda: fip.delete()):
                     with helpers.cleanup_action(
-                            lambda: test_inst.remove_floating_ip(
-                                fip.public_ip)):
-                        test_inst.add_floating_ip(fip.public_ip)
+                            lambda: test_inst.remove_floating_ip(fip)):
+                        test_inst.add_floating_ip(fip)
                         test_inst.refresh()
                         # On Devstack, FloatingIP is listed under private_ips.
                         self.assertIn(fip.public_ip, test_inst.public_ips +

+ 1 - 1
test/test_network_service.py

@@ -121,7 +121,7 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
                 "Floating IP should not have a private IP value ({0})."
                 .format(fip.private_ip))
             self.assertFalse(
-                fip.in_use(),
+                fip.in_use,
                 "Newly created floating IP address should not be in use.")
 
     @helpers.skipIfNoService(['networking.routers'])