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

Source Code for Module 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):
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