mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 04:20:45 +08:00
Match key alerts by optional inventory ne_types, add edit dialog to update device types on existing rules, and keep card pager controls on one row. Co-authored-by: Cursor <cursoragent@cursor.com>
176 lines
6.4 KiB
Python
176 lines
6.4 KiB
Python
from __future__ import annotations
|
||
|
||
import unittest
|
||
from datetime import datetime
|
||
|
||
from sqlalchemy import create_engine
|
||
from sqlalchemy.orm import sessionmaker
|
||
|
||
from netx_api.db import Base
|
||
from netx_api.key_alert_config import invalidate_key_alert_config_cache, set_key_alert_monitor_config
|
||
from netx_api.key_alert_matcher import (
|
||
invalidate_key_alert_rule_cache,
|
||
match_key_alert_rule,
|
||
parse_rule_items,
|
||
rule_storage_key,
|
||
)
|
||
from netx_api.models import UmeInventoryNE, UmeKeyAlertRule
|
||
from netx_api.ume_sync_service import apply_alarm_to_current, notification_id_from_norm
|
||
|
||
|
||
class KeyAlertMatcherTests(unittest.TestCase):
|
||
def setUp(self) -> None:
|
||
engine = create_engine("sqlite+pysqlite:///:memory:", future=True)
|
||
self.SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, expire_on_commit=False)
|
||
Base.metadata.create_all(bind=engine)
|
||
self.db = self.SessionLocal()
|
||
invalidate_key_alert_rule_cache()
|
||
invalidate_key_alert_config_cache()
|
||
|
||
def tearDown(self) -> None:
|
||
self.db.close()
|
||
invalidate_key_alert_config_cache()
|
||
|
||
def test_notification_id_from_norm(self) -> None:
|
||
norm = {"notificationId": "NID-1001", "is-cleared": False}
|
||
self.assertEqual(notification_id_from_norm(norm), "NID-1001")
|
||
norm2 = {"notification-id": "NID-1002"}
|
||
self.assertEqual(notification_id_from_norm(norm2), "NID-1002")
|
||
|
||
def test_apply_alarm_persists_notification_id(self) -> None:
|
||
alarm = {
|
||
"alarmkey": "AK-NID-1",
|
||
"notificationId": "NID-9001",
|
||
"is-cleared": False,
|
||
"perceivedSeverity": "critical",
|
||
}
|
||
action, changed = apply_alarm_to_current(self.db, alarm, touch_ts=datetime.utcnow())
|
||
self.db.commit()
|
||
self.assertEqual(action, "inserted")
|
||
self.assertTrue(changed)
|
||
from netx_api.models import UmeAlarmCurrent
|
||
|
||
row = self.db.get(UmeAlarmCurrent, "AK-NID-1")
|
||
self.assertIsNotNone(row)
|
||
assert row is not None
|
||
self.assertEqual(row.notification_id, "NID-9001")
|
||
|
||
def test_match_insert_and_clear(self) -> None:
|
||
self.db.add(
|
||
UmeKeyAlertRule(
|
||
notification_id="NID-42",
|
||
enabled=1,
|
||
label="test",
|
||
)
|
||
)
|
||
self.db.commit()
|
||
invalidate_key_alert_rule_cache()
|
||
norm = {"notificationId": "NID-42", "is-cleared": False}
|
||
rule = match_key_alert_rule(self.db, norm=norm, action="inserted")
|
||
self.assertIsNotNone(rule)
|
||
cleared = {"notificationId": "NID-42", "is-cleared": True}
|
||
self.assertIsNone(match_key_alert_rule(self.db, norm=cleared, action="deleted"))
|
||
set_key_alert_monitor_config(self.db, forward_on_clear=True)
|
||
invalidate_key_alert_rule_cache()
|
||
self.assertIsNotNone(match_key_alert_rule(self.db, norm=cleared, action="deleted"))
|
||
|
||
def test_parse_rule_items(self) -> None:
|
||
self.assertEqual(parse_rule_items("a, b;c\n d"), ["a", "b", "c", "d"])
|
||
self.assertEqual(parse_rule_items("A, a"), ["A"])
|
||
self.assertEqual(parse_rule_items(""), [])
|
||
|
||
def test_rule_storage_key_keyword(self) -> None:
|
||
self.assertEqual(rule_storage_key(match_type="keyword", value="BGP Down"), "kw:bgp down")
|
||
|
||
def test_keyword_match_insert(self) -> None:
|
||
pk = rule_storage_key(match_type="keyword", value="链路中断")
|
||
self.db.add(
|
||
UmeKeyAlertRule(
|
||
notification_id=pk,
|
||
match_type="keyword",
|
||
match_value="链路中断",
|
||
enabled=1,
|
||
label="链路",
|
||
)
|
||
)
|
||
self.db.commit()
|
||
invalidate_key_alert_rule_cache()
|
||
norm = {
|
||
"notificationId": "NID-999",
|
||
"nativeProbableCause": "核心链路中断告警",
|
||
"is-cleared": False,
|
||
}
|
||
rule = match_key_alert_rule(self.db, norm=norm, action="inserted")
|
||
self.assertIsNotNone(rule)
|
||
assert rule is not None
|
||
self.assertEqual(rule.match_value, "链路中断")
|
||
|
||
norm_miss = {"notificationId": "NID-1", "nativeProbableCause": "CPU high", "is-cleared": False}
|
||
self.assertIsNone(match_key_alert_rule(self.db, norm=norm_miss, action="inserted"))
|
||
|
||
def test_keyword_match_case_insensitive(self) -> None:
|
||
pk = rule_storage_key(match_type="keyword", value="bgp down")
|
||
self.db.add(
|
||
UmeKeyAlertRule(
|
||
notification_id=pk,
|
||
match_type="keyword",
|
||
match_value="BGP DOWN",
|
||
enabled=1,
|
||
label="BGP",
|
||
)
|
||
)
|
||
self.db.commit()
|
||
invalidate_key_alert_rule_cache()
|
||
for cause in ("BGP DOWN alarm", "bgp down on port", "Link BGP DOWN detected"):
|
||
norm = {"notificationId": "NID-x", "nativeProbableCause": cause, "is-cleared": False}
|
||
self.assertIsNotNone(
|
||
match_key_alert_rule(self.db, norm=norm, action="inserted"),
|
||
msg=f"expected match for cause={cause!r}",
|
||
)
|
||
|
||
def test_ne_type_filter(self) -> None:
|
||
from netx_api.key_alert_matcher import serialize_rule_ne_types
|
||
|
||
self.db.add(
|
||
UmeInventoryNE(
|
||
ne_id="NE-PTN-1",
|
||
ne_name="PTN-A",
|
||
ne_type="PTN 9700",
|
||
)
|
||
)
|
||
self.db.add(
|
||
UmeInventoryNE(
|
||
ne_id="NE-ROUTER-1",
|
||
ne_name="Router-A",
|
||
ne_type="ZXR10 M6000",
|
||
)
|
||
)
|
||
self.db.add(
|
||
UmeKeyAlertRule(
|
||
notification_id="NID-NE-TYPE",
|
||
match_type="notification_id",
|
||
match_value="NID-NE-TYPE",
|
||
enabled=1,
|
||
label="PTN only",
|
||
ne_types=serialize_rule_ne_types(["PTN 9700"]),
|
||
)
|
||
)
|
||
self.db.commit()
|
||
invalidate_key_alert_rule_cache()
|
||
|
||
ptn_norm = {
|
||
"notificationId": "NID-NE-TYPE",
|
||
"is-cleared": False,
|
||
"ne-id": "NE-PTN-1",
|
||
}
|
||
router_norm = {
|
||
"notificationId": "NID-NE-TYPE",
|
||
"is-cleared": False,
|
||
"ne-id": "NE-ROUTER-1",
|
||
}
|
||
self.assertIsNotNone(match_key_alert_rule(self.db, norm=ptn_norm, action="inserted"))
|
||
self.assertIsNone(match_key_alert_rule(self.db, norm=router_norm, action="inserted"))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|