def browser_supports_canvas(): user_agent = html.request.user_agent if 'MSIE' in user_agent: matches = regex(r'MSIE ([0-9]{1,}[\.0-9]{0,})').search(user_agent) if matches: ie_version = float(matches.group(1)) if ie_version >= 9.0: return True # Trying to deal with the IE compatiblity mode to detect the real IE version matches = regex(r'Trident/([0-9]{1,}[\.0-9]{0,})').search(user_agent) if matches: trident_version = float(matches.group(1)) + 4 if trident_version >= 9.0: return True return False else: return True
def _convert_pattern_list(self, patterns): # type: (List[Text]) -> Tuple[bool, Pattern[Text]] """Compiles a list of service match patterns to a to a single regex Reducing the number of individual regex matches improves the performance dramatically. This function assumes either all or no pattern is negated (like WATO creates the rules). """ if not patterns: return False, regex(u"") # Match everything negate, patterns = parse_negated_condition_list(patterns) pattern_parts = [] for p in patterns: if isinstance(p, dict): pattern_parts.append(p["$regex"]) else: pattern_parts.append(p) return negate, regex("(?:%s)" % "|".join("(?:%s)" % p for p in pattern_parts))
def _event_match_servicegroups(rule, context, is_regex): # type: (EventRule, EventContext, bool) -> Optional[str] if is_regex: match_type, required_groups = rule.get("match_servicegroups_regex", (None, None)) else: required_groups = rule.get("match_servicegroups") if context["WHAT"] != "SERVICE": if required_groups: return "This rule requires membership in a service group, but this is a host notification" return None if required_groups is not None: sgn = context.get("SERVICEGROUPNAMES") if sgn is None: return ( "No information about service groups is in the context, but service " "must be in group %s" % (" or ".join(required_groups))) if sgn: servicegroups = sgn.split(",") else: return "The service is in no service group, but %s%s is required" % ( (is_regex and "regex " or ""), " or ".join(required_groups)) for group in required_groups: if is_regex: r = regex(group) for sg in servicegroups: if config.define_servicegroups is None: continue match_value = config.define_servicegroups[ sg] if match_type == "match_alias" else sg if r.search(match_value): return None elif group in servicegroups: return None if is_regex: if match_type == "match_alias": if config.define_servicegroups is None: return "No service groups defined." return "The service is only in the groups %s. None of these patterns match: %s" % ( '"' + '", "'.join(config.define_servicegroups[x] for x in servicegroups) + '"', '"' + '" or "'.join(required_groups)) + '"' return "The service is only in the groups %s. None of these patterns match: %s" % ( '"' + '", "'.join(servicegroups) + '"', '"' + '" or "'.join(required_groups)) + '"' return "The service is only in the groups %s, but %s is required" % ( sgn, " or ".join(required_groups)) return None
def event_match_plugin_output(rule, context): if "match_plugin_output" in rule: r = regex(rule["match_plugin_output"]) if context["WHAT"] == "SERVICE": output = context["SERVICEOUTPUT"] else: output = context["HOSTOUTPUT"] if not r.search(output): return "The expression '%s' cannot be found in the plugin output '%s'" % \ (rule["match_plugin_output"], output)
def _get_service_filter_func( service_whitelist: Optional[List[str]], service_blacklist: Optional[List[str]], ) -> _ServiceFilter: if not service_whitelist and not service_blacklist: return _accept_all_services whitelist = ( regex("|".join(f"({p})" for p in service_whitelist)) # if service_whitelist else _MATCH_EVERYTHING) blacklist = ( regex("|".join(f"({p})" for p in service_blacklist)) # if service_blacklist else _MATCH_NOTHING) def _filter_service_by_patterns(service_name: ServiceName) -> bool: return whitelist.match( service_name) is not None and blacklist.match(service_name) is None return _filter_service_by_patterns
def _evaluate_snmp_detection_atom( atom: SNMPDetectAtom, oid_value_getter: Callable[[str], Optional[str]], ) -> bool: oid, pattern, flag = atom value = oid_value_getter(oid) if value is None: # check for "not_exists" return pattern == ".*" and not flag # ignore case! return bool(regex(pattern, re.IGNORECASE | re.DOTALL).fullmatch(value)) is flag
def get_service_description_matches(self, hosts: List[BIHostData], pattern: str) -> List[BIServiceSearchMatch]: matched_services = [] regex_pattern = regex(pattern) for host in hosts: for service_description in host.services.keys(): match = regex_pattern.match(service_description) if match is None: continue matched_services.append( BIServiceSearchMatch(host, service_description, tuple(match.groups()))) return matched_services
def _host_renamed_into(self, hostname, renaming_config): prefix_regex = regex(renaming_config["match_hostname"]) if not prefix_regex.match(hostname): return None new_hostname = hostname for operation in renaming_config["renamings"]: new_hostname = self._host_renaming_operation(operation, new_hostname) if new_hostname != hostname: return new_hostname return None
def get_service_description_matches( self, host_matches: List[BIHostSearchMatch], pattern: str, ) -> List[BIServiceSearchMatch]: matched_services = [] regex_pattern = regex(pattern) for host_match in host_matches: for service_description in host_match.host.services.keys(): if match := regex_pattern.match(service_description): matched_services.append( BIServiceSearchMatch(host_match, service_description, tuple(match.groups())))
def _evaluate_snmp_detection_atom(atom, host_config, cp_name, do_snmp_scan): # type: (SNMPDetectAtom, SNMPHostConfig, str, bool) -> bool oid, pattern, flag = atom value = snmp.get_single_oid( host_config, oid, cp_name, do_snmp_scan=do_snmp_scan, ) if value is None: # check for "not_exists" return pattern == '.*' and not flag # ignore case! return bool(regex(pattern, re.IGNORECASE).fullmatch(value)) is flag
def get_host_alias_matches(self, hosts: List[BIHostData], pattern: str) -> Tuple[List[BIHostData], Dict]: # TODO: alias matches currently costs way more performmance than the host matches # requires alias lookup to fix matched_hosts = [] matched_re_groups = {} regex_pattern = regex(pattern) for host in hosts: match = regex_pattern.match(host.alias) if match is None: continue matched_hosts.append(host) matched_re_groups[host.name] = tuple(match.groups()) return matched_hosts, matched_re_groups
def _evaluate_snmp_detection_atom(atom: SNMPDetectAtom, cp_name: str, do_snmp_scan: bool, *, backend: ABCSNMPBackend) -> bool: oid, pattern, flag = atom value = snmp_modes.get_single_oid( oid, cp_name, do_snmp_scan=do_snmp_scan, backend=backend, ) if value is None: # check for "not_exists" return pattern == '.*' and not flag # ignore case! return bool(regex(pattern, re.IGNORECASE).fullmatch(value)) is flag
def _process_assignments(self, aggr_name, aggr_data): if not self._assignments: self._aggregation_targets.setdefault(None, {})[aggr_name] = aggr_data return if "querying_host" in self._assignments: self._aggregation_targets.setdefault(None, {})[aggr_name] = aggr_data if "affected_hosts" in self._assignments: for hostname in aggr_data["hosts"]: self._aggregation_targets.setdefault(hostname, {})[aggr_name] = aggr_data for pattern, target_host in self._assignments.get("regex", []): if regex(pattern).match(aggr_name): self._aggregation_targets.setdefault(target_host, {})[aggr_name] = aggr_data
def _get_matching_time_settings(source_hostnames, piggybacked_hostname, time_settings): # type: (List[str], str, List[Tuple[Optional[str], str, int]]) -> Dict[Tuple[Optional[str], str], int] matching_time_settings = {} # type: Dict[Tuple[Optional[str], str], int] for expr, key, value in time_settings: # expr may be # - None (global settings) or # - 'source-hostname' or # - 'piggybacked-hostname' or # - '~piggybacked-[hH]ostname' # the first entry ('piggybacked-hostname' vs '~piggybacked-[hH]ostname') wins if expr is None or expr in source_hostnames or expr == piggybacked_hostname: matching_time_settings.setdefault((expr, key), value) elif expr.startswith("~") and regex(expr[1:]).match(piggybacked_hostname): matching_time_settings.setdefault((piggybacked_hostname, key), value) return matching_time_settings
def get_host_name_matches( self, hosts: List[BIHostData], pattern: str, ) -> Tuple[List[BIHostData], Dict]: if pattern == "(.*)": return hosts, self._host_match_groups(hosts) is_regex_match = any( map(lambda x: x in pattern, ["(", ")", "*", "$", "|", "[", "]"])) if not is_regex_match: host = self.hosts.get(pattern) if host: return [host], {pattern: (pattern, )} return [], {} # Hidden "feature": The regex pattern condition for hosts implicitly uses a $ at the end pattern_with_anchor = pattern if not pattern_with_anchor.endswith("$"): pattern_with_anchor += "$" matched_hosts = [] matched_re_groups = {} regex_pattern = regex(pattern_with_anchor) pattern_match_cache = self._host_regex_match_cache.setdefault( pattern_with_anchor, {}) pattern_miss_cache = self._host_regex_miss_cache.setdefault( pattern_with_anchor, {}) for host in hosts: if host.name in pattern_miss_cache: continue cached_match = pattern_match_cache.get(host.name) if cached_match: matched_hosts.append(host) matched_re_groups[host.name] = cached_match continue match = regex_pattern.match(host.name) if match is None: pattern_miss_cache[host.name] = True continue pattern_match_cache[host.name] = match.groups() matched_hosts.append(host) matched_re_groups[host.name] = pattern_match_cache[host.name] return matched_hosts, matched_re_groups
def _get_sanitized_and_translated_piggybacked_hostname( orig_piggyback_header: bytes, hostname: HostName, ) -> Optional[HostName]: piggybacked_hostname = ensure_str(orig_piggyback_header[4:-4]) if not piggybacked_hostname: return None piggybacked_hostname = config.translate_piggyback_host(hostname, piggybacked_hostname) if piggybacked_hostname == hostname or not piggybacked_hostname: return None # unpiggybacked "normal" host # Protect Checkmk against unallowed host names. Normally source scripts # like agent plugins should care about cleaning their provided host names # up, but we need to be sure here to prevent bugs in Checkmk code. return regex("[^%s]" % REGEX_HOST_NAME_CHARS).sub("_", piggybacked_hostname)
def from_headerline( cls, line: bytes, translation: TranslationOptions, *, encoding_fallback: str, ) -> "PiggybackMarker": hostname = ensure_str(line.strip()[4:-4]) assert hostname hostname = translate_piggyback_host( hostname, translation, encoding_fallback=encoding_fallback, ) # Protect Checkmk against unallowed host names. Normally source scripts # like agent plugins should care about cleaning their provided host names # up, but we need to be sure here to prevent bugs in Checkmk code. return cls(regex("[^%s]" % REGEX_HOST_NAME_CHARS).sub("_", hostname))
def matches_host_name(self, host_entries: Optional[HostOrServiceConditions], hostname: HostName) -> bool: if not host_entries: return True negate, host_entries = parse_negated_condition_list(host_entries) if hostname == "": # -> generic agent host return negate for entry in host_entries: if not isinstance(entry, dict) and hostname == entry: return not negate if isinstance(entry, dict) and regex( entry["$regex"]).match(hostname) is not None: return not negate return negate
def _matches_host_name(self, host_entries, hostname): if not host_entries: return True negate, host_entries = parse_negated_condition_list(host_entries) for entry in host_entries: use_regex = isinstance(entry, dict) if hostname is True: # -> generic agent host continue if not use_regex and hostname == entry: return not negate if use_regex and regex(entry["$regex"]).match(hostname) is not None: return not negate return negate
def _event_match_exclude_servicegroups( rule: EventRule, context: EventContext, is_regex: bool ) -> Optional[str]: if is_regex: match_type, excluded_groups = rule.get("match_exclude_servicegroups_regex", (None, None)) else: excluded_groups = rule.get("match_exclude_servicegroups") if context["WHAT"] != "SERVICE": # excluded_groups do not apply to a host notification return None if excluded_groups is not None: context_sgn = context.get("SERVICEGROUPNAMES") if not context_sgn: # No actual groups means no possible negative match return None servicegroups = context_sgn.split(",") for group in excluded_groups: if is_regex: r = regex(group) for sg in servicegroups: if config.define_servicegroups is None: continue match_value = ( config.define_servicegroups[sg] if match_type == "match_alias" else sg ) match_value_inverse = ( sg if match_type == "match_alias" else config.define_servicegroups[sg] ) if r.search(match_value): return 'The service group "%s" (%s) is excluded per regex pattern: %s' % ( match_value, match_value_inverse, group, ) elif group in servicegroups: return "The service group %s is excluded" % group return None
def _get_matching_time_settings( source_hostnames: Container[HostName], piggybacked_hostname: HostName, time_settings: PiggybackTimeSettings, ) -> Mapping[Tuple[Optional[str], str], int]: matching_time_settings: Dict[Tuple[Optional[str], str], int] = {} for expr, key, value in time_settings: # expr may be # - None (global settings) or # - 'source-hostname' or # - 'piggybacked-hostname' or # - '~piggybacked-[hH]ostname' # the first entry ('piggybacked-hostname' vs '~piggybacked-[hH]ostname') wins if expr is None or expr in source_hostnames or expr == piggybacked_hostname: matching_time_settings.setdefault((expr, key), value) elif expr.startswith("~") and regex( expr[1:]).match(piggybacked_hostname): matching_time_settings.setdefault((piggybacked_hostname, key), value) return matching_time_settings
def _check_plugins_missing_data( plugins_missing_data: List[CheckPluginName], exit_spec: ExitSpec, some_success: bool, ) -> Iterable[ActiveCheckResult]: if not plugins_missing_data: return if not some_success: yield ActiveCheckResult(exit_spec.get("empty_output", 2), ("Got no information from host", ), (), ()) return # key is a legacy name, kept for compatibility. specific_plugins_missing_data_spec = exit_spec.get( "specific_missing_sections", []) specific_plugins, generic_plugins = set(), set() for check_plugin_name in plugins_missing_data: for pattern, status in specific_plugins_missing_data_spec: reg = regex(pattern) if reg.match(str(check_plugin_name)): specific_plugins.add((check_plugin_name, status)) break else: # no break generic_plugins.add(str(check_plugin_name)) # key is a legacy name, kept for compatibility. missing_status = exit_spec.get("missing_sections", 1) plugin_list = ", ".join(sorted(generic_plugins)) yield ActiveCheckResult( missing_status, (f"Missing monitoring data for plugins: {plugin_list}{state_markers[missing_status]}", ), (), (), ) yield from (ActiveCheckResult(status, f"{plugin}{state_markers[status]}", (), ()) for plugin, status in sorted(specific_plugins))
def _translate(translation: TranslationOptions, name: str) -> str: # 1. Case conversion caseconf = translation.get("case") if caseconf == "upper": name = name.upper() elif caseconf == "lower": name = name.lower() # 2. Drop domain part (not applied to IP addresses!) if translation.get("drop_domain"): try: ipaddress.ip_address(name) except ValueError: # Drop domain if "name " is not a valid IP address name = name.split(".", 1)[0] # 3. Multiple regular expression conversion if isinstance(translation.get("regex"), tuple): translations = [translation["regex"]] else: translations = translation.get("regex", []) for expr, subst in translations: if not expr.endswith('$'): expr += '$' rcomp = regex(expr) # re.RegexObject.sub() by hand to handle non-existing references mo = rcomp.match(name) if mo: name = subst for nr, text in enumerate(mo.groups("")): name = name.replace("\\%d" % (nr + 1), text) break # 4. Explicity mapping for from_name, to_name in translation.get("mapping", []): if from_name == name: name = to_name break return name.strip()
def convert_pattern_list(patterns: List[str]) -> Optional[Pattern[str]]: """Compiles a list of service match patterns to a single regex Reducing the number of individual regex matches improves the performance dramatically. This function assumes either all or no pattern is negated (like WATO creates the rules). """ if not patterns: return None pattern_parts = [] for pattern in patterns: negate, pattern = _parse_negated(pattern) # Skip ALL_SERVICES from end of negated lists if negate: if pattern == ALL_SERVICES[0]: continue pattern_parts.append("(?!%s)" % pattern) else: pattern_parts.append("(?:%s)" % pattern) return regex("(?:%s)" % "|".join(pattern_parts))
def from_headerline( cls, line: bytes, translation: TranslationOptions, *, encoding_fallback: str, ) -> "PiggybackMarker": raw_host_name = ensure_str_with_fallback( line.strip()[4:-4], encoding='utf-8', fallback=encoding_fallback, ) assert raw_host_name hostname = translate_hostname(translation, raw_host_name) # Protect Checkmk against unallowed host names. Normally source scripts # like agent plugins should care about cleaning their provided host names # up, but we need to be sure here to prevent bugs in Checkmk code. # TODO: this should be moved into the HostName class, if it is ever created. return cls( HostName( regex("[^%s]" % REGEX_HOST_NAME_CHARS).sub("_", hostname)))
def _matches_host_name(self, host_entries, hostname): if not host_entries: return True negate = False if isinstance(host_entries, dict) and "$nor" in host_entries: negate = True host_entries = host_entries["$nor"] for entry in host_entries: use_regex = isinstance(entry, dict) if hostname is True: # -> generic agent host continue if not use_regex and hostname == entry: return not negate if use_regex and regex(entry["$regex"]).match(hostname) is not None: return not negate return negate
def _check_plugins_missing_data( plugins_missing_data: List[CheckPluginName], exit_spec: config.ExitSpec, some_success: bool, ) -> Tuple[ServiceState, ServiceDetails]: if not some_success: return (cast(int, exit_spec.get("empty_output", 2)), "Got no information from host") specific_plugins_missing_data_spec = cast( List[config.ExitSpecSection], exit_spec.get("specific_missing_sections", []), ) specific_plugins, generic_plugins = set(), set() for check_plugin_name in plugins_missing_data: for pattern, status in specific_plugins_missing_data_spec: reg = regex(pattern) if reg.match(str(check_plugin_name)): specific_plugins.add((check_plugin_name, status)) break else: # no break generic_plugins.add(str(check_plugin_name)) generic_plugins_status = cast(int, exit_spec.get("missing_sections", 1)) infotexts = [ "Missing monitoring data for check plugins: %s%s" % ( ", ".join(sorted(generic_plugins)), check_api_utils.state_markers[generic_plugins_status], ), ] for plugin, status in sorted(specific_plugins): infotexts.append("%s%s" % (plugin, check_api_utils.state_markers[status])) generic_plugins_status = max(generic_plugins_status, status) return generic_plugins_status, ", ".join(infotexts)
def add_matching_services(name, description, service_state, start_type, entry): # New wato rule handling svc, *statespec = entry # First match name or description (optional since rule based config option available) if svc: if svc.startswith("~"): r = regex(svc[1:]) if not r.match(name) and not r.match(description): return elif svc not in (name, description): return if isinstance(statespec, list): # New wato rule handling (always given as tuple of two) if (statespec[0] and statespec[0] != service_state) or (statespec[1] and statespec[1] != start_type): return else: for match_criteria in statespec.split("/"): if match_criteria not in {service_state, start_type}: return yield Service(item=name)
def in_extraconf_hostlist(hostlist, hostname): """Whether or not the given host matches the hostlist. Entries in list are hostnames that must equal the hostname. Expressions beginning with ! are negated: if they match, the item is excluded from the list. Expressions beginning with ~ are treated as regular expression. Also the three special tags '@all', '@clusters', '@physical' are allowed. """ # Migration help: print error if old format appears in config file # FIXME: When can this be removed? try: if hostlist[0] == "": raise MKGeneralException( 'Invalid empty entry [ "" ] in configuration') except IndexError: pass # Empty list, no problem. for hostentry in hostlist: if hostentry == '': raise MKGeneralException('Empty hostname in host list %r' % hostlist) negate = False use_regex = False if hostentry[0] == '@': if hostentry == '@all': return True # TODO: Is not used anymore for a long time. Will be cleaned up # with 1.6 tuple ruleset cleanup #ic = is_cluster(hostname) #if hostentry == '@cluster' and ic: # return True #elif hostentry == '@physical' and not ic: # return True # Allow negation of hostentry with prefix '!' else: if hostentry[0] == '!': hostentry = hostentry[1:] negate = True # Allow regex with prefix '~' if hostentry[0] == '~': hostentry = hostentry[1:] use_regex = True try: if not use_regex and hostname == hostentry: return not negate # Handle Regex. Note: hostname == True -> generic unknown host elif use_regex and hostname != True: if regex(hostentry).match(hostname) is not None: return not negate except MKGeneralException: if cmk.utils.debug.enabled(): raise return False
def validate_job_id(job_id: str) -> None: if not regex(REGEX_GENERIC_IDENTIFIER).match(job_id): raise MKGeneralException(_("Invalid Job ID"))