net-connector 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -65
  3. data/README.md +88 -95
  4. data/docs/VERIFICATION.md +2 -1
  5. data/docs/architecture.md +144 -248
  6. data/lib/net/connector/device/base.rb +1 -0
  7. data/lib/net/connector/device/interface_description.rb +3 -0
  8. data/lib/net/connector/device/profile/builder.rb +236 -0
  9. data/lib/net/connector/device/profile.rb +2 -227
  10. data/lib/net/connector/device/running_config/strategy.rb +5 -0
  11. data/lib/net/connector/device/running_config.rb +5 -0
  12. data/lib/net/connector/engine/dialogue.rb +1 -0
  13. data/lib/net/connector/engine/errors.rb +2 -0
  14. data/lib/net/connector/engine/log.rb +13 -9
  15. data/lib/net/connector/engine/session.rb +1 -0
  16. data/lib/net/connector/netdisco/cli.rb +20 -20
  17. data/lib/net/connector/netdisco/device.rb +4 -8
  18. data/lib/net/connector/netdisco/fleet.rb +2 -48
  19. data/lib/net/connector/netdisco/plan.rb +77 -0
  20. data/lib/net/connector/netdisco/planner.rb +12 -23
  21. data/lib/net/connector/netdisco/worker.rb +31 -24
  22. data/lib/net/connector/operations/tftp/file_upload.rb +42 -0
  23. data/lib/net/connector/operations/tftp/strategy.rb +9 -11
  24. data/lib/net/connector/operations/topology/strategy.rb +14 -0
  25. data/lib/net/connector/operations/topology.rb +1 -0
  26. data/lib/net/connector/vendor/cisco_ios/running_config.rb +1 -0
  27. data/lib/net/connector/vendor/cisco_ios/topology.rb +9 -0
  28. data/lib/net/connector/vendor/cisco_nxos/running_config.rb +1 -0
  29. data/lib/net/connector/vendor/h3c/tftp_backup.rb +2 -18
  30. data/lib/net/connector/vendor/h3c/topology.rb +9 -0
  31. data/lib/net/connector/vendor/hillstone/running_config.rb +1 -0
  32. data/lib/net/connector/vendor/hillstone/tftp_backup.rb +4 -0
  33. data/lib/net/connector/vendor/hillstone/topology.rb +9 -0
  34. data/lib/net/connector/vendor/huawei/tftp_backup.rb +2 -18
  35. data/lib/net/connector/vendor/palo_alto/running_config.rb +4 -0
  36. data/lib/net/connector/vendor/palo_alto/tftp_backup.rb +7 -5
  37. data/lib/net/connector/vendor/palo_alto/topology.rb +10 -0
  38. data/lib/net/connector/vendor/radware/tftp_backup.rb +2 -2
  39. data/lib/net/connector/vendor/radware/topology.rb +2 -0
  40. data/lib/net/connector/version.rb +1 -1
  41. metadata +26 -24
@@ -6,6 +6,9 @@ module Net
6
6
  module Connector
7
7
  module Hillstone
8
8
  class TftpBackup < Operations::Tftp::Strategy
9
+ # 山石原生导出的配置文件使用 dat 扩展名。
10
+ def self.file_extension = "dat"
11
+
9
12
  # 构造当前厂商的 TFTP 导出交互脚本。
10
13
  def script(target, source_file:, vrf: nil)
11
14
  raise ArgumentError, "Hillstone TFTP export does not use source_file" if source_file
@@ -31,6 +34,7 @@ module Net
31
34
 
32
35
  private
33
36
 
37
+ # 仅认可设备明确给出目标文件名的 Export ok 回显。
34
38
  def completion_line(result)
35
39
  completion_lines(result).find { |line| line.match?(/\AExport\s+ok\s*,\s*target\s+file\s+name\s+\S+\z/i) }
36
40
  end
@@ -6,15 +6,21 @@ module Net
6
6
  module Connector
7
7
  module Hillstone
8
8
  class Topology < Operations::Topology::Strategy
9
+ # 山石提供 LLDP 邻居、配置描述读取和描述变更。
9
10
  def self.supports?(capability)
10
11
  %i[neighbors interface_descriptions interface_description_changes].include?(capability)
11
12
  end
12
13
 
14
+ # 读取 LLDP 邻居信息。
13
15
  def neighbor_command = "show lldp neighbor-information"
16
+ # 从运行配置解析接口描述。
14
17
  def description_template = "hillstone_interface_descriptions.textfsm"
