Add SNMP device discovery API
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.credentials import SNMP_CREDENTIAL_TYPE
|
||||
from app.auth.dependencies import require_role
|
||||
from app.core.secrets import decrypt_secret
|
||||
from app.db.session import get_db
|
||||
from app.models import Credential, User
|
||||
from app.schemas.core import (
|
||||
SnmpDiscoveredInterfaceRead,
|
||||
SnmpDiscoveryItemRead,
|
||||
SnmpDiscoveryRead,
|
||||
SnmpDiscoveryRequest,
|
||||
)
|
||||
from app.services.snmp import DiscoveredSnmpDevice, SnmpCredential, SnmpDiscoveryError, discover_snmp_device
|
||||
|
||||
router = APIRouter(prefix="/discovery", tags=["discovery"])
|
||||
|
||||
|
||||
@router.post("/snmp", response_model=SnmpDiscoveryRead)
|
||||
def discover_snmp(
|
||||
payload: SnmpDiscoveryRequest,
|
||||
_: User = Depends(require_role("admin")),
|
||||
db: Session = Depends(get_db),
|
||||
) -> SnmpDiscoveryRead:
|
||||
profile = db.get(Credential, payload.credential_profile_id)
|
||||
if profile is None or profile.credential_type != SNMP_CREDENTIAL_TYPE:
|
||||
raise HTTPException(status_code=404, detail="SNMP credential profile not found")
|
||||
|
||||
extra = dict(profile.extra or {})
|
||||
version = str(extra.get("version") or "2c")
|
||||
if version != "2c":
|
||||
raise HTTPException(status_code=400, detail="SNMP discovery currently supports SNMPv2c profiles")
|
||||
|
||||
community = decrypt_secret(profile.encrypted_secret)
|
||||
if not community:
|
||||
raise HTTPException(status_code=400, detail="SNMP credential profile has no usable community string")
|
||||
|
||||
credential = SnmpCredential(
|
||||
community=community,
|
||||
port=int(extra.get("port") or 161),
|
||||
timeout_seconds=int(extra.get("timeout_seconds") or 5),
|
||||
retries=int(extra.get("retries") or 1),
|
||||
)
|
||||
try:
|
||||
discovered = discover_snmp_device(payload.host, credential)
|
||||
except SnmpDiscoveryError as exc:
|
||||
raise HTTPException(status_code=502, detail="SNMP discovery failed") from exc
|
||||
|
||||
return _discovery_to_read(payload.credential_profile_id, discovered)
|
||||
|
||||
|
||||
def _discovery_to_read(credential_profile_id: int, discovered: DiscoveredSnmpDevice) -> SnmpDiscoveryRead:
|
||||
interfaces = [
|
||||
SnmpDiscoveredInterfaceRead(
|
||||
index=interface.index,
|
||||
name=interface.name,
|
||||
description=interface.description,
|
||||
admin_status=interface.admin_status,
|
||||
oper_status=interface.oper_status,
|
||||
speed_bps=interface.speed_bps,
|
||||
)
|
||||
for interface in discovered.interfaces
|
||||
]
|
||||
return SnmpDiscoveryRead(
|
||||
host=discovered.host,
|
||||
credential_profile_id=credential_profile_id,
|
||||
device_name=discovered.device_name,
|
||||
description=discovered.description,
|
||||
uptime_seconds=discovered.uptime_seconds,
|
||||
interfaces=interfaces,
|
||||
monitorable_items=_monitorable_items(discovered),
|
||||
)
|
||||
|
||||
|
||||
def _monitorable_items(discovered: DiscoveredSnmpDevice) -> list[SnmpDiscoveryItemRead]:
|
||||
items = [
|
||||
SnmpDiscoveryItemRead(
|
||||
item_id="device.uptime",
|
||||
item_type="device_uptime",
|
||||
group="Device Health",
|
||||
label="Device uptime",
|
||||
unit="seconds",
|
||||
)
|
||||
]
|
||||
for interface in discovered.interfaces:
|
||||
group = f"Interface {interface.name}"
|
||||
item_prefix = f"interface.{interface.index}"
|
||||
items.extend(
|
||||
[
|
||||
SnmpDiscoveryItemRead(
|
||||
item_id=f"{item_prefix}.status",
|
||||
item_type="interface_status",
|
||||
group=group,
|
||||
label=f"{interface.name} status",
|
||||
),
|
||||
SnmpDiscoveryItemRead(
|
||||
item_id=f"{item_prefix}.traffic",
|
||||
item_type="interface_traffic",
|
||||
group=group,
|
||||
label=f"{interface.name} traffic",
|
||||
unit="bps",
|
||||
),
|
||||
SnmpDiscoveryItemRead(
|
||||
item_id=f"{item_prefix}.errors",
|
||||
item_type="interface_errors",
|
||||
group=group,
|
||||
label=f"{interface.name} errors and discards",
|
||||
unit="count",
|
||||
),
|
||||
]
|
||||
)
|
||||
return items
|
||||
Reference in New Issue
Block a user