跳到正文

运行实验 ​

若想从脚本或流水线在服务器上运行一个实验并了解其结果,请将它发送到 POST /api/run。服务器会把它运行到结束,并用结果作答——或者,如果您要求,也会在每一步发生时给出该步骤。这正是 signallab run --server 所用的。

它与编辑器的 运行实验 和 experiment_start 是同一次运行:一个每个打开的页面都能看到并停止的任务,同样的事件,数据文件夹中同样的报告。

请求 ​

http
POST /api/run
Authorization: Bearer <token>
Content-Type: application/json

{ "template": "osc-ping-reply", "overrides": { "device": "192.0.2.20:9000" }, "seed": 42, "timeout": 30 }

正文指明一个实验——document 或 template,二者不能都有——以及用什么来运行它:

字段类型默认值含义
documentobject—实验,与编辑器保存和导出的一致。较旧的版本会被迁移,如同打开文件时那样
templatestring—按文件名指定的内置模板,可带或不带 .json(见下文)
overridesobject{}仅用于本次运行的参数值。值可以是字符串、数字或布尔值;每个都必须是该实验的参数
profilestring文档中的用此配置运行;"" 表示使用默认值运行
seednumber文档中的,否则为新值0 到 9007199254740991;相同的种子抽取出相同的随机值
timeoutnumber300运行以 run.timeout 失败前的秒数;1 到 300

服务器不认识的字段会被拒绝(400、api.run_invalid)。文档本身会像打开的文件那样读取,因此最大为 4 MiB。

内置模板——编辑器在 模板 下提供的实验:

