# -*- coding: utf-8 -*- # $Id: config_classes.py 12290 2011-02-10 14:02:38Z pstorz $ import re import logging import copy logger = logging.getLogger("directive") import scheduleparser # schedule class from auto_types import * from prettynames import * RXP_SIMPLE_ITEM = re.compile(r''' (^|;)(\s*(?P[\w ]+?)?\s*=\s*(?P"?[^;^\n]+"?)(?P\s+\#.*)?) # xxx = yyy # comment ''',re.M| re.X) #(^|;)(\s*(?P[\w ]+?)?\s*=\s*(?P"?[^{^}^;^\n]+"?)(?P\s+\#.*)?) # xxx = yyy # comment #(^|;)(\s*(?P[\w ]+?)?\s*=\s*(?P"?[^{^}^;^\n^\s]+"?)\s*(?P\s+\#.*)?) # xxx = yyy # comment RXP_RESTYPE = re.compile(r''' ^\s*(?P\w[\w ]+?\s*(=\s+)?\s?)$ ''', re.M|re.X|re.S) # TODO: allow spaces in resourcetypes like sd addresses! # this does not work now # SD Addresses = < RXP_NRTEXT = re.compile(r''' (?P\d+)\s?(?P[A-Za-z]+) ''', re.M|re.X|re.S) #RXP_TEXTNR = re.compile(r''' #(?P\w+)\s?(?P\d+) #''', re.M|re.X|re.S) # what resources can appear multiple times in director configs? MULTIPLE_RESOURCES_IN_DIRD = set([ 'client', 'console', 'fileset', 'job', 'jobdefs', 'messages', 'pool', 'schedule', 'storage', ]) INTERNALLY_REFERENCED_ITEMS = set(['fileset', 'client' , 'catalog', 'messages', 'schedule', 'storage', 'jobdefs', 'pool', 'incrementalbackuppool', 'fullbackuppool', 'differentialbackuppool', 'job', 'basejob', 'device', ]) # translate full name to short name res2shrtn = { 'catalog' :'cat', 'client' :'cli', 'console' :'con', 'counter' :'counter', 'director' :'dir', 'fileset' :'fs', 'filedaemon' :'cli', # filedaemon is the same as cli 'job' :'job', 'jobdefs' :'job', # jobdefs are the same as jobs 'messages' :'msgs', 'pool' :'pool', 'runscript' :'runscript', 'schedule' :'sch', 'storage' :'store', #'console' :'con', 'autochanger' :'changer', 'device' :'dev', 'options' :'options', #manually added 'diraddresses' :'addresses', 'fdaddresses' :'addresses', 'sdaddresses' :'addresses', 'ip' :'ip', 'ipv4' :'ip', 'ipv6' :'ip', 'include' : 'include', 'exclude' : 'include', } # which itemsnames can appear multiple times? MULTIPLE_ALLOWED_ITEMS_SET = set([ 'file', 'run', ]) #MULTIPLE_ALLOWED_RESOURCES_SET = set([ # 'include', # 'exclude', # 'ip', # ]) timemultiplicators = { "seconds" : 1, "minutes" : 60, "mins" : 60, "hours" : 3600, "days" : 3600*24, "weeks" : 3600*24*7, "quarters" : 3600*24*91, "years" : 3600*24*365, "months" : 60*60*24*30, "n" : 60, } datamultiplicators = { "k" : 1024, #/* kilobyte */ "kb": 1000, #/* kb kilobyte */ "m" : 1048576, #/* megabyte */ "mb": 1000000, #/* mb megabyte */ "g" : 1073741824, #/* gigabyte */ "gb": 1000000000}; #/* gb gigabyte */ RESSOURCES_WITH_EQUALSIGN = set(['diraddresses', 'fdaddresses', 'sdaddresses', 'ip', 'ipv4', 'ipv6', ]) #INDENTSTRING = '|--|' INDENTSTRING = ' ' class Item(object): def __init__(self, name, value, defaultvalue, default, type, required, parentresource = None, indentlevel = 0): self.indentlevel = indentlevel self.parentresource = parentresource self.name = name.lower().replace(' ','') self.defaultvalue = defaultvalue self.default = default self.value = value # real value lies in storage self.type = type self.required = required self.printall = False #TODO: try/except this eval #print type, defaultvalue #storage1 = eval(type + '(' + str(defaultvalue) +')' ) #self.storage = copy.deepcopy(storage1) #logger.debug('storage: ' +str(id(self.storage)) + ' storage1: ' + str(id(storage1))) self.storage = eval(type + '(' + str(defaultvalue) +')' ) self.storage.setValue(value) self.storage.indentlevel = self.indentlevel + 1 self.storage.setParentItem(self) def __lt__(self,other): #print "comparing", self.name, other.name if self.name == 'name': # name comes first #print self.name,"<",other.name return True if self.name < other.name : #print self.name,"<",other.name return True else: #print self.name,">",other.name return False def __repr__(self): string_types = set(['store_time']) if self.type in string_types: # explicitly store this in a string return "Item(\'%s\', \'%s\', \'%s\', %s, \'%s\', %s)" % (str(self.name), self.value, self.defaultvalue, self.default, str(self.type), self.required) else: return "Item(\'%s\', %s, %s, %s, \'%s\', %s)" % (str(self.name), self.value, self.defaultvalue, self.default, str(self.type), self.required) ''' find out if this item is to be printed or not ''' def print_item(self): printout = self.printall # override to print all out if self.default: if str(self.storage.value) != str(self.defaultvalue): logger.debug( "printing " + self.name + " as self.storage.value != self.defaultvalue:" + str(self.storage.value) + ' != ' + str(self.defaultvalue) + '<' ) printout = True else: #pass logger.debug( "NOT printing " + self.name + " as self.storage.value == self.defaultvalue:" + str(self.storage.value) + ' == ' + str(self.defaultvalue) + '<' ) elif self.required: # jobs can inherit their items from jobdefs, jobdefs do not need a client # but a name is needed anyway # TODO: merge jobs and jobdefs in order to check if they are complete after merge # jobs and jobdefs if self.parentresource.resourcetype.startswith('job') and self.value == self.defaultvalue and str(self.storage.value) == str(self.defaultvalue): printout = False else: logger.debug( "printing " + self.name + " as self.required is set") printout = True elif self.storage.value is not None: if self.storage.value != self.defaultvalue: logger.debug( "printing " + self.name + " as self.storage.value is set to :>" + str( self.storage.value )+'<') printout = True return printout def __str__(self): ''' standard behaviour: only print items that are configured and have not a different value than the default value ''' if self.print_item(): return '%s%s = %s\n' % ( self.indentlevel * INDENTSTRING, prettyName(self.name), self.storage) #return ' %s = %s\n' % (prettyName(self.name), self.storage) else: return '' def setValue(self, value): '''set value item''' self.storage.setValue(value) #class command(object): # ''' # this class contains the information to a console command, also dot commands # ''' # def __init__(self, commandtext, commandname, usage, help, inrunscript): # self.commandtext = commandtext # self.commandname = commandname # self.usage = usage # self.help = help # self.inrunscript = inrunscript # # def __repr__(self): # return "command('%s','%s','%s','%s','%s')" % (self.commandtext, self.commandname, self.usage, self.help, self.inrunscript) # the bacula internal storage representations # # base classes for all storages class Item_Storage(object): """ base class for the representation of any item object """ def __init__(self,val): self.value = val self.comment = '' self.possiblevalues = [] # what values are possible here ? self.parentitem = None def __str__(self): #TODO do this in a nicer way without a catch-all try-except if self.comment != '': comment = "\t# " + self.comment else: comment = '' try: #return self.value return self.value + comment #+self.__class__.__name__ + ' id: '+ str(id(self)) except: #return str(self.value) return unicode(self.value) + comment #+self.__class__.__name__ + ' id: '+ str(id(self)) def setParentItem(self,parentItem): self.parentitem = parentItem def setValue(self,value): logger.debug( self.__class__.__name__ +':' + 'called self.value with \''+ str(value) + '\'') self.value = value logger.debug("self.value is now" + str(self) ) class Item_Storage_String (Item_Storage): pass #def __str__(self): # return str(self.value) #return '"' + str(self.value) + '"'# + '\t# '+ self.__class__.__name__ # generated class store_pint32( Item_Storage ): def __str__(self): return str(self.value) #+ '\t# '+self.__class__.__name__ + 'id: '+ str(id(self)) #return str(self.value) + '\t# '+self.__class__.__name__ + 'id: '+ str(id(self)) class store_name( Item_Storage_String ): def setValue(self,value): logger.debug( self.__class__.__name__ +':' + 'called self.value with \''+ unicode(value) + '\'') if value != None and value != 0: if type(value) == str: value = value.strip('"') else: value = str(value) self.value = '"' + value.strip('"') + '"' else: self.value = value logger.debug("self.value is now " + unicode(self) ) class store_str( store_name ): pass class store_time( Item_Storage ): def __init__(self,val): self.value = val self.comment = '' self.possiblevalues = [] # what values are possible here ? self.parentitem = None def __str__(self): #TODO do this in a nicer way without a catch-all try-except if self.value is None: return str(self.value) outstring = '' for mult in ["years", "quarters" ,"months" ,"weeks", "days","hours","minutes","seconds"]: if self.value % timemultiplicators[mult] == 0: outstring = "%d %s " % (self.value/timemultiplicators[mult], mult) return outstring return outstring def setParentItem(self,parentItem): self.parentitem = parentItem def setValue(self,value): logger.debug( self.__class__.__name__ +':' + 'called self.value with \''+ str(value) + '\'') self.value = self.__parse_timeinput__(value) logger.debug("self.value is now" + str(self) ) def __partNumberKeywords__(self,string): ''' inserts a space between numbers and keywords if there is none ''' return re.sub(r'([0-9](?=[A-Za-z])|[A-Za-z](?=[0-9]))', "\\1 ", string) #s = '' #for match in RXP_NRTEXT.finditer(string): # #print string,match.groups() # s += ' '.join(match.groups()) + ' ' #return s def __parse_timeinput__(self,timestring): ''' parses the input string and sets the value to the resulting seconds ''' timeval = 0 if timestring is None or timestring == 'None': return None multiplicator = 1 # search for x*x*X*X... index = timestring.find('*') if index != -1: timeval = eval(timestring) #print timeval return timeval #timestring = str(timeval) if timestring.isdigit(): return int(timestring) # search for multiple pairs like "3 seconds 4 days" timestring = self.__partNumberKeywords__(timestring) valueUnitPairs = timestring.split() valueUnitPairs.reverse() #print valueUnitPairs #print timestring+'<' if len(valueUnitPairs)%2: logger.error(valueUnitPairs) logger.error("There should always be a pair of value-unit\n maybe you forgot a space between value and unit?") while len(valueUnitPairs) > 1: vustr = str(valueUnitPairs.pop()) + ' ' + str(valueUnitPairs.pop()) for cut in (range(8)): if cut == 0: # extra, as range -0 leads to nothing instead of giving the whole string cut = -8 for mult in ('months','seconds', 'mins','minutes','hours', 'days','weeks','quarters', 'years'): #,'n' index = vustr.lower().find(mult[:-cut]) #print mult[:-cut],vustr if index != -1 and len(mult[:-cut])>0: multiplicator = timemultiplicators[mult] tmpstring = vustr.replace(mult[:-cut],'') #print mult[:-cut],vustr vustr = tmpstring timeval += float(vustr) * multiplicator #print "%s -> %d " % (vustr,timeval) return int(timeval) class store_bool( Item_Storage ): #pass ''' also base class for store_bit ''' def __init__(self,val): Item_Storage.__init__(self, val) self.setValue(val) def setValue(self,value): logger.debug( self.__class__.__name__ +':' + 'called self.value with \''+ str(value) + '\' type: ' + str(type(value))) if type(value) is str: logger.debug('value was string') if value.lower() == 'yes' or value.lower() == '1': self.value = True else: self.value = False elif type(value) is int: # type int logger.debug('value was int') if value == 1: self.value = True else: self.value = False elif type(value) is bool: # type int logger.debug('value was bool') self.value = value else: logger.error( "value must be or , but is " + str(type(value)) ) logger.debug('value is now ' + str(self.value)) def __str__(self): if self.comment != '': comment = "\t# " + self.comment else: comment = '' if self.value == True: #return 'Yes'#+ '\t# '+self.__class__.__name__ + 'id: '+ str(id(self)) return 'Yes' + comment #+self.__class__.__name__ + 'id: '+ str(id(self)) else: #return 'No' #+ '\t# '+self.__class__.__name__ + 'id: '+ str(id(self)) return 'No' + comment #+self.__class__.__name__ + 'id: '+ str(id(self)) class store_bit( store_bool ): pass class store_dir( store_name ): pass class store_alist_str( Item_Storage_String ): pass class store_password( store_name ): pass class store_res( Item_Storage_String ): pass class store_acl( Item_Storage_String ): def __init__(self,val): self.value = val self.comment = '' self.possiblevalues = [] # what values are possible here ? self.parentitem = None self.ACLDict = {} def __str__(self): return str(self.value) def setParentItem(self,parentItem): self.parentitem = parentItem def setValue(self,value): logger.debug( self.__class__.__name__ +':' + 'called self.setvalue with \''+ str(value) + '\'') self.value = value self.__Value2ACLDict__() self.__ACLDict2value__() #self.__parse_ACLinput__(value) #self.ACLDict2value() logger.debug("self.value is now >%s<" %( self.value )) def __Value2ACLDict__(self): ''' parses the input string and sets the ACL Dictionary ''' #print "parsing the following value: %s " % (self.value) if self.value is None or self.value == 'None' or self.value == u'' or self.value == '': for k in self.ACLDict.keys(): self.ACLDict[k]= False self.value = None else: for acl in self.value.replace(' ','').split(','): self.ACLDict[acl]=True self.__ACLDict2value__() #print self.ACLDict def __ACLDict2value__(self): ''' reads the acl Dictionary and produces the correct output in the value field ''' s = '' for msg,val in self.ACLDict.iteritems(): if val == True: s += '%s,' % (msg) #self.setValue(s[:-1]) #self.value = s[:-1] if (len(s[:-1])) == 0: self.value = None else: self.value = s[:-1]# cut last comma class store_int32( Item_Storage ): pass class store_addresses_port( Item_Storage ): pass class store_addresses( Item_Storage ): pass class store_addresses_address( Item_Storage ): pass class store_inc( Item_Storage ): pass class store_migtype( Item_Storage ): def __init__(self,val): Item_Storage.__init__(self, val) self.possiblevalues = migtypes if val == 0: Item_Storage.setValue(self, 'smallestvolume') class store_short_runscript( Item_Storage ): pass class store_runscript( Item_Storage ): pass class store_alist_res( Item_Storage_String ): pass class store_jobtype( Item_Storage ): def __init__(self,val): Item_Storage.__init__(self, val) self.possiblevalues = jobtypes if val == 0: Item_Storage.setValue(self, 'backup') class store_replace( Item_Storage ): def __init__(self,val): Item_Storage.__init__(self, val) self.possiblevalues = ReplaceOptions if val == 0: Item_Storage.setValue(self, 'always') class store_level( Item_Storage ): def __init__(self,val): Item_Storage.__init__(self, val) self.possiblevalues = joblevels if val == 0: Item_Storage.setValue(self, 'full') class store_size64( Item_Storage ): def __init__(self,val): self.value = val self.comment = '' self.possiblevalues = [] # what values are possible here ? self.parentitem = None def __str__(self): #TODO do this in a nicer way without a catch-all try-except if self.value is None: return str(self.value) outstring = '' for mult in ["gb","mb","kb","g","m","k"]: if self.value % datamultiplicators[mult] == 0: outstring = "%d %s " % (self.value/datamultiplicators[mult], mult) return outstring return str(self.value) #outstring = '' #for mult in ["years", "quarters" ,"months" ,"weeks", "days","hours","minutes","seconds"]: # if self.value % timemultiplicators[mult] == 0: # outstring = "%d %s " % (self.value/timemultiplicators[mult], mult) # return outstring #return outstring def setParentItem(self,parentItem): self.parentitem = parentItem def setValue(self,value): logger.debug( self.__class__.__name__ +':' + 'called self.value with \''+ str(value) + '\'') self.value = self.__parse_datainput__(value) logger.debug("self.value is now" + str(self) ) def __parse_datainput__(self,datastring): ''' parses the input string and sets the value to the resulting seconds ''' if datastring is None or datastring == 'None': return None datastring = str(datastring).lower() multiplicator = 1 dstring = datastring for mult in ["gb","mb","kb","g","m","k"]: index = dstring.lower().find(mult) if index != -1: multiplicator = datamultiplicators[mult] tmpstring = datastring.replace(mult,'') #print datastring dstring = tmpstring dataval = float(dstring) * multiplicator return dataval # search for x*x*X*X... #index = timestring.find('*') #if index != -1: # timeval = eval(timestring) # print timeval # timestring = str(timeval)##### #print timestring+'<' #for mult in timemultiplicators: # for cut in (range(8)): # if cut == 0: # extra, as range -0 leads to nothing instead of giving the whole string # cut = -8 # index = timestring.find(mult[:-cut]) # if index != -1 and len(mult[:-cut])>0: # multiplicator = timemultiplicators[mult] # tmpstring = timestring.replace(mult[:-cut],'') # #print timestring # timestring = tmpstring #timeval = float(timestring) * multiplicator #return timeval class store_strname( Item_Storage_String ): pass class store_label( Item_Storage_String ): pass class store_actiononpurge( Item_Storage ): pass class store_runscript_bool( Item_Storage ): pass class store_runscript_cmd( Item_Storage ): pass class store_runscript_target( Item_Storage ): pass class store_runscript_when( Item_Storage ): pass class store_run( Item_Storage_String ): def __init__(self,val): self.value = val logger.debug('store_run: called with ' + str(val)) self.runentry = scheduleparser.RunEntry(val) self.possiblevalues = [] def setValue(self,value): self.runentry.setValue(value) # set value to None if resutling string contains only whitespace, # in order to have the default value and not print it if len(str(self.runentry).strip()) == 0: self.value = None else: self.value = str(self.runentry) def __str__(self): self.value = str(self.runentry) # update value string return str(self.runentry) class store_device( Item_Storage_String ): pass class store_maxblocksize( Item_Storage ): pass class store_devtype( Item_Storage ): pass class store_msgs( Item_Storage ): def __init__(self,val): self.value = val self.comment = '' self.possiblevalues = [] # what values are possible here ? self.parentitem = None self.messageDict = {} self.parameter = None self.setValue(val) def __str__(self): s = '' if self.parameter is not None: s += self.parameter + ' = ' for msg,val in self.messageDict.iteritems(): if msg == 'all': if self.messageDict['all']: s += 'all,' continue if self.messageDict['all']: if val == False: s += '!%s,' % (msg) else:# not all, so write all that are true if val == True: s += '%s,' % (msg) return s[:-1] # cut last comma def getParameter(self): ''' this returns the target of the messages entry ''' return self.parameter def setParameter(self, parameter): self.parameter = parameter def setParentItem(self,parentItem): self.parentitem = parentItem def setValue(self,value): logger.debug( self.__class__.__name__ +':' + 'called self.setvalue with \''+ str(value) + '\'') self.value = self.__parse_messageinput__(value) logger.debug("self.value is now" + str(self) ) def __parse_messageinput__(self,msgsstring): ''' parses the input string and sets the value ''' if msgsstring is None or msgsstring == 'None': self.parameter = None return None splitted = msgsstring.split('=') # do we have a message string with parameter if len(splitted) > 1: # (like root@localhost = all, !skipped) self.parameter = splitted[0].rstrip(' ') msgsstring = splitted[1] messagestring = msgsstring.lower() for message in msg_types: self.messageDict[message] = None index = messagestring.find('!'+message) if index != -1: logger.debug( "found NOT %s" % (message) ) self.messageDict[message] = False else: index = messagestring.find(message) if index != -1: logger.debug("found %s" % (message)) self.messageDict[message] = True return self.__str__() class store_opts( Item_Storage ): def __init__(self,val): Item_Storage.__init__(self, val) #if val == 0: # Item_Storage.setValue(self, 'full') def setParentItem(self,parentItem): self.parentitem = parentItem try: self.possiblevalues = FS_option_kw[self.parentitem.name] logger.debug(self.possiblevalues) except: logger.error('keyerror in FS_option_kw for ' + self.parentitem.name) class store_drivetype( Item_Storage ): pass class store_regex( Item_Storage ): pass class store_wild( Item_Storage ): pass class store_fstype( Item_Storage ): pass class store_base( Item_Storage ): pass class store_plugin( Item_Storage ): pass class store_excludedir( Item_Storage ): pass class store_fname( store_name ): pass class options_res( Item_Storage ): pass class store_plugin_name( Item_Storage ): pass if __name__ == "__main__": #msgsstore = store_msgs("root@localhost = mount") tmp = store_time("1 hour") #for mult in ('months','seconds','n', # 'mins','minutes','hours', # 'days','weeks','quarters', # 'years'): # for val in range(9): # value = str(val) + ' ' + mult # tmp.setValue(value) # print str(value) + '->' + str(tmp) # tmp.setValue("1month30minutes12seconds") print tmp