示例#1
0
def get(context, name=None, id=None, map_exception=False):
    neutron = neutronapi.get_client(context)
    try:
        if not id and name:
            # NOTE(flwang): The project id should be honoured so as to get
            # the correct security group id when user(with admin role but
            # non-admin project) try to query by name, so as to avoid
            # getting more than duplicated records with the same name.
            id = neutronv20.find_resourceid_by_name_or_id(
                neutron, 'security_group', name, context.project_id)
        group = neutron.show_security_group(id).get('security_group')
        return _convert_to_nova_security_group_format(group)
    except n_exc.NeutronClientNoUniqueMatch as e:
        raise exception.NoUniqueMatch(six.text_type(e))
    except n_exc.NeutronClientException as e:
        exc_info = sys.exc_info()
        if e.status_code == 404:
            LOG.debug("Neutron security group %s not found", name)
            raise exception.SecurityGroupNotFound(six.text_type(e))
        else:
            LOG.error("Neutron Error: %s", e)
            six.reraise(*exc_info)
    except TypeError as e:
        LOG.error("Neutron Error: %s", e)
        msg = _("Invalid security group name: %(name)s.") % {"name": name}
        raise exception.SecurityGroupNotFound(six.text_type(msg))
示例#2
0
def add_to_instance(context, instance, security_group_name):
    """Add security group to the instance."""

    neutron = neutronapi.get_client(context)
    try:
        security_group_id = neutronv20.find_resourceid_by_name_or_id(
            neutron, 'security_group',
            security_group_name,
            context.project_id)
    except n_exc.NeutronClientNoUniqueMatch as e:
        raise exception.NoUniqueMatch(six.text_type(e))
    except n_exc.NeutronClientException as e:
        if e.status_code == 404:
            msg = (_("Security group %(name)s is not found for "
                     "project %(project)s") %
                   {'name': security_group_name,
                    'project': context.project_id})
            raise exception.SecurityGroupNotFound(msg)
        else:
            raise e
    params = {'device_id': instance.uuid}
    try:
        ports = neutron.list_ports(**params).get('ports')
    except n_exc.NeutronClientException:
        with excutils.save_and_reraise_exception():
            LOG.exception("Neutron Error:")

    if not ports:
        msg = (_("instance_id %s could not be found as device id on"
               " any ports") % instance.uuid)
        raise exception.SecurityGroupNotFound(msg)

    for port in ports:
        if not _has_security_group_requirements(port):
            LOG.warning("Cannot add security group %(name)s to "
                        "%(instance)s since the port %(port_id)s "
                        "does not meet security requirements",
                        {'name': security_group_name,
                         'instance': instance.uuid,
                         'port_id': port['id']})
            raise exception.SecurityGroupCannotBeApplied()
        if 'security_groups' not in port:
            port['security_groups'] = []
        port['security_groups'].append(security_group_id)
        updated_port = {'security_groups': port['security_groups']}
        try:
            LOG.info("Adding security group %(security_group_id)s to "
                     "port %(port_id)s",
                     {'security_group_id': security_group_id,
                      'port_id': port['id']})
            neutron.update_port(port['id'], {'port': updated_port})
        except n_exc.NeutronClientException as e:
            if e.status_code == 400:
                raise exception.SecurityGroupCannotBeApplied(
                    six.text_type(e))
            else:
                raise e
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception("Neutron Error:")
示例#3
0
def remove_from_instance(context, instance, security_group_name):
    """Remove the security group associated with the instance."""
    neutron = neutronapi.get_client(context)
    try:
        security_group_id = neutronv20.find_resourceid_by_name_or_id(
            neutron, 'security_group', security_group_name, context.project_id)
    except n_exc.NeutronClientException as e:
        if e.status_code == 404:
            msg = (_("Security group %(name)s is not found for "
                     "project %(project)s") % {
                         'name': security_group_name,
                         'project': context.project_id
                     })
            raise exception.SecurityGroupNotFound(msg)
        else:
            raise e
    params = {'device_id': instance.uuid}
    try:
        ports = neutron.list_ports(**params).get('ports')
    except n_exc.NeutronClientException:
        with excutils.save_and_reraise_exception():
            LOG.exception("Neutron Error:")

    if not ports:
        msg = (_("instance_id %s could not be found as device id on"
                 " any ports") % instance.uuid)
        raise exception.SecurityGroupNotFound(msg)

    found_security_group = False
    for port in ports:
        try:
            port.get('security_groups', []).remove(security_group_id)
        except ValueError:
            # When removing a security group from an instance the security
            # group should be on both ports since it was added this way if
            # done through the nova api. In case it is not a 404 is only
            # raised if the security group is not found on any of the
            # ports on the instance.
            continue

        updated_port = {'security_groups': port['security_groups']}
        try:
            LOG.info(
                "Removing security group %(security_group_id)s from "
                "port %(port_id)s", {
                    'security_group_id': security_group_id,
                    'port_id': port['id']
                })
            neutron.update_port(port['id'], {'port': updated_port})
            found_security_group = True
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception("Neutron Error:")
    if not found_security_group:
        msg = (_("Security group %(security_group_name)s not associated "
                 "with the instance %(instance)s") % {
                     'security_group_name': security_group_name,
                     'instance': instance.uuid
                 })
        raise exception.SecurityGroupNotFound(msg)
