Package Products :: Package ZenEvents :: Module Schedule |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenEvents.Schedule

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13  #! /usr/bin/env python
     14
     15  __doc__='''Schedule
     16
     17  Walk through the maintenance schedule.
     18
     19  $Id$
     20  '''
     21  import time
     22  import logging
     23  import transaction
     24  from twisted.internet import reactor
     25
     26  from Products.ZenEvents.ZenEventClasses import Status_Update
     27  import Event
     28
     29 -class Schedule:
     30
     31 -    def __init__(self, options, dmd):
     32          "start executing the schedule"
     33          self.dmd = dmd
     34          self.maintenance = []
     35          self.options = options
     36          self.log = logging.getLogger("zen.Schedule")
     37          self.workList = []
     38          self.timer = None
     39
     40
     41 -    def buildOptions(self, parser):
     42          "Set options in a borrowed parser"
     43
     44
     45 -    def start(self):
     46          "Start working the schedule"
     47          self.configCycle()
     48
     49
     50 -    def configCycle(self):
     51          "Basic event-driven config loop"
     52          self.run()
     53          reactor.callLater(self.options.cycletime, self.configCycle)
     54
     55
     56 -    def sync(self):
     57          "Synch with the database"
     58          self.dmd._p_jar.sync()
     59
     60 -    def getWindows(self):
     61          result = []
     62          catalog = getattr(self.dmd, 'maintenanceWindowSearch', None)
     63          if catalog is not None:
     64              for brain in catalog():
     65                  try:
     66                      ob = brain.getObject()
     67                  except KeyError:
     68                      # We're just catching the symptom for now, but an actual
     69                      # fix will be forthcoming.
     70                      # http://dev.zenoss.org/trac/ticket/3105
     71                      pass
     72                  else:
     73                      result.append(ob)
     74          else: # Should be removed in 2.3.
     75              self.log.warn('Run zenmigrate to index your maintenance windows.')
     76              for dev in self.dmd.Devices.getSubDevices():
     77                  result.extend(dev.maintenanceWindows())
     78              for name in 'Systems', 'Locations', 'Groups', 'Devices':
     79                  organizer = getattr(self.dmd, name)
     80                  for c in organizer.getSubOrganizers():
     81                      result.extend(c.maintenanceWindows())
     82                  result.extend(organizer.maintenanceWindows())
     83              for lst in [self.dmd.ZenUsers.getAllUserSettings(),
     84                          self.dmd.ZenUsers.getAllGroupSettings()]:
     85                  for us in lst:
     86                      for ar in us.objectValues(spec="ActionRule"):
     87                          result.extend([w for w in ar.windows() if w.enabled])
     88          return result
     89
     90 -    def run(self):
     91          "Re-read work list from the database"
     92          self.sync()
     93          self.workList = self.getWindows()
     94          self.runEvents()
     95
     96 -    def makeWorkList(self, now, workList):
     97          work = [(mw.nextEvent(now), mw) for mw in workList]
     98          work.sort()
     99          # note that None is less than any number of seconds
    100          while len(work):
    101              t, mw = work[0]
    102              if t: break
    103              if mw.enabled:
    104                  self.log.debug("Never going to run Maintenance "
    105                                 "Window %s for %s again",
    106                                 mw.getId(), mw.target().getId())
    107              if mw.started:
    108                  mw.end()
    109              work.pop(0)
    110          return work
    111
    112 -    def now(self):
    113          return time.time()
    114
    115 -    def runEvents(self):
    116          "Execute all the maintanance windows at the proper time"
    117
    118          if self.timer and not self.timer.called:
    119              self.timer.cancel()
    120
    121          # sort events by the next occurance of something to do
    122          now = self.now()
    123          work = self.makeWorkList(now, self.workList)
    124          self.workList = [mw for t, mw in work]
    125
    126          # fire events that should be done now
    127          for next, mw in work:
    128              if next <= now:
    129                  how = {True:'stopping', False:'starting'}[bool(mw.started)]
    130                  severity = {True:Event.Clear, False:Event.Info}[bool(mw.started)]
    131                  # Note: since the MWs always return devices back to their original
    132                  #       prod state, and there may be many devices, just provide an
    133                  #       'unknown' production state for stopping
    134                  prodState = {True:-99, False:mw.startProductionState}[bool(mw.started)]
    135                  mwId = mw.getId()
    136                  devices = mw.target().getId()
    137                  msg = "Maintenance window %s %s for %s" % (how, mwId, devices)
    138                  self.log.debug(msg)
    139                  dedupid = '|'.join(["zenactions",self.monitor,mwId,devices])
    140                  self.sendEvent(Event.Event(
    141                      component="zenactions",
    142                      severity=severity,
    143                      dedupid=dedupid,
    144                      eventClass=Status_Update,
    145                      eventClassKey="mw_change",
    146                      summary=msg,
    147                      maintenance_window=mwId,
    148                      maintenance_devices=devices,
    149                      device=self.monitor,
    150                      prodState=prodState,
    151                  ))
    152                  self.executeMaintenanceWindow(mw, next)
    153              else:
    154                  break
    155
    156          work = self.makeWorkList(now, self.workList)
    157          if work:
    158              wait = max(0, work[0][0] - now)
    159              self.log.debug("Waiting %f seconds", wait)
    160              self.timer = self.callLater(wait)
    161          self.commit()
    162
    163 -    def commit(self):
    164          transaction.commit()
    165
    166 -    def callLater(self, seconds):
    167          return reactor.callLater(seconds, self.runEvents)
    168
    169 -    def executeMaintenanceWindow(self, mw, timestamp):
    170          mw.execute(timestamp)
    171
    172  if __name__ == "__main__":
    173 -    class MySchedule(Schedule):
    174          currentTime = time.time()
    175          objs = None
    176 -        def now(self):
    177              return self.currentTime
    178 -        def callLater(self, seconds):
    179              self.currentTime += seconds
    180 -        def executeMaintenanceWindow(self, mw, timestamp):
    181              print 'executing', mw.id, time.ctime(timestamp)
    182              mw.execute(timestamp)
    183 -        def getWindows(self):
    184              if self.workList:
    185                  return self.workList
    186              return Schedule.getWindows(self)
    187 -        def commit(self):
    188              pass
    189          sync = commit
    190
    191      import Globals
    192      from Products.ZenUtils.ZCmdBase import ZCmdBase
    193
    194      cmd = ZCmdBase()
    195 -    class Options: pass
    196      s = MySchedule(Options(), cmd.dmd)
    197      # compute the schedule for 30 days
    198      end = s.currentTime + 60*60*24*30
    199      while s.currentTime < end:
    200          s.run()
    201