Merge "api: Remove FlavorManageController"
This commit is contained in:
commit
e2ef2240b1
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
]
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
}
|
|
@ -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': {
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue