Merge "api: Remove FlavorManageController"

This commit is contained in:
Zuul 2024-04-22 19:58:38 +00:00 committed by Gerrit Code Review
commit e2ef2240b1
8 changed files with 210 additions and 260 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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
]
)

View File

@ -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. _-]+(?<! )$'
},
'ram': parameter_types.flavor_param_positive,
'vcpus': parameter_types.flavor_param_positive,
'disk': parameter_types.flavor_param_non_negative,
'OS-FLV-EXT-DATA:ephemeral':
parameter_types.flavor_param_non_negative,
'swap': parameter_types.flavor_param_non_negative,
# positive ( > 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,
}

View File

@ -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. _-]+(?<! )$'
},
'ram': parameter_types.flavor_param_positive,
'vcpus': parameter_types.flavor_param_positive,
'disk': parameter_types.flavor_param_non_negative,
'OS-FLV-EXT-DATA:ephemeral':
parameter_types.flavor_param_non_negative,
'swap': parameter_types.flavor_param_non_negative,
# positive ( > 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': {

View File

@ -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):

View File

@ -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:

View File

@ -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)