Merge "placement: remove existing allocs when set allocs"
This commit is contained in:
commit
2decfc77d8
|
@ -1506,17 +1506,13 @@ class Allocation(_HasAResourceProvider):
|
||||||
self._destroy(self._context, self.id)
|
self._destroy(self._context, self.id)
|
||||||
|
|
||||||
|
|
||||||
def _delete_current_allocs(conn, allocs):
|
def _delete_current_allocs(conn, consumer_id):
|
||||||
"""Deletes any existing allocations that correspond to the allocations to
|
"""Deletes any existing allocations that correspond to the allocations to
|
||||||
be written. This is wrapped in a transaction, so if the write subsequently
|
be written. This is wrapped in a transaction, so if the write subsequently
|
||||||
fails, the deletion will also be rolled back.
|
fails, the deletion will also be rolled back.
|
||||||
"""
|
"""
|
||||||
for alloc in allocs:
|
|
||||||
rp_id = alloc.resource_provider.id
|
|
||||||
consumer_id = alloc.consumer_id
|
|
||||||
del_sql = _ALLOC_TBL.delete().where(
|
del_sql = _ALLOC_TBL.delete().where(
|
||||||
sa.and_(_ALLOC_TBL.c.resource_provider_id == rp_id,
|
_ALLOC_TBL.c.consumer_id == consumer_id)
|
||||||
_ALLOC_TBL.c.consumer_id == consumer_id))
|
|
||||||
conn.execute(del_sql)
|
conn.execute(del_sql)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1831,9 +1827,10 @@ class AllocationList(base.ObjectListBase, base.NovaObject):
|
||||||
# against concurrent updates.
|
# against concurrent updates.
|
||||||
with conn.begin():
|
with conn.begin():
|
||||||
# First delete any existing allocations for that rp/consumer combo.
|
# First delete any existing allocations for that rp/consumer combo.
|
||||||
_delete_current_allocs(conn, allocs)
|
consumer_id = allocs[0].consumer_id
|
||||||
|
_delete_current_allocs(conn, consumer_id)
|
||||||
before_gens = _check_capacity_exceeded(conn, allocs)
|
before_gens = _check_capacity_exceeded(conn, allocs)
|
||||||
self._ensure_consumer_project_user(conn, allocs[0].consumer_id)
|
self._ensure_consumer_project_user(conn, consumer_id)
|
||||||
# Now add the allocations that were passed in.
|
# Now add the allocations that were passed in.
|
||||||
for alloc in allocs:
|
for alloc in allocs:
|
||||||
rp = alloc.resource_provider
|
rp = alloc.resource_provider
|
||||||
|
|
|
@ -858,6 +858,140 @@ class TestAllocation(ResourceProviderBaseCase):
|
||||||
|
|
||||||
self.assertEqual(0, len(allocations))
|
self.assertEqual(0, len(allocations))
|
||||||
|
|
||||||
|
def test_multi_provider_allocation(self):
|
||||||
|
"""Tests that an allocation that includes more than one resource
|
||||||
|
provider can be created, listed and deleted properly.
|
||||||
|
|
||||||
|
Bug #1707669 highlighted a situation that arose when attempting to
|
||||||
|
remove part of an allocation for a source host during a resize
|
||||||
|
operation where the exiting allocation was not being properly
|
||||||
|
deleted.
|
||||||
|
"""
|
||||||
|
cn_source = objects.ResourceProvider(
|
||||||
|
context=self.ctx,
|
||||||
|
uuid=uuidsentinel.cn_source,
|
||||||
|
name=uuidsentinel.cn_source,
|
||||||
|
)
|
||||||
|
cn_source.create()
|
||||||
|
|
||||||
|
cn_dest = objects.ResourceProvider(
|
||||||
|
context=self.ctx,
|
||||||
|
uuid=uuidsentinel.cn_dest,
|
||||||
|
name=uuidsentinel.cn_dest,
|
||||||
|
)
|
||||||
|
cn_dest.create()
|
||||||
|
|
||||||
|
# Add same inventory to both source and destination host
|
||||||
|
for cn in (cn_source, cn_dest):
|
||||||
|
cpu_inv = objects.Inventory(
|
||||||
|
context=self.ctx,
|
||||||
|
resource_provider=cn,
|
||||||
|
resource_class=fields.ResourceClass.VCPU,
|
||||||
|
total=24,
|
||||||
|
reserved=0,
|
||||||
|
min_unit=1,
|
||||||
|
max_unit=24,
|
||||||
|
step_size=1,
|
||||||
|
allocation_ratio=16.0)
|
||||||
|
ram_inv = objects.Inventory(
|
||||||
|
context=self.ctx,
|
||||||
|
resource_provider=cn,
|
||||||
|
resource_class=fields.ResourceClass.MEMORY_MB,
|
||||||
|
total=1024,
|
||||||
|
reserved=0,
|
||||||
|
min_unit=64,
|
||||||
|
max_unit=1024,
|
||||||
|
step_size=64,
|
||||||
|
allocation_ratio=1.5)
|
||||||
|
inv_list = objects.InventoryList(context=self.ctx,
|
||||||
|
objects=[cpu_inv, ram_inv])
|
||||||
|
cn.set_inventory(inv_list)
|
||||||
|
|
||||||
|
# Now create an allocation that represents a move operation where the
|
||||||
|
# scheduler has selected cn_dest as the target host and created a
|
||||||
|
# "doubled-up" allocation for the duration of the move operation
|
||||||
|
alloc_list = objects.AllocationList(context=self.ctx,
|
||||||
|
objects=[
|
||||||
|
objects.Allocation(
|
||||||
|
context=self.ctx,
|
||||||
|
consumer_id=uuidsentinel.instance,
|
||||||
|
resource_provider=cn_source,
|
||||||
|
resource_class=fields.ResourceClass.VCPU,
|
||||||
|
used=1),
|
||||||
|
objects.Allocation(
|
||||||
|
context=self.ctx,
|
||||||
|
consumer_id=uuidsentinel.instance,
|
||||||
|
resource_provider=cn_source,
|
||||||
|
resource_class=fields.ResourceClass.MEMORY_MB,
|
||||||
|
used=256),
|
||||||
|
objects.Allocation(
|
||||||
|
context=self.ctx,
|
||||||
|
consumer_id=uuidsentinel.instance,
|
||||||
|
resource_provider=cn_dest,
|
||||||
|
resource_class=fields.ResourceClass.VCPU,
|
||||||
|
used=1),
|
||||||
|
objects.Allocation(
|
||||||
|
context=self.ctx,
|
||||||
|
consumer_id=uuidsentinel.instance,
|
||||||
|
resource_provider=cn_dest,
|
||||||
|
resource_class=fields.ResourceClass.MEMORY_MB,
|
||||||
|
used=256),
|
||||||
|
])
|
||||||
|
alloc_list.create_all()
|
||||||
|
|
||||||
|
src_allocs = objects.AllocationList.get_all_by_resource_provider_uuid(
|
||||||
|
self.ctx, cn_source.uuid)
|
||||||
|
|
||||||
|
self.assertEqual(2, len(src_allocs))
|
||||||
|
|
||||||
|
dest_allocs = objects.AllocationList.get_all_by_resource_provider_uuid(
|
||||||
|
self.ctx, cn_dest.uuid)
|
||||||
|
|
||||||
|
self.assertEqual(2, len(dest_allocs))
|
||||||
|
|
||||||
|
consumer_allocs = objects.AllocationList.get_all_by_consumer_id(
|
||||||
|
self.ctx, uuidsentinel.instance)
|
||||||
|
|
||||||
|
self.assertEqual(4, len(consumer_allocs))
|
||||||
|
|
||||||
|
# Validate that when we create an allocation for a consumer that we
|
||||||
|
# delete any existing allocation and replace it with what the new.
|
||||||
|
# Here, we're emulating the step that occurs on confirm_resize() where
|
||||||
|
# the source host pulls the existing allocation for the instance and
|
||||||
|
# removes any resources that refer to itself and saves the allocation
|
||||||
|
# back to placement
|
||||||
|
new_alloc_list = objects.AllocationList(context=self.ctx,
|
||||||
|
objects=[
|
||||||
|
objects.Allocation(
|
||||||
|
context=self.ctx,
|
||||||
|
consumer_id=uuidsentinel.instance,
|
||||||
|
resource_provider=cn_dest,
|
||||||
|
resource_class=fields.ResourceClass.VCPU,
|
||||||
|
used=1),
|
||||||
|
objects.Allocation(
|
||||||
|
context=self.ctx,
|
||||||
|
consumer_id=uuidsentinel.instance,
|
||||||
|
resource_provider=cn_dest,
|
||||||
|
resource_class=fields.ResourceClass.MEMORY_MB,
|
||||||
|
used=256),
|
||||||
|
])
|
||||||
|
new_alloc_list.create_all()
|
||||||
|
|
||||||
|
src_allocs = objects.AllocationList.get_all_by_resource_provider_uuid(
|
||||||
|
self.ctx, cn_source.uuid)
|
||||||
|
|
||||||
|
self.assertEqual(0, len(src_allocs))
|
||||||
|
|
||||||
|
dest_allocs = objects.AllocationList.get_all_by_resource_provider_uuid(
|
||||||
|
self.ctx, cn_dest.uuid)
|
||||||
|
|
||||||
|
self.assertEqual(2, len(dest_allocs))
|
||||||
|
|
||||||
|
consumer_allocs = objects.AllocationList.get_all_by_consumer_id(
|
||||||
|
self.ctx, uuidsentinel.instance)
|
||||||
|
|
||||||
|
self.assertEqual(2, len(consumer_allocs))
|
||||||
|
|
||||||
def test_destroy(self):
|
def test_destroy(self):
|
||||||
rp, allocation = self._make_allocation()
|
rp, allocation = self._make_allocation()
|
||||||
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
|
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
|
||||||
|
@ -892,67 +1026,6 @@ class TestAllocation(ResourceProviderBaseCase):
|
||||||
self.assertEqual(allocation.resource_provider.id,
|
self.assertEqual(allocation.resource_provider.id,
|
||||||
allocations[0].resource_provider.id)
|
allocations[0].resource_provider.id)
|
||||||
|
|
||||||
def test_get_all_multiple_providers(self):
|
|
||||||
# This confirms that the join with resource provider is
|
|
||||||
# behaving.
|
|
||||||
rp1, allocation1 = self._make_allocation(uuidsentinel.rp1)
|
|
||||||
rp2, allocation2 = self._make_allocation(uuidsentinel.rp2)
|
|
||||||
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
|
|
||||||
self.ctx, rp1.uuid)
|
|
||||||
self.assertEqual(1, len(allocations))
|
|
||||||
self.assertEqual(rp1.id, allocations[0].resource_provider.id)
|
|
||||||
self.assertEqual(allocation1.resource_provider.id,
|
|
||||||
allocations[0].resource_provider.id)
|
|
||||||
|
|
||||||
# add more allocations for the first resource provider
|
|
||||||
# of the same class
|
|
||||||
alloc3 = objects.Allocation(
|
|
||||||
self.ctx,
|
|
||||||
consumer_id=uuidsentinel.consumer1,
|
|
||||||
resource_class=fields.ResourceClass.DISK_GB,
|
|
||||||
resource_provider=rp1,
|
|
||||||
used=2,
|
|
||||||
)
|
|
||||||
alloc_list = objects.AllocationList(self.ctx, objects=[alloc3])
|
|
||||||
alloc_list.create_all()
|
|
||||||
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
|
|
||||||
self.ctx, rp1.uuid)
|
|
||||||
self.assertEqual(2, len(allocations))
|
|
||||||
|
|
||||||
# add more allocations for the first resource provider
|
|
||||||
# of a different class
|
|
||||||
# First we need to add sufficient inventory
|
|
||||||
res_cls = fields.ResourceClass.IPV4_ADDRESS
|
|
||||||
inv = objects.Inventory(self.ctx, resource_provider=rp1,
|
|
||||||
resource_class=res_cls, total=256, max_unit=10)
|
|
||||||
inv.obj_set_defaults()
|
|
||||||
inv.create()
|
|
||||||
# Now allocate 4 of them.
|
|
||||||
alloc4 = objects.Allocation(
|
|
||||||
self.ctx,
|
|
||||||
consumer_id=uuidsentinel.consumer2,
|
|
||||||
resource_class=res_cls,
|
|
||||||
resource_provider=rp1,
|
|
||||||
used=4,
|
|
||||||
)
|
|
||||||
alloc_list = objects.AllocationList(self.ctx, objects=[alloc4])
|
|
||||||
alloc_list.create_all()
|
|
||||||
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
|
|
||||||
self.ctx, rp1.uuid)
|
|
||||||
self.assertEqual(3, len(allocations))
|
|
||||||
self.assertEqual(rp1.uuid, allocations[0].resource_provider.uuid)
|
|
||||||
|
|
||||||
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
|
|
||||||
self.ctx, rp2.uuid)
|
|
||||||
self.assertEqual(1, len(allocations))
|
|
||||||
self.assertEqual(rp2.uuid, allocations[0].resource_provider.uuid)
|
|
||||||
self.assertIn(fields.ResourceClass.DISK_GB,
|
|
||||||
[allocation.resource_class
|
|
||||||
for allocation in allocations])
|
|
||||||
self.assertNotIn(fields.ResourceClass.IPV4_ADDRESS,
|
|
||||||
[allocation.resource_class
|
|
||||||
for allocation in allocations])
|
|
||||||
|
|
||||||
|
|
||||||
class TestAllocationListCreateDelete(ResourceProviderBaseCase):
|
class TestAllocationListCreateDelete(ResourceProviderBaseCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue