net-connector 0.4.0 → 0.4.1

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -65
  3. data/README.md +88 -95
  4. data/docs/VERIFICATION.md +2 -1
  5. data/docs/architecture.md +144 -248
  6. data/lib/net/connector/device/base.rb +1 -0
  7. data/lib/net/connector/device/interface_description.rb +3 -0
  8. data/lib/net/connector/device/profile/builder.rb +236 -0
  9. data/lib/net/connector/device/profile.rb +2 -227
  10. data/lib/net/connector/device/running_config/strategy.rb +5 -0
  11. data/lib/net/connector/device/running_config.rb +5 -0
  12. data/lib/net/connector/engine/dialogue.rb +1 -0
  13. data/lib/net/connector/engine/errors.rb +2 -0
  14. data/lib/net/connector/engine/log.rb +13 -9
  15. data/lib/net/connector/engine/session.rb +1 -0
  16. data/lib/net/connector/netdisco/cli.rb +20 -20
  17. data/lib/net/connector/netdisco/device.rb +4 -8
  18. data/lib/net/connector/netdisco/fleet.rb +2 -48
  19. data/lib/net/connector/netdisco/plan.rb +77 -0
  20. data/lib/net/connector/netdisco/planner.rb +12 -23
  21. data/lib/net/connector/netdisco/worker.rb +31 -24
  22. data/lib/net/connector/operations/tftp/file_upload.rb +42 -0
  23. data/lib/net/connector/operations/tftp/strategy.rb +9 -11
  24. data/lib/net/connector/operations/topology/strategy.rb +14 -0
  25. data/lib/net/connector/operations/topology.rb +1 -0
  26. data/lib/net/connector/vendor/cisco_ios/running_config.rb +1 -0
  27. data/lib/net/connector/vendor/cisco_ios/topology.rb +9 -0
  28. data/lib/net/connector/vendor/cisco_nxos/running_config.rb +1 -0
  29. data/lib/net/connector/vendor/h3c/tftp_backup.rb +2 -18
  30. data/lib/net/connector/vendor/h3c/topology.rb +9 -0
  31. data/lib/net/connector/vendor/hillstone/running_config.rb +1 -0
  32. data/lib/net/connector/vendor/hillstone/tftp_backup.rb +4 -0
  33. data/lib/net/connector/vendor/hillstone/topology.rb +9 -0
  34. data/lib/net/connector/vendor/huawei/tftp_backup.rb +2 -18
  35. data/lib/net/connector/vendor/palo_alto/running_config.rb +4 -0
  36. data/lib/net/connector/vendor/palo_alto/tftp_backup.rb +7 -5
  37. data/lib/net/connector/vendor/palo_alto/topology.rb +10 -0
  38. data/lib/net/connector/vendor/radware/tftp_backup.rb +2 -2
  39. data/lib/net/connector/vendor/radware/topology.rb +2 -0
  40. data/lib/net/connector/version.rb +1 -1
  41. metadata +26 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0a3f3d7d955f359e45f5ce37c02b2043f809e2a55a2017fb159b24252e3ca02
4
- data.tar.gz: b8b0bdea1f9ba9129cc2846cbed0beea71086133ab220be758da7797863a6b8d
3
+ metadata.gz: 1ece440472f988bc86cc30ddb72d954925b57da221ad7ccbf6130728d343abc1
4
+ data.tar.gz: e5ea0f6feab21317d18ca21dfe1427c6b24142fcbf7438778ae3c09ea1734399
5
5
  SHA512:
6
- metadata.gz: 317766ae3e494407065da741a6006a32e9236769fdf377986d72d1ea554ab97d022a63b53e946c7be4f04f582f1f63f5241854a712e70da34833c21513981471
7
- data.tar.gz: ec899fbc55fab9195431769f464b5f45b2ad9fa78d14f8e98fcb2e2fe1a46fb4d85a700ab80c4babaad32ec82ce795c53844f79414de669d076890bf81417275
6
+ metadata.gz: ca6a774c01e474fb5f1140a1e1cdd848ae0a57dd518fab50408fd68c392cd8bea64567ec332229dc210aa98d2bba58bfb7532b6edca1c5bb43e722e604422119
7
+ data.tar.gz: f69e9dc32469d1f9ba6b1c8107be385dbe9704dc8b607a4c7631cf11fe98a34b1d0e682d19d436f0937e6f67d97650c934a0f123cf716f03440b0ca204b0c531
data/CHANGELOG.md CHANGED
@@ -1,82 +1,90 @@
1
- # Changelog
1
+ # 更新记录
2
2
 
3
- ## Unreleased
3
+ ## 0.4.1 - 2026-09-27
4
+
5
+ - 用 Ruby 标准库 `Logger` 替代 ActiveSupport 日志依赖,保留设备标记、级别、脱敏和注入日志器的所有权。
6
+ - 批量任务按线程完成顺序传播中断;创建后续线程失败时也停止已有任务,避免批次退出后仍有设备会话运行。
7
+ - TFTP 批次按实际文件名检查所有厂商的覆盖冲突,拒绝外部计划绕过检查,并正确区分采样限制与文件名冲突。
8
+ - 直接备份和 Netdisco 共用厂商命名规则,H3C 与华为共用已保存文件的上传流程,厂商扩展无需重复维护相同逻辑。
9
+ - 分离 `Profile` 声明构造器与不可变档案,补充公开扩展契约、会话租约及 Rails 线程接入说明。
10
+ - 测试和 CI 输出已加载库文件的行与分支覆盖率,未加载文件单独计数,暂不设硬性门槛。
11
+ - 增加中文 PR 和 Issue 模板,补齐方法注释,并统一公开文档及 CLI 帮助文案。
4
12
 
5
13
  ## 0.4.0 - 2026-09-26
6
14
 
7
- - Redact dynamic login challenge failures before logging or capturing their underlying exceptions, including partial credentials and backtraces.
8
- - Keep PAN-OS LLDP discovery usable when some local interfaces have no neighbors, while rejecting incomplete nonempty records.
9
- - Stop remaining batch workers after an interruption, close incomplete command sessions, and use unique directories for concurrent example backups.
10
- - Detect additional network device credential syntax before publishing, including privileged IOS usernames and hashed local passwords.
11
- - Add shared local/CI checks on Ruby 3.2–4.0 for Linux and macOS, pinned workflow tooling, isolated gem installation, and a real local PTY smoke test.
12
- - Require `expect-pty` 0.3.1 or later in the 0.3 series, declare directly used standard-library gems, and keep development dependencies compatible with Ruby 3.2.
13
- - Add artifact-preserving local and manual CI release scripts with version, changelog, Git, metadata, file-byte and remote-checksum verification.
14
- - Scan source, available Git history and the built gem for credentials and private network addresses; redact reports, replace example addresses and credential placeholders, and ignore local configurations, backups, logs and credentials.
15
- - Make running configuration a device capability under `device/`; move the facade and profile there, and bind one collection strategy per call while preserving inherited method hooks and configuration bytes.
16
- - Co-locate vendor collection, TFTP and topology rules under `vendor/<name>/`; retain old require paths and constants as forwarding aliases, and load vendors and TextFSM only when needed.
17
- - Share interface name matching, description formatting and interface-view command construction. Description plans now abbreviate known neighbor ports by default while preserving case; `abbreviate: false` retains previous formatting and `lowercase: true` is opt-in. Raw evidence, local command names, confirmation and readback remain unchanged.
18
- - Bind collection to the complete known session prompt so configuration descriptions cannot terminate a response early; incomplete responses preserve existing backups.
19
- - Require explicit TFTP completion messages, excluding command/reply/prompt echoes and retaining failure evidence across terminal edits.
20
- - Keep temporary secrets through command preparation, postprocessing, callbacks and exception normalization; redact secrets containing `[REDACTED]` in direct and streaming output.
21
- - Hold one session operation lease across topology revalidation, changes and readback while rejecting nested callbacks and concurrent callers.
22
- - Use normalized management IPs for local backup filenames, preserving rename baselines and unique legacy-file compatibility without deleting old files.
15
+ - 在记录日志和底层异常前,对动态登录挑战失败脱敏,包括部分凭据与回溯。
16
+ - PAN-OS LLDP 输出包含无邻居的本机接口时仍可发现其他邻居,缺字段的非空记录仍会被拒绝。
17
+ - 中断批次时停止其他工作线程,关闭未完成的命令会话,并为并发示例备份建立唯一目录。
18
+ - 发布前识别更多设备凭据语法,包括 IOS 特权用户和哈希形式的本地密码。
19
+ - 增加 Linux、macOS 上 Ruby 3.2 至 4.0 共用的本地与 CI 检查,固定工作流工具版本,隔离安装 gem,并用真实本地 PTY 烟测。
20
+ - 要求 `expect-pty` 0.3 系列至少为 0.3.1,声明直接使用的标准库 gem,并保持开发依赖兼容 Ruby 3.2。
21
+ - 增加保留构建产物的本地及手动 CI 发布脚本,校验版本、更新记录、Git、元数据、文件字节与远端校验和。
22
+ - 扫描源码、可用 Git 历史和 gem 中的凭据及私有地址;报告脱敏,公开示例改用占位值,本地配置、备份和日志加入忽略规则。
23
+ - 将运行配置纳入 `device/` 的设备能力,每次采集绑定一个策略,保留旧钩子及配置字节。
24
+ - 将厂商采集、TFTP、拓扑规则放到 `vendor/<厂商>/`;旧 require 路径和常量转发到同一实现,厂商与 TextFSM 按需加载。
25
+ - 共用接口名称匹配、描述格式和接口视图命令。邻居接口默认缩写但保留大小写;`abbreviate: false` 关闭缩写,`lowercase: true` 显式转小写。原始证据、本机命令名、确认与回读规则不变。
26
+ - 采集时匹配完整的已知会话提示符,避免配置描述提前结束响应;不完整响应保留旧备份。
27
+ - TFTP 必须有明确完成回显;命令、应答、提示符回显不能作为证据,终端控制符覆盖的失败仍会保留。
28
+ - 临时秘密贯穿命令准备、后处理、回调和异常归一化;直接和流式输出都能脱敏含 `[REDACTED]` 字面量的秘密。
29
+ - 拓扑重验、变更和回读共用一个会话租约,同时拒绝嵌套回调与并发调用。
30
+ - 本地备份按规范化管理地址命名,设备改名不改变比较基线,并兼容唯一的旧文件而不删除它。
23
31
 