18
+ # 进入配置视图。
15
19
  def enter_configuration = "configure"
20
+ # 退出配置视图并保存设备配置。
16
21
  def finish_commands = ["exit"] + @device.profile.save_commands
17
22
 
23
+ # 用数据行和设备报告的总数交叉核对,避免遗漏邻居。
18
24
  def expected_neighbor_count(output, _template)
19
25
  reported = output[/Total lldp neighbor number:\s*(\d+)/i, 1]
20
26
  lines = neighbor_lines(output)
@@ -24,17 +30,20 @@ module Net
24
30
  lines.size
25
31
  end
26
32
 
33
+ # 仅在列表为空且有明确零邻居证据时认可空表。
27
34
  def empty_neighbor_output?(output)
28
35
  neighbor_lines(output).empty? && (output.match?(/Total lldp neighbor number:\s*0\b/i) ||
29
36
  output.match?(/System Name\s+Local Interface/i))
30
37
  end
31
38
 
39
+ # 复用公共接口描述命令。
32
40
  def change_commands(change)
33
41
  InterfaceDescription.commands(interface: change.interface, description: change.new_description)
34
42
  end
35
43
 
36
44
  private
37
45
 
46
+ # 去掉命令、表头、总数和提示符,只保留邻居数据行。
38
47
  def neighbor_lines(output)
39
48
  output.lines.map(&:strip).reject do |line|
40
49
  line.empty? || line == neighbor_command || line.match?(/\ASystem Name\s+Local Interface\b/i) ||
@@ -1,33 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/tftp/strategy"
3
+ require_relative "../../operations/tftp/file_upload"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module Huawei
8
- class TftpBackup < Operations::Tftp::Strategy
8
+ class TftpBackup < Operations::Tftp::FileUpload
9
9
  # 要求调用方提供合法的华为启动配置文件路径。
10
10
  def source_file(value)
11
11
  raise ArgumentError, "Huawei TFTP backup requires source_file" unless value
12
12
 
13
13
  TftpTarget.validate_source_file!(value)
14
14
  end
15
-
16
- # 根据华为配置源文件确定默认导出名称。
17
- def default_path(source_file) = File.basename(source_file)
18
-
19
- # 构造当前厂商的 TFTP 导出交互脚本。
20
- def script(target, source_file:, vrf: nil)
21
- raise ArgumentError, "Huawei TFTP backup does not use vrf" if vrf
22
-
23
- destination = target.explicit_path? ? " #{target.path}" : ""
24
- Script.new([Command.new("tftp #{target.host} put #{source_file}#{destination}", timeout: 180)])
25
- end
26
-
27
- # 检查设备回显是否确认 TFTP 传输完成。
28
- def complete?(result)
29
- completion_lines(result).any? { |line| completed_upload?(line) }
30
- end
31
15
  end
32
16
  end
33
17
  end
@@ -7,6 +7,7 @@ module Net
7
7
  module Connector
8
8
  module PaloAlto
9
9
  class RunningConfig < Net::Connector::RunningConfig::Strategy
10
+ # 配置位于 show 步骤,最后的 exit 只负责离开候选视图。
10
11
  def result_step(result)
11
12
  result.steps.find { |step| step.command.text == "show" }
12
13
  end
@@ -21,6 +22,7 @@ module Net
21
22
  end
22
23
  end
23
24
 
25
+ # 采集前后都检查候选差异,拒绝把未提交配置误作运行配置。
24
26
  def check_response(command, response, _execution)
25
27
  return unless command.text == "show config diff"
26
28
  return if body(response.output, command.text).empty?
@@ -29,6 +31,7 @@ module Net
29
31
  code: :uncommitted_configuration, host: @device.host, phase: :collect)
30
32
  end
31
33
 
34
+ # 只接受完整的 set 命令文本,排除 XML 或截断的引号配置。
32
35
  def clean(text)
33
36
  normalized = body(text, "show").sub(/(?:\A|\n)[ \t]*\[edit\][ \t]*\z/, "").strip
34
37
  unless set_commands?(normalized)
@@ -60,6 +63,7 @@ module Net
60
63
  pending.empty?
61
64
  end
62
65
 
66
+ # 去掉命令回显和提示符,保留真正需要校验的设备响应。
63
67
  def body(text, command)
