# Copyright 2014 # The Cloudscaling Group, Inc. # # 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 neutronclient.common import exceptions as neutron_exception from oslo_log import log as logging from ec2api.api import common from ec2api.api import ec2utils from ec2api.api import vpn_connection as vpn_connection_api from ec2api import clients from ec2api.db import api as db_api from ec2api import exception from ec2api.i18n import _ LOG = logging.getLogger(__name__) """VPN gateways related API implementation """ Validator = common.Validator def create_vpn_gateway(context, type, availability_zone=None): vpn_gateway = db_api.add_item(context, 'vgw', {}) return {'vpnGateway': _format_vpn_gateway(vpn_gateway)} def attach_vpn_gateway(context, vpc_id, vpn_gateway_id): vpn_gateway = ec2utils.get_db_item(context, vpn_gateway_id) vpc = ec2utils.get_db_item(context, vpc_id) if vpn_gateway['vpc_id'] and vpn_gateway['vpc_id'] != vpc['id']: raise exception.VpnGatewayAttachmentLimitExceeded() attached_vgw = ec2utils.get_attached_gateway(context, vpc['id'], 'vgw') if attached_vgw and attached_vgw['id'] != vpn_gateway['id']: raise exception.InvalidVpcState(vpc_id=vpc['id'], vgw_id=attached_vgw['id']) subnets = [subnet for subnet in db_api.get_items(context, 'subnet') if subnet['vpc_id'] == vpc['id']] if not vpn_gateway['vpc_id']: external_network_id = None if not ec2utils.get_attached_gateway(context, vpc['id'], 'igw'): external_network_id = ec2utils.get_os_public_network(context)['id'] neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: _attach_vpn_gateway_item(context, vpn_gateway, vpc['id']) cleaner.addCleanup(_detach_vpn_gateway_item, context, vpn_gateway) if external_network_id: neutron.add_gateway_router(vpc['os_id'], {'network_id': external_network_id}) cleaner.addCleanup(neutron.remove_gateway_router, vpc['os_id']) for subnet in subnets: _create_subnet_vpnservice(context, neutron, cleaner, subnet, vpc) vpn_connection_api._reset_vpn_connections( context, neutron, cleaner, vpn_gateway, subnets=subnets) return {'attachment': _format_attachment(vpn_gateway)} def detach_vpn_gateway(context, vpc_id, vpn_gateway_id): vpn_gateway = ec2utils.get_db_item(context, vpn_gateway_id) if vpn_gateway['vpc_id'] != vpc_id: raise exception.InvalidVpnGatewayAttachmentNotFound( vgw_id=vpn_gateway_id, vpc_id=vpc_id) vpc = db_api.get_item_by_id(context, vpc_id) neutron = clients.neutron(context) remove_os_gateway_router = ( ec2utils.get_attached_gateway(context, vpc_id, 'igw') is None) subnets = [subnet for subnet in db_api.get_items(context, 'subnet') if subnet['vpc_id'] == vpc['id']] with common.OnCrashCleaner() as cleaner: _detach_vpn_gateway_item(context, vpn_gateway) cleaner.addCleanup(_attach_vpn_gateway_item, context, vpn_gateway, vpc_id) vpn_connection_api._stop_gateway_vpn_connections( context, neutron, cleaner, vpn_gateway) for subnet in subnets: _delete_subnet_vpnservice(context, neutron, cleaner, subnet) if remove_os_gateway_router: try: neutron.remove_gateway_router(vpc['os_id']) except neutron_exception.NotFound: pass return True def delete_vpn_gateway(context, vpn_gateway_id): vpn_gateway = ec2utils.get_db_item(context, vpn_gateway_id) vpn_connections = db_api.get_items(context, 'vpn') if vpn_gateway['vpc_id'] or any(vpn['vpn_gateway_id'] == vpn_gateway['id'] for vpn in vpn_connections): raise exception.IncorrectState(reason=_('The VPN gateway is in use.')) db_api.delete_item(context, vpn_gateway['id']) return True def describe_vpn_gateways(context, vpn_gateway_id=None, filter=None): formatted_vgws = VpnGatewayDescriber().describe( context, ids=vpn_gateway_id, filter=filter) return {'vpnGatewaySet': formatted_vgws} class VpnGatewayDescriber(common.TaggableItemsDescriber, common.NonOpenstackItemsDescriber): KIND = 'vgw' FILTER_MAP = {'attachment.state': ['attachments', 'state'], 'attachment.vpc-id': ['attachments', 'vpcId'], 'state': 'state', 'type': 'type', 'vpn-gateway-id': 'vpnGatewayId'} def format(self, vpn_gateway): return _format_vpn_gateway(vpn_gateway) def _format_vpn_gateway(vpn_gateway): ec2_vgw = {'vpnGatewayId': vpn_gateway['id'], 'state': 'available', 'type': 'ipsec.1', 'attachments': []} if vpn_gateway['vpc_id']: ec2_vgw['attachments'].append(_format_attachment(vpn_gateway)) return ec2_vgw def _format_attachment(vpn_gateway): return {'state': 'attached', 'vpcId': vpn_gateway['vpc_id']} def _start_vpn_in_subnet(context, neutron, cleaner, subnet, vpc, route_table): vpn_gateway = ec2utils.get_attached_gateway(context, vpc['id'], 'vgw') if not vpn_gateway: return _create_subnet_vpnservice(context, neutron, cleaner, subnet, vpc) vpn_connection_api._reset_vpn_connections(context, neutron, cleaner, vpn_gateway, subnets=[subnet], route_tables=[route_table]) def _stop_vpn_in_subnet(context, neutron, cleaner, subnet): os_vpnservice_id = subnet.get('os_vpnservice_id') if not os_vpnservice_id: return for vpn in db_api.get_items(context, 'vpn'): vpn_connection_api._delete_subnet_vpn(context, neutron, cleaner, subnet, vpn) _safe_delete_vpnservice(neutron, os_vpnservice_id, subnet['id']) def _create_subnet_vpnservice(context, neutron, cleaner, subnet, vpc): os_vpnservice = {'subnet_id': subnet['os_id'], 'router_id': vpc['os_id'], 'name': subnet['id']} os_vpnservice = neutron.create_vpnservice( {'vpnservice': os_vpnservice})['vpnservice'] cleaner.addCleanup(neutron.delete_vpnservice, os_vpnservice['id']) _set_vpnservice_in_subnet_item(context, subnet, os_vpnservice['id']) cleaner.addCleanup(_clear_vpnservice_in_subnet_item, context, subnet) def _delete_subnet_vpnservice(context, neutron, cleaner, subnet): os_vpnservice_id = subnet['os_vpnservice_id'] _clear_vpnservice_in_subnet_item(context, subnet) cleaner.addCleanup(_set_vpnservice_in_subnet_item, context, subnet, os_vpnservice_id) _safe_delete_vpnservice(neutron, os_vpnservice_id, subnet['id']) def _safe_delete_vpnservice(neutron, os_vpnservice_id, subnet_id): try: neutron.delete_vpnservice(os_vpnservice_id) except neutron_exception.NotFound: pass except neutron_exception.Conflict as ex: LOG.warning( 'Failed to delete vpnservice %(os_id)s for subnet %(id)s. ' 'Reason: %(reason)s', {'id': subnet_id, 'os_id': os_vpnservice_id, 'reason': ex.message}) def _attach_vpn_gateway_item(context, vpn_gateway, vpc_id): vpn_gateway['vpc_id'] = vpc_id db_api.update_item(context, vpn_gateway) def _detach_vpn_gateway_item(context, vpn_gateway): vpn_gateway['vpc_id'] = None db_api.update_item(context, vpn_gateway) def _set_vpnservice_in_subnet_item(context, subnet, os_vpnservice_id): subnet['os_vpnservice_id'] = os_vpnservice_id db_api.update_item(context, subnet) def _clear_vpnservice_in_subnet_item(context, subnet): del subnet['os_vpnservice_id'] db_api.update_item(context, subnet)