def test_one_nonsnmp_source(self, hostname, ipaddress, mode, config_cache,
                                host_config, source):
        source = source(hostname, ipaddress, mode=mode)
        assert source.source_type is SourceType.HOST

        broker = ParsedSectionsBroker()
        update_host_sections(
            broker,
            make_nodes(
                config_cache,
                host_config,
                ipaddress,
                mode=mode,
                sources=[source],
            ),
            max_cachefile_age=0,
            host_config=host_config,
            fetcher_messages=[
                FetcherMessage.from_raw_data(
                    result.OK(source.default_raw_data),
                    Snapshot.null(),
                    source.fetcher_type,
                ),
            ],
            selected_sections=NO_SELECTION,
        )
        assert len(broker) == 1

        key = HostKey(hostname, ipaddress, source.source_type)
        assert key in broker

        section = broker[key]

        assert len(section.sections) == 1
        assert section.sections[SectionName("section_name_%s" %
                                            hostname)] == [["section_content"]]
Beispiel #2
0
    def test_multiple_sources_from_the_same_host(
        self,
        hostname,
        ipaddress,
        config_cache,
        host_config,
    ):
        sources = [
            DSProgramDataSource(
                hostname,
                ipaddress,
                configurator=DSProgramConfigurator(hostname, ipaddress, template=""),
            ),
            TCPDataSource(hostname, ipaddress),
        ]

        mhs = make_host_sections(
            config_cache,
            host_config,
            ipaddress,
            sources=sources,
            max_cachefile_age=0,
            selected_raw_sections=None,
        )
        assert len(mhs) == 1

        key = HostKey(hostname, ipaddress, SourceType.HOST)
        assert key in mhs

        section = mhs[key]
        assert isinstance(section, AgentHostSections)

        assert len(section.sections) == 1
        # yapf: disable
        assert (section.sections[SectionName("section_name_%s" % hostname)]
                == len(sources) * [["section_content"]])
Beispiel #3
0
    def test_one_nonsnmp_source(self, hostname, ipaddress, mode, config_cache,
                                host_config, source):
        source = source(hostname, ipaddress, mode=mode)
        assert source.source_type is SourceType.HOST

        mhs = MultiHostSections()
        update_host_sections(
            mhs,
            make_nodes(
                config_cache,
                host_config,
                ipaddress,
                mode=mode,
                sources=[source],
            ),
            max_cachefile_age=0,
            host_config=host_config,
            fetcher_messages=[
                FetcherMessage.from_raw_data(
                    result.OK(source.default_raw_data),
                    Snapshot.null(),
                    source.fetcher_type,
                ),
            ],
        )
        assert len(mhs) == 1

        key = HostKey(hostname, ipaddress, source.source_type)
        assert key in mhs

        section = mhs[key]
        assert isinstance(section, AgentHostSections)

        assert len(section.sections) == 1
        assert section.sections[SectionName("section_name_%s" %
                                            hostname)] == [["section_content"]]
    def test_multiple_sources_from_different_hosts(self, hostname, ipaddress,
                                                   config_cache, host_config):
        sources = [
            ProgramSource.ds(hostname + "0", ipaddress, template=""),
            TCPSource(hostname + "1", ipaddress),
            TCPSource(hostname + "2", ipaddress),
        ]

        nodes = make_nodes(config_cache,
                           host_config,
                           ipaddress,
                           sources=sources)

        host_sections = _collect_host_sections(
            nodes=nodes,
            file_cache_max_age=file_cache.MaxAge.none(),
            fetcher_messages=[
                FetcherMessage.from_raw_data(
                    result.OK(source.default_raw_data),
                    Snapshot.null(),
                    source.fetcher_type,
                ) for _h, _i, sources in nodes for source in sources
            ],
            selected_sections=NO_SELECTION,
        )[0]
        assert len(host_sections) == 1

        key = HostKey(hostname, ipaddress, SourceType.HOST)
        assert key in host_sections

        section = host_sections[key]

        assert len(section.sections) == len(sources)
        for source in sources:
            assert section.sections[SectionName(
                "section_name_%s" % source.hostname)] == [["section_content"]]
Beispiel #5
0
    def test_update_with_store_and_persisting_raw_data(self, logger,
                                                       monkeypatch):
        monkeypatch.setattr(time,
                            "time",
                            lambda c=itertools.count(1000, 50): next(c))
        section_store = MockStore(
            "/dev/null",
            PersistedSections[AgentRawDataSection]({
                SectionName("stored"): (0, 0, [["canned", "section"]]),
            }),
            logger=logger,
        )
        raw_data = AgentRawData(b"<<<fresh:persist(10)>>>\nhello section")
        parser = AgentParser(
            HostName("testhost"),
            section_store,
            check_interval=0,
            keep_outdated=True,
            translation={},
            encoding_fallback="ascii",
            simulation=False,
            logger=logger,
        )

        ahs = parser.parse(raw_data, selection=NO_SELECTION)
        assert ahs.sections == {
            SectionName("fresh"): [["hello", "section"]],
            SectionName("stored"): [["canned", "section"]],
        }
        assert ahs.cache_info == {
            SectionName("stored"): (0, 0),
            SectionName("fresh"): (1000, -990),
        }
        assert ahs.piggybacked_raw_data == {}
        assert section_store.load() == PersistedSections[AgentRawDataSection]({
            SectionName("stored"): (0, 0, [["canned", "section"]]),
            SectionName("fresh"): (1000, 10, [["hello", "section"]]),
        })
