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 Windows services. Retrieved status is 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 from Products.ZenCollector.tasks import SimpleTaskFactory,\
42 SimpleTaskSplitter,\
43 TaskStates
44 from Products.ZenEvents.ZenEventClasses import Error, Clear, Status_WinService
45 from Products.ZenUtils.observable import ObservableMixin
46 from Products.ZenWin.WMIClient import WMIClient
47 from Products.ZenWin.Watcher import Watcher
48 from Products.ZenWin.utils import addNTLMv2Option, setNTLMv2Auth
49
50
51
52
53 from Products.ZenUtils.Utils import unused
54 from Products.ZenCollector.services.config import DeviceProxy
55 unused(DeviceProxy)
56
57
58
59
60 log = logging.getLogger("zen.zenwin")
61
62
63
65 zope.interface.implements(ICollectorPreferences)
66
68 """
69 Construct a new ZenWinPreferences instance and provide default
70 values for needed attributes.
71 """
72 self.collectorName = "zenwin"
73 self.defaultRRDCreateCommand = None
74 self.cycleInterval = 5 * 60
75 self.configCycleInterval = 20
76 self.options = None
77
78
79
80 self.configurationService = 'Products.ZenWin.services.WinServiceConfig'
81
82 self.wmibatchSize = 10
83 self.wmiqueryTimeout = 1000
84
86 parser.add_option('--debug', dest='debug', default=False,
87 action='store_true',
88 help='Increase logging verbosity.')
89 parser.add_option('--proxywmi', dest='proxywmi',
90 default=False, action='store_true',
91 help='Use a process proxy to avoid long-term blocking'
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 parser.add_option('--batchSize', dest='batchSize',
99 default=None, type='int',
100 help='Number of data objects to retrieve in a ' +
101 'single WMI query.')
102 addNTLMv2Option(parser)
103
104 - def postStartup(self):
105
106 logseverity = self.options.logseverity
107 if logseverity <= 5:
108 pysamba.library.DEBUGLEVEL.value = 99
109
110
111 setNTLMv2Auth(self.options)
112
113
115 zope.interface.implements(IScheduledTask)
116
117 STATE_WMIC_CONNECT = 'WMIC_CONNECT'
118 STATE_WMIC_QUERY = 'WMIC_QUERY'
119 STATE_WMIC_PROCESS = 'WMIC_PROCESS'
120 STATE_WATCHER_CONNECT = 'WATCHER_CONNECT'
121 STATE_WATCHER_QUERY = 'WATCHER_QUERY'
122 STATE_WATCHER_PROCESS = 'WATCHER_PROCESS'
123
124 - def __init__(self,
125 deviceId,
126 taskName,
127 scheduleIntervalSeconds,
128 taskConfig):
129 """
130 Construct a new task instance to watch for Windows Event Log changes
131 for the specified device.
132
133 @param deviceId: the Zenoss deviceId to watch
134 @type deviceId: string
135 @param taskName: the unique identifier for this task
136 @type taskName: string
137 @param scheduleIntervalSeconds: the interval at which this task will be
138 collected
139 @type scheduleIntervalSeconds: int
140 @param taskConfig: the configuration for this task
141 """
142 super(ZenWinTask, self).__init__()
143
144 self.name = taskName
145 self.configId = deviceId
146 self.interval = scheduleIntervalSeconds
147 self.state = TaskStates.STATE_IDLE
148
149 self._taskConfig = taskConfig
150 self._devId = deviceId
151 self._manageIp = self._taskConfig.manageIp
152
153 self._eventService = zope.component.queryUtility(IEventService)
154 self._preferences = zope.component.queryUtility(ICollectorPreferences,
155 "zenwin")
156
157
158
159
160
161 self._batchSize = self._preferences.options.batchSize
162 if not self._batchSize:
163 self._batchSize = self._preferences.wmibatchSize
164 self._queryTimeout = self._preferences.options.queryTimeout
165 if not self._queryTimeout:
166 self._queryTimeout = self._preferences.wmiqueryTimeout
167
168 self._wmic = None
169 self._watcher = None
170 self._reset()
171
173 """
174 Reset the WMI client and notification query watcher connection to the
175 device, if they are presently active.
176 """
177 if self._wmic:
178 self._wmic.close()
179 self._wmic = None
180 if self._watcher:
181 self._watcher.close()
182 self._watcher = None
183
185 """
186 Callback activated when the task is complete so that final statistics
187 on the collection can be displayed.
188 """
189 if not isinstance(result, Failure):
190 log.debug("Device %s [%s] scanned successfully",
191 self._devId, self._manageIp)
192 else:
193 log.debug("Device %s [%s] scanned failed, %s",
194 self._devId, self._manageIp, result.getErrorMessage())
195
196
197
198 return result
199
201 """
202 Errback for an unsuccessful asynchronous connection or collection
203 request.
204 """
205 err = result.getErrorMessage()
206 log.error("Unable to scan device %s: %s", self._devId, err)
207
208 self._reset()
209
210 summary = """
211 Could not read Windows services (%s). Check your
212 username/password settings and verify network connectivity.
213 """ % err
214
215 self._eventService.sendEvent(dict(
216 summary=summary,
217 component='zenwin',
218 eventClass=Status_WinService,
219 device=self._devId,
220 severity=Error,
221 agent='zenwin',
222 ))
223
224
225 return result
226
228 """
229 Handle a result from the wmi query. Results from both the initial WMI
230 client query and the watcher's notification query are processed by
231 this method. Log running and stopped transitions. Send an event if the
232 service is monitored.
233 """
234 services = self._taskConfig.services
235 stateDct = {'running': (Clear, log.info),
236 'stopped': (services.get(name), log.critical)}
237 if state in stateDct:
238 summary = "Windows service '%s' is %s" % (name, state)
239 if name in services:
240
241 severity, writeToLog = stateDct[state]
242 event = {'summary': summary,
243 'eventClass': Status_WinService,
244 'device': self._devId,
245 'severity': severity,
246 'agent': 'newzenwin',
247 'component': name,
248 'eventGroup': 'StatusTest'}
249 self._eventService.sendEvent(event)
250 else:
251
252 writeToLog = log.debug
253 writeToLog('%s on %s' % (summary, self._devId))
254
256 """
257 Callback for a successful fetch of services from the remote device.
258 """
259 self.state = ZenWinTask.STATE_WATCHER_PROCESS
260
261 log.debug("Successful collection from %s [%s], results=%s",
262 self._devId, self._manageIp, results)
263
264 if results:
265 for result in [r.targetInstance for r in results]:
266 if result.state:
267 self._handleResult(result.name, result.state.lower())
268
269
270
271
272
273 log.debug("Queuing another fetch for %s [%s]",
274 self._devId, self._manageIp)
275 d = defer.Deferred()
276 reactor.callLater(0, d.callback, None)
277 d.addCallback(self._collectCallback)
278 return d
279
281 """
282 Callback called after a connect or previous collection so that another
283 collection can take place.
284 """
285 log.debug("Polling for events from %s [%s]",
286 self._devId, self._manageIp)
287
288 self.state = ZenWinTask.STATE_WATCHER_QUERY
289 d = self._watcher.getEvents(self._queryTimeout, self._batchSize)
290 d.addCallbacks(self._collectSuccessful, self._failure)
291 return d
292
294 """
295 Callback called after a successful connect to the remote Windows device.
296 """
297 log.debug("Connected to %s [%s]", self._devId, self._manageIp)
298
314
321
323 """
324 Called when a connection needs to be created to the remote Windows
325 device.
326 """
327 log.debug("Connecting to %s [%s]", self._devId, self._manageIp)
328 self.state = ZenWinTask.STATE_WMIC_CONNECT
329 self._wmic = WMIClient(self._taskConfig)
330 d = self._wmic.connect()
331 d.addCallback(self._initialQuery)
332 return d
333
336
338 log.debug("Scanning device %s [%s]", self._devId, self._manageIp)
339
340
341 if not self._watcher:
342 d = self._connect()
343 d.addCallbacks(self._connectCallback, self._failure)
344 else:
345
346
347
348 d = defer.Deferred()
349 reactor.callLater(0, d.callback, None)
350
351
352
353 d.addCallback(self._collectCallback)
354
355
356
357
358 d.addBoth(self._finished)
359
360
361
362 return d
363
364
365
366
367
368 if __name__ == '__main__':
369 myPreferences = ZenWinPreferences()
370 myTaskFactory = SimpleTaskFactory(ZenWinTask)
371 myTaskSplitter = SimpleTaskSplitter(myTaskFactory)
372 daemon = CollectorDaemon(myPreferences, myTaskSplitter)
373 daemon.run()
374