netstack 0.1.1 → 0.2.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 +22 -3
- data/{LICENSE.txt → LICENSE} +7 -6
- data/NOTICE +25 -0
- data/README.md +58 -20
- data/docs/acceptance.md +92 -0
- data/docs/backups.md +100 -0
- data/docs/configuration.md +198 -0
- data/docs/connector.md +187 -0
- data/docs/ddi.md +142 -0
- data/docs/deployment.md +47 -0
- data/docs/foundations.md +45 -0
- data/docs/implementation.md +68 -0
- data/docs/inventory.md +42 -0
- data/docs/monitoring.md +227 -0
- data/docs/motor_integration.md +96 -0
- data/docs/performance.md +39 -0
- data/docs/release.md +20 -0
- data/docs/vxlan.md +91 -0
- data/docs/wireless.md +81 -0
- data/lib/netstack/backups/artifact.rb +23 -0
- data/lib/netstack/backups/diff.rb +15 -0
- data/lib/netstack/backups/repository.rb +77 -0
- data/lib/netstack/backups/request.rb +78 -0
- data/lib/netstack/circuits/business_calendar.rb +51 -0
- data/lib/netstack/circuits/capacity_review.rb +191 -0
- data/lib/netstack/circuits/endpoint.rb +25 -0
- data/lib/netstack/circuits/monitoring_plan.rb +212 -0
- data/lib/netstack/configuration/artifact.rb +85 -0
- data/lib/netstack/configuration/artifact_resolver.rb +30 -0
- data/lib/netstack/configuration/bootstrap/payload.rb +67 -0
- data/lib/netstack/configuration/bootstrap/validator.rb +224 -0
- data/lib/netstack/configuration/catalog.rb +60 -0
- data/lib/netstack/configuration/generator.rb +87 -0
- data/lib/netstack/configuration/input_contract.rb +82 -0
- data/lib/netstack/configuration/payload.rb +168 -0
- data/lib/netstack/configuration/reader.rb +124 -0
- data/lib/netstack/configuration/support.rb +30 -0
- data/lib/netstack/configuration/system_configuration.rb +56 -0
- data/lib/netstack/configuration/template_manager.rb +174 -0
- data/lib/netstack/configuration/template_store.rb +40 -0
- data/lib/netstack/configuration/validator.rb +566 -0
- data/lib/netstack/connector/authentication.rb +77 -0
- data/lib/netstack/connector/authentication_error.rb +11 -0
- data/lib/netstack/connector/backup.rb +19 -0
- data/lib/netstack/connector/backup_busy.rb +11 -0
- data/lib/netstack/connector/backup_persistence_error.rb +32 -0
- data/lib/netstack/connector/command.rb +63 -0
- data/lib/netstack/connector/command_result.rb +24 -0
- data/lib/netstack/connector/command_timeout.rb +11 -0
- data/lib/netstack/connector/configuration.rb +138 -0
- data/lib/netstack/connector/connection_closed.rb +11 -0
- data/lib/netstack/connector/connection_error.rb +11 -0
- data/lib/netstack/connector/device.rb +284 -0
- data/lib/netstack/connector/device_error.rb +11 -0
- data/lib/netstack/connector/dialogue.rb +55 -0
- data/lib/netstack/connector/error.rb +44 -0
- data/lib/netstack/connector/error_metadata.rb +58 -0
- data/lib/netstack/connector/event.rb +24 -0
- data/lib/netstack/connector/execution.rb +110 -0
- data/lib/netstack/connector/interaction.rb +55 -0
- data/lib/netstack/connector/internal_error.rb +11 -0
- data/lib/netstack/connector/known_hosts.rb +128 -0
- data/lib/netstack/connector/local_backup.rb +63 -0
- data/lib/netstack/connector/log/event.rb +65 -0
- data/lib/netstack/connector/log/formatter.rb +18 -0
- data/lib/netstack/connector/log/messages.rb +54 -0
- data/lib/netstack/connector/log/redacting_writer.rb +33 -0
- data/lib/netstack/connector/log/transcript.rb +35 -0
- data/lib/netstack/connector/log.rb +262 -0
- data/lib/netstack/connector/log_error.rb +11 -0
- data/lib/netstack/connector/login_timeout.rb +11 -0
- data/lib/netstack/connector/output_limit_exceeded.rb +11 -0
- data/lib/netstack/connector/parsing_error.rb +11 -0
- data/lib/netstack/connector/profile/builder.rb +238 -0
- data/lib/netstack/connector/profile.rb +225 -0
- data/lib/netstack/connector/prompt_error.rb +11 -0
- data/lib/netstack/connector/recovery.rb +59 -0
- data/lib/netstack/connector/redactor.rb +100 -0
- data/lib/netstack/connector/response.rb +25 -0
- data/lib/netstack/connector/response_reader.rb +140 -0
- data/lib/netstack/connector/result.rb +50 -0
- data/lib/netstack/connector/running_config/rendered.rb +16 -0
- data/lib/netstack/connector/running_config/strategy.rb +28 -0
- data/lib/netstack/connector/running_config.rb +113 -0
- data/lib/netstack/connector/save_config.rb +23 -0
- data/lib/netstack/connector/script.rb +59 -0
- data/lib/netstack/connector/script_error.rb +11 -0
- data/lib/netstack/connector/script_output_limit_exceeded.rb +11 -0
- data/lib/netstack/connector/session.rb +414 -0
- data/lib/netstack/connector/session_busy.rb +11 -0
- data/lib/netstack/connector/storage/backup_lock.rb +117 -0
- data/lib/netstack/connector/storage/private_file.rb +111 -0
- data/lib/netstack/connector/storage/safe_file.rb +64 -0
- data/lib/netstack/connector/storage/saved_config.rb +76 -0
- data/lib/netstack/connector/terminal_renderer.rb +186 -0
- data/lib/netstack/connector/terminal_text.rb +32 -0
- data/lib/netstack/connector/textfsm.rb +71 -0
- data/lib/netstack/connector/tftp/file_upload.rb +53 -0
- data/lib/netstack/connector/tftp/strategy.rb +67 -0
- data/lib/netstack/connector/tftp.rb +116 -0
- data/lib/netstack/connector/tftp_completion_error.rb +36 -0
- data/lib/netstack/connector/tftp_receipt.rb +54 -0
- data/lib/netstack/connector/tftp_target.rb +65 -0
- data/lib/netstack/connector/topology/checkpoint_error.rb +16 -0
- data/lib/netstack/connector/topology/deployment_adapter.rb +163 -0
- data/lib/netstack/connector/topology/execution_result.rb +34 -0
- data/lib/netstack/connector/topology/immediate_strategy.rb +39 -0
- data/lib/netstack/connector/topology/interface_description.rb +46 -0
- data/lib/netstack/connector/topology/interface_name.rb +60 -0
- data/lib/netstack/connector/topology/plan.rb +105 -0
- data/lib/netstack/connector/topology/strategy.rb +76 -0
- data/lib/netstack/connector/topology.rb +255 -0
- data/lib/netstack/connector/transport_error.rb +11 -0
- data/lib/netstack/connector/transports.rb +201 -0
- data/lib/netstack/connector/underlying_error.rb +25 -0
- data/lib/netstack/connector/unsupported_operation.rb +11 -0
- data/lib/netstack/connector/vendors/cisco_ios/running_config.rb +22 -0
- data/lib/netstack/connector/vendors/cisco_ios/tftp_backup.rb +41 -0
- data/lib/netstack/connector/vendors/cisco_ios/topology.rb +54 -0
- data/lib/netstack/connector/vendors/cisco_ios.rb +44 -0
- data/lib/netstack/connector/vendors/cisco_nxos/running_config.rb +25 -0
- data/lib/netstack/connector/vendors/cisco_nxos/tftp_backup.rb +43 -0
- data/lib/netstack/connector/vendors/cisco_nxos.rb +51 -0
- data/lib/netstack/connector/vendors/h3c/tftp_backup.rb +34 -0
- data/lib/netstack/connector/vendors/h3c/topology.rb +72 -0
- data/lib/netstack/connector/vendors/h3c.rb +75 -0
- data/lib/netstack/connector/vendors/h3c_wireless.rb +13 -0
- data/lib/netstack/connector/vendors/hillstone/running_config.rb +16 -0
- data/lib/netstack/connector/vendors/hillstone/tftp_backup.rb +55 -0
- data/lib/netstack/connector/vendors/hillstone/topology.rb +64 -0
- data/lib/netstack/connector/vendors/hillstone.rb +39 -0
- data/lib/netstack/connector/vendors/huawei/tftp_backup.rb +26 -0
- data/lib/netstack/connector/vendors/huawei.rb +62 -0
- data/lib/netstack/connector/vendors/palo_alto/running_config.rb +77 -0
- data/lib/netstack/connector/vendors/palo_alto/tftp_backup.rb +49 -0
- data/lib/netstack/connector/vendors/palo_alto/topology.rb +77 -0
- data/lib/netstack/connector/vendors/palo_alto.rb +39 -0
- data/lib/netstack/connector/vendors/radware/running_config.rb +21 -0
- data/lib/netstack/connector/vendors/radware/tftp_backup.rb +49 -0
- data/lib/netstack/connector/vendors/radware/topology.rb +19 -0
- data/lib/netstack/connector/vendors/radware.rb +59 -0
- data/lib/netstack/connector/write_timeout.rb +11 -0
- data/lib/netstack/connector.rb +32 -0
- data/lib/netstack/ddi/applier.rb +107 -0
- data/lib/netstack/ddi/deployment.rb +137 -0
- data/lib/netstack/ddi/inputs.rb +61 -0
- data/lib/netstack/ddi/manager.rb +197 -0
- data/lib/netstack/ddi/plan.rb +99 -0
- data/lib/netstack/ddi/scope.rb +127 -0
- data/lib/netstack/ddi/settings.rb +44 -0
- data/lib/netstack/ddi/state.rb +166 -0
- data/lib/netstack/deployment/checkpoint_error.rb +14 -0
- data/lib/netstack/deployment/cli_adapter.rb +64 -0
- data/lib/netstack/deployment/executor.rb +131 -0
- data/lib/netstack/deployment/plan.rb +82 -0
- data/lib/netstack/deployment/receipt.rb +78 -0
- data/lib/netstack/deployment/recovery.rb +19 -0
- data/lib/netstack/deployment/step.rb +16 -0
- data/lib/netstack/dimension.rb +30 -0
- data/lib/netstack/error.rb +14 -0
- data/lib/netstack/input_error.rb +9 -0
- data/lib/netstack/integrations/bluecat/client.rb +84 -0
- data/lib/netstack/integrations/http/client.rb +146 -0
- data/lib/netstack/integrations/http/error.rb +10 -0
- data/lib/netstack/integrations/http/response.rb +28 -0
- data/lib/netstack/integrations/http_delivery/adapter.rb +51 -0
- data/lib/netstack/integrations/http_delivery/client.rb +127 -0
- data/lib/netstack/integrations/http_delivery/result.rb +27 -0
- data/lib/netstack/integrations/oxidized/client.rb +89 -0
- data/lib/netstack/integrations/zabbix/client.rb +211 -0
- data/lib/netstack/integrations/zabbix/error.rb +10 -0
- data/lib/netstack/inventory/address.rb +30 -0
- data/lib/netstack/inventory/changes.rb +13 -0
- data/lib/netstack/inventory/cli_parser.rb +75 -0
- data/lib/netstack/inventory/collector.rb +99 -0
- data/lib/netstack/inventory/device.rb +14 -0
- data/lib/netstack/inventory/interface.rb +25 -0
- data/lib/netstack/inventory/neighbor.rb +15 -0
- data/lib/netstack/inventory/reconciler.rb +74 -0
- data/lib/netstack/inventory/snapshot.rb +69 -0
- data/lib/netstack/inventory/transceiver.rb +23 -0
- data/lib/netstack/ipv4.rb +204 -0
- data/lib/netstack/ipv6.rb +88 -0
- data/lib/netstack/monitoring/applier.rb +179 -0
- data/lib/netstack/monitoring/device_plan.rb +23 -0
- data/lib/netstack/monitoring/host_plan.rb +152 -0
- data/lib/netstack/monitoring/inputs.rb +73 -0
- data/lib/netstack/monitoring/measurements.rb +89 -0
- data/lib/netstack/monitoring/mutation.rb +37 -0
- data/lib/netstack/monitoring/plan.rb +69 -0
- data/lib/netstack/monitoring/probe_plan.rb +19 -0
- data/lib/netstack/monitoring/state_comparison.rb +37 -0
- data/lib/netstack/range_set.rb +181 -0
- data/lib/netstack/routing/lookup.rb +46 -0
- data/lib/netstack/routing/lookup_result.rb +14 -0
- data/lib/netstack/routing/route.rb +32 -0
- data/lib/netstack/routing/snapshot.rb +23 -0
- data/lib/netstack/target.rb +15 -0
- data/lib/netstack/unsupported_capability.rb +9 -0
- data/lib/netstack/values.rb +70 -0
- data/lib/netstack/version.rb +1 -1
- data/lib/netstack/vxlan/auditor.rb +187 -0
- data/lib/netstack/vxlan/cisco_nxos/config_parser.rb +327 -0
- data/lib/netstack/vxlan/cisco_nxos/deployment_adapter.rb +63 -0
- data/lib/netstack/vxlan/cisco_nxos/runtime_parser.rb +182 -0
- data/lib/netstack/vxlan/cisco_nxos/segment_renderer.rb +42 -0
- data/lib/netstack/vxlan/collector.rb +77 -0
- data/lib/netstack/vxlan/fabric.rb +29 -0
- data/lib/netstack/vxlan/finding.rb +20 -0
- data/lib/netstack/vxlan/node.rb +51 -0
- data/lib/netstack/vxlan/route_policy.rb +57 -0
- data/lib/netstack/vxlan/runtime_result.rb +24 -0
- data/lib/netstack/vxlan/segment.rb +35 -0
- data/lib/netstack/vxlan/segment_plan.rb +25 -0
- data/lib/netstack/vxlan/segment_planner.rb +156 -0
- data/lib/netstack/vxlan/snapshot.rb +34 -0
- data/lib/netstack/vxlan/validation.rb +58 -0
- data/lib/netstack/vxlan/verifier.rb +52 -0
- data/lib/netstack/vxlan/vpc_domain.rb +20 -0
- data/lib/netstack/wireless/access_point.rb +12 -0
- data/lib/netstack/wireless/ap_group.rb +12 -0
- data/lib/netstack/wireless/assessment.rb +13 -0
- data/lib/netstack/wireless/assessor.rb +83 -0
- data/lib/netstack/wireless/authentication_connection.rb +12 -0
- data/lib/netstack/wireless/bss.rb +12 -0
- data/lib/netstack/wireless/client.rb +12 -0
- data/lib/netstack/wireless/collector.rb +191 -0
- data/lib/netstack/wireless/configuration.rb +13 -0
- data/lib/netstack/wireless/finding.rb +12 -0
- data/lib/netstack/wireless/h3c/config_parser.rb +229 -0
- data/lib/netstack/wireless/h3c/lldp_parser.rb +69 -0
- data/lib/netstack/wireless/h3c/runtime_parser.rb +240 -0
- data/lib/netstack/wireless/huawei/runtime_parser.rb +83 -0
- data/lib/netstack/wireless/invalid_snapshot.rb +11 -0
- data/lib/netstack/wireless/neighbor.rb +12 -0
- data/lib/netstack/wireless/normalizer.rb +90 -0
- data/lib/netstack/wireless/parse_result.rb +13 -0
- data/lib/netstack/wireless/radio.rb +12 -0
- data/lib/netstack/wireless/reconciler.rb +115 -0
- data/lib/netstack/wireless/reconciliation.rb +11 -0
- data/lib/netstack/wireless/security_policy.rb +12 -0
- data/lib/netstack/wireless/service_template.rb +12 -0
- data/lib/netstack/wireless/service_template_bindings.rb +56 -0
- data/lib/netstack/wireless/snapshot.rb +150 -0
- data/lib/netstack/wireless/state.rb +23 -0
- data/lib/netstack/wireless/view.rb +154 -0
- data/lib/netstack.rb +10 -3
- data/resources/configuration/cisco-bootstrap.erb +118 -0
- data/resources/configuration/cisco-distributed-gateway.erb +40 -0
- data/resources/configuration/cisco-n9k-vpc.erb +62 -0
- data/resources/configuration/cisco-server-port.erb +43 -0
- data/resources/configuration/h3c-bootstrap.erb +108 -0
- data/resources/configuration/h3c-irf.erb +57 -0
- data/resources/configuration/h3c-server-port.erb +36 -0
- data/resources/configuration/hillstone-ipsec-vpn.erb +68 -0
- data/resources/configuration/hillstone-policy.erb +31 -0
- data/resources/configuration/paloalto-ipsec-vpn.erb +39 -0
- data/resources/configuration/radware-load-balancer.erb +26 -0
- data/resources/textfsm/cisco_cdp_neighbors_detail.textfsm +7 -0
- data/resources/textfsm/cisco_ios_running_config_interfaces.textfsm +12 -0
- data/resources/textfsm/cisco_ios_show_ip_interface_brief.textfsm +7 -0
- data/resources/textfsm/h3c_interface_descriptions.textfsm +11 -0
- data/resources/textfsm/h3c_lldp_local_first.textfsm +8 -0
- data/resources/textfsm/h3c_lldp_name_first.textfsm +8 -0
- data/resources/textfsm/hillstone_interface_descriptions.textfsm +11 -0
- data/resources/textfsm/hillstone_lldp_neighbors.textfsm +7 -0
- data/resources/textfsm/index +8 -0
- data/resources/textfsm/palo_alto_interface_descriptions.textfsm +6 -0
- data/resources/textfsm/palo_alto_lldp_neighbors.textfsm +11 -0
- data/resources/textfsm/radware_port_names.textfsm +13 -0
- metadata +588 -27
- data/.rubocop.yml +0 -13
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -10
- data/Gemfile.lock +0 -40
- data/Rakefile +0 -8
- data/bin/console +0 -15
- data/bin/setup +0 -8
data/docs/connector.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Connector
|
|
2
|
+
|
|
3
|
+
`Netstack::Connector` 从 net-connector 当前工作树移植单设备能力,源 HEAD 为 `63935c0`。源工作树含未提交修改,以下 SHA-256 记录实际读取内容。保留 MIT 版权归属;全项目许可与归属见 LICENSE 和 NOTICE。
|
|
4
|
+
|
|
5
|
+
不包含 Netdisco、设备清单查询、批量 Worker、报告、批量 TFTP 归档、旧命令行入口或 PostgreSQL 依赖。显式 `backup(path:)` 仍提供单设备原子配置备份,不承担执行任务历史。
|
|
6
|
+
|
|
7
|
+
入口 `require "netstack/connector"`;`build`、`open`、`vendors`、`vendor_class`。源 `Base` 为新 `Device`;厂商为 `Vendors::H3c` 等类,策略嵌套在厂商类内。没有旧常量别名。所有文件按真实常量由统一 Zeitwerk 加载。
|
|
8
|
+
|
|
9
|
+
原始命令输出保持字节;终端展示、解析文本与脱敏诊断分别处理。能力查询不联网。`open` 保证资源清理;会话锁只在当前进程内有效。设备配置变更不具备跨系统事务,未知执行结果不能自动重放。TFTP 回执的 `device_reported` 不代表服务器文件已验证。
|
|
10
|
+
|
|
11
|
+
模板位于 gem 内 `resources/textfsm`。每次解析新建 TextFSM 状态,禁止多个线程共享可变 parser。测试使用合成传输/本地 PTY,未证明真实设备固件兼容。
|
|
12
|
+
|
|
13
|
+
`known_hosts` 合并使用跨进程锁;锁等待单独以 `login_timeout` 为上限,超时返回 `LoginTimeout`,关闭连接并丢弃会话副本,不改变共享信任文件。该上限不是整个登录流程的总时限。
|
|
14
|
+
错误归一化只保留固定错误码/阶段,目标地址取自会话配置,敏感输出上下文同时保护 source 等诊断字段;明确返回的原始命令结果仍保留原文。
|
|
15
|
+
|
|
16
|
+
设备日志只接受普通文件;打开时使用非阻塞模式,并在同一文件描述符上校验类型。
|
|
17
|
+
FIFO、目录等特殊文件返回 `LogError`,不会等待管道读者而阻塞会话。
|
|
18
|
+
|
|
19
|
+
## 接口描述的持久执行
|
|
20
|
+
|
|
21
|
+
`plan_interface_descriptions` 保留只读规划入口,返回私有 `Topology::Plan` 工件。
|
|
22
|
+
工件的版本化 `payload` 可交给宿主安全存储,并由 `Topology::Plan.from_h` 恢复;
|
|
23
|
+
其中包含旧描述和批准的命令,不应写入普通日志。`sha256` 绑定其完整内容,`inspect` 不展开正文。
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
artifact = device.plan_interface_descriptions
|
|
27
|
+
target = Netstack::Target.new(key: device_id, host: device.host, platform: device.vendor)
|
|
28
|
+
# 宿主先保存 artifact.payload,再创建执行记录并取得目标执行所有权。
|
|
29
|
+
plan = artifact.deployment_plan(target: target, artifact_ref: stored_artifact_id)
|
|
30
|
+
outcome = device.apply_interface_descriptions(
|
|
31
|
+
plan, execution_id: execution_id, confirmed: true,
|
|
32
|
+
artifact_resolver: ->(reference) { Netstack::Connector::Topology::Plan.from_h(private_store.fetch(reference)) },
|
|
33
|
+
checkpoint: ->(event) { execution_store.commit_checkpoint!(event); true },
|
|
34
|
+
previous: previous_receipt
|
|
35
|
+
)
|
|
36
|
+
outcome.receipt # 可序列化的执行状态;不含命令或配置正文
|
|
37
|
+
outcome.result # 本次写入和批准的状态读回步骤的 Connector::Result 原始证据
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`apply_interface_descriptions` 接收 `Deployment::Plan`,必须提供同步 checkpoint 和工件解析器;
|
|
41
|
+
旧的 `apply_interface_descriptions(topology_plan, confirmed: true)` 不再允许写入。
|
|
42
|
+
Deployment Plan 只保存工件引用、SHA-256 和批准的读回期望。
|
|
43
|
+
每次首次提交修改或保存步骤前重新解析工件,验证摘要、目标、命令序列与批准的 Deployment Plan。
|
|
44
|
+
普通 SSH/Telnet 命令、`save_config` 等显式 I/O 原语保持原有职责;宿主应通过持久业务执行入口协调业务变更。
|
|
45
|
+
|
|
46
|
+
执行分为 `change` 和 `persist` 两个步骤:每步发送前都须先持久提交 `started`;
|
|
47
|
+
修改后的完整配置读回得到确认,且 `change/verified` checkpoint 已提交,才允许保存。
|
|
48
|
+
回调必须完成宿主持久事务并返回恰好 `true`,不能仅入队或等待外层事务以后提交。
|
|
49
|
+
宿主负责验证执行身份与目标租约,checkpoint 失败立即停止后续写入。
|
|
50
|
+
|
|
51
|
+
恢复验证 execution、target、plan、step 和幂等身份。未知修改只读回,全部目标描述匹配后才可能推进尚未提交的保存步骤;
|
|
52
|
+
恢复尚未开始的保存前会重新核验邻居和目标描述。已提交的未知步骤不会自动重放,也不重新取其命令工件。
|
|
53
|
+
未知保存缺少已持久化的明确成功回执时保持 `unknown`,运行配置正确不能证明启动配置已经保存。
|
|
54
|
+
当前 IOS、NX-OS、H3C、Hillstone 使用明确保存完成行;未新增启动配置读回协议。PAN-OS 的自动描述变更仍不开放。
|
|
55
|
+
|
|
56
|
+
返回的 `Topology::ExecutionResult#success?` 仅在回执全部验证通过或计划无变更时成立;
|
|
57
|
+
`value!` 对未知或失败状态抛错,即使本次普通命令输出看起来成功。
|
|
58
|
+
`result` 延续原业务结果的步骤边界,保留修改、批准的描述读回及保存输出;
|
|
59
|
+
它不是完整会话记录,不包含前置邻居查询和基线重验。前置门禁失败仍抛出既有领域错误。
|
|
60
|
+
恢复结果只含本次状态读回,不伪造前次原始输出。`Topology::CheckpointError` 继承 `Deployment::CheckpointError`,
|
|
61
|
+
保留 `receipt` 和本次 `result`,便于宿主保存已取得的原始证据并安排恢复。
|
|
62
|
+
|
|
63
|
+
拓扑回归与安装包检查使用内存 checkpoint 和合成设备,只证明离线协议、包内模板和资源加载,
|
|
64
|
+
不证明宿主数据库持久性、目标租约或真实设备保存介质。
|
|
65
|
+
|
|
66
|
+
## 源文件映射
|
|
67
|
+
|
|
68
|
+
| 源 `lib/net/connector/` | 新路径 | 源 SHA-256 |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `device/base.rb` | `lib/netstack/connector/device.rb` | `42709ab64057e304ec9cd21d11ad88a134acca63ca17fd30521f9ed9e7168dea` |
|
|
71
|
+
| `device/local_backup.rb` | `lib/netstack/connector/backup.rb` | `a5a5edb84dc33020d93aa512bbfe3eaf7c8f8c155f8d924d397d48bb07608e76` |
|
|
72
|
+
| `device/local_backup.rb` | `lib/netstack/connector/backup_persistence_error.rb` | `a5a5edb84dc33020d93aa512bbfe3eaf7c8f8c155f8d924d397d48bb07608e76` |
|
|
73
|
+
| `device/local_backup.rb` | `lib/netstack/connector/local_backup.rb` | `a5a5edb84dc33020d93aa512bbfe3eaf7c8f8c155f8d924d397d48bb07608e76` |
|
|
74
|
+
| `device/profile/builder.rb` | `lib/netstack/connector/profile/builder.rb` | `419f5849d914289a8a6f9cf4e4027a432593129650215e0a3ad82975e8761999` |
|
|
75
|
+
| `device/profile.rb` | `lib/netstack/connector/profile.rb` | `fdbbef914ef66c7ec74d3ddcd9eda212cbbcf858ebc23f343899a889c0020e44` |
|
|
76
|
+
| `device/running_config/strategy.rb` | `lib/netstack/connector/running_config/strategy.rb` | `0af92cff14da30127dc480bf94c72efd1ded9938a8066e10cd5cb01e2e6fd7d1` |
|
|
77
|
+
| `device/running_config/strategy.rb` | `lib/netstack/connector/running_config/rendered.rb` | `0af92cff14da30127dc480bf94c72efd1ded9938a8066e10cd5cb01e2e6fd7d1` |
|
|
78
|
+
| `device/running_config.rb` | `lib/netstack/connector/running_config.rb` | `d85003303c073ba3f22d63175c47546867edbbf29c665de52110358e41aff246` |
|
|
79
|
+
| `device/save_config.rb` | `lib/netstack/connector/save_config.rb` | `8ead7fe2e34e948e1254d4e4170d262674df9d3864c05209246c703fef0798cd` |
|
|
80
|
+
| `device/tftp/file_upload.rb` | `lib/netstack/connector/tftp/file_upload.rb` | `825bd77d5b35562b2bc698705646e8d169b439000d47376b523e3b4977aea696` |
|
|
81
|
+
| `device/tftp/receipt.rb` | `lib/netstack/connector/tftp_receipt.rb` | `4e3622bbf4b3e2bfbe8000501d9a2be996ed07b4e034fe30996421aea0890224` |
|
|
82
|
+
| `device/tftp/receipt.rb` | `lib/netstack/connector/tftp_completion_error.rb` | `4e3622bbf4b3e2bfbe8000501d9a2be996ed07b4e034fe30996421aea0890224` |
|
|
83
|
+
| `device/tftp/strategy.rb` | `lib/netstack/connector/tftp/strategy.rb` | `0f004cef8e98dd5b5883da2a4610f870d2115839cef15ab7cd1af1704215cf70` |
|
|
84
|
+
| `device/tftp/target.rb` | `lib/netstack/connector/tftp_target.rb` | `dbc1b912776809d67221850315482177ffa70d666b8e90d9fdd8a41f5b84c36c` |
|
|
85
|
+
| `device/tftp.rb` | `lib/netstack/connector/tftp.rb` | `dac1d20d76e9b97cc167788b77f179b34d778e5dfe48b944fd34f7b0a871a5f9` |
|
|
86
|
+
| `device/topology/immediate_strategy.rb` | `lib/netstack/connector/topology/immediate_strategy.rb` | `a738030eae4ef1d784c426c6d07e6405771b21d2d754dbb69bbecd13572fe902` |
|
|
87
|
+
| `device/topology/interface_description.rb` | `lib/netstack/connector/topology/interface_description.rb` | `d8def28e63ddc48f96f8c949da3220ebc2477bee78b3940f09f4fb23c193007f` |
|
|
88
|
+
| `device/topology/interface_name.rb` | `lib/netstack/connector/topology/interface_name.rb` | `8c21239d54e772a83acd67aea171178f2c5ea3f8a46a1061bda00ee227a4a0ba` |
|
|
89
|
+
| `device/topology/strategy.rb` | `lib/netstack/connector/topology/strategy.rb` | `be8f5f63a914688d8e1b19633b1506fb8a5b01acf2e4f3204e19128defa8d224` |
|
|
90
|
+
| `device/topology.rb` | `lib/netstack/connector/topology.rb` | `8b9c554a3828d53328d8c43c3c1095b3d9c185397b2be2bcfd8d4ae596949b92` |
|
|
91
|
+
| `engine/authentication.rb` | `lib/netstack/connector/authentication.rb` | `a1f6ecbd0046809847a2887d5c47de9d952f1bbae6cc4ceed69e1a6f38ad98e3` |
|
|
92
|
+
| `engine/command.rb` | `lib/netstack/connector/command.rb` | `987943274b4bccdf8d81806907f44f54c301b612e3e14d1da889a94ab51bcbf3` |
|
|
93
|
+
| `engine/command.rb` | `lib/netstack/connector/script.rb` | `987943274b4bccdf8d81806907f44f54c301b612e3e14d1da889a94ab51bcbf3` |
|
|
94
|
+
| `engine/configuration.rb` | `lib/netstack/connector/configuration.rb` | `aa02c8bf824bf1579d0eb2b8614353d645a0fa35eed20c420fb1f3519be5c50b` |
|
|
95
|
+
| `engine/dialogue.rb` | `lib/netstack/connector/interaction.rb` | `d9a79bd40987375ceb8b721493a336c61cdd317c016a58ff331079f26d3ff2fe` |
|
|
96
|
+
| `engine/dialogue.rb` | `lib/netstack/connector/dialogue.rb` | `d9a79bd40987375ceb8b721493a336c61cdd317c016a58ff331079f26d3ff2fe` |
|
|
97
|
+
| `engine/dialogue.rb` | `lib/netstack/connector/response.rb` | `d9a79bd40987375ceb8b721493a336c61cdd317c016a58ff331079f26d3ff2fe` |
|
|
98
|
+
| `engine/dialogue.rb` | `lib/netstack/connector/response_reader.rb` | `d9a79bd40987375ceb8b721493a336c61cdd317c016a58ff331079f26d3ff2fe` |
|
|
99
|
+
| `engine/error_metadata.rb` | `lib/netstack/connector/error_metadata.rb` | `bac378c4f7853b315b3564acdb3257d5512470781f27e66722bedb29789655b5` |
|
|
100
|
+
| `engine/errors.rb` | `lib/netstack/connector/error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
101
|
+
| `engine/errors.rb` | `lib/netstack/connector/connection_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
102
|
+
| `engine/errors.rb` | `lib/netstack/connector/authentication_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
103
|
+
| `engine/errors.rb` | `lib/netstack/connector/login_timeout.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
104
|
+
| `engine/errors.rb` | `lib/netstack/connector/command_timeout.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
105
|
+
| `engine/errors.rb` | `lib/netstack/connector/write_timeout.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
106
|
+
| `engine/errors.rb` | `lib/netstack/connector/prompt_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
107
|
+
| `engine/errors.rb` | `lib/netstack/connector/connection_closed.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
108
|
+
| `engine/errors.rb` | `lib/netstack/connector/transport_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
109
|
+
| `engine/errors.rb` | `lib/netstack/connector/device_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
110
|
+
| `engine/errors.rb` | `lib/netstack/connector/script_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
111
|
+
| `engine/errors.rb` | `lib/netstack/connector/internal_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
112
|
+
| `engine/errors.rb` | `lib/netstack/connector/output_limit_exceeded.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
113
|
+
| `engine/errors.rb` | `lib/netstack/connector/script_output_limit_exceeded.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
114
|
+
| `engine/errors.rb` | `lib/netstack/connector/session_busy.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
115
|
+
| `engine/errors.rb` | `lib/netstack/connector/unsupported_operation.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
116
|
+
| `engine/errors.rb` | `lib/netstack/connector/log_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
117
|
+
| `engine/errors.rb` | `lib/netstack/connector/parsing_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
118
|
+
| `engine/errors.rb` | `lib/netstack/connector/underlying_error.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
119
|
+
| `engine/errors.rb` | `lib/netstack/connector/redactor.rb` | `b7c0d7e95312b666609acb00d466c2b8cb58188cf92601cc6bbcf2ab55a95caa` |
|
|
120
|
+
| `engine/execution.rb` | `lib/netstack/connector/execution.rb` | `25161c179e88c20326087db80337435ab06922d3697af24216a55626324baca2` |
|
|
121
|
+
| `engine/known_hosts.rb` | `lib/netstack/connector/known_hosts.rb` | `a65141f63180f61a4df3268772ecd2b25faa0698e9421fc6b4ecf6a21f818d7a` |
|
|
122
|
+
| `engine/log/event.rb` | `lib/netstack/connector/log/event.rb` | `014174c54a2b98dd1e94700928214aa21a4a6502e54978dfff3de3b4f6d2171f` |
|
|
123
|
+
| `engine/log/formatter.rb` | `lib/netstack/connector/log/formatter.rb` | `f114d8747ba7348f52212395942f13d331aeec6f585b46ecbaf0875daa1dae72` |
|
|
124
|
+
| `engine/log/messages.rb` | `lib/netstack/connector/log/messages.rb` | `75cf3261b06a9b2f8ce20034f291f93603fdddf80816e39f93d54f021f0932d5` |
|
|
125
|
+
| `engine/log/stream.rb` | `lib/netstack/connector/log/redacting_writer.rb` | `95bcc9ed2a09cb1acd7f4224394cabee226b4bded175bc6c6968ef1f6b72cc18` |
|
|
126
|
+
| `engine/log/stream.rb` | `lib/netstack/connector/log/transcript.rb` | `95bcc9ed2a09cb1acd7f4224394cabee226b4bded175bc6c6968ef1f6b72cc18` |
|
|
127
|
+
| `engine/log.rb` | `lib/netstack/connector/log.rb` | `40cfd01ffc4da113f1c6bcdd0e4efa95213f4e67a40db2170a2c439cd9efdc73` |
|
|
128
|
+
| `engine/recovery.rb` | `lib/netstack/connector/recovery.rb` | `2fce303db923a77cf3fa99d7bbc70196ae5b4a5bb88b648fc5db0e7f0a42c229` |
|
|
129
|
+
| `engine/result.rb` | `lib/netstack/connector/command_result.rb` | `e16174105375bac53ea00ddc6d3a65a91fab5a0d094c7cd4e02fa50c8253aa69` |
|
|
130
|
+
| `engine/result.rb` | `lib/netstack/connector/result.rb` | `e16174105375bac53ea00ddc6d3a65a91fab5a0d094c7cd4e02fa50c8253aa69` |
|
|
131
|
+
| `engine/session.rb` | `lib/netstack/connector/session.rb` | `307353d6183c402b420cf673551cc3779284b11ee825e195a28500f15651d73e` |
|
|
132
|
+
| `engine/terminal_renderer.rb` | `lib/netstack/connector/terminal_renderer.rb` | `1c716223aecb738e77b3ef6321631413fb39749f8ebbba1196f7eca474d21639` |
|
|
133
|
+
| `engine/terminal_text.rb` | `lib/netstack/connector/terminal_text.rb` | `80851db198f0008026d989eb8da9be7ab44d7f5a071389da19079bf090c6d3b8` |
|
|
134
|
+
| `engine/transport.rb` | `lib/netstack/connector/event.rb` | `ff59a03ab55e25b207d04864c52b7ca6ebfbe54fde666f82934f5f69c65d4183` |
|
|
135
|
+
| `engine/transport.rb` | `lib/netstack/connector/transports.rb` | `ff59a03ab55e25b207d04864c52b7ca6ebfbe54fde666f82934f5f69c65d4183` |
|
|
136
|
+
| `storage/backup_lock.rb` | `lib/netstack/connector/backup_busy.rb` | `4dbb5806e4b92580f8af6f34e7b1cf11f7aad3c0a8bfc5427d81c80964ea4fa6` |
|
|
137
|
+
| `storage/backup_lock.rb` | `lib/netstack/connector/storage/backup_lock.rb` | `4dbb5806e4b92580f8af6f34e7b1cf11f7aad3c0a8bfc5427d81c80964ea4fa6` |
|
|
138
|
+
| `storage/private_file.rb` | `lib/netstack/connector/storage/private_file.rb` | `59992bb0dc264ef7e6d82ea3a20769b60624f805bea810a3f4154b84bbe136ca` |
|
|
139
|
+
| `storage/safe_file.rb` | `lib/netstack/connector/storage/safe_file.rb` | `571d99db152a3947337b1b27d8818f6fd6c1076a37229141e24b37cd6c4e075a` |
|
|
140
|
+
| `storage/saved_config.rb` | `lib/netstack/connector/storage/saved_config.rb` | `6f52412351acf4a1f6d4e5c98d0cbd4598b7eaf19a067b31c0a42f7e22dcec2d` |
|
|
141
|
+
| `textfsm.rb` | `lib/netstack/connector/textfsm.rb` | `1e8197da9a37cf9af6b757a0696dbb93164b14e0705fd2a037279e39dffe577f` |
|
|
142
|
+
| `vendor/cisco_ios/running_config.rb` | `lib/netstack/connector/vendors/cisco_ios/running_config.rb` | `11d809feea41de17c823da3fe6bd107e3df2db444ec1eb285d0ae8d3434f7b9d` |
|
|
143
|
+
| `vendor/cisco_ios/tftp_backup.rb` | `lib/netstack/connector/vendors/cisco_ios/tftp_backup.rb` | `a56aa18d79b42d2dffd720230d4124560acbdeebbfa95536dbb67943ce6d9e4c` |
|
|
144
|
+
| `vendor/cisco_ios/topology.rb` | `lib/netstack/connector/vendors/cisco_ios/topology.rb` | `e0630d2de8adfcdbbc2fe9dc2aca10bee912fd7d26cd2c94d7f733520156a62d` |
|
|
145
|
+
| `vendor/cisco_ios.rb` | `lib/netstack/connector/vendors/cisco_ios.rb` | `302478cc6ff55ce2a5ca90707f2caad4a1d67735fe71e242194a65fc888d09fb` |
|
|
146
|
+
| `vendor/cisco_nxos/running_config.rb` | `lib/netstack/connector/vendors/cisco_nxos/running_config.rb` | `259aae62625b8f37c65e7391486c4e994dc92187a1482152bfc4eb54968d2664` |
|
|
147
|
+
| `vendor/cisco_nxos/tftp_backup.rb` | `lib/netstack/connector/vendors/cisco_nxos/tftp_backup.rb` | `b57e008bdaf726a1501ab37d784bb112cb5bba708b2622b7ed97acdca07ef806` |
|
|
148
|
+
| `vendor/cisco_nxos.rb` | `lib/netstack/connector/vendors/cisco_nxos.rb` | `8ee07b00730437b736d5313473eb2e6765be9aa0f69d70584427c217548b4c3d` |
|
|
149
|
+
| `vendor/h3c/tftp_backup.rb` | `lib/netstack/connector/vendors/h3c/tftp_backup.rb` | `76136351c3af90f28914b79c1706e1662aca58a6b3d3e0fc4938078c155ef8d5` |
|
|
150
|
+
| `vendor/h3c/topology.rb` | `lib/netstack/connector/vendors/h3c/topology.rb` | `b1b10bf5d2d71ad79aa082fdba715db11b65c360fbee939448b1c04209f4f16c` |
|
|
151
|
+
| `vendor/h3c.rb` | `lib/netstack/connector/vendors/h3c.rb` | `1b03458c1a11ef3f2dde2d5cdadf6b16ce556c24a35c9558400ad9ea70797f75` |
|
|
152
|
+
| `vendor/h3c_wireless.rb` | `lib/netstack/connector/vendors/h3c_wireless.rb` | `b702b34128b8b6e8cdc6275392d5420d640319f7e001327c5229622bd582eef3` |
|
|
153
|
+
| `vendor/hillstone/running_config.rb` | `lib/netstack/connector/vendors/hillstone/running_config.rb` | `c84649f204dd00f08a2e9f4706995495fdd12fcd43179c9095a2f3177d10ec1e` |
|
|
154
|
+
| `vendor/hillstone/tftp_backup.rb` | `lib/netstack/connector/vendors/hillstone/tftp_backup.rb` | `e4dcf44e784c7b66610631d6cb726690aa02bc7c2301ba023e75d64f6810646f` |
|
|
155
|
+
| `vendor/hillstone/topology.rb` | `lib/netstack/connector/vendors/hillstone/topology.rb` | `81606694aa18a3372057b5080adb64346165859f63b28e287b6ee1661407cabc` |
|
|
156
|
+
| `vendor/hillstone.rb` | `lib/netstack/connector/vendors/hillstone.rb` | `c921078be010469d762f1820420c08e03a2eaba917a4ab1c00d6a6c8da74bdc8` |
|
|
157
|
+
| `vendor/huawei/tftp_backup.rb` | `lib/netstack/connector/vendors/huawei/tftp_backup.rb` | `702310f7096b9d9624463bc6547f60d1aa1c4a9c65ed70c401303c67457cbf6e` |
|
|
158
|
+
| `vendor/huawei.rb` | `lib/netstack/connector/vendors/huawei.rb` | `eb6fa5b3ec88b9450f183757cfdcf065c3cfcb881427f2513039200f896dd323` |
|
|
159
|
+
| `vendor/palo_alto/running_config.rb` | `lib/netstack/connector/vendors/palo_alto/running_config.rb` | `d0e93012023a06aebeddceebe70c0fee4d3bd840899cac24c63ce82ae1117e07` |
|
|
160
|
+
| `vendor/palo_alto/tftp_backup.rb` | `lib/netstack/connector/vendors/palo_alto/tftp_backup.rb` | `46a277288bddf9b61f99251d32d54c4ba19b92835f17d7f5f5b28e2bf5fd7ec3` |
|
|
161
|
+
| `vendor/palo_alto/topology.rb` | `lib/netstack/connector/vendors/palo_alto/topology.rb` | `0f96170056e7eed570e2e691b1666d04264eb65e24ab54ddd7b258f5a2393f0b` |
|
|
162
|
+
| `vendor/palo_alto.rb` | `lib/netstack/connector/vendors/palo_alto.rb` | `e057bf2c0dd5c2e3e4fddc8953304aeacb05e1970409838dce47f70ce11326c3` |
|
|
163
|
+
| `vendor/radware/running_config.rb` | `lib/netstack/connector/vendors/radware/running_config.rb` | `4022095e149c8c53fbef55bf9967d9c8994cfeceb428a52a6110751b494bf1c7` |
|
|
164
|
+
| `vendor/radware/tftp_backup.rb` | `lib/netstack/connector/vendors/radware/tftp_backup.rb` | `04109e95a5242ab389247b4b2079ce86e71d76a7636e1ccd49b35886180ca1fc` |
|
|
165
|
+
| `vendor/radware/topology.rb` | `lib/netstack/connector/vendors/radware/topology.rb` | `527d34cdbc4ed96e598b871a466db773c4fee3d9ed0a98abc3d59075be5662aa` |
|
|
166
|
+
| `vendor/radware.rb` | `lib/netstack/connector/vendors/radware.rb` | `17a32d794cb2e860a0aca2411cee22bd208e5d6c866b2c81b7084f5e8625884e` |
|
|
167
|
+
|
|
168
|
+
## 资源与测试来源
|
|
169
|
+
|
|
170
|
+
模板全部源于同一 net-connector 工作树的 `lib/net/connector/templates/`。下面记录原始资源摘要,方便后续核对厂商模板变更。
|
|
171
|
+
|
|
172
|
+
| 模板 | 源 SHA-256 |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `cisco_cdp_neighbors_detail.textfsm` | `c45cdab78fcf4247987c9e37a9c2ff523aac6c43cde96a795649049174612b3e` |
|
|
175
|
+
| `cisco_ios_running_config_interfaces.textfsm` | `5584b009427874edb879c7e99988352e2f7392ca658a0c17764fb479c748cf08` |
|
|
176
|
+
| `cisco_ios_show_ip_interface_brief.textfsm` | `ae9f850599b4f2d5c28ed1176e2121debac8fe90cf4bdb88d9c652b67702b4cd` |
|
|
177
|
+
| `h3c_interface_descriptions.textfsm` | `1e7ecb6d0355c71956b2e4edc732a554899e3b6acdc1f0b15f2c63e7796f768d` |
|
|
178
|
+
| `h3c_lldp_local_first.textfsm` | `409ce4393aee6d58b0971e4114096f81a986e7a467788ef0f8ecb4768506c660` |
|
|
179
|
+
| `h3c_lldp_name_first.textfsm` | `88d6a00ef3a44f6fd6c080387052944d26c9ebccd56b5e87051e2494ba553055` |
|
|
180
|
+
| `hillstone_interface_descriptions.textfsm` | `c7074cdad05f3193b135148d6b5d4ef7652b2b005b8a351d282afc9023267766` |
|
|
181
|
+
| `hillstone_lldp_neighbors.textfsm` | `96e0f60add044bd3701c5b64aa7a3cb5b563dc046675ced0115afc269e17b97f` |
|
|
182
|
+
| `index` | `4f7a3228d20bb378811a465a0bc1daa8ba86e0dc6ca757a3f671bb4852175443` |
|
|
183
|
+
| `palo_alto_interface_descriptions.textfsm` | `d6a97dfd473354d78a4af767608b4eeca0b6ea9c24c258d09c3d20841ba69f8f` |
|
|
184
|
+
| `palo_alto_lldp_neighbors.textfsm` | `39ce5e20281e41af06987044198c3d5aa5466a6fc1f8123eed987b03beaf8740` |
|
|
185
|
+
| `radware_port_names.textfsm` | `65c7430b724f3796318f3dde59e0742f7eb74dfb6bc39983679ba8066c3fc9c5` |
|
|
186
|
+
|
|
187
|
+
Minitest 用例保留源断言,仅调整命名空间与加载路径。`backup_lock_test.rb`、`file_persistence_test.rb`、`tftp_boundary_test.rb`、`value_object_contract_test.rb` 只摘取单设备契约;原文件的 Fleet、报告、CLI、批次结果用例不属于此模块。`module_loading_test.rb` 验证新的统一 Zeitwerk、独立入口与安装资源路径。合成 TFTP/拓扑样本的来源说明保存在 `test/support/connector/*_samples.md`。
|
data/docs/ddi.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# DDI and BlueCat
|
|
2
|
+
|
|
3
|
+
`Ddi::Manager` owns DNS, DHCP and IPv4 allocation rules. The BlueCat Legacy v1
|
|
4
|
+
client owns authentication, wire methods, pagination and response validation.
|
|
5
|
+
Neither reads environment variables, Motor models, Netdisco or global settings.
|
|
6
|
+
The host owns authorization, durable intents, locks, transactions and jobs.
|
|
7
|
+
|
|
8
|
+
Passwords retain their original bytes, including surrounding spaces. CNAME target
|
|
9
|
+
comparison ignores DNS name case and the trailing root dot, so an equivalent
|
|
10
|
+
existing record is a no-op rather than a conflict.
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
client = Netstack::Integrations::Bluecat::Client.new(
|
|
14
|
+
base_url: "https://bam.example.test", username: username, password: password
|
|
15
|
+
)
|
|
16
|
+
settings = Netstack::Ddi::Settings.new(
|
|
17
|
+
configuration_id: configuration_id,
|
|
18
|
+
views: [{ id: view_id, roles: [
|
|
19
|
+
{ interface_id: master_interface_id, type: "MASTER" },
|
|
20
|
+
{ interface_id: slave_interface_id, type: "SLAVE" }
|
|
21
|
+
] }],
|
|
22
|
+
server_ids: deployment_server_ids, policy_id: response_policy_id,
|
|
23
|
+
protected_ids: protected_object_ids
|
|
24
|
+
)
|
|
25
|
+
manager = Netstack::Ddi::Manager.new(client: client, settings: settings)
|
|
26
|
+
plan = manager.dns_record(name: "edge.example.test", type: "A", value: "192.0.2.10")
|
|
27
|
+
# Authorize and persist plan.to_h, execution_id and scope before applying it.
|
|
28
|
+
result = Netstack::Ddi::Applier.new(client: client, settings: settings).call(
|
|
29
|
+
plan: plan, execution_id: execution_id, checkpoint: durable_checkpoint
|
|
30
|
+
)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`Settings` takes real server IDs and interface IDs separately. It never assumes
|
|
34
|
+
that one is the other plus or minus one. Configuration, views, servers, configured
|
|
35
|
+
interfaces, response policy and extra protected IDs cannot be deletion targets.
|
|
36
|
+
Plans include a stable digest and explicit settings scope. Restore them with
|
|
37
|
+
`Ddi::Plan.from_h`; changing settings requires replanning and review.
|
|
38
|
+
Object ownership is independently checked through `getParent` ancestry, up to
|
|
39
|
+
64 nodes, ending at the configured Configuration. Missing, cyclic or malformed
|
|
40
|
+
ancestry fails closed. Deletes also reject ancestors of protected objects, and
|
|
41
|
+
the write boundary rechecks scope and the previously observed object identity.
|
|
42
|
+
|
|
43
|
+
| Intent | Planning method | Rules |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| DNS zone | `dns_zone(name:, replace: false)` | Exact labels in each managed view; explicit role topology |
|
|
46
|
+
| A/CNAME | `dns_record(name:, value:, type: "A", replace: false)` | IPv4/FQDN validation; conflicting A/CNAME rejected |
|
|
47
|
+
| Forward zone | `dns_forward(name:, roles:, replace: false)` | Explicit MASTER/SLAVE/FORWARDER interfaces; at least one FORWARDER |
|
|
48
|
+
| Response policy | `blacklist(name:, present: true)` | Exact FQDN membership in configured policy; removal with `present: false` |
|
|
49
|
+
| Delete zone | `delete_zone(name:, recursive: true)` | Explicit recursive intent; exact view/label selection; no fuzzy deletion |
|
|
50
|
+
| IPv4 block | `block(cidr:, parent_id:)` | Aligned IPv4 CIDR; parent configuration or larger block |
|
|
51
|
+
| IPv4 network | `network(cidr:, block_id:, gateway:)` | Explicit parent and usable in-subnet gateway |
|
|
52
|
+
| Delete network | `delete_network(cidr:, block_id:)` | Exact CIDR and parent; protected IDs rejected |
|
|
53
|
+
| DHCP range | `dhcp_range(cidr:, network_id:, start:, end_address:)` | Exact network ID/CIDR; usable endpoints; excludes gateway |
|
|
54
|
+
| Tail policy | `dhcp_range(cidr:, network_id:, reserve_tail: 55)` | Explicit source-business reserve tail; too-small networks rejected |
|
|
55
|
+
| Fixed address | `fixed_address(ip:, mac:)` | Usable non-gateway address; reject another MAC's binding |
|
|
56
|
+
| Reserve address | `reserve_address(ip:)` | Requires existing MAC; preserves that binding |
|
|
57
|
+
|
|
58
|
+
DNS follows the Motor/PDK operation's whole-name zone and apex generic-record
|
|
59
|
+
semantics across configured views. `replace: true` deliberately deletes the
|
|
60
|
+
exact existing zone and its descendants before recreating it. The host must
|
|
61
|
+
authorize that broader destructive intent; use the default conflict rejection
|
|
62
|
+
for ordinary edits. Unrelated records are preserved unless replacement or
|
|
63
|
+
recursive deletion is explicitly requested. The first version supports IPv4,
|
|
64
|
+
A/CNAME and the listed DHCP actions; IPv6 and arbitrary RR types are not implied.
|
|
65
|
+
|
|
66
|
+
Creating a block, network and DHCP range is an explicit sequence of reviewed
|
|
67
|
+
plans: verify the parent first, then plan the child using its actual ID. No
|
|
68
|
+
environment-specific supernet, gateway, server topology or tail policy is
|
|
69
|
+
silently inferred. A DNS plan automatically orders zone creation, roles and
|
|
70
|
+
record creation; subsequent operations pin the verified parent object's ID.
|
|
71
|
+
CIDR input must contain exactly one address and one prefix separated by `/`.
|
|
72
|
+
Extra or trailing slash components are rejected before selecting any object,
|
|
73
|
+
including for network deletion; they are never discarded during normalization.
|
|
74
|
+
|
|
75
|
+
## Writes, recovery and deployment
|
|
76
|
+
|
|
77
|
+
For every operation, `Applier` requires current state to match the reviewed
|
|
78
|
+
precondition, then commits `started`, performs one write, commits its `receipt`,
|
|
79
|
+
reads exact state and commits `verified`. The callback must return literal
|
|
80
|
+
`true` only after durable commit. A callback failure stops the sequence. Persist
|
|
81
|
+
the plan, the complete ordered event stream and the terminal result under an
|
|
82
|
+
exclusive execution ID. Never return `true` from inside an uncommitted outer
|
|
83
|
+
transaction. The gem provides no database implementation or automatic write retry.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
result = applier.recover(
|
|
87
|
+
plan: plan, execution_id: execution_id, events: committed_events,
|
|
88
|
+
checkpoint: durable_checkpoint
|
|
89
|
+
)
|
|
90
|
+
deployment = Netstack::Ddi::Deployment.new(client: client, settings: settings)
|
|
91
|
+
deployment.submit(plan: plan, mutation_result: result, checkpoint: deployment_checkpoint)
|
|
92
|
+
states = deployment.recover(
|
|
93
|
+
plan: plan, execution_id: execution_id, events: deployment_events,
|
|
94
|
+
checkpoint: deployment_checkpoint
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Recovery reads back started writes and never replays them. A matching desired
|
|
99
|
+
object and, when available, matching receipt ID are required. Missing or
|
|
100
|
+
conflicting evidence raises `unknown_result`; the host keeps the intent for
|
|
101
|
+
reconciliation. Empty plans complete without writes; the host persists that
|
|
102
|
+
terminal result separately.
|
|
103
|
+
|
|
104
|
+
Verified configuration writes are separate from deployment submission.
|
|
105
|
+
`Deployment` accepts a matching verified mutation result and submits only the
|
|
106
|
+
plan's DNS/DHCP services to explicit server IDs. Legacy v1 exposes server-wide
|
|
107
|
+
status without a correlation ID. A receipt or an old `DONE` is insufficient:
|
|
108
|
+
completion requires observing an active phase after submission, then `DONE`.
|
|
109
|
+
Lost receipts and missed active transitions remain unknown. The host must
|
|
110
|
+
serialize deployments per server and schedule bounded polling; these APIs do
|
|
111
|
+
not establish causal identity against concurrent administrator deployments.
|
|
112
|
+
Warnings remain a distinct non-success outcome. Never convert unknown to success
|
|
113
|
+
or replay a server submission merely because a job timed out.
|
|
114
|
+
|
|
115
|
+
After an interrupted submission batch, call `submit` with `events:` containing
|
|
116
|
+
the committed deployment stream. It skips servers with receipts and submits
|
|
117
|
+
only servers never started. A started server lacking its receipt stops with
|
|
118
|
+
`unknown_result`; failed or warning deployments stop further submissions.
|
|
119
|
+
`recover` itself only polls and never submits. Active and terminal status
|
|
120
|
+
evidence is also checkpointed. Each server's Configuration ancestry is checked
|
|
121
|
+
before accessing it.
|
|
122
|
+
|
|
123
|
+
The HTTP client verifies TLS by default, enforces response size, per-stage and
|
|
124
|
+
total request budgets, and never follows redirects or retries writes. BlueCat
|
|
125
|
+
credentials and its session token live only in the client instance; inspection
|
|
126
|
+
does not expose them. Invalid JSON, malformed object identities, repeated
|
|
127
|
+
pagination pages, response limits and unknown write receipts fail closed.
|
|
128
|
+
|
|
129
|
+
## Validation and upstream contracts
|
|
130
|
+
|
|
131
|
+
Offline tests cover complete DNS/IPAM/DHCP flows, deliberate replacement,
|
|
132
|
+
protected deletion, stale plans, mismatched receipt IDs, failed checkpoint
|
|
133
|
+
commits, lost receipts, and deployment phase separation. These tests use
|
|
134
|
+
controlled protocol fixtures; no production BAM or DNS/DHCP server was contacted.
|
|
135
|
+
The adapter follows the source system's Legacy v1 API; compatibility with a
|
|
136
|
+
particular BAM release must be checked with its actual responses before rollout.
|
|
137
|
+
|
|
138
|
+
- [BlueCat Legacy v1 API guide](https://docs.bluecatnetworks.com/r/Address-Manager-Legacy-v1-API-Guide/Overview/9.6.0)
|
|
139
|
+
- [Exact CIDR lookup](https://docs.bluecatnetworks.com/r/Address-Manager-Legacy-v1-API-Guide/GET/v1/getEntityByCIDR/9.6.0)
|
|
140
|
+
- [Exact response-policy membership](https://docs.bluecatnetworks.com/r/Address-Manager-Legacy-v1-API-Guide/GET/v1/findResponsePoliciesWithItem/9.6.0)
|
|
141
|
+
- [Deployment submission](https://docs.bluecatnetworks.com/r/Address-Manager-Legacy-v1-API-Guide/POST/v1/deployServerServices/9.6.0)
|
|
142
|
+
- [Deployment status values](https://docs.bluecatnetworks.com/r/Address-Manager-Legacy-v1-API-Guide/Deployment-status/9.6.0)
|
data/docs/deployment.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Execution and recovery
|
|
2
|
+
|
|
3
|
+
`Deployment::Plan` contains one immutable `Target`, ordered business `Step` values, preconditions and metadata. A SHA-256 digest identifies its canonical JSON payload. Persist `plan.payload`; recover with `Plan.from_h`. Do not put credentials or configuration text in a plan: configuration plans contain an artifact reference and digest, and adapters resolve the encrypted artifact only at execution.
|
|
4
|
+
|
|
5
|
+
## Host contract
|
|
6
|
+
|
|
7
|
+
Before calling the executor, the host must durably create an execution identity and claim the target. The claim must exclude concurrent conflicting work, and checkpoints must reject workers that no longer own it. The gem does not own a database, distributed lease or authorization policy.
|
|
8
|
+
|
|
9
|
+
The `checkpoint` callback receives an immutable event with `execution_id`, `plan_digest`, `target_key`, `step_id`, status, observation time and the full versioned `receipt` payload. Commit that payload and any audit record atomically in the host database, then return **true**. Enqueuing another job, writing an ordinary application log or returning a truthy object does not acknowledge durability. A callback inside an uncommitted outer transaction is also insufficient: the remote side cannot be rolled back by that transaction.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
receipt = Netstack::Deployment::Executor.new(adapter: adapter).apply(
|
|
13
|
+
plan: persisted_plan,
|
|
14
|
+
execution_id: execution.id.to_s,
|
|
15
|
+
previous: persisted_receipt && Netstack::Deployment::Receipt.from_h(persisted_receipt),
|
|
16
|
+
credentials: credentials,
|
|
17
|
+
checkpoint: lambda do |event|
|
|
18
|
+
execution.persist_checkpoint!(event, claim_token: claim_token)
|
|
19
|
+
true # persist_checkpoint! must have committed before this point
|
|
20
|
+
end
|
|
21
|
+
)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`persist_checkpoint!` above belongs to the host and must validate the execution, digest, target and current claim under its database concurrency control. Do not copy this example as a callback that blindly updates a record. No network I/O belongs inside a database row lock.
|
|
25
|
+
|
|
26
|
+
## State transitions
|
|
27
|
+
|
|
28
|
+
Each fresh step follows `started` (committed before remote I/O), then `applied` or `accepted`, then readback to `verified`. A transport timeout or malformed response after submission means `unknown`, even when the transport says the connection closed. A verified prefix is not an overall success; every planned step must be verified. An empty plan reports `no_changes` without contacting the device.
|
|
29
|
+
|
|
30
|
+
On restart, `started`, `applied`, `accepted` and `unknown` steps are verified without resubmission. Subsequent steps start only after that readback proves success. `failed` and `not_applied` require review and a deliberate new execution or plan. The executor never resumes at an arbitrary CLI line or replays an uncertain command.
|
|
31
|
+
|
|
32
|
+
`CheckpointError#receipt` preserves the last in-memory observation when a durable acknowledgement fails. It is evidence for recovery; the exception does not claim that the event was committed. The host should reload its durable receipt and perform readback before any further write.
|
|
33
|
+
|
|
34
|
+
## Adapter protocol
|
|
35
|
+
|
|
36
|
+
- `preflight(plan:, previous:, credentials:)` returns exactly true only when target identity, artifact integrity and required baseline conditions are confirmed. Recovery must tolerate already completed planned effects while still detecting unrelated drift.
|
|
37
|
+
- `apply(step:, target:, credentials:, idempotency_key:)` returns a Hash with `status` (`applied`, `accepted`, `failed` or `unknown`) and optional safe `remote_id`, `error_code`, `evidence`.
|
|
38
|
+
- `verify(step:, target:, credentials:, receipt:)` returns `verified`, `failed`, `unknown` or `not_applied` using read-only evidence.
|
|
39
|
+
|
|
40
|
+
HTTP keys are stable for an execution, plan and step. This does not invent server-side idempotency: an HTTP provider must document and honor that contract. Adapter evidence must exclude credentials and raw sensitive response bodies. `save` or platform-specific disruptive operations need their own verifiable step; a successful command acknowledgement alone is insufficient.
|
|
41
|
+
|
|
42
|
+
The executor computes the immutable plan digest and ordered step identities once
|
|
43
|
+
per `apply` invocation. Separate invocations share no cached execution state.
|
|
44
|
+
Checkpoint events still contain the complete receipt, and recovery checks the
|
|
45
|
+
same execution, target, plan, step order and idempotency identities before I/O.
|
|
46
|
+
|
|
47
|
+
Monitoring and DDI have provider-specific journals for object identities and deployment stages. Their documentation describes those journals separately; do not mix their events with `Deployment::Receipt`.
|
data/docs/foundations.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 基础能力复用
|
|
2
|
+
|
|
3
|
+
通用功能优先使用成熟库:IP 地址解析、网络掩码与包含判断来自 Ruby `IPAddr`;对象复制和空白文本判断使用按需加载的 ActiveSupport 扩展;文件加载使用 Zeitwerk。没有全局修改 `IPAddr`、`Set` 或 Rails 配置。
|
|
4
|
+
|
|
5
|
+
`IPAddr` 属于 Ruby 标准库,ActiveSupport 提供的是另外一组扩展。当前复用边界如下:
|
|
6
|
+
|
|
7
|
+
| 通用能力 | 使用的实现 | Netstack 保留的业务部分 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| IP 解析、掩码、包含关系 | `IPAddr` | 设备地址记法、通配掩码、压缩区间与服务编码 |
|
|
10
|
+
| 深复制、空白判断 | 按需加载 ActiveSupport `deep_dup`、`blank` | 值对象不可变、严格类型与范围校验 |
|
|
11
|
+
| 文件与常量加载 | Zeitwerk | 按领域职责划分命名空间 |
|
|
12
|
+
| HTTP、TLS、JSON、摘要 | `Net::HTTP`、OpenSSL、JSON、Digest | 服务协议、身份关联、超时预算和错误契约 |
|
|
13
|
+
| 交互会话与表格解析 | `expect-pty`、TextFSM | 厂商提示符、命令、恢复与采集完整性 |
|
|
14
|
+
|
|
15
|
+
配置模板沿用 Motor 已有的受限语法与变量契约,只解释插值、循环和条件。它不是可执行任意 Ruby 的 ERB 环境;宿主保存的模板不会获得执行 Ruby 代码的权限。
|
|
16
|
+
|
|
17
|
+
## 地址与区间
|
|
18
|
+
|
|
19
|
+
`Netstack::IPv4 < ::IPAddr` 增加设备配置领域所需的十进制前导零、主机 CIDR 保留、通配掩码、地址区间和服务编码。`IPv6` 复用标准 IPAddr 并增加等价的地址区间与 CIDR 输出。两者不自行实现 IP 字符串语法。
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
Netstack::IPv4.host_cidr("192.0.2.10", "255.255.255.0") # "192.0.2.10/24"
|
|
23
|
+
Netstack::IPv4.network_cidr("192.0.2.10/24") # "192.0.2.0/24"
|
|
24
|
+
Netstack::IPv4.parse("192.0.2.10-192.0.2.20") # RangeSet
|
|
25
|
+
Netstack::IPv4.parse("any").cardinality # 4294967296
|
|
26
|
+
Netstack::IPv6.canonical("2001:db8::10/64", host: true)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`RangeSet` 对有序、互不相交的非负整数闭区间做并集 `|`、交集 `&`、差集 `-` 和关系判断。它按区间遍历,不枚举所有 IP。Ruby `Set` 适合离散元素,不能代替覆盖 IPv6 空间的压缩区间集合,因此保留这部分经过回归验证的领域算法。
|
|
30
|
+
|
|
31
|
+
`RangeSet#size` 是区间段数,`#cardinality` 才是元素总数。端点都是非负整数;禁止无穷、倒置或非法区间。对外持久化请使用 JSON 范围端点或 CIDR,不加载不可信 Marshal 数据。
|
|
32
|
+
|
|
33
|
+
## 配置 Reader
|
|
34
|
+
|
|
35
|
+
`Configuration::Reader.new(content, vendor: nil, device_id: nil, device_name: nil, created_at: Time.now.utc)` 接受 UTF-8 原文,保留 CRLF/CR/LF 边界与原始内容,拒绝无效编码和 NUL。它提供游标读取、已解析行标记、未解析行遍历、回退和重置,供厂商解析器共享。
|
|
36
|
+
|
|
37
|
+
Reader 不猜测厂商,也不加载 Algosec 的防火墙策略模块。`checksum` 使用 SHA-256;`parsed_flags` 与未解析行用于保留解析覆盖证据。空白行处理复用 ActiveSupport。来源文件和 SHA-256 记录于根目录 NOTICE。
|
|
38
|
+
|
|
39
|
+
`Reader#inspect` 只显示字节数、行数和摘要,不展开配置正文;读取原始内容仍需显式访问 `content`。
|
|
40
|
+
|
|
41
|
+
## 值对象
|
|
42
|
+
|
|
43
|
+
`Values.copy` 复用 ActiveSupport `deep_dup`,额外施加领域所需的深度不可变约束。它接受 JSON 类型、Time 与本库的 Data 值对象,拒绝无法证明可复制的任意对象;它不会为了冻结结果而冻结调用者持有的原值。
|
|
44
|
+
|
|
45
|
+
边界整数解析仍然显式区分缺失、nil、false、浮点数和十进制文本。ActiveSupport 的便利转换不会代替业务校验,也不会把未知值默认为 0 或 false。
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Implementation record
|
|
2
|
+
|
|
3
|
+
The approved objective is the complete Netstack network business library, with host-owned persistence and no Netdisco implementation. Partial milestones do not constitute completion.
|
|
4
|
+
|
|
5
|
+
## Confirmed choices
|
|
6
|
+
|
|
7
|
+
- Independent Ruby gem; Netstack owns the adapted device Connector implementation.
|
|
8
|
+
- Perl PDK supplies business evidence, not compatibility requirements.
|
|
9
|
+
- No Netdisco client, database access, Fleet, dedicated CLI or synchronization jobs.
|
|
10
|
+
- Host owns authorization, inventory source, credentials, execution checkpoints, target ownership, jobs and recovery scheduling.
|
|
11
|
+
- First usable milestone is direct device and wireless collection, then monitoring, VXLAN, configuration delivery, DDI, external backup and Motor integration.
|
|
12
|
+
- Reuse mature external libraries. ActiveSupport is a runtime dependency for suitable core extensions; IPAddr is Ruby's standard implementation. The gem does not require Rails or ActiveRecord.
|
|
13
|
+
- Absorb Algosec's interval/IP operations and configuration Reader. Netstack names them RangeSet, IPv4, IPv6 and Configuration::Reader without shadowing ::Set or ::IPAddr.
|
|
14
|
+
|
|
15
|
+
## Review follow-up — 2026-10-01
|
|
16
|
+
|
|
17
|
+
The deep review repaired collection completeness and expected Fabric coverage,
|
|
18
|
+
wireless binding identity, monitoring ownership and circuit cleanup, strict input
|
|
19
|
+
and diagnostic boundaries, and bounded template/known-host lock work. Topology
|
|
20
|
+
description writes now use separate durable change and persistence steps with
|
|
21
|
+
artifact verification and readback-only recovery. Inventory reconciliation uses
|
|
22
|
+
identity indexes; Executor reuses identity only within each call.
|
|
23
|
+
|
|
24
|
+
The revised library passes all 664 tests / 7702 assertions on macOS Ruby 3.4.11 and
|
|
25
|
+
4.0.7, 307-file lint, eager loading and isolated installation including the new
|
|
26
|
+
topology API. See [acceptance.md](acceptance.md) for limits and
|
|
27
|
+
[performance.md](performance.md) for reproducible offline measurements. The
|
|
28
|
+
original host/Linux evidence below predates these review changes.
|
|
29
|
+
|
|
30
|
+
## Original delivered scope and evidence — 2026-09-30
|
|
31
|
+
|
|
32
|
+
| Area | Current state |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| Gem, loader, dependency resolution | Implemented; local Bundler resolution, eager loading and installed-package gates succeed on Ruby 3.4.11 and 4.0.7 |
|
|
35
|
+
| Connector | Adapted by connector task; tests cover sessions, partial results, security, locks, backups, parsing and TFTP; source manifest in connector.md |
|
|
36
|
+
| IP/range operations and configuration Reader | Implemented using IPAddr, granular ActiveSupport and the adapted interval/Reader contracts; included in the full passing suite |
|
|
37
|
+
| Inventory and Routing | Typed models, CLI configuration parser, collection, scoped reconciliation and lookup implemented; provider coverage and explicit unsupported dimensions documented in inventory.md |
|
|
38
|
+
| Wireless | H3C/Huawei collection, parsing, strong identities, reconciliation, views and assessment implemented; focused and integration tests pass |
|
|
39
|
+
| Monitoring and Circuits | Zabbix planning/application/recovery, history sampling and business-calendar capacity review implemented; boundary audit and regression checks passed |
|
|
40
|
+
| VXLAN | NX-OS configuration/runtime parsing, Fabric audit, Segment incremental planning and verified deployment implemented; offline tests pass |
|
|
41
|
+
| Deployment | Versioned JSON plans/receipts, strict operation identity, durable-checkpoint executor, CLI and HTTP adapters implemented; offline recovery tests pass |
|
|
42
|
+
| Configuration scenarios | All eight scenarios and eleven bundled templates implemented, with single-target IRF/vPC sections and explicit manual-script artifacts |
|
|
43
|
+
| BlueCat/DDI, Oxidized/Backups | Protocol clients, business operations, object journals and separate deployment/backup verification implemented; configuration ancestry, protected descendants and target identity regressions passed |
|
|
44
|
+
| Motor integration | Actual CLI wireless, configuration generation, HTTP delivery, backup, persistent device monitoring and DDI entrypoints implemented; 83 combined RSpec examples passed, including 34 real PostgreSQL persistence/recovery examples |
|
|
45
|
+
| Documentation, package isolation, full CI/version matrix | Full local macOS/Linux × Ruby 3.4/4.0 gates passed: 614 tests / 7142 assertions per environment, 294 files lint clean, eager load and isolated installed gem with bundled resources verified; remote CI was not run |
|
|
46
|
+
|
|
47
|
+
## Contract coordination
|
|
48
|
+
|
|
49
|
+
- `Netstack::Values.copy` uses ActiveSupport deep copying and enforces deeply immutable accepted values. Boundary parsing still rejects invalid types rather than coercing them silently.
|
|
50
|
+
- `Target.new(key:, host:, platform:, attributes: {})` is an immutable host-supplied identity.
|
|
51
|
+
- `Dimension` distinguishes complete, partial, unsupported, failed and not_requested. An empty array alone does not establish completeness.
|
|
52
|
+
- `Deployment::Plan` owns target, ordered steps, preconditions, metadata and a canonical SHA-256 digest.
|
|
53
|
+
- `Executor#apply(plan:, execution_id:, checkpoint:, credentials:, previous: nil)` requires synchronous checkpoints returning true. The host must persist execution ownership before invoking it.
|
|
54
|
+
- Provider adapter: `preflight(plan:, previous:, credentials:)` returns true, `apply(step:, target:, credentials:, idempotency_key:)` returns a typed status hash, `verify(step:, target:, credentials:, receipt:)` returns a verification status hash.
|
|
55
|
+
- Incomplete execution receipts retain the full planned step identity list. A verified prefix cannot report overall completion.
|
|
56
|
+
- Host checkpoint failures stop further writes. Unknown writes are read back before any further decision and are never replayed automatically.
|
|
57
|
+
- HTTP adapter API is documented by `Integrations::Http::Client` and shared by Zabbix and future integration clients.
|
|
58
|
+
|
|
59
|
+
## Reference trees
|
|
60
|
+
|
|
61
|
+
- net-connector HEAD at start: `63935c03bcd340991075b04faa14741df535d4c1`; current worktree source hashes are required because it includes uncommitted changes.
|
|
62
|
+
- Motor HEAD at start: `46a959ebcfadc35b07d11e1bdfe222f499ddc650`; its worktree also includes uncommitted changes.
|
|
63
|
+
- PDK was inspected from the current crated worktree.
|
|
64
|
+
- Algosec reference checkout has no usable HEAD; source file hashes, rather than an invented commit, identify the absorbed implementations.
|
|
65
|
+
|
|
66
|
+
No production devices or external services have been contacted. Local protocol fixtures and PTY tests establish only their respective boundaries.
|
|
67
|
+
|
|
68
|
+
Final commands, environment versions and delivery limits are recorded in [acceptance.md](acceptance.md). Motor entrypoints and migration requirements are in [motor_integration.md](motor_integration.md). The implementation is local and uncommitted; publication and production routing changes were not performed.
|
data/docs/inventory.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Inventory and Routing
|
|
2
|
+
|
|
3
|
+
`Target` is the host's stable key, management address and connector platform. `Inventory::Snapshot` holds typed dimensions for device, interfaces, transceivers and neighbors; each has its own completeness and observation window. Snapshot completeness means every requested dimension is complete. A request for no dimensions is not a complete observation.
|
|
4
|
+
|
|
5
|
+
## Direct collection
|
|
6
|
+
|
|
7
|
+
`Inventory::Collector#collect(target:, dimensions:, connection_options:, scope:)` collects device identity/version and configured interfaces for H3C, H3C wireless, Huawei, Cisco IOS and Cisco NX-OS. Interface configuration establishes administrative state and configured addresses; it does not establish operational link state. Neighbor collection uses the Connector's advertised capability and parsing strategy.
|
|
8
|
+
|
|
9
|
+
Transceivers have a typed model and reconciliation support, but no generic CLI collection capability is claimed. Supply a complete typed dimension from a dedicated host/provider collector when available; the direct collector reports `unsupported`. Routing has typed snapshot and lookup APIs; provider collection is external to this module. Netdisco integration is deliberately outside this gem.
|
|
10
|
+
|
|
11
|
+
Configuration and version commands are read-only. Input dimensions, scope and target-host overrides are checked before opening a connection. Errors are recorded with safe codes, without including credentials or raw exception text. Failed reads do not become empty complete observations.
|
|
12
|
+
|
|
13
|
+
`scope: { interfaces: ["Ethernet1/1"] }` declares exactly the interface identities collected. Interface identity comparisons normalize case while preserving host display spelling. IPv4 and IPv6 interface addresses preserve host bits; route destinations normalize to network prefixes. Scoped IPv6 text needs an explicit future interface-scope contract and is rejected rather than silently stripped.
|
|
14
|
+
|
|
15
|
+
## Reconciliation
|
|
16
|
+
|
|
17
|
+
`Inventory::Reconciler#call(current:, snapshot:, target:)` returns `Changes`, not database writes. `current` maps dimensions to rows shaped as:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
{ key: "Ethernet1/1", source: "cli", managed_fields: %i[description addresses],
|
|
21
|
+
attributes: { name: "Ethernet1/1", description: "old", addresses: [] } }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Only complete dimensions produce changes. Only fields listed in an existing row's `managed_fields` are updated, and only rows owned by the snapshot source can be updated or removed. Removal is constrained to the declared collection scope. A manually managed row or another provider's row cannot be overwritten merely because its identity matches.
|
|
25
|
+
|
|
26
|
+
Creates and updates retain observation order; removals retain the order of current host rows. Identity indexing preserves these orders and the same source, field ownership, and scope rules for large collections.
|
|
27
|
+
|
|
28
|
+
The host must validate its current device identity and snapshot revision under a short database lock before applying changes. It owns uniqueness constraints, source ownership, collection revision and transaction boundaries. Collect outside the lock, reconcile/apply inside the transaction, and retain old data for partial/failed/unsupported dimensions.
|
|
29
|
+
|
|
30
|
+
## Route lookup
|
|
31
|
+
|
|
32
|
+
`Routing::Route` contains target key, VRF, destination prefix, protocol, optional preference/metric, next hop and interface. `Routing::Snapshot` rejects foreign target keys, foreign VRFs and duplicate routes.
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
result = Netstack::Routing::Lookup.new.call(
|
|
36
|
+
routes: routes, destination: "192.0.2.15", target_key: "leaf-1", vrf: "default"
|
|
37
|
+
)
|
|
38
|
+
result.status # matched, not_found or ambiguous
|
|
39
|
+
result.routes # preserves equal-cost next hops
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Lookup selects the longest prefix, then lower preference and metric. It does not compare metrics across different protocols or invent missing costs. Such cases return `ambiguous` with the candidate routes, rather than an arbitrary winner. VRF, device and address family remain isolated.
|