def _ip_to_hostname(config_cache, ip): if not cmk.base.config_cache.exists("ip_to_hostname"): cache = cmk.base.config_cache.get_dict("ip_to_hostname") for host in config_cache.all_active_realhosts(): try: cache[ip_lookup.lookup_ipv4_address(host)] = host except Exception: pass else: cache = cmk.base.config_cache.get_dict("ip_to_hostname") return cache.get(ip)
def _ip_to_hostname(config_cache, ip): # type: (config.ConfigCache, Optional[HostAddress]) -> Optional[HostName] if not _config_cache.exists("ip_to_hostname"): cache = _config_cache.get_dict("ip_to_hostname") for host in config_cache.all_active_realhosts(): try: cache[ip_lookup.lookup_ipv4_address(host)] = host except Exception: pass else: cache = _config_cache.get_dict("ip_to_hostname") return cache.get(ip)
def _ip_to_hostname(config_cache: config.ConfigCache, ip: Optional[HostAddress]) -> Optional[HostName]: if "ip_to_hostname" not in _config_cache: cache = _config_cache.get("ip_to_hostname") for host in config_cache.all_active_realhosts(): host_config = config_cache.get_host_config(host) try: cache[ip_lookup.lookup_ipv4_address(host_config)] = host except Exception: pass else: cache = _config_cache.get("ip_to_hostname") return cache.get(ip)
def _precompile_hostcheck(config_cache, hostname): # type: (ConfigCache, HostName) -> None host_config = config_cache.get_host_config(hostname) console.verbose("%s%s%-16s%s:", tty.bold, tty.blue, hostname, tty.normal, stream=sys.stderr) check_api_utils.set_hostname(hostname) compiled_filename = cmk.utils.paths.precompiled_hostchecks_dir + "/" + hostname source_filename = compiled_filename + ".py" for fname in [compiled_filename, source_filename]: try: os.remove(fname) except OSError as e: if e.errno != errno.ENOENT: raise needed_check_plugin_names = _get_needed_check_plugin_names(host_config) if not needed_check_plugin_names: console.verbose("(no Check_MK checks)\n") return output = open(source_filename + ".new", "w") output.write("#!/usr/bin/env python3\n") output.write("# encoding: utf-8\n\n") output.write("import logging\n") output.write("import sys\n\n") output.write("if not sys.executable.startswith('/omd'):\n") output.write( " sys.stdout.write(\"ERROR: Only executable with sites python\\n\")\n" ) output.write(" sys.exit(2)\n\n") # Remove precompiled directory from sys.path. Leaving it in the path # makes problems when host names (name of precompiled files) are equal # to python module names like "random" output.write("sys.path.pop(0)\n") output.write("import cmk.utils.log\n") output.write("import cmk.utils.debug\n") output.write("from cmk.utils.exceptions import MKTerminate\n") output.write("\n") output.write("import cmk.base.utils\n") output.write("import cmk.base.config as config\n") output.write("import cmk.base.console as console\n") output.write("import cmk.base.checking as checking\n") output.write("import cmk.base.check_api as check_api\n") output.write("import cmk.base.ip_lookup as ip_lookup\n") # Self-compile: replace symlink with precompiled python-code, if # we are run for the first time if config.delay_precompile: output.write(""" import os if os.path.islink(%(dst)r): import py_compile os.remove(%(dst)r) py_compile.compile(%(src)r, %(dst)r, %(dst)r, True) os.chmod(%(dst)r, 0755) """ % { "src": source_filename, "dst": compiled_filename }) # Register default Check_MK signal handler output.write("cmk.base.utils.register_sigint_handler()\n") # initialize global variables output.write(""" # very simple commandline parsing: only -v (once or twice) and -d are supported cmk.utils.log.setup_console_logging() logger = logging.getLogger("cmk.base") # TODO: This is not really good parsing, because it not cares about syntax like e.g. "-nv". # The later regular argument parsing is handling this correctly. Try to clean this up. cmk.utils.log.logger.setLevel(cmk.utils.log.verbosity_to_log_level(len([ a for a in sys.argv if a in [ "-v", "--verbose"] ]))) if '-d' in sys.argv: cmk.utils.debug.enable() """) output.write("config.load_checks(check_api.get_check_api_context, %r)\n" % _get_needed_check_file_names(needed_check_plugin_names)) for check_plugin_name in sorted(needed_check_plugin_names): console.verbose(" %s%s%s", tty.green, check_plugin_name, tty.normal, stream=sys.stderr) output.write("config.load_packed_config()\n") # IP addresses needed_ipaddresses, needed_ipv6addresses, = {}, {} if host_config.is_cluster: if host_config.nodes is None: raise TypeError() for node in host_config.nodes: node_config = config_cache.get_host_config(node) if node_config.is_ipv4_host: needed_ipaddresses[node] = ip_lookup.lookup_ipv4_address(node) if node_config.is_ipv6_host: needed_ipv6addresses[node] = ip_lookup.lookup_ipv6_address( node) try: if host_config.is_ipv4_host: needed_ipaddresses[hostname] = ip_lookup.lookup_ipv4_address( hostname) except Exception: pass try: if host_config.is_ipv6_host: needed_ipv6addresses[hostname] = ip_lookup.lookup_ipv6_address( hostname) except Exception: pass else: if host_config.is_ipv4_host: needed_ipaddresses[hostname] = ip_lookup.lookup_ipv4_address( hostname) if host_config.is_ipv6_host: needed_ipv6addresses[hostname] = ip_lookup.lookup_ipv6_address( hostname) output.write("config.ipaddresses = %r\n\n" % needed_ipaddresses) output.write("config.ipv6addresses = %r\n\n" % needed_ipv6addresses) # perform actual check with a general exception handler output.write("try:\n") output.write(" sys.exit(checking.do_check(%r, None))\n" % hostname) output.write("except MKTerminate:\n") output.write(" console.output('<Interrupted>\\n', stream=sys.stderr)\n") output.write(" sys.exit(1)\n") output.write("except SystemExit as e:\n") output.write(" sys.exit(e.code)\n") output.write("except Exception as e:\n") output.write(" import traceback, pprint\n") # status output message output.write( " sys.stdout.write(\"UNKNOWN - Exception in precompiled check: %s (details in long output)\\n\" % e)\n" ) # generate traceback for long output output.write( " sys.stdout.write(\"Traceback: %s\\n\" % traceback.format_exc())\n" ) output.write("\n") output.write(" sys.exit(3)\n") output.close() # compile python (either now or delayed), but only if the source # code has not changed. The Python compilation is the most costly # operation here. if os.path.exists(source_filename): if open(source_filename).read() == open(source_filename + ".new").read(): console.verbose(" (%s is unchanged)\n", source_filename, stream=sys.stderr) os.remove(source_filename + ".new") return console.verbose(" (new content)", stream=sys.stderr) os.rename(source_filename + ".new", source_filename) if not config.delay_precompile: py_compile.compile(source_filename, compiled_filename, compiled_filename, True) os.chmod(compiled_filename, 0o755) else: if os.path.exists(compiled_filename) or os.path.islink( compiled_filename): os.remove(compiled_filename) os.symlink(hostname + ".py", compiled_filename) console.verbose(" ==> %s.\n", compiled_filename, stream=sys.stderr)
def scan_parents_of(config_cache: config.ConfigCache, hosts: List[HostName], silent: bool = False, settings: Optional[Dict[str, int]] = None) -> Gateways: if settings is None: settings = {} if config.monitoring_host: host_config = config_cache.get_host_config(config.monitoring_host) nagios_ip = ip_lookup.lookup_ipv4_address(host_config) else: nagios_ip = None os.putenv("LANG", "") os.putenv("LC_ALL", "") # Start processes in parallel procs: List[Tuple[HostName, Optional[HostAddress], Union[str, subprocess.Popen]]] = [] for host in hosts: console.verbose("%s " % host) host_config = config_cache.get_host_config(host) try: ip = ip_lookup.lookup_ipv4_address(host_config) if ip is None: raise RuntimeError() command = [ "traceroute", "-w", "%d" % settings.get("timeout", 8), "-q", "%d" % settings.get("probes", 2), "-m", "%d" % settings.get("max_ttl", 10), "-n", ip ] console.vverbose("Running '%s'\n" % subprocess.list2cmdline(command)) procs.append((host, ip, subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True, encoding="utf-8"))) except Exception as e: if cmk.utils.debug.enabled(): raise procs.append((host, None, "ERROR: %s" % e)) # Output marks with status of each single scan def dot(color: str, dot: str = 'o') -> None: if not silent: out.output(tty.bold + color + dot + tty.normal) # Now all run and we begin to read the answers. For each host # we add a triple to gateways: the gateway, a scan state and a diagnostic output gateways: Gateways = [] for host, ip, proc_or_error in procs: if isinstance(proc_or_error, str): lines = [proc_or_error] exitstatus = 1 else: exitstatus = proc_or_error.wait() if proc_or_error.stdout is None: raise RuntimeError() lines = [l.strip() for l in proc_or_error.stdout.readlines()] if exitstatus: dot(tty.red, '*') gateways.append( (None, "failed", 0, "Traceroute failed with exit code %d" % (exitstatus & 255))) continue if len(lines) == 1 and lines[0].startswith("ERROR:"): message = lines[0][6:].strip() console.verbose("%s: %s\n", host, message, stream=sys.stderr) dot(tty.red, "D") gateways.append((None, "dnserror", 0, message)) continue if len(lines) == 0: if cmk.utils.debug.enabled(): raise MKGeneralException( "Cannot execute %s. Is traceroute installed? Are you root?" % command) dot(tty.red, '!') continue if len(lines) < 2: if not silent: console.error("%s: %s\n" % (host, ' '.join(lines))) gateways.append((None, "garbled", 0, "The output of traceroute seem truncated:\n%s" % ("".join(lines)))) dot(tty.blue) continue # Parse output of traceroute: # traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 40 byte packets # 1 * * * # 2 10.0.0.254 0.417 ms 0.459 ms 0.670 ms # 3 172.16.0.254 0.967 ms 1.031 ms 1.544 ms # 4 217.0.116.201 23.118 ms 25.153 ms 26.959 ms # 5 217.0.76.134 32.103 ms 32.491 ms 32.337 ms # 6 217.239.41.106 32.856 ms 35.279 ms 36.170 ms # 7 74.125.50.149 45.068 ms 44.991 ms * # 8 * 66.249.94.86 41.052 ms 66.249.94.88 40.795 ms # 9 209.85.248.59 43.739 ms 41.106 ms 216.239.46.240 43.208 ms # 10 216.239.48.53 45.608 ms 47.121 ms 64.233.174.29 43.126 ms # 11 209.85.255.245 49.265 ms 40.470 ms 39.870 ms # 12 8.8.8.8 28.339 ms 28.566 ms 28.791 ms routes: List[Optional[str]] = [] for line in lines[1:]: parts = line.split() route = parts[1] if route.count('.') == 3: routes.append(route) elif route == '*': routes.append(None) # No answer from this router else: if not silent: console.error( "%s: invalid output line from traceroute: '%s'\n" % (host, line)) if len(routes) == 0: error = "incomplete output from traceroute. No routes found." console.error("%s: %s\n" % (host, error)) gateways.append((None, "garbled", 0, error)) dot(tty.red) continue # Only one entry -> host is directly reachable and gets nagios as parent - # if nagios is not the parent itself. Problem here: How can we determine # if the host in question is the monitoring host? The user must configure # this in monitoring_host. if len(routes) == 1: if ip == nagios_ip: gateways.append( (None, "root", 0, "")) # We are the root-monitoring host dot(tty.white, 'N') elif config.monitoring_host: gateways.append(((config.monitoring_host, nagios_ip, None), "direct", 0, "")) dot(tty.cyan, 'L') else: gateways.append((None, "direct", 0, "")) continue # Try far most route which is not identical with host itself ping_probes = settings.get("ping_probes", 5) skipped_gateways = 0 this_route: Optional[HostAddress] = None for r in routes[::-1]: if not r or (r == ip): continue # Do (optional) PING check in order to determine if that # gateway can be monitored via the standard host check if ping_probes: if not gateway_reachable_via_ping(r, ping_probes): console.verbose("(not using %s, not reachable)\n", r, stream=sys.stderr) skipped_gateways += 1 continue this_route = r break if not this_route: error = "No usable routing information" if not silent: console.error("%s: %s\n" % (host, error)) gateways.append((None, "notfound", 0, error)) dot(tty.blue) continue # TTLs already have been filtered out) gateway_ip = this_route gateway = _ip_to_hostname(config_cache, this_route) if gateway: console.verbose("%s(%s) ", gateway, gateway_ip) else: console.verbose("%s ", gateway_ip) # Try to find DNS name of host via reverse DNS lookup dns_name = _ip_to_dnsname(gateway_ip) gateways.append( ((gateway, gateway_ip, dns_name), "gateway", skipped_gateways, "")) dot(tty.green, 'G') return gateways