net-connector 0.15.0 → 0.16.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/docs/architecture.md +16 -6
  4. data/docs/netdisco.md +8 -4
  5. data/docs/netdisco.zh-CN.md +10 -6
  6. data/docs/usage.md +1 -1
  7. data/docs/usage.zh-CN.md +2 -2
  8. data/lib/net/connector/collect/progress.rb +70 -0
  9. data/lib/net/connector/config.rb +367 -0
  10. data/lib/net/connector/device/interfaces/audit.rb +3 -3
  11. data/lib/net/connector/device/interfaces/{interface_blocks.rb → blocks.rb} +1 -1
  12. data/lib/net/connector/device/interfaces/{interface_description.rb → description.rb} +6 -6
  13. data/lib/net/connector/device/interfaces/immediate_strategy.rb +2 -2
  14. data/lib/net/connector/device/interfaces/{interface_name.rb → name.rb} +1 -1
  15. data/lib/net/connector/device/interfaces/stages.rb +2 -2
  16. data/lib/net/connector/device/interfaces/strategy.rb +3 -3
  17. data/lib/net/connector/device/interfaces.rb +5 -5
  18. data/lib/net/connector/engine/errors.rb +4 -4
  19. data/lib/net/connector/engine/result.rb +2 -2
  20. data/lib/net/connector/netdisco/cli/backup.rb +17 -13
  21. data/lib/net/connector/netdisco/cli/help.rb +1 -1
  22. data/lib/net/connector/netdisco/cli/options.rb +49 -16
  23. data/lib/net/connector/netdisco/cli/setup.rb +32 -12
  24. data/lib/net/connector/netdisco/cli/topology.rb +14 -7
  25. data/lib/net/connector/netdisco/cli.rb +27 -8
  26. data/lib/net/connector/netdisco/config/defaults.rb +1 -1
  27. data/lib/net/connector/netdisco/config/initializer.rb +17 -11
  28. data/lib/net/connector/netdisco/config.rb +49 -1
  29. data/lib/net/connector/netdisco/fleet.rb +1 -1
  30. data/lib/net/connector/netdisco/operation.rb +3 -3
  31. data/lib/net/connector/netdisco/progress.rb +41 -283
  32. data/lib/net/connector/netdisco/settings.rb +11 -2
  33. data/lib/net/connector/netdisco/topology/options.rb +3 -3
  34. data/lib/net/connector/parallel/pool.rb +3 -4
  35. data/lib/net/connector/progress.rb +288 -0
  36. data/lib/net/connector/resources.rb +40 -0
  37. data/lib/net/connector/vendor/cisco/topology.rb +1 -1
  38. data/lib/net/connector/vendor/h3c/topology.rb +1 -1
  39. data/lib/net/connector/vendor/hillstone/topology.rb +1 -1
  40. data/lib/net/connector/vendor/huawei/topology.rb +2 -2
  41. data/lib/net/connector/version.rb +1 -1
  42. metadata +23 -6
  43. data/lib/net/connector/netdisco/resources.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7834c1a83cf57694959a803bfe20d69ac11b6acba8b7d0e869d331983d73f766
4
- data.tar.gz: 0d5d5d735508b56194abe68cac9c2f982e24f3f8a031f2003dd46781ceae070e
3
+ metadata.gz: 2a856fe47717bf2c4a52253eb7040e638001a5e4fbb780c73121de841bf10cfb
4
+ data.tar.gz: 2465d4ad5839cfd66755276ac93ffa42ba1c663566ca259908ad18826ee68df9
5
5
  SHA512:
6
- metadata.gz: 14e385a0beb987446e825306460bfeaa3b1231676a8e776ab9bfa9bdcf89c2ba1123f8f8b4ad860906e356dfd84e83f298097c36ee9fecc6dee54324fa784a64
7
- data.tar.gz: 5ade1fafd24e2bb15f139916d71ca1749c19a3762f9868650995f193334a0dfb05561750ec7a080083de92f877c87a7a86bbb992c3ba483a1b038beebf64a2a5
6
+ metadata.gz: da0967943641ebb2dced998fccdf816d6b9ac5f061134c463b4a21c51d1071d0ff9262247435a05d99102517e20a5587db432f81be761bd25d9071becccfd216
7
+ data.tar.gz: c59656941ef560f45f0f164bee6f0a8f9ae16dacb08fcb75ec88d4db651d091c2a1cc14cc88206930d130d3dce97a563b0a1e34562179cc0a733dbfb897a6328
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## 未发布
4
4
 
5
+ ## 0.16.0 - 2026-10-20
6
+
7
+ - 将共享资源预算迁至 `Net::Connector::Resources`,接口辅助对象统一为 `Interfaces::Name/Blocks/Description`;调用方及加载路径同步迁移。
8
+
9
+ - 将备份进度的终端刷新、心跳、事件与输出故障处理抽为 `Net::Connector::Progress`;VXLAN 与 Wireless 通过 `Collect::Progress`(`collect/progress.rb`)共用显示,保留各领域的成功判定与安全报告。
10
+
11
+ - 修复普通 `ruby examples/backup.rb` 混用已安装 net-connector 而未读取分层配置的问题;备份、TFTP 与拓扑入口直接加载相邻项目代码,保留第三方依赖环境。入口回归不再额外注入 `-Ilib`,覆盖脚本目录及其他启动目录的真实调用。
12
+ - 统一 VXLAN、Wireless 与备份/拓扑 CLI 的分层配置服务:系统、用户、脚本、工作目录和显式文件逐层覆盖;命令行 > 进程环境 > YAML > `.env` > 默认值,数组整体替换,路径按来源解析,初始化写入用户配置目录。单个 YAML 拒绝重复键和多文档;初始化直接使用共享快照,移除旧 `load_env` 分支。
13
+
14
+ ## 0.15.1 - 2026-10-19
15
+
16
+ - `Result`、领域错误及底层错误的诊断摘要复用受控词表,避免配置处理钩子将敏感正文放入错误元数据后经 `inspect` 或 `PP` 泄露;原始错误字段与已完成业务输出仍保留。
17
+
5
18
  ## 0.15.0 - 2026-10-19
