Browse Source

Add trusted_image_certificates to REST API

This change adds support for the trusted_image_certificates parameter,
which is used to define a list of trusted certificate IDs that can be
used during image signature verification and certificate validation. The
parameter may contain a list of strings, each string representing the ID
of a trusted certificate. The list is restricted to a maximum of 50 IDs.
The list of certificate IDs will be stored in the trusted_certs field of
the instance InstanceExtra and will be used to verify the validity of
the signing certificate of a signed instance image.

The trusted_image_certificates request parameter can be passed to
the server create and rebuild APIs (if allowed by policy):

* POST /servers
* POST /servers/{server_id}/action (rebuild)

The following policy rules were added to restrict the usage of the
``trusted_image_certificates`` request parameter in the server create
and rebuild APIs:

* os_compute_api:servers:create:trusted_certs
* os_compute_api:servers:rebuild:trusted_certs

The trusted_image_certificates parameter will be in the response
body of the following APIs (not restricted by policy):

* GET /servers/detail
* GET /servers/{server_id}
* PUT /servers/{server_id}
* POST /servers/{server_id}/action (rebuild)

APIImpact

Implements blueprint: nova-validate-certificates
Change-Id: Iedd3fea0e86648fae364f075915555dcb2c4f199
changes/04/486204/115
Brianna Poulos 3 years ago
committed by Matt Riedemann
parent
commit
8c7ca368b1
  1. 34
      api-ref/source/parameters.yaml
  2. 10
      api-ref/source/servers-actions.inc
  3. 21
      api-ref/source/servers.inc
  4. 71
      doc/api_samples/servers/v2.63/server-action-rebuild-resp.json
  5. 20
      doc/api_samples/servers/v2.63/server-action-rebuild.json
  6. 28
      doc/api_samples/servers/v2.63/server-create-req.json
  7. 22
      doc/api_samples/servers/v2.63/server-create-resp.json
  8. 93
      doc/api_samples/servers/v2.63/server-get-resp.json
  9. 8
      doc/api_samples/servers/v2.63/server-update-req.json
  10. 66
      doc/api_samples/servers/v2.63/server-update-resp.json
  11. 95
      doc/api_samples/servers/v2.63/servers-details-resp.json
  12. 2
      doc/api_samples/versions/v21-version-get-resp.json
  13. 2
      doc/api_samples/versions/versions-get-resp.json
  14. 4
      nova/api/openstack/api_version_request.py
  15. 23
      nova/api/openstack/compute/rest_api_version_history.rst
  16. 13
      nova/api/openstack/compute/schemas/servers.py
  17. 53
      nova/api/openstack/compute/servers.py
  18. 6
      nova/api/openstack/compute/views/servers.py
  19. 13
      nova/api/validation/parameter_types.py
  20. 104
      nova/compute/api.py
  21. 6
      nova/exception.py
  22. 20
      nova/policies/servers.py
  23. 69
      nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-action-rebuild-resp.json.tpl
  24. 20
      nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-action-rebuild.json.tpl
  25. 28
      nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-create-req.json.tpl
  26. 22
      nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-create-resp.json.tpl
  27. 93
      nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-get-resp.json.tpl
  28. 8
      nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-update-req.json.tpl
  29. 66
      nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-update-resp.json.tpl
  30. 95
      nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/servers-details-resp.json.tpl
  31. 72
      nova/tests/functional/api_sample_tests/test_servers.py
  32. 325
      nova/tests/unit/api/openstack/compute/test_serversV21.py
  33. 32
      nova/tests/unit/api/openstack/fakes.py
  34. 265
      nova/tests/unit/compute/test_compute_api.py
  35. 2
      nova/tests/unit/test_policy.py
  36. 30
      releasenotes/notes/trusted-certs-microversion-589b75f0180d4d51.yaml

34
api-ref/source/parameters.yaml

@ -5706,6 +5706,40 @@ server_tags_create:
required: false
type: array
min_version: 2.52
server_trusted_image_certificates_create_req:
description: |
A list of trusted certificate IDs, which are used during image
signature verification to verify the signing certificate. The list is
restricted to a maximum of 50 IDs. This parameter is optional in server
create requests if allowed by policy, and is not supported for
volume-backed instances.
in: body
required: false
type: array
min_version: 2.63
server_trusted_image_certificates_rebuild_req:
description: |
A list of trusted certificate IDs, which are used during image
signature verification to verify the signing certificate. The list is
restricted to a maximum of 50 IDs. This parameter is optional in server
rebuild requests if allowed by policy, and is not supported
for volume-backed instances.
If ``null`` is specified, the existing trusted certificate IDs are either
unset or reset to the configured defaults.
in: body
required: false
type: array
min_version: 2.63
server_trusted_image_certificates_resp:
description: |
A list of trusted certificate IDs, that were used during image signature
verification to verify the signing certificate. The list is restricted
to a maximum of 50 IDs.
in: body
required: true
type: array
min_version: 2.63
server_usages:
description: |
A list of the server usage objects.

10
api-ref/source/servers-actions.inc