template内容
empty开始和结束
http-check对 http://127.0.0.1:8080/ 的 GET,然后检查状态是否为 200
status-branch对 http://127.0.0.1:8080/ 的 GET;200 时发送一条 OSC 消息,否则延迟 500 ms
parallel-flows一个 并行分支 分成对 http://127.0.0.1:8080/ 的 GET 和一条日志记录,并排,然后 汇合分支
osc-ping-reply向参数 device(127.0.0.1:9000)发送 OSC /ping,然后在 127.0.0.1:9001 上等待 /pong 2 s
poll-until-ready一个 循环,通过 OSC 向 device 询问 /status,直到它应答 ready,至多 10 次
flaky-api一个模拟的 API(参数 api),在正常工作前会失败,在 循环 中询问它直到应答 200
fault-phases一个位于损伤中继之后的 UDP 设备,向它发送 8 s,期间中继依次为干净、丢包、离线、再次干净
dependency-outage一个模拟的 API(参数 api),停机 2 s,期间一个 循环 询问它直到它再次应答 200
websocket-echo连接到参数 service(ws://127.0.0.1:9001/echo),发送一条消息,等待它的回显,检查,关闭

在 模板 下打开一个即可查看其节点和参数;参见实验。

结果 ​

默认情况下,应答为 200,带有 Content-Type: application/json,在运行结束时发送:一个 JSON 对象,即运行的结果。

json
{
  "job_id": 12,
  "experiment": "OSC ping → reply",
  "outcome": "passed",
  "seed": 42,
  "profile": null,
  "overridden": true,
  "params": { "device": "192.0.2.20:9000" },
  "started_ms": 1759600000000,
  "ended_ms": 1759600000310,
  "steps": [ { "job_id": 12, "ts": 1759600000001, "node_id": "start", "state": "running", "detail": "", "message_key": null, "message_params": null }, … ],
  "report_path": "/data/runs/run-1759600000000-12.json"
}
字段类型含义
job_idnumber该运行的任务
experimentstring实验的名称
outcomestringpassed、failed 或 stopped
seednumber它运行所用的种子:把它作为 seed 传回即可抽取相同的值
profilestring or null它运行所用的配置
overriddenboolean部分值来自 overrides
paramsobject本次运行用到的每个参数值
started_ms, ended_msnumber自 1970 年起的毫秒数
errorEngineError它为何失败:其第一个失败。通过时省略
stepsobject[]按发生顺序排列的每个步骤,如 experiment://step
emulatorsobject[]每个 模拟器 节点接收和应答的内容:node、name、protocol、local、counts。没有时省略
impairmentsobject[]每个 网络损伤 节点的中继逐阶段做了什么。没有时省略
report_pathstring该运行在服务器上的报告;用 /api/files 下载它。未写入时省略
report_errorEngineError报告为何无法写入。否则省略

其中的机密值都会被遮蔽。报告文件包含相同的步骤;参见运行与报告。

运行进行期间,服务器每 15 s 发送一个空格。JSON 会忽略值前的空白,因此结果仍能解析,代理也不会把一次漫长而安静的运行当成已断开的连接。

跟踪步骤 ​

要看到步骤发生的过程,请请求 NDJSON:

http
Accept: application/x-ndjson

应答为 200,带有 Content-Type: application/x-ndjson:每行一个 JSON 对象,每个都带有一个 type。

type何时该行的其余部分
started首先,一次job_id、experiment、seed、profile、overridden、started_ms
step每个步骤该步骤,如 experiment://step
heartbeat每 15 s无
ended最后,一次结果,如上所述
text
{"job_id":12,"experiment":"OSC ping → reply","seed":42,"profile":null,"overridden":true,"started_ms":1759600000000,"type":"started"}
{"job_id":12,"ts":1759600000001,"node_id":"start","state":"running","detail":"","message_key":null,"message_params":null,"type":"step"}
…
{"job_id":12,"experiment":"OSC ping → reply","outcome":"passed",…,"type":"ended"}

读取各行直到 ended;忽略您不认识的 type。服务器会发送 X-Accel-Buffering: no,因此 nginx 代理会立即把每一行传递下去。

状态与结果 ​

HTTP 错误状态意味着没有运行启动;正文是一个 EngineError:

状态代码原因
400api.run_invalid正文不是运行请求:不是 JSON、未知字段、不是字符串/数字/布尔值的覆盖值
400api.run_sourcedocument 和 template 都没有,或二者都有
415command.json_required不是 Content-Type: application/json
422api.template_unknown没有该名称的内置模板
422file.json_invalid、file.too_large、doc.*文档无法读取
422任意验证代码、run.override_unknown、profile.active_missing、run.limit_range、seed.range、secret.missing、transport.address_in_use……实验无法启动:它未通过验证、某个值超出范围、某个机密未存储、它监听的某个端口已被占用

运行一旦启动,无论发生什么,状态都是 200:请读取结果中的 outcome。

outcome含义
passed每个步骤都通过,且到达了 结束
failed某个步骤失败,或运行超出其 timeout(run.timeout);error 说明是哪一个以及为什么
stopped它在结束前被停止:被 job_stop、全部停止 或服务器关闭。steps 包含它走到的步骤;不保存报告

客户端离开 ​

关闭连接不会停止运行。它是服务器上的一个任务:会运行到结束并保存报告,就像在浏览器中启动的运行在标签页关闭时那样。用 jobs_list 找到它,用 job_stop 停止它,之后用 experiment_runs 读取它的报告。服务器关闭时,运行会被停止,仍保持连接的客户端会收到 "outcome": "stopped"。

示例 ​

运行一个内置模板并等待结果:

bash
SERVER=http://127.0.0.1:1430
TOKEN=$(cat token.txt)
curl -sS -X POST "$SERVER/api/run" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"template":"osc-ping-reply","overrides":{"device":"192.0.2.20:9000"},"timeout":30}' \
  | jq -r .outcome

发送您自己的实验并更改一个参数,在每一步发生时打印它:

bash
jq '{document: ., overrides: {api: "http://192.0.2.10:8080"}, profile: ""}' smoke.json |
  curl -sSN -X POST "$SERVER/api/run" \
    -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
    -H "Accept: application/x-ndjson" --data @- |
  jq -r 'select(.type == "step") | "\(.node_id)  \(.state)  \(.detail)"'

-N 会阻止 curl 把各行积压起来。要让失败的运行使流水线失败,请检查 outcome:

bash
outcome=$(curl -sS -X POST "$SERVER/api/run" -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"template":"http-check"}' | jq -r .outcome)
[ "$outcome" = "passed" ]

从命令行 ​

带 --server <url> 的 signallab run 通过此端点在服务器上运行:它发送所读取的实验,带上 overrides、seed 和 timeout,请求 NDJSON,并在每一行到达时打印该步骤。令牌来自 --token-file,否则来自 SIGNALLAB_TOKEN。带 --report 时,它通过 /api/files 下载报告。服务器 60 s 内没有发送任何内容——连心跳都没有——就视为已离开。