6
19
 
7
20
  - SSH 归档主连接保持到批次结束,避免空闲超时后密码认证失效;关闭后释放退出清理登记,收尾取消保留原始失败与已完成回执。
data/docs/architecture.md CHANGED
@@ -8,10 +8,12 @@
8
8
  | 对话协议与响应读取 | 对话规则、交互应答与响应值分别建模;读取器管理截止时间与输出预算 | `engine/dialogue.rb`、`engine/interaction.rb`、`engine/response.rb`、`engine/response_reader.rb` |
9
9
  | 错误与脱敏 | 错误类型和安全诊断;秘密作用域及 Expect 脱敏适配独立实现 | `engine/errors.rb`、`engine/redactor.rb` |
10
10
  | 日志 | 事件上下文与生命周期;安全事件、格式和字节流分别实现 | `engine/log.rb`、`engine/log/` |
11
+ | 宿主配置 | Config 发现并逐层加载 YAML 与环境文件,记录来源和覆盖顺序 | `config.rb` |
11
12
  | 设备入口 | 能力组合、会话代理、通用脚本钩子 | `device/base.rb` |
12
13
  | 设备档案 | 不可变规则与策略接口校验;Builder 负责声明 DSL | `device/profile.rb`、`device/profile/` |
13
14
  | 配置采集与保存 | 采集策略生命周期、完整性校验、显式保存 | `device/running_config.rb`、`device/running_config/`、`device/save_config.rb` |
14
- | 并发与观察 | 有限并发、后台观察线程的停止与回收 | `parallel/pool.rb`、`parallel/observer.rb` |
15
+ | 并发与观察 | 有限并发、设备资源预检、后台观察线程的停止与回收 | `parallel/pool.rb`、`resources.rb`、`parallel/observer.rb` |
16
+ | 批次进度 | Progress 管理终端刷新、心跳与输出故障隔离;Collect::Progress 映射采集状态与报告保存结果 | `progress.rb`、`collect/progress.rb` |
15
17
  | 设备作用域 | 创建连接器、执行任务、关闭并分别交付值与故障 | `device/scope.rb` |
16
18
  | 本地备份 | 采集、摘要比较、文件持久化和完成回执 | `device/local_backup.rb` |
17
19
  | TFTP | 参数预检、上传、完成证据、目标和回执 | `device/tftp.rb`、`device/tftp/` |
@@ -31,6 +33,8 @@
31
33
 
32
34
  文件名表达本文件的主要职责,领域归属通过命名空间表达。Netdisco 的清单预算与缓存位于 `inventory/`,分别为 `Inventory::Budget` 和 `Inventory::Cache`;配置操作的批次与逐台结果位于 `operation/`,分别为 `Operation::Batch` 和 `Operation::Outcome`。`Report::Store` 只持久化报告,Fleet 通过 `report_store:` 接收写入器。服务器上传核验与归档分别为 `Tftp::Verifier` 和 `Tftp::Archive`。
33
35
 
36
+ `Interfaces::Name`、`Interfaces::Description`、`Interfaces::Blocks` 分别对应 `interfaces/name.rb`、`description.rb`、`blocks.rb`,子项不重复父级名称。共享宿主配置用 `Net::Connector::Config`,Netdisco 字段校验用 `Net::Connector::Netdisco::Config`;两者在调用处明确区分。会话连接参数仍由 `Configuration` 校验,不读取环境文件。
37
+
34
38
  引擎的 `Command`、`Script`、`Interaction`、`Dialogue`、`Response`、`Command::Result`、`Result` 各有同名文件;传输事件在 `event.rb`,适配器集合在 `transports.rb`。日志的 `RedactingWriter` 与 `Transcript` 位于 `log/` 下各自的同名文件。存储边界使用 `FileReader` 表示受控读取,`AtomicFile` 表示带阶段回执的原子替换;权限和持久性约束仍由这些组件维护。
35
39
 
36
40
  ## 公开业务方法与确认范围
@@ -378,13 +382,13 @@ RunningConfig 的两处受控 `send` 保留,用于私有策略绑定与 protec
378
382
 
379
383
  ## 接口描述规则
380
384
 
381
- 原始 `Neighbor` 字段和计划证据完整保留发现值。`Interfaces::InterfaceName.key` 用于匹配本机接口别名与运行配置名称;`Interfaces::InterfaceName.configuration` 保留配置命令的接口展开规则。`Interfaces::InterfaceName.short` 只决定描述里对端接口的显示形式,绝不替换本机下发命令中的接口名。
385
+ 原始 `Neighbor` 字段和计划证据完整保留发现值。`Interfaces::Name.key` 用于匹配本机接口别名与运行配置名称;`Interfaces::Name.configuration` 保留配置命令的接口展开规则。`Interfaces::Name.short` 只决定描述里对端接口的显示形式,绝不替换本机下发命令中的接口名。
382
386
 
