Merge "Placement: allow to set reserved value equal to total for inventory"

This commit is contained in:
Zuul 2018-05-19 02:53:32 +00:00 committed by Gerrit Code Review
commit b979bcd7e4
9 changed files with 134 additions and 42 deletions

View File

@ -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):

View File

@ -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:

View File

@ -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.
]

View File

@ -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_(

View File

@ -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.

View File

@ -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'],

View File

@ -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

View File

@ -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: /

View File

@ -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.