64
68
  text.gsub("\r\n", "\n").sub(/\A[ \t]*#{Regexp.escape(command)}[ \t]*\n/, "")
65
69
  .sub(@device.profile.command_prompt, "").strip
@@ -6,18 +6,20 @@ module Net
6
6
  module Connector
7
7
  module PaloAlto
8
8
  class TftpBackup < Operations::Tftp::Strategy
9
+ FILENAME = "running-config.xml"
10
+
9
11
  # 返回 PAN-OS 固定使用的运行配置文件名。
10
- def default_path(_source_file) = "running-config.xml"
12
+ def self.filename(_host, **) = FILENAME
11
13
 
12
14
  # 构造当前厂商的 TFTP 导出交互脚本。
13
15
  def script(target, source_file:, vrf: nil)
14
16
  raise ArgumentError, "PAN-OS does not use source_file" if source_file
15
17
  raise ArgumentError, "PAN-OS TFTP backup does not use vrf" if vrf
16
- unless target.path == "running-config.xml"
17
- raise ArgumentError, "PAN-OS TFTP export uses the fixed filename running-config.xml"
18
+ unless target.path == FILENAME
19
+ raise ArgumentError, "PAN-OS TFTP export uses the fixed filename #{FILENAME}"
18
20
  end
19
21
 
20
- Script.new([Command.new("tftp export configuration to #{target.host} from running-config.xml",
22
+ Script.new([Command.new("tftp export configuration to #{target.host} from #{FILENAME}",
21
23
  timeout: 180)])
22
24
  end
23
25
 
@@ -29,7 +31,7 @@ module Net
29
31
  end
30
32
 
31
33
  # 返回 PAN-OS 实际上传的固定文件名。
32
- def remote_path(_target, _result) = "running-config.xml"
34
+ def remote_path(_target, _result) = FILENAME
33
35
  end
34
36
  end
35
37
  end
@@ -6,19 +6,26 @@ module Net
6
6
  module Connector
7
7
  module PaloAlto
8
8
  class Topology < Operations::Topology::Strategy
9
+ # PAN-OS 提供 LLDP 邻居、接口备注读取和备注变更。
9
10
  def self.supports?(capability)
10
11
  %i[neighbors interface_descriptions interface_description_changes].include?(capability)
11
12
  end
12
13
 
14
+ # 读取各本机接口的 LLDP 邻居详情。
13
15
  def neighbor_command = "show lldp neighbors all"
16
+ # 从 set 格式配置解析接口备注。
14
17
  def description_template = "palo_alto_interface_descriptions.textfsm"
18
+ # 进入候选配置视图。
15
19
  def enter_configuration = "configure"
20
+ # 提交候选配置后退出配置视图。
16
21
  def finish_commands = ["commit", "exit"]
17
22
 
23
+ # 只计入非空邻居块,跳过没有对端的本机接口。
18
24
  def expected_neighbor_count(output, _template)
19
25
  neighbor_blocks(output).count { |block| !empty_neighbor_block?(block) }
20
26
  end
21
27
 
28
+ # 输出必须明确说明无邻居,或每个本机接口块都明确为空。
22
29
  def empty_neighbor_output?(output)
23
30
  return true if output.match?(/No LLDP neighbors/i)
24
31
 
@@ -34,6 +41,7 @@ module Net
34
41
  output.split(/^\s*Local information:\s*$/i).select { |block| block.match?(/^\s*Local interface:\s*\S+/i) }
35
42
  end
36
43
 
44
+ # 邻居字段区只有空行和提示符时,才视为明确的空块。
37
45
  def empty_neighbor_block?(block)
38
46
  tail = block.split(/^\s*Neighbor information:\s*$/i, 2)
39
47
  tail.size == 2 && tail.last.lines.all? { |line| line.strip.empty? || line.match?(/^\S+[>#]\s*$/) }
@@ -50,10 +58,12 @@ module Net
50
58
  code: :unrecognized_output, host: @device.host, phase: :parse)
51
59
  end
52
60
 
61
+ # 用 PAN-OS set 语法生成接口备注变更。
53
62
  def change_commands(change)
54
63
  [%Q(set network interface ethernet #{change.interface} comment "#{change.new_description}")]
55
64
  end
56
65
 
66
+ # commit 可能耗时较长,单独放宽该命令的超时。
57
67
  def script_command(command)
58
68
  command == "commit" ? Command.new(command, timeout: 300) : command
59
69
  end
@@ -6,8 +6,8 @@ module Net
6
6
  module Connector
7
7
  module Radware
8
8
  class TftpBackup < Operations::Tftp::Strategy
9
- # 生成 Radware 压缩配置的远端文件名。
10
- def default_path(_source_file) = TftpTarget.filename(@device.host, extension: "tgz")
9
+ # Radware 原生导出生成压缩配置归档。
10
+ def self.file_extension = "tgz"
11
11
 
12
12
  # 构造当前厂商的 TFTP 导出交互脚本。
13
13
  def script(target, source_file:, vrf: nil)
@@ -6,7 +6,9 @@ module Net
6
6
  module Connector
7
7
  module Radware
8
8
  class Topology < Operations::Topology::Strategy
9
+ # Alteon 只支持读取端口描述,不提供邻居发现或自动改写。
9
10
  def self.supports?(capability) = capability == :interface_descriptions
11
+ # 从设备配置转储解析端口名称。
10
12
  def description_template = "radware_port_names.textfsm"
11
13
  end
12
14
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Net
4
4
  module Connector
5
- VERSION = "0.4.0"
5
+ VERSION = "0.4.1"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-connector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - net-connector contributors
@@ -23,26 +23,6 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: 0.3.1
26
- - !ruby/object:Gem::Dependency
27
- name: activesupport
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: '7.2'
33
- - - "<"
34
- - !ruby/object:Gem::Version
35
- version: '9'
36
- type: :runtime
37
- prerelease: false
38
- version_requirements: !ruby/object:Gem::Requirement
39
- requirements:
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- version: '7.2'
43
- - - "<"
44
- - !ruby/object:Gem::Version
45
- version: '9'
46
26
  - !ruby/object:Gem::Dependency
47
27
  name: textfsm
48
28
  requirement: !ruby/object:Gem::Requirement
@@ -159,6 +139,26 @@ dependencies:
159
139
  - - "~>"
160
140
  - !ruby/object:Gem::Version
161
141
  version: '2.0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: logger
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '1.5'
149
+ - - "<"
150
+ - !ruby/object:Gem::Version
151
+ version: '2.0'
152
+ type: :runtime
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '1.5'
159
+ - - "<"
160
+ - !ruby/object:Gem::Version
161
+ version: '2.0'
162
162
  - !ruby/object:Gem::Dependency
163
163
  name: net-http
164
164
  requirement: !ruby/object:Gem::Requirement
@@ -373,8 +373,7 @@ dependencies:
373
373
  - - "<"
374
374
  - !ruby/object:Gem::Version
375
375
  version: '1.0'
376
- description: Automate network device login, configuration collection, command scripts,
377
- interactive prompts, redacted logging, and Netdisco inventory backups.
376
+ description: 通过 SSH 或 Telnet 登录网络设备,采集配置、执行命令脚本、处理交互提示,并提供脱敏日志与基于 Netdisco 清单的批量备份。
378
377
  executables:
379
378
  - net-connector-backup
380
379
  extensions: []
@@ -393,6 +392,7 @@ files:
393
392
  - lib/net/connector/device/interface_description.rb
394
393
  - lib/net/connector/device/interface_name.rb
395
394
  - lib/net/connector/device/profile.rb
395
+ - lib/net/connector/device/profile/builder.rb
396
396
  - lib/net/connector/device/running_config.rb
397
397
  - lib/net/connector/device/running_config/strategy.rb
398
398
  - lib/net/connector/engine.rb
@@ -419,6 +419,7 @@ files:
419
419
  - lib/net/connector/netdisco/config_file.rb
420
420
  - lib/net/connector/netdisco/device.rb
421
421
  - lib/net/connector/netdisco/fleet.rb
422
+ - lib/net/connector/netdisco/plan.rb
422
423
  - lib/net/connector/netdisco/planner.rb
423
424
  - lib/net/connector/netdisco/result_store.rb
424
425
  - lib/net/connector/netdisco/rules.rb
@@ -436,6 +437,7 @@ files:
436
437
  - lib/net/connector/operations/saved_config.rb
437
438
  - lib/net/connector/operations/tftp/cisco_ios.rb
438
439
  - lib/net/connector/operations/tftp/cisco_nxos.rb
440
+ - lib/net/connector/operations/tftp/file_upload.rb
439
441
  - lib/net/connector/operations/tftp/h3c.rb
440
442
  - lib/net/connector/operations/tftp/hillstone.rb
441
443
  - lib/net/connector/operations/tftp/huawei.rb
@@ -513,5 +515,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
513
515
  requirements: []
514
516
  rubygems_version: 4.0.12
515
517
  specification_version: 4
516
- summary: Network device CLI sessions and Netdisco-driven concurrent backups
518
+ summary: 网络设备命令行会话与 Netdisco 清单批量备份
517
519
  test_files: []