api: Add support for 'hostname' parameter

Add microversion 2.90, which allows allows users to configure the
hostname that will be exposed via the nova metadata service when
creating their instance.

Change-Id: I95047c1689ac14fa73eba48e19dc438988b78aad
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2021-01-14 11:42:43 +00:00
parent 982d459c65
commit 5e2c31ab78
36 changed files with 1357 additions and 87 deletions

View File

@ -737,8 +737,10 @@ hostname_query_server:
description: |
Filter the server list result by the host name of server.
This parameter is only valid when specified by administrators.
If non-admin users specify this parameter, it is ignored.
This parameter is only valid when specified by administrators until
microversion 2.90, after which it can be specified by all users.
If non-admin users specify this parameter before microversion 2.90, it is
ignored.
in: query
required: false
type: string
@ -6330,21 +6332,36 @@ server_host_create:
required: false
type: string
min_version: 2.74
server_hostname:
server_hostname: &server_hostname
in: body
required: false
type: string
description: |
The hostname set on the instance when it is booted.
By default, it appears in the response for administrative users only.
The hostname of the instance reported in the metadata service.
This parameter only appears in responses for administrators until
microversion 2.90, after which it is shown for all users.
.. note::
This information is published via the metadata service and requires
application such as ``cloud-init`` to propogate it through to the
instance.
min_version: 2.3
server_hostname_update_rebuild:
server_hostname_req:
in: body
required: false
type: string
description: |
The hostname set on the instance when it is booted.
By default, it appears in the response for administrative users only.
The hostname to configure for the instance in the metadata service.
.. note::
This information is published via the metadata service and requires
application such as ``cloud-init`` to propogate it through to the
instance.
min_version: 2.90
server_hostname_update_rebuild:
<<: *server_hostname
min_version: 2.75
# This is the hypervisor_hostname in a POST (create instance) request body.
server_hypervisor_hostname_create:

View File

@ -22,6 +22,7 @@ into a server since Mitaka release.
You can get an RDP, serial, SPICE, or VNC console for a server.
Add (Associate) Floating Ip (addFloatingIp Action) (DEPRECATED)
================================================================
@ -469,6 +470,7 @@ Response
If successful, this method does not return content in the response body.
.. _reboot:
Reboot Server (reboot Action)
@ -582,12 +584,18 @@ Request
- key_name: key_name_rebuild_req
- user_data: user_data_rebuild_req
- trusted_image_certificates: server_trusted_image_certificates_rebuild_req
- hostname: server_hostname_req
**Example Rebuild Server (rebuild Action) (v2.63)**
.. literalinclude:: ../../doc/api_samples/servers/v2.63/server-action-rebuild.json
:language: javascript
**Example Rebuild Server (rebuild Action) (v2.90)**
.. literalinclude:: ../../doc/api_samples/servers/v2.90/server-action-rebuild.json
:language: javascript
Response
--------
@ -662,6 +670,7 @@ Response
.. literalinclude:: ../../doc/api_samples/servers/v2.75/server-action-rebuild-resp.json
:language: javascript
Remove (Disassociate) Floating Ip (removeFloatingIp Action) (DEPRECATED)
=========================================================================
@ -791,6 +800,7 @@ Response
.. literalinclude:: ../../doc/api_samples/os-rescue/server-rescue.json
:language: javascript
Resize Server (resize Action)
=============================

View File