Beispiel #6
0
def test_gather_available_raw_section_names_defaults(backend, mocker):
    assert snmp_cache.get_oid_from_single_oid_cache(snmp_scan.OID_SYS_DESCR)
    assert snmp_cache.get_oid_from_single_oid_cache(snmp_scan.OID_SYS_OBJ)

    assert snmp_scan.gather_available_raw_section_names(
        [
            SNMPScanSection(_.name, _.detect_spec)
            for _ in config.registered_snmp_sections.values()
        ],
        on_error="raise",
        do_snmp_scan=False,
        binary_host=False,
        backend=backend,
    ) == {
        SectionName("hr_mem"),
        SectionName("mgmt_snmp_info"),
        SectionName("mgmt_snmp_uptime"),
        SectionName("snmp_info"),
        SectionName("snmp_os"),
        SectionName("snmp_uptime"),
    }
Beispiel #7
0
    def test_persist_option_and_persisted_sections(self, parser, store, mocker, monkeypatch):
        monkeypatch.setattr(time, "time", lambda c=itertools.count(1000, 50): next(c))
        monkeypatch.setattr(
            SectionStore,
            "load",
            lambda self: PersistedSections[AgentRawDataSection](
                {
                    SectionName("persisted"): (42, 69, [["content"]]),
                }
            ),
        )
        # Patch IO:
        monkeypatch.setattr(SectionStore, "store", lambda self, sections: None)

        raw_data = AgentRawData(
            b"\n".join(
                (
                    b"<<<section:persist(%i)>>>" % (1000 + 50),
                    b"first line",
                    b"second line",
                )
            )
        )

        ahs = parser.parse(raw_data, selection=NO_SELECTION)

        assert ahs.sections == {
            SectionName("section"): [["first", "line"], ["second", "line"]],
            SectionName("persisted"): [["content"]],
        }
        assert ahs.cache_info == {
            SectionName("section"): (1000, 50),
            SectionName("persisted"): (42, 27),
        }
        assert ahs.piggybacked_raw_data == {}
        assert store.load() == PersistedSections[AgentRawDataSection](
            {
                SectionName("persisted"): (42, 69, [["content"]]),
            }
        )
Beispiel #8
0
    def test_update_with_persisted_and_store(self):
        section_store = MockStore(PersistedSections[SNMPRawDataSection]({
            SectionName("stored"): (0, 0, [["old"]]),
        }))
        _new: SNMPRawDataSection = [["new"]]  # For the type checker only
        raw_data: SNMPRawData = {SectionName("fresh"): _new}
        parser = SNMPParser(
            "testhost",
            section_store,
            check_intervals={},
            keep_outdated=True,
            logger=logging.getLogger("test"),
        )

        shs = parser.parse(raw_data, selection=NO_SELECTION)
        assert shs.sections == {
            SectionName("stored"): [["old"]],
            SectionName("fresh"): [["new"]],
        }
        assert shs.cache_info == {SectionName("stored"): (0, 0)}
        assert shs.piggybacked_raw_data == {}
        assert section_store.load() == {
            SectionName("stored"): (0, 0, [["old"]]),
        }
Beispiel #9
0
 def _from_cache_file(self, raw_data: bytes) -> SNMPRawData:
     return {
         SectionName(k): v
         for k, v in ast.literal_eval(raw_data.decode("utf-8")).items()
     }
def test_parse_sap_hana_diskusage(info, expected_result):
    section_name = SectionName("sap_hana_diskusage")
    section_plugin = register.get_section_plugin(section_name)
    result = section_plugin.parse_function(info)
    assert result == expected_result
Beispiel #11
0
 def raw_data(self):
     table: SNMPTable = []
     raw_data: SNMPRawData = {SectionName("X"): table}
     return raw_data
Beispiel #12
0
 def load(self) -> PersistedSections[TRawDataSection]:
     raw_sections_data = _store.load_object_from_file(self.path, default={})
     return PersistedSections[TRawDataSection](
         {SectionName(k): v
          for k, v in raw_sections_data.items()})
Beispiel #13
0
 def _deserialize(data: bytes) -> SNMPRawData:
     try:
         return {SectionName(k): v for k, v in json.loads(data.decode("utf8")).items()}
     except json.JSONDecodeError:
         raise ValueError(repr(data))
