net-connector 0.6.0 → 0.7.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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -0
  3. data/CONTRIBUTING.md +20 -0
  4. data/README.md +112 -50
  5. data/docs/VERIFICATION.md +42 -1
  6. data/docs/architecture.md +15 -2
  7. data/examples/backup.yml +40 -0
  8. data/lib/net/connector/device/base.rb +30 -38
  9. data/lib/net/connector/device/profile/builder.rb +8 -8
  10. data/lib/net/connector/device/profile.rb +2 -2
  11. data/lib/net/connector/device/running_config.rb +2 -2
  12. data/lib/net/connector/device/tftp/receipt.rb +12 -3
  13. data/lib/net/connector/device/tftp.rb +7 -7
  14. data/lib/net/connector/device/topology.rb +4 -4
  15. data/lib/net/connector/engine/command.rb +2 -2
  16. data/lib/net/connector/engine/configuration.rb +5 -2
  17. data/lib/net/connector/engine/execution.rb +25 -14
  18. data/lib/net/connector/engine/known_hosts.rb +112 -0
  19. data/lib/net/connector/engine/log.rb +10 -7
  20. data/lib/net/connector/engine/session.rb +117 -128
  21. data/lib/net/connector/engine/transport.rb +32 -7
  22. data/lib/net/connector/netdisco/backup_run.rb +132 -0
  23. data/lib/net/connector/netdisco/batch.rb +1 -1
  24. data/lib/net/connector/netdisco/cli/options.rb +139 -0
  25. data/lib/net/connector/netdisco/cli.rb +13 -26
  26. data/lib/net/connector/netdisco/client.rb +6 -6
  27. data/lib/net/connector/netdisco/config_file.rb +20 -19
  28. data/lib/net/connector/netdisco/connection.rb +38 -0
  29. data/lib/net/connector/netdisco/database_client.rb +2 -2
  30. data/lib/net/connector/netdisco/device.rb +11 -1
  31. data/lib/net/connector/netdisco/fleet.rb +68 -47
  32. data/lib/net/connector/netdisco/inventory_budget.rb +4 -4
  33. data/lib/net/connector/netdisco/plan.rb +14 -9
  34. data/lib/net/connector/netdisco/planner.rb +18 -14
  35. data/lib/net/connector/netdisco/progress.rb +253 -0
  36. data/lib/net/connector/netdisco/report/files.rb +41 -0
  37. data/lib/net/connector/netdisco/report/text.rb +45 -0
  38. data/lib/net/connector/netdisco/report.rb +53 -31
  39. data/lib/net/connector/netdisco/result_store.rb +3 -1
  40. data/lib/net/connector/netdisco/rules.rb +10 -2
  41. data/lib/net/connector/netdisco/settings.rb +118 -68
  42. data/lib/net/connector/netdisco/tftp_archive.rb +175 -0
  43. data/lib/net/connector/netdisco/tftp_history.rb +16 -0
  44. data/lib/net/connector/netdisco/tftp_verification.rb +58 -0
  45. data/lib/net/connector/netdisco/worker.rb +8 -3
  46. data/lib/net/connector/netdisco.rb +10 -0
  47. data/lib/net/connector/storage/backup_lock.rb +1 -1
  48. data/lib/net/connector/storage/batch_directory.rb +29 -0
  49. data/lib/net/connector/storage.rb +1 -0
  50. data/lib/net/connector/vendor/h3c.rb +2 -1
  51. data/lib/net/connector/vendor/hillstone/tftp_backup.rb +3 -3
  52. data/lib/net/connector/vendor/palo_alto/tftp_backup.rb +3 -3
  53. data/lib/net/connector/vendor/radware/running_config.rb +19 -0
  54. data/lib/net/connector/vendor/radware.rb +4 -3
  55. data/lib/net/connector/version.rb +1 -1
  56. data/lib/net/connector.rb +2 -2
  57. metadata +42 -111
  58. /data/examples/{netdisco_database.yml → inventory_sql.yml} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9f2c5dfacb73a1c1aecf350bd4d1fe93755e0be25dbd1cc5c964a5676beaee7
4
- data.tar.gz: 3afcf973ef1a25d848b1a91feb5803348eea6881f9d4b2721525378f9611d0d9
3
+ metadata.gz: 83213050dfe70e3f81b22303e33a960c1d50880a6f8455f993568661a0464203
4
+ data.tar.gz: 4e1780c49d1fb686db3e0e955ae78384e1abb11583863b54680535aab6be15ef
5
5
  SHA512:
6
- metadata.gz: 5b168ca118a7323f1b698908b7e6db2e627c97b52f57a4817f8b932456862ba9097491f4f879b23ffcdd9d8ea1c6f287b870354a9c279cf04963295b9cec815f
7
- data.tar.gz: 1d5cdcedba90facbc57a9a33438c66a244c482ea580399e901f9b42572a5b6401f0cbb68f01e1bf106eb53e0562f4f213742de1cc2dc9dff80f0bae47697eb81
6
+ metadata.gz: a0c786d8d8dc12880cdbd44bfd0fc6ed440b01b5245cb20a090428accdaf9a159f133c7f442eb8397feab65e5dbec9431bd6618197bce1819b229708fd832ee7
7
+ data.tar.gz: 21e51022f1e7161026776fc637dfbcfdc484f46edd250d61eadcc505b59731e00e4836dda483fcb27035f858fdc31c514dbd8a1b6678cf5df41b1fd482469766
data/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # 更新记录
2
2
 
