Collection of CheckMK checks (see https://checkmk.com/). All checks and plugins are provided as is. Absolutely no warranty. Send any comments to thl-cmk[at]outlook[dot]com

Skip to content
Snippets Groups Projects
Commit ec1f73e7 authored by thl-cmk's avatar thl-cmk :flag_na:
Browse files

update project

parent 3bc2f707
No related branches found
No related tags found
No related merge requests found
File added
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 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.
def check_traceroute_arguments(params):
args = []
if not params['dns']:
args.append('-n')
method = params['method']
if method == 'icmp':
args.append('-I')
elif method == 'tcp':
args.append('-T')
# else: None -> default method
for router, state in params['routers']:
args += ['-%s' % state, router]
family = params.get('address_family')
if family is None:
family = 'ipv6' if is_ipv6_primary(host_name()) else 'ipv4'
if family == 'ipv6':
args.append('-6')
else:
args.append('-4')
# additional options
if 'port' in params:
args.append(f'-p {params["port"]}')
if 'queries' in params:
args.append(f'-q {params["queries"]}')
if 'max_ttl' in params:
args.append(f'-m {params["max_ttl"]}')
# needs root
if 'source_interface' in params:
args.append(f'-i {params["source_interface"]}')
# needs root
if 'source_address' in params:
args.append(f'-s {params["source_address"]}')
if 'destination_address' in params:
args.append(params['destination_address'])
else:
args.append('$HOSTADDRESS$')
return args
def _check_description(params):
if 'description' in params:
return f'Routing {params["description"]}'
return 'Routing'
active_check_info['traceroute'] = {
'command_line': 'check_traceroute $ARG1$',
'argument_function': check_traceroute_arguments,
'service_description': _check_description,
'has_perfdata': True,
}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 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.
# This check does a traceroute to the specified target host
# (usually $HOSTADDRESS$ itself) and checks which route(s) are
# being taken. That way you can check if your preferred or
# some alternative route in in place.
# traceroute is expected to be in the search path and installed
# with SUID root bit.
# Example output from traceroute -n
# traceroute to www.google.de (173.194.44.55), 30 hops max, 60 byte packets
# 1 10.10.11.4 0.419 ms 0.444 ms 0.518 ms
# 2 33.117.16.28 14.359 ms 14.371 ms 14.434 ms
# 3 112.18.7.119 14.750 ms 14.765 ms 19.530 ms
# 4 184.50.190.61 17.844 ms 17.865 ms 17.862 ms
# 5 67.249.94.88 24.285 ms 78.527 ms 26.834 ms
# 6 209.85.240.99 27.910 ms 27.420 ms 27.442 ms
# 7 173.194.44.55 26.583 ms 20.410 ms 23.142 ms
# Output without -n option:
# traceroute to www.google.de (173.194.44.56), 30 hops max, 60 byte packets
# 1 fritz.box (10.10.11.4) 0.570 ms 0.606 ms 0.677 ms
# 2 foo-bar.x-online.net (33.117.16.28) 14.566 ms 14.580 ms 14.658 ms
# 3 xu-2-3-0.rt-inxs-1.x-online.net (112.13.6.109) 18.214 ms 18.228 ms 18.221 ms
# 4 * * *
# 5 66.249.94.88 (66.249.94.88) 24.481 ms 24.498 ms 24.271 ms
# 6 209.85.240.99 (209.85.240.99) 27.628 ms 21.605 ms 21.943 ms
# 7 muc03s08-in-f24.1e100.net (173.194.44.56) 21.277 ms 22.236 ms 22.192 ms
# Example output for IPv6
# traceroute to ipv6.google.com (2404:6800:4004:80e::200e), 30 hops max, 80 byte packets
# 1 2001:2e8:665:0:2:2:0:1 (2001:2e8:665:0:2:2:0:1) 0.082 ms 0.046 ms 0.044 ms
# 2 2001:2e8:22:204::2 (2001:2e8:22:204::2) 0.893 ms 0.881 ms 0.961 ms
# 3 * 2001:4860:0:1::1abd (2001:4860:0:1::1abd) 225.189 ms *
# 4 2001:4860:0:1003::1 (2001:4860:0:1003::1) 3.052 ms 2.820 ms 2001:4860:0:1002::1 (2001:4860:0:1002::1) 1.501 ms
# 5 nrt13s48-in-x0e.1e100.net (2404:6800:4004:80e::200e) 1.910 ms 1.828 ms 1.753 ms
# It is also possible that for one hop several different answers appear:
# 11 xe-0-0-1-0.co2-96c-1b.ntwk.msn.net (204.152.141.11) 174.185 ms xe-10-0-2-0.co1-96c-1a.ntwk.msn.net (207.46.40.94) 174.279 ms xe-0-0-1-0.co2-96c-1b.ntwk.msn.net (204.152.141.11) 174.444 ms
# if DNS fails then it looks like this:
# 5 66.249.94.88 (66.249.94.88) 24.481 ms 24.498 ms 24.271 ms
# 6 209.85.240.99 (209.85.240.99) 27.628 ms 21.605 ms 21.943 ms
import ast
import getopt
import ipaddress
import os
import subprocess
import sys
class MissingValueError(Exception):
pass
class ProtocolVersionError(Exception):
pass
class ExecutionError(Exception):
pass
def parse_exception(exc):
exc = str(exc)
if exc[0] == "{":
exc = "%d - %s" % list(ast.literal_eval(exc).values())[0]
return str(exc)
def output_check_result(s, perfdata):
if perfdata:
perfdata_output_entries = ["%s=%s" % (p[0], ";".join(map(str, p[1:]))) for p in perfdata]
s += " | %s" % " ".join(perfdata_output_entries)
sys.stdout.write("%s\n" % s)
def option_to_state(c):
return {"w": 1, "c": 2}[c.lower()]
def _execute_traceroute(target, nodns, method, address_family, queries, max_ttl, port, source_addr, source_int):
cmd = ["traceroute"]
if nodns:
cmd.append("-n")
if method:
cmd.append(method)
if address_family:
cmd.append(address_family)
if port and method != "-I":
cmd.append(f"-p {port}")
if queries:
cmd.append(f"-q {queries}")
if max_ttl:
cmd.append(f"-m {max_ttl}")
if source_int:
cmd.append(f"-i {source_int}")
if source_addr:
cmd.append(f"-s {source_addr}")
cmd.append(target)
if (source_int is not None) or (source_addr is not None):
cmd = ' '.join(cmd)
shell = True
else:
shell = False
p = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf8", shell=shell)
sto, ste = p.communicate()
if p.returncode:
raise ExecutionError("UNKNOWN - " + ste.replace("\n", " "))
return sto
def check_traceroute(lines, routes):
# find all visited routers
routers = set([])
hops = len(lines[1:])
for line in lines[1:]:
parts = line.strip().split()
for part in parts:
try:
part = part.lstrip("(").rstrip(",").rstrip(")")
ipaddress.ip_interface(part)
routers.add(part)
except ValueError:
pass
state = 0
bad_routers = []
missing_routers = []
for option, route in routes:
s = option_to_state(option)
if option.islower() and route in routers:
state = max(state, s)
bad_routers.append("%s(%s)" % (route, "!" * s))
elif option.isupper() and route not in routers:
state = max(state, s)
missing_routers.append("%s(%s)" % (route, "!" * s))
info_text = "%d hops, missing routers: %s, bad routers: %s" % (
hops,
missing_routers and ", ".join(missing_routers) or "none",
bad_routers and ", ".join(bad_routers) or "none",
)
perfdata = [("hops", hops)]
return state, info_text, perfdata
def validate_ip_version(address_arg, ip_version_arg):
# ipv6 address can have an appended interface index/name: 'fe80::%{interface}'
try:
ip_address_version = ipaddress.ip_interface(address_arg.split("%")[0]).ip.version
except ValueError:
# May be a host or DNS name, don't execute the validation in this case.
# check_traceroute will perform the name resolution for us.
return
if not ip_address_version == ip_version_arg:
raise ProtocolVersionError(
'IP protocol version "%s" not the same as the IP address version "%s".'
% (ip_version_arg, ip_address_version)
)
def usage():
sys.stdout.write(
"""check_traceroute -{c|w|C|W} ROUTE [-{o|c|w|O|C|W} ROUTE...] TARGET
Check by which routes TARGET is being reached. Each possible route is being
prefixed with a state option:
-w Make outcome WARN if that route is present
-W Make outcome WARN if that route is missing
-c Make outcome CRIT if that route is present
-C Make outcome CRIT if that route is missing
Other options:
-h, --help show this help and exit
--debug show Python exceptions verbosely
-n disable reverse DNS lookups
-I Use ICMP ECHO for probes
-T Use TCP SYN for probes
-4 Use IPv4
-6 Use IPv6
-p port Set the destination port to use
-m max_ttl Set the max number of hops (max TTL to be reached). Default is 30
-s src_addr Use source address
-i device Specify a network interface to operate with
-q nqueries Set the number of probes per each hop. Default is 3
"""
)
def main(args=None):
if args is None:
args = sys.argv[1:]
os.unsetenv("LANG")
opt_verbose = 0
opt_debug = False
opt_nodns = False
opt_method = None
opt_address_family = None
opt_port = None
opt_source_int = None
opt_source_addr = None
opt_queries = None
opt_max_ttl = None
short_options = "hw:W:c:C:i:s:p:q:m:nTI46"
long_options = [
"verbose",
"help",
"debug",
]
route_params = []
try:
opts, args = getopt.getopt(args, short_options, long_options)
if len(args) < 1:
usage()
raise MissingValueError("Please specify the target destination.")
target_address = args[0]
# first parse modifers
for o, a in opts:
if o in ["-v", "--verbose"]:
opt_verbose += 1
elif o in ["-d", "--debug"]:
opt_debug = True
elif o in ["-w", "-W", "-c", "-C"]:
route_params.append((o[1], a))
elif o == "-n":
opt_nodns = True
elif o in ["-T", "-I"]:
opt_method = o
elif o in ["-4", "-6"]:
if opt_address_family:
raise ProtocolVersionError("Cannot use both IPv4 and IPv6")
validate_ip_version(target_address, int(o.lstrip("-")))
opt_address_family = o
elif o in ["-s"]:
opt_source_addr = a
elif o in ["-i"]:
opt_source_int = a
elif o in ["-p"]:
opt_port = a
elif o in ["-q"]:
opt_queries = a
elif o in ["-m"]:
opt_max_ttl = a
# now handle action options
for o, a in opts:
if o in ["-h", "--help"]:
usage()
sys.exit(0)
sto = _execute_traceroute(target_address, opt_nodns, opt_method, opt_address_family, opt_queries,
opt_max_ttl, opt_port, opt_source_addr, opt_source_int)
status, output, perfdata = check_traceroute(sto.split("\n"), route_params)
info_text = output.strip() + "\n%s" % sto
return status, info_text, perfdata
except ExecutionError as e:
return 3, str(e), None
except MissingValueError as e:
return 3, str(e), None
except ProtocolVersionError as e:
return 3, str(e), None
except Exception as e:
if opt_debug:
raise
return 2, "Unhandled exception: %s" % parse_exception(e), None
if __name__ == "__main__":
exitcode, info, perf = main()
output_check_result(info, perf)
sys.exit(exitcode)
{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
'description': 'extended traceroute check plugin\n'
'\n'
'adds the following options:\n'
'- service description suffix\n'
'- alternate destination address\n'
'- destination port for UDP/TCP path trace\n'
'- max hops\n'
'- queries per hop\n'
'- source address \n'
'- source interface (needs root permissions).\n'
'\n'
'Source address and source interface uses the "shell=True" '
'option in in\n'
'the "subprocess.Popen" command. This is highly insecure, so '
'be careful.\n'
'\n'
'Note: the original option TCP path trace also needs root '
'permissions.\n'
'\n'
'To give "traceroute" root permissions you need to set the '
'SUID bit for\n'
'your traceroute program, like "sudo chmod u+s '
'/usr/bin/traceroute.db" \n'
'on Ubuntu 20.04.3 LTS.\n',
'download_url': 'https://thl-cmk.hopto.org',
'files': {'checks': ['check_traceroute'],
'lib': ['nagios/plugins/check_traceroute'],
'web': ['plugins/wato/active_checks_routing.py',
'plugins/metrics/traceroute.py']},
'name': 'active_check_traceroute',
'num_files': 4,
'title': 'Active Check Traceroute',
'version': '20212104.v.0.0.1a',
'version.min_required': '2.0.0',
'version.packaged': '2021.09.20',
'version.usable_until': None}
\ No newline at end of file
#!/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 : 2021-12-04
#
#
#
from cmk.gui.i18n import _
from cmk.gui.plugins.metrics import (
metric_info,
graph_info,
perfometer_info
)
metric_info['hops'] = {
'title': _('Number of hops'),
'unit': 'count',
'color': '41/a',
}
perfometer_info.append({
'type': 'linear',
'segments': ['hops'],
'total': 30,
})
#!/usr/bin/env python3
# -*- encoding: utf-8; py-indent-offset: 4 -*-
#
# (c) 2013 Heinlein Support GmbH
# Robert Sander <r.sander@heinlein-support.de>
#
# This 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 in version 2. This file is distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more de-
# ails. You should have received a copy of the GNU General Public
# License along with GNU Make; see the file COPYING. If not, write
# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA.
from cmk.gui.i18n import _
from cmk.gui.valuespec import (
Dictionary,
ListOf,
Tuple,
Transform,
Checkbox,
DropdownChoice,
TextInput,
Integer,
TextAscii,
)
from cmk.gui.plugins.wato import (
rulespec_registry,
HostRulespec,
)
from cmk.gui.plugins.wato.active_checks import (
RulespecGroupActiveChecks
)
def _ip_address_family_element():
return (
'address_family',
DropdownChoice(
title=_('IP address family'),
choices=[
(None, _('Primary address family')),
('ipv4', _('Enforce IPv4')),
('ipv6', _('Enforce IPv6')),
],
default_value=None,
),
)
def _transform_add_address_family(v):
v.setdefault('address_family', None)
return v
def _valuespec_active_checks_traceroute():
return Transform(
Dictionary(
title=_('Check current routing'),
help=_(
'This active check uses <tt>traceroute</tt> in order to determine the current '
'routing from the monitoring host to the target host. You can specify any number '
'of missing or expected routes in order to detect e.g. an (unintended) failover '
'to a secondary route.'
),
elements=[
('description',
TextAscii(
title=_('Service Description suffix'),
help=_('Must be unique for every host. The service description starts always with \"Routing\".'),
size=30,
)),
('dns',
Checkbox(
title=_('Name resolution'),
label=_('Use DNS to convert IP addresses into hostnames'),
help=_(
'If you use this option, then <tt>traceroute</tt> is <b>not</b> being '
'called with the option <tt>-n</tt>. That means that all IP addresses '
'are tried to be converted into names. This usually adds additional '
'execution time. Also DNS resolution might fail for some addresses.'
),
),),
_ip_address_family_element(),
('routers',
ListOf(
Tuple(
elements=[
TextInput(
title=_('Router (FQDN, IP-Address)'),
allow_empty=False,
),
DropdownChoice(
title=_('How'),
choices=[
('W', _('WARN - if this router is not being used')),
('C', _('CRIT - if this router is not being used')),
('w', _('WARN - if this router is being used')),
('c', _('CRIT - if this router is being used')),
],
),
]
),
title=_('Router that must or must not be used'),
add_label=_('Add Condition'),
),),
('method',
DropdownChoice(
title=_('Method of probing'),
choices=[
(None, _('UDP (default behaviour of traceroute)')),
('icmp', _('ICMP Echo Request')),
('tcp', _('TCP SYN, needs root permissions')),
],
default_value='icmp',
),),
('port',
Integer(
title=_('Port'),
help=_('Set the destination port to use. It is either initial udp port value for \"default\" '
'method (incremented by each probe, default is 33434), or initial seq for \"icmp\" '
'(incremented as well, default from 1), or some constant destination port for other '
'methods (with default of 80 for \"tcp\", 53 for \"udp\", etc.)'),
maxvalue=65535,
),),
('destination_address',
TextAscii(
title=_('Alternate Destination'),
help=_('Path trace to alternate destination instead of \"HOSTADDRESS\".'),
),),
('queries',
Integer(
title=_('Number of queries'),
help=_('Set the number of probes per each hop. Default is 3.'),
default_value=3,
minvalue=1,
maxvalue=10,
),),
('max_ttl',
Integer(
title=_('Max hops'),
help=_('Set the max number of hops (max TTL to be reached). Default is 30'),
default_value=30,
minvalue=1,
maxvalue=255,
),),
('source_interface',
TextAscii(
title=_('Source interface'),
help=_('Specify a network interface to operate with. Needs root permissions.'),
),),
('source_address',
TextAscii(
title=_('Source address'),
help=_('Use source source address for outgoing packets'),
),),
],
optional_keys=['description', 'max_ttl', 'queries', 'destination_address', 'source_address',
'source_interface', 'port'],
),
forth=_transform_add_address_family,
)
rulespec_registry.register(
HostRulespec(
group=RulespecGroupActiveChecks,
match_type='all',
name='active_checks:traceroute',
valuespec=_valuespec_active_checks_traceroute,
)
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment