def _snmp_scan_cache_description( binary_host: bool, *, backend: ABCSNMPBackend, ) -> None: if not binary_host: for oid, name in [ (OID_SYS_DESCR, "system description"), (OID_SYS_OBJ, "system object"), ]: value = snmp_modes.get_single_oid( oid, backend=backend, ) if value is None: raise MKSNMPError( "Cannot fetch %s OID %s. Please check your SNMP " "configuration. Possible reason might be: Wrong credentials, " "wrong SNMP version, Firewall rules, etc." % (name, oid),) else: # Fake OID values to prevent issues with a lot of scan functions console.vverbose( " Skipping system description OID " '(Set %s and %s to "")\n', OID_SYS_DESCR, OID_SYS_OBJ, ) snmp_cache.set_single_oid_cache(OID_SYS_DESCR, "") snmp_cache.set_single_oid_cache(OID_SYS_OBJ, "")
def walk(self, snmp_config, oid, check_plugin_name=None, table_base_oid=None, context_name=None): # type: (SNMPHostConfig, str, Optional[str], Optional[str], Optional[str]) -> SNMPRowInfo protospec = self._snmp_proto_spec(snmp_config) ipaddress = snmp_config.ipaddress if snmp_config.is_ipv6_primary: ipaddress = "[" + ipaddress + "]" portspec = self._snmp_port_spec(snmp_config) command = self._snmp_walk_command(snmp_config, context_name) command += [ "-OQ", "-OU", "-On", "-Ot", "%s%s%s" % (protospec, ipaddress, portspec), oid ] console.vverbose("Running '%s'\n" % subprocess.list2cmdline(command)) snmp_process = None exitstatus = None rowinfo = [] # type: SNMPRowInfo try: snmp_process = subprocess.Popen(command, close_fds=True, stdin=open(os.devnull), stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8") rowinfo = self._get_rowinfo_from_snmp_process(snmp_process) except MKTimeout: # On timeout exception try to stop the process to prevent child process "leakage" if snmp_process: os.kill(snmp_process.pid, signal.SIGTERM) snmp_process.wait() raise finally: # The stdout and stderr pipe are not closed correctly on a MKTimeout # Normally these pipes getting closed after p.communicate finishes # Closing them a second time in a OK scenario won't hurt neither.. if snmp_process: exitstatus = snmp_process.wait() if snmp_process.stderr: error = snmp_process.stderr.read() snmp_process.stderr.close() if snmp_process.stdout: snmp_process.stdout.close() if exitstatus: console.verbose(tty.red + tty.bold + "ERROR: " + tty.normal + "SNMP error: %s\n" % ensure_str(error).strip()) raise MKSNMPError( "SNMP Error on %s: %s (Exit-Code: %d)" % (ipaddress, ensure_str(error).strip(), exitstatus)) return rowinfo
def walk(self, snmp_config, oid, check_plugin_name=None, table_base_oid=None, context_name=None): # type: (SNMPHostConfig, OID, Optional[CheckPluginName], Optional[OID], Optional[ContextName]) -> SNMPRowInfo if oid.startswith("."): oid = oid[1:] if oid.endswith(".*"): oid_prefix = oid[:-2] dot_star = True else: oid_prefix = oid dot_star = False path = cmk.utils.paths.snmpwalks_dir + "/" + snmp_config.hostname console.vverbose(" Loading %s from %s\n" % (oid, path)) if snmp_cache.host_cache_contains(snmp_config.hostname): lines = snmp_cache.host_cache_get(snmp_config.hostname) else: try: lines = open(path).readlines() except IOError: raise MKSNMPError("No snmpwalk file %s" % path) snmp_cache.host_cache_set(snmp_config.hostname, lines) begin = 0 end = len(lines) hit = None while end - begin > 0: current = (begin + end) // 2 # skip over values including newlines to the next oid while not lines[current].startswith(".") and current < end: current += 1 parts = lines[current].split(None, 1) comp = parts[0] hit = self._compare_oids(oid_prefix, comp) if hit == 0: break if hit == 1: # we are too low begin = current + 1 else: end = current if hit != 0: return [] # not found rowinfo = self._collect_until(oid, oid_prefix, lines, current, -1) rowinfo.reverse() rowinfo += self._collect_until(oid, oid_prefix, lines, current + 1, 1) if dot_star: return [rowinfo[0]] return rowinfo
def walk( self, oid: OID, section_name: Optional[SectionName] = None, table_base_oid: Optional[OID] = None, context_name: Optional[SNMPContextName] = None, ) -> SNMPRowInfo: if oid.startswith("."): oid = oid[1:] if oid.endswith(".*"): oid_prefix = oid[:-2] dot_star = True else: oid_prefix = oid dot_star = False host_cache = snmp_cache.host_cache() try: lines = host_cache[self.config.hostname] except KeyError: path = cmk.utils.paths.snmpwalks_dir + "/" + self.config.hostname console.vverbose(" Loading %s from %s\n" % (oid, path)) try: lines = StoredWalkSNMPBackend.read_walk_data(path) except IOError: raise MKSNMPError("No snmpwalk file %s" % path) host_cache[self.config.hostname] = lines begin = 0 end = len(lines) hit = None while end - begin > 0: current = (begin + end) // 2 parts = lines[current].split(None, 1) comp = parts[0] hit = StoredWalkSNMPBackend._compare_oids(oid_prefix, comp) if hit == 0: break if hit == 1: # we are too low begin = current + 1 else: end = current if hit != 0: return [] # not found rowinfo = StoredWalkSNMPBackend._collect_until(oid, oid_prefix, lines, current, -1) rowinfo.reverse() rowinfo += StoredWalkSNMPBackend._collect_until( oid, oid_prefix, lines, current + 1, 1) if dot_star: return [rowinfo[0]] return rowinfo
def _prefetch_description_object(*, backend: SNMPBackend) -> None: for oid, name in ( (OID_SYS_DESCR, "system description"), (OID_SYS_OBJ, "system object"), ): if snmp_modes.get_single_oid(oid, backend=backend) is None: raise MKSNMPError( "Cannot fetch %s OID %s. Please check your SNMP " "configuration. Possible reason might be: Wrong credentials, " "wrong SNMP version, Firewall rules, etc." % (name, oid), )
def walk( self, oid: str, section_name: Optional[SectionName] = None, table_base_oid: Optional[str] = None, context_name: Optional[str] = None, ) -> SNMPRowInfo: protospec = self._snmp_proto_spec() ipaddress = self.config.ipaddress if self.config.is_ipv6_primary: ipaddress = "[" + ipaddress + "]" portspec = self._snmp_port_spec() command = self._snmp_walk_command(context_name) command += [ "-OQ", "-OU", "-On", "-Ot", "%s%s%s" % (protospec, ipaddress, portspec), oid ] console.vverbose("Running '%s'\n" % subprocess.list2cmdline(command)) rowinfo: SNMPRowInfo = [] with subprocess.Popen( command, close_fds=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8", ) as snmp_process: assert snmp_process.stdout assert snmp_process.stderr try: rowinfo = self._get_rowinfo_from_walk_output( snmp_process.stdout) error = snmp_process.stderr.read() except MKTimeout: snmp_process.kill() raise if snmp_process.returncode: console.verbose(tty.red + tty.bold + "ERROR: " + tty.normal + "SNMP error: %s\n" % error.strip()) raise MKSNMPError("SNMP Error on %s: %s (Exit-Code: %d)" % ( ipaddress, error.strip(), snmp_process.returncode, )) return rowinfo
def _snmp_scan(host_config, on_error="ignore", for_inv=False, do_snmp_scan=True, for_mgmt_board=False): # type: (SNMPHostConfig, str, bool, bool, bool) -> Set[CheckPluginName] import cmk.base.inventory_plugins as inventory_plugins # pylint: disable=import-outside-toplevel # Make hostname globally available for scan functions. # This is rarely used, but e.g. the scan for if/if64 needs # this to evaluate if_disabled_if64_checks. check_api_utils.set_hostname(host_config.hostname) snmp_cache.initialize_single_oid_cache(host_config) console.vverbose(" SNMP scan:\n") if not config.get_config_cache().in_binary_hostlist(host_config.hostname, config.snmp_without_sys_descr): for oid, name in [(".1.3.6.1.2.1.1.1.0", "system description"), (".1.3.6.1.2.1.1.2.0", "system object")]: value = snmp.get_single_oid(host_config, oid, do_snmp_scan=do_snmp_scan) if value is None: raise MKSNMPError( "Cannot fetch %s OID %s. This might be OK for some bogus devices. " "In that case please configure the ruleset \"Hosts without system " "description OID\" to tell Check_MK not to fetch the system " "description and system object OIDs." % (name, oid)) else: # Fake OID values to prevent issues with a lot of scan functions console.vverbose(" Skipping system description OID " "(Set .1.3.6.1.2.1.1.1.0 and .1.3.6.1.2.1.1.2.0 to \"\")\n") snmp_cache.set_single_oid_cache(".1.3.6.1.2.1.1.1.0", "") snmp_cache.set_single_oid_cache(".1.3.6.1.2.1.1.2.0", "") if for_inv: these_plugin_names = [ name for name in inventory_plugins.inv_info if inventory_plugins.is_snmp_plugin(name) ] else: # TODO (mo): stop converting to string! these_plugin_names = [str(n) for n in config.registered_snmp_sections] found_plugins = set() # type: Set[CheckPluginName] for check_plugin_name in these_plugin_names: if config.service_ignored(host_config.hostname, check_plugin_name, None): continue detection_spec = _get_detection_spec_from_plugin_name(check_plugin_name, inventory_plugins.inv_info) if detection_spec is None: console.warning(" SNMP check %s: Could not detect specifications for plugin" % check_plugin_name) continue try: def oid_function(oid, default_value=None, cp_name=check_plugin_name): # type: (OID, Optional[DecodedString], Optional[CheckPluginName]) -> Optional[DecodedString] value = snmp.get_single_oid(host_config, oid, cp_name, do_snmp_scan=do_snmp_scan) return default_value if value is None else value if callable(detection_spec): result = detection_spec(oid_function) else: result = _evaluate_snmp_detection(oid_function, detection_spec) if result is not None and not isinstance(result, (str, bool)): if on_error == "warn": console.warning(" SNMP scan function of %s returns invalid type %s." % (check_plugin_name, type(result))) elif on_error == "raise": raise MKGeneralException("SNMP Scan aborted.") elif result: found_plugins.add(check_plugin_name) except MKGeneralException: # some error messages which we explicitly want to show to the user # should be raised through this raise except Exception: if on_error == "warn": console.warning(" Exception in SNMP scan function of %s" % check_plugin_name) elif on_error == "raise": raise _output_snmp_check_plugins("SNMP scan found", found_plugins) filtered = config.filter_by_management_board( host_config.hostname, found_plugins, for_mgmt_board, for_discovery=True, for_inventory=for_inv, ) _output_snmp_check_plugins("SNMP filtered check plugin names", filtered) snmp_cache.write_single_oid_cache(host_config) return filtered
def _snmp_scan(host_config, on_error="ignore", for_inv=False, do_snmp_scan=True, for_mgmt_board=False): # type: (SNMPHostConfig, str, bool, bool, bool) -> Set[CheckPluginName] import cmk.base.inventory_plugins as inventory_plugins # pylint: disable=import-outside-toplevel # Make hostname globally available for scan functions. # This is rarely used, but e.g. the scan for if/if64 needs # this to evaluate if_disabled_if64_checks. check_api_utils.set_hostname(host_config.hostname) snmp_cache.initialize_single_oid_cache(host_config) console.vverbose(" SNMP scan:\n") if not config.get_config_cache().in_binary_hostlist( host_config.hostname, config.snmp_without_sys_descr): for oid, name in [(".1.3.6.1.2.1.1.1.0", "system description"), (".1.3.6.1.2.1.1.2.0", "system object")]: value = snmp.get_single_oid(host_config, oid, do_snmp_scan=do_snmp_scan) if value is None: raise MKSNMPError( "Cannot fetch %s OID %s. This might be OK for some bogus devices. " "In that case please configure the ruleset \"Hosts without system " "description OID\" to tell Check_MK not to fetch the system " "description and system object OIDs." % (name, oid)) else: # Fake OID values to prevent issues with a lot of scan functions console.vverbose( " Skipping system description OID " "(Set .1.3.6.1.2.1.1.1.0 and .1.3.6.1.2.1.1.2.0 to \"\")\n") snmp_cache.set_single_oid_cache(".1.3.6.1.2.1.1.1.0", "") snmp_cache.set_single_oid_cache(".1.3.6.1.2.1.1.2.0", "") # TODO (mo): Assumption here is that inventory plugins are significantly fewer # than check plugins. We should pass an explicit list along, instead # of this flag. That way we would also get rid of the import above. if for_inv: section_names = [PluginName(n) for n in inventory_plugins.inv_info] these_sections = [ config.registered_snmp_sections[section_name] for section_name in section_names if section_name in config.registered_snmp_sections ] else: these_sections = list(config.registered_snmp_sections.values()) found_plugins = set() # type: Set[CheckPluginName] for section_plugin in these_sections: try: if _evaluate_snmp_detection( section_plugin.detect_spec, host_config, str(section_plugin.name), do_snmp_scan, ): found_plugins.add(str(section_plugin.name)) except MKGeneralException: # some error messages which we explicitly want to show to the user # should be raised through this raise except Exception: if on_error == "warn": console.warning(" Exception in SNMP scan function of %s" % section_plugin.name) elif on_error == "raise": raise _output_snmp_check_plugins("SNMP scan found", found_plugins) filtered = config.filter_by_management_board( host_config.hostname, found_plugins, for_mgmt_board, for_discovery=True, for_inventory=for_inv, ) _output_snmp_check_plugins("SNMP filtered check plugin names", filtered) snmp_cache.write_single_oid_cache(host_config) return filtered