@ -368,7 +368,6 @@ Request
.. rest_parameters:: parameters.yaml
- server: server
- flavorRef: flavorRef
- name: server_name
@ -404,6 +403,7 @@ Request
- security_groups: security_groups
- user_data: user_data
- description: server_description
- hostname: server_hostname_req
- tags: server_tags_create
- trusted_image_certificates: server_trusted_image_certificates_create_req
- host: server_host_create
@ -443,6 +443,11 @@ Request
.. literalinclude:: ../../doc/api_samples/servers/v2.74/server-create-req-with-host-and-node.json
:language: javascript
**Example Create Server With Hostname (v2.90)**
.. literalinclude:: ../../doc/api_samples/servers/v2.90/server-create-req.json
:language: javascript
Response
--------
@ -633,8 +638,15 @@ Response
- OS-DCF:diskConfig: disk_config
- OS-EXT-AZ:availability_zone: OS-EXT-AZ:availability_zone
- OS-EXT-SRV-ATTR:host: OS-EXT-SRV-ATTR:host
- OS-EXT-SRV-ATTR:hostname: server_hostname
- OS-EXT-SRV-ATTR:hypervisor_hostname: OS-EXT-SRV-ATTR:hypervisor_hostname
- OS-EXT-SRV-ATTR:instance_name: OS-EXT-SRV-ATTR:instance_name
- OS-EXT-SRV-ATTR:kernel_id: server_kernel_id
- OS-EXT-SRV-ATTR:launch_index: server_launch_index
- OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id
- OS-EXT-SRV-ATTR:reservation_id: server_reservation_id
- OS-EXT-SRV-ATTR:root_device_name: server_root_device_name
- OS-EXT-SRV-ATTR:user_data: server_user_data
- OS-EXT-STS:power_state: OS-EXT-STS:power_state
- OS-EXT-STS:task_state: OS-EXT-STS:task_state
- OS-EXT-STS:vm_state: OS-EXT-STS:vm_state
@ -656,13 +668,6 @@ Response
- security_groups: security_groups_obj_optional
- security_group.name: name
- servers_links: servers_links
- OS-EXT-SRV-ATTR:hostname: server_hostname
- OS-EXT-SRV-ATTR:reservation_id: server_reservation_id
- OS-EXT-SRV-ATTR:launch_index: server_launch_index
- OS-EXT-SRV-ATTR:kernel_id: server_kernel_id
- OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id
- OS-EXT-SRV-ATTR:root_device_name: server_root_device_name
- OS-EXT-SRV-ATTR:user_data: server_user_data
- locked: locked
- host_status: host_status
- description: server_description_resp
@ -757,8 +762,15 @@ Response
- OS-DCF:diskConfig: disk_config
- OS-EXT-AZ:availability_zone: OS-EXT-AZ:availability_zone
- OS-EXT-SRV-ATTR:host: OS-EXT-SRV-ATTR:host
- OS-EXT-SRV-ATTR:hostname: server_hostname
- OS-EXT-SRV-ATTR:hypervisor_hostname: OS-EXT-SRV-ATTR:hypervisor_hostname
- OS-EXT-SRV-ATTR:instance_name: OS-EXT-SRV-ATTR:instance_name
- OS-EXT-SRV-ATTR:kernel_id: server_kernel_id
- OS-EXT-SRV-ATTR:launch_index: server_launch_index
- OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id
- OS-EXT-SRV-ATTR:reservation_id: server_reservation_id
- OS-EXT-SRV-ATTR:root_device_name: server_root_device_name
- OS-EXT-SRV-ATTR:user_data: server_user_data
- OS-EXT-STS:power_state: OS-EXT-STS:power_state
- OS-EXT-STS:task_state: OS-EXT-STS:task_state
- OS-EXT-STS:vm_state: OS-EXT-STS:vm_state
@ -779,13 +791,6 @@ Response
- progress: progress
- security_groups: security_groups_obj_optional
- security_group.name: name
- OS-EXT-SRV-ATTR:hostname: server_hostname
- OS-EXT-SRV-ATTR:reservation_id: server_reservation_id
- OS-EXT-SRV-ATTR:launch_index: server_launch_index
- OS-EXT-SRV-ATTR:kernel_id: server_kernel_id
- OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id
- OS-EXT-SRV-ATTR:root_device_name: server_root_device_name
- OS-EXT-SRV-ATTR:user_data: server_user_data
- locked: locked
- host_status: host_status
- description: server_description_resp
@ -830,11 +835,14 @@ Request
- accessIPv4: accessIPv4_in
- accessIPv6: accessIPv6_in
- name: server_name_optional
- hostname: server_hostname_req
- OS-DCF:diskConfig: OS-DCF:diskConfig
- description: server_description
.. note:: You can specify parameters to update independently.
e.g. ``name`` only, ``description`` only, ``name`` and ``description``, etc.
.. note::
You can specify parameters to update independently.
e.g. ``name`` only, ``description`` only, ``name`` and ``description``, etc.
**Example Update Server (2.63)**
@ -889,18 +897,18 @@ Response
- config_drive: config_drive_resp_update_rebuild
- OS-EXT-AZ:availability_zone: OS-EXT-AZ:availability_zone_update_rebuild
- OS-EXT-SRV-ATTR:host: OS-EXT-SRV-ATTR:host_update_rebuild
- OS-EXT-SRV-ATTR:hostname: server_hostname_update_rebuild
- OS-EXT-SRV-ATTR:hypervisor_hostname: OS-EXT-SRV-ATTR:hypervisor_hostname_update_rebuild
- OS-EXT-SRV-ATTR:instance_name: OS-EXT-SRV-ATTR:instance_name_update_rebuild
- OS-EXT-SRV-ATTR:kernel_id: server_kernel_id_update_rebuild
- OS-EXT-SRV-ATTR:launch_index: server_launch_index_update_rebuild
- OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id_update_rebuild
- OS-EXT-SRV-ATTR:reservation_id: server_reservation_id_update_rebuild
- OS-EXT-SRV-ATTR:root_device_name: server_root_device_name_update_rebuild
- OS-EXT-SRV-ATTR:user_data: server_user_data_update
- OS-EXT-STS:power_state: OS-EXT-STS:power_state_update_rebuild
- OS-EXT-STS:task_state: OS-EXT-STS:task_state_update_rebuild
- OS-EXT-STS:vm_state: OS-EXT-STS:vm_state_update_rebuild
- OS-EXT-SRV-ATTR:hostname: server_hostname_update_rebuild
- OS-EXT-SRV-ATTR:reservation_id: server_reservation_id_update_rebuild
- OS-EXT-SRV-ATTR:launch_index: server_launch_index_update_rebuild
- OS-EXT-SRV-ATTR:kernel_id: server_kernel_id_update_rebuild
- OS-EXT-SRV-ATTR:ramdisk_id: server_ramdisk_id_update_rebuild
- OS-EXT-SRV-ATTR:root_device_name: server_root_device_name_update_rebuild
- OS-EXT-SRV-ATTR:user_data: server_user_data_update
- os-extended-volumes:volumes_attached: os-extended-volumes:volumes_attached_update_rebuild
- os-extended-volumes:volumes_attached.id: os-extended-volumes:volumes_attached.id_update_rebuild
- os-extended-volumes:volumes_attached.delete_on_termination: os-extended-volumes:volumes_attached.delete_on_termination_update_rebuild

View File

@ -0,0 +1,80 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "us-west",
"OS-EXT-SRV-ATTR:hostname": "updated-hostname",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "2021-08-19T15:16:22.177882",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"addr": "192.168.1.30",
"version": 4
}
]
},
"adminPass": "seekr3t",
"config_drive": "",
"created": "2019-04-23T17:10:22Z",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6",
"id": "0c37a84a-c757-4f22-8c7f-0bf8b6970886",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"key_name": null,
"links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/0c37a84a-c757-4f22-8c7f-0bf8b6970886",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/0c37a84a-c757-4f22-8c7f-0bf8b6970886",
"rel": "bookmark"
}
],
"locked": false,
"locked_reason": null,
"metadata": {
"meta_var": "meta_val"
},
"name": "foobar",
"os-extended-volumes:volumes_attached": [],
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "2019-04-23T17:10:24Z",
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi",
"user_id": "fake"
}
}

View File

@ -0,0 +1,15 @@
{
"rebuild" : {
"accessIPv4" : "1.2.3.4",
"accessIPv6" : "80fe::",
"OS-DCF:diskConfig": "AUTO",
"imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b",
"name" : "foobar",
"adminPass" : "seekr3t",
"hostname": "custom-hostname",
"metadata" : {
"meta_var" : "meta_val"
},
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi"
}
}

View File

@ -0,0 +1,30 @@
{
"server" : {
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"name" : "new-server-test",
"imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b",
"flavorRef" : "1",
"availability_zone": "us-west",
"OS-DCF:diskConfig": "AUTO",
"hostname": "custom-hostname",
"metadata" : {
"My Server Name" : "Apache1"
},
"personality": [
{
"path": "/etc/banner.txt",
"contents": "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
}
],
"security_groups": [
{
"name": "default"
}
],
"user_data" : "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg=="
},
"OS-SCH-HNT:scheduler_hints": {
"same_host": "48e6a9f6-30af-47e0-bc04-acaed113bb4e"
}
}

View File

@ -0,0 +1,22 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"adminPass": "6NpUwoz2QDRN",
"id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
"rel": "bookmark"
}
],
"security_groups": [
{
"name": "default"
}
]
}
}

View File