@ -488,10 +488,11 @@ Request
- description: server_description
- key_name: key_name_rebuild_req
- user_data: user_data_rebuild_req
- trusted_image_certificates: server_trusted_image_certificates_rebuild_req
**Example Rebuild Server (rebuild Action) (v2.54)**
**Example Rebuild Server (rebuild Action) (v2.63)**
.. literalinclude:: ../../doc/api_samples/servers/v2.54/server-action-rebuild.json
.. literalinclude:: ../../doc/api_samples/servers/v2.63/server-action-rebuild.json
:language: javascript
Response
@ -537,10 +538,11 @@ Response
- tags: tags
- key_name: key_name_rebuild_resp
- user_data: user_data_rebuild_resp
- trusted_image_certificates: server_trusted_image_certificates_resp
**Example Rebuild Server (rebuild Action) (v2.54)**
**Example Rebuild Server (rebuild Action) (v2.63)**
.. literalinclude:: ../../doc/api_samples/servers/v2.54/server-action-rebuild-resp.json
.. literalinclude:: ../../doc/api_samples/servers/v2.63/server-action-rebuild-resp.json
:language: javascript
Remove (Disassociate) Floating Ip (removeFloatingIp Action) (DEPRECATED)

21
api-ref/source/servers.inc

@ -381,6 +381,7 @@ Request
- os:scheduler_hints.query: os:scheduler_hints_query
- os:scheduler_hints.same_host: os:scheduler_hints_same_host
- os:scheduler_hints.target_cell: os:scheduler_hints_target_cell
- trusted_image_certificates: server_trusted_image_certificates_create_req
**Example Create Server**
@ -392,6 +393,11 @@ Request
.. literalinclude:: ../../doc/api_samples/servers/v2.37/server-create-req.json
:language: javascript
**Example Create Server With Trusted Image Certificates (v2.63)**
.. literalinclude:: ../../doc/api_samples/servers/v2.63/server-create-req.json
:language: javascript
Response
--------
@ -610,10 +616,11 @@ Response
- host_status: host_status
- description: server_description_resp
- tags: tags
- trusted_image_certificates: server_trusted_image_certificates_resp
**Example List Servers Detailed (2.47)**
**Example List Servers Detailed (2.63)**
.. literalinclude:: /../../doc/api_samples/servers/v2.47/servers-details-resp.json
.. literalinclude:: /../../doc/api_samples/servers/v2.63/servers-details-resp.json
:language: javascript
@ -716,10 +723,11 @@ Response
- host_status: host_status
- description: server_description_resp
- tags: tags
- trusted_image_certificates: server_trusted_image_certificates_resp
**Example Show Server Details (2.47)**
**Example Show Server Details (2.63)**
.. literalinclude:: ../../doc/api_samples/servers/v2.47/server-get-resp.json
.. literalinclude:: ../../doc/api_samples/servers/v2.63/server-get-resp.json
:language: javascript
Update Server
@ -808,10 +816,11 @@ Response
- locked: locked
- description: server_description_resp
- tags: tags
- trusted_image_certificates: server_trusted_image_certificates_resp
**Example Update server name (2.47)**
**Example Update server name (2.63)**
.. literalinclude:: ../../doc/api_samples/servers/v2.47/server-update-resp.json
.. literalinclude:: ../../doc/api_samples/servers/v2.63/server-update-resp.json
:language: javascript
Delete Server

71
doc/api_samples/servers/v2.63/server-action-rebuild-resp.json

@ -0,0 +1,71 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"addr": "192.168.0.3",
"version": 4
}
]
},
"adminPass": "seekr3t",
"created": "2017-10-10T16:06:02Z",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {
"hw:cpu_model": "SandyBridge",
"hw:cpu_policy": "dedicated",
"hw:mem_page_size": "2048"
},
"original_name": "m1.tiny.specs",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "28d8d56f0e3a77e20891f455721cbb68032e017045e20aa5dfc6cb66",
"id": "a0a80a94-3d81-4a10-822a-daa0cf9e870b",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/a4baaf2a-3768-4e45-8847-13becef6bc5e",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/a4baaf2a-3768-4e45-8847-13becef6bc5e",
"rel": "bookmark"
}
],
"locked": false,
"metadata": {
"meta_var": "meta_val"
},
"name": "foobar",
"key_name": "new-key",
"description" : "description of foobar",
"progress": 0,
"status": "ACTIVE",
"tags": [],
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi",
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
],
"updated": "2017-10-10T16:06:03Z",
"user_id": "fake"
}
}

20
doc/api_samples/servers/v2.63/server-action-rebuild.json

@ -0,0 +1,20 @@
{
"rebuild" : {
"accessIPv4" : "1.2.3.4",
"accessIPv6" : "80fe::",
"OS-DCF:diskConfig": "AUTO",
"imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b",
"name" : "foobar",
"key_name": "new-key",
"description" : "description of foobar",
"adminPass" : "seekr3t",
"metadata" : {
"meta_var" : "meta_val"
},
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
]
}
}