3
+ ## 未发布
4
+
5
+ ## 0.7.0 - 2026-09-28
6
+
7
+ - 统一内部参数、预算和声明校验的 `validate_*!` 命名;区分脚本发送前与响应后的预算检查,复用并发预检,简化状态计数并补充值对象复制边界测试。保持本轮增量重构的公开接口、错误及完成证据语义。
8
+
9
+ - Ruby 惯用法重构:集中 Session 私有实现,拆分单次登录、脚本构造/收尾及 Settings 来源选项解析;保持连接恢复、锁、脱敏和部分结果契约。Settings 同类协作使用 protected 接口替代私有 `send`。
10
+ - protected 钩子 `login_dialogues` / `confirmation_dialogues` 改为 `login_interactions` / `confirmation_interactions`,不保留别名;子类须同步更新。Fleet 单设备私有方法统一使用 device 命名,公开批次接口不变。
11
+ - 启用适配项目的 Naming 与八项 Ruby Style 规则,补充输入、集合、块、资源和异常约束;新增交互钩子继承、finalize 返回值/退出与配置优先级回归测试。
12
+
13
+ - 按实际职责命名 `Netdisco::TftpArchive` 与 `ResultStore::Json`,内部调用改用 `upload_filename`、`upload_and_archive` 等明确动作;会话输出预算记录最后执行的命令。规划器内部任务统一为 `[清单索引, 设备]`,报告中的状态集合和单台结果使用一致的命名;Fleet 内部区分配置快照与报告成功策略,生成 Report 后不再将其称为 Batch;公开计划与报告结构不变。保留 `TftpHistory`、`ResultStore::Text` 及旧方法作为兼容入口。TFTP 服务器根目录仅暂存上传文件,核验并归档到批次目录后清理暂存文件;原有同名文件先保存到 `previous/`。
14
+
15
+ - TFTP 批量示例在可访问服务器目录时使用 hostname-ip 远端文件名,先保护旧文件,再把已核验文件保存到服务器和本地的批次归档;否则使用批次时间与序号组成的唯一远端名。PAN-OS 固定文件名上传要求可访问服务器目录,无法归档时不再将上传算作成功。
16
+
17
+ - 修复并发主机密钥更新丢失:SSH 使用会话副本,认证后跨进程加锁合并,失败登录不改写共享信任。
18
+ - 统一 BackupRun 编排、CLI 参数覆盖和 Report 成功策略;报告先保存再输出最终状态,示例摘要统一使用 devices,保存失败可见。
19
+ - 标准输入凭据通过 Settings 客户端工厂支持 HTTP/PostgreSQL;TFTP 文件核验进入库,新增 verified 策略并修正自定义日志路径。
20
+ - 增加明确标注 TextFSM 依赖声明补丁的 JSON 3 CI 矩阵及隔离安装,不将源码兼容结果表述为 RubyGems 已发布依赖兼容。
21
+
22
+ - 按职责整理备份辅助接口:CLI::Options、Connection.build、Storage::BatchDirectory.create、Report::Text.write,移除 BackupOptions/BackupConnection/BackupFiles 旧路径与名称。
23
+
24
+ - 示例公共参数、凭据连接、批次文件及报告接口迁入 Netdisco::BackupOptions / BackupConnection / BackupFiles;支持注入参数与 IO,模块不直接退出进程。示例启动设置归入 examples/boot.rb。
25
+
26
+ - 整理 examples:入口改名为 backup.rb、backup_tftp.rb、device_tftp.rb、review_tftp.rb;配置为 backup.yml、inventory_sql.yml,公共启动、参数、认证、目录和报告移至 support/。旧入口移除,调用方需更新路径。
27
+
28
+ - 本地备份示例使用 hostname-ip.txt,批次目录采用 UTC+8 可读时间并原子防重名;每批新增 summary.txt 汇总时间、状态计数、失败分类及失败明细,保留 JSON 报告。
29
+
30
+ - 终端动态进度区增加上下分隔线,随窗口宽度收缩;失败输出与结束清理同步擦除整个进度区。
31
+
32
+ - 批量进度新增耗时、平均吞吐、预计剩余时间、最久任务及失败分类;TTY 心跳每秒刷新,事件刷新限频,窄窗口避免换行残留,异常时回收显示线程。
33
+
34
+ - 备份配置示例使用 accept_new 自动登记首次主机密钥,保留变更拒绝;新增脚本主机密钥策略/文件参数,标准输入凭据不再硬编码 strict,统一沿用批次策略。
35
+
36
+ - Netdisco 厂商标签陈旧时,使用 Comware 操作系统与 H3C 品牌型号的联合证据纠正连接器;支持带 H3C 前缀的 WX/AC 无线型号,显式映射仍优先。
37
+
38
+ - H3C/H3C Wireless 默认命令时限从 10 秒调整为 60 秒,避免慢速核心设备的正常配置采集被提前中断;显式 command_timeout 仍优先。
39
+
40
+ - 批量示例新增并发、设备和 Netdisco 凭据、地址、目录、配置文件命令行参数;提供隐藏密码输入,显式参数覆盖环境与 YAML,凭据不写入计划或结果。
41
+
42
+ - 批量备份示例默认全量,抽样须显式配置或使用 `--sample N`;默认终端进度原地刷新,失败单独显示,`--verbose` 打开命令明细,`--json` 打开机器输出,计划与结果始终落盘。
43
+
44
+ - 统一三个备份示例的 .env 和相对路径初始化;普通 `ruby` 使用已安装 gem,源码开发使用 `bundle exec`,修改库后需重新构建并安装。
45
+
46
+ - 配置迁移:`NET_CONNECTOR_*` 改为 `NC_*`;环境变量仅保留常用连接、凭据和运行参数,映射、筛选、预算、SQL 与厂商协议/TFTP 细节改用 YAML。CLI 与批量示例通过 `NC_CONFIG` 加载配置;旧前缀和已移除的环境设置会提示迁移。新增无凭据 YAML 示例。
47
+
48
+ - 批量备份示例实时显示登录、命令执行、单台结果和选中任务进度;进度写入 STDERR,保留 STDOUT JSON 与私有日志。新增可选脱敏事件回调 `on_event`,沿用日志失败语义。
49
+ - 备份示例自动从项目根目录 `.env` 读取配置,已有进程变量优先;dotenv 仅作为开发/示例依赖,不进入库的运行时依赖。
50
+
51
+ - 运行时依赖统一保留所需最低版本,移除缺少不兼容依据的版本上限;支持宿主预先加载 JSON 3,移除备份示例的 JSON 2 临时指定,并在兼容矩阵保留 JSON 2 验证。
52
+ - 修复 Radware `/cfg/dump` 切换到 Configuration 菜单后仍等待旧提示符导致的配置备份超时;保持完整提示符匹配、私钥交互和配置输出保护。
53
+ - Radware 菜单提示符必须读到末尾 `#`,避免 SSH 分片到达时把提示符前缀误判为登录或命令完成。
54
+
3
55
  ## 0.6.0 - 2026-09-27
4
56
 
5
57
  - 支持直接查询 PostgreSQL 获取 Netdisco 设备清单,SQL 和绑定参数可通过 YAML、环境变量或 CLI 设置,查询结果继续使用已有的厂商映射、计划预览和批量备份流程。
data/CONTRIBUTING.md CHANGED
@@ -30,6 +30,26 @@ bundle exec rake lint
30
30
  - 使用虚构凭据和 `192.0.2.0/24` 等文档地址。不要提交设备配置、备份、私有地址或日志;扫描规则同样适用于测试和示例。
31
31
  - 行为或公开契约变更写入 `CHANGELOG.md` 的 `Unreleased`,同步修改相应文档。保留与本次任务无关的工作区改动。
32
32
 
33
+ ### Ruby 范式与语法边界
34
+
35
+ - 语法保持 Ruby 3.2 兼容:双引号、两空格缩进、冻结字符串;不强制 80 列或统一尾随逗号,不以 10 行/3 参数为拆分目标。
36
+ - 方法按公开入口、protected 扩展点、private 实现组织。步骤提取必须表达独立职责,保留原锁、脱敏、rescue/ensure 和计时范围。
37
+ - 命名沿用领域词:`connect`/`close`、`execute_command`/`execute_script`、`running_config`。纯抛错校验统一用 `validate_*!`,不混用 `check_*`;构造用 `build_*`,资源作用域用 `with_*`;不因方法有副作用就追加 `!`。
38
+ - 转换用 `map`、筛选用 `select`、副作用用 `each`。`filter_map` 会丢弃 false/nil;`to_h` 的重复键会覆盖;有条件累计用 `each_with_object`。替换前确认内容、顺序和返回值等价。
39
+ - 必需键用 `fetch`,允许缺失的嵌套读取才用 `dig`。`&.` 只跳过 nil,不替代 false 或非法类型校验;`||=` 只适用于 nil/false 都等价于未设置的情形。
40
+ - 外部输入先校验再转换,不用 `to_i`、`Array()` 等掩盖非法输入。保留缺失、显式 nil、false 的区别及必要哨兵;不扩大配置来源或改变优先级。
41
+ - 模式匹配只用于稳定结构,并明确未知结构行为;不批量替换分支。Guard clause 保持返回值及预检顺序。完整透传可用匿名块参数,调整参数时显式传递,保留 `super` 的继承语义。
42
+ - Data 用于稳定值对象,Hash 用于配置和外部协议;不为缩短参数列表增加对象。freeze 不会递归冻结,复制规则按对象所有权与现有契约决定。
43
+ - 普通失败通过已有错误边界归一化;保留 `cause: nil` 和脱敏先于截断的顺序。资源清理处的 `rescue Exception` 是有理由的例外,清理后保留原中断,Worker 可抑制次级清理异常;不能转换成业务成功。
44
+ - 不自动重放设备命令;只有现有连接恢复允许重试。路径锁在会话锁外,完成步骤和持久化回执不能因后续失败丢失。ensure 中不能新增覆盖原异常或返回值的控制流。
45
+
46
+ RuboCop 启用 Naming 及 GuardClause、SafeNavigation、RedundantSelf、RedundantReturn、ExplicitBlockArgument、HashTransformValues、MapToHash、Next。
47
+ 同时启用方法间空行与缩进一致性规则。异常变量按角色使用完整名称,关闭强制 `e` 的规则;发布入口 `net-connector.rb` 和 pg 原生接口名称保留明确例外。
48
+ 自动修正仅使用 `-a` 并逐项审阅,尤其检查块、返回值和校验顺序;不使用 `-A` 或生成排除基线。
49
+
50
+ 保持行为的重构先补缺失的边界测试,再独立搬移方法、调整逻辑、修改名称。
51
+ 本次迁移与延后项见源码根目录 `RENAMES.md`;通用 Validation、配置 DSL 和参数对象须有真实复用收益,不能仅依据相似语法提取。
52
+
33
53
  ## 增加厂商或模板
34
54
 
