Refactoring: Changed all check parameters starting with an 'o' to the new rulespec...
[check_mk.git] / checks / brocade_fcport
blobff8d0940180e0ffa5131d943fe34b1914f781fda
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 # lookup tables for check implementation
28 # Taken from swFCPortPhyState
29 brocade_fcport_phystates = {
30 0: '',
31 1: 'no card',
32 2: 'no transceiver',
33 3: 'laser fault',
34 4: 'no light',
35 5: 'no sync',
36 6: 'in sync',
37 7: 'port fault',
38 8: 'diag fault',
39 9: 'lock ref',
40 10: 'validating',
41 11: 'invalid module',
42 12: 'no sig det',
43 13: 'unknown',
46 # Taken from swFCPortOpStatus
47 brocade_fcport_opstates = {
48 0: 'unknown',
49 1: 'online',
50 2: 'offline',
51 3: 'testing',
52 4: 'faulty',
55 # Taken from swFCPortAdmStatus
56 brocade_fcport_admstates = {
57 0: '',
58 1: 'online',
59 2: 'offline',
60 3: 'testing',
61 4: 'faulty',
64 # Taken from swFCPortSpeed
65 brocade_fcport_speed = {
66 0: 'unknown',
67 1: '1Gbit',
68 2: '2Gbit',
69 3: 'auto-Neg',
70 4: '4Gbit',
71 5: '8Gbit',
72 6: '10Gbit',
73 7: 'unknown',
74 8: '16Gbit',
77 # Taken from swNbBaudRate
78 isl_speed = {
79 "1": 0, # other (1) - None of the following.
80 "2": 0.155, # oneEighth (2) - 155 Mbaud.
81 "4": 0.266, # quarter (4) - 266 Mbaud.
82 "8": 0.532, # half (8) - 532 Mbaud.
83 "16": 1, # full (16) - 1 Gbaud.
84 "32": 2, # double (32) - 2 Gbaud.
85 "64": 4, # quadruple (64) - 4 Gbaud.
86 "128": 8, # octuple (128) - 8 Gbaud.
87 "256": 10, # decuple (256) - 10 Gbaud.
88 "512": 16, # sexdecuple (512) - 16 Gbaud
91 factory_settings["brocade_fcport_default_levels"] = {
92 "rxcrcs": (3.0, 20.0), # allowed percentage of CRC errors
93 "rxencoutframes": (3.0, 20.0), # allowed percentage of Enc-OUT Frames
94 "rxencinframes": (3.0, 20.0), # allowed percentage of Enc-In Frames
95 "notxcredits": (3.0, 20.0), # allowed percentage of No Tx Credits
96 "c3discards": (3.0, 20.0), # allowed percentage of C3 discards
97 "assumed_speed": 2.0, # used if speed not available in SNMP data
101 def parse_brocade_fcport(info):
102 if_info, link_info, speed_info, if64_info = info
103 isl_ports = {}
104 if len(info) > 1:
105 isl_ports = dict(link_info)
107 for index, entry in enumerate(speed_info):
108 # if-table and brocade-if-table do NOT have same length but
109 # http://community.brocade.com/t5/Fibre-Channel-SAN/SNMP-FC-port-speed/td-p/64980
110 # says that "1073741824" from if-table correlates with index 1 from
111 # brocade-if-table.
112 if entry[0] == "1073741824":
113 speed_info = [x[1:] for x in speed_info[index:]]
114 break
116 if len(if_info) == len(speed_info):
117 # extract the speed from IF-MIB::ifHighSpeed.
118 # unfortunately ports in the IF-MIB and the brocade MIB
119 # dont have a common index. We hope that at least
120 # the FC ports have the same sequence in both lists.
121 # here we go through ports of the IF-NIB, but consider only FC ports (type 56)
122 # and assume that the sequence number of the FC port here is the same
123 # as the sequence number in the borcade MIB (pindex = item_index)
124 if_table = [x + y if y[0] == "56" else ["", x[-2]] for x, y in zip(if_info, speed_info)]
125 else:
126 if_table = [x + ["", x[-2]] for x in if_info]
128 parsed = []
129 for index, phystate, opstate, admstate, txwords, rxwords, txframes, \
130 rxframes, notxcredits, rxcrcs, rxencinframes, rxencoutframes, \
131 c3discards, brocade_speed, portname, porttype, ifspeed in if_table:
133 # Since FW v8.0.1b [rx/tx]words are no longer available
134 # Use 64bit counters if available
135 bbcredits = None
136 if len(if64_info) > 0:
137 fcmgmt_portstats = []
138 for oidend, tx_elements, rx_elements, bbcredits_64 in if64_info:
139 if index == oidend.split(".")[-1]:
140 fcmgmt_portstats = [
141 binstring_to_int(''.join(map(chr, tx_elements))) / 4,
142 binstring_to_int(''.join(map(chr, rx_elements))) / 4,
143 binstring_to_int(''.join(map(chr, bbcredits_64))),
145 break
146 if fcmgmt_portstats:
147 txwords = fcmgmt_portstats[0]
148 rxwords = fcmgmt_portstats[1]
149 bbcredits = fcmgmt_portstats[2]
150 else:
151 txwords = 0
152 rxwords = 0
154 try:
155 islspeed = None
156 if index in isl_ports:
157 islspeed = isl_speed.get(isl_ports[index])
159 parsed.append({
160 "index": int(index),
161 "phystate": int(phystate),
162 "opstate": int(opstate),
163 "admstate": int(admstate),
164 "txwords": int(txwords),
165 "rxwords": int(rxwords),
166 "txframes": int(txframes),
167 "rxframes": int(rxframes),
168 "notxcredits": int(notxcredits),
169 "rxcrcs": int(rxcrcs),
170 "rxencinframes": int(rxencinframes),
171 "rxencoutframes": int(rxencoutframes),
172 "c3discards": int(c3discards),
173 "brocade_speed": brocade_speed,
174 "portname": portname,
175 "porttype": porttype,
176 "ifspeed": int(ifspeed),
177 "is_isl": index in isl_ports,
178 "islspeed": islspeed, # Might be None
179 "bbcredits": bbcredits, # Might be None
181 except ValueError:
182 continue
183 return parsed
186 def inventory_brocade_fcport(parsed):
187 settings = host_extra_conf_merged(host_name(), brocade_fcport_inventory)
189 inventory = []
190 for if_entry in parsed:
191 admstate = if_entry["admstate"]
192 phystate = if_entry["phystate"]
193 opstate = if_entry["opstate"]
194 if brocade_fcport_inventory_this_port(
195 admstate=admstate,
196 phystate=phystate,
197 opstate=opstate,
198 settings=settings,
200 inventory.append((
201 brocade_fcport_getitem(
202 number_of_ports=len(parsed),
203 index=if_entry["index"],
204 portname=if_entry["portname"],
205 is_isl=if_entry["is_isl"],
206 settings=settings),
207 '{ "phystate": [%d], "opstate": [%d], "admstate": [%d] }' % (phystate, opstate,
208 admstate),
210 return inventory
213 def check_brocade_fcport(item, params, parsed):
214 found_entry = None
215 for if_entry in parsed:
216 if int(item.split()[0]) + 1 == if_entry["index"]:
217 found_entry = if_entry
218 break
220 if found_entry is None:
221 return 3, "No SNMP data found"
223 index = found_entry["index"]
224 txwords = found_entry["txwords"]
225 rxwords = found_entry["rxwords"]
226 txframes = found_entry["txframes"]
227 rxframes = found_entry["rxframes"]
228 notxcredits = found_entry["notxcredits"]
229 rxcrcs = found_entry["rxcrcs"]
230 rxencinframes = found_entry["rxencinframes"]
231 rxencoutframes = found_entry["rxencoutframes"]
232 c3discards = found_entry["c3discards"]
233 brocade_speed = found_entry["brocade_speed"]
234 is_isl = found_entry["is_isl"]
235 isl_speed = found_entry["islspeed"]
236 bbcredits = found_entry["bbcredits"]
237 speed = found_entry["ifspeed"]
239 this_time = time.time()
240 average = params.get("average") # range in minutes
241 bw_thresh = params.get("bw")
243 summarystate = 0
244 output = []
245 perfdata = []
246 perfaverages = []
248 # Lookup port speed in ISL table for ISL ports (older switches do not provide this
249 # information in the normal table)
250 if is_isl and isl_speed is not None:
251 gbit = isl_speed
252 speedmsg = ("ISL speed: %.0f Gbit/s" % isl_speed)
254 else: # no ISL port
255 if brocade_fcport_speed.get(brocade_speed, "unknown") in ["auto-Neg", "unknown"]:
256 if speed > 0:
257 # use actual speed of port if available
258 gbit = speed / 1000
259 speedmsg = "Speed: %g Gbit/s" % gbit
260 else:
261 # let user specify assumed speed via check parameter, default is 2.0
262 gbit = params.get("assumed_speed")
263 speedmsg = "Assumed speed: %g Gbit/s" % gbit
264 else:
265 gbit = float(brocade_fcport_speed[brocade_speed].replace("Gbit", ""))
266 speedmsg = "%.0f Gbit/s" % gbit
268 output.append(speedmsg)
270 # convert gbit netto link-rate to Byte/s (8/10 enc)
271 wirespeed = gbit * 1000000000.0 * 0.8 / 8
272 in_bytes = 4 * get_rate("brocade_fcport.rxwords.%s" % index, this_time, rxwords)
273 out_bytes = 4 * get_rate("brocade_fcport.txwords.%s" % index, this_time, txwords)
275 # B A N D W I D T H
276 # convert thresholds in percentage into MB/s
277 if bw_thresh is None: # no levels
278 warn_bytes, crit_bytes = None, None
279 else:
280 warn, crit = bw_thresh
281 if isinstance(warn, float):
282 warn_bytes = wirespeed * warn / 100.0
283 else: # in MB
284 warn_bytes = warn * 1048576.0
285 if isinstance(crit, float):
286 crit_bytes = wirespeed * crit / 100.0
287 else: # in MB
288 crit_bytes = crit * 1048576.0
290 for what, value in [("In", in_bytes), ("Out", out_bytes)]:
291 output.append("%s: %s/s" % (what, get_bytes_human_readable(value)))
292 perfdata.append((what.lower(), value, warn_bytes, crit_bytes, 0, wirespeed))
293 # average turned on: use averaged traffic values instead of current ones
294 if average:
295 value = get_average("brocade_fcport.%s.%s.avg" % (what, item), this_time, value,
296 average)
297 output.append("Average (%d min): %s/s" % (average, get_bytes_human_readable(value)))
298 perfaverages.append(("%s_avg" % what.lower(), value, warn_bytes, crit_bytes, 0,
299 wirespeed))
301 # handle levels for in/out
302 if crit_bytes is not None and value >= crit_bytes:
303 summarystate = 2
304 output.append(" >= %s/s(!!)" % (get_bytes_human_readable(crit_bytes)))
305 elif warn_bytes is not None and value >= warn_bytes:
306 summarystate = max(1, summarystate)
307 output.append(" >= %s/s(!!)" % (get_bytes_human_readable(warn_bytes)))
309 # put perfdata of averages after perfdata for in and out in order not to confuse the perfometer
310 perfdata.extend(perfaverages)
312 # R X F R A M E S & T X F R A M E S
313 # Put number of frames into performance data (honor averaging)
314 rxframes_rate = get_rate("brocade_fcport.rxframes.%s" % index, this_time, rxframes)
315 txframes_rate = get_rate("brocade_fcport.txframes.%s" % index, this_time, txframes)
316 for what, value in [("rxframes", rxframes_rate), ("txframes", txframes_rate)]:
317 perfdata.append((what, value))
318 if average:
319 value = get_average("brocade_fcport.%s.%s.avg" % (what, item), this_time, value,
320 average)
321 perfdata.append(("%s_avg" % what, value))
323 # E R R O R C O U N T E R S
324 # handle levels on error counters
325 for descr, counter, value, ref in [
326 ("CRC errors", "rxcrcs", rxcrcs, rxframes_rate),
327 ("ENC-Out", "rxencoutframes", rxencoutframes, rxframes_rate),
328 ("ENC-In", "rxencinframes", rxencinframes, rxframes_rate),
329 ("C3 discards", "c3discards", c3discards, txframes_rate),
330 ("No TX buffer credits", "notxcredits", notxcredits, txframes_rate),
332 per_sec = get_rate("brocade_fcport.%s.%s" % (counter, index), this_time, value)
333 perfdata.append((counter, per_sec))
335 # if averaging is on, compute average and apply levels to average
336 if average:
337 per_sec_avg = get_average("brocade_fcport.%s.%s.avg" % \
338 (counter, item), this_time, per_sec, average)
339 perfdata.append(("%s_avg" % counter, per_sec_avg))
341 # compute error rate (errors in relation to number of frames) (from 0.0 to 1.0)
342 if ref > 0 or per_sec > 0:
343 rate = per_sec / (ref + per_sec)
344 else:
345 rate = 0
346 text = "%s: %.2f%%" % (descr, rate * 100.0)
348 # Honor averaging of error rate
349 if average:
350 rate = get_average("brocade_fcport.%s.%s.avgrate" % (counter, item), this_time, rate,
351 average)
352 text += ", Average: %.2f%%" % (rate * 100.0)
354 error_percentage = rate * 100.0
355 warn, crit = params.get(counter, (None, None))
356 if crit is not None and error_percentage >= crit:
357 summarystate = 2
358 text += "(!!)"
359 output.append(text)
360 elif warn is not None and error_percentage >= warn:
361 summarystate = max(1, summarystate)
362 text += "(!)"
363 output.append(text)
365 # P O R T S T A T E
366 for dev_state, state_key, state_info, warn_states, state_map in [
367 (found_entry["phystate"], "phystate", "Physical", [1, 6], brocade_fcport_phystates),
368 (found_entry["opstate"], "opstate", "Operational", [1, 3], brocade_fcport_opstates),
369 (found_entry["admstate"], "admstate", "Administrative", [0, 1, 3],
370 brocade_fcport_admstates),
372 errorflag = ""
373 if params.get(state_key) is not None and dev_state != params[state_key] \
374 and not (isinstance(params[state_key], list) and dev_state in map(int, params[state_key])):
375 if dev_state in warn_states:
376 errorflag = "(!)"
377 summarystate = max(summarystate, 1)
378 else:
379 errorflag = "(!!)"
380 summarystate = 2
381 output.append("%s: %s%s" % (state_info, state_map[dev_state], errorflag))
383 if bbcredits is not None:
384 bbcredit_rate = get_rate("brocade_fcport.bbcredit.%s" % (item), this_time, bbcredits)
385 perfdata.append(("fc_bbcredit_zero", bbcredit_rate))
387 return (summarystate, ', '.join(output), perfdata)
390 check_info["brocade_fcport"] = {
391 'parse_function': parse_brocade_fcport,
392 'check_function': check_brocade_fcport,
393 'inventory_function': inventory_brocade_fcport,
394 'service_description': 'Port %s',
395 'has_perfdata': True,
396 'snmp_info': [
397 ( ".1.3.6.1.4.1.1588.2.1.1.1.6.2.1",[
398 1, # swFCPortIndex
399 3, # swFCPortPhyState
400 4, # swFCPortOpStatus
401 5, # swFCPortAdmStatus
402 11, # swFCPortTxWords
403 12, # swFCPortRxWords
404 13, # swFCPortTxFrames
405 14, # swFCPortRxFrames
406 20, # swFCPortNoTxCredits
407 22, # swFCPortRxCrcs
408 21, # swFCPortRxEncInFrs
409 26, # swFCPortRxEncOutFrs
410 28, # swFCPortC3Discards
411 35, # swFCPortSpeed, deprecated from at least firmware version 7.2.1
412 36, # swFCPortName (not supported by all devices)
415 # Information about Inter-Switch-Links (contains baud rate of port)
416 ( ".1.3.6.1.4.1.1588.2.1.1.1.2.9.1", [
417 2, # swNbMyPort
418 5, # swNbBaudRate
420 # new way to get port speed supported by Brocade
421 ( ".1.3.6.1.2.1", [
422 OID_END,
423 "2.2.1.3", # ifType, needed to extract fibre channel ifs only (type 56)
424 "31.1.1.1.15", # IF-MIB::ifHighSpeed
426 # Not every device supports that
427 (".1.3.6.1.3.94.4.5.1", [
428 OID_END,
429 BINARY("6"), # FCMGMT-MIB::connUnitPortStatCountTxElements
430 BINARY("7"), # FCMGMT-MIB::connUnitPortStatCountRxElements
431 BINARY("8"), # FCMGMT-MIB::connUnitPortStatCountBBCreditZero
434 'snmp_scan_function' : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.1588.2.1.1") \
435 and oid(".1.3.6.1.4.1.1588.2.1.1.1.6.2.1.*") is not None,
436 'includes' : [ 'brocade.include' ],
437 'group' : 'brocade_fcport',
438 'default_levels_variable' : 'brocade_fcport_default_levels',