@ -0,0 +1,81 @@
{
"server": {
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"addr": "192.168.1.30",
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"version": 4
}
]
},
"created": "2013-09-03T04:01:32Z",
"description": null,
"locked": false,
"locked_reason": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "92154fab69d5883ba2c8622b7e65f745dd33257221c07af363c51b29",
"id": "0e44cc9c-e052-415d-afbf-469b0d384170",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"key_name": null,
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/0e44cc9c-e052-415d-afbf-469b0d384170",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/0e44cc9c-e052-415d-afbf-469b0d384170",
"rel": "bookmark"
}
],
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"config_drive": "",
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "nova",
"OS-EXT-SRV-ATTR:hostname": "custom-hostname",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"os-extended-volumes:volumes_attached": [
{"id": "volume_id1", "delete_on_termination": false},
{"id": "volume_id2", "delete_on_termination": false}
],
"OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302",
"OS-SRV-USG:terminated_at": null,
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "2013-09-03T04:01:33Z",
"user_id": "fake"
}
}

View File

@ -0,0 +1,8 @@
{
"server": {
"accessIPv4": "4.3.2.1",
"accessIPv6": "80fe::",
"OS-DCF:diskConfig": "AUTO",
"hostname" : "new-server-hostname"
}
}

View File

@ -0,0 +1,78 @@
{
"server": {
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"addr": "192.168.1.30",
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"version": 4
}
]
},
"created": "2013-09-03T04:01:32Z",
"description": null,
"locked": false,
"locked_reason": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "92154fab69d5883ba2c8622b7e65f745dd33257221c07af363c51b29",
"id": "0e44cc9c-e052-415d-afbf-469b0d384170",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"key_name": null,
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/0e44cc9c-e052-415d-afbf-469b0d384170",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/0e44cc9c-e052-415d-afbf-469b0d384170",
"rel": "bookmark"
}
],
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"config_drive": "",
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "us-west",
"OS-EXT-SRV-ATTR:hostname": "new-server-hostname",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"os-extended-volumes:volumes_attached": [],
"OS-SRV-USG:launched_at": "2013-09-23T13:37:00.880302",
"OS-SRV-USG:terminated_at": null,
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "2013-09-03T04:01:33Z",
"user_id": "fake"
}
}

View File

@ -0,0 +1,88 @@
{
"servers": [
{
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"addr": "192.168.1.30",
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"version": 4
}
]
},
"created": "2013-09-03T04:01:32Z",
"description": "",
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "bcf92836fc9ed4203a75cb0337afc7f917d2be504164b995c2334b25",
"id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"key_name": null,
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
"rel": "bookmark"
}
],
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"config_drive": "",
"locked": false,
"locked_reason": "",
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "nova",
"OS-EXT-SRV-ATTR:hostname": "custom-hostname",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"os-extended-volumes:volumes_attached": [
{"id": "volume_id1", "delete_on_termination": false},
{"id": "volume_id2", "delete_on_termination": false}
],
"OS-SRV-USG:launched_at": "2013-09-23T13:53:12.774549",
"OS-SRV-USG:terminated_at": null,
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "2013-09-03T04:01:32Z",
"user_id": "fake"
}
],
"servers_links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/detail?limit=1&marker=f5dc173b-6804-445a-a6d8-c705dad5b5eb",
"rel": "next"
}
]
}

View File

@ -0,0 +1,24 @@
{
"servers": [
{
"id": "22c91117-08de-4894-9aa9-6ef382400985",
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/22c91117-08de-4894-9aa9-6ef382400985",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/22c91117-08de-4894-9aa9-6ef382400985",
"rel": "bookmark"
}
],
"name": "new-server-test"
}
],
"servers_links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers?limit=1&marker=22c91117-08de-4894-9aa9-6ef382400985",
"rel": "next"
}
]
}

View File

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

View File

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

View File

@ -243,7 +243,10 @@ REST_API_VERSION_HISTORY = """REST API Version History:
* 2.89 - Add ``attachment_id``, ``bdm_uuid`` and remove ``id`` from the
responses of ``GET /servers/{server_id}/os-volume_attachments``
and ``GET /servers/{server_id}/os-volume_attachments/{volume_id}``
* 2.90 - Add support for requesting a specific hostname when creating,
updating or rebuilding an instance. The
``OS-EXT-SRV-ATTR:hostname`` attribute is now returned in various
server responses regardless of policy configuration.
"""
# The minimum and maximum versions of the API supported
@ -252,7 +255,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.89'
_MAX_API_VERSION = '2.90'
DEFAULT_API_VERSION = _MIN_API_VERSION
# Almost all proxy APIs which are related to network, images and baremetal

View File

@ -1180,11 +1180,25 @@ has been removed in favour of including this field in the primary ``GET
.. _microversion 2.89:
2.89 (Maximum in Xena)
----------------------
2.89
----
``attachment_id`` and ``bdm_uuid`` are now included in the responses for ``GET
/servers/{server_id}/os-volume_attachments`` and ``GET
/servers/{server_id}/os-volume_attachments/{volume_id}``. Additionally the
``id`` field is dropped from the response as it duplicates the ``volumeId``
field.
.. _microversion 2.90:
2.90 (Maximum in Xena)
----------------------
The ``POST /servers`` (create server), ``PUT /servers/{id}`` (update server)
and ``POST /servers/{server_id}/action (rebuild)`` (rebuild server) APIs now
accept a ``hostname`` parameter, allowing users to configure a hostname when
creating the instance. When specified, this will replace the auto-generated
hostname based on the display name.
In addition, the ``OS-EXT-SRV-ATTR:hostname`` field for all server
responses is now visible to all users. Previously this was an admin-only field.

View File

