Esempio n. 1
0
    def post(self, allocation):
        """Create a new allocation.

        :param allocation: an allocation within the request body.
        """
        context = api.request.context
        allocation = self._authorize_create_allocation(allocation)

        if (allocation.name
                and not api_utils.is_valid_logical_name(allocation.name)):
            msg = _("Cannot create allocation with invalid name "
                    "'%(name)s'") % {
                        'name': allocation.name
                    }
            raise exception.Invalid(msg)

        if allocation.traits:
            for trait in allocation.traits:
                api_utils.validate_trait(trait)

        node = None
        if allocation.node is not atypes.Unset:
            if api_utils.allow_allocation_backfill():
                try:
                    node = api_utils.get_rpc_node(allocation.node)
                except exception.NodeNotFound as exc:
                    exc.code = http_client.BAD_REQUEST
                    raise
            else:
                msg = _("Cannot set node when creating an allocation "
                        "in this API version")
                raise exception.Invalid(msg)

        if not allocation.resource_class:
            if node:
                allocation.resource_class = node.resource_class
            else:
                msg = _("The resource_class field is mandatory when not "
                        "backfilling")
                raise exception.Invalid(msg)

        if allocation.candidate_nodes:
            # Convert nodes from names to UUIDs and check their validity
            try:
                converted = api.request.dbapi.check_node_list(
                    allocation.candidate_nodes)
            except exception.NodeNotFound as exc:
                exc.code = http_client.BAD_REQUEST
                raise
            else:
                # Make sure we keep the ordering of candidate nodes.
                allocation.candidate_nodes = [
                    converted[ident] for ident in allocation.candidate_nodes
                ]

        all_dict = allocation.as_dict()

        # NOTE(yuriyz): UUID is mandatory for notifications payload
        if not all_dict.get('uuid'):
            if node and node.instance_uuid:
                # When backfilling without UUID requested, assume that the
                # target instance_uuid is the desired UUID
                all_dict['uuid'] = node.instance_uuid
            else:
                all_dict['uuid'] = uuidutils.generate_uuid()

        new_allocation = objects.Allocation(context, **all_dict)
        if node:
            new_allocation.node_id = node.id
            topic = api.request.rpcapi.get_topic_for(node)
        else:
            topic = api.request.rpcapi.get_random_topic()

        notify.emit_start_notification(context, new_allocation, 'create')
        with notify.handle_error_notification(context, new_allocation,
                                              'create'):
            new_allocation = api.request.rpcapi.create_allocation(
                context, new_allocation, topic)
        notify.emit_end_notification(context, new_allocation, 'create')

        # Set the HTTP Location Header
        api.response.location = link.build_url('allocations',
                                               new_allocation.uuid)
        return Allocation.convert_with_links(new_allocation)
Esempio n. 2
0
    def post(self, allocation):
        """Create a new allocation.

        :param allocation: an allocation within the request body.
        """
        context = api.request.context
        cdict = context.to_policy_values()
        allocation = self._authorize_create_allocation(allocation)

        if (allocation.get('name')
                and not api_utils.is_valid_logical_name(allocation['name'])):
            msg = _("Cannot create allocation with invalid name "
                    "'%(name)s'") % {
                        'name': allocation['name']
                    }
            raise exception.Invalid(msg)

        # TODO(TheJulia): We need to likely look at refactoring post
        # processing for allocations as pep8 says it is a complexity of 19,
        # although it is not actually that horrible since it is phased out
        # just modifying/assembling the allocation. Given that, it seems
        # not great to try for a full method rewrite at the same time as
        # RBAC work, so the complexity limit is being raised. :(
        if (CONF.oslo_policy.enforce_new_defaults
                and cdict.get('system_scope') != 'all'):
            # if not a system scope originated request, we need to check/apply
            # an owner - But we can only do this with when new defaults are
            # enabled.
            project_id = cdict.get('project_id')
            req_alloc_owner = allocation.get('owner')
            if req_alloc_owner:
                if not api_utils.check_policy_true(
                        'baremetal:allocation:create_restricted'):
                    if req_alloc_owner != project_id:
                        msg = _("Cannot create allocation with an owner "
                                "Project ID value %(req_owner)s not matching "
                                "the requestor Project ID %(project)s. "
                                "Policy baremetal:allocation:create_restricted"
                                " is required for this capability.") % {
                                    'req_owner': req_alloc_owner,
                                    'project': project_id
                                }
                        raise exception.NotAuthorized(msg)
                # NOTE(TheJulia): IF not restricted, i.e. else above,
                # their supplied allocation owner is okay, they are allowed
                # to provide an override by policy.
            else:
                # An allocation owner was not supplied, we need to save one.
                allocation['owner'] = project_id
        node = None
        if allocation.get('node'):
            if api_utils.allow_allocation_backfill():
                try:
                    node = api_utils.get_rpc_node(allocation['node'])
                    api_utils.check_owner_policy(
                        'node',
                        'baremetal:node:get',
                        node.owner,
                        node.lessee,
                        conceal_node=allocation['node'])
                except exception.NodeNotFound as exc:
                    exc.code = http_client.BAD_REQUEST
                    raise
            else:
                msg = _("Cannot set node when creating an allocation "
                        "in this API version")
                raise exception.Invalid(msg)

        if not allocation.get('resource_class'):
            if node:
                allocation['resource_class'] = node.resource_class
            else:
                msg = _("The resource_class field is mandatory when not "
                        "backfilling")
                raise exception.Invalid(msg)

        if allocation.get('candidate_nodes'):
            # Convert nodes from names to UUIDs and check their validity
            try:
                owner = None
                if not api_utils.check_policy_true(
                        'baremetal:allocation:create_restricted'):
                    owner = cdict.get('project_id')
                # Filter the candidate search by the requestor project ID
                # if any. The result is processes authenticating with system
                # scope will not be impacted, where as project scoped requests
                # will need additional authorization.
                converted = api.request.dbapi.check_node_list(
                    allocation['candidate_nodes'], project=owner)
            except exception.NodeNotFound as exc:
                exc.code = http_client.BAD_REQUEST
                raise
            else:
                # Make sure we keep the ordering of candidate nodes.
                allocation['candidate_nodes'] = [
                    converted[ident] for ident in allocation['candidate_nodes']
                ]

        # NOTE(yuriyz): UUID is mandatory for notifications payload
        if not allocation.get('uuid'):
            if node and node.instance_uuid:
                # When backfilling without UUID requested, assume that the
                # target instance_uuid is the desired UUID
                allocation['uuid'] = node.instance_uuid
            else:
                allocation['uuid'] = uuidutils.generate_uuid()

        new_allocation = objects.Allocation(context, **allocation)
        if node:
            new_allocation.node_id = node.id
            topic = api.request.rpcapi.get_topic_for(node)
        else:
            topic = api.request.rpcapi.get_random_topic()

        notify.emit_start_notification(context, new_allocation, 'create')
        with notify.handle_error_notification(context, new_allocation,
                                              'create'):
            new_allocation = api.request.rpcapi.create_allocation(
                context, new_allocation, topic)
        notify.emit_end_notification(context, new_allocation, 'create')

        # Set the HTTP Location Header
        api.response.location = link.build_url('allocations',
                                               new_allocation.uuid)
        return convert_with_links(new_allocation)