vxlan 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.
@@ -13,22 +13,22 @@ module Vxlan
13
13
  # 在入口验证唯一归属;允许单角色为空,但 Fabric 至少有两个成员。
14
14
  def initialize(policy)
15
15
  unless policy.is_a?(Hash) && policy.keys == [ "fabrics" ] && policy["fabrics"].is_a?(Hash) && policy["fabrics"].any?
16
- raise ArgumentError, "需要 fabrics 策略"
16
+ raise ArgumentError, "A fabrics policy is required"
17
17
  end
18
18
  @groups = policy["fabrics"].to_h do |name, group|
19
19
  unless name.is_a?(String) && !name.empty? && group.is_a?(Hash) && (group.keys - ROLES).empty? && group.any?
20
- raise ArgumentError, "Fabric 需要明确的角色成员清单"
20
+ raise ArgumentError, "Fabric requires explicit role member lists"
21
21
  end
22
22
  roles = ROLES.to_h do |role|
23
23
  hosts = group.fetch(role, [])
24
- raise ArgumentError, "角色成员须为地址列表" unless hosts.is_a?(Array)
25
- hosts.each { |host| raise ArgumentError, "成员地址无效" unless host.is_a?(String) && !host.include?("/") && IPAddr.new(host).to_s == host }
24
+ raise ArgumentError, "Role members must be address lists" unless hosts.is_a?(Array)
25
+ hosts.each { |host| raise ArgumentError, "Invalid member address" unless host.is_a?(String) && !host.include?("/") && IPAddr.new(host).to_s == host }
26
26
  [ role, CtxParser::Immutable.copy(hosts) ]
27
27
  end
28
- raise ArgumentError, "Fabric 至少需要两个成员" unless roles.values.flatten.size >= 2
28
+ raise ArgumentError, "Fabric requires at least two members" unless roles.values.flatten.size >= 2
29
29
  [ name, roles.freeze ]
30
30
  end.freeze
31
- raise ArgumentError, "成员不能重复、跨角色或跨 Fabric" unless hosts.uniq == hosts
31
+ raise ArgumentError, "Members cannot be duplicated or shared across roles or Fabrics" unless hosts.uniq == hosts
32
32
  end
33
33
 
34
34
  # 扁平成员集合用于覆盖核对,不改变角色内的比较范围。
@@ -8,13 +8,13 @@ module Vxlan
8
8
  class Recommendations
9
9
  REFERENCE = "https://www.cisco.com/c/en/us/td/docs/dcn/nx-os/nexus9000/103x/configuration/vxlan/cisco-nexus-9000-series-nx-os-vxlan-configuration-guide-release-103x/m_configuring_vxlan_bgp_evpn.html"
10
10
  TITLES = {
11
- "vlan_vni_without_nve" => "VLAN 已映射 VNI,但缺少 NVE 成员",
12
- "nve_l2_without_vlan" => "L2 NVE 成员缺少 VLAN 映射",
13
- "nve_l2_without_evpn" => "L2 NVE 成员缺少 EVPN 配置",
14
- "nve_l3_without_vrf" => "L3 NVE 成员缺少 VRF 关联",
15
- "evpn_vni_without_nve" => "EVPN VNI 缺少 NVE 成员",
16
- "vni_layer_conflict" => "VNI 的 L2/L3 属性冲突",
17
- "l3_vni_without_nve" => "VRF VNI 缺少 NVE 成员"
11
+ "vlan_vni_without_nve" => "VLAN maps to a VNI but the NVE member is missing",
12
+ "nve_l2_without_vlan" => "L2 NVE member lacks a VLAN mapping",
13
+ "nve_l2_without_evpn" => "L2 NVE member lacks EVPN configuration",
14
+ "nve_l3_without_vrf" => "L3 NVE member lacks a VRF association",
15
+ "evpn_vni_without_nve" => "EVPN VNI lacks an NVE member",
16
+ "vni_layer_conflict" => "VNI has conflicting L2/L3 attributes",
17
+ "l3_vni_without_nve" => "VRF VNI lacks an NVE member"
18
18
  }.freeze
