def associate_route_table(context, route_table_id, subnet_id): route_table = ec2utils.get_db_item(context, route_table_id) subnet = ec2utils.get_db_item(context, subnet_id) if route_table['vpc_id'] != subnet['vpc_id']: msg = _('Route table %(rtb_id)s and subnet %(subnet_id)s belong to ' 'different networks') msg = msg % {'rtb_id': route_table_id, 'subnet_id': subnet_id} raise exception.InvalidParameterValue(msg) if 'route_table_id' in subnet: msg = _('The specified association for route table %(rtb_id)s ' 'conflicts with an existing association') msg = msg % {'rtb_id': route_table_id} raise exception.ResourceAlreadyAssociated(msg) vpc = db_api.get_item_by_id(context, subnet['vpc_id']) main_route_table = db_api.get_item_by_id(context, vpc['route_table_id']) with common.OnCrashCleaner() as cleaner: _associate_subnet_item(context, subnet, route_table['id']) cleaner.addCleanup(_disassociate_subnet_item, context, subnet) _update_subnet_host_routes( context, subnet, route_table, cleaner=cleaner, rollback_route_table_object=main_route_table) return { 'associationId': ec2utils.change_ec2_id_kind(subnet['id'], 'rtbassoc') }
def disassociate_route_table(context, association_id): subnet = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'subnet')) if not subnet: vpc = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'vpc')) if vpc is None: raise exception.InvalidAssociationIDNotFound(id=association_id) msg = _('Cannot disassociate the main route table association ' '%(rtbassoc_id)s') % {'rtbassoc_id': association_id} raise exception.InvalidParameterValue(msg) if 'route_table_id' not in subnet: raise exception.InvalidAssociationIDNotFound(id=association_id) rollback_route_table_id = subnet['route_table_id'] vpc = db_api.get_item_by_id(context, subnet['vpc_id']) main_route_table = db_api.get_item_by_id(context, vpc['route_table_id']) with common.OnCrashCleaner() as cleaner: _disassociate_subnet_item(context, subnet) cleaner.addCleanup(_associate_subnet_item, context, subnet, rollback_route_table_id) _update_subnet_routes(context, cleaner, subnet, main_route_table) return True
def _get_router_objects(context, route_table): return dict( (route['gateway_id'], db_api.get_item_by_id(context, route['gateway_id']) ) if route.get('gateway_id') else ( route['network_interface_id'], db_api.get_item_by_id(context, route['network_interface_id'])) for route in route_table['routes'] if route.get('gateway_id') or 'network_interface_id' in route)
def replace_route_table_association(context, association_id, route_table_id): route_table = ec2utils.get_db_item(context, route_table_id) if route_table['vpc_id'] == ec2utils.change_ec2_id_kind( association_id, 'vpc'): vpc = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'vpc')) if vpc is None: raise exception.InvalidAssociationIDNotFound(id=association_id) rollabck_route_table_object = db_api.get_item_by_id( context, vpc['route_table_id']) with common.OnCrashCleaner() as cleaner: _associate_vpc_item(context, vpc, route_table['id']) cleaner.addCleanup(_associate_vpc_item, context, vpc, rollabck_route_table_object['id']) # NOTE(ft): this can cause unnecessary update of subnets, which are # associated with the route table _update_routes_in_associated_subnets(context, route_table, cleaner, rollabck_route_table_object, is_main=True) else: subnet = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'subnet')) if subnet is None or 'route_table_id' not in subnet: raise exception.InvalidAssociationIDNotFound(id=association_id) if subnet['vpc_id'] != route_table['vpc_id']: msg = _('Route table association %(rtbassoc_id)s and route table ' '%(rtb_id)s belong to different networks') msg = msg % { 'rtbassoc_id': association_id, 'rtb_id': route_table_id } raise exception.InvalidParameterValue(msg) rollabck_route_table_object = db_api.get_item_by_id( context, subnet['route_table_id']) with common.OnCrashCleaner() as cleaner: _associate_subnet_item(context, subnet, route_table['id']) cleaner.addCleanup(_associate_subnet_item, context, subnet, rollabck_route_table_object['id']) _update_subnet_host_routes( context, subnet, route_table, cleaner=cleaner, rollback_route_table_object=rollabck_route_table_object) return {'newAssociationId': association_id}
def test_delete_item(self): item = db_api.add_item(self.context, 'fake', {}) db_api.delete_item(self.context, item['id']) item = db_api.get_item_by_id(self.context, item['id']) self.assertIsNone(item) # NOTE(ft): delete not existing item should pass quitely db_api.delete_item(self.context, fakes.random_ec2_id('fake')) item = db_api.add_item(self.context, 'fake', {}) db_api.delete_item(self.other_context, item['id']) item = db_api.get_item_by_id(self.context, item['id']) self.assertIsNotNone(item)
def test_get_item_by_id(self): self._setup_items() item_id = db_api.get_items(self.context, 'fake')[0]['id'] other_item_id = db_api.get_items(self.other_context, 'fake')[0]['id'] item = db_api.get_item_by_id(self.context, item_id) self.assertThat(item, matchers.DictMatches({'id': item_id, 'os_id': None, 'vpc_id': None})) item = db_api.get_item_by_id(self.context, other_item_id) self.assertIsNone(item) item = db_api.get_item_by_id(self.context, fakes.random_ec2_id('fake')) self.assertIsNone(item)
def detach_network_interface(context, attachment_id, force=None): network_interface = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(attachment_id, 'eni')) if not network_interface or 'instance_id' not in network_interface: raise exception.InvalidAttachmentIDNotFound(id=attachment_id) if network_interface['device_index'] == 0: raise exception.OperationNotPermitted( _('The network interface at device index 0 cannot be detached.')) neutron = clients.neutron(context) os_port = neutron.show_port(network_interface['os_id'])['port'] with common.OnCrashCleaner() as cleaner: instance_id = network_interface['instance_id'] device_index = network_interface['device_index'] attach_time = network_interface['attach_time'] delete_on_termination = network_interface['delete_on_termination'] _detach_network_interface_item(context, network_interface) cleaner.addCleanup(_attach_network_interface_item, context, network_interface, instance_id, device_index, attach_time, delete_on_termination) neutron.update_port(os_port['id'], {'port': { 'device_id': '', 'device_owner': '' }}) return True
def disassociate_address(self, context, public_ip=None, association_id=None): LOG.info('Disassociating %s', association_id) neutron = clients.neutron(context) floatingips=neutron.list_floatingips(tenant_id=context.project_id)['floatingips'] LOG.info('Existing floating ips: %s', floatingips) if public_ip: # TODO(ft): implement search in DB layer address = next((addr for addr in db_api.get_items(context, 'eipalloc') if addr['public_ip'] == public_ip), None) if not CONF.disable_ec2_classic: if address and _is_address_valid(context, neutron, address): msg = _('You must specify an association id when ' 'unmapping an address from a VPC instance') raise exception.InvalidParameterValue(msg) # NOTE(tikitavi): check the public IP exists to raise AWS # exception otherwise os_floating_ip = self.get_os_floating_ip_by_public_ip( context, public_ip) os_ports = self.get_os_ports(context) os_instance_id = _get_os_instance_id(context, os_floating_ip, os_ports) if os_instance_id: nova = clients.nova(context) nova.servers.remove_floating_ip(os_instance_id, public_ip) return None if not address: msg = _("The address '%(public_ip)s' does not belong to you.") raise exception.AuthFailure(msg % {'public_ip': public_ip}) if 'network_interface_id' not in address: msg = _('You must specify an association id when unmapping ' 'an address from a VPC instance') raise exception.InvalidParameterValue(msg) association_id = ec2utils.change_ec2_id_kind(address['id'], 'eipassoc') address = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'eipalloc')) LOG.info('DB address: %s', address) if address is None or not _is_address_valid(context, neutron, address): raise exception.InvalidAssociationIDNotFound( id=association_id) if 'network_interface_id' in address: with common.OnCrashCleaner() as cleaner: network_interface_id = address['network_interface_id'] private_ip_address = address['private_ip_address'] LOG.info('Disassociating %(private_ip_address)s from interface %(network_interface_id)s', {'private_ip_address': private_ip_address, 'network_interface_id': network_interface_id}) _disassociate_address_item(context, address) cleaner.addCleanup(_associate_address_item, context, address, network_interface_id, private_ip_address) update = neutron.update_floatingip(address['os_id'], {'floatingip': {'port_id': None}}) LOG.info('Neutron.update result is %s', update)
def _update_routes_in_associated_subnets(context, cleaner, route_table, default_associations_only=None, update_target=None): if default_associations_only: appropriate_rtb_ids = (None, ) else: vpc = db_api.get_item_by_id(context, route_table['vpc_id']) if vpc['route_table_id'] == route_table['id']: appropriate_rtb_ids = (route_table['id'], None) else: appropriate_rtb_ids = (route_table['id'], ) neutron = clients.neutron(context) subnets = [ subnet for subnet in db_api.get_items(context, 'subnet') if (subnet['vpc_id'] == route_table['vpc_id'] and subnet.get('route_table_id') in appropriate_rtb_ids) ] # NOTE(ft): we need to update host routes for both host and vpn target # because vpn-related routes are present in host routes as well _update_host_routes(context, neutron, cleaner, route_table, subnets) if not update_target or update_target == VPN_TARGET: vpn_connection_api._update_vpn_routes(context, neutron, cleaner, route_table, subnets)
def associate_dhcp_options(context, dhcp_options_id, vpc_id): vpc = ec2utils.get_db_item(context, vpc_id) rollback_dhcp_options_id = vpc.get('dhcp_options_id') if dhcp_options_id == 'default': dhcp_options_id = None dhcp_options = None else: dhcp_options = ec2utils.get_db_item(context, dhcp_options_id) dhcp_options_id = dhcp_options['id'] neutron = clients.neutron(context) os_ports = neutron.list_ports(tenant_id=context.project_id)['ports'] network_interfaces = db_api.get_items(context, 'eni') rollback_dhcp_options_object = ( db_api.get_item_by_id(context, rollback_dhcp_options_id) if dhcp_options_id is not None else None) with common.OnCrashCleaner() as cleaner: _associate_vpc_item(context, vpc, dhcp_options_id) cleaner.addCleanup(_associate_vpc_item, context, vpc, rollback_dhcp_options_id) for network_interface in network_interfaces: os_port = next((p for p in os_ports if p['id'] == network_interface['os_id']), None) if not os_port: continue _add_dhcp_opts_to_port(context, dhcp_options, network_interface, os_port, neutron) cleaner.addCleanup(_add_dhcp_opts_to_port, context, rollback_dhcp_options_object, network_interface, os_port, neutron) return True
def _update_routes_in_associated_subnets(context, route_table, cleaner, rollabck_route_table_object, is_main=None): if is_main is None: vpc = db_api.get_item_by_id(context, route_table['vpc_id']) is_main = vpc['route_table_id'] == route_table['id'] if is_main: appropriate_rtb_ids = (route_table['id'], None) else: appropriate_rtb_ids = (route_table['id'], ) router_objects = _get_router_objects(context, route_table) neutron = clients.neutron(context) for subnet in db_api.get_items(context, 'subnet'): if (subnet['vpc_id'] == route_table['vpc_id'] and subnet.get('route_table_id') in appropriate_rtb_ids): _update_subnet_host_routes( context, subnet, route_table, cleaner=cleaner, rollback_route_table_object=rollabck_route_table_object, router_objects=router_objects, neutron=neutron)
def delete_route(context, route_table_id, destination_cidr_block): route_table = ec2utils.get_db_item(context, route_table_id) for route_index, route in enumerate(route_table['routes']): if route['destination_cidr_block'] != destination_cidr_block: continue if route.get('gateway_id', 0) is None: msg = _('cannot remove local route %(destination_cidr_block)s ' 'in route table %(route_table_id)s') msg = msg % {'route_table_id': route_table_id, 'destination_cidr_block': destination_cidr_block} raise exception.InvalidParameterValue(msg) break else: raise exception.InvalidRouteNotFound( route_table_id=route_table_id, destination_cidr_block=destination_cidr_block) update_target = _get_route_target(route) if update_target == VPN_TARGET: vpn_gateway = db_api.get_item_by_id(context, route['gateway_id']) if (not vpn_gateway or vpn_gateway['vpc_id'] != route_table['vpc_id']): update_target = None rollback_route_table_state = copy.deepcopy(route_table) del route_table['routes'][route_index] with common.OnCrashCleaner() as cleaner: db_api.update_item(context, route_table) cleaner.addCleanup(db_api.update_item, context, rollback_route_table_state) if update_target: _update_routes_in_associated_subnets( context, cleaner, route_table, update_target=update_target) return True
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 disassociate_address(self, context, public_ip=None, association_id=None): neutron = clients.neutron(context) if public_ip: # TODO(ft): implement search in DB layer address = next((addr for addr in db_api.get_items(context, 'eipalloc') if addr['public_ip'] == public_ip), None) if address and _is_address_valid(context, neutron, address): msg = _('You must specify an association id when unmapping ' 'an address from a VPC instance') raise exception.InvalidParameterValue(msg) # NOTE(ft): association_id is unused in EC2 Classic mode, but it's # passed there to validate its emptiness in one place return AddressEngineNova().disassociate_address( context, public_ip=public_ip, association_id=association_id) address = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'eipalloc')) if address is None or not _is_address_valid(context, neutron, address): raise exception.InvalidAssociationIDNotFound( id=association_id) if 'network_interface_id' in address: with common.OnCrashCleaner() as cleaner: network_interface_id = address['network_interface_id'] private_ip_address = address['private_ip_address'] _disassociate_address_item(context, address) cleaner.addCleanup(_associate_address_item, context, address, network_interface_id, private_ip_address) neutron.update_floatingip(address['os_id'], {'floatingip': {'port_id': None}})
def associate_dhcp_options(context, dhcp_options_id, vpc_id): vpc = ec2utils.get_db_item(context, vpc_id) rollback_dhcp_options_id = vpc.get('dhcp_options_id') if dhcp_options_id == 'default': dhcp_options_id = None dhcp_options = None else: dhcp_options = ec2utils.get_db_item(context, dhcp_options_id) dhcp_options_id = dhcp_options['id'] neutron = clients.neutron(context) os_ports = neutron.list_ports(tenant_id=context.project_id)['ports'] network_interfaces = db_api.get_items(context, 'eni') rollback_dhcp_options_object = (db_api.get_item_by_id( context, rollback_dhcp_options_id) if dhcp_options_id is not None else None) with common.OnCrashCleaner() as cleaner: _associate_vpc_item(context, vpc, dhcp_options_id) cleaner.addCleanup(_associate_vpc_item, context, vpc, rollback_dhcp_options_id) for network_interface in network_interfaces: os_port = next( (p for p in os_ports if p['id'] == network_interface['os_id']), None) if not os_port: continue _add_dhcp_opts_to_port(context, dhcp_options, network_interface, os_port, neutron) cleaner.addCleanup(_add_dhcp_opts_to_port, context, rollback_dhcp_options_object, network_interface, os_port, neutron) return True
def disassociate_address(self, context, public_ip=None, association_id=None): neutron = clients.neutron(context) if public_ip: # TODO(ft): implement search in DB layer address = next((addr for addr in db_api.get_items(context, 'eipalloc') if addr['public_ip'] == public_ip), None) if address and _is_address_valid(context, neutron, address): msg = _('You must specify an association id when unmapping ' 'an address from a VPC instance') raise exception.InvalidParameterValue(msg) # NOTE(ft): association_id is unused in EC2 Classic mode, but it's # passed there to validate its emptiness in one place return AddressEngineNova().disassociate_address( context, public_ip=public_ip, association_id=association_id) address = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'eipalloc')) if address is None or not _is_address_valid(context, neutron, address): raise exception.InvalidAssociationIDNotFound(id=association_id) if 'network_interface_id' in address: with common.OnCrashCleaner() as cleaner: network_interface_id = address['network_interface_id'] private_ip_address = address['private_ip_address'] _disassociate_address_item(context, address) cleaner.addCleanup(_associate_address_item, context, address, network_interface_id, private_ip_address) neutron.update_floatingip(address['os_id'], {'floatingip': { 'port_id': None }})
def test_add_item(self): new_item = { 'os_id': fakes.random_os_id(), 'vpc_id': fakes.random_ec2_id('fake_vpc'), 'str_attr': 'fake_str', 'int_attr': 1234, 'bool_attr': True, 'dict_attr': { 'key1': 'val1', 'key2': 'val2' }, 'list_attr': ['fake_str', 1234, True, { 'key': 'val' }, []] } item = db_api.add_item(self.context, 'fake', new_item) self.assertIn('id', item) self.assertIsNotNone(item['id']) item_id = item.pop('id') self.assertTrue(validator.validate_ec2_id(item_id, '', ['fake'])) self.assertThat(item, matchers.DictMatches(new_item, orderless_lists=True)) item = db_api.get_item_by_id(self.context, item_id) new_item['id'] = item_id self.assertThat(item, matchers.DictMatches(new_item, orderless_lists=True))
def get_nexthop(route): if 'gateway_id' in route: gateway_id = route['gateway_id'] if gateway_id: gateway = (router_objects[route['gateway_id']] if router_objects else db_api.get_item_by_id( context, gateway_id)) if (not gateway or gateway.get('vpc_id') != route_table['vpc_id']): return '127.0.0.1' return gateway_ip network_interface = (router_objects[route['network_interface_id']] if router_objects else db_api.get_item_by_id( context, route['network_interface_id'])) if not network_interface: return '127.0.0.1' return network_interface['private_ip_address']
def test_get_item_by_id(self): self._setup_items() item_id = db_api.get_items(self.context, 'fake')[0]['id'] other_item_id = db_api.get_items(self.other_context, 'fake')[0]['id'] item = db_api.get_item_by_id(self.context, item_id) self.assertThat( item, matchers.DictMatches({ 'id': item_id, 'os_id': None, 'vpc_id': None })) item = db_api.get_item_by_id(self.context, other_item_id) self.assertIsNone(item) item = db_api.get_item_by_id(self.context, fakes.random_ec2_id('fake')) self.assertIsNone(item)
def create_subnet(context, vpc_id, cidr_block, availability_zone=None): vpc = ec2utils.get_db_item(context, vpc_id) vpc_ipnet = netaddr.IPNetwork(vpc['cidr_block']) subnet_ipnet = netaddr.IPNetwork(cidr_block) if subnet_ipnet not in vpc_ipnet: raise exception.InvalidSubnetRange(cidr_block=cidr_block) gateway_ip = str(netaddr.IPAddress(subnet_ipnet.first + 1)) main_route_table = db_api.get_item_by_id(context, vpc['route_table_id']) host_routes = route_table_api._get_subnet_host_routes( context, main_route_table, gateway_ip) neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: os_network_body = {'network': {}} try: os_network = neutron.create_network(os_network_body)['network'] cleaner.addCleanup(neutron.delete_network, os_network['id']) # NOTE(Alex): AWS takes 4 first addresses (.1 - .4) but for # OpenStack we decided not to support this as compatibility. os_subnet_body = { 'subnet': { 'network_id': os_network['id'], 'ip_version': '4', 'cidr': cidr_block, 'host_routes': host_routes } } os_subnet = neutron.create_subnet(os_subnet_body)['subnet'] cleaner.addCleanup(neutron.delete_subnet, os_subnet['id']) except neutron_exception.OverQuotaClient: raise exception.SubnetLimitExceeded() try: neutron.add_interface_router(vpc['os_id'], {'subnet_id': os_subnet['id']}) except neutron_exception.BadRequest: raise exception.InvalidSubnetConflict(cidr_block=cidr_block) cleaner.addCleanup(neutron.remove_interface_router, vpc['os_id'], {'subnet_id': os_subnet['id']}) subnet = db_api.add_item(context, 'subnet', { 'os_id': os_subnet['id'], 'vpc_id': vpc['id'] }) cleaner.addCleanup(db_api.delete_item, context, subnet['id']) neutron.update_network(os_network['id'], {'network': { 'name': subnet['id'] }}) neutron.update_subnet(os_subnet['id'], {'subnet': { 'name': subnet['id'] }}) os_ports = neutron.list_ports(tenant_id=context.project_id)['ports'] return { 'subnet': _format_subnet(context, subnet, os_subnet, os_network, os_ports) }
def assert_image_project(self, expected_project_id, image_id): if expected_project_id: context = mock.NonCallableMock(project_id=expected_project_id) else: context = self.context image_item = db_api.get_item_by_id(context, image_id) if expected_project_id: self.assertIsNotNone(image_item) else: self.assertIsNone(image_item)
def create_subnet(context, vpc_id, cidr_block, availability_zone=None): vpc = ec2utils.get_db_item(context, vpc_id) vpc_ipnet = netaddr.IPNetwork(vpc['cidr_block']) subnet_ipnet = netaddr.IPNetwork(cidr_block) if subnet_ipnet not in vpc_ipnet: raise exception.InvalidSubnetRange(cidr_block=cidr_block) main_route_table = db_api.get_item_by_id(context, vpc['route_table_id']) (host_routes, gateway_ip) = route_table_api._get_subnet_host_routes_and_gateway_ip( context, main_route_table, cidr_block) neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: # NOTE(andrey-mp): set fake name to filter networks in instance api os_network_body = {'network': {'name': 'subnet-0'}} try: os_network = neutron.create_network(os_network_body)['network'] cleaner.addCleanup(neutron.delete_network, os_network['id']) # NOTE(Alex): AWS takes 4 first addresses (.1 - .4) but for # OpenStack we decided not to support this as compatibility. os_subnet_body = {'subnet': {'network_id': os_network['id'], 'ip_version': '4', 'cidr': cidr_block, 'host_routes': host_routes}} os_subnet = neutron.create_subnet(os_subnet_body)['subnet'] cleaner.addCleanup(neutron.delete_subnet, os_subnet['id']) except neutron_exception.OverQuotaClient: raise exception.SubnetLimitExceeded() try: neutron.add_interface_router(vpc['os_id'], {'subnet_id': os_subnet['id']}) except neutron_exception.BadRequest: raise exception.InvalidSubnetConflict(cidr_block=cidr_block) cleaner.addCleanup(neutron.remove_interface_router, vpc['os_id'], {'subnet_id': os_subnet['id']}) subnet = db_api.add_item(context, 'subnet', {'os_id': os_subnet['id'], 'vpc_id': vpc['id']}) cleaner.addCleanup(db_api.delete_item, context, subnet['id']) vpn_gateway_api._start_vpn_in_subnet(context, neutron, cleaner, subnet, vpc, main_route_table) neutron.update_network(os_network['id'], {'network': {'name': subnet['id']}}) # NOTE(ft): In some cases we need gateway_ip to be None (see # _get_subnet_host_routes_and_gateway_ip). It's not set during subnet # creation to allow automatic configuration of the default port by # which subnet is attached to the router. neutron.update_subnet(os_subnet['id'], {'subnet': {'name': subnet['id'], 'gateway_ip': gateway_ip}}) os_ports = neutron.list_ports(tenant_id=context.project_id)['ports'] return {'subnet': _format_subnet(context, subnet, os_subnet, os_network, os_ports)}
def test_update_item_os_id(self): item = db_api.add_item(self.context, 'fake', {}) item['os_id'] = 'fake_os_id' db_api.update_item(self.context, item) item = db_api.get_item_by_id(self.context, item['id']) self.assertThat({'os_id': 'fake_os_id'}, matchers.IsSubDictOf(item)) item['os_id'] = 'other_fake_os_id' self.assertRaises(exception.EC2DBInvalidOsIdUpdate, db_api.update_item, self.context, item) item['os_id'] = None self.assertRaises(exception.EC2DBInvalidOsIdUpdate, db_api.update_item, self.context, item)
def release_address(self, context, public_ip, allocation_id): neutron = clients.neutron(context) if public_ip: # TODO(ft): implement search in DB layer address = next((addr for addr in db_api.get_items(context, 'eipalloc') if addr['public_ip'] == public_ip), None) if address and _is_address_valid(context, neutron, address): msg = _('You must specify an allocation id when releasing a ' 'VPC elastic IP address') raise exception.InvalidParameterValue(msg) os_floating_ip = self.get_os_floating_ip_by_public_ip(context, public_ip) try: neutron.delete_floatingip(os_floating_ip['id']) except neutron_exception.NotFound: pass return address = ec2utils.get_db_item(context, allocation_id) if not _is_address_valid(context, neutron, address): raise exception.InvalidAllocationIDNotFound( id=allocation_id) if 'network_interface_id' in address: if CONF.disable_ec2_classic: network_interface_id = address['network_interface_id'] network_interface = db_api.get_item_by_id(context, network_interface_id) default_vpc = ec2utils.check_and_create_default_vpc(context) if default_vpc: default_vpc_id = default_vpc['id'] if (network_interface and network_interface['vpc_id'] == default_vpc_id): association_id = ec2utils.change_ec2_id_kind(address['id'], 'eipassoc') self.disassociate_address( context, association_id=association_id) else: raise exception.InvalidIPAddressInUse( ip_address=address['public_ip']) else: raise exception.InvalidIPAddressInUse( ip_address=address['public_ip']) with common.OnCrashCleaner() as cleaner: db_api.delete_item(context, address['id']) cleaner.addCleanup(db_api.restore_item, context, 'eipalloc', address) try: neutron.delete_floatingip(address['os_id']) except neutron_exception.NotFound: pass
def create_subnet(context, vpc_id, cidr_block, availability_zone=None): vpc = ec2utils.get_db_item(context, vpc_id) vpc_ipnet = netaddr.IPNetwork(vpc['cidr_block']) subnet_ipnet = netaddr.IPNetwork(cidr_block) if subnet_ipnet not in vpc_ipnet: raise exception.InvalidSubnetRange(cidr_block=cidr_block) main_route_table = db_api.get_item_by_id(context, vpc['route_table_id']) (host_routes, gateway_ip) = route_table_api._get_subnet_host_routes_and_gateway_ip( context, main_route_table, cidr_block) neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: os_network_body = {'network': {}} try: os_network = neutron.create_network(os_network_body)['network'] cleaner.addCleanup(neutron.delete_network, os_network['id']) # NOTE(Alex): AWS takes 4 first addresses (.1 - .4) but for # OpenStack we decided not to support this as compatibility. os_subnet_body = {'subnet': {'network_id': os_network['id'], 'ip_version': '4', 'cidr': cidr_block, 'host_routes': host_routes}} os_subnet = neutron.create_subnet(os_subnet_body)['subnet'] cleaner.addCleanup(neutron.delete_subnet, os_subnet['id']) except neutron_exception.OverQuotaClient: raise exception.SubnetLimitExceeded() try: neutron.add_interface_router(vpc['os_id'], {'subnet_id': os_subnet['id']}) except neutron_exception.BadRequest: raise exception.InvalidSubnetConflict(cidr_block=cidr_block) cleaner.addCleanup(neutron.remove_interface_router, vpc['os_id'], {'subnet_id': os_subnet['id']}) subnet = db_api.add_item(context, 'subnet', {'os_id': os_subnet['id'], 'vpc_id': vpc['id']}) cleaner.addCleanup(db_api.delete_item, context, subnet['id']) vpn_gateway_api._start_vpn_in_subnet(context, neutron, cleaner, subnet, vpc, main_route_table) neutron.update_network(os_network['id'], {'network': {'name': subnet['id']}}) # NOTE(ft): In some cases we need gateway_ip to be None (see # _get_subnet_host_routes_and_gateway_ip). It's not set during subnet # creation to allow automatic configuration of the default port by # which subnet is attached to the router. neutron.update_subnet(os_subnet['id'], {'subnet': {'name': subnet['id'], 'gateway_ip': gateway_ip}}) os_ports = neutron.list_ports(tenant_id=context.project_id)['ports'] return {'subnet': _format_subnet(context, subnet, os_subnet, os_network, os_ports)}
def replace_route_table_association(context, association_id, route_table_id): route_table = ec2utils.get_db_item(context, route_table_id) if route_table['vpc_id'] == ec2utils.change_ec2_id_kind( association_id, 'vpc'): vpc = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'vpc')) if vpc is None: raise exception.InvalidAssociationIDNotFound(id=association_id) rollback_route_table_id = vpc['route_table_id'] with common.OnCrashCleaner() as cleaner: _associate_vpc_item(context, vpc, route_table['id']) cleaner.addCleanup(_associate_vpc_item, context, vpc, rollback_route_table_id) _update_routes_in_associated_subnets( context, cleaner, route_table, default_associations_only=True) else: subnet = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'subnet')) if subnet is None or 'route_table_id' not in subnet: raise exception.InvalidAssociationIDNotFound(id=association_id) if subnet['vpc_id'] != route_table['vpc_id']: msg = _('Route table association %(rtbassoc_id)s and route table ' '%(rtb_id)s belong to different networks') msg = msg % { 'rtbassoc_id': association_id, 'rtb_id': route_table_id } raise exception.InvalidParameterValue(msg) rollback_route_table_id = subnet['route_table_id'] with common.OnCrashCleaner() as cleaner: _associate_subnet_item(context, subnet, route_table['id']) cleaner.addCleanup(_associate_subnet_item, context, subnet, rollback_route_table_id) _update_subnet_routes(context, cleaner, subnet, route_table) return {'newAssociationId': association_id}
def disassociate_address(self, context, public_ip=None, association_id=None): neutron = clients.neutron(context) if public_ip: # TODO(ft): implement search in DB layer address = next((addr for addr in db_api.get_items(context, 'eipalloc') if addr['public_ip'] == public_ip), None) if not CONF.disable_ec2_classic: if address and _is_address_valid(context, neutron, address): msg = _('You must specify an association id when ' 'unmapping an address from a VPC instance') raise exception.InvalidParameterValue(msg) # NOTE(tikitavi): check the public IP exists to raise AWS # exception otherwise os_floating_ip = self.get_os_floating_ip_by_public_ip( context, public_ip) os_ports = self.get_os_ports(context) os_instance_id = _get_os_instance_id(context, os_floating_ip, os_ports) if os_instance_id: nova = clients.nova(context) nova.servers.remove_floating_ip(os_instance_id, public_ip) return None if not address: msg = _("The address '%(public_ip)s' does not belong to you.") raise exception.AuthFailure(msg % {'public_ip': public_ip}) if 'network_interface_id' not in address: msg = _('You must specify an association id when unmapping ' 'an address from a VPC instance') raise exception.InvalidParameterValue(msg) association_id = ec2utils.change_ec2_id_kind(address['id'], 'eipassoc') address = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'eipalloc')) if address is None or not _is_address_valid(context, neutron, address): raise exception.InvalidAssociationIDNotFound( id=association_id) if 'network_interface_id' in address: with common.OnCrashCleaner() as cleaner: network_interface_id = address['network_interface_id'] private_ip_address = address['private_ip_address'] _disassociate_address_item(context, address) cleaner.addCleanup(_associate_address_item, context, address, network_interface_id, private_ip_address) neutron.update_floatingip(address['os_id'], {'floatingip': {'port_id': None}})
def replace_route_table_association(context, association_id, route_table_id): route_table = ec2utils.get_db_item(context, route_table_id) if route_table['vpc_id'] == ec2utils.change_ec2_id_kind(association_id, 'vpc'): vpc = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'vpc')) if vpc is None: raise exception.InvalidAssociationIDNotFound(id=association_id) rollback_route_table_id = vpc['route_table_id'] with common.OnCrashCleaner() as cleaner: _associate_vpc_item(context, vpc, route_table['id']) cleaner.addCleanup(_associate_vpc_item, context, vpc, rollback_route_table_id) _update_routes_in_associated_subnets( context, cleaner, route_table, default_associations_only=True) else: subnet = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(association_id, 'subnet')) if subnet is None or 'route_table_id' not in subnet: raise exception.InvalidAssociationIDNotFound(id=association_id) if subnet['vpc_id'] != route_table['vpc_id']: msg = _('Route table association %(rtbassoc_id)s and route table ' '%(rtb_id)s belong to different networks') msg = msg % {'rtbassoc_id': association_id, 'rtb_id': route_table_id} raise exception.InvalidParameterValue(msg) rollback_route_table_id = subnet['route_table_id'] with common.OnCrashCleaner() as cleaner: _associate_subnet_item(context, subnet, route_table['id']) cleaner.addCleanup(_associate_subnet_item, context, subnet, rollback_route_table_id) _update_subnet_routes(context, cleaner, subnet, route_table) return {'newAssociationId': association_id}
def test_update_item(self): item = db_api.add_item(self.context, 'fake', {'key': 'val1', 'key1': 'val'}) item['key'] = 'val2' item.pop('key1') item['key2'] = 'val' item_id = item['id'] db_api.update_item(self.context, item) item = db_api.get_item_by_id(self.context, item_id) self.assertThat(item, matchers.DictMatches({'id': item_id, 'os_id': None, 'vpc_id': None, 'key': 'val2', 'key2': 'val'}))
def _delete_route_table(context, route_table_id, vpc=None, cleaner=None): def get_associated_subnets(): return [s for s in db_api.get_items(context, 'subnet') if s.get('route_table_id') == route_table_id] if (vpc and route_table_id == vpc['route_table_id'] or len(get_associated_subnets()) > 0): msg = _("The routeTable '%(rtb_id)s' has dependencies and cannot " "be deleted.") % {'rtb_id': route_table_id} raise exception.DependencyViolation(msg) if cleaner: route_table = db_api.get_item_by_id(context, route_table_id) db_api.delete_item(context, route_table_id) if cleaner and route_table: cleaner.addCleanup(db_api.restore_item, context, 'rtb', route_table)
def disable_vgw_route_propagation(context, route_table_id, gateway_id): route_table = ec2utils.get_db_item(context, route_table_id) if gateway_id not in route_table.get('propagating_gateways', []): return True vpn_gateway = db_api.get_item_by_id(context, gateway_id) with common.OnCrashCleaner() as cleaner: _remove_propagation_from_route_table_item(context, route_table, gateway_id) cleaner.addCleanup(_append_propagation_to_route_table_item, context, route_table, gateway_id) if vpn_gateway and vpn_gateway['vpc_id'] == route_table['vpc_id']: _update_routes_in_associated_subnets(context, cleaner, route_table, update_target=VPN_TARGET) return True
def _reset_vpn_connections(context, neutron, cleaner, vpn_gateway, subnets=None, route_tables=None, vpn_connections=None): if not vpn_gateway['vpc_id']: return # TODO(ft): implement search filters in DB api vpn_connections = (vpn_connections or [ vpn for vpn in db_api.get_items(context, 'vpn') if vpn['vpn_gateway_id'] == vpn_gateway['id'] ]) if not vpn_connections: return subnets = (subnets or [ subnet for subnet in db_api.get_items(context, 'subnet') if subnet['vpc_id'] == vpn_gateway['vpc_id'] ]) if not subnets: return vpc = db_api.get_item_by_id(context, vpn_gateway['vpc_id']) customer_gateways = { cgw['id']: cgw for cgw in db_api.get_items(context, 'cgw') } route_tables = route_tables or db_api.get_items(context, 'rtb') route_tables = { rtb['id']: rtb for rtb in route_tables if rtb['vpc_id'] == vpc['id'] } route_tables_cidrs = {} for subnet in subnets: route_table_id = subnet.get('route_table_id', vpc['route_table_id']) if route_table_id not in route_tables_cidrs: route_tables_cidrs[route_table_id] = (_get_route_table_vpn_cidrs( route_tables[route_table_id], vpn_gateway, vpn_connections)) cidrs = route_tables_cidrs[route_table_id] for vpn_conn in vpn_connections: if vpn_conn['id'] in cidrs: _set_subnet_vpn( context, neutron, cleaner, subnet, vpn_conn, customer_gateways[vpn_conn['customer_gateway_id']], cidrs[vpn_conn['id']]) else: _delete_subnet_vpn(context, neutron, cleaner, subnet, vpn_conn)
def create_vpn_connection_route(context, vpn_connection_id, destination_cidr_block): vpn_connection = ec2utils.get_db_item(context, vpn_connection_id) if destination_cidr_block in vpn_connection['cidrs']: return True neutron = clients.neutron(context) vpn_gateway = db_api.get_item_by_id(context, vpn_connection['vpn_gateway_id']) with common.OnCrashCleaner() as cleaner: _add_cidr_to_vpn_connection_item(context, vpn_connection, destination_cidr_block) cleaner.addCleanup(_remove_cidr_from_vpn_connection_item, context, vpn_connection, destination_cidr_block) _reset_vpn_connections(context, neutron, cleaner, vpn_gateway, vpn_connections=[vpn_connection]) return True
def deregister_image(context, image_id): os_image = ec2utils.get_os_image(context, image_id) if not os_image: image = db_api.get_item_by_id(context, image_id) if image.get('state') != 'failed': # TODO(ft): figure out corresponding AWS error raise exception.IncorrectState( reason='Image is still being created') else: _check_owner(context, os_image) glance = clients.glance(context) try: glance.images.delete(os_image.id) except glance_exception.HTTPNotFound: pass db_api.delete_item(context, image_id) return True
def delete_subnet(context, subnet_id): subnet = ec2utils.get_db_item(context, subnet_id) vpc = db_api.get_item_by_id(context, subnet['vpc_id']) network_interfaces = network_interface_api.describe_network_interfaces( context, filter=[{ 'name': 'subnet-id', 'value': [subnet_id] }])['networkInterfaceSet'] if network_interfaces: msg = _("The subnet '%(subnet_id)s' has dependencies and " "cannot be deleted.") % { 'subnet_id': subnet_id } raise exception.DependencyViolation(msg) neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: db_api.delete_item(context, subnet['id']) cleaner.addCleanup(db_api.restore_item, context, 'subnet', subnet) vpn_gateway_api._stop_vpn_in_subnet(context, neutron, cleaner, subnet) try: neutron.remove_interface_router(vpc['os_id'], {'subnet_id': subnet['os_id']}) except neutron_exception.NotFound: pass cleaner.addCleanup(neutron.add_interface_router, vpc['os_id'], {'subnet_id': subnet['os_id']}) try: os_subnet = neutron.show_subnet(subnet['os_id'])['subnet'] except neutron_exception.NotFound: pass else: try: neutron.delete_network(os_subnet['network_id']) except neutron_exception.NetworkInUseClient as ex: LOG.warning( _('Failed to delete network %(os_id)s during ' 'deleting Subnet %(id)s. Reason: %(reason)s'), { 'id': subnet['id'], 'os_id': os_subnet['network_id'], 'reason': ex.message }) return True
def assign_private_ip_addresses(context, network_interface_id, private_ip_address=None, secondary_private_ip_address_count=None, allow_reassignment=False): # TODO(Alex): allow_reassignment is not supported at the moment network_interface = ec2utils.get_db_item(context, network_interface_id) subnet = db_api.get_item_by_id(context, network_interface['subnet_id']) neutron = clients.neutron(context) os_subnet = neutron.show_subnet(subnet['os_id'])['subnet'] os_port = neutron.show_port(network_interface['os_id'])['port'] subnet_ipnet = netaddr.IPNetwork(os_subnet['cidr']) fixed_ips = os_port['fixed_ips'] or [] if private_ip_address is not None: for ip_address in private_ip_address: if netaddr.IPAddress(ip_address) not in subnet_ipnet: raise exception.InvalidParameterValue( value=str(ip_address), parameter='PrivateIpAddress', reason='IP address is out of the subnet range') fixed_ips.append({'ip_address': str(ip_address)}) elif secondary_private_ip_address_count > 0: for _i in range(secondary_private_ip_address_count): fixed_ips.append({'subnet_id': os_subnet['id']}) try: neutron.update_port(os_port['id'], {'port': {'fixed_ips': fixed_ips}}) except neutron_exception.IpAddressGenerationFailureClient: raise exception.InsufficientFreeAddressesInSubnet() except neutron_exception.IpAddressInUseClient: msg = _('Some of %(addresses)s is assigned, but move is not ' 'allowed.') % { 'addresses': private_ip_address } raise exception.InvalidParameterValue(msg) except neutron_exception.BadRequest as ex: # NOTE(ft):AWS returns PrivateIpAddressLimitExceeded, but Neutron does # general InvalidInput (converted to BadRequest) in the same case. msg = _('Specified network interface parameters are invalid. ' 'Reason: %(reason)s') % { 'reason': ex.message } raise exception.InvalidParameterValue(msg) return True
def test_add_item_id(self): os_id = fakes.random_os_id() item_id = db_api.add_item_id(self.context, 'fake', os_id) self.assertTrue(validator.validate_ec2_id(item_id, '', ['fake'])) item = db_api.get_item_by_id(self.context, item_id) self.assertIsNone(item) item = db_api.add_item(self.context, 'fake', {'os_id': os_id}) self.assertThat(item, matchers.DictMatches({'id': item_id, 'os_id': os_id, 'vpc_id': None})) # NOTE(ft): add os_id when item exists item_id = db_api.add_item_id(self.context, 'fake', os_id) self.assertEqual(item_id, item['id']) # NOTE(ft): add os_id when id exists os_id = fakes.random_os_id() item_id1 = db_api.add_item_id(self.context, 'fake', os_id) item_id2 = db_api.add_item_id(self.context, 'fake', os_id) self.assertEqual(item_id1, item_id2)
def get_db_item(context, ec2_id, expected_kind=None): """Get an DB item, raise AWS compliant exception if it's not found. Args: context (RequestContext): The request context. ec2_id (str): The ID of the requested item. expected_kind (str): The expected kind of the requested item. It should be specified for a kind of ec2_id to be validated, if you need it. Returns: The DB item. """ item = db_api.get_item_by_id(context, ec2_id) if (item is None or expected_kind and get_ec2_id_kind(ec2_id) != expected_kind): kind = expected_kind or get_ec2_id_kind(ec2_id) params = {'id': ec2_id} raise NOT_FOUND_EXCEPTION_MAP[kind](**params) return item
def delete_vpn_connection_route(context, vpn_connection_id, destination_cidr_block): vpn_connection = ec2utils.get_db_item(context, vpn_connection_id) if destination_cidr_block not in vpn_connection['cidrs']: raise exception.InvalidRouteNotFound( _('The specified route %(destination_cidr_block)s does not exist') % {'destination_cidr_block': destination_cidr_block}) neutron = clients.neutron(context) vpn_gateway = db_api.get_item_by_id(context, vpn_connection['vpn_gateway_id']) with common.OnCrashCleaner() as cleaner: _remove_cidr_from_vpn_connection_item(context, vpn_connection, destination_cidr_block) cleaner.addCleanup(_add_cidr_to_vpn_connection_item, context, vpn_connection, destination_cidr_block) _reset_vpn_connections(context, neutron, cleaner, vpn_gateway, vpn_connections=[vpn_connection]) return True
def test_update_item(self): item = db_api.add_item(self.context, 'fake', { 'key': 'val1', 'key1': 'val' }) item['key'] = 'val2' item.pop('key1') item['key2'] = 'val' item_id = item['id'] db_api.update_item(self.context, item) item = db_api.get_item_by_id(self.context, item_id) self.assertThat( item, matchers.DictMatches({ 'id': item_id, 'os_id': None, 'vpc_id': None, 'key': 'val2', 'key2': 'val' }))
def test_add_item(self): new_item = {'os_id': fakes.random_os_id(), 'vpc_id': fakes.random_ec2_id('fake_vpc'), 'str_attr': 'fake_str', 'int_attr': 1234, 'bool_attr': True, 'dict_attr': {'key1': 'val1', 'key2': 'val2'}, 'list_attr': ['fake_str', 1234, True, {'key': 'val'}, []]} item = db_api.add_item(self.context, 'fake', new_item) self.assertIn('id', item) self.assertIsNotNone(item['id']) item_id = item.pop('id') self.assertTrue(validator.validate_ec2_id(item_id, '', ['fake'])) self.assertThat(item, matchers.DictMatches(new_item, orderless_lists=True)) item = db_api.get_item_by_id(self.context, item_id) new_item['id'] = item_id self.assertThat(item, matchers.DictMatches(new_item, orderless_lists=True))
def _update_routes_in_associated_subnets(context, cleaner, route_table, default_associations_only=None, update_target=None): if default_associations_only: appropriate_rtb_ids = (None,) else: vpc = db_api.get_item_by_id(context, route_table['vpc_id']) if vpc['route_table_id'] == route_table['id']: appropriate_rtb_ids = (route_table['id'], None) else: appropriate_rtb_ids = (route_table['id'],) neutron = clients.neutron(context) subnets = [subnet for subnet in db_api.get_items(context, 'subnet') if (subnet['vpc_id'] == route_table['vpc_id'] and subnet.get('route_table_id') in appropriate_rtb_ids)] # NOTE(ft): we need to update host routes for both host and vpn target # because vpn-related routes are present in host routes as well _update_host_routes(context, neutron, cleaner, route_table, subnets) if not update_target or update_target == VPN_TARGET: vpn_connection_api._update_vpn_routes(context, neutron, cleaner, route_table, subnets)
def assign_private_ip_addresses(context, network_interface_id, private_ip_address=None, secondary_private_ip_address_count=None, allow_reassignment=False): # TODO(Alex): allow_reassignment is not supported at the moment network_interface = ec2utils.get_db_item(context, network_interface_id) subnet = db_api.get_item_by_id(context, network_interface['subnet_id']) neutron = clients.neutron(context) os_subnet = neutron.show_subnet(subnet['os_id'])['subnet'] os_port = neutron.show_port(network_interface['os_id'])['port'] subnet_ipnet = netaddr.IPNetwork(os_subnet['cidr']) fixed_ips = os_port['fixed_ips'] or [] if private_ip_address is not None: for ip_address in private_ip_address: if netaddr.IPAddress(ip_address) not in subnet_ipnet: raise exception.InvalidParameterValue( value=str(ip_address), parameter='PrivateIpAddress', reason='IP address is out of the subnet range') fixed_ips.append({'ip_address': str(ip_address)}) elif secondary_private_ip_address_count > 0: for _i in range(secondary_private_ip_address_count): fixed_ips.append({'subnet_id': os_subnet['id']}) try: neutron.update_port(os_port['id'], {'port': {'fixed_ips': fixed_ips}}) except neutron_exception.IpAddressGenerationFailureClient: raise exception.NetworkInterfaceLimitExceeded( subnet_id=subnet['id']) except neutron_exception.IpAddressInUseClient: msg = _('Some of %(addresses)s is assigned, but move is not ' 'allowed.') % {'addresses': private_ip_address} raise exception.InvalidParameterValue(msg) except neutron_exception.BadRequest as ex: # NOTE(ft):AWS returns PrivateIpAddressLimitExceeded, but Neutron does # general InvalidInput (converted to BadRequest) in the same case. msg = _('Specified network interface parameters are invalid. ' 'Reason: %(reason)s') % {'reason': ex.message} raise exception.InvalidParameterValue(msg) return True
def _reset_vpn_connections(context, neutron, cleaner, vpn_gateway, subnets=None, route_tables=None, vpn_connections=None): if not vpn_gateway['vpc_id']: return # TODO(ft): implement search filters in DB api vpn_connections = (vpn_connections or [vpn for vpn in db_api.get_items(context, 'vpn') if vpn['vpn_gateway_id'] == vpn_gateway['id']]) if not vpn_connections: return subnets = (subnets or [subnet for subnet in db_api.get_items(context, 'subnet') if subnet['vpc_id'] == vpn_gateway['vpc_id']]) if not subnets: return vpc = db_api.get_item_by_id(context, vpn_gateway['vpc_id']) customer_gateways = {cgw['id']: cgw for cgw in db_api.get_items(context, 'cgw')} route_tables = route_tables or db_api.get_items(context, 'rtb') route_tables = {rtb['id']: rtb for rtb in route_tables if rtb['vpc_id'] == vpc['id']} route_tables_cidrs = {} for subnet in subnets: route_table_id = subnet.get('route_table_id', vpc['route_table_id']) if route_table_id not in route_tables_cidrs: route_tables_cidrs[route_table_id] = ( _get_route_table_vpn_cidrs(route_tables[route_table_id], vpn_gateway, vpn_connections)) cidrs = route_tables_cidrs[route_table_id] for vpn_conn in vpn_connections: if vpn_conn['id'] in cidrs: _set_subnet_vpn( context, neutron, cleaner, subnet, vpn_conn, customer_gateways[vpn_conn['customer_gateway_id']], cidrs[vpn_conn['id']]) else: _delete_subnet_vpn(context, neutron, cleaner, subnet, vpn_conn)
def delete_subnet(context, subnet_id): subnet = ec2utils.get_db_item(context, subnet_id) vpc = db_api.get_item_by_id(context, subnet['vpc_id']) network_interfaces = network_interface_api.describe_network_interfaces( context, filter=[{'name': 'subnet-id', 'value': [subnet_id]}])['networkInterfaceSet'] if network_interfaces: msg = _("The subnet '%(subnet_id)s' has dependencies and " "cannot be deleted.") % {'subnet_id': subnet_id} raise exception.DependencyViolation(msg) neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: db_api.delete_item(context, subnet['id']) cleaner.addCleanup(db_api.restore_item, context, 'subnet', subnet) vpn_gateway_api._stop_vpn_in_subnet(context, neutron, cleaner, subnet) try: neutron.remove_interface_router(vpc['os_id'], {'subnet_id': subnet['os_id']}) except neutron_exception.NotFound: pass cleaner.addCleanup(neutron.add_interface_router, vpc['os_id'], {'subnet_id': subnet['os_id']}) try: os_subnet = neutron.show_subnet(subnet['os_id'])['subnet'] except neutron_exception.NotFound: pass else: try: neutron.delete_network(os_subnet['network_id']) except neutron_exception.NetworkInUseClient as ex: LOG.warning(_('Failed to delete network %(os_id)s during ' 'deleting Subnet %(id)s. Reason: %(reason)s'), {'id': subnet['id'], 'os_id': os_subnet['network_id'], 'reason': ex.message}) return True
def detach_network_interface(context, attachment_id, force=None): network_interface = db_api.get_item_by_id( context, ec2utils.change_ec2_id_kind(attachment_id, 'eni')) if not network_interface or 'instance_id' not in network_interface: raise exception.InvalidAttachmentIDNotFound(id=attachment_id) if network_interface['device_index'] == 0: raise exception.OperationNotPermitted( _('The network interface at device index 0 cannot be detached.')) neutron = clients.neutron(context) os_port = neutron.show_port(network_interface['os_id'])['port'] with common.OnCrashCleaner() as cleaner: instance_id = network_interface['instance_id'] device_index = network_interface['device_index'] attach_time = network_interface['attach_time'] delete_on_termination = network_interface['delete_on_termination'] _detach_network_interface_item(context, network_interface) cleaner.addCleanup(_attach_network_interface_item, context, network_interface, instance_id, device_index, attach_time, delete_on_termination) neutron.update_port(os_port['id'], {'port': {'device_id': '', 'device_owner': ''}}) return True
def delete_route_table(context, route_table_id): route_table = ec2utils.get_db_item(context, route_table_id) vpc = db_api.get_item_by_id(context, route_table['vpc_id']) _delete_route_table(context, route_table['id'], vpc) return True
def _set_route(context, route_table_id, destination_cidr_block, gateway_id, instance_id, network_interface_id, vpc_peering_connection_id, do_replace): route_table = ec2utils.get_db_item(context, route_table_id) vpc = db_api.get_item_by_id(context, route_table['vpc_id']) vpc_ipnet = netaddr.IPNetwork(vpc['cidr_block']) route_ipnet = netaddr.IPNetwork(destination_cidr_block) if route_ipnet in vpc_ipnet: msg = _('Cannot create a more specific route for ' '%(destination_cidr_block)s than local route ' '%(vpc_cidr_block)s in route table %(rtb_id)s') msg = msg % {'rtb_id': route_table_id, 'destination_cidr_block': destination_cidr_block, 'vpc_cidr_block': vpc['cidr_block']} raise exception.InvalidParameterValue(msg) obj_param_count = len([p for p in (gateway_id, network_interface_id, instance_id, vpc_peering_connection_id) if p is not None]) if obj_param_count != 1: msg = _('The request must contain exactly one of gatewayId, ' 'networkInterfaceId, vpcPeeringConnectionId or instanceId') if obj_param_count == 0: raise exception.MissingParameter(msg) else: raise exception.InvalidParameterCombination(msg) rollabck_route_table_state = copy.deepcopy(route_table) if do_replace: route_index, old_route = next( ((i, r) for i, r in enumerate(route_table['routes']) if r['destination_cidr_block'] == destination_cidr_block), (None, None)) if route_index is None: msg = _("There is no route defined for " "'%(destination_cidr_block)s' in the route table. " "Use CreateRoute instead.") msg = msg % {'destination_cidr_block': destination_cidr_block} raise exception.InvalidParameterValue(msg) else: del route_table['routes'][route_index] if gateway_id: gateway = ec2utils.get_db_item(context, gateway_id) if gateway.get('vpc_id') != route_table['vpc_id']: if ec2utils.get_ec2_id_kind(gateway_id) == 'vgw': raise exception.InvalidGatewayIDNotFound(id=gateway['id']) else: # igw raise exception.InvalidParameterValue( _('Route table %(rtb_id)s and network gateway %(igw_id)s ' 'belong to different networks') % {'rtb_id': route_table_id, 'igw_id': gateway_id}) route = {'gateway_id': gateway['id']} elif network_interface_id: network_interface = ec2utils.get_db_item(context, network_interface_id) if network_interface['vpc_id'] != route_table['vpc_id']: msg = _('Route table %(rtb_id)s and interface %(eni_id)s ' 'belong to different networks') msg = msg % {'rtb_id': route_table_id, 'eni_id': network_interface_id} raise exception.InvalidParameterValue(msg) route = {'network_interface_id': network_interface['id']} elif instance_id: # TODO(ft): implement search in DB layer network_interfaces = [eni for eni in db_api.get_items(context, 'eni') if eni.get('instance_id') == instance_id] if len(network_interfaces) == 0: msg = _("Invalid value '%(i_id)s' for instance ID. " "Instance is not in a VPC.") msg = msg % {'i_id': instance_id} raise exception.InvalidParameterValue(msg) elif len(network_interfaces) > 1: raise exception.InvalidInstanceId(instance_id=instance_id) network_interface = network_interfaces[0] if network_interface['vpc_id'] != route_table['vpc_id']: msg = _('Route table %(rtb_id)s and interface %(eni_id)s ' 'belong to different networks') msg = msg % {'rtb_id': route_table_id, 'eni_id': network_interface['id']} raise exception.InvalidParameterValue(msg) route = {'network_interface_id': network_interface['id']} else: raise exception.InvalidRequest('Parameter VpcPeeringConnectionId is ' 'not supported by this implementation') route['destination_cidr_block'] = destination_cidr_block update_target = _get_route_target(route) if do_replace: idempotent_call = False old_target = _get_route_target(old_route) if old_target != update_target: update_target = None else: old_route = next((r for r in route_table['routes'] if r['destination_cidr_block'] == destination_cidr_block), None) idempotent_call = old_route == route if old_route and not idempotent_call: raise exception.RouteAlreadyExists( destination_cidr_block=destination_cidr_block) if not idempotent_call: route_table['routes'].append(route) with common.OnCrashCleaner() as cleaner: db_api.update_item(context, route_table) cleaner.addCleanup(db_api.update_item, context, rollabck_route_table_state) _update_routes_in_associated_subnets(context, cleaner, route_table, update_target=update_target) return True
def _format_route_table(context, route_table, is_main=False, associated_subnet_ids=[], gateways={}, network_interfaces={}, vpn_connections_by_gateway_id={}): vpc_id = route_table['vpc_id'] ec2_route_table = { 'routeTableId': route_table['id'], 'vpcId': vpc_id, 'routeSet': [], 'propagatingVgwSet': [ {'gatewayId': vgw_id} for vgw_id in route_table.get('propagating_gateways', [])], # NOTE(ft): AWS returns empty tag set for a route table # if no tag exists 'tagSet': [], } # TODO(ft): refactor to get Nova instances outside of this function nova = clients.nova(context) for route in route_table['routes']: origin = ('CreateRouteTable' if route.get('gateway_id', 0) is None else 'CreateRoute') ec2_route = {'destinationCidrBlock': route['destination_cidr_block'], 'origin': origin} if 'gateway_id' in route: gateway_id = route['gateway_id'] if gateway_id is None: state = 'active' ec2_gateway_id = 'local' else: gateway = gateways.get(gateway_id) state = ('active' if gateway and gateway.get('vpc_id') == vpc_id else 'blackhole') ec2_gateway_id = gateway_id ec2_route.update({'gatewayId': ec2_gateway_id, 'state': state}) else: network_interface_id = route['network_interface_id'] network_interface = network_interfaces.get(network_interface_id) instance_id = (network_interface.get('instance_id') if network_interface else None) state = 'blackhole' if instance_id: instance = db_api.get_item_by_id(context, instance_id) if instance: try: os_instance = nova.servers.get(instance['os_id']) if os_instance and os_instance.status == 'ACTIVE': state = 'active' except nova_exception.NotFound: pass ec2_route.update({'instanceId': instance_id, 'instanceOwnerId': context.project_id}) ec2_route.update({'networkInterfaceId': network_interface_id, 'state': state}) ec2_route_table['routeSet'].append(ec2_route) for vgw_id in route_table.get('propagating_gateways', []): vgw = gateways.get(vgw_id) if vgw and vgw_id in vpn_connections_by_gateway_id: cidrs = set() vpn_connections = vpn_connections_by_gateway_id[vgw_id] for vpn_connection in vpn_connections: cidrs.update(vpn_connection['cidrs']) state = 'active' if vgw['vpc_id'] == vpc_id else 'blackhole' for cidr in cidrs: ec2_route = {'gatewayId': vgw_id, 'destinationCidrBlock': cidr, 'state': state, 'origin': 'EnableVgwRoutePropagation'} ec2_route_table['routeSet'].append(ec2_route) associations = [] if is_main: associations.append({ 'routeTableAssociationId': ec2utils.change_ec2_id_kind(vpc_id, 'rtbassoc'), 'routeTableId': route_table['id'], 'main': True}) for subnet_id in associated_subnet_ids: associations.append({ 'routeTableAssociationId': ec2utils.change_ec2_id_kind(subnet_id, 'rtbassoc'), 'routeTableId': route_table['id'], 'subnetId': subnet_id, 'main': False}) if associations: ec2_route_table['associationSet'] = associations return ec2_route_table