Beispiel #14
0
class SNMPFetcher(Fetcher[SNMPRawData]):
    CPU_SECTIONS_WITHOUT_CPU_IN_NAME = {
        SectionName("brocade_sys"),
        SectionName("bvip_util"),
    }
    plugin_store: SNMPPluginStore = SNMPPluginStore()

    def __init__(
        self,
        file_cache: SNMPFileCache,
        *,
        sections: Dict[SectionName, SectionMeta],
        on_error: str,
        missing_sys_description: bool,
        do_status_data_inventory: bool,
        section_store_path: Union[Path, str],
        snmp_config: SNMPHostConfig,
    ) -> None:
        super().__init__(file_cache, logging.getLogger("cmk.helper.snmp"))
        self.sections: Final = sections
        self.on_error: Final = on_error
        self.missing_sys_description: Final = missing_sys_description
        self.do_status_data_inventory: Final = do_status_data_inventory
        self.snmp_config: Final = snmp_config
        self._section_store = SectionStore[SNMPRawDataSection](
            section_store_path,
            logger=self._logger,
        )
        self._backend = factory.backend(self.snmp_config, self._logger)

    @property
    def disabled_sections(self) -> Set[SectionName]:
        return {name for name, meta in self.sections.items() if meta.disabled}

    @property
    def checking_sections(self) -> Set[SectionName]:
        return {name for name, meta in self.sections.items() if meta.checking}

    @property
    def inventory_sections(self) -> Set[SectionName]:
        return {
            name
            for name, data in self.plugin_store.items() if data.inventory
        }

    @classmethod
    def _from_json(cls, serialized: Dict[str, Any]) -> 'SNMPFetcher':
        # The SNMPv3 configuration is represented by a tuple of different lengths (see
        # SNMPCredentials). Since we just deserialized from JSON, we have to convert the
        # list used by JSON back to a tuple.
        # SNMPv1/v2 communities are represented by a string: Leave it untouched.
        if isinstance(serialized["snmp_config"]["credentials"], list):
            serialized["snmp_config"]["credentials"] = tuple(
                serialized["snmp_config"]["credentials"])

        return cls(
            file_cache=SNMPFileCache.from_json(serialized.pop("file_cache")),
            sections={
                SectionName(s): SectionMeta.deserialize(m)
                for s, m in serialized["sections"].items()
            },
            on_error=serialized["on_error"],
            missing_sys_description=serialized["missing_sys_description"],
            do_status_data_inventory=serialized["do_status_data_inventory"],
            section_store_path=serialized["section_store_path"],
            snmp_config=SNMPHostConfig.deserialize(serialized["snmp_config"]),
        )

    def to_json(self) -> Dict[str, Any]:
        return {
            "file_cache": self.file_cache.to_json(),
            "sections":
            {str(s): m.serialize()
             for s, m in self.sections.items()},
            "on_error": self.on_error,
            "missing_sys_description": self.missing_sys_description,
            "do_status_data_inventory": self.do_status_data_inventory,
            "section_store_path": str(self._section_store.path),
            "snmp_config": self.snmp_config.serialize(),
        }

    def open(self) -> None:
        verify_ipaddress(self.snmp_config.ipaddress)

    def close(self) -> None:
        pass

    def _detect(self, *, select_from: Set[SectionName]) -> Set[SectionName]:
        """Detect the applicable sections for the device in question"""
        return gather_available_raw_section_names(
            sections=[(name, self.plugin_store[name].detect_spec)
                      for name in select_from],
            on_error=self.on_error,
            missing_sys_description=self.missing_sys_description,
            backend=self._backend,
        )

    def _use_snmpwalk_cache(self, mode: Mode) -> bool:
        """Decide whether to load data from the SNMP walk cache

        The SNMP walk cache applies to individual OIDs that are marked as to-be-cached
        in the section definition plugins using `OIDCached`.
        """
        return mode in (Mode.DISCOVERY, Mode.CHECKING)

    def _is_cache_read_enabled(self, mode: Mode) -> bool:
        """Decide whether to try to read data from cache

        Fetching for SNMP data is special in that we have to list the sections to fetch
        in advance, unlike for agent data, where we parse the data and see what we get.

        For discovery, we must not fetch the pre-configured sections (which are the ones
        in the cache), but all sections for which the detection spec evaluates to true,
        which can be many more.
        """
        return mode is Mode.DISCOVERY

    def _is_cache_write_enabled(self, mode: Mode) -> bool:
        """Decide whether to write data to cache

        If we write the fetching result for SNMP, we also "override" the resulting
        sections for the next call that uses the cache. Since we use the cache for
        DISCOVERY only, we must only write it if we're dealing with the right
        sections for discovery.
        """
        return mode is Mode.DISCOVERY

    def _get_selection(self, mode: Mode) -> Set[SectionName]:
        """Determine the sections fetched unconditionally (without detection)"""
        if mode is Mode.CHECKING:
            return self.checking_sections - self.disabled_sections

        if mode is Mode.FORCE_SECTIONS:
            return self.checking_sections

        return set()

    def _get_detected_sections(self, mode: Mode) -> Set[SectionName]:
        """Determine the sections fetched after successful detection"""
        if mode is Mode.INVENTORY or (mode is Mode.CHECKING
                                      and self.do_status_data_inventory):
            return self.inventory_sections - self.disabled_sections

        if mode is Mode.DISCOVERY:
            return set(self.plugin_store) - self.disabled_sections

        return set()

    def _fetch_from_io(self, mode: Mode) -> SNMPRawData:
        """Select the sections we need to fetch and do that

        Note:
            There still may be some fetching from cache involved
            if the fetch interval was overridden by the user.

        Detection:

         * Mode.DISCOVERY:
           In this straight forward case we must determine all applicable sections for
           the device in question.

         * Mode.INVENTORY
           There is no need to try to detect all sections: For the inventory we have a
           set of sections known to be relevant for inventory plugins, and we can restrict
           detection to those.

         * Mode.CHECKING
           Sections needed for checking are known without detection. If the status data
           inventory is enabled, we detect from the inventory sections; but not those,
           which are fetched for checking anyway.

        """
        now = int(time.time())
        persisted_sections = (self._section_store.load()
                              if mode is Mode.CHECKING else
                              PersistedSections[SNMPRawDataSection]({}))
        section_names = self._get_selection(mode)
        section_names |= self._detect(
            select_from=self._get_detected_sections(mode) - section_names)

        walk_cache = snmp_table.WalkCache(self._backend.hostname)
        if self._use_snmpwalk_cache(mode):
            walk_cache_msg = "SNMP walk cache is enabled: Use any locally cached information"
            walk_cache.load(
                trees=(tree for section_name in section_names
                       for tree in self.plugin_store[section_name].trees), )
        else:
            walk_cache_msg = "SNMP walk cache is disabled"

        fetched_data: MutableMapping[SectionName, SNMPRawDataSection] = {}
        for section_name in self._sort_section_names(section_names):
            try:
                _from, until, _section = persisted_sections[section_name]
                if now > until:
                    raise LookupError(section_name)
            except LookupError:
                self._logger.debug("%s: Fetching data (%s)", section_name,
                                   walk_cache_msg)
                section = [
                    snmp_table.get_snmp_table(
                        section_name=section_name,
                        tree=tree,
                        walk_cache=walk_cache,
                        backend=self._backend,
                    ) for tree in self.plugin_store[section_name].trees
                ]

                if any(section):
                    fetched_data[section_name] = section

        walk_cache.save()

        return fetched_data

    @classmethod
    def _sort_section_names(
        cls,
        section_names: Iterable[SectionName],
    ) -> Iterable[SectionName]:
        # In former Checkmk versions (<=1.4.0) CPU check plugins were
        # checked before other check plugins like interface checks.
        # In Checkmk 1.5 the order was random and
        # interface sections where executed before CPU check plugins.
        # This lead to high CPU utilization sent by device. Thus we have
        # to re-order the section names.
        return sorted(
            section_names,
            key=lambda x: (not ('cpu' in str(x) or x in cls.
                                CPU_SECTIONS_WITHOUT_CPU_IN_NAME), x),
        )
