Example #1
0
    def test_consumer_create_exists_different_consumer_type_supplied(self):
        """Tests that we update a consumer's type ID if the one supplied by a
        racing request is different than the one in the existing (recently
        created) record.
        """
        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
        # Request A recently created consumer has type ID = 1
        consumer = consumer_obj.Consumer(
            self.ctx, id=1, project=proj, user=user, generation=1,
            consumer_type_id=1, uuid=uuidsentinel.consumer)
        self.mock_consumer_get.return_value = consumer
        # Request B will encounter ConsumerExists as Request A just created it
        self.mock_consumer_create.side_effect = (
            exception.ConsumerExists(uuid=uuidsentinel.consumer))

        consumer_gen = 1
        consumer, created_new_consumer, request_attr = util.ensure_consumer(
            self.ctx, self.consumer_id, self.project_id, self.user_id,
            consumer_gen, 'TYPE', self.cons_type_req_version)
        util.update_consumers([consumer], {consumer.uuid: request_attr})
        # Expect 1 call to update() to update to the supplied consumer type ID
        self.mock_consumer_update.assert_called_once_with()
        # Consumer should have the new consumer type from the cache
        self.assertEqual(
            self.ctx.ct_cache.id_from_string.return_value,
            consumer.consumer_type_id)
Example #2
0
    def test_existing_consumer_different_consumer_type_supplied(self):
        """Tests that we update a consumer's type ID if the one supplied by the
        user is different than the one in the existing record.
        """
        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 currently has type ID = 1
        consumer = consumer_obj.Consumer(
            self.ctx, id=1, project=proj, user=user, generation=1,
            consumer_type_id=1)
        self.mock_consumer_get.return_value = consumer

        consumer_gen = 1
        consumer, created_new_consumer, request_attr = util.ensure_consumer(
            self.ctx, self.consumer_id, self.project_id, self.user_id,
            consumer_gen, 'TYPE', self.cons_type_req_version)
        util.update_consumers([consumer], {consumer.uuid: request_attr})
        # Expect 1 call to update() to update to the supplied consumer type ID
        self.mock_consumer_update.assert_called_once_with()
        # Consumer should have the new consumer type from the cache
        self.assertEqual(
            self.ctx.ct_cache.id_from_string.return_value,
            consumer.consumer_type_id)
Example #3
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, uuid=self.project_id)
        self.mock_project_get.return_value = proj
        user = user_obj.User(self.ctx, uuid=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()
Example #4
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()
Example #5
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()
Example #6
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 UUID.
        """
        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, self.conf.placement.incomplete_consumer_project_id)
        self.mock_user_get.assert_called_once_with(
            self.ctx, self.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()
Example #7
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, 'TYPE', self.after_version)

        self.mock_project_create.assert_not_called()
        self.mock_user_create.assert_not_called()
        self.mock_consumer_create.assert_not_called()
Example #8
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 3-tuple of (a dict of all consumer objects (by consumer uuid),
                           a list of those consumer objects which are new,
                           a dict of RequestAttr objects (by consumer_uuid))
    """
    # 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 = []
    # Save requested attributes in order to do an update later in the same
    # database transaction as AllocationList.replace_all() so that rollbacks
    # can happen properly. Consumer table updates are guarded by the
    # generation, so we can't necessarily save all of the original attribute
    # values and write them back into the table in the event of an exception.
    # If the generation doesn't match, Consumer.update() is a no-op.
    requested_attrs = {}
    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')
        consumer_type = data[consumer_uuid].get('consumer_type')
        try:
            consumer, new_consumer_created, request_attr = (
                data_util.ensure_consumer(context, consumer_uuid, project_id,
                                          user_id, consumer_generation,
                                          consumer_type, want_version))
            if new_consumer_created:
                new_consumers_created.append(consumer)
            consumers[consumer_uuid] = consumer
            requested_attrs[consumer_uuid] = request_attr
        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, requested_attrs
Example #9
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 = data_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
Example #10
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.
        data_util.ensure_consumer(
            context, consumer_uuid, data.get('project_id'),
            data.get('user_id'), data.get('consumer_generation'), want_version)
        allocations = alloc_obj.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 = data_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)

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

    try:
        _create_allocations(allocation_objects)
    # 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