Updated Branches:
  refs/heads/trunk db36293ca -> 7b714ae08

Add support for floating IPs to OpenStack

Signed-off-by: Tomaz Muraus <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/8ba5b81c
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/8ba5b81c
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/8ba5b81c

Branch: refs/heads/trunk
Commit: 8ba5b81cf65915279568a14b31b682666444cb25
Parents: db36293
Author: Ivan Kusalic <[email protected]>
Authored: Thu Aug 22 17:03:06 2013 +0200
Committer: Tomaz Muraus <[email protected]>
Committed: Fri Aug 23 14:31:07 2013 +0200

----------------------------------------------------------------------
 libcloud/compute/drivers/openstack.py           | 150 +++++++++++++++++++
 .../fixtures/openstack_v1.1/_floating_ip.json   |   1 +
 .../openstack_v1.1/_floating_ip_pools.json      |   1 +
 .../fixtures/openstack_v1.1/_floating_ips.json  |   1 +
 libcloud/test/compute/test_openstack.py         | 106 ++++++++++++-
 5 files changed, 258 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/8ba5b81c/libcloud/compute/drivers/openstack.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/openstack.py 
b/libcloud/compute/drivers/openstack.py
index 963a280..7d2a889 100644
--- a/libcloud/compute/drivers/openstack.py
+++ b/libcloud/compute/drivers/openstack.py
@@ -51,6 +51,8 @@ __all__ = [
     'OpenStack_1_1_Response',
     'OpenStack_1_1_Connection',
     'OpenStack_1_1_NodeDriver',
+    'OpenStack_1_1_FloatingIpPool',
+    'OpenStack_1_1_FloatingIpAddress',
     'OpenStackNodeDriver'
 ]
 
