SNMP + Java - expérience personnelle. Écriture d'un analyseur de fichiers MIB

SNMP n'est pas le protocole le plus convivial: les fichiers MIB sont trop longs et déroutants, et les OID sont tout simplement impossibles à retenir. Mais que faire si vous devez travailler avec SNMP en Java? Par exemple, écrivez des autotests pour vérifier l'API du serveur SNMP.

Par essais et erreurs, s'il y a une quantité plutôt maigre d'informations sur le sujet, nous avons quand même trouvé comment se faire des amis Java et SNMP.

Dans cette série d'articles, je vais essayer de partager l'expérience acquise avec le protocole. Le premier article de la série sera consacré à l'implémentation de l'analyseur de fichiers MIB en Java. Dans la deuxième partie, je parlerai de l'écriture d'un client SNMP. Dans la troisième partie, nous parlerons d'un exemple réel d'utilisation d'une bibliothèque écrite: les autotests pour vérifier l'interaction avec un périphérique via SNMP.



Entrée


Tout a commencé avec la tâche d'écrire des autotests pour vérifier le fonctionnement de l'enregistreur audio-vidéo via SNMP. Pour compliquer la situation, il n'y a pas beaucoup d'informations sur l'interaction avec SNMP en Java, en particulier en ce qui concerne le segment russe de l'Internet. Bien sûr, on pourrait regarder vers C # ou Python. Mais en C #, la situation du protocole est à peu près aussi compliquée qu'en Java. Il existe quelques bonnes bibliothèques en python, mais nous avions déjà une infrastructure prête à l'emploi pour les autotests de l'API REST de ce périphérique en Java.

Comme pour tout autre protocole de communication réseau, nous avions besoin d'un client SNMP pour travailler avec différents types de demandes. Les autotests devraient être en mesure de vérifier le succès des demandes GET et SET pour les paramètres scalaires et de table. Pour les tables, il était également nécessaire de pouvoir vérifier l'ajout et la suppression d'enregistrements, si la table elle-même autorise ces opérations.

Mais en plus du client, la bibliothèque devait contenir une classe pour travailler avec les fichiers MIB. Cette classe aurait dû être capable d'analyser le fichier MIB pour obtenir les types de données, les limites des valeurs acceptables, etc., afin de ne pas coder en dur ce qui peut toujours changer.

