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 # TODO: Move to StoredWalkSNMPBackend?
58 _g_walk_cache
= {} # type: Dict[str, List[str]]
61 # .--caching-------------------------------------------------------------.
63 # | ___ __ _ ___| |__ (_)_ __ __ _ |
64 # | / __/ _` |/ __| '_ \| | '_ \ / _` | |
65 # | | (_| (_| | (__| | | | | | | | (_| | |
66 # | \___\__,_|\___|_| |_|_|_| |_|\__, | |
68 # '----------------------------------------------------------------------'
73 def initialize_single_oid_cache(host_config
, from_disk
=False):
74 global _g_single_oid_cache
, _g_single_oid_ipaddress
, _g_single_oid_hostname
76 if not (_g_single_oid_hostname
== host_config
.hostname \
77 and _g_single_oid_ipaddress
== host_config
.ipaddress
) \
78 or _g_single_oid_cache
is None:
79 _g_single_oid_hostname
= host_config
.hostname
80 _g_single_oid_ipaddress
= host_config
.ipaddress
82 _g_single_oid_cache
= _load_single_oid_cache(host_config
)
84 _g_single_oid_cache
= {}
87 def write_single_oid_cache(host_config
):
88 if not _g_single_oid_cache
:
91 cache_dir
= cmk
.utils
.paths
.snmp_scan_cache_dir
92 if not os
.path
.exists(cache_dir
):
93 os
.makedirs(cache_dir
)
94 cache_path
= "%s/%s.%s" % (cache_dir
, host_config
.hostname
, host_config
.ipaddress
)
95 store
.save_data_to_file(cache_path
, _g_single_oid_cache
, pretty
=False)
98 def set_single_oid_cache(host_config
, oid
, value
):
99 _g_single_oid_cache
[oid
] = value
102 def _is_in_single_oid_cache(host_config
, oid
):
103 return oid
in _g_single_oid_cache
106 def _get_oid_from_single_oid_cache(host_config
, oid
):
107 return _g_single_oid_cache
.get(oid
)
110 def _load_single_oid_cache(host_config
):
111 cache_path
= "%s/%s.%s" % (cmk
.utils
.paths
.snmp_scan_cache_dir
, host_config
.hostname
,
112 host_config
.ipaddress
)
113 return store
.load_data_from_file(cache_path
, {})
116 def cleanup_host_caches():
119 _clear_other_hosts_oid_cache(None)
121 inline_snmp
.cleanup_inline_snmp_globals()
124 def _clear_other_hosts_oid_cache(hostname
):
125 global _g_single_oid_cache
, _g_single_oid_ipaddress
, _g_single_oid_hostname
126 if _g_single_oid_hostname
!= hostname
:
127 _g_single_oid_cache
= None
128 _g_single_oid_hostname
= hostname
129 _g_single_oid_ipaddress
= None
133 # .--Generic SNMP--------------------------------------------------------.
134 # | ____ _ ____ _ _ __ __ ____ |
135 # | / ___| ___ _ __ ___ _ __(_) ___ / ___|| \ | | \/ | _ \ |
136 # | | | _ / _ \ '_ \ / _ \ '__| |/ __| \___ \| \| | |\/| | |_) | |
137 # | | |_| | __/ | | | __/ | | | (__ ___) | |\ | | | | __/ |
138 # | \____|\___|_| |_|\___|_| |_|\___| |____/|_| \_|_| |_|_| |
140 # +----------------------------------------------------------------------+
141 # | Top level functions to realize SNMP functionality for Check_MK. |
142 # '----------------------------------------------------------------------'
145 def create_snmp_host_config(hostname
):
146 # type: (str) -> cmk_base.snmp_utils.SNMPHostConfig
147 return cmk_base
.snmp_utils
.SNMPHostConfig(
148 is_ipv6_primary
=config
.is_ipv6_primary(hostname
),
150 ipaddress
=ip_lookup
.lookup_ipv4_address(hostname
),
151 credentials
=config
.snmp_credentials_of(hostname
),
152 port
=config
.snmp_port_of(hostname
),
153 is_bulkwalk_host
=config
.is_bulkwalk_host(hostname
),
154 is_snmpv2or3_without_bulkwalk_host
=config
.is_snmpv2or3_without_bulkwalk_host(hostname
),
155 bulk_walk_size_of
=config
.bulk_walk_size_of(hostname
),
156 timing
=config
.snmp_timing_of(hostname
),
157 oid_range_limits
=config
.oid_range_limits_of(hostname
),
161 # TODO: OID_END_OCTET_STRING is not used at all. Drop it.
162 def get_snmp_table(host_config
, check_plugin_name
, oid_info
, use_snmpwalk_cache
):
163 # oid_info is either ( oid, columns ) or
164 # ( oid, suboids, columns )
165 # suboids is a list if OID-infixes that are put between baseoid
166 # and the columns and also prefixed to the index column. This
167 # allows to merge distinct SNMP subtrees with a similar structure
168 # to one virtual new tree (look into cmctc_temp for an example)
169 if len(oid_info
) == 2:
170 oid
, targetcolumns
= oid_info
173 oid
, suboids
, targetcolumns
= oid_info
175 if not oid
.startswith("."):
176 raise MKGeneralException("OID definition '%s' does not begin with ." % oid
)
181 for suboid
in suboids
:
183 # Detect missing (empty columns)
187 for colno
, column
in enumerate(targetcolumns
):
188 fetchoid
, value_encoding
= _compute_fetch_oid(oid
, suboid
, column
)
190 # column may be integer or string like "1.5.4.2.3"
191 # if column is 0, we do not fetch any data from snmp, but use
192 # a running counter as index. If the index column is the first one,
193 # we do not know the number of entries right now. We need to fill
194 # in later. If the column is OID_STRING or OID_BIN we do something
195 # similar: we fill in the complete OID of the entry, either as
196 # string or as binary UTF-8 encoded number string
198 cmk_base
.snmp_utils
.OID_END
, cmk_base
.snmp_utils
.OID_STRING
,
199 cmk_base
.snmp_utils
.OID_BIN
, cmk_base
.snmp_utils
.OID_END_BIN
,
200 cmk_base
.snmp_utils
.OID_END_OCTET_STRING
202 if index_column
>= 0 and index_column
!= colno
:
203 raise MKGeneralException(
204 "Invalid SNMP OID specification in implementation of check. "
205 "You can only use one of OID_END, OID_STRING, OID_BIN, OID_END_BIN and OID_END_OCTET_STRING."
208 columns
.append((fetchoid
, [], "string"))
209 index_format
= column
212 rowinfo
= _get_snmpwalk(host_config
, check_plugin_name
, oid
, fetchoid
, column
,
215 columns
.append((fetchoid
, rowinfo
, value_encoding
))
216 number_of_rows
= len(rowinfo
)
217 if number_of_rows
> max_len
:
218 max_len
= number_of_rows
221 if index_column
!= -1:
223 # Take end-oids of non-index columns as indices
224 fetchoid
, max_column
, value_encoding
= columns
[max_len_col
]
225 for o
, _unused_value
in max_column
:
226 if index_format
== cmk_base
.snmp_utils
.OID_END
:
227 index_rows
.append((o
, _extract_end_oid(fetchoid
, o
)))
228 elif index_format
== cmk_base
.snmp_utils
.OID_STRING
:
229 index_rows
.append((o
, o
))
230 elif index_format
== cmk_base
.snmp_utils
.OID_BIN
:
231 index_rows
.append((o
, _oid_to_bin(o
)))
232 elif index_format
== cmk_base
.snmp_utils
.OID_END_BIN
:
233 index_rows
.append((o
, _oid_to_bin(_extract_end_oid(fetchoid
, o
))))
234 elif index_format
== cmk_base
.snmp_utils
.OID_END_OCTET_STRING
:
235 index_rows
.append((o
, _oid_to_bin(_extract_end_oid(fetchoid
, o
))[1:]))
237 raise MKGeneralException("Invalid index format %s" % index_format
)
239 index_encoding
= columns
[index_column
][-1]
240 columns
[index_column
] = fetchoid
, index_rows
, index_encoding
242 # prepend suboid to first column
243 if suboid
and columns
:
244 fetchoid
, first_column
, value_encoding
= columns
[0]
245 new_first_column
= []
246 for o
, val
in first_column
:
247 new_first_column
.append((o
, str(suboid
) + "." + str(val
)))
248 columns
[0] = fetchoid
, new_first_column
, value_encoding
250 # Here we have to deal with a nasty problem: Some brain-dead devices
251 # omit entries in some sub OIDs. This happens e.g. for CISCO 3650
252 # in the interfaces MIB with 64 bit counters. So we need to look at
253 # the OIDs and watch out for gaps we need to fill with dummy values.
254 new_columns
= _sanitize_snmp_table_columns(columns
)
256 # From all SNMP data sources (stored walk, classic SNMP, inline SNMP) we
257 # get normal python strings. But for Check_MK we need unicode strings now.
258 # Convert them by using the standard Check_MK approach for incoming data
259 sanitized_columns
= _sanitize_snmp_encoding(host_config
.hostname
, new_columns
)
261 info
+= _construct_snmp_table_of_rows(sanitized_columns
)
266 # Contextes can only be used when check_plugin_name is given.
267 def get_single_oid(host_config
, oid
, check_plugin_name
=None, do_snmp_scan
=True):
268 # The OID can end with ".*". In that case we do a snmpgetnext and try to
269 # find an OID with the prefix in question. The *cache* is working including
272 if cmk
.utils
.debug
.enabled():
273 raise MKGeneralException("OID definition '%s' does not begin with a '.'" % oid
)
277 # TODO: Use generic cache mechanism
278 if _is_in_single_oid_cache(host_config
, oid
):
279 console
.vverbose(" Using cached OID %s: " % oid
)
280 value
= _get_oid_from_single_oid_cache(host_config
, oid
)
281 console
.vverbose("%s%s%s%s\n" % (tty
.bold
, tty
.green
, value
, tty
.normal
))
284 # get_single_oid() can only return a single value. When SNMPv3 is used with multiple
285 # SNMP contexts, all contextes will be queried until the first answer is received.
286 if check_plugin_name
is not None and cmk_base
.snmp_utils
.is_snmpv3_host(host_config
):
287 snmp_contexts
= _snmpv3_contexts_of(host_config
.hostname
, check_plugin_name
)
289 snmp_contexts
= [None]
291 console
.vverbose(" Getting OID %s: " % oid
)
292 for context_name
in snmp_contexts
:
294 snmp_backend
= SNMPBackendFactory().factory(
295 host_config
, enforce_stored_walks
=_enforce_stored_walks
)
296 value
= snmp_backend
.get(host_config
, oid
, context_name
)
298 if value
is not None:
299 break # Use first received answer in case of multiple contextes
301 if cmk
.utils
.debug
.enabled():
305 if value
is not None:
306 console
.vverbose("%s%s%s%s\n" % (tty
.bold
, tty
.green
, value
, tty
.normal
))
308 console
.vverbose("failed.\n")
310 set_single_oid_cache(host_config
, oid
, value
)
314 class SNMPBackendFactory(object):
316 def factory(host_config
, enforce_stored_walks
):
317 if enforce_stored_walks
or config
.is_usewalk_host(host_config
.hostname
):
318 return StoredWalkSNMPBackend()
320 if config
.is_inline_snmp_host(host_config
.hostname
):
321 return inline_snmp
.InlineSNMPBackend()
323 return classic_snmp
.ClassicSNMPBackend()
326 class StoredWalkSNMPBackend(cmk_base
.snmp_utils
.ABCSNMPBackend
):
327 def get(self
, host_config
, oid
, context_name
=None):
328 walk
= self
.walk(host_config
, oid
)
330 # get_stored_snmpwalk returns all oids that start with oid but here
331 # we need an exact match
332 if len(walk
) == 1 and oid
== walk
[0][0]:
335 elif oid
.endswith(".*") and len(walk
) > 0:
340 def walk(self
, host_config
, oid
):
341 if oid
.startswith("."):
344 if oid
.endswith(".*"):
345 oid_prefix
= oid
[:-2]
351 path
= cmk
.utils
.paths
.snmpwalks_dir
+ "/" + host_config
.hostname
353 console
.vverbose(" Loading %s from %s\n" % (oid
, path
))
357 if host_config
.hostname
in _g_walk_cache
:
358 lines
= _g_walk_cache
[host_config
.hostname
]
361 lines
= file(path
).readlines()
363 raise MKSNMPError("No snmpwalk file %s" % path
)
364 _g_walk_cache
[host_config
.hostname
] = lines
369 while end
- begin
> 0:
370 current
= (begin
+ end
) / 2
371 parts
= lines
[current
].split(None, 1)
373 hit
= self
._compare
_oids
(oid_prefix
, comp
)
376 elif hit
== 1: # we are too low
382 return [] # not found
384 rowinfo
= self
._collect
_until
(oid
, oid_prefix
, lines
, current
, -1)
386 rowinfo
+= self
._collect
_until
(oid
, oid_prefix
, lines
, current
+ 1, 1)
393 def _compare_oids(self
, a
, b
):
394 aa
= self
._to
_bin
_string
(a
)
395 bb
= self
._to
_bin
_string
(b
)
396 if len(aa
) <= len(bb
) and bb
[:len(aa
)] == aa
:
402 def _to_bin_string(self
, oid
):
404 return tuple(map(int, oid
.strip(".").split(".")))
406 raise MKGeneralException("Invalid OID %s" % oid
)
408 def _collect_until(self
, oid
, oid_prefix
, lines
, index
, direction
):
410 # Handle case, where we run after the end of the lines list
411 if index
>= len(lines
):
418 parts
= line
.split(None, 1)
420 if o
.startswith('.'):
422 if o
== oid
or o
.startswith(oid_prefix
+ "."):
425 value
= cmk_base
.agent_simulator
.process(parts
[1])
427 value
= parts
[1] # agent simulator missing in precompiled mode
430 # Fix for missing starting oids
431 rows
.append(('.' + o
, classic_snmp
.strip_snmp_value(value
)))
433 if index
< 0 or index
>= len(lines
):
440 def walk_for_export(host_config
, oid
):
441 if config
.is_inline_snmp_host(host_config
.hostname
):
442 rows
= inline_snmp
.walk(host_config
, None, oid
)
443 return inline_snmp
.convert_rows_for_stored_walk(rows
)
445 return classic_snmp
.walk(host_config
, oid
, hex_plain
=True)
448 def enforce_use_stored_walks():
449 global _enforce_stored_walks
450 _enforce_stored_walks
= True
454 # .--SNMP helpers--------------------------------------------------------.
455 # | ____ _ _ __ __ ____ _ _ |
456 # | / ___|| \ | | \/ | _ \ | |__ ___| |_ __ ___ _ __ ___ |
457 # | \___ \| \| | |\/| | |_) | | '_ \ / _ \ | '_ \ / _ \ '__/ __| |
458 # | ___) | |\ | | | | __/ | | | | __/ | |_) | __/ | \__ \ |
459 # | |____/|_| \_|_| |_|_| |_| |_|\___|_| .__/ \___|_| |___/ |
461 # +----------------------------------------------------------------------+
462 # | Internal helpers for processing SNMP things |
463 # '----------------------------------------------------------------------'
466 def _oid_to_bin(oid
):
467 return u
"".join([unichr(int(p
)) for p
in oid
.strip(".").split(".")])
470 def _extract_end_oid(prefix
, complete
):
471 return complete
[len(prefix
):].lstrip('.')
474 # sort OID strings numerically
475 def _oid_to_intlist(oid
):
477 return map(int, oid
.split('.'))
482 def _cmp_oids(o1
, o2
):
483 return cmp(_oid_to_intlist(o1
), _oid_to_intlist(o2
))
486 def _cmp_oid_pairs(pair1
, pair2
):
487 return cmp(_oid_to_intlist(pair1
[0].lstrip('.')), _oid_to_intlist(pair2
[0].lstrip('.')))
490 def _snmpv3_contexts_of(hostname
, check_plugin_name
):
491 for ty
, rules
in config
.snmpv3_contexts_of(hostname
):
492 if ty
is None or ty
== check_plugin_name
:
497 def _get_snmpwalk(host_config
, check_plugin_name
, oid
, fetchoid
, column
, use_snmpwalk_cache
):
498 is_cachable
= _is_snmpwalk_cachable(column
)
500 if is_cachable
and use_snmpwalk_cache
:
501 # Returns either the cached SNMP walk or None when nothing is cached
502 rowinfo
= _get_cached_snmpwalk(host_config
.hostname
, fetchoid
)
505 rowinfo
= _perform_snmpwalk(host_config
, check_plugin_name
, oid
, fetchoid
)
508 _save_snmpwalk_cache(host_config
.hostname
, fetchoid
, rowinfo
)
513 def _perform_snmpwalk(host_config
, check_plugin_name
, base_oid
, fetchoid
):
516 if cmk_base
.snmp_utils
.is_snmpv3_host(host_config
):
517 snmp_contexts
= _snmpv3_contexts_of(host_config
.hostname
, check_plugin_name
)
519 snmp_contexts
= [None]
521 for context_name
in snmp_contexts
:
522 if _enforce_stored_walks
or config
.is_usewalk_host(host_config
.hostname
):
523 rows
= StoredWalkSNMPBackend().walk(host_config
, fetchoid
)
525 elif config
.is_inline_snmp_host(host_config
.hostname
):
526 rows
= inline_snmp
.walk(
527 host_config
, check_plugin_name
, fetchoid
, base_oid
, context_name
=context_name
)
529 rows
= classic_snmp
.walk(host_config
, fetchoid
, context_name
=context_name
)
531 # I've seen a broken device (Mikrotik Router), that broke after an
532 # update to RouterOS v6.22. It would return 9 time the same OID when
533 # .1.3.6.1.2.1.1.1.0 was being walked. We try to detect these situations
534 # by removing any duplicate OID information
535 if len(rows
) > 1 and rows
[0][0] == rows
[1][0]:
537 "Detected broken SNMP agent. Ignoring duplicate OID %s.\n" % rows
[0][0])
540 for row_oid
, val
in rows
:
541 if row_oid
in added_oids
:
542 console
.vverbose("Duplicate OID found: %s (%s)\n" % (row_oid
, val
))
544 rowinfo
.append((row_oid
, val
))
545 added_oids
.add(row_oid
)
550 def _compute_fetch_oid(oid
, suboid
, column
):
552 value_encoding
= "string"
555 fetchoid
+= "." + str(suboid
)
558 if isinstance(column
, tuple):
559 fetchoid
+= "." + str(column
[1])
560 if column
[0] == "binary":
561 value_encoding
= "binary"
563 fetchoid
+= "." + str(column
)
565 return fetchoid
, value_encoding
568 def _sanitize_snmp_encoding(hostname
, columns
):
569 decode_string_func
= lambda s
: _snmp_decode_string(hostname
, s
)
571 for index
, (column
, value_encoding
) in enumerate(columns
):
572 if value_encoding
== "string":
573 columns
[index
] = map(decode_string_func
, column
)
575 columns
[index
] = map(_snmp_decode_binary
, column
)
579 def _snmp_decode_string(hostname
, text
):
580 encoding
= config
.snmp_character_encoding_of(hostname
)
582 return text
.decode(encoding
)
584 # Try to determine the current string encoding. In case a UTF-8 decoding fails, we decode latin1.
586 return text
.decode('utf-8')
588 return text
.decode('latin1')
591 def _snmp_decode_binary(text
):
592 return map(ord, text
)
595 def _sanitize_snmp_table_columns(columns
):
596 # First compute the complete list of end-oids appearing in the output
597 # by looping all results and putting the endoids to a flat list
599 for fetchoid
, column
, value_encoding
in columns
:
600 for o
, value
in column
:
601 endoid
= _extract_end_oid(fetchoid
, o
)
602 if endoid
not in endoids
:
603 endoids
.append(endoid
)
605 # The list needs to be sorted to prevent problems when the first
606 # column has missing values in the middle of the tree.
607 if not _are_ascending_oids(endoids
):
608 endoids
.sort(cmp=_cmp_oids
)
613 # Now fill gaps in columns where some endois are missing
615 for fetchoid
, column
, value_encoding
in columns
:
616 # It might happen that end OIDs are not ordered. Fix the OID sorting to make
617 # it comparable to the already sorted endoids list. Otherwise we would get
618 # some mixups when filling gaps
620 column
.sort(cmp=_cmp_oid_pairs
)
624 # Loop all lines to fill holes in the middle of the list. All
625 # columns check the following lines for the correct endoid. If
626 # an endoid differs empty values are added until the hole is filled
627 for o
, value
in column
:
628 eo
= _extract_end_oid(fetchoid
, o
)
629 if len(column
) != len(endoids
):
630 while i
< len(endoids
) and endoids
[i
] != eo
:
631 new_column
.append("") # (beginoid + '.' +endoids[i], "" ) )
633 new_column
.append(value
)
636 # At the end check if trailing OIDs are missing
637 while i
< len(endoids
):
638 new_column
.append("") # (beginoid + '.' +endoids[i], "") )
640 new_columns
.append((new_column
, value_encoding
))
645 def _are_ascending_oids(oid_list
):
646 for a
in range(len(oid_list
) - 1):
647 if _cmp_oids(oid_list
[a
], oid_list
[a
+ 1]) > 0: # == 0 should never happen
652 def _construct_snmp_table_of_rows(columns
):
656 # Now construct table by swapping X and Y.
658 for index
in range(len(columns
[0])):
659 row
= [c
[index
] for c
in columns
]
664 def _is_snmpwalk_cachable(column
):
665 return isinstance(column
, tuple) and column
[0] == "cached"
668 def _get_cached_snmpwalk(hostname
, fetchoid
):
669 path
= _snmpwalk_cache_path(hostname
, fetchoid
)
671 console
.vverbose(" Loading %s from walk cache %s\n" % (fetchoid
, path
))
672 return store
.load_data_from_file(path
)
674 if cmk
.utils
.debug
.enabled():
676 console
.verbose(" Failed loading walk cache. Continue without it.\n" % path
)
680 def _save_snmpwalk_cache(hostname
, fetchoid
, rowinfo
):
681 path
= _snmpwalk_cache_path(hostname
, fetchoid
)
683 if not os
.path
.exists(os
.path
.dirname(path
)):
684 os
.makedirs(os
.path
.dirname(path
))
686 console
.vverbose(" Saving walk of %s to walk cache %s\n" % (fetchoid
, path
))
687 store
.save_data_to_file(path
, rowinfo
, pretty
=False)
690 def _snmpwalk_cache_path(hostname
, fetchoid
):
691 return os
.path
.join(cmk
.utils
.paths
.var_dir
, "snmp_cache", hostname
, fetchoid
)
695 # .--Main modes----------------------------------------------------------.
697 # | | \/ | __ _(_)_ __ _ __ ___ ___ __| | ___ ___ |
698 # | | |\/| |/ _` | | '_ \ | '_ ` _ \ / _ \ / _` |/ _ \/ __| |
699 # | | | | | (_| | | | | | | | | | | | (_) | (_| | __/\__ \ |
700 # | |_| |_|\__,_|_|_| |_| |_| |_| |_|\___/ \__,_|\___||___/ |
702 # +----------------------------------------------------------------------+
703 # | Some main modes to help the user |
704 # '----------------------------------------------------------------------'
707 def do_snmptranslate(walk_filename
):
708 if not walk_filename
:
709 raise MKGeneralException("Please provide the name of a SNMP walk file")
711 walk_path
= "%s/%s" % (cmk
.utils
.paths
.snmpwalks_dir
, walk_filename
)
712 if not os
.path
.exists(walk_path
):
713 raise MKGeneralException("The walk '%s' does not exist" % walk_path
)
715 def translate(lines
):
718 oids_for_command
= []
720 oids_for_command
.append(line
.split(" ")[0])
722 command
= ["snmptranslate", "-m", "ALL",
723 "-M+%s" % cmk
.utils
.paths
.local_mibs_dir
] + oids_for_command
724 p
= subprocess
.Popen(
725 command
, stdout
=subprocess
.PIPE
, stderr
=open(os
.devnull
, "w"), close_fds
=True)
727 output
= p
.stdout
.read()
728 result
= output
.split("\n")[0::2]
729 for idx
, line
in enumerate(result
):
730 result_lines
.append((line
.strip(), lines
[idx
].strip()))
732 except Exception as e
:
733 console
.error("%s\n" % e
)
737 # Translate n-oid's per cycle
738 entries_per_cycle
= 500
739 translated_lines
= []
741 walk_lines
= file(walk_path
).readlines()
742 console
.error("Processing %d lines.\n" % len(walk_lines
))
745 while i
< len(walk_lines
):
746 console
.error("\r%d to go... " % (len(walk_lines
) - i
))
747 process_lines
= walk_lines
[i
:i
+ entries_per_cycle
]
748 translated
= translate(process_lines
)
750 translated_lines
+= translated
751 console
.error("\rfinished. \n")
754 for translation
, line
in translated_lines
:
755 console
.output("%s --> %s\n" % (line
, translation
))
758 def do_snmpwalk(options
, hostnames
):
759 if "oids" in options
and "extraoids" in options
:
760 raise MKGeneralException("You cannot specify --oid and --extraoid at the same time.")
763 raise MKBailOut("Please specify host names to walk on.")
765 if not os
.path
.exists(cmk
.utils
.paths
.snmpwalks_dir
):
766 os
.makedirs(cmk
.utils
.paths
.snmpwalks_dir
)
768 for hostname
in hostnames
:
769 #TODO: What about SNMP management boards?
770 host_config
= create_snmp_host_config(hostname
)
773 _do_snmpwalk_on(host_config
, options
, cmk
.utils
.paths
.snmpwalks_dir
+ "/" + hostname
)
774 except Exception as e
:
775 console
.error("Error walking %s: %s\n" % (hostname
, e
))
776 if cmk
.utils
.debug
.enabled():
778 cmk_base
.cleanup
.cleanup_globals()
781 def _do_snmpwalk_on(host_config
, options
, filename
):
782 console
.verbose("%s:\n" % host_config
.hostname
)
784 oids
= oids_to_walk(options
)
786 with
open(filename
, "w") as out
:
787 for rows
in _execute_walks_for_dump(host_config
, oids
):
788 for oid
, value
in rows
:
789 out
.write("%s %s\n" % (oid
, value
))
790 console
.verbose("%d variables.\n" % len(rows
))
792 console
.verbose("Wrote fetched data to %s%s%s.\n" % (tty
.bold
, filename
, tty
.normal
))
795 def _execute_walks_for_dump(host_config
, oids
):
798 console
.verbose("Walk on \"%s\"..." % oid
)
799 yield walk_for_export(host_config
, oid
)
800 except Exception as e
:
801 console
.error("Error: %s\n" % e
)
802 if cmk
.utils
.debug
.enabled():
806 def oids_to_walk(options
=None):
811 ".1.3.6.1.2.1", # SNMPv2-SMI::mib-2
812 ".1.3.6.1.4.1" # SNMPv2-SMI::enterprises
815 if "oids" in options
:
816 oids
= options
["oids"]
818 elif "extraoids" in options
:
819 oids
+= options
["extraoids"]
821 return sorted(oids
, key
=lambda x
: map(int, x
.strip(".").split(".")))
824 def do_snmpget(*args
):
826 raise MKBailOut("You need to specify an OID.")
829 config_cache
= config
.get_config_cache()
831 hostnames
= args
[0][1:]
834 for host
in config
.all_active_realhosts():
835 host_config
= config_cache
.get_host_config(host
)
836 if host_config
.is_snmp_host
:
837 hostnames
.append(host
)
839 for hostname
in hostnames
:
840 #TODO what about SNMP management boards?
841 host_config
= create_snmp_host_config(hostname
)
843 value
= get_single_oid(host_config
, oid
)
844 console
.output("%s (%s): %r\n" % (hostname
, host_config
.ipaddress
, value
))
845 cmk_base
.cleanup
.cleanup_globals()
848 cmk_base
.cleanup
.register_cleanup(cleanup_host_caches
)