def _remove_propagation_from_route_table_item(context, route_table, gateway_id): vgws = route_table['propagating_gateways'] vgws.remove(gateway_id) if not vgws: del route_table['propagating_gateways'] db_api.update_item(context, route_table)
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 auto_update_db(self, image, os_image): if not image: kind = _get_os_image_kind(os_image) if self.context.project_id == os_image.owner: if os_image.properties.get('ec2_id') in self.pending_images: # NOTE(ft): the image is being creating, Glance had created # image, but creating thread doesn't yet update db item image = self.pending_images[os_image.properties['ec2_id']] image['os_id'] = os_image.id image['is_public'] = os_image.is_public db_api.update_item(self.context, image) else: image = ec2utils.get_db_item_by_os_id( self.context, kind, os_image.id, self.items_dict, os_image=os_image) else: image_id = ec2utils.os_id_to_ec2_id( self.context, kind, os_image.id, items_by_os_id=self.items_dict, ids_by_os_id=self.ids_dict) image = {'id': image_id, 'os_id': os_image.id} elif (self.context.project_id == os_image.owner and image.get('is_public') != os_image.is_public): image['is_public'] = os_image.is_public if image['id'] in self.local_images_os_ids: db_api.update_item(self.context, image) else: # TODO(ft): currently update_item can not update id mapping, # because its project_id is None. Instead of upgrade db_api, # we use add_item. But its execution leads to a needless # DB call. This should be reworked in the future. kind = ec2utils.get_ec2_id_kind(image['id']) db_api.add_item(self.context, kind, image) return image
def handle_unpaired_item(self, item): if item['os_id']: return super(ImageDescriber, self).handle_unpaired_item(item) if 'is_public' not in item: return None # NOTE(ft): process creating images, ignoring ids mapping # NOTE(ft): the image is being creating, Glance had created # image, but creating thread doesn't yet update db item os_image = self.ec2_created_os_images.get(item['id']) if os_image: item['os_id'] = os_image.id item['is_public'] = os_image.is_public db_api.update_item(self.context, item) image = self.format(item, os_image) else: # NOTE(ft): Glance image is not yet created, but DB item # exists. So that we adds EC2 image to output results # with all data we have. # TODO(ft): check if euca2ools can process such result image = {'imageId': item['id'], 'imageOwnerId': self.context.project_id, 'imageType': IMAGE_TYPES[ ec2utils.get_ec2_id_kind(item['id'])], 'isPublic': item['is_public']} if 'description' in item: image['description'] = item['description'] image['imageState'] = item.get('state', 'pending') return image
def _attach_network_interface_item(context, network_interface, instance_id, device_index, attach_time=None, delete_on_termination=False): if not attach_time: attach_time = timeutils.isotime(None, True) network_interface.update({ 'instance_id': instance_id, 'device_index': device_index, 'attach_time': attach_time, 'delete_on_termination': delete_on_termination}) db_api.update_item(context, network_interface)
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 _stop_gateway_vpn_connections(context, neutron, cleaner, vpn_gateway): def undo_vpn_connection(context, vpn_connection, connections_ids): vpn_connection['os_ipsec_site_connections'] = connections_ids db_api.update_item(context, vpn_connection) for vpn_connection in db_api.get_items(context, 'vpn'): if vpn_connection['vpn_gateway_id'] == vpn_gateway['id']: _stop_vpn_connection(neutron, vpn_connection) connection_ids = vpn_connection['os_ipsec_site_connections'] vpn_connection['os_ipsec_site_connections'] = {} db_api.update_item(context, vpn_connection) cleaner.addCleanup(undo_vpn_connection, context, vpn_connection, connection_ids)
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 delayed_create(context, image, name, os_instance): try: os_instance.stop() # wait instance for really stopped start_time = time.time() while os_instance.status != 'SHUTOFF': time.sleep(1) os_instance.get() # NOTE(yamahata): timeout and error. 1 hour for now for safety. # Is it too short/long? # Or is there any better way? timeout = 1 * 60 * 60 if time.time() > start_time + timeout: err = (_("Couldn't stop instance within %d sec") % timeout) raise exception.EC2Exception(message=err) # NOTE(ft): create an image with ec2_id metadata to let other code # link os and db objects in race conditions os_image_id = os_instance.create_image( name, metadata={'ec2_id': image['id']}) image['os_id'] = os_image_id db_api.update_item(context, image) except Exception: LOG.exception(_LE('Failed to complete image %s creation'), image.id) try: image['state'] = 'failed' db_api.update_item(context, image) except Exception: LOG.warning(_LW("Couldn't set 'failed' state for db image %s"), image.id, exc_info=True) try: os_instance.start() except Exception: LOG.warning(_LW('Failed to start instance %(i_id)s after ' 'completed creation of image %(image_id)s'), {'i_id': instance['id'], 'image_id': image['id']}, exc_info=True)
def create_vpc(context, cidr_block, instance_tenancy='default'): neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: os_router_body = {'router': {}} try: os_router = neutron.create_router(os_router_body)['router'] except neutron_exception.OverQuotaClient: raise exception.VpcLimitExceeded() cleaner.addCleanup(neutron.delete_router, os_router['id']) vpc = db_api.add_item(context, 'vpc', {'os_id': os_router['id'], 'cidr_block': cidr_block}) cleaner.addCleanup(db_api.delete_item, context, vpc['id']) route_table = route_table_api._create_route_table(context, vpc) cleaner.addCleanup(route_table_api._delete_route_table, context, route_table['id']) vpc['route_table_id'] = route_table['id'] db_api.update_item(context, vpc) neutron.update_router(os_router['id'], {'router': {'name': vpc['id']}}) security_group_api._create_default_security_group(context, vpc) return {'vpc': _format_vpc(vpc)}
def _create_vpc(context, cidr_block, is_default=False): neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: os_router_body = {'router': {}} try: os_router = neutron.create_router(os_router_body)['router'] except neutron_exception.OverQuotaClient: raise exception.VpcLimitExceeded() cleaner.addCleanup(neutron.delete_router, os_router['id']) vpc = db_api.add_item(context, 'vpc', {'os_id': os_router['id'], 'cidr_block': cidr_block, 'is_default': is_default}) cleaner.addCleanup(db_api.delete_item, context, vpc['id']) route_table = route_table_api._create_route_table(context, vpc) cleaner.addCleanup(route_table_api._delete_route_table, context, route_table['id']) vpc['route_table_id'] = route_table['id'] db_api.update_item(context, vpc) neutron.update_router(os_router['id'], {'router': {'name': vpc['id']}}) sg_id = security_group_api._create_default_security_group(context, vpc) cleaner.addCleanup(security_group_api.delete_security_group, context, group_id=sg_id, delete_default=True) if is_default: igw_id = internet_gateway_api.create_internet_gateway( context)['internetGateway']['internetGatewayId'] cleaner.addCleanup(internet_gateway_api.delete_internet_gateway, context, igw_id) internet_gateway_api.attach_internet_gateway(context, igw_id, vpc['id']) cleaner.addCleanup(internet_gateway_api.detach_internet_gateway, context, igw_id, vpc['id']) subnet = subnet_api.create_subnet( context, vpc['id'], DEFAULT_SUBNET_CIDR_BLOCK)['subnet'] cleaner.addCleanup(subnet_api.delete_subnet, context, subnet['subnetId']) route_table_api.create_route(context, route_table['id'], '0.0.0.0/0', gateway_id=igw_id) return vpc
def modify_network_interface_attribute(context, network_interface_id, description=None, source_dest_check=None, security_group_id=None, attachment=None): params_count = ( int(description is not None) + int(source_dest_check is not None) + int(security_group_id is not None) + int(attachment is not None)) if params_count != 1: raise exception.InvalidParameterCombination( 'Multiple attributes specified') network_interface = ec2utils.get_db_item(context, network_interface_id) if description is not None: network_interface['description'] = description db_api.update_item(context, network_interface) neutron = clients.neutron(context) if security_group_id is not None: os_groups = [sg['os_id'] for sg in ec2utils.get_db_items(context, 'sg', security_group_id)] neutron.update_port(network_interface['os_id'], {'port': {'security_groups': os_groups}}) if source_dest_check is not None: allowed = [] if source_dest_check else [{'ip_address': '0.0.0.0/0'}] neutron.update_port(network_interface['os_id'], {'port': {'allowed_address_pairs': allowed}}) network_interface['source_dest_check'] = source_dest_check db_api.update_item(context, network_interface) if attachment: attachment_id = attachment.get('attachment_id') delete_on_termination = attachment.get('delete_on_termination') if attachment_id is None or delete_on_termination is None: raise exception.MissingParameter( _('The request must contain the parameter attachment ' 'deleteOnTermination')) attachment_id_own = ec2utils.change_ec2_id_kind( network_interface['id'], 'eni-attach') if ('instance_id' not in network_interface or attachment_id_own != attachment_id): raise exception.InvalidAttachmentIDNotFound(id=attachment_id) network_interface['delete_on_termination'] = delete_on_termination db_api.update_item(context, network_interface) return True
def undo_vpn_connection(context, vpn_connection, connections_ids): vpn_connection['os_ipsec_site_connections'] = connections_ids db_api.update_item(context, vpn_connection)
def _add_subnet_connection_to_vpn_connection_item(context, vpn_connection, subnet_id, os_connection_id): vpn_connection['os_ipsec_site_connections'][subnet_id] = os_connection_id db_api.update_item(context, vpn_connection)
def _associate_vpc_item(context, vpc, dhcp_options_id): vpc['dhcp_options_id'] = dhcp_options_id db_api.update_item(context, vpc)
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 _attach_vpn_gateway_item(context, vpn_gateway, vpc_id): vpn_gateway['vpc_id'] = vpc_id db_api.update_item(context, vpn_gateway)
def _append_propagation_to_route_table_item(context, route_table, gateway_id): vgws = route_table.setdefault('propagating_gateways', []) vgws.append(gateway_id) db_api.update_item(context, route_table)
def _detach_internet_gateway_item(context, igw): igw['vpc_id'] = None db_api.update_item(context, igw)
def _detach_network_interface_item(context, network_interface): network_interface.pop('instance_id', None) network_interface.pop('device_index', None) network_interface.pop('attach_time', None) network_interface.pop('delete_on_termination', None) db_api.update_item(context, network_interface)
def _disassociate_address_item(context, address): address.pop('network_interface_id') address.pop('private_ip_address') db_api.update_item(context, address)
def modify_image_attribute(context, image_id, attribute=None, user_group=None, operation_type=None, description=None, launch_permission=None, product_code=None, user_id=None, value=None): os_image = ec2utils.get_os_image(context, image_id) if not os_image: # TODO(ft): figure out corresponding AWS error raise exception.IncorrectState( reason='Image is still being created or failed') attributes = set() # NOTE(andrey-mp): launchPermission structure is converted here # to plain parameters: attribute, user_group, operation_type, user_id if launch_permission is not None: attributes.add('launchPermission') user_group = list() user_id = list() if len(launch_permission) == 0: msg = _('No operation specified for launchPermission attribute.') raise exception.InvalidParameterCombination(msg) if len(launch_permission) > 1: msg = _('Only one operation can be specified.') raise exception.InvalidParameterCombination(msg) operation_type, permissions = launch_permission.popitem() for index_key in permissions: permission = permissions[index_key] if 'group' in permission: user_group.append(permission['group']) if 'user_id' in permission: user_id.append(permission['user_id']) if attribute == 'launchPermission': attributes.add('launchPermission') if description is not None: attributes.add('description') value = description if attribute == 'description': attributes.add('description') # check attributes if len(attributes) == 0: if product_code is not None: attribute = 'productCodes' if attribute in [ 'kernel', 'ramdisk', 'productCodes', 'blockDeviceMapping' ]: raise exception.InvalidParameter( _('Parameter %s is invalid. ' 'The attribute is not supported.') % attribute) raise exception.InvalidParameterCombination('No attributes specified.') if len(attributes) > 1: raise exception.InvalidParameterCombination( _('Fields for multiple attribute types specified: %s') % str(attributes)) if 'launchPermission' in attributes: if not user_group: msg = _('No operation specified for launchPermission attribute.') raise exception.InvalidParameterCombination(msg) if len(user_group) != 1 and user_group[0] != 'all': msg = _('only group "all" is supported') raise exception.InvalidParameterValue(parameter='UserGroup', value=user_group, reason=msg) if operation_type not in ['add', 'remove']: msg = _('operation_type must be add or remove') raise exception.InvalidParameterValue(parameter='OperationType', value='operation_type', reason=msg) _check_owner(context, os_image) os_image.update(is_public=(operation_type == 'add')) return True if 'description' in attributes: if not value: raise exception.MissingParameter( 'The request must contain the parameter description') _check_owner(context, os_image) image = ec2utils.get_db_item(context, image_id) image['description'] = value db_api.update_item(context, image) return True
def _associate_address_item(context, address, network_interface_id, private_ip_address): address['network_interface_id'] = network_interface_id address['private_ip_address'] = private_ip_address db_api.update_item(context, address)
def import_image(context, architecture=None, client_data=None, client_token=None, description=None, disk_container=None, hypervisor=None, license_type=None, platform=None, role_name=None): # Check architecture validate_enum(architecture, ('i386', 'x86_64'), 'architecture', allow_empty=True) # Ignore client_data...? if client_data is not None: raise exception.Unsupported(reason='Client data is not supported') # Ignore client_token...? if client_token is not None: raise exception.Unsupported(reason='Client token is not supported') # Description retrieved below # Check disk_container disk = disk_container[0] url = disk.get('url') disk_format = disk.get('format', 'RAW') if disk.get('snapshotid') is not None: raise exception.Unsupported(reason='Snapshot IDs not supported') if url is None or disk.get('userbucket') is not None: raise exception.Unsupported(reason='Buckets not implemented. Need URL') # disk_container descrption overrides default descrption description = disk.get('description') or description # hypervisor set below # Ignore license_type validate_enum(license_type, ('AWS', 'BYOL'), 'license_type', allow_empty=True) # Check platform validate_enum(platform, ('Linux', 'Windows'), 'platform', allow_empty=True) if role_name is not None: raise exception.Unsupported(reason='Buckets not implemented. Need URL') # Create EC2 image ec2_image = { 'is_public': False } if description is not None: ec2_image['description'] = description ec2_image = db_api.add_item(context, 'ami', ec2_image) # Create properties for openstack properties = {} if architecture is not None: properties['architecture'] = architecture if hypervisor is not None: properties['hypervisor_type'] = hypervisor if license_type is not None: properties['license_type'] = license_type if platform is not None: properties['os_type'] = platform if description is not None: properties['description'] = description # Set EC2 id for retrieval on the way back properties['ec2_id'] = ec2_image['id'] # Connect to glance glance = clients.glance(context) # NOTE: container_format is not currently used, so we can always default # to bare. Read more here: # http://docs.openstack.org/developer/glance/formats.html os_image = glance.images.create( name=ec2_image['id'], copy_from=url, disk_format=disk_format, container_format='bare', properties=properties, is_public=False) # Update EC2 id ec2_image['os_id'] = os_image.id db_api.update_item(context, ec2_image) return {'imageId': ec2_image['id']}
def _remove_subnet_connection_from_vpn_connection_item(context, vpn_connection, subnet_id): del vpn_connection['os_ipsec_site_connections'][subnet_id] db_api.update_item(context, vpn_connection)
def _attach_internet_gateway_item(context, igw, vpc_id): igw['vpc_id'] = vpc_id db_api.update_item(context, igw)
def _detach_vpn_gateway_item(context, vpn_gateway): vpn_gateway["vpc_id"] = None db_api.update_item(context, vpn_gateway)
def _clear_vpnservice_in_subnet_item(context, subnet): del subnet["os_vpnservice_id"] db_api.update_item(context, subnet)
def modify_image_attribute(context, image_id, attribute=None, user_group=None, operation_type=None, description=None, launch_permission=None, product_code=None, user_id=None, value=None): os_image = ec2utils.get_os_image(context, image_id) if not os_image: # TODO(ft): figure out corresponding AWS error raise exception.IncorrectState( reason='Image is still being created or failed') attributes = set() # NOTE(andrey-mp): launchPermission structure is converted here # to plain parameters: attribute, user_group, operation_type, user_id if launch_permission is not None: attributes.add('launchPermission') user_group = list() user_id = list() if len(launch_permission) == 0: msg = _('No operation specified for launchPermission attribute.') raise exception.InvalidParameterCombination(msg) if len(launch_permission) > 1: msg = _('Only one operation can be specified.') raise exception.InvalidParameterCombination(msg) operation_type, permissions = launch_permission.popitem() for index_key in permissions: permission = permissions[index_key] if 'group' in permission: user_group.append(permission['group']) if 'user_id' in permission: user_id.append(permission['user_id']) if attribute == 'launchPermission': attributes.add('launchPermission') if description is not None: attributes.add('description') value = description if attribute == 'description': attributes.add('description') # check attributes if len(attributes) == 0: if product_code is not None: attribute = 'productCodes' if attribute in ['kernel', 'ramdisk', 'productCodes', 'blockDeviceMapping']: raise exception.InvalidParameter( _('Parameter %s is invalid. ' 'The attribute is not supported.') % attribute) raise exception.InvalidParameterCombination('No attributes specified.') if len(attributes) > 1: raise exception.InvalidParameterCombination( _('Fields for multiple attribute types specified: %s') % str(attributes)) if 'launchPermission' in attributes: if not user_group: msg = _('No operation specified for launchPermission attribute.') raise exception.InvalidParameterCombination(msg) if len(user_group) != 1 and user_group[0] != 'all': msg = _('only group "all" is supported') raise exception.InvalidParameterValue(parameter='UserGroup', value=user_group, reason=msg) if operation_type not in ['add', 'remove']: msg = _('operation_type must be add or remove') raise exception.InvalidParameterValue(parameter='OperationType', value='operation_type', reason=msg) _check_owner(context, os_image) os_image.update(is_public=(operation_type == 'add')) return True if 'description' in attributes: if not value: raise exception.MissingParameter( 'The request must contain the parameter description') _check_owner(context, os_image) image = ec2utils.get_db_item(context, image_id) image['description'] = value db_api.update_item(context, image) return True
def _add_cidr_to_vpn_connection_item(context, vpn_connection, cidr): vpn_connection['cidrs'].append(cidr) db_api.update_item(context, vpn_connection)
def _detach_vpn_gateway_item(context, vpn_gateway): vpn_gateway['vpc_id'] = None db_api.update_item(context, vpn_gateway)
def _clear_vpnservice_in_subnet_item(context, subnet): del subnet['os_vpnservice_id'] db_api.update_item(context, subnet)
def _associate_vpc_item(context, vpc, route_table_id): vpc['route_table_id'] = route_table_id db_api.update_item(context, vpc)
def _attach_vpn_gateway_item(context, vpn_gateway, vpc_id): vpn_gateway["vpc_id"] = vpc_id db_api.update_item(context, vpn_gateway)
def _remove_cidr_from_vpn_connection_item(context, vpn_connection, cidr): vpn_connection['cidrs'].remove(cidr) db_api.update_item(context, vpn_connection)
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 _disassociate_subnet_item(context, subnet): subnet.pop('route_table_id') db_api.update_item(context, subnet)
def _associate_subnet_item(context, subnet, route_table_id): subnet['route_table_id'] = route_table_id db_api.update_item(context, subnet)
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