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

Source Code for Module Products.ZenEvents.Schedule

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