Placement: allow to set reserved value equal to total for inventory
This is needed for ironic use case, when during cleaning, resources are reserved by ironic itself. Cyborg will also benefit from this during FPGA programming. blueprint: allow-reserved-equal-total-inventory Change-Id: I037d9b8c1bb554c3437434cc9a57ddb630dd62f0
This commit is contained in:
parent
e71c6ddf05
commit
e6dea14d93
@ -99,6 +99,12 @@ class InvalidInventoryCapacity(InvalidInventory):
|
||||
"The reserved value is greater than or equal to total.")
|
||||
|
||||
|
||||
class InvalidInventoryCapacityReservedCanBeTotal(InvalidInventoryCapacity):
|
||||
msg_fmt = _("Invalid inventory for '%(resource_class)s' on "
|
||||
"resource provider '%(resource_provider)s'. "
|
||||
"The reserved value is greater than total.")
|
||||
|
||||
|
||||
# An exception with this name is used on both sides of the placement/
|
||||
# nova interaction.
|
||||
class InventoryInUse(InvalidInventory):
|
||||
|
@ -12,6 +12,7 @@
|
||||
"""Inventory handlers for Placement API."""
|
||||
|
||||
import copy
|
||||
import operator
|
||||
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_serialization import jsonutils
|
||||
@ -147,6 +148,31 @@ def _serialize_inventories(inventories, generation):
|
||||
'inventories': inventories_dict}, last_modified)
|
||||
|
||||
|
||||
def _validate_inventory_capacity(version, inventories):
|
||||
"""Validate inventory capacity.
|
||||
|
||||
:param version: request microversion.
|
||||
:param inventories: Inventory or InventoryList to validate capacities of.
|
||||
:raises: exception.InvalidInventoryCapacityReservedCanBeTotal if request
|
||||
microversion is 1.26 or higher and any inventory has capacity < 0.
|
||||
:raises: exception.InvalidInventoryCapacity if request
|
||||
microversion is lower than 1.26 and any inventory has capacity <= 0.
|
||||
"""
|
||||
if not version.matches((1, 26)):
|
||||
op = operator.le
|
||||
exc_class = exception.InvalidInventoryCapacity
|
||||
else:
|
||||
op = operator.lt
|
||||
exc_class = exception.InvalidInventoryCapacityReservedCanBeTotal
|
||||
if isinstance(inventories, rp_obj.Inventory):
|
||||
inventories = rp_obj.InventoryList(objects=[inventories])
|
||||
for inventory in inventories:
|
||||
if op(inventory.capacity, 0):
|
||||
raise exc_class(
|
||||
resource_class=inventory.resource_class,
|
||||
resource_provider=inventory.resource_provider.uuid)
|
||||
|
||||
|
||||
@wsgi_wrapper.PlacementWsgify
|
||||
@util.require_content('application/json')
|
||||
def create_inventory(req):
|
||||
@ -168,6 +194,8 @@ def create_inventory(req):
|
||||
**data)
|
||||
|
||||
try:
|
||||
_validate_inventory_capacity(
|
||||
req.environ[microversion.MICROVERSION_ENVIRON], inventory)
|
||||
resource_provider.add_inventory(inventory)
|
||||
except (exception.ConcurrentUpdateDetected,
|
||||
db_exc.DBDuplicateEntry) as exc:
|
||||
@ -305,6 +333,8 @@ def set_inventories(req):
|
||||
inventories = rp_obj.InventoryList(objects=inv_list)
|
||||
|
||||
try:
|
||||
_validate_inventory_capacity(
|
||||
req.environ[microversion.MICROVERSION_ENVIRON], inventories)
|
||||
resource_provider.set_inventory(inventories)
|
||||
except exception.ResourceClassNotFound as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
@ -401,6 +431,8 @@ def update_inventory(req):
|
||||
**data)
|
||||
|
||||
try:
|
||||
_validate_inventory_capacity(
|
||||
req.environ[microversion.MICROVERSION_ENVIRON], inventory)
|
||||
resource_provider.update_inventory(inventory)
|
||||
except (exception.ConcurrentUpdateDetected,
|
||||
db_exc.DBDuplicateEntry) as exc:
|
||||
|
@ -68,6 +68,8 @@ VERSIONS = [
|
||||
# GET /resource_providers
|
||||
'1.25', # Adds support for granular resource requests via numbered
|
||||
# querystring groups in GET /allocation_candidates
|
||||
'1.26', # Add ability to specify inventory with reserved value equal to
|
||||
# total.
|
||||
]
|
||||
|
||||
|
||||
|
@ -201,10 +201,6 @@ def _add_inventory_to_provider(ctx, rp, inv_list, to_add):
|
||||
for rc_id in to_add:
|
||||
rc_str = _RC_CACHE.string_from_id(rc_id)
|
||||
inv_record = inv_list.find(rc_str)
|
||||
if inv_record.capacity <= 0:
|
||||
raise exception.InvalidInventoryCapacity(
|
||||
resource_class=rc_str,
|
||||
resource_provider=rp.uuid)
|
||||
ins_stmt = _INV_TBL.insert().values(
|
||||
resource_provider_id=rp.id,
|
||||
resource_class_id=rc_id,
|
||||
@ -232,10 +228,6 @@ def _update_inventory_for_provider(ctx, rp, inv_list, to_update):
|
||||
for rc_id in to_update:
|
||||
rc_str = _RC_CACHE.string_from_id(rc_id)
|
||||
inv_record = inv_list.find(rc_str)
|
||||
if inv_record.capacity <= 0:
|
||||
raise exception.InvalidInventoryCapacity(
|
||||
resource_class=rc_str,
|
||||
resource_provider=rp.uuid)
|
||||
allocation_query = sa.select(
|
||||
[func.sum(_ALLOC_TBL.c.used).label('usage')]).\
|
||||
where(sa.and_(
|
||||
|
@ -323,3 +323,9 @@ The semantic of the (unnumbered) ``resources``, ``required``, and ``member_of``
|
||||
query parameters is unchanged: the resources, traits, and aggregate
|
||||
associations specified thereby may be satisfied by any provider in the same
|
||||
non-sharing tree or associated via the specified aggregate(s).
|
||||
|
||||
1.26 Allow inventories to have reserved value equal to total
|
||||
------------------------------------------------------------
|
||||
|
||||
Starting with this version, it is allowed to set the reserved value of the
|
||||
resource provider inventory to be equal to total.
|
||||
|
@ -584,22 +584,6 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase):
|
||||
self.assertEqual(1, len(new_inv_list))
|
||||
self.assertEqual(2048, new_inv_list[0].total)
|
||||
|
||||
# fail when inventory bad
|
||||
disk_inv = rp_obj.Inventory(
|
||||
resource_provider=rp,
|
||||
resource_class=fields.ResourceClass.DISK_GB,
|
||||
total=2048,
|
||||
reserved=2048)
|
||||
disk_inv.obj_set_defaults()
|
||||
error = self.assertRaises(exception.InvalidInventoryCapacity,
|
||||
rp.update_inventory, disk_inv)
|
||||
self.assertIn("Invalid inventory for '%s'"
|
||||
% fields.ResourceClass.DISK_GB, str(error))
|
||||
self.assertIn("on resource provider '%s'." % rp.uuid, str(error))
|
||||
|
||||
# generation has not bumped
|
||||
self.assertEqual(saved_generation, rp.generation)
|
||||
|
||||
# delete inventory
|
||||
rp.delete_inventory(fields.ResourceClass.DISK_GB)
|
||||
|
||||
@ -693,17 +677,6 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase):
|
||||
mock_log.warning.assert_called_once_with(
|
||||
mock.ANY, {'uuid': rp.uuid, 'resource': 'DISK_GB'})
|
||||
|
||||
def test_add_invalid_inventory(self):
|
||||
rp = self._create_provider(uuidsentinel.rp_name)
|
||||
error = self.assertRaises(
|
||||
exception.InvalidInventoryCapacity,
|
||||
tb.add_inventory, rp, fields.ResourceClass.DISK_GB, 1024,
|
||||
reserved=2048)
|
||||
self.assertIn("Invalid inventory for '%s'"
|
||||
% fields.ResourceClass.DISK_GB, str(error))
|
||||
self.assertIn("on resource provider '%s'."
|
||||
% rp.uuid, str(error))
|
||||
|
||||
def test_add_allocation_increments_generation(self):
|
||||
rp = self._create_provider(name='foo')
|
||||
tb.add_inventory(rp, DISK_INVENTORY['resource_class'],
|
||||
|
@ -649,9 +649,84 @@ tests:
|
||||
status: 400
|
||||
response_strings:
|
||||
- Unable to update inventory
|
||||
- greater than or equal to total
|
||||
response_json_paths:
|
||||
$.errors[0].title: Bad Request
|
||||
|
||||
- name: put all inventory zero capacity old microversion
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 6
|
||||
inventories:
|
||||
IPV4_ADDRESS:
|
||||
total: 253
|
||||
reserved: 253
|
||||
status: 400
|
||||
response_strings:
|
||||
- Unable to update inventory
|
||||
- greater than or equal to total
|
||||
response_json_paths:
|
||||
$.errors[0].title: Bad Request
|
||||
|
||||
- name: put inventory with reserved equal to total
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
openstack-api-version: placement 1.26
|
||||
data:
|
||||
resource_provider_generation: 6
|
||||
inventories:
|
||||
IPV4_ADDRESS:
|
||||
total: 253
|
||||
reserved: 253
|
||||
status: 200
|
||||
|
||||
- name: put all inventory bad capacity in new microversion
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
openstack-api-version: placement 1.26
|
||||
data:
|
||||
resource_provider_generation: 7
|
||||
inventories:
|
||||
IPV4_ADDRESS:
|
||||
total: 253
|
||||
reserved: 512
|
||||
status: 400
|
||||
response_strings:
|
||||
- Unable to update inventory
|
||||
- greater than total
|
||||
response_json_paths:
|
||||
$.errors[0].title: Bad Request
|
||||
|
||||
- name: put one inventory zero capacity old microversion
|
||||
PUT: /resource_providers/$ENVIRON['RP_UUID']/inventories/IPV4_ADDRESS
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 7
|
||||
total: 253
|
||||
reserved: 253
|
||||
status: 400
|
||||
response_strings:
|
||||
- Unable to update inventory
|
||||
- greater than or equal to total
|
||||
response_json_paths:
|
||||
$.errors[0].title: Bad Request
|
||||
|
||||
- name: put one inventory with reserved equal to total new microversion
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
openstack-api-version: placement 1.26
|
||||
data:
|
||||
resource_provider_generation: 7
|
||||
total: 512
|
||||
reserved: 512
|
||||
status: 200
|
||||
|
||||
- name: delete all inventory bad generation
|
||||
PUT: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
@ -680,7 +755,7 @@ tests:
|
||||
- name: get inventories after deletions
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
response_json_paths:
|
||||
$.resource_provider_generation: 8
|
||||
$.resource_provider_generation: 10
|
||||
$.inventories: {}
|
||||
|
||||
- name: post an inventory again
|
||||
@ -699,7 +774,7 @@ tests:
|
||||
response_headers:
|
||||
location: $SCHEME://$NETLOC/resource_providers/$ENVIRON['RP_UUID']/inventories/DISK_GB
|
||||
response_json_paths:
|
||||
$.resource_provider_generation: 9
|
||||
$.resource_provider_generation: 11
|
||||
$.total: 2048
|
||||
$.reserved: 512
|
||||
|
||||
@ -709,17 +784,17 @@ tests:
|
||||
content-type: application/json
|
||||
openstack-api-version: placement 1.4
|
||||
data:
|
||||
resource_provider_generation: 9
|
||||
resource_provider_generation: 11
|
||||
inventories: {}
|
||||
response_json_paths:
|
||||
$.resource_provider_generation: 10
|
||||
$.resource_provider_generation: 12
|
||||
$.inventories: {}
|
||||
status: 200
|
||||
|
||||
- name: get generation after deletion
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
response_json_paths:
|
||||
$.resource_provider_generation: 10
|
||||
$.resource_provider_generation: 12
|
||||
$.inventories: {}
|
||||
|
||||
- name: delete inventories earlier version
|
||||
|
@ -39,13 +39,13 @@ tests:
|
||||
response_json_paths:
|
||||
$.errors[0].title: Not Acceptable
|
||||
|
||||
- name: latest microversion is 1.25
|
||||
- name: latest microversion is 1.26
|
||||
GET: /
|
||||
request_headers:
|
||||
openstack-api-version: placement latest
|
||||
response_headers:
|
||||
vary: /openstack-api-version/
|
||||
openstack-api-version: placement 1.25
|
||||
openstack-api-version: placement 1.26
|
||||
|
||||
- name: other accept header bad version
|
||||
GET: /
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Introduces new placement API version ``1.26``. Starting with this version
|
||||
it is allowed to define resource provider inventories with reserved value
|
||||
equal to total.
|
Loading…
Reference in New Issue
Block a user