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 ) 290 }, 291 ) 292 293 security = ClassSecurityInfo() 294
295 - def __init__(self, id):
296 ZenModelRM.__init__(self, id) 297 self.eventClassKey = id 298 self.sequence = None 299 self.rule = "" 300 self.regex = "" 301 self.example = "" 302 self.explanation = "" 303 self.resolution = ""
304 305
306 - def getStatus(self, **kwargs):
307 """Return the status number for this device of class statClass. 308 """ 309 return EventView.getStatus(self, self.getEventClass())
310
311 - def getEventClass(self):
312 """Return the full EventClass of this EventClassInst.""" 313 return self.getOrganizerName()
314
315 - def getEventClassHref(self):
316 """Return href of our class. 317 """ 318 return self.eventClass().getPrimaryUrlPath()
319 320
321 - def getDmdKey(self):
322 """Return the dmd key of this mapping ie: /App/Start/zentinel 323 """ 324 return self.getOrganizerName() + "/" + self.id
325 326
327 - def applyExtraction(self, evt):
328 """ 329 Apply the event dict regex to extract additional values from the event. 330 """ 331 if self.regex: 332 m = re.search(self.regex, evt.message) 333 if m: evt.updateFromDict(m.groupdict()) 334 return evt
335 336
337 - def applyValues(self, evt):
338 """Modify event with values taken from dict Inst. 339 Any non-None property values are applied to the event. 340 """ 341 evt.eventClass = self.getEventClass() 342 evt.eventClassMapping = '%s/%s' % (self.getEventClass(), self.id) 343 return EventClassPropertyMixin.applyValues(self, evt)
344
345 - def ruleOrRegex(self, limit=None):
346 """Return the rule if it exists else return the regex. 347 limit limits the number of characters returned. 348 """ 349 value = self.rule and self.rule or self.regex 350 if not value and self.example: 351 value = self.example 352 if limit: value = value[:limit] 353 return value
354 355
356 - def match(self, evt, device):
357 """ 358 Match an event message against our regex. 359 360 @parameter evt: event to match in our mapping 361 @type evt: dictionary 362 @parameter device: device 363 @type device: DMD object 364 @return: boolean 365 @rtype: boolean 366 """ 367 value = False 368 log.debug("match on:%s", self.getPrimaryDmdId()) 369 if self.rule: 370 try: 371 log.debug("eval rule:%s", self.rule) 372 value = eval(self.rule, {'evt':evt, 'dev':device, 'device': device}) 373 except Exception, e: 374 logging.warn("EventClassInst: %s rule failure: %s", 375 self.getDmdKey(), e) 376 else: 377 try: 378 log.debug("regex='%s' message='%s'", self.regex, evt.message) 379 value = re.search(self.regex, evt.message, re.I) 380 except sre_constants.error: pass 381 return value
382 383
384 - def testRegexStyle(self):
385 """Test our regex using the example event string. 386 """ 387 if self.example: 388 try: 389 value = re.search(self.regex, self.example, re.I) 390 if not value: return "color:#FF0000;" 391 except sre_constants.error: 392 return "color:#FF0000;"
393 394
395 - def testRuleStyle(self):
396 """Test our rule by compiling it. 397 """ 398 try: 399 if self.rule: 400 compile(self.rule, "<string>", "eval") 401 except: 402 return "color:#FF0000;"
403 404
405 - def sameKey(self):
406 """Return a list of all mappings with the same eventClassKey. 407 """ 408 return [ i for i in self.eventClass().find(self.eventClassKey) \ 409 if i.eventClassKey == self.eventClassKey ]
410 411 412 security.declareProtected('Manage DMD', 'manage_resequence')
413 - def manage_resequence(self, seqmap, REQUEST=None):
414 """Reorder the sequence of eventClassMappings with the same key. 415 """ 416 # first pass set new sequence 417 for i, map in enumerate(self.sameKey()): 418 map.sequence = int(seqmap[i]) 419 # second pass take out any holes 420 for i, map in enumerate(self.sameKey()): 421 map.sequence = i 422 if REQUEST: 423 return self.callZenScreen(REQUEST)
424 425 426 security.declareProtected('Manage DMD', 'manage_editEventClassInst')
427 - def manage_editEventClassInst(self, name="", eventClassKey='', 428 regex='', rule='', example='', 429 transform='', 430 explanation='', resolution='', REQUEST=None):
431 """Edit a EventClassInst from a web page. 432 """ 433 redirect = self.rename(name) 434 if eventClassKey and self.eventClassKey != eventClassKey: 435 self.unindex_object() 436 self.sequence = self.eventClass().nextSequenceNumber(eventClassKey) 437 self.eventClassKey = eventClassKey 438 self.index_object() 439 self.regex = regex 440 self.rule = rule 441 self.example = example 442 self.transform = transform 443 self.explanation = explanation 444 self.resolution = resolution 445 if REQUEST: 446 from Products.ZenUtils.Time import SaveMessage 447 messaging.IMessageSender(self).sendToBrowser( 448 'Saved', SaveMessage()) 449 return self.callZenScreen(REQUEST, redirect)
450 451 452 InitializeClass(EventClassInst) 453