NSX|P: Allow only 1 router interface per network

Change-Id: Ia8c5ac5d8a9564d9cdb6e3e8831866b4a254abe2
This commit is contained in:
Adit Sarfaty 2019-01-17 16:22:34 +02:00
parent a13d49b0ab
commit c734899100
3 changed files with 74 additions and 48 deletions

View File

@ -56,6 +56,7 @@ from neutron_lib.db import api as db_api
from neutron_lib.db import utils as db_utils
from neutron_lib import exceptions as n_exc
from neutron_lib.exceptions import allowedaddresspairs as addr_exc
from neutron_lib.exceptions import l3 as l3_exc
from neutron_lib.exceptions import port_security as psec_exc
from neutron_lib.plugins import utils as plugin_utils
from neutron_lib.services.qos import constants as qos_consts
@ -2352,3 +2353,44 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
'os-neutron-net-id', network_id)
if port_id:
self.nsxlib.logical_port.delete(port_id)
def _support_vlan_router_interfaces(self):
"""Should be implemented by each plugin"""
pass
def _validate_multiple_subnets_routers(self, context, router_id, net_id):
network = self.get_network(context, net_id)
net_type = network.get(pnet.NETWORK_TYPE)
if (net_type and
not self._support_vlan_router_interfaces() and
not self._is_overlay_network(context, net_id)):
err_msg = (_("Only overlay networks can be attached to a logical "
"router. Network %(net_id)s is a %(net_type)s based "
"network") % {'net_id': net_id, 'net_type': net_type})
LOG.error(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
# Unable to attach a trunked network to a router interface
if cfg.CONF.vlan_transparent:
if network.get('vlan_transparent') is True:
err_msg = (_("Transparent VLAN networks cannot be attached to "
"a logical router."))
LOG.error(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
port_filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF],
'network_id': [net_id]}
intf_ports = self.get_ports(context.elevated(), filters=port_filters)
router_ids = [port['device_id']
for port in intf_ports if port['device_id']]
if len(router_ids) > 0:
err_msg = _("Only one subnet of network %(net_id)s can be "
"attached to router, one subnet is already attached "
"to router %(router_id)s") % {
'net_id': net_id,
'router_id': router_ids[0]}
LOG.error(err_msg)
if router_id in router_ids:
# attach to the same router again
raise n_exc.InvalidInput(error_message=err_msg)
else:
# attach to multiple routers
raise l3_exc.RouterInterfaceAttachmentConflict(reason=err_msg)

View File

@ -1350,23 +1350,39 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
def add_router_interface(self, context, router_id, interface_info):
network_id = self._get_interface_network(context, interface_info)
extern_net = self._network_is_external(context, network_id)
overlay_net = self._is_overlay_network(context, network_id)
router_db = self._get_router(context, router_id)
gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
else None)
# A router interface cannot be an external network
if extern_net:
msg = _("An external network cannot be attached as "
"an interface to a router")
raise n_exc.InvalidInput(error_message=msg)
with locking.LockManager.get_lock(str(network_id)):
# disallow more than one subnets belong to same network being
# attached to routers
self._validate_multiple_subnets_routers(
context, router_id, network_id)
# Update the interface of the neutron router
info = super(NsxPolicyPlugin, self).add_router_interface(
context, router_id, interface_info)
# A router interface cannot be an external network
if extern_net:
msg = _("An external network cannot be attached as "
"an interface to a router")
raise n_exc.InvalidInput(error_message=msg)
self._validate_interface_address_scope(context, router_db, info)
# Non overlay networks should be configured with a centralized
# router, which is allowed only if GW network is attached
if not overlay_net and not gw_network_id:
msg = _("A router attached to a VLAN backed network "
"must have an external network assigned")
raise n_exc.InvalidInput(error_message=msg)
# Update the interface of the neutron router
info = super(NsxPolicyPlugin, self).add_router_interface(
context, router_id, interface_info)
try:
# If it is a no-snat router, interface address scope must be the
# same as the gateways
self._validate_interface_address_scope(context, router_db, info)
# Check GW & subnets TZ
subnets = self._find_router_subnets(context.elevated(), router_id)
tier0_uuid = self._get_tier0_uuid_by_router(
@ -2091,3 +2107,6 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
def _get_net_dhcp_relay(self, context, net_id):
# No dhcp relay support yet
return None
def _support_vlan_router_interfaces(self):
return True

View File

@ -44,7 +44,6 @@ from neutron.extensions import providernet
from neutron.extensions import securitygroup as ext_sg
from neutron.quota import resource_registry
from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
from neutron_lib.api.definitions import provider_net as pnet
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
from neutron_lib.api import validators
from neutron_lib.callbacks import events
@ -2637,44 +2636,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
address_groups.append(address_group)
return (ports, address_groups)
def _validate_multiple_subnets_routers(self, context, router_id, net_id):
network = self.get_network(context, net_id)
net_type = network.get(pnet.NETWORK_TYPE)
if (net_type and
not self.nsxlib.feature_supported(
nsxlib_consts.FEATURE_VLAN_ROUTER_INTERFACE) and
not self._is_overlay_network(context, net_id)):
err_msg = (_("Only overlay networks can be attached to a logical "
"router. Network %(net_id)s is a %(net_type)s based "
"network") % {'net_id': net_id, 'net_type': net_type})
LOG.error(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
# Unable to attach a trunked network to a router interface
if cfg.CONF.vlan_transparent:
if network.get('vlan_transparent') is True:
err_msg = (_("Transparent VLAN networks cannot be attached to "
"a logical router."))
LOG.error(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
port_filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF],
'network_id': [net_id]}
intf_ports = self.get_ports(context.elevated(), filters=port_filters)
router_ids = [port['device_id']
for port in intf_ports if port['device_id']]
if len(router_ids) > 0:
err_msg = _("Only one subnet of network %(net_id)s can be "
"attached to router, one subnet is already attached "
"to router %(router_id)s") % {
'net_id': net_id,
'router_id': router_ids[0]}
LOG.error(err_msg)
if router_id in router_ids:
# attach to the same router again
raise n_exc.InvalidInput(error_message=err_msg)
else:
# attach to multiple routers
raise l3_exc.RouterInterfaceAttachmentConflict(reason=err_msg)
def _add_router_interface_wrapper(self, context, router_id,
interface_info):
if cfg.CONF.api_replay_mode:
@ -3418,3 +3379,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
def _get_net_dhcp_relay(self, context, net_id):
return self.get_network_az_by_net_id(
context, net_id).dhcp_relay_service
def _support_vlan_router_interfaces(self):
return self.nsxlib.feature_supported(
nsxlib_consts.FEATURE_VLAN_ROUTER_INTERFACE)