diff --git a/test/functional/test-framework/__init__.py b/test/functional/test-framework/__init__.py deleted file mode 100644 index dce958c..0000000 --- a/test/functional/test-framework/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# diff --git a/test/functional/test-framework/connection/__init__.py b/test/functional/test-framework/connection/__init__.py deleted file mode 100644 index dce958c..0000000 --- a/test/functional/test-framework/connection/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# diff --git a/test/functional/test-framework/connection/base_executor.py b/test/functional/test-framework/connection/base_executor.py deleted file mode 100644 index f47867e..0000000 --- a/test/functional/test-framework/connection/base_executor.py +++ /dev/null @@ -1,85 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import time -from datetime import timedelta - -from core.test_run import TestRun -from test_utils.output import CmdException - - -class BaseExecutor: - def _execute(self, command, timeout): - raise NotImplementedError() - - def _rsync(self, src, dst, delete, symlinks, checksum, exclude_list, timeout, - dut_to_controller): - raise NotImplementedError() - - def rsync_to(self, src, dst, delete=False, symlinks=False, checksum=False, exclude_list=[], - timeout: timedelta = timedelta(seconds=90)): - return self._rsync(src, dst, delete, symlinks, checksum, exclude_list, timeout, False) - - def rsync_from(self, src, dst, delete=False, symlinks=False, checksum=False, exclude_list=[], - timeout: timedelta = timedelta(seconds=90)): - return self._rsync(src, dst, delete, symlinks, checksum, exclude_list, timeout, True) - - def is_remote(self): - return False - - def is_active(self): - return True - - def wait_for_connection(self, timeout: timedelta = None): - pass - - def run(self, command, timeout: timedelta = timedelta(minutes=30)): - if TestRun.dut and TestRun.dut.env: - command = f"{TestRun.dut.env} && {command}" - command_id = TestRun.LOGGER.get_new_command_id() - ip_info = TestRun.dut.ip if len(TestRun.duts) > 1 else "" - TestRun.LOGGER.write_command_to_command_log(command, command_id, info=ip_info) - output = self._execute(command, timeout) - TestRun.LOGGER.write_output_to_command_log(output, command_id) - return output - - def run_in_background(self, - command, - stdout_redirect_path="/dev/null", - stderr_redirect_path="/dev/null"): - command += f" > {stdout_redirect_path} 2> {stderr_redirect_path} &echo $!" - output = self.run(command) - - if output is not None: - return int(output.stdout) - - def wait_cmd_finish(self, pid: int, timeout: timedelta = timedelta(minutes=30)): - self.run(f"tail --pid={pid} -f /dev/null", timeout) - - def check_if_process_exists(self, pid: int): - output = self.run(f"ps aux | awk '{{print $2 }}' | grep ^{pid}$", timedelta(seconds=10)) - return True if output.exit_code == 0 else False - - def kill_process(self, pid: int): - # TERM signal should be used in preference to the KILL signal, since a - # process may install a handler for the TERM signal in order to perform - # clean-up steps before terminating in an orderly fashion. - self.run(f"kill -s SIGTERM {pid} &> /dev/null") - time.sleep(3) - self.run(f"kill -s SIGKILL {pid} &> /dev/null") - - def run_expect_success(self, command, timeout: timedelta = timedelta(minutes=30)): - output = self.run(command, timeout) - if output.exit_code != 0: - raise CmdException(f"Exception occurred while trying to execute '{command}' command.", - output) - return output - - def run_expect_fail(self, command, timeout: timedelta = timedelta(minutes=30)): - output = self.run(command, timeout) - if output.exit_code == 0: - raise CmdException(f"Command '{command}' executed properly but error was expected.", - output) - return output diff --git a/test/functional/test-framework/connection/dummy_executor.py b/test/functional/test-framework/connection/dummy_executor.py deleted file mode 100644 index 0001272..0000000 --- a/test/functional/test-framework/connection/dummy_executor.py +++ /dev/null @@ -1,15 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from connection.base_executor import BaseExecutor - - -class DummyExecutor(BaseExecutor): - def _execute(self, command, timeout=None): - print(command) - - def _rsync(self, src, dst, delete, symlinks, checksum, exclude_list, timeout, - dut_to_controller): - print(f'COPY FROM "{src}" TO "{dst}"') diff --git a/test/functional/test-framework/connection/local_executor.py b/test/functional/test-framework/connection/local_executor.py deleted file mode 100644 index fae9e28..0000000 --- a/test/functional/test-framework/connection/local_executor.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import subprocess -from datetime import timedelta - -from connection.base_executor import BaseExecutor -from test_utils.output import Output - - -class LocalExecutor(BaseExecutor): - def _execute(self, command, timeout): - completed_process = subprocess.run( - command, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - timeout=timeout.total_seconds()) - - return Output(completed_process.stdout, - completed_process.stderr, - completed_process.returncode) - - def _rsync(self, src, dst, delete=False, symlinks=False, checksum=False, exclude_list=[], - timeout: timedelta = timedelta(seconds=90), dut_to_controller=False): - options = [] - - if delete: - options.append("--delete") - if symlinks: - options.append("--links") - if checksum: - options.append("--checksum") - - for exclude in exclude_list: - options.append(f"--exclude {exclude}") - - completed_process = subprocess.run( - f'rsync -r {src} {dst} {" ".join(options)}', - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - timeout=timeout.total_seconds()) - - if completed_process.returncode: - raise Exception(f"rsync failed:\n{completed_process}") diff --git a/test/functional/test-framework/connection/ssh_executor.py b/test/functional/test-framework/connection/ssh_executor.py deleted file mode 100644 index 237420f..0000000 --- a/test/functional/test-framework/connection/ssh_executor.py +++ /dev/null @@ -1,142 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import socket -import subprocess -import paramiko - -from datetime import timedelta, datetime -from connection.base_executor import BaseExecutor -from core.test_run import TestRun -from test_utils.output import Output - - -class SshExecutor(BaseExecutor): - def __init__(self, ip, username, port=22): - self.ip = ip - self.user = username - self.port = port - self.ssh = paramiko.SSHClient() - self._check_config_for_reboot_timeout() - - def __del__(self): - self.ssh.close() - - def connect(self, user=None, port=None, - timeout: timedelta = timedelta(seconds=30)): - user = user or self.user - port = port or self.port - self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - try: - self.ssh.connect(self.ip, username=user, - port=port, timeout=timeout.total_seconds(), - banner_timeout=timeout.total_seconds()) - except paramiko.AuthenticationException as e: - raise paramiko.AuthenticationException( - f"Authentication exception occurred while trying to connect to DUT. " - f"Please check your SSH key-based authentication.\n{e}") - except (paramiko.SSHException, socket.timeout) as e: - raise ConnectionError(f"An exception of type '{type(e)}' occurred while trying to " - f"connect to {self.ip}.\n {e}") - - def disconnect(self): - try: - self.ssh.close() - except Exception: - raise Exception(f"An exception occurred while trying to disconnect from {self.ip}") - - def _execute(self, command, timeout): - try: - (stdin, stdout, stderr) = self.ssh.exec_command(command, - timeout=timeout.total_seconds()) - except paramiko.SSHException as e: - raise ConnectionError(f"An exception occurred while executing command '{command}' on" - f" {self.ip}\n{e}") - - return Output(stdout.read(), stderr.read(), stdout.channel.recv_exit_status()) - - def _rsync(self, src, dst, delete=False, symlinks=False, checksum=False, exclude_list=[], - timeout: timedelta = timedelta(seconds=90), dut_to_controller=False): - options = [] - - if delete: - options.append("--delete") - if symlinks: - options.append("--links") - if checksum: - options.append("--checksum") - - for exclude in exclude_list: - options.append(f"--exclude {exclude}") - - src_to_dst = f"{self.user}@{self.ip}:{src} {dst} " if dut_to_controller else\ - f"{src} {self.user}@{self.ip}:{dst} " - - try: - completed_process = subprocess.run( - f'rsync -r -e "ssh -p {self.port} ' - f'-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" ' - + src_to_dst + f'{" ".join(options)}', - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - timeout=timeout.total_seconds()) - except Exception as e: - TestRun.LOGGER.exception(f"Exception occurred during rsync process. " - f"Please check your SSH key-based authentication.\n{e}") - - if completed_process.returncode: - raise Exception(f"rsync failed:\n{completed_process}") - - def is_remote(self): - return True - - def _check_config_for_reboot_timeout(self): - if "reboot_timeout" in TestRun.config.keys(): - self._parse_timeout_to_int() - else: - self.reboot_timeout = None - - def _parse_timeout_to_int(self): - self.reboot_timeout = int(TestRun.config["reboot_timeout"]) - if self.reboot_timeout < 0: - raise ValueError("Reboot timeout cannot be negative.") - - def reboot(self): - self.run("reboot") - self.wait_for_connection_loss() - self.wait_for_connection(timedelta(seconds=self.reboot_timeout)) \ - if self.reboot_timeout is not None else self.wait_for_connection() - - def is_active(self): - try: - self.ssh.exec_command('', timeout=5) - return True - except Exception: - return False - - def wait_for_connection(self, timeout: timedelta = timedelta(minutes=10)): - start_time = datetime.now() - with TestRun.group("Waiting for DUT ssh connection"): - while start_time + timeout > datetime.now(): - try: - self.connect() - return - except paramiko.AuthenticationException: - raise - except Exception: - continue - raise ConnectionError("Timeout occurred while trying to establish ssh connection") - - def wait_for_connection_loss(self, timeout: timedelta = timedelta(minutes=1)): - with TestRun.group("Waiting for DUT ssh connection loss"): - end_time = datetime.now() + timeout - while end_time > datetime.now(): - self.disconnect() - try: - self.connect(timeout=timedelta(seconds=5)) - except Exception: - return - raise ConnectionError("Timeout occurred before ssh connection loss") diff --git a/test/functional/test-framework/core/__init__.py b/test/functional/test-framework/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/functional/test-framework/core/pair_testing.py b/test/functional/test-framework/core/pair_testing.py deleted file mode 100644 index b7eea73..0000000 --- a/test/functional/test-framework/core/pair_testing.py +++ /dev/null @@ -1,107 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -# The MIT License (MIT) -# -# Copyright (c) 2004-2020 Holger Krekel and others -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -# of the Software, and to permit persons to whom the Software is furnished to do -# so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - -from itertools import product, combinations -import random - -from core.test_run import TestRun - -def testcase_id(param_set): - if len(param_set.values) == 1: - return param_set.values[0] - - return "-".join([str(value) for value in param_set.values]) - - -def generate_pair_testing_testcases(*argvals): - """ - Generate test_cases from provided argument values lists in such way that each possible - (argX, argY) pair will be used. - """ - # if only one argument is used, yield from it - if len(argvals) == 1: - for val in argvals[0]: - yield (val,) - - # append argument index to argument values list to avoid confusion when there are two arguments - # with the same type - for i, arg in enumerate(argvals): - for j, val in enumerate(arg): - arg[j] = (i, val) - - # generate all possible test cases - all_test_cases = list(product(*argvals)) - random.seed(TestRun.random_seed) - random.shuffle(all_test_cases) - - used_pairs = set() - for tc in all_test_cases: - current_pairs = set(combinations(tc, 2)) - # if cardinality of (current_pairs & used_pairs) is lesser than cardinality of current_pairs - # it means not all argument pairs in this tc have been used. return current tc - # and update used_pairs set - if len(current_pairs & used_pairs) != len(current_pairs): - used_pairs.update(current_pairs) - # unpack testcase by deleting argument index - yield list(list(zip(*tc))[1]) - - -def register_testcases(metafunc, argnames, argvals): - """ - Add custom parametrization test cases. Based on metafunc's parametrize method. - """ - from _pytest.python import CallSpec2, _find_parametrized_scope - from _pytest.mark import ParameterSet - from _pytest.fixtures import scope2index - - parameter_sets = [ParameterSet(values=val, marks=[], id=None) for val in argvals] - metafunc._validate_if_using_arg_names(argnames, False) - - arg_value_types = metafunc._resolve_arg_value_types(argnames, False) - - ids = [testcase_id(param_set) for param_set in parameter_sets] - - scope = _find_parametrized_scope(argnames, metafunc._arg2fixturedefs, False) - scopenum = scope2index(scope, descr=f"parametrizex() call in {metafunc.function.__name__}") - - calls = [] - for callspec in metafunc._calls or [CallSpec2(metafunc)]: - for param_index, (param_id, param_set) in enumerate(zip(ids, parameter_sets)): - newcallspec = callspec.copy() - newcallspec.setmulti2( - arg_value_types, - argnames, - param_set.values, - param_id, - param_set.marks, - scopenum, - param_index, - ) - calls.append(newcallspec) - - metafunc._calls = calls diff --git a/test/functional/test-framework/core/plugins.py b/test/functional/test-framework/core/plugins.py deleted file mode 100644 index 7d36224..0000000 --- a/test/functional/test-framework/core/plugins.py +++ /dev/null @@ -1,124 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import pytest -import sys -import importlib -import signal -from core.test_run import TestRun - - -class PluginManager: - def __init__(self, item, config): - if 'plugins_dir' in config: - sys.path.append(config['plugins_dir']) - self.plugins = {} - - self.plugins_config = config.get('plugins', {}) - - self.req_plugins = config.get('req_plugins', {}) - self.opt_plugins = config.get('opt_plugins', {}) - - self.req_plugins.update(dict(map(lambda mark: (mark.args[0], mark.kwargs), - item.iter_markers(name="require_plugin")))) - - req_plugin_mod = {} - opt_plugin_mod = {} - - for name in self.req_plugins: - try: - req_plugin_mod[name] = self.__import_plugin(name) - except ModuleNotFoundError: - pytest.skip("Unable to find requested plugin!") - - for name in self.opt_plugins: - try: - opt_plugin_mod[name] = self.__import_plugin(name) - except ModuleNotFoundError as e: - TestRun.LOGGER.debug( - f"Failed to import '{name}' - optional plugin. " f"Reason: {e}" - ) - continue - - for name, mod in req_plugin_mod.items(): - try: - self.plugins[name] = mod.plugin_class( - self.req_plugins[name], - self.plugins_config.get(name, {}).get("config", {})) - except Exception: - pytest.skip(f"Unable to initialize plugin '{name}'") - - for name, mod in opt_plugin_mod.items(): - try: - self.plugins[name] = mod.plugin_class( - self.opt_plugins[name], - self.plugins_config.get(name, {}).get("config", {})) - except Exception as e: - TestRun.LOGGER.debug( - f"Failed to initialize '{name}' - optional plugin. " f"Reason: {e}" - ) - continue - - def __import_plugin(self, name): - provided_by = self.plugins_config.get(name, {}).get("provided_by") - if provided_by: - return importlib.import_module(provided_by) - - try: - return importlib.import_module(f"internal_plugins.{name}") - except ModuleNotFoundError: - pass - - return importlib.import_module(f"external_plugins.{name}") - - def hook_pre_setup(self): - for plugin in self.plugins.values(): - plugin.pre_setup() - - def hook_post_setup(self): - for plugin in self.plugins.values(): - plugin.post_setup() - - def hook_teardown(self): - for plugin in self.plugins.values(): - plugin.teardown() - - def get_plugin(self, name): - if name not in self.plugins: - raise KeyError("Requested plugin does not exist") - return self.plugins[name] - - def teardown_on_signal(self, sig_id, plugin_name): - try: - plugin = self.get_plugin(plugin_name) - except Exception as e: - TestRun.LOGGER.warning( - f"Failed to setup teardown on signal for {plugin_name}. Reason: {e}") - return - - old_sig_handler = None - - def signal_handler(sig, frame): - plugin.teardown() - - if old_sig_handler is not None: - if old_sig_handler == signal.SIG_DFL: - # In case of SIG_DFL the function pointer points to address 0, - # which is not a valid address. - # We have to reset the handler and raise the signal again - signal.signal(sig, signal.SIG_DFL) - signal.raise_signal(sig) - signal.signal(sig, signal_handler) - elif old_sig_handler == signal.SIG_IGN: - # SIG_IGN has value 1 (also an invalid address). - # Here we can just return (do nothing) - return - else: - # When we received neither SIG_IGN nor SIG_DFL, the received value is - # a valid function pointer and we can call the handler directly - old_sig_handler() - signal.signal(sig, old_sig_handler) - - old_sig_handler = signal.signal(sig_id, signal_handler) diff --git a/test/functional/test-framework/core/test_run.py b/test/functional/test-framework/core/test_run.py deleted file mode 100644 index ae0762e..0000000 --- a/test/functional/test-framework/core/test_run.py +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -from contextlib import contextmanager - -import pytest - -from log.logger import Log - - -class Blocked(Exception): - pass - - -class TestRun: - dut = None - executor = None - LOGGER: Log = None - plugin_manager = None - duts = None - disks = None - - @classmethod - @contextmanager - def use_dut(cls, dut): - cls.dut = dut - cls.config = cls.dut.config - cls.executor = cls.dut.executor - cls.plugin_manager = cls.dut.plugin_manager - cls.disks = cls.dut.req_disks - yield cls.executor - cls.disks = None - cls.plugin_manager = None - cls.executor = None - # setting cls.config to None omitted (causes problems in the teardown stage of execution) - cls.dut = None - - @classmethod - def step(cls, message): - return cls.LOGGER.step(message) - - @classmethod - def group(cls, message): - return cls.LOGGER.group(message) - - @classmethod - def iteration(cls, iterable, group_name=None): - TestRun.LOGGER.start_group(f"{group_name}" if group_name is not None else "Iteration list") - items = list(iterable) - for i, item in enumerate(items, start=1): - cls.LOGGER.start_iteration(f"Iteration {i}/{len(items)}") - yield item - TestRun.LOGGER.end_iteration() - TestRun.LOGGER.end_group() - - @classmethod - def fail(cls, message): - pytest.fail(message) - - @classmethod - def block(cls, message): - raise Blocked(message) diff --git a/test/functional/test-framework/core/test_run_utils.py b/test/functional/test-framework/core/test_run_utils.py deleted file mode 100644 index a27d018..0000000 --- a/test/functional/test-framework/core/test_run_utils.py +++ /dev/null @@ -1,272 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import posixpath -import random -import sys -import traceback - -import pytest -from IPy import IP - -import core.test_run -from connection.local_executor import LocalExecutor -from connection.ssh_executor import SshExecutor -from core.pair_testing import generate_pair_testing_testcases, register_testcases -from core.plugins import PluginManager -from log.base_log import BaseLogResult -from storage_devices.disk import Disk -from test_utils import disk_finder -from test_utils.dut import Dut - -TestRun = core.test_run.TestRun - - -@classmethod -def __configure(cls, config): - config.addinivalue_line( - "markers", - "require_disk(name, type): require disk of specific type, otherwise skip" - ) - config.addinivalue_line( - "markers", - "require_plugin(name, *kwargs): require specific plugins, otherwise skip" - ) - config.addinivalue_line( - "markers", - "remote_only: run test only in case of remote execution, otherwise skip" - ) - config.addinivalue_line( - "markers", - "os_dependent: run test only if its OS dependent, otherwise skip" - ) - config.addinivalue_line( - "markers", - "multidut(number): test requires a number of different platforms to be executed" - ) - config.addinivalue_line( - "markers", - "parametrizex(argname, argvalues): sparse parametrized testing" - ) - config.addinivalue_line( - "markers", - "CI: marks test for continuous integration pipeline" - ) - - cls.random_seed = config.getoption("--random-seed") or random.randrange(sys.maxsize) - random.seed(cls.random_seed) - - -TestRun.configure = __configure - - -@classmethod -def __prepare(cls, item, config): - if not config: - raise Exception("You need to specify DUT config!") - - cls.item = item - cls.config = config - - req_disks = list(map(lambda mark: mark.args, cls.item.iter_markers(name="require_disk"))) - cls.req_disks = dict(req_disks) - if len(req_disks) != len(cls.req_disks): - raise Exception("Disk name specified more than once!") - - -TestRun.prepare = __prepare - - -@classmethod -def __attach_log(cls, log_path, target_name=None): - if target_name is None: - target_name = posixpath.basename(log_path) - if cls.config.get('extra_logs'): - cls.config["extra_logs"][target_name] = log_path - else: - cls.config["extra_logs"] = {target_name: log_path} - - -TestRun.attach_log = __attach_log - - -@classmethod -def __setup_disk(cls, disk_name, disk_type): - cls.disks[disk_name] = next(filter( - lambda disk: disk.disk_type in disk_type.types() and disk not in cls.disks.values(), - cls.dut.disks - ), None) - if not cls.disks[disk_name]: - pytest.skip("Unable to find requested disk!") - - -TestRun.__setup_disk = __setup_disk - - -@classmethod -def __setup_disks(cls): - cls.disks = {} - items = list(cls.req_disks.items()) - while items: - resolved, unresolved = [], [] - for disk_name, disk_type in items: - (resolved, unresolved)[not disk_type.resolved()].append((disk_name, disk_type)) - resolved.sort( - key=lambda disk: (lambda disk_name, disk_type: disk_type)(*disk) - ) - for disk_name, disk_type in resolved: - cls.__setup_disk(disk_name, disk_type) - items = unresolved - cls.dut.req_disks = cls.disks - - -TestRun.__setup_disks = __setup_disks - - -@classmethod -def __presetup(cls): - cls.plugin_manager = PluginManager(cls.item, cls.config) - cls.plugin_manager.hook_pre_setup() - - if cls.config['type'] == 'ssh': - try: - IP(cls.config['ip']) - except ValueError: - TestRun.block("IP address from config is in invalid format.") - - port = cls.config.get('port', 22) - - if 'user' in cls.config: - cls.executor = SshExecutor( - cls.config['ip'], - cls.config['user'], - port - ) - else: - TestRun.block("There is no user given in config.") - elif cls.config['type'] == 'local': - cls.executor = LocalExecutor() - else: - TestRun.block("Execution type (local/ssh) is missing in DUT config!") - - -TestRun.presetup = __presetup - - -@classmethod -def __setup(cls): - if list(cls.item.iter_markers(name="remote_only")): - if not cls.executor.is_remote(): - pytest.skip() - - Disk.plug_all_disks() - if cls.config.get('allow_disk_autoselect', False): - cls.config["disks"] = disk_finder.find_disks() - - try: - cls.dut = Dut(cls.config) - except Exception as ex: - raise Exception(f"Failed to setup DUT instance:\n" - f"{str(ex)}\n{traceback.format_exc()}") - cls.__setup_disks() - - TestRun.LOGGER.info(f"Re-seeding random number generator with seed: {cls.random_seed}") - random.seed(cls.random_seed) - - cls.plugin_manager.hook_post_setup() - - -TestRun.setup = __setup - - -@classmethod -def __makereport(cls, item, call, res): - cls.outcome = res.outcome - step_info = { - 'result': res.outcome, - 'exception': str(call.excinfo.value) if call.excinfo else None - } - setattr(item, "rep_" + res.when, step_info) - - from _pytest.outcomes import Failed - from core.test_run import Blocked - if res.when == "call" and res.failed: - msg = f"{call.excinfo.type.__name__}: {call.excinfo.value}" - if call.excinfo.type is Failed: - cls.LOGGER.error(msg) - elif call.excinfo.type is Blocked: - cls.LOGGER.blocked(msg) - else: - cls.LOGGER.exception(msg) - elif res.when == "setup" and res.failed: - msg = f"{call.excinfo.type.__name__}: {call.excinfo.value}" - cls.LOGGER.exception(msg) - res.outcome = "failed" - - if res.outcome == "skipped": - cls.LOGGER.skip("Test skipped.") - - if res.when == "call" and cls.LOGGER.get_result() == BaseLogResult.FAILED: - res.outcome = "failed" - # To print additional message in final test report, assign it to res.longrepr - - cls.LOGGER.generate_summary(item, cls.config.get('meta')) - - -TestRun.makereport = __makereport - - -@classmethod -def __generate_tests(cls, metafunc): - marks = getattr(metafunc.function, "pytestmark", []) - - parametrizex_marks = [ - mark for mark in marks if mark.name == "parametrizex" - ] - - if not parametrizex_marks: - random.seed(TestRun.random_seed) - return - - argnames = [] - argvals = [] - for mark in parametrizex_marks: - argnames.append(mark.args[0]) - argvals.append(list(mark.args[1])) - - if metafunc.config.getoption("--parametrization-type") == "full": - for name, values in zip(argnames, argvals): - metafunc.parametrize(name, values) - elif metafunc.config.getoption("--parametrization-type") == "pair": - test_cases = generate_pair_testing_testcases(*argvals) - - register_testcases(metafunc, argnames, test_cases) - else: - raise Exception("Not supported parametrization type") - - random.seed(TestRun.random_seed) - - -TestRun.generate_tests = __generate_tests - - -@classmethod -def __addoption(cls, parser): - parser.addoption("--parametrization-type", choices=["pair", "full"], default="pair") - parser.addoption("--random-seed", type=int, default=None) - - -TestRun.addoption = __addoption - - -@classmethod -def __teardown(cls): - for dut in cls.duts: - with cls.use_dut(dut): - if cls.plugin_manager: - cls.plugin_manager.hook_teardown() - - -TestRun.teardown = __teardown diff --git a/test/functional/test-framework/internal_plugins/__init__.py b/test/functional/test-framework/internal_plugins/__init__.py deleted file mode 100644 index dce958c..0000000 --- a/test/functional/test-framework/internal_plugins/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# diff --git a/test/functional/test-framework/internal_plugins/example_plugin/__init__.py b/test/functional/test-framework/internal_plugins/example_plugin/__init__.py deleted file mode 100644 index 4bdec67..0000000 --- a/test/functional/test-framework/internal_plugins/example_plugin/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -class ExamplePlugin: - def __init__(self, params, config): - self.params = params - print(f"Example plugin initialized with params {self.params}") - - def pre_setup(self): - print("Example plugin pre setup") - - def post_setup(self): - print("Example plugin post setup") - - def teardown(self): - print("Example plugin teardown") - - -plugin_class = ExamplePlugin diff --git a/test/functional/test-framework/internal_plugins/power_control_libvirt/__init__.py b/test/functional/test-framework/internal_plugins/power_control_libvirt/__init__.py deleted file mode 100644 index ca2993e..0000000 --- a/test/functional/test-framework/internal_plugins/power_control_libvirt/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -from datetime import timedelta - -from connection.local_executor import LocalExecutor -from connection.ssh_executor import SshExecutor -from core.test_run import TestRun - - -class PowerControlPlugin: - def __init__(self, params, config): - print("Power Control LibVirt Plugin initialization") - try: - self.ip = config['ip'] - self.user = config['user'] - except Exception: - raise Exception("Missing fields in config! ('ip' and 'user' required)") - - def pre_setup(self): - print("Power Control LibVirt Plugin pre setup") - if self.config['connection_type'] == 'ssh': - self.executor = SshExecutor( - self.ip, - self.user, - self.config.get('port', 22) - ) - else: - self.executor = LocalExecutor() - - def post_setup(self): - pass - - def teardown(self): - pass - - def power_cycle(self): - self.executor.run(f"virsh reset {self.config['domain']}") - TestRun.executor.wait_for_connection_loss() - timeout = TestRun.config.get('reboot_timeout') - if timeout: - TestRun.executor.wait_for_connection(timedelta(seconds=int(timeout))) - else: - TestRun.executor.wait_for_connection() - - -plugin_class = PowerControlPlugin diff --git a/test/functional/test-framework/internal_plugins/scsi_debug/__init__.py b/test/functional/test-framework/internal_plugins/scsi_debug/__init__.py deleted file mode 100644 index 95f6563..0000000 --- a/test/functional/test-framework/internal_plugins/scsi_debug/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -from time import sleep - -from core.test_run_utils import TestRun -from storage_devices.device import Device -from test_utils import os_utils -from test_utils.output import CmdException - - -class ScsiDebug: - def __init__(self, params, config): - self.params = params - self.module_name = "scsi_debug" - - def pre_setup(self): - pass - - def post_setup(self): - self.reload() - - def reload(self): - self.teardown() - sleep(1) - load_output = os_utils.load_kernel_module(self.module_name, self.params) - if load_output.exit_code != 0: - raise CmdException(f"Failed to load {self.module_name} module", load_output) - TestRun.LOGGER.info(f"{self.module_name} loaded successfully.") - sleep(10) - TestRun.scsi_debug_devices = Device.get_scsi_debug_devices() - - def teardown(self): - if os_utils.is_kernel_module_loaded(self.module_name): - os_utils.unload_kernel_module(self.module_name) - - -plugin_class = ScsiDebug diff --git a/test/functional/test-framework/internal_plugins/vdbench/__init__.py b/test/functional/test-framework/internal_plugins/vdbench/__init__.py deleted file mode 100644 index d0f7f71..0000000 --- a/test/functional/test-framework/internal_plugins/vdbench/__init__.py +++ /dev/null @@ -1,97 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import time -import posixpath - -from datetime import timedelta -from core.test_run import TestRun -from test_tools import fs_utils - - -class Vdbench: - def __init__(self, params, config): - print("VDBench plugin initialization") - self.run_time = timedelta(seconds=60) - - try: - self.working_dir = config["working_dir"] - self.reinstall = config["reinstall"] - self.source_dir = config["source_dir"] - except Exception: - raise Exception("Missing fields in config! ('working_dir', 'source_dir' and " - "'reinstall' required)") - - self.result_dir = posixpath.join(self.working_dir, 'result.tod') - - def pre_setup(self): - pass - - def post_setup(self): - print("VDBench plugin post setup") - if not self.reinstall and fs_utils.check_if_directory_exists(self.working_dir): - return - - if fs_utils.check_if_directory_exists(self.working_dir): - fs_utils.remove(self.working_dir, True, True) - - fs_utils.create_directory(self.working_dir) - TestRun.LOGGER.info("Copying vdbench to working dir.") - fs_utils.copy(posixpath.join(self.source_dir, "*"), self.working_dir, - True, True) - pass - - def teardown(self): - pass - - def create_config(self, config, run_time: timedelta): - self.run_time = run_time - if config[-1] != ",": - config += "," - config += f"elapsed={int(run_time.total_seconds())}" - TestRun.LOGGER.info(f"Vdbench config:\n{config}") - fs_utils.write_file(posixpath.join(self.working_dir, "param.ini"), config) - - def run(self): - cmd = f"{posixpath.join(self.working_dir, 'vdbench')} " \ - f"-f {posixpath.join(self.working_dir, 'param.ini')} " \ - f"-vr -o {self.result_dir}" - full_cmd = f"screen -dmS vdbench {cmd}" - TestRun.executor.run(full_cmd) - start_time = time.time() - - timeout = self.run_time * 1.5 - - while True: - if not TestRun.executor.run(f"ps aux | grep '{cmd}' | grep -v grep").exit_code == 0: - return self.analyze_log() - - if time.time() - start_time > timeout.total_seconds(): - TestRun.LOGGER.error("Vdbench timeout.") - return False - time.sleep(1) - - def analyze_log(self): - output = TestRun.executor.run( - f"ls -1td {self.result_dir[0:len(self.result_dir) - 3]}* | head -1") - log_path = posixpath.join(output.stdout if output.exit_code == 0 else self.result_dir, - "logfile.html") - - log_file = fs_utils.read_file(log_path) - - if "Vdbench execution completed successfully" in log_file: - TestRun.LOGGER.info("Vdbench execution completed successfully.") - return True - - if "Data Validation error" in log_file or "data_errors=1" in log_file: - TestRun.LOGGER.error("Data corruption occurred!") - elif "Heartbeat monitor:" in log_file: - TestRun.LOGGER.error("Vdbench: heartbeat.") - else: - TestRun.LOGGER.error("Vdbench unknown result.") - return False - - -plugin_class = Vdbench diff --git a/test/functional/test-framework/log/__init__.py b/test/functional/test-framework/log/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/functional/test-framework/log/base_log.py b/test/functional/test-framework/log/base_log.py deleted file mode 100644 index 0f717a4..0000000 --- a/test/functional/test-framework/log/base_log.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from enum import Enum -from re import sub - - -class BaseLogResult(Enum): - DEBUG = 10 - PASSED = 11 - WORKAROUND = 12 - WARNING = 13 - SKIPPED = 14 - FAILED = 15 - EXCEPTION = 16 - BLOCKED = 17 - CRITICAL = 18 - - -def escape(msg): - return sub(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]+', '', msg) - - -class BaseLog(): - def __init__(self, begin_message=None): - self.__begin_msg = begin_message - self.__result = BaseLogResult.PASSED - - def __enter__(self): - if self.__begin_msg is not None: - self.begin(self.__begin_msg) - else: - self.begin("Start BaseLog ...") - - def __exit__(self, *args): - self.end() - - def __try_to_set_new_result(self, new_result): - if new_result.value > self.__result.value: - self.__result = new_result - - def begin(self, message): - pass - - def debug(self, message): - pass - - def info(self, message): - pass - - def workaround(self, message): - self.__try_to_set_new_result(BaseLogResult.WORKAROUND) - - def warning(self, message): - self.__try_to_set_new_result(BaseLogResult.WARNING) - - def skip(self, message): - self.__try_to_set_new_result(BaseLogResult.SKIPPED) - - def error(self, message): - self.__try_to_set_new_result(BaseLogResult.FAILED) - - def blocked(self, message): - self.__try_to_set_new_result(BaseLogResult.BLOCKED) - - def exception(self, message): - self.__try_to_set_new_result(BaseLogResult.EXCEPTION) - - def critical(self, message): - self.__try_to_set_new_result(BaseLogResult.CRITICAL) - - def end(self): - return self.__result - - def get_result(self): - return self.__result diff --git a/test/functional/test-framework/log/group/__init__.py b/test/functional/test-framework/log/group/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/functional/test-framework/log/group/html_chapter_group_log.py b/test/functional/test-framework/log/group/html_chapter_group_log.py deleted file mode 100644 index 56fc7ca..0000000 --- a/test/functional/test-framework/log/group/html_chapter_group_log.py +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from log.base_log import BaseLogResult, BaseLog -from log.group.html_group_log import HtmlGroupLog -from datetime import datetime - - -class HtmlChapterGroupLog(HtmlGroupLog): - SET_RESULT = { - BaseLogResult.PASSED: BaseLog.info, - BaseLogResult.WORKAROUND: BaseLog.workaround, - BaseLogResult.WARNING: BaseLog.warning, - BaseLogResult.SKIPPED: BaseLog.skip, - BaseLogResult.FAILED: BaseLog.error, - BaseLogResult.BLOCKED: BaseLog.blocked, - BaseLogResult.EXCEPTION: BaseLog.exception, - BaseLogResult.CRITICAL: BaseLog.critical} - - def __init__(self, html_base, cfg, begin_msg=None, id='ch0'): - super().__init__(HtmlChapterGroupLog._factory, html_base, cfg, begin_msg, id) - - @staticmethod - def _factory(html_base, cfg, begin_msg, id): - return HtmlChapterGroupLog(html_base, cfg, begin_msg, id) - - def end_dir_group(self, ref_group): - group = super().end_group() - ref_container_id = ref_group._container.get('id') - group._header.set('ondblclick', f"chapterClick('{ref_container_id}')") - - def set_result(self, result): - if self._successor is not None: - self._successor.set_result(result) - HtmlChapterGroupLog.SET_RESULT[result](self, "set result") - - def end(self): - result = super().end() - exe_time = (datetime.now() - self._start_time).seconds - self._cfg.group_chapter_end(exe_time, self._header, self._container, result) - return result diff --git a/test/functional/test-framework/log/group/html_group_log.py b/test/functional/test-framework/log/group/html_group_log.py deleted file mode 100644 index cba5075..0000000 --- a/test/functional/test-framework/log/group/html_group_log.py +++ /dev/null @@ -1,139 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from datetime import datetime -from log.base_log import BaseLog, BaseLogResult - - -class HtmlGroupLog(BaseLog): - def __init__(self, constructor, html_base_element, cfg, begin_message, id_): - super().__init__(begin_message) - self._successor = None - self.__factory = constructor - self.__log_main_store = html_base_element - self._id = id_ - self._container = None - self._header = None - self.__msg_idx = 0 - self._start_time = datetime.now() - self._cfg = cfg - self._header_msg_type = type(begin_message) - - def begin(self, message): - policy = self._cfg.get_policy(type(message)) - self._header, self._container = policy.group_begin(self._id, message, self.__log_main_store) - super().begin(message) - - def get_step_id(self): - if self._successor is not None: - return self._successor.get_step_id() - else: - return f'step.{self._id}.{self.__msg_idx}' - - def __add_test_step(self, message, result=BaseLogResult.PASSED): - policy = self._cfg.get_policy(type(message)) - policy.standard(self.get_step_id(), message, result, self._container) - self.__msg_idx += 1 - - def get_main_log_store(self): - return self.__log_main_store - - def start_group(self, message): - self._header_msg_type = type(message) - if self._successor is not None: - result = self._successor.start_group(message) - else: - new_id = f"{self._id}.{self.__msg_idx}" - self.__msg_idx += 1 - self._successor = self.__factory(self._container, self._cfg, message, new_id) - self._successor.begin(message) - result = self._successor - return result - - def end_group(self): - if self._successor is not None: - if self._successor._successor is None: - self._successor.end() - result = self._successor - self._successor = None - else: - result = self._successor.end_group() - else: - self.end() - result = self - return result - - def debug(self, message): - if self._successor is not None: - self._successor.debug(message) - else: - self.__add_test_step(message, BaseLogResult.DEBUG) - return super().debug(message) - - def info(self, message): - if self._successor is not None: - self._successor.info(message) - else: - self.__add_test_step(message) - super().info(message) - - def workaround(self, message): - if self._successor is not None: - self._successor.workaround(message) - else: - self.__add_test_step(message, BaseLogResult.WORKAROUND) - super().workaround(message) - - def warning(self, message): - if self._successor is not None: - self._successor.warning(message) - else: - self.__add_test_step(message, BaseLogResult.WARNING) - super().warning(message) - - def skip(self, message): - if self._successor is not None: - self._successor.skip(message) - else: - self.__add_test_step(message, BaseLogResult.SKIPPED) - super().skip(message) - - def error(self, message): - if self._successor is not None: - self._successor.error(message) - else: - self.__add_test_step(message, BaseLogResult.FAILED) - super().error(message) - - def blocked(self, message): - if self._successor is not None: - self._successor.blocked(message) - else: - self.__add_test_step(message, BaseLogResult.BLOCKED) - super().blocked(message) - - def critical(self, message): - if self._successor is not None: - self._successor.critical(message) - else: - self.__add_test_step(message, BaseLogResult.CRITICAL) - super().critical(message) - - def exception(self, message): - if self._successor is not None: - self._successor.exception(message) - else: - self.__add_test_step(message, BaseLogResult.EXCEPTION) - super().exception(message) - - def end(self): - return super().end() - - def get_current_group(self): - if self._successor is not None: - result = self._successor.get_current_group() - else: - result = self - return result diff --git a/test/functional/test-framework/log/group/html_iteration_group_log.py b/test/functional/test-framework/log/group/html_iteration_group_log.py deleted file mode 100644 index d594018..0000000 --- a/test/functional/test-framework/log/group/html_iteration_group_log.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from log.group.html_group_log import HtmlGroupLog - - -class HtmlIterationGroupLog(HtmlGroupLog): - def __init__(self, html_base, cfg, begin_msg, id='itg0'): - super().__init__(HtmlIterationGroupLog._factory, html_base, cfg, begin_msg, id) - - @staticmethod - def _factory(html_base, cfg, begin_msg, id): - return HtmlIterationGroupLog(html_base, cfg, begin_msg, id) - - def end(self): - result = super().end() - self._cfg.group_end(self._id, self._header, self._container, result) - return result diff --git a/test/functional/test-framework/log/html_file_item_log.py b/test/functional/test-framework/log/html_file_item_log.py deleted file mode 100644 index b529762..0000000 --- a/test/functional/test-framework/log/html_file_item_log.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from log.html_file_log import HtmlFileLog -from log.group.html_chapter_group_log import HtmlChapterGroupLog -from log.group.html_iteration_group_log import HtmlIterationGroupLog -from datetime import datetime -from lxml.etree import Element - - -class HtmlFileItemLog(HtmlFileLog): - def __init__(self, html_file_path, test_title, cfg, iteration_title="Test summary"): - super().__init__(html_file_path, test_title) - root = self.get_root() - self._log_items_store = root.xpath('/html/body')[0] - self._idx = 0 - self._log_chapters_store = root.xpath('/html/body/section[@id="iteration-chapters"]')[0] - self._chapter_group = HtmlChapterGroupLog(self._log_chapters_store, cfg, test_title) - self._main_group = HtmlIterationGroupLog(self._log_items_store, cfg, test_title) - self._start_time = datetime.now() - iteration_title_node = root.xpath('/html/body/a/h1')[0] - iteration_title_node.text = iteration_title - self._config = cfg - self._fail_container = root.xpath('/html/body/div/select[@id="error-list-selector"]')[0] - - def __add_error(self, msg_idx, msg, error_class): - fail_element = Element('option', value=msg_idx) - fail_element.set('class', error_class) - fail_element.text = msg - self._fail_container.append(fail_element) - - def start_iteration(self, message): - super().begin(message) - - def get_result(self): - return self._main_group.get_result() - - def begin(self, message): - self._chapter_group.begin(message) - self._main_group.begin(message) - - def debug(self, message): - self._main_group.debug(message) - - def info(self, message): - self._main_group.info(message) - - def workaround(self, message): - self._main_group.workaround(message) - - def warning(self, message): - self._main_group.warning(message) - - def skip(self, message): - self._main_group.skip(message) - - def error(self, message): - msg_idx = self._main_group.get_step_id() - self.__add_error(msg_idx, message, "fail") - self._main_group.error(message) - - def blocked(self, message): - msg_idx = self._main_group.get_step_id() - self.__add_error(msg_idx, message, "blocked") - self._main_group.blocked(message) - - def exception(self, message): - msg_idx = self._main_group.get_step_id() - self.__add_error(msg_idx, message, "exception") - self._main_group.exception(message) - - def critical(self, message): - msg_idx = self._main_group.get_step_id() - self.__add_error(msg_idx, message, "critical") - self._main_group.critical(message) - - def start_group(self, message): - self._chapter_group.start_group(message) - self._main_group.start_group(message) - - def end_group(self): - ref_group = self._main_group.get_current_group() - self._chapter_group.set_result(ref_group.get_result()) - self._main_group.end_group() - self._chapter_group.end_dir_group(ref_group) - - def end_all_groups(self): - while self._main_group._successor is not None: - self.end_group() - - def end(self): - while self._main_group._successor is not None: - self.end_group() - self.end_group() - time_result = datetime.now() - self._start_time - time_node = self.get_root().xpath('/html/body/div[@class="iteration-execution-time"]')[0] - status_node = self.get_root().xpath('/html/body/div[@class="iteration-status"]')[0] - self._config.end_iteration_func( - time_node, status_node, time_result.total_seconds(), self.get_result()) - super().end() diff --git a/test/functional/test-framework/log/html_file_log.py b/test/functional/test-framework/log/html_file_log.py deleted file mode 100644 index 55680da..0000000 --- a/test/functional/test-framework/log/html_file_log.py +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from log.base_log import BaseLog -from lxml.html import fromstring -from lxml.html import tostring - - -class HtmlFileLog(BaseLog): - def __init__(self, file_path, title): - super().__init__(title) - self.__path = file_path - with open(file_path) as file_stream: - self.__root = fromstring(file_stream.read()) - node_list = self.__root.xpath('/html/head/title') - node_list[0].text = title - - def get_path(self): - return self.__path - - def get_root(self): - return self.__root - - def end(self): - with open(self.__path, "wb") as file: - x = tostring(self.__root) - file.write(x) diff --git a/test/functional/test-framework/log/html_iteration_log.py b/test/functional/test-framework/log/html_iteration_log.py deleted file mode 100644 index b4f6c4c..0000000 --- a/test/functional/test-framework/log/html_iteration_log.py +++ /dev/null @@ -1,13 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from log.html_file_item_log import HtmlFileItemLog - - -class HtmlIterationLog(HtmlFileItemLog): - def __init__(self, test_title, iteration_title, config): - self.iteration_closed: bool = False - html_file = config.create_iteration_file() - super().__init__(html_file, test_title, config, iteration_title) diff --git a/test/functional/test-framework/log/html_log_config.py b/test/functional/test-framework/log/html_log_config.py deleted file mode 100644 index d06e7f0..0000000 --- a/test/functional/test-framework/log/html_log_config.py +++ /dev/null @@ -1,204 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import os -from os import path, environ, makedirs -from datetime import datetime -from shutil import copyfile -from lxml.etree import Element -from log.base_log import BaseLogResult -from log.presentation_policy import null_policy - - -def convert_seconds_to_str(time_in_sec): - h = str(int(time_in_sec / 3600) % 24).zfill(2) - m = str(int(time_in_sec / 60) % 60).zfill(2) - s = str(int(time_in_sec % 60)).zfill(2) - time_msg = f"{h}:{m}:{s} [s]" - if time_in_sec > 86400: - time_msg = f"{int(time_in_sec // (3600 * 24))}d {time_msg}" - return time_msg - - -class HtmlLogConfig: - STYLE = { - BaseLogResult.DEBUG: 'debug', - BaseLogResult.PASSED: '', - BaseLogResult.WORKAROUND: 'workaround', - BaseLogResult.WARNING: 'warning', - BaseLogResult.SKIPPED: 'skip', - BaseLogResult.FAILED: 'fail', - BaseLogResult.BLOCKED: 'blocked', - BaseLogResult.CRITICAL: 'critical', - BaseLogResult.EXCEPTION: 'exception'} - - __MAIN = 'main' - __SETUP = 'setup' - __T_ITERATION = 'iteration' - __FRAMEWORK_T_FOLDER = 'template' - - MAIN = __MAIN + '.html' - CSS = __MAIN + '.css' - JS = __MAIN + '.js' - - ITERATION_FOLDER = 'iterations' - SETUP = __SETUP + ".html" - - def iteration(self): - return f'{HtmlLogConfig.__T_ITERATION}_{str(self._iteration_id).zfill(3)}.html' - - def __init__(self, base_dir=None, presentation_policy=null_policy): - self._log_base_dir = base_dir - if base_dir is None: - if os.name == 'nt': - self._log_base_dir = 'c:\\History' - else: - if environ["USER"] == 'root': - self._log_base_dir = '/root/history' - else: - self._log_base_dir = f'/home/{environ["USER"]}' - self._log_dir = None - self._presentation_policy = {} - self.register_presentation_policy(str, presentation_policy) - self._iteration_id = 0 - - def get_iteration_id(self): - return self._iteration_id - - def get_policy(self, type): - return self._presentation_policy[type] - - def get_policy_collection(self): - for type, policy in self._presentation_policy.items(): - yield policy - - def register_presentation_policy(self, type, presentation_policy): - self._presentation_policy[type] = presentation_policy - - def __find_template_file(self, name, relative_path=None): - base_dir = path.dirname(path.abspath(__file__)) - file_path = path.join(base_dir, HtmlLogConfig.__FRAMEWORK_T_FOLDER) - if relative_path is not None: - file_path = path.join(file_path, relative_path) - file_path = path.join(file_path, name) - if path.isfile(file_path): - return file_path - else: - raise Exception( - f"Unable to find file: {name} in location: {os.path.dirname(file_path)}") - - def __get_main_template_file_path(self): - return self.__find_template_file(HtmlLogConfig.MAIN) - - def _get_setup_template_file_path(self): - return self.__find_template_file(HtmlLogConfig.SETUP, HtmlLogConfig.ITERATION_FOLDER) - - def __get_iteration_template_path(self): - return self.__find_template_file(HtmlLogConfig.__T_ITERATION + '.html', - HtmlLogConfig.ITERATION_FOLDER) - - def create_html_test_log(self, test_title): - now = datetime.now() - time_stamp = f"{now.year}_{str(now.month).zfill(2)}_{str(now.day).zfill(2)}_" \ - f"{str(now.hour).zfill(2)}_{str(now.minute).zfill(2)}_{str(now.second).zfill(2)}" - self._log_dir = path.join(self._log_base_dir, test_title, time_stamp) - makedirs(self._log_dir) - additional_location = path.join(self._log_dir, HtmlLogConfig.ITERATION_FOLDER) - makedirs(additional_location) - dut_info_folder = path.join(self._log_dir, 'dut_info') - makedirs(dut_info_folder) - main_html = self.__get_main_template_file_path() - main_css = main_html.replace('html', 'css') - main_js = main_html.replace('html', 'js') - copyfile(main_html, path.join(self._log_dir, HtmlLogConfig.MAIN)) - copyfile(main_css, path.join(self._log_dir, HtmlLogConfig.CSS)) - copyfile(main_js, path.join(self._log_dir, HtmlLogConfig.JS)) - copyfile(self._get_setup_template_file_path(), path.join(additional_location, - HtmlLogConfig.SETUP)) - return self._log_dir - - def get_main_file_path(self): - return path.join(self._log_dir, HtmlLogConfig.MAIN) - - def get_setup_file_path(self): - return path.join(self._log_dir, HtmlLogConfig.ITERATION_FOLDER, HtmlLogConfig.SETUP) - - def create_iteration_file(self): - self._iteration_id += 1 - template_file = self.__get_iteration_template_path() - new_file_name = self.iteration() - result = path.join(self._log_dir, HtmlLogConfig.ITERATION_FOLDER, new_file_name) - copyfile(template_file, result) - return result - - def end_iteration(self, - iteration_selector_div, - iteration_selector_select, - iteration_id, - iteration_result): - style = "iteration-selector" - if iteration_result != BaseLogResult.PASSED: - style = f'{style} {HtmlLogConfig.STYLE[iteration_result]}' - if iteration_id and iteration_id % 8 == 0: - new_element = Element("br") - iteration_selector_div[0].append(new_element) - new_element = Element("a") - new_element.set('class', style) - new_element.set('onclick', f"selectIteration('{iteration_id}')") - new_element.text = str(iteration_id) - iteration_selector_div[0].append(new_element) - new_element = Element('option', value=f"{iteration_id}") - new_element.text = 'iteration_' + str(iteration_id).zfill(3) - if iteration_result != BaseLogResult.PASSED: - new_element.set('class', HtmlLogConfig.STYLE[iteration_result]) - iteration_selector_select.append(new_element) - - def end_setup_iteration(self, iteration_selector_div, iteration_selector_select, log_result): - if log_result != BaseLogResult.PASSED: - a_element = iteration_selector_div[0] - select_element = iteration_selector_select[0] - a_element.set('class', f'iteration-selector {HtmlLogConfig.STYLE[log_result]}') - select_element.set('class', HtmlLogConfig.STYLE[log_result]) - - def end_iteration_func(self, time_node, status_node, time_in_sec, log_result): - time_node.text = f"Execution time: {convert_seconds_to_str(time_in_sec)}" - status_node.text = f"Iteration status: {log_result.name}" - if log_result != BaseLogResult.PASSED: - status_node.set('class', f'iteration-status {HtmlLogConfig.STYLE[log_result]}') - - def end_main_log(self, test_status_div, log_result): - if log_result != BaseLogResult.PASSED: - test_status_div[0].set('class', - f"sidebar-test-status {HtmlLogConfig.STYLE[log_result]}") - test_status_div[0].text = f"Test status: {log_result.name}" - - def group_end(self, msg_id, html_header, html_container, log_result): - html_header.set('onclick', f"showHide('ul_{msg_id}')") - sub_element = Element('a', href="#top") - sub_element.text = "[TOP]" - sub_element.set('class', "top-time-marker") - html_header.append(sub_element) - div_style = 'test-group-step' - ul_style = 'iteration-content' - if log_result == BaseLogResult.PASSED: - html_container.set('style', "display: none;") - else: - div_style = f"{div_style} {HtmlLogConfig.STYLE[log_result]}" - ul_style = f"{ul_style} {HtmlLogConfig.STYLE[log_result]}" - html_header.set('class', div_style) - html_container.set('class', ul_style) - - def group_chapter_end(self, time_in_sec, html_header, html_container, log_result): - sub_element = Element('a') - sub_element.text = convert_seconds_to_str(time_in_sec) - sub_element.set('class', 'top-marker') - html_header.append(sub_element) - div_style = 'test-group-step' - ul_style = 'iteration-content' - if log_result != BaseLogResult.PASSED: - div_style = f"{div_style} {HtmlLogConfig.STYLE[log_result]}" - ul_style = f"{ul_style} {HtmlLogConfig.STYLE[log_result]}" - html_header.set('class', div_style) - html_container.set('class', ul_style) diff --git a/test/functional/test-framework/log/html_log_manager.py b/test/functional/test-framework/log/html_log_manager.py deleted file mode 100644 index 30ba196..0000000 --- a/test/functional/test-framework/log/html_log_manager.py +++ /dev/null @@ -1,126 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -from log.base_log import BaseLog, escape -from log.html_iteration_log import HtmlIterationLog -from log.html_log_config import HtmlLogConfig -from log.html_main_log import HtmlMainLog -from log.html_setup_log import HtmlSetupLog - - -class HtmlLogManager(BaseLog): - def __init__(self, begin_message=None, log_config=None): - super().__init__(begin_message) - self._config = HtmlLogConfig() if log_config is None else log_config - self._main = None - self._log_setup = None - self._log_iterations = [] - self._current_log = None - self._files_path = None - - def __add(self, msg): - pass - - def begin(self, message): - self._files_path = self._config.create_html_test_log(message) - self._main = HtmlMainLog(message, self._config) - self._log_setup = HtmlSetupLog(message, config=self._config) - self._current_log = self._log_setup - self._main.begin(message) - self._current_log.begin(message) - self.__add("begin: " + message) - - @property - def base_dir(self): - return self._files_path - - def get_result(self): - log_result = self._log_setup.get_result() - for iteration in self._log_iterations: - if log_result.value < iteration.get_result().value: - log_result = iteration.get_result() - return log_result - - def end(self): - self._log_setup.end() - self._main.end_setup_iteration(self._log_setup.get_result()) - log_result = self.get_result() - self._main.end(log_result) - self.__add("end") - - def add_build_info(self, message): - self._main.add_build_info(escape(message)) - - def start_iteration(self, message): - message = escape(message) - self._log_iterations.append(HtmlIterationLog(message, message, self._config)) - self._main.start_iteration(self._config.get_iteration_id()) - self._current_log = self._log_iterations[-1] - self._current_log.begin(message) - self._log_setup.start_iteration(message) - self.__add("start_iteration: " + message) - - def end_iteration(self): - self._current_log.end() - self._main.end_iteration(self._current_log.get_result()) - self._log_setup.end_iteration(self._current_log.get_result()) - self._current_log.iteration_closed = True - self._current_log = self._log_setup - self.__add("end_iteration: ") - return self._current_log - - def debug(self, message): - self._current_log.debug(escape(message)) - self.__add("debug: " + message) - - def info(self, message): - self._current_log.info(escape(message)) - self.__add("info: " + message) - - def workaround(self, message): - self._current_log.workaround(escape(message)) - self.__add(": " + message) - - def warning(self, message): - self._current_log.warning(escape(message)) - self.__add(": " + message) - - def skip(self, message): - self._current_log.skip(escape(message)) - self.__add("warning: " + message) - - def error(self, message): - self._current_log.error(escape(message)) - self.__add("error: " + message) - - def blocked(self, message): - self._current_log.blocked(escape(message)) - self.__add(f'blocked: {message}') - self.end_all_groups() - - def exception(self, message): - self._current_log.exception(escape(message)) - self.__add("exception: " + message) - self.end_all_groups() - - def critical(self, message): - self._current_log.critical(escape(message)) - self.__add("critical: " + message) - self.end_all_groups() - - def start_group(self, message): - self._current_log.start_group(escape(message)) - self.__add("start_group: " + message) - - def end_group(self): - self._current_log.end_group() - self.__add("end_group") - - def end_all_groups(self): - for iteration in reversed(self._log_iterations): - if not iteration.iteration_closed: - self.end_iteration() - self._current_log.end_all_groups() diff --git a/test/functional/test-framework/log/html_main_log.py b/test/functional/test-framework/log/html_main_log.py deleted file mode 100644 index f19f070..0000000 --- a/test/functional/test-framework/log/html_main_log.py +++ /dev/null @@ -1,53 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from log.html_file_log import HtmlFileLog -from lxml.etree import Element - - -class HtmlMainLog(HtmlFileLog): - def __init__(self, title, config): - super().__init__(config.get_main_file_path(), title) - self._config = config - self.__current_iteration_id = None - root = self.get_root() - test_title_div = root.xpath('/html/body/div/div/div/div[@class="sidebar-test-title"]')[0] - test_title_div.text = title - self.__build_information_set = root.xpath( - '/html/body/div/div/div/div[@id="sidebar-tested-build"]')[0] - - def add_build_info(self, message): - build_info = Element("div") - build_info.text = message - self.__build_information_set.append(build_info) - - def start_iteration(self, iteration_id): - self.__current_iteration_id = iteration_id - - def end_iteration(self): - pass - - def end_iteration(self, iteration_result): - root = self.get_root() - iteration_selector_div = root.xpath('/html/body/div/div/div[@id="iteration-selector"]') - iteration_selector_select = root.xpath( - '/html/body/div/div/select[@id="sidebar-iteration-list"]')[0] - self._config.end_iteration(iteration_selector_div, - iteration_selector_select, - self.__current_iteration_id, - iteration_result) - - def end_setup_iteration(self, result): - root = self.get_root() - iteration_selector_div = root.xpath('/html/body/div/div/div[@id="iteration-selector"]')[0] - iteration_selector_select = root.xpath( - '/html/body/div/div/select[@id="sidebar-iteration-list"]')[0] - self._config.end_setup_iteration(iteration_selector_div, iteration_selector_select, result) - - def end(self, result): - root = self.get_root() - test_status_div = root.xpath('/html/body/div/div/div/div[@class="sidebar-test-status"]') - self._config.end_main_log(test_status_div, result) - super().end() diff --git a/test/functional/test-framework/log/html_presentation_policy.py b/test/functional/test-framework/log/html_presentation_policy.py deleted file mode 100644 index 62cb7be..0000000 --- a/test/functional/test-framework/log/html_presentation_policy.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from log.base_log import BaseLogResult -from lxml.etree import Element -from datetime import datetime -from log.presentation_policy import PresentationPolicy -from log.html_log_config import HtmlLogConfig - - -def std_log_entry(msg_id, msg, log_result, html_node): - test_step = Element('li') - style = 'test-step' - if log_result != BaseLogResult.PASSED: - style = f"{style} {HtmlLogConfig.STYLE[log_result]}" - test_step.set('class', style) - test_time = Element('div') - test_time.set('class', 'ts-time') - test_time_txt = Element('a', name=msg_id) - time = datetime.now() - test_time_txt.text = f"{str(time.hour).zfill(2)}:" \ - f"{str(time.minute).zfill(2)}:{str(time.second).zfill(2)}" - test_time.append(test_time_txt) - test_step.append(test_time) - test_msg = Element('div') - test_msg.set('class', 'ts-msg') - test_msg.text = msg - test_step.append(test_msg) - html_node.append(test_step) - - -def group_log_begin(msg_id, msg, html_node): - element = Element("div") - sub_element = Element('a', name=msg_id) - sub_element.text = msg - element.append(sub_element) - html_node.append(element) - ul_set = Element('ul', id=f'ul_{msg_id}') - html_node.append(ul_set) - return element, ul_set - - -html_policy = PresentationPolicy(std_log_entry, group_log_begin) diff --git a/test/functional/test-framework/log/html_setup_log.py b/test/functional/test-framework/log/html_setup_log.py deleted file mode 100644 index 3b93fb3..0000000 --- a/test/functional/test-framework/log/html_setup_log.py +++ /dev/null @@ -1,34 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from log.html_file_item_log import HtmlFileItemLog -from log.base_log import BaseLogResult - - -class HtmlSetupLog(HtmlFileItemLog): - - LOG_RESULT = { - BaseLogResult.PASSED: HtmlFileItemLog.info, - BaseLogResult.WORKAROUND: HtmlFileItemLog.workaround, - BaseLogResult.WARNING: HtmlFileItemLog.warning, - BaseLogResult.SKIPPED: HtmlFileItemLog.skip, - BaseLogResult.FAILED: HtmlFileItemLog.error, - BaseLogResult.BLOCKED: HtmlFileItemLog.blocked, - BaseLogResult.EXCEPTION: HtmlFileItemLog.exception, - BaseLogResult.CRITICAL: HtmlFileItemLog.critical} - - def __init__(self, test_title, config, iteration_title="Test summary"): - html_file_path = config.get_setup_file_path() - super().__init__(html_file_path, test_title, config, iteration_title) - self._last_iteration_title = '' - - def start_iteration(self, message): - self._last_iteration_title = message - - def end_iteration(self, iteration_result): - HtmlSetupLog.LOG_RESULT[iteration_result](self, self._last_iteration_title) - - def end(self): - super().end() diff --git a/test/functional/test-framework/log/logger.py b/test/functional/test-framework/log/logger.py deleted file mode 100644 index 07a40bb..0000000 --- a/test/functional/test-framework/log/logger.py +++ /dev/null @@ -1,220 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import logging -import os -import sys -from contextlib import contextmanager -from datetime import datetime -from threading import Lock - -import portalocker - -from log.html_log_config import HtmlLogConfig -from log.html_log_manager import HtmlLogManager -from log.html_presentation_policy import html_policy -from test_utils.output import Output -from test_utils.singleton import Singleton - - -def create_log(log_base_path, test_module, additional_args=None): - Log.setup() - log_cfg = HtmlLogConfig(base_dir=log_base_path, - presentation_policy=html_policy) - log = Log(log_config=log_cfg) - test_name = 'TestNameError' - error_msg = None - try: - test_name = test_module - if additional_args: - test_name += f"__{'_'.join(additional_args)}" - except Exception as ex: - error_msg = f'Detected some problems during calculating test name: {ex}' - finally: - log.begin(test_name) - print(f"\n{os.path.join(log.base_dir, 'main.html')}") - if error_msg: - log.exception(error_msg) - return log - - -class Log(HtmlLogManager, metaclass=Singleton): - logger = None - LOG_FORMAT = '%(asctime)s %(levelname)s:\t%(message)s' - DATE_FORMAT = "%Y/%m/%d %H:%M:%S" - command_id = 0 - lock = Lock() - - @classmethod - def destroy(cls): - del cls._instances[cls] - - @classmethod - def setup(cls): - - # Get handle to root logger. - logger = logging.getLogger() - logger.setLevel(logging.DEBUG) - - # Set paramiko log level to warning - logging.getLogger('paramiko').setLevel(logging.WARNING) - - # Create Handlers. - stdout_handler = logging.StreamHandler(sys.stdout) - - # Set logging level on handlers. - stdout_handler.setLevel(logging.DEBUG) - - # Set log formatting on each handler. - formatter = logging.Formatter(Log.LOG_FORMAT, Log.DATE_FORMAT) - stdout_handler.setFormatter(formatter) - - # Attach handlers to root logger. - logger.handlers = [] - logger.addHandler(stdout_handler) - cls.logger = logger - logger.info("Logger successfully initialized.") - - @contextmanager - def step(self, message): - self.step_info(message) - super(Log, self).start_group(message) - if Log.logger: - Log.logger.info(message) - yield - super(Log, self).end_group() - - @contextmanager - def group(self, message): - self.start_group(message) - yield - self.end_group() - - def add_build_info(self, msg): - super(Log, self).add_build_info(msg) - if Log.logger: - Log.logger.info(msg) - - def info(self, msg): - super(Log, self).info(msg) - if Log.logger: - Log.logger.info(msg) - - def debug(self, msg): - super(Log, self).debug(msg) - if Log.logger: - Log.logger.debug(msg) - - def error(self, msg): - super(Log, self).error(msg) - if Log.logger: - Log.logger.error(msg) - - def blocked(self, msg): - super(Log, self).blocked(msg) - if Log.logger: - Log.logger.fatal(msg) - - def exception(self, msg): - super(Log, self).exception(msg) - if Log.logger: - Log.logger.exception(msg) - - def critical(self, msg): - super(Log, self).critical(msg) - if Log.logger: - Log.logger.fatal(msg) - - def workaround(self, msg): - super(Log, self).workaround(msg) - if Log.logger: - Log.logger.warning(msg) - - def warning(self, msg): - super(Log, self).warning(msg) - if Log.logger: - Log.logger.warning(msg) - - def get_new_command_id(self): - self.lock.acquire() - command_id = self.command_id - self.command_id += 1 - self.lock.release() - return command_id - - def write_to_command_log(self, message): - super(Log, self).debug(message) - command_log_path = os.path.join(self.base_dir, "dut_info", 'commands.log') - timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S:%f') - with portalocker.Lock(command_log_path, "ab+") as command_log: - line_to_write = f"[{timestamp}] {message}\n" - command_log.write(line_to_write.encode()) - - def write_command_to_command_log(self, command, command_id, info=None): - added_info = "" if info is None else f"[{info}] " - self.write_to_command_log(f"{added_info}Command id: {command_id}\n{command}") - - def write_output_to_command_log(self, output: Output, command_id): - if output is not None: - line_to_write = f"Command id: {command_id}\n\texit code: {output.exit_code}\n" \ - f"\tstdout: {output.stdout}\n" \ - f"\tstderr: {output.stderr}\n\n\n" - self.write_to_command_log(line_to_write) - else: - self.write_to_command_log(f"Command id: {command_id}\n\tNone output.") - - def step_info(self, step_name): - from core.test_run import TestRun - decorator = "// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n\n" - message = f"\n\n\n{decorator}{step_name}\n\n{decorator}\n" - - try: - serial_monitor = TestRun.plugin_manager.get_plugin("serial_monitor") - serial_monitor.send_to_serial(message) - except (KeyError, AttributeError): - pass - self.write_to_command_log(message) - - def get_additional_logs(self): - from core.test_run import TestRun - from test_tools.fs_utils import check_if_file_exists - messages_log = "/var/log/messages" - if not check_if_file_exists(messages_log): - messages_log = "/var/log/syslog" - log_files = {"messages.log": messages_log, - "dmesg.log": "/tmp/dmesg"} - extra_logs = TestRun.config.get("extra_logs", {}) - log_files.update(extra_logs) - - TestRun.executor.run(f"dmesg > {log_files['dmesg.log']}") - - for log_name, log_source_path in log_files.items(): - try: - log_destination_path = os.path.join( - self.base_dir, f"dut_info", TestRun.dut.ip, log_name - ) - TestRun.executor.rsync_from(log_source_path, log_destination_path) - except Exception as e: - TestRun.LOGGER.warning( - f"There was a problem during gathering {log_name} log.\n{str(e)}" - ) - - def generate_summary(self, item, meta): - import json - summary_path = os.path.join(self.base_dir, 'info.json') - with open(summary_path, "w+") as summary: - data = { - 'module': os.path.relpath(item.fspath, os.getcwd()), - 'function': item.name, - 'meta': meta, - 'status': self.get_result().name, - 'path': os.path.normpath(self.base_dir), - 'stage_status': { - 'setup': getattr(item, "rep_setup", {}), - 'call': getattr(item, "rep_call", {}), - 'teardown': getattr(item, "rep_teardown", {}) - } - } - json.dump(data, summary) diff --git a/test/functional/test-framework/log/presentation_policy.py b/test/functional/test-framework/log/presentation_policy.py deleted file mode 100644 index 5409d08..0000000 --- a/test/functional/test-framework/log/presentation_policy.py +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -class PresentationPolicy: - def __init__(self, standard_log, group_begin_func): - self.standard = standard_log - self.group_begin = group_begin_func - - -def std_log_entry(msg_id, msg, log_result, html_node): - pass - - -def group_log_begin(msg_id, msg, html_node): - return html_node, html_node - - -null_policy = PresentationPolicy(std_log_entry, group_log_begin) diff --git a/test/functional/test-framework/log/template/iterations/iteration.html b/test/functional/test-framework/log/template/iterations/iteration.html deleted file mode 100644 index 15a315e..0000000 --- a/test/functional/test-framework/log/template/iterations/iteration.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - [title] - - - - - -
- View: - - Errors: - - - -
-
-

