[NSXv3]: Refactor v3 L2 Gateway driver

Commit Ib56ee8bfd182c031e468c503acb0cd75daea8c40 refactored code
in L2 gateway base plugin. This patch makes appropriate changes
in NSX plugin and v3 driver.

Change-Id: I45d546e59e99d49d2a9b18258af94d90e91333ca
Partial-Bug: #1591413
This commit is contained in:
Abhishek Raut 2016-05-08 08:57:25 -07:00
parent b4c7750818
commit 6080794f94
5 changed files with 144 additions and 75 deletions

View File

@ -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',

View File

@ -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=

View File

@ -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):

View File

@ -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."""

View File

@ -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,