placement project_id, user_id in PUT /allocations

This adds project_id and user_id required request parameters as part of
a new microversion 1.8 of the placement API.

Two new fields, for project and user ID, have been added to the
AllocationList object, and the method AllocationList.create_all() has
been changed to ensure that records are written to the consumers,
projects, and users tables when project_id and user_id are not None.

After an upgrade, new allocations will write consumer records and
existing allocations will have corresponding consumer records written
when they are updated as part of the resource tracker periodic task for
updating available resources.

Part of blueprint placement-project-user

Co-Authored-By: Jay Pipes <jaypipes@gmail.com>
Change-Id: I3c3b0cfdd33da87160255ead51a0d9ff73667655
This commit is contained in:
melanie witt
2017-05-28 01:36:07 +00:00
parent 3bd970b30b
commit 3684aeb201
8 changed files with 195 additions and 9 deletions

View File

@@ -12,12 +12,14 @@
"""Placement API handlers for setting and deleting allocations."""
import collections
import copy
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import encodeutils
import webob
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement import util
from nova.api.openstack.placement import wsgi_wrapper
from nova import exception
@@ -69,6 +71,15 @@ ALLOCATION_SCHEMA = {
"additionalProperties": False
}
ALLOCATION_SCHEMA_V1_8 = copy.deepcopy(ALLOCATION_SCHEMA)
ALLOCATION_SCHEMA_V1_8['properties']['project_id'] = {'type': 'string',
'minLength': 1,
'maxLength': 255}
ALLOCATION_SCHEMA_V1_8['properties']['user_id'] = {'type': 'string',
'minLength': 1,
'maxLength': 255}
ALLOCATION_SCHEMA_V1_8['required'].extend(['project_id', 'user_id'])
def _allocations_dict(allocations, key_fetcher, resource_provider=None):
"""Turn allocations into a dict of resources keyed by key_fetcher."""
@@ -197,12 +208,10 @@ def list_for_resource_provider(req):
return req.response
@wsgi_wrapper.PlacementWsgify
@util.require_content('application/json')
def set_allocations(req):
def _set_allocations(req, schema):
context = req.environ['placement.context']
consumer_uuid = util.wsgi_path_item(req.environ, 'consumer_uuid')
data = util.extract_json(req.body, ALLOCATION_SCHEMA)
data = util.extract_json(req.body, schema)
allocation_data = data['allocations']
# If the body includes an allocation for a resource provider
@@ -229,7 +238,12 @@ def set_allocations(req):
used=resources[resource_class])
allocation_objects.append(allocation)
allocations = objects.AllocationList(context, objects=allocation_objects)
allocations = objects.AllocationList(
context,
objects=allocation_objects,
project_id=data.get('project_id'),
user_id=data.get('user_id'),
)
try:
allocations.create_all()
@@ -257,6 +271,20 @@ def set_allocations(req):
return req.response
@wsgi_wrapper.PlacementWsgify
@microversion.version_handler('1.0', '1.7')
@util.require_content('application/json')
def set_allocations(req):
return _set_allocations(req, ALLOCATION_SCHEMA)
@wsgi_wrapper.PlacementWsgify # noqa
@microversion.version_handler('1.8')
@util.require_content('application/json')
def set_allocations(req):
return _set_allocations(req, ALLOCATION_SCHEMA_V1_8)
@wsgi_wrapper.PlacementWsgify
def delete_allocations(req):
context = req.environ['placement.context']

View File

@@ -44,6 +44,8 @@ VERSIONS = [
'1.6', # Adds /traits and /resource_providers{uuid}/traits resource
# endpoints
'1.7', # PUT /resource_classes/{name} is bodiless create or update
'1.8', # Adds 'project_id' and 'user_id' required request parameters to
# PUT /allocations
]

View File

@@ -1426,7 +1426,7 @@ class PlacementFixture(fixtures.Fixture):
headers={'x-auth-token': self.token},
raise_exc=False)
def _fake_put(self, *args):
def _fake_put(self, *args, **kwargs):
(url, data) = args[1:]
# NOTE(sdague): using json= instead of data= sets the
# media type to application/json for us. Placement API is

View File

