Skip to content

Commit

Permalink
17275 Introduce cmk/customer host label
Browse files Browse the repository at this point in the history
The MSP edition will now create a `cmk/customer` label for each
host.

This label can be used to configure filters based on customers.

CMK-13530

Change-Id: I5e1e93f41c629d90bf4275f3124072b975374150
  • Loading branch information
loocars committed Sep 17, 2024
1 parent 9f3b3df commit 28df865
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 19 deletions.
17 changes: 17 additions & 0 deletions .werks/17275.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[//]: # (werk v2)
# Introduce cmk/customer host label

key | value
---------- | ---
date | 2024-09-05T13:06:17+00:00
version | 2.4.0b1
class | feature
edition | cme
component | multisite
level | 1
compatible | yes

The MSP edition will now create a `cmk/customer` label for each
host.

This label can be used to configure filters based on customers.
33 changes: 26 additions & 7 deletions cmk/base/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from enum import Enum
from importlib.util import MAGIC_NUMBER as _MAGIC_NUMBER
from pathlib import Path
from types import ModuleType
from typing import (
Any,
AnyStr,
Expand Down Expand Up @@ -61,7 +62,7 @@
from cmk.utils.hostaddress import HostAddress, HostName, Hosts
from cmk.utils.http_proxy_config import http_proxy_config_from_user_setting, HTTPProxyConfig
from cmk.utils.ip_lookup import IPStackConfig
from cmk.utils.labels import Labels, LabelSources
from cmk.utils.labels import BuiltinHostLabelsStore, Labels, LabelSources
from cmk.utils.legacy_check_api import LegacyCheckDefinition
from cmk.utils.log import console
from cmk.utils.macros import replace_macros_in_str
Expand Down Expand Up @@ -140,6 +141,14 @@
from cmk.server_side_calls import v1 as server_side_calls_api
from cmk.server_side_calls_backend.config_processing import PreprocessingResult

cme_labels: ModuleType | None
try:
from cmk.utils.cme import ( # type: ignore[import-untyped, no-redef, unused-ignore]
labels as cme_labels,
)
except ModuleNotFoundError:
cme_labels = None

try:
from cmk.base.cee.rrd import RRDObjectConfig
except ModuleNotFoundError:
Expand Down Expand Up @@ -1931,6 +1940,7 @@ def initialize(self) -> ConfigCache:
clusters_of=self._clusters_of_cache,
nodes_of=self._nodes_cache,
all_configured_hosts=list(set(self.hosts_config)),
builtin_host_labels_store=BuiltinHostLabelsStore(),
debug_matching_stats=ruleset_matching_stats,
)

Expand Down Expand Up @@ -3984,12 +3994,11 @@ def reset_config_cache() -> ConfigCache:

def _create_config_cache() -> ConfigCache:
"""create clean config cache"""
cache_class = (
ConfigCache
if cmk_version.edition(cmk.utils.paths.omd_root) is cmk_version.Edition.CRE
else CEEConfigCache
)
return cache_class()
if cmk_version.edition(cmk.utils.paths.omd_root) is cmk_version.Edition.CRE:
return ConfigCache()
if cmk_version.edition(cmk.utils.paths.omd_root) is cmk_version.Edition.CME:
return CMEConfigCache()
return CEEConfigCache()


# TODO(au): Find a way to retreive the matchtype_information directly from the
Expand Down Expand Up @@ -4700,3 +4709,13 @@ def get_nested_rules(prefix: str, rulesets: dict[str, list]) -> Mapping[int, str
id(service_tag_rules): "service_tag_rules",
id(management_bulkwalk_hosts): "management_bulkwalk_hosts",
}


class CMEConfigCache(CEEConfigCache):
def initialize(self) -> ConfigCache:
super().initialize()
if cme_labels:
self.ruleset_matcher.ruleset_optimizer.set_builtin_host_labels_store(
cme_labels.CMEBuiltinHostLabelsStore()
)
return self
19 changes: 14 additions & 5 deletions cmk/utils/rulesets/ruleset_matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ def __init__(
all_configured_hosts: Sequence[HostName],
clusters_of: Mapping[HostName, Sequence[HostName]],
nodes_of: Mapping[HostName, Sequence[HostName]],
builtin_host_labels_store: BuiltinHostLabelsStore,
debug_matching_stats: bool = False,
) -> None:
super().__init__()
Expand All @@ -197,6 +198,7 @@ def __init__(
all_configured_hosts,
clusters_of,
nodes_of,
builtin_host_labels_store,
debug_matching_stats,
)
self.labels_of_host = self.ruleset_optimizer.labels_of_host
Expand Down Expand Up @@ -504,6 +506,7 @@ def __init__(
all_configured_hosts: Sequence[HostName],
clusters_of: Mapping[HostName, Sequence[HostName]],
nodes_of: Mapping[HostName, Sequence[HostName]],
builtin_host_labels_store: BuiltinHostLabelsStore,
debug_matching_stats: bool = False,
) -> None:
super().__init__()
Expand All @@ -514,6 +517,7 @@ def __init__(
self._host_paths = host_paths
self._clusters_of = clusters_of
self._nodes_of = nodes_of
self._builtin_host_labels_store = builtin_host_labels_store

self._all_configured_hosts = all_configured_hosts

Expand Down Expand Up @@ -547,6 +551,11 @@ def __init__(
self._debug_matching_stats = debug_matching_stats
self.matching_stats: dict[int, HostRulesetMatchingStats | ServiceRulesetMatchingStats] = {}

def set_builtin_host_labels_store(
self, builtin_host_labels_store: BuiltinHostLabelsStore
) -> None:
self._builtin_host_labels_store = builtin_host_labels_store

def clear_ruleset_caches(self) -> None:
self.__host_ruleset_cache.clear()
self.__service_ruleset_cache.clear()
Expand Down Expand Up @@ -965,7 +974,7 @@ def labels_of_host(self, hostname: HostName) -> Labels:
labels.update(self._discovered_labels_of_host(hostname))
labels.update(self._ruleset_labels_of_host(hostname))
labels.update(self._label_manager.explicit_host_labels.get(hostname, {}))
labels.update(RulesetOptimizer._builtin_labels_of_host())
labels.update(self._builtin_labels_of_host())
return self.__labels_of_host.setdefault(hostname, labels)

def label_sources_of_host(self, hostname: HostName) -> LabelSources:
Expand All @@ -974,7 +983,7 @@ def label_sources_of_host(self, hostname: HostName) -> LabelSources:
_get_host_labels()"""
labels: LabelSources = {}
labels.update({k: "discovered" for k in self._discovered_labels_of_host(hostname).keys()})
labels.update({k: "discovered" for k in RulesetOptimizer._builtin_labels_of_host()})
labels.update({k: "discovered" for k in self._builtin_labels_of_host()})
labels.update({k: "ruleset" for k in self._ruleset_labels_of_host(hostname)})
labels.update(
{
Expand All @@ -997,10 +1006,10 @@ def _discovered_labels_of_host(self, hostname: HostName) -> Labels:
)
return {l.name: l.value for l in host_labels}

@staticmethod
def _builtin_labels_of_host() -> Labels:
def _builtin_labels_of_host(self) -> Labels:
return {
label_id: label["value"] for label_id, label in BuiltinHostLabelsStore().load().items()
label_id: label["value"]
for label_id, label in self._builtin_host_labels_store.load().items()
}

def labels_of_service(self, hostname: HostName, service_desc: ServiceName) -> Labels:
Expand Down
19 changes: 14 additions & 5 deletions tests/unit/cmk/base/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import cmk.ccc.version as cmk_version
from cmk.ccc.exceptions import MKGeneralException
from cmk.ccc.version import Edition, edition

import cmk.utils.paths
from cmk.utils.config_path import VersionedConfigPath
Expand Down Expand Up @@ -2030,6 +2031,9 @@ def test_host_label_rules_default() -> None:


def test_labels(monkeypatch: MonkeyPatch) -> None:
additional_labels = {}
if edition(cmk.utils.paths.omd_root) is Edition.CME:
additional_labels = {"cmk/customer": {"value": "provider", "source": "discovered"}}
test_host = HostName("test-host")
xyz_host = HostName("xyz")

Expand All @@ -2056,22 +2060,27 @@ def test_labels(monkeypatch: MonkeyPatch) -> None:
ts.add_host(xyz_host)

config_cache = ts.apply(monkeypatch)
assert config_cache.labels(xyz_host) == {"cmk/site": "NO_SITE"}
assert config_cache.labels(xyz_host) == {
"cmk/site": "NO_SITE",
} | {k: v["value"] for k, v in additional_labels.items()}
assert config_cache.labels(test_host) == {
"cmk/site": "NO_SITE",
"explicit": "ding",
"from-rule": "rule1",
"from-rule2": "rule2",
}
} | {k: v["value"] for k, v in additional_labels.items()}
assert config_cache.label_sources(test_host) == {
"cmk/site": "discovered",
"explicit": "explicit",
"from-rule": "ruleset",
"from-rule2": "ruleset",
}
} | {k: v["source"] for k, v in additional_labels.items()}


def test_host_labels_of_host_discovered_labels(monkeypatch: MonkeyPatch, tmp_path: Path) -> None:
additional_labels = {}
if edition(cmk.utils.paths.omd_root) is Edition.CME:
additional_labels = {"cmk/customer": {"value": "provider", "source": "discovered"}}
test_host = HostName("test-host")
ts = Scenario()
ts.add_host(test_host)
Expand All @@ -2085,11 +2094,11 @@ def test_host_labels_of_host_discovered_labels(monkeypatch: MonkeyPatch, tmp_pat
assert config_cache.labels(test_host) == {
"cmk/site": "NO_SITE",
"äzzzz": "eeeeez",
}
} | {k: v["value"] for k, v in additional_labels.items()}
assert config_cache.label_sources(test_host) == {
"cmk/site": "discovered",
"äzzzz": "discovered",
}
} | {k: v["source"] for k, v in additional_labels.items()}


def test_service_label_rules_default() -> None:
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/cmk/base/test_core_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ def test_get_host_attributes(monkeypatch: MonkeyPatch) -> None:

if cmk_version.edition(cmk.utils.paths.omd_root) is cmk_version.Edition.CME:
expected_attrs["_CUSTOMER"] = "provider"
expected_attrs["__LABEL_cmk/customer"] = "provider"
expected_attrs["__LABELSOURCE_cmk/customer"] = "discovered"

assert (
config_cache.get_host_attributes(
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/cmk/base/test_core_nagios.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ def test_create_nagios_host_spec(
if cmk_version.edition(paths.omd_root) is cmk_version.Edition.CME:
result = result.copy()
result["_CUSTOMER"] = "provider"
result["__LABELSOURCE_cmk/customer"] = "discovered"
result["__LABEL_cmk/customer"] = "provider"

ts = Scenario()
ts.add_host(HostName("localhost"))
Expand Down
20 changes: 18 additions & 2 deletions tests/unit/cmk/base/test_unit_automations.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
from tests.testlib.base import Scenario

import cmk.ccc.version as cmk_version
from cmk.ccc.version import Edition, edition

from cmk.utils import paths
from cmk.utils.hostaddress import HostName
from cmk.utils.labels import LabelSource
from cmk.utils.rulesets.ruleset_matcher import RuleSpec

from cmk.automations.results import AnalyseHostResult, GetServicesLabelsResult
Expand Down Expand Up @@ -67,6 +69,12 @@ def test_registered_automations() -> None:


def test_analyse_host(monkeypatch: MonkeyPatch) -> None:
additional_labels: dict[str, str] = {}
additional_label_sources: dict[str, LabelSource] = {}
if edition(paths.omd_root) is Edition.CME:
additional_labels = {"cmk/customer": "provider"}
additional_label_sources = {"cmk/customer": "discovered"}

automation = automations.AutomationAnalyseHost()

ts = Scenario()
Expand All @@ -81,9 +89,17 @@ def test_analyse_host(monkeypatch: MonkeyPatch) -> None:
)
ts.apply(monkeypatch)

label_sources: dict[str, LabelSource] = {
"cmk/site": "discovered",
"explicit": "explicit",
}
assert automation.execute(["test-host"]) == AnalyseHostResult(
label_sources={"cmk/site": "discovered", "explicit": "explicit"},
labels={"cmk/site": "NO_SITE", "explicit": "ding"},
label_sources=label_sources | additional_label_sources,
labels={
"cmk/site": "NO_SITE",
"explicit": "ding",
}
| additional_labels,
)


Expand Down
9 changes: 9 additions & 0 deletions tests/unit/cmk/utils/rulesets/test_ruleset_matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from tests.testlib.base import Scenario

from cmk.utils.hostaddress import HostName
from cmk.utils.labels import BuiltinHostLabelsStore
from cmk.utils.rulesets.ruleset_matcher import (
LabelManager,
matches_tag_condition,
Expand Down Expand Up @@ -151,6 +152,7 @@ def test_ruleset_matcher_get_host_values_labels(
all_configured_hosts=[HostName("host1"), HostName("host2")],
clusters_of={},
nodes_of={},
builtin_host_labels_store=BuiltinHostLabelsStore(),
)

assert list(matcher.get_host_values(hostname, ruleset=host_label_ruleset)) == expected_result
Expand Down Expand Up @@ -188,6 +190,7 @@ def test_labels_of_service(monkeypatch: MonkeyPatch) -> None:
all_configured_hosts=[test_host, xyz_host],
clusters_of={},
nodes_of={},
builtin_host_labels_store=BuiltinHostLabelsStore(),
)

assert not ruleset_matcher.labels_of_service(xyz_host, "CPU load")
Expand Down Expand Up @@ -220,6 +223,7 @@ def test_labels_of_service_discovered_labels() -> None:
all_configured_hosts=[test_host],
clusters_of={},
nodes_of={},
builtin_host_labels_store=BuiltinHostLabelsStore(),
)

service_description = "CPU load"
Expand Down Expand Up @@ -256,6 +260,7 @@ def test_basic_get_host_values() -> None:
],
clusters_of={},
nodes_of={},
builtin_host_labels_store=BuiltinHostLabelsStore(),
)

assert not list(matcher.get_host_values(HostName("abc"), ruleset=ruleset))
Expand Down Expand Up @@ -296,6 +301,7 @@ def test_basic_get_host_values_subfolders() -> None:
],
clusters_of={},
nodes_of={},
builtin_host_labels_store=BuiltinHostLabelsStore(),
)

assert not list(matcher.get_host_values(HostName("xyz"), ruleset=ruleset))
Expand Down Expand Up @@ -371,6 +377,7 @@ def test_basic_host_ruleset_get_merged_dict_values() -> None:
],
clusters_of={},
nodes_of={},
builtin_host_labels_store=BuiltinHostLabelsStore(),
)

assert not matcher.get_host_merged_dict(HostName("abc"), ruleset=dict_ruleset)
Expand Down Expand Up @@ -446,6 +453,7 @@ def test_basic_host_ruleset_get_host_bool_value() -> None:
],
clusters_of={},
nodes_of={},
builtin_host_labels_store=BuiltinHostLabelsStore(),
)

assert matcher.get_host_bool_value(HostName("abc"), ruleset=binary_ruleset) is False
Expand Down Expand Up @@ -568,6 +576,7 @@ def test_ruleset_matcher_get_host_values_tags(
],
clusters_of={},
nodes_of={},
builtin_host_labels_store=BuiltinHostLabelsStore(),
)
assert list(matcher.get_host_values(hostname, ruleset=tag_ruleset)) == expected_result

Expand Down

0 comments on commit 28df865

Please sign in to comment.