Merge "NSX|V3: Add L2GW connection validation"

This commit is contained in:
Zuul 2019-01-01 10:35:38 +00:00 committed by Gerrit Code Review
commit b1093f0c41
4 changed files with 123 additions and 21 deletions

View File

@ -126,10 +126,6 @@ class NoRouterAvailable(n_exc.ResourceExhausted):
"No tenant router is available for allocation.")
class NsxL2GWConnectionMappingNotFound(n_exc.NotFound):
message = _('Unable to find mapping for L2 gateway connection: %(conn)s')
class NsxL2GWDeviceNotFound(n_exc.NotFound):
message = _('Unable to find logical L2 gateway device.')

View File

@ -388,7 +388,12 @@ def get_l2gw_connection_mapping(session, connection_id):
return (session.query(nsx_models.NsxL2GWConnectionMapping).
filter_by(connection_id=connection_id).one())
except exc.NoResultFound:
raise nsx_exc.NsxL2GWConnectionMappingNotFound(conn=connection_id)
pass
def get_l2gw_connection_mappings_by_bridge(session, bridge_endpoint_id):
return (session.query(nsx_models.NsxL2GWConnectionMapping).
filter_by(bridge_endpoint_id=bridge_endpoint_id).all())
# NSXv3 QoS policy id <-> switch Id mapping

View File

