Merge "[placement] Enforce min_unit, max_unit and step_size"
This commit is contained in:
commit
17fbe727be
@ -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.")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user