Refactoring: Changed all check parameters starting with an 'o' to the new rulespec...
[check_mk.git] / checks / winperf_if
blobb9ca9c92ffbaa4a3db4d78184cf8eb43ba59c02c
1 #!/usr/bin/python
2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
9 # | |
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 # Example output from agent
28 # <<<winperf_if>>>
29 # 1366721523.71 510
30 # 3 instances: Ethernetadapter_der_AMD-PCNET-Familie__2_-_Paketplaner-Miniport Ethernetadapter_der_AMD-PCNET-Familie_-_Paketplaner-Miniport MS_TCP_Loopback_interface
31 # -122 43364 1085829 41602 bulk_count
32 # -110 293 4174 932 counter
33 # -244 138 3560 466 counter
34 # -58 155 614 466 counter
35 # 10 100000000 100000000 10000000 rawcount
36 # -246 21219 780491 20801 counter
37 # 14 0 383 466 counter
38 # 16 138 3176 0 counter
39 # 18 0 0 0 rawcount
40 # 20 0 0 0 rawcount
41 # 22 0 1 0 rawcount
42 # -4 22145 305338 20801 counter
43 # 26 0 428 466 counter
44 # 28 155 186 0 counter
45 # 30 0 0 0 rawcount
46 # 32 0 0 0 rawcount
47 # 34 0 0 0 rawcount
48 # <<<winperf_if:sep(44)>>>
49 # Node,MACAddress,Name,NetConnectionID,NetConnectionStatus
50 # WINDOWSXP,08:00:27:8D:47:A4,Ethernetadapter der AMD-PCNET-Familie,LAN-Verbindung,2
51 # WINDOWSXP,,Asynchroner RAS-Adapter,,
52 # WINDOWSXP,08:00:27:8D:47:A4,Paketplaner-Miniport,,
53 # WINDOWSXP,,WAN-Miniport (L2TP),,
54 # WINDOWSXP,50:50:54:50:30:30,WAN-Miniport (PPTP),,
55 # WINDOWSXP,33:50:6F:45:30:30,WAN-Miniport (PPPOE),,
56 # WINDOWSXP,,Parallelanschluss (direkt),,
57 # WINDOWSXP,,WAN-Miniport (IP),,
58 # WINDOWSXP,00:E5:20:52:41:53,Paketplaner-Miniport,,
59 # WINDOWSXP,08:00:27:35:20:4D,Ethernetadapter der AMD-PCNET-Familie,LAN-Verbindung 2,2
60 # WINDOWSXP,08:00:27:35:20:4D,Paketplaner-Miniport,,
62 # Example output for the optional dhcp section. If this plugin is active, any interface for which
63 # dhcp is enabled will warn
64 # <<<dhcp:sep(44)>>>
65 # Node,Description,DHCPEnabled
66 # WINDOWS,Intel(R) PRO/1000 MT-Desktopadapter,TRUE
67 # WINDOWS,WAN Miniport (IP),FALSE
68 # WINDOWS,Microsoft-ISATAP-Adapter,FALSE
69 # WINDOWS,RAS Async Adapter,FALSE
70 # WINDOWS,Intel(R) PRO/1000 MT-Desktopadapter #2,TRUE
73 def winperf_if_canonize_nic_name(name):
74 return name.replace("_", " ").replace(" ", " ").rstrip()
77 def winperf_if_normalize_nic_name(name, nic_names):
78 # Intel[R] PRO 1000 MT-Desktopadapter__3 (perf counter)
79 # Intel(R) PRO/1000 MT-Desktopadapter 3 (wmic name)
80 # Intel(R) PRO/1000 MT-Desktopadapter #3 (wmic InterfaceDescription)
81 mod_nic_name = name
82 for from_token, to_token in [("/", " "), ("(", "["), (")", "]"), ("#", " ")]:
83 for n in nic_names:
84 if from_token in n:
85 # we do not modify it if this character is in any of the counter names
86 break
87 else:
88 mod_nic_name = mod_nic_name.replace(from_token, to_token).replace(" ", " ")
89 return mod_nic_name
92 def parse_winperf_if(info):
93 try:
94 timestamp_info, names_info, data = info[0], info[1], info[2:]
95 except (IndexError, ValueError):
96 return None, [], [], [], {}
98 try:
99 agent_timestamp = float(timestamp_info[0])
100 except (IndexError, ValueError):
101 agent_timestamp = None
103 nic_names = map(winperf_if_canonize_nic_name, names_info[2:])
104 nic_index = winperf_if_get_nic_index_map(nic_names)
106 lines = iter(data)
107 lines.next() # skip line with timestamp and counter number
108 lines.next() # skip interface line (already evaluated)
109 nics = dict([(n, {}) for n in nic_names])
111 # Scan lines with counters
112 is_teaming_line = False
113 teaming_headers = []
114 teaming_info = {}
116 dhcp_lines = []
118 try:
119 while True:
120 line = lines.next()
122 if line[0] == "[dhcp_start]":
123 line = lines.next() # skip start line
124 while line[0] != "[dhcp_end]":
125 dhcp_lines.append(line)
126 line = lines.next()
127 line = lines.next() # skip end line
129 # Process teaming information
130 if line[0].startswith("[teaming_start]"):
131 is_teaming_line = True
132 teaming_headers = lines.next()
133 continue
134 elif line[0].startswith("[teaming_end]"):
135 is_teaming_line = False
136 continue
137 if is_teaming_line:
138 as_dict = dict(zip(teaming_headers, [x.rstrip() for x in line]))
139 for guid in as_dict["GUID"].split(";"):
140 teaming_info[guid] = as_dict
141 continue
143 counter = saveint(line[0])
144 if counter:
145 for nr, value in enumerate(line[1:len(nic_names) + 1]):
146 nics[nic_names[nr]][counter] = int(value)
147 # Not an integer: then this must be the line with the additional
148 # information from wmic (which is optional!)
149 else:
150 headers = line
151 while True:
152 line = lines.next()
153 if line[0].startswith("[teaming_start]"):
154 is_teaming_line = True
155 teaming_headers = lines.next()
156 break
158 as_dict = dict(zip(headers, [x.strip() for x in line]))
160 # Unfortunately teamed interfaces lose their #x index and are no longer
161 # distinguishable with the data from the Win32_NetworkAdapter.
162 # Fortunately this index information is still available in the teaming section
163 # The GUID is used to combine the two datasets
164 guid = as_dict.get("GUID")
165 if guid in teaming_info:
166 guid_to_name = dict(
167 zip(teaming_info[guid]["GUID"].split(";"),
168 teaming_info[guid]["MemberDescriptions"].split(";")))
169 nic_name = winperf_if_canonize_nic_name(guid_to_name[guid])
170 elif "Name" in as_dict:
171 nic_name = winperf_if_canonize_nic_name(as_dict["Name"])
172 else:
173 # There seems a bug with some windows configurations
174 continue
176 found_match = False
178 # we need to ignore data on interfaces in the optional
179 # wmic section which are marked as non-existing, since
180 # it may happen that there are non-existing interfaces
181 # with the same nic_name as an active one (at least on HP
182 # hardware)
183 if as_dict.get("NetConnectionStatus") == "4":
184 continue
186 # Exact match
187 if nic_name in nic_names:
188 found_match = True
190 # In the perf counters the nics have strange suffixes, e.g.
191 # Ethernetadapter der AMD-PCNET-Familie 2 - Paketplaner-Miniport, while
192 # in wmic it's only named "Ethernetadapter der AMD-PCNET-Familie 2".
193 if not found_match:
194 mod_nic_name = winperf_if_normalize_nic_name(nic_name, nic_names)
196 if mod_nic_name not in nic_names:
197 for n in nic_names:
198 if n.startswith(mod_nic_name + " "):
199 l = len(mod_nic_name)
200 if not (n[l:].strip()[0]).isdigit():
201 nic_name = n
202 found_match = True
203 break
204 else:
205 found_match = True
206 nic_name = mod_nic_name
208 if not found_match:
209 # Ignore interfaces that do not have counters
210 continue
212 nics[nic_name].update(as_dict)
213 except StopIteration:
214 pass
216 # Now convert the dicts into the format that is needed by if.include
217 converted = []
219 # Sort NIC names
220 nic_names.sort(reverse=True)
222 for nic_name in nic_names:
223 nic = nics[nic_name]
224 mac_txt = nic.get('MACAddress')
225 bandwidth = saveint(nic.get('Speed'))
226 # Some interfaces report several exabyte as bandwidth when down..
227 if bandwidth > 1024**5:
228 # Greater than petabyte
229 bandwidth = 0
231 if mac_txt:
232 mac = "".join(chr(int(x, 16)) for x in mac_txt.split(':'))
233 else:
234 mac = ''
236 index_info = str(nic_index[nic_name])
237 # Automatically group teamed interfaces
238 if nic.get("GUID") in teaming_info:
239 index_info = (teaming_info[nic.get("GUID")]["TeamName"], index_info)
241 # NetConnectionTable Stuff:
243 # if we have no status, but link information, we assume IF is connected
244 connection_status = nic.get('NetConnectionStatus')
245 if not connection_status:
246 connection_status = '2'
248 # Windows NetConnectionStatus Table
249 connection_states = {
250 '0': ('2', 'Disconnected'),
251 '1': ('2', 'Connecting'),
252 '2': ('1', 'Connected'),
253 '3': ('2', 'Disconnecting'),
254 '4': ('2', 'Hardware not present'),
255 '5': ('2', 'Hardware disabled'),
256 '6': ('2', 'Hardware malfunction'),
257 '7': ('7', 'Media disconnected'),
258 '8': ('2', 'Authenticating'),
259 '9': ('2', 'Authentication succeeded'),
260 '10': ('2', 'Authentication failed'),
261 '11': ('2', 'Invalid address'),
262 '12': ('2', 'Credentials required'),
265 # ifOperStatus Table
266 # 1 up
267 # 2 down
268 # 3 testing
269 # 4 unknown
270 # 5 dormant
271 # 6 notPresent
272 # 7 lowerLayerDown
274 ifoperstatus_annotated = connection_states[connection_status]
276 converted.append((
277 index_info,
278 nic_name,
279 "loopback" in nic_name.lower() and '24' or '6',
280 bandwidth or nic[10], # Bandwidth
281 ifoperstatus_annotated, # ifOperStatus
282 nic[-246], # ifInOctets,
283 nic[14], # inucast
284 0, # inmcast
285 nic[16], # non-unicast empfangen
286 nic[18], # ifInDiscards
287 nic[20], # ifInErrors
288 nic[-4], # ifOutOctets (Bytes gesendet)
289 nic[26], # outucast
291 nic[28], # outnonucast
292 nic[30], # ifOutDiscards
293 nic[32], # ifOutErrors
294 nic[34], # ifOutQLen
295 nic.get('NetConnectionID', nic_name),
296 mac,
299 return agent_timestamp, converted, dhcp_lines, nic_names, nic_index
302 def winperf_if_get_nic_index_map(nic_names):
303 return {x: i + 1 for i, x in enumerate(nic_names)}
306 def check_if_dhcp(item, _no_params, info, nic_names, nic_index):
307 lines = iter(info)
308 header = lines.next()
310 for line in lines:
311 # wmic is bugged on some windows versions such that we can't use proper csv output, only
312 # visual tables. Those aren't properly split up by the check_mk parser.
313 # Try to fix that mess
315 # assumption 1: header fields contain no spaces
316 num_fields = len(header)
317 # assumption 2: only the leftmost field contains spaces
318 lm_field = " ".join(line[:(num_fields - 1) * -1])
320 line = [lm_field] + line[(len(line) - num_fields + 1):]
322 as_dict = dict(zip(header, [x.rstrip() for x in line]))
323 name = winperf_if_normalize_nic_name(as_dict["Description"], nic_names)
324 idx = nic_index.get(name)
326 try:
327 match = idx == int(item)
328 except ValueError:
329 match = name == item
331 if match:
332 if as_dict["DHCPEnabled"] == "TRUE":
333 return 1, "dhcp enabled"
334 return 0, "dhcp %s" % as_dict["DHCPEnabled"]
335 return 0, ""
338 def inventory_winperf_if(parsed):
339 _agent_timestamp, perf, _dhcp, _nic_names, _nic_index = parsed
341 # dhcp info irrelevant to inventory
342 return inventory_if_common(perf)
345 def check_winperf_if(item, params, parsed):
346 agent_timestamp, perf, dhcp, nic_names, nic_index = parsed
348 yield check_if_common(item, params, perf, group_name="Teaming", timestamp=agent_timestamp)
349 yield check_if_dhcp(item, params, dhcp, nic_names, nic_index)
352 check_info["winperf_if"] = {
353 'parse_function': parse_winperf_if,
354 'inventory_function': inventory_winperf_if,
355 'check_function': check_winperf_if,
356 'service_description': 'Interface %s',
357 'has_perfdata': True,
358 'includes': ['if.include'],
359 'group': 'if',
360 'default_levels_variable': 'if_default_levels'