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:
@@ -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']
|
||||
|
||||
@@ -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
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -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: /
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user