Skip to content

Commit f43d5eb

Browse files
feat(tests): integrate with xcp-ng fixtures
- fix(disk_perf): add configurable numjob - fix(disk_perf): styling and readability - misc(disk_perf): simplify fixtures - misc(disk_perf): styling - fix(disk_perf): usage of double quote in nested string Signed-off-by: Mathieu Labourier <[email protected]>
1 parent 500f788 commit f43d5eb

File tree

3 files changed

+128
-64
lines changed

3 files changed

+128
-64
lines changed

tests/storage/benchmarks/conftest.py

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,90 @@
33
import os
44
import logging
55

6-
from helpers import load_results_from_csv
6+
from lib.commands import SSHCommandFailed
7+
from .helpers import load_results_from_csv
78

8-
@pytest.fixture(scope='package')
9-
def ext_sr(host, sr_disk):
10-
sr = host.sr_create('ext', "EXT-local-SR-test", {'device': '/dev/' + sr_disk})
11-
yield sr
12-
# teardown
13-
sr.destroy()
14-
15-
@pytest.fixture(scope='module', params=['raw', 'vhd', 'qcow2'])
16-
def disk_on_ext_sr(request, ext_sr):
17-
disk_type = request.param
18-
disk = {}
19-
if disk_type == 'raw':
20-
...
21-
elif disk_type == 'vhd':
22-
...
23-
elif disk_type == 'qcow2':
24-
...
9+
MAX_LENGTH = 64 * (1024**3) # 64GiB
10+
11+
# use vhd, qcow2, raw... when image_format support will be available
12+
@pytest.fixture(scope="module", params=['vdi'])
13+
def image_format(request):
14+
return request.param
15+
16+
@pytest.fixture(scope="module")
17+
def running_unix_vm_with_fio(running_unix_vm):
18+
vm = running_unix_vm
19+
install_cmds = (
20+
("command -v apt", "apt update && apt install -y fio", "apt remove -y fio"),
21+
("command -v dnf", "dnf install -y fio", "dnf remove -y fio"),
22+
("command -v yum", "yum install -y fio", "yum remove -y fio"),
23+
("command -v apk", "apk add fio", "apk del fio")
24+
)
25+
26+
for check_cmd, install_cmd, remove in install_cmds:
27+
try:
28+
vm.ssh(check_cmd, check=True)
29+
logging.info(f">> Installing fio with {install_cmd}")
30+
vm.ssh(install_cmd, check=True)
31+
remove_cmd = remove
32+
break
33+
except SSHCommandFailed:
34+
...
2535
else:
26-
raise ValueError(f"Unsupported disk type: {disk_type}")
36+
raise RuntimeError("Unsupported package manager: could not install fio")
2737

28-
yield disk
38+
yield vm
2939

3040
# teardown
31-
...
41+
logging.info(f"<< Removing fio with {remove_cmd}")
42+
vm.ssh(remove_cmd, check=False)
43+
44+
45+
@pytest.fixture(scope="module")
46+
def vdi_on_local_sr(host, local_sr_on_hostA1, image_format):
47+
sr = local_sr_on_hostA1
48+
vdi = sr.create_vdi("testVDI", MAX_LENGTH)
49+
vdi.image_format = image_format
50+
logging.info(f">> Created VDI {vdi.uuid} of type {image_format}")
51+
52+
yield vdi
3253

33-
@pytest.fixture(scope='module')
34-
def vm_on_ext_sr(host, ext_sr, vm_ref):
35-
vm = host.import_vm(vm_ref, sr_uuid=ext_sr.uuid)
36-
yield vm
3754
# teardown
38-
logging.info("<< Destroy VM")
39-
vm.destroy(verify=True)
55+
logging.info(f"<< Destroying VDI {vdi.uuid}")
56+
vdi.destroy()
4057

