From 0552350a3b0ad0d9b8d1dc158fe963c6d7fd0eb7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 13 Nov 2024 17:47:27 +0000 Subject: [PATCH] api: Add response body schemas for server diagnostics API Change-Id: Id54d4f397aee20007f5e52e855c1d755b6f17946 Signed-off-by: Stephen Finucane --- .../compute/schemas/server_diagnostics.py | 137 ++++++++++++++++++ .../openstack/compute/server_diagnostics.py | 3 + .../unit/policies/test_server_diagnostics.py | 29 +++- nova/virt/driver.py | 2 +- 4 files changed, 165 insertions(+), 6 deletions(-) diff --git a/nova/api/openstack/compute/schemas/server_diagnostics.py b/nova/api/openstack/compute/schemas/server_diagnostics.py index 0f857da133af..d7209ef99309 100644 --- a/nova/api/openstack/compute/schemas/server_diagnostics.py +++ b/nova/api/openstack/compute/schemas/server_diagnostics.py @@ -16,3 +16,140 @@ index_query = { 'properties': {}, 'additionalProperties': True, } + +# NOTE(stephenfin): We could define all available response types for the +# various virt drivers, but we'd need to be able to do this (accurately) for +# every virt driver including those we once supported but no longer do. Seems +# like a lot of work with very little in return. +index_response = { + 'type': 'object', + 'properties': {}, + 'required': [], + 'additionalProperties': True, +} + +index_response_v248 = { + 'type': 'object', + 'properties': { + 'config_drive': {'type': 'boolean'}, + 'cpu_details': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'id': { + 'type': ['integer', 'null'], + }, + 'time': { + 'type': ['integer', 'null'], + }, + 'utilisation': { + 'type': ['integer', 'null'], + }, + }, + 'required': ['id', 'time', 'utilisation'], + }, + }, + 'disk_details': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'errors_count': {'type': ['integer', 'null']}, + 'read_bytes': {'type': ['integer', 'null']}, + 'read_requests': {'type': ['integer', 'null']}, + 'write_bytes': {'type': ['integer', 'null']}, + 'write_requests': {'type': ['integer', 'null']}, + }, + 'required': [ + 'errors_count', + 'read_bytes', + 'read_requests', + 'write_bytes', + 'write_requests', + ], + }, + }, + 'driver': { + 'type': 'string', + 'enum': [ + 'ironic', + 'libvirt', + 'vmwareapi', + 'xenapi', + ], + }, + 'hypervisor': {'type': ['string', 'null']}, + 'hypervisor_os': {'type': ['string', 'null']}, + 'memory_details': { + 'type': 'object', + 'properties': { + 'maximum': {'type': ['integer', 'null']}, + 'used': {'type': ['integer', 'null']}, + }, + 'required': ['maximum', 'used'], + }, + 'nic_details': { + 'type': 'array', + 'items': { + 'type': 'object', + 'properties': { + 'mac_address': {'type': ['string', 'null']}, + 'rx_drop': {'type': ['integer', 'null']}, + 'rx_errors': {'type': ['integer', 'null']}, + 'rx_octets': {'type': ['integer', 'null']}, + 'rx_packets': {'type': ['integer', 'null']}, + 'rx_rate': {'type': ['integer', 'null']}, + 'tx_drop': {'type': ['integer', 'null']}, + 'tx_errors': {'type': ['integer', 'null']}, + 'tx_octets': {'type': ['integer', 'null']}, + 'tx_packets': {'type': ['integer', 'null']}, + 'tx_rate': {'type': ['integer', 'null']}, + }, + 'required': [ + 'mac_address', + 'rx_drop', + 'rx_errors', + 'rx_octets', + 'rx_packets', + 'rx_rate', + 'tx_drop', + 'tx_errors', + 'tx_octets', + 'tx_packets', + 'tx_rate', + ], + }, + }, + 'num_cpus': {'type': ['integer', 'null']}, + 'num_disks': {'type': ['integer', 'null']}, + 'num_nics': {'type': ['integer', 'null']}, + 'state': { + 'type': 'string', + 'enum': [ + 'pending', + 'running', + 'paused', + 'shutdown', + 'crashed', + 'suspended', + ], + }, + 'uptime': {'type': ['integer', 'null']}, + }, + 'required': [ + 'config_drive', + 'cpu_details', + 'disk_details', + 'driver', + 'hypervisor', + 'hypervisor_os', + 'memory_details', + 'nic_details', + 'num_cpus', + 'num_disks', + 'num_nics', + 'state', + 'uptime', + ], +} diff --git a/nova/api/openstack/compute/server_diagnostics.py b/nova/api/openstack/compute/server_diagnostics.py index 755cfeaf9319..803e7d1b238d 100644 --- a/nova/api/openstack/compute/server_diagnostics.py +++ b/nova/api/openstack/compute/server_diagnostics.py @@ -26,6 +26,7 @@ from nova import exception from nova.policies import server_diagnostics as sd_policies +@validation.validated class ServerDiagnosticsController(wsgi.Controller): _view_builder_class = server_diagnostics.ViewBuilder @@ -35,6 +36,8 @@ class ServerDiagnosticsController(wsgi.Controller): @wsgi.expected_errors((400, 404, 409, 501)) @validation.query_schema(schema.index_query) + @validation.response_body_schema(schema.index_response, '2.1', '2.47') + @validation.response_body_schema(schema.index_response_v248, '2.48') def index(self, req, server_id): context = req.environ["nova.context"] instance = common.get_instance(self.compute_api, context, server_id) diff --git a/nova/tests/unit/policies/test_server_diagnostics.py b/nova/tests/unit/policies/test_server_diagnostics.py index 4a4b192baa0f..f8301f7ceb15 100644 --- a/nova/tests/unit/policies/test_server_diagnostics.py +++ b/nova/tests/unit/policies/test_server_diagnostics.py @@ -10,14 +10,13 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock - import fixtures from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import timeutils from nova.api.openstack.compute import server_diagnostics from nova.compute import vm_states +from nova import objects from nova.policies import base as base_policy from nova.policies import server_diagnostics as policies from nova.tests.unit.api.openstack import fakes @@ -38,14 +37,34 @@ class ServerDiagnosticsPolicyTest(base.BasePolicyTest): super(ServerDiagnosticsPolicyTest, self).setUp() self.controller = server_diagnostics.ServerDiagnosticsController() self.req = fakes.HTTPRequest.blank('', version='2.48') - self.controller.compute_api.get_instance_diagnostics = mock.MagicMock() - self.mock_get = self.useFixture( + + self.mock_get_instance = self.useFixture( fixtures.MockPatch('nova.api.openstack.common.get_instance')).mock self.instance = fake_instance.fake_instance_obj( self.project_member_context, project_id=self.project_id, id=1, uuid=uuids.fake_id, vm_state=vm_states.ACTIVE, task_state=None, launched_at=timeutils.utcnow()) - self.mock_get.return_value = self.instance + self.mock_get_instance.return_value = self.instance + + self.mock_get_diagnostics = self.useFixture( + fixtures.MockPatch('nova.compute.api.API.get_instance_diagnostics') + ).mock + self.diagnostics = objects.Diagnostics( + config_drive=False, + state='running', + driver='libvirt', + uptime=5, + hypervisor='hypervisor', + # hypervisor_os is unset + cpu_details=[], + nic_details=[], + disk_details=[], + num_cpus=4, + num_disks=1, + num_nics=1, + memory_details=objects.MemoryDiagnostics(maximum=8192, used=3072), + ) + self.mock_get_diagnostics.return_value = self.diagnostics # With legacy rule, any admin is able get server diagnostics. self.project_admin_authorized_contexts = [ diff --git a/nova/virt/driver.py b/nova/virt/driver.py index a268d435d114..5998838bac6b 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -701,7 +701,7 @@ class ComputeDriver(object): :return: Has a big overlap to the return value of the older interface :func:`get_diagnostics` - :rtype: nova.virt.diagnostics.Diagnostics + :rtype: nova.objects.diagnostics.Diagnostics """ raise NotImplementedError()