35
55
  1. 在 `lib/net/connector/vendor/<厂商>.rb` 声明提示符、命令、交互及策略绑定,并在设备注册入口登记厂商键。已有规则可直接复用,差异逻辑放在该厂商目录中。
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
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
- 支持 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。
7
+ 支持 Ruby 3.2 及以上版本和 POSIX 系统。SSH 调用本机 OpenSSH,Telnet 需要本机安装 `telnet` 并显式选择。主要依赖为 [`expect-pty`](https://rubygems.org/gems/expect-pty) 0.5.0 及以上和 [`textfsm`](https://rubygems.org/gems/textfsm) 0.2.0 及以上。
8
8
 
9
9
  脱敏直接复用 expect-pty 从 0.5.0 起公开的 `Expect::Redactor` 接口;连接器只管理秘密作用域和配置输出的隐私策略。
10
10
 
@@ -18,6 +18,10 @@ gem install net-connector
18
18
  require "net/connector"
19
19
  ```
20
20
 
21
+ 运行时依赖只声明所需最低版本,不设置缺少兼容性依据的上限;宿主应用通过自己的 Gemfile/锁文件选择版本。正式发布依赖的安装验证使用 JSON 2。RubyGems 上 textfsm 0.2.0 仍约束 json ~> 2.0;JSON 3 使用固定 TextFSM 源码和显式依赖声明补丁单独验证,不代表公开依赖已经支持 JSON 3。开发工具及兼容测试矩阵的版本约束不影响 gem 使用者。
22
+
23
+ 示例脚本通过开发依赖 `dotenv` 自动读取项目根目录 `.env`(先执行 `bundle install`),已有进程环境变量优先;库和 CLI 本身不会隐式读取 `.env`。
24
+
21
25
  ## 支持的设备
22
26
 
23
27
  `device.supports?(:tftp_backup)` 等能力查询不会连接设备,只表示该连接器实现了对应操作,不代表设备权限或固件一定支持。完整能力矩阵和扩展示例见[厂商能力](docs/architecture.md#厂商能力)。
@@ -73,7 +77,7 @@ end
73
77
 
74
78
  策略先校验参数组合,再在同一次会话租约中完成源探测、上传、证据检查和收尾。上传已确认但日志或清理失败时抛出 `TftpCompletionError`,通过 `error.receipt` 保留完成事实。显式请求与实际路径不同返回 `:transfer_path_mismatch`;实际路径无法安全确认返回 `:transfer_path_unconfirmed`,此时回执的 `path` 为 `nil`。这些错误不会自动重传;Fleet 保留上传事实并标为 `reported_with_error`。
75
79
 
76
- 可运行[单设备示例](examples/tftp_backup.rb)。它从环境变量读取 `DEVICE_VENDOR`、`DEVICE_HOST`、`DEVICE_USERNAME`、`DEVICE_PASSWORD` 和 `TFTP_HOST`;`TFTP_SOURCE_FILE`、`TFTP_PATH`、`TFTP_VRF` 分别指定源文件、目标文件和设备 VRF。
80
+ 可运行[单设备示例](examples/device_tftp.rb)。它从环境变量读取 `DEVICE_VENDOR`、`DEVICE_HOST`、`DEVICE_USERNAME`、`DEVICE_PASSWORD` 和 `TFTP_HOST`;`TFTP_SOURCE_FILE`、`TFTP_PATH`、`TFTP_VRF` 分别指定源文件、目标文件和设备 VRF。
77
81
 
78
82
  只需在内存中采集配置时,调用 `device.running_config`;它返回 `Result`,`result.value!` 返回清理后的文本,失败时抛出对应错误。
79
83
 
@@ -81,6 +85,8 @@ end
81
85
  旧 `operations/running_config`、TFTP / 拓扑厂商转发路径及 `engine/base` 等设备转发入口已移除;
82
86
  自定义扩展请按[加载入口与迁移表](docs/architecture.md#加载入口与厂商策略)使用当前路径和常量。
83
87
 
88
+ 子类可覆盖 protected 的 `login_interactions`、`confirmation_interactions`,通过 `super` 取得档案中的交互数组再追加规则。原 `login_dialogues`、`confirmation_dialogues` 已改名,不保留别名;这两个钩子仍不属于应用层公开调用入口。
89
+
84
90
  配置采集默认屏蔽日志和错误诊断中的配置正文,包括 debug/raw 日志和外部 logger;返回的配置、步骤输出和备份内容保持完整。调用方应按敏感数据保管这些业务结果。
85
91
 
86
92
  ## TextFSM 解析
@@ -167,6 +173,21 @@ end
167
173
 
168
174
  ## 连接与日志设置
169
175
 
176
+ 连接参数 `on_event: ->(event) { ... }` 接收不可变、已脱敏的 `Log::Event`,可与日志文件或应用 logger 共用;按 `log_level` 过滤,独立于应用 logger 的阈值。回调同步执行,应保持简短;异常按日志故障处理,不会静默吞掉。它不提供未脱敏的配置正文。
177
+
178
+ `examples/backup.rb` 和 `examples/backup_tftp.rb` 默认全量备份符合筛选条件的设备,并在终端原地刷新两行进度(登录、采集、排队、成功和未完成数,以及耗时、平均吞吐、预计剩余时间);失败单独输出,结束后显示汇总和报告路径。重定向时每 25 台输出一次进度,避免刷屏。`--verbose` 显示逐条登录和命令事件,`--json` 才在 STDOUT 输出 JSON;完整计划和结果始终保存在批次目录。`NC_PROGRESS=0` 关闭人类进度。百分比表示任务完成比例,不是成功率。
179
+
180
+ ```sh
181
+ ruby examples/backup.rb # 全量,默认并发 4
182
+ ruby examples/backup.rb --sample 3 # 每厂商最多 3 台
183
+ ruby examples/backup.rb --verbose # 详细命令过程
184
+ ruby examples/backup.rb --json > result.jsonl
185
+ ruby examples/backup.rb --concurrency 10 --username backup-user --ask-password
186
+ ```
187
+
188
+ 批量示例支持 `--concurrency`(1 至 50)、`--username`、`--password`、`--enable-password`、`--netdisco-url`、`--netdisco-username`、`--netdisco-password`、`--directory` 和 `--config`。命令行优先于环境变量与 YAML;显式设备凭据逐字段覆盖对应厂商凭据。指定 Netdisco 用户名或密码时不再沿用环境中的 API key。密码可使用 `--ask-password` / `--ask-netdisco-password` 隐藏输入,避免命令行密码进入 shell 历史或进程参数;自动化仍可用环境变量或原有 `--stdin-credentials`。`--config` 与 `--directory` 的相对路径基于项目根目录。
189
+
190
+
170
191
  `Configuration` 支持 `protocol: :ssh`(默认)或 `:telnet`,以及端口、超时、输出大小、`log_file`、`logger`、`log_format`、`log_level` 和 `known_hosts` 等参数。文本日志使用 Ruby 标准库 `Logger`,记录毫秒时间、级别、设备、中文说明和完整事件字段。`:info` 包括连接、登录、命令响应、脚本处理和 TFTP 结果;`:debug` 增加逐行脱敏回显;`:warn`、`:error` 只保留相应级别。`:raw` 文件只写经过现有敏感保护的设备字节,不添加事件字段。
171
192
 
172
193
  每次连接生成 `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)`。
@@ -197,13 +218,13 @@ end
197
218
 
198
219
  每次 `Client#devices` 默认限制单响应 16 MiB、累计响应 128 MiB、去重前 100,000 条记录、10,000 页和 300 秒总期限。认证、分页及兼容查询共用这些预算;默认 HTTP 客户端逐块计数,超限会关闭连接并抛出带稳定 `code` 的 `Client::Error`,Fleet 不会执行半份清单。这些默认值是可调整的设计起点,不是实测容量。可注入 `requester: ->(uri, request)`;该回调只能在回调返回后检查正文和期限,回调自身的阻塞及内存用量由注入方控制。
199
220
 
200
- 推荐使用 HTTPS,标准证书验证保持开启。`allow_insecure_http` 默认为 `true`;设为 `false` 可在发请求前拒绝 HTTP,ENV 中对应 `NETDISCO_ALLOW_INSECURE_HTTP=false`。HTTP 会明文传输登录凭据和 API key,迁移时应先提供可验证的 HTTPS 端点。
221
+ 推荐使用 HTTPS,标准证书验证保持开启。`allow_insecure_http` 默认为 `true`;设为 `false` 可在发请求前拒绝 HTTP,YAML 中对应 `netdisco.allow_insecure_http: false`。HTTP 会明文传输登录凭据和 API key,迁移时应先提供可验证的 HTTPS 端点。
201
222
 