41-
@pytest.fixture
42-
def temp_dir():
58+
@pytest.fixture(scope="module")
59+
def plugged_vbd(vdi_on_local_sr, running_unix_vm_with_fio):
60+
vm = running_unix_vm_with_fio
61+
vdi = vdi_on_local_sr
62+
vbd = vm.create_vbd("autodetect", vdi.uuid)
63+
64+
logging.info(f">> Plugging VDI {vdi.uuid} on VM {vm.uuid}")
65+
vbd.plug()
66+
67+
yield vbd
68+
69+
# teardown
70+
logging.info(f"<< Unplugging VDI {vdi.uuid} from VM {vm.uuid}")
71+
vbd.unplug()
72+
vbd.destroy()
73+
74+
@pytest.fixture(scope="module")
75+
def local_temp_dir():
4376
with tempfile.TemporaryDirectory() as tmpdir:
4477
yield tmpdir
4578

79+
@pytest.fixture(scope="module")
80+
def temp_dir(running_unix_vm_with_fio):
81+
vm = running_unix_vm_with_fio
82+
tempdir = vm.ssh("mktemp -d")
83+
84+
yield tempdir
85+
86+
# teardown
87+
vm.ssh(f"rm -r {tempdir}")
88+
89+
4690
def pytest_addoption(parser):
4791
parser.addoption(
4892
"--prev-csv",
@@ -57,4 +101,4 @@ def prev_results(request):
57101
results = {}
58102
if csv_path and os.path.exists(csv_path):
59103
load_results_from_csv(csv_path)
60-
return results
104+
return results

tests/storage/benchmarks/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,4 @@ def load_results_from_csv(csv_path):
4545
def mean(data, key):
4646
return statistics.mean(
4747
[float(x[key]) for x in data if key in x]
48-
)
48+
)

tests/storage/benchmarks/test_disk_perf.py

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,54 @@
22
import os
33
import json
44
import statistics
5-
import subprocess
65
import pytest
76
import logging
87
from datetime import datetime
98

10-
from helpers import load_results_from_csv, log_result_csv, mean
9+
from lib.commands import SSHCommandFailed
10+
from .helpers import load_results_from_csv, log_result_csv, mean
1111

12-
### Tests default settings ###
12+
# Tests default settings #
1313

14-
CSV_FILE = f"/tmp/results_{datetime.now().strftime("%Y-%m-%d_%H:%M:%S")}.csv"
14+
CSV_FILE = f"/tmp/results_{datetime.now().strftime('%Y-%m-%d_%H:%M:%S')}.csv"
1515

1616
DEFAULT_SAMPLES_NUM = 10
1717
DEFAULT_SIZE = "1G"
1818
DEFAULT_BS = "4k"
1919
DEFAULT_IODEPTH = 1
20+
DEFAULT_NUMJOBS = 1
2021
DEFAULT_FILE = "fio-testfile"
2122

22-
### Tests parameters
23+
# Tests parameters #
2324

2425
system_memory = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES')
2526

2627
block_sizes = ("4k", "16k", "64k", "1M")
27-
file_sizes = ("1G", "4G", f"{(system_memory/(1024.**3))*2}G")
28+
file_sizes = ("1G", "4G", f"{int((system_memory // (1024.**3)) * 2)}G")
29+
2830
modes = (
29-
"read",
30-
"randread",
31-
"write",
32-
"randwrite"
31+
"read",
32+
"randread",
33+
"write",
34+
"randwrite"
3335
)
3436

35-
test_types = {
36-
"read": "seq_read",
37-
"randread": "rand_read",
38-
"write": "seq_write",
39-
"randwrite": "rand_write"
40-
}
41-
42-
### End of tests parameters ###
37+
# End of tests parameters #
4338

