Module: check_mk
Branch: master
Commit: ac4ce814e9ab7e5b84273df5b32dcec0f1b3365f
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=ac4ce814e9ab7e…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Mon Apr 15 08:59:04 2019 +0200
Started implementing the ruleset matcher
* It uses the RuleMatcher() to produce a list of matching rules for
a host / service object.
* It provides several methods that interpret the matched rules in
different ways, depending on what the caller needs.
* The functions replace the existing matching functions like this:
is_matching() replaces in_binary_hostlist / in_boolean_serviceconf_list
get_merged_dict() replaces host_extra_conf_merged / service_extra_conf_merged
get_values() replaces host_extra_conf / service_extra_conf
CMK-1941
Change-Id: I7223f835a65658b495430cdf749472f4fe84ef28
---
cmk/utils/rulesets/ruleset_matcher.py | 80 +++++++++++++
.../cmk/utils/rulesets/test_ruleset_matcher.py | 130 +++++++++++++++++++++
2 files changed, 210 insertions(+)
diff --git a/cmk/utils/rulesets/ruleset_matcher.py
b/cmk/utils/rulesets/ruleset_matcher.py
new file mode 100644
index 0000000..2654f8e
--- /dev/null
+++ b/cmk/utils/rulesets/ruleset_matcher.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python2
+# -*- encoding: utf-8; py-indent-offset: 4 -*-
+# +------------------------------------------------------------------+
+# | ____ _ _ __ __ _ __ |
+# | / ___| |__ ___ ___| | __ | \/ | |/ / |
+# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
+# | | |___| | | | __/ (__| < | | | | . \ |
+# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
+# | |
+# | Copyright Mathias Kettner 2016 mk(a)mathias-kettner.de |
+# +------------------------------------------------------------------+
+#
+# This file is part of Check_MK.
+# The official homepage is at
http://mathias-kettner.de/check_mk.
+#
+# check_mk is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation in version 2. check_mk is distributed
+# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
+# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU General Public License for more de-
+# tails. You should have received a copy of the GNU General Public
+# License along with GNU Make; see the file COPYING. If not, write
+# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301 USA.
+"""This module provides generic Check_MK ruleset processing
functionality"""
+
+# TODO: Reduce different ruleset types (item vs. non-item and binary vs. value)
+# TODO: Use objects for object_spec. We already have
+# cmk_base.config.HostConfig. We would need something similar for service
+# objects
+
+from typing import List, Dict, Any # pylint: disable=unused-import
+
+from cmk.utils.rulesets.rule_matcher import RuleMatcher
+
+
+class RulesetMatcher(object):
+ def __init__(self):
+ # type: () -> None
+ super(RulesetMatcher, self).__init__()
+ self._matcher = RuleMatcher()
+
+ def is_matching(self, object_spec, ruleset):
+ # type: (Dict, List[Dict]) -> bool
+ """Compute outcome of a ruleset set that just says yes/no
+
+ The binary match only cares about the first matching rule of an object.
+ Depending on the value the outcome is negated or not.
+
+ Replaces in_binary_hostlist / in_boolean_serviceconf_list"""
+ for rule in ruleset:
+ if self._matcher.match(object_spec, rule["condition"]):
+ return rule["value"]
+ return False
+
+ def get_merged_dict(self, object_spec, ruleset):
+ # type: (Dict, List[Dict]) -> Dict
+ """Returns a dictionary of the merged dict values of the matched
rules
+ The first dict setting a key defines the final value.
+
+ Replaces host_extra_conf_merged / service_extra_conf_merged"""
+ rule_dict = {} # type: Dict
+ for value_dict in self.get_values(object_spec, ruleset):
+ for key, value in value_dict.items():
+ rule_dict.setdefault(key, value)
+ return rule_dict
+
+ def get_values(self, object_spec, ruleset):
+ # type: (Dict, List) -> List[Any]
+ """Returns a list of the values of the matched rules
+
+ Replaces host_extra_conf / service_extra_conf"""
+ return [r["value"] for r in self.get_matching_rules(object_spec,
ruleset)]
+
+ def get_matching_rules(self, object_spec, ruleset):
+ # type: (Dict, List) -> List[Dict]
+ """Filter the ruleset of this matcher for the given object and
return the filtered rule list
+ """
+ return [rule for rule in ruleset if self._matcher.match(object_spec,
rule["condition"])]
diff --git a/tests/unit/cmk/utils/rulesets/test_ruleset_matcher.py
b/tests/unit/cmk/utils/rulesets/test_ruleset_matcher.py
new file mode 100644
index 0000000..9cf519a
--- /dev/null
+++ b/tests/unit/cmk/utils/rulesets/test_ruleset_matcher.py
@@ -0,0 +1,130 @@
+# encoding: utf-8
+# pylint: disable=redefined-outer-name
+from cmk.utils.rulesets.ruleset_matcher import RulesetMatcher
+
+
+def test_basic_matching():
+ ruleset = [{"condition": {"a": "b"}},
{"condition": {"a": "c"}}]
+ matcher = RulesetMatcher()
+ assert matcher.get_matching_rules({"a": "b"}, ruleset=ruleset) ==
[ruleset[0]]
+
+
+ruleset = [
+ {
+ "value": "BLA",
+ "condition": {
+ "host_name": "host1",
+ },
+ "options": {},
+ },
+ {
+ "value": "BLUB",
+ "condition": {
+ "host_name": {
+ "$in": ["host1", "host2"]
+ },
+ },
+ "options": {},
+ },
+]
+
+
+def test_basic_host_ruleset_get_matching_rules():
+ matcher = RulesetMatcher()
+ assert matcher.get_matching_rules({"host_name": "abc"},
ruleset=ruleset) == []
+ assert matcher.get_matching_rules({"host_name": "host1"},
ruleset=ruleset) == ruleset
+ assert matcher.get_matching_rules({"host_name": "host2"},
ruleset=ruleset) == [ruleset[1]]
+
+
+def test_basic_host_ruleset_get_values():
+ matcher = RulesetMatcher()
+ assert matcher.get_values({"host_name": "abc"}, ruleset=ruleset)
== []
+ assert matcher.get_values({"host_name": "host1"},
ruleset=ruleset) == ["BLA", "BLUB"]
+ assert matcher.get_values({"host_name": "host2"},
ruleset=ruleset) == ["BLUB"]
+
+
+dict_ruleset = [
+ {
+ "value": {
+ "hu": "BLA"
+ },
+ "condition": {
+ "host_name": "host1",
+ },
+ "options": {},
+ },
+ {
+ "value": {
+ "ho": "BLA"
+ },
+ "condition": {
+ "host_name": {
+ "$in": ["host1", "host2"]
+ },
+ },
+ "options": {},
+ },
+ {
+ "value": {
+ "hu": "BLUB",
+ "he": "BLUB",
+ },
+ "condition": {
+ "host_name": {
+ "$in": ["host1", "host2"]
+ },
+ },
+ "options": {},
+ },
+]
+
+
+def test_basic_host_ruleset_get_merged_dict_values():
+ matcher = RulesetMatcher()
+ assert matcher.get_merged_dict({"host_name": "abc"},
ruleset=dict_ruleset) == {}
+ assert matcher.get_merged_dict({"host_name": "host1"},
ruleset=dict_ruleset) == {
+ "hu": "BLA",
+ "ho": "BLA",
+ "he": "BLUB",
+ }
+ assert matcher.get_merged_dict({"host_name": "host2"},
ruleset=dict_ruleset) == {
+ "hu": "BLUB",
+ "ho": "BLA",
+ "he": "BLUB",
+ }
+
+
+binary_ruleset = [
+ {
+ "value": True,
+ "condition": {
+ "host_name": "host1",
+ },
+ "options": {},
+ },
+ {
+ "value": False,
+ "condition": {
+ "host_name": {
+ "$in": ["host1", "host2"]
+ },
+ },
+ "options": {},
+ },
+ {
+ "value": True,
+ "condition": {
+ "host_name": {
+ "$in": ["host1", "host2"]
+ },
+ },
+ "options": {},
+ },
+]
+
+
+def test_basic_host_ruleset_is_matching():
+ matcher = RulesetMatcher()
+ assert matcher.is_matching({"host_name": "abc"},
ruleset=binary_ruleset) is False
+ assert matcher.is_matching({"host_name": "host1"},
ruleset=binary_ruleset) is True
+ assert matcher.is_matching({"host_name": "host2"},
ruleset=binary_ruleset) is False