202
223
  `Fleet#plan_backup` 和 `Fleet#plan_tftp_backup` 从同一份清单生成计划。把计划传给 `backup_all(plan:)` 或 `tftp_backup_all(plan:)`,可使预览与执行选择同一批设备;计划与清单不符时会拒绝执行。单台设备异常或结果回调失败不会阻止其他设备。`batch.summary` 包含总数、成功、失败、部分成功、跳过、具体状态和逐台结果。部分成功包括已保存但关闭失败,以及本地文件已替换但目录同步或收尾失败;后者保留 backup 并标为 `saved_with_error`。TFTP 的 `reported_uploaded` 仅代表设备报告上传,不代表服务器文件已核验。
203
224
 
204
225
  本地 `backup(path:)` 用 SHA-256 比较新旧配置,`backup.change` 返回 `:created`、`:changed` 或 `:unchanged`;内容未变且权限、文件身份正常时保留修改时间。这不补验历史写入的断电持久性。`backup_all` 的 `on_change:` 仅在新建或更改文件保存后触发;`on_start:` 和 `on_result:` 观察每台已尝试设备。回调异常记录在 `batch.callback_errors`,不丢弃设备结果。每项结果包含开始、结束和耗时。TFTP 无法比较服务器文件,因此没有 `change`,也不触发变更通知。
205
226
 
206
- 每批默认写入私有 JSON 报告,路径见 `batch.report_location`。调用方如有数据库仓储,可传 `ResultStore::Database.new(repository: YourModel)`;仓储需实现 `create!(attributes)`。`result_store: nil` 表示由调用方自行持久化。报告失败保留在 `batch.report_error`,同时使 `batch.success?` 为假。若报告已替换但目录同步失败,仍保留位置;离线 `--export --output` 遇到同类错误返回 2,并说明文件已经提交。
227
+ 每批默认由 `ResultStore::Json` 写入私有 JSON 报告,路径见 `batch.report_location`。调用方如有数据库仓储,可传 `ResultStore::Database.new(repository: YourModel)`;仓储需实现 `create!(attributes)`。`result_store: nil` 表示由调用方自行持久化。报告失败保留在 `batch.report_error`,同时使 `batch.success?` 为假。若报告已替换但目录同步失败,仍保留位置;离线 `--export --output` 遇到同类错误返回 2,并说明文件已经提交。
207
228
 
208
229
  Fleet 统一返回 `Netdisco::Report`,JSON 的 `schema_version` 固定为 `2`。报告包含 `policy`、`policy_success`、清单覆盖和受控诊断;`success?` / `status` 表示严格完成情况,`policy_success?` 表示所选成功策略。任务耗时使用单调时钟,UTC 开始/结束时间独立保留。`report.batch` 是原始执行快照;手工构造的 Batch 可用 `batch.build_report(policy: :selected)` 生成报告,不会再次执行设备或重写文件。
209
230
 
@@ -213,10 +234,10 @@ Fleet 统一返回 `Netdisco::Report`,JSON 的 `schema_version` 固定为 `2`
213
234
  export NETDISCO_URL=https://netdisco.example/netdisco
214
235
  export NETDISCO_USERNAME=inventory-reader
215
236
  export NETDISCO_PASSWORD='replace-me'
216
- export NET_CONNECTOR_DEVICE_USERNAME=backup-user
217
- export NET_CONNECTOR_DEVICE_PASSWORD='replace-me'
218
- export NET_CONNECTOR_BACKUP_DIRECTORY=/var/backups/network
219
- export NET_CONNECTOR_CONCURRENCY=4
237
+ export NC_DEVICE_USERNAME=backup-user
238
+ export NC_DEVICE_PASSWORD='replace-me'
239
+ export NC_BACKUP_DIRECTORY=/var/backups/network
240
+ export NC_CONCURRENCY=4
220
241
  ```
221
242
 
222
243
  ```ruby
@@ -237,7 +258,7 @@ end
237
258
  exit 1 unless batch.success?
238
259
  ```
239
260
 
240
- 命令行程序 `net-connector-backup` 的 YAML 文件只允许非敏感设置;Netdisco 和设备凭据留在环境变量中。环境变量优先于 YAML。只有传入 `--config FILE` 或设置 `NET_CONNECTOR_CONFIG` 时才加载文件:
261
+ 命令行程序 `net-connector-backup` 的 YAML 文件只允许非敏感设置;Netdisco 和设备凭据留在环境变量中。常用环境变量优先于 YAML,复杂参数只通过 YAML 或 CLI 设置。批量示例也支持 `NC_CONFIG`,可从 [完整配置示例](examples/backup.yml) 开始。只有传入 `--config FILE` 或设置 `NC_CONFIG` 时才加载文件:
241
262
 
242
263
  ```yaml
243
264
  netdisco:
@@ -276,7 +297,7 @@ net-connector-backup --config config.yml --export 192.0.2.7 --output ./exports/d
276
297
 
277
298
  ### PostgreSQL 联机查询
278
299
 
279
- 设置 `netdisco.source: postgres` 可以直接从数据库查询清单,继续使用同一套 Fleet、规则、计划和备份流程。库不内置表名、SQL 或业务筛选条件;必须提供查询,可直接修改 [YAML 示例](examples/netdisco_database.yml):
300
+ 设置 `netdisco.source: postgres` 可以直接从数据库查询清单,继续使用同一套 Fleet、规则、计划和备份流程。库不内置表名、SQL 或业务筛选条件;必须提供查询,可直接修改 [YAML 示例](examples/inventory_sql.yml):
280
301
 
281
302
  ```yaml
282
303
  netdisco:
@@ -301,13 +322,13 @@ export NETDISCO_DB_PASS='replace-me'
301
322
  export NETDISCO_DB_SSLMODE=verify-full
302
323
  export NETDISCO_DB_SSLROOTCERT=/etc/net-connector/database-ca.crt
303
324
 
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"]'
325
+ net-connector-backup --config examples/inventory_sql.yml --show-config
326
+ net-connector-backup --config examples/inventory_sql.yml --plan
327
+ # 覆盖查询参数;实际备份仍需设置 NC_DEVICE_* 凭据。
328
+ net-connector-backup --config examples/inventory_sql.yml --plan --query-params '["Cisco"]'
308
329
  ```
309
330
 
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:` 执行时不会重新查询。
331
+ SQL、参数和来源也可分别通过 `--query SQL`、`--query-params JSON`、`--source postgres` 覆盖 YAML,不再从环境变量读取。SQL 和参数属于可公开配置,会出现在 `--show-config` 中;数据库密码只放在连接环境变量中。连接信息不进入策略快照、计划、报告或 `inspect`。已有 Fleet 每次重新查询时读取最新连接凭据;传入已有 `plan:` 执行时不会重新查询。
311
332
 
312
333
  客户端通过 `pg` 驱动执行只读事务,使用参数化游标分批取数,并在每批启用单行读取。PostgreSQL 原生解析拒绝多条语句,写入和锁定查询会失败;查询账户应仅授予所需表/视图的 SELECT 权限,只读事务不能替代账户权限隔离。驱动只在实际查询时加载,HTTP 和离线导出路径不加载它。
313
334
 
@@ -321,60 +342,61 @@ CLI 的计划与批次摘要使用 JSON。默认 `--success-policy strict` 使
321
342
 
322
343
  显式使用 `--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 参数指定,不改变已批准的清单选择,也不触发重试。
323
344
 
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。
345
+ 本地批量示例和 TFTP 批量示例默认全量;显式 `--sample N`、`NC_SAMPLE_PER_VENDOR` 或 YAML `backup.limit_per_vendor` 才限制每厂商数量(示例允许 1 至 5)。优先级为命令行抽样 > 环境变量 > YAML。并发默认 4,可用 `NC_CONCURRENCY` 覆盖。两类示例在备份目录下创建唯一批次目录。TFTP 批量示例在可访问服务器目录时使用 hostname-ip 远端文件名并逐批归档;无法访问服务器目录时使用带批次标识的远端文件名,避免覆盖旧文件;上传完成与服务器文件验证是不同状态。
325
346
 
