Add Graylog Input Metrics plugin for CheckMK 2.2.0.
This commit is contained in:
parent
6de602a399
commit
3d9e54f470
BIN
check_mk-graylog_metrics/graylog_input_metrics-0.0.3.mkp
Normal file
BIN
check_mk-graylog_metrics/graylog_input_metrics-0.0.3.mkp
Normal file
Binary file not shown.
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
|
||||
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
|
||||
# conditions defined in the file COPYING, which is part of this source code package.
|
||||
|
||||
from cmk.gui.i18n import _
|
||||
from cmk.gui.plugins.wato.utils import (
|
||||
CheckParameterRulespecWithItem,
|
||||
rulespec_registry,
|
||||
RulespecGroupCheckParametersDiscovery,
|
||||
)
|
||||
from cmk.gui.valuespec import Dictionary, Float, Tuple
|
||||
|
||||
defaults={
|
||||
"im_rate_upper": 999999,
|
||||
"im_rate_lower": 0,
|
||||
"rs_rate_upper": 999999,
|
||||
"rs_rate_lower": 0,
|
||||
}
|
||||
mappings={
|
||||
"im": "messages",
|
||||
"rs": "bytes",
|
||||
"upper": "above",
|
||||
"lower": "below",
|
||||
}
|
||||
|
||||
|
||||
def _rate(metric, time, level):
|
||||
unit=mappings[metric]
|
||||
direction=mappings[level]
|
||||
return (
|
||||
f"{metric}_{time}_rate_{level}",
|
||||
Tuple(
|
||||
title=_(f"{level.capitalize()} level"),
|
||||
elements=[
|
||||
Float(
|
||||
title=_(f"Warning {direction}"),
|
||||
unit=f"{unit}/{time[1:]}min",
|
||||
default_value=defaults[f"{metric}_rate_{level}"],
|
||||
),
|
||||
Float(
|
||||
title=_(f"Critical {direction}"),
|
||||
unit=f"{unit}/{time[1:]}min",
|
||||
default_value=defaults[f"{metric}_rate_{level}"],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _element(metric, time):
|
||||
return (
|
||||
f"{metric}_{time}_rate",
|
||||
Dictionary(
|
||||
title=_(f"Incoming {mappings[metric]} for past {time[1:]} minute"),
|
||||
elements=[
|
||||
_rate(metric, time, "upper"),
|
||||
_rate(metric, time, "lower")
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _parameter_valuespec_graylog_input_metrics():
|
||||
matrix=[
|
||||
("im", "m1"), ("im", "m5"), ("im", "m15"),
|
||||
("rs", "m1"), ("rs", "m5"), ("rs", "m15"),
|
||||
]
|
||||
|
||||
elements=[
|
||||
_element(metric, time) for metric, time in matrix
|
||||
]
|
||||
|
||||
return Dictionary(
|
||||
title=_("Message and data rates"),
|
||||
help=_(
|
||||
"These rates are queried directly from the Graylog instance. "
|
||||
"Upper and lower levels can be specified for individual metric."
|
||||
),
|
||||
elements=elements
|
||||
)
|
||||
|
||||
|
||||
rulespec_registry.register(
|
||||
CheckParameterRulespecWithItem(
|
||||
check_group_name="graylog_input_metrics",
|
||||
group=RulespecGroupCheckParametersDiscovery,
|
||||
match_type="dict",
|
||||
parameter_valuespec=_parameter_valuespec_graylog_input_metrics,
|
||||
title=lambda: _("Graylog input metrics"),
|
||||
)
|
||||
)
|
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
|
||||
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
|
||||
# conditions defined in the file COPYING, which is part of this source code package.
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from typing import NamedTuple
|
||||
|
||||
import requests
|
||||
import urllib3
|
||||
|
||||
import cmk.utils.password_store
|
||||
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
cmk.utils.password_store.replace_passwords()
|
||||
|
||||
|
||||
class GraylogSection(NamedTuple):
|
||||
name: str
|
||||
uri: str
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
|
||||
args = parse_arguments(argv)
|
||||
|
||||
try:
|
||||
handle_request(args)
|
||||
except Exception:
|
||||
if args.debug:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def handle_request(args): # pylint: disable=too-many-branches
|
||||
url_base = f"{args.proto}://{args.hostname}:{args.port}/api"
|
||||
url = url_base + "/system/metrics"
|
||||
value = handle_response(url, args).json()
|
||||
|
||||
# Handle the input_metrics section. We need to merge information from
|
||||
# both inputstates and input_metrics, and we do that by extracting the
|
||||
# ids returned by both calls. Once merged, we return a single dictionary
|
||||
# with state, name, type, port, and the various rates (either raw or
|
||||
# incomingMessages).
|
||||
url_inputs_data = url_base + "/cluster/inputstates"
|
||||
inputs_data = handle_response(url_inputs_data, args).json()
|
||||
inputs_data = tuple(inputs_data.values())[0]
|
||||
metrics_data = value.get("meters")
|
||||
|
||||
if inputs_data is None or metrics_data is None:
|
||||
return
|
||||
|
||||
# Create a dictionary, containing all metrics with substrings
|
||||
# "incomingMessages" and "rawSize".
|
||||
# All rates should exist, created and with values as low as "0.0".
|
||||
metrics_dict = {}
|
||||
for metric, metric_rate in metrics_data.items():
|
||||
metric_id = metric.split(".")[-2]
|
||||
metric_type = None
|
||||
if "incomingMessages" in metric:
|
||||
metric_type = "im"
|
||||
elif "rawSize" in metric:
|
||||
metric_type = "rs"
|
||||
if metric_type:
|
||||
metric_key = metrics_dict.setdefault(metric_id, {})
|
||||
metric_key[f"{metric_type}_m1_rate"] = metric_rate["m1_rate"]
|
||||
metric_key[f"{metric_type}_m5_rate"] = metric_rate["m5_rate"]
|
||||
metric_key[f"{metric_type}_m15_rate"] = metric_rate["m15_rate"]
|
||||
|
||||
# Create a dictionary with all inputs and add the rates from
|
||||
# the previous dictionary, metrics_dict. This is passed as output.
|
||||
# Some inputs don't have a "port", so we handle this with .get("port").
|
||||
inputs_dict = {}
|
||||
for inputs in inputs_data:
|
||||
message_input = inputs["message_input"]
|
||||
input_id = inputs["id"]
|
||||
input_state = inputs["state"]
|
||||
input_name = message_input["title"]
|
||||
input_type = message_input["name"]
|
||||
input_port = message_input["attributes"].get("port")
|
||||
input_rate = metrics_dict[input_id]
|
||||
inputs_dict[input_id] = {
|
||||
"input_state": input_state,
|
||||
"input_name": input_name,
|
||||
"input_type": input_type,
|
||||
"input_port": input_port,
|
||||
"im_m1_rate": input_rate["im_m1_rate"],
|
||||
"im_m5_rate": input_rate["im_m5_rate"],
|
||||
"im_m15_rate": input_rate["im_m15_rate"],
|
||||
"rs_m1_rate": input_rate["rs_m1_rate"],
|
||||
"rs_m5_rate": input_rate["rs_m5_rate"],
|
||||
"rs_m15_rate": input_rate["rs_m15_rate"]
|
||||
}
|
||||
|
||||
if inputs_dict:
|
||||
handle_output(inputs_dict)
|
||||
|
||||
|
||||
def handle_response(url, args):
|
||||
try:
|
||||
return requests.get(url, auth=(args.user, args.password), verify=not args.no_cert_check)
|
||||
except requests.exceptions.RequestException as e:
|
||||
sys.stderr.write("Error: %s\n" % e)
|
||||
if args.debug:
|
||||
raise
|
||||
|
||||
|
||||
def handle_output(value):
|
||||
sys.stdout.write("<<<graylog_input_metrics:sep(0)>>>\n")
|
||||
if isinstance(value, list):
|
||||
for entry in value:
|
||||
sys.stdout.write("%s\n" % json.dumps(entry))
|
||||
return
|
||||
|
||||
sys.stdout.write("%s\n" % json.dumps(value))
|
||||
|
||||
return
|
||||
|
||||
|
||||
def parse_arguments(argv):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument("-u", "--user", default=None, help="Username for graylog login")
|
||||
parser.add_argument("-s", "--password", default=None, help="Password for graylog login")
|
||||
parser.add_argument(
|
||||
"-P",
|
||||
"--proto",
|
||||
default="https",
|
||||
help="Use 'http' or 'https' for connection to graylog (default=https)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", "--port", default=443, type=int, help="Use alternative port (default: 443)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--debug", action="store_true", help="Debug mode: let Python exceptions come through"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-cert-check", action="store_true", help="Disable SSL certificate validation"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"hostname", metavar="HOSTNAME", help="Name of the graylog instance to query."
|
||||
)
|
||||
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
|
||||
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
|
||||
# conditions defined in the file COPYING, which is part of this source code package.
|
||||
|
||||
import sys
|
||||
|
||||
from cmk.special_agents.agent_graylog_input_metrics import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
|
||||
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
|
||||
# conditions defined in the file COPYING, which is part of this source code package.
|
||||
#
|
||||
# {
|
||||
# 'proto': 'http',
|
||||
# 'port': 9000,
|
||||
# 'user': 'hell',
|
||||
# 'password': 'yeah',
|
||||
# }
|
||||
|
||||
from typing import Any, Mapping, Optional, Sequence, Union
|
||||
|
||||
|
||||
def agent_graylog_input_metrics_arguments(
|
||||
params: Mapping[str, Any], hostname: str, ipaddress: Optional[str]
|
||||
) -> Sequence[Union[str, tuple[str, str, str]]]:
|
||||
args = [
|
||||
"-P",
|
||||
params["protocol"],
|
||||
"-u",
|
||||
params["user"],
|
||||
"-s",
|
||||
passwordstore_get_cmdline("%s", params["password"]),
|
||||
]
|
||||
|
||||
if "port" in params:
|
||||
args += ["-p", params["port"]]
|
||||
|
||||
if "no-cert-check" in params:
|
||||
args += ["--no-cert-check"]
|
||||
|
||||
args.append(params["instance"])
|
||||
|
||||
return args
|
||||
|
||||
|
||||
special_agent_info["graylog_input_metrics"] = agent_graylog_input_metrics_arguments
|
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
|
||||
# conditions defined in the file COPYING, which is part of this source code package.
|
||||
|
||||
# NOTE: Careful when replacing the *-import below with a more specific import. This can cause
|
||||
# problems because it might remove variables from the check-context which are necessary for
|
||||
# resolving legacy discovery results such as [("SUMMARY", "diskstat_default_levels")]. Furthermore,
|
||||
# it might also remove variables needed for accessing discovery rulesets.
|
||||
import json
|
||||
|
||||
#<<<graylog_input_metrics:sep(0)>>>
|
||||
# {"641e88d05d447a677efde199": {"input_state": "FAILED", "input_name": "kafka_cef_test",
|
||||
# "input_type": "CEF Kafka", "input_port": null, "im_m1_rate": 0.0, "im_m5_rate": 0.0,
|
||||
# "im_m15_rate": 0.0, "rs_m1_rate": 0.0, "rs_m5_rate": 0.0, "rs_m15_rate": 0.0},
|
||||
# "641e32885d447a677efd2dbf": {"input_state": "RUNNING", "input_name": "UDP-test",
|
||||
# "input_type": "Syslog UDP", "input_port": 1514, "im_m1_rate": 1.0846244336700077,
|
||||
# "im_m5_rate": 1.3700826278955827, "im_m15_rate": 1.254406787430692, "rs_m1_rate": 145.45579305762527,
|
||||
# "rs_m5_rate": 180.6486220431909, "rs_m15_rate": 165.26666376319292},
|
||||
# "641e32795d447a677efd2d9e": {"input_state": "RUNNING", "input_name": "testTCP", "input_type": "Syslog TCP",
|
||||
# "input_port": 1515, "im_m1_rate": 1.057872514816615, "im_m5_rate": 1.364957693749168,
|
||||
# "im_m15_rate": 1.2528742858546844, "rs_m1_rate": 140.4719944116262, "rs_m5_rate": 178.57816158901215,
|
||||
# "rs_m15_rate": 163.80530659055356}}
|
||||
|
||||
def parse_graylog_input_metrics(section):
|
||||
parsed = json.loads(section[0][0])
|
||||
return parsed
|
||||
|
||||
|
||||
def inventory_graylog_input_metrics(parsed):
|
||||
for input_id, input_info in parsed.items():
|
||||
input_name = input_info["input_name"]
|
||||
yield Service(f"{input_name} ({input_id})")
|
||||
|
||||
|
||||
def check_graylog_input_metrics(item, params, parsed):
|
||||
# if parsed is None: return
|
||||
|
||||
item_id = item.split()[-1][1:-1]
|
||||
input_info = parsed[item_id]
|
||||
input_state = input_info["input_state"]
|
||||
|
||||
state = 1
|
||||
if input_state == "RUNNING": state = 0
|
||||
elif input_state == "FAILED": state = 2
|
||||
|
||||
yield state, "State: %s" % input_state
|
||||
yield 0, "Type: %s" % input_info["input_type"]
|
||||
|
||||
if input_info["input_port"]:
|
||||
yield 0, "Port: %s" % input_info["input_port"]
|
||||
|
||||
for key, dsname, unit, unit_func, infotext in [
|
||||
("im_m1_rate", "im_m1_rate", "/1min", float, "Incoming messages"),
|
||||
("im_m5_rate", "im_m5_rate", "/5min", float, ""),
|
||||
("im_m15_rate", "im_m15_rate", "/15min", float, ""),
|
||||
("rs_m1_rate", "rs_m1_rate", "/1min", get_bytes_human_readable, "Incoming data"),
|
||||
("rs_m5_rate", "rs_m5_rate", "/5min", get_bytes_human_readable, ""),
|
||||
("rs_m15_rate", "rs_m15_rate", "/15min", get_bytes_human_readable, ""),
|
||||
]:
|
||||
|
||||
value = parsed[item_id][key]
|
||||
value = round(value, 2)
|
||||
rate_upper = params.get(key, {}).get("%s_upper" % key, (None, None))
|
||||
rate_lower = params.get(key, {}).get("%s_lower" % key, (None, None))
|
||||
yield check_levels(
|
||||
value,
|
||||
dsname,
|
||||
rate_upper + rate_lower,
|
||||
human_readable_func=unit_func,
|
||||
unit=unit,
|
||||
infoname=infotext
|
||||
)
|
||||
|
||||
check_info["graylog_input_metrics"] = {
|
||||
"parse_function": parse_graylog_input_metrics,
|
||||
"check_function": check_graylog_input_metrics,
|
||||
"inventory_function": inventory_graylog_input_metrics,
|
||||
"service_description": "Graylog Input %s",
|
||||
"has_perfdata": True,
|
||||
"group": "graylog_input_metrics",
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
|
||||
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
|
||||
# conditions defined in the file COPYING, which is part of this source code package.
|
||||
|
||||
|
||||
from cmk.gui.i18n import _
|
||||
from cmk.gui.plugins.wato.special_agents.common import RulespecGroupDatasourceProgramsApps
|
||||
from cmk.gui.plugins.wato.utils import HostRulespec, IndividualOrStoredPassword, rulespec_registry
|
||||
from cmk.gui.valuespec import Age, Dictionary, DropdownChoice, Integer, ListChoice, TextInput
|
||||
from cmk.gui.watolib.rulespecs import Rulespec
|
||||
|
||||
|
||||
def _factory_default_special_agents_graylog_input_metrics():
|
||||
# No default, do not use setting if no rule matches
|
||||
return Rulespec.FACTORY_DEFAULT_UNUSED
|
||||
|
||||
|
||||
def _valuespec_special_agents_graylog_input_metrics():
|
||||
return Dictionary(
|
||||
title=_("Graylog Input Metrics"),
|
||||
help=_("Requests input metrics data from a Graylog instance."),
|
||||
optional_keys=["port", "no-cert-check"],
|
||||
elements=[
|
||||
(
|
||||
"instance",
|
||||
TextInput(
|
||||
title=_("Graylog instance to query"),
|
||||
help=_(
|
||||
"Use this option to set which instance should be "
|
||||
"checked by the special agent. Please add the "
|
||||
"hostname here, eg. my_graylog.com."
|
||||
),
|
||||
size=32,
|
||||
allow_empty=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
TextInput(
|
||||
title=_("Username"),
|
||||
help=_(
|
||||
"The username that should be used for accessing the "
|
||||
"Graylog API. Has to have read permissions at least."
|
||||
),
|
||||
size=32,
|
||||
allow_empty=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"password",
|
||||
IndividualOrStoredPassword(
|
||||
title=_("Password of the user"),
|
||||
allow_empty=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"protocol",
|
||||
DropdownChoice(
|
||||
title=_("Protocol"),
|
||||
choices=[
|
||||
("http", "HTTP"),
|
||||
("https", "HTTPS"),
|
||||
],
|
||||
default_value="https",
|
||||
),
|
||||
),
|
||||
(
|
||||
"port",
|
||||
Integer(
|
||||
title=_("Port"),
|
||||
help=_(
|
||||
"Use this option to query a port which is different from standard port 443."
|
||||
),
|
||||
default_value=443,
|
||||
),
|
||||
),
|
||||
(
|
||||
"no-cert-check",
|
||||
FixedValue(
|
||||
True,
|
||||
title=_("Disable SSL certificate validation"),
|
||||
totext=_("SSL certificate validation is disabled"),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
rulespec_registry.register(
|
||||
HostRulespec(
|
||||
factory_default=_factory_default_special_agents_graylog_input_metrics(),
|
||||
group=RulespecGroupDatasourceProgramsApps,
|
||||
name="special_agents:graylog_input_metrics",
|
||||
valuespec=_valuespec_special_agents_graylog_input_metrics,
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue
Block a user