def _find_host_candidates( broker: ParsedSectionsBroker, preliminary_candidates: List[checking_classes.CheckPlugin], parsed_sections_of_interest: Set[ParsedSectionName], ) -> Set[CheckPluginName]: available_parsed_sections = broker.filter_available( parsed_sections_of_interest, SourceType.HOST, ) return { plugin.name for plugin in preliminary_candidates # *filter out* all names of management only check plugins if not plugin.name.is_management_name() and any( section in available_parsed_sections for section in plugin.sections) }
def _discover_host_labels_for_source_type( *, host_key: HostKey, parsed_sections_broker: ParsedSectionsBroker, on_error: OnError, ) -> Mapping[str, HostLabel]: host_labels = {} try: parsed_results = parsed_sections_broker.all_parsing_results(host_key) console.vverbose("Trying host label discovery with: %s\n" % ", ".join(str(r.section.name) for r in parsed_results)) for (section_data, _cache_info), section_plugin in parsed_results: kwargs = {'section': section_data} host_label_params = config.get_host_label_parameters(host_key.hostname, section_plugin) if host_label_params is not None: kwargs["params"] = host_label_params try: for label in section_plugin.host_label_function(**kwargs): console.vverbose(f" {label.name}: {label.value} ({section_plugin.name})\n") host_labels[label.name] = HostLabel( label.name, label.value, section_plugin.name, ) except (KeyboardInterrupt, MKTimeout): raise except Exception as exc: if on_error is OnError.RAISE: raise if on_error is OnError.WARN: console.error( f"Host label discovery of '{section_plugin.name}' failed: {exc}\n") except KeyboardInterrupt: raise MKGeneralException("Interrupted by Ctrl-C.") return host_labels
def test_multiple_sources_from_different_hosts(self, hostname, ipaddress, mode, config_cache, host_config): sources = [ ProgramSource.ds(hostname + "0", ipaddress, mode=mode, template=""), TCPSource(hostname + "1", ipaddress, mode=mode), TCPSource(hostname + "2", ipaddress, mode=mode), ] nodes = make_nodes( config_cache, host_config, ipaddress, mode=mode, sources=sources, ) broker = ParsedSectionsBroker(_collect_host_sections( nodes=nodes, file_cache_max_age=0, 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(broker) == 1 key = HostKey(hostname, ipaddress, SourceType.HOST) assert key in broker section = broker[key] assert len(section.sections) == len(sources) for source in sources: # yapf: disable assert ( section.sections[SectionName("section_name_%s" % source.hostname)] == [["section_content"]])
def test_get_section_content(hostname, host_entries, cluster_node_keys, expected_result): parsed_sections_broker = ParsedSectionsBroker({ HostKey(nodename, "127.0.0.1", SourceType.HOST): SectionsParser(host_sections=AgentHostSections( sections={SectionName("section_plugin_name"): node_section_content})) for nodename, node_section_content in host_entries }) mhs = _MultiHostSections(parsed_sections_broker) section_content = mhs.get_section_content( HostKey(hostname, "127.0.0.1", SourceType.HOST), 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), 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), 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 _find_mgmt_candidates( broker: ParsedSectionsBroker, preliminary_candidates: List[checking_classes.CheckPlugin], parsed_sections_of_interest: Set[ParsedSectionName], ) -> Set[CheckPluginName]: available_parsed_sections = { s.parsed_section_name for s in broker.determine_applicable_sections( parsed_sections_of_interest, SourceType.MANAGEMENT, ) } return { # *create* all management only names of the plugins plugin.name.create_management_name() for plugin in preliminary_candidates if any(section in available_parsed_sections for section in plugin.sections) }
def test_one_snmp_source(self, hostname, ipaddress, mode, config_cache, host_config): broker = ParsedSectionsBroker( _collect_host_sections( nodes=make_nodes( config_cache, host_config, ipaddress, mode=mode, sources=[ SNMPSource.snmp( hostname, ipaddress, mode=mode, selected_sections=NO_SELECTION, force_cache_refresh=False, on_scan_error="raise", ), ], ), file_cache_max_age=0, fetcher_messages=[ FetcherMessage.from_raw_data( result.OK({}), Snapshot.null(), FetcherType.SNMP, ), ], selected_sections=NO_SELECTION, )[0]) 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_get_section_cluster_kwargs(required_sections, expected_result): node1_sections = AgentHostSections( sections={ SectionName("one"): NODE_1, SectionName("two"): NODE_1, SectionName("three"): NODE_1 }) node2_sections = AgentHostSections(sections={ SectionName("two"): NODE_2, SectionName("three"): NODE_2, }) parsed_sections_broker = ParsedSectionsBroker({ HostKey("node1", "127.0.0.1", SourceType.HOST): ( ParsedSectionsResolver(section_plugins=[ SECTION_ONE, SECTION_TWO, SECTION_THREE, SECTION_FOUR ], ), SectionsParser(host_sections=node1_sections), ), HostKey("node2", "127.0.0.1", SourceType.HOST): ( ParsedSectionsResolver(section_plugins=[ SECTION_ONE, SECTION_TWO, SECTION_THREE, SECTION_FOUR ], ), SectionsParser(host_sections=node2_sections), ), }) kwargs = get_section_cluster_kwargs( parsed_sections_broker, [ 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
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(_collect_host_sections( nodes=made_nodes, file_cache_max_age=0, 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, )[0]) 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__find_candidates(): broker = ParsedSectionsBroker({ # we just care about the keys here, content set to arbitrary values that can be parsed. # section names are chosen arbitrarily. HostKey("test_node", "1.2.3.4", SourceType.HOST): SectionsParser( host_sections=AgentHostSections({ SectionName("kernel"): [], # host only SectionName("uptime"): [['123']], # host & mgmt }), ), HostKey("test_node", "1.2.3.4", SourceType.MANAGEMENT): SectionsParser( host_sections=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_plugin_names=EVERYTHING, ) == { 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"), }
def get_aggregated_result( parsed_sections_broker: ParsedSectionsBroker, host_config: config.HostConfig, ipaddress: Optional[HostAddress], service: Service, plugin: Optional[checking_classes.CheckPlugin], params_function: Callable[[], Parameters], *, value_store_manager: item_state.ValueStoreManager, ) -> AggregatedResult: """Run the check function and aggregate the subresults This function is also called during discovery. """ if plugin is None: return AggregatedResult( submit=True, data_received=True, result=CHECK_NOT_IMPLEMENTED, cache_info=None, ) check_function = (plugin.cluster_check_function if host_config.is_cluster else plugin.check_function) source_type = (SourceType.MANAGEMENT if service.check_plugin_name.is_management_name() else SourceType.HOST) config_cache = config.get_config_cache() kwargs = {} try: kwargs = parsed_sections_broker.get_section_cluster_kwargs( config_cache.get_clustered_service_node_keys( host_config.hostname, source_type, service.description, ) or [], plugin.sections, ) if host_config.is_cluster else parsed_sections_broker.get_section_kwargs( HostKey(host_config.hostname, ipaddress, source_type), plugin.sections, ) if not kwargs and not service.check_plugin_name.is_management_name(): # in 1.6 some plugins where discovered for management boards, but with # the regular host plugins name. In this case retry with the source type # forced to MANAGEMENT: kwargs = parsed_sections_broker.get_section_cluster_kwargs( config_cache.get_clustered_service_node_keys( host_config.hostname, SourceType.MANAGEMENT, service.description, ) or [], plugin.sections, ) if host_config.is_cluster else parsed_sections_broker.get_section_kwargs( HostKey(host_config.hostname, ipaddress, SourceType.MANAGEMENT), plugin.sections, ) if not kwargs: # no data found return AggregatedResult( submit=False, data_received=False, result=RECEIVED_NO_DATA, cache_info=None, ) if service.item is not None: kwargs["item"] = service.item if plugin.check_default_parameters is not None: kwargs["params"] = params_function() with plugin_contexts.current_host(host_config.hostname), \ plugin_contexts.current_service(service), \ value_store_manager.namespace(service.id()): result = _aggregate_results(check_function(**kwargs)) except (item_state.MKCounterWrapped, checking_classes.IgnoreResultsError) as e: msg = str(e) or "No service summary available" return AggregatedResult( submit=False, data_received=True, result=(0, msg, []), cache_info=None, ) except MKTimeout: raise except Exception: if cmk.utils.debug.enabled(): raise table = check_table.get_check_table(host_config.hostname, skip_autochecks=True) result = 3, cmk.base.crash_reporting.create_check_crash_dump( host_name=host_config.hostname, service_name=service.description, plugin_name=service.check_plugin_name, plugin_kwargs=kwargs, is_manual=service.id() in table, ), [] return AggregatedResult( submit=True, data_received=True, result=result, cache_info=parsed_sections_broker.get_cache_info(plugin.sections), )
def _realhost_scenario(monkeypatch): hostname = "test-realhost" ipaddress = "1.2.3.4" ts = Scenario().add_host(hostname, ipaddress=ipaddress) 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.apply(monkeypatch) def fake_lookup_ip_address(*_a, **_kw): return ipaddress monkeypatch.setattr(ip_lookup, "lookup_ip_address", fake_lookup_ip_address) DiscoveredHostLabelsStore(hostname).save({ 'existing_label': { 'plugin_name': 'foo', 'value': 'bar', }, 'another_label': { 'plugin_name': 'labels', 'value': 'true', } }) broker = ParsedSectionsBroker({ HostKey(hostname=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', ], ], }) }) return RealHostScenario(hostname, ipaddress, broker)
def check_discovery( host_name: HostName, ipaddress: Optional[HostAddress], *, # The next argument *must* remain optional for the DiscoCheckExecutor. # See Also: `cmk.base.checking.do_check()`. fetcher_messages: Sequence[FetcherMessage] = (), ) -> Tuple[int, List[str], List[str], List[Tuple]]: # Note: '--cache' is set in core_cmc, nagios template or even on CL and means: # 1. use caches as default: # - Set FileCacheFactory.maybe = True (set max_cachefile_age, else 0) # - Set FileCacheFactory.use_outdated = True # 2. Then these settings are used to read cache file or not config_cache = config.get_config_cache() host_config = config_cache.get_host_config(host_name) discovery_parameters = DiscoveryParameters( on_error="raise", load_labels=True, save_labels=False, only_host_labels=False, ) params = host_config.discovery_check_parameters if params is None: params = host_config.default_discovery_check_parameters() # In case of keepalive discovery we always have an ipaddress. When called as non keepalive # ipaddress is always None if ipaddress is None and not host_config.is_cluster: ipaddress = config.lookup_ip_address(host_config) nodes = sources.make_nodes( config_cache, host_config, ipaddress, Mode.DISCOVERY, sources.make_sources(host_config, ipaddress, mode=Mode.DISCOVERY), ) use_caches = cmk.core_helpers.cache.FileCacheFactory.maybe max_cachefile_age = config.discovery_max_cachefile_age() if use_caches else 0 if not fetcher_messages: # Note: *Not* calling `fetch_all(sources)` here is probably buggy. # Also See: `cmk.base.checking.do_check()` fetcher_messages = list( sources.fetch_all( nodes, max_cachefile_age=max_cachefile_age, host_config=host_config, )) parsed_sections_broker = ParsedSectionsBroker() result = sources.update_host_sections( parsed_sections_broker, nodes, max_cachefile_age=max_cachefile_age, host_config=host_config, fetcher_messages=fetcher_messages, selected_sections=NO_SELECTION, ) services, host_label_discovery_result = _get_host_services( host_config, ipaddress, parsed_sections_broker, discovery_parameters, ) status, infotexts, long_infotexts, perfdata, need_rediscovery = _aggregate_subresults( _check_service_lists(host_name, services, params), _check_host_labels(host_label_discovery_result, params), _check_data_sources(result), ) if need_rediscovery: if host_config.is_cluster and host_config.nodes: for nodename in host_config.nodes: _set_rediscovery_flag(nodename) else: _set_rediscovery_flag(host_name) infotexts.append(u"rediscovery scheduled") return status, infotexts, long_infotexts, perfdata
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 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, mode=mode, sources=make_sources(host_config, address, mode=mode) ) broker = ParsedSectionsBroker(_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(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 get_aggregated_result( parsed_sections_broker: ParsedSectionsBroker, host_config: config.HostConfig, ipaddress: Optional[HostAddress], service: Service, plugin: Optional[checking_classes.CheckPlugin], # missleading. These are prams that *may* be *partially* time specific timespecific_parameters: LegacyCheckParameters, *, value_store_manager: value_store.ValueStoreManager, persist_value_store_changes: bool, ) -> AggregatedResult: """Run the check function and aggregate the subresults This function is also called during discovery. """ if plugin is None: return AggregatedResult( submit=True, data_received=True, result=CHECK_NOT_IMPLEMENTED, cache_info=None, ) config_cache = config.get_config_cache() check_function = ( _cluster_modes.get_cluster_check_function( *config_cache.get_clustered_service_configuration( host_config.hostname, service.description, ), plugin=plugin, service_id=service.id(), persist_value_store_changes=persist_value_store_changes, ) if host_config.is_cluster else plugin.check_function ) source_type = ( SourceType.MANAGEMENT if service.check_plugin_name.is_management_name() else SourceType.HOST ) try: kwargs = ( get_section_cluster_kwargs( parsed_sections_broker, config_cache.get_clustered_service_node_keys( host_config.hostname, source_type, service.description, ) or [], plugin.sections, ) if host_config.is_cluster else get_section_kwargs( parsed_sections_broker, HostKey(host_config.hostname, ipaddress, source_type), plugin.sections, ) ) if not kwargs and not service.check_plugin_name.is_management_name(): # in 1.6 some plugins where discovered for management boards, but with # the regular host plugins name. In this case retry with the source type # forced to MANAGEMENT: kwargs = ( get_section_cluster_kwargs( parsed_sections_broker, config_cache.get_clustered_service_node_keys( host_config.hostname, SourceType.MANAGEMENT, service.description, ) or [], plugin.sections, ) if host_config.is_cluster else get_section_kwargs( parsed_sections_broker, HostKey(host_config.hostname, ipaddress, SourceType.MANAGEMENT), plugin.sections, ) ) if not kwargs: # no data found return AggregatedResult( submit=False, data_received=False, result=RECEIVED_NO_DATA, cache_info=None, ) kwargs = { **kwargs, **({} if service.item is None else {"item": service.item}), **( {} if plugin.check_default_parameters is None else {"params": _final_read_only_check_parameters(timespecific_parameters)} ), } with plugin_contexts.current_host(host_config.hostname), plugin_contexts.current_service( service ), value_store_manager.namespace(service.id()): result = _aggregate_results(check_function(**kwargs)) except (item_state.MKCounterWrapped, checking_classes.IgnoreResultsError) as e: msg = str(e) or "No service summary available" return AggregatedResult( submit=False, data_received=True, result=(0, msg, []), cache_info=None, ) except MKTimeout: raise except Exception: if cmk.utils.debug.enabled(): raise table = check_table.get_check_table(host_config.hostname, skip_autochecks=True) result = ( 3, cmk.base.crash_reporting.create_check_crash_dump( host_name=host_config.hostname, service_name=service.description, plugin_name=service.check_plugin_name, plugin_kwargs=globals().get("kwargs", {}), is_manual=service.id() in table, ), [], ) return AggregatedResult( submit=True, data_received=True, result=result, cache_info=parsed_sections_broker.get_cache_info(plugin.sections), )
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, dry_run: bool = False, 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 = config.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, dry_run=dry_run, 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.summarize(host_sections) if source_output != "": status = max(status, source_state) infotexts.append("[%s] %s" % (source.id, source_output)) 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: _submit_to_core.finalize()
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(*_a, **_kw): 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({ HostKey(hostname=node1_hostname, ipaddress=ipaddress, source_type=SourceType.HOST): SectionsParser(host_sections=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): SectionsParser(host_sections=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 _discover_host_labels_for_source_type( *, host_key: HostKey, parsed_sections_broker: ParsedSectionsBroker, discovery_parameters: DiscoveryParameters, ) -> Mapping[str, HostLabel]: try: host_data = parsed_sections_broker[host_key] except KeyError: return {} host_labels = {} try: # We do *not* process all available raw sections. Instead we see which *parsed* # sections would result from them, and then process those. parse_sections = { agent_based_register.get_section_plugin(rs).parsed_section_name for rs in host_data.sections } applicable_sections = parsed_sections_broker.determine_applicable_sections( parse_sections, host_key.source_type, ) console.vverbose("Trying host label discovery with: %s\n" % ", ".join(str(s.name) for s in applicable_sections)) for section_plugin in _sort_sections_by_label_priority( applicable_sections): kwargs = { 'section': parsed_sections_broker.get_parsed_section( host_key, section_plugin.parsed_section_name), } host_label_params = config.get_host_label_parameters( host_key.hostname, section_plugin) if host_label_params is not None: kwargs["params"] = host_label_params try: for label in section_plugin.host_label_function(**kwargs): console.vverbose( f" {label.name}: {label.value} ({section_plugin.name})\n" ) host_labels[label.name] = HostLabel( label.name, label.value, section_plugin.name, ) except (KeyboardInterrupt, MKTimeout): raise except Exception as exc: if cmk.utils.debug.enabled( ) or discovery_parameters.on_error == "raise": raise if discovery_parameters.on_error == "warn": console.error("Host label discovery of '%s' failed: %s\n" % (section_plugin.name, exc)) except KeyboardInterrupt: raise MKGeneralException("Interrupted by Ctrl-C.") return host_labels
def get_check_preview( *, host_name: HostName, max_cachefile_age: int, use_cached_snmp_data: bool, on_error: str, ) -> Tuple[CheckPreviewTable, DiscoveredHostLabels]: """Get the list of service of a host or cluster and guess the current state of all services if possible""" config_cache = config.get_config_cache() host_config = config_cache.get_host_config(host_name) ip_address = None if host_config.is_cluster else config.lookup_ip_address(host_config) discovery_parameters = DiscoveryParameters( on_error=on_error, load_labels=False, save_labels=False, only_host_labels=False, ) _set_cache_opts_of_checkers(use_cached_snmp_data=use_cached_snmp_data) nodes = sources.make_nodes( config_cache, host_config, ip_address, Mode.DISCOVERY, sources.make_sources( host_config, ip_address, mode=Mode.DISCOVERY, on_scan_error=on_error, )) parsed_sections_broker = ParsedSectionsBroker() sources.update_host_sections( parsed_sections_broker, nodes, max_cachefile_age=max_cachefile_age, host_config=host_config, fetcher_messages=list( sources.fetch_all( nodes, max_cachefile_age=max_cachefile_age, host_config=host_config, )), selected_sections=NO_SELECTION, ) grouped_services, host_label_result = _get_host_services( host_config, ip_address, parsed_sections_broker, discovery_parameters, ) table: CheckPreviewTable = [] for check_source, services_with_nodes in grouped_services.items(): for service, found_on_nodes in services_with_nodes: plugin = agent_based_register.get_check_plugin(service.check_plugin_name) params = _preview_params(host_name, service, plugin, check_source) if check_source in ['legacy', 'active', 'custom']: exitcode = None output = u"WAITING - %s check, cannot be done offline" % check_source.title() ruleset_name: Optional[RulesetName] = None else: ruleset_name = (str(plugin.check_ruleset_name) if plugin and plugin.check_ruleset_name else None) wrapped_params = (Parameters(wrap_parameters(params)) if plugin and plugin.check_default_parameters is not None else None) exitcode, output, _perfdata = checking.get_aggregated_result( parsed_sections_broker, host_config, ip_address, service, plugin, lambda p=wrapped_params: p, # type: ignore[misc] # "type of lambda" ).result # Service discovery never uses the perfdata in the check table. That entry # is constantly discarded, yet passed around(back and forth) as part of the # discovery result in the request elements. Some perfdata VALUES are not parsable # by ast.literal_eval such as "inf" it lead to ValueErrors. Thus keep perfdata empty perfdata: List[MetricTuple] = [] table.append(( _preview_check_source(host_name, service, check_source), str(service.check_plugin_name), ruleset_name, service.item, service.parameters, params, service.description, exitcode, output, perfdata, service.service_labels.to_dict(), found_on_nodes, )) return table, DiscoveredHostLabels( *{ # TODO (mo): According to unit tests, this is what was done prior to refactoring. # Im not sure this is desired. If it is, it should be explained. **{l.name: l for l in host_label_result.vanished}, **{l.name: l for l in host_label_result.present}, }.values())
def get_aggregated_result( parsed_sections_broker: ParsedSectionsBroker, host_config: config.HostConfig, ipaddress: Optional[HostAddress], service: Service, plugin: Optional[checking_classes.CheckPlugin], timespecific_parameters: Union[LegacyCheckParameters, TimespecificParameters], *, value_store_manager: value_store.ValueStoreManager, persist_value_store_changes: bool, ) -> AggregatedResult: """Run the check function and aggregate the subresults This function is also called during discovery. """ if plugin is None: return AggregatedResult( submit=True, data_received=True, result=ServiceCheckResult.check_not_implemented(), cache_info=None, ) config_cache = config.get_config_cache() check_function = (_cluster_modes.get_cluster_check_function( *config_cache.get_clustered_service_configuration( host_config.hostname, service.description, ), plugin=plugin, service_id=service.id(), persist_value_store_changes=persist_value_store_changes, ) if host_config.is_cluster else plugin.check_function) section_kws, error_result = _get_monitoring_data_kwargs_handle_pre20_services( parsed_sections_broker, host_config, config_cache, ipaddress, service, plugin.sections, ) if not section_kws: # no data found return AggregatedResult( submit=False, data_received=False, result=error_result, cache_info=None, ) item_kw = {} if service.item is None else {"item": service.item} params_kw = ({} if plugin.check_default_parameters is None else { "params": _final_read_only_check_parameters(timespecific_parameters) }) try: with plugin_contexts.current_host( host_config.hostname), plugin_contexts.current_service( service), value_store_manager.namespace(service.id()): result = _aggregate_results( check_function( **item_kw, **params_kw, **section_kws, )) except (item_state.MKCounterWrapped, checking_classes.IgnoreResultsError) as e: msg = str(e) or "No service summary available" return AggregatedResult( submit=False, data_received=True, result=ServiceCheckResult(output=msg), cache_info=None, ) except MKTimeout: raise except Exception: if cmk.utils.debug.enabled(): raise table = check_table.get_check_table(host_config.hostname, skip_autochecks=True) result = ServiceCheckResult( 3, cmk.base.crash_reporting.create_check_crash_dump( host_name=host_config.hostname, service_name=service.description, plugin_name=service.check_plugin_name, plugin_kwargs={ **item_kw, **params_kw, **section_kws }, is_manual=service.id() in table, ), ) return AggregatedResult( submit=True, data_received=True, result=result, cache_info=parsed_sections_broker.get_cache_info(plugin.sections), )
def do_discovery( arg_hostnames: Set[HostName], *, selected_sections: SectionNameCollection, run_only_plugin_names: Optional[Set[CheckPluginName]], arg_only_new: bool, only_host_labels: bool = False, ) -> None: config_cache = config.get_config_cache() use_caches = not arg_hostnames or cmk.core_helpers.cache.FileCacheFactory.maybe on_error = "raise" if cmk.utils.debug.enabled() else "warn" discovery_parameters = DiscoveryParameters( on_error=on_error, load_labels=arg_only_new, save_labels=True, only_host_labels=only_host_labels, ) host_names = _preprocess_hostnames(arg_hostnames, config_cache, only_host_labels) mode = Mode.DISCOVERY if selected_sections is NO_SELECTION else Mode.FORCE_SECTIONS # Now loop through all hosts for host_name in sorted(host_names): host_config = config_cache.get_host_config(host_name) section.section_begin(host_name) try: ipaddress = config.lookup_ip_address(host_config) nodes = sources.make_nodes( config_cache, host_config, ipaddress, mode, sources.make_sources( host_config, ipaddress, mode=mode, selected_sections=selected_sections, on_scan_error=on_error, ), ) max_cachefile_age = config.discovery_max_cachefile_age() if use_caches else 0 parsed_sections_broker = ParsedSectionsBroker() sources.update_host_sections( parsed_sections_broker, nodes, max_cachefile_age=max_cachefile_age, host_config=host_config, fetcher_messages=list( sources.fetch_all( nodes, max_cachefile_age=max_cachefile_age, host_config=host_config, )), selected_sections=selected_sections, ) _do_discovery_for( host_name, ipaddress, parsed_sections_broker, run_only_plugin_names, arg_only_new, discovery_parameters, ) except Exception as e: if cmk.utils.debug.enabled(): raise section.section_error("%s" % e) finally: cmk.utils.cleanup.cleanup_globals()
def _do_inv_for_realhost( host_config: config.HostConfig, ipaddress: Optional[HostAddress], *, parsed_sections_broker: ParsedSectionsBroker, run_plugin_names: Container[InventoryPluginName], retentions_tracker: RetentionsTracker, ) -> InventoryTrees: tree_aggregator = TreeAggregator() _set_cluster_property(tree_aggregator.trees.inventory, host_config) section.section_step("Executing inventory plugins") for inventory_plugin in agent_based_register.iter_all_inventory_plugins(): if inventory_plugin.name not in run_plugin_names: continue for source_type in (SourceType.HOST, SourceType.MANAGEMENT): kwargs = get_section_kwargs( parsed_sections_broker, HostKey(host_config.hostname, ipaddress, source_type), inventory_plugin.sections, ) if not kwargs: console.vverbose( " %s%s%s%s: skipped (no data)\n", tty.yellow, tty.bold, inventory_plugin.name, tty.normal, ) continue # Inventory functions can optionally have a second argument: parameters. # These are configured via rule sets (much like check parameters). if inventory_plugin.inventory_ruleset_name is not None: kwargs = { **kwargs, "params": host_config.inventory_parameters( inventory_plugin.inventory_ruleset_name), } exception = tree_aggregator.aggregate_results( inventory_generator=inventory_plugin.inventory_function( **kwargs), retentions_tracker=retentions_tracker, raw_cache_info=parsed_sections_broker.get_cache_info( inventory_plugin.sections), is_legacy_plugin=inventory_plugin.module is None, ) if exception: console.warning( " %s%s%s%s: failed: %s", tty.red, tty.bold, inventory_plugin.name, tty.normal, exception, ) else: console.verbose(" %s%s%s%s", tty.green, tty.bold, inventory_plugin.name, tty.normal) console.vverbose(": ok\n") console.verbose("\n") return tree_aggregator.trees
def discover_on_host( *, config_cache: config.ConfigCache, host_config: config.HostConfig, mode: str, service_filters: Optional[_ServiceFilters], on_error: str, use_cached_snmp_data: bool, max_cachefile_age: int, ) -> DiscoveryResult: console.verbose(" Doing discovery with mode '%s'...\n" % mode) host_name = host_config.hostname result = DiscoveryResult() discovery_parameters = DiscoveryParameters( on_error=on_error, load_labels=(mode != "remove"), save_labels=(mode != "remove"), only_host_labels=(mode == "only-host-labels"), ) if host_name not in config_cache.all_active_hosts(): result.error_text = "" return result _set_cache_opts_of_checkers(use_cached_snmp_data=use_cached_snmp_data) try: # in "refresh" mode we first need to remove all previously discovered # checks of the host, so that _get_host_services() does show us the # new discovered check parameters. if mode == "refresh": result.self_removed += host_config.remove_autochecks() # this is cluster-aware! if host_config.is_cluster: ipaddress = None else: ipaddress = config.lookup_ip_address(host_config) nodes = sources.make_nodes( config_cache, host_config, ipaddress, Mode.DISCOVERY, sources.make_sources( host_config, ipaddress, mode=Mode.DISCOVERY, on_scan_error=on_error, ), ) parsed_sections_broker = ParsedSectionsBroker() sources.update_host_sections( parsed_sections_broker, nodes, max_cachefile_age=max_cachefile_age, host_config=host_config, fetcher_messages=list( sources.fetch_all( nodes, max_cachefile_age=max_cachefile_age, host_config=host_config, )), selected_sections=NO_SELECTION, ) # Compute current state of new and existing checks services, host_labels = _get_host_services( host_config, ipaddress, parsed_sections_broker, discovery_parameters, ) old_services = services.get("old", []) # Create new list of checks new_services = _get_post_discovery_services(host_name, services, service_filters or _ServiceFilters.accept_all(), result, mode) host_config.set_autochecks(new_services) result.diff_text = make_object_diff( _make_services_audit_log_object([x.service for x in old_services]), _make_services_audit_log_object([x.service for x in new_services])) except MKTimeout: raise # let general timeout through except Exception as e: if cmk.utils.debug.enabled(): raise result.error_text = str(e) else: if mode != "remove": result.self_new_host_labels = len(host_labels.new) result.self_total_host_labels = len(host_labels.present) result.self_total = result.self_new + result.self_kept return result