326
- 全量 TFTP 计划保存为 `plan.json`。目标文件名通常为 `<设备名>-<IP>.cfg`,Radware 用 `.tgz`,山石用 `.dat`。计划按实际文件名检查所有厂商的覆盖冲突,包括地址规范化后的重名;保留首个入选目标,其余标记为 `remote_filename_collision`。PAN-OS 固定使用 `running-config.xml`,还要求 `Sent ... bytes` 完成行。H3C 从 `display startup` 发现源文件,可用厂商环境变量覆盖;华为默认 `flash:/startup.cfg`。Nexus 9000 默认 VRF 为 `management`,山石为 `mgt-vr`;`NET_CONNECTOR_TFTP_VRFS` 接受按厂商键配置的 JSON,例如 `{"cisco_nxos":"backup","hillstone":"mgt-vr"}`。山石命令在 `vrouter` 参数后追加唯一的 `.dat` 文件名;直接调用山石连接器且不指定 `path:` 时,由设备生成文件名并在结果中返回。小批次会尝试回读服务器文件;全量任务跳过逐文件回读并标为未验证。可运行 `ruby examples/review_tftp_backup.rb <批次目录>`,根据会话日志复核剩余失败,而不改写原始结果。日志、`events.jsonl` 和逐台结果均以私有权限保存。
347
+ 全量 TFTP 计划保存为 `plan.json`。目标文件名通常为 `<设备名>-<IP>.cfg`,Radware 用 `.tgz`,山石用 `.dat`。可访问本机 TFTP 服务器目录时,批量示例直接使用 `<设备名>-<IP>.<扩展名>` 上传,并在服务器的 `archive/<批次>/` 与本地 `<批次目录>/tftp/` 各保留一份;上传前已有的同名文件先保存到新归档目录的 `previous/`。无法访问服务器目录时,远端文件名增加东八区批次标识及同秒序号,避免覆盖旧文件;实际文件名见 `summary.json` 的 `remote_path`。计划仍检查同批目标名冲突。PAN-OS 固定使用 `running-config.xml`,还要求 `Sent ... bytes` 完成行;为避免下次上传覆盖它,运行批量示例时必须能访问本机 TFTP 服务器目录,否则该设备会明确失败且不会发起上传。H3C 从 `display startup` 发现源文件,可用 YAML `tftp.h3c_source_file` 覆盖;华为默认 `flash:/startup.cfg`。Nexus 9000 默认 VRF 为 `management`,山石为 `mgt-vr`;YAML `tftp.vrfs` 接受按厂商键配置的映射。山石命令在 `vrouter` 参数后追加唯一的 `.dat` 文件名;直接调用山石连接器且不指定 `path:` 时,由设备生成文件名并在结果中返回。通过 `--tftp-root DIR` 或 `TFTP_ROOT` 指定本机或已挂载的服务器目录时,批量示例在每台设备完成后核验实际回执路径、非空文件、修改时间和 SHA-256,并立即保存两份历史文件;报告的 `local_file` 和 `server_archive_file` 分别指向本地批次副本和服务器归档副本。核验或归档失败不会报告成功。未提供本机目录时,可命名设备仍使用不覆盖旧文件的远端文件名,回执保留为 `device_reported`,本地批次不包含配置副本。默认示例使用 `selected` 策略;要求服务器文件核验时使用 `--success-policy verified`,并提供本机服务器目录。可运行 `ruby examples/review_tftp.rb <批次目录>`,根据会话日志复核剩余失败,而不改写原始结果。日志、`events.jsonl` 和逐台结果均以私有权限保存。
327
348
 
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`。源文件是设备上的路径,需符合连接器校验规则。
349
+ 批量 TFTP 源文件通过 YAML `tftp.h3c_source_file`、`tftp.h3c_wireless_source_file`、`tftp.huawei_source_file` 设置。源文件是设备上的路径,需符合连接器校验规则。
329
350
 
330
351
  本地配置备份写到 `<目录>/<IP>.txt`,IPv6 的 `:` 转成 `_`。设备改名不改变文件名或比较基线;只读取该规范路径,缺失时创建新备份。其他名称的文件不参与查找或哈希比较。Fleet 和离线读取拒绝符号链接及非普通文件。文件原子替换为 `0600`,新目录权限为 `0700`。批次在当前进程执行,需要定时任务或持久队列时由调用方安排;失败命令不会自动重试。
331
352
 
332
353
 
333
354
  | 环境变量 | 默认值 | 用途 |
334
355
  | --- | --- | --- |
335
- | `NETDISCO_SOURCE` | `http` | 清单来源,支持 `http`、`postgres` |
336
356
  | `NETDISCO_URL` | HTTP 模式必填 | Netdisco 服务根地址,可包含租户路径 |
337
- | `NET_CONNECTOR_CONFIG` | 未设置 | CLI 的非敏感 YAML 配置文件 |
357
+ | `NC_CONFIG` | 未设置 | CLI / 批量示例的非敏感 YAML 配置文件 |
338
358
  | `NETDISCO_USERNAME`, `NETDISCO_PASSWORD` | 未提供 API 密钥时必填 | 清单 API 登录 |
339
359
  | `NETDISCO_API_KEY` | 未设置 | 直接使用已有 API 密钥 |
340
- | `NETDISCO_QUERY` | PostgreSQL 模式必填 | 用户提供的单条清单 SQL |
341
- | `NETDISCO_QUERY_PARAMS` | `[]` | SQL 参数的 JSON 标量数组 |
342
360
  | `NETDISCO_DB_HOST`, `NETDISCO_DB_NAME` | PostgreSQL 模式必填 | 数据库主机或 Unix socket 目录、数据库名 |
343
361
  | `NETDISCO_DB_USER`, `NETDISCO_DB_PASS` | PostgreSQL 模式必填 | 仅从环境注入的数据库用户名、密码 |
344
362
  | `NETDISCO_DB_PORT` | libpq 默认 `5432` | PostgreSQL 端口 |
345
363
  | `NETDISCO_DB_SSLMODE`, `NETDISCO_DB_SSLROOTCERT` | libpq 默认 | TLS 模式、CA 文件;远程连接建议 `verify-full` |
346
364
  | `NETDISCO_DB_CONNECT_TIMEOUT` | 未单独设置 | 可选正整数秒;连接始终受清单总期限限制 |
347
- | `NETDISCO_PAGE_SIZE` | `500` | 清单分页大小 |
348
- | `NETDISCO_MAX_PAGES` | `10000` | 最大分页次数 |
349
- | `NETDISCO_MAX_RESPONSE_BYTES` | `16777216` | 单次响应正文上限,认证和错误正文也计数 |
350
- | `NETDISCO_MAX_INVENTORY_BYTES` | `134217728` | 一次清单调用的累计正文上限 |
351
- | `NETDISCO_MAX_DEVICES` | `100000` | 去重前累计记录上限,兼容查询共用 |
352
- | `NETDISCO_INVENTORY_TIMEOUT` | `300` | 整次清单调用的有限正数秒数 |
353
- | `NETDISCO_ALLOW_INSECURE_HTTP` | `true` | 显式设为 `false` 拒绝明文 HTTP |
354
- | `NET_CONNECTOR_DEVICE_USERNAME`, `NET_CONNECTOR_DEVICE_PASSWORD` | 未设置 | 设备登录默认凭据 |
355
- | `NET_CONNECTOR_<VENDOR>_USERNAME`, `NET_CONNECTOR_<VENDOR>_PASSWORD` | 未设置 | 单厂商凭据,例如 `CISCO_IOS` |
356
- | `NET_CONNECTOR_BACKUP_DIRECTORY` | `./backups` | 备份及默认报告目录 |
357
- | `NET_CONNECTOR_CONCURRENCY` | `4` | 并发设备数,范围 1 至 50 |
358
- | `NET_CONNECTOR_MAX_SCRIPT_OUTPUT_BYTES` | 未设置 | 每个脚本的累计响应上限;CLI `--max-script-output-bytes N` 优先 |
359
- | `NET_CONNECTOR_INCLUDE_HOSTS`, `NET_CONNECTOR_EXCLUDE_HOSTS` | 未设置 | 逗号分隔的管理地址过滤器 |
360
- | `NET_CONNECTOR_INCLUDE_VENDORS` | 未设置 | 逗号分隔的厂商标识过滤器 |
361
- | `NET_CONNECTOR_VENDOR_OVERRIDES` | `{}` | Netdisco 厂商标签到连接器标识的 JSON 映射 |
362
- | `NET_CONNECTOR_HOST_OVERRIDES` | `{}` | 管理地址到连接器标识的 JSON 映射 |
363
- | `NET_CONNECTOR_DEVICE_RULES` | `[]` | 含 `vendor`、可选 `os` 或 `model_prefix` 及 `connector` 的映射规则 |
364
- | `NET_CONNECTOR_PROTOCOL` | `ssh` | 默认连接协议,可按厂商覆盖 |
365
- | `NET_CONNECTOR_KNOWN_HOSTS`, `NET_CONNECTOR_HOST_KEY_POLICY` | 系统主机记录、`strict` | SSH 主机密钥设置 |
366
- | `NET_CONNECTOR_LOG_DIRECTORY` | 未设置 | 逐台会话日志目录 |
367
- | `NET_CONNECTOR_LOG_LEVEL` | `info`(TFTP 示例为 `debug`) | `debug`、`info`、`warn`、`error` 日志级别 |
368
- | `NET_CONNECTOR_TFTP_VRFS` | `{}` | NX-OS 和山石的 TFTP VRF 映射 |
369
- | `NET_CONNECTOR_<VENDOR>_TFTP_SOURCE_FILE` | 按厂商决定 | 批量 TFTP 使用的设备源文件,H3C 可覆盖自动发现结果 |
370
-
371
- 设备映射规则先于厂商标签覆盖和内置规则执行,可用 `model_prefix` 区分同厂商型号:
365
+ | `NC_DEVICE_USERNAME`, `NC_DEVICE_PASSWORD` | 未设置 | 设备登录默认凭据 |
366
+ | `NC_<VENDOR>_USERNAME`, `NC_<VENDOR>_PASSWORD` | 未设置 | 单厂商凭据,例如 `CISCO_IOS` |
367
+ | `NC_BACKUP_DIRECTORY` | `./backups` | 备份及默认报告目录 |
368
+ | `NC_CONCURRENCY` | `4` | 并发设备数,范围 1 至 50 |
369
+ | `NC_PROTOCOL` | `ssh` | 默认连接协议,可按厂商覆盖 |
370
+ | `NC_KNOWN_HOSTS`, `NC_HOST_KEY_POLICY` | 系统主机记录、`strict` | SSH 主机密钥设置 |
371
+ | `NC_LOG_DIRECTORY` | 未设置 | 逐台会话日志目录 |
372
+ | `NC_LOG_LEVEL` | `info` | `debug`、`info`、`warn`、`error` 日志级别 |
373
+
374
+ 环境变量前缀统一为 `NC_`;`NETDISCO_*` 仅保留服务地址、认证和数据库连接设置。旧 `NET_CONNECTOR_*` 及已移除的复杂环境变量会报迁移错误,不会静默忽略。凭据保持在环境变量中,YAML 不接受密码。
375
+
376
+ | 其他常用变量 | 用途 |
377
+ | --- | --- |
378
+ | `NC_SAMPLE_PER_VENDOR` | 可选抽样上限;示例不设置时全量,设置时允许 1 至 5 |
379
+ | `NC_PROGRESS` | 批量示例实时进度,默认 1;0 关闭 |
380
+ | `NC_ENABLE_PASSWORD`, `NC_<VENDOR>_ENABLE_PASSWORD` | 可选提权密码 |
381
+ | `TFTP_HOST`, `TFTP_ROOT` | TFTP 地址、示例验证用的本地服务器目录 |
382
+
383
+ 将原来的环境配置移到 YAML:清单预算放在 `netdisco`;过滤、厂商/主机映射及规则放在 `inventory`;脚本输出预算与厂商协议放在 `ssh`;VRF 和源文件放在 `tftp`。例如:
372
384
 
373
- ```sh
374
- export NET_CONNECTOR_DEVICE_RULES='[{"vendor":"Cisco","model_prefix":"N9K","connector":"cisco_nxos"}]'
385
+ ```yaml
386
+ inventory:
387
+ include_vendors: [h3c, cisco_nxos]
388
+ device_rules:
389
+ - vendor: Cisco
390
+ model_prefix: N9K
391
+ connector: cisco_nxos
392
+ ssh:
393
+ vendor_protocols:
394
+ h3c: ssh
395
+ max_script_output_bytes: 16777216
375
396
  ```
