Exemple #1
0
def _new_allocations(context, resource_provider_uuid, consumer_uuid, resources,
                     project_id, user_id):
    """Create new allocation objects for a set of resources

    Returns a list of Allocation objects.

    :param context: The placement context.
    :param resource_provider_uuid: The uuid of the resource provider that
                                   has the resources.
    :param consumer_uuid: The uuid of the consumer of the resources.
    :param resources: A dict of resource classes and values.
    :param project_id: The project consuming the resources.
    :param user_id: The user consuming the resources.
    """
    allocations = []
    try:
        resource_provider = rp_obj.ResourceProvider.get_by_uuid(
            context, resource_provider_uuid)
    except exception.NotFound:
        raise webob.exc.HTTPBadRequest(
            _("Allocation for resource provider '%(rp_uuid)s' "
              "that does not exist.") % {'rp_uuid': resource_provider_uuid})
    util.ensure_consumer(context, consumer_uuid, project_id, user_id)
    for resource_class in resources:
        allocation = rp_obj.Allocation(resource_provider=resource_provider,
                                       consumer_id=consumer_uuid,
                                       resource_class=resource_class,
                                       project_id=project_id,
                                       user_id=user_id,
                                       used=resources[resource_class])
        allocations.append(allocation)
    return allocations
Exemple #2
0
    def test_no_existing_project_user_consumer(self):
        self.mock_project_get.side_effect = exception.NotFound
        self.mock_user_get.side_effect = exception.NotFound
        self.mock_consumer_get.side_effect = exception.NotFound

        util.ensure_consumer(self.ctx, self.consumer_id, self.project_id,
                             self.user_id)

        self.mock_project_get.assert_called_once_with(self.ctx,
                                                      self.project_id)
        self.mock_user_get.assert_called_once_with(self.ctx, self.user_id)
        self.mock_consumer_get.assert_called_once_with(self.ctx,
                                                       self.consumer_id)
        self.mock_project_create.assert_called_once()
        self.mock_user_create.assert_called_once()
        self.mock_consumer_create.assert_called_once()
Exemple #3
0
def _new_allocations(context, resource_provider_uuid, consumer_uuid, resources,
                     project_id, user_id, consumer_generation, want_version):
    """Create new allocation objects for a set of resources

    Returns a list of Allocation objects.

    :param context: The placement context.
    :param resource_provider_uuid: The uuid of the resource provider that
                                   has the resources.
    :param consumer_uuid: The uuid of the consumer of the resources.
    :param resources: A dict of resource classes and values.
    :param project_id: The project consuming the resources.
    :param user_id: The user consuming the resources.
    :param consumer_generation: The generation supplied by the user when
                                PUT/POST'ing allocations. May be None if
                                the microversion is <1.28
    :param want_version: The microversion object from the context.
    """
    allocations = []
    try:
        resource_provider = rp_obj.ResourceProvider.get_by_uuid(
            context, resource_provider_uuid)
    except exception.NotFound:
        raise webob.exc.HTTPBadRequest(
            _("Allocation for resource provider '%(rp_uuid)s' "
              "that does not exist.") % {'rp_uuid': resource_provider_uuid})
    consumer = util.ensure_consumer(context, consumer_uuid, project_id,
                                    user_id, consumer_generation, want_version)
    for resource_class in resources:
        allocation = rp_obj.Allocation(resource_provider=resource_provider,
                                       consumer=consumer,
                                       resource_class=resource_class,
                                       used=resources[resource_class])
        allocations.append(allocation)
    return allocations
Exemple #4
0
    def test_existing_project_user_no_existing_consumer(self):
        """Check that if we find an existing project and user, that we use
        those found objects in creating the consumer.
        """
        proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
        self.mock_project_get.return_value = proj
        user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
        self.mock_user_get.return_value = user
        self.mock_consumer_get.side_effect = exception.NotFound

        util.ensure_consumer(self.ctx, self.consumer_id, self.project_id,
                             self.user_id)

        self.mock_project_create.assert_not_called()
        self.mock_user_create.assert_not_called()
        self.mock_consumer_create.assert_called_once()
