summaryrefslogtreecommitdiffstats
path: root/src/spdk/test/vhost/common/run_fio.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
commit483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch)
treee5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/spdk/test/vhost/common/run_fio.py
parentInitial commit. (diff)
downloadceph-upstream.tar.xz
ceph-upstream.zip
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/test/vhost/common/run_fio.py')
-rwxr-xr-xsrc/spdk/test/vhost/common/run_fio.py168
1 files changed, 168 insertions, 0 deletions
diff --git a/src/spdk/test/vhost/common/run_fio.py b/src/spdk/test/vhost/common/run_fio.py
new file mode 100755
index 00000000..0760b018
--- /dev/null
+++ b/src/spdk/test/vhost/common/run_fio.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import getopt
+import subprocess
+import signal
+import re
+
+fio_bin = "fio"
+
+
+def show_help():
+ print("""Usage: {} run_fio.py [options] [args]
+ Description:
+ Run FIO job file 'fio.job' on remote machines.
+ NOTE: The job file must exist on remote machines on '/root/' directory.
+ Args:
+ [VMs] (ex. vm1_IP:vm1_port:vm1_disk1:vm_disk2,vm2_IP:vm2_port:vm2_disk1,etc...)
+ Options:
+ -h, --help Show this message.
+ -j, --job-file Paths to file with FIO job configuration on remote host.
+ -f, --fio-bin Location of FIO binary on local host (Default "fio")
+ -o, --out Directory used to save generated job files and
+ files with test results
+ -J, --json Use JSON format for output
+ -p, --perf-vmex Enable aggregating statistic for VMEXITS for VMs
+ """.format(os.path.split(sys.executable)[-1]))
+
+
+def exec_cmd(cmd, blocking):
+ # Print result to STDOUT for now, we don't have json support yet.
+ p = subprocess.Popen(cmd.split(" "), stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
+ if blocking is True:
+ out, _ = p.communicate()
+ return p.returncode, out.decode()
+ return p
+
+
+def save_file(path, mode, contents):
+ with open(path, mode) as fh:
+ fh.write(contents)
+ fh.close()
+
+
+def run_fio(vms, fio_cfg_fname, out_path, perf_vmex=False, json=False):
+ global fio_bin
+ job_name = os.path.splitext(os.path.basename(fio_cfg_fname))[0]
+
+ # Build command for FIO
+ fio_cmd = " ".join([fio_bin, "--eta=never"])
+ if json:
+ fio_cmd = " ".join([fio_bin, "--output-format=json"])
+ for vm in vms:
+ # vm[0] = IP address, vm[1] = Port number
+ fio_cmd = " ".join([fio_cmd,
+ "--client={vm_ip},{vm_port}".format(vm_ip=vm[0], vm_port=vm[1]),
+ "--remote-config {cfg}".format(cfg=fio_cfg_fname)])
+ print(fio_cmd)
+
+ if perf_vmex:
+ perf_dir = os.path.join(out_path, "perf_stats")
+ try:
+ os.mkdir(perf_dir)
+ except OSError:
+ pass
+
+ # Start gathering perf statistics for host and VM guests
+ perf_rec_file = os.path.join(perf_dir, "perf.data.kvm")
+ perf_run_cmd = "perf kvm --host --guest " + \
+ "-o {0} stat record -a".format(perf_rec_file)
+ print(perf_run_cmd)
+ perf_p = exec_cmd(perf_run_cmd, blocking=False)
+
+ # Run FIO test on VMs
+ rc, out = exec_cmd(fio_cmd, blocking=True)
+
+ # if for some reason output contains lines with "eta" - remove them
+ out = re.sub(r'.+\[eta\s+\d{2}m:\d{2}s\]', '', out)
+
+ print(out)
+
+ if rc != 0:
+ print("ERROR! While executing FIO jobs - RC: {rc}".format(rc=rc, out=out))
+ sys.exit(rc)
+ else:
+ save_file(os.path.join(out_path, ".".join([job_name, "log"])), "w", out)
+
+ if perf_vmex:
+ # Stop gathering perf statistics and prepare some result files
+ perf_p.send_signal(signal.SIGINT)
+ perf_p.wait()
+
+ perf_stat_cmd = "perf kvm --host -i {perf_rec} stat report --event vmexit"\
+ .format(perf_rec=perf_rec_file)
+
+ rc, out = exec_cmd(" ".join([perf_stat_cmd, "--event vmexit"]),
+ blocking=True)
+ print("VMexit host stats:")
+ print("{perf_out}".format(perf_out=out))
+ save_file(os.path.join(perf_dir, "vmexit_stats_" + job_name),
+ "w", "{perf_out}".format(perf_out=out))
+ try:
+ os.remove(perf_rec_file)
+ except OSError:
+ pass
+
+
+def main():
+ global fio_bin
+
+ abspath = os.path.abspath(__file__)
+ dname = os.path.dirname(abspath)
+
+ vms = []
+ fio_cfg = None
+ out_dir = None
+ perf_vmex = False
+ json = False
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "hJj:f:o:p",
+ ["help", "job-file=", "fio-bin=",
+ "out=", "perf-vmex", "json"])
+ except getopt.GetoptError:
+ show_help()
+ sys.exit(1)
+
+ if len(args) < 1:
+ show_help()
+ sys.exit(1)
+
+ for o, a in opts:
+ if o in ("-j", "--job-file"):
+ fio_cfg = a
+ elif o in ("-h", "--help"):
+ show_help()
+ sys.exit(1)
+ elif o in ("-p", "--perf-vmex"):
+ perf_vmex = True
+ elif o in ("-o", "--out"):
+ out_dir = a
+ elif o in ("-f", "--fio-bin"):
+ fio_bin = a
+ elif o in ("-J", "--json"):
+ json = True
+
+ if fio_cfg is None:
+ print("ERROR! No FIO job provided!")
+ sys.exit(1)
+
+ if out_dir is None or not os.path.exists(out_dir):
+ print("ERROR! Folder {out_dir} does not exist ".format(out_dir=out_dir))
+ sys.exit(1)
+
+ # Get IP, port and fio 'filename' information from positional args
+ for arg in args[0].split(","):
+ _ = arg.split(":")
+ ip, port, filenames = _[0], _[1], ":".join(_[2:])
+ vms.append((ip, port, filenames))
+
+ print("Running job file: {0}".format(fio_cfg))
+ run_fio(vms, fio_cfg, out_dir, perf_vmex, json)
+
+
+if __name__ == "__main__":
+ sys.exit(main())