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 extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
from nova.api.openstack import xmlutil
|
from nova.api.openstack import xmlutil
|
||||||
|
from nova.compute import flavors
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common.gettextutils import _
|
from nova.openstack.common.gettextutils import _
|
||||||
|
|
||||||
|
|
||||||
authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
|
authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,6 +57,12 @@ class FlavorExtraSpecsController(object):
|
||||||
expl = _('No Request Body')
|
expl = _('No Request Body')
|
||||||
raise exc.HTTPBadRequest(explanation=expl)
|
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)
|
@wsgi.serializers(xml=ExtraSpecsTemplate)
|
||||||
def index(self, req, flavor_id):
|
def index(self, req, flavor_id):
|
||||||
"""Returns the list of extra specs for a given flavor."""
|
"""Returns the list of extra specs for a given flavor."""
|
||||||
|
@ -70,6 +76,7 @@ class FlavorExtraSpecsController(object):
|
||||||
authorize(context, action='create')
|
authorize(context, action='create')
|
||||||
self._check_body(body)
|
self._check_body(body)
|
||||||
specs = body.get('extra_specs')
|
specs = body.get('extra_specs')
|
||||||
|
self._check_key_names(specs.keys())
|
||||||
try:
|
try:
|
||||||
db.flavor_extra_specs_update_or_create(context,
|
db.flavor_extra_specs_update_or_create(context,
|
||||||
flavor_id,
|
flavor_id,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import webob
|
||||||
|
|
||||||
from nova.api.openstack import extensions
|
from nova.api.openstack import extensions
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
|
from nova.compute import flavors
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common.db import exception as db_exc
|
from nova.openstack.common.db import exception as db_exc
|
||||||
|
@ -43,6 +44,12 @@ class FlavorExtraSpecsController(object):
|
||||||
expl = _('No Request Body')
|
expl = _('No Request Body')
|
||||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
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(())
|
@extensions.expected_errors(())
|
||||||
def index(self, req, flavor_id):
|
def index(self, req, flavor_id):
|
||||||
"""Returns the list of extra specs for a given flavor."""
|
"""Returns the list of extra specs for a given flavor."""
|
||||||
|
@ -59,6 +66,7 @@ class FlavorExtraSpecsController(object):
|
||||||
specs = body.get('extra_specs', {})
|
specs = body.get('extra_specs', {})
|
||||||
if not specs or type(specs) is not dict:
|
if not specs or type(specs) is not dict:
|
||||||
raise webob.exc.HTTPBadRequest(_('No or bad extra_specs provided'))
|
raise webob.exc.HTTPBadRequest(_('No or bad extra_specs provided'))
|
||||||
|
self._check_key_names(specs.keys())
|
||||||
try:
|
try:
|
||||||
db.flavor_extra_specs_update_or_create(context, flavor_id,
|
db.flavor_extra_specs_update_or_create(context, flavor_id,
|
||||||
specs)
|
specs)
|
||||||
|
|
|
@ -55,6 +55,9 @@ LOG = logging.getLogger(__name__)
|
||||||
VALID_ID_REGEX = re.compile("^[\w\.\- ]*$")
|
VALID_ID_REGEX = re.compile("^[\w\.\- ]*$")
|
||||||
VALID_NAME_REGEX = re.compile("^[\w\.\- ]*$", re.UNICODE)
|
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):
|
def _int_or_none(val):
|
||||||
if val is not None:
|
if val is not None:
|
||||||
|
@ -309,3 +312,11 @@ def delete_flavor_info(metadata, *prefixes):
|
||||||
del metadata[to_key]
|
del metadata[to_key]
|
||||||
pci_request.delete_flavor_pci_info(metadata, *prefixes)
|
pci_request.delete_flavor_pci_info(metadata, *prefixes)
|
||||||
return metadata
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import mock
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute.contrib import flavorextraspecs
|
from nova.api.openstack.compute.contrib import flavorextraspecs
|
||||||
|
@ -153,6 +154,30 @@ class FlavorsExtraSpecsTest(test.TestCase):
|
||||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||||
req, 1, '')
|
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):
|
def test_update_item(self):
|
||||||
self.stubs.Set(nova.db,
|
self.stubs.Set(nova.db,
|
||||||
'flavor_extra_specs_update_or_create',
|
'flavor_extra_specs_update_or_create',
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import mock
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute.plugins.v3 import flavors_extraspecs
|
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,
|
self.assertRaises(webob.exc.HTTPConflict, self.controller.create,
|
||||||
req, 1, body)
|
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):
|
def test_update_item(self):
|
||||||
self.stubs.Set(nova.db,
|
self.stubs.Set(nova.db,
|
||||||
'flavor_extra_specs_update_or_create',
|
'flavor_extra_specs_update_or_create',
|
||||||
|
|
Loading…
Reference in New Issue