@ -355,6 +355,10 @@ create_v274['properties']['server'][
create_v274['properties']['server'][
'properties']['hypervisor_hostname'] = parameter_types.fqdn
# Add hostname in server
create_v290 = copy.deepcopy(create_v274)
create_v290['properties']['server'][
'properties']['hostname'] = parameter_types.hostname
update = {
'type': 'object',
@ -383,6 +387,10 @@ update_v219['properties']['server'][
'properties']['description'] = parameter_types.description
update_v290 = copy.deepcopy(update_v219)
update_v290['properties']['server'][
'properties']['hostname'] = parameter_types.hostname
rebuild = {
'type': 'object',
'properties': {
@ -437,6 +445,10 @@ rebuild_v263 = copy.deepcopy(rebuild_v257)
rebuild_v263['properties']['rebuild']['properties'][
'trusted_image_certificates'] = parameter_types.trusted_certs
rebuild_v290 = copy.deepcopy(rebuild_v263)
rebuild_v290['properties']['rebuild']['properties'][
'hostname'] = parameter_types.hostname
resize = {
'type': 'object',

View File

@ -665,7 +665,8 @@ class ServersController(wsgi.Controller):
@validation.schema(schema_servers.create_v257, '2.57', '2.62')
@validation.schema(schema_servers.create_v263, '2.63', '2.66')
@validation.schema(schema_servers.create_v267, '2.67', '2.73')
@validation.schema(schema_servers.create_v274, '2.74')
@validation.schema(schema_servers.create_v274, '2.74', '2.89')
@validation.schema(schema_servers.create_v290, '2.90')
def create(self, req, body):
"""Creates a new server for a given user."""
context = req.environ['nova.context']
@ -675,6 +676,9 @@ class ServersController(wsgi.Controller):
description = name
if api_version_request.is_supported(req, min_version='2.19'):
description = server_dict.get('description')
hostname = None
if api_version_request.is_supported(req, min_version='2.90'):
hostname = server_dict.get('hostname')
# Arguments to be passed to instance create function
create_kwargs = {}
@ -772,6 +776,7 @@ class ServersController(wsgi.Controller):
image_uuid,
display_name=name,
display_description=description,
hostname=hostname,
availability_zone=availability_zone,
forced_host=host, forced_node=node,
metadata=server_dict.get('metadata', {}),
@ -815,6 +820,7 @@ class ServersController(wsgi.Controller):
exception.MismatchVolumeAZException,
exception.MultiplePortsNotApplicable,
exception.InvalidFixedIpAndMaxCountRequest,
exception.AmbiguousHostnameForMultipleInstances,
exception.InstanceUserDataMalformed,
exception.PortNotFound,
exception.FixedIpAlreadyInUse,
@ -888,7 +894,8 @@ class ServersController(wsgi.Controller):
@wsgi.expected_errors(404)
@validation.schema(schema_servers.update_v20, '2.0', '2.0')
@validation.schema(schema_servers.update, '2.1', '2.18')
@validation.schema(schema_servers.update_v219, '2.19')
@validation.schema(schema_servers.update_v219, '2.19', '2.89')
@validation.schema(schema_servers.update_v290, '2.90')
def update(self, req, id, body):
"""Update server then pass on to version-specific controller."""
@ -899,7 +906,7 @@ class ServersController(wsgi.Controller):
target={'user_id': instance.user_id,
'project_id': instance.project_id})
show_server_groups = api_version_request.is_supported(
req, min_version='2.71')
req, min_version='2.71')
server = body['server']
@ -911,11 +918,14 @@ class ServersController(wsgi.Controller):
# This is allowed to be None (remove description)
update_dict['display_description'] = server['description']
if 'hostname' in server:
update_dict['hostname'] = server['hostname']
helpers.translate_attributes(helpers.UPDATE, server, update_dict)
try:
instance = self.compute_api.update_instance(ctxt, instance,
update_dict)
instance = self.compute_api.update_instance(
ctxt, instance, update_dict)
# NOTE(gmann): Starting from microversion 2.75, PUT and Rebuild
# API response will show all attributes like GET /servers API.
@ -1134,7 +1144,8 @@ class ServersController(wsgi.Controller):
@validation.schema(schema_servers.rebuild_v219, '2.19', '2.53')
@validation.schema(schema_servers.rebuild_v254, '2.54', '2.56')
@validation.schema(schema_servers.rebuild_v257, '2.57', '2.62')
@validation.schema(schema_servers.rebuild_v263, '2.63')
@validation.schema(schema_servers.rebuild_v263, '2.63', '2.89')
@validation.schema(schema_servers.rebuild_v290, '2.90')
def _action_rebuild(self, req, id, body):
"""Rebuild an instance with the given attributes."""
rebuild_dict = body['rebuild']
@ -1158,8 +1169,10 @@ class ServersController(wsgi.Controller):
helpers.translate_attributes(helpers.REBUILD, rebuild_dict, kwargs)
if (api_version_request.is_supported(req, min_version='2.54') and
'key_name' in rebuild_dict):
if (
api_version_request.is_supported(req, min_version='2.54') 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
@ -1171,17 +1184,25 @@ class ServersController(wsgi.Controller):
# Skip policy check for 'rebuild:trusted_certs' if no trusted
# certificate IDs were provided.
if ((api_version_request.is_supported(req, min_version='2.63')) and
# Note that this is different from server create since with
# rebuild a user can unset/reset the trusted certs by
# specifying trusted_image_certificates=None, similar to
# key_name.
('trusted_image_certificates' in rebuild_dict)):
if (
api_version_request.is_supported(req, min_version='2.63') and
# Note that this is different from server create since with
# rebuild a user can unset/reset the trusted certs by
# specifying trusted_image_certificates=None, similar to
# key_name.
'trusted_image_certificates' in rebuild_dict
):
kwargs['trusted_certs'] = rebuild_dict.get(
'trusted_image_certificates')
context.can(server_policies.SERVERS % 'rebuild:trusted_certs',
target=target)
if (
api_version_request.is_supported(req, min_version='2.90') and
'hostname' in rebuild_dict
):
kwargs['hostname'] = rebuild_dict['hostname']
for request_attribute, instance_attribute in attr_map.items():
try:
if request_attribute == 'name':
@ -1380,6 +1401,8 @@ class ServersController(wsgi.Controller):
'created_at', 'launched_at', 'terminated_at',
'power_state', 'task_state', 'vm_state', 'progress',
'user_id',)
if api_version_request.is_supported(req, min_version='2.90'):
opt_list += ('hostname',)
return opt_list
def _get_instance(self, context, instance_uuid):

View File

@ -313,6 +313,7 @@ class ViewBuilder(common.ViewBuilder):
show_extended_attr = context.can(
esa_policies.BASE_POLICY_NAME, fatal=False,
target={'project_id': instance.project_id})
if show_extended_attr:
properties = ['host', 'name', 'node']
if api_version_request.is_supported(request, min_version='2.3'):
@ -342,6 +343,7 @@ class ViewBuilder(common.ViewBuilder):
# the OS-EXT-SRV-ATTR prefix for the attribute key name.
key = "OS-EXT-SRV-ATTR:%s" % attr
server["server"][key] = getattr(instance, attr)
if show_extended_status:
# NOTE(gmann): Removed 'locked_by' from extended status
# to make it same as V2. If needed it can be added with
@ -352,6 +354,7 @@ class ViewBuilder(common.ViewBuilder):
# compat with v2.0.
key = "%s:%s" % ('OS-EXT-STS', state)
server["server"][key] = instance[state]
if show_extended_volumes:
# NOTE(mriedem): The os-extended-volumes prefix should not be used
# for new attributes after v2.1. They are only in v2.1 for backward
@ -364,7 +367,8 @@ class ViewBuilder(common.ViewBuilder):
self._add_volumes_attachments(server["server"],
bdms,
add_delete_on_termination)
if (api_version_request.is_supported(request, min_version='2.16')):
if api_version_request.is_supported(request, min_version='2.16'):
if show_host_status is None:
unknown_only = self._get_host_status_unknown_only(
context, instance)
@ -404,6 +408,13 @@ class ViewBuilder(common.ViewBuilder):
trusted_certs = instance.trusted_certs.ids
server["server"]["trusted_image_certificates"] = trusted_certs
if api_version_request.is_supported(request, min_version='2.90'):
# API 2.90 made this field visible to non-admins, but we only show
# it if it's not already added
if not show_extended_attr:
server["server"]["OS-EXT-SRV-ATTR:hostname"] = \
instance.hostname
if show_server_groups:
server['server']['server_groups'] = self._get_server_groups(
context,

View File

@ -245,6 +245,38 @@ non_negative_integer = {
'pattern': '^[0-9]*$', 'minimum': 0, 'minLength': 1
}
# A host-specific or leaf label.
#
# This is based on the specifications from two RFCs, RFCC 952 and RFC 1123.
#
# From RFC 952:
#
# A "name" (Net, Host, Gateway, or Domain name) is a text string up to 24
# characters drawn from the alphabet (A-Z), digits (0-9), minus sign (-), and
# period (.). Note that periods are only allowed when they serve to delimit
# components of "domain style names". (See RFC-921, "Domain Name System
# Implementation Schedule", for background). No blank or space characters are
# permitted as part of a name. No distinction is made between upper and lower
# case. The first character must be an alpha character. The last character
# must not be a minus sign or period. [...] Single character names or
# nicknames are not allowed.
#
# From RFC 1123, which revises RFC 952:
#
# The syntax of a legal Internet host name was specified in RFC-952 [DNS:4].
# One aspect of host name syntax is hereby changed: the restriction on the
# first character is relaxed to allow either a letter or a digit. Host
# software MUST support this more liberal syntax.
#
# Host software MUST handle host names of up to 63 characters and SHOULD
# handle host names of up to 255 characters.
hostname = {
'type': 'string',
'minLength': 2,
'maxLength': 63,
'pattern': '^[a-zA-Z0-9]+[a-zA-Z0-9-]*[a-zA-Z0-9]+$',
}
fqdn = {
'type': 'string', 'minLength': 1, 'maxLength': 255,
# NOTE: 'host' is defined in "services" table, and that

View File

@ -999,7 +999,7 @@ class API:
def _validate_and_build_base_options(
self, context, flavor, boot_meta, image_href, image_id, kernel_id,
ramdisk_id, display_name, display_description, key_name,
ramdisk_id, display_name, display_description, hostname, key_name,
key_data, security_groups, availability_zone, user_data, metadata,
access_ip_v4, access_ip_v6, requested_networks, config_drive,
auto_disk_config, reservation_id, max_count,
@ -1091,6 +1091,7 @@ class API:
'ephemeral_gb': flavor['ephemeral_gb'],
'display_name': display_name,
'display_description': display_description,
'hostname': hostname,
'user_data': user_data,
'key_name': key_name,
'key_data': key_data,
@ -1500,7 +1501,7 @@ class API:
def _create_instance(self, context, flavor,
image_href, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
display_name, display_description, hostname,
key_name, key_data, security_groups,
availability_zone, user_data, metadata, injected_files,
admin_password, access_ip_v4, access_ip_v6,
@ -1550,7 +1551,7 @@ class API:
) = self._validate_and_build_base_options(
context, flavor, boot_meta, image_href, image_id,
kernel_id, ramdisk_id, display_name, display_description,
key_name, key_data, security_groups, availability_zone,
hostname, key_name, key_data, security_groups, availability_zone,
user_data, metadata, access_ip_v4, access_ip_v6,
requested_networks, config_drive, auto_disk_config,
reservation_id, max_count, supports_port_resource_request,
@ -1586,8 +1587,8 @@ class API:
flavor, metadata, injected_files,
block_device_mapping.root_bdm(), validate_numa=False)
instance_group = self._get_requested_instance_group(context,
filter_properties)
instance_group = self._get_requested_instance_group(
context, filter_properties)
tags = self._create_tag_list_obj(context, tags)
@ -1903,24 +1904,21 @@ class API:
if 'display_name' not in instance or instance.display_name is None:
instance.display_name = 'Server %s' % instance.uuid
# if we're booting multiple instances, we need to add an indexing
# suffix to both instance.hostname and instance.display_name. This is
# not necessary for a single instance.
if num_instances == 1:
default_hostname = 'Server-%s' % instance.uuid
instance.hostname = utils.sanitize_hostname(
instance.display_name, default_hostname)
elif num_instances > 1:
old_display_name = instance.display_name
new_display_name = '%s-%d' % (old_display_name, index + 1)
# only set the hostname if the user hasn't already requested one
if 'hostname' not in instance or not instance.hostname:
# if we're booting multiple instances, we need to add an indexing
# suffix to both instance.hostname and instance.display_name.
# This is not necessary for a single instance.
hostname = utils.sanitize_hostname(instance.display_name)
if not hostname:
hostname = f'Server-{instance.uuid}'
elif num_instances > 1:
hostname = f'{hostname}-{index + 1}'
if utils.sanitize_hostname(old_display_name) == "":
instance.hostname = 'Server-%s' % instance.uuid
else:
instance.hostname = utils.sanitize_hostname(
new_display_name)
instance.hostname = hostname
instance.display_name = new_display_name
if num_instances > 1:
instance.display_name = f'{instance.display_name}-{index + 1}'
def _populate_instance_for_create(
self, context, instance, image, index, security_groups, flavor,
@ -2029,7 +2027,7 @@ class API:
self, context, flavor,
image_href, kernel_id=None, ramdisk_id=None,
min_count=None, max_count=None,
display_name=None, display_description=None,
display_name=None, display_description=None, hostname=None,
key_name=None, key_data=None, security_groups=None,
availability_zone=None, forced_host=None, forced_node=None,
user_data=None, metadata=None, injected_files=None,
@ -2054,6 +2052,9 @@ class API:
self._check_multiple_instances_with_neutron_ports(
requested_networks)
if hostname and max_count is not None and max_count > 1:
raise exception.AmbiguousHostnameForMultipleInstances()
if availability_zone and forced_host is None:
azs = availability_zones.get_availability_zones(
context.elevated(), self.host_api, get_only_available=True)
@ -2068,7 +2069,7 @@ class API:
context, flavor,
image_href, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
display_name, display_description, hostname,
key_name, key_data, security_groups,
availability_zone, user_data, metadata,
injected_files, admin_password,
@ -3483,6 +3484,9 @@ class API:
instance.trusted_certs = self._retrieve_trusted_certs_object(
context, trusted_certs, rebuild=True)
if 'hostname' in kwargs:
instance.hostname = kwargs.pop('hostname')
image_id, image = self._get_image(context, image_href)
self._check_auto_disk_config(image=image,
auto_disk_config=auto_disk_config)

View File

@ -501,6 +501,10 @@ class MultiplePortsNotApplicable(Invalid):
msg_fmt = _("Failed to launch instances: %(reason)s")
class AmbiguousHostnameForMultipleInstances(Invalid):
msg_fmt = _("Unable to allocate a single hostname to multiple instances")
class InvalidFixedIpAndMaxCountRequest(Invalid):
msg_fmt = _("Failed to launch instances: %(reason)s")

View File

@ -42,6 +42,10 @@ This rule will control the visibility for a set of servers attributes:
Microvision 2.75 added the above attributes in the ``PUT /servers/{server_id}``
and ``POST /servers/{server_id}/action (rebuild)`` API responses which are
also controlled by this policy rule, like the ``GET /servers*`` APIs.
Microversion 2.90 made the ``OS-EXT-SRV-ATTR:hostname`` attribute available to
all users, so this policy has no effect on that field for microversions 2.90
and greater.
""",
operations=[
{

View File

@ -0,0 +1,80 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "us-west",
"OS-EXT-SRV-ATTR:hostname": "%(hostname)s",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "%(strtime)s",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"addr": "192.168.1.30",
"version": 4
}
]
},
"adminPass": "seekr3t",
"config_drive": "",
"created": "%(isotime)s",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6",
"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"
}
],
"locked": false,
"locked_reason": null,
"metadata": {
"meta_var": "meta_val"
},
"name": "foobar",
"os-extended-volumes:volumes_attached": [],
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "%(isotime)s",
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi",
"user_id": "fake"
}
}

View File

@ -0,0 +1,15 @@
{
"rebuild" : {
"accessIPv4" : "%(access_ip_v4)s",
"accessIPv6" : "%(access_ip_v6)s",
"OS-DCF:diskConfig": "AUTO",
"imageRef" : "%(uuid)s",
"name" : "%(name)s",
"adminPass" : "%(pass)s",
"hostname": "%(hostname)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" : "1",
"OS-DCF:diskConfig": "AUTO",
"metadata" : {
"My Server Name" : "Apache1"
},
"security_groups": [
{
"name": "default"
}
],
"user_data" : "%(user_data)s",
"networks": "auto",
"hostname": "custom-hostname"
}
}

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

@ -0,0 +1,81 @@
{
"server": {
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"addresses": {
"private": [
{
"addr": "%(ip)s",
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"version": 4
}
]
},
"created": "%(isotime)s",
"description": null,
"locked": false,
"locked_reason": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"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",
"config_drive": "%(cdrive)s",
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "nova",
"OS-EXT-SRV-ATTR:hostname": "%(hostname)s",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"os-extended-volumes:volumes_attached": [
{"id": "volume_id1", "delete_on_termination": false},
{"id": "volume_id2", "delete_on_termination": false}
],
"OS-SRV-USG:launched_at": "%(strtime)s",
"OS-SRV-USG:terminated_at": null,
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "%(isotime)s",
"user_id": "fake"
}
}

View File

@ -0,0 +1,8 @@
{
"server": {
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"OS-DCF:diskConfig": "AUTO",
"hostname": "new-server-hostname"
}
}

View File

@ -0,0 +1,78 @@
{
"server": {
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"addresses": {
"private": [
{
"addr": "%(ip)s",
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"version": 4
}
]
},
"created": "%(isotime)s",
"description": null,
"locked": false,
"locked_reason": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"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",
"config_drive": "",
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "us-west",
"OS-EXT-SRV-ATTR:hostname": "new-server-hostname",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"os-extended-volumes:volumes_attached": [],
"OS-SRV-USG:launched_at": "%(strtime)s",
"OS-SRV-USG:terminated_at": null,
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"server_groups": [],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "%(isotime)s",
"user_id": "fake"
}
}

View File

@ -0,0 +1,88 @@
{
"servers": [
{
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"addresses": {
"private": [
{
"addr": "%(ip)s",
"OS-EXT-IPS-MAC:mac_addr": "00:0c:29:0d:11:74",
"OS-EXT-IPS:type": "fixed",
"version": 4
}
]
},
"created": "%(isotime)s",
"description": "",
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {},
"original_name": "m1.tiny",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"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",
"config_drive": "%(cdrive)s",
"locked": false,
"locked_reason": "",
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "nova",
"OS-EXT-SRV-ATTR:hostname": "custom-hostname",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"os-extended-volumes:volumes_attached": [
{"id": "volume_id1", "delete_on_termination": false},
{"id": "volume_id2", "delete_on_termination": false}
],
"OS-SRV-USG:launched_at": "%(strtime)s",
"OS-SRV-USG:terminated_at": null,
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": null,
"updated": "%(isotime)s",
"user_id": "fake"
}
],
"servers_links": [
{
"href": "%(versioned_compute_endpoint)s/servers/detail?limit=1&marker=%(id)s",
"rel": "next"
}
]
}

View File

@ -0,0 +1,24 @@
{
"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"
}
],
"servers_links": [
{
"href": "%(versioned_compute_endpoint)s/servers?limit=1&marker=%(id)s",
"rel": "next"
}
]
}

View File

@ -596,9 +596,6 @@ class ServersSampleJson274Test(ServersSampleBase):
def _setup_compute_service(self):
return self.start_service('compute', host='openstack-node-01')
def setUp(self):
super(ServersSampleJson274Test, self).setUp()
def test_servers_post_with_only_host(self):
self._post_server(use_common_server_api_samples=False,
sample_name='server-create-req-with-only-host')
@ -612,6 +609,15 @@ class ServersSampleJson274Test(ServersSampleBase):
sample_name='server-create-req-with-host-and-node')
class ServersSampleJson290Test(ServersSampleJsonTest):
microversion = '2.90'
scenarios = [('v2_90', {'api_major_version': 'v2.1'})]
use_common_server_post = False
# we want to show that the non-admin response includes the hostname
ADMIN_API = False
class ServersUpdateSampleJsonTest(ServersSampleBase):
# Many of the 'os_compute_api:servers:*' policies are admin-only, and we
@ -634,7 +640,7 @@ class ServersUpdateSampleJson247Test(ServersUpdateSampleJsonTest):
scenarios = [('v2_47', {'api_major_version': 'v2.1'})]
class ServersSampleJson275Test(ServersUpdateSampleJsonTest):
class ServersUpdateSampleJson275Test(ServersUpdateSampleJsonTest):
microversion = '2.75'
scenarios = [('v2_75', {'api_major_version': 'v2.1'})]
@ -656,6 +662,46 @@ class ServersSampleJson275Test(ServersUpdateSampleJsonTest):
self._verify_response('server-action-rebuild-resp', subs, resp, 202)
class ServersUpdateSampleJson289Test(ServersUpdateSampleJsonTest):
microversion = '2.90'
scenarios = [('v2_90', {'api_major_version': 'v2.1'})]
# we want to show that the non-admin response includes the hostname
ADMIN_API = False
def test_update_server(self):
uuid = self._post_server()
subs = {}
subs['hostid'] = '[a-f0-9]+'
subs['access_ip_v4'] = '1.2.3.4'
subs['access_ip_v6'] = '80fe::'
subs['hostname'] = 'updated-hostname'
response = self._do_put('servers/%s' % uuid,
'server-update-req', subs)
self._verify_response('server-update-resp', subs, response, 200)
def test_server_rebuild(self):
uuid = self._post_server()
params = {
'uuid': self.glance.auto_disk_config_enabled_image['id'],
'name': 'foobar',
'pass': 'seekr3t',
'hostid': '[a-f0-9]+',
'access_ip_v4': '1.2.3.4',
'access_ip_v6': '80fe::',
'hostname': 'updated-hostname',
}
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 ServerSortKeysJsonTests(ServersSampleBase):
sample_dir = 'servers-sort'

View File

@ -2845,6 +2845,67 @@ class ServersControllerTestV283(ControllerTest):
self.assertEqual(uuids.fake, servers[0]['id'])
class ServersControllerTestV290(ControllerTest):
filters = ['hostname']
def test_get_servers_by_new_filter_for_non_admin(self):
def fake_get_all(context, search_opts=None, **kwargs):
self.assertIsNotNone(search_opts)
for f in self.filters:
self.assertIn(f, search_opts)
return objects.InstanceList(
objects=[fakes.stub_instance_obj(100, uuid=uuids.fake)])
self.mock_get_all.side_effect = fake_get_all
query_str = '&'.join('%s=test_value' % f for f in self.filters)
req = fakes.HTTPRequest.blank(
self.path_with_query % query_str, version='2.90')
servers = self.controller.index(req)['servers']
self.assertEqual(1, len(servers))
self.assertEqual(uuids.fake, servers[0]['id'])
def test_get_servers_new_filters_for_non_admin_old_version(self):
def fake_get_all(context, search_opts=None, **kwargs):
self.assertIsNotNone(search_opts)
for f in self.filters:
self.assertNotIn(f, search_opts)
return objects.InstanceList(objects=[])
# Without policy edition, test will fail and admin filter will work.
self.policy.set_rules({'os_compute_api:servers:index': ''})
self.mock_get_all.side_effect = fake_get_all
query_str = '&'.join('%s=test_value' % f for f in self.filters)
req = fakes.HTTPRequest.blank(
self.path_with_query % query_str, version='2.89')
servers = self.controller.index(req)['servers']
self.assertEqual(0, len(servers))
def test_get_servers_by_node_fail_non_admin(self):
def fake_get_all(context, search_opts=None, **kwargs):
self.assertIsNotNone(search_opts)
self.assertNotIn('node', search_opts)
return objects.InstanceList(
objects=[fakes.stub_instance_obj(100, uuid=uuids.fake)])
self.mock_get_all.side_effect = fake_get_all
self.policy.set_rules({
'os_compute_api:servers:index': '',
'os_compute_api:servers:allow_all_filters': 'role:admin',
})
query_str = "node=node1"
req = fakes.HTTPRequest.blank(
self.path_with_query % query_str, version='2.90')
servers = self.controller.index(req)['servers']
self.assertEqual(1, len(servers))
self.assertEqual(uuids.fake, servers[0]['id'])
class ServersControllerDeleteTest(ControllerTest):
def setUp(self):
@ -3609,6 +3670,11 @@ class ServersControllerRebuildTestV263(ControllerTest):
server = self.controller._action_rebuild(
self.req, FAKE_UUID, body=self.body).obj['server']
# TODO(stephenfin): This is a lie. We call '_get_server' immediately
# after making the call to 'nova.compute.api.API().rebuild_server' in
# '_action_rebuild', which means all we're testing here is the value
# returned by 'mock_get' above. Drop it in favour of testing the calls
# to the API itself
if certs:
self.assertEqual(certs, server['trusted_image_certificates'])
else:
@ -3761,6 +3827,69 @@ class ServersControllerRebuildTestV271(ControllerTest):
self.assertEqual([], server['server_groups'])
class ServersControllerRebuildTestV290(ControllerTest):
microversion = '2.90'
image_uuid = nova_fixtures.GlanceFixture.image3['id']
def setUp(self):
super().setUp()
mock_rebuild = mock.patch(
'nova.compute.api.API.rebuild', return_value=None)
self.mock_rebuild = mock_rebuild.start()
self.addCleanup(mock_rebuild.stop)
def _get_request(self, body=None):
req = fakes.HTTPRequest.blank(
self.path_action % FAKE_UUID,
use_admin_context=True,
)
req.method = 'POST'
req.content_type = 'application/json'
req.body = jsonutils.dump_as_bytes(body)
req.api_version_request = api_version_request.APIVersionRequest(
self.microversion)
return req
def test_rebuild_server_with_hostname(self):
body = {
'rebuild': {
'imageRef': self.image_uuid,
'hostname': 'new-hostname',
}
}
req = self._get_request(body)
# There's nothing to check here from the return value since the
# 'rebuild' API is a cast and we immediately fetch the instance from
# the database after this cast...which returns a mocked Instance
self.controller._action_rebuild(
req, FAKE_UUID, body=body,
).obj['server']
# ...so instead we check the call to the API itself
self.mock_rebuild.assert_called_once()
self.assertIn('hostname', self.mock_rebuild.call_args[0][1])
def test_rebuild_server_with_hostname_old_version(self):
"""Tests that trying to rebuild with hostname before 2.90 fails."""
body = {
'rebuild': {
'imageRef': self.image_uuid,
'hostname': 'new-hostname',
}
}
req = self._get_request(body)
req.api_version_request = api_version_request.APIVersionRequest('2.89')
ex = self.assertRaises(
exception.ValidationError,
self.controller._action_rebuild,
req, FAKE_UUID, body=body)
self.assertIn('hostname', str(ex))
class _ServersControllerUpdateTest(ControllerTest):
microversion = None
@ -3970,6 +4099,47 @@ class ServersControllerUpdateTestV271(_ServersControllerUpdateTest):
self.assertEqual([], res_dict['server']['server_groups'])
class ServersControllerUpdateTestV290(_ServersControllerUpdateTest):
microversion = '2.90'
def test_update_server_with_hostname(self):
body = {'server': {'hostname': 'my-hostname'}}
req = self._get_request(body)
res_dict = self.controller.update(req, FAKE_UUID, body=body)
self.assertEqual(res_dict['server']['id'], FAKE_UUID)
self.assertEqual(
res_dict['server']["OS-EXT-SRV-ATTR:hostname"], 'my-hostname')
def test_update_server_with_hostname__too_long(self):
body = {'server': {'hostname': 'a' * 64}}
req = self._get_request(body)
self.assertRaises(
exception.ValidationError, self.controller.update,
req, FAKE_UUID, body=body)
def test_update_server_with_hostname__invalid_fqdn(self):
# only hostnames are allowed, not FQDN
body = {'server': {'hostname': 'hostname.example.com'}}
req = self._get_request(body)
self.assertRaises(
exception.ValidationError, self.controller.update,
req, FAKE_UUID, body=body)
def test_update_server_with_hostname_old_version(self):
"""Tests that trying to update with hostname before 2.90 fails."""
body = {'server': {'hostname': 'new-hostname'}}
req = self._get_request(body)
req.api_version_request = api_version_request.APIVersionRequest('2.89')
ex = self.assertRaises(
exception.ValidationError,
self.controller.update,
req, FAKE_UUID, body=body)
self.assertIn('hostname', str(ex))
class ServersControllerTriggerCrashDumpTest(ControllerTest):
def setUp(self):
@ -7049,6 +7219,52 @@ class ServersControllerCreateTestV274(ServersControllerCreateTest):
pass
class ServersControllerCreateTestV290(ServersControllerCreateTest):
def _generate_req(self, hostname=None, api_version='2.90'):
if hostname:
self.body['server']['hostname'] = hostname
self.req.body = jsonutils.dump_as_bytes(self.body)
self.req.api_version_request = \
api_version_request.APIVersionRequest(api_version)
def test_create_instance_with_hostname(self):
self._generate_req(hostname='test-hostname')
self.controller.create(self.req, body=self.body).obj['server']
def test_create_instance_with_hostname_invalid(self):
self._generate_req(hostname='example.com')
ex = self.assertRaises(
exception.ValidationError,
self.controller.create,
self.req, body=self.body)
self.assertIn('Invalid input for field/attribute hostname.', str(ex))
def test_create_instance_with_hostname_old_version(self):
self._generate_req(hostname='hostname', api_version='2.89')
ex = self.assertRaises(
exception.ValidationError,
self.controller.create,
self.req, body=self.body)
self.assertIn("'hostname' was unexpected", str(ex))
@mock.patch.object(
compute_api.API, 'create',
side_effect=exception.AmbiguousHostnameForMultipleInstances())
def test_create_multiple_instances_with_hostname(self, mock_create):
self.body['server']['hostname'] = 'hostname'
self.body['server']['max_count'] = 2
self.req.body = jsonutils.dump_as_bytes(self.body)
self.req.api_version_request = \
api_version_request.APIVersionRequest('2.90')
self.assertRaises(
webob.exc.HTTPBadRequest,
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'

View File

@ -355,6 +355,14 @@ class _ComputeAPIUnitTestMixIn(object):
self.compute_api.create, self.context, 'fake_flavor', 'image_id',
max_count=2, requested_networks=requested_networks)
def test_specified_hostname_and_multiple_instances(self):
# Tests that if hostname is specified then there is only one instance
# request allowed (i.e max_count == 1)
self.assertRaises(
exception.AmbiguousHostnameForMultipleInstances,
self.compute_api.create, self.context, 'fake_flavor', 'image_id',
max_count=2, hostname='foo')
# TODO(huaqiang): Remove in Wallaby
@mock.patch('nova.compute.api.API._check_requested_networks',
new=mock.Mock(return_value=1))
@ -4856,8 +4864,7 @@ class _ComputeAPIUnitTestMixIn(object):
volume_types)
self.assertEqual(bdms[0].volume_type, volume_types[0]['name'])
def _test_provision_instances_with_cinder_error(self,
expected_exception):
def _test_provision_instances_with_cinder_error(self, expected_exception):
@mock.patch('nova.compute.utils.check_num_instances_quota')
@mock.patch.object(objects.Instance, 'create')
@mock.patch.object(objects.RequestSpec, 'from_components')
@ -7185,11 +7192,11 @@ class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
self.compute_api._validate_and_build_base_options(
self.context, flavor, boot_meta, uuids.image_href,
mock.sentinel.image_id, kernel_id, ramdisk_id,
'fake-display-name', 'fake-description', key_name,
key_data, requested_secgroups, 'fake-az', user_data,
metadata, access_ip_v4, access_ip_v6, requested_networks,
config_drive, auto_disk_config, reservation_id, max_count,
supports_port_resource_request
'fake-display-name', 'fake-description', 'fake-hostname',
key_name, key_data, requested_secgroups, 'fake-az',
user_data, metadata, access_ip_v4, access_ip_v6,
requested_networks, config_drive, auto_disk_config,
reservation_id, max_count, supports_port_resource_request,
)
)
# Assert the neutron security group API get method was called once

View File

@ -0,0 +1,16 @@
---
features:
- |
The 2.90 microversion has been added. This microversion allows users to
specify a requested hostname to be configured for the instance metadata
when creating an instance (``POST /servers``), updating an instance
(``PUT /servers/{id}``), or rebuilding an instance
(``POST /servers/{server_id}/action (rebuild)``). When specified, this
hostname replaces the hostname that nova auto-generates from the instance
display name. As with the auto-generated hostnames, a service such as
``cloud-init`` can automatically configure the hostname in the guest OS
using this information retrieved from the metadata service.
In addition, starting with the 2.90 microversion, the
``OS-EXT-SRV-ATTR:hostname`` field is now returned for all users.
Previously this was restricted to admin users.