Exemple #1
0
 def allocate_port_for_instance(self,
                                context,
                                instance,
                                port_id,
                                network_id=None,
                                requested_ip=None,
                                pci_list=None):
     """Allocate a port for the instance."""
     if not network_id and not port_id:
         nets = self._get_available_networks(context, context.project_id)
         if len(nets) > 1:
             msg = _("Multiple possible networks found, use a Network "
                     "ID to be more specific.")
             raise exception.NetworkAmbiguous(msg)
     requested_networks = objects.NetworkRequestList(objects=[
         objects.NetworkRequest(network_id=network_id,
                                address=requested_ip,
                                port_id=port_id,
                                pci_request_id=None)
     ])
     return self.allocate_for_instance(
         context,
         instance,
         requested_networks=requested_networks,
         pci_list=pci_list)
Exemple #2
0
    def validate_networks(self, context, requested_networks):
        """Validate that the tenant can use the requested networks."""
        LOG.debug(_('validate_networks() for %s'),
                  requested_networks)

        if not requested_networks:
            nets = self._get_available_networks(context, context.project_id)
            if len(nets) > 1:
                # Attaching to more than one network by default doesn't
                # make sense, as the order will be arbitrary and the guest OS
                # won't know which to configure
                msg = _("Multiple possible networks found, use a Network "
                         "ID to be more specific.")
                raise exception.NetworkAmbiguous(msg)
            return

        net_ids = []

        for (net_id, _i, port_id) in requested_networks:
            if port_id:
                try:
                    port = (neutronv2.get_client(context)
                                     .show_port(port_id)
                                     .get('port'))
                except neutronv2.exceptions.NeutronClientException as e:
                    if e.status_code == 404:
                        port = None
                if not port:
                    raise exception.PortNotFound(port_id=port_id)
                if port.get('device_id', None):
                    raise exception.PortInUse(port_id=port_id)
                net_id = port['network_id']
            if net_id in net_ids:
                raise exception.NetworkDuplicated(network_id=net_id)
            net_ids.append(net_id)

        # Now check to see if all requested networks exist
        nets = self._get_available_networks(context,
                                context.project_id, net_ids)

        if len(nets) != len(net_ids):
            requsted_netid_set = set(net_ids)
            returned_netid_set = set([net['id'] for net in nets])
            lostid_set = requsted_netid_set - returned_netid_set
            id_str = ''
            for _id in lostid_set:
                id_str = id_str and id_str + ', ' + _id or _id
            raise exception.NetworkNotFound(network_id=id_str)
Exemple #3
0
    def allocate_port_for_instance(self, context, instance, provider_port):
        neutron = neutronv2.get_client(context)
        LOG.debug('allocate_port_for_instance()', instance=instance)
        if not instance.project_id:
            msg = _('empty project id for instance %s')
            raise exception.InvalidInput(reason=msg % instance.uuid)

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

        if (not provider_port or provider_port.is_single_unspecified):
            # 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)

        zone = 'compute:%s' % instance.availability_zone
        port_req_body = {
            'port': {
                'device_id': instance.uuid,
                'device_owner': zone
            }
        }
        port_client = (neutron if not self._has_port_binding_extension(context)
                       else neutronv2.get_client(context, admin=True))

        self._check_external_network_attach(context, nets)
        created_port = self._create_port(port_client, instance,
                                         provider_port.network_id,
                                         provider_port.port_name,
                                         port_req_body, provider_port.port_mac)
        nw_info = network_model.NetworkInfo()
        if created_port:
            vif_active = False
            if (created_port['admin_state_up'] is False
                    or created_port['status'] == 'ACTIVE'):
                vif_active = True

            client = neutronv2.get_client(context, admin=True)
            network_IPs = self._nw_info_get_ips(client, created_port)
            subnets = self._nw_info_get_subnets(context, created_port,
                                                network_IPs)

            devname = "tap" + created_port['id']
            devname = devname[:network_model.NIC_NAME_LEN]

            network, ovs_interfaceid = (self._nw_info_build_network(
                created_port, nets, subnets))

            nw_info.append(
                network_model.VIF(
                    id=created_port['id'],
                    address=created_port['mac_address'],
                    network=network,
                    vnic_type=created_port.get('binding:vnic_type',
                                               network_model.VNIC_TYPE_NORMAL),
                    type=created_port.get('binding:vif_type'),
                    profile=created_port.get('binding:profile'),
                    details=created_port.get('binding:vif_details'),
                    ovs_interfaceid=ovs_interfaceid,
                    devname=devname,
                    active=vif_active))

        return nw_info
    def validate_networks(self, context, requested_networks, num_instances):
        """Validate that the tenant can use the requested networks.

        Return the number of instances than can be successfully allocated
        with the requested network configuration.
        """
        LOG.debug(_('validate_networks() for %s'), requested_networks)

        neutron = neutronv2.get_client(context)
        ports_needed_per_instance = 0

        if not requested_networks:
            nets = self._get_available_networks(context,
                                                context.project_id,
                                                neutron=neutron)
            if len(nets) > 1:
                # Attaching to more than one network by default doesn't
                # make sense, as the order will be arbitrary and the guest OS
                # won't know which to configure
                msg = _("Multiple possible networks found, use a Network "
                        "ID to be more specific.")
                raise exception.NetworkAmbiguous(msg)
            else:
                ports_needed_per_instance = 1

        else:
            net_ids = []

            for (net_id, _i, port_id) in requested_networks:
                if port_id:
                    try:
                        port = neutron.show_port(port_id).get('port')
                    except neutronv2.exceptions.NeutronClientException as e:
                        if e.status_code == 404:
                            port = None
                        else:
                            with excutils.save_and_reraise_exception():
                                LOG.exception(_("Failed to access port %s"),
                                              port_id)
                    if not port:
                        raise exception.PortNotFound(port_id=port_id)
                    if port.get('device_id', None):
                        raise exception.PortInUse(port_id=port_id)
                    if not port.get('fixed_ips'):
                        raise exception.PortRequiresFixedIP(port_id=port_id)
                    net_id = port['network_id']
                else:
                    ports_needed_per_instance += 1

                if net_id in net_ids:
                    raise exception.NetworkDuplicated(network_id=net_id)
                net_ids.append(net_id)

            # Now check to see if all requested networks exist
            nets = self._get_available_networks(context,
                                                context.project_id,
                                                net_ids,
                                                neutron=neutron)
            for net in nets:
                if not net.get('subnets'):
                    raise exception.NetworkRequiresSubnet(
                        network_uuid=net['id'])

            if len(nets) != len(net_ids):
                requsted_netid_set = set(net_ids)
                returned_netid_set = set([net['id'] for net in nets])
                lostid_set = requsted_netid_set - returned_netid_set
                id_str = ''
                for _id in lostid_set:
                    id_str = id_str and id_str + ', ' + _id or _id
                raise exception.NetworkNotFound(network_id=id_str)

        # Note(PhilD): Ideally Nova would create all required ports as part of
        # network validation, but port creation requires some details
        # from the hypervisor.  So we just check the quota and return
        # how many of the requested number of instances can be created

        ports = []
        quotas = neutron.show_quota(tenant_id=context.project_id)['quota']
        if quotas.get('port', -1) == -1:
            # Unlimited Port Quota
            return num_instances
        else:
            free_ports = quotas.get('port') - len(ports)
            ports_needed = ports_needed_per_instance * num_instances
            if free_ports >= ports_needed:
                return num_instances
            else:
                return free_ports // ports_needed_per_instance
Exemple #5
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
        ])