Decouple cutover HF from portrait and tighten ops UI.

Portrait stays on biz-monitor; cutover create/ensure owns purpose=cutover_hf slots with project collect override, multi-interval bindings, window pause, and industrial detail UX. Also expand ZTE collect/config parsers and TextFSM for route/BGP/L2VPN/VRRP coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-19 21:11:31 +08:00
parent a65c9fc420
commit 285c19e5a3
67 changed files with 9510 additions and 723 deletions

View file

@ -11,6 +11,14 @@ from .pipeline import prefer_fsm
RULE_KEYS = ("zte_zxros_show_ip_vrf",)
_VRF_LINE_RE = re.compile(
r"^\s*(?P<name>[A-Za-z0-9_./:-]+)\s+"
r"(?P<rd>\d+:\d+|<not set>|\S+:\S+)\s*"
r"(?:(?P<protocols>ipv4(?:,ipv6)?|ipv6(?:,ipv4)?|ipv4|ipv6)\s*)?"
r"(?P<vrf_id>\d+)?\s*$",
re.I,
)
def _map_fsm_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
out: list[dict[str, Any]] = []
@ -22,11 +30,16 @@ def _map_fsm_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
if name in seen:
continue
seen.add(name)
protocols = row_get(r, "PROTOCOLS", "ADDRESS_FAMILY", "protocols")
# Legacy templates may put protocols in INTERFACES
if not protocols:
protocols = row_get(r, "INTERFACES", "interfaces")
out.append(
{
"vrf_name": name[:128],
"rd": row_get(r, "DEFAULT_RD", "RD", "rd")[:64],
"protocols": row_get(r, "PROTOCOLS", "ADDRESS_FAMILY", "INTERFACES", "protocols")[:64],
"rd": row_get(r, "RD", "DEFAULT_RD", "rd")[:64],
"protocols": protocols[:64],
"vrf_id": row_get(r, "VRF_ID", "vrf_id")[:32],
}
)
return out
@ -36,16 +49,23 @@ def _hand_parse(*, raw_text: str, **_kw: Any) -> list[dict[str, Any]]:
out: list[dict[str, Any]] = []
seen: set[str] = set()
for line in str(raw_text or "").splitlines():
m = re.match(r"^\s*([A-Za-z0-9_./:-]+)\s+(\d+:\d+|<not set>|\S+:\S+)\s*", line)
m = _VRF_LINE_RE.match(line)
if not m:
continue
name = m.group(1)
name = m.group("name")
if name.lower() in ("name", "vrf", "vpn-instance", "total"):
continue
if name in seen:
continue
seen.add(name)
out.append({"vrf_name": name[:128], "rd": m.group(2)[:64], "protocols": ""})
out.append(
{
"vrf_name": name[:128],
"rd": (m.group("rd") or "")[:64],
"protocols": (m.group("protocols") or "")[:64],
"vrf_id": (m.group("vrf_id") or "")[:32],
}
)
return out