def _load_config(self): self._bi_constants = { 'ALL_HOSTS': 'ALL_HOSTS-f41e728b-0bce-40dc-82ea-51091d034fc3', 'HOST_STATE': 'HOST_STATE-f41e728b-0bce-40dc-82ea-51091d034fc3', 'HIDDEN': 'HIDDEN-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_HOST': 'FOREACH_HOST-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_CHILD': 'FOREACH_CHILD-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_CHILD_WITH': 'FOREACH_CHILD_WITH-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_PARENT': 'FOREACH_PARENT-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_SERVICE': 'FOREACH_SERVICE-f41e728b-0bce-40dc-82ea-51091d034fc3', 'REMAINING': 'REMAINING-f41e728b-0bce-40dc-82ea-51091d034fc3', 'DISABLED': 'DISABLED-f41e728b-0bce-40dc-82ea-51091d034fc3', 'HARD_STATES': 'HARD_STATES-f41e728b-0bce-40dc-82ea-51091d034fc3', 'DT_AGGR_WARN': 'DT_AGGR_WARN-f41e728b-0bce-40dc-82ea-51091d034fc3', } self._hosttags_transformer = RulesetToDictTransformer( tag_to_group_map=get_tag_to_group_map(config.tags)) try: vars_: Dict[str, Any] = { "aggregation_rules": {}, "aggregations": [], "host_aggregations": [], "bi_packs": {}, } vars_.update(self._bi_constants) exec(self._get_config_string(), vars_, vars_) # put legacy non-pack stuff into packs if (vars_["aggregation_rules"] or vars_["aggregations"] or vars_["host_aggregations"]) and \ "default" not in vars_["bi_packs"]: vars_["bi_packs"]["default"] = { "title": "Default Pack", "rules": vars_["aggregation_rules"], "aggregations": vars_["aggregations"], "host_aggregations": vars_["host_aggregations"], "public": True, "contact_groups": [], } self._packs = {} for pack_id, pack in vars_["bi_packs"].items(): # Convert rules from old-style tuples to new-style dicts aggregation_rules = {} for ruleid, rule in pack["rules"].items(): aggregation_rules[ruleid] = self._convert_rule_from_bi( rule, ruleid) aggregations = [] for aggregation in pack["aggregations"]: aggregations.append( self._convert_aggregation_from_bi(aggregation, single_host=False)) for aggregation in pack["host_aggregations"]: aggregations.append( self._convert_aggregation_from_bi(aggregation, single_host=True)) self._packs[pack_id] = { "id": pack_id, "title": pack["title"], "rules": aggregation_rules, "aggregations": aggregations, "public": pack["public"], "contact_groups": pack["contact_groups"], } self._add_missing_aggr_ids() except Exception as e: logger.error("Unable to load legacy bi.mk configuration %s", str(e)) raise
class BIManagement: def __init__(self): self._load_config() # .--------------------------------------------------------------------. # | Loading and saving | # '--------------------------------------------------------------------' def _get_config_string(self): filename = Path(cmk.utils.paths.default_config_dir, "multisite.d", "wato", "bi.mk") with filename.open("rb") as f: return f.read() def _load_config(self): self._bi_constants = { 'ALL_HOSTS': 'ALL_HOSTS-f41e728b-0bce-40dc-82ea-51091d034fc3', 'HOST_STATE': 'HOST_STATE-f41e728b-0bce-40dc-82ea-51091d034fc3', 'HIDDEN': 'HIDDEN-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_HOST': 'FOREACH_HOST-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_CHILD': 'FOREACH_CHILD-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_CHILD_WITH': 'FOREACH_CHILD_WITH-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_PARENT': 'FOREACH_PARENT-f41e728b-0bce-40dc-82ea-51091d034fc3', 'FOREACH_SERVICE': 'FOREACH_SERVICE-f41e728b-0bce-40dc-82ea-51091d034fc3', 'REMAINING': 'REMAINING-f41e728b-0bce-40dc-82ea-51091d034fc3', 'DISABLED': 'DISABLED-f41e728b-0bce-40dc-82ea-51091d034fc3', 'HARD_STATES': 'HARD_STATES-f41e728b-0bce-40dc-82ea-51091d034fc3', 'DT_AGGR_WARN': 'DT_AGGR_WARN-f41e728b-0bce-40dc-82ea-51091d034fc3', } self._hosttags_transformer = RulesetToDictTransformer( tag_to_group_map=get_tag_to_group_map(config.tags)) try: vars_: Dict[str, Any] = { "aggregation_rules": {}, "aggregations": [], "host_aggregations": [], "bi_packs": {}, } vars_.update(self._bi_constants) exec(self._get_config_string(), vars_, vars_) # put legacy non-pack stuff into packs if (vars_["aggregation_rules"] or vars_["aggregations"] or vars_["host_aggregations"]) and \ "default" not in vars_["bi_packs"]: vars_["bi_packs"]["default"] = { "title": "Default Pack", "rules": vars_["aggregation_rules"], "aggregations": vars_["aggregations"], "host_aggregations": vars_["host_aggregations"], "public": True, "contact_groups": [], } self._packs = {} for pack_id, pack in vars_["bi_packs"].items(): # Convert rules from old-style tuples to new-style dicts aggregation_rules = {} for ruleid, rule in pack["rules"].items(): aggregation_rules[ruleid] = self._convert_rule_from_bi( rule, ruleid) aggregations = [] for aggregation in pack["aggregations"]: aggregations.append( self._convert_aggregation_from_bi(aggregation, single_host=False)) for aggregation in pack["host_aggregations"]: aggregations.append( self._convert_aggregation_from_bi(aggregation, single_host=True)) self._packs[pack_id] = { "id": pack_id, "title": pack["title"], "rules": aggregation_rules, "aggregations": aggregations, "public": pack["public"], "contact_groups": pack["contact_groups"], } self._add_missing_aggr_ids() except Exception as e: logger.error("Unable to load legacy bi.mk configuration %s", str(e)) raise def _add_missing_aggr_ids(self): # Determine existing IDs used_aggr_ids = set() for pack_id, pack in self._packs.items(): used_aggr_ids.update( {x["ID"] for x in pack["aggregations"] if "ID" in x}) # Compute missing IDs new_id = "" for pack_id, pack in self._packs.items(): aggr_id_counter = 0 for aggregation in pack["aggregations"]: if "ID" not in aggregation: while True: aggr_id_counter += 1 new_id = "%s_aggr_%d" % (pack_id, aggr_id_counter) if new_id in used_aggr_ids: continue break used_aggr_ids.add(new_id) aggregation["ID"] = new_id def _convert_pack_to_bi(self, pack): converted_rules = { rule_id: self._convert_rule_to_bi(rule) for rule_id, rule in pack["rules"].items() } converted_aggregations: List[Tuple[BIAggrOptions, BIAggrGroups, BIAggrNode]] = [] converted_host_aggregations: List[Tuple[BIAggrOptions, BIAggrGroups, BIAggrNode]] = [] for aggregation in pack["aggregations"]: converted_aggregation = self._convert_aggregation_to_bi( aggregation) if aggregation["single_host"]: converted_host_aggregations.append(converted_aggregation) else: converted_aggregations.append(converted_aggregation) converted_pack = pack.copy() converted_pack["aggregations"] = converted_aggregations converted_pack["host_aggregations"] = converted_host_aggregations converted_pack["rules"] = converted_rules return converted_pack def _replace_bi_constants(self, s): for name, uuid in self._bi_constants.items(): while True: n = s.replace("'%s'" % uuid, name) if n != s: s = n else: break return s[0] + '\n ' + s[1:-1] + '\n' + s[-1] def _convert_aggregation_to_bi(self, aggr): node = self._convert_node_to_bi(aggr["node"]) option_keys: List[Tuple[str, Any]] = [ ("ID", None), ("node_visualization", {}), ("hard_states", False), ("downtime_aggr_warn", False), ("disabled", False), ] if cmk_version.is_managed_edition(): option_keys.append(("customer", managed.default_customer_id())) # Create dict with all aggregation options options = {} for option, default_value in option_keys: options[option] = aggr.get(option, default_value) return (options, self._convert_aggregation_groups( aggr["groups"])) + node def _convert_node_to_bi(self, node): if node[0] == "call": return node[1] if node[0] == "host": return (node[1][0], self._bi_constants['HOST_STATE']) if node[0] == "remaining": return (node[1][0], self._bi_constants['REMAINING']) if node[0] == "service": return node[1] if node[0] == "foreach_host": what = node[1][0] tags = node[1][1] if node[1][2]: hostspec = node[1][2] else: hostspec = self._bi_constants['ALL_HOSTS'] if isinstance(what, tuple) and what[0] == 'child_with': child_conditions = what[1] what = what[0] child_tags = child_conditions[0] child_hostspec = child_conditions[1] if child_conditions[ 1] else self._bi_constants['ALL_HOSTS'] return (self._bi_constants["FOREACH_" + what.upper()], child_tags, child_hostspec, tags, hostspec) \ + self._convert_node_to_bi(node[1][3]) return (self._bi_constants["FOREACH_" + what.upper()], tags, hostspec) + self._convert_node_to_bi(node[1][3]) if node[0] == "foreach_service": tags = node[1][0] if node[1][1]: spec = node[1][1] else: spec = self._bi_constants['ALL_HOSTS'] service = node[1][2] return (self._bi_constants["FOREACH_SERVICE"], tags, spec, service) + self._convert_node_to_bi(node[1][3]) def _convert_aggregation_from_bi(self, aggr, single_host): if isinstance(aggr[0], dict): options = aggr[0] aggr = aggr[1:] else: # Legacy configuration options = {} if aggr[0] == self._bi_constants["DISABLED"]: options["disabled"] = True aggr = aggr[1:] else: options["disabled"] = False if aggr[0] == self._bi_constants["DT_AGGR_WARN"]: options["downtime_aggr_warn"] = True aggr = aggr[1:] else: options["downtime_aggr_warn"] = False if aggr[0] == self._bi_constants["HARD_STATES"]: options["hard_states"] = True aggr = aggr[1:] else: options["hard_states"] = False node = self._convert_node_from_bi(aggr[1:]) aggr_dict = { "groups": self._convert_aggregation_groups(aggr[0]), "node": node, "single_host": single_host, } aggr_dict.update(options) return aggr_dict def _convert_aggregation_groups(self, old_groups): if isinstance(old_groups, list): return old_groups return [old_groups] # Make some conversions so that the format of the # valuespecs is matched def _convert_rule_from_bi(self, rule, ruleid): def tryint(x): try: return int(x) except ValueError: return x if isinstance(rule, tuple): rule = { "title": rule[0], "params": rule[1], "aggregation": rule[2], "nodes": rule[3], } crule = {} crule.update(rule) crule["nodes"] = list(map(self._convert_node_from_bi, rule["nodes"])) parts = rule["aggregation"].split("!") crule["aggregation"] = (parts[0], tuple(map(tryint, parts[1:]))) crule["id"] = ruleid return crule def _convert_rule_to_bi(self, rule): brule = {} brule.update(rule) if "id" in brule: del brule["id"] brule["nodes"] = list(map(self._convert_node_to_bi, rule["nodes"])) brule["aggregation"] = "!".join([rule["aggregation"][0]] + list(map(str, rule["aggregation"][1]))) return brule # Convert node-Tuple into format used by CascadingDropdown def _convert_node_from_bi(self, node): if len(node) == 2: if isinstance(node[1], list): return ("call", node) if node[1] == self._bi_constants['HOST_STATE']: return ("host", (node[0], )) if node[1] == self._bi_constants['REMAINING']: return ("remaining", (node[0], )) return ("service", node) foreach_spec = node[0] if foreach_spec == self._bi_constants['FOREACH_CHILD_WITH']: # extract the conditions meant for matching the childs child_conditions = list(node[1:3]) if child_conditions[1] == self._bi_constants['ALL_HOSTS']: child_conditions[1] = None node = node[0:1] + node[3:] if not isinstance(child_conditions[0], dict): new_tags = self._hosttags_transformer.transform_host_tags( child_conditions[0]) child_conditions[0] = new_tags.get("host_tags", {}) # Extract the list of tags if isinstance(node[1], (list, dict)): tags = node[1] node = node[0:1] + node[2:] if not isinstance(tags, dict): new_tags = self._hosttags_transformer.transform_host_tags(tags) tags = new_tags.get("host_tags", {}) else: tags = {} hostspec = node[1] if hostspec == self._bi_constants['ALL_HOSTS']: hostspec = None if foreach_spec == self._bi_constants['FOREACH_SERVICE']: service = node[2] subnode = self._convert_node_from_bi(node[3:]) return ("foreach_service", (tags, hostspec, service, subnode)) subnode = self._convert_node_from_bi(node[2:]) if foreach_spec == self._bi_constants['FOREACH_HOST']: what: Union[str, Tuple] = "host" elif foreach_spec == self._bi_constants['FOREACH_CHILD']: what = "child" elif foreach_spec == self._bi_constants['FOREACH_CHILD_WITH']: what = ("child_with", child_conditions) elif foreach_spec == self._bi_constants['FOREACH_PARENT']: what = "parent" return ("foreach_host", (what, tags, hostspec, subnode))