Show history by play and add get status action

Show history filtered by plays to get a better view
if a log file contains a lot of playbooks.

Add get_status action in order to get debug information
of a given validation failure

Change-Id: I29ab90647d9ef9c86546065ca16a62c401be1969
This commit is contained in:
Mathieu Bultel 2020-06-29 22:09:24 +02:00 committed by mathieu bultel
parent 47db76f3f3
commit 21a722adac
5 changed files with 106 additions and 12 deletions

View File

@ -159,7 +159,10 @@ VALIDATIONS_LOGS_CONTENTS_LIST = [{
'unreachable': 0 'unreachable': 0
} }
}, },
'validation_output': [] 'validation_output': [{'task': {
'hosts': {u'foo': {}},
'name': u'Check if iscsi.service is enabled',
'status': u'FAILED'}}]
}] }]
VALIDATIONS_DATA = {'Description': 'My Validation One Description', VALIDATIONS_DATA = {'Description': 'My Validation One Description',
@ -169,7 +172,7 @@ VALIDATIONS_DATA = {'Description': 'My Validation One Description',
'parameters': {}} 'parameters': {}}
VALIDATIONS_STATS = {'Last execution date': '2019-11-25 13:40:14', VALIDATIONS_STATS = {'Last execution date': '2019-11-25 13:40:14',
'Number of execution': 'Total: 1, Passed: 1, Failed: 0'} 'Number of execution': 'Total: 1, Passed: 0, Failed: 1'}
FAKE_PLAYBOOK = [{'hosts': 'undercloud', FAKE_PLAYBOOK = [{'hosts': 'undercloud',
'roles': ['advanced_format_512e_support'], 'roles': ['advanced_format_512e_support'],

View File

@ -154,11 +154,20 @@ class TestValidationActions(TestCase):
'Description': 'foo', 'Groups': ['prep', 'pre-deployment'], 'Description': 'foo', 'Groups': ['prep', 'pre-deployment'],
'ID': '512e'} 'ID': '512e'}
data.update({'Last execution date': '2019-11-25 13:40:14', data.update({'Last execution date': '2019-11-25 13:40:14',
'Number of execution': 'Total: 1, Passed: 1, Failed: 0'}) 'Number of execution': 'Total: 1, Passed: 0, Failed: 1'})
validations_show = ValidationActions() validations_show = ValidationActions()
out = validations_show.show_validations('512e') out = validations_show.show_validations('512e')
self.assertEqual(out, data) self.assertEqual(out, data)
@mock.patch('os.path.exists', return_value=False)
def test_validation_show_not_found(self, mock_exists):
validations_show = ValidationActions()
self.assertRaises(
RuntimeError,
validations_show.show_validations,
'512e'
)
@mock.patch('validations_libs.utils.parse_all_validations_on_disk', @mock.patch('validations_libs.utils.parse_all_validations_on_disk',
return_value=fakes.VALIDATIONS_LIST) return_value=fakes.VALIDATIONS_LIST)
@mock.patch('yaml.safe_load', return_value=fakes.GROUP) @mock.patch('yaml.safe_load', return_value=fakes.GROUP)
@ -193,11 +202,12 @@ class TestValidationActions(TestCase):
@mock.patch('six.moves.builtins.open') @mock.patch('six.moves.builtins.open')
def test_show_history(self, mock_open, mock_load, mock_get_log): def test_show_history(self, mock_open, mock_load, mock_get_log):
v_actions = ValidationActions() v_actions = ValidationActions()
col, values = v_actions.show_history('foo') col, values = v_actions.show_history('512e')
self.assertEqual(col, ('UUID', 'Validations', self.assertEqual(col, ('UUID', 'Validations',
'Status', 'Execution at', 'Status', 'Execution at',
'Duration')) 'Duration'))
self.assertEqual(values, [('123', 'foo', 'PASSED', self.assertEqual(values, [('008886df-d297-1eaa-2a74-000000000008',
'512e', 'PASSED',
'2019-11-25T13:40:14.404623Z', '2019-11-25T13:40:14.404623Z',
'0:00:03.753')]) '0:00:03.753')])
@ -213,6 +223,24 @@ class TestValidationActions(TestCase):
self.assertEqual(col, ('UUID', 'Validations', self.assertEqual(col, ('UUID', 'Validations',
'Status', 'Execution at', 'Status', 'Execution at',
'Duration')) 'Duration'))
self.assertEqual(values, [('123', 'foo', 'PASSED', self.assertEqual(values, [('008886df-d297-1eaa-2a74-000000000008',
'512e', 'PASSED',
'2019-11-25T13:40:14.404623Z', '2019-11-25T13:40:14.404623Z',
'0:00:03.753')]) '0:00:03.753')])
@mock.patch('validations_libs.validation_logs.ValidationLogs.'
'get_logfile_by_validation',
return_value=['/tmp/123_foo_2020-03-30T13:17:22.447857Z.json'])
@mock.patch('json.load',
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0])
@mock.patch('six.moves.builtins.open')
def test_get_status(self, mock_open, mock_load, mock_get_log):
v_actions = ValidationActions()
col, values = v_actions.get_status('foo')
self.assertEqual(col, ['name', 'host', 'status', 'task_data'])
self.assertEqual(values, [('Check if iscsi.service is enabled', 'foo',
'FAILED', {})])
def test_get_status_no_param(self):
v_actions = ValidationActions()
self.assertRaises(RuntimeError, v_actions.get_status)

View File

@ -224,3 +224,26 @@ class TestValidationLog(TestCase):
val = ValidationLog( val = ValidationLog(
logfile='/tmp/123_foo_2020-03-30T13:17:22.447857Z.json') logfile='/tmp/123_foo_2020-03-30T13:17:22.447857Z.json')
self.assertTrue(val.is_valid_format()) self.assertTrue(val.is_valid_format())
@mock.patch('json.load',
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0])
@mock.patch('six.moves.builtins.open')
def test_get_plays(self, mock_open, mock_json):
val = ValidationLog(
logfile='/tmp/123_foo_2020-03-30T13:17:22.447857Z.json')
plays = val.get_plays
self.assertEquals(
plays,
[fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0]['plays'][0]['play']])
@mock.patch('json.load',
return_value=fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0])
@mock.patch('six.moves.builtins.open')
def test_get_tasks_data(self, mock_open, mock_json):
val = ValidationLog(
logfile='/tmp/123_foo_2020-03-30T13:17:22.447857Z.json')
tasks_data = val.get_tasks_data
self.assertEquals(
tasks_data,
[fakes.VALIDATIONS_LOGS_CONTENTS_LIST[0]
['validation_output'][0]['task']])

