Add Docstrings to validations_libs/validations_logs.py file

This patch also adds new unittests.

Change-Id: I6f299db1b22d98fdabc148e297098e023cf863bd
Signed-off-by: Gael Chamoulaud (Strider) <gchamoul@redhat.com>
This commit is contained in:
Gael Chamoulaud (Strider) 2020-11-16 15:53:00 +01:00
parent c1115710c7
commit 9f644f0dd7
No known key found for this signature in database
GPG Key ID: 4119D0305C651D66
2 changed files with 261 additions and 33 deletions

View File

@ -37,6 +37,24 @@ class TestValidationLog(TestCase):
self.assertEquals(val.validation_id, 'foo') self.assertEquals(val.validation_id, 'foo')
self.assertEquals(val.datetime, '2020-03-30T13:17:22.447857Z') self.assertEquals(val.datetime, '2020-03-30T13:17:22.447857Z')
@mock.patch('json.load')
@mock.patch('six.moves.builtins.open')
def test_validation_uuid_wo_validation_id(self, mock_open, mock_json):
with self.assertRaises(Exception) as exc_mgr:
ValidationLog(uuid='123')
self.assertEqual('When not using logfile argument, the uuid and '
'validation_id have to be set',
str(exc_mgr.exception))
@mock.patch('json.load')
@mock.patch('six.moves.builtins.open')
def test_validation_validation_id_wo_uuid(self, mock_open, mock_json):
with self.assertRaises(Exception) as exc_mgr:
ValidationLog(validation_id='foo')
self.assertEqual('When not using logfile argument, the uuid and '
'validation_id have to be set',
str(exc_mgr.exception))
@mock.patch('json.load') @mock.patch('json.load')
@mock.patch('six.moves.builtins.open') @mock.patch('six.moves.builtins.open')
def test_validation_underscore_validation_id(self, mock_open, mock_json): def test_validation_underscore_validation_id(self, mock_open, mock_json):

View File

