def _valuespec_discovery_systemd_units_services_rules(): return Dictionary( title=_("Systemd Service Discovery"), elements=[ ('descriptions', ListOfStrings(title=_("Descriptions"))), ('names', ListOfStrings(title=_("Service unit names"))), ('states', ListOf( DropdownChoice(choices=[ ("active", "active"), ("inactive", "inactive"), ("failed", "failed"), ], ), title=_("States"), )), ], help= _('This rule can be used to configure the discovery of the Linux services check. ' 'You can configure specific Linux services to be monitored by the Linux check by ' 'selecting them by description, unit name, or current state during the discovery.' ), )
def valuespec(self): return ListOfStrings( valuespec=ConfigHostname(), title=_("Parents"), help=_("Parents are used to configure the reachability of hosts by the " "monitoring server. A host is considered to be <b>unreachable</b> if all " "of its parents are unreachable or down. Unreachable hosts will not be " "actively monitored.<br><br><b>Clusters</b> automatically configure all " "of their nodes as parents, but only if you do not configure parents " "manually.<br><br>In a distributed setup make sure that the host and all " "of its parents are monitored by the same site."), orientation="horizontal", )
def _vs_regex_matching(match_obj): return ListOfStrings( title=_("Match interface %s (regex)" % match_obj), help=_( "Apply this rule only to interfaces whose %s matches one of the configured regular " "expressions. The match is done on the beginning of the %s." % (match_obj, match_obj)), orientation="horizontal", valuespec=RegExp( size=32, mode=RegExp.prefix, ), )
def _valuespec_inventory_solaris_services_rules(): return Dictionary( title=_("Solaris Service Discovery"), elements=[ ('descriptions', ListOfStrings(title=_("Descriptions"))), ('categories', ListOfStrings(title=_("Categories"))), ('names', ListOfStrings(title=_("Names"))), ('instances', ListOfStrings(title=_("Instances"))), ('states', ListOf( DropdownChoice(choices=[ ("online", _("online")), ("disabled", _("disabled")), ("maintenance", _("maintenance")), ("legacy_run", _("legacy run")), ], ), title=_("States"), )), ('outcome', Alternative( title=_("Service name"), style="dropdown", elements=[ FixedValue("full_descr", title=_("Full Description"), totext=""), FixedValue("descr_without_prefix", title=_("Description without type prefix"), totext=""), ], )), ], help= _('This rule can be used to configure the discovery of the Solaris services check. ' 'You can configure specific Solaris services to be monitored by the Solaris check by ' 'selecting them by description, category, name, or current state during the discovery.' ), )
def vs_choices(title): return CascadingDropdown( title=title, choices=[ ("nothing", _("Restrict all")), ("choices", _("Restrict the following keys"), ListOfStrings( orientation="horizontal", size=15, allow_empty=True, )), ], default_value="nothing", )
def _parameter_valuespec_services(): return Dictionary(elements=[ ("additional_servicenames", ListOfStrings( title=_("Alternative names for the service"), help=_("Here you can specify alternative names that the service might have. " "This helps when the exact spelling of the services can changed from " "one version to another."), )), ("states", ListOf( Tuple(orientation="horizontal", elements=[ DropdownChoice( title=_("Expected state"), default_value="running", choices=[(None, _("ignore the state")), ("running", _("running")), ("stopped", _("stopped"))], ), DropdownChoice( title=_("Start type"), default_value="auto", choices=[ (None, _("ignore the start type")), ("demand", _("demand")), ("disabled", _("disabled")), ("auto", _("auto")), ("unknown", _("unknown (old agent)")), ], ), MonitoringState(title=_("Resulting state"),), ], default_value=("running", "auto", 0)), title=_("Services states"), help=_("You can specify a separate monitoring state for each possible " "combination of service state and start type. If you do not use " "this parameter, then only running/auto will be assumed to be OK."), )), ( "else", MonitoringState( title=_("State if no entry matches"), default_value=2, ), ), ('icon', UserIconOrAction( title=_("Add custom icon or action"), help=_("You can assign icons or actions to the found services in the status GUI."), )) ],)
def _vs_ip_range(self): return CascadingDropdown(choices=[ ("ip_range", _("IP-Range"), Tuple( elements=[ IPv4Address(title=_("From:"), ), IPv4Address(title=_("To:"), ), ], orientation="horizontal", )), ("ip_network", _("IP Network"), Tuple( elements=[ IPv4Address(title=_("Network address:"), ), Integer( title=_("Netmask"), minvalue=8, maxvalue=30, ), ], orientation="horizontal", )), ("ip_list", _("Explicit List of IP Addresses"), ListOfStrings( valuespec=IPv4Address(), orientation="horizontal", )), ("ip_regex_list", _("List of patterns to exclude"), ListOfStrings( valuespec=RegExp(mode=RegExp.prefix, ), orientation="horizontal", help= _("A list of regular expressions which are matched against the found " "IP addresses to exclude them. The matched addresses are excluded." ), )), ])
def _valuespec_inventory_ipmi_rules_single(): return Dictionary(elements=[ ( "ignored_sensors", ListOfStrings( title=_("Ignore the following IPMI sensors"), help= _("Names of IPMI sensors that should be ignored during discovery. " "The pattern specified here must match exactly the beginning of " "the actual ensor name (case sensitive)."), orientation="horizontal"), ), ( "ignored_sensorstates", ListOfStrings( title=_("Ignore the following IPMI sensor states"), help= _("IPMI sensors with these states that should be gnored during discovery. " "The pattern specified here must match exactly the beginning of the actual " "sensor state (case sensitive)."), orientation="horizontal", ), ), ], )
def _valuespec_inv_parameters_lnx_sysctl(): return Dictionary( title=_("Inventory of Linux kernel configuration (sysctl)"), help=_( "This rule allows for defining regex-patterns for in- and excluding kernel " "configuration parameters in the inventory. By default, no parameters are included. " "Note that some kernel configuration parameters change frequently. Inventorizing " "one of these parameters will lead to frequent changes in the HW/SW inventory, " "which can quickly fill up the temporary file system." ), elements=[ ( "include_patterns", ListOfStrings( valuespec=RegExp(mode=RegExp.prefix), title=_("Inclusion patterns"), help=_( "Define patterns for including kernel configuration parameters in the " "inventory." ), ), ), ( "exclude_patterns", ListOfStrings( valuespec=RegExp(mode=RegExp.prefix), title=_("Exclusion patterns"), help=_( "Define patterns for excluding kernel configuration parameters from the " "inventory." ), ), ), ], optional_keys=False, )
def _valuespec_special_agents_salesforce(): return Dictionary( title=_("Salesforce"), help=_("This rule selects the special agent for Salesforce."), elements=[ ( "instances", ListOfStrings( title=_("Instances"), allow_empty=False, ), ), ], optional_keys=[], )
def _valuespec_inventory_services_rules(): return Dictionary( title=_("Windows service discovery"), elements=[ ( "services", ListOfStrings( title=_("Services (Regular Expressions)"), help= _("Regular expressions matching the begining of the internal name " "or the description of the service. " "If no name is given then this rule will match all services. The " "match is done on the <i>beginning</i> of the service name. It " "is done <i>case sensitive</i>. You can do a case insensitive match " "by prefixing the regular expression with <tt>(?i)</tt>. Example: " "<tt>(?i).*mssql</tt> matches all services which contain <tt>MSSQL</tt> " "or <tt>MsSQL</tt> or <tt>mssql</tt> or..."), orientation="horizontal", ), ), ( "state", DropdownChoice( choices=[ ("running", _("Running")), ("stopped", _("Stopped")), ], title=_("Create check if service is in state"), ), ), ( "start_mode", DropdownChoice( choices=[ ("auto", _("Automatic")), ("demand", _("Manual")), ("disabled", _("Disabled")), ], title=_("Create check if service is in start mode"), ), ), ], help= _("This rule can be used to configure the inventory of the windows services check. " "You can configure specific windows services to be monitored by the windows check by " "selecting them by name, current state during the inventory, or start mode." ), )
def _parameter_valuespec_solaris_services(): return Dictionary(elements=[ ("additional_servicenames", ListOfStrings( title=_("Alternative names for the service"), help= _("Here you can specify alternative names that the service might have. " "This helps when the exact spelling of the services can changed from " "one version to another."), )), ("states", ListOf( Tuple( orientation="horizontal", elements=[ DropdownChoice( title=_("Expected state"), choices=[ (None, _("Ignore the state")), ("online", _("Online")), ("disabled", _("Disabled")), ("maintenance", _("Maintenance")), ("legacy_run", _("Legacy run")), ], ), DropdownChoice( title=_("STIME"), choices=[ (None, _("Ignore")), (True, _("Has changed")), (False, _("Did not changed")), ], ), MonitoringState(title=_("Resulting state"), ), ], ), title=_("Services states"), help= _("You can specify a separate monitoring state for each possible " "combination of service state. If you do not use this parameter, " "then only online/legacy_run will be assumed to be OK."), )), ("else", MonitoringState( title=_("State if no entry matches"), default_value=2, )), ], )
def _discovery_parameters_valuespec_alertmanager(): return Dictionary( title=_("Alertmanager discovery"), elements=[ ("group_services", CascadingDropdown( title=_("Service creation"), choices=[ (True, _("Create services for alert rule groups"), Dictionary( elements=[ ("min_amount_rules", Integer( title= _("Minimum amount of alert rules in a group to create a group service" ), minvalue=1, default_value=3, help=_( "Below the specified value alert rules will be monitored as a" "single service."), )), ("no_group_services", ListOfStrings(title=_( "Don't create a group service for the following groups" ), )), ], optional_keys=[], )), (False, _("Create one service per alert rule"), FixedValue( {}, title=_("Enabled"), totext="", )), ], )), ("summary_service", FixedValue( True, title=_("Create a summary service for all alert rules"), totext="", )), ], optional_keys=["summary_service"], default_keys=["summary_service"], )
def _parameter_valuespec_ipsecvpn(): return Transform( Dictionary( elements=[("levels", Tuple( title=_("Levels for number of down channels"), elements=[ Integer(title=_("Warning at"), default_value=1), Integer(title=_("Critical at"), default_value=2), ], )), ("tunnels_ignore_levels", ListOfStrings(title=_("Tunnels which ignore levels")))], optional_keys=[], ), forth=lambda params: isinstance(params, dict) and params or {"levels": params}, )
def __init__(self, **kwargs): kwargs["elements"] = [ ( "port", Integer( title=_("TCP port"), minvalue=1, maxvalue=65535, default_value=kwargs.pop("tcp_port", 6557), ), ), ( "only_from", ListOfStrings( title=_("Restrict access to IP addresses"), help=_( "The access to Livestatus via TCP will only be allowed from the " "configured source IP addresses. You can either configure specific " "IP addresses or networks in the syntax <tt>10.3.3.0/24</tt>." ), valuespec=IPNetwork(), orientation="horizontal", allow_empty=False, default_value=["0.0.0.0", "::/0"], ), ), ( "tls", FixedValue( value=True, title=_("Encrypt communication"), totext=_("Encrypt TCP Livestatus connections"), help=_( "Since Check_MK 1.6 it is possible to encrypt the TCP Livestatus " "connections using SSL. This is enabled by default for sites that " "enable Livestatus via TCP with 1.6 or newer. Sites that already " "have this option enabled keep the communication unencrypted for " "compatibility reasons. However, it is highly recommended to " "migrate to an encrypted communication." ), ), ), ] kwargs["optional_keys"] = ["only_from", "tls"] super().__init__(**kwargs)
def valuespec_alert_remapping(): return ListOf( Dictionary( elements=[ ( "rule_names", ListOfStrings( title=_("Alert rule names"), help=_("A list of rule names as defined in Alertmanager."), ), ), ( "map", Dictionary( title=_("States"), elements=[ ("inactive", MonitoringState(title="inactive")), ("pending", MonitoringState(title="pending")), ("firing", MonitoringState(title="firing")), ("none", MonitoringState(title="none")), ("n/a", MonitoringState(title="n/a")), ], optional_keys=[], ), ), ], optional_keys=[], ), title=_("Remap alert rule states"), add_label=_("Add mapping"), help=_("Configure the monitoring state for Alertmanager rules."), allow_empty=False, default_value=[ { "map": { "inactive": 2, "pending": 2, "firing": 0, "none": 2, "n/a": 2, }, "rule_names": ["Watchdog"], } ], )
def _parameter_valuespec_win_license(): return Dictionary(elements=[ ("status", ListOfStrings( title=_("Allowed license states"), help=_("Here you can specify the allowed license states for windows."), default_value=['Licensed', 'Initial grace period'], )), ("expiration_time", Tuple( title=_("Time until license expiration"), help=_("Remaining days until the Windows license expires"), elements=[ Age(title=_("Warning at"), default_value=14 * 24 * 60 * 60), Age(title=_("Critical at"), default_value=7 * 24 * 60 * 60) ], )), ],)
def _vs_aws_tags(title): return ListOf( valuespec=Tuple( help=_( "How to configure AWS tags please see " "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html" ), orientation="horizontal", elements=[ TextInput(title=_("Key")), ListOfStrings(title=_("Values"), orientation="horizontal"), ], ), add_label=_("Add new tag"), movable=False, title=title, validate=validate_aws_tags, )
def _valuespec_inventory_fujitsu_ca_ports(): return Dictionary( title=_("Discovery of Fujtsu storage CA ports"), elements=[ ("indices", ListOfStrings(title=_("CA port indices"))), ("modes", DualListChoice( title=_("CA port modes"), choices=[ ("CA", _("CA")), ("RA", _("RA")), ("CARA", _("CARA")), ("Initiator", _("Initiator")), ], rows=4, size=30, )), ], )
def valuespec(cls): def convert_to_vs(value): if value.get("rule_id") is None: return None bi_pack = get_cached_bi_packs().get_pack_of_rule(value["rule_id"]) if bi_pack is None: return None return ( (bi_pack.id, value["rule_id"]), value["params"]["arguments"], ) def convert_from_vs(value): return { "type": cls.type(), "params": { "arguments": value[1], }, "rule_id": value[0][1] } return Transform( Tuple( elements=[ Transform( CascadingDropdown( title=_("Rule:"), orientation="horizontal", choices=cls._allowed_rule_choices(), sorted=True, ), ), ListOfStrings( orientation="horizontal", size=80, title=_("Arguments:"), ), ], validate=cls._validate_rule_call, ), title=_("Call a rule"), forth=convert_to_vs, back=convert_from_vs, )
def valuespec(cls): return Dictionary( elements=[("arguments", ListOf( Transform( Tuple(elements=[ TextInput(title=_("Keyword")), ListOfStrings( title=_("Values"), orientation="horizontal", ) ]), forth=cls._convert_to_vs, back=cls._convert_from_vs, ), magic="#keys#", ))], optional_keys=[], )
def valuespec(self): return Dictionary( title=_("Windows Service Discovery"), elements=[ ('services', ListOfStrings( title=_("Services (Regular Expressions)"), help= _('Regular expressions matching the begining of the internal name ' 'or the description of the service. ' 'If no name is given then this rule will match all services. The ' 'match is done on the <i>beginning</i> of the service name. It ' 'is done <i>case sensitive</i>. You can do a case insensitive match ' 'by prefixing the regular expression with <tt>(?i)</tt>. Example: ' '<tt>(?i).*mssql</tt> matches all services which contain <tt>MSSQL</tt> ' 'or <tt>MsSQL</tt> or <tt>mssql</tt> or...'), orientation="horizontal", )), ('state', DropdownChoice( choices=[ ('running', _('Running')), ('stopped', _('Stopped')), ], title=_("Create check if service is in state"), )), ('start_mode', DropdownChoice( choices=[ ('auto', _('Automatic')), ('demand', _('Manual')), ('disabled', _('Disabled')), ], title=_("Create check if service is in start mode"), )), ], help= _('This rule can be used to configure the inventory of the windows services check. ' 'You can configure specific windows services to be monitored by the windows check by ' 'selecting them by name, current state during the inventory, or start mode.' ), )
def _valuespec_special_agents_couchbase(): return Dictionary( title=_("Couchbase servers"), help=_( "This rule allows to select a Couchbase server to monitor as well as " "configure buckets for further checks"), elements=[ ( "buckets", ListOfStrings(title=_("Bucket names"), help=_("Name of the Buckets to monitor.")), ), ( "timeout", Integer(title=_("Timeout"), default_value=10, help=_("Timeout for requests in seconds.")), ), ( "port", Integer( title=_("Port"), default_value=8091, help=_("The port that is used for the api call."), ), ), ( "authentication", Tuple( title=_("Authentication"), help=_( "The credentials for api calls with authentication."), elements=[ TextInput(title=_("Username"), allow_empty=False), PasswordFromStore(title=_("Password of the user"), allow_empty=False), ], ), ), ], )
def _parameter_valuespec_esx_vsphere_objects_count(): return Dictionary( optional_keys=False, elements=[ ("distribution", ListOf( Dictionary( optional_keys=False, elements=[("vm_names", ListOfStrings(title=_("VMs"))), ("hosts_count", Integer(title=_("Number of hosts"), default_value=2)), ("state", MonitoringState(title=_("State if violated"), default_value=1))], ), title=_("VM distribution"), help=_( "You can specify lists of VM names and a number of hosts," " to make sure the specfied VMs are distributed across at least so many hosts." " E.g. provide two VM names and set 'Number of hosts' to two," " to make sure those VMs are not running on the same host."))), ], )
def _parameter_valuespec_datadog_monitors() -> Dictionary: return Dictionary( [ ( "state_mapping", Dictionary( [ ( datadog_state, MonitoringState( title=datadog_state, default_value=checkmk_state, ), ) for datadog_state, checkmk_state in _DEFAULT_DATADOG_AND_CHECKMK_STATES ], title=_("Map monitor states to Checkmk monitoring states"), optional_keys=False, ), ), ( "tags_to_show", ListOfStrings( valuespec=RegExp( RegExp.prefix, size=30, allow_empty=False, ), title=_("Datadog tags shown in service output"), help=_( "This option allows you to configure which Datadog tags will be shown in " "the service output. This is done by entering regular expressions matching " "one or more Datadog tags. Any matching tag will be displayed in the " "service output." ), ), ), ], )
def _valuespec_agent_config_windows_patch_day(): return Alternative( title=_("Windows Patch Day (Windows)"), help= _("This will deploy the agent plugin <tt>windows_patch_day.ps1</tt> to check the Windows update installation time." "As option you can set the maximum amount of updates in history that should be transfered. It is also possible to" "filter unwanted update entries like the daily Windows Defender Pattern updates." ), elements=[ Dictionary( title=_("Deploy plugin for Windows patch day"), elements=[ ("updatecount", Integer( title=_("Update history count"), unit=_("Updates"), default_value=40, )), ("filterstring", ListOfStrings( title=_("Unwanted updates (Regular Expressions)"), help= _('Regular expressions matching the begining of the installed update name.' ), orientation="horizontal", default_value=[ "Security Intelligence-Update für Microsoft Defender Antivirus", "Update für Microsoft Defender Antivirus-Antischadsoftwareplattform", "Windows-Tool zum Entfernen bösartiger Software" ], )), ], optional_keys=False, ), FixedValue(None, title=_("Do not deploy the Windows Patch day plugin"), totext=_("(disabled)")), ], )
def _vs_jira_projects(title): return ListOf( valuespec=Tuple( orientation="horizontal", elements=[ TextInput( title=_("Project"), help=_( "Enter the full name of the " "project here. You can find " "the name in Jira within " '"Projects" - "View all ' 'projects" - column: "Project". ' "This field is case " "insensitive" ), allow_empty=False, regex="^[^']*$", regex_error=_("Single quotes are not allowed here."), ), ListOfStrings( title=_("Workflows"), help=_('Enter the workflow name for the project here. E.g. "in progress".'), valuespec=TextInput( allow_empty=False, regex="^[^']*$", regex_error=_("Single quotes are not allowed here."), ), orientation="horizontal", ), ], ), add_label=_("Add new project"), movable=False, title=title, validate=validate_aws_tags, )
def _parameter_valuespec_services_summary(): return Dictionary( title=_('Autostart Services'), elements=[ ('ignored', ListOfStrings( title=_("Ignored autostart services"), help=_('Regular expressions matching the begining of the internal name ' 'or the description of the service. ' 'If no name is given then this rule will match all services. The ' 'match is done on the <i>beginning</i> of the service name. It ' 'is done <i>case sensitive</i>. You can do a case insensitive match ' 'by prefixing the regular expression with <tt>(?i)</tt>. Example: ' '<tt>(?i).*mssql</tt> matches all services which contain <tt>MSSQL</tt> ' 'or <tt>MsSQL</tt> or <tt>mssql</tt> or...'), orientation="horizontal", )), ('state_if_stopped', MonitoringState( title=_("Default state if stopped autostart services are found"), default_value=0, )), ], )
vs_elements_if_groups_matches: List[DictionaryEntry] = [ ("iftype", Transform( DropdownChoice( title=_("Select interface port type"), choices=ListChoice.dict_choices(defines.interface_port_types()), help=_("Only interfaces with the given port type are put into this group. " "For example 53 (propVirtual)."), ), forth=str, back=int, )), ("items", ListOfStrings( title=_("Restrict interface items"), help=_("Only interface with these item names are put into this group."), )), ] vs_elements_if_groups_group = [ ("group_name", TextAscii( title=_("Group name"), help=_("Name of group in service description"), allow_empty=False, )), ("group_presence", DropdownChoice( title=_("Group interface presence"), help=_("Determine whether the group interface is created as an " "separate service or not. In second case the choosen interface "
def _valuespec_inventory_if_rules(): return Transform( Dictionary( title=_("Network Interface and Switch Port Discovery"), elements=[ ('item_appearance', DropdownChoice( title=_("Appearance of network interface"), help= _("This option lets Check_MK use either the interface description, alias or " " port number as item. The port number is the fallback/default." "used anyway."), choices=[ ('descr', _('Use description')), ('alias', _('Use alias')), ('index', _('Use index')), ], default_value='index', )), ("pad_portnumbers", DropdownChoice( choices=[ (True, _('Pad port numbers with zeros')), (False, _('Do not pad')), ], title=_("Port numbers"), help= _("If this option is activated then Check_MK will pad port numbers of " "network interfaces with zeroes so that all port descriptions from " "all ports of a host or switch have the same length and thus sort " "currectly in the GUI. In versions prior to 1.1.13i3 there was no " "padding. You can switch back to the old behaviour by disabling this " "option. This will retain the old service descriptions and the old " "performance data."), )), ("match_alias", ListOfStrings( title=_("Match interface alias (regex)"), help= _("Only discover interfaces whose alias matches one of the configured " "regular expressions. The match is done on the beginning of the alias. " "This allows you to select interfaces based on the alias without having " "the alias be part of the service description."), orientation="horizontal", valuespec=RegExp( size=32, mode=RegExp.prefix, ), )), ("match_desc", ListOfStrings( title=_("Match interface description (regex)"), help= _("Only discover interfaces whose the description matches one of the configured " "regular expressions. The match is done on the beginning of the description. " "This allows you to select interfaces based on the description without having " "the alias be part of the service description."), orientation="horizontal", valuespec=RegExp( size=32, mode=RegExp.prefix, ), )), ("portstates", ListChoice( title=_("Network interface port states to discover"), help= _("When doing discovery on switches or other devices with network interfaces " "then only ports found in one of the configured port states will be added to the monitoring. " "Note: the state <i>admin down</i> is in fact not an <tt>ifOperStatus</tt> but represents the " "<tt>ifAdminStatus</tt> of <tt>down</tt> - a port administratively switched off. If you check this option " "then an alternate version of the check is being used that fetches the <tt>ifAdminState</tt> in addition. " "This will add about 5% of additional SNMP traffic."), choices=defines.interface_oper_states(), toggle_all=True, default_value=['1'], )), ("porttypes", DualListChoice( title=_("Network interface port types to discover"), help= _("When doing discovery on switches or other devices with network interfaces " "then only ports of the specified types will be created services for." ), choices=defines.interface_port_types(), rows=40, default_value=[ '6', '32', '62', '117', '127', '128', '129', '180', '181', '182', '205', '229' ], )), ("rmon", DropdownChoice( choices=[ (True, _("Create extra service with RMON statistics data (if available for the device)" )), (False, _('Do not create extra services')), ], title=_("Collect RMON statistics data"), help= _("If you enable this option, for every RMON capable switch port an additional service will " "be created which is always OK and collects RMON data. This will give you detailed information " "about the distribution of packet sizes transferred over the port. Note: currently " "this extra RMON check does not honor the inventory settings for switch ports. In a future " "version of Check_MK RMON data may be added to the normal interface service and not add " "an additional service."), )), ], help= _('This rule can be used to control the inventory for network ports. ' 'You can configure the port types and port states for inventory ' 'and the use of alias or description as service name.'), ), forth=_transform_discovery_if_rules, )