4439
def run_fio(
40+
vm,
4541
test_name,
4642
rw_mode,
4743
temp_dir,
44+
local_temp_dir,
4845
bs=DEFAULT_BS,
4946
iodepth=DEFAULT_IODEPTH,
5047
size=DEFAULT_SIZE,
48+
numjobs=DEFAULT_NUMJOBS,
5149
file_path="",
5250
):
5351
json_output_path = os.path.join(temp_dir, f"{test_name}.json")
52+
local_json_path = os.path.join(local_temp_dir, f"{test_name}.json")
5453
if not file_path:
5554
file_path = os.path.join(temp_dir, DEFAULT_FILE)
5655
fio_cmd = [
@@ -64,17 +63,19 @@ def run_fio(
6463
"--direct=1",
6564
"--end_fsync=1",
6665
"--fsync_on_close=1",
67-
"--numjobs=1",
66+
f"--numjobs={numjobs}",
6867
"--group_reporting",
6968
"--output-format=json",
7069
f"--output={json_output_path}"
7170
]
72-
73-
result = subprocess.run(fio_cmd, capture_output=True, text=True)
74-
if result.returncode != 0:
75-
raise RuntimeError(f"fio failed for {test_name}:\n{result.stderr}")
76-
77-
with open(json_output_path) as f:
71+
logging.debug(f"Running {fio_cmd}")
72+
try:
73+
vm.ssh(fio_cmd, check=True)
74+
except SSHCommandFailed as e:
75+
raise RuntimeError(f"fio failed for {test_name}:{e}")
76+
vm.scp(json_output_path, local_json_path, local_dest=True)
77+
logging.debug(f"Stored json at {local_json_path}")
78+
with open(local_json_path) as f:
7879
return json.load(f)
7980

8081
def assert_performance_not_degraded(current, previous, threshold=10):
@@ -86,7 +87,7 @@ def assert_performance_not_degraded(current, previous, threshold=10):
8687
except statistics.StatisticsError:
8788
logging.info(f"Missing metric ({metric}), skipping comparison")
8889
continue
89-
diff = (curr-prev if metric == "latency" else prev-curr) / (prev * 100)
90+
diff = (curr - prev if metric == "latency" else prev - curr) / (prev * 100)
9091
assert diff <= threshold, \
9192
f"{metric} changed by {diff:.2f}% (allowed {threshold}%)"
9293
diffs[metric] = diff
@@ -97,27 +98,46 @@ def assert_performance_not_degraded(current, previous, threshold=10):
9798
logging.info(f"- {k}: {sign}{abs(v):.2f}%")
9899

99100

100-
class TestDiskPerfDestroy: ...
101-
102-
103101
class TestDiskPerf:
104102
test_cases = itertools.product(block_sizes, file_sizes, modes)
105103

106104
@pytest.mark.parametrize("block_size,file_size,rw_mode", test_cases)
107105
def test_disk_benchmark(
108106
self,
109107
temp_dir,
108+
local_temp_dir,
110109
prev_results,
111110
block_size,
112111
file_size,
113-
rw_mode
112+
rw_mode,
113+
running_unix_vm_with_fio,
114+
plugged_vbd,
115+
image_format
114116
):
115-
test_type = test_types[rw_mode]
117+
vm = running_unix_vm_with_fio
118+
vbd = plugged_vbd
119+
device = f"/dev/{vbd.param_get(param_name='device')}"
120+
test_type = "{}-{}-{}-{}".format(
121+
block_size,
122+
file_size,
123+
rw_mode,
124+
image_format
125+
)
126+
116127
for i in range(DEFAULT_SAMPLES_NUM):
117-
result = run_fio(test_type, rw_mode, temp_dir)
128+
result = run_fio(
129+
vm,
130+
test_type,
131+
rw_mode,
132+
temp_dir,
133+
local_temp_dir,
134+
file_path=device,
135+
bs=block_size,
136+
size=file_size
137+
)
118138
summary = log_result_csv(test_type, rw_mode, result, CSV_FILE)
119139
assert summary["IOPS"] > 0
120-
results = load_results_from_csv(CSV_FILE)
121140
key = (test_type, rw_mode)
122141
if prev_results and key in prev_results:
142+
results = load_results_from_csv(CSV_FILE)
123143
assert_performance_not_degraded(results[key], prev_results[key])

0 commit comments

Comments
 (0)