Exemple #5
0
    def test_existing_project_no_existing_consumer_before_gen_success(self):
        """Check that if we find an existing project and user, that we use
        those found objects in creating the consumer. Do not require a consumer
        generation before the appropriate microversion.
        """
        proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
        self.mock_project_get.return_value = proj
        user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
        self.mock_user_get.return_value = user
        self.mock_consumer_get.side_effect = exception.NotFound

        consumer_gen = None  # should be ignored
        util.ensure_consumer(self.ctx, self.consumer_id, self.project_id,
                             self.user_id, consumer_gen, self.before_version)

        self.mock_project_create.assert_not_called()
        self.mock_user_create.assert_not_called()
        self.mock_consumer_create.assert_called_once()
Exemple #6
0
    def test_existing_project_no_existing_consumer_before_gen_success(self):
        """Check that if we find an existing project and user, that we use
        those found objects in creating the consumer. Do not require a consumer
        generation before the appropriate microversion.
        """
        proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
        self.mock_project_get.return_value = proj
        user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
        self.mock_user_get.return_value = user
        self.mock_consumer_get.side_effect = exception.NotFound

        consumer_gen = None  # should be ignored
        util.ensure_consumer(
            self.ctx, self.consumer_id, self.project_id, self.user_id,
            consumer_gen, self.before_version)

        self.mock_project_create.assert_not_called()
        self.mock_user_create.assert_not_called()
        self.mock_consumer_create.assert_called_once()
Exemple #7
0
    def test_no_existing_project_user_consumer_use_incomplete(self):
        """Verify that if the project_id arg is None, that we fall back to the
        CONF options for incomplete project and user ID.
        """
        self.mock_project_get.side_effect = exception.NotFound
        self.mock_user_get.side_effect = exception.NotFound
        self.mock_consumer_get.side_effect = exception.NotFound

        util.ensure_consumer(self.ctx, self.consumer_id, None, None)

        self.mock_project_get.assert_called_once_with(
            self.ctx, CONF.placement.incomplete_consumer_project_id)
        self.mock_user_get.assert_called_once_with(
            self.ctx, CONF.placement.incomplete_consumer_user_id)
        self.mock_consumer_get.assert_called_once_with(self.ctx,
                                                       self.consumer_id)
        self.mock_project_create.assert_called_once()
        self.mock_user_create.assert_called_once()
        self.mock_consumer_create.assert_called_once()
Exemple #8
0
    def test_no_existing_project_user_consumer_before_gen_success(self):
        """Tests that we don't require a consumer_generation=None before the
        appropriate microversion.
        """
        self.mock_project_get.side_effect = exception.NotFound
        self.mock_user_get.side_effect = exception.NotFound
        self.mock_consumer_get.side_effect = exception.NotFound

        consumer_gen = 1  # should be ignored
        util.ensure_consumer(self.ctx, self.consumer_id, self.project_id,
                             self.user_id, consumer_gen, self.before_version)

        self.mock_project_get.assert_called_once_with(self.ctx,
                                                      self.project_id)
        self.mock_user_get.assert_called_once_with(self.ctx, self.user_id)
        self.mock_consumer_get.assert_called_once_with(self.ctx,
                                                       self.consumer_id)
        self.mock_project_create.assert_called_once()
        self.mock_user_create.assert_called_once()
        self.mock_consumer_create.assert_called_once()
Exemple #9
0
    def test_no_existing_project_user_consumer_after_gen_success(self):
        """Tests that we require a consumer_generation=None after the
        appropriate microversion.
        """
        self.mock_project_get.side_effect = exception.NotFound
        self.mock_user_get.side_effect = exception.NotFound
        self.mock_consumer_get.side_effect = exception.NotFound

        consumer_gen = None  # should NOT be ignored (and None is expected)
        util.ensure_consumer(self.ctx, self.consumer_id, self.project_id,
                             self.user_id, consumer_gen, self.after_version)

        self.mock_project_get.assert_called_once_with(self.ctx,
                                                      self.project_id)
        self.mock_user_get.assert_called_once_with(self.ctx, self.user_id)
        self.mock_consumer_get.assert_called_once_with(self.ctx,
                                                       self.consumer_id)
        self.mock_project_create.assert_called_once()
        self.mock_user_create.assert_called_once()
        self.mock_consumer_create.assert_called_once()