376
397
 
377
- `Fleet` 每次规划或执行前通过 `Settings#snapshot(mode:)` 固定非敏感设置,包括筛选规则、目录、并发、协议、主机密钥、日志、TFTP 参数和清单预算。执行中修改 ENV 不改变当批策略;再次调用会读取新值。CLI 一次调用的规划与执行共用策略,优先级为 CLI > ENV > YAML > 默认值。`Settings#validate!(mode:)` 复用连接配置及 Planner 的枚举和范围规则;`--show-config` 会拒绝非法设置,`--export` 只验证离线目录,不要求清单地址或认证。
398
+
399
+ `Fleet` 每次规划或执行前通过 `Settings#snapshot(mode:)` 固定非敏感设置,包括筛选规则、目录、并发、协议、主机密钥、日志、TFTP 参数和清单预算。执行中修改 ENV 不改变当批策略;再次调用会读取新值。CLI 一次调用的规划与执行共用策略,优先级为 CLI > 常用环境变量 > YAML > 默认值。`Settings#validate!(mode:)` 复用连接配置及 Planner 的枚举和范围规则;`--show-config` 会拒绝非法设置,`--export` 只验证离线目录,不要求清单地址或认证。
378
400
 
379
401
  每台任务开始时仍读取设备凭据,`Settings.from_file` 也保留轮换能力。纯策略快照不保存密码、API key 或凭据解析器。需要让多次 API 调用共用策略时,可传 `Settings.new.for_run(mode: :backup)`。自定义 `credentials:` 解析器仍可逐设备返回连接设置,它显式给出的选项优先于批次默认值,由调用方负责一致性;注入的 `client:` 生命周期也由调用方管理。传入 `plan:` 的执行不会重新拉取或筛选已批准清单。`fleet.devices` 可在不连接设备时检查映射。
380
402
 
@@ -392,3 +414,43 @@ CI 在 Linux 和 macOS 上覆盖 Ruby 3.2、3.3、3.4、4.0。`script/ci` 扫描
392
414
  参与开发见 [CONTRIBUTING.md](CONTRIBUTING.md),漏洞报告见 [SECURITY.md](SECURITY.md)。实现注释和主要文档使用中文,欢迎中文或英文的问题与 PR。
393
415
 
394
416
  真实凭据应放在环境变量和版本库外的本地配置中。检查范围、依赖政策和忽略规则见[验证文档](docs/VERIFICATION.md),发布流程见[发布文档](docs/RELEASING.md)。