Beispiel #15
0
 def deserialize(cls, serialized: Mapping[str, Any]) -> "SNMPPluginStore":
     return cls({
         SectionName(k): SNMPPluginStoreItem.deserialize(v)
         for k, v in serialized["plugin_store"].items()
     })
Beispiel #16
0
def _execute_check_legacy_mode(multi_host_sections: MultiHostSections, hostname: HostName,
                               ipaddress: Optional[HostAddress], service: Service) -> bool:
    legacy_check_plugin_name = config.legacy_check_plugin_names.get(service.check_plugin_name)
    if legacy_check_plugin_name is None:
        _submit_check_result(hostname, service.description, CHECK_NOT_IMPLEMENTED, None)
        return True

    check_function = config.check_info[legacy_check_plugin_name].get("check_function")
    if check_function is None:
        _submit_check_result(hostname, service.description, CHECK_NOT_IMPLEMENTED, None)
        return True

    # Make a bit of context information globally available, so that functions
    # called by checks know this context. check_api_utils.set_service has
    # already been called.
    item_state.set_item_state_prefix(str(service.check_plugin_name), service.item)

    section_name = legacy_check_plugin_name.split('.')[0]

    section_content = None
    mgmt_board_info = config.get_management_board_precedence(section_name, config.check_info)
    source_type = SourceType.MANAGEMENT if mgmt_board_info == LEGACY_MGMT_ONLY else SourceType.HOST
    try:
        section_content = multi_host_sections.get_section_content(
            HostKey(hostname, ipaddress, source_type),
            mgmt_board_info,
            section_name,
            for_discovery=False,
            cluster_node_keys=config.get_config_cache().get_clustered_service_node_keys(
                hostname,
                source_type,
                service.description,
                ip_lookup.lookup_ip_address,
            ),
            check_legacy_info=config.check_info,
        )

        # TODO: Move this to a helper function
        if section_content is None:  # No data for this check type
            return False

        # Call the actual check function
        item_state.reset_wrapped_counters()

        used_params = legacy_determine_check_params(service.parameters)
        raw_result = check_function(service.item, used_params, section_content)
        result = sanitize_check_result(raw_result)
        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",
                        ensure_str(service.description), e)
        # Don't submit to core - we're done.
        return True

    except MKTimeout:
        raise

    except Exception:
        if cmk.utils.debug.enabled():
            raise
        result = 3, cmk.base.crash_reporting.create_check_crash_dump(
            hostname,
            service.check_plugin_name,
            {
                "item": service.item,
                "params": used_params,
                "section_content": section_content
            },
            is_manual_check(hostname, service.id()),
            service.description,
        ), []

    _submit_check_result(
        hostname,
        service.description,
        result,
        _legacy_determine_cache_info(multi_host_sections, SectionName(section_name)),
    )
    return True
