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 # Author: Lars Michelsen <lm@mathias-kettner.de>
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
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
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
75 # get if_name using the if_policy: 9
76 # .1.3.6.1.2.1.31.1.1.1.1.9 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
84 # Test to find the badwidth of the classes. Not finished...
86 # 'cbQosObjectsType' => {
89 # 3 => 'matchStatement',
91 # 5 => 'randomDetect',
92 # 6 => 'trafficShaping',
97 # .1.3.6.1.4.1.9.9.166.1.5.1.1.2.258.1244739 1608
100 # .1.3.6.1.4.1.9.9.166.1.5.1.1.3.258.1244739 4
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
110 # .1.3.6.1.4.1.9.9.166.1.5.1.1.2.258.6184 18785
112 # .1.3.6.1.4.1.9.9.166.1.5.1.1.3.258.6184 2
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
):
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
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(
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
), {})]
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):
155 "post": tuple(int(x
) if x
else None for x
in params
[0:2]),
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
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
183 for cid
, cname
in classes
.iteritems():
184 if class_name
== cname
:
188 # Gather the interface id by class_name
190 for iid2
in ifs
.itervalues():
191 if if_name
== if_names
.get(iid2
):
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(
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
]
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":
238 # 3 percentageRemaining
242 qos_unit
= int(if_qos_bandwidth_units
[config
[b_key
]])
243 qos_bandwidth
= saveint(if_qos_bandwidth
[config
[b_key
]])
245 speed
= qos_bandwidth
* 1000
248 speed
= speed
* (qos_bandwidth
/ 100.0)
251 remaining
= (speed
/ 100) * qos_bandwidth
252 speed
= speed
- remaining
257 # Bandwidth needs to be in bytes for later calculations
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):
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
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
285 this_time
= time
.time()
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
))
297 perfdata
.append((name
, rate
, warn
, crit
, min_val
, max_val
))
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
,
305 perfdata
.extend(perfdata_avg
)
307 def format_value(value
):
310 return get_nic_speed_human_readable(value
)
311 return "%s/s" % get_bytes_human_readable(value
)
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
)
326 elif warn
is not None and rate
>= warn
:
327 state
= max(1, state
)
331 infotext
+= ', Policy-Name: %s, Int-Bandwidth: %s' % (policy_name
, format_value(bw
))
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.*")