Merge "NSX|V3: Add L2GW connection validation"
This commit is contained in:
commit
b1093f0c41
|
@ -126,10 +126,6 @@ class NoRouterAvailable(n_exc.ResourceExhausted):
|
||||||
"No tenant router is available for allocation.")
|
"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):
|
class NsxL2GWDeviceNotFound(n_exc.NotFound):
|
||||||
message = _('Unable to find logical L2 gateway device.')
|
message = _('Unable to find logical L2 gateway device.')
|
||||||
|
|
||||||
|
|
|
@ -388,7 +388,12 @@ def get_l2gw_connection_mapping(session, connection_id):
|
||||||
return (session.query(nsx_models.NsxL2GWConnectionMapping).
|
return (session.query(nsx_models.NsxL2GWConnectionMapping).
|
||||||
filter_by(connection_id=connection_id).one())
|
filter_by(connection_id=connection_id).one())
|
||||||
except exc.NoResultFound:
|
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
|
# NSXv3 QoS policy id <-> switch Id mapping
|
||||||
|
|
|
@ -29,6 +29,7 @@ from neutron_lib.callbacks import registry
|
||||||
from neutron_lib.callbacks import resources
|
from neutron_lib.callbacks import resources
|
||||||
from neutron_lib import constants
|
from neutron_lib import constants
|
||||||
from neutron_lib import context
|
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 import exceptions as n_exc
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from neutron_lib.plugins import utils as plugin_utils
|
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)
|
network_id = gw_connection.get(l2gw_const.NETWORK_ID)
|
||||||
self._validate_network(context, 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):
|
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):
|
def create_l2_gateway_connection_postcommit(self, context, gw_connection):
|
||||||
"""Create a L2 gateway connection."""
|
"""Create a L2 gateway connection."""
|
||||||
l2gw_id = gw_connection.get(l2gw_const.L2GATEWAY_ID)
|
l2gw_id = gw_connection.get(l2gw_const.L2GATEWAY_ID)
|
||||||
network_id = gw_connection.get(l2gw_const.NETWORK_ID)
|
network_id = gw_connection.get(l2gw_const.NETWORK_ID)
|
||||||
devices = self._get_l2_gateway_devices(context, l2gw_id)
|
device_name = self._get_bridge_cluster(context, l2gw_id)
|
||||||
# In NSXv3, there will be only one device configured per L2 gateway.
|
seg_id = self._get_conn_seg_id(context, gw_connection)
|
||||||
# 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)
|
|
||||||
self._validate_segment_id(seg_id)
|
self._validate_segment_id(seg_id)
|
||||||
tenant_id = gw_connection['tenant_id']
|
tenant_id = gw_connection['tenant_id']
|
||||||
if context.is_admin and not tenant_id:
|
if context.is_admin and not tenant_id:
|
||||||
|
@ -272,9 +317,9 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin):
|
||||||
fixed_ip['ip_address'])
|
fixed_ip['ip_address'])
|
||||||
LOG.debug("IP addresses deallocated on port %s", port['id'])
|
LOG.debug("IP addresses deallocated on port %s", port['id'])
|
||||||
except (nsxlib_exc.ManagerError,
|
except (nsxlib_exc.ManagerError,
|
||||||
n_exc.NeutronException):
|
n_exc.NeutronException) as e:
|
||||||
LOG.exception("Unable to create L2 gateway port, "
|
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(
|
self._core_plugin.nsxlib.bridge_endpoint.delete(
|
||||||
bridge_endpoint['id'])
|
bridge_endpoint['id'])
|
||||||
raise l2gw_exc.L2GatewayServiceDriverError(
|
raise l2gw_exc.L2GatewayServiceDriverError(
|
||||||
|
@ -309,6 +354,11 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin):
|
||||||
conn_mapping = nsx_db.get_l2gw_connection_mapping(
|
conn_mapping = nsx_db.get_l2gw_connection_mapping(
|
||||||
session=context.session,
|
session=context.session,
|
||||||
connection_id=gw_connection)
|
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')
|
bridge_endpoint_id = conn_mapping.get('bridge_endpoint_id')
|
||||||
# Delete the logical port from the bridge endpoint.
|
# Delete the logical port from the bridge endpoint.
|
||||||
self._core_plugin.delete_port(context=context,
|
self._core_plugin.delete_port(context=context,
|
||||||
|
|
|
@ -191,6 +191,56 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase,
|
||||||
self.assertEqual(net['id'], l2gw_conn['network_id'])
|
self.assertEqual(net['id'], l2gw_conn['network_id'])
|
||||||
self.assertEqual(l2gw['id'], l2gw_conn['l2_gateway_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):
|
def test_delete_l2_gateway_connection(self):
|
||||||
type(self.driver)._core_plugin = self.core_plugin
|
type(self.driver)._core_plugin = self.core_plugin
|
||||||
bc_uuid = uuidutils.generate_uuid()
|
bc_uuid = uuidutils.generate_uuid()
|
||||||
|
@ -202,6 +252,7 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase,
|
||||||
l2gw_conn_data = {constants.CONNECTION_RESOURCE_NAME: {
|
l2gw_conn_data = {constants.CONNECTION_RESOURCE_NAME: {
|
||||||
'l2_gateway_id': l2gw['id'],
|
'l2_gateway_id': l2gw['id'],
|
||||||
'tenant_id': 'fake_tenant_id',
|
'tenant_id': 'fake_tenant_id',
|
||||||
|
'project_id': 'fake_tenant_id',
|
||||||
'network_id': net['id']}}
|
'network_id': net['id']}}
|
||||||
l2gw_conn = self.l2gw_plugin.create_l2_gateway_connection(
|
l2gw_conn = self.l2gw_plugin.create_l2_gateway_connection(
|
||||||
self.context,
|
self.context,
|
||||||
|
|
Loading…
Reference in New Issue