Beispiel #17
0
def test_cross_class_comparison_fails():
    with pytest.raises(TypeError):
        _ = CheckPluginName("foo") == SectionName("foo")
Beispiel #18
0
 def snmp_payload(self):
     table: SNMPTable = []
     return SNMPResultMessage({SectionName("name"): table})
Beispiel #19
0
    def get_section_content(
        self,
        host_key: HostKey,
        management_board_info: str,
        check_plugin_name: str,
        for_discovery: bool,
        *,
        cluster_node_keys: Optional[List[HostKey]] = None,
        check_legacy_info: Dict[str, Dict[str, Any]],
    ) -> Union[None, ParsedSectionContent, List[ParsedSectionContent]]:
        """Prepares the section_content construct for a Check_MK check on ANY host

        The section_content construct is then handed over to the check, inventory or
        discovery functions for doing their work.

        If the host is a cluster, the sections from all its nodes is merged together
        here. Optionally the node info is added to the nodes section content.

        It handles the whole data and cares about these aspects:

        a) Extract the section_content for the given check_plugin_name
        b) Adds node_info to the section_content (if check asks for this)
        c) Applies the parse function (if check has some)

        It can return an section_content construct or None when there is no section content
        for this check available.
        """

        section_name = section_name_of(check_plugin_name)
        cache_key = (host_key, management_board_info, section_name, for_discovery,
                     bool(cluster_node_keys))

        try:
            return self._section_content_cache[cache_key]
        except KeyError:
            pass

        section_content = self._get_section_content(
            host_key._replace(source_type=SourceType.MANAGEMENT if management_board_info ==
                              LEGACY_MGMT_ONLY else SourceType.HOST),
            check_plugin_name,
            SectionName(section_name),
            for_discovery,
            cluster_node_keys=cluster_node_keys,
            check_legacy_info=check_legacy_info,
        )

        # If we found nothing, see if we must check the management board:
        if (section_content is None and host_key.source_type is SourceType.HOST and
                management_board_info == LEGACY_HOST_PRECEDENCE):
            section_content = self._get_section_content(
                host_key._replace(source_type=SourceType.MANAGEMENT),
                check_plugin_name,
                SectionName(section_name),
                for_discovery,
                cluster_node_keys=cluster_node_keys,
                check_legacy_info=check_legacy_info,
            )

        self._section_content_cache[cache_key] = section_content
        return section_content
Beispiel #20
0
def _get_aggregated_result(
    *,
    parsed_sections_broker: ParsedSectionsBroker,
    hostname: HostName,
    ipaddress: Optional[HostAddress],
    service: Service,
    used_params: LegacyCheckParameters,
) -> AggregatedResult:
    legacy_check_plugin_name = config.legacy_check_plugin_names.get(service.check_plugin_name)
    if legacy_check_plugin_name is None:
        return AggregatedResult(
            submit=True,
            data_received=True,
            result=CHECK_NOT_IMPLEMENTED,
            cache_info=None,
        )

    check_function = config.check_info[legacy_check_plugin_name].get("check_function")
    if check_function is None:
        return AggregatedResult(
            submit=True,
            data_received=True,
            result=CHECK_NOT_IMPLEMENTED,
            cache_info=None,
        )

    section_name = legacy_check_plugin_name.split('.')[0]
    main_check_info = config.check_info.get(section_name, {})

    section_content = None
    multi_host_sections = _MultiHostSections(parsed_sections_broker)
    mgmt_board_info = main_check_info.get("management_board") or LEGACY_HOST_PRECEDENCE
    source_type = SourceType.MANAGEMENT if mgmt_board_info == LEGACY_MGMT_ONLY else SourceType.HOST
    try:
        section_content = multi_host_sections.get_section_content(
            HostKey(hostname, ipaddress, source_type),
            mgmt_board_info,
            section_name,
            for_discovery=False,
            cluster_node_keys=config.get_config_cache().get_clustered_service_node_keys(
                hostname,
                source_type,
                service.description,
            ),
            check_legacy_info=config.check_info,
        )

        if section_content is None:  # No data for this check type
            return AggregatedResult(
                submit=False,
                data_received=False,
                result=RECEIVED_NO_DATA,
                cache_info=None,
            )

        # Call the actual check function
        item_state.reset_wrapped_counters()

        raw_result = check_function(service.item, used_params, section_content)
        result = _sanitize_check_result(raw_result)
        item_state.raise_counter_wrap()

    except item_state.MKCounterWrapped as exc:
        # 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:
        return AggregatedResult(
            submit=False,
            data_received=True,
            result=(0, f"Cannot compute check result: {exc}\n", []),
            cache_info=None,
        )

    except MKTimeout:
        raise

    except Exception:
        if cmk.utils.debug.enabled():
            raise
        result = 3, cmk.base.crash_reporting.create_check_crash_dump(
            host_name=hostname,
            service_name=service.description,
            plugin_name=service.check_plugin_name,
            plugin_kwargs={
                "item": service.item,
                "params": used_params,
                "section_content": section_content
            },
            is_manual=service.id() in check_table.get_check_table(hostname, skip_autochecks=True),
        ), []

    return AggregatedResult(
        submit=True,
        data_received=True,
        result=result,
        cache_info=multi_host_sections.legacy_determine_cache_info(SectionName(section_name)),
    )