示例#4
0
def add_rules(context, id, name, vals):
    """Add security group rule(s) to security group.

    Note: the Nova security group API doesn't support adding multiple
    security group rules at once but the EC2 one does. Therefore,
    this function is written to support both. Multiple rules are
    installed to a security group in neutron using bulk support.
    """

    neutron = neutronapi.get_client(context)
    body = _make_neutron_security_group_rules_list(vals)
    try:
        rules = neutron.create_security_group_rule(body).get(
            'security_group_rules')
    except n_exc.NeutronClientException as e:
        exc_info = sys.exc_info()
        if e.status_code == 404:
            LOG.exception("Neutron Error getting security group %s", name)
            raise exception.SecurityGroupNotFound(six.text_type(e))
        elif e.status_code == 409:
            LOG.exception("Neutron Error adding rules to security "
                          "group %s", name)
            raise exception.SecurityGroupLimitExceeded(six.text_type(e))
        elif e.status_code == 400:
            LOG.exception("Neutron Error: %s", e)
            raise exception.Invalid(six.text_type(e))
        else:
            six.reraise(*exc_info)
    converted_rules = []
    for rule in rules:
        converted_rules.append(
            _convert_to_nova_security_group_rule_format(rule))
    return converted_rules
示例#5
0
    def create(self, req, body):
        context = req.environ['nova.context']
        group_id = body['security_group_rule']['parent_group_id']

        self.compute_api.ensure_default_security_group(context)
        security_group = db.security_group_get(context, group_id)
        if not security_group:
            raise exception.SecurityGroupNotFound(security_group_id=group_id)

        msg = "Authorize security group ingress %s"
        LOG.audit(_(msg), security_group['name'], context=context)
        values = self._revoke_rule_args_to_dict(context,
                                                **body['security_group_rule'])
        if values is None:
            raise exception.ApiError(_("Not enough parameters to build a "
                                       "valid rule."))
        values['parent_group_id'] = security_group.id

        if self._security_group_rule_exists(security_group, values):
            raise exception.ApiError(_('This rule already exists in group %s')
                                     % group_id)

        security_group_rule = db.security_group_rule_create(context, values)

        self.compute_api.trigger_security_group_rules_refresh(context,
                                      security_group_id=security_group['id'])

        return {'security_group_rule': self._format_security_group_rule(
                                                        context,
                                                        security_group_rule)}
