net-connector 0.11.1 → 0.13.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +1 -1
- data/README.zh-CN.md +1 -1
- data/docs/VERIFICATION.md +17 -3
- data/docs/architecture.md +77 -57
- data/docs/netdisco.md +9 -5
- data/docs/netdisco.zh-CN.md +14 -10
- data/docs/templates.md +59 -0
- data/docs/usage.md +6 -1
- data/docs/usage.zh-CN.md +7 -2
- data/docs/vendors.md +61 -0
- data/lib/net/connector/concurrency/observer.rb +71 -0
- data/lib/net/connector/concurrency/pool.rb +116 -0
- data/lib/net/connector/device/base.rb +6 -2
- data/lib/net/connector/device/local_backup.rb +14 -10
- data/lib/net/connector/device/profile/builder.rb +6 -0
- data/lib/net/connector/device/profile.rb +5 -3
- data/lib/net/connector/device/remote_backup/receipt.rb +40 -0
- data/lib/net/connector/device/remote_backup/target.rb +36 -0
- data/lib/net/connector/device/remote_backup.rb +51 -0
- data/lib/net/connector/device/running_config/strategy.rb +3 -3
- data/lib/net/connector/device/running_config.rb +2 -2
- data/lib/net/connector/device/scope.rb +45 -0
- data/lib/net/connector/device/tftp/file_upload.rb +7 -9
- data/lib/net/connector/device/tftp/strategy.rb +20 -6
- data/lib/net/connector/device/topology/change.rb +22 -0
- data/lib/net/connector/device/topology/immediate_strategy.rb +6 -12
- data/lib/net/connector/device/topology/interface_blocks.rb +18 -0
- data/lib/net/connector/device/topology/neighbor.rb +22 -0
- data/lib/net/connector/device/topology/plan.rb +39 -0
- data/lib/net/connector/device/topology/quoted_text.rb +23 -0
- data/lib/net/connector/device/topology/stages.rb +1 -1
- data/lib/net/connector/device/topology/strategy.rb +4 -0
- data/lib/net/connector/device/topology.rb +7 -12
- data/lib/net/connector/engine/command/result.rb +34 -0
- data/lib/net/connector/engine/command.rb +1 -50
- data/lib/net/connector/engine/core.rb +1 -0
- data/lib/net/connector/engine/dialogue.rb +3 -67
- data/lib/net/connector/engine/error/metadata.rb +57 -0
- data/lib/net/connector/engine/errors.rb +3 -3
- data/lib/net/connector/engine/event.rb +27 -0
- data/lib/net/connector/engine/execution.rb +2 -2
- data/lib/net/connector/engine/interaction.rb +57 -0
- data/lib/net/connector/engine/log/event.rb +2 -2
- data/lib/net/connector/engine/log/{stream.rb → redacting_writer.rb} +1 -24
- data/lib/net/connector/engine/log/transcript.rb +32 -0
- data/lib/net/connector/engine/log.rb +9 -8
- data/lib/net/connector/engine/response.rb +21 -0
- data/lib/net/connector/engine/response_reader.rb +8 -2
- data/lib/net/connector/engine/result.rb +3 -27
- data/lib/net/connector/engine/script.rb +57 -0
- data/lib/net/connector/engine/session.rb +1 -1
- data/lib/net/connector/engine/terminal/renderer.rb +186 -0
- data/lib/net/connector/engine/terminal/text.rb +35 -0
- data/lib/net/connector/engine/{transport.rb → transports.rb} +1 -21
- data/lib/net/connector/netdisco/batch.rb +37 -6
- data/lib/net/connector/netdisco/cli/backup.rb +173 -0
- data/lib/net/connector/netdisco/cli/credentials.rb +39 -0
- data/lib/net/connector/netdisco/cli.rb +3 -3
- data/lib/net/connector/netdisco/device/access.rb +31 -0
- data/lib/net/connector/netdisco/device.rb +3 -3
- data/lib/net/connector/netdisco/diagnostic.rb +7 -7
- data/lib/net/connector/netdisco/fleet.rb +32 -41
- data/lib/net/connector/netdisco/inventory/budget.rb +53 -0
- data/lib/net/connector/netdisco/inventory/cache.rb +226 -0
- data/lib/net/connector/netdisco/inventory/http.rb +255 -0
- data/lib/net/connector/netdisco/inventory/postgres.rb +236 -0
- data/lib/net/connector/netdisco/inventory.rb +47 -0
- data/lib/net/connector/netdisco/operations/batch.rb +78 -0
- data/lib/net/connector/netdisco/operations/outcome.rb +76 -0
- data/lib/net/connector/netdisco/operations.rb +28 -53
- data/lib/net/connector/netdisco/progress.rb +4 -4
- data/lib/net/connector/netdisco/report/files.rb +3 -3
- data/lib/net/connector/netdisco/report/store.rb +47 -0
- data/lib/net/connector/netdisco/report/text.rb +2 -2
- data/lib/net/connector/netdisco/report.rb +16 -13
- data/lib/net/connector/netdisco/rules.rb +5 -0
- data/lib/net/connector/netdisco/settings.rb +32 -32
- data/lib/net/connector/netdisco/tftp/archive.rb +214 -0
- data/lib/net/connector/netdisco/tftp/verifier.rb +93 -0
- data/lib/net/connector/netdisco/worker.rb +3 -3
- data/lib/net/connector/netdisco.rb +1 -1
- data/lib/net/connector/storage/{private_file.rb → atomic_file.rb} +1 -1
- data/lib/net/connector/storage/{safe_file.rb → file_reader.rb} +1 -1
- data/lib/net/connector/storage/saved_config.rb +5 -5
- data/lib/net/connector/storage.rb +2 -2
- data/lib/net/connector/templates/{cisco_ios_running_config_interfaces.textfsm → cisco/show_running_config.textfsm} +1 -0
- data/lib/net/connector/templates/cisco_nxos/show_cdp_neighbors_detail.textfsm +11 -0
- data/lib/net/connector/templates/cisco_nxos/show_ip_interface_brief.textfsm +10 -0
- data/lib/net/connector/templates/cisco_nxos/show_running_config.textfsm +13 -0
- data/lib/net/connector/templates/fortinet/diagnose_lldp_rx_neighbor.textfsm +7 -0
- data/lib/net/connector/templates/fortinet/show_full_configuration.textfsm +21 -0
- data/lib/net/connector/templates/{h3c_interface_descriptions.textfsm → h3c/display_current_configuration.textfsm} +1 -0
- data/lib/net/connector/templates/{h3c_lldp_local_first.textfsm → h3c/display_lldp_neighbor_information_list.textfsm} +7 -1
- data/lib/net/connector/templates/{hillstone_interface_descriptions.textfsm → hillstone/show_configuration_running.textfsm} +1 -0
- data/lib/net/connector/templates/huawei/display_current_configuration.textfsm +13 -0
- data/lib/net/connector/templates/huawei/display_lldp_neighbor_brief.textfsm +9 -0
- data/lib/net/connector/templates/index +22 -7
- data/lib/net/connector/templates/juniper/show_configuration_display_set.textfsm +10 -0
- data/lib/net/connector/templates/juniper/show_lldp_neighbors_detail.textfsm +19 -0
- data/lib/net/connector/templates/{palo_alto_interface_descriptions.textfsm → palo_alto/show.textfsm} +1 -0
- data/lib/net/connector/templates/radware/cfg_dump.textfsm +15 -0
- data/lib/net/connector/textfsm.rb +2 -2
- data/lib/net/connector/vendor/cisco/running_config.rb +25 -0
- data/lib/net/connector/vendor/cisco/topology.rb +53 -0
- data/lib/net/connector/vendor/cisco.rb +25 -0
- data/lib/net/connector/vendor/cisco_ios/tftp.rb +40 -0
- data/lib/net/connector/vendor/cisco_ios.rb +5 -14
- data/lib/net/connector/vendor/cisco_nxos/tftp.rb +46 -0
- data/lib/net/connector/vendor/cisco_nxos/topology.rb +22 -0
- data/lib/net/connector/vendor/cisco_nxos.rb +7 -17
- data/lib/net/connector/vendor/fortinet/tftp.rb +30 -0
- data/lib/net/connector/vendor/fortinet/topology.rb +58 -0
- data/lib/net/connector/vendor/fortinet.rb +40 -0
- data/lib/net/connector/vendor/h3c/tftp.rb +36 -0
- data/lib/net/connector/vendor/h3c/topology.rb +49 -47
- data/lib/net/connector/vendor/h3c.rb +5 -5
- data/lib/net/connector/vendor/h3c_wireless.rb +2 -2
- data/lib/net/connector/vendor/hillstone/running_config.rb +8 -5
- data/lib/net/connector/vendor/hillstone/tftp.rb +54 -0
- data/lib/net/connector/vendor/hillstone/topology.rb +44 -41
- data/lib/net/connector/vendor/hillstone.rb +6 -6
- data/lib/net/connector/vendor/huawei/tftp.rb +28 -0
- data/lib/net/connector/vendor/huawei/topology.rb +48 -0
- data/lib/net/connector/vendor/huawei.rb +6 -4
- data/lib/net/connector/vendor/juniper/remote_backup.rb +41 -0
- data/lib/net/connector/vendor/juniper/topology.rb +41 -0
- data/lib/net/connector/vendor/juniper.rb +31 -0
- data/lib/net/connector/vendor/palo_alto/running_config.rb +70 -67
- data/lib/net/connector/vendor/palo_alto/tftp.rb +48 -0
- data/lib/net/connector/vendor/palo_alto/topology.rb +48 -51
- data/lib/net/connector/vendor/palo_alto.rb +6 -6
- data/lib/net/connector/vendor/radware/running_config.rb +11 -8
- data/lib/net/connector/vendor/radware/tftp.rb +50 -0
- data/lib/net/connector/vendor/radware/topology.rb +12 -7
- data/lib/net/connector/vendor/radware.rb +6 -6
- data/lib/net/connector/vendor/topsec.rb +27 -0
- data/lib/net/connector/vendor.rb +24 -0
- data/lib/net/connector/version.rb +1 -1
- data/lib/net/connector.rb +3 -19
- metadata +80 -43
- data/lib/net/connector/background_task.rb +0 -69
- data/lib/net/connector/device_session.rb +0 -43
- data/lib/net/connector/engine/error_metadata.rb +0 -52
- data/lib/net/connector/engine/terminal_renderer.rb +0 -184
- data/lib/net/connector/engine/terminal_text.rb +0 -33
- data/lib/net/connector/execution_pool.rb +0 -114
- data/lib/net/connector/netdisco/backup_run.rb +0 -171
- data/lib/net/connector/netdisco/client.rb +0 -281
- data/lib/net/connector/netdisco/connection.rb +0 -37
- data/lib/net/connector/netdisco/database_client.rb +0 -233
- data/lib/net/connector/netdisco/inventory_budget.rb +0 -49
- data/lib/net/connector/netdisco/inventory_cache.rb +0 -224
- data/lib/net/connector/netdisco/operation_batch.rb +0 -154
- data/lib/net/connector/netdisco/result_store.rb +0 -44
- data/lib/net/connector/netdisco/tftp_archive.rb +0 -212
- data/lib/net/connector/netdisco/tftp_verification.rb +0 -91
- data/lib/net/connector/templates/h3c_lldp_name_first.textfsm +0 -8
- data/lib/net/connector/templates/radware_port_names.textfsm +0 -13
- data/lib/net/connector/vendor/cisco_ios/running_config.rb +0 -22
- data/lib/net/connector/vendor/cisco_ios/tftp_backup.rb +0 -41
- data/lib/net/connector/vendor/cisco_ios/topology.rb +0 -50
- data/lib/net/connector/vendor/cisco_nxos/running_config.rb +0 -13
- data/lib/net/connector/vendor/cisco_nxos/tftp_backup.rb +0 -43
- data/lib/net/connector/vendor/h3c/tftp_backup.rb +0 -33
- data/lib/net/connector/vendor/hillstone/tftp_backup.rb +0 -55
- data/lib/net/connector/vendor/huawei/tftp_backup.rb +0 -25
- data/lib/net/connector/vendor/palo_alto/tftp_backup.rb +0 -49
- data/lib/net/connector/vendor/radware/tftp_backup.rb +0 -49
- /data/lib/net/connector/templates/{cisco_cdp_neighbors_detail.textfsm → cisco/show_cdp_neighbors_detail.textfsm} +0 -0
- /data/lib/net/connector/templates/{cisco_ios_show_ip_interface_brief.textfsm → cisco_ios/show_ip_interface_brief.textfsm} +0 -0
- /data/lib/net/connector/templates/{hillstone_lldp_neighbors.textfsm → hillstone/show_lldp_neighbor_information.textfsm} +0 -0
- /data/lib/net/connector/templates/{palo_alto_lldp_neighbors.textfsm → palo_alto/show_lldp_neighbors_all.textfsm} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f018d69d8d4cf234bd90fcdd4e56a60e4184ef5ddb4f4cf15f4ff49206d5631
|
|
4
|
+
data.tar.gz: f8e0afe1a9131435134e5580dc0f3ffdc9823e55b7fc5665d60b1768f3c223fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 620f62156a11192ae6648f48878ef61cb810830ee3c9dee8c7e8c6bc12b2b88e4c3f8ba420535bbb82b94a423328897c52844a4b7d76c4a404ae4e59b9f54c95
|
|
7
|
+
data.tar.gz: dc0ea681afb4a9a9cbeef79778d1cfebdf009004ee036a61dfcc8e9cb9f53579ad1967088a784bc2ed730874bdb1770207a57603b800caa55545dec8c8089915
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.13.0 - 2026-10-18
|
|
4
|
+
|
|
5
|
+
- 不兼容调整:H3C 的 LLDP 列序变体合并为 `h3c/display_lldp_neighbor_information_list.textfsm`,显式引用旧模板路径的调用方需要更新。
|
|
6
|
+
- Huawei VRP 增加 LLDP 邻居和接口描述读取,FortiOS 增加当前视图的接口描述读取,Junos 分别返回物理接口与逻辑单元描述。
|
|
7
|
+
- 补齐各平台配置解析索引,修复 H3C 本地接口首列时的字段错位,并要求厂商名和命令完整匹配,避免选择错误模板。
|
|
8
|
+
- 接口描述缺少值、引号未闭合或配置作用域不明时拒绝解析,Radware 端口名称中的转义引号可正确还原。
|
|
9
|
+
- 拓扑计划和备份批次固定各自拥有的嵌套数据,调用方修改原始容器不再改变已生成结果,更新需通过 `with` 创建新快照。
|
|
10
|
+
- 批量报告复用状态计数,减少重复统计时对设备结果的遍历,未知状态仍不能满足成功策略。
|
|
11
|
+
- 修复 Ruby 3.2 下远程备份目标及回执的 `with` 绕过校验的问题,拒绝非法协议、路径及没有核验证据的完成声明。
|
|
12
|
+
|
|
13
|
+
## 0.12.0 - 2026-10-17
|
|
14
|
+
|
|
15
|
+
- 不兼容调整:模块命名空间和加载路径按领域重组,厂商类改为 `Vendor::CiscoIos` 等直接入口,清单与报告存储分别使用 `inventory:`、`report_store:` 注入,旧名称及兼容别名已移除。
|
|
16
|
+
- 模板统一采用平台目录下的完整命令名,H3C 列序变体使用文件名后缀,调用方需同步更新显式模板路径。
|
|
17
|
+
- 新增 Topsec TopOS、Fortinet FortiOS、Juniper Junos 的配置采集和本地备份,Netdisco 可按设备系统映射到对应平台。
|
|
18
|
+
- FortiOS 支持显式保存、完整配置 TFTP 导出及跨 7.6.3 的 LLDP 查询,Junos 支持已提交配置的 FTP/SCP 导出、LLDP 邻居和接口描述读取。
|
|
19
|
+
- NX-OS 增加独立的 CDP、运行配置及多 VRF IP 接口摘要模板,残缺邻居块和后续配置段不会混入已识别记录,Junos 管理信息中的端口编号也不会覆盖邻居端口。
|
|
20
|
+
- 动态确认、登录挑战及特权应答在发送前重新检查总期限,迟到应答不再发送,保留对应阶段错误和已完成步骤。
|
|
21
|
+
- 备份回执及批次结果持有独立冻结的摘要和时间副本,结果回调不能原地改写已完成备份的审计信息。
|
|
22
|
+
|
|
3
23
|
## 0.11.1 - 2026-10-17
|
|
4
24
|
|
|
5
25
|
- 允许控制器操作租约的同一线程和 Fiber 在命令结束后关闭会话;关闭期间仍拒绝回调重入、其他线程和其他 Fiber,嵌套 AP 清理失败可在释放租约前隔离连接。
|
data/README.md
CHANGED
|
@@ -31,7 +31,7 @@ The block closes the session on exit. Backups use private files and atomic repla
|
|
|
31
31
|
|
|
32
32
|
## Supported devices
|
|
33
33
|
|
|
34
|
-
H3C Comware and wireless controllers, Cisco IOS/IOS XE and NX-OS, Huawei, Hillstone StoneOS, Radware Alteon,
|
|
34
|
+
H3C Comware and wireless controllers, Cisco IOS/IOS XE and NX-OS, Huawei, Hillstone StoneOS, Radware Alteon, Palo Alto PAN-OS, Topsec TopOS, Fortinet FortiOS, and Juniper Junos OS. Capabilities vary by vendor; see the [device guide](docs/usage.md#supported-devices). Synthetic tests do not establish support for every firmware version.
|
|
35
35
|
|
|
36
36
|
## Fleet backups
|
|
37
37
|
|
data/README.zh-CN.md
CHANGED
|
@@ -31,7 +31,7 @@ end
|
|
|
31
31
|
|
|
32
32
|
## 支持的设备
|
|
33
33
|
|
|
34
|
-
H3C Comware 及无线控制器、Cisco IOS/IOS XE 与 NX-OS、华为、山石 StoneOS、Radware Alteon
|
|
34
|
+
H3C Comware 及无线控制器、Cisco IOS/IOS XE 与 NX-OS、华为、山石 StoneOS、Radware Alteon 、Palo Alto PAN-OS、天融信 TopOS、Fortinet FortiOS 和 Juniper Junos OS。各厂商能力不同,详见[设备操作指南](docs/usage.zh-CN.md#支持的设备)。合成测试不代表所有固件均已现场验证。
|
|
35
35
|
|
|
36
36
|
## 批量备份
|
|
37
37
|
|
data/docs/VERIFICATION.md
CHANGED
|
@@ -97,7 +97,7 @@ debug Logger 计数目标样本;不关闭协议检查、丢弃步骤或共享
|
|
|
97
97
|
| --- | --- | --- |
|
|
98
98
|
| 核心引擎 | `lib/net/connector/engine/**/*.rb` | 80% / 80% |
|
|
99
99
|
| 脱敏与错误处理 | `lib/net/connector/engine/errors.rb`,包含 `Redactor` | 80% / 80% |
|
|
100
|
-
| 批量并发 | `lib/net/connector/
|
|
100
|
+
| 批量并发 | `lib/net/connector/concurrency/observer.rb`、`lib/net/connector/concurrency/pool.rb`、`lib/net/connector/device/scope.rb`、`netdisco/worker.rb` | 80% / 80% |
|
|
101
101
|
|
|
102
102
|
关键组为空、任一关键文件未测量或组内没有可执行行时均失败。没有分支的组
|
|
103
103
|
不需要分支命中,但仍须通过行覆盖率及文件加载检查。
|
|
@@ -141,7 +141,7 @@ SHA-256,首次使用时从官方 GitHub Release 下载,缓存到 `tmp/tools/
|
|
|
141
141
|
|
|
142
142
|
`test/tftp_boundary_test.rb` 验证内置策略在首次 I/O 前拒绝不支持的参数组合,使用 Queue 固定 H3C 源探测后的竞争窗口,检查线程/Fiber 拒绝。上传确认后的日志、命令清理、租约清理、路径和元数据钩子失败保留回执且不重传;随机假秘密不进入新错误正文。统一回执测试核对配置来源、实际路径、设备报告等级和冻结字段;Fleet 拒绝第三方和子类提供的完成事实。既有成功/失败/echo/控制符证据用同一套合成夹具继续测试,来源和 unknown 固件范围见 `test/fixtures/tftp/README.md`。普通及最小 Bundler 安装通过本地 PTY 验证唯一的 TFTP 回执入口,没有连接真实 TFTP 服务器或上传配置。
|
|
143
143
|
|
|
144
|
-
`test/netdisco_reporting_test.rb` 覆盖 strict/selected 的状态决策矩阵、显式 CLI 退出码、统一 schema 2、Outcome 的耗时与诊断成员、Fleet/
|
|
144
|
+
`test/netdisco_reporting_test.rb` 覆盖 strict/selected 的状态决策矩阵、显式 CLI 退出码、统一 schema 2、Outcome 的耗时与诊断成员、Fleet/Report::Store 的 Report 契约,以及部分成功/回调/报告错误阻止 selected 成功。动态假秘密放入异常消息、输出、命令、source、line、phase、code 和自定义类型名,报告与私有 JSON 不得含它。受控文件/TFTP 回执阶段跨 Worker 复制仍保留,普通文件异常不能冒充设备产物。可控时钟让 UTC 倒退而单调时间前进,断言设备及批次耗时准确且无负值;没有用固定睡眠推断时间行为。
|
|
145
145
|
|
|
146
146
|
`test/backup_lock_test.rb` 通过 Queue、独立 Ruby 进程及可控单调时钟检查采集前互斥、有限等待、锁释放、线程/Fiber/回调递归、路径别名、私有权限、硬链接/FIFO 拒绝和符号链接替换。`test/backup_identity_test.rb` 验证规范地址命名、改名后的稳定身份、mtime、非普通文件拒绝和非规范文件不参与比较;稳定锁文件不作为多余备份计数。
|
|
147
147
|
|
|
@@ -232,7 +232,7 @@ BUNDLE_GEMFILE=gemfiles/json3.gemfile bundle exec ruby script/verify_json3.rb
|
|
|
232
232
|
|
|
233
233
|
`test/examples_test.rb` 验证示例进程退出码与报告策略一致、报告写入错误、统一 devices 结构、
|
|
234
234
|
TFTP 远端未核验回执以及自定义日志目录;离线复核同时支持历史 outcomes 和新 devices 报告。
|
|
235
|
-
`test/
|
|
235
|
+
`test/tftp_verifier_test.rb` 验证空文件、旧文件、符号链接、目录逃逸和 verified 策略。
|
|
236
236
|
|
|
237
237
|
## Ruby 惯用法重构回归
|
|
238
238
|
|
|
@@ -246,3 +246,17 @@ TFTP 远端未核验回执以及自定义日志目录;离线复核同时支持
|
|
|
246
246
|
源码根目录 `RENAMES.md` 记录批准的改名与兼容边界。完整验收仍运行 `bundle exec rake ci`;
|
|
247
247
|
临时 PostgreSQL 使用 `bundle exec rake test:postgres`,可通过 `NC_TEST_PG_BINDIR` 指定服务端工具目录。
|
|
248
248
|
最低 Ruby 3.2 必须在对应解释器中运行 lint/test;RuboCop 的 TargetRubyVersion 和 Ruby 4.0 测试不能替代此项。
|
|
249
|
+
|
|
250
|
+
## 报告统计基准
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
bundle exec ruby script/benchmarks/report_workload.rb 100000 20
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
脚本分别输出批次构造、首次统计及重复统计的单调时钟耗时与对象分配数;合成结果的创建不计入测量。比较两个源码版本时,在同一 Ruby 与主机上各运行至少三次。批次构造会复制结果容器并累计状态,重复统计复用计数;不读取报告的调用方也承担构造成本。TFTP 文件核验与逐设备明细仍有线性成本,此基准不测网络吞吐或现场设备性能。
|
|
257
|
+
|
|
258
|
+
## 厂商能力与模板一致性
|
|
259
|
+
|
|
260
|
+
`test/capabilities_test.rb` 离线核对全部已注册平台的能力;`test/vendor_topology_parity_test.rb` 覆盖 Huawei LLDP 简表与接口描述、FortiOS 顶层接口描述/嵌套表隔离,以及 Junos 物理接口和逻辑单元描述。`test/textfsm_test.rb` 同时检查 H3C 两种列序在通用命令解析入口中的语义、内置厂商/命令的完整匹配,以及各配置方言的索引入口。完整性拒绝路径覆盖缺失表头、残缺记录、未闭合接口表/引号、VDOM 作用域不明和没有值的 Junos description。
|
|
261
|
+
|
|
262
|
+
新增样本来自官方结构说明构造的离线会话,尚无设备联机验收;模板解析成功不能证明固件覆盖或候选配置写入安全。
|
data/docs/architecture.md
CHANGED
|
@@ -5,24 +5,32 @@
|
|
|
5
5
|
| 层次 | 职责 | 位置 |
|
|
6
6
|
| --- | --- | --- |
|
|
7
7
|
| 会话引擎 | 登录、传输、命令、脚本及资源所有权 | `engine/` |
|
|
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
11
|
| 设备入口 | 能力组合、会话代理、通用脚本钩子 | `device/base.rb` |
|
|
12
12
|
| 设备档案 | 不可变规则与策略接口校验;Builder 负责声明 DSL | `device/profile.rb`、`device/profile/` |
|
|
13
13
|
| 配置采集与保存 | 采集策略生命周期、完整性校验、显式保存 | `device/running_config.rb`、`device/running_config/`、`device/save_config.rb` |
|
|
14
|
+
| 并发与观察 | 有限并发、后台观察线程的停止与回收 | `concurrency/pool.rb`、`concurrency/observer.rb` |
|
|
15
|
+
| 设备作用域 | 创建连接器、执行任务、关闭并分别交付值与故障 | `device/scope.rb` |
|
|
14
16
|
| 本地备份 | 采集、摘要比较、文件持久化和完成回执 | `device/local_backup.rb` |
|
|
15
17
|
| TFTP | 参数预检、上传、完成证据、目标和回执 | `device/tftp.rb`、`device/tftp/` |
|
|
16
18
|
| 拓扑 | 邻居发现、接口名称/描述、变更计划与分阶段执行 | `device/topology.rb`、`device/topology/` |
|
|
17
19
|
| 厂商差异 | 档案组装及各项能力的厂商策略 | `vendor/<厂商>.rb`、`vendor/<厂商>/` |
|
|
18
20
|
| 文件存储 | 路径锁、安全读取、私有原子写入、离线配置 | `storage.rb`、`storage/` |
|
|
19
21
|
| 解析 | TextFSM 模板选择、记录转换及异常映射 | `textfsm.rb`、`templates/` |
|
|
20
|
-
| 终端文本 | 严格 UTF-8 字节验证和终端渲染 | `engine/
|
|
21
|
-
|
|
|
22
|
+
| 终端文本 | 严格 UTF-8 字节验证和终端渲染 | `engine/terminal/text.rb`、`engine/terminal/renderer.rb` |
|
|
23
|
+
| 清单来源 | 公共字段、预算与错误;HTTP、PostgreSQL 及缓存 | `netdisco/inventory.rb`、`netdisco/inventory/` |
|
|
24
|
+
| 设备接入 | 动态凭据与批次连接策略的合并 | `netdisco/device/access.rb` |
|
|
25
|
+
| 批量编排 | 策略快照、计划校验、工作线程和报告 | `netdisco/` |
|
|
22
26
|
| 命令行入口 | 转交给 CLI,参数处理与业务流程由库负责 | `bin/net-backup` |
|
|
23
27
|
|
|
24
28
|
`Base` 引入 `RunningConfig::Capability`、`LocalBackup::Capability`、`Tftp::Capability`、`Topology::Capability`、`TextFSM::Capability` 及 `SaveConfig`。能力入口与实现共置,Base 不再重复业务包装。配置采集的临时策略绑定也由 RunningConfig 管理;备份与拓扑通过 `device.running_config` 复用同一流程。
|
|
25
29
|
|
|
30
|
+
文件名表达本文件的主要职责,领域归属通过命名空间表达。Netdisco 的清单预算与缓存位于 `inventory/`,分别为 `Inventory::Budget` 和 `Inventory::Cache`;配置操作的批次与逐台结果位于 `operations/`,分别为 `Operations::Batch` 和 `Operations::Outcome`。`Report::Store` 只持久化报告,Fleet 通过 `report_store:` 接收写入器。服务器上传核验与归档分别为 `Tftp::Verifier` 和 `Tftp::Archive`。
|
|
31
|
+
|
|
32
|
+
引擎的 `Command`、`Script`、`Interaction`、`Dialogue`、`Response`、`Command::Result`、`Result` 各有同名文件;传输事件在 `event.rb`,适配器集合在 `transports.rb`。日志的 `RedactingWriter` 与 `Transcript` 位于 `log/` 下各自的同名文件。存储边界使用 `FileReader` 表示受控读取,`AtomicFile` 表示带阶段回执的原子替换;权限和持久性约束仍由这些组件维护。
|
|
33
|
+
|
|
26
34
|
## 公开业务方法与确认范围
|
|
27
35
|
|
|
28
36
|
下表区分执行结果、业务产物和直接抛出的前置错误;调用方不能假设所有失败都在
|
|
@@ -67,9 +75,9 @@ IOS/NX-OS 清理器处理单条配置命令的响应,不把正文当成多命
|
|
|
67
75
|
|
|
68
76
|
Fleet 在规范目标校验、凭据解析及连接器构造之前加同一把锁,再向下层备份授权一次同进程、同 Fiber 的借用。借用在采集前消费,回调递归备份不能重复使用;没有全局路径缓存。路径锁在外、会话锁在内,`Base#backup` 拒绝已有会话操作中的嵌套调用。独立进程必须采用同一 flock 协议;调用方保护目录及祖先,锁不约束不合作的写入者。
|
|
69
77
|
|
|
70
|
-
`
|
|
78
|
+
`FileReader` 对一次 NOFOLLOW/NONBLOCK 打开的 FD 做 fstat 和读取,拒绝非普通文件。`SavedConfig#read/#fingerprint`、离线导出和解析复用该边界;`find` 返回当时已验证的路径,不保证调用方日后重新打开时身份不变。直接备份继续安全替换末级符号链接,Fleet/SavedConfig 则保留拒绝契约。内容不变时再核对目录项身份,避免因路径替换跳过必要写入;正常未变文件保持 mtime。
|
|
71
79
|
|
|
72
|
-
`Storage::
|
|
80
|
+
`Storage::AtomicFile.write` 统一返回 `Receipt`,执行临时文件写入、flush/fsync、rename、父目录 fsync,并跟踪 not_committed/committed/durable 和出错阶段。rename 前失败保留旧文件;之后失败保留新文件。目录 fsync 的 EINVAL/ENOSYS/ENOTSUP/EOPNOTSUPP/NotImplementedError 单独表示不支持;其他错误不能降级为成功。durable 只表示同步调用成功,未变内容不补验过去写入,断电恢复也未在本地测试中验证。
|
|
73
81
|
|
|
74
82
|
提交后的 `BackupPersistenceError` 只携带原 `Backup` 元数据和受控写入回执,Fleet 保留产物并使用已有 saved_with_error 分类。只接受库定义的具体完成错误及匹配产物类型,第三方异常的 backup 属性或自定义 WriteError 子类不作为完成证据。报告保留已提交位置,导出错误说明 committed;设备命令不会自动重放。
|
|
75
83
|
|
|
@@ -111,11 +119,11 @@ PAN-OS 使用候选配置模型。[官方提交说明](https://docs.paloaltonetw
|
|
|
111
119
|
|
|
112
120
|
`Worker` 按线程完成顺序接收终止通知,设备结果仍写入原清单槽位。任一线程中断时,调用方无需等待先创建的慢线程;创建后续线程失败时,也会停止并等待已启动的任务清理资源。普通设备故障继续转换为逐台结果,回调故障单独记录。
|
|
113
121
|
|
|
114
|
-
设备耗时由 CLOCK_MONOTONIC 度量,包含 on_start、设备任务及关闭,截止于 on_result 前。报告的总耗时覆盖目录准备和全部 worker/callback,截止于报告写入前;两者不以 UTC 时间相减。Outcome 的 duration_ms 和 Diagnostic 直接声明为 Data 成员,未测量的耗时为 nil
|
|
122
|
+
设备耗时由 CLOCK_MONOTONIC 度量,包含 on_start、设备任务及关闭,截止于 on_result 前。报告的总耗时覆盖目录准备和全部 worker/callback,截止于报告写入前;两者不以 UTC 时间相减。Outcome 的 duration_ms 和 Diagnostic 直接声明为 Data 成员,未测量的耗时为 nil。Backup 的路径、摘要及采集时间与 Outcome 的审计时间均复制冻结,构造和 `with` 更新遵守同一所有权边界;结果回调不能原地改写已保存文件的证据。`with` 替换审计时间时清除原计时,替换错误字段时清除原诊断;显式提供的新值仍经过验证。
|
|
115
123
|
|
|
116
124
|
Fleet 始终返回 Report,委托 Batch 的执行结果及严格 success?/status,单独提供 policy 和 policy_success?。默认 strict 要求非空且全部成功;selected 必须至少包含一个成功结果,其他状态只能为 filtered/sample_limit,且 callback_errors 为空、report_error 为 nil。部分成功、任何其他跳过和未知状态都阻止策略成功。
|
|
117
125
|
|
|
118
|
-
报告 summary 使用单一 schema_version: 2,包含 policy、policy_success、coverage、单调 duration_ms、逐台 diagnostic 及 report_diagnostic。coverage 仅按已尝试状态计数,失败和部分成功也算尝试。`
|
|
126
|
+
报告 summary 使用单一 schema_version: 2,包含 policy、policy_success、coverage、单调 duration_ms、逐台 diagnostic 及 report_diagnostic。coverage 仅按已尝试状态计数,失败和部分成功也算尝试。`Report::Store#write(report, directory:)` 始终接收 Report。已存文件是写入前快照;写入自身失败时,返回对象与 CLI 携带最终报告故障。`Batch#build_report` 只构造报告,不重跑任务,也不能补出未记录的总耗时。
|
|
119
127
|
|
|
120
128
|
`Report#statistics` 仅计算汇总,字段等同于 `summary.except(:devices)`,供文本与进度输出使用;完整设备明细由 JSON 输出时生成。两者不缓存结果,保留调用方持有 Batch 容器的既有语义。静默进度不读取报告。
|
|
121
129
|
|
|
@@ -123,17 +131,21 @@ Diagnostic 只保存固定词表中的码、类型、阶段和受控产物状态
|
|
|
123
131
|
|
|
124
132
|
`Planner` 先按厂商采样,再按实际 TFTP 文件名排除覆盖冲突,保留采样顺序中的首台设备。未入选设备仍标记为 `sample_limit`,只有入选后目标重名才标记为 `remote_filename_collision`。`Plan#validate!` 校验任务与清单的对应关系;调用方传入或通过 `with` 修改的计划也必须在读取凭据、创建目录及设备 I/O 前通过校验。冲突规则适用于全部厂商,包括不同地址规范化后产生相同文件名的情况。
|
|
125
133
|
|
|
126
|
-
`Settings` 保留动态秘密来源,`snapshot(mode:)` 只复制允许公开的非敏感字段,并在副作用前复用 `Configuration`、`Planner` 和 `
|
|
134
|
+
`Settings` 保留动态秘密来源,`snapshot(mode:)` 只复制允许公开的非敏感字段,并在副作用前复用 `Configuration`、`Planner` 和 `Inventory::HTTP.options` 校验。Fleet 的一次规划/执行只使用该份冻结策略;CLI 通过 `for_run` 将规划与执行绑定到同一策略。每台设备的凭据读取与策略解析分开,自定义凭据解析器给出的显式连接选项仍保留原优先级。带 `plan:` 的执行不再查询清单;以后新建的批次可以读取新策略和新的 API 凭据。
|
|
127
135
|
|
|
128
|
-
`
|
|
136
|
+
`CLI::Backup` 在创建批次目录和读取凭据前固定并验证策略。已固定的 `Settings#with_overrides` 只复制非敏感快照上的覆盖值,不重新读取环境中的策略字段;后续 `snapshot(mode:)` 继续按当前业务模式校验,离线导出不会隐式转成网络备份校验。原凭据来源仍保持动态。
|
|
129
137
|
|
|
130
|
-
`
|
|
138
|
+
`Inventory::HTTP#devices` 每次持有独立 `Inventory::Budget`,认证、分页、QueryRequired 查询共同累计正文字节和去重前记录数,并共享单调时钟 deadline。默认 HTTP 使用 `read_body`,先检查实际接收字节再追加,不信任 Content-Length。每阶段缩短原生连接/读/写超时;总期限还覆盖慢速响应头。期限观察线程只关闭本次拥有的 HTTP 连接,防止 chunked 的收尾读取拖延返回,调用结束即唤醒并 join。默认 HTTP 不做隐式 GET 重试;这一机制不进入设备命令路径。
|
|
131
139
|
|
|
132
140
|
注入的 `requester` 接收 URI 和 request。它返回后才接受长度和期限检查,不在任意用户 Ruby 回调中注入异步异常。解析和集合操作完成后也检查期限,但这不构成任意 CPU 回调的抢占保证。预算失败仅报告错误码与安全类型,没有部分清单、响应正文或底层 cause。HTTPS 的标准证书检查不变;默认允许明文 HTTP,可通过显式策略禁止。
|
|
133
141
|
|
|
134
|
-
`Settings#
|
|
142
|
+
`Settings#inventory` 构建所选清单来源,`Fleet.new(inventory: ...)` 与 `Inventory::Cache.new(inventory: ...)` 接收实现 `devices` 的来源对象。`Settings#inventory_options` 校验其公开策略。
|
|
143
|
+
|
|
144
|
+
`Inventory::Error`、`Inventory::Timeout`、`Inventory::FIELDS` 和 `Inventory.options` 属于清单领域,HTTP、PostgreSQL、缓存及设备记录只依赖这些公共契约。设备接入由 `Device::Access.resolve` 合并动态凭据、固定策略和日志路径,不创建连接,也不保存秘密。
|
|
145
|
+
|
|
146
|
+
`Settings#inventory_source` 选择 HTTP `Inventory::HTTP` 或 PostgreSQL `Inventory::Postgres`,两者只向 Fleet 提供 `devices`,不改变设备映射、计划和执行接口。数据库 SQL 与标量参数由用户配置,不内置业务表名或筛选;结果用列别名适配 `Inventory::FIELDS`,只保留这份现有字段白名单。数据库身份使用 `ND_DB_HOST/NAME/USER/PASS`,每次新建客户端时读取;端口、TLS 和超时来自 YAML 并进入不可变策略快照。
|
|
135
147
|
|
|
136
|
-
`
|
|
148
|
+
`Inventory::Postgres` 每次调用独占 PostgreSQL 连接,在只读事务中用扩展查询协议声明游标,原生解析器拒绝多语句和非查询输入。FETCH 大小及次数复用清单预算,单行模式使 libpq 不缓存整页;每行在追加前计数,额外列也消耗字节预算。驱动必须先解码单行,所以单个超大字段仍可超过 Ruby 预算的瞬时内存。总 deadline 覆盖连接和所有语句,每次 FETCH 前缩短 statement_timeout;正常完成回滚只读事务,异常关闭连接,任何失败均不交付部分清单。NOTICE、原始数据库异常及 cause 不进入日志或 CLI;稳定错误码区分连接失败、查询失败、无效清单和预算超限。SQL/参数可在 show-config 中查看,不能用于传递凭据。
|
|
137
149
|
|
|
138
150
|
数据库集成测试运行 `bundle exec rake test:postgres`,通过 `pg_config --bindir`(或 `NC_TEST_PG_BINDIR`)定位服务端工具。测试只创建临时 SCRAM 数据库和私有 Unix socket,退出时停止并删除;不会读取真实 Netdisco 凭据或连接已有服务。覆盖 SQL/参数、只读限制、真实认证失败、查询中途失败、各项预算、连接回收以及 CLI 计划。
|
|
139
151
|
|
|
@@ -155,9 +167,9 @@ Diagnostic 只保存固定词表中的码、类型、阶段和受控产物状态
|
|
|
155
167
|
任务书 NC-12(协作取消和整批 deadline)为后置可选项,本轮 **deferred**:没有新增
|
|
156
168
|
取消令牌、取消状态或“停止后不派发”的公开保证。现有 Worker 的异常 kill/join、
|
|
157
169
|
任务 ensure 和 Session 超时仍保留;任意用户回调应自行保证返回,不能承诺非合作
|
|
158
|
-
回调的硬期限。清单获取 deadline 只约束
|
|
170
|
+
回调的硬期限。清单获取 deadline 只约束 Inventory::HTTP#devices,不覆盖整个 Fleet 任务。
|
|
159
171
|
|
|
160
|
-
NC-07C 的本地服务器目录核验已接入 `
|
|
172
|
+
NC-07C 的本地服务器目录核验已接入 `Tftp::Verifier`;单设备 API 仍只报告设备回执。
|
|
161
173
|
Fleet 可把回执与本地普通文件的时间、稳定性、摘要及目录项身份关联,生成 server_verified。
|
|
162
174
|
启用历史归档并提供服务器目录时,PAN-OS 固定名可在路径锁内顺序上传、核验和归档;
|
|
163
175
|
其他场景仍拒绝同批固定名碰撞。验证失败不能重传设备命令。
|
|
@@ -225,7 +237,7 @@ Ruby 对象显式拥有资源并使用关键字参数。`Profile` 提供有限
|
|
|
225
237
|
|
|
226
238
|
## 厂商能力
|
|
227
239
|
|
|
228
|
-
`device.supports?(capability)` 读取档案与基于方法的采集命令,不建立传输连接或策略实例。接受 `:running_config`、`:save_config`、`:backup`、`:tftp_backup`、`:neighbors`、`:interface_descriptions`、`:interface_description_changes`;未知名称返回 `false`。它只表明实现了能力,不验证设备授权或固件兼容性。
|
|
240
|
+
`device.supports?(capability)` 读取档案与基于方法的采集命令,不建立传输连接或策略实例。接受 `:running_config`、`:save_config`、`:backup`、`:remote_backup`、`:tftp_backup`、`:neighbors`、`:interface_descriptions`、`:interface_description_changes`;未知名称返回 `false`。它只表明实现了能力,不验证设备授权或固件兼容性。
|
|
229
241
|
|
|
230
242
|
| 厂商 | 采集及本地备份 | 保存 | TFTP | 邻居 | 描述读取 | 描述计划及下发 |
|
|
231
243
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
@@ -235,10 +247,15 @@ Ruby 对象显式拥有资源并使用关键字参数。`Profile` 提供有限
|
|
|
235
247
|
| Cisco NX-OS | 是 | 是 | 是 | 是 | 是 | 是 |
|
|
236
248
|
| Radware Alteon | 是 | 是 | 是 | 否 | 是 | 否 |
|
|
237
249
|
| PAN-OS | 是 | 否 | 是 | 是 | 是 | 否,候选隔离未验证 |
|
|
238
|
-
| 华为 | 是 | 是 | 是 |
|
|
250
|
+
| 华为 VRP | 是 | 是 | 是 | 是,LLDP 简表 | 是 | 否,提交阶段未验证 |
|
|
239
251
|
| 山石 | 是 | 是 | 是 | 是 | 是 | 是 |
|
|
252
|
+
| Topsec TopOS | 是 | 否 | 否 | 否 | 否 | 否 |
|
|
253
|
+
| Fortinet FortiOS | 是 | 是 | 是 | 是 | 是,当前视图顶层接口表 | 否 |
|
|
254
|
+
| Juniper Junos | 是 | 否 | 否,原生 FTP/SCP | 是 | 是 | 否,候选库提交未开放 |
|
|
240
255
|
|
|
241
|
-
`
|
|
256
|
+
Junos 的 `remote_backup` 通过 `RemoteBackup` 协调与厂商策略导出已提交配置;回执区分设备报告与远端核验。其余平台保持明确的能力声明,见[平台说明](vendors.md)。
|
|
257
|
+
|
|
258
|
+
`Profile` DSL 声明静态命令、提示符、交互和策略绑定。配置、TFTP、拓扑规则位于各自的 `vendor/<厂商>/` 下;公共业务负责校验、执行和结果语义。相同规则直接复用:NX-OS 复用 Cisco 拓扑变更流程,并绑定独立的 NX-OS 解析模板,H3C 无线继承 H3C,H3C、华为、Radware 使用公共终端渲染配置策略。目录存在不意味着必须复制一份规则,子类可只覆盖需要变化的绑定。
|
|
242
259
|
|
|
243
260
|
```mermaid
|
|
244
261
|
flowchart LR
|
|
@@ -257,7 +274,7 @@ flowchart LR
|
|
|
257
274
|
require "net/connector"
|
|
258
275
|
require "net/connector/vendor/cisco_ios"
|
|
259
276
|
|
|
260
|
-
class VariantTransfer < Net::Connector::CiscoIos::
|
|
277
|
+
class VariantTransfer < Net::Connector::Vendor::CiscoIos::Tftp
|
|
261
278
|
# 只覆盖该型号需要变化的方法,并遵守 Strategy 接口。
|
|
262
279
|
end
|
|
263
280
|
|
|
@@ -273,7 +290,7 @@ router.supports?(:tftp_backup) # => true,不连接设备
|
|
|
273
290
|
|
|
274
291
|
配置采集可把 `running_config_strategy` 绑定到 `Net::Connector::RunningConfig::Strategy` 子类。策略实现 `clean`、`result_step`、`prompt_text` 和 `validate_response!`;PAN-OS 候选差异只影响配置采集,直接 `execute_command("show config diff")` 返回该命令输出。静态命令列表留在档案中。`clean_config` 和受保护的 `config_result_step` 钩子可覆盖并调用 `super`。Base 负责通用脚本与锁内回调,不决定配置是否完整。
|
|
275
292
|
|
|
276
|
-
拓扑策略通过 `topology_strategy YourStrategy` 绑定。类方法和实例方法 `supports?` 声明三种拓扑能力,其余方法提供命令、模板、输出完整性和接口拼写。Profile 校验完整接口,包括 `validate_descriptions!`、`change_error_code`、`enter_configuration_command`、`leave_configuration_commands`、`verification_commands`、`persistence_commands` 和 `persistence_confirmed?`。只读策略继承默认空阶段;可写策略必须明确每个阶段,不能用合并的收尾命令推断保存边界。即时生效且使用 interface 块的设备可继承 `Topology::ImmediateStrategy`。自定义采集必须执行已列入计划的命令并返回完成步骤;自定义厂商标签通过 `neighbor_template` 提供模板。
|
|
293
|
+
拓扑策略通过 `topology_strategy YourStrategy` 绑定。类方法和实例方法 `supports?` 声明三种拓扑能力,其余方法提供命令、模板、输出完整性和接口拼写。Profile 校验完整接口,包括 `description_key`、`validate_descriptions!`、`change_error_code`、`enter_configuration_command`、`leave_configuration_commands`、`verification_commands`、`persistence_commands` 和 `persistence_confirmed?`。只读策略继承默认空阶段;可写策略必须明确每个阶段,不能用合并的收尾命令推断保存边界。即时生效且使用 interface 块的设备可继承 `Topology::ImmediateStrategy`。自定义采集必须执行已列入计划的命令并返回完成步骤;自定义厂商标签通过 `neighbor_template` 提供模板。
|
|
277
294
|
|
|
278
295
|
业务操作针对单个连接器构造,并提供 `call`。TFTP 策略只处理厂商传输差异,公共操作统一成功和失败规则。生成的文件名经过 `TftpTarget` 校验及长度限制;带作用域的 IPv6 地址会转为安全 ASCII 标记,特别长的地址使用稳定 SHA-256 标记。原本合法的文件名保留拼写,只有整体过长才缩短清单名称。TFTP 返回值表示设备报告上传完成,服务器文件核对仍由调用方负责。
|
|
279
296
|
|
|
@@ -281,7 +298,7 @@ TFTP 策略的类方法 `filename(host, label: nil)` 是必需的无 I/O 命名
|
|
|
281
298
|
|
|
282
299
|
TFTP 策略必须实现 `validate_options!(target, source_file:, vrf:)`、`receipt_metadata(target, source_file:, explicit_source:)`、`resolve_source_file`、`default_path`、`remote_path`、`script` 和 `device_reported_complete?`。可继承 `Tftp::Strategy` 的公共默认值;重写上传流程时必须一并审视继承的参数约束和来源声明。预检不得访问设备,元数据只允许 configuration_kind/source_file/format/requested_path。接口缺失在 Profile 构造时失败,不再动态回退到另一套行为。
|
|
283
300
|
|
|
284
|
-
单设备 TFTP API 返回 device_reported;Fleet 的本地目录核验可提升为 server_verified。`
|
|
301
|
+
单设备 TFTP API 返回 device_reported;Fleet 的本地目录核验可提升为 server_verified。`Tftp::Archive` 在持有目标路径锁后保存旧文件,并记录其 dev、ino、size、mtime、ctime。上传时间取同一服务器目录内临时文件的 mtime,随即清理临时文件,避免文件系统时钟精度低于 Time.now 时误拒绝新上传。核验要求非空文件的新鲜时间及稳定读取;已有文件还必须发生状态变化。重新写入相同内容可以通过,未变化的旧文件即使 mtime 在未来也不能冒充新上传。预创建的服务器目标文件在上传前保持可用,成功归档后按原规则清理;未合作的外部写入者仍不受路径锁约束。
|
|
285
302
|
|
|
286
303
|
验收入口是 `script/ci`,也可用 `bundle exec rake release:check`。它检查源码、可用 Git 历史和 gem 内容中的敏感数据,执行 Ruby 与工作流 lint、完整测试,并在隔离 gem 目录及最小 Bundler 应用中安装。真实本地 PTY 烟测覆盖厂商加载、配置采集、打包模板和 CLI,不接触网络设备。初次下载依赖和工具需要联网,详见[验证文档](VERIFICATION.md)及[发布文档](RELEASING.md)。
|
|
287
304
|
|
|
@@ -303,17 +320,21 @@ TFTP 策略必须实现 `validate_options!(target, source_file:, vrf:)`、`recei
|
|
|
303
320
|
|
|
304
321
|
## 加载入口与厂商策略
|
|
305
322
|
|
|
306
|
-
公开设备 `open` 与批量任务共用 `
|
|
323
|
+
公开设备 `open` 与批量任务共用 `Device::Scope` 的关闭边界。PTY、SSH 信任快照、日志及本地文件在取得资源与登记所有权之间延后异步取消,关闭时保护回收过程;设备命令、文件读取和锁竞争仍可取消。清理保留首个故障,二次取消不能替换主错误或形成循环异常原因。
|
|
307
324
|
|
|
308
325
|
`require "net/connector"` 加载设备 API 和引擎,厂商由 `vendor_class` 按需加载。TextFSM 能力模块可预先组合,但外部 `textfsm` gem 只在实际解析时加载。仅需会话引擎时使用 `require "net/connector/engine/core"`;离线文件操作使用 `net/connector/storage`,不加载厂商。
|
|
309
326
|
|
|
310
|
-
`engine/dialogue`
|
|
327
|
+
`engine/interaction`、`engine/dialogue`、`engine/response` 分别定义交互规则、对话语法与响应值;读取循环位于 `engine/response_reader`。`engine/errors` 不加载脱敏运行时,`engine/redactor` 负责秘密作用域。Session 明确加载并持有读取器与脱敏器。
|
|
311
328
|
|
|
312
|
-
Netdisco 的 Settings、Planner、Fleet、CLI 等文件各自声明实际依赖,单独 require 后即可调用,不依赖 `netdisco.rb` 的预设加载顺序。总入口只组合 CLI 和
|
|
329
|
+
Netdisco 的 Settings、Planner、Fleet、CLI 等文件各自声明实际依赖,单独 require 后即可调用,不依赖 `netdisco.rb` 的预设加载顺序。总入口只组合 CLI 和 CLI::Backup;Batch 通过 autoload 创建 Report,避免两者循环加载。内部清单预算 `Inventory::Budget` 由 Inventory 清单适配器加载。Progress 的终端显示、Fleet 的设备执行、Report 的结果解释和 Storage 的文件持久化保持独立职责。
|
|
313
330
|
|
|
314
331
|
公共策略位于 `RunningConfig::Strategy` / `Rendered`、`Tftp::Strategy` / `FileUpload`、`Topology::Strategy` / `ImmediateStrategy`。厂商文件直接依赖公共策略,由 Profile 绑定;没有反向加载和厂商转发别名。
|
|
315
332
|
|
|
316
|
-
`
|
|
333
|
+
`Vendor` 持有平台注册表并按需加载平台类。平台本身就是连接器,例如 `Vendor::CiscoIos`,不再套一层同名 Connector。IOS 与 NX-OS 继承 `Vendor::Cisco` 的运行配置采集、CDP 拓扑和复制确认规则;各平台分别声明提示符、分页、诊断及保存命令;NX-OS 拓扑子类绑定独立的 CDP、接口配置模板。NX-OS 不再保留空的配置清理子类。H3C Wireless 继续继承 H3C 的 Comware 流程;Huawei 与 H3C 的视图和特权语义独立,不因部分命令相似而共用整份档案。
|
|
334
|
+
|
|
335
|
+
TFTP 策略的 `script` 统一执行预检并构造有界命令;平台通过私有 `transfer_command` 和 `transfer_interactions` 提供实际命令与确认应答。源文件发现、设备完成行、文件种类和服务器证据仍分别核验,不将不同平台的成功文本合并为宽松的通用匹配。模板按平台与命令组织,详见 [模板目录](templates.md)。
|
|
336
|
+
|
|
337
|
+
`Terminal::Text.utf8` 在副本上验证设备或文件的原始字节,`Terminal::Text.render` 再使用严格终端渲染。Ruby 源码默认 UTF-8 不决定 PTY 或 `File.binread` 返回的编码。非法原始字节不能因控制符擦除而通过检查;渲染损坏多字节字符时同样拒绝,错误为不含正文或 cause 的 `invalid_output_encoding`。拓扑在表头匹配和计数前复用验证,TextFSM 每次解析创建独立 Parser。日志允许转义非法字节,原始备份不转码。
|
|
317
338
|
|
|
318
339
|
### Ruby 实现与扩展边界
|
|
319
340
|
|
|
@@ -330,31 +351,18 @@ RunningConfig 的两处受控 `send` 保留,用于私有策略绑定与 protec
|
|
|
330
351
|
|
|
331
352
|
项目在开发阶段只维护当前接口,不保留旧路径转发或旧方法别名。
|
|
332
353
|
|
|
333
|
-
|
|
|
354
|
+
| 领域 | 当前类型与位置 |
|
|
334
355
|
| --- | --- |
|
|
335
|
-
|
|
|
336
|
-
|
|
|
337
|
-
|
|
|
338
|
-
|
|
|
339
|
-
|
|
|
340
|
-
|
|
|
341
|
-
|
|
|
342
|
-
|
|
|
343
|
-
|
|
|
344
|
-
|
|
|
345
|
-
| `execute`、引擎 `query` / `exchange` | `execute_command` |
|
|
346
|
-
| `run`、引擎 `execute` 脚本入口 | `execute_script` |
|
|
347
|
-
| `collect_config` | `running_config` |
|
|
348
|
-
| `tftp_backup_receipt`、三字段 `TftpBackup` | `tftp_backup` 返回 `TftpReceipt`;实际目标为 `path`;完成后错误通过 `receipt` 取回 |
|
|
349
|
-
| `PrivateFile.write_receipt` | `Storage::PrivateFile.write` 返回持久化回执 |
|
|
350
|
-
| `report_schema`、`--report-schema` | 删除版本选择;Fleet 始终返回 Report,summary 固定 schema 2 |
|
|
351
|
-
| `Batch#report`、`Device#connector` | `build_report`、`build_connector` |
|
|
352
|
-
| `Settings#credentials_for` | `device_credentials_for`;非敏感设置由批次 Policy 单独提供 |
|
|
353
|
-
| `check_response` | `validate_response!` |
|
|
354
|
-
| 拓扑 `enter_configuration` / `leave_configuration` | `enter_configuration_command` / `leave_configuration_commands` |
|
|
355
|
-
| TFTP `source_file` / `complete?` | `resolve_source_file` / `device_reported_complete?` |
|
|
356
|
-
| `command_scope` / `output_scope` | `with_command_redaction` / `with_sensitive_output` |
|
|
357
|
-
| `LegacyIndex` 和旧名称文件查找 | 只读取规范 `<IP>.txt`,无目录扫描和迁移分支 |
|
|
356
|
+
| 终端数据 | `Terminal::Text`、`Terminal::Renderer`,位于 `engine/terminal/` |
|
|
357
|
+
| 命令结果 | `Command::Result`,位于 `engine/command/result.rb`;脚本汇总仍为 `Result` |
|
|
358
|
+
| 安全诊断词表 | `Error::Metadata`,位于 `engine/error/metadata.rb` |
|
|
359
|
+
| 清单预算与缓存 | `Netdisco::Inventory::Budget/Cache`,位于 `netdisco/inventory/` |
|
|
360
|
+
| 配置操作结果 | `Netdisco::Operations::Batch/Outcome`,位于 `netdisco/operations/` |
|
|
361
|
+
| 报告存储 | `Netdisco::Report::Store`,位于 `netdisco/report/store.rb`,通过 `report_store:` 注入 |
|
|
362
|
+
| 服务器上传证据 | `Netdisco::Tftp::Verifier/Archive`,位于 `netdisco/tftp/` |
|
|
363
|
+
| 厂商平台 | `Vendor::CiscoIos` 等平台类直接继承 Base 或协议家族类;策略位于平台类下 |
|
|
364
|
+
| 文件边界 | `Storage::FileReader` 与 `Storage::AtomicFile`,分别负责受控读取与原子替换 |
|
|
365
|
+
| 终端应用 | `Netdisco::CLI::Backup/Credentials/Options`,位于 `netdisco/cli/` |
|
|
358
366
|
|
|
359
367
|
工厂方法用 `build_*` 表明返回新对象;抛错校验使用 `validate_*!`;块作用域使用明确的 `with_*`。单命令、脚本、命令文本构造和完成证据判断分别命名,避免从通用的 execute/check/complete 猜测行为。`legacy_ssh` 表示设备 SSH 协议协商选项,Netdisco QueryRequired 则是服务端 API 行为,两者均独立于本库旧版本接口。
|
|
360
368
|
|
|
@@ -386,13 +394,13 @@ RunningConfig 的两处受控 `send` 保留,用于私有策略绑定与 protec
|
|
|
386
394
|
|
|
387
395
|
批量备份只用规范化管理地址命名为 `<IP>.txt`,IPv6 的 `:` 改为 `_`。清单名称保留在结果元数据及 TFTP 文件名中。`Storage::SavedConfig` 只读取该规范路径,不扫描其他名称;缺失时 required 查找或读取抛出 ENOENT,`find(required: false)` 返回 nil。
|
|
388
396
|
|
|
389
|
-
新建规范文件时报告 created,之后只与同一路径的内容比较。设备改名不会改变身份或基线,其他名称文件不会被读取、迁移或覆盖。Fleet 在凭据解析和连接前检查规范目标,拒绝符号链接和非普通文件;持有路径锁直至备份结束。`
|
|
397
|
+
新建规范文件时报告 created,之后只与同一路径的内容比较。设备改名不会改变身份或基线,其他名称文件不会被读取、迁移或覆盖。Fleet 在凭据解析和连接前检查规范目标,拒绝符号链接和非普通文件;持有路径锁直至备份结束。`FileReader` 在同一 FD 上完成检查和读取;它与 flock 均不能阻止不合作的外部写入者,目录保护仍由调用方负责。
|
|
390
398
|
|
|
391
399
|
## 日志组件与事件
|
|
392
400
|
|
|
393
401
|
`Log` 管理日志资源、级别和上下文;`Log::Event` 冻结已脱敏的标量字段;`Log::Formatter` 提供标准 Logger 文本格式;`Log::Transcript` 以行组织设备回显,`Log::RedactingWriter` 将分片直接交给 Expect::Redactor。日志模块不再实现另一套秘密匹配算法。
|
|
394
402
|
|
|
395
|
-
连接、命令和操作失败统一经过 `Log#log_failure`。错误类型、错误码和阶段由 `
|
|
403
|
+
连接、命令和操作失败统一经过 `Log#log_failure`。错误类型、错误码和阶段由 `Error::Metadata` 的固定词表过滤,Netdisco 报告复用同一规则;未知错误码不输出,未知阶段使用引擎当前阶段,未知类型归为 StandardError。异常正文仍先由会话脱敏,防止配置钩子把未登记的配置放进错误元数据后进入日志。
|
|
396
404
|
|
|
397
405
|
操作后处理无论抛出异常还是返回失败 Result,都在敏感作用域退出前归一化错误,再记录完成事件;Result 的已完成步骤及配置内容保持原样。最终处理继承实际执行的命令及交互敏感性,包括准备钩子、替换命令和追加查询;只累计标记,不长期保留秘密,也不影响后续普通命令的日志。
|
|
398
406
|
|
|
@@ -408,7 +416,7 @@ RunningConfig 的两处受控 `send` 保留,用于私有策略绑定与 protec
|
|
|
408
416
|
|
|
409
417
|
注入的 logger 由调用方持有;连接器不修改级别、formatter、progname,也不关闭它。每次写入同时检查配置与 logger 当前级别。消息对象支持 to_s/inspect 和安全 to_h,应用可自行输出 JSON。自有文本日志使用毫秒时间及逐行事件;raw 文件只写经过敏感保护的字节。上下文退出前先收尾渲染与脱敏缓冲,避免上一命令尾部被标成下一条命令。
|
|
410
418
|
|
|
411
|
-
|
|
419
|
+
终端进度显示由 Progress 单独管理:终端 write/flush 的普通故障会停用后续显示,并只保留固定词表内的错误类型,不携带原始异常。心跳照常回收,设备任务、events.jsonl 与报告持久化继续进行;CLI::Backup 将显示故障合并为一次批次 callback_errors。若首次故障发生在报告保存后的最终显示阶段,仅补写一次报告诊断,最终 JSON 与退出码采用更新后的策略结果;已有报告错误保持优先,不重放设备备份或事件。
|
|
412
420
|
|
|
413
421
|
`log_file` 使用 NOFOLLOW/NONBLOCK 打开,再对同一 FD 检查普通文件类型,随后才修改权限或写入。FIFO、设备文件和目录不能用作日志文件;应用拥有的输出流通过 `logger` 接入。日志打开先于登录,不能依赖登录超时为文件打开提供期限。
|
|
414
422
|
|
|
@@ -416,14 +424,14 @@ RunningConfig 的两处受控 `send` 保留,用于私有策略绑定与 protec
|
|
|
416
424
|
|
|
417
425
|
应用代码优先使用 `Net::Connector.open/build`、`Base` 的公开设备方法、`Command`、`Script`、结果对象,以及 `Netdisco::Fleet`。厂商扩展使用 `Profile` DSL 和文档中列出的策略接口;`Profile::Builder`、`Session` 的状态字段、工作线程调度及解析辅助方法属于内部实现。扩展直接加载当前设备模块或厂商目录下的类。项目仍处于 0.x,公开契约的变更会在更新记录中说明。
|
|
418
426
|
|
|
419
|
-
gem 不依赖 Rails,也不自动创建数据库模型或管理应用的连接池。`logger: Rails.logger` 由应用持有,连接器写入安全事件对象,不修改或关闭它。数据库报告通过 `
|
|
427
|
+
gem 不依赖 Rails,也不自动创建数据库模型或管理应用的连接池。`logger: Rails.logger` 由应用持有,连接器写入安全事件对象,不修改或关闭它。数据库报告通过 `Report::Store::Database` 注入仓储,写报告发生在调用线程中;逐设备凭据解析、连接器工厂和回调则运行在工作线程中。
|
|
420
428
|
|
|
421
429
|
在 Rails 内调用涉及模型、自动加载或应用状态的线程代码时,应由应用用 `Rails.application.executor.wrap` 包住相应调用。仅在外层包住 `fleet.backup_all` 不会覆盖子线程。例如,凭据解析器可以写为:
|
|
422
430
|
|
|
423
431
|
```ruby
|
|
424
432
|
credentials = lambda do |device|
|
|
425
433
|
Rails.application.executor.wrap do
|
|
426
|
-
DeviceCredential.for_host(device.host).
|
|
434
|
+
DeviceCredential.for_host(device.host).connection_options
|
|
427
435
|
end
|
|
428
436
|
end
|
|
429
437
|
```
|
|
@@ -432,10 +440,22 @@ end
|
|
|
432
440
|
|
|
433
441
|
## 公共并发、配置批次与清单缓存
|
|
434
442
|
|
|
435
|
-
`
|
|
443
|
+
`Concurrency::Pool` 只持有线程、启动闸门和完成事件,按输入顺序交付块结果。普通设备失败如何转成结果由业务适配负责;创建并登记线程时延迟异步中断,登记完成后恢复,启动失败不执行任何设备块。工作线程捕获异常并由调用线程同步传播,避免宿主全局线程策略影响无关线程。原 `Netdisco::Worker` 保留备份计时、结果槽位和通知适配,不再自行管理线程。
|
|
444
|
+
|
|
445
|
+
`Device::Scope` 持有单台任务连接器,分别交付业务值与普通故障,收尾不覆盖先前错误或中断。`Netdisco::Operations` 为配置脚本与接口描述同步、规划/应用提供全批预检、固定策略、动态凭据和安全摘要;它保留领域 `Result`、计划和完成步骤,使用独立 `Operations::Batch`,不借用备份路径或上传状态。应用审批计划必须走原拓扑重验、变更、回读、保存流程;有限并发不代表跨设备事务或自动重试安全。
|
|
446
|
+
|
|
447
|
+
`Device::Access` 为备份与配置操作统一解析逐设备连接设置:每台设备重新读取凭据,自定义设置覆盖本批固定连接策略,批次日志目录决定日志路径。它不缓存凭据或创建连接;缺少凭据返回 nil,调用入口保留各自的结果分类。`CLI::Credentials` 负责清单客户端与凭据来源的组装,两者不共享设备会话生命周期。
|
|
448
|
+
|
|
449
|
+
`Settings#inventory` 为原始清单适配器包裹 `Inventory::Cache`,默认三天有效。缓存仅保存完整白名单 rows,Rules 和重复地址判定每次重做。身份、SQL 及绑定参数只参与摘要分区,不写入文件;凭据缺失仍在缓存读取前拒绝。过期刷新在有界、按身份的跨进程文件锁内完成,成功后原子替换;失败使用原时间的已验证历史,成功空清单替换旧值。最新查询成功与本地缓存持久化成功是两个事实,不能因写盘失败回退旧数据或重复查询。强刷跳过有效期,不跳过身份、限额、文件校验和失败回退边界。
|
|
450
|
+
|
|
451
|
+
后台心跳和 HTTP 超时观察使用内部 `Concurrency::Observer` 作用域,前台保留主业务值和原始故障;观察线程的异常同步交付所有者,不受宿主全局线程异常策略影响。创建至登记期间延后中断,停止和等待期间保证线程回收;心跳普通故障转换为显示诊断,正常 HTTP 请求的未知观察故障仍传播。
|
|
452
|
+
|
|
453
|
+
## 领域值与更新边界
|
|
454
|
+
|
|
455
|
+
`Topology::Neighbor`、`Topology::Change`、`Topology::Plan` 各有同名文件,构造时复制并冻结自己拥有的字符串与容器。不可变子对象可在单设备、批次和结果之间共享;调用方的原始容器与格式化回调返回的字符串仍归调用方所有。计划构造校验数据形状,执行边界继续负责地址规范化、命令预检、现场证据重验及读回。
|
|
436
456
|
|
|
437
|
-
`
|
|
457
|
+
`Netdisco::Batch` 复制结果数组和回调诊断,在构造时累计状态计数;`counts` 返回可修改的独立 Hash。未知状态不能满足成功策略。更新结果集合须使用 `with`,它重新计算计数。上述领域值和远程备份目标、回执的 `with` 显式经过构造函数,避免依赖不同 Ruby 版本中原生 `Data#with` 的初始化行为。
|
|
438
458
|
|
|
439
|
-
`
|
|
459
|
+
拓扑的 `InterfaceBlocks` 模块集中核对 interface 块与解析记录的完整性,由立即生效的变更策略和 Huawei 的只读策略共同组合,读取能力不必继承写入流程。`description_key(row)` 把描述记录映射为领域身份;Junos 逻辑单元使用 `<物理接口>.<单元号>`,保留物理接口描述。FortiOS 读取当前视图顶层 `config system interface`,接口 `description` 与显示 `alias` 分开处理,嵌套 IPv6 表不产生接口记录;无法确认作用域、闭合或记录数量时拒绝结果。
|
|
440
460
|
|
|
441
|
-
|
|
461
|
+
`Topology::QuotedText` 是描述字段的纯解码边界,不接触设备会话;厂商策略选择无引号文本的处理规则,解析异常统一携带 host、parse 阶段和 unrecognized_output 代码,不携带字段原文或底层异常 cause。配置模板保留完整带引号字段供业务层解码,不能先删外层引号而留下未还原的转义字符。
|
data/docs/netdisco.md
CHANGED
|
@@ -59,7 +59,7 @@ Expired inventory refreshes automatically. A remote inventory failure falls back
|
|
|
59
59
|
|
|
60
60
|
An empty or malformed cache file is a cache miss and triggers a remote query. It cannot supply fallback history if that query fails.
|
|
61
61
|
|
|
62
|
-
Use `net-backup --refresh-inventory`, `fleet.devices(force_refresh: true)`, or `fleet.plan_backup(force_refresh: true)` to bypass freshness. TFTP planning accepts the same keyword. A forced refresh also falls back to valid history if the remote request fails. `cache_enabled: false` disables caching. Injected
|
|
62
|
+
Use `net-backup --refresh-inventory`, `fleet.devices(force_refresh: true)`, or `fleet.plan_backup(force_refresh: true)` to bypass freshness. TFTP planning accepts the same keyword. A forced refresh also falls back to valid history if the remote request fails. `cache_enabled: false` disables caching. Injected inventory sources stay uncached unless explicitly wrapped in `Netdisco::Inventory::Cache`; shared wrapper status describes the latest call, so synchronize concurrent access to the same instance. Cache locks bound waiting and coalesce ordinary expired-cache refreshes across processes; independently requested forced refreshes still query the source.
|
|
63
63
|
|
|
64
64
|
## CLI planning and execution
|
|
65
65
|
|
|
@@ -74,6 +74,10 @@ net-backup --config config.yml --export 192.0.2.7 --output ./exports/device.cfg
|
|
|
74
74
|
|
|
75
75
|
The installed command is `net-backup`; source checkouts use `bundle exec bin/net-backup`. `--show-config` validates public settings without querying inventory. `--plan` queries and validates inventory without connecting devices. `--host` selects one address and fails if absent. Local CLI backups select all ready devices by default; TFTP selects at most five per vendor unless `--all` is given. `--limit-per-vendor` accepts 1 through 5.
|
|
76
76
|
|
|
77
|
+
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.
|
|
78
|
+
|
|
79
|
+
Interactive batch examples use `CLI::Backup`; `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/`.
|
|
80
|
+
|
|
77
81
|
`--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.
|
|
78
82
|
|
|
79
83
|
Plans and results are JSON. Exit codes:
|
|
@@ -110,7 +114,7 @@ Fleet returns `Netdisco::Report`, delegating raw execution fields to `report.bat
|
|
|
110
114
|
|
|
111
115
|
Local backups compare SHA-256. `on_change:` fires for created or changed artifacts; `on_start:` and `on_result:` observe attempted tasks. Callback faults appear in `callback_errors`. Saved artifacts with later errors remain `saved_with_error`; confirmed uploads with later errors remain `reported_with_error`. Neither result permits automatic replay.
|
|
112
116
|
|
|
113
|
-
`
|
|
117
|
+
`Report::Store::Json` writes private JSON by default. Use `Report::Store::Database.new(repository: YourModel)` with a `create!(attributes)` repository, or `report_store: nil` to persist yourself. Custom stores receive `write(report, directory:)`. Report errors retain known committed locations and prevent success.
|
|
114
118
|
|
|
115
119
|
## Configuration batches and interface changes
|
|
116
120
|
|
|
@@ -132,13 +136,13 @@ Scripts accept `Script`, command arrays or multiline text. The full batch is par
|
|
|
132
136
|
|
|
133
137
|
Interface application calls the existing topology operation, retaining evidence revalidation, configuration readback and persistence confirmation. It does not execute an approved plan as an ordinary script. Both write operations require `confirmed: true`, and never automatically retry commands. Each task owns and closes its connector. Ordinary failures remain per-device outcomes; `Interrupt` stops and joins all owned workers. Confirmed work is not rolled back across devices.
|
|
134
138
|
|
|
135
|
-
`
|
|
139
|
+
`Operations::Batch` is separate from backup reports. `outcomes` retain the original `Result` or plan, completed steps and the first failure; `:partial` preserves work followed by an error, and missing credentials prevent success. `plans` selects only fully successful planning results. `to_h` includes safe diagnostics and counts, excluding commands, outputs, credentials and evidence. `on_start` and `on_result` run on workers and must be thread safe; ordinary callback faults are recorded without repeating device operations. Logging uses the configured `ssh.log_directory`.
|
|
136
140
|
|
|
137
141
|
`sync_interface_descriptions` is the direct update entry: one request and one connection per device, from discovery through readback and save. Matching descriptions cause no writes or saves; a no-op does not confirm an earlier save. The default format is `To_<peer name>_<interface abbreviation>`; formatting options and blocks remain backend choices. Separate preview/approval workflows remain available through `plan_interface_descriptions` and `apply_interface_descriptions(plans: planned.plans, confirmed: true)`.
|
|
138
142
|
|
|
139
143
|
Each device publishes one immutable outcome after execution and connection cleanup. Ordinary credential, connection, execution and cleanup failures affect that device while active and queued siblings continue. Once scheduling completes, `summary` and `to_h[:summary]` report `total`, `succeeded` and `failed`, with total = succeeded + failed. Total counts the devices requested and scheduled in this batch; partial completion and missing credentials count as failures and also appear in `partial` and `missing_credentials`. `callback_errors` counts notification faults separately without changing device outcomes. Empty batches report zero counts and `no_devices`. The library returns the report; callers choose terminal output or JSON export. Whole-batch approval validation and interruption retain their existing boundaries, and completed device changes cannot be rolled back.
|
|
140
144
|
|
|
141
|
-
For other independent tasks, load `net/connector/
|
|
145
|
+
For other independent tasks, load `net/connector/concurrency/pool` and use `Net::Connector::Concurrency::Pool.new(concurrency: 4).map(items) { |item| ... }`. Results preserve input order, all threads must start before any block runs, and uncaught exceptions stop the pool and propagate to its caller. Business code decides which ordinary errors become per-task results.
|
|
142
146
|
|
|
143
147
|
## PostgreSQL inventory
|
|
144
148
|
|
|
@@ -179,7 +183,7 @@ Default budgets cover authentication, pagination, fallback queries, or database
|
|
|
179
183
|
|
|
180
184
|
HTTP counts actual chunks and closes on excess. Injected `requester:` callbacks are checked only after returning; their blocking and memory belong to the caller. PostgreSQL counts column names and values, including discarded fields, after libpq decodes each row; a huge field may still consume driver memory before the check. Connection, query, and FETCH share the deadline and use database statement timeouts.
|
|
181
185
|
|
|
182
|
-
`Settings#snapshot(mode:)` freezes public policy before side effects. CLI planning and execution share it; later calls can read new settings. Credentials remain dynamic per task. `Settings.new.for_run(mode: :backup)` shares policy across API calls; non-sensitive overrides preserve an existing snapshot. A custom `credentials:` resolver can supply per-device connection options, taking precedence over defaults. Injected
|
|
186
|
+
`Settings#snapshot(mode:)` freezes public policy before side effects. CLI planning and execution share it; later calls can read new settings. Credentials remain dynamic per task. `Settings.new.for_run(mode: :backup)` shares policy across API calls; non-sensitive overrides preserve an existing snapshot. A custom `credentials:` resolver can supply per-device connection options, taking precedence over defaults. Injected inventory sources remain caller-owned.
|
|
183
187
|
|
|
184
188
|
Snapshot backup, cache, log, TFTP verification, SSH known-hosts and explicit PostgreSQL CA certificate paths resolve against the working directory captured when the snapshot is created, including default paths and later relative overrides. Paths beginning with `~/` and the path `~` use the home directory captured by that snapshot. PostgreSQL CA paths retain libpq's literal path semantics without tilde expansion; `sslrootcert: system` keeps its system trust-store meaning, while `./system` names a file. Live settings continue to resolve paths against the current working directory and home directory.
|
|
185
189
|
|