-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]>
- Loading branch information
1 parent
3a07a7e
commit 973a11c
Showing
10 changed files
with
298 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
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="./hwbench/tests/parsing/cpu_cores/v2321", | ||
cpuinfo="./hwbench/tests/parsing/cpu_info/v2321", | ||
numa="./hwbench/tests/parsing/numa/8domainsllc", | ||
) | ||
self.load_benches("./hwbench/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() == 2 | ||
assert self.benches.count_jobs() == 1 | ||
assert self.benches.runtime() == 30 | ||
|
||
for bench in self.benches.benchs: | ||
self.assertIsNone(bench.validate_parameters()) | ||
bench.get_parameters().get_name() == "randread_cmdline" | ||
|
||
bench_0 = self.get_bench_parameters(0) | ||
assert ( | ||
bench_0.get_engine_module_parameter_base() | ||
== "--runtime=15 --time_based --output-format=json+ --numjobs=4 --name=randread_cmdline_0 --numjobs=4 --filename=/dev/sdp --direct=1 --rw=randread --bs=4k --ioengine=libaio --iodepth=256 --time_based --group_reporting --readonly" | ||
) | ||
|
||
bench_1 = self.get_bench_parameters(1) | ||
assert ( | ||
bench_1.get_engine_module_parameter_base() | ||
== "--runtime=15 --time_based --output-format=json+ --numjobs=6 --name=randread_cmdline_1 --numjobs=6 --filename=/dev/sdp --direct=1 --rw=randread --bs=4k --ioengine=libaio --iodepth=256 --time_based --group_reporting --readonly" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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=4,6 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
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("./hwbench/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}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
from typing import Any | ||
|
||
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) | ||
FioCmdLine(self, p).parse_parameters(True) | ||
return msg | ||
|
||
def run_cmd(self, p: BenchmarkParameters): | ||
return FioCmdLine(self, p).run_cmd() | ||
|
||
def run(self, p: BenchmarkParameters): | ||
return FioCmdLine(self, p).run() | ||
|
||
def fully_skipped_job(self, p) -> bool: | ||
return FioCmdLine(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: | ||
self.version = stdout.split(b"-")[1].strip() | ||
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 version_compatible(self) -> bool: | ||
engine = self.engine_module.get_engine() | ||
return engine.version_major() >= 3 and engine.version_minor() >= 19 | ||
|
||
def parse_parameters(self): | ||
self.runtime = self.parameters.runtime | ||
|
||
def need_skip_because_version(self): | ||
if self.skip: | ||
# we already skipped this benchmark, we can't know the reason anymore | ||
# because we might not have run the version command. | ||
return ["echo", "skipped benchmark"] | ||
if not self.version_compatible(): | ||
print(f"WARNING: skipping benchmark {self.name}, needs fio >= 3.19") | ||
self.skip = True | ||
return ["echo", "skipped benchmark"] | ||
return None | ||
|
||
def run_cmd(self) -> list[str]: | ||
skip = self.need_skip_because_version() | ||
if skip: | ||
return skip | ||
|
||
# Let's build the command line to run the tool | ||
args = [ | ||
self.engine_module.get_engine().get_binary(), | ||
] | ||
|
||
return self.get_taskset(args) | ||
|
||
def get_default_fio_command_line(self) -> str: | ||
"""Return the default fio arguments""" | ||
cmdline = f"--runtime={self.parameters.get_runtime()}" | ||
cmdline += " --time_based" | ||
cmdline += " --output-format=json+" | ||
cmdline += f" --numjobs={self.parameters.get_engine_instances_count()}" | ||
cmdline += f" --name={self.parameters.get_name_with_position()}" | ||
return f"{cmdline} " | ||
|
||
def parse_cmd(self, stdout: bytes, stderr: bytes) -> dict[str, Any]: | ||
# Add the score to the global output | ||
if self.skip: | ||
return self.parameters.get_result_format() | self.empty_result() | ||
ret: dict[str, Any] = {} | ||
return ret | self.parameters.get_result_format() | ||
|
||
@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) | ||
|
||
def empty_result(self): | ||
"""Default empty results for fio""" | ||
return { | ||
"effective_runtime": 0, | ||
"skipped": True, | ||
} | ||
|
||
|
||
class FioCmdLine(Fio): | ||
def parse_parameters(self, fix_epmb=False): | ||
"""Removing fio arguments set by the engine""" | ||
# If we only mean to check parameters, let's return | ||
if not fix_epmb: | ||
return | ||
|
||
# We need to ensure we have a proper fio command line | ||
# Let's remove duplicated and enforce some | ||
args = self.parameters.get_engine_module_parameter_base().split() | ||
for argument in args: | ||
# These overrided arguments has a parameter, let's inform the user its dropped | ||
for keyword in ["--runtime", "--name", "--numjobs", "--output-format"]: | ||
if keyword in argument: | ||
args.remove(argument) | ||
print( | ||
f"{self.parameters.get_name_with_position()}: Overriding '{argument}' from engine module parameter base" | ||
) | ||
# These overrided arguments has no parameter, just prevent duplicates | ||
if argument.startswith("--time_based"): | ||
args.remove(argument) | ||
|
||
# Overriding empb to represent the real executed command | ||
self.parameters.engine_module_parameter_base = ( | ||
self.get_default_fio_command_line() + " ".join(args) | ||
) | ||
|
||
def run_cmd(self) -> list[str]: | ||
# Let's build the command line to run the tool | ||
return ( | ||
super().run_cmd() | ||
+ self.parameters.get_engine_module_parameter_base().split() | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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("./hwbench/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
3.19 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
fio-3.19 |