Merge "Add support for inventories to placement API"
This commit is contained in:
commit
a0ad1258a5
@ -26,6 +26,7 @@ method.
|
||||
import routes
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.placement.handlers import inventory
|
||||
from nova.api.openstack.placement.handlers import resource_provider
|
||||
from nova.api.openstack.placement.handlers import root
|
||||
from nova.api.openstack.placement import util
|
||||
@ -50,6 +51,16 @@ ROUTE_DECLARATIONS = {
|
||||
'DELETE': resource_provider.delete_resource_provider,
|
||||
'PUT': resource_provider.update_resource_provider
|
||||
},
|
||||
'/resource_providers/{uuid}/inventories': {
|
||||
'GET': inventory.get_inventories,
|
||||
'POST': inventory.create_inventory,
|
||||
'PUT': inventory.set_inventories
|
||||
},
|
||||
'/resource_providers/{uuid}/inventories/{resource_class}': {
|
||||
'GET': inventory.get_inventory,
|
||||
'PUT': inventory.update_inventory,
|
||||
'DELETE': inventory.delete_inventory
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
408
nova/api/openstack/placement/handlers/inventory.py
Normal file
408
nova/api/openstack/placement/handlers/inventory.py
Normal file
@ -0,0 +1,408 @@
|
||||
# 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.
|
||||
"""Inventory handlers for Placement API."""
|
||||
|
||||
import copy
|
||||
|
||||
import jsonschema
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_serialization import jsonutils
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.placement import util
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
|
||||
|
||||
BASE_INVENTORY_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resource_provider_generation": {
|
||||
"type": "integer"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
},
|
||||
"reserved": {
|
||||
"type": "integer"
|
||||
},
|
||||
"min_unit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"max_unit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"step_size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"allocation_ratio": {
|
||||
"type": "number"
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"total",
|
||||
"resource_provider_generation"
|
||||
],
|
||||
"additionalProperties": False
|
||||
}
|
||||
POST_INVENTORY_SCHEMA = copy.deepcopy(BASE_INVENTORY_SCHEMA)
|
||||
POST_INVENTORY_SCHEMA['properties']['resource_class'] = {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z0-9_]+$"
|
||||
}
|
||||
POST_INVENTORY_SCHEMA['required'].append('resource_class')
|
||||
POST_INVENTORY_SCHEMA['required'].remove('resource_provider_generation')
|
||||
PUT_INVENTORY_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resource_provider_generation": {
|
||||
"type": "integer"
|
||||
},
|
||||
"inventories": {
|
||||
"type": "array",
|
||||
"items": POST_INVENTORY_SCHEMA
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"resource_provider_generation",
|
||||
"inventories"
|
||||
],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
# NOTE(cdent): We keep our own representation of inventory defaults
|
||||
# and output fields, separate from the versioned object to avoid
|
||||
# inadvertent API changes when the object defaults are changed.
|
||||
OUTPUT_INVENTORY_FIELDS = [
|
||||
'total',
|
||||
'reserved',
|
||||
'min_unit',
|
||||
'max_unit',
|
||||
'step_size',
|
||||
'allocation_ratio',
|
||||
]
|
||||
INVENTORY_DEFAULTS = {
|
||||
'reserved': 0,
|
||||
'min_unit': 0,
|
||||
'max_unit': 0,
|
||||
'step_size': 1,
|
||||
'allocation_ratio': 1.0
|
||||
}
|
||||
|
||||
|
||||
def _extract_json(body, schema):
|
||||
"""Extract and validate data from JSON body."""
|
||||
try:
|
||||
data = jsonutils.loads(body)
|
||||
except ValueError as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
'Malformed JSON: %s' % exc,
|
||||
json_formatter=util.json_error_formatter)
|
||||
try:
|
||||
jsonschema.validate(data, schema)
|
||||
except jsonschema.ValidationError as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
'JSON does not validate: %s' % exc,
|
||||
json_formatter=util.json_error_formatter)
|
||||
return data
|
||||
|
||||
|
||||
def _extract_inventory(body, schema):
|
||||
"""Extract and validate inventory from JSON body."""
|
||||
data = _extract_json(body, schema)
|
||||
|
||||
inventory_data = copy.copy(INVENTORY_DEFAULTS)
|
||||
inventory_data.update(data)
|
||||
|
||||
return inventory_data
|
||||
|
||||
|
||||
def _extract_inventories(body, schema):
|
||||
"""Extract and validate multiple inventories from JSON body."""
|
||||
data = _extract_json(body, schema)
|
||||
|
||||
inventories = []
|
||||
for raw_inventory in data['inventories']:
|
||||
inventory_data = copy.copy(INVENTORY_DEFAULTS)
|
||||
inventory_data.update(raw_inventory)
|
||||
inventories.append(inventory_data)
|
||||
|
||||
data['inventories'] = inventories
|
||||
return data
|
||||
|
||||
|
||||
def _make_inventory_object(resource_provider, **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":
|
||||
# 0) for non-negative integers. It's not clear if that is
|
||||
# duplication or decoupling so leaving it as this for now.
|
||||
try:
|
||||
inventory = objects.Inventory(
|
||||
resource_provider=resource_provider, **data)
|
||||
except (ValueError, TypeError) as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
'Bad inventory %s for resource provider %s: %s'
|
||||
% (data['resource_class'], resource_provider.uuid, exc),
|
||||
json_formatter=util.json_error_formatter)
|
||||
return inventory
|
||||
|
||||
|
||||
def _send_inventories(response, resource_provider, inventories):
|
||||
"""Send a JSON representation of a list of inventories."""
|
||||
response.status = 200
|
||||
response.body = jsonutils.dumps(_serialize_inventories(
|
||||
resource_provider.generation, inventories))
|
||||
response.content_type = 'application/json'
|
||||
return response
|
||||
|
||||
|
||||
def _send_inventory(response, resource_provider, inventory, status=200):
|
||||
"""Send a JSON representation of one single inventory."""
|
||||
response.status = status
|
||||
response.body = jsonutils.dumps(_serialize_inventory(
|
||||
resource_provider.generation, inventory))
|
||||
response.content_type = 'application/json'
|
||||
return response
|
||||
|
||||
|
||||
def _serialize_inventory(generation, inventory):
|
||||
"""Turn a single inventory into a dictionary."""
|
||||
data = {
|
||||
field: getattr(inventory, field)
|
||||
for field in OUTPUT_INVENTORY_FIELDS
|
||||
}
|
||||
data['resource_provider_generation'] = generation
|
||||
return data
|
||||
|
||||
|
||||
def _serialize_inventories(generation, inventories):
|
||||
"""Turn a list of inventories in a dict by resource class."""
|
||||
inventories_by_class = {inventory.resource_class: inventory
|
||||
for inventory in inventories}
|
||||
inventories_dict = {}
|
||||
for resource_class, inventory in inventories_by_class.items():
|
||||
inventories_dict[resource_class] = _serialize_inventory(
|
||||
generation, inventory)
|
||||
return {'inventories': inventories_dict}
|
||||
|
||||
|
||||
@webob.dec.wsgify
|
||||
@util.require_content('application/json')
|
||||
def create_inventory(req):
|
||||
"""POST to create one inventory.
|
||||
|
||||
On success return a 201 response, a location header pointing
|
||||
to the newly created inventory and an application/json representation
|
||||
of the inventory.
|
||||
"""
|
||||
context = req.environ['placement.context']
|
||||
uuid = util.wsgi_path_item(req.environ, 'uuid')
|
||||
resource_provider = objects.ResourceProvider.get_by_uuid(
|
||||
context, uuid)
|
||||
data = _extract_inventory(req.body, POST_INVENTORY_SCHEMA)
|
||||
|
||||
inventory = _make_inventory_object(resource_provider, **data)
|
||||
|
||||
try:
|
||||
resource_provider.add_inventory(inventory)
|
||||
except (exception.ConcurrentUpdateDetected,
|
||||
db_exc.DBDuplicateEntry) as exc:
|
||||
raise webob.exc.HTTPConflict(
|
||||
'Update conflict: %s' % exc,
|
||||
json_formatter=util.json_error_formatter)
|
||||
except exception.InvalidInventoryCapacity as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
'Unable to create inventory for resource provider %s: %s'
|
||||
% (resource_provider.uuid, exc),
|
||||
json_formatter=util.json_error_formatter)
|
||||
|
||||
response = req.response
|
||||
response.location = util.inventory_url(
|
||||
req.environ, resource_provider, data['resource_class'])
|
||||
return _send_inventory(response, resource_provider, inventory,
|
||||
status=201)
|
||||
|
||||
|
||||
@webob.dec.wsgify
|
||||
def delete_inventory(req):
|
||||
"""DELETE to destroy a single inventory.
|
||||
|
||||
If the inventory is in use or resource provider generation is out
|
||||
of sync return a 409.
|
||||
|
||||
On success return a 204 and an empty body.
|
||||
"""
|
||||
context = req.environ['placement.context']
|
||||
uuid = util.wsgi_path_item(req.environ, 'uuid')
|
||||
resource_class = util.wsgi_path_item(req.environ, 'resource_class')
|
||||
|
||||
resource_provider = objects.ResourceProvider.get_by_uuid(
|
||||
context, uuid)
|
||||
try:
|
||||
resource_provider.delete_inventory(resource_class)
|
||||
except (exception.ConcurrentUpdateDetected,
|
||||
exception.InventoryInUse) as exc:
|
||||
raise webob.exc.HTTPConflict(
|
||||
'Unable to delete inventory of class %s: %s' % (
|
||||
resource_class, exc),
|
||||
json_formatter=util.json_error_formatter)
|
||||
|
||||
response = req.response
|
||||
response.status = 204
|
||||
response.content_type = None
|
||||
return response
|
||||
|
||||
|
||||
@webob.dec.wsgify
|
||||
@util.check_accept('application/json')
|
||||
def get_inventories(req):
|
||||
"""GET a list of inventories.
|
||||
|
||||
On success return a 200 with an application/json body representing
|
||||
a collection of inventories.
|
||||
"""
|
||||
context = req.environ['placement.context']
|
||||
uuid = util.wsgi_path_item(req.environ, 'uuid')
|
||||
resource_provider = objects.ResourceProvider.get_by_uuid(
|
||||
context, uuid)
|
||||
inventories = objects.InventoryList.get_all_by_resource_provider_uuid(
|
||||
context, resource_provider.uuid)
|
||||
|
||||
return _send_inventories(req.response, resource_provider, inventories)
|
||||
|
||||
|
||||
@webob.dec.wsgify
|
||||
@util.check_accept('application/json')
|
||||
def get_inventory(req):
|
||||
"""GET one inventory.
|
||||
|
||||
On success return a 200 an application/json body representing one
|
||||
inventory.
|
||||
"""
|
||||
context = req.environ['placement.context']
|
||||
uuid = util.wsgi_path_item(req.environ, 'uuid')
|
||||
resource_class = util.wsgi_path_item(req.environ, 'resource_class')
|
||||
|
||||
resource_provider = objects.ResourceProvider.get_by_uuid(
|
||||
context, uuid)
|
||||
inventory = objects.InventoryList.get_all_by_resource_provider_uuid(
|
||||
context, resource_provider.uuid).find(resource_class)
|
||||
|
||||
if not inventory:
|
||||
raise webob.exc.HTTPNotFound(
|
||||
'No inventory of class %s for %s'
|
||||
% (resource_class, resource_provider.uuid),
|
||||
json_formatter=util.json_error_formatter)
|
||||
|
||||
return _send_inventory(req.response, resource_provider, inventory)
|
||||
|
||||
|
||||
@webob.dec.wsgify
|
||||
@util.require_content('application/json')
|
||||
def set_inventories(req):
|
||||
"""PUT to set all inventory for a resource provider.
|
||||
|
||||
Create, update and delete inventory as required to reset all
|
||||
the inventory.
|
||||
|
||||
If the resource generation is out of sync, return a 409.
|
||||
If an inventory to be deleted is in use, return a 409.
|
||||
If an inventory to be updated would set capacity to exceed existing
|
||||
use, return a 409.
|
||||
If any inventory to be created or updated has settings which are
|
||||
invalid (for example reserved exceeds capacity), return a 400.
|
||||
|
||||
On success return a 200 with an application/json body representing
|
||||
the inventories.
|
||||
"""
|
||||
context = req.environ['placement.context']
|
||||
uuid = util.wsgi_path_item(req.environ, 'uuid')
|
||||
resource_provider = objects.ResourceProvider.get_by_uuid(
|
||||
context, uuid)
|
||||
|
||||
data = _extract_inventories(req.body, PUT_INVENTORY_SCHEMA)
|
||||
if data['resource_provider_generation'] != resource_provider.generation:
|
||||
raise webob.exc.HTTPConflict(
|
||||
'resource provider generation conflict',
|
||||
json_formatter=util.json_error_formatter)
|
||||
|
||||
inv_list = []
|
||||
for inventory_data in data['inventories']:
|
||||
inventory = _make_inventory_object(
|
||||
resource_provider, **inventory_data)
|
||||
inv_list.append(inventory)
|
||||
inventories = objects.InventoryList(objects=inv_list)
|
||||
|
||||
try:
|
||||
resource_provider.set_inventory(inventories)
|
||||
except (exception.ConcurrentUpdateDetected,
|
||||
exception.InventoryInUse,
|
||||
exception.InvalidInventoryNewCapacityExceeded,
|
||||
db_exc.DBDuplicateEntry) as exc:
|
||||
raise webob.exc.HTTPConflict(
|
||||
'update conflict: %s' % exc,
|
||||
json_formatter=util.json_error_formatter)
|
||||
except exception.InvalidInventoryCapacity as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
'Unable to update inventory for resource provider %s: %s'
|
||||
% (resource_provider.uuid, exc),
|
||||
json_formatter=util.json_error_formatter)
|
||||
|
||||
return _send_inventories(req.response, resource_provider, inventories)
|
||||
|
||||
|
||||
@webob.dec.wsgify
|
||||
@util.require_content('application/json')
|
||||
def update_inventory(req):
|
||||
"""PUT to update one inventory.
|
||||
|
||||
If the resource generation is out of sync, return a 409.
|
||||
If the inventory would set capacity to exceed existing use, return
|
||||
a 409.
|
||||
If the inventory has settings which are invalid (for example
|
||||
reserved exceeds capacity), return a 400.
|
||||
|
||||
On success return a 200 with an application/json body representing
|
||||
the inventory.
|
||||
"""
|
||||
context = req.environ['placement.context']
|
||||
uuid = util.wsgi_path_item(req.environ, 'uuid')
|
||||
resource_class = util.wsgi_path_item(req.environ, 'resource_class')
|
||||
|
||||
resource_provider = objects.ResourceProvider.get_by_uuid(
|
||||
context, uuid)
|
||||
|
||||
data = _extract_inventory(req.body, BASE_INVENTORY_SCHEMA)
|
||||
if data['resource_provider_generation'] != resource_provider.generation:
|
||||
raise webob.exc.HTTPConflict(
|
||||
'resource provider generation conflict',
|
||||
json_formatter=util.json_error_formatter)
|
||||
|
||||
data['resource_class'] = resource_class
|
||||
inventory = _make_inventory_object(resource_provider, **data)
|
||||
|
||||
try:
|
||||
resource_provider.update_inventory(inventory)
|
||||
except (exception.ConcurrentUpdateDetected,
|
||||
exception.InvalidInventoryNewCapacityExceeded,
|
||||
db_exc.DBDuplicateEntry) as exc:
|
||||
raise webob.exc.HTTPConflict(
|
||||
'update conflict: %s' % exc,
|
||||
json_formatter=util.json_error_formatter)
|
||||
except exception.InvalidInventoryCapacity as exc:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
'Unable to update inventory for resource provider %s: %s'
|
||||
% (resource_provider.uuid, exc),
|
||||
json_formatter=util.json_error_formatter)
|
||||
|
||||
return _send_inventory(req.response, resource_provider, inventory)
|
@ -58,6 +58,13 @@ def check_accept(*types):
|
||||
return decorator
|
||||
|
||||
|
||||
def inventory_url(environ, resource_provider, resource_class=None):
|
||||
url = '%s/inventories' % resource_provider_url(environ, resource_provider)
|
||||
if resource_class:
|
||||
url = '%s/%s' % (url, resource_class)
|
||||
return url
|
||||
|
||||
|
||||
def json_error_formatter(body, status, title, environ):
|
||||
"""A json_formatter for webob exceptions.
|
||||
|
||||
|
@ -0,0 +1,306 @@
|
||||
fixtures:
|
||||
- APIFixture
|
||||
|
||||
defaults:
|
||||
request_headers:
|
||||
x-auth-token: admin
|
||||
|
||||
tests:
|
||||
- name: inventories for missing provider
|
||||
GET: /resource_providers/7260669a-e3d4-4867-aaa7-683e2ab6958c/inventories
|
||||
status: 404
|
||||
|
||||
- name: post new resource provider
|
||||
POST: /resource_providers
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
name: $ENVIRON['RP_NAME']
|
||||
uuid: $ENVIRON['RP_UUID']
|
||||
status: 201
|
||||
response_headers:
|
||||
location: //resource_providers/[a-f0-9-]+/
|
||||
|
||||
- name: get empty inventories
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
response_json_paths:
|
||||
$.inventories: {}
|
||||
|
||||
- name: post an conflicting capacity inventory
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_class: DISK_GB
|
||||
total: 256
|
||||
reserved: 512
|
||||
status: 400
|
||||
response_strings:
|
||||
- Unable to create inventory for resource provider
|
||||
|
||||
- name: post an inventory
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_class: DISK_GB
|
||||
total: 2048
|
||||
reserved: 512
|
||||
min_unit: 10
|
||||
max_unit: 1024
|
||||
step_size: 10
|
||||
allocation_ratio: 1.0
|
||||
status: 201
|
||||
response_headers:
|
||||
location: $SCHEME://$NETLOC/resource_providers/$ENVIRON['RP_UUID']/inventories/DISK_GB
|
||||
response_json_paths:
|
||||
$.total: 2048
|
||||
$.reserved: 512
|
||||
|
||||
- name: get that inventory
|
||||
GET: $LOCATION
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.resource_provider_generation: 1
|
||||
$.total: 2048
|
||||
$.reserved: 512
|
||||
$.min_unit: 10
|
||||
$.max_unit: 1024
|
||||
$.step_size: 10
|
||||
$.allocation_ratio: 1.0
|
||||
|
||||
- name: modify the inventory
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 1
|
||||
total: 2048
|
||||
reserved: 1024
|
||||
min_unit: 10
|
||||
max_unit: 1024
|
||||
step_size: 10
|
||||
allocation_ratio: 1.0
|
||||
status: 200
|
||||
response_headers:
|
||||
content-type: /application/json/
|
||||
response_json_paths:
|
||||
$.reserved: 1024
|
||||
|
||||
- name: confirm inventory change
|
||||
GET: $LAST_URL
|
||||
response_json_paths:
|
||||
$.resource_provider_generation: 2
|
||||
$.total: 2048
|
||||
$.reserved: 1024
|
||||
|
||||
- name: modify inventory invalid generation
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 5
|
||||
total: 2048
|
||||
status: 409
|
||||
response_strings:
|
||||
- resource provider generation conflict
|
||||
|
||||
- name: modify inventory invalid data
|
||||
desc: This should 400 because reserved is greater than total
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 2
|
||||
total: 2048
|
||||
reserved: 4096
|
||||
min_unit: 10
|
||||
max_unit: 1024
|
||||
step_size: 10
|
||||
allocation_ratio: 1.0
|
||||
status: 400
|
||||
response_strings:
|
||||
- Unable to update inventory for resource provider $ENVIRON['RP_UUID']
|
||||
|
||||
- name: put inventory bad form
|
||||
desc: This should 400 because reserved is greater than total
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
house: red
|
||||
car: blue
|
||||
status: 400
|
||||
response_strings:
|
||||
- JSON does not validate
|
||||
|
||||
- name: post inventory malformed json
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data: '{"foo": }'
|
||||
status: 400
|
||||
response_strings:
|
||||
- Malformed JSON
|
||||
|
||||
- name: post inventory bad syntax schema
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_class: bad_class
|
||||
total: 2048
|
||||
status: 400
|
||||
|
||||
- name: post inventory bad resource class
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_class: NO_CLASS_14
|
||||
total: 2048
|
||||
status: 400
|
||||
|
||||
- name: post inventory duplicated resource class
|
||||
desc: DISK_GB was already created above
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_class: DISK_GB
|
||||
total: 2048
|
||||
status: 409
|
||||
response_strings:
|
||||
- Update conflict
|
||||
|
||||
- name: get list of inventories
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
response_json_paths:
|
||||
$.inventories.DISK_GB.total: 2048
|
||||
$.inventories.DISK_GB.reserved: 1024
|
||||
|
||||
- name: delete the inventory
|
||||
DELETE: /resource_providers/$ENVIRON['RP_UUID']/inventories/DISK_GB
|
||||
status: 204
|
||||
|
||||
- name: get now empty inventories
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
response_json_paths:
|
||||
$.inventories: {}
|
||||
|
||||
- name: post new disk inventory
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_class: DISK_GB
|
||||
total: 1024
|
||||
status: 201
|
||||
|
||||
- name: post new ipv4 address inventory
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_class: IPV4_ADDRESS
|
||||
total: 255
|
||||
reserved: 2
|
||||
status: 201
|
||||
|
||||
- name: list both those inventories
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
response_json_paths:
|
||||
$.inventories.DISK_GB.total: 1024
|
||||
$.inventories.IPV4_ADDRESS.total: 255
|
||||
|
||||
- name: post ipv4 address inventory again
|
||||
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_class: IPV4_ADDRESS
|
||||
total: 255
|
||||
reserved: 2
|
||||
status: 409
|
||||
|
||||
- name: delete inventory
|
||||
DELETE: /resource_providers/$ENVIRON['RP_UUID']/inventories/IPV4_ADDRESS
|
||||
status: 204
|
||||
response_forbidden_headers:
|
||||
- content-type
|
||||
|
||||
- name: delete inventory again
|
||||
DELETE: /resource_providers/$ENVIRON['RP_UUID']/inventories/IPV4_ADDRESS
|
||||
status: 404
|
||||
|
||||
- name: get missing inventory class
|
||||
GET: /resource_providers/$ENVIRON['RP_UUID']/inventories/IPV4_ADDRESS
|
||||
status: 404
|
||||
|
||||
- name: create another resource provider
|
||||
POST: /resource_providers
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
name: disk-network
|
||||
status: 201
|
||||
|
||||
- name: put all inventory
|
||||
PUT: $LOCATION/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 0
|
||||
inventories:
|
||||
- resource_class: IPV4_ADDRESS
|
||||
total: 253
|
||||
- resource_class: DISK_GB
|
||||
total: 1024
|
||||
status: 200
|
||||
response_json_paths:
|
||||
$.inventories.IPV4_ADDRESS.total: 253
|
||||
$.inventories.IPV4_ADDRESS.reserved: 0
|
||||
$.inventories.DISK_GB.total: 1024
|
||||
$.inventories.DISK_GB.allocation_ratio: 1.0
|
||||
|
||||
- name: check both inventory classes
|
||||
GET: $LAST_URL
|
||||
response_json_paths:
|
||||
$.inventories.DISK_GB.total: 1024
|
||||
$.inventories.IPV4_ADDRESS.total: 253
|
||||
|
||||
- name: check one inventory class
|
||||
GET: $LAST_URL/DISK_GB
|
||||
response_json_paths:
|
||||
$.total: 1024
|
||||
|
||||
- name: put all inventory bad generation
|
||||
PUT: /resource_providers/$ENVIRON['RP_UUID']/inventories
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 99
|
||||
inventories:
|
||||
- resource_class: IPV4_ADDRESS
|
||||
total: 253
|
||||
status: 409
|
||||
response_strings:
|
||||
- resource provider generation conflict
|
||||
|
||||
# NOTE(cdent): The generation is 6 now, based on the activity at
|
||||
# the start of this file.
|
||||
- name: put all inventory bad capacity
|
||||
PUT: $LAST_URL
|
||||
request_headers:
|
||||
content-type: application/json
|
||||
data:
|
||||
resource_provider_generation: 6
|
||||
inventories:
|
||||
- resource_class: IPV4_ADDRESS
|
||||
total: 253
|
||||
reserved: 512
|
||||
status: 400
|
||||
response_strings:
|
||||
- Unable to update inventory
|
@ -203,3 +203,18 @@ class TestPlacementURLs(test.NoDBTestCase):
|
||||
% uuidsentinel.rp_uuid)
|
||||
self.assertEqual(expected_url, util.resource_provider_url(
|
||||
environ, self.resource_provider))
|
||||
|
||||
def test_inventories_url(self):
|
||||
environ = {}
|
||||
expected_url = ('/resource_providers/%s/inventories'
|
||||
% uuidsentinel.rp_uuid)
|
||||
self.assertEqual(expected_url, util.inventory_url(
|
||||
environ, self.resource_provider))
|
||||
|
||||
def test_inventory_url(self):
|
||||
resource_class = 'DISK_GB'
|
||||
environ = {}
|
||||
expected_url = ('/resource_providers/%s/inventories/%s'
|
||||
% (uuidsentinel.rp_uuid, resource_class))
|
||||
self.assertEqual(expected_url, util.inventory_url(
|
||||
environ, self.resource_provider, resource_class))
|
||||
|
Loading…
Reference in New Issue
Block a user