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 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 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) 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_detach_network_interface_invalid_parameters(self): # NOTE(ft): eni is not found self.set_mock_db_items() self.assert_execution_error( 'InvalidAttachmentID.NotFound', 'DetachNetworkInterface', {'AttachmentId': ec2utils.change_ec2_id_kind( fakes.ID_EC2_NETWORK_INTERFACE_2, 'eni-attach')}) # NOTE(ft): eni is attached with device index = 0 self.set_mock_db_items(fakes.DB_NETWORK_INTERFACE_2) self.assert_execution_error( 'OperationNotPermitted', 'DetachNetworkInterface', {'AttachmentId': ec2utils.change_ec2_id_kind( fakes.ID_EC2_NETWORK_INTERFACE_2, 'eni-attach')})
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(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) 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 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 attach_network_interface(context, network_interface_id, instance_id, device_index): network_interface = ec2utils.get_db_item(context, network_interface_id) if 'instance_id' in network_interface: raise exception.InvalidParameterValue( _("Network interface '%(id)s' is currently in use.") % {'id': network_interface_id}) os_instance_id = ec2utils.get_db_item(context, instance_id)['os_id'] # TODO(Alex) Check that the instance is not yet attached to another VPC # TODO(Alex) Check that the instance is "our", not created via nova # (which means that it doesn't belong to any VPC and can't be attached) if any(eni['device_index'] == device_index for eni in db_api.get_items(context, 'eni') if eni.get('instance_id') == instance_id): raise exception.InvalidParameterValue( _("Instance '%(id)s' already has an interface attached at " "device index '%(index)s'.") % {'id': instance_id, 'index': device_index}) neutron = clients.neutron(context) os_port = neutron.show_port(network_interface['os_id'])['port'] nova = clients.nova(context) with common.OnCrashCleaner() as cleaner: # TODO(Alex) nova inserts compute:%availability_zone into device_owner # 'device_owner': 'compute:None'}}) _attach_network_interface_item(context, network_interface, instance_id, device_index) cleaner.addCleanup(_detach_network_interface_item, context, network_interface) nova.servers.interface_attach(os_instance_id, os_port['id'], None, None) return {'attachmentId': ec2utils.change_ec2_id_kind( network_interface['id'], 'eni-attach')}
def test_modify_network_interface_attribute_invalid_parameters(self): self.assert_execution_error( 'InvalidParameterCombination', 'ModifyNetworkInterfaceAttribute', {'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_1, 'Description.Value': 'New description', 'SourceDestCheck.Value': 'True'}) self.set_mock_db_items(fakes.DB_NETWORK_INTERFACE_1, fakes.DB_NETWORK_INTERFACE_2) self.assert_execution_error( 'MissingParameter', 'ModifyNetworkInterfaceAttribute', {'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_2, 'Attachment.DeleteOnTermination': 'True'}) self.assert_execution_error( 'MissingParameter', 'ModifyNetworkInterfaceAttribute', {'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_2, 'Attachment.AttachmentId': ( fakes.ID_EC2_NETWORK_INTERFACE_2_ATTACH)}) self.assert_execution_error( 'InvalidAttachmentID.NotFound', 'ModifyNetworkInterfaceAttribute', {'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_1, 'Attachment.AttachmentId': ( fakes.ID_EC2_NETWORK_INTERFACE_2_ATTACH), 'Attachment.DeleteOnTermination': 'True'}) self.assert_execution_error( 'InvalidAttachmentID.NotFound', 'ModifyNetworkInterfaceAttribute', {'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_2, 'Attachment.AttachmentId': ec2utils.change_ec2_id_kind( fakes.ID_EC2_NETWORK_INTERFACE_1, 'eni-attach'), 'Attachment.DeleteOnTermination': 'True'})
def _create_security_group(context, group_name, group_description, vpc_id=None, default=False): neutron = clients.neutron(context) with common.OnCrashCleaner() as cleaner: try: secgroup_body = ( {'security_group': {'name': group_name, 'description': group_description}}) os_security_group = neutron.create_security_group( secgroup_body)['security_group'] except neutron_exception.OverQuotaClient: raise exception.ResourceLimitExceeded(resource='security groups') cleaner.addCleanup(neutron.delete_security_group, os_security_group['id']) if vpc_id: # NOTE(Alex) Check if such vpc exists ec2utils.get_db_item(context, vpc_id) item = {'vpc_id': vpc_id, 'os_id': os_security_group['id']} if not default: security_group = db_api.add_item(context, 'sg', item) else: item['id'] = ec2utils.change_ec2_id_kind(vpc_id, 'sg') # NOTE(andrey-mp): try to add item with specific id # and catch exception if it exists security_group = db_api.restore_item(context, 'sg', item) return {'return': 'true', 'groupId': security_group['id']}
def _create_security_group(context, group_name, group_description, vpc_id=None, default=False): nova = clients.nova(context) with common.OnCrashCleaner() as cleaner: try: os_security_group = nova.security_groups.create( group_name, group_description) except nova_exception.OverLimit: raise exception.ResourceLimitExceeded(resource='security groups') cleaner.addCleanup(nova.security_groups.delete, os_security_group.id) if vpc_id: # NOTE(Alex) Check if such vpc exists ec2utils.get_db_item(context, vpc_id) item = {'vpc_id': vpc_id, 'os_id': os_security_group.id} if not default: security_group = db_api.add_item(context, 'sg', item) else: item['id'] = ec2utils.change_ec2_id_kind(vpc_id, 'sg') # NOTE(andrey-mp): try to add item with specific id # and catch exception if it exists security_group = db_api.restore_item(context, 'sg', item) return {'return': 'true', 'groupId': security_group['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 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 _format_address(context, address, os_floating_ip, os_ports=[], db_instances_dict=None): ec2_address = {'publicIp': os_floating_ip['floating_ip_address']} fixed_ip_address = os_floating_ip.get('fixed_ip_address') if fixed_ip_address: ec2_address['privateIpAddress'] = fixed_ip_address port_id = os_floating_ip.get('port_id') os_fip = os_floating_ip.get('instance_id') if port_id: port = next((port for port in os_ports if port['id'] == port_id), None) if port and port.get('device_id'): ec2_address['instanceId'] = ( _get_instance_ec2_id_by_os_id(context, port['device_id'], db_instances_dict)) elif os_fip: ec2_address['instanceId'] = ( _get_instance_ec2_id_by_os_id(context, os_fip, db_instances_dict)) if not address: ec2_address['domain'] = 'standard' else: ec2_address['domain'] = 'vpc' ec2_address['allocationId'] = address['id'] if 'network_interface_id' in address: ec2_address.update({ 'associationId': ec2utils.change_ec2_id_kind( ec2_address['allocationId'], 'eipassoc'), 'networkInterfaceId': address['network_interface_id'], 'networkInterfaceOwnerId': context.project_id}) return ec2_address
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 test_detach_network_interface_invalid_parameters(self): # NOTE(ft): eni is not found self.set_mock_db_items() self.assert_execution_error( 'InvalidAttachmentID.NotFound', 'DetachNetworkInterface', { 'AttachmentId': ec2utils.change_ec2_id_kind(fakes.ID_EC2_NETWORK_INTERFACE_2, 'eni-attach') }) # NOTE(ft): eni is attached with device index = 0 self.set_mock_db_items(fakes.DB_NETWORK_INTERFACE_2) self.assert_execution_error( 'OperationNotPermitted', 'DetachNetworkInterface', { 'AttachmentId': ec2utils.change_ec2_id_kind(fakes.ID_EC2_NETWORK_INTERFACE_2, 'eni-attach') })
def test_replace_route_table_association_invalid_parameters(self): def do_check(params, error_code): self.assert_execution_error(error_code, 'ReplaceRouteTableAssociation', params) self.set_mock_db_items() do_check( { 'AssociationId': fakes.ID_EC2_ROUTE_TABLE_ASSOCIATION_1, 'RouteTableId': fakes.ID_EC2_ROUTE_TABLE_1 }, 'InvalidRouteTableID.NotFound') # NOTE(ft): association with vpc is obsolete self.set_mock_db_items(fakes.DB_ROUTE_TABLE_1) do_check( { 'AssociationId': fakes.ID_EC2_ROUTE_TABLE_ASSOCIATION_1, 'RouteTableId': fakes.ID_EC2_ROUTE_TABLE_1 }, 'InvalidAssociationID.NotFound') # NOTE(ft): association with subnet is obsolete (no subnet) self.set_mock_db_items(fakes.DB_ROUTE_TABLE_3) do_check( { 'AssociationId': fakes.ID_EC2_ROUTE_TABLE_ASSOCIATION_3, 'RouteTableId': fakes.ID_EC2_ROUTE_TABLE_3 }, 'InvalidAssociationID.NotFound') # NOTE(ft): association with subnet is obsolete (subnet is # disassociated) self.set_mock_db_items( fakes.DB_ROUTE_TABLE_3, tools.purge_dict(fakes.DB_SUBNET_2, ['route_table_id'])) do_check( { 'AssociationId': fakes.ID_EC2_ROUTE_TABLE_ASSOCIATION_3, 'RouteTableId': fakes.ID_EC2_ROUTE_TABLE_3 }, 'InvalidAssociationID.NotFound') # NOTE(ft): association belongs to different vpc id_ec2_subnet_vpc_2 = fakes.random_ec2_id('subnet') db_subnet_vpc_2 = { 'id': id_ec2_subnet_vpc_2, 'os_id': fakes.random_os_id(), 'vpc_id': fakes.ID_EC2_VPC_2, 'route_table_id': fakes.random_ec2_id('rtb') } self.set_mock_db_items(fakes.DB_ROUTE_TABLE_2, db_subnet_vpc_2) do_check( { 'AssociationId': ec2utils.change_ec2_id_kind(id_ec2_subnet_vpc_2, 'rtbassoc'), 'RouteTableId': fakes.ID_EC2_ROUTE_TABLE_2 }, 'InvalidParameterValue')
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 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 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 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 test_modify_network_interface_attribute_invalid_parameters(self): self.assert_execution_error( 'InvalidParameterCombination', 'ModifyNetworkInterfaceAttribute', { 'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_1, 'Description.Value': 'New description', 'SourceDestCheck.Value': 'True' }) self.set_mock_db_items(fakes.DB_NETWORK_INTERFACE_1, fakes.DB_NETWORK_INTERFACE_2) self.assert_execution_error( 'MissingParameter', 'ModifyNetworkInterfaceAttribute', { 'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_2, 'Attachment.DeleteOnTermination': 'True' }) self.assert_execution_error( 'MissingParameter', 'ModifyNetworkInterfaceAttribute', { 'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_2, 'Attachment.AttachmentId': (fakes.ID_EC2_NETWORK_INTERFACE_2_ATTACH) }) self.assert_execution_error( 'InvalidAttachmentID.NotFound', 'ModifyNetworkInterfaceAttribute', { 'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_1, 'Attachment.AttachmentId': (fakes.ID_EC2_NETWORK_INTERFACE_2_ATTACH), 'Attachment.DeleteOnTermination': 'True' }) self.assert_execution_error( 'InvalidAttachmentID.NotFound', 'ModifyNetworkInterfaceAttribute', { 'NetworkInterfaceId': fakes.ID_EC2_NETWORK_INTERFACE_2, 'Attachment.AttachmentId': ec2utils.change_ec2_id_kind(fakes.ID_EC2_NETWORK_INTERFACE_1, 'eni-attach'), 'Attachment.DeleteOnTermination': '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 test_detach_network_interface(self): network_interface = tools.update_dict(fakes.DB_NETWORK_INTERFACE_2, {'device_index': 1}) self.set_mock_db_items(network_interface) self.neutron.show_port.return_value = ( {'port': fakes.OS_PORT_2}) self.execute( 'DetachNetworkInterface', {'AttachmentId': ec2utils.change_ec2_id_kind( fakes.ID_EC2_NETWORK_INTERFACE_2, 'eni-attach')}) self.neutron.update_port.assert_called_once_with( fakes.ID_OS_PORT_2, {'port': {'device_id': '', 'device_owner': ''}} ) self.db_api.update_item.assert_called_once_with( mock.ANY, tools.purge_dict(fakes.DB_NETWORK_INTERFACE_2, {'device_index', 'instance_id', 'delete_on_termination', 'attach_time'}))
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 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) with common.OnCrashCleaner() as cleaner: _associate_subnet_item(context, subnet, route_table['id']) cleaner.addCleanup(_disassociate_subnet_item, context, subnet) _update_subnet_routes(context, cleaner, subnet, route_table) return {'associationId': ec2utils.change_ec2_id_kind(subnet['id'], 'rtbassoc')}
def test_detach_network_interface(self): network_interface = tools.update_dict(fakes.DB_NETWORK_INTERFACE_2, {'device_index': 1}) self.set_mock_db_items(network_interface) self.neutron.show_port.return_value = ({'port': fakes.OS_PORT_2}) self.execute( 'DetachNetworkInterface', { 'AttachmentId': ec2utils.change_ec2_id_kind(fakes.ID_EC2_NETWORK_INTERFACE_2, 'eni-attach') }) self.neutron.update_port.assert_called_once_with( fakes.ID_OS_PORT_2, {'port': { 'device_id': '', 'device_owner': '' }}) self.db_api.update_item.assert_called_once_with( mock.ANY, tools.purge_dict( fakes.DB_NETWORK_INTERFACE_2, { 'device_index', 'instance_id', 'delete_on_termination', 'attach_time' }))
def _create_security_group(context, group_name, group_description, vpc_id=None, default=False): nova = clients.nova(context) with common.OnCrashCleaner() as cleaner: try: os_security_group = nova.security_groups.create(group_name, group_description) except nova_exception.OverLimit: raise exception.ResourceLimitExceeded(resource='security groups') cleaner.addCleanup(nova.security_groups.delete, os_security_group.id) if vpc_id: # NOTE(Alex) Check if such vpc exists ec2utils.get_db_item(context, vpc_id) item = {'vpc_id': vpc_id, 'os_id': os_security_group.id} if not default: security_group = db_api.add_item(context, 'sg', item) else: item['id'] = ec2utils.change_ec2_id_kind(vpc_id, 'sg') # NOTE(andrey-mp): try to add item with specific id # and catch exception if it exists security_group = db_api.restore_item(context, 'sg', item) return {'return': 'true', 'groupId': security_group['id']}
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
def _format_network_interface(context, network_interface, os_port, associated_ec2_addresses=[], security_groups={}): ec2_network_interface = {} ec2_network_interface['networkInterfaceId'] = network_interface['id'] ec2_network_interface['subnetId'] = network_interface['subnet_id'] ec2_network_interface['vpcId'] = network_interface['vpc_id'] ec2_network_interface['description'] = network_interface['description'] ec2_network_interface['sourceDestCheck'] = ( network_interface.get('source_dest_check', True)) ec2_network_interface['requesterManaged'] = ( os_port.get('device_owner', '').startswith('network:')) ec2_network_interface['ownerId'] = context.project_id security_group_set = [] for sg_id in os_port['security_groups']: if security_groups.get(sg_id): security_group_set.append(security_groups[sg_id]) ec2_network_interface['groupSet'] = security_group_set if 'instance_id' in network_interface: ec2_network_interface['status'] = 'in-use' ec2_network_interface['attachment'] = { 'attachmentId': ec2utils.change_ec2_id_kind( network_interface['id'], 'eni-attach'), 'instanceId': network_interface['instance_id'], 'deviceIndex': network_interface['device_index'], 'status': 'attached', 'deleteOnTermination': network_interface['delete_on_termination'], 'attachTime': network_interface['attach_time'], 'instanceOwnerId': context.project_id } else: ec2_network_interface['status'] = 'available' ec2_network_interface['macAddress'] = os_port['mac_address'] if os_port['fixed_ips']: ipsSet = [] for ip in os_port['fixed_ips']: primary = ( network_interface.get('private_ip_address', '') == ip['ip_address']) item = {'privateIpAddress': ip['ip_address'], 'primary': primary} ec2_address = next( (addr for addr in associated_ec2_addresses if addr['privateIpAddress'] == ip['ip_address']), None) if ec2_address: item['association'] = { 'associationId': ec2utils.change_ec2_id_kind( ec2_address['allocationId'], 'eipassoc'), 'allocationId': ec2_address['allocationId'], 'ipOwnerId': context.project_id, 'publicDnsName': None, 'publicIp': ec2_address['publicIp'], } if primary: ipsSet.insert(0, item) else: ipsSet.append(item) ec2_network_interface['privateIpAddressesSet'] = ipsSet primary_ip = ipsSet[0] ec2_network_interface['privateIpAddress'] = ( primary_ip['privateIpAddress']) if 'association' in primary_ip: ec2_network_interface['association'] = primary_ip['association'] # NOTE(ft): AWS returns empty tag set for a network interface # if no tag exists ec2_network_interface['tagSet'] = [] return ec2_network_interface
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 eni.get('instance_id') == instance_id: instance_network_interfaces.append(eni) 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 instance_network_interfaces: msg = _('You must specify an allocation id when mapping ' 'an address to a VPC instance') raise exception.InvalidParameterCombination(msg) 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}) os_instance_id = ec2utils.get_db_item(context, instance_id)['os_id'] # NOTE(ft): check the public IP exists to raise AWS exception # otherwise self.get_os_floating_ip_by_public_ip(context, public_ip) nova = clients.nova(context) nova.servers.add_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}) allocation_id = address['id'] 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 _format_network_interface(context, network_interface, os_port, associated_ec2_addresses=[], security_groups={}): ec2_network_interface = {} ec2_network_interface['networkInterfaceId'] = network_interface['id'] ec2_network_interface['subnetId'] = network_interface['subnet_id'] ec2_network_interface['vpcId'] = network_interface['vpc_id'] ec2_network_interface['description'] = network_interface['description'] ec2_network_interface['sourceDestCheck'] = (network_interface.get( 'source_dest_check', True)) ec2_network_interface['requesterManaged'] = (os_port.get( 'device_owner', '').startswith('network:')) ec2_network_interface['ownerId'] = context.project_id security_group_set = [] for sg_id in os_port['security_groups']: if security_groups.get(sg_id): security_group_set.append(security_groups[sg_id]) ec2_network_interface['groupSet'] = security_group_set if 'instance_id' in network_interface: ec2_network_interface['status'] = 'in-use' ec2_network_interface['attachment'] = { 'attachmentId': ec2utils.change_ec2_id_kind(network_interface['id'], 'eni-attach'), 'instanceId': network_interface['instance_id'], 'deviceIndex': network_interface['device_index'], 'status': 'attached', 'deleteOnTermination': network_interface['delete_on_termination'], 'attachTime': network_interface['attach_time'], 'instanceOwnerId': context.project_id } else: ec2_network_interface['status'] = 'available' ec2_network_interface['macAddress'] = os_port['mac_address'] if os_port['fixed_ips']: ipsSet = [] for ip in os_port['fixed_ips']: primary = (network_interface.get('private_ip_address', '') == ip['ip_address']) item = {'privateIpAddress': ip['ip_address'], 'primary': primary} ec2_address = next( (addr for addr in associated_ec2_addresses if addr['privateIpAddress'] == ip['ip_address']), None) if ec2_address: item['association'] = { 'associationId': ec2utils.change_ec2_id_kind(ec2_address['allocationId'], 'eipassoc'), 'allocationId': ec2_address['allocationId'], 'ipOwnerId': context.project_id, 'publicDnsName': None, 'publicIp': ec2_address['publicIp'], } if primary: ipsSet.insert(0, item) else: ipsSet.append(item) ec2_network_interface['privateIpAddressesSet'] = ipsSet primary_ip = ipsSet[0] ec2_network_interface['privateIpAddress'] = ( primary_ip['privateIpAddress']) if 'association' in primary_ip: ec2_network_interface['association'] = primary_ip['association'] # NOTE(ft): AWS returns empty tag set for a network interface # if no tag exists ec2_network_interface['tagSet'] = [] return ec2_network_interface
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')