mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 03:10:46 +08:00
Rename shared bgp_peer workbook sheet to BGP Status Summary.
Batch sheets are keyed by metric_id, so all AF summaries land in one tab; stop inheriting the first profile title (VPNv4). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
c20a731f72
commit
e89427297d
2 changed files with 89 additions and 4 deletions
|
|
@ -571,6 +571,25 @@ def run_purge_for_task(db: Session, task_id: str) -> dict[str, Any]:
|
||||||
return purge_task_batches(db, task)
|
return purge_task_batches(db, task)
|
||||||
|
|
||||||
|
|
||||||
|
# Workbook sheets are keyed by metric_id. Multiple AF-specific collect profiles
|
||||||
|
# share one metric (e.g. all BGP summaries → bgp_peer); use a neutral title.
|
||||||
|
_METRIC_SHEET_TITLES: dict[str, str] = {
|
||||||
|
"bgp_peer": "BGP Status Summary",
|
||||||
|
"bgp_route": "BGP Neighbor Routes",
|
||||||
|
"vrrp": "VRRP",
|
||||||
|
"ip_route": "IPv4 Forwarding",
|
||||||
|
"ipv6_route": "IPv6 Forwarding",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _metric_sheet_title(metric_id: str, fallback: str = "") -> str:
|
||||||
|
mid = str(metric_id or "").strip()
|
||||||
|
if mid in _METRIC_SHEET_TITLES:
|
||||||
|
return _METRIC_SHEET_TITLES[mid]
|
||||||
|
fb = str(fallback or "").strip()
|
||||||
|
return fb or mid
|
||||||
|
|
||||||
|
|
||||||
def get_batch(db: Session, batch_id: str) -> dict[str, Any]:
|
def get_batch(db: Session, batch_id: str) -> dict[str, Any]:
|
||||||
"""Batch workbook summary: meta + commands + sheet catalog (no metric row payload)."""
|
"""Batch workbook summary: meta + commands + sheet catalog (no metric row payload)."""
|
||||||
b = db.get(BizStateBatch, batch_id)
|
b = db.get(BizStateBatch, batch_id)
|
||||||
|
|
@ -617,8 +636,7 @@ def get_batch(db: Session, batch_id: str) -> dict[str, Any]:
|
||||||
if id_ not in sheets_order:
|
if id_ not in sheets_order:
|
||||||
sheets_order.append(id_)
|
sheets_order.append(id_)
|
||||||
sheet_cmds.setdefault(id_, [])
|
sheet_cmds.setdefault(id_, [])
|
||||||
if title:
|
sheet_titles[id_] = _metric_sheet_title(id_, title)
|
||||||
sheet_titles[id_] = title
|
|
||||||
if cmd_info is not None:
|
if cmd_info is not None:
|
||||||
# Prefer primary collect rows over aux / aux_cached for the same CLI
|
# Prefer primary collect rows over aux / aux_cached for the same CLI
|
||||||
sheet_cmds[id_].append(cmd_info)
|
sheet_cmds[id_].append(cmd_info)
|
||||||
|
|
@ -704,11 +722,12 @@ def get_batch(db: Session, batch_id: str) -> dict[str, Any]:
|
||||||
sheet_cmds.setdefault(mid, [])
|
sheet_cmds.setdefault(mid, [])
|
||||||
if mid not in sheet_titles:
|
if mid not in sheet_titles:
|
||||||
# Best-effort title from any profile with this metric_id
|
# Best-effort title from any profile with this metric_id
|
||||||
|
fallback = mid
|
||||||
for p in all_profiles():
|
for p in all_profiles():
|
||||||
if p.metric_id == mid and p.enabled:
|
if p.metric_id == mid and p.enabled:
|
||||||
sheet_titles[mid] = str(p.title or mid)
|
fallback = str(p.title or mid)
|
||||||
break
|
break
|
||||||
sheet_titles.setdefault(mid, mid)
|
sheet_titles[mid] = _metric_sheet_title(mid, fallback)
|
||||||
|
|
||||||
sheets = [
|
sheets = [
|
||||||
{
|
{
|
||||||
|
|
@ -1013,6 +1032,7 @@ def preview_items(db: Session, *, vendor: str, device_type: str, items: list[dic
|
||||||
|
|
||||||
def _resolve_export_profile(profile_id: str):
|
def _resolve_export_profile(profile_id: str):
|
||||||
"""Resolve collect profile; remap disabled if_intf → config_interface."""
|
"""Resolve collect profile; remap disabled if_intf → config_interface."""
|
||||||
|
|
||||||
pid = str(profile_id or "").strip()
|
pid = str(profile_id or "").strip()
|
||||||
if not pid:
|
if not pid:
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,71 @@ class BatchWorkbookApiTests(unittest.TestCase):
|
||||||
self.assertEqual(out["sheets"][0]["commands"][0]["raw_command"], "show arp | one-line")
|
self.assertEqual(out["sheets"][0]["commands"][0]["raw_command"], "show arp | one-line")
|
||||||
self.assertTrue(out["sheets"][0].get("title"))
|
self.assertTrue(out["sheets"][0].get("title"))
|
||||||
|
|
||||||
|
def test_bgp_peer_sheet_uses_status_summary_title(self) -> None:
|
||||||
|
"""Shared metric_id bgp_peer must not inherit first AF profile title."""
|
||||||
|
batch = BizStateBatch(
|
||||||
|
id="b1",
|
||||||
|
task_id="t1",
|
||||||
|
status="ok",
|
||||||
|
command_count=2,
|
||||||
|
row_count=3,
|
||||||
|
)
|
||||||
|
cmds = [
|
||||||
|
BizStateBatchCommand(
|
||||||
|
id="c1",
|
||||||
|
batch_id="b1",
|
||||||
|
profile_id="zte.bgp_vpnv4_summary",
|
||||||
|
parser_id="bgp_peer",
|
||||||
|
metric_id="bgp_peer",
|
||||||
|
raw_command="show bgp vpnv4 unicast summary | one-line",
|
||||||
|
parse_status="ok",
|
||||||
|
row_count=2,
|
||||||
|
),
|
||||||
|
BizStateBatchCommand(
|
||||||
|
id="c2",
|
||||||
|
batch_id="b1",
|
||||||
|
profile_id="zte.bgp_ipv4_summary",
|
||||||
|
parser_id="bgp_peer",
|
||||||
|
metric_id="bgp_peer",
|
||||||
|
raw_command="show bgp ipv4 unicast summary | one-line",
|
||||||
|
parse_status="ok",
|
||||||
|
row_count=1,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
db = MagicMock()
|
||||||
|
db.get.side_effect = lambda model, pk: batch if pk == "b1" else None
|
||||||
|
cmd_q = MagicMock()
|
||||||
|
cmd_q.filter.return_value.order_by.return_value.all.return_value = cmds
|
||||||
|
metric_count_q = MagicMock()
|
||||||
|
metric_count_q.filter.return_value.group_by.return_value.all.return_value = [
|
||||||
|
("bgp_peer", 3)
|
||||||
|
]
|
||||||
|
lldp_count_q = MagicMock()
|
||||||
|
lldp_count_q.filter.return_value.scalar.return_value = 0
|
||||||
|
|
||||||
|
def query(*_args, **_kwargs):
|
||||||
|
n = query.n
|
||||||
|
query.n += 1
|
||||||
|
if n == 0:
|
||||||
|
return cmd_q
|
||||||
|
if n == 1:
|
||||||
|
return metric_count_q
|
||||||
|
return lldp_count_q
|
||||||
|
|
||||||
|
query.n = 0
|
||||||
|
db.query.side_effect = query
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"netx_api.biz_state.service.batch_protect_info",
|
||||||
|
return_value={"protected": False, "reasons": []},
|
||||||
|
):
|
||||||
|
out = get_batch(db, "b1")
|
||||||
|
|
||||||
|
bgp = next(s for s in out["sheets"] if s["metric_id"] == "bgp_peer")
|
||||||
|
self.assertEqual(bgp["title"], "BGP Status Summary")
|
||||||
|
self.assertEqual(bgp["row_count"], 3)
|
||||||
|
self.assertEqual(len(bgp["commands"]), 2)
|
||||||
|
|
||||||
def test_list_metric_rows_rejects_commands_sheet(self) -> None:
|
def test_list_metric_rows_rejects_commands_sheet(self) -> None:
|
||||||
db = MagicMock()
|
db = MagicMock()
|
||||||
db.get.return_value = BizStateBatch(id="b1", task_id="t1")
|
db.get.return_value = BizStateBatch(id="b1", task_id="t1")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue