1
0
Files
DiAL/openspec/specs/icmp-checker/spec.md
lanyuanxiaoyao 550c427814 feat: 新增 ICMP/Ping checker,支持跨平台主机存活检测与延迟监控
实现 type: ping checker,通过 Bun.spawn 调用系统 ping 命令,自行实现跨平台
输出解析器(Linux/macOS/Windows 含中文 locale),支持 alive、丢包率、延迟、
耗时等 expect 断言,复用现有 checker 架构零外部依赖。

包含完整的类型定义、TypeBox schema、语义校验、命令构建、解析、断言、执行、
注册、配置加载测试,以及 probe-config.schema.json 更新和文档更新。

审查修复:提取 buildPingCommand 为独立纯函数并补充跨平台单测,补充
maxDurationMs/maxAvgLatencyMs 类型非法和空字符串 host 边界测试用例。

变更已归档,delta specs 已同步至 main specs。
2026-05-18 10:45:17 +08:00

193 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Purpose
定义 ICMP/Ping checker 的配置格式、命令执行、跨平台输出解析、expect 校验、失败结构和状态摘要。
## Requirements
### Requirement: ping target 配置
系统 SHALL 支持 `type: ping` 的 target 配置,通过 `ping.host` 描述目标主机地址,并通过可选字段控制探测行为。
#### Scenario: 解析最简 ping target
- **WHEN** YAML 中 target 配置 `type: ping``ping.host: "10.0.0.1"`
- **THEN** 系统 SHALL 将其解析为 ping checker并填充 `count=3``packetSize=56`、interval、timeout、group 和 expect 配置
#### Scenario: ping target 缺少 host
- **WHEN** YAML 中 target 配置 `type: ping` 但缺少 `ping.host`
- **THEN** 系统 SHALL 以配置错误退出,并提示该 target 缺少 ping.host 字段
#### Scenario: ping host 类型非法
- **WHEN** YAML 中 ping target 的 `ping.host` 不是非空字符串
- **THEN** 系统 SHALL 以配置错误退出,提示 ping.host 必须为非空字符串
#### Scenario: ping count 配置
- **WHEN** YAML 中 ping target 配置 `ping.count: 5`
- **THEN** 系统 SHALL 使用 5 作为 ICMP 包发送数量
#### Scenario: ping count 非法
- **WHEN** YAML 中 ping target 的 `ping.count` 不是 1 到 100 之间的正整数
- **THEN** 系统 SHALL 以配置错误退出,提示 ping.count 必须为 1-100 的正整数
#### Scenario: ping packetSize 配置
- **WHEN** YAML 中 ping target 配置 `ping.packetSize: 1472`
- **THEN** 系统 SHALL 使用 1472 作为 ICMP 包大小bytes
#### Scenario: ping packetSize 非法
- **WHEN** YAML 中 ping target 的 `ping.packetSize` 不是 1 到 65500 之间的正整数
- **THEN** 系统 SHALL 以配置错误退出,提示 ping.packetSize 必须为 1-65500 的正整数
#### Scenario: ping 分组未知字段失败
- **WHEN** YAML 中 ping target 的 `ping` 分组包含 `timeout: 5` 等未知字段
- **THEN** 系统 SHALL 以配置错误退出,提示 ping 分组包含未知字段
#### Scenario: ping 序列化展示摘要
- **WHEN** 系统同步 ping target 到 targets 表
- **THEN** `target` 展示摘要 SHALL 为 `ping <host>``config` JSON SHALL 包含 resolved 后的 host、count 和 packetSize
### Requirement: ping checker 执行
系统 SHALL 通过调用系统 `ping` 命令执行 ICMP 探测,记录完整执行耗时,并在命令不可用、超时或解析失败时产生结构化失败信息。
#### Scenario: ping 命令构建Linux
- **WHEN** 系统平台为 linuxping target 配置 host="10.0.0.1"、count=3、packetSize=56且外层 timeoutMs=10000
- **THEN** 系统 SHALL 执行 `ping -c 3 -s 56 -W 10 10.0.0.1`-W 单位为秒,向上取整)
#### Scenario: ping 命令构建macOS
- **WHEN** 系统平台为 darwinping target 配置 host="10.0.0.1"、count=3、packetSize=56且外层 timeoutMs=10000
- **THEN** 系统 SHALL 执行 `ping -c 3 -s 56 -W 10000 10.0.0.1`-W 单位为毫秒)
#### Scenario: ping 命令构建Windows
- **WHEN** 系统平台为 win32ping target 配置 host="10.0.0.1"、count=3、packetSize=56且外层 timeoutMs=10000
- **THEN** 系统 SHALL 执行 `ping -n 3 -l 56 -w 10000 10.0.0.1`-w 单位为毫秒)
#### Scenario: ping 命令不存在
- **WHEN** 系统未安装 `ping` 命令spawn 抛出 ENOENT
- **THEN** 系统 SHALL 记录 `matched=false`failure 的 kind 为 `error`phase 为 `ping`path 为 `spawn`message 包含 "ping 命令不可用" 和原始错误信息
#### Scenario: ping 执行超时
- **WHEN** 引擎注入的 `ctx.signal` 在 ping 命令执行过程中 abort
- **THEN** 系统 SHALL 调用 `proc.kill()` 终止子进程,记录 `matched=false`failure 的 kind 为 `error`phase 为 `ping`message 包含超时信息
#### Scenario: ping 目标可达
- **WHEN** ping target 指向可达主机,且 ping 命令正常返回
- **THEN** 系统 SHALL 解析 stdout 获取统计数据,并按断言链执行 expect 校验
#### Scenario: ping 目标不可达
- **WHEN** ping target 指向不可达主机,且 ping 命令返回 100% packet loss
- **THEN** 系统 SHALL 解析 stdout 获取统计数据,`alive` 为 false延迟字段为 null
#### Scenario: duration 覆盖完整执行
- **WHEN** ping 命令执行完成
- **THEN** 结果中的 `durationMs` SHALL 覆盖从 spawn 到进程退出的完整耗时
### Requirement: 跨平台 ping 输出解析
系统 SHALL 实现跨平台 ping 输出解析器,支持 Linux、macOS 和 Windows含多语言 locale从 stdout 中提取 transmitted、received、packetLoss、minLatencyMs、avgLatencyMs、maxLatencyMs。
#### Scenario: 解析 Linux ping 输出
- **WHEN** 平台为 linuxstdout 包含 "3 packets transmitted, 3 received, 0% packet loss" 和 "rtt min/avg/max/mdev = 1.234/2.345/3.456/0.567 ms"
- **THEN** 系统 SHALL 解析为 transmitted=3, received=3, packetLoss=0, minLatencyMs=1.234, avgLatencyMs=2.345, maxLatencyMs=3.456
#### Scenario: 解析 macOS ping 输出
- **WHEN** 平台为 darwinstdout 包含 "3 packets transmitted, 3 packets received, 0.0% packet loss" 和 "round-trip min/avg/max/stddev = 1.234/2.345/3.456/0.567 ms"
- **THEN** 系统 SHALL 解析为 transmitted=3, received=3, packetLoss=0, minLatencyMs=1.234, avgLatencyMs=2.345, maxLatencyMs=3.456
#### Scenario: 解析 Windows 英文 ping 输出
- **WHEN** 平台为 win32stdout 包含 "Packets: Sent = 3, Received = 3, Lost = 0 (0% loss)" 和 "Minimum = 1ms, Maximum = 3ms, Average = 2ms"
- **THEN** 系统 SHALL 解析为 transmitted=3, received=3, packetLoss=0, minLatencyMs=1, avgLatencyMs=2, maxLatencyMs=3
#### Scenario: 解析 Windows 中文 ping 输出
- **WHEN** 平台为 win32stdout 包含 "数据包: 已发送 = 3已接收 = 3丢失 = 0 (0% 丢失)" 和 "最短 = 1ms最长 = 3ms平均 = 2ms"
- **THEN** 系统 SHALL 解析为 transmitted=3, received=3, packetLoss=0, minLatencyMs=1, avgLatencyMs=2, maxLatencyMs=3
#### Scenario: 解析全部丢包(无延迟行)
- **WHEN** stdout 包含丢包统计行但无延迟统计行100% packet loss
- **THEN** 系统 SHALL 解析为 alive=false延迟字段min/avg/max均为 null
#### Scenario: 输出无法解析
- **WHEN** stdout 不匹配任何已知的统计行格式
- **THEN** 系统 SHALL 记录 `matched=false`failure 的 kind 为 `error`phase 为 `ping`path 为 `parse`message 包含 "无法解析 ping 输出"
### Requirement: ping expect 校验
系统 SHALL 支持 ping 专属 expect包括 `alive``maxPacketLoss``maxAvgLatencyMs``maxMaxLatencyMs``maxDurationMs`,并按 alive、packetLoss、avgLatency、maxLatency、duration 的阶段顺序快速失败。
#### Scenario: 默认 alive 成功语义
- **WHEN** ping target 未显式配置 `expect.alive`
- **THEN** 系统 SHALL 使用默认 `expect.alive: true` 进行校验
#### Scenario: alive 校验通过
- **WHEN** ping target 配置 `expect.alive: true`,且目标主机可达
- **THEN** 系统 SHALL 判定 alive 阶段通过
#### Scenario: alive 校验失败
- **WHEN** ping target 配置 `expect.alive: true`,且目标主机不可达
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 kind 为 `mismatch`phase 为 `alive`
#### Scenario: 反向 alive 断言
- **WHEN** ping target 配置 `expect.alive: false`,且目标主机不可达
- **THEN** 系统 SHALL 判定 alive 阶段通过(`matched=true`
#### Scenario: 反向 alive 断言失败
- **WHEN** ping target 配置 `expect.alive: false`,但目标主机可达
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 kind 为 `mismatch`phase 为 `alive`
#### Scenario: maxPacketLoss 校验通过
- **WHEN** ping target 配置 `expect.maxPacketLoss: 10`,且实际丢包率为 0%
- **THEN** 系统 SHALL 判定 packetLoss 阶段通过
#### Scenario: maxPacketLoss 校验失败
- **WHEN** ping target 配置 `expect.maxPacketLoss: 10`,且实际丢包率为 33%
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 kind 为 `mismatch`phase 为 `packetLoss`
#### Scenario: maxAvgLatencyMs 校验通过
- **WHEN** ping target 配置 `expect.maxAvgLatencyMs: 200`,且实际平均延迟为 12ms
- **THEN** 系统 SHALL 判定 avgLatency 阶段通过
#### Scenario: maxAvgLatencyMs 校验失败
- **WHEN** ping target 配置 `expect.maxAvgLatencyMs: 100`,且实际平均延迟为 156ms
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 kind 为 `mismatch`phase 为 `avgLatency`
#### Scenario: maxMaxLatencyMs 校验通过
- **WHEN** ping target 配置 `expect.maxMaxLatencyMs: 500`,且实际最大延迟为 340ms
- **THEN** 系统 SHALL 判定 maxLatency 阶段通过
#### Scenario: maxMaxLatencyMs 校验失败
- **WHEN** ping target 配置 `expect.maxMaxLatencyMs: 200`,且实际最大延迟为 340ms
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 kind 为 `mismatch`phase 为 `maxLatency`
#### Scenario: maxDurationMs 校验
- **WHEN** ping target 配置 `expect.maxDurationMs: 5000`,且完整执行耗时超过 5000ms
- **THEN** 系统 SHALL 返回 `matched=false`failure 的 phase 为 `duration`
#### Scenario: alive=false 时跳过延迟断言
- **WHEN** ping target 配置 `expect.alive: true``expect.maxAvgLatencyMs: 100`,且目标不可达
- **THEN** 系统 SHALL 在 alive 阶段即返回失败,不执行后续延迟断言
#### Scenario: ping expect 未知字段失败
- **WHEN** YAML 中 ping target 的 expect 包含 `status: [200]` 或其他非 ping expect 字段
- **THEN** 系统 SHALL 以配置错误退出,提示 expect 包含未知字段
#### Scenario: maxPacketLoss 类型非法
- **WHEN** YAML 中 ping target 的 `expect.maxPacketLoss` 不是 0 到 100 之间的数字
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.maxPacketLoss 必须为 0-100 的数字
#### Scenario: maxAvgLatencyMs 类型非法
- **WHEN** YAML 中 ping target 的 `expect.maxAvgLatencyMs` 不是非负有限数字
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.maxAvgLatencyMs 格式错误
#### Scenario: maxMaxLatencyMs 类型非法
- **WHEN** YAML 中 ping target 的 `expect.maxMaxLatencyMs` 不是非负有限数字
- **THEN** 系统 SHALL 以配置错误退出,提示 expect.maxMaxLatencyMs 格式错误
### Requirement: ping statusDetail 摘要
系统 SHALL 在 ping 执行成功后生成结构化 statusDetail 摘要,展示关键指标。
#### Scenario: 目标可达无丢包
- **WHEN** ping 结果为 alive=true, avg=12ms, packetLoss=0%, transmitted=3, received=3
- **THEN** statusDetail SHALL 为 `alive, avg 12ms, loss 0% (3/3)`
#### Scenario: 目标可达有丢包
- **WHEN** ping 结果为 alive=true, avg=156ms, max=340ms, packetLoss=33%, transmitted=3, received=2
- **THEN** statusDetail SHALL 包含 avg、max 和 loss 信息
#### Scenario: 目标不可达
- **WHEN** ping 结果为 alive=false, transmitted=3, received=0
- **THEN** statusDetail SHALL 为 `unreachable (0/3 received)`