19
19
 
20
20
  # 联合证据可以解决本机未知,但角色冲突优先阻断所有草稿;不改变原始发现。
@@ -58,11 +58,11 @@ module Vxlan
58
58
  config.fetch("interfaces", {}).flat_map do |name, interface|
59
59
  interface.fetch("acl_bindings", {}).filter_map do |direction, acl|
60
60
  next if config.fetch("acls", {}).key?(acl)
61
- result = item("acl_reference_undefined", "#{name} / #{direction}", "接口引用 ACL #{acl},本机缺少定义",
61
+ result = item("acl_reference_undefined", "#{name} / #{direction}", "Interface references ACL #{acl}, but its local definition is missing",
62
62
  evidence: { interface: name, direction: direction, acl: acl, addresses: interface.fetch("addresses", []), vlan_vni: config.fetch("vlan_vnis", {})[name.delete_prefix("Vlan")] },
63
63
  verify: [ "show running-config", "show ip interface brief vrf all" ])
64
- result[:blockers] << "先确认接口是否承载业务及 ACL 的权威定义;无地址或无 VNI 可能是残留配置,不能直接推断业务中断,也不能按名称复制其他 Fabric 的 ACL。"
65
- result[:impact] = "新增 ACL 定义会作用于本机所有同名引用;须核对全部引用及回退范围。"
64
+ result[:blockers] << "First confirm whether the interface carries a service and identify the authoritative ACL definition; missing addresses or VNIs may be residual configuration. Do not infer a service outage or copy another Fabric's ACL by name."
65
+ result[:impact] = "Adding an ACL definition affects all local references to that name; verify every reference and the rollback scope."
66
66
  result[:references] = []
67
67
  result
68
68
  end
@@ -73,20 +73,20 @@ module Vxlan
73
73
  def item(code, scope, title, evidence:, verify:, category: "configuration")
74
74
  { code: code, scope: scope, title: title, category: category, status: "blocked", evidence: evidence,
75
75
  prerequisites: [], blockers: [], commands: [], rollback: [], verify: verify,
76
- expected: "重新采集并核对该项事实;业务连通性须另行验收。", references: [ REFERENCE ] }
76
+ expected: "Collect again and verify this fact; service connectivity requires separate acceptance.", references: [ REFERENCE ] }
77
77
  end
78
78
 
79
79
  # 仅在完整配置证实关联缺失时讨论增量补齐,不自动删除疑似闲置配置。
80
80
  def configuration_item(config, finding)
81
81
  vni = finding["vni"]
