Merge "[placement] Allow _set_allocations to delete allocations"

This commit is contained in:
Zuul 2017-10-27 20:52:59 +00:00 committed by Gerrit Code Review
commit 25ecb01963
2 changed files with 155 additions and 8 deletions

View File

@ -1666,7 +1666,7 @@ def _check_capacity_exceeded(conn, allocs):
resource_provider=rp_uuid)
if rp_uuid not in res_providers:
res_providers[rp_uuid] = alloc.resource_provider
return list(res_providers.values())
return res_providers
def _ensure_lookup_table_entry(conn, tbl, external_id):
@ -1799,12 +1799,31 @@ class AllocationList(base.ObjectListBase, base.NovaObject):
for alloc in allocs:
consumer_id = alloc.consumer_id
_delete_allocations_for_consumer(context, consumer_id)
# If there are any allocations with string resource class names
# that don't exist this will raise a ResourceClassNotFound
# exception.
before_gens = _check_capacity_exceeded(conn, allocs)
# Don't check capacity when alloc.used is zero. Zero is not a
# valid amount when making an allocation (the minimum consumption
# of a resource is one) but is used in this method to indicate a
# need for removal. Providing 0 is controlled at the HTTP API layer
# where PUT /allocations does not allow empty allocations. When
# POST /allocations is implemented it will for the special case of
# atomically setting and removing different allocations in the same
# request. _check_capacity_exceeded will raise a
# ResourceClassNotFound # if any allocation is using a resource
# class that does not exist.
visited_rps = _check_capacity_exceeded(conn,
[alloc for alloc in
allocs if alloc.used > 0])
seen_consumers = set()
for alloc in allocs:
# If alloc.used is set to zero that is a signal that we don't
# want to (re-)create any allocations for this resource class.
# _delete_current_allocs has already wiped out allocations so
# all that's being done here is adding the resource provider
# to visited_rps so its generation will be checked at the end
# of the transaction.
if alloc.used == 0:
rp = alloc.resource_provider
visited_rps[rp.uuid] = rp
continue
consumer_id = alloc.consumer_id
# Only set consumer <-> project/user association if we
# haven't set it already.
@ -1826,7 +1845,7 @@ class AllocationList(base.ObjectListBase, base.NovaObject):
# this will raise a ConcurrentUpdateDetected which can be caught
# by the caller to choose to try again. It will also rollback the
# transaction so that these changes always happen atomically.
for rp in before_gens:
for rp in visited_rps.values():
rp.generation = _increment_provider_generation(conn, rp)
@classmethod

View File