示例#6
0
    def allocate(self, context, instance_id, tenant_id, zone, **kwargs):
        neutron = neutronv2.get_client(context, admin=True)
        dhcp_opts = kwargs.get('dhcp_options', None)
        net_ids = [kwargs.get('network_id')]
        nets = self._get_available_networks(context, tenant_id, net_ids)
        if not nets:
            LOG.warn(_LW("No network configured!"))
            return network_model.NetworkInfo([])
        security_groups = kwargs.get('security_groups', [])
        security_group_ids = []
        if len(security_groups):
            search_opts = {'tenant_id': tenant_id}
            user_security_groups = neutron.list_security_groups(
                **search_opts).get('security_groups')
        for security_group in security_groups:
            name_match = None
            uuid_match = None
            for user_security_group in user_security_groups:
                if user_security_group['name'] == security_group:
                    if name_match:
                        raise exception.NoUniqueMatch(
                            _("Multiple security groups found matching"
                              " '%s'. Use an ID to be more specific.") %
                               security_group)

                    name_match = user_security_group['id']
                if user_security_group['id'] == security_group:
                    uuid_match = user_security_group['id']

            # If a user names the security group the same as
            # another's security groups uuid, the name takes priority.
            if not name_match and not uuid_match:
                raise exception.SecurityGroupNotFound(
                    security_group_id=security_group)
            elif name_match:
                security_group_ids.append(name_match)
            elif uuid_match:
                security_group_ids.append(uuid_match)
        for net in nets:
            if net['id'] == kwargs.get('network_id'):
                network = net
                break
        if (security_groups and not (
                network['subnets']
                and network.get('port_security_enabled', True))):
            raise exception.SecurityGroupCannotBeApplied()
        port_req_body = {'port': {'device_id': instance_id,
                                  'device_owner': zone,
                                  'binding:host_id': 'qjnode2.test.ibm.com'}}
        created_port = self._create_port(
                neutron, tenant_id, network['id'],
                port_req_body, None, security_group_ids, dhcp_opts)
        nw_info = self._build_network_info_model(context, instance_id, tenant_id,
                                                 networks=[net],
                                                 port_ids=[created_port])
        return network_model.NetworkInfo([vif for vif in nw_info])
示例#7
0
def get_rule(context, id):
    neutron = neutronapi.get_client(context)
    try:
        rule = neutron.show_security_group_rule(id).get('security_group_rule')
    except n_exc.NeutronClientException as e:
        if e.status_code == 404:
            LOG.debug("Neutron security group rule %s not found", id)
            raise exception.SecurityGroupNotFound(str(e))
        else:
            LOG.error("Neutron Error: %s", e)
            raise e
    return _convert_to_nova_security_group_rule_format(rule)
示例#8
0
def get(context, id):
    neutron = neutronapi.get_client(context)
    try:
        group = neutron.show_security_group(id).get('security_group')
        return _convert_to_nova_security_group_format(group)
    except n_exc.NeutronClientException as e:
        if e.status_code == 404:
            LOG.debug('Neutron security group %s not found', id)
            raise exception.SecurityGroupNotFound(six.text_type(e))
        else:
            LOG.error("Neutron Error: %s", e)
            raise e
示例#9
0
    def create(self, req, body):
        context = _authorize_context(req)

        sg_rule = self._from_body(body, 'security_group_rule')

        with translate_exceptions():
            parent_group_id = self.security_group_api.validate_id(
                sg_rule.get('parent_group_id', None))
            security_group = self.security_group_api.get(context,
                                                         None,
                                                         parent_group_id,
                                                         map_exception=True)
        try:
            new_rule = self._rule_args_to_dict(
                context,
                to_port=sg_rule.get('to_port'),
                from_port=sg_rule.get('from_port'),
                ip_protocol=sg_rule.get('ip_protocol'),
                cidr=sg_rule.get('cidr'),
                group_id=sg_rule.get('group_id'))
        except Exception as exp:
            raise exc.HTTPBadRequest(explanation=unicode(exp))

        if new_rule is None:
            msg = _("Not enough parameters to build a valid rule.")
            raise exc.HTTPBadRequest(explanation=msg)

        new_rule['parent_group_id'] = security_group['id']

        if 'cidr' in new_rule:
            net, prefixlen = netutils.get_net_and_prefixlen(new_rule['cidr'])
            if net not in ('0.0.0.0', '::') and prefixlen == '0':
                msg = _("Bad prefix for network in cidr %s") % new_rule['cidr']
                raise exc.HTTPBadRequest(explanation=msg)

        with translate_exceptions():
            security_group_rule = (
                self.security_group_api.create_security_group_rule(
                    context, security_group, new_rule))

        formatted_rule = self._format_security_group_rule(
            context, security_group_rule)
        if formatted_rule:
            return {"security_group_rule": formatted_rule}

        # TODO(arosen): if we first look up the security group information for
        # the group_id before creating the rule we can avoid the case that
        # the remote group (group_id) has been deleted when we go to look
        # up it's name.
        with translate_exceptions():
            raise exception.SecurityGroupNotFound(
                security_group_id=sg_rule['group_id'])
