Merge "[placement] Enforce min_unit, max_unit and step_size"

This commit is contained in:
Jenkins 2016-11-16 11:42:07 +00:00 committed by Gerrit Code Review
commit 17fbe727be
4 changed files with 133 additions and 11 deletions

View File

@ -2148,6 +2148,12 @@ class InvalidAllocationCapacityExceeded(InvalidInventory):
"amount would exceed the capacity.")
class InvalidAllocationConstraintsViolated(InvalidInventory):
msg_fmt = _("Unable to create allocation for '%(resource_class)s' on "
"resource provider '%(resource_provider)s'. The requested "
"amount would violate inventory constraints.")
class UnsupportedPointerModelRequested(Invalid):
msg_fmt = _("Pointer model '%(model)s' requested is not supported by "
"host.")

View File

@ -707,9 +707,14 @@ def _check_capacity_exceeded(conn, allocs):
the inventories involved having their capacity exceeded.
Raises an InvalidAllocationCapacityExceeded exception if any inventory
would be exhausted by the allocation. If no inventories would be exceeded
by the allocation, the function returns a list of `ResourceProvider`
objects that contain the generation at the time of the check.
would be exhausted by the allocation. Raises an
InvalidAllocationConstraintsViolated exception if any of the `step_size`,
`min_unit` or `max_unit` constraints in an inventory will be violated
by any one of the allocations.
If no inventories would be exceeded or violated by the allocations, the
function returns a list of `ResourceProvider` objects that contain the
generation at the time of the check.
:param conn: SQLalchemy Connection object to use
:param allocs: List of `Allocation` objects to check
@ -769,6 +774,9 @@ def _check_capacity_exceeded(conn, allocs):
_INV_TBL.c.total,
_INV_TBL.c.reserved,
_INV_TBL.c.allocation_ratio,
_INV_TBL.c.min_unit,
_INV_TBL.c.max_unit,
_INV_TBL.c.step_size,
usage.c.used,
]
@ -803,6 +811,28 @@ def _check_capacity_exceeded(conn, allocs):
usage = usage_map[key]
amount_needed = alloc.used
allocation_ratio = usage['allocation_ratio']
min_unit = usage['min_unit']
max_unit = usage['max_unit']
step_size = usage['step_size']
# check min_unit, max_unit, step_size
if (amount_needed < min_unit or amount_needed > max_unit or
amount_needed % step_size != 0):
LOG.warning(
_LW("Allocation for %(rc)s on resource provider %(rp)s "
"violates min_unit, max_unit, or step_size. "
"Requested: %(requested)s, min_unit: %(min_unit)s, "
"max_unit: %(max_unit)s, step_size: %(step_size)s"),
{'rc': alloc.resource_class,
'rp': rp_uuid,
'requested': amount_needed,
'min_unit': min_unit,
'max_unit': max_unit,
'step_size': step_size})
raise exception.InvalidAllocationConstraintsViolated(
resource_class=alloc.resource_class,
resource_provider=rp_uuid)
# usage["used"] can be returned as None
used = usage['used'] or 0
capacity = (usage['total'] - usage['reserved']) * allocation_ratio

View File

@ -265,28 +265,28 @@ tests:
request_headers:
content-type: application/json
data:
# TODO(cdent): This format is going to go out of date because
# of other changes
resource_provider_generation: 0
inventories:
VCPU:
total: 32
max_unit: 32
DISK_GB:
total: 10
max_unit: 10
- name: set inventory on rp2
PUT: /resource_providers/fcfa516a-abbe-45d1-8152-d5225d82e596/inventories
request_headers:
content-type: application/json
data:
# TODO(cdent): This format is going to go out of date because
# of other changes
resource_provider_generation: 0
inventories:
VCPU:
total: 16
max_unit: 16
DISK_GB:
total: 20
max_unit: 20
status: 200
- name: put allocations on both those providers one

View File