@ -1173,8 +1173,8 @@ class TestAllocationListCreateDelete(ResourceProviderBaseCase):
def _make_rp_and_inventory(self, **kwargs):
# Create one resource provider and set some inventory
rp_name = uuidsentinel.rp_name
rp_uuid = uuidsentinel.rp_uuid
rp_name = kwargs.get('rp_name') or uuidsentinel.rp_name
rp_uuid = kwargs.get('rp_uuid') or uuidsentinel.rp_uuid
rp = rp_obj.ResourceProvider(
self.ctx, name=rp_name, uuid=rp_uuid)
rp.create()
@ -1314,6 +1314,134 @@ class TestAllocationListCreateDelete(ResourceProviderBaseCase):
self.assertEqual(1, len(usage_list))
self.assertEqual(200, usage_list[0].usage)
def test_create_and_clear(self):
"""Test that a used of 0 in an allocation wipes allocations."""
consumer_uuid = uuidsentinel.consumer
rp_class = fields.ResourceClass.DISK_GB
target_rp = self._make_rp_and_inventory(resource_class=rp_class,
max_unit=500)
# Create two allocations with values and confirm the resulting
# usage is as expected.
allocation1 = rp_obj.Allocation(resource_provider=target_rp,
consumer_id=consumer_uuid,
resource_class=rp_class,
project_id=self.ctx.project_id,
user_id=self.ctx.user_id,
used=100)
allocation2 = rp_obj.Allocation(resource_provider=target_rp,
consumer_id=consumer_uuid,
resource_class=rp_class,
project_id=self.ctx.project_id,
user_id=self.ctx.user_id,
used=200)
allocation_list = rp_obj.AllocationList(
self.ctx,
objects=[allocation1, allocation2],
)
allocation_list.create_all()
allocations = rp_obj.AllocationList.get_all_by_consumer_id(
self.ctx, consumer_uuid)
self.assertEqual(2, len(allocations))
usage = sum(alloc.used for alloc in allocations)
self.assertEqual(300, usage)
# Create two allocations, one with 0 used, to confirm the
# resulting usage is only of one.
allocation1 = rp_obj.Allocation(resource_provider=target_rp,
consumer_id=consumer_uuid,
resource_class=rp_class,
project_id=self.ctx.project_id,
user_id=self.ctx.user_id,
used=0)
allocation2 = rp_obj.Allocation(resource_provider=target_rp,
consumer_id=consumer_uuid,
resource_class=rp_class,
project_id=self.ctx.project_id,
user_id=self.ctx.user_id,
used=200)
allocation_list = rp_obj.AllocationList(
self.ctx,
objects=[allocation1, allocation2],
)
allocation_list.create_all()
allocations = rp_obj.AllocationList.get_all_by_consumer_id(
self.ctx, consumer_uuid)
self.assertEqual(1, len(allocations))
usage = allocations[0].used
self.assertEqual(200, usage)
# add a source rp and a migration consumer
migration_uuid = uuidsentinel.migration
source_rp = self._make_rp_and_inventory(
rp_name=uuidsentinel.source_name, rp_uuid=uuidsentinel.source_uuid,
resource_class=rp_class, max_unit=500)
# Create two allocations, one as the consumer, one as the
# migration.
allocation1 = rp_obj.Allocation(resource_provider=target_rp,
consumer_id=consumer_uuid,
resource_class=rp_class,
project_id=self.ctx.project_id,
user_id=self.ctx.user_id,
used=200)
allocation2 = rp_obj.Allocation(resource_provider=source_rp,
consumer_id=migration_uuid,
resource_class=rp_class,
project_id=self.ctx.project_id,
user_id=self.ctx.user_id,
used=200)
allocation_list = rp_obj.AllocationList(
self.ctx,
objects=[allocation1, allocation2],
)
allocation_list.create_all()
# Check primary consumer allocations.
allocations = rp_obj.AllocationList.get_all_by_consumer_id(
self.ctx, consumer_uuid)
self.assertEqual(1, len(allocations))
usage = allocations[0].used
self.assertEqual(200, usage)
# Check migration allocations.
allocations = rp_obj.AllocationList.get_all_by_consumer_id(
self.ctx, migration_uuid)
self.assertEqual(1, len(allocations))
usage = allocations[0].used
self.assertEqual(200, usage)
# Clear the migration and confirm the target.
allocation1 = rp_obj.Allocation(resource_provider=target_rp,
consumer_id=consumer_uuid,
resource_class=rp_class,
project_id=self.ctx.project_id,
user_id=self.ctx.user_id,
used=200)
allocation2 = rp_obj.Allocation(resource_provider=source_rp,
consumer_id=migration_uuid,
resource_class=rp_class,
project_id=self.ctx.project_id,
user_id=self.ctx.user_id,
used=0)
allocation_list = rp_obj.AllocationList(
self.ctx,
objects=[allocation1, allocation2],
)
allocation_list.create_all()
allocations = rp_obj.AllocationList.get_all_by_consumer_id(
self.ctx, consumer_uuid)
self.assertEqual(1, len(allocations))
usage = allocations[0].used
self.assertEqual(200, usage)
allocations = rp_obj.AllocationList.get_all_by_consumer_id(
self.ctx, migration_uuid)
self.assertEqual(0, len(allocations))
class UsageListTestCase(ResourceProviderBaseCase):