vxlan 0.2.2 → 0.2.3
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 +10 -0
- data/lib/vxlan/cisco_nxos/evpn_l2route.rb +1 -1
- data/lib/vxlan/cisco_nxos/parser.rb +2 -2
- data/lib/vxlan/cisco_nxos/parsers/show_bgp_l2vpn_evpn_summary.rb +2 -2
- data/lib/vxlan/cisco_nxos/parsers/show_nve_peers.rb +2 -2
- data/lib/vxlan/cisco_nxos/parsers/show_running_config.rb +29 -3
- data/lib/vxlan/cisco_nxos/parsers/show_vpc_brief.rb +1 -1
- data/lib/vxlan/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 715f96e8f586f8a7bacd62d8a7d5e47d4665884cdc20c3953d75c6282184955d
|
|
4
|
+
data.tar.gz: c3e70fb64cb7677d3e960d05f11622720f0a5842fc0171c7775047d0fd6b8d8a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c13eb74ea3ec0e75a8985a3bdf92503db849a6fc55e90af1a7a8ce051eaa810cdf50b25ab90869dae9696ca85bec142a83f0b7bbd649c5e6a7c6aea4019a09b6
|
|
7
|
+
data.tar.gz: 2bbb1204ab00cfcc50998a878b3d61c8614b03cc158833fecb1c3f1a2835672751bdd89d36e9dd79eca3cd893f91dfb4870c5ff8499817a2fe53bc758ec5eee4
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Changes to the public contract are recorded here. Versions follow Semantic Versioning.
|
|
4
4
|
|
|
5
|
+
## 0.2.3 (2026-10-18)
|
|
6
|
+
|
|
7
|
+
- Validate optional IPv4 next hops following static-route output interfaces without changing route fields or option text.
|
|
8
|
+
|
|
9
|
+
- Clear Bundler bootstrap variables when verifying packages in an isolated gem installation, including calls made through bundle exec.
|
|
10
|
+
|
|
11
|
+
- Reject unsupported or misplaced VRF address families before projecting targets and policy consumers, and validate anycast gateway MAC values and static-route next-hop forms.
|
|
12
|
+
|
|
13
|
+
- Parse runtime line boundaries and wrapped records independently of the host Ruby input/output record separators.
|
|
14
|
+
|
|
5
15
|
## 0.2.2 (2026-10-18)
|
|
6
16
|
|
|
7
17
|
### Changed
|
|
@@ -22,7 +22,7 @@ module Vxlan
|
|
|
22
22
|
type = self.class::TYPE
|
|
23
23
|
@totals = []
|
|
24
24
|
fields = previous = next_hop_index = nil
|
|
25
|
-
text.each_line.with_index(1) do |source, number|
|
|
25
|
+
text.each_line("\n").with_index(1) do |source, number|
|
|
26
26
|
line = source.strip
|
|
27
27
|
next if line.empty? || line.match?(/\A[-=\s]+\z/) || line.match?(/\A(?:Flags\s*-\s*)?(?:\([A-Za-z0-9]+\):[A-Za-z0-9 ,\/-]+\s*)+\z/)
|
|
28
28
|
if line.match?(/\ATopology\b/i)
|
|
@@ -25,7 +25,7 @@ module Vxlan
|
|
|
25
25
|
return CtxParser::Result.new(status: :partial, issues: [ { code: :invalid_encoding } ])
|
|
26
26
|
end
|
|
27
27
|
if Terminal.invalid_output?(text)
|
|
28
|
-
line = text.each_line.with_index(1).find { |source, _number| Terminal.invalid_output?(source) }.last
|
|
28
|
+
line = text.each_line("\n").with_index(1).find { |source, _number| Terminal.invalid_output?(source) }.last
|
|
29
29
|
return CtxParser::Result.new(status: :partial, issues: [ { code: :invalid_terminal_output, line: line } ])
|
|
30
30
|
end
|
|
31
31
|
if self.class::UNSUPPORTED&.match?(text)
|
|
@@ -72,7 +72,7 @@ module Vxlan
|
|
|
72
72
|
|
|
73
73
|
# 逐行提供有效表格内容,并记录明确空表证据。
|
|
74
74
|
def each_content_line(text, separators: false)
|
|
75
|
-
text.each_line.with_index(1) do |source, number|
|
|
75
|
+
text.each_line("\n").with_index(1) do |source, number|
|
|
76
76
|
line = source.strip
|
|
77
77
|
next if line.empty? || (!separators && line.match?(/\A[-=+\s]+\z/))
|
|
78
78
|
if self.class::EMPTY && line.match?(/\A(?:No #{self.class::EMPTY}(?: found)?|0 #{self.class::EMPTY})\.?\z/i)
|
|
@@ -67,14 +67,14 @@ module Vxlan
|
|
|
67
67
|
# 归一化平台值,同时保留未知或不支持的状态。
|
|
68
68
|
def normalize(text)
|
|
69
69
|
output = []
|
|
70
|
-
super.each_line do |line|
|
|
70
|
+
super.each_line("\n") do |line|
|
|
71
71
|
if output.last&.strip&.end_with?("State/") && line.strip == "PfxRcd"
|
|
72
72
|
output[-1] = output.last.rstrip + line.strip + "\n"
|
|
73
73
|
else
|
|
74
74
|
output << line
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
|
-
output.join
|
|
77
|
+
output.join("")
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
end
|
|
@@ -31,7 +31,7 @@ module Vxlan
|
|
|
31
31
|
# 归一化平台值,同时保留未知或不支持的状态。
|
|
32
32
|
def normalize(text)
|
|
33
33
|
output = []
|
|
34
|
-
super.each_line do |line|
|
|
34
|
+
super.each_line("\n") do |line|
|
|
35
35
|
tail, previous = line.strip, output.last&.strip
|
|
36
36
|
header = previous && previous.end_with?("Route") && tail == "r-Mac"
|
|
37
37
|
mac = previous && tail.match?(/\A[\da-f]{4}\.[\da-f]{4}\z/i) && previous.match?(/\Anve\d+\s+\d+(?:\.\d+){3}\s+(?:Up|Down)\s+\S+\s+\S+\s+[\da-f]{4}\.\z/i)
|
|
@@ -41,7 +41,7 @@ module Vxlan
|
|
|
41
41
|
output << line
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
|
-
output.join
|
|
44
|
+
output.join("")
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -111,7 +111,7 @@ module Vxlan
|
|
|
111
111
|
analyze_children(block) do |child|
|
|
112
112
|
if (match = /\Avni (\d+) (l[23])\z/i.match(child.text))
|
|
113
113
|
id = integer(match[1], 1..16_777_215)
|
|
114
|
-
store_unique_fact(data[:evpn], id, child, path: [ :evpn, id ]) { vrf(child).merge(type: match[2].downcase) }
|
|
114
|
+
store_unique_fact(data[:evpn], id, child, path: [ :evpn, id ]) { vrf(child, address_families: false).merge(type: match[2].downcase) }
|
|
115
115
|
else
|
|
116
116
|
issue(child)
|
|
117
117
|
end
|
|
@@ -156,7 +156,9 @@ module Vxlan
|
|
|
156
156
|
assign(data, :nv_overlay_evpn, block.text == "nv overlay evpn", block)
|
|
157
157
|
capture(block, data[:nv_overlay_evpn], [ :nv_overlay_evpn ]) if data.key?(:nv_overlay_evpn)
|
|
158
158
|
when /\Afabric forwarding anycast-gateway-mac (\S+)\z/
|
|
159
|
-
|
|
159
|
+
mac = $1
|
|
160
|
+
CtxParser::Values.mac_address(mac)
|
|
161
|
+
assign(data, :anycast_mac, mac, block)
|
|
160
162
|
capture(block, data[:anycast_mac], [ :anycast_mac ]) if data.key?(:anycast_mac)
|
|
161
163
|
when /\A(?:interface|vlan|vrf|router|vpc|evpn|feature|ip prefix-list|ipv6 prefix-list|route-map)\b/ then issue(block)
|
|
162
164
|
when /\A(?:%|ERROR:|Invalid command|--More--)/i then issue(block, :invalid_command_output)
|
|
@@ -166,7 +168,7 @@ module Vxlan
|
|
|
166
168
|
end
|
|
167
169
|
|
|
168
170
|
# 解析 VRF 定义及其 VNI 或 route-target 关联。
|
|
169
|
-
def vrf(block)
|
|
171
|
+
def vrf(block, address_families: true)
|
|
170
172
|
values = { route_targets: [], routes: [] }
|
|
171
173
|
analyze_children(block) do |child|
|
|
172
174
|
case child.text
|
|
@@ -175,6 +177,8 @@ module Vxlan
|
|
|
175
177
|
when /\Aroute-target / then values[:route_targets] << route_target(child)
|
|
176
178
|
when /\Aaddress-family (.+)\z/
|
|
177
179
|
family = $1
|
|
180
|
+
next issue(child) unless address_families && %w[ipv4\ unicast ipv6\ unicast].include?(family)
|
|
181
|
+
|
|
178
182
|
targets_start = values[:route_targets].size
|
|
179
183
|
exports_start = values.fetch(:exports, []).size
|
|
180
184
|
analyze_children(child) do |setting|
|
|
@@ -242,9 +246,31 @@ module Vxlan
|
|
|
242
246
|
def static_route(block)
|
|
243
247
|
words = block.text.split
|
|
244
248
|
raise ArgumentError if words.size < 4
|
|
249
|
+
validate_static_next_hop(words.drop(3))
|
|
250
|
+
|
|
245
251
|
{ prefix: ipv4_prefix(words[2]), next_hop: words[3], options: words.drop(4).join(" ") }
|
|
246
252
|
end
|
|
247
253
|
|
|
254
|
+
# IP route 的下一跳是 IPv4 或出接口;接口分词形式不能误当作地址拒绝。
|
|
255
|
+
def validate_static_next_hop(words)
|
|
256
|
+
hop = words.first
|
|
257
|
+
interface = /\A(?:ethernet\d+(?:\/\d+){1,2}(?:\.\d+)?|port-channel\d+(?:\.\d+)?|vlan\d+|mgmt\d+|null0)\z/i
|
|
258
|
+
offset = if interface.match?(hop)
|
|
259
|
+
1
|
|
260
|
+
elsif /\A(?:ethernet|port-channel|vlan|mgmt|null)\z/i.match?(hop)
|
|
261
|
+
raise ArgumentError unless words[1] && interface.match?(hop + words[1])
|
|
262
|
+
|
|
263
|
+
2
|
|
264
|
+
else
|
|
265
|
+
0
|
|
266
|
+
end
|
|
267
|
+
hop = words[offset]
|
|
268
|
+
# 出接口之后可直接跟已有选项;其余词须先作为 IPv4 下一跳校验。
|
|
269
|
+
return if offset.positive? && (hop.nil? || /\A(?:name|tag|track|vrf|\d+)\z/.match?(hop))
|
|
270
|
+
|
|
271
|
+
raise ArgumentError unless IPAddr.new(ip_address(hop)).ipv4?
|
|
272
|
+
end
|
|
273
|
+
|
|
248
274
|
# 解析十进制整数,并校验声明的边界。
|
|
249
275
|
def integer(value, range)
|
|
250
276
|
raise ArgumentError unless value.is_a?(String)
|
|
@@ -30,7 +30,7 @@ module Vxlan
|
|
|
30
30
|
end
|
|
31
31
|
row.merge!(consistency: member[1], reason: member[2], active_vlans_summary: member[3])
|
|
32
32
|
elsif match[4].match?(/\A[\d, .-]+\z/)
|
|
33
|
-
row[:active_vlans_summary] = rest.join
|
|
33
|
+
row[:active_vlans_summary] = rest.join("")
|
|
34
34
|
else
|
|
35
35
|
unparsed_records(number)
|
|
36
36
|
next
|
data/lib/vxlan/version.rb
CHANGED