View File

@ -53,6 +53,11 @@ class ValidationActions(object):
# Get validation data: # Get validation data:
vlog = ValidationLogs(log_path) vlog = ValidationLogs(log_path)
data = v_utils.get_validations_data(validation, self.validation_path) data = v_utils.get_validations_data(validation, self.validation_path)
if not data:
msg = "Validation {} not found in the path: {}".format(
validation,
self.validation_path)
raise RuntimeError(msg)
logfiles = vlog.get_all_logfiles_content() logfiles = vlog.get_all_logfiles_content()
format = vlog.get_validations_stats(logfiles) format = vlog.get_validations_stats(logfiles)
data.update(format) data.update(format)
@ -148,9 +153,10 @@ class ValidationActions(object):
f.write(params) f.write(params)
return params return params
def show_history(self, validation_id=None, extension='json'): def show_history(self, validation_id=None, extension='json',
log_path=constants.VALIDATIONS_LOG_BASEDIR):
"""Return validations history""" """Return validations history"""
vlogs = ValidationLogs(self.validation_path) vlogs = ValidationLogs(log_path)
logs = (vlogs.get_logfile_by_validation(validation_id) logs = (vlogs.get_logfile_by_validation(validation_id)
if validation_id else vlogs.get_all_logfiles(extension)) if validation_id else vlogs.get_all_logfiles(extension))
@ -158,11 +164,35 @@ class ValidationActions(object):
column_name = ('UUID', 'Validations', column_name = ('UUID', 'Validations',
'Status', 'Execution at', 'Status', 'Execution at',
'Duration') 'Duration')
for log in logs: for log in logs:
vlog = ValidationLog(logfile=log) vlog = ValidationLog(logfile=log)
if vlog.is_valid_format(): if vlog.is_valid_format():
values.append((vlog.get_uuid, vlog.validation_id, for play in vlog.get_plays:
vlog.get_status, vlog.get_start_time, values.append((play['id'], play['validation_id'],
vlog.get_duration)) vlog.get_status,
play['duration'].get('start'),
play['duration'].get('time_elapsed')))
return (column_name, values)
def get_status(self, validation_id=None, uuid=None, status='FAILED',
log_path=constants.VALIDATIONS_LOG_BASEDIR):
"""Return validations execution details by status"""
vlogs = ValidationLogs(log_path)
if validation_id:
logs = vlogs.get_logfile_by_validation(validation_id)
elif uuid:
logs = vlogs.get_logfile_by_uuid(uuid)
else:
raise RuntimeError("You need to provide a validation_id or a uuid")
values = []
column_name = ['name', 'host', 'status', 'task_data']
for log in logs:
vlog = ValidationLog(logfile=log)
if vlog.is_valid_format():
for task in vlog.get_tasks_data:
if task['status'] == status:
for host in task['hosts']:
values.append((task['name'], host, task['status'],
task['hosts'][host]))
return (column_name, values) return (column_name, values)

View File

@ -150,6 +150,16 @@ class ValidationLog(object):
play in self.content['plays']] play in self.content['plays']]
return ', '.join(filter(None, start_time)) return ', '.join(filter(None, start_time))
@property
def get_plays(self):
"""Return a list of Playbook data"""
return [play['play'] for play in self.content['plays']]
@property
def get_tasks_data(self):
"""Return a list of task from validation output"""
return [output['task'] for output in self.content['validation_output']]
class ValidationLogs(object): class ValidationLogs(object):