1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 __doc__ = """WinCollector
17 PB daemon-izable base class for creating WMI collectors
18 """
19
20 import time
21
22 import Globals
23 from Products.ZenHub.PBDaemon import PBDaemon, FakeRemote
24 from Products.ZenEvents.ZenEventClasses import App_Start, Clear, \
25 Status_Wmi
26 from Products.ZenEvents.Event import Error
27 from Products.ZenUtils.Driver import drive, driveLater
28 from Products.ZenUtils.Utils import unused
29
30
31 from Products.DataCollector import DeviceProxy
32 from Products.DataCollector.Plugins import PluginLoader
33 unused(DeviceProxy, PluginLoader)
34
35 from twisted.internet import reactor, defer
36 from twisted.python.failure import Failure
37 from pysamba.library import DEBUGLEVEL
38
39
41 """
42 Base class to be sub-classed by WMI daemons
43 """
44 configCycleInterval = 20
45 wmiqueryTimeout = 1000
46
47
48 whatIDo = 'Override whatIDo in a subclass'
49
50 initialServices = PBDaemon.initialServices\
51 + ['Products.ZenWin.services.WmiConfig']
52
53 attributes = ('configCycleInterval', 'wmiqueryTimeout')
54 deviceAttributes = (
55 'manageIp', 'zWinPassword', 'zWinUser', 'zWmiMonitorIgnore')
56
65
76
78 """
79 Stop (reactor.stop()) the collection
80
81 @param unused: unused
82 @type unused: string
83 """
84 self.stop()
85
87 """
88 Generator function to collect data in one scan cycle
89
90 @param driver: driver
91 @type driver: string
92 @return: defered task
93 @rtype: Twisted defered or DeferredList
94 """
95 now = time.time()
96 cycle = self.cycleInterval()
97 try:
98 yield self.eventService().callRemote('getWmiConnIssues')
99 self.wmiprobs = [e[0] for e in driver.next()]
100 self.log.debug('Wmi Probs %r', self.wmiprobs)
101 devices = []
102 if self.devices is not None:
103 for device in self.devices:
104 if not device.plugins:
105 continue
106 if self.options.device and device.id\
107 != self.options.device:
108 continue
109 if device.id in self.wmiprobs:
110 self.log.debug('WMI problems on %s: skipping'
111 % device.id)
112 continue
113 devices.append(device)
114 yield self.processLoop(devices, cycle)
115 driver.next()
116 if not self.options.cycle:
117 self.stopScan()
118 else:
119 self.heartbeat()
120 count = len(self.devices)
121 if self.options.device:
122 count = 1
123 delay = time.time() - now
124 self.sendEvents(self.rrdStats.gauge('cycleTime', cycle,
125 delay) + self.rrdStats.gauge('devices',
126 cycle, count))
127 self.log.info('Scanned %d devices in %.1f seconds',
128 count, delay)
129 except (Failure, Exception), ex:
130 self.log.exception('Error processing main loop')
131
132
133 if self.options.cycle:
134 delay = time.time() - now
135 driveLater(max(0, cycle - delay), self.scanCycle)
136
137
139 """
140 Cycle through the list of devices and collect from them.
141
142 @param devices: device object list
143 @type devices: list
144 @param timeoutSecs: timeoutSecs
145 @type timeoutSecs: int
146 @return: list of defereds to processDevice()
147 @rtype: Twisted DeferredList
148 """
149 deferreds = []
150 for device in devices:
151 deferreds.append(self.processDevice(device, timeoutSecs))
152 return defer.DeferredList(deferreds)
153
155 """
156 Perform a collection service on a device
157
158 @param device: device to query
159 @type device: object
160 @param timeoutSecs: timeoutSecs
161 @type timeoutSecs: int
162 @return: defered to complete the processing
163 @rtype: Twisted defered
164 """
165 raise NotImplementedError('You must override this method.')
166
168 """
169 Return the length of time in a scan cycle
170
171 Method which must be overridden
172
173 @return: number of seconds in a cycle
174 @rtype: int
175 """
176 raise NotImplementedError('You must override this method')
177
179 """
180 Gather this daemons WMI configuration
181
182 @return: zenhub configuration information
183 @rtype: WmiConfig
184 """
185 return self.services.get('Products.ZenWin.services.WmiConfig',
186 FakeRemote())
187
188
190 for att in self.deviceAttributes:
191 if getattr(d1, att, None) != getattr(d2, att, None):
192 return False
193 return True
194
195
197 """
198 Update device configuration
199
200 @param devices: list of devices
201 @type devices: list
202 """
203 self.devices = devices
204 newDevices = {}
205 for d in devices: newDevices[d.id] = d
206 for (deviceName, watcher) in self.watchers.items():
207 changed = False
208 if deviceName not in newDevices:
209 self.log.info("Stopping monitoring of %s.", deviceName)
210 changed = True
211 elif not self.devicesEqual(watcher.device, newDevices[deviceName]):
212 self.log.info("Updating configuration of %s.", deviceName)
213 changed = True
214
215 if changed:
216 del self.watchers[deviceName]
217 watcher.close()
218
219
221 """
222 Function called from zenhub to remove a device from our
223 list of devices.
224
225 @param deviceId: deviceId
226 @type deviceId: string
227 """
228 devices = []
229 for d in self.devices:
230 if deviceId == d.id:
231 self.log.info("Stopping monitoring of %s.", deviceId)
232 else:
233 devices.append(d)
234 self.devices = devices
235
236
238 """
239 Twisted errback routine to log messages
240
241 @param why: error message
242 @type why: string
243 """
244 self.log.error(why.getErrorMessage())
245
247 """
248 updateConfig
249
250 @param cfg: configuration from zenhub
251 @type cfg: WmiConfig
252 """
253 cfg = dict(cfg)
254 for attribute in self.attributes:
255 current = getattr(self, attribute, None)
256 value = cfg.get(attribute)
257 self.log.debug( "Received %s = %r from zenhub" % ( attribute, value))
258 if current is not None and current != value:
259 self.log.info('Setting %s to %r', attribute, value)
260 setattr(self, attribute, value)
261 self.heartbeatTimeout = self.cycleInterval() * 3
262
272
274 """
275 Calls start() and then goes through scanCycle() until finished
276
277 @param unused: unused
278 @type unused: string
279 """
280 self.start()
281 d = drive(self.scanCycle)
282
307
324
354
356 """
357 Gather configuration and set up to re-check.
358
359 @return: defered
360 @rtype: Twisted defered
361 """
362 def driveAgain(result):
363 """
364 callback and errback to gather configurations
365
366 @param result: result
367 @type result: value
368 @return: defered
369 @rtype: Twisted deferrd
370 """
371 driveLater(self.configCycleInterval * 60, self.reconfigure)
372 return result
373
374 return drive(self.reconfigure).addBoth(driveAgain)
375
377 """
378 Method called after a connection to zenhub is established.
379 Calls startConfigCycle() and startScan()
380 """
381 d = self.startConfigCycle()
382 d.addCallback(self.startScan)
383
385 """
386 Command-line option builder
387 """
388 PBDaemon.buildOptions(self)
389 self.parser.add_option('-d', '--device', dest='device',
390 default=None,
391 help='The name of a single device to collect')
392 self.parser.add_option('--debug', dest='debug', default=False,
393 action='store_true',
394 help='Increase logging verbosity.')
395 self.parser.add_option('--proxywmi', dest='proxywmi',
396 default=False, action='store_true',
397 help='Use a process proxy to avoid long-term blocking'
398 )
399 self.parser.add_option('--queryTimeout', dest='queryTimeout',
400 default=None, type='int',
401 help='The number of milliseconds to wait for WMI query to respond.' + \
402 ' Overrides the server settings.' )
403