[title]

-
Iteration status: [status]
-
Execution time: [time] [s]
-
-

Groups:

-
- - diff --git a/test/functional/test-framework/log/template/iterations/setup.html b/test/functional/test-framework/log/template/iterations/setup.html deleted file mode 100644 index c59dd42..0000000 --- a/test/functional/test-framework/log/template/iterations/setup.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Setup - - - - - -
- View: - - Errors: - - - -
-
- -

Test summary

-
-
Iteration status: [STATUS]
-
Execution time: [time] [s]
-
-

Groups:

-
- - diff --git a/test/functional/test-framework/log/template/main.css b/test/functional/test-framework/log/template/main.css deleted file mode 100644 index 4f5449d..0000000 --- a/test/functional/test-framework/log/template/main.css +++ /dev/null @@ -1,383 +0,0 @@ -/* - Copyright(c) 2019-2021 Intel Corporation - SPDX-License-Identifier: BSD-3-Clause -*/ - -html, body { - margin: 0; - padding: 0; - background-color: #F0F0F0; - font-family: Calibri; - color: black; -} - -div { display: block; } - -h2 { margin: 0; padding: 0; } -h4 { margin: 0; padding: 0; } - -div.meta-container { - margin-left: 502px; - min-width: 500px; - height: 100vh; -} - -div.main-layaut { - float: right; - width: 100%; - background-color: #FDFDFD; - height: 100vh; - overflow-y: scroll; - overflow-x: hidden; -} - -div.sidebar { - float: left; - width: 500px; - height: 100vh; - margin-left: -502px; - border: 4px; - background-color: #F0F0F0; - overflow-x: hidden; - overflow-y: auto; - text-align: center; - color: white; - overflow-x: hidden; - overflow-y: hidden; -} - -div.sidebar-hide { - padding: 3px; - height: 20px; - margin: 5px auto; - font-family: Consolas; - font-weight: normal; - font-size: 15px; - color: white; - text-shadow: 1px 1px 3px black; - background-color: rgb(40,80,180); - cursor: default; - border: 2px solid silver; - border-radius: 25px; -} - -div.sidebar-show { color: balck; height: 50%; } - -div.sidebar-test { overflow-x: hidden; overflow-y: hidden;} - -div.sidebar-test-title { - padding: 10px; - height: 40px; - margin: 5px auto; - background-color: rgb(40,80,180); - font-size: 100%; - border: 2px solid silver; - border-radius: 25px; -} - -div.sidebar-test-status { - padding: 3px; - height: 20px; - background-color: green; - border: 2px solid silver; - border-radius: 25px; -} - -div.sidebar-tested-build { - color: black; - border-radius: 25px; - width: 80%; - margin: 5px auto; - padding: 25px; - background-color: #F7F7F7; - border: 1px solid silver; - word-wrap: break-word; - word-break: break-all; - overflow: hidden; - text-align: left; -} - -div.sidebar-test-iteration { - padding: 3px; - height: 20px; - margin: 5px auto; - font-family: Consolas; - font-weight: normal; - font-size: 15px; - color: white; - text-shadow: 1px 1px 3px black; - background-color: rgb(40,80,180); - cursor: default; - border: 2px solid silver; - border-radius: 25px; -} - -.debug { display: none; } - -select.sidebar-iteration-list { - margin: 5px auto; - background-color: white; - color: black; - width: 90%; -} -select.warning { background-color: #ff0; color: black; } -select.workaround { background-color: #fff8dc; color: black; } -select.skip { background-color: silver; color: black; } -select.fail { background-color: red; color: white; } -select.blocked { background-color: #7030a0; color: white; } -select.exception { background-color: #e29517; color: white; } -select.critical { background-color: #002060; color: white; } - -option { - background-color: green; - color: white; - margin: 2px; -} -option.warning { background-color: #ff0; color: black; } -option.workaround { background-color: #fff8dc; color: black; } -option.skip { background-color: silver; color: black; } -option.error { background-color: red; color: white; } -option.blocked { background-color: #7030a0; color: white; } -option.exception { background-color: #e29517; color: white; } -select.critical { background-color: #002060; color: white; } - -a.iteration-selector { - border: 2px solid silver; - border-radius: 40px; - width: 36px; - height: 36px; - margin: 0; - padding: 0; - vertical-align: middle; - display: table-cell; - color: white; - background-color: green; - text-shadow: 0 0 3px black; - font-size: 20px; - font-weight: bold; - line-height: 1em; - text-align: center; - cursor: pointer; -} -a.warning { background-color: #ff0; } -a.workaround { background-color: #fff8dc; } -a.skip { background-color: silver; } -a.fail { background-color: red; } -a.exception { background-color: #e29517; } -a.blocked { background-color: #7030a0; } -a.critical { background-color: #002060; } -a.selected { border: 2px solid black; } - -select.error-list-selector { background-color: silver; } - -div.test-chapter-step { - margin: 4px auto; - border-style: solid; - border-color: #8CB9AE; - border-radius: 10px; - padding-left: 10px; - padding-right: 10px; - cursor: pointer; -} - -div.sidebar-copyright { - position: absolute; - background-color: #DDD; - text-align: center; - padding: 4px; - color: #888; - bottom: 0; - font-size: 12px; - font-family: Consolas; -} - -div.floating { - right: 0; - border: 3px solid silver; - width: 40%; - text-align: center; - vertical-align: top; - position: fixed; - background-color : #F0F0F0; - border-bottom: 1px solid #999; - z-index: 999; - color: #333; - box-shadow: 0 0px 6px gray; -} - -h1 { - display: block; - font-size: 2em; - font-weight: bold; -} - -div.iteration-selector { - margin: 5px auto; -} - -div.iteration-status { - padding: 3px; - height: 20px; - background-color: green; - border: 2px solid silver; - border-radius: 25px; - color: white; - text-align: center; -} - -h1.iteration-title { text-align: center; } - -div.iteration-execution-time { text-align: center; } - -section.iteration-chapters { - border-radius: 25px; - width: 80%; - margin: 10px auto; - padding: 25px; - background-color: #F7F7F7; - border: 1px solid silver; - word-wrap: break-word; - word-break: break-all; - overflow: hidden; -} - -ul.iteration-content { - list-style-type: none; - border-left-color: green; - border-left-style: solid; - margin: 0px; -} -ul.warning { border-left-color: #ff0; } -ul.workaround { border-left-color: #fff8dc; } -ul.skip { border-left-color: silver; } -ul.fail { border-left-color: red; } -ul.blocked { border-left-color: #7030a0; } -ul.critical { border-left-color: #002060; } -ul.exception { border-left-color: #e29517; } - -li.iteration-content { - border-color: rgba(192, 192, 192, 1); - background-color: rgba(238, 238, 238, 1); - display: block; - margin: 2px auto; - border: 1px solid #C0C0C0; - padding: 3px 6px; - font-family: Calibri; - font-size: 16px; - line-height: 1.15em; - word-wrap: break-word; - word-break: break-all; - overflow: hidden; - border-left-color: green; - border-left-style: solid; - word-break: break-all; -} - -div.test-group-step { - color: black; - background-color: #8CB9AE; - border: 1px solid #5C8880; - font-size: 18px; - letter-spacing: 2px; - cursor: pointer; - margin: 4px; - border-radius: 10px; - padding-left: 10px; - padding-right: 10px; - overflow-wrap: break-word; - word-wrap: break-word; - word-break: break-all; -} - -div.warning { background-color: #ff0; color: black; } -div.workaround { background-color: #fff8dc; color: black; } -div.skip { background-color: silver; color: black; } -div.fail { background-color: red; color: white; } -div.blocked { background-color: #7030a0; color: white; } -div.critical { background-color: #002060; color: white; } -div.exception { background-color: #e29517; color: white; } - -a.top-marker { cursor: pointer; float: right; } - -a.top-time-marker { - word-wrap: break-word; - float: right; -} - -li.test-step { - color: black; - border-color: rgba(192, 192, 192, 1); - background-color: rgba(238, 238, 238, 1); - display: block; - margin: 4px auto; - border: 1px solid #C0C0C0; - padding: 3px 6px; - font-family: Calibri; - font-size: 16px; - line-height: 1.15em; - word-wrap: break-word; - word-break: break-all; - overflow: hidden; - border-left-color: green; - border-left-style: solid; - border-radius: 10px; - padding-left: 10px; - padding-right: 10px -} -li.warning { background-color: #ff0; border-left-color: #ff0; } -li.workaround { background-color: #fff8dc; border-left-color: #fff8dc; } -li.skip { background-color: silver; border-left-color: silver; } -li.fail { - background-color: red; - border-left-color: red; - color: white; -} -li.blocked { - background-color: #7030a0; - border-left-color: #7030a0; - color: white; -} -li.exception { - background-color: #e29517; - border-left-color: #e29517; - color: white; -} - -li.critical { - background-color: #002060; - border-left-color: #002060; - color: white; -} - -div.ts-iteration { - float: left; - margin: 2px auto; - border: 1px solid silver; - padding: 3px 3px; - text-align: center; -} - -div.ts-total-time { - margin: 2px auto; - border: 1px solid silver; - padding: 3px 3px; - text-align: right; -} - -div.ts-time { - float: left; - font-size: 12px; - margin: 2px auto; - border: 1px solid #A7A7A7; - padding: 3px 3px; -} - -div.ts-msg { - font-size: 16px; - font-family: Courier; - margin: 2px auto; - border: 1px solid #A7A7A7; - padding: 3px 3px; - white-space: pre-wrap; - word-break: break-all; -} diff --git a/test/functional/test-framework/log/template/main.html b/test/functional/test-framework/log/template/main.html deleted file mode 100644 index 8d0da20..0000000 --- a/test/functional/test-framework/log/template/main.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - [test title] - - - - -
- -
- -
-
- - - diff --git a/test/functional/test-framework/log/template/main.js b/test/functional/test-framework/log/template/main.js deleted file mode 100644 index 79e81d8..0000000 --- a/test/functional/test-framework/log/template/main.js +++ /dev/null @@ -1,223 +0,0 @@ -/* - Copyright(c) 2019-2021 Intel Corporation - SPDX-License-Identifier: BSD-3-Clause -*/ - -function onLoadDocument() { - hideDebug(); -} - -function selectMode() { - var selector = document.getElementById('mode-selector'); - if (selector.value.includes('info')) { - hideDebug(); - } else { - showDebug(); - } -} - -function hideDebug() { - var debugTestStepArray = document.getElementsByTagName('li'); - for (i = 0; i < debugTestStepArray.length; i ++) { - if(debugTestStepArray[i].className.includes('debug')) { - debugTestStepArray[i].style.display = 'none'; - } - } -} - -function showDebug() { - var debugTestStepArray = document.getElementsByTagName('li'); - for (i = 0; i < debugTestStepArray.length; i ++) { - if(debugTestStepArray[i].className.includes('debug')) { - debugTestStepArray[i].style.display = ''; - } - } -} - -function sidebarCtrl(ctrlHideId, ctrlShowClass) { - var metaContainer = document.getElementsByClassName("meta-container")[0]; - var sidebar = document.getElementsByClassName('sidebar')[0]; - var sidebarTest = document.getElementById('sidebar-test'); - var ctrlHide = document.getElementById(ctrlHideId); - var ctrlShowSet = document.getElementsByClassName(ctrlShowClass); - - if(sidebar.style.width.includes('15px')) { - showSidebar(metaContainer, sidebar, ctrlHide, ctrlShowSet, sidebarTest); - } else { - hideSidebar(metaContainer, sidebar, ctrlHide, ctrlShowSet, sidebarTest); - } -} - -function showSidebar(mContainer, sidebar, ctrlHide, ctrlShowSet, sidebarTest) { - sidebar.style.cursor = 'default'; - mContainer.style.marginLeft = ''; - sidebarTest.style.width = ''; - sidebarTest.style.height = ''; - sidebar.style.height = ''; - sidebar.style.marginLeft = ''; - sidebar.style.width = ''; - var i; - for (i = 0; i < sidebarTest.children.length; i++) { - sidebarTest.children[i].style.display = ''; - } - document.getElementById('iteration-selector').style.display = ''; - document.getElementById('sidebar-iteration-list').style.display = ''; - document.getElementById('sidebar-copyright').style.display = ''; - for(i = 0; i < ctrlShowSet.length; i ++) { - ctrlShowSet[i].style.display = 'none'; - } -} - -function hideSidebar(mContainer, sidebar, ctrlHide, ctrlShowSet, sidebarTest) { - document.getElementById('iteration-selector').style.display = 'none'; - document.getElementById('sidebar-iteration-list').style.display = 'none'; - document.getElementById('sidebar-copyright').style.display = 'none'; - var i; - for (i = 0; i < sidebarTest.children.length; i++) { - sidebarTest.children[i].style.display = 'none'; - } - sidebarTest.style.display = ''; - for(i = 0; i < ctrlShowSet.length; i ++) { - ctrlShowSet[i].style.display = ''; - ctrlShowSet[i].style.color = 'black'; - } - sidebar.style.width = '15px'; - sidebar.style.marginLeft = '-15px'; - sidebar.style.height = '100%'; - sidebarTest.style.height = '100%'; - sidebarTest.style.width = '100%'; - mContainer.style.marginLeft = '16px'; - sidebar.style.cursor = 'pointer'; -} - -function previousError() { - var errorSelector = document.getElementById("error-list-selector"); - if (errorSelector.length > 1) { - var id = errorSelector.selectedIndex; - if (id - 1 > 0) { - errorSelector.selectedIndex = (id - 1); - } else { - errorSelector.selectedIndex = (errorSelector.length - 1); - } - errorSelected('error-list-selector'); - } -} - -function nextError() { - var errorSelector = document.getElementById("error-list-selector"); - if (errorSelector.length > 1) { - var id = errorSelector.selectedIndex; - if (id + 1 < errorSelector.length) { - errorSelector.selectedIndex = (id + 1); - } else { - errorSelector.selectedIndex = 1; - } - errorSelected('error-list-selector'); - } -} - -function selectIterationFromSelect() { - var element = document.getElementById("sidebar-iteration-list"); - loadDocument(element.value); - updateIterationSelector(element); -} - -function clickSelectIteration() { - var element = document.getElementById("sidebar-iteration-list"); - for (i = 0; i < element.length; i ++) { - option = element[i]; - var cls = option.getAttribute('class'); - switch(cls) { - case "warning": - option.style.backgroundColor = "yellow"; - option.style.color = "black"; - break; - case "skip": - option.style.backgroundColor = "silver"; - option.style.color = "black"; - break; - case "fail": - option.style.backgroundColor = "red"; - option.style.color = "white"; - break; - case "exception": - option.style.backgroundColor = "blueviolet"; - option.style.color = "white"; - break; - default: - option.style.backgroundColor = "white"; - option.style.color = "black"; - break; - } - - }; -} - -function selectIteration(iteration) { - var selectElement = document.getElementById("sidebar-iteration-list"); - var docId = loadDocument(iteration); - selectElement.selectedIndex = docId; - updateIterationSelector(selectElement); -} - -function loadDocument(fileId) { - var result = 0; - if(fileId == 'M') { - document.getElementById("main-view").src = "iterations/setup.html"; - } else { - var id = pad(fileId, 3); - document.getElementById("main-view").src = "iterations/iteration_" + id + ".html"; - result = parseInt(fileId); - } - return result; -} - -function updateIterationSelector(element) { - var index = element.selectedIndex - var option_class = element[index].getAttribute('class') - if (option_class != null) { - element.setAttribute('class', "sidebar-iteration-list " + option_class); - } else { - element.setAttribute('class', "sidebar-iteration-list"); - } -} - -function errorSelected(selectorId) { - var newLocation = document.getElementById(selectorId).value; - window.location.hash = newLocation; -} - -function pad(strNumber, padding) { - while((strNumber.length + 1) <= padding) { - strNumber = "0" + strNumber; - } - return strNumber; -} - -function showHide(id) { - var ulElement = document.getElementById(id); - if(ulElement.style.display == 'none') { - ulElement.style.display = ''; - } else { - ulElement.style.display = 'none'; - } -} - -function chapterClick(id) { - var id_array = id.split('.'); - var node_id = ""; - var i = 0; - var destinationElement = document.getElementById(id); - if (destinationElement.style.display == 'none') { - do { - node_id += id_array[i]; - var ele = document.getElementById(node_id); - ele.style.display = ''; - node_id += '.'; - i += 1; - } while (i < id_array.length); - window.location = '#' + id; - } else { - destinationElement.style.display = 'none'; - } -} diff --git a/test/functional/test-framework/storage_devices/device.py b/test/functional/test-framework/storage_devices/device.py deleted file mode 100644 index 566f403..0000000 --- a/test/functional/test-framework/storage_devices/device.py +++ /dev/null @@ -1,117 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -import posixpath - -from core.test_run import TestRun -from test_tools import disk_utils, fs_utils -from test_tools.disk_utils import get_device_filesystem_type, get_sysfs_path -from test_utils.io_stats import IoStats -from test_utils.size import Size, Unit - - -class Device: - def __init__(self, path): - disk_utils.validate_dev_path(path) - self.path = path - self.size = Size(disk_utils.get_size(self.get_device_id()), Unit.Byte) - self.filesystem = get_device_filesystem_type(self.get_device_id()) - self.mount_point = None - - def create_filesystem(self, fs_type: disk_utils.Filesystem, force=True, blocksize=None): - disk_utils.create_filesystem(self, fs_type, force, blocksize) - self.filesystem = fs_type - - def wipe_filesystem(self, force=True): - disk_utils.wipe_filesystem(self, force) - self.filesystem = None - - def is_mounted(self): - output = TestRun.executor.run(f"findmnt {self.path}") - if output.exit_code != 0: - return False - else: - mount_point_line = output.stdout.split('\n')[1] - device_path = fs_utils.readlink(self.path) - self.mount_point = mount_point_line[0:mount_point_line.find(device_path)].strip() - return True - - def mount(self, mount_point, options: [str] = None): - if not self.is_mounted(): - if disk_utils.mount(self, mount_point, options): - self.mount_point = mount_point - else: - raise Exception(f"Device is already mounted! Actual mount point: {self.mount_point}") - - def unmount(self): - if not self.is_mounted(): - TestRun.LOGGER.info("Device is not mounted.") - elif disk_utils.unmount(self): - self.mount_point = None - - def get_device_link(self, directory: str): - items = self.get_all_device_links(directory) - return next(i for i in items if i.full_path.startswith(directory)) - - def get_device_id(self): - return fs_utils.readlink(self.path).split('/')[-1] - - def get_all_device_links(self, directory: str): - from test_tools import fs_utils - output = fs_utils.ls(f"$(find -L {directory} -samefile {self.path})") - return fs_utils.parse_ls_output(output, self.path) - - def get_io_stats(self): - return IoStats.get_io_stats(self.get_device_id()) - - def get_sysfs_property(self, property_name): - path = posixpath.join(disk_utils.get_sysfs_path(self.get_device_id()), - "queue", property_name) - return TestRun.executor.run_expect_success(f"cat {path}").stdout - - def set_sysfs_property(self, property_name, value): - TestRun.LOGGER.info( - f"Setting {property_name} for device {self.get_device_id()} to {value}.") - path = posixpath.join(disk_utils.get_sysfs_path(self.get_device_id()), "queue", - property_name) - fs_utils.write_file(path, str(value)) - - def set_max_io_size(self, new_max_io_size: Size): - self.set_sysfs_property("max_sectors_kb", - int(new_max_io_size.get_value(Unit.KibiByte))) - - def get_max_io_size(self): - return Size(int(self.get_sysfs_property("max_sectors_kb")), Unit.KibiByte) - - def get_max_hw_io_size(self): - return Size(int(self.get_sysfs_property("max_hw_sectors_kb")), Unit.KibiByte) - - def get_discard_granularity(self): - return self.get_sysfs_property("discard_granularity") - - def get_discard_max_bytes(self): - return self.get_sysfs_property("discard_max_bytes") - - def get_discard_zeroes_data(self): - return self.get_sysfs_property("discard_zeroes_data") - - def get_numa_node(self): - return int(TestRun.executor.run_expect_success( - f"cat {get_sysfs_path(self.get_device_id())}/device/numa_node").stdout) - - def __str__(self): - return ( - f'system path: {self.path}, short link: /dev/{self.get_device_id()},' - f' filesystem: {self.filesystem}, mount point: {self.mount_point}, size: {self.size}' - ) - - def __repr__(self): - return str(self) - - @staticmethod - def get_scsi_debug_devices(): - scsi_debug_devices = TestRun.executor.run_expect_success( - "lsscsi --scsi_id | grep scsi_debug").stdout - return [Device(f'/dev/disk/by-id/scsi-{device.split()[-1]}') - for device in scsi_debug_devices.splitlines()] diff --git a/test/functional/test-framework/storage_devices/disk.py b/test/functional/test-framework/storage_devices/disk.py deleted file mode 100644 index 83b8c9d..0000000 --- a/test/functional/test-framework/storage_devices/disk.py +++ /dev/null @@ -1,237 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -import itertools -import json -import re -from datetime import timedelta -from enum import IntEnum - -from core.test_run import TestRun -from storage_devices.device import Device -from test_tools import disk_utils, fs_utils, nvme_cli -from test_utils import disk_finder -from test_utils.os_utils import wait -from test_utils.size import Unit -from test_tools.disk_utils import get_pci_address - - -class DiskType(IntEnum): - hdd = 0 - hdd4k = 1 - sata = 2 - nand = 3 - optane = 4 - - -class DiskTypeSetBase: - def resolved(self): - raise NotImplementedError() - - def types(self): - raise NotImplementedError() - - def json(self): - return json.dumps({ - "type": "set", - "values": [t.name for t in self.types()] - }) - - def __lt__(self, other): - return min(self.types()) < min(other.types()) - - def __le__(self, other): - return min(self.types()) <= min(other.types()) - - def __eq__(self, other): - return min(self.types()) == min(other.types()) - - def __ne__(self, other): - return min(self.types()) != min(other.types()) - - def __gt__(self, other): - return min(self.types()) > min(other.types()) - - def __ge__(self, other): - return min(self.types()) >= min(other.types()) - - -class DiskTypeSet(DiskTypeSetBase): - def __init__(self, *args): - self.__types = set(*args) - - def resolved(self): - return True - - def types(self): - return self.__types - - -class DiskTypeLowerThan(DiskTypeSetBase): - def __init__(self, disk_name): - self.__disk_name = disk_name - - def resolved(self): - return self.__disk_name in TestRun.disks - - def types(self): - if not self.resolved(): - raise LookupError("Disk type not resolved!") - disk_type = TestRun.disks[self.__disk_name].disk_type - return set(filter(lambda d: d < disk_type, [*DiskType])) - - def json(self): - return json.dumps({ - "type": "operator", - "name": "lt", - "args": [self.__disk_name] - }) - - -class Disk(Device): - def __init__( - self, - path, - disk_type: DiskType, - serial_number, - block_size, - ): - Device.__init__(self, path) - self.serial_number = serial_number - self.block_size = Unit(block_size) - self.disk_type = disk_type - self.partitions = [] - - def create_partitions( - self, - sizes: [], - partition_table_type=disk_utils.PartitionTable.gpt): - disk_utils.create_partitions(self, sizes, partition_table_type) - - def remove_partition(self, part): - part_number = int(part.path.split("part")[1]) - disk_utils.remove_parition(self, part_number) - self.partitions.remove(part) - - def umount_all_partitions(self): - TestRun.LOGGER.info( - f"Umounting all partitions from: {self.path}") - cmd = f'umount -l {fs_utils.readlink(self.path)}*?' - TestRun.executor.run(cmd) - - def remove_partitions(self): - for part in self.partitions: - if part.is_mounted(): - part.unmount() - if disk_utils.remove_partitions(self): - self.partitions.clear() - - def is_detected(self): - if self.serial_number: - serial_numbers = disk_finder.get_all_serial_numbers() - return self.serial_number in serial_numbers - elif self.path: - output = fs_utils.ls_item(f"{self.path}") - return fs_utils.parse_ls_output(output)[0] is not None - raise Exception("Couldn't check if device is detected by the system") - - def wait_for_plug_status(self, should_be_visible): - if not wait(lambda: should_be_visible == self.is_detected(), - timedelta(minutes=1), - timedelta(seconds=1)): - raise Exception(f"Timeout occurred while trying to " - f"{'plug' if should_be_visible else 'unplug'} disk.") - - def plug(self): - if self.is_detected(): - return - TestRun.executor.run_expect_success(self.plug_command) - self.wait_for_plug_status(True) - - def unplug(self): - if not self.is_detected(): - return - TestRun.executor.run_expect_success(self.unplug_command) - self.wait_for_plug_status(False) - - @staticmethod - def plug_all_disks(): - TestRun.executor.run_expect_success(NvmeDisk.plug_all_command) - TestRun.executor.run_expect_success(SataDisk.plug_all_command) - - def __str__(self): - disk_str = f'system path: {self.path}, type: {self.disk_type.name}, ' \ - f'serial: {self.serial_number}, size: {self.size}, ' \ - f'block size: {self.block_size}, partitions:\n' - for part in self.partitions: - disk_str += f'\t{part}' - return disk_str - - @staticmethod - def create_disk(path, - disk_type: DiskType, - serial_number, - block_size): - if disk_type is DiskType.nand or disk_type is DiskType.optane: - return NvmeDisk(path, disk_type, serial_number, block_size) - else: - return SataDisk(path, disk_type, serial_number, block_size) - - -class NvmeDisk(Disk): - plug_all_command = "echo 1 > /sys/bus/pci/rescan" - - def __init__(self, path, disk_type, serial_number, block_size): - Disk.__init__(self, path, disk_type, serial_number, block_size) - self.plug_command = NvmeDisk.plug_all_command - self.unplug_command = f"echo 1 > /sys/block/{self.get_device_id()}/device/remove || " \ - f"echo 1 > /sys/block/{self.get_device_id()}/device/device/remove" - self.pci_address = get_pci_address(self.get_device_id()) - - def __str__(self): - disk_str = super().__str__() - disk_str = f"pci address: {self.pci_address}, " + disk_str - return disk_str - - def format_disk(self, metadata_size=None, block_size=None, - force=True, format_params=None, reset=True): - nvme_cli.format_disk(self, metadata_size, block_size, force, format_params, reset) - - def get_lba_formats(self): - return nvme_cli.get_lba_formats(self) - - def get_lba_format_in_use(self): - return nvme_cli.get_lba_format_in_use(self) - - -class SataDisk(Disk): - plug_all_command = "for i in $(find -H /sys/devices/ -path '*/scsi_host/*/scan' -type f); " \ - "do echo '- - -' > $i; done;" - - def __init__(self, path, disk_type, serial_number, block_size): - Disk.__init__(self, path, disk_type, serial_number, block_size) - self.plug_command = SataDisk.plug_all_command - self.unplug_command = \ - f"echo 1 > {self.get_sysfs_properties(self.get_device_id()).full_path}/device/delete" - - def get_sysfs_properties(self, device_id): - ls_command = f"$(find -H /sys/devices/ -name {device_id} -type d)" - output = fs_utils.ls_item(f"{ls_command}") - sysfs_addr = fs_utils.parse_ls_output(output)[0] - if not sysfs_addr: - raise Exception(f"Failed to find sysfs address: ls -l {ls_command}") - dirs = sysfs_addr.full_path.split('/') - scsi_address = dirs[-3] - matches = re.search( - r"^(?P\d+)[-:](?P\d+)[-:](?P\d+)[-:](?P\d+)$", - scsi_address) - controller_id = matches["controller"] - port_id = matches["port"] - target_id = matches["target"] - lun = matches["lun"] - - host_path = "/".join(itertools.takewhile(lambda x: not x.startswith("host"), dirs)) - self.plug_command = f"echo '{port_id} {target_id} {lun}' > " \ - f"{host_path}/host{controller_id}/scsi_host/host{controller_id}/scan" - return sysfs_addr diff --git a/test/functional/test-framework/storage_devices/drbd.py b/test/functional/test-framework/storage_devices/drbd.py deleted file mode 100644 index 66baeda..0000000 --- a/test/functional/test-framework/storage_devices/drbd.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# Copyright(c) 2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause-Clear -# - -import os -import posixpath - -from core.test_run import TestRun -from storage_devices.device import Device -from test_tools.drbdadm import Drbdadm -from test_utils.filesystem.symlink import Symlink -from test_utils.output import CmdException - - -class Drbd(Device): - def __init__(self, config): - if Drbdadm.dump_config(config.name).exit_code != 0: - raise ValueError(f"Resource {config.name} not found") - self.config = config - - def create_metadata(self, force): - return Drbdadm.create_metadata(self.config.name, force) - - def up(self): - output = Drbdadm.up(self.config.name) - if output.exit_code != 0: - raise CmdException(f"Failed to create {self.config.name} drbd instance") - - self.path = posixpath.join("/dev/disk/by-id/", posixpath.basename(self.config.device)) - self.symlink = Symlink.get_symlink(self.path, self.config.device, True) - self.device = Device(self.path) - - return self.device - - def wait_for_sync(self): - return Drbdadm.wait_for_sync(self.config.name) - - def is_in_sync(self): - return Drbdadm.in_sync(self.config.name) - - def get_status(self): - return Drbdadm.get_status(self.config.name) - - def set_primary(self, force=False): - return Drbdadm.set_node_primary(self.config.name, force) - - def down(self): - output = Drbdadm.down(self.config.name) - if output.exit_code != 0: - raise CmdException(f"Failed to stop {self.config.name} drbd instance") - - self.device = None - self.symlink.remove(True, True) - - @staticmethod - def down_all(): - try: - Drbdadm.down_all() - except CmdException as e: - if "no resources defined" not in str(e): - raise e - - @staticmethod - def is_installed(): - return TestRun.executor.run("which drbdadm && modinfo drbd").exit_code == 0 diff --git a/test/functional/test-framework/storage_devices/lvm.py b/test/functional/test-framework/storage_devices/lvm.py deleted file mode 100644 index b1cfa88..0000000 --- a/test/functional/test-framework/storage_devices/lvm.py +++ /dev/null @@ -1,531 +0,0 @@ -# -# Copyright(c) 2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -import threading - -from typing import Union - -from api.cas.core import Core -from core.test_run import TestRun -from storage_devices.device import Device -from storage_devices.disk import Disk, NvmeDisk -from storage_devices.partition import Partition -from test_tools.fs_utils import readlink -from test_utils.disk_finder import resolve_to_by_id_link -from test_utils.filesystem.symlink import Symlink -from test_utils.size import Size - -lvm_config_path = "/etc/lvm/lvm.conf" -filter_prototype_regex = r"^\sfilter\s=\s\[" -types_prototype_regex = r"^\stypes\s=\s\[" -global_filter_prototype_regex = r"^\sglobal_filter\s=\s\[" -tab = "\\\\t" - - -class LvmConfiguration: - def __init__( - self, - lvm_filters: [] = None, - pv_num: int = None, - vg_num: int = None, - lv_num: int = None, - cache_num: int = None, - cas_dev_num: int = None - ): - self.lvm_filters = lvm_filters - self.pv_num = pv_num - self.vg_num = vg_num - self.lv_num = lv_num - self.cache_num = cache_num - self.cas_dev_num = cas_dev_num - - @staticmethod - def __read_definition_from_lvm_config( - prototype_regex: str - ): - cmd = f"grep '{prototype_regex}' {lvm_config_path}" - output = TestRun.executor.run(cmd).stdout - - return output - - @classmethod - def __add_block_dev_to_lvm_config( - cls, - block_device_type: str, - number_of_partitions: int = 16 - ): - types_definition = cls.read_types_definition_from_lvm_config() - - if types_definition: - if block_device_type in types_definition: - TestRun.LOGGER.info(f"Device type '{block_device_type}' already present in config") - return - - TestRun.LOGGER.info(f"Add block device type to existing list") - new_type_prefix = f"types = [\"{block_device_type}\", {number_of_partitions}, " - - config_update_cmd = f"sed -i 's/{types_prototype_regex}/\t{new_type_prefix}/g'" \ - f" {lvm_config_path}" - else: - TestRun.LOGGER.info(f"Create new types variable") - new_types = f"types = [\"{block_device_type}\", {number_of_partitions}]" - characteristic_line = f"# Configuration option devices\\/sysfs_scan." - config_update_cmd = f"sed -i /'{characteristic_line}'/i\\ '{tab}{new_types}' " \ - f"{lvm_config_path}" - - TestRun.LOGGER.info(f"Adding {block_device_type} ({number_of_partitions} partitions) " - f"to supported types in {lvm_config_path}") - TestRun.executor.run(config_update_cmd) - - @classmethod - def __add_filter_to_lvm_config( - cls, - filter: str - ): - if filter is None: - TestRun.LOGGER.error(f"Lvm filter for lvm config not provided.") - - filters_definition = cls.read_filter_definition_from_lvm_config() - - if filters_definition: - if filter in filters_definition: - TestRun.LOGGER.info(f"Filter definition '{filter}' already present in config") - return - - new_filter_formatted = filter.replace("/", "\\/") - new_filter_prefix = f"filter = [ \"{new_filter_formatted}\", " - - TestRun.LOGGER.info(f"Adding filter to existing list") - config_update_cmd = f"sed -i 's/{filter_prototype_regex}/\t{new_filter_prefix}/g'" \ - f" {lvm_config_path}" - else: - TestRun.LOGGER.info(f"Create new filter variable") - new_filter = f"filter = [\"{filter}\"]" - characteristic_line = f"# Configuration option devices\\/global_filter." - config_update_cmd = f"sed -i /'{characteristic_line}'/i\\ '{tab}{new_filter}' " \ - f"{lvm_config_path}" - - TestRun.LOGGER.info(f"Adding filter '{filter}' to {lvm_config_path}") - TestRun.executor.run(config_update_cmd) - - @classmethod - def read_types_definition_from_lvm_config(cls): - return cls.__read_definition_from_lvm_config(types_prototype_regex) - - @classmethod - def read_filter_definition_from_lvm_config(cls): - return cls.__read_definition_from_lvm_config(filter_prototype_regex) - - @classmethod - def read_global_filter_definition_from_lvm_config(cls): - return cls.__read_definition_from_lvm_config(global_filter_prototype_regex) - - @classmethod - def add_block_devices_to_lvm_config( - cls, - device_type: str - ): - if device_type is None: - TestRun.LOGGER.error(f"No device provided.") - - cls.__add_block_dev_to_lvm_config(device_type) - - @classmethod - def add_filters_to_lvm_config( - cls, - filters: [] - ): - if filters is None: - TestRun.LOGGER.error(f"Lvm filters for lvm config not provided.") - - for f in filters: - cls.__add_filter_to_lvm_config(f) - - @classmethod - def configure_dev_types_in_config( - cls, - devices: ([Device], Device) - ): - if isinstance(devices, list): - devs = [] - for device in devices: - dev = device.parent_device if isinstance(device, Partition) else device - devs.append(dev) - - if any(isinstance(dev, Core) for dev in devs): - cls.add_block_devices_to_lvm_config("cas") - if any(isinstance(dev, NvmeDisk) for dev in devs): - cls.add_block_devices_to_lvm_config("nvme") - else: - dev = devices.parent_device if isinstance(devices, Partition) else devices - if isinstance(dev, Core): - cls.add_block_devices_to_lvm_config("cas") - if isinstance(dev, NvmeDisk): - cls.add_block_devices_to_lvm_config("nvme") - - @classmethod - def configure_filters( - cls, - lvm_filters: [], - devices: ([Device], Device) - ): - if lvm_filters: - TestRun.LOGGER.info(f"Preparing configuration for LVMs - filters.") - LvmConfiguration.add_filters_to_lvm_config(lvm_filters) - - cls.configure_dev_types_in_config(devices) - - @staticmethod - def remove_global_filter_from_config(): - cmd = f"sed -i '/{global_filter_prototype_regex}/d' {lvm_config_path}" - TestRun.executor.run(cmd) - - @staticmethod - def remove_filters_from_config(): - cmd = f"sed -i '/{filter_prototype_regex}/d' {lvm_config_path}" - TestRun.executor.run(cmd) - - -class VolumeGroup: - __unique_vg_id = 0 - __lock = threading.Lock() - - def __init__(self, name: str = None): - self.name = name - - def __eq__(self, other): - try: - return self.name == other.name - except AttributeError: - return False - - @classmethod - def __get_vg_name(cls, prefix: str = "vg"): - with cls.__lock: - cls.__unique_vg_id += 1 - return f"{prefix}{cls.__unique_vg_id}" - - @staticmethod - def get_all_volume_groups(): - output_lines = TestRun.executor.run(f"pvscan").stdout.splitlines() - - volume_groups = {} - for line in output_lines: - if "PV" not in line: - continue - - line_elements = line.split() - pv = line_elements[line_elements.index("PV") + 1] - vg = "" - if "VG" in line: - vg = line_elements[line_elements.index("VG") + 1] - - if vg not in volume_groups: - volume_groups[vg] = [] - volume_groups[vg].append(pv) - - return volume_groups - - @staticmethod - def create_vg(vg_name: str, device_paths: str): - if not vg_name: - raise ValueError("Name needed for VG creation.") - if not device_paths: - raise ValueError("Device paths needed for VG creation.") - - cmd = f"vgcreate --yes {vg_name} {device_paths} " - TestRun.executor.run_expect_success(cmd) - - @classmethod - def is_vg_already_present(cls, dev_number: int, device_paths: str = None): - if not device_paths: - TestRun.LOGGER.exception("No devices provided.") - - volume_groups = cls.get_all_volume_groups() - - for vg in volume_groups: - for pv in volume_groups[vg]: - if len(volume_groups[vg]) == dev_number and pv in device_paths: - return cls(vg) - - for vg in volume_groups: - for pv in volume_groups[vg]: - if pv in device_paths: - TestRun.LOGGER.warning(f"Some devices are used in other LVM volume group") - return False - - @classmethod - def create(cls, device_paths: str = None): - vg_name = cls.__get_vg_name() - - VolumeGroup.create_vg(vg_name, device_paths) - - volume_groups = VolumeGroup.get_all_volume_groups() - - if vg_name in volume_groups: - return cls(vg_name) - else: - TestRun.LOGGER.error("Had not found newly created VG.") - - @staticmethod - def remove(vg_name: str): - if not vg_name: - raise ValueError("Name needed for VG remove operation.") - - cmd = f"vgremove {vg_name}" - return TestRun.executor.run(cmd) - - @staticmethod - def get_logical_volumes_path(vg_name: str): - cmd = f"lvdisplay | grep /dev/{vg_name}/ | awk '{{print $3}}'" - paths = TestRun.executor.run(cmd).stdout.splitlines() - - return paths - - -class Lvm(Disk): - __unique_lv_id = 0 - __lock = threading.Lock() - - def __init__( - self, - path_dm: str, # device mapper path - volume_group: VolumeGroup, - volume_name: str = None - ): - Device.__init__(self, resolve_to_by_id_link(path_dm)) - self.device_name = path_dm.split('/')[-1] - self.volume_group = volume_group - self.volume_name = volume_name - - def __eq__(self, other): - try: - return self.device_name == other.device_name and \ - self.volume_group == other.volume_group and \ - self.volume_name == other.volume_name - except AttributeError: - return False - - @classmethod - def __get_unique_lv_name(cls, prefix: str = "lv"): - with cls.__lock: - cls.__unique_lv_id += 1 - return f"{prefix}{cls.__unique_lv_id}" - - @classmethod - def __create( - cls, - name: str, - volume_size_cmd: str, - volume_group: VolumeGroup - ): - TestRun.LOGGER.info(f"Creating LV '{name}'.") - cmd = f"lvcreate {volume_size_cmd} --name {name} {volume_group.name} --yes" - TestRun.executor.run_expect_success(cmd) - - volumes = cls.discover_logical_volumes() - for volume in volumes: - if name == volume.volume_name: - return volume - - @classmethod - def configure_global_filter( - cls, - dev_first: Device, - lv_amount: int, - pv_devs: ([Device], Device) - ): - device_first = dev_first.parent_device if isinstance(dev_first, Partition) else dev_first - if lv_amount > 1 and isinstance(device_first, Core): - - global_filter_def = LvmConfiguration.read_global_filter_definition_from_lvm_config() - if not isinstance(pv_devs, list): - pv_devs = [pv_devs] - - if global_filter_def: - TestRun.LOGGER.info(f"Configure 'global filter' variable") - links = [] - for pv_dev in pv_devs: - link = pv_dev.get_device_link("/dev/disk/by-id") - links.append(str(link)) - - for link in links: - if link in global_filter_def: - TestRun.LOGGER.info(f"Global filter definition already contains '{link}'") - continue - - new_link_formatted = link.replace("/", "\\/") - new_global_filter_prefix = f"global_filter = [ \"r|{new_link_formatted}|\", " - - TestRun.LOGGER.info(f"Adding global filter '{link}' to existing list") - config_update_cmd = f"sed -i 's/{global_filter_prototype_regex}/\t" \ - f"{new_global_filter_prefix}/g' {lvm_config_path}" - TestRun.executor.run(config_update_cmd) - else: - for pv_dev in pv_devs: - link = pv_dev.get_device_link("/dev/disk/by-id") - global_filter = f"\"r|{link}|\"" - global_filter += ", " - global_filter = global_filter[:-2] - - TestRun.LOGGER.info(f"Create new 'global filter' variable") - - new_global = f"global_filter = [{global_filter}]" - characteristic_line = f"# Configuration option devices\\/types." - config_update_cmd = f"sed -i /'{characteristic_line}'/i\\ " \ - f"'{tab}{new_global}' {lvm_config_path}" - - TestRun.LOGGER.info(f"Adding global filter '{global_filter}' to {lvm_config_path}") - TestRun.executor.run(config_update_cmd) - - TestRun.LOGGER.info(f"Remove 'filter' in order to 'global_filter' to be used") - if LvmConfiguration.read_filter_definition_from_lvm_config(): - LvmConfiguration.remove_filters_from_config() - - @classmethod - def create_specific_lvm_configuration( - cls, - devices: ([Device], Device), - lvm_configuration: LvmConfiguration, - lvm_as_core: bool = False - ): - pv_per_vg = int(lvm_configuration.pv_num / lvm_configuration.vg_num) - lv_per_vg = int(lvm_configuration.lv_num / lvm_configuration.vg_num) - lv_size_percentage = int(100 / lv_per_vg) - - LvmConfiguration.configure_filters(lvm_configuration.lvm_filters, devices) - - logical_volumes = [] - - for vg_iter in range(lvm_configuration.vg_num): - if isinstance(devices, list): - pv_devs = [] - start_range = vg_iter * pv_per_vg - end_range = start_range + pv_per_vg - for i in range(start_range, end_range): - pv_devs.append(devices[i]) - device_first = devices[0] - else: - pv_devs = devices - device_first = devices - - for j in range(lv_per_vg): - lv = cls.create(lv_size_percentage, pv_devs) - logical_volumes.append(lv) - - if lvm_as_core: - cls.configure_global_filter(device_first, lv_per_vg, pv_devs) - - return logical_volumes - - @classmethod - def create( - cls, - volume_size_or_percent: Union[Size, int], - devices: ([Device], Device), - name: str = None - ): - if isinstance(volume_size_or_percent, Size): - size_cmd = f"--size {volume_size_or_percent.get_value()}B" - elif isinstance(volume_size_or_percent, int): - size_cmd = f"--extents {volume_size_or_percent}%VG" - else: - TestRun.LOGGER.error(f"Incorrect type of the first argument (volume_size_or_percent).") - - if not name: - name = cls.__get_unique_lv_name() - - devices_paths = cls.get_devices_path(devices) - dev_number = len(devices) if isinstance(devices, list) else 1 - - vg = VolumeGroup.is_vg_already_present(dev_number, devices_paths) - - if not vg: - vg = VolumeGroup.create(devices_paths) - - return cls.__create(name, size_cmd, vg) - - @staticmethod - def get_devices_path(devices: ([Device], Device)): - if isinstance(devices, list): - return " ".join([Symlink(dev.path).get_target() for dev in devices]) - else: - return Symlink(devices.path).get_target() - - @classmethod - def discover_logical_volumes(cls): - vol_groups = VolumeGroup.get_all_volume_groups() - volumes = [] - for vg in vol_groups: - lv_discovered = VolumeGroup.get_logical_volumes_path(vg) - if lv_discovered: - for lv_path in lv_discovered: - cls.make_sure_lv_is_active(lv_path) - lv_name = lv_path.split('/')[-1] - volumes.append( - cls( - readlink(lv_path), - VolumeGroup(vg), - lv_name - ) - ) - else: - TestRun.LOGGER.info(f"No LVMs present in the system.") - - return volumes - - @classmethod - def discover(cls): - TestRun.LOGGER.info("Discover LVMs in system...") - return cls.discover_logical_volumes() - - @staticmethod - def remove(lv_name: str, vg_name: str): - if not lv_name: - raise ValueError("LV name needed for LV remove operation.") - if not vg_name: - raise ValueError("VG name needed for LV remove operation.") - - cmd = f"lvremove -f {vg_name}/{lv_name}" - return TestRun.executor.run(cmd) - - @staticmethod - def remove_pv(pv_name: str): - if not pv_name: - raise ValueError("Name needed for PV remove operation.") - - cmd = f"pvremove {pv_name}" - return TestRun.executor.run(cmd) - - @classmethod - def remove_all(cls): - cmd = f"lvdisplay | grep 'LV Path' | awk '{{print $3}}'" - lvm_paths = TestRun.executor.run(cmd).stdout.splitlines() - for lvm_path in lvm_paths: - lv_name = lvm_path.split('/')[-1] - vg_name = lvm_path.split('/')[-2] - cls.remove(lv_name, vg_name) - - cmd = f"vgdisplay | grep 'VG Name' | awk '{{print $3}}'" - vg_names = TestRun.executor.run(cmd).stdout.splitlines() - for vg_name in vg_names: - TestRun.executor.run(f"vgchange -an {vg_name}") - VolumeGroup.remove(vg_name) - - cmd = f"pvdisplay | grep 'PV Name' | awk '{{print $3}}'" - pv_names = TestRun.executor.run(cmd).stdout.splitlines() - for pv_name in pv_names: - cls.remove_pv(pv_name) - - TestRun.LOGGER.info(f"Successfully removed all LVMs.") - - @staticmethod - def make_sure_lv_is_active(lv_path: str): - cmd = f"lvscan" - output_lines = TestRun.executor.run_expect_success(cmd).stdout.splitlines() - - for line in output_lines: - if "inactive " in line and lv_path in line: - cmd = f"lvchange -ay {lv_path}" - TestRun.executor.run_expect_success(cmd) diff --git a/test/functional/test-framework/storage_devices/partition.py b/test/functional/test-framework/storage_devices/partition.py deleted file mode 100644 index 080ab4c..0000000 --- a/test/functional/test-framework/storage_devices/partition.py +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from storage_devices.device import Device -from test_tools import disk_utils -from test_utils.size import Size - - -class Partition(Device): - def __init__(self, parent_dev, type, number, begin: Size, end: Size): - Device.__init__(self, disk_utils.get_partition_path(parent_dev.path, number)) - self.number = number - self.parent_device = parent_dev - self.type = type - self.begin = begin - self.end = end - - def __str__(self): - return f"\tsystem path: {self.path}, size: {self.size}, type: {self.type}, " \ - f"parent device: {self.parent_device.path}\n" diff --git a/test/functional/test-framework/storage_devices/raid.py b/test/functional/test-framework/storage_devices/raid.py deleted file mode 100644 index 4e3685c..0000000 --- a/test/functional/test-framework/storage_devices/raid.py +++ /dev/null @@ -1,182 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -import threading -from enum import IntEnum, Enum - -from core.test_run import TestRun -from storage_devices.device import Device -from storage_devices.disk import Disk -from test_tools.fs_utils import readlink -from test_tools.mdadm import Mdadm -from test_utils.disk_finder import resolve_to_by_id_link -from test_utils.size import Size, Unit - - -def get_devices_paths_string(devices: [Device]): - return " ".join([d.path for d in devices]) - - -class Level(IntEnum): - Raid0 = 0 - Raid1 = 1 - Raid4 = 4 - Raid5 = 5 - Raid6 = 6 - Raid10 = 10 - - -class StripSize(IntEnum): - Strip4K = 4 - Strip8K = 8 - Strip16K = 16 - Strip32K = 32 - Strip64K = 64 - Strip128K = 128 - Strip256K = 256 - Strip1M = 1024 - - -class MetadataVariant(Enum): - Legacy = "legacy" - Imsm = "imsm" - - -class RaidConfiguration: - def __init__( - self, - level: Level = None, - metadata: MetadataVariant = MetadataVariant.Imsm, - number_of_devices: int = 0, - size: Size = None, - strip_size: StripSize = None, - name: str = None, - ): - self.level = level - self.metadata = metadata - self.number_of_devices = number_of_devices - self.size = size - self.strip_size = strip_size - self.name = name - - -class Raid(Disk): - __unique_id = 0 - __lock = threading.Lock() - - def __init__( - self, - path: str, - level: Level, - uuid: str, - container_uuid: str = None, - container_path: str = None, - metadata: MetadataVariant = MetadataVariant.Imsm, - array_devices: [Device] = [], - volume_devices: [Device] = [], - ): - Device.__init__(self, resolve_to_by_id_link(path.replace("/dev/", ""))) - self.device_name = path.split('/')[-1] - self.level = level - self.uuid = uuid - self.container_uuid = container_uuid - self.container_path = container_path - self.metadata = metadata - self.array_devices = array_devices if array_devices else volume_devices.copy() - self.volume_devices = volume_devices - self.partitions = [] - self.__block_size = None - - def __eq__(self, other): - try: - return self.uuid == other.uuid - except AttributeError: - return False - - @property - def block_size(self): - if not self.__block_size: - self.__block_size = Unit(int(self.get_sysfs_property("logical_block_size"))) - return self.__block_size - - def stop(self): - Mdadm.stop(self.path) - if self.container_path: - Mdadm.stop(self.container_path) - - @classmethod - def discover(cls): - TestRun.LOGGER.info("Discover RAIDs in system...") - raids = [] - for raid in Mdadm.examine_result(): - raids.append( - cls( - raid["path"], - Level[raid["level"]], - raid["uuid"], - raid["container"]["uuid"] if "container" in raid else None, - raid["container"]["path"] if "container" in raid else None, - MetadataVariant(raid["metadata"]), - [Device(d) for d in raid["array_devices"]], - [Device(d) for d in raid["devices"]] - ) - ) - - return raids - - @classmethod - def create( - cls, - raid_configuration: RaidConfiguration, - devices: [Device] - ): - import copy - raid_conf = copy.deepcopy(raid_configuration) - - if not raid_conf.number_of_devices: - raid_conf.number_of_devices = len(devices) - elif len(devices) < raid_conf.number_of_devices: - raise ValueError("RAID configuration requires at least " - f"{raid_conf.number_of_devices} devices") - - md_dir_path = "/dev/md/" - array_devices = devices - volume_devices = devices[:raid_conf.number_of_devices] - - if raid_conf.metadata != MetadataVariant.Legacy: - container_conf = RaidConfiguration( - name=cls.__get_unique_name(raid_conf.metadata.value), - metadata=raid_conf.metadata, - number_of_devices=len(array_devices) - ) - Mdadm.create(container_conf, get_devices_paths_string(array_devices)) - - if not raid_conf.name: - raid_conf.name = cls.__get_unique_name() - - Mdadm.create(raid_conf, get_devices_paths_string(volume_devices)) - - raid_link = md_dir_path + raid_conf.name - raid = [r for r in Mdadm.examine_result() if readlink(r["path"]) == readlink(raid_link)][0] - - return cls( - raid["path"], - raid_conf.level, - raid["uuid"], - raid["container"]["uuid"] if "container" in raid else None, - raid["container"]["path"] if "container" in raid else None, - raid_conf.metadata, - array_devices, - volume_devices - ) - - @staticmethod - def remove_all(): - Mdadm.stop() - - @classmethod - def __get_unique_name(cls, prefix: str = "Raid"): - with cls.__lock: - cls.__unique_id += 1 - return f"{prefix}{cls.__unique_id}" diff --git a/test/functional/test-framework/storage_devices/ramdisk.py b/test/functional/test-framework/storage_devices/ramdisk.py deleted file mode 100644 index 011b812..0000000 --- a/test/functional/test-framework/storage_devices/ramdisk.py +++ /dev/null @@ -1,80 +0,0 @@ -# -# Copyright(c) 2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import posixpath - -from core.test_run import TestRun -from storage_devices.device import Device -from test_tools import disk_utils -from test_tools.fs_utils import ls, parse_ls_output -from test_utils.filesystem.symlink import Symlink -from test_utils.os_utils import reload_kernel_module, unload_kernel_module, is_kernel_module_loaded -from test_utils.size import Size, Unit - - -class RamDisk(Device): - _module = "brd" - - @classmethod - def create(cls, disk_size: Size, disk_count: int = 1): - if disk_count < 1: - raise ValueError("Wrong number of RAM disks requested") - - TestRun.LOGGER.info("Configure RAM disks...") - params = { - "rd_size": int(disk_size.get_value(Unit.KiB)), - "rd_nr": disk_count - } - reload_kernel_module(cls._module, params) - - if not cls._is_configured(disk_size, disk_count): - raise EnvironmentError(f"Wrong RAM disk configuration after loading '{cls._module}' " - "module") - - return cls.list() - - @classmethod - def remove_all(cls): - if not is_kernel_module_loaded(cls._module): - return - - for ram_disk in cls._list_devices(): - TestRun.executor.run(f"umount {ram_disk.full_path}") - link_path = posixpath.join("/dev/disk/by-id", ram_disk.name) - try: - link = Symlink.get_symlink(link_path=link_path, target=ram_disk.full_path) - link.remove(force=True) - except FileNotFoundError: - pass - TestRun.LOGGER.info("Removing RAM disks...") - unload_kernel_module(cls._module) - - @classmethod - def list(cls): - ram_disks = [] - for ram_disk in cls._list_devices(): - link_path = posixpath.join("/dev/disk/by-id", ram_disk.name) - link = Symlink.get_symlink( - link_path=link_path, target=ram_disk.full_path, create=True - ) - ram_disks.append(cls(link.full_path)) - - return ram_disks - - @classmethod - def _is_configured(cls, disk_size: Size, disk_count: int): - ram_disks = cls._list_devices() - return ( - len(ram_disks) >= disk_count - and Size(disk_utils.get_size(ram_disks[0].name), Unit.Byte).align_down(Unit.MiB.value) - == disk_size.align_down(Unit.MiB.value) - ) - - @staticmethod - def _list_devices(): - ls_ram_disks = ls("/dev/ram*") - if "No such file or directory" in ls_ram_disks: - return [] - return parse_ls_output(ls_ram_disks) diff --git a/test/functional/test-framework/test_tools/__init__.py b/test/functional/test-framework/test_tools/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/functional/test-framework/test_tools/blktrace.py b/test/functional/test-framework/test_tools/blktrace.py deleted file mode 100644 index c9c5653..0000000 --- a/test/functional/test-framework/test_tools/blktrace.py +++ /dev/null @@ -1,225 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -import math - -from aenum import IntFlag, Enum - -from datetime import timedelta - -from core.test_run import TestRun -from storage_devices.device import Device -from test_utils.filesystem.directory import Directory -from test_utils.os_utils import is_mounted, drop_caches, DropCachesMode -from test_utils.size import Size, Unit - -DEBUGFS_MOUNT_POINT = "/sys/kernel/debug" -PREFIX = "trace_" -HEADER_FORMAT = "%a|%C|%d|%e|%n|%N|%S|%5T.%9t\\n" - - -class BlkTraceMask(IntFlag): - read = 1 - write = 1 << 1 - flush = 1 << 2 - sync = 1 << 3 - queue = 1 << 4 - requeue = 1 << 5 - issue = 1 << 6 - complete = 1 << 7 - fs = 1 << 8 - pc = 1 << 9 - notify = 1 << 10 - ahead = 1 << 11 - meta = 1 << 12 - discard = 1 << 13 - drv_data = 1 << 14 - fua = 1 << 15 - - -class ActionKind(Enum): - IoDeviceRemap = "A" - IoBounce = "B" - IoCompletion = "C" - IoToDriver = "D" - IoFrontMerge = "F" - GetRequest = "G" - IoInsert = "I" - IoMerge = "M" - PlugRequest = "P" - IoHandled = "Q" - RequeueRequest = "R" - SleepRequest = "S" - TimeoutUnplug = "T" # old version of TimerUnplug - UnplugRequest = "U" - TimerUnplug = "UT" - Split = "X" - - -class RwbsKind(IntFlag): - Undefined = 0 - R = 1 # Read - W = 1 << 1 # Write - D = 1 << 2 # Discard - F = 1 << 3 # Flush - S = 1 << 4 # Synchronous - M = 1 << 5 # Metadata - A = 1 << 6 # Read Ahead - N = 1 << 7 # None of the above - - def __str__(self): - ret = [] - if self & RwbsKind.R: - ret.append("read") - if self & RwbsKind.W: - ret.append("write") - if self & RwbsKind.D: - ret.append("discard") - if self & RwbsKind.F: - ret.append("flush") - if self & RwbsKind.S: - ret.append("sync") - if self & RwbsKind.M: - ret.append("metadata") - if self & RwbsKind.A: - ret.append("readahead") - if self & RwbsKind.N: - ret.append("none") - - return "|".join(ret) - - -class BlkTrace: - def __init__(self, device: Device, *masks: BlkTraceMask): - self._mount_debugfs() - if device is None: - raise Exception("Device not provided") - self.device = device - self.masks = "" if not masks else f' -a {" -a ".join([m.name for m in masks])}' - self.blktrace_pid = -1 - self.__outputDirectoryPath = None - - @staticmethod - def _mount_debugfs(): - if not is_mounted(DEBUGFS_MOUNT_POINT): - TestRun.executor.run_expect_success(f"mount -t debugfs none {DEBUGFS_MOUNT_POINT}") - - def start_monitoring(self, buffer_size: Size = None, number_of_subbuffers: int = None): - if self.blktrace_pid != -1: - raise Exception(f"blktrace already running with PID: {self.blktrace_pid}") - - self.__outputDirectoryPath = Directory.create_temp_directory().full_path - - drop_caches(DropCachesMode.ALL) - - number_of_subbuffers = ("" if number_of_subbuffers is None - else f" --num-sub-buffers={number_of_subbuffers}") - buffer_size = ("" if buffer_size is None - else f" --buffer-size={buffer_size.get_value(Unit.KibiByte)}") - command = (f"blktrace{number_of_subbuffers}{buffer_size} --dev={self.device.path}" - f"{self.masks} --output={PREFIX} --output-dir={self.__outputDirectoryPath}") - echo_output = TestRun.executor.run_expect_success( - f"nohup {command} {self.__outputDirectoryPath}/out & echo $!" - ) - self.blktrace_pid = int(echo_output.stdout) - TestRun.LOGGER.info(f"blktrace monitoring for device {self.device.path} started" - f" (PID: {self.blktrace_pid}, output dir: {self.__outputDirectoryPath}") - - def stop_monitoring(self): - if self.blktrace_pid == -1: - raise Exception("PID for blktrace is not set - has monitoring been started?") - - drop_caches(DropCachesMode.ALL) - - TestRun.executor.run_expect_success(f"kill -s SIGINT {self.blktrace_pid}") - self.blktrace_pid = -1 - - # dummy command for swallowing output of killed command - TestRun.executor.run("sleep 2 && echo dummy") - TestRun.LOGGER.info(f"blktrace monitoring for device {self.device.path} stopped") - - return self.__parse_blktrace_output() - - def __parse_blktrace_output(self): - TestRun.LOGGER.info(f"Parsing blktrace headers from {self.__outputDirectoryPath}... " - "Be patient") - command = (f'blkparse --input-dir={self.__outputDirectoryPath} --input={PREFIX} ' - f'--format="{HEADER_FORMAT}"') - blkparse_output = TestRun.executor.run_expect_success( - command, timeout=timedelta(minutes=60) - ) - parsed_headers = [] - for line in blkparse_output.stdout.splitlines(): - # At the end per-cpu summary is posted - there is no need for it now - if line.startswith('CPU'): - break - - header = Header.parse(line) - if header is None: - continue - parsed_headers.append(header) - TestRun.LOGGER.info( - f"Parsed {len(parsed_headers)} blktrace headers from {self.__outputDirectoryPath}" - ) - parsed_headers.sort(key=lambda x: x.timestamp) - return parsed_headers - - -class Header: - def __init__(self): - self.action = None - self.block_count = None - self.byte_count = None - self.command = None - self.error_value = None - self.rwbs = RwbsKind.Undefined - self.sector_number = None - self.timestamp = None - - @staticmethod - def parse(header_line: str): - # messages/notifies are not formatted according to --format - # so should be ignored (or parsed using standard format): - if "m N" in header_line: - return None - - header_fields = header_line.split('|') - if len(header_fields) != 8: - return None - - timestamp_fields = header_fields[7].split('.') - timestamp_nano = int(timestamp_fields[-1]) if len(timestamp_fields) == 2 else 0 - - header = Header() - header.action = ActionKind(header_fields[0]) - header.command = header_fields[1] - if len(header_fields[2]): - header.rwbs = RwbsKind['|'.join(list(header_fields[2]))] - header.error_value = int(header_fields[3]) - header.block_count = int(header_fields[4]) - header.byte_count = int(header_fields[5]) - header.sector_number = int(header_fields[6]) - header.timestamp = int(timestamp_fields[0]) * math.pow(10, 9) + timestamp_nano - - return header - - def __str__(self): - ret = [] - if self.action: - ret.append(f"action: {self.action.name}") - if self.block_count: - ret.append(f"block_count: {self.block_count}") - if self.byte_count: - ret.append(f"byte_count: {self.byte_count}") - if self.command: - ret.append(f"command: {self.command}") - if self.error_value: - ret.append(f"error_value: {self.error_value}") - if self.rwbs: - ret.append(f"rwbs: {self.rwbs}") - if self.sector_number: - ret.append(f"sector_number: {self.sector_number}") - if self.timestamp: - ret.append(f"timestamp: {self.timestamp}") - return " ".join(ret) diff --git a/test/functional/test-framework/test_tools/checksec.sh b/test/functional/test-framework/test_tools/checksec.sh deleted file mode 100644 index dd1f72e..0000000 --- a/test/functional/test-framework/test_tools/checksec.sh +++ /dev/null @@ -1,882 +0,0 @@ -#!/bin/bash -# -# The BSD License (http://www.opensource.org/licenses/bsd-license.php) -# specifies the terms and conditions of use for checksec.sh: -# -# Copyright (c) 2009-2011, Tobias Klein. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Tobias Klein nor the name of trapkit.de may be -# used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -# -# Name : checksec.sh -# Version : 1.5 -# Author : Tobias Klein -# Date : November 2011 -# Download: http://www.trapkit.de/tools/checksec.html -# Changes : http://www.trapkit.de/tools/checksec_changes.txt -# -# Description: -# -# Modern Linux distributions offer some mitigation techniques to make it -# harder to exploit software vulnerabilities reliably. Mitigations such -# as RELRO, NoExecute (NX), Stack Canaries, Address Space Layout -# Randomization (ASLR) and Position Independent Executables (PIE) have -# made reliably exploiting any vulnerabilities that do exist far more -# challenging. The checksec.sh script is designed to test what *standard* -# Linux OS and PaX (http://pax.grsecurity.net/) security features are being -# used. -# -# As of version 1.3 the script also lists the status of various Linux kernel -# protection mechanisms. -# -# Credits: -# -# Thanks to Brad Spengler (grsecurity.net) for the PaX support. -# Thanks to Jon Oberheide (jon.oberheide.org) for the kernel support. -# Thanks to Ollie Whitehouse (Research In Motion) for rpath/runpath support. -# -# Others that contributed to checksec.sh (in no particular order): -# -# Simon Ruderich, Denis Scherbakov, Stefan Kuttler, Radoslaw Madej, -# Anthony G. Basile, Martin Vaeth and Brian Davis. -# - -# global vars -have_readelf=1 -verbose=false - -# FORTIFY_SOURCE vars -FS_end=_chk -FS_cnt_total=0 -FS_cnt_checked=0 -FS_cnt_unchecked=0 -FS_chk_func_libc=0 -FS_functions=0 -FS_libc=0 - -# version information -version() { - echo "checksec v1.5, Tobias Klein, www.trapkit.de, November 2011" - echo -} - -# help -help() { - echo "Usage: checksec [OPTION]" - echo - echo "Options:" - echo - echo " --file " - echo " --dir [-v]" - echo " --proc " - echo " --proc-all" - echo " --proc-libs " - echo " --kernel" - echo " --fortify-file " - echo " --fortify-proc " - echo " --version" - echo " --help" - echo - echo "For more information, see:" - echo " http://www.trapkit.de/tools/checksec.html" - echo -} - -# check if command exists -command_exists () { - type $1 > /dev/null 2>&1; -} - -# check if directory exists -dir_exists () { - if [ -d $1 ] ; then - return 0 - else - return 1 - fi -} - -# check user privileges -root_privs () { - if [ $(/usr/bin/id -u) -eq 0 ] ; then - return 0 - else - return 1 - fi -} - -# check if input is numeric -isNumeric () { - echo "$@" | grep -q -v "[^0-9]" -} - -# check if input is a string -isString () { - echo "$@" | grep -q -v "[^A-Za-z]" -} - -# check file(s) -filecheck() { - # check for RELRO support - if readelf -l $1 2>/dev/null | grep -q 'GNU_RELRO'; then - if readelf -d $1 2>/dev/null | grep -q 'BIND_NOW'; then - echo -n -e '\033[32mFull RELRO \033[m ' - else - echo -n -e '\033[33mPartial RELRO\033[m ' - fi - else - echo -n -e '\033[31mNo RELRO \033[m ' - fi - - # check for stack canary support - if readelf -s $1 2>/dev/null | grep -q '__stack_chk_fail'; then - echo -n -e '\033[32mCanary found \033[m ' - else - echo -n -e '\033[31mNo canary found\033[m ' - fi - - # check for NX support - if readelf -W -l $1 2>/dev/null | grep 'GNU_STACK' | grep -q 'RWE'; then - echo -n -e '\033[31mNX disabled\033[m ' - else - echo -n -e '\033[32mNX enabled \033[m ' - fi - - # check for PIE support - if readelf -h $1 2>/dev/null | grep -q 'Type:[[:space:]]*EXEC'; then - echo -n -e '\033[31mNo PIE \033[m ' - elif readelf -h $1 2>/dev/null | grep -q 'Type:[[:space:]]*DYN'; then - if readelf -d $1 2>/dev/null | grep -q '(DEBUG)'; then - echo -n -e '\033[32mPIE enabled \033[m ' - else - echo -n -e '\033[33mDSO \033[m ' - fi - else - echo -n -e '\033[33mNot an ELF file\033[m ' - fi - - # check for rpath / run path - if readelf -d $1 2>/dev/null | grep -q 'rpath'; then - echo -n -e '\033[31mRPATH \033[m ' - else - echo -n -e '\033[32mNo RPATH \033[m ' - fi - - if readelf -d $1 2>/dev/null | grep -q 'runpath'; then - echo -n -e '\033[31mRUNPATH \033[m ' - else - echo -n -e '\033[32mNo RUNPATH \033[m ' - fi -} - -# check process(es) -proccheck() { - # check for RELRO support - if readelf -l $1/exe 2>/dev/null | grep -q 'Program Headers'; then - if readelf -l $1/exe 2>/dev/null | grep -q 'GNU_RELRO'; then - if readelf -d $1/exe 2>/dev/null | grep -q 'BIND_NOW'; then - echo -n -e '\033[32mFull RELRO \033[m ' - else - echo -n -e '\033[33mPartial RELRO \033[m ' - fi - else - echo -n -e '\033[31mNo RELRO \033[m ' - fi - else - echo -n -e '\033[31mPermission denied (please run as root)\033[m\n' - exit 1 - fi - - # check for stack canary support - if readelf -s $1/exe 2>/dev/null | grep -q 'Symbol table'; then - if readelf -s $1/exe 2>/dev/null | grep -q '__stack_chk_fail'; then - echo -n -e '\033[32mCanary found \033[m ' - else - echo -n -e '\033[31mNo canary found \033[m ' - fi - else - if [ "$1" != "1" ] ; then - echo -n -e '\033[33mPermission denied \033[m ' - else - echo -n -e '\033[33mNo symbol table found\033[m ' - fi - fi - - # first check for PaX support - if cat $1/status 2> /dev/null | grep -q 'PaX:'; then - pageexec=( $(cat $1/status 2> /dev/null | grep 'PaX:' | cut -b6) ) - segmexec=( $(cat $1/status 2> /dev/null | grep 'PaX:' | cut -b10) ) - mprotect=( $(cat $1/status 2> /dev/null | grep 'PaX:' | cut -b8) ) - randmmap=( $(cat $1/status 2> /dev/null | grep 'PaX:' | cut -b9) ) - if [[ "$pageexec" = "P" || "$segmexec" = "S" ]] && [[ "$mprotect" = "M" && "$randmmap" = "R" ]] ; then - echo -n -e '\033[32mPaX enabled\033[m ' - elif [[ "$pageexec" = "p" && "$segmexec" = "s" && "$randmmap" = "R" ]] ; then - echo -n -e '\033[33mPaX ASLR only\033[m ' - elif [[ "$pageexec" = "P" || "$segmexec" = "S" ]] && [[ "$mprotect" = "m" && "$randmmap" = "R" ]] ; then - echo -n -e '\033[33mPaX mprot off \033[m' - elif [[ "$pageexec" = "P" || "$segmexec" = "S" ]] && [[ "$mprotect" = "M" && "$randmmap" = "r" ]] ; then - echo -n -e '\033[33mPaX ASLR off\033[m ' - elif [[ "$pageexec" = "P" || "$segmexec" = "S" ]] && [[ "$mprotect" = "m" && "$randmmap" = "r" ]] ; then - echo -n -e '\033[33mPaX NX only\033[m ' - else - echo -n -e '\033[31mPaX disabled\033[m ' - fi - # fallback check for NX support - elif readelf -W -l $1/exe 2>/dev/null | grep 'GNU_STACK' | grep -q 'RWE'; then - echo -n -e '\033[31mNX disabled\033[m ' - else - echo -n -e '\033[32mNX enabled \033[m ' - fi - - # check for PIE support - if readelf -h $1/exe 2>/dev/null | grep -q 'Type:[[:space:]]*EXEC'; then - echo -n -e '\033[31mNo PIE \033[m ' - elif readelf -h $1/exe 2>/dev/null | grep -q 'Type:[[:space:]]*DYN'; then - if readelf -d $1/exe 2>/dev/null | grep -q '(DEBUG)'; then - echo -n -e '\033[32mPIE enabled \033[m ' - else - echo -n -e '\033[33mDynamic Shared Object\033[m ' - fi - else - echo -n -e '\033[33mNot an ELF file \033[m ' - fi -} - -# check mapped libraries -libcheck() { - libs=( $(awk '{ print $6 }' /proc/$1/maps | grep '/' | sort -u | xargs file | grep ELF | awk '{ print $1 }' | sed 's/:/ /') ) - - printf "\n* Loaded libraries (file information, # of mapped files: ${#libs[@]}):\n\n" - - for element in $(seq 0 $((${#libs[@]} - 1))) - do - echo " ${libs[$element]}:" - echo -n " " - filecheck ${libs[$element]} - printf "\n\n" - done -} - -# check for system-wide ASLR support -aslrcheck() { - # PaX ASLR support - if !(cat /proc/1/status 2> /dev/null | grep -q 'Name:') ; then - echo -n -e ':\033[33m insufficient privileges for PaX ASLR checks\033[m\n' - echo -n -e ' Fallback to standard Linux ASLR check' - fi - - if cat /proc/1/status 2> /dev/null | grep -q 'PaX:'; then - printf ": " - if cat /proc/1/status 2> /dev/null | grep 'PaX:' | grep -q 'R'; then - echo -n -e '\033[32mPaX ASLR enabled\033[m\n\n' - else - echo -n -e '\033[31mPaX ASLR disabled\033[m\n\n' - fi - else - # standard Linux 'kernel.randomize_va_space' ASLR support - # (see the kernel file 'Documentation/sysctl/kernel.txt' for a detailed description) - printf " (kernel.randomize_va_space): " - if /sbin/sysctl -a 2>/dev/null | grep -q 'kernel.randomize_va_space = 1'; then - echo -n -e '\033[33mOn (Setting: 1)\033[m\n\n' - printf " Description - Make the addresses of mmap base, stack and VDSO page randomized.\n" - printf " This, among other things, implies that shared libraries will be loaded to \n" - printf " random addresses. Also for PIE-linked binaries, the location of code start\n" - printf " is randomized. Heap addresses are *not* randomized.\n\n" - elif /sbin/sysctl -a 2>/dev/null | grep -q 'kernel.randomize_va_space = 2'; then - echo -n -e '\033[32mOn (Setting: 2)\033[m\n\n' - printf " Description - Make the addresses of mmap base, heap, stack and VDSO page randomized.\n" - printf " This, among other things, implies that shared libraries will be loaded to random \n" - printf " addresses. Also for PIE-linked binaries, the location of code start is randomized.\n\n" - elif /sbin/sysctl -a 2>/dev/null | grep -q 'kernel.randomize_va_space = 0'; then - echo -n -e '\033[31mOff (Setting: 0)\033[m\n' - else - echo -n -e '\033[31mNot supported\033[m\n' - fi - printf " See the kernel file 'Documentation/sysctl/kernel.txt' for more details.\n\n" - fi -} - -# check cpu nx flag -nxcheck() { - if grep -q nx /proc/cpuinfo; then - echo -n -e '\033[32mYes\033[m\n\n' - else - echo -n -e '\033[31mNo\033[m\n\n' - fi -} - -# check for kernel protection mechanisms -kernelcheck() { - printf " Description - List the status of kernel protection mechanisms. Rather than\n" - printf " inspect kernel mechanisms that may aid in the prevention of exploitation of\n" - printf " userspace processes, this option lists the status of kernel configuration\n" - printf " options that harden the kernel itself against attack.\n\n" - printf " Kernel config: " - - if [ -f /proc/config.gz ] ; then - kconfig="zcat /proc/config.gz" - printf "\033[32m/proc/config.gz\033[m\n\n" - elif [ -f /boot/config-`uname -r` ] ; then - kconfig="cat /boot/config-`uname -r`" - printf "\033[33m/boot/config-`uname -r`\033[m\n\n" - printf " Warning: The config on disk may not represent running kernel config!\n\n"; - elif [ -f "${KBUILD_OUTPUT:-/usr/src/linux}"/.config ] ; then - kconfig="cat ${KBUILD_OUTPUT:-/usr/src/linux}/.config" - printf "\033[33m%s\033[m\n\n" "${KBUILD_OUTPUT:-/usr/src/linux}/.config" - printf " Warning: The config on disk may not represent running kernel config!\n\n"; - else - printf "\033[31mNOT FOUND\033[m\n\n" - exit 0 - fi - - printf " GCC stack protector support: " - if $kconfig | grep -qi 'CONFIG_CC_STACKPROTECTOR=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Strict user copy checks: " - if $kconfig | grep -qi 'CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Enforce read-only kernel data: " - if $kconfig | grep -qi 'CONFIG_DEBUG_RODATA=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - printf " Restrict /dev/mem access: " - if $kconfig | grep -qi 'CONFIG_STRICT_DEVMEM=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Restrict /dev/kmem access: " - if $kconfig | grep -qi 'CONFIG_DEVKMEM=y'; then - printf "\033[31mDisabled\033[m\n" - else - printf "\033[32mEnabled\033[m\n" - fi - - printf "\n" - printf "* grsecurity / PaX: " - - if $kconfig | grep -qi 'CONFIG_GRKERNSEC=y'; then - if $kconfig | grep -qi 'CONFIG_GRKERNSEC_HIGH=y'; then - printf "\033[32mHigh GRKERNSEC\033[m\n\n" - elif $kconfig | grep -qi 'CONFIG_GRKERNSEC_MEDIUM=y'; then - printf "\033[33mMedium GRKERNSEC\033[m\n\n" - elif $kconfig | grep -qi 'CONFIG_GRKERNSEC_LOW=y'; then - printf "\033[31mLow GRKERNSEC\033[m\n\n" - else - printf "\033[33mCustom GRKERNSEC\033[m\n\n" - fi - - printf " Non-executable kernel pages: " - if $kconfig | grep -qi 'CONFIG_PAX_KERNEXEC=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Prevent userspace pointer deref: " - if $kconfig | grep -qi 'CONFIG_PAX_MEMORY_UDEREF=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Prevent kobject refcount overflow: " - if $kconfig | grep -qi 'CONFIG_PAX_REFCOUNT=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Bounds check heap object copies: " - if $kconfig | grep -qi 'CONFIG_PAX_USERCOPY=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Disable writing to kmem/mem/port: " - if $kconfig | grep -qi 'CONFIG_GRKERNSEC_KMEM=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Disable privileged I/O: " - if $kconfig | grep -qi 'CONFIG_GRKERNSEC_IO=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Harden module auto-loading: " - if $kconfig | grep -qi 'CONFIG_GRKERNSEC_MODHARDEN=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - - printf " Hide kernel symbols: " - if $kconfig | grep -qi 'CONFIG_GRKERNSEC_HIDESYM=y'; then - printf "\033[32mEnabled\033[m\n" - else - printf "\033[31mDisabled\033[m\n" - fi - else - printf "\033[31mNo GRKERNSEC\033[m\n\n" - printf " The grsecurity / PaX patchset is available here:\n" - printf " http://grsecurity.net/\n" - fi - - printf "\n" - printf "* Kernel Heap Hardening: " - - if $kconfig | grep -qi 'CONFIG_KERNHEAP=y'; then - if $kconfig | grep -qi 'CONFIG_KERNHEAP_FULLPOISON=y'; then - printf "\033[32mFull KERNHEAP\033[m\n\n" - else - printf "\033[33mPartial KERNHEAP\033[m\n\n" - fi - else - printf "\033[31mNo KERNHEAP\033[m\n\n" - printf " The KERNHEAP hardening patchset is available here:\n" - printf " https://www.subreption.com/kernheap/\n\n" - fi -} - -# --- FORTIFY_SOURCE subfunctions (start) --- - -# is FORTIFY_SOURCE supported by libc? -FS_libc_check() { - printf "* FORTIFY_SOURCE support available (libc) : " - - if [ "${#FS_chk_func_libc[@]}" != "0" ] ; then - printf "\033[32mYes\033[m\n" - else - printf "\033[31mNo\033[m\n" - exit 1 - fi -} - -# was the binary compiled with FORTIFY_SOURCE? -FS_binary_check() { - printf "* Binary compiled with FORTIFY_SOURCE support: " - - for FS_elem_functions in $(seq 0 $((${#FS_functions[@]} - 1))) - do - if [[ ${FS_functions[$FS_elem_functions]} =~ _chk ]] ; then - printf "\033[32mYes\033[m\n" - return - fi - done - printf "\033[31mNo\033[m\n" - exit 1 -} - -FS_comparison() { - echo - printf " ------ EXECUTABLE-FILE ------- . -------- LIBC --------\n" - printf " FORTIFY-able library functions | Checked function names\n" - printf " -------------------------------------------------------\n" - - for FS_elem_libc in $(seq 0 $((${#FS_chk_func_libc[@]} - 1))) - do - for FS_elem_functions in $(seq 0 $((${#FS_functions[@]} - 1))) - do - FS_tmp_func=${FS_functions[$FS_elem_functions]} - FS_tmp_libc=${FS_chk_func_libc[$FS_elem_libc]} - - if [[ $FS_tmp_func =~ ^$FS_tmp_libc$ ]] ; then - printf " \033[31m%-30s\033[m | __%s%s\n" $FS_tmp_func $FS_tmp_libc $FS_end - let FS_cnt_total++ - let FS_cnt_unchecked++ - elif [[ $FS_tmp_func =~ ^$FS_tmp_libc(_chk) ]] ; then - printf " \033[32m%-30s\033[m | __%s%s\n" $FS_tmp_func $FS_tmp_libc $FS_end - let FS_cnt_total++ - let FS_cnt_checked++ - fi - - done - done -} - -FS_summary() { - echo - printf "SUMMARY:\n\n" - printf "* Number of checked functions in libc : ${#FS_chk_func_libc[@]}\n" - printf "* Total number of library functions in the executable: ${#FS_functions[@]}\n" - printf "* Number of FORTIFY-able functions in the executable : %s\n" $FS_cnt_total - printf "* Number of checked functions in the executable : \033[32m%s\033[m\n" $FS_cnt_checked - printf "* Number of unchecked functions in the executable : \033[31m%s\033[m\n" $FS_cnt_unchecked - echo -} - -# --- FORTIFY_SOURCE subfunctions (end) --- - -if !(command_exists readelf) ; then - printf "\033[31mWarning: 'readelf' not found! It's required for most checks.\033[m\n\n" - have_readelf=0 -fi - -# parse command-line arguments -case "$1" in - - --version) - version - exit 0 - ;; - - --help) - help - exit 0 - ;; - - --dir) - if [ "$3" = "-v" ] ; then - verbose=true - fi - if [ $have_readelf -eq 0 ] ; then - exit 1 - fi - if [ -z "$2" ] ; then - printf "\033[31mError: Please provide a valid directory.\033[m\n\n" - exit 1 - fi - # remove trailing slashes - tempdir=`echo $2 | sed -e "s/\/*$//"` - if [ ! -d $tempdir ] ; then - printf "\033[31mError: The directory '$tempdir' does not exist.\033[m\n\n" - exit 1 - fi - cd $tempdir - printf "RELRO STACK CANARY NX PIE RPATH RUNPATH FILE\n" - for N in [A-Za-z]*; do - if [ "$N" != "[A-Za-z]*" ]; then - # read permissions? - if [ ! -r $N ]; then - printf "\033[31mError: No read permissions for '$tempdir/$N' (run as root).\033[m\n" - else - # ELF executable? - out=`file $N` - if [[ ! $out =~ ELF ]] ; then - if [ "$verbose" = "true" ] ; then - printf "\033[34m*** Not an ELF file: $tempdir/" - file $N - printf "\033[m" - fi - else - filecheck $N - if [ `find $tempdir/$N \( -perm -004000 -o -perm -002000 \) -type f -print` ]; then - printf "\033[37;41m%s%s\033[m" $2 $N - else - printf "%s%s" $tempdir/ $N - fi - echo - fi - fi - fi - done - exit 0 - ;; - - --file) - if [ $have_readelf -eq 0 ] ; then - exit 1 - fi - if [ -z "$2" ] ; then - printf "\033[31mError: Please provide a valid file.\033[m\n\n" - exit 1 - fi - # does the file exist? - if [ ! -e $2 ] ; then - printf "\033[31mError: The file '$2' does not exist.\033[m\n\n" - exit 1 - fi - # read permissions? - if [ ! -r $2 ] ; then - printf "\033[31mError: No read permissions for '$2' (run as root).\033[m\n\n" - exit 1 - fi - # ELF executable? - out=`file $2` - if [[ ! $out =~ ELF ]] ; then - printf "\033[31mError: Not an ELF file: " - file $2 - printf "\033[m\n" - exit 1 - fi - printf "RELRO STACK CANARY NX PIE RPATH RUNPATH FILE\n" - filecheck $2 - if [ `find $2 \( -perm -004000 -o -perm -002000 \) -type f -print` ] ; then - printf "\033[37;41m%s%s\033[m" $2 $N - else - printf "%s" $2 - fi - echo - exit 0 - ;; - - --proc-all) - if [ $have_readelf -eq 0 ] ; then - exit 1 - fi - cd /proc - printf "* System-wide ASLR" - aslrcheck - printf "* Does the CPU support NX: " - nxcheck - printf " COMMAND PID RELRO STACK CANARY NX/PaX PIE\n" - for N in [1-9]*; do - if [ $N != $$ ] && readlink -q $N/exe > /dev/null; then - printf "%16s" `head -1 $N/status | cut -b 7-` - printf "%7d " $N - proccheck $N - echo - fi - done - if [ ! -e /usr/bin/id ] ; then - printf "\n\033[33mNote: If you are running 'checksec.sh' as an unprivileged user, you\n" - printf " will not see all processes. Please run the script as root.\033[m\n\n" - else - if !(root_privs) ; then - printf "\n\033[33mNote: You are running 'checksec.sh' as an unprivileged user.\n" - printf " Too see all processes, please run the script as root.\033[m\n\n" - fi - fi - exit 0 - ;; - - --proc) - if [ $have_readelf -eq 0 ] ; then - exit 1 - fi - if [ -z "$2" ] ; then - printf "\033[31mError: Please provide a valid process name.\033[m\n\n" - exit 1 - fi - if !(isString "$2") ; then - printf "\033[31mError: Please provide a valid process name.\033[m\n\n" - exit 1 - fi - cd /proc - printf "* System-wide ASLR" - aslrcheck - printf "* Does the CPU support NX: " - nxcheck - printf " COMMAND PID RELRO STACK CANARY NX/PaX PIE\n" - for N in `ps -Ao pid,comm | grep $2 | cut -b1-6`; do - if [ -d $N ] ; then - printf "%16s" `head -1 $N/status | cut -b 7-` - printf "%7d " $N - # read permissions? - if [ ! -r $N/exe ] ; then - if !(root_privs) ; then - printf "\033[31mNo read permissions for '/proc/$N/exe' (run as root).\033[m\n\n" - exit 1 - fi - if [ ! `readlink $N/exe` ] ; then - printf "\033[31mPermission denied. Requested process ID belongs to a kernel thread.\033[m\n\n" - exit 1 - fi - exit 1 - fi - proccheck $N - echo - fi - done - exit 0 - ;; - - --proc-libs) - if [ $have_readelf -eq 0 ] ; then - exit 1 - fi - if [ -z "$2" ] ; then - printf "\033[31mError: Please provide a valid process ID.\033[m\n\n" - exit 1 - fi - if !(isNumeric "$2") ; then - printf "\033[31mError: Please provide a valid process ID.\033[m\n\n" - exit 1 - fi - cd /proc - printf "* System-wide ASLR" - aslrcheck - printf "* Does the CPU support NX: " - nxcheck - printf "* Process information:\n\n" - printf " COMMAND PID RELRO STACK CANARY NX/PaX PIE\n" - N=$2 - if [ -d $N ] ; then - printf "%16s" `head -1 $N/status | cut -b 7-` - printf "%7d " $N - # read permissions? - if [ ! -r $N/exe ] ; then - if !(root_privs) ; then - printf "\033[31mNo read permissions for '/proc/$N/exe' (run as root).\033[m\n\n" - exit 1 - fi - if [ ! `readlink $N/exe` ] ; then - printf "\033[31mPermission denied. Requested process ID belongs to a kernel thread.\033[m\n\n" - exit 1 - fi - exit 1 - fi - proccheck $N - echo - libcheck $N - fi - exit 0 - ;; - - --kernel) - cd /proc - printf "* Kernel protection information:\n\n" - kernelcheck - exit 0 - ;; - - --fortify-file) - if [ $have_readelf -eq 0 ] ; then - exit 1 - fi - if [ -z "$2" ] ; then - printf "\033[31mError: Please provide a valid file.\033[m\n\n" - exit 1 - fi - # does the file exist? - if [ ! -e $2 ] ; then - printf "\033[31mError: The file '$2' does not exist.\033[m\n\n" - exit 1 - fi - # read permissions? - if [ ! -r $2 ] ; then - printf "\033[31mError: No read permissions for '$2' (run as root).\033[m\n\n" - exit 1 - fi - # ELF executable? - out=`file $2` - if [[ ! $out =~ ELF ]] ; then - printf "\033[31mError: Not an ELF file: " - file $2 - printf "\033[m\n" - exit 1 - fi - if [ -e /lib/libc.so.6 ] ; then - FS_libc=/lib/libc.so.6 - elif [ -e /lib64/libc.so.6 ] ; then - FS_libc=/lib64/libc.so.6 - elif [ -e /lib/i386-linux-gnu/libc.so.6 ] ; then - FS_libc=/lib/i386-linux-gnu/libc.so.6 - elif [ -e /lib/x86_64-linux-gnu/libc.so.6 ] ; then - FS_libc=/lib/x86_64-linux-gnu/libc.so.6 - else - printf "\033[31mError: libc not found.\033[m\n\n" - exit 1 - fi - - FS_chk_func_libc=( $(readelf -s $FS_libc | grep _chk@@ | awk '{ print $8 }' | cut -c 3- | sed -e 's/_chk@.*//') ) - FS_functions=( $(readelf -s $2 | awk '{ print $8 }' | sed 's/_*//' | sed -e 's/@.*//') ) - - FS_libc_check - FS_binary_check - FS_comparison - FS_summary - - exit 0 - ;; - - --fortify-proc) - if [ $have_readelf -eq 0 ] ; then - exit 1 - fi - if [ -z "$2" ] ; then - printf "\033[31mError: Please provide a valid process ID.\033[m\n\n" - exit 1 - fi - if !(isNumeric "$2") ; then - printf "\033[31mError: Please provide a valid process ID.\033[m\n\n" - exit 1 - fi - cd /proc - N=$2 - if [ -d $N ] ; then - # read permissions? - if [ ! -r $N/exe ] ; then - if !(root_privs) ; then - printf "\033[31mNo read permissions for '/proc/$N/exe' (run as root).\033[m\n\n" - exit 1 - fi - if [ ! `readlink $N/exe` ] ; then - printf "\033[31mPermission denied. Requested process ID belongs to a kernel thread.\033[m\n\n" - exit 1 - fi - exit 1 - fi - if [ -e /lib/libc.so.6 ] ; then - FS_libc=/lib/libc.so.6 - elif [ -e /lib64/libc.so.6 ] ; then - FS_libc=/lib64/libc.so.6 - elif [ -e /lib/i386-linux-gnu/libc.so.6 ] ; then - FS_libc=/lib/i386-linux-gnu/libc.so.6 - elif [ -e /lib/x86_64-linux-gnu/libc.so.6 ] ; then - FS_libc=/lib/x86_64-linux-gnu/libc.so.6 - else - printf "\033[31mError: libc not found.\033[m\n\n" - exit 1 - fi - printf "* Process name (PID) : %s (%d)\n" `head -1 $N/status | cut -b 7-` $N - FS_chk_func_libc=( $(readelf -s $FS_libc | grep _chk@@ | awk '{ print $8 }' | cut -c 3- | sed -e 's/_chk@.*//') ) - FS_functions=( $(readelf -s $2/exe | awk '{ print $8 }' | sed 's/_*//' | sed -e 's/@.*//') ) - - FS_libc_check - FS_binary_check - FS_comparison - FS_summary - fi - exit 0 - ;; - - *) - if [ "$#" != "0" ] ; then - printf "\033[31mError: Unknown option '$1'.\033[m\n\n" - fi - help - exit 1 - ;; -esac diff --git a/test/functional/test-framework/test_tools/dd.py b/test/functional/test-framework/test_tools/dd.py deleted file mode 100644 index 1ca51db..0000000 --- a/test/functional/test-framework/test_tools/dd.py +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import test_utils.linux_command as linux_comm -import test_utils.size as size -from core.test_run import TestRun - - -class Dd(linux_comm.LinuxCommand): - def __init__(self): - linux_comm.LinuxCommand.__init__(self, TestRun.executor, 'dd') - - def block_size(self, value: size.Size): - return self.set_param('bs', int(value.get_value())) - - def count(self, value): - return self.set_param('count', value) - - def input(self, value): - return self.set_param('if', value) - - def iflag(self, *values): - return self.set_param('iflag', *values) - - def oflag(self, *values): - return self.set_param('oflag', *values) - - def conv(self, *values): - return self.set_param('conv', *values) - - def output(self, value): - return self.set_param('of', value) - - def seek(self, value): - return self.set_param('seek', value) - - def skip(self, value): - return self.set_param('skip', value) diff --git a/test/functional/test-framework/test_tools/ddrescue.py b/test/functional/test-framework/test_tools/ddrescue.py deleted file mode 100644 index dfd3079..0000000 --- a/test/functional/test-framework/test_tools/ddrescue.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import test_utils.linux_command as linux_comm -import test_utils.size as size -from core.test_run import TestRun - - -class Ddrescue(linux_comm.LinuxCommand): - def __init__(self): - linux_comm.LinuxCommand.__init__(self, TestRun.executor, 'ddrescue') - self.source_path = None - self.destination_path = None - self.param_name_prefix = "--" - - def source(self, value): - self.source_path = value - return self - - def destination(self, value): - self.destination_path = value - return self - - def reverse(self): - return self.set_flags("reverse") - - def synchronous(self): - return self.set_flags("synchronous") - - def direct(self): - return self.set_flags("direct") - - def force(self): - return self.set_flags("force") - - def block_size(self, value: size.Size): - return self.set_param('sector-size', int(value.get_value())) - - def size(self, value: size.Size): - return self.set_param('size', int(value.get_value())) - - def __str__(self): - command = linux_comm.LinuxCommand.__str__(self) - command += f" {self.source_path} {self.destination_path}" - return command diff --git a/test/functional/test-framework/test_tools/device_mapper.py b/test/functional/test-framework/test_tools/device_mapper.py deleted file mode 100644 index ede38df..0000000 --- a/test/functional/test-framework/test_tools/device_mapper.py +++ /dev/null @@ -1,329 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from enum import Enum - -from core.test_run import TestRun -from storage_devices.device import Device -from test_utils.disk_finder import resolve_to_by_id_link -from test_utils.linux_command import LinuxCommand -from test_utils.size import Size, Unit - - -class DmTarget(Enum): - # Fill argument types for other targets if you need them - LINEAR = (str, int) - STRIPED = (int, int, list) - ERROR = () - ZERO = () - CRYPT = () - DELAY = (str, int, int, str, int, int) - FLAKEY = (str, int, int, int) - MIRROR = () - MULTIPATH = () - RAID = () - SNAPSHOT = () - - def __str__(self): - return self.name.lower() - - -class DmTable: - class TableEntry: - pass - - -class DmTable: - class TableEntry: - def __init__(self, offset: int, length: int, target: DmTarget, *params): - self.offset = int(offset) - self.length = int(length) - self.target = DmTarget(target) - self.params = list(params) - self.validate() - - def validate(self): - if self.target.value: - for i in range(len(self.params)): - try: - self.params[i] = self.target.value[i](self.params[i]) - except IndexError: - raise ValueError("invalid dm target parameter") - - def __str__(self): - ret = f"{self.offset} {self.length} {self.target}" - for param in self.params: - ret += f" {param}" - - return ret - - def __init__(self): - self.table = [] - - @classmethod - def uniform_error_table( - cls, start_lba: int, stop_lba: int, num_error_zones: int, error_zone_size: Size - ): - table = cls() - increment = (stop_lba - start_lba) // num_error_zones - - for zone_start in range(start_lba, stop_lba, increment): - table.add_entry( - DmTable.TableEntry( - zone_start, - error_zone_size.get_value(Unit.Blocks512), - DmTarget.ERROR, - ) - ) - - return table - - @classmethod - def passthrough_table(cls, device: Device): - table = cls() - - table.add_entry( - DmTable.TableEntry( - 0, - device.size.get_value(Unit.Blocks512), - DmTarget.LINEAR, - device.path, - 0, - ) - ) - - return table - - @classmethod - def error_table(cls, offset: int, size: Size): - table = cls() - - table.add_entry( - DmTable.TableEntry(offset, size.get_value(Unit.Blocks512), DmTarget.ERROR) - ) - - return table - - def fill_gaps(self, device: Device, fill_end=True): - gaps = self.get_gaps() - - for gap in gaps[:-1]: - self.add_entry( - DmTable.TableEntry( - gap[0], gap[1], DmTarget.LINEAR, device.path, int(gap[0]) - ) - ) - - table_end = gaps[-1][0] - - if fill_end and (Size(table_end, Unit.Blocks512) < device.size): - self.add_entry( - DmTable.TableEntry( - table_end, - device.size.get_value(Unit.Blocks512) - table_end, - DmTarget.LINEAR, - device.path, - table_end, - ) - ) - - return self - - def add_entry(self, entry: DmTable.TableEntry): - self.table.append(entry) - return self - - def get_gaps(self): - if not self.table: - return [(0, -1)] - - gaps = [] - - self.table.sort(key=lambda entry: entry.offset) - - if self.table[0].offset != 0: - gaps.append((0, self.table[0].offset)) - - for e1, e2 in zip(self.table, self.table[1:]): - if e1.offset + e1.length != e2.offset: - gaps.append( - (e1.offset + e1.length, e2.offset - (e1.offset + e1.length)) - ) - - if len(self.table) > 1: - gaps.append((e2.offset + e2.length, -1)) - else: - gaps.append((self.table[0].offset + self.table[0].length, -1)) - - return gaps - - def validate(self): - self.table.sort(key=lambda entry: entry.offset) - - if self.table[0].offset != 0: - raise ValueError(f"dm table should start at LBA 0: {self.table[0]}") - - for e1, e2 in zip(self.table, self.table[1:]): - if e1.offset + e1.length != e2.offset: - raise ValueError( - f"dm table should not have any holes or overlaps: {e1} -> {e2}" - ) - - def get_size(self): - self.table.sort(key=lambda entry: entry.offset) - - return Size(self.table[-1].offset + self.table[-1].length, Unit.Blocks512) - - def __str__(self): - output = "" - - for entry in self.table: - output += f"{entry}\n" - - return output - - -class DeviceMapper(LinuxCommand): - @classmethod - def remove_all(cls, force=True): - TestRun.LOGGER.info("Removing all device mapper devices") - - cmd = "dmsetup remove_all" - if force: - cmd += " --force" - - return TestRun.executor.run_expect_success(cmd) - - def __init__(self, name: str): - LinuxCommand.__init__(self, TestRun.executor, "dmsetup") - self.name = name - - @staticmethod - def wrap_table(table: DmTable): - return f"<< ENDHERE\n{str(table)}ENDHERE\n" - - def get_path(self): - return f"/dev/mapper/{self.name}" - - def clear(self): - return TestRun.executor.run_expect_success(f"{self.command_name} clear {self.name}") - - def create(self, table: DmTable): - try: - table.validate() - except ValueError: - for entry in table.table: - TestRun.LOGGER.error(f"{entry}") - raise - - TestRun.LOGGER.info(f"Creating device mapper device '{self.name}'") - - for entry in table.table: - TestRun.LOGGER.debug(f"{entry}") - - return TestRun.executor.run_expect_success( - f"{self.command_name} create {self.name} {self.wrap_table(table)}" - ) - - def remove(self): - TestRun.LOGGER.info(f"Removing device mapper device '{self.name}'") - - return TestRun.executor.run_expect_success(f"{self.command_name} remove {self.name}") - - def suspend(self): - TestRun.LOGGER.info(f"Suspending device mapper device '{self.name}'") - return TestRun.executor.run_expect_success(f"{self.command_name} suspend {self.name}") - - def resume(self): - TestRun.LOGGER.info(f"Resuming device mapper device '{self.name}'") - return TestRun.executor.run_expect_success(f"{self.command_name} resume {self.name}") - - def reload(self, table: DmTable): - table.validate() - TestRun.LOGGER.info(f"Reloading table for device mapper device '{self.name}'") - - for entry in table.table: - TestRun.LOGGER.debug(f"{entry}") - - return TestRun.executor.run_expect_success( - f"{self.command_name} reload {self.name} {self.wrap_table(table)}" - ) - - -class ErrorDevice(Device): - def __init__(self, name: str, base_device: Device, table: DmTable = None): - self.device = base_device - self.mapper = DeviceMapper(name) - self.name = name - self.table = DmTable.passthrough_table(base_device) if not table else table - self.active = False - self.start() - self.path = resolve_to_by_id_link(self.mapper.get_path().replace('/dev/', '')) - - @property - def system_path(self): - if self.active: - output = TestRun.executor.run_expect_success(f"realpath {self.mapper.get_path()}") - - return output.stdout - - return None - - @property - def size(self): - if self.active: - return self.table.get_size() - - return None - - def start(self): - self.mapper.create(self.table) - self.active = True - - def stop(self): - self.mapper.remove() - self.active = False - - def change_table(self, table: DmTable, permanent=True): - if self.active: - self.mapper.suspend() - - self.mapper.reload(table) - - self.mapper.resume() - - if permanent: - self.table = table - - def suspend_errors(self): - empty_table = DmTable.passthrough_table(self.device) - TestRun.LOGGER.info(f"Suspending issuing errors for error device '{self.name}'") - - self.change_table(empty_table, False) - - def resume_errors(self): - TestRun.LOGGER.info(f"Resuming issuing errors for error device '{self.name}'") - - self.change_table(self.table, False) - - def suspend(self): - if not self.active: - TestRun.LOGGER.warning( - f"cannot suspend error device '{self.name}'! It's already running" - ) - - self.mapper.suspend() - - self.active = False - - def resume(self): - if self.active: - TestRun.LOGGER.warning( - f"cannot resume error device '{self.name}'! It's already running" - ) - - self.mapper.resume() - - self.active = True diff --git a/test/functional/test-framework/test_tools/disk_utils.py b/test/functional/test-framework/test_tools/disk_utils.py deleted file mode 100644 index 38ac3a7..0000000 --- a/test/functional/test-framework/test_tools/disk_utils.py +++ /dev/null @@ -1,397 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import posixpath -import re -import time -from enum import Enum - -from core.test_run import TestRun -from test_tools import fs_utils -from test_tools.dd import Dd -from test_tools.fs_utils import readlink, parse_ls_output, ls -from test_utils.output import CmdException -from test_utils.size import Size, Unit - -SECTOR_SIZE = 512 - - -class Filesystem(Enum): - xfs = 0 - ext3 = 1 - ext4 = 2 - - -class PartitionTable(Enum): - msdos = 0 - gpt = 1 - - -class PartitionType(Enum): - efi = 0 - primary = 1 - extended = 2 - logical = 3 - lvm = 4 - msr = 5 - swap = 6 - standard = 7 - unknown = 8 - - -def create_filesystem(device, filesystem: Filesystem, force=True, blocksize=None): - TestRun.LOGGER.info( - f"Creating filesystem ({filesystem.name}) on device: {device.path}") - force_param = ' -f ' if filesystem == Filesystem.xfs else ' -F ' - force_param = force_param if force else '' - block_size_param = f' -b size={blocksize}' if filesystem == Filesystem.xfs \ - else f' -b {blocksize}' - block_size_param = block_size_param if blocksize else '' - cmd = f'mkfs.{filesystem.name} {force_param} {device.path} {block_size_param}' - cmd = re.sub(' +', ' ', cmd) - TestRun.executor.run_expect_success(cmd) - TestRun.LOGGER.info( - f"Successfully created filesystem on device: {device.path}") - - -def create_partition_table(device, partition_table_type: PartitionTable = PartitionTable.gpt): - TestRun.LOGGER.info( - f"Creating partition table ({partition_table_type.name}) for device: {device.path}") - cmd = f'parted --script {device.path} mklabel {partition_table_type.name}' - TestRun.executor.run_expect_success(cmd) - device.partition_table = partition_table_type - TestRun.LOGGER.info( - f"Successfully created {partition_table_type.name} " - f"partition table on device: {device.path}") - - -def get_partition_path(parent_dev, number): - # TODO: change this to be less specific hw dependent (kernel) - if "dev/cas" not in parent_dev: - id_separator = '-part' - else: - id_separator = 'p' # "cas1-1p1" - return f'{parent_dev}{id_separator}{number}' - - -def remove_parition(device, part_number): - TestRun.LOGGER.info(f"Removing part {part_number} from {device.path}") - cmd = f'parted --script {device.path} rm {part_number}' - output = TestRun.executor.run(cmd) - - if output.exit_code != 0: - TestRun.executor.run_expect_success("partprobe") - - -def create_partition( - device, - part_size, - part_number, - part_type: PartitionType = PartitionType.primary, - unit=Unit.MebiByte, - aligned: bool = True): - TestRun.LOGGER.info( - f"Creating {part_type.name} partition on device: {device.path}") - - begin = get_first_partition_offset(device, aligned) - for part in device.partitions: - begin += part.size - if part.type == PartitionType.logical: - begin += Size(1, Unit.MebiByte if not aligned else device.block_size) - - if part_type == PartitionType.logical: - begin += Size(1, Unit.MebiByte if not aligned else device.block_size) - - if part_size != Size.zero(): - end = (begin + part_size) - end_cmd = f'{end.get_value(unit)}{unit_to_string(unit)}' - else: - end_cmd = '100%' - - cmd = f'parted --script {device.path} mkpart ' \ - f'{part_type.name} ' \ - f'{begin.get_value(unit)}{unit_to_string(unit)} ' \ - f'{end_cmd}' - output = TestRun.executor.run(cmd) - - if output.exit_code != 0: - TestRun.executor.run_expect_success("partprobe") - - TestRun.executor.run_expect_success("udevadm settle") - if not check_partition_after_create( - part_size, - part_number, - device.path, - part_type, - aligned): - raise Exception("Could not create partition!") - - if part_type != PartitionType.extended: - from storage_devices.partition import Partition - new_part = Partition(device, - part_type, - part_number, - begin, - end if type(end) is Size else device.size) - dd = Dd().input("/dev/zero") \ - .output(new_part.path) \ - .count(1) \ - .block_size(Size(1, Unit.Blocks4096)) \ - .oflag("direct") - dd.run() - device.partitions.append(new_part) - - TestRun.LOGGER.info(f"Successfully created {part_type.name} partition on {device.path}") - - -def available_disk_size(device): - dev = f"/dev/{device.get_device_id()}" - # get number of device's sectors - disk_sectors = int(TestRun.executor.run(f"fdisk -l {dev} | grep {dev} | grep sectors " - f"| awk '{{print $7 }}' ").stdout) - # get last occupied sector - last_occupied_sector = int(TestRun.executor.run(f"fdisk -l {dev} | grep {dev} " - f"| awk '{{print $3 }}' | tail -1").stdout) - available_disk_sectors = disk_sectors - last_occupied_sector - return Size(available_disk_sectors, Unit(get_block_size(device))) - - -def create_partitions(device, sizes: [], partition_table_type=PartitionTable.gpt): - create_partition_table(device, partition_table_type) - partition_type = PartitionType.primary - partition_number_offset = 0 - msdos_part_max_size = Size(2, Unit.TeraByte) - - for s in sizes: - size = Size( - s.get_value(device.block_size) - device.block_size.value, device.block_size) - if partition_table_type == PartitionTable.msdos and \ - len(sizes) > 4 and len(device.partitions) == 3: - if available_disk_size(device) > msdos_part_max_size: - part_size = msdos_part_max_size - else: - part_size = Size.zero() - create_partition(device, - part_size, - 4, - PartitionType.extended) - partition_type = PartitionType.logical - partition_number_offset = 1 - - partition_number = len(device.partitions) + 1 + partition_number_offset - create_partition(device, - size, - partition_number, - partition_type, - Unit.MebiByte, - True) - - -def get_block_size(device): - try: - block_size = float(TestRun.executor.run( - f"cat {get_sysfs_path(device)}/queue/hw_sector_size").stdout) - except ValueError: - block_size = Unit.Blocks512.value - return block_size - - -def get_size(device): - output = TestRun.executor.run_expect_success(f"cat {get_sysfs_path(device)}/size") - blocks_count = int(output.stdout) - return blocks_count * SECTOR_SIZE - - -def get_sysfs_path(device): - sysfs_path = f"/sys/class/block/{device}" - if TestRun.executor.run(f"test -d {sysfs_path}").exit_code != 0: - sysfs_path = f"/sys/block/{device}" - return sysfs_path - - -def get_pci_address(device): - pci_address = TestRun.executor.run(f"cat /sys/block/{device}/device/address").stdout - return pci_address - - -def check_partition_after_create(size, part_number, parent_dev_path, part_type, aligned): - partition_path = get_partition_path(parent_dev_path, part_number) - if "dev/cas" not in partition_path: - cmd = f"find {partition_path} -type l" - else: - cmd = f"find {partition_path}" - output = TestRun.executor.run_expect_success(cmd).stdout - if partition_path not in output: - TestRun.LOGGER.info( - "Partition created, but could not find it in system, trying 'hdparm -z'") - TestRun.executor.run_expect_success(f"hdparm -z {parent_dev_path}") - output_after_hdparm = TestRun.executor.run_expect_success( - f"parted --script {parent_dev_path} print").stdout - TestRun.LOGGER.info(output_after_hdparm) - - counter = 0 - while partition_path not in output and counter < 10: - time.sleep(2) - output = TestRun.executor.run(cmd).stdout - counter += 1 - - if len(output.split('\n')) > 1 or partition_path not in output: - return False - - if aligned and part_type != PartitionType.extended \ - and size.get_value(Unit.Byte) % Unit.Blocks4096.value != 0: - TestRun.LOGGER.warning( - f"Partition {partition_path} is not 4k aligned: {size.get_value(Unit.KibiByte)}KiB") - - partition_size = get_size(readlink(partition_path).split('/')[-1]) - if part_type == PartitionType.extended or \ - partition_size == size.get_value(Unit.Byte): - return True - - TestRun.LOGGER.warning( - f"Partition size {partition_size} does not match expected {size.get_value(Unit.Byte)} size." - ) - return True - - -def get_first_partition_offset(device, aligned: bool): - if aligned: - return Size(1, Unit.MebiByte) - # 33 sectors are reserved for the backup GPT - return Size(34, Unit(device.blocksize)) \ - if device.partition_table == PartitionTable.gpt else Size(1, device.blocksize) - - -def remove_partitions(device): - from test_utils.os_utils import Udev - if device.is_mounted(): - device.unmount() - - for partition in device.partitions: - unmount(partition) - - TestRun.LOGGER.info(f"Removing partitions from device: {device.path} " - f"({device.get_device_id()}).") - device.wipe_filesystem() - Udev.trigger() - Udev.settle() - output = TestRun.executor.run(f"ls {device.path}* -1") - if len(output.stdout.split('\n')) > 1: - TestRun.LOGGER.error(f"Could not remove partitions from device {device.path}") - return False - return True - - -def mount(device, mount_point, options: [str] = None): - if not fs_utils.check_if_directory_exists(mount_point): - fs_utils.create_directory(mount_point, True) - TestRun.LOGGER.info(f"Mounting device {device.path} ({device.get_device_id()}) " - f"to {mount_point}.") - cmd = f"mount {device.path} {mount_point}" - if options: - cmd = f"{cmd} -o {','.join(options)}" - output = TestRun.executor.run(cmd) - if output.exit_code != 0: - raise Exception(f"Failed to mount {device.path} to {mount_point}") - device.mount_point = mount_point - - -def unmount(device): - TestRun.LOGGER.info(f"Unmounting device {device.path} ({device.get_device_id()}).") - if device.mount_point is not None: - output = TestRun.executor.run(f"umount {device.mount_point}") - if output.exit_code != 0: - TestRun.LOGGER.error("Could not unmount device.") - return False - return True - else: - TestRun.LOGGER.info("Device is not mounted.") - return True - - -def unit_to_string(unit): - unit_string = { - Unit.Byte: 'B', - Unit.Blocks512: 's', - Unit.Blocks4096: 's', - Unit.KibiByte: 'KiB', - Unit.MebiByte: 'MiB', - Unit.GibiByte: 'GiB', - Unit.TebiByte: 'TiB', - Unit.KiloByte: 'kB', - Unit.MegaByte: 'MB', - Unit.GigaByte: 'GB', - Unit.TeraByte: 'TB' - } - return unit_string.get(unit, "Invalid unit.") - - -def wipe_filesystem(device, force=True): - TestRun.LOGGER.info(f"Erasing the device: {device.path}") - force_param = ' -f' if force else '' - cmd = f'wipefs -a{force_param} {device.path}' - TestRun.executor.run_expect_success(cmd) - TestRun.LOGGER.info( - f"Successfully wiped device: {device.path}") - - -def check_if_device_supports_trim(device): - if device.get_device_id().startswith("nvme"): - return True - command_output = TestRun.executor.run( - f'hdparm -I {device.path} | grep "TRIM supported"') - return command_output.exit_code == 0 - - -def get_device_filesystem_type(device_id): - cmd = f'lsblk -l -o NAME,FSTYPE | sort | uniq | grep "{device_id} "' - try: - stdout = TestRun.executor.run_expect_success(cmd).stdout - except CmdException: - # unusual devices might not be listed in output (i.e. RAID containers) - if TestRun.executor.run(f"test -b /dev/{device_id}").exit_code != 0: - raise - else: - return None - split_stdout = stdout.strip().split() - if len(split_stdout) <= 1: - return None - else: - try: - return Filesystem[split_stdout[1]] - except KeyError: - TestRun.LOGGER.warning(f"Unrecognized filesystem: {split_stdout[1]}") - return None - - -def _is_by_id_path(path: str): - """check if given path already is proper by-id path""" - dev_by_id_dir = "/dev/disk/by-id" - by_id_paths = parse_ls_output(ls(dev_by_id_dir), dev_by_id_dir) - return path in [posixpath.join(dev_by_id_dir, id_path.full_path) for id_path in by_id_paths] - - -def _is_dev_path_whitelisted(path: str): - """check if given path is whitelisted""" - whitelisted_paths = [r"cas\d+-\d+", r"/dev/dm-\d+"] - - for whitelisted_path in whitelisted_paths: - if re.search(whitelisted_path, path) is not None: - return True - - return False - - -def validate_dev_path(path: str): - if not posixpath.isabs(path): - raise ValueError(f'Given path "{path}" is not absolute.') - - if _is_dev_path_whitelisted(path): - return path - - if _is_by_id_path(path): - return path - - raise ValueError(f'By-id device link {path} is broken.') diff --git a/test/functional/test-framework/test_tools/drbdadm.py b/test/functional/test-framework/test_tools/drbdadm.py deleted file mode 100644 index 8213e91..0000000 --- a/test/functional/test-framework/test_tools/drbdadm.py +++ /dev/null @@ -1,67 +0,0 @@ -# -# Copyright(c) 2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause-Clear -# - -from core.test_run import TestRun - - -class Drbdadm: - # create metadata for resource - @staticmethod - def create_metadata(resource_name: str, force: bool): - cmd = "drbdadm create-md" + (" --force" if force else "") + f" {resource_name}" - return TestRun.executor.run_expect_success(cmd) - - # enable resource - @staticmethod - def up(resource_name: str): - cmd = f"drbdadm up {resource_name}" - return TestRun.executor.run_expect_success(cmd) - - # disable resource - @staticmethod - def down_all(): - cmd = f"drbdadm down all" - return TestRun.executor.run_expect_success(cmd) - - @staticmethod - def down(resource_name): - cmd = f"drbdadm down {resource_name}" - return TestRun.executor.run_expect_success(cmd) - - # promote resource to primary - @staticmethod - def set_node_primary(resource_name: str, force=False): - cmd = f"drbdadm primary {resource_name}" - cmd += " --force" if force else "" - return TestRun.executor.run_expect_success(cmd) - - # demote resource to secondary - @staticmethod - def set_node_secondary(resource_name: str): - cmd = f"drbdadm secondary {resource_name}" - return TestRun.executor.run_expect_success(cmd) - - # check status for all or for specified resource - @staticmethod - def get_status(resource_name: str = ""): - cmd = f"drbdadm status {resource_name}" - return TestRun.executor.run_expect_success(cmd) - - @staticmethod - def in_sync(resource_name: str): - cmd = f"drbdadm status {resource_name} | grep Inconsistent" - return TestRun.executor.run(cmd).exit_code == 1 - - # wait sync - @staticmethod - def wait_for_sync(resource_name: str = ""): - # ssh connection might timeout in case on long sync - cmd = f"drbdadm wait-sync {resource_name}" - return TestRun.executor.run_expect_success(cmd) - - @staticmethod - def dump_config(resource_name: str): - cmd = f"drbdadm dump {resource_name}" - return TestRun.executor.run(cmd) diff --git a/test/functional/test-framework/test_tools/fio/__init__.py b/test/functional/test-framework/test_tools/fio/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/functional/test-framework/test_tools/fio/fio.py b/test/functional/test-framework/test_tools/fio/fio.py deleted file mode 100644 index 963ae0e..0000000 --- a/test/functional/test-framework/test_tools/fio/fio.py +++ /dev/null @@ -1,105 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import datetime -import uuid - -import test_tools.fio.fio_param -import test_tools.fs_utils -from core.test_run import TestRun -from test_tools import fs_utils -from test_utils import os_utils - - -class Fio: - def __init__(self, executor_obj=None): - self.fio_version = "fio-3.30" - self.default_run_time = datetime.timedelta(hours=1) - self.jobs = [] - self.executor = executor_obj if executor_obj is not None else TestRun.executor - self.base_cmd_parameters: test_tools.fio.fio_param.FioParam = None - self.global_cmd_parameters: test_tools.fio.fio_param.FioParam = None - - def create_command(self, output_type=test_tools.fio.fio_param.FioOutput.json): - self.base_cmd_parameters = test_tools.fio.fio_param.FioParamCmd(self, self.executor) - self.global_cmd_parameters = test_tools.fio.fio_param.FioParamConfig(self, self.executor) - - self.fio_file = f'fio_run_{datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}_{uuid.uuid4().hex}' - self.base_cmd_parameters\ - .set_param('eta', 'always')\ - .set_param('output-format', output_type.value)\ - .set_param('output', self.fio_file) - - self.global_cmd_parameters.set_flags('group_reporting') - - return self.global_cmd_parameters - - def is_installed(self): - return self.executor.run("fio --version").stdout.strip() == self.fio_version - - def install(self): - fio_url = f"http://brick.kernel.dk/snaps/{self.fio_version}.tar.bz2" - fio_package = os_utils.download_file(fio_url) - fs_utils.uncompress_archive(fio_package) - TestRun.executor.run_expect_success(f"cd {fio_package.parent_dir}/{self.fio_version}" - f" && ./configure && make -j && make install") - - def calculate_timeout(self): - if "time_based" not in self.global_cmd_parameters.command_flags: - return self.default_run_time - - total_time = self.global_cmd_parameters.get_parameter_value("runtime") - if len(total_time) != 1: - raise ValueError("Wrong fio 'runtime' parameter configuration") - total_time = int(total_time[0]) - ramp_time = self.global_cmd_parameters.get_parameter_value("ramp_time") - if ramp_time is not None: - if len(ramp_time) != 1: - raise ValueError("Wrong fio 'ramp_time' parameter configuration") - ramp_time = int(ramp_time[0]) - total_time += ramp_time - return datetime.timedelta(seconds=total_time) - - def run(self, timeout: datetime.timedelta = None): - if timeout is None: - timeout = self.calculate_timeout() - - self.prepare_run() - return self.executor.run(str(self), timeout) - - def run_in_background(self): - self.prepare_run() - return self.executor.run_in_background(str(self)) - - def prepare_run(self): - if not self.is_installed(): - self.install() - - if len(self.jobs) > 0: - self.executor.run(f"{str(self)}-showcmd -") - TestRun.LOGGER.info(self.executor.run(f"cat {self.fio_file}").stdout) - TestRun.LOGGER.info(str(self)) - - def execution_cmd_parameters(self): - if len(self.jobs) > 0: - separator = "\n\n" - return f"{str(self.global_cmd_parameters)}\n" \ - f"{separator.join(str(job) for job in self.jobs)}" - else: - return str(self.global_cmd_parameters) - - def __str__(self): - if len(self.jobs) > 0: - command = f"echo '{self.execution_cmd_parameters()}' |" \ - f" {str(self.base_cmd_parameters)} -" - else: - fio_parameters = test_tools.fio.fio_param.FioParamCmd(self, self.executor) - fio_parameters.command_env_var.update(self.base_cmd_parameters.command_env_var) - fio_parameters.command_param.update(self.base_cmd_parameters.command_param) - fio_parameters.command_param.update(self.global_cmd_parameters.command_param) - fio_parameters.command_flags.extend(self.global_cmd_parameters.command_flags) - fio_parameters.set_param('name', 'fio') - command = str(fio_parameters) - return command diff --git a/test/functional/test-framework/test_tools/fio/fio_param.py b/test/functional/test-framework/test_tools/fio/fio_param.py deleted file mode 100644 index 8db911b..0000000 --- a/test/functional/test-framework/test_tools/fio/fio_param.py +++ /dev/null @@ -1,388 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import datetime -import json -import secrets -from enum import Enum -from types import SimpleNamespace as Namespace - -from connection.base_executor import BaseExecutor -from core.test_run import TestRun -from storage_devices.device import Device -from test_tools.fio.fio_result import FioResult -from test_utils.linux_command import LinuxCommand -from test_utils.size import Size - - -class CpusAllowedPolicy(Enum): - shared = 0, - split = 1 - - -class ErrorFilter(Enum): - none = 0, - read = 1, - write = 2, - io = 3, - verify = 4, - all = 5 - - -class FioOutput(Enum): - normal = 'normal' - terse = 'terse' - json = 'json' - jsonplus = 'json+' - - -class IoEngine(Enum): - # Basic read or write I/O. fseek is used to position the I/O location. - sync = 0, - # Linux native asynchronous I/O. - libaio = 1, - # Basic pread or pwrite I/O. - psync = 2, - # Basic readv or writev I/O. - # Will emulate queuing by coalescing adjacent IOs into a single submission. - vsync = 3, - # Basic preadv or pwritev I/O. - pvsync = 4, - # POSIX asynchronous I/O using aio_read and aio_write. - posixaio = 5, - # File is memory mapped with mmap and data copied using memcpy. - mmap = 6, - # RADOS Block Device - rbd = 7, - # SPDK Block Device - spdk_bdev = 8 - - -class ReadWrite(Enum): - randread = 0, - randrw = 1, - randwrite = 2, - read = 3, - readwrite = 4, - write = 5, - trim = 6, - randtrim = 7, - trimwrite = 8 - - -class VerifyMethod(Enum): - # Use an md5 sum of the data area and store it in the header of each block. - md5 = 0, - # Use an experimental crc64 sum of the data area and store it in the header of each block. - crc64 = 1, - # Use optimized sha1 as the checksum function. - sha1 = 2, - # Verify a strict pattern. - # Normally fio includes a header with some basic information and a checksum, but if this - # option is set, only the specific pattern set with verify_pattern is verified. - pattern = 3, - # Write extra information about each I/O (timestamp, block number, etc.). - # The block number is verified. - meta = 4 - - -class RandomGenerator(Enum): - tausworthe = 0, - lfsr = 1, - tausworthe64 = 2 - - -class FioParam(LinuxCommand): - def __init__(self, fio, command_executor: BaseExecutor, command_name): - LinuxCommand.__init__(self, command_executor, command_name) - self.verification_pattern = '' - self.fio = fio - - def get_verification_pattern(self): - if not self.verification_pattern: - self.verification_pattern = f'0x{secrets.token_hex(32)}' - return self.verification_pattern - - def allow_mounted_write(self, value: bool = True): - return self.set_param('allow_mounted_write', int(value)) - - # example: "bs=8k,32k" => 8k for reads, 32k for writes and trims - def block_size(self, *sizes: Size): - return self.set_param('blocksize', *[int(size) for size in sizes]) - - def blocksize_range(self, ranges): - value = [] - for bs_range in ranges: - str_range = str(int(bs_range[0])) + '-' + str(int(bs_range[1])) - value.append(str_range) - return self.set_param('blocksize_range', ",".join(value)) - - def bs_split(self, value): - return self.set_param('bssplit', value) - - def buffer_pattern(self, pattern): - return self.set_param('buffer_pattern', pattern) - - def continue_on_error(self, value: ErrorFilter): - return self.set_param('continue_on_error', value.name) - - def cpus_allowed(self, value): - return self.set_param('cpus_allowed', ",".join(value)) - - def cpus_allowed_policy(self, value: CpusAllowedPolicy): - return self.set_param('cpus_allowed_policy', value.name) - - def direct(self, value: bool = True): - if 'buffered' in self.command_param: - self.remove_param('buffered') - return self.set_param('direct', int(value)) - - def directory(self, directory): - return self.set_param('directory', directory) - - def do_verify(self, value: bool = True): - return self.set_param('do_verify', int(value)) - - def exit_all_on_error(self, value: bool = True): - return self.set_flags('exitall_on_error') if value \ - else self.remove_flag('exitall_on_error') - - def group_reporting(self, value: bool = True): - return self.set_flags('group_reporting') if value else self.remove_flag('group_reporting') - - def file_name(self, path): - return self.set_param('filename', path) - - def file_size(self, size: Size): - return self.set_param('filesize', int(size)) - - def file_size_range(self, ranges): - value = [] - for bs_range in ranges: - str_range = str(int(bs_range[0])) + '-' + str(int(bs_range[1])) - value.append(str_range) - return self.set_param('filesize', ",".join(value)) - - def fsync(self, value: int): - return self.set_param('fsync', value) - - def ignore_errors(self, read_errors, write_errors, verify_errors): - separator = ':' - return self.set_param( - 'ignore_error', - separator.join(str(err) for err in read_errors), - separator.join(str(err) for err in write_errors), - separator.join(str(err) for err in verify_errors)) - - def io_depth(self, value: int): - if value != 1: - if 'ioengine' in self.command_param and \ - self.command_param['ioengine'] == 'sync': - TestRun.LOGGER.warning("Setting iodepth will have no effect with " - "'ioengine=sync' setting") - return self.set_param('iodepth', value) - - def io_engine(self, value: IoEngine): - if value == IoEngine.sync: - if 'iodepth' in self.command_param and self.command_param['iodepth'] != 1: - TestRun.LOGGER.warning("Setting 'ioengine=sync' will cause iodepth setting " - "to be ignored") - return self.set_param('ioengine', value.name) - - def io_size(self, value: Size): - return self.set_param('io_size', int(value.get_value())) - - def loops(self, value: int): - return self.set_param('loops', value) - - def no_random_map(self, value: bool = True): - if 'verify' in self.command_param: - raise ValueError("'NoRandomMap' parameter is mutually exclusive with verify") - if value: - return self.set_flags('norandommap') - else: - return self.remove_flag('norandommap') - - def nr_files(self, value: int): - return self.set_param('nrfiles', value) - - def num_ios(self, value: int): - return self.set_param('number_ios', value) - - def num_jobs(self, value: int): - return self.set_param('numjobs', value) - - def offset(self, value: Size): - return self.set_param('offset', int(value.get_value())) - - def offset_increment(self, value: Size): - return self.set_param('offset_increment', f"{value.value}{value.unit.get_short_name()}") - - def percentage_random(self, value: int): - if value <= 100: - return self.set_param('percentage_random', value) - raise ValueError("Argument out of range. Should be 0-100.") - - def pool(self, value): - return self.set_param('pool', value) - - def ramp_time(self, value: datetime.timedelta): - return self.set_param('ramp_time', int(value.total_seconds())) - - def random_distribution(self, value): - return self.set_param('random_distribution', value) - - def rand_repeat(self, value: int): - return self.set_param('randrepeat', value) - - def rand_seed(self, value: int): - return self.set_param('randseed', value) - - def read_write(self, rw: ReadWrite): - return self.set_param('readwrite', rw.name) - - def run_time(self, value: datetime.timedelta): - if value.total_seconds() == 0: - raise ValueError("Runtime parameter must not be set to 0.") - return self.set_param('runtime', int(value.total_seconds())) - - def serialize_overlap(self, value: bool = True): - return self.set_param('serialize_overlap', int(value)) - - def size(self, value: Size): - return self.set_param('size', int(value.get_value())) - - def stonewall(self, value: bool = True): - return self.set_flags('stonewall') if value else self.remove_param('stonewall') - - def sync(self, value: bool = True): - return self.set_param('sync', int(value)) - - def time_based(self, value: bool = True): - return self.set_flags('time_based') if value else self.remove_flag('time_based') - - def thread(self, value: bool = True): - return self.set_flags('thread') if value else self.remove_param('thread') - - def lat_percentiles(self, value: bool): - return self.set_param('lat_percentiles', int(value)) - - def scramble_buffers(self, value: bool): - return self.set_param('scramble_buffers', int(value)) - - def slat_percentiles(self, value: bool): - return self.set_param('slat_percentiles', int(value)) - - def spdk_core_mask(self, value: str): - return self.set_param('spdk_core_mask', value) - - def spdk_json_conf(self, path): - return self.set_param('spdk_json_conf', path) - - def clat_percentiles(self, value: bool): - return self.set_param('clat_percentiles', int(value)) - - def percentile_list(self, value: []): - val = ':'.join(value) if len(value) > 0 else '100' - return self.set_param('percentile_list', val) - - def verification_with_pattern(self, pattern=None): - if pattern is not None and pattern != '': - self.verification_pattern = pattern - return self.verify(VerifyMethod.pattern) \ - .set_param('verify_pattern', self.get_verification_pattern()) \ - .do_verify() - - def verify(self, value: VerifyMethod): - return self.set_param('verify', value.name) - - def create_only(self, value: bool = False): - return self.set_param('create_only', int(value)) - - def verify_pattern(self, pattern=None): - return self.set_param('verify_pattern', pattern or self.get_verification_pattern()) - - def verify_backlog(self, value: int): - return self.set_param('verify_backlog', value) - - def verify_dump(self, value: bool = True): - return self.set_param('verify_dump', int(value)) - - def verify_fatal(self, value: bool = True): - return self.set_param('verify_fatal', int(value)) - - def verify_only(self, value: bool = True): - return self.set_flags('verify_only') if value else self.remove_param('verify_only') - - def write_hint(self, value: str): - return self.set_param('write_hint', value) - - def write_percentage(self, value: int): - if value <= 100: - return self.set_param('rwmixwrite', value) - raise ValueError("Argument out of range. Should be 0-100.") - - def random_generator(self, value: RandomGenerator): - return self.set_param('random_generator', value.name) - - def target(self, target): - if isinstance(target, Device): - return self.file_name(target.path) - return self.file_name(target) - - def add_job(self, job_name=None): - if not job_name: - job_name = f'job{len(self.fio.jobs)}' - new_job = FioParamConfig(self.fio, self.command_executor, f'[{job_name}]') - self.fio.jobs.append(new_job) - return new_job - - def clear_jobs(self): - self.fio.jobs = [] - - return self - - def edit_global(self): - return self.fio.global_cmd_parameters - - def run(self, fio_timeout: datetime.timedelta = None): - if "per_job_logs" in self.fio.global_cmd_parameters.command_param: - self.fio.global_cmd_parameters.set_param("per_job_logs", '0') - fio_output = self.fio.run(fio_timeout) - if fio_output.exit_code != 0: - raise Exception(f"Exception occurred while trying to execute fio, exit_code:" - f"{fio_output.exit_code}.\n" - f"stdout: {fio_output.stdout}\nstderr: {fio_output.stderr}") - TestRun.executor.run(f"sed -i '/^[[:alnum:]]/d' {self.fio.fio_file}") # Remove warnings - out = self.command_executor.run_expect_success(f"cat {self.fio.fio_file}").stdout - return self.get_results(out) - - def run_in_background(self): - if "per_job_logs" in self.fio.global_cmd_parameters.command_param: - self.fio.global_cmd_parameters.set_param("per_job_logs", '0') - return self.fio.run_in_background() - - @staticmethod - def get_results(result): - data = json.loads(result, object_hook=lambda d: Namespace(**d)) - jobs_list = [] - if hasattr(data, 'jobs'): - jobs = data.jobs - for job in jobs: - job_result = FioResult(data, job) - jobs_list.append(job_result) - return jobs_list - - -class FioParamCmd(FioParam): - def __init__(self, fio, command_executor: BaseExecutor, command_name='fio'): - FioParam.__init__(self, fio, command_executor, command_name) - self.param_name_prefix = "--" - - -class FioParamConfig(FioParam): - def __init__(self, fio, command_executor: BaseExecutor, command_name='[global]'): - FioParam.__init__(self, fio, command_executor, command_name) - self.param_name_prefix = "\n" diff --git a/test/functional/test-framework/test_tools/fio/fio_patterns.py b/test/functional/test-framework/test_tools/fio/fio_patterns.py deleted file mode 100644 index 22ca70b..0000000 --- a/test/functional/test-framework/test_tools/fio/fio_patterns.py +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import secrets -from aenum import Enum - - -class Pattern(Enum): - cyclic = "0x00336699ccffcc996633" - sequential = "0x" + "".join([f"{i:02x}" for i in range(0, 256)]) - high = "0xaa" - low = "0x84210" - zeroes = "0x00" - ones = "0xff" - bin_1 = high - bin_2 = "0x55" - random = "0x" + secrets.token_hex() diff --git a/test/functional/test-framework/test_tools/fio/fio_result.py b/test/functional/test-framework/test_tools/fio/fio_result.py deleted file mode 100644 index 334e977..0000000 --- a/test/functional/test-framework/test_tools/fio/fio_result.py +++ /dev/null @@ -1,164 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -from test_utils.size import Size, Unit, UnitPerSecond -from test_utils.time import Time - - -class FioResult: - def __init__(self, result, job): - self.result = result - self.job = job - - def __str__(self): - result_dict = { - "Total read I/O": self.read_io(), - "Total read bandwidth ": self.read_bandwidth(), - "Read bandwidth average ": self.read_bandwidth_average(), - "Read bandwidth deviation ": self.read_bandwidth_deviation(), - "Read IOPS": self.read_iops(), - "Read runtime": self.read_runtime(), - "Read average completion latency": self.read_completion_latency_average(), - "Total write I/O": self.write_io(), - "Total write bandwidth ": self.write_bandwidth(), - "Write bandwidth average ": self.write_bandwidth_average(), - "Write bandwidth deviation ": self.write_bandwidth_deviation(), - "Write IOPS": self.write_iops(), - "Write runtime": self.write_runtime(), - "Write average completion latency": self.write_completion_latency_average(), - } - - disks_name = self.disks_name() - if disks_name: - result_dict.update({"Disk name": ",".join(disks_name)}) - - result_dict.update({"Total number of errors": self.total_errors()}) - - s = "" - for key in result_dict.keys(): - s += f"{key}: {result_dict[key]}\n" - return s - - def total_errors(self): - return getattr(self.job, "total_err", 0) - - def disks_name(self): - disks_name = [] - if hasattr(self.result, "disk_util"): - for disk in self.result.disk_util: - disks_name.append(disk.name) - return disks_name - - def read_io(self): - return Size(self.job.read.io_kbytes, Unit.KibiByte) - - def read_bandwidth(self): - return Size(self.job.read.bw, UnitPerSecond(Unit.KibiByte)) - - def read_bandwidth_average(self): - return Size(self.job.read.bw_mean, UnitPerSecond(Unit.KibiByte)) - - def read_bandwidth_deviation(self): - return Size(self.job.read.bw_dev, UnitPerSecond(Unit.KibiByte)) - - def read_iops(self): - return self.job.read.iops - - def read_runtime(self): - return Time(microseconds=self.job.read.runtime) - - def read_completion_latency_min(self): - return Time(nanoseconds=self.job.read.lat_ns.min) - - def read_completion_latency_max(self): - return Time(nanoseconds=self.job.read.lat_ns.max) - - def read_completion_latency_average(self): - return Time(nanoseconds=self.job.read.lat_ns.mean) - - def read_completion_latency_percentile(self): - return self.job.read.lat_ns.percentile.__dict__ - - def read_requests_number(self): - return self.result.disk_util[0].read_ios - - def write_io(self): - return Size(self.job.write.io_kbytes, Unit.KibiByte) - - def write_bandwidth(self): - return Size(self.job.write.bw, UnitPerSecond(Unit.KibiByte)) - - def write_bandwidth_average(self): - return Size(self.job.write.bw_mean, UnitPerSecond(Unit.KibiByte)) - - def write_bandwidth_deviation(self): - return Size(self.job.write.bw_dev, UnitPerSecond(Unit.KibiByte)) - - def write_iops(self): - return self.job.write.iops - - def write_runtime(self): - return Time(microseconds=self.job.write.runtime) - - def write_completion_latency_average(self): - return Time(nanoseconds=self.job.write.lat_ns.mean) - - def write_completion_latency_min(self): - return Time(nanoseconds=self.job.write.lat_ns.min) - - def write_completion_latency_max(self): - return Time(nanoseconds=self.job.write.lat_ns.max) - - def write_completion_latency_average(self): - return Time(nanoseconds=self.job.write.lat_ns.mean) - - def write_completion_latency_percentile(self): - return self.job.write.lat_ns.percentile.__dict__ - - def write_requests_number(self): - return self.result.disk_util[0].write_ios - - def trim_io(self): - return Size(self.job.trim.io_kbytes, Unit.KibiByte) - - def trim_bandwidth(self): - return Size(self.job.trim.bw, UnitPerSecond(Unit.KibiByte)) - - def trim_bandwidth_average(self): - return Size(self.job.trim.bw_mean, UnitPerSecond(Unit.KibiByte)) - - def trim_bandwidth_deviation(self): - return Size(self.job.trim.bw_dev, UnitPerSecond(Unit.KibiByte)) - - def trim_iops(self): - return self.job.trim.iops - - def trim_runtime(self): - return Time(microseconds=self.job.trim.runtime) - - def trim_completion_latency_average(self): - return Time(nanoseconds=self.job.trim.lat_ns.mean) - - def trim_completion_latency_min(self): - return Time(nanoseconds=self.job.trim.lat_ns.min) - - def trim_completion_latency_max(self): - return Time(nanoseconds=self.job.trim.lat_ns.max) - - def trim_completion_latency_average(self): - return Time(nanoseconds=self.job.trim.lat_ns.mean) - - def trim_completion_latency_percentile(self): - return self.job.trim.lat_ns.percentile.__dict__ - - @staticmethod - def result_list_to_dict(results): - result_dict = {} - - for result in results: - result_dict[result.job.jobname] = result.job - - return result_dict diff --git a/test/functional/test-framework/test_tools/fs_utils.py b/test/functional/test-framework/test_tools/fs_utils.py deleted file mode 100644 index 512bacc..0000000 --- a/test/functional/test-framework/test_tools/fs_utils.py +++ /dev/null @@ -1,378 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -import base64 -import math -import textwrap - -from aenum import IntFlag, Enum -from collections import namedtuple -from datetime import datetime - -from core.test_run import TestRun -from test_tools.dd import Dd -from test_utils.size import Size, Unit - - -class Permissions(IntFlag): - r = 4 - w = 2 - x = 1 - - def __str__(self): - ret_string = "" - for p in Permissions: - if p in self: - ret_string += p.name - return ret_string - - -class PermissionsUsers(IntFlag): - u = 4 - g = 2 - o = 1 - - def __str__(self): - ret_string = "" - for p in PermissionsUsers: - if p in self: - ret_string += p.name - return ret_string - - -class PermissionSign(Enum): - add = '+' - remove = '-' - set = '=' - - -class FilesPermissions(): - perms_exceptions = {} - - def __init__(self, files_list: list): - self.files_list = files_list - - def add_exceptions(self, perms: dict): - self.perms_exceptions.update(perms) - - def check(self, file_perm: int = 644, dir_perm: int = 755): - failed_perms = [] - FailedPerm = namedtuple("FailedPerm", ["file", "current_perm", "expected_perm"]) - - for file in self.files_list: - perm = get_permissions(file) - - if file in self.perms_exceptions: - if perm != self.perms_exceptions[file]: - failed_perms.append(FailedPerm(file, perm, self.perms_exceptions[file])) - continue - - if check_if_regular_file_exists(file): - if perm != file_perm: - failed_perms.append(FailedPerm(file, perm, file_perm)) - elif check_if_directory_exists(file): - if perm != dir_perm: - failed_perms.append(FailedPerm(file, perm, dir_perm)) - else: - raise Exception(f"{file}: File type not recognized.") - - return failed_perms - - -def create_directory(path, parents: bool = False): - cmd = f"mkdir {'--parents ' if parents else ''}\"{path}\"" - return TestRun.executor.run_expect_success(cmd) - - -def check_if_directory_exists(path): - return TestRun.executor.run(f"test -d \"{path}\"").exit_code == 0 - - -def check_if_file_exists(path): - return TestRun.executor.run(f"test -e \"{path}\"").exit_code == 0 - - -def check_if_regular_file_exists(path): - return TestRun.executor.run(f"test -f \"{path}\"").exit_code == 0 - - -def check_if_symlink_exists(path): - return TestRun.executor.run(f"test -L \"{path}\"").exit_code == 0 - - -def copy(source: str, - destination: str, - force: bool = False, - recursive: bool = False, - dereference: bool = False): - cmd = f"cp{' --force' if force else ''}" \ - f"{' --recursive' if recursive else ''}" \ - f"{' --dereference' if dereference else ''} " \ - f"\"{source}\" \"{destination}\"" - return TestRun.executor.run_expect_success(cmd) - - -def move(source, destination, force: bool = False): - cmd = f"mv{' --force' if force else ''} \"{source}\" \"{destination}\"" - return TestRun.executor.run_expect_success(cmd) - - -def remove(path, force: bool = False, recursive: bool = False, ignore_errors: bool = False): - cmd = f"rm{' --force' if force else ''}{' --recursive' if recursive else ''} \"{path}\"" - output = TestRun.executor.run(cmd) - if output.exit_code != 0 and not ignore_errors: - raise Exception(f"Could not remove file {path}." - f"\nstdout: {output.stdout}\nstderr: {output.stderr}") - return output - - -def get_permissions(path, dereference: bool = True): - cmd = f"stat --format='%a' {'--dereference' if dereference else ''} \"{path}\"" - return int(TestRun.executor.run_expect_success(cmd).stdout) - - -def chmod(path, permissions: Permissions, users: PermissionsUsers, - sign: PermissionSign = PermissionSign.set, recursive: bool = False): - cmd = f"chmod{' --recursive' if recursive else ''} " \ - f"{str(users)}{sign.value}{str(permissions)} \"{path}\"" - output = TestRun.executor.run(cmd) - return output - - -def chmod_numerical(path, permissions: int, recursive: bool = False): - cmd = f"chmod{' --recursive' if recursive else ''} {permissions} \"{path}\"" - return TestRun.executor.run_expect_success(cmd) - - -def chown(path, owner, group, recursive): - cmd = f"chown {'--recursive ' if recursive else ''}{owner}:{group} \"{path}\"" - return TestRun.executor.run_expect_success(cmd) - - -def create_file(path): - if not path.strip(): - raise ValueError("Path cannot be empty or whitespaces.") - cmd = f"touch \"{path}\"" - return TestRun.executor.run_expect_success(cmd) - - -def compare(file, other_file): - output = TestRun.executor.run( - f"cmp --silent \"{file}\" \"{other_file}\"") - if output.exit_code == 0: - return True - elif output.exit_code > 1: - raise Exception(f"Compare command execution failed. {output.stdout}\n{output.stderr}") - else: - return False - - -def diff(file, other_file): - output = TestRun.executor.run( - f"diff \"{file}\" \"{other_file}\"") - if output.exit_code == 0: - return None - elif output.exit_code > 1: - raise Exception(f"Diff command execution failed. {output.stdout}\n{output.stderr}") - else: - return output.stderr - - -# For some reason separators other than '/' don't work when using sed on system paths -# This requires escaping '/' in pattern and target string -def escape_sed_string(string: str, sed_replace: bool = False): - string = string.replace("'", r"\x27").replace("/", r"\/") - # '&' has special meaning in sed replace and needs to be escaped - if sed_replace: - string = string.replace("&", r"\&") - return string - - -def insert_line_before_pattern(file, pattern, new_line): - pattern = escape_sed_string(pattern) - new_line = escape_sed_string(new_line) - cmd = f"sed -i '/{pattern}/i {new_line}' \"{file}\"" - return TestRun.executor.run_expect_success(cmd) - - -def replace_first_pattern_occurrence(file, pattern, new_string): - pattern = escape_sed_string(pattern) - new_string = escape_sed_string(new_string, sed_replace=True) - cmd = f"sed -i '0,/{pattern}/s//{new_string}/' \"{file}\"" - return TestRun.executor.run_expect_success(cmd) - - -def replace_in_lines(file, pattern, new_string, regexp=False): - pattern = escape_sed_string(pattern) - new_string = escape_sed_string(new_string, sed_replace=True) - cmd = f"sed -i{' -r' if regexp else ''} 's/{pattern}/{new_string}/g' \"{file}\"" - return TestRun.executor.run_expect_success(cmd) - - -def append_line(file, string): - cmd = f"echo '{string}' >> \"{file}\"" - return TestRun.executor.run_expect_success(cmd) - - -def remove_lines(file, pattern, regexp=False): - pattern = escape_sed_string(pattern) - cmd = f"sed -i{' -r' if regexp else ''} '/{pattern}/d' \"{file}\"" - return TestRun.executor.run_expect_success(cmd) - - -def read_file(file): - if not file.strip(): - raise ValueError("File path cannot be empty or whitespace.") - output = TestRun.executor.run_expect_success(f"cat \"{file}\"") - return output.stdout - - -def write_file(file, content, overwrite: bool = True, unix_line_end: bool = True): - if not file.strip(): - raise ValueError("File path cannot be empty or whitespace.") - if not content: - raise ValueError("Content cannot be empty.") - if unix_line_end: - content.replace('\r', '') - content += '\n' - max_length = 60000 - split_content = textwrap.TextWrapper(width=max_length, replace_whitespace=False).wrap(content) - split_content[-1] += '\n' - for s in split_content: - redirection_char = '>' if overwrite else '>>' - overwrite = False - encoded_content = base64.b64encode(s.encode("utf-8")) - cmd = f"printf '{encoded_content.decode('utf-8')}' " \ - f"| base64 --decode {redirection_char} \"{file}\"" - TestRun.executor.run_expect_success(cmd) - - -def uncompress_archive(file, destination=None): - from test_utils.filesystem.file import File - - if not isinstance(file, File): - file = File(file) - if not destination: - destination = file.parent_dir - command = (f"unzip -u {file.full_path} -d {destination}" - if str(file).endswith(".zip") - else f"tar --extract --file={file.full_path} --directory={destination}") - TestRun.executor.run_expect_success(command) - - -def ls(path, options=''): - default_options = "-lA --time-style=+'%Y-%m-%d %H:%M:%S'" - output = TestRun.executor.run( - f"ls {default_options} {options} \"{path}\"") - return output.stdout - - -def ls_item(path): - output = ls(path, '-d') - return output.splitlines()[0] if output else None - - -def parse_ls_output(ls_output, dir_path=''): - split_output = ls_output.split('\n') - fs_items = [] - for line in split_output: - if not line.strip(): - continue - line_fields = line.split() - if len(line_fields) < 8: - continue - file_type = line[0] - if file_type not in ['-', 'd', 'l', 'b', 'c', 'p', 's']: - continue - permissions = line_fields[0][1:].replace('.', '') - owner = line_fields[2] - group = line_fields[3] - has_size = ',' not in line_fields[4] - if has_size: - size = Size(float(line_fields[4]), Unit.Byte) - else: - size = None - line_fields.pop(4) - split_date = line_fields[5].split('-') - split_time = line_fields[6].split(':') - modification_time = datetime(int(split_date[0]), int(split_date[1]), int(split_date[2]), - int(split_time[0]), int(split_time[1]), int(split_time[2])) - if dir_path and file_type != 'l': - full_path = '/'.join([dir_path, line_fields[7]]) - else: - full_path = line_fields[7] - - from test_utils.filesystem.file import File, FsItem - from test_utils.filesystem.directory import Directory - from test_utils.filesystem.symlink import Symlink - - if file_type == '-': - fs_item = File(full_path) - elif file_type == 'd': - fs_item = Directory(full_path) - elif file_type == 'l': - fs_item = Symlink(full_path) - else: - fs_item = FsItem(full_path) - - fs_item.permissions.user = Permissions['|'.join(list(permissions[:3].replace('-', '')))] \ - if permissions[:3] != '---' else Permissions(0) - fs_item.permissions.group = Permissions['|'.join(list(permissions[3:6].replace('-', '')))] \ - if permissions[3:6] != '---' else Permissions(0) - fs_item.permissions.other = Permissions['|'.join(list(permissions[6:].replace('-', '')))] \ - if permissions[6:] != '---' else Permissions(0) - - fs_item.owner = owner - fs_item.group = group - fs_item.size = size - fs_item.modification_time = modification_time - fs_items.append(fs_item) - return fs_items - - -def find_all_files(path: str, recursive: bool = True): - if not path.strip(): - raise ValueError("No path given.") - - output = TestRun.executor.run_expect_success(f"find \"{path}\" {'-maxdepth 1' if not recursive else ''} \( -type f -o -type l \) -print") - - return output.stdout.splitlines() - - -def find_all_dirs(path: str, recursive: bool = True): - if not path.strip(): - raise ValueError("No path given.") - - output = TestRun.executor.run_expect_success(f"find \"{path}\" {'-maxdepth 1' if not recursive else ''} -type d -print") - - return output.stdout.splitlines() - - -def find_all_items(path: str, recursive: bool = True): - return [*find_all_files(path, recursive), *find_all_dirs(path, recursive)] - - -def readlink(link: str, options="--canonicalize-existing"): - return TestRun.executor.run_expect_success( - f"readlink {options} \"{link}\"" - ).stdout - - -def create_random_test_file(target_file_path: str, - file_size: Size = Size(1, Unit.MebiByte), - random: bool = True): - from test_utils.filesystem.file import File - bs = Size(512, Unit.KibiByte) - cnt = math.ceil(file_size.value / bs.value) - file = File.create_file(target_file_path) - dd = Dd().output(target_file_path) \ - .input("/dev/urandom" if random else "/dev/zero") \ - .block_size(bs) \ - .count(cnt) \ - .oflag("direct") - dd.run() - file.refresh_item() - return file diff --git a/test/functional/test-framework/test_tools/iostat.py b/test/functional/test-framework/test_tools/iostat.py deleted file mode 100644 index 3e310bf..0000000 --- a/test/functional/test-framework/test_tools/iostat.py +++ /dev/null @@ -1,179 +0,0 @@ -# -# Copyright(c) 2020-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from core.test_run import TestRun -from storage_devices.device import Device -from test_utils.size import Size, Unit, UnitPerSecond -from test_utils.time import Time -import csv - - -class IOstatExtended: - iostat_option = "x" - - def __init__(self, device_statistics: dict): - - # Notes about params: - # await param is displayed only on flag -s - # avgrq-sz doesn't appear in newer versions of iostat -x - - self.device_name = device_statistics["Device"] - # rrqm/s - self.read_requests_merged_per_sec = float(device_statistics["rrqm/s"]) - # wrqm/s - self.write_requests_merged_per_sec = float(device_statistics["wrqm/s"]) - # r/s - self.read_requests_per_sec = float(device_statistics["r/s"]) - # w/s - self.write_requests_per_sec = float(device_statistics["w/s"]) - # rkB/s - self.reads_per_sec = Size(float(device_statistics["rkB/s"]), UnitPerSecond(Unit.KiloByte)) - # wkB/s - self.writes_per_sec = Size(float(device_statistics["wkB/s"]), UnitPerSecond(Unit.KiloByte)) - # avgqu-sz - in newer versions is named aqu-sz - self.average_queue_length = float( - device_statistics["aqu-sz"] - if "aqu-sz" in device_statistics - else device_statistics.get("avgqu-sz", 0) - ) - # r_await - self.read_average_service_time = Time(milliseconds=float(device_statistics["r_await"])) - # w_await - self.write_average_service_time = Time(milliseconds=float(device_statistics["w_await"])) - # iostat's documentation says to not trust 11th field - # util - self.utilization = float(device_statistics["%util"]) - - def __str__(self): - return ( - f"\n=========={self.device_name} IO stats: ==========\n" - f"Read requests merged per second: {self.read_requests_merged_per_sec}\n" - f"Write requests merged per second: {self.write_requests_merged_per_sec}\n" - f"Read requests: {self.read_requests_per_sec}\n" - f"Write requests: {self.write_requests_per_sec}\n" - f"Reads per second: {self.reads_per_sec}\n" - f"Writes per second {self.writes_per_sec}\n" - f"Average queue length {self.average_queue_length}\n" - f"Read average service time {self.read_average_service_time}\n" - f"Write average service time: {self.write_average_service_time}\n" - f"Utilization: {self.utilization}\n" - f"=================================================\n" - ) - - def __repr__(self): - return str(self) - - def __eq__(self, other): - if not other: - return False - return ( - self.read_requests_merged_per_sec == other.read_requests_merged_per_sec - and self.write_requests_merged_per_sec == other.write_requests_merged_per_sec - and self.read_requests_per_sec == other.read_requests_per_sec - and self.write_requests_per_sec == other.write_requests_per_sec - and self.reads_per_sec == other.reads_per_sec - and self.writes_per_sec == other.writes_per_sec - and self.average_queue_length == other.average_queue_length - and self.read_average_service_time == other.read_average_service_time - and self.write_average_service_time == other.write_average_service_time - and self.utilization == other.utilization - ) - - @classmethod - def get_iostat_list( - cls, - devices_list: [Device], - since_boot: bool = True, - interval: int = 1, - ): - """ - Returns list of IOstat objects containing extended statistics displayed - in kibibytes/kibibytes per second. - """ - return _get_iostat_list(cls, devices_list, since_boot, interval) - - -class IOstatBasic: - iostat_option = "d" - - def __init__(self, device_statistics): - - self.device_name = device_statistics["Device"] - # tps - self.transfers_per_second = float(device_statistics["tps"]) - # kB_read/s - self.reads_per_second = Size( - float(device_statistics["kB_read/s"]), UnitPerSecond(Unit.KiloByte) - ) - # kB_wrtn/s - self.writes_per_second = Size( - float(device_statistics["kB_wrtn/s"]), UnitPerSecond(Unit.KiloByte) - ) - # kB_read - self.total_reads = Size(float(device_statistics["kB_read"]), Unit.KibiByte) - # kB_wrtn - self.total_writes = Size(float(device_statistics["kB_wrtn"]), Unit.KibiByte) - - def __str__(self): - return ( - f"\n=========={self.device_name} IO stats: ==========\n" - f"Transfers per second: {self.transfers_per_second}\n" - f"Kilobytes read per second: {self.reads_per_second}\n" - f"Kilobytes written per second: {self.writes_per_second}\n" - f"Kilobytes read: {self.total_reads}\n" - f"Kilobytes written: {self.total_writes}\n" - f"=================================================\n" - ) - - def __repr__(self): - return str(self) - - def __eq__(self, other): - if not isinstance(other, IOstatBasic): - return False - return vars(self) == vars(other) - - @classmethod - def get_iostat_list( - cls, - devices_list: [Device], - since_boot: bool = True, - interval: int = 1, - ): - """ - Returns list of IOstat objects containing basic statistics displayed - in kibibytes/kibibytes per second. - """ - return _get_iostat_list(cls, devices_list, since_boot, interval) - - -def _get_iostat_list( - class_type: type, - devices_list: [Device], - since_boot: bool, - interval: int, -): - if interval < 1: - raise ValueError("iostat interval must be positive!") - - iostat_cmd = f"iostat -k -{class_type.iostat_option} " - - if not since_boot: - iostat_cmd += f"-y {interval} 1 " - - iostat_cmd += " ".join([name.get_device_id() for name in devices_list]) - - sed_cmd = "sed -n '/^$/d;s/\s\+/,/g;/^Device/,$p'" - - cmd = f"{iostat_cmd} | {sed_cmd}" - - lines = TestRun.executor.run(cmd).stdout.splitlines() - table_contents = csv.DictReader(lines, delimiter=",") - - ret = [] - for device in table_contents: - ret += [class_type(device)] - - return ret diff --git a/test/functional/test-framework/test_tools/kedr.py b/test/functional/test-framework/test_tools/kedr.py deleted file mode 100644 index cceb43f..0000000 --- a/test/functional/test-framework/test_tools/kedr.py +++ /dev/null @@ -1,129 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import wget -import os -from enum import Enum - -from core.test_run import TestRun -from test_tools import fs_utils -from test_utils.os_utils import DEBUGFS_MOUNT_POINT - - -KEDR_0_6_URL = "https://github.com/euspectre/kedr/archive/v0.6.tar.gz" -BUILD_DIR = "build" -LEAKS_LOGS_PATH = f"{DEBUGFS_MOUNT_POINT}/kedr_leak_check" -KMALLOC_FAULT_SIMULATION_PATH = "/sys/kernel/debug/kedr_fault_simulation" - - -class KedrProfile(Enum): - MEM_LEAK_CHECK = "leak_check.conf" - FAULT_SIM = "fsim.conf" - - -class Kedr: - @staticmethod - def is_installed(): - return "KEDR version" in TestRun.executor.run("kedr --version").stdout.strip() - - @classmethod - def install(cls): - if cls.is_installed(): - TestRun.LOGGER.info("Kedr is already installed!") - return - - # TODO check if cmake is installed before - # TODO consider using os_utils.download_file() - kedr_archive = wget.download(KEDR_0_6_URL) - - TestRun.executor.rsync_to( - f"\"{kedr_archive}\"", - f"{TestRun.config['working_dir']}") - - kedr_dir = TestRun.executor.run_expect_success( - f"cd {TestRun.config['working_dir']} && " - f"tar -ztf \"{kedr_archive}\" | sed -e 's@/.*@@' | uniq" - ).stdout - - TestRun.executor.run_expect_success( - f"cd {TestRun.config['working_dir']} && " - f"tar -xf \"{kedr_archive}\" && " - f"cd {kedr_dir} && " - f"mkdir -p {BUILD_DIR} && " - f"cd {BUILD_DIR} && " - f"cmake ../sources/ && " - f"make && " - f"make install" - ) - - os.remove(kedr_archive) - TestRun.LOGGER.info("Kedr installed succesfully") - - @classmethod - def is_loaded(cls): - if not cls.is_installed(): - raise Exception("Kedr is not installed!") - - if "KEDR status: loaded" in TestRun.executor.run_expect_success("kedr status").stdout: - return True - else: - return False - - @classmethod - def start(cls, module, profile: KedrProfile = KedrProfile.MEM_LEAK_CHECK): - if not cls.is_installed(): - raise Exception("Kedr is not installed!") - - TestRun.LOGGER.info(f"Starting kedr with {profile} profile") - start_cmd = f"kedr start {module} -f {profile.value}" - TestRun.executor.run_expect_success(start_cmd) - - # TODO extend to scenarios other than kmalloc - def setup_fault_injections(condition: str = "1"): - TestRun.executor.run_expect_success( - f'echo "kmalloc" > {KMALLOC_FAULT_SIMULATION_PATH}/points/kmalloc/current_indicator') - TestRun.executor.run_expect_success( - f'echo "{condition}" > {KMALLOC_FAULT_SIMULATION_PATH}/points/kmalloc/expression') - - @classmethod - def fsim_show_last_fault(cls): - if not cls.is_installed(): - raise Exception("Kedr is not installed!") - - if not cls.is_loaded(): - raise Exception("Kedr is not loaded!") - - return fs_utils.read_file(f"{KMALLOC_FAULT_SIMULATION_PATH}/last_fault") - - @classmethod - def stop(cls): - if not cls.is_installed(): - raise Exception("Kedr is not installed!") - - TestRun.executor.run_expect_success("kedr stop") - - @classmethod - def check_for_mem_leaks(cls, module): - if not cls.is_installed(): - raise Exception("Kedr is not installed!") - - if not cls.is_loaded(): - raise Exception("Kedr is not loaded!") - - if fs_utils.check_if_directory_exists(f"{LEAKS_LOGS_PATH}/{module}"): - logs_path = f"{LEAKS_LOGS_PATH}/{module}" - elif fs_utils.check_if_directory_exists(f"{DEBUGFS_MOUNT_POINT}"): - logs_path = f"{LEAKS_LOGS_PATH}" - else: - raise Exception("Couldn't find kedr logs dir!") - - leaks = fs_utils.read_file(f"{logs_path}/possible_leaks") - frees = fs_utils.read_file(f"{logs_path}/unallocated_frees") - summary = fs_utils.read_file(f"{logs_path}/info") - if leaks or frees: - raise Exception("Memory leaks found!\n" - f"Kedr summary: {summary}\n" - f"Possible memory leaks: {leaks}\n" - f"Unallocated frees: {frees}\n") diff --git a/test/functional/test-framework/test_tools/mdadm.py b/test/functional/test-framework/test_tools/mdadm.py deleted file mode 100644 index ae962bb..0000000 --- a/test/functional/test-framework/test_tools/mdadm.py +++ /dev/null @@ -1,140 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -import re - -from core.test_run import TestRun -from test_utils.size import Unit - - -class Mdadm: - @staticmethod - def assemble(device_paths: str = None): - cmd = f"mdadm --assemble " + (device_paths if device_paths else "--scan") - return TestRun.executor.run(cmd) - - @staticmethod - def create(conf, device_paths: str): - if not conf.name: - raise ValueError("Name needed for RAID creation.") - if not device_paths: - raise ValueError("Device paths needed for RAID creation.") - - cmd = f"mdadm --create --run /dev/md/{conf.name} " - if conf.metadata.value != "legacy": - cmd += f"--metadata={conf.metadata.value} " - if conf.level is not None: - cmd += f"--level={conf.level.value} " - if conf.number_of_devices: - cmd += f"--raid-devices={conf.number_of_devices} " - if conf.strip_size: - cmd += f"--chunk={conf.strip_size} " - if conf.size: - cmd += f"--size={int(conf.size.get_value(Unit.KibiByte))} " - cmd += device_paths - return TestRun.executor.run_expect_success(cmd) - - @staticmethod - def detail(raid_device_paths: str): - if not raid_device_paths: - raise ValueError("Provide paths of RAID devices to show details for.") - cmd = f"mdadm --detail {raid_device_paths} --prefer=by-id" - return TestRun.executor.run_expect_success(cmd) - - @classmethod - def detail_result(cls, raid_device_paths: str): - output = cls.detail(raid_device_paths) - details = {} - for device_details in re.split("^/dev/", output.stdout, flags=re.MULTILINE): - if not device_details: - continue - lines = device_details.splitlines() - key = "/dev/" + lines[0].rstrip(':') - details[key] = {} - details[key]["path"] = key - details[key]["devices"] = cls.__parse_devices(device_details) - details[key]["level"] = cls.__parse_level(device_details) - details[key]["uuid"] = cls.__parse_uuid(device_details) - metadata = cls.__parse_metadata(device_details) - if metadata: - details[key]["metadata"] = metadata - - return details - - @staticmethod - def examine(brief: bool = True, device_paths: str = None): - cmd = f"mdadm --examine " - if brief: - cmd += "--brief " - cmd += (device_paths if device_paths else "--scan") - return TestRun.executor.run_expect_success(cmd) - - @classmethod - def examine_result(cls, device_paths: str = None): - output = cls.examine(device_paths=device_paths) - raids = [] - - uuid_path_prefix = "/dev/disk/by-id/md-uuid-" - # sometimes links for RAIDs are not properly created, force udev to create them - TestRun.executor.run("udevadm trigger && udevadm settle") - - for line in output.stdout.splitlines(): - split_line = line.split() - try: - uuid = [i for i in split_line if i.startswith("UUID=")][0].split("=")[-1] - except IndexError: - continue - raid_link = uuid_path_prefix + uuid - raid = Mdadm.detail_result(raid_link)[raid_link] - if raid["level"] == "Container": - continue - raid["metadata"], raid["array_devices"] = "legacy", [] - container = ( - [i for i in split_line if i.startswith("container=")][0] - if "container=" in line else None - ) - if container: - container_link = uuid_path_prefix + container.split("=")[-1] - raid["container"] = cls.detail_result(container_link)[container_link] - raid["metadata"] = raid["container"]["metadata"] - raid["array_devices"] = raid["container"]["devices"] - raids.append(raid) - return raids - - @staticmethod - def stop(device_paths: str = None): - cmd = f"mdadm --stop " + (device_paths if device_paths else "--scan") - return TestRun.executor.run_expect_success(cmd) - - @staticmethod - def zero_superblock(device_paths: str): - cmd = f"mdadm --zero-superblock {device_paths}" - return TestRun.executor.run_expect_success(cmd) - - @staticmethod - def __parse_devices(details: str): - devices = [] - for detail in [d.strip() for d in details.splitlines() if " /dev/" in d]: - devices.append(detail.split()[-1]) - return devices - - @staticmethod - def __parse_level(details: str): - level = [line for line in details.splitlines() if "Raid Level" in line][0].split(" : ")[-1] - return level.capitalize() - - @staticmethod - def __parse_uuid(details: str): - uuid = [line for line in details.splitlines() if "UUID" in line][0].split(" : ")[-1] - return uuid - - @staticmethod - def __parse_metadata(details: str): - try: - return [ - line.strip() for line in details.splitlines() - if line.strip().startswith("Version :") - ][0].split(" : ")[-1] - except IndexError: - return None diff --git a/test/functional/test-framework/test_tools/nvme_cli.py b/test/functional/test-framework/test_tools/nvme_cli.py deleted file mode 100644 index 5844587..0000000 --- a/test/functional/test-framework/test_tools/nvme_cli.py +++ /dev/null @@ -1,60 +0,0 @@ -# -# Copyright(c) 2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -import json -from core.test_run import TestRun - - -def format_disk(device, metadata_size=None, block_size=None, - force=True, format_params=None, reset=True): - force_param = '-f' if force else '' - reset_param = '-r' if reset else '' - format_params = ' '.join(format_params) if format_params else '' - lbafs = get_lba_formats(device) - if metadata_size: - lbafs = [lbaf for lbaf in lbafs if lbaf['metadata_size'] == metadata_size] - if block_size: - lbafs = [lbaf for lbaf in lbafs if lbaf['block_size'] == block_size] - if len(lbafs) == 1: - TestRun.LOGGER.info( - f"Formatting device {device.path} with {metadata_size} metadata size " - f"and {lbafs[0]['block_size']} block size") - TestRun.executor.run_expect_success( - f"nvme format {device.path} -l {lbafs[0]['lba_format']} " - f"{force_param} {reset_param} {format_params}") - TestRun.LOGGER.info(f"Successfully format device: {device.path}") - else: - raise Exception(f"Wrong parameters to format device: {device.path}") - elif block_size: - lbafs = [lbaf for lbaf in lbafs if lbaf['block_size'] == block_size] - if len(lbafs) > 0: - TestRun.LOGGER.info( - f"Formatting device {device.path} with {block_size} block size") - TestRun.executor.run_expect_success( - f"nvme format {device.path} -b {block_size} " - f"{force_param} {reset_param} {format_params}") - TestRun.LOGGER.info(f"Successfully format device: {device.path}") - else: - raise Exception(f"Wrong parameters to format device: {device.path}") - else: - raise Exception("Cannot format device without specified parameters") - - -def get_lba_formats(device): - output = json.loads(TestRun.executor.run_expect_success( - f"nvme id-ns {device.path} -o json").stdout) - entries = output['lbafs'] - lbafs = [] - for entry in entries: - lbaf = {"lba_format": entries.index(entry), - "metadata_size": entry['ms'], - "block_size": 2 ** entry['ds'], - "in_use": entries.index(entry) == output['flbas']} - lbafs.append(lbaf) - return lbafs - - -def get_lba_format_in_use(device): - lbafs = get_lba_formats(device) - return next((lbaf for lbaf in lbafs if lbaf['in_use'])) diff --git a/test/functional/test-framework/test_tools/packaging.py b/test/functional/test-framework/test_tools/packaging.py deleted file mode 100644 index 01935d8..0000000 --- a/test/functional/test-framework/test_tools/packaging.py +++ /dev/null @@ -1,121 +0,0 @@ -# -# Copyright(c) 2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -import os -import re - -from core.test_run import TestRun -from test_utils.output import CmdException - - -class RpmSet(): - def __init__(self, packages_paths: list): - self.packages = packages_paths - - def _get_package_names(self): - return " ".join([os.path.splitext(os.path.basename(pckg))[0] for pckg in self.packages]) - - def check_if_installed(self): - if not self.packages: - raise ValueError("No packages given.") - - output = TestRun.executor.run(f"rpm --query {self._get_package_names()}") - - return output.exit_code == 0 - - def install(self): - TestRun.LOGGER.info(f"Installing RPM packages") - - if not self.packages: - raise ValueError("No packages given.") - - output = TestRun.executor.run( - f"rpm --upgrade --verbose --replacepkgs {' '.join(self.packages)}" - ) - if ( - output.exit_code != 0 - or re.search("error", output.stdout, re.IGNORECASE) - or re.search("error", output.stderr, re.IGNORECASE) - ): - raise CmdException("Installation failed or errors found during the process.", output) - - def uninstall(self): - TestRun.LOGGER.info(f"Uninstalling RPM packages") - - if not self.check_if_installed(): - raise FileNotFoundError("Could not uninstall - packages not installed yet.") - - output = TestRun.executor.run(f"rpm --erase --verbose {self._get_package_names()}") - if ( - output.exit_code != 0 - or re.search("error", output.stdout, re.IGNORECASE) - or re.search("error", output.stderr, re.IGNORECASE) - ): - raise CmdException("Uninstallation failed or errors found during the process.", output) - - @staticmethod - def uninstall_all_matching(*packages_names: str): - for name in packages_names: - TestRun.LOGGER.info(f"Uninstalling all RPM packages matching '{name}'") - TestRun.executor.run_expect_success( - f"rpm --query --all | grep {name} | " - f"xargs --no-run-if-empty rpm --erase --verbose" - ) - - -class DebSet(): - def __init__(self, packages_paths: list): - self.packages = packages_paths - - def _get_package_names(self): - return " ".join([os.path.basename(pckg).split("_")[0] for pckg in self.packages]) - - def check_if_installed(self): - if not self.packages: - raise ValueError("No packages given.") - - output = TestRun.executor.run(f"dpkg --no-pager --list {self._get_package_names()}") - - return output.exit_code == 0 - - def install(self): - TestRun.LOGGER.info(f"Installing DEB packages") - - if not self.packages: - raise ValueError("No packages given.") - - output = TestRun.executor.run( - f"dpkg --force-confdef --force-confold --install {' '.join(self.packages)}" - ) - if ( - output.exit_code != 0 - or re.search("error", output.stdout, re.IGNORECASE) - or re.search("error", output.stderr, re.IGNORECASE) - ): - raise CmdException("Installation failed or errors found during the process.", output) - - def uninstall(self): - TestRun.LOGGER.info(f"Uninstalling DEB packages") - - if not self.check_if_installed(): - raise FileNotFoundError("Could not uninstall - packages not installed yet.") - - output = TestRun.executor.run(f"dpkg --purge {self._get_package_names()}") - if ( - output.exit_code != 0 - or re.search("error", output.stdout, re.IGNORECASE) - or re.search("error", output.stderr, re.IGNORECASE) - ): - raise CmdException("Uninstallation failed or errors found during the process.", output) - - @staticmethod - def uninstall_all_matching(*packages_names: str): - for name in packages_names: - TestRun.LOGGER.info(f"Uninstalling all DEB packages matching '{name}'") - TestRun.executor.run_expect_success( - f"dpkg-query --no-pager --showformat='${{Package}}\n' --show | grep {name} | " - f"xargs --no-run-if-empty dpkg --purge" - ) diff --git a/test/functional/test-framework/test_tools/peach_fuzzer/__init__.py b/test/functional/test-framework/test_tools/peach_fuzzer/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/functional/test-framework/test_tools/peach_fuzzer/config_template.xml b/test/functional/test-framework/test_tools/peach_fuzzer/config_template.xml deleted file mode 100644 index 1c6c8a8..0000000 --- a/test/functional/test-framework/test_tools/peach_fuzzer/config_template.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/functional/test-framework/test_tools/peach_fuzzer/peach_fuzzer.py b/test/functional/test-framework/test_tools/peach_fuzzer/peach_fuzzer.py deleted file mode 100644 index 4adb3a3..0000000 --- a/test/functional/test-framework/test_tools/peach_fuzzer/peach_fuzzer.py +++ /dev/null @@ -1,208 +0,0 @@ -# -# Copyright(c) 2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import os -import wget -import base64 -import posixpath -import random -import tempfile -import lxml.etree as etree -from collections import namedtuple - -from core.test_run import TestRun -from test_tools import fs_utils -from test_tools.fs_utils import create_directory, check_if_file_exists, write_file - - -class PeachFuzzer: - """ - API to work with Peach Fuzzer tool in Test-Framework. - Peach Fuzzer is used only for generating fuzzed values that later are used in Test-Framework - in order to execute fuzzed CLI commands or to prepare fuzzed config files. - """ - - peach_fuzzer_3_0_url = "https://sourceforge.net/projects/peachfuzz/files/Peach/3.0/" \ - "peach-3.0.202-linux-x86_64-release.zip" - base_dir = "/root/Fuzzy" - peach_dir = "peach-3.0.202-linux-x86_64-release" - xml_config_template = posixpath.join(posixpath.dirname(__file__), "config_template.xml") - xml_config_file = posixpath.join(base_dir, "fuzzerConfig.xml") - xml_namespace = "http://peachfuzzer.com/2012/Peach" - fuzzy_output_file = posixpath.join(base_dir, "fuzzedParams.txt") - tested_param_placeholder = b"{param}" - # escape backslash first, so it doesn't interfere with escaping other characters - escape_chars = '\\\n"\'&|;()`<>$! ' - - @classmethod - def get_fuzzed_command(cls, command_template: bytes, count: int): - """ - Generate command with fuzzed parameter provided on command_template. - Command is ready to be executed with test executor - :param command_template: byte string with command to be executed. - parameter to be replaced with fuzzed string has to be tested_param_placeholder - :param count: amount of fuzzed commands to generate - :returns: named tuple with fuzzed param and CLI ready to be executed with Test-Framework - executors. Param is returned in order to implement correct values checkers in the tests - """ - TestRun.LOGGER.info(f"Try to get commands with fuzzed parameters") - FuzzedCommand = namedtuple('FuzzedCommand', ['param', 'command']) - if cls.tested_param_placeholder not in command_template: - TestRun.block("No param placeholder is found in command template!") - cmd_prefix = b"echo " - cmd_suffix = b" | base64 --decode | sh" - for fuzzed_parameter in cls.generate_peach_fuzzer_parameters(count): - yield FuzzedCommand(fuzzed_parameter, - cmd_prefix + base64.b64encode(command_template.replace( - cls.tested_param_placeholder, fuzzed_parameter)) + cmd_suffix) - - @classmethod - def generate_peach_fuzzer_parameters(cls, count: int): - """ - Generate fuzzed parameter according to Peach Fuzzer XML config - Fuzzed parameter later can be used for either generating cli command or config. - :param count: amount of fuzzed strings to generate - :returns: fuzzed value in byte string - """ - if not cls._is_installed(): - TestRun.LOGGER.info("Try to install Peach Fuzzer") - cls._install() - if not cls._is_xml_config_prepared(): - TestRun.block("No Peach Fuzzer XML config needed to generate fuzzed values was found!") - fs_utils.remove(cls.fuzzy_output_file, force=True, ignore_errors=True) - TestRun.LOGGER.info(f"Generate {count} unique fuzzed values") - cmd = f"cd {cls.base_dir}; {cls.peach_dir}/peach --range 0,{count - 1} " \ - f"--seed {random.randrange(2 ** 32)} {cls.xml_config_file} > " \ - f"{cls.base_dir}/peachOutput.log" - TestRun.executor.run_expect_success(cmd) - if not check_if_file_exists(cls.fuzzy_output_file): - TestRun.block("No expected fuzzy output file was found!") - - # process fuzzy output file locally on the controller as it can be very big - local_fuzzy_file = tempfile.NamedTemporaryFile(delete=False) - local_fuzzy_file.close() - TestRun.executor.rsync_from(cls.fuzzy_output_file, local_fuzzy_file.name) - with open(local_fuzzy_file.name, "r") as fd: - for fuzzed_param_line in fd: - fuzzed_param_bytes = base64.b64decode(fuzzed_param_line) - fuzzed_param_bytes = cls._escape_special_chars(fuzzed_param_bytes) - yield fuzzed_param_bytes - - @classmethod - def generate_config(cls, data_model_config: list): - """ - Generate Peach Fuzzer XML config based on template provided in xml_config_template - and data template passed as an argument. - :param data_model_config: dictionary with config that has to be used for generating - DataModel section in PeachFuzzer XML config. Config can be stored in test in more compact - form, e.g. in yaml, and can be converted to dict just before passing to this function. - Example of such config in yaml: - - name: String - attributes: - name: CacheId - value: '1' - size: '14' - mutable: 'true' - children: - - name: Hint - attributes: - name: NumericalString - value: 'true' - """ - - if not posixpath.exists(cls.xml_config_template): - TestRun.block("Peach fuzzer xml config template not found!") - root = etree.parse(cls.xml_config_template) - data_model = root.find(f'{{{cls.xml_namespace}}}DataModel[@name="Value"]') - cls.__create_xml_nodes(data_model, data_model_config) - create_directory(cls.base_dir, True) - write_file(cls.xml_config_file, etree.tostring(root, encoding="unicode")) - - @classmethod - def copy_config(cls, config_file: str): - """ - Instead of generating config with "generate_config" method, config can be prepared manually - and just passed as is to PeachFuzzer. - :param config_file: Peach Fuzzer XML config to be copied to the DUT - """ - if not posixpath.exists(config_file): - TestRun.block("Peach fuzzer xml config to be copied doesn't exist!") - create_directory(cls.base_dir, True) - TestRun.executor.rsync_to(config_file, cls.xml_config_file) - - @classmethod - def __create_xml_nodes(cls, xml_node, config): - """ - Create XML code for Peach Fuzzer based on python dict config - """ - for element in config: - new_node = etree.Element(element["name"]) - for attr_name, attr_value in element["attributes"].items(): - new_node.set(attr_name, attr_value) - if element.get("children"): - cls.__create_xml_nodes(new_node, element.get("children")) - xml_node.append(new_node) - - @classmethod - def _install(cls): - """ - Install Peach Fuzzer on the DUT - """ - peach_archive = wget.download(cls.peach_fuzzer_3_0_url) - create_directory(cls.base_dir, True) - TestRun.executor.rsync_to(f"\"{peach_archive}\"", f"{cls.base_dir}") - TestRun.executor.run_expect_success( - f'cd {cls.base_dir} && unzip -u "{peach_archive}"') - if cls._is_installed(): - TestRun.LOGGER.info("Peach fuzzer installed successfully") - os.remove(peach_archive) - else: - TestRun.block("Peach fuzzer installation failed!") - - @classmethod - def _is_installed(cls): - """ - Check if Peach Fuzzer is installed on the DUT - """ - if not cls._is_mono_installed(): - TestRun.block("Mono is not installed, can't continue with Peach Fuzzer!") - if fs_utils.check_if_directory_exists(posixpath.join(cls.base_dir, cls.peach_dir)): - return "Peach" in TestRun.executor.run( - f"cd {cls.base_dir} && {cls.peach_dir}/peach --version").stdout.strip() - else: - return False - - @classmethod - def _escape_special_chars(cls, fuzzed_str: bytes): - """ - Escape special chars provided in escape_chars list in the fuzzed string generated by - Peach Fuzzer - Escaping is done for example in order to make fuzzed string executable in Linux CLI - If fuzzed string will be used in other places, escape_chars list may be overwritten. - """ - for i in cls.escape_chars: - i = bytes(i, "utf-8") - if i in fuzzed_str[:]: - fuzzed_str = fuzzed_str.replace(i, b'\\' + i) - return fuzzed_str - - @classmethod - def _is_xml_config_prepared(cls): - """ - Check if Peach Fuzzer XML config is present on the DUT - """ - if fs_utils.check_if_file_exists(cls.xml_config_file): - return True - else: - return False - - @staticmethod - def _is_mono_installed(): - """ - Check if Mono (.NET compatible framework) is installed on the DUT - If it's not, it has to be installed manually. - For RHEL-based OSes it's usually mono-complete package - """ - return TestRun.executor.run("which mono").exit_code == 0 diff --git a/test/functional/test-framework/test_utils/__init__.py b/test/functional/test-framework/test_utils/__init__.py deleted file mode 100644 index dce958c..0000000 --- a/test/functional/test-framework/test_utils/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# diff --git a/test/functional/test-framework/test_utils/asynchronous.py b/test/functional/test-framework/test_utils/asynchronous.py deleted file mode 100644 index 9be0159..0000000 --- a/test/functional/test-framework/test_utils/asynchronous.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import concurrent - - -def start_async_func(func, *args): - """ - Starts asynchronous task and returns an Future object, which in turn returns an - actual result after triggering result() method on it. - - result() method is waiting for the task to be completed. - - done() method returns True when task ended (have a result or ended with an exception) - otherwise returns False - """ - executor = concurrent.futures.ThreadPoolExecutor() - return executor.submit(func, *args) diff --git a/test/functional/test-framework/test_utils/disk_finder.py b/test/functional/test-framework/test_utils/disk_finder.py deleted file mode 100644 index 21eb0db..0000000 --- a/test/functional/test-framework/test_utils/disk_finder.py +++ /dev/null @@ -1,190 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import posixpath - -from core.test_run import TestRun -from test_tools import disk_utils -from test_tools.fs_utils import check_if_file_exists, readlink -from test_utils import os_utils -from test_utils.output import CmdException - - -def find_disks(): - devices_result = [] - - TestRun.LOGGER.info("Finding platform's disks.") - - # TODO: intelmas should be implemented as a separate tool in the future. - # There will be intelmas installer in case, when it is not installed - output = TestRun.executor.run('intelmas') - if output.exit_code != 0: - raise Exception(f"Error while executing command: 'intelmas'.\n" - f"stdout: {output.stdout}\nstderr: {output.stderr}") - block_devices = get_block_devices_list() - try: - discover_ssd_devices(block_devices, devices_result) - discover_hdd_devices(block_devices, devices_result) - except Exception as e: - raise Exception(f"Exception occurred while looking for disks: {str(e)}") - - return devices_result - - -def get_block_devices_list(): - devices = TestRun.executor.run_expect_success("ls /sys/block -1").stdout.splitlines() - os_disks = get_system_disks() - block_devices = [] - - for dev in devices: - if ('sd' in dev or 'nvme' in dev) and dev not in os_disks: - block_devices.append(dev) - - return block_devices - - -def discover_hdd_devices(block_devices, devices_res): - for dev in block_devices: - if TestRun.executor.run_expect_success(f"cat /sys/block/{dev}/removable").stdout == "1": - continue # skip removable drives - block_size = disk_utils.get_block_size(dev) - if int(block_size) == 4096: - disk_type = 'hdd4k' - else: - disk_type = 'hdd' - devices_res.append({ - "type": disk_type, - "path": f"{resolve_to_by_id_link(dev)}", - "serial": TestRun.executor.run_expect_success( - f"sg_inq /dev/{dev} | grep -i 'serial number'" - ).stdout.split(': ')[1].strip(), - "blocksize": block_size, - "size": disk_utils.get_size(dev)}) - block_devices.clear() - - -# This method discovers only Intel SSD devices -def discover_ssd_devices(block_devices, devices_res): - ssd_count = int(TestRun.executor.run_expect_success( - 'intelmas show -intelssd | grep DevicePath | wc -l').stdout) - for i in range(0, ssd_count): - # Workaround for intelmas bug that lists all of the devices (non intel included) - # with -intelssd flag - if TestRun.executor.run( - f"intelmas show -display index -intelssd {i} | grep -w Intel").exit_code == 0: - device_path = TestRun.executor.run_expect_success( - f"intelmas show -intelssd {i} | grep DevicePath").stdout.split()[2] - dev = device_path.replace("/dev/", "") - if "sg" in dev: - sata_dev = TestRun.executor.run_expect_success( - f"sg_map | grep {dev}").stdout.split()[1] - dev = sata_dev.replace("/dev/", "") - if dev not in block_devices: - continue - serial_number = TestRun.executor.run_expect_success( - f"intelmas show -intelssd {i} | grep SerialNumber").stdout.split()[2].strip() - if 'nvme' not in device_path: - disk_type = 'sata' - device_path = dev - elif TestRun.executor.run( - f"intelmas show -intelssd {i} | grep Optane").exit_code == 0: - disk_type = 'optane' - else: - disk_type = 'nand' - - devices_res.append({ - "type": disk_type, - "path": resolve_to_by_id_link(device_path), - "serial": serial_number, - "blocksize": disk_utils.get_block_size(dev), - "size": disk_utils.get_size(dev)}) - block_devices.remove(dev) - - -def get_disk_serial_number(dev_path): - commands = [ - f"(udevadm info --query=all --name={dev_path} | grep 'SCSI.*_SERIAL' || " - f"udevadm info --query=all --name={dev_path} | grep 'ID_SERIAL_SHORT') | " - "awk --field-separator '=' '{print $NF}'", - f"sg_inq {dev_path} 2> /dev/null | grep '[Ss]erial number:' | " - "awk '{print $NF}'", - f"udevadm info --query=all --name={dev_path} | grep 'ID_SERIAL' | " - "awk --field-separator '=' '{print $NF}'" - ] - for command in commands: - serial = TestRun.executor.run(command).stdout - if serial: - return serial.split('\n')[0] - return None - - -def get_all_serial_numbers(): - serial_numbers = {} - block_devices = get_block_devices_list() - for dev in block_devices: - serial = get_disk_serial_number(dev) - try: - path = resolve_to_by_id_link(dev) - except Exception: - continue - if serial: - serial_numbers[serial] = path - else: - TestRun.LOGGER.warning(f"Device {path} ({dev}) does not have a serial number.") - serial_numbers[path] = path - return serial_numbers - - -def get_system_disks(): - system_device = TestRun.executor.run_expect_success('mount | grep " / "').stdout.split()[0] - readlink_output = readlink(system_device) - device_name = readlink_output.split('/')[-1] - sys_block_path = os_utils.get_sys_block_path() - used_device_names = __get_slaves(device_name) - if not used_device_names: - used_device_names = [device_name] - disk_names = [] - for device_name in used_device_names: - if check_if_file_exists(f'{sys_block_path}/{device_name}/partition'): - parent_device = readlink(f'{sys_block_path}/{device_name}/..').split('/')[-1] - disk_names.append(parent_device) - else: - disk_names.append(device_name) - - return disk_names - - -def __get_slaves(device_name: str): - try: - device_names = TestRun.executor.run_expect_success( - f'ls {os_utils.get_sys_block_path()}/{device_name}/slaves').stdout.splitlines() - except CmdException as e: - if "No such file or directory" not in e.output.stderr: - raise - return None - device_list = [] - for device_name in device_names: - slaves = __get_slaves(device_name) - if slaves: - for slave in slaves: - device_list.append(slave) - else: - device_list.append(device_name) - return device_list - - -def resolve_to_by_id_link(path): - by_id_paths = TestRun.executor.run_expect_success("ls /dev/disk/by-id -1").stdout.splitlines() - dev_full_paths = [posixpath.join("/dev/disk/by-id", by_id_path) for by_id_path in by_id_paths] - - for full_path in dev_full_paths: - # handle exception for broken links - try: - if readlink(full_path) == readlink(posixpath.join("/dev", path)): - return full_path - except CmdException: - continue - - raise ValueError(f'By-id device link not found for device {path}') diff --git a/test/functional/test-framework/test_utils/drbd.py b/test/functional/test-framework/test_utils/drbd.py deleted file mode 100644 index bec4497..0000000 --- a/test/functional/test-framework/test_utils/drbd.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright(c) 2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause-Clear -# - -import os - -from test_utils.filesystem.file import File - - -class Resource: - def __init__(self, name, device, nodes, options=None): - self.name = name - self.device = device - self.nodes = nodes - self.options = options - - def __str__(self): - output = ( - f"resource {self.name} {{ \n" - f" device {self.device}; \n" - f"{''.join([str(node) for node in self.nodes])}" - ) - - if self.options: - output += f" options {{\n" - for (k, v) in self.options.items(): - output += f" {k} {v};\n" - output += f" }}\n" - - output += f"}}" - return output - - def __repr__(self): - return str(self) - - def save(self, path="/etc/drbd.d/", filename=None): - filename = filename if filename else f"{self.name}.res" - file = File(path + filename) - file.write(str(self)) - - -class Node: - def __init__(self, name, disk, meta_disk, ip, port): - self.name = name - self.disk = disk - self.meta_disk = meta_disk - self.ip = ip - self.port = port - - def __str__(self): - return ( - f" on {self.name} {{ \n" - f" disk {self.disk};\n" - f" meta-disk {self.meta_disk};\n" - f" address {self.ip}:{self.port};\n" - f" }} \n" - ) - - def __repr__(self): - return str(self) diff --git a/test/functional/test-framework/test_utils/dut.py b/test/functional/test-framework/test_utils/dut.py deleted file mode 100644 index 0780dcb..0000000 --- a/test/functional/test-framework/test_utils/dut.py +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from storage_devices.disk import Disk, DiskType - - -class Dut: - def __init__(self, dut_info): - self.config = dut_info - self.disks = [] - for disk_info in dut_info.get('disks', []): - self.disks.append(Disk.create_disk(disk_info['path'], - DiskType[disk_info['type']], - disk_info['serial'], - disk_info['blocksize'])) - self.disks.sort(key=lambda disk: disk.disk_type, reverse=True) - - self.ipmi = dut_info['ipmi'] if 'ipmi' in dut_info else None - self.spider = dut_info['spider'] if 'spider' in dut_info else None - self.wps = dut_info['wps'] if 'wps' in dut_info else None - self.env = dut_info['env'] if 'env' in dut_info else None - self.ip = dut_info['ip'] if 'ip' in dut_info else "127.0.0.1" - - def __str__(self): - dut_str = f'ip: {self.ip}\n' - dut_str += f'ipmi: {self.ipmi["ip"]}\n' if self.ipmi is not None else '' - dut_str += f'spider: {self.spider["ip"]}\n' if self.spider is not None else '' - dut_str += f'wps: {self.wps["ip"]} port: {self.wps["port"]}\n' \ - if self.wps is not None else '' - dut_str += f'disks:\n' - for disk in self.disks: - dut_str += f"\t{disk}" - dut_str += "\n" - return dut_str - - def get_disks_of_type(self, disk_type: DiskType): - ret_list = [] - for d in self.disks: - if d.disk_type == disk_type: - ret_list.append(d) - return ret_list diff --git a/test/functional/test-framework/test_utils/emergency_escape.py b/test/functional/test-framework/test_utils/emergency_escape.py deleted file mode 100644 index a997032..0000000 --- a/test/functional/test-framework/test_utils/emergency_escape.py +++ /dev/null @@ -1,113 +0,0 @@ -# -# Copyright(c) 2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from textwrap import dedent -from string import Template -from pathlib import Path - -from .systemd import enable_service, reload_daemon, systemd_service_directory, disable_service -from test_tools.fs_utils import ( - create_file, - write_file, - remove, -) - - -class EmergencyEscape: - escape_marker = "EMERGENCY_ESCAPE" - escape_service = Path("emergency-escape.service") - escape_service_template = Template( - dedent( - f""" - [Unit] - After=emergency.target - IgnoreOnIsolate=true - DefaultDependencies=no - - [Service] - Type=oneshot - ExecStart=/bin/sh -c '/usr/bin/echo "{escape_marker}" > /dev/kmsg' - $user_method - ExecStart=/usr/bin/systemctl daemon-reload - ExecStart=/usr/bin/systemctl default --no-block - - [Install] - WantedBy=emergency.target - """ - ).strip() - ) - cleanup_service = Path("emergency-escape-cleanup.service") - cleanup_service_template = Template( - dedent( - """ - [Unit] - After=emergency-escape.service - IgnoreOnIsolate=true - DefaultDependencies=no - - [Service] - Type=oneshot - $user_method - ExecStart=/usr/bin/systemctl disable emergency-escape.service - ExecStart=/usr/bin/rm -f /usr/lib/systemd/system/emergency-escape.service - ExecStart=/usr/bin/systemctl daemon-reload - - [Install] - WantedBy=emergency-escape.service - """ - ).strip() - ) - - def __init__(self): - self.escape_method = [] - self.cleanup_method = [] - - def arm(self): - escape_path = str(systemd_service_directory / EmergencyEscape.escape_service) - cleanup_path = str(systemd_service_directory / EmergencyEscape.cleanup_service) - - create_file(escape_path) - create_file(cleanup_path) - - user_escape = "\n".join([f"ExecStart={method}" for method in self.escape_method]) - user_cleanup = "\n".join([f"ExecStart={method}" for method in self.cleanup_method]) - - escape_contents = EmergencyEscape.escape_service_template.substitute( - user_method=user_escape - ) - cleanup_contents = EmergencyEscape.cleanup_service_template.substitute( - user_method=user_cleanup - ) - - write_file(escape_path, escape_contents) - write_file(cleanup_path, cleanup_contents) - - enable_service(EmergencyEscape.escape_service) - enable_service(EmergencyEscape.cleanup_service) - - def cleanup(self): - remove(str(systemd_service_directory / EmergencyEscape.cleanup_service), ignore_errors=True) - remove(str(systemd_service_directory / EmergencyEscape.escape_service), ignore_errors=True) - reload_daemon() - - @classmethod - def verify_trigger_in_log(cls, log_list): - for l in log_list: - if cls.escape_marker in l: - return True - - return False - - def add_escape_method_command(self, method): - self.escape_method.append(method) - - def add_cleanup_method_command(self, method): - self.cleanup_method.append(method) - - def __enter__(self): - self.arm() - - def __exit__(self, exc_type, exc_value, exc_traceback): - self.cleanup() diff --git a/test/functional/test-framework/test_utils/filesystem/__init__.py b/test/functional/test-framework/test_utils/filesystem/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/functional/test-framework/test_utils/filesystem/directory.py b/test/functional/test-framework/test_utils/filesystem/directory.py deleted file mode 100644 index 7e46332..0000000 --- a/test/functional/test-framework/test_utils/filesystem/directory.py +++ /dev/null @@ -1,31 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -from core.test_run import TestRun -from test_tools import fs_utils -from test_tools.fs_utils import check_if_directory_exists -from test_utils.filesystem.fs_item import FsItem - - -class Directory(FsItem): - def __init__(self, full_path): - FsItem.__init__(self, full_path) - - def ls(self): - output = fs_utils.ls(f"{self.full_path}") - return fs_utils.parse_ls_output(output, self.full_path) - - @staticmethod - def create_directory(path: str, parents: bool = False): - fs_utils.create_directory(path, parents) - output = fs_utils.ls_item(path) - return fs_utils.parse_ls_output(output)[0] - - @staticmethod - def create_temp_directory(parent_dir_path: str = "/tmp"): - command = f"mktemp --directory --tmpdir={parent_dir_path}" - output = TestRun.executor.run_expect_success(command) - if not check_if_directory_exists(output.stdout): - TestRun.LOGGER.exception("'mktemp' succeeded, but created directory does not exist") - return Directory(output.stdout) diff --git a/test/functional/test-framework/test_utils/filesystem/file.py b/test/functional/test-framework/test_utils/filesystem/file.py deleted file mode 100644 index e6071e1..0000000 --- a/test/functional/test-framework/test_utils/filesystem/file.py +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from core.test_run import TestRun -from test_tools import fs_utils -from test_tools.dd import Dd -from test_utils.filesystem.fs_item import FsItem -from test_utils.size import Size - - -class File(FsItem): - def __init__(self, full_path): - FsItem.__init__(self, full_path) - - def compare(self, other_file): - return fs_utils.compare(str(self), str(other_file)) - - def diff(self, other_file): - return fs_utils.diff(str(self), str(other_file)) - - def md5sum(self, binary=True): - output = TestRun.executor.run( - f"md5sum {'-b' if binary else ''} {self.full_path}") - if output.exit_code != 0: - raise Exception(f"Md5sum command execution failed! {output.stdout}\n{output.stderr}") - return output.stdout.split()[0] - - def read(self): - return fs_utils.read_file(str(self)) - - def write(self, content, overwrite: bool = True): - fs_utils.write_file(str(self), content, overwrite) - self.refresh_item() - - def get_properties(self): - return FileProperties(self) - - @staticmethod - def create_file(path: str): - fs_utils.create_file(path) - output = fs_utils.ls_item(path) - return fs_utils.parse_ls_output(output)[0] - - def padding(self, size: Size): - dd = Dd().input("/dev/zero").output(self).count(1).block_size(size) - dd.run() - self.refresh_item() - - def remove(self, force: bool = False, ignore_errors: bool = False): - fs_utils.remove(str(self), force=force, ignore_errors=ignore_errors) - - def copy(self, - destination, - force: bool = False, - recursive: bool = False, - dereference: bool = False): - fs_utils.copy(str(self), destination, force, recursive, dereference) - if fs_utils.check_if_directory_exists(destination): - path = f"{destination}{'/' if destination[-1] != '/' else ''}{self.name}" - else: - path = destination - output = fs_utils.ls_item(path) - return fs_utils.parse_ls_output(output)[0] - - -class FileProperties: - def __init__(self, file): - file = fs_utils.parse_ls_output(fs_utils.ls_item(file.full_path))[0] - self.full_path = file.full_path - self.parent_dir = FsItem.get_parent_dir(self.full_path) - self.name = FsItem.get_name(self.full_path) - self.modification_time = file.modification_time - self.owner = file.owner - self.group = file.group - self.permissions = file.permissions - self.size = file.size - - def __eq__(self, other): - return (self.permissions == other.permissions and self.size == other.size - and self.owner == other.owner and self.group == other.group - and self.name == other.name) diff --git a/test/functional/test-framework/test_utils/filesystem/fs_item.py b/test/functional/test-framework/test_utils/filesystem/fs_item.py deleted file mode 100644 index c060ae3..0000000 --- a/test/functional/test-framework/test_utils/filesystem/fs_item.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import posixpath - -from test_tools import fs_utils - - -class FsItem: - def __init__(self, full_path): - self.full_path = full_path - # all below values must be refreshed in refresh_item() - self.parent_dir = self.get_parent_dir(self.full_path) - self.name = self.get_name(self.full_path) - self.modification_time = None - self.owner = None - self.group = None - self.permissions = FsPermissions() - self.size = None - - @staticmethod - def get_name(path): - head, tail = posixpath.split(path) - return tail or posixpath.basename(head) - - @staticmethod - def get_parent_dir(path): - head, tail = posixpath.split(path) - if tail: - return head - else: - head, tail = posixpath.split(head) - return head - - def __str__(self): - return self.full_path - - def chmod_numerical(self, permissions: int, recursive: bool = False): - fs_utils.chmod_numerical(self.full_path, permissions, recursive) - self.refresh_item() - - def chmod(self, - permissions: fs_utils.Permissions, - users: fs_utils.PermissionsUsers, - sign: fs_utils.PermissionSign = fs_utils.PermissionSign.set, - recursive: bool = False): - fs_utils.chmod(self.full_path, permissions, users, sign=sign, recursive=recursive) - self.refresh_item() - - def chown(self, owner, group, recursive: bool = False): - fs_utils.chown(self.full_path, owner, group, recursive) - self.refresh_item() - - def copy(self, - destination, - force: bool = False, - recursive: bool = False, - dereference: bool = False): - target_dir_exists = fs_utils.check_if_directory_exists(destination) - fs_utils.copy(str(self), destination, force, recursive, dereference) - if target_dir_exists: - path = f"{destination}{'/' if destination[-1] != '/' else ''}{self.name}" - else: - path = destination - output = fs_utils.ls_item(f"{path}") - return fs_utils.parse_ls_output(output)[0] - - def move(self, - destination, - force: bool = False): - target_dir_exists = fs_utils.check_if_directory_exists(destination) - fs_utils.move(str(self), destination, force) - if target_dir_exists: - self.full_path = f"{destination}{'/' if destination[-1] != '/' else ''}{self.name}" - else: - self.full_path = destination - self.refresh_item() - return self - - def refresh_item(self): - updated_file = fs_utils.parse_ls_output(fs_utils.ls_item(self.full_path))[0] - # keep order the same as in __init__() - self.parent_dir = updated_file.parent_dir - self.name = updated_file.name - self.modification_time = updated_file.modification_time - self.owner = updated_file.owner - self.group = updated_file.group - self.permissions = updated_file.permissions - self.size = updated_file.size - return self - - -class FsPermissions: - def __init__(self, user=None, group=None, other=None): - self.user = user - self.group = group - self.other = other - - def __eq__(self, other): - return self.user == other.user and self.group == other.group and self.other == other.other diff --git a/test/functional/test-framework/test_utils/filesystem/symlink.py b/test/functional/test-framework/test_utils/filesystem/symlink.py deleted file mode 100644 index e67906c..0000000 --- a/test/functional/test-framework/test_utils/filesystem/symlink.py +++ /dev/null @@ -1,91 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from core.test_run import TestRun -from test_tools.fs_utils import ( - readlink, - create_directory, - check_if_symlink_exists, - check_if_directory_exists, -) -from test_utils.filesystem.file import File - - -class Symlink(File): - def __init__(self, full_path): - File.__init__(self, full_path) - - def md5sum(self, binary=True): - output = TestRun.executor.run_expect_success( - f"md5sum {'-b' if binary else ''} {self.get_target()}" - ) - return output.stdout.split()[0] - - def get_target(self): - return readlink(self.full_path) - - def get_symlink_path(self): - return self.full_path - - def remove_symlink(self): - path = self.get_symlink_path() - TestRun.executor.run_expect_success(f"rm -f {path}") - - @classmethod - def create_symlink(cls, link_path: str, target: str, force: bool = False): - """ - Creates a Symlink - new or overwrites existing one if force parameter is True - :param link_path: path to the place where we want to create a symlink - :param target: the path of an object that the requested Symlink points to - :param force: determines if the existing symlink with the same name should be overridden - return: Symlink object located under link_path - """ - cmd = f"ln --symbolic {target} {link_path}" - is_dir = check_if_directory_exists(link_path) - parent_dir = cls.get_parent_dir(link_path) - if is_dir: - raise IsADirectoryError(f"'{link_path}' is an existing directory.") - if force: - if not check_if_directory_exists(parent_dir): - create_directory(parent_dir, True) - TestRun.executor.run_expect_success(f"rm -f {link_path}") - TestRun.executor.run_expect_success(cmd) - return cls(link_path) - - @classmethod - def get_symlink(cls, link_path: str, target: str = None, create: bool = False): - """ - Request a Symlink (create new or identify existing) - :param link_path: full path of the requested Symlink - :param target: path of an object that the requested Symlink points to - (required if create is True) - :param create: determines if the requested Symlink should be created if it does not exist - :return: Symlink object located under link_path - """ - if create and not target: - raise AttributeError("Target is required for symlink creation.") - - is_symlink = check_if_symlink_exists(link_path) - if is_symlink: - if not target or readlink(link_path) == readlink(target): - return cls(link_path) - else: - raise FileExistsError("Existing symlink points to a different target.") - elif not create: - raise FileNotFoundError("Requested symlink does not exist.") - - is_dir = check_if_directory_exists(link_path) - if is_dir: - raise IsADirectoryError( - f"'{link_path}' is an existing directory." "\nUse a full path for symlink creation." - ) - - parent_dir = cls.get_parent_dir(link_path) - if not check_if_directory_exists(parent_dir): - create_directory(parent_dir, True) - - cmd = f"ln --symbolic {target} {link_path}" - TestRun.executor.run_expect_success(cmd) - return cls(link_path) diff --git a/test/functional/test-framework/test_utils/fstab.py b/test/functional/test-framework/test_utils/fstab.py deleted file mode 100644 index fceb375..0000000 --- a/test/functional/test-framework/test_utils/fstab.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from test_tools import fs_utils -from test_utils import systemd - - -def add_mountpoint(device, mount_point, fs_type, mount_now=True): - fs_utils.append_line("/etc/fstab", - f"{device.path} {mount_point} {fs_type.name} defaults 0 0") - systemd.reload_daemon() - if mount_now: - systemd.restart_service("local-fs.target") - - -def remove_mountpoint(device): - fs_utils.remove_lines("/etc/fstab", device.path) - systemd.reload_daemon() diff --git a/test/functional/test-framework/test_utils/generator.py b/test/functional/test-framework/test_utils/generator.py deleted file mode 100644 index 8c64ba6..0000000 --- a/test/functional/test-framework/test_utils/generator.py +++ /dev/null @@ -1,11 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import random -import string - - -def random_string(length: int, chars=string.ascii_letters + string.digits): - return ''.join(random.choice(chars) for i in range(length)) diff --git a/test/functional/test-framework/test_utils/io_stats.py b/test/functional/test-framework/test_utils/io_stats.py deleted file mode 100644 index 9a3f218..0000000 --- a/test/functional/test-framework/test_utils/io_stats.py +++ /dev/null @@ -1,112 +0,0 @@ -# -# Copyright(c) 2020-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# -import re - -from core.test_run import TestRun -from test_utils.output import CmdException - -SYSFS_LINE_FORMAT = r"^(\d+\s+){10,}\d+$" -PROCFS_LINE_FORMAT = r"^\d+\s+\d+\s+\w+\s+" + SYSFS_LINE_FORMAT[1:] - - -# This class represents block device I/O statistics. -# For more information see: -# https://www.kernel.org/doc/Documentation/admin-guide/iostats.rst -class IoStats: - def __init__(self): - self.reads = None # field 0 - self.reads_merged = None # field 1 - self.sectors_read = None # field 2 - self.read_time_ms = None # field 3 - self.writes = None # field 4 - self.writes_merged = None # field 5 - self.sectors_written = None # field 6 - self.write_time_ms = None # field 7 - self.ios_in_progress = None # field 8 - self.io_time_ms = None # field 9 - self.io_time_weighed_ms = None # field 10 - # only in kernels 4.18+ - self.discards = None # field 11 - self.discards_merged = None # field 12 - self.sectors_discarded = None # field 13 - self.discard_time_ms = None # field 14 - # only in kernels 5.5+ - self.flushes = None # field 15 - self.flush_time_ms = None # field 16 - - def __sub__(self, other): - if self.reads < other.reads: - raise Exception("Cannot subtract Reads") - if self.writes < other.writes: - raise Exception("Cannot subtract Writes") - - stats = IoStats() - stats.reads = self.reads - other.reads - stats.reads_merged = self.reads_merged - other.reads_merged - stats.sectors_read = self.sectors_read - other.sectors_read - stats.read_time_ms = self.read_time_ms - other.read_time_ms - stats.writes = self.writes - other.writes - stats.writes_merged = self.writes_merged - other.writes_merged - stats.sectors_written = self.sectors_written - other.sectors_written - stats.write_time_ms = self.write_time_ms - other.write_time_ms - stats.ios_in_progress = 0 - stats.io_time_ms = self.io_time_ms - other.io_time_ms - stats.io_time_weighed_ms = self.io_time_weighed_ms - other.io_time_weighed_ms - if stats.discards and other.discards: - stats.discards = self.discards - other.discards - if stats.discards_merged and other.discards_merged: - stats.discards_merged = self.discards_merged - other.discards_merged - if stats.sectors_discarded and other.sectors_discarded: - stats.sectors_discarded = self.sectors_discarded - other.sectors_discarded - if stats.discard_time_ms and other.discard_time_ms: - stats.discard_time_ms = self.discard_time_ms - other.discard_time_ms - if stats.flushes and other.flushes: - stats.flushes = self.flushes - other.flushes - if stats.flush_time_ms and other.flush_time_ms: - stats.flush_time_ms = self.flush_time_ms - other.flush_time_ms - return stats - - @staticmethod - def parse(stats_line: str): - stats_line = stats_line.strip() - - if re.match(SYSFS_LINE_FORMAT, stats_line): - fields = stats_line.split() - elif re.match(PROCFS_LINE_FORMAT, stats_line): - fields = stats_line.split()[3:] - else: - raise Exception(f"Wrong input format for diskstat parser") - - values = [int(f) for f in fields] - - stats = IoStats() - stats.reads = values[0] - stats.reads_merged = values[1] - stats.sectors_read = values[2] - stats.read_time_ms = values[3] - stats.writes = values[4] - stats.writes_merged = values[5] - stats.sectors_written = values[6] - stats.write_time_ms = values[7] - stats.ios_in_progress = values[8] - stats.io_time_ms = values[9] - stats.io_time_weighed_ms = values[10] - if len(values) > 11: - stats.discards = values[11] - stats.discards_merged = values[12] - stats.sectors_discarded = values[13] - stats.discard_time_ms = values[14] - if len(values) > 15: - stats.flushes = values[15] - stats.flush_time_ms = values[16] - return stats - - @staticmethod - def get_io_stats(device_id): - stats_output = TestRun.executor.run_expect_success( - f"cat /proc/diskstats | grep '{device_id} '") - if not stats_output.stdout.strip(): - raise CmdException("Failed to get statistics for device " + device_id, stats_output) - return IoStats.parse(stats_line=stats_output.stdout.splitlines()[0]) diff --git a/test/functional/test-framework/test_utils/linux_command.py b/test/functional/test-framework/test_utils/linux_command.py deleted file mode 100644 index b6d887b..0000000 --- a/test/functional/test-framework/test_utils/linux_command.py +++ /dev/null @@ -1,79 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from collections import defaultdict - - -class LinuxCommand: - def __init__(self, command_executor, command_name): - self.command_executor = command_executor - self.command_param = defaultdict(list) - self.command_flags = [] - self.command_name = command_name - self.param_name_prefix = '' - self.param_separator = ' ' - self.param_value_prefix = '=' - self.param_value_list_separator = ',' - self.command_env_var = defaultdict(list) - self.env_var_separator = ' ' - self.env_var_value_prefix = '=' - - def run(self): - return self.command_executor.run(str(self)) - - def run_in_background(self): - return self.command_executor.run_in_background(str(self)) - - def set_flags(self, *flag): - for f in flag: - self.command_flags.append(f) - return self - - def remove_flag(self, flag): - if flag in self.command_flags: - self.command_flags.remove(flag) - return self - - def set_param(self, key, *values): - self.remove_param(key) - - for val in values: - self.command_param[key].append(str(val)) - return self - - def remove_param(self, key): - if key in self.command_param: - del self.command_param[key] - return self - - def set_env_var(self, key, *values): - self.remove_env_var(key) - - for val in values: - self.command_env_var[key].append(str(val)) - return self - - def remove_env_var(self, key): - if key in self.command_env_var: - del self.command_env_var[key] - return self - - def get_parameter_value(self, param_name): - if param_name in self.command_param.keys(): - return self.command_param[param_name] - return None - - def __str__(self): - command = '' - for key, value in self.command_env_var.items(): - command += f'{key}{self.env_var_value_prefix}{",".join(value)}' \ - f'{self.env_var_separator}' - command += self.command_name - for key, value in self.command_param.items(): - command += f'{self.param_separator}{self.param_name_prefix}' \ - f'{key}{self.param_value_prefix}{",".join(value)}' - for flag in self.command_flags: - command += f'{self.param_separator}{self.param_name_prefix}{flag}' - return command diff --git a/test/functional/test-framework/test_utils/os_utils.py b/test/functional/test-framework/test_utils/os_utils.py deleted file mode 100644 index 8d68662..0000000 --- a/test/functional/test-framework/test_utils/os_utils.py +++ /dev/null @@ -1,462 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import math -import posixpath -import re -import time -from datetime import timedelta, datetime - -from aenum import IntFlag, Enum, IntEnum -from packaging import version - -from core.test_run import TestRun -from storage_devices.device import Device -from test_tools.dd import Dd -from test_tools.disk_utils import get_sysfs_path -from test_tools.fs_utils import check_if_directory_exists, create_directory, check_if_file_exists -from test_utils.filesystem.file import File -from test_utils.output import CmdException -from test_utils.retry import Retry -from test_utils.size import Size, Unit - -DEBUGFS_MOUNT_POINT = "/sys/kernel/debug" -MEMORY_MOUNT_POINT = "/mnt/memspace" - - -class DropCachesMode(IntFlag): - PAGECACHE = 1 - SLAB = 2 - ALL = PAGECACHE | SLAB - - -class OvercommitMemoryMode(Enum): - DEFAULT = 0 - ALWAYS = 1 - NEVER = 2 - - -class Runlevel(IntEnum): - """ - Halt the system. - SysV Runlevel: 0 - systemd Target: runlevel0.target, poweroff.target - """ - runlevel0 = 0 - poweroff = runlevel0 - - """ - Single user mode. - SysV Runlevel: 1, s, single - systemd Target: runlevel1.target, rescue.target - """ - runlevel1 = 1 - rescue = runlevel1 - - """ - User-defined/Site-specific runlevels. By default, identical to 3. - SysV Runlevel: 2, 4 - systemd Target: runlevel2.target, runlevel4.target, multi-user.target - """ - runlevel2 = 2 - - """ - Multi-user, non-graphical. Users can usually login via multiple consoles or via the network. - SysV Runlevel: 3 - systemd Target: runlevel3.target, multi-user.target - """ - runlevel3 = 3 - multi_user = runlevel3 - - """ - Multi-user, graphical. Usually has all the services of runlevel 3 plus a graphical login. - SysV Runlevel: 5 - systemd Target: runlevel5.target, graphical.target - """ - runlevel5 = 5 - graphical = runlevel5 - - """ - Reboot - SysV Runlevel: 6 - systemd Target: runlevel6.target, reboot.target - """ - runlevel6 = 6 - reboot = runlevel6 - - """ - Emergency shell - SysV Runlevel: emergency - systemd Target: emergency.target - """ - runlevel7 = 7 - emergency = runlevel7 - - -class SystemManagerType(Enum): - sysv = 0 - systemd = 1 - - -def get_system_manager(): - output = TestRun.executor.run_expect_success("ps -p 1").stdout - type = output.split('\n')[1].split()[3] - if type == "init": - return SystemManagerType.sysv - elif type == "systemd": - return SystemManagerType.systemd - raise Exception(f"Unknown system manager type ({type}).") - - -def change_runlevel(runlevel: Runlevel): - if runlevel == get_runlevel(): - return - if Runlevel.runlevel0 < runlevel < Runlevel.runlevel6: - system_manager = get_system_manager() - if system_manager == SystemManagerType.systemd: - TestRun.executor.run_expect_success(f"systemctl set-default {runlevel.name}.target") - else: - TestRun.executor.run_expect_success( - f"sed -i 's/^.*id:.*$/id:{runlevel.value}:initdefault: /' /etc/inittab") - TestRun.executor.run_expect_success(f"init {runlevel.value}") - - -def get_runlevel(): - system_manager = get_system_manager() - if system_manager == SystemManagerType.systemd: - result = TestRun.executor.run_expect_success("systemctl get-default") - try: - name = result.stdout.split(".")[0].replace("-", "_") - return Runlevel[name] - except Exception: - raise Exception(f"Cannot parse '{result.output}' to runlevel.") - else: - result = TestRun.executor.run_expect_success("runlevel") - try: - split_output = result.stdout.split() - runlevel = Runlevel(int(split_output[1])) - return runlevel - except Exception: - raise Exception(f"Cannot parse '{result.output}' to runlevel.") - - -class Udev(object): - @staticmethod - def enable(): - TestRun.LOGGER.info("Enabling udev") - TestRun.executor.run_expect_success("udevadm control --start-exec-queue") - - @staticmethod - def disable(): - TestRun.LOGGER.info("Disabling udev") - TestRun.executor.run_expect_success("udevadm control --stop-exec-queue") - - @staticmethod - def trigger(): - TestRun.executor.run_expect_success("udevadm trigger") - - @staticmethod - def settle(): - TestRun.executor.run_expect_success("udevadm settle") - - -def drop_caches(level: DropCachesMode = DropCachesMode.ALL): - TestRun.executor.run_expect_success( - f"echo {level.value} > /proc/sys/vm/drop_caches") - - -def disable_memory_affecting_functions(): - """Disables system functions affecting memory""" - # Don't allow sshd to be killed in case of out-of-memory: - TestRun.executor.run( - "echo '-1000' > /proc/`cat /var/run/sshd.pid`/oom_score_adj" - ) - TestRun.executor.run( - "echo -17 > /proc/`cat /var/run/sshd.pid`/oom_adj" - ) # deprecated - TestRun.executor.run_expect_success( - f"echo {OvercommitMemoryMode.NEVER.value} > /proc/sys/vm/overcommit_memory" - ) - TestRun.executor.run_expect_success("echo '100' > /proc/sys/vm/overcommit_ratio") - TestRun.executor.run_expect_success( - "echo '64 64 32' > /proc/sys/vm/lowmem_reserve_ratio" - ) - TestRun.executor.run_expect_success("swapoff --all") - drop_caches(DropCachesMode.SLAB) - - -def defaultize_memory_affecting_functions(): - """Sets default values to system functions affecting memory""" - TestRun.executor.run_expect_success( - f"echo {OvercommitMemoryMode.DEFAULT.value} > /proc/sys/vm/overcommit_memory" - ) - TestRun.executor.run_expect_success("echo 50 > /proc/sys/vm/overcommit_ratio") - TestRun.executor.run_expect_success( - "echo '256 256 32' > /proc/sys/vm/lowmem_reserve_ratio" - ) - TestRun.executor.run_expect_success("swapon --all") - - -def get_free_memory(): - """Returns free amount of memory in bytes""" - output = TestRun.executor.run_expect_success("free -b") - output = output.stdout.splitlines() - for line in output: - if 'free' in line: - index = line.split().index('free') + 1 # 1st row has 1 element less than following rows - if 'Mem' in line: - mem_line = line.split() - - return Size(int(mem_line[index])) - - -def get_mem_available(): - """Returns amount of available memory from /proc/meminfo""" - cmd = "cat /proc/meminfo | grep MemAvailable | awk '{ print $2 }'" - mem_available = TestRun.executor.run(cmd).stdout - - return Size(int(mem_available), Unit.KibiByte) - - -def get_module_mem_footprint(module_name): - """Returns allocated size of specific module's metadata from /proc/vmallocinfo""" - cmd = f"cat /proc/vmallocinfo | grep {module_name} | awk '{{ print $2 }}' " - output_lines = TestRun.executor.run(cmd).stdout.splitlines() - memory_used = 0 - for line in output_lines: - memory_used += int(line) - - return Size(memory_used) - - -def allocate_memory(size: Size): - """Allocates given amount of memory""" - mount_ramfs() - TestRun.LOGGER.info(f"Allocating {size.get_value(Unit.MiB):0.2f} MiB of memory.") - bs = Size(1, Unit.Blocks512) - dd = ( - Dd() - .block_size(bs) - .count(math.ceil(size / bs)) - .input("/dev/zero") - .output(f"{MEMORY_MOUNT_POINT}/data") - ) - output = dd.run() - if output.exit_code != 0: - raise CmdException("Allocating memory failed.", output) - - -def get_number_of_processors_from_cpuinfo(): - """Returns number of processors (count) which are listed out in /proc/cpuinfo""" - cmd = f"cat /proc/cpuinfo | grep processor | wc -l" - output = TestRun.executor.run(cmd).stdout - - return int(output) - - -def get_number_of_processes(process_name): - cmd = f"ps aux | grep {process_name} | grep -v grep | wc -l" - output = TestRun.executor.run(cmd).stdout - - return int(output) - - -def mount_ramfs(): - """Mounts ramfs to enable allocating memory space""" - if not check_if_directory_exists(MEMORY_MOUNT_POINT): - create_directory(MEMORY_MOUNT_POINT) - if not is_mounted(MEMORY_MOUNT_POINT): - TestRun.executor.run_expect_success(f"mount -t ramfs ramfs {MEMORY_MOUNT_POINT}") - - -def unmount_ramfs(): - """Unmounts ramfs and releases whole space allocated by it in memory""" - TestRun.executor.run_expect_success(f"umount {MEMORY_MOUNT_POINT}") - - -def download_file(url, destination_dir="/tmp"): - # TODO use wget module instead - command = ("wget --tries=3 --timeout=5 --continue --quiet " - f"--directory-prefix={destination_dir} {url}") - TestRun.executor.run_expect_success(command) - path = f"{destination_dir.rstrip('/')}/{File.get_name(url)}" - return File(path) - - -def get_kernel_version(): - version_string = TestRun.executor.run_expect_success("uname -r").stdout - version_string = version_string.split('-')[0] - return version.Version(version_string) - - -class ModuleRemoveMethod(Enum): - rmmod = "rmmod" - modprobe = "modprobe -r" - - -def is_kernel_module_loaded(module_name): - output = TestRun.executor.run(f"lsmod | grep ^{module_name}") - return output.exit_code == 0 - - -def get_sys_block_path(): - sys_block = "/sys/class/block" - if not check_if_directory_exists(sys_block): - sys_block = "/sys/block" - return sys_block - - -def load_kernel_module(module_name, module_args: {str, str}=None): - cmd = f"modprobe {module_name}" - if module_args is not None: - for key, value in module_args.items(): - cmd += f" {key}={value}" - return TestRun.executor.run(cmd) - - -def unload_kernel_module(module_name, unload_method: ModuleRemoveMethod = ModuleRemoveMethod.rmmod): - cmd = f"{unload_method.value} {module_name}" - return TestRun.executor.run_expect_success(cmd) - - -def get_kernel_module_parameter(module_name, parameter): - param_file_path = f"/sys/module/{module_name}/parameters/{parameter}" - if not check_if_file_exists(param_file_path): - raise FileNotFoundError(f"File {param_file_path} does not exist!") - return File(param_file_path).read() - - -def is_mounted(path: str): - if path is None or path.isspace(): - raise Exception("Checked path cannot be empty") - command = f"mount | grep --fixed-strings '{path.rstrip('/')} '" - return TestRun.executor.run(command).exit_code == 0 - - -def mount_debugfs(): - if not is_mounted(DEBUGFS_MOUNT_POINT): - TestRun.executor.run_expect_success(f"mount -t debugfs none {DEBUGFS_MOUNT_POINT}") - - -def reload_kernel_module(module_name, module_args: {str, str}=None, - unload_method: ModuleRemoveMethod = ModuleRemoveMethod.rmmod): - if is_kernel_module_loaded(module_name): - unload_kernel_module(module_name, unload_method) - - Retry.run_while_false( - lambda: load_kernel_module(module_name, module_args).exit_code == 0, - timeout=timedelta(seconds=5) - ) - - -def get_module_path(module_name): - cmd = f"modinfo {module_name}" - - # module path is in second column of first line of `modinfo` output - module_info = TestRun.executor.run_expect_success(cmd).stdout - module_path = module_info.splitlines()[0].split()[1] - - return module_path - - -def get_executable_path(exec_name): - cmd = f"which {exec_name}" - - path = TestRun.executor.run_expect_success(cmd).stdout - - return path - - -def get_udev_service_path(unit_name): - cmd = f"systemctl cat {unit_name}" - - # path is in second column of first line of output - info = TestRun.executor.run_expect_success(cmd).stdout - path = info.splitlines()[0].split()[1] - - return path - - -def kill_all_io(): - # TERM signal should be used in preference to the KILL signal, since a - # process may install a handler for the TERM signal in order to perform - # clean-up steps before terminating in an orderly fashion. - TestRun.executor.run("killall -q --signal TERM dd fio blktrace") - time.sleep(3) - TestRun.executor.run("killall -q --signal KILL dd fio blktrace") - TestRun.executor.run("kill -9 `ps aux | grep -i vdbench.* | awk '{ print $2 }'`") - - if TestRun.executor.run("pgrep -x dd").exit_code == 0: - raise Exception(f"Failed to stop dd!") - if TestRun.executor.run("pgrep -x fio").exit_code == 0: - raise Exception(f"Failed to stop fio!") - if TestRun.executor.run("pgrep -x blktrace").exit_code == 0: - raise Exception(f"Failed to stop blktrace!") - if TestRun.executor.run("pgrep vdbench").exit_code == 0: - raise Exception(f"Failed to stop vdbench!") - - -def wait(predicate, timeout: timedelta, interval: timedelta = None): - start_time = datetime.now() - result = False - while start_time + timeout > datetime.now(): - result = predicate() - if result: - break - if interval is not None: - time.sleep(interval.total_seconds()) - return result - - -def sync(): - TestRun.executor.run_expect_success("sync") - - -def get_dut_cpu_number(): - return int(TestRun.executor.run_expect_success("nproc").stdout) - - -def get_dut_cpu_physical_cores(): - """ Get list of CPU numbers that don't share physical cores """ - output = TestRun.executor.run_expect_success("lscpu --all --parse").stdout - - core_list = [] - visited_phys_cores = [] - for line in output.split("\n"): - if "#" in line: - continue - - cpu_no, phys_core_no = line.split(",")[:2] - if phys_core_no not in visited_phys_cores: - core_list.append(cpu_no) - visited_phys_cores.append(phys_core_no) - - return core_list - - -def set_wbt_lat(device: Device, value: int): - if value < 0: - raise ValueError("Write back latency can't be negative number") - - wbt_lat_config_path = posixpath.join( - get_sysfs_path(device.get_device_id()), "queue/wbt_lat_usec" - ) - - return TestRun.executor.run_expect_success(f"echo {value} > {wbt_lat_config_path}") - - -def get_wbt_lat(device: Device): - wbt_lat_config_path = posixpath.join( - get_sysfs_path(device.get_device_id()), "queue/wbt_lat_usec" - ) - - return int(TestRun.executor.run_expect_success(f"cat {wbt_lat_config_path}").stdout) - - -def get_cores_ids_range(numa_node: int): - output = TestRun.executor.run_expect_success(f"lscpu --all --parse").stdout - parse_output = re.findall(r'(\d+),(\d+),(?:\d+),(\d+),,', output, re.I) - - return [element[0] for element in parse_output if int(element[2]) == numa_node] diff --git a/test/functional/test-framework/test_utils/output.py b/test/functional/test-framework/test_utils/output.py deleted file mode 100644 index 73e8f94..0000000 --- a/test/functional/test-framework/test_utils/output.py +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -class Output: - def __init__(self, output_out, output_err, return_code): - self.stdout = output_out.decode('utf-8', errors="ignore").rstrip() if \ - type(output_out) == bytes else output_out - self.stderr = output_err.decode('utf-8', errors="ignore").rstrip() if \ - type(output_err) == bytes else output_err - self.exit_code = return_code - - def __str__(self): - return f"exit_code: {self.exit_code}\nstdout: {self.stdout}\nstderr: {self.stderr}" - - -class CmdException(Exception): - def __init__(self, message: str, output: Output): - super().__init__(f"{message}\n{str(output)}") - self.output = output diff --git a/test/functional/test-framework/test_utils/retry.py b/test/functional/test-framework/test_utils/retry.py deleted file mode 100644 index 10ca573..0000000 --- a/test/functional/test-framework/test_utils/retry.py +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright(c) 2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from datetime import datetime, timedelta -from functools import partial - -from core.test_run import TestRun - - -class Retry: - """ - The Retry class implements methods designed to retry execution until desired result. - The func parameter is meant to be a method. If this method needs args/kwargs, they should be - encapsulated with the method, i.e. using a partial function (an example of this is contained - within run_command_until_success()) - """ - @classmethod - def run_command_until_success( - cls, command: str, retries: int = None, timeout: timedelta = None - ): - # encapsulate method and args/kwargs as a partial function - func = partial(TestRun.executor.run_expect_success, command) - return cls.run_while_exception(func, retries=retries, timeout=timeout) - - @classmethod - def run_while_exception(cls, func, retries: int = None, timeout: timedelta = None): - result = None - - def wrapped_func(): - nonlocal result - try: - result = func() - return True - except: - return False - - cls.run_while_false(wrapped_func, retries=retries, timeout=timeout) - return result - - @classmethod - def run_while_false(cls, func, retries: int = None, timeout: timedelta = None): - if retries is None and timeout is None: - raise AttributeError("At least one stop condition is required for Retry calls!") - start = datetime.now() - retry_calls = 0 - result = func() - - while not result: - result = func() - retry_calls += 1 - if result \ - or (timeout is not None and datetime.now() - start > timeout) \ - or (retries is not None and retry_calls == retries): - break - return result diff --git a/test/functional/test-framework/test_utils/scsi_debug.py b/test/functional/test-framework/test_utils/scsi_debug.py deleted file mode 100644 index d5fea09..0000000 --- a/test/functional/test-framework/test_utils/scsi_debug.py +++ /dev/null @@ -1,77 +0,0 @@ -# -# Copyright(c) 2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import re - -from core.test_run import TestRun - -syslog_path = "/var/log/messages" - - -class Logs: - last_read_line = 1 - FLUSH = re.compile(r"scsi_debug:[\s\S]*cmd 35") - FUA = re.compile(r"scsi_debug:[\s\S]*cmd 2a 08") - - @staticmethod - def check_syslog_for_signals(): - Logs.check_syslog_for_flush() - Logs.check_syslog_for_fua() - - @staticmethod - def check_syslog_for_flush(): - """Check syslog for FLUSH logs""" - log_lines = Logs._read_syslog(Logs.last_read_line) - flush_logs_counter = Logs._count_logs(log_lines, Logs.FLUSH) - log_type = "FLUSH" - Logs._validate_logs_amount(flush_logs_counter, log_type) - - @staticmethod - def check_syslog_for_fua(): - """Check syslog for FUA logs""" - log_lines = Logs._read_syslog(Logs.last_read_line) - fua_logs_counter = Logs._count_logs(log_lines, Logs.FUA) - log_type = "FUA" - Logs._validate_logs_amount(fua_logs_counter, log_type) - - @staticmethod - def _read_syslog(last_read_line: int): - """Read recent lines in syslog, mark last line and return read lines as list.""" - log_lines = TestRun.executor.run_expect_success( - f"tail -qn +{last_read_line} {syslog_path}" - ).stdout.splitlines() - # mark last read line to continue next reading from here - Logs.last_read_line += len(log_lines) - - return log_lines - - @staticmethod - def _count_logs(log_lines: list, expected_log): - """Count specified log in list and return its amount.""" - logs_counter = 0 - - for line in log_lines: - is_log_in_line = expected_log.search(line) - if is_log_in_line is not None: - logs_counter += 1 - - return logs_counter - - @staticmethod - def _validate_logs_amount(logs_counter: int, log_type: str): - """Validate amount of logs and return""" - if logs_counter == 0: - if Logs._is_flush(log_type): - TestRun.LOGGER.error(f"{log_type} log not occured") - else: - TestRun.LOGGER.warning(f"{log_type} log not occured") - elif logs_counter == 1: - TestRun.LOGGER.warning(f"{log_type} log occured only once.") - else: - TestRun.LOGGER.info(f"{log_type} log occured {logs_counter} times.") - - @staticmethod - def _is_flush(log_type: str): - return log_type == "FLUSH" diff --git a/test/functional/test-framework/test_utils/singleton.py b/test/functional/test-framework/test_utils/singleton.py deleted file mode 100644 index a484929..0000000 --- a/test/functional/test-framework/test_utils/singleton.py +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - - -class Singleton(type): - """ - Singleton class - """ - _instances = {} - - def __call__(cls, *args, **kwargs): - if cls not in cls._instances: - cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) - return cls._instances[cls] diff --git a/test/functional/test-framework/test_utils/size.py b/test/functional/test-framework/test_utils/size.py deleted file mode 100644 index 8efcedf..0000000 --- a/test/functional/test-framework/test_utils/size.py +++ /dev/null @@ -1,211 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -import enum -import math - -from multimethod import multimethod - - -def parse_unit(str_unit: str): - for u in Unit: - if str_unit == u.name: - return u - - if str_unit == "KiB": - return Unit.KibiByte - elif str_unit in ["4KiB blocks", "4KiB Blocks"]: - return Unit.Blocks4096 - elif str_unit == "MiB": - return Unit.MebiByte - elif str_unit == "GiB": - return Unit.GibiByte - elif str_unit == "TiB": - return Unit.TebiByte - - if str_unit == "B": - return Unit.Byte - elif str_unit == "KB": - return Unit.KiloByte - elif str_unit == "MB": - return Unit.MegaByte - elif str_unit == "GB": - return Unit.GigaByte - elif str_unit == "TB": - return Unit.TeraByte - - raise ValueError(f"Unable to parse {str_unit}") - - -class Unit(enum.Enum): - Byte = 1 - KiloByte = 1000 - KibiByte = 1024 - MegaByte = 1000 * KiloByte - MebiByte = 1024 * KibiByte - GigaByte = 1000 * MegaByte - GibiByte = 1024 * MebiByte - TeraByte = 1000 * GigaByte - TebiByte = 1024 * GibiByte - Blocks512 = 512 - Blocks4096 = 4096 - - KiB = KibiByte - KB = KiloByte - MiB = MebiByte - MB = MegaByte - GiB = GibiByte - GB = GigaByte - TiB = TebiByte - TB = TeraByte - - def get_value(self): - return self.value - - def __str__(self): - return self.get_name() - - def get_name(self): - return self.name - - def get_short_name(self): - if self == Unit.Byte: - return "B" - elif self == Unit.KibiByte: - return "KiB" - elif self == Unit.KiloByte: - return "KB" - elif self == Unit.MebiByte: - return "MiB" - elif self == Unit.MegaByte: - return "MB" - elif self == Unit.GibiByte: - return "GiB" - elif self == Unit.GigaByte: - return "GB" - elif self == Unit.TebiByte: - return "TiB" - elif self == Unit.TeraByte: - return "TB" - raise ValueError(f"Unable to get short unit name for {self}.") - - -class UnitPerSecond: - def __init__(self, unit): - self.value = unit.get_value() - self.name = unit.name + "/s" - - def get_value(self): - return self.value - - -class Size: - def __init__(self, value: float, unit: Unit = Unit.Byte): - if value < 0: - raise ValueError("Size has to be positive.") - self.value = value * unit.value - self.unit = unit - - def __str__(self): - return f"{self.get_value(self.unit)} {self.unit}" - - def __hash__(self): - return self.value.__hash__() - - def __int__(self): - return int(self.get_value()) - - def __add__(self, other): - return Size(self.get_value() + other.get_value()) - - def __lt__(self, other): - return self.get_value() < other.get_value() - - def __le__(self, other): - return self.get_value() <= other.get_value() - - def __eq__(self, other): - return self.get_value() == other.get_value() - - def __ne__(self, other): - return self.get_value() != other.get_value() - - def __gt__(self, other): - return self.get_value() > other.get_value() - - def __ge__(self, other): - return self.get_value() >= other.get_value() - - def __radd__(self, other): - return Size(other + self.get_value()) - - def __sub__(self, other): - if self < other: - raise ValueError("Subtracted value is too big. Result size cannot be negative.") - return Size(self.get_value() - other.get_value()) - - @multimethod - def __mul__(self, other: int): - return Size(math.ceil(self.get_value() * other)) - - @multimethod - def __rmul__(self, other: int): - return Size(math.ceil(self.get_value() * other)) - - @multimethod - def __mul__(self, other: float): - return Size(math.ceil(self.get_value() * other)) - - @multimethod - def __rmul__(self, other: float): - return Size(math.ceil(self.get_value() * other)) - - @multimethod - def __truediv__(self, other): - if other.get_value() == 0: - raise ValueError("Divisor must not be equal to 0.") - return self.get_value() / other.get_value() - - @multimethod - def __truediv__(self, other: int): - if other == 0: - raise ValueError("Divisor must not be equal to 0.") - return Size(math.ceil(self.get_value() / other)) - - def set_unit(self, new_unit: Unit): - new_size = Size(self.get_value(target_unit=new_unit), unit=new_unit) - - if new_size != self: - raise ValueError(f"{new_unit} is not precise enough for {self}") - - self.value = new_size.value - self.unit = new_size.unit - - return self - - def get_value(self, target_unit: Unit = Unit.Byte): - return self.value / target_unit.value - - def is_zero(self): - if self.value == 0: - return True - else: - return False - - def align_up(self, alignment): - if self == self.align_down(alignment): - return Size(int(self)) - return Size(int(self.align_down(alignment)) + alignment) - - def align_down(self, alignment): - if alignment <= 0: - raise ValueError("Alignment must be a positive value!") - if alignment & (alignment - 1): - raise ValueError("Alignment must be a power of two!") - return Size(int(self) & ~(alignment - 1)) - - @staticmethod - def zero(): - return Size(0) diff --git a/test/functional/test-framework/test_utils/systemd.py b/test/functional/test-framework/test_utils/systemd.py deleted file mode 100644 index dc4bcfd..0000000 --- a/test/functional/test-framework/test_utils/systemd.py +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright(c) 2019-2022 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from pathlib import Path - -from core.test_run import TestRun - -systemd_service_directory = Path("/usr/lib/systemd/system/") - -def enable_service(name): - TestRun.executor.run_expect_success(f"systemctl enable {name}") - - -def disable_service(name): - TestRun.executor.run_expect_success(f"systemctl disable {name}") - - -def reload_daemon(): - TestRun.executor.run_expect_success("systemctl daemon-reload") - - -def restart_service(name): - TestRun.executor.run_expect_success(f"systemctl restart {name}") diff --git a/test/functional/test-framework/test_utils/time.py b/test/functional/test-framework/test_utils/time.py deleted file mode 100644 index af7268f..0000000 --- a/test/functional/test-framework/test_utils/time.py +++ /dev/null @@ -1,14 +0,0 @@ -# -# Copyright(c) 2019-2021 Intel Corporation -# SPDX-License-Identifier: BSD-3-Clause -# - -from attotime import attotimedelta - - -class Time(attotimedelta): - def total_microseconds(self): - return self.total_nanoseconds() / 1_000 - - def total_milliseconds(self): - return self.total_nanoseconds() / 1_000_000