2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
10 # | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
29 from typing
import Any
, Dict
, List
# pylint: disable=unused-import
31 import cmk
.utils
.debug
32 import cmk
.utils
.tty
as tty
33 from cmk
.utils
.exceptions
import MKGeneralException
, MKBailOut
34 import cmk
.utils
.store
as store
37 import cmk_base
.config
as config
38 import cmk_base
.console
as console
39 import cmk_base
.classic_snmp
as classic_snmp
40 import cmk_base
.ip_lookup
as ip_lookup
41 import cmk_base
.agent_simulator
42 from cmk_base
.exceptions
import MKSNMPError
43 import cmk_base
.cleanup
44 import cmk_base
.snmp_utils
47 import cmk_base
.cee
.inline_snmp
as inline_snmp
49 inline_snmp
= None # type: ignore
51 _enforce_stored_walks
= False
53 # TODO: Replace this by generic caching
54 _g_single_oid_hostname
= None
55 _g_single_oid_ipaddress
= None
56 _g_single_oid_cache
= None
57 _g_walk_cache
= {} # type: Dict[str, List[str]]
60 # .--caching-------------------------------------------------------------.
62 # | ___ __ _ ___| |__ (_)_ __ __ _ |
63 # | / __/ _` |/ __| '_ \| | '_ \ / _` | |
64 # | | (_| (_| | (__| | | | | | | | (_| | |
65 # | \___\__,_|\___|_| |_|_|_| |_|\__, | |
67 # '----------------------------------------------------------------------'
72 def initialize_single_oid_cache(access_data
, from_disk
=False):
73 hostname
= access_data
["hostname"]
74 ipaddress
= access_data
["ipaddress"]
75 global _g_single_oid_cache
, _g_single_oid_ipaddress
, _g_single_oid_hostname
77 if not (_g_single_oid_hostname
== hostname \
78 and _g_single_oid_ipaddress
== ipaddress
) \
79 or _g_single_oid_cache
is None:
80 _g_single_oid_hostname
= hostname
81 _g_single_oid_ipaddress
= ipaddress
83 _g_single_oid_cache
= _load_single_oid_cache(access_data
)
85 _g_single_oid_cache
= {}
88 def write_single_oid_cache(access_data
):
89 if not _g_single_oid_cache
:
92 cache_dir
= cmk
.utils
.paths
.snmp_scan_cache_dir
93 if not os
.path
.exists(cache_dir
):
94 os
.makedirs(cache_dir
)
95 cache_path
= "%s/%s.%s" % (cache_dir
, access_data
["hostname"], access_data
["ipaddress"])
96 store
.save_data_to_file(cache_path
, _g_single_oid_cache
, pretty
=False)
99 def set_single_oid_cache(hostname
, oid
, value
):
100 _g_single_oid_cache
[oid
] = value
103 def _is_in_single_oid_cache(hostname
, oid
):
104 return oid
in _g_single_oid_cache
107 def _get_oid_from_single_oid_cache(hostname
, oid
):
108 return _g_single_oid_cache
.get(oid
)
111 def _load_single_oid_cache(access_data
):
112 cache_path
= "%s/%s.%s" % (cmk
.utils
.paths
.snmp_scan_cache_dir
, access_data
["hostname"],
113 access_data
["ipaddress"])
114 return store
.load_data_from_file(cache_path
, {})
117 def cleanup_host_caches():
120 _clear_other_hosts_oid_cache(None)
122 inline_snmp
.cleanup_inline_snmp_globals()
125 def _clear_other_hosts_oid_cache(hostname
):
126 global _g_single_oid_cache
, _g_single_oid_ipaddress
, _g_single_oid_hostname
127 if _g_single_oid_hostname
!= hostname
:
128 _g_single_oid_cache
= None
129 _g_single_oid_hostname
= hostname
130 _g_single_oid_ipaddress
= None
134 # .--Generic SNMP--------------------------------------------------------.
135 # | ____ _ ____ _ _ __ __ ____ |
136 # | / ___| ___ _ __ ___ _ __(_) ___ / ___|| \ | | \/ | _ \ |
137 # | | | _ / _ \ '_ \ / _ \ '__| |/ __| \___ \| \| | |\/| | |_) | |
138 # | | |_| | __/ | | | __/ | | | (__ ___) | |\ | | | | __/ |
139 # | \____|\___|_| |_|\___|_| |_|\___| |____/|_| \_|_| |_|_| |
141 # +----------------------------------------------------------------------+
142 # | Top level functions to realize SNMP functionality for Check_MK. |
143 # '----------------------------------------------------------------------'
146 def get_snmp_table(access_data
, check_plugin_name
, oid_info
, use_snmpwalk_cache
):
147 hostname
= access_data
["hostname"]
148 # oid_info is either ( oid, columns ) or
149 # ( oid, suboids, columns )
150 # suboids is a list if OID-infixes that are put between baseoid
151 # and the columns and also prefixed to the index column. This
152 # allows to merge distinct SNMP subtrees with a similar structure
153 # to one virtual new tree (look into cmctc_temp for an example)
154 if len(oid_info
) == 2:
155 oid
, targetcolumns
= oid_info
158 oid
, suboids
, targetcolumns
= oid_info
160 if not oid
.startswith("."):
161 raise MKGeneralException("OID definition '%s' does not begin with ." % oid
)
166 for suboid
in suboids
:
168 # Detect missing (empty columns)
172 for colno
, column
in enumerate(targetcolumns
):
173 fetchoid
, value_encoding
= _compute_fetch_oid(oid
, suboid
, column
)
175 # column may be integer or string like "1.5.4.2.3"
176 # if column is 0, we do not fetch any data from snmp, but use
177 # a running counter as index. If the index column is the first one,
178 # we do not know the number of entries right now. We need to fill
179 # in later. If the column is OID_STRING or OID_BIN we do something
180 # similar: we fill in the complete OID of the entry, either as
181 # string or as binary UTF-8 encoded number string
183 cmk_base
.snmp_utils
.OID_END
, cmk_base
.snmp_utils
.OID_STRING
,
184 cmk_base
.snmp_utils
.OID_BIN
, cmk_base
.snmp_utils
.OID_END_BIN
,
185 cmk_base
.snmp_utils
.OID_END_OCTET_STRING
187 if index_column
>= 0 and index_column
!= colno
:
188 raise MKGeneralException(
189 "Invalid SNMP OID specification in implementation of check. "
190 "You can only use one of OID_END, OID_STRING, OID_BIN, OID_END_BIN and OID_END_OCTET_STRING."
193 columns
.append((fetchoid
, [], "string"))
194 index_format
= column
197 rowinfo
= _get_snmpwalk(access_data
, check_plugin_name
, oid
, fetchoid
, column
,
200 columns
.append((fetchoid
, rowinfo
, value_encoding
))
201 number_of_rows
= len(rowinfo
)
202 if number_of_rows
> max_len
:
203 max_len
= number_of_rows
206 if index_column
!= -1:
208 # Take end-oids of non-index columns as indices
209 fetchoid
, max_column
, value_encoding
= columns
[max_len_col
]
210 for o
, _unused_value
in max_column
:
211 if index_format
== cmk_base
.snmp_utils
.OID_END
:
212 index_rows
.append((o
, _extract_end_oid(fetchoid
, o
)))
213 elif index_format
== cmk_base
.snmp_utils
.OID_STRING
:
214 index_rows
.append((o
, o
))
215 elif index_format
== cmk_base
.snmp_utils
.OID_BIN
:
216 index_rows
.append((o
, _oid_to_bin(o
)))
217 elif index_format
== cmk_base
.snmp_utils
.OID_END_BIN
:
218 index_rows
.append((o
, _oid_to_bin(_extract_end_oid(fetchoid
, o
))))
219 elif index_format
== cmk_base
.snmp_utils
.OID_END_OCTET_STRING
:
220 index_rows
.append((o
, _oid_to_bin(_extract_end_oid(fetchoid
, o
))[1:]))
222 raise MKGeneralException("Invalid index format %s" % index_format
)
224 index_encoding
= columns
[index_column
][-1]
225 columns
[index_column
] = fetchoid
, index_rows
, index_encoding
227 # prepend suboid to first column
228 if suboid
and columns
:
229 fetchoid
, first_column
, value_encoding
= columns
[0]
230 new_first_column
= []
231 for o
, val
in first_column
:
232 new_first_column
.append((o
, str(suboid
) + "." + str(val
)))
233 columns
[0] = fetchoid
, new_first_column
, value_encoding
235 # Here we have to deal with a nasty problem: Some brain-dead devices
236 # omit entries in some sub OIDs. This happens e.g. for CISCO 3650
237 # in the interfaces MIB with 64 bit counters. So we need to look at
238 # the OIDs and watch out for gaps we need to fill with dummy values.
239 new_columns
= _sanitize_snmp_table_columns(columns
)
241 # From all SNMP data sources (stored walk, classic SNMP, inline SNMP) we
242 # get normal python strings. But for Check_MK we need unicode strings now.
243 # Convert them by using the standard Check_MK approach for incoming data
244 sanitized_columns
= _sanitize_snmp_encoding(hostname
, new_columns
)
246 info
+= _construct_snmp_table_of_rows(sanitized_columns
)
251 # Contextes can only be used when check_plugin_name is given.
252 def get_single_oid(access_data
, oid
, check_plugin_name
=None, do_snmp_scan
=True):
253 # New in Check_MK 1.1.11: oid can end with ".*". In that case
254 # we do a snmpgetnext and try to find an OID with the prefix
255 # in question. The *cache* is working including the X, however.
256 hostname
= access_data
["hostname"]
258 if cmk
.utils
.debug
.enabled():
259 raise MKGeneralException("OID definition '%s' does not begin with a '.'" % oid
)
263 # TODO: Use generic cache mechanism
264 if _is_in_single_oid_cache(hostname
, oid
):
265 console
.vverbose(" Using cached OID %s: " % oid
)
266 value
= _get_oid_from_single_oid_cache(hostname
, oid
)
267 console
.vverbose("%s%s%s%s\n" % (tty
.bold
, tty
.green
, value
, tty
.normal
))
270 console
.vverbose(" Getting OID %s: " % oid
)
271 if _enforce_stored_walks
or config
.is_usewalk_host(hostname
):
272 walk
= _get_stored_snmpwalk(hostname
, oid
)
273 # get_stored_snmpwalk returns all oids that start with oid but here
274 # we need an exact match
275 if len(walk
) == 1 and oid
== walk
[0][0]:
277 elif oid
.endswith(".*") and len(walk
) > 0:
283 # get_single_oid() can only return a single value. When SNMPv3 is used with multiple
284 # SNMP contexts, all contextes will be queried until the first answer is received.
285 if check_plugin_name
is not None and config
.is_snmpv3_host(hostname
):
286 snmp_contexts
= _snmpv3_contexts_of(hostname
, check_plugin_name
)
288 snmp_contexts
= [None]
290 for context_name
in snmp_contexts
:
292 if config
.is_inline_snmp_host(hostname
):
293 value
= inline_snmp
.get(
296 ipaddress
=access_data
["ipaddress"],
297 context_name
=context_name
,
298 credentials
=access_data
["credentials"])
300 value
= classic_snmp
.get(access_data
, oid
, context_name
=context_name
)
302 if value
is not None:
303 break # Use first received answer in case of multiple contextes
305 if cmk
.utils
.debug
.enabled():
309 if value
is not None:
310 console
.vverbose("%s%s%s%s\n" % (tty
.bold
, tty
.green
, value
, tty
.normal
))
312 console
.vverbose("failed.\n")
314 set_single_oid_cache(hostname
, oid
, value
)
318 def walk_for_export(access_data
, oid
):
319 hostname
= access_data
["hostname"]
320 if config
.is_inline_snmp_host(hostname
):
321 rows
= inline_snmp
.walk(
325 ipaddress
=access_data
["ipaddress"],
326 credentials
=access_data
["credentials"])
327 return inline_snmp
.convert_rows_for_stored_walk(rows
)
329 return classic_snmp
.walk(access_data
, oid
, hex_plain
=True)
332 def enforce_use_stored_walks():
333 global _enforce_stored_walks
334 _enforce_stored_walks
= True
338 # .--SNMP helpers--------------------------------------------------------.
339 # | ____ _ _ __ __ ____ _ _ |
340 # | / ___|| \ | | \/ | _ \ | |__ ___| |_ __ ___ _ __ ___ |
341 # | \___ \| \| | |\/| | |_) | | '_ \ / _ \ | '_ \ / _ \ '__/ __| |
342 # | ___) | |\ | | | | __/ | | | | __/ | |_) | __/ | \__ \ |
343 # | |____/|_| \_|_| |_|_| |_| |_|\___|_| .__/ \___|_| |___/ |
345 # +----------------------------------------------------------------------+
346 # | Internal helpers for processing SNMP things |
347 # '----------------------------------------------------------------------'
350 def _oid_to_bin(oid
):
351 return u
"".join([unichr(int(p
)) for p
in oid
.strip(".").split(".")])
354 def _extract_end_oid(prefix
, complete
):
355 return complete
[len(prefix
):].lstrip('.')
358 # sort OID strings numerically
359 def _oid_to_intlist(oid
):
361 return map(int, oid
.split('.'))
366 def _cmp_oids(o1
, o2
):
367 return cmp(_oid_to_intlist(o1
), _oid_to_intlist(o2
))
370 def _cmp_oid_pairs(pair1
, pair2
):
371 return cmp(_oid_to_intlist(pair1
[0].lstrip('.')), _oid_to_intlist(pair2
[0].lstrip('.')))
374 def _snmpv3_contexts_of(hostname
, check_plugin_name
):
375 for ty
, rules
in config
.snmpv3_contexts_of(hostname
):
376 if ty
is None or ty
== check_plugin_name
:
381 def _get_snmpwalk(access_data
, check_plugin_name
, oid
, fetchoid
, column
, use_snmpwalk_cache
):
382 hostname
= access_data
["hostname"]
383 is_cachable
= _is_snmpwalk_cachable(column
)
385 if is_cachable
and use_snmpwalk_cache
:
386 # Returns either the cached SNMP walk or None when nothing is cached
387 rowinfo
= _get_cached_snmpwalk(hostname
, fetchoid
)
390 if _enforce_stored_walks
or config
.is_usewalk_host(hostname
):
391 rowinfo
= _get_stored_snmpwalk(hostname
, fetchoid
)
393 rowinfo
= _perform_snmpwalk(access_data
, check_plugin_name
, oid
, fetchoid
)
396 _save_snmpwalk_cache(hostname
, fetchoid
, rowinfo
)
401 def _perform_snmpwalk(access_data
, check_plugin_name
, base_oid
, fetchoid
):
402 hostname
= access_data
["hostname"]
405 if config
.is_snmpv3_host(hostname
):
406 snmp_contexts
= _snmpv3_contexts_of(hostname
, check_plugin_name
)
408 snmp_contexts
= [None]
410 for context_name
in snmp_contexts
:
411 if config
.is_inline_snmp_host(hostname
):
412 rows
= inline_snmp
.walk(
417 context_name
=context_name
,
418 ipaddress
=access_data
["ipaddress"],
419 credentials
=access_data
["credentials"])
421 rows
= classic_snmp
.walk(access_data
, fetchoid
, context_name
=context_name
)
423 # I've seen a broken device (Mikrotik Router), that broke after an
424 # update to RouterOS v6.22. It would return 9 time the same OID when
425 # .1.3.6.1.2.1.1.1.0 was being walked. We try to detect these situations
426 # by removing any duplicate OID information
427 if len(rows
) > 1 and rows
[0][0] == rows
[1][0]:
429 "Detected broken SNMP agent. Ignoring duplicate OID %s.\n" % rows
[0][0])
432 for row_oid
, val
in rows
:
433 if row_oid
in added_oids
:
434 console
.vverbose("Duplicate OID found: %s (%s)\n" % (row_oid
, val
))
436 rowinfo
.append((row_oid
, val
))
437 added_oids
.add(row_oid
)
442 def _compute_fetch_oid(oid
, suboid
, column
):
444 value_encoding
= "string"
447 fetchoid
+= "." + str(suboid
)
450 if isinstance(column
, tuple):
451 fetchoid
+= "." + str(column
[1])
452 if column
[0] == "binary":
453 value_encoding
= "binary"
455 fetchoid
+= "." + str(column
)
457 return fetchoid
, value_encoding
460 def _sanitize_snmp_encoding(hostname
, columns
):
461 decode_string_func
= lambda s
: _snmp_decode_string(hostname
, s
)
463 for index
, (column
, value_encoding
) in enumerate(columns
):
464 if value_encoding
== "string":
465 columns
[index
] = map(decode_string_func
, column
)
467 columns
[index
] = map(_snmp_decode_binary
, column
)
471 def _snmp_decode_string(hostname
, text
):
472 encoding
= config
.snmp_character_encoding_of(hostname
)
474 return text
.decode(encoding
)
476 # Try to determine the current string encoding. In case a UTF-8 decoding fails, we decode latin1.
478 return text
.decode('utf-8')
480 return text
.decode('latin1')
483 def _snmp_decode_binary(text
):
484 return map(ord, text
)
487 def _sanitize_snmp_table_columns(columns
):
488 # First compute the complete list of end-oids appearing in the output
489 # by looping all results and putting the endoids to a flat list
491 for fetchoid
, column
, value_encoding
in columns
:
492 for o
, value
in column
:
493 endoid
= _extract_end_oid(fetchoid
, o
)
494 if endoid
not in endoids
:
495 endoids
.append(endoid
)
497 # The list needs to be sorted to prevent problems when the first
498 # column has missing values in the middle of the tree.
499 if not _are_ascending_oids(endoids
):
500 endoids
.sort(cmp=_cmp_oids
)
505 # Now fill gaps in columns where some endois are missing
507 for fetchoid
, column
, value_encoding
in columns
:
508 # It might happen that end OIDs are not ordered. Fix the OID sorting to make
509 # it comparable to the already sorted endoids list. Otherwise we would get
510 # some mixups when filling gaps
512 column
.sort(cmp=_cmp_oid_pairs
)
516 # Loop all lines to fill holes in the middle of the list. All
517 # columns check the following lines for the correct endoid. If
518 # an endoid differs empty values are added until the hole is filled
519 for o
, value
in column
:
520 eo
= _extract_end_oid(fetchoid
, o
)
521 if len(column
) != len(endoids
):
522 while i
< len(endoids
) and endoids
[i
] != eo
:
523 new_column
.append("") # (beginoid + '.' +endoids[i], "" ) )
525 new_column
.append(value
)
528 # At the end check if trailing OIDs are missing
529 while i
< len(endoids
):
530 new_column
.append("") # (beginoid + '.' +endoids[i], "") )
532 new_columns
.append((new_column
, value_encoding
))
537 def _are_ascending_oids(oid_list
):
538 for a
in range(len(oid_list
) - 1):
539 if _cmp_oids(oid_list
[a
], oid_list
[a
+ 1]) > 0: # == 0 should never happen
544 def _construct_snmp_table_of_rows(columns
):
548 # Now construct table by swapping X and Y.
550 for index
in range(len(columns
[0])):
551 row
= [c
[index
] for c
in columns
]
556 def _is_snmpwalk_cachable(column
):
557 return isinstance(column
, tuple) and column
[0] == "cached"
560 def _get_cached_snmpwalk(hostname
, fetchoid
):
561 path
= _snmpwalk_cache_path(hostname
, fetchoid
)
563 console
.vverbose(" Loading %s from walk cache %s\n" % (fetchoid
, path
))
564 return store
.load_data_from_file(path
)
566 if cmk
.utils
.debug
.enabled():
568 console
.verbose(" Failed loading walk cache. Continue without it.\n" % path
)
572 def _save_snmpwalk_cache(hostname
, fetchoid
, rowinfo
):
573 path
= _snmpwalk_cache_path(hostname
, fetchoid
)
575 if not os
.path
.exists(os
.path
.dirname(path
)):
576 os
.makedirs(os
.path
.dirname(path
))
578 console
.vverbose(" Saving walk of %s to walk cache %s\n" % (fetchoid
, path
))
579 store
.save_data_to_file(path
, rowinfo
, pretty
=False)
582 def _snmpwalk_cache_path(hostname
, fetchoid
):
583 return os
.path
.join(cmk
.utils
.paths
.var_dir
, "snmp_cache", hostname
, fetchoid
)
586 def _get_stored_snmpwalk(hostname
, oid
):
587 if oid
.startswith("."):
590 if oid
.endswith(".*"):
591 oid_prefix
= oid
[:-2]
597 path
= cmk
.utils
.paths
.snmpwalks_dir
+ "/" + hostname
599 console
.vverbose(" Loading %s from %s\n" % (oid
, path
))
603 # New implementation: use binary search
604 def to_bin_string(oid
):
606 return tuple(map(int, oid
.strip(".").split(".")))
608 raise MKGeneralException("Invalid OID %s" % oid
)
610 def compare_oids(a
, b
):
611 aa
= to_bin_string(a
)
612 bb
= to_bin_string(b
)
613 if len(aa
) <= len(bb
) and bb
[:len(aa
)] == aa
:
619 if hostname
in _g_walk_cache
:
620 lines
= _g_walk_cache
[hostname
]
623 lines
= file(path
).readlines()
625 raise MKSNMPError("No snmpwalk file %s" % path
)
626 _g_walk_cache
[hostname
] = lines
631 while end
- begin
> 0:
632 current
= (begin
+ end
) / 2
633 parts
= lines
[current
].split(None, 1)
635 hit
= compare_oids(oid_prefix
, comp
)
638 elif hit
== 1: # we are too low
644 return [] # not found
646 def collect_until(index
, direction
):
648 # Handle case, where we run after the end of the lines list
649 if index
>= len(lines
):
656 parts
= line
.split(None, 1)
658 if o
.startswith('.'):
660 if o
== oid
or o
.startswith(oid_prefix
+ "."):
663 value
= cmk_base
.agent_simulator
.process(parts
[1])
665 value
= parts
[1] # agent simulator missing in precompiled mode
668 # Fix for missing starting oids
669 rows
.append(('.' + o
, classic_snmp
.strip_snmp_value(value
)))
671 if index
< 0 or index
>= len(lines
):
677 rowinfo
= collect_until(current
, -1)
679 rowinfo
+= collect_until(current
+ 1, 1)
688 # .--Main modes----------------------------------------------------------.
690 # | | \/ | __ _(_)_ __ _ __ ___ ___ __| | ___ ___ |
691 # | | |\/| |/ _` | | '_ \ | '_ ` _ \ / _ \ / _` |/ _ \/ __| |
692 # | | | | | (_| | | | | | | | | | | | (_) | (_| | __/\__ \ |
693 # | |_| |_|\__,_|_|_| |_| |_| |_| |_|\___/ \__,_|\___||___/ |
695 # +----------------------------------------------------------------------+
696 # | Some main modes to help the user |
697 # '----------------------------------------------------------------------'
700 def do_snmptranslate(walk_filename
):
701 if not walk_filename
:
702 raise MKGeneralException("Please provide the name of a SNMP walk file")
704 walk_path
= "%s/%s" % (cmk
.utils
.paths
.snmpwalks_dir
, walk_filename
)
705 if not os
.path
.exists(walk_path
):
706 raise MKGeneralException("The walk '%s' does not exist" % walk_path
)
708 def translate(lines
):
711 oids_for_command
= []
713 oids_for_command
.append(line
.split(" ")[0])
715 command
= ["snmptranslate", "-m", "ALL",
716 "-M+%s" % cmk
.utils
.paths
.local_mibs_dir
] + oids_for_command
717 p
= subprocess
.Popen(
718 command
, stdout
=subprocess
.PIPE
, stderr
=open(os
.devnull
, "w"), close_fds
=True)
720 output
= p
.stdout
.read()
721 result
= output
.split("\n")[0::2]
722 for idx
, line
in enumerate(result
):
723 result_lines
.append((line
.strip(), lines
[idx
].strip()))
725 except Exception as e
:
726 console
.error("%s\n" % e
)
730 # Translate n-oid's per cycle
731 entries_per_cycle
= 500
732 translated_lines
= []
734 walk_lines
= file(walk_path
).readlines()
735 console
.error("Processing %d lines.\n" % len(walk_lines
))
738 while i
< len(walk_lines
):
739 console
.error("\r%d to go... " % (len(walk_lines
) - i
))
740 process_lines
= walk_lines
[i
:i
+ entries_per_cycle
]
741 translated
= translate(process_lines
)
743 translated_lines
+= translated
744 console
.error("\rfinished. \n")
747 for translation
, line
in translated_lines
:
748 console
.output("%s --> %s\n" % (line
, translation
))
751 def do_snmpwalk(options
, hostnames
):
752 if "oids" in options
and "extraoids" in options
:
753 raise MKGeneralException("You cannot specify --oid and --extraoid at the same time.")
756 raise MKBailOut("Please specify host names to walk on.")
758 if not os
.path
.exists(cmk
.utils
.paths
.snmpwalks_dir
):
759 os
.makedirs(cmk
.utils
.paths
.snmpwalks_dir
)
761 for hostname
in hostnames
:
762 #TODO: What about SNMP management boards?
764 "hostname": hostname
,
765 "ipaddress": ip_lookup
.lookup_ipv4_address(hostname
),
766 "credentials": config
.snmp_credentials_of(hostname
),
769 do_snmpwalk_on(options
, access_data
, cmk
.utils
.paths
.snmpwalks_dir
+ "/" + hostname
)
770 except Exception as e
:
771 console
.error("Error walking %s: %s\n" % (hostname
, e
))
772 if cmk
.utils
.debug
.enabled():
774 cmk_base
.cleanup
.cleanup_globals()
777 def do_snmpwalk_on(options
, access_data
, filename
):
778 hostname
= access_data
["hostname"]
779 console
.verbose("%s:\n" % hostname
)
781 oids
= oids_to_walk(options
)
783 with
open(filename
, "w") as out
:
784 for rows
in _execute_walks_for_dump(hostname
, access_data
, oids
):
785 for oid
, value
in rows
:
786 out
.write("%s %s\n" % (oid
, value
))
787 console
.verbose("%d variables.\n" % len(rows
))
789 console
.verbose("Wrote fetched data to %s%s%s.\n" % (tty
.bold
, filename
, tty
.normal
))
792 def _execute_walks_for_dump(hostname
, access_data
, oids
):
795 console
.verbose("Walk on \"%s\"..." % oid
)
796 yield walk_for_export(access_data
, oid
)
797 except Exception as e
:
798 console
.error("Error: %s\n" % e
)
799 if cmk
.utils
.debug
.enabled():
803 def oids_to_walk(options
=None):
808 ".1.3.6.1.2.1", # SNMPv2-SMI::mib-2
809 ".1.3.6.1.4.1" # SNMPv2-SMI::enterprises
812 if "oids" in options
:
813 oids
= options
["oids"]
815 elif "extraoids" in options
:
816 oids
+= options
["extraoids"]
818 return sorted(oids
, key
=lambda x
: map(int, x
.strip(".").split(".")))
821 def do_snmpget(*args
):
823 raise MKBailOut("You need to specify an OID.")
826 hostnames
= args
[0][1:]
829 for host
in config
.all_active_realhosts():
830 if config
.is_snmp_host(host
):
831 hostnames
.append(host
)
833 for hostname
in hostnames
:
834 #TODO what about SNMP management boards?
835 ipaddress
= ip_lookup
.lookup_ipv4_address(hostname
)
837 "hostname": hostname
,
838 "ipaddress": ipaddress
,
839 "credentials": config
.snmp_credentials_of(hostname
),
841 value
= get_single_oid(access_data
, oid
)
842 console
.output("%s (%s): %r\n" % (hostname
, ipaddress
, value
))
843 cmk_base
.cleanup
.cleanup_globals()
846 cmk_base
.cleanup
.register_cleanup(cleanup_host_caches
)