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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -65
- data/README.md +88 -95
- data/docs/VERIFICATION.md +2 -1
- data/docs/architecture.md +144 -248
- data/lib/net/connector/device/base.rb +1 -0
- data/lib/net/connector/device/interface_description.rb +3 -0
- data/lib/net/connector/device/profile/builder.rb +236 -0
- data/lib/net/connector/device/profile.rb +2 -227
- data/lib/net/connector/device/running_config/strategy.rb +5 -0
- data/lib/net/connector/device/running_config.rb +5 -0
- data/lib/net/connector/engine/dialogue.rb +1 -0
- data/lib/net/connector/engine/errors.rb +2 -0
- data/lib/net/connector/engine/log.rb +13 -9
- data/lib/net/connector/engine/session.rb +1 -0
- data/lib/net/connector/netdisco/cli.rb +20 -20
- data/lib/net/connector/netdisco/device.rb +4 -8
- data/lib/net/connector/netdisco/fleet.rb +2 -48
- data/lib/net/connector/netdisco/plan.rb +77 -0
- data/lib/net/connector/netdisco/planner.rb +12 -23
- data/lib/net/connector/netdisco/worker.rb +31 -24
- data/lib/net/connector/operations/tftp/file_upload.rb +42 -0
- data/lib/net/connector/operations/tftp/strategy.rb +9 -11
- data/lib/net/connector/operations/topology/strategy.rb +14 -0
- data/lib/net/connector/operations/topology.rb +1 -0
- data/lib/net/connector/vendor/cisco_ios/running_config.rb +1 -0
- data/lib/net/connector/vendor/cisco_ios/topology.rb +9 -0
- data/lib/net/connector/vendor/cisco_nxos/running_config.rb +1 -0
- data/lib/net/connector/vendor/h3c/tftp_backup.rb +2 -18
- data/lib/net/connector/vendor/h3c/topology.rb +9 -0
- data/lib/net/connector/vendor/hillstone/running_config.rb +1 -0
- data/lib/net/connector/vendor/hillstone/tftp_backup.rb +4 -0
- data/lib/net/connector/vendor/hillstone/topology.rb +9 -0
- data/lib/net/connector/vendor/huawei/tftp_backup.rb +2 -18
- data/lib/net/connector/vendor/palo_alto/running_config.rb +4 -0
- data/lib/net/connector/vendor/palo_alto/tftp_backup.rb +7 -5
- data/lib/net/connector/vendor/palo_alto/topology.rb +10 -0
- data/lib/net/connector/vendor/radware/tftp_backup.rb +2 -2
- data/lib/net/connector/vendor/radware/topology.rb +2 -0
- data/lib/net/connector/version.rb +1 -1
- metadata +26 -24
data/docs/architecture.md
CHANGED
|
@@ -1,148 +1,102 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Running configuration is a basic device capability: `device/` owns the facade,
|
|
7
|
-
immutable profile, and shared collection flow. `Net::Connector::Operations`
|
|
8
|
-
groups optional workflows over that device: backup, parsing, neighbor discovery,
|
|
9
|
-
and interface description plans. Saved-file export is a local operation and
|
|
10
|
-
does not open a device session.
|
|
11
|
-
|
|
12
|
-
| Layer | Responsibility | Location |
|
|
1
|
+
# 设备操作架构
|
|
2
|
+
|
|
3
|
+
一个公开连接器对象对应一台设备的一段会话,负责连接状态、命令执行和厂商档案。调用方仍使用 `device.running_config`、`device.backup(path:)`、`device.tftp_backup(...)`。运行配置是设备的基本能力,因此设备入口、不可变档案和公共采集流程放在 `device/`。`Operations` 承载备份、解析、邻居发现和接口描述计划等业务;已有文件的导出只读取本地备份,不建立设备连接。
|
|
4
|
+
|
|
5
|
+
| 层次 | 职责 | 位置 |
|
|
13
6
|
| --- | --- | --- |
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
| TextFSM
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
| TFTP
|
|
26
|
-
| TFTP
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
`
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
The complete recheck/write/readback sequence holds a session operation lease.
|
|
59
|
-
Sequential scripts from its owning thread and Fiber may execute; other callers,
|
|
60
|
-
close attempts and reentrant script callbacks receive `SessionBusy`. This lease
|
|
61
|
-
is local to one connector instance, not a device-side or cross-process lock.
|
|
62
|
-
`Fleet` keeps skipped, failed, successful, and saved-with-close-error outcomes
|
|
63
|
-
distinct; callback and report errors remain visible without discarding device
|
|
64
|
-
results. Neighbor table headers alone can establish an empty table, but unknown
|
|
65
|
-
or partly parsed rows cannot establish a complete discovery result. Diagnostics
|
|
66
|
-
are checked against both original output and terminal-rendered text so that
|
|
67
|
-
color sequences cannot hide failures and carriage returns cannot erase them.
|
|
68
|
-
Plan revalidation compares the complete discovered neighbor, including chassis
|
|
69
|
-
ID when available, without changing the public evidence hash shape. PAN-OS
|
|
70
|
-
configuration collection preserves multiline quoted values, but the interface
|
|
71
|
-
description template supports single-line comments only; incomplete quoted
|
|
72
|
-
comments raise `ParsingError` instead of becoming truncated plan evidence.
|
|
73
|
-
|
|
74
|
-
## Expect semantics and Ruby boundaries
|
|
75
|
-
|
|
76
|
-
The [Tcl Expect manual](https://core.tcl-lang.org/expect/doc/trunk/expect.man)
|
|
77
|
-
defines ordered matching, `exp_continue -continue_timer`, buffer consumption,
|
|
78
|
-
EOF, and separate close/wait responsibilities. Its
|
|
79
|
-
[matching loop](https://github.com/tcltk-depot/expect/blob/main/expect.c)
|
|
80
|
-
keeps the deadline when `EXP_CONTINUE_TIMER` is returned; its
|
|
81
|
-
[process handling](https://github.com/tcltk-depot/expect/blob/main/exp_command.c)
|
|
82
|
-
waits for the owned child and retries interrupted waits. These are useful
|
|
83
|
-
reference semantics; `net-connector` is a device operations library, not a Tcl
|
|
84
|
-
interpreter or a complete Expect API port.
|
|
85
|
-
|
|
86
|
-
| Concern | Connector contract | Owner |
|
|
7
|
+
| 设备会话 | 登录、命令交互、脚本和日志 | `engine/` |
|
|
8
|
+
| 设备 | 公开入口、不可变档案、配置采集和接口命名 | `device/` |
|
|
9
|
+
| 厂商组装 | 提示符、命令、交互、策略绑定及会话钩子 | `vendor/<厂商>.rb` |
|
|
10
|
+
| 公共配置采集 | 执行、完整性判断和旧钩子适配 | `device/running_config.rb`、`device/running_config/strategy.rb` |
|
|
11
|
+
| 厂商配置采集 | 清理文本、切换视图和检查响应 | `vendor/<厂商>/running_config.rb` |
|
|
12
|
+
| 接口文本 | 名称匹配、简称、描述和公共接口视图命令 | `device/interface_name.rb`、`device/interface_description.rb` |
|
|
13
|
+
| TextFSM 解析 | 选择模板,将命令或配置转为记录 | `operations/parse_output.rb`、`templates/` |
|
|
14
|
+
| 拓扑与描述计划 | 读取邻居及旧描述、生成命令、重验后下发 | `operations/topology.rb` |
|
|
15
|
+
| 本地备份 | 采集、比较哈希、原子保存并报告变化 | `operations/local_backup.rb` |
|
|
16
|
+
| 已存配置导出 | 不访问清单或设备,读取已有备份 | `operations/saved_config.rb` |
|
|
17
|
+
| 私有文件写入 | 以 `0600` 权限原子替换文件 | `operations/private_file.rb` |
|
|
18
|
+
| TFTP 备份 | 校验目标、执行导出、核对成功证据 | `operations/tftp_backup.rb` |
|
|
19
|
+
| TFTP 厂商策略 | 命令、提示、源文件、成功证据和目标名称 | `vendor/<厂商>/tftp_backup.rb` |
|
|
20
|
+
| 拓扑厂商策略 | 发现命令、解析证据、配置视图及特殊命令 | `vendor/<厂商>/topology.rb` |
|
|
21
|
+
| 清单规划 | 选择就绪设备、限制厂商数量、记录跳过原因 | `netdisco/planner.rb` |
|
|
22
|
+
| 清单计划 | 保存不可变快照,校验任务槽位、跳过原因和目标冲突 | `netdisco/plan.rb` |
|
|
23
|
+
| 批量执行 | 分派独立设备任务,隔离回调故障 | `netdisco/worker.rb` |
|
|
24
|
+
| 批量结果 | 汇总设备状态并保留报告故障 | `netdisco/batch.rb` |
|
|
25
|
+
| 设备集合 | 读取清单、执行本地或 TFTP 任务、写报告 | `netdisco/fleet.rb` |
|
|
26
|
+
| 设置 | 读取环境变量及 YAML 覆盖项 | `netdisco/settings.rb` |
|
|
27
|
+
|
|
28
|
+
## 业务约束
|
|
29
|
+
|
|
30
|
+
连接器独占一个会话。`Session` 串行执行登录和脚本,失败时关闭传输,不自动重放设备命令。`Result` 在后续步骤失败时仍保存已完成步骤。`RunningConfig` 每次采集创建一个新策略,同一策略负责响应检查、结果选择和清理。选择与清理在会话锁内经过现有设备钩子,子类覆盖后可调用 `super`。清理失败时也会解除临时绑定,其他 Fiber 的离线清理不能借用该策略。
|
|
31
|
+
|
|
32
|
+
采集命令匹配当前会话的完整提示符行,不以末尾单个 `#`、`>` 或 `]` 判断完成。PAN-OS 切换视图时保留已认证的设备身份。缺少最终提示符会使采集失败,旧备份保持不变;只有提示符或命令回显的响应属于 `:incomplete_configuration`,不是成功的空配置。PAN-OS 在 `show` 前后都检查候选配置差异。
|
|
33
|
+
|
|
34
|
+
`LocalBackup` 先采集再替换私有文件。TFTP 只确认设备报告的上传结果:策略去掉命令、应答和提示符回显后,检查明确的完成行;文件名或表示“即将上传”的进度文字不算成功。原始输出或终端渲染文本中的失败证据优先于成功文字。
|
|
35
|
+
|
|
36
|
+
`Topology` 读取邻居和旧描述,冻结计划,要求显式确认,下发前重验全部证据,重建命令,并在执行后回读。重验、下发、回读期间持有会话操作租约;只有所属线程和 Fiber 能顺序执行脚本,其他调用、关闭请求和命令回调中的重入均返回 `SessionBusy`。租约只保护一个连接器实例,不是设备端或跨进程锁。
|
|
37
|
+
|
|
38
|
+
`Session` 使用 `Mutex#try_lock`,让竞争调用立即失败而不等待长时间设备命令。租约同时记录线程与 Fiber;同线程的另一个 Fiber 不能借用。`@performing` 还阻止命令回调嵌套执行脚本。可重入锁本身无法区分“租约内顺序执行脚本”和“命令回调嵌套执行脚本”;替换锁实现时仍须保留这层业务判断。
|
|
39
|
+
|
|
40
|
+
`Fleet` 区分跳过、失败、成功和保存成功但关闭失败的结果;回调和报告故障不会丢弃设备结果。邻居表头可以证明空表,但未知或部分解析的数据行不能证明完整发现。诊断同时检查原始输出与终端渲染文本,防止颜色控制符隐藏错误或回车覆盖失败信息。计划重验比较完整邻居身份,包括可用的机箱 ID,公开证据哈希结构不变。PAN-OS 配置采集保留引号内的多行文本;接口描述模板只支持单行备注,未闭合引号会返回 `ParsingError`。
|
|
41
|
+
|
|
42
|
+
`Worker` 按线程完成顺序接收终止通知,设备结果仍写入原清单槽位。任一线程中断时,调用方无需等待先创建的慢线程;创建后续线程失败时,也会停止并等待已启动的任务清理资源。普通设备故障继续转换为逐台结果,回调故障单独记录。
|
|
43
|
+
|
|
44
|
+
`Planner` 先按厂商采样,再按实际 TFTP 文件名排除覆盖冲突,保留采样顺序中的首台设备。未入选设备仍标记为 `sample_limit`,只有入选后目标重名才标记为 `remote_filename_collision`。`Plan#validate!` 校验任务与清单的对应关系;调用方传入或通过 `with` 修改的计划也必须在读取凭据、创建目录及设备 I/O 前通过校验。冲突规则适用于全部厂商,包括不同地址规范化后产生相同文件名的情况。
|
|
45
|
+
|
|
46
|
+
## Expect 语义与 Ruby 边界
|
|
47
|
+
|
|
48
|
+
[Tcl Expect 手册](https://core.tcl-lang.org/expect/doc/trunk/expect.man)定义有序匹配、`exp_continue -continue_timer`、缓冲区消费、EOF,以及分离的关闭和等待职责。[匹配循环](https://github.com/tcltk-depot/expect/blob/main/expect.c)在继续匹配时保留截止时间,[进程处理](https://github.com/tcltk-depot/expect/blob/main/exp_command.c)负责等待子进程并重试中断。这些是参考语义;`net-connector` 是设备操作库,不实现 Tcl 解释器或完整 Expect API。
|
|
49
|
+
|
|
50
|
+
| 关注点 | 连接器约束 | 负责对象 |
|
|
87
51
|
| --- | --- | --- |
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
| EOF |
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
`Profile` provides finite declarations; strategies contain vendor behavior.
|
|
106
|
-
Private guards use direct names such as `check_block!` and
|
|
107
|
-
`check_change_support!`. Public methods, vendor hooks, result objects, and CLI
|
|
108
|
-
JSON fields remain stable. There is no runtime method injection or workflow DSL.
|
|
109
|
-
|
|
110
|
-
## Vendor capabilities
|
|
111
|
-
|
|
112
|
-
`device.supports?(capability)` reads the profile and method-based collection
|
|
113
|
-
commands without opening a transport or constructing a strategy. Accepted
|
|
114
|
-
capabilities are `:running_config`, `:save_config`, `:backup`, `:tftp_backup`,
|
|
115
|
-
`:neighbors`, `:interface_descriptions`, and
|
|
116
|
-
`:interface_description_changes`; unknown names return `false`. This reports
|
|
117
|
-
implementation support, not device authorization or firmware compatibility.
|
|
118
|
-
|
|
119
|
-
| Vendor | Collect / local backup | Save | TFTP | Neighbors | Descriptions | Plan and apply descriptions |
|
|
52
|
+
| 匹配 | 先识别连接失败,再处理交互,最后匹配提示符;提示符匹配必须消费字节 | `ResponseReader` |
|
|
53
|
+
| 时间 | 命令写入和提示应答共用单调时钟截止时间,进度与分页不延长它 | `Session`、`ResponseReader` |
|
|
54
|
+
| 缓冲区 | 输出上限由 `max_output_bytes` 控制,流式匹配保留 32 KiB 未匹配尾部 | `Transports::Pty`、`ResponseReader` |
|
|
55
|
+
| EOF | 返回 `ConnectionClosed`,保留先前步骤,关闭会话 | `ResponseReader`、`Execution`、`Session` |
|
|
56
|
+
| 资源 | 通过 `expect-pty#hard_close` 管理子进程;设置或刷新失败仍释放日志文件 | `Transports::Pty`、`Log` |
|
|
57
|
+
| 人工交互 | 人工接管结束自动会话,后续操作需重新连接 | `Session` |
|
|
58
|
+
| 并发 | 单个脚本或多脚本操作独占会话,回调不能重入;独立设备由限量线程池执行 | `Session`、`Netdisco::Worker` |
|
|
59
|
+
|
|
60
|
+
提示符和交互标记是 Ruby 正则表达式,应短到能放入未匹配尾部;流式适配器不支持需要无限长历史的模式。收集输出的上限与匹配窗口上限不同。终端渲染器只处理常见行编辑控制符,不是完整屏幕终端模拟器。`Profile#terminal_size` 使用 `[宽, 高]`,PTY 适配器转换为 Ruby 的 `[行, 列]`。
|
|
61
|
+
|
|
62
|
+
Ruby 对象显式拥有资源并使用关键字参数。`Profile` 提供有限声明入口,厂商策略负责差异行为。公开方法、厂商钩子、结果对象和 CLI JSON 字段延续现有契约;内部不做运行时方法注入,也没有工作流 DSL。
|
|
63
|
+
|
|
64
|
+
## 厂商能力
|
|
65
|
+
|
|
66
|
+
`device.supports?(capability)` 读取档案与基于方法的采集命令,不建立传输连接或策略实例。接受 `:running_config`、`:save_config`、`:backup`、`:tftp_backup`、`:neighbors`、`:interface_descriptions`、`:interface_description_changes`;未知名称返回 `false`。它只表明实现了能力,不验证设备授权或固件兼容性。
|
|
67
|
+
|
|
68
|
+
| 厂商 | 采集及本地备份 | 保存 | TFTP | 邻居 | 描述读取 | 描述计划及下发 |
|
|
120
69
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
121
|
-
| H3C |
|
|
122
|
-
| H3C
|
|
123
|
-
| Cisco IOS / IOS XE |
|
|
124
|
-
| Cisco NX-OS |
|
|
125
|
-
| Radware Alteon |
|
|
126
|
-
| PAN-OS |
|
|
127
|
-
|
|
|
128
|
-
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
70
|
+
| H3C | 是 | 是 | 是 | 是 | 是 | 是 |
|
|
71
|
+
| H3C 无线 | 是 | 是 | 是 | 是 | 是 | 是 |
|
|
72
|
+
| Cisco IOS / IOS XE | 是 | 是 | 是 | 是 | 是 | 是 |
|
|
73
|
+
| Cisco NX-OS | 是 | 是 | 是 | 是 | 是 | 是 |
|
|
74
|
+
| Radware Alteon | 是 | 是 | 是 | 否 | 是 | 否 |
|
|
75
|
+
| PAN-OS | 是 | 否 | 是 | 是 | 是 | 是 |
|
|
76
|
+
| 华为 | 是 | 是 | 是 | 否 | 否 | 否 |
|
|
77
|
+
| 山石 | 是 | 是 | 是 | 是 | 是 | 是 |
|
|
78
|
+
|
|
79
|
+
`Profile` DSL 声明静态命令、提示符、交互和策略绑定。配置、TFTP、拓扑规则位于各自的 `vendor/<厂商>/` 下;公共业务负责校验、执行和结果语义。相同规则直接复用:NX-OS 使用 IOS 拓扑规则,H3C 无线继承 H3C,H3C、华为、Radware 使用公共终端渲染配置策略。目录存在不意味着必须复制一份规则,子类可只覆盖需要变化的绑定。
|
|
80
|
+
|
|
81
|
+
```mermaid
|
|
82
|
+
flowchart LR
|
|
83
|
+
A[厂商 profile 声明块] --> B[Profile.define]
|
|
84
|
+
B --> C[Profile::Builder]
|
|
85
|
+
C --> D[命令、提示符、分页和策略字段]
|
|
86
|
+
D --> E[Builder#build]
|
|
87
|
+
E --> F[Profile.new 校验并冻结]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`device/profile.rb` 中的 `Profile` 是不可变值对象;`device/profile/builder.rb` 保存可变声明 DSL 及字段构造器。`Profile.define` 复制父档案,运行一次厂商声明块,再由 `build` 校验并冻结新档案。子类覆盖规则不会污染父类。TFTP 或拓扑绑定设为 `nil` 会关闭对应能力;配置采集绑定为 `nil` 时使用默认策略,空采集命令列表则关闭采集。
|
|
91
|
+
|
|
92
|
+
例如,型号变体只替换 TFTP 流程:
|
|
139
93
|
|
|
140
94
|
```ruby
|
|
141
95
|
require "net/connector"
|
|
142
96
|
require "net/connector/vendor/cisco_ios"
|
|
143
97
|
|
|
144
98
|
class VariantTransfer < Net::Connector::CiscoIos::TftpBackup
|
|
145
|
-
#
|
|
99
|
+
# 只覆盖该型号需要变化的方法,并遵守 Strategy 接口。
|
|
146
100
|
end
|
|
147
101
|
|
|
148
102
|
class VariantRouter < Net::Connector.vendor_class(:cisco_ios)
|
|
@@ -152,117 +106,59 @@ class VariantRouter < Net::Connector.vendor_class(:cisco_ios)
|
|
|
152
106
|
end
|
|
153
107
|
|
|
154
108
|
router = VariantRouter.new(host: "192.0.2.10", username: "operator")
|
|
155
|
-
router.supports?(:tftp_backup) # => true
|
|
109
|
+
router.supports?(:tftp_backup) # => true,不连接设备
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
配置采集可把 `running_config_strategy` 绑定到 `Net::Connector::RunningConfig::Strategy` 子类。策略定义清理、结果步骤、视图提示符和逐响应检查。PAN-OS 候选差异只影响配置采集;直接 `execute("show config diff")` 仍返回该命令输出。静态命令列表留在档案中。已有的 `collect_config`、`clean_config` 和受保护的 `config_result_step` 钩子仍可覆盖并调用 `super`。`Base` 负责通用脚本与锁内回调,不决定配置是否完整。
|
|
113
|
+
|
|
114
|
+
拓扑策略通过 `topology_strategy YourStrategy` 绑定。类方法 `supports?` 声明三种拓扑能力,实例方法提供命令、模板、输出完整性和接口拼写。厂商使用自定义清单标签时,还须通过 `neighbor_template` 提供 TextFSM 模板,因为内置索引只匹配已有厂商键。确认、证据复核及回读仍由公共 `Topology` 完成。
|
|
115
|
+
|
|
116
|
+
业务操作针对单个连接器构造,并提供 `call`。TFTP 策略只处理厂商传输差异,公共操作统一成功和失败规则。生成的文件名经过 `TftpTarget` 校验及长度限制;带作用域的 IPv6 地址会转为安全 ASCII 标记,特别长的地址使用稳定 SHA-256 标记。原本合法的文件名保留拼写,只有整体过长才缩短清单名称。TFTP 返回值表示设备报告上传完成,服务器文件核对仍由调用方负责。
|
|
117
|
+
|
|
118
|
+
TFTP 策略的类方法 `filename(host, label: nil)` 是不产生 I/O 的命名接口,默认使用 `file_extension` 声明的扩展名。Netdisco 只清理清单名称,不再维护厂商扩展名或固定文件名的分支。Radware 和山石分别声明 `tgz`、`dat`,PAN-OS 返回固定名称。旧自定义策略未实现该类方法时仍使用通用 `cfg` 名称。H3C、华为继承公共 `Tftp::FileUpload`,共用上传脚本、默认源文件名和完成证据;各自只负责取得并校验源文件。
|
|
119
|
+
|
|
120
|
+
验收入口是 `script/ci`,也可用 `bundle exec rake release:check`。它检查源码、可用 Git 历史和 gem 内容中的敏感数据,执行 Ruby 与工作流 lint、完整测试,并在隔离 gem 目录及最小 Bundler 应用中安装。真实本地 PTY 烟测覆盖厂商加载、配置采集、打包模板和 CLI,不接触网络设备。初次下载依赖和工具需要联网,详见[验证文档](VERIFICATION.md)及[发布文档](RELEASING.md)。
|
|
121
|
+
|
|
122
|
+
## 加载与兼容路径
|
|
123
|
+
|
|
124
|
+
`require "net/connector"` 只加载设备 API 和引擎,不预先加载厂商规则或 TextFSM。每个厂商入口只组装自身规则和公共父类;解析操作按需加载。更底层的调用方可用 `require "net/connector/engine/core"`,不加载设备定义、业务操作或厂商规则。`engine/base`、`engine/profile`、`engine` 保留为旧入口的转发路径。
|
|
125
|
+
|
|
126
|
+
旧的 `Operations::RunningConfig`、`Operations::RunningConfig::<Vendor>`、`Operations::Tftp::<Vendor>`、`Operations::Topology::<Vendor>` 常量和 require 路径都转发到同一实现类,不维护两份逻辑。已有公开结果常量也通过 autoload 保留。新增厂商代码应直接使用厂商目录下的类。
|
|
127
|
+
|
|
128
|
+
## 接口描述规则
|
|
129
|
+
|
|
130
|
+
原始 `Neighbor` 字段和计划证据完整保留发现值。`InterfaceName.key` 用于匹配本机接口别名与运行配置名称;`InterfaceName.configuration` 保留配置命令的接口展开规则。`InterfaceName.short` 只决定描述里对端接口的显示形式,绝不替换本机下发命令中的接口名。
|
|
131
|
+
|
|
132
|
+
`InterfaceDescription.format(neighbor, abbreviate: true, lowercase: false)` 是各厂商默认计划共用的纯函数,生成 `To <名称> <接口>`。已知接口族会缩写,并保留原有大小写:Ethernet/Eth 为 Eth,GigabitEthernet/GE/Gi 为 Gi,Ten-GigabitEthernet/TenGigabitEthernet/XGE/Te 为 Te,FastEthernet/Fa 为 Fa,port-channel/Po 为 Po。端口编号与子接口后缀保留。`ge-0/0/1`、`100GE1/0/1`、`Port 12` 等未知形式默认不变;这是有限映射,不声称识别全部厂商命名。
|
|
133
|
+
|
|
134
|
+
默认建议因此从 `To peer Ethernet1/2` 变为 `To peer Eth1/2`。`abbreviate: false` 保留原拼写,`lowercase: true` 仅将对端接口改为小写。自定义代码块拿到原始邻居,可生成完整描述。输出仍必须经过 80 字节及字符校验、证据复核、确认和回读。
|
|
135
|
+
|
|
136
|
+
`InterfaceDescription.commands(interface:, description:, leave: "exit")` 为 IOS/NX-OS 和山石分别生成 `interface`、`description`、退出命令;H3C 使用 `leave: "quit"`。进入配置视图和保存配置仍由厂商负责。PAN-OS 保留 `set network interface ... comment`、`commit` 及延长的提交超时。公共命令构造器不连接设备,也不能绕开已审核的拓扑计划。
|
|
137
|
+
|
|
138
|
+
## 敏感命令的生命周期
|
|
139
|
+
|
|
140
|
+
一个临时脱敏范围贯穿命令准备、收发、厂商后处理、用户回调和错误归一化。后续查询复用外层范围,`ensure` 在结束时清除临时秘密。敏感错误保留类型、代码、阶段和已完成步骤,但隐藏可能包含部分秘密的消息、输出及底层回溯。显式结果数据仍是原始数据。直接与流式脱敏先匹配真实秘密,包括含字面量 `[REDACTED]` 的秘密,再保留已有标记。
|
|
141
|
+
|
|
142
|
+
## 本地备份标识与旧文件
|
|
143
|
+
|
|
144
|
+
批量备份只用规范化管理地址命名为 `<IP>.txt`,IPv6 的 `:` 改为 `_`。清单名称仍保留在结果元数据及 TFTP 文件名中。`SavedConfig` 优先使用规范文件;缺失时只接受唯一的旧版 `<名称>-<IP>.txt`。匹配多个旧文件时明确失败,不按修改时间随意选择。
|
|
145
|
+
|
|
146
|
+
首次成功的规范备份会与唯一旧文件的哈希比较,旧文件保持不变,并报告 `changed` 或 `unchanged`。采集失败不会创建规范文件。连接设备前先拒绝符号链接和非普通文件。旧文件存在歧义时,应保留原件,由操作人员核对后把当前正确配置放到规范路径,再恢复备份或导出。
|
|
147
|
+
|
|
148
|
+
## 公开契约与 Rails 接入
|
|
149
|
+
|
|
150
|
+
应用代码优先使用 `Net::Connector.open/build`、`Base` 的公开设备方法、`Command`、`Script`、结果对象,以及 `Netdisco::Fleet`。厂商扩展使用 `Profile` DSL 和文档中列出的策略接口;`Profile::Builder`、`Session` 的状态字段、工作线程调度及解析辅助方法属于内部实现。兼容 require 路径只转发到当前实现,新增扩展使用厂商目录下的类。项目仍处于 0.x,公开契约的变更会在更新记录中说明。
|
|
151
|
+
|
|
152
|
+
gem 不依赖 Rails,也不自动创建数据库模型或管理应用的连接池。`logger: Rails.logger` 由应用持有,连接器只追加设备标记,不修改或关闭它。数据库报告通过 `ResultStore::Database` 注入仓储,写报告发生在调用线程中;逐设备凭据解析、连接器工厂和回调则运行在工作线程中。
|
|
153
|
+
|
|
154
|
+
在 Rails 内调用涉及模型、自动加载或应用状态的线程代码时,应由应用用 `Rails.application.executor.wrap` 包住相应调用。仅在外层包住 `fleet.backup_all` 不会覆盖子线程。例如,凭据解析器可以写为:
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
credentials = lambda do |device|
|
|
158
|
+
Rails.application.executor.wrap do
|
|
159
|
+
DeviceCredential.for_host(device.host).connection_settings
|
|
160
|
+
end
|
|
161
|
+
end
|
|
156
162
|
```
|
|
157
163
|
|
|
158
|
-
|
|
159
|
-
`Net::Connector::RunningConfig::Strategy`. The strategy defines cleaning, result-step
|
|
160
|
-
selection, expected view transitions and per-response validation. PAN-OS candidate
|
|
161
|
-
diff checks apply only to collection; a direct `execute("show config diff")` still
|
|
162
|
-
returns the requested output. Static command lists stay in the profile. The
|
|
163
|
-
facade's `collect_config`, `clean_config` and protected `config_result_step` hooks
|
|
164
|
-
remain available for existing subclasses. `Base` runs generic scripts and locked
|
|
165
|
-
callbacks; it does not decide whether a configuration is complete.
|
|
166
|
-
|
|
167
|
-
For topology, bind a subclass of `Operations::Topology::Strategy` (or an
|
|
168
|
-
existing vendor strategy) with `topology_strategy YourStrategy`. Its static
|
|
169
|
-
`supports?` declares which of the three topology operations it implements;
|
|
170
|
-
the instance methods provide commands, templates, output completeness, and
|
|
171
|
-
interface spelling. A vendor using a custom inventory label must also provide
|
|
172
|
-
a TextFSM template through `neighbor_template`, because the bundled index
|
|
173
|
-
matches the existing vendor keys. Keep confirmation, evidence checks, and
|
|
174
|
-
readback in `Topology`.
|
|
175
|
-
|
|
176
|
-
Device operations are constructed for one connector and have a `call` method. TFTP
|
|
177
|
-
strategies contain only device-specific transfer behavior; the operation owns
|
|
178
|
-
the shared success and failure rules. Each vendor binds its strategies in its
|
|
179
|
-
profile, so adding a backup operation does not add methods to every vendor
|
|
180
|
-
connector. Generated TFTP filenames share `TftpTarget` validation and length
|
|
181
|
-
limits. Scoped IPv6 addresses become safe ASCII filename tokens; an unusually
|
|
182
|
-
long address uses a stable SHA-256 token. Existing valid filenames retain their
|
|
183
|
-
spelling, and inventory labels are shortened only when the complete name would
|
|
184
|
-
exceed the target limit.
|
|
185
|
-
|
|
186
|
-
The facade keeps the existing device API while allowing batch workers and
|
|
187
|
-
single-device callers to share the same operations. The TFTP result means the
|
|
188
|
-
device reported upload completion; checking the server file remains the
|
|
189
|
-
caller's responsibility.
|
|
190
|
-
|
|
191
|
-
The acceptance gate is `script/ci` (also `bundle exec rake release:check`). It
|
|
192
|
-
checks source, available Git history and gem contents for sensitive data; runs
|
|
193
|
-
Ruby and workflow lint and the full test suite; and installs the gem into both
|
|
194
|
-
an isolated gem home and a minimal Bundler application. A local PTY smoke checks
|
|
195
|
-
vendor loading, configuration collection, packaged TextFSM templates and the CLI.
|
|
196
|
-
RuboCop checks Ruby lint, security, whitespace, frozen string comments and the
|
|
197
|
-
project's double-quoted string convention. No real device is contacted. Initial
|
|
198
|
-
dependency and tool downloads require internet access; see
|
|
199
|
-
[verification](VERIFICATION.md) and [release instructions](RELEASING.md).
|
|
200
|
-
|
|
201
|
-
## Loading and compatibility
|
|
202
|
-
|
|
203
|
-
`require "net/connector"` loads the device API and engine, without loading vendor
|
|
204
|
-
rules or TextFSM. Each vendor entry point assembles only its own rules and shared
|
|
205
|
-
parents. Parsing loads when a parsing or topology workflow is used. Lower-level
|
|
206
|
-
callers can load `net/connector/engine/core` without device definitions, business
|
|
207
|
-
operations or vendor rules. `engine/base`, `engine/profile`, and `engine` remain
|
|
208
|
-
forwarding entry points for existing callers.
|
|
209
|
-
|
|
210
|
-
Old `Operations::RunningConfig`, `Operations::RunningConfig::<Vendor>`,
|
|
211
|
-
`Operations::Tftp::<Vendor>` and `Operations::Topology::<Vendor>` constants and
|
|
212
|
-
require paths forward to the same classes, rather than maintaining duplicate
|
|
213
|
-
implementations. Existing public result constants also remain available through
|
|
214
|
-
autoload. New vendor code uses the vendor-owned classes directly.
|
|
215
|
-
|
|
216
|
-
## Interface description policy
|
|
217
|
-
|
|
218
|
-
Raw `Neighbor` fields and plan evidence retain the exact discovered values.
|
|
219
|
-
`InterfaceName.key` matches local aliases to running configuration names;
|
|
220
|
-
`InterfaceName.configuration` preserves the established CLI expansion rules.
|
|
221
|
-
`InterfaceName.short` is exclusively a display policy for the remote port in a
|
|
222
|
-
description. It never replaces the local interface used in commands.
|
|
223
|
-
|
|
224
|
-
`InterfaceDescription.format(neighbor, abbreviate: true, lowercase: false)` is
|
|
225
|
-
shared by every vendor's default plan. It retains the neighbor device name and
|
|
226
|
-
produces `To <name> <port>`. Known families shorten as follows, preserving lower,
|
|
227
|
-
upper or initial-capital case: Ethernet/Eth → Eth, GigabitEthernet/GE/Gi → Gi,
|
|
228
|
-
Ten-GigabitEthernet/TenGigabitEthernet/XGE/Te → Te, FastEthernet/Fa → Fa, and
|
|
229
|
-
port-channel/Po → Po. Port numbers and subinterface suffixes stay intact. Unknown
|
|
230
|
-
forms such as `ge-0/0/1`, `100GE1/0/1` and `Port 12` stay unchanged by default;
|
|
231
|
-
this is a finite mapping, not a claim to recognize all vendors or interface types.
|
|
232
|
-
|
|
233
|
-
This changes the default proposal from `To peer Ethernet1/2` to
|
|
234
|
-
`To peer Eth1/2`. Use `plan_interface_descriptions(abbreviate: false)` to keep the
|
|
235
|
-
previous spelling, or `lowercase: true` to lowercase only the remote interface.
|
|
236
|
-
An explicit formatter block receives the original neighbor and controls the
|
|
237
|
-
complete description. All outputs still pass the same 80-byte and character
|
|
238
|
-
validation, evidence recheck, confirmation and readback requirements.
|
|
239
|
-
|
|
240
|
-
`InterfaceDescription.commands(interface:, description:, leave: "exit")` builds
|
|
241
|
-
separate `interface`, `description` and exit commands for IOS/NX-OS and Hillstone;
|
|
242
|
-
H3C supplies `leave: "quit"`. Entering configuration mode and saving remain
|
|
243
|
-
vendor responsibilities. PAN-OS retains `set network interface ... comment`,
|
|
244
|
-
`commit`, and its extended commit timeout. The common builder is pure: it does
|
|
245
|
-
not send commands or bypass the reviewed topology plan.
|
|
246
|
-
|
|
247
|
-
## Sensitive command lifetime
|
|
248
|
-
|
|
249
|
-
One temporary redaction scope spans preparation, exchange, vendor postprocessing,
|
|
250
|
-
user callbacks and error normalization. Follow-up queries share the enclosing
|
|
251
|
-
scope; `ensure` removes temporary secrets afterward. Sensitive errors preserve
|
|
252
|
-
type, code, phase and completed steps but hide arbitrary messages, output and
|
|
253
|
-
underlying backtraces that could contain partial secrets. Explicit result data
|
|
254
|
-
remains raw. Direct and streaming redaction prioritize actual secrets, including
|
|
255
|
-
secrets containing the literal `[REDACTED]`, before preserving existing markers.
|
|
256
|
-
|
|
257
|
-
## Local backup identity and legacy files
|
|
258
|
-
|
|
259
|
-
Fleet backups use the normalized management IP alone (`<IP>.txt`, with IPv6 `:`
|
|
260
|
-
replaced by `_`). Inventory names remain result metadata and TFTP labels.
|
|
261
|
-
`SavedConfig` prefers this canonical file. If absent, it accepts exactly one
|
|
262
|
-
legacy `<name>-<IP>.txt` file; multiple matches fail rather than choosing by mtime.
|
|
263
|
-
The first successful canonical backup compares against the unique legacy digest,
|
|
264
|
-
retains the legacy file, and reports `changed` or `unchanged` accordingly. Failed
|
|
265
|
-
collection creates no canonical file. Symlinks and non-regular candidates are
|
|
266
|
-
rejected before connecting. Existing ambiguous legacy sets need operator review:
|
|
267
|
-
retain the originals and place the verified current configuration at the canonical
|
|
268
|
-
path before resuming backup/export.
|
|
164
|
+
这里的 `DeviceCredential` 是应用自己的模型。涉及 Rails 的工厂、回调或自定义连接器方法也遵循同一规则;Rails 的 Executor 负责应用代码的执行边界及数据库连接回收。不要在工作线程中使用 Reloader 包住整批任务。需要每台设备独立重试和调度时,可在应用的 Active Job 中调用单设备 API。具体规则见 [Rails 线程与代码执行指南](https://guides.rubyonrails.org/threading_and_code_execution.html)。
|
|
@@ -6,6 +6,7 @@ module Net
|
|
|
6
6
|
module Connector
|
|
7
7
|
# 描述文本与常见接口视图命令的纯构造方法,不连接或修改设备。
|
|
8
8
|
module InterfaceDescription
|
|
9
|
+
# 将邻居名称和接口拼成默认描述;只缩写邻居接口,保留原始大小写。
|
|
9
10
|
def self.format(neighbor, abbreviate: true, lowercase: false)
|
|
10
11
|
port = if abbreviate
|
|
11
12
|
InterfaceName.short(neighbor.neighbor_interface, lowercase: lowercase)
|
|
@@ -15,6 +16,7 @@ module Net
|
|
|
15
16
|
validate!("To #{neighbor.neighbor_name} #{port}")
|
|
16
17
|
end
|
|
17
18
|
|
|
19
|
+
# 生成进入接口、设置描述和退出视图的命令,不执行设备操作。
|
|
18
20
|
def self.commands(interface:, description:, leave: "exit")
|
|
19
21
|
validate_interface!(interface)
|
|
20
22
|
validate!(description)
|
|
@@ -31,6 +33,7 @@ module Net
|
|
|
31
33
|
raise ArgumentError, "description must be 1-80 bytes of plain interface text"
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
# 拒绝可能把描述命令拆成多条命令的不安全接口名。
|
|
34
37
|
def self.validate_interface!(value)
|
|
35
38
|
return value if value.is_a?(String) && value.match?(/\A[A-Za-z][A-Za-z0-9.\/-]*\z/)
|
|
36
39
|
|