net-connector 0.4.2 → 0.5.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.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -1
  3. data/README.md +42 -19
  4. data/docs/VERIFICATION.md +17 -7
  5. data/docs/architecture.md +96 -64
  6. data/lib/net/connector/device/base.rb +27 -107
  7. data/lib/net/connector/device/local_backup.rb +96 -0
  8. data/lib/net/connector/device/profile.rb +8 -3
  9. data/lib/net/connector/device/running_config/strategy.rb +1 -1
  10. data/lib/net/connector/device/running_config.rb +44 -5
  11. data/lib/net/connector/device/save_config.rb +21 -0
  12. data/lib/net/connector/device/tftp/file_upload.rb +53 -0
  13. data/lib/net/connector/{operations/tftp_receipt.rb → device/tftp/receipt.rb} +18 -20
  14. data/lib/net/connector/device/tftp/strategy.rb +67 -0
  15. data/lib/net/connector/device/tftp/target.rb +63 -0
  16. data/lib/net/connector/device/tftp.rb +117 -0
  17. data/lib/net/connector/device/topology/immediate_strategy.rb +40 -0
  18. data/lib/net/connector/device/topology/interface_description.rb +46 -0
  19. data/lib/net/connector/device/topology/interface_name.rb +58 -0
  20. data/lib/net/connector/device/topology/strategy.rb +75 -0
  21. data/lib/net/connector/device/topology.rb +277 -0
  22. data/lib/net/connector/engine/authentication.rb +1 -1
  23. data/lib/net/connector/engine/configuration.rb +1 -1
  24. data/lib/net/connector/engine/dialogue.rb +17 -17
  25. data/lib/net/connector/engine/error_metadata.rb +50 -0
  26. data/lib/net/connector/engine/errors.rb +15 -1
  27. data/lib/net/connector/engine/execution.rb +15 -8
  28. data/lib/net/connector/engine/log/event.rb +66 -0
  29. data/lib/net/connector/engine/log/formatter.rb +16 -0
  30. data/lib/net/connector/engine/log/messages.rb +52 -0
  31. data/lib/net/connector/engine/log/stream.rb +56 -0
  32. data/lib/net/connector/engine/log.rb +105 -84
  33. data/lib/net/connector/engine/session.rb +83 -42
  34. data/lib/net/connector/engine/terminal_text.rb +33 -0
  35. data/lib/net/connector/engine/transport.rb +2 -2
  36. data/lib/net/connector/netdisco/batch.rb +9 -45
  37. data/lib/net/connector/netdisco/cli.rb +7 -9
  38. data/lib/net/connector/netdisco/client.rb +2 -2
  39. data/lib/net/connector/netdisco/device.rb +4 -5
  40. data/lib/net/connector/netdisco/diagnostic.rb +9 -39
  41. data/lib/net/connector/netdisco/fleet.rb +26 -39
  42. data/lib/net/connector/netdisco/report.rb +37 -16
  43. data/lib/net/connector/netdisco/result_store.rb +2 -2
  44. data/lib/net/connector/netdisco/settings.rb +0 -6
  45. data/lib/net/connector/{operations → storage}/backup_lock.rb +2 -2
  46. data/lib/net/connector/{operations → storage}/private_file.rb +2 -7
  47. data/lib/net/connector/{operations → storage}/safe_file.rb +1 -1
  48. data/lib/net/connector/{operations → storage}/saved_config.rb +10 -35
  49. data/lib/net/connector/storage.rb +15 -0
  50. data/lib/net/connector/textfsm.rb +72 -0
  51. data/lib/net/connector/vendor/cisco_ios/tftp_backup.rb +3 -3
  52. data/lib/net/connector/vendor/cisco_ios/topology.rb +5 -5
  53. data/lib/net/connector/vendor/cisco_nxos/tftp_backup.rb +3 -3
  54. data/lib/net/connector/vendor/h3c/tftp_backup.rb +4 -4
  55. data/lib/net/connector/vendor/h3c/topology.rb +5 -5
  56. data/lib/net/connector/vendor/h3c.rb +3 -3
  57. data/lib/net/connector/vendor/hillstone/tftp_backup.rb +3 -3
  58. data/lib/net/connector/vendor/hillstone/topology.rb +5 -5
  59. data/lib/net/connector/vendor/huawei/tftp_backup.rb +3 -3
  60. data/lib/net/connector/vendor/palo_alto/running_config.rb +1 -1
  61. data/lib/net/connector/vendor/palo_alto/tftp_backup.rb +3 -3
  62. data/lib/net/connector/vendor/palo_alto/topology.rb +3 -6
  63. data/lib/net/connector/vendor/radware/tftp_backup.rb +3 -3
  64. data/lib/net/connector/vendor/radware/topology.rb +2 -2
  65. data/lib/net/connector/version.rb +1 -1
  66. data/lib/net/connector.rb +1 -6
  67. metadata +25 -39
  68. data/lib/net/connector/device/interface_description.rb +0 -44
  69. data/lib/net/connector/device/interface_name.rb +0 -56
  70. data/lib/net/connector/engine/base.rb +0 -4
  71. data/lib/net/connector/engine/log_messages.rb +0 -47
  72. data/lib/net/connector/engine/profile.rb +0 -4
  73. data/lib/net/connector/engine.rb +0 -4
  74. data/lib/net/connector/operations/local_backup.rb +0 -89
  75. data/lib/net/connector/operations/parse_output.rb +0 -77
  76. data/lib/net/connector/operations/running_config/cisco.rb +0 -14
  77. data/lib/net/connector/operations/running_config/hillstone.rb +0 -12
  78. data/lib/net/connector/operations/running_config/palo_alto.rb +0 -12
  79. data/lib/net/connector/operations/running_config/strategy.rb +0 -3
  80. data/lib/net/connector/operations/running_config.rb +0 -16
  81. data/lib/net/connector/operations/saved_config/legacy_index.rb +0 -109
  82. data/lib/net/connector/operations/tftp/cisco_ios.rb +0 -11
  83. data/lib/net/connector/operations/tftp/cisco_nxos.rb +0 -11
  84. data/lib/net/connector/operations/tftp/file_upload.rb +0 -55
  85. data/lib/net/connector/operations/tftp/h3c.rb +0 -11
  86. data/lib/net/connector/operations/tftp/hillstone.rb +0 -11
  87. data/lib/net/connector/operations/tftp/huawei.rb +0 -11
  88. data/lib/net/connector/operations/tftp/palo_alto.rb +0 -11
  89. data/lib/net/connector/operations/tftp/radware.rb +0 -11
  90. data/lib/net/connector/operations/tftp/strategy.rb +0 -75
  91. data/lib/net/connector/operations/tftp_backup.rb +0 -183
  92. data/lib/net/connector/operations/topology/cisco.rb +0 -11
  93. data/lib/net/connector/operations/topology/h3c.rb +0 -11
  94. data/lib/net/connector/operations/topology/hillstone.rb +0 -11
  95. data/lib/net/connector/operations/topology/immediate_strategy.rb +0 -45
  96. data/lib/net/connector/operations/topology/palo_alto.rb +0 -11
  97. data/lib/net/connector/operations/topology/radware.rb +0 -11
  98. data/lib/net/connector/operations/topology/strategy.rb +0 -87
  99. data/lib/net/connector/operations/topology.rb +0 -261
  100. data/lib/net/connector/operations.rb +0 -26
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/topology/immediate_strategy"
3
+ require_relative "../../device/topology/immediate_strategy"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module H3c
8
- class Topology < Operations::Topology::ImmediateStrategy
8
+ class Topology < Topology::ImmediateStrategy
9
9
  TABLE_HEADER = /^\s*(?:System Name\s+Local Interface\b|(?:Local Interface|LocalIf)\s+)/i
