From c760d4f26f4b4753c80269437d2cd0b8f63dbc7c Mon Sep 17 00:00:00 2001 From: Hongbin Lu Date: Fri, 12 Jan 2018 23:52:26 +0000 Subject: [PATCH] Add port_details to Floating IP If the floating IP is associated with a port, this attribute will contain detailed information of the associated port. This allows users to retrieve the floating IPs with information of its associated port (if any) in one API call. Other related patches: * neutron-lib: https://review.openstack.org/#/c/534882/ * osc: https://review.openstack.org/#/c/533809/ * sdk: https://review.openstack.org/#/c/533811/ * tempest-plugin: https://review.openstack.org/#/c/561710/ APIImpact the API reference needs to be updated Change-Id: I31e940d2986278d2fbee6fdfea4ff15f7c07ebaa Partial-Bug: #1723026 --- neutron/db/l3_fip_port_details.py | 42 ++++++ neutron/db/models/l3.py | 3 + neutron/extensions/_fip_port_details_lib.py | 43 ++++++ neutron/extensions/fip_port_details.py | 21 +++ .../services/l3_router/l3_router_plugin.py | 7 +- .../tests/contrib/hooks/api_all_extensions | 1 + .../unit/extensions/test_fip_port_details.py | 136 ++++++++++++++++++ ...etails-to-floatingip-fefceab2c740e482.yaml | 6 + 8 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 neutron/db/l3_fip_port_details.py create mode 100644 neutron/extensions/_fip_port_details_lib.py create mode 100644 neutron/extensions/fip_port_details.py create mode 100644 neutron/tests/unit/extensions/test_fip_port_details.py create mode 100644 releasenotes/notes/add-port_details-to-floatingip-fefceab2c740e482.yaml diff --git a/neutron/db/l3_fip_port_details.py b/neutron/db/l3_fip_port_details.py new file mode 100644 index 00000000000..4352544617c --- /dev/null +++ b/neutron/db/l3_fip_port_details.py @@ -0,0 +1,42 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutron_lib.api.definitions import l3 as l3_apidef + +from neutron.db import _resource_extend as resource_extend + + +def _make_port_details_dict(port): + return {'name': port['name'], + 'network_id': port['network_id'], + 'mac_address': port['mac_address'], + 'admin_state_up': port['admin_state_up'], + 'status': port['status'], + 'device_id': port['device_id'], + 'device_owner': port['device_owner']} + + +@resource_extend.has_resource_extenders +class Fip_port_details_db_mixin(object): + """Mixin class to enable floating IP's port_details attributes.""" + + @staticmethod + @resource_extend.extends([l3_apidef.FLOATINGIPS]) + def _extend_fip_dict_device_id(fip_res, fip_db): + if fip_db.fixed_port: + fip_res['port_details'] = _make_port_details_dict( + fip_db.fixed_port) + else: + fip_res['port_details'] = None + return fip_res diff --git a/neutron/db/models/l3.py b/neutron/db/models/l3.py index 3e1289505e5..a6ffff544d9 100644 --- a/neutron/db/models/l3.py +++ b/neutron/db/models/l3.py @@ -90,6 +90,9 @@ class FloatingIP(standard_attr.HasStandardAttributes, model_base.BASEV2, cascade='all,delete-orphan'), foreign_keys='FloatingIP.floating_port_id') fixed_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id')) + fixed_port = orm.relationship(models_v2.Port, + foreign_keys='FloatingIP.fixed_port_id', + lazy='joined') fixed_ip_address = sa.Column(sa.String(64)) router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id')) # Additional attribute for keeping track of the router where the floating diff --git a/neutron/extensions/_fip_port_details_lib.py b/neutron/extensions/_fip_port_details_lib.py new file mode 100644 index 00000000000..a08661d9343 --- /dev/null +++ b/neutron/extensions/_fip_port_details_lib.py @@ -0,0 +1,43 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +TODO(hongbin): This module should be deleted once neutron-lib containing +https://review.openstack.org/#/c/534882/ change is released. +""" + +from neutron_lib.api.definitions import l3 +from neutron_lib import constants + + +PORT_DETAILS = 'port_details' + +ALIAS = 'fip-port-details' +IS_SHIM_EXTENSION = False +IS_STANDARD_ATTR_EXTENSION = False +NAME = 'Floating IP Port Details Extension' +DESCRIPTION = 'Add port_details attribute to Floating IP resource' +UPDATED_TIMESTAMP = '2018-04-09T10:00:00-00:00' +RESOURCE_ATTRIBUTE_MAP = { + l3.FLOATINGIPS: { + PORT_DETAILS: { + 'allow_post': False, 'allow_put': False, + 'default': constants.ATTR_NOT_SPECIFIED, + 'is_visible': True + } + } +} +SUB_RESOURCE_ATTRIBUTE_MAP = {} +ACTION_MAP = {} +REQUIRED_EXTENSIONS = [l3.ALIAS] +OPTIONAL_EXTENSIONS = [] +ACTION_STATUS = {} diff --git a/neutron/extensions/fip_port_details.py b/neutron/extensions/fip_port_details.py new file mode 100644 index 00000000000..4a3123c0879 --- /dev/null +++ b/neutron/extensions/fip_port_details.py @@ -0,0 +1,21 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutron_lib.api import extensions + +from neutron.extensions import _fip_port_details_lib as apidef + + +class Fip_port_details(extensions.APIExtensionDescriptor): + """Extension class adding port_details to Floating IP.""" + + api_definition = apidef diff --git a/neutron/services/l3_router/l3_router_plugin.py b/neutron/services/l3_router/l3_router_plugin.py index 524bb566fd8..6cd57807835 100644 --- a/neutron/services/l3_router/l3_router_plugin.py +++ b/neutron/services/l3_router/l3_router_plugin.py @@ -32,6 +32,7 @@ from neutron.db import dns_db from neutron.db import extraroute_db from neutron.db import l3_dvr_ha_scheduler_db from neutron.db import l3_dvrscheduler_db +from neutron.db import l3_fip_port_details from neutron.db import l3_fip_qos from neutron.db import l3_gwmode_db from neutron.db import l3_hamode_db @@ -67,7 +68,8 @@ class L3RouterPlugin(service_base.ServicePluginBase, l3_gwmode_db.L3_NAT_db_mixin, l3_dvr_ha_scheduler_db.L3_DVR_HA_scheduler_db_mixin, dns_db.DNSDbMixin, - l3_fip_qos.FloatingQoSDbMixin): + l3_fip_qos.FloatingQoSDbMixin, + l3_fip_port_details.Fip_port_details_db_mixin): """Implementation of the Neutron L3 Router Service Plugin. @@ -81,7 +83,8 @@ class L3RouterPlugin(service_base.ServicePluginBase, _supported_extension_aliases = ["dvr", "router", "ext-gw-mode", "extraroute", "l3_agent_scheduler", "l3-ha", "router_availability_zone", - "l3-flavors", "qos-fip"] + "l3-flavors", "qos-fip", + "fip-port-details"] __native_pagination_support = True __native_sorting_support = True diff --git a/neutron/tests/contrib/hooks/api_all_extensions b/neutron/tests/contrib/hooks/api_all_extensions index 47c19becf34..99832fff816 100644 --- a/neutron/tests/contrib/hooks/api_all_extensions +++ b/neutron/tests/contrib/hooks/api_all_extensions @@ -14,6 +14,7 @@ NETWORK_API_EXTENSIONS+=",ext-gw-mode" NETWORK_API_EXTENSIONS+=",external-net" NETWORK_API_EXTENSIONS+=",extra_dhcp_opt" NETWORK_API_EXTENSIONS+=",extraroute" +NETWORK_API_EXTENSIONS+=",fip-port-details" NETWORK_API_EXTENSIONS+=",flavors" NETWORK_API_EXTENSIONS+=",ip-substring-filtering" NETWORK_API_EXTENSIONS+=",l3-flavors" diff --git a/neutron/tests/unit/extensions/test_fip_port_details.py b/neutron/tests/unit/extensions/test_fip_port_details.py new file mode 100644 index 00000000000..06102100db8 --- /dev/null +++ b/neutron/tests/unit/extensions/test_fip_port_details.py @@ -0,0 +1,136 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from oslo_config import cfg + +from neutron.db import l3_fip_port_details +from neutron.extensions import _fip_port_details_lib as apiref +from neutron.extensions import l3 +from neutron.tests.unit.extensions import test_l3 + + +class FloatingIPPortDetailsTestExtensionManager(object): + + def get_resources(self): + return l3.L3.get_resources() + + def get_actions(self): + return [] + + def get_request_extensions(self): + return [] + + +class TestFloatingIPPortDetailsIntPlugin( + test_l3.TestL3NatIntPlugin, + l3_fip_port_details.Fip_port_details_db_mixin): + supported_extension_aliases = ["external-net", "router", + apiref.ALIAS] + + +class TestFloatingIPPortDetailsL3NatServicePlugin( + test_l3.TestL3NatServicePlugin, + l3_fip_port_details.Fip_port_details_db_mixin): + supported_extension_aliases = ["router", apiref.ALIAS] + + +class FloatingIPPortDetailsDBTestCaseBase(test_l3.L3NatTestCaseMixin): + + def _assert_port_details(self, port, port_details): + port['name'] = port_details['name'] + port['network_id'] = port_details['network_id'] + port['mac_address'] = port_details['mac_address'] + port['admin_state_up'] = port_details['admin_state_up'] + port['status'] = port_details['status'] + port['device_id'] = port_details['device_id'] + port['device_owner'] = port_details['device_owner'] + + def test_floatingip_create_with_port_details(self): + with self.port() as p: + with self.floatingip_with_assoc(port_id=p['port']['id']) as fip: + body = self._show('floatingips', fip['floatingip']['id']) + self.assertEqual(body['floatingip']['id'], + fip['floatingip']['id']) + self.assertEqual(body['floatingip']['port_id'], + fip['floatingip']['port_id']) + self._assert_port_details( + p['port'], body['floatingip']['port_details']) + + def test_floatingip_update_with_port_details(self): + with self.port() as p: + private_sub = {'subnet': {'id': + p['port']['fixed_ips'][0]['subnet_id']}} + with self.floatingip_no_assoc(private_sub) as fip: + body = self._show('floatingips', fip['floatingip']['id']) + self.assertIsNone(body['floatingip']['port_id']) + self.assertIsNone(body['floatingip']['port_details']) + + port_id = p['port']['id'] + body = self._update('floatingips', fip['floatingip']['id'], + {'floatingip': {'port_id': port_id}}) + self.assertEqual(port_id, body['floatingip']['port_id']) + self._assert_port_details( + p['port'], body['floatingip']['port_details']) + + def test_floatingip_list_with_port_details(self): + with self.port() as p: + with self.floatingip_with_assoc(port_id=p['port']['id']) as fip: + body = self._list('floatingips') + self.assertEqual(body['floatingips'][0]['id'], + fip['floatingip']['id']) + self.assertEqual(body['floatingips'][0]['port_id'], + fip['floatingip']['port_id']) + self._assert_port_details( + p['port'], body['floatingips'][0]['port_details']) + + +class FloatingIPPortDetailsDBIntTestCase(test_l3.L3BaseForIntTests, + FloatingIPPortDetailsDBTestCaseBase): + + def setUp(self, plugin=None): + if not plugin: + plugin = ('neutron.tests.unit.extensions.test_fip_port_details.' + 'TestFloatingIPPortDetailsIntPlugin') + # for these tests we need to enable overlapping ips + cfg.CONF.set_default('allow_overlapping_ips', True) + cfg.CONF.set_default('max_routes', 3) + ext_mgr = FloatingIPPortDetailsTestExtensionManager() + super(test_l3.L3BaseForIntTests, self).setUp( + plugin=plugin, + ext_mgr=ext_mgr) + + self.setup_notification_driver() + + +class FloatingIPPortDetailsDBSepTestCase(test_l3.L3BaseForSepTests, + FloatingIPPortDetailsDBTestCaseBase): + + def setUp(self): + # the plugin without L3 support + plugin = 'neutron.tests.unit.extensions.test_l3.TestNoL3NatPlugin' + # the L3 service plugin + l3_plugin = ('neutron.tests.unit.extensions.test_fip_port_details.' + 'TestFloatingIPPortDetailsL3NatServicePlugin') + service_plugins = {'l3_plugin_name': l3_plugin} + + # for these tests we need to enable overlapping ips + cfg.CONF.set_default('allow_overlapping_ips', True) + cfg.CONF.set_default('max_routes', 3) + ext_mgr = FloatingIPPortDetailsTestExtensionManager() + super(test_l3.L3BaseForSepTests, self).setUp( + plugin=plugin, + ext_mgr=ext_mgr, + service_plugins=service_plugins) + + self.setup_notification_driver() diff --git a/releasenotes/notes/add-port_details-to-floatingip-fefceab2c740e482.yaml b/releasenotes/notes/add-port_details-to-floatingip-fefceab2c740e482.yaml new file mode 100644 index 00000000000..a902d8329bb --- /dev/null +++ b/releasenotes/notes/add-port_details-to-floatingip-fefceab2c740e482.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add attribute ``port_details`` to floating IP. The value of this attribute + contains information of the associated port. +