diff --git a/vmware_nsx/common/utils.py b/vmware_nsx/common/utils.py index d93e3fc6c3..92676f13b6 100644 --- a/vmware_nsx/common/utils.py +++ b/vmware_nsx/common/utils.py @@ -139,10 +139,15 @@ def build_v3_tags_payload(resource, resource_type, project_name): # There may be cases when the plugin creates the port, for example DHCP if not project_name: project_name = 'NSX Neutron plugin' + tenant_id = resource.get('tenant_id', '') + # If tenant_id is present in resource and set to None, explicitly set + # the tenant_id in tags as ''. + if tenant_id is None: + tenant_id = '' return [{'scope': resource_type, 'tag': resource.get('id', '')[:MAX_TAG_LEN]}, {'scope': 'os-project-id', - 'tag': resource.get('tenant_id', '')[:MAX_TAG_LEN]}, + 'tag': tenant_id[:MAX_TAG_LEN]}, {'scope': 'os-project-name', 'tag': project_name[:MAX_TAG_LEN]}, {'scope': 'os-api-version', diff --git a/vmware_nsx/services/l2gateway/README.rst b/vmware_nsx/services/l2gateway/README.rst index a8840ad4b3..79dccec632 100644 --- a/vmware_nsx/services/l2gateway/README.rst +++ b/vmware_nsx/services/l2gateway/README.rst @@ -2,6 +2,8 @@ Enabling NSX L2 Gateway Plugin in DevStack ============================================ +Following steps are meant for L2GW service in neutron for stable/mitaka* release onwards. + 1. Download DevStack 2. Add networking-l2gw repo as an external repository and configure following flags in ``local.conf``:: @@ -9,11 +11,17 @@ [[local|localrc]] enable_plugin networking-l2gw https://github.com/openstack/networking-l2gw ENABLED_SERVICES=l2gw-plugin - NETWORKING_L2GW_SERVICE_DRIVER=L2GW:vmware-nsx-l2gw:vmware_nsx.services.l2gateway.common.plugin.NsxL2GatewayPlugin:default 3. For NSXv3 include the following additional flags in ``local.conf``:: [[local|localrc]] - NSX_L2GW_DRIVER='vmware_nsx.services.l2gateway.nsx_v3.driver.NsxV3Driver' + NETWORKING_L2GW_SERVICE_DRIVER=L2GW:vmware-nsx-l2gw:vmware_nsx.services.l2gateway.nsx_v3.driver.NsxV3Driver:default DEFAULT_BRIDGE_CLUSTER_UUID= 4. run ``stack.sh`` + +* Configuration for stable/liberty release in ``local.conf``:: + [[local|localrc]] + enable_plugin networking-l2gw https://github.com/openstack/networking-l2gw + NSX_L2GW_DRIVER='vmware_nsx.services.l2gateway.nsx_v3.driver.NsxV3Driver' + Q_SERVICE_PLUGIN_CLASSES=vmware_nsx_l2gw + DEFAULT_BRIDGE_CLUSTER_UUID= diff --git a/vmware_nsx/services/l2gateway/common/plugin.py b/vmware_nsx/services/l2gateway/common/plugin.py index 3c62a5b1a9..fb3a96f689 100644 --- a/vmware_nsx/services/l2gateway/common/plugin.py +++ b/vmware_nsx/services/l2gateway/common/plugin.py @@ -34,10 +34,18 @@ class NsxL2GatewayPlugin(l2gateway_db.L2GatewayMixin): _methods_to_delegate = ["create_l2_gateway", "get_l2_gateway", "delete_l2_gateway", "get_l2_gateways", "update_l2_gateway", + "create_l2_gateway_precommit", + "create_l2_gateway_postcommit", + "delete_l2_gateway_precommit", + "delete_l2_gateway_postcommit", "create_l2_gateway_connection", + "create_l2_gateway_connection_precommit", + "create_l2_gateway_connection_postcommit", "get_l2_gateway_connection", "get_l2_gateway_connections", "update_l2_gateway_connection", + "delete_l2_gateway_connection_precommit", + "delete_l2_gateway_connection_postcommit", "delete_l2_gateway_connection"] def __init__(self, plugin): diff --git a/vmware_nsx/services/l2gateway/nsx_v3/driver.py b/vmware_nsx/services/l2gateway/nsx_v3/driver.py index 642fccab96..0449d84c44 100644 --- a/vmware_nsx/services/l2gateway/nsx_v3/driver.py +++ b/vmware_nsx/services/l2gateway/nsx_v3/driver.py @@ -48,14 +48,27 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): """Class to handle API calls for L2 gateway and NSXv3 backend.""" gateway_resource = l2gw_const.GATEWAY_RESOURCE_NAME - def __init__(self): + def __init__(self, plugin): # Create a default L2 gateway if default_bridge_cluster is # provided in nsx.ini + super(NsxV3Driver, self).__init__() + self._plugin = plugin + LOG.debug("Starting service plugin for NSX L2Gateway") self._ensure_default_l2_gateway() self.subscribe_callback_notifications() LOG.debug("Initialization complete for NSXv3 driver for " "L2 gateway service plugin.") + @staticmethod + def get_plugin_type(): + """Get type of the plugin.""" + return l2gw_const.L2GW + + @staticmethod + def get_plugin_description(): + """Get description of the plugin.""" + return l2gw_const.L2_GATEWAY_SERVICE_PLUGIN + @property def _core_plugin(self): return manager.NeutronManager.get_plugin() @@ -89,7 +102,9 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): def_l2gw = {'name': 'default-l2gw', 'devices': [device]} l2gw_dict = {self.gateway_resource: def_l2gw} - l2_gateway = self.create_l2_gateway(admin_ctx, l2gw_dict) + self.create_l2_gateway(admin_ctx, l2gw_dict) + l2_gateway = super(NsxV3Driver, self).create_l2_gateway(admin_ctx, + l2gw_dict) # Verify that only one default L2 gateway is created def_l2gw_exists = False l2gateways = self._get_l2_gateways(admin_ctx) @@ -101,13 +116,17 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): LOG.info(_LI("Default L2 gateway is already created.")) try: # Try deleting this duplicate default L2 gateway - self.delete_l2_gateway(admin_ctx, l2gateway['id']) + self.validate_l2_gateway_for_delete( + admin_ctx, l2gateway['id']) + super(NsxV3Driver, self).delete_l2_gateway( + admin_ctx, l2gateway['id']) except l2gw_exc.L2GatewayInUse: # If the L2 gateway we are trying to delete is in # use then we should delete the L2 gateway which # we just created ensuring there is only one # default L2 gateway in the database. - self.delete_l2_gateway(admin_ctx, l2_gateway['id']) + super(NsxV3Driver, self).delete_l2_gateway( + admin_ctx, l2_gateway['id']) else: def_l2gw_exists = True return l2_gateway @@ -141,8 +160,21 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): gw = l2_gateway[self.gateway_resource] devices = gw['devices'] self._validate_device_list(devices) - return super(NsxV3Driver, self).create_l2_gateway(context, - l2_gateway) + + def create_l2_gateway_precommit(self, context, l2_gateway): + pass + + def create_l2_gateway_postcommit(self, context, l2_gateway): + pass + + def delete_l2_gateway(self, context, l2_gateway_id): + pass + + def delete_l2_gateway_precommit(self, context, l2_gateway_id): + pass + + def delete_l2_gateway_postcommit(self, context, l2_gateway_id): + pass def _validate_network(self, context, network_id): network = self._core_plugin.get_network(context, network_id) @@ -160,16 +192,17 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): return n_utils.is_valid_vlan_tag(seg_id) def create_l2_gateway_connection(self, context, l2_gateway_connection): - """Create a L2 gateway connection.""" - #TODO(abhiraut): Move backend logic in a separate method - gw_connection = l2_gateway_connection.get(l2gw_const. - CONNECTION_RESOURCE_NAME) + gw_connection = l2_gateway_connection.get(self.connection_resource) network_id = gw_connection.get(l2gw_const.NETWORK_ID) self._validate_network(context, network_id) - l2gw_connection = super( - NsxV3Driver, self).create_l2_gateway_connection( - context, l2_gateway_connection) + + def create_l2_gateway_connection_precommit(self, context, gw_connection): + pass + + def create_l2_gateway_connection_postcommit(self, context, gw_connection): + """Create a L2 gateway connection.""" l2gw_id = gw_connection.get(l2gw_const.L2GATEWAY_ID) + network_id = gw_connection.get(l2gw_const.NETWORK_ID) devices = self._get_l2_gateway_devices(context, l2gw_id) # In NSXv3, there will be only one device configured per L2 gateway. # The name of the device shall carry the backend bridge cluster's UUID. @@ -184,6 +217,10 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): interface = self._get_l2_gw_interfaces(context, devices[0]['id']) seg_id = interface[0].get(l2gw_const.SEG_ID) self._validate_segment_id(seg_id) + tenant_id = gw_connection['tenant_id'] + if context.is_admin and not tenant_id: + tenant_id = context.tenant_id + gw_connection['tenant_id'] = tenant_id try: tags = nsx_utils.build_v3_tags_payload( gw_connection, resource_type='os-neutron-l2gw-id', @@ -192,18 +229,13 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): device_name=device_name, seg_id=seg_id, tags=tags) - except nsx_exc.ManagerError: - LOG.exception(_LE("Unable to update NSX backend, rolling back " - "changes on neutron")) - with excutils.save_and_reraise_exception(): - super(NsxV3Driver, - self).delete_l2_gateway_connection(context, - l2gw_connection['id']) - # Create a logical port and connect it to the bridge endpoint. - tenant_id = gw_connection['tenant_id'] - if context.is_admin and not tenant_id: - tenant_id = context.tenant_id + except nsx_exc.ManagerError as e: + LOG.exception(_LE("Unable to create bridge endpoint, rolling back " + "changes on neutron. Exception is %s"), e) + raise l2gw_exc.L2GatewayServiceDriverError( + method='create_l2_gateway_connection_postcommit') #TODO(abhiraut): Consider specifying the name of the port + # Create a logical port and connect it to the bridge endpoint. port_dict = {'port': { 'tenant_id': tenant_id, 'network_id': network_id, @@ -226,18 +258,16 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): LOG.debug("IP addresses deallocated on port %s", port['id']) except (nsx_exc.ManagerError, n_exc.NeutronException): - with excutils.save_and_reraise_exception(): - LOG.exception(_LE("Unable to create L2 gateway port, " - "rolling back changes on neutron")) - nsxlib.delete_bridge_endpoint(bridge_endpoint['id']) - super(NsxV3Driver, - self).delete_l2_gateway_connection(context, - l2gw_connection['id']) + LOG.exception(_LE("Unable to create L2 gateway port, " + "rolling back changes on neutron")) + nsxlib.delete_bridge_endpoint(bridge_endpoint['id']) + raise l2gw_exc.L2GatewayServiceDriverError( + method='create_l2_gateway_connection_postcommit') try: # Update neutron's database with the mappings. nsx_db.add_l2gw_connection_mapping( session=context.session, - connection_id=l2gw_connection['id'], + connection_id=gw_connection['id'], bridge_endpoint_id=bridge_endpoint['id'], port_id=port['id']) except db_exc.DBError: @@ -246,15 +276,22 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): "mappings, rolling back changes on neutron")) nsxlib.delete_bridge_endpoint(bridge_endpoint['id']) super(NsxV3Driver, - self).delete_l2_gateway_connection(context, - l2gw_connection['id']) - return l2gw_connection + self).delete_l2_gateway_connection( + context, + gw_connection['id']) + return gw_connection - def delete_l2_gateway_connection(self, context, l2_gateway_connection): + def delete_l2_gateway_connection(self, context, gw_connection): + pass + + def delete_l2_gateway_connection_precommit(self, context, gw_connection): + pass + + def delete_l2_gateway_connection_postcommit(self, context, gw_connection): """Delete a L2 gateway connection.""" conn_mapping = nsx_db.get_l2gw_connection_mapping( session=context.session, - connection_id=l2_gateway_connection) + connection_id=gw_connection) bridge_endpoint_id = conn_mapping.get('bridge_endpoint_id') # Delete the logical port from the bridge endpoint. self._core_plugin.delete_port(context=context, @@ -262,12 +299,12 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): l2gw_port_check=False) try: nsxlib.delete_bridge_endpoint(bridge_endpoint_id) - except nsx_exc.ManagerError: - LOG.exception(_LE("Unable to delete bridge endpoint %s on the " - "backend.") % bridge_endpoint_id) - return (super(NsxV3Driver, self). - delete_l2_gateway_connection(context, - l2_gateway_connection)) + except nsx_exc.ManagerError as e: + LOG.exception(_LE("Unable to delete bridge endpoint %(id)s on the " + "backend due to exc: %(exc)s"), + {'id': bridge_endpoint_id, 'exc': e}) + raise l2gw_exc.L2GatewayServiceDriverError( + method='delete_l2_gateway_connection_postcommit') def prevent_l2gw_port_deletion(self, context, port_id): """Prevent core plugin from deleting L2 gateway port.""" diff --git a/vmware_nsx/tests/unit/services/l2gateway/test_nsxv3_driver.py b/vmware_nsx/tests/unit/services/l2gateway/test_nsxv3_driver.py index 657dd438cd..bacab5e263 100644 --- a/vmware_nsx/tests/unit/services/l2gateway/test_nsxv3_driver.py +++ b/vmware_nsx/tests/unit/services/l2gateway/test_nsxv3_driver.py @@ -14,8 +14,11 @@ # limitations under the License. import mock +from networking_l2gw.db.l2gateway import l2gateway_db +from networking_l2gw.services.l2gateway.common import config from networking_l2gw.services.l2gateway.common import constants from networking_l2gw.services.l2gateway import exceptions as l2gw_exc +from networking_l2gw.services.l2gateway import plugin as core_l2gw_plugin from networking_l2gw.tests.unit.db import test_l2gw_db from oslo_config import cfg from oslo_utils import importutils @@ -27,7 +30,6 @@ from neutron.tests import base from neutron_lib import exceptions as n_exc from vmware_nsx.common import nsx_constants from vmware_nsx.nsxlib import v3 as nsxlib -from vmware_nsx.services.l2gateway.common import plugin as l2gw_plugin from vmware_nsx.services.l2gateway.nsx_v3 import driver as nsx_v3_driver from vmware_nsx.tests.unit.nsx_v3 import mocks as nsx_v3_mocks from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_nsx_v3_plugin @@ -44,13 +46,17 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, def setUp(self): super(TestNsxV3L2GatewayDriver, self).setUp() - cfg.CONF.set_override("nsx_l2gw_driver", - NSX_V3_L2GW_DRIVER_CLASS_PATH) - self.core_plugin = importutils.import_object(NSX_V3_PLUGIN_CLASS) - - self.driver = nsx_v3_driver.NsxV3Driver() - self.l2gw_plugin = l2gw_plugin.NsxL2GatewayPlugin(mock.MagicMock()) + self.driver = nsx_v3_driver.NsxV3Driver(mock.MagicMock()) + mock.patch.object(config, 'register_l2gw_opts_helper') + mock.patch('neutron.services.service_base.load_drivers', + return_value=({'dummyprovider': self.driver}, + 'dummyprovider')).start() + mock.patch.object(l2gateway_db.L2GatewayMixin, '__init__'), + mock.patch.object(l2gateway_db, 'subscribe') + mock.patch('neutron.db.servicetype_db.ServiceTypeManager.get_instance', + return_value=mock.MagicMock()).start() + self.l2gw_plugin = core_l2gw_plugin.L2GatewayPlugin() self.context = context.get_admin_context() def _get_nw_data(self): @@ -65,7 +71,7 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, 'subscribe_callback_notifications') as sub: with mock.patch.object(nsx_v3_driver.LOG, 'debug') as debug: - l2gw_plugin.NsxL2GatewayPlugin(mock.MagicMock()) + nsx_v3_driver.NsxV3Driver(mock.MagicMock()) self.assertTrue(def_gw.called) self.assertTrue(sub.called) self.assertTrue(debug.called) @@ -77,7 +83,7 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, cfg.CONF.set_override("default_bridge_cluster", def_bridge_cluster_name, "nsx_v3") - l2gw_plugin.NsxL2GatewayPlugin(mock.MagicMock()) + nsx_v3_driver.NsxV3Driver(mock.MagicMock()) l2gws = self.driver._get_l2_gateways(self.context) def_bridge_cluster_id = nsxlib.get_bridge_cluster_id_by_name_or_id( def_bridge_cluster_name) @@ -99,8 +105,8 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, cfg.CONF.set_override("default_bridge_cluster", def_bridge_cluster_name, "nsx_v3") - l2gw_plugin.NsxL2GatewayPlugin(mock.MagicMock()) - l2gw_plugin.NsxL2GatewayPlugin(mock.MagicMock()) + nsx_v3_driver.NsxV3Driver(mock.MagicMock()) + nsx_v3_driver.NsxV3Driver(mock.MagicMock()) l2gws = self.driver._get_l2_gateways(self.context) # Verify whether only one default L2 gateway is created self.assertEqual(1, len(l2gws)) @@ -108,7 +114,7 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, def test_create_default_l2_gateway_no_bc_uuid_noop(self): with mock.patch.object(nsx_v3_driver.NsxV3Driver, 'subscribe_callback_notifications'): - l2gw_plugin.NsxL2GatewayPlugin(mock.MagicMock()) + nsx_v3_driver.NsxV3Driver(mock.MagicMock()) l2gws = self.driver._get_l2_gateways(self.context) # Verify no default L2 gateway is created if bridge cluster id is # not configured in nsx.ini @@ -126,7 +132,7 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, [{"name": "interface_2"}], "device_name": "device2"}]}} self.assertRaises(n_exc.InvalidInput, - self.driver.create_l2_gateway, + self.l2gw_plugin.create_l2_gateway, self.context, invalid_l2gw_dict) def test_create_l2_gateway_multiple_interfaces_fail(self): @@ -139,7 +145,7 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, {"name": "interface2"}], "device_name": "device1"}]}} self.assertRaises(n_exc.InvalidInput, - self.driver.create_l2_gateway, + self.l2gw_plugin.create_l2_gateway, self.context, invalid_l2gw_dict) def test_create_l2_gateway_invalid_device_name_fail(self): @@ -151,14 +157,14 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, [{"name": "interface_1"}], "device_name": "device-1"}]}} self.assertRaises(n_exc.InvalidInput, - self.driver.create_l2_gateway, + self.l2gw_plugin.create_l2_gateway, self.context, invalid_l2gw_dict) def test_create_l2_gateway_valid(self): bc_uuid = uuidutils.generate_uuid() l2gw_data = self._get_l2_gateway_data(name='gw1', device_name=bc_uuid) - l2gw = self.driver.create_l2_gateway(self.context, l2gw_data) + l2gw = self.l2gw_plugin.create_l2_gateway(self.context, l2gw_data) self.assertIsNotNone(l2gw) self.assertEqual("gw1", l2gw["name"]) self.assertEqual("port1", @@ -177,8 +183,8 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, 'l2_gateway_id': l2gw['id'], 'tenant_id': 'fake_tenant_id', 'network_id': net['id']}} - l2gw_conn = self.driver.create_l2_gateway_connection(self.context, - l2gw_conn_data) + l2gw_conn = self.l2gw_plugin.create_l2_gateway_connection( + self.context, l2gw_conn_data) self.assertIsNotNone(l2gw_conn) self.assertEqual(net['id'], l2gw_conn['network_id']) self.assertEqual(l2gw['id'], l2gw_conn['l2_gateway_id']) @@ -195,13 +201,14 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, 'l2_gateway_id': l2gw['id'], 'tenant_id': 'fake_tenant_id', 'network_id': net['id']}} - l2gw_conn = self.driver.create_l2_gateway_connection(self.context, - l2gw_conn_data) - self.driver.delete_l2_gateway_connection(self.context, - l2gw_conn['id']) + l2gw_conn = self.l2gw_plugin.create_l2_gateway_connection( + self.context, + l2gw_conn_data) + self.l2gw_plugin.delete_l2_gateway_connection(self.context, + l2gw_conn['id']) # Verify that the L2 gateway connection was deleted self.assertRaises(l2gw_exc.L2GatewayConnectionNotFound, - self.driver.get_l2_gateway_connection, + self.l2gw_plugin.get_l2_gateway_connection, self.context, l2gw_conn['id']) ports = self.core_plugin.get_ports(self.context) # Verify that the L2 gateway connection port was cleaned up @@ -215,11 +222,13 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, l2gw = self._create_l2gateway(l2gw_data) net_data = self._get_nw_data() net = self.core_plugin.create_network(self.context, net_data) - l2gw_conn_data = {constants.CONNECTION_RESOURCE_NAME: { + l2gw_conn_data = { + 'id': uuidutils.generate_uuid(), 'l2_gateway_id': l2gw['id'], 'tenant_id': 'fake_tenant_id', - 'network_id': net['id']}} - self.driver.create_l2_gateway_connection(self.context, l2gw_conn_data) + 'network_id': net['id']} + self.driver.create_l2_gateway_connection_postcommit(self.context, + l2gw_conn_data) ports = self.core_plugin.get_ports(self.context) # Verify that the L2 gateway connection port was created with device # owner BRIDGEENDPOINT @@ -238,11 +247,13 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, l2gw = self._create_l2gateway(l2gw_data) net_data = self._get_nw_data() net = self.core_plugin.create_network(self.context, net_data) - l2gw_conn_data = {constants.CONNECTION_RESOURCE_NAME: { + l2gw_conn_data = { + 'id': uuidutils.generate_uuid(), 'l2_gateway_id': l2gw['id'], 'tenant_id': 'fake_tenant_id', - 'network_id': net['id']}} - self.driver.create_l2_gateway_connection(self.context, l2gw_conn_data) + 'network_id': net['id']} + self.driver.create_l2_gateway_connection_postcommit(self.context, + l2gw_conn_data) port = self.core_plugin.get_ports(self.context)[0] self.assertRaises(n_exc.ServicePortInUse, self.core_plugin.delete_port,