10
10
  # H3C 提供 LLDP 邻居、配置描述读取和描述变更。
11
11
  def self.supports?(capability)
@@ -19,10 +19,10 @@ module Net
19
19
  def description_template = "h3c_interface_descriptions.textfsm"
20
20
 
21
21
  # 进入系统视图以修改接口描述。
22
- def enter_configuration = "system-view"
22
+ def enter_configuration_command = "system-view"
23
23
 
24
24
  # 返回用户视图读回;保存交给后续独立阶段。
25
- def leave_configuration = ["return"]
25
+ def leave_configuration_commands = ["return"]
26
26
 
27
27
  def persistence_pattern = /\ASaved the current configuration to mainboard device successfully\.\z/i
28
28
 
@@ -52,7 +52,7 @@ module Net
52
52
 
53
53
  # 在接口视图设置描述后用 quit 返回系统视图。
54
54
  def change_commands(change)
55
- InterfaceDescription.commands(interface: change.interface, description: change.new_description, leave: "quit")
55
+ Net::Connector::Topology::InterfaceDescription.commands(interface: change.interface, description: change.new_description, leave: "quit")
56
56
  end
57
57
 
58
58
  private
@@ -56,7 +56,7 @@ module Net
56
56
  return command unless /\A[ \t]*rule[ \t]+XXX[ \t]+append\b/i.match?(command.text)
