net-connector 0.4.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -1
  3. data/README.md +92 -20
  4. data/docs/VERIFICATION.md +17 -7
  5. data/docs/architecture.md +102 -64
  6. data/examples/netdisco_database.yml +16 -0
  7. data/lib/net/connector/device/base.rb +27 -107
  8. data/lib/net/connector/device/local_backup.rb +96 -0
  9. data/lib/net/connector/device/profile.rb +8 -3
  10. data/lib/net/connector/device/running_config/strategy.rb +1 -1
  11. data/lib/net/connector/device/running_config.rb +44 -5
  12. data/lib/net/connector/device/save_config.rb +21 -0
  13. data/lib/net/connector/device/tftp/file_upload.rb +53 -0
  14. data/lib/net/connector/{operations/tftp_receipt.rb → device/tftp/receipt.rb} +18 -20
  15. data/lib/net/connector/device/tftp/strategy.rb +67 -0
  16. data/lib/net/connector/device/tftp/target.rb +63 -0
  17. data/lib/net/connector/device/tftp.rb +117 -0
  18. data/lib/net/connector/device/topology/immediate_strategy.rb +40 -0
  19. data/lib/net/connector/device/topology/interface_description.rb +46 -0
  20. data/lib/net/connector/device/topology/interface_name.rb +58 -0
  21. data/lib/net/connector/device/topology/strategy.rb +75 -0
  22. data/lib/net/connector/device/topology.rb +277 -0
  23. data/lib/net/connector/engine/authentication.rb +2 -2
  24. data/lib/net/connector/engine/configuration.rb +1 -1
  25. data/lib/net/connector/engine/dialogue.rb +21 -21
  26. data/lib/net/connector/engine/error_metadata.rb +50 -0
  27. data/lib/net/connector/engine/errors.rb +15 -1
  28. data/lib/net/connector/engine/execution.rb +17 -10
  29. data/lib/net/connector/engine/log/event.rb +66 -0
  30. data/lib/net/connector/engine/log/formatter.rb +16 -0
  31. data/lib/net/connector/engine/log/messages.rb +52 -0
  32. data/lib/net/connector/engine/log/stream.rb +56 -0
  33. data/lib/net/connector/engine/log.rb +105 -84
  34. data/lib/net/connector/engine/session.rb +83 -42
  35. data/lib/net/connector/engine/terminal_text.rb +33 -0
  36. data/lib/net/connector/engine/transport.rb +2 -2
  37. data/lib/net/connector/netdisco/batch.rb +9 -45
  38. data/lib/net/connector/netdisco/cli.rb +17 -10
  39. data/lib/net/connector/netdisco/client.rb +4 -3
  40. data/lib/net/connector/netdisco/config_file.rb +6 -3
  41. data/lib/net/connector/netdisco/database_client.rb +197 -0
  42. data/lib/net/connector/netdisco/device.rb +4 -5
  43. data/lib/net/connector/netdisco/diagnostic.rb +9 -39
  44. data/lib/net/connector/netdisco/fleet.rb +26 -39
  45. data/lib/net/connector/netdisco/report.rb +37 -16
  46. data/lib/net/connector/netdisco/result_store.rb +2 -2
  47. data/lib/net/connector/netdisco/settings.rb +26 -9
  48. data/lib/net/connector/netdisco.rb +1 -0
  49. data/lib/net/connector/{operations → storage}/backup_lock.rb +2 -2
  50. data/lib/net/connector/{operations → storage}/private_file.rb +2 -7
  51. data/lib/net/connector/{operations → storage}/safe_file.rb +1 -1
  52. data/lib/net/connector/{operations → storage}/saved_config.rb +10 -35
  53. data/lib/net/connector/storage.rb +15 -0
  54. data/lib/net/connector/textfsm.rb +72 -0
  55. data/lib/net/connector/vendor/cisco_ios/tftp_backup.rb +3 -3
  56. data/lib/net/connector/vendor/cisco_ios/topology.rb +5 -5
  57. data/lib/net/connector/vendor/cisco_nxos/tftp_backup.rb +3 -3
  58. data/lib/net/connector/vendor/h3c/tftp_backup.rb +4 -4
  59. data/lib/net/connector/vendor/h3c/topology.rb +5 -5
  60. data/lib/net/connector/vendor/h3c.rb +3 -3
  61. data/lib/net/connector/vendor/hillstone/tftp_backup.rb +3 -3
  62. data/lib/net/connector/vendor/hillstone/topology.rb +5 -5
  63. data/lib/net/connector/vendor/huawei/tftp_backup.rb +3 -3
  64. data/lib/net/connector/vendor/palo_alto/running_config.rb +1 -1
  65. data/lib/net/connector/vendor/palo_alto/tftp_backup.rb +3 -3
  66. data/lib/net/connector/vendor/palo_alto/topology.rb +3 -6
  67. data/lib/net/connector/vendor/radware/tftp_backup.rb +3 -3
  68. data/lib/net/connector/vendor/radware/topology.rb +2 -2
  69. data/lib/net/connector/version.rb +1 -1
  70. data/lib/net/connector.rb +1 -6
  71. metadata +47 -39
  72. data/lib/net/connector/device/interface_description.rb +0 -44
  73. data/lib/net/connector/device/interface_name.rb +0 -56
  74. data/lib/net/connector/engine/base.rb +0 -4
  75. data/lib/net/connector/engine/log_messages.rb +0 -47
  76. data/lib/net/connector/engine/profile.rb +0 -4
  77. data/lib/net/connector/engine.rb +0 -4
  78. data/lib/net/connector/operations/local_backup.rb +0 -89
  79. data/lib/net/connector/operations/parse_output.rb +0 -77
  80. data/lib/net/connector/operations/running_config/cisco.rb +0 -14
  81. data/lib/net/connector/operations/running_config/hillstone.rb +0 -12
  82. data/lib/net/connector/operations/running_config/palo_alto.rb +0 -12
  83. data/lib/net/connector/operations/running_config/strategy.rb +0 -3
  84. data/lib/net/connector/operations/running_config.rb +0 -16
  85. data/lib/net/connector/operations/saved_config/legacy_index.rb +0 -109
  86. data/lib/net/connector/operations/tftp/cisco_ios.rb +0 -11
  87. data/lib/net/connector/operations/tftp/cisco_nxos.rb +0 -11
  88. data/lib/net/connector/operations/tftp/file_upload.rb +0 -55
  89. data/lib/net/connector/operations/tftp/h3c.rb +0 -11
  90. data/lib/net/connector/operations/tftp/hillstone.rb +0 -11
  91. data/lib/net/connector/operations/tftp/huawei.rb +0 -11
  92. data/lib/net/connector/operations/tftp/palo_alto.rb +0 -11
  93. data/lib/net/connector/operations/tftp/radware.rb +0 -11
  94. data/lib/net/connector/operations/tftp/strategy.rb +0 -75
  95. data/lib/net/connector/operations/tftp_backup.rb +0 -183
  96. data/lib/net/connector/operations/topology/cisco.rb +0 -11
  97. data/lib/net/connector/operations/topology/h3c.rb +0 -11
  98. data/lib/net/connector/operations/topology/hillstone.rb +0 -11
  99. data/lib/net/connector/operations/topology/immediate_strategy.rb +0 -45
  100. data/lib/net/connector/operations/topology/palo_alto.rb +0 -11
  101. data/lib/net/connector/operations/topology/radware.rb +0 -11
  102. data/lib/net/connector/operations/topology/strategy.rb +0 -87
  103. data/lib/net/connector/operations/topology.rb +0 -261
  104. data/lib/net/connector/operations.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 270bbad958cd8e550f509079506edbc5af22c0875d80da8c21dc62dc14429517
