Example #1
0
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, "")
Example #2
0
    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
Example #3
0
    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
Example #4
0
    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
Example #5
0
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), )
Example #6
0
    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
Example #7
0
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
Example #8
0
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