Exemple #10
0
    def test_existing_consumer_after_gen_matches_supplied_gen(self):
        """Tests that we require a consumer_generation after the
        appropriate microversion and that when the consumer already exists,
        then we ensure a matching generation is supplied
        """
        proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
        self.mock_project_get.return_value = proj
        user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
        self.mock_user_get.return_value = user
        consumer = consumer_obj.Consumer(
            self.ctx, id=1, project=proj, user=user, generation=2)
        self.mock_consumer_get.return_value = consumer

        consumer_gen = 2  # should NOT be ignored (and 2 is expected)
        util.ensure_consumer(
            self.ctx, self.consumer_id, self.project_id, self.user_id,
            consumer_gen, self.after_version)

        self.mock_project_create.assert_not_called()
        self.mock_user_create.assert_not_called()
        self.mock_consumer_create.assert_not_called()
Exemple #11
0
    def test_no_existing_project_user_consumer_after_gen_success(self):
        """Tests that we require a consumer_generation=None after the
        appropriate microversion.
        """
        self.mock_project_get.side_effect = exception.NotFound
        self.mock_user_get.side_effect = exception.NotFound
        self.mock_consumer_get.side_effect = exception.NotFound

        consumer_gen = None  # should NOT be ignored (and None is expected)
        util.ensure_consumer(
            self.ctx, self.consumer_id, self.project_id, self.user_id,
            consumer_gen, self.after_version)

        self.mock_project_get.assert_called_once_with(
            self.ctx, self.project_id)
        self.mock_user_get.assert_called_once_with(
            self.ctx, self.user_id)
        self.mock_consumer_get.assert_called_once_with(
            self.ctx, self.consumer_id)
        self.mock_project_create.assert_called_once()
        self.mock_user_create.assert_called_once()
        self.mock_consumer_create.assert_called_once()
Exemple #12
0
    def test_no_existing_project_user_consumer_before_gen_success(self):
        """Tests that we don't require a consumer_generation=None before the
        appropriate microversion.
        """
        self.mock_project_get.side_effect = exception.NotFound
        self.mock_user_get.side_effect = exception.NotFound
        self.mock_consumer_get.side_effect = exception.NotFound

        consumer_gen = 1  # should be ignored
        util.ensure_consumer(
            self.ctx, self.consumer_id, self.project_id, self.user_id,
            consumer_gen, self.before_version)

        self.mock_project_get.assert_called_once_with(
            self.ctx, self.project_id)
        self.mock_user_get.assert_called_once_with(
            self.ctx, self.user_id)
        self.mock_consumer_get.assert_called_once_with(
            self.ctx, self.consumer_id)
        self.mock_project_create.assert_called_once()
        self.mock_user_create.assert_called_once()
        self.mock_consumer_create.assert_called_once()
Exemple #13
0
    def test_no_existing_project_user_consumer_use_incomplete(self):
        """Verify that if the project_id arg is None, that we fall back to the
        CONF options for incomplete project and user ID.
        """
        self.mock_project_get.side_effect = exception.NotFound
        self.mock_user_get.side_effect = exception.NotFound
        self.mock_consumer_get.side_effect = exception.NotFound

        consumer_gen = None  # should NOT be ignored (and None is expected)
        util.ensure_consumer(
            self.ctx, self.consumer_id, None, None,
            consumer_gen, self.before_version)

        self.mock_project_get.assert_called_once_with(
            self.ctx, CONF.placement.incomplete_consumer_project_id)
        self.mock_user_get.assert_called_once_with(
            self.ctx, CONF.placement.incomplete_consumer_user_id)
        self.mock_consumer_get.assert_called_once_with(
            self.ctx, self.consumer_id)
        self.mock_project_create.assert_called_once()
        self.mock_user_create.assert_called_once()
        self.mock_consumer_create.assert_called_once()
