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