129 lines
4.6 KiB
Python
129 lines
4.6 KiB
Python
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,
|
|
profile_key=discovered.profile_key,
|
|
profile_name=discovered.profile_name,
|
|
capabilities=discovered.capabilities,
|
|
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 = []
|
|
if discovered.uptime_seconds is not None:
|
|
items.append(
|
|
SnmpDiscoveryItemRead(
|
|
item_id="device.uptime",
|
|
item_type="device_uptime",
|
|
group="Device Health",
|
|
label="Device uptime",
|
|
unit="seconds",
|
|
)
|
|
)
|
|
items.extend(
|
|
SnmpDiscoveryItemRead(
|
|
item_id=item.item_id,
|
|
item_type=item.item_type,
|
|
group=item.group,
|
|
label=item.label,
|
|
unit=item.unit,
|
|
)
|
|
for item in discovered.health_items
|
|
)
|
|
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
|