Handle uuids in os-hypervisors API
There are quite a few changes here as this is not only handling uuids for the hypervisor id but it's also a refactor in several APIs for consistency. The main changes are detailed in the REST API Version History doc in this change, but to summarize the changes: * Hypervisor and service IDs are handled as the UUIDs for those resources; this is necessary for accurately working with these resources across multiple cells. * The 'servers' and 'search' routes are deprecated and folded into the index and detail methods as query parameters, validated using json schema. * The show method will also be able to return the list of servers hosted on the given hypervisor using the with_servers query parameter. * The marker used when paging over lists of hypervisors is the compute node UUID. * Using the hypervisor_hostname_pattern query parameter will not work with paging parameters. * API reference docs are updated for the detailed changes. * Functional and unit tests are provided for all changes. Part of blueprint service-hyper-uuid-in-api Change-Id: I828350c179df8bcfa4739910abeafaba2f96982b
This commit is contained in:
parent
2f7bf29d47
commit
622bfb2e95
@ -25,7 +25,7 @@ the ``policy.json`` file.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error response codes: unauthorized(401), forbidden(403)
|
||||
Error response codes: badRequest(400), unauthorized(401), forbidden(403)
|
||||
|
||||
Request
|
||||
-------
|
||||
@ -34,6 +34,9 @@ Request
|
||||
|
||||
- limit: hypervisor_limit
|
||||
- marker: hypervisor_marker
|
||||
- marker: hypervisor_marker_uuid
|
||||
- hypervisor_hostname_pattern: hypervisor_hostname_pattern_query
|
||||
- with_servers: hypervisor_with_servers_query
|
||||
|
||||
Response
|
||||
--------
|
||||
@ -43,15 +46,24 @@ Response
|
||||
- hypervisors: hypervisors
|
||||
- hypervisor_hostname: hypervisor_hostname
|
||||
- id: hypervisor_id_body
|
||||
- id: hypervisor_id_body_uuid
|
||||
- state: hypervisor_state
|
||||
- status: hypervisor_status
|
||||
- hypervisor_links: hypervisor_links
|
||||
- servers: hypervisor_servers
|
||||
- servers.uuid: hypervisor_servers_uuid
|
||||
- servers.name: hypervisor_servers_name
|
||||
|
||||
**Example List Hypervisors (v2.33): JSON response**
|
||||
|
||||
.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.33/hypervisors-list-resp.json
|
||||
:language: javascript
|
||||
|
||||
**Example List Hypervisors With Servers (v2.53): JSON response**
|
||||
|
||||
.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.53/hypervisors-with-servers-resp.json
|
||||
:language: javascript
|
||||
|
||||
List Hypervisors Details
|
||||
========================
|
||||
|
||||
@ -65,7 +77,7 @@ the ``policy.json`` file.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error response codes: unauthorized(401), forbidden(403)
|
||||
Error response codes: badRequest(400), unauthorized(401), forbidden(403)
|
||||
|
||||
Request
|
||||
-------
|
||||
@ -74,6 +86,9 @@ Request
|
||||
|
||||
- limit: hypervisor_limit
|
||||
- marker: hypervisor_marker
|
||||
- marker: hypervisor_marker_uuid
|
||||
- hypervisor_hostname_pattern: hypervisor_hostname_pattern_query
|
||||
- with_servers: hypervisor_with_servers_query
|
||||
|
||||
Response
|
||||
--------
|
||||
@ -93,14 +108,19 @@ Response
|
||||
- hypervisor_type: hypervisor_type_body
|
||||
- hypervisor_version: hypervisor_version
|
||||
- id: hypervisor_id_body
|
||||
- id: hypervisor_id_body_uuid
|
||||
- local_gb: local_gb
|
||||
- local_gb_used: local_gb_used
|
||||
- memory_mb: memory_mb
|
||||
- memory_mb_used: memory_mb_used
|
||||
- running_vms: running_vms
|
||||
- servers: hypervisor_servers
|
||||
- servers.uuid: hypervisor_servers_uuid
|
||||
- servers.name: hypervisor_servers_name
|
||||
- service: hypervisor_service
|
||||
- service.host: host_name_body
|
||||
- service.id: service_id_body
|
||||
- service.id: service_id_body_2_52
|
||||
- service.id: service_id_body_2_53
|
||||
- service.disable_reason: service_disable_reason
|
||||
- vcpus: hypervisor_vcpus
|
||||
- vcpus_used: hypervisor_vcpus_used
|
||||
@ -111,6 +131,11 @@ Response
|
||||
.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.33/hypervisors-detail-resp.json
|
||||
:language: javascript
|
||||
|
||||
**Example List Hypervisors Details (v2.53): JSON response**
|
||||
|
||||
.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.53/hypervisors-detail-resp.json
|
||||
:language: javascript
|
||||
|
||||
Show Hypervisor Statistics
|
||||
==========================
|
||||
|
||||
@ -163,7 +188,7 @@ the ``policy.json`` file.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404)
|
||||
Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404)
|
||||
|
||||
Request
|
||||
-------
|
||||
@ -171,6 +196,8 @@ Request
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- hypervisor_id: hypervisor_id
|
||||
- hypervisor_id: hypervisor_id_uuid
|
||||
- with_servers: hypervisor_with_servers_query
|
||||
|
||||
Response
|
||||
--------
|
||||
@ -190,14 +217,19 @@ Response
|
||||
- hypervisor_type: hypervisor_type_body
|
||||
- hypervisor_version: hypervisor_version
|
||||
- id: hypervisor_id_body
|
||||
- id: hypervisor_id_body_uuid
|
||||
- local_gb: local_gb
|
||||
- local_gb_used: local_gb_used
|
||||
- memory_mb: memory_mb
|
||||
- memory_mb_used: memory_mb_used
|
||||
- running_vms: running_vms
|
||||
- servers: hypervisor_servers
|
||||
- servers.uuid: hypervisor_servers_uuid
|
||||
- servers.name: hypervisor_servers_name
|
||||
- service: hypervisor_service
|
||||
- service.host: host_name_body
|
||||
- service.id: service_id_body
|
||||
- service.id: service_id_body_2_52
|
||||
- service.id: service_id_body_2_53
|
||||
- service.disable_reason: service_disable_reason
|
||||
- vcpus: hypervisor_vcpus
|
||||
- vcpus_used: hypervisor_vcpus_used
|
||||
@ -207,6 +239,11 @@ Response
|
||||
.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.28/hypervisors-show-resp.json
|
||||
:language: javascript
|
||||
|
||||
**Example Show Hypervisor Details With Servers (v2.53): JSON response**
|
||||
|
||||
.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.53/hypervisors-show-with-servers-resp.json
|
||||
:language: javascript
|
||||
|
||||
Show Hypervisor Uptime
|
||||
======================
|
||||
|
||||
@ -220,7 +257,7 @@ the ``policy.json`` file.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
Error response codes: unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501)
|
||||
Error response codes: badRequest(400), unauthorized(401), forbidden(403), itemNotFound(404), NotImplemented(501)
|
||||
|
||||
Request
|
||||
-------
|
||||
@ -228,6 +265,7 @@ Request
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- hypervisor_id: hypervisor_id
|
||||
- hypervisor_id: hypervisor_id_uuid
|
||||
|
||||
Response
|
||||
--------
|
||||
@ -237,6 +275,7 @@ Response
|
||||
- hypervisor: hypervisor
|
||||
- hypervisor_hostname: hypervisor_hostname
|
||||
- id: hypervisor_id_body
|
||||
- id: hypervisor_id_body_uuid
|
||||
- state: hypervisor_state
|
||||
- status: hypervisor_status
|
||||
- uptime: uptime
|
||||
@ -246,13 +285,23 @@ Response
|
||||
.. literalinclude:: ../../doc/api_samples/os-hypervisors/hypervisors-uptime-resp.json
|
||||
:language: javascript
|
||||
|
||||
**Example Show Hypervisor Uptime (v2.53): JSON response**
|
||||
|
||||
.. literalinclude:: ../../doc/api_samples/os-hypervisors/v2.53/hypervisors-uptime-resp.json
|
||||
:language: javascript
|
||||
|
||||
Search Hypervisor
|
||||
=================
|
||||
|
||||
.. rest_method:: GET /os-hypervisors/{hypervisor_hostname_pattern}/search
|
||||
max_version: 2.52
|
||||
|
||||
Search hypervisor by a given hypervisor host name or portion of it.
|
||||
|
||||
.. warning:: This API is deprecated starting with microversion 2.53. Use
|
||||
`List Hypervisors`_ with the ``hypervisor_hostname_pattern`` query
|
||||
parameter with microversion 2.53 and later.
|
||||
|
||||
Policy defaults enable only users with the administrative role to perform
|
||||
this operation. Cloud providers can change these permissions through
|
||||
the ``policy.json`` file.
|
||||
@ -275,7 +324,7 @@ Response
|
||||
|
||||
- hypervisors: hypervisors
|
||||
- hypervisor_hostname: hypervisor_hostname
|
||||
- id: hypervisor_id_body
|
||||
- id: hypervisor_id_body_no_version
|
||||
- state: hypervisor_state
|
||||
- status: hypervisor_status
|
||||
|
||||
@ -288,10 +337,15 @@ List Hypervisor Servers
|
||||
=======================
|
||||
|
||||
.. rest_method:: GET /os-hypervisors/{hypervisor_hostname_pattern}/servers
|
||||
max_version: 2.52
|
||||
|
||||
List all servers belong to each hypervisor whose host name is matching
|
||||
a given hypervisor host name or portion of it.
|
||||
|
||||
.. warning:: This API is deprecated starting with microversion 2.53. Use
|
||||
`List Hypervisors`_ with the ``hypervisor_hostname_pattern`` and
|
||||
``with_servers`` query parameters with microversion 2.53 and later.
|
||||
|
||||
Policy defaults enable only users with the administrative role to perform
|
||||
this operation. Cloud providers can change these permissions through
|
||||
the ``policy.json`` file.
|
||||
@ -314,7 +368,7 @@ Response
|
||||
|
||||
- hypervisors: hypervisors
|
||||
- hypervisor_hostname: hypervisor_hostname
|
||||
- id: hypervisor_id_body
|
||||
- id: hypervisor_id_body_no_version
|
||||
- state: hypervisor_state
|
||||
- status: hypervisor_status
|
||||
- servers: servers
|
||||
|
@ -182,6 +182,14 @@ hypervisor_id:
|
||||
in: path
|
||||
required: true
|
||||
type: integer
|
||||
max_version: 2.52
|
||||
hypervisor_id_uuid:
|
||||
description: |
|
||||
The ID of the hypervisor as a UUID.
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
min_version: 2.53
|
||||
image_id:
|
||||
description: |
|
||||
The UUID of the image.
|
||||
@ -565,6 +573,19 @@ hostname_query_server:
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
hypervisor_hostname_pattern_query:
|
||||
description: |
|
||||
The hypervisor host name or a portion of it. The hypervisor hosts are
|
||||
selected with the host name matching this pattern.
|
||||
|
||||
.. note:: ``limit`` and ``marker`` query parameters for paging are
|
||||
not supported when listing hypervisors using a hostname pattern.
|
||||
Also, ``links`` will not be returned in the response when using this
|
||||
query parameter.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
min_version: 2.53
|
||||
hypervisor_limit:
|
||||
description: |
|
||||
Requests a page size of items. Returns a number of items up to a limit value.
|
||||
@ -584,12 +605,29 @@ hypervisor_marker:
|
||||
required: false
|
||||
type: integer
|
||||
min_version: 2.33
|
||||
max_version: 2.52
|
||||
hypervisor_marker_uuid:
|
||||
description: |
|
||||
The ID of the last-seen item as a UUID. Use the ``limit`` parameter to make
|
||||
an initial limited request and use the ID of the last-seen item from the
|
||||
response as the ``marker`` parameter value in a subsequent limited request.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
min_version: 2.53
|
||||
hypervisor_query:
|
||||
description: |
|
||||
Filters the response by a hypervisor type.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
hypervisor_with_servers_query:
|
||||
description: |
|
||||
Include all servers which belong to each hypervisor in the response output.
|
||||
in: query
|
||||
required: false
|
||||
type: boolean
|
||||
min_version: 2.53
|
||||
image_name_query:
|
||||
description: |
|
||||
Filters the response by an image name, as a string.
|
||||
@ -2965,6 +3003,20 @@ hypervisor_id_body:
|
||||
in: body
|
||||
required: true
|
||||
type: integer
|
||||
max_version: 2.52
|
||||
hypervisor_id_body_no_version:
|
||||
description: |
|
||||
The id of the hypervisor.
|
||||
in: body
|
||||
required: true
|
||||
type: integer
|
||||
hypervisor_id_body_uuid:
|
||||
description: |
|
||||
The id of the hypervisor as a UUID.
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
min_version: 2.53
|
||||
hypervisor_links:
|
||||
description: |
|
||||
Links to the hypervisors resource. See `API Guide / Links and
|
||||
@ -2982,6 +3034,27 @@ hypervisor_os_diagnostics:
|
||||
type: string
|
||||
required: true
|
||||
min_version: 2.48
|
||||
hypervisor_servers:
|
||||
description: |
|
||||
A list of ``server`` objects.
|
||||
in: body
|
||||
required: false
|
||||
type: array
|
||||
min_version: 2.53
|
||||
hypervisor_servers_name:
|
||||
description: |
|
||||
The server name.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
min_version: 2.53
|
||||
hypervisor_servers_uuid:
|
||||
description: |
|
||||
The server ID.
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
min_version: 2.53
|
||||
hypervisor_service:
|
||||
description: |
|
||||
The hypervisor service object.
|
||||
|
@ -0,0 +1,49 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"current_workload": 0,
|
||||
"status": "enabled",
|
||||
"state": "up",
|
||||
"disk_available_least": 0,
|
||||
"host_ip": "1.1.1.1",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": "1bb62a04-c576-402c-8147-9e89757a09e3",
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "host1",
|
||||
"id": "62f62f6e-a713-4cbe-87d3-3ecf8a1e0f8d",
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
],
|
||||
"hypervisors_links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors/detail?limit=1&marker=1bb62a04-c576-402c-8147-9e89757a09e3",
|
||||
"rel": "next"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"current_workload": 0,
|
||||
"status": "enabled",
|
||||
"servers": [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}
|
||||
],
|
||||
"state": "up",
|
||||
"disk_available_least": 0,
|
||||
"host_ip": "1.1.1.1",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d",
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "host1",
|
||||
"id": "5d343e1d-938e-4284-b98b-6a2b5406ba76",
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "1bb62a04-c576-402c-8147-9e89757a09e3",
|
||||
"state": "up",
|
||||
"status": "enabled"
|
||||
}
|
||||
],
|
||||
"hypervisors_links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors?limit=1&marker=1bb62a04-c576-402c-8147-9e89757a09e3",
|
||||
"rel": "next"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d",
|
||||
"state": "up",
|
||||
"status": "enabled"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"hypervisor": {
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"current_workload": 0,
|
||||
"disk_available_least": 0,
|
||||
"host_ip": "1.1.1.1",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d",
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "043b3cacf6f34c90a7245151fc8ebcda",
|
||||
"id": "5d343e1d-938e-4284-b98b-6a2b5406ba76",
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
{
|
||||
"hypervisor": {
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"servers": [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}
|
||||
],
|
||||
"current_workload": 0,
|
||||
"disk_available_least": 0,
|
||||
"host_ip": "1.1.1.1",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d",
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "043b3cacf6f34c90a7245151fc8ebcda",
|
||||
"id": "5d343e1d-938e-4284-b98b-6a2b5406ba76",
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"hypervisor_statistics": {
|
||||
"count": 1,
|
||||
"current_workload": 0,
|
||||
"disk_available_least": 0,
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"hypervisor": {
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d",
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"uptime": " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14"
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d",
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"servers": [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "b1e43b5f-eec1-44e0-9f10-7b4945c0226d",
|
||||
"state": "up",
|
||||
"status": "enabled"
|
||||
}
|
||||
]
|
||||
}
|
@ -124,9 +124,10 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
non-admins can see instance action event details except for the
|
||||
traceback field.
|
||||
* 2.52 - Adds support for applying tags when creating a server.
|
||||
* 2.53 - Service database ids are hidden. The os-services API now returns
|
||||
a uuid in the id field, and takes a uuid in
|
||||
DELETE /services/{service_uuid}.
|
||||
* 2.53 - Service and compute node (hypervisor) database ids are hidden.
|
||||
The os-services and os-hypervisors APIs now return a uuid in the
|
||||
id field, and takes a uuid in requests. PUT and GET requests
|
||||
and responses are also changed.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
|
@ -17,21 +17,29 @@
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import uuidutils
|
||||
import webob.exc
|
||||
|
||||
from nova.api.openstack import api_version_request
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack.compute.schemas import hypervisors as hyper_schema
|
||||
from nova.api.openstack.compute.views import hypervisors as hyper_view
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api import validation
|
||||
from nova.cells import utils as cells_utils
|
||||
from nova import compute
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.policies import hypervisors as hv_policies
|
||||
from nova import servicegroup
|
||||
from nova import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
UUID_FOR_ID_MIN_VERSION = '2.53'
|
||||
|
||||
|
||||
class HypervisorsController(wsgi.Controller):
|
||||
"""The Hypervisors API controller for the OpenStack API."""
|
||||
@ -46,15 +54,18 @@ class HypervisorsController(wsgi.Controller):
|
||||
def _view_hypervisor(self, hypervisor, service, detail, req, servers=None,
|
||||
**kwargs):
|
||||
alive = self.servicegroup_api.service_is_up(service)
|
||||
# The 2.53 microversion returns the compute node uuid rather than id.
|
||||
uuid_for_id = api_version_request.is_supported(
|
||||
req, min_version=UUID_FOR_ID_MIN_VERSION)
|
||||
hyp_dict = {
|
||||
'id': hypervisor.id,
|
||||
'id': hypervisor.uuid if uuid_for_id else hypervisor.id,
|
||||
'hypervisor_hostname': hypervisor.hypervisor_hostname,
|
||||
'state': 'up' if alive else 'down',
|
||||
'status': ('disabled' if service.disabled
|
||||
else 'enabled'),
|
||||
}
|
||||
|
||||
if detail and not servers:
|
||||
if detail:
|
||||
for field in ('vcpus', 'memory_mb', 'local_gb', 'vcpus_used',
|
||||
'memory_mb_used', 'local_gb_used',
|
||||
'hypervisor_type', 'hypervisor_version',
|
||||
@ -62,8 +73,9 @@ class HypervisorsController(wsgi.Controller):
|
||||
'running_vms', 'disk_available_least', 'host_ip'):
|
||||
hyp_dict[field] = getattr(hypervisor, field)
|
||||
|
||||
service_id = service.uuid if uuid_for_id else service.id
|
||||
hyp_dict['service'] = {
|
||||
'id': service.id,
|
||||
'id': service_id,
|
||||
'host': hypervisor.host,
|
||||
'disabled_reason': service.disabled_reason,
|
||||
}
|
||||
@ -108,21 +120,58 @@ class HypervisorsController(wsgi.Controller):
|
||||
context = req.environ['nova.context']
|
||||
context.can(hv_policies.BASE_POLICY_NAME)
|
||||
|
||||
try:
|
||||
compute_nodes = self.host_api.compute_node_get_all(
|
||||
context, limit=limit, marker=marker)
|
||||
except exception.MarkerNotFound:
|
||||
msg = _('marker [%s] not found') % marker
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
# The 2.53 microversion moves the search and servers routes into
|
||||
# GET /os-hypervisors and GET /os-hypervisors/detail with query
|
||||
# parameters.
|
||||
if api_version_request.is_supported(
|
||||
req, min_version=UUID_FOR_ID_MIN_VERSION):
|
||||
hypervisor_match = req.GET.get('hypervisor_hostname_pattern')
|
||||
with_servers = strutils.bool_from_string(
|
||||
req.GET.get('with_servers', False), strict=True)
|
||||
else:
|
||||
hypervisor_match = None
|
||||
with_servers = False
|
||||
|
||||
if hypervisor_match is not None:
|
||||
# We have to check for 'limit' in the request itself because
|
||||
# the limit passed in is CONF.api.max_limit by default.
|
||||
if 'limit' in req.GET or marker:
|
||||
# Paging with hostname pattern isn't supported.
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
_('Paging over hypervisors with the '
|
||||
'hypervisor_hostname_pattern query parameter is not '
|
||||
'supported.'))
|
||||
|
||||
# Explicitly do not try to generate links when querying with the
|
||||
# hostname pattern since the request in the link would fail the
|
||||
# check above.
|
||||
links = False
|
||||
|
||||
# Get all compute nodes with a hypervisor_hostname that matches
|
||||
# the given pattern. If none are found then it's a 404 error.
|
||||
compute_nodes = self._get_compute_nodes_by_name_pattern(
|
||||
context, hypervisor_match)
|
||||
else:
|
||||
# Get all compute nodes.
|
||||
try:
|
||||
compute_nodes = self.host_api.compute_node_get_all(
|
||||
context, limit=limit, marker=marker)
|
||||
except exception.MarkerNotFound:
|
||||
msg = _('marker [%s] not found') % marker
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
hypervisors_list = []
|
||||
for hyp in compute_nodes:
|
||||
try:
|
||||
instances = None
|
||||
if with_servers:
|
||||
instances = self.host_api.instance_get_all_by_host(
|
||||
context, hyp.host)
|
||||
service = self.host_api.service_get_by_compute_host(
|
||||
context, hyp.host)
|
||||
hypervisors_list.append(
|
||||
self._view_hypervisor(
|
||||
hyp, service, detail, req))
|
||||
hyp, service, detail, req, servers=instances))
|
||||
except (exception.ComputeHostNotFound,
|
||||
exception.HostMappingNotFound):
|
||||
# The compute service could be deleted which doesn't delete
|
||||
@ -140,7 +189,21 @@ class HypervisorsController(wsgi.Controller):
|
||||
hypervisors_dict['hypervisors_links'] = hypervisors_links
|
||||
return hypervisors_dict
|
||||
|
||||
@wsgi.Controller.api_version("2.33") # noqa
|
||||
@wsgi.Controller.api_version(UUID_FOR_ID_MIN_VERSION)
|
||||
@validation.query_schema(hyper_schema.list_query_schema_v253,
|
||||
UUID_FOR_ID_MIN_VERSION)
|
||||
@extensions.expected_errors((400, 404))
|
||||
def index(self, req):
|
||||
"""Starting with the 2.53 microversion, the id field in the response
|
||||
is the compute_nodes.uuid value. Also, the search and servers routes
|
||||
are superseded and replaced with query parameters for listing
|
||||
hypervisors by a hostname pattern and whether or not to include
|
||||
hosted servers in the response.
|
||||
"""
|
||||
limit, marker = common.get_limit_and_marker(req)
|
||||
return self._index(req, limit=limit, marker=marker, links=True)
|
||||
|
||||
@wsgi.Controller.api_version("2.33", "2.52") # noqa
|
||||
@extensions.expected_errors((400))
|
||||
def index(self, req):
|
||||
limit, marker = common.get_limit_and_marker(req)
|
||||
@ -155,7 +218,21 @@ class HypervisorsController(wsgi.Controller):
|
||||
return self._get_hypervisors(req, detail=False, limit=limit,
|
||||
marker=marker, links=links)
|
||||
|
||||
@wsgi.Controller.api_version("2.33") # noqa
|
||||
@wsgi.Controller.api_version(UUID_FOR_ID_MIN_VERSION)
|
||||
@validation.query_schema(hyper_schema.list_query_schema_v253,
|
||||
UUID_FOR_ID_MIN_VERSION)
|
||||
@extensions.expected_errors((400, 404))
|
||||
def detail(self, req):
|
||||
"""Starting with the 2.53 microversion, the id field in the response
|
||||
is the compute_nodes.uuid value. Also, the search and servers routes
|
||||
are superseded and replaced with query parameters for listing
|
||||
hypervisors by a hostname pattern and whether or not to include
|
||||
hosted servers in the response.
|
||||
"""
|
||||
limit, marker = common.get_limit_and_marker(req)
|
||||
return self._detail(req, limit=limit, marker=marker, links=True)
|
||||
|
||||
@wsgi.Controller.api_version("2.33", "2.52") # noqa
|
||||
@extensions.expected_errors((400))
|
||||
def detail(self, req):
|
||||
limit, marker = common.get_limit_and_marker(req)
|
||||
@ -170,12 +247,69 @@ class HypervisorsController(wsgi.Controller):
|
||||
return self._get_hypervisors(req, detail=True, limit=limit,
|
||||
marker=marker, links=links)
|
||||
|
||||
@staticmethod
|
||||
def _validate_id(req, hypervisor_id):
|
||||
"""Validates that the id is a uuid for microversions that require it.
|
||||
|
||||
:param req: The HTTP request object which contains the requested
|
||||
microversion information.
|
||||
:param hypervisor_id: The provided hypervisor id.
|
||||
:raises: webob.exc.HTTPBadRequest if the requested microversion is
|
||||
greater than or equal to 2.53 and the id is not a uuid.
|
||||
:raises: webob.exc.HTTPNotFound if the requested microversion is
|
||||
less than 2.53 and the id is not an integer.
|
||||
"""
|
||||
expect_uuid = api_version_request.is_supported(
|
||||
req, min_version=UUID_FOR_ID_MIN_VERSION)
|
||||
if expect_uuid:
|
||||
if not uuidutils.is_uuid_like(hypervisor_id):
|
||||
msg = _('Invalid uuid %s') % hypervisor_id
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
else:
|
||||
# This API is supported for cells v1 and as such the id can be
|
||||
# a cell v1 delimited string, so we have to parse it first.
|
||||
if cells_utils.CELL_ITEM_SEP in str(hypervisor_id):
|
||||
hypervisor_id = cells_utils.split_cell_and_item(
|
||||
hypervisor_id)[1]
|
||||
try:
|
||||
utils.validate_integer(hypervisor_id, 'id')
|
||||
except exception.InvalidInput:
|
||||
msg = (_("Hypervisor with ID '%s' could not be found.") %
|
||||
hypervisor_id)
|
||||
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
@wsgi.Controller.api_version(UUID_FOR_ID_MIN_VERSION)
|
||||
@validation.query_schema(hyper_schema.show_query_schema_v253,
|
||||
UUID_FOR_ID_MIN_VERSION)
|
||||
@extensions.expected_errors((400, 404))
|
||||
def show(self, req, id):
|
||||
"""The 2.53 microversion requires that the id is a uuid and as a result
|
||||
it can also return a 400 response if an invalid uuid is passed.
|
||||
|
||||
The 2.53 microversion also supports the with_servers query parameter
|
||||
to include a list of servers on the given hypervisor if requested.
|
||||
"""
|
||||
with_servers = strutils.bool_from_string(
|
||||
req.GET.get('with_servers', False), strict=True)
|
||||
return self._show(req, id, with_servers)
|
||||
|
||||
@wsgi.Controller.api_version("2.1", "2.52") # noqa F811
|
||||
@extensions.expected_errors(404)
|
||||
def show(self, req, id):
|
||||
return self._show(req, id)
|
||||
|
||||
def _show(self, req, id, with_servers=False):
|
||||
context = req.environ['nova.context']
|
||||
context.can(hv_policies.BASE_POLICY_NAME)
|
||||
|
||||
self._validate_id(req, id)
|
||||
|
||||
try:
|
||||
hyp = self.host_api.compute_node_get(context, id)
|
||||
instances = None
|
||||
if with_servers:
|
||||
instances = self.host_api.instance_get_all_by_host(
|
||||
context, hyp.host)
|
||||
service = self.host_api.service_get_by_compute_host(
|
||||
context, hyp.host)
|
||||
except (ValueError, exception.ComputeHostNotFound,
|
||||
@ -183,12 +317,15 @@ class HypervisorsController(wsgi.Controller):
|
||||
msg = _("Hypervisor with ID '%s' could not be found.") % id
|
||||
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||
return dict(hypervisor=self._view_hypervisor(
|
||||
hyp, service, True, req))
|
||||
hyp, service, True, req, instances))
|
||||
|
||||
@extensions.expected_errors((400, 404, 501))
|
||||
def uptime(self, req, id):
|
||||
context = req.environ['nova.context']
|
||||
context.can(hv_policies.BASE_POLICY_NAME)
|
||||
|
||||
self._validate_id(req, id)
|
||||
|
||||
try:
|
||||
hyp = self.host_api.compute_node_get(context, id)
|
||||
except (ValueError, exception.ComputeHostNotFound):
|
||||
@ -214,8 +351,14 @@ class HypervisorsController(wsgi.Controller):
|
||||
return dict(hypervisor=self._view_hypervisor(hyp, service, False, req,
|
||||
uptime=uptime))
|
||||
|
||||
@wsgi.Controller.api_version('2.1', '2.52')
|
||||
@extensions.expected_errors(404)
|
||||
def search(self, req, id):
|
||||
"""Prior to microversion 2.53 you could search for hypervisors by a
|
||||
hostname pattern on a dedicated route. Starting with 2.53, searching
|
||||
by a hostname pattern is a query parameter in the GET /os-hypervisors
|
||||
index and detail methods.
|
||||
"""
|
||||
context = req.environ['nova.context']
|
||||
context.can(hv_policies.BASE_POLICY_NAME)
|
||||
hypervisors = self._get_compute_nodes_by_name_pattern(context, id)
|
||||
@ -231,8 +374,15 @@ class HypervisorsController(wsgi.Controller):
|
||||
msg = _("No hypervisor matching '%s' could be found.") % id
|
||||
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
@wsgi.Controller.api_version('2.1', '2.52')
|
||||
@extensions.expected_errors(404)
|
||||
def servers(self, req, id):
|
||||
"""Prior to microversion 2.53 you could search for hypervisors by a
|
||||
hostname pattern and include servers on those hosts in the response on
|
||||
a dedicated route. Starting with 2.53, searching by a hostname pattern
|
||||
and including hosted servers is a query parameter in the
|
||||
GET /os-hypervisors index and detail methods.
|
||||
"""
|
||||
context = req.environ['nova.context']
|
||||
context.can(hv_policies.BASE_POLICY_NAME)
|
||||
compute_nodes = self._get_compute_nodes_by_name_pattern(context, id)
|
||||
|
@ -657,3 +657,30 @@ user documentation.
|
||||
|
||||
* ``PUT /os-services/{service_uuid}`` will now return a full service resource
|
||||
representation like in a ``GET`` response
|
||||
|
||||
**os-hypervisors**
|
||||
|
||||
Hypervisors are now identified by uuid instead of database id to ensure
|
||||
uniqueness across cells. This microversion brings the following changes:
|
||||
|
||||
* ``GET /os-hypervisors/{hypervisor_hostname_pattern}/search`` is deprecated
|
||||
and replaced with the ``hypervisor_hostname_pattern`` query parameter on
|
||||
the ``GET /os-hypervisors`` and ``GET /os-hypervisors/detail`` APIs.
|
||||
Paging with ``hypervisor_hostname_pattern`` is not supported.
|
||||
* ``GET /os-hypervisors/{hypervisor_hostname_pattern}/servers`` is deprecated
|
||||
and replaced with the ``with_servers`` query parameter on the
|
||||
``GET /os-hypervisors`` and ``GET /os-hypervisors/detail`` APIs.
|
||||
* ``GET /os-hypervisors/{hypervisor_id}`` supports the ``with_servers`` query
|
||||
parameter to include hosted server details in the response.
|
||||
* ``GET /os-hypervisors/{hypervisor_id}`` and
|
||||
``GET /os-hypervisors/{hypervisor_id}/uptime`` APIs now take a uuid value
|
||||
for the ``{hypervisor_id}`` path parameter.
|
||||
* The ``GET /os-hypervisors`` and ``GET /os-hypervisors/detail`` APIs will
|
||||
now use a uuid marker for paging across cells.
|
||||
* The following APIs will now return a uuid value for the hypervisor id and
|
||||
optionally service id fields in the response:
|
||||
|
||||
* ``GET /os-hypervisors``
|
||||
* ``GET /os-hypervisors/detail``
|
||||
* ``GET /os-hypervisors/{hypervisor_id}``
|
||||
* ``GET /os-hypervisors/{hypervisor_id}/uptime``
|
||||
|
43
nova/api/openstack/compute/schemas/hypervisors.py
Normal file
43
nova/api/openstack/compute/schemas/hypervisors.py
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright 2017 Huawei Technologies Co.,LTD.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova.api.validation import parameter_types
|
||||
|
||||
|
||||
list_query_schema_v253 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
# The 2.33 microversion added support for paging by limit and marker.
|
||||
'limit': parameter_types.single_param(
|
||||
parameter_types.non_negative_integer),
|
||||
'marker': parameter_types.single_param({'type': 'string'}),
|
||||
# The 2.53 microversion adds support for filtering by hostname pattern
|
||||
# and requesting hosted servers in the GET /os-hypervisors and
|
||||
# GET /os-hypervisors/detail response.
|
||||
'hypervisor_hostname_pattern': parameter_types.single_param(
|
||||
parameter_types.hostname),
|
||||
'with_servers': parameter_types.single_param(
|
||||
parameter_types.boolean)
|
||||
},
|
||||
'additionalProperties': False
|
||||
}
|
||||
|
||||
show_query_schema_v253 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'with_servers': parameter_types.single_param(
|
||||
parameter_types.boolean)
|
||||
},
|
||||
'additionalProperties': False
|
||||
}
|
@ -34,7 +34,7 @@ PATH_CELL_SEP = '!'
|
||||
# meaningful PATH_CELL_SEP in an invalid way will need to suffice.
|
||||
BLOCK_SYNC_FLAG = '!!'
|
||||
# Separator used between cell name and item
|
||||
_CELL_ITEM_SEP = '@'
|
||||
CELL_ITEM_SEP = '@'
|
||||
|
||||
CONF = nova.conf.CONF
|
||||
|
||||
@ -192,12 +192,12 @@ def cell_with_item(cell_name, item):
|
||||
"""Turn cell_name and item into <cell_name>@<item>."""
|
||||
if cell_name is None:
|
||||
return item
|
||||
return cell_name + _CELL_ITEM_SEP + str(item)
|
||||
return cell_name + CELL_ITEM_SEP + str(item)
|
||||
|
||||
|
||||
def split_cell_and_item(cell_and_item):
|
||||
"""Split a combined cell@item and return them."""
|
||||
result = cell_and_item.rsplit(_CELL_ITEM_SEP, 1)
|
||||
result = cell_and_item.rsplit(CELL_ITEM_SEP, 1)
|
||||
if len(result) == 1:
|
||||
return (None, cell_and_item)
|
||||
else:
|
||||
|
@ -0,0 +1,49 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"current_workload": 0,
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"disk_available_least": 0,
|
||||
"host_ip": "%(ip)s",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": "%(hypervisor_id)s",
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "%(host_name)s",
|
||||
"id": "%(service_id)s",
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
],
|
||||
"hypervisors_links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors/detail?limit=1&marker=%(hypervisor_id)s",
|
||||
"rel": "next"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"current_workload": 0,
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"servers": [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}
|
||||
],
|
||||
"disk_available_least": 0,
|
||||
"host_ip": "%(ip)s",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": "%(hypervisor_id)s",
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "%(host_name)s",
|
||||
"id": "%(service_id)s",
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "%(hypervisor_id)s",
|
||||
"state": "up",
|
||||
"status": "enabled"
|
||||
}
|
||||
],
|
||||
"hypervisors_links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2.1/6f70656e737461636b20342065766572/hypervisors?limit=1&marker=%(hypervisor_id)s",
|
||||
"rel": "next"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "%(hypervisor_id)s",
|
||||
"state": "up",
|
||||
"status": "enabled"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"hypervisor": {
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"current_workload": 0,
|
||||
"disk_available_least": 0,
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"host_ip": "%(ip)s",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": "%(hypervisor_id)s",
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "%(host_name)s",
|
||||
"id": "%(service_id)s",
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
{
|
||||
"hypervisor": {
|
||||
"cpu_info": {
|
||||
"arch": "x86_64",
|
||||
"model": "Nehalem",
|
||||
"vendor": "Intel",
|
||||
"features": [
|
||||
"pge",
|
||||
"clflush"
|
||||
],
|
||||
"topology": {
|
||||
"cores": 1,
|
||||
"threads": 1,
|
||||
"sockets": 4
|
||||
}
|
||||
},
|
||||
"current_workload": 0,
|
||||
"disk_available_least": 0,
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"servers": [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}
|
||||
],
|
||||
"host_ip": "%(ip)s",
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"hypervisor_type": "fake",
|
||||
"hypervisor_version": 1000,
|
||||
"id": "%(hypervisor_id)s",
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"service": {
|
||||
"host": "%(host_name)s",
|
||||
"id": "%(service_id)s",
|
||||
"disabled_reason": null
|
||||
},
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"hypervisor_statistics": {
|
||||
"count": 1,
|
||||
"current_workload": 0,
|
||||
"disk_available_least": 0,
|
||||
"free_disk_gb": 1028,
|
||||
"free_ram_mb": 7680,
|
||||
"local_gb": 1028,
|
||||
"local_gb_used": 0,
|
||||
"memory_mb": 8192,
|
||||
"memory_mb_used": 512,
|
||||
"running_vms": 0,
|
||||
"vcpus": 1,
|
||||
"vcpus_used": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"hypervisor": {
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "%(hypervisor_id)s",
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"uptime": " 08:32:11 up 93 days, 18:25, 12 users, load average: 0.20, 0.12, 0.14"
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "%(hypervisor_id)s",
|
||||
"state": "up",
|
||||
"status": "enabled",
|
||||
"servers": [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"hypervisors": [
|
||||
{
|
||||
"hypervisor_hostname": "fake-mini",
|
||||
"id": "%(hypervisor_id)s",
|
||||
"state": "up",
|
||||
"status": "enabled"
|
||||
}
|
||||
]
|
||||
}
|
@ -169,3 +169,147 @@ class HypervisorsSampleJson233Tests(api_sample_base.ApiSampleTestBaseV21):
|
||||
}
|
||||
response = self._do_get('os-hypervisors/detail?limit=1&marker=1')
|
||||
self._verify_response('hypervisors-detail-resp', subs, response, 200)
|
||||
|
||||
|
||||
class HypervisorsSampleJson253Tests(HypervisorsSampleJson228Tests):
|
||||
microversion = '2.53'
|
||||
scenarios = [('v2_53', {'api_major_version': 'v2.1'})]
|
||||
|
||||
def setUp(self):
|
||||
super(HypervisorsSampleJson253Tests, self).setUp()
|
||||
self.compute_node_1 = self.compute.service_ref.compute_node
|
||||
|
||||
def generalize_subs(self, subs, vanilla_regexes):
|
||||
"""Give the test a chance to modify subs after the server response
|
||||
was verified, and before the on-disk doc/api_samples file is checked.
|
||||
"""
|
||||
# When comparing the template to the sample we just care that the
|
||||
# hypervisor id and service id are UUIDs.
|
||||
subs['hypervisor_id'] = vanilla_regexes['uuid']
|
||||
subs['service_id'] = vanilla_regexes['uuid']
|
||||
return subs
|
||||
|
||||
def test_hypervisors_list(self):
|
||||
# Start another compute service to get a 2nd compute for paging tests.
|
||||
compute_node_2 = self.start_service(
|
||||
'compute', host='host2').service_ref.compute_node
|
||||
marker = self.compute_node_1.uuid
|
||||
response = self._do_get('os-hypervisors?limit=1&marker=%s' % marker)
|
||||
subs = {'hypervisor_id': compute_node_2.uuid}
|
||||
self._verify_response('hypervisors-list-resp', subs, response, 200)
|
||||
|
||||
def test_hypervisors_detail(self):
|
||||
# Start another compute service to get a 2nd compute for paging tests.
|
||||
service_2 = self.start_service('compute', host='host2').service_ref
|
||||
compute_node_2 = service_2.compute_node
|
||||
marker = self.compute_node_1.uuid
|
||||
subs = {
|
||||
'hypervisor_id': compute_node_2.uuid,
|
||||
'service_id': service_2.uuid
|
||||
}
|
||||
response = self._do_get('os-hypervisors/detail?limit=1&marker=%s' %
|
||||
marker)
|
||||
self._verify_response('hypervisors-detail-resp', subs, response, 200)
|
||||
|
||||
@mock.patch("nova.compute.api.HostAPI.instance_get_all_by_host")
|
||||
def test_hypervisors_detail_with_servers(self, instance_get_all_by_host):
|
||||
"""List hypervisors with details and with hosted servers."""
|
||||
instances = [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}]
|
||||
instance_get_all_by_host.return_value = instances
|
||||
response = self._do_get('os-hypervisors/detail?with_servers=1')
|
||||
subs = {
|
||||
'hypervisor_id': self.compute_node_1.uuid,
|
||||
'service_id': self.compute.service_ref.uuid,
|
||||
}
|
||||
self._verify_response('hypervisors-detail-with-servers-resp',
|
||||
subs, response, 200)
|
||||
|
||||
def test_hypervisors_search(self):
|
||||
"""The search route is deprecated in 2.53 and is now a query parameter
|
||||
on the GET /os-hypervisors API.
|
||||
"""
|
||||
response = self._do_get(
|
||||
'os-hypervisors?hypervisor_hostname_pattern=fake')
|
||||
subs = {'hypervisor_id': self.compute_node_1.uuid}
|
||||
self._verify_response('hypervisors-search-resp', subs, response, 200)
|
||||
|
||||
@mock.patch("nova.compute.api.HostAPI.instance_get_all_by_host")
|
||||
def test_hypervisors_with_servers(self, instance_get_all_by_host):
|
||||
"""The servers route is deprecated in 2.53 and is now a query parameter
|
||||
on the GET /os-hypervisors API.
|
||||
"""
|
||||
instances = [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}]
|
||||
instance_get_all_by_host.return_value = instances
|
||||
response = self._do_get('os-hypervisors?with_servers=true')
|
||||
subs = {'hypervisor_id': self.compute_node_1.uuid}
|
||||
self._verify_response('hypervisors-with-servers-resp', subs,
|
||||
response, 200)
|
||||
|
||||
def test_hypervisors_without_servers(self):
|
||||
# This is the same as GET /os-hypervisors in 2.53 which is covered by
|
||||
# test_hypervisors_list already.
|
||||
pass
|
||||
|
||||
def test_hypervisors_uptime(self):
|
||||
def fake_get_host_uptime(self, context, hyp):
|
||||
return (" 08:32:11 up 93 days, 18:25, 12 users, load average:"
|
||||
" 0.20, 0.12, 0.14")
|
||||
|
||||
self.stub_out('nova.compute.api.HostAPI.get_host_uptime',
|
||||
fake_get_host_uptime)
|
||||
hypervisor_id = self.compute_node_1.uuid
|
||||
response = self._do_get('os-hypervisors/%s/uptime' % hypervisor_id)
|
||||
subs = {
|
||||
'hypervisor_id': hypervisor_id,
|
||||
}
|
||||
self._verify_response('hypervisors-uptime-resp', subs, response, 200)
|
||||
|
||||
def test_hypervisors_show(self):
|
||||
hypervisor_id = self.compute_node_1.uuid
|
||||
subs = {
|
||||
'hypervisor_id': hypervisor_id,
|
||||
'service_id': self.compute.service_ref.uuid,
|
||||
}
|
||||
response = self._do_get('os-hypervisors/%s' % hypervisor_id)
|
||||
self._verify_response('hypervisors-show-resp', subs, response, 200)
|
||||
|
||||
@mock.patch("nova.compute.api.HostAPI.instance_get_all_by_host")
|
||||
def test_hypervisors_show_with_servers(self, instance_get_all_by_host):
|
||||
"""Tests getting details for a specific hypervisor and including the
|
||||
hosted servers in the response.
|
||||
"""
|
||||
instances = [
|
||||
{
|
||||
"name": "test_server1",
|
||||
"uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
||||
},
|
||||
{
|
||||
"name": "test_server2",
|
||||
"uuid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
||||
}]
|
||||
instance_get_all_by_host.return_value = instances
|
||||
hypervisor_id = self.compute_node_1.uuid
|
||||
subs = {
|
||||
'hypervisor_id': hypervisor_id,
|
||||
'service_id': self.compute.service_ref.uuid,
|
||||
}
|
||||
response = self._do_get('os-hypervisors/%s?with_servers=1' %
|
||||
hypervisor_id)
|
||||
self._verify_response('hypervisors-show-with-servers-resp', subs,
|
||||
response, 200)
|
||||
|
@ -48,6 +48,8 @@ class ExtendedHypervisorsTestV21(test.NoDBTestCase):
|
||||
del DETAIL_HYPERS_DICTS[1]['service_id']
|
||||
del DETAIL_HYPERS_DICTS[0]['host']
|
||||
del DETAIL_HYPERS_DICTS[1]['host']
|
||||
del DETAIL_HYPERS_DICTS[0]['uuid']
|
||||
del DETAIL_HYPERS_DICTS[1]['uuid']
|
||||
DETAIL_HYPERS_DICTS[0].update({'state': 'up',
|
||||
'status': 'enabled',
|
||||
'service': dict(id=1, host='compute1',
|
||||
|
@ -18,6 +18,7 @@ import copy
|
||||
import mock
|
||||
import netaddr
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
from webob import exc
|
||||
|
||||
from nova.api.openstack.compute import hypervisors \
|
||||
@ -39,6 +40,7 @@ CPU_INFO = """
|
||||
|
||||
TEST_HYPERS = [
|
||||
dict(id=1,
|
||||
uuid=uuids.hyper1,
|
||||
service_id=1,
|
||||
host="compute1",
|
||||
vcpus=4,
|
||||
@ -58,6 +60,7 @@ TEST_HYPERS = [
|
||||
disk_available_least=100,
|
||||
host_ip=netaddr.IPAddress('1.1.1.1')),
|
||||
dict(id=2,
|
||||
uuid=uuids.hyper2,
|
||||
service_id=2,
|
||||
host="compute2",
|
||||
vcpus=4,
|
||||
@ -80,6 +83,7 @@ TEST_HYPERS = [
|
||||
|
||||
TEST_SERVICES = [
|
||||
objects.Service(id=1,
|
||||
uuid=uuids.service1,
|
||||
host="compute1",
|
||||
binary="nova-compute",
|
||||
topic="compute_topic",
|
||||
@ -88,6 +92,7 @@ TEST_SERVICES = [
|
||||
disabled_reason=None,
|
||||
availability_zone="nova"),
|
||||
objects.Service(id=2,
|
||||
uuid=uuids.service2,
|
||||
host="compute2",
|
||||
binary="nova-compute",
|
||||
topic="compute_topic",
|
||||
@ -110,12 +115,13 @@ TEST_SERVERS = [dict(name="inst1", uuid=uuids.instance_1, host="compute1"),
|
||||
|
||||
|
||||
def fake_compute_node_get_all(context, limit=None, marker=None):
|
||||
if marker in ['99999']:
|
||||
if marker in ['99999', uuids.invalid_marker]:
|
||||
raise exception.MarkerNotFound(marker)
|
||||
marker_found = True if marker is None else False
|
||||
output = []
|
||||
for hyper in TEST_HYPERS_OBJ:
|
||||
if not marker_found and marker == str(hyper.id):
|
||||
# Starting with the 2.53 microversion, the marker is a uuid.
|
||||
if not marker_found and marker in (str(hyper.id), hyper.uuid):
|
||||
marker_found = True
|
||||
elif marker_found:
|
||||
if limit is None or len(output) < int(limit):
|
||||
@ -129,7 +135,7 @@ def fake_compute_node_search_by_hypervisor(context, hypervisor_re):
|
||||
|
||||
def fake_compute_node_get(context, compute_id):
|
||||
for hyper in TEST_HYPERS_OBJ:
|
||||
if hyper.id == int(compute_id):
|
||||
if hyper.uuid == compute_id or hyper.id == int(compute_id):
|
||||
return hyper
|
||||
raise exception.ComputeHostNotFound(host=compute_id)
|
||||
|
||||
@ -177,6 +183,9 @@ def fake_instance_get_all_by_host(context, host):
|
||||
|
||||
class HypervisorsTestV21(test.NoDBTestCase):
|
||||
api_version = '2.1'
|
||||
# Allow subclasses to override if the id value in the response is the
|
||||
# compute node primary key integer id or the uuid.
|
||||
expect_uuid_for_id = False
|
||||
|
||||
# copying the objects locally so the cells testcases can provide their own
|
||||
TEST_HYPERS_OBJ = copy.deepcopy(TEST_HYPERS_OBJ)
|
||||
@ -188,6 +197,8 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
del DETAIL_HYPERS_DICTS[1]['service_id']
|
||||
del DETAIL_HYPERS_DICTS[0]['host']
|
||||
del DETAIL_HYPERS_DICTS[1]['host']
|
||||
del DETAIL_HYPERS_DICTS[0]['uuid']
|
||||
del DETAIL_HYPERS_DICTS[1]['uuid']
|
||||
DETAIL_HYPERS_DICTS[0].update({'state': 'up',
|
||||
'status': 'enabled',
|
||||
'service': dict(id=1, host='compute1',
|
||||
@ -213,6 +224,15 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
self.controller.servicegroup_api.service_is_up = mock.MagicMock(
|
||||
return_value=True)
|
||||
|
||||
def _get_hyper_id(self):
|
||||
"""Helper function to get the proper hypervisor id for a request
|
||||
|
||||
:returns: The first hypervisor's uuid for microversions that expect a
|
||||
uuid for the id, otherwise the hypervisor's id primary key
|
||||
"""
|
||||
return (self.TEST_HYPERS_OBJ[0].uuid if self.expect_uuid_for_id
|
||||
else self.TEST_HYPERS_OBJ[0].id)
|
||||
|
||||
def setUp(self):
|
||||
super(HypervisorsTestV21, self).setUp()
|
||||
self._set_up_controller()
|
||||
@ -317,7 +337,8 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
result = self.controller.index(req)
|
||||
self.assertEqual(1, len(result['hypervisors']))
|
||||
expected = {
|
||||
'id': compute_nodes[0].id,
|
||||
'id': compute_nodes[0].uuid if self.expect_uuid_for_id
|
||||
else compute_nodes[0].id,
|
||||
'hypervisor_hostname': compute_nodes[0].hypervisor_hostname,
|
||||
'state': 'up',
|
||||
'status': 'enabled',
|
||||
@ -350,7 +371,8 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
result = self.controller.index(req)
|
||||
self.assertEqual(1, len(result['hypervisors']))
|
||||
expected = {
|
||||
'id': compute_nodes[0].id,
|
||||
'id': compute_nodes[0].uuid if self.expect_uuid_for_id
|
||||
else compute_nodes[0].id,
|
||||
'hypervisor_hostname': compute_nodes[0].hypervisor_hostname,
|
||||
'state': 'up',
|
||||
'status': 'enabled',
|
||||
@ -461,31 +483,25 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
don't fail when listing hypervisors.
|
||||
"""
|
||||
|
||||
# two computes, a matching service only exists for the first one
|
||||
compute_nodes = objects.ComputeNodeList(objects=[
|
||||
objects.ComputeNode(**TEST_HYPERS[0]),
|
||||
objects.ComputeNode(**TEST_HYPERS[1])
|
||||
])
|
||||
|
||||
def fake_service_get_by_compute_host(context, host):
|
||||
return TEST_SERVICES[0]
|
||||
|
||||
@mock.patch.object(self.controller.host_api, 'compute_node_get',
|
||||
fake_service_get_by_compute_host)
|
||||
return_value=self.TEST_HYPERS_OBJ[0])
|
||||
@mock.patch.object(self.controller.host_api,
|
||||
'service_get_by_compute_host')
|
||||
def _test(self, mock_service):
|
||||
def _test(self, mock_service, mock_compute_node_get):
|
||||
req = self._get_request(True)
|
||||
mock_service.side_effect = exception.HostMappingNotFound(
|
||||
name='foo')
|
||||
hyper_id = self._get_hyper_id()
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.show,
|
||||
req, compute_nodes[0].id)
|
||||
req, hyper_id)
|
||||
self.assertTrue(mock_service.called)
|
||||
mock_compute_node_get.assert_called_once_with(mock.ANY, hyper_id)
|
||||
_test(self)
|
||||
|
||||
def test_show_noid(self):
|
||||
req = self._get_request(True)
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.show, req, '3')
|
||||
hyperid = uuids.hyper3 if self.expect_uuid_for_id else '3'
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.show, req, hyperid)
|
||||
|
||||
def test_show_non_integer_id(self):
|
||||
req = self._get_request(True)
|
||||
@ -493,7 +509,8 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
|
||||
def test_show_withid(self):
|
||||
req = self._get_request(True)
|
||||
result = self.controller.show(req, self.TEST_HYPERS_OBJ[0].id)
|
||||
hyper_id = self._get_hyper_id()
|
||||
result = self.controller.show(req, hyper_id)
|
||||
|
||||
self.assertEqual(dict(hypervisor=self.DETAIL_HYPERS_DICTS[0]), result)
|
||||
|
||||
@ -501,20 +518,22 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
req = self._get_request(False)
|
||||
self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.controller.show, req,
|
||||
self.TEST_HYPERS_OBJ[0].id)
|
||||
self._get_hyper_id())
|
||||
|
||||
def test_uptime_noid(self):
|
||||
req = self._get_request(True)
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.uptime, req, '3')
|
||||
hyper_id = uuids.hyper3 if self.expect_uuid_for_id else '3'
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.uptime, req,
|
||||
hyper_id)
|
||||
|
||||
def test_uptime_notimplemented(self):
|
||||
with mock.patch.object(self.controller.host_api, 'get_host_uptime',
|
||||
side_effect=exc.HTTPNotImplemented()
|
||||
) as mock_get_uptime:
|
||||
req = self._get_request(True)
|
||||
hyper_id = self._get_hyper_id()
|
||||
self.assertRaises(exc.HTTPNotImplemented,
|
||||
self.controller.uptime, req,
|
||||
self.TEST_HYPERS_OBJ[0].id)
|
||||
self.controller.uptime, req, hyper_id)
|
||||
self.assertEqual(1, mock_get_uptime.call_count)
|
||||
|
||||
def test_uptime_implemented(self):
|
||||
@ -522,7 +541,8 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
return_value="fake uptime"
|
||||
) as mock_get_uptime:
|
||||
req = self._get_request(True)
|
||||
result = self.controller.uptime(req, self.TEST_HYPERS_OBJ[0].id)
|
||||
hyper_id = self._get_hyper_id()
|
||||
result = self.controller.uptime(req, hyper_id)
|
||||
|
||||
expected_dict = copy.deepcopy(self.INDEX_HYPER_DICTS[0])
|
||||
expected_dict.update({'uptime': "fake uptime"})
|
||||
@ -544,9 +564,9 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
side_effect=exception.ComputeServiceUnavailable(host='dummy')
|
||||
) as mock_get_uptime:
|
||||
req = self._get_request(True)
|
||||
hyper_id = self._get_hyper_id()
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.uptime, req,
|
||||
self.TEST_HYPERS_OBJ[0].id)
|
||||
self.controller.uptime, req, hyper_id)
|
||||
mock_get_uptime.assert_called_once_with(
|
||||
mock.ANY, self.TEST_HYPERS_OBJ[0].host)
|
||||
|
||||
@ -559,9 +579,9 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
name='dummy'))
|
||||
def _test(mock_get, _, __):
|
||||
req = self._get_request(True)
|
||||
hyper_id = self._get_hyper_id()
|
||||
self.assertRaises(exc.HTTPNotFound,
|
||||
self.controller.uptime, req,
|
||||
self.TEST_HYPERS_OBJ[0].id)
|
||||
self.controller.uptime, req, hyper_id)
|
||||
self.assertTrue(mock_get.called)
|
||||
|
||||
_test()
|
||||
@ -571,9 +591,9 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
||||
side_effect=exception.HostMappingNotFound(name='dummy')
|
||||
) as mock_get_uptime:
|
||||
req = self._get_request(True)
|
||||
hyper_id = self._get_hyper_id()
|
||||
self.assertRaises(exc.HTTPNotFound,
|
||||
self.controller.uptime, req,
|
||||
self.TEST_HYPERS_OBJ[0].id)
|
||||
self.controller.uptime, req, hyper_id)
|
||||
mock_get_uptime.assert_called_once_with(
|
||||
mock.ANY, self.TEST_HYPERS_OBJ[0].host)
|
||||
|
||||
@ -872,3 +892,398 @@ class HypervisorsTestV233(HypervisorsTestV228):
|
||||
'/v2/1234/os-hypervisors/detail?marker=99999')
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.detail, req)
|
||||
|
||||
|
||||
class HypervisorsTestV252(HypervisorsTestV233):
|
||||
"""This is a boundary test to make sure 2.52 works like 2.33."""
|
||||
api_version = '2.52'
|
||||
|
||||
|
||||
class HypervisorsTestV253(HypervisorsTestV252):
|
||||
api_version = hypervisors_v21.UUID_FOR_ID_MIN_VERSION
|
||||
expect_uuid_for_id = True
|
||||
|
||||
# This is an expected response for index().
|
||||
INDEX_HYPER_DICTS = [
|
||||
dict(id=uuids.hyper1, hypervisor_hostname="hyper1",
|
||||
state='up', status='enabled'),
|
||||
dict(id=uuids.hyper2, hypervisor_hostname="hyper2",
|
||||
state='up', status='enabled')]
|
||||
|
||||
def setUp(self):
|
||||
super(HypervisorsTestV253, self).setUp()
|
||||
# This is an expected response for detail().
|
||||
for index, detail_hyper_dict in enumerate(self.DETAIL_HYPERS_DICTS):
|
||||
detail_hyper_dict['id'] = TEST_HYPERS[index]['uuid']
|
||||
detail_hyper_dict['service']['id'] = TEST_SERVICES[index].uuid
|
||||
|
||||
def test_servers(self):
|
||||
"""Asserts that calling the servers route after 2.48 fails."""
|
||||
self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
||||
self.controller.servers,
|
||||
self._get_request(True), 'hyper')
|
||||
|
||||
def test_servers_with_no_server(self):
|
||||
"""Tests GET /os-hypervisors?with_servers=1 when there are no
|
||||
instances on the given host.
|
||||
"""
|
||||
with mock.patch.object(self.controller.host_api,
|
||||
'instance_get_all_by_host',
|
||||
return_value=[]) as mock_inst_get_all:
|
||||
req = self._get_request(use_admin_context=True,
|
||||
url='/os-hypervisors?with_servers=1')
|
||||
result = self.controller.index(req)
|
||||
self.assertEqual(dict(hypervisors=self.INDEX_HYPER_DICTS), result)
|
||||
# instance_get_all_by_host is called for each hypervisor
|
||||
self.assertEqual(2, mock_inst_get_all.call_count)
|
||||
mock_inst_get_all.assert_has_calls((
|
||||
mock.call(req.environ['nova.context'], TEST_HYPERS_OBJ[0].host),
|
||||
mock.call(req.environ['nova.context'], TEST_HYPERS_OBJ[1].host)))
|
||||
|
||||
def test_servers_not_mapped(self):
|
||||
"""Tests that instance_get_all_by_host fails with HostMappingNotFound.
|
||||
"""
|
||||
req = self._get_request(use_admin_context=True,
|
||||
url='/os-hypervisors?with_servers=1')
|
||||
with mock.patch.object(
|
||||
self.controller.host_api, 'instance_get_all_by_host',
|
||||
side_effect=exception.HostMappingNotFound(name='something')):
|
||||
result = self.controller.index(req)
|
||||
self.assertEqual(dict(hypervisors=[]), result)
|
||||
|
||||
def test_list_with_servers(self):
|
||||
"""Tests GET /os-hypervisors?with_servers=True"""
|
||||
instances = [
|
||||
objects.InstanceList(objects=[objects.Instance(
|
||||
id=1, uuid=uuids.hyper1_instance1)]),
|
||||
objects.InstanceList(objects=[objects.Instance(
|
||||
id=2, uuid=uuids.hyper2_instance1)])]
|
||||
with mock.patch.object(self.controller.host_api,
|
||||
'instance_get_all_by_host',
|
||||
side_effect=instances) as mock_inst_get_all:
|
||||
req = self._get_request(use_admin_context=True,
|
||||
url='/os-hypervisors?with_servers=True')
|
||||
result = self.controller.index(req)
|
||||
index_with_servers = copy.deepcopy(self.INDEX_HYPER_DICTS)
|
||||
index_with_servers[0]['servers'] = [
|
||||
{'name': 'instance-00000001', 'uuid': uuids.hyper1_instance1}]
|
||||
index_with_servers[1]['servers'] = [
|
||||
{'name': 'instance-00000002', 'uuid': uuids.hyper2_instance1}]
|
||||
self.assertEqual(dict(hypervisors=index_with_servers), result)
|
||||
# instance_get_all_by_host is called for each hypervisor
|
||||
self.assertEqual(2, mock_inst_get_all.call_count)
|
||||
mock_inst_get_all.assert_has_calls((
|
||||
mock.call(req.environ['nova.context'], TEST_HYPERS_OBJ[0].host),
|
||||
mock.call(req.environ['nova.context'], TEST_HYPERS_OBJ[1].host)))
|
||||
|
||||
def test_list_with_servers_invalid_parameter(self):
|
||||
"""Tests using an invalid with_servers query parameter."""
|
||||
req = self._get_request(use_admin_context=True,
|
||||
url='/os-hypervisors?with_servers=invalid')
|
||||
self.assertRaises(
|
||||
exception.ValidationError, self.controller.index, req)
|
||||
|
||||
def test_list_with_hostname_pattern_and_paging_parameters(self):
|
||||
"""This is a negative test to validate that trying to list hypervisors
|
||||
with a hostname pattern and paging parameters results in a 400 error.
|
||||
"""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors?hypervisor_hostname_pattern=foo&'
|
||||
'limit=1&marker=%s' % uuids.marker)
|
||||
ex = self.assertRaises(exc.HTTPBadRequest, self.controller.index, req)
|
||||
self.assertIn('Paging over hypervisors with the '
|
||||
'hypervisor_hostname_pattern query parameter is not '
|
||||
'supported.', six.text_type(ex))
|
||||
|
||||
def test_servers_with_non_integer_hypervisor_id(self):
|
||||
"""This is a poorly named test, it's really checking the 404 case where
|
||||
there is no match for the hostname pattern.
|
||||
"""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors?with_servers=yes&'
|
||||
'hypervisor_hostname_pattern=shenzhen')
|
||||
with mock.patch.object(self.controller.host_api,
|
||||
'compute_node_search_by_hypervisor',
|
||||
return_value=objects.ComputeNodeList()) as s:
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.index, req)
|
||||
s.assert_called_once_with(req.environ['nova.context'], 'shenzhen')
|
||||
|
||||
def test_servers_non_admin(self):
|
||||
"""There is no reason to test this for 2.53 since the
|
||||
/os-hypervisors/servers route is deprecated.
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_servers_non_id(self):
|
||||
"""There is no reason to test this for 2.53 since the
|
||||
/os-hypervisors/servers route is deprecated.
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_search_old_route(self):
|
||||
"""Asserts that calling the search route after 2.48 fails."""
|
||||
self.assertRaises(exception.VersionNotFoundForAPIMethod,
|
||||
self.controller.search,
|
||||
self._get_request(True), 'hyper')
|
||||
|
||||
def test_search(self):
|
||||
"""Test listing hypervisors with details and using the
|
||||
hypervisor_hostname_pattern query string.
|
||||
"""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors?hypervisor_hostname_pattern=shenzhen')
|
||||
with mock.patch.object(self.controller.host_api,
|
||||
'compute_node_search_by_hypervisor',
|
||||
return_value=objects.ComputeNodeList(
|
||||
objects=[TEST_HYPERS_OBJ[0]])) as s:
|
||||
result = self.controller.detail(req)
|
||||
s.assert_called_once_with(req.environ['nova.context'], 'shenzhen')
|
||||
|
||||
expected = {
|
||||
'hypervisors': [
|
||||
{'cpu_info': {'arch': 'x86_64',
|
||||
'features': [],
|
||||
'model': '',
|
||||
'topology': {'cores': 1,
|
||||
'sockets': 1,
|
||||
'threads': 1},
|
||||
'vendor': 'fake'},
|
||||
'current_workload': 2,
|
||||
'disk_available_least': 100,
|
||||
'free_disk_gb': 125,
|
||||
'free_ram_mb': 5120,
|
||||
'host_ip': netaddr.IPAddress('1.1.1.1'),
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'hypervisor_type': 'xen',
|
||||
'hypervisor_version': 3,
|
||||
'id': TEST_HYPERS_OBJ[0].uuid,
|
||||
'local_gb': 250,
|
||||
'local_gb_used': 125,
|
||||
'memory_mb': 10240,
|
||||
'memory_mb_used': 5120,
|
||||
'running_vms': 2,
|
||||
'service': {'disabled_reason': None,
|
||||
'host': 'compute1',
|
||||
'id': TEST_SERVICES[0].uuid},
|
||||
'state': 'up',
|
||||
'status': 'enabled',
|
||||
'vcpus': 4,
|
||||
'vcpus_used': 2}
|
||||
]
|
||||
}
|
||||
# There are no links when using the hypervisor_hostname_pattern
|
||||
# query string since we can't page using a pattern matcher.
|
||||
self.assertNotIn('hypervisors_links', result)
|
||||
self.assertDictEqual(expected, result)
|
||||
|
||||
def test_search_invalid_hostname_pattern_parameter(self):
|
||||
"""Tests passing an invalid hypervisor_hostname_pattern query
|
||||
parameter.
|
||||
"""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors?hypervisor_hostname_pattern=invalid~host')
|
||||
self.assertRaises(
|
||||
exception.ValidationError, self.controller.detail, req)
|
||||
|
||||
def test_search_non_exist(self):
|
||||
"""This is a duplicate of test_servers_with_non_integer_hypervisor_id.
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_search_non_admin(self):
|
||||
"""There is no reason to test this for 2.53 since the
|
||||
/os-hypervisors/search route is deprecated.
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_search_unmapped(self):
|
||||
"""This is already tested with test_index_compute_host_not_mapped."""
|
||||
pass
|
||||
|
||||
def test_show_non_integer_id(self):
|
||||
"""There is no reason to test this for 2.53 since 2.53 requires a
|
||||
non-integer id (requires a uuid).
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_show_integer_id(self):
|
||||
"""Tests that we get a 400 if passed a hypervisor integer id to show().
|
||||
"""
|
||||
req = self._get_request(True)
|
||||
ex = self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.show, req, '1')
|
||||
self.assertIn('Invalid uuid 1', six.text_type(ex))
|
||||
|
||||
def test_show_with_servers_invalid_parameter(self):
|
||||
"""Tests passing an invalid value for the with_servers query parameter
|
||||
to the show() method to make sure the query parameter is validated.
|
||||
"""
|
||||
hyper_id = self._get_hyper_id()
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors/%s?with_servers=invalid' % hyper_id)
|
||||
ex = self.assertRaises(
|
||||
exception.ValidationError, self.controller.show, req, hyper_id)
|
||||
self.assertIn('with_servers', six.text_type(ex))
|
||||
|
||||
def test_show_with_servers_host_mapping_not_found(self):
|
||||
"""Tests that a 404 is returned if instance_get_all_by_host raises
|
||||
HostMappingNotFound.
|
||||
"""
|
||||
hyper_id = self._get_hyper_id()
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors/%s?with_servers=true' % hyper_id)
|
||||
with mock.patch.object(
|
||||
self.controller.host_api, 'instance_get_all_by_host',
|
||||
side_effect=exception.HostMappingNotFound(name=hyper_id)):
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.show,
|
||||
req, hyper_id)
|
||||
|
||||
def test_show_with_servers(self):
|
||||
"""Tests the show() result when servers are included in the output."""
|
||||
instances = objects.InstanceList(objects=[objects.Instance(
|
||||
id=1, uuid=uuids.hyper1_instance1)])
|
||||
hyper_id = self._get_hyper_id()
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors/%s?with_servers=on' % hyper_id)
|
||||
with mock.patch.object(self.controller.host_api,
|
||||
'instance_get_all_by_host',
|
||||
return_value=instances) as mock_inst_get_all:
|
||||
result = self.controller.show(req, hyper_id)
|
||||
show_with_servers = copy.deepcopy(self.DETAIL_HYPERS_DICTS[0])
|
||||
show_with_servers['servers'] = [
|
||||
{'name': 'instance-00000001', 'uuid': uuids.hyper1_instance1}]
|
||||
self.assertDictEqual(dict(hypervisor=show_with_servers), result)
|
||||
# instance_get_all_by_host is called
|
||||
mock_inst_get_all.assert_called_once_with(
|
||||
req.environ['nova.context'], TEST_HYPERS_OBJ[0].host)
|
||||
|
||||
def test_uptime_non_integer_id(self):
|
||||
"""There is no reason to test this for 2.53 since 2.53 requires a
|
||||
non-integer id (requires a uuid).
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_uptime_integer_id(self):
|
||||
"""Tests that we get a 400 if passed a hypervisor integer id to
|
||||
uptime().
|
||||
"""
|
||||
req = self._get_request(True)
|
||||
ex = self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.uptime, req, '1')
|
||||
self.assertIn('Invalid uuid 1', six.text_type(ex))
|
||||
|
||||
def test_detail_pagination(self):
|
||||
"""Tests details paging with uuid markers."""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors/detail?limit=1&marker=%s' %
|
||||
TEST_HYPERS_OBJ[0].uuid)
|
||||
result = self.controller.detail(req)
|
||||
link = ('http://localhost/v2/hypervisors/detail?limit=1&marker=%s' %
|
||||
TEST_HYPERS_OBJ[1].uuid)
|
||||
expected = {
|
||||
'hypervisors': [
|
||||
{'cpu_info': {'arch': 'x86_64',
|
||||
'features': [],
|
||||
'model': '',
|
||||
'topology': {'cores': 1,
|
||||
'sockets': 1,
|
||||
'threads': 1},
|
||||
'vendor': 'fake'},
|
||||
'current_workload': 2,
|
||||
'disk_available_least': 100,
|
||||
'free_disk_gb': 125,
|
||||
'free_ram_mb': 5120,
|
||||
'host_ip': netaddr.IPAddress('2.2.2.2'),
|
||||
'hypervisor_hostname': 'hyper2',
|
||||
'hypervisor_type': 'xen',
|
||||
'hypervisor_version': 3,
|
||||
'id': TEST_HYPERS_OBJ[1].uuid,
|
||||
'local_gb': 250,
|
||||
'local_gb_used': 125,
|
||||
'memory_mb': 10240,
|
||||
'memory_mb_used': 5120,
|
||||
'running_vms': 2,
|
||||
'service': {'disabled_reason': None,
|
||||
'host': 'compute2',
|
||||
'id': TEST_SERVICES[1].uuid},
|
||||
'state': 'up',
|
||||
'status': 'enabled',
|
||||
'vcpus': 4,
|
||||
'vcpus_used': 2}
|
||||
],
|
||||
'hypervisors_links': [{'href': link, 'rel': 'next'}]
|
||||
}
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_detail_pagination_with_invalid_marker(self):
|
||||
"""Tests detail paging with an invalid marker (not found)."""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors/detail?marker=%s' % uuids.invalid_marker)
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.detail, req)
|
||||
|
||||
def test_index_pagination(self):
|
||||
"""Tests index paging with uuid markers."""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors?limit=1&marker=%s' %
|
||||
TEST_HYPERS_OBJ[0].uuid)
|
||||
result = self.controller.index(req)
|
||||
link = ('http://localhost/v2/hypervisors?limit=1&marker=%s' %
|
||||
TEST_HYPERS_OBJ[1].uuid)
|
||||
expected = {
|
||||
'hypervisors': [{
|
||||
'hypervisor_hostname': 'hyper2',
|
||||
'id': TEST_HYPERS_OBJ[1].uuid,
|
||||
'state': 'up',
|
||||
'status': 'enabled'
|
||||
}],
|
||||
'hypervisors_links': [{'href': link, 'rel': 'next'}]
|
||||
}
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_index_pagination_with_invalid_marker(self):
|
||||
"""Tests index paging with an invalid marker (not found)."""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors?marker=%s' % uuids.invalid_marker)
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.index, req)
|
||||
|
||||
def test_list_duplicate_query_parameters_validation(self):
|
||||
"""Tests that the list query parameter schema enforces only a single
|
||||
entry for any query parameter.
|
||||
"""
|
||||
params = {
|
||||
'limit': 1,
|
||||
'marker': uuids.marker,
|
||||
'hypervisor_hostname_pattern': 'foo',
|
||||
'with_servers': 'true'
|
||||
}
|
||||
for param, value in params.items():
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors?%s=%s&%s=%s' %
|
||||
(param, value, param, value))
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.controller.index, req)
|
||||
|
||||
def test_show_duplicate_query_parameters_validation(self):
|
||||
"""Tests that the show query parameter schema enforces only a single
|
||||
entry for any query parameter.
|
||||
"""
|
||||
req = self._get_request(
|
||||
use_admin_context=True,
|
||||
url='/os-hypervisors/%s?with_servers=1&with_servers=1' %
|
||||
uuids.hyper1)
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.controller.show, req, uuids.hyper1)
|
||||
|
@ -147,7 +147,7 @@ class CellsUtilsTestCase(test.NoDBTestCase):
|
||||
cell = cells_utils.PATH_CELL_SEP.join(path)
|
||||
item = 'host_5'
|
||||
together = cells_utils.cell_with_item(cell, item)
|
||||
self.assertEqual(cells_utils._CELL_ITEM_SEP.join([cell, item]),
|
||||
self.assertEqual(cells_utils.CELL_ITEM_SEP.join([cell, item]),
|
||||
together)
|
||||
|
||||
# Test normal usage
|
||||
|
@ -1,9 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Microversion 2.53 changes service IDs to UUIDs to ensure uniqueness across
|
||||
cells. Prior to this, ID collisions were possible in multi-cell
|
||||
deployments. See the `REST API Version History`_ and
|
||||
Microversion 2.53 changes service and hypervisor IDs to UUIDs to ensure
|
||||
uniqueness across cells. Prior to this, ID collisions were possible in
|
||||
multi-cell deployments. See the `REST API Version History`_ and
|
||||
`Compute API reference`_ for details.
|
||||
|
||||
.. _REST API Version History: https://docs.openstack.org/developer/nova/api_microversion_history.html
|
||||
|
Loading…
Reference in New Issue
Block a user