Add support for usages in the placement API

GET /resources_providers/{uuid}/usages returns a dictionary of resource
usage by resource class.

Change-Id: I3ad0d16594301635966bd9e9352bc3f1f29dab14
Partially-Implements: blueprint generic-resource-pools
This commit is contained in:
Chris Dent 2016-08-18 13:19:24 +00:00
parent 9d61e0f1ed
commit b0787f57a2
5 changed files with 151 additions and 0 deletions

View File

@ -29,6 +29,7 @@ import webob
from nova.api.openstack.placement.handlers import inventory from nova.api.openstack.placement.handlers import inventory
from nova.api.openstack.placement.handlers import resource_provider from nova.api.openstack.placement.handlers import resource_provider
from nova.api.openstack.placement.handlers import root from nova.api.openstack.placement.handlers import root
from nova.api.openstack.placement.handlers import usage
from nova.api.openstack.placement import util from nova.api.openstack.placement import util
from nova import exception from nova import exception
@ -61,6 +62,9 @@ ROUTE_DECLARATIONS = {
'PUT': inventory.update_inventory, 'PUT': inventory.update_inventory,
'DELETE': inventory.delete_inventory 'DELETE': inventory.delete_inventory
}, },
'/resource_providers/{uuid}/usages': {
'GET': usage.list_usages
},
} }

View File

@ -0,0 +1,55 @@
# 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 handlers for usage information."""
from oslo_serialization import jsonutils
import webob
from nova.api.openstack.placement import util
from nova import objects
def _serialize_usages(resource_provider, usage):
usage_dict = {resource.resource_class: resource.usage
for resource in usage}
return {'resource_provider_generation': resource_provider.generation,
'usages': usage_dict}
@webob.dec.wsgify
@util.check_accept('application/json')
def list_usages(req):
"""GET a dictionary of resource provider usage by resource class.
If the resource provider does not exist return a 404.
On success return a 200 with an application/json representation of
the usage dictionary.
"""
context = req.environ['placement.context']
uuid = util.wsgi_path_item(req.environ, 'uuid')
# Resource provider object needed for two things: If it is
# NotFound we'll get a 404 here, which needs to happen because
# get_all_by_resource_provider_uuid can return an empty list.
# It is also needed for the generation, used in the outgoing
# representation.
resource_provider = objects.ResourceProvider.get_by_uuid(
context, uuid)
usage = objects.UsageList.get_all_by_resource_provider_uuid(
context, uuid)
response = req.response
response.body = jsonutils.dumps(
_serialize_usages(resource_provider, usage))
req.response.content_type = 'application/json'
return req.response

View File

@ -18,6 +18,8 @@ from oslo_utils import uuidutils
from nova.api.openstack.placement import deploy from nova.api.openstack.placement import deploy
from nova import conf from nova import conf
from nova import config from nova import config
from nova import context
from nova import objects
from nova.tests import fixtures from nova.tests import fixtures
@ -66,3 +68,34 @@ class APIFixture(fixture.GabbiFixture):
self.main_db_fixture.cleanup() self.main_db_fixture.cleanup()
if self.conf: if self.conf:
self.conf.reset() self.conf.reset()
class AllocationFixture(APIFixture):
"""An APIFixture that has some pre-made Allocations."""
def start_fixture(self):
super(AllocationFixture, self).start_fixture()
self.context = context.get_admin_context()
# Stealing from the super
rp_name = os.environ['RP_NAME']
rp_uuid = os.environ['RP_UUID']
rp = objects.ResourceProvider(
self.context, name=rp_name, uuid=rp_uuid)
rp.create()
inventory = objects.Inventory(
self.context, resource_provider=rp,
resource_class='DISK_GB', total=2048)
inventory.obj_set_defaults()
rp.add_inventory(inventory)
allocation = objects.Allocation(
self.context, resource_provider=rp,
resource_class='DISK_GB',
consumer_id=uuidutils.generate_uuid(),
used=512)
allocation.create()
allocation = objects.Allocation(
self.context, resource_provider=rp,
resource_class='DISK_GB',
consumer_id=uuidutils.generate_uuid(),
used=512)
allocation.create()

View File

@ -0,0 +1,34 @@
# More interesting tests for usages are in with_allocations
fixtures:
- APIFixture
defaults:
request_headers:
x-auth-token: admin
tests:
- name: fail to get usages for missing provider
GET: /resource_providers/fae14fa3-4b43-498c-a33c-4a1d00edb577/usages
status: 404
- name: create provider
POST: /resource_providers
request_headers:
content-type: application/json
data:
name: a name
status: 201
- name: check provider exists
GET: $LOCATION
response_json_paths:
name: a name
- name: get empty usages
GET: $LAST_URL/usages
request_headers:
content-type: application/json
response_json_paths:
usages: {}

View File

@ -0,0 +1,25 @@
fixtures:
- AllocationFixture
defaults:
request_headers:
x-auth-token: admin
tests:
- name: confirm inventories
GET: /resource_providers/$ENVIRON['RP_UUID']/inventories
response_json_paths:
$.inventories.DISK_GB.total: 2048
$.inventories.DISK_GB.reserved: 0
- name: get usages
GET: /resource_providers/$ENVIRON['RP_UUID']/usages
response_headers:
# use a regex here because charset, which is not only not
# required but superfluous, is present
content-type: /application/json/
response_json_paths:
$.resource_provider_generation: 1
$.usages.DISK_GB: 1024