1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """
17 This module provides a collector daemon that polls Windows devices for changes
18 to the Windows Event Log. Retrieved events are then converted into Zenoss events
19 and sent back to ZenHub for further processing.
20 """
21
22 import logging
23
24
25
26
27
28 import pysamba.twisted.reactor
29
30 import Globals
31 import zope.component
32 import zope.interface
33
34 from twisted.internet import defer, reactor
35 from twisted.python.failure import Failure
36
37 from Products.ZenCollector.daemon import CollectorDaemon
38 from Products.ZenCollector.interfaces import ICollectorPreferences,\
39 IEventService,\
40 IScheduledTask,\
41 IStatisticsService
42 from Products.ZenCollector.tasks import SimpleTaskFactory,\
43 SimpleTaskSplitter,\
44 TaskStates
45 from Products.ZenEvents.ZenEventClasses import Clear, Error, Warning, Info, \
46 Debug, Status_Wmi
47 from Products.ZenUtils.observable import ObservableMixin
48 from Products.ZenWin.Watcher import Watcher
49 from Products.ZenWin.utils import addNTLMv2Option, setNTLMv2Auth
50
51
52
53
54 from Products.ZenUtils.Utils import unused
55 from Products.ZenCollector.services.config import DeviceProxy
56 unused(DeviceProxy)
57
58
59
60
61 log = logging.getLogger("zen.zeneventlog")
62
63
64
65
67 zope.interface.implements(ICollectorPreferences)
68
70 """
71 Constructs a new ZenEventLogPreferences instance and provide default
72 values for needed attributes.
73 """
74 self.collectorName = "zeneventlog"
75 self.defaultRRDCreateCommand = None
76 self.cycleInterval = 5 * 60
77 self.configCycleInterval = 20
78 self.options = None
79
80
81
82 self.configurationService = 'Products.ZenWin.services.EventLogConfig'
83
84 self.wmibatchSize = 10
85 self.wmiqueryTimeout = 1000
86
88 parser.add_option('--batchSize', dest='batchSize',
89 default=None, type='int',
90 help='Number of data objects to retrieve in a ' +
91 'single WMI query.')
92
93 parser.add_option('--queryTimeout', dest='queryTimeout',
94 default=None, type='int',
95 help='The number of milliseconds to wait for ' + \
96 'WMI query to respond. Overrides the ' + \
97 'server settings.')
98
99 parser.add_option('--testClause', dest='testClause',
100 default=None,
101 help="Override the device's zWinEventlogClause" \
102 " with this string.")
103 addNTLMv2Option(parser)
104
105 - def postStartup(self):
106
107 logseverity = self.options.logseverity
108 if logseverity <= 5:
109 pysamba.library.DEBUGLEVEL.value = 99
110
111
112 setNTLMv2Auth(self.options)
113
114
115 statService = zope.component.queryUtility(IStatisticsService)
116 statService.addStatistic("events", "COUNTER")
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
133 """
134 A scheduled task that watches the event log on a single Windows device.
135 """
136 zope.interface.implements(IScheduledTask)
137
138 EVENT_LOG_NOTIFICATION_QUERY = """
139 SELECT * FROM __InstanceCreationEvent
140 WHERE TargetInstance ISA 'Win32_NTLogEvent'
141 AND TargetInstance.EventType <= %d
142 """
143
144 STATE_CONNECTING = 'CONNECTING'
145 STATE_POLLING = 'POLLING'
146 STATE_PROCESSING = 'PROCESSING'
147
148 - def __init__(self,
149 deviceId,
150 taskName,
151 scheduleIntervalSeconds,
152 taskConfig):
153 """
154 Construct a new task instance to watch for Windows Event Log changes
155 for the specified device.
156
157 @param deviceId: the Zenoss deviceId to watch
158 @type deviceId: string
159 @param taskName: the unique identifier for this task
160 @type taskName: string
161 @param scheduleIntervalSeconds: the interval at which this task will be
162 collected
163 @type scheduleIntervalSeconds: int
164 @param taskConfig: the configuration for this task
165 """
166 super(ZenEventLogTask, self).__init__()
167
168 self.name = taskName
169 self.configId = deviceId
170 self.interval = scheduleIntervalSeconds
171 self.state = TaskStates.STATE_IDLE
172
173 self._taskConfig = taskConfig
174 self._devId = deviceId
175 self._manageIp = self._taskConfig.manageIp
176
177 self._eventService = zope.component.queryUtility(IEventService)
178 self._statService = zope.component.queryUtility(IStatisticsService)
179 self._preferences = zope.component.queryUtility(ICollectorPreferences,
180 "zeneventlog")
181
182
183
184
185
186 self._batchSize = self._preferences.options.batchSize
187 if not self._batchSize:
188 self._batchSize = self._preferences.wmibatchSize
189 self._queryTimeout = self._preferences.options.queryTimeout
190 if not self._queryTimeout:
191 self._queryTimeout = self._preferences.wmiqueryTimeout
192
193
194
195
196
197 self._wmiQuery = ZenEventLogTask.EVENT_LOG_NOTIFICATION_QUERY % \
198 int(self._taskConfig.zWinEventlogMinSeverity)
199 testClause = self._preferences.options.testClause
200 andClause = self._taskConfig.zWinEventlogClause
201 if testClause is not None:
202 if testClause.strip():
203 self._wmiQuery += " AND " + testClause
204 elif andClause and andClause.strip():
205 self._wmiQuery += " AND " + andClause
206
207 self._watcher = None
208 self._reset()
209
211 """
212 Reset the WMI notification query watcher connection to the device, if
213 one is presently active.
214 """
215 if self._watcher:
216 self._watcher.close()
217 self._watcher = None
218
220 """
221 Put event in the queue to be sent to the ZenEventManager.
222
223 @param lrec: log record
224 @type lrec: log record object
225 @return: dictionary with event keys and values
226 @rtype: dictionary
227 """
228 lrec = lrec.targetinstance
229 evtkey = '%s_%s' % (lrec.sourcename, lrec.eventcode)
230 sev = Debug
231 if lrec.eventtype == 1:
232 sev = Error
233 elif lrec.eventtype == 2:
234 sev = Warning
235 elif lrec.eventtype in (3, 4, 5):
236 sev = Info
237
238 log.debug( "---- log record info --------------" )
239 for item in dir(lrec):
240 if item[0] == '_':
241 continue
242 log.debug("%s = %s" % (item, getattr(lrec, item, '')))
243 log.debug( "---- log record info --------------" )
244
245 ts= lrec.timegenerated
246 try:
247 date_ts = '/'.join( [ ts[0:4], ts[4:6], ts[6:8] ])
248 time_ts = ':'.join( [ts[8:10], ts[10:12], ts[12:14] ])
249 ts = date_ts + ' ' + time_ts
250 except:
251 pass
252
253 event_message = str(lrec.message).strip()
254 if not event_message or event_message == 'None':
255 event_message = "Message text from Windows not available." + \
256 " See source system's event log."
257
258 evt = dict(
259 device=self._devId,
260 eventClassKey=evtkey,
261 eventGroup=lrec.logfile,
262 component=lrec.sourcename,
263 ntevid=lrec.eventcode,
264 summary=event_message,
265 agent='zeneventlog',
266 severity=sev,
267 monitor=self._preferences.options.monitor,
268 user=lrec.user,
269 categorystring=lrec.categorystring,
270 originaltime=ts,
271 computername=lrec.computername,
272 eventidentifier=lrec.eventidentifier,
273 )
274 log.debug("Device:%s msg:'%s'", self._devId, lrec.message)
275 return evt
276
278 """
279 Callback activated when the task is complete so that final statistics
280 on the collection can be displayed.
281 """
282 if not isinstance(result, Failure):
283 log.debug("Device %s [%s] scanned successfully, %d events processed",
284 self._devId, self._manageIp, self._eventsFetched)
285 stat = self._statService.getStatistic("events")
286 stat.value += self._eventsFetched
287 else:
288 log.debug("Device %s [%s] scanned failed, %s",
289 self._devId, self._manageIp, result.getErrorMessage())
290
291
292
293 return result
294
296 """
297 Errback for an unsuccessful asynchronous connection or collection
298 request.
299 """
300 err = result.getErrorMessage()
301 log.error("Unable to scan device %s: %s", self._devId, err)
302
303 self._reset()
304
305 if 'WBEM_E_UNPARSABLE_QUERY' in err:
306 summary = """
307 There was an error found while processing the zWinEventlogClause
308 query: %s
309 """ % err
310 else:
311 summary = """
312 Could not read the Windows event log (%s). Check your
313 username/password settings and verify network connectivity.
314 """ % err
315
316 self._eventService.sendEvent(dict(
317 summary=summary,
318 component='zeneventlog',
319 eventClass=Status_Wmi,
320 device=self._devId,
321 severity=Error,
322 agent='zeneventlog',
323 ))
324
325
326 return result
327
329 """
330 Callback for a successful fetch of events from the remote device.
331 """
332 self.state = ZenEventLogTask.STATE_PROCESSING
333
334 log.debug("Successful collection from %s [%s], result=%s",
335 self._devId, self._manageIp, result)
336
337 events = result
338 if events:
339
340 for logRecord in events:
341 self._eventsFetched += 1
342
343 self._eventService.sendEvent(self._makeEvent(logRecord))
344
345
346
347
348
349 log.debug("Queuing another fetch for %s [%s]",
350 self._devId, self._manageIp)
351 d = defer.Deferred()
352 reactor.callLater(0, d.callback, None)
353 d.addCallback(self._collectCallback)
354 return d
355
365
367 """
368 Callback called after a connect or previous collection so that another
369 collection can take place.
370 """
371 log.debug("Polling for events from %s [%s]",
372 self._devId, self._manageIp)
373
374 self.state = ZenEventLogTask.STATE_POLLING
375 d = self._watcher.getEvents(self._queryTimeout, self._batchSize)
376 d.addCallbacks(self._collectSuccessful, self._failure)
377 d.addCallbacks(self._deviceUp)
378 return d
379
381 """
382 Callback called after a successful connect to the remote Windows device.
383 """
384 log.debug("Connected to %s [%s]", self._devId, self._manageIp)
385
387 """
388 Called when a connection needs to be created to the remote Windows
389 device.
390 """
391 log.debug("Connecting to %s [%s]", self._devId, self._manageIp)
392
393 self.state = ZenEventLogTask.STATE_CONNECTING
394 self._watcher = Watcher(self._taskConfig, self._wmiQuery)
395 return self._watcher.connect()
396
399
401 log.debug("Scanning device %s [%s]", self._devId, self._manageIp)
402
403 self._eventsFetched = 0
404
405
406 if not self._watcher:
407 d = self._connect()
408 d.addCallbacks(self._connectCallback, self._failure)
409 else:
410
411
412
413 d = defer.Deferred()
414 reactor.callLater(0, d.callback, None)
415
416
417
418 d.addCallback(self._collectCallback)
419
420
421
422
423 d.addBoth(self._finished)
424
425
426
427 return d
428
429
430
431
432 if __name__ == '__main__':
433 myPreferences = ZenEventLogPreferences()
434
435 myTaskFactory = SimpleTaskFactory(ZenEventLogTask)
436 myTaskSplitter = SimpleTaskSplitter(myTaskFactory)
437 daemon = CollectorDaemon(myPreferences, myTaskSplitter)
438 daemon.run()
439