-
-
Notifications
You must be signed in to change notification settings - Fork 14
/
2013-snimpy.html
234 lines (192 loc) · 9.01 KB
/
2013-snimpy.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
---
title: "Snimpy : SNMP & Python"
uuid: ce260d95-a456-4e9c-8780-f155da37ce54
tags:
- network-snmp
- programming-python
---
Souvent considéré comme désuet, [SNMP][] est toujours omniprésent pour
interagir avec des équipements réseau. Pour la supervision, il permet
d'exposer **diverses métriques** telles que les compteurs liés aux
interfaces réseau. Il permet également d'interagir sur la
**configuration des équipements**.
Les variables exposées par les _agents_ SNMP (serveurs) sont contenues
dans une base hiérarchique appelée
[_Management Information Base_][Management Information Bases] (ou
_MIB_[^smi]). Chaque entrée est identifiée par un OID. En interrogeant un
OID spécifique, un _manager_ (client) obtient la valeur associée à la
variable.
[^smi]: Une MIB est définie à l'aide de SMI, un sous-ensemble de
ASN.1. Toutefois, il n'est pas rare d'appeler également
« MIB » cette définition.
Par exemple, `IF-MIB` est une MIB présentée dans la [RFC 2863][]. Elle
contient les objets utilisés pour gérer les interfaces réseau. L'un
d'eux est `ifTable` dont chaque ligne représente une interface réseau
logique : son nom, ses caractéristiques et divers compteurs.
| ifIndex | ifDescr | ifPhysAddress | ifOperStatus | ifOutOctets |
| --------- | --------- | --------------- | -------------- | ------------- |
| 1 | lo | | up | 545721741 |
| 2 | eth0 | 0:18:f3:3:4e:4 | up | 78875421 |
| 3 | eth1 | 0:18:f3:3:4e:5 | down | 0 |
`ifTable` est indexée par sa première colonne, `ifIndex`. Pour obtenir
le statut opérationnel de la seconde interface, il suffit d'interroger
`IF-MIB::ifOperStatus.2` que l'on peut traduire vers l'OID
`.1.3.6.1.2.1.2.2.1.8.2` à l'aide des informations contenues dans la
définition de la MIB.
# Automatiser SNMP
Un agent SNMP peut fournir de nombreuses informations intéressantes :
- les compteurs des interfaces réseau via l'[IF-MIB][RFC 2863],
- les adresses IP via l'[IP-MIB][RFC 4293],
- les tables de routage via l'[IP-FORWARD-MIB][RFC 4292],
- l'inventaire physique via l'[ENTITY-MIB][RFC 4133],
- les voisins via la [LLDP-MIB][IEEE 802.1AB].
Des outils tels que `snmpget` et `snmpwalk` permettent de collecter
manuellement ces informations :
::console
$ snmpwalk -v 2c -c public localhost IF-MIB::ifDescr
IF-MIB::ifDescr.1 = STRING: lo
IF-MIB::ifDescr.2 = STRING: eth0
IF-MIB::ifDescr.3 = STRING: eth1
Toutefois, il est assez fastidieux de construire des scripts robustes
à l'aide de ceux-ci. Par exemple, voici un script pour obtenir les
descriptions de toutes les interfaces réseau actives ainsi que le
nombre total d'octets transmis :
::sh
#!/bin/sh
set -e
host="${1:-localhost}"
community="${2:-public}"
args="-v2c -c $community $host"
for idx in $(snmpwalk -Ov -OQ $args IF-MIB::ifIndex); do
descr=$(snmpget -Ov -OQ $args IF-MIB::ifDescr.$idx)
oper=$(snmpget -Ov -OQ $args IF-MIB::ifOperStatus.$idx)
in=$(snmpget -Ov -OQ $args IF-MIB::ifInOctets.$idx)
out=$(snmpget -Ov -OQ $args IF-MIB::ifOutOctets.$idx)
[ x"$descr" != x"lo" ] || continue
[ x"$oper" = x"up" ] || continue
echo $descr $in $out
done
Heureusement, il existe des extensions SNMP pour la plupart des
langages. À titre d'exemple, voici comment pourrait être réécrit le
script ci-dessus en exploitant l'extension SNMP pour Python livrée
avec Net-SNMP :
::python
import argparse
import netsnmp
parser = argparse.ArgumentParser()
parser.add_argument(
"host", default="localhost", nargs="?",
help="Agent to retrieve variables from")
parser.add_argument(
"community", default="public", nargs="?",
help="Community to query the agent")
options = parser.parse_args()
args = {
"Version": 2,
"DestHost": options.host,
"Community": options.community
}
for idx in netsnmp.snmpwalk(netsnmp.Varbind("IF-MIB::ifIndex"),
**args):
descr, oper, cin, cout = netsnmp.snmpget(
netsnmp.Varbind("IF-MIB::ifDescr", idx),
netsnmp.Varbind("IF-MIB::ifOperStatus", idx),
netsnmp.Varbind("IF-MIB::ifInOctets", idx),
netsnmp.Varbind("IF-MIB::ifOutOctets", idx),
**args)
assert(descr is not None and
cin is not None and
cout is not None) # ❶
if descr == "lo":
continue
if oper != "1": # ❷
continue
print("{} {} {}".format(descr, cin, cout))
Cette extension a plusieurs défauts importants :
1. Tout est présenté sous forme de chaînes de caractères comme on
peut le voir en ❷.
2. La gestion des erreurs est inexistante. En cas d'erreur sur le nom
d'une variable, le message `snmp_build: unknown failure` s'affiche
mais aucune exception n'est levée. Si une variable n'existe pas,
les fonctions retournent `None`. Voir en ❶.
L'impossibilité de gérer les erreurs rend cette extension
dangereuse. On ne peut imaginer effectuer des modifications
importantes sur la base des réponses retournées. Une vérification
oubliée et un script peut enchaîner des actions inappropriées !
# Snimpy
N'ayant pas trouvé d'extension fiable pour Python, j'ai donc décidé
d'écrire [Snimpy][] avec deux principaux objectifs :
1. S'appuyer sur les informations contenues dans les MIB pour fournir
une **interface pythonique**.
2. Toute erreur doit se traduire en une **exception**.
Voici comment le précédent script pourrait être réécrit :
::python
#!/usr/bin/env snimpy
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
"host", default="localhost", nargs="?",
help="Agent to retrieve variables from")
parser.add_argument(
"community", default="public", nargs="?",
help="Community to query the agent")
options = parser.parse_args()
m = M(options.host, options.community, 2)
load("IF-MIB")
for idx in m.ifDescr:
if m.ifDescr[idx] == "lo":
continue
if m.ifOperStatus[idx] != "up":
continue
print("{} {} {}".format(m.ifDescr[idx],
m.ifInOctets[idx],
m.ifOutOctets[idx]))
Une alternative est d'utiliser les listes en compréhension :
::python
load("IF-MIB")
print("\n".join([
"{} {} {}".format(
m.ifDescr[idx],
m.ifInOctets[idx],
m.ifOutOctets[idx])
for idx in m.ifDescr
if m.ifDescr[idx] != "lo"
and m.ifOperStatus[idx] == "up" ]))
Voici un autre exemple pour obtenir la table de routage de l'agent :
::python
load("IP-FORWARD-MIB")
m=M("localhost", "public", 2)
routes = m.ipCidrRouteNextHop
for x in routes:
net, netmask, tos, src = x
print("{:>15s}/{:<15s} via {:<15s} src {:<15s}".format(
net, netmask, routes[x], src))
`IP-FORWARD-MIB::ipCidrRouteNextHop` est issue d'une table utilisant
un index composé. Cependant, son utilisation semble toujours
naturelle.
Techniquement, les requêtes SNMP sont gérées par [PySNMP][] et les MIB
sont interprétées à l'aide de [libsmi][][^libsmi]. _Snimpy_ fonctionne
à la fois avec Python 2 et Python 3. Pour plus d'informations, jetez
un œil sur la [documentation de Snimpy][Snimpy's documentation].
[^libsmi]: <del>À l'heure actuelle, il n'existe malheureusement pas
d'analyseur robuste pour SMI écrit en Python. Par exemple,
[PySNMP][] s'appuie sur l'outil `smidump` livré avec
[libsmi][].</del> [PySNMP][] s'appuie désormais sur
[PySMI][]. _Snimpy_ exploite _libsmi_ via [CFFI][].
*[SNMP]: Simple Network Management Protocol
*[OID]: Object Identifier
*[MIB]: Management Information Base
*[SMI]: Structure of Management Information
[SNMP]: https://fr.wikipedia.org/wiki/Snmp "Simple Network Management Protocol sur Wikipédia"
[Management Information Bases]: https://en.wikipedia.org/wiki/Management_information_base "Management Information Base sur Wikipédia"
[RFC 2863]: rfc://2863 "RFC 2863: The Interfaces Group MIB"
[RFC 4133]: rfc://4133 "RFC 4133: Entity MIB"
[RFC 4292]: rfc://4292 "RFC 4292: IP Forwarding Table MIB"
[RFC 4293]: rfc://4293 "RFC 4293: Management Information Base for the Internet Protocol (IP)"
[IEEE 802.1AB]: https://sci-hub.tw/10.1109/IEEESTD.2005.96285 "IEEE 802.1AB-2005 on Sci-Hub"
[Snimpy]: https://github.com/vincentbernat/snimpy "Snimpy: interactive SNMP tool with Python"
[PySNMP]: http://pysnmp.sourceforge.net/ "The PySNMP project"
[libsmi]: https://www.ibr.cs.tu-bs.de/projects/libsmi/ "libsmi: a library to access SMI MIB information"
[Snimpy's documentation]: https://snimpy.readthedocs.io/en/latest/ "Documentation de Snimpy sur ReadTheDocs"
[CFFI]: https://cffi.readthedocs.io/en/latest/ "CFFI: Foreign Function interface for Python calling C code"
[PySMI]: https://web.archive.org/web/2013/http://snmplabs.com/pysmi/ "SNMP SMI compiler"