cosmetic
[nodemanager.git] / plugins / reservation.py
1 # $Id$
2 # $URL$
3 #
4 # NodeManager plugin - first step of handling reservable nodes
5
6 """
7 Manages running slices when reservation_policy is 'lease_or_idle' or 'lease_or_shared'
8 """
9
10 import time
11 import threading
12
13 import logger
14
15 priority = 45
16
17 # this instructs nodemanager that we want to use the latest known data in case the plc link is down
18 persistent_data = True
19
20 # of course things would be simpler if node manager was to create one instance of the plugins 
21 # instead of blindly caling functions in the module...
22
23 ##############################
24 # rough implementation for a singleton class
25 def Singleton (klass,*args,**kwds):
26     if not hasattr(klass,'_instance'):
27         klass._instance=klass(*args,**kwds)
28     return klass._instance
29
30 def start(options, conf):
31     return Singleton(reservation).start(options,conf)
32
33 def GetSlivers(data, conf = None, plc = None):
34     return Singleton(reservation).GetSlivers(data, conf, plc)
35
36 ##############################
37 class reservation:
38
39     def __init__ (self):
40         # the last snapshot of data exposed by GetSlivers
41         self.data = None
42         # this is a dict mapping a raounded timestamp to the corr. Timer object
43         self.timers = {}
44  
45     # the granularity is set in the API (initial value is 15 minutes)
46     # and it used to round all leases start/until times
47     # changing this dynamically can have some weird effects of course..
48     def granularity (self):
49         try:
50             return self.data['lease_granularity']
51         # in case we'd try to access this before it's populated..
52         except:
53             return 60*60
54
55     # round to granularity
56     def round_time (self, time):
57         granularity=self.granularity()
58         return ((int(time)+granularity/2)/granularity)*granularity
59
60     def clear_timers (self):
61         for timer in self.timers.values():
62             timer.cancel()
63         self.timers={}
64
65     def clear_timer (self,timestamp):
66         round=self.round_time(timestamp)
67         if round in self.timers:
68             timer=self.timers[round]
69             timer.cancel()
70             del self.timers[round]
71
72     def sync_timers_from_leases (self):
73         self.clear_timers()
74         for lease in self.data['leases']:
75             self.ensure_timer(lease['t_from'])
76             self.ensure_timer(lease['t_until'])
77
78     def ensure_timer(self, timestamp):
79         now=time.time()
80         # forget about past events
81         if timestamp < now: return
82         round=self.round_time(timestamp)
83         if round in self.timers: return
84         def this_closure ():
85             self.round_time_callback (round)
86         timer=threading.Timer(timestamp-now,this_closure)
87         self.timers[round]=timer
88         timer.start()
89
90     def round_time_callback (self, time_arg):
91         now=time.time()
92         round_now=self.round_time(now)
93         logger.log('reservation.round_time_callback now=%f round_now=%d arg=%d...'%(now,round_now,time_arg))
94         leases=self.data.leases
95         if leases:
96             logger.verbose('Listing leases beg')
97             for lease in leases:
98                 logger.verbose("lease=%r"%lease)
99             logger.verbose('Listing leases end')
100         for lease in leases:
101             if lease['t_until']==round_now:
102                 logger.log('Suspending slice %s - ending lease %d'%(lease['name'],lease['lease_id']))
103                 self.suspend_slice (lease['name'])
104         for lease in leases:
105             if lease['t_from']==round_now:
106                 logger.log('Starting slice %s - starting lease %d'%(lease['name'],lease['lease_id']))
107                 self.restart_slice (lease['name'])
108
109
110     def suspend_slice(self, slicename):
111         logger.log('reservation.suspend_slice, slice %s, to be written'%slicename)
112                 
113     def restart_slice(self, slicename):
114         logger.log('reservation.restart_slice, slice %s, to be written'%slicename)
115
116     def show_time (self, timestamp):
117         return time.strftime ("%Y-%m-%d %H:%M %Z",time.gmtime(timestamp))
118
119     ####################
120     def start(self,options,conf):
121         logger.log("reservation: plugin performing dummy start...")
122
123     # this method is entirely about making sure that we have events scheduled 
124     # at the <granularity> intervals where there is a lease that starts or ends
125     def GetSlivers (self, data, conf=None, plc=None):
126     
127         # check we're using a compliant GetSlivers
128         if 'reservation_policy' not in data: 
129             logger.log_missing_data("reservation.GetSlivers",'reservation_policy')
130             return
131         reservation_policy=data['reservation_policy']
132         if 'leases' not in data: 
133             logger.log_missing_data("reservation.GetSlivers",'leases')
134             return
135     
136
137         # store data locally
138         # since we've asked for persistent_data, we should not get an empty data here
139         if data: self.data = data
140
141         # regular nodes are not affected
142         if reservation_policy == 'none':
143             return
144         elif reservation_policy not in ['lease_or_idle','lease_or_shared']:
145             logger.log("reservation: ignoring -- unexpected value for reservation_policy %r"%reservation_policy)
146             return
147         # at this point we have reservation_policy in ['lease_or_idle','lease_or_shared']
148         # we make no difference for now
149         logger.verbose('reservation.GetSlivers : reservable node -- listing timers ')
150         
151         self.sync_timers_from_leases()
152         for timestamp in self.timers.keys():
153             logger.verbose('TIMER armed for %s'%self.show_time(timestamp))
154            
155         logger.verbose('reservation.GetSlivers : end listing timers')
156