| |
@@ -8,186 +8,130 @@
|
| |
|
| |
|
| |
This script uses the dnf to do various operations on modular packages and reports such operations
|
| |
- were succesfull from the users' perspective. It does not test the sanity of the modular content,
|
| |
+ were succesful from the users' perspective. It does not test the sanity of the modular content,
|
| |
or any correctness of installed or removed RPM packages.
|
| |
|
| |
@licence: GPLv3
|
| |
"""
|
| |
|
| |
+ # stdlib imports
|
| |
import argparse
|
| |
- import dnf
|
| |
import json
|
| |
- import libdnf
|
| |
import logging
|
| |
import os
|
| |
- import pprint
|
| |
- import re
|
| |
+ import shutil
|
| |
import subprocess
|
| |
import sys
|
| |
- import yaml
|
| |
|
| |
+ # external imports
|
| |
+ import dnf
|
| |
+ import libdnf
|
| |
+ import yaml
|
| |
|
| |
- class DNFoutput:
|
| |
- # This class uses the DNF code to communicate with DNF in order to get a correct and parsed list of all modules to further processing.
|
| |
- def __init__(self):
|
| |
- self.base = dnf.Base()
|
| |
- self.base.read_all_repos()
|
| |
- self.base.fill_sack()
|
| |
- self.modstate = libdnf.module.ModulePackageContainer.ModuleState_UNKNOWN
|
| |
- self.mods = self.base._moduleContainer.getModulePackages()
|
| |
- self.latest = self.base._moduleContainer.getLatestModulesPerRepo(self.modstate, self.mods)
|
| |
-
|
| |
- def _profile_report_formater(self, modulePackage, default_profiles, enabled_str):
|
| |
- installed_profiles = self.base._moduleContainer.getInstalledProfiles(modulePackage.getName())
|
| |
- available_profiles = modulePackage.getProfiles()
|
| |
- profiles_str = ""
|
| |
- for profile in available_profiles:
|
| |
- profiles_str += "{}{}".format(
|
| |
- profile.getName(), " [d]" if profile.getName() in default_profiles else "")
|
| |
- profiles_str += " [i], " if profile.getName() in installed_profiles and enabled_str \
|
| |
- else ", "
|
| |
- return profiles_str[:-2]
|
| |
-
|
| |
- def _module_strs_formater(self, modulePackage, markActive=False):
|
| |
- default_str = ""
|
| |
- enabled_str = ""
|
| |
- disabled_str = ""
|
| |
- if modulePackage.getStream() == self.base._moduleContainer.getDefaultStream(modulePackage.getName()):
|
| |
- default_str = " [d]"
|
| |
- if self.base._moduleContainer.isEnabled(modulePackage):
|
| |
- if not default_str:
|
| |
- enabled_str = " "
|
| |
- enabled_str += "[e]"
|
| |
- elif self.base._moduleContainer.isDisabled(modulePackage):
|
| |
- if not default_str:
|
| |
- disabled_str = " "
|
| |
- disabled_str += "[x]"
|
| |
- if markActive and self.base._moduleContainer.isModuleActive(modulePackage):
|
| |
- if not default_str:
|
| |
- disabled_str = " "
|
| |
- disabled_str += "[a]"
|
| |
- return default_str, enabled_str, disabled_str
|
| |
-
|
| |
- def _create_and_fill_table(self, latest):
|
| |
- modules = []
|
| |
- for latest_per_repo in latest:
|
| |
- for nameStreamArch in latest_per_repo:
|
| |
- if len(nameStreamArch) == 1:
|
| |
- modulePackage = nameStreamArch[0]
|
| |
- else:
|
| |
- active = [module for module in nameStreamArch
|
| |
- if self.base._moduleContainer.isModuleActive(module)]
|
| |
- if active:
|
| |
- modulePackage = active[0]
|
| |
- else:
|
| |
- modulePackage = nameStreamArch[0]
|
| |
-
|
| |
- default_str, enabled_str, disabled_str = self._module_strs_formater(modulePackage)
|
| |
- default_profiles = self.base._moduleContainer.getDefaultProfiles(modulePackage.getName(), modulePackage.getStream())
|
| |
- profiles_str = self._profile_report_formater(modulePackage, default_profiles, enabled_str)
|
| |
- modules.append(
|
| |
- {
|
| |
- "name": modulePackage.getName(),
|
| |
- "stream": modulePackage.getStream() + default_str + enabled_str + disabled_str,
|
| |
- "profiles": profiles_str,
|
| |
- "summary": modulePackage.getSummary()
|
| |
- }
|
| |
- )
|
| |
-
|
| |
- return modules
|
| |
|
| |
- def output(self):
|
| |
- return self._create_and_fill_table(self.latest)
|
| |
+ def clean_fakeroot():
|
| |
+ """Cleans installation data in fake environment."""
|
| |
+ # All fake tests run in installroot=/testinstall, before each test, the installroot
|
| |
+ # should be deleted to achieve a clean test environment.
|
| |
+ if os.path.isdir('/testinstall'):
|
| |
+ shutil.rmtree('/testinstall')
|
| |
+ print("\n======== The test environment has been cleaned. ========\n")
|
| |
+ logging.info("The test environment has been wiped succesfully.")
|
| |
+ else:
|
| |
+ print("\n======== The test environment was not cleaned because it had already "
|
| |
+ "been done or did not yet exist. ========\n")
|
| |
|
| |
|
| |
class TestSuite:
|
| |
- def __init__(self, dnf, whitelist=None, test='all'):
|
| |
- self.dnf = dnf
|
| |
- self.test = test
|
| |
+ """The test suite, which handles running commands and a few other
|
| |
+ things for the tests in ModuleTest.
|
| |
+ """
|
| |
+ def __init__(self, whitelist=None, releasever=0):
|
| |
self.whitelist = whitelist
|
| |
- self.modlist = {}
|
| |
- self.results = {}
|
| |
+ self._module_list = {}
|
| |
self.outputs = {}
|
| |
+ if releasever:
|
| |
+ print("======== The test will run in fake environment.========")
|
| |
+ logging.info("Fake environment has been required for this test.")
|
| |
+ clean_fakeroot()
|
| |
+ self.extradnfargs = [
|
| |
+ "--installroot=/testinstall", f"--releasever={releasever}",
|
| |
+ f"--setopt=module_platform_id=platform:f{releasever}",
|
| |
+ "--setopt=tsflags=justdb"
|
| |
+ ]
|
| |
+ else:
|
| |
+ print("======== The test will run on the real host packages.========")
|
| |
+ logging.info("Test is running with real system data.")
|
| |
+ self.extradnfargs = []
|
| |
logging.info('The test suite has been initialized.')
|
| |
- self.releasever = None
|
| |
- self.fake = False
|
| |
- self.clean_installation()
|
| |
self.yamldb = {}
|
| |
- #self.install = "--installroot=/testinstall"
|
| |
- #self.relver = f"--releasever={self.releasever}"
|
| |
- #self.extras1 = f"--setopt=module_platform_id=platform:f{self.releasever}"
|
| |
- #self.extras2 = f"--setopt=tsflags=justdb"
|
| |
-
|
| |
- def store_results(self, key, value):
|
| |
- if key in self.results.keys():
|
| |
- v = self.results[key]
|
| |
- v.append(value)
|
| |
- self.results[key] = v
|
| |
-
|
| |
- else:
|
| |
- self.results[key] = [value]
|
| |
- logging.debug('The store_results method has stored the results into self.results.')
|
| |
|
| |
+ @property
|
| |
def module_list(self):
|
| |
- # Get the modular data from the DNF. It comes as a list of dictionaries.
|
| |
- processed = self.dnf.output()
|
| |
-
|
| |
- # Let us parse the data and for each module create a dictionary, where name is the key and the rest comes as a value.
|
| |
- modulelist = {}
|
| |
- for line in processed:
|
| |
- module = {}
|
| |
- name = line['name']
|
| |
- module['name'] = name
|
| |
- stream = line['stream']
|
| |
- # If the stream is a default stream, we can find a '[d]' symbol in it.
|
| |
- # We record the default stream elsewhere and delete the symbol.
|
| |
- if '[d]' in stream:
|
| |
- stream = stream.split('[')[0].strip()
|
| |
- module['default-stream'] = stream
|
| |
- elif '[' in stream:
|
| |
- stream = stream.split('[')[0].strip()
|
| |
- module['stream'] = stream
|
| |
-
|
| |
- # There might be more profiles and one can be set as a default profile.
|
| |
- # This procedure breaks the list of profiles and finds a default one if present.
|
| |
- profiles = line['profiles'].split(',')
|
| |
- nprofiles = []
|
| |
- for profile in profiles:
|
| |
- if '[d]' in profile:
|
| |
- profile = profile.split('[')[0].strip()
|
| |
- module['default-profile'] = profile
|
| |
- nprofiles.append(profile)
|
| |
- elif '[i]' in profile:
|
| |
- profile = profile.split('[')[0].strip()
|
| |
- nprofiles.append(profile)
|
| |
- else:
|
| |
- nprofiles.append(profile)
|
| |
- module['profiles'] = nprofiles
|
| |
- info = line['summary']
|
| |
- module['info'] = info
|
| |
-
|
| |
- # Now, let us make a database (dict) with all the modules, with name as keys and list of dicts
|
| |
- # for various streams as values.
|
| |
- if name not in modulelist.keys(): #If this is first occurence
|
| |
- modulelist[name] = [module]
|
| |
- else: # If this is another occurence of same module with different streams.
|
| |
- if module not in modulelist[name]:
|
| |
- modulelist[name].append(module)
|
| |
- else:
|
| |
- pass
|
| |
-
|
| |
- self.modlist = modulelist
|
| |
-
|
| |
- logging.debug('The "dnf module list" operation was succesful. Data have been obtained from the DNF.')
|
| |
- return self.modlist
|
| |
-
|
| |
- def module_info(self, moduleName):
|
| |
+ """A dictionary with module names as keys. Values are lists of
|
| |
+ dicts, each dict represents an occurrence of the module in the
|
| |
+ DNF module list output for a different stream, with stream,
|
| |
+ profile and summary information.
|
| |
+ """
|
| |
+ if not self._module_list:
|
| |
+ base = dnf.Base()
|
| |
+ base.read_all_repos()
|
| |
+ base.fill_sack()
|
| |
+ modcon = base._moduleContainer
|
| |
+ latest = modcon.getLatestModulesPerRepo(
|
| |
+ libdnf.module.ModulePackageContainer.ModuleState_UNKNOWN,
|
| |
+ modcon.getModulePackages())
|
| |
+
|
| |
+ for latest_per_repo in latest:
|
| |
+ for name_stream_arch in latest_per_repo:
|
| |
+ if len(name_stream_arch) == 1:
|
| |
+ module_package = name_stream_arch[0]
|
| |
+ else:
|
| |
+ active = [module for module in name_stream_arch
|
| |
+ if modcon.isModuleActive(module)]
|
| |
+ if active:
|
| |
+ module_package = active[0]
|
| |
+ else:
|
| |
+ module_package = name_stream_arch[0]
|
| |
+
|
| |
+ name = module_package.getName()
|
| |
+ stream = module_package.getStream()
|
| |
+ module = {
|
| |
+ 'name': name,
|
| |
+ 'stream': stream,
|
| |
+ 'profiles': [],
|
| |
+ 'info': module_package.getSummary()
|
| |
+ }
|
| |
+ if stream == modcon.getDefaultStream(name):
|
| |
+ module['default-stream'] = stream
|
| |
+
|
| |
+ default_profiles = modcon.getDefaultProfiles(name, stream)
|
| |
+ available_profiles = module_package.getProfiles()
|
| |
+ for profile in available_profiles:
|
| |
+ profname = profile.getName()
|
| |
+ module['profiles'].append(profname)
|
| |
+ if profname in default_profiles:
|
| |
+ module['default-profile'] = profname
|
| |
+
|
| |
+ # Now, let us make a database (dict) with all the modules, with name as keys
|
| |
+ # and list of dicts for various streams as values.
|
| |
+ if name not in self._module_list:
|
| |
+ # This is the first occurrence
|
| |
+ self._module_list[name] = [module]
|
| |
+ elif module not in self._module_list[name]:
|
| |
+ # This is another occurrence of same module with different streams
|
| |
+ self._module_list[name].append(module)
|
| |
+
|
| |
+ logging.debug('DNF module list parsing successful.')
|
| |
+
|
| |
+ return self._module_list
|
| |
+
|
| |
+ def module_info(self, modname):
|
| |
"""Returns a dictionary with streams as keys and profiles as values."""
|
| |
- modules = self.module_list()
|
| |
- module = modules[moduleName]
|
| |
+ module = self.module_list[modname]
|
| |
info = {}
|
| |
- for s in module:
|
| |
- info[s['stream']] = s['profiles']
|
| |
+ for item in module:
|
| |
+ info[item['stream']] = item['profiles']
|
| |
logging.debug("The module_info method ran successfully.")
|
| |
return info
|
| |
|
| |
@@ -195,57 +139,34 @@
|
| |
"""This method calls different modular commands."""
|
| |
# Each time, we need to call a modular dnf command, we will call this method
|
| |
# with different arguments.
|
| |
- if stream == None:
|
| |
- smodifier = ''
|
| |
- else:
|
| |
+ smodifier = ''
|
| |
+ pmodifier = ''
|
| |
+ if stream:
|
| |
smodifier = f":{stream}"
|
| |
- if profile == None:
|
| |
- pmodifier = ''
|
| |
- else:
|
| |
+ if profile:
|
| |
pmodifier = f"/{profile}"
|
| |
modifier = f"{module}{smodifier}{pmodifier}"
|
| |
key = f"dnf module {operation} {modifier}"
|
| |
# For some tests, it is good, when we use a fake environment via installroot and we limit
|
| |
- # the DNF operations to "justdb" (only make changes in the database without installing anything).
|
| |
- # This is very useful in case of multiple operations that are much faster. To use this fake option
|
| |
- # do not forget to set_fake(True) before the process.
|
| |
- if self.fake == True:
|
| |
- releasever = self.releasever
|
| |
- install = self.install
|
| |
- relver = self.relver
|
| |
- extras1 = self.extras1
|
| |
- extras2 = self.extras2
|
| |
- raw = subprocess.run(['dnf', 'module', operation, '-y', modifier, install, relver, extras1, extras2], capture_output=True)
|
| |
- logging.debug(raw.stdout.decode('utf-8'))
|
| |
- else:
|
| |
- # When the command should be real, we will use a different command. This will make changes to the host system.
|
| |
- raw = subprocess.run(['dnf', 'module', operation, '-y', modifier], capture_output=True)
|
| |
+ # the DNF operations to "justdb" (only make changes in the database without installing
|
| |
+ # anything). This will be done if 'releasever' is set when initializing the instance,
|
| |
+ # it should be set to the desired Fedora release number, e.g. TestSuite(releasever=30)
|
| |
+ args = ['dnf', 'module', operation, '-y', modifier] + self.extradnfargs
|
| |
+ raw = subprocess.run(args, capture_output=True)
|
| |
+ logging.debug(raw.stdout.decode('utf-8'))
|
| |
self.outputs[operation] = raw.stdout.decode('utf-8')
|
| |
if raw.returncode == 0:
|
| |
result = raw.returncode
|
| |
- logging.info(f"The {key} operation ran successfully.")
|
| |
+ logging.info("The %s operation ran successfully.", key)
|
| |
result = 0
|
| |
else:
|
| |
- problem = raw.stderr.decode('utf-8')
|
| |
- logging.error(f"The operation {key} has NOT finished successfully, because of {problem}.")
|
| |
+ problem = raw.stderr.decode('utf-8')
|
| |
+ logging.error("The operation %s has NOT finished successfully, because of %s.",
|
| |
+ key, problem)
|
| |
print('There was a DNF problem! Check the log for further information.')
|
| |
result = 1
|
| |
- self.store_results(f'dnf module {operation}', result)
|
| |
return result
|
| |
|
| |
- def clean_installation(self):
|
| |
- """Cleans installation data in fake environment."""
|
| |
- # All fake tests run in installroot=/testinstall, before each test, the installroot
|
| |
- # should be deleted to achieve a clean test environment.
|
| |
- raw = subprocess.run(['rm', '-rf', '/testinstall'], capture_output=True)
|
| |
- if raw.returncode == 0:
|
| |
- print("\n======== The test environment has been cleaned. ========\n")
|
| |
- logging.info("The test environment has been wiped succesfully.")
|
| |
- else:
|
| |
- print("\n======== There was a problem deleting the test environment, because it had been already deleted or never existed. ========\n")
|
| |
- raw = raw.stderr.decode('utf-8')
|
| |
- logging.error(f"The test date have not been wiped succesfully: {raw} ")
|
| |
-
|
| |
def is_listed(self, module, stream, profile, inlist):
|
| |
"""Returns if the specified module is listed in dnf module list."""
|
| |
option = f"--{inlist}"
|
| |
@@ -253,15 +174,8 @@
|
| |
# If we operate in the fake environment, we must list in that environment, too, otherwise
|
| |
# we will never get the correct information because the situation is different on the
|
| |
# host computer.
|
| |
- if self.fake == True:
|
| |
- releasever = self.releasever
|
| |
- install = self.install
|
| |
- relver = self.relver
|
| |
- extras1 = self.extras1
|
| |
- extras2 = self.extras2
|
| |
- raw = subprocess.run(['dnf', 'module', 'list', option, install, relver, extras1, extras2], capture_output=True)
|
| |
- else:
|
| |
- raw = subprocess.run(['dnf', 'module', 'list', option], capture_output=True)
|
| |
+ args = ['dnf', 'module', 'list', option] + self.extradnfargs
|
| |
+ raw = subprocess.run(args, capture_output=True)
|
| |
# If the list command ran successfully, parse the list and provide result.
|
| |
if raw.returncode == 0:
|
| |
raw = raw.stdout.decode('utf-8')
|
| |
@@ -273,54 +187,29 @@
|
| |
for line in raw:
|
| |
if module in line:
|
| |
modfound = 0
|
| |
- if stream != None and stream in line:
|
| |
+ if not stream or stream in line:
|
| |
strfound = 0
|
| |
- elif stream == None:
|
| |
- strfound = 0
|
| |
- if profile != None and profile in line:
|
| |
- profound = 0
|
| |
- elif profile == None:
|
| |
+ if not profile or profile in line:
|
| |
profound = 0
|
| |
result = modfound + strfound + profound
|
| |
if result == 0:
|
| |
break
|
| |
- # The result show a 1 for every error. Therefore, it can be easily spotted, where the problem is
|
| |
- # even if you do now read the log file.
|
| |
+ # The result show a 1 for every error. Therefore, it can be easily spotted, where
|
| |
+ # the problem is even if you do not read the log file.
|
| |
# 0 - everything OK
|
| |
# 1 - the specified profile is not in list, but the name and stream are
|
| |
# 10 - the specified stream is not in list, but the name and profile are
|
| |
# 11 - the specified stream and profile are not in list, but the name is
|
| |
# 111 - nothing is listed
|
| |
result = modfound + strfound + profound
|
| |
- logging.info(f"The {key} command ran successfully.")
|
| |
+ logging.info("The %s command ran successfully.", key)
|
| |
else:
|
| |
problem = raw.stderr.decode('utf-8')
|
| |
- logging.error(f"The operation {key} has NOT finished successfully, because of {problem}.")
|
| |
+ logging.error("The operation %s has NOT finished successfully, because of %s.",
|
| |
+ key, problem)
|
| |
result = 1
|
| |
- self.store_results(key, result)
|
| |
return result
|
| |
|
| |
- def in_output(self, text, what):
|
| |
- """Checks that the text is in the DNF output."""
|
| |
- if isinstance(text, str):
|
| |
- text = text.split("\n")
|
| |
- elif isinstance(text, list):
|
| |
- pass
|
| |
- else:
|
| |
- text = []
|
| |
- c = 0
|
| |
- for line in text:
|
| |
- if what in line:
|
| |
- c += 1
|
| |
- else:
|
| |
- 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.')
|
| |
-
|
| |
def load_whitelist(self):
|
| |
"""Reads the file with whitelisted modules and returns the content."""
|
| |
# Some modules do not have the default streams and/or profiles set on purpose.
|
| |
@@ -330,66 +219,52 @@
|
| |
else:
|
| |
logging.info("Module whitelist found. It will be used for some tests.")
|
| |
try:
|
| |
- with open(self.whitelist) as f:
|
| |
- wlist = f.readlines()
|
| |
+ with open(self.whitelist) as wlfh:
|
| |
+ wlist = wlfh.readlines()
|
| |
whitelist = []
|
| |
- for m in wlist:
|
| |
- whitelist.append(m.strip())
|
| |
+ for module in wlist:
|
| |
+ whitelist.append(module.strip())
|
| |
|
| |
except FileNotFoundError:
|
| |
- print(f"File {self.whitelist} has not been found. Whitelist could not be read, proceeding without it.")
|
| |
- logging.error(f"File {self.whitelist} has not been found. Proceeding without it.")
|
| |
- whitelist = []
|
| |
+ text = (f"File {self.whitelist} has not been found. Whitelist could not be read, "
|
| |
+ "proceeding without it.")
|
| |
+ print(text)
|
| |
+ logging.error(text)
|
| |
+ whitelist = []
|
| |
return whitelist
|
| |
|
| |
- def set_fake(self, fake=False, releasever=None):
|
| |
- """Switches the fake environment on."""
|
| |
- # If you use this in the test method, the operation will run in test environment. Otherwise, it will run
|
| |
- # for real on the host system data. You can also switch the test environment on using -d (--dryrun) when
|
| |
- # invoking the script on CLI.
|
| |
- if fake == 'true' or fake == 'True':
|
| |
- print("======== The test will run in fake environment.========")
|
| |
- logging.info("Fake environment has been required for this test.")
|
| |
- self.fake = True
|
| |
- else:
|
| |
- print("======== The test will run on the real host packages.========")
|
| |
- logging.info("Test is running with real system data.")
|
| |
- self.fake = False
|
| |
- self.install = "--installroot=/testinstall"
|
| |
- self.releasever = releasever
|
| |
- self.relver = f"--releasever={self.releasever}"
|
| |
- self.extras1 = f"--setopt=module_platform_id=platform:f{self.releasever}"
|
| |
- self.extras2 = f"--setopt=tsflags=justdb"
|
| |
- return 0
|
| |
-
|
| |
def create_yamldb(self, directory):
|
| |
"""Read yaml files from the given directory and return a database of collected info."""
|
| |
listdir = os.listdir(directory)
|
| |
ymls = [yml for yml in listdir if 'yaml' in yml]
|
| |
yamldb = {}
|
| |
for yml in ymls:
|
| |
- f = f"{directory}{yml}"
|
| |
- with open(f,'r') as yamlfile:
|
| |
+ filename = f"{directory}{yml}"
|
| |
+ with open(filename, 'r') as yamlfile:
|
| |
yamldb[yml] = yaml.safe_load(yamlfile)
|
| |
self.yamldb = yamldb
|
| |
return yamldb
|
| |
|
| |
def check_yaml_exists(self, module):
|
| |
+ """Check yaml file actually exists for a given module."""
|
| |
key = f"{module}.yaml"
|
| |
try:
|
| |
yamldata = self.yamldb[key]
|
| |
return yamldata
|
| |
except KeyError:
|
| |
print(f"{key}")
|
| |
- logging.error(f"The {key} seems not to exist for {module}.")
|
| |
+ logging.error("The %s seems not to exist for %s.", key, module)
|
| |
return None
|
| |
|
| |
|
| |
class ModuleTest:
|
| |
- # These are various test methods that do the actual testing.
|
| |
- def __init__(self, suite, scenario):
|
| |
+ """This class contains methods that do the actual testing. The
|
| |
+ 'overall' dict tracks the overall test results and is updated by
|
| |
+ each test method.
|
| |
+ """
|
| |
+ def __init__(self, suite, scenarios):
|
| |
self.suite = suite
|
| |
- self.scenario = scenario
|
| |
+ self.scenarios = scenarios
|
| |
self.overall = {}
|
| |
self.newer = None
|
| |
self.older = None
|
| |
@@ -402,50 +277,39 @@
|
| |
110: 'The module stream was not found in the selected list.',
|
| |
210: 'The module stream and profile were not found in the selected list.',
|
| |
111: 'The module was not found in the selected list.',
|
| |
- }
|
| |
-
|
| |
- def results(self):
|
| |
- """Return the overall results."""
|
| |
- return self.overall
|
| |
+ }
|
| |
|
| |
def decode_error(self, code):
|
| |
"""Decode the exit error code."""
|
| |
# We do not use it, yet, I suppose.
|
| |
return self.errorcodes[code]
|
| |
|
| |
- def list_module(self, module, stream=None, profile=None):
|
| |
+ def list_module(self, module):
|
| |
"""Provide information on the specified module."""
|
| |
print('-------- List module ---------')
|
| |
- key = f"{module}:{stream}/{profile}"
|
| |
- modules = self.suite.module_list()
|
| |
- if module in modules.keys():
|
| |
- info = modules[module]
|
| |
- self.overall['list'] = 'pass'
|
| |
- print(f"Showing information for: {module}\n========================================")
|
| |
- for rec in info:
|
| |
- print(f"Stream: {rec['stream']}")
|
| |
- try:
|
| |
- d = rec['default-stream']
|
| |
- print(f"Default stream: yes")
|
| |
- except KeyError:
|
| |
- print(f"Default stream: no")
|
| |
- prostring = ''
|
| |
- for p in rec['profiles']:
|
| |
- prostring = f"{prostring} {p}"
|
| |
- print(f"Available profiles: {prostring}")
|
| |
- try:
|
| |
- d = rec['default-profile']
|
| |
- print(f"Default profile: {d}")
|
| |
- except KeyError:
|
| |
- print(f"Default profile: none")
|
| |
- print('------------------------------')
|
| |
- logging.info("The list method finished successfully.")
|
| |
- return(info)
|
| |
- else:
|
| |
+ if module not in self.suite.module_list:
|
| |
print('Module does not exist.')
|
| |
logging.error("Module does not exist.")
|
| |
self.overall['list'] = 'fail'
|
| |
- return None
|
| |
+ return
|
| |
+
|
| |
+ info = self.suite.module_list[module]
|
| |
+ self.overall['list'] = 'pass'
|
| |
+ print(f"Showing information for: {module}\n========================================")
|
| |
+ for rec in info:
|
| |
+ print(f"Stream: {rec['stream']}")
|
| |
+ if 'default-stream' in rec:
|
| |
+ print("Default stream: yes")
|
| |
+ else:
|
| |
+ print("Default stream: no")
|
| |
+ print("Available profiles: " + ' '.join(rec['profiles']))
|
| |
+ try:
|
| |
+ defprof = rec['default-profile']
|
| |
+ print(f"Default profile: {defprof}")
|
| |
+ except KeyError:
|
| |
+ print(f"Default profile: none")
|
| |
+ print('------------------------------')
|
| |
+ logging.info("The list method finished successfully.")
|
| |
|
| |
def check_install(self, module, stream):
|
| |
"""Check that a module is installed."""
|
| |
@@ -454,15 +318,12 @@
|
| |
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.")
|
| |
- return 0
|
| |
else:
|
| |
self.overall['checkinstall'] = 'fail'
|
| |
- logging.info(f"The result of {key} is FAIL.")
|
| |
- return 1
|
| |
- print(' ')
|
| |
+ logging.info("The result of checking %s is %s.", key, self.overall['checkinstall'].upper())
|
| |
+ print('')
|
| |
|
| |
- def enable_module(self, module, stream, profile = None):
|
| |
+ def enable_module(self, module, stream, profile=None):
|
| |
"""Test that a module can be enabled."""
|
| |
print('-------- Enable module ---------')
|
| |
key = f"{module}:{stream}"
|
| |
@@ -477,28 +338,23 @@
|
| |
print(f"{key} is listed in --disabled => {res3}")
|
| |
if res1 == 0 and res2 == 0 and res3 == 111:
|
| |
self.overall['enable'] = 'pass'
|
| |
- logging.info(f"The result of enabling {key} is PASS.")
|
| |
- return 0
|
| |
else:
|
| |
problems = [res1, res2, res3]
|
| |
- for p in problems:
|
| |
- logging.error(f"Error code = {p}. {self.decode_error(p)}")
|
| |
+ for problem in problems:
|
| |
+ logging.error("Error code = %s. %s", problem, self.decode_error(problem))
|
| |
if self.fail == 'hard':
|
| |
self.overall['enable'] = 'fail'
|
| |
- logging.info(f"The result of enabling {key} is FAIL.")
|
| |
- return 1
|
| |
else:
|
| |
- self.overall['enable'] = 'soft'
|
| |
- logging.info(f"The result of enabling {key} is SOFTFAIL.")
|
| |
- return 2
|
| |
+ self.overall['enable'] = 'softfail'
|
| |
+ logging.info("The result of enabling %s is %s.", key, self.overall['enable'].upper())
|
| |
print('')
|
| |
|
| |
- def disable_module(self, module, stream, profile = None):
|
| |
+ def disable_module(self, module, stream, profile=None):
|
| |
"""Test that a module can be disabled."""
|
| |
print('-------- Disable module ---------')
|
| |
if self.newer != 'dummy':
|
| |
stream = self.newer
|
| |
- if stream != None:
|
| |
+ if stream:
|
| |
key = f"{module}:{stream}"
|
| |
else:
|
| |
key = f"{module}"
|
| |
@@ -515,28 +371,23 @@
|
| |
print(f"{key} is listed in --installed =>", res4)
|
| |
if res1 == 0 and res2 == 0 and res3 == 111 and res4 == 111:
|
| |
self.overall['disable'] = 'pass'
|
| |
- logging.info(f"The result of disabling {key} is PASS.")
|
| |
- return 0
|
| |
else:
|
| |
problems = [res1, res2, res3, res4]
|
| |
- for p in problems:
|
| |
- logging.error(f"Error code = {p}. {self.decode_error(p)}")
|
| |
+ for problem in problems:
|
| |
+ logging.error("Error code = %s. %s", problem, self.decode_error(problem))
|
| |
if self.fail == 'hard':
|
| |
self.overall['disable'] = 'fail'
|
| |
- logging.info(f"The result of disabling {key} is FAIL. ")
|
| |
- return 1
|
| |
else:
|
| |
- self.overall['disable'] = 'soft'
|
| |
- logging.info(f"The result of disabling {key} is SOFTFAIL.")
|
| |
- return 2
|
| |
+ self.overall['disable'] = 'softfail'
|
| |
+ logging.info("The result of disabling %s is %s.", key, self.overall['disable'].upper())
|
| |
print('')
|
| |
|
| |
def install_module(self, module, stream=None, profile=None):
|
| |
"""Test that a module can be installed."""
|
| |
print('-------- Install module ---------')
|
| |
- if stream != None and profile != None:
|
| |
+ if stream and profile:
|
| |
key = f"{module}:{stream}/{profile}"
|
| |
- elif stream != None and profile == None:
|
| |
+ elif stream:
|
| |
key = f"{module}:{stream}"
|
| |
else:
|
| |
key = f"{module}"
|
| |
@@ -552,17 +403,12 @@
|
| |
print(f"{key} is listed in --disabled =>", res4)
|
| |
if res1 == 0 and res2 == 0 and res3 == 0 and res4 == 111:
|
| |
self.overall['install'] = 'pass'
|
| |
- logging.info(f"The result of installing {key} is PASS.")
|
| |
- return 0
|
| |
else:
|
| |
if self.fail == 'hard':
|
| |
self.overall['install'] = 'fail'
|
| |
- logging.info(f"The result of installing {key} is FAIL.")
|
| |
- return 1
|
| |
else:
|
| |
- self.overall['install'] = 'soft'
|
| |
- logging.info(f"The result of installing {key} is SOFTFAIL.")
|
| |
- return 2
|
| |
+ self.overall['install'] = 'softfail'
|
| |
+ logging.info("The result of installing %s is %s.", key, self.overall['install'].upper())
|
| |
print('')
|
| |
|
| |
def remove_module(self, module, stream=None, profile=None):
|
| |
@@ -570,7 +416,7 @@
|
| |
print('-------- Remove module ---------')
|
| |
if self.newer != 'dummy':
|
| |
stream = self.newer
|
| |
- if stream != None:
|
| |
+ if stream:
|
| |
key = f"{module}:{stream}"
|
| |
else:
|
| |
key = f"{module}"
|
| |
@@ -582,28 +428,23 @@
|
| |
print(f"{key} is listed in --installed =>", res2)
|
| |
if res1 == 0 and res2 == 111:
|
| |
self.overall['remove'] = 'pass'
|
| |
- logging.info(f"The result of removing {key} is PASS.")
|
| |
- return 0
|
| |
else:
|
| |
if self.fail == 'hard':
|
| |
self.overall['remove'] = 'fail'
|
| |
- logging.info(f"The result of removing {key} is FAIL.")
|
| |
- return 1
|
| |
else:
|
| |
- self.overall['remove'] = 'soft'
|
| |
- logging.info(f"The result of removing {key} is SOFTFAIL.")
|
| |
- return 2
|
| |
+ self.overall['remove'] = 'softfail'
|
| |
+ logging.info("The result of removing %s is %s.", key, self.overall['remove'].upper())
|
| |
print('')
|
| |
|
| |
def reset_module(self, module, stream=None, profile=None):
|
| |
"""Tests resetting the module."""
|
| |
# When the module status quo is changed once, it will always play some role in modularity.
|
| |
- # It will either be enabled, disabled, or installed. Removing an installed module, will not disable it,
|
| |
- # disabling an enabled module will not bring it to its neutral state (and vice versa). To bring the
|
| |
- # module into a neutral state, one has to reset it. Resetting a module is an important part of
|
| |
- # module stream switching, if one decides to do so.
|
| |
- # To reset a module, we will check that it is either enabled, or disabled (that means, not neutral, then
|
| |
- # we reset it and check that it is not enabled nor disabled -> neutral.
|
| |
+ # It will either be enabled, disabled, or installed. Removing an installed module will
|
| |
+ # not disable it; disabling an enabled module will not bring it to its neutral state (and
|
| |
+ # vice versa). To bring the module into a neutral state, one has to reset it. Resetting a
|
| |
+ # module is an important part of module stream switching, if one decides to do so.
|
| |
+ # To reset a module, we will check that it is either enabled, or disabled (that means, not
|
| |
+ # neutral), then we reset it and check that it is not enabled nor disabled -> neutral.
|
| |
print('-------- Resetting module ---------')
|
| |
res1 = self.suite.is_listed(module, stream, profile, 'enabled')
|
| |
res2 = self.suite.is_listed(module, stream, profile, 'disabled')
|
| |
@@ -616,12 +457,10 @@
|
| |
print(f"{module} is listed in --disabled => {res2}")
|
| |
if res3 == 0 and res1 == 111 and res2 == 111:
|
| |
self.overall['reset'] = 'pass'
|
| |
- logging.info(f"The result of resetting {module} is PASS.")
|
| |
- return 0
|
| |
else:
|
| |
self.overall['reset'] = 'fail'
|
| |
- logging.info(f"The result of resetting {module} is FAIL.")
|
| |
- return 1
|
| |
+ logging.info("The result of resetting %s is %s.",
|
| |
+ module, self.overall['reset'].upper())
|
| |
|
| |
else:
|
| |
print('It seems that the module does not exist or is already reset.')
|
| |
@@ -629,117 +468,115 @@
|
| |
self.overall['reset'] = 'fail'
|
| |
|
| |
def switch_stream(self, module, oldstr, newstr):
|
| |
- """Tests switching the stream"""
|
| |
- # Direct stream switching is not allowed any more. To switch
|
| |
- # streams, it is required to remove the current module and
|
| |
- # stream, reset it and install the same module with a different
|
| |
- # stream again. This action uses other (predefined) actions to
|
| |
- # achieve it. Same functionality can be achieved by using two
|
| |
- # different tests.
|
| |
+ """Tests switching the stream."""
|
| |
+ # Direct stream switching is not allowed any more. To switch streams, it is required to
|
| |
+ # remove the current module and stream, reset it and install the same module with a
|
| |
+ # different stream again. This action uses other (predefined) actions to achieve it. Same
|
| |
+ # functionality can be achieved by using two different tests.
|
| |
|
| |
print('-------- Switching streams ---------')
|
| |
oldkey = f"{module}:{oldstr}"
|
| |
newkey = f"{module}:{newstr}"
|
| |
|
| |
- res1 = self.suite.use_module(module, oldstr, 'install')
|
| |
- res2 = self.suite.use_module(module, oldstr, 'remove')
|
| |
- res3 = self.suite.use_module(module, oldstr, 'reset')
|
| |
- res4 = self.suite.use_module(module, newstr, 'install')
|
| |
+ res1 = self.suite.use_module(module, oldstr, 'install')
|
| |
+ res2 = self.suite.use_module(module, oldstr, 'remove')
|
| |
+ res3 = self.suite.use_module(module, oldstr, 'reset')
|
| |
+ res4 = self.suite.use_module(module, newstr, 'install')
|
| |
|
| |
if res1 == 0 and res2 == 0 and res3 == 0 and res4 == 0:
|
| |
self.overall['switch'] = 'pass'
|
| |
- logging.info(f"The result of switching {oldkey} for {newkey} is PASS.")
|
| |
else:
|
| |
if self.fail == 'hard':
|
| |
self.overall['switch'] = 'fail'
|
| |
- logging.info(f"The result of switching {oldkey} for {newkey} is FAIL.")
|
| |
else:
|
| |
- self.overall['switch'] = 'soft'
|
| |
- logging.info(f"The result of switching {oldkey} for {newkey} is SOFTFAIL.")
|
| |
+ self.overall['switch'] = 'softfail'
|
| |
+ logging.info("The result of switching %s for %s is %s.", oldkey, newkey,
|
| |
+ self.overall['switch'].upper())
|
| |
print('')
|
| |
|
| |
def find_no_defaults(self):
|
| |
"""Finds all modules without default streams and profiles set."""
|
| |
- modules = self.suite.module_list()
|
| |
- print(f"{len(self.suite.modlist)} modules will be tested.")
|
| |
+ modules = self.suite.module_list
|
| |
+ print(f"{len(modules)} modules will be tested.")
|
| |
totalresults = {}
|
| |
whitelist = self.suite.load_whitelist()
|
| |
- if len(whitelist)>1:
|
| |
- print(f"{len(whitelist)} whitelisted modules will be skipped. ")
|
| |
- logging.info(f"The following whitelisted modules will be skipped: {whitelist}")
|
| |
- for module in modules.keys():
|
| |
- varieties = modules[module]
|
| |
+ if len(whitelist) > 1:
|
| |
+ print(f"{len(whitelist)} whitelisted modules will be skipped.")
|
| |
+ logging.info("The following whitelisted modules will be skipped: %s", whitelist)
|
| |
+ for (module, varieties) in modules.items():
|
| |
rstream = []
|
| |
rprofile = []
|
| |
- # Originally, this was written to check whether a module has the defaults (stream and profile)
|
| |
- # correctly set. Whitelisted modules were not tested because they did not have the defaults on purpose.
|
| |
- # However, it has been decided that only if there is no default profile set, the test should fail.
|
| |
- # Non default profiles are correct, because the maintainer do not want modular content precede the
|
| |
- # ursine content.
|
| |
+ # Originally, this was written to check whether a module has the defaults (stream and
|
| |
+ # profile) correctly set. Whitelisted modules were not tested because they did not
|
| |
+ # have the defaults on purpose. However, it has been decided that only if there is no
|
| |
+ # default profile set, the test should fail. Non default profiles are correct, because
|
| |
+ # the maintainer do not want modular content precede the ursine content.
|
| |
for variety in varieties:
|
| |
keys = variety.keys()
|
| |
if 'default-stream' in keys:
|
| |
rstream.append('pass')
|
| |
else:
|
| |
- # When there is no default stream defined, we only should test that this is a wanted
|
| |
- # behaviour by checking the particular yaml file in the defaults git repository.
|
| |
- # Such test will also be added later. Until then, you will be warned to check that
|
| |
- # repository, when a module without default stream is found.
|
| |
+ # When there is no default stream defined, we only should test that this is a
|
| |
+ # wanted behaviour by checking the particular yaml file in the defaults git
|
| |
+ # repository. Such test will also be added later. Until then, you will be
|
| |
+ # warned to check that repository, when a module without default stream is
|
| |
+ # found.
|
| |
rstream.append('check')
|
| |
|
| |
if 'default-profile' in keys:
|
| |
rprofile.append('pass')
|
| |
else:
|
| |
- # When no default profile is set, fail immediately, because such module cannot be
|
| |
- # installed using `dnf module install module:stream`, which is a requirement.
|
| |
+ # When no default profile is set, fail immediately, because such module cannot
|
| |
+ # be installed using `dnf module install module:stream`, which is required.
|
| |
rprofile.append('fail')
|
| |
- logging.error(f"The {module} module has no default profiles set for its streams.")
|
| |
- result = []
|
| |
+ logging.error("The %s module has no default profiles set for its streams.",
|
| |
+ module)
|
| |
+ results = []
|
| |
if 'pass' not in rstream:
|
| |
if 'check' in rstream:
|
| |
- result.append('check yaml for stream definition')
|
| |
+ results.append('check yaml for stream definition')
|
| |
else:
|
| |
- result.append('stream')
|
| |
+ results.append('stream')
|
| |
if 'pass' not in rprofile:
|
| |
- result.append('profile')
|
| |
- if 'stream' not in result and 'profile' not in result:
|
| |
- result.append('pass')
|
| |
+ results.append('profile')
|
| |
+ if 'stream' not in results and 'profile' not in results:
|
| |
+ results.append('pass')
|
| |
if module in whitelist:
|
| |
- result.append('pass')
|
| |
- result.append('whitelisted')
|
| |
+ results.append('pass')
|
| |
+ results.append('whitelisted')
|
| |
if len(varieties) > 1:
|
| |
- result.append('multi')
|
| |
+ results.append('multi')
|
| |
|
| |
- totalresults[module] = result
|
| |
+ totalresults[module] = results
|
| |
logging.info(json.dumps(totalresults, sort_keys=True))
|
| |
|
| |
problems = 0
|
| |
print("======== Showing only problematic modules. ========")
|
| |
- for p in totalresults.keys():
|
| |
- if 'pass' not in totalresults[p]:
|
| |
- print(f"{p}: {totalresults[p]}")
|
| |
- logging.error(f"{p}: {totalresults[p]}")
|
| |
+ for (key, results) in totalresults.items():
|
| |
+ if 'pass' not in results:
|
| |
+ out = f"{key}: {results}"
|
| |
+ print(out)
|
| |
+ logging.error(out)
|
| |
problems += 1
|
| |
print(f"\nThere were altogether {problems} incomplete modules.")
|
| |
|
| |
- if problems == 0:
|
| |
- self.overall['checkdefaults'] = 'pass'
|
| |
- logging.info(f"There were no errors in module stream and profile definitions found.")
|
| |
- return 0
|
| |
- else:
|
| |
+ if problems:
|
| |
self.overall['checkdefaults'] = 'fail'
|
| |
- logging.info(f"There were {problems} problems found in module stream and profile definitions.")
|
| |
- return 1
|
| |
+ logging.info("There were %d problems found in module stream and profile definitions.",
|
| |
+ problems)
|
| |
+ else:
|
| |
+ self.overall['checkdefaults'] = 'pass'
|
| |
+ logging.info("There were no errors in module stream and profile definitions found.")
|
| |
+
|
| |
+ return problems
|
| |
|
| |
def install_all(self):
|
| |
"""Install all modules, all their streams and profiles. One after another."""
|
| |
- # This tests all available modules, if they can be installed. As this uses
|
| |
- # the fake environment which is newly prepared before each module is
|
| |
- # installed, it downloads all the dnf metadata and therefore takes
|
| |
- # quite a long time.
|
| |
- modules = self.suite.module_list()
|
| |
- self.suite.fake = True
|
| |
- total = len(self.suite.modlist)
|
| |
+ # This tests all available modules, if they can be installed. As this uses the fake
|
| |
+ # environment which is newly prepared before each module is installed, it downloads all
|
| |
+ # the dnf metadata and therefore takes quite a long time.
|
| |
+ modules = self.suite.module_list
|
| |
+ total = len(modules)
|
| |
if total == 0:
|
| |
total = 1
|
| |
partial = 0
|
| |
@@ -748,10 +585,8 @@
|
| |
whitelist = self.suite.load_whitelist()
|
| |
for name in modules.keys():
|
| |
completed = round((partial/total)*100)
|
| |
- variants = self.suite.module_info(name)
|
| |
- streams = variants.keys()
|
| |
- for stream in streams:
|
| |
- profiles = variants[stream]
|
| |
+ variants = self.suite.module_info(name)
|
| |
+ for (stream, profiles) in variants.items():
|
| |
for profile in profiles:
|
| |
profile = profile.strip()
|
| |
print("--------------------------------------------------")
|
| |
@@ -759,10 +594,10 @@
|
| |
if name != prevname:
|
| |
prevname = name
|
| |
partial += 1
|
| |
- self.suite.clean_installation()
|
| |
- retcode = self.install_module(name, stream, profile)
|
| |
+ clean_fakeroot()
|
| |
+ self.install_module(name, stream, profile)
|
| |
key = f"{name}:{stream}/{profile}"
|
| |
- if retcode == 0:
|
| |
+ if self.overall['install'] == 'pass':
|
| |
self.overall[key] = 'pass'
|
| |
elif name in whitelist:
|
| |
pass
|
| |
@@ -774,13 +609,12 @@
|
| |
# This makes sure that there is yaml file defined for every module in the system defined
|
| |
# and published in https://pagure.io/releng/fedora-module-defaults.git, and that the yaml
|
| |
# files do have all their requirements set.
|
| |
- modules = self.suite.module_list()
|
| |
self.suite.create_yamldb('./yaml/')
|
| |
whitelist = self.suite.load_whitelist()
|
| |
print("------- Non-existing yaml files --------")
|
| |
results = []
|
| |
parsed_results = {}
|
| |
- for module in modules.keys():
|
| |
+ for module in self.suite.module_list.keys():
|
| |
parsed = {'pass':[], 'fail':[]}
|
| |
yamldata = self.suite.check_yaml_exists(module)
|
| |
if yamldata:
|
| |
@@ -810,8 +644,8 @@
|
| |
parsed['pass'].append('module')
|
| |
else:
|
| |
parsed['fail'].append('module')
|
| |
- print("The module field value does not match the module name." )
|
| |
- logging.error("The module field value does not match the module name." )
|
| |
+ print("The module field value does not match the module name.")
|
| |
+ logging.error("The module field value does not match the module name.")
|
| |
if modata['profiles']:
|
| |
profiles = modata['profiles']
|
| |
for key in profiles.keys():
|
| |
@@ -825,7 +659,7 @@
|
| |
parsed['pass'].append(f"default-stream-{defstr}")
|
| |
else:
|
| |
parsed['fail'].append(f"default-stream-{defstr}")
|
| |
- if len(parsed['fail']) > 0 and module not in whitelist:
|
| |
+ if parsed['fail'] and module not in whitelist:
|
| |
parsed_results[module] = parsed['fail']
|
| |
|
| |
else:
|
| |
@@ -833,8 +667,8 @@
|
| |
results.append('fail')
|
| |
|
| |
if parsed_results:
|
| |
- print(f"There are problems with some of the yaml files. Check logs.")
|
| |
- logging.error(f"Problems with yaml files found.")
|
| |
+ print("There are problems with some of the yaml files. Check logs.")
|
| |
+ logging.error("Problems with yaml files found.")
|
| |
self.overall['yaml_sanity'] = 'fail'
|
| |
else:
|
| |
self.overall['yaml_sanity'] = 'pass'
|
| |
@@ -845,104 +679,93 @@
|
| |
self.overall['yaml_exists'] = 'pass'
|
| |
|
| |
def run_test(self, module, fail, newer, stream, profile):
|
| |
- """Run a particular test."""
|
| |
- # This runs the test assigned to an action. An action, can be selected on the CLI
|
| |
- # using the -a (--action) switch. Multiple actions can be specified, for example:
|
| |
- # -a enable,install,remove,disable,reset
|
| |
+ """This runs the test assigned to an action. An action can be
|
| |
+ selected using the -a (--action) switch. Multiple actions can
|
| |
+ be specified, for example:
|
| |
+ -a enable,install,remove,disable,reset
|
| |
+ """
|
| |
self.newer = newer
|
| |
self.older = stream
|
| |
self.fail = fail
|
| |
- if stream != None:
|
| |
+ if stream:
|
| |
key = f"{module}:{stream}"
|
| |
else:
|
| |
key = f"{module}"
|
| |
- print(f"\n======= Starting tests for {key} using {self.scenario} =======\n")
|
| |
- for s in self.scenario:
|
| |
- if s == 'enable':
|
| |
+ print(f"\n======= Starting tests for {key} using {self.scenarios} =======\n")
|
| |
+ for scenario in self.scenarios:
|
| |
+ if scenario == 'enable':
|
| |
self.enable_module(module, stream)
|
| |
- elif s == 'disable':
|
| |
+ elif scenario == 'disable':
|
| |
self.disable_module(module, stream)
|
| |
- elif s == 'install':
|
| |
+ elif scenario == 'install':
|
| |
self.install_module(module, stream, profile)
|
| |
- elif s == 'remove':
|
| |
+ elif scenario == 'remove':
|
| |
self.remove_module(module, stream)
|
| |
- elif s == 'switch':
|
| |
+ elif scenario == 'switch':
|
| |
self.switch_stream(module, stream, newer)
|
| |
- elif s == 'list':
|
| |
- self.list_module(module, stream)
|
| |
- elif s == 'checkinstall':
|
| |
+ elif scenario == 'list':
|
| |
+ self.list_module(module)
|
| |
+ elif scenario == 'checkinstall':
|
| |
self.check_install(module, stream)
|
| |
- elif s == 'reset':
|
| |
+ elif scenario == 'reset':
|
| |
self.reset_module(module)
|
| |
- elif s == 'info':
|
| |
+ elif scenario == 'info':
|
| |
info = self.suite.module_info(module)
|
| |
print(f"Available streams and profiles of the {module} module:")
|
| |
print("----------------------------------")
|
| |
print(json.dumps(info, sort_keys=True, indent=4))
|
| |
- elif s == 'checkdefaults':
|
| |
+ elif scenario == 'checkdefaults':
|
| |
self.find_no_defaults()
|
| |
- elif s == 'testyaml':
|
| |
+ elif scenario == 'testyaml':
|
| |
self.testyaml()
|
| |
- elif s == 'allinstall':
|
| |
+ elif scenario == 'allinstall':
|
| |
self.install_all()
|
| |
|
| |
-
|
| |
- class Parser:
|
| |
- def __init__(self):
|
| |
- """Inititate the CLI control."""
|
| |
- self.parser = argparse.ArgumentParser()
|
| |
- self.parser.add_argument('-m', '--module', default='testmodule', help='The name of the module you wish to work with.')
|
| |
- self.parser.add_argument('-s', '--stream', default=None, help='The name of the stream you want to use.')
|
| |
- 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).')
|
| |
- self.parser.add_argument('-p', '--profile', default=None, help='The name of the profile you want to use.')
|
| |
- self.parser.add_argument('-w', '--whitelist', default=None, help='File with modules to be skipped when testing default streams and profiles.')
|
| |
- self.parser.add_argument('-r', '--releasever', default=None, help='Sets the release version number for fake installation test (use only when you want to invoke mass instalation).')
|
| |
- self.parser.add_argument('-d', '--dryrun', default=False, help='Switches dry run when installing modules.')
|
| |
- #self.parser.add_argument('--everything', default=False, help='Run all tests on all modules in the system.')
|
| |
-
|
| |
- def return_args(self):
|
| |
- args = self.parser.parse_args()
|
| |
- return args
|
| |
-
|
| |
-
|
| |
- if __name__ == '__main__':
|
| |
- options = Parser()
|
| |
- args = options.return_args()
|
| |
- args = args.__dict__
|
| |
- module = args['module']
|
| |
- oldstream = args['stream']
|
| |
- profile = args['profile']
|
| |
- newstream = args['upgrade']
|
| |
- action = args['action'].split(',')
|
| |
- fail = args['fail']
|
| |
- log = args['log']
|
| |
- whitelist = args['whitelist']
|
| |
- releasever = args['releasever']
|
| |
- #everything = args['everything']
|
| |
- fake = args['dryrun']
|
| |
- numloglevel = getattr(logging, log.upper())
|
| |
+ def parse_args():
|
| |
+ """Parse and return command line arguments."""
|
| |
+ parser = argparse.ArgumentParser()
|
| |
+ parser.add_argument('-m', '--module', default='testmodule',
|
| |
+ help='The name of the module you wish to work with.')
|
| |
+ parser.add_argument('-s', '--stream', default=None,
|
| |
+ help='The name of the stream you want to use.')
|
| |
+ parser.add_argument('-u', '--upgrade', default='dummy',
|
| |
+ help='The name of the stream you want to switch to.')
|
| |
+ parser.add_argument('-a', '--action', default='list', help='List of actions, you want to run.')
|
| |
+ parser.add_argument('-f', '--fail', default='hard', help='Fail hard/soft at list errors.')
|
| |
+ parser.add_argument('-l', '--log', default='info',
|
| |
+ help='Log Level use (info, warning, debug).')
|
| |
+ parser.add_argument('-p', '--profile', default=None,
|
| |
+ help='The name of the profile you want to use.')
|
| |
+ parser.add_argument('-w', '--whitelist', default=None, help='File with modules to be skipped '
|
| |
+ 'when testing default streams and profiles.')
|
| |
+ parser.add_argument('-r', '--releasever', '-d', '--dryrun', type=int, default=0,
|
| |
+ help='A Fedora release number: if set, a non-default install root will be '
|
| |
+ 'used so as not to affect the installed system.')
|
| |
+ #parser.add_argument('--everything', default=False,
|
| |
+ # help='Run all tests on all modules in the system.')
|
| |
+
|
| |
+ return parser.parse_args()
|
| |
+
|
| |
+ def main():
|
| |
+ """Do the things! Print the output!"""
|
| |
+ args = parse_args()
|
| |
+ action = args.action.split(',')
|
| |
+ if 'allinstall' in action and not args.releasever:
|
| |
+ sys.exit("allinstall action must be run in fake install root, pass --releasever!")
|
| |
+ numloglevel = getattr(logging, args.log.upper())
|
| |
|
| |
logging.basicConfig(filename='modular.log', filemode='w', level=numloglevel)
|
| |
logging.info('Script started.')
|
| |
|
| |
- dnf = DNFoutput()
|
| |
- suite = TestSuite(dnf, whitelist)
|
| |
- # When releasever and fake are not set, it will send the False value
|
| |
- # and the fake environment will not be switched on.
|
| |
- # Release version is important for the installroot, it will not install anything without it.
|
| |
- # The correct releasver should be taked from environmental variables, if you want to use
|
| |
- # this script in OpenQA or some others CIs.
|
| |
- suite.set_fake(fake, releasever)
|
| |
+ # When releasever is set, the fake install root will be enabled
|
| |
+ suite = TestSuite(whitelist=args.whitelist, releasever=args.releasever)
|
| |
|
| |
test = ModuleTest(suite, action)
|
| |
- test.run_test(module, fail, newstream, oldstream, profile)
|
| |
+ test.run_test(args.module, args.fail, args.upgrade, args.stream, args.profile)
|
| |
|
| |
print('')
|
| |
print('========= Results ==========')
|
| |
- results = test.results()
|
| |
+ results = test.overall
|
| |
for key in results:
|
| |
print(f"Tested functionality: {key} => {results[key]}")
|
| |
suite.outputs['total_results'] = results
|
| |
@@ -954,3 +777,10 @@
|
| |
else:
|
| |
logging.info('Script finished with exit code 0.')
|
| |
sys.exit(0)
|
| |
+
|
| |
+ if __name__ == '__main__':
|
| |
+ try:
|
| |
+ main()
|
| |
+ except KeyboardInterrupt:
|
| |
+ sys.stderr.write("Interrupted, exiting...\n")
|
| |
+ sys.exit(1)
|
| |
This fixes a crapton of lint issues, and makes the parsing of the basic module info from DNF a lot saner.