Lors de la recherche de bibliothèques appropriées pour Java, nous n'avons trouvé aucune bibliothèque permettant de travailler à la fois avec les requêtes et les fichiers MIB. Par conséquent, nous nous sommes installés sur deux bibliothèques différentes. Pour le client, le choix de la très utilisée org.snmp4j.snmp4j (https://www.snmp4j.org) semblait tout à fait logique, et pour l'analyseur de fichiers MIB, le choix a été fait pour la bibliothèque net.percederberg.mibble, peu connue (https: // www. mibble.org). Si avec snmp4j le choix était évident, alors mibble a été choisi pour la disponibilité d'une documentation suffisamment détaillée (quoique en anglais) avec des exemples. Commençons donc.

Écriture d'un analyseur de fichiers MIB


Tous ceux qui ont déjà vu des fichiers MIB savent que c'est pénible. Essayons de résoudre ce problème à l'aide d'un analyseur simple, ce qui facilitera considérablement la recherche d'informations à partir d'un fichier, en le réduisant à appeler une méthode particulière.

Un tel analyseur peut être utilisé comme un utilitaire distinct pour travailler avec des fichiers MIB ou inclus dans tout autre projet sous SNMP, par exemple, lors de l'écriture d'un client SNMP ou du test d'automatisation.

Préparation du projet


Pour faciliter le montage, nous utilisons Maven. En fonction de la bibliothèque add net.percederberg.mibble (https://www.mibble.org), ce qui nous permettra de travailler plus facilement avec les fichiers MIB:

<dependency> <groupId>net.percederberg.mibble</groupId> <artifactId>mibble</artifactId> <version>2.9.3</version> </dependency> 

Comme il ne se trouve pas dans le référentiel Maven central, nous ajoutons le code suivant à pom.xml:

 <repositories> <repository> <id>opennms</id> <name>OpenNMS</name> <url>http://repo.opennms.org/maven2/</url> </repository> </repositories> 

Si le projet est assemblé sans erreur avec le Maven, tout est prêt pour le travail. Il ne reste plus qu'à créer une classe d'analyseur (appelons-la MIBParser) et à importer tout ce dont nous avons besoin, à savoir:

 import net.percederberg.mibble.*; 

Téléchargez et validez un fichier MIB


À l'intérieur de la classe, il n'y aura qu'un seul champ - un objet de type net.percederberg.mibble.Mib pour stocker le fichier MIB téléchargé:

 private Mib mib; 

Pour télécharger un fichier, nous écrivons cette méthode:

 private Mib loadMib(File file) throws MibLoaderException, IOException { MibLoader loader = new MibLoader(); Mib mib; file = file.getAbsoluteFile(); try { loader.addDir(file.getParentFile()); mib = loader.load(file); } catch (MibLoaderException e) { e.getLog().printTo(System.err); throw e; } catch (IOException e) { e.printStackTrace(); throw e; } return mib; } 

La classe net.percederberg.mibble.MIBLoader valide le fichier que nous essayons de charger et lève une exception net.percederberg.mibble.MibLoaderException si elle y trouve des erreurs, y compris des erreurs d'importation à partir d'autres fichiers MIB, si elles ne se trouvent pas dans le même répertoire ou ne contiennent pas de caractères MIB importés.

Dans la méthode loadMib, nous interceptons toutes les exceptions, les écrivons dans le journal et les transmettons plus loin, car à ce stade, la poursuite du travail est impossible - le dossier n'est pas valide.

Nous appelons la méthode écrite dans le constructeur de l'analyseur:

 public MIBParser(File file) throws MibLoaderException, IOException { if (!file.exists()) throw new FileNotFoundException("File not found in location: " + file.getAbsolutePath()); mib = loadMib(file.getAbsoluteFile()); if (!mib.isLoaded()) throw new MibLoaderException(file, "Not loaded."); } 

Si le fichier a été téléchargé et analysé avec succès, continuez à travailler.

Méthodes de récupération des informations d'un fichier MIB


En utilisant les méthodes de la classe net.percederberg.mibble.Mib, vous pouvez rechercher des caractères individuels d'un fichier MIB par nom ou OID en appelant respectivement les méthodes getSymbol (String name) ou getSymbolByOid (String oid). Ces méthodes renvoient l'objet net.percederberg.mibble.MibSymbol, dont nous utiliserons les méthodes pour obtenir les informations nécessaires sur un symbole MIB spécifique.

Commençons par les méthodes d'écriture les plus simples pour obtenir le nom d'un symbole par son OID et, inversement, l'OID par son nom:

 public String getName(String oid) { return mib.getSymbolByOid(oid).getName(); } public String getOid(String name) { String oid = null; MibSymbol s = mib.getSymbol(name); if (s instanceof MibValueSymbol) { oid = ((MibValueSymbol) s).getValue().toString(); if (((MibValueSymbol) s).isScalar()) oid = new OID(oid).append(0).toDottedString(); } return oid; } 

Ce sont peut-être les caractéristiques d'un fichier MIB spécifique, avec lequel je devais travailler, mais pour une raison quelconque, les paramètres scalaires ont renvoyé un OID sans zéro à la fin, donc un code a été ajouté à la méthode d'obtention de l'OID, qui, si le MIB le caractère est scalaire, il ajoute simplement ".0" à l'OID reçu en utilisant la méthode append (int index) de la classe net.percederberg.mibble.OID. Si cela fonctionne pour vous sans béquille, félicitations :)

Pour obtenir le reste des données sur le symbole, nous écrivons une méthode auxiliaire, où nous obtenons l'objet net.percederberg.mibble.snmp.SnmpObjectType, qui contient toutes les informations nécessaires sur le symbole MIB à partir duquel il a été obtenu.

 private SnmpObjectType getSnmpObjectType(MibSymbol symbol) { if (symbol instanceof MibValueSymbol) { MibType type = ((MibValueSymbol) symbol).getType(); if (type instanceof SnmpObjectType) { return (SnmpObjectType) type; } } return null; } 

Par exemple, nous pouvons obtenir le type du symbole MIB:

 public String getType(String name) { MibSymbol s = mib.getSymbol(name); if (getSnmpObjectType(s).getSyntax().getReferenceSymbol() == null) return getSnmpObjectType(s).getSyntax().getName(); else return getSnmpObjectType(s).getSyntax().getReferenceSymbol().getName(); } 

Il y a 2 façons d'obtenir du type, car pour les types primitifs, la première option fonctionne:

 getSnmpObjectType(s).getSyntax().getName(); 

et pour les importés, le second:

 getSnmpObjectType(s).getSyntax().getReferenceSymbol().getName(); 

Vous pouvez obtenir le niveau d'accès d'un personnage:

 public String getAccess(String name) { MibSymbol s = mib.getSymbol(name); return getSnmpObjectType(s).getAccess().toString(); } 

Valeur minimale valide pour un paramètre numérique:

 public Integer getDigitMinValue(String name) { MibSymbol s = mib.getSymbol(name); String syntax = getSnmpObjectType(s).getSyntax().toString(); if (syntax.contains("STRING")) return null; Pattern p = Pattern.compile("(-?\\d+)..(-?\\d+)"); Matcher m = p.matcher(syntax); if (m.find()) { return Integer.parseInt(m.group(1)); } return null; } 

Valeur maximale autorisée d'un paramètre numérique:

 public Integer getDigitMaxValue(String name) { MibSymbol s = mib.getSymbol(name); String syntax = getSnmpObjectType(s).getSyntax().toString(); if (syntax.contains("STRING")) return null; Pattern p = Pattern.compile("(-?\\d+)..(-?\\d+)"); Matcher m = p.matcher(syntax); if (m.find()) { return Integer.parseInt(m.group(2)); } return null; } 

Longueur de chaîne minimale autorisée (dépend du type de chaîne):

 public Integer getStringMinLength(String name) { MibSymbol s = this.mib.getSymbol(name); String syntax = this.getSnmpObjectType(s).getSyntax().toString(); Pattern p = Pattern.compile("(-?\\d+)..(-?\\d+)"); Matcher m = p.matcher(syntax); return syntax.contains("STRING") && m.find()?Integer.valueOf(Integer.parseInt(m.group(1))):null; } 

Longueur de chaîne maximale autorisée (dépend du type de chaîne):

 public Integer getStringMaxLength(String name) { MibSymbol s = mib.getSymbol(name); String syntax = getSnmpObjectType(s).getSyntax().toString(); Pattern p = Pattern.compile("(-?\\d+)..(-?\\d+)"); Matcher m = p.matcher(syntax); if (syntax.contains("STRING") && m.find()) { return Integer.parseInt(m.group(2)); } return null; } 

Vous pouvez également obtenir les noms de toutes les colonnes du tableau par son nom:

 public ArrayList<String> getTableColumnNames(String tableName) { ArrayList<String> mibSymbolNamesList = new ArrayList<>(); MibValueSymbol table = (MibValueSymbol) mib.findSymbol(tableName, true); if (table.isTable() && table.getChild(0).isTableRow()) { MibValueSymbol[] symbols = table.getChild(0).getChildren(); for (MibValueSymbol mvs : symbols) { mibSymbolNamesList.add(mvs.getName()); } } return mibSymbolNamesList; } 

Tout d'abord, de manière standard, nous obtenons un symbole MIB par son nom:

 MibValueSymbol table = (MibValueSymbol) mib.findSymbol(tableName, true); 

Ensuite, nous vérifions s'il s'agit d'une table et que le symbole MIB enfant est une ligne de cette table et, si la condition renvoie true, dans la boucle, nous parcourons les enfants de la ligne de la table et ajoutons le nom de l'élément au tableau résultant:

 if (table.isTable() && table.getChild(0).isTableRow()) { MibValueSymbol[] symbols = table.getChild(0).getChildren(); for (MibValueSymbol mvs : symbols) { mibSymbolNamesList.add(mvs.getName()); } } 

Résumé


Ces méthodes sont suffisantes pour obtenir des informations du fichier MIB pour chaque caractère spécifique, ne connaissant que son nom. Par exemple, lors de l'écriture d'un client SNMP, vous pouvez y inclure un analyseur afin que les méthodes client n'acceptent pas les OID, mais les noms de symboles MIB. Cela augmentera la fiabilité du code, car une faute de frappe dans OID peut ne pas conduire au caractère auquel nous voulons faire référence. Et pas d'OID - pas de problèmes.

Le plus est la lisibilité du code, ce qui signifie sa maintenabilité. Il est plus facile de comprendre l'essence du projet si le code fonctionne avec des noms humains.

Une autre application est l'automatisation des tests. Dans les données de test, on peut obtenir dynamiquement des valeurs limites de paramètres numériques à partir d'un fichier MIB. Ainsi, si les valeurs limites de certains symboles MIB dans la nouvelle version du composant testé changent, vous n'aurez pas à modifier le code d'autotest.

En général, en utilisant un analyseur, travailler avec des fichiers MIB devient beaucoup plus agréable, et ils cessent d'être une telle douleur.

Source: https://habr.com/ru/post/fr427267/


All Articles