[placement] Add /reshaper handler for POST
/reshaper provides a way to atomically modify some allocations and inventory in a single transaction, allowing operations like migrating some inventory from a parent provider to a new child. A fair amount of code is reused from handler/inventory.py, some refactoring is in order before things get too far with that. In handler/allocation.py some code is extracted to its own methods so it can be reused from reshaper.py. This is done as microversion 1.30. A suite of gabbi tests is provided which attempt to cover various failures including schema violations, generation conflicts, and data conflicts. api-ref, release notes and rest history are updated Change-Id: I5b33ac3572bc3789878174ffc86ca42ae8035cfa Partially-Implements: blueprint reshape-provider-tree
This commit is contained in:
parent
fa66d9a730
commit
4d525b4ec1
@ -45,3 +45,4 @@ DUPLICATE_NAME = 'placement.duplicate_name'
|
||||
PROVIDER_IN_USE = 'placement.resource_provider.inuse'
|
||||
PROVIDER_CANNOT_DELETE_PARENT = (
|
||||
'placement.resource_provider.cannot_delete_parent')
|
||||
RESOURCE_PROVIDER_NOT_FOUND = 'placement.resource_provider.not_found'
|
||||
|
@ -33,6 +33,7 @@ from nova.api.openstack.placement.handlers import aggregate
|
||||
from nova.api.openstack.placement.handlers import allocation
|
||||
from nova.api.openstack.placement.handlers import allocation_candidate
|
||||
from nova.api.openstack.placement.handlers import inventory
|
||||
from nova.api.openstack.placement.handlers import reshaper
|
||||
from nova.api.openstack.placement.handlers import resource_class
|
||||
from nova.api.openstack.placement.handlers import resource_provider
|
||||
from nova.api.openstack.placement.handlers import root
|
||||
@ -126,6 +127,9 @@ ROUTE_DECLARATIONS = {
|
||||
'/usages': {
|
||||
'GET': usage.get_total_usages,
|
||||
},
|
||||
'/reshaper': {
|
||||
'POST': reshaper.reshape,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ import uuid
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import timeutils
|
||||
from oslo_utils import uuidutils
|
||||
import webob
|
||||
@ -195,6 +196,47 @@ def create_allocation_list(context, data, consumers):
|
||||
return rp_obj.AllocationList(context, objects=allocation_objects)
|
||||
|
||||
|
||||
def inspect_consumers(context, data, want_version):
|
||||
"""Look at consumer data in allocations and create consumers as needed.
|
||||
|
||||
Keep a record of the consumers that are created in case they need
|
||||
to be removed later.
|
||||
|
||||
If an exception is raised by ensure_consumer, commonly HTTPConflict but
|
||||
also anything else, the newly created consumers will be deleted and the
|
||||
exception reraised to the caller.
|
||||
|
||||
:param context: The placement context.
|
||||
:param data: A dictionary of multiple allocations by consumer uuid.
|
||||
:param want_version: the microversion matcher.
|
||||
:return: A tuple of a dict of all consumer objects (by consumer uuid)
|
||||
and a list of those consumer objects which are new.
|
||||
"""
|
||||
# First, ensure that all consumers referenced in the payload actually
|
||||
# exist. And if not, create them. Keep a record of auto-created consumers
|
||||
# so we can clean them up if the end allocation replace_all() fails.
|
||||
consumers = {} # dict of Consumer objects, keyed by consumer UUID
|
||||
new_consumers_created = []
|
||||
for consumer_uuid in data:
|
||||
project_id = data[consumer_uuid]['project_id']
|
||||
user_id = data[consumer_uuid]['user_id']
|
||||
consumer_generation = data[consumer_uuid].get('consumer_generation')
|
||||
try:
|
||||
consumer, new_consumer_created = util.ensure_consumer(
|
||||
context, consumer_uuid, project_id, user_id,
|
||||
consumer_generation, want_version)
|
||||
if new_consumer_created:
|
||||
new_consumers_created.append(consumer)
|
||||
consumers[consumer_uuid] = consumer
|
||||
except Exception:
|
||||
# If any errors (for instance, a consumer generation conflict)
|
||||
# occur when ensuring consumer records above, make sure we delete
|
||||
# any auto-created consumers.
|
||||
with excutils.save_and_reraise_exception():
|
||||
delete_consumers(new_consumers_created)
|
||||
return consumers, new_consumers_created
|
||||
|
||||
|
||||
@wsgi_wrapper.PlacementWsgify
|
||||
@util.check_accept('application/json')
|
||||
def list_for_consumer(req):
|
||||
@ -313,7 +355,7 @@ def _new_allocations(context, resource_provider, consumer, resources):
|
||||
return allocations
|
||||
|
||||
|
||||
def _delete_consumers(consumers):
|
||||
def delete_consumers(consumers):
|
||||
"""Helper function that deletes any consumer object supplied to it
|
||||
|
||||
:param consumers: iterable of Consumer objects to delete
|
||||
@ -399,7 +441,7 @@ def _set_allocations_for_consumer(req, schema):
|
||||
LOG.debug("Successfully wrote allocations %s", alloc_list)
|
||||
except Exception:
|
||||
if created_new_consumer:
|
||||
_delete_consumers([consumer])
|
||||
delete_consumers([consumer])
|
||||
raise
|
||||
|
||||
try:
|
||||
@ -466,29 +508,8 @@ def set_allocations(req):
|
||||
want_schema = schema.POST_ALLOCATIONS_V1_28
|
||||
data = util.extract_json(req.body, want_schema)
|
||||
|
||||
# First, ensure that all consumers referenced in the payload actually
|
||||
# exist. And if not, create them. Keep a record of auto-created consumers
|
||||
# so we can clean them up if the end allocation replace_all() fails.
|
||||
consumers = {} # dict of Consumer objects, keyed by consumer UUID
|
||||
new_consumers_created = []
|
||||
for consumer_uuid in data:
|
||||
project_id = data[consumer_uuid]['project_id']
|
||||
user_id = data[consumer_uuid]['user_id']
|
||||
consumer_generation = data[consumer_uuid].get('consumer_generation')
|
||||
try:
|
||||
consumer, new_consumer_created = util.ensure_consumer(
|
||||
context, consumer_uuid, project_id, user_id,
|
||||
consumer_generation, want_version)
|
||||
if new_consumer_created:
|
||||
new_consumers_created.append(consumer)
|
||||
consumers[consumer_uuid] = consumer
|
||||
except Exception:
|
||||
# If any errors (for instance, a consumer generation conflict)
|
||||
# occur when ensuring consumer records above, make sure we delete
|
||||
# any auto-created consumers.
|
||||
_delete_consumers(new_consumers_created)
|
||||
raise
|
||||
|
||||
consumers, new_consumers_created = inspect_consumers(
|
||||
context, data, want_version)
|
||||
# Create a sequence of allocation objects to be used in one
|
||||
# AllocationList.replace_all() call, which will mean all the changes
|
||||
# happen within a single transaction and with resource provider
|
||||
@ -500,7 +521,7 @@ def set_allocations(req):
|
||||
alloc_list.replace_all()
|
||||
LOG.debug("Successfully wrote allocations %s", alloc_list)
|
||||
except Exception:
|
||||
_delete_consumers(new_consumers_created)
|
||||
delete_consumers(new_consumers_created)
|
||||
raise
|
||||
|
||||
try:
|
||||
|
@ -75,7 +75,7 @@ def _extract_inventories(body, schema):
|
||||
return data
|
||||
|
||||
|
||||
def _make_inventory_object(resource_provider, resource_class, **data):
|
||||
def make_inventory_object(resource_provider, resource_class, **data):
|
||||
"""Single place to catch malformed Inventories."""
|
||||
# TODO(cdent): Some of the validation checks that are done here
|
||||
# could be done via JSONschema (using, for example, "minimum":
|
||||
@ -191,7 +191,7 @@ def create_inventory(req):
|
||||
data = _extract_inventory(req.body, schema.POST_INVENTORY_SCHEMA)
|
||||
resource_class = data.pop('resource_class')
|
||||
|
||||
inventory = _make_inventory_object(resource_provider,
|
||||
inventory = make_inventory_object(resource_provider,
|
||||
resource_class,
|
||||
**data)
|
||||
|
||||
@ -336,7 +336,7 @@ def set_inventories(req):
|
||||
|
||||
inv_list = []
|
||||
for res_class, inventory_data in data['inventories'].items():
|
||||
inventory = _make_inventory_object(
|
||||
inventory = make_inventory_object(
|
||||
resource_provider, res_class, **inventory_data)
|
||||
inv_list.append(inventory)
|
||||
inventories = rp_obj.InventoryList(objects=inv_list)
|
||||
@ -440,7 +440,7 @@ def update_inventory(req):
|
||||
_('resource provider generation conflict'),
|
||||
comment=errors.CONCURRENT_UPDATE)
|
||||
|
||||
inventory = _make_inventory_object(resource_provider,
|
||||
inventory = make_inventory_object(resource_provider,
|
||||
resource_class,
|
||||
**data)
|
||||
|
||||
|
127
nova/api/openstack/placement/handlers/reshaper.py
Normal file
127
nova/api/openstack/placement/handlers/reshaper.py
Normal file
@ -0,0 +1,127 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Placement API handler for the reshaper.
|
||||
|
||||
The reshaper provides for atomically migrating resource provider inventories
|
||||
and associated allocations when some of the inventory moves from one resource
|
||||
provider to another, such as when a class of inventory moves from a parent
|
||||
provider to a new child provider.
|
||||
"""
|
||||
|
||||
import copy
|
||||
|
||||
from oslo_utils import excutils
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.placement import errors
|
||||
from nova.api.openstack.placement import exception
|
||||
# TODO(cdent): That we are doing this suggests that there's stuff to be
|
||||
# extracted from the handler to a shared module.
|
||||
from nova.api.openstack.placement.handlers import allocation
|
||||
from nova.api.openstack.placement.handlers import inventory
|
||||
from nova.api.openstack.placement import microversion
|
||||
from nova.api.openstack.placement.objects import resource_provider as rp_obj
|
||||
from nova.api.openstack.placement.policies import reshaper as policies
|
||||
from nova.api.openstack.placement.schemas import reshaper as schema
|
||||
from nova.api.openstack.placement import util
|
||||
from nova.api.openstack.placement import wsgi_wrapper
|
||||
# TODO(cdent): placement needs its own version of this
|
||||
from nova.i18n import _
|
||||
|
||||
|
||||
@wsgi_wrapper.PlacementWsgify
|
||||
@microversion.version_handler('1.30')
|
||||
@util.require_content('application/json')
|
||||
def reshape(req):
|
||||
context = req.environ['placement.context']
|
||||
want_version = req.environ[microversion.MICROVERSION_ENVIRON]
|
||||
context.can(policies.RESHAPE)
|
||||
data = util.extract_json(req.body, schema.POST_RESHAPER_SCHEMA)
|
||||
inventories = data['inventories']
|
||||
allocations = data['allocations']
|
||||
# We're going to create several InventoryList, by rp uuid.
|
||||
inventory_by_rp = {}
|
||||
|
||||
# TODO(cdent): this has overlaps with inventory:set_inventories
|
||||
# and is a mess of bad names and lack of method extraction.
|
||||
for rp_uuid, inventory_data in inventories.items():
|
||||
try:
|
||||
resource_provider = rp_obj.ResourceProvider.get_by_uuid(
|
||||
context, rp_uuid)
|
||||
except exception.NotFound as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
_('Resource provider %(rp_uuid)s in inventories not found: '
|
||||
'%(error)s') % {'rp_uuid': rp_uuid, 'error': exc},
|
||||
comment=errors.RESOURCE_PROVIDER_NOT_FOUND)
|
||||
|
||||
# Do an early generation check.
|
||||
generation = inventory_data['resource_provider_generation']
|
||||
if generation != resource_provider.generation:
|
||||
raise webob.exc.HTTPConflict(
|
||||
_('resource provider generation conflict: '
|
||||
'actual: %(actual)s, given: %(given)s') %
|
||||
{'actual': resource_provider.generation,
|
||||
'given': generation},
|
||||
comment=errors.CONCURRENT_UPDATE)
|
||||
|
||||
inv_list = []
|
||||
for res_class, raw_inventory in inventory_data['inventories'].items():
|
||||
inv_data = copy.copy(inventory.INVENTORY_DEFAULTS)
|
||||
inv_data.update(raw_inventory)
|
||||
inv_obj = inventory.make_inventory_object(
|
||||
resource_provider, res_class, **inv_data)
|
||||
inv_list.append(inv_obj)
|
||||
inventory_by_rp[rp_uuid] = rp_obj.InventoryList(objects=inv_list)
|
||||
|
||||
# Make the consumer objects associated with the allocations.
|
||||
consumers, new_consumers_created = allocation.inspect_consumers(
|
||||
context, allocations, want_version)
|
||||
|
||||
# Nest exception handling so that any exception results in new consumer
|
||||
# objects being deleted, then reraise for translating to HTTP exceptions.
|
||||
try:
|
||||
try:
|
||||
# When these allocations are created they get resource provider
|
||||
# objects which are different instances (usually with the same
|
||||
# data) from those loaded above when creating inventory objects.
|
||||
# The reshape method below is responsible for ensuring that the
|
||||
# resource providers and their generations do not conflict.
|
||||
allocation_objects = allocation.create_allocation_list(
|
||||
context, allocations, consumers)
|
||||
|
||||
rp_obj.reshape(context, inventory_by_rp, allocation_objects)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
allocation.delete_consumers(new_consumers_created)
|
||||
# Generation conflict is a (rare) possibility in a few different
|
||||
# places in reshape().
|
||||
except exception.ConcurrentUpdateDetected as exc:
|
||||
raise webob.exc.HTTPConflict(
|
||||
_('update conflict: %(error)s') % {'error': exc},
|
||||
comment=errors.CONCURRENT_UPDATE)
|
||||
# A NotFound here means a resource class that does not exist was named
|
||||
except exception.NotFound as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
_('malformed reshaper data: %(error)s') % {'error': exc})
|
||||
# Distinguish inventory in use (has allocations on it)...
|
||||
except exception.InventoryInUse as exc:
|
||||
raise webob.exc.HTTPConflict(
|
||||
_('update conflict: %(error)s') % {'error': exc},
|
||||
comment=errors.INVENTORY_INUSE)
|
||||
# ...from allocations which won't fit for a variety of reasons.
|
||||
except exception.InvalidInventory as exc:
|
||||
raise webob.exc.HTTPConflict(
|
||||
_('Unable to allocate inventory: %(error)s') % {'error': exc})
|
||||
|
||||
req.response.status = 204
|
||||
req.response.content_type = None
|
||||
return req.response
|
@ -75,6 +75,8 @@ VERSIONS = [
|
||||
# the resource class is not in the requested resources.
|
||||
'1.28', # Add support for consumer generation
|
||||
'1.29', # Support nested providers in GET /allocation_candidates API.
|
||||
'1.30', # Add POST /reshaper for atomically migrating resource provider
|
||||
# inventories and allocations.
|
||||
]
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ from nova.api.openstack.placement.policies import allocation
|
||||
from nova.api.openstack.placement.policies import allocation_candidate
|
||||
from nova.api.openstack.placement.policies import base
|
||||
from nova.api.openstack.placement.policies import inventory
|
||||
from nova.api.openstack.placement.policies import reshaper
|
||||
from nova.api.openstack.placement.policies import resource_class
|
||||
from nova.api.openstack.placement.policies import resource_provider
|
||||
from nova.api.openstack.placement.policies import trait
|
||||
@ -33,5 +34,6 @@ def list_rules():
|
||||
usage.list_rules(),
|
||||
trait.list_rules(),
|
||||
allocation.list_rules(),
|
||||
allocation_candidate.list_rules()
|
||||
allocation_candidate.list_rules(),
|
||||
reshaper.list_rules(),
|
||||
)
|
||||
|
38
nova/api/openstack/placement/policies/reshaper.py
Normal file
38
nova/api/openstack/placement/policies/reshaper.py
Normal file
@ -0,0 +1,38 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from oslo_policy import policy
|
||||
|
||||
from nova.api.openstack.placement.policies import base
|
||||
|
||||
|
||||
PREFIX = 'placement:reshaper:%s'
|
||||
RESHAPE = PREFIX % 'reshape'
|
||||
|
||||
rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
RESHAPE,
|
||||
base.RULE_ADMIN_API,
|
||||
"Reshape Inventory and Allocations.",
|
||||
[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': '/reshaper'
|
||||
}
|
||||
],
|
||||
scope_types=['system']),
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return rules
|
@ -504,3 +504,15 @@ provider trees are present, ``allocation_requests`` in the response of
|
||||
multiple resource providers in the same tree.
|
||||
2) ``root_provider_uuid`` and ``parent_provider_uuid`` are added to
|
||||
``provider_summaries`` in the response of ``GET /allocation_candidates``.
|
||||
|
||||
1.30 Provide a /reshaper resource
|
||||
---------------------------------
|
||||
|
||||
Add support for a ``POST /reshaper`` resource that provides for atomically
|
||||
migrating resource provider inventories and associated allocations when some of
|
||||
the inventory moves from one resource provider to another, such as when a class
|
||||
of inventory moves from a parent provider to a new child provider.
|
||||
|
||||
.. note:: This is a special operation that should only be used in rare cases
|
||||
of resource provider topology changing when inventory is in use.
|
||||
Only use this if you are really sure of what you are doing.
|
||||
|
47
nova/api/openstack/placement/schemas/reshaper.py
Normal file
47
nova/api/openstack/placement/schemas/reshaper.py
Normal file
@ -0,0 +1,47 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Reshaper schema for Placement API."""
|
||||
|
||||
import copy
|
||||
|
||||
from nova.api.openstack.placement.schemas import allocation
|
||||
from nova.api.openstack.placement.schemas import common
|
||||
from nova.api.openstack.placement.schemas import inventory
|
||||
|
||||
|
||||
ALLOCATIONS = copy.deepcopy(allocation.POST_ALLOCATIONS_V1_28)
|
||||
# In the reshaper we need to allow allocations to be an empty dict
|
||||
# because it may be the case that there simply are no allocations
|
||||
# (now) for any of the inventory being moved.
|
||||
ALLOCATIONS['minProperties'] = 0
|
||||
POST_RESHAPER_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"inventories": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
# resource provider uuid
|
||||
common.UUID_PATTERN: inventory.PUT_INVENTORY_SCHEMA,
|
||||
},
|
||||
# We expect at least one inventories, otherwise there is no reason
|
||||
# to call the reshaper.
|
||||
"minProperties": 1,
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"allocations": ALLOCATIONS,
|
||||
},
|
||||
"required": [
|
||||
"inventories",
|
||||
"allocations",
|
||||
],
|
||||
"additionalProperties": False,
|
||||
}
|
@ -174,7 +174,6 @@ class AllocationFixture(APIFixture):
|
||||
# Create a second consumer for the VCPU allocations
|
||||
consumer2 = tb.ensure_consumer(self.context, user, project)
|
||||
tb.set_allocation(self.context, rp, consumer2, {'VCPU': 6})
|
||||
# This consumer is referenced from the gabbits
|
||||
os.environ['CONSUMER_ID'] = consumer2.uuid
|
||||
|
||||
# Create a consumer object for a different user
|
||||
|
@ -41,13 +41,13 @@ tests:
|
||||
response_json_paths:
|
||||
$.errors[0].title: Not Acceptable
|
||||
|
||||
- name: latest microversion is 1.29
|
||||
- name: latest microversion is 1.30
|
||||
GET: /
|
||||
request_headers:
|
||||
openstack-api-version: placement latest
|
||||
response_headers:
|
||||
vary: /openstack-api-version/
|
||||
openstack-api-version: placement 1.29
|
||||
openstack-api-version: placement 1.30
|
||||
|
||||
- name: other accept header bad version
|
||||
GET: /
|
||||
|
@ -0,0 +1,20 @@
|
||||
# This tests POSTs to /reshaper using a non-admin user with an open policy
|
||||
# configuration. The response is a 400 because of bad content, meaning we got
|
||||
# past policy enforcement. If policy was being enforced we'd get a 403.
|
||||
fixtures:
|
||||
- OpenPolicyFixture
|
||||
|
||||
defaults:
|
||||
request_headers:
|
||||
x-auth-token: user
|
||||
accept: application/json
|
||||
content-type: application/json
|
||||
openstack-api-version: placement latest
|
||||
|
||||
tests:
|
||||
|
||||
- name: attempt reshape
|
||||
POST: /reshaper
|
||||
data:
|
||||
bad: content
|
||||
status: 400
|
@ -0,0 +1,599 @@
|
||||
# /reshaper provides a way to atomically move inventory and allocations from
|
||||
# one resource provider to another, often from a root provider to a new child.
|
||||
|
||||
fixtures:
|
||||
- AllocationFixture
|
||||
|
||||
defaults:
|
||||
request_headers:
|
||||
x-auth-token: admin
|
||||
accept: application/json
|
||||
content-type: application/json
|
||||
openstack-api-version: placement 1.30
|
||||
|
||||
tests:
|
||||
|
||||
- name: reshaper is POST only
|
||||
GET: /reshaper
|
||||
status: 405
|
||||
response_headers:
|
||||
allow: POST
|
||||
|
||||
- name: reshaper requires admin not user
|
||||
POST: /reshaper
|
||||
request_headers:
|
||||
x-auth-token: user
|
||||
status: 403
|
||||
|
||||
- name: reshaper not there old
|
||||
POST: /reshaper
|
||||
request_headers:
|
||||
openstack-api-version: placement 1.29
|
||||
status: 404
|
||||
|
||||
- name: very invalid 400
|
||||
POST: /reshaper
|
||||
status: 400
|
||||
data:
|
||||
cows: moo
|
||||
response_strings:
|
||||
- JSON does not validate
|
||||
|
||||
- name: missing allocations
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 0
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 1
|
||||
status: 400
|
||||
|
||||
# There are existing allocations on RP_UUID (created by the AllocationFixture).
|
||||
# As the code is currently we cannot null out those allocations from reshaper
|
||||
# because the allocations identify nothing (replace_all() is a no op).
|
||||
- name: empty allocations inv in use
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 1
|
||||
allocations: {}
|
||||
status: 409
|
||||
response_json_paths:
|
||||
$.errors[0].code: placement.inventory.inuse
|
||||
|
||||
# Again, with the existing allocations on RP_UUID being held by CONSUMER_ID,
|
||||
# not INSTANCE_ID, when we try to allocate here, we don't have room. This
|
||||
# is a correctly invalid operation as to be actually reshaping here, we
|
||||
# would be needing to move the CONSUMER_ID allocations in this call (and
|
||||
# setting the inventory to something that could accomodate them).
|
||||
- name: with allocations
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 1
|
||||
allocations:
|
||||
$ENVIRON['INSTANCE_UUID']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
VCPU: 1
|
||||
consumer_generation: null
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
status: 409
|
||||
response_strings:
|
||||
- Unable to allocate inventory
|
||||
|
||||
- name: bad rp gen
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 4
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 1
|
||||
allocations: {}
|
||||
status: 409
|
||||
response_strings:
|
||||
- resource provider generation conflict
|
||||
- 'actual: 5, given: 4'
|
||||
|
||||
- name: bad consumer gen
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 1
|
||||
allocations:
|
||||
$ENVIRON['INSTANCE_UUID']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
VCPU: 1
|
||||
# The correct generation here is null, because INSTANCE_UUID
|
||||
# represents a new consumer at this point.
|
||||
consumer_generation: 99
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
status: 409
|
||||
response_strings:
|
||||
- consumer generation conflict
|
||||
|
||||
- name: create a child provider
|
||||
POST: /resource_providers
|
||||
data:
|
||||
uuid: $ENVIRON['ALT_RP_UUID']
|
||||
name: $ENVIRON['ALT_RP_NAME']
|
||||
parent_provider_uuid: $ENVIRON['RP_UUID']
|
||||
|
||||
# This and subsequent error checking tests are modelled on the successful
|
||||
# test which is at the end of this file. Using the same data, with minor
|
||||
# adjustments, so that the cause of failure is clear.
|
||||
|
||||
- name: move to bad child 400
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
DISK_GB:
|
||||
total: 2048
|
||||
step_size: 10
|
||||
min_unit: 10
|
||||
# this was 600 originally but we reset it to 1200
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 1200
|
||||
# This resource provider does not exist.
|
||||
'39bafc00-3fff-444d-b87a-2ead3f866e05':
|
||||
resource_provider_generation: 0
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 10
|
||||
# this was 4 originally but we reset it to 8
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 8
|
||||
# these consumer generations are all 1 because they have
|
||||
# previously allocated
|
||||
allocations:
|
||||
$ENVIRON['CONSUMER_0']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 1000
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 8
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['ALT_CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 20
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 1
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['ALT_USER_ID']
|
||||
consumer_generation: 1
|
||||
status: 400
|
||||
response_json_paths:
|
||||
$.errors[0].code: placement.resource_provider.not_found
|
||||
|
||||
- name: poorly formed inventory 400
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
DISK_GB:
|
||||
total: 2048
|
||||
step_size: 10
|
||||
min_unit: 10
|
||||
# this was 600 originally but we reset it to 1200
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 1200
|
||||
bad_field: moo
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resource_provider_generation: 0
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 10
|
||||
# this was 4 originally but we reset it to 8
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 8
|
||||
# these consumer generations are all 1 because they have
|
||||
# previously allocated
|
||||
allocations:
|
||||
$ENVIRON['CONSUMER_0']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 1000
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 8
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['ALT_CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 20
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 1
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['ALT_USER_ID']
|
||||
consumer_generation: 1
|
||||
status: 400
|
||||
response_strings:
|
||||
- JSON does not validate
|
||||
- "'bad_field' was unexpected"
|
||||
|
||||
- name: poorly formed allocation 400
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
DISK_GB:
|
||||
total: 2048
|
||||
step_size: 10
|
||||
min_unit: 10
|
||||
# this was 600 originally but we reset it to 1200
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 1200
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resource_provider_generation: 0
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 10
|
||||
# this was 4 originally but we reset it to 8
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 8
|
||||
# these consumer generations are all 1 because they have
|
||||
# previously allocated
|
||||
allocations:
|
||||
$ENVIRON['CONSUMER_0']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 1000
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
# This bad field will cause a failure in the schema.
|
||||
bad_field: moo
|
||||
$ENVIRON['CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 8
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['ALT_CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 20
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 1
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['ALT_USER_ID']
|
||||
consumer_generation: 1
|
||||
status: 400
|
||||
response_strings:
|
||||
- JSON does not validate
|
||||
- "'bad_field' was unexpected"
|
||||
|
||||
- name: target resource class not found
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
# not a real inventory, but valid form
|
||||
DISK_OF_STEEL:
|
||||
total: 2048
|
||||
step_size: 10
|
||||
min_unit: 10
|
||||
# this was 600 originally but we reset it to 1200
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 1200
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resource_provider_generation: 0
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 10
|
||||
# this was 4 originally but we reset it to 8
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 8
|
||||
# these consumer generations are all 1 because they have
|
||||
# previously allocated
|
||||
allocations:
|
||||
$ENVIRON['CONSUMER_0']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 1000
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 8
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['ALT_CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 20
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 1
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['ALT_USER_ID']
|
||||
consumer_generation: 1
|
||||
status: 400
|
||||
response_strings:
|
||||
- No such resource class DISK_OF_STEEL
|
||||
|
||||
- name: move bad allocation 409
|
||||
desc: max unit on disk gb inventory violated
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
DISK_GB:
|
||||
total: 2048
|
||||
step_size: 10
|
||||
min_unit: 10
|
||||
max_unit: 600
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resource_provider_generation: 0
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 10
|
||||
# this was 4 originally but we reset it to 8
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 8
|
||||
# these consumer generations are all 1 because they have
|
||||
# previously allocated
|
||||
allocations:
|
||||
$ENVIRON['CONSUMER_0']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
# Violates max unit
|
||||
DISK_GB: 1000
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 8
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['ALT_CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 20
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 1
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['ALT_USER_ID']
|
||||
consumer_generation: 1
|
||||
status: 409
|
||||
response_strings:
|
||||
- Unable to allocate inventory
|
||||
|
||||
# This is a successful reshape using information as it was established above
|
||||
# or in the AllocationFixture. A non-obvious fact of this test is that it
|
||||
# confirms that resource provider and consumer generations are rolled back
|
||||
# when failures occur, as in the tests above.
|
||||
- name: move vcpu inventory and allocations to child
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 5
|
||||
inventories:
|
||||
DISK_GB:
|
||||
total: 2048
|
||||
step_size: 10
|
||||
min_unit: 10
|
||||
# this was 600 originally but we reset it to 1200
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 1200
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resource_provider_generation: 0
|
||||
inventories:
|
||||
VCPU:
|
||||
total: 10
|
||||
# this was 4 originally but we reset it to 8
|
||||
# here because the fixture allocated twice for
|
||||
# the same consumer and we can't do that from
|
||||
# the api.
|
||||
max_unit: 8
|
||||
# these consumer generations are all 1 because they have
|
||||
# previously allocated
|
||||
allocations:
|
||||
$ENVIRON['CONSUMER_0']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 1000
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 8
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 1
|
||||
$ENVIRON['ALT_CONSUMER_ID']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 20
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resources:
|
||||
VCPU: 1
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['ALT_USER_ID']
|
||||
consumer_generation: 1
|
||||
status: 204
|
||||
|
||||
- name: get usages on parent after move
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
|
||||
response_json_paths:
|
||||
$.usages:
|
||||
DISK_GB: 1020
|
||||
$.resource_provider_generation: 8
|
||||
|
||||
- name: get usages on child after move
|
||||
GET: /resource_providers/$ENVIRON['ALT_RP_UUID']/usages
|
||||
response_json_paths:
|
||||
$.usages:
|
||||
VCPU: 9
|
||||
$.resource_provider_generation: 3
|
||||
|
||||
# Now move some of the inventory back to the original provider, and put all
|
||||
# the allocations under two new consumers. This is an artificial test to
|
||||
# exercise new consumer creation.
|
||||
- name: consolidate inventory and allocations
|
||||
# TODO(efried): bug https://bugs.launchpad.net/nova/+bug/1783130
|
||||
xfail: true
|
||||
POST: /reshaper
|
||||
data:
|
||||
inventories:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resource_provider_generation: 8
|
||||
inventories:
|
||||
DISK_GB:
|
||||
total: 2048
|
||||
step_size: 10
|
||||
min_unit: 10
|
||||
max_unit: 1200
|
||||
VCPU:
|
||||
total: 10
|
||||
max_unit: 8
|
||||
$ENVIRON['ALT_RP_UUID']:
|
||||
resource_provider_generation: 3
|
||||
# bug https://bugs.launchpad.net/nova/+bug/1783130
|
||||
# means that this will cause an IndexError.
|
||||
inventories: {}
|
||||
allocations:
|
||||
$ENVIRON['CONSUMER_0']:
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 1000
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: 2
|
||||
'7bd2e864-0415-445c-8fc2-328520ef7642':
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
VCPU: 8
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['USER_ID']
|
||||
consumer_generation: null
|
||||
'2dfa608c-cecb-4fe0-a1bb-950015fa731f':
|
||||
allocations:
|
||||
$ENVIRON['RP_UUID']:
|
||||
resources:
|
||||
DISK_GB: 20
|
||||
VCPU: 1
|
||||
project_id: $ENVIRON['PROJECT_ID']
|
||||
user_id: $ENVIRON['ALT_USER_ID']
|
||||
consumer_generation: null
|
||||
status: 204
|
||||
|
||||
- name: get usages on parent after move back
|
||||
# TODO(efried): bug https://bugs.launchpad.net/nova/+bug/1783130
|
||||
# Fails because 'consolidate inventory and allocations' above fails.
|
||||
xfail: true
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
|
||||
response_json_paths:
|
||||
$.usages:
|
||||
VCPU: 9
|
||||
DISK_GB: 1040
|
||||
$.resource_provider_generation: 11
|
||||
|
||||
- name: get usages on child after move back
|
||||
# TODO(efried): bug https://bugs.launchpad.net/nova/+bug/1783130
|
||||
# Fails because 'consolidate inventory and allocations' above fails.
|
||||
xfail: true
|
||||
GET: /resource_providers/$ENVIRON['ALT_RP_UUID']/usages
|
||||
response_json_paths:
|
||||
$.usages: {}
|
||||
$.resource_provider_generation: 5
|
@ -88,7 +88,7 @@ class TestMicroversionIntersection(testtools.TestCase):
|
||||
# if you add two different versions of method 'foobar' the
|
||||
# number only goes up by one if no other version foobar yet
|
||||
# exists. This operates as a simple sanity check.
|
||||
TOTAL_VERSIONED_METHODS = 19
|
||||
TOTAL_VERSIONED_METHODS = 20
|
||||
|
||||
def test_methods_versioned(self):
|
||||
methods_data = microversion.VERSIONED_METHODS
|
||||
|
@ -37,7 +37,7 @@ Request
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- consumer_uuid: consumer_uuid_body
|
||||
- consumer_generation: consumer_generation
|
||||
- consumer_generation: consumer_generation_min
|
||||
- project_id: project_id_body
|
||||
- user_id: user_id_body
|
||||
- allocations: allocations_dict_empty
|
||||
@ -89,7 +89,7 @@ Response
|
||||
- allocations: allocations_by_resource_provider
|
||||
- generation: resource_provider_generation
|
||||
- resources: resources
|
||||
- consumer_generation: consumer_generation
|
||||
- consumer_generation: consumer_generation_min
|
||||
- project_id: project_id_body_1_12
|
||||
- user_id: user_id_body_1_12
|
||||
|
||||
@ -131,7 +131,7 @@ Request (microversions 1.12 - )
|
||||
- consumer_uuid: consumer_uuid
|
||||
- allocations: allocations_dict
|
||||
- resources: resources
|
||||
- consumer_generation: consumer_generation
|
||||
- consumer_generation: consumer_generation_min
|
||||
- project_id: project_id_body
|
||||
- user_id: user_id_body
|
||||
- generation: resource_provider_generation_optional
|
||||
|
@ -30,3 +30,4 @@ header for APIs sending data payloads in the request body (i.e. ``PUT`` and
|
||||
.. include:: usages.inc
|
||||
.. include:: resource_provider_usages.inc
|
||||
.. include:: allocation_candidates.inc
|
||||
.. include:: reshaper.inc
|
||||
|
@ -315,14 +315,16 @@ capacity:
|
||||
required: true
|
||||
description: >
|
||||
The amount of the resource that the provider can accommodate.
|
||||
consumer_generation:
|
||||
consumer_generation: &consumer_generation
|
||||
type: integer
|
||||
in: body
|
||||
required: true
|
||||
min_version: 1.28
|
||||
description: >
|
||||
The generation of the consumer. Should be set to ``null`` when indicating
|
||||
that the caller expects the consumer does not yet exist.
|
||||
consumer_generation_min:
|
||||
<<: *consumer_generation
|
||||
min_version: 1.28
|
||||
consumer_uuid_body:
|
||||
<<: *consumer_uuid
|
||||
in: body
|
||||
@ -392,6 +394,26 @@ reserved_opt:
|
||||
Up to microversion 1.25, this value has to be less than the value of
|
||||
``total``. Starting from microversion 1.26, this value has to be less
|
||||
than or equal to the value of ``total``.
|
||||
reshaper_allocations:
|
||||
type: object
|
||||
in: body
|
||||
required: true
|
||||
description: >
|
||||
A dictionary of multiple allocations, keyed by consumer uuid. Each
|
||||
collection of allocations describes the full set of allocations for
|
||||
each consumer. Each consumer allocations dict is itself a dictionary
|
||||
of resource allocations keyed by resource provider uuid. An empty
|
||||
dictionary indicates no change in existing allocations, whereas an empty
|
||||
``allocations`` dictionary **within** a consumer dictionary indicates that
|
||||
all allocations for that consumer should be deleted.
|
||||
reshaper_inventories:
|
||||
type: object
|
||||
in: body
|
||||
required: true
|
||||
description: >
|
||||
A dictionary of multiple inventories, keyed by resource provider uuid. Each
|
||||
inventory describes the desired full inventory for each resource provider.
|
||||
An empty dictionary causes the inventory for that provider to be deleted.
|
||||
resource_class:
|
||||
<<: *resource_class_path
|
||||
in: body
|
||||
|
44
placement-api-ref/source/reshaper.inc
Normal file
44
placement-api-ref/source/reshaper.inc
Normal file
@ -0,0 +1,44 @@
|
||||
========
|
||||
Reshaper
|
||||
========
|
||||
|
||||
.. note:: Reshaper requests are available starting from version 1.30.
|
||||
|
||||
Reshaper
|
||||
========
|
||||
|
||||
Atomically migrate resource provider inventories and associated allocations.
|
||||
This is used when some of the inventory needs to move from one resource
|
||||
provider to another, such as when a class of inventory moves from a parent
|
||||
provider to a new child provider.
|
||||
|
||||
.. note:: This is a special operation that should only be used in rare cases
|
||||
of resource provider topology changing when inventory is in use.
|
||||
Only use this if you are really sure of what you are doing.
|
||||
|
||||
.. rest_method:: POST /reshaper
|
||||
|
||||
Normal Response Codes: 204
|
||||
|
||||
Error Response Codes: badRequest(400), conflict(409)
|
||||
|
||||
Request
|
||||
-------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- inventories: reshaper_inventories
|
||||
- inventories.{resource_provider_uuid}.resource_provider_generation: resource_provider_generation
|
||||
- inventories.{resource_provider_uuid}.inventories: inventories
|
||||
- allocations: reshaper_allocations
|
||||
- allocations.{consumer_uuid}.allocations: allocations_dict_empty
|
||||
- allocations.{consumer_uuid}.allocations.{resource_provider_uuid}.resources: resources
|
||||
- allocations.{consumer_uuid}.project_id: project_id_body
|
||||
- allocations.{consumer_uuid}.user_id: user_id_body
|
||||
- allocations.{consumer_uuid}.consumer_generation: consumer_generation
|
||||
|
||||
Request Example
|
||||
---------------
|
||||
|
||||
.. literalinclude:: ./samples/reshaper/post-reshaper-1.30.json
|
||||
:language: javascript
|
@ -0,0 +1,67 @@
|
||||
{
|
||||
"allocations": {
|
||||
"9ae60315-80c2-48a0-a168-ca4f27c307e1": {
|
||||
"allocations": {
|
||||
"a7466641-cd72-499b-b6c9-c208eacecb3d": {
|
||||
"resources": {
|
||||
"DISK_GB": 1000
|
||||
}
|
||||
}
|
||||
},
|
||||
"project_id": "2f0c4ffc-4c4d-407a-b334-56297b871b7f",
|
||||
"user_id": "cc8a0fe0-2b7c-4392-ae51-747bc73cf473",
|
||||
"consumer_generation": 1
|
||||
},
|
||||
"4a6444e5-10d6-43f6-9a0b-8acce9309ac9": {
|
||||
"allocations": {
|
||||
"c4ddddbb-01ee-4814-85c9-f57a962c22ba": {
|
||||
"resources": {
|
||||
"VCPU": 1
|
||||
}
|
||||
},
|
||||
"a7466641-cd72-499b-b6c9-c208eacecb3d": {
|
||||
"resources": {
|
||||
"DISK_GB": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
"project_id": "2f0c4ffc-4c4d-407a-b334-56297b871b7f",
|
||||
"user_id": "406e1095-71cb-47b9-9b3c-aedb7f663f5a",
|
||||
"consumer_generation": 1
|
||||
},
|
||||
"e10e7ca0-2ac5-4c98-bad9-51c95b1930ed": {
|
||||
"allocations": {
|
||||
"c4ddddbb-01ee-4814-85c9-f57a962c22ba": {
|
||||
"resources": {
|
||||
"VCPU": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
"project_id": "2f0c4ffc-4c4d-407a-b334-56297b871b7f",
|
||||
"user_id": "cc8a0fe0-2b7c-4392-ae51-747bc73cf473",
|
||||
"consumer_generation": 1
|
||||
}
|
||||
},
|
||||
"inventories": {
|
||||
"c4ddddbb-01ee-4814-85c9-f57a962c22ba": {
|
||||
"inventories": {
|
||||
"VCPU": {
|
||||
"max_unit": 8,
|
||||
"total": 10
|
||||
}
|
||||
},
|
||||
"resource_provider_generation": null
|
||||
},
|
||||
"a7466641-cd72-499b-b6c9-c208eacecb3d": {
|
||||
"inventories": {
|
||||
"DISK_GB": {
|
||||
"min_unit": 10,
|
||||
"total": 2048,
|
||||
"max_unit": 1200,
|
||||
"step_size": 10
|
||||
}
|
||||
},
|
||||
"resource_provider_generation": 5
|
||||
}
|
||||
}
|
||||
}
|
12
releasenotes/notes/placement-reshaper-6f3ef70c3a550d09.yaml
Normal file
12
releasenotes/notes/placement-reshaper-6f3ef70c3a550d09.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Microversion 1.30 of the placement API adds support for a
|
||||
``POST /reshaper`` resource that provides for atomically migrating resource
|
||||
provider inventories and associated allocations when some of the inventory
|
||||
moves from one resource provider to another, such as when a class of
|
||||
inventory moves from a parent provider to a new child provider.
|
||||
|
||||
.. note:: This is a special operation that should only be used in rare
|
||||
cases of resource provider topology changing when inventory is in
|
||||
use. Only use this if you are really sure of what you are doing.
|
Loading…
Reference in New Issue
Block a user