Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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