Merge "api: Add response body schemas for server usage audit log APIs"

This commit is contained in:
Zuul
2025-12-15 18:03:27 +00:00
committed by Gerrit Code Review
7 changed files with 107 additions and 38 deletions

View File

@@ -1,25 +1,25 @@
{ {
"instance_usage_audit_logs": { "instance_usage_audit_logs": {
"hosts_not_run": [ "hosts_not_run": [
"samplehost3" "329fa448-f6bb-4e72-b954-faa66c30d4fa"
], ],
"log": { "log": {
"samplehost0": { "2c8ef37b-f0cc-4a9e-92a6-32df0095cb12": {
"errors": 1, "errors": 1,
"instances": 1, "instances": 1,
"message": "Instance usage audit ran for host samplehost0, 1 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 2c8ef37b-f0cc-4a9e-92a6-32df0095cb12, 1 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
}, },
"samplehost1": { "60dbe74d-0cf3-419b-83f5-407e4b78c7b4": {
"errors": 1, "errors": 1,
"instances": 2, "instances": 2,
"message": "Instance usage audit ran for host samplehost1, 2 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 60dbe74d-0cf3-419b-83f5-407e4b78c7b4, 2 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
}, },
"samplehost2": { "2aa90c00-23eb-4da6-aff9-eda66bb56182": {
"errors": 1, "errors": 1,
"instances": 3, "instances": 3,
"message": "Instance usage audit ran for host samplehost2, 3 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 2aa90c00-23eb-4da6-aff9-eda66bb56182, 3 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
} }
}, },

View File

@@ -1,25 +1,25 @@
{ {
"instance_usage_audit_log": { "instance_usage_audit_log": {
"hosts_not_run": [ "hosts_not_run": [
"samplehost3" "329fa448-f6bb-4e72-b954-faa66c30d4fa"
], ],
"log": { "log": {
"samplehost0": { "2c8ef37b-f0cc-4a9e-92a6-32df0095cb12": {
"errors": 1, "errors": 1,
"instances": 1, "instances": 1,
"message": "Instance usage audit ran for host samplehost0, 1 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 2c8ef37b-f0cc-4a9e-92a6-32df0095cb12, 1 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
}, },
"samplehost1": { "60dbe74d-0cf3-419b-83f5-407e4b78c7b4": {
"errors": 1, "errors": 1,
"instances": 2, "instances": 2,
"message": "Instance usage audit ran for host samplehost1, 2 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 60dbe74d-0cf3-419b-83f5-407e4b78c7b4, 2 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
}, },
"samplehost2": { "2aa90c00-23eb-4da6-aff9-eda66bb56182": {
"errors": 1, "errors": 1,
"instances": 3, "instances": 3,
"message": "Instance usage audit ran for host samplehost2, 3 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 2aa90c00-23eb-4da6-aff9-eda66bb56182, 3 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
} }
}, },

View File

@@ -27,6 +27,7 @@ from nova.policies import instance_usage_audit_log as iual_policies
from nova import utils from nova import utils
@validation.validated
class InstanceUsageAuditLogController(wsgi.Controller): class InstanceUsageAuditLogController(wsgi.Controller):
def __init__(self): def __init__(self):
@@ -35,6 +36,7 @@ class InstanceUsageAuditLogController(wsgi.Controller):
@wsgi.expected_errors(()) @wsgi.expected_errors(())
@validation.query_schema(schema.index_query) @validation.query_schema(schema.index_query)
@validation.response_body_schema(schema.index_response)
def index(self, req): def index(self, req):
context = req.environ['nova.context'] context = req.environ['nova.context']
context.can(iual_policies.BASE_POLICY_NAME % 'list', target={}) context.can(iual_policies.BASE_POLICY_NAME % 'list', target={})
@@ -43,16 +45,17 @@ class InstanceUsageAuditLogController(wsgi.Controller):
@wsgi.expected_errors(400) @wsgi.expected_errors(400)
@validation.query_schema(schema.show_query) @validation.query_schema(schema.show_query)
@validation.response_body_schema(schema.show_response)
def show(self, req, id): def show(self, req, id):
context = req.environ['nova.context'] context = req.environ['nova.context']
context.can(iual_policies.BASE_POLICY_NAME % 'show', target={}) context.can(iual_policies.BASE_POLICY_NAME % 'show', target={})
try: try:
if '.' in id: if '.' in id:
before_date = datetime.datetime.strptime(str(id), before_date = datetime.datetime.strptime(
"%Y-%m-%d %H:%M:%S.%f") str(id), "%Y-%m-%d %H:%M:%S.%f")
else: else:
before_date = datetime.datetime.strptime(str(id), before_date = datetime.datetime.strptime(
"%Y-%m-%d %H:%M:%S") str(id), "%Y-%m-%d %H:%M:%S")
except ValueError: except ValueError:
msg = _("Invalid timestamp for date %s") % id msg = _("Invalid timestamp for date %s") % id
raise webob.exc.HTTPBadRequest(explanation=msg) raise webob.exc.HTTPBadRequest(explanation=msg)

View File

@@ -21,3 +21,66 @@ show_query = {
'properties': {}, 'properties': {},
'additionalProperties': True, 'additionalProperties': True,
} }
_instance_usage_audit_log_response = {
'type': 'object',
'properties': {
'hosts_not_run': {
'type': 'array',
'items': {'type': 'string', 'format': 'uuid'},
},
'log': {
'type': 'object',
'additionalProperties': {
'instances': {'type': 'integer', 'minimum': 0},
'errors': {'type': 'integer', 'minimum': 0},
'message': {'type': 'string'},
'state': {'type': 'string', 'enum': ['RUNNING', 'DONE']},
},
},
'num_hosts': {'type': 'integer', 'minimum': 0},
'num_hosts_done': {'type': 'integer', 'minimum': 0},
'num_hosts_not_run': {'type': 'integer', 'minimum': 0},
'num_hosts_running': {'type': 'integer', 'minimum': 0},
'overall_status': {'type': 'string'},
'period_beginning': {'type': 'string', 'format': 'date-time'},
'period_ending': {'type': 'string', 'format': 'date-time'},
'total_errors': {'type': 'integer', 'minimum': 0},
'total_instances': {'type': 'integer', 'minimum': 0},
},
'required': [
'hosts_not_run',
'log',
'num_hosts',
'num_hosts_done',
'num_hosts_not_run',
'num_hosts_running',
'overall_status',
'period_beginning',
'period_ending',
'total_errors',
'total_instances',
],
'additionalProperties': False,
}
index_response = {
'type': 'object',
'properties': {
# NOTE(stephenfin): Yes, this is correct: the index response is
# identical to the show response. In fact, the show response is really
# the index response with a 'before' filter and a singular key
'instance_usage_audit_logs': _instance_usage_audit_log_response,
},
'required': ['instance_usage_audit_logs'],
'additionalProperties': False,
}
show_response = {
'type': 'object',
'properties': {
'instance_usage_audit_log': _instance_usage_audit_log_response,
},
'required': ['instance_usage_audit_log'],
'additionalProperties': False,
}

View File

@@ -1,25 +1,25 @@
{ {
"instance_usage_audit_logs": { "instance_usage_audit_logs": {
"hosts_not_run": [ "hosts_not_run": [
"samplehost3" "329fa448-f6bb-4e72-b954-faa66c30d4fa"
], ],
"log": { "log": {
"samplehost0": { "2c8ef37b-f0cc-4a9e-92a6-32df0095cb12": {
"errors": 1, "errors": 1,
"instances": 1, "instances": 1,
"message": "Instance usage audit ran for host samplehost0, 1 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 2c8ef37b-f0cc-4a9e-92a6-32df0095cb12, 1 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
}, },
"samplehost1": { "60dbe74d-0cf3-419b-83f5-407e4b78c7b4": {
"errors": 1, "errors": 1,
"instances": 2, "instances": 2,
"message": "Instance usage audit ran for host samplehost1, 2 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 60dbe74d-0cf3-419b-83f5-407e4b78c7b4, 2 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
}, },
"samplehost2": { "2aa90c00-23eb-4da6-aff9-eda66bb56182": {
"errors": 1, "errors": 1,
"instances": 3, "instances": 3,
"message": "Instance usage audit ran for host samplehost2, 3 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 2aa90c00-23eb-4da6-aff9-eda66bb56182, 3 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
} }
}, },

View File

@@ -1,25 +1,25 @@
{ {
"instance_usage_audit_log": { "instance_usage_audit_log": {
"hosts_not_run": [ "hosts_not_run": [
"samplehost3" "329fa448-f6bb-4e72-b954-faa66c30d4fa"
], ],
"log": { "log": {
"samplehost0": { "2c8ef37b-f0cc-4a9e-92a6-32df0095cb12": {
"errors": 1, "errors": 1,
"instances": 1, "instances": 1,
"message": "Instance usage audit ran for host samplehost0, 1 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 2c8ef37b-f0cc-4a9e-92a6-32df0095cb12, 1 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
}, },
"samplehost1": { "60dbe74d-0cf3-419b-83f5-407e4b78c7b4": {
"errors": 1, "errors": 1,
"instances": 2, "instances": 2,
"message": "Instance usage audit ran for host samplehost1, 2 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 60dbe74d-0cf3-419b-83f5-407e4b78c7b4, 2 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
}, },
"samplehost2": { "2aa90c00-23eb-4da6-aff9-eda66bb56182": {
"errors": 1, "errors": 1,
"instances": 3, "instances": 3,
"message": "Instance usage audit ran for host samplehost2, 3 instances in 0.01 seconds.", "message": "Instance usage audit ran for host 2aa90c00-23eb-4da6-aff9-eda66bb56182, 3 instances in 0.01 seconds.",
"state": "DONE" "state": "DONE"
} }
}, },

View File

@@ -28,13 +28,16 @@ class InstanceUsageAuditLogJsonTest(api_sample_base.ApiSampleTestBaseV21):
def setUp(self): def setUp(self):
super(InstanceUsageAuditLogJsonTest, self).setUp() super(InstanceUsageAuditLogJsonTest, self).setUp()
hosts = (
'2c8ef37b-f0cc-4a9e-92a6-32df0095cb12',
'60dbe74d-0cf3-419b-83f5-407e4b78c7b4',
'2aa90c00-23eb-4da6-aff9-eda66bb56182',
'329fa448-f6bb-4e72-b954-faa66c30d4fa',
)
def fake_service_get_all(self, context, def fake_service_get_all(self, context,
filters=None, set_zones=False): filters=None, set_zones=False):
services = [objects.Service(host='samplehost0'), return [objects.Service(host=host) for host in hosts]
objects.Service(host='samplehost1'),
objects.Service(host='samplehost2'),
objects.Service(host='samplehost3')]
return services
def fake_utcnow(with_timezone=False): def fake_utcnow(with_timezone=False):
# It is not UTC time, but no effect for testing # It is not UTC time, but no effect for testing
@@ -46,7 +49,7 @@ class InstanceUsageAuditLogJsonTest(api_sample_base.ApiSampleTestBaseV21):
fake_service_get_all) fake_service_get_all)
for i in range(0, 3): for i in range(0, 3):
self._create_task_log('samplehost%d' % i, i + 1) self._create_task_log(hosts[i], i + 1)
def _create_task_log(self, host, num_instances): def _create_task_log(self, host, num_instances):
task_log = objects.TaskLog(context.get_admin_context()) task_log = objects.TaskLog(context.get_admin_context())