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. +