diff --git a/devstack/nsx_v/devstackgaterc b/devstack/nsx_v/devstackgaterc index e7706cce25..2b22085fb1 100644 --- a/devstack/nsx_v/devstackgaterc +++ b/devstack/nsx_v/devstackgaterc @@ -22,6 +22,7 @@ r="^(?!.*" r="$r(?:tempest\.api\.network\.test_ports\.PortsTestJSON\.test_create_update_port_with_second_ip.*)" +r="$r|(?:tempest\.api\.network\.test_floating_ips\.FloatingIPTestJSON\.test_create_update_floatingip_with_port_multiple_ip_address.*)" # End list of exclusions. r="$r)" diff --git a/devstack/nsx_v3/devstackgaterc b/devstack/nsx_v3/devstackgaterc index c98866bc70..ad4a5e1215 100644 --- a/devstack/nsx_v3/devstackgaterc +++ b/devstack/nsx_v3/devstackgaterc @@ -32,6 +32,7 @@ r="$r|(?:tempest\.api\.network\.test_ports\.PortsTestJSON\.test_create_update_po r="$r|(?:tempest\.api\.network\.test_ports\.PortsTestJSON\.test_update_port_with_security_group_and_extra_attributes.*)" r="$r|(?:tempest\.api\.network\.test_ports\.PortsTestJSON\.test_update_port_with_two_security_groups_and_extra_attributes.*)" r="$r|(?:tempest\.api\.network\.test_extra_dhcp_options\.ExtraDHCPOptionsTestJSON\.test_.*_with_extra_dhcp_options.*)" +r="$r|(?:tempest\.api\.network\.test_floating_ips\.FloatingIPTestJSON\.test_create_update_floatingip_with_port_multiple_ip_address.*)" # End list of exclusions. r="$r)" diff --git a/vmware_nsx/plugins/common/plugin.py b/vmware_nsx/plugins/common/plugin.py index f3109237e8..941ea51dfe 100644 --- a/vmware_nsx/plugins/common/plugin.py +++ b/vmware_nsx/plugins/common/plugin.py @@ -26,9 +26,11 @@ from neutron_lib.api.definitions import address_scope as ext_address_scope from neutron_lib.api.definitions import network as net_def from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import subnet as subnet_def +from neutron_lib.api import validators from neutron_lib import context as n_context from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory +from neutron_lib.utils import net from vmware_nsx._i18n import _ from vmware_nsx.common import exceptions as nsx_exc @@ -254,3 +256,17 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2, self.recalculate_snat_rules_for_router(context, rtr, affected_subnets) + + def _validate_max_ips_per_port(self, fixed_ip_list, device_owner): + """Validate the number of fixed ips on a port + + Do not allow multiple ip addresses on a port since the nsx backend + cannot add multiple static dhcp bindings with the same port + """ + if (device_owner and + net.is_port_trusted({'device_owner': device_owner})): + return + + if validators.is_attr_set(fixed_ip_list) and len(fixed_ip_list) > 1: + msg = _('Exceeded maximum amount of fixed ips per port') + raise n_exc.InvalidInput(error_message=msg) diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index cde221a16a..90d373655b 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -1718,6 +1718,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, port_data = port['port'] dhcp_opts = port_data.get(ext_edo.EXTRADHCPOPTS) self._validate_extra_dhcp_options(dhcp_opts) + self._validate_max_ips_per_port(port_data.get('fixed_ips', []), + port_data.get('device_owner')) with db_api.context_manager.writer.using(context): # First we allocate port in neutron database @@ -1926,6 +1928,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, self._validate_extra_dhcp_options(dhcp_opts) if addr_pair.ADDRESS_PAIRS in attrs: self._validate_address_pairs(attrs, original_port) + self._validate_max_ips_per_port( + port_data.get('fixed_ips', []), + port_data.get('device_owner', original_port['device_owner'])) orig_has_port_security = (cfg.CONF.nsxv.spoofguard_enabled and original_port[psec.PORTSECURITY]) port_ip_change = port_data.get('fixed_ips') is not None diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index fecbea5264..73aec3a1cf 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -2081,6 +2081,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, port_data = port['port'] dhcp_opts = port_data.get(ext_edo.EXTRADHCPOPTS) self._validate_extra_dhcp_options(dhcp_opts) + self._validate_max_ips_per_port(port_data.get('fixed_ips', []), + port_data.get('device_owner')) # TODO(salv-orlando): Undo logical switch creation on failure with db_api.context_manager.writer.using(context): @@ -2504,31 +2506,35 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, with db_api.context_manager.writer.using(context): original_port = super(NsxV3Plugin, self).get_port(context, id) + port_data = port['port'] nsx_lswitch_id, nsx_lport_id = nsx_db.get_nsx_switch_and_port_id( context.session, id) is_external_net = self._network_is_external( context, original_port['network_id']) if is_external_net: - self._assert_on_external_net_with_compute(port['port']) - self._assert_on_external_net_port_with_qos(port['port']) + self._assert_on_external_net_with_compute(port_data) + self._assert_on_external_net_port_with_qos(port_data) - dhcp_opts = port['port'].get(ext_edo.EXTRADHCPOPTS) + dhcp_opts = port_data.get(ext_edo.EXTRADHCPOPTS) self._validate_extra_dhcp_options(dhcp_opts) - device_owner = (port['port']['device_owner'] - if 'device_owner' in port['port'] + device_owner = (port_data['device_owner'] + if 'device_owner' in port_data else original_port.get('device_owner')) self._assert_on_router_port_with_qos( - port['port'], device_owner) + port_data, device_owner) + + self._validate_max_ips_per_port( + port_data.get('fixed_ips', []), device_owner) updated_port = super(NsxV3Plugin, self).update_port(context, id, port) - self._extension_manager.process_update_port(context, port['port'], + self._extension_manager.process_update_port(context, port_data, updated_port) # copy values over - except fixed_ips as # they've already been processed - port['port'].pop('fixed_ips', None) - updated_port.update(port['port']) + port_data.pop('fixed_ips', None) + updated_port.update(port_data) updated_port = self._update_port_preprocess_security( context, port, id, updated_port, validate_port_sec) @@ -2544,7 +2550,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, (port_security, has_ip) = self._determine_port_security_and_has_ip( context, updated_port) self._process_portbindings_create_and_update( - context, port['port'], updated_port) + context, port_data, updated_port) self._extend_nsx_port_dict_binding(context, updated_port) mac_learning_state = updated_port.get(mac_ext.MAC_LEARNING) if mac_learning_state is not None: diff --git a/vmware_nsx/tests/unit/nsx_v/test_plugin.py b/vmware_nsx/tests/unit/nsx_v/test_plugin.py index 470fca8f81..448a3a03dc 100644 --- a/vmware_nsx/tests/unit/nsx_v/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v/test_plugin.py @@ -874,15 +874,7 @@ class TestPortsV2(NsxVPluginV2TestCase, self.skipTest('No DHCP v6 Support yet') def test_create_port_anticipating_allocation(self): - with self.network(shared=True) as network: - with self.subnet(network=network, cidr='10.0.0.0/24', - enable_dhcp=False) as subnet: - fixed_ips = [{'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id'], - 'ip_address': '10.0.0.2'}] - self._create_port(self.fmt, network['network']['id'], - webob.exc.HTTPCreated.code, - fixed_ips=fixed_ips) + self.skipTest('Multiple fixed ips on a port are not supported') def test_update_port_mac_ip(self): with self.subnet(enable_dhcp=False) as subnet: @@ -1209,94 +1201,10 @@ class TestPortsV2(NsxVPluginV2TestCase, self.vnic_type)) def test_range_allocation(self): - with self.subnet(enable_dhcp=False, gateway_ip='10.0.0.3', - cidr='10.0.0.0/29') as subnet: - kwargs = {"fixed_ips": - [{'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id']}]} - net_id = subnet['subnet']['network_id'] - res = self._create_port(self.fmt, net_id=net_id, **kwargs) - port = self.deserialize(self.fmt, res) - ips = port['port']['fixed_ips'] - self.assertEqual(len(ips), 5) - alloc = ['10.0.0.1', '10.0.0.2', '10.0.0.4', '10.0.0.5', - '10.0.0.6'] - for ip in ips: - self.assertIn(ip['ip_address'], alloc) - self.assertEqual(ip['subnet_id'], - subnet['subnet']['id']) - alloc.remove(ip['ip_address']) - self.assertEqual(len(alloc), 0) - self._delete('ports', port['port']['id']) - - with self.subnet(enable_dhcp=False, gateway_ip='11.0.0.6', - cidr='11.0.0.0/29') as subnet: - kwargs = {"fixed_ips": - [{'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet['subnet']['id']}]} - net_id = subnet['subnet']['network_id'] - res = self._create_port(self.fmt, net_id=net_id, **kwargs) - port = self.deserialize(self.fmt, res) - ips = port['port']['fixed_ips'] - self.assertEqual(len(ips), 5) - alloc = ['11.0.0.1', '11.0.0.2', '11.0.0.3', '11.0.0.4', - '11.0.0.5'] - for ip in ips: - self.assertIn(ip['ip_address'], alloc) - self.assertEqual(ip['subnet_id'], - subnet['subnet']['id']) - alloc.remove(ip['ip_address']) - self.assertEqual(len(alloc), 0) - self._delete('ports', port['port']['id']) + self.skipTest('Multiple fixed ips on a port are not supported') def test_requested_subnet_id_v4_and_v6(self): - with self.subnet(enable_dhcp=False) as subnet: - # Get an IPv4 and IPv6 address - tenant_id = subnet['subnet']['tenant_id'] - net_id = subnet['subnet']['network_id'] - res = self._create_subnet( - self.fmt, - tenant_id=tenant_id, - net_id=net_id, - cidr='2607:f0d0:1002:51::/124', - ip_version=6, - gateway_ip=constants.ATTR_NOT_SPECIFIED, - enable_dhcp=False) - subnet2 = self.deserialize(self.fmt, res) - kwargs = {"fixed_ips": - [{'subnet_id': subnet['subnet']['id']}, - {'subnet_id': subnet2['subnet']['id']}]} - res = self._create_port(self.fmt, net_id=net_id, **kwargs) - port3 = self.deserialize(self.fmt, res) - ips = port3['port']['fixed_ips'] - cidr_v4 = subnet['subnet']['cidr'] - cidr_v6 = subnet2['subnet']['cidr'] - self.assertEqual(2, len(ips)) - self._test_requested_port_subnet_ids(ips, - [subnet['subnet']['id'], - subnet2['subnet']['id']]) - self._test_dual_stack_port_ip_addresses_in_subnets(ips, - cidr_v4, - cidr_v6) - res = self._create_port(self.fmt, net_id=net_id) - port4 = self.deserialize(self.fmt, res) - # Check that a v4 and a v6 address are allocated - ips = port4['port']['fixed_ips'] - self.assertEqual(len(ips), 2) - self._test_requested_port_subnet_ids(ips, - [subnet['subnet']['id'], - subnet2['subnet']['id']]) - self._test_dual_stack_port_ip_addresses_in_subnets(ips, - cidr_v4, - cidr_v6) - self._delete('ports', port3['port']['id']) - self._delete('ports', port4['port']['id']) + self.skipTest('Multiple fixed ips on a port are not supported') def test_update_port_update_ip(self): """Test update of port IP. @@ -1496,36 +1404,47 @@ class TestPortsV2(NsxVPluginV2TestCase, old_ip, None, None) - def test_update_port_update_ip_address_only(self): - with self.subnet(enable_dhcp=False) as subnet: - ip_address = '10.0.0.2' - fixed_ip_data = [{'ip_address': ip_address, - 'subnet_id': subnet['subnet']['id']}] - with self.port(subnet=subnet, fixed_ips=fixed_ip_data) as port: - ips = port['port']['fixed_ips'] - self.assertEqual(1, len(ips)) - self.assertEqual(ip_address, ips[0]['ip_address']) - self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id']) - data = {'port': {'fixed_ips': [{'subnet_id': - subnet['subnet']['id'], - 'ip_address': "10.0.0.10"}, - {'ip_address': ip_address}]}} + def test_update_port_add_additional_ip(self): + """Test update of port with additional IP fails.""" + with self.subnet() as subnet: + with self.port(subnet=subnet) as port: + data = {'port': {'admin_state_up': False, + 'fixed_ips': [{'subnet_id': + subnet['subnet']['id']}, + {'subnet_id': + subnet['subnet']['id']}]}} req = self.new_update_request('ports', data, port['port']['id']) - res = self.deserialize(self.fmt, req.get_response(self.api)) - ips = res['port']['fixed_ips'] - self.assertEqual(2, len(ips)) - self.assertIn({'ip_address': ip_address, - 'subnet_id': subnet['subnet']['id']}, ips) - self.assertIn({'ip_address': '10.0.0.10', - 'subnet_id': subnet['subnet']['id']}, ips) + res = req.get_response(self.api) + self.assertEqual(webob.exc.HTTPBadRequest.code, + res.status_int) + + def test_create_port_additional_ip(self): + """Test that creation of port with additional IP fails.""" + with self.subnet() as subnet: + data = {'port': {'network_id': subnet['subnet']['network_id'], + 'tenant_id': subnet['subnet']['tenant_id'], + 'fixed_ips': [{'subnet_id': + subnet['subnet']['id']}, + {'subnet_id': + subnet['subnet']['id']}]}} + port_req = self.new_create_request('ports', data) + res = port_req.get_response(self.api) + self.assertEqual(webob.exc.HTTPBadRequest.code, + res.status_int) + + def test_update_port_update_ip_address_only(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_requested_invalid_fixed_ips(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_requested_subnet_id_v4_and_v6_slaac(self): + self.skipTest('Multiple fixed ips on a port are not supported') def test_update_dhcp_port_with_exceeding_fixed_ips(self): self.skipTest('Updating dhcp port IP is not supported') - def test_requested_subnet_id_v4_and_v6_slaac(self): - self.skipTest('No DHCP v6 Support yet') - def test_create_router_port_ipv4_and_ipv6_slaac_no_fixed_ips(self): self.skipTest('No DHCP v6 Support yet') @@ -2246,6 +2165,21 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxVPluginV2TestCase): def test_router_add_gateway_no_subnet(self): self.skipTest('No support for no subnet gateway set') + def test_floatingip_create_different_fixed_ip_same_port(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_router_add_interface_multiple_ipv4_subnet_port_returns_400(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_router_add_interface_multiple_ipv6_subnet_port(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_floatingip_update_different_fixed_ip_same_port(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_create_multiple_floatingips_same_fixed_ip_same_port(self): + self.skipTest('Multiple fixed ips on a port are not supported') + def _set_net_external(self, net_id): self._update('networks', net_id, {'network': {external_net.EXTERNAL: True}}) @@ -2666,29 +2600,6 @@ class L3NatTestCaseBase(test_l3_plugin.L3NatTestCaseMixin): s2['subnet']['id'], None) - def test_router_add_interface_multiple_ipv6_subnet_port(self): - """A port with multiple IPv6 subnets can be added to a router - - Create a port with multiple associated IPv6 subnets and attach - it to a router. The action should succeed. - """ - with self.network() as n, self.router() as r: - with self.subnet(network=n, cidr='fd00::/64', - ip_version=6, enable_dhcp=False) as s1, ( - self.subnet(network=n, cidr='fd01::/64', - ip_version=6, enable_dhcp=False)) as s2: - fixed_ips = [{'subnet_id': s1['subnet']['id']}, - {'subnet_id': s2['subnet']['id']}] - with self.port(subnet=s1, fixed_ips=fixed_ips) as p: - self._router_interface_action('add', - r['router']['id'], - None, - p['port']['id']) - self._router_interface_action('remove', - r['router']['id'], - None, - p['port']['id']) - def test_router_add_interface_ipv6_subnet_without_gateway_ip(self): with self.router() as r: with self.subnet(ip_version=6, cidr='fe80::/64', diff --git a/vmware_nsx/tests/unit/nsx_v3/test_dhcp_metadata.py b/vmware_nsx/tests/unit/nsx_v3/test_dhcp_metadata.py index 471e92c9f8..f7414ce533 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_dhcp_metadata.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_dhcp_metadata.py @@ -765,49 +765,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxV3PluginTestCaseMixin): context.get_admin_context(), port['port']['id'], data) update_dhcp_binding.assert_not_called() - def test_dhcp_binding_with_multiple_ips(self): - # Test create/update/delete DHCP binding with multiple IPs on a - # compute port. - with mock.patch.object(nsx_resources.LogicalDhcpServer, - 'create_binding', - side_effect=[{"id": uuidutils.generate_uuid()}, - {"id": uuidutils.generate_uuid()}] - ) as create_dhcp_binding: - with mock.patch.object(nsx_resources.LogicalDhcpServer, - 'update_binding' - ) as update_dhcp_binding: - with mock.patch.object(nsx_resources.LogicalDhcpServer, - 'delete_binding' - ) as delete_dhcp_binding: - with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True - ) as subnet: - device_owner = (constants.DEVICE_OWNER_COMPUTE_PREFIX + - 'None') - device_id = uuidutils.generate_uuid() - fixed_ips = [{'subnet_id': subnet['subnet']['id'], - 'ip_address': '10.0.0.3'}, - {'subnet_id': subnet['subnet']['id'], - 'ip_address': '10.0.0.4'}] - with self.port(subnet=subnet, - device_owner=device_owner, - device_id=device_id, - fixed_ips=fixed_ips) as port: - self.assertEqual(create_dhcp_binding.call_count, 2) - new_fixed_ips = [ - {'subnet_id': subnet['subnet']['id'], - 'ip_address': '10.0.0.5'}, - {'subnet_id': subnet['subnet']['id'], - 'ip_address': '10.0.0.6'}] - self.plugin.update_port( - context.get_admin_context(), - port['port']['id'], - {'port': {'fixed_ips': new_fixed_ips}}) - self.assertEqual(update_dhcp_binding.call_count, 2) - self.plugin.delete_port( - context.get_admin_context(), - port['port']['id']) - self.assertEqual(delete_dhcp_binding.call_count, 2) - def test_create_network_with_bad_az_hint(self): p = directory.get_plugin() ctx = context.get_admin_context() diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py index 66aab640d6..c0938dcb6e 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py @@ -282,6 +282,12 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin): self.plugin.create_subnet, context.get_admin_context(), data) + def test_subnet_update_ipv4_and_ipv6_pd_v6stateless_subnets(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_subnet_update_ipv4_and_ipv6_pd_slaac_subnets(self): + self.skipTest('Multiple fixed ips on a port are not supported') + class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin, test_bindings.PortBindingsTestCase, @@ -502,6 +508,59 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin, networks = self.plugin.get_ports(ctx) self.assertListEqual([], networks) + def test_update_port_add_additional_ip(self): + """Test update of port with additional IP fails.""" + with self.subnet() as subnet: + with self.port(subnet=subnet) as port: + data = {'port': {'admin_state_up': False, + 'fixed_ips': [{'subnet_id': + subnet['subnet']['id']}, + {'subnet_id': + subnet['subnet']['id']}]}} + req = self.new_update_request('ports', data, + port['port']['id']) + res = req.get_response(self.api) + self.assertEqual(exc.HTTPBadRequest.code, + res.status_int) + + def test_create_port_additional_ip(self): + """Test that creation of port with additional IP fails.""" + with self.subnet() as subnet: + data = {'port': {'network_id': subnet['subnet']['network_id'], + 'tenant_id': subnet['subnet']['tenant_id'], + 'fixed_ips': [{'subnet_id': + subnet['subnet']['id']}, + {'subnet_id': + subnet['subnet']['id']}]}} + port_req = self.new_create_request('ports', data) + res = port_req.get_response(self.api) + self.assertEqual(exc.HTTPBadRequest.code, + res.status_int) + + def test_update_port_update_ip_address_only(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_update_port_with_new_ipv6_slaac_subnet_in_fixed_ips(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_update_port_mac_v6_slaac(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_requested_subnet_id_v4_and_v6(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_requested_invalid_fixed_ips(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_requested_subnet_id_v4_and_v6_slaac(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_range_allocation(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_create_port_anticipating_allocation(self): + self.skipTest('Multiple fixed ips on a port are not supported') + class DHCPOptsTestCase(test_dhcpopts.TestExtraDhcpOpt, NsxV3PluginTestCaseMixin): @@ -588,6 +647,21 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxV3PluginTestCaseMixin, self.plugin_instance.__class__.__name__) self._plugin_class = self.plugin_instance.__class__ + def test_floatingip_create_different_fixed_ip_same_port(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_router_add_interface_multiple_ipv4_subnet_port_returns_400(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_router_add_interface_multiple_ipv6_subnet_port(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_floatingip_update_different_fixed_ip_same_port(self): + self.skipTest('Multiple fixed ips on a port are not supported') + + def test_create_multiple_floatingips_same_fixed_ip_same_port(self): + self.skipTest('Multiple fixed ips on a port are not supported') + class TestL3NatTestCase(L3NatTest, test_l3_plugin.L3NatDBIntTestCase,