def test_get_section_cluster_kwargs(patch_register, required_sections, expected_result): node1_section_content = { SectionName("one"): NODE_1, SectionName("two"): NODE_1, SectionName("three"): NODE_1 } node2_section_content = { SectionName("two"): NODE_2, SectionName("three"): NODE_2, } parsed_sections_broker = ParsedSectionsBroker() parsed_sections_broker.setdefault( HostKey("node1", "127.0.0.1", SourceType.HOST), AgentHostSections(sections=node1_section_content), ) parsed_sections_broker.setdefault( HostKey("node2", "127.0.0.1", SourceType.HOST), AgentHostSections(sections=node2_section_content), ) kwargs = parsed_sections_broker.get_section_cluster_kwargs( [ HostKey("node1", "127.0.0.1", SourceType.HOST), HostKey("node2", "127.0.0.1", SourceType.HOST), ], [ParsedSectionName(n) for n in required_sections], ) assert expected_result == kwargs,\ "Section content: Expected '%s' but got '%s'" % (expected_result, kwargs)
def test_no_sources(self, hostname, ipaddress, mode, config_cache, host_config): broker = ParsedSectionsBroker() update_host_sections( broker, make_nodes( config_cache, host_config, ipaddress, mode=mode, sources=(), ), max_cachefile_age=0, host_config=host_config, fetcher_messages=(), selected_sections=NO_SELECTION, ) # The length is not zero because the function always sets, # at least, a piggy back section. assert len(broker) == 1 key = HostKey(hostname, ipaddress, SourceType.HOST) assert key in broker section = broker[key] # Public attributes from HostSections: assert not section.sections assert not section.cache_info assert not section.piggybacked_raw_data
def test_multiple_sources_from_the_same_host( self, hostname, ipaddress, mode, config_cache, host_config, ): sources = [ ProgramSource.ds( hostname, ipaddress, mode=mode, template="", ), TCPSource( hostname, ipaddress, mode=mode, ), ] broker = ParsedSectionsBroker() update_host_sections( broker, make_nodes( config_cache, host_config, ipaddress, mode=mode, sources=sources, ), 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, ) for source in sources ], selected_sections=NO_SELECTION, ) assert len(broker) == 1 key = HostKey(hostname, ipaddress, SourceType.HOST) assert key in broker section = broker[key] assert len(section.sections) == 1 # yapf: disable assert (section.sections[SectionName("section_name_%s" % hostname)] == len(sources) * [["section_content"]])
def _fetch_parsed_sections_broker_for_inv( config_cache: config.ConfigCache, host_config: config.HostConfig, ipaddress: Optional[HostAddress], selected_sections: SectionNameCollection, ) -> Tuple[ParsedSectionsBroker, Sequence[Tuple[Source, result.Result[ HostSections, Exception]]]]: if host_config.is_cluster: return ParsedSectionsBroker(), [] mode = (Mode.INVENTORY if selected_sections is NO_SELECTION else Mode.FORCE_SECTIONS) nodes = sources.make_nodes( config_cache, host_config, ipaddress, mode, sources.make_sources( host_config, ipaddress, mode=mode, selected_sections=selected_sections, ), ) parsed_sections_broker = ParsedSectionsBroker() results = sources.update_host_sections( parsed_sections_broker, nodes, max_cachefile_age=host_config.max_cachefile_age, host_config=host_config, fetcher_messages=list( sources.fetch_all( nodes, max_cachefile_age=host_config.max_cachefile_age, host_config=host_config, )), selected_sections=selected_sections, ) return parsed_sections_broker, results
def test_get_section_content(hostname, host_entries, cluster_node_keys, expected_result): parsed_sections_broker = ParsedSectionsBroker() for nodename, node_section_content in host_entries: parsed_sections_broker.setdefault( HostKey(nodename, "127.0.0.1", SourceType.HOST), AgentHostSections( sections={ SectionName("section_plugin_name"): node_section_content }), ) mhs = MultiHostSections(parsed_sections_broker) section_content = mhs.get_section_content( HostKey(hostname, "127.0.0.1", SourceType.HOST), check_api_utils.HOST_ONLY, "section_plugin_name", False, cluster_node_keys=cluster_node_keys, check_legacy_info= {}, # only for parse_function lookup, not needed in this test ) assert expected_result == section_content section_content = mhs.get_section_content( HostKey(hostname, "127.0.0.1", SourceType.HOST), check_api_utils.HOST_PRECEDENCE, "section_plugin_name", False, cluster_node_keys=cluster_node_keys, check_legacy_info= {}, # only for parse_function lookup, not needed in this test ) assert expected_result == section_content section_content = mhs.get_section_content( HostKey(hostname, "127.0.0.1", SourceType.MANAGEMENT), check_api_utils.MGMT_ONLY, "section_plugin_name", False, cluster_node_keys=None if cluster_node_keys is None else [ HostKey(hn, ip, SourceType.MANAGEMENT) for (hn, ip, _st) in cluster_node_keys ], check_legacy_info= {}, # only for parse_function lookup, not needed in this test ) assert section_content is None
def _get_host_section_for_parse_sections_test(): node_section_content = { SectionName("one"): NODE_1, SectionName("four"): NODE_1, } host_key = HostKey("node1", "127.0.0.1", SourceType.HOST) broker = ParsedSectionsBroker() broker.setdefault( host_key, AgentHostSections(sections=node_section_content), ) return host_key, broker
def test_get_parsed_section(patch_register, node_section_content, expected_result): parsed_sections_broker = ParsedSectionsBroker() parsed_sections_broker.setdefault( HostKey("node1", "127.0.0.1", SourceType.HOST), AgentHostSections(sections=node_section_content), ) content = parsed_sections_broker.get_parsed_section( HostKey("node1", "127.0.0.1", SourceType.HOST), ParsedSectionName("parsed"), ) assert expected_result == content,\ "Section content: Expected '%s' but got '%s'" % (expected_result, content)
def test_one_snmp_source(self, hostname, ipaddress, mode, config_cache, host_config): broker = ParsedSectionsBroker() update_host_sections( broker, make_nodes( config_cache, host_config, ipaddress, mode=mode, sources=[ SNMPSource.snmp( hostname, ipaddress, mode=mode, selected_sections=NO_SELECTION, on_scan_error="raise", ), ], ), max_cachefile_age=0, host_config=host_config, fetcher_messages=[ FetcherMessage.from_raw_data( result.OK({}), Snapshot.null(), FetcherType.SNMP, ), ], selected_sections=NO_SELECTION, ) assert len(broker) == 1 key = HostKey(hostname, ipaddress, SourceType.HOST) assert key in broker section = broker[key] assert len(section.sections) == 1 assert section.sections[SectionName("section_name_%s" % hostname)] == [["section_content"]]
def test_no_sources(self, cluster, nodes, config_cache, host_config, mode): made_nodes = make_nodes( config_cache, host_config, None, mode=mode, sources=(), ) broker = ParsedSectionsBroker() update_host_sections( broker, made_nodes, max_cachefile_age=0, host_config=host_config, fetcher_messages=[ # We do not pass sources explicitly but still append Piggyback. FetcherMessage.from_raw_data( result.OK(AgentRawData(b"")), Snapshot.null(), FetcherType.PIGGYBACK, ) for _n in made_nodes ], selected_sections=NO_SELECTION, ) assert len(broker) == len(nodes) key_clu = HostKey(cluster, None, SourceType.HOST) assert key_clu not in broker for hostname, addr in nodes.items(): key = HostKey(hostname, addr, SourceType.HOST) assert key in broker section = broker[key] # yapf: disable assert (section.sections[SectionName("section_name_%s" % hostname)] == [["section_content_%s" % hostname]]) assert not section.cache_info assert not section.piggybacked_raw_data
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"]]
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 check(_, *args, **kwargs): return result.OK(AgentHostSections(sections={section_name: [[str(section_name)]]})) monkeypatch.setattr( ip_lookup, "lookup_ip_address", 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, mode=mode, sources=make_sources(host_config, address, mode=mode) ) broker = ParsedSectionsBroker() update_host_sections( broker, nodes, max_cachefile_age=host_config.max_cachefile_age, host_config=host_config, 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, ) assert len(broker) == 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 broker section = broker[key] assert len(section.sections) == 1 assert next(iter(section.sections)) == section_name assert not section.cache_info assert not section.piggybacked_raw_data
def do_check( hostname: HostName, ipaddress: Optional[HostAddress], *, # The following arguments *must* remain optional for Nagios and the `DiscoCheckExecutor`. # See Also: `cmk.base.discovery.check_discovery()` fetcher_messages: Sequence[FetcherMessage] = (), run_only_plugin_names: Optional[Set[CheckPluginName]] = None, selected_sections: SectionNameCollection = NO_SELECTION, submit_to_core: bool = True, show_perfdata: bool = False, ) -> Tuple[int, List[ServiceDetails], List[ServiceAdditionalDetails], List[str]]: console.verbose("Checkmk version %s\n", cmk_version.__version__) config_cache = config.get_config_cache() host_config = config_cache.get_host_config(hostname) exit_spec = host_config.exit_code_spec() mode = Mode.CHECKING if selected_sections is NO_SELECTION else Mode.FORCE_SECTIONS status: ServiceState = 0 infotexts: List[ServiceDetails] = [] long_infotexts: List[ServiceAdditionalDetails] = [] perfdata: List[str] = [] try: license_usage.try_history_update() # In case of keepalive we always have an ipaddress (can be 0.0.0.0 or :: when # address is unknown). When called as non keepalive ipaddress may be None or # is already an address (2nd argument) if ipaddress is None and not host_config.is_cluster: ipaddress = ip_lookup.lookup_ip_address(host_config) # When monitoring Checkmk clusters, the cluster nodes are responsible for fetching all # information from the monitored host and cache the result for the cluster checks to be # performed on the cached information. # # This means that in case of SNMP nodes, they need to take the clustered services of the # node into account, fetch the needed sections and cache them for the cluster host. # # But later, when checking the node services, the node has to only deal with the unclustered # services. # # TODO: clean this up. The fetched sections are computed in the checkers # _make_configured_snmp_sections now. # belongs_to_cluster = len(config_cache.clusters_of(hostname)) > 0 services_to_fetch = _get_services_to_fetch( host_name=hostname, belongs_to_cluster=belongs_to_cluster, config_cache=config_cache, ) services_to_check = _filter_clustered_services( config_cache=config_cache, host_name=hostname, belongs_to_cluster=belongs_to_cluster, services=services_to_fetch, run_only_plugin_names=run_only_plugin_names, ) nodes = sources.make_nodes( config_cache, host_config, ipaddress, mode, sources.make_sources( host_config, ipaddress, mode=mode, selected_sections=selected_sections, ), ) if not fetcher_messages: # Note: `fetch_all(sources)` is almost always called in similar # code in discovery and inventory. The only other exception # is `cmk.base.discovery.check_discovery(...)`. This does # not seem right. fetcher_messages = list( sources.fetch_all( nodes, max_cachefile_age=host_config.max_cachefile_age, host_config=host_config, )) with CPUTracker() as tracker: broker = ParsedSectionsBroker() result = sources.update_host_sections( broker, nodes, max_cachefile_age=host_config.max_cachefile_age, host_config=host_config, fetcher_messages=fetcher_messages, selected_sections=selected_sections, ) num_success, plugins_missing_data = _do_all_checks_on_host( config_cache, host_config, ipaddress, parsed_sections_broker=broker, services=services_to_check, submit_to_core=submit_to_core, show_perfdata=show_perfdata, ) if run_only_plugin_names is None: inventory.do_inventory_actions_during_checking_for( config_cache, host_config, ipaddress, parsed_sections_broker=broker, ) for source, host_sections in result: source_state, source_output, source_perfdata = source.summarize( host_sections) if source_output != "": status = max(status, source_state) infotexts.append("[%s] %s" % (source.id, source_output)) perfdata.extend( [_convert_perf_data(p) for p in source_perfdata]) if plugins_missing_data: missing_data_status, missing_data_infotext = _check_plugins_missing_data( plugins_missing_data, exit_spec, bool(num_success), ) status = max(status, missing_data_status) infotexts.append(missing_data_infotext) total_times = tracker.duration for msg in fetcher_messages: total_times += msg.stats.duration infotexts.append("execution time %.1f sec" % total_times.process.elapsed) if config.check_mk_perfdata_with_times: perfdata += [ "execution_time=%.3f" % total_times.process.elapsed, "user_time=%.3f" % total_times.process.user, "system_time=%.3f" % total_times.process.system, "children_user_time=%.3f" % total_times.process.children_user, "children_system_time=%.3f" % total_times.process.children_system, ] summary: DefaultDict[str, Snapshot] = defaultdict(Snapshot.null) for msg in fetcher_messages if fetcher_messages else (): if msg.fetcher_type in ( FetcherType.PIGGYBACK, FetcherType.PROGRAM, FetcherType.SNMP, FetcherType.TCP, ): summary[{ FetcherType.PIGGYBACK: "agent", FetcherType.PROGRAM: "ds", FetcherType.SNMP: "snmp", FetcherType.TCP: "agent", }[msg.fetcher_type]] += msg.stats.duration for phase, duration in summary.items(): perfdata.append("cmk_time_%s=%.3f" % (phase, duration.idle)) else: perfdata.append("execution_time=%.3f" % total_times.process.elapsed) return status, infotexts, long_infotexts, perfdata finally: if _checkresult_file_fd is not None: _close_checkresult_file()
def _cluster_scenario(monkeypatch): hostname = "test-clusterhost" ipaddress = "1.2.3.4" node1_hostname = 'test-node1' node2_hostname = 'test-node2' def fake_lookup_ip_address(host_config, *, family, for_mgmt_board=True): return ipaddress monkeypatch.setattr(ip_lookup, "lookup_ip_address", fake_lookup_ip_address) ts = Scenario() ts.add_host(node1_hostname) ts.add_host(node2_hostname) ts.add_cluster(hostname, nodes=[node1_hostname, node2_hostname]) ts.set_ruleset("inventory_df_rules", [{ 'value': { 'ignore_fs_types': ['tmpfs', 'nfs', 'smbfs', 'cifs', 'iso9660'], 'never_ignore_mountpoints': ['~.*/omd/sites/[^/]+/tmp$'] }, 'condition': { 'host_labels': { 'cmk/check_mk_server': 'yes' } } }]) ts.set_ruleset("clustered_services", [([], [node1_hostname], ['fs_'])]) host_config = ts.apply(monkeypatch).get_host_config(hostname) DiscoveredHostLabelsStore(node1_hostname).save({ 'node1_existing_label': { 'plugin_name': 'node1_plugin', 'value': 'true', } }) DiscoveredHostLabelsStore(hostname).save({ 'existing_label': { 'plugin_name': 'foo', 'value': 'bar', }, 'another_label': { 'plugin_name': 'labels', 'value': 'true', } }) broker = ParsedSectionsBroker() broker.update({ HostKey(hostname=node1_hostname, ipaddress=ipaddress, source_type=SourceType.HOST): AgentHostSections( sections={ SectionName("labels"): [ [ '{"cmk/check_mk_server":"yes"}', ], ], SectionName("df"): [ [ '/dev/sda1', 'vfat', '523248', '3668', '519580', '1%', '/boot/test-efi', ], [ 'tmpfs', 'tmpfs', '8152916', '244', '8152672', '1%', '/opt/omd/sites/test-heute/tmp', ], ], }), HostKey(hostname=node2_hostname, ipaddress=ipaddress, source_type=SourceType.HOST): AgentHostSections( sections={ SectionName("labels"): [ [ '{"node2_live_label":"true"}', ], ], SectionName("df"): [ [ '/dev/sda1', 'vfat', '523248', '3668', '519580', '1%', '/boot/test-efi', ], [ 'tmpfs', 'tmpfs', '8152916', '244', '8152672', '1%', '/opt/omd/sites/test-heute2/tmp', ], ], }), }) return ClusterScenario( host_config, ipaddress, broker, node1_hostname, node2_hostname, )
def test__find_candidates(): broker = ParsedSectionsBroker() broker._data = { # we just care about the keys here, content set to arbitrary values that can be parsed. # section names have been are chosen arbitrarily. HostKey("test_node", "1.2.3.4", SourceType.HOST): AgentHostSections({ SectionName("kernel"): [], # host only SectionName("uptime"): [['123']], # host & mgmt }), HostKey("test_node", "1.2.3.4", SourceType.MANAGEMENT): SNMPHostSections({ # host & mgmt: SectionName("uptime"): [['123']], # type: ignore[dict-item] # mgmt only: SectionName("liebert_fans"): [[['Fan', '67', 'umin']]], # type: ignore[dict-item] # is already mgmt_ prefixed: SectionName("mgmt_snmp_info"): [[['a', 'b', 'c', 'd']]], # type: ignore[dict-item] }), } preliminary_candidates = list( agent_based_register.iter_all_check_plugins()) parsed_sections_of_interest = { parsed_section_name for plugin in preliminary_candidates for parsed_section_name in plugin.sections } assert discovery._discovered_services._find_host_candidates( broker, preliminary_candidates, parsed_sections_of_interest, ) == { CheckPluginName('docker_container_status_uptime'), CheckPluginName("kernel"), CheckPluginName('kernel_performance'), CheckPluginName('kernel_util'), CheckPluginName("uptime"), } assert discovery._discovered_services._find_mgmt_candidates( broker, preliminary_candidates, parsed_sections_of_interest, ) == { CheckPluginName('mgmt_docker_container_status_uptime'), CheckPluginName("mgmt_liebert_fans"), CheckPluginName("mgmt_uptime"), } assert discovery._discovered_services._find_candidates( broker, run_only_plugin_names=None, ) == { CheckPluginName('docker_container_status_uptime'), CheckPluginName("kernel"), CheckPluginName('kernel_performance'), CheckPluginName('kernel_util'), CheckPluginName('mgmt_docker_container_status_uptime'), CheckPluginName("mgmt_liebert_fans"), CheckPluginName("mgmt_uptime"), CheckPluginName("uptime"), }