From bbac7b11cae0ed2f2121e109a1bce753230b50f8 Mon Sep 17 00:00:00 2001 From: Marsell Kukuljevic Date: Mon, 12 May 2025 21:15:08 +0200 Subject: [PATCH] SCO-241: Update domain_checks plugin to use 2.3.0 CheckMK plugin API (since 2.4.0 requires this API). Also merge discovery and check GUIs into single pages. --- domains/domain_checks-0.1.2.mkp | Bin 3792 -> 0 bytes domains/domain_checks-0.2.0.mkp | Bin 0 -> 4376 bytes .../plugins/agent_based/domains_expiry.py | 80 ------------- .../agent_based/domains_nameservers.py | 100 ----------------- .../domains/agent_based/domains_expiry.py | 64 +++++++++++ .../agent_based/domains_nameservers.py | 72 ++++++++++++ .../domains/libexec/agent_domains_expiry | 42 +++++++ .../domains/libexec/agent_domains_nameservers | 47 ++++++++ .../domains/rulesets/domains_expiry.py | 52 +++++++++ .../domains/rulesets/domains_nameservers.py | 70 ++++++++++++ .../server_side_calls/agent_domains_expiry.py | 23 ++++ .../agent_domains_nameservers.py | 34 ++++++ .../agents/special/agent_domains_expiry | 8 -- .../agents/special/agent_domains_nameservers | 8 -- .../check_mk/checks/agent_domains_expiry | 7 -- .../check_mk/checks/agent_domains_nameservers | 7 -- .../web/plugins/wato/domains_expiry.py | 84 -------------- .../web/plugins/wato/domains_nameservers.py | 105 ------------------ 18 files changed, 404 insertions(+), 399 deletions(-) delete mode 100755 domains/domain_checks-0.1.2.mkp create mode 100755 domains/domain_checks-0.2.0.mkp delete mode 100644 domains/local/lib/check_mk/base/plugins/agent_based/domains_expiry.py delete mode 100644 domains/local/lib/check_mk/base/plugins/agent_based/domains_nameservers.py create mode 100644 domains/local/lib/python3/cmk_addons/plugins/domains/agent_based/domains_expiry.py create mode 100644 domains/local/lib/python3/cmk_addons/plugins/domains/agent_based/domains_nameservers.py create mode 100755 domains/local/lib/python3/cmk_addons/plugins/domains/libexec/agent_domains_expiry create mode 100755 domains/local/lib/python3/cmk_addons/plugins/domains/libexec/agent_domains_nameservers create mode 100644 domains/local/lib/python3/cmk_addons/plugins/domains/rulesets/domains_expiry.py create mode 100644 domains/local/lib/python3/cmk_addons/plugins/domains/rulesets/domains_nameservers.py create mode 100644 domains/local/lib/python3/cmk_addons/plugins/domains/server_side_calls/agent_domains_expiry.py create mode 100644 domains/local/lib/python3/cmk_addons/plugins/domains/server_side_calls/agent_domains_nameservers.py delete mode 100755 domains/local/share/check_mk/agents/special/agent_domains_expiry delete mode 100755 domains/local/share/check_mk/agents/special/agent_domains_nameservers delete mode 100644 domains/local/share/check_mk/checks/agent_domains_expiry delete mode 100644 domains/local/share/check_mk/checks/agent_domains_nameservers delete mode 100644 domains/local/share/check_mk/web/plugins/wato/domains_expiry.py delete mode 100644 domains/local/share/check_mk/web/plugins/wato/domains_nameservers.py diff --git a/domains/domain_checks-0.1.2.mkp b/domains/domain_checks-0.1.2.mkp deleted file mode 100755 index 668b10e0028484399e28a753de6e855dba2dd4cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3792 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 diff --git a/domains/domain_checks-0.2.0.mkp b/domains/domain_checks-0.2.0.mkp new file mode 100755 index 0000000000000000000000000000000000000000..432f5c44d9d998145b453d09ca976f40e326022d GIT binary patch literal 4376 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 DWq+H5 literal 0 HcmV?d00001 diff --git a/domains/local/lib/check_mk/base/plugins/agent_based/domains_expiry.py b/domains/local/lib/check_mk/base/plugins/agent_based/domains_expiry.py deleted file mode 100644 index 2e39b46..0000000 --- a/domains/local/lib/check_mk/base/plugins/agent_based/domains_expiry.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025 Spearhead Systems SRL - -import datetime -from cmk.base.plugins.agent_based.agent_based_api.v1 import register, Result, Service, State - - -# Incoming agent output takes the form: -# -# google.com: -# 2028-09-14 -# yahoo.com: -# 2026-01-19 -# -# The domains being queries end with a colon. The expiry response does not. -# -# Return a dictionary which uses domain as key, and returns an expiry as -# datetime.date. -def parse_domains_expiry(string_table): - expiries = {} - current_domain = "" - - for line in string_table: - line = line[0] - - domain = line[:-1] - ending = line[-1] - - if ending == ":": - current_domain = domain - else: - date = datetime.datetime.strptime(line, '%Y-%m-%d').date() - expiries[current_domain] = date - - return expiries - - -def discover_domains_expiry(section): - for domain, date in section.items(): - yield Service(item=domain) - - -def check_domains_expiry(item, params, section): - expiry = section.get(item) - if not expiry: - yield Result(state=State.WARN, summary="Expiry not found in whois") - return - - alert_delta = params.get("days_remaining") - if not alert_delta: - yield Result(state=State.WARN, summary="No expiry check rule configured") - return - - valid_days_left = (expiry - datetime.date.today()).days - summary = "Domain expires in %s days" % valid_days_left - state = State.OK - - if valid_days_left < alert_delta[1]: - state = State.CRIT - elif valid_days_left < alert_delta[0]: - state = State.WARN - - yield Result(state=state, summary=summary) - - -register.agent_section( - name="domains_expiry", - parse_function=parse_domains_expiry -) - -register.check_plugin( - name="domains_expiry", - service_name="Expiry for Domain '%s'", - - discovery_function=discover_domains_expiry, - - check_function=check_domains_expiry, - check_default_parameters={}, - check_ruleset_name="domains_expiry", -) diff --git a/domains/local/lib/check_mk/base/plugins/agent_based/domains_nameservers.py b/domains/local/lib/check_mk/base/plugins/agent_based/domains_nameservers.py deleted file mode 100644 index 9064da6..0000000 --- a/domains/local/lib/check_mk/base/plugins/agent_based/domains_nameservers.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025 Spearhead Systems SRL - -from cmk.base.plugins.agent_based.agent_based_api.v1 import register, Result, Service, State - - -# Incoming agent output takes the form: -# -# google.com: -# ns3.google.com. -# ns1.google.com. -# ns4.google.com. -# ns2.google.com. -# yahoo.com: -# ns2.yahoo.com. -# ns1.yahoo.com. -# ns3.yahoo.com. -# ns4.yahoo.com. -# ns5.yahoo.com. -# -# The domains being queries end with a colon. The nameserver responses end with -# a period. Nameservers follow the domain being queried. -# -# Return a dictionary which uses domain as key, and returns a list of associated -# nameservers for that key. -def parse_domains_nameservers(string_table): - nameservers = {} - current_domain = "" - - for line in string_table: - domain = line[0][:-1] - ending = line[0][-1] - - if ending == ":": - current_domain = domain - nameservers[current_domain] = [] - elif ending == ".": - nameservers[current_domain].append(domain) - - return nameservers - - -def discover_domains_nameservers(section): - for domain, nameservers in section.items(): - yield Service(item=domain) - - -def check_domains_nameservers(item, params, section): - nameservers = section.get(item) - if not nameservers: - yield Result(state=State.WARN, summary="Nameservers missing") - return - - configs = params.get("domain_nameservers") - if not configs: - yield Result(state=State.WARN, summary="No nameservers check rule configured") - return - - alert_level = params["alert_level"] - - nameservers.sort() - - domain_found = False - - for config in configs: - domains = config["domains"] - - if item in domains: - domain_found = True - - expected_nameservers = config["nameservers"] - expected_nameservers.sort() - - if expected_nameservers == nameservers: - yield Result(state=State.OK, summary="Expected nameservers present") - else: - expected_str = ", ".join(expected_nameservers) - found_str = ", ".join(nameservers) - yield Result(state=State(alert_level), summary="Mismatch in nameservers. Expected: [%s], Found: [%s]" % (expected_str, found_str)) - - if not domain_found: - yield Result(state=State.WARN, summary="Domain not found in any nameserver check rule") - - - -register.agent_section( - name="domains_nameservers", - parse_function=parse_domains_nameservers -) - -register.check_plugin( - name="domains_nameservers", - service_name="Nameservers for Domain '%s'", - - discovery_function=discover_domains_nameservers, - - check_function=check_domains_nameservers, - check_default_parameters={}, - check_ruleset_name="domains_nameservers", -) diff --git a/domains/local/lib/python3/cmk_addons/plugins/domains/agent_based/domains_expiry.py b/domains/local/lib/python3/cmk_addons/plugins/domains/agent_based/domains_expiry.py new file mode 100644 index 0000000..22e8822 --- /dev/null +++ b/domains/local/lib/python3/cmk_addons/plugins/domains/agent_based/domains_expiry.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL + +import datetime +import json +from cmk.agent_based.v2 import Result, Service, State, CheckPlugin, AgentSection + + +# Incoming agent output is JSON of the form: +# +# {"domain": "google.com", "state": "CRIT", "expires": "2028-09-14"} +# +# "domain" and "state" are always present, whereas "expires" is optional +# (e.g. most ccTLDs do not return expiry information). The "state" is one of +# (OK, WARN, CRIT, UNKNOWN). +# +# Return a dictionary which uses domain as key, and the rest of the dictionary +# as a value. +def parse_domains_expiry(string_table): + results = {} + + for [line] in string_table: + result = json.loads(line) + results[result["domain"]] = result + + return results + + +def discover_domains_expiry(section): + for domain in section.keys(): + yield Service(item=domain) + + +def check_domains_expiry(item, params, section): + data = section.get(item) + if not data: + yield Result(state=State.WARN, summary="Expiry not found in whois") + return + + state = State[data["state"]] + expiry = data.get("expires") + + if expiry: + summary = f"Expires on {expiry}" + else: + summary = "No expiry found" + + yield Result(state=state, summary=summary) + + +agent_section_domains_expiry = AgentSection( + name="domains_expiry", + parse_function=parse_domains_expiry +) + +check_plugin_domains_expiry = CheckPlugin( + name="domains_expiry", + service_name="Expiry for Domain '%s'", + + discovery_function=discover_domains_expiry, + + check_function=check_domains_expiry, + check_default_parameters={}, +) diff --git a/domains/local/lib/python3/cmk_addons/plugins/domains/agent_based/domains_nameservers.py b/domains/local/lib/python3/cmk_addons/plugins/domains/agent_based/domains_nameservers.py new file mode 100644 index 0000000..65cba11 --- /dev/null +++ b/domains/local/lib/python3/cmk_addons/plugins/domains/agent_based/domains_nameservers.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL + +import json +from cmk.agent_based.v2 import Result, Service, State, CheckPlugin, AgentSection + + +# Incoming agent output is JSON of the form: +# +# {"domain": "yahoo.com", "state": "WARN", "unexpected": ["ns5.yahoo.com"], "missing": ["ns6.yahoo.com"]} +# +# Each JSON row is for a single domain. Each row always has "domain" and "state" +# fields, with optional "unexpected" and "missing" fields. The "state" field is +# one of "OK", "WARN", "CRIT", or "UNKNOWN". The "unexpected" field is a list of +# unwanted nameservers that are present in the dig query result, whereas the +# "missing" field are wanted nameservers that are not present. +# +# Return a dictionary which uses domain as key, and a dictionary of +# state/unexpected/missing. +def parse_domains_nameservers(string_table): + results = {} + + for [line] in string_table: + result = json.loads(line) + results[result["domain"]] = result + + return results + + +def discover_domains_nameservers(section): + for domain in section.keys(): + yield Service(item=domain) + + +def check_domains_nameservers(item, params, section): + data = section.get(item) + if not data: + yield Result(state=State.WARN, summary="Nameservers missing") + return + + state = State[data["state"]] + unexpected = data.get("unexpected") + missing = data.get("missing") + + summary = "" + + if unexpected: + summary += f"Unexpected nameserver(s) found: {''.join(unexpected)}. " + + if missing: + summary += f"Expected nameserver(s) missing: {''.join(missing)}. " + + if not summary: + summary = "All expected nameservers found, none unexpected found" + + yield Result(state=state, summary=summary) + + +agent_section_triton_wedge = AgentSection( + name="domains_nameservers", + parse_function=parse_domains_nameservers +) + +check_plugin_domains_nameservers = CheckPlugin( + name="domains_nameservers", + service_name="Nameservers for Domain '%s'", + + discovery_function=discover_domains_nameservers, + + check_function=check_domains_nameservers, + check_default_parameters={}, +) diff --git a/domains/local/lib/python3/cmk_addons/plugins/domains/libexec/agent_domains_expiry b/domains/local/lib/python3/cmk_addons/plugins/domains/libexec/agent_domains_expiry new file mode 100755 index 0000000..6307eff --- /dev/null +++ b/domains/local/lib/python3/cmk_addons/plugins/domains/libexec/agent_domains_expiry @@ -0,0 +1,42 @@ +#!/bin/bash +# Copyright (C) 2025 Spearhead Systems SRL + +set -eu + +if [[ $# < 3 ]]; then + echo "Usage: ${@: 0:1} " 1>&2 + echo "Example: ${@: 0:1} google.com yahoo.com 2024-05-07 2024-05-21" 1>&2 + exit 1 +fi + +# Extract from args +domains="${@: 1:$#-2}" +warn="${@: -1:1}" +crit="${@: -2:1}" + +echo "<<>>" + +for domain in $domains; do + echo -n "{\"domain\": \"$domain\", \"state\": \"" + + # Unfortunately, there's no actual format for whois entries, so this is a + # best-effort based on things seen in the wild. Note that ccTLDs usually + # do not publish expiry dates at all. + expires=$(whois "$domain" | grep 'Expir.*' | head -1 | grep -Eo '[0-9]{4}-[0-9]{2}-[0-9]{2}' || true) + + if [[ "$expires" == "" ]]; then + echo -n "UNKNOWN" + elif [[ "$expires" < "$crit" ]]; then + echo -n "CRIT" + elif [[ "$expires" < "$warn" ]]; then + echo -n "WARN" + else + echo -n "OK" + fi + + if [[ "$expires" == "" ]]; then + echo "\"}" + else + echo "\", \"expires\": \"$expires\"}" + fi +done diff --git a/domains/local/lib/python3/cmk_addons/plugins/domains/libexec/agent_domains_nameservers b/domains/local/lib/python3/cmk_addons/plugins/domains/libexec/agent_domains_nameservers new file mode 100755 index 0000000..a928991 --- /dev/null +++ b/domains/local/lib/python3/cmk_addons/plugins/domains/libexec/agent_domains_nameservers @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL + + +import json +import optparse +import subprocess + + +print("<<>>") + + +parser = optparse.OptionParser() +parser.add_option('-d', '--domains', action="append") +parser.add_option('-n', '--nameservers', action="append") +parser.add_option('-a', '--alert', default="WARN") +opts, _ = parser.parse_args() + +alert = opts.alert +assert ["OK", "WARN", "CRIT", "UNKNOWN"].count(alert) + +for domain_str, nameserver_str in zip(opts.domains, opts.nameservers): + domains = domain_str.split(",") + nameservers = set(nameserver_str.split(",")) + + for domain in domains: + dig_result = subprocess.run(["dig", "+short", "NS", domain], capture_output=True) + dig_ns = set(dig_result.stdout.decode("utf-8").split('.\n')) + dig_ns.remove('') + + result = { + "domain": domain, + "state": "OK", + } + + if dig_ns != nameservers: + result["state"] = alert + + unexpected = list(dig_ns - nameservers) + missing = list(nameservers - dig_ns) + + if unexpected: + result["unexpected"] = unexpected + if missing: + result["missing"] = missing + + print(json.dumps(result)) diff --git a/domains/local/lib/python3/cmk_addons/plugins/domains/rulesets/domains_expiry.py b/domains/local/lib/python3/cmk_addons/plugins/domains/rulesets/domains_expiry.py new file mode 100644 index 0000000..79f051b --- /dev/null +++ b/domains/local/lib/python3/cmk_addons/plugins/domains/rulesets/domains_expiry.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL + +from cmk.rulesets.v1.form_specs.validators import LengthInRange, NumberInRange +from cmk.rulesets.v1.form_specs import Dictionary, DictElement, List, String, Integer, DefaultValue +from cmk.rulesets.v1.rule_specs import SpecialAgent, Topic, Title, Help + + +def _formspec(): + return Dictionary( + title=Title("Domains Expiry"), + elements={ + "domains": DictElement( + required=True, + parameter_form=List( + title=Title("Domain names"), + help_text=Help("List of domain names to check"), + editable_order=False, + custom_validate=(LengthInRange(min_value=1),), + element_template=String( + custom_validate=(LengthInRange(min_value=3),), + ), + ), + ), + "days_warn": DictElement( + required=True, + parameter_form=Integer( + title=Title("Warn if expires within days"), + help_text=Help("If there are fewer days until one of the above domains expires, issue an alert"), + custom_validate=(NumberInRange(min_value=0),), + prefill=DefaultValue(30), + ), + ), + "days_crit": DictElement( + required=True, + parameter_form=Integer( + title=Title("Crit if expires within days"), + help_text=Help("If there are fewer days until one of the above domains expires, issue an alert"), + custom_validate=(NumberInRange(min_value=0),), + prefill=DefaultValue(7), + ), + ), + } + ) + + +rule_spec_agent_config_domains_expiry = SpecialAgent( + topic=Topic.NETWORKING, + name="domains_expiry", + title=Title("Domains Expiry"), + parameter_form=_formspec, +) diff --git a/domains/local/lib/python3/cmk_addons/plugins/domains/rulesets/domains_nameservers.py b/domains/local/lib/python3/cmk_addons/plugins/domains/rulesets/domains_nameservers.py new file mode 100644 index 0000000..8e7a80b --- /dev/null +++ b/domains/local/lib/python3/cmk_addons/plugins/domains/rulesets/domains_nameservers.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL + +from cmk.rulesets.v1.form_specs.validators import LengthInRange +from cmk.rulesets.v1.form_specs import Dictionary, DictElement, SingleChoice, SingleChoiceElement, List, String, DefaultValue +from cmk.rulesets.v1.rule_specs import SpecialAgent, Topic, Title, Help +from cmk.agent_based.v2 import State + + +def _formspec(): + return Dictionary( + title=Title("Domains Nameservers"), + elements={ + "domain_nameservers": DictElement( + required=True, + parameter_form=List( + title=Title("Domains to check"), + help_text=Help("List of domain names to check"), + editable_order=False, + custom_validate=(LengthInRange(min_value=1),), + element_template=Dictionary( + elements={ + "domains": DictElement( + required=True, + parameter_form=List( + title=Title("Domain names"), + help_text=Help("List of domain names the below nameservers apply to"), + custom_validate=(LengthInRange(min_value=1),), + element_template=String( + custom_validate=(LengthInRange(min_value=3),), + ), + ), + ), + "nameservers": DictElement( + required=True, + parameter_form=List( + title=Title("Nameservers"), + help_text=Help("List of nameservers that the above domain names should have"), + custom_validate=(LengthInRange(min_value=1),), + element_template=String( + custom_validate=(LengthInRange(min_value=3),), + ), + ), + ), + } + ) + ), + ), + "alert_level": DictElement( + required=True, + parameter_form=SingleChoice( + title=Title("Alert level used on mismatch"), + help_text=Help("Alert level used when there is a mismatch in domain name servers for a domain"), + prefill=DefaultValue(State.WARN.name), + elements=[ + SingleChoiceElement(name=State.CRIT.name, title=Title(State.CRIT.name)), + SingleChoiceElement(name=State.WARN.name, title=Title(State.WARN.name)), + SingleChoiceElement(name=State.OK.name, title=Title(State.OK.name)), + ], + ), + ), + }, + ) + +rule_spec_agent_config_domains_nameservers = SpecialAgent( + topic=Topic.NETWORKING, + name="domains_nameservers", + title=Title("Domains Nameservers"), + parameter_form=_formspec, +) diff --git a/domains/local/lib/python3/cmk_addons/plugins/domains/server_side_calls/agent_domains_expiry.py b/domains/local/lib/python3/cmk_addons/plugins/domains/server_side_calls/agent_domains_expiry.py new file mode 100644 index 0000000..1a74a02 --- /dev/null +++ b/domains/local/lib/python3/cmk_addons/plugins/domains/server_side_calls/agent_domains_expiry.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL + +from datetime import datetime, timedelta +from cmk.server_side_calls.v1 import noop_parser, SpecialAgentConfig, SpecialAgentCommand + + +def _agent_arguments(params, host_config): + today = datetime.today() + + args = params["domains"] + for key in ["days_crit", "days_warn"]: + date = (today + timedelta(days=params[key])).date() + args.append(str(date)) + + yield SpecialAgentCommand(command_arguments=args) + + +special_agent_domains_expiry = SpecialAgentConfig( + name="domains_expiry", + parameter_parser=noop_parser, + commands_function=_agent_arguments, +) diff --git a/domains/local/lib/python3/cmk_addons/plugins/domains/server_side_calls/agent_domains_nameservers.py b/domains/local/lib/python3/cmk_addons/plugins/domains/server_side_calls/agent_domains_nameservers.py new file mode 100644 index 0000000..7940629 --- /dev/null +++ b/domains/local/lib/python3/cmk_addons/plugins/domains/server_side_calls/agent_domains_nameservers.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL + +from cmk.server_side_calls.v1 import noop_parser, SpecialAgentConfig, SpecialAgentCommand + + +# Although the API makes it seem like we can yield several +# SpecialAgentCommands, the use of a table for _elems in +# lib/python3/cmk/base/sources/_builder.py (at least in 2.3.0p31) means that +# we can only emit one SpecialAgentCommands. This CheckMK bug means that +# SpecialAgentCommands() will be forgotten. +# +# Alas, that means we need to pack everything into a single command invocation. +def _agent_arguments(params, host_config): + args = [] + + alert_level = params["alert_level"] + + for ele in params["domain_nameservers"]: + domains = ele["domains"] + nameservers = ele["nameservers"] + + args.append("--domains=" + ",".join(domains)) + args.append("--nameservers=" + ",".join(nameservers)) + args.append("--alert=" + alert_level) + + yield SpecialAgentCommand(command_arguments=args) + + +special_agent_domains_nameservers = SpecialAgentConfig( + name="domains_nameservers", + parameter_parser=noop_parser, + commands_function=_agent_arguments, +) diff --git a/domains/local/share/check_mk/agents/special/agent_domains_expiry b/domains/local/share/check_mk/agents/special/agent_domains_expiry deleted file mode 100755 index e66e6e2..0000000 --- a/domains/local/share/check_mk/agents/special/agent_domains_expiry +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# Copyright (C) 2025 Spearhead Systems SRL - -echo "<<>>" -for domain in "$@"; do - echo "$domain:" - whois "$domain" | grep 'Expir.*' | head -1 | grep -Eo '[0-9]{4}-[0-9]{2}-[0-9]{2}' -done diff --git a/domains/local/share/check_mk/agents/special/agent_domains_nameservers b/domains/local/share/check_mk/agents/special/agent_domains_nameservers deleted file mode 100755 index df3141b..0000000 --- a/domains/local/share/check_mk/agents/special/agent_domains_nameservers +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# Copyright (C) 2025 Spearhead Systems SRL - -echo "<<>>" -for domain in "$@"; do - echo "$domain:" - dig +short NS "$domain" -done diff --git a/domains/local/share/check_mk/checks/agent_domains_expiry b/domains/local/share/check_mk/checks/agent_domains_expiry deleted file mode 100644 index 3674278..0000000 --- a/domains/local/share/check_mk/checks/agent_domains_expiry +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025 Spearhead Systems SRL - -def agent_domains_expiry_args(params, hostname, ipaddress): - return params["domains"] - -special_agent_info["domains_expiry"] = agent_domains_expiry_args diff --git a/domains/local/share/check_mk/checks/agent_domains_nameservers b/domains/local/share/check_mk/checks/agent_domains_nameservers deleted file mode 100644 index 8794c43..0000000 --- a/domains/local/share/check_mk/checks/agent_domains_nameservers +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025 Spearhead Systems SRL - -def agent_domains_nameservers_args(params, hostname, ipaddress): - return params["domains"] - -special_agent_info["domains_nameservers"] = agent_domains_nameservers_args diff --git a/domains/local/share/check_mk/web/plugins/wato/domains_expiry.py b/domains/local/share/check_mk/web/plugins/wato/domains_expiry.py deleted file mode 100644 index 6b263ff..0000000 --- a/domains/local/share/check_mk/web/plugins/wato/domains_expiry.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025 Spearhead Systems SRL - -import copy -from cmk.base.plugins.agent_based.agent_based_api.v1 import State -from cmk.gui.i18n import _ -from cmk.gui.plugins.wato.utils import ( - rulespec_registry, - HostRulespec, - RulespecGroupCheckParametersNetworking, - CheckParameterRulespecWithItem -) -from cmk.gui.watolib.rulespecs import Rulespec -from cmk.gui.valuespec import ( - Dictionary, - Integer, - ListOfStrings, - Tuple, - TextInput -) - - -def _valuespec_special_agents_domains_expiry_query(): - return Dictionary( - title=_("Domains Expiry Query"), - required_keys=["domains"], - elements=[ - ( - "domains", - ListOfStrings( - title=_("Domain names"), - help=_("List of domain names to check"), - allow_empty=False, - ), - ), - ] - ) - -def _valuespec_special_agents_domains_expiry_checks(): - return Dictionary( - title=_("Domains Expiry"), - required_keys=["days_remaining"], - elements=[ - ( - "days_remaining", - Tuple( - title=_("Days Remaining"), - help=_("If there are fewer days until one of the above domains expires, issue an alert"), - elements=[ - Integer( - title=_("Warn if fewer days than"), - minvalue=0, - default_value=30 - ), - Integer( - title=_("Crit if fewer days than"), - minvalue=0, - default_value=7 - ) - ] - ) - ), - ], - ) - - -rulespec_registry.register( - HostRulespec( - name="special_agents:domains_expiry", - group=RulespecGroupCheckParametersNetworking, - match_type='dict', - valuespec=_valuespec_special_agents_domains_expiry_query, - ) -) -rulespec_registry.register( - CheckParameterRulespecWithItem( - check_group_name="domains_expiry", - group=RulespecGroupCheckParametersNetworking, - match_type="dict", - parameter_valuespec=_valuespec_special_agents_domains_expiry_checks, - item_spec=lambda: TextInput(title=_("Expiry")), - title=lambda: _("Domains Expiry Checks"), - ) -) diff --git a/domains/local/share/check_mk/web/plugins/wato/domains_nameservers.py b/domains/local/share/check_mk/web/plugins/wato/domains_nameservers.py deleted file mode 100644 index 2013311..0000000 --- a/domains/local/share/check_mk/web/plugins/wato/domains_nameservers.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2025 Spearhead Systems SRL - -import copy -from cmk.base.plugins.agent_based.agent_based_api.v1 import State -from cmk.gui.i18n import _ -from cmk.gui.plugins.wato.utils import ( - rulespec_registry, - HostRulespec, - CheckParameterRulespecWithItem, - RulespecGroupCheckParametersNetworking, -) -from cmk.gui.watolib.rulespecs import Rulespec -from cmk.gui.valuespec import ( - Dictionary, - Integer, - ListOfStrings, - DropdownChoice, - Tuple, - ListOf, - TextInput -) - - -def _valuespec_special_agents_domains_nameservers_query(): - return Dictionary( - title=_("Domains Nameservers Query"), - required_keys=["domains"], - elements=[ - ( - "domains", - ListOfStrings( - title=_("Domain names"), - help=_("List of domain names to check"), - allow_empty=False, - ), - ), - ] - ) - -def _valuespec_special_agents_domains_nameservers_checks(): - return Dictionary( - title=_("Domains Nameservers Checks"), - required_keys=["domain_nameservers", "alert_level"], - elements=[ - ( - "domain_nameservers", - ListOf( - valuespec=Dictionary( - required_keys=["domains", "nameservers"], - elements=[ - ( - "domains", - ListOfStrings( - title=_("Domain names"), - help=_("List of domain names the below nameservers apply to"), - allow_empty=False, - ) - ), - ( - "nameservers", - ListOfStrings( - title=_("Nameservers"), - help=_("List of nameservers that all of the above domain names should have"), - allow_empty=False, - ) - ), - ] - ) - ) - ), - ( - "alert_level", - DropdownChoice( - title=_("Alert level used on mismatch"), - help=_("Alert level used when there is a mismatch in domain name servers for a domain"), - default_value=State.WARN.value, - choices=[ - (State.CRIT.value, _(State.CRIT.name)), - (State.WARN.value, _(State.WARN.name)), - (State.OK.value, _(State.OK.name)), - ], - ), - ), - ], - ) - -rulespec_registry.register( - HostRulespec( - name="special_agents:domains_nameservers", - group=RulespecGroupCheckParametersNetworking, - match_type='dict', - valuespec=_valuespec_special_agents_domains_nameservers_query, - ) -) -rulespec_registry.register( - CheckParameterRulespecWithItem( - check_group_name="domains_nameservers", - group=RulespecGroupCheckParametersNetworking, - match_type="dict", - parameter_valuespec=_valuespec_special_agents_domains_nameservers_checks, - item_spec=lambda: TextInput(title=_("Nameserver")), - title=lambda: _("Domains Nameservers Checks"), - ) -)