#!/usr/bin/env python # -*- coding: utf-8 -*- # $Id: scheduleparser.py 12290 2011-02-10 14:02:38Z pstorz $ import re import operator import datetime,time # parser for schedules # 1.: downcase string # 2.: find strings: jan-dec, w00-w53, mon-sun, 1-31, 1st-5th| first-fifth, overrides: # Keywords (from dird/run_conf.c # TODO: put this in central c2xxx file keyword_definition_string = """ static struct s_keyw keyw[] = { {NT_("on"), s_none, 0}, {NT_("at"), s_at, 0}, {NT_("sun"), s_wday, 0}, {NT_("mon"), s_wday, 1}, {NT_("tue"), s_wday, 2}, {NT_("wed"), s_wday, 3}, {NT_("thu"), s_wday, 4}, {NT_("fri"), s_wday, 5}, {NT_("sat"), s_wday, 6}, {NT_("jan"), s_month, 0}, {NT_("feb"), s_month, 1}, {NT_("mar"), s_month, 2}, {NT_("apr"), s_month, 3}, {NT_("may"), s_month, 4}, {NT_("jun"), s_month, 5}, {NT_("jul"), s_month, 6}, {NT_("aug"), s_month, 7}, {NT_("sep"), s_month, 8}, {NT_("oct"), s_month, 9}, {NT_("nov"), s_month, 10}, {NT_("dec"), s_month, 11}, {NT_("sunday"), s_wday, 0}, {NT_("monday"), s_wday, 1}, {NT_("tuesday"), s_wday, 2}, {NT_("wednesday"), s_wday, 3}, {NT_("thursday"), s_wday, 4}, {NT_("friday"), s_wday, 5}, {NT_("saturday"), s_wday, 6}, {NT_("january"), s_month, 0}, {NT_("february"), s_month, 1}, {NT_("march"), s_month, 2}, {NT_("april"), s_month, 3}, {NT_("june"), s_month, 5}, {NT_("july"), s_month, 6}, {NT_("august"), s_month, 7}, {NT_("september"), s_month, 8}, {NT_("october"), s_month, 9}, {NT_("november"), s_month, 10}, {NT_("december"), s_month, 11}, {NT_("daily"), s_daily, 0}, {NT_("weekly"), s_weekly, 0}, {NT_("monthly"), s_monthly, 0}, {NT_("hourly"), s_hourly, 0}, {NT_("1st"), s_wom, 0}, {NT_("2nd"), s_wom, 1}, {NT_("3rd"), s_wom, 2}, {NT_("4th"), s_wom, 3}, {NT_("5th"), s_wom, 4}, {NT_("first"), s_wom, 0}, {NT_("second"), s_wom, 1}, {NT_("third"), s_wom, 2}, {NT_("fourth"), s_wom, 3}, {NT_("fifth"), s_wom, 4}, {NULL, s_none, 0} }; """ #RXP_KEYWORDDEF = re.compile('^\s+\{NT_\("(?P[\w|\d]+"\)).*') RXP_KEYWORDDEF = re.compile('\s+{NT_\("(.*)"\),.*s_(\w+),.*(\d+)},\n') #RXP_KEYWORDDEF = re.compile('.*') RXP_OVERRIDE = re.compile('(?P\S+\s*)=(?P\s*\S+)') #RXP_OVERRIDE = re.compile('(?P\w+)\s*=\s*(?P\w+)') on= {} at = {} wday = {} month = {} daily = {} weekly = {} monthly = {} hourly = {} wom = {} mday = {} for i in range(31): mday[str(i+1)] = i+1 woy = {} for i in range(54): index = "w%02d" % (i) woy[index] = i keywords = {} keywords['on'] = on keywords['at'] = at keywords['wday'] = wday keywords['month'] = month keywords['daily'] = daily keywords['weekly'] = weekly keywords['hourly'] = hourly keywords['monthly'] = monthly keywords['wom'] = wom keywords['mday'] = mday keywords['woy'] = woy #for kwdef in RXP_KEYWORDDEF.finditer(keyword_definition_string): #print kwdef.group(1), kwdef.group(2),kwdef.group(3) #if kwdef.group(2) == 'none': # continue # evalstring = "%s['%s']=%s" % (kwdef.group(2), kwdef.group(1),kwdef.group(3)) # print evalstring on['on']=0 at['at']=0 wday['sun']=0 wday['mon']=1 wday['tue']=2 wday['wed']=3 wday['thu']=4 wday['fri']=5 wday['sat']=6 month['jan']=0 month['feb']=1 month['mar']=2 month['apr']=3 month['may']=4 month['jun']=5 month['jul']=6 month['aug']=7 month['sep']=8 month['oct']=9 month['nov']=10 month['dec']=11 wday['sunday']=0 wday['monday']=1 wday['tuesday']=2 wday['wednesday']=3 wday['thursday']=4 wday['friday']=5 wday['saturday']=6 month['january']=0 month['february']=1 month['march']=2 month['april']=3 month['june']=5 month['july']=6 month['august']=7 month['september']=8 month['october']=9 month['november']=10 month['december']=11 daily['daily']=0 weekly['weekly']=0 monthly['monthly']=0 hourly['hourly']=0 wom['1st']=0 wom['2nd']=1 wom['3rd']=2 wom['4th']=3 wom['5th']=4 wom['first']=0 wom['second']=1 wom['third']=2 wom['fourth']=3 wom['fifth']=4 scheduleKeywords = ('at', 'on', 'wday', 'month', 'wom', 'mday', 'woy', 'hourly','daily','weekly','monthly') periodKeywords = ('hourly','daily','weekly','monthly') weekdays = ('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat') ordinals = ('1st', '2nd', '3rd', '4th', '5th') months = ('jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec') overrides = ('level','pool','storage', 'spoolsize','spooldata','messages', 'fullpool','incrementalpool','differentialpool', 'writepartafterjob', 'priority') bool_overrides = ('spooldata','writepartafterjob') class RunEntry: def __init__(self,inputString=""): self.inputString = inputString self.sched = {} self.hour = 0 self.minute = 0 self.value = '' self.runTimes = [] for kw in scheduleKeywords: self.sched[str(kw)] = set([]) self.overrides = {} for ov in overrides: self.overrides[ov] = None #for pk in periodKeywords: # self.sched[pk] = True if type(self.inputString).__name__ == 'str' and len(self.inputString) > 0: self.parseString() elif self.inputString == None: self.value=None else: self.sched['at'] = set(['0']) self.sched['on'] = set(['0']) # disabled for profiling #self.calcStarttimes() def setValue(self,string): if type(string).__name__ == 'str' or type(string).__name__ == 'unicode': self.inputString = string if len(self.inputString) > 0: self.parseString( ) elif string is None: self.value = None else: print("setValue called with wrong param type:%s") % type(string) self.inputString = '' #self.calcStarttimes() def runsAtDateTime(self,dt): ''' check if this runentry will run at given datetime ''' wom = (dt.day-1)/7 (year,weeknumber,weekday) = dt.isocalendar() #tm_wom(dt.day,(dt.weekday()+8)%7) #print wom #if runentry.minute != dt.minute: #print "minute yes: %s == %s" % (runentry.minute, dt.minute) #else: #print "minute no: %s != %s" % (runentry.minute, dt.minute) # return False #print len(runentry.sched['hourly']) if self.hour != dt.hour and len(self.sched['hourly']) == 0: #print "hour yes: %s == %s" % (runentry.hour, dt.hour) #else: #print "hour no: %s != %s" % (runentry.hour, dt.hour) return False #if len(runentry.sched['wom']) > 0: if ( len(self.sched['wom']) != 0 and wom not in self.sched['wom']): #print "WOM_YES %s %s" % (wom,runentry.sched['wom']) #else: #print "WOM_NO %s %s" % (wom,runentry.sched['wom']) return False if (len(self.sched['woy']) != 0 and weeknumber not in self.sched['woy']): # print "WOY_YES %s %s" % (weeknumber,runentry.sched['woy']) #else: #print "WOY_NO %s %s" % (weeknumber,runentry.sched['woy']) return False #else: # print "WOY_YES %s %s" % (weeknumber,runentry.sched['woy']) if (len(self.sched['mday']) != 0 and dt.day not in self.sched['mday']): #print "MDAY_YES %s %s" % (dt.day,runentry.sched['mday']) #else: #print "MDAY_NO %s %s" % (dt.day,runentry.sched['mday']) return False if (len(self.sched['month']) != 0 # month is configured and dt.month-1 not in self.sched['month'] # actual month is not given ): #print "MONTH_YES %s %s" % (dt.month,runentry.sched['month']) #else: #print "MONTH_NO %s %s" % (dt.month,runentry.sched['month']) return False if (len(self.sched['wday']) != 0 and (dt.weekday()+1)%7 not in self.sched['wday']): #print "WEEKDAY_YES %s %s" % (dt.weekday(),runentry.sched['wday']) #else: #print "WEEKDAY_NO %s %s" % ((dt.weekday()+1)%7,runentry.sched['wday']) return False return True def calcStarttimes(self): ''' calculates the starting times of this run entry for the next year ''' self.runTimes=[] timestart= (int(time.time())/60*60) timeend = timestart + 86400*366 #TODO: echte Zeitdifferenze in 1 Jahr nehmen ts = timestart while ts < timeend: ts += 3600 # check every hour #ts += 86400 #print datetime.datetime.fromtimestamp(ts)#, runsAtDateTime(datetime.datetime.fromtimestamp(ts)) dt = datetime.datetime.fromtimestamp(ts) if self.runsAtDateTime(dt): dt = dt.replace(minute=self.minute) #print dt, dt.weekday() self.runTimes.append(dt) #print dt def findRanges(self,list): if len(list) == 0: return set() ranges = [] #inrange = False rangeentry = [None,None] for number in sorted(list): #print number, if rangeentry[1] != number - 1: if rangeentry != [None,None]: # do not append initial rageentry ranges.append(rangeentry) rangeentry=[number,number] # start new rangeentry #print "startet new rangeentry:" + str(rangeentry) else: rangeentry[1]=number # inside range ranges.append(rangeentry) # append last range return ranges def __str__(self): s = '' # outputstring # print overrides for ov,val in self.overrides.iteritems(): if val != None: s += "%s=%s " %(ov,val) if len(self.sched['on']): s += 'on ' # month outlist = [] for m in self.findRanges(self.sched['month']): if m[0]==m[1]: # range start == range stop outlist.append(months[m[0]]) else: outlist.append("%s-%s" % (months[m[0]],months[m[1]]) ) s += ','.join(outlist) s += ' ' # monthday outlist = [] for m in self.findRanges(self.sched['mday']): if m[0]==m[1]: # range start == range stop outlist.append(str(m[0])) else: outlist.append("%s-%s" % (str(m[0]),str(m[1])) ) #outlist.append(str(m)) s += ','.join(outlist) s += ' ' # weekofmonth outlist = [] for m in self.findRanges(self.sched['wom']): if m[0]==m[1]: # range start == range stop outlist.append(ordinals[m[0]]) else: outlist.append("%s-%s" % (ordinals[m[0]],ordinals[m[1]]) ) #outlist.append(ordinals[m]) s += ','.join(outlist) s += ' ' # weekday outlist = [] for m in self.findRanges((self.sched['wday'])): if m[0]==m[1]: # range start == range stop outlist.append(weekdays[m[0]]) else: outlist.append("%s-%s" % (weekdays[m[0]],weekdays[m[1]]) ) #outlist.append(weekdays[m]) s += ','.join(outlist) s += ' ' # yearweek outlist = [] for m in self.findRanges((self.sched['woy'])): if m[0]==m[1]: # range start == range stop outlist.append("w%02d" % (m[0])) else: outlist.append("%s-%s" % ("w%02d" % (m[0]),"w%02d" % (m[1])) ) #outlist.append("w%02d" % (m)) s += ','.join(outlist) s += ' ' # hourly daily weekly monthly for kw in periodKeywords: if self.sched[kw]: s += kw + ' ' # at keyword if len(self.sched ['at']): s += 'at ' # here TIME if self.value is not None: s += "%02d:%02d" %(self.hour, self.minute) return s def parseString(self): # extract overrides if len(self.inputString) == 0: print "inputString has zero length!" return #print "parsestring called with string>" + self.inputString +"<" for ov in RXP_OVERRIDE.finditer(self.inputString): #print "Found override:" + ov.group('name') +'='+ ov.group('value') #print "stripped override:" + ov.group('name').strip().lower() +'='+ ov.group('value').strip() self.overrides[ov.group('name').strip().lower()] = ov.group('value').strip() tmpstring = self.inputString.replace(ov.group('name')+'='+ov.group('value'),'') # remove found values from string self.inputString = tmpstring self.inputString = self.inputString.lower() #print self.inputString # extract time timeindex = self.inputString.rfind(':') time = self.inputString[timeindex-2:timeindex+3] self.minute = int(time[-2:]) self.hour = int(time[:2]) #print time tmpstring = self.inputString.replace(time,'') # remove time from string self.inputString = tmpstring #print self.inputString rng = {} # range for keywordclasses #for keywordclass, keywords in sorted(keywords.iteritems(), reverse = True): #for keywordclass, kws in keywords.iteritems(): # this is not possible, we need the right order for keywordclass in ['hourly','daily','weekly','monthly', # first, may collide with mon(day) 'woy', # may collide with mday 'wday',# may collide with mday (1)st 'at', 'on', 'month', 'wom', 'mday' ]: kws = keywords[keywordclass] rng['start'] = None rng['end'] = None #print "-----"+ keywordclass, kws # for keyword,value in sorted(kws.iteritems()): for keyword,value in sorted(kws.items(), key = operator.itemgetter(1), reverse = True): # check for range rangestart = self.inputString.find(keyword + '-') if rangestart != -1: #print "rangestart found:" + keyword + ": -> " + str(value) rng['start'] = value endrange = self.inputString.find('-' + keyword) if endrange != -1: #print "rangeend found:" + keyword + ": -> " + str(value) rng['end'] = value #print keyword,value kwindex = self.inputString.find(keyword) if kwindex != -1: # cut out found string, so it can not match next keywords.... tmpstring = self.inputString.replace(keyword,'') self.inputString = tmpstring #print self.inputString if ( (rng['start'] is None) or (rng['end'] is None) ): #print "found " + keywordclass + ": -> " + str(value) #print self.sched[keywordclass] self.sched[keywordclass].add(value) #print self.inputString # do we have a range? if (rng['start'] is not None) and (rng['end'] is not None): for i in range(rng['start'],rng['end']): #print "setting %d because of range" % i self.sched[keywordclass].add(i) # 1.: downcase string # 2.: find strings: jan-dec, w00-w53, mon-sun, 1-31, 1st-5th| first-fifth, overrides: if __name__ == "__main__": # 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' # print inputstring # schedule = Schedule(inputstring) # print schedule inputstring = 'Level = Full Pool=FileStoragePool Storage=FileStorage on sat at 23:10' # print inputstring # schedule = Schedule(inputstring) # print schedule #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' print inputstring schedule = RunEntry(inputstring) #print schedule, schedule.value, print len(schedule.runTimes), schedule.runTimes #dt = datetime.datetime.now() #print schedule.runsAtDateTime(dt)#,schedule.runTimes #schedule = RunEntry(None) #print schedule, schedule.value