@ -29,6 +29,7 @@ from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants
from neutron_lib import context
from neutron_lib.db import api as db_api
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
from neutron_lib.plugins import utils as plugin_utils
@ -211,26 +212,70 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin):
network_id = gw_connection.get(l2gw_const.NETWORK_ID)
self._validate_network(context, network_id)
def _get_bridge_cluster(self, 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.
devices = self._get_l2_gateway_devices(context, l2gw_id)
return devices[0].get('device_name')
def _get_conn_seg_id(self, context, gw_connection):
if not gw_connection:
return
seg_id = gw_connection.get(l2gw_const.SEG_ID)
if not seg_id:
# Seg-id was not passed as part of connection-create. Retrieve
# seg-id from L2 gateway's interface.
l2gw_id = gw_connection.get(l2gw_const.L2GATEWAY_ID)
devices = self._get_l2_gateway_devices(context, l2gw_id)
interface = self._get_l2_gw_interfaces(context, devices[0]['id'])
seg_id = interface[0].get(l2gw_const.SEG_ID)
return seg_id
def create_l2_gateway_connection_precommit(self, context, gw_connection):
pass
"""Validate the L2 gateway connection
Do not allow another connection with the same bride cluster and seg_id
"""
admin_ctx = context.elevated()
l2gw_id = gw_connection.get(l2gw_const.L2GATEWAY_ID)
seg_id = self._get_conn_seg_id(admin_ctx, gw_connection)
bridge_cluster = self._get_bridge_cluster(admin_ctx, l2gw_id)
# get all bridge endpoint ports
with db_api.CONTEXT_WRITER.using(admin_ctx):
port_filters = {'device_owner': [nsx_constants.BRIDGE_ENDPOINT]}
ports = self._core_plugin.get_ports(
admin_ctx, filters=port_filters)
for port in ports:
# get the nsx mapping by bridge endpoint
if port.get('device_id'):
mappings = nsx_db.get_l2gw_connection_mappings_by_bridge(
admin_ctx.session, port['device_id'])
for mapping in mappings:
conn_id = mapping.connection_id
# get the matching GW connection
conn = self._get_l2_gateway_connection(
admin_ctx, conn_id)
con_seg_id = self._get_conn_seg_id(admin_ctx, conn)
if (conn and con_seg_id and
int(con_seg_id) == int(seg_id)):
# compare the bridge cluster
conn_bridge_cluster = self._get_bridge_cluster(
admin_ctx, conn.l2_gateway_id)
if conn_bridge_cluster == bridge_cluster:
msg = (_("Cannot create multiple connections "
"with the same segmentation id "
"%(seg_id)s for bridge cluster "
"%(bridge)s") % {
'seg_id': seg_id,
'bridge': bridge_cluster})
raise n_exc.InvalidInput(error_message=msg)
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.
device_name = devices[0].get('device_name')
# The seg-id will be provided either during gateway create or gateway
# connection create. l2gateway_db_mixin makes sure that it is
# configured one way or the other.
seg_id = gw_connection.get(l2gw_const.SEG_ID)
if not seg_id:
# Seg-id was not passed as part of connection-create. Retrieve
# seg-id from L2 gateway's interface.
interface = self._get_l2_gw_interfaces(context, devices[0]['id'])
seg_id = interface[0].get(l2gw_const.SEG_ID)
device_name = self._get_bridge_cluster(context, l2gw_id)
seg_id = self._get_conn_seg_id(context, gw_connection)
self._validate_segment_id(seg_id)
tenant_id = gw_connection['tenant_id']
if context.is_admin and not tenant_id:
@ -272,9 +317,9 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin):
fixed_ip['ip_address'])
LOG.debug("IP addresses deallocated on port %s", port['id'])
except (nsxlib_exc.ManagerError,
n_exc.NeutronException):
n_exc.NeutronException) as e:
LOG.exception("Unable to create L2 gateway port, "
"rolling back changes on neutron")
"rolling back changes on neutron: %s", e)
self._core_plugin.nsxlib.bridge_endpoint.delete(
bridge_endpoint['id'])
raise l2gw_exc.L2GatewayServiceDriverError(
@ -309,6 +354,11 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin):
conn_mapping = nsx_db.get_l2gw_connection_mapping(
session=context.session,
connection_id=gw_connection)
if not conn_mapping:
LOG.error("Unable to delete gateway connection %(id)s: mapping "
"not found", {'id': gw_connection})
# Do not block the deletion
return
bridge_endpoint_id = conn_mapping.get('bridge_endpoint_id')
# Delete the logical port from the bridge endpoint.
self._core_plugin.delete_port(context=context,

View File

@ -191,6 +191,56 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase,
self.assertEqual(net['id'], l2gw_conn['network_id'])
self.assertEqual(l2gw['id'], l2gw_conn['l2_gateway_id'])
def test_create_l2_gateway_connections_same_params(self):
type(self.driver)._core_plugin = self.core_plugin
bc_uuid = uuidutils.generate_uuid()
l2gw_data1 = self._get_l2_gateway_data(name='def-l2gw1',
device_name=bc_uuid)
l2gw1 = self._create_l2gateway(l2gw_data1)
l2gw_data2 = self._get_l2_gateway_data(name='def-l2gw2',
device_name=bc_uuid)
l2gw2 = self._create_l2gateway(l2gw_data2)
net_data = self._get_nw_data()
net = self.core_plugin.create_network(self.context, net_data)
l2gw_conn_data1 = {constants.CONNECTION_RESOURCE_NAME: {
'l2_gateway_id': l2gw1['id'],
'tenant_id': 'fake_tenant_id',
'network_id': net['id']}}
self.l2gw_plugin.create_l2_gateway_connection(
self.context, l2gw_conn_data1)
l2gw_conn_data2 = {constants.CONNECTION_RESOURCE_NAME: {
'l2_gateway_id': l2gw2['id'],
'tenant_id': 'fake_tenant_id',
'network_id': net['id']}}
self.assertRaises(n_exc.InvalidInput,
self.l2gw_plugin.create_l2_gateway_connection,
self.context, l2gw_conn_data2)
def test_create_l2_gateway_connections_different_bridge(self):
type(self.driver)._core_plugin = self.core_plugin
bc_uuid1 = uuidutils.generate_uuid()
bc_uuid2 = uuidutils.generate_uuid()
l2gw_data1 = self._get_l2_gateway_data(name='def-l2gw1',
device_name=bc_uuid1)
l2gw1 = self._create_l2gateway(l2gw_data1)
l2gw_data2 = self._get_l2_gateway_data(name='def-l2gw2',
device_name=bc_uuid2)
l2gw2 = self._create_l2gateway(l2gw_data2)
net_data = self._get_nw_data()
net = self.core_plugin.create_network(self.context, net_data)
l2gw_conn_data1 = {constants.CONNECTION_RESOURCE_NAME: {
'l2_gateway_id': l2gw1['id'],
'tenant_id': 'fake_tenant_id',
'network_id': net['id']}}
self.l2gw_plugin.create_l2_gateway_connection(
self.context, l2gw_conn_data1)
l2gw_conn_data2 = {constants.CONNECTION_RESOURCE_NAME: {
'l2_gateway_id': l2gw2['id'],
'tenant_id': 'fake_tenant_id',
'network_id': net['id']}}
self.l2gw_plugin.create_l2_gateway_connection(
self.context, l2gw_conn_data2)
def test_delete_l2_gateway_connection(self):
type(self.driver)._core_plugin = self.core_plugin
bc_uuid = uuidutils.generate_uuid()
@ -202,6 +252,7 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase,
l2gw_conn_data = {constants.CONNECTION_RESOURCE_NAME: {
'l2_gateway_id': l2gw['id'],
'tenant_id': 'fake_tenant_id',
'project_id': 'fake_tenant_id',
'network_id': net['id']}}
l2gw_conn = self.l2gw_plugin.create_l2_gateway_connection(
self.context,