api: Address issues with hypervisors APIs

* Address an off-by-one error: the cpu_info field was modified in v2.28,
  not v2.27,
* Correct the api-ref to indicate that the 'servers' field is not
  actually required and will be missing if '?with_servers=false', while
  the 'name' and 'uuid' fields of servers entries *are* required.
* Clarify a comment about the above in the schemas.
* Uncouple the '_hypervisor_response' and '_hypervisor_detail_response'
  helper schemas. The minor increase in lines of code is worth it for
  the decrease in complexity.
* Add the 'host_ip', 'hypervisor_type', and 'hypervisor_version' fields
  to the list of required fields for "detail"-style responses (show and
  detailed list).
* Make the 'current_workload', 'disk_available_least', 'free_disk_gb',
  'free_ram_mb', 'host_ip' and 'running_vms' fields of the hypervisor
  "detail"-style responses nullable, and the 'current_workload',
  'disk_available_least', 'free_disk_gb', 'free_ram_mb' and
  'running_vms' fields of the deprecated statistics API nullable.

Change-Id: Ibe55b44e65fe17141c63cceae8a003816ffe4f23
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane
2025-05-26 13:26:38 +01:00
parent c918fcc587
commit a0af4648b5
3 changed files with 91 additions and 73 deletions

View File

@ -3843,25 +3843,24 @@ hypervisor_os_diagnostics:
min_version: 2.48
hypervisor_servers:
description: |
A list of ``server`` objects.
This field has become mandatory in microversion 2.75. If no servers is on hypervisor
then empty list is returned.
A list of ``server`` objects. Before microversion 2.75, this field is only
returned if non-empty. From microversion 2.75, this field will always be
returned unless the ``with_servers`` query parameter is provided and is
set to ``false``.
in: body
required: true
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:

View File

@ -256,8 +256,8 @@ class HypervisorsController(wsgi.Controller):
@validation.query_schema(schema.index_query, '2.1', '2.32')
@validation.query_schema(schema.index_query_v233, '2.33', '2.52')
@validation.query_schema(schema.index_query_v253, '2.53')
@validation.response_body_schema(schema.detail_response, '2.1', '2.26')
@validation.response_body_schema(schema.detail_response_v227, '2.27', '2.32') # noqa: E501
@validation.response_body_schema(schema.detail_response, '2.1', '2.27')
@validation.response_body_schema(schema.detail_response_v228, '2.28', '2.32') # noqa: E501
@validation.response_body_schema(schema.detail_response_v233, '2.33', '2.52') # noqa: E501
@validation.response_body_schema(schema.detail_response_v253, '2.53', '2.87') # noqa: E501
@validation.response_body_schema(schema.detail_response_v288, '2.88')
@ -313,8 +313,8 @@ class HypervisorsController(wsgi.Controller):
@wsgi.expected_errors((400, 404), '2.53')
@validation.query_schema(schema.show_query, '2.1', '2.52')
@validation.query_schema(schema.show_query_v253, '2.53')
@validation.response_body_schema(schema.show_response, '2.1', '2.26')
@validation.response_body_schema(schema.show_response_v227, '2.27', '2.52')
@validation.response_body_schema(schema.show_response, '2.1', '2.27')
@validation.response_body_schema(schema.show_response_v228, '2.28', '2.52')
@validation.response_body_schema(schema.show_response_v253, '2.53', '2.87')
@validation.response_body_schema(schema.show_response_v288, '2.88')
def show(self, req, id):

View File