24
- - Reject zero-width prompt matches so an old prompt cannot complete a later command, and map declared terminal width/height to PTY rows/columns correctly.
25
- - Reject prompt-only and command-echo-only configuration responses while preserving completed steps and existing backup files.
26
- - Reject unknown H3C/Hillstone neighbor rows instead of treating partial output as an empty or complete table; ignore H3C discovery command echoes in TextFSM parsing.
27
- - Recheck complete neighbor identity, including chassis ID, before applying description plans and reject truncated PAN-OS multiline comment evidence.
28
- - Recognize colorized command and TFTP failures while retaining failures overwritten by terminal controls.
29
- - Share safe TFTP filename generation and length limits between direct operations and inventory batches, including scoped IPv6 addresses.
30
- - Close log files when initialization fails and clear owned log state after close failures.
31
- - Simplify the private Profile block guard to `check_block!` and document Expect semantics and resource ownership.
32
+ - 拒绝零宽度提示符匹配,避免旧提示符结束后续命令;终端宽高正确映射为 PTY 行列。
33
+ - 拒绝只有提示符或命令回显的配置响应,保留已完成步骤和旧备份。
34
+ - H3C、山石邻居输出含未知行时拒绝把部分解析当成空表或完整表;H3C TextFSM 解析忽略发现命令回显。
35
+ - 下发描述前重验含机箱 ID 的完整邻居身份,并拒绝截断的 PAN-OS 多行备注证据。
36
+ - 识别带颜色的命令与 TFTP 失败,同时保留被终端控制符覆盖的故障信息。
37
+ - 直接操作与清单批次共用安全的 TFTP 文件名生成和长度限制,覆盖带作用域的 IPv6 地址。
38
+ - 日志初始化失败时关闭文件,关闭失败后也清除会话持有状态。
39
+ - 将 `Profile` 声明块校验简化为 `check_block!`,记录 Expect 语义与资源所有权。
32
40
 
33
- - Bind vendor TFTP and topology strategies through the existing Profile DSL, and expose read-only `supports?` capability queries.
34
- - Keep configuration collection in one locked execution path, including PAN-OS step selection; reject missing, blank, or invalid cleaned configuration as incomplete.
35
- - Move vendor-specific topology commands, parsing evidence, interface spelling, and commit rules into strategies while retaining plan revalidation and readback.
41
+ - 通过现有 `Profile` DSL 绑定厂商 TFTP 与拓扑策略,并提供只读的 `supports?` 能力查询。
42
+ - 配置采集统一在一条持锁执行路径中完成,包括 PAN-OS 步骤选择;清理后配置缺失、空白或无效时判定为不完整。
43
+ - 将厂商拓扑命令、解析证据、接口拼写和提交规则移入策略,同时保留计划重验及回读。
36
44
 
37
- - Redact configured credentials across log chunks and terminal rendering, reject raw application loggers consistently, and release stale session state before reconnecting.
38
- - Commit PAN-OS interface descriptions before leaving configuration mode and parse NX-OS indented descriptions correctly.
39
- - Keep TFTP preview filenames consistent with execution and avoid treating diagnostic words inside filenames as transfer failures.
40
- - Suppress sensitive underlying Netdisco exceptions and preserve empty or invalid-inventory outcomes in backup examples.
45
+ - 跨日志分片和终端渲染脱敏已配置凭据,一致拒绝原始格式与应用日志器混用,重连前释放旧会话状态。
46
+ - PAN-OS 在退出配置视图前提交接口描述,正确解析 NX-OS 缩进的描述。
47
+ - 保持 TFTP 预览与执行文件名一致,避免把文件名中的诊断词误判为传输失败。
48
+ - 隐藏 Netdisco 底层敏感异常,批量示例保留空清单和无效清单结果。
41
49
 
