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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -1
- data/README.md +42 -19
- data/docs/VERIFICATION.md +17 -7
- data/docs/architecture.md +96 -64
- data/lib/net/connector/device/base.rb +27 -107
- data/lib/net/connector/device/local_backup.rb +96 -0
- data/lib/net/connector/device/profile.rb +8 -3
- data/lib/net/connector/device/running_config/strategy.rb +1 -1
- data/lib/net/connector/device/running_config.rb +44 -5
- data/lib/net/connector/device/save_config.rb +21 -0
- data/lib/net/connector/device/tftp/file_upload.rb +53 -0
- data/lib/net/connector/{operations/tftp_receipt.rb → device/tftp/receipt.rb} +18 -20
- data/lib/net/connector/device/tftp/strategy.rb +67 -0
- data/lib/net/connector/device/tftp/target.rb +63 -0
- data/lib/net/connector/device/tftp.rb +117 -0
- data/lib/net/connector/device/topology/immediate_strategy.rb +40 -0
- data/lib/net/connector/device/topology/interface_description.rb +46 -0
- data/lib/net/connector/device/topology/interface_name.rb +58 -0
- data/lib/net/connector/device/topology/strategy.rb +75 -0
- data/lib/net/connector/device/topology.rb +277 -0
- data/lib/net/connector/engine/authentication.rb +1 -1
- data/lib/net/connector/engine/configuration.rb +1 -1
- data/lib/net/connector/engine/dialogue.rb +17 -17
- data/lib/net/connector/engine/error_metadata.rb +50 -0
- data/lib/net/connector/engine/errors.rb +15 -1
- data/lib/net/connector/engine/execution.rb +15 -8
- data/lib/net/connector/engine/log/event.rb +66 -0
- data/lib/net/connector/engine/log/formatter.rb +16 -0
- data/lib/net/connector/engine/log/messages.rb +52 -0
- data/lib/net/connector/engine/log/stream.rb +56 -0
- data/lib/net/connector/engine/log.rb +105 -84
- data/lib/net/connector/engine/session.rb +83 -42
- data/lib/net/connector/engine/terminal_text.rb +33 -0
- data/lib/net/connector/engine/transport.rb +2 -2
- data/lib/net/connector/netdisco/batch.rb +9 -45
- data/lib/net/connector/netdisco/cli.rb +7 -9
- data/lib/net/connector/netdisco/client.rb +2 -2
- data/lib/net/connector/netdisco/device.rb +4 -5
- data/lib/net/connector/netdisco/diagnostic.rb +9 -39
- data/lib/net/connector/netdisco/fleet.rb +26 -39
- data/lib/net/connector/netdisco/report.rb +37 -16
- data/lib/net/connector/netdisco/result_store.rb +2 -2
- data/lib/net/connector/netdisco/settings.rb +0 -6
- data/lib/net/connector/{operations → storage}/backup_lock.rb +2 -2
- data/lib/net/connector/{operations → storage}/private_file.rb +2 -7
- data/lib/net/connector/{operations → storage}/safe_file.rb +1 -1
- data/lib/net/connector/{operations → storage}/saved_config.rb +10 -35
- data/lib/net/connector/storage.rb +15 -0
- data/lib/net/connector/textfsm.rb +72 -0
- data/lib/net/connector/vendor/cisco_ios/tftp_backup.rb +3 -3
- data/lib/net/connector/vendor/cisco_ios/topology.rb +5 -5
- data/lib/net/connector/vendor/cisco_nxos/tftp_backup.rb +3 -3
- data/lib/net/connector/vendor/h3c/tftp_backup.rb +4 -4
- data/lib/net/connector/vendor/h3c/topology.rb +5 -5
- data/lib/net/connector/vendor/h3c.rb +3 -3
- data/lib/net/connector/vendor/hillstone/tftp_backup.rb +3 -3
- data/lib/net/connector/vendor/hillstone/topology.rb +5 -5
- data/lib/net/connector/vendor/huawei/tftp_backup.rb +3 -3
- data/lib/net/connector/vendor/palo_alto/running_config.rb +1 -1
- data/lib/net/connector/vendor/palo_alto/tftp_backup.rb +3 -3
- data/lib/net/connector/vendor/palo_alto/topology.rb +3 -6
- data/lib/net/connector/vendor/radware/tftp_backup.rb +3 -3
- data/lib/net/connector/vendor/radware/topology.rb +2 -2
- data/lib/net/connector/version.rb +1 -1
- data/lib/net/connector.rb +1 -6
- metadata +25 -39
- data/lib/net/connector/device/interface_description.rb +0 -44
- data/lib/net/connector/device/interface_name.rb +0 -56
- data/lib/net/connector/engine/base.rb +0 -4
- data/lib/net/connector/engine/log_messages.rb +0 -47
- data/lib/net/connector/engine/profile.rb +0 -4
- data/lib/net/connector/engine.rb +0 -4
- data/lib/net/connector/operations/local_backup.rb +0 -89
- data/lib/net/connector/operations/parse_output.rb +0 -77
- data/lib/net/connector/operations/running_config/cisco.rb +0 -14
- data/lib/net/connector/operations/running_config/hillstone.rb +0 -12
- data/lib/net/connector/operations/running_config/palo_alto.rb +0 -12
- data/lib/net/connector/operations/running_config/strategy.rb +0 -3
- data/lib/net/connector/operations/running_config.rb +0 -16
- data/lib/net/connector/operations/saved_config/legacy_index.rb +0 -109
- data/lib/net/connector/operations/tftp/cisco_ios.rb +0 -11
- data/lib/net/connector/operations/tftp/cisco_nxos.rb +0 -11
- data/lib/net/connector/operations/tftp/file_upload.rb +0 -55
- data/lib/net/connector/operations/tftp/h3c.rb +0 -11
- data/lib/net/connector/operations/tftp/hillstone.rb +0 -11
- data/lib/net/connector/operations/tftp/huawei.rb +0 -11
- data/lib/net/connector/operations/tftp/palo_alto.rb +0 -11
- data/lib/net/connector/operations/tftp/radware.rb +0 -11
- data/lib/net/connector/operations/tftp/strategy.rb +0 -75
- data/lib/net/connector/operations/tftp_backup.rb +0 -183
- data/lib/net/connector/operations/topology/cisco.rb +0 -11
- data/lib/net/connector/operations/topology/h3c.rb +0 -11
- data/lib/net/connector/operations/topology/hillstone.rb +0 -11
- data/lib/net/connector/operations/topology/immediate_strategy.rb +0 -45
- data/lib/net/connector/operations/topology/palo_alto.rb +0 -11
- data/lib/net/connector/operations/topology/radware.rb +0 -11
- data/lib/net/connector/operations/topology/strategy.rb +0 -87
- data/lib/net/connector/operations/topology.rb +0 -261
- data/lib/net/connector/operations.rb +0 -26
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../../
|
|
3
|
+
require_relative "../../device/topology/immediate_strategy"
|
|
4
4
|
|
|
5
5
|
module Net
|
|
6
6
|
module Connector
|
|
7
7
|
module H3c
|
|
8
|
-
class Topology <
|
|
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
|
|
22
|
+
def enter_configuration_command = "system-view"
|
|
23
23
|
|
|
24
24
|
# 返回用户视图读回;保存交给后续独立阶段。
|
|
25
|
-
def
|
|
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.
|
|
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.
|
|
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.
|
|
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 "../../
|
|
3
|
+
require_relative "../../device/tftp/strategy"
|
|
4
4
|
|
|
5
5
|
module Net
|
|
6
6
|
module Connector
|
|
7
7
|
module Hillstone
|
|
8
|
-
class TftpBackup <
|
|
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
|
|
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 "../../
|
|
3
|
+
require_relative "../../device/topology/immediate_strategy"
|
|
4
4
|
|
|
5
5
|
module Net
|
|
6
6
|
module Connector
|
|
7
7
|
module Hillstone
|
|
8
|
-
class Topology <
|
|
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
|
|
21
|
+
def enter_configuration_command = "configure"
|
|
22
22
|
|
|
23
23
|
# 退出配置视图后先读回,保存动作随后单独执行。
|
|
24
|
-
def
|
|
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 "../../
|
|
3
|
+
require_relative "../../device/tftp/file_upload"
|
|
4
4
|
|
|
5
5
|
module Net
|
|
6
6
|
module Connector
|
|
7
7
|
module Huawei
|
|
8
|
-
class TftpBackup <
|
|
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
|
|
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
|
|
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 "../../
|
|
3
|
+
require_relative "../../device/tftp/strategy"
|
|
4
4
|
|
|
5
5
|
module Net
|
|
6
6
|
module Connector
|
|
7
7
|
module PaloAlto
|
|
8
|
-
class TftpBackup <
|
|
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
|
|
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 "../../
|
|
3
|
+
require_relative "../../device/topology/strategy"
|
|
4
4
|
|
|
5
5
|
module Net
|
|
6
6
|
module Connector
|
|
7
7
|
module PaloAlto
|
|
8
|
-
class Topology <
|
|
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
|
|
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 "../../
|
|
3
|
+
require_relative "../../device/tftp/strategy"
|
|
4
4
|
|
|
5
5
|
module Net
|
|
6
6
|
module Connector
|
|
7
7
|
module Radware
|
|
8
|
-
class TftpBackup <
|
|
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
|
|
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 "../../
|
|
3
|
+
require_relative "../../device/topology/strategy"
|
|
4
4
|
|
|
5
5
|
module Net
|
|
6
6
|
module Connector
|
|
7
7
|
module Radware
|
|
8
|
-
class Topology <
|
|
8
|
+
class Topology < Topology::Strategy
|
|
9
9
|
# Alteon 只支持读取端口描述,不提供邻居发现或自动改写。
|
|
10
10
|
def self.supports?(capability) = capability == :interface_descriptions
|
|
11
11
|
|
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 =
|
|
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
|
+
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/
|
|
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/
|
|
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/
|
|
431
|
-
- lib/net/connector/engine/
|
|
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/
|
|
454
|
-
- lib/net/connector/
|
|
455
|
-
- lib/net/connector/
|
|
456
|
-
- lib/net/connector/
|
|
457
|
-
- lib/net/connector/
|
|
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,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
|
data/lib/net/connector/engine.rb
DELETED
|
@@ -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
|