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

Source Code for Module Products.ZenEvents.EventClassInst

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