def test_create_agent_section_plugin(): with pytest.raises(NotImplementedError): plugin = section_plugins.create_agent_section_plugin( name="norris", parsed_section_name="chuck", parse_function=_parse_dummy, supersedes=None, forbidden_names=[], ) with pytest.raises(NotImplementedError): plugin = section_plugins.create_agent_section_plugin( name="norris", parsed_section_name=None, parse_function=_parse_dummy, supersedes=["Foo", "Bar"], forbidden_names=[], ) plugin = section_plugins.create_agent_section_plugin( name="norris", parsed_section_name=None, # "chuck" parse_function=_parse_dummy, supersedes=None, # ["Foo", "Bar"], forbidden_names=[], ) assert isinstance(plugin, section_types.AgentSectionPlugin) assert len(plugin) == 5 assert plugin.name == PluginName("norris") assert plugin.parsed_section_name == PluginName("norris") # "chuck") assert plugin.parse_function is _parse_dummy assert plugin.host_label_function is section_plugins._noop_host_label_function assert plugin.supersedes == [] # [PluginName("Bar"), PluginName("Foo")]
def create_snmp_section_plugin( *, name, parsed_section_name=None, parse_function, host_label_function=None, supercedes=None, detect_spec, trees, forbidden_names, ): # type: (str, Optional[str], SNMPParseFunction, Optional[HostLabelFunction], Optional[List[str]], SNMPDetectSpec, List[SNMPTree], List[PluginName]) -> SNMPSectionPlugin """Return an SNMPSectionPlugin object after validating and converting the arguments one by one""" plugin_name = PluginName(name, forbidden_names) _validate_parse_function(parse_function) _validate_detect_spec(detect_spec) return SNMPSectionPlugin( plugin_name, PluginName(parsed_section_name) if parsed_section_name else plugin_name, parse_function, _create_host_label_function(host_label_function), _create_supercedes(supercedes), detect_spec, trees, )
def create_agent_section_plugin( #*, name=None, # type: Optional[str] parsed_section_name=None, # type: Optional[str] parse_function=None, # type: Optional[AgentParseFunction] host_label_function=None, # type: Optional[HostLabelFunction] supersedes=None, # type: Optional[List[str]] forbidden_names=None, # type: Optional[List[PluginName]] ): # type: (...) -> AgentSectionPlugin """Return an AgentSectionPlugin 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. """ # TODO (mo): unhack this CMK-3983 if (name is None or parse_function is None or forbidden_names is None): raise TypeError() # TODO (mo): Well, implement it, and remove pragma below! if supersedes is not None: raise NotImplementedError("supersedes is not yet available") if parsed_section_name is not None: raise NotImplementedError("parsed_section_name is not yet available") plugin_name = PluginName(name, forbidden_names=forbidden_names) _validate_parse_function(parse_function) return AgentSectionPlugin( plugin_name, PluginName(parsed_section_name) if parsed_section_name else plugin_name, # type: ignore parse_function, _create_host_label_function(host_label_function), _create_supersedes(supersedes), )
def test_validate_supersedings(): supersedes = [ PluginName("foo"), PluginName("bar"), PluginName("foo"), ] with pytest.raises(ValueError, match="duplicate"): section_plugins._validate_supersedings(supersedes)
def test_create_snmp_section_plugin(): trees = [ section_types.SNMPTree( base='.1.2.3', oids=[OIDEnd(), '2.3'], ), ] # type: List[ABCSNMPTree] detect = [ [('.1.2.3.4.5', 'Foo.*', True)], ] with pytest.raises(NotImplementedError): plugin = section_plugins.create_snmp_section_plugin( name="norris", parsed_section_name="chuck", parse_function=_parse_dummy, trees=trees, detect_spec=detect, supersedes=None, forbidden_names=[], ) with pytest.raises(NotImplementedError): plugin = section_plugins.create_snmp_section_plugin( name="norris", parsed_section_name=None, parse_function=_parse_dummy, trees=trees, detect_spec=detect, supersedes=["Foo", "Bar"], forbidden_names=[], ) plugin = section_plugins.create_snmp_section_plugin( name="norris", parsed_section_name=None, # "chuck", parse_function=_parse_dummy, trees=trees, detect_spec=detect, supersedes=None, # ["Foo", "Bar"], forbidden_names=[], ) assert isinstance(plugin, section_types.SNMPSectionPlugin) assert len(plugin) == 7 assert plugin.name == PluginName("norris") assert plugin.parsed_section_name == PluginName("norris") # "chuck") assert plugin.parse_function is _parse_dummy assert plugin.host_label_function is section_plugins._noop_host_label_function assert plugin.detect_spec == detect assert plugin.trees == trees assert plugin.supersedes == [] # [PluginName("Bar"), PluginName("Foo")]
def test_create_check_plugin(): plugin = check_plugins.create_check_plugin(**MINIMAL_CREATION_KWARGS) assert plugin.name == PluginName(MINIMAL_CREATION_KWARGS["name"]) assert plugin.sections == [PluginName(MINIMAL_CREATION_KWARGS["name"])] assert plugin.service_name == MINIMAL_CREATION_KWARGS["service_name"] assert plugin.management_board is None assert plugin.discovery_function is MINIMAL_CREATION_KWARGS[ "discovery_function"] assert plugin.discovery_default_parameters is None assert plugin.discovery_ruleset_name is None assert plugin.check_function is MINIMAL_CREATION_KWARGS["check_function"] assert plugin.check_default_parameters is None assert plugin.check_ruleset_name is None assert plugin.cluster_check_function is None
def test_get_section_cluster_kwargs(monkeypatch, required_sections, expected_result): _set_up(monkeypatch, "cluster", ["node2", "node1"], { "node1": "cluster", "node2": "cluster" }) node1_section_content = { "one": NODE_1, # TODO (mo): CMK-4232 # "two": NODE_1, "three": NODE_1 } node2_section_content = {"two": NODE_2, "three": NODE_2} multi_host_sections = MultiHostSections() multi_host_sections.setdefault_host_sections( ("node1", "127.0.0.1"), AgentHostSections(sections=node1_section_content), ) multi_host_sections.setdefault_host_sections( ("node2", "127.0.0.1"), AgentHostSections(sections=node2_section_content), ) kwargs = multi_host_sections.get_section_cluster_kwargs( "cluster", [PluginName(n) for n in required_sections], "_service_description") assert expected_result == kwargs,\ "Section content: Expected '%s' but got '%s'" % (expected_result, kwargs)
def test_get_section_kwargs(monkeypatch, required_sections, expected_result): _set_up(monkeypatch, "node1", None, {}) node_section_content = { "one": NODE_1, # TODO (mo): CMK-4232 # "two": NODE_1, "three": NODE_1 } host_key = ("node1", "127.0.0.1", SourceType.HOST) multi_host_sections = MultiHostSections() multi_host_sections.setdefault_host_sections( host_key, AgentHostSections(sections=node_section_content), ) kwargs = multi_host_sections.get_section_kwargs( host_key, [PluginName(n) for n in required_sections], ) assert expected_result == kwargs,\ "Section content: Expected '%s' but got '%s'" % (expected_result, kwargs)
def _extract_snmp_sections(): # type: () -> None for plugin_name, plugin_info in sorted(inv_info.items()): if 'snmp_info' not in plugin_info: continue section_name = section_name_of(plugin_name) if config.get_registered_section_plugin(PluginName(section_name)): continue fallback_files = ( [_include_file_path(i) for i in plugin_info.get('includes', [])] + [_plugin_file_lookup[plugin_name]]) try: snmp_section_plugin = create_snmp_section_plugin_from_legacy( section_name, {}, plugin_info['snmp_scan_function'], plugin_info['snmp_info'], scan_function_fallback_files=fallback_files, ) except (NotImplementedError, KeyError, AssertionError, ValueError): msg = config.AUTO_MIGRATION_ERR_MSG % ('section', plugin_name) if cmk.utils.debug.enabled(): raise MKGeneralException(msg) # TODO (mo): bring this back: #console.warning(msg) else: config.registered_snmp_sections[ snmp_section_plugin.name] = snmp_section_plugin
def _make_oid_infos(self): # type: () -> Dict[str, List[ABCSNMPTree]] oid_infos = {} # Dict[str, List[ABCSNMPTree]] raw_sections_to_process = { PluginName(n) for n in self._get_raw_section_names_to_process() } for section_name in self._sort_section_names(raw_sections_to_process): plugin = config.registered_snmp_sections.get(section_name) if plugin is None: self._logger.debug("%s: No such section definiton", section_name) continue if section_name in self._fetched_raw_section_names: continue # This checks data is configured to be persisted (snmp_check_interval) and recent enough. # Skip gathering new data here. The persisted data will be added later if self._persisted_sections and str( section_name) in self._persisted_sections: self._logger.debug( "%s: Skip fetching data (persisted info exists)", section_name) continue oid_infos[str(section_name)] = plugin.trees return oid_infos
def test_create_section_plugin_from_legacy(check_info, snmp_info, migrated_agent_sections, migrated_snmp_sections): for name, check_info_dict in check_info.items(): # only test main checks if name != check_utils.section_name_of(name): continue section_name = PluginName(name) with known_exceptions('section', name): section = migrated_agent_sections.get(section_name) if section is not None: assert isinstance(section, AgentSectionPlugin) else: section = migrated_snmp_sections.get(section_name) if section is None: raise NotImplementedError(name) assert isinstance(section, SNMPSectionPlugin) if section is None: continue original_parse_function = check_info_dict["parse_function"] if original_parse_function is not None: assert original_parse_function.__name__ == section.parse_function.__name__
def get_check_plugin_candidates(self): # type: () -> Set[PluginName] """Return names of check plugins that this multi_host_section may contain data for. Given this mutli_host_section, there is no point in trying to discover any check plugins not returned by this function. This does not address the question wether or not the returned check plugins will discover something. """ raw_section_names = { PluginName(name) for node_data in self._multi_host_sections.values() for name in node_data.sections } raw_sections = [(name, config.get_registered_section_plugin(name)) for name in raw_section_names] parsed_section_names = { name if section is None else section.parsed_section_name for name, section in raw_sections } return { plugin.name for plugin in config.registered_check_plugins.values() if any( section in parsed_section_names for section in plugin.sections) }
def test_value_store(): store = value_store.get_value_store() with pytest.raises(MKGeneralException): store["foo"] = 42 saved_prefix = value_store.get_item_state_prefix() with value_store.context(PluginName("plugin"), "item"): assert len(store) == 0 assert not store assert "foo" not in store assert store.get("foo") is None with pytest.raises(KeyError): _ = store["foo"] with pytest.raises(TypeError): store[2] = "key must be string" # type: ignore[index] store["foo"] = 42 store["bar"] = 23 assert set(store) == {"foo", "bar"} del store["bar"] assert "foo" in store assert len(store) == 1 assert bool(store) assert store["foo"] == 42 assert value_store.get_item_state_prefix() == saved_prefix
def test_create_check_plugin_from_legacy(): plugin = check_plugins_legacy.create_check_plugin_from_legacy( "norris", MINIMAL_CHECK_INFO, [], ) assert plugin.name == PluginName("norris") assert plugin.sections == [PluginName("norris")] assert plugin.service_name == MINIMAL_CHECK_INFO["service_description"] assert plugin.discovery_function.__name__ == 'discovery_migration_wrapper' assert plugin.discovery_default_parameters is None assert plugin.discovery_ruleset_name is None assert plugin.check_function.__name__ == 'check_migration_wrapper' assert plugin.check_default_parameters is None assert plugin.check_ruleset_name is None assert plugin.cluster_check_function.__name__ == "cluster_legacy_mode_from_hell"
def _create_sections(sections, plugin_name): # type: (Optional[List[str]], PluginName) -> List[PluginName] if sections is None: return [plugin_name] if not isinstance(sections, list): raise TypeError("[%s]: 'sections' must be a list of str, got %r" % (plugin_name, sections)) if not sections: raise ValueError("[%s]: 'sections' must not be empty" % plugin_name) return [PluginName(n) for n in sections]
def _create_supersedes(supersedes): # type: (Optional[List[str]]) -> List[PluginName] if supersedes is None: return [] supersedes_plugins = [PluginName(n) for n in supersedes] _validate_supersedings(supersedes_plugins) return sorted(supersedes_plugins)
def _sort_section_names(section_names): # type: (Set[PluginName]) -> List[PluginName] # In former Check_MK versions (<=1.4.0) CPU check plugins were # checked before other check plugins like interface checks. # In Check_MK versions >= 1.5.0 the order is random and # interface check plugins are executed before CPU check plugins. # This leads to high CPU utilization sent by device. Thus we have # to re-order the check plugin names. # There are some nested check plugin names which have to be considered, too. # for f in $(grep "service_description.*CPU [^lL]" -m1 * | cut -d":" -f1); do # if grep -q "snmp_info" $f; then echo $f; fi done cpu_sections_without_cpu_in_name = { PluginName("brocade_sys"), PluginName("bvip_util"), } return sorted(section_names, key=lambda x: (not ('cpu' in str( x) or x in cpu_sections_without_cpu_in_name), x))
def test_create_snmp_section_plugin_from_legacy(): plugin = section_plugins_legacy.create_snmp_section_plugin_from_legacy( "norris", { 'parse_function': old_school_parse_function, 'inventory_function': old_school_discover_function, }, old_school_scan_function, (".1.2.3.4.5", ["2", 3]), ) assert plugin.name == PluginName("norris") assert plugin.parsed_section_name == PluginName("norris") assert plugin.parse_function.__name__ == "old_school_parse_function" assert plugin.host_label_function.__name__ == "host_label_function" assert plugin.supersedes == [] assert plugin.detect_spec == [[(".1.2.3.4.5", "norris.*", True)]] assert plugin.trees == [SNMPTree(base=".1.2.3.4.5", oids=["2", "3"])]
def test_create_section_plugin_from_legacy(inv_info): for name, inv_info_dict in inv_info.items(): if 'snmp_info' not in inv_info_dict: continue section_name = PluginName(check_utils.section_name_of(name)) with known_exceptions(name): if section_name not in config.registered_snmp_sections: raise NotImplementedError(name) section = config.registered_snmp_sections[section_name] assert isinstance(section, SNMPSectionPlugin)
def _get_detection_spec_from_plugin_name(check_plugin_name, inv_info): # type: (CheckPluginName, Dict[str, Any]) -> Union[SNMPDetectSpec, Optional[ScanFunction]] # This function will hopefully shrink and finally disappear during API development. section_name = section_name_of(check_plugin_name) section_plugin = config.registered_snmp_sections.get(PluginName(section_name)) if section_plugin: return section_plugin.detect_spec # TODO (mo): migrate section definitions from inventory plugins to # section plugins and remove this conditional entirely info = inv_info[section_name] return info.get("snmp_scan_function")
def test_plugin_name_sort(): plugin_dict = { PluginName("Stuart"): None, PluginName("Bob"): None, PluginName("Dave"): None, } assert sorted(plugin_dict) == [PluginName("Bob"), PluginName("Dave"), PluginName("Stuart")]
def create_snmp_section_plugin( *, name, # type: str parsed_section_name=None, # type: Optional[str] parse_function, # type: SNMPParseFunction host_label_function=None, # type: Optional[HostLabelFunction] supersedes=None, # type: Optional[List[str]] detect_spec, # type: SNMPDetectSpec trees, # type: List[ABCSNMPTree] forbidden_names=None, # type: Optional[List[PluginName]] ): # type: (...) -> 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. """ # TODO (mo): Well, implement it, and remove pragma below! if supersedes is not None: raise NotImplementedError("supersedes is not yet available") if parsed_section_name is not None: raise NotImplementedError("parsed_section_name is not yet available") plugin_name = PluginName(name, forbidden_names) _validate_parse_function(parse_function) _validate_detect_spec(detect_spec) _validate_snmp_trees(trees) return SNMPSectionPlugin( plugin_name, PluginName(parsed_section_name) if parsed_section_name else plugin_name, # type: ignore parse_function, _create_host_label_function(host_label_function), _create_supersedes(supersedes), detect_spec, trees, )
def test_get_parsed_section(monkeypatch, node_section_content, expected_result): _set_up(monkeypatch, "node1", None, {}) multi_host_sections = MultiHostSections() multi_host_sections.add_or_get_host_sections( "node1", "127.0.0.1", AgentHostSections(sections=node_section_content)) content = multi_host_sections.get_parsed_section("node1", "127.0.0.1", PluginName("parsed")) assert expected_result == content,\ "Section content: Expected '%s' but got '%s'" % (expected_result, content)
def create_agent_section_plugin( *, name, parsed_section_name=None, parse_function, host_label_function=None, supercedes=None, forbidden_names, ): # type: (str, Optional[str], AgentParseFunction, Optional[HostLabelFunction], Optional[List[str]], List[PluginName]) -> AgentSectionPlugin """Return an AgentSectionPlugin object after validating and converting the arguments one by one""" plugin_name = PluginName(name, forbidden_names=forbidden_names) _validate_parse_function(parse_function) return AgentSectionPlugin( plugin_name, PluginName(parsed_section_name) if parsed_section_name else plugin_name, parse_function, _create_host_label_function(host_label_function), _create_supercedes(supercedes), )
def _update_with_parse_function(self, section_content, section_name): # type: (AbstractSectionContent, SectionName) -> ParsedSectionContent """Transform the section_content using the defined parse functions. Some checks define a parse function that is used to transform the section_content somehow. It is applied by this function. Please note that this is not a check/subcheck individual setting. This option is related to the agent section. All exceptions raised by the parse function will be catched and re-raised as MKParseFunctionError() exceptions.""" # TODO (mo): change this function to expect a PluginName as argument section_plugin_name = PluginName(section_name) section_plugin = config.get_registered_section_plugin( section_plugin_name) if section_plugin is None: # use legacy parse function for unmigrated sections parse_function = config.check_info.get(section_name, {}).get("parse_function") if parse_function is None: return section_content else: # TODO (mo): deal with the parsed_section_name feature (CMK-4006) if section_plugin.name != section_plugin.parsed_section_name: raise NotImplementedError() parse_function = section_plugin.parse_function # TODO (mo): make this unnecessary parse_function = cast( Callable[[AbstractSectionContent], ParsedSectionContent], parse_function) # TODO: Item state needs to be handled in local objects instead of the # item_state._cached_item_states object # TODO (mo): ValueStores (formally Item state) need to be *only* available # from within the check function, nowhere else. orig_item_state_prefix = item_state.get_item_state_prefix() try: item_state.set_item_state_prefix(section_name, None) return parse_function(section_content) except Exception: if cmk.utils.debug.enabled(): raise raise MKParseFunctionError(*sys.exc_info()) finally: item_state.set_item_state_prefix(*orig_item_state_prefix)
def _oid_info_from_section_name(section_name): # type: (str) -> Tuple[Union[Optional[OIDInfo], List[SNMPTree]], bool] import cmk.base.inventory_plugins # pylint: disable=import-outside-toplevel has_snmp_info = False oid_info = None # type: Optional[Union[OIDInfo, List[SNMPTree]]] snmp_section_plugin = config.registered_snmp_sections.get(PluginName(section_name)) if snmp_section_plugin: oid_info = snmp_section_plugin.trees elif section_name in cmk.base.inventory_plugins.inv_info: # TODO: merge this into config.registered_snmp_sections! oid_info = cmk.base.inventory_plugins.inv_info[section_name].get("snmp_info") if oid_info: has_snmp_info = True else: oid_info = None return oid_info, has_snmp_info
def test_get_parsed_section(monkeypatch, node_section_content, expected_result): _set_up(monkeypatch, "node1", None, {}) multi_host_sections = MultiHostSections() multi_host_sections.setdefault_host_sections( ("node1", "127.0.0.1", SourceType.HOST), AgentHostSections(sections=node_section_content), ) content = multi_host_sections.get_parsed_section( ("node1", "127.0.0.1", SourceType.HOST), PluginName("parsed"), ) assert expected_result == content,\ "Section content: Expected '%s' but got '%s'" % (expected_result, content)
def get_parsed_section( self, host_name, # type: HostName ip_address, # type: Optional[HostAddress] section_name, # type: PluginName ): # type: (...) -> Optional[ParsedSectionContent] cache_key = (host_name, ip_address, section_name) if cache_key in self._parsed_renamed_sections: return self._parsed_renamed_sections[cache_key] try: hosts_raw_sections = self._multi_host_sections[( host_name, ip_address)].sections except KeyError: return self._parsed_renamed_sections.setdefault(cache_key, None) available_raw_sections = [PluginName(n) for n in hosts_raw_sections] section_def = config.get_parsed_section_creator( section_name, available_raw_sections) if section_def is None: # no section creating the desired one was found, assume a 'default' section: raw_section_name = section_name parse_function = parse_string_table # type: Union[SNMPParseFunction, AgentParseFunction] else: raw_section_name = section_def.name parse_function = section_def.parse_function try: string_table = hosts_raw_sections[str(raw_section_name)] except KeyError: return self._parsed_renamed_sections.setdefault(cache_key, None) try: parsed = parse_function(string_table) except Exception: if cmk.utils.debug.enabled(): raise raise MKParseFunctionError(*sys.exc_info()) return self._parsed_renamed_sections.setdefault(cache_key, parsed)
def _get_raw_section(self, host_key, section_name): # type: (HostKey, PluginName) -> Union[AgentSectionPlugin, SNMPSectionPlugin] """Get the raw sections name that will be parsed into the required section Raw sections may get renamed once they are parsed, if they declare it. This function deals with the task of determining which section we need to parse, in order to end up with the desired parsed section. This depends on the available raw sections, and thus on the host. """ cache_key = host_key + (section_name,) if cache_key in self._parsed_to_raw_map: return self._parsed_to_raw_map[cache_key] try: hosts_raw_sections = self._multi_host_sections[host_key].sections except KeyError: return self._parsed_to_raw_map.setdefault(cache_key, None) available_raw_sections = [PluginName(n) for n in hosts_raw_sections] section_def = config.get_parsed_section_creator(section_name, available_raw_sections) return self._parsed_to_raw_map.setdefault(cache_key, section_def)
def execute_check(multi_host_sections, host_config, ipaddress, service): # type: (data_sources.MultiHostSections, config.HostConfig, Optional[HostAddress], Service) -> bool # TODO (mo): centralize maincheckify: CMK-4295 plugin_name = PluginName(maincheckify(service.check_plugin_name)) plugin = config.registered_check_plugins.get(plugin_name) # check if we must use legacy mode. remove this block entirely one day if (plugin is not None and host_config.is_cluster and plugin.cluster_check_function.__name__ == CLUSTER_LEGACY_MODE_FROM_HELL): return _execute_check_legacy_mode( multi_host_sections, host_config.hostname, ipaddress, service, ) submit, data_received, result = _get_aggregated_result( multi_host_sections, host_config, ipaddress, service, ) if submit: _submit_check_result( host_config.hostname, service.description, result, multi_host_sections.get_cache_info(plugin.sections) if plugin else None, ) elif data_received: console.verbose("%-20s PEND - %s\n", ensure_str(service.description), result[1]) return data_received