Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed Graylog alerts #765

Open
wants to merge 1 commit into
base: 2.2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 21 additions & 26 deletions cmk/base/plugins/agent_based/graylog_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,43 @@
# 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.

"""
Kuhn & Rueß GmbH
Consulting and Development
https://kuhn-ruess.de
"""


import json
from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any
from typing import Any, NamedTuple

from .agent_based_api.v1 import check_levels, register, render, Service
from .agent_based_api.v1 import check_levels, register, Service
from .agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable

# <<<graylog_alerts>>>
# {"alerts": {"num_of_alerts": 0, "has_since_argument": false, "alerts_since": null, "num_of_alerts_in_range": 0}}
# {"alerts": {"num_of_events": 947, "num_of_alerts": 174}}

# <<<graylog_alerts>>>
# {"alerts": {"num_of_alerts": 5, "has_since_argument": true, "alerts_since": 1800, "num_of_alerts_in_range": 2}}
# {"alerts": {"num_of_events": 543, "num_of_alerts": 0}}


@dataclass
class AlertsInfo:
class AlertsInfo(NamedTuple):
num_of_events: int
num_of_alerts: int
has_since_argument: bool
alerts_since: int | None
num_of_alerts_in_range: int


def parse_graylog_alerts(string_table: StringTable) -> AlertsInfo | None:
alerts_section = json.loads(string_table[0][0])

if len(alerts_section) != 1:
return None

alerts_data = alerts_section.get("alerts")

return AlertsInfo(
num_of_events=alerts_data.get("num_of_events"),
num_of_alerts=alerts_data.get("num_of_alerts"),
has_since_argument=alerts_data.get("has_since_argument"),
alerts_since=alerts_data.get("alerts_since"),
num_of_alerts_in_range=alerts_data.get("num_of_alerts_in_range"),
)


Expand All @@ -53,21 +55,14 @@ def discover_graylog_alerts(section: AlertsInfo) -> DiscoveryResult:


def check_graylog_alerts(params: Mapping[str, Any], section: AlertsInfo) -> CheckResult:
yield from check_levels(
value=section.num_of_alerts,
levels_upper=params.get("alerts_upper", (None, None)),
levels_lower=params.get("alerts_lower", (None, None)),
render_func=lambda x: str(int(x)),
label="Total number of alerts",
)

if section.has_since_argument and section.alerts_since:
for which in ["alerts", "events"]:
yield from check_levels(
value=section.num_of_alerts_in_range,
levels_upper=params.get("alerts_in_range_upper", (None, None)),
levels_lower=params.get("alerts_in_range_lower", (None, None)),
value=(section._asdict())[f"num_of_{which}"],
levels_upper=params.get(f"{which}_upper", (None, None)),
levels_lower=params.get(f"{which}_lower", (None, None)),
metric_name=f"graylog_{which}",
render_func=lambda x: str(int(x)),
label=f"Total number of alerts in the last {render.timespan(section.alerts_since)}",
label=f"Total number of {which}",
)


Expand Down
54 changes: 54 additions & 0 deletions cmk/gui/plugins/metrics/graylog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3

"""
Kuhn & Rueß GmbH
Consulting and Development
https://kuhn-ruess.de
"""


from cmk.gui.i18n import _
from cmk.gui.plugins.metrics.utils import graph_info, metric_info

# .
# .--Metrics-------------------------------------------------------------.
# | __ __ _ _ |
# | | \/ | ___| |_ _ __(_) ___ ___ |
# | | |\/| |/ _ \ __| '__| |/ __/ __| |
# | | | | | __/ |_| | | | (__\__ \ |
# | |_| |_|\___|\__|_| |_|\___|___/ |
# | |
# +----------------------------------------------------------------------+
# | Definitions of metrics |
# '----------------------------------------------------------------------'

metric_info["graylog_alerts"] = {
"title": "Total amount of alerts",
"unit": "count",
"color": "blue",
}
metric_info["graylog_events"] = {
"title": "Total amount of events",
"unit": "count",
"color": "green",
}

# .
# .--Graphs--------------------------------------------------------------.
# | ____ _ |
# | / ___|_ __ __ _ _ __ | |__ ___ |
# | | | _| '__/ _` | '_ \| '_ \/ __| |
# | | |_| | | | (_| | |_) | | | \__ \ |
# | \____|_| \__,_| .__/|_| |_|___/ |
# | |_| |
# +----------------------------------------------------------------------+
# | Definitions of time series graphs |
# '----------------------------------------------------------------------'

graph_info["graylog_alerts"] = {
"title": _("Graylog alerts and events"),
"metrics": [
("graylog_alerts", "line"),
("graylog_events", "line"),
],
}
31 changes: 19 additions & 12 deletions cmk/gui/plugins/wato/check_parameters/graylog_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
# 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.

"""
Kuhn & Rueß GmbH
Consulting and Development
https://kuhn-ruess.de
"""


