Skip to content

Commit 8fcbb0c

Browse files
authored
orchestrator (#50)
1 parent 1027293 commit 8fcbb0c

12 files changed

Lines changed: 289 additions & 6 deletions

File tree

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ deploy-benchmark-tools:
7373
ssh $(BENCHMARK_USER)@$(BENCHMARK_HOST) "mkdir -p /benchmark"
7474
ssh $(BENCHMARK_USER)@$(BENCHMARK_HOST) "[ -f /benchmark/config.sh ]" || \
7575
scp benchmark/config.sh $(BENCHMARK_USER)@$(BENCHMARK_HOST):/benchmark/config.sh
76+
#ssh $(BENCHMARK_USER)@$(BENCHMARK_HOST) "[ -f /benchmark/testcases.json ]"
77+
scp benchmark/testcases.json $(BENCHMARK_USER)@$(BENCHMARK_HOST):/benchmark/testcases.json
7678
scp -r benchmark/scripts $(BENCHMARK_USER)@$(BENCHMARK_HOST):/benchmark
7779
scp -r benchmark/testcase $(BENCHMARK_USER)@$(BENCHMARK_HOST):/benchmark
7880

benchmark/orchestrator.py

Whitespace-only changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
VMID="$1"
6+
STORAGE="$2"
7+
8+
if [ -z "$VMID" ] || [ -z "$STORAGE" ]; then
9+
echo "Usage: $0 <vmid> <storage>"
10+
exit 1
11+
fi
12+
13+
qm set ${VMID} --hookscript ${STORAGE}:snippets/proxmox-cpu-affinity-hook
14+
15+
exit 0

benchmark/scripts/orchestrator.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env python3
2+
import json
3+
import subprocess
4+
import os
5+
import sys
6+
import argparse
7+
8+
def run_command(command, env_vars, step_name, dry_run=False, verbose=False, quiet=False):
9+
"""Executes a shell command with the specific environment."""
10+
display_cmd = command
11+
prefix = "[EXEC]"
12+
13+
if dry_run:
14+
prefix = "[DRY-RUN]"
15+
if verbose:
16+
# Expand the variables
17+
prefix = "[EXPANDED]"
18+
for key in sorted(env_vars.keys(), key=len, reverse=True):
19+
val = env_vars[key]
20+
display_cmd = display_cmd.replace(f"${key}", val).replace(f"${{{key}}}", val)
21+
22+
print(f" {prefix} {step_name}: {display_cmd}")
23+
24+
if verbose and len(env_vars) > 0:
25+
for key, val in env_vars.items():
26+
print(f" {key}={val}")
27+
28+
if dry_run:
29+
return
30+
31+
try:
32+
# Merge system env with our custom env
33+
full_env = os.environ.copy()
34+
full_env.update(env_vars)
35+
36+
stdout_dest = None
37+
stderr_dest = None
38+
if quiet:
39+
stdout_dest = subprocess.DEVNULL
40+
stderr_dest = subprocess.DEVNULL
41+
42+
subprocess.run(
43+
command,
44+
shell=True,
45+
env=full_env,
46+
check=True,
47+
executable='/bin/bash',
48+
stdout=stdout_dest,
49+
stderr=stderr_dest
50+
)
51+
except subprocess.CalledProcessError as e:
52+
print(f" [ERROR] Step '{step_name}' failed with exit code {e.returncode}")
53+
raise e
54+
55+
def main():
56+
parser = argparse.ArgumentParser(description="Benchmark Orchestrator")
57+
parser.add_argument("config", nargs="?", default="/benchmark/testcases.json", help="Path to configuration file")
58+
parser.add_argument("--dry-run", action="store_true", help="Simulate execution")
59+
parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
60+
parser.add_argument("-q", "--quiet", action="store_true", help="Suppress command output")
61+
parser.add_argument("-t", "--testcase", help="Only run the specified testcase")
62+
args = parser.parse_args()
63+
64+
if not os.path.exists(args.config):
65+
print(f"Error: {args.config} not found.")
66+
sys.exit(1)
67+
68+
with open(args.config, 'r') as f:
69+
data = json.load(f)
70+
71+
global_env = data.get("global_config", {}).get("env", {})
72+
73+
for testcase in data.get("testcases", []):
74+
if "name" not in testcase:
75+
print("Error: Testcase definition missing required 'name' field.")
76+
sys.exit(1)
77+
78+
tc_name = testcase["name"]
79+
if args.testcase and args.testcase != tc_name:
80+
continue
81+
82+
print(f"\n========================================")
83+
print(f"RUNNING TESTCASE: {tc_name}")
84+
print(f"========================================")
85+
86+
# 1. Setup Base Environment (Global + Testcase)
87+
tc_env = global_env.copy()
88+
tc_env["TESTCASE"] = tc_name
89+
tc_env.update(testcase.get("env", {}))
90+
91+
scripts = testcase.get("scripts", {})
92+
93+
# 2. Iterate EXACTLY as defined in the JSON file
94+
for step_name, step_config in scripts.items():
95+
# Merge Step-specific Env
96+
step_env = tc_env.copy()
97+
step_env.update(step_config.get("env", {}))
98+
99+
# Handle "cmds" (list) vs "cmd" (string)
100+
commands = []
101+
if "cmds" in step_config:
102+
commands = step_config["cmds"]
103+
elif "cmd" in step_config:
104+
commands = [step_config["cmd"]]
105+
106+
# Execute
107+
try:
108+
for cmd in commands:
109+
run_command(cmd, step_env, step_name, dry_run=args.dry_run, verbose=args.verbose, quiet=args.quiet)
110+
except subprocess.CalledProcessError:
111+
print(f"Aborting testcase '{tc_name}' due to failure in step '{step_name}'.")
112+
break
113+
114+
if __name__ == "__main__":
115+
main()

benchmark/scripts/testcase-deploy.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ for i in $(seq 1 $NUM_VMS); do
4646

4747
case "$TESTCASE_ACTION" in
4848
install)
49-
echo "Installing testcase ${TESTCASE}to VM..."
49+
echo "Installing testcase ${TESTCASE} to VM..."
5050
ssh $SSH_OPTS "${SSH_USER}@${VM_IP}" "sudo mkdir -p /testcase && sudo chown -R ${SSH_USER} /testcase" || exit 1
5151
scp -r $SCP_OPTS "$TESTCASE_DIR" "${SSH_USER}@${VM_IP}:/testcase" || exit 1
5252
;;

