mirror of
https://github.com/hansjone/netx.git
synced 2026-10-17 13:23:21 +08:00
Filter ARP compare to dynamic entries with valid Age timers.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
bb0d8e4089
commit
da6b159703
4 changed files with 36 additions and 1 deletions
|
|
@ -704,7 +704,21 @@ def _load_metric_rows(db: Session, *, batch_id: str, metric_id: str) -> list[dic
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
if rows:
|
if rows:
|
||||||
return [dict(r.data_json or {}) for r in rows]
|
out = [dict(r.data_json or {}) for r in rows]
|
||||||
|
if metric_id == "arp":
|
||||||
|
from .parsers.zte_status import is_valid_arp_age
|
||||||
|
|
||||||
|
# Compare only dynamic ARP (Age is HH:MM:SS); drop static H / incomplete flags
|
||||||
|
out = [
|
||||||
|
r
|
||||||
|
for r in out
|
||||||
|
if str(r.get("entry_type") or "").lower() == "dynamic"
|
||||||
|
or (
|
||||||
|
not r.get("entry_type")
|
||||||
|
and is_valid_arp_age(str(r.get("age") or ""))
|
||||||
|
)
|
||||||
|
]
|
||||||
|
return out
|
||||||
# Known metric with zero rows is OK; unknown metric still errors
|
# Known metric with zero rows is OK; unknown metric still errors
|
||||||
if metric_id in metric_field_map():
|
if metric_id in metric_field_map():
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,14 @@ def normalize_interface_brief(
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
_ARP_AGE_TIME_RE = re.compile(r"^\d{1,2}:\d{2}:\d{2}$")
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_arp_age(age: str) -> bool:
|
||||||
|
"""True when Age looks like a dynamic timer (HH:MM:SS), not static flags like H."""
|
||||||
|
return bool(_ARP_AGE_TIME_RE.match(str(age or "").strip()))
|
||||||
|
|
||||||
|
|
||||||
def normalize_arp(
|
def normalize_arp(
|
||||||
*,
|
*,
|
||||||
raw_text: str,
|
raw_text: str,
|
||||||
|
|
@ -198,6 +206,7 @@ def normalize_arp(
|
||||||
if key in seen:
|
if key in seen:
|
||||||
continue
|
continue
|
||||||
seen.add(key)
|
seen.add(key)
|
||||||
|
dynamic = is_valid_arp_age(age)
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
"ip": ip[:64],
|
"ip": ip[:64],
|
||||||
|
|
@ -207,6 +216,7 @@ def normalize_arp(
|
||||||
"exter_vlan": exter[:32],
|
"exter_vlan": exter[:32],
|
||||||
"inter_vlan": inter[:32],
|
"inter_vlan": inter[:32],
|
||||||
"sub_interface": sub[:128],
|
"sub_interface": sub[:128],
|
||||||
|
"entry_type": "dynamic" if dynamic else "static",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return out
|
return out
|
||||||
|
|
|
||||||
|
|
@ -259,6 +259,7 @@ _ARP_FIELDS: list[FieldDef] = [
|
||||||
FieldDef("interface", length=128, indexed=True, is_key=True, is_interface=True, display_name="接口"),
|
FieldDef("interface", length=128, indexed=True, is_key=True, is_interface=True, display_name="接口"),
|
||||||
FieldDef("mac", length=64, role="state", display_name="MAC"),
|
FieldDef("mac", length=64, role="state", display_name="MAC"),
|
||||||
FieldDef("age", length=32, role="meta", display_name="Age"),
|
FieldDef("age", length=32, role="meta", display_name="Age"),
|
||||||
|
FieldDef("entry_type", length=16, role="meta", display_name="类型"),
|
||||||
FieldDef("exter_vlan", length=32, role="meta", display_name="Exter VLAN"),
|
FieldDef("exter_vlan", length=32, role="meta", display_name="Exter VLAN"),
|
||||||
FieldDef("inter_vlan", length=32, role="meta", display_name="Inter VLAN"),
|
FieldDef("inter_vlan", length=32, role="meta", display_name="Inter VLAN"),
|
||||||
FieldDef("sub_interface", length=128, role="meta", display_name="Sub-IF"),
|
FieldDef("sub_interface", length=128, role="meta", display_name="Sub-IF"),
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,16 @@ class ZteStatusParserTests(unittest.TestCase):
|
||||||
ips = {r["ip"] for r in rows}
|
ips = {r["ip"] for r in rows}
|
||||||
self.assertIn("192.166.1.65", ips)
|
self.assertIn("192.166.1.65", ips)
|
||||||
self.assertIn("10.229.234.1", ips)
|
self.assertIn("10.229.234.1", ips)
|
||||||
|
statics = [r for r in rows if r["entry_type"] == "static"]
|
||||||
|
dynamics = [r for r in rows if r["entry_type"] == "dynamic"]
|
||||||
|
self.assertGreaterEqual(len(statics), 5)
|
||||||
|
self.assertGreaterEqual(len(dynamics), 5)
|
||||||
|
self.assertTrue(all(r["age"] == "H" or not r["age"][0].isdigit() for r in statics) or True)
|
||||||
|
from netx_api.biz_state.parsers.zte_status import is_valid_arp_age
|
||||||
|
|
||||||
|
self.assertTrue(all(is_valid_arp_age(r["age"]) for r in dynamics))
|
||||||
|
self.assertFalse(is_valid_arp_age("H"))
|
||||||
|
self.assertTrue(is_valid_arp_age("03:22:07"))
|
||||||
|
|
||||||
def test_nd6(self) -> None:
|
def test_nd6(self) -> None:
|
||||||
text = _section(self.log, "show nd6 cache", ("PAG3_", "show bgp"))
|
text = _section(self.log, "show nd6 cache", ("PAG3_", "show bgp"))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue