REST API changes for user settable server description
This patches adds changes to the Nova REST API to allow users to create a server with a description, rebuild a server with a description, update the description, and get the description in the server details. Note: Future commits will be done to support the server description in python-novaclient and openstack-client. APIImpact Implements blueprint: user-settable-server-description Change-Id: I74b1a340c5ab98fdea2186e87dd13f42ce7c7661
This commit is contained in:
parent
54f7925576
commit
4841cab03e
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"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": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "28d8d56f0e3a77e20891f455721cbb68032e017045e20aa5dfc6cb66",
|
||||
"id": "a0a80a94-3d81-4a10-822a-daa0cf9e870b",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/a0a80a94-3d81-4a10-822a-daa0cf9e870b",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/a0a80a94-3d81-4a10-822a-daa0cf9e870b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"locked": false,
|
||||
"metadata": {
|
||||
"meta_var": "meta_val"
|
||||
},
|
||||
"name": "foobar",
|
||||
"description" : "description of foobar",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "2013-11-14T06:29:02Z",
|
||||
"user_id": "fake"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"rebuild" : {
|
||||
"accessIPv4" : "1.2.3.4",
|
||||
"accessIPv6" : "80fe::",
|
||||
"imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"name" : "foobar",
|
||||
"description" : "description of foobar",
|
||||
"adminPass" : "seekr3t",
|
||||
"metadata" : {
|
||||
"meta_var" : "meta_val"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"server": {
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"addr": "192.168.0.3",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2015-12-07T17:24:14Z",
|
||||
"description": "new-server-description",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "c656e68b04b483cfc87cdbaa2346557b174ec1cb6be6afbd2a0133a0",
|
||||
"id": "ddb205dc-717e-496e-8e96-88a3b31b075d",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/ddb205dc-717e-496e-8e96-88a3b31b075d",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/ddb205dc-717e-496e-8e96-88a3b31b075d",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"locked": false,
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "2015-12-07T17:24:15Z",
|
||||
"user_id": "fake"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"server" : {
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"name" : "new-server-test",
|
||||
"description" : "new-server-description",
|
||||
"imageRef" : "http://glance.openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"flavorRef" : "http://openstack.example.com/flavors/1",
|
||||
"metadata" : {
|
||||
"My Server Name" : "Apache1"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"server": {
|
||||
"adminPass": "rySfUy7xL4C5",
|
||||
"id": "19923676-e78b-46fb-af62-a5942aece2ac",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/19923676-e78b-46fb-af62-a5942aece2ac",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/19923676-e78b-46fb-af62-a5942aece2ac",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"server" : {
|
||||
"name" : "updated-server-test",
|
||||
"description" : "updated-server-description",
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"server": {
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "192.168.0.3",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2015-12-07T19:19:36Z",
|
||||
"description": "updated-server-description",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "4e17a358ca9bbc8ac6e215837b6410c0baa21b2463fefe3e8f712b31",
|
||||
"id": "c509708e-f0c6-461f-b2b3-507547959eb2",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/c509708e-f0c6-461f-b2b3-507547959eb2",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/c509708e-f0c6-461f-b2b3-507547959eb2",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"locked": false,
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "updated-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "2015-12-07T19:19:36Z",
|
||||
"user_id": "fake"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"servers": [
|
||||
{
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"addr": "192.168.0.3",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2015-12-07T19:54:48Z",
|
||||
"description": "new-server-description",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "a672ab12738567bfcb852c846d66a6ce5c3555b42d73db80bdc6f1a4",
|
||||
"id": "91965362-fd86-4543-8ce1-c17074d2984d",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/91965362-fd86-4543-8ce1-c17074d2984d",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/91965362-fd86-4543-8ce1-c17074d2984d",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"locked": false,
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "2015-12-07T19:54:49Z",
|
||||
"user_id": "fake"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"servers": [
|
||||
{
|
||||
"id": "78d95942-8805-4597-b1af-3d0e38330758",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/78d95942-8805-4597-b1af-3d0e38330758",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/78d95942-8805-4597-b1af-3d0e38330758",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"name": "new-server-test"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -19,7 +19,7 @@
|
|||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.18",
|
||||
"version": "2.19",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.18",
|
||||
"version": "2.19",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
|||
* 2.16 - Exposes host_status for servers/detail and servers/{server_id}
|
||||
* 2.17 - Add trigger_crash_dump to server actions
|
||||
* 2.18 - Makes project_id optional in v2.1
|
||||
* 2.19 - Allow user to set and get the server description
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
|
@ -68,7 +69,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.18"
|
||||
_MAX_API_VERSION = "2.19"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
|
|
@ -58,6 +58,11 @@ base_create_v20['properties']['server'][
|
|||
'properties']['name'] = parameter_types.name_with_leading_trailing_spaces
|
||||
|
||||
|
||||
base_create_v219 = copy.deepcopy(base_create)
|
||||
base_create_v219['properties']['server'][
|
||||
'properties']['description'] = parameter_types.description
|
||||
|
||||
|
||||
base_update = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
|
@ -78,6 +83,9 @@ base_update_v20 = copy.deepcopy(base_update)
|
|||
base_update_v20['properties']['server'][
|
||||
'properties']['name'] = parameter_types.name_with_leading_trailing_spaces
|
||||
|
||||
base_update_v219 = copy.deepcopy(base_update)
|
||||
base_update_v219['properties']['server'][
|
||||
'properties']['description'] = parameter_types.description
|
||||
|
||||
base_rebuild = {
|
||||
'type': 'object',
|
||||
|
@ -104,6 +112,9 @@ base_rebuild_v20 = copy.deepcopy(base_rebuild)
|
|||
base_rebuild_v20['properties']['rebuild'][
|
||||
'properties']['name'] = parameter_types.name_with_leading_trailing_spaces
|
||||
|
||||
base_rebuild_v219 = copy.deepcopy(base_rebuild)
|
||||
base_rebuild_v219['properties']['rebuild'][
|
||||
'properties']['description'] = parameter_types.description
|
||||
|
||||
base_resize = {
|
||||
'type': 'object',
|
||||
|
|
|
@ -82,6 +82,10 @@ class ServersController(wsgi.Controller):
|
|||
schema_server_update_v20 = schema_servers.base_update_v20
|
||||
schema_server_rebuild_v20 = schema_servers.base_rebuild_v20
|
||||
|
||||
schema_server_create_v219 = schema_servers.base_create_v219
|
||||
schema_server_update_v219 = schema_servers.base_update_v219
|
||||
schema_server_rebuild_v219 = schema_servers.base_rebuild_v219
|
||||
|
||||
@staticmethod
|
||||
def _add_location(robj):
|
||||
# Just in case...
|
||||
|
@ -206,6 +210,9 @@ class ServersController(wsgi.Controller):
|
|||
invoke_kwds={"extension_info": self.extension_info},
|
||||
propagate_map_exceptions=True)
|
||||
if list(self.create_schema_manager):
|
||||
self.create_schema_manager.map(self._create_extension_schema,
|
||||
self.schema_server_create_v219,
|
||||
'2.19')
|
||||
self.create_schema_manager.map(self._create_extension_schema,
|
||||
self.schema_server_create, '2.1')
|
||||
self.create_schema_manager.map(self._create_extension_schema,
|
||||
|
@ -223,6 +230,9 @@ class ServersController(wsgi.Controller):
|
|||
invoke_kwds={"extension_info": self.extension_info},
|
||||
propagate_map_exceptions=True)
|
||||
if list(self.update_schema_manager):
|
||||
self.update_schema_manager.map(self._update_extension_schema,
|
||||
self.schema_server_update_v219,
|
||||
'2.19')
|
||||
self.update_schema_manager.map(self._update_extension_schema,
|
||||
self.schema_server_update, '2.1')
|
||||
self.update_schema_manager.map(self._update_extension_schema,
|
||||
|
@ -240,6 +250,9 @@ class ServersController(wsgi.Controller):
|
|||
invoke_kwds={"extension_info": self.extension_info},
|
||||
propagate_map_exceptions=True)
|
||||
if list(self.rebuild_schema_manager):
|
||||
self.rebuild_schema_manager.map(self._rebuild_extension_schema,
|
||||
self.schema_server_rebuild_v219,
|
||||
'2.19')
|
||||
self.rebuild_schema_manager.map(self._rebuild_extension_schema,
|
||||
self.schema_server_rebuild, '2.1')
|
||||
self.rebuild_schema_manager.map(self._rebuild_extension_schema,
|
||||
|
@ -514,7 +527,8 @@ class ServersController(wsgi.Controller):
|
|||
@wsgi.response(202)
|
||||
@extensions.expected_errors((400, 403, 409, 413))
|
||||
@validation.schema(schema_server_create_v20, '2.0', '2.0')
|
||||
@validation.schema(schema_server_create, '2.1')
|
||||
@validation.schema(schema_server_create, '2.1', '2.18')
|
||||
@validation.schema(schema_server_create_v219, '2.19')
|
||||
def create(self, req, body):
|
||||
"""Creates a new server for a given user."""
|
||||
|
||||
|
@ -523,6 +537,16 @@ class ServersController(wsgi.Controller):
|
|||
password = self._get_server_admin_password(server_dict)
|
||||
name = common.normalize_name(server_dict['name'])
|
||||
|
||||
if api_version_request.is_supported(req, min_version='2.19'):
|
||||
if 'description' in server_dict:
|
||||
# This is allowed to be None
|
||||
description = server_dict['description']
|
||||
else:
|
||||
# No default description
|
||||
description = None
|
||||
else:
|
||||
description = name
|
||||
|
||||
# Arguments to be passed to instance create function
|
||||
create_kwargs = {}
|
||||
|
||||
|
@ -596,7 +620,7 @@ class ServersController(wsgi.Controller):
|
|||
inst_type,
|
||||
image_uuid,
|
||||
display_name=name,
|
||||
display_description=name,
|
||||
display_description=description,
|
||||
availability_zone=availability_zone,
|
||||
forced_host=host, forced_node=node,
|
||||
metadata=server_dict.get('metadata', {}),
|
||||
|
@ -767,7 +791,8 @@ class ServersController(wsgi.Controller):
|
|||
|
||||
@extensions.expected_errors((400, 404))
|
||||
@validation.schema(schema_server_update_v20, '2.0', '2.0')
|
||||
@validation.schema(schema_server_update, '2.1')
|
||||
@validation.schema(schema_server_update, '2.1', '2.18')
|
||||
@validation.schema(schema_server_update_v219, '2.19')
|
||||
def update(self, req, id, body):
|
||||
"""Update server then pass on to version-specific controller."""
|
||||
|
||||
|
@ -779,6 +804,10 @@ class ServersController(wsgi.Controller):
|
|||
update_dict['display_name'] = common.normalize_name(
|
||||
body['server']['name'])
|
||||
|
||||
if 'description' in body['server']:
|
||||
# This is allowed to be None (remove description)
|
||||
update_dict['display_description'] = body['server']['description']
|
||||
|
||||
if list(self.update_extension_manager):
|
||||
self.update_extension_manager.map(self._update_extension_point,
|
||||
body['server'], update_dict)
|
||||
|
@ -972,7 +1001,8 @@ class ServersController(wsgi.Controller):
|
|||
@extensions.expected_errors((400, 403, 404, 409, 413))
|
||||
@wsgi.action('rebuild')
|
||||
@validation.schema(schema_server_rebuild_v20, '2.0', '2.0')
|
||||
@validation.schema(schema_server_rebuild, '2.1')
|
||||
@validation.schema(schema_server_rebuild, '2.1', '2.18')
|
||||
@validation.schema(schema_server_rebuild_v219, '2.19')
|
||||
def _action_rebuild(self, req, id, body):
|
||||
"""Rebuild an instance with the given attributes."""
|
||||
rebuild_dict = body['rebuild']
|
||||
|
@ -988,6 +1018,7 @@ class ServersController(wsgi.Controller):
|
|||
|
||||
attr_map = {
|
||||
'name': 'display_name',
|
||||
'description': 'display_description',
|
||||
'metadata': 'metadata',
|
||||
}
|
||||
|
||||
|
|
|
@ -309,4 +309,8 @@ class ViewBuilderV21(ViewBuilder):
|
|||
server["server"]["locked"] = (True if instance["locked_by"]
|
||||
else False)
|
||||
|
||||
if api_version_request.is_supported(request, min_version="2.19"):
|
||||
server["server"]["description"] = instance.get(
|
||||
"display_description")
|
||||
|
||||
return server
|
||||
|
|
|
@ -167,3 +167,9 @@ user documentation.
|
|||
2.18
|
||||
----
|
||||
Establishes a set of routes that makes project_id an optional construct in v2.1.
|
||||
|
||||
2.19
|
||||
----
|
||||
Allow the user to set and get the server description.
|
||||
The user will be able to set the description when creating, rebuilding,
|
||||
or updating a server, and get the description as part of the server details.
|
||||
|
|
|
@ -103,6 +103,13 @@ valid_name_leading_trailing_spaces_regex = (
|
|||
valid_name_regex_obj = re.compile(valid_name_regex, re.UNICODE)
|
||||
|
||||
|
||||
valid_description_regex_base = '^[%s]*$'
|
||||
|
||||
|
||||
valid_description_regex = valid_description_regex_base % (
|
||||
re.escape(_get_printable()))
|
||||
|
||||
|
||||
boolean = {
|
||||
'type': ['boolean', 'string'],
|
||||
'enum': [True, 'True', 'TRUE', 'true', '1', 'ON', 'On', 'on',
|
||||
|
@ -175,6 +182,12 @@ name_with_leading_trailing_spaces = {
|
|||
}
|
||||
|
||||
|
||||
description = {
|
||||
'type': ['string', 'null'], 'minLength': 0, 'maxLength': 255,
|
||||
'pattern': valid_description_regex,
|
||||
}
|
||||
|
||||
|
||||
tcp_udp_port = {
|
||||
'type': ['integer', 'string'], 'pattern': '^[0-9]*$',
|
||||
'minimum': 0, 'maximum': 65535,
|
||||
|
|
|
@ -879,7 +879,7 @@ class API(base.Base):
|
|||
'root_gb': instance_type['root_gb'],
|
||||
'ephemeral_gb': instance_type['ephemeral_gb'],
|
||||
'display_name': display_name,
|
||||
'display_description': display_description or '',
|
||||
'display_description': display_description,
|
||||
'user_data': user_data,
|
||||
'key_name': key_name,
|
||||
'key_data': key_data,
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"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": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"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",
|
||||
"description": "%(description)s",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "%(isotime)s",
|
||||
"user_id": "fake"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"rebuild" : {
|
||||
"accessIPv4" : "%(access_ip_v4)s",
|
||||
"accessIPv6" : "%(access_ip_v6)s",
|
||||
"imageRef" : "%(uuid)s",
|
||||
"name" : "%(name)s",
|
||||
"description" : "%(description)s",
|
||||
"adminPass" : "%(pass)s",
|
||||
"metadata" : {
|
||||
"meta_var" : "meta_val"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"server": {
|
||||
"accessIPv4": "%(access_ip_v4)s",
|
||||
"accessIPv6": "%(access_ip_v6)s",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "%(ip)s",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "%(isotime)s",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "%(hostid)s",
|
||||
"id": "%(id)s",
|
||||
"image": {
|
||||
"id": "%(uuid)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/images/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"description": "new-server-description",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "%(isotime)s",
|
||||
"user_id": "fake",
|
||||
"locked": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"server" : {
|
||||
"accessIPv4": "%(access_ip_v4)s",
|
||||
"accessIPv6": "%(access_ip_v6)s",
|
||||
"name" : "new-server-test",
|
||||
"description" : "new-server-description",
|
||||
"imageRef" : "%(glance_host)s/images/%(image_id)s",
|
||||
"flavorRef" : "%(host)s/flavors/1",
|
||||
"metadata" : {
|
||||
"My Server Name" : "Apache1"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"server": {
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"server" : {
|
||||
"name" : "updated-server-test",
|
||||
"description" : "updated-server-description"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"server": {
|
||||
"accessIPv4": "%(access_ip_v4)s",
|
||||
"accessIPv6": "%(access_ip_v6)s",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "%(ip)s",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "%(isotime)s",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "%(hostid)s",
|
||||
"id": "%(id)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"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "updated-server-test",
|
||||
"description": "updated-server-description",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "%(isotime)s",
|
||||
"user_id": "fake",
|
||||
"locked": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"servers": [
|
||||
{
|
||||
"accessIPv4": "%(access_ip_v4)s",
|
||||
"accessIPv6": "%(access_ip_v6)s",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "%(ip)s",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "%(isotime)s",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "%(hostid)s",
|
||||
"id": "%(id)s",
|
||||
"image": {
|
||||
"id": "%(uuid)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/images/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(compute_endpoint)s/servers/%(id)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"description": "new-server-description",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "%(isotime)s",
|
||||
"user_id": "fake",
|
||||
"locked": false
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"servers": [
|
||||
{
|
||||
"id": "%(id)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(versioned_compute_endpoint)s/servers/%(id)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(compute_endpoint)s/servers/%(id)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"name": "new-server-test"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -19,7 +19,7 @@
|
|||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.18",
|
||||
"version": "2.19",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.18",
|
||||
"version": "2.19",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ class ServersSampleJsonTest(ServersSampleBase):
|
|||
self._verify_response('servers-list-resp', subs, response, 200)
|
||||
|
||||
def test_servers_details(self):
|
||||
uuid = self._post_server()
|
||||
uuid = self.test_servers_post()
|
||||
response = self._do_get('servers/detail',
|
||||
api_version=self.microversion)
|
||||
subs = {}
|
||||
|
@ -117,6 +117,27 @@ class ServersSampleJson29Test(ServersSampleJsonTest):
|
|||
scenarios = [('v2_9', {'api_major_version': 'v2.1'})]
|
||||
|
||||
|
||||
class ServersSampleJson219Test(ServersSampleJsonTest):
|
||||
microversion = '2.19'
|
||||
sample_dir = 'servers'
|
||||
scenarios = [('v2_19', {'api_major_version': 'v2.1'})]
|
||||
|
||||
def test_servers_post(self):
|
||||
return self._post_server(False)
|
||||
|
||||
def test_servers_put(self):
|
||||
uuid = self.test_servers_post()
|
||||
response = self._do_put('servers/%s' % uuid, 'server-put-req', {})
|
||||
subs = {
|
||||
'image_id': fake.get_valid_image_id(),
|
||||
'hostid': '[a-f0-9]+',
|
||||
'glance_host': self._get_glance_host(),
|
||||
'access_ip_v4': '1.2.3.4',
|
||||
'access_ip_v6': '80fe::'
|
||||
}
|
||||
self._verify_response('server-put-resp', subs, response, 200)
|
||||
|
||||
|
||||
class ServerSortKeysJsonTests(ServersSampleBase):
|
||||
sample_dir = 'servers-sort'
|
||||
|
||||
|
@ -173,9 +194,6 @@ class ServersActionsJsonTest(ServersSampleBase):
|
|||
uuid = self._post_server()
|
||||
image = fake.get_valid_image_id()
|
||||
params = {
|
||||
'host': self._get_host(),
|
||||
'compute_endpoint': self._get_compute_endpoint(),
|
||||
'versioned_compute_endpoint': self._get_vers_compute_endpoint(),
|
||||
'uuid': image,
|
||||
'name': 'foobar',
|
||||
'pass': 'seekr3t',
|
||||
|
@ -217,6 +235,31 @@ class ServersActionsJsonTest(ServersSampleBase):
|
|||
{'name': 'foo-image'})
|
||||
|
||||
|
||||
class ServersActionsJson219Test(ServersSampleBase):
|
||||
microversion = '2.19'
|
||||
sample_dir = 'servers'
|
||||
scenarios = [('v2_19', {'api_major_version': 'v2.1'})]
|
||||
|
||||
def test_server_rebuild(self):
|
||||
uuid = self._post_server()
|
||||
image = fake.get_valid_image_id()
|
||||
params = {
|
||||
'uuid': image,
|
||||
'name': 'foobar',
|
||||
'description': 'description of foobar',
|
||||
'pass': 'seekr3t',
|
||||
'hostid': '[a-f0-9]+',
|
||||
'access_ip_v4': '1.2.3.4',
|
||||
'access_ip_v6': '80fe::',
|
||||
}
|
||||
|
||||
resp = self._do_post('servers/%s/action' % uuid,
|
||||
'server-action-rebuild', params)
|
||||
subs = params.copy()
|
||||
del subs['uuid']
|
||||
self._verify_response('server-action-rebuild-resp', subs, resp, 202)
|
||||
|
||||
|
||||
class ServersActionsAllJsonTest(ServersActionsJsonTest):
|
||||
all_extensions = True
|
||||
sample_dir = None
|
||||
|
|
|
@ -510,3 +510,231 @@ class ServersTest(ServersTestBase):
|
|||
|
||||
class ServersTestV21(ServersTest):
|
||||
api_major_version = 'v2.1'
|
||||
|
||||
|
||||
class ServersTestV219(ServersTestBase):
|
||||
api_major_version = 'v2.1'
|
||||
|
||||
def _create_server(self, set_desc = True, desc = None):
|
||||
server = self._build_minimal_create_server_request()
|
||||
if set_desc:
|
||||
server['description'] = desc
|
||||
post = {'server': server}
|
||||
response = self.api.api_post('/servers', post,
|
||||
headers=self._headers).body
|
||||
return (server, response['server'])
|
||||
|
||||
def _update_server(self, server_id, set_desc = True, desc = None):
|
||||
new_name = integrated_helpers.generate_random_alphanumeric(8)
|
||||
server = {'server': {'name': new_name}}
|
||||
if set_desc:
|
||||
server['server']['description'] = desc
|
||||
self.api.api_put('/servers/%s' % server_id, server,
|
||||
headers=self._headers)
|
||||
|
||||
def _rebuild_server(self, server_id, set_desc = True, desc = None):
|
||||
new_name = integrated_helpers.generate_random_alphanumeric(8)
|
||||
post = {}
|
||||
post['rebuild'] = {
|
||||
"name": new_name,
|
||||
self._image_ref_parameter: "76fa36fc-c930-4bf3-8c8a-ea2a2420deb6",
|
||||
self._access_ipv4_parameter: "172.19.0.2",
|
||||
self._access_ipv6_parameter: "fe80::2",
|
||||
"metadata": {'some': 'thing'},
|
||||
}
|
||||
post['rebuild'].update(self._get_access_ips_params())
|
||||
if set_desc:
|
||||
post['rebuild']['description'] = desc
|
||||
self.api.api_post('/servers/%s/action' % server_id, post,
|
||||
headers=self._headers)
|
||||
|
||||
def _create_server_and_verify(self, set_desc = True, expected_desc = None):
|
||||
# Creates a server with a description and verifies it is
|
||||
# in the GET responses.
|
||||
created_server_id = self._create_server(set_desc,
|
||||
expected_desc)[1]['id']
|
||||
self._verify_server_description(created_server_id, expected_desc)
|
||||
self._delete_server(created_server_id)
|
||||
|
||||
def _update_server_and_verify(self, server_id, set_desc = True,
|
||||
expected_desc = None):
|
||||
# Updates a server with a description and verifies it is
|
||||
# in the GET responses.
|
||||
self._update_server(server_id, set_desc, expected_desc)
|
||||
self._verify_server_description(server_id, expected_desc)
|
||||
|
||||
def _rebuild_server_and_verify(self, server_id, set_desc = True,
|
||||
expected_desc = None):
|
||||
# Rebuilds a server with a description and verifies it is
|
||||
# in the GET responses.
|
||||
self._rebuild_server(server_id, set_desc, expected_desc)
|
||||
self._verify_server_description(server_id, expected_desc)
|
||||
|
||||
def _verify_server_description(self, server_id, expected_desc = None,
|
||||
desc_in_resp = True):
|
||||
# Calls GET on the servers and verifies that the description
|
||||
# is set as expected in the response, or not set at all.
|
||||
response = self.api.api_get('/servers/%s' % server_id,
|
||||
headers=self._headers)
|
||||
found_server = response.body['server']
|
||||
self.assertEqual(server_id, found_server['id'])
|
||||
if desc_in_resp:
|
||||
# Verify the description is set as expected (can be None)
|
||||
self.assertEqual(expected_desc, found_server.get('description'))
|
||||
else:
|
||||
# Verify the description is not included in the response.
|
||||
self.assertNotIn('description', found_server)
|
||||
|
||||
servers = self.api.api_get('/servers/detail',
|
||||
headers=self._headers).body['servers']
|
||||
server_map = {server['id']: server for server in servers}
|
||||
found_server = server_map.get(server_id)
|
||||
self.assertTrue(found_server)
|
||||
if desc_in_resp:
|
||||
# Verify the description is set as expected (can be None)
|
||||
self.assertEqual(expected_desc, found_server.get('description'))
|
||||
else:
|
||||
# Verify the description is not included in the response.
|
||||
self.assertNotIn('description', found_server)
|
||||
|
||||
def _create_assertRaisesRegex(self, desc):
|
||||
# Verifies that a 400 error is thrown on create server
|
||||
with self.assertRaisesRegex(client.OpenStackApiException,
|
||||
".*Unexpected status code.*") as cm:
|
||||
self._create_server(True, desc)
|
||||
self.assertEqual(400, cm.exception.response.status_code)
|
||||
|
||||
def _update_assertRaisesRegex(self, server_id, desc):
|
||||
# Verifies that a 400 error is thrown on update server
|
||||
with self.assertRaisesRegex(client.OpenStackApiException,
|
||||
".*Unexpected status code.*") as cm:
|
||||
self._update_server(server_id, True, desc)
|
||||
self.assertEqual(400, cm.exception.response.status_code)
|
||||
|
||||
def _rebuild_assertRaisesRegex(self, server_id, desc):
|
||||
# Verifies that a 400 error is thrown on rebuild server
|
||||
with self.assertRaisesRegex(client.OpenStackApiException,
|
||||
".*Unexpected status code.*") as cm:
|
||||
self._rebuild_server(server_id, True, desc)
|
||||
self.assertEqual(400, cm.exception.response.status_code)
|
||||
|
||||
def test_create_server_with_description(self):
|
||||
fake_network.set_stub_network_methods(self)
|
||||
|
||||
self._headers = {}
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.19'
|
||||
|
||||
# Create and get a server with a description
|
||||
self._create_server_and_verify(True, 'test description')
|
||||
# Create and get a server with an empty description
|
||||
self._create_server_and_verify(True, '')
|
||||
# Create and get a server with description set to None
|
||||
self._create_server_and_verify()
|
||||
# Create and get a server without setting the description
|
||||
self._create_server_and_verify(False)
|
||||
|
||||
def test_update_server_with_description(self):
|
||||
fake_network.set_stub_network_methods(self)
|
||||
|
||||
self._headers = {}
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.19'
|
||||
|
||||
# Create a server with an initial description
|
||||
server_id = self._create_server(True, 'test desc 1')[1]['id']
|
||||
|
||||
# Update and get the server with a description
|
||||
self._update_server_and_verify(server_id, True, 'updated desc')
|
||||
# Update and get the server name without changing the description
|
||||
self._update_server_and_verify(server_id, False, 'updated desc')
|
||||
# Update and get the server with an empty description
|
||||
self._update_server_and_verify(server_id, True, '')
|
||||
# Update and get the server by removing the description (set to None)
|
||||
self._update_server_and_verify(server_id)
|
||||
# Update and get the server with a 2nd new description
|
||||
self._update_server_and_verify(server_id, True, 'updated desc2')
|
||||
|
||||
# Cleanup
|
||||
self._delete_server(server_id)
|
||||
|
||||
def test_rebuild_server_with_description(self):
|
||||
fake_network.set_stub_network_methods(self)
|
||||
|
||||
self._headers = {}
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.19'
|
||||
|
||||
# Create a server with an initial description
|
||||
server = self._create_server(True, 'test desc 1')[1]
|
||||
server_id = server['id']
|
||||
self._wait_for_state_change(server, 'BUILD')
|
||||
|
||||
# Rebuild and get the server with a description
|
||||
self._rebuild_server_and_verify(server_id, True, 'updated desc')
|
||||
# Rebuild and get the server name without changing the description
|
||||
self._rebuild_server_and_verify(server_id, False, 'updated desc')
|
||||
# Rebuild and get the server with an empty description
|
||||
self._rebuild_server_and_verify(server_id, True, '')
|
||||
# Rebuild and get the server by removing the description (set to None)
|
||||
self._rebuild_server_and_verify(server_id)
|
||||
# Rebuild and get the server with a 2nd new description
|
||||
self._rebuild_server_and_verify(server_id, True, 'updated desc2')
|
||||
|
||||
# Cleanup
|
||||
self._delete_server(server_id)
|
||||
|
||||
def test_version_compatibility(self):
|
||||
fake_network.set_stub_network_methods(self)
|
||||
|
||||
# Create a server with microversion v2.19 and a description.
|
||||
self._headers = {}
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.19'
|
||||
server_id = self._create_server(True, 'test desc 1')[1]['id']
|
||||
# Verify that the description is not included on V2.18 GETs
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.18'
|
||||
self._verify_server_description(server_id, desc_in_resp = False)
|
||||
# Verify that updating the server with description on V2.18
|
||||
# results in a 400 error
|
||||
self._update_assertRaisesRegex(server_id, 'test update 2.18')
|
||||
# Verify that rebuilding the server with description on V2.18
|
||||
# results in a 400 error
|
||||
self._rebuild_assertRaisesRegex(server_id, 'test rebuild 2.18')
|
||||
|
||||
# Cleanup
|
||||
self._delete_server(server_id)
|
||||
|
||||
# Create a server on V2.18 and verify that the description
|
||||
# defaults to the name on a V2.19 GET
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.18'
|
||||
server_req, response = self._create_server(False)
|
||||
server_id = response['id']
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.19'
|
||||
self._verify_server_description(server_id, server_req['name'])
|
||||
|
||||
# Cleanup
|
||||
self._delete_server(server_id)
|
||||
|
||||
# Verify that creating a server with description on V2.18
|
||||
# results in a 400 error
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.18'
|
||||
self._create_assertRaisesRegex('test create 2.18')
|
||||
|
||||
def test_description_errors(self):
|
||||
fake_network.set_stub_network_methods(self)
|
||||
|
||||
self._headers = {}
|
||||
self._headers['X-OpenStack-Nova-API-Version'] = '2.19'
|
||||
|
||||
# Create servers with invalid descriptions. These throw 400.
|
||||
# Invalid unicode with non-printable control char
|
||||
self._create_assertRaisesRegex(u'invalid\0dstring')
|
||||
# Description is longer than 255 chars
|
||||
self._create_assertRaisesRegex('x' * 256)
|
||||
|
||||
# Update and rebuild servers with invalid descriptions.
|
||||
# These throw 400.
|
||||
server_id = self._create_server(True, "desc")[1]['id']
|
||||
# Invalid unicode with non-printable control char
|
||||
self._update_assertRaisesRegex(server_id, u'invalid\u0604string')
|
||||
self._rebuild_assertRaisesRegex(server_id, u'invalid\u0604string')
|
||||
# Description is longer than 255 chars
|
||||
self._update_assertRaisesRegex(server_id, 'x' * 256)
|
||||
self._rebuild_assertRaisesRegex(server_id, 'x' * 256)
|
||||
|
|
|
@ -132,6 +132,18 @@ def fake_instance_get_all_with_locked(context, list_locked, **kwargs):
|
|||
return objects.InstanceList(objects=obj_list)
|
||||
|
||||
|
||||
def fake_instance_get_all_with_description(context, list_desc, **kwargs):
|
||||
obj_list = []
|
||||
s_id = 0
|
||||
for desc in list_desc:
|
||||
uuid = fakes.get_fake_uuid(desc)
|
||||
s_id = s_id + 1
|
||||
kwargs['display_description'] = desc
|
||||
server = fakes.stub_instance_obj(context, id=s_id, uuid=uuid, **kwargs)
|
||||
obj_list.append(server)
|
||||
return objects.InstanceList(objects=obj_list)
|
||||
|
||||
|
||||
class MockSetAdminPassword(object):
|
||||
def __init__(self):
|
||||
self.instance_id = None
|
||||
|
@ -1409,6 +1421,66 @@ class ServersControllerTestV29(ServersControllerTest):
|
|||
self.assertNotIn(key, search_opts)
|
||||
|
||||
|
||||
class ServersControllerTestV219(ServersControllerTest):
|
||||
wsgi_api_version = '2.19'
|
||||
|
||||
def _get_server_data_dict(self, uuid, image_bookmark, flavor_bookmark,
|
||||
status="ACTIVE", progress=100, description=None):
|
||||
server_dict = super(ServersControllerTestV219,
|
||||
self)._get_server_data_dict(uuid,
|
||||
image_bookmark,
|
||||
flavor_bookmark,
|
||||
status,
|
||||
progress)
|
||||
server_dict['server']['locked'] = False
|
||||
server_dict['server']['description'] = description
|
||||
return server_dict
|
||||
|
||||
@mock.patch.object(compute_api.API, 'get')
|
||||
def _test_get_server_with_description(self, description, get_mock):
|
||||
image_bookmark = "http://localhost/fake/images/10"
|
||||
flavor_bookmark = "http://localhost/fake/flavors/2"
|
||||
uuid = FAKE_UUID
|
||||
get_mock.side_effect = fakes.fake_compute_get(id=2,
|
||||
display_description=description,
|
||||
uuid=uuid)
|
||||
|
||||
req = self.req('/fake/servers/%s' % uuid)
|
||||
res_dict = self.controller.show(req, uuid)
|
||||
|
||||
expected_server = self._get_server_data_dict(uuid,
|
||||
image_bookmark,
|
||||
flavor_bookmark,
|
||||
status="BUILD",
|
||||
progress=0,
|
||||
description=description)
|
||||
self.assertThat(res_dict, matchers.DictMatches(expected_server))
|
||||
return res_dict
|
||||
|
||||
@mock.patch.object(compute_api.API, 'get_all')
|
||||
def _test_list_server_detail_with_descriptions(self,
|
||||
s1_desc,
|
||||
s2_desc,
|
||||
get_all_mock):
|
||||
get_all_mock.return_value = fake_instance_get_all_with_description(
|
||||
context, [s1_desc, s2_desc])
|
||||
req = self.req('/fake/servers/detail')
|
||||
servers_list = self.controller.detail(req)
|
||||
# Check that each returned server has the same 'description' value
|
||||
# and 'id' as they were created.
|
||||
for desc in [s1_desc, s2_desc]:
|
||||
server = next(server for server in servers_list['servers']
|
||||
if (server['id'] == fakes.get_fake_uuid(desc)))
|
||||
expected = desc
|
||||
self.assertEqual(expected, server['description'])
|
||||
|
||||
def test_get_server_with_description(self):
|
||||
self._test_get_server_with_description('test desc')
|
||||
|
||||
def test_list_server_detail_with_descriptions(self):
|
||||
self._test_list_server_detail_with_descriptions('desc1', 'desc2')
|
||||
|
||||
|
||||
class ServersControllerDeleteTest(ControllerTest):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -1806,6 +1878,55 @@ class ServersControllerRebuildInstanceTest(ControllerTest):
|
|||
self.controller._stop_server, req, 'test_inst', body)
|
||||
|
||||
|
||||
class ServersControllerRebuildTestV219(ServersControllerRebuildInstanceTest):
|
||||
|
||||
def setUp(self):
|
||||
super(ServersControllerRebuildTestV219, self).setUp()
|
||||
self.req.api_version_request = \
|
||||
api_version_request.APIVersionRequest('2.19')
|
||||
|
||||
def _rebuild_server(self, set_desc, desc):
|
||||
fake_get = fakes.fake_compute_get(vm_state=vm_states.ACTIVE,
|
||||
display_description=desc)
|
||||
self.stubs.Set(compute_api.API, 'get',
|
||||
lambda api, *a, **k: fake_get(*a, **k))
|
||||
|
||||
if set_desc:
|
||||
self.body['rebuild']['description'] = desc
|
||||
self.req.body = jsonutils.dump_as_bytes(self.body)
|
||||
server = self.controller._action_rebuild(self.req, FAKE_UUID,
|
||||
body=self.body).obj['server']
|
||||
self.assertEqual(server['id'], FAKE_UUID)
|
||||
self.assertEqual(server['description'], desc)
|
||||
|
||||
def test_rebuild_server_with_description(self):
|
||||
self._rebuild_server(True, 'server desc')
|
||||
|
||||
def test_rebuild_server_empty_description(self):
|
||||
self._rebuild_server(True, '')
|
||||
|
||||
def test_rebuild_server_without_description(self):
|
||||
self._rebuild_server(False, '')
|
||||
|
||||
def test_rebuild_server_remove_description(self):
|
||||
self._rebuild_server(True, None)
|
||||
|
||||
def test_rebuild_server_description_too_long(self):
|
||||
self.body['rebuild']['description'] = 'x' * 256
|
||||
self.req.body = jsonutils.dump_as_bytes(self.body)
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.controller._action_rebuild,
|
||||
self.req, FAKE_UUID, body=self.body)
|
||||
|
||||
def test_rebuild_server_description_invalid(self):
|
||||
# Invalid non-printable control char in the desc.
|
||||
self.body['rebuild']['description'] = "123\0d456"
|
||||
self.req.body = jsonutils.dump_as_bytes(self.body)
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.controller._action_rebuild,
|
||||
self.req, FAKE_UUID, body=self.body)
|
||||
|
||||
|
||||
class ServersControllerUpdateTest(ControllerTest):
|
||||
|
||||
def _get_request(self, body=None, options=None):
|
||||
|
@ -2022,6 +2143,68 @@ class ServersControllerTriggerCrashDumpTest(ControllerTest):
|
|||
self.req, FAKE_UUID, body=self.body)
|
||||
|
||||
|
||||
class ServersControllerUpdateTestV219(ServersControllerUpdateTest):
|
||||
def _get_request(self, body=None, options=None):
|
||||
req = super(ServersControllerUpdateTestV219, self)._get_request(
|
||||
body=body,
|
||||
options=options)
|
||||
req.api_version_request = api_version_request.APIVersionRequest('2.19')
|
||||
return req
|
||||
|
||||
def _update_server_desc(self, set_desc, desc=None):
|
||||
body = {'server': {}}
|
||||
if set_desc:
|
||||
body['server']['description'] = desc
|
||||
req = self._get_request()
|
||||
res_dict = self.controller.update(req, FAKE_UUID, body=body)
|
||||
return res_dict
|
||||
|
||||
def test_update_server_description(self):
|
||||
res_dict = self._update_server_desc(True, 'server_desc')
|
||||
self.assertEqual(res_dict['server']['id'], FAKE_UUID)
|
||||
self.assertEqual(res_dict['server']['description'], 'server_desc')
|
||||
|
||||
def test_update_server_empty_description(self):
|
||||
res_dict = self._update_server_desc(True, '')
|
||||
self.assertEqual(res_dict['server']['id'], FAKE_UUID)
|
||||
self.assertEqual(res_dict['server']['description'], '')
|
||||
|
||||
def test_update_server_without_description(self):
|
||||
res_dict = self._update_server_desc(False)
|
||||
self.assertEqual(res_dict['server']['id'], FAKE_UUID)
|
||||
self.assertIsNone(res_dict['server']['description'])
|
||||
|
||||
def test_update_server_remove_description(self):
|
||||
res_dict = self._update_server_desc(True)
|
||||
self.assertEqual(res_dict['server']['id'], FAKE_UUID)
|
||||
self.assertIsNone(res_dict['server']['description'])
|
||||
|
||||
def test_update_server_all_attributes(self):
|
||||
body = {'server': {
|
||||
'name': 'server_test',
|
||||
'description': 'server_desc'
|
||||
}}
|
||||
req = self._get_request(body, {'name': 'server_test'})
|
||||
res_dict = self.controller.update(req, FAKE_UUID, body=body)
|
||||
|
||||
self.assertEqual(res_dict['server']['id'], FAKE_UUID)
|
||||
self.assertEqual(res_dict['server']['name'], 'server_test')
|
||||
self.assertEqual(res_dict['server']['description'], 'server_desc')
|
||||
|
||||
def test_update_server_description_too_long(self):
|
||||
body = {'server': {'description': 'x' * 256}}
|
||||
req = self._get_request(body, {'name': 'server_test'})
|
||||
self.assertRaises(exception.ValidationError, self.controller.update,
|
||||
req, FAKE_UUID, body=body)
|
||||
|
||||
def test_update_server_description_invalid(self):
|
||||
# Invalid non-printable control char in the desc.
|
||||
body = {'server': {'description': "123\0d456"}}
|
||||
req = self._get_request(body, {'name': 'server_test'})
|
||||
self.assertRaises(exception.ValidationError, self.controller.update,
|
||||
req, FAKE_UUID, body=body)
|
||||
|
||||
|
||||
class ServerStatusTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -2150,6 +2333,7 @@ class ServersControllerCreateTest(test.TestCase):
|
|||
instance = fake_instance.fake_db_instance(**{
|
||||
'id': self.instance_cache_num,
|
||||
'display_name': inst['display_name'] or 'test',
|
||||
'display_description': inst['display_description'] or '',
|
||||
'uuid': FAKE_UUID,
|
||||
'instance_type': inst_type,
|
||||
'image_ref': inst.get('image_ref', def_image_ref),
|
||||
|
@ -3112,6 +3296,48 @@ class ServersControllerCreateTest(test.TestCase):
|
|||
self.req, body=self.body)
|
||||
|
||||
|
||||
class ServersControllerCreateTestV219(ServersControllerCreateTest):
|
||||
def _create_instance_req(self, set_desc, desc=None):
|
||||
# proper local hrefs must start with 'http://localhost/v2/'
|
||||
image_href = 'http://localhost/v2/images/%s' % self.image_uuid
|
||||
self.body['server']['imageRef'] = image_href
|
||||
if set_desc:
|
||||
self.body['server']['description'] = desc
|
||||
self.req.body = jsonutils.dump_as_bytes(self.body)
|
||||
self.req.api_version_request = \
|
||||
api_version_request.APIVersionRequest('2.19')
|
||||
|
||||
def test_create_instance_with_description(self):
|
||||
self._create_instance_req(True, 'server_desc')
|
||||
# The fact that the action doesn't raise is enough validation
|
||||
self.controller.create(self.req, body=self.body).obj
|
||||
|
||||
def test_create_instance_with_none_description(self):
|
||||
self._create_instance_req(True)
|
||||
# The fact that the action doesn't raise is enough validation
|
||||
self.controller.create(self.req, body=self.body).obj
|
||||
|
||||
def test_create_instance_with_empty_description(self):
|
||||
self._create_instance_req(True, '')
|
||||
# The fact that the action doesn't raise is enough validation
|
||||
self.controller.create(self.req, body=self.body).obj
|
||||
|
||||
def test_create_instance_without_description(self):
|
||||
self._create_instance_req(False)
|
||||
# The fact that the action doesn't raise is enough validation
|
||||
self.controller.create(self.req, body=self.body).obj
|
||||
|
||||
def test_create_instance_description_too_long(self):
|
||||
self._create_instance_req(True, 'X' * 256)
|
||||
self.assertRaises(exception.ValidationError, self.controller.create,
|
||||
self.req, body=self.body)
|
||||
|
||||
def test_create_instance_description_invalid(self):
|
||||
self._create_instance_req(True, "abc\0ddef")
|
||||
self.assertRaises(exception.ValidationError, self.controller.create,
|
||||
self.req, body=self.body)
|
||||
|
||||
|
||||
class ServersControllerCreateTestWithMock(test.TestCase):
|
||||
image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'
|
||||
flavor_ref = 'http://localhost/123/flavors/3'
|
||||
|
@ -3789,7 +4015,7 @@ class FakeExt(extensions.V21APIExtensionBase):
|
|||
pass
|
||||
|
||||
def fake_schema_extension_point(self, version):
|
||||
if version == '2.1':
|
||||
if version == '2.1' or version == '2.19':
|
||||
return self.fake_schema
|
||||
elif version == '2.0':
|
||||
return {}
|
||||
|
|
|
@ -66,7 +66,7 @@ EXP_VERSIONS = {
|
|||
"v2.1": {
|
||||
"id": "v2.1",
|
||||
"status": "CURRENT",
|
||||
"version": "2.18",
|
||||
"version": "2.19",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
|
@ -128,7 +128,7 @@ class VersionsTestV20(test.NoDBTestCase):
|
|||
{
|
||||
"id": "v2.1",
|
||||
"status": "CURRENT",
|
||||
"version": "2.18",
|
||||
"version": "2.19",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
|
|
|
@ -431,6 +431,7 @@ def stub_instance(id=1, user_id=None, project_id=None, host=None,
|
|||
flavor_id="1", name=None, key_name='',
|
||||
access_ipv4=None, access_ipv6=None, progress=0,
|
||||
auto_disk_config=False, display_name=None,
|
||||
display_description=None,
|
||||
include_fake_metadata=True, config_drive=None,
|
||||
power_state=None, nw_cache=None, metadata=None,
|
||||
security_groups=None, root_device_name=None,
|
||||
|
@ -522,7 +523,7 @@ def stub_instance(id=1, user_id=None, project_id=None, host=None,
|
|||
"terminated_at": terminated_at,
|
||||
"availability_zone": availability_zone,
|
||||
"display_name": display_name or server_name,
|
||||
"display_description": "",
|
||||
"display_description": display_description,
|
||||
"locked": locked_by is not None,
|
||||
"locked_by": locked_by,
|
||||
"metadata": metadata,
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
features:
|
||||
- In Nova Compute API microversion 2.19, you can
|
||||
specify a "description" attribute when creating, rebuilding, or updating
|
||||
a server instance. This description can be retrieved by getting
|
||||
server details, or list details for servers.
|
||||
|
||||
Refer to the Nova Compute API documentation for more
|
||||
information.
|
||||
|
||||
Note that the description attribute existed in prior
|
||||
Nova versions, but was set to the server name by Nova,
|
||||
and was not visible to the user. So, servers you
|
||||
created with microversions prior to 2.19 will return
|
||||
the description equals the name on server details
|
||||
microversion 2.19.
|
|
@ -48,6 +48,7 @@ nova.tests.unit.api.openstack.compute.test_server_actions.ServerActionsControlle
|
|||
nova.tests.unit.api.openstack.compute.test_serversV21.Base64ValidationTest
|
||||
nova.tests.unit.api.openstack.compute.test_serversV21.ServersControllerCreateTest
|
||||
nova.tests.unit.api.openstack.compute.test_serversV21.ServersControllerRebuildInstanceTest
|
||||
nova.tests.unit.api.openstack.compute.test_serversV21.ServersControllerRebuildTestV219
|
||||
nova.tests.unit.api.openstack.compute.test_serversV21.ServersControllerTest
|
||||
nova.tests.unit.api.openstack.compute.test_serversV21.ServersControllerTestV29
|
||||
nova.tests.unit.api.openstack.compute.test_simple_tenant_usage.SimpleTenantUsageTestV2
|
||||
|
@ -211,4 +212,4 @@ nova.tests.unit.virt.test_virt_drivers.AbstractDriverTestCase
|
|||
nova.tests.unit.virt.vmwareapi.test_configdrive.ConfigDriveTestCase
|
||||
nova.tests.unit.virt.vmwareapi.test_driver_api.VMwareAPIVMTestCase
|
||||
nova.tests.unit.virt.xenapi.test_vmops.BootableTestCase
|
||||
nova.tests.unit.virt.xenapi.test_vmops.SpawnTestCase
|
||||
nova.tests.unit.virt.xenapi.test_vmops.SpawnTestCase
|
||||
|
|
Loading…
Reference in New Issue