Merge "Filter leading/trailing spaces for name field in v2.1 compat mode"

This commit is contained in:
Jenkins
2015-09-23 12:31:00 +00:00
committed by Gerrit Code Review
36 changed files with 810 additions and 59 deletions

View File

@@ -512,6 +512,17 @@ def get_instance(compute_api, context, instance_id, expected_attrs=None):
raise exc.HTTPNotFound(explanation=e.format_message())
def normalize_name(name):
# NOTE(alex_xu): This method is used by v2.1 legacy v2 compat mode.
# In the legacy v2 API, some of APIs strip the spaces and some of APIs not.
# The v2.1 disallow leading/trailing, for compatible v2 API and consistent,
# we enable leading/trailing spaces and strip spaces in legacy v2 compat
# mode. Althrough in legacy v2 API there are some APIs didn't strip spaces,
# but actually leading/trailing spaces(that means user depend on leading/
# trailing spaces distinguish different instance) is pointless usecase.
return name.strip()
def raise_feature_not_supported(msg=None):
if msg is None:
msg = _("The requested functionality is not supported.")

View File

@@ -98,7 +98,7 @@ class AccessIPs(extensions.V21APIExtensionBase):
server_update = server_create
server_rebuild = server_create
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return access_ips.server_create
get_server_update_schema = get_server_create_schema

View File

@@ -19,6 +19,7 @@ import datetime
from webob import exc
from nova.api.openstack import common
from nova.api.openstack.compute.schemas import aggregates
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
@@ -52,7 +53,8 @@ class AggregateController(wsgi.Controller):
# NOTE(gmann): Returns 200 for backwards compatibility but should be 201
# as this operation complete the creation of aggregates resource.
@extensions.expected_errors((400, 409))
@validation.schema(aggregates.create)
@validation.schema(aggregates.create_v20, '2.0', '2.0')
@validation.schema(aggregates.create, '2.1')
def create(self, req, body):
"""Creates an aggregate, given its name and
optional availability zone.
@@ -60,8 +62,10 @@ class AggregateController(wsgi.Controller):
context = _get_context(req)
authorize(context, action='create')
host_aggregate = body["aggregate"]
name = host_aggregate["name"]
name = common.normalize_name(host_aggregate["name"])
avail_zone = host_aggregate.get("availability_zone")
if avail_zone:
avail_zone = common.normalize_name(avail_zone)
try:
aggregate = self.api.create_aggregate(context, name, avail_zone)
@@ -91,12 +95,15 @@ class AggregateController(wsgi.Controller):
return self._marshall_aggregate(aggregate)
@extensions.expected_errors((400, 404, 409))
@validation.schema(aggregates.update)
@validation.schema(aggregates.update_v20, '2.0', '2.0')
@validation.schema(aggregates.update, '2.1')
def update(self, req, id, body):
"""Updates the name and/or availability_zone of given aggregate."""
context = _get_context(req)
authorize(context, action='update')
updates = body["aggregate"]
if 'name' in updates:
updates['name'] = common.normalize_name(updates['name'])
try:
aggregate = self.api.update_aggregate(context, id, updates)

View File

@@ -139,7 +139,13 @@ class AvailabilityZone(extensions.V21APIExtensionBase):
# NOTE(gmann): This function is not supposed to use 'body_deprecated_param'
# parameter as this is placed to handle scheduler_hint extension for V2.1.
def server_create(self, server_dict, create_kwargs, body_deprecated_param):
# NOTE(alex_xu): For v2.1 compat mode, we strip the spaces when create
# availability_zone. But we don't strip at here for backward-compatible
# with some users already created availability_zone with
# leading/trailing spaces with legacy v2 API.
create_kwargs['availability_zone'] = server_dict.get(ATTRIBUTE_NAME)
def get_server_create_schema(self):
def get_server_create_schema(self, version):
if version == "2.0":
return schema.server_create_v20
return schema.server_create

View File

@@ -73,5 +73,5 @@ class BlockDeviceMapping(extensions.V21APIExtensionBase):
# Unset the legacy_bdm flag if we got a block device mapping.
create_kwargs['legacy_bdm'] = False
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return schema_block_device_mapping.server_create

View File

@@ -64,5 +64,5 @@ class BlockDeviceMappingV1(extensions.V21APIExtensionBase):
# Sets the legacy_bdm flag if we got a legacy block device mapping.
create_kwargs['legacy_bdm'] = True
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return schema_block_device_mapping.server_create

View File

@@ -194,6 +194,9 @@ class CellsController(wsgi.Controller):
* Merging existing transport URL with transport information.
"""
if 'name' in cell:
cell['name'] = common.normalize_name(cell['name'])
# Start with the cell type conversion
if 'type' in cell:
cell['is_parent'] = cell['type'] == 'parent'
@@ -236,7 +239,8 @@ class CellsController(wsgi.Controller):
# returning a response.
@extensions.expected_errors((400, 403, 501))
@common.check_cells_enabled
@validation.schema(cells.create)
@validation.schema(cells.create_v20, '2.0', '2.0')
@validation.schema(cells.create, '2.1')
def create(self, req, body):
"""Create a child cell entry."""
context = req.environ['nova.context']
@@ -253,7 +257,8 @@ class CellsController(wsgi.Controller):
@extensions.expected_errors((400, 403, 404, 501))
@common.check_cells_enabled
@validation.schema(cells.update)
@validation.schema(cells.update_v20, '2.0', '2.0')
@validation.schema(cells.update, '2.1')
def update(self, req, id, body):
"""Update a child cell entry. 'id' is the cell name to update."""
context = req.environ['nova.context']