417
+
418
+ 备份示例通过 `examples/boot.rb` 读取项目根目录 `.env`,不会自动切换依赖或注入源码路径。直接 `ruby examples/backup.rb`(或在 `examples/` 中执行 `ruby backup.rb`)使用已安装的 gem;修改库后必须重新构建并安装,开发源码验证则使用 `bundle exec ruby examples/backup.rb`。示例中的相对备份、日志路径统一基于项目根目录;进程中显式传入的 `NC_CONFIG` 路径按启动目录解析。
419
+
420
+ 清单自动识别保留显式主机映射、设备规则和厂商覆盖的优先级。若厂商标签陈旧,而 `os: Comware` 与 `model: H3C ...` 同时确认 H3C,则自动使用 H3C 连接器;`H3C WX...` / `H3C AC...` 使用无线连接器。仅型号片段或操作系统单项不会覆盖其他厂商。
421
+
422
+ 备份部署示例 `.env.example` 使用 `NC_HOST_KEY_POLICY=accept_new`:首次连接自动登记到 OpenSSH known_hosts,已有密钥变化仍拒绝连接。`--host-key-policy strict|accept_new` 可覆盖本次策略,`--known-hosts FILE` 指定持久保存位置;不应删除密钥文件,否则会丢失历史身份记录。库本身默认仍为 strict。
423
+
424
+ 设备密钥变化也需自动更新时,使用 `--host-key-policy replace --known-hosts FILE`,或设置 `NC_HOST_KEY_POLICY=replace` 与 `NC_KNOWN_HOSTS`。文件必须显式指定;只在主机密钥变化错误时删除该设备旧记录并重连一次,不重放设备命令。建议使用备份任务专用的持久密钥文件。
425
+
426
+ 进度每秒更新一次心跳;设备无新输出时仍显示耗时,超过 10 秒的最久任务显示地址、阶段与任务耗时。预计剩余时间按已完成任务的平均吞吐计算,前五台完成前显示“估算中”,异构设备和末尾慢任务会使估算波动。终端事件刷新最多约每秒五次,退出或异常时回收显示线程;结束按错误代码汇总未完成任务。
427
+
428
+ 本地批量示例将配置写为 `hostname-ip.txt`(名称取 Netdisco name/dns),空名称使用 `unnamed`,特殊字符清理,IPv6 冒号替换为下划线。批次目录固定使用 UTC+8 的 `YYYY-MM-DD_HH-mm-ss`,同秒冲突追加 `_01` 等序号。每批包含 `plan.json`、`summary.json` 和人类可读的 `summary.txt`(时间、计数、失败分类及失败明细),报告权限为 0600。历史目录不改名;通用 Fleet/CLI 仍默认 IP 文件名,API 可显式传入 `filename_style: :hostname_ip`,该模式设备改名会改变文件路径与比较基线。
429
+
430
+ 公共备份辅助接口随 gem 分发,不依赖 examples 文件:
431
+
432
+ ```ruby
433
+ require "net/connector/netdisco"
434
+ netdisco = Net::Connector::Netdisco
435
+ options = netdisco::CLI::Options.parse(argv: ["--concurrency", "10"])
436
+ settings = netdisco::CLI::Options.settings(options)
437
+ client, credentials = netdisco::Connection.build(settings)
438
+ directory = Net::Connector::Storage::BatchDirectory.create("./backups")
439
+ # 获得 report 和 plan 后:
440
+ # report = netdisco::Report::Files.write(report, directory: directory, plan: plan, concurrency: 10)
441
+ ```
442
+
443
+ `CLI::Options.parse` 接受 `argv/input/output/error/program`,不修改传入参数数组;帮助通过返回值 `:help` 通知调用方,非法输入抛出 `ArgumentError`。`Connection.build` 接受 `input:`,标准输入凭据仅消费一行,RUN 确认由脚本编排负责。模块不加载 `.env` 或切换目录。
444
+
445
+
446
+ 批量示例由 `Netdisco::BackupRun` 统一编排,`CLI::Options.settings` 为示例和正式 CLI 共用配置覆盖入口。
447
+ 示例默认 `selected`,正式 CLI 保留 `strict` 默认;两者均支持显式 `--success-policy`,退出码与报告的 `policy_success` 一致。
448
+ 新示例 `summary.json` 使用标准 schema 2 的 `devices`,不再另造 `outcomes`;旧报告可继续用 `review_tftp.rb` 读取。
449
+ TFTP 的 `verification` 汇总与设备条目的 `server_file_verified` 区分上传回执和服务器文件核验,核验成功还包含 `local_file`、`server_archive_file`、`bytes`、`sha256`。
450
+ 报告通过私有原子写入保存;报告保存失败会阻止成功退出,终端最终结果在报告保存之后输出。
451
+
452
+ `--stdin-credentials` 的一行 JSON 保留 `netdisco_username`、`netdisco_password`、`device_username`、`device_password` 四个字段。
453
+ 清单来源为 PostgreSQL 时,前两个字段覆盖数据库用户名和密码,主机、数据库名及 TLS 参数仍由 NETDISCO_DB_* 提供;第二行 `RUN` 仍是执行设备任务的确认。
454
+
455
+ 使用 `accept_new` / `replace` 时,SSH 在每个会话的临时 known_hosts 中协商,认证后通过独立锁文件合并到共享文件。
456
+ 网络登录不占用共享锁;失败登录不删除原信任记录,`replace` 只替换当前主机。该同步协调本库进程,外部手工工具应避免同时改写同一文件。
data/docs/VERIFICATION.md CHANGED
@@ -107,7 +107,7 @@ debug Logger 计数目标样本;不关闭协议检查、丢弃步骤或共享
107
107
 
108
108
  每次完整测试同时写入 `tmp/coverage/summary.json`:Ruby 描述和平台、实际依赖版本、
109
109
  库文件总数/已加载数、逐文件及上述三个组的行/分支计数、未命中位置和未加载文件。
110
- `NET_CONNECTOR_COVERAGE_OUTPUT` 只改变输出位置,不改变门槛。报告不保存配置正文、
110
+ `NC_COVERAGE_OUTPUT` 只改变输出位置,不改变门槛。报告不保存配置正文、
111
111
  环境变量值或源码片段。CI 每个 Ruby/平台和最低依赖任务分别上传报告。
112
112
 
113
113
  `script/coverage-baseline.json` 来自 NC-00 实测的初始 dirty 工作树,保存三个组及
@@ -205,3 +205,44 @@ Gitleaks 默认规则之外,还检查网络设备密码/SNMP community 配置
205
205
  若凭据已经泄露,需在对应系统撤销或轮换;仅修改示例不能使旧凭据失效。
206
206
  扫描工具不会自动改写 Git 历史或真实设备备份。规则用于拦截常见泄露,
207
207
  发布前仍需人工确认设备名称、拓扑和业务配置等上下文信息是否适合公开。
208
+
209
+ ## JSON 2 发布依赖与 JSON 3 源码兼容分开验证
210
+
211
+ 默认及最低依赖任务验证实际发布依赖。TextFSM 0.2.0 的公开声明仍为 `json ~> 2.0`;
212
+ 本地同版本 gem 的修订不能证明普通消费者能安装 JSON 3。
213
+
214
+ CI 的 `json3-source-compatibility` 在 Ruby 3.2 / 4.0 检出固定 TextFSM commit
215
+ `733340de378f2d7fbe530b48f2cf1dd6c30c69b0`,应用仓库内
216
+ `script/compatibility/textfsm-json3.patch`,只放宽依赖声明,再执行完整测试和真实打包隔离安装。
217
+ 该任务明确代表尚未发布依赖声明下的源码兼容性;不自动发布、替换正式依赖或修改用户的全局 gem。
218
+
219
+ 复现时,在 `tmp/json3/textfsm` 准备该源码并应用补丁,然后运行:
220
+
221
+ ```sh
222
+ BUNDLE_GEMFILE=gemfiles/json3.gemfile bundle install
223
+ BUNDLE_GEMFILE=gemfiles/json3.gemfile bundle exec rake test
224
+ BUNDLE_GEMFILE=gemfiles/json3.gemfile bundle exec ruby script/verify_json3.rb
225
+ ```
226
+
227
+ ## 批次入口与密钥并发回归
228
+
229
+ `test/known_hosts_test.rb` 使用临时密钥文件、多进程同步屏障和本地 PTY,
230
+ 验证并发替换/首次登记、不相关主机保留、登录失败不提交、非标准端口以及并发密钥冲突。
231
+ 这些测试不连接真实 SSH 服务器。
232
+
233
+ `test/examples_test.rb` 验证示例进程退出码与报告策略一致、报告写入错误、统一 devices 结构、
234
+ TFTP 远端未核验回执以及自定义日志目录;离线复核同时支持历史 outcomes 和新 devices 报告。
235
+ `test/tftp_verification_test.rb` 验证空文件、旧文件、符号链接、目录逃逸和 verified 策略。
236
+
237
+ ## Ruby 惯用法重构回归
238
+
239
+ `test/profile_contract_test.rb` 验证 protected 交互钩子覆盖并调用 `super` 后,登录与命令确认仍采用扩展规则。
240
+ `test/engine_boundary_test.rb` 覆盖 finalize 的 nil/false 返回、回调重入、throw 与 open 块的 break、
241
+ 登录钩子异常/中断清理及后续重新连接;原有恢复测试继续限制最多一次连接恢复。
242
+ `test/netdisco_settings_test.rb` 验证显式 nil/false 优先于默认值,以及预算校验先于来源查询校验。
243
+ 输出敏感性、线程/Fiber 所有权、完成步骤保留由原有输出、拓扑和批次回归共同验证。
244
+
245
+ 实施记录使用当前工作区文件和方法可见性快照,区别已有改动与本轮增量,不以 Git HEAD 冒充修改前基线。
246
+ 源码根目录 `RENAMES.md` 记录批准的改名与兼容边界。完整验收仍运行 `bundle exec rake ci`;
247
+ 临时 PostgreSQL 使用 `bundle exec rake test:postgres`,可通过 `NC_TEST_PG_BINDIR` 指定服务端工具目录。
248
+ 最低 Ruby 3.2 必须在对应解释器中运行 lint/test;RuboCop 的 TargetRubyVersion 和 Ruby 4.0 测试不能替代此项。
data/docs/architecture.md CHANGED
@@ -116,7 +116,7 @@ Diagnostic 只保存固定词表中的码、类型、阶段和受控产物状态
116
116
 