57
57
 
58
58
  rule_id = execution.context[:rule_id]
59
- raise execution.failure("new rule ID has not been acquired") unless rule_id
59
+ raise execution.build_error("new rule ID has not been acquired") unless rule_id
60
60
 
61
61
  command.with_text(command.text.sub(/XXX/i, rule_id))
62
62
  end
@@ -65,9 +65,9 @@ module Net
65
65
  def after_command(command, _response, execution)
66
66
  return unless /\A[ \t]*rule[ \t]+pass\b/i.match?(command.text)
67
67
 
68
- response = execution.query("dis this")
68
+ response = execution.execute_command("dis this")
69
69
  rule_id = response.raw[/rule\s+(\d+)\s+pass[^\n]+\n\s*#/i, 1]
70
- raise execution.failure("device did not return the new rule ID") unless rule_id
70
+ raise execution.build_error("device did not return the new rule ID") unless rule_id
71
71
 
72
72
  execution.context[:rule_id] = rule_id
73
73
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/tftp/strategy"
3
+ require_relative "../../device/tftp/strategy"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module Hillstone
8
- class TftpBackup < Operations::Tftp::Strategy
8
+ class TftpBackup < Tftp::Strategy
9
9
  # 山石原生导出的配置文件使用 dat 扩展名。
10
10
  def self.file_extension = "dat"
11
11
 
@@ -31,7 +31,7 @@ module Net
31
31
  end
32
32
 
33
33
  # 检查设备回显是否确认 TFTP 传输完成。
34
- def complete?(result)
34
+ def device_reported_complete?(result)
35
35
  !completion_line(result).nil?
36
36
  end
37
37
 
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/topology/immediate_strategy"
3
+ require_relative "../../device/topology/immediate_strategy"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module Hillstone
8
- class Topology < Operations::Topology::ImmediateStrategy
8
+ class Topology < Topology::ImmediateStrategy
9
9
  # 山石提供 LLDP 邻居、配置描述读取和描述变更。
10
10
  def self.supports?(capability)
11
11
  %i[neighbors interface_descriptions interface_description_changes].include?(capability)
@@ -18,10 +18,10 @@ module Net
18
18
  def description_template = "hillstone_interface_descriptions.textfsm"
19
19
 
20
20
  # 进入配置视图。
21
- def enter_configuration = "configure"
21
+ def enter_configuration_command = "configure"
22
22
 
23
23
  # 退出配置视图后先读回,保存动作随后单独执行。
24
- def leave_configuration = ["exit"]
24
+ def leave_configuration_commands = ["exit"]
25
25
 
26
26
  def persistence_pattern = /\ASaving configuration is finished\.?\z/i
27
27
 
@@ -43,7 +43,7 @@ module Net
43
43
 
44
44
  # 复用公共接口描述命令。
45
45
  def change_commands(change)
46
- InterfaceDescription.commands(interface: change.interface, description: change.new_description)
46
+ Net::Connector::Topology::InterfaceDescription.commands(interface: change.interface, description: change.new_description)
47
47
  end
48
48
 
49
49
  private
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/tftp/file_upload"
3
+ require_relative "../../device/tftp/file_upload"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module Huawei
8
- class TftpBackup < Operations::Tftp::FileUpload
8
+ class TftpBackup < Tftp::FileUpload
9
9
  # 缺少源文件也必须在首次设备 I/O 之前拒绝。
10
10
  def validate_options!(target, source_file:, vrf: nil)
11
11
  super
@@ -13,7 +13,7 @@ module Net
13
13
  end
14
14
 
15
15
  # 要求调用方提供合法的华为启动配置文件路径。
16
- def source_file(value)
16
+ def resolve_source_file(value)
17
17
  raise ArgumentError, "Huawei TFTP backup requires source_file" unless value
18
18
 
19
19
  TftpTarget.validate_source_file!(value)
@@ -23,7 +23,7 @@ module Net
23
23
  end
24
24
 
25
25
  # 采集前后都检查候选差异,拒绝把未提交配置误作运行配置。
26
- def check_response(command, response, _execution)
26
+ def validate_response!(command, response, _execution)
27
27
  return unless command.text == "show config diff"
28
28
  return if body(response.output, command.text).empty?
29
29
 
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/tftp/strategy"
3
+ require_relative "../../device/tftp/strategy"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module PaloAlto
8
- class TftpBackup < Operations::Tftp::Strategy
8
+ class TftpBackup < Tftp::Strategy
9
9
  FILENAME = "running-config.xml"
10
10
 
11
11
  # 返回 PAN-OS 固定使用的运行配置文件名。
@@ -33,7 +33,7 @@ module Net
33
33
  end
34
34
 
35
35
  # 检查设备回显是否确认 TFTP 传输完成。
36
- def complete?(result)
36
+ def device_reported_complete?(result)
37
37
  completion_lines(result).any? do |line|
38
38
  line.match?(/\ASent\s+[1-9]\d*\s+bytes(?:\s+in\s+[\d.]+\s+(?:secs?|seconds?))?[.!]?\z/i)
39
39
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/topology/strategy"
3
+ require_relative "../../device/topology/strategy"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module PaloAlto
8
- class Topology < Operations::Topology::Strategy
8
+ class Topology < Topology::Strategy
9
9
  # 候选配置缺少经固件实验确认的所有权/commit 协议,暂只提供读取能力。
10
10
  def self.supports?(capability)
11
11
  %i[neighbors interface_descriptions].include?(capability)
@@ -20,10 +20,7 @@ module Net
20
20
  def description_template = "palo_alto_interface_descriptions.textfsm"
21
21
 
22
22
  # 进入候选配置视图。
23
- def enter_configuration = "configure"
24
-
25
- # 提交候选配置后退出配置视图。
26
- def finish_commands = ["commit", "exit"]
23
+ def enter_configuration_command = "configure"
27
24
 
28
25
  # 只计入非空邻居块,跳过没有对端的本机接口。
29
26
  def expected_neighbor_count(output, _template)
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/tftp/strategy"
3
+ require_relative "../../device/tftp/strategy"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module Radware
8
- class TftpBackup < Operations::Tftp::Strategy
8
+ class TftpBackup < Tftp::Strategy
9
9
  # Radware 原生导出生成压缩配置归档。
10
10
  def self.file_extension = "tgz"
11
11
 
@@ -32,7 +32,7 @@ module Net
32
32
  end
33
33
 
34
34
  # 检查设备回显是否确认 TFTP 传输完成。
35
- def complete?(result)
35
+ def device_reported_complete?(result)
36
36
  completion_lines(result).any? do |line|
37
37
  line.match?(/\A
38
38
  (?:Current\s+)?(?:configuration|config)\s+
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../operations/topology/strategy"
3
+ require_relative "../../device/topology/strategy"
4
4
 
5
5
  module Net
6
6
  module Connector
7
7
  module Radware
8
- class Topology < Operations::Topology::Strategy
8
+ class Topology < Topology::Strategy
9
9
  # Alteon 只支持读取端口描述,不提供邻居发现或自动改写。
10
10
  def self.supports?(capability) = capability == :interface_descriptions
11
11
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Net
4
4
  module Connector
5
- VERSION = "0.4.2"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
data/lib/net/connector.rb CHANGED
@@ -16,18 +16,13 @@ module Net
16
16
  hillstone: "Hillstone"
17
17
  }.freeze
18
18
 
19
- ALIASES = {
20
- "cisco_n9k" => :cisco_nxos,
21
- "paloalto" => :palo_alto
22
- }.freeze
23
-
24
19
  # 返回当前支持的厂商标识集合。
25
20
  def self.vendors = VENDORS.keys
26
21
 
27
22
  # 根据厂商标识查找连接器类。
28
23
  def self.vendor_class(vendor)
29
24
  key = vendor.to_s.downcase.tr(" -", "__")
30
- key = ALIASES.fetch(key, key.to_sym)
25
+ key = key.to_sym
31
26
  name = VENDORS.fetch(key) { raise ArgumentError, "unsupported vendor: #{vendor.inspect}" }
32
27
  require_relative "connector/vendor/#{key}"
33
28
  const_get(name, false).const_get(:Connector, false)
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.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - net-connector contributors
@@ -411,28 +411,40 @@ files:
411
411
  - lib/net-connector.rb
412
412
  - lib/net/connector.rb
413
413
  - lib/net/connector/device/base.rb
414
- - lib/net/connector/device/interface_description.rb
415
- - lib/net/connector/device/interface_name.rb
414
+ - lib/net/connector/device/local_backup.rb
416
415
  - lib/net/connector/device/profile.rb
417
416
  - lib/net/connector/device/profile/builder.rb
418
417
  - lib/net/connector/device/running_config.rb
419
418
  - lib/net/connector/device/running_config/strategy.rb
420
- - lib/net/connector/engine.rb
419
+ - lib/net/connector/device/save_config.rb
420
+ - lib/net/connector/device/tftp.rb
421
+ - lib/net/connector/device/tftp/file_upload.rb
422
+ - lib/net/connector/device/tftp/receipt.rb
423
+ - lib/net/connector/device/tftp/strategy.rb
424
+ - lib/net/connector/device/tftp/target.rb
425
+ - lib/net/connector/device/topology.rb
426
+ - lib/net/connector/device/topology/immediate_strategy.rb
427
+ - lib/net/connector/device/topology/interface_description.rb
428
+ - lib/net/connector/device/topology/interface_name.rb
429
+ - lib/net/connector/device/topology/strategy.rb
421
430
  - lib/net/connector/engine/authentication.rb
422
- - lib/net/connector/engine/base.rb
423
431
  - lib/net/connector/engine/command.rb
424
432
  - lib/net/connector/engine/configuration.rb
425
433
  - lib/net/connector/engine/core.rb
426
434
  - lib/net/connector/engine/dialogue.rb
435
+ - lib/net/connector/engine/error_metadata.rb
427
436
  - lib/net/connector/engine/errors.rb
428
437
  - lib/net/connector/engine/execution.rb
429
438
  - lib/net/connector/engine/log.rb
430
- - lib/net/connector/engine/log_messages.rb
431
- - lib/net/connector/engine/profile.rb
439
+ - lib/net/connector/engine/log/event.rb
440
+ - lib/net/connector/engine/log/formatter.rb
441
+ - lib/net/connector/engine/log/messages.rb
442
+ - lib/net/connector/engine/log/stream.rb
432
443
  - lib/net/connector/engine/recovery.rb
433
444
  - lib/net/connector/engine/result.rb
434
445
  - lib/net/connector/engine/session.rb
435
446
  - lib/net/connector/engine/terminal_renderer.rb
447
+ - lib/net/connector/engine/terminal_text.rb
436
448
  - lib/net/connector/engine/transport.rb
437
449
  - lib/net/connector/netdisco.rb
438
450
  - lib/net/connector/netdisco/batch.rb
@@ -450,38 +462,11 @@ files:
450
462
  - lib/net/connector/netdisco/rules.rb
451
463
  - lib/net/connector/netdisco/settings.rb
452
464
  - lib/net/connector/netdisco/worker.rb
453
- - lib/net/connector/operations.rb
454
- - lib/net/connector/operations/backup_lock.rb
455
- - lib/net/connector/operations/local_backup.rb
456
- - lib/net/connector/operations/parse_output.rb
457
- - lib/net/connector/operations/private_file.rb
458
- - lib/net/connector/operations/running_config.rb
459
- - lib/net/connector/operations/running_config/cisco.rb
460
- - lib/net/connector/operations/running_config/hillstone.rb
461
- - lib/net/connector/operations/running_config/palo_alto.rb
462
- - lib/net/connector/operations/running_config/strategy.rb
463
- - lib/net/connector/operations/safe_file.rb
464
- - lib/net/connector/operations/saved_config.rb
465
- - lib/net/connector/operations/saved_config/legacy_index.rb
466
- - lib/net/connector/operations/tftp/cisco_ios.rb
467
- - lib/net/connector/operations/tftp/cisco_nxos.rb
468
- - lib/net/connector/operations/tftp/file_upload.rb
469
- - lib/net/connector/operations/tftp/h3c.rb
470
- - lib/net/connector/operations/tftp/hillstone.rb
471
- - lib/net/connector/operations/tftp/huawei.rb
472
- - lib/net/connector/operations/tftp/palo_alto.rb
473
- - lib/net/connector/operations/tftp/radware.rb
474
- - lib/net/connector/operations/tftp/strategy.rb
475
- - lib/net/connector/operations/tftp_backup.rb
476
- - lib/net/connector/operations/tftp_receipt.rb
477
- - lib/net/connector/operations/topology.rb
478
- - lib/net/connector/operations/topology/cisco.rb
479
- - lib/net/connector/operations/topology/h3c.rb
480
- - lib/net/connector/operations/topology/hillstone.rb
481
- - lib/net/connector/operations/topology/immediate_strategy.rb
482
- - lib/net/connector/operations/topology/palo_alto.rb
483
- - lib/net/connector/operations/topology/radware.rb
484
- - lib/net/connector/operations/topology/strategy.rb
465
+ - lib/net/connector/storage.rb
466
+ - lib/net/connector/storage/backup_lock.rb
467
+ - lib/net/connector/storage/private_file.rb
468
+ - lib/net/connector/storage/safe_file.rb
469
+ - lib/net/connector/storage/saved_config.rb
485
470
  - lib/net/connector/templates/cisco_cdp_neighbors_detail.textfsm
486
471
  - lib/net/connector/templates/cisco_ios_running_config_interfaces.textfsm
487
472
  - lib/net/connector/templates/cisco_ios_show_ip_interface_brief.textfsm
@@ -494,6 +479,7 @@ files:
494
479
  - lib/net/connector/templates/palo_alto_interface_descriptions.textfsm
495
480
  - lib/net/connector/templates/palo_alto_lldp_neighbors.textfsm
496
481
  - lib/net/connector/templates/radware_port_names.textfsm
482
+ - lib/net/connector/textfsm.rb
497
483
  - lib/net/connector/vendor/cisco_ios.rb
498
484
  - lib/net/connector/vendor/cisco_ios/running_config.rb
499
485
  - lib/net/connector/vendor/cisco_ios/tftp_backup.rb
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "interface_name"
4
-
5
- module Net
6
- module Connector
7
- # 描述文本与常见接口视图命令的纯构造方法,不连接或修改设备。
8
- module InterfaceDescription
9
- # 将邻居名称和接口拼成默认描述;只缩写邻居接口,保留原始大小写。
10
- def self.format(neighbor, abbreviate: true, lowercase: false)
11
- port = if abbreviate
12
- InterfaceName.short(neighbor.neighbor_interface, lowercase: lowercase)
13
- else
14
- lowercase ? neighbor.neighbor_interface.downcase : neighbor.neighbor_interface
15
- end
16
- validate!("To #{neighbor.neighbor_name} #{port}")
17
- end
18
-
19
- # 生成进入接口、设置描述和退出视图的命令,不执行设备操作。
20
- def self.commands(interface:, description:, leave: "exit")
21
- validate_interface!(interface)
22
- validate!(description)
23
- raise ArgumentError, "interface exit must be exit or quit" unless %w[exit quit].include?(leave)
24
-
25
- ["interface #{interface}", "description #{description}", leave]
26
- end
27
-
28
- # 共享的输入约束同样适用于 PAN-OS 的独立命令语法。
29
- def self.validate!(value)
30
- return value if value.is_a?(String) && value.bytesize.between?(1, 80) &&
31
- value.match?(/\A[\p{Alnum}_.: \/-]+\z/u)
32
-
33
- raise ArgumentError, "description must be 1-80 bytes of plain interface text"
34
- end
35
-
36
- # 拒绝可能把描述命令拆成多条命令的不安全接口名。
37
- def self.validate_interface!(value)
38
- return value if value.is_a?(String) && value.match?(/\A[A-Za-z][A-Za-z0-9.\/-]*\z/)
39
-
40
- raise ArgumentError, "neighbor interface name is unsafe"
41
- end
42
- end
43
- end
44
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Net
4
- module Connector
5
- # 接口身份匹配与描述展示共用名称规则;展示简称不能直接用作下发命令。
6
- module InterfaceName
7
- RULES = [
8
- [/\A(?:xge|ten-gigabitethernet|tengigabitethernet|te)(?=\d)/i, "tengigabitethernet", "Te"],
9
- [/\A(?:gigabitethernet|ge|gi)(?=\d)/i, "gigabitethernet", "Gi"],
10
- [/\A(?:fastethernet|fa)(?=\d)/i, "fastethernet", "Fa"],
11
- [/\A(?:ethernet|eth)(?=\d)/i, "ethernet", "Eth"],
12
- [/\A(?:port-channel|po)(?=\d)/i, "port-channel", "Po"]
13
- ].map(&:freeze).freeze
14
-
15
- # 用于本机邻居表与配置的关联;保留已有证据键的拼写契约。
16
- def self.key(name)
17
- rule = RULES.find { |pattern, _key, _short| pattern.match?(name) }
18
- rule ? name.sub(rule.first, rule[1]).downcase : name.downcase
19
- end
20
-
21
- # 仅缩短已识别的前缀,保留端口、子接口编号及未知命名。
22
- # 默认延续输入的大写、小写或首字母大写形式;统一小写由调用方选择。
23
- def self.short(name, lowercase: false)
24
- rule = RULES.find { |pattern, _key, _short| pattern.match?(name) }
25
- formatted = if rule
26
- name.sub(rule.first) do |prefix|
27
- abbreviation = rule.last
28
- if prefix == prefix.downcase
29
- abbreviation.downcase
30
- elsif prefix == prefix.upcase
31
- abbreviation.upcase
32
- elsif prefix.casecmp?(abbreviation)
33
- prefix
34
- else
35
- abbreviation
36
- end
37
- end
38
- else
39
- name.dup
40
- end
41
- lowercase ? formatted.downcase : formatted
42
- end
43
-
44
- # 保留现有配置视图展开规则;厂商策略可覆盖不适用的名称。
45
- def self.configuration(name)
46
- name.sub(/\AXGE(?=\d)/i, "Ten-GigabitEthernet")
47
- .sub(/\AGE(?=\d)/i, "GigabitEthernet")
48
- .sub(/\AGi(?=\d)/i, "GigabitEthernet")
49
- .sub(/\ATe(?=\d)/i, "TenGigabitEthernet")
50
- .sub(/\AFa(?=\d)/i, "FastEthernet")
51
- .sub(/\AEth(?=\d)/i, "Ethernet")
52
- .sub(/\APo(?=\d)/i, "port-channel")
53
- end
54
- end
55
- end
56
- end
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # 旧加载路径保留为转发入口;设备定义位于 device/。
4
- require_relative "../device/base"
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Net
4
- module Connector
5
- # 独立于日志目标的人类可读事件文案。
6
- module LogMessages
7
- module_function
8
-
9
- # 将结构化会话事件转换为便于人工阅读的中文日志。
10
- def format(name, fields)
11
- case name
12
- when "connect"
13
- "开始连接 #{fields[:host]}(#{fields[:protocol].to_s.upcase},账号 #{fields[:username]})"
14
- when "connect_failed"
15
- "连接失败:#{fields[:message]}(#{fields[:error]})"
16
- when "login_start"
17
- "等待设备登录提示"
18
- when "login_output"
19
- "登录过程回显(已脱敏):"
20
- when "login_complete"
21
- fields[:status] == "ok" ? "登录成功,设备提示符:#{fields[:prompt]}" :
22
- "登录失败:#{fields[:message]}(#{fields[:error]})"
23
- when "command_start"
24
- "下发命令:#{fields[:text]}"
25
- when "device_output"
26
- "设备回显:"
27
- when "command_complete"
28
- fields[:status] == "response_received" ? "命令回显结束,已收到设备提示符" :
29
- "命令执行失败:#{fields[:message]}(#{fields[:error]})"
30
- when "command_detail"
31
- "命令耗时 #{fields[:duration_ms]} 毫秒,收到 #{fields[:response_bytes]} 字节回显"
32
- when "tftp_backup"
33
- case fields[:status]
34
- when "reported_uploaded"
35
- "TFTP 备份:设备报告上传成功,目标 #{fields[:server]}/#{fields[:path]};服务器文件尚未核验"
36
- when "transfer_failed"
37
- "TFTP 备份失败:设备报告传输失败,服务器 #{fields[:server]}"
38
- else
39
- "TFTP 备份未确认:设备未报告上传成功,服务器 #{fields[:server]}"
40
- end
41
- else
42
- name.to_s
43
- end
44
- end
45
- end
46
- end
47
- end
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # 旧加载路径保留为转发入口;设备定义位于 device/。
4
- require_relative "../device/profile"
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # 保留原设备门面入口;只需要会话执行层时加载 engine/core。
4
- require_relative "device/base"
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "digest"
4
- require_relative "private_file"
5
- require_relative "backup_lock"
6
- require_relative "safe_file"
7
-
8
- module Net
9
- module Connector
10
- Backup = Data.define(:path, :bytes, :sha256, :collected_at, :change, :previous_sha256) do
11
- # 保存配置备份的路径、摘要和变更状态。
12
- def initialize(path:, bytes:, sha256:, collected_at:, change: nil, previous_sha256: nil)
13
- super
14
- end
15
-
16
- # 判断本次采集是否产生新的配置内容。
17
- def changed? = [:created, :changed].include?(change)
18
- end
19
-
20
- # 只携带库自身的提交回执和配置元数据,不携带正文或原始文件系统异常。
21
- class BackupPersistenceError < Error
22
- attr_reader :backup, :receipt, :underlying_type
23
-
24
- def initialize(backup:, write_error:, host: nil)
25
- unless backup.instance_of?(Backup) && Operations::PrivateFile.receipt_error?(write_error) &&
26
- write_error.receipt.committed? && write_error.receipt.path == backup.path
27
- raise ArgumentError, "backup persistence error requires a matching committed backup receipt"
28
- end
29
-
30
- @backup, @receipt = backup, write_error.receipt
31
- @underlying_type = write_error.underlying_type
32
- code = if write_error.instance_of?(Operations::PrivateFile::DirectorySyncUnsupported)
33
- :backup_durability_unsupported
34
- elsif receipt.durable?
35
- :backup_finalize_failed
36
- else
37
- :backup_persistence_unconfirmed
38
- end
39
- super(write_error.message, code: code, host: host, phase: :backup)
40
- end
41
- end
42
-
43
- module Operations
44
- class LocalBackup
45
- # 保存执行配置采集的设备对象。
46
- def initialize(device) = @device = device
47
-
48
- # 采集配置、识别变更并原子写入私有文件。
49
- def call(path:, lock_timeout: 0)
50
- host = @device.host if @device.respond_to?(:host)
51
- BackupLock.synchronize(path, timeout: lock_timeout, host: host) { |lock| collect_and_write(lock.path) }
52
- end
53
-
54
- private
55
-
56
- def collect_and_write(destination)
57
- contents = @device.running_config.value!
58
- digest = Digest::SHA256.hexdigest(contents)
59
- previous = SafeFile.fingerprint(destination, missing: true, replace_symlink: true)
60
- previous_digest = previous&.sha256
61
- change = if previous_digest.nil?
62
- :created
63
- elsif previous_digest == digest
64
- :unchanged
65
- else
66
- :changed
67
- end
68
-
69
- backup = Backup.new(path: destination, bytes: contents.bytesize,
70
- sha256: digest, collected_at: Time.now.utc, change: change,
71
- previous_sha256: previous_digest)
72
- if change != :unchanged || previous.mode != 0o600 || !SafeFile.same_entry?(destination, previous)
73
- write_backup(backup, contents)
74
- end
75
- backup
76
- end
77
-
78
- def write_backup(backup, contents)
79
- PrivateFile.write_receipt(backup.path, contents)
80
- rescue PrivateFile::WriteError => error
81
- raise unless PrivateFile.receipt_error?(error) && error.receipt.committed?
82
-
83
- host = @device.host if @device.respond_to?(:host)
84
- raise BackupPersistenceError.new(backup: backup, write_error: error, host: host), cause: nil
85
- end
86
- end
87
- end
88
- end
89
- end