neutron-vpnaas/neutron_vpnaas/db/vpn/vpn_db.py

772 lines
36 KiB
Python

# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
# (c) Copyright 2015 Cisco Systems 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.
from neutron.db import common_db_mixin as base_db
from neutron.db import models_v2
from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants as lib_constants
from neutron_lib.exceptions import l3 as l3_exception
from neutron_lib.plugins import constants as p_constants
from neutron_lib.plugins import directory
from neutron_lib.plugins import utils
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import uuidutils
import sqlalchemy as sa
from sqlalchemy.orm import exc
from neutron_vpnaas.db.vpn import vpn_models
from neutron_vpnaas.db.vpn import vpn_validator
from neutron_vpnaas.extensions import vpn_endpoint_groups
from neutron_vpnaas.extensions import vpnaas
from neutron_vpnaas.services.vpn.common import constants as v_constants
LOG = logging.getLogger(__name__)
class VPNPluginDb(vpnaas.VPNPluginBase,
vpn_endpoint_groups.VPNEndpointGroupsPluginBase,
base_db.CommonDbMixin):
"""VPN plugin database class using SQLAlchemy models."""
def _get_validator(self):
"""Obtain validator to use for attribute validation.
Subclasses may override this with a different validator, as needed.
Note: some UTs will directly create a VPNPluginDb object and then
call its methods, instead of creating a VPNDriverPlugin, which
will have a service driver associated that will provide a
validator object. As a result, we use the reference validator here.
"""
return vpn_validator.VpnReferenceValidator()
def update_status(self, context, model, v_id, status):
with context.session.begin(subtransactions=True):
v_db = self._get_resource(context, model, v_id)
v_db.update({'status': status})
def _get_resource(self, context, model, v_id):
try:
r = self._get_by_id(context, model, v_id)
except exc.NoResultFound:
with excutils.save_and_reraise_exception(reraise=False) as ctx:
if issubclass(model, vpn_models.IPsecSiteConnection):
raise vpnaas.IPsecSiteConnectionNotFound(
ipsec_site_conn_id=v_id
)
elif issubclass(model, vpn_models.IKEPolicy):
raise vpnaas.IKEPolicyNotFound(ikepolicy_id=v_id)
elif issubclass(model, vpn_models.IPsecPolicy):
raise vpnaas.IPsecPolicyNotFound(ipsecpolicy_id=v_id)
elif issubclass(model, vpn_models.VPNService):
raise vpnaas.VPNServiceNotFound(vpnservice_id=v_id)
elif issubclass(model, vpn_models.VPNEndpointGroup):
raise vpnaas.VPNEndpointGroupNotFound(
endpoint_group_id=v_id)
ctx.reraise = True
return r
def assert_update_allowed(self, obj):
status = getattr(obj, 'status', None)
_id = getattr(obj, 'id', None)
if utils.in_pending_status(status):
raise vpnaas.VPNStateInvalidToUpdate(id=_id, state=status)
def _make_ipsec_site_connection_dict(self, ipsec_site_conn, fields=None):
res = {'id': ipsec_site_conn['id'],
'tenant_id': ipsec_site_conn['tenant_id'],
'name': ipsec_site_conn['name'],
'description': ipsec_site_conn['description'],
'peer_address': ipsec_site_conn['peer_address'],
'peer_id': ipsec_site_conn['peer_id'],
'local_id': ipsec_site_conn['local_id'],
'route_mode': ipsec_site_conn['route_mode'],
'mtu': ipsec_site_conn['mtu'],
'auth_mode': ipsec_site_conn['auth_mode'],
'psk': ipsec_site_conn['psk'],
'initiator': ipsec_site_conn['initiator'],
'dpd': {
'action': ipsec_site_conn['dpd_action'],
'interval': ipsec_site_conn['dpd_interval'],
'timeout': ipsec_site_conn['dpd_timeout']
},
'admin_state_up': ipsec_site_conn['admin_state_up'],
'status': ipsec_site_conn['status'],
'vpnservice_id': ipsec_site_conn['vpnservice_id'],
'ikepolicy_id': ipsec_site_conn['ikepolicy_id'],
'ipsecpolicy_id': ipsec_site_conn['ipsecpolicy_id'],
'peer_cidrs': [pcidr['cidr']
for pcidr in ipsec_site_conn['peer_cidrs']],
'local_ep_group_id': ipsec_site_conn['local_ep_group_id'],
'peer_ep_group_id': ipsec_site_conn['peer_ep_group_id'],
}
return self._fields(res, fields)
def get_endpoint_info(self, context, ipsec_sitecon):
"""Obtain all endpoint info, and store in connection for validation."""
ipsec_sitecon['local_epg_subnets'] = self.get_endpoint_group(
context, ipsec_sitecon['local_ep_group_id'])
ipsec_sitecon['peer_epg_cidrs'] = self.get_endpoint_group(
context, ipsec_sitecon['peer_ep_group_id'])
def validate_connection_info(self, context, validator, ipsec_sitecon,
vpnservice):
"""Collect info and validate connection.
If endpoint groups used (default), collect the group info and
do not specify the IP version (as it will come from endpoints).
Otherwise, get the IP version from the (legacy) subnet for
validation purposes.
NOTE: Once the deprecated subnet is removed, the caller can just
call get_endpoint_info() and validate_ipsec_site_connection().
"""
if ipsec_sitecon['local_ep_group_id']:
self.get_endpoint_info(context, ipsec_sitecon)
ip_version = None
else:
ip_version = vpnservice.subnet.ip_version
validator.validate_ipsec_site_connection(context, ipsec_sitecon,
ip_version, vpnservice)
def create_ipsec_site_connection(self, context, ipsec_site_connection):
ipsec_sitecon = ipsec_site_connection['ipsec_site_connection']
validator = self._get_validator()
validator.assign_sensible_ipsec_sitecon_defaults(ipsec_sitecon)
with context.session.begin(subtransactions=True):
#Check permissions
vpnservice_id = ipsec_sitecon['vpnservice_id']
self._get_resource(context, vpn_models.VPNService, vpnservice_id)
self._get_resource(context, vpn_models.IKEPolicy,
ipsec_sitecon['ikepolicy_id'])
self._get_resource(context, vpn_models.IPsecPolicy,
ipsec_sitecon['ipsecpolicy_id'])
vpnservice = self._get_vpnservice(context, vpnservice_id)
validator.validate_ipsec_conn_optional_args(ipsec_sitecon,
vpnservice.subnet)
self.validate_connection_info(context, validator, ipsec_sitecon,
vpnservice)
validator.resolve_peer_address(ipsec_sitecon, vpnservice.router)
ipsec_site_conn_db = vpn_models.IPsecSiteConnection(
id=uuidutils.generate_uuid(),
tenant_id=ipsec_sitecon['tenant_id'],
name=ipsec_sitecon['name'],
description=ipsec_sitecon['description'],
peer_address=ipsec_sitecon['peer_address'],
peer_id=ipsec_sitecon['peer_id'],
local_id=ipsec_sitecon['local_id'],
route_mode='static',
mtu=ipsec_sitecon['mtu'],
auth_mode='psk',
psk=ipsec_sitecon['psk'],
initiator=ipsec_sitecon['initiator'],
dpd_action=ipsec_sitecon['dpd_action'],
dpd_interval=ipsec_sitecon['dpd_interval'],
dpd_timeout=ipsec_sitecon['dpd_timeout'],
admin_state_up=ipsec_sitecon['admin_state_up'],
status=lib_constants.PENDING_CREATE,
vpnservice_id=vpnservice_id,
ikepolicy_id=ipsec_sitecon['ikepolicy_id'],
ipsecpolicy_id=ipsec_sitecon['ipsecpolicy_id'],
local_ep_group_id=ipsec_sitecon['local_ep_group_id'],
peer_ep_group_id=ipsec_sitecon['peer_ep_group_id']
)
context.session.add(ipsec_site_conn_db)
for cidr in ipsec_sitecon['peer_cidrs']:
peer_cidr_db = vpn_models.IPsecPeerCidr(
cidr=cidr,
ipsec_site_connection_id=ipsec_site_conn_db['id']
)
context.session.add(peer_cidr_db)
return self._make_ipsec_site_connection_dict(ipsec_site_conn_db)
def update_ipsec_site_connection(
self, context,
ipsec_site_conn_id, ipsec_site_connection):
ipsec_sitecon = ipsec_site_connection['ipsec_site_connection']
changed_peer_cidrs = False
validator = self._get_validator()
with context.session.begin(subtransactions=True):
ipsec_site_conn_db = self._get_resource(
context, vpn_models.IPsecSiteConnection, ipsec_site_conn_id)
vpnservice_id = ipsec_site_conn_db['vpnservice_id']
vpnservice = self._get_vpnservice(context, vpnservice_id)
validator.assign_sensible_ipsec_sitecon_defaults(
ipsec_sitecon, ipsec_site_conn_db)
validator.validate_ipsec_conn_optional_args(ipsec_sitecon,
vpnservice.subnet)
self.validate_connection_info(context, validator, ipsec_sitecon,
vpnservice)
if 'peer_address' in ipsec_sitecon:
validator.resolve_peer_address(ipsec_sitecon,
vpnservice.router)
self.assert_update_allowed(ipsec_site_conn_db)
if "peer_cidrs" in ipsec_sitecon:
changed_peer_cidrs = True
old_peer_cidr_list = ipsec_site_conn_db['peer_cidrs']
old_peer_cidr_dict = dict(
(peer_cidr['cidr'], peer_cidr)
for peer_cidr in old_peer_cidr_list)
new_peer_cidr_set = set(ipsec_sitecon["peer_cidrs"])
old_peer_cidr_set = set(old_peer_cidr_dict)
new_peer_cidrs = list(new_peer_cidr_set)
for peer_cidr in old_peer_cidr_set - new_peer_cidr_set:
context.session.delete(old_peer_cidr_dict[peer_cidr])
for peer_cidr in new_peer_cidr_set - old_peer_cidr_set:
pcidr = vpn_models.IPsecPeerCidr(
cidr=peer_cidr,
ipsec_site_connection_id=ipsec_site_conn_id)
context.session.add(pcidr)
# Note: Unconditionally remove peer_cidrs, as they will be set to
# previous, if unchanged (to be able to validate above).
del ipsec_sitecon["peer_cidrs"]
if ipsec_sitecon:
ipsec_site_conn_db.update(ipsec_sitecon)
result = self._make_ipsec_site_connection_dict(ipsec_site_conn_db)
if changed_peer_cidrs:
result['peer_cidrs'] = new_peer_cidrs
return result
def delete_ipsec_site_connection(self, context, ipsec_site_conn_id):
with context.session.begin(subtransactions=True):
ipsec_site_conn_db = self._get_resource(
context, vpn_models.IPsecSiteConnection, ipsec_site_conn_id)
context.session.delete(ipsec_site_conn_db)
def _get_ipsec_site_connection(
self, context, ipsec_site_conn_id):
return self._get_resource(
context, vpn_models.IPsecSiteConnection, ipsec_site_conn_id)
def get_ipsec_site_connection(self, context,
ipsec_site_conn_id, fields=None):
ipsec_site_conn_db = self._get_ipsec_site_connection(
context, ipsec_site_conn_id)
return self._make_ipsec_site_connection_dict(
ipsec_site_conn_db, fields)
def get_ipsec_site_connections(self, context, filters=None, fields=None):
return self._get_collection(context, vpn_models.IPsecSiteConnection,
self._make_ipsec_site_connection_dict,
filters=filters, fields=fields)
def update_ipsec_site_conn_status(self, context, conn_id, new_status):
with context.session.begin():
self._update_connection_status(context, conn_id, new_status, True)
def _update_connection_status(self, context, conn_id, new_status,
updated_pending):
"""Update the connection status, if changed.
If the connection is not in a pending state, unconditionally update
the status. Likewise, if in a pending state, and have an indication
that the status has changed, then update the database.
"""
try:
conn_db = self._get_ipsec_site_connection(context, conn_id)
except vpnaas.IPsecSiteConnectionNotFound:
return
if not utils.in_pending_status(conn_db.status) or updated_pending:
conn_db.status = new_status
def _make_ikepolicy_dict(self, ikepolicy, fields=None):
res = {'id': ikepolicy['id'],
'tenant_id': ikepolicy['tenant_id'],
'name': ikepolicy['name'],
'description': ikepolicy['description'],
'auth_algorithm': ikepolicy['auth_algorithm'],
'encryption_algorithm': ikepolicy['encryption_algorithm'],
'phase1_negotiation_mode': ikepolicy['phase1_negotiation_mode'],
'lifetime': {
'units': ikepolicy['lifetime_units'],
'value': ikepolicy['lifetime_value'],
},
'ike_version': ikepolicy['ike_version'],
'pfs': ikepolicy['pfs']
}
return self._fields(res, fields)
def create_ikepolicy(self, context, ikepolicy):
ike = ikepolicy['ikepolicy']
validator = self._get_validator()
lifetime_info = ike['lifetime']
lifetime_units = lifetime_info.get('units', 'seconds')
lifetime_value = lifetime_info.get('value', 3600)
with context.session.begin(subtransactions=True):
validator.validate_ike_policy(context, ike)
ike_db = vpn_models.IKEPolicy(
id=uuidutils.generate_uuid(),
tenant_id=ike['tenant_id'],
name=ike['name'],
description=ike['description'],
auth_algorithm=ike['auth_algorithm'],
encryption_algorithm=ike['encryption_algorithm'],
phase1_negotiation_mode=ike['phase1_negotiation_mode'],
lifetime_units=lifetime_units,
lifetime_value=lifetime_value,
ike_version=ike['ike_version'],
pfs=ike['pfs']
)
context.session.add(ike_db)
return self._make_ikepolicy_dict(ike_db)
def update_ikepolicy(self, context, ikepolicy_id, ikepolicy):
ike = ikepolicy['ikepolicy']
validator = self._get_validator()
with context.session.begin(subtransactions=True):
validator.validate_ike_policy(context, ike)
if context.session.query(vpn_models.IPsecSiteConnection).filter_by(
ikepolicy_id=ikepolicy_id).first():
raise vpnaas.IKEPolicyInUse(ikepolicy_id=ikepolicy_id)
ike_db = self._get_resource(
context, vpn_models.IKEPolicy, ikepolicy_id)
if ike:
lifetime_info = ike.get('lifetime')
if lifetime_info:
if lifetime_info.get('units'):
ike['lifetime_units'] = lifetime_info['units']
if lifetime_info.get('value'):
ike['lifetime_value'] = lifetime_info['value']
ike_db.update(ike)
return self._make_ikepolicy_dict(ike_db)
def delete_ikepolicy(self, context, ikepolicy_id):
with context.session.begin(subtransactions=True):
if context.session.query(vpn_models.IPsecSiteConnection).filter_by(
ikepolicy_id=ikepolicy_id).first():
raise vpnaas.IKEPolicyInUse(ikepolicy_id=ikepolicy_id)
ike_db = self._get_resource(
context, vpn_models.IKEPolicy, ikepolicy_id)
context.session.delete(ike_db)
def get_ikepolicy(self, context, ikepolicy_id, fields=None):
ike_db = self._get_resource(
context, vpn_models.IKEPolicy, ikepolicy_id)
return self._make_ikepolicy_dict(ike_db, fields)
def get_ikepolicies(self, context, filters=None, fields=None):
return self._get_collection(context, vpn_models.IKEPolicy,
self._make_ikepolicy_dict,
filters=filters, fields=fields)
def _make_ipsecpolicy_dict(self, ipsecpolicy, fields=None):
res = {'id': ipsecpolicy['id'],
'tenant_id': ipsecpolicy['tenant_id'],
'name': ipsecpolicy['name'],
'description': ipsecpolicy['description'],
'transform_protocol': ipsecpolicy['transform_protocol'],
'auth_algorithm': ipsecpolicy['auth_algorithm'],
'encryption_algorithm': ipsecpolicy['encryption_algorithm'],
'encapsulation_mode': ipsecpolicy['encapsulation_mode'],
'lifetime': {
'units': ipsecpolicy['lifetime_units'],
'value': ipsecpolicy['lifetime_value'],
},
'pfs': ipsecpolicy['pfs']
}
return self._fields(res, fields)
def create_ipsecpolicy(self, context, ipsecpolicy):
ipsecp = ipsecpolicy['ipsecpolicy']
validator = self._get_validator()
lifetime_info = ipsecp['lifetime']
lifetime_units = lifetime_info.get('units', 'seconds')
lifetime_value = lifetime_info.get('value', 3600)
with context.session.begin(subtransactions=True):
validator.validate_ipsec_policy(context, ipsecp)
ipsecp_db = vpn_models.IPsecPolicy(
id=uuidutils.generate_uuid(),
tenant_id=ipsecp['tenant_id'],
name=ipsecp['name'],
description=ipsecp['description'],
transform_protocol=ipsecp['transform_protocol'],
auth_algorithm=ipsecp['auth_algorithm'],
encryption_algorithm=ipsecp['encryption_algorithm'],
encapsulation_mode=ipsecp['encapsulation_mode'],
lifetime_units=lifetime_units,
lifetime_value=lifetime_value,
pfs=ipsecp['pfs'])
context.session.add(ipsecp_db)
return self._make_ipsecpolicy_dict(ipsecp_db)
def update_ipsecpolicy(self, context, ipsecpolicy_id, ipsecpolicy):
ipsecp = ipsecpolicy['ipsecpolicy']
validator = self._get_validator()
with context.session.begin(subtransactions=True):
validator.validate_ipsec_policy(context, ipsecp)
if context.session.query(vpn_models.IPsecSiteConnection).filter_by(
ipsecpolicy_id=ipsecpolicy_id).first():
raise vpnaas.IPsecPolicyInUse(ipsecpolicy_id=ipsecpolicy_id)
ipsecp_db = self._get_resource(
context, vpn_models.IPsecPolicy, ipsecpolicy_id)
if ipsecp:
lifetime_info = ipsecp.get('lifetime')
if lifetime_info:
if lifetime_info.get('units'):
ipsecp['lifetime_units'] = lifetime_info['units']
if lifetime_info.get('value'):
ipsecp['lifetime_value'] = lifetime_info['value']
ipsecp_db.update(ipsecp)
return self._make_ipsecpolicy_dict(ipsecp_db)
def delete_ipsecpolicy(self, context, ipsecpolicy_id):
with context.session.begin(subtransactions=True):
if context.session.query(vpn_models.IPsecSiteConnection).filter_by(
ipsecpolicy_id=ipsecpolicy_id).first():
raise vpnaas.IPsecPolicyInUse(ipsecpolicy_id=ipsecpolicy_id)
ipsec_db = self._get_resource(
context, vpn_models.IPsecPolicy, ipsecpolicy_id)
context.session.delete(ipsec_db)
def get_ipsecpolicy(self, context, ipsecpolicy_id, fields=None):
ipsec_db = self._get_resource(
context, vpn_models.IPsecPolicy, ipsecpolicy_id)
return self._make_ipsecpolicy_dict(ipsec_db, fields)
def get_ipsecpolicies(self, context, filters=None, fields=None):
return self._get_collection(context, vpn_models.IPsecPolicy,
self._make_ipsecpolicy_dict,
filters=filters, fields=fields)
def _make_vpnservice_dict(self, vpnservice, fields=None):
res = {'id': vpnservice['id'],
'name': vpnservice['name'],
'description': vpnservice['description'],
'tenant_id': vpnservice['tenant_id'],
'subnet_id': vpnservice['subnet_id'],
'router_id': vpnservice['router_id'],
'flavor_id': vpnservice['flavor_id'],
'admin_state_up': vpnservice['admin_state_up'],
'external_v4_ip': vpnservice['external_v4_ip'],
'external_v6_ip': vpnservice['external_v6_ip'],
'status': vpnservice['status']}
return self._fields(res, fields)
def create_vpnservice(self, context, vpnservice):
vpns = vpnservice['vpnservice']
flavor_id = vpns.get('flavor_id', None)
validator = self._get_validator()
with context.session.begin(subtransactions=True):
validator.validate_vpnservice(context, vpns)
vpnservice_db = vpn_models.VPNService(
id=uuidutils.generate_uuid(),
tenant_id=vpns['tenant_id'],
name=vpns['name'],
description=vpns['description'],
subnet_id=vpns['subnet_id'],
router_id=vpns['router_id'],
flavor_id=flavor_id,
admin_state_up=vpns['admin_state_up'],
status=lib_constants.PENDING_CREATE)
context.session.add(vpnservice_db)
return self._make_vpnservice_dict(vpnservice_db)
def set_external_tunnel_ips(self, context, vpnservice_id, v4_ip=None,
v6_ip=None):
"""Update the external tunnel IP(s) for service."""
vpns = {'external_v4_ip': v4_ip, 'external_v6_ip': v6_ip}
with context.session.begin(subtransactions=True):
vpns_db = self._get_resource(context, vpn_models.VPNService,
vpnservice_id)
vpns_db.update(vpns)
return self._make_vpnservice_dict(vpns_db)
def update_vpnservice(self, context, vpnservice_id, vpnservice):
vpns = vpnservice['vpnservice']
with context.session.begin(subtransactions=True):
vpns_db = self._get_resource(context, vpn_models.VPNService,
vpnservice_id)
self.assert_update_allowed(vpns_db)
if vpns:
vpns_db.update(vpns)
return self._make_vpnservice_dict(vpns_db)
def delete_vpnservice(self, context, vpnservice_id):
with context.session.begin(subtransactions=True):
if context.session.query(vpn_models.IPsecSiteConnection).filter_by(
vpnservice_id=vpnservice_id
).first():
raise vpnaas.VPNServiceInUse(vpnservice_id=vpnservice_id)
vpns_db = self._get_resource(context, vpn_models.VPNService,
vpnservice_id)
context.session.delete(vpns_db)
def _get_vpnservice(self, context, vpnservice_id):
return self._get_resource(context, vpn_models.VPNService,
vpnservice_id)
def get_vpnservice(self, context, vpnservice_id, fields=None):
vpns_db = self._get_resource(context, vpn_models.VPNService,
vpnservice_id)
return self._make_vpnservice_dict(vpns_db, fields)
def get_vpnservices(self, context, filters=None, fields=None):
return self._get_collection(context, vpn_models.VPNService,
self._make_vpnservice_dict,
filters=filters, fields=fields)
def check_router_in_use(self, context, router_id):
vpnservices = self.get_vpnservices(
context, filters={'router_id': [router_id]})
if vpnservices:
plural = "s" if len(vpnservices) > 1 else ""
services = ",".join([v['id'] for v in vpnservices])
raise l3_exception.RouterInUse(
router_id=router_id,
reason="is currently used by VPN service%(plural)s "
"(%(services)s)" % {'plural': plural,
'services': services})
def check_subnet_in_use(self, context, subnet_id, router_id):
with context.session.begin(subtransactions=True):
vpnservices = context.session.query(
vpn_models.VPNService).filter_by(
subnet_id=subnet_id, router_id=router_id).first()
if vpnservices:
raise vpnaas.SubnetInUseByVPNService(
subnet_id=subnet_id,
vpnservice_id=vpnservices['id'])
query = context.session.query(vpn_models.IPsecSiteConnection)
query = query.join(
vpn_models.VPNEndpointGroup,
vpn_models.VPNEndpointGroup.id ==
vpn_models.IPsecSiteConnection.local_ep_group_id).filter(
vpn_models.VPNEndpointGroup.endpoint_type ==
v_constants.SUBNET_ENDPOINT)
query = query.join(
vpn_models.VPNEndpoint,
vpn_models.VPNEndpoint.endpoint_group_id ==
vpn_models.IPsecSiteConnection.local_ep_group_id).filter(
vpn_models.VPNEndpoint.endpoint == subnet_id)
query = query.join(
vpn_models.VPNService,
vpn_models.VPNService.id ==
vpn_models.IPsecSiteConnection.vpnservice_id).filter(
vpn_models.VPNService.router_id == router_id)
connection = query.first()
if connection:
raise vpnaas.SubnetInUseByIPsecSiteConnection(
subnet_id=subnet_id,
ipsec_site_connection_id=connection['id'])
def check_subnet_in_use_by_endpoint_group(self, context, subnet_id):
with context.session.begin(subtransactions=True):
query = context.session.query(vpn_models.VPNEndpointGroup)
query = query.filter(vpn_models.VPNEndpointGroup.endpoint_type ==
v_constants.SUBNET_ENDPOINT)
query = query.join(
vpn_models.VPNEndpoint,
sa.and_(vpn_models.VPNEndpoint.endpoint_group_id ==
vpn_models.VPNEndpointGroup.id,
vpn_models.VPNEndpoint.endpoint == subnet_id))
group = query.first()
if group:
raise vpnaas.SubnetInUseByEndpointGroup(
subnet_id=subnet_id, group_id=group['id'])
def _make_endpoint_group_dict(self, endpoint_group, fields=None):
res = {'id': endpoint_group['id'],
'tenant_id': endpoint_group['tenant_id'],
'name': endpoint_group['name'],
'description': endpoint_group['description'],
'type': endpoint_group['endpoint_type'],
'endpoints': [ep['endpoint']
for ep in endpoint_group['endpoints']]}
return self._fields(res, fields)
def create_endpoint_group(self, context, endpoint_group):
group = endpoint_group['endpoint_group']
validator = self._get_validator()
with context.session.begin(subtransactions=True):
validator.validate_endpoint_group(context, group)
endpoint_group_db = vpn_models.VPNEndpointGroup(
id=uuidutils.generate_uuid(),
tenant_id=group['tenant_id'],
name=group['name'],
description=group['description'],
endpoint_type=group['type'])
context.session.add(endpoint_group_db)
for endpoint in group['endpoints']:
endpoint_db = vpn_models.VPNEndpoint(
endpoint=endpoint,
endpoint_group_id=endpoint_group_db['id']
)
context.session.add(endpoint_db)
return self._make_endpoint_group_dict(endpoint_group_db)
def update_endpoint_group(self, context, endpoint_group_id,
endpoint_group):
group_changes = endpoint_group['endpoint_group']
# Note: Endpoints cannot be changed, so will not do validation
with context.session.begin(subtransactions=True):
endpoint_group_db = self._get_resource(context,
vpn_models.VPNEndpointGroup,
endpoint_group_id)
endpoint_group_db.update(group_changes)
return self._make_endpoint_group_dict(endpoint_group_db)
def delete_endpoint_group(self, context, endpoint_group_id):
with context.session.begin(subtransactions=True):
self.check_endpoint_group_not_in_use(context, endpoint_group_id)
endpoint_group_db = self._get_resource(
context, vpn_models.VPNEndpointGroup, endpoint_group_id)
context.session.delete(endpoint_group_db)
def get_endpoint_group(self, context, endpoint_group_id, fields=None):
endpoint_group_db = self._get_resource(
context, vpn_models.VPNEndpointGroup, endpoint_group_id)
return self._make_endpoint_group_dict(endpoint_group_db, fields)
def get_endpoint_groups(self, context, filters=None, fields=None):
return self._get_collection(context, vpn_models.VPNEndpointGroup,
self._make_endpoint_group_dict,
filters=filters, fields=fields)
def check_endpoint_group_not_in_use(self, context, group_id):
query = context.session.query(vpn_models.IPsecSiteConnection)
query = query.filter(
sa.or_(
vpn_models.IPsecSiteConnection.local_ep_group_id == group_id,
vpn_models.IPsecSiteConnection.peer_ep_group_id == group_id)
)
if query.first():
raise vpnaas.EndpointGroupInUse(group_id=group_id)
class VPNPluginRpcDbMixin(object):
def _build_local_subnet_cidr_map(self, context):
"""Build a dict of all local endpoint subnets, with list of CIDRs."""
query = context.session.query(models_v2.Subnet.id,
models_v2.Subnet.cidr)
query = query.join(vpn_models.VPNEndpoint,
vpn_models.VPNEndpoint.endpoint ==
models_v2.Subnet.id)
query = query.join(vpn_models.VPNEndpointGroup,
vpn_models.VPNEndpointGroup.id ==
vpn_models.VPNEndpoint.endpoint_group_id)
query = query.join(vpn_models.IPsecSiteConnection,
vpn_models.IPsecSiteConnection.local_ep_group_id ==
vpn_models.VPNEndpointGroup.id)
return {sn.id: sn.cidr for sn in query.all()}
def update_status_by_agent(self, context, service_status_info_list):
"""Updating vpnservice and vpnconnection status.
:param context: context variable
:param service_status_info_list: list of status
The structure is
[{id: vpnservice_id,
status: ACTIVE|DOWN|ERROR,
updated_pending_status: True|False
ipsec_site_connections: {
ipsec_site_connection_id: {
status: ACTIVE|DOWN|ERROR,
updated_pending_status: True|False
}
}]
The agent will set updated_pending_status as True,
when agent updates any pending status.
"""
with context.session.begin(subtransactions=True):
for vpnservice in service_status_info_list:
try:
vpnservice_db = self._get_vpnservice(
context, vpnservice['id'])
except vpnaas.VPNServiceNotFound:
LOG.warning('vpnservice %s in db is already deleted',
vpnservice['id'])
continue
if (not utils.in_pending_status(vpnservice_db.status) or
vpnservice['updated_pending_status']):
vpnservice_db.status = vpnservice['status']
for conn_id, conn in vpnservice[
'ipsec_site_connections'].items():
self._update_connection_status(
context, conn_id, conn['status'],
conn['updated_pending_status'])
def vpn_router_gateway_callback(resource, event, trigger, payload=None):
# the event payload objects
vpn_plugin = directory.get_plugin(p_constants.VPN)
if vpn_plugin:
context = payload.context
router_id = payload.resource_id
if resource == resources.ROUTER_GATEWAY:
vpn_plugin.check_router_in_use(context, router_id)
elif resource == resources.ROUTER_INTERFACE:
subnet_id = payload.metadata.get('subnet_id')
vpn_plugin.check_subnet_in_use(context, subnet_id, router_id)
def migration_callback(resource, event, trigger, **kwargs):
context = kwargs['context']
router = kwargs['router']
vpn_plugin = directory.get_plugin(p_constants.VPN)
if vpn_plugin:
vpn_plugin.check_router_in_use(context, router['id'])
return True
def subnet_callback(resource, event, trigger, payload=None):
"""Respond to subnet based notifications - see if subnet in use."""
context = payload.context
subnet_id = payload.resource_id
vpn_plugin = directory.get_plugin(p_constants.VPN)
if vpn_plugin:
vpn_plugin.check_subnet_in_use_by_endpoint_group(context, subnet_id)
def subscribe():
registry.subscribe(
vpn_router_gateway_callback, resources.ROUTER_GATEWAY,
events.BEFORE_DELETE)
registry.subscribe(
vpn_router_gateway_callback, resources.ROUTER_INTERFACE,
events.BEFORE_DELETE)
registry.subscribe(
migration_callback, resources.ROUTER, events.BEFORE_UPDATE)
registry.subscribe(
subnet_callback, resources.SUBNET, events.BEFORE_DELETE)
# NOTE(armax): multiple VPN service plugins (potentially out of tree) may
# inherit from vpn_db and may need the callbacks to be processed. Having an
# implicit subscription (through the module import) preserves the existing
# behavior, and at the same time it avoids fixing it manually in each and
# every vpn plugin outta there. That said, The subscription is also made
# explicitly in the reference vpn plugin. The subscription operation is
# idempotent so there is no harm in registering the same callback multiple
# times.
subscribe()