@ -104,50 +104,64 @@ _hypervisor_response_v253['properties'].update({
'servers': copy.deepcopy(_servers_response),
})
_hypervisor_detail_response = copy.deepcopy(_hypervisor_response)
_hypervisor_detail_response['properties'].update({
'cpu_info': {'type': 'string'},
'current_workload': {'type': 'integer'},
'disk_available_least': {'type': 'integer'},
'free_disk_gb': {'type': 'integer'},
'free_ram_mb': {'type': 'integer'},
'host_ip': {'type': 'string'},
'hypervisor_type': {'type': 'string'},
'hypervisor_version': {'type': ['string', 'integer']},
'local_gb': {'type': 'integer'},
'local_gb_used': {'type': 'integer'},
'memory_mb': {'type': 'integer'},
'memory_mb_used': {'type': 'integer'},
'running_vms': {'type': 'integer'},
'service': {
'type': 'object',
'properties': {
'disabled_reason': {'type': ['null', 'string']},
'host': {'type': 'string'},
'id': {'type': 'integer'},
_hypervisor_detail_response = {
'type': 'object',
'properties': {
'cpu_info': {'type': 'string'},
'current_workload': {'type': ['null', 'integer']},
'disk_available_least': {'type': ['null', 'integer']},
'free_disk_gb': {'type': ['null', 'integer']},
'free_ram_mb': {'type': ['null', 'integer']},
'host_ip': {'type': ['null', 'string']},
'hypervisor_hostname': {'type': 'string'},
'hypervisor_type': {'type': 'string'},
'hypervisor_version': {'type': ['string', 'integer']},
'id': {'type': 'integer'},
'local_gb': {'type': 'integer'},
'local_gb_used': {'type': 'integer'},
'memory_mb': {'type': 'integer'},
'memory_mb_used': {'type': 'integer'},
'running_vms': {'type': ['null', 'integer']},
'service': {
'type': 'object',
'properties': {
'disabled_reason': {'type': ['null', 'string']},
'host': {'type': 'string'},
'id': {'type': 'integer'},
},
'required': ['disabled_reason', 'host', 'id'],
},
'required': ['disabled_reason', 'host', 'id'],
'state': {'enum': ['up', 'down']},
'status': {'enum': ['enabled', 'disabled']},
'vcpus': {'type': 'integer'},
'vcpus_used': {'type': 'integer'},
},
'vcpus': {'type': 'integer'},
'vcpus_used': {'type': 'integer'},
})
_hypervisor_detail_response['required'].extend([
'cpu_info',
'current_workload',
'free_disk_gb',
'free_ram_mb',
'local_gb',
'local_gb_used',
'memory_mb',
'memory_mb_used',
'running_vms',
'service',
'vcpus',
'vcpus_used',
])
'required': [
'cpu_info',
'current_workload',
'free_disk_gb',
'free_ram_mb',
'host_ip',
'hypervisor_hostname',
'hypervisor_type',
'hypervisor_version',
'id',
'local_gb',
'local_gb_used',
'memory_mb',
'memory_mb_used',
'running_vms',
'service',
'state',
'status',
'vcpus',
'vcpus_used',
],
'additionalProperties': False,
}
_hypervisor_detail_response_v227 = copy.deepcopy(_hypervisor_detail_response)
_hypervisor_detail_response_v227['properties'].update({
_hypervisor_detail_response_v228 = copy.deepcopy(_hypervisor_detail_response)
_hypervisor_detail_response_v228['properties'].update({
'cpu_info': {
# NOTE(stephenfin): This is virt-driver specific hence no schema
'type': 'object',
@ -158,11 +172,12 @@ _hypervisor_detail_response_v227['properties'].update({
})
_hypervisor_detail_response_v253 = copy.deepcopy(
_hypervisor_detail_response_v227
)
_hypervisor_detail_response_v253['properties'].update(
_hypervisor_response_v253['properties']
_hypervisor_detail_response_v228
)
_hypervisor_detail_response_v253['properties'].update({
'id': {'type': 'string', 'format': 'uuid'},
'servers': copy.deepcopy(_servers_response),
})
_hypervisor_detail_response_v253['properties']['service'][
'properties'
].update({
@ -213,8 +228,10 @@ index_response_v233['properties'].update({
# v2.53 adds the 'servers' field but only if a user requests it via the
# 'with_servers' query arg. It also changes the 'id' field to a UUID. Note that
# v2.75 makes the 'servers' property always present even if empty, but that's
# not something we can capture with jsonschema so we don't try
# v2.75 makes the 'servers' property always present even if empty *unless* the
# 'with_servers' query parameter is 'false'. This dependency between a query
# parameter and a response parameter is not something we can capture with
# jsonschema and we can't update 'required' as a result
index_response_v253 = copy.deepcopy(index_response_v233)
index_response_v253['properties']['hypervisors']['items'] = (
_hypervisor_response_v253
@ -232,22 +249,24 @@ detail_response['properties']['hypervisors'][
'items'
] = _hypervisor_detail_response
# v2.27 changes the 'cpu_info' field from a stringified object to a real object
detail_response_v227 = copy.deepcopy(detail_response)
detail_response_v227['properties']['hypervisors'][
# v2.28 changes the 'cpu_info' field from a stringified object to a real object
detail_response_v228 = copy.deepcopy(detail_response)
detail_response_v228['properties']['hypervisors'][
'items'
] = _hypervisor_detail_response_v227
] = _hypervisor_detail_response_v228
# v2.33 adds the hypervisors_links field
detail_response_v233 = copy.deepcopy(detail_response_v227)
detail_response_v233 = copy.deepcopy(detail_response_v228)
detail_response_v233['properties'].update({
'hypervisors_links': response_types.collection_links,
})
# v2.53 adds the 'servers' field but only if a user requests it via the
# 'with_servers' query arg. It also changes the 'id' field to a UUID. Note that
# v2.75 makes the 'servers' property always present even if empty, but that's
# not something we can capture with jsonschema so we don't try
# v2.75 makes the 'servers' property always present even if empty *unless* the
# 'with_servers' query parameter is 'false'. This dependency between a query
# parameter and a response parameter is not something we can capture with
# jsonschema and we can't update 'required' as a result
detail_response_v253 = copy.deepcopy(detail_response_v233)
detail_response_v253['properties']['hypervisors'][
'items'
@ -269,12 +288,12 @@ show_response = {
'additionalProperties': False,
}
show_response_v227 = copy.deepcopy(show_response)
show_response_v227['properties']['hypervisor'] = copy.deepcopy(
_hypervisor_detail_response_v227
show_response_v228 = copy.deepcopy(show_response)
show_response_v228['properties']['hypervisor'] = copy.deepcopy(
_hypervisor_detail_response_v228
)
show_response_v253 = copy.deepcopy(show_response_v227)
show_response_v253 = copy.deepcopy(show_response_v228)
show_response_v253['properties']['hypervisor'] = copy.deepcopy(
_hypervisor_detail_response_v253
)
@ -313,15 +332,15 @@ statistics_response = {
'type': 'object',
'properties': {
'count': {'type': 'integer'},
'current_workload': {'type': 'integer'},
'disk_available_least': {'type': 'integer'},
'free_disk_gb': {'type': 'integer'},
'free_ram_mb': {'type': 'integer'},
'current_workload': {'type': ['null', 'integer']},
'disk_available_least': {'type': ['null', 'integer']},
'free_disk_gb': {'type': ['null', 'integer']},
'free_ram_mb': {'type': ['null', 'integer']},
'local_gb': {'type': 'integer'},
'local_gb_used': {'type': 'integer'},
'memory_mb': {'type': 'integer'},
'memory_mb_used': {'type': 'integer'},
'running_vms': {'type': 'integer'},
'running_vms': {'type': ['null', 'integer']},
'vcpus': {'type': 'integer'},
'vcpus_used': {'type': 'integer'},
},