Volver a la wiki

Servicio snmp_service — SNMP walker con logging mejorado

Descripción

Módulo monitoring/services/snmp_service.py que ejecuta queries SNMP (get, walk, interfaces). Implementa el protocolo SNMPv2c y SNMPv3 usando pysnmp.

En sesión 107 se mejoró el logging para debugging sin exponer credenciales.

Cambios sesión 107

Logging de excepciones SNMP

Antes: Los métodos get_single, walk, get_interface_traffic y test_connection tragaban excepciones sin logging:

except Exception:
    return None  # or pass

Ahora: Se registran en DEBUG:

except Exception as e:
    logger.debug("SNMP get failed for %s oid=%s: %s", self.host, oid, e)
    return None

Permite debugging sin impacto en producción (logs a DEBUG level).

test_connection() no expone str(exc)

Antes: devolvía {"success": False, "error": str(e)}

Ahora: devolvía {"success": False, "error": "SNMP connection failed"} (genérico) + log warning interno:

except Exception as e:
    logger.warning("SNMP test_connection failed for %s: %s", self.host, e)
    return {"success": False, "error": "SNMP connection failed"}

Credenciales SNMP v3 en logs

Antes (network/services/device_discovery/snmp_auth.py):

logger.debug(f"[SNMP] v3 auth: user={username}, auth={auth_protocol}, priv={priv_protocol}")

Ahora: No incluye username:

logger.debug(f"[SNMP] v3 auth configured: auth={auth_protocol or 'none'}, priv={priv_protocol or 'none'}")

Evita registrar credenciales en logs (sensitive data).

Métodos principales

__init__(host: str, version: str = "2c", community: str | None = None, **v3_kwargs)

Constructor. Almacena parámetros SNMP (host, versión, credenciales).

async get_single(oid: str) → str | None

Obtiene un valor SNMP individual (OID).

Parámetros:

Retorna: String con el valor, o None si no disponible/error.

Cambio sesión 107: Ahora loga logger.debug(...) en caso de excepción.


async walk(oid: str) → dict[int, str]

Camina un árbol SNMP (múltiples valores bajo un OID base).

Retorna: Dict {index: value_str}.

Cambio sesión 107: Loga excepción en DEBUG.


def get_interfaces() → list[InterfaceStats]

Obtiene todas las interfaces (OIDs estándar: ifIndex, ifName, ifType, ifSpeed, etc.).

Retorna: Lista de InterfaceStats (dataclass).


async get_interface_traffic(interface_index: int) → dict | None

Obtiene octetos in/out de una interfaz específica.

Retorna: {"in_octets": int, "out_octets": int} o None si no disponible.

Cambio sesión 107: Loga excepción en DEBUG.


def test_connection() → dict

Prueba conectividad SNMP (intenta leer sysUpTime, 1.3.6.1.2.1.1.3.0).

Retorna: {"success": True} o {"success": False, "error": "..."}

Cambio sesión 107: No expone str(e) al cliente, loga en WARNING.


def get_system_info() → dict

Lee grupo SNMP system (sysDescr, sysObjectID, sysUpTime, sysContact, sysName, sysLocation).


Dataclass InterfaceStats

@dataclass
class InterfaceStats:
    index: int
    name: str
    type: str              # "ethernetCsmacd", "softwareLoopback", etc.
    speed: int | None      # bits per second
    mtu: int | None
    admin_status: str      # "up", "down", "testing"
    oper_status: str       # "up", "down", "testing"
    in_octets: int | None
    out_octets: int | None
    
    def to_dict(self) -> dict:
        """Convierte a dict (para API)."""

OIDs conocidos

OIDS = {
    "sysDescr": "1.3.6.1.2.1.1.1.0",
    "sysUpTime": "1.3.6.1.2.1.1.3.0",
    # ... más en el código
    "ifInOctets": "1.3.6.1.2.1.2.2.1.10",        # HC: 1.3.6.1.2.1.31.1.1.1.6
    "ifOutOctets": "1.3.6.1.2.1.2.2.1.16",       # HC: 1.3.6.1.2.1.31.1.1.1.10
}

Autenticación SNMP

Se configura vía monitoring.models.MonitoringTarget.config:

{
  "snmp_version": "2c",
  "snmp_community": "public"
}

o para v3:

{
  "snmp_version": "3",
  "snmp_username": "user",
  "snmp_auth_type": "md5",
  "snmp_auth_pass": "pass",
  "snmp_priv_type": "des",
  "snmp_priv_pass": "privpass"
}

Las credenciales se construyen en network/services/device_discovery/snmp_auth.py (no las logueamos en v3).

Testing

Cubierto por tests de autorización (sesión 107) en tests/api/test_monitoring.py. Tests funcionales más profundos dependen de fixtures SNMP reales o mocks.

Véase también

Subir