50afa71853
1.Upgrade pylint to 2.4.4, add exclusions to the tests, and fix some lint errors in the code 2. Fix user creation with GRANT in MySQL 8.0(Ubuntu Focal) In Ubuntu Bionic (18.04) mysql 5.7 version used to create the user implicitly when using using the GRANT. Ubuntu Focal (20.04) has mysql 8.0 and with mysql 8.0 there is no implicit user creation with GRANT. We need to create the user first before using GRANT command. See also commit I97b0dcbb88c6ef7c22e3c55970211bed792bbd0d 3. Remove fwaas from the zuul.yaml 4. Remove DB migration test which is failing ue to FWaaS migration with py38 5. Fix cover tests python version in .tox 6. fix requirememnts Change-Id: I22654a5d5ccaad3185ae3365a90afba1ce870695
352 lines
16 KiB
Python
352 lines
16 KiB
Python
# Copyright 2016 VMware, Inc.
|
|
# All Rights Reserved
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import netaddr
|
|
from neutron_lib.plugins import directory
|
|
from neutron_vpnaas.services.vpn import service_drivers
|
|
from oslo_log import log as logging
|
|
from oslo_utils import excutils
|
|
|
|
from vmware_nsx._i18n import _
|
|
from vmware_nsx.common import exceptions as nsxv_exc
|
|
from vmware_nsx.common import locking
|
|
from vmware_nsx.common import nsxv_constants
|
|
from vmware_nsx.db import nsxv_db
|
|
from vmware_nsx.extensions import projectpluginmap
|
|
from vmware_nsx.plugins.nsx_v.vshield.common import exceptions as vcns_exc
|
|
from vmware_nsx.services.vpnaas.nsxv import ipsec_validator
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
IPSEC = 'ipsec'
|
|
|
|
|
|
class NSXvIPsecVpnDriver(service_drivers.VpnDriver):
|
|
|
|
def __init__(self, service_plugin):
|
|
self._core_plugin = directory.get_plugin()
|
|
if self._core_plugin.is_tvd_plugin():
|
|
self._core_plugin = self._core_plugin.get_plugin_by_type(
|
|
projectpluginmap.NsxPlugins.NSX_V)
|
|
self._vcns = self._core_plugin.nsx_v.vcns
|
|
validator = ipsec_validator.IPsecValidator(service_plugin)
|
|
super(NSXvIPsecVpnDriver, self).__init__(service_plugin, validator)
|
|
|
|
@property
|
|
def l3_plugin(self):
|
|
return self._core_plugin
|
|
|
|
@property
|
|
def service_type(self):
|
|
return IPSEC
|
|
|
|
def _get_router_edge_id(self, context, vpnservice_id):
|
|
vpnservice = self.service_plugin._get_vpnservice(context,
|
|
vpnservice_id)
|
|
router_id = vpnservice['router_id']
|
|
edge_binding = nsxv_db.get_nsxv_router_binding(context.session,
|
|
router_id)
|
|
if not edge_binding:
|
|
msg = _("Couldn't find edge binding for router %s") % router_id
|
|
raise nsxv_exc.NsxPluginException(err_msg=msg)
|
|
|
|
if edge_binding['edge_type'] == nsxv_constants.VDR_EDGE:
|
|
edge_manager = self._core_plugin.edge_manager
|
|
router_id = edge_manager.get_plr_by_tlr_id(context, router_id)
|
|
binding = nsxv_db.get_nsxv_router_binding(context.session,
|
|
router_id)
|
|
edge_id = binding['edge_id']
|
|
else:
|
|
# Get exclusive edge id
|
|
edge_id = edge_binding['edge_id']
|
|
return router_id, edge_id
|
|
|
|
def _convert_ipsec_conn(self, context, ipsec_site_connection):
|
|
ipsec_id = ipsec_site_connection['ipsecpolicy_id']
|
|
vpnservice_id = ipsec_site_connection['vpnservice_id']
|
|
ipsecpolicy = self.service_plugin.get_ipsecpolicy(context, ipsec_id)
|
|
vpnservice = self.service_plugin._get_vpnservice(context,
|
|
vpnservice_id)
|
|
local_cidr = vpnservice['subnet']['cidr']
|
|
router_id = vpnservice['router_id']
|
|
router = self._core_plugin.get_router(context, router_id)
|
|
local_addr = (router['external_gateway_info']['external_fixed_ips']
|
|
[0]["ip_address"])
|
|
encrypt = nsxv_constants.ENCRYPTION_ALGORITHM_MAP.get(
|
|
ipsecpolicy.get('encryption_algorithm'))
|
|
site = {
|
|
'enabled': True,
|
|
'enablePfs': True,
|
|
'dhGroup': nsxv_constants.PFS_MAP.get(ipsecpolicy.get('pfs')),
|
|
'name': ipsec_site_connection.get('name'),
|
|
'description': ipsec_site_connection.get('description'),
|
|
'localId': local_addr,
|
|
'localIp': local_addr,
|
|
'peerId': ipsec_site_connection['peer_id'],
|
|
'peerIp': ipsec_site_connection.get('peer_address'),
|
|
'localSubnets': {
|
|
'subnets': [local_cidr]},
|
|
'peerSubnets': {
|
|
'subnets': ipsec_site_connection.get('peer_cidrs')},
|
|
'authenticationMode': ipsec_site_connection.get('auth_mode'),
|
|
'psk': ipsec_site_connection.get('psk'),
|
|
'encryptionAlgorithm': encrypt
|
|
}
|
|
return site
|
|
|
|
def _generate_new_sites(self, edge_id, ipsec_site_conn):
|
|
# Fetch the previous ipsec vpn configuration
|
|
ipsecvpn_configs = self._get_ipsec_config(edge_id)
|
|
vse_sites = []
|
|
if ipsecvpn_configs[1]['enabled']:
|
|
vse_sites = ([site for site
|
|
in ipsecvpn_configs[1]['sites']['sites']])
|
|
vse_sites.append(ipsec_site_conn)
|
|
return vse_sites
|
|
|
|
def _generate_ipsecvpn_firewall_rules(self, plugin_type, context,
|
|
edge_id=None):
|
|
ipsecvpn_configs = self._get_ipsec_config(edge_id)
|
|
ipsec_vpn_fw_rules = []
|
|
if ipsecvpn_configs[1]['enabled']:
|
|
for site in ipsecvpn_configs[1]['sites']['sites']:
|
|
peer_subnets = site['peerSubnets']['subnets']
|
|
local_subnets = site['localSubnets']['subnets']
|
|
ipsec_vpn_fw_rules.append({
|
|
'name': 'VPN ' + site.get('name', 'rule'),
|
|
'action': 'allow',
|
|
'enabled': True,
|
|
'source_ip_address': peer_subnets,
|
|
'destination_ip_address': local_subnets})
|
|
return ipsec_vpn_fw_rules
|
|
|
|
def _update_firewall_rules(self, context, vpnservice_id):
|
|
vpnservice = self.service_plugin._get_vpnservice(context,
|
|
vpnservice_id)
|
|
router_db = (
|
|
self._core_plugin._get_router(context, vpnservice['router_id']))
|
|
self._core_plugin._update_subnets_and_dnat_firewall(context,
|
|
router_db)
|
|
|
|
def _update_status(self, context, vpn_service_id, ipsec_site_conn_id,
|
|
status, updated_pending_status=True):
|
|
status_list = []
|
|
vpn_status = {}
|
|
ipsec_site_conn = {}
|
|
vpn_status['id'] = vpn_service_id
|
|
vpn_status['updated_pending_status'] = updated_pending_status
|
|
vpn_status['status'] = status
|
|
ipsec_site_conn['status'] = status
|
|
ipsec_site_conn['updated_pending_status'] = updated_pending_status
|
|
vpn_status['ipsec_site_connections'] = {ipsec_site_conn_id:
|
|
ipsec_site_conn}
|
|
status_list.append(vpn_status)
|
|
self.service_plugin.update_status_by_agent(context, status_list)
|
|
|
|
def create_ipsec_site_connection(self, context, ipsec_site_connection):
|
|
LOG.debug('Creating ipsec site connection %(conn_info)s.',
|
|
{"conn_info": ipsec_site_connection})
|
|
new_ipsec = self._convert_ipsec_conn(context, ipsec_site_connection)
|
|
vpnservice_id = ipsec_site_connection['vpnservice_id']
|
|
edge_id = self._get_router_edge_id(context, vpnservice_id)[1]
|
|
with locking.LockManager.get_lock(edge_id):
|
|
vse_sites = self._generate_new_sites(edge_id, new_ipsec)
|
|
ipsec_id = ipsec_site_connection["id"]
|
|
try:
|
|
LOG.debug('Updating ipsec vpn configuration %(vse_sites)s.',
|
|
{'vse_sites': vse_sites})
|
|
self._update_ipsec_config(edge_id, vse_sites, enabled=True)
|
|
except vcns_exc.VcnsApiException:
|
|
self._update_status(context, vpnservice_id, ipsec_id,
|
|
"ERROR")
|
|
msg = (_("Failed to create ipsec site connection "
|
|
"configuration with %(edge_id)s.") %
|
|
{'edge_id': edge_id})
|
|
raise nsxv_exc.NsxPluginException(err_msg=msg)
|
|
|
|
LOG.debug('Updating ipsec vpn firewall')
|
|
try:
|
|
self._update_firewall_rules(context, vpnservice_id)
|
|
except vcns_exc.VcnsApiException:
|
|
self._update_status(context, vpnservice_id, ipsec_id, "ERROR")
|
|
msg = (_("Failed to update firewall rule for ipsec vpn "
|
|
"with %(edge_id)s.") % {'edge_id': edge_id})
|
|
raise nsxv_exc.NsxPluginException(err_msg=msg)
|
|
self._update_status(context, vpnservice_id, ipsec_id, "ACTIVE")
|
|
|
|
def _get_ipsec_config(self, edge_id):
|
|
return self._vcns.get_ipsec_config(edge_id)
|
|
|
|
def delete_ipsec_site_connection(self, context, ipsec_site_conn):
|
|
LOG.debug('Deleting ipsec site connection %(site)s.',
|
|
{"site": ipsec_site_conn})
|
|
ipsec_id = ipsec_site_conn['id']
|
|
edge_id = self._get_router_edge_id(context,
|
|
ipsec_site_conn['vpnservice_id'])[1]
|
|
with locking.LockManager.get_lock(edge_id):
|
|
del_site, vse_sites = self._find_vse_site(context, edge_id,
|
|
ipsec_site_conn)
|
|
if not del_site:
|
|
LOG.error("Failed to find ipsec_site_connection "
|
|
"%(ipsec_site_conn)s with %(edge_id)s.",
|
|
{'ipsec_site_conn': ipsec_site_conn,
|
|
'edge_id': edge_id})
|
|
raise nsxv_exc.NsxIPsecVpnMappingNotFound(conn=ipsec_id)
|
|
|
|
vse_sites.remove(del_site)
|
|
enabled = bool(vse_sites)
|
|
try:
|
|
self._update_ipsec_config(edge_id, vse_sites, enabled)
|
|
except vcns_exc.VcnsApiException:
|
|
msg = (_("Failed to delete ipsec site connection "
|
|
"configuration with edge_id: %(edge_id)s.") %
|
|
{'egde_id': edge_id})
|
|
raise nsxv_exc.NsxPluginException(err_msg=msg)
|
|
try:
|
|
self._update_firewall_rules(context,
|
|
ipsec_site_conn['vpnservice_id'])
|
|
except vcns_exc.VcnsApiException:
|
|
msg = _("Failed to update firewall rule for ipsec vpn with "
|
|
"%(edge_id)s.") % {'edge_id': edge_id}
|
|
raise nsxv_exc.NsxPluginException(err_msg=msg)
|
|
|
|
def _find_vse_site(self, context, edge_id, site):
|
|
# Fetch the previous ipsec vpn configuration
|
|
ipsecvpn_configs = self._get_ipsec_config(edge_id)[1]
|
|
vpnservice = self.service_plugin._get_vpnservice(context,
|
|
site['vpnservice_id'])
|
|
local_cidr = vpnservice['subnet']['cidr']
|
|
old_site = None
|
|
vse_sites = None
|
|
if ipsecvpn_configs['enabled']:
|
|
vse_sites = ipsecvpn_configs['sites'].get('sites')
|
|
for s in vse_sites:
|
|
if ((s['peerSubnets'].get('subnets') == site['peer_cidrs']) and
|
|
(s['localSubnets'].get('subnets')[0] == local_cidr)):
|
|
old_site = s
|
|
break
|
|
return old_site, vse_sites
|
|
|
|
def _update_site_dict(self, context, edge_id, site,
|
|
ipsec_site_connection):
|
|
# Fetch the previous ipsec vpn configuration
|
|
old_site, vse_sites = self._find_vse_site(context, edge_id, site)
|
|
if old_site:
|
|
vse_sites.remove(old_site)
|
|
if 'peer_addresses' in ipsec_site_connection:
|
|
old_site['peerIp'] = ipsec_site_connection['peer_address']
|
|
if 'peer_cidrs' in ipsec_site_connection:
|
|
old_site['peerSubnets']['subnets'] = (ipsec_site_connection
|
|
['peer_cidrs'])
|
|
vse_sites.append(old_site)
|
|
return vse_sites
|
|
|
|
def update_ipsec_site_connection(self, context, old_ipsec_conn,
|
|
ipsec_site_connection):
|
|
LOG.debug('Updating ipsec site connection %(site)s.',
|
|
{"site": ipsec_site_connection})
|
|
vpnservice_id = old_ipsec_conn['vpnservice_id']
|
|
ipsec_id = old_ipsec_conn['id']
|
|
edge_id = self._get_router_edge_id(context, vpnservice_id)[1]
|
|
with locking.LockManager.get_lock(edge_id):
|
|
vse_sites = self._update_site_dict(context, edge_id,
|
|
old_ipsec_conn,
|
|
ipsec_site_connection)
|
|
if not vse_sites:
|
|
self._update_status(context, vpnservice_id, ipsec_id,
|
|
"ERROR")
|
|
LOG.error("Failed to find ipsec_site_connection "
|
|
"%(ipsec_site_conn)s with %(edge_id)s.",
|
|
{'ipsec_site_conn': ipsec_site_connection,
|
|
'edge_id': edge_id})
|
|
raise nsxv_exc.NsxIPsecVpnMappingNotFound(conn=ipsec_id)
|
|
try:
|
|
LOG.debug('Updating ipsec vpn configuration %(vse_sites)s.',
|
|
{'vse_sites': vse_sites})
|
|
self._update_ipsec_config(edge_id, vse_sites)
|
|
except vcns_exc.VcnsApiException:
|
|
self._update_status(context, vpnservice_id, ipsec_id, "ERROR")
|
|
msg = (_("Failed to create ipsec site connection "
|
|
"configuration with %(edge_id)s.") %
|
|
{'edge_id': edge_id})
|
|
raise nsxv_exc.NsxPluginException(err_msg=msg)
|
|
|
|
if 'peer_cidrs' in ipsec_site_connection:
|
|
# Update firewall
|
|
old_ipsec_conn['peer_cidrs'] = (
|
|
ipsec_site_connection['peer_cidrs'])
|
|
try:
|
|
self._update_firewall_rules(context, vpnservice_id)
|
|
except vcns_exc.VcnsApiException:
|
|
self._update_status(context, vpnservice_id, ipsec_id,
|
|
"ERROR")
|
|
msg = (_("Failed to update firewall rule for ipsec "
|
|
"vpn with %(edge_id)s.") % {'edge_id': edge_id})
|
|
raise nsxv_exc.NsxPluginException(err_msg=msg)
|
|
|
|
def _get_gateway_ips(self, router):
|
|
"""Obtain the IPv4 and/or IPv6 GW IP for the router.
|
|
|
|
If there are multiples, (arbitrarily) use the first one.
|
|
"""
|
|
v4_ip = v6_ip = None
|
|
for fixed_ip in router.gw_port['fixed_ips']:
|
|
addr = fixed_ip['ip_address']
|
|
vers = netaddr.IPAddress(addr).version
|
|
if vers == 4:
|
|
if v4_ip is None:
|
|
v4_ip = addr
|
|
elif v6_ip is None:
|
|
v6_ip = addr
|
|
return v4_ip, v6_ip
|
|
|
|
def create_vpnservice(self, context, vpnservice):
|
|
LOG.debug('Creating VPN service %(vpn)s', {'vpn': vpnservice})
|
|
vpnservice_id = vpnservice['id']
|
|
try:
|
|
self.validator.validate_vpnservice(context, vpnservice)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
# Rolling back change on the neutron
|
|
self.service_plugin.delete_vpnservice(context, vpnservice_id)
|
|
|
|
vpnservice = self.service_plugin._get_vpnservice(context,
|
|
vpnservice_id)
|
|
v4_ip, v6_ip = self._get_gateway_ips(vpnservice.router)
|
|
if v4_ip:
|
|
vpnservice['external_v4_ip'] = v4_ip
|
|
if v6_ip:
|
|
vpnservice['external_v6_ip'] = v6_ip
|
|
self.service_plugin.set_external_tunnel_ips(context,
|
|
vpnservice_id,
|
|
v4_ip=v4_ip, v6_ip=v6_ip)
|
|
|
|
def update_vpnservice(self, context, old_vpnservice, vpnservice):
|
|
pass
|
|
|
|
def delete_vpnservice(self, context, vpnservice):
|
|
pass
|
|
|
|
def _update_ipsec_config(self, edge_id, sites, enabled=True):
|
|
ipsec_config = {'featureType': "ipsec_4.0",
|
|
'enabled': enabled}
|
|
|
|
ipsec_config['sites'] = {'sites': sites}
|
|
try:
|
|
self._vcns.update_ipsec_config(edge_id, ipsec_config)
|
|
except vcns_exc.VcnsApiException:
|
|
msg = _("Failed to update ipsec vpn configuration with "
|
|
"edge_id: %s") % edge_id
|
|
raise nsxv_exc.NsxPluginException(err_msg=msg)
|