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