from cmk.gui.i18n import _
from cmk.gui.plugins.wato.utils import (
CheckParameterRulespecWithoutItem,
Expand All @@ -20,8 +27,8 @@ def _parameter_valuespec_graylog_alerts():
Tuple(
title=_("Total alerts count upper levels"),
elements=[
Integer(title=_("Warning at")),
Integer(title=_("Critical at")),
Integer(title=_("Warning at"), unit="alerts"),
Integer(title=_("Critical at"), unit="alerts"),
],
),
),
Expand All @@ -30,28 +37,28 @@ def _parameter_valuespec_graylog_alerts():
Tuple(
title=_("Total alerts count lower levels"),
elements=[
Integer(title=_("Warning below")),
Integer(title=_("Critical below")),
Integer(title=_("Warning below"), unit="alerts"),
Integer(title=_("Critical below"), unit="alerts"),
],
),
),
(
"alerts_in_range_upper",
"events_upper",
Tuple(
title=_("Number of alerts in defined timespan upper level"),
title=_("Total events count upper levesl"),
elements=[
Integer(title=_("Warning below"), unit="alerts"),
Integer(title=_("Critical below"), unit="alerts"),
Integer(title=_("Warning at"), unit="events"),
Integer(title=_("Critical at"), unit="events"),
],
),
),
(
"alerts_in_range_lower",
"events_lower",
Tuple(
title=_("Number of alerts in defined timespan lower level"),
title=_("Total event count lower levels"),
elements=[
Integer(title=_("Warning at"), unit="alerts"),
Integer(title=_("Critical at"), unit="alerts"),
Integer(title=_("Warning below"), unit="events"),
Integer(title=_("Critical below"), unit="events"),
],
),
),
Expand Down
74 changes: 50 additions & 24 deletions cmk/special_agents/agent_graylog.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
# 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.

"""
Kuhn & Rueß GmbH
Consulting and Development
https://kuhn-ruess.de
"""


import argparse
import json
import sys
Expand Down Expand Up @@ -34,7 +41,7 @@ def main(argv=None):

# Add new queries here
sections = [
GraylogSection(name="alerts", uri="/streams/alerts?limit=300"),
GraylogSection(name="alerts", uri="/events/search"),
GraylogSection(name="cluster_health", uri="/system/indexer/cluster/health"),
GraylogSection(name="cluster_inputstates", uri="/cluster/inputstates"),
GraylogSection(name="cluster_stats", uri="/system/cluster/stats"),
Expand Down Expand Up @@ -70,6 +77,14 @@ def handle_request(args, sections): # pylint: disable=too-many-branches

if section.name == "events":
value = handle_response(url, args, "POST").json()

elif section.name == "alerts":
content = {
"filter": {"alerts": "include"},
"timerange": {"type": "relative", "range": args.alerts_since},
}
value = handle_response(url, args, "POST", args.alerts_since, content).json()

else:
value = handle_response(url, args).json()

Expand Down Expand Up @@ -157,24 +172,24 @@ def handle_request(args, sections): # pylint: disable=too-many-branches
handle_output([events], section.name, args)

if section.name == "alerts":
num_of_alerts = value.get("total", 0)
num_of_alerts_in_range = 0
alerts_since_argument = args.alerts_since

if alerts_since_argument:
url_alerts_in_range = f"{url}%since={str(alerts_since_argument)}"
num_of_alerts_in_range = (
handle_response(url_alerts_in_range, args).json().get("total", 0)
)
num_of_events = value.get("total_events", 0)

content = {
"filter": {"alerts": "only"},
"timerange": {"type": "relative", "range": args.alerts_since},
}
value = handle_response(url, args, "POST", args.alerts_since, content).json()

num_of_alerts = value.get("total_events", 0)
num_of_events -= num_of_alerts

alerts = {
"alerts": {
"num_of_events": num_of_events,
"num_of_alerts": num_of_alerts,
"has_since_argument": bool(alerts_since_argument),
"alerts_since": alerts_since_argument if alerts_since_argument else None,
"num_of_alerts_in_range": num_of_alerts_in_range,
}
}

handle_output([alerts], section.name, args)

if section.name == "sources":
Expand Down Expand Up @@ -219,18 +234,29 @@ def handle_request(args, sections): # pylint: disable=too-many-branches
handle_output(value, section.name, args)


def handle_response(url, args, method="GET", events_since=86400):
def handle_response(url, args, method="GET", events_since=86400, content=None):
if method == "POST":
try:
response = requests.post(
url,
auth=(args.user, args.password),
headers={
"Content-Type": "application/json",
"X-Requested-By": args.user,
},
json={"timerange": {"type": "relative", "range": events_since}},
)
if content:
response = requests.post(
url,
auth=(args.user, args.password),
headers={
"Content-Type": "application/json",
"X-Requested-By": args.user,
},
json=content,
)
else:
response = requests.post(
url,
auth=(args.user, args.password),
headers={
"Content-Type": "application/json",
"X-Requested-By": args.user,
},
json={"timerange": {"type": "relative", "range": events_since}},
)
except requests.exceptions.RequestException as e:
sys.stderr.write("Error: %s\n" % e)
if args.debug:
Expand Down Expand Up @@ -330,7 +356,7 @@ def parse_arguments(argv):
"-m",
"--sections",
default=sections,
help="""Comma separated list of data to query. Possible values: %s (default: all)"""
help="""Comma seperated list of data to query. Possible values: %s (default: all)"""
% ", ".join(sections),
)
parser.add_argument(
Expand Down
Loading
Loading