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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a061a49349ba46f1b10443be9f63d0f33703b101ceb9332f7bdd9b1155c16af7
|
|
4
|
+
data.tar.gz: b1d581a112546d007308ca9ce0300fe51a974c89c998e0d8c718ced72750e7d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b761c5cddf8d87887f4ffac0615fc0e3514e3ebe6d25f185e6fba326d6c4cb6a894348db8fcaf8e334f0a2a56f01eb1b073bdb8c5163488921447fde4d999db
|
|
7
|
+
data.tar.gz: afb5a7bd3f5e2df4ed23e445dea4b1de7df8df7e8590107008ce2dcc134b4018dbf00c23ea427e6f7a511ab4cffc2d80cb5cdc7ce1816912cf46415ebe585bde
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.2.0 — 2026-10-01
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- Replace the legacy 0.1.x API with the standalone Netstack API described in README; this is not a drop-in upgrade.
|
|
6
|
+
- Preserve BlueCat password bytes and compare CNAME targets independently of case and the trailing root dot.
|
|
7
|
+
- Reject overlapping H3C bootstrap access/uplink ranges, unordered ranges and authentication ranges outside the access scope.
|
|
8
|
+
- Require global anycast MAC and bound ACL definitions during VXLAN configuration readback.
|
|
9
|
+
- Merge observed wireless blacklist/whitelist fields by MAC without removing unobserved entries.
|
|
10
|
+
- Reject special files when opening device logs without blocking on FIFOs; add Chinese comments for module ownership and recovery contracts.
|
|
11
|
+
|
|
12
|
+
- Harden topology description writes with separate durable change/persist steps, artifact identity checks, raw-result preservation and readback-only recovery of uncertain writes. The unreleased apply API now requires a Deployment plan and host checkpoint/resolver.
|
|
13
|
+
- Preserve expected VXLAN fabric coverage and mark unrecognized runtime/wireless output partial; reconcile service-template bindings by case-insensitive identity with explicit AP precedence.
|
|
14
|
+
- Preserve disabled monitoring ownership and clean up circuit triggers by host/side, including repeated recovery; canonicalize backup IPv6 association and reject malformed DDI CIDRs.
|
|
15
|
+
- Reject invalid optional configuration types and ambiguous numeric syntax, bound template work and known-host lock waiting, and keep sensitive configuration out of inspection and error metadata.
|
|
16
|
+
- Index inventory reconciliation and reuse execution identity within each Executor call, preserving output order and receipt serialization.
|
|
17
|
+
|
|
18
|
+
- Introduce an independent `Netstack` namespace for device connectivity, observations, network business plans and verified execution.
|
|
19
|
+
- Adapt net-connector's single-device sessions, vendor strategies, TextFSM resources, safe backup and redaction contracts; omit Netdisco and fleet orchestration.
|
|
20
|
+
- Reuse ActiveSupport and standard IPAddr; absorb Algosec interval operations and configuration Reader under responsibility-based names.
|
|
21
|
+
- Add typed inventory, routing, wireless, VXLAN, monitoring/circuits, configuration, DDI and backup modules.
|
|
22
|
+
- Separate complete/partial/unsupported observations and host-owned persistence. Require durable checkpoints before remote writes and readback for uncertain execution.
|
|
23
|
+
- Add offline regression, Zeitwerk, lint, package isolation and Ruby/platform CI gates. Live-device and external-service acceptance remain separate.
|
|
24
|
+
- Provide explicit Motor integration for wireless collection, configuration generation/delivery, external backup, and PostgreSQL-backed monitoring/DDI execution with target ownership and durable recovery.
|
data/{LICENSE.txt → LICENSE}
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 netstack contributors
|
|
4
|
+
Copyright (c) 2026 net-connector contributors
|
|
4
5
|
|
|
5
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
7
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -9,13 +10,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
9
10
|
copies of the Software, and to permit persons to whom the Software is
|
|
10
11
|
furnished to do so, subject to the following conditions:
|
|
11
12
|
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
14
15
|
|
|
15
16
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
17
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
18
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
19
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
20
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
data/NOTICE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Netstack includes adapted device connection code and templates from net-connector.
|
|
2
|
+
Copyright (c) 2026 net-connector contributors. Licensed under the MIT License.
|
|
3
|
+
The original copyright and permission notice is retained in LICENSE.
|
|
4
|
+
|
|
5
|
+
Domain design draws on local Motor and PDK business implementations. Perl APIs,
|
|
6
|
+
Netdisco integrations, and application authentication are not included.
|
|
7
|
+
|
|
8
|
+
RangeSet, IPv4 and Configuration::Reader adapt algorithms from the user's
|
|
9
|
+
algosec-rails/lib/algosec tree. The original project is not a runtime dependency.
|
|
10
|
+
Source SHA-256 at adaptation:
|
|
11
|
+
ipaddr.rb: a1287b0e4e87399dfc4becb38982429136c2fb350829c7de6aacfd2e8069bf68
|
|
12
|
+
set.rb: a2bb791073b930e4931e1333ff74ea804c6d5ea71334415a7a74cbe449625c6d
|
|
13
|
+
config/reader.rb: f93f34f37bff121b8321bdb170b6595e8557b2988b7160f4473746144caf3d57
|
|
14
|
+
Netstack uses Ruby IPAddr and granular ActiveSupport extensions. It introduces
|
|
15
|
+
no global IPAddr or Set patch. Reader uses SHA-256, and bare IPv4 "any" denotes
|
|
16
|
+
the entire IPv4 space; these are intentional new APIs, not compatibility aliases.
|
|
17
|
+
|
|
18
|
+
Configuration scenarios and restricted template syntax adapt Motor's
|
|
19
|
+
backend/app/services/motor/netconf and backend/app/views/netconf implementations.
|
|
20
|
+
The adapted templates are packaged under resources/configuration. Host database,
|
|
21
|
+
Redis, authorization, spreadsheet uploads and notification code are not bundled.
|
|
22
|
+
|
|
23
|
+
Netstack-specific hardening adds durable interface-description execution and safe
|
|
24
|
+
configuration inspection. The source hashes above identify the original adaptation
|
|
25
|
+
inputs, not the current modified library files; attribution remains unchanged.
|
data/README.md
CHANGED
|
@@ -1,43 +1,81 @@
|
|
|
1
1
|
# Netstack
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Netstack 是独立 Ruby 网络业务库。它从 Motor、PDK 和 net-connector 的现有实现提取设备连接、网络观察、规划、配置生成与执行验证;宿主应用负责身份权限、数据库、凭据、任务队列和执行所有权。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
要求 Ruby 3.4+。运行时复用 ActiveSupport、Ruby IPAddr、Zeitwerk、expect-pty 和 TextFSM,不依赖 Rails、ActiveRecord、PostgreSQL 或 Netdisco。
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 使用
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
安装 RubyGems 版本:
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
|
-
gem
|
|
12
|
+
gem "netstack", "~> 0.2.0"
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
0.2.0 使用本文描述的独立 `Netstack` API,不兼容旧 0.1.x API;升级前需要调整调用代码。开发时也可显式引用本地工作树:
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
```ruby
|
|
18
|
+
gem "netstack", path: "/path/to/netstack"
|
|
19
|
+
```
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
```ruby
|
|
22
|
+
require "netstack"
|
|
23
|
+
|
|
24
|
+
target = Netstack::Target.new(
|
|
25
|
+
key: "leaf-1", host: "192.0.2.10", platform: :cisco_nxos
|
|
26
|
+
)
|
|
27
|
+
snapshot = Netstack::Inventory::Collector.new.collect(
|
|
28
|
+
target: target,
|
|
29
|
+
dimensions: %i[device interfaces],
|
|
30
|
+
connection_options: { username: username, password: password }
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
snapshot.dimensions.each do |name, dimension|
|
|
34
|
+
# 只有 complete 的对应范围可以用于完整替换;其余状态保留旧数据。
|
|
35
|
+
puts "#{name}: #{dimension.status}"
|
|
36
|
+
end
|
|
37
|
+
```
|
|
20
38
|
|
|
21
|
-
|
|
39
|
+
示例地址使用文档网段。真实调用需要显式传入设备、凭据和信任配置;`require "netstack"` 与 `Netstack.eager_load!` 本身不连接外部系统。
|
|
22
40
|
|
|
23
|
-
##
|
|
41
|
+
## 模块分布
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
| 路径 / 命名空间 | 职责 |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `connector/` / `Connector` | 单设备 SSH/Telnet 会话、厂商命令、配置备份、邻居、TextFSM |
|
|
46
|
+
| `inventory/` / `Inventory` | 设备、接口、地址、光模块、邻居值对象,CLI 采集及按来源对账 |
|
|
47
|
+
| `routing/` / `Routing` | 路由快照、VRF/地址族隔离、最长前缀、优先级和 ECMP 查询 |
|
|
48
|
+
| `wireless/` / `Wireless` | AP、Radio、BSS、Client、配置与运行状态、完整性和质量评估 |
|
|
49
|
+
| `vxlan/` / `Vxlan` | NX-OS 配置/运行观察、Fabric 审计、Segment 增量规划与验证 |
|
|
50
|
+
| `monitoring/`, `circuits/` | Zabbix 设备/链路监控规划、测量、工作日容量评估 |
|
|
51
|
+
| `configuration/` / `Configuration` | 配置读取、业务输入校验、受限模板、配置工件和场景生成 |
|
|
52
|
+
| `deployment/` / `Deployment` | 版本化计划、持久检查点、执行收据、未知结果恢复 |
|
|
53
|
+
| `ddi/`, `backups/` | DNS/DHCP/IPAM 编排、外部配置备份访问 |
|
|
54
|
+
| `integrations/` | HTTP、Zabbix、BlueCat、Oxidized 与 HTTP 配置交付协议 |
|
|
55
|
+
| `ipv4.rb`, `ipv6.rb`, `range_set.rb` | 标准 IPAddr 上的领域地址与压缩区间运算 |
|
|
26
56
|
|
|
27
|
-
|
|
57
|
+
文件名对应 Zeitwerk 常量名。持久化与授权不下沉到领域对象;协议差异留在厂商或集成模块中。CLI 不支持的采集维度会明确返回 `unsupported`,不会伪造空的完整快照。
|
|
28
58
|
|
|
29
|
-
|
|
59
|
+
## 业务边界
|
|
30
60
|
|
|
31
|
-
|
|
61
|
+
- 完整、部分、失败、不支持、未请求分别记录。完整性按维度和范围判断,避免部分采集删除有效旧数据。
|
|
62
|
+
- 计划可以离线检查和持久化;执行前必须有宿主的持久意图、目标所有权和同步检查点。
|
|
63
|
+
- 提交、接受、外部效果确认是不同事实。未知执行先只读验证,禁止自动重放不确定 CLI 操作。
|
|
64
|
+
- 接口描述变更也使用 `Deployment::Plan`:配置和保存分别先提交持久检查点;私有命令工件由引用解析并校验 SHA-256。调用方式见 [Connector](docs/connector.md)。
|
|
65
|
+
- 凭据运行时注入;配置原文、备份原文和执行原始输出由宿主按敏感数据管理,不作为普通日志。
|
|
66
|
+
- 不提供 Netdisco 数据库/HTTP 实现、Fleet、专用 CLI、宿主账号/RBAC 或通知发送。
|
|
32
67
|
|
|
33
|
-
|
|
68
|
+
详见 [执行与恢复](docs/deployment.md)、[设备采集与路由](docs/inventory.md)、[基础对象复用](docs/foundations.md)、[Connector](docs/connector.md)、[无线](docs/wireless.md)、[VXLAN](docs/vxlan.md)、[监控](docs/monitoring.md)、[配置生成与交付](docs/configuration.md)、[DDI](docs/ddi.md)、[外部备份](docs/backups.md) 和 [实施记录](docs/implementation.md)。
|
|
34
69
|
|
|
35
|
-
|
|
70
|
+
## 开发验证
|
|
36
71
|
|
|
37
|
-
|
|
72
|
+
```sh
|
|
73
|
+
bundle install
|
|
74
|
+
bundle exec rake ci
|
|
75
|
+
```
|
|
38
76
|
|
|
39
|
-
|
|
77
|
+
`ci` 包含 eager load、Minitest、RuboCop、gem 构建和独立 GEM_HOME 安装验证。`script/verify_package.rb` 仅使用 Bundler 已解析的本地 gem 缓存;缺失依赖缓存时先执行 `bundle cache`。该验证不访问生产设备、Zabbix、BlueCat 或 Oxidized。
|
|
40
78
|
|
|
41
|
-
|
|
79
|
+
CI 配置覆盖 Linux/macOS、Ruby 3.4/4.0。配置矩阵并不代表远端 CI 或真实平台验收已经完成,实际结果见实施记录。Motor 的显式接入入口与独立 bundle 见 [宿主接入](docs/motor_integration.md)。
|
|
42
80
|
|
|
43
|
-
|
|
81
|
+
源实现与改编说明见 [NOTICE](NOTICE)。版本变更见 [CHANGELOG](CHANGELOG.md)。
|
data/docs/acceptance.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Release acceptance — 0.2.0
|
|
2
|
+
|
|
3
|
+
The release preparation run on macOS arm64 / Ruby 4.0.6 passed
|
|
4
|
+
`bundle exec rake ci`: 675 tests / 7752 assertions, zero failures/errors/skips,
|
|
5
|
+
307 Ruby files lint clean, eager loading and isolated installation with bundled
|
|
6
|
+
templates. All six follow-up findings have regression coverage observed failing
|
|
7
|
+
before repair and passing afterwards. Host and live-provider validation were not
|
|
8
|
+
rerun; the historical results below do not prove current host integration.
|
|
9
|
+
|
|
10
|
+
Release CI runs Ruby 3.4/4.0 on Ubuntu/macOS. After all four jobs pass, a dedicated
|
|
11
|
+
package job verifies and uploads the exact gem and SHA256SUMS for publication.
|
|
12
|
+
Remote run and artifact provenance are recorded in the GitHub Release.
|
|
13
|
+
|
|
14
|
+
## Historical review acceptance — 2026-10-01
|
|
15
|
+
|
|
16
|
+
The approved reliability/performance review is implemented in Netstack only. The
|
|
17
|
+
working tree at that checkpoint was uncommitted with no HEAD. Original files were preserved and
|
|
18
|
+
compared with a separate pre-review source snapshot; no Motor files were changed.
|
|
19
|
+
|
|
20
|
+
| Environment | Behavioral suite | Other gates |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| macOS arm64, Ruby 3.4.11 | 664 tests, 7702 assertions; zero failures/errors/skips | Zeitwerk, 307 Ruby files lint clean, isolated installed gem passed |
|
|
23
|
+
| macOS arm64, Ruby 4.0.7 | 664 tests, 7702 assertions; zero failures/errors/skips | Zeitwerk, 307 Ruby files lint clean, isolated installed gem passed |
|
|
24
|
+
|
|
25
|
+
Both runs executed `bundle exec rake ci`. Ruby 3.4 used the existing isolated
|
|
26
|
+
`BUNDLE_PATH=tmp/ruby34-bundle`. Package verification now also exercises the public
|
|
27
|
+
topology artifact → Deployment plan → checkpointed change/readback/persist API
|
|
28
|
+
from the installed gem, with its bundled templates. New regressions were first
|
|
29
|
+
observed failing against the earlier implementation, then passed after repair.
|
|
30
|
+
|
|
31
|
+
The review fixes incomplete collection coverage, binding/ownership reconciliation,
|
|
32
|
+
checkpointed topology writes, input and diagnostic boundaries, and bounded local
|
|
33
|
+
lock/template work. Cross-review also caught an unknown port-channel member token
|
|
34
|
+
and preserved the existing multiline interface-input contract. Reproducible timing
|
|
35
|
+
and allocation measurements are in [performance.md](performance.md).
|
|
36
|
+
|
|
37
|
+
This review did not rerun Linux/Docker, Motor/PostgreSQL host integration or remote
|
|
38
|
+
CI, and contacted no live device/provider. The older integration results below
|
|
39
|
+
are historical evidence for the original implementation, not revalidation of
|
|
40
|
+
these changes. Host persistence and real-device save confirmation remain separate
|
|
41
|
+
acceptance requirements. No commit, push, publication or deployment was performed.
|
|
42
|
+
|
|
43
|
+
# Original integration acceptance — 2026-09-30
|
|
44
|
+
|
|
45
|
+
The approved implementation is complete in the local working trees. Netstack is an unreleased 0.1.0 gem; this repository has no commit yet. Motor integration adds explicit entrypoints and preserves the existing production dependency source and job/controller defaults.
|
|
46
|
+
|
|
47
|
+
## Library gates
|
|
48
|
+
|
|
49
|
+
| Environment | Behavioral suite | Other gates |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| macOS arm64, Ruby 3.4.11 | 614 tests, 7142 assertions; zero failures/errors/skips | Eager load, RuboCop, isolated installed package passed |
|
|
52
|
+
| macOS arm64, Ruby 4.0.7 | 614 tests, 7142 assertions; zero failures/errors/skips | Eager load, RuboCop, isolated installed package passed |
|
|
53
|
+
| Linux aarch64, Ruby 3.4.11, `ruby:3.4-bookworm` | 614 tests, 7142 assertions; zero failures/errors/skips | Eager load, RuboCop, isolated installed package passed |
|
|
54
|
+
| Linux aarch64, Ruby 4.0.7, `ruby:4.0-bookworm` | 614 tests, 7142 assertions; zero failures/errors/skips | Eager load, RuboCop, isolated installed package passed |
|
|
55
|
+
|
|
56
|
+
The project gate is `bundle exec rake ci`. It checks Zeitwerk, the full Minitest suite, 294 Ruby files with RuboCop, and a real `.gem` installed under a fresh GEM_HOME outside the source tree. The installed check loads all constants, parses all TextFSM templates, loads all configuration templates, and rejects Rails/ActiveRecord/Netdisco runtime coupling. The macOS 3.4 final run used the equivalent `rake test lint zeitwerk:check` plus `rake package:verify` separately to avoid concurrent writes to the shared package output.
|
|
57
|
+
|
|
58
|
+
Docker gates use a copied source tree, with the host source mounted read-only. The dependency set includes ActiveSupport 8.1.4, expect-pty 0.6.1 and TextFSM 0.2.0. Package warnings concern the absent homepage and, on older RubyGems, open-ended dependency requirements; no repository URL or unsupported upper version bound was invented to suppress them.
|
|
59
|
+
|
|
60
|
+
## Motor gates
|
|
61
|
+
|
|
62
|
+
Actual Motor source was copied into a separate container with Ruby 4.0.6, Rails 8.1.3.1, PostgreSQL 18 and Redis 8. `Gemfile.netstack` explicitly selected the local gem. Tests used a dedicated database, not the user's existing database.
|
|
63
|
+
|
|
64
|
+
| Gate | Result |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| Wireless CLI integration | 17 RSpec examples passed |
|
|
67
|
+
| Configuration generator contract | 16 RSpec examples passed |
|
|
68
|
+
| HTTP delivery through the existing executor | 11 RSpec examples passed |
|
|
69
|
+
| Oxidized repository integration | 5 RSpec examples passed |
|
|
70
|
+
| Durable device monitoring | 17 RSpec examples passed |
|
|
71
|
+
| Durable BlueCat DDI | 17 RSpec examples passed |
|
|
72
|
+
| All six entrypoints in one invocation | 83 examples, zero failures |
|
|
73
|
+
| New/changed integration Ruby files | 17 files, RuboCop clean |
|
|
74
|
+
| Original Motor bundle eager loading | `rails zeitwerk:check` passed without adding Netstack to that bundle |
|
|
75
|
+
| Existing executor and Oxidized behavior under the original bundle | 16 RSpec examples passed |
|
|
76
|
+
|
|
77
|
+
The 34 monitoring/DDI examples commit real transactions. A separate PostgreSQL connection verifies checkpoint visibility before provider writes, active ownership uniqueness, session-lock contention and release, configuration row locks and absence of a transaction during provider I/O. They also cover connection replacement, same-session recursion, delayed checkpoints, endpoint/revision drift, fresh-service recovery, lost receipts and sensitive-data exclusion. Provider responses are controlled fixtures.
|
|
78
|
+
|
|
79
|
+
The new migration was executed against the isolated database. Its generated schema was compared with the existing Motor working schema; only the version, new execution table/constraints and two foreign keys differed. Those generated changes were copied back while preserving prior Motor work. RailsAdmin excludes the execution model from generic CRUD.
|
|
80
|
+
|
|
81
|
+
Motor's runtime set uses ActiveSupport 8.1.3.1 and TextFSM 0.2.1; eager loading and every bundled template also passed under that set. The library's Minitest suite uses its own development bundle. An extra attempt to run it through Motor's RSpec-oriented bundle stopped at the missing `minitest/mock` development dependency before any assertions; Motor dependencies were not changed to disguise that setup mismatch.
|
|
82
|
+
|
|
83
|
+
Reproduction commands and the six explicit spec paths are in [motor_integration.md](motor_integration.md). Motor's own persistence guide is `docs/NETSTACK_EXECUTIONS.md` in its repository.
|
|
84
|
+
|
|
85
|
+
## Delivery limits
|
|
86
|
+
|
|
87
|
+
- No production devices, Zabbix, BlueCat, Oxidized or HTTP delivery service were contacted. No remote CI run, commit, push, gem publication or production deployment was performed.
|
|
88
|
+
- The OS/version matrix above is local ARM evidence. The GitHub Actions matrix is configured but was not run remotely.
|
|
89
|
+
- Default Motor jobs/controllers have not been switched. A target must leave its old writer before enabling the new durable entrypoints. Authorization, approval, queue scheduling and manual resolution remain host responsibilities.
|
|
90
|
+
- Device/vendor and protocol coverage is stated in each domain document. Unsupported dimensions and uncertain external effects remain explicit; no empty snapshot or accepted request is treated as proof of completion.
|
|
91
|
+
|
|
92
|
+
The implementation and cross-review completion record is [implementation.md](implementation.md); mature-library reuse is documented in [foundations.md](foundations.md).
|
data/docs/backups.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Configuration backups and Oxidized
|
|
2
|
+
|
|
3
|
+
`Integrations::Oxidized::Client` implements node inventory, current configuration,
|
|
4
|
+
version history, selected-version diff and backup queue requests. `Backups::Repository`
|
|
5
|
+
adds target association and version ownership. HTTP defaults verify TLS, enforce
|
|
6
|
+
byte/time budgets and disable redirects and retries. There is no implicit
|
|
7
|
+
Netdisco, Rails, environment configuration or filesystem backup store.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
client = Netstack::Integrations::Oxidized::Client.new(base_url: "https://backup.example.test")
|
|
11
|
+
repository = Netstack::Backups::Repository.new(client: client)
|
|
12
|
+
node = repository.node!(target: target, aliases: ["edge.example.test"])
|
|
13
|
+
configuration = repository.configuration(node: node)
|
|
14
|
+
versions = repository.versions(node: node)
|
|
15
|
+
diff = repository.diff(node: node, from_oid: older_oid, to_oid: newer_oid)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Node matching uses explicit target identities and aliases; ambiguity raises an
|
|
19
|
+
error. Version identifiers must be distinct and both belong to the selected
|
|
20
|
+
node. Address matching canonicalizes IPv4/IPv6 notation on both sides; equivalent
|
|
21
|
+
compressed and expanded IPv6 addresses still participate in ambiguity checks.
|
|
22
|
+
Invalid rows are errors, not silently discarded. Node/group path segments
|
|
23
|
+
and query values are encoded separately. `node` returns nil when no match exists;
|
|
24
|
+
`node!` raises `node_not_found`.
|
|
25
|
+
|
|
26
|
+
Configuration and diff return immutable `Artifact`/`Diff` objects. Inspection
|
|
27
|
+
shows only size and SHA-256, and never returns original bytes. Deliberate access
|
|
28
|
+
uses `artifact.content` or `diff.lines`; the host controls authorization,
|
|
29
|
+
encryption, retention, download responses and audit logging. Do not log explicit
|
|
30
|
+
content merely because the wrapper's default inspection is safe. Line endings
|
|
31
|
+
in raw configuration are preserved.
|
|
32
|
+
|
|
33
|
+
## Requesting and verifying a backup
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
request = Netstack::Backups::Request.new(repository: repository)
|
|
37
|
+
receipt = request.call(
|
|
38
|
+
target: target, aliases: [], execution_id: execution_id,
|
|
39
|
+
checkpoint: durable_checkpoint
|
|
40
|
+
)
|
|
41
|
+
result = request.recover(
|
|
42
|
+
execution_id: execution_id, events: committed_events,
|
|
43
|
+
checkpoint: durable_checkpoint
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The host persists an authorized intent and holds an exclusive execution lock
|
|
48
|
+
before `call`. The callback must synchronously commit the event and return
|
|
49
|
+
literal `true`. `started` precedes the queue request; `receipt` means only that
|
|
50
|
+
Oxidized acknowledged it. Keep the complete event stream under the execution ID;
|
|
51
|
+
recovery rejects events from another execution and never submits another backup.
|
|
52
|
+
|
|
53
|
+
Checkpoints bind the target key/host/platform/serial and the node's name, group, IP,
|
|
54
|
+
full name and model. A same-name node replacement does not confirm the original
|
|
55
|
+
request. Legacy or incomplete checkpoints lacking that fingerprint cannot
|
|
56
|
+
establish completion and require reconciliation.
|
|
57
|
+
|
|
58
|
+
Oxidized Web's `/node/next` is a side-effectful GET and its implementation ignores
|
|
59
|
+
group. Requests therefore require a globally unique node name even if a read
|
|
60
|
+
can be selected by group. An HTTP timeout is an unknown queue result, not an
|
|
61
|
+
instruction to retry. The low-level client's `request_backup` is a transport
|
|
62
|
+
primitive; application writes use `Backups::Request` and the durable checkpoint.
|
|
63
|
+
|
|
64
|
+
Completion requires a fresh successful attempt whose start is on or after the
|
|
65
|
+
recorded request, whose end follows its start, and whose completion differs from
|
|
66
|
+
the baseline. An unchanged configuration can still complete, without requiring
|
|
67
|
+
a new Git commit. Old success, an attempt that started before the request,
|
|
68
|
+
missing timing evidence and an in-progress attempt remain pending. A fresh
|
|
69
|
+
failed attempt raises `backup_failed`. The host schedules polling with a finite
|
|
70
|
+
deadline and preserves unresolved results for reconciliation.
|
|
71
|
+
|
|
72
|
+
This freshness check assumes synchronized host/Oxidized clocks. Oxidized supplies
|
|
73
|
+
no per-request queue ID: fresh successful observation proves a subsequent backup,
|
|
74
|
+
not causal ownership against concurrent scheduled or administrator requests.
|
|
75
|
+
The client accepts ISO 8601 timestamps and upstream's `Time#to_s` UTC serialization;
|
|
76
|
+
invalid timestamps are rejected. No API response is interpreted as a backup
|
|
77
|
+
completion merely because it is HTTP 200.
|
|
78
|
+
|
|
79
|
+
## Motor integration
|
|
80
|
+
|
|
81
|
+
Motor has an opt-in `Motor::Devices::NetstackRepository` facade at
|
|
82
|
+
`backend/app/services/motor/devices/netstack_repository.rb`. Construct it with a
|
|
83
|
+
real `Netstack::Backups::Repository`; it preserves Motor's read-response shapes
|
|
84
|
+
and enabled configuration control. It exposes checkpoint-requiring backup
|
|
85
|
+
requests and recovery and verifies recovery belongs to the device. Existing
|
|
86
|
+
`OxidizedConfiguration` remains the default path until explicitly migrated.
|
|
87
|
+
|
|
88
|
+
Use Motor's independent `Gemfile.netstack` integration bundle with explicit
|
|
89
|
+
`NETSTACK_PATH`. The integration RSpec exercises the actual gem through HTTP
|
|
90
|
+
fixtures. Motor retains route authorization, encrypted settings, jobs and
|
|
91
|
+
durable execution storage. A test callback collecting events in memory is not
|
|
92
|
+
a production persistence implementation.
|
|
93
|
+
|
|
94
|
+
Offline tests cover node ambiguity, unsafe queue identity, invalid protocol
|
|
95
|
+
rows, version ownership, raw-byte handling, checkpoint failure and stale/fresh
|
|
96
|
+
backup evidence. Motor integration tests validate the facade with its actual
|
|
97
|
+
Rails models and the gem. No live Oxidized service or network device was used.
|
|
98
|
+
|
|
99
|
+
- [Oxidized Web routes and queue behavior](https://github.com/ytti/oxidized-web/blob/master/lib/oxidized/web/webapp.rb)
|
|
100
|
+
- [Oxidized node serialization](https://github.com/ytti/oxidized/blob/master/lib/oxidized/node.rb)
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Configuration generation and delivery
|
|
2
|
+
|
|
3
|
+
`Netstack::Configuration` extracts Motor Netconf's input contracts, validation,
|
|
4
|
+
payload construction and 11 safe templates into a Ruby library. It does not own
|
|
5
|
+
users, authorization, encrypted settings, template versions, jobs, artifact storage,
|
|
6
|
+
ZIP archives, email or retention. Those stay in the host application.
|
|
7
|
+
|
|
8
|
+
| Scenario | Vendors | Included behavior |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| `bootstrap` | H3C, Cisco NX-OS | Management, AAA/TACACS, NTP/DNS/syslog/SNMP, users, security controls; H3C access/aggregation/core and RADIUS admission; NX-OS leaf/spine/border, OSPF underlay, BGP EVPN/NVE, external peers and optional vPC |
|
|
11
|
+
| `h3c-irf` | H3C | Master links, standby renumber/reboot, standby links, BFD MAD addressing and inspection commands |
|
|
12
|
+
| `distributed-gateway` | Cisco NX-OS | VLAN/VNI, EVPN, VRF SVI/gateway/ACL/MTU, NVE BGP replication |
|
|
13
|
+
| `cisco-n9k-vpc` | Cisco NX-OS | Separate primary/secondary roles, keepalive and virtual peer-link, peer port-channel, fabric ports, infrastructure VLAN |
|
|
14
|
+
| `server-port` | H3C, Cisco NX-OS | Standalone/IRF/vPC, access/trunk/ranges, explicit or automatic aggregates and member consistency |
|
|
15
|
+
| `hillstone-policy` | Hillstone | Zones, IPv4/CIDR/ranges/Any, TCP/UDP ports/ranges/Any, address and service groups, ordered permit/deny rules and logging |
|
|
16
|
+
| `load-balancer` | Radware | Real servers, groups, VIP services, proxy IP, port translation, health checks, timeout/persistence and explicit host routing |
|
|
17
|
+
| `ipsec-vpn` | Palo Alto, Hillstone | IKEv1/v2, proposals, vendor crypto options, peer/PSK, subnet cross-product selectors, tunnel, optional routing and DPD |
|
|
18
|
+
|
|
19
|
+
The existing Motor scenario/vendor names and nested input fields are preserved.
|
|
20
|
+
`Catalog.all`, `Catalog.variables_for_template` and `InputContract.fields_for`
|
|
21
|
+
describe them. Cisco templates are NX-OS-specific; they do not advertise IOS support.
|
|
22
|
+
Historical cryptographic options remain expressible for existing configurations;
|
|
23
|
+
the catalog is not a recommendation to choose those algorithms for new tunnels.
|
|
24
|
+
|
|
25
|
+
## Generate and inspect
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
require "netstack"
|
|
29
|
+
|
|
30
|
+
artifact = Netstack::Configuration::Generator.call(
|
|
31
|
+
scenario: "distributed-gateway", vendor: "cisco",
|
|
32
|
+
input: { gateways: [{ vlan: 100, subnet: "10.100.0.0/24", vrf: "APP" }] }
|
|
33
|
+
)
|
|
34
|
+
artifact.summary # digest, size, filename, scenario, vendor, timestamp
|
|
35
|
+
artifact.content # explicit access to potentially secret configuration text
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Bootstrap requires explicit `system_config:` (or a complete `input[:system]`):
|
|
39
|
+
domain, timezone, management VLAN, NTP/DNS/syslog/TACACS lists and credentials,
|
|
40
|
+
SNMP community, SSH ACLs, local users and security controls. RADIUS is additionally
|
|
41
|
+
required when H3C admission is enabled. Blank secret overrides preserve a saved
|
|
42
|
+
secret only from the supplied defaults; a new username never borrows another
|
|
43
|
+
user's password. `SystemConfiguration.frontend_defaults` removes secret values.
|
|
44
|
+
Only omitted values, `nil` and blank text select optional defaults; `false`, arrays
|
|
45
|
+
and objects are rejected where a text or numeric value is required. Secret overrides
|
|
46
|
+
must be strings even when their optional feature is disabled.
|
|
47
|
+
|
|
48
|
+
H3C access and authentication ranges must use ordered physical ports on the same
|
|
49
|
+
interface type/member/slot. Uplinks must be outside both ranges; the authentication
|
|
50
|
+
range must be inside the access range. Invalid topology fails before rendering.
|
|
51
|
+
|
|
52
|
+
Radware generation requires `settings: { next_hop: "192.0.2.1" }`; its route metric
|
|
53
|
+
defaults to 1 and may be supplied explicitly. The Motor installation's original
|
|
54
|
+
`10.20.0.1` is deliberately not a universal library default. IPsec accepts explicit
|
|
55
|
+
`virtual_router`, `route_metric`, `dpd_interval`, `dpd_retry`, `hillstone_sa_index`
|
|
56
|
+
and `tunnel_zone` in addition to the original Motor fields.
|
|
57
|
+
|
|
58
|
+
For bootstrap batch input, pass `{ devices: [...], system: {...} }`. All devices
|
|
59
|
+
are validated together (including unique hostname and management address) before
|
|
60
|
+
returning a frozen array of individual artifacts. The host may archive them.
|
|
61
|
+
`Generator.new(clock: ...)` supplies deterministic timestamps and policy object names.
|
|
62
|
+
|
|
63
|
+
`h3c-irf` and `cisco-n9k-vpc` also generate their full review document, but that
|
|
64
|
+
document cannot be directly planned for execution. Select the explicit section:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
vpc.section("primary") # or secondary
|
|
68
|
+
irf.section("master") # standby-renumber, standby-links, mad
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
IRF standby renumber contains a reboot. The host schedules the correct physical
|
|
72
|
+
target for each section, accounts for reconnect/renumbering, and supplies verification.
|
|
73
|
+
Automatic rollback, multi-node atomicity, save/commit insertion, and reboot recovery
|
|
74
|
+
are not inferred. Built-in templates preserve their explicit save commands; Radware
|
|
75
|
+
apply and Palo Alto commit are left to the reviewed workflow. A generated script is
|
|
76
|
+
not evidence that a device supports every command or that its runtime converged.
|
|
77
|
+
|
|
78
|
+
## Templates
|
|
79
|
+
|
|
80
|
+
`TemplateStore.new(overrides: { "h3c-bootstrap.erb" => source })` captures an
|
|
81
|
+
immutable template snapshot. Template digests travel with artifacts. Only catalog
|
|
82
|
+
filenames are accepted; arbitrary file paths are rejected. The `.erb` extension
|
|
83
|
+
denotes a restricted dialect inherited from Motor:
|
|
84
|
+
|
|
85
|
+
```erb
|
|
86
|
+
<% policies.each do |policy| %>
|
|
87
|
+
<% if policy.log %>log session-end<% else %>no log<% end %>
|
|
88
|
+
description <%= policy.description %>
|
|
89
|
+
<% end %>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Only scalar interpolation, hash paths, array loops and if/else/end are interpreted.
|
|
93
|
+
There is no Ruby evaluation, method dispatch, arbitrary expression, require, or ERB
|
|
94
|
+
engine. Unknown roots, missing fields, malformed blocks and invalid collection types
|
|
95
|
+
fail. Source, output, nesting and rendering work are bounded. The work budget counts
|
|
96
|
+
every visited syntax node and loop iteration, including empty output and empty loop
|
|
97
|
+
bodies. Hosts still authorize template edits: literal text can intentionally contain
|
|
98
|
+
any device command.
|
|
99
|
+
Custom templates must include their appropriate configuration-mode transitions.
|
|
100
|
+
|
|
101
|
+
The validator rejects fractional integer coercion, unknown/duplicate keys, invalid
|
|
102
|
+
address/range/aggregate relationships, nonboolean flags, command control characters,
|
|
103
|
+
and quotes/backslashes in interpolated description/secret fields. Secret values use
|
|
104
|
+
8–128 printable non-space ASCII characters excluding quotes, backslash, semicolon
|
|
105
|
+
and backtick. Callers receive field-only errors, without input values.
|
|
106
|
+
VLAN lists/ranges, numeric service ports and decimal OSPF areas use decimal syntax;
|
|
107
|
+
leading zeroes are normalized before rendering. Hexadecimal, signed and underscored
|
|
108
|
+
forms are rejected. `Validator#inspect` omits supplied values.
|
|
109
|
+
|
|
110
|
+
## Durable plans and generic CLI execution
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
target = Netstack::Target.new(key: "leaf1", host: "192.0.2.10", platform: "cisco_nxos")
|
|
114
|
+
plan = artifact.plan(target: target, artifact_ref: "encrypted-store:artifact-42",
|
|
115
|
+
expected: { vlan: 100, vni: 10100, gateway: "10.100.0.254/24" })
|
|
116
|
+
|
|
117
|
+
adapter = Netstack::Deployment::CliAdapter.new(
|
|
118
|
+
artifact_resolver: ->(reference) { encrypted_store.fetch(reference) },
|
|
119
|
+
preflight: ->(plan:, previous:, credentials:) { identity_and_baseline_match?(plan, previous, credentials) },
|
|
120
|
+
verifier: ->(step:, target:, credentials:, receipt:) { readback_result(step, target, credentials, receipt) }
|
|
121
|
+
)
|
|
122
|
+
receipt = Netstack::Deployment::Executor.new(adapter: adapter).apply(
|
|
123
|
+
plan: plan, execution_id: "operation-42", credentials: connection_credentials,
|
|
124
|
+
checkpoint: ->(event) { durable_store.commit!(event); true }
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The checkpoint must commit `event[:receipt]` synchronously before returning exactly
|
|
129
|
+
`true`. The host holds a target lease across the execution/recovery attempt. See
|
|
130
|
+
the deployment contract for restart and lock ownership. The plan contains only
|
|
131
|
+
`artifact_ref` and SHA256, optional request/target identifiers and nonsecret expected
|
|
132
|
+
state. Never put scripts or credentials into expected state, target attributes,
|
|
133
|
+
metadata or checkpoint evidence. Store the Artifact securely, or reconstruct it
|
|
134
|
+
from protected bytes with the same content digest before resolving a reference.
|
|
135
|
+
|
|
136
|
+
CLI commands and outputs are marked sensitive. Comments/blank lines are omitted,
|
|
137
|
+
host is pinned to `Target#host`, and there is no implicit write retry. A successful
|
|
138
|
+
transport is `applied`; the verifier must return `verified`, `failed`, `unknown` or
|
|
139
|
+
`not_applied` with bounded nonsecret evidence. Partial writes are unknown. Resuming
|
|
140
|
+
started/unknown work invokes readback, never a second script submission, and does
|
|
141
|
+
not need to reload an already submitted artifact.
|
|
142
|
+
|
|
143
|
+
For an existing reviewed manual script, including Huawei or Cisco IOS:
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
manual = Netstack::Configuration::Artifact.script(target: target, content: reviewed_script)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
This artifact binds to that target's key, address and platform. It carries no template
|
|
150
|
+
digest and cannot be moved to another target by changing the plan. Manual scripts
|
|
151
|
+
must include their own CLI mode transitions and save/commit decisions. HTTP delivery
|
|
152
|
+
may use platforms supported by the external service; CLI uses Connector's platforms.
|
|
153
|
+
|
|
154
|
+
## HTTP delivery
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
http = Netstack::Integrations::Http::Client.new(base_url: endpoint)
|
|
158
|
+
client = Netstack::Integrations::HttpDelivery::Client.new(http: http, lookup_path: "lookup")
|
|
159
|
+
adapter = Netstack::Integrations::HttpDelivery::Adapter.new(
|
|
160
|
+
client: client, artifact_resolver: ->(reference) { encrypted_store.fetch(reference) }
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
POST retains Motor's `request_no`, `target_id`, `idempotency_key`, `device`,
|
|
165
|
+
`credentials: { username:, password: }` and `script` fields and the
|
|
166
|
+
`Idempotency-Key` header. Target attributes may provide the existing snapshot fields
|
|
167
|
+
`id`, `serial`, `name`, `hostname`, `vendor`, `model`, `os`, `device_role`, `source`;
|
|
168
|
+
the IP always comes from the target. Unrelated attributes are omitted.
|
|
169
|
+
|
|
170
|
+
GET `<endpoint>/<encoded remote id>` reads an acknowledged task. An uncertain POST
|
|
171
|
+
with no remote id instead uses GET `lookup_path?idempotency_key=<original key>`;
|
|
172
|
+
the default lookup path is the submission endpoint. `lookup_parameter:` is configurable.
|
|
173
|
+
The key-lookup response **must echo the same `idempotency_key`**. A missing key,
|
|
174
|
+
wrong task, unsupported status, timeout or 404 remains unknown. The remote service
|
|
175
|
+
must durably index each idempotency key and return that operation's terminal result;
|
|
176
|
+
if it cannot, the adapter cannot safely reconcile the uncertainty.
|
|
177
|
+
|
|
178
|
+
`success/succeeded/completed`, `failed/error/rejected`, and
|
|
179
|
+
`accepted/pending/queued/running/processing` are normalized; absent status accepts
|
|
180
|
+
only strict boolean `success`. No automatic POST retry, redirect, or polling loop
|
|
181
|
+
is added. The host schedules subsequent Executor calls with the previous receipt.
|
|
182
|
+
`verified` here proves the remote service reported completion of the identified
|
|
183
|
+
operation; it does not independently prove device running-state convergence.
|
|
184
|
+
Raw remote output is available through `Result#output`, excluded from `inspect` and
|
|
185
|
+
checkpoints. Failure checkpoints use a fixed local error code, not server messages.
|
|
186
|
+
|
|
187
|
+
## Validation and sources
|
|
188
|
+
|
|
189
|
+
Offline tests cover all 11 templates; bootstrap roles/admission/batches, IRF/vPC
|
|
190
|
+
sections, gateway and aggregate conflicts, policy objects, Radware grouping, both
|
|
191
|
+
VPN vendors/IKE versions, safe template negative cases, artifact digest changes,
|
|
192
|
+
sensitive CLI commands, checkpoint-before-write and unknown HTTP reconciliation.
|
|
193
|
+
No real device or delivery service is exercised by these tests.
|
|
194
|
+
|
|
195
|
+
Business sources: Motor `app/services/motor/netconf/{catalog,input_contract,validator,
|
|
196
|
+
payload,template_manager,bootstrap/*}.rb`, its `app/views/netconf/*.erb` templates,
|
|
197
|
+
and `lib/motor/api/netconf_delivery_client.rb`. The host's original persistence and
|
|
198
|
+
retry implementation is not copied into the library.
|