@ -669,6 +669,7 @@ class TestAllocationListCreateDelete(ResourceProviderBaseCase):
If this fails, we get a KeyError at create_all()
"""
max_unit = 10
consumer_uuid = uuidsentinel.consumer
consumer_uuid2 = uuidsentinel.consumer2
@ -687,12 +688,13 @@ class TestAllocationListCreateDelete(ResourceProviderBaseCase):
inv = objects.Inventory(resource_provider=rp1,
resource_class=rp1_class,
total=1024)
total=1024, max_unit=max_unit)
inv.obj_set_defaults()
inv2 = objects.Inventory(resource_provider=rp1,
resource_class=rp2_class,
total=255, reserved=2)
resource_class=rp2_class,
total=255, reserved=2,
max_unit=max_unit)
inv2.obj_set_defaults()
inv_list = objects.InventoryList(objects=[inv, inv2])
rp1.set_inventory(inv_list)
@ -728,6 +730,7 @@ class TestAllocationListCreateDelete(ResourceProviderBaseCase):
allocation_list.create_all()
def test_allocation_list_create(self):
max_unit = 10
consumer_uuid = uuidsentinel.consumer
# Create two resource providers
@ -789,7 +792,21 @@ class TestAllocationListCreateDelete(ResourceProviderBaseCase):
inv_list = objects.InventoryList(objects=[inv])
rp2.set_inventory(inv_list)
# Now the allocations will work.
# Now the allocations will still fail because max_unit 1
self.assertRaises(exception.InvalidAllocationConstraintsViolated,
allocation_list.create_all)
inv1 = objects.Inventory(resource_provider=rp1,
resource_class=rp1_class,
total=1024, max_unit=max_unit)
inv1.obj_set_defaults()
rp1.set_inventory(objects.InventoryList(objects=[inv1]))
inv2 = objects.Inventory(resource_provider=rp2,
resource_class=rp2_class,
total=255, reserved=2, max_unit=max_unit)
inv2.obj_set_defaults()
rp2.set_inventory(objects.InventoryList(objects=[inv2]))
# Now we can finally allocate.
allocation_list.create_all()
# Check that those allocations changed usage on each
@ -833,6 +850,75 @@ class TestAllocationListCreateDelete(ResourceProviderBaseCase):
self.assertEqual(0, rp1_usage[0].usage)
self.assertEqual(0, rp2_usage[0].usage)
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 = objects.ResourceProvider(
self.context, name=rp_name, uuid=rp_uuid)
rp.create()
inv = objects.Inventory(resource_provider=rp,
total=1024, allocation_ratio=1,
reserved=0, **kwargs)
inv.obj_set_defaults()
rp.set_inventory(objects.InventoryList(objects=[inv]))
return rp
def _validate_usage(self, rp, usage):
rp_usage = objects.UsageList.get_all_by_resource_provider_uuid(
self.context, rp.uuid)
self.assertEqual(usage, rp_usage[0].usage)
def _check_create_allocations(self, inventory_kwargs,
bad_used, good_used):
consumer_uuid = uuidsentinel.consumer
rp_class = fields.ResourceClass.DISK_GB
rp = self._make_rp_and_inventory(resource_class=rp_class,
**inventory_kwargs)
# allocation, bad step_size
allocation = objects.Allocation(resource_provider=rp,
consumer_id=consumer_uuid,
resource_class=rp_class,
used=bad_used)
allocation_list = objects.AllocationList(self.context,
objects=[allocation])
self.assertRaises(exception.InvalidAllocationConstraintsViolated,
allocation_list.create_all)
# correct for step size
allocation.used = good_used
allocation_list = objects.AllocationList(self.context,
objects=[allocation])
allocation_list.create_all()
# check usage
self._validate_usage(rp, allocation.used)
def test_create_all_step_size(self):
bad_used = 4
good_used = 5
inventory_kwargs = {'max_unit': 9999, 'step_size': 5}
self._check_create_allocations(inventory_kwargs,
bad_used, good_used)
def test_create_all_min_unit(self):
bad_used = 4
good_used = 5
inventory_kwargs = {'max_unit': 9999, 'min_unit': 5}
self._check_create_allocations(inventory_kwargs,
bad_used, good_used)
def test_create_all_max_unit(self):
bad_used = 5
good_used = 3
inventory_kwargs = {'max_unit': 3}
self._check_create_allocations(inventory_kwargs,
bad_used, good_used)
class UsageListTestCase(ResourceProviderBaseCase):