Merge "NSX-V3: Enhance VPNaaS related validations"
This commit is contained in:
commit
4277b21c5a
@ -38,7 +38,7 @@ from vmware_nsxlib.v3 import vpn_ipsec
|
|||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
IPSEC = 'ipsec'
|
IPSEC = 'ipsec'
|
||||||
VPN_PORT_OWNER = constants.DEVICE_OWNER_NEUTRON_PREFIX + 'vpnservice'
|
VPN_PORT_OWNER = 'vpnservice'
|
||||||
|
|
||||||
|
|
||||||
class RouterWithSNAT(nexception.BadRequest):
|
class RouterWithSNAT(nexception.BadRequest):
|
||||||
@ -46,6 +46,16 @@ class RouterWithSNAT(nexception.BadRequest):
|
|||||||
"SNAT")
|
"SNAT")
|
||||||
|
|
||||||
|
|
||||||
|
class RouterWithOverlapNoSnat(nexception.BadRequest):
|
||||||
|
message = _("Router %(router_id)s has a subnet overlapping with a VPN "
|
||||||
|
"local subnet, and cannot disable SNAT")
|
||||||
|
|
||||||
|
|
||||||
|
class RouterOverlapping(nexception.BadRequest):
|
||||||
|
message = _("Router %(router_id)s interface is overlapping with a VPN "
|
||||||
|
"local subnet and cannot be added")
|
||||||
|
|
||||||
|
|
||||||
class NSXv3IPsecVpnDriver(service_drivers.VpnDriver):
|
class NSXv3IPsecVpnDriver(service_drivers.VpnDriver):
|
||||||
|
|
||||||
def __init__(self, service_plugin):
|
def __init__(self, service_plugin):
|
||||||
@ -63,6 +73,10 @@ class NSXv3IPsecVpnDriver(service_drivers.VpnDriver):
|
|||||||
self._delete_local_endpoint, resources.ROUTER_GATEWAY,
|
self._delete_local_endpoint, resources.ROUTER_GATEWAY,
|
||||||
events.AFTER_DELETE)
|
events.AFTER_DELETE)
|
||||||
|
|
||||||
|
registry.subscribe(
|
||||||
|
self._verify_overlap_subnet, resources.ROUTER_INTERFACE,
|
||||||
|
events.BEFORE_CREATE)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def l3_plugin(self):
|
def l3_plugin(self):
|
||||||
return self._core_plugin
|
return self._core_plugin
|
||||||
@ -364,7 +378,7 @@ class NSXv3IPsecVpnDriver(service_drivers.VpnDriver):
|
|||||||
|
|
||||||
def _find_vpn_service_port(self, context, router_id):
|
def _find_vpn_service_port(self, context, router_id):
|
||||||
"""Look for the neutron port created for the vpnservice of a router"""
|
"""Look for the neutron port created for the vpnservice of a router"""
|
||||||
filters = {'device_id': [router_id],
|
filters = {'device_id': ['router-' + router_id],
|
||||||
'device_owner': [VPN_PORT_OWNER]}
|
'device_owner': [VPN_PORT_OWNER]}
|
||||||
ports = self.l3_plugin.get_ports(context, filters=filters)
|
ports = self.l3_plugin.get_ports(context, filters=filters)
|
||||||
if ports:
|
if ports:
|
||||||
@ -383,18 +397,68 @@ class NSXv3IPsecVpnDriver(service_drivers.VpnDriver):
|
|||||||
if port:
|
if port:
|
||||||
self.l3_plugin.delete_port(ctx, port['id'])
|
self.l3_plugin.delete_port(ctx, port['id'])
|
||||||
|
|
||||||
|
def _check_subnets_overlap_with_all_conns(self, context, subnets):
|
||||||
|
# find all vpn services with connections
|
||||||
|
filters = {'status': [constants.ACTIVE]}
|
||||||
|
connections = self.vpn_plugin.get_ipsec_site_connections(
|
||||||
|
context, filters=filters)
|
||||||
|
for conn in connections:
|
||||||
|
srv_id = conn.get('vpnservice_id')
|
||||||
|
srv = self.vpn_plugin._get_vpnservice(context, srv_id)
|
||||||
|
srv_subnet = self.l3_plugin.get_subnet(
|
||||||
|
context, srv['subnet_id'])
|
||||||
|
if netaddr.IPSet(subnets) & netaddr.IPSet([srv_subnet['cidr']]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _verify_overlap_subnet(self, resource, event, trigger, **kwargs):
|
||||||
|
"""Upon router interface creation validation overlapping with vpn"""
|
||||||
|
router_db = kwargs.get('router_db')
|
||||||
|
port = kwargs.get('port')
|
||||||
|
if not port or not router_db:
|
||||||
|
LOG.warning("NSX V3 VPNaaS ROUTER_INTERFACE BEFORE_CRAETE "
|
||||||
|
"callback didn't get all the relevant information")
|
||||||
|
return
|
||||||
|
|
||||||
|
if router_db.enable_snat:
|
||||||
|
# checking only no-snat routers
|
||||||
|
return
|
||||||
|
|
||||||
|
admin_con = n_context.get_admin_context()
|
||||||
|
subnet_id = port['fixed_ips'][0].get('subnet_id')
|
||||||
|
if subnet_id:
|
||||||
|
subnet = self._core_plugin.get_subnet(admin_con, subnet_id)
|
||||||
|
# find all vpn services with connections
|
||||||
|
if not self._check_subnets_overlap_with_all_conns(
|
||||||
|
admin_con, [subnet['cidr']]):
|
||||||
|
raise RouterOverlapping(router_id=kwargs.get('router_id'))
|
||||||
|
|
||||||
def validate_router_gw_info(self, context, router_id, gw_info):
|
def validate_router_gw_info(self, context, router_id, gw_info):
|
||||||
"""Upon router gw update - verify no-snat"""
|
"""Upon router gw update - verify no-snat"""
|
||||||
# ckeck if this router has a vpn service
|
# check if this router has a vpn service
|
||||||
|
admin_con = context.elevated()
|
||||||
filters = {'router_id': [router_id],
|
filters = {'router_id': [router_id],
|
||||||
'status': [constants.ACTIVE]}
|
'status': [constants.ACTIVE]}
|
||||||
services = self.vpn_plugin.get_vpnservices(
|
services = self.vpn_plugin.get_vpnservices(admin_con, filters=filters)
|
||||||
context.elevated(), filters=filters)
|
|
||||||
if services:
|
if services:
|
||||||
# do not allow enable-snat
|
# do not allow enable-snat
|
||||||
if (gw_info and
|
if (gw_info and
|
||||||
gw_info.get('enable_snat', cfg.CONF.enable_snat_by_default)):
|
gw_info.get('enable_snat', cfg.CONF.enable_snat_by_default)):
|
||||||
raise RouterWithSNAT(router_id=router_id)
|
raise RouterWithSNAT(router_id=router_id)
|
||||||
|
else:
|
||||||
|
# if this is a non-vpn router. if snat was disabled, should check
|
||||||
|
# there is no overlapping with vpn connections
|
||||||
|
if (gw_info and
|
||||||
|
not gw_info.get('enable_snat',
|
||||||
|
cfg.CONF.enable_snat_by_default)):
|
||||||
|
# get router subnets
|
||||||
|
subnets = self._core_plugin._find_router_subnets_cidrs(
|
||||||
|
context, router_id)
|
||||||
|
# find all vpn services with connections
|
||||||
|
if not self._check_subnets_overlap_with_all_conns(
|
||||||
|
admin_con, subnets):
|
||||||
|
raise RouterWithOverlapNoSnat(router_id=router_id)
|
||||||
|
|
||||||
def _get_session_rules(self, context, connection, vpnservice):
|
def _get_session_rules(self, context, connection, vpnservice):
|
||||||
# TODO(asarfaty): support vpn-endpoint-groups too
|
# TODO(asarfaty): support vpn-endpoint-groups too
|
||||||
@ -600,7 +664,7 @@ class NSXv3IPsecVpnDriver(service_drivers.VpnDriver):
|
|||||||
def _create_vpn_service(self, tier0_uuid):
|
def _create_vpn_service(self, tier0_uuid):
|
||||||
try:
|
try:
|
||||||
service = self._nsx_vpn.service.create(
|
service = self._nsx_vpn.service.create(
|
||||||
'Neutron VPN service for router ' + tier0_uuid,
|
'Neutron VPN service for T0 router ' + tier0_uuid,
|
||||||
tier0_uuid,
|
tier0_uuid,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
ike_log_level=ipsec_utils.DEFAULT_LOG_LEVEL,
|
ike_log_level=ipsec_utils.DEFAULT_LOG_LEVEL,
|
||||||
@ -658,13 +722,15 @@ class NSXv3IPsecVpnDriver(service_drivers.VpnDriver):
|
|||||||
port = self._find_vpn_service_port(context, router_id)
|
port = self._find_vpn_service_port(context, router_id)
|
||||||
if not port:
|
if not port:
|
||||||
# create a new port, on the external network of the router
|
# create a new port, on the external network of the router
|
||||||
|
# Note(asarfaty): using a unique device owner and device id to
|
||||||
|
# make sure tis port will be ignored in certain queries
|
||||||
ext_net = vpnservice.router.gw_port['network_id']
|
ext_net = vpnservice.router.gw_port['network_id']
|
||||||
port_data = {
|
port_data = {
|
||||||
'port': {
|
'port': {
|
||||||
'network_id': ext_net,
|
'network_id': ext_net,
|
||||||
'name': None,
|
'name': 'VPN local address port',
|
||||||
'admin_state_up': True,
|
'admin_state_up': True,
|
||||||
'device_id': vpnservice.router['id'],
|
'device_id': 'router-' + vpnservice.router['id'],
|
||||||
'device_owner': VPN_PORT_OWNER,
|
'device_owner': VPN_PORT_OWNER,
|
||||||
'fixed_ips': constants.ATTR_NOT_SPECIFIED,
|
'fixed_ips': constants.ATTR_NOT_SPECIFIED,
|
||||||
'mac_address': constants.ATTR_NOT_SPECIFIED,
|
'mac_address': constants.ATTR_NOT_SPECIFIED,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from neutron_lib import constants
|
from neutron_lib import constants
|
||||||
@ -235,7 +236,8 @@ class IPsecV3Validator(vpn_validator.VpnReferenceValidator):
|
|||||||
if (rtr['id'] != this_router and
|
if (rtr['id'] != this_router and
|
||||||
rtr.get('external_gateway_info') and
|
rtr.get('external_gateway_info') and
|
||||||
not rtr['external_gateway_info'].get(
|
not rtr['external_gateway_info'].get(
|
||||||
'enable_snat', True))]
|
'enable_snat',
|
||||||
|
cfg.CONF.enable_snat_by_default))]
|
||||||
for rtr in nosnat_routers:
|
for rtr in nosnat_routers:
|
||||||
if rtr['id'] == this_router:
|
if rtr['id'] == this_router:
|
||||||
continue
|
continue
|
||||||
@ -271,9 +273,6 @@ class IPsecV3Validator(vpn_validator.VpnReferenceValidator):
|
|||||||
'conn': conn['id']})
|
'conn': conn['id']})
|
||||||
raise nsx_exc.NsxVpnValidationError(details=msg)
|
raise nsx_exc.NsxVpnValidationError(details=msg)
|
||||||
|
|
||||||
# TODO(asarfaty): also add this validation when adding an interface
|
|
||||||
# or no-snat to a router through the nsx-v3 plugin
|
|
||||||
|
|
||||||
def validate_ipsec_site_connection(self, context, ipsec_site_conn):
|
def validate_ipsec_site_connection(self, context, ipsec_site_conn):
|
||||||
"""Called upon create/update of a connection"""
|
"""Called upon create/update of a connection"""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user