Package Products :: Package ZenEvents :: Module EventClassInst
[hide private]
[frames] | no frames]

Source Code for Module Products.ZenEvents.EventClassInst

  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 ZODB.transact import transact 
 12   
 13  import copy 
 14  import re 
 15  import sre_constants 
 16  import logging 
 17  import transaction 
 18  import urllib 
 19  from Products.ZenMessaging.audit import audit 
 20   
 21  log = logging.getLogger("zen.Events") 
 22   
 23  from Globals import InitializeClass 
 24  from AccessControl import ClassSecurityInfo 
 25  from AccessControl import Permissions 
 26  from Acquisition import aq_chain 
 27  from zope.interface import implements 
 28   
 29  from Products.ZenModel.interfaces import IIndexed 
 30  from Products.ZenModel.ZenossSecurity import * 
 31  from Products.ZenRelations.RelSchema import * 
 32  from Products.ZenModel.ZenModelRM import ZenModelRM 
 33  from Products.ZenModel.EventView import EventView 
 34  from Products.ZenModel.ZenPackable import ZenPackable 
 35  from Products.ZenWidgets import messaging 
 36  from Products.ZenUtils.guid.interfaces import IGloballyIdentifiable 
 37  from Products.ZenUtils.Utils import convToUnits, zdecode, getDisplayName 
 38  from Products.ZenUtils.Time import SaveMessage 
 39  from Products import Zuul 
 40  from Products.Zuul.interfaces import IInfo 
 41   
 42   
