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