{{tag>dev python snmp get}}
====== Python : Interroger un agent SNMP ======
**PySNMP** est un module open-source Python prenant en charge toutes les versions du protocole SNMP et permettant d'agir autant comme manager que comme agent.
===== Installation =====
Méthode standard, depuis l'environnement virtuel on utilise pip:
pip3 install pysnmp
===== SNMP GET =====
L'opération SNMP GET permet de récupérer la valeur d'un objet de la MIB exposée par l'agent SNMP. Depuis une script Python, il nous faut importer le module pysnmp, instancier un contrôleur et manipuler ce contrôleur à la fois pour envoyer des requêtes SNMP et pour obtenir les résultats retournés par la cible.
===== Mode interactif =====
Pour appréhender le fonctionnement de pysnmp on peut utiliser le mode interactif:
===== Création d'une fonction (helper) =====
from pysnmp import hlapi
def get(target, oids, credentials, port=161, engine=hlapi.SnmpEngine(), context=hlapi.ContextData()):
handler = hlapi.getCmd(
engine,
credentials,
hlapi.UdpTransportTarget((target, port)),
context,
*construct_object_types(oids)
)
return fetch(handler, 1)[0]
Dans le code ci-dessus on définit une fonction get() avec 3 arguments obligatoires:
* **target** IP ou nom DNS de la cible (l'agent SNMP à contacter)
* **oids** liste d' Object IDs à interroger
* **credentials** permettant de spécifier une authentification pour la session SNMP
Via les paramètres optionnels on peut:
* modifier le port;
* spécifier un moteur SNMP préexistant;
* spécifier un contexte préexistant.
Il peut être utile d'utiliser un même moteur pour récupérer plusieurs OIDs sur une même cible.
Via l'appel de **hlapi.getCmd()**, la fonction instancie le contrôleur (handler) en charge de la session SNMP. C'est également par l'intermédiaire du contrôleur qu'on peut récupérer la réponse retournée par l'agent.
Notons que le paramètre **oids** n'est pas directement transmis à la fonction **hlapi.getCmd()** il est traité par une fonction qui transforme la liste de chaînes de caractères en une liste d'objets attendus de type ''hlapi.ObjectType''.
Ci dessous le code de la fonction:
def construct_object_types(list_of_oids):
object_types = []
for oid in list_of_oids:
object_types.append(hlapi.ObjectType(hlapi.ObjectIdentity(oid)))
return object_types
La fonction construct_object_types() retourne une liste qui est développée lors de l'appel par l'usage de l'opérateur '*'.
===== Récupérer les valeurs =====
La fonction fetch() est la pièce angulaire du script Python proposé ici. Elle est pensée pour pouvoir être réutilisée avec d'autre fonctions SNMP disponible via le handler telle que **get-bulk()**. Elle permet de boucler sur le contrôleur autant de fois que l'indique la valeur du paramètre count. Si elle rencontre une erreur, elle stoppe sont éxecution et lève une exception de type ''RuntimeError''. Sinon les données sont stockées dans une liste de dictionnaires.
def fetch(handler, count):
result = []
for i in range(count):
try:
error_indication, error_status, error_index, var_binds = next(handler)
if not error_indication and not error_status:
items = {}
for var_bind in var_binds:
items[str(var_bind[0])] = cast(var_bind[1])
result.append(items)
else:
raise RuntimeError('Got SNMP error: {0}'.format(error_indication))
except StopIteration:
break
return result
Le bloc ''try ... except StopIteration'' est nécessaire pour le cas ou la valeur de count spécifiée par l'utilisateur est supérieure au nombre d'objets retournés.. Dans ce cas on capture l'exception, on arrête simplement la boucle et on retourne la liste de résultats récupérée.
Les dictionnaires sont nécessaires car chaque opération GET peut contenir de multiples OIDs. Chaque dictionnaire a ainsi comme clé l'OID et comme valeur la valeur retournée par la cible.
Le résultat retourné par fetch() est une liste. Dans le cas de l'appel de la commande SNMP GET la valeur est retournée une seule fois. Cependant si l'on fait appel à la commande SNMP BULK nous allons demander la meme information plusieurs fois sur différentes instances. Cela peut être utile par exemple si on souhaite remonter le compteur d'erreurs sur l'ensemble des interfaces de l'agent (la requête demande un meme compteur d'erreur mais il y a de multiples instances: une par interface).
Notons enfin que la fonction fetch() utilise la fonction cast() qui se charge de convertir les chaînes de caractères reçues de PySNMP en types Python int, float ou string.
def cast(value):
try:
return int(value)
except (ValueError, TypeError):
try:
return float(value)
except (ValueError, TypeError):
try:
return str(value)
except (ValueError, TypeError):
pass
return value
===== Références =====
* https://www.ictshore.com/sdn/python-snmp-tutorial/
* https://www.yaklin.ca/2021/08/25/snmp-queries-with-python.html*
* https://roscas.github.io/reseau/reseau-Labo-6-SNMP.html
* https://www.technologuepro.com/reseaux/Configuration-Agent-SNMP/Configuration-snmp-lunix.html
* https://makina-corpus.com/python/initiation-snmp-avec-python-pysnmp-partie-1-le-protocole-et-les-commandes