Enforce FlavorExtraSpecs Key format.
When creating a key name with characters like '/' that are not being encoded or cannot be handled as expected by routes when matching the url, then it's not possible to remove them anymore. The new format apply the restriction: "[\w\.\- :]+$" This change will fix the bug without API change. Change-Id: I64bc098c4bde33c40fa149191a751ea3eb19f932 Closes-Bug: #1256119
This commit is contained in:
parent
4b2bd32199
commit
050ce0e589
|
@ -22,11 +22,11 @@ from webob import exc
|
|||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import xmlutil
|
||||
from nova.compute import flavors
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova.openstack.common.gettextutils import _
|
||||
|
||||
|
||||
authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
|
||||
|
||||
|
||||
|
@ -57,6 +57,12 @@ class FlavorExtraSpecsController(object):
|
|||
expl = _('No Request Body')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
def _check_key_names(self, keys):
|
||||
try:
|
||||
flavors.validate_extra_spec_keys(keys)
|
||||
except exception.InvalidInput as error:
|
||||
raise exc.HTTPBadRequest(explanation=error.format_message())
|
||||
|
||||
@wsgi.serializers(xml=ExtraSpecsTemplate)
|
||||
def index(self, req, flavor_id):
|
||||
"""Returns the list of extra specs for a given flavor."""
|
||||
|
@ -70,6 +76,7 @@ class FlavorExtraSpecsController(object):
|
|||
authorize(context, action='create')
|
||||
self._check_body(body)
|
||||
specs = body.get('extra_specs')
|
||||
self._check_key_names(specs.keys())
|
||||
try:
|
||||
db.flavor_extra_specs_update_or_create(context,
|
||||
flavor_id,
|
||||
|
|
|
@ -19,6 +19,7 @@ import webob
|
|||
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.compute import flavors
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova.openstack.common.db import exception as db_exc
|
||||
|
@ -43,6 +44,12 @@ class FlavorExtraSpecsController(object):
|
|||
expl = _('No Request Body')
|
||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
def _check_key_names(self, keys):
|
||||
try:
|
||||
flavors.validate_extra_spec_keys(keys)
|
||||
except exception.InvalidInput as error:
|
||||
raise webob.exc.HTTPBadRequest(explanation=error.format_message())
|
||||
|
||||
@extensions.expected_errors(())
|
||||
def index(self, req, flavor_id):
|
||||
"""Returns the list of extra specs for a given flavor."""
|
||||
|
@ -59,6 +66,7 @@ class FlavorExtraSpecsController(object):
|
|||
specs = body.get('extra_specs', {})
|
||||
if not specs or type(specs) is not dict:
|
||||
raise webob.exc.HTTPBadRequest(_('No or bad extra_specs provided'))
|
||||
self._check_key_names(specs.keys())
|
||||
try:
|
||||
db.flavor_extra_specs_update_or_create(context, flavor_id,
|
||||
specs)
|
||||
|
|
|
@ -55,6 +55,9 @@ LOG = logging.getLogger(__name__)
|
|||
VALID_ID_REGEX = re.compile("^[\w\.\- ]*$")
|
||||
VALID_NAME_REGEX = re.compile("^[\w\.\- ]*$", re.UNICODE)
|
||||
|
||||
# Validate extra specs key names.
|
||||
VALID_EXTRASPEC_NAME_REGEX = re.compile(r"[\w\.\- :]+$", re.UNICODE)
|
||||
|
||||
|
||||
def _int_or_none(val):
|
||||
if val is not None:
|
||||
|
@ -309,3 +312,11 @@ def delete_flavor_info(metadata, *prefixes):
|
|||
del metadata[to_key]
|
||||
pci_request.delete_flavor_pci_info(metadata, *prefixes)
|
||||
return metadata
|
||||
|
||||
|
||||
def validate_extra_spec_keys(key_names_list):
|
||||
for key_name in key_names_list:
|
||||
if not VALID_EXTRASPEC_NAME_REGEX.match(key_name):
|
||||
expl = _('Key Names can only contain alphanumeric characters, '
|
||||
'periods, dashes, underscores, colons and spaces.')
|
||||
raise exception.InvalidInput(message=expl)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute.contrib import flavorextraspecs
|
||||
|
@ -153,6 +154,30 @@ class FlavorsExtraSpecsTest(test.TestCase):
|
|||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
req, 1, '')
|
||||
|
||||
@mock.patch('nova.db.flavor_extra_specs_update_or_create')
|
||||
def test_create_invalid_specs_key(self, mock_flavor_extra_specs):
|
||||
invalid_keys = ("key1/", "<key>", "$$akey$", "!akey", "")
|
||||
mock_flavor_extra_specs.side_effects = return_create_flavor_extra_specs
|
||||
|
||||
for key in invalid_keys:
|
||||
body = {"extra_specs": {key: "value1"}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs',
|
||||
use_admin_context=True)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
req, 1, body)
|
||||
|
||||
@mock.patch('nova.db.flavor_extra_specs_update_or_create')
|
||||
def test_create_valid_specs_key(self, mock_flavor_extra_specs):
|
||||
valid_keys = ("key1", "month.price", "I_am-a Key", "finance:g2")
|
||||
mock_flavor_extra_specs.side_effects = return_create_flavor_extra_specs
|
||||
|
||||
for key in valid_keys:
|
||||
body = {"extra_specs": {key: "value1"}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/flavors/1/os-extra_specs',
|
||||
use_admin_context=True)
|
||||
res_dict = self.controller.create(req, 1, body)
|
||||
self.assertEqual('value1', res_dict['extra_specs'][key])
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(nova.db,
|
||||
'flavor_extra_specs_update_or_create',
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute.plugins.v3 import flavors_extraspecs
|
||||
|
@ -178,6 +179,33 @@ class FlavorsExtraSpecsTest(test.TestCase):
|
|||
self.assertRaises(webob.exc.HTTPConflict, self.controller.create,
|
||||
req, 1, body)
|
||||
|
||||
@mock.patch('nova.db.flavor_extra_specs_update_or_create')
|
||||
def test_create_invalid_specs_key(self, mock_flavor_extra_specs):
|
||||
invalid_keys = ("key1/", "<key>", "$$akey$", "!akey", "")
|
||||
mock_flavor_extra_specs.side_effects = return_create_flavor_extra_specs
|
||||
|
||||
for key in invalid_keys:
|
||||
body = {"extra_specs": {key: "value1"}}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v3/flavors/1/extra-specs',
|
||||
use_admin_context=True)
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
req, 1, body)
|
||||
|
||||
@mock.patch('nova.db.flavor_extra_specs_update_or_create')
|
||||
def test_create_valid_specs_key(self, mock_flavor_extra_specs):
|
||||
valid_keys = ("key1", "month.price", "I_am-a Key", "finance:g2")
|
||||
mock_flavor_extra_specs.side_effects = return_create_flavor_extra_specs
|
||||
|
||||
for key in valid_keys:
|
||||
body = {"extra_specs": {key: "value1"}}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v3/flavors/1/extra-specs',
|
||||
use_admin_context=True)
|
||||
res_dict = self.controller.create(req, 1, body)
|
||||
self.assertEqual('value1', res_dict['extra_specs'][key])
|
||||
self.assertEqual(self.controller.create.wsgi_code, 201)
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(nova.db,
|
||||
'flavor_extra_specs_update_or_create',
|
||||
|
|
Loading…
Reference in New Issue