From 66a1fc0a5af660f4a9f77b80a6970c33b01aa8ee Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Sun, 21 Apr 2024 15:44:07 +0200 Subject: [PATCH] update project --- README.md | 2 +- mkp/unbound-1.2.0-20240421.mkp | Bin 0 -> 4853 bytes source/agent_based/unbound.py | 239 ++++++++++++++++++ source/agents/plugins/unbound | 4 + source/checkman/.gitkeep | 0 source/checkman/unbound | 45 ---- source/gui/metrics/unbound.py | 126 +++++++++ source/gui/wato/check_parameters/unbound.py | 165 ++++++++++++ .../cmk/base/cee/plugins/bakery/unbound.py | 36 +++ source/packages/unbound | 19 ++ 10 files changed, 590 insertions(+), 46 deletions(-) create mode 100644 mkp/unbound-1.2.0-20240421.mkp create mode 100644 source/agent_based/unbound.py create mode 100755 source/agents/plugins/unbound delete mode 100644 source/checkman/.gitkeep delete mode 100644 source/checkman/unbound create mode 100644 source/gui/metrics/unbound.py create mode 100644 source/gui/wato/check_parameters/unbound.py create mode 100644 source/lib/python3/cmk/base/cee/plugins/bakery/unbound.py create mode 100644 source/packages/unbound diff --git a/README.md b/README.md index 9209cf4..dd1b6d1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[PACKAGE]: ../../raw/master/packagee-0.1.2-20230706.mkp "package-0.1.2-20230706.mkp" +[PACKAGE]: ../../raw/master/mkp/unbound-1.2.0-20240421.mkp "unbound-1.2.0-20240421.mkp" # Title A short description about the plugin diff --git a/mkp/unbound-1.2.0-20240421.mkp b/mkp/unbound-1.2.0-20240421.mkp new file mode 100644 index 0000000000000000000000000000000000000000..9a1d9420f21bc07886feb932228d2bf5e1a62e90 GIT binary patch literal 4853 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 literal 0 HcmV?d00001 diff --git a/source/agent_based/unbound.py b/source/agent_based/unbound.py new file mode 100644 index 0000000..4f6e23f --- /dev/null +++ b/source/agent_based/unbound.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2022, Jan-Philipp Litza (PLUTEX) <jpl@plutex.de>. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# https://nlnetlabs.nl/projects/unbound/about/ + +# changes by thl-cmk[at]outlook[dot]com +# 2024-04-21: removed Union -> no need "int | float" should do +# added levels_upper_NOERROR to default parameters -> show up in info line + +from typing import ( + Any, + Mapping, + # Union, +) + +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( + CheckResult, + DiscoveryResult, + StringTable, +) + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + GetRateError, + Service, + check_levels, + get_rate, + get_value_store, + register, + render, +) + + +# UnboundSection = Mapping[str, Union[int, float, "UnboundSection"]] +UnboundSection = Mapping[str, int | float] + + +def render_qps(x: float) -> str: + return f'{x:.2f}/s' + + +def parse_unbound(string_table: StringTable) -> UnboundSection: + section = {} + for key, value in string_table: + try: + section[key] = int(value) + except ValueError: + section[key] = float(value) + return section + + +register.agent_section( + name="unbound", + parse_function=parse_unbound, +) + + +def discover_unbound_cache(section: UnboundSection) -> DiscoveryResult: + if 'total.num.cachehits' in section and 'total.num.cachemiss' in section: + yield Service() + + +def check_unbound_cache( + params: Mapping[str, Any], + section: UnboundSection, +) -> CheckResult: + cumulative_cache_hits = section.get('total.num.cachehits') + cumulative_cache_miss = section.get('total.num.cachemiss') + now = section.get('time.now') + + if None in (cumulative_cache_hits, cumulative_cache_miss, now): + return + + cache_hits = get_rate( + get_value_store(), + 'unbound_cache_hits', + now, + cumulative_cache_hits, + raise_overflow=True, + ) + cache_miss = get_rate( + get_value_store(), + 'unbound_cache_miss', + now, + cumulative_cache_miss, + raise_overflow=True, + ) + total = cache_hits + cache_miss + hit_perc = (cache_hits / float(total)) * 100.0 if total != 0 else 100.0 + + yield from check_levels( + value=cache_miss, + metric_name="cache_misses_rate", + levels_upper=params.get("cache_misses"), + render_func=render_qps, + label='Cache Misses', + notice_only=True, + ) + + yield from check_levels( + value=cache_hits, + metric_name="cache_hit_rate", + render_func=render_qps, + label='Cache Hits', + notice_only=True, + ) + + yield from check_levels( + value=hit_perc, + metric_name="cache_hit_ratio", + levels_lower=params.get("cache_hits"), + render_func=render.percent, + label='Cache Hit Ratio', + ) + + +register.check_plugin( + name="unbound_cache", + service_name="Unbound Cache", + sections=["unbound"], + discovery_function=discover_unbound_cache, + check_function=check_unbound_cache, + check_default_parameters={}, + check_ruleset_name="unbound_cache", +) + + +def discover_unbound_answers(section: UnboundSection) -> DiscoveryResult: + if 'time.now' in section and 'num.answer.rcode.SERVFAIL' in section: + yield Service() + + +def check_unbound_answers(params: Mapping, section: UnboundSection) -> CheckResult: + key_prefix = 'num.answer.rcode.' + if 'time.now' not in section: + return + + now = section['time.now'] + + total = sum( + value for key, value in section.items() + if key.startswith(key_prefix) + ) + + for key, value in section.items(): + if not key.startswith(key_prefix): + continue + answer = key[len(key_prefix):] + + try: + rate = get_rate( + get_value_store(), + f'unbound_answers_{answer}', + now, + value, + raise_overflow=True, + ) + except GetRateError: + pass + else: + levels_upper = params.get(f'levels_upper_{answer}') + if levels_upper is not None and len(levels_upper) == 3: + # levels on the ratio of answers + levels_upper = ( + levels_upper[0] * total, + levels_upper[1] * total, + ) + yield from check_levels( + value=rate, + levels_upper=levels_upper, + metric_name=f'unbound_answers_{answer}', + render_func=render_qps, + label=answer, + notice_only=f'levels_upper_{answer}' not in params, + ) + + +register.check_plugin( + name="unbound_answers", + service_name="Unbound Answers", + sections=["unbound"], + discovery_function=discover_unbound_answers, + check_function=check_unbound_answers, + check_default_parameters={ + 'levels_upper_NOERROR': (101,101), + 'levels_upper_SERVFAIL': (10, 100), + 'levels_upper_REFUSED': (10, 100), + }, + check_ruleset_name="unbound_answers", +) + + +def discover_unbound_unwanted_replies(section: UnboundSection) -> DiscoveryResult: + if 'time.now' in section and 'unwanted.replies' in section: + yield Service() + + +def check_unbound_unwanted_replies(section: UnboundSection) -> CheckResult: + if 'time.now' not in section or 'unwanted.replies' not in section: + return + + rate = get_rate( + get_value_store(), + 'unbound_unwanted_replies', + section['time.now'], + section['unwanted.replies'], + raise_overflow=True, + ) + + yield from check_levels( + value=rate, + levels_upper=(10, 100), + metric_name='unbound_unwanted_replies', + render_func=render_qps, + label='Unwanted Replies', + ) + + +register.check_plugin( + name="unbound_unwanted_replies", + service_name="Unbound Unwanted Replies", + sections=["unbound"], + discovery_function=discover_unbound_unwanted_replies, + check_function=check_unbound_unwanted_replies, +) diff --git a/source/agents/plugins/unbound b/source/agents/plugins/unbound new file mode 100755 index 0000000..52d336e --- /dev/null +++ b/source/agents/plugins/unbound @@ -0,0 +1,4 @@ +#!/bin/sh +echo '<<<unbound:sep(61)>>>' +unbound-control stats_noreset +echo '<<<>>>' diff --git a/source/checkman/.gitkeep b/source/checkman/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/source/checkman/unbound b/source/checkman/unbound deleted file mode 100644 index 08ef898..0000000 --- a/source/checkman/unbound +++ /dev/null @@ -1,45 +0,0 @@ -title: Dummy check man page - used as template for new check manuals -agents: linux, windows, aix, solaris, hpux, vms, freebsd, snmp -catalog: see modules/catalog.py for possible values -license: GPL -distribution: check_mk -description: - Describe here: (1) what the check actually does, (2) under which - circumstances it goes warning/critical, (3) which devices are supported - by the check, (4) if the check requires a separated plugin or - tool or separate configuration on the target host. - -item: - Describe the syntax and meaning of the check's item here. Provide all - information one needs if coding a manual check with {checks +=} in {main.mk}. - Give an example. If the check uses {None} as sole item, - then leave out this section. - -examples: - # Give examples for configuration in {main.mk} here. If the check has - # configuration variable, then give example for them here. - - # set default levels to 40 and 60 percent: - foo_default_values = (40, 60) - - # another configuration variable here: - inventory_foo_filter = [ "superfoo", "superfoo2" ] - -perfdata: - Describe precisely the number and meaning of performance variables - the check sends. If it outputs no performance data, then leave out this - section. - -inventory: - Describe how the inventory for the check works. Which items - will it find? Describe the influence of check specific - configuration parameters to the inventory. - -[parameters] -foofirst(int): describe the first parameter here (if parameters are grouped - as tuple) -fooother(string): describe another parameter here. - -[configuration] -foo_default_levels(int, int): Describe global configuration variable of - foo here. Important: also tell the user how they are preset. diff --git a/source/gui/metrics/unbound.py b/source/gui/metrics/unbound.py new file mode 100644 index 0000000..deec35d --- /dev/null +++ b/source/gui/metrics/unbound.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# -*- encoding: utf-8; py-indent-offset: 4 -*- + +# Copyright (C) 2022, Jan-Philipp Litza (PLUTEX) <jpl@plutex.de>. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# modifications by thl-cmk[at]outlook[dot]com +# 2024-02-21: changed import path form mk.gui.plugins.metrics to mk.gui.plugins.metrics.utils +# added metrics/graph for unbound_unwanted_replies +# moved to ~/local/lib/check_mk/gui/plugins/metrics (from local/share/check_mk/web/plugins/metrics) +# added perfometer for unbound_answers (NOERROR/SERVFAIL) and unbound_unwanted_replies + +from cmk.gui.i18n import _ + +from cmk.gui.plugins.metrics.utils import ( + metric_info, + graph_info, + perfometer_info, +) + +metric_info['unbound_answers_NOERROR'] = { + 'title': _('Rate of NOERROR answers'), + 'unit': '1/s', + 'color': '31/a', +} + +metric_info['unbound_answers_FORMERR'] = { + 'title': _('Rate of FORMERR answers'), + 'unit': '1/s', + 'color': '21/a', +} + +metric_info['unbound_answers_SERVFAIL'] = { + 'title': _('Rate of SERVFAIL answers'), + 'unit': '1/s', + 'color': '11/a', +} + +metric_info['unbound_answers_NXDOMAIN'] = { + 'title': _('Rate of NXDOMAIN answers'), + 'unit': '1/s', + 'color': '51/a', +} + +metric_info['unbound_answers_NOTIMPL'] = { + 'title': _('Rate of NOTIMPL answers'), + 'unit': '1/s', + 'color': '41/a', +} + +metric_info['unbound_answers_REFUSED'] = { + 'title': _('Rate of REFUSED answers'), + 'unit': '1/s', + 'color': '26/a', +} + +metric_info['unbound_answers_nodata'] = { + 'title': _('Rate of answers without data'), + 'unit': '1/s', + 'color': '52/a', +} + +graph_info['unbound_answers'] = { + 'title': _('Rate of answers'), + 'metrics': [ + (f'unbound_answers_{answer}', 'line') + for answer in ('NOERROR', 'FORMERR', 'SERVFAIL', 'NXDOMAIN', 'NOTIMPL', 'REFUSED', 'nodata') + ], +} + +perfometer_info.append(('stacked', [ + { + 'type': 'logarithmic', + 'metric': 'unbound_answers_NOERROR', + 'half_value': 100.0, # ome year + 'exponent': 2, + }, + { + 'type': 'logarithmic', + 'metric': 'unbound_answers_SERVFAIL', + 'half_value': 50.0, + 'exponent': 2, + }, +])) + +metric_info['cache_hit_rate'] = { + 'title': _('Cache hits per second'), + 'unit': '1/s', + 'color': '26/a', +} + +graph_info['cache_hit_misses'] = { + 'title': _('Cache Hits and Misses'), + 'metrics': [('cache_hit_rate', 'line'), ('cache_misses_rate', 'line')], +} + +metric_info['unbound_unwanted_replies'] = { + 'title': _('Unwanted replies'), + 'unit': '1/s', + 'color': '26/a', +} + +graph_info['unbound_unwanted_replies'] = { + 'title': _('Unwanted replies'), + 'metrics': [('unbound_unwanted_replies', 'area')], +} + +perfometer_info.append({ + 'type': 'logarithmic', + 'metric': 'unbound_unwanted_replies', + 'half_value': 100.0, # ome year + 'exponent': 2, +}) diff --git a/source/gui/wato/check_parameters/unbound.py b/source/gui/wato/check_parameters/unbound.py new file mode 100644 index 0000000..5e52a07 --- /dev/null +++ b/source/gui/wato/check_parameters/unbound.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (C) 2022, Jan-Philipp Litza (PLUTEX) <jpl@plutex.de>. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# modifications by thl-cmk[at]outlook[dot]com +# 2024-04-21: fixed missing FixedValue in import +# changed Alternative titles to "Upper levels in qps"/"Upper levels in %" to better differentiate between them +# added explicit unit "%2 +# added ruleset for bakery +# renamed to unbound.py (from unbound_parameters.py) +# moved to ~/local/lib/check_mk/gui/plugins/wato/check_parameters (from local/share/check_mk/web/plugins/wato) +# + +from cmk.gui.i18n import _ +from cmk.gui.plugins.wato.utils import ( + CheckParameterRulespecWithoutItem, + rulespec_registry, + RulespecGroupCheckParametersApplications, + HostRulespec, +) +from cmk.gui.cee.plugins.wato.agent_bakery.rulespecs.utils import ( + RulespecGroupMonitoringAgentsAgentPlugins, +) + +from cmk.gui.valuespec import Dictionary, Float, Percentage, Tuple, Alternative, FixedValue + + +def _parameter_valuespec_unbound_cache(): + return Dictionary( + title=_("Unbound: Cache"), + elements=[ + ( + "cache_misses", + Tuple( + title="Levels on cache misses per second", + elements=[ + Float( + title="warn", + ), + Float( + title="crit", + ), + ], + ), + ), + ( + "cache_hits", + Tuple( + title="Lower levels for hits in %", + elements=[ + Percentage( + title="warn", + ), + Percentage( + title="crit", + ), + ], + ), + ), + ], + ) + + +rulespec_registry.register( + CheckParameterRulespecWithoutItem( + check_group_name="unbound_cache", + group=RulespecGroupCheckParametersApplications, + match_type="dict", + parameter_valuespec=_parameter_valuespec_unbound_cache, + title=lambda: _("Unbound Cache"), + ) +) + + +def _parameter_valuespec_unbound_answers(): + return Dictionary( + elements=[ + ( + f"levels_upper_{answer}", + Alternative( + title=f'Upper levels for {answer} answers', + show_alternative_title=True, + elements=[ + Tuple( + elements=[ + Float(title=_("Warning at"), unit=_("qps")), + Float(title=_("Critical at"), unit=_("qps")), + ], + title=f'Upper levels in qps', + ), + Tuple( + elements=[ + Percentage(title=_("Warning at"), unit=_("%")), + Percentage(title=_("Critical at"), unit=_("%")), + FixedValue(value="%", totext=""), # need to decide between both variants + ], + title=f'Upper levels in %', + ), + ] + ) + ) + for answer in ( + 'NOERROR', + 'FORMERR', + 'SERVFAIL', + 'NXDOMAIN', + 'NOTIMPL', + 'REFUSED', + 'nodata', + ) + ], + ) + + +rulespec_registry.register( + CheckParameterRulespecWithoutItem( + check_group_name="unbound_answers", + group=RulespecGroupCheckParametersApplications, + match_type="dict", + parameter_valuespec=_parameter_valuespec_unbound_answers, + title=lambda: _("Unbound Answers"), + ) +) + + +def _parameter_valuespec_unbound_bakery(): + return Alternative( + title=_('unbound'), + elements=[ + FixedValue( + True, + title=_('Deploy the unbound agent plugin'), + totext=_('The unbound agent plugin will be deployed') + ), + FixedValue( + None, + title=_('Do not deploy the unbound agent plugin'), + totext=_('The unbound agent plugin will not be deployed') + ), + ], + ) + + +rulespec_registry.register( + HostRulespec( + group=RulespecGroupMonitoringAgentsAgentPlugins, + name='agent_config:unbound', + valuespec=_parameter_valuespec_unbound_bakery, + ) +) diff --git a/source/lib/python3/cmk/base/cee/plugins/bakery/unbound.py b/source/lib/python3/cmk/base/cee/plugins/bakery/unbound.py new file mode 100644 index 0000000..ac4f50d --- /dev/null +++ b/source/lib/python3/cmk/base/cee/plugins/bakery/unbound.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 +# +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2024-04-21 +# File : share/check_mk/agents/unbound.py +# +# bakery unbound plugin +# + + +from pathlib import Path + +from cmk.base.cee.plugins.bakery.bakery_api.v1 import ( + FileGenerator, + OS, + Plugin, + register +) + + +def get_unbound_files(conf) -> FileGenerator: + if conf is True: + yield Plugin( + base_os=OS.LINUX, + source=Path('unbound'), + ) + + +register.bakery_plugin( + name='unbound', + files_function=get_unbound_files, +) diff --git a/source/packages/unbound b/source/packages/unbound new file mode 100644 index 0000000..5741833 --- /dev/null +++ b/source/packages/unbound @@ -0,0 +1,19 @@ +{'author': 'Jan-Philipp Litza <jpl@plutex.de>', + 'description': 'Plugin to gather statistics from unbound caching DNS resolver ' + 'via agent plugin. It monitors answer types, cache hit ratio ' + 'and miss rate as well as unwanted reply rate.\n' + '\n' + 'needs in server config:\n' + ' server:\n' + ' extended-statistics: yes\n', + 'download_url': 'https://github.com/PLUTEX/checkmk-unbound/', + 'files': {'agent_based': ['unbound.py'], + 'agents': ['plugins/unbound'], + 'gui': ['metrics/unbound.py', 'wato/check_parameters/unbound.py'], + 'lib': ['python3/cmk/base/cee/plugins/bakery/unbound.py']}, + 'name': 'unbound', + 'title': 'Unbound', + 'version': '1.2.0-20240421', + 'version.min_required': '2.2.0b1', + 'version.packaged': '2.2.0p24', + 'version.usable_until': None} -- GitLab