42
- - Reject inconsistent inventory plans before device I/O, preserve immutable device snapshots, and require a backup artifact before reporting success.
43
- - Mark empty backup batches as `no_devices` and reject `--host` addresses absent from the inventory.
44
- - Reuse the private atomic file writer for batch JSON reports.
45
- - Use Active Support tagged logging for session events, with debug device output in the same log file.
46
- - Separate vendor CLI profiles from configuration collection, local backup, and TFTP export operation objects.
47
- - Move each vendor's TFTP command, prompt, source-file, and completion rules into a dedicated transfer strategy.
48
- - Add a backup CLI with safe non-secret YAML settings, effective-config display, inventory preview, targeted runs, and TFTP batch execution.
49
- - Export an existing local device configuration to stdout or a private file without reconnecting to Netdisco or the device.
50
- - Compare local configuration backups by SHA-256, preserve unchanged files, and expose created/changed/unchanged states.
51
- - Add per-device start and result callbacks plus change-only notification callbacks, with isolated callback failures and task timing.
52
- - Share batch worker and device lifecycle handling between local and TFTP backups.
53
- - Use `vrf:` for NX-OS and Hillstone device exports, and per-vendor `vrfs:` for fleet exports.
54
- - Move Netdisco batch planning and worker dispatch out of the examples; keep device failures independent.
55
- - Add batch execution summaries with private JSON reports by default and an injected database repository option.
56
- - Add Hillstone StoneOS running-configuration collection and native startup-configuration TFTP export.
57
- - Complete Radware Alteon TFTP prompts for `.tgz` filename, private-key choice, and `mansync`.
58
- - Distinguish explicit device-side TFTP failures from transfers without a success confirmation.
59
- - Add configurable session log levels, readable login and command events, full debug device output, and TFTP outcome events.
60
- - Render session events as human-readable Chinese actions with local timestamps, while retaining full device output at debug level.
61
- - Add concise per-device TFTP results to the end of each session log.
62
- - Match the observed PAN-OS TFTP export command order and require a positive `Sent ... bytes` completion line.
63
- - Allow full-inventory TFTP batches with 50 workers while preserving per-device outcomes and PAN-OS filename-collision protection.
64
- - Discover H3C startup paths from each device, recognize completed TFTP progress, and support host-specific Netdisco connector overrides.
65
- - Add a session-log review for full TFTP batches without rewriting original outcomes.
50
+ - 设备 I/O 前拒绝不一致的清单计划,保留不可变设备快照,报告成功前要求真实备份产物。
51
+ - 空批次标为 `no_devices`,拒绝清单中不存在的 `--host` 地址。
52
+ - 批次 JSON 报告复用私有文件的原子写入器。
53
+ - 会话事件使用 Active Support 标签日志,调试级设备回显写入同一文件。
54
+ - 将厂商 CLI 档案与配置采集、本地备份及 TFTP 导出业务对象分离。
55
+ - 将各厂商 TFTP 命令、提示、源文件和完成规则放入独立传输策略。
56
+ - 增加备份 CLI,支持无凭据 YAML 设置、有效配置展示、清单预览、定向运行和 TFTP 批次。
57
+ - 不重新连接 Netdisco 或设备,即可将已有本地配置导出到标准输出或私有文件。
58
+ - 用 SHA-256 比较本地配置备份,未变化文件保持原样,并报告新建、变化和未变化状态。
59
+ - 增加逐设备开始和结果回调、仅变化时的通知回调,隔离回调故障并记录耗时。
60
+ - 本地和 TFTP 备份共用批量工作线程与设备生命周期处理。
61
+ - NX-OS 与山石的单设备导出使用 `vrf:`,设备集合导出按厂商使用 `vrfs:`。
62
+ - 将 Netdisco 批次规划与工作分派移出示例,让设备故障相互独立。
63
+ - 增加批次摘要、默认私有 JSON 报告,以及可注入的数据库仓储。
64
+ - 增加山石 StoneOS 运行配置采集和原生启动配置 TFTP 导出。
65
+ - 补全 Radware Alteon 的 `.tgz` 文件名、私钥选择及 `mansync` TFTP 提示。
66
+ - 区分设备明确报告传输失败与没有成功确认的传输。
67
+ - 增加可配置会话日志级别、易读的登录和命令事件、调试级完整回显及 TFTP 结果事件。
68
+ - 将会话事件写为带本地时间的中文动作,调试级仍保留完整设备回显。
69
+ - 在每台设备日志末尾补充简洁的 TFTP 结果。
70
+ - 按现场观察的顺序执行 PAN-OS TFTP 导出,并要求明确的 `Sent ... bytes` 完成行。
71
+ - 全量 TFTP 批次允许 50 个工作线程,保留逐设备结果并避免 PAN-OS 固定文件名冲突。
72
+ - 从 H3C 设备发现启动配置路径,识别已完成的 TFTP 进度,并支持按主机覆盖 Netdisco 连接器。
73
+ - 增加全量 TFTP 会话日志复核,不改写原始结果。
66
74
 
67
75
  ## 0.3.0
68
76
 
69
- - Add device-initiated native TFTP backup with vendor-specific commands and transfer checks.
70
- - Save Netdisco backups as sanitized `<device name>-<IP>.txt` files for easier lookup.
71
- - Render terminal carriage returns in H3C and Huawei configuration backups.
77
+ - 增加设备发起的原生 TFTP 备份、厂商命令和传输结果检查。
78
+ - Netdisco 备份使用已清理的 `<设备名>-<IP>.txt` 文件名,便于查找。
79
+ - H3C 和华为配置备份正确渲染终端回车。
72
80
 
73
81
  ## 0.2.0
74
82
 
75
- - Move shared connector implementation into `engine/` and remove the obsolete top-level core files.
76
- - Add a standalone Netdisco client with validated inventory pagination and legacy query support.
77
- - Map discovered devices to connector profiles with configurable selection and mapping rules.
78
- - Run bounded concurrent backups with environment-backed credentials, paths, and per-device outcomes.
83
+ - 公共连接器实现移入 `engine/`,移除过时的顶层核心文件。
84
+ - 增加独立 Netdisco 客户端,校验清单分页并兼容旧查询方式。
85
+ - 将发现的设备映射到连接器档案,支持配置选择和映射规则。
86
+ - 用有上限的并发执行备份,凭据、路径从环境变量读取,保留逐设备结果。
79
87
 
80
88
  ## 0.1.0
81
89
 
82
- - Initial standalone connector with SSH and Telnet sessions, script execution, configuration collection, logging, and seven vendor profiles.
90
+ - 首个独立版本,支持 SSH、Telnet 会话、脚本执行、配置采集、日志和七个厂商档案。
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # net-connector
2
2
 
3
- `net-connector` runs network device CLI sessions over SSH or Telnet. It collects running configuration, writes private backup files, executes command scripts, answers device prompts, records redacted session logs, and returns structured errors and partial results.
3
+ `net-connector` 通过 SSH 或 Telnet 操作网络设备的命令行。它可以采集运行配置、保存私有备份、执行命令脚本、回答设备提示、记录脱敏会话日志,并在失败时返回结构化错误和已完成的步骤。
4
4
 
5
- The code is organized by responsibility: `lib/net/connector/engine/` owns sessions, transport, scripts, results, and logging; `device/` owns the device facade, profile, running configuration and interface text helpers; `vendor/<name>.rb` assembles that vendor's rules from `vendor/<name>/running_config.rb`, `tftp_backup.rb` and `topology.rb` where needed; `operations/` owns shared backup, parsing and topology workflows; `netdisco/` owns inventory and batch orchestration. Identical vendor rules are shared. See [docs/architecture.md](docs/architecture.md) for the model. Load the public API with `require "net/connector"` or the Netdisco integration with `require "net/connector/netdisco"`. The public API loads vendor rules and parsing on demand; `require "net/connector/engine/core"` loads only the session execution layer.
5
+ 代码按职责组织:`engine/` 管理会话、传输、脚本、结果和日志;`device/` 提供设备入口、档案、运行配置和接口名称处理;`vendor/<厂商>/` 保存各厂商的采集、TFTP 和拓扑规则;`operations/` 实现公共业务流程;`netdisco/` 负责清单和批量编排。相同规则直接复用,设计说明见[架构文档](docs/architecture.md)。用 `require "net/connector"` 加载设备 API,用 `require "net/connector/netdisco"` 加载 Netdisco 集成;厂商规则和 TextFSM 解析器按需加载。
6
6
 