28
doc/api_samples/servers/v2.63/server-create-req.json

@ -0,0 +1,28 @@
{
"server" : {
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"name" : "new-server-test",
"imageRef" : "70a599e0-31e7-49b7-b260-868f441e862b",
"flavorRef" : "6",
"availability_zone": "nova",
"OS-DCF:diskConfig": "AUTO",
"metadata" : {
"My Server Name" : "Apache1"
},
"security_groups": [
{
"name": "default"
}
],
"user_data" : "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
"networks": "auto",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
]
},
"OS-SCH-HNT:scheduler_hints": {
"same_host": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
}
}

22
doc/api_samples/servers/v2.63/server-create-resp.json

@ -0,0 +1,22 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"adminPass": "wKLKinb9u7GM",
"id": "aab35fd0-b459-4b59-9308-5a23147f3165",
"links": [
{
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/servers/aab35fd0-b459-4b59-9308-5a23147f3165",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/aab35fd0-b459-4b59-9308-5a23147f3165",
"rel": "bookmark"
}
],
"security_groups": [
{
"name": "default"
}
]
}
}

93
doc/api_samples/servers/v2.63/server-get-resp.json

@ -0,0 +1,93 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "nova",
"OS-EXT-SRV-ATTR:host": "compute",
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "r-ov3q80zj",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
"OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "2017-02-14T19:23:59.895661",
"OS-SRV-USG:terminated_at": null,
"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
}
]
},
"config_drive": "",
"created": "2017-02-14T19:23:58Z",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {
"hw:cpu_model": "SandyBridge",
"hw:cpu_policy": "dedicated",
"hw:mem_page_size": "2048"
},
"original_name": "m1.tiny.specs",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6",
"host_status": "UP",
"id": "9168b536-cd40-4630-b43f-b259807c6e87",
"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/9168b536-cd40-4630-b43f-b259807c6e87",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/9168b536-cd40-4630-b43f-b259807c6e87",
"rel": "bookmark"
}
],
"locked": false,
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"os-extended-volumes:volumes_attached": [],
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
],
"updated": "2017-02-14T19:24:00Z",
"user_id": "fake"
}
}

8
doc/api_samples/servers/v2.63/server-update-req.json

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

66
doc/api_samples/servers/v2.63/server-update-resp.json

@ -0,0 +1,66 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"accessIPv4": "1.2.3.4",
"accessIPv6": "80fe::",
"addresses": {
"private": [
{
"addr": "192.168.0.3",
"version": 4
}
]
},
"created": "2012-12-02T02:11:57Z",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {
"hw:cpu_model": "SandyBridge",
"hw:cpu_policy": "dedicated",
"hw:mem_page_size": "2048"
},
"original_name": "m1.tiny.specs",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "6e84af987b4e7ec1c039b16d21f508f4a505672bd94fb0218b668d07",
"id": "324dfb7d-f4a9-419a-9a19-237df04b443b",
"image": {
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
"links": [
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/324dfb7d-f4a9-419a-9a19-237df04b443b",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/324dfb7d-f4a9-419a-9a19-237df04b443b",
"rel": "bookmark"
}
],
"locked": false,
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"progress": 0,
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
],
"updated": "2012-12-02T02:11:58Z",
"user_id": "fake"
}
}

95
doc/api_samples/servers/v2.63/servers-details-resp.json

@ -0,0 +1,95 @@
{
"servers": [
{
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "nova",
"OS-EXT-SRV-ATTR:host": "compute",
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "r-y0w4v32k",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
"OS-EXT-SRV-ATTR:user_data": "IyEvYmluL2Jhc2gKL2Jpbi9zdQplY2hvICJJIGFtIGluIHlvdSEiCg==",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "2017-10-10T15:49:09.516729",
"OS-SRV-USG:terminated_at": null,
"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
}
]
},
"config_drive": "",
"created": "2017-10-10T15:49:08Z",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {
"hw:cpu_model": "SandyBridge",
"hw:cpu_policy": "dedicated",
"hw:mem_page_size": "2048"
},
"original_name": "m1.tiny.specs",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "2091634baaccdc4c5a1d57069c833e402921df696b7f970791b12ec6",
"host_status": "UP",
"id": "569f39f9-7c76-42a1-9c2d-8394e2638a6d",
"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/569f39f9-7c76-42a1-9c2d-8394e2638a6d",
"rel": "self"
},
{
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/569f39f9-7c76-42a1-9c2d-8394e2638a6d",
"rel": "bookmark"
}
],
"locked": false,
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"os-extended-volumes:volumes_attached": [],
"progress": 0,
"security_groups": [
{
"name": "default"
}
],
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
],
"updated": "2017-10-10T15:49:09Z",
"user_id": "fake"
}
]
}

2
doc/api_samples/versions/v21-version-get-resp.json

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

2
doc/api_samples/versions/versions-get-resp.json

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

4
nova/api/openstack/api_version_request.py

