Fix ZTE BGP vpnv4/vpnv6 RD VRF filldown and map VRF-scoped AFI to ipv4/ipv6.

Stop inheriting `(default for vrf)` onto RD-only blocks, and normalize CE vrf commands so `(afi,vrf)` sheet splits no longer collide with global VPNx tables.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-27 16:42:52 +08:00
parent d701e4a35e
commit c6a98e2526
3 changed files with 90 additions and 6 deletions

View file

@ -33,9 +33,32 @@ _LOCAL_AS_IN_CMD_RE = re.compile(r"(?i)(?:^|\s)as\s+(\S+)(?:\s*\||\s*$)")
def _detect_bgp_afi(command: str, params: dict[str, str] | None) -> str:
"""Detect BGP AFI for peer/route rows.
VRF-scoped CLI (``… vrf <name> …``) is the CE/ipv4|ipv6 VRF AF even when
ZTE spells the command as ``vpnv4|vpnv6 unicast vrf``. Map those to
``ipv4``/``ipv6`` so split-by-(afi,vrf) does not collide with global
VPNv4/VPNv6 tables that may carry RD ``(default for vrf X)`` into ``vrf``.
"""
low = str(command or "").lower()
has_vrf = bool(_detect_vrf(command, params))
if has_vrf:
# Prefer command tokens over stale params.afi=vpnv*
if "vpnv6" in low or "ipv6" in low:
return "ipv6"
if "vpnv4" in low or "ipv4" in low:
return "ipv4"
# Bare ``vrf`` with params.afi hint
if params and params.get("afi"):
af = str(params.get("afi") or "").strip().lower()
if af in ("vpnv6", "ipv6"):
return "ipv6"
if af in ("vpnv4", "ipv4"):
return "ipv4"
return "ipv4"
if params and params.get("afi"):
return str(params.get("afi") or "").strip().lower()
low = str(command or "").lower()
if "l2vpn" in low and "evpn" in low:
return "evpn"
if "l2vpn" in low and "vpls" in low:

View file

@ -269,7 +269,10 @@ def _hand_parse(
pending_nh = ""
pending_rest = ""
current_rd = ""
current_vrf = vrf
# Command-scoped VRF (CE view); RD blocks may temporarily override via
# ``(default for vrf X)`` but must not fill-down onto RD-only segments.
cmd_vrf = vrf
current_vrf = cmd_vrf
def _flush_pending(*, rest: str = "", path_continuation: bool = False) -> dict[str, Any] | None:
nonlocal pending_net, pending_flags, pending_nh, pending_rest
@ -313,9 +316,8 @@ def _hand_parse(
if row:
yield row
current_rd = (rd_m.group("rd") or "").strip()
vrf_from_rd = (rd_m.group("vrf") or "").strip()
if vrf_from_rd:
current_vrf = vrf_from_rd
# Reset every RD header: annotation wins; else fall back to cmd VRF.
current_vrf = (rd_m.group("vrf") or "").strip() or cmd_vrf
continue
if _skip_noise_line(line):
continue