Beispiel #21
0
    make_sources,
)
from cmk.base.data_sources.agent import AgentHostSections
from cmk.base.data_sources.host_sections import HostKey, MultiHostSections
from cmk.base.data_sources.piggyback import PiggyBackDataSource
from cmk.base.data_sources.programs import DSProgramConfigurator, DSProgramDataSource
from cmk.base.data_sources.snmp import SNMPDataSource, SNMPHostSections
from cmk.base.data_sources.tcp import TCPDataSource

_TestSection = collections.namedtuple(
    "TestSection",
    "name, parsed_section_name, parse_function, supercedes",
)

SECTION_ONE = _TestSection(
    SectionName("one"),
    ParsedSectionName("parsed"),
    lambda x: {
        "parsed_by": "one",
        "node": x[0][0]
    },
    [],
)

SECTION_TWO = _TestSection(
    SectionName("two"),
    ParsedSectionName("parsed"),
    lambda x: {
        "parsed_by": "two",
        "node": x[0][0]
    },
Beispiel #22
0
def create_snmp_section_plugin(
    *,
    name: str,
    detect_spec: SNMPDetectBaseType,
    fetch: Union[SNMPTree, List[SNMPTree]],
    parsed_section_name: Optional[str] = None,
    parse_function: Union[SimpleSNMPParseFunction, SNMPParseFunction,
                          None] = None,
    host_label_function: Optional[HostLabelFunction] = None,
    host_label_default_parameters: Optional[Dict] = None,
    host_label_ruleset_name: Optional[str] = None,
    host_label_ruleset_type: RuleSetType = "merged",
    supersedes: Optional[List[str]] = None,
    module: Optional[str] = None,
    validate_creation_kwargs: bool = True,
) -> SNMPSectionPlugin:
    """Return an SNMPSectionPlugin object after validating and converting the arguments one by one

    For a detailed description of the parameters please refer to the exposed function in the
    'register' namespace of the API.
    """
    section_name = SectionName(name)

    # normalize to List[SNMPTree]
    tree_list = [fetch] if isinstance(fetch, SNMPTree) else fetch

    if validate_creation_kwargs:
        _validate_detect_spec(detect_spec)
        _validate_fetch_spec(tree_list)

        if parse_function is not None:
            needs_bytes = any(oid.encoding == "binary" for tree in tree_list
                              for oid in tree.oids)
            _validate_parse_function(
                parse_function,
                expected_annotation=_create_parse_annotation(
                    needs_bytes=needs_bytes,
                    is_list=isinstance(fetch, list),
                ),
            )

        if host_label_function is not None:
            _validate_host_label_kwargs(
                host_label_function=host_label_function,
                host_label_default_parameters=host_label_default_parameters,
                host_label_ruleset_name=host_label_ruleset_name,
                host_label_ruleset_type=host_label_ruleset_type,
            )

    return SNMPSectionPlugin(
        name=section_name,
        parsed_section_name=ParsedSectionName(
            parsed_section_name if parsed_section_name else str(section_name)),
        parse_function=_create_snmp_parse_function(parse_function,
                                                   isinstance(fetch,
                                                              SNMPTree)),
        host_label_function=_create_host_label_function(host_label_function),
        host_label_default_parameters=host_label_default_parameters,
        host_label_ruleset_name=(None if host_label_ruleset_name is None else
                                 RuleSetName(host_label_ruleset_name)),
        host_label_ruleset_type=host_label_ruleset_type,
        supersedes=_create_supersedes(section_name, supersedes),
        detect_spec=detect_spec,
        trees=tree_list,
        module=module,
    )
Beispiel #23
0
class TestSectionMarker:
    def test_options_serialize_options(self):
        section_header = SectionMarker.from_headerline(
            b"<<<"
            + b":".join(
                (
                    b"section",
                    b"cached(1,2)",
                    b"encoding(ascii)",
                    b"nostrip()",
                    b"persist(42)",
                    b"sep(124)",
                )
            )
            + b">>>"
        )
        assert section_header == SectionMarker.from_headerline(str(section_header).encode("ascii"))

    def test_options_deserialize_defaults(self):
        section_header = SectionMarker.from_headerline(b"<<<section>>>")
        other_header = SectionMarker.from_headerline(str(section_header).encode("ascii"))
        assert section_header == other_header
        assert str(section_header) == str(other_header)

    @pytest.mark.parametrize(
        "headerline, section_name, section_options",
        [
            ("norris", SectionName("norris"), {}),
            ("norris:chuck", SectionName("norris"), {"chuck": None}),
            (
                "my_section:sep(0):cached(23,42)",
                SectionName("my_section"),
                {"sep": "0", "cached": "23,42"},
            ),
            ("my.section:sep(0):cached(23,42)", None, {}),  # invalid section name
            ("", None, {}),  # invalid section name
        ],
    )  # yapf: disable
    def test_options_from_headerline(self, headerline, section_name, section_options):
        try:
            SectionMarker.from_headerline(
                f"<<<{headerline}>>>".encode("ascii")
            ) == (  # type: ignore[comparison-overlap]
                section_name,
                section_options,
            )
        except ValueError:
            assert section_name is None

    def test_options_decode_values(self):
        section_header = SectionMarker.from_headerline(
            b"<<<"
            + b":".join(
                (
                    b"name",
                    b"cached(1,2)",
                    b"encoding(ascii)",
                    b"nostrip()",
                    b"persist(42)",
                    b"sep(124)",
                )
            )
            + b">>>"
        )
        assert section_header.name == SectionName("name")
        assert section_header.cached == (1, 2)
        assert section_header.encoding == "ascii"
        assert section_header.nostrip is True
        assert section_header.persist == 42
        assert section_header.separator == "|"

    def test_options_decode_defaults(self):
        section_header = SectionMarker.from_headerline(b"<<<name>>>")
        assert section_header.name == SectionName("name")
        assert section_header.cached is None
        assert section_header.encoding == "utf-8"
        assert section_header.nostrip is False
        assert section_header.persist is None
        assert section_header.separator is None
Beispiel #24
0
def test_validate_supersedings_raise_self_superseding():
    with pytest.raises(ValueError, match="cannot supersede myself"):
        section_plugins._validate_supersedings(SectionName("foo"),
                                               [SectionName("foo")])
Beispiel #25
0
 def snmp_raw_data(self):
     table: SNMPTable = [[[6500337, 11822045]]]
     return {SectionName("snmp_uptime"): table}
Beispiel #26
0
    Mode,
)
from cmk.base.data_sources.agent import AgentHostSections
from cmk.base.data_sources.host_sections import HostKey, MultiHostSections
from cmk.base.data_sources.piggyback import PiggybackConfigurator
from cmk.base.data_sources.programs import ProgramConfigurator
from cmk.base.data_sources.snmp import SNMPConfigurator, SNMPHostSections, CachedSNMPDetector
from cmk.base.data_sources.tcp import TCPConfigurator

