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