def enable(): # type: () -> None global _profile import cProfile _profile = cProfile.Profile() _profile.enable() console.verbose("Enabled profiling.\n")
def walk(self, snmp_config, oid, check_plugin_name=None, table_base_oid=None, context_name=None): # type: (snmp_utils.SNMPHostConfig, str, Optional[str], Optional[str], Optional[str]) -> snmp_utils.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: snmp_utils.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" % six.ensure_str(error).strip()) raise MKSNMPError( "SNMP Error on %s: %s (Exit-Code: %d)" % (ipaddress, six.ensure_str(error).strip(), exitstatus)) return rowinfo
def update_dns_cache(): # type: () -> UpdateDNSCacheResult failed = [] ip_lookup_cache = _get_ip_lookup_cache() ip_lookup_cache.persist_on_update = False console.verbose("Cleaning up existing DNS cache...\n") _clear_ip_lookup_cache(ip_lookup_cache) console.verbose("Updating DNS cache...\n") for hostname, family in _get_dns_cache_lookup_hosts(): console.verbose("%s (IPv%d)..." % (hostname, family)) try: ip = lookup_ip_address(hostname, family) console.verbose("%s\n" % ip) except (MKTerminate, MKTimeout): # We should be more specific with the exception handler below, then we # could drop this special handling here raise except Exception as e: failed.append(hostname) console.verbose("lookup failed: %s\n" % e) if cmk.utils.debug.enabled(): raise continue ip_lookup_cache.persist_on_update = True ip_lookup_cache.save_persisted() return len(ip_lookup_cache), failed
def enable(): # type: () -> None global _profile import cProfile # pylint: disable=import-outside-toplevel _profile = cProfile.Profile() _profile.enable() console.verbose("Enabled profiling.\n")
def wrapped_check_func(hostname, *args, **kwargs): # type: (HostName, Any, Any) -> int host_config = config.get_config_cache().get_host_config(hostname) exit_spec = host_config.exit_code_spec() status, infotexts, long_infotexts, perfdata = 0, [], [], [] try: status, infotexts, long_infotexts, perfdata = check_func( hostname, *args, **kwargs) except SystemExit: raise except MKTimeout: if _in_keepalive_mode(): raise else: infotexts.append("Timed out") status = max(status, cast(int, exit_spec.get("timeout", 2))) except (MKAgentError, MKSNMPError, MKIPAddressLookupError) as e: infotexts.append("%s" % e) status = cast(int, exit_spec.get("connection", 2)) except MKGeneralException as e: infotexts.append("%s" % e) status = max(status, cast(int, exit_spec.get("exception", 3))) except Exception: if cmk.utils.debug.enabled(): raise crash_output = cmk.base.crash_reporting.create_check_crash_dump( hostname, check_plugin_name, None, False, None, description, []) infotexts.append( crash_output.replace("Crash dump:\n", "Crash dump:\\n")) status = max(status, cast(int, exit_spec.get("exception", 3))) # Produce the service check result output output_txt = "%s - %s" % (defines.short_service_state_name(status), ", ".join(infotexts)) if perfdata: output_txt += " | %s" % " ".join(perfdata) if long_infotexts: output_txt = "%s\n%s" % (output_txt, "\n".join(long_infotexts)) output_txt += "\n" if _in_keepalive_mode(): keepalive.add_keepalive_active_check_result( hostname, output_txt) console.verbose(output_txt.encode("utf-8")) else: console.output(output_txt.encode("utf-8")) return status
def _execute_walks_for_dump(snmp_config, oids): for oid in oids: try: console.verbose("Walk on \"%s\"..." % oid) yield walk_for_export(snmp_config, oid) except Exception as e: console.error("Error: %s\n" % e) if cmk.utils.debug.enabled(): raise
def _create_tarfile(self): # type: () -> None filepaths = self._get_filepaths() console.verbose("Pack temporary files:\n") with tarfile.open(name=self.tarfile_path, mode='w:gz') as tar: for filepath in filepaths: console.verbose(" '%s'\n" % _get_short_filepath(filepath)) tar.add(str(filepath), arcname=filepath.name)
def _execute_walks_for_dump(snmp_config, oids): # type: (SNMPHostConfig, List[OID]) -> Iterable[SNMPRowInfo] for oid in oids: try: console.verbose("Walk on \"%s\"..." % oid) yield walk_for_export(snmp_config, oid) except Exception as e: console.error("Error: %s\n" % e) if cmk.utils.debug.enabled(): raise
def cached_dns_lookup(hostname, family): # type: (HostName, int) -> Optional[str] cache = cmk.base.config_cache.get_dict("cached_dns_lookup") cache_id = hostname, family # Address has already been resolved in prior call to this function? try: return cache[cache_id] except KeyError: pass ip_lookup_cache = _get_ip_lookup_cache() cached_ip = ip_lookup_cache.get(cache_id) if cached_ip and config.use_dns_cache: cache[cache_id] = cached_ip return cached_ip host_config = config.get_config_cache().get_host_config(hostname) if host_config.is_no_ip_host: cache[cache_id] = None return None # Now do the actual DNS lookup try: ipa = socket.getaddrinfo( hostname, None, family == 4 and socket.AF_INET or socket.AF_INET6)[0][4][0] # Update our cached address if that has changed or was missing if ipa != cached_ip: console.verbose("Updating IPv%d DNS cache for %s: %s\n" % (family, hostname, ipa)) ip_lookup_cache.update_cache(cache_id, ipa) cache[cache_id] = ipa # Update in-memory-cache return ipa except (MKTerminate, MKTimeout): # We should be more specific with the exception handler below, then we # could drop this special handling here raise except Exception as e: # DNS failed. Use cached IP address if present, even if caching # is disabled. if cached_ip: cache[cache_id] = cached_ip return cached_ip else: cache[cache_id] = None raise MKIPAddressLookupError( "Failed to lookup IPv%d address of %s via DNS: %s" % (family, hostname, e))
def _cleanup_dump_folder(self): # type: () -> None dumps = sorted( [(dump.stat().st_mtime, dump) for dump in self.dump_folder.glob("*%s" % SUFFIX)], key=lambda t: t[0])[:-self._keep_num_dumps] console.verbose("Cleanup dump folder (remove old dumps, keep the last %s dumps):\n" % self._keep_num_dumps) for _mtime, filepath in dumps: console.verbose(" '%s'\n" % _get_short_filepath(filepath)) self._remove_file(filepath)
def _output_check_result(servicedesc, state, infotext, perftexts): # type: (ServiceName, ServiceState, ServiceDetails, List[str]) -> None if _show_perfdata: infotext_fmt = "%-56s" p = ' (%s)' % (" ".join(perftexts)) else: p = '' infotext_fmt = "%s" console.verbose("%-20s %s%s" + infotext_fmt + "%s%s\n", servicedesc.encode('utf-8'), tty.bold, tty.states[state], make_utf8(infotext.split('\n')[0]), tty.normal, make_utf8(p))
def _get_cached_snmpwalk(hostname, fetchoid): # type: (HostName, OID) -> Optional[SNMPRowInfo] path = _snmpwalk_cache_path(hostname, fetchoid) try: console.vverbose(" Loading %s from walk cache %s\n" % (fetchoid, path)) return store.load_object_from_file(path) except Exception: if cmk.utils.debug.enabled(): raise console.verbose(" Failed loading walk cache from %s. Continue without it.\n" % path) return None
def _get_filepaths(self): # type: () -> List[Path] console.output("Collect diagnostics information:\n") filepaths = [] for element in self.elements: filepath = element.add_or_get_file(self.tmp_dump_folder) if filepath is None: console.verbose(" %s: No informations\n" % element.ident) continue console.output(" %s\n" % element.description) filepaths.append(filepath) return filepaths
def service_outside_check_period(config_cache, hostname, description): # type: (config.ConfigCache, HostName, ServiceName) -> bool period = config_cache.check_period_of_service(hostname, description) if period is None: return False if cmk.base.core.check_timeperiod(period): console.vverbose("Service %s: timeperiod %s is currently active.\n", six.ensure_str(description), period) return False console.verbose("Skipping service %s: currently not in timeperiod %s.\n", six.ensure_str(description), period) return True
def _create_tarfile(self): # type: () -> None filepaths = self._get_filepaths() console.verbose("Pack temporary files:\n") tarfile_filepath = self.dump_folder.joinpath(str( uuid.uuid4())).with_suffix(SUFFIX) with tarfile.open(name=tarfile_filepath, mode='w:gz') as tar: for filepath in filepaths: console.verbose(" '%s'\n" % self._get_short_filepath(filepath)) tar.add(str(filepath)) console.output("Created diagnostics dump:\n") console.output(" '%s'\n" % self._get_short_filepath(tarfile_filepath))
def try_get_activation_lock(): global _restart_lock_fd # In some bizarr cases (as cmk -RR) we need to avoid duplicate locking! if config.restart_locking and _restart_lock_fd is None: lock_file = cmk.utils.paths.default_config_dir + "/main.mk" _restart_lock_fd = os.open(lock_file, os.O_RDONLY) # Make sure that open file is not inherited to monitoring core! fcntl.fcntl(_restart_lock_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC) try: console.verbose("Waiting for exclusive lock on %s.\n" % lock_file, stream=sys.stderr) fcntl.flock( _restart_lock_fd, fcntl.LOCK_EX | (config.restart_locking == "abort" and fcntl.LOCK_NB or 0)) except Exception: return True return False
def _run_inventory_export_hooks(host_config, inventory_tree): import cmk.base.inventory_plugins as inventory_plugins hooks = host_config.inventory_export_hooks if not hooks: return console.step("Execute inventory export hooks") for hookname, params in hooks: console.verbose("Execute export hook: %s%s%s%s" % (tty.blue, tty.bold, hookname, tty.normal)) try: func = inventory_plugins.inv_export[hookname]["export_function"] func(host_config.hostname, params, inventory_tree.get_raw_tree()) except Exception as e: if cmk.utils.debug.enabled(): raise raise MKGeneralException("Failed to execute export hook %s: %s" % (hookname, e))
def precompile_hostchecks(): console.verbose("Creating precompiled host check config...\n") config.PackedConfig().save() if not os.path.exists(cmk.utils.paths.precompiled_hostchecks_dir): os.makedirs(cmk.utils.paths.precompiled_hostchecks_dir) config_cache = config.get_config_cache() console.verbose("Precompiling host checks...\n") for host in config_cache.all_active_hosts(): try: _precompile_hostcheck(config_cache, host) except Exception as e: if cmk.utils.debug.enabled(): raise console.error("Error precompiling checks for host %s: %s\n" % (host, e)) sys.exit(5)
def _save_inventory_tree(hostname, inventory_tree): # type: (HostName, StructuredDataTree) -> Optional[StructuredDataTree] store.makedirs(cmk.utils.paths.inventory_output_dir) filepath = cmk.utils.paths.inventory_output_dir + "/" + hostname if inventory_tree.is_empty(): # Remove empty inventory files. Important for host inventory icon if os.path.exists(filepath): os.remove(filepath) if os.path.exists(filepath + ".gz"): os.remove(filepath + ".gz") return None old_tree = StructuredDataTree().load_from(filepath) old_tree.normalize_nodes() if old_tree.is_equal(inventory_tree): console.verbose("Inventory was unchanged\n") return None if old_tree.is_empty(): console.verbose("New inventory tree\n") else: console.verbose("Inventory tree has changed\n") old_time = os.stat(filepath).st_mtime arcdir = "%s/%s" % (cmk.utils.paths.inventory_archive_dir, hostname) store.makedirs(arcdir) os.rename(filepath, arcdir + ("/%d" % old_time)) inventory_tree.save_to(cmk.utils.paths.inventory_output_dir, hostname) return old_tree
def _do_inv_for_realhost(host_config, sources, multi_host_sections, hostname, ipaddress, inventory_tree, status_data_tree): # type: (config.HostConfig, data_sources.DataSources, Optional[data_sources.MultiHostSections], HostName, Optional[HostAddress], StructuredDataTree, StructuredDataTree) -> None for source in sources.get_data_sources(): if isinstance(source, data_sources.SNMPDataSource): source.set_on_error("raise") source.set_do_snmp_scan(True) source.disable_data_source_cache() source.set_use_snmpwalk_cache(False) source.set_ignore_check_interval(True) source.set_check_plugin_name_filter( _gather_snmp_check_plugin_names_inventory) if multi_host_sections is not None: # Status data inventory already provides filled multi_host_sections object. # SNMP data source: If 'do_status_data_inv' is enabled there may be # sections for inventory plugins which were not fetched yet. source.enforce_check_plugin_names(None) host_sections = multi_host_sections.add_or_get_host_sections( hostname, ipaddress, deflt=SNMPHostSections()) source.set_fetched_check_plugin_names( set(host_sections.sections)) host_sections_from_source = source.run() host_sections.update(host_sections_from_source) if multi_host_sections is None: multi_host_sections = sources.get_host_sections() console.step("Executing inventory plugins") import cmk.base.inventory_plugins as inventory_plugins # pylint: disable=import-outside-toplevel console.verbose("Plugins:") for section_name, plugin in inventory_plugins.sorted_inventory_plugins(): section_content = multi_host_sections.get_section_content( hostname, ipaddress, section_name, for_discovery=False) if not section_content: # section not present (None or []) # Note: this also excludes existing sections without info.. continue if all([x in [[], {}, None] for x in section_content]): # Inventory plugins which get parsed info from related # check plugin may have more than one return value, eg # parse function of oracle_tablespaces returns ({}, {}) continue console.verbose(" %s%s%s%s" % (tty.green, tty.bold, section_name, tty.normal)) # Inventory functions can optionally have a second argument: parameters. # These are configured via rule sets (much like check parameters). inv_function = plugin["inv_function"] kwargs = cmk.utils.misc.make_kwargs_for( inv_function, inventory_tree=inventory_tree, status_data_tree=status_data_tree) non_kwargs = set( cmk.utils.misc.getfuncargs(inv_function)) - set(kwargs) args = [section_content] if len(non_kwargs) == 2: args += [host_config.inventory_parameters(section_name)] inv_function(*args, **kwargs) console.verbose("\n")
def get(self, snmp_config, oid, context_name=None): # type: (SNMPHostConfig, OID, Optional[ContextName]) -> Optional[RawValue] if oid.endswith(".*"): oid_prefix = oid[:-2] commandtype = "getnext" else: oid_prefix = oid commandtype = "get" 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_base_command(commandtype, snmp_config, context_name) + [ "-On", "-OQ", "-Oe", "-Ot", "%s%s%s" % (protospec, ipaddress, portspec), oid_prefix ]) console.vverbose("Running '%s'\n" % subprocess.list2cmdline(command)) snmp_process = subprocess.Popen( command, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8", ) exitstatus = snmp_process.wait() if snmp_process.stderr is None or snmp_process.stdout is None: raise TypeError() if exitstatus: console.verbose(tty.red + tty.bold + "ERROR: " + tty.normal + "SNMP error\n") console.verbose(snmp_process.stderr.read() + "\n") return None line = snmp_process.stdout.readline().strip() if not line: console.verbose("Error in response to snmpget.\n") return None parts = line.split("=", 1) if len(parts) != 2: return None item = parts[0] value = parts[1].strip() console.vverbose("SNMP answer: ==> [%s]\n" % value) if value.startswith('No more variables') or \ value.startswith('End of MIB') or \ value.startswith('No Such Object available') or \ value.startswith('No Such Instance currently exists'): return None # In case of .*, check if prefix is the one we are looking for if commandtype == "getnext" and not item.startswith(oid_prefix + "."): return None return strip_snmp_value(value)
def _create_dump_folders(self): # type: () -> None console.verbose("Create dump folders:\n") self.dump_folder.mkdir(parents=True, exist_ok=True) console.verbose(" '%s'\n" % _get_short_filepath(self.dump_folder)) self.tmp_dump_folder.mkdir(parents=True, exist_ok=True) console.verbose(" '%s'\n" % _get_short_filepath(self.tmp_dump_folder))
def _cleanup_tmp_dump_folder(self): # type: () -> None console.verbose("Remove temporary files:\n") for filepath in self.tmp_dump_folder.iterdir(): console.verbose(" '%s'\n" % _get_short_filepath(filepath)) self._remove_file(filepath) console.verbose("Remove temporary dump folder:\n") console.verbose(" '%s'\n" % _get_short_filepath(self.tmp_dump_folder)) try: self.tmp_dump_folder.rmdir() except OSError as e: if e.errno != errno.ENOTEMPTY: raise
def do_backup(tarname): console.verbose("Creating backup file '%s'...\n", tarname) tar = tarfile.open(tarname, "w:gz") for name, path, canonical_name, descr, is_dir, in backup_paths(): absdir = os.path.abspath(path) if os.path.exists(path): if is_dir: subtarname = name + ".tar" subfile = StringIO.StringIO() subtar = tarfile.open(mode="w", fileobj=subfile, dereference=True) subtar.add(path, arcname=".") subdata = subfile.getvalue() else: subtarname = canonical_name subdata = open(absdir).read() info = tarfile.TarInfo(subtarname) info.mtime = time.time() info.uid = 0 info.gid = 0 info.size = len(subdata) info.mode = 0o644 info.type = tarfile.REGTYPE info.name = subtarname console.verbose(" Added %s (%s) with a size of %s\n", descr, absdir, render.fmt_bytes(info.size)) tar.addfile(info, StringIO.StringIO(subdata)) tar.close() console.verbose("Successfully created backup.\n")
def _get_filepaths(self): # type: () -> List[Path] console.verbose("Collect diagnostics files.\n") filepaths = [] for element in self.elements: filepath = element.add_or_get_file(self.tmp_dump_folder) if filepath is None: console.verbose(" %s: No informations\n" % element.ident) continue console.verbose(" '%s'\n" % self._get_short_filepath(filepath)) filepaths.append(filepath) return filepaths
def _do_snmpwalk_on(snmp_config, options, filename): # type: (SNMPHostConfig, SNMPWalkOptions, str) -> None console.verbose("%s:\n" % snmp_config.hostname) oids = oids_to_walk(options) with Path(filename).open("w", encoding="utf-8") as out: for rows in _execute_walks_for_dump(snmp_config, oids): for oid, value in rows: out.write(six.ensure_text("%s %s\n" % (oid, value))) console.verbose("%d variables.\n" % len(rows)) console.verbose("Wrote fetched data to %s%s%s.\n" % (tty.bold, filename, tty.normal))
def _do_snmpwalk_on(snmp_config, options, filename): console.verbose("%s:\n" % snmp_config.hostname) oids = oids_to_walk(options) with open(filename, "w") as out: for rows in _execute_walks_for_dump(snmp_config, oids): for oid, value in rows: out.write("%s %s\n" % (oid, value)) console.verbose("%d variables.\n" % len(rows)) console.verbose("Wrote fetched data to %s%s%s.\n" % (tty.bold, filename, tty.normal))
def get(self, snmp_config, oid, context_name=None): if oid.endswith(".*"): oid_prefix = oid[:-2] commandtype = "getnext" else: oid_prefix = oid commandtype = "get" 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_base_command(commandtype, snmp_config, context_name) + \ [ "-On", "-OQ", "-Oe", "-Ot", "%s%s%s" % (protospec, ipaddress, portspec), oid_prefix ] console.vverbose("Running '%s'\n" % subprocess.list2cmdline(command)) snmp_process = subprocess.Popen(command, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) exitstatus = snmp_process.wait() if exitstatus: console.verbose(tty.red + tty.bold + "ERROR: " + tty.normal + "SNMP error\n") console.verbose(snmp_process.stderr.read() + "\n") return None line = snmp_process.stdout.readline().strip() if not line: console.verbose("Error in response to snmpget.\n") return None item, value = line.split("=", 1) value = value.strip() console.vverbose("SNMP answer: ==> [%s]\n" % value) if value.startswith('No more variables') or value.startswith('End of MIB') \ or value.startswith('No Such Object available') or value.startswith('No Such Instance currently exists'): value = None # In case of .*, check if prefix is the one we are looking for if commandtype == "getnext" and not item.startswith(oid_prefix + "."): value = None # Strip quotes if value and value.startswith('"') and value.endswith('"'): value = value[1:-1] return value
def do_check(hostname, ipaddress, only_check_plugin_names=None): # type: (HostName, Optional[HostAddress], Optional[List[CheckPluginName]]) -> Tuple[int, List[ServiceDetails], List[ServiceAdditionalDetails], List[Text]] cpu_tracking.start("busy") console.verbose("Check_MK version %s\n", six.ensure_str(cmk.__version__)) config_cache = config.get_config_cache() host_config = config_cache.get_host_config(hostname) exit_spec = host_config.exit_code_spec() status = 0 # type: ServiceState infotexts = [] # type: List[ServiceDetails] long_infotexts = [] # type: List[ServiceAdditionalDetails] perfdata = [] # type: List[Text] try: # In case of keepalive we always have an ipaddress (can be 0.0.0.0 or :: when # address is unknown). When called as non keepalive ipaddress may be None or # is already an address (2nd argument) if ipaddress is None and not host_config.is_cluster: ipaddress = ip_lookup.lookup_ip_address(hostname) item_state.load(hostname) sources = data_sources.DataSources(hostname, ipaddress) num_success, missing_sections = \ _do_all_checks_on_host(sources, host_config, ipaddress, only_check_plugin_names) if _submit_to_core: item_state.save(hostname) for source in sources.get_data_sources(): source_state, source_output, source_perfdata = source.get_summary_result_for_checking( ) if source_output != "": status = max(status, source_state) infotexts.append("[%s] %s" % (source.id(), source_output)) perfdata.extend( [_convert_perf_data(p) for p in source_perfdata]) if missing_sections and num_success > 0: missing_sections_status, missing_sections_infotext = \ _check_missing_sections(missing_sections, exit_spec) status = max(status, missing_sections_status) infotexts.append(missing_sections_infotext) elif missing_sections: infotexts.append("Got no information from host") status = max(status, cast(int, exit_spec.get("empty_output", 2))) cpu_tracking.end() phase_times = cpu_tracking.get_times() total_times = phase_times["TOTAL"] run_time = total_times[4] infotexts.append("execution time %.1f sec" % run_time) if config.check_mk_perfdata_with_times: perfdata += [ "execution_time=%.3f" % run_time, "user_time=%.3f" % total_times[0], "system_time=%.3f" % total_times[1], "children_user_time=%.3f" % total_times[2], "children_system_time=%.3f" % total_times[3], ] for phase, times in phase_times.items(): if phase in ["agent", "snmp", "ds"]: t = times[4] - sum(times[:4]) # real time - CPU time perfdata.append("cmk_time_%s=%.3f" % (phase, t)) else: perfdata.append("execution_time=%.3f" % run_time) return status, infotexts, long_infotexts, perfdata finally: if _checkresult_file_fd is not None: _close_checkresult_file() # "ipaddress is not None": At least when working with a cluster host it seems the ipaddress # may be None. This needs to be understood in detail and cleaned up. As the InlineSNMP # stats feature is a very rarely used debugging feature, the analyzation and fix is # postponed now. if config.record_inline_snmp_stats \ and ipaddress is not None \ and host_config.snmp_config(ipaddress).is_inline_snmp_host: inline_snmp.save_snmp_stats()
def execute_check(config_cache, multi_host_sections, hostname, ipaddress, check_plugin_name, item, params, description): # type: (config.ConfigCache, data_sources.MultiHostSections, HostName, Optional[HostAddress], CheckPluginName, Item, CheckParameters, ServiceName) -> Optional[bool] # Make a bit of context information globally available, so that functions # called by checks now this context check_api_utils.set_service(check_plugin_name, description) item_state.set_item_state_prefix(check_plugin_name, item) # Skip checks that are not in their check period period = config_cache.check_period_of_service(hostname, description) if period is not None: if not cmk.base.core.check_timeperiod(period): console.verbose( "Skipping service %s: currently not in timeperiod %s.\n", six.ensure_str(description), period) return None console.vverbose("Service %s: timeperiod %s is currently active.\n", six.ensure_str(description), period) section_name = cmk.base.check_utils.section_name_of(check_plugin_name) dont_submit = False section_content = None try: # TODO: There is duplicate code with discovery._execute_discovery(). Find a common place! try: section_content = multi_host_sections.get_section_content( hostname, ipaddress, section_name, for_discovery=False, service_description=description) except MKParseFunctionError as e: x = e.exc_info() # re-raise the original exception to not destory the trace. This may raise a MKCounterWrapped # exception which need to lead to a skipped check instead of a crash # TODO CMK-3729, PEP-3109 new_exception = x[0](x[1]) new_exception.__traceback__ = x[2] # type: ignore[attr-defined] raise new_exception # TODO: Move this to a helper function if section_content is None: # No data for this check type return False # In case of SNMP checks but missing agent response, skip this check. # TODO: This feature predates the 'parse_function', and is not needed anymore. # # Special checks which still need to be called even with empty data # # may declare this. if not section_content and cmk.base.check_utils.is_snmp_check(check_plugin_name) \ and not config.check_info[check_plugin_name]["handle_empty_info"]: return False check_function = config.check_info[check_plugin_name].get( "check_function") if check_function is None: check_function = lambda item, params, section_content: ( 3, 'UNKNOWN - Check not implemented') # Call the actual check function item_state.reset_wrapped_counters() raw_result = check_function(item, determine_check_params(params), section_content) result = sanitize_check_result( raw_result, cmk.base.check_utils.is_snmp_check(check_plugin_name)) item_state.raise_counter_wrap() except item_state.MKCounterWrapped as e: # handle check implementations that do not yet support the # handling of wrapped counters via exception on their own. # Do not submit any check result in that case: console.verbose("%-20s PEND - Cannot compute check result: %s\n", six.ensure_str(description), e) dont_submit = True except MKTimeout: raise except Exception as e: if cmk.utils.debug.enabled(): raise result = 3, cmk.base.crash_reporting.create_check_crash_dump( hostname, check_plugin_name, item, is_manual_check(hostname, check_plugin_name, item), params, description, section_content), [] if not dont_submit: # Now add information about the age of the data in the agent # sections. This is in data_sources.g_agent_cache_info. For clusters we # use the oldest of the timestamps, of course. oldest_cached_at = None largest_interval = None def minn(a, b): # type: (Optional[int], Optional[int]) -> Optional[int] if a is None: return b if b is None: return a return min(a, b) for host_sections in multi_host_sections.get_host_sections().values(): section_entries = host_sections.cache_info if section_name in section_entries: cached_at, cache_interval = section_entries[section_name] oldest_cached_at = minn(oldest_cached_at, cached_at) largest_interval = max(largest_interval, cache_interval) _submit_check_result(hostname, description, result, cached_at=oldest_cached_at, cache_interval=largest_interval) return True