Add API schema for v2.1/v3 config_drive extension

By defining the API schema, it is possible to separate the validation
code from the API method. The API method can be more simple.
In addition, a response of API validation error can be consistent for
the whole Nova API.

Partially implements blueprint v3-api-schema

Change-Id: Ia0681941c5449051c90425348466bd7dc44c2e45
This commit is contained in:
Eiichi Aikawa
2014-01-23 14:37:31 +09:00
parent 46116eed75
commit 04aeabe7c0
3 changed files with 52 additions and 43 deletions

View File

@@ -15,6 +15,8 @@
"""Config Drive extension.""" """Config Drive extension."""
from nova.api.openstack.compute.schemas.v3 import config_drive as \
schema_config_drive
from nova.api.openstack import extensions from nova.api.openstack import extensions
from nova.api.openstack import wsgi from nova.api.openstack import wsgi
@@ -68,3 +70,6 @@ class ConfigDrive(extensions.V3APIExtensionBase):
def server_create(self, server_dict, create_kwargs): def server_create(self, server_dict, create_kwargs):
create_kwargs['config_drive'] = server_dict.get(ATTRIBUTE_NAME) create_kwargs['config_drive'] = server_dict.get(ATTRIBUTE_NAME)
def get_server_create_schema(self):
return schema_config_drive.server_create

View File

@@ -0,0 +1,19 @@
# Copyright 2014 NEC Corporation. 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.
from nova.api.validation import parameter_types
server_create = {
'os-config-drive:config_drive': parameter_types.boolean,
}

View File

@@ -25,6 +25,7 @@ from nova.api.openstack.compute.plugins.v3 import servers
from nova.compute import api as compute_api from nova.compute import api as compute_api
from nova.compute import flavors from nova.compute import flavors
from nova import db from nova import db
from nova import exception
from nova.network import manager from nova.network import manager
from nova.openstack.common import jsonutils from nova.openstack.common import jsonutils
from nova import test from nova import test
@@ -202,7 +203,7 @@ class ServersControllerCreateTest(test.TestCase):
self._test_create_extra(params, self._test_create_extra(params,
override_controller=self.no_config_drive_controller) override_controller=self.no_config_drive_controller)
def test_create_instance_with_config_drive(self): def _create_instance_body_of_config_drive(self, param):
def create(*args, **kwargs): def create(*args, **kwargs):
self.assertIn('config_drive', kwargs) self.assertIn('config_drive', kwargs)
return old_create(*args, **kwargs) return old_create(*args, **kwargs)
@@ -220,7 +221,7 @@ class ServersControllerCreateTest(test.TestCase):
'hello': 'world', 'hello': 'world',
'open': 'stack', 'open': 'stack',
}, },
config_drive.ATTRIBUTE_NAME: "true", config_drive.ATTRIBUTE_NAME: param,
}, },
} }
@@ -228,55 +229,39 @@ class ServersControllerCreateTest(test.TestCase):
req.method = 'POST' req.method = 'POST'
req.body = jsonutils.dumps(body) req.body = jsonutils.dumps(body)
req.headers["content-type"] = "application/json" req.headers["content-type"] = "application/json"
res = self.controller.create(req, body=body).obj
return req, body
def test_create_instance_with_config_drive(self):
param = True
req, body = self._create_instance_body_of_config_drive(param)
res = self.controller.create(req, body=body).obj
server = res['server']
self.assertEqual(FAKE_UUID, server['id'])
def test_create_instance_with_config_drive_as_boolean_string(self):
param = 'false'
req, body = self._create_instance_body_of_config_drive(param)
res = self.controller.create(req, body=body).obj
server = res['server'] server = res['server']
self.assertEqual(FAKE_UUID, server['id']) self.assertEqual(FAKE_UUID, server['id'])
def test_create_instance_with_bad_config_drive(self): def test_create_instance_with_bad_config_drive(self):
image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' param = 12345
flavor_ref = 'http://localhost/v3/flavors/3' req, body = self._create_instance_body_of_config_drive(param)
body = { self.assertRaises(exception.ValidationError,
'server': {
'name': 'config_drive_test',
'image_ref': image_href,
'flavor_ref': flavor_ref,
'metadata': {
'hello': 'world',
'open': 'stack',
},
config_drive.ATTRIBUTE_NAME: image_href,
},
}
req = fakes.HTTPRequestV3.blank('/servers')
req.method = 'POST'
req.body = jsonutils.dumps(body)
req.headers["content-type"] = "application/json"
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, req, body=body) self.controller.create, req, body=body)
def test_create_instance_without_config_drive(self): def test_create_instance_without_config_drive(self):
image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' param = True
flavor_ref = 'http://localhost/v3/flavors/3' req, body = self._create_instance_body_of_config_drive(param)
body = { del body['server'][config_drive.ATTRIBUTE_NAME]
'server': {
'name': 'config_drive_test',
'image_ref': image_href,
'flavor_ref': flavor_ref,
'metadata': {
'hello': 'world',
'open': 'stack',
},
},
}
req = fakes.HTTPRequestV3.blank('/servers')
req.method = 'POST'
req.body = jsonutils.dumps(body)
req.headers["content-type"] = "application/json"
res = self.controller.create(req, body=body).obj res = self.controller.create(req, body=body).obj
server = res['server'] server = res['server']
self.assertEqual(FAKE_UUID, server['id']) self.assertEqual(FAKE_UUID, server['id'])
def test_create_instance_with_empty_config_drive(self):
param = ''
req, body = self._create_instance_body_of_config_drive(param)
self.assertRaises(exception.ValidationError,
self.controller.create, req, body=body)