Merge "Deprecate file injection"

This commit is contained in:
Zuul 2017-12-13 21:08:27 +00:00 committed by Gerrit Code Review
commit 29e453df8e
59 changed files with 1026 additions and 62 deletions

View File

@ -1739,6 +1739,7 @@ contents:
in: body
required: true
type: string
max_version: 2.56
cores: &cores
description: |
The number of allowed server cores for each tenant.
@ -3300,6 +3301,7 @@ injected_file_content_bytes:
in: body
required: true
type: integer
max_version: 2.56
injected_file_content_bytes_quota_details:
description: |
The object of detailed injected file content bytes quota,
@ -3308,18 +3310,21 @@ injected_file_content_bytes_quota_details:
in: body
required: true
type: object
max_version: 2.56
injected_file_content_bytes_quota_optional:
description: |
The number of allowed bytes of content for each injected file.
in: body
required: false
type: integer
max_version: 2.56
injected_file_path_bytes:
description: |
The number of allowed bytes for each injected file path.
in: body
required: true
type: integer
max_version: 2.56
injected_file_path_bytes_quota_details:
description: |
The object of detailed injected file path bytes quota,
@ -3328,18 +3333,21 @@ injected_file_path_bytes_quota_details:
in: body
required: true
type: object
max_version: 2.56
injected_file_path_bytes_quota_optional:
description: |
The number of allowed bytes for each injected file path.
in: body
required: false
type: integer
max_version: 2.56
injected_files: &injected_files
description: |
The number of allowed injected files for each tenant.
in: body
required: true
type: integer
max_version: 2.56
injected_files_quota_class: &injected_files_quota_class
<<: *injected_files
description: |
@ -3354,12 +3362,14 @@ injected_files_quota_details:
in: body
required: true
type: object
max_version: 2.56
injected_files_quota_optional:
description: |
The number of allowed injected files for each tenant.
in: body
required: false
type: integer
max_version: 2.56
injectNetworkInfo:
description: |
The action.
@ -4575,6 +4585,7 @@ path:
in: body
required: true
type: string
max_version: 2.56
pause:
description: |
The action to pause a server.
@ -4603,6 +4614,7 @@ personality:
in: body
required: false
type: array
max_version: 2.56
policies:
description: |
A list of exactly one policy name to associate with the server group. The
@ -5846,6 +5858,22 @@ user_data:
in: body
required: false
type: string
user_data_rebuild_req:
description: |
Configuration information or scripts to use upon rebuild.
Must be Base64 encoded. If ``null`` is specified, the existing user_data
is unset.
in: body
required: false
type: string
min_version: 2.57
user_data_rebuild_resp:
in: body
required: true
type: string
description: |
The current user_data for the instance.
min_version: 2.57
user_id:
description: |
The user ID of the user who owns the server.

View File

@ -488,6 +488,7 @@ Request
- preserve_ephemeral: preserve_ephemeral
- description: server_description
- key_name: key_name_rebuild_req
- user_data: user_data_rebuild_req
**Example Rebuild Server (rebuild Action) (v2.54)**
@ -536,6 +537,7 @@ Response
- description: server_description_resp
- tags: tags
- key_name: key_name_rebuild_resp
- user_data: user_data_rebuild_resp
**Example Rebuild Server (rebuild Action) (v2.54)**

View File

@ -44,6 +44,10 @@ the fixed IP address to assign to the server interface.
**Server personality**
.. note:: The use of personality files is deprecated starting with the 2.57
microversion. Use ``metadata`` and ``user_data`` to customize a server
instance.
To customize the personality of a server instance, you can inject data
into its file system. For example, you might insert ssh keys, set
configuration files, or store data that you want to retrieve from inside

View File

@ -0,0 +1,18 @@
{
"limits": {
"absolute": {
"maxServerMeta": 128,
"maxTotalCores": 20,
"maxTotalInstances": 10,
"maxTotalKeypairs": 100,
"maxTotalRAMSize": 51200,
"maxServerGroups": 10,
"maxServerGroupMembers": 10,
"totalCoresUsed": 0,
"totalInstancesUsed": 0,
"totalRAMUsed": 0,
"totalServerGroupsUsed": 0
},
"rate": []
}
}

View File