示例#10
0
def destroy(context, security_group):
    """This function deletes a security group."""

    neutron = neutronapi.get_client(context)
    try:
        neutron.delete_security_group(security_group['id'])
    except n_exc.NeutronClientException as e:
        if e.status_code == 404:
            raise exception.SecurityGroupNotFound(str(e))
        elif e.status_code == 409:
            raise exception.Invalid(str(e))
        else:
            LOG.error("Neutron Error: %s", e)
            raise e
示例#11
0
    def delete(self, req, id):
        context = req.environ['nova.context']
        rule = sqlalchemy_api.security_group_rule_get(context, id)
        if not rule:
           raise exception.ApiError(_("Rule not found"))
        group_id = rule.parent_group_id

        self.compute_api.ensure_default_security_group(context)

        security_group = db.security_group_get(context, group_id)
        if not security_group:
            raise exception.SecurityGroupNotFound(security_group_id=group_id)

        msg = "Revoke security group ingress %s"
        LOG.audit(_(msg), security_group['name'], context=context)

        db.security_group_rule_destroy(context, rule['id'])
        self.compute_api.trigger_security_group_rules_refresh(context,
                                security_group_id=security_group['id'])
        return exc.HTTPAccepted()
示例#12
0
def validate_name(context: nova_context.RequestContext, name: str):
    """Validate a security group name and return the corresponding UUID.

    :param context: The nova request context.
    :param name: The name of the security group.
    :raises NoUniqueMatch: If there is no unique match for the provided name.
    :raises SecurityGroupNotFound: If there's no match for the provided name.
    :raises NeutronClientException: For all other exceptions.
    """
    neutron = neutronapi.get_client(context)
    try:
        return neutronv20.find_resourceid_by_name_or_id(
            neutron, 'security_group', name, context.project_id)
    except n_exc.NeutronClientNoUniqueMatch as e:
        raise exception.NoUniqueMatch(str(e))
    except n_exc.NeutronClientException as e:
        if e.status_code == 404:
            LOG.debug('Neutron security group %s not found', name)
            raise exception.SecurityGroupNotFound(str(e))
        else:
            LOG.error('Neutron Error: %s', e)
            raise e
