Ping the host first to check availability and measure actual network latency. Only proceed with SNMP query if the host is reachable. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
160 lines
5.1 KiB
Python
160 lines
5.1 KiB
Python
"""
|
|
Checker SNMP.
|
|
|
|
Vérifie la disponibilité d'un équipement réseau via SNMP et peut
|
|
récupérer sa température.
|
|
"""
|
|
|
|
import asyncio
|
|
import platform
|
|
import re
|
|
import subprocess
|
|
|
|
from pysnmp.hlapi.v1arch.asyncio import (
|
|
Slim,
|
|
ObjectIdentity,
|
|
ObjectType,
|
|
)
|
|
|
|
from .base import BaseChecker, CheckResult
|
|
|
|
|
|
class SnmpChecker(BaseChecker):
|
|
"""
|
|
Vérifie la disponibilité d'un équipement via SNMP.
|
|
|
|
Utilise SNMPv2c pour interroger un OID et optionnellement
|
|
récupérer la température de l'équipement.
|
|
|
|
Configuration YAML:
|
|
host: Adresse IP de l'équipement (obligatoire).
|
|
port: Port SNMP (défaut: 161).
|
|
community: Communauté SNMP (défaut: public).
|
|
oid: OID à interroger (défaut: sysDescr).
|
|
temperature_oid: OID de la température (optionnel).
|
|
Note: Utiliser l'OID complet (feuille, pas branche).
|
|
Exemples:
|
|
- Mikrotik: 1.3.6.1.4.1.14988.1.1.3.100.1.3.52.0
|
|
- Synology: 1.3.6.1.4.1.6574.1.2.0
|
|
timeout: Délai d'attente en secondes (défaut: 5).
|
|
"""
|
|
|
|
def check(self) -> CheckResult:
|
|
"""
|
|
Exécute la vérification SNMP.
|
|
|
|
Wrapper synchrone autour de _async_check() pour compatibilité
|
|
avec l'interface BaseChecker.
|
|
|
|
Returns:
|
|
CheckResult avec success=True si l'équipement répond.
|
|
"""
|
|
return asyncio.run(self._async_check())
|
|
|
|
def _ping(self, host: str, timeout: int) -> tuple[bool, float | None]:
|
|
"""
|
|
Exécute un ping vers l'hôte pour vérifier sa disponibilité.
|
|
|
|
Args:
|
|
host: Adresse IP ou nom d'hôte.
|
|
timeout: Délai d'attente en secondes.
|
|
|
|
Returns:
|
|
Tuple (succès, RTT en ms ou None).
|
|
"""
|
|
if platform.system().lower() == "windows":
|
|
cmd = ["ping", "-n", "1", "-w", str(timeout * 1000), host]
|
|
else:
|
|
cmd = ["ping", "-c", "1", "-W", str(timeout), host]
|
|
|
|
try:
|
|
result = subprocess.run(
|
|
cmd,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=timeout + 5
|
|
)
|
|
if result.returncode == 0:
|
|
match = re.search(r"time[=<]([\d.]+)\s*ms", result.stdout)
|
|
return True, float(match.group(1)) if match else None
|
|
return False, None
|
|
except Exception:
|
|
return False, None
|
|
|
|
async def _async_check(self) -> CheckResult:
|
|
"""
|
|
Exécute la vérification SNMP de manière asynchrone.
|
|
|
|
Effectue d'abord un ping pour vérifier la disponibilité et mesurer
|
|
la latence, puis interroge l'OID principal et optionnellement
|
|
l'OID de température via SNMP.
|
|
|
|
Returns:
|
|
CheckResult contenant le résultat et la température si configurée.
|
|
"""
|
|
host = self.config["host"]
|
|
port = self.config.get("port", 161)
|
|
community = self.config.get("community", "public")
|
|
oid = self.config.get("oid", "1.3.6.1.2.1.1.1.0") # sysDescr
|
|
temperature_oid = self.config.get("temperature_oid")
|
|
timeout_val = self.config.get("timeout", 5)
|
|
|
|
# Ping d'abord pour vérifier la disponibilité et mesurer la latence
|
|
ping_success, response_time = self._ping(host, timeout_val)
|
|
if not ping_success:
|
|
return CheckResult(
|
|
success=False,
|
|
message="Host is unreachable",
|
|
response_time=None
|
|
)
|
|
|
|
# Requête SNMP
|
|
try:
|
|
with Slim() as slim:
|
|
oids = [ObjectType(ObjectIdentity(oid))]
|
|
if temperature_oid:
|
|
oids.append(ObjectType(ObjectIdentity(temperature_oid)))
|
|
|
|
error_indication, error_status, error_index, var_binds = await slim.get(
|
|
community,
|
|
host,
|
|
port,
|
|
*oids,
|
|
timeout=timeout_val,
|
|
retries=1
|
|
)
|
|
|
|
if error_indication:
|
|
return CheckResult(
|
|
success=False,
|
|
message=f"SNMP error: {error_indication}",
|
|
response_time=response_time
|
|
)
|
|
elif error_status:
|
|
return CheckResult(
|
|
success=False,
|
|
message=f"SNMP error: {error_status.prettyPrint()}",
|
|
response_time=response_time
|
|
)
|
|
else:
|
|
details = {str(var_binds[0][0]): str(var_binds[0][1])}
|
|
|
|
if temperature_oid and len(var_binds) >= 2:
|
|
try:
|
|
details["temperature"] = int(var_binds[1][1])
|
|
except (ValueError, TypeError):
|
|
pass
|
|
|
|
return CheckResult(
|
|
success=True,
|
|
message="SNMP response OK",
|
|
response_time=response_time,
|
|
details=details
|
|
)
|
|
except Exception as e:
|
|
return CheckResult(
|
|
success=False,
|
|
message=f"SNMP error: {e}",
|
|
response_time=response_time
|
|
)
|