benchmark/scripts/vms-create.sh

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ SCRIPTDIR="$(dirname "$0")"
66

77
. ${SCRIPTDIR}/../config.sh
88

9+
STORAGE="${STORAGE:-local-zfs}"
10+
11+
USER_SCRIPT=""
12+
if [ -n "$1" ] && [ -f "$1" ]; then
13+
USER_SCRIPT="$1"
14+
shift
15+
fi
16+
917
if [ -n "$1" ]; then
1018
SOURCE_VMID="$1"
1119
else
@@ -36,7 +44,10 @@ for i in $(seq 1 $NUM_VMS); do
3644
# Configure resources
3745
qm set $NEW_VMID --cores $CORES --memory $MEMORY
3846

39-
# TODO: we might want to run a user script to tweak the machines even more
47+
if [ -n "$USER_SCRIPT" ]; then
48+
echo "Running user script $USER_SCRIPT on VM $NEW_VMID with storage $STORAGE..."
49+
$USER_SCRIPT "$NEW_VMID" "$STORAGE"
50+
fi
4051
done
4152

4253
echo "All $NUM_VMS VMs created."

benchmark/testcase/helloworld/init.sh

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ fi
77

88
TESTCASE="$1"
99

10-
date > /etc/here
11-
12-
id >> /etc/here
10+
apt-get update
11+
apt-get install -y sysbench time

benchmark/testcase/helloworld/run.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ for i in {1..3}; do
3232
echo "Log entry $i" > "${RESULT_DIR}/detail-${i}.txt"
3333
done
3434

35-
sleep 10
35+
# Measure resources and duration, outputting to result.json
36+
/usr/bin/time -f "{\"testcase\": \"$TESTCASE\", \"duration_sec\": %e, \"max_rss_kb\": %M, \"cpu_user_sec\": %U, \"cpu_sys_sec\": %S}" -o "${RESULT_DIR}/result.json" \
37+
sleep 10
3638

3739
date
3840
echo "done"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
3+
if [ -z "$1" ]; then
4+
echo "Error: TESTCASE is required."
5+
exit 1
6+
fi
7+
8+
TESTCASE="$1"
9+
10+
apt-get update
11+
apt-get install -y sysbench time

0 commit comments

Comments
 (0)