示例#13
0
    def allocate_for_instance(self, context, instance, **kwargs):
        """Allocate network resources for the instance.

        :param requested_networks: optional value containing
            network_id, fixed_ip, and port_id
        :param security_groups: security groups to allocate for instance
        :param macs: None or a set of MAC addresses that the instance
            should use. macs is supplied by the hypervisor driver (contrast
            with requested_networks which is user supplied).
            NB: NeutronV2 currently assigns hypervisor supplied MAC addresses
            to arbitrary networks, which requires openflow switches to
            function correctly if more than one network is being used with
            the bare metal hypervisor (which is the only one known to limit
            MAC addresses).
        """
        hypervisor_macs = kwargs.get('macs', None)
        available_macs = None
        if hypervisor_macs is not None:
            # Make a copy we can mutate: records macs that have not been used
            # to create a port on a network. If we find a mac with a
            # pre-allocated port we also remove it from this set.
            available_macs = set(hypervisor_macs)
        neutron = neutronv2.get_client(context)
        LOG.debug(_('allocate_for_instance() for %s'),
                  instance['display_name'])
        if not instance['project_id']:
            msg = _('empty project id for instance %s')
            raise exception.InvalidInput(
                reason=msg % instance['display_name'])
        requested_networks = kwargs.get('requested_networks')
        ports = {}
        fixed_ips = {}
        net_ids = []
        if requested_networks:
            for network_id, fixed_ip, port_id in requested_networks:
                if port_id:
                    port = neutron.show_port(port_id)['port']
                    if hypervisor_macs is not None:
                        if port['mac_address'] not in hypervisor_macs:
                            raise exception.PortNotUsable(port_id=port_id,
                                instance=instance['display_name'])
                        else:
                            # Don't try to use this MAC if we need to create a
                            # port on the fly later. Identical MACs may be
                            # configured by users into multiple ports so we
                            # discard rather than popping.
                            available_macs.discard(port['mac_address'])
                    network_id = port['network_id']
                    ports[network_id] = port
                elif fixed_ip and network_id:
                    fixed_ips[network_id] = fixed_ip
                if network_id:
                    net_ids.append(network_id)

        nets = self._get_available_networks(context, instance['project_id'],
                                            net_ids)

        if not nets:
            LOG.warn(_("No network configured!"), instance=instance)
            return []

        security_groups = kwargs.get('security_groups', [])
        security_group_ids = []

        # TODO(arosen) Should optimize more to do direct query for security
        # group if len(security_groups) == 1
        if len(security_groups):
            search_opts = {'tenant_id': instance['project_id']}
            user_security_groups = neutron.list_security_groups(
                **search_opts).get('security_groups')

        for security_group in security_groups:
            name_match = None
            uuid_match = None
            for user_security_group in user_security_groups:
                if user_security_group['name'] == security_group:
                    if name_match:
                        msg = (_("Multiple security groups found matching"
                                 " '%s'. Use an ID to be more specific."),
                                 security_group)
                        raise exception.NoUniqueMatch(msg)
                    name_match = user_security_group['id']
                if user_security_group['id'] == security_group:
                    uuid_match = user_security_group['id']

            # If a user names the security group the same as
            # another's security groups uuid, the name takes priority.
            if not name_match and not uuid_match:
                raise exception.SecurityGroupNotFound(
                    security_group_id=security_group)
            elif name_match:
                security_group_ids.append(name_match)
            elif uuid_match:
                security_group_ids.append(uuid_match)

        touched_port_ids = []
        created_port_ids = []
        for network in nets:
            # If security groups are requested on an instance then the
            # network must has a subnet associated with it. Some plugins
            # implement the port-security extension which requires
            # 'port_security_enabled' to be True for security groups.
            # That is why True is returned if 'port_security_enabled'
            # is not found.
            if (security_groups and not (
                    network['subnets']
                    and network.get('port_security_enabled', True))):

                raise exception.SecurityGroupCannotBeApplied()
            network_id = network['id']
            zone = 'compute:%s' % instance['availability_zone']
            port_req_body = {'port': {'device_id': instance['uuid'],
                                      'device_owner': zone}}
            try:
                port = ports.get(network_id)
                self._populate_neutron_extension_values(instance,
                                                        port_req_body)
                # Requires admin creds to set port bindings
                port_client = (neutron if not
                               self._has_port_binding_extension() else
                               neutronv2.get_client(context, admin=True))
                if port:
                    port_client.update_port(port['id'], port_req_body)
                    touched_port_ids.append(port['id'])
                else:
                    fixed_ip = fixed_ips.get(network_id)
                    if fixed_ip:
                        port_req_body['port']['fixed_ips'] = [{'ip_address':
                                                               fixed_ip}]
                    port_req_body['port']['network_id'] = network_id
                    port_req_body['port']['admin_state_up'] = True
                    port_req_body['port']['tenant_id'] = instance['project_id']
                    if security_group_ids:
                        port_req_body['port']['security_groups'] = (
                            security_group_ids)
                    if available_macs is not None:
                        if not available_macs:
                            raise exception.PortNotFree(
                                instance=instance['display_name'])
                        mac_address = available_macs.pop()
                        port_req_body['port']['mac_address'] = mac_address
                    created_port_ids.append(
                        port_client.create_port(port_req_body)['port']['id'])
            except Exception:
                with excutils.save_and_reraise_exception():
                    for port_id in touched_port_ids:
                        try:
                            port_req_body = {'port': {'device_id': None}}
                            # Requires admin creds to set port bindings
                            if self._has_port_binding_extension():
                                port_req_body['port']['binding:host_id'] = None
                                port_client = neutronv2.get_client(
                                    context, admin=True)
                            else:
                                port_client = neutron
                            port_client.update_port(port_id, port_req_body)
                        except Exception:
                            msg = _("Failed to update port %s")
                            LOG.exception(msg, port_id)

                    for port_id in created_port_ids:
                        try:
                            neutron.delete_port(port_id)
                        except Exception:
                            msg = _("Failed to delete port %s")
                            LOG.exception(msg, port_id)

        nw_info = self._get_instance_nw_info(context, instance, networks=nets)
        # NOTE(danms): Only return info about ports we created in this run.
        # In the initial allocation case, this will be everything we created,
        # and in later runs will only be what was created that time. Thus,
        # this only affects the attach case, not the original use for this
        # method.
        return network_model.NetworkInfo([port for port in nw_info
                                          if port['id'] in created_port_ids +
                                                           touched_port_ids])
示例#14
0
 def raise_not_found(msg):
     raise exception.SecurityGroupNotFound(msg)
