Move floatingips to neutron api LIBCLOUD-874
Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/84aa986f Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/84aa986f Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/84aa986f Branch: refs/heads/trunk Commit: 84aa986ff8c21db2e547b22c117cbff3795204dd Parents: 2b2e1f1 Author: micafer <[email protected]> Authored: Tue Sep 25 15:35:28 2018 +0200 Committer: Rick van de Loo <[email protected]> Committed: Tue Dec 4 09:45:48 2018 +0100 ---------------------------------------------------------------------- libcloud/common/openstack.py | 5 +- libcloud/compute/drivers/openstack.py | 122 ++++++++++++++++++- .../openstack_v1.1/_v2_0__floatingip.json | 23 ++++ .../openstack_v1.1/_v2_0__floatingips.json | 52 ++++++++ .../openstack_v1.1/_v2_0__networks_public.json | 64 ++++++++++ libcloud/test/compute/test_openstack.py | 70 ++++++++++- 6 files changed, 329 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/libcloud/blob/84aa986f/libcloud/common/openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/common/openstack.py b/libcloud/common/openstack.py index b64f669..db9f43f 100644 --- a/libcloud/common/openstack.py +++ b/libcloud/common/openstack.py @@ -390,8 +390,9 @@ class OpenStackResponse(Response): context = self.connection.context driver = self.connection.driver key_pair_name = context.get('key_pair_name', None) - - if len(values) > 0 and values[0]['code'] == 404 and key_pair_name: + print(values) + if len(values) > 0 and 'code' in values[0] and \ + values[0]['code'] == 404 and key_pair_name: raise KeyPairDoesNotExistError(name=key_pair_name, driver=driver) elif len(values) > 0 and 'message' in values[0]: http://git-wip-us.apache.org/repos/asf/libcloud/blob/84aa986f/libcloud/compute/drivers/openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/compute/drivers/openstack.py b/libcloud/compute/drivers/openstack.py index de6d445..fb92dab 100644 --- a/libcloud/compute/drivers/openstack.py +++ b/libcloud/compute/drivers/openstack.py @@ -59,6 +59,7 @@ __all__ = [ 'OpenStack_1_1_Connection', 'OpenStack_1_1_NodeDriver', 'OpenStack_1_1_FloatingIpPool', + 'OpenStack_2_FloatingIpPool', 'OpenStack_1_1_FloatingIpAddress', 'OpenStack_2_PortInterfaceState', 'OpenStack_2_PortInterface', @@ -3255,10 +3256,29 @@ class OpenStack_2_NodeDriver(OpenStack_1_1_NodeDriver): :rtype: ``bool`` """ - resp = self.connection.request('/v2.0/security-group-rules/%s' % - (rule.id), method='DELETE') + resp = self.network_connection.request( + '/v2.0/security-group-rules/%s' % (rule.id), method='DELETE') return resp.status == httplib.NO_CONTENT + def _to_floating_ip_pool(self, obj): + return OpenStack_2_FloatingIpPool(obj['id'], obj['name'], + self.network_connection) + + def _to_floating_ip_pools(self, obj): + pool_elements = obj['networks'] + return [self._to_floating_ip_pool(pool) for pool in pool_elements] + + def ex_list_floating_ip_pools(self): + """ + List available floating IP pools + + :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpPool` + """ + return self._to_floating_ip_pools( + self.network_connection.request('/v2.0/networks?router:external' + '=True&fields=id&fields=' + 'name').object) + class OpenStack_1_1_FloatingIpPool(object): """ @@ -3365,6 +3385,104 @@ class OpenStack_1_1_FloatingIpAddress(object): % (self.id, self.ip_address, self.pool, self.driver)) +class OpenStack_2_FloatingIpPool(OpenStack_1_1_FloatingIpPool): + """ + Floating IP Pool info. + """ + + def __init__(self, id, name, connection): + self.id = id + self.name = name + self.connection = connection + + def _to_floating_ips(self, obj): + ip_elements = obj['floatingips'] + return [self._to_floating_ip(ip) for ip in ip_elements] + + def _to_floating_ip(self, obj): + instance_id = None + + # In neutron version prior to 13.0.0 port_details does not exists + if 'port_details' not in obj and 'port_id' in obj: + port = self.connection.driver.ex_get_port(obj['port_id']) + if port: + obj['port_details'] = {"device_id": port.extra["device_id"], + "device_owner": + port.extra["device_owner"], + "mac_address": + port.extra["mac_address"]} + + if 'port_details' in obj and obj['port_details']: + if obj['port_details']['device_owner'] == 'compute:nova': + instance_id = obj['port_details']['device_id'] + + ip_address = obj['floating_ip_address'] + return OpenStack_1_1_FloatingIpAddress(id=obj['id'], + ip_address=ip_address, + pool=self, + node_id=instance_id, + driver=self.connection.driver) + + def list_floating_ips(self): + """ + List floating IPs in the pool + + :rtype: ``list`` of :class:`OpenStack_1_1_FloatingIpAddress` + """ + return self._to_floating_ips( + self.connection.request('/v2.0/floatingips').object) + + def get_floating_ip(self, ip): + """ + Get specified floating IP from the pool + + :param ip: floating IP to get + :type ip: ``str`` + + :rtype: :class:`OpenStack_1_1_FloatingIpAddress` + """ + floating_ips = self._to_floating_ips( + self.connection.request('/v2.0/floatingips?floating_ip_address' + '=%s' % ip).object) + return floating_ips[0] + + def create_floating_ip(self): + """ + Create new floating IP in the pool + + :rtype: :class:`OpenStack_1_1_FloatingIpAddress` + """ + resp = self.connection.request('/v2.0/floatingips', + method='POST', + data={'floatingip': + {'floating_network_id': self.id}} + ) + data = resp.object['floatingip'] + id = data['id'] + ip_address = data['floating_ip_address'] + return OpenStack_1_1_FloatingIpAddress(id=id, + ip_address=ip_address, + pool=self, + node_id=None, + driver=self.connection.driver) + + def delete_floating_ip(self, ip): + """ + Delete specified floating IP from the pool + + :param ip: floating IP to remove + :type ip: :class:`OpenStack_1_1_FloatingIpAddress` + + :rtype: ``bool`` + """ + resp = self.connection.request('/v2.0/floatingips/%s' % ip.id, + method='DELETE') + return resp.status in (httplib.NO_CONTENT, httplib.ACCEPTED) + + def __repr__(self): + return ('<OpenStack_2_FloatingIpPool: name=%s>' % self.name) + + class OpenStack_2_SubNet(object): """ A Virtual SubNet. http://git-wip-us.apache.org/repos/asf/libcloud/blob/84aa986f/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__floatingip.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__floatingip.json b/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__floatingip.json new file mode 100644 index 0000000..0ef2ef5 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__floatingip.json @@ -0,0 +1,23 @@ +{ + "floatingip": + { + "router_id": null, + "description": "for test", + "dns_domain": "my-domain.org.", + "dns_name": "myfip2", + "created_at": "2016-12-21T11:55:50Z", + "updated_at": "2016-12-21T11:55:53Z", + "revision_number": 2, + "project_id": "4969c491a3c74ee4af974e6d800c62de", + "tenant_id": "4969c491a3c74ee4af974e6d800c62de", + "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", + "fixed_ip_address": null, + "floating_ip_address": "10.3.1.42", + "port_id": null, + "id": "09ea1784-2f81-46dc-8c91-244b4df75bde", + "status": "DOWN", + "port_details": null, + "tags": ["tag1,tag2"], + "port_forwardings": [] + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/84aa986f/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__floatingips.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__floatingips.json b/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__floatingips.json new file mode 100644 index 0000000..fa71752 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__floatingips.json @@ -0,0 +1,52 @@ +{ + "floatingips": [ + { + "router_id": null, + "description": "for test", + "dns_domain": "my-domain.org.", + "dns_name": "myfip2", + "created_at": "2016-12-21T11:55:50Z", + "updated_at": "2016-12-21T11:55:53Z", + "revision_number": 2, + "project_id": "4969c491a3c74ee4af974e6d800c62de", + "tenant_id": "4969c491a3c74ee4af974e6d800c62de", + "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", + "fixed_ip_address": null, + "floating_ip_address": "10.3.1.42", + "port_id": null, + "id": "09ea1784-2f81-46dc-8c91-244b4df75bde", + "status": "DOWN", + "port_details": null, + "tags": ["tag1,tag2"], + "port_forwardings": [] + }, + { + "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f", + "description": "for test", + "dns_domain": "my-domain.org.", + "dns_name": "myfip", + "created_at": "2016-12-21T10:55:50Z", + "updated_at": "2016-12-21T10:55:53Z", + "revision_number": 1, + "project_id": "4969c491a3c74ee4af974e6d800c62de", + "tenant_id": "4969c491a3c74ee4af974e6d800c62de", + "floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57", + "fixed_ip_address": "10.0.0.3", + "floating_ip_address": "10.3.1.1", + "port_id": "ce705c24-c1ef-408a-bda3-7bbd946164ab", + "id": "04c5336a-0629-4694-ba30-04b0bdfa88a4", + "status": "ACTIVE", + "port_details": { + "status": "ACTIVE", + "name": "", + "admin_state_up": true, + "network_id": "02dd8479-ef26-4398-a102-d19d0a7b3a1f", + "device_owner": "compute:nova", + "mac_address": "fa:16:3e:b1:3b:30", + "device_id": "fcfc96da-19e2-40fd-8497-f29da1b21143" + }, + "tags": ["tag1,tag2"], + "port_forwardings": [] + } + ] +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/libcloud/blob/84aa986f/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__networks_public.json ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__networks_public.json b/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__networks_public.json new file mode 100644 index 0000000..4ac5688 --- /dev/null +++ b/libcloud/test/compute/fixtures/openstack_v1.1/_v2_0__networks_public.json @@ -0,0 +1,64 @@ +{ + "networks": [ + { + "admin_state_up": true, + "availability_zone_hints": [], + "availability_zones": [ + "nova" + ], + "created_at": "2016-03-08T20:19:41", + "dns_domain": "my-domain.org.", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ipv4_address_scope": null, + "ipv6_address_scope": null, + "l2_adjacency": false, + "mtu": 1500, + "name": "public", + "port_security_enabled": true, + "project_id": "4fd44f30292945e481c7b8a0c8908869", + "qos_policy_id": "6a8454ade84346f59e8d40665f878b2e", + "revision_number": 1, + "router:external": true, + "shared": false, + "status": "ACTIVE", + "subnets": [ + "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + ], + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "updated_at": "2016-03-08T20:19:41", + "vlan_transparent": true, + "description": "", + "is_default": false + }, + { + "admin_state_up": true, + "availability_zone_hints": [], + "availability_zones": [ + "nova" + ], + "created_at": "2016-03-08T20:19:41", + "dns_domain": "my-domain.org.", + "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22", + "ipv4_address_scope": null, + "ipv6_address_scope": null, + "l2_adjacency": false, + "mtu": 1500, + "name": "foobar", + "port_security_enabled": true, + "project_id": "4fd44f30292945e481c7b8a0c8908869", + "qos_policy_id": "6a8454ade84346f59e8d40665f878b2e", + "revision_number": 1, + "router:external": true, + "shared": false, + "status": "ACTIVE", + "subnets": [ + "54d6f61d-db07-451c-9ab3-b9609b6b6f0b" + ], + "tenant_id": "4fd44f30292945e481c7b8a0c8908869", + "updated_at": "2016-03-08T20:19:41", + "vlan_transparent": true, + "description": "", + "is_default": false + } + ] +} http://git-wip-us.apache.org/repos/asf/libcloud/blob/84aa986f/libcloud/test/compute/test_openstack.py ---------------------------------------------------------------------- diff --git a/libcloud/test/compute/test_openstack.py b/libcloud/test/compute/test_openstack.py index 3faea2b..db7b357 100644 --- a/libcloud/test/compute/test_openstack.py +++ b/libcloud/test/compute/test_openstack.py @@ -45,7 +45,7 @@ from libcloud.compute.drivers.openstack import ( OpenStack_1_1_NodeDriver, OpenStackSecurityGroup, OpenStackSecurityGroupRule, OpenStack_1_1_FloatingIpPool, OpenStack_1_1_FloatingIpAddress, OpenStackKeyPair, - OpenStack_1_0_Connection, + OpenStack_1_0_Connection, OpenStack_2_FloatingIpPool, OpenStackNodeDriver, OpenStack_2_NodeDriver, OpenStack_2_PortInterfaceState, OpenStackNetwork) from libcloud.compute.base import Node, NodeImage, NodeSize @@ -1399,6 +1399,53 @@ class OpenStack_1_1_Tests(unittest.TestCase, TestCaseMixin): self.assertEqual(pool.delete_floating_ip.call_count, 1) + def test_OpenStack_2_FloatingIpPool_list_floating_ips(self): + pool = OpenStack_2_FloatingIpPool(1, '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_2_FloatingIpPool_get_floating_ip(self): + pool = OpenStack_2_FloatingIpPool(1, '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_2_FloatingIpPool_create_floating_ip(self): + pool = OpenStack_2_FloatingIpPool(1, '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_2_FloatingIpPool_delete_floating_ip(self): + pool = OpenStack_2_FloatingIpPool(1, '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_2_FloatingIpAddress_delete(self): + pool = OpenStack_2_FloatingIpPool(1, '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) + def test_ex_get_metadata_for_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) @@ -2374,8 +2421,12 @@ class OpenStack_1_1_MockHttp(MockHttp, unittest.TestCase): def _v2_1337_v2_0_networks(self, method, url, body, headers): if method == 'GET': - body = self.fixtures.load('_v2_0__networks.json') - return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + if "router:external=True" in url: + body = self.fixtures.load('_v2_0__networks_public.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + else: + body = self.fixtures.load('_v2_0__networks.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) elif method == 'POST': body = self.fixtures.load('_v2_0__networks_POST.json') return (httplib.ACCEPTED, body, self.json_content_headers, httplib.responses[httplib.OK]) @@ -2453,6 +2504,19 @@ class OpenStack_1_1_MockHttp(MockHttp, unittest.TestCase): body = '' return (httplib.NO_CONTENT, body, self.json_content_headers, httplib.responses[httplib.OK]) + def _v2_1337_v2_0_floatingips(self, method, url, body, headers): + if method == 'POST': + body = self.fixtures.load('_v2_0__floatingip.json') + return (httplib.CREATED, body, self.json_content_headers, httplib.responses[httplib.OK]) + if method == 'GET': + body = self.fixtures.load('_v2_0__floatingips.json') + return (httplib.OK, body, self.json_content_headers, httplib.responses[httplib.OK]) + + def _v2_1337_v2_0_floatingips_foo_bar_id(self, method, url, body, headers): + if method == 'DELETE': + body = '' + return (httplib.NO_CONTENT, 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.