@ -148,6 +148,8 @@ REST_API_VERSION_HISTORY = """REST API Version History:
/flavors APIs.
* 2.62 - Add ``host`` and ``hostId`` fields to instance action detail API
responses.
* 2.63 - Add support for applying trusted certificates when creating or
rebuilding a server.
"""
# The minimum and maximum versions of the API supported
@ -156,7 +158,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.62"
_MAX_API_VERSION = "2.63"
DEFAULT_API_VERSION = _MIN_API_VERSION
# Almost all proxy APIs which are related to network, images and baremetal

23
nova/api/openstack/compute/rest_api_version_history.rst

@ -795,3 +795,26 @@ the newly added ``host`` field will be controlled via policy rule
``os_compute_api:os-instance-actions:events``, which is the same policy used
for the ``events.traceback`` field. If the user is prevented by policy, only
``hostId`` will be displayed.
2.63
----
Adds support for the ``trusted_image_certificates`` parameter, which is used to
define a list of trusted certificate IDs that can be used during image
signature verification and certificate validation. The list is restricted to
a maximum of 50 IDs. Note that ``trusted_image_certificates`` is not supported
with volume-backed servers.
The ``trusted_image_certificates`` request parameter can be passed to
the server create and rebuild APIs:
* ``POST /servers``
* ``POST /servers/{server_id}/action (rebuild)``
The ``trusted_image_certificates`` parameter will be in the response body of
the following APIs:
* ``GET /servers/detail``
* ``GET /servers/{server_id}``
* ``PUT /servers/{server_id}``
* ``POST /servers/{server_id}/action (rebuild)``

13
nova/api/openstack/compute/schemas/servers.py

