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 _precompile_hostcheck(config_cache, hostname): 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 = file(source_filename + ".new", "w") output.write("#!/usr/bin/env python\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: 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, e:\n") output.write(" sys.exit(e.code)\n") output.write("except Exception, 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 file(source_filename).read() == file(source_filename + ".new").read(): console.verbose(" (%s is unchanged)\n", source_filename, stream=sys.stderr) os.remove(source_filename + ".new") return else: 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, hosts, silent=False, settings=None): if settings is None: settings = {} if config.monitoring_host: nagios_ip = ip_lookup.lookup_ipv4_address(config.monitoring_host) else: nagios_ip = None os.putenv("LANG", "") os.putenv("LC_ALL", "") # Start processes in parallel procs = [] for host in hosts: console.verbose("%s " % host) try: ip = ip_lookup.lookup_ipv4_address(host) 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))) 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, dot='o'): if not silent: console.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 = [] for host, ip, proc_or_error in procs: if isinstance(proc_or_error, six.string_types): lines = [proc_or_error] exitstatus = 1 else: exitstatus = proc_or_error.wait() 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 elif len(lines) == 0: if cmk.utils.debug.enabled(): raise MKGeneralException( "Cannot execute %s. Is traceroute installed? Are you root?" % command) else: dot(tty.red, '!') continue elif 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 = [] 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. elif 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 route = 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 route = r break if not 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 = route gateway = _ip_to_hostname(config_cache, 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