Exemple #1
0
    def post(self, allocation):
        """Create a new allocation.

        :param allocation: an allocation within the request body.
        """
        context = pecan.request.context
        cdict = context.to_policy_values()
        policy.authorize('baremetal:allocation:create', cdict, cdict)

        if allocation.node_uuid is not wtypes.Unset:
            msg = _("Cannot set node_uuid when creating an allocation")
            raise exception.Invalid(msg)

        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)

        if allocation.candidate_nodes:
            # Convert nodes from names to UUIDs and check their validity
            try:
                converted = pecan.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'):
            all_dict['uuid'] = uuidutils.generate_uuid()

        new_allocation = objects.Allocation(context, **all_dict)
        topic = pecan.request.rpcapi.get_random_topic()

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

        # Set the HTTP Location Header
        pecan.response.location = link.build_url('allocations',
                                                 new_allocation.uuid)
        return Allocation.convert_with_links(new_allocation)
Exemple #2
0
 def test_owner_unsupported_set_no_remove_default(self):
     # Physical network set, no change required.
     allocation = objects.Allocation(self.context, **self.fake_allocation)
     allocation.owner = None
     allocation.obj_reset_changes()
     allocation._convert_to_version("1.0", False)
     self.assertIsNone(allocation.owner)
     self.assertEqual({}, allocation.obj_get_changes())
Exemple #3
0
 def test_owner_unsupported_set_no_remove_non_default(self):
     # Physical network set, should be set to default.
     allocation = objects.Allocation(self.context, **self.fake_allocation)
     allocation.owner = 'owner1'
     allocation.obj_reset_changes()
     allocation._convert_to_version("1.0", False)
     self.assertIsNone(allocation.owner)
     self.assertEqual({'owner': None}, allocation.obj_get_changes())
Exemple #4
0
 def test_owner_unsupported_set_remove(self):
     # Physical network set, should be removed.
     allocation = objects.Allocation(self.context, **self.fake_allocation)
     allocation.owner = 'owner1'
     allocation.obj_reset_changes()
     allocation._convert_to_version("1.0")
     self.assertNotIn('owner', allocation)
     self.assertEqual({}, allocation.obj_get_changes())
Exemple #5
0
 def test_owner_unsupported_missing(self):
     # Physical network not set, no change required.
     allocation = objects.Allocation(self.context, **self.fake_allocation)
     delattr(allocation, 'owner')
     allocation.obj_reset_changes()
     allocation._convert_to_version("1.0")
     self.assertNotIn('owner', allocation)
     self.assertEqual({}, allocation.obj_get_changes())
Exemple #6
0
 def test_owner_supported_set(self):
     # Physical network set, no change required.
     allocation = objects.Allocation(self.context, **self.fake_allocation)
     allocation.owner = 'owner1'
     allocation.obj_reset_changes()
     allocation._convert_to_version("1.1")
     self.assertEqual('owner1', allocation.owner)
     self.assertEqual({}, allocation.obj_get_changes())
Exemple #7
0
 def test_owner_supported_missing(self):
     # Physical network not set, should be set to default.
     allocation = objects.Allocation(self.context, **self.fake_allocation)
     delattr(allocation, 'owner')
     allocation.obj_reset_changes()
     allocation._convert_to_version("1.1")
     self.assertIsNone(allocation.owner)
     self.assertEqual({'owner': None}, allocation.obj_get_changes())
Exemple #8
0
    def test_create(self):
        allocation = objects.Allocation(self.context, **self.fake_allocation)
        with mock.patch.object(self.dbapi, 'create_allocation',
                               autospec=True) as mock_create_allocation:
            mock_create_allocation.return_value = (
                db_utils.get_test_allocation())

            allocation.create()

            args, _kwargs = mock_create_allocation.call_args
            self.assertEqual(objects.Allocation.VERSION, args[0]['version'])
Exemple #9
0
def get_test_allocation(ctxt, **kw):
    """Return an Allocation object with appropriate attributes.

    NOTE: The object leaves the attributes marked as changed, such
    that a create() could be used to commit it to the DB.
    """
    kw['object_type'] = 'allocation'
    get_db_allocation_checked = check_keyword_arguments(
        db_utils.get_test_allocation)
    db_allocation = get_db_allocation_checked(**kw)

    # Let DB generate ID if it isn't specified explicitly
    if 'id' not in kw:
        del db_allocation['id']
    allocation = objects.Allocation(ctxt)
    for key in db_allocation:
        setattr(allocation, key, db_allocation[key])
    return allocation
Exemple #10
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)
Exemple #11
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)