XML_NS_V1 = 'http://docs.openstack.org/volume/api/v1'
+# Regex that matches alphanumeric characters, periods, hypens,
+# colons and underscores:
+# ^ assert position at start of the string
+# [\w\.\-\:\_] match expression
+# $ assert position at end of the string
+VALID_KEY_NAME_REGEX = re.compile(r"^[\w\.\-\:\_]+$", re.UNICODE)
+
+
+def validate_key_names(key_names_list):
+ """Validate each item of the list to match key name regex."""
+ for key_name in key_names_list:
+ if not VALID_KEY_NAME_REGEX.match(key_name):
+ return False
+ return True
+
+
def get_pagination_params(request):
"""Return marker, limit tuple from request.
import webob
+from cinder.api import common
from cinder.api import extensions
from cinder.api.openstack import wsgi
from cinder.api import xmlutil
raise webob.exc.HTTPBadRequest()
self._check_type(context, type_id)
-
specs = body['extra_specs']
+ self._check_key_names(specs.keys())
db.volume_type_extra_specs_update_or_create(context,
type_id,
specs)
notifier_api.INFO, notifier_info)
return webob.Response(status_int=202)
+ def _check_key_names(self, keys):
+ if not common.validate_key_names(keys):
+ expl = _('Key names can only contain alphanumeric characters, '
+ 'underscores, periods, colons and hyphens.')
+
+ raise webob.exc.HTTPBadRequest(explanation=expl)
+
class Types_extra_specs(extensions.ExtensionDescriptor):
"""Type extra specs support."""
from lxml import etree
import webob
+import mock
+
from cinder.api.contrib import types_extra_specs
from cinder import exception
from cinder.openstack.common.notifier import api as notifier_api
self.assertEqual('value1', res_dict['extra_specs']['key1'])
+ @mock.patch.object(cinder.db, 'volume_type_extra_specs_update_or_create')
+ def test_create_key_allowed_chars(
+ self, volume_type_extra_specs_update_or_create):
+ mock_return_value = {"key1": "value1",
+ "key2": "value2",
+ "key3": "value3",
+ "key4": "value4",
+ "key5": "value5"}
+ volume_type_extra_specs_update_or_create.\
+ return_value = mock_return_value
+
+ body = {"extra_specs": {"other_alphanum.-_:": "value1"}}
+
+ self.assertEqual(len(test_notifier.NOTIFICATIONS), 0)
+
+ req = fakes.HTTPRequest.blank(self.api_path)
+ res_dict = self.controller.create(req, 1, body)
+ self.assertEqual(len(test_notifier.NOTIFICATIONS), 1)
+ self.assertEqual('value1',
+ res_dict['extra_specs']['other_alphanum.-_:'])
+
+ @mock.patch.object(cinder.db, 'volume_type_extra_specs_update_or_create')
+ def test_create_too_many_keys_allowed_chars(
+ self, volume_type_extra_specs_update_or_create):
+ mock_return_value = {"key1": "value1",
+ "key2": "value2",
+ "key3": "value3",
+ "key4": "value4",
+ "key5": "value5"}
+ volume_type_extra_specs_update_or_create.\
+ return_value = mock_return_value
+
+ body = {"extra_specs": {"other_alphanum.-_:": "value1",
+ "other2_alphanum.-_:": "value2",
+ "other3_alphanum.-_:": "value3"}}
+
+ self.assertEqual(len(test_notifier.NOTIFICATIONS), 0)
+
+ req = fakes.HTTPRequest.blank(self.api_path)
+ res_dict = self.controller.create(req, 1, body)
+ self.assertEqual(len(test_notifier.NOTIFICATIONS), 1)
+ self.assertEqual('value1',
+ res_dict['extra_specs']['other_alphanum.-_:'])
+ self.assertEqual('value2',
+ res_dict['extra_specs']['other2_alphanum.-_:'])
+ self.assertEqual('value3',
+ res_dict['extra_specs']['other3_alphanum.-_:'])
+
def test_update_item(self):
self.stubs.Set(cinder.db,
'volume_type_extra_specs_update_or_create',
def _extra_specs_create_bad_body(self, body):
req = fakes.HTTPRequest.blank('/v2/fake/types/1/extra_specs')
req.method = 'POST'
+
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, req, '1', body)
body = {'extra_specs': 'string'}
self._extra_specs_create_bad_body(body=body)
+ def test_create_invalid_key(self):
+ body = {"extra_specs": {"ke/y1": "value1"}}
+ self._extra_specs_create_bad_body(body=body)
+
+ def test_create_invalid_too_many_key(self):
+ body = {"key1": "value1", "ke/y2": "value2", "key3": "value3"}
+ self._extra_specs_create_bad_body(body=body)
+
class VolumeTypeExtraSpecsSerializerTest(test.TestCase):
def test_index_create_serializer(self):