Cleanup config.nodes_of
[check_mk.git] / checks / cisco_qos
blob9dc29c4481659b09244885eddfa8443b942bd58c
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 # Author: Lars Michelsen <lm@mathias-kettner.de>
29 # Relevant SNMP OIDs:
30 # .1.3.6.1.4.1.9.9.166.1.1.1.1.4.144 9
31 # .1.3.6.1.4.1.9.9.166.1.1.1.1.4.258 16
32 # .1.3.6.1.4.1.9.9.166.1.1.1.1.4.400 25
34 # .1.3.6.1.4.1.9.9.166.1.6.1.1.1.3974704 "6cos"
35 # .1.3.6.1.4.1.9.9.166.1.6.1.1.1.6208592 "ingress-map"
37 # .1.3.6.1.4.1.9.9.166.1.7.1.1.1.1593 "class-default"
38 # .1.3.6.1.4.1.9.9.166.1.7.1.1.1.18785 "EF"
39 # .1.3.6.1.4.1.9.9.166.1.7.1.1.1.284945 "AF1"
40 # .1.3.6.1.4.1.9.9.166.1.7.1.1.1.284961 "AF2"
41 # .1.3.6.1.4.1.9.9.166.1.7.1.1.1.284977 "AF3"
43 # http://www.oidview.com/mibs/9/CISCO-CLASS-BASED-QOS-MIB.html
45 # TEST:
47 # search class table:
48 # .1.3.6.1.4.1.9.9.166.1.7.1.1.1.284945 (cbQosCMName) "AF1"
49 # class_id = 284945 (cbQosConfigIndex)
51 # search config table for matching value
52 # .1.3.6.1.4.1.9.9.166.1.5.1.1.2.144.5256 284945
53 # key = 144.5256 (cbQosPolicyIndex: 144, cbQosObjectsIndex: 5256)
55 # search if table for matchin if_id: 144
56 # .1.3.6.1.4.1.9.9.166.1.1.1.1.4.144 (cbQosIfIndex) 9
57 # if_policy = 9 (ifIndex -> standard mib)
59 # get config_id from config table using if_id.if_id 144.144
60 # .1.3.6.1.4.1.9.9.166.1.5.1.1.2.144.144 (cbQosConfigIndex) 6208592
61 # config_index = 6208592
63 # get policy name using the policy_index
64 # .1.3.6.1.4.1.9.9.166.1.6.1.1.1.6208592 "ingress-map"
65 # policy_name = "ingress-map"
67 # get post bytes using the key
68 # .1.3.6.1.4.1.9.9.166.1.15.1.1.9.144.5256 0
69 # post_bytes = 0
71 # get dropped bytes using the key
72 # .1.3.6.1.4.1.9.9.166.1.15.1.1.16.144.5256 0
73 # dropped_bytes = 0
75 # get if_name using the if_policy: 9
76 # .1.3.6.1.2.1.31.1.1.1.1.9 Vl1
77 # if_name = Vl1
79 # get if_speed using the if_policy: 9
80 # .1.3.6.1.2.1.2.2.1.5.9 100000000
81 # if_speed = 100000000
83 ###
84 # Test to find the badwidth of the classes. Not finished...
86 # 'cbQosObjectsType' => {
87 # 1 => 'policymap',
88 # 2 => 'classmap',
89 # 3 => 'matchStatement',
90 # 4 => 'queueing',
91 # 5 => 'randomDetect',
92 # 6 => 'trafficShaping',
93 # 7 => 'police',
94 # 8 => 'set' },
96 # Index:
97 # .1.3.6.1.4.1.9.9.166.1.5.1.1.2.258.1244739 1608
99 # Type:
100 # .1.3.6.1.4.1.9.9.166.1.5.1.1.3.258.1244739 4
102 # Parent ID:
103 # .1.3.6.1.4.1.9.9.166.1.5.1.1.4.258.1244739 6184
105 # cbQosQueueingStatsEntry:
106 # .1.3.6.1.4.1.9.9.166.1.18.1.1.2.258.1244739 64
107 # ...
109 # Index:
110 # .1.3.6.1.4.1.9.9.166.1.5.1.1.2.258.6184 18785
111 # Type:
112 # .1.3.6.1.4.1.9.9.166.1.5.1.1.3.258.6184 2
113 # Parent ID:
114 # .1.3.6.1.4.1.9.9.166.1.5.1.1.4.258.6184 258
116 # get cbQosQueueingCfgBandwidth
117 # .1.3.6.1.4.1.9.9.166.1.9.1.1.1.1608 3094
119 cisco_qos_default_levels = (None, None, 0.01, 0.01)
121 #factory_settings["cisco_qos_default_values"] = {
122 # "drop" : (0.01, 0.01)
126 def cisco_qos_get_config_entries_by_class_id(config, class_id):
127 return [if_index.split('.') for if_index, value in config.iteritems() if value == class_id]
130 def inventory_cisco_qos(info):
131 if len(info) == 12:
132 ifs = dict(info[0])
133 config = dict([('.'.join(oid.split('.')[-2:]), value) for oid, value in info[3]])
134 if_names = dict(info[6])
136 # Find all interfaces for each class and create one service for each pair
137 items = []
138 for class_id, class_name in info[2]:
139 # Get interface ids which use this qos class
140 for policy_id, _objects_id in cisco_qos_get_config_entries_by_class_id(
141 config, class_id):
142 if ifs.get(policy_id) in if_names:
143 if_name = if_names[ifs[policy_id]]
144 items += [('%s: %s' % (if_name, class_name), {})]
146 return items
149 def check_cisco_qos(item, params, info):
150 # Convert old params definitions
151 # Note: the float values of the post levels are converted to int
152 had_legacy_params = False
153 if isinstance(params, tuple):
154 params = {
155 "post": tuple(int(x) if x else None for x in params[0:2]),
156 "drop": params[2:4],
158 had_legacy_params = True
160 unit = params.get("unit", "bit")
161 average = params.get("average")
162 post_warn, post_crit = params.get("post", (None, None))
163 drop_warn, drop_crit = params.get("drop", (None, None))
165 # Load values and format them
166 ifs = dict(info[0])
167 policies = dict(info[1])
168 classes = dict(info[2])
169 config = dict([('.'.join(oid.split('.')[-2:]), value) for oid, value in info[3]])
170 post_bytes = dict([('.'.join(oid.split('.')[-2:]), value) for oid, value in info[4]])
171 drop_bytes = dict([('.'.join(oid.split('.')[-2:]), value) for oid, value in info[5]])
172 if_names = dict(info[6])
173 if_speeds = dict(info[7])
174 parents = dict(info[8])
175 if_qos_bandwidth = dict(info[9])
176 if_qos_bandwidth_units = dict(info[10])
177 object_types = dict(info[11])
179 if_name, class_name = item.split(': ')
181 # Gather the class id by class_name
182 class_id = None
183 for cid, cname in classes.iteritems():
184 if class_name == cname:
185 class_id = cid
186 break
188 # Gather the interface id by class_name
189 if_id = None
190 for iid2 in ifs.itervalues():
191 if if_name == if_names.get(iid2):
192 if_id = iid2
193 break
195 if not if_id or not class_id:
196 return (3, "QoS class not found for that interface")
198 policy_id, objects_id, policy_map_id, policy_name = None, None, None, None
199 for this_policy_id, this_objects_id in cisco_qos_get_config_entries_by_class_id(
200 config, class_id):
201 if if_id != ifs[this_policy_id]:
202 continue # skip the ones of other interfaces
204 # Get the policy_map_id. To retrieve this get one of the config entries
205 # of type "policy map" from the config table. All of this type should have
206 # the same value, which is then the policy_map_id.
207 for key in object_types.keys():
208 if key.startswith(this_policy_id + '.') and object_types[key] == '1':
209 policy_map_id = config[key]
210 break
212 if policy_map_id is None:
213 return 3, 'Invalid policy map id'
215 policy_name = policies.get(policy_map_id)
216 policy_id = this_policy_id
217 objects_id = this_objects_id
219 if policy_id is None or objects_id is None:
220 return 3, 'Could not find policy_id or objects_id'
222 post_b = post_bytes.get(policy_id + '.' + objects_id, 0)
223 drop_b = drop_bytes.get(policy_id + '.' + objects_id, 0)
224 speed = saveint(if_speeds[if_id])
226 parent_value_cache = {}
227 for a_key, a_value in config.items():
228 parent_value_cache.update({a_value: a_key.split(".")[1]})
230 # if a_value == class_id:
231 # parent_value = a_key.split(".")[1]
232 for b_key, b_value in parents.items():
233 if parent_value_cache[class_id] == b_value:
234 if object_types[b_key] == "4":
235 try:
236 # 1 kbps
237 # 2 percentage
238 # 3 percentageRemaining
239 # 4 ratioRemaining
240 # 5 perThousand
241 # 6 perMillion
242 qos_unit = int(if_qos_bandwidth_units[config[b_key]])
243 qos_bandwidth = saveint(if_qos_bandwidth[config[b_key]])
244 if qos_unit == 1:
245 speed = qos_bandwidth * 1000
247 elif qos_unit == 2:
248 speed = speed * (qos_bandwidth / 100.0)
250 elif qos_unit == 3:
251 remaining = (speed / 100) * qos_bandwidth
252 speed = speed - remaining
253 break
254 except KeyError:
255 pass
257 # Bandwidth needs to be in bytes for later calculations
258 bw = speed / 8.0
260 # Determine post warn/crit levels
261 if isinstance(post_warn, float):
262 post_warn = bw / 100.0 * post_warn
263 post_crit = bw / 100.0 * post_crit
264 elif isinstance(post_warn, int):
265 if unit == 'bit':
266 post_warn = post_warn / 8
267 post_crit = post_crit / 8
269 # Determine drop warn/crit levels
270 if isinstance(drop_warn, float) and not had_legacy_params:
271 drop_warn = bw / 100.0 * drop_warn
272 drop_crit = bw / 100.0 * drop_crit
273 # Convert the drop levels to byte
274 elif unit == "bit":
275 # But only if our params where already provided in the new format
276 if not had_legacy_params:
277 if isinstance(drop_warn, int):
278 drop_warn = drop_warn / 8.0
279 if isinstance(drop_crit, int):
280 drop_crit = drop_crit / 8.0
282 # Handle counter values
283 state = 0
284 infotext = ''
285 this_time = time.time()
286 rates = []
287 perfdata = []
288 perfdata_avg = []
290 min_value = "0.0" if unit == "bit" else "0"
291 for name, counter, warn, crit, min_val, max_val in [
292 ("post", post_b, post_warn, post_crit, min_value, bw),
293 ("drop", drop_b, drop_warn, drop_crit, min_value, bw),
295 rate = get_rate("cisco_qos.%s.%s" % (name, item), this_time, saveint(counter))
296 rates.append(rate)
297 perfdata.append((name, rate, warn, crit, min_val, max_val))
299 if average:
300 avg_value = get_average("cisco_qos.%s.%s.avg" % (name, item), this_time, rate, average)
301 rates.append(avg_value)
302 perfdata_avg.append(("%s_avg_%d" % (name, average), avg_value, warn, crit, min_val,
303 max_val))
305 perfdata.extend(perfdata_avg)
307 def format_value(value):
308 if unit == "bit":
309 value = value * 8
310 return get_nic_speed_human_readable(value)
311 return "%s/s" % get_bytes_human_readable(value)
313 if average:
314 post_rate = rates[1]
315 drop_rate = rates[3]
316 else:
317 post_rate = rates[0]
318 drop_rate = rates[1]
320 for what, rate, warn, crit in [("post", post_rate, post_warn, post_crit),
321 ("drop", drop_rate, drop_warn, drop_crit)]:
322 infotext += ', %s: %s' % (what, format_value(rate))
323 if crit is not None and rate >= crit:
324 state = max(2, state)
325 infotext += '(!!)'
326 elif warn is not None and rate >= warn:
327 state = max(1, state)
328 infotext += '(!)'
330 if policy_name:
331 infotext += ', Policy-Name: %s, Int-Bandwidth: %s' % (policy_name, format_value(bw))
332 else:
333 infotext += ', Policy-Map-ID: %s, Int-Bandwidth: %s' % (policy_map_id, format_value(bw))
334 return (state, infotext.lstrip(', '), perfdata)
337 check_info["cisco_qos"] = {
338 "service_description": "QoS %s",
339 "check_function": check_cisco_qos,
340 "inventory_function": inventory_cisco_qos,
341 "has_perfdata": True,
342 "group": "cisco_qos",
343 "default_levels_variable": "cisco_qos_default_"
346 snmp_info['cisco_qos'] = [
347 ('.1.3.6.1.4.1.9.9.166.1', [OID_END, '1.1.1.4']), # qosIfIndex
348 ('.1.3.6.1.4.1.9.9.166.1', [OID_END, '6.1.1.1']), # qosPolicies
349 ('.1.3.6.1.4.1.9.9.166.1', [OID_END, '7.1.1.1']), # qosClasses
350 ('.1.3.6.1.4.1.9.9.166.1', [OID_STRING, '5.1.1.2']), # qosConfig
351 ('.1.3.6.1.4.1.9.9.166.1', [OID_STRING, '15.1.1.9']), # qosPostBytes
352 ('.1.3.6.1.4.1.9.9.166.1', [OID_STRING, '15.1.1.16']), # qosDropBytes
353 ('.1.3.6.1.2.1.2.2.1', [OID_END, '2']), # ifNames
354 ('.1.3.6.1.2.1.2.2.1', [OID_END, '5']), # ifSpeeds
355 ('.1.3.6.1.4.1.9.9.166.1', [OID_END, '5.1.1.4']), # cbQosParentObjectsIndex
356 ('.1.3.6.1.4.1.9.9.166.1', [OID_END, '9.1.1.1']), # qosQueueingConfigBandwidth
357 ('.1.3.6.1.4.1.9.9.166.1', [OID_END, '9.1.1.2']), # cbQosQueueingCfgBandwidthUnits
358 ('.1.3.6.1.4.1.9.9.166.1', [OID_END, '5.1.1.3']), # cbQosObjectsType
360 snmp_scan_functions['cisco_qos'] = lambda oid: "cisco" in oid(".1.3.6.1.2.1.1.1.0").lower() and \
361 oid(".1.3.6.1.4.1.9.9.166.1.1.1.1.4.*")