mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 03:10:46 +08:00
Merge duplicate if-intf checks into Config Interface Intent.
Hide standalone IF VRF from the catalog and point ARP enrich at config_interface so one CLI maps to one check item. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
c1b1c0f75d
commit
1642a0adec
8 changed files with 37 additions and 21 deletions
|
|
@ -21,7 +21,7 @@ Cross-command (multi aux)
|
||||||
1. Implement each aux as a normal status profile (steps 1–4)
|
1. Implement each aux as a normal status profile (steps 1–4)
|
||||||
2. On the primary profile::
|
2. On the primary profile::
|
||||||
|
|
||||||
aux_commands=[AuxCommand(key="if_intf", profile_id="zte.if_intf")]
|
aux_commands=[AuxCommand(key="if_intf", profile_id="zte.config_interface")]
|
||||||
enrich_joins=[EnrichJoin(from_aux="if_intf", on="interface", take=("vrf",))]
|
enrich_joins=[EnrichJoin(from_aux="if_intf", on="interface", take=("vrf",))]
|
||||||
|
|
||||||
3. CollectSession caches identical concrete CLI in one batch; enrich runs after
|
3. CollectSession caches identical concrete CLI in one batch; enrich runs after
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"""ZTE: show arp [| one-line].
|
"""ZTE: show arp [| one-line].
|
||||||
|
|
||||||
Pipeline: TextFSM ``zte_zxros_show_arp`` → map → hand fallback.
|
Pipeline: TextFSM ``zte_zxros_show_arp`` → map → hand fallback.
|
||||||
Cross-command VRF comes from profile ``enrich_joins`` (if_intf), not here.
|
Cross-command VRF comes from profile ``enrich_joins`` (config_interface aux), not here.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
|
||||||
|
|
@ -605,14 +605,14 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
command_template="show arp | one-line",
|
command_template="show arp | one-line",
|
||||||
match=r"(?i)^\s*show\s+arp(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+arp(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show arp",
|
textfsm_command="show arp",
|
||||||
description="ARP entries (IP/MAC/interface); VRF from if-intf aux.",
|
description="ARP entries (IP/MAC/interface); VRF from config_interface aux.",
|
||||||
fields=list(_ARP_FIELDS),
|
fields=list(_ARP_FIELDS),
|
||||||
tags=["arp", "l3", "status"],
|
tags=["arp", "l3", "status"],
|
||||||
sort_order=320,
|
sort_order=320,
|
||||||
enabled=True,
|
enabled=True,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
aux_commands=[
|
aux_commands=[
|
||||||
AuxCommand(key="if_intf", profile_id="zte.if_intf"),
|
AuxCommand(key="if_intf", profile_id="zte.config_interface"),
|
||||||
],
|
],
|
||||||
enrich_joins=[
|
enrich_joins=[
|
||||||
EnrichJoin(from_aux="if_intf", on="interface", take=("vrf",)),
|
EnrichJoin(from_aux="if_intf", on="interface", take=("vrf",)),
|
||||||
|
|
@ -628,13 +628,13 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
match=r"(?i)^\s*show\s+running-config\s+if-intf\s*$",
|
match=r"(?i)^\s*show\s+running-config\s+if-intf\s*$",
|
||||||
textfsm_command="show running-config if-intf",
|
textfsm_command="show running-config if-intf",
|
||||||
description=(
|
description=(
|
||||||
"Interface VRF map for ARP enrich (aux). "
|
"Compat/aux-only interface→VRF map (disabled in catalog). "
|
||||||
"Free-form CLI match prefers zte.config_interface (longer regex)."
|
"Use Config Interface Intent (zte.config_interface) instead."
|
||||||
),
|
),
|
||||||
fields=list(_IF_INTF_FIELDS),
|
fields=list(_IF_INTF_FIELDS),
|
||||||
tags=["interface", "vrf", "config", "status", "aux"],
|
tags=["interface", "vrf", "config", "status", "aux"],
|
||||||
sort_order=325,
|
sort_order=325,
|
||||||
enabled=True,
|
enabled=False,
|
||||||
kind="collect",
|
kind="collect",
|
||||||
),
|
),
|
||||||
ParseProfile(
|
ParseProfile(
|
||||||
|
|
@ -1202,7 +1202,10 @@ def _zte_status_profiles() -> list[ParseProfile]:
|
||||||
command_template="show running-config if-intf",
|
command_template="show running-config if-intf",
|
||||||
match=r"(?i)^\s*show\s+running-config\s+if-intf(?:\s*\|\s*one-line)?\s*$",
|
match=r"(?i)^\s*show\s+running-config\s+if-intf(?:\s*\|\s*one-line)?\s*$",
|
||||||
textfsm_command="show running-config if-intf",
|
textfsm_command="show running-config if-intf",
|
||||||
description="Interface VRF/IP/admin intent (richer than if_intf aux map).",
|
description=(
|
||||||
|
"Interface VRF/IP/admin intent; also ARP VRF enrich source "
|
||||||
|
"(replaces standalone IF VRF / if_intf check)."
|
||||||
|
),
|
||||||
fields=list(_CONFIG_IFACE_FIELDS),
|
fields=list(_CONFIG_IFACE_FIELDS),
|
||||||
tags=["config", "interface", "intent"],
|
tags=["config", "interface", "intent"],
|
||||||
sort_order=510,
|
sort_order=510,
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ ParseProfile (命令 + schema + aux + enrich)
|
||||||
| `netx_api/biz_state/enrich.py` | 声明式等值 join |
|
| `netx_api/biz_state/enrich.py` | 声明式等值 join |
|
||||||
| `netx_api/biz_state/collect_runner.py` | 任务会话采集落库 |
|
| `netx_api/biz_state/collect_runner.py` | 任务会话采集落库 |
|
||||||
|
|
||||||
样板:`zte/interface_brief`(单命令)、`zte/if_intf`(可复用辅表)、`zte.arp`(主+辅+enrich)。
|
样板:`zte/interface_brief`(单命令)、`zte.config_interface`(可复用辅表)、`zte.arp`(主+辅+enrich)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ ParseProfile (命令 + schema + aux + enrich)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
aux_commands=[
|
aux_commands=[
|
||||||
AuxCommand(key="if_intf", profile_id="zte.if_intf"),
|
AuxCommand(key="if_intf", profile_id="zte.config_interface"),
|
||||||
# 多辅:再 append;key 唯一
|
# 多辅:再 append;key 唯一
|
||||||
],
|
],
|
||||||
enrich_joins=[
|
enrich_joins=[
|
||||||
|
|
@ -159,5 +159,5 @@ Sheet(`metrics_json[]`)能力:
|
||||||
- Enrich:`biz_state/enrich.py`
|
- Enrich:`biz_state/enrich.py`
|
||||||
- Session:`biz_state/collect_session.py`
|
- Session:`biz_state/collect_session.py`
|
||||||
- Compare:`biz_state/compare_rules.py`、`compare_engine.py`、`compare_service.py`
|
- Compare:`biz_state/compare_rules.py`、`compare_engine.py`、`compare_service.py`
|
||||||
- ARP+VRF:`profiles.py` → `zte.arp`;`parsers/zte/arp.py`;`parsers/zte/if_intf.py`
|
- ARP+VRF:`profiles.py` → `zte.arp`;`parsers/zte/arp.py`;辅表 `zte.config_interface`(`zte.if_intf` 已禁用仅兼容)
|
||||||
- 模板:`cli_templates/zte/zte_zxros_show_arp.textfsm`、`..._if_intf.textfsm`
|
- 模板:`cli_templates/zte/zte_zxros_show_arp.textfsm`、`..._if_intf.textfsm`
|
||||||
|
|
|
||||||
|
|
@ -59,18 +59,20 @@ class AuxResolveTests(unittest.TestCase):
|
||||||
reload_profiles()
|
reload_profiles()
|
||||||
|
|
||||||
def test_resolve_from_profile_id(self) -> None:
|
def test_resolve_from_profile_id(self) -> None:
|
||||||
ra = resolve_aux_command(AuxCommand(key="if_intf", profile_id="zte.if_intf"))
|
ra = resolve_aux_command(AuxCommand(key="if_intf", profile_id="zte.config_interface"))
|
||||||
self.assertEqual(ra.command, "show running-config if-intf")
|
self.assertEqual(ra.command, "show running-config if-intf")
|
||||||
self.assertEqual(ra.parser_id, "if_intf")
|
self.assertEqual(ra.parser_id, "config_interface")
|
||||||
self.assertIn("zte_zxros_show_running_config_if_intf", ra.rule_keys)
|
|
||||||
|
|
||||||
def test_arp_profile_slim(self) -> None:
|
def test_arp_profile_slim(self) -> None:
|
||||||
p = get_profile("zte.arp")
|
p = get_profile("zte.arp")
|
||||||
assert p is not None
|
assert p is not None
|
||||||
self.assertEqual(p.aux_commands[0].key, "if_intf")
|
self.assertEqual(p.aux_commands[0].key, "if_intf")
|
||||||
self.assertEqual(p.aux_commands[0].profile_id, "zte.if_intf")
|
self.assertEqual(p.aux_commands[0].profile_id, "zte.config_interface")
|
||||||
self.assertEqual(len(p.enrich_joins), 1)
|
self.assertEqual(len(p.enrich_joins), 1)
|
||||||
self.assertEqual(p.enrich_joins[0].take, ("vrf",))
|
self.assertEqual(p.enrich_joins[0].take, ("vrf",))
|
||||||
|
ii = get_profile("zte.if_intf")
|
||||||
|
assert ii is not None
|
||||||
|
self.assertFalse(ii.enabled)
|
||||||
|
|
||||||
|
|
||||||
class CollectSessionCacheTests(unittest.TestCase):
|
class CollectSessionCacheTests(unittest.TestCase):
|
||||||
|
|
@ -98,13 +100,13 @@ class ArpEnrichPipelineTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_run_primary_with_enrich(self) -> None:
|
def test_run_primary_with_enrich(self) -> None:
|
||||||
if_recs, if_fsm, _ = run_parser(
|
if_recs, if_fsm, _ = run_parser(
|
||||||
"if_intf",
|
"config_interface",
|
||||||
raw_text=IF_INTF_SAMPLE,
|
raw_text=IF_INTF_SAMPLE,
|
||||||
vendor="zte",
|
vendor="zte",
|
||||||
device_type="zte_zxros",
|
device_type="zte_zxros",
|
||||||
command="show running-config if-intf",
|
command="show running-config if-intf",
|
||||||
)
|
)
|
||||||
ra = resolve_aux_command(AuxCommand(key="if_intf", profile_id="zte.if_intf"))
|
ra = resolve_aux_command(AuxCommand(key="if_intf", profile_id="zte.config_interface"))
|
||||||
bundle = build_parse_bundle(
|
bundle = build_parse_bundle(
|
||||||
primary_raw=ARP_MATCHING,
|
primary_raw=ARP_MATCHING,
|
||||||
primary_parser_id="arp",
|
primary_parser_id="arp",
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,10 @@ class ArpMultiCommandTests(unittest.TestCase):
|
||||||
assert p is not None
|
assert p is not None
|
||||||
self.assertEqual(len(p.aux_commands), 1)
|
self.assertEqual(len(p.aux_commands), 1)
|
||||||
self.assertEqual(p.aux_commands[0].key, "if_intf")
|
self.assertEqual(p.aux_commands[0].key, "if_intf")
|
||||||
self.assertEqual(p.aux_commands[0].profile_id, "zte.if_intf")
|
self.assertEqual(p.aux_commands[0].profile_id, "zte.config_interface")
|
||||||
|
ii = get_profile("zte.if_intf")
|
||||||
|
assert ii is not None
|
||||||
|
self.assertFalse(ii.enabled)
|
||||||
self.assertEqual(get_parser_meta("if_intf")["rule_keys"], ("zte_zxros_show_running_config_if_intf",))
|
self.assertEqual(get_parser_meta("if_intf")["rule_keys"], ("zte_zxros_show_running_config_if_intf",))
|
||||||
|
|
||||||
def test_arp_enriches_vrf_via_aux_records(self) -> None:
|
def test_arp_enriches_vrf_via_aux_records(self) -> None:
|
||||||
|
|
@ -110,13 +113,13 @@ class ArpMultiCommandTests(unittest.TestCase):
|
||||||
from netx_api.biz_state.profiles import AuxCommand
|
from netx_api.biz_state.profiles import AuxCommand
|
||||||
|
|
||||||
if_recs, if_fsm, _ = run_parser(
|
if_recs, if_fsm, _ = run_parser(
|
||||||
"if_intf",
|
"config_interface",
|
||||||
raw_text=IF_INTF_SAMPLE,
|
raw_text=IF_INTF_SAMPLE,
|
||||||
vendor="zte",
|
vendor="zte",
|
||||||
device_type="zte_zxros",
|
device_type="zte_zxros",
|
||||||
command="show running-config if-intf",
|
command="show running-config if-intf",
|
||||||
)
|
)
|
||||||
ra = resolve_aux_command(AuxCommand(key="if_intf", profile_id="zte.if_intf"))
|
ra = resolve_aux_command(AuxCommand(key="if_intf", profile_id="zte.config_interface"))
|
||||||
bundle = build_parse_bundle(
|
bundle = build_parse_bundle(
|
||||||
primary_raw=ARP_MATCHING,
|
primary_raw=ARP_MATCHING,
|
||||||
primary_parser_id="arp",
|
primary_parser_id="arp",
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,9 @@ class ZteConfigIntentTests(unittest.TestCase):
|
||||||
self.assertIsNotNone(hit)
|
self.assertIsNotNone(hit)
|
||||||
assert hit is not None
|
assert hit is not None
|
||||||
self.assertEqual(hit.profile.profile_id, "zte.config_interface")
|
self.assertEqual(hit.profile.profile_id, "zte.config_interface")
|
||||||
|
ii = get_profile("zte.if_intf")
|
||||||
|
assert ii is not None
|
||||||
|
self.assertFalse(ii.enabled)
|
||||||
|
|
||||||
def test_config_vrf(self) -> None:
|
def test_config_vrf(self) -> None:
|
||||||
rows = normalize_config_vrf(raw_text=_CFG_VRF, command="show running-config vrf")
|
rows = normalize_config_vrf(raw_text=_CFG_VRF, command="show running-config vrf")
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,12 @@ class ZteStatusParserTests(unittest.TestCase):
|
||||||
for mid in ("isis_adjacency", "interface_brief", "arp", "nd6_cache", "bgp_peer", "if_intf"):
|
for mid in ("isis_adjacency", "interface_brief", "arp", "nd6_cache", "bgp_peer", "if_intf"):
|
||||||
self.assertIn(mid, metric_field_map())
|
self.assertIn(mid, metric_field_map())
|
||||||
self.assertIsNotNone(get_profile("zte.isis_adjacency"))
|
self.assertIsNotNone(get_profile("zte.isis_adjacency"))
|
||||||
self.assertIsNotNone(get_profile("zte.if_intf"))
|
ii = get_profile("zte.if_intf")
|
||||||
|
self.assertIsNotNone(ii)
|
||||||
|
assert ii is not None
|
||||||
|
self.assertFalse(ii.enabled)
|
||||||
|
self.assertNotIn("zte.if_intf", zte)
|
||||||
|
self.assertIn("zte.config_interface", zte)
|
||||||
|
|
||||||
def test_isis_adjacency(self) -> None:
|
def test_isis_adjacency(self) -> None:
|
||||||
text = _section(
|
text = _section(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue