diff --git a/vmware_nsx/dhcpmeta_modes.py b/vmware_nsx/dhcp_meta/modes.py similarity index 100% rename from vmware_nsx/dhcpmeta_modes.py rename to vmware_nsx/dhcp_meta/modes.py diff --git a/vmware_nsx/plugins/dvs/plugin.py b/vmware_nsx/plugins/dvs/plugin.py index 4ade73e0f2..739d6790d3 100644 --- a/vmware_nsx/plugins/dvs/plugin.py +++ b/vmware_nsx/plugins/dvs/plugin.py @@ -45,7 +45,7 @@ from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils as c_utils from vmware_nsx.db import db as nsx_db -from vmware_nsx import dhcpmeta_modes +from vmware_nsx.dhcp_meta import modes as dhcpmeta_modes from vmware_nsx.dvs import dvs from vmware_nsx.dvs import dvs_utils diff --git a/vmware_nsx/plugins/nsx_mh/plugin.py b/vmware_nsx/plugins/nsx_mh/plugin.py index 5d0693c0ca..7281f3808b 100644 --- a/vmware_nsx/plugins/nsx_mh/plugin.py +++ b/vmware_nsx/plugins/nsx_mh/plugin.py @@ -72,7 +72,7 @@ from vmware_nsx.db import maclearning as mac_db from vmware_nsx.db import networkgw_db from vmware_nsx.db import nsx_models from vmware_nsx.db import qos_db -from vmware_nsx import dhcpmeta_modes +from vmware_nsx.dhcp_meta import modes as dhcpmeta_modes from vmware_nsx.extensions import maclearning as mac_ext from vmware_nsx.extensions import networkgw from vmware_nsx.extensions import qos diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index af3b033dc4..8716d96ce4 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -64,6 +64,7 @@ from vmware_nsx.common import locking from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils from vmware_nsx.db import db as nsx_db +from vmware_nsx.dhcp_meta import rpc as nsx_rpc from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.nsxlib.v3 import client as nsx_client from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster @@ -651,6 +652,7 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, with context.session.begin(subtransactions=True): neutron_db = super(NsxV3Plugin, self).create_port(context, port) port["port"].update(neutron_db) + nsx_rpc.handle_port_metadata_access(self, context, neutron_db) (is_psec_on, has_ip) = self._create_port_preprocess_security( context, port, port_data, neutron_db) @@ -708,6 +710,8 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, context.session, port_id) self._port_client.delete(nsx_port_id) self.disassociate_floatingips(context, port_id) + nsx_rpc.handle_port_metadata_access(self, context, port, + is_delete=True) ret_val = super(NsxV3Plugin, self).delete_port(context, port_id) return ret_val @@ -1016,6 +1020,8 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, return self.get_router(context, router['id']) def delete_router(self, context, router_id): + nsx_rpc.handle_router_metadata_access(self, context, router_id, + interface=None) router = self.get_router(context, router_id) if router.get(l3.EXTERNAL_GW_INFO): self._update_router_gw_info(context, router_id, {}) @@ -1190,6 +1196,11 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, # TODO(berlin): Announce the subnet on tier0 if enable_snat # is False pass + # Ensure the NSX logical router has a connection to a + # 'metadata access' network (with a proxy listening on + # its DHCP port), by creating it if needed. + nsx_rpc.handle_router_metadata_access(self, context, router_id, + interface=info) except nsx_exc.ManagerError: with excutils.save_and_reraise_exception(): self.remove_router_interface( @@ -1257,8 +1268,13 @@ class NsxV3Plugin(addr_pair_db.AllowedAddressPairsMixin, "%(net_id)s not found at the backend"), {'router_id': router_id, 'net_id': subnet['network_id']}) - return super(NsxV3Plugin, self).remove_router_interface( + info = super(NsxV3Plugin, self).remove_router_interface( context, router_id, interface_info) + # Ensure the connection to the 'metadata access network' is removed + # (with the network) if this the last subnet on the router. + nsx_rpc.handle_router_metadata_access(self, context, router_id, + interface=info) + return info def create_floatingip(self, context, floatingip): new_fip = super(NsxV3Plugin, self).create_floatingip( diff --git a/vmware_nsx/tests/unit/extensions/test_metadata.py b/vmware_nsx/tests/unit/extensions/test_metadata.py new file mode 100644 index 0000000000..9c1c60d3a8 --- /dev/null +++ b/vmware_nsx/tests/unit/extensions/test_metadata.py @@ -0,0 +1,259 @@ +# Copyright 2015 VMware, Inc. +# All Rights Reserved +# +# 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. + +import mock +import netaddr +from oslo_config import cfg +import webob.exc + +from neutron.common import constants +from neutron.common import exceptions as n_exc +from neutron import manager + +from vmware_nsx.api_client import exception as api_exc +from vmware_nsx.common import config + + +class MetaDataTestCase(object): + + def _metadata_setup(self, mode=config.MetadataModes.DIRECT): + cfg.CONF.set_override('metadata_mode', mode, 'NSX') + + def _metadata_teardown(self): + cfg.CONF.set_override('metadata_mode', None, 'NSX') + + def test_router_add_interface_subnet_with_metadata_access(self): + self._metadata_setup() + self.test_router_add_interface_subnet() + self._metadata_teardown() + + def test_router_add_interface_port_with_metadata_access(self): + self._metadata_setup() + self.test_router_add_interface_port() + self._metadata_teardown() + + def test_router_add_interface_dupsubnet_returns_400_with_metadata(self): + self._metadata_setup() + self.test_router_add_interface_dup_subnet1_returns_400() + self._metadata_teardown() + + def test_router_add_interface_overlapped_cidr_returns_400_with(self): + self._metadata_setup() + self.test_router_add_interface_overlapped_cidr_returns_400() + self._metadata_teardown() + + def test_router_remove_interface_inuse_returns_409_with_metadata(self): + self._metadata_setup() + self.test_router_remove_interface_inuse_returns_409() + self._metadata_teardown() + + def test_router_remove_iface_wrong_sub_returns_400_with_metadata(self): + self._metadata_setup() + self.test_router_remove_interface_wrong_subnet_returns_400() + self._metadata_teardown() + + def test_router_delete_with_metadata_access(self): + self._metadata_setup() + self.test_router_delete() + self._metadata_teardown() + + def test_router_delete_with_port_existed_returns_409_with_metadata(self): + self._metadata_setup() + self.test_router_delete_with_port_existed_returns_409() + self._metadata_teardown() + + def test_delete_port_with_metadata(self): + self._metadata_setup(config.MetadataModes.INDIRECT) + with self.subnet() as s: + with self.port(subnet=s, fixed_ips=[], device_id='1234', + device_owner=constants.DEVICE_OWNER_DHCP) as port: + self._delete('ports', port['port']['id']) + self._metadata_teardown() + + def test_metadatata_network_created_with_router_interface_add(self): + self._metadata_setup() + with mock.patch.object(self._plugin_class, 'schedule_network') as f: + with self.router() as r: + with self.subnet() as s: + self._router_interface_action('add', + r['router']['id'], + s['subnet']['id'], + None) + r_ports = self._list('ports')['ports'] + self.assertEqual(len(r_ports), 2) + ips = [] + for port in r_ports: + ips.extend([netaddr.IPAddress(fixed_ip['ip_address']) + for fixed_ip in port['fixed_ips']]) + meta_cidr = netaddr.IPNetwork('169.254.0.0/16') + self.assertTrue(any([ip in meta_cidr for ip in ips])) + # Needed to avoid 409. + self._router_interface_action('remove', + r['router']['id'], + s['subnet']['id'], + None) + # Verify that the metadata network gets scheduled, so that + # an active dhcp agent can pick it up. + expected_meta_net = { + 'status': 'ACTIVE', + 'subnets': [], + 'name': 'meta-%s' % r['router']['id'], + 'admin_state_up': True, + 'tenant_id': '', + 'port_security_enabled': False, + 'shared': False, + 'id': mock.ANY, + 'mtu': mock.ANY + } + f.assert_any_call(mock.ANY, expected_meta_net) + self._metadata_teardown() + + def test_metadata_network_create_rollback_on_create_subnet_failure(self): + self._metadata_setup() + with self.router() as r: + with self.subnet() as s: + # Raise a NeutronException (eg: NotFound). + with mock.patch.object(self._plugin_class, + 'create_subnet', + side_effect=n_exc.NotFound): + self._router_interface_action( + 'add', r['router']['id'], s['subnet']['id'], None) + # Ensure metadata network was removed. + nets = self._list('networks')['networks'] + self.assertEqual(len(nets), 1) + # Needed to avoid 409. + self._router_interface_action('remove', + r['router']['id'], + s['subnet']['id'], + None) + self._metadata_teardown() + + def test_metadata_network_create_rollback_on_add_rtr_iface_failure(self): + self._metadata_setup() + with self.router() as r: + with self.subnet() as s: + # Save function being mocked. + real_func = self._plugin_class.add_router_interface + plugin_instance = manager.NeutronManager.get_plugin() + + # Raise a NeutronException when adding metadata subnet + # to router. + def side_effect(*args): + if args[-1]['subnet_id'] == s['subnet']['id']: + # Do the real thing. + return real_func(plugin_instance, *args) + # Otherwise raise. + raise api_exc.NsxApiException() + + with mock.patch.object(self._plugin_class, + 'add_router_interface', + side_effect=side_effect): + self._router_interface_action( + 'add', r['router']['id'], s['subnet']['id'], None) + # Ensure metadata network was removed. + nets = self._list('networks')['networks'] + self.assertEqual(len(nets), 1) + # Needed to avoid 409. + self._router_interface_action('remove', + r['router']['id'], + s['subnet']['id'], + None) + self._metadata_teardown() + + def test_metadata_network_removed_with_router_interface_remove(self): + self._metadata_setup() + with self.router() as r: + with self.subnet() as s: + self._router_interface_action('add', r['router']['id'], + s['subnet']['id'], None) + subnets = self._list('subnets')['subnets'] + self.assertEqual(len(subnets), 2) + meta_cidr = netaddr.IPNetwork('169.254.0.0/16') + for subnet in subnets: + cidr = netaddr.IPNetwork(subnet['cidr']) + if meta_cidr == cidr or meta_cidr in cidr.supernet(16): + meta_sub_id = subnet['id'] + meta_net_id = subnet['network_id'] + ports = self._list( + 'ports', + query_params='network_id=%s' % meta_net_id)['ports'] + self.assertEqual(len(ports), 1) + meta_port_id = ports[0]['id'] + self._router_interface_action('remove', r['router']['id'], + s['subnet']['id'], None) + self._show('networks', meta_net_id, + webob.exc.HTTPNotFound.code) + self._show('ports', meta_port_id, + webob.exc.HTTPNotFound.code) + self._show('subnets', meta_sub_id, + webob.exc.HTTPNotFound.code) + self._metadata_teardown() + + def test_metadata_network_remove_rollback_on_failure(self): + self._metadata_setup() + with self.router() as r: + with self.subnet() as s: + self._router_interface_action('add', r['router']['id'], + s['subnet']['id'], None) + networks = self._list('networks')['networks'] + for network in networks: + if network['id'] != s['subnet']['network_id']: + meta_net_id = network['id'] + ports = self._list( + 'ports', + query_params='network_id=%s' % meta_net_id)['ports'] + meta_port_id = ports[0]['id'] + # Save function being mocked. + real_func = self._plugin_class.remove_router_interface + plugin_instance = manager.NeutronManager.get_plugin() + + # Raise a NeutronException when removing metadata subnet + # from router. + def side_effect(*args): + if args[-1].get('subnet_id') == s['subnet']['id']: + # Do the real thing. + return real_func(plugin_instance, *args) + # Otherwise raise. + raise api_exc.NsxApiException() + + with mock.patch.object(self._plugin_class, + 'remove_router_interface', + side_effect=side_effect): + self._router_interface_action('remove', r['router']['id'], + s['subnet']['id'], None) + # Metadata network and subnet should still be there. + self._show('networks', meta_net_id, + webob.exc.HTTPOk.code) + self._show('ports', meta_port_id, + webob.exc.HTTPOk.code) + self._metadata_teardown() + + def test_metadata_dhcp_host_route(self): + self._metadata_setup(config.MetadataModes.INDIRECT) + subnets = self._list('subnets')['subnets'] + with self.subnet() as s: + with self.port(subnet=s, device_id='1234', + device_owner=constants.DEVICE_OWNER_DHCP) as port: + subnets = self._list('subnets')['subnets'] + self.assertEqual(len(subnets), 1) + self.assertEqual(subnets[0]['host_routes'][0]['nexthop'], + '10.0.0.2') + self.assertEqual(subnets[0]['host_routes'][0]['destination'], + '169.254.169.254/32') + self._delete('ports', port['port']['id']) + subnets = self._list('subnets')['subnets'] + # Test that route is deleted after dhcp port is removed. + self.assertEqual(len(subnets[0]['host_routes']), 0) + self._metadata_teardown() diff --git a/vmware_nsx/tests/unit/nsx_mh/test_plugin.py b/vmware_nsx/tests/unit/nsx_mh/test_plugin.py index 2b57c4fc3c..f9fa2e64f0 100644 --- a/vmware_nsx/tests/unit/nsx_mh/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_mh/test_plugin.py @@ -16,7 +16,6 @@ import uuid import mock -import netaddr from neutron.api.v2 import attributes from neutron.common import constants from neutron.common import exceptions as ntn_exc @@ -52,6 +51,7 @@ from vmware_nsx.common import utils from vmware_nsx.db import db as nsx_db from vmware_nsx.nsxlib import mh as nsxlib from vmware_nsx.tests import unit as vmware +from vmware_nsx.tests.unit.extensions import test_metadata from vmware_nsx.tests.unit.nsx_mh.apiclient import fake from vmware_nsx.tests.unit import test_utils @@ -532,7 +532,8 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxPluginV2TestCase): class TestL3NatTestCase(L3NatTest, test_l3_plugin.L3NatDBIntTestCase, - NsxPluginV2TestCase): + NsxPluginV2TestCase, + test_metadata.MetaDataTestCase): def _test_create_l3_ext_network(self, vlan_id=0): name = 'l3_ext_net' @@ -761,23 +762,12 @@ class TestL3NatTestCase(L3NatTest, def test_floatingip_with_invalid_create_port(self): self._test_floatingip_with_invalid_create_port(self._plugin_name) - def _metadata_setup(self): - cfg.CONF.set_override('metadata_mode', 'access_network', 'NSX') - - def _metadata_teardown(self): - cfg.CONF.set_override('metadata_mode', None, 'NSX') - def test_create_router_name_exceeds_40_chars(self): name = 'this_is_a_router_whose_name_is_longer_than_40_chars' with self.router(name=name) as rtr: # Assert Neutron name is not truncated self.assertEqual(rtr['router']['name'], name) - def test_router_add_interface_subnet_with_metadata_access(self): - self._metadata_setup() - self.test_router_add_interface_subnet() - self._metadata_teardown() - def test_router_add_interface_port(self): orig_update_port = self.plugin.update_port with self.router() as r, ( @@ -804,216 +794,6 @@ class TestL3NatTestCase(L3NatTest, None, p['port']['id']) - def test_router_add_interface_port_with_metadata_access(self): - self._metadata_setup() - self.test_router_add_interface_port() - self._metadata_teardown() - - def test_router_add_interface_dupsubnet_returns_400_with_metadata(self): - self._metadata_setup() - self.test_router_add_interface_dup_subnet1_returns_400() - self._metadata_teardown() - - def test_router_add_interface_overlapped_cidr_returns_400_with(self): - self._metadata_setup() - self.test_router_add_interface_overlapped_cidr_returns_400() - self._metadata_teardown() - - def test_router_remove_interface_inuse_returns_409_with_metadata(self): - self._metadata_setup() - self.test_router_remove_interface_inuse_returns_409() - self._metadata_teardown() - - def test_router_remove_iface_wrong_sub_returns_400_with_metadata(self): - self._metadata_setup() - self.test_router_remove_interface_wrong_subnet_returns_400() - self._metadata_teardown() - - def test_router_delete_with_metadata_access(self): - self._metadata_setup() - self.test_router_delete() - self._metadata_teardown() - - def test_router_delete_with_port_existed_returns_409_with_metadata(self): - self._metadata_setup() - self.test_router_delete_with_port_existed_returns_409() - self._metadata_teardown() - - def test_metadatata_network_created_with_router_interface_add(self): - self._metadata_setup() - with mock.patch.object(self._plugin_class, 'schedule_network') as f: - with self.router() as r: - with self.subnet() as s: - self._router_interface_action('add', - r['router']['id'], - s['subnet']['id'], - None) - r_ports = self._list('ports')['ports'] - self.assertEqual(len(r_ports), 2) - ips = [] - for port in r_ports: - ips.extend([netaddr.IPAddress(fixed_ip['ip_address']) - for fixed_ip in port['fixed_ips']]) - meta_cidr = netaddr.IPNetwork('169.254.0.0/16') - self.assertTrue(any([ip in meta_cidr for ip in ips])) - # Needed to avoid 409 - self._router_interface_action('remove', - r['router']['id'], - s['subnet']['id'], - None) - # Verify that the metadata network gets scheduled, so that - # an active dhcp agent can pick it up - expected_meta_net = { - 'status': 'ACTIVE', - 'subnets': [], - 'name': 'meta-%s' % r['router']['id'], - 'admin_state_up': True, - 'tenant_id': '', - 'port_security_enabled': False, - 'shared': False, - 'id': mock.ANY, - 'mtu': mock.ANY - } - f.assert_any_call(mock.ANY, expected_meta_net) - self._metadata_teardown() - - def test_metadata_network_create_rollback_on_create_subnet_failure(self): - self._metadata_setup() - with self.router() as r: - with self.subnet() as s: - # Raise a NeutronException (eg: NotFound) - with mock.patch.object(self._plugin_class, - 'create_subnet', - side_effect=ntn_exc.NotFound): - self._router_interface_action( - 'add', r['router']['id'], s['subnet']['id'], None) - # Ensure metadata network was removed - nets = self._list('networks')['networks'] - self.assertEqual(len(nets), 1) - # Needed to avoid 409 - self._router_interface_action('remove', - r['router']['id'], - s['subnet']['id'], - None) - self._metadata_teardown() - - def test_metadata_network_create_rollback_on_add_rtr_iface_failure(self): - self._metadata_setup() - with self.router() as r: - with self.subnet() as s: - # Raise a NeutronException when adding metadata subnet - # to router - # save function being mocked - real_func = self._plugin_class.add_router_interface - plugin_instance = manager.NeutronManager.get_plugin() - - def side_effect(*args): - if args[-1]['subnet_id'] == s['subnet']['id']: - # do the real thing - return real_func(plugin_instance, *args) - # otherwise raise - raise api_exc.NsxApiException() - - with mock.patch.object(self._plugin_class, - 'add_router_interface', - side_effect=side_effect): - self._router_interface_action( - 'add', r['router']['id'], s['subnet']['id'], None) - # Ensure metadata network was removed - nets = self._list('networks')['networks'] - self.assertEqual(len(nets), 1) - # Needed to avoid 409 - self._router_interface_action('remove', - r['router']['id'], - s['subnet']['id'], - None) - self._metadata_teardown() - - def test_metadata_network_removed_with_router_interface_remove(self): - self._metadata_setup() - with self.router() as r: - with self.subnet() as s: - self._router_interface_action('add', r['router']['id'], - s['subnet']['id'], None) - subnets = self._list('subnets')['subnets'] - self.assertEqual(len(subnets), 2) - meta_cidr = netaddr.IPNetwork('169.254.0.0/16') - for subnet in subnets: - cidr = netaddr.IPNetwork(subnet['cidr']) - if meta_cidr == cidr or meta_cidr in cidr.supernet(16): - meta_sub_id = subnet['id'] - meta_net_id = subnet['network_id'] - ports = self._list( - 'ports', - query_params='network_id=%s' % meta_net_id)['ports'] - self.assertEqual(len(ports), 1) - meta_port_id = ports[0]['id'] - self._router_interface_action('remove', r['router']['id'], - s['subnet']['id'], None) - self._show('networks', meta_net_id, - webob.exc.HTTPNotFound.code) - self._show('ports', meta_port_id, - webob.exc.HTTPNotFound.code) - self._show('subnets', meta_sub_id, - webob.exc.HTTPNotFound.code) - self._metadata_teardown() - - def test_metadata_network_remove_rollback_on_failure(self): - self._metadata_setup() - with self.router() as r: - with self.subnet() as s: - self._router_interface_action('add', r['router']['id'], - s['subnet']['id'], None) - networks = self._list('networks')['networks'] - for network in networks: - if network['id'] != s['subnet']['network_id']: - meta_net_id = network['id'] - ports = self._list( - 'ports', - query_params='network_id=%s' % meta_net_id)['ports'] - meta_port_id = ports[0]['id'] - # Raise a NeutronException when removing - # metadata subnet from router - # save function being mocked - real_func = self._plugin_class.remove_router_interface - plugin_instance = manager.NeutronManager.get_plugin() - - def side_effect(*args): - if args[-1].get('subnet_id') == s['subnet']['id']: - # do the real thing - return real_func(plugin_instance, *args) - # otherwise raise - raise api_exc.NsxApiException() - - with mock.patch.object(self._plugin_class, - 'remove_router_interface', - side_effect=side_effect): - self._router_interface_action('remove', r['router']['id'], - s['subnet']['id'], None) - # Metadata network and subnet should still be there - self._show('networks', meta_net_id, - webob.exc.HTTPOk.code) - self._show('ports', meta_port_id, - webob.exc.HTTPOk.code) - self._metadata_teardown() - - def test_metadata_dhcp_host_route(self): - cfg.CONF.set_override('metadata_mode', 'dhcp_host_route', 'NSX') - subnets = self._list('subnets')['subnets'] - with self.subnet() as s: - with self.port(subnet=s, device_id='1234', - device_owner=constants.DEVICE_OWNER_DHCP) as port: - subnets = self._list('subnets')['subnets'] - self.assertEqual(len(subnets), 1) - self.assertEqual(subnets[0]['host_routes'][0]['nexthop'], - '10.0.0.2') - self.assertEqual(subnets[0]['host_routes'][0]['destination'], - '169.254.169.254/32') - self._delete('ports', port['port']['id']) - subnets = self._list('subnets')['subnets'] - # Test that route is deleted after dhcp port is removed - self.assertEqual(len(subnets[0]['host_routes']), 0) - def _test_floatingip_update(self, expected_status): super(TestL3NatTestCase, self).test_floatingip_update( expected_status) diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py index 862f9a8d63..a842764461 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py @@ -45,6 +45,7 @@ from vmware_nsx.nsxlib.v3 import client as nsx_client from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster from vmware_nsx.plugins.nsx_v3 import plugin as nsx_plugin from vmware_nsx.tests import unit as vmware +from vmware_nsx.tests.unit.extensions import test_metadata from vmware_nsx.tests.unit.nsx_v3 import mocks as nsx_v3_mocks from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase @@ -257,12 +258,14 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxV3PluginTestCaseMixin): class TestL3NatTestCase(L3NatTest, test_l3_plugin.L3NatDBIntTestCase, - test_ext_route.ExtraRouteDBTestCaseBase): + test_ext_route.ExtraRouteDBTestCaseBase, + test_metadata.MetaDataTestCase): def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None, service_plugins=None): super(TestL3NatTestCase, self).setUp(plugin=plugin, ext_mgr=ext_mgr) + cfg.CONF.set_override('metadata_mode', None, 'NSX') def _test_create_l3_ext_network( self, physical_network=nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID):