From 8423a9cd1539d1f7621f6b703c8782ce09846868 Mon Sep 17 00:00:00 2001 From: He Jie Xu Date: Wed, 1 Apr 2015 00:47:14 +0000 Subject: [PATCH] Add json-schema for v2.1 fixed-ips This patch adds json-schema for v2.1 fixed-ips. The json-schame limits the input for actions reserve/unreserve. Also fixed the API sample file at same time and add parameter_type none. Co-Authored-By: Ken'ichi Ohmichi Change-Id: Id2d0416c3ccc2a50d3cb66bed8747082f98fb194 Closes-Bug: #1438480 --- .../os-fixed-ips/fixedip-post-req.json | 2 +- .../os-fixed-ips/fixedip-post-req.json | 2 +- .../openstack/compute/plugins/v3/fixed_ips.py | 4 ++ .../openstack/compute/schemas/v3/fixed_ips.py | 36 ++++++++++++++++ nova/api/validation/parameter_types.py | 5 +++ .../os-fixed-ips/fixedip-post-req.json.tpl | 2 +- nova/tests/functional/test_api_samples.py | 4 +- .../os-fixed-ips/fixedip-post-req.json.tpl | 2 +- nova/tests/functional/v3/test_fixed_ips.py | 4 +- .../compute/contrib/test_fixed_ips.py | 16 ++++---- nova/tests/unit/test_api_validation.py | 41 +++++++++++++++++++ 11 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 nova/api/openstack/compute/schemas/v3/fixed_ips.py diff --git a/doc/api_samples/os-fixed-ips/fixedip-post-req.json b/doc/api_samples/os-fixed-ips/fixedip-post-req.json index cf8ba0e0bdcf..fd8ee48bf50d 100644 --- a/doc/api_samples/os-fixed-ips/fixedip-post-req.json +++ b/doc/api_samples/os-fixed-ips/fixedip-post-req.json @@ -1,3 +1,3 @@ { - "reserve": "None" + "reserve": null } \ No newline at end of file diff --git a/doc/v3/api_samples/os-fixed-ips/fixedip-post-req.json b/doc/v3/api_samples/os-fixed-ips/fixedip-post-req.json index cf8ba0e0bdcf..fd8ee48bf50d 100644 --- a/doc/v3/api_samples/os-fixed-ips/fixedip-post-req.json +++ b/doc/v3/api_samples/os-fixed-ips/fixedip-post-req.json @@ -1,3 +1,3 @@ { - "reserve": "None" + "reserve": null } \ No newline at end of file diff --git a/nova/api/openstack/compute/plugins/v3/fixed_ips.py b/nova/api/openstack/compute/plugins/v3/fixed_ips.py index 7490b9575814..3d710d881524 100644 --- a/nova/api/openstack/compute/plugins/v3/fixed_ips.py +++ b/nova/api/openstack/compute/plugins/v3/fixed_ips.py @@ -15,8 +15,10 @@ import webob import webob.exc +from nova.api.openstack.compute.schemas.v3 import fixed_ips from nova.api.openstack import extensions from nova.api.openstack import wsgi +from nova.api import validation from nova import exception from nova.i18n import _ from nova import objects @@ -60,6 +62,7 @@ class FixedIPController(wsgi.Controller): @wsgi.response(202) @extensions.expected_errors((400, 404)) + @validation.schema(fixed_ips.reserve) @wsgi.action('reserve') def reserve(self, req, id, body): context = req.environ['nova.context'] @@ -69,6 +72,7 @@ class FixedIPController(wsgi.Controller): @wsgi.response(202) @extensions.expected_errors((400, 404)) + @validation.schema(fixed_ips.unreserve) @wsgi.action('unreserve') def unreserve(self, req, id, body): context = req.environ['nova.context'] diff --git a/nova/api/openstack/compute/schemas/v3/fixed_ips.py b/nova/api/openstack/compute/schemas/v3/fixed_ips.py new file mode 100644 index 000000000000..d0ee2b2eca8a --- /dev/null +++ b/nova/api/openstack/compute/schemas/v3/fixed_ips.py @@ -0,0 +1,36 @@ +# Copyright 2015 Intel 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 + + +reserve = { + 'type': 'object', + 'properties': { + 'reserve': parameter_types.none, + }, + 'required': ['reserve'], + 'additionalProperties': False, +} + + +unreserve = { + 'type': 'object', + 'properties': { + 'unreserve': parameter_types.none, + }, + 'required': ['unreserve'], + 'additionalProperties': False, +} diff --git a/nova/api/validation/parameter_types.py b/nova/api/validation/parameter_types.py index 501032a7463b..aba2d0c6abba 100644 --- a/nova/api/validation/parameter_types.py +++ b/nova/api/validation/parameter_types.py @@ -62,6 +62,11 @@ boolean = { } +none = { + 'enum': ['None', None, {}] +} + + positive_integer = { 'type': ['integer', 'string'], 'pattern': '^[0-9]*$', 'minimum': 1 diff --git a/nova/tests/functional/api_samples/os-fixed-ips/fixedip-post-req.json.tpl b/nova/tests/functional/api_samples/os-fixed-ips/fixedip-post-req.json.tpl index 85ae4890add0..4a962d98163e 100644 --- a/nova/tests/functional/api_samples/os-fixed-ips/fixedip-post-req.json.tpl +++ b/nova/tests/functional/api_samples/os-fixed-ips/fixedip-post-req.json.tpl @@ -1,3 +1,3 @@ { - "reserve": "%(reserve)s" + "reserve": null } diff --git a/nova/tests/functional/test_api_samples.py b/nova/tests/functional/test_api_samples.py index 27d390a06487..81e243f956d2 100644 --- a/nova/tests/functional/test_api_samples.py +++ b/nova/tests/functional/test_api_samples.py @@ -1204,10 +1204,8 @@ class FixedIpJsonTest(ApiSampleTestBaseV2): def test_fixed_ip_reserve(self): # Reserve a Fixed IP. - project = {'reserve': None} response = self._do_post('os-fixed-ips/192.168.1.1/action', - 'fixedip-post-req', - project) + 'fixedip-post-req', {}) self.assertEqual(response.status_code, 202) self.assertEqual(response.content, "") diff --git a/nova/tests/functional/v3/api_samples/os-fixed-ips/fixedip-post-req.json.tpl b/nova/tests/functional/v3/api_samples/os-fixed-ips/fixedip-post-req.json.tpl index 85ae4890add0..4a962d98163e 100644 --- a/nova/tests/functional/v3/api_samples/os-fixed-ips/fixedip-post-req.json.tpl +++ b/nova/tests/functional/v3/api_samples/os-fixed-ips/fixedip-post-req.json.tpl @@ -1,3 +1,3 @@ { - "reserve": "%(reserve)s" + "reserve": null } diff --git a/nova/tests/functional/v3/test_fixed_ips.py b/nova/tests/functional/v3/test_fixed_ips.py index fc0110ec0ba1..af95076a6717 100644 --- a/nova/tests/functional/v3/test_fixed_ips.py +++ b/nova/tests/functional/v3/test_fixed_ips.py @@ -80,10 +80,8 @@ class FixedIpTest(test_servers.ServersSampleBase): def test_fixed_ip_reserve(self): # Reserve a Fixed IP. - project = {'reserve': None} response = self._do_post('os-fixed-ips/192.168.1.1/action', - 'fixedip-post-req', - project) + 'fixedip-post-req', {}) self.assertEqual(response.status_code, 202) self.assertEqual(response.content, "") diff --git a/nova/tests/unit/api/openstack/compute/contrib/test_fixed_ips.py b/nova/tests/unit/api/openstack/compute/contrib/test_fixed_ips.py index 2e971ff2d826..8a71d7430a5d 100644 --- a/nova/tests/unit/api/openstack/compute/contrib/test_fixed_ips.py +++ b/nova/tests/unit/api/openstack/compute/contrib/test_fixed_ips.py @@ -168,7 +168,7 @@ class FixedIpTestV21(test.NoDBTestCase): body = {'reserve': None} req = fakes.HTTPRequest.blank('%s/192.168.1.1/action' % self.url) action = self._get_reserve_action() - result = action(req, "192.168.1.1", body) + result = action(req, "192.168.1.1", body=body) self._assert_equal(result or action, 202) self.assertEqual(fake_fixed_ips[0]['reserved'], True) @@ -179,7 +179,7 @@ class FixedIpTestV21(test.NoDBTestCase): action = self._get_reserve_action() self.assertRaises(webob.exc.HTTPNotFound, action, req, - '10.0.0.1', body) + '10.0.0.1', body=body) def test_fixed_ip_reserve_invalid_ip_address(self): body = {'reserve': None} @@ -187,7 +187,7 @@ class FixedIpTestV21(test.NoDBTestCase): action = self._get_reserve_action() self.assertRaises(webob.exc.HTTPBadRequest, - action, req, 'inv.ali.d.ip', body) + action, req, 'inv.ali.d.ip', body=body) def test_fixed_ip_reserve_deleted_ip(self): body = {'reserve': None} @@ -195,14 +195,14 @@ class FixedIpTestV21(test.NoDBTestCase): req = fakes.HTTPRequest.blank('%s/10.0.0.2/action' % self.url) self.assertRaises(webob.exc.HTTPNotFound, action, req, - '10.0.0.2', body) + '10.0.0.2', body=body) def test_fixed_ip_unreserve(self): fake_fixed_ips[0]['reserved'] = True body = {'unreserve': None} req = fakes.HTTPRequest.blank('%s/192.168.1.1/action' % self.url) action = self._get_unreserve_action() - result = action(req, "192.168.1.1", body) + result = action(req, "192.168.1.1", body=body) self._assert_equal(result or action, 202) self.assertEqual(fake_fixed_ips[0]['reserved'], False) @@ -213,21 +213,21 @@ class FixedIpTestV21(test.NoDBTestCase): action = self._get_unreserve_action() self.assertRaises(webob.exc.HTTPNotFound, action, req, - '10.0.0.1', body) + '10.0.0.1', body=body) def test_fixed_ip_unreserve_invalid_ip_address(self): body = {'unreserve': None} req = fakes.HTTPRequest.blank('%s/inv.ali.d.ip/action' % self.url) action = self._get_unreserve_action() self.assertRaises(webob.exc.HTTPBadRequest, - action, req, 'inv.ali.d.ip', body) + action, req, 'inv.ali.d.ip', body=body) def test_fixed_ip_unreserve_deleted_ip(self): body = {'unreserve': None} req = fakes.HTTPRequest.blank('%s/10.0.0.2/action' % self.url) action = self._get_unreserve_action() self.assertRaises(webob.exc.HTTPNotFound, action, req, - '10.0.0.2', body) + '10.0.0.2', body=body) class FixedIpTestV2(FixedIpTestV21): diff --git a/nova/tests/unit/test_api_validation.py b/nova/tests/unit/test_api_validation.py index 9fc8cbf10205..cc1011581eba 100644 --- a/nova/tests/unit/test_api_validation.py +++ b/nova/tests/unit/test_api_validation.py @@ -621,6 +621,47 @@ class NameTestCase(APIValidationTestCase): pass +class NoneTypeTestCase(APIValidationTestCase): + + def setUp(self): + super(NoneTypeTestCase, self).setUp() + schema = { + 'type': 'object', + 'properties': { + 'foo': parameter_types.none + } + } + + @validation.schema(request_body_schema=schema) + def post(req, body): + return 'Validation succeeded.' + + self.post = post + + def test_validate_none(self): + self.assertEqual('Validation succeeded.', + self.post(body={'foo': 'None'}, + req=FakeRequest())) + self.assertEqual('Validation succeeded.', + self.post(body={'foo': None}, + req=FakeRequest())) + self.assertEqual('Validation succeeded.', + self.post(body={'foo': {}}, + req=FakeRequest())) + + def test_validate_none_fails(self): + detail = ("Invalid input for field/attribute foo. Value: ." + " '' is not one of ['None', None, {}]") + self.check_validation_error(self.post, body={'foo': ''}, + expected_detail=detail) + + detail = ("Invalid input for field/attribute foo. Value: " + "{'key': 'val'}. {'key': 'val'} is not one of " + "['None', None, {}]") + self.check_validation_error(self.post, body={'foo': {'key': 'val'}}, + expected_detail=detail) + + class TcpUdpPortTestCase(APIValidationTestCase): def setUp(self):