Exemple #14
0
    def test_existing_consumer_after_gen_matches_supplied_gen(self):
        """Tests that we require a consumer_generation after the
        appropriate microversion and that when the consumer already exists,
        then we ensure a matching generation is supplied
        """
        proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
        self.mock_project_get.return_value = proj
        user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
        self.mock_user_get.return_value = user
        consumer = consumer_obj.Consumer(self.ctx,
                                         id=1,
                                         project=proj,
                                         user=user,
                                         generation=2)
        self.mock_consumer_get.return_value = consumer

        consumer_gen = 2  # should NOT be ignored (and 2 is expected)
        util.ensure_consumer(self.ctx, self.consumer_id, self.project_id,
                             self.user_id, consumer_gen, self.after_version)

        self.mock_project_create.assert_not_called()
        self.mock_user_create.assert_not_called()
        self.mock_consumer_create.assert_not_called()
Exemple #15
0
def inspect_consumers(context, data, want_version):
    """Look at consumer data in allocations and create consumers as needed.

    Keep a record of the consumers that are created in case they need
    to be removed later.

    If an exception is raised by ensure_consumer, commonly HTTPConflict but
    also anything else, the newly created consumers will be deleted and the
    exception reraised to the caller.

    :param context: The placement context.
    :param data: A dictionary of multiple allocations by consumer uuid.
    :param want_version: the microversion matcher.
    :return: A tuple of a dict of all consumer objects (by consumer uuid)
             and a list of those consumer objects which are new.
    """
    # First, ensure that all consumers referenced in the payload actually
    # exist. And if not, create them. Keep a record of auto-created consumers
    # so we can clean them up if the end allocation replace_all() fails.
    consumers = {}  # dict of Consumer objects, keyed by consumer UUID
    new_consumers_created = []
    for consumer_uuid in data:
        project_id = data[consumer_uuid]['project_id']
        user_id = data[consumer_uuid]['user_id']
        consumer_generation = data[consumer_uuid].get('consumer_generation')
        try:
            consumer, new_consumer_created = util.ensure_consumer(
                context, consumer_uuid, project_id, user_id,
                consumer_generation, want_version)
            if new_consumer_created:
                new_consumers_created.append(consumer)
            consumers[consumer_uuid] = consumer
        except Exception:
            # If any errors (for instance, a consumer generation conflict)
            # occur when ensuring consumer records above, make sure we delete
            # any auto-created consumers.
            with excutils.save_and_reraise_exception():
                delete_consumers(new_consumers_created)
    return consumers, new_consumers_created
Exemple #16
0
def inspect_consumers(context, data, want_version):
    """Look at consumer data in allocations and create consumers as needed.

    Keep a record of the consumers that are created in case they need
    to be removed later.

    If an exception is raised by ensure_consumer, commonly HTTPConflict but
    also anything else, the newly created consumers will be deleted and the
    exception reraised to the caller.

    :param context: The placement context.
    :param data: A dictionary of multiple allocations by consumer uuid.
    :param want_version: the microversion matcher.
    :return: A tuple of a dict of all consumer objects (by consumer uuid)
             and a list of those consumer objects which are new.
    """
    # First, ensure that all consumers referenced in the payload actually
    # exist. And if not, create them. Keep a record of auto-created consumers
    # so we can clean them up if the end allocation replace_all() fails.
    consumers = {}  # dict of Consumer objects, keyed by consumer UUID
    new_consumers_created = []
    for consumer_uuid in data:
        project_id = data[consumer_uuid]['project_id']
        user_id = data[consumer_uuid]['user_id']
        consumer_generation = data[consumer_uuid].get('consumer_generation')
        try:
            consumer, new_consumer_created = util.ensure_consumer(
                context, consumer_uuid, project_id, user_id,
                consumer_generation, want_version)
            if new_consumer_created:
                new_consumers_created.append(consumer)
            consumers[consumer_uuid] = consumer
        except Exception:
            # If any errors (for instance, a consumer generation conflict)
            # occur when ensuring consumer records above, make sure we delete
            # any auto-created consumers.
            with excutils.save_and_reraise_exception():
                delete_consumers(new_consumers_created)
    return consumers, new_consumers_created
