From e504b765083a3892cf9e45001c7eb55c5eaad1e9 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 16 Feb 2024 19:29:09 +0000 Subject: [PATCH] api: Remove FlavorManageController This is an odd child, registering standard REST operations as actions (in the '/action' API sense of the term). There's no reason for this delineation these days so simply remove it. This makes auto-generation much easier down the road. Change-Id: Ia45013fc988acb9517aea42c3caa1fa45d63892e Signed-off-by: Stephen Finucane --- nova/api/openstack/compute/flavor_manage.py | 127 ------------------ nova/api/openstack/compute/flavors.py | 96 +++++++++++++ nova/api/openstack/compute/routes.py | 2 - .../compute/schemas/flavor_manage.py | 102 -------------- nova/api/openstack/compute/schemas/flavors.py | 86 ++++++++++++ .../openstack/compute/test_flavor_manage.py | 40 +++--- .../unit/policies/test_flavor_extra_specs.py | 7 +- .../tests/unit/policies/test_flavor_manage.py | 10 +- 8 files changed, 210 insertions(+), 260 deletions(-) delete mode 100644 nova/api/openstack/compute/flavor_manage.py delete mode 100644 nova/api/openstack/compute/schemas/flavor_manage.py diff --git a/nova/api/openstack/compute/flavor_manage.py b/nova/api/openstack/compute/flavor_manage.py deleted file mode 100644 index 5b0b051783..0000000000 --- a/nova/api/openstack/compute/flavor_manage.py +++ /dev/null @@ -1,127 +0,0 @@ -# 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. - -import webob - -from nova.api.openstack import api_version_request -from nova.api.openstack.compute.schemas import flavor_manage -from nova.api.openstack.compute.views import flavors as flavors_view -from nova.api.openstack import wsgi -from nova.api import validation -from nova.compute import flavors -from nova import exception -from nova import objects -from nova.policies import flavor_extra_specs as fes_policies -from nova.policies import flavor_manage as fm_policies - - -class FlavorManageController(wsgi.Controller): - """The Flavor Lifecycle API controller for the OpenStack API.""" - _view_builder_class = flavors_view.ViewBuilder - - # NOTE(oomichi): Return 202 for backwards compatibility but should be - # 204 as this operation complete the deletion of aggregate resource and - # return no response body. - @wsgi.response(202) - @wsgi.expected_errors(404) - @wsgi.action("delete") - def _delete(self, req, id): - context = req.environ['nova.context'] - context.can(fm_policies.POLICY_ROOT % 'delete', target={}) - - flavor = objects.Flavor(context=context, flavorid=id) - try: - flavor.destroy() - except exception.FlavorNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - # NOTE(oomichi): Return 200 for backwards compatibility but should be 201 - # as this operation complete the creation of flavor resource. - @wsgi.action("create") - @wsgi.expected_errors((400, 409)) - @validation.schema(flavor_manage.create_v20, '2.0', '2.0') - @validation.schema(flavor_manage.create, '2.1', '2.54') - @validation.schema(flavor_manage.create_v2_55, - flavors_view.FLAVOR_DESCRIPTION_MICROVERSION) - def _create(self, req, body): - context = req.environ['nova.context'] - context.can(fm_policies.POLICY_ROOT % 'create', target={}) - - vals = body['flavor'] - - name = vals['name'] - flavorid = vals.get('id') - memory = vals['ram'] - vcpus = vals['vcpus'] - root_gb = vals['disk'] - ephemeral_gb = vals.get('OS-FLV-EXT-DATA:ephemeral', 0) - swap = vals.get('swap', 0) - rxtx_factor = vals.get('rxtx_factor', 1.0) - is_public = vals.get('os-flavor-access:is_public', True) - - # The user can specify a description starting with microversion 2.55. - include_description = api_version_request.is_supported( - req, flavors_view.FLAVOR_DESCRIPTION_MICROVERSION) - description = vals.get('description') if include_description else None - - try: - flavor = flavors.create(name, memory, vcpus, root_gb, - ephemeral_gb=ephemeral_gb, - flavorid=flavorid, swap=swap, - rxtx_factor=rxtx_factor, - is_public=is_public, - description=description) - # NOTE(gmann): For backward compatibility, non public flavor - # access is not being added for created tenant. Ref -bug/1209101 - except (exception.FlavorExists, - exception.FlavorIdExists) as err: - raise webob.exc.HTTPConflict(explanation=err.format_message()) - - include_extra_specs = False - if api_version_request.is_supported( - req, flavors_view.FLAVOR_EXTRA_SPECS_MICROVERSION): - include_extra_specs = context.can( - fes_policies.POLICY_ROOT % 'index', fatal=False) - # NOTE(yikun): This empty extra_specs only for keeping consistent - # with PUT and GET flavor APIs. extra_specs in flavor is added - # after creating the flavor so to avoid the error in _view_builder - # flavor.extra_specs is populated with the empty string. - flavor.extra_specs = {} - - return self._view_builder.show(req, flavor, include_description, - include_extra_specs=include_extra_specs) - - @wsgi.Controller.api_version(flavors_view.FLAVOR_DESCRIPTION_MICROVERSION) - @wsgi.action('update') - @wsgi.expected_errors((400, 404)) - @validation.schema(flavor_manage.update_v2_55, - flavors_view.FLAVOR_DESCRIPTION_MICROVERSION) - def _update(self, req, id, body): - # Validate the policy. - context = req.environ['nova.context'] - context.can(fm_policies.POLICY_ROOT % 'update', target={}) - - # Get the flavor and update the description. - try: - flavor = objects.Flavor.get_by_flavor_id(context, id) - flavor.description = body['flavor']['description'] - flavor.save() - except exception.FlavorNotFound as e: - raise webob.exc.HTTPNotFound(explanation=e.format_message()) - - include_extra_specs = False - if api_version_request.is_supported( - req, flavors_view.FLAVOR_EXTRA_SPECS_MICROVERSION): - include_extra_specs = context.can( - fes_policies.POLICY_ROOT % 'index', fatal=False) - return self._view_builder.show(req, flavor, include_description=True, - include_extra_specs=include_extra_specs) diff --git a/nova/api/openstack/compute/flavors.py b/nova/api/openstack/compute/flavors.py index 3986e428b3..04b8fcfd71 100644 --- a/nova/api/openstack/compute/flavors.py +++ b/nova/api/openstack/compute/flavors.py @@ -27,6 +27,7 @@ from nova import exception from nova.i18n import _ from nova import objects from nova.policies import flavor_extra_specs as fes_policies +from nova.policies import flavor_manage as fm_policies from nova import utils @@ -35,6 +36,101 @@ class FlavorsController(wsgi.Controller): _view_builder_class = flavors_view.ViewBuilder + # NOTE(oomichi): Return 202 for backwards compatibility but should be + # 204 as this operation complete the deletion of aggregate resource and + # return no response body. + @wsgi.response(202) + @wsgi.expected_errors(404) + def delete(self, req, id): + context = req.environ['nova.context'] + context.can(fm_policies.POLICY_ROOT % 'delete', target={}) + + flavor = objects.Flavor(context=context, flavorid=id) + try: + flavor.destroy() + except exception.FlavorNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + # NOTE(oomichi): Return 200 for backwards compatibility but should be 201 + # as this operation complete the creation of flavor resource. + @wsgi.expected_errors((400, 409)) + @validation.schema(schema.create_v20, '2.0', '2.0') + @validation.schema(schema.create, '2.1', '2.54') + @validation.schema(schema.create_v2_55, + flavors_view.FLAVOR_DESCRIPTION_MICROVERSION) + def create(self, req, body): + context = req.environ['nova.context'] + context.can(fm_policies.POLICY_ROOT % 'create', target={}) + + vals = body['flavor'] + + name = vals['name'] + flavorid = vals.get('id') + memory = vals['ram'] + vcpus = vals['vcpus'] + root_gb = vals['disk'] + ephemeral_gb = vals.get('OS-FLV-EXT-DATA:ephemeral', 0) + swap = vals.get('swap', 0) + rxtx_factor = vals.get('rxtx_factor', 1.0) + is_public = vals.get('os-flavor-access:is_public', True) + + # The user can specify a description starting with microversion 2.55. + include_description = api_version_request.is_supported( + req, flavors_view.FLAVOR_DESCRIPTION_MICROVERSION) + description = vals.get('description') if include_description else None + + try: + flavor = flavors.create(name, memory, vcpus, root_gb, + ephemeral_gb=ephemeral_gb, + flavorid=flavorid, swap=swap, + rxtx_factor=rxtx_factor, + is_public=is_public, + description=description) + # NOTE(gmann): For backward compatibility, non public flavor + # access is not being added for created tenant. Ref -bug/1209101 + except (exception.FlavorExists, + exception.FlavorIdExists) as err: + raise webob.exc.HTTPConflict(explanation=err.format_message()) + + include_extra_specs = False + if api_version_request.is_supported( + req, flavors_view.FLAVOR_EXTRA_SPECS_MICROVERSION): + include_extra_specs = context.can( + fes_policies.POLICY_ROOT % 'index', fatal=False) + # NOTE(yikun): This empty extra_specs only for keeping consistent + # with PUT and GET flavor APIs. extra_specs in flavor is added + # after creating the flavor so to avoid the error in _view_builder + # flavor.extra_specs is populated with the empty string. + flavor.extra_specs = {} + + return self._view_builder.show(req, flavor, include_description, + include_extra_specs=include_extra_specs) + + @wsgi.Controller.api_version(flavors_view.FLAVOR_DESCRIPTION_MICROVERSION) + @wsgi.expected_errors((400, 404)) + @validation.schema(schema.update_v2_55, + flavors_view.FLAVOR_DESCRIPTION_MICROVERSION) + def update(self, req, id, body): + # Validate the policy. + context = req.environ['nova.context'] + context.can(fm_policies.POLICY_ROOT % 'update', target={}) + + # Get the flavor and update the description. + try: + flavor = objects.Flavor.get_by_flavor_id(context, id) + flavor.description = body['flavor']['description'] + flavor.save() + except exception.FlavorNotFound as e: + raise webob.exc.HTTPNotFound(explanation=e.format_message()) + + include_extra_specs = False + if api_version_request.is_supported( + req, flavors_view.FLAVOR_EXTRA_SPECS_MICROVERSION): + include_extra_specs = context.can( + fes_policies.POLICY_ROOT % 'index', fatal=False) + return self._view_builder.show(req, flavor, include_description=True, + include_extra_specs=include_extra_specs) + @validation.query_schema(schema.index_query_275, '2.75') @validation.query_schema(schema.index_query, '2.0', '2.74') @wsgi.expected_errors(400) diff --git a/nova/api/openstack/compute/routes.py b/nova/api/openstack/compute/routes.py index 7ffdc696e0..5b1eeefe8b 100644 --- a/nova/api/openstack/compute/routes.py +++ b/nova/api/openstack/compute/routes.py @@ -37,7 +37,6 @@ from nova.api.openstack.compute import evacuate from nova.api.openstack.compute import extension_info from nova.api.openstack.compute import fixed_ips from nova.api.openstack.compute import flavor_access -from nova.api.openstack.compute import flavor_manage from nova.api.openstack.compute import flavors from nova.api.openstack.compute import flavors_extraspecs from nova.api.openstack.compute import floating_ip_dns @@ -143,7 +142,6 @@ fixed_ips_controller = functools.partial(_create_controller, flavor_controller = functools.partial(_create_controller, flavors.FlavorsController, [ - flavor_manage.FlavorManageController, flavor_access.FlavorActionController ] ) diff --git a/nova/api/openstack/compute/schemas/flavor_manage.py b/nova/api/openstack/compute/schemas/flavor_manage.py deleted file mode 100644 index ad3ebe57a7..0000000000 --- a/nova/api/openstack/compute/schemas/flavor_manage.py +++ /dev/null @@ -1,102 +0,0 @@ -# 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. - -import copy - -from nova.api.validation import parameter_types - -create = { - 'type': 'object', - 'properties': { - 'flavor': { - 'type': 'object', - 'properties': { - # in nova/flavors.py, name with all white spaces is forbidden. - 'name': parameter_types.name, - # forbid leading/trailing whitespaces - 'id': { - 'type': ['string', 'number', 'null'], - 'minLength': 1, 'maxLength': 255, - 'pattern': '^(?! )[a-zA-Z0-9. _-]+(? 0) float - 'rxtx_factor': { - 'type': ['number', 'string'], - 'pattern': r'^[0-9]+(\.[0-9]+)?$', - 'minimum': 0, 'exclusiveMinimum': True, - # maximum's value is limited to db constant's - # SQL_SP_FLOAT_MAX (in nova/db/constants.py) - 'maximum': 3.40282e+38 - }, - 'os-flavor-access:is_public': parameter_types.boolean, - }, - # TODO(oomichi): 'id' should be required with v2.1+microversions. - # On v2.0 API, nova-api generates a flavor-id automatically if - # specifying null as 'id' or not specifying 'id'. Ideally a client - # should specify null as 'id' for requesting auto-generated id - # exactly. However, this strict limitation causes a backwards - # incompatible issue on v2.1. So now here relaxes the requirement - # of 'id'. - 'required': ['name', 'ram', 'vcpus', 'disk'], - 'additionalProperties': False, - }, - }, - 'required': ['flavor'], - 'additionalProperties': False, -} - - -create_v20 = copy.deepcopy(create) -create_v20['properties']['flavor']['properties']['name'] = (parameter_types. - name_with_leading_trailing_spaces) - - -# 2.55 adds an optional description field with a max length of 65535 since the -# backing database column is a TEXT column which is 64KiB. -flavor_description = { - 'type': ['string', 'null'], 'minLength': 0, 'maxLength': 65535, - 'pattern': parameter_types.valid_description_regex, -} - - -create_v2_55 = copy.deepcopy(create) -create_v2_55['properties']['flavor']['properties']['description'] = ( - flavor_description) - - -update_v2_55 = { - 'type': 'object', - 'properties': { - 'flavor': { - 'type': 'object', - 'properties': { - 'description': flavor_description - }, - # Since the only property that can be specified on update is the - # description field, it is required. If we allow updating other - # flavor attributes in a later microversion, we should reconsider - # what is required. - 'required': ['description'], - 'additionalProperties': False, - }, - }, - 'required': ['flavor'], - 'additionalProperties': False, -} diff --git a/nova/api/openstack/compute/schemas/flavors.py b/nova/api/openstack/compute/schemas/flavors.py index c6064f02ef..f0cecb3dc4 100644 --- a/nova/api/openstack/compute/schemas/flavors.py +++ b/nova/api/openstack/compute/schemas/flavors.py @@ -26,6 +26,92 @@ VALID_SORT_KEYS = [ VALID_SORT_DIR = ['asc', 'desc'] +create = { + 'type': 'object', + 'properties': { + 'flavor': { + 'type': 'object', + 'properties': { + # in nova/flavors.py, name with all white spaces is forbidden. + 'name': parameter_types.name, + # forbid leading/trailing whitespaces + 'id': { + 'type': ['string', 'number', 'null'], + 'minLength': 1, 'maxLength': 255, + 'pattern': '^(?! )[a-zA-Z0-9. _-]+(? 0) float + 'rxtx_factor': { + 'type': ['number', 'string'], + 'pattern': r'^[0-9]+(\.[0-9]+)?$', + 'minimum': 0, 'exclusiveMinimum': True, + # maximum's value is limited to db constant's + # SQL_SP_FLOAT_MAX (in nova/db/constants.py) + 'maximum': 3.40282e+38 + }, + 'os-flavor-access:is_public': parameter_types.boolean, + }, + # TODO(oomichi): 'id' should be required with v2.1+microversions. + # On v2.0 API, nova-api generates a flavor-id automatically if + # specifying null as 'id' or not specifying 'id'. Ideally a client + # should specify null as 'id' for requesting auto-generated id + # exactly. However, this strict limitation causes a backwards + # incompatible issue on v2.1. So now here relaxes the requirement + # of 'id'. + 'required': ['name', 'ram', 'vcpus', 'disk'], + 'additionalProperties': False, + }, + }, + 'required': ['flavor'], + 'additionalProperties': False, +} + + +create_v20 = copy.deepcopy(create) +create_v20['properties']['flavor']['properties']['name'] = ( + parameter_types.name_with_leading_trailing_spaces +) + + +# 2.55 adds an optional description field with a max length of 65535 since the +# backing database column is a TEXT column which is 64KiB. +_flavor_description = { + 'type': ['string', 'null'], 'minLength': 0, 'maxLength': 65535, + 'pattern': parameter_types.valid_description_regex, +} + + +create_v2_55 = copy.deepcopy(create) +create_v2_55['properties']['flavor']['properties']['description'] = ( + _flavor_description) + + +update_v2_55 = { + 'type': 'object', + 'properties': { + 'flavor': { + 'type': 'object', + 'properties': { + 'description': _flavor_description + }, + # Since the only property that can be specified on update is the + # description field, it is required. If we allow updating other + # flavor attributes in a later microversion, we should reconsider + # what is required. + 'required': ['description'], + 'additionalProperties': False, + }, + }, + 'required': ['flavor'], + 'additionalProperties': False, +} + index_query = { 'type': 'object', 'properties': { diff --git a/nova/tests/unit/api/openstack/compute/test_flavor_manage.py b/nova/tests/unit/api/openstack/compute/test_flavor_manage.py index 948f255f34..7cc352b07b 100644 --- a/nova/tests/unit/api/openstack/compute/test_flavor_manage.py +++ b/nova/tests/unit/api/openstack/compute/test_flavor_manage.py @@ -20,7 +20,7 @@ from oslo_serialization import jsonutils import webob from nova.api.openstack.compute import flavor_access as flavor_access_v21 -from nova.api.openstack.compute import flavor_manage as flavormanage_v21 +from nova.api.openstack.compute import flavors as flavors_v21 from nova.compute import flavors from nova.db import constants as db_const from nova import exception @@ -57,7 +57,7 @@ def fake_create_without_swap(newflavor): class FlavorManageTestV21(test.NoDBTestCase): - controller = flavormanage_v21.FlavorManageController() + controller = flavors_v21.FlavorsController() validation_error = exception.ValidationError base_url = '/v2/%s/flavors' % fakes.FAKE_PROJECT_ID microversion = '2.1' @@ -91,13 +91,13 @@ class FlavorManageTestV21(test.NoDBTestCase): @mock.patch('nova.objects.Flavor.destroy') def test_delete(self, mock_destroy): - res = self.controller._delete(self._get_http_request(), 1234) + res = self.controller.delete(self._get_http_request(), 1234) # NOTE: on v2.1, http status code is set as wsgi_code of API # method instead of status_int in a response object. if isinstance(self.controller, - flavormanage_v21.FlavorManageController): - status_int = self.controller._delete.wsgi_code + flavors_v21.FlavorsController): + status_int = self.controller.delete.wsgi_code else: status_int = res.status_int self.assertEqual(202, status_int) @@ -105,7 +105,7 @@ class FlavorManageTestV21(test.NoDBTestCase): # subsequent delete should fail mock_destroy.side_effect = exception.FlavorNotFound(flavor_id=1234) self.assertRaises(webob.exc.HTTPNotFound, - self.controller._delete, self._get_http_request(), + self.controller.delete, self._get_http_request(), 1234) def _test_create_missing_parameter(self, parameter): @@ -125,7 +125,7 @@ class FlavorManageTestV21(test.NoDBTestCase): del body['flavor'][parameter] - self.assertRaises(self.validation_error, self.controller._create, + self.assertRaises(self.validation_error, self.controller.create, self._get_http_request(), body=body) def test_create_missing_name(self): @@ -172,7 +172,7 @@ class FlavorManageTestV21(test.NoDBTestCase): self.expected_flavor["flavor"][key]) def _create_flavor_bad_request_case(self, body): - self.assertRaises(self.validation_error, self.controller._create, + self.assertRaises(self.validation_error, self.controller.create, self._get_http_request(), body=body) def test_create_invalid_name(self): @@ -321,7 +321,7 @@ class FlavorManageTestV21(test.NoDBTestCase): raise exception.FlavorExists(name=name) self.stub_out('nova.compute.flavors.create', fake_create) - self.assertRaises(webob.exc.HTTPConflict, self.controller._create, + self.assertRaises(webob.exc.HTTPConflict, self.controller.create, self._get_http_request(), body=expected) def test_invalid_memory_mb(self): @@ -341,7 +341,7 @@ class FlavorManageTestV21(test.NoDBTestCase): """With microversion <2.55 this should return a failure.""" self.request_body['flavor']['description'] = 'invalid' ex = self.assertRaises( - self.validation_error, self.controller._create, + self.validation_error, self.controller.create, self._get_http_request(), body=self.request_body) self.assertIn('description', str(ex)) @@ -349,7 +349,7 @@ class FlavorManageTestV21(test.NoDBTestCase): """With microversion <2.55 this should return a failure.""" flavor = self._create_flavor_success_case(self.request_body)['flavor'] self.assertRaises( - exception.VersionNotFoundForAPIMethod, self.controller._update, + exception.VersionNotFoundForAPIMethod, self.controller.update, self._get_http_request(), flavor['id'], body={'flavor': {'description': 'nope'}}) @@ -387,7 +387,7 @@ class FlavorManageTestV2_55(FlavorManageTestV21): self.assertEqual('test description', flavor['description']) mock_get.return_value = self.get_flavor(flavor) # Now null out the flavor description. - flavor = self.controller._update( + flavor = self.controller.update( self._get_http_request(), flavor['id'], body={'flavor': {'description': None}})['flavor'] self.assertIsNone(flavor['description']) @@ -400,7 +400,7 @@ class FlavorManageTestV2_55(FlavorManageTestV21): def test_flavor_update_not_found(self, mock_get): """Tests that a 404 is returned if the flavor is not found.""" self.assertRaises(webob.exc.HTTPNotFound, - self.controller._update, + self.controller.update, self._get_http_request(), 'notfound', body={'flavor': {'description': None}}) @@ -409,7 +409,7 @@ class FlavorManageTestV2_55(FlavorManageTestV21): is provided in the update request body. """ self.assertRaises(self.validation_error, - self.controller._update, + self.controller.update, self._get_http_request(), 'invalid', body={'flavor': {}}) @@ -420,7 +420,7 @@ class FlavorManageTestV2_55(FlavorManageTestV21): for description in ('bad !@#!$%\x00 description', # printable chars 'a' * 65536): # maxLength self.request_body['flavor']['description'] = description - self.assertRaises(self.validation_error, self.controller._create, + self.assertRaises(self.validation_error, self.controller.create, self._get_http_request(), body=self.request_body) @mock.patch('nova.objects.Flavor.get_by_flavor_id') @@ -443,7 +443,7 @@ class FlavorManageTestV2_55(FlavorManageTestV21): for description in ('bad !@#!$%\x00 description', # printable chars 'a' * 65536): # maxLength self.request_body['flavor']['description'] = description - self.assertRaises(self.validation_error, self.controller._update, + self.assertRaises(self.validation_error, self.controller.update, self._get_http_request(), flavor['id'], body={'flavor': {'description': description}}) @@ -467,7 +467,7 @@ class FlavorManageTestV2_61(FlavorManageTestV2_55): # First create a flavor. flavor = self._create_flavor_success_case(self.request_body)['flavor'] mock_get.return_value = self.get_flavor(flavor) - flavor = self.controller._update( + flavor = self.controller.update( self._get_http_request(), flavor['id'], body={'flavor': {'description': None}})['flavor'] self.assertEqual({"key1": "value1"}, flavor['extra_specs']) @@ -509,7 +509,7 @@ class FlavorManageTestV2_75(FlavorManageTestV2_61): req = fakes.HTTPRequest.blank('/%s/flavors' % fakes.FAKE_PROJECT_ID, version='2.74') req.method = 'PUT' - response = self.controller._update( + response = self.controller.update( req, flavor['id'], body={'flavor': {'description': None}})['flavor'] self.assertEqual(response['swap'], '') @@ -530,14 +530,14 @@ class FlavorManageTestV2_75(FlavorManageTestV2_61): flavor = self._create_flavor_success_case(self.request_body)['flavor'] req = fakes.HTTPRequest.blank('/%s/flavors' % fakes.FAKE_PROJECT_ID, version=self.microversion) - response = self.controller._update( + response = self.controller.update( req, flavor['id'], body={'flavor': {'description': None}})['flavor'] self.assertEqual(response['swap'], 0) class PrivateFlavorManageTestV21(test.TestCase): - controller = flavormanage_v21.FlavorManageController() + controller = flavors_v21.FlavorsController() base_url = '/v2/%s/flavors' % fakes.FAKE_PROJECT_ID def setUp(self): diff --git a/nova/tests/unit/policies/test_flavor_extra_specs.py b/nova/tests/unit/policies/test_flavor_extra_specs.py index f3c8cacd57..7c3efccdc3 100644 --- a/nova/tests/unit/policies/test_flavor_extra_specs.py +++ b/nova/tests/unit/policies/test_flavor_extra_specs.py @@ -14,7 +14,6 @@ from unittest import mock from oslo_utils.fixture import uuidsentinel as uuids -from nova.api.openstack.compute import flavor_manage from nova.api.openstack.compute import flavors from nova.api.openstack.compute import flavors_extraspecs from nova.policies import flavor_extra_specs as policies @@ -36,7 +35,7 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest): super(FlavorExtraSpecsPolicyTest, self).setUp() self.controller = flavors_extraspecs.FlavorExtraSpecsController() self.flavor_ctrl = flavors.FlavorsController() - self.fm_ctrl = flavor_manage.FlavorManageController() + self.fm_ctrl = flavors.FlavorsController() self.req = fakes.HTTPRequest.blank('') def get_flavor_extra_specs(context, flavor_id): @@ -168,7 +167,7 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest): } authorize_res, unauthorize_res = self.common_policy_auth( self.all_project_authorized_contexts, - rule_name, self.fm_ctrl._create, req, body=body, + rule_name, self.fm_ctrl.create, req, body=body, fatal=False) for resp in authorize_res: self.assertIn('extra_specs', resp['flavor']) @@ -188,7 +187,7 @@ class FlavorExtraSpecsPolicyTest(base.BasePolicyTest): authorize_res, unauthorize_res = self.common_policy_auth( self.all_project_authorized_contexts, - rule_name, self.fm_ctrl._update, req, '1', + rule_name, self.fm_ctrl.update, req, '1', body={'flavor': {'description': None}}, fatal=False) for resp in authorize_res: diff --git a/nova/tests/unit/policies/test_flavor_manage.py b/nova/tests/unit/policies/test_flavor_manage.py index 0663a689cb..08f79e5d68 100644 --- a/nova/tests/unit/policies/test_flavor_manage.py +++ b/nova/tests/unit/policies/test_flavor_manage.py @@ -14,7 +14,7 @@ from unittest import mock from oslo_utils.fixture import uuidsentinel as uuids -from nova.api.openstack.compute import flavor_manage +from nova.api.openstack.compute import flavors from nova.policies import flavor_manage as fm_policies from nova.tests.unit.api.openstack import fakes from nova.tests.unit.policies import base @@ -30,7 +30,7 @@ class FlavorManagePolicyTest(base.BasePolicyTest): def setUp(self): super(FlavorManagePolicyTest, self).setUp() - self.controller = flavor_manage.FlavorManageController() + self.controller = flavors.FlavorsController() self.req = fakes.HTTPRequest.blank('') # With legacy rule and no scope checks, all admin can manage # the flavors. @@ -62,7 +62,7 @@ class FlavorManagePolicyTest(base.BasePolicyTest): } } self.common_policy_auth(self.admin_authorized_contexts, - rule_name, self.controller._create, + rule_name, self.controller.create, self.req, body=body) @mock.patch('nova.objects.Flavor.get_by_flavor_id') @@ -71,7 +71,7 @@ class FlavorManagePolicyTest(base.BasePolicyTest): rule_name = fm_policies.POLICY_ROOT % 'update' req = fakes.HTTPRequest.blank('', version='2.55') self.common_policy_auth(self.admin_authorized_contexts, - rule_name, self.controller._update, + rule_name, self.controller.update, req, uuids.fake_id, body={'flavor': {'description': None}}) @@ -79,7 +79,7 @@ class FlavorManagePolicyTest(base.BasePolicyTest): def test_delete_flavor_policy(self, mock_delete): rule_name = fm_policies.POLICY_ROOT % 'delete' self.common_policy_auth(self.admin_authorized_contexts, - rule_name, self.controller._delete, + rule_name, self.controller.delete, self.req, uuids.fake_id)