| Trees | Indices | Help |
|
|---|
|
|
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
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
52
53 transform = ''
54
55 _properties = (
56 {'id':'transform', 'type':'text', 'mode':'w'},
57 )
58
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
82 """
83 Convenience function to number the transform info
84 """
85 return '\n'.join("%3s %s" % enumText
86 for enumText in enumerate(transformLines))
87
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
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
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
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
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
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
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
312 """Return the status number for this device of class statClass.
313 """
314 return EventView.getStatus(self, self.getEventClass())
315
319
324
325
327 """Return the dmd key of this mapping ie: /App/Start/zentinel
328 """
329 return self.getOrganizerName() + "/" + self.id
330
331
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
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
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
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
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
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
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')
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1.1812 on Mon Jul 30 17:11:26 2012 | http://epydoc.sourceforge.net |