7
- Ruby 3.2+ and a POSIX system are required. SSH uses the local OpenSSH client. Telnet requires the local `telnet` program and must be selected explicitly. The gem depends on [`expect-pty`](https://rubygems.org/gems/expect-pty) 0.3.x (at least 0.3.1) and [`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.3.x(至少 0.3.1)和 [`textfsm`](https://rubygems.org/gems/textfsm) 0.2.x。
8
8
 
9
- ## Install
9
+ ## 安装
10
10
 
11
11
  ```sh
12
12
  gem install net-connector
@@ -16,28 +16,24 @@ gem install net-connector
16
16
  require "net/connector"
17
17
  ```
18
18
 
19
- ## Supported devices
19
+ ## 支持的设备
20
20
 
21
- Use `device.supports?(:tftp_backup)` or another capability to check whether a
22
- connector implements an operation without contacting the device. The complete
23
- [capability matrix and extension example](docs/architecture.md#vendor-capabilities)
24
- cover collection, saving, TFTP, neighbor discovery, and description changes.
25
- This check does not test device permissions or firmware behavior.
21
+ `device.supports?(:tftp_backup)` 等能力查询不会连接设备,只表示该连接器实现了对应操作,不代表设备权限或固件一定支持。完整能力矩阵和扩展示例见[厂商能力](docs/architecture.md#厂商能力)。
26
22
 
27
- | Vendor key | Device family | Running configuration | Save configuration |
23
+ | 厂商标识 | 设备系列 | 运行配置命令 | 保存配置命令 |
28
24
  | --- | --- | --- | --- |
29
25
  | `:h3c` | H3C Comware | `dis cur` | `save force` |
30
- | `:h3c_wireless` | H3C wireless controller, Comware CLI | `dis cur` | `save force` |
26
+ | `:h3c_wireless` | H3C 无线控制器 | `dis cur` | `save force` |
31
27
  | `:cisco_ios` | Cisco IOS / IOS XE | `show running-config` | `copy running-config startup-config` |
32
28
  | `:cisco_nxos` | Cisco NX-OS | `show running-config` | `copy run start` |
33
- | `:radware` | Radware Alteon CLI | `/cfg/dump` | `/cfg/save` |
34
- | `:palo_alto` | Palo Alto PAN-OS CLI | set format candidate export | unsupported |
35
- | `:huawei` | Huawei CLI | `dis cur` | `save force` |
36
- | `:hillstone` | Hillstone StoneOS | `show configuration running` | `save all` |
29
+ | `:radware` | Radware Alteon | `/cfg/dump` | `/cfg/save` |
30
+ | `:palo_alto` | Palo Alto PAN-OS | 候选视图中的 set 格式导出 | 不支持 |
31
+ | `:huawei` | 华为 | `dis cur` | `save force` |
32
+ | `:hillstone` | 山石 StoneOS | `show configuration running` | `save all` |
37
33
 
38
- Aliases `:cisco_n9k` and `:paloalto` are accepted. Vendor classes live directly under `Net::Connector`, such as `Net::Connector::H3cWireless::Connector`.
34
+ 兼容标识 `:cisco_n9k` 和 `:paloalto`。厂商连接器位于 `Net::Connector` 下,例如 `Net::Connector::H3cWireless::Connector`。
39
35
 
40
- ## Login and backup
36
+ ## 登录与本地备份
41
37
 
42
38
  ```ruby
43
39
  Net::Connector.open(:cisco_ios,
@@ -45,33 +41,35 @@ Net::Connector.open(:cisco_ios,
45
41
  known_hosts: "/etc/net-connector/known_hosts", host_key_policy: :strict,
46
42
  log_file: "/var/log/net-connector/router.log") do |device|
47
43
  backup = device.backup(path: "/var/backups/router-running.cfg")
48
- puts "#{backup.bytes} bytes, SHA-256 #{backup.sha256}"
44
+ puts "#{backup.bytes} 字节,SHA-256 #{backup.sha256}"
49
45
  end
50
46
  ```
51
47
 
52
- `open` closes the session even when the block fails. `backup` collects first, then atomically replaces the requested file with mode `0600`. Collection failure leaves an existing file unchanged. It returns `Backup(path:, bytes:, sha256:, collected_at:)`. The caller must create the destination directory and protect backups because running configurations can contain device secrets.
48
+ `open` 会在代码块结束或出错时关闭会话。`backup` 先采集配置,再以 `0600` 权限原子替换目标文件;采集失败不会覆盖旧备份。返回值为 `Backup(path:, bytes:, sha256:, collected_at:)`。调用方应创建并保护备份目录,运行配置可能含有设备凭据。
53
49
 
54
- ## Native TFTP backup
50
+ ## 设备发起的 TFTP 备份
55
51
 
56
- `tftp_backup` asks the device to send its native configuration directly to a TFTP server. Each vendor supplies its own command and prompt handling. For example, Huawei and H3C use a device file as the source:
52
+ `tftp_backup` 让设备把原生配置直接上传到 TFTP 服务器,命令和提示交互由厂商策略提供。H3C 和华为从设备文件读取源配置,例如:
57
53
 
58
54
  ```ruby
59
55
  Net::Connector.open(:huawei, host: "192.0.2.20", username: ENV.fetch("DEVICE_USERNAME"),
60
56
  password: ENV.fetch("DEVICE_PASSWORD")) do |device|
61
57
  transfer = device.tftp_backup(host: "192.0.2.30", source_file: "flash:/startup.cfg")
62
- puts "Uploaded #{transfer.path} to #{transfer.server}"
58
+ puts "已上传 #{transfer.path} 到 #{transfer.server}"
63
59
  end
64
60
  ```
65
61
 
66
- This emits `tftp 192.0.2.30 put flash:/startup.cfg`; pass `path: "site/switch.cfg"` to choose a different remote filename. H3C discovers its saved startup file with `display startup` unless `source_file:` is given. Huawei requires `source_file:` because its saved file location varies by model. Cisco IOS exports `running-config` using its interactive `copy running-config tftp:` flow. Cisco Nexus 9000 exports `running-config` with `vrf management` by default; pass `vrf: "other-vrf"` to override it. Hillstone exports the saved startup configuration with `export configuration startup to tftp server <server> <filename>`; its separate running-configuration query uses `show configuration running`. Radware Alteon uses `/cfg/ptcfg <server> -tftp`, produces a `.tgz` file, declines private-key export, and answers `mansync` for the internal-index prompt. Palo Alto exports `running-config.xml` and requires its fixed remote filename. All other vendors default the remote filename to `<management IP>.cfg`. The method returns `TftpBackup(server:, path:, completed_at:)` only when the device output confirms a transfer. Explicit device-side transfer failures use `:transfer_failed`; a missing success confirmation uses `:transfer_unconfirmed`. It does not read back the file from the TFTP server. TFTP carries configuration data without encryption; use it only on an appropriate management network.
62
+ 示例会下发 `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`。
67
63
 
68
- The runnable [examples/tftp_backup.rb](examples/tftp_backup.rb) reads `DEVICE_VENDOR`, `DEVICE_HOST`, `DEVICE_USERNAME`, `DEVICE_PASSWORD`, and `TFTP_HOST` from the environment. Set `TFTP_SOURCE_FILE` for H3C or Huawei, `TFTP_PATH` for a remote filename, or `TFTP_VRF` to override the NX-OS VRF or Hillstone vrouter.
64
+ 只有设备回显确认传输完成,方法才返回 `TftpBackup(server:, path:, completed_at:)`。明确失败对应 `:transfer_failed`,缺少成功证据对应 `:transfer_unconfirmed`。本方法不读取服务器上的文件。TFTP 不加密配置数据,应限制在合适的管理网络中使用。
69
65
 
70
- For in-memory collection, call `device.running_config`. It returns a `Result`; `result.value!` yields the cleaned configuration or raises its typed error.
66
+ 可运行[单设备示例](examples/tftp_backup.rb)。它从环境变量读取 `DEVICE_VENDOR`、`DEVICE_HOST`、`DEVICE_USERNAME`、`DEVICE_PASSWORD` 和 `TFTP_HOST`;`TFTP_SOURCE_FILE`、`TFTP_PATH`、`TFTP_VRF` 分别指定源文件、目标文件和设备 VRF。
71
67
 
72
- ## TextFSM parsing
68
+ 只需在内存中采集配置时,调用 `device.running_config`;它返回 `Result`,`result.value!` 返回清理后的文本,失败时抛出对应错误。
73
69
 
74
- `parse_command` executes one CLI command, then selects a TextFSM template by vendor and command. The bundled index covers Cisco IOS `show ip interface brief` and the CDP/LLDP commands used by topology discovery:
70
+ ## TextFSM 解析
71
+
72
+ `parse_command` 执行一条命令,再按厂商和命令选择 TextFSM 模板。内置索引覆盖 Cisco IOS 的 `show ip interface brief`,以及拓扑发现使用的 CDP/LLDP 命令:
75
73
 
76
74
  ```ruby
77
75
  Net::Connector.open(:cisco_ios, host: "192.0.2.10", username: "admin",
@@ -81,22 +79,22 @@ Net::Connector.open(:cisco_ios, host: "192.0.2.10", username: "admin",
81
79
  end
82
80
  ```
83
81
 
84
- `parse_config` collects the cleaned running configuration and requires an explicit template. The bundled Cisco IOS template extracts interface names and descriptions; it is not a complete configuration model:
82
+ `parse_config` 采集运行配置,要求显式指定模板。内置 Cisco IOS 模板只提取接口名称和描述,并非完整配置模型:
85
83
 
86
84
  ```ruby
87
85
  interfaces = device.parse_config(template: "cisco_ios_running_config_interfaces.textfsm")
88
86
  ```
89
87
 
90
- Both methods return an Array of Hash records using the template's field names. A valid template with no matching records returns `[]`. A missing or invalid template raises `Net::Connector::ParsingError`; a failed device command retains its original connector error. To use other vendors or commands, provide `template: "/path/to/template.textfsm"`, or `template_dir: "/path/to/templates"` with a TextFSM `index` containing `Template, Vendor, Command` columns. Each parse creates a fresh parser, so concurrent device tasks do not share parsing state. Existing local backups can be parsed without connecting to a device:
88
+ 两种方法都返回由模板字段名组成的哈希数组;匹配不到记录时返回 `[]`。模板缺失或无效会抛出 `ParsingError`,设备命令失败则保留原始连接器错误。可用 `template:` 指定外部模板,或用 `template_dir:` 指定含 `index` 的模板目录。每次解析使用独立解析器,批量任务之间不共享状态。已有本地备份也可离线解析:
91
89
 
92
90
  ```ruby
93
91
  saved = Net::Connector::Operations::SavedConfig.new(directory: "/var/backups")
94
92
  rows = saved.parse(host: "192.0.2.10", template: "/path/to/template.textfsm")
95
93
  ```
96
94
 
97
- ## Neighbors and interface descriptions
95
+ ## 邻居发现与接口描述
98
96
 
99
- `neighbors` queries CDP on Cisco IOS/NX-OS and LLDP on H3C, H3C wireless, Hillstone, and PAN-OS. It returns records with `local_interface`, `neighbor_name`, `neighbor_interface`, `chassis_id`, and `protocol`. H3C LLDP list output is selected by its column header, covering releases that put the system name first or last. Unknown output raises `ParsingError`; it is not treated as an empty neighbor list. `interface_descriptions` reads the current running configuration, including Alteon port names in a Radware configuration dump.
97
+ `neighbors` 在 Cisco IOS/NX-OS 上查询 CDP,在 H3C、H3C 无线、山石和 PAN-OS 上查询 LLDP。记录包含 `local_interface`、`neighbor_name`、`neighbor_interface`、`chassis_id` 和 `protocol`。H3C 会按表头选择不同列顺序的模板;未知输出会抛出 `ParsingError`,不会误判为空表。`interface_descriptions` 从运行配置读取当前描述,Radware 则从配置转储读取端口名称。
100
98
 
101
99
  ```ruby
102
100
  Net::Connector.open(:h3c, host: "192.0.2.10", username: ENV.fetch("DEVICE_USERNAME"),
@@ -104,21 +102,23 @@ Net::Connector.open(:h3c, host: "192.0.2.10", username: ENV.fetch("DEVICE_USERNA
104
102
  plan = device.plan_interface_descriptions
105
103
  plan.changes.each { |change| puts "#{change.interface}: #{change.old_description.inspect} -> #{change.new_description.inspect}" }
106
104
  puts plan.commands.join("\n")
107
- # Review the exact commands and obtain operator approval before applying.
105
+ # 核对将要下发的命令,并完成操作审批。
108
106
  result = device.apply_interface_descriptions(plan, confirmed: true)
109
107
  result.value!
110
108
  end
111
109
  ```
112
110
 
113
- The default proposal is `To <neighbor name> <abbreviated neighbor port>`. Abbreviation is enabled and preserves case: `ethernet1/1` becomes `eth1/1`, `Ethernet1/1` becomes `Eth1/1`, and `GigabitEthernet1/0/1` becomes `Gi1/0/1`. Unknown interface forms remain unchanged. Raw neighbors and plan evidence retain their original values; local command interface names are not abbreviated. Use `plan_interface_descriptions(abbreviate: false)` for the previous default, or `lowercase: true` to lowercase the remote port. Pass a block to control the complete description using the original neighbor. The pure common formatter is also available as `Net::Connector::InterfaceDescription.format(neighbor, abbreviate: true, lowercase: false)`. Planning rejects ambiguous neighbors, missing peer identities, unsafe text, and unrecognized output. Applying requires `confirmed: true` and reads neighbors and old descriptions again; changed evidence raises `:stale_plan`. The plan includes the vendor's save command; PAN-OS uses `commit`. After the script, the operation reads the configuration back and reports `:description_unconfirmed` if the new text is absent. A failed script returns a `Result` with completed steps and its error. Radware Alteon can advertise LLDP on documented versions, but its documented CLI does not provide neighbor discovery; `neighbors` therefore raises `:neighbor_discovery_unsupported`. Its port names remain readable through `interface_descriptions`. Validate commands and output against the specific firmware before using an approved plan on a live device.
111
+ 默认描述为 `To <邻居名称> <邻居接口简称>`。接口缩写默认开启并保留大小写:`ethernet1/1` 变为 `eth1/1`,`Ethernet1/1` 变为 `Eth1/1`,`GigabitEthernet1/0/1` 变为 `Gi1/0/1`;未知形式保持原样。原始邻居记录、计划证据和本机下发接口名不会被缩写。`abbreviate: false` 关闭缩写,`lowercase: true` 才会转为小写;也可传入代码块自行生成完整描述。公共纯函数是 `Net::Connector::InterfaceDescription.format`。
112
+
113
+ 规划会拒绝歧义邻居、缺少对端身份、不安全文本和无法识别的输出。下发必须传 `confirmed: true`,操作会再次读取邻居和旧描述;证据变化返回 `:stale_plan`。计划包含厂商保存命令,PAN-OS 使用 `commit`。执行后还会回读配置,未确认新描述时返回 `:description_unconfirmed`。脚本失败的 `Result` 保留已完成步骤。Radware 支持读取端口名称,但当前不提供邻居发现或自动改写;其 `neighbors` 返回 `:neighbor_discovery_unsupported`。现场下发前应按设备固件核对命令与回显。
114
114
 
115
- PAN-OS collection checks for pending candidate changes before and after export, and rejects XML or non-set output. This avoids reporting an ambiguous candidate configuration as a running configuration backup. Device-specific CLI and firmware differences still need validation against the target device.
115
+ PAN-OS 在导出前后检查候选配置差异,并拒绝 XML 或非 set 格式输出,避免将未提交配置误作运行配置。
116
116
 
117
- ## Scripts and automatic interaction
117
+ ## 命令脚本与自动交互
118
118
 
119
119
  ```ruby
120
120
  script = Net::Connector::Script.parse(<<~CLI, name: "change-123")
121
- # Comments on their own line are ignored.
121
+ # 独立成行的注释会被忽略。
122
122
  configure terminal
123
123
  interface GigabitEthernet1/0/1
124
124
  description uplink
@@ -126,34 +126,34 @@ script = Net::Connector::Script.parse(<<~CLI, name: "change-123")
126
126
  CLI
127
127
 
128
128
  result = device.execute_script(script) do |step|
129
- puts "#{step.command.text}: #{step.duration.round(2)}s"
129
+ puts "#{step.command.text}: #{step.duration.round(2)} 秒"
130
130
  end
131
131
 
132
132
  if result.failure?
133
- warn "#{result.error.code} at #{result.error.phase}"
134
- warn "#{result.steps.size} commands completed before failure"
133
+ warn "#{result.error.code},阶段 #{result.error.phase}"
134
+ warn "失败前已完成 #{result.steps.size} 条命令"
135
135
  end
136
136
  ```
137
137
 
138
- `execute` sends one command; `execute_script` accepts a `Script` or an array of commands. `Script.load(path)` reads a file. Scripts are validated before any device I/O. A result retains completed steps when a later command fails. The library does not replay commands after failure. `save_config` explicitly sends the vendor's save command when supported; script execution does not save automatically.
138
+ `execute` 执行一条命令;`execute_script` 接收 `Script` 或命令数组;`Script.load(path)` 读取脚本文件。所有命令在设备 I/O 前校验,后续步骤失败时仍保留已完成结果,库不会自动重放命令。`save_config` 显式执行厂商保存命令,普通脚本不会自动保存。
139
139
 
140
- Vendor profiles cover pager prompts and common confirmation prompts. For a command-specific dialogue, pass `interactions: [Net::Connector::Interaction.new(/Token:\z/, ->(_) { "value\n" }, sensitive: true)]` to `execute`. Sensitive commands and responses pause session logging and are redacted in errors. Do not place secrets in ordinary command text unless `sensitive: true` is set.
140
+ 厂商档案处理分页和常见确认提示。特定命令可给 `execute` 传入 `interactions: [Net::Connector::Interaction.new(/Token:\z/, ->(_) { "value\n" }, sensitive: true)]`。敏感命令及交互会暂停回显日志并在错误中脱敏;若普通命令文本包含秘密,必须显式标记 `sensitive: true`。
141
141
 
142
- ## Connection settings
142
+ ## 连接与日志设置
143
143
 
144
- `Configuration` accepts `protocol: :ssh` (default) or `:telnet`, `port`, `login_timeout`, `command_timeout`, `write_timeout`, `max_output_bytes`, `log_file`, `logger`, `log_format: :text` or `:raw`, `log_level: :info` (default), and `known_hosts`. Text logs use `ActiveSupport::Logger` and `TaggedLogging`: each line has a local timestamp, severity, device tag, and readable Chinese action. At `:info`, one `log_file` contains connection, login, command, and TFTP outcomes. At `:debug`, that same file also contains sanitized login and device output plus command timing. No separate transcript file is created. `:warn` and `:error` retain only events at those levels or higher. The TFTP example defaults to `:debug`; set `NET_CONNECTOR_LOG_LEVEL` to change it. `log_format: :raw` writes only device bytes to `log_file` without event metadata. Configured credentials and sensitive command interactions are excluded from device output. Inject a Rails logger with `logger: Rails.logger` to send tagged events to the host application; the connector does not close or change its level. Host key policy defaults to `:strict`; `:accept_new` allows first-contact keys, while `:replace` requires an explicit known-hosts file. `telnet_fallback` and `legacy_ssh` are disabled by default and only apply to known connection failures. Commands are passed as argv, without a shell.
144
+ `Configuration` 支持 `protocol: :ssh`(默认)或 `:telnet`,以及端口、超时、输出大小、`log_file`、`logger`、`log_format`、`log_level` 和 `known_hosts` 等参数。文本日志使用 Ruby 标准库 `Logger`,记录本地时间、级别、设备标记和中文事件。`:info` 记录连接、登录、命令和 TFTP 结果;`:debug` 还记录脱敏回显与耗时;`:warn`、`:error` 只保留相应级别。`:raw` 文件只写设备字节,不写事件元数据。可注入 `logger: Rails.logger`,连接器不会关闭或修改调用方的日志器。
145
145
 
146
- Telnet sends credentials without SSH encryption. Enable it only on a trusted management network. The gem does not check device authorization or review change plans; callers must enforce their own operational approval flow.
146
+ 主机密钥策略默认为 `:strict`;`:accept_new` 接受首次连接的密钥;`:replace` 需要显式 `known_hosts` 文件。`telnet_fallback` 和 `legacy_ssh` 默认关闭,只在已识别的连接失败时使用。外部命令以参数数组执行,不经 shell。Telnet 不提供 SSH 加密,只应在可信管理网络启用。设备授权和变更审批由调用方负责。
147
147
 
148
- ## Netdisco inventory and batch backup
148
+ ## Netdisco 清单与批量备份
149
149
 
150
- `Net::Connector::Netdisco` reads the full Netdisco device inventory, maps supported rows to connector instances, then runs backups with a bounded number of worker threads. Netdisco supplies only inventory fields; device login credentials come from your environment or a resolver you provide. Inventory is fetched and validated before any device session starts. Unsupported, filtered, duplicate, missing-credential, failed, and saved-with-close-error outcomes stay distinct.
150
+ `Net::Connector::Netdisco` 读取并验证完整清单,再将支持的记录映射到连接器,使用有上限的工作线程执行备份。Netdisco 只提供清单字段;设备凭据来自环境变量或调用方提供的解析器。清单会在连接任何设备前完成校验;不支持、被过滤、重复、缺少凭据、失败,以及保存成功但关闭失败的结果分别保留。
151
151
 
152
- `Fleet#plan_backup` and `Fleet#plan_tftp_backup` select devices from one validated Netdisco snapshot. Pass the returned plan to `backup_all(plan:)` or `tftp_backup_all(plan:)` so the preview and execution use exactly the same devices. Execution rejects a plan whose tasks or skip results no longer match its inventory. `Netdisco::Worker` runs each selected device independently; an exception or result-callback failure on one device does not stop the others. `batch.summary` reports total, succeeded, failed, partial, skipped, exact status counts, per-device outcomes, and an overall `status` of `succeeded`, `incomplete`, or `no_devices`. A partial result means the device reported a completed backup but closing its session failed. TFTP `reported_uploaded` only means the device reported an upload; it does not verify a file on the server.
152
+ `Fleet#plan_backup` 和 `Fleet#plan_tftp_backup` 从同一份清单生成计划。把计划传给 `backup_all(plan:)` 或 `tftp_backup_all(plan:)`,可使预览与执行选择同一批设备;计划与清单不符时会拒绝执行。单台设备异常或结果回调失败不会阻止其他设备。`batch.summary` 包含总数、成功、失败、部分成功、跳过、具体状态和逐台结果。部分成功表示设备已报告备份完成,但会话关闭失败。TFTP 的 `reported_uploaded` 仅代表设备报告上传,不代表服务器文件已核验。
153
153
 
154
- Local `backup(path:)` compares the new configuration with the existing file by SHA-256. It reports `:created`, `:changed`, or `:unchanged` through `backup.change`; unchanged files keep their modification time. The `on_change:` callback on `backup_all` runs only after a newly created or changed file is saved, including a saved file whose session later fails to close. `on_start:` and `on_result:` observe each attempted device for either batch method. Callback exceptions are recorded in `batch.callback_errors` without stopping other devices. Each outcome records its start, finish, and duration. TFTP uploads have no `change` value because this library cannot compare the server file; a device-reported upload must not trigger a change notification.
154
+ 本地 `backup(path:)` 用 SHA-256 比较新旧配置,`backup.change` 返回 `:created`、`:changed` 或 `:unchanged`;未变化文件保留修改时间。`backup_all` 的 `on_change:` 仅在新建或更改文件保存后触发;`on_start:` 和 `on_result:` 观察每台已尝试设备。回调异常记录在 `batch.callback_errors`,不丢弃设备结果。每项结果包含开始、结束和耗时。TFTP 无法比较服务器文件,因此没有 `change`,也不触发变更通知。
155
155
 
156
- Each batch writes a private JSON report by default and returns its path in `batch.report_location`. Set `result_store: Net::Connector::Netdisco::ResultStore::Database.new(repository: YourModel)` when the caller owns a database table; the repository must implement `create!(attributes)` for `batch.summary`. Pass `result_store: nil` to handle persistence elsewhere. Report write failures remain visible as `batch.report_error`, and `batch.success?` becomes false without losing device outcomes.
156
+ 每批默认写入私有 JSON 报告,路径见 `batch.report_location`。调用方如有数据库仓储,可传 `ResultStore::Database.new(repository: YourModel)`;仓储需实现 `create!(attributes)`。`result_store: nil` 表示由调用方自行持久化。报告失败保留在 `batch.report_error`,同时使 `batch.success?` 为假。
157
157
 
158
158
  ```sh
159
159
  export NETDISCO_URL=https://netdisco.example/netdisco
@@ -183,7 +183,7 @@ end
183
183
  exit 1 unless batch.success?
184
184
  ```
185
185
 
186
- The gem also installs `net-connector-backup`. Its YAML file contains non-secret settings; keep Netdisco and device credentials in environment variables. Environment variables override YAML values. The file is loaded only when `--config FILE` or `NET_CONNECTOR_CONFIG` is set. For example:
186
+ 命令行程序 `net-connector-backup` 的 YAML 文件只允许非敏感设置;Netdisco 和设备凭据留在环境变量中。环境变量优先于 YAML。只有传入 `--config FILE` 或设置 `NET_CONNECTOR_CONFIG` 时才加载文件:
187
187
 
188
188
  ```yaml
189
189
  netdisco:
@@ -213,65 +213,58 @@ net-connector-backup --config config.yml --tftp --all
213
213
  net-connector-backup --config config.yml --export 192.0.2.7 --output ./exports/device.cfg
214
214
  ```
215
215
 
216
- `--plan` fetches and validates inventory without logging into devices. `--host` selects one management IP from that inventory. `--tftp` selects up to five devices per vendor by default; `--all` selects every ready device. Local backups select all ready devices unless `--limit-per-vendor` or `backup.limit_per_vendor` is set. `--show-config` prints effective non-secret settings and does not contact Netdisco. The CLI rejects unknown YAML keys, Ruby object tags, and secrets in the YAML schema. `--export IP` reads an existing local `<IP>.txt` backup (or a unique legacy `<device name>-<IP>.txt` file) without contacting Netdisco or the device; it writes the exact contents to stdout, or atomically creates a mode `0600` file when `--output` is set. Treat exported configurations as sensitive.
216
+ `--plan` 只拉取并验证清单;`--host` 选择一个管理地址;`--tftp` 默认每厂商最多选择五台,`--all` 选择所有就绪设备。本地备份默认选择所有就绪设备,可用 `--limit-per-vendor` 限制。`--show-config` 只输出有效的非敏感设置,不访问 Netdisco。CLI 会拒绝未知 YAML 字段、Ruby 对象标签及配置中的凭据。`--export IP` 离线读取已有 `<IP>.txt`,或唯一匹配的旧版 `<设备名>-<IP>.txt`;默认原样写到标准输出,指定 `--output` 后以 `0600` 权限原子写文件。导出的配置仍是敏感数据。
217
+
218
+ CLI 的计划与批次摘要使用 JSON。非空清单且全部成功时退出码为 `0`;空清单或有跳过、部分成功、失败时为 `1`;清单或配置错误为 `2`。`--host` 未在清单中找到也返回 `2`。由于其他清单记录会标记为过滤,单主机备份成功时批次退出码仍可能是 `1`;应查看 JSON 中的 `succeeded`、`skipped` 和逐台 `status`。
217
219
 
218
- The CLI prints JSON for plans and batch summaries. Exit status is `0` when a nonempty inventory has only successful outcomes, `1` when the inventory is empty or any outcome was skipped, partial, or failed, and `2` for an inventory or configuration error. A `--host` address absent from the inventory is an error with status `2`. A `--host` run can return `1` when its selected backup succeeds because other inventory rows are reported as filtered; inspect `succeeded`, `skipped`, and per-device `status` in the JSON summary.
220
+ 小范围现场试运行可用[本地批量示例](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。
219
221
 
220
- For a small live trial, run [examples/netdisco_backup.rb](examples/netdisco_backup.rb). It fetches a validated inventory snapshot and backs up at most three ready devices per mapped vendor by default. Set `NET_CONNECTOR_SAMPLE_PER_VENDOR` to an integer from 1 to 5 to change the sample size. Results and a per-device `summary.json` are written under a unique `examples/backups/<UTC timestamp>-<suffix>/` directory; that directory is Git-ignored. The example uses the same environment variables listed below.
222
+ 全量 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` 和逐台结果均以私有权限保存。
221
223
 
222
- For device-initiated TFTP uploads, run [examples/netdisco_tftp_backup.rb](examples/netdisco_tftp_backup.rb) with `NETDISCO_URL`, `TFTP_HOST`, and the credential environment variables below. It samples at most five devices per vendor by default; set `NET_CONNECTOR_ALL=1` to select every ready inventory device. Full runs use 50 simultaneous tasks by default; `NET_CONNECTOR_CONCURRENCY` can override this. The full-run plan is saved as `plan.json`. Remote filenames are unique `<device name>-<IP>.cfg` (`.tgz` for Radware, `.dat` for Hillstone). PAN-OS uses its fixed `running-config.xml` filename: additional Palo Alto devices are reported as `remote_filename_collision` rather than overwriting another backup. PAN-OS requires a positive `Sent ... bytes` response to report upload success. H3C and H3C wireless read `display startup` to find the saved startup file; override the source with `NET_CONNECTOR_H3C_TFTP_SOURCE_FILE` or `NET_CONNECTOR_H3C_WIRELESS_TFTP_SOURCE_FILE`. Huawei defaults to `flash:/startup.cfg` and accepts `NET_CONNECTOR_HUAWEI_TFTP_SOURCE_FILE`. Nexus 9000 uses `management` by default and Hillstone uses `mgt-vr`; set `NET_CONNECTOR_TFTP_VRFS` to a JSON map such as `{"cisco_nxos":"backup","hillstone":"mgt-vr"}` to override either one. Hillstone appends its unique `.dat` filename after the device `vrouter` argument. When calling a Hillstone connector directly without `path:`, the device generates its own filename and the return value reports that filename. `reported_uploaded` means the device reported success. Small runs attempt a server readback; full runs skip per-file readback and mark these files unverified. Run `ruby examples/review_tftp_backup.rb <batch directory>` to keep the original summary and review remaining failures from the session logs. Each `logs/<IP>.log` contains the session events and a final backup result; at debug level it also includes the sanitized login and device output in that same file. Per-device outcomes, incremental `events.jsonl`, and logs are written privately under a unique `examples/backups/<UTC timestamp>-<suffix>/` directory.
224
+ 批量 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`。源文件是设备上的路径,需符合连接器校验规则。
223
225
 
224
- Local backups are written to `<backup directory>/<IP>.txt`, using the normalized management IP; `:` in IPv6 addresses becomes `_`. Device renames preserve the filename and comparison baseline. If the canonical file is absent, a unique legacy `<device name>-<IP>.txt` file supplies the comparison baseline and remains untouched. Multiple legacy matches fail explicitly; review them and place the verified current configuration at the canonical path. Canonical files take precedence, and symlinks or non-regular files are rejected. Inventory names remain in result metadata and TFTP filenames. Files are atomically replaced with mode `0600`. The directory is created with mode `0700` if absent. A batch runs in the current process; schedule it with your job runner or cron if you need recurring or durable work. It does not retry a failed device command.
226
+ 本地配置备份写到 `<目录>/<IP>.txt`,IPv6 的 `:` 转成 `_`。设备改名不改变文件名或比较基线。若规范文件不存在,唯一匹配的旧版 `<设备名>-<IP>.txt` 可作比较基线但不会被改写;匹配多个旧文件时会明确失败。规范文件优先,符号链接和非普通文件会被拒绝。文件原子替换为 `0600`,新目录权限为 `0700`。批次在当前进程执行,需要定时任务或持久队列时由调用方安排;失败命令不会自动重试。
225
227
 
226
- | Environment variable | Default | Purpose |
228
+ | 环境变量 | 默认值 | 用途 |
227
229
  | --- | --- | --- |
228
- | `NETDISCO_URL` | required | Netdisco base URL, including any tenant path |
229
- | `NET_CONNECTOR_CONFIG` | unset | Explicit non-secret YAML settings file for the CLI |
230
- | `NETDISCO_USERNAME`, `NETDISCO_PASSWORD` | required unless API key is supplied | Inventory API login |
231
- | `NETDISCO_API_KEY` | unset | Use an existing API key instead of login |
232
- | `NETDISCO_PAGE_SIZE` | `500` | Device inventory page size |
233
- | `NET_CONNECTOR_DEVICE_USERNAME`, `NET_CONNECTOR_DEVICE_PASSWORD` | unset | Device login defaults |
234
- | `NET_CONNECTOR_<VENDOR>_USERNAME`, `NET_CONNECTOR_<VENDOR>_PASSWORD` | unset | Override credentials for one connector key, such as `CISCO_IOS` |
235
- | `NET_CONNECTOR_BACKUP_DIRECTORY` | `./backups` | Backup destination and default batch-report directory |
236
- | `NET_CONNECTOR_CONCURRENCY` | `4` | Maximum simultaneous device backups, from 1 to 50 |
237
- | `NET_CONNECTOR_INCLUDE_HOSTS`, `NET_CONNECTOR_EXCLUDE_HOSTS` | unset | Comma-separated management IP filters |
238
- | `NET_CONNECTOR_INCLUDE_VENDORS` | unset | Comma-separated connector keys to include |
239
- | `NET_CONNECTOR_VENDOR_OVERRIDES` | `{}` | JSON object mapping a Netdisco vendor label to a connector key |
240
- | `NET_CONNECTOR_HOST_OVERRIDES` | `{}` | JSON object mapping a management IP to a connector key |
241
- | `NET_CONNECTOR_DEVICE_RULES` | `[]` | JSON array of mapping rules with `vendor`, optional `os` or `model_prefix`, and `connector` |
242
- | `NET_CONNECTOR_PROTOCOL` | `ssh` | Default device protocol; per-vendor override is available |
243
- | `NET_CONNECTOR_KNOWN_HOSTS`, `NET_CONNECTOR_HOST_KEY_POLICY` | system hosts, `strict` | SSH host-key settings |
244
- | `NET_CONNECTOR_LOG_DIRECTORY` | unset | Optional per-device session log directory |
245
- | `NET_CONNECTOR_LOG_LEVEL` | `info` (`debug` in TFTP example) | `debug`, `info`, `warn`, or `error` event detail |
246
- | `NET_CONNECTOR_TFTP_VRFS` | `{}` | JSON map of NX-OS and Hillstone VRF names for the TFTP example |
247
-
248
- Mapping rules are evaluated before vendor-label overrides and built-in rules. A specific rule can distinguish models that share a vendor label:
230
+ | `NETDISCO_URL` | 必填 | Netdisco 服务根地址,可包含租户路径 |
231
+ | `NET_CONNECTOR_CONFIG` | 未设置 | CLI 的非敏感 YAML 配置文件 |
232
+ | `NETDISCO_USERNAME`, `NETDISCO_PASSWORD` | 未提供 API 密钥时必填 | 清单 API 登录 |
233
+ | `NETDISCO_API_KEY` | 未设置 | 直接使用已有 API 密钥 |
234
+ | `NETDISCO_PAGE_SIZE` | `500` | 清单分页大小 |
235
+ | `NET_CONNECTOR_DEVICE_USERNAME`, `NET_CONNECTOR_DEVICE_PASSWORD` | 未设置 | 设备登录默认凭据 |
236
+ | `NET_CONNECTOR_<VENDOR>_USERNAME`, `NET_CONNECTOR_<VENDOR>_PASSWORD` | 未设置 | 单厂商凭据,例如 `CISCO_IOS` |
237
+ | `NET_CONNECTOR_BACKUP_DIRECTORY` | `./backups` | 备份及默认报告目录 |
238
+ | `NET_CONNECTOR_CONCURRENCY` | `4` | 并发设备数,范围 1 至 50 |
239
+ | `NET_CONNECTOR_INCLUDE_HOSTS`, `NET_CONNECTOR_EXCLUDE_HOSTS` | 未设置 | 逗号分隔的管理地址过滤器 |
240
+ | `NET_CONNECTOR_INCLUDE_VENDORS` | 未设置 | 逗号分隔的厂商标识过滤器 |
241
+ | `NET_CONNECTOR_VENDOR_OVERRIDES` | `{}` | Netdisco 厂商标签到连接器标识的 JSON 映射 |
242
+ | `NET_CONNECTOR_HOST_OVERRIDES` | `{}` | 管理地址到连接器标识的 JSON 映射 |
243
+ | `NET_CONNECTOR_DEVICE_RULES` | `[]` | 含 `vendor`、可选 `os` 或 `model_prefix` 及 `connector` 的映射规则 |
244
+ | `NET_CONNECTOR_PROTOCOL` | `ssh` | 默认连接协议,可按厂商覆盖 |
245
+ | `NET_CONNECTOR_KNOWN_HOSTS`, `NET_CONNECTOR_HOST_KEY_POLICY` | 系统主机记录、`strict` | SSH 主机密钥设置 |
246
+ | `NET_CONNECTOR_LOG_DIRECTORY` | 未设置 | 逐台会话日志目录 |
247
+ | `NET_CONNECTOR_LOG_LEVEL` | `info`(TFTP 示例为 `debug`) | `debug`、`info`、`warn`、`error` 日志级别 |
248
+ | `NET_CONNECTOR_TFTP_VRFS` | `{}` | NX-OS 和山石的 TFTP VRF 映射 |
249
+ | `NET_CONNECTOR_<VENDOR>_TFTP_SOURCE_FILE` | 按厂商决定 | 批量 TFTP 使用的设备源文件,H3C 可覆盖自动发现结果 |
250
+
251
+ 设备映射规则先于厂商标签覆盖和内置规则执行,可用 `model_prefix` 区分同厂商型号:
249
252
 
250
253
  ```sh
251
254
  export NET_CONNECTOR_DEVICE_RULES='[{"vendor":"Cisco","model_prefix":"N9K","connector":"cisco_nxos"}]'
252
255
  ```
253
256
 
254
- The environment is read when a `Settings` object builds its client or rules, and device credentials are read for each backup task. A new batch can therefore pick up rotated credentials. For per-device secrets managed outside the environment, inject a resolver: `Fleet.new(credentials: ->(device) { { username: "...", password: "..." } })`. To inspect association without connecting, call `fleet.devices`; a ready item can instantiate its connector with `device.connector(username: "...", password: "...")`.
257
+ `Settings` 创建客户端或规则时读取环境变量;每台任务启动时才读取设备凭据,因此新批次可接收轮换后的凭据。也可给 `Fleet.new` 注入 `credentials:` 解析器。`fleet.devices` 可在不连接设备时检查映射,随后用 `device.connector(...)` 创建连接器。
255
258
 
256
- ## Development
259
+ ## 开发与验证
257
260
 
258
261
  ```sh
259
262
  bundle install
260
263
  script/ci
261
264
  ```
262
265
 
263
- The same command runs in CI on Linux and macOS with Ruby 3.2, 3.3, 3.4 and 4.0.
264
- It scans source and available Git history for sensitive data, runs Ruby and
265
- workflow lint plus the complete test suite, and verifies the built gem through
266
- isolated installation and a real local PTY session. No network device is needed.
267
- The first run downloads checksum-pinned Gitleaks and actionlint binaries.
268
-
269
- `bundle exec rake test` runs tests, `bundle exec rake lint` checks Ruby code,
270
- and `bundle exec rake security:check` scans source and history. The full
271
- pre-release check is also available as `bundle exec rake release:check`.
272
- Builds and redacted scan reports go under ignored `tmp/` directories.
273
-
274
- Keep real credentials in environment variables and local configuration outside
275
- version control. See [verification and sensitive-data policy](docs/VERIFICATION.md)
276
- for scan coverage, dependency policy and `.gitignore`, and
277
- [release instructions](docs/RELEASING.md) for local and GitHub Actions publishing.
266
+ CI 在 Linux 和 macOS 上覆盖 Ruby 3.2、3.3、3.4、4.0。`script/ci` 扫描源码与可用 Git 历史中的敏感数据,执行 Ruby 与工作流 lint、完整测试,并验证已构建 gem 的隔离安装和本地 PTY 烟测;不连接真实网络设备。首次运行需下载固定校验和的 Gitleaks 与 actionlint。
267
+
268
+ `bundle exec rake test` 报告已加载库文件的行、分支覆盖率和未加载文件数;当前只记录基线,不作为发布门槛。`bundle exec rake lint` 检查 Ruby 代码,`bundle exec rake security:check` 扫描敏感数据,`bundle exec rake release:check` 执行完整预检。构建产物和脱敏扫描报告保存在被忽略的 `tmp/` 下。实现注释、公开文档和贡献模板均使用中文。
269
+
270
+ 真实凭据应放在环境变量和版本库外的本地配置中。检查范围、依赖政策和忽略规则见[验证文档](docs/VERIFICATION.md),发布流程见[发布文档](docs/RELEASING.md)。
data/docs/VERIFICATION.md CHANGED
@@ -17,13 +17,14 @@ script/ci
17
17
  | `security:check` | 扫描待提交源码和可用的完整 Git 历史;拒绝混入源码的本地凭据、配置和产物 |
18
18
  | `lint` | 对库、脚本、示例、测试、Gemfile、gemspec 和 Rakefile 执行 RuboCop |
19
19
  | `lint:workflows` | 用 actionlint 校验 GitHub Actions 工作流 |
20
- | `test` | 执行全部 Minitest;敏感信息和发布测试使用临时文件、临时仓库与模拟远端响应 |
20
+ | `test` | 执行全部 Minitest,并报告已加载库文件的行与分支覆盖率;敏感信息和发布测试使用临时文件、临时仓库与模拟远端响应 |
21
21
  | `package:verify` | 构建 gem,检查元数据、文件白名单、源文件字节和执行位,扫描解包内容及元数据,再进行隔离安装 |
22
22
 
23
23
  隔离安装清除当前 Bundler 和 Ruby 注入变量,分别验证普通 `gem install`
24
24
  和只有 `net-connector` 依赖的最小 Bundler 应用。烟测加载全部厂商,使用本地
25
25
  PTY 子进程采集配置,读取包内 TextFSM 模板,并检查 CLI。它不连接网络设备,
26
26
  也不证明现场设备协议或真实发布服务已验收。
27
+ 覆盖率目前只用于观察,不设硬性门槛;未加载文件会单独计数。
27
28
 
28
29
  CI 矩阵为 Ubuntu 24.04 / macOS 15 × Ruby 3.2、3.3、3.4、4.0。
29
30
  GitHub Actions 固定提交 SHA;Gitleaks 与 actionlint 固定版本和各平台归档