vxlan 0.1.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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +21 -0
  3. data/LICENSE +22 -0
  4. data/README-zh.md +90 -0
  5. data/README.md +90 -0
  6. data/docs/architecture-zh.md +95 -0
  7. data/docs/release.md +13 -0
  8. data/examples/offline.rb +16 -0
  9. data/lib/vxlan/bgp_neighbors.rb +29 -0
  10. data/lib/vxlan/cisco_nxos/configuration.rb +84 -0
  11. data/lib/vxlan/cisco_nxos/configuration_document.rb +31 -0
  12. data/lib/vxlan/cisco_nxos/configuration_parsing/interfaces.rb +91 -0
  13. data/lib/vxlan/cisco_nxos/configuration_parsing/policies.rb +58 -0
  14. data/lib/vxlan/cisco_nxos/configuration_parsing/routing.rb +62 -0
  15. data/lib/vxlan/cisco_nxos/evpn_l2route.rb +118 -0
  16. data/lib/vxlan/cisco_nxos/parser.rb +105 -0
  17. data/lib/vxlan/cisco_nxos/parsers/show_bgp_l2vpn_evpn_summary.rb +80 -0
  18. data/lib/vxlan/cisco_nxos/parsers/show_cdp_neighbors_detail.rb +86 -0
  19. data/lib/vxlan/cisco_nxos/parsers/show_interface_brief.rb +84 -0
  20. data/lib/vxlan/cisco_nxos/parsers/show_ip_ospf_neighbors.rb +48 -0
  21. data/lib/vxlan/cisco_nxos/parsers/show_l2route_evpn_mac_all.rb +14 -0
  22. data/lib/vxlan/cisco_nxos/parsers/show_l2route_evpn_mac_ip_all.rb +14 -0
  23. data/lib/vxlan/cisco_nxos/parsers/show_mac_address_table.rb +42 -0
  24. data/lib/vxlan/cisco_nxos/parsers/show_nve_peers.rb +49 -0
  25. data/lib/vxlan/cisco_nxos/parsers/show_nve_vni.rb +39 -0
  26. data/lib/vxlan/cisco_nxos/parsers/show_port_channel_summary.rb +49 -0
  27. data/lib/vxlan/cisco_nxos/parsers/show_running_config.rb +303 -0
  28. data/lib/vxlan/cisco_nxos/parsers/show_vpc_brief.rb +77 -0
  29. data/lib/vxlan/cisco_nxos/profile.rb +39 -0
  30. data/lib/vxlan/collector.rb +127 -0
  31. data/lib/vxlan/configuration.rb +20 -0
  32. data/lib/vxlan/configuration_review.rb +43 -0
  33. data/lib/vxlan/l2route_parser.rb +21 -0
  34. data/lib/vxlan/observation.rb +24 -0
  35. data/lib/vxlan/parser_registry.rb +9 -0
  36. data/lib/vxlan/prefix_list_references.rb +89 -0
  37. data/lib/vxlan/runtime_parser.rb +22 -0
  38. data/lib/vxlan/snapshot.rb +23 -0
  39. data/lib/vxlan/version.rb +6 -0
  40. data/lib/vxlan.rb +16 -0
  41. metadata +140 -0
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parser_registry"
4
+ require "ctx_driver"
5
+ require_relative "observation"
6
+ require_relative "snapshot"
7
+
8
+ module Vxlan
9
+ # 单设备单会话顺序采集;各维度独立失败,断链后不重放查询。
10
+ class Collector
11
+ COMMANDS = CiscoNxos::Profile::COMMANDS
12
+ PREPARATION_COMMANDS = [ "terminal length 0", "terminal width 511" ].freeze
13
+
14
+ # 注入为顺序命令观测生成时间戳的时钟。
15
+ def initialize(clock: -> { Time.now.utc })
16
+ @clock = clock
17
+ end
18
+
19
+ # 通过调用方持有的连接器顺序采集请求维度。
20
+ def collect(connector:, target:, fabric_key:, model: nil, dimensions: COMMANDS.keys)
21
+ raise ArgumentError, "Target must be a hash" unless target.is_a?(Hash)
22
+
23
+ unless dimensions.is_a?(Array) && dimensions.any? && dimensions.uniq == dimensions && (dimensions - COMMANDS.keys).empty?
24
+ raise ArgumentError, "Unknown or duplicate VXLAN dimensions"
25
+ end
26
+ raise ArgumentError, "VXLAN requires Cisco NX-OS" unless connector.platform.to_s == "cisco_nxos"
27
+ profile = ParserRegistry.fetch(connector.platform, model: model)
28
+
29
+ observations = {}
30
+ session_finished = false
31
+ begin
32
+ connector.open do |session|
33
+ reader = CtxDriver::Reader.new(session: session, clock: @clock)
34
+ if (code = prepare(reader))
35
+ dimensions.each { |name| observations[name] = failure(name, code) }
36
+ observations[:collection] = failure(:collection, code)
37
+ next
38
+ end
39
+ dimensions.each do |name|
40
+ config = observations[:configuration]
41
+ if name == :vpc && !reader.failure && unconfigured?(name, config)
42
+ observations[name] = Observation.new(name: name, status: :complete, completed_at: @clock.call,
43
+ issues: [ { code: :vpc_not_configured } ])
44
+ else
45
+ observations[name] = read(reader, name, config, profile)
46
+ end
47
+ end
48
+ session_finished = true
49
+ end
50
+ rescue StandardError => error
51
+ raise unless CtxDriver::Failure.connection_error?(error, connector)
52
+
53
+ code = CtxDriver::Failure.code(error, :connection_failed)
54
+ dimensions.each { |name| observations[name] ||= failure(name, code) }
55
+ observations[:collection] = failure(:collection, session_finished ? :session_close_failed : code)
56
+ end
57
+ status = if observations.values.all?(&:complete?)
58
+ :complete
59
+ elsif observations.values.any? { |value| %i[complete partial].include?(value.status) }
60
+ :partial
61
+ else
62
+ :failed
63
+ end
64
+ observations[:collection] ||= Observation.new(name: :collection, status: status, completed_at: @clock.call)
65
+ Snapshot.new(target: target,
66
+ fabric_key: fabric_key, dimensions: observations, observed_at: @clock.call)
67
+ end
68
+
69
+ private
70
+
71
+ # 采集设备回显前,禁用分页并归一化终端宽度。
72
+ def prepare(reader)
73
+ PREPARATION_COMMANDS.each do |command|
74
+ capture = reader.read(command)
75
+ return capture.failure.fetch(:code) unless capture.success?
76
+ end
77
+ nil
78
+ end
79
+
80
+ # 解析失败限定在单条查询内;Reader 负责传输与回执校验。
81
+ def read(reader, name, configuration, profile)
82
+ command = profile.command(name)
83
+ capture = reader.read(command)
84
+ metadata = { started_at: capture.started_at, completed_at: capture.completed_at, evidence: capture.evidence }
85
+ unless capture.success?
86
+ return failed_capture(name, capture, command).with(**metadata)
87
+ end
88
+ begin
89
+ body = capture.body
90
+ parsed = if body.strip.empty? && unconfigured?(name, configuration)
91
+ code = name == :port_channels ? :port_channels_not_configured : :nve_not_configured
92
+ Observation.new(name: name, status: :complete, issues: [ { code: code } ])
93
+ else
94
+ Observation.new(name: name, **profile.parse(name, body, complete: true).to_h)
95
+ end
96
+ parsed.with(**metadata)
97
+ rescue StandardError => error
98
+ details = CtxDriver::Failure.details(error, fallback: :command_processing_failed, stage: :parse)
99
+ failure(name, details.fetch(:code), **details.except(:code), command: command).with(**metadata)
100
+ end
101
+ end
102
+
103
+ # 保留传输首次不可用原因,并标记从未发送的命令。
104
+ def failed_capture(name, capture, command)
105
+ failure(name, capture.failure.fetch(:code), **capture.failure.except(:code), command: command, skipped: capture.skipped?)
106
+ end
107
+
108
+ # 仅当同一会话的完整配置证明功能未启用时,才接受空运行回显。
109
+ def unconfigured?(name, configuration)
110
+ return false unless configuration&.complete? && configuration.records.one?
111
+
112
+ settings = configuration.records.first
113
+ case name
114
+ when :vpc then !settings[:vpc_domain] && !settings.fetch(:features).include?("vpc")
115
+ when :nve_peers, :nve_vnis then !settings.fetch(:nve)[:interface]
116
+ when :port_channels
117
+ settings.fetch(:interfaces).none? { |interface, values| /\Aport-channel\d+\z/i.match?(interface) || values[:channel_group] }
118
+ else false
119
+ end
120
+ end
121
+
122
+ # 构建失败观测,不丢弃已成功的同级维度。
123
+ def failure(name, code, **details)
124
+ Observation.new(name: name, status: :failed, issues: [ { code: code, **details } ], completed_at: @clock.call)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parser_registry"
4
+ require_relative "observation"
5
+
6
+ module Vxlan
7
+ # 配置与运行观测使用相同的平台、型号策略。
8
+ class Configuration
9
+ # 按当前平台和型号选择 VXLAN 配置策略。
10
+ def initialize(platform: :cisco_nxos, model: nil)
11
+ @profile = ParserRegistry.fetch(platform, model: model)
12
+ end
13
+
14
+ # 解析 NX-OS 配置,将事实封装为 VXLAN 配置观测。
15
+ def parse(text, complete: false)
16
+ result = @profile.parse(:configuration, text, complete: complete)
17
+ Observation.new(name: :configuration, **result.to_h)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vxlan
4
+ # 配置关联审查独立于语法解析及运行健康检查。
5
+ class ConfigurationReview
6
+ # 绑定归一化配置,用于交叉引用一致性检查。
7
+ def initialize(configuration)
8
+ @configuration = configuration
9
+ end
10
+
11
+ # 检查完整配置中的关联缺失或引用矛盾。
12
+ def call(complete: false)
13
+ raise ArgumentError, "Completeness must be boolean" unless [ true, false ].include?(complete)
14
+ return [] unless complete
15
+
16
+ members = @configuration.fetch(:nve).fetch(:members, {})
17
+ vlans = @configuration.fetch(:vlan_vnis).group_by { |_vlan, vni| vni }
18
+ evpn = @configuration.fetch(:evpn)
19
+ findings = []
20
+ vlans.each do |vni, entries|
21
+ next unless vni && !members.key?(vni)
22
+
23
+ findings << { code: :vlan_vni_without_nve, vni: vni, vlan_ids: entries.map(&:first).sort }
24
+ end
25
+ members.each do |vni, settings|
26
+ next if settings[:associate_vrf]
27
+
28
+ findings << { code: :nve_l2_without_vlan, vni: vni } unless vlans.key?(vni)
29
+ if @configuration[:nv_overlay_evpn] && !evpn.key?(vni)
30
+ findings << { code: :nve_l2_without_evpn, vni: vni }
31
+ end
32
+ end
33
+ evpn.each_key { |vni| findings << { code: :evpn_vni_without_nve, vni: vni } unless members.key?(vni) }
34
+ @configuration.fetch(:vrfs).each do |name, settings|
35
+ next unless settings[:vni]
36
+ member = members[settings[:vni]]
37
+ code = member ? (!member[:associate_vrf] ? :vni_layer_conflict : nil) : :l3_vni_without_nve
38
+ findings << { code: code, vni: settings[:vni], vrf: name } if code
39
+ end
40
+ findings.sort_by { |finding| [ finding[:vni], finding[:code].to_s ] }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "runtime_parser"
4
+
5
+ module Vxlan
6
+ # 保留 L2RIB 公共入口,由平台策略选择解析实现。
7
+ class L2routeParser
8
+ TYPES = { l2route_macs: :mac, l2route_mac_ips: :mac_ip }.freeze
9
+
10
+ # 选择 EVPN 路由维度使用的运行解析器。
11
+ def initialize(platform: :cisco_nxos, model: nil)
12
+ @parser = RuntimeParser.new(platform: platform, model: model)
13
+ end
14
+
15
+ # 校验 L2RIB 维度,将 EVPN 路由解析委派给平台策略。
16
+ def parse(name, text, complete:)
17
+ TYPES.fetch(name)
18
+ @parser.parse(name, text, complete: complete)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ctx_parser/immutable"
4
+
5
+ module Vxlan
6
+ # 保留命令事实与完整性;默认检查不输出原始配置或凭据。
7
+ Observation = Data.define(:name, :status, :records, :issues, :evidence, :started_at, :completed_at) do
8
+ # 冻结单个维度的记录、状态及诊断元数据。
9
+ def initialize(name:, status:, records: [], issues: [], evidence: [], started_at: nil, completed_at: nil)
10
+ raise ArgumentError, "Invalid observation status" unless %i[complete partial failed unsupported not_requested].include?(status)
11
+ raise ArgumentError, "Records must be hashes" unless records.is_a?(Array) && records.all? { |row| row.is_a?(Hash) }
12
+
13
+ super(name: name, status: status, records: CtxParser::Immutable.copy(records),
14
+ issues: CtxParser::Immutable.copy(issues), evidence: CtxParser::Immutable.copy(evidence),
15
+ started_at: started_at&.dup&.freeze, completed_at: completed_at&.dup&.freeze)
16
+ end
17
+
18
+ # 判断所有已选事实是否都有完整证据支持。
19
+ def complete? = status == :complete
20
+
21
+ # 展示对象身份与状态,避免泄露原始回显或凭据。
22
+ def inspect = "#<#{self.class.name} name=#{name} status=#{status} records=#{records.size}>"
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ctx_parser/registry"
4
+ require_relative "cisco_nxos/profile"
5
+
6
+ module Vxlan
7
+ # VXLAN 在本领域声明平台支持,公共解析库不依赖领域实现。
8
+ ParserRegistry = CtxParser::Registry.new(profiles: { cisco_nxos: CiscoNxos::Profile })
9
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "bgp_neighbors"
4
+
5
+ module Vxlan
6
+ # 关联命名列表、route-map 选择器及所有支持的路由使用方。
7
+ class PrefixListReferences
8
+ # 绑定路由配置,用于解析前缀列表的使用方。
9
+ def initialize(configuration)
10
+ @configuration = configuration
11
+ end
12
+
13
+ # 按地址族将前缀列表关联到 route-map 条目和路由使用方。
14
+ def call(address_family: "ipv4")
15
+ raise ArgumentError, "Unsupported address family" unless %w[ipv4 ipv6].include?(address_family)
16
+
17
+ bindings = route_map_bindings
18
+ @configuration.fetch(:route_maps).each_with_object({}) do |(name, entries), references|
19
+ entries.sort_by { |entry| entry.fetch(:sequence) }.each do |entry|
20
+ matches = entry.fetch(:prefix_list_matches) do
21
+ [ { address_family: "ipv4", selector: "address", names: entry.fetch(:prefix_lists, []) } ]
22
+ end
23
+ matches.select { |match| match[:address_family] == address_family }.each do |match|
24
+ caller = { type: "route_map", name: name, sequence: entry.fetch(:sequence), action: entry.fetch(:action),
25
+ bindings: bindings.fetch(name, []).uniq }
26
+ caller[:selector] = match[:selector] unless match[:selector] == "address"
27
+ match.fetch(:names).uniq.each do |prefix_list|
28
+ callers = (references[prefix_list] ||= [])
29
+ callers << caller unless callers.include?(caller)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # 关联关系保留协议、VRF、地址族及对等体,避免共享策略被混用。
39
+ def route_map_bindings
40
+ result = Hash.new { |hash, key| hash[key] = [] }
41
+ bgp = @configuration.fetch(:bgp)
42
+ { "default" => bgp }.merge(bgp.fetch(:vrfs, {})).each do |vrf, settings|
43
+ settings.fetch(:address_families, {}).each do |family, values|
44
+ context = { protocol: "bgp", process: bgp[:asn].to_s, vrf: vrf, address_family: family }
45
+ add_redistribution(result, values, context)
46
+ values.fetch(:networks, []).each do |network|
47
+ next unless network[:route_map]
48
+
49
+ result[network[:route_map]] << context.merge(usage: "network", prefix: network.fetch(:prefix))
50
+ end
51
+ retain = values[:retain_route_target]
52
+ if retain&.start_with?("route-map ")
53
+ result[retain.delete_prefix("route-map ")] << context.merge(usage: "retain_route_target")
54
+ end
55
+ end
56
+ end
57
+ BgpNeighbors.new(bgp).each do |vrf, peer, settings|
58
+ settings.fetch(:address_families, {}).each do |family, values|
59
+ values.fetch(:route_maps, {}).each do |direction, route_map|
60
+ result[route_map] << { protocol: "bgp", process: bgp[:asn].to_s, vrf: vrf,
61
+ address_family: family, usage: "neighbor_route_map", peer: peer, direction: direction.to_s }
62
+ end
63
+ end
64
+ end
65
+ @configuration.fetch(:ospf).each do |process, settings|
66
+ { "default" => settings }.merge(settings.fetch(:vrfs, {})).each do |vrf, values|
67
+ add_redistribution(result, values, protocol: "ospf", process: process, vrf: vrf)
68
+ end
69
+ end
70
+ @configuration.fetch(:vrfs, {}).each do |vrf, settings|
71
+ settings.fetch(:exports, []).each do |entry|
72
+ result[entry.fetch(:route_map)] << { protocol: "vrf", vrf: vrf, address_family: entry.fetch(:address_family),
73
+ usage: "export", target_vrf: entry.fetch(:vrf) }
74
+ end
75
+ end
76
+ result
77
+ end
78
+
79
+ # 将重分发 route-map 关联到其路由使用方。
80
+ def add_redistribution(result, settings, context)
81
+ settings.fetch(:redistribute, []).each do |entry|
82
+ next unless entry[:route_map]
83
+
84
+ result[entry[:route_map]] << context.merge(usage: "redistribute", source_protocol: entry.fetch(:protocol),
85
+ source_process: entry[:process]).compact
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parser_registry"
4
+ require_relative "observation"
5
+
6
+ module Vxlan
7
+ # 将纯平台解析事实封装为对应 VXLAN 维度观测。
8
+ class RuntimeParser
9
+ # 按请求的平台和型号选择运行解析行为。
10
+ def initialize(platform: :cisco_nxos, model: nil)
11
+ @profile = ParserRegistry.fetch(platform, model: model)
12
+ end
13
+
14
+ # 拒绝配置维度,仅解析所选 VXLAN 运行查询。
15
+ def parse(name, text, complete: false)
16
+ raise ArgumentError, "Unsupported runtime dimension" if name == :configuration || !@profile.commands.key?(name)
17
+
18
+ result = @profile.parse(name, text, complete: complete)
19
+ Observation.new(name: name, **result.to_h)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ctx_parser/immutable"
4
+
5
+ module Vxlan
6
+ # 携带单设备 Fabric 身份及观测结果,不管理资产清单。
7
+ Snapshot = Data.define(:target, :fabric_key, :dimensions, :observed_at) do
8
+ # 事实由不可变领域观测持有;仅复制维度选择,不重复复制事实。
9
+ def initialize(target:, fabric_key:, dimensions:, observed_at:)
10
+ super(target: CtxParser::Immutable.copy(target), fabric_key: CtxParser::Immutable.copy(fabric_key),
11
+ dimensions: dimensions.dup.freeze, observed_at: observed_at&.dup&.freeze)
12
+ end
13
+
14
+ # 判断所有已选事实是否都有完整证据支持。
15
+ def complete?
16
+ selected = dimensions.values.reject { |dimension| dimension.status == :not_requested }
17
+ selected.any? && selected.all?(&:complete?)
18
+ end
19
+
20
+ # 展示对象身份与状态,避免泄露原始回显或凭据。
21
+ def inspect = "#<#{self.class.name} fabric=#{fabric_key.inspect} complete=#{complete?}>"
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vxlan
4
+ # 公开包版本,供 RubyGems 元数据和运行时调用方共用。
5
+ VERSION = "0.1.0"
6
+ end
data/lib/vxlan.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "vxlan/version"
4
+
5
+ module Vxlan
6
+ end
7
+
8
+ require_relative "vxlan/observation"
9
+ require_relative "vxlan/snapshot"
10
+ require_relative "vxlan/configuration"
11
+ require_relative "vxlan/runtime_parser"
12
+ require_relative "vxlan/l2route_parser"
13
+ require_relative "vxlan/bgp_neighbors"
14
+ require_relative "vxlan/configuration_review"
15
+ require_relative "vxlan/prefix_list_references"
16
+ require_relative "vxlan/collector"
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vxlan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gatework contributors
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ctx_parser
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ctx_driver
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: digest
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: ipaddr
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ description: Independent configuration and runtime parsers for VXLAN fabrics, with
69
+ immutable snapshots and injected collection sessions.
70
+ executables: []
71
+ extensions: []
72
+ extra_rdoc_files: []
73
+ files:
74
+ - CHANGELOG.md
75
+ - LICENSE
76
+ - README-zh.md
77
+ - README.md
78
+ - docs/architecture-zh.md
79
+ - docs/release.md
80
+ - examples/offline.rb
81
+ - lib/vxlan.rb
82
+ - lib/vxlan/bgp_neighbors.rb
83
+ - lib/vxlan/cisco_nxos/configuration.rb
84
+ - lib/vxlan/cisco_nxos/configuration_document.rb
85
+ - lib/vxlan/cisco_nxos/configuration_parsing/interfaces.rb
86
+ - lib/vxlan/cisco_nxos/configuration_parsing/policies.rb
87
+ - lib/vxlan/cisco_nxos/configuration_parsing/routing.rb
88
+ - lib/vxlan/cisco_nxos/evpn_l2route.rb
89
+ - lib/vxlan/cisco_nxos/parser.rb
90
+ - lib/vxlan/cisco_nxos/parsers/show_bgp_l2vpn_evpn_summary.rb
91
+ - lib/vxlan/cisco_nxos/parsers/show_cdp_neighbors_detail.rb
92
+ - lib/vxlan/cisco_nxos/parsers/show_interface_brief.rb
93
+ - lib/vxlan/cisco_nxos/parsers/show_ip_ospf_neighbors.rb
94
+ - lib/vxlan/cisco_nxos/parsers/show_l2route_evpn_mac_all.rb
95
+ - lib/vxlan/cisco_nxos/parsers/show_l2route_evpn_mac_ip_all.rb
96
+ - lib/vxlan/cisco_nxos/parsers/show_mac_address_table.rb
97
+ - lib/vxlan/cisco_nxos/parsers/show_nve_peers.rb
98
+ - lib/vxlan/cisco_nxos/parsers/show_nve_vni.rb
99
+ - lib/vxlan/cisco_nxos/parsers/show_port_channel_summary.rb
100
+ - lib/vxlan/cisco_nxos/parsers/show_running_config.rb
101
+ - lib/vxlan/cisco_nxos/parsers/show_vpc_brief.rb
102
+ - lib/vxlan/cisco_nxos/profile.rb
103
+ - lib/vxlan/collector.rb
104
+ - lib/vxlan/configuration.rb
105
+ - lib/vxlan/configuration_review.rb
106
+ - lib/vxlan/l2route_parser.rb
107
+ - lib/vxlan/observation.rb
108
+ - lib/vxlan/parser_registry.rb
109
+ - lib/vxlan/prefix_list_references.rb
110
+ - lib/vxlan/runtime_parser.rb
111
+ - lib/vxlan/snapshot.rb
112
+ - lib/vxlan/version.rb
113
+ homepage: https://github.com/gatework/vxlan
114
+ licenses:
115
+ - MIT
116
+ metadata:
117
+ source_code_uri: https://github.com/gatework/vxlan
118
+ bug_tracker_uri: https://github.com/gatework/vxlan/issues
119
+ changelog_uri: https://github.com/gatework/vxlan/blob/main/CHANGELOG.md
120
+ documentation_uri: https://github.com/gatework/vxlan#readme
121
+ allowed_push_host: https://rubygems.org
122
+ rubygems_mfa_required: 'true'
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 4.0.6
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubygems_version: 4.0.16
138
+ specification_version: 4
139
+ summary: Parse and collect Cisco NX-OS VXLAN evidence.
140
+ test_files: []