4
- data.tar.gz: a5954b6dac0c7b127789c332cbe70ef112dcb527b0b0c52d82134f9c06f863a7
3
+ metadata.gz: b9f2c5dfacb73a1c1aecf350bd4d1fe93755e0be25dbd1cc5c964a5676beaee7
4
+ data.tar.gz: 3afcf973ef1a25d848b1a91feb5803348eea6881f9d4b2721525378f9611d0d9
5
5
  SHA512:
6
- metadata.gz: 7e56210010899199028a1cff560bd1fd333938537f317cc90d4c0d8e354c85e5182cee74cf71b887f6ac08e1e28853a77a945da14c66e4e40daf4be6e5f1e7fa
7
- data.tar.gz: 0d431b190d4abc3fcefcb9cb992863ff19f940dfeecdc5841388f16ce774280d3c26eaee3831c94177edb9c696ed5e8a056d3498260de0a6a84311983bdea52d
6
+ metadata.gz: 5b168ca118a7323f1b698908b7e6db2e627c97b52f57a4817f8b932456862ba9097491f4f879b23ffcdd9d8ea1c6f287b870354a9c279cf04963295b9cec815f
7
+ data.tar.gz: 1d5cdcedba90facbc57a9a33438c66a244c482ea580399e901f9b42572a5b6401f0cbb68f01e1bf106eb53e0562f4f213742de1cc2dc9dff80f0bae47697eb81
data/CHANGELOG.md CHANGED
@@ -1,6 +1,28 @@
1
1
  # 更新记录
2
2
 