View File

@@ -73,5 +73,5 @@ class ConfigDrive(extensions.V21APIExtensionBase):
def server_create(self, server_dict, create_kwargs, body_deprecated_param):
create_kwargs['config_drive'] = server_dict.get(ATTRIBUTE_NAME)
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return schema_config_drive.server_create

View File

@@ -36,7 +36,8 @@ class CreateBackupController(wsgi.Controller):
@extensions.expected_errors((400, 403, 404, 409))
@wsgi.action('createBackup')
@validation.schema(create_backup.create_backup)
@validation.schema(create_backup.create_backup_v20, '2.0', '2.0')
@validation.schema(create_backup.create_backup, '2.1')
def _create_backup(self, req, id, body):
"""Backup a server instance.
@@ -52,7 +53,7 @@ class CreateBackupController(wsgi.Controller):
authorize(context)
entity = body["createBackup"]
image_name = entity["name"]
image_name = common.normalize_name(entity["name"])
backup_type = entity["backup_type"]
rotation = int(entity["rotation"])

View File

@@ -146,7 +146,7 @@ class DiskConfig(extensions.V21APIExtensionBase):
server_rebuild = server_create
server_resize = server_create
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return disk_config.server_create
get_server_update_schema = get_server_create_schema

View File

@@ -54,7 +54,8 @@ class FlavorManageController(wsgi.Controller):
# as this operation complete the creation of flavor resource.
@wsgi.action("create")
@extensions.expected_errors((400, 409, 500))
@validation.schema(flavor_manage.create)
@validation.schema(flavor_manage.create_v20, '2.0', '2.0')
@validation.schema(flavor_manage.create, '2.1')
def _create(self, req, body):
context = req.environ['nova.context']
authorize(context)

View File

@@ -18,6 +18,7 @@
import webob
import webob.exc
from nova.api.openstack import common
from nova.api.openstack.compute.schemas import keypairs
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
@@ -93,7 +94,8 @@ class KeypairController(wsgi.Controller):
@wsgi.Controller.api_version("2.1", "2.1") # noqa
@extensions.expected_errors((400, 403, 409))
@validation.schema(keypairs.create)
@validation.schema(keypairs.create_v20, "2.0", "2.0")
@validation.schema(keypairs.create, "2.1", "2.1")
def create(self, req, body):
"""Create or import keypair.
@@ -111,7 +113,7 @@ class KeypairController(wsgi.Controller):
def _create(self, req, body, user_id=None, **keypair_filters):
context = req.environ['nova.context']
params = body['keypair']
name = params['name']
name = common.normalize_name(params['name'])
key_type = params.get('type', keypair_obj.KEYPAIR_TYPE_SSH)
user_id = user_id or context.user_id
authorize(context, action='create',
@@ -304,7 +306,14 @@ class Keypairs(extensions.V21APIExtensionBase):
# NOTE(gmann): This function is not supposed to use 'body_deprecated_param'
# parameter as this is placed to handle scheduler_hint extension for V2.1.
def server_create(self, server_dict, create_kwargs, body_deprecated_param):
# NOTE(alex_xu): The v2.1 API compat mode, we strip the spaces for
# keypair create. But we didn't strip spaces at here for
# backward-compatible some users already created keypair and name with
# leading/trailing spaces by legacy v2 API.
create_kwargs['key_name'] = server_dict.get('key_name')
def get_server_create_schema(self):
return keypairs.server_create
def get_server_create_schema(self, version):
if version == '2.0':
return keypairs.server_create_v20
else:
return keypairs.server_create

View File

@@ -60,5 +60,5 @@ class MultipleCreate(extensions.V21APIExtensionBase):
create_kwargs['max_count'] = max_count
create_kwargs['return_reservation_id'] = return_id
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return schema_multiple_create.server_create

View File

@@ -59,7 +59,7 @@ class Personality(extensions.V21APIExtensionBase):
create_kwargs['files_to_inject'] = self._get_injected_files(
server_dict['personality'])
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return personality.server_create
get_server_rebuild_schema = get_server_create_schema

View File

@@ -40,5 +40,5 @@ class PreserveEphemeralRebuild(extensions.V21APIExtensionBase):
rebuild_kwargs['preserve_ephemeral'] = strutils.bool_from_string(
rebuild_dict['preserve_ephemeral'], strict=True)
def get_server_rebuild_schema(self):
def get_server_rebuild_schema(self, version):
return preserve_ephemeral_rebuild.server_rebuild

View File

@@ -44,5 +44,5 @@ class SchedulerHints(extensions.V21APIExtensionBase):
create_kwargs['scheduler_hints'] = scheduler_hints
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return schema.server_create

View File

@@ -18,6 +18,10 @@ from nova.api.validation import parameter_types
availability_zone = copy.deepcopy(parameter_types.name)
availability_zone['type'] = ['string', 'null']
availability_zone_with_leading_trailing_spaces = copy.deepcopy(parameter_types.
name_with_leading_trailing_spaces)
availability_zone_with_leading_trailing_spaces['type'] = ['string', 'null']
create = {
'type': 'object',
@@ -37,6 +41,14 @@ create = {
'additionalProperties': False,
}
create_v20 = copy.deepcopy(create)
create_v20['properties']['aggregate']['properties']['name'] = (parameter_types.
name_with_leading_trailing_spaces)
create_v20['properties']['aggregate']['properties']['availability_zone'] = (
availability_zone_with_leading_trailing_spaces)
update = {
'type': 'object',
'properties': {
@@ -44,7 +56,7 @@ update = {
'aggregate': {
'type': 'object',
'properties': {
'name': parameter_types.name,
'name': parameter_types.name_with_leading_trailing_spaces,
'availability_zone': availability_zone
},
'additionalProperties': False,
@@ -58,6 +70,14 @@ update = {
'additionalProperties': False,
}
update_v20 = copy.deepcopy(update)
update_v20['properties']['aggregate']['properties']['name'] = (parameter_types.
name_with_leading_trailing_spaces)
update_v20['properties']['aggregate']['properties']['availability_zone'] = (
availability_zone_with_leading_trailing_spaces)
add_host = {
'type': 'object',
'properties': {

View File

@@ -17,3 +17,7 @@ from nova.api.validation import parameter_types
server_create = {
'availability_zone': parameter_types.name,
}
server_create_v20 = {
'availability_zone': parameter_types.name_with_leading_trailing_spaces,
}

View File

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from nova.api.validation import parameter_types
@@ -55,6 +57,11 @@ create = {
}
create_v20 = copy.deepcopy(create)
create_v20['properties']['cell']['properties']['name'] = (parameter_types.
cell_name_leading_trailing_spaces)
update = {
'type': 'object',
'properties': {
@@ -85,6 +92,11 @@ update = {
}
update_v20 = copy.deepcopy(create)
update_v20['properties']['cell']['properties']['name'] = (parameter_types.
cell_name_leading_trailing_spaces)
sync_instances = {
'type': 'object',
'properties': {

View File

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from nova.api.validation import parameter_types
@@ -37,3 +39,9 @@ create_backup = {
'required': ['createBackup'],
'additionalProperties': False,
}
create_backup_v20 = copy.deepcopy(create_backup)
create_backup_v20['properties'][
'createBackup']['properties']['name'] = (parameter_types.
name_with_leading_trailing_spaces)

View File

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from nova.api.validation import parameter_types
create = {
@@ -56,3 +58,8 @@ create = {
'required': ['flavor'],
'additionalProperties': False,
}
create_v20 = copy.deepcopy(create)
create_v20['properties']['flavor']['properties']['name'] = (parameter_types.
name_with_leading_trailing_spaces)

View File

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from nova.api.validation import parameter_types
@@ -32,6 +34,12 @@ create = {
'additionalProperties': False,
}
create_v20 = copy.deepcopy(create)
create_v20['properties']['keypair']['properties']['name'] = (parameter_types.
name_with_leading_trailing_spaces)
create_v22 = {
'type': 'object',
'properties': {
@@ -78,3 +86,7 @@ create_v210 = {
server_create = {
'key_name': parameter_types.name,
}
server_create_v20 = {
'key_name': parameter_types.name_with_leading_trailing_spaces,
}

View File

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from nova.api.validation import parameter_types
server_create = {
@@ -29,3 +31,8 @@ server_create = {
}
},
}
server_create_v20 = copy.deepcopy(server_create)
server_create_v20['security_groups']['items']['properties']['name'] = (
parameter_types.name_with_leading_trailing_spaces)

View File

@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from nova.api.validation import parameter_types
@@ -50,6 +52,12 @@ base_create = {
'additionalProperties': False,
}
base_create_v20 = copy.deepcopy(base_create)
base_create_v20['properties']['server'][
'properties']['name'] = parameter_types.name_with_leading_trailing_spaces
base_update = {
'type': 'object',
'properties': {
@@ -65,6 +73,12 @@ base_update = {
'additionalProperties': False,
}
base_update_v20 = copy.deepcopy(base_update)
base_update_v20['properties']['server'][
'properties']['name'] = parameter_types.name_with_leading_trailing_spaces
base_rebuild = {
'type': 'object',
'properties': {
@@ -85,6 +99,12 @@ base_rebuild = {
'additionalProperties': False,
}
base_rebuild_v20 = copy.deepcopy(base_rebuild)
base_rebuild_v20['properties']['rebuild'][
'properties']['name'] = parameter_types.name_with_leading_trailing_spaces
base_resize = {
'type': 'object',
'properties': {
@@ -118,6 +138,12 @@ create_image = {
'additionalProperties': False
}
create_image_v20 = copy.deepcopy(create_image)
create_image_v20['properties']['createImage'][
'properties']['name'] = parameter_types.name_with_leading_trailing_spaces
reboot = {
'type': 'object',
'properties': {

View File

@@ -527,5 +527,7 @@ class SecurityGroups(extensions.V21APIExtensionBase):
create_kwargs['security_group'] = list(
set(create_kwargs['security_group']))
def get_server_create_schema(self):
def get_server_create_schema(self, version):
if version == '2.0':
return schema_security_groups.server_create_v20
return schema_security_groups.server_create

View File

@@ -82,6 +82,10 @@ class ServersController(wsgi.Controller):
schema_server_rebuild = schema_servers.base_rebuild
schema_server_resize = schema_servers.base_resize
schema_server_create_v20 = schema_servers.base_create_v20
schema_server_update_v20 = schema_servers.base_update_v20
schema_server_rebuild_v20 = schema_servers.base_rebuild_v20
@staticmethod
def _add_location(robj):
# Just in case...
@@ -201,7 +205,10 @@ class ServersController(wsgi.Controller):
propagate_map_exceptions=True)
if list(self.create_schema_manager):
self.create_schema_manager.map(self._create_extension_schema,
self.schema_server_create)
self.schema_server_create, '2.1')
self.create_schema_manager.map(self._create_extension_schema,
self.schema_server_create_v20,
'2.0')
else:
LOG.debug("Did not find any server create schemas")
@@ -215,7 +222,10 @@ class ServersController(wsgi.Controller):
propagate_map_exceptions=True)
if list(self.update_schema_manager):
self.update_schema_manager.map(self._update_extension_schema,
self.schema_server_update)
self.schema_server_update, '2.1')
self.update_schema_manager.map(self._update_extension_schema,
self.schema_server_update_v20,
'2.0')
else:
LOG.debug("Did not find any server update schemas")
@@ -229,7 +239,10 @@ class ServersController(wsgi.Controller):
propagate_map_exceptions=True)
if list(self.rebuild_schema_manager):
self.rebuild_schema_manager.map(self._rebuild_extension_schema,
self.schema_server_rebuild)
self.schema_server_rebuild, '2.1')
self.rebuild_schema_manager.map(self._rebuild_extension_schema,
self.schema_server_rebuild_v20,
'2.0')
else:
LOG.debug("Did not find any server rebuild schemas")
@@ -243,7 +256,7 @@ class ServersController(wsgi.Controller):
propagate_map_exceptions=True)
if list(self.resize_schema_manager):
self.resize_schema_manager.map(self._resize_extension_schema,
self.schema_server_resize)
self.schema_server_resize, '2.1')
else:
LOG.debug("Did not find any server resize schemas")
@@ -506,14 +519,15 @@ class ServersController(wsgi.Controller):
@wsgi.response(202)
@extensions.expected_errors((400, 403, 409, 413))
@validation.schema(schema_server_create)
@validation.schema(schema_server_create_v20, '2.0', '2.0')
@validation.schema(schema_server_create, '2.1')
def create(self, req, body):
"""Creates a new server for a given user."""
context = req.environ['nova.context']
server_dict = body['server']
password = self._get_server_admin_password(server_dict)
name = server_dict['name']
name = common.normalize_name(server_dict['name'])
# Arguments to be passed to instance create function
create_kwargs = {}
@@ -704,11 +718,11 @@ class ServersController(wsgi.Controller):
LOG.debug("Running _update_extension_point for %s", ext.obj)
handler.server_update(update_dict, update_kwargs)
def _create_extension_schema(self, ext, create_schema):
def _create_extension_schema(self, ext, create_schema, version):
handler = ext.obj
LOG.debug("Running _create_extension_schema for %s", ext.obj)
schema = handler.get_server_create_schema()
schema = handler.get_server_create_schema(version)
if ext.obj.name == 'SchedulerHints':
# NOTE(oomichi): The request parameter position of scheduler-hint
# extension is different from the other extensions, so here handles
@@ -717,25 +731,25 @@ class ServersController(wsgi.Controller):
else:
create_schema['properties']['server']['properties'].update(schema)
def _update_extension_schema(self, ext, update_schema):
def _update_extension_schema(self, ext, update_schema, version):
handler = ext.obj
LOG.debug("Running _update_extension_schema for %s", ext.obj)
schema = handler.get_server_update_schema()
schema = handler.get_server_update_schema(version)
update_schema['properties']['server']['properties'].update(schema)
def _rebuild_extension_schema(self, ext, rebuild_schema):
def _rebuild_extension_schema(self, ext, rebuild_schema, version):
handler = ext.obj
LOG.debug("Running _rebuild_extension_schema for %s", ext.obj)
schema = handler.get_server_rebuild_schema()
schema = handler.get_server_rebuild_schema(version)
rebuild_schema['properties']['rebuild']['properties'].update(schema)
def _resize_extension_schema(self, ext, resize_schema):
def _resize_extension_schema(self, ext, resize_schema, version):
handler = ext.obj
LOG.debug("Running _resize_extension_schema for %s", ext.obj)
schema = handler.get_server_resize_schema()
schema = handler.get_server_resize_schema(version)
resize_schema['properties']['resize']['properties'].update(schema)
def _delete(self, context, req, instance_uuid):
@@ -753,7 +767,8 @@ class ServersController(wsgi.Controller):
self.compute_api.delete(context, instance)
@extensions.expected_errors((400, 404))
@validation.schema(schema_server_update)
@validation.schema(schema_server_update_v20, '2.0', '2.0')
@validation.schema(schema_server_update, '2.1')
def update(self, req, id, body):
"""Update server then pass on to version-specific controller."""
@@ -762,7 +777,8 @@ class ServersController(wsgi.Controller):
authorize(ctxt, action='update')
if 'name' in body['server']:
update_dict['display_name'] = body['server']['name']
update_dict['display_name'] = common.normalize_name(
body['server']['name'])
if list(self.update_extension_manager):
self.update_extension_manager.map(self._update_extension_point,
@@ -957,7 +973,8 @@ class ServersController(wsgi.Controller):
@wsgi.response(202)
@extensions.expected_errors((400, 403, 404, 409, 413))
@wsgi.action('rebuild')
@validation.schema(schema_server_rebuild)
@validation.schema(schema_server_rebuild_v20, '2.0', '2.0')
@validation.schema(schema_server_rebuild, '2.1')
def _action_rebuild(self, req, id, body):
"""Rebuild an instance with the given attributes."""
rebuild_dict = body['rebuild']
@@ -984,8 +1001,12 @@ class ServersController(wsgi.Controller):
for request_attribute, instance_attribute in attr_map.items():
try:
rebuild_kwargs[instance_attribute] = rebuild_dict[
request_attribute]
if request_attribute == 'name':
rebuild_kwargs[instance_attribute] = common.normalize_name(
rebuild_dict[request_attribute])
else:
rebuild_kwargs[instance_attribute] = rebuild_dict[
request_attribute]
except (KeyError, TypeError):
pass
@@ -1033,14 +1054,15 @@ class ServersController(wsgi.Controller):
@extensions.expected_errors((400, 403, 404, 409))
@wsgi.action('createImage')
@common.check_snapshots_enabled
@validation.schema(schema_servers.create_image)
@validation.schema(schema_servers.create_image, '2.0', '2.0')
@validation.schema(schema_servers.create_image, '2.1')
def _action_create_image(self, req, id, body):
"""Snapshot a server instance."""
context = req.environ['nova.context']
authorize(context, action='create_image')
entity = body["createImage"]
image_name = entity["name"]
image_name = common.normalize_name(entity["name"])
metadata = entity.get('metadata', {})
common.check_img_metadata_properties_quota(context, metadata)

View File

@@ -38,5 +38,5 @@ class UserData(extensions.V21APIExtensionBase):
def server_create(self, server_dict, create_kwargs, body_deprecated_param):
create_kwargs['user_data'] = server_dict.get(ATTRIBUTE_NAME)
def get_server_create_schema(self):
def get_server_create_schema(self, version):
return schema_user_data.server_create

View File

@@ -58,16 +58,48 @@ def _get_printable(exclude=None):
_printable_ws = ''.join(c for c in _get_all_chars()
if unicodedata.category(c) == "Zs")
valid_name_regex = '^(?![%s])[%s]*(?<![%s])$' % (
def _get_printable_no_ws(exclude=None):
if exclude is None:
exclude = []
return ''.join(c for c in _get_all_chars()
if _is_printable(c) and
unicodedata.category(c) != "Zs" and
c not in exclude)
valid_name_regex_base = '^(?![%s])[%s]*(?<![%s])$'
valid_name_regex = valid_name_regex_base % (
re.escape(_printable_ws), re.escape(_get_printable()),
re.escape(_printable_ws))
# cell's name disallow '!', '.' and '@'.
valid_cell_name_regex = '^(?![%s])[%s]*(?<![%s])$' % (
# This regex allows leading/trailing whitespace
valid_name_leading_trailing_spaces_regex_base = (
"^[%(ws)s]*[%(no_ws)s]+[%(ws)s]*$|"
"^[%(ws)s]*[%(no_ws)s][%(no_ws)s%(ws)s]+[%(no_ws)s][%(ws)s]*$")
valid_cell_name_regex = valid_name_regex_base % (
re.escape(_printable_ws),
re.escape(_get_printable(exclude=['!', '.', '@'])),
re.escape(_printable_ws))
# cell's name disallow '!', '.' and '@'.
valid_cell_name_leading_trailing_spaces_regex = (
valid_name_leading_trailing_spaces_regex_base % {
'ws': re.escape(_printable_ws),
'no_ws': re.escape(_get_printable_no_ws(exclude=['!', '.', '@']))})
valid_name_leading_trailing_spaces_regex = (
valid_name_leading_trailing_spaces_regex_base % {
'ws': re.escape(_printable_ws),
'no_ws': re.escape(_get_printable_no_ws())})
boolean = {
'type': ['boolean', 'string'],
'enum': [True, 'True', 'TRUE', 'true', '1', 'ON', 'On', 'on',
@@ -128,6 +160,18 @@ cell_name = {
}
cell_name_leading_trailing_spaces = {
'type': 'string', 'minLength': 1, 'maxLength': 255,
'pattern': valid_cell_name_leading_trailing_spaces_regex,
}
name_with_leading_trailing_spaces = {
'type': 'string', 'minLength': 1, 'maxLength': 255,
'pattern': valid_name_leading_trailing_spaces_regex,
}
tcp_udp_port = {
'type': ['integer', 'string'], 'pattern': '^[0-9]*$',
'minimum': 0, 'maximum': 65535,