Package Products :: Package ZenModel :: Module MinMaxThreshold
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenModel.MinMaxThreshold

  1  ############################################################################## 
  2  #  
  3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved. 
  4  #  
  5  # This content is made available according to terms specified in 
  6  # License.zenoss under the directory where your Zenoss product is installed. 
  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  # Note:  this import is for backwards compatibility. 
 35  # Import Products.ZenRRD.utils.rpneval directy. 
 36  from Products.ZenRRD.utils import rpneval 
 37   
 38  NaN = float('nan') 
 39   
40 -class MinMaxThreshold(ThresholdClass):
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
71 - def createThresholdInstance(self, context):
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
85 - def getMinval(self, context):
86 """Build the min value for this threshold. 87 """ 88 minval = None 89 if self.minval: 90 try: 91 express = "python:%s" % self.minval 92 minval = talesEval(express, context) 93 except: 94 msg= "User-supplied Python expression (%s) for minimum value caused error: %s" % \ 95 ( self.minval, self.dsnames ) 96 log.error( msg ) 97 raise pythonThresholdException(msg) 98 minval = None 99 return nanToNone(minval)
100 101
102 - def getMaxval(self, context):
103 """Build the max value for this threshold. 104 """ 105 maxval = None 106 if self.maxval: 107 try: 108 express = "python:%s" % self.maxval 109 maxval = talesEval(express, context) 110 except: 111 msg= "User-supplied Python expression (%s) for maximum value caused error: %s" % \ 112 ( self.maxval, self.dsnames ) 113 log.error( msg ) 114 raise pythonThresholdException(msg) 115 maxval = None 116 return nanToNone(maxval)
117 118 InitializeClass(MinMaxThreshold) 119 MinMaxThresholdClass = MinMaxThreshold 120 121 122
123 -class MinMaxThresholdInstance(RRDThresholdInstance):
124 # Not strictly necessary, but helps when restoring instances from 125 # pickle files that were not constructed with a count member. 126 count = {} 127
128 - def __init__(self, id, context, dpNames, 129 minval, maxval, eventClass, severity, escalateCount):
130 RRDThresholdInstance.__init__(self, id, context, dpNames, eventClass, severity) 131 self.count = {} 132 self.minimum = minval 133 self.maximum = maxval 134 self.escalateCount = escalateCount
135
136 - def countKey(self, dp):
137 return ':'.join(self.context().key()) + ':' + dp
138
139 - def getCount(self, dp):
140 countKey = self.countKey(dp) 141 if not countKey in self.count: 142 return None 143 return self.count[countKey]
144
145 - def incrementCount(self, dp):
146 countKey = self.countKey(dp) 147 if not countKey in self.count: 148 self.resetCount(dp) 149 self.count[countKey] += 1 150 return self.count[countKey]
151
152 - def resetCount(self, dp):
153 self.count[self.countKey(dp)] = 0
154
155 - def checkRange(self, dp, value):
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 # Handle all cases where both minimum and maximum are set. 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 # Handle simple cases where only minimum or maximum is set. 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
203 - def _create_event_dict(self, current, summary, severity, how=None):
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
224 - def processEvent(self, evt):
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
235 - def processClearEvent(self, evt):
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
246 - def raiseRPNExc( self ):
247 """ 248 Raise an RPN exception, taking care to log all details. 249 """ 250 msg= "The following RPN exception is from user-supplied code." 251 log.exception( msg ) 252 raise rpnThresholdException(msg)
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 # Attempt any RPN expressions 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
328 - def getNames(self, relatedGps):
329 names = sorted(set(x.split('_', 1)[1] for x in self.dataPointNames)) 330 return ', '.join(names)
331
332 - def setPower(self, number):
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
341 - def _checkImpl(self, dataPoint, value):
342 return self.checkRange(dataPoint, value)
343 344 from twisted.spread import pb 345 pb.setUnjellyableForClass(MinMaxThresholdInstance, MinMaxThresholdInstance) 346