@@ -1805,3 +1807,151 @@ class OpenStack_1_1_NodeDriver(OpenStackNodeDriver):
         """
         resp = self._node_action(node, 'unrescue')
         return resp.status == httplib.ACCEPTED
+
+    def _to_floating_ip_pools(self, obj):
+        pool_elements = obj['floating_ip_pools']
+        return [self._to_floating_ip_pool(pool) for pool in pool_elements]
+
+    def _to_floating_ip_pool(self, obj):
+        return OpenStack_1_1_FloatingIpPool(obj['name'], self.connection)
+
+    def ex_list_floating_ip_pools(self):
+        """
+        List available floating IP pools
+
+        @rtype: C{list} of L{OpenStack_1_1_FloatingIpPool}
+        """
+        return self._to_floating_ip_pools(
+            self.connection.request('/os-floating-ip-pools').object)
+
+    def ex_attach_floating_ip_to_node(self, node, ip):
+        """
+        Attach the floating IP to the node
+
+        @param      node: node
+        @type       node: L{Node}
+
+        @param      ip: floating IP to attach
+        @type       ip: C{str} or L{OpenStack_1_1_FloatingIpAddress}
+
+        @rtype: C{bool}
+        """
+        address = ip.ip_address if hasattr(ip, 'ip_address') else ip
+        data = {
+            'addFloatingIp': { 'address': address }
+        }
+        resp = self.connection.request('/servers/%s/action' % node.id,
+            method='POST', data=data)
+        return resp.status == httplib.ACCEPTED
+
+    def ex_detach_floating_ip_from_node(self, node, ip):
+        """
+        Detach the floating IP from the node
+
+        @param      node: node
+        @type       node: L{Node}
+
+        @param      ip: floating IP to remove
+        @type       ip: C{str} or L{OpenStack_1_1_FloatingIpAddress}
+
+        @rtype: C{bool}
+        """
+        address = ip.ip_address if hasattr(ip, 'ip_address') else ip
+        data = {
+            'removeFloatingIp': { 'address': address }
+        }
+        resp = self.connection.request('/servers/%s/action' % node.id,
+            method='POST', data=data)
+        return resp.status == httplib.ACCEPTED
+
+
+class OpenStack_1_1_FloatingIpPool(object):
+    """
+    Floating IP Pool info.
+    """
+
+    def __init__(self, name, connection):
+        self.name = name
+        self.connection = connection
+
+    def list_floating_ips(self):
+        """
+        List floating IPs in the pool
+
+        @rtype: C{list} of L{OpenStack_1_1_FloatingIpAddress}
+        """
+        return self._to_floating_ips(
+            self.connection.request('/os-floating-ips').object)
+
+    def _to_floating_ips(self, obj):
+        ip_elements = obj['floating_ips']
+        return [self._to_floating_ip(ip) for ip in ip_elements]
+
+    def _to_floating_ip(self, obj):
+        return OpenStack_1_1_FloatingIpAddress(obj['id'], obj['ip'], self,
+            obj['instance_id'])
+
+    def get_floating_ip(self, ip):
+        """
+        Get specified floating IP from the pool
+
+        @param      ip: floating IP to remove
+        @type       ip: C{str}
+
+        @rtype: L{OpenStack_1_1_FloatingIpAddress}
+        """
+        ip_obj, = [x for x in self.list_floating_ips() if x.ip_address == ip]
+        return ip_obj
+
+    def create_floating_ip(self):
+        """
+        Create new floating IP in the pool
+
+        @rtype: L{OpenStack_1_1_FloatingIpAddress}
+        """
+        resp = self.connection.request('/os-floating-ips',
+            method='POST', data={ 'pool': self.name })
+        data = resp.object['floating_ip']
+        id = data['id']
+        ip_address = data['ip']
+        return OpenStack_1_1_FloatingIpAddress(id, ip_address, self)
+
+    def delete_floating_ip(self, ip):
+        """
+        Delete specified floating IP from the pool
+
+        @param      ip: floating IP to remove
+        @type       ip:L{OpenStack_1_1_FloatingIpAddress}
+
+        @rtype: C{bool}
+        """
+        resp = self.connection.request('/os-floating-ips/%s' % ip.id,
+            method='DELETE')
+        return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED)
+
+    def __repr__(self):
+        return ('<OpenStack_1_1_FloatingIpPool: name=%s>' % self.name)
+
+
+class OpenStack_1_1_FloatingIpAddress(object):
+    """
+    Floating IP info.
+    """
+
+    def __init__(self, id, ip_address, pool, node_id=None):
+        self.id = str(id)
+        self.ip_address = ip_address
+        self.pool = pool
+        self.node_id = node_id
+
+    def delete(self):
+        """
+        Delete this floating IP
+
+        @rtype: C{bool}
+        """
+        return self.pool.delete_floating_ip(self)
+
+    def __repr__(self):
+        return ('<OpenStack_1_1_FloatingIpAddress: id=%s, ip_addr=%s, pool=%s>'
+            % (self.id, self.ip_address, self.pool))

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8ba5b81c/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ip.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ip.json 
b/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ip.json
new file mode 100644
index 0000000..7825a48
--- /dev/null
+++ b/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ip.json
@@ -0,0 +1 @@
+{"floating_ip": {"instance_id": null, "ip": "10.3.1.42", "fixed_ip": null, 
"id": "09ea1784-2f81-46dc-8c91-244b4df75bde", "pool": "public"}}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8ba5b81c/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ip_pools.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ip_pools.json 
b/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ip_pools.json
new file mode 100644
index 0000000..8961f51
--- /dev/null
+++ b/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ip_pools.json
@@ -0,0 +1 @@
+{"floating_ip_pools": [{"name": "public"}, {"name": "foobar"}]}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8ba5b81c/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ips.json
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ips.json 
b/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ips.json
new file mode 100644
index 0000000..82a0157
--- /dev/null
+++ b/libcloud/test/compute/fixtures/openstack_v1.1/_floating_ips.json
@@ -0,0 +1 @@
+{"floating_ips": [{"instance_id": null, "ip": "10.3.1.42", "fixed_ip": null, 
"id": "09ea1784-2f81-46dc-8c91-244b4df75bde", "pool": "public"}, 
{"instance_id": "fcfc96da-19e2-40fd-8497-f29da1b21143", "ip": "10.3.1.1", 
"fixed_ip": "172.16.21.4", "id": "04c5336a-0629-4694-ba30-04b0bdfa88a4", 
"pool": "public"}]}

http://git-wip-us.apache.org/repos/asf/libcloud/blob/8ba5b81c/libcloud/test/compute/test_openstack.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_openstack.py 
b/libcloud/test/compute/test_openstack.py
index 730dced..3dc4cab 100644
--- a/libcloud/test/compute/test_openstack.py
+++ b/libcloud/test/compute/test_openstack.py
@@ -37,7 +37,9 @@ from libcloud.compute.types import Provider
 from libcloud.compute.providers import get_driver
 from libcloud.compute.drivers.openstack import (
     OpenStack_1_0_NodeDriver, OpenStack_1_0_Response,
-    OpenStack_1_1_NodeDriver, OpenStackSecurityGroup, 
OpenStackSecurityGroupRule
+    OpenStack_1_1_NodeDriver, OpenStackSecurityGroup,
+    OpenStackSecurityGroupRule, OpenStack_1_1_FloatingIpPool,
+    OpenStack_1_1_FloatingIpAddress
 )
 from libcloud.compute.base import Node, NodeImage, NodeSize, StorageVolume
 from libcloud.pricing import set_pricing, clear_pricing_data
@@ -1140,6 +1142,75 @@ class OpenStack_1_1_Tests(unittest.TestCase, 
TestCaseMixin):
         result = self.driver.ex_delete_security_group_rule(security_group_rule)
         self.assertTrue(result)
 
+    def test_ex_list_floating_ip_pools(self):
+        ret = self.driver.ex_list_floating_ip_pools()
+        self.assertEqual(ret[0].name, 'public')
+        self.assertEqual(ret[1].name, 'foobar')
+
+    def test_ex_attach_floating_ip_to_node(self):
+        image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', 
driver=self.driver)
+        size = NodeSize(1, '256 slice', None, None, None, None, 
driver=self.driver)
+        node = self.driver.create_node(name='racktest', image=image, size=size)
+        node.id = 4242
+        ip = '42.42.42.42'
+
+        self.assertTrue(self.driver.ex_attach_floating_ip_to_node(node, ip))
+
+    def test_detach_floating_ip_from_node(self):
+        image = NodeImage(id=11, name='Ubuntu 8.10 (intrepid)', 
driver=self.driver)
+        size = NodeSize(1, '256 slice', None, None, None, None, 
driver=self.driver)
+        node = self.driver.create_node(name='racktest', image=image, size=size)
+        node.id = 4242
+        ip = '42.42.42.42'
+
+        self.assertTrue(self.driver.ex_detach_floating_ip_from_node(node, ip))
+
+    def test_OpenStack_1_1_FloatingIpPool_list_floating_ips(self):
+        pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection)
+        ret = pool.list_floating_ips()
+
+        self.assertEqual(ret[0].id, '09ea1784-2f81-46dc-8c91-244b4df75bde')
+        self.assertEqual(ret[0].pool, pool)
+        self.assertEqual(ret[0].ip_address, '10.3.1.42')
+        self.assertEqual(ret[0].node_id, None)
+        self.assertEqual(ret[1].id, '04c5336a-0629-4694-ba30-04b0bdfa88a4')
+        self.assertEqual(ret[1].pool, pool)
+        self.assertEqual(ret[1].ip_address, '10.3.1.1')
+        self.assertEqual(ret[1].node_id, 
'fcfc96da-19e2-40fd-8497-f29da1b21143')
+
+    def test_OpenStack_1_1_FloatingIpPool_get_floating_ip(self):
+        pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection)
+        ret = pool.get_floating_ip('10.3.1.42')
+
+        self.assertEqual(ret.id, '09ea1784-2f81-46dc-8c91-244b4df75bde')
+        self.assertEqual(ret.pool, pool)
+        self.assertEqual(ret.ip_address, '10.3.1.42')
+        self.assertEqual(ret.node_id, None)
+
+    def test_OpenStack_1_1_FloatingIpPool_create_floating_ip(self):
+        pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection)
+        ret = pool.create_floating_ip()
+
+        self.assertEqual(ret.id, '09ea1784-2f81-46dc-8c91-244b4df75bde')
+        self.assertEqual(ret.pool, pool)
+        self.assertEqual(ret.ip_address, '10.3.1.42')
+        self.assertEqual(ret.node_id, None)
+
+    def test_OpenStack_1_1_FloatingIpPool_delete_floating_ip(self):
+        pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection)
+        ip = OpenStack_1_1_FloatingIpAddress('foo-bar-id', '42.42.42.42', pool)
+
+        self.assertTrue(pool.delete_floating_ip(ip))
+
+    def test_OpenStack_1_1_FloatingIpAddress_delete(self):
+        pool = OpenStack_1_1_FloatingIpPool('foo', self.driver.connection)
+        pool.delete_floating_ip = Mock()
+        ip = OpenStack_1_1_FloatingIpAddress('foo-bar-id', '42.42.42.42', pool)
+
+        ip.pool.delete_floating_ip()
+
+        self.assertEqual(pool.delete_floating_ip.call_count, 1)
+
 
 class OpenStack_1_1_FactoryMethodTests(OpenStack_1_1_Tests):
     should_list_locations = False
@@ -1369,6 +1440,39 @@ class OpenStack_1_1_MockHttp(MockHttpTestCase):
 
         return (httplib.OK, body, self.json_content_headers, 
httplib.responses[httplib.OK])
 
+    def _v1_1_slug_os_floating_ip_pools(self, method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load('_floating_ip_pools.json')
+            return (httplib.OK, body, self.json_content_headers, 
httplib.responses[httplib.OK])
+        else:
+            raise NotImplementedError()
+
+    def _v1_1_slug_os_floating_ips_foo_bar_id(self, method, url, body, 
headers):
+        if method == "DELETE":
+            body = ''
+            return (httplib.ACCEPTED, body, self.json_content_headers, 
httplib.responses[httplib.OK])
+        else:
+            raise NotImplementedError()
+
+    def _v1_1_slug_os_floating_ips(self, method, url, body, headers):
+        if method == "GET":
+            body = self.fixtures.load('_floating_ips.json')
+            return (httplib.OK, body, self.json_content_headers, 
httplib.responses[httplib.OK])
+        elif method == "POST":
+            body = self.fixtures.load('_floating_ip.json')
+            return (httplib.OK, body, self.json_content_headers, 
httplib.responses[httplib.OK])
+        else:
+            raise NotImplementedError()
+
+    def _v1_1_slug_servers_4242_action(self, method, url, body, headers):
+        if method == "POST":
+            body = ''
+            return (httplib.ACCEPTED, body, self.json_content_headers, 
httplib.responses[httplib.OK])
+        else:
+            raise NotImplementedError()
+
+        return (httplib.OK, body, self.json_content_headers, 
httplib.responses[httplib.OK])
+
 
 # This exists because the nova compute url in devstack has v2 in there but the 
v1.1 fixtures
 # work fine.

Reply via email to