"Fossies" - the Fresh Open Source Software Archive

Member "ec2-api-12.0.0/ec2api/api/network_interface.py" (14 Apr 2021, 27378 Bytes) of package /linux/misc/openstack/ec2-api-12.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "network_interface.py" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 12.0.0_vs_13.0.0.

    1 # Copyright 2014
    2 # The Cloudscaling Group, Inc.
    3 #
    4 # Licensed under the Apache License, Version 2.0 (the "License");
    5 # you may not use this file except in compliance with the License.
    6 # You may obtain a copy of the License at
    7 # http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS,
   11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12 # See the License for the specific language governing permissions and
   13 # limitations under the License.
   14 
   15 
   16 import collections
   17 
   18 import netaddr
   19 from neutronclient.common import exceptions as neutron_exception
   20 from oslo_config import cfg
   21 from oslo_log import log as logging
   22 
   23 from ec2api.api import address as address_api
   24 from ec2api.api import common
   25 from ec2api.api import dhcp_options
   26 from ec2api.api import ec2utils
   27 from ec2api.api import security_group as security_group_api
   28 from ec2api import clients
   29 from ec2api.db import api as db_api
   30 from ec2api import exception
   31 from ec2api.i18n import _
   32 
   33 
   34 CONF = cfg.CONF
   35 LOG = logging.getLogger(__name__)
   36 
   37 
   38 """Network interface related API implementation
   39 """
   40 
   41 
   42 Validator = common.Validator
   43 
   44 
   45 def create_network_interface(context, subnet_id,
   46                              private_ip_address=None,
   47                              private_ip_addresses=None,
   48                              secondary_private_ip_address_count=None,
   49                              description=None,
   50                              security_group_id=None):
   51     subnet = ec2utils.get_db_item(context, subnet_id)
   52     if subnet is None:
   53         raise exception.InvalidSubnetIDNotFound(id=subnet_id)
   54     neutron = clients.neutron(context)
   55     os_subnet = neutron.show_subnet(subnet['os_id'])['subnet']
   56     # NOTE(Alex): Combine and check ip addresses. Neutron will accept
   57     # ip_address as a parameter for specified address and subnet_id for
   58     # address to auto-allocate.
   59     # TODO(Alex): Implement better diagnostics.
   60     subnet_ipnet = netaddr.IPNetwork(os_subnet['cidr'])
   61     if not private_ip_addresses:
   62         private_ip_addresses = []
   63     if private_ip_address is not None:
   64         private_ip_addresses.insert(0,
   65                                     {'private_ip_address': private_ip_address,
   66                                      'primary': True})
   67     primary_ip = None
   68     fixed_ips = []
   69     for ip in private_ip_addresses:
   70         ip_address = netaddr.IPAddress(ip['private_ip_address'])
   71         if ip_address not in subnet_ipnet:
   72             raise exception.InvalidParameterValue(
   73                 value=str(ip_address),
   74                 parameter='PrivateIpAddresses',
   75                 reason='IP address is out of the subnet range')
   76         if ip.get('primary', False):
   77             if primary_ip is not None:
   78                 raise exception.InvalidParameterValue(
   79                     value=str(ip_address),
   80                     parameter='PrivateIpAddresses',
   81                     reason='More than one primary ip is supplied')
   82             else:
   83                 primary_ip = str(ip_address)
   84                 fixed_ips.insert(0, {'ip_address': primary_ip})
   85         else:
   86             fixed_ips.append({'ip_address': str(ip_address)})
   87     if not fixed_ips and not secondary_private_ip_address_count:
   88         secondary_private_ip_address_count = 1
   89     if secondary_private_ip_address_count is None:
   90         secondary_private_ip_address_count = 0
   91     if secondary_private_ip_address_count > 0:
   92         for _i in range(secondary_private_ip_address_count):
   93             fixed_ips.append({'subnet_id': os_subnet['id']})
   94     vpc = db_api.get_item_by_id(context, subnet['vpc_id'])
   95     vpc_id = vpc['id']
   96     dhcp_options_id = vpc.get('dhcp_options_id', None)
   97     if not security_group_id:
   98         default_groups = security_group_api.describe_security_groups(
   99             context,
  100             filter=[{'name': 'vpc-id', 'value': [vpc_id]},
  101                     {'name': 'group-name', 'value': ['default']}]
  102         )['securityGroupInfo']
  103         security_group_id = [default_group['groupId']
  104                              for default_group in default_groups]
  105     security_groups = db_api.get_items_by_ids(context, security_group_id)
  106     if any(security_group['vpc_id'] != vpc['id']
  107            for security_group in security_groups):
  108         msg = _('You have specified two resources that belong to '
  109                 'different networks.')
  110         raise exception.InvalidGroupNotFound(msg)
  111     os_groups = [security_group['os_id'] for security_group in security_groups]
  112     with common.OnCrashCleaner() as cleaner:
  113         os_port_body = {'port': {'network_id': os_subnet['network_id'],
  114                                  'security_groups': os_groups}}
  115         os_port_body['port']['fixed_ips'] = fixed_ips
  116         try:
  117             os_port = neutron.create_port(os_port_body)['port']
  118         except (neutron_exception.IpAddressGenerationFailureClient,
  119                 neutron_exception.OverQuotaClient):
  120             raise exception.InsufficientFreeAddressesInSubnet()
  121         except (neutron_exception.IpAddressInUseClient,
  122                 neutron_exception.BadRequest) as ex:
  123             # NOTE(ft): AWS returns InvalidIPAddress.InUse for a primary IP
  124             # address, but InvalidParameterValue for secondary one.
  125             # AWS returns PrivateIpAddressLimitExceeded, but Neutron does
  126             # general InvalidInput (converted to BadRequest) in the same case.
  127             msg = _('Specified network interface parameters are invalid. '
  128                     'Reason: %(reason)s') % {'reason': ex.message}
  129             raise exception.InvalidParameterValue(msg)
  130         cleaner.addCleanup(neutron.delete_port, os_port['id'])
  131         if primary_ip is None:
  132             primary_ip = os_port['fixed_ips'][0]['ip_address']
  133         network_interface = db_api.add_item(context, 'eni',
  134                                             {'os_id': os_port['id'],
  135                                              'vpc_id': subnet['vpc_id'],
  136                                              'subnet_id': subnet['id'],
  137                                              'description': description,
  138                                              'private_ip_address': primary_ip})
  139         cleaner.addCleanup(db_api.delete_item,
  140                            context, network_interface['id'])
  141 
  142         network_interface_id = network_interface['id']
  143         neutron.update_port(os_port['id'],
  144                             {'port': {'name': network_interface_id}})
  145         if dhcp_options_id:
  146             dhcp_options._add_dhcp_opts_to_port(
  147                 context,
  148                 db_api.get_item_by_id(context, dhcp_options_id),
  149                 network_interface,
  150                 os_port)
  151     security_groups = security_group_api._format_security_groups_ids_names(
  152         context)
  153     return {'networkInterface':
  154             _format_network_interface(context,
  155                                       network_interface,
  156                                       os_port,
  157                                       security_groups=security_groups)}
  158 
  159 
  160 def delete_network_interface(context, network_interface_id):
  161     network_interface = ec2utils.get_db_item(context, network_interface_id)
  162     if 'instance_id' in network_interface:
  163         msg = _("Network interface '%(eni_id)s' is currently in use.")
  164         msg = msg % {'eni_id': network_interface_id}
  165         raise exception.InvalidParameterValue(msg)
  166 
  167     for address in db_api.get_items(context, 'eipalloc'):
  168         if address.get('network_interface_id') == network_interface['id']:
  169             address_api._disassociate_address_item(context, address)
  170 
  171     neutron = clients.neutron(context)
  172     with common.OnCrashCleaner() as cleaner:
  173         db_api.delete_item(context, network_interface['id'])
  174         cleaner.addCleanup(db_api.restore_item, context, 'eni',
  175                            network_interface)
  176         try:
  177             neutron.delete_port(network_interface['os_id'])
  178         except neutron_exception.PortNotFoundClient:
  179             pass
  180     return True
  181 
  182 
  183 class NetworkInterfaceDescriber(common.TaggableItemsDescriber):
  184 
  185     KIND = 'eni'
  186     FILTER_MAP = {'addresses.private-ip-address': ['privateIpAddressesSet',
  187                                                    'privateIpAddress'],
  188                   'addresses.primary': ['privateIpAddressesSet', 'primary'],
  189                   'addresses.association.public-ip': ['privateIpAddressesSet',
  190                                                       ('association',
  191                                                        'publicIp')],
  192                   'addresses.association.owner-id': ['privateIpAddressesSet',
  193                                                      ('association',
  194                                                       'ipOwnerId')],
  195                   'association.association-id': ('association',
  196                                                  'associationId'),
  197                   'association.allocation-id': ('association', 'allocationId'),
  198                   'association.ip-owner-id': ('association', 'ipOwnerId'),
  199                   'association.public-ip': ('association', 'publicIp'),
  200                   'attachment.attachment-id': ('attachment', 'attachmentId'),
  201                   'attachment.instance-id': ('attachment', 'instanceId'),
  202                   'attachment.instance-owner-id': ('attachment',
  203                                                    'instanceOwnerId'),
  204                   'attachment.device-index': ('attachment', 'deviceIndex'),
  205                   'attachment.status': ('attachment', 'status'),
  206                   'attachment.attach.time': ('attachment', 'attachTime'),
  207                   'attachment.delete-on-termination': ('attachment',
  208                                                        'deleteOnTermination'),
  209                   'description': 'description',
  210                   'group-id': ['groupSet', 'groupId'],
  211                   'group-name': ['groupSet', 'groupName'],
  212                   'mac-address': 'macAddress',
  213                   'network-interface-id': 'networkInterfaceId',
  214                   'owner-id': 'ownerId',
  215                   'private-ip-address': 'privateIpAddress',
  216                   'requester-managed': 'requesterManaged',
  217                   'source-dest-check': 'sourceDestCheck',
  218                   'status': 'status',
  219                   'vpc-id': 'vpcId',
  220                   'subnet-id': 'subnetId'}
  221 
  222     def format(self, network_interface, os_port):
  223         if not network_interface:
  224             return None
  225         return _format_network_interface(
  226                 self.context, network_interface, os_port,
  227                 self.ec2_addresses[network_interface['id']],
  228                 self.security_groups)
  229 
  230     def get_os_items(self):
  231         addresses = address_api.describe_addresses(self.context)
  232         self.ec2_addresses = collections.defaultdict(list)
  233         for address in addresses['addressesSet']:
  234             if 'networkInterfaceId' in address:
  235                 self.ec2_addresses[
  236                         address['networkInterfaceId']].append(address)
  237         self.security_groups = (
  238             security_group_api._format_security_groups_ids_names(self.context))
  239         neutron = clients.neutron(self.context)
  240         return neutron.list_ports(tenant_id=self.context.project_id)['ports']
  241 
  242     def get_name(self, os_item):
  243         return ''
  244 
  245 
  246 def describe_network_interfaces(context, network_interface_id=None,
  247                                 filter=None):
  248     formatted_network_interfaces = NetworkInterfaceDescriber().describe(
  249             context, ids=network_interface_id, filter=filter)
  250     return {'networkInterfaceSet': formatted_network_interfaces}
  251 
  252 
  253 def assign_private_ip_addresses(context, network_interface_id,
  254                                 private_ip_address=None,
  255                                 secondary_private_ip_address_count=None,
  256                                 allow_reassignment=False):
  257     # TODO(Alex): allow_reassignment is not supported at the moment
  258     network_interface = ec2utils.get_db_item(context, network_interface_id)
  259     subnet = db_api.get_item_by_id(context, network_interface['subnet_id'])
  260     neutron = clients.neutron(context)
  261     os_subnet = neutron.show_subnet(subnet['os_id'])['subnet']
  262     os_port = neutron.show_port(network_interface['os_id'])['port']
  263     subnet_ipnet = netaddr.IPNetwork(os_subnet['cidr'])
  264     fixed_ips = os_port['fixed_ips'] or []
  265     if private_ip_address is not None:
  266         for ip_address in private_ip_address:
  267             if netaddr.IPAddress(ip_address) not in subnet_ipnet:
  268                 raise exception.InvalidParameterValue(
  269                     value=str(ip_address),
  270                     parameter='PrivateIpAddress',
  271                     reason='IP address is out of the subnet range')
  272             fixed_ips.append({'ip_address': str(ip_address)})
  273     elif secondary_private_ip_address_count > 0:
  274         for _i in range(secondary_private_ip_address_count):
  275             fixed_ips.append({'subnet_id': os_subnet['id']})
  276     try:
  277         neutron.update_port(os_port['id'],
  278                             {'port': {'fixed_ips': fixed_ips}})
  279     except neutron_exception.IpAddressGenerationFailureClient:
  280         raise exception.InsufficientFreeAddressesInSubnet()
  281     except neutron_exception.IpAddressInUseClient:
  282         msg = _('Some of %(addresses)s is assigned, but move is not '
  283                 'allowed.') % {'addresses': private_ip_address}
  284         raise exception.InvalidParameterValue(msg)
  285     except neutron_exception.BadRequest as ex:
  286         # NOTE(ft):AWS returns PrivateIpAddressLimitExceeded, but Neutron does
  287         # general InvalidInput (converted to BadRequest) in the same case.
  288         msg = _('Specified network interface parameters are invalid. '
  289                 'Reason: %(reason)s') % {'reason': ex.message}
  290         raise exception.InvalidParameterValue(msg)
  291     return True
  292 
  293 
  294 def unassign_private_ip_addresses(context, network_interface_id,
  295                                   private_ip_address):
  296     network_interface = ec2utils.get_db_item(context, network_interface_id)
  297     if network_interface['private_ip_address'] in private_ip_address:
  298         raise exception.InvalidParameterValue(
  299                 value=str(network_interface['private_ip_address']),
  300                 parameter='PrivateIpAddresses',
  301                 reason='Primary IP address cannot be unassigned')
  302     neutron = clients.neutron(context)
  303     os_port = neutron.show_port(network_interface['os_id'])['port']
  304     fixed_ips = os_port['fixed_ips'] or []
  305     new_fixed_ips = [ip for ip in fixed_ips
  306                      if ip['ip_address'] not in private_ip_address]
  307     if len(new_fixed_ips) + len(private_ip_address) != len(fixed_ips):
  308         msg = _('Some of the specified addresses are not assigned to '
  309                 'interface %(id)s') % {'id': network_interface_id}
  310         raise exception.InvalidParameterValue(msg)
  311     os_port = neutron.update_port(os_port['id'],
  312                                   {'port': {'fixed_ips': new_fixed_ips}})
  313     return True
  314 
  315 
  316 def describe_network_interface_attribute(context, network_interface_id,
  317                                          attribute=None):
  318     if attribute is None:
  319         raise exception.InvalidParameterCombination(
  320             _('No attributes specified.'))
  321     network_interface = ec2utils.get_db_item(context, network_interface_id)
  322 
  323     def _format_attr_description(result):
  324         result['description'] = {
  325             'value': network_interface.get('description', '')}
  326 
  327     def _format_attr_source_dest_check(result):
  328         result['sourceDestCheck'] = {
  329             'value': network_interface.get('source_dest_check', True)}
  330 
  331     def _format_attr_group_set(result):
  332         ec2_network_interface = describe_network_interfaces(context,
  333             network_interface_id=[network_interface_id]
  334         )['networkInterfaceSet'][0]
  335         result['groupSet'] = ec2_network_interface['groupSet']
  336 
  337     def _format_attr_attachment(result):
  338         ec2_network_interface = describe_network_interfaces(context,
  339             network_interface_id=[network_interface_id]
  340         )['networkInterfaceSet'][0]
  341         if 'attachment' in ec2_network_interface:
  342             result['attachment'] = ec2_network_interface['attachment']
  343 
  344     attribute_formatter = {
  345         'description': _format_attr_description,
  346         'sourceDestCheck': _format_attr_source_dest_check,
  347         'groupSet': _format_attr_group_set,
  348         'attachment': _format_attr_attachment,
  349     }
  350 
  351     fn = attribute_formatter.get(attribute)
  352     if fn is None:
  353         raise exception.InvalidParameterValue(value=attribute,
  354                                               parameter='attribute',
  355                                               reason='Unknown attribute.')
  356 
  357     result = {'networkInterfaceId': network_interface['id']}
  358     fn(result)
  359     return result
  360 
  361 
  362 def modify_network_interface_attribute(context, network_interface_id,
  363                                        description=None,
  364                                        source_dest_check=None,
  365                                        security_group_id=None,
  366                                        attachment=None):
  367     params_count = (
  368         int(description is not None) +
  369         int(source_dest_check is not None) +
  370         int(security_group_id is not None) +
  371         int(attachment is not None))
  372     if params_count != 1:
  373         raise exception.InvalidParameterCombination(
  374             'Multiple attributes specified')
  375     network_interface = ec2utils.get_db_item(context, network_interface_id)
  376     if description is not None:
  377         network_interface['description'] = description
  378         db_api.update_item(context, network_interface)
  379     neutron = clients.neutron(context)
  380     if security_group_id is not None:
  381         os_groups = [sg['os_id']
  382                      for sg in ec2utils.get_db_items(context, 'sg',
  383                                                      security_group_id)]
  384         neutron.update_port(network_interface['os_id'],
  385                             {'port': {'security_groups': os_groups}})
  386     if source_dest_check is not None:
  387         allowed = [] if source_dest_check else [{'ip_address': '0.0.0.0/0'}]
  388         neutron.update_port(network_interface['os_id'],
  389                             {'port': {'allowed_address_pairs': allowed}})
  390         network_interface['source_dest_check'] = source_dest_check
  391         db_api.update_item(context, network_interface)
  392     if attachment:
  393         attachment_id = attachment.get('attachment_id')
  394         delete_on_termination = attachment.get('delete_on_termination')
  395         if attachment_id is None or delete_on_termination is None:
  396             raise exception.MissingParameter(
  397                 _('The request must contain the parameter attachment '
  398                   'deleteOnTermination'))
  399         attachment_id_own = ec2utils.change_ec2_id_kind(
  400                 network_interface['id'], 'eni-attach')
  401         if ('instance_id' not in network_interface
  402                 or attachment_id_own != attachment_id):
  403             raise exception.InvalidAttachmentIDNotFound(id=attachment_id)
  404         network_interface['delete_on_termination'] = delete_on_termination
  405         db_api.update_item(context, network_interface)
  406     return True
  407 
  408 
  409 def reset_network_interface_attribute(context, network_interface_id,
  410                                       attribute):
  411     # TODO(Alex) This is only a stub because it's not supported by
  412     # Openstack. True will be returned for now in any case.
  413     # NOTE(Alex) There is a bug in the AWS doc about this method -
  414     # "sourceDestCheck" should be used instead of "SourceDestCheck".
  415     # Also aws cli doesn't work with it because it doesn't comply with
  416     # the API.
  417     if attribute == 'sourceDestCheck':
  418         return modify_network_interface_attribute(context,
  419                                                   network_interface_id,
  420                                                   source_dest_check=True)
  421     return True
  422 
  423 
  424 def attach_network_interface(context, network_interface_id,
  425                              instance_id, device_index):
  426     network_interface = ec2utils.get_db_item(context, network_interface_id)
  427     if 'instance_id' in network_interface:
  428         raise exception.InvalidParameterValue(
  429             _("Network interface '%(id)s' is currently in use.") %
  430             {'id': network_interface_id})
  431     os_instance_id = ec2utils.get_db_item(context, instance_id)['os_id']
  432     # TODO(Alex) Check that the instance is not yet attached to another VPC
  433     # TODO(Alex) Check that the instance is "our", not created via nova
  434     # (which means that it doesn't belong to any VPC and can't be attached)
  435     if any(eni['device_index'] == device_index
  436            for eni in db_api.get_items(context, 'eni')
  437            if eni.get('instance_id') == instance_id):
  438         raise exception.InvalidParameterValue(
  439             _("Instance '%(id)s' already has an interface attached at "
  440               "device index '%(index)s'.") % {'id': instance_id,
  441                                               'index': device_index})
  442     neutron = clients.neutron(context)
  443     os_port = neutron.show_port(network_interface['os_id'])['port']
  444     nova = clients.nova(context)
  445     with common.OnCrashCleaner() as cleaner:
  446         # TODO(Alex) nova inserts compute:%availability_zone into device_owner
  447         #                              'device_owner': 'compute:None'}})
  448         _attach_network_interface_item(context, network_interface,
  449                                        instance_id, device_index)
  450         cleaner.addCleanup(_detach_network_interface_item, context,
  451                            network_interface)
  452         nova.servers.interface_attach(os_instance_id, os_port['id'],
  453                                       None, None)
  454     return {'attachmentId': ec2utils.change_ec2_id_kind(
  455                     network_interface['id'], 'eni-attach')}
  456 
  457 
  458 def detach_network_interface(context, attachment_id, force=None):
  459     network_interface = db_api.get_item_by_id(
  460             context, ec2utils.change_ec2_id_kind(attachment_id, 'eni'))
  461     if not network_interface or 'instance_id' not in network_interface:
  462         raise exception.InvalidAttachmentIDNotFound(id=attachment_id)
  463     if network_interface['device_index'] == 0:
  464         raise exception.OperationNotPermitted(
  465             _('The network interface at device index 0 cannot be detached.'))
  466     neutron = clients.neutron(context)
  467     os_port = neutron.show_port(network_interface['os_id'])['port']
  468     with common.OnCrashCleaner() as cleaner:
  469         instance_id = network_interface['instance_id']
  470         device_index = network_interface['device_index']
  471         attach_time = network_interface['attach_time']
  472         delete_on_termination = network_interface['delete_on_termination']
  473         _detach_network_interface_item(context, network_interface)
  474         cleaner.addCleanup(_attach_network_interface_item,
  475                            context, network_interface, instance_id,
  476                            device_index, attach_time, delete_on_termination)
  477         neutron.update_port(os_port['id'],
  478                             {'port': {'device_id': '',
  479                                       'device_owner': ''}})
  480     return True
  481 
  482 
  483 def _format_network_interface(context, network_interface, os_port,
  484                               associated_ec2_addresses=[], security_groups={}):
  485     ec2_network_interface = {}
  486     ec2_network_interface['networkInterfaceId'] = network_interface['id']
  487     ec2_network_interface['subnetId'] = network_interface['subnet_id']
  488     ec2_network_interface['vpcId'] = network_interface['vpc_id']
  489     ec2_network_interface['description'] = network_interface['description']
  490     ec2_network_interface['sourceDestCheck'] = (
  491         network_interface.get('source_dest_check', True))
  492     ec2_network_interface['requesterManaged'] = (
  493         os_port.get('device_owner', '').startswith('network:'))
  494     ec2_network_interface['ownerId'] = context.project_id
  495     security_group_set = []
  496     for sg_id in os_port['security_groups']:
  497         if security_groups.get(sg_id):
  498             security_group_set.append(security_groups[sg_id])
  499     ec2_network_interface['groupSet'] = security_group_set
  500     if 'instance_id' in network_interface:
  501         ec2_network_interface['status'] = 'in-use'
  502         ec2_network_interface['attachment'] = {
  503             'attachmentId': ec2utils.change_ec2_id_kind(
  504                     network_interface['id'], 'eni-attach'),
  505             'instanceId': network_interface['instance_id'],
  506             'deviceIndex': network_interface['device_index'],
  507             'status': 'attached',
  508             'deleteOnTermination': network_interface['delete_on_termination'],
  509             'attachTime': network_interface['attach_time'],
  510             'instanceOwnerId': context.project_id
  511         }
  512     else:
  513         ec2_network_interface['status'] = 'available'
  514     ec2_network_interface['macAddress'] = os_port['mac_address']
  515     if os_port['fixed_ips']:
  516         ipsSet = []
  517         for ip in os_port['fixed_ips']:
  518             primary = (
  519                 network_interface.get('private_ip_address', '') ==
  520                 ip['ip_address'])
  521             item = {'privateIpAddress': ip['ip_address'],
  522                     'primary': primary}
  523             ec2_address = next(
  524                 (addr for addr in associated_ec2_addresses
  525                  if addr['privateIpAddress'] == ip['ip_address']),
  526                 None)
  527             if ec2_address:
  528                 item['association'] = {
  529                     'associationId': ec2utils.change_ec2_id_kind(
  530                                     ec2_address['allocationId'], 'eipassoc'),
  531                     'allocationId': ec2_address['allocationId'],
  532                     'ipOwnerId': context.project_id,
  533                     'publicDnsName': None,
  534                     'publicIp': ec2_address['publicIp'],
  535                 }
  536             if primary:
  537                 ipsSet.insert(0, item)
  538             else:
  539                 ipsSet.append(item)
  540         ec2_network_interface['privateIpAddressesSet'] = ipsSet
  541         primary_ip = ipsSet[0]
  542         ec2_network_interface['privateIpAddress'] = (
  543             primary_ip['privateIpAddress'])
  544         if 'association' in primary_ip:
  545             ec2_network_interface['association'] = primary_ip['association']
  546     # NOTE(ft): AWS returns empty tag set for a network interface
  547     # if no tag exists
  548     ec2_network_interface['tagSet'] = []
  549     return ec2_network_interface
  550 
  551 
  552 def _attach_network_interface_item(context, network_interface, instance_id,
  553                                    device_index, attach_time=None,
  554                                    delete_on_termination=False):
  555     if not attach_time:
  556         attach_time = ec2utils.isotime(None, True)
  557     network_interface.update({
  558         'instance_id': instance_id,
  559         'device_index': device_index,
  560         'attach_time': attach_time,
  561         'delete_on_termination': delete_on_termination})
  562     db_api.update_item(context, network_interface)
  563 
  564 
  565 def _detach_network_interface_item(context, network_interface):
  566     network_interface.pop('instance_id', None)
  567     network_interface.pop('device_index', None)
  568     network_interface.pop('attach_time', None)
  569     network_interface.pop('delete_on_termination', None)
  570     db_api.update_item(context, network_interface)