82
- result = item(finding["code"], "VNI #{vni}", TITLES.fetch(finding["code"], "配置关联需要核查"),
82
+ result = item(finding["code"], "VNI #{vni}", TITLES.fetch(finding["code"], "Configuration associations require verification"),
83
83
  evidence: finding, verify: [ "show running-config", "show nve vni", "show nve peers", "show bgp l2vpn evpn summary" ])
84
84
  result[:vni] = vni
85
- result[:prerequisites] = [ "确认该 VNI 应在本机承载业务,而非预配置或退网残留。", "核对当前软件版本、设备角色、同 Fabric 对端的 VLAN/VNI、RD/RT 与复制方式。", "重新采集配置并确认与本报告一致;审批维护窗口后逐台操作,出现异常先回读,勿盲目重放。" ]
86
- result[:impact] = "补齐可能开始发布路由或扩展二层广播域;须核对隔离边界、对端配置和回退条件。"
87
- result[:expected] = "VLAN、NVE 与 EVPN 关联一致;预期承载业务的 VNI 状态 Up,BGP/对端正常,业务验收通过。"
85
+ result[:prerequisites] = [ "Confirm that this VNI should carry a service locally, rather than being staged configuration or a decommissioning remnant.", "Check the current software version, device role, and same-Fabric peers' VLAN/VNI, RD/RT and replication mode.", "Collect configuration again and verify it matches this report; after maintenance window approval, operate one device at a time. On anomalies, read back first and avoid blind replay." ]
86
+ result[:impact] = "Completing the configuration may start route advertisement or extend the L2 broadcast domain; verify isolation boundaries, peer configuration and rollback conditions."
87
+ result[:expected] = "VLAN, NVE and EVPN associations are consistent; VNIs expected to carry services are Up, BGP/peers are healthy, and service acceptance passes."
88
88
  unless vni.is_a?(Integer) && vni.between?(1, 16_777_215)
89
- result[:blockers] << "VNI 非有效整数,无法生成命令。"
89
+ result[:blockers] << "VNI is not a valid integer; commands cannot be generated."
90
90
  return result
91
91
  end
92
92
  vlans = config.fetch("vlan_vnis", {}).select { |_vlan, mapped| mapped == vni }.keys
@@ -97,9 +97,9 @@ module Vxlan
97
97
  case finding["code"]
98
98
  when "nve_l2_without_evpn"
99
99
  if l2_context?(config, vni, vlans) && member && member["associate_vrf"] == false && member["replication"] == "bgp" && !evpn && auto_policy?(config)
100
- candidate(result, evpn_add(vni), evpn_remove(vni), "本机现有 L2 EVPN 均采用 RD auto、RT import/export auto;候选沿用此模式,仍需确认本 VNI 的业务意图。")
100
+ candidate(result, evpn_add(vni), evpn_remove(vni), "All existing local L2 EVPN instances use RD auto and RT import/export auto; the candidate follows this pattern, but the service intent of this VNI still requires confirmation.")
101
101
  else
102
- result[:blockers] << "需要唯一 VLAN 映射、L2 BGP 复制以及一致的 RD/RT 策略;缺失或冲突时不能套用 auto。"
102
+ result[:blockers] << "A unique VLAN mapping, L2 BGP replication and consistent RD/RT policy are required; missing or conflicting evidence does not permit applying auto."
103
103
  end
104
104
  when "vlan_vni_without_nve", "evpn_vni_without_nve"
105
105
  if l2_context?(config, vni, vlans) && !member && bgp_replication?(nve) && (evpn ? auto_evpn?(evpn) : auto_policy?(config))
@@ -107,15 +107,15 @@ module Vxlan
107
107
  rollback = [ "interface #{nve['interface']}", " no member vni #{vni}", "exit" ]
108
108
  commands.concat(evpn_add(vni)) unless evpn
109
109
  rollback.concat(evpn_remove(vni)) unless evpn
110
- candidate(result, commands, rollback, "本机既有 L2 成员统一使用 BGP 复制、EVPN auto 策略;仅补齐缺失的 NVE/EVPN,不启用额外 ARP 抑制。")
110
+ candidate(result, commands, rollback, "Existing local L2 members uniformly use BGP replication and EVPN auto policy; complete only missing NVE/EVPN configuration, without enabling additional ARP suppression.")
111
111
  else
112
- result[:blockers] << "需确认 VNI 的 L2/L3 角色、VLAN、NVE 接口、复制模式和 RD/RT,当前不能唯一确定增量配置。"
112
+ result[:blockers] << "Confirm the VNI's L2/L3 role, VLAN, NVE interface, replication mode and RD/RT; a unique configuration change cannot currently be determined."
113
113
  end
114
114
  when "nve_l2_without_vlan"
115
- result[:blockers] << "请提供本 VNI 对应 VLAN ID,并核对 access/trunk/SVI;不能由 VNI 尾数或其他设备名称猜测。"
115
+ result[:blockers] << "Provide the VLAN ID for this VNI and verify access/trunk/SVI configuration; do not infer it from VNI suffixes or other device names."
116
116
  result[:template] = [ "vlan <VLAN_ID>", " vn-segment #{vni}" ]
117
117
  else
118
- result[:blockers] << "需要明确 VRF/VNI 业务映射和 L2/L3 角色;先核对设计,再补齐或清理残留配置。"
118
+ result[:blockers] << "An explicit VRF/VNI service mapping and L2/L3 role are required; verify the design before completing or removing residual configuration."
119
119
  end
120
120
  result
121
121
  end
@@ -175,8 +175,8 @@ module Vxlan
175
175
  records = observation.fetch("records", [])
176
176
  evidence.merge!(configuration_records: records.size, review_status: records.map { |record| record["review_status"] })
177
177
  end
178
- item("evidence_incomplete", name, "证据不完整,先补采", evidence: evidence,
179
- verify: Array(CiscoNxos::Profile::COMMANDS[name.to_sym]), category: "evidence").merge(status: "investigate", expected: "完整回显或明确的零记录证明;空响应不能等同无配置。")
178
+ item("evidence_incomplete", name, "Evidence incomplete; collect missing evidence first", evidence: evidence,
179
+ verify: Array(CiscoNxos::Profile::COMMANDS[name.to_sym]), category: "evidence").merge(status: "investigate", expected: "A complete response or explicit zero-record evidence is required; an empty response does not establish absence of configuration.")
180
180
  end
181
181
  end
182
182
 
@@ -185,8 +185,8 @@ module Vxlan
185
185
  %w[nve_vnis nve_peers bgp_neighbors].filter_map do |dimension|
186
186
  rows = @dimensions.fetch(dimension, {}).fetch("records", []).select { |row| %w[down idle active connect opensent openconfirm].include?(row["state"].to_s.downcase) }
187
187
  next if rows.empty?
188
- item("#{dimension}_not_up", dimension, "观测到未建立的运行状态", evidence: rows,
189
- verify: [ CiscoNxos::Profile::COMMANDS[dimension.to_sym], "show running-config" ], category: "runtime").merge(status: "investigate", prerequisites: [ "先确认业务预期,再检查对端、底层路由、接口和配置;不自动重启邻居或启用接口。" ])
188
+ item("#{dimension}_not_up", dimension, "Observed runtime state is not established", evidence: rows,
189
+ verify: [ CiscoNxos::Profile::COMMANDS[dimension.to_sym], "show running-config" ], category: "runtime").merge(status: "investigate", prerequisites: [ "Confirm service expectations first, then check peers, underlay routing, interfaces and configuration; do not automatically restart neighbors or enable interfaces." ])
190
190
  end
191
191
  end
192
192
  end
data/lib/vxlan/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Vxlan
4
4
  # 公开包版本,供 RubyGems 元数据和运行时调用方共用。
5
- VERSION = "0.4.2"
5
+ VERSION = "0.5.0"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vxlan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gatework contributors
@@ -13,64 +13,58 @@ dependencies:
13
13
  name: ctx_cli
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 0.2.0
18
+ version: 0.2.1
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 0.2.0
25
+ version: 0.2.1
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: ctx_runner
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - "~>"
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.1.1
32
+ version: 0.2.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
37
+ - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 0.1.1
39
+ version: 0.2.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: ctx_parser
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - "~>"
44
+ - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 0.2.0
46
+ version: 0.2.7
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - "~>"
51
+ - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: 0.2.0
53
+ version: 0.2.7
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: ctx_driver
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '0.2'
61
58
  - - ">="
62
59
  - !ruby/object:Gem::Version
63
- version: 0.2.4
60
+ version: 0.2.7
64
61
  type: :runtime
65
62
  prerelease: false
66
63
  version_requirements: !ruby/object:Gem::Requirement
67
64
  requirements:
68
- - - "~>"
69
- - !ruby/object:Gem::Version
70
- version: '0.2'
71
65
  - - ">="
72
66
  - !ruby/object:Gem::Version
73
- version: 0.2.4
67
+ version: 0.2.7
74
68
  - !ruby/object:Gem::Dependency
75
69
  name: digest
76
70
  requirement: !ruby/object:Gem::Requirement