Merge "Filter leading/trailing spaces for name field in v2.1 compat mode"
This commit is contained in:
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"])
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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': {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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': {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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': {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user