def wmiCollect(self, device, ip, timeout): """ Start the Windows Management Instrumentation (WMI) collector @param device: device to collect against @type device: string @param ip: IP address of device to collect against @type ip: string @param timeout: timeout before failing the connection @type timeout: integer """ if self.options.nowmi: return client = None try: plugins = self.selectPlugins(device, 'wmi') if not plugins: self.log.info("No WMI plugins found for %s" % device.id) return if self.checkCollection(device): self.log.info('WMI collector method for device %s' % device.id) self.log.info("plugins: %s", ", ".join(map(lambda p: p.name(), plugins))) client = WMIClient(device, self, plugins) if not client or not plugins: self.log.warn("WMI collector creation failed") return except (SystemExit, KeyboardInterrupt): raise except Exception: self.log.exception("Error opening WMI collector") self.addClient(client, timeout, 'WMI', device.id)
def _connect(self): log.debug("Connecting to %s [%s]", self._devId, self._manageIp) self.state = ZenWinTask.STATE_WMIC_CONNECT self._wmic = WMIClient(self._taskConfig) yield self._wmic.connect() self.state = ZenWinTask.STATE_WMIC_QUERY wql = "SELECT Name, State, StartMode FROM Win32_Service" result = yield self._wmic.query({'query': wql}) self.state = ZenWinTask.STATE_WMIC_PROCESS for service in result['query']: self._cacheServiceState(service) self._wmic.close() self._wmic = None self.state = ZenWinTask.STATE_WATCHER_CONNECT wql = "SELECT * FROM __InstanceModificationEvent WITHIN 5 "\ "WHERE TargetInstance ISA 'Win32_Service'" self._watcher = Watcher(self._taskConfig, wql) yield self._watcher.connect() log.debug("Connected to %s [%s]", self._devId, self._manageIp)
class ZenWinTask(ObservableMixin): zope.interface.implements(IScheduledTask) STATE_WMIC_CONNECT = 'WMIC_CONNECT' STATE_WMIC_QUERY = 'WMIC_QUERY' STATE_WMIC_PROCESS = 'WMIC_PROCESS' STATE_WATCHER_CONNECT = 'WATCHER_CONNECT' STATE_WATCHER_QUERY = 'WATCHER_QUERY' STATE_WATCHER_PROCESS = 'WATCHER_PROCESS' # windows service states from wmi queries (lowercased) RUNNING = "running" STOPPED = "stopped" def __init__(self, deviceId, taskName, scheduleIntervalSeconds, taskConfig): """ Construct a new task instance to watch for Windows Event Log changes for the specified device. @param deviceId: the Zenoss deviceId to watch @type deviceId: string @param taskName: the unique identifier for this task @type taskName: string @param scheduleIntervalSeconds: the interval at which this task will be collected @type scheduleIntervalSeconds: int @param taskConfig: the configuration for this task """ super(ZenWinTask, self).__init__() self.name = taskName self.configId = deviceId self.interval = scheduleIntervalSeconds self.state = TaskStates.STATE_IDLE self._taskConfig = taskConfig self._devId = deviceId self._manageIp = self._taskConfig.manageIp self._eventService = zope.component.queryUtility(IEventService) self._preferences = zope.component.queryUtility( ICollectorPreferences, "zenwin") # if the user hasn't specified the batchSize or queryTimeout as command # options then use whatever has been specified in the collector # preferences # TODO: convert these to zProperties self._batchSize = self._preferences.options.batchSize if not self._batchSize: self._batchSize = self._preferences.wmibatchSize self._queryTimeout = self._preferences.options.queryTimeout if not self._queryTimeout: self._queryTimeout = self._preferences.wmiqueryTimeout # Calculate the number of cycles, after which the state of every # service should be reinitialised, to make sure the service state # is up to date even if some notification messages were missed. self._refreshCycles = self._preferences.options.statusRefresh * 60 / self.interval self._currentCycles = 0 self._wmic = None # the WMIClient self._watcher = None self._reset() def _reset(self): """ Reset the WMI client and notification query watcher connection to the device, if they are presently active. """ if self._wmic: self._wmic.close() self._wmic = None if self._watcher: self._watcher.close() self._watcher = None def _sendWinServiceEvent(self, name, summary, severity): event = { 'summary': summary, 'eventClass': Status_WinService, 'device': self._devId, 'severity': severity, 'agent': 'zenwin', 'component': name, 'eventGroup': 'StatusTest' } self._eventService.sendEvent(event) def _isRunning(self, state): return state.lower() == self.RUNNING def _cacheServiceState(self, service): if service.name in self._taskConfig.services: _, stoppedSeverity, _, monitoredStartModes = self._taskConfig.services[ service.name] running = self._isRunning(service.state) log.debug("Service %s has initial state: %s mode: %s", service.name, service.state, service.startMode) self._taskConfig.services[service.name] =\ (running, stoppedSeverity, service.startMode, monitoredStartModes) def _handleResult(self, name, state, startMode): """ Handle a result from the wmi query. Results from both the initial WMI client query and the watcher's notification query are processed by this method. Log running and stopped transitions. Send an event if the service is monitored. """ if state is None: state = "unknown" state = state.lower() summary = "Windows service '%s' is %s" % (name, state) logLevel = logging.DEBUG if name in self._taskConfig.services: was_running, stoppedSeverity, oldStartMode, monitoredStartModes = \ self._taskConfig.services[name] running = self._isRunning(state) service_was_important = (oldStartMode in monitoredStartModes) service_is_important = (startMode in monitoredStartModes) logLevel = logging.INFO if service_is_important: if running: self._sendWinServiceEvent(name, summary, Clear) else: self._sendWinServiceEvent(name, summary, stoppedSeverity) logLevel = logging.CRITICAL else: self._sendWinServiceEvent(name, summary, Clear) self._taskConfig.services[name] = (running, stoppedSeverity, startMode, monitoredStartModes) logState = state if startMode == '' or startMode is None: logState = "unknown" summary = "Windows service '%s' is %s" % (name, logState) log.log(logLevel, '%s on %s', summary, self._devId) def cleanup(self): return self._reset() @defer.inlineCallbacks def _connect(self): log.debug("Connecting to %s [%s]", self._devId, self._manageIp) self.state = ZenWinTask.STATE_WMIC_CONNECT self._wmic = WMIClient(self._taskConfig) yield self._wmic.connect() self.state = ZenWinTask.STATE_WMIC_QUERY wql = "SELECT Name, State, StartMode FROM Win32_Service" result = yield self._wmic.query({'query': wql}) self.state = ZenWinTask.STATE_WMIC_PROCESS for service in result['query']: self._cacheServiceState(service) self._wmic.close() self._wmic = None self.state = ZenWinTask.STATE_WATCHER_CONNECT wql = "SELECT * FROM __InstanceModificationEvent WITHIN 5 "\ "WHERE TargetInstance ISA 'Win32_Service'" self._watcher = Watcher(self._taskConfig, wql) yield self._watcher.connect() log.debug("Connected to %s [%s]", self._devId, self._manageIp) @defer.inlineCallbacks def doTask(self): log.debug("Scanning device %s [%s]", self._devId, self._manageIp) try: # Refresh the initial state of services. if self._currentCycles >= self._refreshCycles: log.debug("Refreshing the initial states of services") self._reset() self._currentCycles = 0 # see if we need to connect first before doing any collection if not self._watcher: yield self._connect() # try collecting events after a successful connect, or if we're already # connected if self._watcher: log.debug("Polling for events from %s [%s]", self._devId, self._manageIp) # make a local copy of monitored services list services = self._taskConfig.services.copy() # read until we get an empty results list returned from our query self.state = ZenWinTask.STATE_WATCHER_QUERY results = [] while True: newresults = yield self._watcher.getEvents( self._queryTimeout, self._batchSize) if not newresults: break results.extend(newresults) log.debug("Queuing another fetch for %s [%s]", self._devId, self._manageIp) if log.isEnabledFor(logging.DEBUG): showattrs = lambda ob, attrs: tuple( getattr(ob, attr, '') for attr in attrs) log.debug("Successful collection from %s [%s], results=%s", self._devId, self._manageIp, [ showattrs(r.targetInstance, ('name', 'state', 'startmode')) for r in results ]) self.state = ZenWinTask.STATE_WATCHER_PROCESS # collapse repeated results for same service - this maintains our # state model from collection to collection, without weird # intermediate states caused by multiple service state and # configuration changes between collections messing things up results_summary = {} for r in results: result = r.targetInstance if result.state: results_summary[result.name] = r # now process all results to update service state # and emit CRITICAL/CLEAR events as needed for r in results_summary.itervalues(): result = r.targetInstance # remove service from local copy services.pop(result.name, None) self._handleResult(result.name, result.state, result.startmode) # send events for the services that did not show up in results for name, data in services.iteritems(): running, failSeverity, startMode, monitoredStartModes = data if running: state = self.RUNNING else: state = self.STOPPED self._handleResult(name, state, startMode) msg = 'WMI connection to %s up.' % self._devId self._eventService.sendEvent( dict(summary=msg, eventClass=Status_Wmi, device=self._devId, severity=Clear, component='zenwin')) # Increment the number of cycles passed. self._currentCycles += 1 log.debug("Device %s [%s] scanned successfully", self._devId, self._manageIp) except Exception as e: err = str(e) log.debug("Device %s [%s] scanned failed, %s", self._devId, self._manageIp, err) log.error("Unable to scan device %s: %s", self._devId, err) self._reset() summary = """ Could not read Windows services (%s). Check your username/password settings and verify network connectivity. """ % err self._eventService.sendEvent( dict(summary=summary, component='zenwin', eventClass=Status_Wmi, device=self._devId, severity=Error, traceback=traceback.format_exc()))
class ZenWinTask(ObservableMixin): zope.interface.implements(IScheduledTask) STATE_WMIC_CONNECT = 'WMIC_CONNECT' STATE_WMIC_QUERY = 'WMIC_QUERY' STATE_WMIC_PROCESS = 'WMIC_PROCESS' STATE_WATCHER_CONNECT = 'WATCHER_CONNECT' STATE_WATCHER_QUERY = 'WATCHER_QUERY' STATE_WATCHER_PROCESS = 'WATCHER_PROCESS' # windows service states from wmi queries (lowercased) RUNNING = "running" STOPPED = "stopped" def __init__(self, deviceId, taskName, scheduleIntervalSeconds, taskConfig): """ Construct a new task instance to watch for Windows Event Log changes for the specified device. @param deviceId: the Zenoss deviceId to watch @type deviceId: string @param taskName: the unique identifier for this task @type taskName: string @param scheduleIntervalSeconds: the interval at which this task will be collected @type scheduleIntervalSeconds: int @param taskConfig: the configuration for this task """ super(ZenWinTask, self).__init__() self.name = taskName self.configId = deviceId self.interval = scheduleIntervalSeconds self.state = TaskStates.STATE_IDLE self._taskConfig = taskConfig self._devId = deviceId self._manageIp = self._taskConfig.manageIp self._eventService = zope.component.queryUtility(IEventService) self._preferences = zope.component.queryUtility(ICollectorPreferences, "zenwin") # if the user hasn't specified the batchSize or queryTimeout as command # options then use whatever has been specified in the collector # preferences # TODO: convert these to zProperties self._batchSize = self._preferences.options.batchSize if not self._batchSize: self._batchSize = self._preferences.wmibatchSize self._queryTimeout = self._preferences.options.queryTimeout if not self._queryTimeout: self._queryTimeout = self._preferences.wmiqueryTimeout # Calculate the number of cycles, after which the state of every # service should be reinitialised, to make sure the service state # is up to date even if some notification messages were missed. self._refreshCycles = self._preferences.options.statusRefresh * 60 / self.interval self._currentCycles = 0 self._wmic = None # the WMIClient self._watcher = None self._reset() def _reset(self): """ Reset the WMI client and notification query watcher connection to the device, if they are presently active. """ if self._wmic: self._wmic.close() self._wmic = None if self._watcher: self._watcher.close() self._watcher = None def _sendWinServiceEvent(self, name, summary, severity): event = {'summary': summary, 'eventClass': Status_WinService, 'device': self._devId, 'severity': severity, 'agent': 'zenwin', 'component': name, 'eventGroup': 'StatusTest'} self._eventService.sendEvent(event) def _isRunning(self, state): return state.lower() == self.RUNNING def _cacheServiceState(self, service): if service.name in self._taskConfig.services: _, stoppedSeverity, _, monitoredStartModes = self._taskConfig.services[service.name] running = self._isRunning(service.state) log.debug("Service %s has initial state: %s mode: %s", service.name, service.state, service.startMode) self._taskConfig.services[service.name] =\ (running, stoppedSeverity, service.startMode, monitoredStartModes) def _handleResult(self, name, state, startMode): """ Handle a result from the wmi query. Results from both the initial WMI client query and the watcher's notification query are processed by this method. Log running and stopped transitions. Send an event if the service is monitored. """ if state is None: state = "unknown" state = state.lower() summary = "Windows service '%s' is %s" % (name, state) logLevel = logging.DEBUG if name in self._taskConfig.services: was_running, stoppedSeverity, oldStartMode, monitoredStartModes = \ self._taskConfig.services[name] running = self._isRunning(state) service_was_important = (oldStartMode in monitoredStartModes) service_is_important = (startMode in monitoredStartModes) logLevel = logging.INFO if service_is_important: if running: self._sendWinServiceEvent(name, summary, Clear) else: self._sendWinServiceEvent(name, summary, stoppedSeverity) logLevel = logging.CRITICAL else: self._sendWinServiceEvent(name, summary, Clear) self._taskConfig.services[name] = ( running, stoppedSeverity, startMode, monitoredStartModes) logState = state if startMode == '' or startMode is None: logState = "unknown" summary = "Windows service '%s' is %s" % (name, logState) log.log(logLevel, '%s on %s', summary, self._devId) def cleanup(self): return self._reset() @defer.inlineCallbacks def _connect(self): log.debug("Connecting to %s [%s]", self._devId, self._manageIp) self.state = ZenWinTask.STATE_WMIC_CONNECT self._wmic = WMIClient(self._taskConfig) yield self._wmic.connect() self.state = ZenWinTask.STATE_WMIC_QUERY wql = "SELECT Name, State, StartMode FROM Win32_Service" result = yield self._wmic.query({'query': wql}) self.state = ZenWinTask.STATE_WMIC_PROCESS for service in result['query']: self._cacheServiceState(service) self._wmic.close() self._wmic = None self.state = ZenWinTask.STATE_WATCHER_CONNECT wql = "SELECT * FROM __InstanceModificationEvent WITHIN 5 "\ "WHERE TargetInstance ISA 'Win32_Service'" self._watcher = Watcher(self._taskConfig, wql) yield self._watcher.connect() log.debug("Connected to %s [%s]", self._devId, self._manageIp) @defer.inlineCallbacks def doTask(self): log.debug("Scanning device %s [%s]", self._devId, self._manageIp) try: # Refresh the initial state of services. if self._currentCycles >= self._refreshCycles: log.debug("Refreshing the initial states of services") self._reset() self._currentCycles = 0 # see if we need to connect first before doing any collection if not self._watcher: yield self._connect() # try collecting events after a successful connect, or if we're already # connected if self._watcher: log.debug("Polling for events from %s [%s]", self._devId, self._manageIp) # make a local copy of monitored services list services = self._taskConfig.services.copy() # read until we get an empty results list returned from our query self.state = ZenWinTask.STATE_WATCHER_QUERY results = [] while True: newresults = yield self._watcher.getEvents(self._queryTimeout, self._batchSize) if not newresults: break results.extend(newresults) log.debug("Queuing another fetch for %s [%s]", self._devId, self._manageIp) if log.isEnabledFor(logging.DEBUG): showattrs = lambda ob,attrs: tuple(getattr(ob,attr,'') for attr in attrs) log.debug("Successful collection from %s [%s], results=%s", self._devId, self._manageIp, [showattrs(r.targetInstance, ('name','state','startmode')) for r in results] ) self.state = ZenWinTask.STATE_WATCHER_PROCESS # collapse repeated results for same service - this maintains our # state model from collection to collection, without weird # intermediate states caused by multiple service state and # configuration changes between collections messing things up results_summary = {} for r in results: result = r.targetInstance if result.state: results_summary[result.name] = r # now process all results to update service state # and emit CRITICAL/CLEAR events as needed for r in results_summary.itervalues(): result = r.targetInstance # remove service from local copy services.pop(result.name, None) self._handleResult(result.name, result.state, result.startmode) # send events for the services that did not show up in results for name, data in services.iteritems(): running, failSeverity, startMode, monitoredStartModes = data if running: state = self.RUNNING else: state = self.STOPPED self._handleResult(name, state, startMode) msg = 'WMI connection to %s up.' % self._devId self._eventService.sendEvent(dict( summary=msg, eventClass=Status_Wmi, device=self._devId, severity=Clear, component='zenwin')) # Increment the number of cycles passed. self._currentCycles += 1 log.debug("Device %s [%s] scanned successfully", self._devId, self._manageIp) except Exception as e: err = str(e) log.debug("Device %s [%s] scanned failed, %s", self._devId, self._manageIp, err) log.error("Unable to scan device %s: %s", self._devId, err) self._reset() summary = """ Could not read Windows services (%s). Check your username/password settings and verify network connectivity. """ % err self._eventService.sendEvent(dict( summary=summary, component='zenwin', eventClass=Status_Wmi, device=self._devId, severity=Error, traceback=traceback.format_exc() ))