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 or (at your 
  8  # option) any later version as published by 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   
 22  import time 
 23  import logging 
 24  from twisted.internet import reactor 
 25   
 26  from ZODB.transact import transact 
 27  from Products.ZenEvents.ZenEventClasses import Status_Update 
 28  from Products.ZenEvents import Event 
29 30 -class Schedule:
31
32 - def __init__(self, options, dmd):
33 "start executing the schedule" 34 self.dmd = dmd 35 self.maintenance = [] 36 self.options = options 37 self.log = logging.getLogger("zen.Schedule") 38 self.workList = [] 39 self.timer = None
40 41
42 - def buildOptions(self, parser):
43 "Set options in a borrowed parser"
44 45
46 - def start(self):
47 "Start working the schedule" 48 self.configCycle()
49 50
51 - def configCycle(self):
52 "Basic event-driven config loop" 53 self.run() 54 reactor.callLater(self.options.cycletime, 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 133 # sort events by the next occurance of something to do 134 now = self.now() 135 work = self.makeWorkList(now, self.workList) 136 self.workList = [mw for t, mw in work] 137 # fire events that should be done now 138 for next, mw in work: 139 if next <= now: 140 how = {True:'stopping', False:'starting'}[bool(mw.started)] 141 severity = {True:Event.Clear, False:Event.Info}[bool(mw.started)] 142 # Note: since the MWs always return devices back to their original 143 # prod state, and there may be many devices, just provide an 144 # 'unknown' production state for stopping 145 prodState = {True:-99, False:mw.startProductionState}[bool(mw.started)] 146 mwId = mw.getId() 147 devices = mw.target().getId() 148 msg = "Maintenance window %s %s for %s" % (how, mwId, devices) 149 self.log.debug(msg) 150 dedupid = '|'.join(["zenjobs",self.monitor,mwId,devices]) 151 self.sendEvent(Event.Event( 152 component="zenjobs", 153 severity=severity, 154 dedupid=dedupid, 155 eventClass=Status_Update, 156 eventClassKey="mw_change", 157 summary=msg, 158 eventKey='|'.join([mwId,devices]), 159 maintenance_window=mwId, 160 maintenance_devices=devices, 161 device=self.monitor, 162 prodState=prodState, 163 )) 164 self.executeMaintenanceWindow(mw, next) 165 else: 166 break 167 168 work = self.makeWorkList(now, self.workList) 169 if work: 170 wait = max(0, work[0][0] - now) 171 self.log.debug("Waiting %f seconds", wait) 172 self.timer = self.callLater(wait)
173
174 - def callLater(self, seconds):
175 return reactor.callLater(seconds, self.runEvents)
176 177 @transact
178 - def executeMaintenanceWindow(self, mw, timestamp):
180 181 if __name__ == "__main__":
182 - class MySchedule(Schedule):
183 currentTime = time.time() 184 objs = None
185 - def now(self):
186 return self.currentTime
187 - def callLater(self, seconds):
188 self.currentTime += seconds
189 - def executeMaintenanceWindow(self, mw, timestamp):
190 print 'executing', mw.id, time.ctime(timestamp) 191 mw.execute(timestamp)
192 - def getWindows(self):
193 if self.workList: 194 return self.workList 195 return Schedule.getWindows(self)
196 - def commit(self):
197 pass
198 sync = commit
199 200 import Globals 201 from Products.ZenUtils.ZCmdBase import ZCmdBase 202 203 cmd = ZCmdBase()
204 - class Options: pass
205 s = MySchedule(Options(), cmd.dmd) 206 # compute the schedule for 30 days 207 end = s.currentTime + 60*60*24*30 208 while s.currentTime < end: 209 s.run() 210