117
117
  `DatabaseClient` 每次调用独占 PostgreSQL 连接,在只读事务中用扩展查询协议声明游标,原生解析器拒绝多语句和非查询输入。FETCH 大小及次数复用清单预算,单行模式使 libpq 不缓存整页;每行在追加前计数,额外列也消耗字节预算。驱动必须先解码单行,所以单个超大字段仍可超过 Ruby 预算的瞬时内存。总 deadline 覆盖连接和所有语句,每次 FETCH 前缩短 statement_timeout;正常完成回滚只读事务,异常关闭连接,任何失败均不交付部分清单。NOTICE、原始数据库异常及 cause 不进入日志或 CLI;稳定错误码区分连接失败、查询失败、无效清单和预算超限。SQL/参数可在 show-config 中查看,不能用于传递凭据。
118
118
 
119
- 数据库集成测试运行 `bundle exec rake test:postgres`,通过 `pg_config --bindir`(或 `NET_CONNECTOR_TEST_PG_BINDIR`)定位服务端工具。测试只创建临时 SCRAM 数据库和私有 Unix socket,退出时停止并删除;不会读取真实 Netdisco 凭据或连接已有服务。覆盖 SQL/参数、只读限制、真实认证失败、查询中途失败、各项预算、连接回收以及 CLI 计划。
119
+ 数据库集成测试运行 `bundle exec rake test:postgres`,通过 `pg_config --bindir`(或 `NC_TEST_PG_BINDIR`)定位服务端工具。测试只创建临时 SCRAM 数据库和私有 Unix socket,退出时停止并删除;不会读取真实 Netdisco 凭据或连接已有服务。覆盖 SQL/参数、只读限制、真实认证失败、查询中途失败、各项预算、连接回收以及 CLI 计划。
120
120
 
121
121
  ## 迁移与尚未启用的能力
122
122
 
@@ -166,7 +166,7 @@ NC-07C(TFTP 服务端验证适配器)同为可选扩展,本轮 **deferred*
166
166
 
167
167
  累计等于上限时当前脚本可正常结束,但下一条查询在发送前失败;超过上限的完整主响应先进入 steps,再返回 `ScriptOutputLimitExceeded`,后处理和下一命令不会继续。追加查询不进入主脚本的公开 steps,但计入预算并在错误中保留实际查询命令。吞掉查询预算异常的钩子不能让超额脚本报告成功。已执行命令不重放;确认的 TFTP 完成行仍可构造带收尾错误的回执。
168
168
 
169
- 累计检查不在读取中途切断当前命令,最多还会接收一个受 `max_output_bytes` 限制的响应;已完成步骤不截断或丢弃。配置清理、解析、用户回调和反复 `Result#output` 的副本不计入该字节预算,因此它不是 RSS 或整批内存硬上限。合成基准不足以确定适合所有设备的默认阈值,因此默认不限制累计值。设置通过 YAML `ssh.max_script_output_bytes`、`NET_CONNECTOR_MAX_SCRIPT_OUTPUT_BYTES` 和 CLI 同名选项进入批次策略快照,优先级为 CLI > ENV > YAML;未配置时不传递该可选键,显式凭据 resolver 可覆盖连接参数。
169
+ 累计检查不在读取中途切断当前命令,最多还会接收一个受 `max_output_bytes` 限制的响应;已完成步骤不截断或丢弃。配置清理、解析、用户回调和反复 `Result#output` 的副本不计入该字节预算,因此它不是 RSS 或整批内存硬上限。合成基准不足以确定适合所有设备的默认阈值,因此默认不限制累计值。设置通过 YAML `ssh.max_script_output_bytes`、`NC_MAX_SCRIPT_OUTPUT_BYTES` 和 CLI 同名选项进入批次策略快照,优先级为 CLI > ENV > YAML;未配置时不传递该可选键,显式凭据 resolver 可覆盖连接参数。
170
170
 
171
171
  Ruby 对象显式拥有资源并使用关键字参数。`Profile` 提供有限声明入口,厂商策略负责差异行为。公开方法、厂商钩子、结果对象和 CLI JSON 字段以当前文档为准;内部不做运行时方法注入,也没有工作流 DSL。
172
172
 
@@ -288,12 +288,25 @@ TFTP 策略必须实现 `validate_options!(target, source_file:, vrf:)`、`recei
288
288
 
289
289
  `TerminalText.utf8` 在副本上验证设备或文件的原始字节,`TerminalText.render` 再使用严格终端渲染。Ruby 源码默认 UTF-8 不决定 PTY 或 `File.binread` 返回的编码。非法原始字节不能因控制符擦除而通过检查;渲染损坏多字节字符时同样拒绝,错误为不含正文或 cause 的 `invalid_output_encoding`。拓扑在表头匹配和计数前复用验证,TextFSM 每次解析创建独立 Parser。日志允许转义非法字节,原始备份不转码。
290
290
 
291
+ ### Ruby 实现与扩展边界
292
+
293
+ Base 的 protected `login_interactions`、`confirmation_interactions` 与 Profile 同名,子类可覆盖并调用 `super`;其他档案转发和脚本钩子继续保留。
294
+ 脚本执行器构造与结果收尾分别由私有方法实现,finalize 始终位于原会话锁和敏感输出作用域内。
295
+ Session 的私有方法统一放在 private 段;`login_once` 处理一次登录,外层连接流程拥有计时起点、失败清理及最多一次恢复。
296
+
297
+ Settings 的 `raw`、`config_hash` 是 protected 同类协作接口,快照和运行设置通过显式接收者调用;应用层使用 `public_config`。
298
+ 客户端预算、HTTP 与 PostgreSQL 选项分别解析,默认值和来源规则仍由原有定义提供,不重复声明一套配置 schema。
299
+ RunningConfig 的两处受控 `send` 保留,用于私有策略绑定与 protected 结果选择;不扩大策略生命周期接口。
300
+ 具体语法与异常约束见 [开发约定](../CONTRIBUTING.md#ruby-范式与语法边界)。
301
+
291
302
  ### 当前命名与接口调整
292
303
 
293
304
  项目在开发阶段只维护当前接口,不保留旧路径转发或旧方法别名。
294
305
 
295
306
  | 原名称或入口 | 当前入口 |
296
307
  | --- | --- |
308
+ | `Base#login_dialogues` / `confirmation_dialogues` | protected `login_interactions` / `confirmation_interactions`;子类覆盖同步改名,继续支持 `super` |
309
+ | `Fleet#backup_one` / `tftp_backup_one` / `run_one` | private `backup_device` / `tftp_backup_device` / `run_device`;公开批次入口不变 |
297
310
  | `engine`、`engine/base`、`engine/profile` | `net/connector`;底层分别为 `engine/core`、`device/base`、`device/profile` |
298
311
  | `Operations::*`、`operations/` | 设备流程为 `device/`;文件为 `Storage` / `storage/`;解析为 `TextFSM` / `textfsm.rb` |
299
312
  | 公共层中的厂商策略别名 | `Net::Connector::<Vendor>::RunningConfig` / `TftpBackup` / `Topology`,位于 `vendor/<厂商>/` |
@@ -0,0 +1,40 @@
1
+ # Non-secret settings only. Credentials belong in environment variables.
2
+ # Common NC_* environment variables override their YAML counterparts.
3
+ backup:
4
+ directory: ./examples/backups
5
+ concurrency: 4
6
+ # limit_per_vendor: 3 # Omit for full inventory.
7
+ netdisco:
8
+ source: http
9
+ page_size: 500
10
+ max_pages: 10000
11
+ max_response_bytes: 16777216
12
+ max_inventory_bytes: 134217728
13
+ max_devices: 100000
14
+ inventory_timeout: 300
15
+ allow_insecure_http: false
16
+ inventory:
17
+ include_hosts: []
18
+ exclude_hosts: []
19
+ include_vendors: []
20
+ vendor_overrides: {}
21
+ host_overrides: {}
22
+ device_rules: []
23
+ # device_rules:
24
+ # - vendor: Cisco
25
+ # model_prefix: N9K
26
+ # connector: cisco_nxos
27
+ ssh:
28
+ protocol: ssh
29
+ vendor_protocols: {}
30
+ host_key_policy: accept_new # Persist first-seen keys; reject changed keys.
31
+ log_level: info
32
+ max_script_output_bytes: null
33
+ tftp:
34
+ vrfs:
35
+ cisco_nxos: management
36
+ hillstone: mgt-vr
37
+ # server: 192.0.2.10
38
+ # h3c_source_file: flash:/startup.cfg
39
+ # h3c_wireless_source_file: flash:/startup.cfg
40
+ # huawei_source_file: flash:/startup.cfg