fix(webui): repair broken scenario Run button onclick, dedupe build.sh overlay staging
- page_scenario.go: onclick built via JSON.stringify() embedded raw double
quotes inside a double-quoted HTML attribute, truncating the attribute so
the click handler never compiled; pass the name through an escaped
data-scenario-name attribute instead.
- build.sh: overlay staging rsyncs (OVERLAY_DIR->stage, stage->includes.chroot)
ran without --delete, so a scenario removed from the repo (a9924b0) stayed
baked into every ISO built from the persistent stage cache since — the
"second script" in the Scenario page's list.
- blackbox: rewritten around a deterministic local zip + incremental
patch-the-changed-suffix onto removable media, instead of walking/copying
~90 files through a synchronous ntfs-3g FUSE mount every cycle. journalctl
captures are now "--since last sync" (were "--since boot", growing with
uptime) and metrics.db is excluded (was copied whole every cycle).
- scenario: nvbandwidth-acs-ab now escalates GPU count (same-socket pair,
other socket's pair, one cross-socket pair, all GPUs) under each ACS state
instead of always running all 6 GPUs at once, using a new `bee
gpu-bandwidth-groups` subcommand that discovers socket layout from
`nvidia-smi topo -m` at runtime — gpu_indices is host-specific, so this
can't be baked into the scenario file.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -88,6 +89,8 @@ func run(args []string, stdout, stderr io.Writer) (exitCode int) {
|
||||
return runBenchmark(args[1:], stdout, stderr)
|
||||
case "bee-worker":
|
||||
return runBeeWorker(args[1:], stdout, stderr)
|
||||
case "gpu-bandwidth-groups":
|
||||
return runGPUBandwidthGroups(args[1:], stdout, stderr)
|
||||
case "version", "--version", "-version":
|
||||
fmt.Fprintln(stdout, Version)
|
||||
return 0
|
||||
@@ -111,6 +114,7 @@ func printRootUsage(w io.Writer) {
|
||||
bee run <file.json|name> (bare name is looked up as scenarios/<name>.json on removable media)
|
||||
bee benchmark nvidia [--profile standard|stability|overnight]
|
||||
bee bee-worker --export-dir `+app.DefaultExportDir+` --task-id TASK-001
|
||||
bee gpu-bandwidth-groups [--stage <label>]
|
||||
bee version
|
||||
bee help [command]`)
|
||||
}
|
||||
@@ -139,6 +143,8 @@ func runHelp(args []string, stdout, stderr io.Writer) int {
|
||||
return runBenchmark([]string{"--help"}, stdout, stderr)
|
||||
case "bee-worker":
|
||||
return runBeeWorker([]string{"--help"}, stdout, stderr)
|
||||
case "gpu-bandwidth-groups":
|
||||
return runGPUBandwidthGroups([]string{"--help"}, stdout, stderr)
|
||||
case "version":
|
||||
fmt.Fprintln(stdout, "usage: bee version")
|
||||
return 0
|
||||
@@ -622,6 +628,72 @@ func runBenchmark(args []string, stdout, stderr io.Writer) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// runGPUBandwidthGroups discovers this host's GPU-to-socket layout from
|
||||
// "nvidia-smi topo -m" and prints a progressive multi-GPU test plan: a pair
|
||||
// within each socket, one cross-socket pair, then every GPU. gpu_indices in
|
||||
// a scenario JSON file is host-specific (see scenarios/README.md) — this
|
||||
// exists so a scenario can stage a GPU-count escalation ("same socket, other
|
||||
// socket, cross-socket, all") without baking any host's specific GPU indices
|
||||
// into the file at all.
|
||||
//
|
||||
// With --stage, prints just that stage's comma-joined GPU indices (for a
|
||||
// scenario job's cmd to substitute directly, e.g.
|
||||
// `dcgmi diag -r nvbandwidth -i "$(bee gpu-bandwidth-groups --stage all)"`)
|
||||
// and exits nonzero with nothing on stdout if that stage doesn't apply on
|
||||
// this host (e.g. only one socket present, so there's no "cross-socket"
|
||||
// stage) — callers should treat that as "skip this stage here", not a
|
||||
// hard failure.
|
||||
// Without --stage, lists every applicable stage as "<label>\t<gpu,indices>".
|
||||
func runGPUBandwidthGroups(args []string, stdout, stderr io.Writer) int {
|
||||
fs := flag.NewFlagSet("gpu-bandwidth-groups", flag.ContinueOnError)
|
||||
fs.SetOutput(stderr)
|
||||
stage := fs.String("stage", "", "print only this stage's GPU indices (same-socket-1, same-socket-2, ..., cross-socket, all)")
|
||||
fs.Usage = func() {
|
||||
fmt.Fprintln(stderr, "usage: bee gpu-bandwidth-groups [--stage <label>]")
|
||||
fs.PrintDefaults()
|
||||
}
|
||||
if err := fs.Parse(args); err != nil {
|
||||
if err == flag.ErrHelp {
|
||||
return 0
|
||||
}
|
||||
return 2
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
fs.Usage()
|
||||
return 2
|
||||
}
|
||||
|
||||
raw, err := exec.Command("nvidia-smi", "topo", "-m").CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "bee gpu-bandwidth-groups: nvidia-smi topo -m: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
groups := platform.NvidiaProgressiveBandwidthGroups(platform.ParseNvidiaSocketGroups(string(raw)))
|
||||
|
||||
if *stage == "" {
|
||||
for _, g := range groups {
|
||||
fmt.Fprintf(stdout, "%s\t%s\n", g.Label, joinInts(g.GPUIndices))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
for _, g := range groups {
|
||||
if g.Label == *stage {
|
||||
fmt.Fprintln(stdout, joinInts(g.GPUIndices))
|
||||
return 0
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(stderr, "bee gpu-bandwidth-groups: stage %q does not apply on this host\n", *stage)
|
||||
return 1
|
||||
}
|
||||
|
||||
func joinInts(vals []int) string {
|
||||
parts := make([]string, len(vals))
|
||||
for i, v := range vals {
|
||||
parts[i] = strconv.Itoa(v)
|
||||
}
|
||||
return strings.Join(parts, ",")
|
||||
}
|
||||
|
||||
func runBeeWorker(args []string, stdout, stderr io.Writer) int {
|
||||
fs := flag.NewFlagSet("bee-worker", flag.ContinueOnError)
|
||||
fs.SetOutput(stderr)
|
||||
|
||||
Reference in New Issue
Block a user