netstack 0.1.1 → 0.2.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 +22 -3
- data/{LICENSE.txt → LICENSE} +7 -6
- data/NOTICE +25 -0
- data/README.md +58 -20
- data/docs/acceptance.md +92 -0
- data/docs/backups.md +100 -0
- data/docs/configuration.md +198 -0
- data/docs/connector.md +187 -0
- data/docs/ddi.md +142 -0
- data/docs/deployment.md +47 -0
- data/docs/foundations.md +45 -0
- data/docs/implementation.md +68 -0
- data/docs/inventory.md +42 -0
- data/docs/monitoring.md +227 -0
- data/docs/motor_integration.md +96 -0
- data/docs/performance.md +39 -0
- data/docs/release.md +20 -0
- data/docs/vxlan.md +91 -0
- data/docs/wireless.md +81 -0
- data/lib/netstack/backups/artifact.rb +23 -0
- data/lib/netstack/backups/diff.rb +15 -0
- data/lib/netstack/backups/repository.rb +77 -0
- data/lib/netstack/backups/request.rb +78 -0
- data/lib/netstack/circuits/business_calendar.rb +51 -0
- data/lib/netstack/circuits/capacity_review.rb +191 -0
- data/lib/netstack/circuits/endpoint.rb +25 -0
- data/lib/netstack/circuits/monitoring_plan.rb +212 -0
- data/lib/netstack/configuration/artifact.rb +85 -0
- data/lib/netstack/configuration/artifact_resolver.rb +30 -0
- data/lib/netstack/configuration/bootstrap/payload.rb +67 -0
- data/lib/netstack/configuration/bootstrap/validator.rb +224 -0
- data/lib/netstack/configuration/catalog.rb +60 -0
- data/lib/netstack/configuration/generator.rb +87 -0
- data/lib/netstack/configuration/input_contract.rb +82 -0
- data/lib/netstack/configuration/payload.rb +168 -0
- data/lib/netstack/configuration/reader.rb +124 -0
- data/lib/netstack/configuration/support.rb +30 -0
- data/lib/netstack/configuration/system_configuration.rb +56 -0
- data/lib/netstack/configuration/template_manager.rb +174 -0
- data/lib/netstack/configuration/template_store.rb +40 -0
- data/lib/netstack/configuration/validator.rb +566 -0
- data/lib/netstack/connector/authentication.rb +77 -0
- data/lib/netstack/connector/authentication_error.rb +11 -0
- data/lib/netstack/connector/backup.rb +19 -0
- data/lib/netstack/connector/backup_busy.rb +11 -0
- data/lib/netstack/connector/backup_persistence_error.rb +32 -0
- data/lib/netstack/connector/command.rb +63 -0
- data/lib/netstack/connector/command_result.rb +24 -0
- data/lib/netstack/connector/command_timeout.rb +11 -0
- data/lib/netstack/connector/configuration.rb +138 -0
- data/lib/netstack/connector/connection_closed.rb +11 -0
- data/lib/netstack/connector/connection_error.rb +11 -0
- data/lib/netstack/connector/device.rb +284 -0
- data/lib/netstack/connector/device_error.rb +11 -0
- data/lib/netstack/connector/dialogue.rb +55 -0
- data/lib/netstack/connector/error.rb +44 -0
- data/lib/netstack/connector/error_metadata.rb +58 -0
- data/lib/netstack/connector/event.rb +24 -0
- data/lib/netstack/connector/execution.rb +110 -0
- data/lib/netstack/connector/interaction.rb +55 -0
- data/lib/netstack/connector/internal_error.rb +11 -0
- data/lib/netstack/connector/known_hosts.rb +128 -0
- data/lib/netstack/connector/local_backup.rb +63 -0
- data/lib/netstack/connector/log/event.rb +65 -0
- data/lib/netstack/connector/log/formatter.rb +18 -0
- data/lib/netstack/connector/log/messages.rb +54 -0
- data/lib/netstack/connector/log/redacting_writer.rb +33 -0
- data/lib/netstack/connector/log/transcript.rb +35 -0
- data/lib/netstack/connector/log.rb +262 -0
- data/lib/netstack/connector/log_error.rb +11 -0
- data/lib/netstack/connector/login_timeout.rb +11 -0
- data/lib/netstack/connector/output_limit_exceeded.rb +11 -0
- data/lib/netstack/connector/parsing_error.rb +11 -0
- data/lib/netstack/connector/profile/builder.rb +238 -0
- data/lib/netstack/connector/profile.rb +225 -0
- data/lib/netstack/connector/prompt_error.rb +11 -0
- data/lib/netstack/connector/recovery.rb +59 -0
- data/lib/netstack/connector/redactor.rb +100 -0
- data/lib/netstack/connector/response.rb +25 -0
- data/lib/netstack/connector/response_reader.rb +140 -0
- data/lib/netstack/connector/result.rb +50 -0
- data/lib/netstack/connector/running_config/rendered.rb +16 -0
- data/lib/netstack/connector/running_config/strategy.rb +28 -0
- data/lib/netstack/connector/running_config.rb +113 -0
- data/lib/netstack/connector/save_config.rb +23 -0
- data/lib/netstack/connector/script.rb +59 -0
- data/lib/netstack/connector/script_error.rb +11 -0
- data/lib/netstack/connector/script_output_limit_exceeded.rb +11 -0
- data/lib/netstack/connector/session.rb +414 -0
- data/lib/netstack/connector/session_busy.rb +11 -0
- data/lib/netstack/connector/storage/backup_lock.rb +117 -0
- data/lib/netstack/connector/storage/private_file.rb +111 -0
- data/lib/netstack/connector/storage/safe_file.rb +64 -0
- data/lib/netstack/connector/storage/saved_config.rb +76 -0
- data/lib/netstack/connector/terminal_renderer.rb +186 -0
- data/lib/netstack/connector/terminal_text.rb +32 -0
- data/lib/netstack/connector/textfsm.rb +71 -0
- data/lib/netstack/connector/tftp/file_upload.rb +53 -0
- data/lib/netstack/connector/tftp/strategy.rb +67 -0
- data/lib/netstack/connector/tftp.rb +116 -0
- data/lib/netstack/connector/tftp_completion_error.rb +36 -0
- data/lib/netstack/connector/tftp_receipt.rb +54 -0
- data/lib/netstack/connector/tftp_target.rb +65 -0
- data/lib/netstack/connector/topology/checkpoint_error.rb +16 -0
- data/lib/netstack/connector/topology/deployment_adapter.rb +163 -0
- data/lib/netstack/connector/topology/execution_result.rb +34 -0
- data/lib/netstack/connector/topology/immediate_strategy.rb +39 -0
- data/lib/netstack/connector/topology/interface_description.rb +46 -0
- data/lib/netstack/connector/topology/interface_name.rb +60 -0
- data/lib/netstack/connector/topology/plan.rb +105 -0
- data/lib/netstack/connector/topology/strategy.rb +76 -0
- data/lib/netstack/connector/topology.rb +255 -0
- data/lib/netstack/connector/transport_error.rb +11 -0
- data/lib/netstack/connector/transports.rb +201 -0
- data/lib/netstack/connector/underlying_error.rb +25 -0
- data/lib/netstack/connector/unsupported_operation.rb +11 -0
- data/lib/netstack/connector/vendors/cisco_ios/running_config.rb +22 -0
- data/lib/netstack/connector/vendors/cisco_ios/tftp_backup.rb +41 -0
- data/lib/netstack/connector/vendors/cisco_ios/topology.rb +54 -0
- data/lib/netstack/connector/vendors/cisco_ios.rb +44 -0
- data/lib/netstack/connector/vendors/cisco_nxos/running_config.rb +25 -0
- data/lib/netstack/connector/vendors/cisco_nxos/tftp_backup.rb +43 -0
- data/lib/netstack/connector/vendors/cisco_nxos.rb +51 -0
- data/lib/netstack/connector/vendors/h3c/tftp_backup.rb +34 -0
- data/lib/netstack/connector/vendors/h3c/topology.rb +72 -0
- data/lib/netstack/connector/vendors/h3c.rb +75 -0
- data/lib/netstack/connector/vendors/h3c_wireless.rb +13 -0
- data/lib/netstack/connector/vendors/hillstone/running_config.rb +16 -0
- data/lib/netstack/connector/vendors/hillstone/tftp_backup.rb +55 -0
- data/lib/netstack/connector/vendors/hillstone/topology.rb +64 -0
- data/lib/netstack/connector/vendors/hillstone.rb +39 -0
- data/lib/netstack/connector/vendors/huawei/tftp_backup.rb +26 -0
- data/lib/netstack/connector/vendors/huawei.rb +62 -0
- data/lib/netstack/connector/vendors/palo_alto/running_config.rb +77 -0
- data/lib/netstack/connector/vendors/palo_alto/tftp_backup.rb +49 -0
- data/lib/netstack/connector/vendors/palo_alto/topology.rb +77 -0
- data/lib/netstack/connector/vendors/palo_alto.rb +39 -0
- data/lib/netstack/connector/vendors/radware/running_config.rb +21 -0
- data/lib/netstack/connector/vendors/radware/tftp_backup.rb +49 -0
- data/lib/netstack/connector/vendors/radware/topology.rb +19 -0
- data/lib/netstack/connector/vendors/radware.rb +59 -0
- data/lib/netstack/connector/write_timeout.rb +11 -0
- data/lib/netstack/connector.rb +32 -0
- data/lib/netstack/ddi/applier.rb +107 -0
- data/lib/netstack/ddi/deployment.rb +137 -0
- data/lib/netstack/ddi/inputs.rb +61 -0
- data/lib/netstack/ddi/manager.rb +197 -0
- data/lib/netstack/ddi/plan.rb +99 -0
- data/lib/netstack/ddi/scope.rb +127 -0
- data/lib/netstack/ddi/settings.rb +44 -0
- data/lib/netstack/ddi/state.rb +166 -0
- data/lib/netstack/deployment/checkpoint_error.rb +14 -0
- data/lib/netstack/deployment/cli_adapter.rb +64 -0
- data/lib/netstack/deployment/executor.rb +131 -0
- data/lib/netstack/deployment/plan.rb +82 -0
- data/lib/netstack/deployment/receipt.rb +78 -0
- data/lib/netstack/deployment/recovery.rb +19 -0
- data/lib/netstack/deployment/step.rb +16 -0
- data/lib/netstack/dimension.rb +30 -0
- data/lib/netstack/error.rb +14 -0
- data/lib/netstack/input_error.rb +9 -0
- data/lib/netstack/integrations/bluecat/client.rb +84 -0
- data/lib/netstack/integrations/http/client.rb +146 -0
- data/lib/netstack/integrations/http/error.rb +10 -0
- data/lib/netstack/integrations/http/response.rb +28 -0
- data/lib/netstack/integrations/http_delivery/adapter.rb +51 -0
- data/lib/netstack/integrations/http_delivery/client.rb +127 -0
- data/lib/netstack/integrations/http_delivery/result.rb +27 -0
- data/lib/netstack/integrations/oxidized/client.rb +89 -0
- data/lib/netstack/integrations/zabbix/client.rb +211 -0
- data/lib/netstack/integrations/zabbix/error.rb +10 -0
- data/lib/netstack/inventory/address.rb +30 -0
- data/lib/netstack/inventory/changes.rb +13 -0
- data/lib/netstack/inventory/cli_parser.rb +75 -0
- data/lib/netstack/inventory/collector.rb +99 -0
- data/lib/netstack/inventory/device.rb +14 -0
- data/lib/netstack/inventory/interface.rb +25 -0
- data/lib/netstack/inventory/neighbor.rb +15 -0
- data/lib/netstack/inventory/reconciler.rb +74 -0
- data/lib/netstack/inventory/snapshot.rb +69 -0
- data/lib/netstack/inventory/transceiver.rb +23 -0
- data/lib/netstack/ipv4.rb +204 -0
- data/lib/netstack/ipv6.rb +88 -0
- data/lib/netstack/monitoring/applier.rb +179 -0
- data/lib/netstack/monitoring/device_plan.rb +23 -0
- data/lib/netstack/monitoring/host_plan.rb +152 -0
- data/lib/netstack/monitoring/inputs.rb +73 -0
- data/lib/netstack/monitoring/measurements.rb +89 -0
- data/lib/netstack/monitoring/mutation.rb +37 -0
- data/lib/netstack/monitoring/plan.rb +69 -0
- data/lib/netstack/monitoring/probe_plan.rb +19 -0
- data/lib/netstack/monitoring/state_comparison.rb +37 -0
- data/lib/netstack/range_set.rb +181 -0
- data/lib/netstack/routing/lookup.rb +46 -0
- data/lib/netstack/routing/lookup_result.rb +14 -0
- data/lib/netstack/routing/route.rb +32 -0
- data/lib/netstack/routing/snapshot.rb +23 -0
- data/lib/netstack/target.rb +15 -0
- data/lib/netstack/unsupported_capability.rb +9 -0
- data/lib/netstack/values.rb +70 -0
- data/lib/netstack/version.rb +1 -1
- data/lib/netstack/vxlan/auditor.rb +187 -0
- data/lib/netstack/vxlan/cisco_nxos/config_parser.rb +327 -0
- data/lib/netstack/vxlan/cisco_nxos/deployment_adapter.rb +63 -0
- data/lib/netstack/vxlan/cisco_nxos/runtime_parser.rb +182 -0
- data/lib/netstack/vxlan/cisco_nxos/segment_renderer.rb +42 -0
- data/lib/netstack/vxlan/collector.rb +77 -0
- data/lib/netstack/vxlan/fabric.rb +29 -0
- data/lib/netstack/vxlan/finding.rb +20 -0
- data/lib/netstack/vxlan/node.rb +51 -0
- data/lib/netstack/vxlan/route_policy.rb +57 -0
- data/lib/netstack/vxlan/runtime_result.rb +24 -0
- data/lib/netstack/vxlan/segment.rb +35 -0
- data/lib/netstack/vxlan/segment_plan.rb +25 -0
- data/lib/netstack/vxlan/segment_planner.rb +156 -0
- data/lib/netstack/vxlan/snapshot.rb +34 -0
- data/lib/netstack/vxlan/validation.rb +58 -0
- data/lib/netstack/vxlan/verifier.rb +52 -0
- data/lib/netstack/vxlan/vpc_domain.rb +20 -0
- data/lib/netstack/wireless/access_point.rb +12 -0
- data/lib/netstack/wireless/ap_group.rb +12 -0
- data/lib/netstack/wireless/assessment.rb +13 -0
- data/lib/netstack/wireless/assessor.rb +83 -0
- data/lib/netstack/wireless/authentication_connection.rb +12 -0
- data/lib/netstack/wireless/bss.rb +12 -0
- data/lib/netstack/wireless/client.rb +12 -0
- data/lib/netstack/wireless/collector.rb +191 -0
- data/lib/netstack/wireless/configuration.rb +13 -0
- data/lib/netstack/wireless/finding.rb +12 -0
- data/lib/netstack/wireless/h3c/config_parser.rb +229 -0
- data/lib/netstack/wireless/h3c/lldp_parser.rb +69 -0
- data/lib/netstack/wireless/h3c/runtime_parser.rb +240 -0
- data/lib/netstack/wireless/huawei/runtime_parser.rb +83 -0
- data/lib/netstack/wireless/invalid_snapshot.rb +11 -0
- data/lib/netstack/wireless/neighbor.rb +12 -0
- data/lib/netstack/wireless/normalizer.rb +90 -0
- data/lib/netstack/wireless/parse_result.rb +13 -0
- data/lib/netstack/wireless/radio.rb +12 -0
- data/lib/netstack/wireless/reconciler.rb +115 -0
- data/lib/netstack/wireless/reconciliation.rb +11 -0
- data/lib/netstack/wireless/security_policy.rb +12 -0
- data/lib/netstack/wireless/service_template.rb +12 -0
- data/lib/netstack/wireless/service_template_bindings.rb +56 -0
- data/lib/netstack/wireless/snapshot.rb +150 -0
- data/lib/netstack/wireless/state.rb +23 -0
- data/lib/netstack/wireless/view.rb +154 -0
- data/lib/netstack.rb +10 -3
- data/resources/configuration/cisco-bootstrap.erb +118 -0
- data/resources/configuration/cisco-distributed-gateway.erb +40 -0
- data/resources/configuration/cisco-n9k-vpc.erb +62 -0
- data/resources/configuration/cisco-server-port.erb +43 -0
- data/resources/configuration/h3c-bootstrap.erb +108 -0
- data/resources/configuration/h3c-irf.erb +57 -0
- data/resources/configuration/h3c-server-port.erb +36 -0
- data/resources/configuration/hillstone-ipsec-vpn.erb +68 -0
- data/resources/configuration/hillstone-policy.erb +31 -0
- data/resources/configuration/paloalto-ipsec-vpn.erb +39 -0
- data/resources/configuration/radware-load-balancer.erb +26 -0
- data/resources/textfsm/cisco_cdp_neighbors_detail.textfsm +7 -0
- data/resources/textfsm/cisco_ios_running_config_interfaces.textfsm +12 -0
- data/resources/textfsm/cisco_ios_show_ip_interface_brief.textfsm +7 -0
- data/resources/textfsm/h3c_interface_descriptions.textfsm +11 -0
- data/resources/textfsm/h3c_lldp_local_first.textfsm +8 -0
- data/resources/textfsm/h3c_lldp_name_first.textfsm +8 -0
- data/resources/textfsm/hillstone_interface_descriptions.textfsm +11 -0
- data/resources/textfsm/hillstone_lldp_neighbors.textfsm +7 -0
- data/resources/textfsm/index +8 -0
- data/resources/textfsm/palo_alto_interface_descriptions.textfsm +6 -0
- data/resources/textfsm/palo_alto_lldp_neighbors.textfsm +11 -0
- data/resources/textfsm/radware_port_names.textfsm +13 -0
- metadata +588 -27
- data/.rubocop.yml +0 -13
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -10
- data/Gemfile.lock +0 -40
- data/Rakefile +0 -8
- data/bin/console +0 -15
- data/bin/setup +0 -8
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class H3c < Netstack::Connector::Device
|
|
9
|
+
vendor :h3c
|
|
10
|
+
# 声明 H3C 的命令、提示、诊断和保存确认规则。
|
|
11
|
+
profile do
|
|
12
|
+
running_config_strategy Netstack::Connector::RunningConfig::Rendered
|
|
13
|
+
tftp_strategy Netstack::Connector::Vendors::H3c::TftpBackup
|
|
14
|
+
topology_strategy Netstack::Connector::Vendors::H3c::Topology
|
|
15
|
+
commands do
|
|
16
|
+
running_config "dis cur"
|
|
17
|
+
save_config "save force"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
prompts do
|
|
21
|
+
login(/(?:\A|(?<=[\r\n]))(?:[^\S\n]|\x00)*\S+[>\]](?:[^\S\n]|\x00)*\z/)
|
|
22
|
+
command(/(?:\A|(?<=[\r\n]))(?:[^\S\n]|\x00)*[^\r\n]+[>\]](?:[^\S\n]|\x00)*\z/)
|
|
23
|
+
username(/(?:login|Username):\s*\z/i)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
errors do
|
|
27
|
+
command(
|
|
28
|
+
/
|
|
29
|
+
^[ \t]*%[ \t]+(?:(?:Unrecognized|Ambiguous|Incomplete)\s+command|Too\s+many\s+parameters)
|
|
30
|
+
\s+found\s+at\s+'\^'\s+position\.
|
|
31
|
+
/ix,
|
|
32
|
+
/^[ \t]*error:/i,
|
|
33
|
+
/^[ \t]*Permission\s+denied\./i,
|
|
34
|
+
/^\s*\^/
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# 核心交换机配置采集实测超过 10 秒,保留有界的整条命令预算。
|
|
39
|
+
command_timeout 60
|
|
40
|
+
|
|
41
|
+
interactions do
|
|
42
|
+
confirm %r{Are\s+you\s+sure\?\s*\[Y/N\]}i, response: "y\n"
|
|
43
|
+
confirm(/press\s+the\s+enter\s+key\)/i, response: "\n")
|
|
44
|
+
confirm %r{Continue\?\s*\[Y/N\]}i, response: "y\n"
|
|
45
|
+
confirm(/Please\s+input\s+the\s+file\s+name\(\*\.cfg\)\[[^\]]+\]\s*\z/i, response: "\n")
|
|
46
|
+
confirm %r{overwrite\?\s*\[Y/N\]}i, response: "y\n"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
protected
|
|
51
|
+
|
|
52
|
+
# 发送规则追加命令前,用本批次已获取的真实规则编号替换 XXX 占位符。
|
|
53
|
+
def prepare_command(command, execution)
|
|
54
|
+
return command unless /\A[ \t]*rule[ \t]+XXX[ \t]+append\b/i.match?(command.text)
|
|
55
|
+
|
|
56
|
+
rule_id = execution.context[:rule_id]
|
|
57
|
+
raise execution.build_error("new rule ID has not been acquired") unless rule_id
|
|
58
|
+
|
|
59
|
+
command.with_text(command.text.sub(/XXX/i, rule_id))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# 规则创建成功后查询当前配置,提取设备分配的编号并保存到本批次变量。
|
|
63
|
+
def after_command(command, _response, execution)
|
|
64
|
+
return unless /\A[ \t]*rule[ \t]+pass\b/i.match?(command.text)
|
|
65
|
+
|
|
66
|
+
response = execution.execute_command("dis this")
|
|
67
|
+
rule_id = response.raw[/rule\s+(\d+)\s+pass[^\n]+\n\s*#/i, 1]
|
|
68
|
+
raise execution.build_error("device did not return the new rule ID") unless rule_id
|
|
69
|
+
|
|
70
|
+
execution.context[:rule_id] = rule_id
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class Hillstone
|
|
9
|
+
class RunningConfig < Netstack::Connector::RunningConfig::Strategy
|
|
10
|
+
# 去掉山石 CLI 用退格控制符绘制的分页痕迹。
|
|
11
|
+
def clean(text) = text.gsub(/\x00?\x08+[ \t]+\x08+/, "")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class Hillstone
|
|
9
|
+
class TftpBackup < Tftp::Strategy
|
|
10
|
+
# 山石原生导出的配置文件使用 dat 扩展名。
|
|
11
|
+
def self.file_extension = "dat"
|
|
12
|
+
|
|
13
|
+
# 在连接前拒绝源文件及远端目录;未指定文件名时仍由设备生成。
|
|
14
|
+
def validate_options!(target, source_file:, **)
|
|
15
|
+
raise ArgumentError, "Hillstone TFTP export does not use source_file" unless source_file.nil?
|
|
16
|
+
return unless target.explicit_path? && target.path.include?("/")
|
|
17
|
+
|
|
18
|
+
raise ArgumentError, "Hillstone TFTP filename cannot contain a directory"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def receipt_metadata(target, **)
|
|
22
|
+
{ configuration_kind: :startup, format: :dat, requested_path: target.explicit_path? ? target.path : nil }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# 构造当前厂商的 TFTP 导出交互脚本。
|
|
26
|
+
def script(target, source_file:, vrf: nil)
|
|
27
|
+
validate_options!(target, source_file: source_file, vrf: vrf)
|
|
28
|
+
|
|
29
|
+
command = "export configuration startup to tftp server #{target.host} vrouter #{vrf || "mgt-vr"}"
|
|
30
|
+
command += " #{target.path}" if target.explicit_path?
|
|
31
|
+
Script.new([Command.new(command, timeout: 180)])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# 检查设备回显是否确认 TFTP 传输完成。
|
|
35
|
+
def device_reported_complete?(result)
|
|
36
|
+
!completion_line(result).nil?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# 从山石成功回显提取实际目标文件名。
|
|
40
|
+
def remote_path(_target, result)
|
|
41
|
+
filename = completion_line(result)&.split&.last
|
|
42
|
+
TftpTarget.validate_path!(filename)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# 仅认可设备明确给出目标文件名的 Export ok 回显。
|
|
48
|
+
def completion_line(result)
|
|
49
|
+
completion_lines(result).find { |line| line.match?(/\AExport\s+ok\s*,\s*target\s+file\s+name\s+\S+\z/i) }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class Hillstone
|
|
9
|
+
class Topology < Netstack::Connector::Topology::ImmediateStrategy
|
|
10
|
+
# 山石提供 LLDP 邻居、配置描述读取和描述变更。
|
|
11
|
+
def self.supports?(capability)
|
|
12
|
+
%i[neighbors interface_descriptions interface_description_changes].include?(capability)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# 读取 LLDP 邻居信息。
|
|
16
|
+
def neighbor_command = "show lldp neighbor-information"
|
|
17
|
+
|
|
18
|
+
# 从运行配置解析接口描述。
|
|
19
|
+
def description_template = "hillstone_interface_descriptions.textfsm"
|
|
20
|
+
|
|
21
|
+
# 进入配置视图。
|
|
22
|
+
def enter_configuration_command = "configure"
|
|
23
|
+
|
|
24
|
+
# 退出配置视图后先读回,保存动作随后单独执行。
|
|
25
|
+
def leave_configuration_commands = ["exit"]
|
|
26
|
+
|
|
27
|
+
def persistence_pattern = /\ASaving configuration is finished\.?\z/i
|
|
28
|
+
|
|
29
|
+
# 用数据行和设备报告的总数交叉核对,避免遗漏邻居。
|
|
30
|
+
def expected_neighbor_count(output, _template)
|
|
31
|
+
reported = output[/Total lldp neighbor number:\s*(\d+)/i, 1]
|
|
32
|
+
lines = neighbor_lines(output)
|
|
33
|
+
return unless lines.all? { |line| line.match?(/\A\S+\s+ethernet\d+\/\d+\s/i) }
|
|
34
|
+
return if reported && reported.to_i != lines.size
|
|
35
|
+
|
|
36
|
+
lines.size
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# 仅在列表为空且有明确零邻居证据时认可空表。
|
|
40
|
+
def empty_neighbor_output?(output)
|
|
41
|
+
neighbor_lines(output).empty? && (output.match?(/Total lldp neighbor number:\s*0\b/i) ||
|
|
42
|
+
output.match?(/System Name\s+Local Interface/i))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# 复用公共接口描述命令。
|
|
46
|
+
def change_commands(change)
|
|
47
|
+
Netstack::Connector::Topology::InterfaceDescription.commands(interface: change.interface, description: change.new_description)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# 去掉命令、表头、总数和提示符,只保留邻居数据行。
|
|
53
|
+
def neighbor_lines(output)
|
|
54
|
+
output.lines.map(&:strip).reject do |line|
|
|
55
|
+
line.empty? || line == neighbor_command || line.match?(/\ASystem Name\s+Local Interface\b/i) ||
|
|
56
|
+
line.match?(/\ATotal lldp neighbor number:\s*\d+\.?\z/i) ||
|
|
57
|
+
line.match?(/\A[-=+ ]+\z/) || line.match?(/\A\S+(?:#|\([MBF]\))\z/)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class Hillstone < Netstack::Connector::Device
|
|
9
|
+
vendor :hillstone
|
|
10
|
+
profile do
|
|
11
|
+
running_config_strategy Netstack::Connector::Vendors::Hillstone::RunningConfig
|
|
12
|
+
tftp_strategy Netstack::Connector::Vendors::Hillstone::TftpBackup
|
|
13
|
+
topology_strategy Netstack::Connector::Vendors::Hillstone::Topology
|
|
14
|
+
commands do
|
|
15
|
+
running_config "terminal length 0", "show configuration running"
|
|
16
|
+
save_config "save all"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
prompts do
|
|
20
|
+
login(/(?:\A|(?<=[\r\n]))\S+(?:[#>$]|\([MBF]\))\s*\z/)
|
|
21
|
+
command(/(?:\A|(?<=[\r\n]))\S+(?:#|\([MBF]\))\s*\z/)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
pager do
|
|
25
|
+
pattern(/(?:\A|(?<=[\r\n]))[ \t]*--More--[ \t\x00\x08]*(?=\r|\n|\z)/i)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
errors do
|
|
29
|
+
command(/^[ \t]*\^-+/i, /^\s*\^/,
|
|
30
|
+
/^[ \t]*(?:Unknown|Invalid|Error)\s+command/i,
|
|
31
|
+
/^[ \t]*Error:\s*[^\r\n]+/i)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
command_timeout 60
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class Huawei
|
|
9
|
+
class TftpBackup < Tftp::FileUpload
|
|
10
|
+
# 缺少源文件也必须在首次设备 I/O 之前拒绝。
|
|
11
|
+
def validate_options!(target, source_file:, vrf: nil)
|
|
12
|
+
super
|
|
13
|
+
raise ArgumentError, "Huawei TFTP backup requires source_file" if source_file.nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# 要求调用方提供合法的华为启动配置文件路径。
|
|
17
|
+
def resolve_source_file(value)
|
|
18
|
+
raise ArgumentError, "Huawei TFTP backup requires source_file" unless value
|
|
19
|
+
|
|
20
|
+
TftpTarget.validate_source_file!(value)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class Huawei < Netstack::Connector::Device
|
|
9
|
+
vendor :huawei
|
|
10
|
+
# 声明 Huawei 的命令、提示、诊断、提权和保存确认规则。
|
|
11
|
+
profile do
|
|
12
|
+
running_config_strategy Netstack::Connector::RunningConfig::Rendered
|
|
13
|
+
tftp_strategy Netstack::Connector::Vendors::Huawei::TftpBackup
|
|
14
|
+
commands do
|
|
15
|
+
running_config "dis cur"
|
|
16
|
+
save_config "save force"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
prompts do
|
|
20
|
+
login(/(?:\A|(?<=[\r\n]))\s*\S+[>\]]\s*\z/)
|
|
21
|
+
command(/(?:\A|(?<=[\r\n]))[^\r\n]+[>\]]\s*\z/)
|
|
22
|
+
username(/(?:login|Username):\s*\z/i)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
errors do
|
|
26
|
+
command(
|
|
27
|
+
/^\s*\^/,
|
|
28
|
+
/^[ \t]*error:/i,
|
|
29
|
+
/^[ \t]*Permission\s+denied\./i,
|
|
30
|
+
/
|
|
31
|
+
^[ \t]*%[ \t]+(?:(?:Unrecognized|Ambiguous|Incomplete)\s+command|Too\s+many\s+parameters)
|
|
32
|
+
\s+found\s+at\s+'\^'\s+position\.
|
|
33
|
+
/ix
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
privilege do
|
|
38
|
+
command "su"
|
|
39
|
+
prompt(/privilege\s+(?:level\s+is|is\s+).+>\s*\z/im)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
command_timeout 10
|
|
43
|
+
|
|
44
|
+
interactions do
|
|
45
|
+
confirm %r{Are\s+you\s+sure\?\s*\[Y/N\]}i, response: "y\n"
|
|
46
|
+
confirm(/press\s+the\s+enter\s+key\)/i, response: "\n")
|
|
47
|
+
confirm %r{Continue\?\s*\[Y/N\]}i, response: "y\n"
|
|
48
|
+
confirm(/Please\s+input\s+the\s+file\s+name\(\*\.cfg\)\[[^\]]+\]\s*\z/i, response: "\n")
|
|
49
|
+
confirm %r{overwrite\?\s*\[Y/N\]}i, response: "y\n"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
# 登录后若提示符以 ] 结尾,则确认设备已经处于特权视图。
|
|
56
|
+
def after_login(session, response)
|
|
57
|
+
session.mark_privileged! if response.prompt.include?("]")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
require "shellwords"
|
|
6
|
+
|
|
7
|
+
module Netstack
|
|
8
|
+
module Connector
|
|
9
|
+
module Vendors
|
|
10
|
+
class PaloAlto
|
|
11
|
+
class RunningConfig < Netstack::Connector::RunningConfig::Strategy
|
|
12
|
+
# 配置位于 show 步骤,最后的 exit 只负责离开候选视图。
|
|
13
|
+
def result_step(result)
|
|
14
|
+
result.steps.find { |step| step.command.text == "show" }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# 采集切换视图时只改变结束符,仍绑定已认证的设备身份。
|
|
18
|
+
def prompt_text(command)
|
|
19
|
+
prompt = super
|
|
20
|
+
case command.text
|
|
21
|
+
when "configure" then prompt.sub(/[>#]\z/, "#")
|
|
22
|
+
when "exit" then prompt.sub(/[>#]\z/, ">")
|
|
23
|
+
else prompt
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# 采集前后都检查候选差异,拒绝把未提交配置误作运行配置。
|
|
28
|
+
def validate_response!(command, response, _execution)
|
|
29
|
+
return unless command.text == "show config diff"
|
|
30
|
+
return if body(response.output, command.text).empty?
|
|
31
|
+
|
|
32
|
+
raise DeviceError.new("PAN-OS candidate differs from running configuration",
|
|
33
|
+
code: :uncommitted_configuration, host: @device.host, phase: :collect)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# 只接受完整的 set 命令文本,排除 XML 或截断的引号配置。
|
|
37
|
+
def clean(text)
|
|
38
|
+
normalized = body(text, "show").sub(/(?:\A|\n)[ \t]*\[edit\][ \t]*\z/, "").strip
|
|
39
|
+
unless set_commands?(normalized)
|
|
40
|
+
raise DeviceError.new("PAN-OS configuration must be set format",
|
|
41
|
+
code: :unsupported_configuration_format, host: @device.host, phase: :collect)
|
|
42
|
+
end
|
|
43
|
+
normalized
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# 证书等引号内换行必须闭合,不能将截断文本当成配置。
|
|
49
|
+
def set_commands?(text)
|
|
50
|
+
return false if text.empty?
|
|
51
|
+
|
|
52
|
+
pending = +""
|
|
53
|
+
text.each_line do |line|
|
|
54
|
+
next if pending.empty? && line.strip.empty?
|
|
55
|
+
return false if pending.empty? && !line.start_with?("set ")
|
|
56
|
+
|
|
57
|
+
pending << line
|
|
58
|
+
begin
|
|
59
|
+
Shellwords.split(pending)
|
|
60
|
+
pending.clear
|
|
61
|
+
rescue ArgumentError
|
|
62
|
+
# 未闭合引号继续读取;末尾仍未闭合则拒绝整个采集结果。
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
pending.empty?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# 去掉命令回显和提示符,保留真正需要校验的设备响应。
|
|
69
|
+
def body(text, command)
|
|
70
|
+
text.gsub("\r\n", "\n").sub(/\A[ \t]*#{Regexp.escape(command)}[ \t]*\n/, "")
|
|
71
|
+
.sub(@device.profile.command_prompt, "").strip
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class PaloAlto
|
|
9
|
+
class TftpBackup < Tftp::Strategy
|
|
10
|
+
FILENAME = "running-config.xml"
|
|
11
|
+
|
|
12
|
+
# 返回 PAN-OS 固定使用的运行配置文件名。
|
|
13
|
+
def self.filename(_host, **) = FILENAME
|
|
14
|
+
|
|
15
|
+
# 默认文件名尚未生成时只校验显式目标;固定名冲突仍由批量计划拒绝。
|
|
16
|
+
def validate_options!(target, source_file:, vrf: nil)
|
|
17
|
+
raise ArgumentError, "PAN-OS does not use source_file" unless source_file.nil?
|
|
18
|
+
raise ArgumentError, "PAN-OS TFTP backup does not use vrf" unless vrf.nil?
|
|
19
|
+
return unless target.explicit_path? && target.path != FILENAME
|
|
20
|
+
|
|
21
|
+
raise ArgumentError, "PAN-OS TFTP export uses the fixed filename #{FILENAME}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def receipt_metadata(_target, **)
|
|
25
|
+
{ configuration_kind: :running, format: :xml, source_file: FILENAME }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# 构造当前厂商的 TFTP 导出交互脚本。
|
|
29
|
+
def script(target, source_file:, vrf: nil)
|
|
30
|
+
validate_options!(target, source_file: source_file, vrf: vrf)
|
|
31
|
+
|
|
32
|
+
Script.new([Command.new("tftp export configuration to #{target.host} from #{FILENAME}",
|
|
33
|
+
timeout: 180)])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# 检查设备回显是否确认 TFTP 传输完成。
|
|
37
|
+
def device_reported_complete?(result)
|
|
38
|
+
completion_lines(result).any? do |line|
|
|
39
|
+
line.match?(/\ASent\s+[1-9]\d*\s+bytes(?:\s+in\s+[\d.]+\s+(?:secs?|seconds?))?[.!]?\z/i)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# 返回 PAN-OS 实际上传的固定文件名。
|
|
44
|
+
def remote_path(_target, _result) = FILENAME
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class PaloAlto
|
|
9
|
+
class Topology < Netstack::Connector::Topology::Strategy
|
|
10
|
+
# 候选配置缺少经固件实验确认的所有权/commit 协议,暂只提供读取能力。
|
|
11
|
+
def self.supports?(capability)
|
|
12
|
+
%i[neighbors interface_descriptions].include?(capability)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def change_error_code = :candidate_isolation_unavailable
|
|
16
|
+
|
|
17
|
+
# 读取各本机接口的 LLDP 邻居详情。
|
|
18
|
+
def neighbor_command = "show lldp neighbors all"
|
|
19
|
+
|
|
20
|
+
# 从 set 格式配置解析接口备注。
|
|
21
|
+
def description_template = "palo_alto_interface_descriptions.textfsm"
|
|
22
|
+
|
|
23
|
+
# 进入候选配置视图。
|
|
24
|
+
def enter_configuration_command = "configure"
|
|
25
|
+
|
|
26
|
+
# 只计入非空邻居块,跳过没有对端的本机接口。
|
|
27
|
+
def expected_neighbor_count(output, _template)
|
|
28
|
+
neighbor_blocks(output).count { |block| !empty_neighbor_block?(block) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# 输出必须明确说明无邻居,或每个本机接口块都明确为空。
|
|
32
|
+
def empty_neighbor_output?(output)
|
|
33
|
+
return true if output.match?(/No LLDP neighbors/i)
|
|
34
|
+
|
|
35
|
+
blocks = neighbor_blocks(output)
|
|
36
|
+
!blocks.empty? && blocks.all? { |block| empty_neighbor_block?(block) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
# PAN-OS 会列出启用 LLDP、但没有邻居的本机接口;只有明确为空的
|
|
42
|
+
# Neighbor information 块可跳过,缺字段的非空块继续交给数量校验拒绝。
|
|
43
|
+
def neighbor_blocks(output)
|
|
44
|
+
output.split(/^\s*Local information:\s*$/i).select { |block| block.match?(/^\s*Local interface:\s*\S+/i) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# 邻居字段区只有空行和提示符时,才视为明确的空块。
|
|
48
|
+
def empty_neighbor_block?(block)
|
|
49
|
+
tail = block.split(/^\s*Neighbor information:\s*$/i, 2)
|
|
50
|
+
tail.size == 2 && tail.last.lines.all? { |line| line.strip.empty? || line.match?(/^\S+[>#]\s*$/) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
public
|
|
54
|
+
|
|
55
|
+
# 模板按行读取;未闭合的引号表示证据不完整,不能把首行当成旧描述。
|
|
56
|
+
def decode_description(value)
|
|
57
|
+
value = value.strip
|
|
58
|
+
value.start_with?('"') ? Shellwords.split(value).join(" ") : value
|
|
59
|
+
rescue ArgumentError
|
|
60
|
+
raise ParsingError.new("PAN-OS interface comment is not a complete single-line value",
|
|
61
|
+
code: :unrecognized_output, host: @device.host, phase: :parse)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# 用 PAN-OS set 语法生成接口备注变更。
|
|
65
|
+
def change_commands(change)
|
|
66
|
+
[%Q(set network interface ethernet #{change.interface} comment "#{change.new_description}")]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# commit 可能耗时较长,单独放宽该命令的超时。
|
|
70
|
+
def script_command(command)
|
|
71
|
+
command == "commit" ? Command.new(command, timeout: 300) : command
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class PaloAlto < Netstack::Connector::Device
|
|
9
|
+
vendor :palo_alto
|
|
10
|
+
# 声明 PAN-OS 的命令、提示、超时、诊断和登录确认规则。
|
|
11
|
+
profile do
|
|
12
|
+
running_config_strategy Netstack::Connector::Vendors::PaloAlto::RunningConfig
|
|
13
|
+
tftp_strategy Netstack::Connector::Vendors::PaloAlto::TftpBackup
|
|
14
|
+
topology_strategy Netstack::Connector::Vendors::PaloAlto::Topology
|
|
15
|
+
commands do
|
|
16
|
+
running_config "set cli pager off", "set cli config-output-format set", "show config diff",
|
|
17
|
+
"configure", "show", "exit", "show config diff"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
prompts do
|
|
21
|
+
login(/(?:\A|(?<=[\r\n]))(?:[^\s@]+)@[^\s>#]+[>#]\s*\z/)
|
|
22
|
+
command(/(?:\A|(?<=[\r\n]))(?:[^\s@]+)@[^\s>#]+[>#]\s*\z/)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
errors do
|
|
26
|
+
command(/^[ \t]*(?:Invalid syntax|Unknown command|Server error|Command failed)(?:[.:]|\s)/i)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
command_timeout 120
|
|
30
|
+
|
|
31
|
+
interactions do
|
|
32
|
+
login %r{(?:acknowledge|accept)[\s\S]*(?:yes/no|\[y/n\]|enter\s+yes)[^\r\n]*\z}i,
|
|
33
|
+
response: "yes\n"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class Radware
|
|
9
|
+
class RunningConfig < Netstack::Connector::RunningConfig::Rendered
|
|
10
|
+
# /cfg/dump 进入 Configuration 菜单;保留设备前缀,只替换菜单名称。
|
|
11
|
+
def prompt_text(command)
|
|
12
|
+
prompt = super
|
|
13
|
+
return prompt unless command.text == "/cfg/dump"
|
|
14
|
+
|
|
15
|
+
prompt.sub(/\A(>>[ \t]+(?:.* - )?)[^\r\n#]+#\z/, '\1Configuration#')
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Adapted from net-connector; copyright (c) 2026 net-connector contributors (MIT).
|
|
4
|
+
|
|
5
|
+
module Netstack
|
|
6
|
+
module Connector
|
|
7
|
+
module Vendors
|
|
8
|
+
class Radware
|
|
9
|
+
class TftpBackup < Tftp::Strategy
|
|
10
|
+
# Radware 原生导出生成压缩配置归档。
|
|
11
|
+
def self.file_extension = "tgz"
|
|
12
|
+
|
|
13
|
+
# 在连接前拒绝当前厂商不支持的参数组合。
|
|
14
|
+
def validate_options!(_target, source_file:, vrf: nil)
|
|
15
|
+
raise ArgumentError, "Radware Alteon does not use source_file" unless source_file.nil?
|
|
16
|
+
raise ArgumentError, "Radware TFTP backup does not use vrf" unless vrf.nil?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def receipt_metadata(_target, **) = { configuration_kind: :native_archive, format: :tgz }
|
|
20
|
+
|
|
21
|
+
# 构造当前厂商的 TFTP 导出交互脚本。
|
|
22
|
+
def script(target, source_file:, vrf: nil)
|
|
23
|
+
validate_options!(target, source_file: source_file, vrf: vrf)
|
|
24
|
+
|
|
25
|
+
Script.new([Command.new("/cfg/ptcfg #{target.host} -tftp", timeout: 180, interactions: [
|
|
26
|
+
interaction(/Enter\s+(?:hostname|IP address)[^\r\n]*:\s*\z/i, "#{target.host}\n"),
|
|
27
|
+
interaction(/Enter\s+name\s+of\s+(?:\.tgz\s+)?file[^\r\n]*:\s*\z/i, "#{target.path}\n"),
|
|
28
|
+
interaction(/Enter\s+username[^\r\n]*:\s*\z/i, "\n"),
|
|
29
|
+
interaction(/Include\s+private\s+keys\?\s*\[y\/n\]:\s*\z/i, "n\n"),
|
|
30
|
+
interaction(/Enter\s+"?mansync"?\s+to\s+get\s+real\/group\/virt\s+internal\s+index\s+config:\s*\z/i,
|
|
31
|
+
"mansync\n")
|
|
32
|
+
])])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# 检查设备回显是否确认 TFTP 传输完成。
|
|
36
|
+
def device_reported_complete?(result)
|
|
37
|
+
completion_lines(result).any? do |line|
|
|
38
|
+
line.match?(/\A
|
|
39
|
+
(?:Current\s+)?(?:configuration|config)\s+
|
|
40
|
+
(?:successfully\s+(?:uploaded|tftp'd)|(?:uploaded|tftp'd)(?:\s+successfully)?|
|
|
41
|
+
complete(?:d)?(?:\s+successfully)?)[.!]?
|
|
42
|
+
\z/ix)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|