source: dassmodus/trunk/dassmodus/nosferatu/nosferatu/scheduleparser.py@ 994

Last change on this file since 994 was 953, checked in by pstorz, on Sep 28, 2011 at 11:32:32 AM

first checkin

File size: 15.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: scheduleparser.py 12290 2011-02-10 14:02:38Z pstorz $
4import re
5import operator
6
7import datetime,time
8
9# parser for schedules
10# 1.: downcase string
11# 2.: find strings: jan-dec, w00-w53, mon-sun, 1-31, 1st-5th| first-fifth, overrides:
12
13# Keywords (from dird/run_conf.c
14
15# TODO: put this in central c2xxx file
16
17keyword_definition_string = """
18static struct s_keyw keyw[] = {
19 {NT_("on"), s_none, 0},
20 {NT_("at"), s_at, 0},
21
22 {NT_("sun"), s_wday, 0},
23 {NT_("mon"), s_wday, 1},
24 {NT_("tue"), s_wday, 2},
25 {NT_("wed"), s_wday, 3},
26 {NT_("thu"), s_wday, 4},
27 {NT_("fri"), s_wday, 5},
28 {NT_("sat"), s_wday, 6},
29 {NT_("jan"), s_month, 0},
30 {NT_("feb"), s_month, 1},
31 {NT_("mar"), s_month, 2},
32 {NT_("apr"), s_month, 3},
33 {NT_("may"), s_month, 4},
34 {NT_("jun"), s_month, 5},
35 {NT_("jul"), s_month, 6},
36 {NT_("aug"), s_month, 7},
37 {NT_("sep"), s_month, 8},
38 {NT_("oct"), s_month, 9},
39 {NT_("nov"), s_month, 10},
40 {NT_("dec"), s_month, 11},
41
42 {NT_("sunday"), s_wday, 0},
43 {NT_("monday"), s_wday, 1},
44 {NT_("tuesday"), s_wday, 2},
45 {NT_("wednesday"), s_wday, 3},
46 {NT_("thursday"), s_wday, 4},
47 {NT_("friday"), s_wday, 5},
48 {NT_("saturday"), s_wday, 6},
49 {NT_("january"), s_month, 0},
50 {NT_("february"), s_month, 1},
51 {NT_("march"), s_month, 2},
52 {NT_("april"), s_month, 3},
53 {NT_("june"), s_month, 5},
54 {NT_("july"), s_month, 6},
55 {NT_("august"), s_month, 7},
56 {NT_("september"), s_month, 8},
57 {NT_("october"), s_month, 9},
58 {NT_("november"), s_month, 10},
59 {NT_("december"), s_month, 11},
60
61 {NT_("daily"), s_daily, 0},
62 {NT_("weekly"), s_weekly, 0},
63 {NT_("monthly"), s_monthly, 0},
64 {NT_("hourly"), s_hourly, 0},
65
66 {NT_("1st"), s_wom, 0},
67 {NT_("2nd"), s_wom, 1},
68 {NT_("3rd"), s_wom, 2},
69 {NT_("4th"), s_wom, 3},
70 {NT_("5th"), s_wom, 4},
71
72 {NT_("first"), s_wom, 0},
73 {NT_("second"), s_wom, 1},
74 {NT_("third"), s_wom, 2},
75 {NT_("fourth"), s_wom, 3},
76 {NT_("fifth"), s_wom, 4},
77 {NULL, s_none, 0}
78};
79"""
80#RXP_KEYWORDDEF = re.compile('^\s+\{NT_\("(?P<keyword>[\w|\d]+"\)).*')
81RXP_KEYWORDDEF = re.compile('\s+{NT_\("(.*)"\),.*s_(\w+),.*(\d+)},\n')
82#RXP_KEYWORDDEF = re.compile('.*')
83RXP_OVERRIDE = re.compile('(?P<name>\S+\s*)=(?P<value>\s*\S+)')
84#RXP_OVERRIDE = re.compile('(?P<name>\w+)\s*=\s*(?P<value>\w+)')
85
86
87on= {}
88at = {}
89wday = {}
90month = {}
91daily = {}
92weekly = {}
93monthly = {}
94hourly = {}
95wom = {}
96
97mday = {}
98for i in range(31):
99 mday[str(i+1)] = i+1
100
101woy = {}
102for i in range(54):
103 index = "w%02d" % (i)
104 woy[index] = i
105
106
107keywords = {}
108keywords['on'] = on
109keywords['at'] = at
110keywords['wday'] = wday
111keywords['month'] = month
112keywords['daily'] = daily
113keywords['weekly'] = weekly
114keywords['hourly'] = hourly
115keywords['monthly'] = monthly
116keywords['wom'] = wom
117keywords['mday'] = mday
118keywords['woy'] = woy
119
120#for kwdef in RXP_KEYWORDDEF.finditer(keyword_definition_string):
121 #print kwdef.group(1), kwdef.group(2),kwdef.group(3)
122 #if kwdef.group(2) == 'none':
123 # continue
124# evalstring = "%s['%s']=%s" % (kwdef.group(2), kwdef.group(1),kwdef.group(3))
125# print evalstring
126
127on['on']=0
128at['at']=0
129wday['sun']=0
130wday['mon']=1
131wday['tue']=2
132wday['wed']=3
133wday['thu']=4
134wday['fri']=5
135wday['sat']=6
136month['jan']=0
137month['feb']=1
138month['mar']=2
139month['apr']=3
140month['may']=4
141month['jun']=5
142month['jul']=6
143month['aug']=7
144month['sep']=8
145month['oct']=9
146month['nov']=10
147month['dec']=11
148wday['sunday']=0
149wday['monday']=1
150wday['tuesday']=2
151wday['wednesday']=3
152wday['thursday']=4
153wday['friday']=5
154wday['saturday']=6
155month['january']=0
156month['february']=1
157month['march']=2
158month['april']=3
159month['june']=5
160month['july']=6
161month['august']=7
162month['september']=8
163month['october']=9
164month['november']=10
165month['december']=11
166daily['daily']=0
167weekly['weekly']=0
168monthly['monthly']=0
169hourly['hourly']=0
170wom['1st']=0
171wom['2nd']=1
172wom['3rd']=2
173wom['4th']=3
174wom['5th']=4
175wom['first']=0
176wom['second']=1
177wom['third']=2
178wom['fourth']=3
179wom['fifth']=4
180
181
182scheduleKeywords = ('at', 'on', 'wday', 'month', 'wom', 'mday', 'woy',
183 'hourly','daily','weekly','monthly')
184
185periodKeywords = ('hourly','daily','weekly','monthly')
186
187weekdays = ('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat')
188ordinals = ('1st', '2nd', '3rd', '4th', '5th')
189months = ('jan', 'feb', 'mar', 'apr', 'may', 'jun',
190 'jul', 'aug', 'sep', 'oct', 'nov', 'dec')
191
192overrides = ('level','pool','storage',
193 'spoolsize','spooldata','messages',
194 'fullpool','incrementalpool','differentialpool',
195 'writepartafterjob', 'priority')
196
197bool_overrides = ('spooldata','writepartafterjob')
198
199
200
201
202
203class RunEntry:
204 def __init__(self,inputString=""):
205 self.inputString = inputString
206 self.sched = {}
207 self.hour = 0
208 self.minute = 0
209 self.value = ''
210 self.runTimes = []
211 for kw in scheduleKeywords:
212 self.sched[str(kw)] = set([])
213
214 self.overrides = {}
215 for ov in overrides:
216 self.overrides[ov] = None
217
218 #for pk in periodKeywords:
219 # self.sched[pk] = True
220
221 if type(self.inputString).__name__ == 'str' and len(self.inputString) > 0:
222 self.parseString()
223 elif self.inputString == None:
224 self.value=None
225 else:
226 self.sched['at'] = set(['0'])
227 self.sched['on'] = set(['0'])
228 # disabled for profiling
229 #self.calcStarttimes()
230
231 def setValue(self,string):
232 if type(string).__name__ == 'str' or type(string).__name__ == 'unicode':
233 self.inputString = string
234 if len(self.inputString) > 0:
235 self.parseString( )
236 elif string is None:
237 self.value = None
238 else:
239 print("setValue called with wrong param type:%s") % type(string)
240 self.inputString = ''
241
242 #self.calcStarttimes()
243
244
245 def runsAtDateTime(self,dt):
246 '''
247 check if this runentry will run at given datetime
248 '''
249 wom = (dt.day-1)/7
250 (year,weeknumber,weekday) = dt.isocalendar()
251 #tm_wom(dt.day,(dt.weekday()+8)%7)
252 #print wom
253
254 #if runentry.minute != dt.minute:
255 #print "minute yes: %s == %s" % (runentry.minute, dt.minute)
256 #else:
257 #print "minute no: %s != %s" % (runentry.minute, dt.minute)
258 # return False
259 #print len(runentry.sched['hourly'])
260 if self.hour != dt.hour and len(self.sched['hourly']) == 0:
261 #print "hour yes: %s == %s" % (runentry.hour, dt.hour)
262 #else:
263 #print "hour no: %s != %s" % (runentry.hour, dt.hour)
264 return False
265
266 #if len(runentry.sched['wom']) > 0:
267 if ( len(self.sched['wom']) != 0
268 and wom not in self.sched['wom']):
269 #print "WOM_YES %s %s" % (wom,runentry.sched['wom'])
270 #else:
271 #print "WOM_NO %s %s" % (wom,runentry.sched['wom'])
272 return False
273
274 if (len(self.sched['woy']) != 0
275 and weeknumber not in self.sched['woy']):
276 # print "WOY_YES %s %s" % (weeknumber,runentry.sched['woy'])
277 #else:
278 #print "WOY_NO %s %s" % (weeknumber,runentry.sched['woy'])
279 return False
280 #else:
281 # print "WOY_YES %s %s" % (weeknumber,runentry.sched['woy'])
282
283 if (len(self.sched['mday']) != 0
284 and dt.day not in self.sched['mday']):
285 #print "MDAY_YES %s %s" % (dt.day,runentry.sched['mday'])
286 #else:
287 #print "MDAY_NO %s %s" % (dt.day,runentry.sched['mday'])
288 return False
289
290 if (len(self.sched['month']) != 0 # month is configured
291 and dt.month-1 not in self.sched['month'] # actual month is not given
292 ):
293 #print "MONTH_YES %s %s" % (dt.month,runentry.sched['month'])
294 #else:
295 #print "MONTH_NO %s %s" % (dt.month,runentry.sched['month'])
296 return False
297
298 if (len(self.sched['wday']) != 0
299 and (dt.weekday()+1)%7 not in self.sched['wday']):
300 #print "WEEKDAY_YES %s %s" % (dt.weekday(),runentry.sched['wday'])
301 #else:
302 #print "WEEKDAY_NO %s %s" % ((dt.weekday()+1)%7,runentry.sched['wday'])
303 return False
304
305 return True
306
307
308 def calcStarttimes(self):
309 '''
310 calculates the starting times
311 of this run entry for the next year
312 '''
313 self.runTimes=[]
314 timestart= (int(time.time())/60*60)
315 timeend = timestart + 86400*366 #TODO: echte Zeitdifferenze in 1 Jahr nehmen
316
317 ts = timestart
318 while ts < timeend:
319 ts += 3600 # check every hour
320 #ts += 86400
321 #print datetime.datetime.fromtimestamp(ts)#, runsAtDateTime(datetime.datetime.fromtimestamp(ts))
322 dt = datetime.datetime.fromtimestamp(ts)
323 if self.runsAtDateTime(dt):
324 dt = dt.replace(minute=self.minute)
325 #print dt, dt.weekday()
326 self.runTimes.append(dt)
327 #print dt
328
329
330 def findRanges(self,list):
331 if len(list) == 0:
332 return set()
333 ranges = []
334 #inrange = False
335 rangeentry = [None,None]
336 for number in sorted(list):
337 #print number,
338 if rangeentry[1] != number - 1:
339 if rangeentry != [None,None]: # do not append initial rageentry
340 ranges.append(rangeentry)
341 rangeentry=[number,number] # start new rangeentry
342 #print "startet new rangeentry:" + str(rangeentry)
343 else:
344 rangeentry[1]=number # inside range
345
346 ranges.append(rangeentry) # append last range
347 return ranges
348
349 def __str__(self):
350 s = '' # outputstring
351 # print overrides
352 for ov,val in self.overrides.iteritems():
353 if val != None:
354 s += "%s=%s " %(ov,val)
355
356
357 if len(self.sched['on']):
358 s += 'on '
359
360 # month
361 outlist = []
362 for m in self.findRanges(self.sched['month']):
363 if m[0]==m[1]: # range start == range stop
364 outlist.append(months[m[0]])
365 else:
366 outlist.append("%s-%s" % (months[m[0]],months[m[1]]) )
367 s += ','.join(outlist)
368 s += ' '
369
370 # monthday
371 outlist = []
372 for m in self.findRanges(self.sched['mday']):
373 if m[0]==m[1]: # range start == range stop
374 outlist.append(str(m[0]))
375 else:
376 outlist.append("%s-%s" % (str(m[0]),str(m[1])) )
377 #outlist.append(str(m))
378 s += ','.join(outlist)
379 s += ' '
380
381 # weekofmonth
382 outlist = []
383 for m in self.findRanges(self.sched['wom']):
384 if m[0]==m[1]: # range start == range stop
385 outlist.append(ordinals[m[0]])
386 else:
387 outlist.append("%s-%s" % (ordinals[m[0]],ordinals[m[1]]) )
388 #outlist.append(ordinals[m])
389 s += ','.join(outlist)
390 s += ' '
391
392 # weekday
393 outlist = []
394 for m in self.findRanges((self.sched['wday'])):
395 if m[0]==m[1]: # range start == range stop
396 outlist.append(weekdays[m[0]])
397 else:
398 outlist.append("%s-%s" % (weekdays[m[0]],weekdays[m[1]]) )
399
400 #outlist.append(weekdays[m])
401 s += ','.join(outlist)
402 s += ' '
403
404 # yearweek
405 outlist = []
406 for m in self.findRanges((self.sched['woy'])):
407 if m[0]==m[1]: # range start == range stop
408 outlist.append("w%02d" % (m[0]))
409 else:
410 outlist.append("%s-%s" % ("w%02d" % (m[0]),"w%02d" % (m[1])) )
411 #outlist.append("w%02d" % (m))
412 s += ','.join(outlist)
413 s += ' '
414
415 # hourly daily weekly monthly
416 for kw in periodKeywords:
417 if self.sched[kw]:
418 s += kw + ' '
419 # at keyword
420 if len(self.sched ['at']):
421 s += 'at '
422 # here TIME
423 if self.value is not None:
424 s += "%02d:%02d" %(self.hour, self.minute)
425 return s
426
427 def parseString(self):
428 # extract overrides
429 if len(self.inputString) == 0:
430 print "inputString has zero length!"
431 return
432 #print "parsestring called with string>" + self.inputString +"<"
433 for ov in RXP_OVERRIDE.finditer(self.inputString):
434 #print "Found override:" + ov.group('name') +'='+ ov.group('value')
435 #print "stripped override:" + ov.group('name').strip().lower() +'='+ ov.group('value').strip()
436 self.overrides[ov.group('name').strip().lower()] = ov.group('value').strip()
437 tmpstring = self.inputString.replace(ov.group('name')+'='+ov.group('value'),'') # remove found values from string
438 self.inputString = tmpstring
439
440 self.inputString = self.inputString.lower()
441 #print self.inputString
442
443 # extract time
444 timeindex = self.inputString.rfind(':')
445 time = self.inputString[timeindex-2:timeindex+3]
446 self.minute = int(time[-2:])
447 self.hour = int(time[:2])
448 #print time
449 tmpstring = self.inputString.replace(time,'') # remove time from string
450 self.inputString = tmpstring
451 #print self.inputString
452
453 rng = {} # range for keywordclasses
454
455 #for keywordclass, keywords in sorted(keywords.iteritems(), reverse = True):
456 #for keywordclass, kws in keywords.iteritems(): # this is not possible, we need the right order
457 for keywordclass in ['hourly','daily','weekly','monthly', # first, may collide with mon(day)
458 'woy', # may collide with mday
459 'wday',# may collide with mday (1)st
460 'at', 'on', 'month', 'wom', 'mday'
461 ]:
462 kws = keywords[keywordclass]
463 rng['start'] = None
464 rng['end'] = None
465 #print "-----"+ keywordclass, kws
466 # for keyword,value in sorted(kws.iteritems()):
467
468 for keyword,value in sorted(kws.items(), key = operator.itemgetter(1), reverse = True):
469
470 # check for range
471 rangestart = self.inputString.find(keyword + '-')
472 if rangestart != -1:
473 #print "rangestart found:" + keyword + ": -> " + str(value)
474 rng['start'] = value
475
476 endrange = self.inputString.find('-' + keyword)
477 if endrange != -1:
478 #print "rangeend found:" + keyword + ": -> " + str(value)
479 rng['end'] = value
480 #print keyword,value
481
482 kwindex = self.inputString.find(keyword)
483
484 if kwindex != -1:
485 # cut out found string, so it can not match next keywords....
486 tmpstring = self.inputString.replace(keyword,'')
487 self.inputString = tmpstring
488 #print self.inputString
489 if ( (rng['start'] is None) or (rng['end'] is None) ):
490 #print "found " + keywordclass + ": -> " + str(value)
491 #print self.sched[keywordclass]
492 self.sched[keywordclass].add(value)
493
494 #print self.inputString
495
496 # do we have a range?
497 if (rng['start'] is not None) and (rng['end'] is not None):
498 for i in range(rng['start'],rng['end']):
499 #print "setting %d because of range" % i
500 self.sched[keywordclass].add(i)
501
502
503
504# 1.: downcase string
505# 2.: find strings: jan-dec, w00-w53, mon-sun, 1-31, 1st-5th| first-fifth, overrides:
506if __name__ == "__main__":
507# inputstring = 'on Jul,Aug,Oct,sep 1,2,3,4,10,16 first,2nd,3rd,fifth Tue,Wed w23,w26,w28,w37 monthly at 03:00'
508# print inputstring
509# schedule = Schedule(inputstring)
510# print schedule
511
512 inputstring = 'Level = Full Pool=FileStoragePool Storage=FileStorage on sat at 23:10'
513# print inputstring
514# schedule = Schedule(inputstring)
515# print schedule
516
517 #inputstring = 'Level = Full hourly SpoolData=yes Pool=Export-Server-Pool Storage=TandbergT40 w01-w19 1-12 jan-nov 1st-3rd mon-sat at 0:01'
518 print inputstring
519 schedule = RunEntry(inputstring)
520 #print schedule, schedule.value,
521 print len(schedule.runTimes), schedule.runTimes
522
523 #dt = datetime.datetime.now()
524 #print schedule.runsAtDateTime(dt)#,schedule.runTimes
525
526 #schedule = RunEntry(None)
527 #print schedule, schedule.value
528
529
530
Note: See TracBrowser for help on using the repository browser.