@ -0,0 +1,12 @@
{
"quota_class_set": {
"cores": 20,
"id": "test_class",
"instances": 10,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,11 @@
{
"quota_class_set": {
"instances": 50,
"cores": 50,
"ram": 51200,
"metadata_items": 128,
"key_pairs": 100,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,11 @@
{
"quota_class_set": {
"cores": 50,
"instances": 50,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,12 @@
{
"quota_set": {
"cores": 20,
"id": "fake_tenant",
"instances": 10,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,40 @@
{
"quota_set": {
"cores": {
"in_use": 0,
"limit": 20,
"reserved": 0
},
"id": "fake_tenant",
"instances": {
"in_use": 0,
"limit": 10,
"reserved": 0
},
"key_pairs": {
"in_use": 0,
"limit": 100,
"reserved": 0
},
"metadata_items": {
"in_use": 0,
"limit": 128,
"reserved": 0
},
"ram": {
"in_use": 0,
"limit": 51200,
"reserved": 0
},
"server_group_members": {
"in_use": 0,
"limit": 10,
"reserved": 0
},
"server_groups": {
"in_use": 0,
"limit": 10,
"reserved": 0
}
}
}

View File

@ -0,0 +1,12 @@
{
"quota_set": {
"cores": 20,
"id": "fake_tenant",
"instances": 10,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,6 @@
{
"quota_set": {
"force": "True",
"instances": 45
}
}

View File

@ -0,0 +1,11 @@
{
"quota_set": {
"cores": 20,
"instances": 45,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,5 @@
{
"quota_set": {
"instances": 20
}
}

View File

@ -0,0 +1,11 @@
{
"quota_set": {
"cores": 20,
"instances": 20,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,12 @@
{
"quota_set": {
"cores": 20,
"id": "fake_tenant",
"instances": 10,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,5 @@
{
"quota_set": {
"instances": 9
}
}

View File

@ -0,0 +1,11 @@
{
"quota_set": {
"cores": 20,
"instances": 9,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,61 @@
{
"server": {
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"addr": "192.168.0.3",
"version": 4
}
]
},
"adminPass": "seekr3t",
"created": "2013-11-14T06:29:00Z",
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "28d8d56f0e3a77e20891f455721cbb68032e017045e20aa5dfc6cb66",
"id": "a0a80a94-3d81-4a10-822a-daa0cf9e870b",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/a0a80a94-3d81-4a10-822a-daa0cf9e870b",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a0a80a94-3d81-4a10-822a-daa0cf9e870b",
"rel": "bookmark"
}
],
"locked": false,
"metadata": {
"meta_var": "meta_val"
},
"name": "foobar",
"key_name": "new-key",
"description": "description of foobar",
"progress": 0,
"status": "ACTIVE",
"OS-DCF:diskConfig": "AUTO",
"tenant_id": "6f70656e737461636b20342065766572",
"updated": "2013-11-14T06:29:02Z",
"user_id": "fake",
"tags": [],
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi"
}
}

View File

@ -0,0 +1,15 @@
{
"rebuild" : {
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"imageRef": "70a599e0-31e7-49b7-b260-868f441e862b",
"name": "foobar",
"key_name": "new-key",
"description": "description of foobar",
"adminPass": "seekr3t",
"metadata" : {
"meta_var": "meta_val"
},
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi"
}
}

View File

@ -0,0 +1,21 @@
{
"server" : {
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"name" : "new-server-test",
"imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b",
"flavorRef" : "http://openstack.example.com/flavors/1",
"availability_zone": "nova",
"OS-DCF:diskConfig": "AUTO",
"metadata" : {
"My Server Name" : "Apache1"
},
"security_groups": [
{
"name": "default"
}
],
"user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
"networks": "auto"
}
}

View File

@ -0,0 +1,22 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"adminPass": "S5wqy9sPYUvU",
"id": "97108291-2fd7-4dc2-a909-eaae0306a6a9",
"links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/97108291-2fd7-4dc2-a909-eaae0306a6a9",
"rel": "bookmark"
}
],
"security_groups": [
{
"name": "default"
}
]
}
}

View File

@ -19,7 +19,7 @@
}
],
"status": "CURRENT",
"version": "2.56",
"version": "2.57",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}

View File

@ -22,7 +22,7 @@
}
],
"status": "CURRENT",
"version": "2.56",
"version": "2.57",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}

View File

@ -133,6 +133,10 @@ REST_API_VERSION_HISTORY = """REST API Version History:
* 2.56 - Add a host parameter in migrate request body in order to
enable users to specify a target host in cold migration.
The target host is checked by the scheduler.
* 2.57 - Deprecated personality files from POST /servers and the rebuild
server action APIs. Added the ability to pass new user_data to
the rebuild server action API. Personality / file injection
related limits and quota resources are also removed.
"""
# The minimum and maximum versions of the API supported
@ -141,7 +145,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
# Note(cyeoh): This only applies for the v2.1 API once microversions
# support is fully merged. It does not affect the V2 API.
_MIN_API_VERSION = "2.1"
_MAX_API_VERSION = "2.56"
_MAX_API_VERSION = "2.57"
DEFAULT_API_VERSION = _MIN_API_VERSION
# Almost all proxy APIs which are related to network, images and baremetal

View File

@ -32,6 +32,15 @@ from nova import quota
QUOTAS = quota.QUOTAS
# This is a list of limits which needs to filter out from the API response.
# This is due to the deprecation of network related proxy APIs, the related
# limit should be removed from the API also.
FILTERED_LIMITS_2_36 = ['floating_ips', 'security_groups',
'security_group_rules']
FILTERED_LIMITS_2_57 = list(FILTERED_LIMITS_2_36)
FILTERED_LIMITS_2_57.extend(['injected_files', 'injected_file_content_bytes'])
class LimitsController(wsgi.Controller):
"""Controller for accessing limits in the OpenStack API."""
@ -47,16 +56,22 @@ class LimitsController(wsgi.Controller):
@extensions.expected_errors(())
@validation.query_schema(limits.limits_query_schema)
def index(self, req):
return self._index(req, filter_result=True)
return self._index(req, FILTERED_LIMITS_2_36)
@wsgi.Controller.api_version( # noqa
MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION) # noqa
MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION, '2.56') # noqa
@extensions.expected_errors(())
@validation.query_schema(limits.limits_query_schema)
def index(self, req):
return self._index(req, filter_result=True, max_image_meta=False)
return self._index(req, FILTERED_LIMITS_2_36, max_image_meta=False)
def _index(self, req, filter_result=False, max_image_meta=True):
@wsgi.Controller.api_version('2.57') # noqa
@extensions.expected_errors(())
@validation.query_schema(limits.limits_query_schema)
def index(self, req):
return self._index(req, FILTERED_LIMITS_2_57, max_image_meta=False)
def _index(self, req, filtered_limits=None, max_image_meta=True):
"""Return all global limit information."""
context = req.environ['nova.context']
context.can(limits_policies.BASE_POLICY_NAME)
@ -66,5 +81,5 @@ class LimitsController(wsgi.Controller):
abs_limits = {k: v['limit'] for k, v in quotas.items()}
builder = limits_views.ViewBuilder()
return builder.build(abs_limits, filter_result=filter_result,
return builder.build(abs_limits, filtered_limits=filtered_limits,
max_image_meta=max_image_meta)

View File

@ -16,7 +16,6 @@
import copy
import webob
from nova.api.openstack import api_version_request
from nova.api.openstack.compute.schemas import quota_classes
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
@ -36,8 +35,13 @@ EXTENDED_QUOTAS = ['server_groups', 'server_group_members']
# NOTE(gmann): Network related quotas are filter out in
# microversion 2.50. Bug#1701211.
FILTERED_QUOTAS = ["fixed_ips", "floating_ips", "networks",
"security_group_rules", "security_groups"]
FILTERED_QUOTAS_2_50 = ["fixed_ips", "floating_ips", "networks",
"security_group_rules", "security_groups"]
# Microversion 2.57 removes personality (injected) files from the API.
FILTERED_QUOTAS_2_57 = list(FILTERED_QUOTAS_2_50)
FILTERED_QUOTAS_2_57.extend(['injected_files', 'injected_file_content_bytes',
'injected_file_path_bytes'])
class QuotaClassSetsController(wsgi.Controller):
@ -47,7 +51,8 @@ class QuotaClassSetsController(wsgi.Controller):
def __init__(self, **kwargs):
self.supported_quotas = QUOTAS.resources
def _format_quota_set(self, quota_class, quota_set, req):
def _format_quota_set(self, quota_class, quota_set, filtered_quotas=None,
exclude_server_groups=False):
"""Convert the quota object to a result dict."""
if quota_class:
@ -55,13 +60,13 @@ class QuotaClassSetsController(wsgi.Controller):
else:
result = {}
original_quotas = copy.deepcopy(self.supported_quotas)
if api_version_request.is_supported(req, min_version="2.50"):
if filtered_quotas:
original_quotas = [resource for resource in original_quotas
if resource not in FILTERED_QUOTAS]
if resource not in filtered_quotas]
# NOTE(gmann): Before microversion v2.50, v2.1 API does not return the
# 'server_groups' & 'server_group_members' key in quota class API
# response.
else:
if exclude_server_groups:
for resource in EXTENDED_QUOTAS:
original_quotas.remove(resource)
for resource in original_quotas:
@ -70,17 +75,49 @@ class QuotaClassSetsController(wsgi.Controller):
return dict(quota_class_set=result)
@wsgi.Controller.api_version('2.1', '2.49')
@extensions.expected_errors(())
def show(self, req, id):
return self._show(req, id, exclude_server_groups=True)
@wsgi.Controller.api_version('2.50', '2.56') # noqa
@extensions.expected_errors(())
def show(self, req, id):
return self._show(req, id, FILTERED_QUOTAS_2_50)
@wsgi.Controller.api_version('2.57') # noqa
@extensions.expected_errors(())
def show(self, req, id):
return self._show(req, id, FILTERED_QUOTAS_2_57)
def _show(self, req, id, filtered_quotas=None,
exclude_server_groups=False):
context = req.environ['nova.context']
context.can(qcs_policies.POLICY_ROOT % 'show', {'quota_class': id})
values = QUOTAS.get_class_quotas(context, id)
return self._format_quota_set(id, values, req)
return self._format_quota_set(id, values, filtered_quotas,
exclude_server_groups)
@wsgi.Controller.api_version("2.1", "2.49") # noqa
@extensions.expected_errors(400)
@validation.schema(quota_classes.update, "2.0", "2.49")
@validation.schema(quota_classes.update_v250, "2.50")
@validation.schema(quota_classes.update)
def update(self, req, id, body):
return self._update(req, id, body, exclude_server_groups=True)
@wsgi.Controller.api_version("2.50", "2.56") # noqa
@extensions.expected_errors(400)
@validation.schema(quota_classes.update_v250)
def update(self, req, id, body):
return self._update(req, id, body, FILTERED_QUOTAS_2_50)
@wsgi.Controller.api_version("2.57") # noqa
@extensions.expected_errors(400)
@validation.schema(quota_classes.update_v257)
def update(self, req, id, body):
return self._update(req, id, body, FILTERED_QUOTAS_2_57)
def _update(self, req, id, body, filtered_quotas=None,
exclude_server_groups=False):
context = req.environ['nova.context']
context.can(qcs_policies.POLICY_ROOT % 'update', {'quota_class': id})
try:
@ -99,4 +136,5 @@ class QuotaClassSetsController(wsgi.Controller):
objects.Quotas.create_class(context, quota_class, key, value)
values = QUOTAS.get_class_quotas(context, quota_class)
return self._format_quota_set(None, values, req)
return self._format_quota_set(None, values, filtered_quotas,
exclude_server_groups)

View File

@ -38,8 +38,12 @@ from nova import quota
CONF = nova.conf.CONF
QUOTAS = quota.QUOTAS
FILTERED_QUOTAS = ["fixed_ips", "floating_ips", "networks",
"security_group_rules", "security_groups"]
FILTERED_QUOTAS_2_36 = ["fixed_ips", "floating_ips", "networks",
"security_group_rules", "security_groups"]
FILTERED_QUOTAS_2_57 = list(FILTERED_QUOTAS_2_36)
FILTERED_QUOTAS_2_57.extend(['injected_files', 'injected_file_content_bytes',
'injected_file_path_bytes'])
class QuotaSetsController(wsgi.Controller):
@ -106,10 +110,16 @@ class QuotaSetsController(wsgi.Controller):
def show(self, req, id):
return self._show(req, id, [])
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa
@wsgi.Controller.api_version( # noqa
MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, '2.56')
@extensions.expected_errors(400)
def show(self, req, id):
return self._show(req, id, FILTERED_QUOTAS)
return self._show(req, id, FILTERED_QUOTAS_2_36)
@wsgi.Controller.api_version('2.57') # noqa
@extensions.expected_errors(400)
def show(self, req, id):
return self._show(req, id, FILTERED_QUOTAS_2_57)
@validation.query_schema(quota_sets.query_schema)
def _show(self, req, id, filtered_quotas):
@ -128,10 +138,16 @@ class QuotaSetsController(wsgi.Controller):
def detail(self, req, id):
return self._detail(req, id, [])
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa
@wsgi.Controller.api_version( # noqa
MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, '2.56')
@extensions.expected_errors(400)
def detail(self, req, id):
return self._detail(req, id, FILTERED_QUOTAS)
return self._detail(req, id, FILTERED_QUOTAS_2_36)
@wsgi.Controller.api_version('2.57') # noqa
@extensions.expected_errors(400)
def detail(self, req, id):
return self._detail(req, id, FILTERED_QUOTAS_2_57)
@validation.query_schema(quota_sets.query_schema)
def _detail(self, req, id, filtered_quotas):
@ -151,11 +167,18 @@ class QuotaSetsController(wsgi.Controller):
def update(self, req, id, body):
return self._update(req, id, body, [])
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa
@wsgi.Controller.api_version( # noqa
MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, '2.56')
@extensions.expected_errors(400)
@validation.schema(quota_sets.update_v236)
def update(self, req, id, body):
return self._update(req, id, body, FILTERED_QUOTAS)
return self._update(req, id, body, FILTERED_QUOTAS_2_36)
@wsgi.Controller.api_version('2.57') # noqa
@extensions.expected_errors(400)
@validation.schema(quota_sets.update_v257)
def update(self, req, id, body):
return self._update(req, id, body, FILTERED_QUOTAS_2_57)
@validation.query_schema(quota_sets.query_schema)
def _update(self, req, id, body, filtered_quotas):
@ -221,10 +244,16 @@ class QuotaSetsController(wsgi.Controller):
def defaults(self, req, id):
return self._defaults(req, id, [])
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION) # noqa
@wsgi.Controller.api_version( # noqa
MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, '2.56')
@extensions.expected_errors(400)
def defaults(self, req, id):
return self._defaults(req, id, FILTERED_QUOTAS)
return self._defaults(req, id, FILTERED_QUOTAS_2_36)
@wsgi.Controller.api_version('2.57') # noqa
@extensions.expected_errors(400)
def defaults(self, req, id):
return self._defaults(req, id, FILTERED_QUOTAS_2_57)
def _defaults(self, req, id, filtered_quotas):
context = req.environ['nova.context']

View File

@ -715,3 +715,17 @@ The embedded flavor description will not be included in server representations.
the optional ``host`` string field defaulted to ``null``. If ``host`` is
set the migrate action verifies the provided host with the nova scheduler
and uses it as the destination for the migration.
2.57
----
The 2.57 microversion makes the following changes:
* The ``personality`` parameter is removed from the server create and rebuild
APIs.
* The ``user_data`` parameter is added to the server rebuild API.
* The ``maxPersonality`` and ``maxPersonalitySize`` limits are excluded from
the ``GET /limits`` API response.
* The ``injected_files``, ``injected_file_content_bytes`` and
``injected_file_path_bytes`` quotas are removed from the ``os-quota-sets``
and ``os-quota-class-sets`` APIs.

View File

@ -36,3 +36,12 @@ del update_v250['properties']['quota_class_set']['properties'][
del update_v250['properties']['quota_class_set']['properties'][
'security_group_rules']
del update_v250['properties']['quota_class_set']['properties']['networks']
# 2.57 builds on 2.50 and removes injected_file* quotas.
update_v257 = copy.deepcopy(update_v250)
del update_v257['properties']['quota_class_set']['properties'][
'injected_files']
del update_v257['properties']['quota_class_set']['properties'][
'injected_file_content_bytes']
del update_v257['properties']['quota_class_set']['properties'][
'injected_file_path_bytes']

View File

@ -69,6 +69,14 @@ update = {
update_v236 = copy.deepcopy(update)
update_v236['properties']['quota_set']['properties'] = update_quota_set_v236
# 2.57 builds on 2.36 and removes injected_file* quotas.
update_quota_set_v257 = copy.deepcopy(update_quota_set_v236)
del update_quota_set_v257['injected_files']
del update_quota_set_v257['injected_file_content_bytes']
del update_quota_set_v257['injected_file_path_bytes']
update_v257 = copy.deepcopy(update_v236)
update_v257['properties']['quota_set']['properties'] = update_quota_set_v257
query_schema = {
'type': 'object',
'properties': {

View File

@ -14,6 +14,7 @@
import copy
from nova.api.openstack.compute.schemas import user_data
from nova.api.validation import parameter_types
from nova.api.validation.parameter_types import multi_params
from nova.objects import instance
@ -139,6 +140,11 @@ base_create_v252['properties']['server']['properties']['tags'] = {
}
# 2.57 builds on 2.52 and removes the personality parameter.
base_create_v257 = copy.deepcopy(base_create_v252)
base_create_v257['properties']['server']['properties'].pop('personality')
base_update = {
'type': 'object',
'properties': {
@ -203,6 +209,18 @@ base_rebuild_v254 = copy.deepcopy(base_rebuild_v219)
base_rebuild_v254['properties']['rebuild'][
'properties']['key_name'] = parameter_types.name_or_none
# 2.57 builds on 2.54 and makes the following changes:
# 1. Remove the personality parameter.
# 2. Add the user_data parameter which is nullable so user_data can be reset.
base_rebuild_v257 = copy.deepcopy(base_rebuild_v254)
base_rebuild_v257['properties']['rebuild']['properties'].pop('personality')
base_rebuild_v257['properties']['rebuild']['properties']['user_data'] = ({
'oneOf': [
user_data.common_user_data,
{'type': 'null'}
]
})
resize = {
'type': 'object',
'properties': {

View File

@ -80,11 +80,13 @@ class ServersController(wsgi.Controller):
schema_server_update_v219 = schema_servers.base_update_v219
schema_server_rebuild_v219 = schema_servers.base_rebuild_v219
schema_server_rebuild_v254 = schema_servers.base_rebuild_v254
schema_server_rebuild_v257 = schema_servers.base_rebuild_v257
schema_server_create_v232 = schema_servers.base_create_v232
schema_server_create_v237 = schema_servers.base_create_v237
schema_server_create_v242 = schema_servers.base_create_v242
schema_server_create_v252 = schema_servers.base_create_v252
schema_server_create_v257 = schema_servers.base_create_v257
# NOTE(alex_xu): Please do not add more items into this list. This list
# should be removed in the future.
@ -134,6 +136,7 @@ class ServersController(wsgi.Controller):
# TODO(alex_xu): The final goal is that merging all of
# extended json-schema into server main json-schema.
self._create_schema(self.schema_server_create_v257, '2.57')
self._create_schema(self.schema_server_create_v252, '2.52')
self._create_schema(self.schema_server_create_v242, '2.42')
self._create_schema(self.schema_server_create_v237, '2.37')
@ -447,7 +450,8 @@ class ServersController(wsgi.Controller):
@validation.schema(schema_server_create_v232, '2.32', '2.36')
@validation.schema(schema_server_create_v237, '2.37', '2.41')
@validation.schema(schema_server_create_v242, '2.42', '2.51')
@validation.schema(schema_server_create_v252, '2.52')
@validation.schema(schema_server_create_v252, '2.52', '2.56')
@validation.schema(schema_server_create_v257, '2.57')
def create(self, req, body):
"""Creates a new server for a given user."""
context = req.environ['nova.context']
@ -878,7 +882,8 @@ class ServersController(wsgi.Controller):
@validation.schema(schema_server_rebuild_v20, '2.0', '2.0')
@validation.schema(schema_server_rebuild, '2.1', '2.18')
@validation.schema(schema_server_rebuild_v219, '2.19', '2.53')
@validation.schema(schema_server_rebuild_v254, '2.54')
@validation.schema(schema_server_rebuild_v254, '2.54', '2.56')
@validation.schema(schema_server_rebuild_v257, '2.57')
def _action_rebuild(self, req, id, body):
"""Rebuild an instance with the given attributes."""
rebuild_dict = body['rebuild']
@ -906,6 +911,13 @@ class ServersController(wsgi.Controller):
and 'key_name' in rebuild_dict):
kwargs['key_name'] = rebuild_dict.get('key_name')
# If user_data is not specified, we don't include it in kwargs because
# we don't want to overwrite the existing user_data.
include_user_data = api_version_request.is_supported(
req, min_version='2.57')
if include_user_data and 'user_data' in rebuild_dict:
kwargs['user_data'] = rebuild_dict['user_data']
for request_attribute, instance_attribute in attr_map.items():
try:
if request_attribute == 'name':
@ -962,6 +974,9 @@ class ServersController(wsgi.Controller):
# NOTE(liuyulong): set the new key_name for the API response.
view['server']['key_name'] = instance.key_name
if include_user_data:
view['server']['user_data'] = instance.user_data
robj = wsgi.ResponseObject(view)
return self._add_location(robj)

View File

@ -14,12 +14,6 @@
# under the License.
# This is a list of limits which needs to filter out from the API response.
# This is due to the deprecation of network related proxy APIs, the related
# limit should be removed from the API also.
FILTERED_LIMITS = ['floating_ips', 'security_groups', 'security_group_rules']
class ViewBuilder(object):
"""OpenStack API base limits view builder."""
@ -41,9 +35,10 @@ class ViewBuilder(object):
"server_group_members": ["maxServerGroupMembers"]
}
def build(self, absolute_limits, filter_result=False, max_image_meta=True):
def build(self, absolute_limits, filtered_limits=None,
max_image_meta=True):
absolute_limits = self._build_absolute_limits(
absolute_limits, filter_result=filter_result,
absolute_limits, filtered_limits,
max_image_meta=max_image_meta)
output = {
@ -55,17 +50,17 @@ class ViewBuilder(object):
return output
def _build_absolute_limits(self, absolute_limits, filter_result=False,
def _build_absolute_limits(self, absolute_limits, filtered_limits=None,
max_image_meta=True):
"""Builder for absolute limits
absolute_limits should be given as a dict of limits.
For example: {"ram": 512, "gigabytes": 1024}.
filtered_limits is an optional list of limits to exclude from the
result set.
"""
filtered_limits = []
if filter_result:
filtered_limits = FILTERED_LIMITS
filtered_limits = filtered_limits or []
limits = {}
for name, value in absolute_limits.items():
if (name in self.limit_names and

View File

@ -0,0 +1,18 @@
{
"limits": {
"absolute": {
"maxServerMeta": 128,
"maxTotalCores": 20,
"maxTotalInstances": 10,
"maxTotalKeypairs": 100,
"maxTotalRAMSize": 51200,
"maxServerGroups": 10,
"maxServerGroupMembers": 10,
"totalCoresUsed": 0,
"totalInstancesUsed": 0,
"totalRAMUsed": 0,
"totalServerGroupsUsed": 0
},
"rate": []
}
}

View File

@ -0,0 +1,12 @@
{
"quota_class_set": {
"cores": 20,
"id": "%(set_id)s",
"instances": 10,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,11 @@
{
"quota_class_set": {
"instances": 50,
"cores": 50,
"ram": 51200,
"metadata_items": 128,
"key_pairs": 100,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,11 @@
{
"quota_class_set": {
"cores": 50,
"instances": 50,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,12 @@
{
"quota_set": {
"cores": 20,
"id": "fake_tenant",
"instances": 10,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,40 @@
{
"quota_set": {
"cores": {
"in_use": 0,
"limit": 20,
"reserved": 0
},
"id": "fake_tenant",
"instances": {
"in_use": 0,
"limit": 10,
"reserved": 0
},
"key_pairs": {
"in_use": 0,
"limit": 100,
"reserved": 0
},
"metadata_items": {
"in_use": 0,
"limit": 128,
"reserved": 0
},
"ram": {
"in_use": 0,
"limit": 51200,
"reserved": 0
},
"server_group_members": {
"in_use": 0,
"limit": 10,
"reserved": 0
},
"server_groups": {
"in_use": 0,
"limit": 10,
"reserved": 0
}
}
}

View File

@ -0,0 +1,12 @@
{
"quota_set": {
"cores": 20,
"id": "fake_tenant",
"instances": 10,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,6 @@
{
"quota_set": {
"force": "True",
"instances": 45
}
}

View File

@ -0,0 +1,11 @@
{
"quota_set": {
"cores": 20,
"instances": 45,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,5 @@
{
"quota_set": {
"instances": 20
}
}

View File

@ -0,0 +1,11 @@
{
"quota_set": {
"cores": 20,
"instances": 20,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,12 @@
{
"quota_set": {
"cores": 20,
"id": "fake_tenant",
"instances": 10,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,5 @@
{
"quota_set": {
"instances": 9
}
}

View File

@ -0,0 +1,11 @@
{
"quota_set": {
"cores": 20,
"instances": 9,
"key_pairs": 100,
"metadata_items": 128,
"ram": 51200,
"server_groups": 10,
"server_group_members": 10
}
}

View File

@ -0,0 +1,61 @@
{
"server": {
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"addresses": {
"private": [
{
"addr": "%(ip)s",
"version": 4
}
]
},
"adminPass": "%(password)s",
"created": "%(isotime)s",
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "%(hostid)s",
"id": "%(uuid)s",
"image": {
"id": "%(uuid)s",
"links": [
{
"href": "%(compute_endpoint)s/images/%(uuid)s",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/servers/%(uuid)s",
"rel": "bookmark"
}
],
"locked": false,
"metadata": {
"meta_var": "meta_val"
},
"name": "%(name)s",
"key_name": "%(key_name)s",
"description": "%(description)s",
"progress": 0,
"OS-DCF:diskConfig": "AUTO",
"status": "ACTIVE",
"tenant_id": "6f70656e737461636b20342065766572",
"updated": "%(isotime)s",
"user_id": "fake",
"tags": [],
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi"
}
}

View File

@ -0,0 +1,15 @@
{
"rebuild" : {
"accessIPv4" : "%(access_ip_v4)s",
"accessIPv6" : "%(access_ip_v6)s",
"imageRef" : "%(uuid)s",
"name" : "%(name)s",
"key_name" : "%(key_name)s",
"description" : "%(description)s",
"adminPass" : "%(pass)s",
"metadata" : {
"meta_var" : "meta_val"
},
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi"
}
}

View File

@ -0,0 +1,21 @@
{
"server" : {
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"name" : "new-server-test",
"imageRef" : "%(image_id)s",
"flavorRef" : "http://openstack.example.com/flavors/1",
"availability_zone": "nova",
"OS-DCF:diskConfig": "AUTO",
"metadata" : {
"My Server Name" : "Apache1"
},
"security_groups": [
{
"name": "default"
}
],
"user_data" : "%(user_data)s",
"networks": "auto"
}
}

View File

@ -0,0 +1,22 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"adminPass": "%(password)s",
"id": "%(id)s",
"links": [
{
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/servers/%(uuid)s",
"rel": "bookmark"
}
],
"security_groups": [
{
"name": "default"
}
]
}
}

View File

@ -65,3 +65,15 @@ class LimitsV239Test(api_sample_base.ApiSampleTestBaseV21):
self.api.microversion = self.microversion
response = self._do_get('limits')
self._verify_response('limit-get-resp', {}, response, 200)
class LimitsV257Test(api_sample_base.ApiSampleTestBaseV21):
"""Test limits don't return maxPersonality* fields after 2.57."""
sample_dir = "limits"
microversion = '2.57'
scenarios = [('v2_57', {'api_major_version': 'v2.1'})]
def test_limits_get(self):
self.api.microversion = self.microversion
response = self._do_get('limits')
self._verify_response('limit-get-resp', {}, response, 200)

View File

@ -40,3 +40,8 @@ class QuotaClassesSampleJsonTests(api_sample_base.ApiSampleTestBaseV21):
class QuotaClassesV250SampleJsonTests(QuotaClassesSampleJsonTests):
microversion = '2.50'
scenarios = [('v2_50', {'api_major_version': 'v2.1'})]
class QuotaClassesV257SampleJsonTests(QuotaClassesSampleJsonTests):
microversion = '2.57'
scenarios = [('v2_57', {'api_major_version': 'v2.1'})]

View File

@ -83,6 +83,14 @@ class QuotaSetsSampleJsonTests2_36(QuotaSetsSampleJsonTests):
scenarios = [('v2_36', {'api_major_version': 'v2.1'})]
class QuotaSetsSampleJsonTestsV2_57(QuotaSetsSampleJsonTests):
"""Tests that injected_file* quotas are not in request or response values.
starting with microversion 2.57.
"""
microversion = '2.57'
scenarios = [('v2_57', {'api_major_version': 'v2.1'})]
class NoopQuotaSetsSampleJsonTests(QuotaSetsSampleJsonTests):
sample_dir = "os-quota-sets-noop"

View File

@ -463,9 +463,12 @@ class ServersActionsJson254Test(ServersSampleBase):
sample_dir = 'servers'
scenarios = [('v2_54', {'api_major_version': 'v2.1'})]
def _create_server(self):
return self._post_server()
def test_server_rebuild(self):
fakes.stub_out_key_pair_funcs(self)
uuid = self._post_server()
uuid = self._create_server()
image = fake.get_valid_image_id()
params = {
'uuid': image,
@ -485,6 +488,15 @@ class ServersActionsJson254Test(ServersSampleBase):
self._verify_response('server-action-rebuild-resp', subs, resp, 202)
class ServersActionsJson257Test(ServersActionsJson254Test):
"""Tests rebuilding a server with new user_data."""
microversion = '2.57'
scenarios = [('v2_57', {'api_major_version': 'v2.1'})]
def _create_server(self):
return self._post_server(use_common_server_api_samples=False)
class ServersCreateImageJsonTest(ServersSampleBase,
_ServersActionsJsonTestMixin):
"""Tests the createImage server action API against 2.1."""

View File

@ -33,6 +33,7 @@ class QuotaClassSetsTestV21(test.TestCase):
'security_groups': 10,
'security_group_rules': 20, 'key_pairs': 100,
'injected_file_path_bytes': 255}
filtered_quotas = None
def quota_set(self, class_name):
quotas = copy.deepcopy(self.quota_resources)
@ -58,18 +59,15 @@ class QuotaClassSetsTestV21(test.TestCase):
def test_format_quota_set(self):
quota_set = self.controller._format_quota_set('test_class',
self.quota_resources,
self.req)
self.filtered_quotas)
qs = quota_set['quota_class_set']
self.assertEqual(qs['id'], 'test_class')
self.assertEqual(qs['instances'], 10)
self.assertEqual(qs['cores'], 20)
self.assertEqual(qs['ram'], 51200)
self.assertEqual(qs['metadata_items'], 128)
self.assertEqual(qs['injected_files'], 5)
self.assertEqual(qs['injected_file_path_bytes'], 255)
self.assertEqual(qs['injected_file_content_bytes'], 10240)
self.assertEqual(qs['key_pairs'], 100)
for resource, value in self.quota_resources.items():
self.assertEqual(value, qs[resource])
if self.filtered_quotas:
for resource in self.filtered_quotas:
self.assertNotIn(resource, qs)
self._check_filtered_extended_quota(qs)
def test_quotas_show(self):
@ -135,25 +133,31 @@ class QuotaClassSetsTestV250(QuotaClassSetsTestV21):
'injected_file_path_bytes': 255,
'server_groups': 10,
'server_group_members': 10}
filtered_quotas = quota_classes_v21.FILTERED_QUOTAS_2_50
def _check_filtered_extended_quota(self, quota_set):
self.assertEqual(10, quota_set['server_groups'])
self.assertEqual(10, quota_set['server_group_members'])
self.assertNotIn('floating_ips', quota_set)
self.assertNotIn('fixed_ips', quota_set)
self.assertNotIn('security_groups', quota_set)
self.assertNotIn('security_group_rules', quota_set)
self.assertNotIn('networks', quota_set)
for resource in self.filtered_quotas:
self.assertNotIn(resource, quota_set)
def test_quotas_update_with_filtered_quota(self):
filtered_quotas = ["fixed_ips", "floating_ips", "networks",
"security_group_rules", "security_groups"]
for resource in filtered_quotas:
for resource in self.filtered_quotas:
body = {'quota_class_set': {resource: 10}}
self.assertRaises(self.validation_error, self.controller.update,
self.req, 'test_class', body=body)
class QuotaClassSetsTestV257(QuotaClassSetsTestV250):
api_version = '2.57'
def setUp(self):
super(QuotaClassSetsTestV257, self).setUp()
for resource in quota_classes_v21.FILTERED_QUOTAS_2_57:
self.quota_resources.pop(resource, None)
self.filtered_quotas.extend(quota_classes_v21.FILTERED_QUOTAS_2_57)
class QuotaClassesPolicyEnforcementV21(test.NoDBTestCase):
def setUp(self):

View File

@ -564,6 +564,7 @@ class QuotaSetsPolicyEnforcementV21(test.NoDBTestCase):
class QuotaSetsTestV236(test.NoDBTestCase):
microversion = '2.36'
def setUp(self):
super(QuotaSetsTestV236, self).setUp()
@ -613,7 +614,7 @@ class QuotaSetsTestV236(test.NoDBTestCase):
'server_groups': 10
}
self.controller = quotas_v21.QuotaSetsController()
self.req = fakes.HTTPRequest.blank('', version='2.36')
self.req = fakes.HTTPRequest.blank('', version=self.microversion)
def _ensure_filtered_quotas_existed_in_old_api(self):
res_dict = self.controller.show(self.old_req, 1234)
@ -666,3 +667,11 @@ class QuotaSetsTestV236(test.NoDBTestCase):
body={'quota_set': {'cores': 100}})
for filtered in self.filtered_quotas:
self.assertNotIn(filtered, res_dict['quota_set'])
class QuotaSetsTestV257(QuotaSetsTestV236):
microversion = '2.57'
def setUp(self):
super(QuotaSetsTestV257, self).setUp()
self.filtered_quotas.extend(quotas_v21.FILTERED_QUOTAS_2_57)

View File

@ -2295,6 +2295,105 @@ class ServersControllerRebuildTestV254(ServersControllerRebuildInstanceTest):
self.req, FAKE_UUID, body=body)
class ServersControllerRebuildTestV257(ServersControllerRebuildTestV254):
"""Tests server rebuild at microversion 2.57 where user_data can be
provided and personality files are no longer accepted.
"""
def setUp(self):
super(ServersControllerRebuildTestV257, self).setUp()
self.req.api_version_request = \
api_version_request.APIVersionRequest('2.57')
def test_rebuild_personality(self):
"""Tests that trying to rebuild with personality files fails."""
body = {
"rebuild": {
"imageRef": self.image_uuid,
"personality": [{
"path": "/path/to/file",
"contents": base64.encode_as_text("Test String"),
}]
}
}
ex = self.assertRaises(exception.ValidationError,
self.controller._action_rebuild,
self.req, FAKE_UUID, body=body)
self.assertIn('personality', six.text_type(ex))
def test_rebuild_user_data_old_version(self):
"""Tests that trying to rebuild with user_data before 2.57 fails."""
body = {
"rebuild": {
"imageRef": self.image_uuid,
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi"
}
}
self.req.api_version_request = \
api_version_request.APIVersionRequest('2.55')
ex = self.assertRaises(exception.ValidationError,
self.controller._action_rebuild,
self.req, FAKE_UUID, body=body)
self.assertIn('user_data', six.text_type(ex))
def test_rebuild_user_data_malformed(self):
"""Tests that trying to rebuild with malformed user_data fails."""
body = {
"rebuild": {
"imageRef": self.image_uuid,
"user_data": b'invalid'
}
}
ex = self.assertRaises(exception.ValidationError,
self.controller._action_rebuild,
self.req, FAKE_UUID, body=body)
self.assertIn('user_data', six.text_type(ex))
def test_rebuild_user_data_too_large(self):
"""Tests that passing user_data to rebuild that is too large fails."""
body = {
"rebuild": {
"imageRef": self.image_uuid,
"user_data": ('MQ==' * 16384)
}
}
ex = self.assertRaises(exception.ValidationError,
self.controller._action_rebuild,
self.req, FAKE_UUID, body=body)
self.assertIn('user_data', six.text_type(ex))
@mock.patch.object(context.RequestContext, 'can')
@mock.patch.object(compute_api.API, 'get')
@mock.patch('nova.db.instance_update_and_get_original')
def test_rebuild_reset_user_data(self, mock_update, mock_get, mock_policy):
"""Tests that passing user_data=None resets the user_data on the
instance.
"""
body = {
"rebuild": {
"imageRef": self.image_uuid,
"user_data": None
}
}
mock_get.return_value = fakes.stub_instance_obj(
context.RequestContext(self.req_user_id, self.req_project_id),
user_data='ZWNobyAiaGVsbG8gd29ybGQi')
def fake_instance_update_and_get_original(
ctxt, instance_uuid, values, **kwargs):
# save() is called twice and the second one has system_metadata
# in the updates, so we can ignore that one.
if 'system_metadata' not in values:
self.assertIn('user_data', values)
self.assertIsNone(values['user_data'])
return instance_update_and_get_original(
ctxt, instance_uuid, values, **kwargs)
mock_update.side_effect = fake_instance_update_and_get_original
self.controller._action_rebuild(self.req, FAKE_UUID, body=body)
self.assertEqual(2, mock_update.call_count)
class ServersControllerRebuildTestV219(ServersControllerRebuildInstanceTest):
def setUp(self):
@ -4096,6 +4195,33 @@ class ServersControllerCreateTestV252(test.NoDBTestCase):
exception.ValidationError, self._create_server, tags)
class ServersControllerCreateTestV257(test.NoDBTestCase):
"""Tests that trying to create a server with personality files using
microversion 2.57 fails.
"""
def test_create_server_with_personality_fails(self):
controller = servers.ServersController()
body = {
'server': {
'name': 'no-personality-files',
'imageRef': '6b0edabb-8cde-4684-a3f4-978960a51378',
'flavorRef': '2',
'networks': 'auto',
'personality': [{
'path': '/path/to/file',
'contents': 'ZWNobyAiaGVsbG8gd29ybGQi'
}]
}
}
req = fakes.HTTPRequestV21.blank('/servers', version='2.57')
req.body = jsonutils.dump_as_bytes(body)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
ex = self.assertRaises(
exception.ValidationError, controller.create, req, body=body)
self.assertIn('personality', six.text_type(ex))
class ServersControllerCreateTestWithMock(test.TestCase):
image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'
flavor_ref = 'http://localhost/123/flavors/3'

View File

@ -0,0 +1,17 @@
---
features:
- |
The 2.57 microversion makes the following changes:
* The ``user_data`` parameter is added to the server rebuild API.
* The ``personality`` parameter is removed from the server create and
rebuild APIs. Use the ``user_data`` parameter instead.
* The ``maxPersonality`` and ``maxPersonalitySize`` limits are excluded
from the ``GET /limits`` API response.
* The ``injected_files``, ``injected_file_content_bytes`` and
``injected_file_path_bytes`` quotas are removed from the
``os-quota-sets`` and ``os-quota-class-sets`` APIs.
See the `spec`_ for more details and reasoning.
.. _spec: https://specs.openstack.org/openstack/nova-specs/specs/queens/approved/deprecate-file-injection.html