1
2
3
4
5
6
7
8
9
10
11 from Products.ZenModel.ThresholdInstance import RRDThresholdInstance
12
13 __doc__= """MinMaxThreshold
14 Make threshold comparisons dynamic by using TALES expresssions,
15 rather than just number bounds checking.
16 """
17
18 from AccessControl import Permissions
19
20 from Globals import InitializeClass
21 from ThresholdClass import ThresholdClass
22 from ThresholdInstance import ThresholdContext
23 from Products.ZenEvents import Event
24 from Products.ZenEvents.ZenEventClasses import Perf_Snmp
25 from Products.ZenUtils.ZenTales import talesEval, talesEvalStr
26 from Products.ZenEvents.Exceptions import pythonThresholdException, \
27 rpnThresholdException
28
29 import logging
30 log = logging.getLogger('zen.MinMaxCheck')
31
32 from Products.ZenUtils.Utils import unused, nanToNone
33
34
35
36 from Products.ZenRRD.utils import rpneval
37
38 NaN = float('nan')
39
41 """
42 Threshold class that can evaluate RPNs and Python expressions
43 """
44
45 minval = ""
46 maxval = ""
47 eventClass = Perf_Snmp
48 severity = 3
49 escalateCount = 0
50
51 _properties = ThresholdClass._properties + (
52 {'id':'minval', 'type':'string', 'mode':'w'},
53 {'id':'maxval', 'type':'string', 'mode':'w'},
54 {'id':'escalateCount', 'type':'int', 'mode':'w'}
55 )
56
57 factory_type_information = (
58 {
59 'immediate_view' : 'editRRDThreshold',
60 'actions' :
61 (
62 { 'id' : 'edit'
63 , 'name' : 'Min/Max Threshold'
64 , 'action' : 'editRRDThreshold'
65 , 'permissions' : ( Permissions.view, )
66 },
67 )
68 },
69 )
70
72 """Return the config used by the collector to process min/max
73 thresholds. (id, minval, maxval, severity, escalateCount)
74 """
75 mmt = MinMaxThresholdInstance(self.id,
76 ThresholdContext(context),
77 self.dsnames,
78 minval=self.getMinval(context),
79 maxval=self.getMaxval(context),
80 eventClass=self.eventClass,
81 severity=self.severity,
82 escalateCount=self.escalateCount)
83 return mmt
84
100
101
117
118 InitializeClass(MinMaxThreshold)
119 MinMaxThresholdClass = MinMaxThreshold
120
121
122
124
125
126 count = {}
127
128 - def __init__(self, id, context, dpNames,
129 minval, maxval, eventClass, severity, escalateCount):
135
137 return ':'.join(self.context().key()) + ':' + dp
138
144
151
154
156 'Check the value for min/max thresholds'
157 log.debug("Checking %s %s against min %s and max %s",
158 dp, value, self.minimum, self.maximum)
159 if value is None:
160 return []
161 if isinstance(value, basestring):
162 value = float(value)
163 thresh = None
164
165
166 if self.maximum is not None and self.minimum is not None:
167 if self.maximum >= self.minimum:
168 if value > self.maximum:
169 thresh = self.maximum
170 how = 'exceeded'
171 elif value < self.minimum:
172 thresh = self.minimum
173 how = 'not met'
174 elif self.maximum < value < self.minimum:
175 thresh = self.maximum
176 how = 'violated'
177
178
179 elif self.maximum is not None and value > self.maximum:
180 thresh = self.maximum
181 how = 'exceeded'
182 elif self.minimum is not None and value < self.minimum:
183 thresh = self.minimum
184 how = 'not met'
185
186 if thresh is not None:
187 severity = self.severity
188 count = self.incrementCount(dp)
189 if self.escalateCount and count >= self.escalateCount:
190 severity = min(severity + 1, 5)
191 summary = 'threshold of %s %s: current value %f' % (
192 self.name(), how, float(value))
193 evtdict = self._create_event_dict(value, summary, severity, how)
194 if self.escalateCount:
195 evtdict['escalation_count'] = count
196 return self.processEvent(evtdict)
197 else:
198 summary = 'threshold of %s restored: current value %f' % (
199 self.name(), value)
200 self.resetCount(dp)
201 return self.processClearEvent(self._create_event_dict(value, summary, Event.Clear))
202
204 event_dict = dict(device=self.context().deviceName,
205 summary=summary,
206 eventKey=self.id,
207 eventClass=self.eventClass,
208 component=self.context().componentName,
209 min=self.minimum,
210 max=self.maximum,
211 current=current,
212 severity=severity)
213 deviceUrl = getattr(self.context(), "deviceUrl", None)
214 if deviceUrl is not None:
215 event_dict["zenoss.device.url"] = deviceUrl
216 devicePath = getattr(self.context(), "devicePath", None)
217 if devicePath is not None:
218 event_dict["zenoss.device.path"] = devicePath
219 if how is not None:
220 event_dict['how'] = how
221 return event_dict
222
223
225 """
226 When a threshold condition is violated, pre-process it for (possibly) nicer
227 formatting or more complicated logic.
228
229 @paramater evt: event
230 @type evt: dictionary
231 @rtype: list of dictionaries
232 """
233 return [evt]
234
236 """
237 When a threshold condition is restored, pre-process it for (possibly) nicer
238 formatting or more complicated logic.
239
240 @paramater evt: event
241 @type evt: dictionary
242 @rtype: list of dictionaries
243 """
244 return [evt]
245
253
254
255 - def getGraphElements(self, template, context, gopts, namespace, color,
256 legend, relatedGps):
257 """Produce a visual indication on the graph of where the
258 threshold applies."""
259 unused(template, namespace)
260 if not color.startswith('#'):
261 color = '#%s' % color
262 minval = self.minimum
263 if minval is None:
264 minval = NaN
265 maxval = self.maximum
266 if maxval is None:
267 maxval = NaN
268 if not self.dataPointNames:
269 return gopts
270 gp = relatedGps[self.dataPointNames[0]]
271
272
273 rpn = getattr(gp, 'rpn', None)
274 if rpn:
275 try:
276 rpn = talesEvalStr(rpn, context)
277 except:
278 self.raiseRPNExc()
279 return gopts
280
281 try:
282 minval = rpneval(minval, rpn)
283 except:
284 minval= 0
285 self.raiseRPNExc()
286
287 try:
288 maxval = rpneval(maxval, rpn)
289 except:
290 maxval= 0
291 self.raiseRPNExc()
292
293 minstr = self.setPower(minval)
294 maxstr = self.setPower(maxval)
295
296 minval = nanToNone(minval)
297 maxval = nanToNone(maxval)
298 if legend:
299 gopts.append(
300 "HRULE:%s%s:%s\\j" % (minval or maxval, color, legend))
301 elif minval is not None and maxval is not None:
302 if minval == maxval:
303 gopts.append(
304 "HRULE:%s%s:%s not equal to %s\\j" % (minval, color,
305 self.getNames(relatedGps), minstr))
306 elif minval < maxval:
307 gopts.append(
308 "HRULE:%s%s:%s not within %s and %s\\j" % (minval, color,
309 self.getNames(relatedGps), minstr, maxstr))
310 gopts.append("HRULE:%s%s" % (maxval, color))
311 elif minval > maxval:
312 gopts.append(
313 "HRULE:%s%s:%s between %s and %s\\j" % (minval, color,
314 self.getNames(relatedGps), maxstr, minstr))
315 gopts.append("HRULE:%s%s" % (maxval, color))
316 elif minval is not None :
317 gopts.append(
318 "HRULE:%s%s:%s less than %s\\j" % (minval, color,
319 self.getNames(relatedGps), minstr))
320 elif maxval is not None:
321 gopts.append(
322 "HRULE:%s%s:%s greater than %s\\j" % (maxval, color,
323 self.getNames(relatedGps), maxstr))
324
325 return gopts
326
327
329 names = sorted(set(x.split('_', 1)[1] for x in self.dataPointNames))
330 return ', '.join(names)
331
333 powers = ("k", "M", "G")
334 if number < 1000: return number
335 for power in powers:
336 number = number / 1000.0
337 if number < 1000:
338 return "%0.2f%s" % (number, power)
339 return "%.2f%s" % (number, powers[-1])
340
343
344 from twisted.spread import pb
345 pb.setUnjellyableForClass(MinMaxThresholdInstance, MinMaxThresholdInstance)
346