Exemple #17
0
def _set_allocations_for_consumer(req, schema):
    context = req.environ['placement.context']
    context.can(policies.ALLOC_UPDATE)
    consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid')
    if not uuidutils.is_uuid_like(consumer_uuid):
        raise webob.exc.HTTPBadRequest(
            _('Malformed consumer_uuid: %(consumer_uuid)s') %
            {'consumer_uuid': consumer_uuid})
    consumer_uuid = str(uuid.UUID(consumer_uuid))
    data = util.extract_json(req.body, schema)
    allocation_data = data['allocations']

    # Normalize allocation data to dict.
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    if not want_version.matches((1, 12)):
        allocations_dict = {}
        # Allocation are list-ish, transform to dict-ish
        for allocation in allocation_data:
            resource_provider_uuid = allocation['resource_provider']['uuid']
            allocations_dict[resource_provider_uuid] = {
                'resources': allocation['resources']
            }
        allocation_data = allocations_dict

    allocation_objects = []
    # Consumer object saved in case we need to delete the auto-created consumer
    # record
    consumer = None
    # Whether we created a new consumer record
    created_new_consumer = False
    if not allocation_data:
        # The allocations are empty, which means wipe them out. Internal
        # to the allocation object this is signalled by a used value of 0.
        # We still need to verify the consumer's generation, though, which
        # we do in _ensure_consumer()
        # NOTE(jaypipes): This will only occur 1.28+. The JSONSchema will
        # prevent an empty allocations object from being passed when there is
        # no consumer generation, so this is safe to do.
        util.ensure_consumer(context, consumer_uuid, data.get('project_id'),
             data.get('user_id'), data.get('consumer_generation'),
             want_version)
        allocations = rp_obj.AllocationList.get_all_by_consumer_id(
            context, consumer_uuid)
        for allocation in allocations:
            allocation.used = 0
            allocation_objects.append(allocation)
    else:
        # If the body includes an allocation for a resource provider
        # that does not exist, raise a 400.
        rp_objs = _resource_providers_by_uuid(context, allocation_data.keys())
        consumer, created_new_consumer = util.ensure_consumer(
            context, consumer_uuid, data.get('project_id'),
            data.get('user_id'), data.get('consumer_generation'),
            want_version)
        for resource_provider_uuid, allocation in allocation_data.items():
            resource_provider = rp_objs[resource_provider_uuid]
            new_allocations = _new_allocations(context,
                                               resource_provider,
                                               consumer,
                                               allocation['resources'])
            allocation_objects.extend(new_allocations)

    allocations = rp_obj.AllocationList(
        context, objects=allocation_objects)

    def _create_allocations(alloc_list):
        try:
            alloc_list.replace_all()
            LOG.debug("Successfully wrote allocations %s", alloc_list)
        except Exception:
            if created_new_consumer:
                delete_consumers([consumer])
            raise

    try:
        _create_allocations(allocations)
    # InvalidInventory is a parent for several exceptions that
    # indicate either that Inventory is not present, or that
    # capacity limits have been exceeded.
    except exception.NotFound as exc:
        raise webob.exc.HTTPBadRequest(
                _("Unable to allocate inventory for consumer "
                  "%(consumer_uuid)s: %(error)s") %
            {'consumer_uuid': consumer_uuid, 'error': exc})
    except exception.InvalidInventory as exc:
        raise webob.exc.HTTPConflict(
            _('Unable to allocate inventory: %(error)s') % {'error': exc})
    except exception.ConcurrentUpdateDetected as exc:
        raise webob.exc.HTTPConflict(
            _('Inventory and/or allocations changed while attempting to '
              'allocate: %(error)s') % {'error': exc},
              comment=errors.CONCURRENT_UPDATE)

    req.response.status = 204
    req.response.content_type = None
    return req.response