@ -148,6 +148,13 @@ base_create_v257 = copy.deepcopy(base_create_v252)
base_create_v257['properties']['server']['properties'].pop('personality')
# 2.63 builds on 2.57 and makes the following changes:
# Allowing adding trusted certificates to instances when booting
base_create_v263 = copy.deepcopy(base_create_v257)
base_create_v263['properties']['server']['properties'][
'trusted_image_certificates'] = parameter_types.trusted_certs
base_update = {
'type': 'object',
'properties': {
@ -224,6 +231,12 @@ base_rebuild_v257['properties']['rebuild']['properties']['user_data'] = ({
]
})
# 2.63 builds on 2.57 and makes the following changes:
# Allowing adding trusted certificates to instances when rebuilding
base_rebuild_v263 = copy.deepcopy(base_rebuild_v257)
base_rebuild_v263['properties']['rebuild']['properties'][
'trusted_image_certificates'] = parameter_types.trusted_certs
resize = {
'type': 'object',
'properties': {

53
nova/api/openstack/compute/servers.py

@ -86,6 +86,9 @@ class ServersController(wsgi.Controller):
schema_server_create_v252 = schema_servers.base_create_v252
schema_server_create_v257 = schema_servers.base_create_v257
schema_server_create_v263 = schema_servers.base_create_v263
schema_server_rebuild_v263 = schema_servers.base_rebuild_v263
# NOTE(alex_xu): Please do not add more items into this list. This list
# should be removed in the future.
schema_func_list = [
@ -132,6 +135,7 @@ class ServersController(wsgi.Controller):
# TODO(alex_xu): The final goal is that merging all of
# extended json-schema into server main json-schema.
self._create_schema(self.schema_server_create_v263, '2.63')
self._create_schema(self.schema_server_create_v257, '2.57')
self._create_schema(self.schema_server_create_v252, '2.52')
self._create_schema(self.schema_server_create_v242, '2.42')
@ -304,6 +308,8 @@ class ServersController(wsgi.Controller):
expected_attrs.append('services')
if api_version_request.is_supported(req, '2.26'):
expected_attrs.append("tags")
if api_version_request.is_supported(req, '2.63'):
expected_attrs.append("trusted_certs")
# merge our expected attrs with what the view builder needs for
# showing details
@ -345,6 +351,8 @@ class ServersController(wsgi.Controller):
if is_detail:
if api_version_request.is_supported(req, '2.26'):
expected_attrs.append("tags")
if api_version_request.is_supported(req, '2.63'):
expected_attrs.append("trusted_certs")
expected_attrs = self._view_builder.get_show_expected_attrs(
expected_attrs)
instance = common.get_instance(self.compute_api, context,
@ -456,7 +464,8 @@ class ServersController(wsgi.Controller):
@validation.schema(schema_server_create_v237, '2.37', '2.41')
@validation.schema(schema_server_create_v242, '2.42', '2.51')
@validation.schema(schema_server_create_v252, '2.52', '2.56')
@validation.schema(schema_server_create_v257, '2.57')
@validation.schema(schema_server_create_v257, '2.57', '2.62')
@validation.schema(schema_server_create_v263, '2.63')
def create(self, req, body):
"""Creates a new server for a given user."""
context = req.environ['nova.context']
@ -489,6 +498,14 @@ class ServersController(wsgi.Controller):
'availability_zone': availability_zone}
context.can(server_policies.SERVERS % 'create', target)
# Skip policy check for 'create:trusted_certs' if no trusted
# certificate IDs were provided.
trusted_certs = server_dict.get('trusted_image_certificates', None)
if trusted_certs:
create_kwargs['trusted_certs'] = trusted_certs
context.can(server_policies.SERVERS % 'create:trusted_certs',
target=target)
# TODO(Shao He, Feng) move this policy check to os-availability-zone
# extension after refactor it.
parse_az = self.compute_api.parse_availability_zone
@ -634,13 +651,15 @@ class ServersController(wsgi.Controller):
exception.RealtimeMaskNotFoundOrInvalid,
exception.SnapshotNotFound,
exception.UnableToAutoAllocateNetwork,
exception.MultiattachNotSupportedOldMicroversion) as error:
exception.MultiattachNotSupportedOldMicroversion,
exception.CertificateValidationFailed) as error:
raise exc.HTTPBadRequest(explanation=error.format_message())
except (exception.PortInUse,
exception.InstanceExists,
exception.NetworkAmbiguous,
exception.NoUniqueMatch,
exception.MultiattachSupportNotYetAvailable) as error:
exception.MultiattachSupportNotYetAvailable,
exception.CertificateValidationNotYetAvailable) as error:
raise exc.HTTPConflict(explanation=error.format_message())
# If the caller wanted a reservation_id, return it
@ -895,7 +914,8 @@ class ServersController(wsgi.Controller):
@validation.schema(schema_server_rebuild, '2.1', '2.18')
@validation.schema(schema_server_rebuild_v219, '2.19', '2.53')
@validation.schema(schema_server_rebuild_v254, '2.54', '2.56')
@validation.schema(schema_server_rebuild_v257, '2.57')
@validation.schema(schema_server_rebuild_v257, '2.57', '2.62')
@validation.schema(schema_server_rebuild_v263, '2.63')
def _action_rebuild(self, req, id, body):
"""Rebuild an instance with the given attributes."""
rebuild_dict = body['rebuild']
@ -906,9 +926,9 @@ class ServersController(wsgi.Controller):
context = req.environ['nova.context']
instance = self._get_server(context, req, id)
context.can(server_policies.SERVERS % 'rebuild',
target={'user_id': instance.user_id,
'project_id': instance.project_id})
target = {'user_id': instance.user_id,
'project_id': instance.project_id}
context.can(server_policies.SERVERS % 'rebuild', target=target)
attr_map = {
'name': 'display_name',
'description': 'display_description',
@ -930,6 +950,19 @@ class ServersController(wsgi.Controller):
if include_user_data and 'user_data' in rebuild_dict:
kwargs['user_data'] = rebuild_dict['user_data']
# 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'))
# 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.
and ('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)
for request_attribute, instance_attribute in attr_map.items():
try:
if request_attribute == 'name':
@ -947,7 +980,8 @@ class ServersController(wsgi.Controller):
image_href,
password,
**kwargs)
except exception.InstanceIsLocked as e:
except (exception.InstanceIsLocked,
exception.CertificateValidationNotYetAvailable) as e:
raise exc.HTTPConflict(explanation=e.format_message())
except exception.InstanceInvalidState as state_error:
common.raise_http_conflict_for_instance_invalid_state(state_error,
@ -970,7 +1004,8 @@ class ServersController(wsgi.Controller):
exception.FlavorDiskTooSmall,
exception.FlavorMemoryTooSmall,
exception.InvalidMetadata,
exception.AutoDiskConfigDisabledByImage) as error:
exception.AutoDiskConfigDisabledByImage,
exception.CertificateValidationFailed) as error:
raise exc.HTTPBadRequest(explanation=error.format_message())
instance = self._get_server(context, req, id, is_detail=True)

6
nova/api/openstack/compute/views/servers.py

@ -171,6 +171,12 @@ class ViewBuilder(common.ViewBuilder):
if api_version_request.is_supported(request, min_version="2.26"):
server["server"]["tags"] = [t.tag for t in instance.tags]
if api_version_request.is_supported(request, min_version="2.63"):
trusted_certs = None
if instance.trusted_certs:
trusted_certs = instance.trusted_certs.ids
server["server"]["trusted_image_certificates"] = trusted_certs
return server
def index(self, request, instances):

13
nova/api/validation/parameter_types.py

@ -476,3 +476,16 @@ pagination_parameters = {
'limit': multi_params(non_negative_integer),
'marker': multi_params({'type': 'string'})
}
# The trusted_certs list is restricted to a maximum of 50 IDs.
# "null" is allowed to unset/reset trusted certs during rebuild.
trusted_certs = {
"type": ["array", "null"],
"minItems": 1,
"maxItems": 50,
"uniqueItems": True,
"items": {
"type": "string",
"minLength": 1,
}
}

104
nova/compute/api.py

@ -104,6 +104,7 @@ AGGREGATE_ACTION_ADD = 'Add'
BFV_RESERVE_MIN_COMPUTE_VERSION = 17
CINDER_V3_ATTACH_MIN_COMPUTE_VERSION = 24
MIN_COMPUTE_MULTIATTACH = 27
MIN_COMPUTE_TRUSTED_CERTS = 31
# FIXME(danms): Keep a global cache of the cells we find the
# first time we look. This needs to be refreshed on a timer or
@ -853,7 +854,7 @@ class API(base.Base):
max_count, base_options, boot_meta, security_groups,
block_device_mapping, shutdown_terminate,
instance_group, check_server_group_quota, filter_properties,
key_pair, tags, supports_multiattach=False):
key_pair, tags, trusted_certs, supports_multiattach=False):
# Check quotas
num_instances = compute_utils.check_num_instances_quota(
context, instance_type, min_count, max_count)
@ -887,6 +888,10 @@ class API(base.Base):
instance.keypairs = objects.KeyPairList(objects=[])
if key_pair:
instance.keypairs.objects.append(key_pair)
instance.trusted_certs = self._retrieve_trusted_certs_object(
context, trusted_certs)
instance = self.create_db_entry_for_new_instance(context,
instance_type, boot_meta, instance, security_groups,
block_device_mapping, num_instances, i,
@ -961,6 +966,65 @@ class API(base.Base):
return instances_to_build
@staticmethod
def _retrieve_trusted_certs_object(context, trusted_certs, rebuild=False):
"""Convert user-requested trusted cert IDs to TrustedCerts object
Also validates that the deployment is new enough to support trusted
image certification validation.
:param context: The user request auth context
:param trusted_certs: list of user-specified trusted cert string IDs,
may be None
:param rebuild: True if rebuilding the server, False if creating a
new server
:returns: nova.objects.TrustedCerts object or None if no user-specified
trusted cert IDs were given and nova is not configured with
default trusted cert IDs
:raises: nova.exception.CertificateValidationNotYetAvailable: If
rebuilding a server with trusted certs on a compute host that is
too old to supported trusted image cert validation, or if creating
a server with trusted certs and there are no compute hosts in the
deployment that are new enough to support trusted image cert
validation
"""
# Retrieve trusted_certs parameter, or use CONF value if certificate
# validation is enabled
if trusted_certs:
certs_to_return = objects.TrustedCerts(ids=trusted_certs)
elif (CONF.glance.verify_glance_signatures and
CONF.glance.enable_certificate_validation and
CONF.glance.default_trusted_certificate_ids):
certs_to_return = objects.TrustedCerts(
ids=CONF.glance.default_trusted_certificate_ids)
else:
return None
# Confirm trusted_certs are supported by the minimum nova
# compute service version
# TODO(mriedem): This minimum version compat code can be dropped in the
# 19.0.0 Stein release when all computes must be at a minimum running
# Rocky code.
if rebuild:
# we only care about the current cell since this is
# a rebuild
min_compute_version = objects.Service.get_minimum_version(
context, 'nova-compute')
else:
# we don't know which cell it's going to get scheduled
# to, so check all cells
# NOTE(mriedem): For multi-create server requests, we're hitting
# this for each instance since it's not cached; we could likely
# optimize this.
min_compute_version = \
objects.service.get_minimum_version_all_cells(
context, ['nova-compute'])
if min_compute_version < MIN_COMPUTE_TRUSTED_CERTS:
raise exception.CertificateValidationNotYetAvailable()
return certs_to_return
def _get_bdm_image_metadata(self, context, block_device_mapping,
legacy_bdm=True):
"""If we are booting from a volume, we need to get the
@ -1031,7 +1095,7 @@ class API(base.Base):
block_device_mapping, auto_disk_config, filter_properties,
reservation_id=None, legacy_bdm=True, shutdown_terminate=False,
check_server_group_quota=False, tags=None,
supports_multiattach=False):
supports_multiattach=False, trusted_certs=None):
"""Verify all the input parameters regardless of the provisioning
strategy being performed and schedule the instance(s) for
creation.
@ -1049,6 +1113,14 @@ class API(base.Base):
if image_href:
image_id, boot_meta = self._get_image(context, image_href)
else:
# This is similar to the logic in _retrieve_trusted_certs_object.
if (trusted_certs or
(CONF.glance.verify_glance_signatures and
CONF.glance.enable_certificate_validation and
CONF.glance.default_trusted_certificate_ids)):
msg = _("Image certificate validation is not supported "
"when booting from volume")
raise exception.CertificateValidationFailed(message=msg)
image_id = None
boot_meta = self._get_bdm_image_metadata(
context, block_device_mapping, legacy_bdm)
@ -1096,7 +1168,8 @@ class API(base.Base):
context, instance_type, min_count, max_count, base_options,
boot_meta, security_groups, block_device_mapping,
shutdown_terminate, instance_group, check_server_group_quota,
filter_properties, key_pair, tags, supports_multiattach)
filter_properties, key_pair, tags, trusted_certs,
supports_multiattach)
instances = []
request_specs = []
@ -1577,7 +1650,7 @@ class API(base.Base):
config_drive=None, auto_disk_config=None, scheduler_hints=None,
legacy_bdm=True, shutdown_terminate=False,
check_server_group_quota=False, tags=None,
supports_multiattach=False):
supports_multiattach=False, trusted_certs=None):
"""Provision instances, sending instance information to the
scheduler. The scheduler will determine where the instance(s)
go and will handle creating the DB entries.
@ -1617,7 +1690,8 @@ class API(base.Base):
legacy_bdm=legacy_bdm,
shutdown_terminate=shutdown_terminate,
check_server_group_quota=check_server_group_quota,
tags=tags, supports_multiattach=supports_multiattach)
tags=tags, supports_multiattach=supports_multiattach,
trusted_certs=trusted_certs)
def _check_auto_disk_config(self, instance=None, image=None,
**extra_instance_updates):
@ -2998,6 +3072,18 @@ class API(base.Base):
instance.key_data = None
instance.keypairs = objects.KeyPairList(objects=[])
# Use trusted_certs value from kwargs to create TrustedCerts object
trusted_certs = None
if 'trusted_certs' in kwargs:
# Note that the user can set, change, or unset / reset trusted
# certs. If they are explicitly specifying
# trusted_image_certificates=None, that means we'll either unset
# them on the instance *or* reset to use the defaults (if defaults
# are configured).
trusted_certs = kwargs.pop('trusted_certs')
instance.trusted_certs = self._retrieve_trusted_certs_object(
context, trusted_certs, rebuild=True)
image_id, image = self._get_image(context, image_href)
self._check_auto_disk_config(image=image, **kwargs)
@ -3012,6 +3098,14 @@ class API(base.Base):
is_volume_backed = compute_utils.is_volume_backed_instance(
context, instance, bdms)
if is_volume_backed:
if trusted_certs:
# The only way we can get here is if the user tried to set
# trusted certs or specified trusted_image_certificates=None
# and default_trusted_certificate_ids is configured.
msg = _("Image certificate validation is not supported "
"for volume-backed servers.")
raise exception.CertificateValidationFailed(message=msg)
# For boot from volume, instance.image_ref is empty, so we need to
# query the image from the volume.
if root_bdm is None:

6
nova/exception.py

@ -2297,3 +2297,9 @@ class AllocationCreateFailed(NovaException):
class CertificateValidationFailed(NovaException):
msg_fmt = _("Image signature certificate validation failed for "
"certificate: %(cert_uuid)s. %(reason)s")
class CertificateValidationNotYetAvailable(NovaException):
msg_fmt = _("Image signature certificate validation support is "
"not yet available.")
code = 409

20
nova/policies/servers.py

@ -127,6 +127,16 @@ rules = [
'path': '/servers'
}
]),
policy.DocumentedRuleDefault(
SERVERS % 'create:trusted_certs',
RULE_AOO,
"Create a server with trusted image certificate IDs",
[
{
'method': 'POST',
'path': '/servers'
}
]),
policy.DocumentedRuleDefault(
NETWORK_ATTACH_EXTERNAL,
'is_admin:True',
@ -213,6 +223,16 @@ rules = [
'path': '/servers/{server_id}/action (rebuild)'
}
]),
policy.DocumentedRuleDefault(
SERVERS % 'rebuild:trusted_certs',
RULE_AOO,
"Rebuild a server with trusted image certificate IDs",
[
{
'method': 'POST',
'path': '/servers/{server_id}/action (rebuild)'
}
]),
policy.DocumentedRuleDefault(
SERVERS % 'create_image',
RULE_AOO,

69
nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-action-rebuild-resp.json.tpl

@ -0,0 +1,69 @@
{
"server": {
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"addresses": {
"private": [
{
"addr": "%(ip)s",
"version": 4
}
]
},
"adminPass": "%(password)s",
"created": "%(isotime)s",
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {
"hw:cpu_model": "SandyBridge",
"hw:mem_page_size": "2048",
"hw:cpu_policy": "dedicated"
},
"original_name": "m1.tiny.specs",
"ram": 512,
"swap": 0,
"vcpus": 1
},
"hostId": "%(hostid)s",
"id": "%(uuid)s",
"image": {
"id": "%(uuid)s",
"links": [
{
"href": "%(compute_endpoint)s/images/%(uuid)s",
"rel": "bookmark"
}
]
},
"links": [
{
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/servers/%(uuid)s",
"rel": "bookmark"
}
],
"locked": false,
"metadata": {
"meta_var": "meta_val"
},
"name": "%(name)s",
"key_name": "%(key_name)s",
"description": "%(description)s",
"progress": 0,
"OS-DCF:diskConfig": "AUTO",
"status": "ACTIVE",
"tags": [],
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi",
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
],
"updated": "%(isotime)s",
"user_id": "fake"
}
}

20
nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-action-rebuild.json.tpl

@ -0,0 +1,20 @@
{
"rebuild" : {
"accessIPv4" : "%(access_ip_v4)s",
"accessIPv6" : "%(access_ip_v6)s",
"OS-DCF:diskConfig": "AUTO",
"imageRef" : "%(uuid)s",
"name" : "%(name)s",
"key_name" : "%(key_name)s",
"description" : "%(description)s",
"adminPass" : "%(pass)s",
"metadata" : {
"meta_var" : "meta_val"
},
"user_data": "ZWNobyAiaGVsbG8gd29ybGQi",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
]
}
}

28
nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-create-req.json.tpl

@ -0,0 +1,28 @@
{
"server" : {
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"name" : "%(name)s",
"imageRef" : "%(image_id)s",
"flavorRef" : "6",
"availability_zone": "nova",
"OS-DCF:diskConfig": "AUTO",
"metadata" : {
"My Server Name" : "Apache1"
},
"security_groups": [
{
"name": "default"
}
],
"user_data" : "%(user_data)s",
"networks": "auto",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
]
},
"OS-SCH-HNT:scheduler_hints": {
"same_host": "%(uuid)s"
}
}

22
nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-create-resp.json.tpl

@ -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"
}
]
}
}

93
nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-get-resp.json.tpl

@ -0,0 +1,93 @@
{
"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",
"description": null,
"host_status": "UP",
"locked": false,
"tags": [],
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {
"hw:cpu_model": "SandyBridge",
"hw:cpu_policy": "dedicated",
"hw:mem_page_size": "2048"
},
"original_name": "m1.tiny.specs",
"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:host": "%(compute_host)s",
"OS-EXT-SRV-ATTR:hostname": "%(hostname)s",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "%(hypervisor_hostname)s",
"OS-EXT-SRV-ATTR:instance_name": "%(instance_name)s",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "%(reservation_id)s",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
"OS-EXT-SRV-ATTR:user_data": "%(user_data)s",
"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"
}
],
"status": "ACTIVE",
"tenant_id": "6f70656e737461636b20342065766572",
"updated": "%(isotime)s",
"user_id": "fake",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
]
}
}

8
nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-update-req.json.tpl

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

66
nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/server-update-resp.json.tpl

@ -0,0 +1,66 @@
{
"server": {
"OS-DCF:diskConfig": "AUTO",
"accessIPv4": "%(access_ip_v4)s",
"accessIPv6": "%(access_ip_v6)s",
"addresses": {
"private": [
{
"addr": "192.168.0.3",
"version": 4
}
]
},
"created": "%(isotime)s",
"description": null,
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {
"hw:cpu_model": "SandyBridge",
"hw:mem_page_size": "2048",
"hw:cpu_policy": "dedicated"
},
"original_name": "m1.tiny.specs",
"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"
}
]
},
"links": [
{
"href": "%(versioned_compute_endpoint)s/servers/%(id)s",
"rel": "self"
},
{
"href": "%(compute_endpoint)s/servers/%(id)s",
"rel": "bookmark"
}
],
"locked": false,
"metadata": {
"My Server Name": "Apache1"
},
"name": "new-server-test",
"progress": 0,
"status": "ACTIVE",
"tags": [],
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
],
"updated": "%(isotime)s",
"user_id": "fake"
}
}

95
nova/tests/functional/api_sample_tests/api_samples/servers/v2.63/servers-details-resp.json.tpl

@ -0,0 +1,95 @@
{
"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",
"description": null,
"host_status": "UP",
"locked": false,
"tags": [],
"flavor": {
"disk": 1,
"ephemeral": 0,
"extra_specs": {
"hw:cpu_model": "SandyBridge",
"hw:mem_page_size": "2048",
"hw:cpu_policy": "dedicated"
},
"original_name": "m1.tiny.specs",
"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",
"OS-DCF:diskConfig": "AUTO",
"OS-EXT-AZ:availability_zone": "nova",
"OS-EXT-SRV-ATTR:host": "%(compute_host)s",
"OS-EXT-SRV-ATTR:hostname": "%(hostname)s",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "%(hypervisor_hostname)s",
"OS-EXT-SRV-ATTR:instance_name": "%(instance_name)s",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "%(reservation_id)s",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
"OS-EXT-SRV-ATTR:user_data": "%(user_data)s",
"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"
}
],
"status": "ACTIVE",
"tenant_id": "6f70656e737461636b20342065766572",
"trusted_image_certificates": [
"0b5d2c72-12cc-4ba6-a8d7-3ff5cc1d8cb8",
"674736e3-f25c-405c-8362-bbf991e0ce0a"
],
"updated": "%(isotime)s",
"user_id": "fake"
}
]
}

72
nova/tests/functional/api_sample_tests/test_servers.py

@ -19,10 +19,13 @@ import time
import six
from nova.api.openstack import api_version_request as avr
import nova.conf
from nova.tests.functional.api_sample_tests import api_sample_base
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit.image import fake
CONF = nova.conf.CONF
class ServersSampleBase(api_sample_base.ApiSampleTestBaseV21):
microversion = None
@ -237,6 +240,75 @@ class ServersSampleJson252Test(ServersSampleJsonTest):
use_common_server_post = False
class ServersSampleJson263Test(ServersSampleBase):
microversion = '2.63'
scenarios = [('v2_63', {'api_major_version': 'v2.1'})]