def disassociate_address(context, public_ip=None, association_id=None): if not public_ip and not association_id: msg = _('Either public IP or association id must be specified') raise exception.MissingParameter(msg) if public_ip and association_id: msg = _('You may specify public IP or association id, ' 'but not both in the same call') raise exception.InvalidParameterCombination(msg) address_engine.disassociate_address(context, public_ip, association_id) return True
def disassociate_address(context, public_ip=None, association_id=None): LOG.info('Disassociate address: %(public_ip)s. Association: %(association_id)s.', {'public_ip': public_ip, 'association_id': association_id}) if not public_ip and not association_id: msg = _('Either public IP or association id must be specified') raise exception.MissingParameter(msg) if public_ip and association_id: msg = _('You may specify public IP or association id, ' 'but not both in the same call') raise exception.InvalidParameterCombination(msg) disassociate = address_engine.disassociate_address(context, public_ip, association_id) LOG.info('Disassociation is %s', disassociate) return True
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 describe_snapshots(context, snapshot_id=None, owner=None, restorable_by=None, filter=None, max_results=None, next_token=None): if snapshot_id and max_results: msg = _('The parameter snapshotSet cannot be used with the parameter ' 'maxResults') raise exception.InvalidParameterCombination(msg) snapshot_describer = SnapshotDescriber() formatted_snapshots = snapshot_describer.describe( context, ids=snapshot_id, filter=filter, max_results=max_results, next_token=next_token) result = {'snapshotSet': formatted_snapshots} if snapshot_describer.next_token: result['nextToken'] = snapshot_describer.next_token return result
def describe_network_interface_attribute(context, network_interface_id, attribute=None): if attribute is None: raise exception.InvalidParameterCombination( _('No attributes specified.')) network_interface = ec2utils.get_db_item(context, network_interface_id) def _format_attr_description(result): result['description'] = { 'value': network_interface.get('description', '') } def _format_attr_source_dest_check(result): result['sourceDestCheck'] = { 'value': network_interface.get('source_dest_check', True) } def _format_attr_group_set(result): ec2_network_interface = describe_network_interfaces( context, network_interface_id=[network_interface_id ])['networkInterfaceSet'][0] result['groupSet'] = ec2_network_interface['groupSet'] def _format_attr_attachment(result): ec2_network_interface = describe_network_interfaces( context, network_interface_id=[network_interface_id ])['networkInterfaceSet'][0] if 'attachment' in ec2_network_interface: result['attachment'] = ec2_network_interface['attachment'] attribute_formatter = { 'description': _format_attr_description, 'sourceDestCheck': _format_attr_source_dest_check, 'groupSet': _format_attr_group_set, 'attachment': _format_attr_attachment, } fn = attribute_formatter.get(attribute) if fn is None: raise exception.InvalidParameterValue(value=attribute, parameter='attribute', reason='Unknown attribute.') result = {'networkInterfaceId': network_interface['id']} fn(result) return result
def describe_volumes(context, volume_id=None, filter=None, max_results=None, next_token=None): if volume_id and max_results: msg = _('The parameter volumeSet cannot be used with the parameter ' 'maxResults') raise exception.InvalidParameterCombination(msg) volume_describer = VolumeDescriber() formatted_volumes = volume_describer.describe(context, ids=volume_id, filter=filter, max_results=max_results, next_token=next_token) result = {'volumeSet': formatted_volumes} if volume_describer.next_token: result['nextToken'] = volume_describer.next_token return result
def associate_address(context, public_ip=None, instance_id=None, allocation_id=None, network_interface_id=None, private_ip_address=None, allow_reassociation=False): if not public_ip and not allocation_id: msg = _('Either public IP or allocation id must be specified') raise exception.MissingParameter(msg) if public_ip and allocation_id: msg = _('You may specify public IP or allocation id, ' 'but not both in the same call') raise exception.InvalidParameterCombination(msg) if not instance_id and not network_interface_id: msg = _('Either instance ID or network interface id must be specified') raise exception.MissingParameter(msg) associationId = address_engine.associate_address( context, public_ip, instance_id, allocation_id, network_interface_id, private_ip_address, allow_reassociation) if associationId: return {'return': True, 'associationId': associationId} return {'return': True}
def associate_address(self, context, public_ip=None, instance_id=None, allocation_id=None, network_interface_id=None, private_ip_address=None, allow_reassociation=False): instance_network_interfaces = [] if instance_id: # TODO(ft): implement search in DB layer for eni in db_api.get_items(context, 'eni'): if instance_id and eni.get('instance_id') == instance_id: instance_network_interfaces.append(eni) neutron = clients.neutron(context) if public_ip: if instance_network_interfaces: msg = _('You must specify an allocation id when mapping ' 'an address to a VPC instance') raise exception.InvalidParameterCombination(msg) # 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 = _("The address '%(public_ip)s' does not belong to you.") raise exception.AuthFailure(msg % {'public_ip': public_ip}) # NOTE(ft): in fact only the first two parameters are used to # associate an address in EC2 Classic mode. Other parameters are # sent to validate their emptiness in one place return AddressEngineNova().associate_address( context, public_ip=public_ip, instance_id=instance_id, allocation_id=allocation_id, network_interface_id=network_interface_id, private_ip_address=private_ip_address, allow_reassociation=allow_reassociation) if instance_id: if not instance_network_interfaces: # NOTE(ft): check the instance exists ec2utils.get_db_item(context, instance_id) msg = _('You must specify an IP address when mapping ' 'to a non-VPC instance') raise exception.InvalidParameterCombination(msg) if len(instance_network_interfaces) > 1: raise exception.InvalidInstanceId(instance_id=instance_id) network_interface = instance_network_interfaces[0] else: network_interface = ec2utils.get_db_item(context, network_interface_id) if not private_ip_address: private_ip_address = network_interface['private_ip_address'] address = ec2utils.get_db_item(context, allocation_id) if not _is_address_valid(context, neutron, address): raise exception.InvalidAllocationIDNotFound(id=allocation_id) if address.get('network_interface_id') == network_interface['id']: # NOTE(ft): idempotent call pass elif address.get('network_interface_id') and not allow_reassociation: msg = _('resource %(eipalloc_id)s is already associated with ' 'associate-id %(eipassoc_id)s') msg = msg % { 'eipalloc_id': allocation_id, 'eipassoc_id': ec2utils.change_ec2_id_kind(address['id'], 'eipassoc') } raise exception.ResourceAlreadyAssociated(msg) else: internet_gateways = ( internet_gateway_api.describe_internet_gateways( context, filter=[{ 'name': 'attachment.vpc-id', 'value': [network_interface['vpc_id']] }])['internetGatewaySet']) if len(internet_gateways) == 0: msg = _('Network %(vpc_id)s is not attached to any internet ' 'gateway') % { 'vpc_id': network_interface['vpc_id'] } raise exception.GatewayNotAttached(msg) with common.OnCrashCleaner() as cleaner: _associate_address_item(context, address, network_interface['id'], private_ip_address) cleaner.addCleanup(_disassociate_address_item, context, address) os_floating_ip = { 'port_id': network_interface['os_id'], 'fixed_ip_address': private_ip_address } neutron.update_floatingip(address['os_id'], {'floatingip': os_floating_ip}) # TODO(ft): generate unique association id for each act of association return ec2utils.change_ec2_id_kind(address['id'], 'eipassoc')
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 register_image(context, name=None, image_location=None, description=None, architecture=None, root_device_name=None, block_device_mapping=None, virtualization_type=None, kernel_id=None, ramdisk_id=None, sriov_net_support=None): if not image_location and not root_device_name: # NOTE(ft): for backward compatibility with a hypothetical code # which uses name as image_location image_location = name if not image_location and not root_device_name: msg = _("Either imageLocation or rootDeviceName must be set.") raise exception.InvalidParameterCombination(msg) if not image_location and not name: msg = _('The request must contain the parameter name') raise exception.MissingParameter(msg) # TODO(ft): check parameters properties = {} metadata = {'properties': properties} if name: # TODO(ft): check the name is unique (at least for EBS image case) metadata['name'] = name if image_location: properties['image_location'] = image_location if 'name' not in metadata: # NOTE(ft): it's needed for backward compatibility metadata['name'] = image_location if root_device_name: properties['root_device_name'] = root_device_name cinder = clients.cinder(context) if block_device_mapping: mappings = instance_api._parse_block_device_mapping( context, block_device_mapping) # TODO(ft): merge with image manifets's virtual device mappings short_root_device_name = ( ec2utils.block_device_strip_dev(root_device_name)) for bdm in mappings: instance_api._populate_parsed_bdm_parameter( bdm, short_root_device_name) if 'volume_size' in bdm: continue try: if bdm['source_type'] == 'snapshot': snapshot = cinder.volume_snapshots.get(bdm['snapshot_id']) bdm['volume_size'] = snapshot.size elif bdm['source_type'] == 'volume': volume = cinder.volumes.get(bdm['volume_id']) bdm['volume_size'] = volume.size except cinder_exception.NotFound: pass properties['bdm_v2'] = True properties['block_device_mapping'] = json.dumps(mappings) if architecture is not None: properties['architecture'] = architecture if kernel_id: properties['kernel_id'] = ec2utils.get_os_image(context, kernel_id).id if ramdisk_id: properties['ramdisk_id'] = ec2utils.get_os_image(context, ramdisk_id).id with common.OnCrashCleaner() as cleaner: if 'image_location' in properties: os_image = _s3_create(context, metadata) else: metadata.update({'size': 0, 'is_public': False}) # TODO(ft): set default values of image properties glance = clients.glance(context) os_image = glance.images.create(**metadata) cleaner.addCleanup(os_image.delete) kind = _get_os_image_kind(os_image) image = db_api.add_item(context, kind, { 'os_id': os_image.id, 'is_public': False, 'description': description }) return {'imageId': image['id']}
def register_image(context, name=None, image_location=None, description=None, architecture=None, root_device_name=None, block_device_mapping=None, virtualization_type=None, kernel_id=None, ramdisk_id=None, sriov_net_support=None): # Setup default flags is_s3_import = False is_url_import = False # Process the input arguments if not image_location and not root_device_name: # NOTE(ft): for backward compatibility with a hypothetical code # which uses name as image_location image_location = name if not image_location and not root_device_name: msg = _("Either imageLocation or rootDeviceName must be set.") raise exception.InvalidParameterCombination(msg) if not image_location and not name: msg = _('The request must contain the parameter name') raise exception.MissingParameter(msg) # TODO(ft): check parameters metadata = {} if name: # TODO(ft): check the name is unique (at least for EBS image case) metadata['name'] = name if image_location: # Resolve the import type metadata['image_location'] = image_location parsed_url = six.moves.urllib.parse.urlparse(image_location) is_s3_import = (parsed_url.scheme == '') or (parsed_url.scheme == 's3') is_url_import = not is_s3_import # Check if the name is in the metadata if 'name' not in metadata: # NOTE(ft): it's needed for backward compatibility metadata['name'] = image_location if root_device_name: metadata['root_device_name'] = root_device_name cinder = clients.cinder(context) if block_device_mapping: mappings = instance_api._parse_block_device_mapping( context, block_device_mapping) # TODO(ft): merge with image manifets's virtual device mappings short_root_device_name = ( ec2utils.block_device_strip_dev(root_device_name)) for bdm in mappings: instance_api._populate_parsed_bdm_parameter( bdm, short_root_device_name) if 'volume_size' in bdm: continue try: if bdm['source_type'] == 'snapshot': snapshot = cinder.volume_snapshots.get(bdm['snapshot_id']) bdm['volume_size'] = snapshot.size elif bdm['source_type'] == 'volume': volume = cinder.volumes.get(bdm['volume_id']) bdm['volume_size'] = volume.size except cinder_exception.NotFound: pass metadata['bdm_v2'] = 'True' metadata['block_device_mapping'] = json.dumps(mappings) if architecture is not None: metadata['architecture'] = architecture if kernel_id: metadata['kernel_id'] = ec2utils.get_os_image(context, kernel_id).id if ramdisk_id: metadata['ramdisk_id'] = ec2utils.get_os_image(context, ramdisk_id).id # Begin the import/registration process with common.OnCrashCleaner() as cleaner: # Setup the glance client glance = clients.glance(context) # Check if this is an S3 import if is_s3_import: os_image = _s3_create(context, metadata) # Condition for all non-S3 imports else: # Create the image in glance metadata.update({ 'visibility': 'private', 'container_format': 'bare', 'disk_format': 'raw' }) os_image = glance.images.create(**metadata) # Kick-off the URL image import if from URL if is_url_import: glance.images.image_import(os_image.id, method='web-download', uri=metadata['image_location']) # Otherwise, use the default method else: glance.images.upload(os_image.id, '', image_size=0) # Add cleanups and complete the registration process cleaner.addCleanup(glance.images.delete, os_image.id) kind = _get_os_image_kind(os_image) image = db_api.add_item(context, kind, { 'os_id': os_image.id, 'is_public': False, 'description': description }) # Return the image ID for the registration process return {'imageId': image['id']}
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