Exemple #18
0
def set_allocations(req):
    context = req.environ['placement.context']
    context.can(policies.ALLOC_MANAGE)
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    want_schema = schema.POST_ALLOCATIONS_V1_13
    if want_version.matches((1, 28)):
        want_schema = schema.POST_ALLOCATIONS_V1_28
    data = util.extract_json(req.body, want_schema)

    # First, ensure that all consumers referenced in the payload actually
    # exist. And if not, create them. Keep a record of auto-created consumers
    # so we can clean them up if the end allocation replace_all() fails.
    consumers = {}  # dict of Consumer objects, keyed by consumer UUID
    new_consumers_created = []
    for consumer_uuid in data:
        project_id = data[consumer_uuid]['project_id']
        user_id = data[consumer_uuid]['user_id']
        consumer_generation = data[consumer_uuid].get('consumer_generation')
        try:
            consumer, new_consumer_created = util.ensure_consumer(
                context, consumer_uuid, project_id, user_id,
                consumer_generation, want_version)
            if new_consumer_created:
                new_consumers_created.append(consumer)
            consumers[consumer_uuid] = consumer
        except Exception:
            # If any errors (for instance, a consumer generation conflict)
            # occur when ensuring consumer records above, make sure we delete
            # any auto-created consumers.
            _delete_consumers(new_consumers_created)
            raise

    # Create a sequence of allocation objects to be used in one
    # AllocationList.replace_all() call, which will mean all the changes
    # happen within a single transaction and with resource provider
    # and consumer generations (if applicable) check all in one go.
    allocations = create_allocation_list(context, data, consumers)

    def _create_allocations(alloc_list):
        try:
            alloc_list.replace_all()
            LOG.debug("Successfully wrote allocations %s", alloc_list)
        except Exception:
            _delete_consumers(new_consumers_created)
            raise

    try:
        _create_allocations(allocations)
    except exception.NotFound as exc:
        raise webob.exc.HTTPBadRequest(
            _("Unable to allocate inventory %(error)s") % {'error': exc})
    except exception.InvalidInventory as exc:
        # InvalidInventory is a parent for several exceptions that
        # indicate either that Inventory is not present, or that
        # capacity limits have been exceeded.
        raise webob.exc.HTTPConflict(
            _('Unable to allocate inventory: %(error)s') % {'error': exc})
    except exception.ConcurrentUpdateDetected as exc:
        raise webob.exc.HTTPConflict(
            _('Inventory and/or allocations changed while attempting to '
              'allocate: %(error)s') % {'error': exc},
              comment=errors.CONCURRENT_UPDATE)

    req.response.status = 204
    req.response.content_type = None
    return req.response
Exemple #19
0
def _set_allocations_for_consumer(req, schema):
    context = req.environ['placement.context']
    context.can(policies.ALLOC_UPDATE)
    consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid')
    data = util.extract_json(req.body, schema)
    allocation_data = data['allocations']

    # Normalize allocation data to dict.
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    if not want_version.matches((1, 12)):
        allocations_dict = {}
        # Allocation are list-ish, transform to dict-ish
        for allocation in allocation_data:
            resource_provider_uuid = allocation['resource_provider']['uuid']
            allocations_dict[resource_provider_uuid] = {
                'resources': allocation['resources']
            }
        allocation_data = allocations_dict

    # If the body includes an allocation for a resource provider
    # that does not exist, raise a 400.
    allocation_objects = []
    if not allocation_data:
        # The allocations are empty, which means wipe them out. Internal
        # to the allocation object this is signalled by a used value of 0.
        # We still need to verify the consumer's generation, though, which
        # we do in _ensure_consumer()
        # NOTE(jaypipes): This will only occur 1.28+. The JSONSchema will
        # prevent an empty allocations object from being passed when there is
        # no consumer generation, so this is safe to do.
        util.ensure_consumer(context, consumer_uuid, data.get('project_id'),
                             data.get('user_id'),
                             data.get('consumer_generation'), want_version)
        allocations = rp_obj.AllocationList.get_all_by_consumer_id(
            context, consumer_uuid)
        for allocation in allocations:
            allocation.used = 0
            allocation_objects.append(allocation)
    else:
        for resource_provider_uuid, allocation in allocation_data.items():
            new_allocations = _new_allocations(context, resource_provider_uuid,
                                               consumer_uuid,
                                               allocation['resources'],
                                               data.get('project_id'),
                                               data.get('user_id'),
                                               data.get('consumer_generation'),
                                               want_version)
            allocation_objects.extend(new_allocations)

    allocations = rp_obj.AllocationList(context, objects=allocation_objects)

    try:
        allocations.create_all()
        LOG.debug("Successfully wrote allocations %s", allocations)
    # InvalidInventory is a parent for several exceptions that
    # indicate either that Inventory is not present, or that
    # capacity limits have been exceeded.
    except exception.NotFound as exc:
        raise webob.exc.HTTPBadRequest(
            _("Unable to allocate inventory for consumer "
              "%(consumer_uuid)s: %(error)s") % {
                  'consumer_uuid': consumer_uuid,
                  'error': exc
              })
    except exception.InvalidInventory as exc:
        raise webob.exc.HTTPConflict(
            _('Unable to allocate inventory: %(error)s') % {'error': exc})
    except exception.ConcurrentUpdateDetected as exc:
        raise webob.exc.HTTPConflict(
            _('Inventory and/or allocations changed while attempting to '
              'allocate: %(error)s') % {'error': exc},
            comment=errors.CONCURRENT_UPDATE)

    req.response.status = 204
    req.response.content_type = None
    return req.response
