net-connector 0.18.1 → 0.19.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 +4 -0
- data/README.md +19 -0
- data/lib/net/connector/credentials.rb +39 -0
- data/lib/net/connector/netdisco/catalog.rb +53 -0
- data/lib/net/connector/netdisco/fleet.rb +5 -28
- data/lib/net/connector/netdisco/settings.rb +1 -7
- data/lib/net/connector/version.rb +1 -1
- data/lib/net/connector.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fe3f1f3bc71cf02e0007ec892699ea03fb13f30b697f5e318ca256159e77a466
|
|
4
|
+
data.tar.gz: 7f2ed60b91541c0eb362b1dc1c063661f5f22eac07719f1ed9d56f552a7d175b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03ea333dc6fdc10956b58478de7b67d8e3061345b3d96af8601a847dd393b158cd0bdbd7e4ca3ebc69c7cdca03d58ced76e34964b0370d9b2b0dad27c4d1f9dc
|
|
7
|
+
data.tar.gz: 38784dee207a4b5161add61183ebb49cf7b4a7a843171a29da2effe393105cd9c4bd00f2a0d3780ab296c430149a43792c85b9054375cb1c97269e89b1ad7f96
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.19.0 - 2026-10-29
|
|
6
|
+
|
|
7
|
+
- Extract `Netdisco::Catalog#devices(policy:)` for immutable inventory without credentials or operations; Fleet delegates inventory access. Add live, injected `Credentials#resolve(platform:)` with platform-specific username/password/enable-password precedence and independent secret snapshots.
|
|
8
|
+
|
|
5
9
|
## 0.18.1 - 2026-10-24
|
|
6
10
|
|
|
7
11
|
- Stream SSH archive bodies through bounded binary frames and incremental hashes instead of whole-file base64 copies; preserve deadlines, verified server receipts and committed local paths.
|
data/README.md
CHANGED
|
@@ -66,3 +66,22 @@ Licensed under [MIT](LICENSE).
|
|
|
66
66
|
## ctx_cli 公共入口
|
|
67
67
|
|
|
68
68
|
宿主配置文件读取、私有文件初始化、公共参数与终端交互由 ctx_cli 维护。原 `Net::Connector::Config`、配置路径、优先级和 backup/TFTP/topology 策略继续保留。源码联调使用 `CTX_CLI_PATH=../ctx_cli`;隔离安装可通过 `CTX_CLI_GEM=/absolute/path/ctx_cli-0.1.0.gem` 传入精确候选。
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## Inventory and credentials without device operations
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
require "net/connector/netdisco/catalog"
|
|
75
|
+
require "net/connector/credentials"
|
|
76
|
+
|
|
77
|
+
settings = Net::Connector::Netdisco::Settings.new(env: ENV)
|
|
78
|
+
policy = settings.snapshot(mode: :inventory)
|
|
79
|
+
catalog = Net::Connector::Netdisco::Catalog.new(settings: settings)
|
|
80
|
+
devices = catalog.devices(policy: policy)
|
|
81
|
+
# Inspect device.issue before selecting targets; duplicate rows are retained.
|
|
82
|
+
credentials = Net::Connector::Credentials.new(env: ENV).resolve(platform: :cisco_nxos)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Catalog owns inventory reading, normalization, duplicate marking and cache status (`#status`). It does not resolve device credentials, log into devices or create operation reports. Pass one policy snapshot throughout a run. Fleet delegates to Catalog and retains operation orchestration.
|
|
86
|
+
|
|
87
|
+
Credentials reads the injected environment on every `resolve`; it returns an independent frozen hash, or nil when no username is available. Platform-specific `NC_<PLATFORM>_USERNAME`, `_PASSWORD`, and `_ENABLE_PASSWORD` override `NC_DEVICE_USERNAME`, `NC_DEVICE_PASSWORD`, and `NC_ENABLE_PASSWORD` respectively. Blank values fall back. Secrets are never cached or included in object inspection.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Net
|
|
4
|
+
module Connector
|
|
5
|
+
# 设备凭据的字段优先级;借用来源,每次解析独立快照,不缓存秘密。
|
|
6
|
+
class Credentials
|
|
7
|
+
def initialize(env: ENV)
|
|
8
|
+
raise ArgumentError, "Credential source must support key lookup" unless env.respond_to?(:[])
|
|
9
|
+
@env = env
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# 平台字段优先于通用字段;缺少用户名时由宿主决定跳过或拒绝。
|
|
13
|
+
def resolve(platform:)
|
|
14
|
+
unless (platform.is_a?(String) || platform.is_a?(Symbol)) && /\A[a-z][a-z0-9_]*\z/i.match?(platform.to_s)
|
|
15
|
+
raise ArgumentError, "Credential platform must be a name"
|
|
16
|
+
end
|
|
17
|
+
prefix = "NC_#{platform.to_s.upcase}_"
|
|
18
|
+
username = value(prefix + "USERNAME") || value("NC_DEVICE_USERNAME")
|
|
19
|
+
return nil unless username
|
|
20
|
+
|
|
21
|
+
{ username: username, password: value(prefix + "PASSWORD") || value("NC_DEVICE_PASSWORD"),
|
|
22
|
+
enable_password: value(prefix + "ENABLE_PASSWORD") || value("NC_ENABLE_PASSWORD") }.freeze
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def inspect = "#<#{self.class}>"
|
|
26
|
+
alias to_s inspect
|
|
27
|
+
def pretty_print(printer) = printer.text(inspect)
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# 拒绝非法值而不回显内容,也不冻结调用方的字符串。
|
|
32
|
+
def value(key)
|
|
33
|
+
result = @env[key]
|
|
34
|
+
raise ArgumentError, "#{key} must be a String" unless result.nil? || result.is_a?(String)
|
|
35
|
+
result.dup.freeze unless result.nil? || result.strip.empty?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "settings"
|
|
4
|
+
require_relative "device"
|
|
5
|
+
|
|
6
|
+
module Net
|
|
7
|
+
module Connector
|
|
8
|
+
module Netdisco
|
|
9
|
+
# 只读取、归一化及标记清单;凭据、连接、操作和报告由宿主拥有。
|
|
10
|
+
class Catalog
|
|
11
|
+
attr_reader :status
|
|
12
|
+
|
|
13
|
+
def initialize(settings: Settings.new, inventory: nil, rules: nil)
|
|
14
|
+
@settings, @inventory, @rules = settings, inventory, rules
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# 使用调用方固定的同批策略,保留被过滤、无效及重复的记录。
|
|
18
|
+
def devices(policy:, force_refresh: false)
|
|
19
|
+
@status = nil
|
|
20
|
+
raise ArgumentError, "force_refresh must be true or false" unless [true, false].include?(force_refresh)
|
|
21
|
+
|
|
22
|
+
inventory = @inventory || @settings.inventory(policy: policy)
|
|
23
|
+
cache_mode = policy.inventory_cache_mode
|
|
24
|
+
raise ArgumentError, "cache-only cannot force refresh" if cache_mode == :only && force_refresh
|
|
25
|
+
if cache_mode == :only && !inventory.is_a?(Inventory::Cache)
|
|
26
|
+
raise Inventory::Error.new("validated inventory cache is unavailable", code: :cache_unavailable), cause: nil
|
|
27
|
+
end
|
|
28
|
+
rows = if inventory.is_a?(Inventory::Cache)
|
|
29
|
+
inventory.devices(force_refresh: force_refresh, cache_mode: cache_mode)
|
|
30
|
+
else
|
|
31
|
+
inventory.devices
|
|
32
|
+
end
|
|
33
|
+
@status = if inventory.is_a?(Inventory::Cache)
|
|
34
|
+
{ source: inventory.status, fetched_at: inventory.fetched_at,
|
|
35
|
+
refresh_error_code: inventory.refresh_error_code, write_error_type: inventory.write_error_type }.freeze
|
|
36
|
+
else
|
|
37
|
+
{ source: :uncached }.freeze
|
|
38
|
+
end
|
|
39
|
+
raise Inventory::Error, "Netdisco returned an invalid device inventory" unless rows.is_a?(Array)
|
|
40
|
+
|
|
41
|
+
rules = @rules || policy.rules
|
|
42
|
+
devices = rows.map { |row| Device.from_row(row, rules: rules) }
|
|
43
|
+
host_counts = devices.each_with_object(Hash.new(0)) { |device, counts| counts[device.host] += 1 if device.host }
|
|
44
|
+
devices.map { |device| host_counts[device.host] > 1 ? device.with(issue: :duplicate_host) : device }.freeze
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def inspect = "#<#{self.class}>"
|
|
48
|
+
alias to_s inspect
|
|
49
|
+
def pretty_print(printer) = printer.text(inspect)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -4,6 +4,7 @@ require "fileutils"
|
|
|
4
4
|
require_relative "../storage"
|
|
5
5
|
require_relative "../device/scope"
|
|
6
6
|
require_relative "inventory"
|
|
7
|
+
require_relative "catalog"
|
|
7
8
|
require_relative "settings"
|
|
8
9
|
require_relative "device"
|
|
9
10
|
require_relative "device/access"
|
|
@@ -35,8 +36,7 @@ module Net
|
|
|
35
36
|
def initialize(inventory: nil, settings: Settings.new, rules: nil, credentials: nil, connector_factory: nil,
|
|
36
37
|
report_store: Report::Store::Json.new)
|
|
37
38
|
@settings = settings
|
|
38
|
-
@
|
|
39
|
-
@rules = rules
|
|
39
|
+
@catalog = Catalog.new(settings: settings, inventory: inventory, rules: rules)
|
|
40
40
|
@credentials = credentials
|
|
41
41
|
@connector_factory = connector_factory || ->(device, access_options) { device.build_connector(**access_options) }
|
|
42
42
|
@report_store = report_store
|
|
@@ -176,32 +176,9 @@ module Net
|
|
|
176
176
|
|
|
177
177
|
# 策略在清单请求之前固定;注入的清单来源仍由调用方负责其自身生命周期。
|
|
178
178
|
def inventory(settings_snapshot, force_refresh: false)
|
|
179
|
-
@
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
inventory = @inventory || @settings.inventory(policy: settings_snapshot)
|
|
183
|
-
cache_mode = settings_snapshot.inventory_cache_mode
|
|
184
|
-
raise ArgumentError, "cache-only cannot force refresh" if cache_mode == :only && force_refresh
|
|
185
|
-
if cache_mode == :only && !inventory.is_a?(Inventory::Cache)
|
|
186
|
-
raise Inventory::Error.new("validated inventory cache is unavailable", code: :cache_unavailable), cause: nil
|
|
187
|
-
end
|
|
188
|
-
rows = if inventory.is_a?(Inventory::Cache)
|
|
189
|
-
inventory.devices(force_refresh: force_refresh, cache_mode: cache_mode)
|
|
190
|
-
else
|
|
191
|
-
inventory.devices
|
|
192
|
-
end
|
|
193
|
-
@inventory_status = if inventory.is_a?(Inventory::Cache)
|
|
194
|
-
{ source: inventory.status, fetched_at: inventory.fetched_at,
|
|
195
|
-
refresh_error_code: inventory.refresh_error_code, write_error_type: inventory.write_error_type }.freeze
|
|
196
|
-
else
|
|
197
|
-
{ source: :uncached }.freeze
|
|
198
|
-
end
|
|
199
|
-
raise Inventory::Error, "Netdisco returned an invalid device inventory" unless rows.is_a?(Array)
|
|
200
|
-
|
|
201
|
-
rules = @rules || settings_snapshot.rules
|
|
202
|
-
devices = rows.map { |row| Device.from_row(row, rules: rules) }
|
|
203
|
-
host_counts = devices.each_with_object(Hash.new(0)) { |device, counts| counts[device.host] += 1 if device.host }
|
|
204
|
-
devices.map { |device| host_counts[device.host] > 1 ? device.with(issue: :duplicate_host) : device }.freeze
|
|
179
|
+
@catalog.devices(policy: settings_snapshot, force_refresh: force_refresh)
|
|
180
|
+
ensure
|
|
181
|
+
@inventory_status = @catalog.status
|
|
205
182
|
end
|
|
206
183
|
|
|
207
184
|
# 在固定清单快照上生成模式对应的计划,规划阶段不读取设备凭据。
|
|
@@ -346,13 +346,7 @@ module Net
|
|
|
346
346
|
|
|
347
347
|
# 每台任务读取最新厂商凭据,缺失字段回退到通用凭据;无用户名时跳过设备。
|
|
348
348
|
def device_credentials_for(device)
|
|
349
|
-
|
|
350
|
-
username = secret_value(prefix + "USERNAME") || secret_value("NC_DEVICE_USERNAME")
|
|
351
|
-
return nil unless username
|
|
352
|
-
|
|
353
|
-
{ username: username,
|
|
354
|
-
password: secret_value(prefix + "PASSWORD") || secret_value("NC_DEVICE_PASSWORD"),
|
|
355
|
-
enable_password: secret_value(prefix + "ENABLE_PASSWORD") || secret_value("NC_ENABLE_PASSWORD") }
|
|
349
|
+
Net::Connector::Credentials.new(env: @env).resolve(platform: device.vendor)
|
|
356
350
|
end
|
|
357
351
|
|
|
358
352
|
# 会话日志目录属于公开策略;调用方和批次入口负责实际创建。
|
data/lib/net/connector.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: net-connector
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- net-connector contributors
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-29 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ctx_cli
|
|
@@ -375,6 +375,7 @@ files:
|
|
|
375
375
|
- lib/net/connector.rb
|
|
376
376
|
- lib/net/connector/collect/progress.rb
|
|
377
377
|
- lib/net/connector/config.rb
|
|
378
|
+
- lib/net/connector/credentials.rb
|
|
378
379
|
- lib/net/connector/device/base.rb
|
|
379
380
|
- lib/net/connector/device/interfaces.rb
|
|
380
381
|
- lib/net/connector/device/interfaces/audit.rb
|
|
@@ -436,6 +437,7 @@ files:
|
|
|
436
437
|
- lib/net/connector/inventory/selection.rb
|
|
437
438
|
- lib/net/connector/netdisco.rb
|
|
438
439
|
- lib/net/connector/netdisco/batch.rb
|
|
440
|
+
- lib/net/connector/netdisco/catalog.rb
|
|
439
441
|
- lib/net/connector/netdisco/cli.rb
|
|
440
442
|
- lib/net/connector/netdisco/cli/backup.rb
|
|
441
443
|
- lib/net/connector/netdisco/cli/credentials.rb
|