示例#15
0
    def allocate_for_instance(self, context, instance, **kwargs):
        """Allocate network resources for the instance.

        :param context: The request context.
        :param instance: nova.objects.instance.Instance object.
        :param requested_networks: optional value containing
            network_id, fixed_ip, and port_id
        :param security_groups: security groups to allocate for instance
        :param macs: None or a set of MAC addresses that the instance
            should use. macs is supplied by the hypervisor driver (contrast
            with requested_networks which is user supplied).
            NB: NeutronV2 currently assigns hypervisor supplied MAC addresses
            to arbitrary networks, which requires openflow switches to
            function correctly if more than one network is being used with
            the bare metal hypervisor (which is the only one known to limit
            MAC addresses).
        :param dhcp_options: None or a set of key/value pairs that should
            determine the DHCP BOOTP response, eg. for PXE booting an instance
            configured with the baremetal hypervisor. It is expected that these
            are already formatted for the neutron v2 api.
            See nova/virt/driver.py:dhcp_options_for_instance for an example.
        """
        hypervisor_macs = kwargs.get('macs', None)
        available_macs = None
        if hypervisor_macs is not None:
            # Make a copy we can mutate: records macs that have not been used
            # to create a port on a network. If we find a mac with a
            # pre-allocated port we also remove it from this set.
            available_macs = set(hypervisor_macs)
        neutron = neutronv2.get_client(context)
        LOG.debug('allocate_for_instance()', instance=instance)
        if not instance.project_id:
            msg = _('empty project id for instance %s')
            raise exception.InvalidInput(reason=msg % instance.uuid)
        requested_networks = kwargs.get('requested_networks')
        dhcp_opts = kwargs.get('dhcp_options', None)
        ports = {}
        net_ids = []
        ordered_networks = []
        if requested_networks:
            for request in requested_networks:
                if request.port_id:
                    try:
                        port = neutron.show_port(request.port_id)['port']
                    except neutron_client_exc.PortNotFoundClient:
                        raise exception.PortNotFound(port_id=request.port_id)
                    if port['tenant_id'] != instance.project_id:
                        raise exception.PortNotUsable(port_id=request.port_id,
                                                      instance=instance.uuid)
                    if (port.get('device_id')
                            and port.get('device_id') != instance['uuid']):
                        raise exception.PortInUse(port_id=request.port_id)
                    if hypervisor_macs is not None:
                        if port['mac_address'] not in hypervisor_macs:
                            raise exception.PortNotUsable(
                                port_id=request.port_id,
                                instance=instance.uuid)
                        else:
                            # Don't try to use this MAC if we need to create a
                            # port on the fly later. Identical MACs may be
                            # configured by users into multiple ports so we
                            # discard rather than popping.
                            available_macs.discard(port['mac_address'])
                    request.network_id = port['network_id']
                    ports[request.port_id] = port
                if request.network_id:
                    net_ids.append(request.network_id)
                    ordered_networks.append(request)

        nets = self._get_available_networks(context, instance.project_id,
                                            net_ids)
        if not nets:
            LOG.warn(_LW("No network configured!"), instance=instance)
            return network_model.NetworkInfo([])

        # if this function is directly called without a requested_network param
        # or if it is indirectly called through allocate_port_for_instance()
        # with None params=(network_id=None, requested_ip=None, port_id=None,
        # pci_request_id=None):
        if (not requested_networks
                or requested_networks.is_single_unspecified):
            # bug/1267723 - if no network is requested and more
            # than one is available then raise NetworkAmbiguous Exception
            if len(nets) > 1:
                msg = _("Multiple possible networks found, use a Network "
                        "ID to be more specific.")
                raise exception.NetworkAmbiguous(msg)
            ordered_networks.append(
                objects.NetworkRequest(network_id=nets[0]['id']))
            db_req_networks = list()
            db_obj = huawei_instance_extra.HuaweiInstanceExtra(
                instance_uuid=instance.uuid)
            db_instance = db_obj.get_by_instance_uuid(
                context, instance_uuid=instance.uuid)
            if db_instance.request_network:
                db_req_networks = jsonutils.loads(db_instance.request_network)
            db_req_networks.append([nets[0]['id'], None, None])
            db_obj.request_network = jsonutils.dumps(db_req_networks)
            db_obj.create(context)

        # NOTE(): check external net attach permission after the
        #                check for ambiguity, there could be another
        #                available net which is permitted bug/1364344
        self._check_external_network_attach(context, nets)

        security_groups = kwargs.get('security_groups', [])
        security_group_ids = []

        # TODO() Should optimize more to do direct query for security
        # group if len(security_groups) == 1
        if len(security_groups):
            search_opts = {'tenant_id': instance.project_id}
            user_security_groups = neutron.list_security_groups(
                **search_opts).get('security_groups')

        for security_group in security_groups:
            name_match = None
            uuid_match = None
            for user_security_group in user_security_groups:
                if user_security_group['name'] == security_group:
                    if name_match:
                        raise exception.NoUniqueMatch(
                            _("Multiple security groups found matching"
                              " '%s'. Use an ID to be more specific.") %
                            security_group)

                    name_match = user_security_group['id']
                if user_security_group['id'] == security_group:
                    uuid_match = user_security_group['id']

            # If a user names the security group the same as
            # another's security groups uuid, the name takes priority.
            if not name_match and not uuid_match:
                raise exception.SecurityGroupNotFound(
                    security_group_id=security_group)
            elif name_match:
                security_group_ids.append(name_match)
            elif uuid_match:
                security_group_ids.append(uuid_match)

        touched_port_ids = []
        created_port_ids = []
        ports_in_requested_order = []
        nets_in_requested_order = []
        for request in ordered_networks:
            # Network lookup for available network_id
            network = None
            for net in nets:
                if net['id'] == request.network_id:
                    network = net
                    break
            # if network_id did not pass validate_networks() and not available
            # here then skip it safely not continuing with a None Network
            else:
                continue

            nets_in_requested_order.append(network)
            # If security groups are requested on an instance then the
            # network must has a subnet associated with it. Some plugins
            # implement the port-security extension which requires
            # 'port_security_enabled' to be True for security groups.
            # That is why True is returned if 'port_security_enabled'
            # is not found.
            if (security_groups
                    and not (network['subnets']
                             and network.get('port_security_enabled', True))):
                # add for roll back
                self._delete_ports(neutron, instance, created_port_ids)
                raise exception.SecurityGroupCannotBeApplied()
            request.network_id = network['id']
            zone = 'compute:%s' % instance.availability_zone
            port_req_body = {
                'port': {
                    'device_id': instance.uuid,
                    'device_owner': zone
                }
            }
            try:
                self._populate_neutron_extension_values(
                    context, instance, request.pci_request_id, port_req_body)
                # Requires admin creds to set port bindings
                port_client = (neutron
                               if not self._has_port_binding_extension(context)
                               else neutronv2.get_client(context, admin=True))
                if request.port_id:
                    port = ports[request.port_id]
                    port_client.update_port(port['id'], port_req_body)
                    touched_port_ids.append(port['id'])
                    ports_in_requested_order.append(port['id'])
                else:
                    created_port = self._create_port(port_client, instance,
                                                     request.network_id,
                                                     port_req_body,
                                                     request.address,
                                                     security_group_ids,
                                                     available_macs, dhcp_opts)
                    created_port_ids.append(created_port)
                    ports_in_requested_order.append(created_port)
            except Exception:
                with excutils.save_and_reraise_exception():
                    for port_id in touched_port_ids:
                        try:
                            port_req_body = {'port': {'device_id': ''}}
                            # Requires admin creds to set port bindings
                            if self._has_port_binding_extension(context):
                                port_req_body['port']['binding:host_id'] = None
                                port_client = neutronv2.get_client(context,
                                                                   admin=True)
                            else:
                                port_client = neutron
                            port_client.update_port(port_id, port_req_body)
                        except Exception:
                            msg = _LE("Failed to update port %s")
                            LOG.exception(msg, port_id)

                    self._delete_ports(neutron, instance, created_port_ids)

        pci_list = kwargs.get('pci_list', [])
        nw_info = self.get_instance_nw_info(context,
                                            instance,
                                            networks=nets_in_requested_order,
                                            port_ids=ports_in_requested_order,
                                            pci_list=pci_list)
        # NOTE(): Only return info about ports we created in this run.
        # In the initial allocation case, this will be everything we created,
        # and in later runs will only be what was created that time. Thus,
        # this only affects the attach case, not the original use for this
        # method.
        return network_model.NetworkInfo([
            vif for vif in nw_info
            if vif['id'] in created_port_ids + touched_port_ids
        ])