@ -25,22 +25,51 @@ LOG = logging.getLogger(__name__ + ".validation_logs")
class ValidationLog(object): class ValidationLog(object):
"""An object for encapsulating a Validation Log file"""
def __init__(self, uuid=None, validation_id=None, logfile=None, def __init__(self, uuid=None, validation_id=None, logfile=None,
log_path=constants.VALIDATIONS_LOG_BASEDIR, log_path=constants.VALIDATIONS_LOG_BASEDIR,
extension='json'): extension='json'):
"""Wrap the Validation Log file
:param uuid: The uuid of the validation execution
:type uuid: ``string``
:param validation_id: The ID of the validation
:type validation_id: ``string``
:param logfile: The absolute path of the log file
:type logfile: ``string`
:param log_path: The absolute path of the logs directory
:type log_path: ``string``
:param extension: The file extension (Default to 'json')
:type extension: ````
"""
# Set properties # Set properties
self.uuid = uuid self.uuid = uuid
self.validation_id = validation_id self.validation_id = validation_id
self.log_path = log_path self.log_path = log_path
self.extension = extension self.extension = extension
self.content = {}
self.name = None
self.datetime = None
if not logfile and (not uuid or not validation_id):
raise Exception(
'When not using logfile argument, the uuid and '
'validation_id have to be set'
)
# Get full path and content # Get full path and content
if logfile: if logfile:
full_path = logfile full_path = logfile
else: else:
full_path = self.get_log_path() if uuid and validation_id:
self.content = self._get_content(full_path) full_path = self.get_log_path()
self.name = os.path.splitext(os.path.basename(full_path))[0]
if full_path:
self.content = self._get_content(full_path)
self.name = os.path.splitext(os.path.basename(full_path))[0]
self.datetime = self.name.rsplit('_', 1)[-1]
# if we have a log file then extract uuid, validation_id and timestamp # if we have a log file then extract uuid, validation_id and timestamp
if logfile: if logfile:
try: try:
@ -69,44 +98,94 @@ class ValidationLog(object):
self.extension))[0] self.extension))[0]
def is_valid_format(self): def is_valid_format(self):
""" Return True if the log file is a valid validation format """ """Return True if the log file is a valid validation format
The validation log file has to contain three level of data.
- ``plays`` will contain the Ansible execution logs of the playbooks
- ``stat`` will contain the statistics for each targeted hosts
- ``validation_output`` will contain only the warning or failed tasks
.. code:: bash
{
'plays': [],
'stats': {},
'validation_output': []
}
:return: ``True`` if the log file is valid, ``False`` if not.
:rtype: ``boolean``
"""
validation_keys = ['stats', 'validation_output', 'plays'] validation_keys = ['stats', 'validation_output', 'plays']
return bool(set(validation_keys).intersection(self.content.keys())) return bool(set(validation_keys).intersection(self.content.keys()))
@property @property
def get_logfile_infos(self): def get_logfile_infos(self):
""" """Return log file information from the log file basename
Return log file information:
uuid, :return: A list with the UUID, the validation name and the
validation_id, datetime of the log file
datetime :rtype: ``list``
:Example:
>>> logfile = '/tmp/123_foo_2020-03-30T13:17:22.447857Z.json'
>>> val = ValidationLog(logfile=logfile)
>>> print(val.get_logfile_infos)
['123', 'foo', '2020-03-30T13:17:22.447857Z']
""" """
return self.name.replace('.{}'.format(self.extension), '').split('_') return self.name.replace('.{}'.format(self.extension), '').split('_')
@property @property
def get_logfile_datetime(self): def get_logfile_datetime(self):
"""Return log file datetime from a UUID and a validation ID""" """Return log file datetime from a UUID and a validation ID
:return: The datetime of the log file
:rtype: ``list``
:Example:
>>> logfile = '/tmp/123_foo_2020-03-30T13:17:22.447857Z.json'
>>> val = ValidationLog(logfile=logfile)
>>> print(val.get_logfile_datetime)
['2020-03-30T13:17:22.447857Z']
"""
return self.name.replace('.{}'.format(self.extension), return self.name.replace('.{}'.format(self.extension),
'').split('_')[2] '').split('_')[2]
@property @property
def get_logfile_content(self): def get_logfile_content(self):
"""Return logfile content as a dict""" """Return logfile content
:rtype: ``dict``
"""
return self.content return self.content
@property @property
def get_uuid(self): def get_uuid(self):
"""Return log uuid""" """Return log uuid
:rtype: ``string``
"""
return self.uuid return self.uuid
@property @property
def get_validation_id(self): def get_validation_id(self):
"""Return validation id""" """Return validation id
:rtype: ``string``
"""
return self.validation_id return self.validation_id
@property @property
def get_status(self): def get_status(self):
"""Return validation status""" """Return validation status
:return: 'FAILED' if there is failure(s), 'PASSED' if not.
If no tasks have been executed, it returns 'NOT_RUN'.
:rtype: ``string``
"""
failed = 0 failed = 0
for h in self.content['stats'].keys(): for h in self.content['stats'].keys():
if self.content['stats'][h].get('failures'): if self.content['stats'][h].get('failures'):
@ -115,13 +194,28 @@ class ValidationLog(object):
@property @property
def get_host_group(self): def get_host_group(self):
"""Return host group""" """Return host group
:return: A comma-separated list of host(s)
:rtype: ``string``
"""
return ', '.join([play['play'].get('host') for return ', '.join([play['play'].get('host') for
play in self.content['plays']]) play in self.content['plays']])
@property @property
def get_hosts_status(self): def get_hosts_status(self):
"""Return hosts status""" """Return status by host(s)
:return: A comma-separated string of host with its status
:rtype: ``string``
:Example:
>>> logfile = '/tmp/123_foo_2020-03-30T13:17:22.447857Z.json'
>>> val = ValidationLog(logfile=logfile)
>>> print(val.get_hosts_status)
'localhost,PASSED, webserver1,FAILED, webserver2,PASSED'
"""
hosts = [] hosts = []
for h in self.content['stats'].keys(): for h in self.content['stats'].keys():
if self.content['stats'][h].get('failures'): if self.content['stats'][h].get('failures'):
@ -132,20 +226,53 @@ class ValidationLog(object):
@property @property
def get_unreachable_hosts(self): def get_unreachable_hosts(self):
"""Return unreachable hosts""" """Return unreachable hosts
:return: A list of unreachable host(s)
:rtype: ``string``
:Example:
- Multiple unreachable hosts
>>> logfile = '/tmp/123_foo_2020-03-30T13:17:22.447857Z.json'
>>> val = ValidationLog(logfile=logfile)
>>> print(val.get_unreachable_hosts)
'localhost, webserver2'
- Only one unreachable host
>>> logfile = '/tmp/123_foo_2020-03-30T13:17:22.447857Z.json'
>>> val = ValidationLog(logfile=logfile)
>>> print(val.get_unreachable_hosts)
'localhost'
- No unreachable host
>>> logfile = '/tmp/123_foo_2020-03-30T13:17:22.447857Z.json'
>>> val = ValidationLog(logfile=logfile)
>>> print(val.get_unreachable_hosts)
''
"""
return ', '.join(h for h in self.content['stats'].keys() return ', '.join(h for h in self.content['stats'].keys()
if self.content['stats'][h].get('unreachable')) if self.content['stats'][h].get('unreachable'))
@property @property
def get_duration(self): def get_duration(self):
"""Return duration of Ansible runtime""" """Return duration of Ansible runtime
:rtype: ``string``
"""
duration = [play['play']['duration'].get('time_elapsed') for duration = [play['play']['duration'].get('time_elapsed') for
play in self.content['plays']] play in self.content['plays']]
return ', '.join(filter(None, duration)) return ', '.join(filter(None, duration))
@property @property
def get_start_time(self): def get_start_time(self):
"""Return Ansible start time""" """Return Ansible start time
:rtype: ``string``
"""
start_time = [play['play']['duration'].get('start') for start_time = [play['play']['duration'].get('start') for
play in self.content['plays']] play in self.content['plays']]
return ', '.join(filter(None, start_time)) return ', '.join(filter(None, start_time))
@ -162,6 +289,7 @@ class ValidationLog(object):
class ValidationLogs(object): class ValidationLogs(object):
"""An object for encapsulating the Validation Log files"""
def __init__(self, logs_path=constants.VALIDATIONS_LOG_BASEDIR): def __init__(self, logs_path=constants.VALIDATIONS_LOG_BASEDIR):
self.logs_path = logs_path self.logs_path = logs_path
@ -175,51 +303,113 @@ class ValidationLogs(object):
raise IOError(msg) raise IOError(msg)
def get_logfile_by_validation(self, validation_id): def get_logfile_by_validation(self, validation_id):
"""Return logfiles by validation_id""" """Return logfiles by validation_id
:param validation_id: The ID of the validation
:type validation_id: ``string``
:return: The list of the log files for a validation
:rtype: ``list``
"""
return glob.glob("{}/*_{}_*".format(self.logs_path, validation_id)) return glob.glob("{}/*_{}_*".format(self.logs_path, validation_id))
def get_logfile_content_by_validation(self, validation_id): def get_logfile_content_by_validation(self, validation_id):
"""Return logfiles content by validation_id""" """Return logfiles content by validation_id
:param validation_id: The ID of the validation
:type validation_id: ``string``
:return: The list of the log files contents for a validation
:rtype: ``list``
"""
log_files = glob.glob("{}/*_{}_*".format(self.logs_path, log_files = glob.glob("{}/*_{}_*".format(self.logs_path,
validation_id)) validation_id))
return [self._get_content(log) for log in log_files] return [self._get_content(log) for log in log_files]
def get_logfile_by_uuid(self, uuid): def get_logfile_by_uuid(self, uuid):
"""Return logfiles by uuid""" """Return logfiles by uuid
:param uuid: The UUID of the validation execution
:type uuid: ``string``
:return: The list of the log files by UUID
:rtype: ``list``
"""
return glob.glob("{}/{}_*".format(self.logs_path, uuid)) return glob.glob("{}/{}_*".format(self.logs_path, uuid))
def get_logfile_content_by_uuid(self, uuid): def get_logfile_content_by_uuid(self, uuid):
"""Return logfiles content by uuid""" """Return logfiles content by uuid
:param uuid: The UUID of the validation execution
:type uuid: ``string``
:return: The list of the log files contents by UUID
:rtype: ``list``
"""
log_files = glob.glob("{}/{}_*".format(self.logs_path, uuid)) log_files = glob.glob("{}/{}_*".format(self.logs_path, uuid))
return [self._get_content(log) for log in log_files] return [self._get_content(log) for log in log_files]
def get_logfile_by_uuid_validation_id(self, uuid, validation_id): def get_logfile_by_uuid_validation_id(self, uuid, validation_id):
"""Return logfiles by uuid""" """Return logfiles by uuid and validation_id
:param uuid: The UUID of the validation execution
:type uuid: ``string``
:param validation_id: The ID of the validation
:type validation_id: ``string``
:return: A list of the log files by UUID and validation_id
:rtype: ``list``
"""
return glob.glob("{}/{}_{}_*".format(self.logs_path, uuid, return glob.glob("{}/{}_{}_*".format(self.logs_path, uuid,
validation_id)) validation_id))
def get_logfile_content_by_uuid_validation_id(self, uuid, validation_id): def get_logfile_content_by_uuid_validation_id(self, uuid, validation_id):
"""Return logfiles content filter by uuid and content""" """Return logfiles content filter by uuid and validation_id
:param uuid: The UUID of the validation execution
:type uuid: ``string``
:param validation_id: The ID of the validation
:type validation_id: ``string``
:return: A list of the log files content by UUID and validation_id
:rtype: ``list``
"""
log_files = glob.glob("{}/{}_{}_*".format(self.logs_path, uuid, log_files = glob.glob("{}/{}_{}_*".format(self.logs_path, uuid,
validation_id)) validation_id))
return [self._get_content(log) for log in log_files] return [self._get_content(log) for log in log_files]
def get_all_logfiles(self, extension='json'): def get_all_logfiles(self, extension='json'):
"""Return logfiles from logs_path""" """Return logfiles from logs_path
:param extension: The extension file (Defaults to 'json')
:type extension: ``string``
:return: A list of the absolute path log files
:rtype: ``list``
"""
return [join(self.logs_path, f) for f in os.listdir(self.logs_path) if return [join(self.logs_path, f) for f in os.listdir(self.logs_path) if
os.path.isfile(join(self.logs_path, f)) and os.path.isfile(join(self.logs_path, f)) and
extension in os.path.splitext(join(self.logs_path, f))[1]] extension in os.path.splitext(join(self.logs_path, f))[1]]
def get_all_logfiles_content(self): def get_all_logfiles_content(self):
"""Return logfiles content filter by uuid and content""" """Return logfiles content
:return: A list of the contents of every log files available
:rtype: ``list``
"""
return [self._get_content(join(self.logs_path, f)) return [self._get_content(join(self.logs_path, f))
for f in os.listdir(self.logs_path) for f in os.listdir(self.logs_path)
if os.path.isfile(join(self.logs_path, f))] if os.path.isfile(join(self.logs_path, f))]
def get_validations_stats(self, logs): def get_validations_stats(self, logs):
""" """Return validations stats from log files
Return validations stats from log files
logs: list of dict :param logs: A list of log file contents
:type logs: ``list``
:return: Information about validation statistics.
``last execution date`` and ``number of execution``
:rtype: ``dict``
""" """
if not isinstance(logs, list): if not isinstance(logs, list):
logs = [logs] logs = [logs]
@ -252,9 +442,29 @@ class ValidationLogs(object):
failed_number)} failed_number)}
def get_results(self, uuid, validation_id=None): def get_results(self, uuid, validation_id=None):
""" """Return a list of validation results by uuid
Return a list of validation results by uuid Can be filter by validation_id
Can be filter by validation_id
:param uuid: The UUID of the validation execution
:type uuid: ``string` or ``list``
:param validation_id: The ID of the validation
:type validation_id: ``string``
:return: A list of the log files content by UUID and validation_id
:rtype: ``list``
:Example:
>>> v_logs = ValidationLogs()
>>> uuid = '78df1c3f-dfc3-4a1f-929e-f51762e67700'
>>> print(v_logs.get_results(uuid=uuid)
[{'Duration': '0:00:00.514',
'Host_Group': 'undercloud,Controller',
'Status': 'FAILED',
'Status_by_Host': 'undercloud,FAILED, underclou1d,FAILED',
'UUID': '78df1c3f-dfc3-4a1f-929e-f51762e67700',
'Unreachable_Hosts': 'undercloud',
'Validations': 'check-cpu'}]
""" """
if isinstance(uuid, list): if isinstance(uuid, list):
results = [] results = []