mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 03:10:46 +08:00
Add BGP as <local_as> binding for multi-process ZTE collects.
Stamp local_as on peer/route/evpn results and discover (local_as, neighbor/vrf) pairs from config_bgp_peer so each router bgp process is collected separately. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b453c6871e
commit
262082ef08
10 changed files with 690 additions and 318 deletions
|
|
@ -223,7 +223,13 @@ def expand_from_bindings(
|
||||||
val = params.get(ph.name) or params.get(ph.schema_field) or ""
|
val = params.get(ph.name) or params.get(ph.schema_field) or ""
|
||||||
if ph.required and not val:
|
if ph.required and not val:
|
||||||
raise ValueError(f"missing placeholder {ph.name} for {profile.profile_id}")
|
raise ValueError(f"missing placeholder {ph.name} for {profile.profile_id}")
|
||||||
rendered = rendered.replace(f"<{ph.name}>", val)
|
if not val:
|
||||||
|
# Optional empty: drop `` as <local_as>`` (or bare `` <name>``) so
|
||||||
|
# legacy single-AS binds still render a valid show command.
|
||||||
|
rendered = rendered.replace(f" as <{ph.name}>", "")
|
||||||
|
rendered = rendered.replace(f"<{ph.name}>", "")
|
||||||
|
else:
|
||||||
|
rendered = rendered.replace(f"<{ph.name}>", val)
|
||||||
concrete = normalize_command(rendered)
|
concrete = normalize_command(rendered)
|
||||||
if re.search(r"<[^>]+>", concrete):
|
if re.search(r"<[^>]+>", concrete):
|
||||||
raise ValueError(f"unresolved placeholders in: {concrete}")
|
raise ValueError(f"unresolved placeholders in: {concrete}")
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ _CONT_RE = re.compile(
|
||||||
re.I,
|
re.I,
|
||||||
)
|
)
|
||||||
_VRF_IN_CMD_RE = re.compile(r"(?i)\bvrf\s+(\S+)")
|
_VRF_IN_CMD_RE = re.compile(r"(?i)\bvrf\s+(\S+)")
|
||||||
|
# ``as <local_as>`` sits before ``| one-line`` (optional for legacy cmds).
|
||||||
|
_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:
|
def _detect_bgp_afi(command: str, params: dict[str, str] | None) -> str:
|
||||||
|
|
@ -56,6 +58,13 @@ def _detect_vrf(command: str, params: dict[str, str] | None) -> str:
|
||||||
return m.group(1).strip() if m else ""
|
return m.group(1).strip() if m else ""
|
||||||
|
|
||||||
|
|
||||||
|
def _detect_local_as(command: str, params: dict[str, str] | None) -> str:
|
||||||
|
if params and params.get("local_as"):
|
||||||
|
return str(params.get("local_as") or "").strip()
|
||||||
|
m = _LOCAL_AS_IN_CMD_RE.search(str(command or ""))
|
||||||
|
return m.group(1).strip() if m else ""
|
||||||
|
|
||||||
|
|
||||||
def _state_and_pfx(state_raw: str) -> tuple[str, str]:
|
def _state_and_pfx(state_raw: str) -> tuple[str, str]:
|
||||||
raw = str(state_raw or "").strip()
|
raw = str(state_raw or "").strip()
|
||||||
if raw.isdigit():
|
if raw.isdigit():
|
||||||
|
|
@ -66,6 +75,7 @@ def _state_and_pfx(state_raw: str) -> tuple[str, str]:
|
||||||
|
|
||||||
def _peer_row(
|
def _peer_row(
|
||||||
*,
|
*,
|
||||||
|
local_as: str,
|
||||||
afi: str,
|
afi: str,
|
||||||
vrf: str,
|
vrf: str,
|
||||||
nei: str,
|
nei: str,
|
||||||
|
|
@ -78,6 +88,7 @@ def _peer_row(
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
state, pfx = _state_and_pfx(state_raw)
|
state, pfx = _state_and_pfx(state_raw)
|
||||||
return {
|
return {
|
||||||
|
"local_as": local_as[:16],
|
||||||
"afi": afi[:32],
|
"afi": afi[:32],
|
||||||
"vrf": vrf[:128],
|
"vrf": vrf[:128],
|
||||||
"neighbor": nei[:128],
|
"neighbor": nei[:128],
|
||||||
|
|
@ -94,6 +105,7 @@ def _peer_row(
|
||||||
def _map_fsm_rows(
|
def _map_fsm_rows(
|
||||||
rows: list[dict[str, Any]],
|
rows: list[dict[str, Any]],
|
||||||
*,
|
*,
|
||||||
|
local_as: str,
|
||||||
afi: str,
|
afi: str,
|
||||||
vrf: str,
|
vrf: str,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
|
|
@ -123,6 +135,7 @@ def _map_fsm_rows(
|
||||||
state_raw = row_get(r, "STATE_PFX", "STATE", "state", "pfx_rcd")
|
state_raw = row_get(r, "STATE_PFX", "STATE", "state", "pfx_rcd")
|
||||||
out.append(
|
out.append(
|
||||||
_peer_row(
|
_peer_row(
|
||||||
|
local_as=local_as,
|
||||||
afi=afi,
|
afi=afi,
|
||||||
vrf=vrf,
|
vrf=vrf,
|
||||||
nei=nei,
|
nei=nei,
|
||||||
|
|
@ -140,6 +153,7 @@ def _map_fsm_rows(
|
||||||
def _hand_parse(
|
def _hand_parse(
|
||||||
*,
|
*,
|
||||||
raw_text: str,
|
raw_text: str,
|
||||||
|
local_as: str = "",
|
||||||
afi: str = "unknown",
|
afi: str = "unknown",
|
||||||
vrf: str = "",
|
vrf: str = "",
|
||||||
**_kw: Any,
|
**_kw: Any,
|
||||||
|
|
@ -165,6 +179,7 @@ def _hand_parse(
|
||||||
pending = ""
|
pending = ""
|
||||||
out.append(
|
out.append(
|
||||||
_peer_row(
|
_peer_row(
|
||||||
|
local_as=local_as,
|
||||||
afi=afi,
|
afi=afi,
|
||||||
vrf=vrf,
|
vrf=vrf,
|
||||||
nei=nei,
|
nei=nei,
|
||||||
|
|
@ -190,6 +205,7 @@ def _hand_parse(
|
||||||
seen.add(nei)
|
seen.add(nei)
|
||||||
out.append(
|
out.append(
|
||||||
_peer_row(
|
_peer_row(
|
||||||
|
local_as=local_as,
|
||||||
afi=afi,
|
afi=afi,
|
||||||
vrf=vrf,
|
vrf=vrf,
|
||||||
nei=nei,
|
nei=nei,
|
||||||
|
|
@ -213,6 +229,7 @@ def normalize_bgp_peer(
|
||||||
command: str = "",
|
command: str = "",
|
||||||
params: dict[str, str] | None = None,
|
params: dict[str, str] | None = None,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
|
local_as = _detect_local_as(command, params)
|
||||||
afi = _detect_bgp_afi(command, params)
|
afi = _detect_bgp_afi(command, params)
|
||||||
vrf = _detect_vrf(command, params)
|
vrf = _detect_vrf(command, params)
|
||||||
tables = dict(fsm_tables or {})
|
tables = dict(fsm_tables or {})
|
||||||
|
|
@ -229,10 +246,10 @@ def normalize_bgp_peer(
|
||||||
)
|
)
|
||||||
|
|
||||||
def _map(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
def _map(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||||
return _map_fsm_rows(rows, afi=afi, vrf=vrf)
|
return _map_fsm_rows(rows, local_as=local_as, afi=afi, vrf=vrf)
|
||||||
|
|
||||||
def _hand(*, raw_text: str, **kw: Any) -> list[dict[str, Any]]:
|
def _hand(*, raw_text: str, **kw: Any) -> list[dict[str, Any]]:
|
||||||
return _hand_parse(raw_text=raw_text, afi=afi, vrf=vrf, **kw)
|
return _hand_parse(raw_text=raw_text, local_as=local_as, afi=afi, vrf=vrf, **kw)
|
||||||
|
|
||||||
return prefer_fsm(tables, RULE_KEYS, _map, _hand, raw_text=raw_text)
|
return prefer_fsm(tables, RULE_KEYS, _map, _hand, raw_text=raw_text)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from typing import Any, Mapping
|
||||||
from ....lldp_shared import resolve_vendor_key
|
from ....lldp_shared import resolve_vendor_key
|
||||||
from ....ntc_parse import apply_rules, resolve_cli_platform, row_get
|
from ....ntc_parse import apply_rules, resolve_cli_platform, row_get
|
||||||
from ..common.pipeline import prefer_fsm
|
from ..common.pipeline import prefer_fsm
|
||||||
from .bgp_peer import _detect_bgp_afi, _detect_vrf
|
from .bgp_peer import _detect_bgp_afi, _detect_local_as, _detect_vrf
|
||||||
|
|
||||||
RULE_KEYS = ("zte_zxros_show_bgp_neighbor_routes",)
|
RULE_KEYS = ("zte_zxros_show_bgp_neighbor_routes",)
|
||||||
|
|
||||||
|
|
@ -152,6 +152,7 @@ def _emit_route(
|
||||||
out: list[dict[str, Any]],
|
out: list[dict[str, Any]],
|
||||||
seen: set[str],
|
seen: set[str],
|
||||||
*,
|
*,
|
||||||
|
local_as: str,
|
||||||
afi: str,
|
afi: str,
|
||||||
vrf: str,
|
vrf: str,
|
||||||
neighbor: str,
|
neighbor: str,
|
||||||
|
|
@ -172,6 +173,7 @@ def _emit_route(
|
||||||
metric, loc, tag, path = _split_rest(rest, path_continuation=path_continuation)
|
metric, loc, tag, path = _split_rest(rest, path_continuation=path_continuation)
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
|
"local_as": local_as[:16],
|
||||||
"afi": afi[:32],
|
"afi": afi[:32],
|
||||||
"vrf": vrf[:128],
|
"vrf": vrf[:128],
|
||||||
"neighbor": neighbor[:128],
|
"neighbor": neighbor[:128],
|
||||||
|
|
@ -193,6 +195,7 @@ def _emit_route(
|
||||||
def _map_fsm_rows(
|
def _map_fsm_rows(
|
||||||
rows: list[dict[str, Any]],
|
rows: list[dict[str, Any]],
|
||||||
*,
|
*,
|
||||||
|
local_as: str,
|
||||||
afi: str,
|
afi: str,
|
||||||
vrf: str,
|
vrf: str,
|
||||||
neighbor: str,
|
neighbor: str,
|
||||||
|
|
@ -214,6 +217,7 @@ def _map_fsm_rows(
|
||||||
path = row_get(r, "PATH", "path")
|
path = row_get(r, "PATH", "path")
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
|
"local_as": local_as[:16],
|
||||||
"afi": afi[:32],
|
"afi": afi[:32],
|
||||||
"vrf": vrf[:128],
|
"vrf": vrf[:128],
|
||||||
"neighbor": neighbor[:128],
|
"neighbor": neighbor[:128],
|
||||||
|
|
@ -236,6 +240,7 @@ def _map_fsm_rows(
|
||||||
def _hand_parse(
|
def _hand_parse(
|
||||||
*,
|
*,
|
||||||
raw_text: str,
|
raw_text: str,
|
||||||
|
local_as: str = "",
|
||||||
afi: str = "unknown",
|
afi: str = "unknown",
|
||||||
vrf: str = "",
|
vrf: str = "",
|
||||||
neighbor: str = "",
|
neighbor: str = "",
|
||||||
|
|
@ -256,6 +261,7 @@ def _hand_parse(
|
||||||
_emit_route(
|
_emit_route(
|
||||||
out,
|
out,
|
||||||
seen,
|
seen,
|
||||||
|
local_as=local_as,
|
||||||
afi=afi,
|
afi=afi,
|
||||||
vrf=vrf,
|
vrf=vrf,
|
||||||
neighbor=neighbor,
|
neighbor=neighbor,
|
||||||
|
|
@ -300,6 +306,7 @@ def _hand_parse(
|
||||||
_emit_route(
|
_emit_route(
|
||||||
out,
|
out,
|
||||||
seen,
|
seen,
|
||||||
|
local_as=local_as,
|
||||||
afi=afi,
|
afi=afi,
|
||||||
vrf=vrf,
|
vrf=vrf,
|
||||||
neighbor=neighbor,
|
neighbor=neighbor,
|
||||||
|
|
@ -333,6 +340,7 @@ def normalize_bgp_route(
|
||||||
command: str = "",
|
command: str = "",
|
||||||
params: dict[str, str] | None = None,
|
params: dict[str, str] | None = None,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
|
local_as = _detect_local_as(command, params)
|
||||||
afi = _detect_bgp_afi(command, params)
|
afi = _detect_bgp_afi(command, params)
|
||||||
vrf = _detect_vrf(command, params)
|
vrf = _detect_vrf(command, params)
|
||||||
neighbor = _detect_neighbor(command, params)
|
neighbor = _detect_neighbor(command, params)
|
||||||
|
|
@ -355,12 +363,18 @@ def normalize_bgp_route(
|
||||||
|
|
||||||
def _map(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
def _map(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||||
return _map_fsm_rows(
|
return _map_fsm_rows(
|
||||||
rows, afi=afi, vrf=vrf, neighbor=neighbor, direction=direction
|
rows,
|
||||||
|
local_as=local_as,
|
||||||
|
afi=afi,
|
||||||
|
vrf=vrf,
|
||||||
|
neighbor=neighbor,
|
||||||
|
direction=direction,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _hand(*, raw_text: str, **kw: Any) -> list[dict[str, Any]]:
|
def _hand(*, raw_text: str, **kw: Any) -> list[dict[str, Any]]:
|
||||||
return _hand_parse(
|
return _hand_parse(
|
||||||
raw_text=raw_text,
|
raw_text=raw_text,
|
||||||
|
local_as=local_as,
|
||||||
afi=afi,
|
afi=afi,
|
||||||
vrf=vrf,
|
vrf=vrf,
|
||||||
neighbor=neighbor,
|
neighbor=neighbor,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
"""ZTE config intent: ``show running-config bgp`` / MIM ``!<bgp>``.
|
"""ZTE config intent: ``show running-config bgp`` / MIM ``!<bgp>``.
|
||||||
|
|
||||||
Emits one row per ``(afi, vrf, neighbor|peer_group)`` activation under
|
Emits one row per ``(local_as, afi, vrf, neighbor|peer_group)`` activation under
|
||||||
address-family. IP literals go in ``neighbor``; non-IP names (peer-groups)
|
address-family. IP literals go in ``neighbor``; non-IP names (peer-groups)
|
||||||
go in ``peer_group``. Never captures password / secret lines.
|
go in ``peer_group``. Never captures password / secret lines.
|
||||||
|
|
||||||
|
Supports **multiple** ``router bgp <asn>`` instances: each peer is scoped to
|
||||||
|
its enclosing local AS (meta / peer-group expand do not cross AS boundaries).
|
||||||
|
|
||||||
For **global** (non-VRF) address-families, a peer-group ``activate`` is also
|
For **global** (non-VRF) address-families, a peer-group ``activate`` is also
|
||||||
expanded into one row per global member ``neighbor <ip> peer-group <name>``
|
expanded into one row per global member ``neighbor <ip> peer-group <name>``
|
||||||
so discover/bind sees the real Neighbor IPs (direct activates ∪ group
|
so discover/bind sees the real Neighbor IPs (direct activates ∪ group
|
||||||
|
|
@ -23,6 +26,7 @@ from .config_common import extract_mim_section, is_secret_line
|
||||||
|
|
||||||
RULE_KEYS: tuple[str, ...] = ()
|
RULE_KEYS: tuple[str, ...] = ()
|
||||||
|
|
||||||
|
_ROUTER_BGP_RE = re.compile(r"^\s*router\s+bgp\s+(\S+)\s*$", re.I)
|
||||||
_NEI_REMOTE_RE = re.compile(r"^\s*neighbor\s+(\S+)\s+remote-as\s+(\S+)\s*$", re.I)
|
_NEI_REMOTE_RE = re.compile(r"^\s*neighbor\s+(\S+)\s+remote-as\s+(\S+)\s*$", re.I)
|
||||||
_NEI_UPD_RE = re.compile(r"^\s*neighbor\s+(\S+)\s+update-source\s+(\S+)\s*$", re.I)
|
_NEI_UPD_RE = re.compile(r"^\s*neighbor\s+(\S+)\s+update-source\s+(\S+)\s*$", re.I)
|
||||||
_NEI_PG_RE = re.compile(r"^\s*neighbor\s+(\S+)\s+peer-group(?:\s+(\S+))?\s*$", re.I)
|
_NEI_PG_RE = re.compile(r"^\s*neighbor\s+(\S+)\s+peer-group(?:\s+(\S+))?\s*$", re.I)
|
||||||
|
|
@ -32,9 +36,18 @@ _NEI_RM_RE = re.compile(
|
||||||
_NEI_ACT_RE = re.compile(
|
_NEI_ACT_RE = re.compile(
|
||||||
r"^\s*neighbor\s+(\S+)\s+activate(?:\s+(disable))?\s*$", re.I
|
r"^\s*neighbor\s+(\S+)\s+activate(?:\s+(disable))?\s*$", re.I
|
||||||
)
|
)
|
||||||
|
_AF_V4_VRF_RE = re.compile(r"^\s*address-family\s+ipv4\s+vrf\s+(\S+)\s*$", re.I)
|
||||||
|
_AF_V6_VRF_RE = re.compile(r"^\s*address-family\s+ipv6\s+vrf\s+(\S+)\s*$", re.I)
|
||||||
|
_AF_L2VPN_RE = re.compile(r"^\s*address-family\s+l2vpn\s+(\S+)\s*$", re.I)
|
||||||
|
# Multi-word AFs first (e.g. ``ipv6 sr-policy``); then single token.
|
||||||
|
_AF_MULTI_RE = re.compile(
|
||||||
|
r"^\s*address-family\s+(ipv6\s+sr-policy|ipv4\s+sr-policy)\s*$", re.I
|
||||||
|
)
|
||||||
|
_AF_SINGLE_RE = re.compile(r"^\s*address-family\s+(\S+)\s*$", re.I)
|
||||||
_IPV4_RE = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}$")
|
_IPV4_RE = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}$")
|
||||||
|
|
||||||
_AfKey = tuple[str, str, str] # (afi, vrf, token)
|
_AfKey = tuple[str, str, str, str] # (local_as, afi, vrf, token)
|
||||||
|
_MetaKey = tuple[str, str] # (local_as, token)
|
||||||
|
|
||||||
|
|
||||||
def _is_ip_neighbor(token: str) -> bool:
|
def _is_ip_neighbor(token: str) -> bool:
|
||||||
|
|
@ -68,14 +81,15 @@ def _split_neighbor_cols(token: str, info: Mapping[str, str]) -> tuple[str, str]
|
||||||
def _merge_info(
|
def _merge_info(
|
||||||
token: str,
|
token: str,
|
||||||
*,
|
*,
|
||||||
|
local_as: str,
|
||||||
afi: str,
|
afi: str,
|
||||||
vrf: str,
|
vrf: str,
|
||||||
meta: Mapping[str, dict[str, str]],
|
meta: Mapping[_MetaKey, dict[str, str]],
|
||||||
af_rm: Mapping[_AfKey, dict[str, str]],
|
af_rm: Mapping[_AfKey, dict[str, str]],
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""Global neighbor meta + AF-scoped route-maps (AF wins on conflict)."""
|
"""Global neighbor meta + AF-scoped route-maps (AF wins on conflict)."""
|
||||||
info = dict(meta.get(token) or {})
|
info = dict(meta.get((local_as, token)) or {})
|
||||||
scoped = af_rm.get((afi, vrf, token)) or {}
|
scoped = af_rm.get((local_as, afi, vrf, token)) or {}
|
||||||
for key in ("route_map_in", "route_map_out"):
|
for key in ("route_map_in", "route_map_out"):
|
||||||
if scoped.get(key):
|
if scoped.get(key):
|
||||||
info[key] = scoped[key]
|
info[key] = scoped[key]
|
||||||
|
|
@ -84,6 +98,7 @@ def _merge_info(
|
||||||
|
|
||||||
def _row(
|
def _row(
|
||||||
*,
|
*,
|
||||||
|
local_as: str,
|
||||||
afi: str,
|
afi: str,
|
||||||
vrf: str,
|
vrf: str,
|
||||||
token: str,
|
token: str,
|
||||||
|
|
@ -92,6 +107,7 @@ def _row(
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
neighbor, peer_group = _split_neighbor_cols(token, info)
|
neighbor, peer_group = _split_neighbor_cols(token, info)
|
||||||
return {
|
return {
|
||||||
|
"local_as": str(local_as or "")[:16],
|
||||||
"afi": afi[:32],
|
"afi": afi[:32],
|
||||||
"vrf": vrf[:128],
|
"vrf": vrf[:128],
|
||||||
"neighbor": neighbor[:128],
|
"neighbor": neighbor[:128],
|
||||||
|
|
@ -104,6 +120,12 @@ def _row(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_afi_token(raw: str) -> str:
|
||||||
|
"""Collapse multi-word AF names to a stable token (``ipv6 sr-policy`` → ``ipv6-sr-policy``)."""
|
||||||
|
parts = [p for p in re.split(r"\s+", str(raw or "").strip().lower()) if p]
|
||||||
|
return "-".join(parts)
|
||||||
|
|
||||||
|
|
||||||
def normalize_config_bgp_peer(
|
def normalize_config_bgp_peer(
|
||||||
*,
|
*,
|
||||||
raw_text: str,
|
raw_text: str,
|
||||||
|
|
@ -115,96 +137,133 @@ def normalize_config_bgp_peer(
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
_ = (fsm_tables, vendor, device_type, command, params)
|
_ = (fsm_tables, vendor, device_type, command, params)
|
||||||
body = extract_mim_section(raw_text, "bgp")
|
body = extract_mim_section(raw_text, "bgp")
|
||||||
meta: dict[str, dict[str, str]] = {}
|
meta: dict[_MetaKey, dict[str, str]] = {}
|
||||||
af_rm: dict[_AfKey, dict[str, str]] = {}
|
af_rm: dict[_AfKey, dict[str, str]] = {}
|
||||||
|
local_as = ""
|
||||||
afi = ""
|
afi = ""
|
||||||
vrf = ""
|
vrf = ""
|
||||||
activations: list[tuple[str, str, str, str]] = []
|
activations: list[tuple[str, str, str, str, str]] = []
|
||||||
# (afi, vrf, token, activate)
|
# (local_as, afi, vrf, token, activate)
|
||||||
|
|
||||||
|
def _meta(token: str) -> dict[str, str]:
|
||||||
|
key = (local_as, token)
|
||||||
|
slot = meta.get(key)
|
||||||
|
if slot is None:
|
||||||
|
slot = {}
|
||||||
|
meta[key] = slot
|
||||||
|
return slot
|
||||||
|
|
||||||
for raw in body.splitlines():
|
for raw in body.splitlines():
|
||||||
line = raw.rstrip()
|
line = raw.rstrip()
|
||||||
if is_secret_line(line):
|
if is_secret_line(line):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
m = _ROUTER_BGP_RE.match(line)
|
||||||
|
if m:
|
||||||
|
local_as = m.group(1).strip()
|
||||||
|
afi, vrf = "", ""
|
||||||
|
continue
|
||||||
|
|
||||||
|
# End of ``router bgp`` block (column-0 ``$``).
|
||||||
|
if re.match(r"^\$\s*$", line):
|
||||||
|
afi, vrf = "", ""
|
||||||
|
local_as = ""
|
||||||
|
continue
|
||||||
|
|
||||||
|
# End of address-family (exactly two-space indent ``$``).
|
||||||
|
if re.match(r"^ \$\s*$", line) and afi:
|
||||||
|
afi, vrf = "", ""
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not local_as:
|
||||||
|
continue
|
||||||
|
|
||||||
m = _NEI_REMOTE_RE.match(line)
|
m = _NEI_REMOTE_RE.match(line)
|
||||||
if m:
|
if m:
|
||||||
nei = m.group(1).strip()
|
nei = m.group(1).strip()
|
||||||
meta.setdefault(nei, {})
|
_meta(nei)["remote_as"] = m.group(2).strip()
|
||||||
meta[nei]["remote_as"] = m.group(2).strip()
|
|
||||||
continue
|
continue
|
||||||
m = _NEI_UPD_RE.match(line)
|
m = _NEI_UPD_RE.match(line)
|
||||||
if m:
|
if m:
|
||||||
nei = m.group(1).strip()
|
nei = m.group(1).strip()
|
||||||
meta.setdefault(nei, {})
|
_meta(nei)["update_source"] = m.group(2).strip()
|
||||||
meta[nei]["update_source"] = m.group(2).strip()
|
|
||||||
continue
|
continue
|
||||||
m = _NEI_PG_RE.match(line)
|
m = _NEI_PG_RE.match(line)
|
||||||
if m:
|
if m:
|
||||||
nei = m.group(1).strip()
|
nei = m.group(1).strip()
|
||||||
meta.setdefault(nei, {})
|
slot = _meta(nei)
|
||||||
group = (m.group(2) or "").strip()
|
group = (m.group(2) or "").strip()
|
||||||
if group:
|
if group:
|
||||||
# neighbor <ip> peer-group <name>
|
# neighbor <ip> peer-group <name>
|
||||||
meta[nei]["peer_group"] = group
|
slot["peer_group"] = group
|
||||||
else:
|
else:
|
||||||
# neighbor <name> peer-group → peer-group definition
|
# neighbor <name> peer-group → peer-group definition
|
||||||
meta[nei]["is_group"] = "1"
|
slot["is_group"] = "1"
|
||||||
continue
|
continue
|
||||||
m = _NEI_RM_RE.match(line)
|
m = _NEI_RM_RE.match(line)
|
||||||
if m and not afi:
|
if m and not afi:
|
||||||
nei = m.group(1).strip()
|
nei = m.group(1).strip()
|
||||||
meta.setdefault(nei, {})
|
|
||||||
direction = m.group(3).lower()
|
direction = m.group(3).lower()
|
||||||
key = "route_map_in" if direction == "in" else "route_map_out"
|
key = "route_map_in" if direction == "in" else "route_map_out"
|
||||||
meta[nei][key] = m.group(2).strip()
|
_meta(nei)[key] = m.group(2).strip()
|
||||||
continue
|
continue
|
||||||
m = re.match(r"^\s*address-family\s+ipv4\s+vrf\s+(\S+)\s*$", line, re.I)
|
m = _AF_V4_VRF_RE.match(line)
|
||||||
if m:
|
if m:
|
||||||
afi, vrf = "ipv4", m.group(1).strip()
|
afi, vrf = "ipv4", m.group(1).strip()
|
||||||
continue
|
continue
|
||||||
m = re.match(r"^\s*address-family\s+ipv6\s+vrf\s+(\S+)\s*$", line, re.I)
|
m = _AF_V6_VRF_RE.match(line)
|
||||||
if m:
|
if m:
|
||||||
afi, vrf = "ipv6", m.group(1).strip()
|
afi, vrf = "ipv6", m.group(1).strip()
|
||||||
continue
|
continue
|
||||||
m = re.match(r"^\s*address-family\s+l2vpn\s+(\S+)\s*$", line, re.I)
|
m = _AF_L2VPN_RE.match(line)
|
||||||
if m:
|
if m:
|
||||||
afi, vrf = f"l2vpn-{m.group(1).lower()}", ""
|
afi, vrf = f"l2vpn-{m.group(1).lower()}", ""
|
||||||
continue
|
continue
|
||||||
m = re.match(r"^\s*address-family\s+(\S+)\s*$", line, re.I)
|
m = _AF_MULTI_RE.match(line)
|
||||||
|
if m:
|
||||||
|
afi, vrf = _normalize_afi_token(m.group(1)), ""
|
||||||
|
continue
|
||||||
|
m = _AF_SINGLE_RE.match(line)
|
||||||
if m:
|
if m:
|
||||||
afi, vrf = m.group(1).lower(), ""
|
afi, vrf = m.group(1).lower(), ""
|
||||||
continue
|
continue
|
||||||
if re.match(r"^\s*\$\s*$", line) and afi:
|
|
||||||
if line.startswith(" ") and not line.startswith(" "):
|
|
||||||
afi, vrf = "", ""
|
|
||||||
continue
|
|
||||||
m = _NEI_ACT_RE.match(line)
|
m = _NEI_ACT_RE.match(line)
|
||||||
if m and afi:
|
if m and afi:
|
||||||
nei = m.group(1).strip()
|
nei = m.group(1).strip()
|
||||||
act = "disable" if m.group(2) else "enable"
|
act = "disable" if m.group(2) else "enable"
|
||||||
activations.append((afi, vrf, nei, act))
|
activations.append((local_as, afi, vrf, nei, act))
|
||||||
continue
|
continue
|
||||||
m = _NEI_RM_RE.match(line)
|
m = _NEI_RM_RE.match(line)
|
||||||
if m and afi:
|
if m and afi:
|
||||||
nei = m.group(1).strip()
|
nei = m.group(1).strip()
|
||||||
direction = m.group(3).lower()
|
direction = m.group(3).lower()
|
||||||
rk = "route_map_in" if direction == "in" else "route_map_out"
|
rk = "route_map_in" if direction == "in" else "route_map_out"
|
||||||
slot = af_rm.setdefault((afi, vrf, nei), {})
|
slot = af_rm.setdefault((local_as, afi, vrf, nei), {})
|
||||||
slot[rk] = m.group(2).strip()
|
slot[rk] = m.group(2).strip()
|
||||||
|
|
||||||
out: list[dict[str, Any]] = []
|
out: list[dict[str, Any]] = []
|
||||||
seen: set[tuple[str, str, str, str]] = set()
|
seen: set[tuple[str, str, str, str, str]] = set()
|
||||||
|
|
||||||
def _append(row: dict[str, Any]) -> None:
|
def _append(row: dict[str, Any]) -> None:
|
||||||
key = (row["afi"], row["vrf"], row["neighbor"], row["peer_group"])
|
key = (
|
||||||
|
row["local_as"],
|
||||||
|
row["afi"],
|
||||||
|
row["vrf"],
|
||||||
|
row["neighbor"],
|
||||||
|
row["peer_group"],
|
||||||
|
)
|
||||||
if key in seen:
|
if key in seen:
|
||||||
return
|
return
|
||||||
seen.add(key)
|
seen.add(key)
|
||||||
out.append(row)
|
out.append(row)
|
||||||
|
|
||||||
for afi_s, vrf_s, token, act in activations:
|
for las, afi_s, vrf_s, token, act in activations:
|
||||||
info = _merge_info(token, afi=afi_s, vrf=vrf_s, meta=meta, af_rm=af_rm)
|
info = _merge_info(
|
||||||
row = _row(afi=afi_s, vrf=vrf_s, token=token, act=act, info=info)
|
token, local_as=las, afi=afi_s, vrf=vrf_s, meta=meta, af_rm=af_rm
|
||||||
|
)
|
||||||
|
row = _row(
|
||||||
|
local_as=las, afi=afi_s, vrf=vrf_s, token=token, act=act, info=info
|
||||||
|
)
|
||||||
_append(row)
|
_append(row)
|
||||||
|
|
||||||
# Global AF: peer-group activate → expand to member Neighbor IPs.
|
# Global AF: peer-group activate → expand to member Neighbor IPs.
|
||||||
|
|
@ -214,27 +273,45 @@ def normalize_config_bgp_peer(
|
||||||
pg = str(row.get("peer_group") or "").strip()
|
pg = str(row.get("peer_group") or "").strip()
|
||||||
if row.get("neighbor") or not pg:
|
if row.get("neighbor") or not pg:
|
||||||
continue
|
continue
|
||||||
for member, minfo in meta.items():
|
for (m_as, member), minfo in meta.items():
|
||||||
|
if m_as != las:
|
||||||
|
continue
|
||||||
if not _is_ip_neighbor(member):
|
if not _is_ip_neighbor(member):
|
||||||
continue
|
continue
|
||||||
if str(minfo.get("peer_group") or "").strip() != pg:
|
if str(minfo.get("peer_group") or "").strip() != pg:
|
||||||
continue
|
continue
|
||||||
m_info = _merge_info(
|
m_info = _merge_info(
|
||||||
member, afi=afi_s, vrf=vrf_s, meta=meta, af_rm=af_rm
|
member, local_as=las, afi=afi_s, vrf=vrf_s, meta=meta, af_rm=af_rm
|
||||||
)
|
)
|
||||||
|
# AF route-maps on the peer-group name apply to expanded members
|
||||||
|
# unless the member already has an AF-specific map.
|
||||||
|
pg_info = _merge_info(
|
||||||
|
pg, local_as=las, afi=afi_s, vrf=vrf_s, meta=meta, af_rm=af_rm
|
||||||
|
)
|
||||||
|
for key in ("route_map_in", "route_map_out"):
|
||||||
|
if pg_info.get(key) and not m_info.get(key):
|
||||||
|
m_info[key] = pg_info[key]
|
||||||
m_row = _row(
|
m_row = _row(
|
||||||
afi=afi_s, vrf=vrf_s, token=member, act=act, info=m_info
|
local_as=las,
|
||||||
|
afi=afi_s,
|
||||||
|
vrf=vrf_s,
|
||||||
|
token=member,
|
||||||
|
act=act,
|
||||||
|
info=m_info,
|
||||||
)
|
)
|
||||||
m_row["peer_group"] = pg[:64]
|
m_row["peer_group"] = pg[:64]
|
||||||
_append(m_row)
|
_append(m_row)
|
||||||
|
|
||||||
# Global peers with remote-as but no AF activate
|
# Peers with remote-as but no AF activate (scoped per local AS)
|
||||||
for token, info in meta.items():
|
activated_tokens = {(las, n) for las, _, _, n, _ in activations}
|
||||||
|
for (las, token), info in meta.items():
|
||||||
if not info.get("remote_as"):
|
if not info.get("remote_as"):
|
||||||
continue
|
continue
|
||||||
if any(n == token for _, _, n, _ in activations):
|
if (las, token) in activated_tokens:
|
||||||
continue
|
continue
|
||||||
row = _row(afi="global", vrf="", token=token, act="", info=info)
|
row = _row(
|
||||||
|
local_as=las, afi="global", vrf="", token=token, act="", info=info
|
||||||
|
)
|
||||||
_append(row)
|
_append(row)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from typing import Any, Mapping
|
||||||
from ....lldp_shared import resolve_vendor_key
|
from ....lldp_shared import resolve_vendor_key
|
||||||
from ....ntc_parse import apply_rules, resolve_cli_platform, row_get
|
from ....ntc_parse import apply_rules, resolve_cli_platform, row_get
|
||||||
from ..common.pipeline import prefer_fsm
|
from ..common.pipeline import prefer_fsm
|
||||||
|
from .bgp_peer import _detect_local_as
|
||||||
|
|
||||||
RULE_KEYS = ("zte_zxros_show_bgp_evpn_mac",)
|
RULE_KEYS = ("zte_zxros_show_bgp_evpn_mac",)
|
||||||
|
|
||||||
|
|
@ -17,7 +18,11 @@ _ROUTE_RE = re.compile(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _map_fsm_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
def _map_fsm_rows(
|
||||||
|
rows: list[dict[str, Any]],
|
||||||
|
*,
|
||||||
|
local_as: str,
|
||||||
|
) -> list[dict[str, Any]]:
|
||||||
out: list[dict[str, Any]] = []
|
out: list[dict[str, Any]] = []
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
for r in rows:
|
for r in rows:
|
||||||
|
|
@ -27,6 +32,7 @@ def _map_fsm_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||||
seen.add(net)
|
seen.add(net)
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
|
"local_as": local_as[:16],
|
||||||
"network": net[:256],
|
"network": net[:256],
|
||||||
"next_hop": row_get(r, "NEXT_HOP", "next_hop")[:128],
|
"next_hop": row_get(r, "NEXT_HOP", "next_hop")[:128],
|
||||||
"metric": row_get(r, "METRIC", "metric")[:32],
|
"metric": row_get(r, "METRIC", "metric")[:32],
|
||||||
|
|
@ -39,7 +45,7 @@ def _map_fsm_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def _hand_parse(*, raw_text: str, **_kw: Any) -> list[dict[str, Any]]:
|
def _hand_parse(*, raw_text: str, local_as: str = "", **_kw: Any) -> list[dict[str, Any]]:
|
||||||
out: list[dict[str, Any]] = []
|
out: list[dict[str, Any]] = []
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
for raw in str(raw_text or "").splitlines():
|
for raw in str(raw_text or "").splitlines():
|
||||||
|
|
@ -79,6 +85,7 @@ def _hand_parse(*, raw_text: str, **_kw: Any) -> list[dict[str, Any]]:
|
||||||
path_parts.append(p)
|
path_parts.append(p)
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
|
"local_as": local_as[:16],
|
||||||
"network": net[:256],
|
"network": net[:256],
|
||||||
"next_hop": m.group("nh")[:128],
|
"next_hop": m.group("nh")[:128],
|
||||||
"metric": metric[:32],
|
"metric": metric[:32],
|
||||||
|
|
@ -100,7 +107,7 @@ def normalize_evpn_mac(
|
||||||
command: str = "",
|
command: str = "",
|
||||||
params: dict[str, str] | None = None,
|
params: dict[str, str] | None = None,
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
_ = params
|
local_as = _detect_local_as(command, params)
|
||||||
tables = dict(fsm_tables or {})
|
tables = dict(fsm_tables or {})
|
||||||
if not any(tables.get(k) for k in RULE_KEYS):
|
if not any(tables.get(k) for k in RULE_KEYS):
|
||||||
platform = resolve_cli_platform(
|
platform = resolve_cli_platform(
|
||||||
|
|
@ -113,7 +120,14 @@ def normalize_evpn_mac(
|
||||||
tables = apply_rules(
|
tables = apply_rules(
|
||||||
platform=platform, text=raw_text, rule_keys=RULE_KEYS, command=cmd
|
platform=platform, text=raw_text, rule_keys=RULE_KEYS, command=cmd
|
||||||
)
|
)
|
||||||
return prefer_fsm(tables, RULE_KEYS, _map_fsm_rows, _hand_parse, raw_text=raw_text)
|
|
||||||
|
def _map(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||||
|
return _map_fsm_rows(rows, local_as=local_as)
|
||||||
|
|
||||||
|
def _hand(*, raw_text: str, **kw: Any) -> list[dict[str, Any]]:
|
||||||
|
return _hand_parse(raw_text=raw_text, local_as=local_as, **kw)
|
||||||
|
|
||||||
|
return prefer_fsm(tables, RULE_KEYS, _map, _hand, raw_text=raw_text)
|
||||||
|
|
||||||
|
|
||||||
normalize_evpn_mac.RULE_KEYS = RULE_KEYS
|
normalize_evpn_mac.RULE_KEYS = RULE_KEYS
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,7 @@ _ND6_FIELDS: list[FieldDef] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
_BGP_PEER_FIELDS: list[FieldDef] = [
|
_BGP_PEER_FIELDS: list[FieldDef] = [
|
||||||
|
FieldDef("local_as", length=16, indexed=True, is_key=True, display_name="Local AS", from_command_param=True),
|
||||||
FieldDef("afi", length=32, indexed=True, is_key=True, display_name="AFI", from_command_param=True),
|
FieldDef("afi", length=32, indexed=True, is_key=True, display_name="AFI", from_command_param=True),
|
||||||
FieldDef("vrf", length=128, indexed=True, is_key=True, display_name="VRF", from_command_param=True),
|
FieldDef("vrf", length=128, indexed=True, is_key=True, display_name="VRF", from_command_param=True),
|
||||||
FieldDef("neighbor", length=128, indexed=True, is_key=True, display_name="Neighbor"),
|
FieldDef("neighbor", length=128, indexed=True, is_key=True, display_name="Neighbor"),
|
||||||
|
|
@ -285,12 +286,11 @@ _BGP_PEER_FIELDS: list[FieldDef] = [
|
||||||
FieldDef("msg_rcvd", length=32, role="meta", display_name="MsgRcvd"),
|
FieldDef("msg_rcvd", length=32, role="meta", display_name="MsgRcvd"),
|
||||||
FieldDef("msg_send", length=32, role="meta", display_name="MsgSend"),
|
FieldDef("msg_send", length=32, role="meta", display_name="MsgSend"),
|
||||||
FieldDef("up_down", length=32, role="meta", display_name="Up/Down"),
|
FieldDef("up_down", length=32, role="meta", display_name="Up/Down"),
|
||||||
# From config_vrf enrich (VRF summary aux).
|
|
||||||
FieldDef("rd", length=64, role="meta", display_name="RD"),
|
FieldDef("rd", length=64, role="meta", display_name="RD"),
|
||||||
FieldDef("address_families", length=64, role="meta", display_name="AF"),
|
FieldDef("address_families", length=64, role="meta", display_name="AF"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# BGP VRF summary still binds VRF from config VRF intent.
|
# BGP VRF summary still binds VRF from config VRF intent (ip/ipv6 route profiles).
|
||||||
_VRF_PLACEHOLDER_IPV4 = PlaceholderDef(
|
_VRF_PLACEHOLDER_IPV4 = PlaceholderDef(
|
||||||
name="vrf",
|
name="vrf",
|
||||||
schema_field="vrf",
|
schema_field="vrf",
|
||||||
|
|
@ -315,11 +315,24 @@ _VRF_PLACEHOLDER_IPV6 = PlaceholderDef(
|
||||||
discover_filter_contains="ipv6",
|
discover_filter_contains="ipv6",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Local AS from ``router bgp <asn>`` (config_bgp_peer). Optional → expand all processes.
|
||||||
|
_BGP_LOCAL_AS = PlaceholderDef(
|
||||||
|
name="local_as",
|
||||||
|
schema_field="local_as",
|
||||||
|
required=False,
|
||||||
|
bind_mode="discover_select",
|
||||||
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
|
discover_value_field="local_as",
|
||||||
|
discover_label_field="local_as",
|
||||||
|
discover_require_nonempty="local_as",
|
||||||
|
)
|
||||||
|
|
||||||
# BGP neighbor in/out: discover peers from Config BGP Peer Intent, filtered by AF.
|
# BGP neighbor in/out: discover peers from Config BGP Peer Intent, filtered by AF.
|
||||||
|
# required=False with local_as → unbound collect expands all (local_as, neighbor) pairs.
|
||||||
_BGP_NEIGHBOR_VPNV4 = PlaceholderDef(
|
_BGP_NEIGHBOR_VPNV4 = PlaceholderDef(
|
||||||
name="neighbor",
|
name="neighbor",
|
||||||
schema_field="neighbor",
|
schema_field="neighbor",
|
||||||
required=True,
|
required=False,
|
||||||
bind_mode="discover_select",
|
bind_mode="discover_select",
|
||||||
discover_profile_id="zte.config_bgp_peer",
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
discover_value_field="neighbor",
|
discover_value_field="neighbor",
|
||||||
|
|
@ -327,12 +340,13 @@ _BGP_NEIGHBOR_VPNV4 = PlaceholderDef(
|
||||||
discover_filter_field="afi",
|
discover_filter_field="afi",
|
||||||
discover_filter_contains="vpnv4",
|
discover_filter_contains="vpnv4",
|
||||||
discover_require_empty="vrf",
|
discover_require_empty="vrf",
|
||||||
|
discover_require_nonempty="neighbor",
|
||||||
)
|
)
|
||||||
|
|
||||||
_BGP_NEIGHBOR_VPNV6 = PlaceholderDef(
|
_BGP_NEIGHBOR_VPNV6 = PlaceholderDef(
|
||||||
name="neighbor",
|
name="neighbor",
|
||||||
schema_field="neighbor",
|
schema_field="neighbor",
|
||||||
required=True,
|
required=False,
|
||||||
bind_mode="discover_select",
|
bind_mode="discover_select",
|
||||||
discover_profile_id="zte.config_bgp_peer",
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
discover_value_field="neighbor",
|
discover_value_field="neighbor",
|
||||||
|
|
@ -340,12 +354,13 @@ _BGP_NEIGHBOR_VPNV6 = PlaceholderDef(
|
||||||
discover_filter_field="afi",
|
discover_filter_field="afi",
|
||||||
discover_filter_contains="vpnv6",
|
discover_filter_contains="vpnv6",
|
||||||
discover_require_empty="vrf",
|
discover_require_empty="vrf",
|
||||||
|
discover_require_nonempty="neighbor",
|
||||||
)
|
)
|
||||||
|
|
||||||
_BGP_NEIGHBOR_IPV4 = PlaceholderDef(
|
_BGP_NEIGHBOR_IPV4 = PlaceholderDef(
|
||||||
name="neighbor",
|
name="neighbor",
|
||||||
schema_field="neighbor",
|
schema_field="neighbor",
|
||||||
required=True,
|
required=False,
|
||||||
bind_mode="discover_select",
|
bind_mode="discover_select",
|
||||||
discover_profile_id="zte.config_bgp_peer",
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
discover_value_field="neighbor",
|
discover_value_field="neighbor",
|
||||||
|
|
@ -353,12 +368,13 @@ _BGP_NEIGHBOR_IPV4 = PlaceholderDef(
|
||||||
discover_filter_field="afi",
|
discover_filter_field="afi",
|
||||||
discover_filter_contains="ipv4",
|
discover_filter_contains="ipv4",
|
||||||
discover_require_empty="vrf",
|
discover_require_empty="vrf",
|
||||||
|
discover_require_nonempty="neighbor",
|
||||||
)
|
)
|
||||||
|
|
||||||
_BGP_NEIGHBOR_IPV6 = PlaceholderDef(
|
_BGP_NEIGHBOR_IPV6 = PlaceholderDef(
|
||||||
name="neighbor",
|
name="neighbor",
|
||||||
schema_field="neighbor",
|
schema_field="neighbor",
|
||||||
required=True,
|
required=False,
|
||||||
bind_mode="discover_select",
|
bind_mode="discover_select",
|
||||||
discover_profile_id="zte.config_bgp_peer",
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
discover_value_field="neighbor",
|
discover_value_field="neighbor",
|
||||||
|
|
@ -366,13 +382,14 @@ _BGP_NEIGHBOR_IPV6 = PlaceholderDef(
|
||||||
discover_filter_field="afi",
|
discover_filter_field="afi",
|
||||||
discover_filter_contains="ipv6",
|
discover_filter_contains="ipv6",
|
||||||
discover_require_empty="vrf",
|
discover_require_empty="vrf",
|
||||||
|
discover_require_nonempty="neighbor",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Per-VRF CE peers live under address-family ipv4/ipv6 vrf <name>.
|
# Per-VRF CE peers live under address-family ipv4/ipv6 vrf <name>.
|
||||||
_BGP_VRF_PEER_IPV4 = PlaceholderDef(
|
_BGP_VRF_PEER_IPV4 = PlaceholderDef(
|
||||||
name="vrf",
|
name="vrf",
|
||||||
schema_field="vrf",
|
schema_field="vrf",
|
||||||
required=True,
|
required=False,
|
||||||
bind_mode="discover_select",
|
bind_mode="discover_select",
|
||||||
discover_profile_id="zte.config_bgp_peer",
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
discover_value_field="vrf",
|
discover_value_field="vrf",
|
||||||
|
|
@ -385,7 +402,7 @@ _BGP_VRF_PEER_IPV4 = PlaceholderDef(
|
||||||
_BGP_NEIGHBOR_IPV4_VRF = PlaceholderDef(
|
_BGP_NEIGHBOR_IPV4_VRF = PlaceholderDef(
|
||||||
name="neighbor",
|
name="neighbor",
|
||||||
schema_field="neighbor",
|
schema_field="neighbor",
|
||||||
required=True,
|
required=False,
|
||||||
bind_mode="discover_select",
|
bind_mode="discover_select",
|
||||||
discover_profile_id="zte.config_bgp_peer",
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
discover_value_field="neighbor",
|
discover_value_field="neighbor",
|
||||||
|
|
@ -398,7 +415,7 @@ _BGP_NEIGHBOR_IPV4_VRF = PlaceholderDef(
|
||||||
_BGP_VRF_PEER_IPV6 = PlaceholderDef(
|
_BGP_VRF_PEER_IPV6 = PlaceholderDef(
|
||||||
name="vrf",
|
name="vrf",
|
||||||
schema_field="vrf",
|
schema_field="vrf",
|
||||||
required=True,
|
required=False,
|
||||||
bind_mode="discover_select",
|
bind_mode="discover_select",
|
||||||
discover_profile_id="zte.config_bgp_peer",
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
discover_value_field="vrf",
|
discover_value_field="vrf",
|
||||||
|
|
@ -411,7 +428,7 @@ _BGP_VRF_PEER_IPV6 = PlaceholderDef(
|
||||||
_BGP_NEIGHBOR_IPV6_VRF = PlaceholderDef(
|
_BGP_NEIGHBOR_IPV6_VRF = PlaceholderDef(
|
||||||
name="neighbor",
|
name="neighbor",
|
||||||
schema_field="neighbor",
|
schema_field="neighbor",
|
||||||
required=True,
|
required=False,
|
||||||
bind_mode="discover_select",
|
bind_mode="discover_select",
|
||||||
discover_profile_id="zte.config_bgp_peer",
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
discover_value_field="neighbor",
|
discover_value_field="neighbor",
|
||||||
|
|
@ -421,6 +438,49 @@ _BGP_NEIGHBOR_IPV6_VRF = PlaceholderDef(
|
||||||
discover_require_nonempty="vrf",
|
discover_require_nonempty="vrf",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# VRF BGP summary: unique (local_as, vrf) from peer intent (not bare config_vrf).
|
||||||
|
_BGP_VRF_SUMMARY_IPV4 = PlaceholderDef(
|
||||||
|
name="vrf",
|
||||||
|
schema_field="vrf",
|
||||||
|
required=False,
|
||||||
|
bind_mode="discover_select",
|
||||||
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
|
discover_value_field="vrf",
|
||||||
|
discover_label_field="vrf",
|
||||||
|
discover_filter_field="afi",
|
||||||
|
discover_filter_contains="ipv4",
|
||||||
|
discover_require_nonempty="vrf",
|
||||||
|
)
|
||||||
|
|
||||||
|
_BGP_VRF_SUMMARY_IPV6 = PlaceholderDef(
|
||||||
|
name="vrf",
|
||||||
|
schema_field="vrf",
|
||||||
|
required=False,
|
||||||
|
bind_mode="discover_select",
|
||||||
|
discover_profile_id="zte.config_bgp_peer",
|
||||||
|
discover_value_field="vrf",
|
||||||
|
discover_label_field="vrf",
|
||||||
|
discover_filter_field="afi",
|
||||||
|
discover_filter_contains="ipv6",
|
||||||
|
discover_require_nonempty="vrf",
|
||||||
|
)
|
||||||
|
|
||||||
|
_BGP_ENRICH_PEER = [
|
||||||
|
EnrichJoin(
|
||||||
|
from_aux="config_bgp_peer",
|
||||||
|
left_on="local_as,afi,neighbor",
|
||||||
|
right_on="local_as,afi,neighbor",
|
||||||
|
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
_BGP_ENRICH_PEER_VRF = [
|
||||||
|
EnrichJoin(
|
||||||
|
from_aux="config_bgp_peer",
|
||||||
|
left_on="local_as,afi,vrf,neighbor",
|
||||||
|
right_on="local_as,afi,vrf,neighbor",
|
||||||
|
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
||||||
|
),
|
||||||
|
]
|
||||||
_OSPF_FIELDS: list[FieldDef] = [
|
_OSPF_FIELDS: list[FieldDef] = [
|
||||||
FieldDef("process_id", length=32, indexed=True, is_key=True, display_name="Process ID"),
|
FieldDef("process_id", length=32, indexed=True, is_key=True, display_name="Process ID"),
|
||||||
FieldDef("neighbor_id", length=64, indexed=True, is_key=True, display_name="Neighbor ID"),
|
FieldDef("neighbor_id", length=64, indexed=True, is_key=True, display_name="Neighbor ID"),
|
||||||
|
|
@ -456,6 +516,7 @@ _OPTICAL_FIELDS: list[FieldDef] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
_BGP_ROUTE_FIELDS: list[FieldDef] = [
|
_BGP_ROUTE_FIELDS: list[FieldDef] = [
|
||||||
|
FieldDef("local_as", length=16, indexed=True, is_key=True, display_name="Local AS", from_command_param=True),
|
||||||
FieldDef("afi", length=32, indexed=True, is_key=True, display_name="AFI", from_command_param=True),
|
FieldDef("afi", length=32, indexed=True, is_key=True, display_name="AFI", from_command_param=True),
|
||||||
FieldDef("vrf", length=128, indexed=True, is_key=True, display_name="VRF", from_command_param=True),
|
FieldDef("vrf", length=128, indexed=True, is_key=True, display_name="VRF", from_command_param=True),
|
||||||
FieldDef("neighbor", length=128, indexed=True, is_key=True, display_name="Neighbor", from_command_param=True),
|
FieldDef("neighbor", length=128, indexed=True, is_key=True, display_name="Neighbor", from_command_param=True),
|
||||||
|
|
@ -554,6 +615,7 @@ _L2VPN_MAC_FIELDS: list[FieldDef] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
_EVPN_MAC_FIELDS: list[FieldDef] = [
|
_EVPN_MAC_FIELDS: list[FieldDef] = [
|
||||||
|
FieldDef("local_as", length=16, indexed=True, is_key=True, display_name="Local AS", from_command_param=True),
|
||||||
FieldDef("network", length=256, indexed=True, is_key=True, display_name="Network"),
|
FieldDef("network", length=256, indexed=True, is_key=True, display_name="Network"),
|
||||||
FieldDef("next_hop", length=128, role="state", display_name="NextHop"),
|
FieldDef("next_hop", length=128, role="state", display_name="NextHop"),
|
||||||
FieldDef("path", length=256, role="state", display_name="Path"),
|
FieldDef("path", length=256, role="state", display_name="Path"),
|
||||||
|
|
@ -585,6 +647,7 @@ _CONFIG_IFACE_FIELDS: list[FieldDef] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
_CONFIG_BGP_PEER_FIELDS: list[FieldDef] = [
|
_CONFIG_BGP_PEER_FIELDS: list[FieldDef] = [
|
||||||
|
FieldDef("local_as", length=16, indexed=True, is_key=True, display_name="Local AS"),
|
||||||
FieldDef("afi", length=32, indexed=True, is_key=True, display_name="AFI"),
|
FieldDef("afi", length=32, indexed=True, is_key=True, display_name="AFI"),
|
||||||
FieldDef("vrf", length=128, indexed=True, is_key=True, display_name="VRF"),
|
FieldDef("vrf", length=128, indexed=True, is_key=True, display_name="VRF"),
|
||||||
FieldDef("neighbor", length=128, indexed=True, is_key=True, display_name="Neighbor"),
|
FieldDef("neighbor", length=128, indexed=True, is_key=True, display_name="Neighbor"),
|
||||||
|
|
@ -779,10 +842,11 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_peer",
|
metric_id="bgp_peer",
|
||||||
parser_id="bgp_peer",
|
parser_id="bgp_peer",
|
||||||
title="BGP VPNv4 Summary",
|
title="BGP VPNv4 Summary",
|
||||||
command_template="show bgp vpnv4 unicast summary | one-line",
|
command_template="show bgp vpnv4 unicast summary as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+summary(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+summary(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp vpnv4 unicast summary",
|
textfsm_command="show bgp vpnv4 unicast summary",
|
||||||
description="BGP VPNv4 peer summary (afi=vpnv4).",
|
description="BGP VPNv4 peer summary (afi=vpnv4).",
|
||||||
|
placeholders=[_BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_PEER_FIELDS),
|
fields=list(_BGP_PEER_FIELDS),
|
||||||
tags=["bgp", "vpnv4", "status"],
|
tags=["bgp", "vpnv4", "status"],
|
||||||
sort_order=340,
|
sort_order=340,
|
||||||
|
|
@ -795,10 +859,11 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_peer",
|
metric_id="bgp_peer",
|
||||||
parser_id="bgp_peer",
|
parser_id="bgp_peer",
|
||||||
title="BGP IPv4 Summary",
|
title="BGP IPv4 Summary",
|
||||||
command_template="show bgp ipv4 unicast summary | one-line",
|
command_template="show bgp ipv4 unicast summary as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+ipv4\s+unicast\s+summary(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+ipv4\s+unicast\s+summary(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp ipv4 unicast summary",
|
textfsm_command="show bgp ipv4 unicast summary",
|
||||||
description="BGP IPv4 unicast peer summary (afi=ipv4).",
|
description="BGP IPv4 unicast peer summary (afi=ipv4).",
|
||||||
|
placeholders=[_BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_PEER_FIELDS),
|
fields=list(_BGP_PEER_FIELDS),
|
||||||
tags=["bgp", "ipv4", "status"],
|
tags=["bgp", "ipv4", "status"],
|
||||||
sort_order=350,
|
sort_order=350,
|
||||||
|
|
@ -811,10 +876,11 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_peer",
|
metric_id="bgp_peer",
|
||||||
parser_id="bgp_peer",
|
parser_id="bgp_peer",
|
||||||
title="BGP VPNv6 Summary",
|
title="BGP VPNv6 Summary",
|
||||||
command_template="show bgp vpnv6 unicast summary | one-line",
|
command_template="show bgp vpnv6 unicast summary as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+summary(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+summary(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp vpnv6 unicast summary",
|
textfsm_command="show bgp vpnv6 unicast summary",
|
||||||
description="BGP VPNv6 peer summary (afi=vpnv6).",
|
description="BGP VPNv6 peer summary (afi=vpnv6).",
|
||||||
|
placeholders=[_BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_PEER_FIELDS),
|
fields=list(_BGP_PEER_FIELDS),
|
||||||
tags=["bgp", "vpnv6", "status"],
|
tags=["bgp", "vpnv6", "status"],
|
||||||
sort_order=360,
|
sort_order=360,
|
||||||
|
|
@ -827,10 +893,11 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_peer",
|
metric_id="bgp_peer",
|
||||||
parser_id="bgp_peer",
|
parser_id="bgp_peer",
|
||||||
title="BGP IPv6 Summary",
|
title="BGP IPv6 Summary",
|
||||||
command_template="show bgp ipv6 unicast summary | one-line",
|
command_template="show bgp ipv6 unicast summary as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+ipv6\s+unicast\s+summary(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+ipv6\s+unicast\s+summary(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp ipv6 unicast summary",
|
textfsm_command="show bgp ipv6 unicast summary",
|
||||||
description="BGP IPv6 unicast peer summary (afi=ipv6).",
|
description="BGP IPv6 unicast peer summary (afi=ipv6).",
|
||||||
|
placeholders=[_BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_PEER_FIELDS),
|
fields=list(_BGP_PEER_FIELDS),
|
||||||
tags=["bgp", "ipv6", "status"],
|
tags=["bgp", "ipv6", "status"],
|
||||||
sort_order=365,
|
sort_order=365,
|
||||||
|
|
@ -843,27 +910,19 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_peer",
|
metric_id="bgp_peer",
|
||||||
parser_id="bgp_peer",
|
parser_id="bgp_peer",
|
||||||
title="BGP VPNv4 VRF Summary",
|
title="BGP VPNv4 VRF Summary",
|
||||||
command_template="show bgp vpnv4 unicast vrf <vrf> summary | one-line",
|
command_template="show bgp vpnv4 unicast vrf <vrf> summary as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+summary(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+summary(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp vpnv4 unicast summary",
|
textfsm_command="show bgp vpnv4 unicast summary",
|
||||||
description=(
|
description=(
|
||||||
"Per-VRF BGP VPNv4 peer summary. Bind one or more VRFs; "
|
"Per-VRF BGP VPNv4 peer summary. Bind (local_as, vrf) from BGP peer intent; "
|
||||||
"aux: config_vrf (RD/AF enrich). IPv4 FIB is a separate monitor item."
|
"IPv4 FIB is a separate monitor item."
|
||||||
),
|
),
|
||||||
placeholders=[_VRF_PLACEHOLDER_IPV4],
|
placeholders=[_BGP_VRF_SUMMARY_IPV4, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_PEER_FIELDS),
|
fields=list(_BGP_PEER_FIELDS),
|
||||||
tags=["bgp", "vpnv4", "vrf", "status"],
|
tags=["bgp", "vpnv4", "vrf", "status"],
|
||||||
sort_order=370,
|
sort_order=370,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
enrich_joins=[
|
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_vrf",
|
|
||||||
left_on="vrf",
|
|
||||||
right_on="vrf_name",
|
|
||||||
take=("rd", "address_families"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_vpnv6_vrf_summary",
|
profile_id="zte.bgp_vpnv6_vrf_summary",
|
||||||
|
|
@ -871,27 +930,19 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_peer",
|
metric_id="bgp_peer",
|
||||||
parser_id="bgp_peer",
|
parser_id="bgp_peer",
|
||||||
title="BGP VPNv6 VRF Summary",
|
title="BGP VPNv6 VRF Summary",
|
||||||
command_template="show bgp vpnv6 unicast vrf <vrf> summary | one-line",
|
command_template="show bgp vpnv6 unicast vrf <vrf> summary as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+summary(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+summary(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp vpnv6 unicast summary",
|
textfsm_command="show bgp vpnv6 unicast summary",
|
||||||
description=(
|
description=(
|
||||||
"Per-VRF BGP VPNv6 peer summary. Bind one or more VRFs; "
|
"Per-VRF BGP VPNv6 peer summary. Bind (local_as, vrf) from BGP peer intent; "
|
||||||
"aux: config_vrf (RD/AF enrich). IPv6 FIB is a separate monitor item."
|
"IPv6 FIB is a separate monitor item."
|
||||||
),
|
),
|
||||||
placeholders=[_VRF_PLACEHOLDER_IPV6],
|
placeholders=[_BGP_VRF_SUMMARY_IPV6, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_PEER_FIELDS),
|
fields=list(_BGP_PEER_FIELDS),
|
||||||
tags=["bgp", "vpnv6", "vrf", "status"],
|
tags=["bgp", "vpnv6", "vrf", "status"],
|
||||||
sort_order=375,
|
sort_order=375,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
enrich_joins=[
|
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_vrf",
|
|
||||||
left_on="vrf",
|
|
||||||
right_on="vrf_name",
|
|
||||||
take=("rd", "address_families"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_evpn_summary",
|
profile_id="zte.bgp_evpn_summary",
|
||||||
|
|
@ -899,10 +950,11 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_peer",
|
metric_id="bgp_peer",
|
||||||
parser_id="bgp_peer",
|
parser_id="bgp_peer",
|
||||||
title="BGP L2VPN EVPN Summary",
|
title="BGP L2VPN EVPN Summary",
|
||||||
command_template="show bgp l2vpn evpn summary | one-line",
|
command_template="show bgp l2vpn evpn summary as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+l2vpn\s+evpn\s+summary(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+l2vpn\s+evpn\s+summary(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp l2vpn evpn summary",
|
textfsm_command="show bgp l2vpn evpn summary",
|
||||||
description="BGP L2VPN EVPN peer summary (afi=evpn).",
|
description="BGP L2VPN EVPN peer summary (afi=evpn).",
|
||||||
|
placeholders=[_BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_PEER_FIELDS),
|
fields=list(_BGP_PEER_FIELDS),
|
||||||
tags=["bgp", "evpn", "status"],
|
tags=["bgp", "evpn", "status"],
|
||||||
sort_order=380,
|
sort_order=380,
|
||||||
|
|
@ -915,10 +967,11 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_peer",
|
metric_id="bgp_peer",
|
||||||
parser_id="bgp_peer",
|
parser_id="bgp_peer",
|
||||||
title="BGP L2VPN VPLS Summary",
|
title="BGP L2VPN VPLS Summary",
|
||||||
command_template="show bgp l2vpn vpls summary | one-line",
|
command_template="show bgp l2vpn vpls summary as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+l2vpn\s+vpls\s+summary(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+l2vpn\s+vpls\s+summary(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp l2vpn vpls summary",
|
textfsm_command="show bgp l2vpn vpls summary",
|
||||||
description="BGP L2VPN VPLS peer summary (afi=vpls).",
|
description="BGP L2VPN VPLS peer summary (afi=vpls).",
|
||||||
|
placeholders=[_BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_PEER_FIELDS),
|
fields=list(_BGP_PEER_FIELDS),
|
||||||
tags=["bgp", "vpls", "status"],
|
tags=["bgp", "vpls", "status"],
|
||||||
sort_order=385,
|
sort_order=385,
|
||||||
|
|
@ -997,27 +1050,20 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP VPNv4 Neighbor In",
|
title="BGP VPNv4 Neighbor In",
|
||||||
command_template="show bgp vpnv4 unicast neighbor in <neighbor> | one-line",
|
command_template="show bgp vpnv4 unicast neighbor in <neighbor> as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp vpnv4 unicast neighbor in",
|
textfsm_command="show bgp vpnv4 unicast neighbor in",
|
||||||
description=(
|
description=(
|
||||||
"Routes learned from VPNv4 neighbor; discover neighbor from BGP peer intent; "
|
"Routes learned from VPNv4 neighbor; discover neighbor from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_NEIGHBOR_VPNV4],
|
placeholders=[_BGP_NEIGHBOR_VPNV4, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "vpnv4", "route"],
|
tags=["bgp", "vpnv4", "route"],
|
||||||
sort_order=420,
|
sort_order=420,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="afi,neighbor",
|
|
||||||
right_on="afi,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_vpnv4_neighbor_out",
|
profile_id="zte.bgp_vpnv4_neighbor_out",
|
||||||
|
|
@ -1025,28 +1071,21 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP VPNv4 Neighbor Out",
|
title="BGP VPNv4 Neighbor Out",
|
||||||
command_template="show bgp vpnv4 unicast neighbor out <neighbor> | one-line",
|
command_template="show bgp vpnv4 unicast neighbor out <neighbor> as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp vpnv4 unicast neighbor out",
|
textfsm_command="show bgp vpnv4 unicast neighbor out",
|
||||||
description=(
|
description=(
|
||||||
"Routes advertised to VPNv4 neighbor; discover from BGP peer intent; "
|
"Routes advertised to VPNv4 neighbor; discover from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_NEIGHBOR_VPNV4],
|
placeholders=[_BGP_NEIGHBOR_VPNV4, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "vpnv4", "route"],
|
tags=["bgp", "vpnv4", "route"],
|
||||||
sort_order=425,
|
sort_order=425,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
collect_lane="heavy",
|
collect_lane="heavy",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="afi,neighbor",
|
|
||||||
right_on="afi,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_vpnv6_neighbor_in",
|
profile_id="zte.bgp_vpnv6_neighbor_in",
|
||||||
|
|
@ -1054,27 +1093,20 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP VPNv6 Neighbor In",
|
title="BGP VPNv6 Neighbor In",
|
||||||
command_template="show bgp vpnv6 unicast neighbor in <neighbor> | one-line",
|
command_template="show bgp vpnv6 unicast neighbor in <neighbor> as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp vpnv6 unicast neighbor in",
|
textfsm_command="show bgp vpnv6 unicast neighbor in",
|
||||||
description=(
|
description=(
|
||||||
"Routes learned from VPNv6 neighbor; discover from BGP peer intent; "
|
"Routes learned from VPNv6 neighbor; discover from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_NEIGHBOR_VPNV6],
|
placeholders=[_BGP_NEIGHBOR_VPNV6, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "vpnv6", "route"],
|
tags=["bgp", "vpnv6", "route"],
|
||||||
sort_order=426,
|
sort_order=426,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="afi,neighbor",
|
|
||||||
right_on="afi,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_vpnv6_neighbor_out",
|
profile_id="zte.bgp_vpnv6_neighbor_out",
|
||||||
|
|
@ -1082,28 +1114,21 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP VPNv6 Neighbor Out",
|
title="BGP VPNv6 Neighbor Out",
|
||||||
command_template="show bgp vpnv6 unicast neighbor out <neighbor> | one-line",
|
command_template="show bgp vpnv6 unicast neighbor out <neighbor> as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp vpnv6 unicast neighbor out",
|
textfsm_command="show bgp vpnv6 unicast neighbor out",
|
||||||
description=(
|
description=(
|
||||||
"Routes advertised to VPNv6 neighbor; discover from BGP peer intent; "
|
"Routes advertised to VPNv6 neighbor; discover from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_NEIGHBOR_VPNV6],
|
placeholders=[_BGP_NEIGHBOR_VPNV6, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "vpnv6", "route"],
|
tags=["bgp", "vpnv6", "route"],
|
||||||
sort_order=427,
|
sort_order=427,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
collect_lane="heavy",
|
collect_lane="heavy",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="afi,neighbor",
|
|
||||||
right_on="afi,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_ipv4_neighbor_in",
|
profile_id="zte.bgp_ipv4_neighbor_in",
|
||||||
|
|
@ -1111,27 +1136,20 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP IPv4 Neighbor In",
|
title="BGP IPv4 Neighbor In",
|
||||||
command_template="show bgp ipv4 unicast neighbor in <neighbor> | one-line",
|
command_template="show bgp ipv4 unicast neighbor in <neighbor> as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+ipv4\s+unicast\s+neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+ipv4\s+unicast\s+neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp ipv4 unicast neighbor in",
|
textfsm_command="show bgp ipv4 unicast neighbor in",
|
||||||
description=(
|
description=(
|
||||||
"Routes learned from IPv4 unicast neighbor; discover from BGP peer intent "
|
"Routes learned from IPv4 unicast neighbor; discover from BGP peer intent "
|
||||||
"(direct + peer-group members); aux: show running-config bgp."
|
"(direct + peer-group members); aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_NEIGHBOR_IPV4],
|
placeholders=[_BGP_NEIGHBOR_IPV4, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "ipv4", "route"],
|
tags=["bgp", "ipv4", "route"],
|
||||||
sort_order=428,
|
sort_order=428,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="afi,neighbor",
|
|
||||||
right_on="afi,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_ipv4_neighbor_out",
|
profile_id="zte.bgp_ipv4_neighbor_out",
|
||||||
|
|
@ -1139,28 +1157,21 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP IPv4 Neighbor Out",
|
title="BGP IPv4 Neighbor Out",
|
||||||
command_template="show bgp ipv4 unicast neighbor out <neighbor> | one-line",
|
command_template="show bgp ipv4 unicast neighbor out <neighbor> as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+ipv4\s+unicast\s+neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+ipv4\s+unicast\s+neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp ipv4 unicast neighbor out",
|
textfsm_command="show bgp ipv4 unicast neighbor out",
|
||||||
description=(
|
description=(
|
||||||
"Routes advertised to IPv4 unicast neighbor; discover from BGP peer intent; "
|
"Routes advertised to IPv4 unicast neighbor; discover from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_NEIGHBOR_IPV4],
|
placeholders=[_BGP_NEIGHBOR_IPV4, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "ipv4", "route"],
|
tags=["bgp", "ipv4", "route"],
|
||||||
sort_order=429,
|
sort_order=429,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
collect_lane="heavy",
|
collect_lane="heavy",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="afi,neighbor",
|
|
||||||
right_on="afi,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_ipv6_neighbor_in",
|
profile_id="zte.bgp_ipv6_neighbor_in",
|
||||||
|
|
@ -1168,27 +1179,20 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP IPv6 Neighbor In",
|
title="BGP IPv6 Neighbor In",
|
||||||
command_template="show bgp ipv6 unicast neighbor in <neighbor> | one-line",
|
command_template="show bgp ipv6 unicast neighbor in <neighbor> as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+ipv6\s+unicast\s+neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+ipv6\s+unicast\s+neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp ipv6 unicast neighbor in",
|
textfsm_command="show bgp ipv6 unicast neighbor in",
|
||||||
description=(
|
description=(
|
||||||
"Routes learned from IPv6 unicast neighbor; discover from BGP peer intent "
|
"Routes learned from IPv6 unicast neighbor; discover from BGP peer intent "
|
||||||
"(direct + peer-group members); aux: show running-config bgp."
|
"(direct + peer-group members); aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_NEIGHBOR_IPV6],
|
placeholders=[_BGP_NEIGHBOR_IPV6, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "ipv6", "route"],
|
tags=["bgp", "ipv6", "route"],
|
||||||
sort_order=430,
|
sort_order=430,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="afi,neighbor",
|
|
||||||
right_on="afi,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_ipv6_neighbor_out",
|
profile_id="zte.bgp_ipv6_neighbor_out",
|
||||||
|
|
@ -1196,28 +1200,21 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP IPv6 Neighbor Out",
|
title="BGP IPv6 Neighbor Out",
|
||||||
command_template="show bgp ipv6 unicast neighbor out <neighbor> | one-line",
|
command_template="show bgp ipv6 unicast neighbor out <neighbor> as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+ipv6\s+unicast\s+neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+ipv6\s+unicast\s+neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp ipv6 unicast neighbor out",
|
textfsm_command="show bgp ipv6 unicast neighbor out",
|
||||||
description=(
|
description=(
|
||||||
"Routes advertised to IPv6 unicast neighbor; discover from BGP peer intent; "
|
"Routes advertised to IPv6 unicast neighbor; discover from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_NEIGHBOR_IPV6],
|
placeholders=[_BGP_NEIGHBOR_IPV6, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "ipv6", "route"],
|
tags=["bgp", "ipv6", "route"],
|
||||||
sort_order=431,
|
sort_order=431,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
collect_lane="heavy",
|
collect_lane="heavy",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="afi,neighbor",
|
|
||||||
right_on="afi,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_vpnv4_vrf_neighbor_in",
|
profile_id="zte.bgp_vpnv4_vrf_neighbor_in",
|
||||||
|
|
@ -1225,30 +1222,24 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP VPNv4 VRF Neighbor In",
|
title="BGP VPNv4 VRF Neighbor In",
|
||||||
command_template="show bgp vpnv4 unicast vrf <vrf> neighbor in <neighbor> | one-line",
|
command_template="show bgp vpnv4 unicast vrf <vrf> neighbor in <neighbor> as <local_as> | one-line",
|
||||||
match=(
|
match=(
|
||||||
r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+"
|
r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+"
|
||||||
r"neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$"
|
r"neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)"
|
||||||
|
r"(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$"
|
||||||
),
|
),
|
||||||
textfsm_command="show bgp vpnv4 unicast neighbor in",
|
textfsm_command="show bgp vpnv4 unicast neighbor in",
|
||||||
description=(
|
description=(
|
||||||
"Per-VRF CE peer routes; discover (vrf,neighbor) from BGP peer intent; "
|
"Per-VRF CE peer routes; discover (local_as,vrf,neighbor) from BGP peer intent; "
|
||||||
"aux: show running-config bgp (no placeholders)."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_VRF_PEER_IPV4, _BGP_NEIGHBOR_IPV4_VRF],
|
placeholders=[_BGP_VRF_PEER_IPV4, _BGP_NEIGHBOR_IPV4_VRF, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "vpnv4", "vrf", "route"],
|
tags=["bgp", "vpnv4", "vrf", "route"],
|
||||||
sort_order=440,
|
sort_order=440,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER_VRF),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="vrf,neighbor",
|
|
||||||
right_on="vrf,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_vpnv4_vrf_neighbor_out",
|
profile_id="zte.bgp_vpnv4_vrf_neighbor_out",
|
||||||
|
|
@ -1256,31 +1247,25 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP VPNv4 VRF Neighbor Out",
|
title="BGP VPNv4 VRF Neighbor Out",
|
||||||
command_template="show bgp vpnv4 unicast vrf <vrf> neighbor out <neighbor> | one-line",
|
command_template="show bgp vpnv4 unicast vrf <vrf> neighbor out <neighbor> as <local_as> | one-line",
|
||||||
match=(
|
match=(
|
||||||
r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+"
|
r"(?i)^\s*show\s+bgp\s+vpnv4\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+"
|
||||||
r"neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$"
|
r"neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)"
|
||||||
|
r"(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$"
|
||||||
),
|
),
|
||||||
textfsm_command="show bgp vpnv4 unicast neighbor out",
|
textfsm_command="show bgp vpnv4 unicast neighbor out",
|
||||||
description=(
|
description=(
|
||||||
"Per-VRF CE advertised routes; discover pairs from BGP peer intent; "
|
"Per-VRF CE advertised routes; discover (local_as,vrf,neighbor) from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_VRF_PEER_IPV4, _BGP_NEIGHBOR_IPV4_VRF],
|
placeholders=[_BGP_VRF_PEER_IPV4, _BGP_NEIGHBOR_IPV4_VRF, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "vpnv4", "vrf", "route"],
|
tags=["bgp", "vpnv4", "vrf", "route"],
|
||||||
sort_order=445,
|
sort_order=445,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
collect_lane="heavy",
|
collect_lane="heavy",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER_VRF),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="vrf,neighbor",
|
|
||||||
right_on="vrf,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_vpnv6_vrf_neighbor_in",
|
profile_id="zte.bgp_vpnv6_vrf_neighbor_in",
|
||||||
|
|
@ -1288,30 +1273,24 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP VPNv6 VRF Neighbor In",
|
title="BGP VPNv6 VRF Neighbor In",
|
||||||
command_template="show bgp vpnv6 unicast vrf <vrf> neighbor in <neighbor> | one-line",
|
command_template="show bgp vpnv6 unicast vrf <vrf> neighbor in <neighbor> as <local_as> | one-line",
|
||||||
match=(
|
match=(
|
||||||
r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+"
|
r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+"
|
||||||
r"neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$"
|
r"neighbor\s+(?P<direction>in)\s+(?P<neighbor>\S+)"
|
||||||
|
r"(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$"
|
||||||
),
|
),
|
||||||
textfsm_command="show bgp vpnv6 unicast neighbor in",
|
textfsm_command="show bgp vpnv6 unicast neighbor in",
|
||||||
description=(
|
description=(
|
||||||
"Per-VRF IPv6 CE peer routes; discover pairs from BGP peer intent; "
|
"Per-VRF IPv6 CE peer routes; discover (local_as,vrf,neighbor) from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_VRF_PEER_IPV6, _BGP_NEIGHBOR_IPV6_VRF],
|
placeholders=[_BGP_VRF_PEER_IPV6, _BGP_NEIGHBOR_IPV6_VRF, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "vpnv6", "vrf", "route"],
|
tags=["bgp", "vpnv6", "vrf", "route"],
|
||||||
sort_order=450,
|
sort_order=450,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER_VRF),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="vrf,neighbor",
|
|
||||||
right_on="vrf,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
profile_id="zte.bgp_vpnv6_vrf_neighbor_out",
|
profile_id="zte.bgp_vpnv6_vrf_neighbor_out",
|
||||||
|
|
@ -1319,31 +1298,25 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="bgp_route",
|
metric_id="bgp_route",
|
||||||
parser_id="bgp_route",
|
parser_id="bgp_route",
|
||||||
title="BGP VPNv6 VRF Neighbor Out",
|
title="BGP VPNv6 VRF Neighbor Out",
|
||||||
command_template="show bgp vpnv6 unicast vrf <vrf> neighbor out <neighbor> | one-line",
|
command_template="show bgp vpnv6 unicast vrf <vrf> neighbor out <neighbor> as <local_as> | one-line",
|
||||||
match=(
|
match=(
|
||||||
r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+"
|
r"(?i)^\s*show\s+bgp\s+vpnv6\s+unicast\s+vrf\s+(?P<vrf>\S+)\s+"
|
||||||
r"neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)(?:\s*\|\s*one-line)?\s*$"
|
r"neighbor\s+(?P<direction>out)\s+(?P<neighbor>\S+)"
|
||||||
|
r"(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$"
|
||||||
),
|
),
|
||||||
textfsm_command="show bgp vpnv6 unicast neighbor out",
|
textfsm_command="show bgp vpnv6 unicast neighbor out",
|
||||||
description=(
|
description=(
|
||||||
"Per-VRF IPv6 CE advertised routes; discover pairs from BGP peer intent; "
|
"Per-VRF IPv6 CE advertised routes; discover (local_as,vrf,neighbor) from BGP peer intent; "
|
||||||
"aux: show running-config bgp."
|
"aux: show running-config bgp."
|
||||||
),
|
),
|
||||||
placeholders=[_BGP_VRF_PEER_IPV6, _BGP_NEIGHBOR_IPV6_VRF],
|
placeholders=[_BGP_VRF_PEER_IPV6, _BGP_NEIGHBOR_IPV6_VRF, _BGP_LOCAL_AS],
|
||||||
fields=list(_BGP_ROUTE_FIELDS),
|
fields=list(_BGP_ROUTE_FIELDS),
|
||||||
tags=["bgp", "vpnv6", "vrf", "route"],
|
tags=["bgp", "vpnv6", "vrf", "route"],
|
||||||
sort_order=455,
|
sort_order=455,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
collect_lane="heavy",
|
collect_lane="heavy",
|
||||||
enrich_joins=[
|
enrich_joins=list(_BGP_ENRICH_PEER_VRF),
|
||||||
EnrichJoin(
|
|
||||||
from_aux="config_bgp_peer",
|
|
||||||
left_on="vrf,neighbor",
|
|
||||||
right_on="vrf,neighbor",
|
|
||||||
take=("remote_as", "activate", "route_map_in", "route_map_out"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
# --- Phase 4 forwarding / L2 ---
|
# --- Phase 4 forwarding / L2 ---
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
|
|
@ -1482,10 +1455,11 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
metric_id="evpn_mac",
|
metric_id="evpn_mac",
|
||||||
parser_id="evpn_mac",
|
parser_id="evpn_mac",
|
||||||
title="BGP EVPN MAC",
|
title="BGP EVPN MAC",
|
||||||
command_template="show bgp evpn mac | one-line",
|
command_template="show bgp evpn mac as <local_as> | one-line",
|
||||||
match=r"(?i)^\s*show\s+bgp\s+evpn\s+mac(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+bgp\s+evpn\s+mac(?:\s+as\s+(?P<local_as>\S+))?(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show bgp evpn mac",
|
textfsm_command="show bgp evpn mac",
|
||||||
description="BGP EVPN MAC NLRI (large; disabled by default).",
|
description="BGP EVPN MAC NLRI (large; disabled by default).",
|
||||||
|
placeholders=[_BGP_LOCAL_AS],
|
||||||
fields=list(_EVPN_MAC_FIELDS),
|
fields=list(_EVPN_MAC_FIELDS),
|
||||||
tags=["bgp", "evpn", "mac"],
|
tags=["bgp", "evpn", "mac"],
|
||||||
sort_order=490,
|
sort_order=490,
|
||||||
|
|
@ -1537,7 +1511,7 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
command_template="show running-config bgp | one-line",
|
command_template="show running-config bgp | one-line",
|
||||||
match=r"(?i)^\s*show\s+running-config\s+bgp(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+running-config\s+bgp(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show running-config bgp",
|
textfsm_command="show running-config bgp",
|
||||||
description="BGP neighbor AF activate / remote-as intent (passwords skipped).",
|
description="BGP neighbor AF activate / remote-as / local-as intent (multi router-bgp; passwords skipped).",
|
||||||
fields=list(_CONFIG_BGP_PEER_FIELDS),
|
fields=list(_CONFIG_BGP_PEER_FIELDS),
|
||||||
tags=["config", "bgp", "intent"],
|
tags=["config", "bgp", "intent"],
|
||||||
sort_order=520,
|
sort_order=520,
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,20 @@ class DiscoverCacheTests(unittest.TestCase):
|
||||||
self.assertEqual(v6.placeholders[0].discover_profile_id, "zte.config_bgp_peer")
|
self.assertEqual(v6.placeholders[0].discover_profile_id, "zte.config_bgp_peer")
|
||||||
|
|
||||||
records = [
|
records = [
|
||||||
{"afi": "vpnv4", "vrf": "", "neighbor": "10.0.0.1", "remote_as": "65001"},
|
{
|
||||||
{"afi": "vpnv6", "vrf": "", "neighbor": "FC00::1", "remote_as": "65002"},
|
"local_as": "64900",
|
||||||
|
"afi": "vpnv4",
|
||||||
|
"vrf": "",
|
||||||
|
"neighbor": "10.0.0.1",
|
||||||
|
"remote_as": "65001",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local_as": "64900",
|
||||||
|
"afi": "vpnv6",
|
||||||
|
"vrf": "",
|
||||||
|
"neighbor": "FC00::1",
|
||||||
|
"remote_as": "65002",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
db = MagicMock()
|
db = MagicMock()
|
||||||
|
|
||||||
|
|
@ -53,7 +65,12 @@ class DiscoverCacheTests(unittest.TestCase):
|
||||||
)
|
)
|
||||||
self.assertTrue(first["ok"])
|
self.assertTrue(first["ok"])
|
||||||
self.assertFalse(first.get("cache_hit"))
|
self.assertFalse(first.get("cache_hit"))
|
||||||
self.assertEqual([c["value"] for c in first["candidates"]], ["10.0.0.1"])
|
self.assertTrue(first.get("pair_mode"))
|
||||||
|
self.assertEqual(len(first["candidates"]), 1)
|
||||||
|
self.assertEqual(
|
||||||
|
first["candidates"][0]["bindings"],
|
||||||
|
{"neighbor": "10.0.0.1", "local_as": "64900"},
|
||||||
|
)
|
||||||
self.assertEqual(open_conn.call_count, 1)
|
self.assertEqual(open_conn.call_count, 1)
|
||||||
self.assertEqual(send_cmd.call_count, 1)
|
self.assertEqual(send_cmd.call_count, 1)
|
||||||
self.assertEqual(run_parser.call_count, 1)
|
self.assertEqual(run_parser.call_count, 1)
|
||||||
|
|
@ -67,7 +84,11 @@ class DiscoverCacheTests(unittest.TestCase):
|
||||||
)
|
)
|
||||||
self.assertTrue(second["ok"])
|
self.assertTrue(second["ok"])
|
||||||
self.assertTrue(second.get("cache_hit"))
|
self.assertTrue(second.get("cache_hit"))
|
||||||
self.assertEqual([c["value"] for c in second["candidates"]], ["FC00::1"])
|
self.assertEqual(len(second["candidates"]), 1)
|
||||||
|
self.assertEqual(
|
||||||
|
second["candidates"][0]["bindings"],
|
||||||
|
{"neighbor": "FC00::1", "local_as": "64900"},
|
||||||
|
)
|
||||||
# Same discover profile → no second SSH/parse
|
# Same discover profile → no second SSH/parse
|
||||||
self.assertEqual(open_conn.call_count, 1)
|
self.assertEqual(open_conn.call_count, 1)
|
||||||
self.assertEqual(send_cmd.call_count, 1)
|
self.assertEqual(send_cmd.call_count, 1)
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,8 @@ class BizStateExportCommandsTests(unittest.TestCase):
|
||||||
cmds = plan["commands"]
|
cmds = plan["commands"]
|
||||||
self.assertTrue(any("lldp" in c.lower() for c in cmds))
|
self.assertTrue(any("lldp" in c.lower() for c in cmds))
|
||||||
self.assertTrue(any("CUST_A" in c and "summary" in c for c in cmds))
|
self.assertTrue(any("CUST_A" in c and "summary" in c for c in cmds))
|
||||||
# Default export includes discover-backed aux (config_vrf), not FIB
|
# Default export includes discover-backed aux (config_bgp_peer)
|
||||||
self.assertTrue(any("running-config vrf" in c for c in cmds))
|
self.assertTrue(any("running-config bgp" in c for c in cmds))
|
||||||
self.assertFalse(any("forwarding route" in c for c in cmds))
|
self.assertFalse(any("forwarding route" in c for c in cmds))
|
||||||
# Disabled item excluded by default
|
# Disabled item excluded by default
|
||||||
self.assertFalse(any("isis" in c.lower() for c in cmds))
|
self.assertFalse(any("isis" in c.lower() for c in cmds))
|
||||||
|
|
@ -91,14 +91,14 @@ class BizStateExportCommandsTests(unittest.TestCase):
|
||||||
primary_only = plan_task_collect_commands(
|
primary_only = plan_task_collect_commands(
|
||||||
self.db, self.task.id, include_aux=False
|
self.db, self.task.id, include_aux=False
|
||||||
)
|
)
|
||||||
self.assertFalse(any("running-config vrf" in c for c in primary_only["commands"]))
|
self.assertFalse(any("running-config bgp" in c for c in primary_only["commands"]))
|
||||||
|
|
||||||
text = export_task_commands_text(self.db, self.task.id)
|
text = export_task_commands_text(self.db, self.task.id)
|
||||||
self.assertIn("task_id=t_export", text)
|
self.assertIn("task_id=t_export", text)
|
||||||
self.assertIn("show lldp neighbor brief", text)
|
self.assertIn("show lldp neighbor brief", text)
|
||||||
self.assertIn("CUST_A", text)
|
self.assertIn("CUST_A", text)
|
||||||
self.assertIn("# aux:", text)
|
self.assertIn("# aux:", text)
|
||||||
self.assertIn("running-config vrf", text)
|
self.assertIn("running-config bgp", text)
|
||||||
self.assertIn("# ---- flat unique commands ----", text)
|
self.assertIn("# ---- flat unique commands ----", text)
|
||||||
|
|
||||||
def test_shared_aux_repeated_per_item_deduped_in_flat(self) -> None:
|
def test_shared_aux_repeated_per_item_deduped_in_flat(self) -> None:
|
||||||
|
|
@ -157,7 +157,7 @@ class BizStateExportCommandsTests(unittest.TestCase):
|
||||||
flat = text.split("# ---- flat unique commands ----", 1)[-1]
|
flat = text.split("# ---- flat unique commands ----", 1)[-1]
|
||||||
self.assertEqual(flat.count(aux_cli), 1)
|
self.assertEqual(flat.count(aux_cli), 1)
|
||||||
|
|
||||||
def test_unbound_required_placeholder_noted(self) -> None:
|
def test_unbound_optional_placeholder_expand_all(self) -> None:
|
||||||
item = BizStateTaskItem(
|
item = BizStateTaskItem(
|
||||||
id="i_exp",
|
id="i_exp",
|
||||||
task_id=self.task.id,
|
task_id=self.task.id,
|
||||||
|
|
@ -173,24 +173,25 @@ class BizStateExportCommandsTests(unittest.TestCase):
|
||||||
plan = plan_task_collect_commands(self.db, self.task.id)
|
plan = plan_task_collect_commands(self.db, self.task.id)
|
||||||
sec = plan["items"][0]
|
sec = plan["items"][0]
|
||||||
notes = " ".join(sec.get("notes") or [])
|
notes = " ".join(sec.get("notes") or [])
|
||||||
self.assertIn("requires parameter bindings", notes)
|
self.assertIn("expand_all", notes)
|
||||||
# Primary is template-only; aux (config_vrf) still counts as executable
|
# Primary is template-only; aux (config_bgp_peer) still counts as executable
|
||||||
self.assertEqual(plan["command_count"], 1)
|
self.assertEqual(plan["command_count"], 1)
|
||||||
self.assertTrue(any("running-config vrf" in c for c in plan["commands"]))
|
self.assertTrue(any("running-config bgp" in c for c in plan["commands"]))
|
||||||
tmpl_cmds = [c for c in sec.get("commands") or [] if c.get("role") == "template"]
|
tmpl_cmds = [c for c in sec.get("commands") or [] if c.get("role") == "template"]
|
||||||
self.assertEqual(len(tmpl_cmds), 1)
|
self.assertEqual(len(tmpl_cmds), 1)
|
||||||
self.assertIn("<vrf>", tmpl_cmds[0]["command"])
|
self.assertIn("<vrf>", tmpl_cmds[0]["command"])
|
||||||
|
self.assertIn("<local_as>", tmpl_cmds[0]["command"])
|
||||||
aux_cmds = [c for c in sec.get("commands") or [] if c.get("role") == "aux"]
|
aux_cmds = [c for c in sec.get("commands") or [] if c.get("role") == "aux"]
|
||||||
self.assertEqual(len(aux_cmds), 1)
|
self.assertEqual(len(aux_cmds), 1)
|
||||||
|
|
||||||
text = export_task_commands_text(self.db, self.task.id)
|
text = export_task_commands_text(self.db, self.task.id)
|
||||||
self.assertIn("# show bgp vpnv4 unicast vrf <vrf> summary", text)
|
self.assertIn("# show bgp vpnv4 unicast vrf <vrf> summary as <local_as>", text)
|
||||||
self.assertIn("# aux:", text)
|
self.assertIn("# aux:", text)
|
||||||
self.assertIn("running-config vrf", text)
|
self.assertIn("running-config bgp", text)
|
||||||
# Template must not appear in the flat executable list
|
# Template must not appear in the flat executable list
|
||||||
flat = text.split("# ---- flat unique commands ----", 1)[-1]
|
flat = text.split("# ---- flat unique commands ----", 1)[-1]
|
||||||
self.assertNotIn("<vrf>", flat)
|
self.assertNotIn("<vrf>", flat)
|
||||||
self.assertIn("running-config vrf", flat)
|
self.assertIn("running-config bgp", flat)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,74 @@ $
|
||||||
!</bgp>
|
!</bgp>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Multi local-AS (trimmed from MER1 show running-config bgp)
|
||||||
|
_CFG_BGP_MULTI_AS = """
|
||||||
|
!<bgp>
|
||||||
|
bgp as-notation plain
|
||||||
|
router bgp 64900
|
||||||
|
neighbor 2c0f:f7c0:0:500::50:2 remote-as 64900
|
||||||
|
neighbor 2c0f:f7c0:0:500::50:2 update-source loopback130
|
||||||
|
neighbor 172.16.0.131 remote-as 64900
|
||||||
|
neighbor 172.16.0.131 update-source loopback130
|
||||||
|
neighbor 10.229.147.93 remote-as 64900
|
||||||
|
neighbor 10.229.147.93 update-source loopback130
|
||||||
|
address-family link-state
|
||||||
|
neighbor 10.229.147.93 activate
|
||||||
|
neighbor 10.229.147.93 route-reflector-client
|
||||||
|
$
|
||||||
|
address-family vpnv4
|
||||||
|
neighbor 172.16.0.131 activate
|
||||||
|
neighbor 172.16.0.131 route-reflector-client
|
||||||
|
$
|
||||||
|
address-family ipv6
|
||||||
|
neighbor 2c0f:f7c0:0:500::50:2 activate
|
||||||
|
$
|
||||||
|
address-family ipv6 sr-policy
|
||||||
|
neighbor 10.229.147.93 activate
|
||||||
|
$
|
||||||
|
address-family vpnv6
|
||||||
|
neighbor 2c0f:f7c0:0:500::50:2 activate
|
||||||
|
$
|
||||||
|
$
|
||||||
|
router bgp 64600
|
||||||
|
neighbor 444::2 remote-as 64600
|
||||||
|
neighbor 444::2 activate disable
|
||||||
|
neighbor 444::2 update-source loopback400
|
||||||
|
address-family l2vpn evpn
|
||||||
|
neighbor 444::2 activate
|
||||||
|
neighbor 444::2 announce-sid
|
||||||
|
$
|
||||||
|
$
|
||||||
|
router bgp 64580
|
||||||
|
neighbor 22:22:22::22 remote-as 64580
|
||||||
|
neighbor 22:22:22::22 activate
|
||||||
|
neighbor 22:22:22::22 update-source loopback0
|
||||||
|
$
|
||||||
|
router bgp 100
|
||||||
|
neighbor MAR_GROUP_V6_1 peer-group
|
||||||
|
neighbor MAR_GROUP_V6_1 remote-as 100
|
||||||
|
neighbor MAR_GROUP_V6_1 activate disable
|
||||||
|
neighbor MAR_GROUP_V6_1 update-source loopback1001
|
||||||
|
neighbor 2408:8121:8400:1:1000::4:0 remote-as 100
|
||||||
|
neighbor 2408:8121:8400:1:1000::4:0 peer-group MAR_GROUP_V6_1
|
||||||
|
neighbor 2408:8121:8400:1:1000::4:0 activate disable
|
||||||
|
neighbor 100.0.0.2 remote-as 100
|
||||||
|
neighbor 100.0.0.2 activate
|
||||||
|
address-family l2vpn evpn
|
||||||
|
neighbor MAR_GROUP_V6_1 activate
|
||||||
|
neighbor MAR_GROUP_V6_1 route-map TO_MAR_EVPN_SRV6_GROUP_1 out
|
||||||
|
$
|
||||||
|
address-family vpnv4
|
||||||
|
neighbor 100.0.0.2 activate
|
||||||
|
$
|
||||||
|
address-family ipv6 vrf SRv6
|
||||||
|
neighbor 2000::1 remote-as 100
|
||||||
|
neighbor 2000::1 activate
|
||||||
|
$
|
||||||
|
$
|
||||||
|
!</bgp>
|
||||||
|
"""
|
||||||
|
|
||||||
_CFG_L2VPN = """
|
_CFG_L2VPN = """
|
||||||
!<l2vpn>
|
!<l2vpn>
|
||||||
vpws VPN_A
|
vpws VPN_A
|
||||||
|
|
@ -289,36 +357,116 @@ class ZteConfigIntentTests(unittest.TestCase):
|
||||||
self.assertNotIn("SKIPME", blob)
|
self.assertNotIn("SKIPME", blob)
|
||||||
self.assertNotIn("password", blob.lower())
|
self.assertNotIn("password", blob.lower())
|
||||||
|
|
||||||
by = {(r["afi"], r["vrf"], r["neighbor"], r["peer_group"]): r for r in rows}
|
by = {
|
||||||
self.assertIn(("vpnv4", "", "10.0.0.1", "CORE_RR"), by)
|
(r["local_as"], r["afi"], r["vrf"], r["neighbor"], r["peer_group"]): r
|
||||||
self.assertEqual(by[("vpnv4", "", "10.0.0.1", "CORE_RR")]["remote_as"], "65001")
|
for r in rows
|
||||||
self.assertEqual(by[("vpnv4", "", "10.0.0.1", "CORE_RR")]["activate"], "enable")
|
}
|
||||||
self.assertEqual(by[("vpnv4", "", "10.0.0.1", "CORE_RR")]["update_source"], "loopback1")
|
self.assertIn(("65000", "vpnv4", "", "10.0.0.1", "CORE_RR"), by)
|
||||||
self.assertEqual(by[("vpnv4", "", "10.0.0.1", "CORE_RR")]["route_map_in"], "RM_IN")
|
self.assertEqual(by[("65000", "vpnv4", "", "10.0.0.1", "CORE_RR")]["remote_as"], "65001")
|
||||||
self.assertEqual(by[("vpnv4", "", "10.0.0.1", "CORE_RR")]["route_map_out"], "RM_OUT")
|
self.assertEqual(by[("65000", "vpnv4", "", "10.0.0.1", "CORE_RR")]["activate"], "enable")
|
||||||
|
self.assertEqual(by[("65000", "vpnv4", "", "10.0.0.1", "CORE_RR")]["update_source"], "loopback1")
|
||||||
|
self.assertEqual(by[("65000", "vpnv4", "", "10.0.0.1", "CORE_RR")]["route_map_in"], "RM_IN")
|
||||||
|
self.assertEqual(by[("65000", "vpnv4", "", "10.0.0.1", "CORE_RR")]["route_map_out"], "RM_OUT")
|
||||||
# AF-scoped RM must not bleed into other address-families
|
# AF-scoped RM must not bleed into other address-families
|
||||||
self.assertEqual(by[("l2vpn-evpn", "", "10.0.0.1", "CORE_RR")]["route_map_in"], "RM_IN")
|
self.assertEqual(by[("65000", "l2vpn-evpn", "", "10.0.0.1", "CORE_RR")]["route_map_in"], "RM_IN")
|
||||||
self.assertEqual(by[("l2vpn-evpn", "", "10.0.0.1", "CORE_RR")]["route_map_out"], "")
|
self.assertEqual(by[("65000", "l2vpn-evpn", "", "10.0.0.1", "CORE_RR")]["route_map_out"], "")
|
||||||
# peer-group name is not an IP → neighbor empty, peer_group set
|
# peer-group name is not an IP → neighbor empty, peer_group set
|
||||||
self.assertIn(("vpnv4", "", "", "CORE_RR"), by)
|
self.assertIn(("65000", "vpnv4", "", "", "CORE_RR"), by)
|
||||||
self.assertEqual(by[("vpnv4", "", "", "CORE_RR")]["remote_as"], "65009")
|
self.assertEqual(by[("65000", "vpnv4", "", "", "CORE_RR")]["remote_as"], "65009")
|
||||||
# Global AF expands peer-group members (10.0.0.9 only via group activate)
|
# Global AF expands peer-group members (10.0.0.9 only via group activate)
|
||||||
self.assertIn(("vpnv4", "", "10.0.0.9", "CORE_RR"), by)
|
self.assertIn(("65000", "vpnv4", "", "10.0.0.9", "CORE_RR"), by)
|
||||||
self.assertEqual(by[("vpnv4", "", "10.0.0.9", "CORE_RR")]["remote_as"], "65019")
|
self.assertEqual(by[("65000", "vpnv4", "", "10.0.0.9", "CORE_RR")]["remote_as"], "65019")
|
||||||
self.assertEqual(by[("vpnv4", "", "10.0.0.9", "CORE_RR")]["activate"], "enable")
|
self.assertEqual(by[("65000", "vpnv4", "", "10.0.0.9", "CORE_RR")]["activate"], "enable")
|
||||||
self.assertEqual(by[("ipv4", "CUST_A", "10.0.0.2", "")]["remote_as"], "65003")
|
self.assertEqual(by[("65000", "ipv4", "CUST_A", "10.0.0.2", "")]["remote_as"], "65003")
|
||||||
# Global ipv4 AF: same peer-group expand as vpnv4 (not VRF)
|
# Global ipv4 AF: same peer-group expand as vpnv4 (not VRF)
|
||||||
self.assertIn(("ipv4", "", "", "CORE_RR"), by)
|
self.assertIn(("65000", "ipv4", "", "", "CORE_RR"), by)
|
||||||
self.assertIn(("ipv4", "", "10.0.0.1", "CORE_RR"), by)
|
self.assertIn(("65000", "ipv4", "", "10.0.0.1", "CORE_RR"), by)
|
||||||
self.assertIn(("ipv4", "", "10.0.0.9", "CORE_RR"), by)
|
self.assertIn(("65000", "ipv4", "", "10.0.0.9", "CORE_RR"), by)
|
||||||
# VRF must not pick up global peer-group members
|
# VRF must not pick up global peer-group members
|
||||||
self.assertNotIn(("ipv4", "CUST_A", "10.0.0.9", "CORE_RR"), by)
|
self.assertNotIn(("65000", "ipv4", "CUST_A", "10.0.0.9", "CORE_RR"), by)
|
||||||
self.assertNotIn(("ipv4", "CUST_A", "10.0.0.1", "CORE_RR"), by)
|
self.assertNotIn(("65000", "ipv4", "CUST_A", "10.0.0.1", "CORE_RR"), by)
|
||||||
self.assertIn(("l2vpn-evpn", "", "10.0.0.1", "CORE_RR"), by)
|
self.assertIn(("65000", "l2vpn-evpn", "", "10.0.0.1", "CORE_RR"), by)
|
||||||
# global IPv6 neighbor without AF activate
|
# global IPv6 neighbor without AF activate
|
||||||
self.assertIn(("global", "", "FC00:1::1", ""), by)
|
self.assertIn(("65000", "global", "", "FC00:1::1", ""), by)
|
||||||
# no name left in neighbor column
|
# no name left in neighbor column
|
||||||
self.assertTrue(all((not r["neighbor"]) or _is_ip_neighbor(r["neighbor"]) for r in rows))
|
self.assertTrue(all((not r["neighbor"]) or _is_ip_neighbor(r["neighbor"]) for r in rows))
|
||||||
|
self.assertTrue(all(r["local_as"] == "65000" for r in rows))
|
||||||
|
|
||||||
|
def test_config_bgp_peer_multi_local_as(self) -> None:
|
||||||
|
rows = normalize_config_bgp_peer(
|
||||||
|
raw_text=_CFG_BGP_MULTI_AS, command="show running-config bgp"
|
||||||
|
)
|
||||||
|
local_ases = {r["local_as"] for r in rows}
|
||||||
|
self.assertEqual(local_ases, {"64900", "64600", "64580", "100"})
|
||||||
|
|
||||||
|
by = {
|
||||||
|
(r["local_as"], r["afi"], r["vrf"], r["neighbor"], r["peer_group"]): r
|
||||||
|
for r in rows
|
||||||
|
}
|
||||||
|
# AS 64900 AF activates
|
||||||
|
self.assertEqual(
|
||||||
|
by[("64900", "vpnv4", "", "172.16.0.131", "")]["remote_as"], "64900"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
by[("64900", "vpnv4", "", "172.16.0.131", "")]["update_source"],
|
||||||
|
"loopback130",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
by[("64900", "ipv6", "", "2c0f:f7c0:0:500::50:2", "")]["activate"], "enable"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
by[("64900", "vpnv6", "", "2c0f:f7c0:0:500::50:2", "")]["activate"], "enable"
|
||||||
|
)
|
||||||
|
self.assertIn(("64900", "link-state", "", "10.229.147.93", ""), by)
|
||||||
|
# Multi-word AF must not collapse into plain ipv6
|
||||||
|
self.assertIn(("64900", "ipv6-sr-policy", "", "10.229.147.93", ""), by)
|
||||||
|
self.assertNotEqual(
|
||||||
|
by[("64900", "ipv6-sr-policy", "", "10.229.147.93", "")]["afi"],
|
||||||
|
"ipv6",
|
||||||
|
)
|
||||||
|
# VRF CE under AS 100 (not 64900)
|
||||||
|
self.assertEqual(
|
||||||
|
by[("100", "ipv6", "SRv6", "2000::1", "")]["remote_as"], "100"
|
||||||
|
)
|
||||||
|
self.assertNotIn(("64900", "ipv6", "SRv6", "2000::1", ""), by)
|
||||||
|
# AS 64600 EVPN peer (not mixed into 64900)
|
||||||
|
self.assertEqual(
|
||||||
|
by[("64600", "l2vpn-evpn", "", "444::2", "")]["remote_as"], "64600"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
by[("64600", "l2vpn-evpn", "", "444::2", "")]["update_source"],
|
||||||
|
"loopback400",
|
||||||
|
)
|
||||||
|
self.assertNotIn(("64900", "l2vpn-evpn", "", "444::2", ""), by)
|
||||||
|
# AS 64580: top-level activate only → global row
|
||||||
|
self.assertIn(("64580", "global", "", "22:22:22::22", ""), by)
|
||||||
|
self.assertEqual(
|
||||||
|
by[("64580", "global", "", "22:22:22::22", "")]["update_source"],
|
||||||
|
"loopback0",
|
||||||
|
)
|
||||||
|
# AS 100: peer-group expand on EVPN
|
||||||
|
self.assertIn(("100", "l2vpn-evpn", "", "", "MAR_GROUP_V6_1"), by)
|
||||||
|
self.assertIn(
|
||||||
|
("100", "l2vpn-evpn", "", "2408:8121:8400:1:1000::4:0", "MAR_GROUP_V6_1"),
|
||||||
|
by,
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
by[
|
||||||
|
("100", "l2vpn-evpn", "", "2408:8121:8400:1:1000::4:0", "MAR_GROUP_V6_1")
|
||||||
|
]["route_map_out"],
|
||||||
|
"TO_MAR_EVPN_SRV6_GROUP_1",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
by[("100", "vpnv4", "", "100.0.0.2", "")]["remote_as"], "100"
|
||||||
|
)
|
||||||
|
# Peer-group members must not leak across local AS
|
||||||
|
self.assertNotIn(
|
||||||
|
("64900", "l2vpn-evpn", "", "2408:8121:8400:1:1000::4:0", "MAR_GROUP_V6_1"),
|
||||||
|
by,
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
all((not r["neighbor"]) or _is_ip_neighbor(r["neighbor"]) for r in rows)
|
||||||
|
)
|
||||||
|
|
||||||
def test_config_l2vpn_pw(self) -> None:
|
def test_config_l2vpn_pw(self) -> None:
|
||||||
rows = normalize_config_l2vpn_pw(
|
rows = normalize_config_l2vpn_pw(
|
||||||
|
|
|
||||||
|
|
@ -233,27 +233,33 @@ class ZteExtendedParserTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_bgp_peer_ipv4_and_ipv6_wrap(self) -> None:
|
def test_bgp_peer_ipv4_and_ipv6_wrap(self) -> None:
|
||||||
v4 = normalize_bgp_peer(
|
v4 = normalize_bgp_peer(
|
||||||
raw_text=_BGP_V4_SUMMARY, command="show bgp vpnv4 unicast summary"
|
raw_text=_BGP_V4_SUMMARY,
|
||||||
|
command="show bgp vpnv4 unicast summary as 64900 | one-line",
|
||||||
|
params={"local_as": "64900"},
|
||||||
)
|
)
|
||||||
self.assertEqual(len(v4), 2)
|
self.assertEqual(len(v4), 2)
|
||||||
self.assertEqual(v4[0]["afi"], "vpnv4")
|
self.assertEqual(v4[0]["afi"], "vpnv4")
|
||||||
|
self.assertEqual(v4[0]["local_as"], "64900")
|
||||||
self.assertEqual(v4[0]["state"], "Established")
|
self.assertEqual(v4[0]["state"], "Established")
|
||||||
self.assertEqual(v4[0]["pfx_rcd"], "5")
|
self.assertEqual(v4[0]["pfx_rcd"], "5")
|
||||||
self.assertEqual(v4[1]["state"], "Connect")
|
self.assertEqual(v4[1]["state"], "Connect")
|
||||||
|
|
||||||
v6 = normalize_bgp_peer(
|
v6 = normalize_bgp_peer(
|
||||||
raw_text=_BGP_V6_SUMMARY, command="show bgp vpnv6 unicast summary"
|
raw_text=_BGP_V6_SUMMARY,
|
||||||
|
command="show bgp vpnv6 unicast summary as 64900",
|
||||||
)
|
)
|
||||||
self.assertEqual(len(v6), 2)
|
self.assertEqual(len(v6), 2)
|
||||||
self.assertTrue(all(r["afi"] == "vpnv6" for r in v6))
|
self.assertTrue(all(r["afi"] == "vpnv6" for r in v6))
|
||||||
|
self.assertTrue(all(r["local_as"] == "64900" for r in v6))
|
||||||
self.assertEqual(v6[0]["neighbor"].upper(), "FC00:1::1")
|
self.assertEqual(v6[0]["neighbor"].upper(), "FC00:1::1")
|
||||||
|
|
||||||
vrf = normalize_bgp_peer(
|
vrf = normalize_bgp_peer(
|
||||||
raw_text=_BGP_V4_SUMMARY,
|
raw_text=_BGP_V4_SUMMARY,
|
||||||
command="show bgp vpnv4 unicast vrf CUST_A summary",
|
command="show bgp vpnv4 unicast vrf CUST_A summary as 65000",
|
||||||
params={"vrf": "CUST_A"},
|
params={"vrf": "CUST_A", "local_as": "65000"},
|
||||||
)
|
)
|
||||||
self.assertTrue(all(r["vrf"] == "CUST_A" for r in vrf))
|
self.assertTrue(all(r["vrf"] == "CUST_A" for r in vrf))
|
||||||
|
self.assertTrue(all(r["local_as"] == "65000" for r in vrf))
|
||||||
|
|
||||||
# Real wrapped vpnv6 summary + IPv6 neighbor in/out routes
|
# Real wrapped vpnv6 summary + IPv6 neighbor in/out routes
|
||||||
sample_dir = Path(__file__).resolve().parents[2] / "test" / "show-zte"
|
sample_dir = Path(__file__).resolve().parents[2] / "test" / "show-zte"
|
||||||
|
|
@ -425,12 +431,14 @@ class ZteExtendedParserTests(unittest.TestCase):
|
||||||
intent = normalize_config_bgp_peer(
|
intent = normalize_config_bgp_peer(
|
||||||
raw_text="""
|
raw_text="""
|
||||||
!<bgp>
|
!<bgp>
|
||||||
|
router bgp 65000
|
||||||
neighbor 10.0.0.1 remote-as 65001
|
neighbor 10.0.0.1 remote-as 65001
|
||||||
address-family ipv4 vrf CUST_A
|
address-family ipv4 vrf CUST_A
|
||||||
neighbor 10.0.0.1 activate
|
neighbor 10.0.0.1 activate
|
||||||
neighbor 10.0.0.1 route-map RM_IN in
|
neighbor 10.0.0.1 route-map RM_IN in
|
||||||
$
|
$
|
||||||
$
|
$
|
||||||
|
!</bgp>
|
||||||
""",
|
""",
|
||||||
command="show running-config bgp",
|
command="show running-config bgp",
|
||||||
)
|
)
|
||||||
|
|
@ -458,6 +466,7 @@ $
|
||||||
intent2 = normalize_config_bgp_peer(
|
intent2 = normalize_config_bgp_peer(
|
||||||
raw_text="""
|
raw_text="""
|
||||||
!<bgp>
|
!<bgp>
|
||||||
|
router bgp 65000
|
||||||
neighbor 10.0.0.1 remote-as 65001
|
neighbor 10.0.0.1 remote-as 65001
|
||||||
address-family ipv4 vrf CUST_A
|
address-family ipv4 vrf CUST_A
|
||||||
neighbor 10.0.0.1 activate
|
neighbor 10.0.0.1 activate
|
||||||
|
|
@ -468,6 +477,7 @@ $
|
||||||
neighbor 10.0.0.1 route-map RM_B in
|
neighbor 10.0.0.1 route-map RM_B in
|
||||||
$
|
$
|
||||||
$
|
$
|
||||||
|
!</bgp>
|
||||||
""",
|
""",
|
||||||
command="show running-config bgp",
|
command="show running-config bgp",
|
||||||
)
|
)
|
||||||
|
|
@ -493,6 +503,7 @@ $
|
||||||
intent3 = normalize_config_bgp_peer(
|
intent3 = normalize_config_bgp_peer(
|
||||||
raw_text="""
|
raw_text="""
|
||||||
!<bgp>
|
!<bgp>
|
||||||
|
router bgp 65000
|
||||||
neighbor 10.0.0.1 remote-as 65001
|
neighbor 10.0.0.1 remote-as 65001
|
||||||
address-family ipv4
|
address-family ipv4
|
||||||
neighbor 10.0.0.1 activate
|
neighbor 10.0.0.1 activate
|
||||||
|
|
@ -503,6 +514,7 @@ $
|
||||||
neighbor 10.0.0.1 route-map RM_VPNV4 in
|
neighbor 10.0.0.1 route-map RM_VPNV4 in
|
||||||
$
|
$
|
||||||
$
|
$
|
||||||
|
!</bgp>
|
||||||
""",
|
""",
|
||||||
command="show running-config bgp",
|
command="show running-config bgp",
|
||||||
)
|
)
|
||||||
|
|
@ -688,28 +700,32 @@ $
|
||||||
self.assertEqual(p.aux_commands[0].profile_id, "zte.config_bgp_peer")
|
self.assertEqual(p.aux_commands[0].profile_id, "zte.config_bgp_peer")
|
||||||
pairs = expand_from_bindings(
|
pairs = expand_from_bindings(
|
||||||
profile=p,
|
profile=p,
|
||||||
bindings=[{"vrf": "CUST_A", "neighbor": "10.0.0.1"}],
|
bindings=[{"vrf": "CUST_A", "neighbor": "10.0.0.1", "local_as": "64900"}],
|
||||||
)
|
)
|
||||||
self.assertEqual(len(pairs), 1)
|
self.assertEqual(len(pairs), 1)
|
||||||
cmd, params = pairs[0]
|
cmd, params = pairs[0]
|
||||||
self.assertIn("vrf CUST_A", cmd)
|
self.assertIn("vrf CUST_A", cmd)
|
||||||
self.assertIn("10.0.0.1", cmd)
|
self.assertIn("10.0.0.1", cmd)
|
||||||
|
self.assertIn("as 64900", cmd)
|
||||||
hit = match_command(vendor_key="zte", command=cmd)
|
hit = match_command(vendor_key="zte", command=cmd)
|
||||||
self.assertIsNotNone(hit)
|
self.assertIsNotNone(hit)
|
||||||
assert hit is not None
|
assert hit is not None
|
||||||
self.assertEqual(hit.params.get("vrf"), "CUST_A")
|
self.assertEqual(hit.params.get("vrf"), "CUST_A")
|
||||||
self.assertEqual(hit.params.get("neighbor"), "10.0.0.1")
|
self.assertEqual(hit.params.get("neighbor"), "10.0.0.1")
|
||||||
|
self.assertEqual(hit.params.get("local_as"), "64900")
|
||||||
|
|
||||||
# BGP VRF summary / Forwarding VRF: required bind + config_vrf aux
|
# Forwarding VRF: required bind + config_vrf aux
|
||||||
from netx_api.biz_state.command_match import expand_bindings_from_discover_records
|
# BGP VRF summary: optional (local_as, vrf) from config_bgp_peer
|
||||||
|
from netx_api.biz_state.command_match import (
|
||||||
|
EXPAND_ALL_COMMAND,
|
||||||
|
expand_bindings_from_discover_records,
|
||||||
|
filter_discover_records,
|
||||||
|
normalize_binding_dicts,
|
||||||
|
shared_discover_placeholders,
|
||||||
|
)
|
||||||
from netx_api.biz_state.collect_session import resolve_aux_command
|
from netx_api.biz_state.collect_session import resolve_aux_command
|
||||||
|
|
||||||
for pid in (
|
for pid in ("zte.ip_route_vrf", "zte.ipv6_route_vrf"):
|
||||||
"zte.bgp_vpnv4_vrf_summary",
|
|
||||||
"zte.bgp_vpnv6_vrf_summary",
|
|
||||||
"zte.ip_route_vrf",
|
|
||||||
"zte.ipv6_route_vrf",
|
|
||||||
):
|
|
||||||
prof = get_profile(pid)
|
prof = get_profile(pid)
|
||||||
assert prof is not None
|
assert prof is not None
|
||||||
self.assertTrue(prof.placeholders)
|
self.assertTrue(prof.placeholders)
|
||||||
|
|
@ -719,43 +735,76 @@ $
|
||||||
|
|
||||||
v4 = get_profile("zte.bgp_vpnv4_vrf_summary")
|
v4 = get_profile("zte.bgp_vpnv4_vrf_summary")
|
||||||
assert v4 is not None
|
assert v4 is not None
|
||||||
self.assertEqual([a.key for a in v4.aux_commands], ["config_vrf"])
|
self.assertEqual([a.key for a in v4.aux_commands], ["config_bgp_peer"])
|
||||||
with self.assertRaises(ValueError):
|
self.assertFalse(any(ph.required for ph in v4.placeholders))
|
||||||
expand_from_bindings(profile=v4, bindings=[])
|
expand_all = expand_from_bindings(profile=v4, bindings=[])
|
||||||
bound = expand_from_bindings(profile=v4, bindings=[{"vrf": "CUST_A"}])
|
self.assertEqual(expand_all[0][0], EXPAND_ALL_COMMAND)
|
||||||
self.assertEqual(bound[0][0], "show bgp vpnv4 unicast vrf CUST_A summary | one-line")
|
bound = expand_from_bindings(
|
||||||
ra_cfg = resolve_aux_command(
|
profile=v4, bindings=[{"vrf": "CUST_A", "local_as": "64900"}]
|
||||||
next(a for a in v4.aux_commands if a.key == "config_vrf"),
|
|
||||||
params={"vrf": "CUST_A"},
|
|
||||||
)
|
)
|
||||||
self.assertEqual(ra_cfg.command, "show running-config vrf | one-line")
|
self.assertEqual(
|
||||||
|
bound[0][0],
|
||||||
|
"show bgp vpnv4 unicast vrf CUST_A summary as 64900 | one-line",
|
||||||
|
)
|
||||||
|
# Optional local_as omitted → strip `` as <local_as>``
|
||||||
|
bound_no_as = expand_from_bindings(profile=v4, bindings=[{"vrf": "CUST_A"}])
|
||||||
|
self.assertEqual(
|
||||||
|
bound_no_as[0][0],
|
||||||
|
"show bgp vpnv4 unicast vrf CUST_A summary | one-line",
|
||||||
|
)
|
||||||
|
ra_cfg = resolve_aux_command(
|
||||||
|
next(a for a in v4.aux_commands if a.key == "config_bgp_peer"),
|
||||||
|
params={"vrf": "CUST_A", "local_as": "64900"},
|
||||||
|
)
|
||||||
|
self.assertEqual(ra_cfg.command, "show running-config bgp | one-line")
|
||||||
|
|
||||||
v6 = get_profile("zte.bgp_vpnv6_vrf_summary")
|
v6 = get_profile("zte.bgp_vpnv6_vrf_summary")
|
||||||
assert v6 is not None
|
assert v6 is not None
|
||||||
self.assertEqual([a.key for a in v6.aux_commands], ["config_vrf"])
|
self.assertEqual([a.key for a in v6.aux_commands], ["config_bgp_peer"])
|
||||||
|
|
||||||
records = [
|
peer_vrf_recs = [
|
||||||
|
{"local_as": "64900", "afi": "ipv4", "vrf": "CUST_A", "neighbor": "10.0.0.2"},
|
||||||
|
{"local_as": "64900", "afi": "ipv4", "vrf": "CUST_B", "neighbor": "10.0.0.3"},
|
||||||
|
{"local_as": "64900", "afi": "ipv6", "vrf": "CUST_A", "neighbor": "FC00::2"},
|
||||||
|
{"local_as": "64900", "afi": "ipv6", "vrf": "CUST_C", "neighbor": "FC00::3"},
|
||||||
|
{"local_as": "65000", "afi": "ipv4", "vrf": "CUST_A", "neighbor": "10.0.0.9"},
|
||||||
|
]
|
||||||
|
ipv4_pairs = expand_bindings_from_discover_records(
|
||||||
|
profile=v4, records=peer_vrf_recs
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
{(p[1]["local_as"], p[1]["vrf"]) for p in ipv4_pairs},
|
||||||
|
{("64900", "CUST_A"), ("64900", "CUST_B"), ("65000", "CUST_A")},
|
||||||
|
)
|
||||||
|
ipv6_pairs = expand_bindings_from_discover_records(
|
||||||
|
profile=v6, records=peer_vrf_recs
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
{(p[1]["local_as"], p[1]["vrf"]) for p in ipv6_pairs},
|
||||||
|
{("64900", "CUST_A"), ("64900", "CUST_C")},
|
||||||
|
)
|
||||||
|
|
||||||
|
# FIB VRF still expands from config_vrf records
|
||||||
|
ip_vrf = get_profile("zte.ip_route_vrf")
|
||||||
|
assert ip_vrf is not None
|
||||||
|
vrf_records = [
|
||||||
{"vrf_name": "CUST_A", "address_families": "ipv4,ipv6", "rd": "100:1"},
|
{"vrf_name": "CUST_A", "address_families": "ipv4,ipv6", "rd": "100:1"},
|
||||||
{"vrf_name": "CUST_B", "address_families": "ipv4", "rd": "100:2"},
|
{"vrf_name": "CUST_B", "address_families": "ipv4", "rd": "100:2"},
|
||||||
{"vrf_name": "CUST_C", "address_families": "ipv6", "rd": "100:3"},
|
{"vrf_name": "CUST_C", "address_families": "ipv6", "rd": "100:3"},
|
||||||
]
|
]
|
||||||
ipv4_pairs = expand_bindings_from_discover_records(profile=v4, records=records)
|
fib_pairs = expand_bindings_from_discover_records(
|
||||||
self.assertEqual({p[1]["vrf"] for p in ipv4_pairs}, {"CUST_A", "CUST_B"})
|
profile=ip_vrf, records=vrf_records
|
||||||
ipv6_pairs = expand_bindings_from_discover_records(profile=v6, records=records)
|
)
|
||||||
self.assertEqual({p[1]["vrf"] for p in ipv6_pairs}, {"CUST_A", "CUST_C"})
|
self.assertEqual({p[1]["vrf"] for p in fib_pairs}, {"CUST_A", "CUST_B"})
|
||||||
|
|
||||||
# BGP neighbor in/out: discover from config_bgp_peer with AF filters
|
# BGP neighbor in/out: discover from config_bgp_peer with AF filters
|
||||||
from netx_api.biz_state.command_match import (
|
|
||||||
filter_discover_records,
|
|
||||||
normalize_binding_dicts,
|
|
||||||
shared_discover_placeholders,
|
|
||||||
)
|
|
||||||
|
|
||||||
glob_v4 = get_profile("zte.bgp_vpnv4_neighbor_in")
|
glob_v4 = get_profile("zte.bgp_vpnv4_neighbor_in")
|
||||||
assert glob_v4 is not None
|
assert glob_v4 is not None
|
||||||
self.assertEqual(glob_v4.placeholders[0].discover_profile_id, "zte.config_bgp_peer")
|
self.assertEqual(glob_v4.placeholders[0].discover_profile_id, "zte.config_bgp_peer")
|
||||||
self.assertEqual(glob_v4.placeholders[0].discover_filter_contains, "vpnv4")
|
self.assertEqual(glob_v4.placeholders[0].discover_filter_contains, "vpnv4")
|
||||||
self.assertEqual(glob_v4.placeholders[0].bind_mode, "discover_select")
|
self.assertEqual(glob_v4.placeholders[0].bind_mode, "discover_select")
|
||||||
|
self.assertTrue(any(ph.name == "local_as" for ph in glob_v4.placeholders))
|
||||||
|
self.assertIn("as <local_as>", glob_v4.command_template)
|
||||||
|
|
||||||
glob_v6 = get_profile("zte.bgp_vpnv6_neighbor_in")
|
glob_v6 = get_profile("zte.bgp_vpnv6_neighbor_in")
|
||||||
assert glob_v6 is not None
|
assert glob_v6 is not None
|
||||||
|
|
@ -767,13 +816,57 @@ $
|
||||||
self.assertEqual(glob_ipv4.placeholders[0].discover_require_empty, "vrf")
|
self.assertEqual(glob_ipv4.placeholders[0].discover_require_empty, "vrf")
|
||||||
|
|
||||||
peer_recs = [
|
peer_recs = [
|
||||||
{"afi": "vpnv4", "vrf": "", "neighbor": "10.0.0.1", "remote_as": "65001"},
|
{
|
||||||
{"afi": "vpnv4", "vrf": "", "neighbor": "", "peer_group": "CORE_RR", "remote_as": "65009"},
|
"local_as": "64900",
|
||||||
{"afi": "vpnv4", "vrf": "", "neighbor": "10.0.0.9", "peer_group": "CORE_RR", "remote_as": "65019"},
|
"afi": "vpnv4",
|
||||||
{"afi": "vpnv6", "vrf": "", "neighbor": "FC00::1", "remote_as": "65002"},
|
"vrf": "",
|
||||||
{"afi": "ipv4", "vrf": "CUST_A", "neighbor": "10.0.0.2", "remote_as": "65003"},
|
"neighbor": "10.0.0.1",
|
||||||
{"afi": "ipv4", "vrf": "", "neighbor": "10.0.0.8", "remote_as": "65004"},
|
"remote_as": "65001",
|
||||||
{"afi": "ipv6", "vrf": "CUST_B", "neighbor": "FC00::2", "remote_as": "65005"},
|
},
|
||||||
|
{
|
||||||
|
"local_as": "64900",
|
||||||
|
"afi": "vpnv4",
|
||||||
|
"vrf": "",
|
||||||
|
"neighbor": "",
|
||||||
|
"peer_group": "CORE_RR",
|
||||||
|
"remote_as": "65009",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local_as": "64900",
|
||||||
|
"afi": "vpnv4",
|
||||||
|
"vrf": "",
|
||||||
|
"neighbor": "10.0.0.9",
|
||||||
|
"peer_group": "CORE_RR",
|
||||||
|
"remote_as": "65019",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local_as": "64900",
|
||||||
|
"afi": "vpnv6",
|
||||||
|
"vrf": "",
|
||||||
|
"neighbor": "FC00::1",
|
||||||
|
"remote_as": "65002",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local_as": "64900",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"vrf": "CUST_A",
|
||||||
|
"neighbor": "10.0.0.2",
|
||||||
|
"remote_as": "65003",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local_as": "64900",
|
||||||
|
"afi": "ipv4",
|
||||||
|
"vrf": "",
|
||||||
|
"neighbor": "10.0.0.8",
|
||||||
|
"remote_as": "65004",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local_as": "64900",
|
||||||
|
"afi": "ipv6",
|
||||||
|
"vrf": "CUST_B",
|
||||||
|
"neighbor": "FC00::2",
|
||||||
|
"remote_as": "65005",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
filter_discover_records(peer_recs, glob_v4.placeholders[0]),
|
filter_discover_records(peer_recs, glob_v4.placeholders[0]),
|
||||||
|
|
@ -799,33 +892,40 @@ $
|
||||||
)
|
)
|
||||||
|
|
||||||
shared = shared_discover_placeholders(vrf_nei)
|
shared = shared_discover_placeholders(vrf_nei)
|
||||||
self.assertEqual(len(shared), 2)
|
self.assertEqual(len(shared), 3)
|
||||||
self.assertTrue(all(ph.discover_profile_id == "zte.config_bgp_peer" for ph in shared))
|
self.assertTrue(all(ph.discover_profile_id == "zte.config_bgp_peer" for ph in shared))
|
||||||
pair_cmds = expand_bindings_from_discover_records(profile=vrf_nei, records=peer_recs)
|
pair_cmds = expand_bindings_from_discover_records(profile=vrf_nei, records=peer_recs)
|
||||||
self.assertEqual(len(pair_cmds), 1)
|
self.assertEqual(len(pair_cmds), 1)
|
||||||
self.assertEqual(pair_cmds[0][1], {"vrf": "CUST_A", "neighbor": "10.0.0.2"})
|
self.assertEqual(
|
||||||
|
pair_cmds[0][1],
|
||||||
|
{"vrf": "CUST_A", "neighbor": "10.0.0.2", "local_as": "64900"},
|
||||||
|
)
|
||||||
self.assertIn("vrf CUST_A", pair_cmds[0][0])
|
self.assertIn("vrf CUST_A", pair_cmds[0][0])
|
||||||
self.assertIn("10.0.0.2", pair_cmds[0][0])
|
self.assertIn("10.0.0.2", pair_cmds[0][0])
|
||||||
|
self.assertIn("as 64900", pair_cmds[0][0])
|
||||||
|
|
||||||
# Interleaved placeholder/value rows zip into combined bindings
|
# Interleaved placeholder/value rows zip into combined bindings
|
||||||
zipped = normalize_binding_dicts(
|
zipped = normalize_binding_dicts(
|
||||||
[
|
[
|
||||||
{"placeholder": "vrf", "value": "CUST_A"},
|
{"placeholder": "vrf", "value": "CUST_A"},
|
||||||
{"placeholder": "neighbor", "value": "10.0.0.2"},
|
{"placeholder": "neighbor", "value": "10.0.0.2"},
|
||||||
|
{"placeholder": "local_as", "value": "64900"},
|
||||||
{"placeholder": "vrf", "value": "CUST_B"},
|
{"placeholder": "vrf", "value": "CUST_B"},
|
||||||
{"placeholder": "neighbor", "value": "10.0.0.3"},
|
{"placeholder": "neighbor", "value": "10.0.0.3"},
|
||||||
|
{"placeholder": "local_as", "value": "64900"},
|
||||||
],
|
],
|
||||||
placeholders=vrf_nei.placeholders,
|
placeholders=vrf_nei.placeholders,
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
zipped,
|
zipped,
|
||||||
[
|
[
|
||||||
{"vrf": "CUST_A", "neighbor": "10.0.0.2"},
|
{"vrf": "CUST_A", "neighbor": "10.0.0.2", "local_as": "64900"},
|
||||||
{"vrf": "CUST_B", "neighbor": "10.0.0.3"},
|
{"vrf": "CUST_B", "neighbor": "10.0.0.3", "local_as": "64900"},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
expanded = expand_from_bindings(profile=vrf_nei, bindings=zipped)
|
expanded = expand_from_bindings(profile=vrf_nei, bindings=zipped)
|
||||||
self.assertEqual(len(expanded), 2)
|
self.assertEqual(len(expanded), 2)
|
||||||
|
self.assertTrue(all("as 64900" in c for c, _ in expanded))
|
||||||
|
|
||||||
def test_interface_detail_and_vpnv6_neighbor(self) -> None:
|
def test_interface_detail_and_vpnv6_neighbor(self) -> None:
|
||||||
from netx_api.biz_state.parsers.zte import normalize_interface_detail
|
from netx_api.biz_state.parsers.zte import normalize_interface_detail
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue