net-connector 0.14.0 → 0.15.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 +65 -0
- data/CONTRIBUTING.md +4 -4
- data/README.md +2 -2
- data/README.zh-CN.md +2 -2
- data/docs/VERIFICATION.md +20 -11
- data/docs/architecture.md +45 -36
- data/docs/netdisco.md +40 -11
- data/docs/netdisco.zh-CN.md +31 -15
- data/docs/templates.md +2 -2
- data/docs/usage.md +19 -3
- data/docs/usage.zh-CN.md +19 -7
- data/docs/vendors.md +12 -1
- data/examples/{backup.yml → backup.example.yml} +1 -1
- data/examples/inventory.yml +1 -1
- data/lib/net/connector/device/base.rb +24 -6
- data/lib/net/connector/device/{topology → interfaces}/audit.rb +21 -7
- data/lib/net/connector/device/{topology → interfaces}/change.rb +3 -1
- data/lib/net/connector/device/{topology → interfaces}/immediate_strategy.rb +3 -1
- data/lib/net/connector/device/{topology → interfaces}/interface_blocks.rb +2 -1
- data/lib/net/connector/device/{topology → interfaces}/interface_description.rb +15 -1
- data/lib/net/connector/device/{topology → interfaces}/interface_name.rb +6 -4
- data/lib/net/connector/device/{topology → interfaces}/neighbor.rb +18 -1
- data/lib/net/connector/device/{topology → interfaces}/plan.rb +4 -1
- data/lib/net/connector/device/{topology → interfaces}/quoted_text.rb +2 -1
- data/lib/net/connector/device/{topology → interfaces}/stages.rb +3 -2
- data/lib/net/connector/device/{topology → interfaces}/strategy.rb +5 -1
- data/lib/net/connector/device/{topology.rb → interfaces.rb} +68 -74
- data/lib/net/connector/device/local_backup.rb +5 -0
- data/lib/net/connector/device/profile/builder.rb +2 -0
- data/lib/net/connector/device/profile.rb +1 -0
- data/lib/net/connector/device/remote_backup/receipt.rb +3 -0
- data/lib/net/connector/device/remote_backup/target.rb +2 -0
- data/lib/net/connector/device/remote_backup.rb +3 -0
- data/lib/net/connector/device/running_config/strategy.rb +11 -1
- data/lib/net/connector/device/running_config.rb +19 -7
- data/lib/net/connector/device/scope.rb +5 -1
- data/lib/net/connector/device/tftp/file_upload.rb +3 -2
- data/lib/net/connector/device/tftp/receipt.rb +3 -0
- data/lib/net/connector/device/tftp/strategy.rb +4 -1
- data/lib/net/connector/device/tftp/target.rb +1 -0
- data/lib/net/connector/engine/command/result.rb +1 -0
- data/lib/net/connector/engine/command.rb +1 -0
- data/lib/net/connector/engine/configuration.rb +2 -0
- data/lib/net/connector/engine/dialogue.rb +1 -0
- data/lib/net/connector/engine/error/metadata.rb +15 -2
- data/lib/net/connector/engine/errors.rb +6 -2
- data/lib/net/connector/engine/execution.rb +26 -16
- data/lib/net/connector/engine/interaction.rb +1 -0
- data/lib/net/connector/engine/known_hosts.rb +52 -21
- data/lib/net/connector/engine/log/event.rb +6 -0
- data/lib/net/connector/engine/log/formatter.rb +1 -0
- data/lib/net/connector/engine/log/redacting_writer.rb +4 -0
- data/lib/net/connector/engine/log/transcript.rb +4 -0
- data/lib/net/connector/engine/log.rb +5 -1
- data/lib/net/connector/engine/redactor.rb +3 -4
- data/lib/net/connector/engine/response.rb +1 -0
- data/lib/net/connector/engine/result.rb +1 -0
- data/lib/net/connector/engine/script.rb +1 -0
- data/lib/net/connector/engine/session.rb +11 -2
- data/lib/net/connector/engine/terminal/text.rb +3 -0
- data/lib/net/connector/engine/transports.rb +17 -20
- data/lib/net/connector/netdisco/{address_filter.rb → address_set.rb} +5 -1
- data/lib/net/connector/netdisco/batch.rb +9 -6
- data/lib/net/connector/netdisco/cli/backup.rb +36 -25
- data/lib/net/connector/netdisco/cli/credentials.rb +1 -0
- data/lib/net/connector/netdisco/cli/help.rb +10 -6
- data/lib/net/connector/netdisco/cli/options.rb +17 -12
- data/lib/net/connector/netdisco/cli/setup.rb +136 -0
- data/lib/net/connector/netdisco/cli/topology.rb +117 -46
- data/lib/net/connector/netdisco/cli.rb +5 -7
- data/lib/net/connector/netdisco/config/defaults.rb +68 -0
- data/lib/net/connector/netdisco/config/initializer.rb +26 -9
- data/lib/net/connector/netdisco/config.rb +4 -3
- data/lib/net/connector/netdisco/device/access.rb +1 -0
- data/lib/net/connector/netdisco/device/policy.rb +6 -0
- data/lib/net/connector/netdisco/device.rb +1 -0
- data/lib/net/connector/netdisco/fleet/options.rb +17 -10
- data/lib/net/connector/netdisco/fleet.rb +108 -30
- data/lib/net/connector/netdisco/inventory/budget.rb +3 -0
- data/lib/net/connector/netdisco/inventory/cache.rb +14 -0
- data/lib/net/connector/netdisco/inventory/http.rb +55 -23
- data/lib/net/connector/netdisco/inventory/postgres.rb +6 -0
- data/lib/net/connector/netdisco/inventory.rb +3 -0
- data/lib/net/connector/netdisco/{operations → operation}/batch.rb +21 -10
- data/lib/net/connector/netdisco/{operations → operation}/outcome.rb +17 -10
- data/lib/net/connector/netdisco/{operations.rb → operation.rb} +89 -46
- data/lib/net/connector/netdisco/plan.rb +4 -0
- data/lib/net/connector/netdisco/planner.rb +1 -0
- data/lib/net/connector/netdisco/progress.rb +53 -22
- data/lib/net/connector/netdisco/report/files.rb +4 -3
- data/lib/net/connector/netdisco/report/text.rb +17 -6
- data/lib/net/connector/netdisco/report.rb +13 -2
- data/lib/net/connector/netdisco/resources.rb +36 -0
- data/lib/net/connector/netdisco/rules.rb +7 -3
- data/lib/net/connector/netdisco/settings.rb +39 -13
- data/lib/net/connector/netdisco/ssh/archive/client.rb +320 -0
- data/lib/net/connector/netdisco/ssh/archive/pool.rb +71 -0
- data/lib/net/connector/netdisco/ssh/archive/worker.py +269 -0
- data/lib/net/connector/netdisco/ssh/archive.rb +4 -0
- data/lib/net/connector/netdisco/ssh/authentication.rb +33 -0
- data/lib/net/connector/netdisco/tftp/archive.rb +20 -1
- data/lib/net/connector/netdisco/tftp/policy.rb +14 -1
- data/lib/net/connector/netdisco/tftp/root.rb +48 -0
- data/lib/net/connector/netdisco/tftp/verifier.rb +8 -1
- data/lib/net/connector/netdisco/topology/files.rb +59 -0
- data/lib/net/connector/netdisco/topology/options.rb +38 -9
- data/lib/net/connector/netdisco/topology/presentation.rb +59 -0
- data/lib/net/connector/netdisco/worker.rb +37 -11
- data/lib/net/connector/{concurrency → parallel}/observer.rb +5 -1
- data/lib/net/connector/{concurrency → parallel}/pool.rb +18 -14
- data/lib/net/connector/parallel.rb +8 -0
- data/lib/net/connector/storage/atomic_file.rb +9 -0
- data/lib/net/connector/storage/backup_lock.rb +12 -0
- data/lib/net/connector/storage/file_reader.rb +4 -0
- data/lib/net/connector/storage/saved_config.rb +2 -0
- data/lib/net/connector/textfsm.rb +1 -1
- data/lib/net/connector/vendor/centec/running_config.rb +22 -0
- data/lib/net/connector/vendor/centec/tftp.rb +46 -0
- data/lib/net/connector/vendor/centec.rb +33 -0
- data/lib/net/connector/vendor/cisco/topology.rb +3 -3
- data/lib/net/connector/vendor/cisco_ios/tftp.rb +2 -0
- data/lib/net/connector/vendor/cisco_ios.rb +1 -1
- data/lib/net/connector/vendor/cisco_nxos/tftp.rb +2 -0
- data/lib/net/connector/vendor/cisco_nxos/topology.rb +3 -0
- data/lib/net/connector/vendor/cisco_nxos.rb +1 -1
- data/lib/net/connector/vendor/fortinet/tftp.rb +4 -0
- data/lib/net/connector/vendor/fortinet/topology.rb +10 -3
- data/lib/net/connector/vendor/h3c/tftp.rb +3 -0
- data/lib/net/connector/vendor/h3c/topology.rb +3 -3
- data/lib/net/connector/vendor/hillstone/tftp.rb +1 -0
- data/lib/net/connector/vendor/hillstone/topology.rb +3 -3
- data/lib/net/connector/vendor/huawei/topology.rb +11 -4
- data/lib/net/connector/vendor/juniper/remote_backup.rb +2 -0
- data/lib/net/connector/vendor/juniper/topology.rb +10 -3
- data/lib/net/connector/vendor/palo_alto/tftp.rb +3 -1
- data/lib/net/connector/vendor/palo_alto/topology.rb +3 -3
- data/lib/net/connector/vendor/radware/tftp.rb +23 -1
- data/lib/net/connector/vendor/radware/topology.rb +4 -3
- data/lib/net/connector/vendor.rb +3 -1
- data/lib/net/connector/version.rb +1 -1
- metadata +59 -44
- /data/lib/net/connector/{templates → textfsm}/cisco/show_cdp_neighbors_detail.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/cisco/show_running_config.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/cisco_ios/show_ip_interface_brief.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/cisco_nxos/show_cdp_neighbors_detail.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/cisco_nxos/show_ip_interface_brief.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/cisco_nxos/show_running_config.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/fortinet/diagnose_lldp_rx_neighbor.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/fortinet/show_full_configuration.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/h3c/display_current_configuration.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/h3c/display_lldp_neighbor_information_list.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/hillstone/show_configuration_running.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/hillstone/show_lldp_neighbor_information.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/huawei/display_current_configuration.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/huawei/display_lldp_neighbor_brief.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/index +0 -0
- /data/lib/net/connector/{templates → textfsm}/juniper/show_configuration_display_set.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/juniper/show_lldp_neighbors_detail.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/palo_alto/show.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/palo_alto/show_lldp_neighbors_all.textfsm +0 -0
- /data/lib/net/connector/{templates → textfsm}/radware/cfg_dump.textfsm +0 -0
data/docs/netdisco.md
CHANGED
|
@@ -18,9 +18,11 @@ export NC_DEVICE_PASSWORD='replace-me'
|
|
|
18
18
|
|
|
19
19
|
Use `ND_API_KEY` instead of the HTTP username/password pair when available. HTTPS retains standard certificate verification. HTTP is allowed by default for compatibility; set `netdisco.allow_insecure_http: false` to reject it before sending credentials.
|
|
20
20
|
|
|
21
|
-
`
|
|
21
|
+
`NC_PARALLEL` sets the default worker count for backups, configuration scripts and interface changes (1–500, default 4). An explicit API argument or CLI option takes precedence over the environment, then YAML.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Before connecting devices, batches reserve file descriptor capacity for SSH pipes, PTYs, logs and locks. The process soft limit is raised within its existing hard limit when needed; an insufficient hard limit stops execution with a `ulimit -n` hint. Shell and system limits are not changed. SSH archive connections are sized per server, with up to four simultaneous connection preflights.
|
|
24
|
+
|
|
25
|
+
YAML contains only non-sensitive settings. Unknown fields, Ruby object tags, and credentials in YAML are rejected. Settings precedence is CLI, supported environment variables, YAML, then defaults. Files load only through `--config FILE` or `NC_CONFIG`. Start with [backup.example.yml](../examples/backup.example.yml):
|
|
24
26
|
|
|
25
27
|
```yaml
|
|
26
28
|
netdisco:
|
|
@@ -28,7 +30,7 @@ netdisco:
|
|
|
28
30
|
allow_insecure_http: false
|
|
29
31
|
backup:
|
|
30
32
|
directory: /var/backups/network
|
|
31
|
-
|
|
33
|
+
parallel: 4
|
|
32
34
|
inventory:
|
|
33
35
|
include_vendors: [h3c, huawei, cisco_ios]
|
|
34
36
|
ssh:
|
|
@@ -108,7 +110,7 @@ puts report.report_location
|
|
|
108
110
|
exit 1 unless report.policy_success?
|
|
109
111
|
```
|
|
110
112
|
|
|
111
|
-
`plan_backup` and `plan_tftp_backup` build inventory snapshots. Passing `plan:` preserves the selection and avoids another inventory query; malformed external plans are rejected before credentials, directories, or device I/O. Tasks run with bounded
|
|
113
|
+
`plan_backup` and `plan_tftp_backup` build inventory snapshots. Passing `plan:` preserves the selection and avoids another inventory query; malformed external plans are rejected before credentials, directories, or device I/O. Tasks run with bounded parallel and retain inventory order. Ordinary device/callback failures do not discard other results; interruption stops workers and cleans resources.
|
|
112
114
|
|
|
113
115
|
Fleet returns `Netdisco::Report`, delegating raw execution fields to `report.batch`. `success?` and `status` remain strict; `policy_success?` evaluates the chosen policy. `summary` uses schema 2, with public settings, coverage, per-device diagnostics, timestamps, monotonic durations, and verification. `statistics` omits device detail. `Batch#build_report` only wraps existing execution data.
|
|
114
116
|
|
|
@@ -123,12 +125,12 @@ fleet = Net::Connector::Netdisco::Fleet.new
|
|
|
123
125
|
batch = fleet.execute_scripts(
|
|
124
126
|
scripts: { "192.0.2.1" => ["system-view", "interface GigabitEthernet1/0/1", "description uplink", "quit", "quit"] },
|
|
125
127
|
confirmed: true,
|
|
126
|
-
|
|
128
|
+
parallel: 4
|
|
127
129
|
)
|
|
128
130
|
puts JSON.pretty_generate(batch.to_h)
|
|
129
131
|
puts "Total=#{batch.summary[:total]} succeeded=#{batch.summary[:succeeded]} failed=#{batch.summary[:failed]}"
|
|
130
132
|
|
|
131
|
-
batch = fleet.
|
|
133
|
+
batch = fleet.sync_descriptions(hosts: ["192.0.2.1"])
|
|
132
134
|
puts "Total=#{batch.summary[:total]} succeeded=#{batch.summary[:succeeded]} failed=#{batch.summary[:failed]}"
|
|
133
135
|
```
|
|
134
136
|
|
|
@@ -136,13 +138,17 @@ Scripts accept `Script`, command arrays or multiline text. The full batch is par
|
|
|
136
138
|
|
|
137
139
|
Interface application calls the existing topology operation, retaining evidence revalidation, configuration readback and persistence confirmation. It does not execute an approved plan as an ordinary script. Both write operations require `confirmed: true`, and never automatically retry commands. Each task owns and closes its connector. Ordinary failures remain per-device outcomes; `Interrupt` stops and joins all owned workers. Confirmed work is not rolled back across devices.
|
|
138
140
|
|
|
139
|
-
`
|
|
141
|
+
`Operation::Batch` is separate from backup reports. `outcomes` retain the original `Result` or plan, completed steps and the first failure; `:partial` preserves work followed by an error, and missing credentials prevent success. `plans` selects only fully successful planning results. `to_h` includes safe diagnostics and counts, excluding commands, outputs, credentials and evidence. `on_start` and `on_result` run on workers and must be thread safe; ordinary callback faults are recorded without repeating device operations. Logging uses the configured `ssh.log_directory`.
|
|
142
|
+
|
|
143
|
+
Device outcomes are published before `on_result`. After an `Interrupt`, inspect `Fleet#last_operation_batch` or `Operation#last_batch`: completed outcomes remain intact, started tasks without a result are `:interrupted`, and queued tasks are `:not_started`. The batch has `interrupted: true`, reports `:interrupted`, and cannot report success even if every device finished before a callback cancelled the batch. Cancelled tasks have no measured duration; unstarted tasks also have no timestamps. The original interrupt propagates after worker cleanup. An interrupted task may already have changed its device; read back its state before deciding what to do next. Each new operation attempt clears the previous operation batch, including attempts rejected during preflight. Backup reports remain available separately through `Fleet#last_report`.
|
|
144
|
+
|
|
145
|
+
These multi-device APIs return `Operation::Batch` and invoke each device through `device.interfaces.plan/apply/sync`. See the [device guide](usage.md#topology-and-interface-descriptions) for single-device reads, audit and changes.
|
|
140
146
|
|
|
141
|
-
`
|
|
147
|
+
`sync_descriptions` is the direct update entry: one request and one connection per device, from discovery through readback and save. Matching descriptions cause no writes or saves; a no-op does not confirm an earlier save. The default format is `To_<peer name>_<interface abbreviation>`; formatting options and blocks remain backend choices. Separate preview/approval workflows remain available through `plan_descriptions` and `apply_descriptions(plans: planned.plans, confirmed: true)`.
|
|
142
148
|
|
|
143
149
|
Each device publishes one immutable outcome after execution and connection cleanup. Ordinary credential, connection, execution and cleanup failures affect that device while active and queued siblings continue. Once scheduling completes, `summary` and `to_h[:summary]` report `total`, `succeeded` and `failed`, with total = succeeded + failed. Total counts the devices requested and scheduled in this batch; partial completion and missing credentials count as failures and also appear in `partial` and `missing_credentials`. `callback_errors` counts notification faults separately without changing device outcomes. Empty batches report zero counts and `no_devices`. The library returns the report; callers choose terminal output or JSON export. Whole-batch approval validation and interruption retain their existing boundaries, and completed device changes cannot be rolled back.
|
|
144
150
|
|
|
145
|
-
For other independent tasks, load `net/connector/
|
|
151
|
+
For other independent tasks, load `net/connector/parallel/pool` and use `Net::Connector::Parallel::Pool.new(parallel: 4).map(items) { |item| ... }`. Results preserve input order, all threads must start before any block runs, and uncaught exceptions stop the pool and propagate to its caller. Business code decides which ordinary errors become per-task results.
|
|
146
152
|
|
|
147
153
|
## PostgreSQL inventory
|
|
148
154
|
|
|
@@ -193,6 +199,19 @@ Host and vendor overrides reject normalized aliases that select different connec
|
|
|
193
199
|
|
|
194
200
|
## TFTP history and verification
|
|
195
201
|
|
|
202
|
+
For an SSH-only server, use a password-free root such as `ssh://archive@backup.example/srv/tftp`
|
|
203
|
+
in `tftp.verification_roots`, keyed by the corresponding TFTP server. SSH roots require
|
|
204
|
+
`preserve_history: true` (enabled by the example CLI). The server needs Python 3 and a writable
|
|
205
|
+
archive root. Supply `NC_TFTP_SSH_PASSWORD` through the caller's credential environment,
|
|
206
|
+
or use an available SSH key/agent. The example scripts load this credential from the
|
|
207
|
+
project-root `.env`. Authentication never prompts: missing or rejected credentials
|
|
208
|
+
stop the batch before device connections and print an actionable error. Passwords
|
|
209
|
+
are sent through a private, unlogged PTY and never appear in policy or SSH arguments.
|
|
210
|
+
The remote lock spans upload and verification, existing files are preserved first, and staging
|
|
211
|
+
is removed only after both server and local archives have matching SHA-256 evidence.
|
|
212
|
+
The SSH archive pool allows four concurrent transactions and limits each file to 64 MiB.
|
|
213
|
+
See [example setup](../examples/README.md#ssh-服务器核验与归档).
|
|
214
|
+
|
|
196
215
|
Direct upload receipts only establish `device_reported`. Fleet can verify a local/mounted TFTP directory via `verification_root:`; the CLI accepts `--tftp-root DIR` or YAML `tftp.verification_root`. A verified receipt requires a current, nonempty regular file matching the actual receipt path, with SHA-256 evidence. Missing evidence prevents `verified` success. A mismatched receipt path is retained as device evidence, but cannot be verified or archived without a baseline for that locked target.
|
|
197
216
|
|
|
198
217
|
Verification holds a target lock across the pre-upload file-state/time baseline, upload and fingerprint check, including batches without history preservation. An unchanged old file cannot qualify through a future timestamp. The server directory must be writable for lock files and the filesystem-time probe. Result callbacks receive the verified outcome, and file evidence never substitutes for a failed device operation or cleanup.
|
|
@@ -209,7 +228,7 @@ Reports distinguish `verification`, `server_file_verified`, `local_file`, `serve
|
|
|
209
228
|
|
|
210
229
|
```sh
|
|
211
230
|
bundle exec ruby examples/backup.rb --json
|
|
212
|
-
bundle exec ruby examples/backup.rb --sample 3 --
|
|
231
|
+
bundle exec ruby examples/backup.rb --sample 3 --parallel 10
|
|
213
232
|
bundle exec ruby examples/backup_tftp.rb --tftp-root /srv/tftp --success-policy verified
|
|
214
233
|
```
|
|
215
234
|
|
|
@@ -231,7 +250,7 @@ Examples load the project `.env` through `dotenv` without overriding existing en
|
|
|
231
250
|
| `ND_API_KEY` | Existing API key |
|
|
232
251
|
| `ND_DB_HOST`, `ND_DB_NAME`, `ND_DB_USER`, `ND_DB_PASS` | Required database connection fields |
|
|
233
252
|
| `NC_CONFIG` | Non-sensitive YAML file |
|
|
234
|
-
| `
|
|
253
|
+
| `NC_PARALLEL` | Default batch worker count; integer 1–500, default 4 |
|
|
235
254
|
| `NC_DEVICE_USERNAME`, `NC_DEVICE_PASSWORD` | Default device credentials |
|
|
236
255
|
| `NC_<VENDOR>_USERNAME`, `NC_<VENDOR>_PASSWORD` | Vendor credentials, e.g. `CISCO_IOS` |
|
|
237
256
|
| `NC_ENABLE_PASSWORD`, `NC_<VENDOR>_ENABLE_PASSWORD` | Optional privilege credentials |
|
|
@@ -239,3 +258,13 @@ Examples load the project `.env` through `dotenv` without overriding existing en
|
|
|
239
258
|
Only the variables listed above are supported. There are no legacy aliases. Public settings belong in YAML: budgets under `netdisco`, filters and mappings under `inventory`, script output budget and vendor protocols under `ssh`, and source files/VRFs under `tftp`. Database port, TLS mode, CA path, and connection timeout use `netdisco.db_port`, `db_sslmode`, `db_sslrootcert`, and `db_connect_timeout`. TFTP uses `tftp.server` and `tftp.verification_root`; progress uses `--no-progress`. Removed settings and old prefixes are rejected without echoing their values. Vendor source fields are `h3c_source_file`, `h3c_wireless_source_file`, and `huawei_source_file` under `tftp`.
|
|
240
259
|
|
|
241
260
|
Scheduling and durable queues belong to the caller. No device command retries are added by batch orchestration. For development gates and evidence limits, see [verification](VERIFICATION.md); for protected extension interfaces and report helpers, see [architecture](architecture.md) (Chinese).
|
|
261
|
+
|
|
262
|
+
An interrupted batch exits with status 130 and reports `status: interrupted`. Completed artifacts retain their receipts; remaining slots distinguish `interrupted` from `not_started`. Uploads are never replayed. `events.jsonl` includes artifact metadata without configuration bodies. Library callers still receive the original `Interrupt` and may inspect `Fleet#last_report`; a new run clears that report.
|
|
263
|
+
|
|
264
|
+
The canonical parallel entry is now `net/connector/parallel/pool` / `Net::Connector::Parallel::Pool`. The former `parallel` require paths and `Parallel` namespace have been removed. Built-in TextFSM assets now reside in `lib/net/connector/textfsm/`; default parser lookup follows this automatically, while literal resource paths must be updated.
|
|
265
|
+
|
|
266
|
+
Topology comparisons preserve semantically equal existing descriptions. Set `topology.neighbor_name_prefixes: ["AP-"]` to explicitly accept a legacy name prefix during comparison; the default empty list makes no prefix assumptions. Unknown neighbor identities produce no commands, while other verified interfaces can still be planned independently.
|
|
267
|
+
|
|
268
|
+
Topology examples also export `scripts/<name>-<IP>.cfg` for verified changes and `devices/<name>-<IP>.txt` for every result, using the backup filename rules. Plans and summaries reference the files and command SHA-256. No-change, discovery and failed results have no empty command file. Export failure prevents application; exported commands do not bypass live evidence revalidation.
|
|
269
|
+
|
|
270
|
+
Topology output distinguishes unresolved evidence from execution failures and defaults to a compact Chinese summary. Use `--verbose` for per-device events. JSON keeps status codes and adds `statistics`; pending changes or unresolved evidence still exit with code 1. Startup shows the resolved configuration file: `NC_CONFIG` also takes precedence over the topology example default, so shared configuration must include the appropriate `topology` policy.
|
data/docs/netdisco.zh-CN.md
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
`Fleet#plan_backup` 和 `Fleet#plan_tftp_backup` 从同一份清单生成计划。把计划传给 `backup_all(plan:)` 或 `tftp_backup_all(plan:)`,可使预览与执行选择同一批设备;计划与清单不符时会拒绝执行。单台设备异常或结果回调失败不会阻止其他设备。`batch.summary` 包含总数、成功、失败、部分成功、跳过、具体状态和逐台结果。部分成功包括已保存但关闭失败,以及本地文件已替换但目录同步或收尾失败;后者保留 backup 并标为 `saved_with_error`。TFTP 的 `reported_uploaded` 仅代表设备报告上传,不代表服务器文件已核验。
|
|
14
14
|
|
|
15
|
+
配置脚本与接口描述操作使用独立的 `Operation::Batch`。设备执行、关闭和结果登记均先于 `on_result` 通知;中断仍在回收线程后抛出原始 `Interrupt`,调用方可从 `Fleet#last_operation_batch` 或 `Operation#last_batch` 检查本次回执。已完成结果保留原状态,启动后未取得结果的任务为 `interrupted`,尚未启动的任务为 `not_started`;批次的 `interrupted` 为 true、状态为 `interrupted`,不会因设备恰好全部完成就报告整体成功。取消任务不补造耗时,未启动任务不补造起止时间。结果未知的设备可能已经发生变更,后续处理前应读回核实,不自动重放命令。每次新操作尝试都会清除旧操作回执,预检失败也不例外;备份报告仍独立保存在 `Fleet#last_report`。
|
|
16
|
+
|
|
15
17
|
本地 `backup(path:)` 用 SHA-256 比较新旧配置,`backup.change` 返回 `:created`、`:changed` 或 `:unchanged`;内容未变且权限、文件身份正常时保留修改时间。这不补验历史写入的断电持久性。`backup_all` 的 `on_change:` 仅在新建或更改文件保存后触发;`on_start:` 和 `on_result:` 观察每台已尝试设备。回调异常记录在 `batch.callback_errors`,不丢弃设备结果。每项结果包含开始、结束和耗时。TFTP 无法比较服务器文件,因此没有 `change`,也不触发变更通知。
|
|
16
18
|
|
|
17
19
|
每批默认由 `Report::Store::Json` 写入私有 JSON 报告,路径见 `batch.report_location`。调用方如有数据库仓储,可传 `Report::Store::Database.new(repository: YourModel)`;仓储需实现 `create!(attributes)`。`report_store: nil` 表示由调用方自行持久化。报告失败保留在 `batch.report_error`,同时使 `batch.success?` 为假。若报告已替换但目录同步失败,仍保留位置;离线 `--export --output` 遇到同类错误返回 2,并说明文件已经提交。
|
|
@@ -50,7 +52,7 @@ exit 1 unless batch.success?
|
|
|
50
52
|
|
|
51
53
|
gem 安装后的命令为 `net-backup`;源码入口为 `bin/net-backup`,可在项目目录运行 `bundle exec bin/net-backup --help`。原命令 `net-connector-backup` 已改名。
|
|
52
54
|
|
|
53
|
-
命令行程序 `net-backup` 的 YAML 文件只允许非敏感设置;Netdisco 和设备凭据留在环境变量中。`ND_URL` 从环境覆盖 YAML 地址,`
|
|
55
|
+
命令行程序 `net-backup` 的 YAML 文件只允许非敏感设置;Netdisco 和设备凭据留在环境变量中。`ND_URL` 从环境覆盖 YAML 地址,`NC_PARALLEL` 覆盖 YAML 并发数;其他非敏感策略通过 YAML 或 CLI 设置。批量示例也支持 `NC_CONFIG`,可从 [完整配置示例](../examples/backup.yml) 开始。只有传入 `--config FILE` 或设置 `NC_CONFIG` 时才加载文件:
|
|
54
56
|
|
|
55
57
|
```yaml
|
|
56
58
|
netdisco:
|
|
@@ -64,7 +66,7 @@ netdisco:
|
|
|
64
66
|
allow_insecure_http: false
|
|
65
67
|
backup:
|
|
66
68
|
directory: /var/backups/network
|
|
67
|
-
|
|
69
|
+
parallel: 4
|
|
68
70
|
inventory:
|
|
69
71
|
include_vendors: [h3c, huawei, cisco_ios]
|
|
70
72
|
host_overrides:
|
|
@@ -138,7 +140,7 @@ CLI 的计划与批次摘要使用 JSON。默认 `--success-policy strict` 使
|
|
|
138
140
|
|
|
139
141
|
显式使用 `--success-policy selected` 后,CLI 按上述 selected 规则决定退出码,保留严格的 `status: incomplete` 与跳过计数,另列 `policy_success`。例如 `net-backup --config config.yml --host 192.0.2.7 --success-policy selected`。成功策略由本次 CLI/API 参数指定,不改变已批准的清单选择,也不触发重试。
|
|
140
142
|
|
|
141
|
-
本地批量示例和 TFTP 批量示例默认全量;显式 `--sample N` 或 YAML `backup.limit_per_vendor` 才限制每厂商数量(示例允许 1 至 5)。优先级为 CLI > YAML。并发默认 4,可用 `--
|
|
143
|
+
本地批量示例和 TFTP 批量示例默认全量;显式 `--sample N` 或 YAML `backup.limit_per_vendor` 才限制每厂商数量(示例允许 1 至 5)。优先级为 CLI > YAML。并发默认 4,可用 `--parallel N`、环境变量 `NC_PARALLEL` 或 YAML `backup.parallel` 覆盖,优先级依次降低。两类示例在备份目录下创建唯一批次目录。TFTP 批量示例在可访问服务器目录时使用 hostname-ip 远端文件名并逐批归档;无法访问服务器目录时使用带批次标识的远端文件名,避免覆盖旧文件;上传完成与服务器文件验证是不同状态。
|
|
142
144
|
|
|
143
145
|
全量 TFTP 计划保存为 `plan.json`。目标文件名通常为 `<设备名>-<IP>.cfg`,Radware 用 `.tgz`,山石用 `.dat`。可访问本机 TFTP 服务器目录时,批量示例直接使用 `<设备名>-<IP>.<扩展名>` 上传,并在服务器的 `archive/<批次>/` 与本地 `<批次目录>/tftp/` 各保留一份;上传前已有的同名文件先保存到新归档目录的 `previous/`。无法访问服务器目录时,远端文件名增加东八区批次标识及同秒序号,避免覆盖旧文件;实际文件名见 `summary.json` 的 `remote_path`。计划仍检查同批目标名冲突。PAN-OS 固定使用 `running-config.xml`,还要求 `Sent ... bytes` 完成行;为避免下次上传覆盖它,运行批量示例时必须能访问本机 TFTP 服务器目录,否则该设备会明确失败且不会发起上传。H3C 从 `display startup` 发现源文件,可用 YAML `tftp.h3c_source_file` 覆盖;华为默认 `flash:/startup.cfg`。Nexus 9000 默认 VRF 为 `management`,山石为 `mgt-vr`;YAML `tftp.vrfs` 接受按厂商键配置的映射。山石命令在 `vrouter` 参数后追加唯一的 `.dat` 文件名;直接调用山石连接器且不指定 `path:` 时,由设备生成文件名并在结果中返回。通过 `--tftp-root DIR` 或 YAML `tftp.verification_root` 指定本机或已挂载的服务器目录时,批量示例在每台设备完成后核验实际回执路径、非空文件、修改时间和 SHA-256,并立即保存两份历史文件;报告的 `local_file` 和 `server_archive_file` 分别指向本地批次副本和服务器归档副本。核验或归档失败不会报告成功。未提供本机目录时,可命名设备仍使用不覆盖旧文件的远端文件名,回执保留为 `device_reported`,本地批次不包含配置副本。默认示例使用 `selected` 策略;要求服务器文件核验时使用 `--success-policy verified`,并提供本机服务器目录。日志、`events.jsonl` 和逐台结果均以私有权限保存。
|
|
144
146
|
|
|
@@ -149,7 +151,7 @@ CLI 的计划与批次摘要使用 JSON。默认 `--success-policy strict` 使
|
|
|
149
151
|
| 环境变量 | 默认值 | 用途 |
|
|
150
152
|
| --- | --- | --- |
|
|
151
153
|
| `ND_URL` | HTTP 模式必填 | Netdisco 服务根地址,可包含租户路径 |
|
|
152
|
-
| `
|
|
154
|
+
| `NC_PARALLEL` | 默认 4,范围 1 至 500 | 备份、配置批次和接口修正共用的并发数 |
|
|
153
155
|
| `ND_CACHE_TTL` | 默认 259200 秒(三天) | 清单缓存有效期,须为有限正数,优先于 YAML `netdisco.cache_ttl` |
|
|
154
156
|
| `NC_CONFIG` | 未设置 | CLI / 批量示例的非敏感 YAML 配置文件 |
|
|
155
157
|
| `ND_USERNAME`, `ND_PASSWORD` | 未提供 API 密钥时必填 | 清单 API 登录 |
|
|
@@ -181,12 +183,14 @@ ssh:
|
|
|
181
183
|
|
|
182
184
|
每台任务开始时仍读取设备凭据,`Settings.from_file` 也保留轮换能力。纯策略快照不保存密码、API key 或凭据解析器。需要让多次 API 调用共用策略时,可传 `Settings.new.for_run(mode: :backup)`。自定义 `credentials:` 解析器仍可逐设备返回连接设置,它显式给出的选项优先于批次默认值,由调用方负责一致性;注入的 `inventory:` 生命周期也由调用方管理。传入 `plan:` 的执行不会重新拉取或筛选已批准清单。`fleet.devices` 可在不连接设备时检查映射。
|
|
183
185
|
|
|
184
|
-
|
|
186
|
+
快照中的备份、缓存、日志、本地 TFTP 核验目录、SSH known-hosts 路径及显式 PostgreSQL CA 证书路径,以创建快照时的工作目录为基准,默认路径和后续相对路径覆盖也沿用这一基准。`~/` 开头的路径及 `~` 使用快照创建时的主目录。TFTP 的 `ssh://user@host/绝对目录` 核验 URI 保留原值,不作本地路径展开,且必须启用历史归档;SSH 认证、远端要求和并发限制见[示例设置](../examples/README.md#ssh-服务器核验与归档)。PostgreSQL CA 路径保留 libpq 的字面路径语义,不展开波浪号;`sslrootcert: system` 仍表示系统信任库,`./system` 则表示文件。未固定的活设置仍按调用时的当前工作目录和主目录解析路径。
|
|
185
187
|
|
|
186
188
|
地址和厂商覆盖规则规范化后,同一键不能指向不同连接器;指向同一连接器的别名可以并存。必需的厂商标签规范化后也不能为空。非法规则在连接设备前拒绝。
|
|
187
189
|
|
|
188
190
|
## 批量示例与进度
|
|
189
191
|
|
|
192
|
+
批次连接设备前会为 SSH 管道、设备伪终端、日志及文件锁预留文件描述符额度;需要时只在本进程硬上限内提高软额度,不修改终端和系统配置。硬上限不足时先停止整批,并提示调整 `ulimit -n` 或降低 `--parallel`。SSH 归档连接按各服务器实际设备数分配,连接预检最多四条同时进行。
|
|
193
|
+
|
|
190
194
|
`examples/backup.rb` 和 `examples/backup_tftp.rb` 默认全量备份符合筛选条件的设备,并在终端原地刷新两行进度(登录、采集、排队、成功和未完成数,以及耗时、平均吞吐、预计剩余时间);失败单独输出,结束后显示汇总和报告路径。重定向时每 25 台输出一次进度,避免刷屏。`--verbose` 显示逐条登录和命令事件,`--json` 才在 STDOUT 输出 JSON;完整计划和结果始终保存在批次目录。`--no-progress` 关闭人类进度。百分比表示任务完成比例,不是成功率。
|
|
191
195
|
|
|
192
196
|
```sh
|
|
@@ -194,10 +198,10 @@ ruby examples/backup.rb # 全量,默认并发 4
|
|
|
194
198
|
ruby examples/backup.rb --sample 3 # 每厂商最多 3 台
|
|
195
199
|
ruby examples/backup.rb --verbose # 详细命令过程
|
|
196
200
|
ruby examples/backup.rb --json > result.jsonl
|
|
197
|
-
ruby examples/backup.rb --
|
|
201
|
+
ruby examples/backup.rb --parallel 10 --username backup-user --ask-password
|
|
198
202
|
```
|
|
199
203
|
|
|
200
|
-
批量示例支持 `--
|
|
204
|
+
批量示例支持 `--parallel`(1 至 500)、`--username`、`--password`、`--enable-password`、`--netdisco-url`、`--netdisco-username`、`--netdisco-password`、`--directory` 和 `--config`。命令行优先于环境变量与 YAML;显式设备凭据逐字段覆盖对应厂商凭据。指定 Netdisco 用户名或密码时不再沿用环境中的 API key。密码可使用 `--ask-password` / `--ask-netdisco-password` 隐藏输入,避免命令行密码进入 shell 历史或进程参数;自动化仍可用环境变量或原有 `--stdin-credentials`。`--config` 与 `--directory` 的相对路径基于项目根目录。
|
|
201
205
|
|
|
202
206
|
## 部署、示例与验证
|
|
203
207
|
|
|
@@ -206,7 +210,7 @@ bundle install
|
|
|
206
210
|
script/ci
|
|
207
211
|
```
|
|
208
212
|
|
|
209
|
-
CI 在 Linux 和 macOS 上覆盖 Ruby 3.
|
|
213
|
+
CI 在 Linux 和 macOS 上覆盖 Ruby 3.4、4.0。`script/ci` 扫描源码与可用 Git 历史中的敏感数据,执行 Ruby 与工作流 lint、完整测试,并验证已构建 gem 的隔离安装和本地 PTY 烟测;不连接真实网络设备。首次运行需下载固定校验和的 Gitleaks 与 actionlint。
|
|
210
214
|
|
|
211
215
|
`bundle exec rake test` 报告当前测试进程的行、分支覆盖率和未加载文件清单。核心引擎、脱敏与错误处理(含 `Redactor`)、批量工作线程分别要求行和分支覆盖率均达到 80%,关键文件没有覆盖率数据也会失败;该门槛同时阻止 CI 和发布预检通过。`bundle exec rake lint` 检查 Ruby 代码,并对 `engine/`、`netdisco/` 限制方法长度(40)和 ABC 复杂度(60)。`bundle exec rake security:check` 扫描敏感数据,`bundle exec rake release:check` 执行完整预检。构建产物和脱敏扫描报告保存在被忽略的 `tmp/` 下。
|
|
212
216
|
|
|
@@ -231,12 +235,12 @@ CI 在 Linux 和 macOS 上覆盖 Ruby 3.2、3.3、3.4、4.0。`script/ci` 扫描
|
|
|
231
235
|
```ruby
|
|
232
236
|
require "net/connector/netdisco"
|
|
233
237
|
netdisco = Net::Connector::Netdisco
|
|
234
|
-
options = netdisco::CLI::Options.parse(argv: ["--
|
|
238
|
+
options = netdisco::CLI::Options.parse(argv: ["--parallel", "10"])
|
|
235
239
|
settings = netdisco::CLI::Options.settings(options)
|
|
236
240
|
inventory, credentials = netdisco::CLI::Credentials.resolve(settings)
|
|
237
241
|
directory = Net::Connector::Storage::BatchDirectory.create("./backups")
|
|
238
242
|
# 获得 report 和 plan 后:
|
|
239
|
-
# report = netdisco::Report::Files.write(report, directory: directory, plan: plan,
|
|
243
|
+
# report = netdisco::Report::Files.write(report, directory: directory, plan: plan, parallel: 10)
|
|
240
244
|
```
|
|
241
245
|
|
|
242
246
|
`CLI::Options.parse` 接受 `argv/input/output/error/program`,不修改传入参数数组;帮助通过返回值 `:help` 通知调用方,非法输入抛出 `ArgumentError`。`CLI::Credentials.resolve` 接受 `input:`,标准输入凭据仅消费一行,RUN 确认由脚本编排负责。模块不加载 `.env` 或切换目录。
|
|
@@ -281,23 +285,35 @@ fleet = Net::Connector::Netdisco::Fleet.new
|
|
|
281
285
|
batch = fleet.execute_scripts(
|
|
282
286
|
scripts: { "192.0.2.1" => ["system-view", "interface GigabitEthernet1/0/1", "description uplink", "quit", "quit"] },
|
|
283
287
|
confirmed: true,
|
|
284
|
-
|
|
288
|
+
parallel: 4
|
|
285
289
|
)
|
|
286
290
|
puts JSON.pretty_generate(batch.to_h)
|
|
287
291
|
puts "总数=#{batch.summary[:total]} 成功=#{batch.summary[:succeeded]} 失败=#{batch.summary[:failed]}"
|
|
288
292
|
|
|
289
|
-
batch = fleet.
|
|
293
|
+
batch = fleet.sync_descriptions(hosts: ["192.0.2.1"])
|
|
290
294
|
puts "清单 #{batch.summary[:total]},成功 #{batch.summary[:succeeded]},失败 #{batch.summary[:failed]}"
|
|
291
295
|
```
|
|
292
296
|
|
|
293
297
|
脚本接受 `Script`、命令数组或多行文本。全批先解析并固定脚本、规范化地址,再读取凭据或连接设备;重复、未知、过滤和不支持的目标提前拒绝。脚本须包含对应厂商的完整命令,包括进入/退出配置视图及需要的保存命令。空批次为 `:no_devices`,不判为成功。
|
|
294
298
|
|
|
295
|
-
|
|
299
|
+
这些是多设备批次 API,返回 `Operation::Batch`;内部通过每台设备的 `device.interfaces.plan/apply/sync` 执行。单设备的读取、审计与变更入口见[设备指南](usage.zh-CN.md#邻居发现与接口描述)。
|
|
300
|
+
|
|
301
|
+
默认使用 `sync_descriptions` 一次完成发现、修改、读回与保存,每台设备只建立一个连接,描述一致时不修改、不保存。默认格式为 `To_<邻居名称>_<接口简称>`,各厂商规则见[设备指南](usage.zh-CN.md#邻居发现与接口描述);定制规则留给后端参数或代码块。无变化成功不证明先前已保存。需要单独预览审批时,后端仍可使用 `plan_descriptions` 与 `apply_descriptions(plans: planned.plans, confirmed: true)`。
|
|
296
302
|
|
|
297
303
|
接口应用复用单设备拓扑操作,保留证据重验、配置回读和持久化确认,不将审批计划当作普通脚本执行。脚本与独立计划应用要求 `confirmed: true`;显式同步调用本身表达修改意图,不自动重放命令。每台任务独占并关闭连接器;普通失败隔离为设备结果,中断停止并等待全部工作线程。跨设备已完成的变更不会自动回滚。
|
|
298
304
|
|
|
299
|
-
`
|
|
305
|
+
`Operation::Batch` 独立于备份报告,`outcomes` 保留原 `Result` 或计划、完成步骤和第一故障;`:partial` 表示已有工作后出错,缺少凭据也使批次不成功。`plans` 只选完整成功的规划结果。`to_h` 仅输出安全诊断和数量,不展开命令、回显、凭据或证据。`on_start` 和 `on_result` 在工作线程执行,回调须线程安全;普通回调故障记录后继续,不重复设备操作。日志使用 `ssh.log_directory`。
|
|
300
306
|
|
|
301
307
|
每台设备独占会话,在操作和关闭完成后发布一个不可变终态。连接、凭据、执行和关闭的普通故障只影响该设备,其他运行中或排队任务继续。整批调度结束后返回完整结果;`summary` 和 `to_h[:summary]` 提供 `total`、`succeeded`、`failed`,满足总数 = 成功 + 失败,并细分 `partial`、`missing_credentials` 和 `callback_errors`。总数是本次请求并调度的设备数量;部分完成与缺少凭据计入失败,回调故障单列,不改写设备执行结果。空批次各项为零且状态为 `no_devices`。库不直接输出终端内容,调用方可将 `to_h` 导出 JSON 报告。设备上的已完成变更不能回滚;全批审批预检和显式中断仍维持原有失败边界。
|
|
302
308
|
|
|
303
|
-
其他独立任务可加载 `net/connector/
|
|
309
|
+
其他独立任务可加载 `net/connector/parallel/pool`,通过 `Net::Connector::Parallel::Pool.new(parallel: 4).map(items) { |item| ... }` 执行。结果按输入顺序返回,全部线程建立成功才运行块,未处理异常会停止线程池并回到调用线程。业务代码负责决定哪些普通错误转换为单任务结果。显式参数优先于 `NC_PARALLEL` 环境变量,默认 4、上限 500。
|
|
310
|
+
|
|
311
|
+
取消批次时退出码为 130,摘要状态为 `interrupted`。已完成设备保留文件与上传回执;未完成槽位分别标记 `interrupted` 和 `not_started`,不会自动重试。`events.jsonl` 的 `artifact` 保存已完成备份或上传的元数据,不包含配置正文。库调用者仍收到原始 `Interrupt`,可从同一 Fleet 的 `last_report` 读取部分结果;新的运行会清除旧报告。
|
|
312
|
+
|
|
313
|
+
并行入口现为 `net/connector/parallel/pool` 和 `Net::Connector::Parallel::Pool`。旧 `parallel` 加载路径及 `Parallel` 命名空间已移除。内置 TextFSM 模板移至 `lib/net/connector/textfsm/`;使用默认模板目录的调用无需修改,手工拼接旧资源路径的调用需更新。
|
|
314
|
+
|
|
315
|
+
拓扑描述比较保留语义一致的旧值。`topology.neighbor_name_prefixes: ["AP-"]` 可显式声明历史名称前缀,仅用于比较;默认空列表不猜测名称前缀。未知邻居身份不生成命令,其他已核实接口仍可独立规划。
|
|
316
|
+
|
|
317
|
+
拓扑示例按备份命名规则输出 `scripts/设备名-IP.cfg` 和 `devices/设备名-IP.txt`,计划与摘要记录路径及命令文件 SHA-256。无变更、发现及失败结果不生成空命令文件。导出失败阻止应用,文本导出不能绕过现场证据重验。
|
|
318
|
+
|
|
319
|
+
拓扑默认输出中文分类摘要,待核实证据和执行失败分别统计;`--verbose` 显示逐设备事件。JSON 状态码保持稳定并新增 `statistics`,存在待变更或未核实证据时退出码仍为 1。启动显示实际配置文件;`NC_CONFIG` 也优先于拓扑示例默认文件,共享配置须补充对应 `topology` 策略。
|
data/docs/templates.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 目录与命名
|
|
6
6
|
|
|
7
|
-
`lib/net/connector/
|
|
7
|
+
`lib/net/connector/textfsm/` 统一采用 `<平台>/<完整命令名>[_格式变体].textfsm`。命令使用小写,空格、连字符和路径分隔符转为下划线;不使用缩写命令、字段用途名或额外的命令子目录。管道的输出格式选项保留为命令词,纯分页控制 `no-more` 不进入文件名。
|
|
8
8
|
|
|
9
9
|
例如,`show running-config` 对应 `show_running_config.textfsm`,`display current-configuration` 对应 `display_current_configuration.textfsm`,`/cfg/dump` 对应 `cfg_dump.textfsm`,`show configuration | display set` 对应 `show_configuration_display_set.textfsm`。PAN-OS 配置视图的命令是 `show`,其 set 输出对应 `palo_alto/show.textfsm`。H3C 同一命令的列序由同一模板按表头切换状态。
|
|
10
10
|
|
|
@@ -56,4 +56,4 @@ NX-OS 模板独立放在 `cisco_nxos/`,CDP 按邻居块清理状态并与设
|
|
|
56
56
|
|
|
57
57
|
## 描述字段的词法与业务值
|
|
58
58
|
|
|
59
|
-
带引号的描述字段在模板中保留完整词法文本,`
|
|
59
|
+
带引号的描述字段在模板中保留完整词法文本,`Interfaces::QuotedText` 负责还原引号与转义,并将不完整字段转为不含原文的领域错误。Junos、FortiOS、PAN-OS 与 Radware 使用同一解码边界;是否拆分无引号文本由厂商策略明确指定。接口不存在描述语句时可返回空描述,`description ""` 等显式空字符串也有明确含义;缺少值的 description/comment/name 语句则是残缺输入,不能被默认空字段掩盖。
|
data/docs/usage.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[简体中文](usage.zh-CN.md) | [Home](../README.md) | [Netdisco and fleet backups](netdisco.md)
|
|
4
4
|
|
|
5
|
+
Requires Ruby 3.4+, expect-pty 0.9.4+ and a POSIX system.
|
|
6
|
+
|
|
5
7
|
## Supported devices
|
|
6
8
|
|
|
7
9
|
Use these vendor keys with `Net::Connector.open` or `Net::Connector.build`:
|
|
@@ -14,6 +16,7 @@ Use these vendor keys with `Net::Connector.open` or `Net::Connector.build`:
|
|
|
14
16
|
| `:cisco_nxos` | Cisco NX-OS | `show running-config` | `copy run start` |
|
|
15
17
|
| `:huawei` | Huawei | `dis cur` | `save force` |
|
|
16
18
|
| `:hillstone` | Hillstone StoneOS | `show configuration running` | `save all` |
|
|
19
|
+
| `:centec` | Centec / DclingCloudOS | `show running-config` | `copy running-config startup-config` |
|
|
17
20
|
| `:radware` | Radware Alteon | `/cfg/dump` | `/cfg/save` |
|
|
18
21
|
| `:palo_alto` | Palo Alto PAN-OS | Set-format export from configuration view | Unsupported |
|
|
19
22
|
| `:topsec` | Topsec TopOS firewall | `show-running nostop` | Unsupported |
|
|
@@ -80,13 +83,26 @@ Parsing validates UTF-8 before and after terminal rendering. Invalid bytes raise
|
|
|
80
83
|
## Topology and interface descriptions
|
|
81
84
|
|
|
82
85
|
```ruby
|
|
83
|
-
device.
|
|
86
|
+
interfaces = device.interfaces
|
|
87
|
+
neighbors = interfaces.neighbors
|
|
88
|
+
descriptions = interfaces.descriptions
|
|
89
|
+
plan = interfaces.plan
|
|
90
|
+
audit = interfaces.audit
|
|
91
|
+
|
|
92
|
+
# Apply the reviewed plan, or use interfaces.sync for a direct update.
|
|
93
|
+
interfaces.apply(plan, confirmed: true).value!
|
|
84
94
|
```
|
|
85
95
|
|
|
86
|
-
|
|
96
|
+
`device.interfaces` returns a reusable `Net::Connector::Interfaces` collection without accessing the device. It uses the device's session; each operation reads fresh evidence. `plan` and `audit` are read-only and return an immutable `Interfaces::Plan` and `Interfaces::Audit`. The audit also records interfaces whose evidence cannot be verified.
|
|
97
|
+
|
|
98
|
+
`interfaces.sync.value!` explicitly completes neighbor discovery, description changes, readback and save in one session. Matching descriptions cause no writes or saves. Callers do not manage a plan or a second confirmation step. Separate preview and approval uses `interfaces.plan`, then `interfaces.apply(plan, confirmed: true)`.
|
|
99
|
+
|
|
100
|
+
All three planning entries validate `abbreviate:` and `lowercase:` as booleans before device access. Planning holds the device session lease across neighbor and configuration reads; other threads or fibers cannot insert commands between them. The lease does not isolate administrators or other device sessions.
|
|
87
101
|
|
|
88
102
|
IOS/NX-OS use CDP; H3C, H3C wireless, Hillstone, and PAN-OS use LLDP. Neighbor records contain local interface, peer name/interface, chassis ID, and protocol. Unknown or partial output raises an error rather than becoming an empty table. Radware can read port names but cannot discover neighbors or apply descriptions.
|
|
89
103
|
|
|
104
|
+
Planning, audit and sync preserve semantically equal existing descriptions: case and equivalent port aliases (G/GE/Gi or SE/Smartrate-Ethernet) do not trigger writes. Different peer names, port numbers or speeds still require changes. Placeholder names or ports such as `-` and `unknown` cannot produce changes: audits retain unresolved issues, while direct planning/sync and application revalidation reject unknown identities.
|
|
105
|
+
|
|
90
106
|
The default description is `To_<peer name>_<interface abbreviation>`, for example `To_Core-SW_Gi1/0/1`. Known vendor spellings and case normalize to the same abbreviation; spaces in displayed names become underscores. Use `abbreviate: false`, `lowercase: true`, or a formatter block to customize them. Raw identities and evidence are unchanged.
|
|
91
107
|
|
|
92
108
|
Applying a separately approved plan requires `confirmed: true`. Both workflows recheck neighbor and description evidence. Changed evidence raises `:stale_plan` before writing. Supported immediate-change devices follow change, exit configuration, read back, then save. Failed/incomplete readback prevents save; unconfirmed persistence returns `:persistence_unconfirmed` with completed steps. Changes may already be active, so commands are never replayed or rolled back automatically.
|
|
@@ -145,6 +161,6 @@ Per-command prompts use `interactions: [Net::Connector::Interaction.new(/Token:\
|
|
|
145
161
|
|
|
146
162
|
## Dependencies and extensions
|
|
147
163
|
|
|
148
|
-
Load device APIs with `require "net/connector"`; Netdisco has a separate entrypoint. Vendor implementations and TextFSM load on demand.
|
|
164
|
+
Load device APIs with `require "net/connector"`; Netdisco has a separate entrypoint. Vendor implementations and TextFSM load on demand. The gemspec requires expect-pty 0.9.4+; the host application's lockfile selects concrete dependency versions. The minimum dependency matrix uses TextFSM 0.2.0 and JSON 2, with a separate fixed-source TextFSM patch matrix for JSON 3.
|
|
149
165
|
|
|
150
166
|
Examples use the development dependency `dotenv` to read the project `.env`; the library and CLI do not. Follow the [loading and migration reference](architecture.md#加载入口与厂商策略) (Chinese) for custom profiles, strategies, and protected hooks.
|
data/docs/usage.zh-CN.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[English](usage.md) | [返回首页](../README.zh-CN.md) | [Netdisco 批量备份](netdisco.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
支持 Ruby 3.
|
|
5
|
+
支持 Ruby 3.4 及以上版本和 POSIX 系统。SSH 调用本机 OpenSSH,Telnet 需要本机安装 `telnet` 并显式选择。要求 [`expect-pty`](https://rubygems.org/gems/expect-pty) 0.9.4 及以上;[`textfsm`](https://rubygems.org/gems/textfsm) 由宿主选择版本,最低依赖测试覆盖 0.2.0。
|
|
6
6
|
|
|
7
|
-
脱敏直接复用 expect-pty
|
|
7
|
+
脱敏直接复用 expect-pty 的公开 `Expect::Redactor` 接口;连接器只管理秘密作用域和配置输出的隐私策略。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
gemspec 声明 expect-pty 的接口版本下界;宿主应用通过自己的 Gemfile 和锁文件固定具体依赖版本。最低依赖组合使用 textfsm 0.2.0 与 JSON 2;JSON 3 另有固定 TextFSM 源码及显式依赖声明补丁的验证通道。开发工具及测试矩阵的版本约束不进入 gem 的运行依赖。
|
|
10
10
|
|
|
11
11
|
示例脚本通过开发依赖 `dotenv` 自动读取项目根目录 `.env`(先执行 `bundle install`),已有进程环境变量优先;库和 CLI 本身不会隐式读取 `.env`。
|
|
12
12
|
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
| `:palo_alto` | Palo Alto PAN-OS | 候选视图中的 set 格式导出 | 不支持 |
|
|
25
25
|
| `:huawei` | 华为 | `dis cur` | `save force` |
|
|
26
26
|
| `:hillstone` | 山石 StoneOS | `show configuration running` | `save all` |
|
|
27
|
+
| `:centec` | Centec / DclingCloudOS | `show running-config` | `copy running-config startup-config` |
|
|
27
28
|
| `:topsec` | 天融信 TopOS 防火墙 | `show-running nostop` | 未开放 |
|
|
28
29
|
| `:fortinet` | FortiGate / FortiOS | `show full-configuration` | `execute cfg save` |
|
|
29
30
|
| `:juniper` | Juniper Junos OS | `show configuration \| display set \| no-more` | 不隐式提交候选库 |
|
|
@@ -113,18 +114,29 @@ rows = saved.parse(host: "192.0.2.10", template: "/path/to/template.textfsm")
|
|
|
113
114
|
|
|
114
115
|
## 邻居发现与接口描述
|
|
115
116
|
|
|
116
|
-
`neighbors` 在 Cisco IOS/NX-OS 上查询 CDP,在 H3C、H3C 无线、山石和 PAN-OS 上查询 LLDP。记录包含 `local_interface`、`neighbor_name`、`neighbor_interface`、`chassis_id` 和 `protocol`。H3C 会按表头选择不同列顺序的模板;未知输出会抛出 `ParsingError`,不会误判为空表。`
|
|
117
|
+
`device.interfaces` 返回绑定当前设备的 `Net::Connector::Interfaces` 集合,获取对象不访问设备;它复用设备会话,每次操作重新读取现场证据。`interfaces.neighbors` 在 Cisco IOS/NX-OS 上查询 CDP,在 H3C、H3C 无线、山石和 PAN-OS 上查询 LLDP。记录包含 `local_interface`、`neighbor_name`、`neighbor_interface`、`chassis_id` 和 `protocol`。H3C 会按表头选择不同列顺序的模板;未知输出会抛出 `ParsingError`,不会误判为空表。`interfaces.descriptions` 从运行配置读取当前描述,Radware 则从配置转储读取端口名称。
|
|
117
118
|
|
|
118
119
|
```ruby
|
|
119
120
|
Net::Connector.open(:h3c, host: "192.0.2.10", username: ENV.fetch("NC_DEVICE_USERNAME"),
|
|
120
121
|
password: ENV.fetch("NC_DEVICE_PASSWORD")) do |device|
|
|
121
|
-
device.
|
|
122
|
+
interfaces = device.interfaces
|
|
123
|
+
neighbors = interfaces.neighbors
|
|
124
|
+
descriptions = interfaces.descriptions
|
|
125
|
+
plan = interfaces.plan
|
|
126
|
+
audit = interfaces.audit
|
|
127
|
+
|
|
128
|
+
# 审阅计划后显式应用;直接同步可使用 interfaces.sync。
|
|
129
|
+
interfaces.apply(plan, confirmed: true).value!
|
|
122
130
|
end
|
|
123
131
|
```
|
|
124
132
|
|
|
125
|
-
|
|
133
|
+
`plan` 和 `audit` 为只读操作,分别返回不可变的 `Interfaces::Plan` 和 `Interfaces::Audit`;审计还保留无法核实的接口问题。单独预览审批使用 `interfaces.plan` 与 `interfaces.apply(plan, confirmed: true)`。
|
|
126
134
|
|
|
127
|
-
|
|
135
|
+
显式调用 `interfaces.sync.value!`,在同一连接中完成发现、修改、读回和保存,不需要处理计划或二次确认;已有描述一致时不修改、不保存。三个规划入口都在访问设备前校验 `abbreviate:`、`lowercase:` 必须为布尔值。规划的邻居和配置读取全程持有设备会话租约,其他线程或 Fiber 不能插入命令;租约不隔离管理员和其他设备会话。
|
|
136
|
+
|
|
137
|
+
规划、审计与同步会保留语义一致的旧描述:仅大小写或同类接口别名(如 G/GE/Gi、SE/Smartrate-Ethernet)不同不触发写入。邻居名称或端口编号、速率不同仍需修正。未知名称或端口占位符(如 `-`、`unknown`)不能形成变更,审计记录问题,直接规划/同步及下发重验会拒绝未知身份。
|
|
138
|
+
|
|
139
|
+
默认描述为 `To_<邻居名称>_<接口简称>`,例如 `To_Core-SW_Gi1/0/1`。同类接口的厂商全称、简称和大小写统一,显示文本中的空格替换为下划线。端口、拆分通道与子接口编号完整保留;原始邻居记录和本机下发接口拼写不改变。`abbreviate: false` 保留对端接口全称,`lowercase: true` 仅将对端接口简称转为小写;后端也可用代码块定制。公共纯函数是 `Net::Connector::Interfaces::InterfaceDescription.format`。
|
|
128
140
|
|
|
129
141
|
| 接口类型 | 厂商名称与别名 | 描述简称 |
|
|
130
142
|
| --- | --- | --- |
|
data/docs/vendors.md
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 厂商能力与验证
|
|
2
|
+
|
|
3
|
+
## Centec / DclingCloudOS
|
|
4
|
+
|
|
5
|
+
`centec` 使用独立的 `Vendor::Centec` 档案,Netdisco 的 `Centec Networks Inc.` 自动映射到该驱动。
|
|
6
|
+
配置采集依次使用 `terminal length 0`、`show running-config`,只移除边界回显,保留配置正文。
|
|
7
|
+
TFTP 导出运行配置,只有非零 `Sent ... bytes in ... seconds` 完成行才确认上传;失败证据优先。
|
|
8
|
+
`tftp.source_interfaces.centec` 接受 `management`(命令追加 `mgmt-if`)或 `data`(使用默认数据路由),省略时沿用设备默认值。
|
|
9
|
+
示例备份配置默认使用管理口;不更改设备的持久路由或接口配置。
|
|
10
|
+
保存命令为显式能力 `copy running-config startup-config`,本次未执行现场保存;拓扑读写能力未开放。
|
|
11
|
+
|
|
12
|
+
## Topsec、Fortinet 与 Juniper
|
|
2
13
|
|
|
3
14
|
三个平台使用 `Vendor` 注册表、Profile 和现有会话执行器,均支持 SSH/Telnet 命令执行、运行配置采集与本地私有备份。能力声明区分设备系统,不能仅凭品牌把 FortiMail、ScreenOS 等产品映射到 FortiOS 或 Junos。
|
|
4
15
|
|
data/examples/inventory.yml
CHANGED
|
@@ -9,7 +9,7 @@ require_relative "save_config"
|
|
|
9
9
|
require_relative "local_backup"
|
|
10
10
|
require_relative "remote_backup"
|
|
11
11
|
require_relative "tftp"
|
|
12
|
-
require_relative "
|
|
12
|
+
require_relative "interfaces"
|
|
13
13
|
require_relative "../textfsm"
|
|
14
14
|
|
|
15
15
|
module Net
|
|
@@ -22,15 +22,19 @@ module Net
|
|
|
22
22
|
include LocalBackup::Capability
|
|
23
23
|
include RemoteBackup::Capability
|
|
24
24
|
include Tftp::Capability
|
|
25
|
-
include
|
|
25
|
+
include Interfaces::Capability
|
|
26
26
|
include TextFSM::Capability
|
|
27
27
|
|
|
28
|
+
# 读取当前设备的固定连接配置与有效命令超时。
|
|
28
29
|
attr_reader :configuration, :command_timeout
|
|
29
30
|
|
|
31
|
+
# 将连接、特权和状态查询委托给本设备持有的会话。
|
|
30
32
|
def_delegators :@session, :connected?, :privileged?, :state
|
|
33
|
+
# 将主机、用户名及日志位置查询委托给固定连接配置。
|
|
31
34
|
def_delegators :@configuration, :host, :username, :log_file
|
|
32
35
|
|
|
33
36
|
class << self
|
|
37
|
+
# 设置或读取厂商标识,子类未声明时沿用父类标识。
|
|
34
38
|
def vendor(key = nil)
|
|
35
39
|
return @vendor = key if key
|
|
36
40
|
|
|
@@ -92,6 +96,7 @@ module Net
|
|
|
92
96
|
# 返回当前连接器使用的不可变设备档案,便于检查厂商声明。
|
|
93
97
|
def profile = self.class.profile || Profile.default
|
|
94
98
|
|
|
99
|
+
# 读取当前设备类声明或继承的厂商标识。
|
|
95
100
|
def vendor = self.class.vendor
|
|
96
101
|
|
|
97
102
|
# 只查询实现能力,不连接设备;不代表现场权限或固件验证成功。
|
|
@@ -111,7 +116,7 @@ module Net
|
|
|
111
116
|
else
|
|
112
117
|
false
|
|
113
118
|
end
|
|
114
|
-
rescue
|
|
119
|
+
rescue ConfigurationError
|
|
115
120
|
false
|
|
116
121
|
end
|
|
117
122
|
|
|
@@ -163,20 +168,27 @@ module Net
|
|
|
163
168
|
@session.interact(input: input, output: output, escape: escape, timeout: timeout)
|
|
164
169
|
end
|
|
165
170
|
|
|
171
|
+
# 返回对象的安全摘要,不展开正文或认证凭据。
|
|
166
172
|
def inspect = "#<#{self.class} host=#{host.inspect} state=#{state}>"
|
|
167
173
|
|
|
168
174
|
protected
|
|
169
175
|
|
|
176
|
+
# 读取设备分页提示的匹配规则。
|
|
170
177
|
def pager_pattern = profile.pager_pattern
|
|
171
178
|
|
|
179
|
+
# 读取遇到分页提示时发送的续页文本。
|
|
172
180
|
def pager_response = profile.pager_response
|
|
173
181
|
|
|
182
|
+
# 读取设备密码输入提示的匹配规则。
|
|
174
183
|
def password_prompt = profile.password_prompt
|
|
175
184
|
|
|
185
|
+
# 读取设备用户名输入提示的匹配规则。
|
|
176
186
|
def username_prompt = profile.username_prompt
|
|
177
187
|
|
|
188
|
+
# 读取设备认证失败回显的匹配规则。
|
|
178
189
|
def authentication_error_patterns = profile.authentication_error_patterns
|
|
179
190
|
|
|
191
|
+
# 读取设备命令失败回显的匹配规则。
|
|
180
192
|
def command_error_patterns = profile.command_error_patterns
|
|
181
193
|
|
|
182
194
|
# 返回命令期间需要自动应答的确认对话。
|
|
@@ -185,14 +197,19 @@ module Net
|
|
|
185
197
|
# 返回登录期间需要自动应答的附加对话。
|
|
186
198
|
def login_interactions = profile.login_interactions
|
|
187
199
|
|
|
200
|
+
# 读取允许尝试的旧版 SSH 协商参数。
|
|
188
201
|
def legacy_ssh_arguments = profile.legacy_ssh_arguments
|
|
189
202
|
|
|
203
|
+
# 读取进入特权视图的命令;未配置时不隐式提权。
|
|
190
204
|
def enable_command = profile.privilege_command
|
|
191
205
|
|
|
206
|
+
# 读取特权认证完成提示的匹配规则。
|
|
192
207
|
def enable_prompt = profile.privilege_prompt
|
|
193
208
|
|
|
209
|
+
# 读取厂商声明的默认命令超时。
|
|
194
210
|
def default_command_timeout = profile.command_timeout
|
|
195
211
|
|
|
212
|
+
# 读取设备交互终端的行列尺寸。
|
|
196
213
|
def terminal_size = profile.terminal_size
|
|
197
214
|
|
|
198
215
|
# 返回登录完成提示;厂商必须实现。
|
|
@@ -200,7 +217,7 @@ module Net
|
|
|
200
217
|
prompt = profile.login_prompt
|
|
201
218
|
return prompt if prompt
|
|
202
219
|
|
|
203
|
-
raise
|
|
220
|
+
raise ConfigurationError, "#{self.class} must define login_prompt"
|
|
204
221
|
end
|
|
205
222
|
|
|
206
223
|
# 返回普通命令提示;厂商必须实现。
|
|
@@ -208,7 +225,7 @@ module Net
|
|
|
208
225
|
prompt = profile.command_prompt
|
|
209
226
|
return prompt if prompt
|
|
210
227
|
|
|
211
|
-
raise
|
|
228
|
+
raise ConfigurationError, "#{self.class} must define command_prompt"
|
|
212
229
|
end
|
|
213
230
|
|
|
214
231
|
# 登录成功后执行厂商初始化钩子。
|
|
@@ -221,7 +238,7 @@ module Net
|
|
|
221
238
|
execution.enable(enable_command, enable_prompt)
|
|
222
239
|
end
|
|
223
240
|
|
|
224
|
-
#
|
|
241
|
+
# 返回调整后的 Command 或 nil 跳过该命令;其他类型在发送前拒绝。
|
|
225
242
|
def prepare_command(command, _execution) = command
|
|
226
243
|
|
|
227
244
|
# 单条命令完成后执行厂商后处理钩子。
|
|
@@ -268,6 +285,7 @@ module Net
|
|
|
268
285
|
Result.new(steps: execution ? execution.steps : [], error: error)
|
|
269
286
|
end
|
|
270
287
|
|
|
288
|
+
# 组合厂商准备与后处理钩子,建立单次脚本的执行状态。
|
|
271
289
|
def build_execution(operation:, prompt:, after_command:, privilege:)
|
|
272
290
|
finish_step = lambda do |command, response, context|
|
|
273
291
|
self.after_command(command, response, context)
|