Exemple #20
0
def set_allocations(req):
    context = req.environ['placement.context']
    context.can(policies.ALLOC_MANAGE)
    want_version = req.environ[microversion.MICROVERSION_ENVIRON]
    want_schema = schema.POST_ALLOCATIONS_V1_13
    if want_version.matches((1, 28)):
        want_schema = schema.POST_ALLOCATIONS_V1_28
    data = util.extract_json(req.body, want_schema)

    # First, ensure that all consumers referenced in the payload actually
    # exist. And if not, create them. Keep a record of auto-created consumers
    # so we can clean them up if the end allocation create_all() fails.
    consumers = {}  # dict of Consumer objects, keyed by consumer UUID
    new_consumers_created = []
    for consumer_uuid in data:
        project_id = data[consumer_uuid]['project_id']
        user_id = data[consumer_uuid]['user_id']
        consumer_generation = data[consumer_uuid].get('consumer_generation')
        try:
            consumer, new_consumer_created = util.ensure_consumer(
                context, consumer_uuid, project_id, user_id,
                consumer_generation, want_version)
            if new_consumer_created:
                new_consumers_created.append(consumer)
            consumers[consumer_uuid] = consumer
        except Exception:
            # If any errors (for instance, a consumer generation conflict)
            # occur when ensuring consumer records above, make sure we delete
            # any auto-created consumers.
            _delete_consumers(new_consumers_created)
            raise

    # Create a sequence of allocation objects to be used in one
    # AllocationList.create_all() call, which will mean all the changes
    # happen within a single transaction and with resource provider
    # and consumer generations (if applicable) check all in one go.
    allocations = create_allocation_list(context, data, consumers)

    def _create_allocations(alloc_list):
        try:
            alloc_list.create_all()
            LOG.debug("Successfully wrote allocations %s", alloc_list)
        except Exception:
            _delete_consumers(new_consumers_created)
            raise

    try:
        _create_allocations(allocations)
    except exception.NotFound as exc:
        raise webob.exc.HTTPBadRequest(
            _("Unable to allocate inventory %(error)s") % {'error': exc})
    except exception.InvalidInventory as exc:
        # InvalidInventory is a parent for several exceptions that
        # indicate either that Inventory is not present, or that
        # capacity limits have been exceeded.
        raise webob.exc.HTTPConflict(
            _('Unable to allocate inventory: %(error)s') % {'error': exc})
    except exception.ConcurrentUpdateDetected as exc:
        raise webob.exc.HTTPConflict(
            _('Inventory and/or allocations changed while attempting to '
              'allocate: %(error)s') % {'error': exc},
              comment=errors.CONCURRENT_UPDATE)

    req.response.status = 204
    req.response.content_type = None
    return req.response