_TestSection = collections.namedtuple(
    "TestSection",
    "name, parsed_section_name, parse_function, supersedes",
)

SECTION_ONE = _TestSection(
    SectionName("one"),
    ParsedSectionName("parsed"),
    lambda x: {
        "parsed_by": "one",
        "node": x[0][0]
    },
    set(),
)

SECTION_TWO = _TestSection(
    SectionName("two"),
    ParsedSectionName("parsed"),
    lambda x: {
        "parsed_by": "two",
        "node": x[0][0]
    },
Beispiel #27
0
    def get_section_content(
        self,
        host_key: HostKey,
        management_board_info: str,
        check_plugin_name: CheckPluginNameStr,
        for_discovery: bool,
        service_description: Optional[ServiceName] = None
    ) -> FinalSectionContent:
        """Prepares the section_content construct for a Check_MK check on ANY host

        The section_content construct is then handed over to the check, inventory or
        discovery functions for doing their work.

        If the host is a cluster, the sections from all its nodes is merged together
        here. Optionally the node info is added to the nodes section content.

        It handles the whole data and cares about these aspects:

        a) Extract the section_content for the given check_plugin_name
        b) Adds node_info to the section_content (if check asks for this)
        c) Applies the parse function (if check has some)
        d) Adds extra_sections (if check asks for this)
           and also applies node_info and extra_section handling to this

        It can return an section_content construct or None when there is no section content
        for this check available.
        """

        section_name = section_name_of(check_plugin_name)
        nodes_of_clustered_service = self._get_nodes_of_clustered_service(
            host_key.hostname, service_description)
        cache_key = (host_key, management_board_info, section_name,
                     for_discovery, bool(nodes_of_clustered_service))

        try:
            return self._section_content_cache[cache_key]
        except KeyError:
            pass

        section_content = self._get_section_content(
            host_key._replace(
                source_type=SourceType.MANAGEMENT if management_board_info ==
                LEGACY_MGMT_ONLY else SourceType.HOST),
            check_plugin_name,
            SectionName(section_name),
            for_discovery,
            nodes_of_clustered_service,
        )

        # If we found nothing, see if we must check the management board:
        if (section_content is None and host_key.source_type is SourceType.HOST
                and management_board_info == LEGACY_HOST_PRECEDENCE):
            section_content = self._get_section_content(
                host_key._replace(source_type=SourceType.MANAGEMENT),
                check_plugin_name,
                SectionName(section_name),
                for_discovery,
                nodes_of_clustered_service,
            )

        self._section_content_cache[cache_key] = section_content
        return section_content
Beispiel #28
0
def test_get_host_sections_cluster(mode, monkeypatch, mocker):
    hostname = "testhost"
    hosts = {
        "host0": "10.0.0.0",
        "host1": "10.0.0.1",
        "host2": "10.0.0.2",
    }
    address = "1.2.3.4"
    tags = {"agent": "no-agent"}
    section_name = SectionName("test_section")
    config_cache = make_scenario(hostname, tags).apply(monkeypatch)
    host_config = config.HostConfig.make_host_config(hostname)

    def lookup_ip_address(host_config, family=None, for_mgmt_board=False):
        return hosts[host_config.hostname]

    def make_piggybacked_sections(hc):
        if hc.nodes == host_config.nodes:
            return {section_name: True}
        return {}

    def check(_, *args, **kwargs):
        return AgentHostSections(sections={section_name: [[str(section_name)]]})

    monkeypatch.setattr(
        ip_lookup,
        "lookup_ip_address",
        lookup_ip_address,
    )
    monkeypatch.setattr(
        _data_sources,
        "_make_piggybacked_sections",
        make_piggybacked_sections,
    )
    monkeypatch.setattr(
        ABCChecker,
        "check",
        check,
    )
    mocker.patch.object(
        cmk.utils.piggyback,
        "remove_source_status_file",
        autospec=True,
    )
    mocker.patch.object(
        cmk.utils.piggyback,
        "_store_status_file_of",
        autospec=True,
    )

    # Create a cluster
    host_config.nodes = list(hosts.keys())

    mhs = MultiHostSections()
    update_host_sections(
        mhs,
        make_nodes(
            config_cache,
            host_config,
            address,
            mode=mode,
            sources=make_checkers(host_config, address, mode=mode),
        ),
        max_cachefile_age=host_config.max_cachefile_age,
        selected_raw_sections=None,
        host_config=host_config,
    )
    assert len(mhs) == len(hosts) == 3
    cmk.utils.piggyback._store_status_file_of.assert_not_called()  # type: ignore[attr-defined]
    assert cmk.utils.piggyback.remove_source_status_file.call_count == 3  # type: ignore[attr-defined]

    for host, addr in hosts.items():
        remove_source_status_file = cmk.utils.piggyback.remove_source_status_file
        remove_source_status_file.assert_any_call(host)  # type: ignore[attr-defined]
        key = HostKey(host, addr, SourceType.HOST)
        assert key in mhs
        section = mhs[key]
        assert len(section.sections) == 1
        assert next(iter(section.sections)) == section_name
        assert not section.cache_info
        assert not section.piggybacked_raw_data
        assert not section.persisted_sections
Beispiel #29
0
 def raw_data(self, file_cache):
     if isinstance(file_cache, DefaultAgentFileCache):
         return AgentRawData(b"<<<check_mk>>>\nagent raw data")
     assert isinstance(file_cache, SNMPFileCache)
     table: Sequence[SNMPTable] = []
     return {SectionName("X"): table}
def test_get_host_sections_cluster(monkeypatch, mocker):
    hostname = "testhost"
    hosts = {
        "host0": "10.0.0.0",
        "host1": "10.0.0.1",
        "host2": "10.0.0.2",
    }
    address = "1.2.3.4"
    tags = {"agent": "no-agent"}
    section_name = SectionName("test_section")
    config_cache = make_scenario(hostname, tags).apply(monkeypatch)
    host_config = config.HostConfig.make_host_config(hostname)

    def fake_lookup_ip_address(host_config, family=None):
        return hosts[host_config.hostname]

    def check(_, *args, **kwargs):
        return result.OK(AgentHostSections(sections={section_name: [[str(section_name)]]}))

    monkeypatch.setattr(
        config,
        "lookup_ip_address",
        fake_lookup_ip_address,
    )
    monkeypatch.setattr(
        Source,
        "parse",
        check,
    )
    mocker.patch.object(
        cmk.utils.piggyback,
        "remove_source_status_file",
        autospec=True,
    )
    mocker.patch.object(
        cmk.utils.piggyback,
        "_store_status_file_of",
        autospec=True,
    )

    # Create a cluster
    host_config.nodes = list(hosts.keys())

    nodes = make_nodes(
        config_cache,
        host_config,
        address,
        sources=make_sources(host_config, address),
    )

    host_sections = _collect_host_sections(
        nodes=nodes,
        file_cache_max_age=host_config.max_cachefile_age,
        fetcher_messages=[
                FetcherMessage.from_raw_data(
                    result.OK(source.default_raw_data),
                    Snapshot.null(),
                    source.fetcher_type,
                )
                for _h, _i, sources in nodes for source in sources
            ],
        selected_sections=NO_SELECTION,
    )[0]
    assert len(host_sections) == len(hosts) == 3
    cmk.utils.piggyback._store_status_file_of.assert_not_called()  # type: ignore[attr-defined]
    assert cmk.utils.piggyback.remove_source_status_file.call_count == 3  # type: ignore[attr-defined]

    for host, addr in hosts.items():
        remove_source_status_file = cmk.utils.piggyback.remove_source_status_file
        remove_source_status_file.assert_any_call(host)  # type: ignore[attr-defined]
        key = HostKey(host, addr, SourceType.HOST)
        assert key in host_sections
        section = host_sections[key]
        assert len(section.sections) == 1
        assert next(iter(section.sections)) == section_name
        assert not section.cache_info
        assert not section.piggybacked_raw_data