Add SNMP profile mapping and fix asset cleanup
This commit is contained in:
@@ -2,7 +2,27 @@ from fastapi.testclient import TestClient
|
||||
|
||||
from app.core.secrets import encrypt_secret
|
||||
from app.models import Credential
|
||||
from app.services.snmp import DiscoveredSnmpDevice, DiscoveredSnmpInterface, SnmpCredential, SnmpDiscoveryError
|
||||
from app.services.snmp import (
|
||||
ENT_PHYSICAL_NAME,
|
||||
ENT_PHY_SENSOR_TYPE,
|
||||
ENT_PHY_SENSOR_VALUE,
|
||||
HR_PROCESSOR_LOAD,
|
||||
HR_STORAGE_ALLOCATION_UNITS,
|
||||
HR_STORAGE_DESCR,
|
||||
HR_STORAGE_FIXED_DISK,
|
||||
HR_STORAGE_RAM,
|
||||
HR_STORAGE_SIZE,
|
||||
HR_STORAGE_TYPE,
|
||||
HR_STORAGE_USED,
|
||||
SYS_DESCR,
|
||||
SYS_NAME,
|
||||
SYS_UPTIME,
|
||||
DiscoveredSnmpDevice,
|
||||
DiscoveredSnmpInterface,
|
||||
SnmpCredential,
|
||||
SnmpDiscoveryError,
|
||||
discover_snmp_device,
|
||||
)
|
||||
|
||||
|
||||
def test_snmp_discovery_uses_profile_and_returns_friendly_results(client: TestClient, db_session, monkeypatch) -> None:
|
||||
@@ -49,6 +69,9 @@ def test_snmp_discovery_uses_profile_and_returns_friendly_results(client: TestCl
|
||||
body = response.json()
|
||||
assert body["host"] == "192.0.2.10"
|
||||
assert body["credential_profile_id"] == profile.id
|
||||
assert body["profile_key"] == "generic_snmp"
|
||||
assert body["profile_name"] == "Generic SNMP"
|
||||
assert body["capabilities"] == {}
|
||||
assert body["device_name"] == "core-sw-1"
|
||||
assert body["description"] == "Core switch"
|
||||
assert body["uptime_seconds"] == 12345
|
||||
@@ -96,6 +119,66 @@ def test_snmp_discovery_uses_profile_and_returns_friendly_results(client: TestCl
|
||||
assert "1.3.6" not in response.text
|
||||
|
||||
|
||||
def test_snmp_profile_mapping_discovers_standard_health_items(monkeypatch) -> None:
|
||||
class FakeClient:
|
||||
def __init__(self, host: str, credential: SnmpCredential) -> None:
|
||||
self.host = host
|
||||
self.credential = credential
|
||||
|
||||
def get_many(self, _oids):
|
||||
return {
|
||||
SYS_NAME: "edge-router",
|
||||
SYS_DESCR: "Linux edge-router net-snmp",
|
||||
SYS_UPTIME: 10_000,
|
||||
}
|
||||
|
||||
def walk(self, base_oid, max_items=128):
|
||||
values = {
|
||||
HR_PROCESSOR_LOAD: {(*HR_PROCESSOR_LOAD, 196608): 17},
|
||||
HR_STORAGE_TYPE: {
|
||||
(*HR_STORAGE_TYPE, 1): HR_STORAGE_RAM,
|
||||
(*HR_STORAGE_TYPE, 31): HR_STORAGE_FIXED_DISK,
|
||||
},
|
||||
HR_STORAGE_DESCR: {
|
||||
(*HR_STORAGE_DESCR, 1): "Physical memory",
|
||||
(*HR_STORAGE_DESCR, 31): "/",
|
||||
},
|
||||
HR_STORAGE_ALLOCATION_UNITS: {
|
||||
(*HR_STORAGE_ALLOCATION_UNITS, 1): 1024,
|
||||
(*HR_STORAGE_ALLOCATION_UNITS, 31): 4096,
|
||||
},
|
||||
HR_STORAGE_SIZE: {
|
||||
(*HR_STORAGE_SIZE, 1): 2048,
|
||||
(*HR_STORAGE_SIZE, 31): 4096,
|
||||
},
|
||||
HR_STORAGE_USED: {
|
||||
(*HR_STORAGE_USED, 1): 1024,
|
||||
(*HR_STORAGE_USED, 31): 1024,
|
||||
},
|
||||
ENT_PHY_SENSOR_TYPE: {(*ENT_PHY_SENSOR_TYPE, 10): 8},
|
||||
ENT_PHY_SENSOR_VALUE: {(*ENT_PHY_SENSOR_VALUE, 10): 310},
|
||||
ENT_PHYSICAL_NAME: {(*ENT_PHYSICAL_NAME, 10): "Inlet"},
|
||||
}
|
||||
return values.get(base_oid, {})
|
||||
|
||||
monkeypatch.setattr("app.services.snmp.SnmpV2Client", FakeClient)
|
||||
|
||||
discovered = discover_snmp_device("192.0.2.20", SnmpCredential(community="private-community"))
|
||||
|
||||
assert discovered.profile_key == "net_snmp"
|
||||
assert discovered.profile_name == "Net-SNMP Host Resources"
|
||||
assert discovered.capabilities["cpu"] is True
|
||||
assert discovered.capabilities["memory"] is True
|
||||
assert discovered.capabilities["storage"] is True
|
||||
assert discovered.capabilities["sensors"] is True
|
||||
assert [(item.item_id, item.item_type, item.group, item.label, item.unit) for item in discovered.health_items] == [
|
||||
("cpu.196608.load", "cpu_load", "Device Health", "CPU load", "%"),
|
||||
("storage.1.memory", "memory_usage", "Device Health", "Memory used", "%"),
|
||||
("storage.31.usage", "storage_usage", "Storage", "Disk / usage", "%"),
|
||||
("sensor.10.value", "sensor_value", "Environmental", "Temperature Inlet", "C"),
|
||||
]
|
||||
|
||||
|
||||
def test_snmp_discovery_rejects_missing_profile(client: TestClient) -> None:
|
||||
response = client.post("/discovery/snmp", json={"host": "192.0.2.10", "credential_profile_id": 999})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.secrets import encrypt_secret
|
||||
from app.models import AlertRule, Asset, Credential, Monitor
|
||||
from app.models import AlertRule, Asset, Credential, Incident, Monitor
|
||||
|
||||
|
||||
def test_create_website_monitor_creates_asset_and_alert_rule(client: TestClient, db_session: Session) -> None:
|
||||
@@ -211,3 +211,41 @@ def test_create_snmp_monitors_rejects_missing_profile(client: TestClient, db_ses
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_delete_asset_deletes_attached_monitors_and_resolves_incidents(client: TestClient, db_session: Session) -> None:
|
||||
asset = Asset(name="Router", asset_type="network_device", address="192.0.2.1", status="down", extra={})
|
||||
monitor = Monitor(
|
||||
asset=asset,
|
||||
name="Router ping",
|
||||
monitor_type="ping",
|
||||
target="192.0.2.1",
|
||||
config={},
|
||||
interval_seconds=60,
|
||||
status="down",
|
||||
)
|
||||
db_session.add_all([asset, monitor])
|
||||
db_session.flush()
|
||||
incident = Incident(
|
||||
asset_id=asset.id,
|
||||
monitor_id=monitor.id,
|
||||
alert_rule_id=None,
|
||||
title="Router ping is failing",
|
||||
severity="warning",
|
||||
status="open",
|
||||
details={"last_message": "Ping failed"},
|
||||
)
|
||||
db_session.add(incident)
|
||||
db_session.commit()
|
||||
|
||||
response = client.delete(f"/assets/{asset.id}")
|
||||
|
||||
assert response.status_code == 204
|
||||
assert db_session.get(Asset, asset.id) is None
|
||||
assert db_session.get(Monitor, monitor.id) is None
|
||||
db_session.refresh(incident)
|
||||
assert incident.status == "resolved"
|
||||
assert incident.resolved_at is not None
|
||||
assert incident.asset_id is None
|
||||
assert incident.monitor_id is None
|
||||
assert incident.details["recovery_message"] == "Asset was deleted"
|
||||
|
||||
Reference in New Issue
Block a user