3
- ## Unreleased
3
+ ## 0.6.0 - 2026-09-27
4
+
5
+ - 支持直接查询 PostgreSQL 获取 Netdisco 设备清单,SQL 和绑定参数可通过 YAML、环境变量或 CLI 设置,查询结果继续使用已有的厂商映射、计划预览和批量备份流程。
6
+ - 数据库连接信息只从 `NETDISCO_DB_*` 环境变量读取,查询在只读事务中执行并限制时间和容量,失败时停止整批任务,错误输出不包含数据库凭据或原始响应。
7
+ - 增加可编辑的数据库查询示例并随 gem 分发,安装时包含所需的 `pg` 驱动,原有 HTTP 清单及离线配置导出保持可用。
8
+
9
+ ## 0.5.0 - 2026-09-27
10
+
11
+ 本版包含不兼容的接口调整,不保留旧路径或方法别名;升级时请按[当前接口说明](https://github.com/gatework/net-connector/blob/v0.5.0/docs/architecture.md#当前命名与接口调整)更新调用方。
12
+
13
+ - 统一设备能力的入口与实现:配置采集/保存、本地备份、TFTP 和拓扑归入 `device/`,`Base` 通过能力模块组合;接口名称与描述规则归入 `Topology`,文件锁、私有读写及离线配置归入 `Storage`,厂商差异只保留在 `vendor/`。
14
+ - 移除 `Operations` 命名空间及目录、厂商转发别名,以及 `engine`、`engine/base`、`engine/profile` 旧设备入口,不保留兼容转发。这是开发阶段的接口整理;调用方直接采用当前路径与接口。
15
+ - TextFSM 适配统一为 `Net::Connector::TextFSM`(`textfsm.rb`),实际解析时才加载外部依赖;UTF-8 字节校验及严格终端渲染抽到 `TerminalText`,供解析和拓扑证据检查共用,不转码或修改原始配置。
16
+ - 日志拆分为事件、formatter 和流组件,继续使用标准库 `Logger` 与 expect-pty 脱敏。增加会话/命令标识、操作、阶段、耗时、字节数和错误码;逐行回显带相同上下文,脚本后处理失败另有完成事件,应用 formatter 可读取冻结的安全字段。
17
+ - 实时遵守注入 logger 的级别,保留 formatter/progname/资源所有权;自定义事件保留安全字段,敏感范围隐藏任意载荷。统一公开日志入口为 `log_event`,移除 `record_event`;不再重复发送 `command_detail`,耗时和字节数直接放在 `command_complete`。
18
+ - 失败日志与批次报告共用错误类型、错误码和阶段词表,避免自定义钩子将未登记的配置正文通过错误元数据写入日志;TFTP 回执严格区分未知路径 nil 与无效路径,手工构造及 `with` 更新执行相同校验。
19
+ - 敏感命令、交互和追加查询的保护覆盖最终处理,直接返回失败结果也会脱敏;错误归一化保留业务回执,租约结束时日志故障保留已完成步骤,避免丢失设备已执行的证据。
20
+
21
+ - 统一动作命名:单命令入口为 `execute_command`,脚本为 `execute_script`;日志用 `log_event`,抛错校验用 `validate_*!`,错误构造用 `build_*_error`,作用域用明确的 `with_*`。删除 `run`、`collect_config` 和厂商标识别名,调用方、示例及测试同步更新。
22
+ - 删除 `LegacyIndex` 与旧备份命名查找,只以规范管理地址文件作为本地备份、比较和导出入口;保留路径锁、同 FD 文件校验及原子持久化保护。
23
+ - TFTP 统一通过 `tftp_backup` 返回直接包含来源、格式和实际路径的 `TftpReceipt`,完成后错误只提供 `receipt`。移除双入口、原三字段结果和动态策略兜底,策略接口在 Profile 构造时完整校验。
24
+ - Fleet 始终返回 `Netdisco::Report`,统一 schema 2 和受控诊断,移除 `report_schema` 与 CLI 版本选项。Outcome 的耗时和诊断直接成为 Data 成员;未测量的耗时为 nil,不用墙钟推算。
25
+ - `Storage::PrivateFile.write` 统一返回持久化回执,移除 `write_receipt` 双入口;业务导出和报告存储从回执读取目标路径。
4
26
 
5
27
  ## 0.4.2 - 2026-09-27
6
28
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  `net-connector` 通过 SSH 或 Telnet 操作网络设备的命令行。它可以采集运行配置、保存私有备份、执行命令脚本、回答设备提示、记录脱敏会话日志,并在失败时返回结构化错误和已完成的步骤。
4
4
 
5
- 代码按职责组织:`engine/` 管理会话、传输、脚本、结果和日志;`device/` 提供设备入口、档案、运行配置和接口名称处理;`vendor/<厂商>/` 保存各厂商的采集、TFTP 和拓扑规则;`operations/` 实现公共业务流程;`netdisco/` 负责清单和批量编排。相同规则直接复用,设计说明见[架构文档](docs/architecture.md)。用 `require "net/connector"` 加载设备 API,用 `require "net/connector/netdisco"` 加载 Netdisco 集成;厂商规则和 TextFSM 解析器按需加载。
5
+ 代码按职责组织:`engine/` 管理会话、传输、脚本、结果和日志;`device/` 集中设备入口、档案、配置采集/保存、备份和拓扑能力;`vendor/<厂商>/` 保存厂商差异;`storage/` 负责私有文件、路径锁和离线配置;`textfsm.rb` 提供唯一的 TextFSM 适配入口;`netdisco/` 负责清单和批量编排。每项设备能力的公开方法与实现放在一起,由 `Base` 组合,设计说明见[架构文档](docs/architecture.md)。用 `require "net/connector"` 加载设备 API,用 `require "net/connector/netdisco"` 加载 Netdisco 集成;厂商规则和 TextFSM 依赖按需加载。
6
6
 
7
7
  支持 Ruby 3.2 及以上版本和 POSIX 系统。SSH 调用本机 OpenSSH,Telnet 需要本机安装 `telnet` 并显式选择。主要依赖为 [`expect-pty`](https://rubygems.org/gems/expect-pty) 0.5.x 和 [`textfsm`](https://rubygems.org/gems/textfsm) 0.2.x。
8
8
 
@@ -33,7 +33,7 @@ require "net/connector"
33
33
  | `:huawei` | 华为 | `dis cur` | `save force` |
34
34
  | `:hillstone` | 山石 StoneOS | `show configuration running` | `save all` |
35
35
 
36
- 兼容标识 `:cisco_n9k` 和 `:paloalto`。厂商连接器位于 `Net::Connector` 下,例如 `Net::Connector::H3cWireless::Connector`。
36
+ 厂商标识仅使用上表的规范名称。厂商连接器位于 `Net::Connector` 下,例如 `Net::Connector::H3cWireless::Connector`。
37
37
 
38
38
  ## 登录与本地备份
39
39
 
@@ -67,16 +67,20 @@ end
67
67
 
68
68
  示例会下发 `tftp 192.0.2.30 put flash:/startup.cfg`;可传入 `path: "site/switch.cfg"` 指定目标文件名。H3C 默认通过 `display startup` 查找保存配置;华为因型号差异需要显式指定 `source_file:`。Cisco IOS 使用交互式 `copy running-config tftp:`;Nexus 9000 默认使用 `vrf management`,可用 `vrf:` 覆盖。山石导出已保存的启动配置,Radware Alteon 生成 `.tgz` 并处理私钥及 `mansync` 提示,PAN-OS 使用固定的 `running-config.xml` 文件名。其他厂商默认使用 `<管理地址>.cfg`。
69
69
 
70
- 只有设备回显确认传输完成,方法才返回 `TftpBackup(server:, path:, completed_at:)`。明确失败对应 `:transfer_failed`,缺少成功证据对应 `:transfer_unconfirmed`。本方法不读取服务器上的文件。TFTP 不加密配置数据,应限制在合适的管理网络中使用。
70
+ 只有设备回显确认传输完成,`tftp_backup` 才返回不可变的 `TftpReceipt`。回执直接包含 `server`、实际目标 `path`、`completed_at`、`configuration_kind`、`source_file`、`format`、`requested_path`、`verification` 和 `server_sha256`。明确失败对应 `:transfer_failed`,缺少成功证据对应 `:transfer_unconfirmed`。本方法不读取服务器上的文件。TFTP 不加密配置数据,应限制在合适的管理网络中使用。
71
71
 
72
- 需要来源和格式时,调用 `device.tftp_backup_receipt(...)`,参数与旧入口相同。返回的不可变 `TftpReceipt` 包含原 `transfer`,以及 `configuration_kind`、`source_file`、`format`、`requested_path`、`actual_path`、`verification` 和 `server_sha256`。当前验证等级为 `:device_reported`,摘要为 `nil`。H3C 自动探测结果标为 `:startup`;H3C/华为显式文件标为 `:saved_file`,格式为 `:unknown`,不凭扩展名推断内容。山石未指定文件名时 `requested_path` 为 `nil`,`actual_path` 保留设备生成名称。
72
+ 回执的当前验证等级为 `:device_reported`,服务器摘要为 `nil`。H3C 自动探测结果标为 `:startup`;H3C/华为显式文件标为 `:saved_file`,格式为 `:unknown`,不凭扩展名推断内容。山石未指定文件名时 `requested_path` 为 `nil`,`path` 保留设备生成名称。
73
73
 
74
- 内置策略先校验参数组合,再在同一次会话租约中完成源探测、上传、证据检查和收尾。上传已确认但日志或清理失败时抛出 `TftpCompletionError`,通过 `error.receipt` 和 `error.transfer` 保留完成事实。显式请求与实际路径不同返回 `:transfer_path_mismatch`;实际路径无法安全确认返回 `:transfer_path_unconfirmed`,此时回执的 `actual_path` 和 `transfer.path` 为 `nil`。这些错误不会自动重传;Fleet 保留上传事实并标为 `reported_with_error`。原 `TftpBackup` 的三个成员和构造方式不变。
74
+ 策略先校验参数组合,再在同一次会话租约中完成源探测、上传、证据检查和收尾。上传已确认但日志或清理失败时抛出 `TftpCompletionError`,通过 `error.receipt` 保留完成事实。显式请求与实际路径不同返回 `:transfer_path_mismatch`;实际路径无法安全确认返回 `:transfer_path_unconfirmed`,此时回执的 `path` 为 `nil`。这些错误不会自动重传;Fleet 保留上传事实并标为 `reported_with_error`。
75
75
 
76
76
  可运行[单设备示例](examples/tftp_backup.rb)。它从环境变量读取 `DEVICE_VENDOR`、`DEVICE_HOST`、`DEVICE_USERNAME`、`DEVICE_PASSWORD` 和 `TFTP_HOST`;`TFTP_SOURCE_FILE`、`TFTP_PATH`、`TFTP_VRF` 分别指定源文件、目标文件和设备 VRF。
77
77
 
78
78
  只需在内存中采集配置时,调用 `device.running_config`;它返回 `Result`,`result.value!` 返回清理后的文本,失败时抛出对应错误。
79
79
 
80
+ 配置采集统一由 `device/running_config` 提供,厂商差异位于 `vendor/<厂商>/running_config`。
81
+ 旧 `operations/running_config`、TFTP / 拓扑厂商转发路径及 `engine/base` 等设备转发入口已移除;
82
+ 自定义扩展请按[加载入口与迁移表](docs/architecture.md#加载入口与厂商策略)使用当前路径和常量。
83
+
80
84
  配置采集默认屏蔽日志和错误诊断中的配置正文,包括 debug/raw 日志和外部 logger;返回的配置、步骤输出和备份内容保持完整。调用方应按敏感数据保管这些业务结果。
81
85
 
82
86
  ## TextFSM 解析
@@ -100,7 +104,7 @@ interfaces = device.parse_config(template: "cisco_ios_running_config_interfaces.
100
104
  两种方法都返回由模板字段名组成的哈希数组;匹配不到记录时返回 `[]`。模板缺失或无效会抛出 `ParsingError`,设备命令失败则保留原始连接器错误。可用 `template:` 指定外部模板,或用 `template_dir:` 指定含 `index` 的模板目录。每次解析使用独立解析器,批量任务之间不共享状态。已有本地备份也可离线解析:
101
105
 
102
106
  ```ruby
103
- saved = Net::Connector::Operations::SavedConfig.new(directory: "/var/backups")
107
+ saved = Net::Connector::Storage::SavedConfig.new(directory: "/var/backups")
104
108
  rows = saved.parse(host: "192.0.2.10", template: "/path/to/template.textfsm")
105
109
  ```
106
110
 
@@ -153,17 +157,37 @@ if result.failure?
153
157
  end
154
158
  ```
155
159
 
156
- `execute` 执行一条命令;`execute_script` 接收 `Script` 或命令数组;`Script.load(path)` 读取脚本文件。所有命令在设备 I/O 前校验,后续步骤失败时仍保留已完成结果,库不会自动重放命令。`save_config` 显式执行厂商保存命令,普通脚本不会自动保存。
160
+ `execute_command` 执行一条命令;`execute_script` 接收 `Script` 或命令数组;`Script.load(path)` 读取脚本文件。所有命令在设备 I/O 前校验,后续步骤失败时仍保留已完成结果,库不会自动重放命令。`save_config` 显式执行厂商保存命令,普通脚本不会自动保存。
157
161
 
158
162
  创建连接器时可设置 `max_script_output_bytes: 8 * 1024 * 1024`,限制每个脚本及其追加查询累计收到的原始响应字节;默认 `nil` 保持原有完整输出行为。达到上限后不再发送下一命令,当前响应超过上限时保留刚完成的步骤并返回 `ScriptOutputLimitExceeded`(`code: :script_output_limit_exceeded`)。命令可能已经执行,不会自动重试。原有 `max_output_bytes` 继续限制单次读取响应;累计预算不是进程内存上限,详细计数规则见架构文档。
159
163
 
160
- 厂商档案处理分页和常见确认提示。特定命令可给 `execute` 传入 `interactions: [Net::Connector::Interaction.new(/Token:\z/, ->(_) { "value\n" }, sensitive: true)]`。敏感命令及交互会暂停回显日志并在错误中脱敏;若普通命令文本包含秘密,必须显式标记 `sensitive: true`。
164
+ 厂商档案处理分页和常见确认提示。特定命令可给 `execute_command` 传入 `interactions: [Net::Connector::Interaction.new(/Token:\z/, ->(_) { "value\n" }, sensitive: true)]`。敏感命令及交互会暂停回显日志并在错误中脱敏;若普通命令文本包含秘密,必须显式标记 `sensitive: true`。
161
165
 
162
- 命令文本安全、输出可能包含秘密时,使用 `device.execute("show running-config", output_sensitive: true)`。该标记保护响应及其准备、后处理、回调异常,保留安全命令文字;不会改写 `Result` 的业务输出。`running_config` 和 `collect_config` 自动启用此保护,手写脚本的默认值仍为 `false`。
166
+ 命令文本安全、输出可能包含秘密时,使用 `device.execute_command("show running-config", output_sensitive: true)`。该标记保护响应及其准备、后处理、回调异常,保留安全命令文字;不会改写 `Result` 的业务输出。`running_config` 自动启用此保护,手写脚本的默认值仍为 `false`。
163
167
 
164
168
  ## 连接与日志设置
165
169
 
166
- `Configuration` 支持 `protocol: :ssh`(默认)或 `:telnet`,以及端口、超时、输出大小、`log_file`、`logger`、`log_format`、`log_level` 和 `known_hosts` 等参数。文本日志使用 Ruby 标准库 `Logger`,记录本地时间、级别、设备标记和中文事件。`:info` 记录连接、登录、命令和 TFTP 结果;`:debug` 还记录脱敏回显与耗时;`:warn`、`:error` 只保留相应级别。`:raw` 文件只写设备字节,不写事件元数据。可注入 `logger: Rails.logger`,连接器不会关闭或修改调用方的日志器。
170
+ `Configuration` 支持 `protocol: :ssh`(默认)或 `:telnet`,以及端口、超时、输出大小、`log_file`、`logger`、`log_format`、`log_level` 和 `known_hosts` 等参数。文本日志使用 Ruby 标准库 `Logger`,记录毫秒时间、级别、设备、中文说明和完整事件字段。`:info` 包括连接、登录、命令响应、脚本处理和 TFTP 结果;`:debug` 增加逐行脱敏回显;`:warn`、`:error` 只保留相应级别。`:raw` 文件只写经过现有敏感保护的设备字节,不添加事件字段。
171
+
172
+ 每次连接生成 `session_id`,每条实际发送的命令分配 `command_id`;日志还包含 `operation`、`phase`、脚本 `source` / `line`、`duration_ms`、`response_bytes` 和失败 `code`。`command_complete` 的 `response_received` 只表示收到了提示符;`operation_complete` 覆盖脚本准备、执行及后处理,不替代 TFTP 服务端核验或设备持久化证据。普通自定义事件使用 `device.log_event("audit", level: :info, count: 2)`。
173
+
174
+ 可注入 `logger: Rails.logger` 或普通 `Logger`。有效级别取 `log_level` 与调用方**当前**级别中较严格的一项;连接器不修改它的级别、formatter 或 progname,也不关闭它。消息是已脱敏且冻结的 `Net::Connector::Log::Event`,`to_s` 供文本显示,`to_h` 供应用 formatter 输出 JSON:
175
+
176
+ ```ruby
177
+ require "net/connector"
178
+ require "json"
179
+ require "logger"
180
+ require "time"
181
+
182
+ logger = Logger.new($stdout)
183
+ logger.formatter = lambda do |severity, time, program, message|
184
+ fields = message.is_a?(Net::Connector::Log::Event) ? message.to_h : { message: message.to_s }
185
+ "#{JSON.generate(time: time.iso8601(3), severity: severity, program: program, **fields)}\n"
186
+ end
187
+ # 将 logger: logger 传给 Net::Connector.build / open。
188
+ ```
189
+
190
+ 事件字段接受字符串、符号、整数、有限浮点数、布尔和 nil;复杂对象统一隐藏,不展开对象内容。事件名、字段名和值经过校验/脱敏,会话与命令标识不可由自定义字段覆盖。敏感命令或配置处理期间,自定义事件的名称和载荷整体隐藏,避免钩子把未登记的配置秘密写进日志。更多边界见[架构文档](docs/architecture.md)。
167
191
 
168
192
  主机密钥策略默认为 `:strict`;`:accept_new` 接受首次连接的密钥;`:replace` 需要显式 `known_hosts` 文件。`telnet_fallback` 和 `legacy_ssh` 默认关闭,只在已识别的连接失败时使用。外部命令以参数数组执行,不经 shell。Telnet 不提供 SSH 加密,只应在可信管理网络启用。设备授权和变更审批由调用方负责。
169
193
 
@@ -171,9 +195,9 @@ end
171
195
 
172
196
  `Net::Connector::Netdisco` 读取并验证完整清单,再将支持的记录映射到连接器,使用有上限的工作线程执行备份。Netdisco 只提供清单字段;设备凭据来自环境变量或调用方提供的解析器。清单会在连接任何设备前完成校验;不支持、被过滤、重复、缺少凭据、失败,以及保存成功但关闭失败的结果分别保留。
173
197
 
174
- 每次 `Client#devices` 默认限制单响应 16 MiB、累计响应 128 MiB、去重前 100,000 条记录、10,000 页和 300 秒总期限。认证、分页及兼容查询共用这些预算;默认 HTTP 客户端逐块计数,超限会关闭连接并抛出带稳定 `code` 的 `Client::Error`,Fleet 不会执行半份清单。这些默认值是可调整的设计起点,不是实测容量。旧 `requester: ->(uri, request)` 仍可使用,但只能在回调返回后检查正文和期限,回调自身的阻塞及内存用量由注入方控制。
198
+ 每次 `Client#devices` 默认限制单响应 16 MiB、累计响应 128 MiB、去重前 100,000 条记录、10,000 页和 300 秒总期限。认证、分页及兼容查询共用这些预算;默认 HTTP 客户端逐块计数,超限会关闭连接并抛出带稳定 `code` 的 `Client::Error`,Fleet 不会执行半份清单。这些默认值是可调整的设计起点,不是实测容量。可注入 `requester: ->(uri, request)`;该回调只能在回调返回后检查正文和期限,回调自身的阻塞及内存用量由注入方控制。
175
199
 
176
- 推荐使用 HTTPS,标准证书验证保持开启。为兼容既有部署,`allow_insecure_http` 默认仍为 `true`;设为 `false` 可在发请求前拒绝 HTTP,ENV 中对应 `NETDISCO_ALLOW_INSECURE_HTTP=false`。HTTP 会明文传输登录凭据和 API key,迁移时应先提供可验证的 HTTPS 端点。
200
+ 推荐使用 HTTPS,标准证书验证保持开启。`allow_insecure_http` 默认为 `true`;设为 `false` 可在发请求前拒绝 HTTP,ENV 中对应 `NETDISCO_ALLOW_INSECURE_HTTP=false`。HTTP 会明文传输登录凭据和 API key,迁移时应先提供可验证的 HTTPS 端点。
177
201
 
178
202
  `Fleet#plan_backup` 和 `Fleet#plan_tftp_backup` 从同一份清单生成计划。把计划传给 `backup_all(plan:)` 或 `tftp_backup_all(plan:)`,可使预览与执行选择同一批设备;计划与清单不符时会拒绝执行。单台设备异常或结果回调失败不会阻止其他设备。`batch.summary` 包含总数、成功、失败、部分成功、跳过、具体状态和逐台结果。部分成功包括已保存但关闭失败,以及本地文件已替换但目录同步或收尾失败;后者保留 backup 并标为 `saved_with_error`。TFTP 的 `reported_uploaded` 仅代表设备报告上传,不代表服务器文件已核验。
179
203
 
@@ -181,9 +205,9 @@ end
181
205
 
182
206
  每批默认写入私有 JSON 报告,路径见 `batch.report_location`。调用方如有数据库仓储,可传 `ResultStore::Database.new(repository: YourModel)`;仓储需实现 `create!(attributes)`。`result_store: nil` 表示由调用方自行持久化。报告失败保留在 `batch.report_error`,同时使 `batch.success?` 为假。若报告已替换但目录同步失败,仍保留位置;离线 `--export --output` 遇到同类错误返回 2,并说明文件已经提交。
183
207
 
184
- 默认仍返回原 `Batch` 和原 JSON 字段。显式传 `report_schema: 2` 可获得 `Netdisco::Report`,通过 `report.batch` 访问原批次;`success?` 和 `status` 仍保留严格语义。新报告增加 `policy`、`policy_success`、清单覆盖和受控诊断。任务耗时使用单调时钟,UTC 开始/结束时间独立保留;墙钟调整不会产生负的设备耗时。已有批次可用 `batch.report(policy: :selected)` 创建 v2 视图,不会再次执行设备或重写已有报告。
208
+ Fleet 统一返回 `Netdisco::Report`,JSON 的 `schema_version` 固定为 `2`。报告包含 `policy`、`policy_success`、清单覆盖和受控诊断;`success?` / `status` 表示严格完成情况,`policy_success?` 表示所选成功策略。任务耗时使用单调时钟,UTC 开始/结束时间独立保留。`report.batch` 是原始执行快照;手工构造的 Batch 可用 `batch.build_report(policy: :selected)` 生成报告,不会再次执行设备或重写文件。
185
209
 
186
- `success_policy: :selected` 显式使用 v2:至少一台设备成功,其他记录只因 `filtered` 或 `sample_limit` 跳过,而且没有部分成功、回调或报告错误时,`report.policy_success?` 才为真。缺少凭据、重复地址、无效地址、未知厂商、目标冲突和未知状态均会阻止成功。`coverage.complete` 只表示每条清单记录都已尝试任务;失败任务也计入尝试,不能据此判断配置已保存。所有设备成功与否仍单独查看 `policy_success` 和逐台结果。自定义 `ResultStore#write(result, directory:)` 签名保持;选择 v2 时传入 Report,其 `summary` 为新格式。
210
+ `success_policy: :selected` 要求至少一台设备成功,其他记录只因 `filtered` 或 `sample_limit` 跳过,而且没有部分成功、回调或报告错误。缺少凭据、重复地址、无效地址、未知厂商、目标冲突和未知状态均会阻止成功。`coverage.complete` 只表示每条清单记录都已尝试任务;失败任务也计入尝试,不能据此判断配置已保存。自定义 `ResultStore#write(report, directory:)` 始终接收 Report,并通过 `summary` 获取统一 JSON 结构。
187
211
 
188
212
  ```sh
189
213
  export NETDISCO_URL=https://netdisco.example/netdisco
@@ -250,11 +274,52 @@ net-connector-backup --config config.yml --tftp --all
250
274
  net-connector-backup --config config.yml --export 192.0.2.7 --output ./exports/device.cfg
251
275
  ```
252
276
 
253
- `--plan` 只拉取并验证清单;`--host` 选择一个管理地址;`--tftp` 默认每厂商最多选择五台,`--all` 选择所有就绪设备。本地备份默认选择所有就绪设备,可用 `--limit-per-vendor` 限制。`--show-config` 只输出有效的非敏感设置,不访问 Netdisco。CLI 会拒绝未知 YAML 字段、Ruby 对象标签及配置中的凭据。`--export IP` 离线读取已有 `<IP>.txt`,或唯一匹配的旧版 `<设备名>-<IP>.txt`;默认原样写到标准输出,指定 `--output` 后以 `0600` 权限原子写文件。导出的配置仍是敏感数据。
277
+ ### PostgreSQL 联机查询
278
+
279
+ 设置 `netdisco.source: postgres` 可以直接从数据库查询清单,继续使用同一套 Fleet、规则、计划和备份流程。库不内置表名、SQL 或业务筛选条件;必须提供查询,可直接修改 [YAML 示例](examples/netdisco_database.yml):
280
+
281
+ ```yaml
282
+ netdisco:
283
+ source: postgres
284
+ query: |
285
+ SELECT host(ip) AS ip, name, dns, vendor, os, model, os_ver, serial
286
+ FROM device
287
+ WHERE vendor = $1
288
+ ORDER BY ip
289
+ query_params: [H3C]
290
+ ```
291
+
292
+ 查询必须返回唯一命名的 `ip` 列;可选列为 `name`、`dns`、`vendor`、`os`、`model`、`os_ver`、`serial`,其他列会丢弃。自定义表、视图、JOIN 和只读 CTE 均可用列别名适配这一契约。PostgreSQL 的 `inet` 字段建议用 `host(ip) AS ip` 去除前缀长度;筛选及排序由 SQL 决定。参数按数组顺序绑定 `$1`、`$2`,支持字符串、数字、布尔和 null,不做字符串插值或环境变量展开。
293
+
294
+ 数据库连接信息只从进程环境读取,沿用 [Netdisco 官方环境变量命名](https://github.com/netdisco/netdisco/wiki/Environment-Variables)。`HOST`、`NAME`、`USER`、`PASS` 必填;HTTP 地址、API key 和设备登录凭据不是清单查询的前置条件:
295
+
296
+ ```sh
297
+ export NETDISCO_DB_HOST=database.example
298
+ export NETDISCO_DB_NAME=netdisco
299
+ export NETDISCO_DB_USER=inventory-reader
300
+ export NETDISCO_DB_PASS='replace-me'
301
+ export NETDISCO_DB_SSLMODE=verify-full
302
+ export NETDISCO_DB_SSLROOTCERT=/etc/net-connector/database-ca.crt
303
+
304
+ net-connector-backup --config examples/netdisco_database.yml --show-config
305
+ net-connector-backup --config examples/netdisco_database.yml --plan
306
+ # 覆盖查询参数;实际备份仍需设置 NET_CONNECTOR_DEVICE_* 凭据。
307
+ net-connector-backup --config examples/netdisco_database.yml --plan --query-params '["Cisco"]'
308
+ ```
309
+
310
+ SQL 也可通过 `NETDISCO_QUERY` / `--query SQL` 提供,参数通过 `NETDISCO_QUERY_PARAMS` / `--query-params JSON` 提供;来源对应 `NETDISCO_SOURCE` / `--source postgres`。优先级均为 CLI > ENV > YAML。SQL 和参数属于可公开配置,会出现在 `--show-config` 中;数据库密码只放在连接环境变量中。连接信息不进入策略快照、计划、报告或 `inspect`。已有 Fleet 每次重新查询时读取最新连接凭据;传入已有 `plan:` 执行时不会重新查询。
311
+
312
+ 客户端通过 `pg` 驱动执行只读事务,使用参数化游标分批取数,并在每批启用单行读取。PostgreSQL 原生解析拒绝多条语句,写入和锁定查询会失败;查询账户应仅授予所需表/视图的 SELECT 权限,只读事务不能替代账户权限隔离。驱动只在实际查询时加载,HTTP 和离线导出路径不加载它。
313
+
314
+ 数据库查询复用现有清单预算:`page_size` 控制每次 FETCH 的行数,`max_pages` 限制 FETCH 次数,`max_devices` 在去重前计数;字节预算统计返回列名与文本值,包含最终丢弃的列。字节检查发生在 libpq 解码一行之后,不能限制单个超大字段在驱动内部的瞬时内存。连接、查询和全部 FETCH 共用总期限,同时设置数据库 statement_timeout。任一查询错误、超时或超限均关闭连接并丢弃整份清单,不连接设备;错误消息不输出原始 SQL、参数或数据库响应。建议只选所需字段,并提供明确的 ORDER BY 保持采样顺序稳定。
315
+
316
+ ### 计划与执行
317
+
318
+ `--plan` 只拉取并验证清单;`--host` 选择一个管理地址;`--tftp` 默认每厂商最多选择五台,`--all` 选择所有就绪设备。本地备份默认选择所有就绪设备,可用 `--limit-per-vendor` 限制。`--show-config` 只输出有效的非敏感设置,不访问 Netdisco。CLI 会拒绝未知 YAML 字段、Ruby 对象标签及配置中的凭据。`--export IP` 只离线读取规范化管理地址对应的 `<IP>.txt`;默认原样写到标准输出,指定 `--output` 后以 `0600` 权限原子写文件。导出的配置仍是敏感数据。
254
319
 
255
- CLI 的计划与批次摘要使用 JSON。默认 `--success-policy strict` 保留原规则:非空清单且全部成功、回调及报告正常时为 `0`;空清单或有跳过、部分成功、失败时为 `1`;清单或配置错误为 `2`。`--host` 未在清单中找到也返回 `2`。由于其他清单记录会标记为过滤,默认单主机备份成功时批次退出码仍可能是 `1`;应查看 JSON 中的 `succeeded`、`skipped` 和逐台 `status`。
320
+ CLI 的计划与批次摘要使用 JSON。默认 `--success-policy strict` 使用严格规则:非空清单且全部成功、回调及报告正常时为 `0`;空清单或有跳过、部分成功、失败时为 `1`;清单或配置错误为 `2`。`--host` 未在清单中找到也返回 `2`。由于其他清单记录会标记为过滤,默认单主机备份成功时批次退出码仍可能是 `1`;应查看 JSON 中的 `succeeded`、`skipped` 和逐台 `status`。
256
321
 
257
- 显式使用 `--success-policy selected` 后,CLI 按上述 selected 规则决定退出码,并自动输出/存储 v2 报告,保留 `status: incomplete` 与跳过计数,另列 `policy_success`。例如 `net-connector-backup --config config.yml --host 192.0.2.7 --success-policy selected`。仅想增加诊断时使用 `--report-schema 2`,成功策略仍为 strict;selected 不能配合 schema 1。策略和报告版本由本次 CLI/API 参数指定,不改变已批准的清单选择,也不触发重试。
322
+ 显式使用 `--success-policy selected` 后,CLI 按上述 selected 规则决定退出码,保留严格的 `status: incomplete` 与跳过计数,另列 `policy_success`。例如 `net-connector-backup --config config.yml --host 192.0.2.7 --success-policy selected`。成功策略由本次 CLI/API 参数指定,不改变已批准的清单选择,也不触发重试。
258
323
 
259
324
  小范围现场试运行可用[本地批量示例](examples/netdisco_backup.rb),默认每厂商最多三台;`NET_CONNECTOR_SAMPLE_PER_VENDOR` 可设为 1 至 5。设备发起 TFTP 上传可用[批量 TFTP 示例](examples/netdisco_tftp_backup.rb),默认每厂商最多五台;`NET_CONNECTOR_ALL=1` 才选择全部就绪设备,全量任务默认并发 50。`NET_CONNECTOR_CONCURRENCY` 可覆盖并发数。两类示例将结果和日志写入唯一的 `examples/backups/<UTC 时间戳>-<后缀>/` 目录,该目录不纳入 Git。
260
325
 
@@ -262,16 +327,23 @@ CLI 的计划与批次摘要使用 JSON。默认 `--success-policy strict` 保
262
327
 
263
328
  批量 TFTP 可用 `NET_CONNECTOR_<VENDOR>_TFTP_SOURCE_FILE` 指定单厂商源文件。`NET_CONNECTOR_H3C_TFTP_SOURCE_FILE` 与 `NET_CONNECTOR_H3C_WIRELESS_TFTP_SOURCE_FILE` 可分别覆盖 H3C 设备的自动发现结果;华为使用 `NET_CONNECTOR_HUAWEI_TFTP_SOURCE_FILE`。源文件是设备上的路径,需符合连接器校验规则。
264
329
 
265
- 本地配置备份写到 `<目录>/<IP>.txt`,IPv6 的 `:` 转成 `_`。设备改名不改变文件名或比较基线。若规范文件不存在,唯一匹配的旧版 `<设备名>-<IP>.txt` 可作比较基线但不会被改写;匹配多个旧文件时会明确失败。规范文件优先,符号链接和非普通文件会被拒绝。文件原子替换为 `0600`,新目录权限为 `0700`。批次在当前进程执行,需要定时任务或持久队列时由调用方安排;失败命令不会自动重试。
330
+ 本地配置备份写到 `<目录>/<IP>.txt`,IPv6 的 `:` 转成 `_`。设备改名不改变文件名或比较基线;只读取该规范路径,缺失时创建新备份。其他名称的文件不参与查找或哈希比较。Fleet 和离线读取拒绝符号链接及非普通文件。文件原子替换为 `0600`,新目录权限为 `0700`。批次在当前进程执行,需要定时任务或持久队列时由调用方安排;失败命令不会自动重试。
266
331
 
267
- 同批次的旧文件目录最多扫描一次,规范文件全命中时不扫描;新批次重建索引。索引只保留文件名和身份元数据,读取时再次核验;旧文件在快照后消失、替换或修改会返回 `SavedConfigChanged`(`code: :saved_config_changed`),该设备不继续采集。批次中新出现的旧名称文件到下一批才可见,规范文件始终优先。
268
332
 
269
333
  | 环境变量 | 默认值 | 用途 |
270
334
  | --- | --- | --- |
271
- | `NETDISCO_URL` | 必填 | Netdisco 服务根地址,可包含租户路径 |
335
+ | `NETDISCO_SOURCE` | `http` | 清单来源,支持 `http`、`postgres` |
336
+ | `NETDISCO_URL` | HTTP 模式必填 | Netdisco 服务根地址,可包含租户路径 |
272
337
  | `NET_CONNECTOR_CONFIG` | 未设置 | CLI 的非敏感 YAML 配置文件 |
273
338
  | `NETDISCO_USERNAME`, `NETDISCO_PASSWORD` | 未提供 API 密钥时必填 | 清单 API 登录 |
274
339
  | `NETDISCO_API_KEY` | 未设置 | 直接使用已有 API 密钥 |
340
+ | `NETDISCO_QUERY` | PostgreSQL 模式必填 | 用户提供的单条清单 SQL |
341
+ | `NETDISCO_QUERY_PARAMS` | `[]` | SQL 参数的 JSON 标量数组 |
342
+ | `NETDISCO_DB_HOST`, `NETDISCO_DB_NAME` | PostgreSQL 模式必填 | 数据库主机或 Unix socket 目录、数据库名 |
343
+ | `NETDISCO_DB_USER`, `NETDISCO_DB_PASS` | PostgreSQL 模式必填 | 仅从环境注入的数据库用户名、密码 |
344
+ | `NETDISCO_DB_PORT` | libpq 默认 `5432` | PostgreSQL 端口 |
345
+ | `NETDISCO_DB_SSLMODE`, `NETDISCO_DB_SSLROOTCERT` | libpq 默认 | TLS 模式、CA 文件;远程连接建议 `verify-full` |
346
+ | `NETDISCO_DB_CONNECT_TIMEOUT` | 未单独设置 | 可选正整数秒;连接始终受清单总期限限制 |
275
347
  | `NETDISCO_PAGE_SIZE` | `500` | 清单分页大小 |
276
348
  | `NETDISCO_MAX_PAGES` | `10000` | 最大分页次数 |
277
349
  | `NETDISCO_MAX_RESPONSE_BYTES` | `16777216` | 单次响应正文上限,认证和错误正文也计数 |
data/docs/VERIFICATION.md CHANGED
@@ -51,6 +51,18 @@ bundle exec ruby -Ilib:test test/module_loading_test.rb
51
51
  实施记录保存在源码树的 `docs/optimization/`,不进入 gem 发布白名单。
52
52
  每个工作包的初始状态、已运行命令与未验证边界均以该记录中的日期和环境为限。
53
53
 
54
+ `test/module_loading_test.rb` 在独立进程中分别检查设备入口与厂商策略先加载、
55
+ 公共 API 先加载两种顺序,确认业务流程可用、公共层无厂商别名、没有额外厂商
56
+ 或 TextFSM 被提前加载。`test/running_config_strategy_test.rb` 检查同次采集的策略
57
+ 状态贯穿响应校验、步骤选择和清理,覆盖子类 `super`、其他 Fiber 的离线清理、
58
+ 嵌套采集拒绝及失败重连。两者共同约束模块边界和执行行为。
59
+
60
+ ## 日志与命名边界
61
+
62
+ `test/logging_test.rb` 检查安全事件对象、文本与 JSON formatter、会话/命令关联、脚本来源、响应字节和耗时;覆盖 logger 级别在运行中变化、重连分配新标识、回调失败、逐行输出的命令归属,以及调用方 logger 的资源所有权。自定义字段不能覆盖上下文,复杂对象和非有限浮点数不会进入 JSON,敏感范围内的任意事件名称和载荷整体隐藏。
63
+
64
+ 库、示例、测试和隔离安装烟测统一使用 `log_event`、`execute_command` / `execute_script`、`running_config`、单一 `tftp_backup` 回执和 Report。策略接口在 Profile 构造时校验,不通过旧方法名或可选旧钩子回退。历史版本说明和 `docs/optimization/` 的原始记录保留当时名称,不是当前可调用接口。
65
+
54
66
  ## 离线内存基准与累计预算
55
67
 
56
68
  `bundle exec ruby script/benchmark_memory.rb --suite baseline --directory tmp/benchmarks/my-run`
@@ -127,19 +139,17 @@ SHA-256,首次使用时从官方 GitHub Release 下载,缓存到 `tmp/tools/
127
139
 
128
140
  `test/topology_stages_test.rb` 使用 `test/support/topology_fixture.rb` 的四类合成对话,验证视图转换、先读回后保存、各阶段超时、读回不匹配/不完整、审批与实际查询一致、旧计划拒绝、保存完成证据和重连不重放。Queue 固定读回完成至保存之前的竞争窗口,检查线程和 Fiber 所有权。PAN-OS 无候选隔离证据时在 I/O 前拒绝自动改写,保留只读解析。夹具的官方来源、推断和 unknown 固件范围见 `test/fixtures/topology/README.md`;测试不等于现场设备认证。普通及最小 Bundler 隔离安装另用本地 PTY 验证分阶段拓扑流程及读回输出敏感标记。
129
141
 
130
- `test/tftp_boundary_test.rb` 验证内置策略在首次 I/O 前拒绝不支持的参数组合,使用 Queue 固定 H3C 源探测后的竞争窗口,检查线程/Fiber 拒绝。上传确认后的日志、命令清理、租约清理、路径和元数据钩子失败保留回执且不重传;随机假秘密不进入新错误正文。回执测试保留旧 Data 解构/成员,核对配置来源及设备报告等级;Fleet 拒绝第三方和子类提供的完成事实。既有成功/失败/echo/控制符证据用同一套合成夹具继续测试,来源和 unknown 固件范围见 `test/fixtures/tftp/README.md`。普通及最小 Bundler 安装通过本地 PTY 验证新的回执入口,没有连接真实 TFTP 服务器或上传配置。
131
-
132
- `test/netdisco_reporting_test.rb` 覆盖 strict/selected 的状态决策矩阵、显式 CLI 退出码、默认 JSON 键及 Data 成员/位置参数不变、旧 Fleet/ResultStore 签名,以及部分成功/回调/报告错误阻止 selected 成功。动态假秘密放入异常消息、输出、命令、source、line、phase、code 和自定义类型名,v2 报告与私有 JSON 不得含它。受控文件/TFTP 回执阶段跨 Worker 复制仍保留,普通文件异常不能冒充设备产物。可控时钟让 UTC 倒退而单调时间前进,断言设备及批次耗时准确且无负值;没有用固定睡眠推断时间行为。
142
+ `test/tftp_boundary_test.rb` 验证内置策略在首次 I/O 前拒绝不支持的参数组合,使用 Queue 固定 H3C 源探测后的竞争窗口,检查线程/Fiber 拒绝。上传确认后的日志、命令清理、租约清理、路径和元数据钩子失败保留回执且不重传;随机假秘密不进入新错误正文。统一回执测试核对配置来源、实际路径、设备报告等级和冻结字段;Fleet 拒绝第三方和子类提供的完成事实。既有成功/失败/echo/控制符证据用同一套合成夹具继续测试,来源和 unknown 固件范围见 `test/fixtures/tftp/README.md`。普通及最小 Bundler 安装通过本地 PTY 验证唯一的 TFTP 回执入口,没有连接真实 TFTP 服务器或上传配置。
133
143
 
134
- `test/backup_lock_test.rb` 通过 Queue、独立 Ruby 进程及可控单调时钟检查采集前互斥、有限等待、锁释放、线程/Fiber/回调递归、路径别名、私有权限、硬链接/FIFO 拒绝和符号链接替换。`test/backup_identity_test.rb` 保留旧命名比较及 mtime 契约;稳定锁文件不再作为多余备份计数。
144
+ `test/netdisco_reporting_test.rb` 覆盖 strict/selected 的状态决策矩阵、显式 CLI 退出码、统一 schema 2、Outcome 的耗时与诊断成员、Fleet/ResultStore 的 Report 契约,以及部分成功/回调/报告错误阻止 selected 成功。动态假秘密放入异常消息、输出、命令、source、line、phase、code 和自定义类型名,报告与私有 JSON 不得含它。受控文件/TFTP 回执阶段跨 Worker 复制仍保留,普通文件异常不能冒充设备产物。可控时钟让 UTC 倒退而单调时间前进,断言设备及批次耗时准确且无负值;没有用固定睡眠推断时间行为。
135
145
 
136
- `test/legacy_index_test.rb` 用 8 台合成设备和 4 个 worker 统计目录扫描:旧命名迁移一批只扫描一次,规范文件全命中时为零;同一 Fleet 的新批次重新识别歧义。覆盖 IPv6/zone 身份、文件类型、快照失败及文件消失、替换、原位修改;在读返回期间注入修改,断言失效正文不交付。端到端批次验证失效基线在凭据和连接器调用前失败。计数证明扫描次数减少,不代表已经测出墙钟性能提升。
146
+ `test/backup_lock_test.rb` 通过 Queue、独立 Ruby 进程及可控单调时钟检查采集前互斥、有限等待、锁释放、线程/Fiber/回调递归、路径别名、私有权限、硬链接/FIFO 拒绝和符号链接替换。`test/backup_identity_test.rb` 验证规范地址命名、改名后的稳定身份、mtime、非普通文件拒绝和非规范文件不参与比较;稳定锁文件不作为多余备份计数。
137
147
 
138
- `test/module_loading_test.rb` 在独立 Ruby 进程核验 Netdisco 加载、CLI 离线导出和实际解析的 `$LOADED_FEATURES`;旧入口双加载顺序继续检查。`test/parsing_encoding_test.rb` 覆盖 UTF-8/二进制标签、非法 UTF-8、Latin-1 字节、回车/退格/ANSI、被控制符隐藏的非法输入、分裂多字节字符、原备份不变及拓扑不能把异常输出当作空表。原有 PAN-OS 引号多行拒绝及多线程独立解析测试保留。没有真实其他编码设备样本,本次不增加自动或显式设备编码配置;严格转码能力留待实际设备需求验证。
148
+ `test/module_loading_test.rb` 在独立 Ruby 进程核验 Netdisco 加载、CLI 离线导出和实际解析的 `$LOADED_FEATURES`;当前设备与厂商入口的双加载顺序分别检查,确认公共层无厂商别名,Storage 不加载设备。`test/parsing_encoding_test.rb` 覆盖 UTF-8/二进制标签、非法 UTF-8、Latin-1 字节、回车/退格/ANSI、被控制符隐藏的非法输入、分裂多字节字符、原备份不变及拓扑不能把异常输出当作空表。原有 PAN-OS 引号多行拒绝及多线程独立解析测试保留。没有真实其他编码设备样本,本次不增加自动或显式设备编码配置;严格转码能力留待实际设备需求验证。
139
149
 
140
150
  `test/file_persistence_test.rb` 对临时文件创建、写入、flush/fsync、rename、父目录打开/同步及收尾注入故障,检查磁盘内容、阶段回执、无正文诊断、Fleet 部分成功及报告/导出行为。同步不支持与 EIO 分开,第三方回执子类不能供应完成事实。隔离安装烟测还通过真实本地 PTY 采集写入,验证私有锁、目录同步及安全读取。本地 macOS 测试证明协议与故障分支;Linux、网络文件系统、真实断电恢复需单独验证。
141
151
 
142
- `test/netdisco_budget_test.rb` 用合成响应、可控单调时钟及本地 TCP HTTP 端点验证单页/累计字节、记录数、分页、认证和兼容查询的共用期限。流式上限覆盖 chunked、无长度和虚报长度;超限时设备工厂与凭据解析器均未调用。阻塞读取场景断言关闭自有传输并回收观察线程,不靠固定 sleep 判断竞态。旧 requester 只验证返回后的预算,不声称能强制中止任意回调。
152
+ `test/netdisco_budget_test.rb` 用合成响应、可控单调时钟及本地 TCP HTTP 端点验证单页/累计字节、记录数、分页、认证和兼容查询的共用期限。流式上限覆盖 chunked、无长度和虚报长度;超限时设备工厂与凭据解析器均未调用。阻塞读取场景断言关闭自有传输并回收观察线程,不靠固定 sleep 判断竞态。注入 requester 只验证返回后的预算,不声称能强制中止任意回调。
143
153
 
144
154
  `test/netdisco_settings_test.rb` 验证批内 ENV 策略变化被隔离、逐设备凭据轮换、后续批次刷新、批准计划不重抓清单、CLI/ENV/YAML 优先级及离线导出。策略快照的序列化和 inspect 不含秘密;非法枚举、采样范围及预算在创建 Fleet 或设备前拒绝。默认预算没有经过生产容量压测,真实 Netdisco 及多平台远端矩阵仍需单独验收。
145
155