2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
10 # | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
27 # This check does a traceroute to the specified target host
28 # (usually $HOSTADDRESS$ itself) and checks which route(s) are
29 # being taken. That way you can check if your preferred or
30 # some alternative route in in place.
31 # traceroute is expected to be in the search path and installed
34 # Example output from traceroute -n
35 # traceroute to www.google.de (173.194.44.55), 30 hops max, 60 byte packets
36 # 1 10.10.11.4 0.419 ms 0.444 ms 0.518 ms
37 # 2 33.117.16.28 14.359 ms 14.371 ms 14.434 ms
38 # 3 112.18.7.119 14.750 ms 14.765 ms 19.530 ms
39 # 4 184.50.190.61 17.844 ms 17.865 ms 17.862 ms
40 # 5 67.249.94.88 24.285 ms 78.527 ms 26.834 ms
41 # 6 209.85.240.99 27.910 ms 27.420 ms 27.442 ms
42 # 7 173.194.44.55 26.583 ms 20.410 ms 23.142 ms
44 # Output without -n option:
45 # traceroute to www.google.de (173.194.44.56), 30 hops max, 60 byte packets
46 # 1 fritz.box (10.10.11.4) 0.570 ms 0.606 ms 0.677 ms
47 # 2 foo-bar.x-online.net (33.117.16.28) 14.566 ms 14.580 ms 14.658 ms
48 # 3 xu-2-3-0.rt-inxs-1.x-online.net (112.13.6.109) 18.214 ms 18.228 ms 18.221 ms
50 # 5 66.249.94.88 (66.249.94.88) 24.481 ms 24.498 ms 24.271 ms
51 # 6 209.85.240.99 (209.85.240.99) 27.628 ms 21.605 ms 21.943 ms
52 # 7 muc03s08-in-f24.1e100.net (173.194.44.56) 21.277 ms 22.236 ms 22.192 ms
54 # It is also possible that for one hop several different answers appear:
55 # 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
57 # if DNS fails then it looks like this:
58 # 5 66.249.94.88 (66.249.94.88) 24.481 ms 24.498 ms 24.271 ms
59 # 6 209.85.240.99 (209.85.240.99) 27.628 ms 21.605 ms 21.943 ms
67 def option_to_state(c
):
68 return {"w": 1, "c": 2}[c
.lower()]
71 def check_traceroute(target
, routes
, nodns
, method
):
78 p
= subprocess
.Popen(cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=False)
79 sto
, ste
= p
.communicate()
81 return 3, "UNKNOWN - " + ste
.replace("\n", " "), ""
83 # find all visited routers
85 for line
in sto
.split("\n")[1:]:
86 parts
= line
.strip().split()
88 if part
.count(".") == 3 or (not part
[0].isdigit() and part
not in ["ms", "*"]):
89 part
= part
.lstrip("(").rstrip(",").rstrip(")")
95 for option
, route
in routes
:
96 s
= option_to_state(option
)
97 if option
.islower() and route
in routers
:
99 bad_routers
.append("%s(%s)" % (route
, "!" * s
))
100 elif option
.isupper() and route
not in routers
:
101 state
= max(state
, s
)
102 missing_routers
.append("%s(%s)" % (route
, "!" * s
))
104 info
= "%s - %d hops, missing routers: %s, bad routers: %s" % (
110 len(sto
.split("\n")[1:]),
111 missing_routers
and ", ".join(missing_routers
) or "none",
112 bad_routers
and ", ".join(bad_routers
) or "none",
115 return state
, info
, sto
118 def bail_out(reason
):
119 sys
.stderr
.write("FATAL ERROR: %s\n" % reason
)
124 sys
.stdout
.write("""check_traceroute -{c|w|C|W} ROUTE [-{o|c|w|O|C|W} ROUTE...] TARGET
126 Check by which routes TARGET is being reached. Each possible route is being
127 prefixed with a state option:
129 -w Make outcome WARN if that route is present
130 -W Make outcome WARN if that route is missing
131 -c Make outcome CRIT if that route is present
132 -C Make outcome CRIT if that route is missing
136 -h, --help show this help and exit
137 --debug show Python exceptions verbosely
138 -n disable reverse DNS lookups
139 -I Use ICMP ECHO for probes
140 -T Use TCP SYN for probes
152 short_options
= "hw:W:c:C:nTI"
162 opts
, args
= getopt
.getopt(sys
.argv
[1:], short_options
, long_options
)
164 # first parse modifers
166 if o
in ['-v', '--verbose']:
168 elif o
in ['-d', '--debug']:
170 elif o
in ['-w', '-W', '-c', '-C']:
171 route_params
.append((o
[1], a
))
174 elif o
in ['-T', '-I']:
177 # now handle action options
179 if o
in ['-h', '--help']:
184 bail_out("Please specify the target destination.")
186 target_address
= args
[0]
188 status
, output
, long_output
= check_traceroute(target_address
, route_params
, opt_nodns
,
190 sys
.stdout
.write(output
.strip() + "\n" + long_output
)