@@ -77,6 +77,8 @@ class APIFixture(fixture.GabbiFixture):
os.environ['RP_UUID'] = uuidutils.generate_uuid()
os.environ['RP_NAME'] = uuidutils.generate_uuid()
os.environ['CUSTOM_RES_CLASS'] = 'CUSTOM_IRON_NFV'
os.environ['PROJECT_ID'] = uuidutils.generate_uuid()
os.environ['USER_ID'] = uuidutils.generate_uuid()
def stop_fixture(self):
self.api_db_fixture.cleanup()

View File

@@ -0,0 +1,152 @@
fixtures:
- APIFixture
defaults:
request_headers:
x-auth-token: admin
accept: application/json
OpenStack-API-Version: placement 1.8
tests:
- name: put an allocation no project_id or user_id
PUT: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958
request_headers:
content-type: application/json
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
status: 400
response_strings:
- Failed validating 'required' in schema
- name: put an allocation no project_id
PUT: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958
request_headers:
content-type: application/json
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
user_id: $ENVIRON['USER_ID']
status: 400
response_strings:
- Failed validating 'required' in schema
- name: put an allocation no user_id
PUT: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958
request_headers:
content-type: application/json
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
project_id: $ENVIRON['PROJECT_ID']
status: 400
response_strings:
- Failed validating 'required' in schema
- name: put an allocation project_id less than min length
PUT: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958
request_headers:
content-type: application/json
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
project_id: ""
user_id: $ENVIRON['USER_ID']
status: 400
response_strings:
- "Failed validating 'minLength'"
- name: put an allocation user_id less than min length
PUT: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958
request_headers:
content-type: application/json
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
project_id: $ENVIRON['PROJECT_ID']
user_id: ""
status: 400
response_strings:
- "Failed validating 'minLength'"
- name: put an allocation project_id exceeds max length
PUT: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958
request_headers:
content-type: application/json
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
project_id: 78725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b1
user_id: $ENVIRON['USER_ID']
status: 400
response_strings:
- "Failed validating 'maxLength'"
- name: put an allocation user_id exceeds max length
PUT: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958
request_headers:
content-type: application/json
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
project_id: $ENVIRON['PROJECT_ID']
user_id: 78725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b178725f09-5c01-4c9e-97a5-98d75e1e32b1
status: 400
response_strings:
- "Failed validating 'maxLength'"
- name: create the resource provider
POST: /resource_providers
request_headers:
content-type: application/json
data:
name: $ENVIRON['RP_NAME']
uuid: $ENVIRON['RP_UUID']
status: 201
- name: post some inventory
POST: /resource_providers/$ENVIRON['RP_UUID']/inventories
request_headers:
content-type: application/json
data:
resource_class: DISK_GB
total: 2048
min_unit: 10
max_unit: 1024
status: 201
- name: put an allocation
PUT: /allocations/599ffd2d-526a-4b2e-8683-f13ad25f9958
request_headers:
content-type: application/json
data:
allocations:
- resource_provider:
uuid: $ENVIRON['RP_UUID']
resources:
DISK_GB: 10
project_id: $ENVIRON['PROJECT_ID']
user_id: $ENVIRON['USER_ID']
status: 204

View File

@@ -39,13 +39,13 @@ tests:
response_json_paths:
$.errors[0].title: Not Acceptable
- name: latest microversion is 1.7
- name: latest microversion is 1.8
GET: /
request_headers:
openstack-api-version: placement latest
response_headers:
vary: /OpenStack-API-Version/
openstack-api-version: placement 1.7
openstack-api-version: placement 1.8
- name: other accept header bad version
GET: /

View File

@@ -76,6 +76,8 @@ class SchedulerReportClientTests(test.TestCase):
self.instance_uuid = uuids.inst
self.instance = objects.Instance(
uuid=self.instance_uuid,
project_id = uuids.project,
user_id = uuids.user,
flavor=objects.Flavor(root_gb=10,
swap=1,
ephemeral_gb=100,

View File

@@ -74,7 +74,7 @@ class TestMicroversionIntersection(test.NoDBTestCase):
# 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 = 12
TOTAL_VERSIONED_METHODS = 13
def test_methods_versioned(self):
methods_data = microversion.VERSIONED_METHODS