| |
@@ -16,6 +16,7 @@
|
| |
|
| |
import argparse
|
| |
import json
|
| |
+ import logging
|
| |
import subprocess
|
| |
import sys
|
| |
|
| |
@@ -25,6 +26,7 @@
|
| |
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 @@
|
| |
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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 @@
|
| |
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)
|
| |
|
| |
|
| |
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.