From 0c4268b11d3428279e8d5f70c2f5b23d8b328d12 Mon Sep 17 00:00:00 2001 From: Erwan Velu Date: Tue, 20 Aug 2024 16:57:44 +0200 Subject: [PATCH] fio: Adding fio engine This first commit is adding a first cmdline engine_module to execute a single fio command line. This commit is not functional yet but set the base of the logic to parse the engine. Signed-off-by: Erwan Velu --- configs/fio.conf | 14 +++ hwbench/bench/test_fio.py | 25 +++++ hwbench/config/fio.conf | 14 +++ hwbench/config/test_parse_fio.py | 27 +++++ hwbench/engines/fio.py | 105 ++++++++++++++++++ hwbench/engines/test_parse_fio.py | 26 +++++ hwbench/tests/parsing/fio/v319/version | 1 + hwbench/tests/parsing/fio/v319/version-stderr | 0 hwbench/tests/parsing/fio/v319/version-stdout | 1 + 9 files changed, 213 insertions(+) create mode 100644 configs/fio.conf create mode 100644 hwbench/bench/test_fio.py create mode 100644 hwbench/config/fio.conf create mode 100644 hwbench/config/test_parse_fio.py create mode 100644 hwbench/engines/fio.py create mode 100644 hwbench/engines/test_parse_fio.py create mode 100644 hwbench/tests/parsing/fio/v319/version create mode 100644 hwbench/tests/parsing/fio/v319/version-stderr create mode 100644 hwbench/tests/parsing/fio/v319/version-stdout diff --git a/configs/fio.conf b/configs/fio.conf new file mode 100644 index 0000000..1a53ecb --- /dev/null +++ b/configs/fio.conf @@ -0,0 +1,14 @@ +# This configuration will : +# - load all cores with a matrixprod test during 15 sec. +[global] +runtime=15 +monitor=all + +[randread_cmdline] +engine=fio +engine_module=cmdline +engine_module_parameter_base="--filename=/dev/sdp --direct=1 --rw=randread --bs=4k --ioengine=libaio --iodepth=256 --numjobs=4 --time_based --group_reporting --readonly" +hosting_cpu_cores=all +hosting_cpu_cores_scaling=none +stressor_range=auto + diff --git a/hwbench/bench/test_fio.py b/hwbench/bench/test_fio.py new file mode 100644 index 0000000..29353ac --- /dev/null +++ b/hwbench/bench/test_fio.py @@ -0,0 +1,25 @@ +from . import test_benchmarks_common as tbc + + +class TestFio(tbc.TestCommon): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.load_mocked_hardware( + cpucores="./tests/parsing/cpu_cores/v2321", + cpuinfo="./tests/parsing/cpu_info/v2321", + numa="./tests/parsing/numa/8domainsllc", + ) + self.load_benches("./config/fio.conf") + self.parse_jobs_config() + self.QUADRANT0 = list(range(0, 16)) + list(range(64, 80)) + self.QUADRANT1 = list(range(16, 32)) + list(range(80, 96)) + self.ALL = list(range(0, 128)) + + def test_fio(self): + """Check fio syntax.""" + assert self.benches.count_benchmarks() == 1 + assert self.benches.count_jobs() == 1 + assert self.benches.runtime() == 15 + self.assertIsNone(self.benches.benchs[0].validate_parameters()) + bench = self.get_bench_parameters(0) + assert bench.get_name() == "randread_cmdline" diff --git a/hwbench/config/fio.conf b/hwbench/config/fio.conf new file mode 100644 index 0000000..1a53ecb --- /dev/null +++ b/hwbench/config/fio.conf @@ -0,0 +1,14 @@ +# This configuration will : +# - load all cores with a matrixprod test during 15 sec. +[global] +runtime=15 +monitor=all + +[randread_cmdline] +engine=fio +engine_module=cmdline +engine_module_parameter_base="--filename=/dev/sdp --direct=1 --rw=randread --bs=4k --ioengine=libaio --iodepth=256 --numjobs=4 --time_based --group_reporting --readonly" +hosting_cpu_cores=all +hosting_cpu_cores_scaling=none +stressor_range=auto + diff --git a/hwbench/config/test_parse_fio.py b/hwbench/config/test_parse_fio.py new file mode 100644 index 0000000..0937f19 --- /dev/null +++ b/hwbench/config/test_parse_fio.py @@ -0,0 +1,27 @@ +import pathlib +from unittest.mock import patch +from ..environment.mock import MockHardware +from ..bench import test_benchmarks_common as tbc + + +class TestParseConfig(tbc.TestCommon): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.hw = MockHardware() + self.load_benches("./config/fio.conf") + + def test_sections_name(self): + """Check if sections names are properly detected.""" + sections = self.get_jobs_config().get_sections() + assert sections == [ + "randread_cmdline", + ] + + def test_keywords(self): + """Check if all keywords are valid.""" + try: + with patch("hwbench.utils.helpers.is_binary_available") as iba: + iba.return_value = True + self.get_jobs_config().validate_sections() + except Exception as exc: + assert False, f"'validate_sections' detected a syntax error {exc}" diff --git a/hwbench/engines/fio.py b/hwbench/engines/fio.py new file mode 100644 index 0000000..1e489e9 --- /dev/null +++ b/hwbench/engines/fio.py @@ -0,0 +1,105 @@ +from ..bench.parameters import BenchmarkParameters +from ..bench.engine import EngineBase, EngineModuleBase +from ..bench.benchmark import ExternalBench + + +class EngineModuleCmdline(EngineModuleBase): + """This class implements the EngineModuleBase for fio""" + + def __init__(self, engine: EngineBase, engine_module_name: str, fake_stdout=None): + super().__init__(engine, engine_module_name) + self.engine_module_name = engine_module_name + self.load_module_parameter(fake_stdout) + + def load_module_parameter(self, fake_stdout=None): + # if needed add module parameters to your module + self.add_module_parameter("cmdline") + + def validate_module_parameters(self, p: BenchmarkParameters): + msg = super().validate_module_parameters(p) + Fio(self, p).parse_parameters() + return msg + + def run_cmd(self, p: BenchmarkParameters): + return Fio(self, p).run_cmd() + + def run(self, p: BenchmarkParameters): + return Fio(self, p).run() + + def fully_skipped_job(self, p) -> bool: + return Fio(self, p).fully_skipped_job() + + +class Engine(EngineBase): + """The main fio class.""" + + def __init__(self, fake_stdout=None): + super().__init__("fio", "fio") + self.add_module(EngineModuleCmdline(self, "cmdline", fake_stdout)) + + def run_cmd_version(self) -> list[str]: + return [ + self.get_binary(), + "--version", + ] + + def run_cmd(self) -> list[str]: + return [] + + def parse_version(self, stdout: bytes, _stderr: bytes) -> bytes: + print(stdout) + self.version = stdout.split(b"-")[1] + return self.version + + def version_major(self) -> int: + if self.version: + return int(self.version.split(b".")[0]) + return 0 + + def version_minor(self) -> int: + if self.version: + return int(self.version.split(b".")[1]) + return 0 + + def parse_cmd(self, stdout: bytes, stderr: bytes): + return {} + + +class Fio(ExternalBench): + """The Fio stressor.""" + + def __init__( + self, engine_module: EngineModuleBase, parameters: BenchmarkParameters + ): + ExternalBench.__init__(self, engine_module, parameters) + self.parameters = parameters + self.engine_module = engine_module + self.parse_parameters() + + def parse_parameters(self): + runtime = self.parameters.runtime + + def run_cmd(self) -> list[str]: + # Let's build the command line to run the tool + args = [ + self.engine_module.get_engine().get_binary(), + str(self.parameters.get_runtime()), + ] + + return self.get_taskset(args) + + def parse_cmd(self, stdout: bytes, stderr: bytes): + # Add the score to the global output + return self.parameters.get_result_format() | { + "bogo ops/s": self.parameters.get_runtime() + } + + @property + def name(self) -> str: + return self.engine_module.get_engine().get_name() + + def run_cmd_version(self) -> list[str]: + return self.engine_module.get_engine().run_cmd_version() + + def parse_version(self, stdout: bytes, _stderr: bytes) -> bytes: + return self.engine_module.get_engine().parse_version(stdout, _stderr) diff --git a/hwbench/engines/test_parse_fio.py b/hwbench/engines/test_parse_fio.py new file mode 100644 index 0000000..8c60676 --- /dev/null +++ b/hwbench/engines/test_parse_fio.py @@ -0,0 +1,26 @@ +import pathlib +import unittest +from unittest.mock import patch + +from .fio import Engine as Fio + + +def mock_engine() -> Fio: + with patch("hwbench.utils.helpers.is_binary_available") as iba: + iba.return_value = True + return Fio() + + +class TestParse(unittest.TestCase): + def test_engine_parsing_version(self): + test_dir = pathlib.Path("./tests/parsing/fio") + for d in test_dir.iterdir(): + test_target = mock_engine() + if not d.is_dir(): + continue + ver_stdout = (d / "version-stdout").read_bytes() + ver_stderr = (d / "version-stderr").read_bytes() + version = test_target.parse_version(ver_stdout, ver_stderr) + assert version == (d / "version").read_bytes().strip() + assert test_target.version_major() == 3 + assert test_target.version_minor() == 19 diff --git a/hwbench/tests/parsing/fio/v319/version b/hwbench/tests/parsing/fio/v319/version new file mode 100644 index 0000000..318956c --- /dev/null +++ b/hwbench/tests/parsing/fio/v319/version @@ -0,0 +1 @@ +3.19 \ No newline at end of file diff --git a/hwbench/tests/parsing/fio/v319/version-stderr b/hwbench/tests/parsing/fio/v319/version-stderr new file mode 100644 index 0000000..e69de29 diff --git a/hwbench/tests/parsing/fio/v319/version-stdout b/hwbench/tests/parsing/fio/v319/version-stdout new file mode 100644 index 0000000..b56bf77 --- /dev/null +++ b/hwbench/tests/parsing/fio/v319/version-stdout @@ -0,0 +1 @@ +fio-3.19 \ No newline at end of file