383
- `Interfaces::InterfaceDescription.format(neighbor, abbreviate: true, lowercase: false)` 是各厂商同步与计划共用的纯函数,生成 `To_<名称>_<接口简称>`。接口族的全称、简称和大小写统一,端口、拆分通道与子接口后缀保持不变;H3C 二层和三层聚合身份不合并。完整规则及厂商命名依据见[设备指南](usage.zh-CN.md#邻居发现与接口描述)。
387
+ `Interfaces::Description.format(neighbor, abbreviate: true, lowercase: false)` 是各厂商同步与计划共用的纯函数,生成 `To_<名称>_<接口简称>`。接口族的全称、简称和大小写统一,端口、拆分通道与子接口后缀保持不变;H3C 二层和三层聚合身份不合并。完整规则及厂商命名依据见[设备指南](usage.zh-CN.md#邻居发现与接口描述)。
384
388
 
385
389
  `abbreviate: false` 保留接口原拼写,`lowercase: true` 仅将对端接口改为小写。自定义代码块拿到原始邻居,可生成完整描述。输出仍须经过 80 字节及字符校验、证据复核和回读;同步调用表达写入意图,独立计划应用仍要求确认。
386
390
 
387
- `Interfaces::InterfaceDescription.commands(interface:, description:, leave: "exit")` 为 IOS/NX-OS 和山石分别生成 `interface`、`description`、退出命令;H3C 使用 `leave: "quit"`。进入、退出全局配置视图和保存完成证据由厂商负责。各阶段命令分别由策略声明,PAN-OS 的命令构造/超时辅助方法不会启用未经验证的候选提交。命令构造器本身不连接设备,也不能绕开已审核的拓扑计划。
391
+ `Interfaces::Description.commands(interface:, description:, leave: "exit")` 为 IOS/NX-OS 和山石分别生成 `interface`、`description`、退出命令;H3C 使用 `leave: "quit"`。进入、退出全局配置视图和保存完成证据由厂商负责。各阶段命令分别由策略声明,PAN-OS 的命令构造/超时辅助方法不会启用未经验证的候选提交。命令构造器本身不连接设备,也不能绕开已审核的拓扑计划。
388
392
 
389
393
  ## 敏感命令的生命周期
390
394
 
@@ -412,6 +416,8 @@ RunningConfig 的两处受控 `send` 保留,用于私有策略绑定与 protec
412
416
 
413
417
  连接、命令和操作失败统一经过 `Log#log_failure`。错误类型、错误码和阶段由 `Error::Metadata` 的固定词表过滤,Netdisco 报告复用同一规则;未知错误码不输出,未知阶段使用引擎当前阶段,未知类型归为 StandardError。异常正文仍先由会话脱敏,防止配置钩子把未登记的配置放进错误元数据后进入日志。
414
418
 
419
+ `Result`、`Error` 和 `UnderlyingError` 的 `inspect` 同样使用诊断词表,`PP` 沿用这些摘要。错误摘要不展开调用方提供的主机字段;原始错误字段仍可显式读取。`Error#to_s` 保持 Ruby 异常的消息语义,不替换为摘要。
420
+
415
421
  操作后处理无论抛出异常还是返回失败 Result,都在敏感作用域退出前归一化错误,再记录完成事件;Result 的已完成步骤及配置内容保持原样。最终处理继承实际执行的命令及交互敏感性,包括准备钩子、替换命令和追加查询;只累计标记,不长期保留秘密,也不影响后续普通命令的日志。
416
422
 
417
423
  业务执行与完成事件的异常范围分开:完成事件只尝试一次,观察者失败不能触发第二次相同通知。已有登录、命令或业务错误优先于失败日志的错误;脚本最终处理已生成的 Result.config 与 steps 不因日志收尾丢失。日志上下文刷新和传输接线恢复仍执行清理,但保留正在传播的主错误及 Interrupt。TFTP 的失败证据同样先决定错误码,再尝试记录失败事件。
@@ -432,7 +438,7 @@ RunningConfig 的两处受控 `send` 保留,用于私有策略绑定与 protec
432
438
 
433
439
  ## 公开契约与 Rails 接入
434
440
 
435
- 应用代码优先使用 `Net::Connector.open/build`、`Base` 的公开设备方法、`Command`、`Script`、结果对象,以及 `Netdisco::Fleet`。厂商扩展使用 `Profile` DSL 和文档中列出的策略接口;`Profile::Builder`、`Session` 的状态字段、工作线程调度及解析辅助方法属于内部实现。扩展直接加载当前设备模块或厂商目录下的类。项目仍处于 0.x,公开契约的变更会在更新记录中说明。
441
+ 应用代码优先使用 `Net::Connector.open/build`、`Base` 的公开设备方法、`Command`、`Script`、结果对象,以及 `Netdisco::Fleet`。独立批次宿主可直接加载 `net/connector/parallel/pool` 和 `net/connector/resources`,复用 `Parallel::Pool#map` 与 `Resources.prepare!`。厂商扩展使用 `Profile` DSL 和文档中列出的策略接口;`Profile::Builder`、`Session` 的状态字段、线程池内部调度及解析辅助方法属于内部实现。扩展直接加载当前设备模块或厂商目录下的类。项目仍处于 0.x,公开契约的变更会在更新记录中说明。
436
442
 
437
443
  gem 不依赖 Rails,也不自动创建数据库模型或管理应用的连接池。`logger: Rails.logger` 由应用持有,连接器写入安全事件对象,不修改或关闭它。数据库报告通过 `Report::Store::Database` 注入仓储,写报告发生在调用线程中;逐设备凭据解析、连接器工厂和回调则运行在工作线程中。
438
444
 
@@ -450,8 +456,12 @@ end
450
456
 
451
457
  ## 公共并发、配置批次与清单缓存
452
458
 
459
+ 子职责按所属业务组织目录和常量:`collect/progress.rb` 对应 `Collect::Progress`,`parallel/pool.rb` 对应 `Parallel::Pool`,`netdisco/report/files.rb` 对应 `Netdisco::Report::Files`。采集进度适配器复用公共 `Progress` 渲染与观察线程,设备选择和结果解释仍由各业务入口负责。
460
+
453
461
  `Parallel::Pool` 只持有线程、启动闸门和完成事件,按输入顺序交付块结果。普通设备失败如何转成结果由业务适配负责;创建并登记线程时延迟异步中断,登记完成后恢复,启动失败不执行任何设备块。工作线程捕获异常并由调用线程同步传播,避免宿主全局线程策略影响无关线程。`Netdisco::Worker` 为备份与配置操作共用计时、结果槽位和通知适配,不再自行管理线程;设备结果先写入槽位,再执行通知回调。
454
462
 
463
+ `Resources.prepare!(parallel:)` 独立负责设备文件句柄预算,在创建会话前按实际活跃设备数预检,只提高当前进程软上限,不修改硬上限或系统配置。空批次传入 0,其他值须为 1..500 的整数。Netdisco、VXLAN 和 Wireless 复用同一资源预算及线程池;通用 Pool 不隐式修改进程资源限制。队列只持有输入索引,任务数组在入口固定一次,避免每个任务额外分配包装数组;任务对象的业务校验和不可变所有权仍由宿主负责。
464
+
455
465
  `Device::Scope` 持有单台任务连接器,分别交付业务值与普通故障,收尾不覆盖先前错误或中断。`Netdisco::Operation` 为配置脚本与接口描述同步、规划/应用提供全批预检、固定策略、动态凭据和安全摘要;它保留领域 `Result`、计划和完成步骤,使用独立 `Operation::Batch`,不借用备份路径或上传状态。应用审批计划必须走原拓扑重验、变更、回读、保存流程;有限并发不代表跨设备事务或自动重试安全。
456
466
 
457
467
  操作中断后,`Operation#last_batch` 和 `Fleet#last_operation_batch` 保留已完成回执、已启动但结果未知的 `interrupted` 槽位及 `not_started` 槽位。批次仍抛出原始 `Interrupt`,并明确标记整体中断;下一次操作开始即清除旧回执,预检失败也不能读到上一次结果。取消任务不补造耗时,未启动任务不补造起止时间,结果未知不代表可以重放配置。
@@ -468,6 +478,6 @@ end
468
478
 
469
479
  `Netdisco::Batch` 复制结果数组和回调诊断,在构造时累计状态计数;`counts` 返回可修改的独立 Hash。未知状态不能满足成功策略。更新结果集合须使用 `with`,它重新计算计数。上述领域值和远程备份目标、回执的 `with` 显式经过构造函数,避免依赖不同 Ruby 版本中原生 `Data#with` 的初始化行为。
470
480
 
471
- 拓扑的 `InterfaceBlocks` 模块集中核对 interface 块与解析记录的完整性,由立即生效的变更策略和 Huawei 的只读策略共同组合,读取能力不必继承写入流程。`description_key(row)` 把描述记录映射为领域身份;Junos 逻辑单元使用 `<物理接口>.<单元号>`,保留物理接口描述。FortiOS 读取当前视图顶层 `config system interface`,接口 `description` 与显示 `alias` 分开处理,嵌套 IPv6 表不产生接口记录;无法确认作用域、闭合或记录数量时拒绝结果。
481
+ 拓扑的 `Blocks` 模块集中核对 interface 块与解析记录的完整性,由立即生效的变更策略和 Huawei 的只读策略共同组合,读取能力不必继承写入流程。`description_key(row)` 把描述记录映射为领域身份;Junos 逻辑单元使用 `<物理接口>.<单元号>`,保留物理接口描述。FortiOS 读取当前视图顶层 `config system interface`,接口 `description` 与显示 `alias` 分开处理,嵌套 IPv6 表不产生接口记录;无法确认作用域、闭合或记录数量时拒绝结果。
472
482
 
473
483
  `Interfaces::QuotedText` 是描述字段的纯解码边界,不接触设备会话;厂商策略选择无引号文本的处理规则,解析异常统一携带 host、parse 阶段和 unrecognized_output 代码,不携带字段原文或底层异常 cause。配置模板保留完整带引号字段供业务层解码,不能先删外层引号而留下未还原的转义字符。
data/docs/netdisco.md CHANGED
@@ -22,7 +22,7 @@ Use `ND_API_KEY` instead of the HTTP username/password pair when available. HTTP
22
22
 
23
23
  Before connecting devices, batches reserve file descriptor capacity for SSH pipes, PTYs, logs and locks. The process soft limit is raised within its existing hard limit when needed; an insufficient hard limit stops execution with a `ulimit -n` hint. Shell and system limits are not changed. SSH archive connections are sized per server, with up to four simultaneous connection preflights.
24
24
 
25
- YAML contains only non-sensitive settings. Unknown fields, Ruby object tags, and credentials in YAML are rejected. Settings precedence is CLI, supported environment variables, YAML, then defaults. Files load only through `--config FILE` or `NC_CONFIG`. Start with [backup.example.yml](../examples/backup.example.yml):
25
+ YAML contains only non-sensitive settings. Unknown fields, Ruby object tags, and credentials in YAML are rejected. CLI precedence is CLI options, original process environment, layered YAML, layered `.env`, then defaults. The shared process `NC_PARALLEL` also overrides YAML `topology.parallel`; the mode-specific process `NC_TOPOLOGY_PARALLEL` wins within that layer, and CLI `--parallel` wins overall. Canonical public environment names follow `Config::FIELDS`; library `Settings.from_env` and `Settings.from_file` retain their explicit contracts. Start with [backup.example.yml](../examples/backup.example.yml):
26
26
 
27
27
  ```yaml
28
28
  netdisco:
@@ -78,7 +78,9 @@ The installed command is `net-backup`; source checkouts use `bundle exec bin/net
78
78
 
79
79
  Here `vendor` is the connector platform key, such as `cisco_ios` or `cisco_nxos`, rather than the manufacturer. Filtering and sampling use that key: a limit of five can select five IOS devices and five NX-OS devices. `source_vendor` retains the original inventory vendor label.
80
80
 
81
- Interactive batch examples use `CLI::Backup` and `CLI::Topology`; `CLI::Credentials.resolve` supplies the inventory source and per-device credential resolver, while `CLI::Options` parses arguments and configuration overrides. These components live under `netdisco/cli/`. The examples explicitly pass `load_env: true` to make public environment values defaults below YAML and CLI options. The formal CLI retains its existing environment precedence.
81
+ Interactive batch examples use `CLI::Backup` and `CLI::Topology`; `CLI::Credentials.resolve` supplies the inventory source and per-device credential resolver, while `CLI::Options` parses arguments and configuration overrides. These components live under `netdisco/cli/`. All entrypoints use the same configuration snapshot: CLI > process environment > YAML > `.env` > built-in defaults. Initialization consumes that snapshot too; there is no separate environment-loading switch.
82
+
83
+ Host, name and vendor selectors reject whitespace-only entries and empty comma-separated items. YAML `[]` and an empty environment string explicitly clear a selector; malformed text never becomes an unrestricted selection. Higher-priority replacements are applied before the effective selector is validated.
82
84
 
83
85
  `--export IP` reads an existing canonical `<IP>.txt` file offline. It writes original bytes to stdout, or atomically writes a private file with `--output`. It does not require inventory or device credentials. Hostname-prefixed batch-example files are not searched by this command.
84
86
 
@@ -238,7 +240,9 @@ Each batch retains `plan.json`, `events.jsonl`, `summary.json`, and `summary.txt
238
240
 
239
241
  Examples support hidden `--ask-password` and `--ask-netdisco-password` prompts, plus environment credentials. `--stdin-credentials` reads one JSON line with `netdisco_username`, `netdisco_password`, `device_username`, and `device_password`; a second `RUN` line confirms device execution. For PostgreSQL, the first pair overrides database user/password.
240
242
 
241
- Examples load the project `.env` through `dotenv` without overriding existing environment values. Direct `ruby examples/backup.rb` uses the installed gem; `bundle exec ruby` tests checkout code. Example directory paths are based on the project root; an environment-provided `NC_CONFIG` resolves from the startup directory. Shared library helpers do not load `.env` or change directories. See [examples](../examples/README.md) (Chinese).
243
+ CLI and example hosts discover directories from low to high: `/etc/net-connector`, `/usr/local/etc/net-connector`, `$XDG_CONFIG_HOME/net-connector` (default `~/.config/net-connector` for the running user), the entry script directory, and startup cwd. Duplicate directories are read once; parents are not searched. Each directory contributes `net-connector.yml` (`common` then `backup` or `topology` namespaces), then its tool YAML, plus `.env`. Common settings accept only `netdisco`, `inventory`, and `ssh`; tool namespaces contain a complete native tool document. `--config FILE` / `NC_CONFIG` append a final YAML layer; `--env-file FILE` appends a final environment layer. Arrays replace as a whole. YAML local paths resolve relative to their source file; `.env` selectors and declared paths resolve relative to that environment file; CLI and process paths resolve from startup cwd. SSH TFTP URIs remain intact. Explicit missing or malformed files fail; absent automatic candidates are skipped. Each YAML file must contain one document with unique mapping keys; duplicate keys and additional documents are rejected before settings are merged. `--show-config` reports public settings and source paths without passwords or environment file contents. Help reads no configuration. `--init` creates missing YAML and `.env` files in the user configuration directory or explicit paths, without overwriting existing files. It never writes into the installed gem directory. Default outputs remain `backups/` and `topologys/` under startup cwd. Hosts leave `ENV` and cwd unchanged. Both plain `ruby examples/backup.rb` and `bundle exec ruby examples/backup.rb` load the adjacent project library; third-party dependencies retain the caller's RubyGems/Bundler selection. The installed `net-backup` executable uses its installed gem. See [examples](../examples/README.md) (Chinese).
244
+
245
+ An explicitly selected `net-connector.yml` retains the same shared namespaces and source-relative paths as automatic discovery. `--init --non-interactive` writes a parallel count only when `--parallel` is supplied, so later changes to the shared `NC_PARALLEL` remain effective. Interactive initialization displays the inherited count; pressing Enter preserves inheritance, while entering a number writes an explicit override.
242
246
 
243
247
  ## Environment reference
244
248
 
@@ -267,4 +271,4 @@ Topology comparisons preserve semantically equal existing descriptions. Set `top
267
271
 
268
272
  Topology examples also export `scripts/<name>-<IP>.cfg` for verified changes and `devices/<name>-<IP>.txt` for every result, using the backup filename rules. Plans and summaries reference the files and command SHA-256. No-change, discovery and failed results have no empty command file. Export failure prevents application; exported commands do not bypass live evidence revalidation.
269
273
 
270
- Topology output distinguishes unresolved evidence from execution failures and defaults to a compact Chinese summary. Use `--verbose` for per-device events. JSON keeps status codes and adds `statistics`; pending changes or unresolved evidence still exit with code 1. Startup shows the resolved configuration file: `NC_CONFIG` also takes precedence over the topology example default, so shared configuration must include the appropriate `topology` policy.
274
+ Topology output distinguishes unresolved evidence from execution failures and defaults to a compact Chinese summary. Use `--verbose` for per-device events. JSON keeps status codes and adds `statistics`; pending changes or unresolved evidence still exit with code 1. Startup shows the resolved configuration file: `NC_CONFIG` is also the final topology configuration layer, so shared configuration must include the appropriate `topology` policy.
@@ -52,7 +52,9 @@ exit 1 unless batch.success?
52
52
 
53
53
  gem 安装后的命令为 `net-backup`;源码入口为 `bin/net-backup`,可在项目目录运行 `bundle exec bin/net-backup --help`。原命令 `net-connector-backup` 已改名。
54
54
 
55
- 命令行程序 `net-backup` 的 YAML 文件只允许非敏感设置;Netdisco 和设备凭据留在环境变量中。`ND_URL` 从环境覆盖 YAML 地址,`NC_PARALLEL` 覆盖 YAML 并发数;其他非敏感策略通过 YAML 或 CLI 设置。批量示例也支持 `NC_CONFIG`,可从 [完整配置示例](../examples/backup.yml) 开始。只有传入 `--config FILE` 或设置 `NC_CONFIG` 时才加载文件:
55
+ 地址、名称和厂商筛选拒绝空白条目及逗号分隔的空项。YAML `[]` 与环境变量的空字符串可显式清空筛选;无效文本不会被当作全量选择。先应用高优先级覆盖,再校验最终筛选值。
56
+
57
+ 命令行程序 `net-backup` 的 YAML 文件只允许非敏感设置;Netdisco 和设备凭据留在环境变量中。CLI 值优先级为:命令行参数 > 原进程环境 > 分层 YAML > 分层 `.env` > 内置默认值;支持 `Config::FIELDS` 中的规范公开环境变量。原进程的共享 `NC_PARALLEL` 也覆盖 YAML 的 `topology.parallel`;同层同时设置时,专用 `NC_TOPOLOGY_PARALLEL` 优先,CLI `--parallel` 最高。库的 `Settings.from_env` / `Settings.from_file` 保留显式加载契约。批量示例也支持 `NC_CONFIG`,可从 [完整配置示例](../examples/backup.yml) 开始。自动逐层加载配置;`--config FILE` 或 `NC_CONFIG` 作为最后一层,不丢弃低层未覆盖字段:
56
58
 
57
59
  ```yaml
58
60
  netdisco:
@@ -179,7 +181,7 @@ ssh:
179
181
  max_script_output_bytes: 16777216
180
182
  ```
181
183
 
182
- `Fleet` 每次规划或执行前通过 `Settings#snapshot(mode:)` 固定非敏感设置,包括筛选规则、目录、并发、协议、主机密钥、日志、TFTP 参数和清单预算。执行中修改 ENV 不改变当批策略;再次调用会读取新值。CLI 一次调用的规划与执行共用策略,优先级为 CLI > 常用环境变量 > YAML > 默认值。`Settings#validate!(mode:)` 复用连接配置及 Planner 的枚举和范围规则;`--show-config` 会拒绝非法设置,`--export` 只验证离线目录,不要求清单地址或认证。
184
+ `Fleet` 每次规划或执行前通过 `Settings#snapshot(mode:)` 固定非敏感设置,包括筛选规则、目录、并发、协议、主机密钥、日志、TFTP 参数和清单预算。执行中修改 ENV 不改变当批策略;再次调用会读取新值。CLI 一次调用的规划与执行共用策略,优先级为 CLI > 进程环境 > YAML > `.env` > 默认值。`Settings#validate!(mode:)` 复用连接配置及 Planner 的枚举和范围规则;`--show-config` 会拒绝非法设置,`--export` 只验证离线目录,不要求清单地址或认证。
183
185
 
184
186
  每台任务开始时仍读取设备凭据,`Settings.from_file` 也保留轮换能力。纯策略快照不保存密码、API key 或凭据解析器。需要让多次 API 调用共用策略时,可传 `Settings.new.for_run(mode: :backup)`。自定义 `credentials:` 解析器仍可逐设备返回连接设置,它显式给出的选项优先于批次默认值,由调用方负责一致性;注入的 `inventory:` 生命周期也由调用方管理。传入 `plan:` 的执行不会重新拉取或筛选已批准清单。`fleet.devices` 可在不连接设备时检查映射。
185
187
 
@@ -201,7 +203,7 @@ ruby examples/backup.rb --json > result.jsonl
201
203
  ruby examples/backup.rb --parallel 10 --username backup-user --ask-password
202
204
  ```
203
205
 
204
- 批量示例支持 `--parallel`(1 至 500)、`--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` 的相对路径基于项目根目录。
206
+ 批量示例支持 `--parallel`(1 至 500)、`--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` 的相对路径基于启动目录。
205
207
 
206
208
  ## 部署、示例与验证
207
209
 
@@ -218,7 +220,9 @@ CI 在 Linux 和 macOS 上覆盖 Ruby 3.4、4.0。`script/ci` 扫描源码与可
218
220
 
219
221
  真实凭据应放在环境变量和版本库外的本地配置中。检查范围、依赖政策和忽略规则见[验证文档](VERIFICATION.md),发布流程见[发布文档](RELEASING.md)。
220
222
 
221
- 备份示例通过 `examples/boot.rb` 读取项目根目录 `.env`,不会自动切换依赖或注入源码路径。直接 `ruby examples/backup.rb`(或在 `examples/` 中执行 `ruby backup.rb`)使用已安装的 gem;修改库后必须重新构建并安装,开发源码验证则使用 `bundle exec ruby examples/backup.rb`。示例中的相对备份、日志路径统一基于项目根目录;进程中显式传入的 `NC_CONFIG` 路径按启动目录解析。
223
+ 备份示例与正式 CLI 共用配置发现器;`boot.rb` 只加载库,不修改 `ENV` 或工作目录。目录由低到高:`/etc/net-connector`、`/usr/local/etc/net-connector`、`$XDG_CONFIG_HOME/net-connector`(默认当前运行用户 `~/.config/net-connector`)、入口脚本目录、启动目录;重复只读一次,不递归搜索父目录。每层先读 `net-connector.yml` 的 `common` 与当前工具命名空间,再读 `backup.yml` / `topology.yml`,同时叠加 `.env`;`common` 只放 `netdisco`、`inventory`、`ssh`,专用命名空间包含完整的原生配置。`--env-file FILE` 最后叠加环境文件,原进程环境始终优先。YAML 本地路径按各自源文件目录解析;`.env` 的配置选择器和声明路径相对该环境文件,CLI 与进程路径相对启动目录;SSH TFTP URI 保留原值。数组整体替换,显式缺失或无效文件报错,自动缺失候选跳过。每个 YAML 文件只接受单文档且映射键不得重复,避免设备范围被静默覆盖或忽略。`--show-config` 可查看公开值与来源路径,不输出凭据或环境文件正文;帮助不读取用户配置。`--init` 在用户配置目录或显式路径创建缺失文件,不覆盖已有文件、不写入 gem 安装目录,输出默认仍为启动目录的 `backups/` 与 `topologys/`。直接 `ruby examples/backup.rb` 和 `bundle exec ruby examples/backup.rb` 均加载入口相邻项目的 `lib/`,第三方依赖沿用调用方环境;正式 `net-backup` 使用安装的 gem。
224
+
225
+ 显式 `--config` / `NC_CONFIG` 指向 `net-connector.yml` 时,仍按共享命名空间解析,并保留来源目录的路径语义。`--init --non-interactive` 仅在本次提供 `--parallel` 时写入并发值,默认值和继承的公共 `NC_PARALLEL` 不固定到 YAML,后续公共环境调整仍然生效。交互初始化提示当前继承值;回车保留继承,明确输入数字才保存覆盖。
222
226
 
223
227
  清单自动识别保留显式主机映射、设备规则和厂商覆盖的优先级。若厂商标签陈旧,而 `os: Comware` 与 `model: H3C ...` 同时确认 H3C,则自动使用 H3C 连接器;`H3C WX...` / `H3C AC...` 使用无线连接器。仅型号片段或操作系统单项不会覆盖其他厂商。
224
228
 
@@ -247,7 +251,7 @@ directory = Net::Connector::Storage::BatchDirectory.create("./backups")
247
251
 
248
252
  批量示例由 `Netdisco::CLI::Backup` 统一编排,`CLI::Options.settings` 为示例和正式 CLI 共用配置覆盖入口。
249
253
 
250
- 终端入口按职责放在 `cli/`:`CLI::Backup` 编排批量备份,`CLI::Topology` 编排发现、规划与显式应用,`CLI::Credentials.resolve` 解析凭据来源,`CLI::Options` 解析参数和配置覆盖。示例显式传入 `load_env: true`,将公开环境变量作为 YAML 和 CLI 之下的默认值;正式 CLI 保留既有的环境覆盖规则。
254
+ 终端入口按职责放在 `cli/`:`CLI::Backup` 编排批量备份,`CLI::Topology` 编排发现、规划与显式应用,`CLI::Credentials.resolve` 解析凭据来源,`CLI::Options` 解析参数和配置覆盖。所有入口与初始化共用同一份配置快照:CLI > 进程环境 > YAML > `.env` > 内置默认值,不再提供单独的环境加载开关。
251
255
  示例默认 `selected`,正式 CLI 保留 `strict` 默认;两者均支持显式 `--success-policy`,退出码与报告的 `policy_success` 一致。
252
256
  示例 `summary.json` 使用标准 schema 2 的 `devices`,逐台结果与诊断统一保存在批次报告中。
253
257
  TFTP 的 `verification` 汇总与设备条目的 `server_file_verified` 区分上传回执和服务器文件核验,核验成功还包含 `local_file`、`server_archive_file`、`bytes`、`sha256`。
@@ -316,4 +320,4 @@ puts "清单 #{batch.summary[:total]},成功 #{batch.summary[:succeeded]},
316
320
 
317
321
  拓扑示例按备份命名规则输出 `scripts/设备名-IP.cfg` 和 `devices/设备名-IP.txt`,计划与摘要记录路径及命令文件 SHA-256。无变更、发现及失败结果不生成空命令文件。导出失败阻止应用,文本导出不能绕过现场证据重验。
318
322
 
319
- 拓扑默认输出中文分类摘要,待核实证据和执行失败分别统计;`--verbose` 显示逐设备事件。JSON 状态码保持稳定并新增 `statistics`,存在待变更或未核实证据时退出码仍为 1。启动显示实际配置文件;`NC_CONFIG` 也优先于拓扑示例默认文件,共享配置须补充对应 `topology` 策略。
323
+ 拓扑默认输出中文分类摘要,待核实证据和执行失败分别统计;`--verbose` 显示逐设备事件。JSON 状态码保持稳定并新增 `statistics`,存在待变更或未核实证据时退出码仍为 1。启动显示实际配置文件;`NC_CONFIG` 也是拓扑配置的最后一层,共享配置须补充对应 `topology` 策略。
data/docs/usage.md CHANGED
@@ -163,4 +163,4 @@ Per-command prompts use `interactions: [Net::Connector::Interaction.new(/Token:\
163
163
 
164
164
  Load device APIs with `require "net/connector"`; Netdisco has a separate entrypoint. Vendor implementations and TextFSM load on demand. The gemspec requires expect-pty 0.9.4+; the host application's lockfile selects concrete dependency versions. The minimum dependency matrix uses TextFSM 0.2.0 and JSON 2, with a separate fixed-source TextFSM patch matrix for JSON 3.
165
165
 
166
- Examples use the development dependency `dotenv` to read the project `.env`; the library and CLI do not. Follow the [loading and migration reference](architecture.md#加载入口与厂商策略) (Chinese) for custom profiles, strategies, and protected hooks.
166
+ CLI entry points share layered configuration and `.env` loading; dotenv is a declared runtime dependency. Core library calls do not discover host files. Follow the [loading and migration reference](architecture.md#加载入口与厂商策略) (Chinese) for custom profiles, strategies, and protected hooks.
data/docs/usage.zh-CN.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  gemspec 声明 expect-pty 的接口版本下界;宿主应用通过自己的 Gemfile 和锁文件固定具体依赖版本。最低依赖组合使用 textfsm 0.2.0 与 JSON 2;JSON 3 另有固定 TextFSM 源码及显式依赖声明补丁的验证通道。开发工具及测试矩阵的版本约束不进入 gem 的运行依赖。
10
10
 
11
- 示例脚本通过开发依赖 `dotenv` 自动读取项目根目录 `.env`(先执行 `bundle install`),已有进程环境变量优先;库和 CLI 本身不会隐式读取 `.env`。
11
+ CLI 与示例入口共用分层配置和 `.env` 加载,dotenv 已声明为运行时依赖;核心库调用不自动发现宿主文件。
12
12
 
13
13
  ## 支持的设备
14
14
 
@@ -136,7 +136,7 @@ end
136
136
 
137
137
  规划、审计与同步会保留语义一致的旧描述:仅大小写或同类接口别名(如 G/GE/Gi、SE/Smartrate-Ethernet)不同不触发写入。邻居名称或端口编号、速率不同仍需修正。未知名称或端口占位符(如 `-`、`unknown`)不能形成变更,审计记录问题,直接规划/同步及下发重验会拒绝未知身份。
138
138
 
139
- 默认描述为 `To_<邻居名称>_<接口简称>`,例如 `To_Core-SW_Gi1/0/1`。同类接口的厂商全称、简称和大小写统一,显示文本中的空格替换为下划线。端口、拆分通道与子接口编号完整保留;原始邻居记录和本机下发接口拼写不改变。`abbreviate: false` 保留对端接口全称,`lowercase: true` 仅将对端接口简称转为小写;后端也可用代码块定制。公共纯函数是 `Net::Connector::Interfaces::InterfaceDescription.format`。
139
+ 默认描述为 `To_<邻居名称>_<接口简称>`,例如 `To_Core-SW_Gi1/0/1`。同类接口的厂商全称、简称和大小写统一,显示文本中的空格替换为下划线。端口、拆分通道与子接口编号完整保留;原始邻居记录和本机下发接口拼写不改变。`abbreviate: false` 保留对端接口全称,`lowercase: true` 仅将对端接口简称转为小写;后端也可用代码块定制。公共纯函数是 `Net::Connector::Interfaces::Description.format`。
140
140
 
141
141
  | 接口类型 | 厂商名称与别名 | 描述简称 |
142
142
  | --- | --- | --- |
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../progress"
4
+
5
+ module Net
6
+ module Connector
7
+ module Collect
8
+ # VXLAN 与无线采集共用的证据结果协议;状态与报告保存分别判断。
9
+ class Progress < Net::Connector::Progress
10
+ # 清单总量在筛选前统计;未选设备不进入执行阶段。
11
+ def plan(devices:, total:, parallel:, label:)
12
+ @skipped = total - devices.size
13
+ super(total: total, selected: devices.size, parallel: parallel, label: label)
14
+ end
15
+
16
+ # 采集设备使用统一的公开身份字段,不传入凭据。
17
+ def start(device)
18
+ super(host: device.fetch("host"))
19
+ end
20
+
21
+ # 完整且已保存的证据才计为成功;正常设备默认不逐台刷屏。
22
+ def result(result)
23
+ status = result_status(result)
24
+ code = diagnostic_code(result)
25
+ message = case status
26
+ when :complete then "采集完成;报告已保存"
27
+ when :partial then "部分成功(#{code})"
28
+ else "未完成(#{code})"
29
+ end
30
+ super(host: result.fetch(:device).fetch("host"), success: status == :complete,
31
+ duration_ms: result.fetch(:duration_ms), message: message, error_code: code)
32
+ end
33
+
34
+ # 使用持久化摘要作最终判定,不把已处理数量或完整性当作健康结论。
35
+ def finish(summary)
36
+ results = summary.fetch(:devices)
37
+ counts = results.group_by { |result| result_status(result) }.transform_values(&:size)
38
+ failures = results.reject { |result| result_status(result) == :complete }
39
+ .group_by { |result| diagnostic_code(result) }.transform_values(&:size)
40
+ super(success: summary.fetch(:status) == :complete, succeeded: counts.fetch(:complete, 0),
41
+ partial: counts.fetch(:partial, 0), failed: counts.fetch(:failed, 0), skipped: @skipped || 0, failures: failures)
42
+ pending = summary.fetch(:pending).size
43
+ notice("未采集 #{pending} 台;详见批次报告") if pending.positive?
44
+ notice("证据完整性不代表网络健康,审查结果见报告。")
45
+ location(File.join(summary.fetch(:directory), "summary.json"))
46
+ location(File.join(summary.fetch(:directory), "summary.txt"), label: "文本报告")
47
+ end
48
+
49
+ private
50
+
51
+ # 写入失败的完整快照仍是未交付结果,不能在终端宣称成功。
52
+ def result_status(result)
53
+ return :failed unless result[:report_saved] == true
54
+
55
+ %i[complete partial].include?(result[:status]) ? result[:status] : :failed
56
+ end
57
+
58
+ # 完整维度的提示不属于失败;优先保留会话错误,再选择未完成维度的诊断码。
59
+ def diagnostic_code(result)
60
+ return :report_not_saved unless result[:report_saved] == true
61
+ return nil if result[:status] == :complete
62
+ return result[:error] if result[:error]
63
+
64
+ dimensions = result.fetch(:dimensions, {}).values.reject { |dimension| dimension[:status] == :complete }
65
+ dimensions.flat_map { |dimension| dimension.fetch(:issues, []) }.first || :collection_incomplete
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end