NSX|P: Allow only 1 router interface per network
Change-Id: Ia8c5ac5d8a9564d9cdb6e3e8831866b4a254abe2
This commit is contained in:
parent
a13d49b0ab
commit
c734899100
|
@ -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.db import utils as db_utils
|
||||||
from neutron_lib import exceptions as n_exc
|
from neutron_lib import exceptions as n_exc
|
||||||
from neutron_lib.exceptions import allowedaddresspairs as addr_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.exceptions import port_security as psec_exc
|
||||||
from neutron_lib.plugins import utils as plugin_utils
|
from neutron_lib.plugins import utils as plugin_utils
|
||||||
from neutron_lib.services.qos import constants as qos_consts
|
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)
|
'os-neutron-net-id', network_id)
|
||||||
if port_id:
|
if port_id:
|
||||||
self.nsxlib.logical_port.delete(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)
|
||||||
|
|
|
@ -1350,23 +1350,39 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||||
def add_router_interface(self, context, router_id, interface_info):
|
def add_router_interface(self, context, router_id, interface_info):
|
||||||
network_id = self._get_interface_network(context, interface_info)
|
network_id = self._get_interface_network(context, interface_info)
|
||||||
extern_net = self._network_is_external(context, network_id)
|
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)
|
router_db = self._get_router(context, router_id)
|
||||||
gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
|
gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
|
||||||
else None)
|
else None)
|
||||||
|
|
||||||
# A router interface cannot be an external network
|
with locking.LockManager.get_lock(str(network_id)):
|
||||||
if extern_net:
|
# disallow more than one subnets belong to same network being
|
||||||
msg = _("An external network cannot be attached as "
|
# attached to routers
|
||||||
"an interface to a router")
|
self._validate_multiple_subnets_routers(
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
context, router_id, network_id)
|
||||||
|
|
||||||
# Update the interface of the neutron router
|
# A router interface cannot be an external network
|
||||||
info = super(NsxPolicyPlugin, self).add_router_interface(
|
if extern_net:
|
||||||
context, router_id, interface_info)
|
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:
|
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
|
# Check GW & subnets TZ
|
||||||
subnets = self._find_router_subnets(context.elevated(), router_id)
|
subnets = self._find_router_subnets(context.elevated(), router_id)
|
||||||
tier0_uuid = self._get_tier0_uuid_by_router(
|
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):
|
def _get_net_dhcp_relay(self, context, net_id):
|
||||||
# No dhcp relay support yet
|
# No dhcp relay support yet
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _support_vlan_router_interfaces(self):
|
||||||
|
return True
|
||||||
|
|
|
@ -44,7 +44,6 @@ from neutron.extensions import providernet
|
||||||
from neutron.extensions import securitygroup as ext_sg
|
from neutron.extensions import securitygroup as ext_sg
|
||||||
from neutron.quota import resource_registry
|
from neutron.quota import resource_registry
|
||||||
from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
|
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.definitions import vlantransparent as vlan_apidef
|
||||||
from neutron_lib.api import validators
|
from neutron_lib.api import validators
|
||||||
from neutron_lib.callbacks import events
|
from neutron_lib.callbacks import events
|
||||||
|
@ -2637,44 +2636,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||||
address_groups.append(address_group)
|
address_groups.append(address_group)
|
||||||
return (ports, address_groups)
|
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,
|
def _add_router_interface_wrapper(self, context, router_id,
|
||||||
interface_info):
|
interface_info):
|
||||||
if cfg.CONF.api_replay_mode:
|
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):
|
def _get_net_dhcp_relay(self, context, net_id):
|
||||||
return self.get_network_az_by_net_id(
|
return self.get_network_az_by_net_id(
|
||||||
context, net_id).dhcp_relay_service
|
context, net_id).dhcp_relay_service
|
||||||
|
|
||||||
|
def _support_vlan_router_interfaces(self):
|
||||||
|
return self.nsxlib.feature_supported(
|
||||||
|
nsxlib_consts.FEATURE_VLAN_ROUTER_INTERFACE)
|
||||||
|
|
Loading…
Reference in New Issue