From 28b8849da57d8c470ba2def78c96131388913dfb Mon Sep 17 00:00:00 2001 From: Lukas Ruzicka Date: Feb 05 2019 12:30:00 +0000 Subject: Fix the reported traceback The script was failing, because the DNF output changed and some new strings appeared which it could not handle. Now it can parse the output again. FIXES #1. Logging support was added to log some of the information. FIXES #2. --- diff --git a/modular_functions.py b/modular_functions.py index b6883f0..26a26ba 100755 --- a/modular_functions.py +++ b/modular_functions.py @@ -16,6 +16,7 @@ or any correctness of installed or removed RPM packages. import argparse import json +import logging import subprocess import sys @@ -25,6 +26,7 @@ class TestSuite: self.modlist = {} self.results = {} self.outputs = {} + logging.info('The test suite has been initialized.') def store_results(self, key, value): if key in self.results.keys(): @@ -34,23 +36,44 @@ class TestSuite: else: self.results[key] = [value] + logging.debug('The store_results method has stored the results into self.results.') def module_list(self): raw = subprocess.run(['dnf', 'module', 'list'], capture_output=True) result = {} + if raw.returncode == 0: result['dnf_module_list'] = 'pass' + logging.info('The DNF operation of module_list ran successfully (0)') else: result['dnf_module_list'] = raw.stderr.decode('utf-8') + logging.warning('The DNF operation of module_list did not run successfuly!') raw = raw.stdout.decode('utf-8') - self.outputs['dnf_module_list'] = raw - raw = raw.split('\n')[3:-3] - raw = [x for x in raw if x[0] != ' '] - strip = [x.strip().split(' ') for x in raw] + + if raw == '': + logging.warning('The DNF output is empty.') + else: + logging.debug('The raw DNF output is:' + raw) + + preprocessed = raw.split('\n')[3:-3] + logging.debug('The preprocessed DNF output is:' + str('\n'.join(preprocessed))) + + processed = [] + try: # This gets rid of all descriptive lines in the DNF output + for x in preprocessed: + if x != '' and 'Fedora' not in x and 'Name' not in x: + processed.append(x) + logging.debug('The cleaned DNF output is:' + str('\n'.join(processed))) + except IndexError: + logging.warning('The DNF output could not be properly parsed.') + raise + + strip = [x.strip().split(' ') for x in processed] # Here we split the output lines into columns mods = [] - for line in strip: + + for line in strip: # Gets the modules lines from the output into a list of modules module = [] for char in line: if char == '' or char == '...': @@ -58,12 +81,13 @@ class TestSuite: else: module.append(char.strip(',')) mods.append(module) - for module in mods: + + for module in mods: # This will parse the module lines and chunk it into modules, streams, etc. try: name = module[0] stream = module[1] except IndexError: - pass + logging.warning('List of modules seems to be empty.') if name in self.modlist.keys(): value = self.modlist[name] if isinstance(value, list): @@ -76,7 +100,9 @@ class TestSuite: self.modlist[name] = vlist else: self.modlist[name] = stream + self.store_results('dnf module list', result) + logging.info('The "dnf module list" operation was succesful.') return self.modlist @@ -88,8 +114,10 @@ class TestSuite: self.outputs[operation] = raw.stdout.decode('utf-8') if raw.returncode == 0: result[mod] = 'pass' + logging.info(f"The operation {key} has finished successfully.") else: result[mod] = raw.stderr.decode('utf-8') + logging.warning(f"The operation {key} has NOT finished successfully.") self.store_results(f'dnf module {operation}', result) return result @@ -110,8 +138,10 @@ class TestSuite: break else: result[mod] = 'no' + logging.info(f"The operation {key} has finished successfully.") else: result[key] = raw.stderr.decode('utf-8') + logging.warning(f"The operation {key} has NOT finished successfully.") self.store_results(key, result) return result @@ -130,8 +160,10 @@ class TestSuite: pass if c > 0: return (1, c) + logging.debug('The string was found in the DNF output.') else: return (0, c) + logging.debug('The string was NOT found in the DNF output.') class ModuleTest: @@ -159,9 +191,11 @@ class ModuleTest: if result == True: print(f'{key} exists in modules => yes') self.overall['list'] = 'pass' + logging.info(f"The result of {key} exists in modules is PASS.") else: print(f'{key} exists in modules => no') self.overall['list'] = 'fail' + logging.info(f"The result of {key} exists in modules is FAIL.") return(result) def check_install(self, module, stream): @@ -170,8 +204,10 @@ class ModuleTest: res1 = self.suite.is_listed(module, stream, 'installed') if res1[key] == 'pass': self.overall['checkinstall'] = 'pass' + logging.info(f"The result of {key} is PASS.") else: self.overall['checkinstall'] = 'fail' + logging.info(f"The result of {key} is FAIL.") print(' ') @@ -186,11 +222,14 @@ class ModuleTest: print(f"{key} is listed in --disabled =>", res3[key]) if res1[key] == 'pass' and res2[key] == 'yes' and res3[key] == 'no': self.overall['enable'] = 'pass' + logging.info(f"The result of enabling {key} is PASS.") else: if self.fail == 'hard': self.overall['enable'] = 'fail' + logging.info(f"The result of enabling {key} is FAIL.") else: self.overall['enable'] = 'soft' + logging.info(f"The result of enabling {key} is SOFTFAIL.") print('') def disable_module(self, module, stream): @@ -208,11 +247,14 @@ class ModuleTest: print(f"{key} is listed in --installed =>", res4[key]) if res1[key] == 'pass' and res2[key] == 'yes' and res3[key] == 'no' and res4[key] == 'no': self.overall['disable'] = 'pass' + logging.info(f"The result of disabling {key} is PASS.") else: if self.fail == 'hard': self.overall['disable'] = 'fail' + logging.info(f"The result of disabling {key} is FAIL.") else: self.overall['disable'] = 'soft' + logging.info(f"The result of disabling {key} is SOFTFAIL.") print('') def install_module(self, module, stream): @@ -228,11 +270,14 @@ class ModuleTest: print(f"{key} is listed in --disabled =>", res4[key]) if res1[key] == 'pass' and res2[key] == 'yes' and res3[key] == 'yes' and res4[key] == 'no': self.overall['install'] = 'pass' + logging.info(f"The result of installing {key} is PASS.") else: if self.fail == 'hard': self.overall['install'] = 'fail' + logging.info(f"The result of installing {key} is FAIL.") else: self.overall['install'] = 'soft' + logging.info(f"The result of installing {key} is SOFTFAIL.") print('') def remove_module(self, module, stream): @@ -247,11 +292,14 @@ class ModuleTest: print(f"{key} is listed in --installed =>", res2[key]) if res1[key] == 'pass' and res2[key] == 'no': self.overall['remove'] = 'pass' + logging.info(f"The result of removing {key} is PASS.") else: if self.fail == 'hard': self.overall['remove'] = 'fail' + logging.info(f"The result of removing {key} is FAIL.") else: self.overall['remove'] = 'soft' + logging.info(f"The result of removing {key} is SOFTFAIL.") print('') def switch_stream(self, module, oldstr, newstr): @@ -271,11 +319,14 @@ class ModuleTest: print(f"{oldkey} is listed in --disabled =>", res5[oldkey]) if res1[newkey] == 'pass' and res2[newkey] == 'yes' and res3[oldkey] == 'no' and res4[newkey] == 'yes' and res5[oldkey] == 'yes': self.overall['switch'] = 'pass' + logging.info(f"The result of removing {key} is PASS.") else: if self.fail == 'hard': self.overall['switch'] = 'fail' + logging.info(f"The result of removing {key} is FAIL.") else: self.overall['switch'] = 'soft' + logging.info(f"The result of removing {key} is SOFTFAIL.") print('') def run_test(self, module='testmodule', stream='master', newer='dummy', fail='hard'): @@ -308,15 +359,13 @@ class Parser: self.parser.add_argument('-u', '--upgrade', default='dummy', help='The name of the stream you want to switch to.') self.parser.add_argument('-a', '--action',default='list', help='List of actions, you want to run.') self.parser.add_argument('-f', '--fail',default='hard', help='Fail hard/soft at list errors.') + self.parser.add_argument('-l', '--log', default='info', help='Log Level use (info, warning, debug).') def return_args(self): args = self.parser.parse_args() return args if __name__ == '__main__': - - suite = TestSuite() options = Parser() - args = options.return_args() args = args.__dict__ module = args['module'] @@ -324,7 +373,14 @@ if __name__ == '__main__': newstream = args['upgrade'] action = args['action'].split(',') fail = args['fail'] + log = args['log'] + numloglevel = getattr(logging, log.upper()) + logging.basicConfig(filename='modular.log', filemode='w', level=numloglevel) + logging.info('Script started.') + + suite = TestSuite() + test = ModuleTest(suite, action) test.run_test(module, oldstream, newstream, fail) @@ -334,15 +390,13 @@ if __name__ == '__main__': for key in results: print(f"Tested functionality: {key} => {results[key]}") suite.outputs['total_results'] = results - log = json.dumps(suite.outputs, indent=4) - with open('/root/modular.log','w') as outfile: - outfile.write(log) - print("\nLog files have been saved.") if 'fail' in results.values(): + logging.info('Script finished with exit code 1.') sys.exit(1) else: + logging.info('Script finished with exit code 0.') sys.exit(0)