Merge "V3 jsonschema validation: scheduler_hints"

This commit is contained in:
Zuul 2018-05-27 22:07:56 +00:00 committed by Gerrit Code Review
commit 5dcf4f52ad
3 changed files with 131 additions and 13 deletions

View File

@ -12,32 +12,28 @@
# License for the specific language governing permissions and limitations
# under the License.
import webob.exc
from cinder.api import extensions
from cinder.api.openstack import wsgi
from cinder.i18n import _
from cinder.api.schemas import scheduler_hints
from cinder.api import validation
class SchedulerHintsController(wsgi.Controller):
@staticmethod
def _extract_scheduler_hints(body):
@validation.schema(scheduler_hints.create)
def _extract_scheduler_hints(self, req, body):
hints = {}
attr = '%s:scheduler_hints' % Scheduler_hints.alias
try:
if attr in body:
hints.update(body[attr])
except ValueError:
msg = _("Malformed scheduler_hints attribute")
raise webob.exc.HTTPBadRequest(explanation=msg)
if body.get(attr) is not None:
hints.update(body.get(attr))
return hints
@wsgi.extends
def create(self, req, body):
hints = self._extract_scheduler_hints(body)
attr = '%s:scheduler_hints' % Scheduler_hints.alias
scheduler_hints_body = dict.fromkeys((attr,), body.get(attr))
hints = self._extract_scheduler_hints(req, body=scheduler_hints_body)
if 'volume' in body:
body['volume']['scheduler_hints'] = hints

View File

@ -0,0 +1,79 @@
# Copyright (C) 2018 NTT DATA
# All Rights Reserved.
#
# 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.
"""
Schema for V3 scheduler_hints API.
"""
from cinder.api.validation import parameter_types
create = {
'type': 'object',
'properties': {
'OS-SCH-HNT:scheduler_hints': {
'type': ['object', 'null'],
'properties': {
'local_to_instance': parameter_types.optional_uuid,
'different_host': {
# NOTE: The value of 'different_host' is the set of volume
# uuids where a new volume is scheduled on a different
# host. A user can specify one volume as string parameter
# and should specify multiple volumes as array parameter
# instead.
'oneOf': [
{
'type': 'string',
'format': 'uuid'
},
{
'type': 'array',
'items': parameter_types.uuid,
'uniqueItems': True,
}
]
},
'same_host': {
# NOTE: The value of 'same_host' is the set of volume
# uuids where a new volume is scheduled on the same host.
# A user can specify one volume as string parameter and
# should specify multiple volumes as array parameter
# instead.
'oneOf': [
{
'type': 'string',
'format': 'uuid'
},
{
'type': 'array',
'items': parameter_types.uuid,
'uniqueItems': True,
}
]
},
'query': {
# NOTE: The value of 'query' is converted to dict data with
# jsonutils.loads() and used for filtering hosts.
'type': ['string', 'object'],
},
},
# NOTE: As this Mail:
# http://lists.openstack.org/pipermail/openstack-dev/2015-June/067996.html
# pointed out the limit the scheduler-hints in the API is
# problematic. So relax it.
'additionalProperties': True
},
},
}

View File

@ -15,6 +15,7 @@
import datetime
import ddt
from oslo_serialization import jsonutils
from six.moves import http_client
@ -30,6 +31,7 @@ from cinder.tests.unit import fake_constants as fake
UUID = fakes.FAKE_UUID
@ddt.ddt
class SchedulerHintsTestCase(test.TestCase):
def setUp(self):
@ -104,3 +106,44 @@ class SchedulerHintsTestCase(test.TestCase):
req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, res.status_int)
@ddt.data({'local_to_instance': UUID},
{'local_to_instance': None},
{'different_host': [fake.UUID1, fake.UUID2]},
{'same_host': UUID},
{'same_host': [fake.UUID1, fake.UUID2]},
{'fake_key': 'fake_value'},
{'query': 'query_testing'},
{'query': {}},
None)
def test_scheduler_hints_with_valid_body(self, value):
req = fakes.HTTPRequest.blank('/v2/%s/volumes' % fake.PROJECT_ID)
req.method = 'POST'
req.content_type = 'application/json'
body = {'volume': {'size': 1},
'OS-SCH-HNT:scheduler_hints': value}
req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, res.status_int)
@ddt.data({'local_to_instance': 'local_to_instance'},
{'different_host': 'different_host'},
{'different_host': ['different_host']},
{'different_host': [UUID, UUID]},
{'same_host': 'same_host'},
{'same_host': ['same_host']},
{'same_host': [UUID, UUID]},
{'query': None},
{'scheduler_hints'})
def test_scheduler_hints_with_invalid_body(self, value):
req = fakes.HTTPRequest.blank('/v2/%s/volumes' % fake.PROJECT_ID)
req.method = 'POST'
req.content_type = 'application/json'
body = {'volume': {'size': 1},
'OS-SCH-HNT:scheduler_hints': value}
req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, res.status_int)