43 -def manage_addEventClassInst(context, id, REQUEST = None):
44 """make a device class""" 45 dc = EventClassInst(id) 46 context._setObject(id, dc) 47 if REQUEST is not None: 48 REQUEST['RESPONSE'].redirect(context.absolute_url() + '/manage_main')
49 50
51 -class EventClassPropertyMixin(object):
52 53 transform = '' 54 55 _properties = ( 56 {'id':'transform', 'type':'text', 'mode':'w'}, 57 ) 58
59 - def applyValues(self, evt):
60 """Modify event with values taken from dict Inst. 61 Any non-None property values are applied to the event. 62 """ 63 evt._clearClasses = copy.copy(getattr(self, "zEventClearClasses", [])) 64 evt._action = getattr(self, "zEventAction", "status") 65 sev = getattr(self, "zEventSeverity", -1) 66 if sev >= 0: 67 if evt.severity > 0: 68 evt.severity = sev 69 log.debug("Per transform/mapping, using severity %s, action '%s' and clear classes %s", 70 evt.severity, evt._action, evt._clearClasses) 71 updates = {} 72 for name in 'resolution', 'explanation': 73 value = getattr(self, name, None) 74 if value is not None and value != '': 75 updates[name] = value 76 if updates: 77 log.debug("Adding fields from transform/mapping: %s", updates) 78 evt.updateFromDict(updates) 79 return evt
80
81 - def formatTransform(self, transformLines):
82 """ 83 Convenience function to number the transform info 84 """ 85 return '\n'.join("%3s %s" % enumText 86 for enumText in enumerate(transformLines))
87
88 - def sendTransformException(self, eventclass, evt):
89 """ 90 Try to convert the rather horrible looking traceback that 91 is hard to understand into something actionable by the user. 92 """ 93 transformName = '/%s'% '/'.join(eventclass.getPhysicalPath()[4:]) 94 summary = "Error processing transform/mapping on Event Class %s" % \ 95 transformName 96 97 import sys 98 from traceback import format_exc, extract_tb 99 tb = extract_tb(sys.exc_info()[2]) 100 exceptionText = format_exc(0).splitlines()[1] 101 102 transformLines = eventclass.transform.splitlines() 103 transformFormatted = self.formatTransform(transformLines) 104 105 # try to extract line number and code from traceback, but set up 106 # default values in case this fails - don't want to throw a traceback 107 # when cleaning up a traceback 108 badLineNo = None 109 badLineText = '' 110 try: 111 # if tb has only 1 entry, then the transform didn't compile/run at all 112 if len(tb) > 1: 113 # runtime error, get line number from last entry in traceback 114 # (subtract 1, since traceback numbering is 1-based) 115 badLineNo = tb[-1][1] - 1 116 badLineText = transformLines[badLineNo] 117 else: 118 # assume compile error, with exceptionText in the form: 119 # ' File "<string>", line 4' 120 badLineText = "<transform failed to compile>>" 121 badLineNo = int(exceptionText.rsplit(None,1)[1]) - 1 122 badLineText = transformLines[badLineNo] 123 exceptionText = "compile error on line %d" % badLineNo 124 except Exception: 125 pass 126 127 message = """%s 128 Problem on line %s: %s 129 %s 130 131 Transform: 132 %s 133 """ % (summary, badLineNo, exceptionText, badLineText, 134 transformFormatted) 135 log.warn(message) 136 137 # Now send an event 138 zem = self.getDmd().ZenEventManager 139 badEvt = dict( 140 dedupid='|'.join([transformName,zem.host]), 141 # Don't send the *same* event class or we trash and 142 # and crash endlessly 143 eventClass='/', 144 device=zem.host, 145 component=transformName, 146 summary=summary, 147 severity=4, 148 message = "Problem with line %s: %s" % (badLineNo, badLineText), 149 transform=transformFormatted, 150 exception=exceptionText, 151 # Set False if the root ('/') transform failed; avoids looping 152 # infinitely on creating transform event failures. 153 applyTransforms=False if (transformName == '/') else True, 154 ) 155 zem.sendEvent(badEvt)
156
157 - def applyTransform(self, evt, device, component=None):
158 """ 159 Apply transforms on an event from the top level of the Event Class Tree 160 down to the actual Event Rules (EventClassInst) 161 """ 162 transpath = self._eventClassPath() 163 variables_and_funcs = { 164 'evt':evt, 'device':device, 'dev':device, 165 'convToUnits':convToUnits, 'zdecode':zdecode, 166 'txnCommit':transaction.commit, # this function is deprecated in favor of transforms using @transact 167 'transact':transact, 'dmd':self.dmd, 168 'log':log, 'component':component, 169 'getFacade':Zuul.getFacade, 'IInfo':IInfo, 170 } 171 for eventclass in transpath: 172 if not eventclass.transform: continue 173 try: 174 log.debug('Applying transform/mapping at Event Class %s', 175 eventclass.getPrimaryDmdId()) 176 exec(eventclass.transform, variables_and_funcs) 177 log.debug('Results after transform: %s', 178 variables_and_funcs['evt']) 179 except (Exception, SystemExit): 180 self.sendTransformException(eventclass, evt) 181 182 return variables_and_funcs['evt']
183 184
185 - def inheritedTransforms(self):
186 """ 187 Make a string that brings together all the transforms inherited from the 188 base EventClass to self. 189 """ 190 transpath = self._eventClassPath() 191 transtext = [] 192 for obj in transpath: 193 if not obj.transform: continue 194 if obj.transform == self.transform: break 195 transtext.append("""<a href='%s/editEventClassTransform'>%s<a> 196 """ % (obj.getPrimaryUrlPath(), obj.getPrimaryDmdId())) 197 transtext.append("<pre>%s</pre>" % obj.transform) 198 return "\n".join(transtext)
199 200
201 - def testTransformStyle(self):
202 """Test our transform by compiling it. 203 """ 204 try: 205 if self.transform: 206 compile(self.transform, "<string>", "exec") 207 except: 208 return "color:#FF0000;"
209 210
211 - def _eventClassPath(self):
212 """ 213 Return the path to our current EventClassInst from the top level 214 EventClass down. We use this to process and display the heirarchy of 215 event transforms. 216 """ 217 transpath = [] 218 for obj in aq_chain(self): 219 # skip over relationships in the aq_chain 220 if not isinstance(obj, EventClassPropertyMixin): continue 221 if obj.id == 'dmd': break 222 transpath.append(obj) 223 transpath.reverse() 224 return transpath
225 226 # Why is this a subclass of EventView? 227
228 -class EventClassInst(EventClassPropertyMixin, ZenModelRM, EventView, 229 ZenPackable):
230 """ 231 EventClassInst. 232 """ 233 implements(IIndexed, IGloballyIdentifiable) 234 235 event_key = meta_type = "EventClassInst" 236 237 default_catalog = "eventClassSearch" 238 239 actions = ("status", "history", "heartbeat", "drop") 240 241 _properties = EventClassPropertyMixin._properties + ( 242 {'id':'eventClassKey', 'type':'string', 'mode':'w'}, 243 {'id':'sequence', 'type':'int', 'mode':'w'}, 244 {'id':'rule', 'type':'string', 'mode':'w'}, 245 {'id':'regex', 'type':'string', 'mode':'w'}, 246 {'id':'example', 'type':'string', 'mode':'w'}, 247 {'id':'explanation', 'type':'text', 'mode':'w'}, 248 {'id':'resolution', 'type':'text', 'mode':'w'}, 249 ) 250 251 252 _relations = ZenPackable._relations + ( 253 ("eventClass", ToOne(ToManyCont,"Products.ZenEvents.EventClass","instances")), 254 ) 255 256 257 # Screen action bindings (and tab definitions) 258 factory_type_information = ( 259 { 260 'id' : 'EventClassInst', 261 'meta_type' : 'EventClassInst', 262 'description' : """Base class for all devices""", 263 'icon' : 'EventClassInst.gif', 264 'product' : 'ZenEvents', 265 'factory' : 'manage_addEventClassInst', 266 'immediate_view' : 'eventClassInstStatus', 267 'actions' : 268 ( 269 { 'id' : 'status' 270 , 'name' : 'Status' 271 , 'action' : 'eventClassInstStatus' 272 , 'permissions' : (Permissions.view, ) 273 }, 274 { 'id' : 'edit' 275 , 'name' : 'Edit' 276 , 'action' : 'eventClassInstEdit' 277 , 'permissions' : ("Manage DMD", ) 278 }, 279 { 'id' : 'sequence' 280 , 'name' : 'Sequence' 281 , 'action' : 'eventClassInstSequence' 282 , 'permissions' : (Permissions.view,) 283 }, 284 { 'id' : 'config' 285 , 'name' : 'Configuration Properties' 286 , 'action' : 'zPropertyEditNew' 287 , 'permissions' : ("Manage DMD",) 288 }, 289 { 'id' : 'events' 290 , 'name' : 'Events' 291 , 'action' : 'viewEvents' 292 , 'permissions' : (Permissions.view, ) 293 }, 294 ) 295 }, 296 ) 297 298 security = ClassSecurityInfo() 299
300 - def __init__(self, id):
301 ZenModelRM.__init__(self, id) 302 self.eventClassKey = id 303 self.sequence = None 304 self.rule = "" 305 self.regex = "" 306 self.example = "" 307 self.explanation = "" 308 self.resolution = ""
309 310
311 - def getStatus(self, **kwargs):
312 """Return the status number for this device of class statClass. 313 """ 314 return EventView.getStatus(self, self.getEventClass())
315
316 - def getEventClass(self):
317 """Return the full EventClass of this EventClassInst.""" 318 return self.getOrganizerName()
319
320 - def getEventClassHref(self):
321 """Return href of our class. 322 """ 323 return self.eventClass().getPrimaryUrlPath()
324 325
326 - def getDmdKey(self):
327 """Return the dmd key of this mapping ie: /App/Start/zentinel 328 """ 329 return self.getOrganizerName() + "/" + self.id
330 331
332 - def applyExtraction(self, evt):
333 """ 334 Apply the event dict regex to extract additional values from the event. 335 """ 336 if self.regex: 337 m = re.search(self.regex, evt.message) 338 if m: evt.updateFromDict(m.groupdict()) 339 return evt
340 341
342 - def applyValues(self, evt):
343 """Modify event with values taken from dict Inst. 344 Any non-None property values are applied to the event. 345 """ 346 evt.eventClass = self.getEventClass() 347 evt.eventClassMapping = '%s/%s' % (self.getEventClass(), self.id) 348 return EventClassPropertyMixin.applyValues(self, evt)
349
350 - def ruleOrRegex(self, limit=None):
351 """Return the rule if it exists else return the regex. 352 limit limits the number of characters returned. 353 """ 354 value = self.rule if self.rule else self.regex 355 if not value and self.example: 356 value = self.example 357 if limit: value = value[:limit] 358 return value
359 360
361 - def match(self, evt, device):
362 """ 363 Match an event message against our regex. 364 365 @parameter evt: event to match in our mapping 366 @type evt: dictionary 367 @parameter device: device 368 @type device: DMD object 369 @return: boolean 370 @rtype: boolean 371 """ 372 value = False 373 log.debug("match on:%s", self.getPrimaryDmdId()) 374 if self.rule: 375 try: 376 log.debug("eval rule:%s", self.rule) 377 value = eval(self.rule, {'evt':evt, 'dev':device, 'device': device}) 378 except Exception, e: 379 logging.warn("EventClassInst: %s rule failure: %s", 380 self.getDmdKey(), e) 381 else: 382 try: 383 log.debug("regex='%s' message='%s'", self.regex, evt.message) 384 value = re.search(self.regex, evt.message, re.I) 385 except sre_constants.error: pass 386 return value
387 388
389 - def testRegexStyle(self):
390 """Test our regex using the example event string. 391 """ 392 if self.example: 393 try: 394 value = re.search(self.regex, self.example, re.I) 395 if not value: return "color:#FF0000;" 396 except sre_constants.error: 397 return "color:#FF0000;"
398 399
400 - def testRuleStyle(self):
401 """Test our rule by compiling it. 402 """ 403 try: 404 if self.rule: 405 compile(self.rule, "<string>", "eval") 406 except: 407 return "color:#FF0000;"
408 409
410 - def sameKey(self):
411 """Return a list of all mappings with the same eventClassKey. 412 """ 413 return [ i for i in self.eventClass().find(self.eventClassKey) \ 414 if i.eventClassKey == self.eventClassKey ]
415 416 417 security.declareProtected('Manage DMD', 'manage_resequence')
418 - def manage_resequence(self, seqmap, seqid, REQUEST=None):
419 """ 420 Reorder the sequence of eventClassMappings with the same key. 421 """ 422 for num, path in zip(seqmap, seqid): 423 self.unrestrictedTraverse(urllib.unquote(path)).sequence = int(num) 424 # second pass take out any holes 425 for i, map in enumerate(self.sameKey()): 426 map.sequence = i 427 if REQUEST: 428 audit('UI.EventClassMapping.Resequence', self.id, sequence=seqmap) 429 return self.callZenScreen(REQUEST)
430 431 432 security.declareProtected('Manage DMD', 'manage_editEventClassInst')
433 - def manage_editEventClassInst(self, name='', eventClassKey='', 434 regex='', rule='', example='', 435 transform='', 436 explanation='', resolution='', REQUEST=None):
437 """Edit an EventClassInst from a web page.""" 438 439 #save arguments for audit logging 440 values = locals() 441 442 oldValues = { 443 'name':getDisplayName(self), 444 'eventClassKey':self.eventClassKey, 445 'regex':self.regex, 446 'rule':self.rule, 447 'example':self.example, 448 'transform':self.transform, 449 'explanation':self.explanation, 450 'resolution':self.resolution, 451 } 452 453 redirect = self.rename(name) 454 if eventClassKey and self.eventClassKey != eventClassKey: 455 self.unindex_object() 456 self.sequence = self.eventClass().nextSequenceNumber(eventClassKey) 457 self.eventClassKey = eventClassKey 458 self.index_object() 459 self.regex = regex 460 self.rule = rule 461 self.example = example 462 self.transform = transform 463 self.explanation = explanation 464 self.resolution = resolution 465 if REQUEST: 466 audit('UI.EventClassMapping.Edit', self, data_=values, oldData_=oldValues, skipFields_=['self','REQUEST']) 467 messaging.IMessageSender(self).sendToBrowser( 468 'Saved', SaveMessage()) 469 return self.callZenScreen(REQUEST, redirect)
470 471 472 InitializeClass(EventClassInst) 473