#!/usr/bin/env python # -*- coding: utf-8 -*- # $Id: bacresources.py 12826 2011-04-03 16:49:56Z pstorz $ import time #from directive import * #from configrules import * from auto_configrules import * import logging from auto_onlinehelp import * logger = logging.getLogger('bacresources') import os import tarfile import tempfile import glob import shutil import string import random #TODO: we have a problem if inside of a password there is a special character like {} def genPw(self,length=32, chars=string.letters + string.digits): # from http://code.activestate.com/recipes/59873-random-password-generation/ return ''.join([random.choice(chars) for i in range(length)]) def config2blocks(configstring, processEventsCallback = None): """ Parses the configstring and returns a list of blocks. Each block is represented by its start- and end characterindex saved in 'start' and 'end' the configstring is expected without comment lines """ blockdepth = 0 blockstack = [] blocks = [] instring = False # are we in a string between (") ? lastcharbackslash = False for i in range(0, len(configstring)): #if processEventsCallback is not None and not (i%100): ## #print "calling processEventsCallback" # processEventsCallback(i) chr = configstring[i] if chr == '\\': lastcharbackslash = True #print 'foundbackslash' else: lastcharbackslash = False if chr == '"' and not lastcharbackslash: # we may have a brace in a password, but can escape with \" instring = not instring # TODO:password with { or } inside this still make problems! if chr == '{' and not instring: blockdepth += 1 #print '\n ',blockdepth,' **{** ->',configstring[i-10:i+20],'<-', b = {} b['start'] = i b['level'] = blockdepth blockstack.append(b) elif chr == '}' and not instring: blockdepth -= 1 b = blockstack.pop() b['end'] = i #print '\n ', blockdepth , ' **}** ->',configstring[i-10:i+1],'<-' # find name just before the block beginning s = configstring[:b['start']] m = RXP_RESTYPE.search(s) #logger.debug( "searching for ResType in >" + s + "<" ) if m: b['namestart'] = b['start'] - len(m.group('resourcetype')) - 1 # start of resname......{ instead of { b['name'] = m.group('resourcetype').strip(' =\n').replace(' ','') # there are also resname = {xxx} #b['name'] = m.group('resourcetype').strip(' =\n') # there are also resname = {xxx} if b['name'].endswith('\n'): print '>>>>>>>>>>>>>>>>>>>>>>>>><>' + b['name'] + '<' blocks.append(b) else: b['name'] = 'name not found!' logger.error('Problem parsing the config file, regex did not match') if blockdepth != 0: # TODO: throw exception print "unbalanced braces in config2blocks" return "unbalanced braces in config2blocks" return blocks ''' Class for ressources inside of configurations hierarchy: Config -> Resource -> Items+ |-> Resource -> Items ''' class Resource(object): """ This class represents a bacula resource . On creation, it is automatically filled with the possible configuration items for the specific resource type. Some items can exist multiple times (like file=, plugin= etc.), These are created one time and set to their default value """ def __init__(self, parentconfig, resourcetype, indentlevel = 1, printall = False, onlinehelpURL = ''): #def __init__(self, parentconfig, resourcetype, name = None, indentlevel = 1, printall = False): self.onlinehelpURL = onlinehelpURL self.indentlevel = indentlevel #print resourcetype, type(self.indentlevel) ,self.indentlevel self.shortname = res2shrtn[resourcetype] self.parentconfig = parentconfig # what configuration does this resource belong to? #print "my parentconfig is" + repr(parentconfig) self.printall = printall self.resources = [] self.items = [] self.reflist = [] # what resources reference this resource? if resourcetype == 'runscript': items = eval ( "dird_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works self.items = copy.deepcopy(items) logging.debug('eval: ' + " dird_"+ res2shrtn[resourcetype] + "_items[:]") elif resourcetype == 'messages': items = eval ( "lib_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works self.items = copy.deepcopy(items) logging.debug('eval: ' + " lib_"+ res2shrtn[resourcetype] + "_items[:]") elif resourcetype == 'options': items = eval ( "inc_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works self.items = copy.deepcopy(items) logging.debug('eval: ' + "inc_"+ res2shrtn[resourcetype] + "_items[:]") else: #print resourcetype #self.items = eval ( self.parentconfig.shortname+"_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works items = eval ( self.parentconfig.shortname+"_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works self.items = copy.deepcopy(items) logging.debug('eval: ' + self.parentconfig.shortname+"_"+ res2shrtn[resourcetype] + "_items[:]" ) #for item in self.items: # logger.debug(item.name + ' id: '+ str(id(item))) # if item.name in MULTIPLE_ALLOWED_ITEMS_SET: # del item # set parent resource for all own items for item in self.items: item.parentresource = self item.indentlevel = 1 self.__create_valid_items_set__() self.__create_items_dict___() self.resourcetype = resourcetype # director, filedaemon ... #self.name = name # bacula-dir, bacula-fd ... #self.items.sort() logger.debug('Created ressource ' + resourcetype + ' with id #' + str(id(self)) ) def getResourcesListByResType(self,restype): """ returns a list of resources of the specified resource type. """ reslist = [] if restype == 'client' or restype == 'filedaemon': for res in self.resources: if res.resourcetype == 'client' or res.resourcetype == 'filedaemon': reslist.append(res) else: for res in self.resources: if res.resourcetype == restype: reslist.append(res) return reslist def getItemsListByItemName(self,itemname): ''' returns a list of items of the specified item name ''' itemlist = [] for item in self.items: if item.name == itemname: itemlist.append(item) return itemlist def getName(self): ''' gets the value of the item "name" ''' if self.items_dict.has_key('name'): return self.items_dict['name'].storage.value.strip('"') else: return '' def setName(self,name): ''' gets the value of the item "name" ''' if self.items_dict.has_key('name'): self.items_dict['name'].setValue(name) else: print "could not set name " + name def getPassword(self): ''' gets the value of the item "password" ''' if self.items_dict.has_key('password'): return self.items_dict['password'].storage.value.strip('"') else: return "ERROR: no password defined" def getItemValue(self, itemname): ''' gets the value of the itemname ''' if self.items_dict.has_key(itemname): return self.items_dict[itemname].storage.value else: return "ERROR: no item " + itemname + "defined" def setItemValue(self, itemname, newValue): ''' gets the value of the itemname ''' if self.items_dict.has_key(itemname): self.items_dict[itemname].setValue(newValue) else: print "ERROR: no item " + itemname + "defined" def __create_valid_items_set__(self): """ create a set of the valid items for this resource. """ self.validitemsset = set() for item in self.items: self.validitemsset.add(item.name) def __create_items_dict___(self): """ create a dict with the name of the items as key for easier access """ self.items_dict = {} for item in self.items: self.items_dict[item.name] = item item.indentlevel = self.indentlevel + 1 def __str__(self): """ create a string representation of the resource including subresources and items """ if self.resourcetype in RESSOURCES_WITH_EQUALSIGN: s = "%s%s = { \n" % (self.indentlevel * INDENTSTRING, prettyName(self.resourcetype))#, self.__class__.__name__ +' id: '+ str(id(self)) ) else: s = "%s%s { \n" % (self.indentlevel * INDENTSTRING, prettyName(self.resourcetype))#, self.__class__.__name__ +' id: '+ str(id(self)) ) # print own items for item in self.items: item.printall = self.printall if str(item) != '': s += str(item) # print subresources for v in self.resources: s += str(v) s += '%s}\n' % ( self.indentlevel * INDENTSTRING ) return s def parse_string(self, configstring): ''' parse resource configuration string and set the resource items accordingly. ''' logger.debug( self.resourcetype + " : parse_string called with \n\"" + configstring + '\"' ) #TODO REMOVE THE FOUND SUBBLOCK FROM THE ANALYSIS INSIND OF THE PARENTBLOCK: # search for subblocks block_to_check = configstring blocks = config2blocks(configstring) for block in blocks: if block['level'] == 1: # find containing blocks and only parse the block without the subblocks (resources) #logger.debug( "found block:" + str(block['start']) + ':' + str(block['end']) ) #logger.debug( "block now :" + block_to_check ) block_to_del = configstring[block['namestart']:block['end']+1] logger.debug( "delete:"+ block_to_del + 'in' + block_to_check) tmp = block_to_check.replace(block_to_del,'') # remove the subblock/resource block_to_check = tmp #logger.debug(self.resourcetype +':' + block['name'].lower() ) # parentconfig, resourcetype, name = None, indentlevel = 1, printall = False newres = Resource(self, block['name'].lower(), self.indentlevel + 1 ) if newres != None: newres.parse_string(configstring[block['start']+1:block['end']]) #newres.parse_string(configstring[block['start']+1:block['end']]) self.resources.append(newres) logger.debug( "just appended resource :" + str(id(newres)) ) else: logger.error('did not get a resource back, maybe resource not allowed in this configuration?') ''' find all occurrences of "simple" item = value lines''' for line in RXP_SIMPLE_ITEM.finditer(block_to_check): logger.debug("checking for simple_items: " +block_to_check) #logger.debug( line.groups()) logger.debug('SIMPLE_ITEM_MATCH:*' + self.resourcetype + '* >' + line.group('itemname') + '< >' + line.group('value') + '<') normalized_itemname = line.group('itemname').strip().lower().replace(' ','') #logger.debug(str(id(self))+ '*' + str(self)) if normalized_itemname not in self.validitemsset: logger.error( normalized_itemname + ' not allowed in this ressource') else: logger.debug( normalized_itemname + ' ok in this ressource') #if normalized_itemname == 'commandacl': # self.items_dict[normalized_itemname].ACLDict = commands.keys(); if normalized_itemname not in MULTIPLE_ALLOWED_ITEMS_SET: self.items_dict[normalized_itemname].setValue(line.group('value').strip()) elif normalized_itemname == 'file': newitem = Item('file', 0, 0, False, 'store_dir', False, indentlevel = self.indentlevel + 1) # "File" Items can exist multiple times logger.debug( 'created new \"' + normalized_itemname + '\" Item with id #' + str(id(newitem)) ) newitem.setValue(line.group('value')) self.items.append(newitem) newitem.parentresource = self #logger.debug('appended #' + str(id(newitem)) + str(newitem) + ' to #' + str(id(self)) + self.name + ' ' + self.resourcetype) #logger.debug(self.items) elif normalized_itemname == 'run': newitem = Item('run', '', 0, False, 'store_run', False, indentlevel = self.indentlevel + 1) # "run" Items can exist multiple times logger.debug( 'created new \"' + normalized_itemname + '\" Item with id #' + str(id(newitem)) ) newitem.setValue(line.group('value')) self.items.append(newitem) newitem.parentresource = self else: logger.debug( 'please create logic for multiple item type ' + normalized_itemname ) class BaculaConfig(object): ''' base class for bacula config file (console, dir, sd, fd) if processEventsCallback is given,it is called at regular basis to enable the update of the gui. ''' def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None): self.filename = filename self.parentdatacenter = parentdatacenter self.validresources = eval(self.shortname+'_resources') # TODO: Try/Except #self.onlinehelp = {} if not self.resources: self.resources = [] self.name = '' # Name of director, filedaemon, storage daemon, console self.__create_validresources_set__() if len(configAsString) > 0 : # if configstring is not empty self.parse_configfile(configAsString, processEventsCallback) else: for r in self.validresourcesset: # if configstring is empty -> initialize needed resources if (self.__class__.__name__ == 'FileDaemonConfig' and r == 'client'): # Filedaemon and Client are the same, we only need one continue logger.debug("initializing new resource:" + r) self.createResource(r) logger.debug( 'Created BaculaConfig ' + self.__class__.__name__ ) def getName(self): ''' get name from own main resource: in director config the name of the director resource, other resource files respectively ''' mainresources = self.getResourcesListByResType(self.longname) logger.debug(mainresources, self.longname) #assert len(mainresources) < 2 # this assertion can break on console.confs, where multiple consoles can be defined. # TODO: check this # we only take the name of the first defined console, maybe we should show all names? if len(mainresources) == 0: return '' elif len(mainresources) == 1: return mainresources[0].getName() else: namelist = [] for res in mainresources: namelist.append(res.getName()) return ','.join(namelist) def getPassword(self): ''' get password from own main resource: in director config the name of the director resource, other resource files respectively ''' mainresources = self.getResourcesListByResType(self.longname) logger.debug(mainresources, self.longname) #assert len(mainresources) < 2 # this assertion can break on console.confs, where multiple consoles can be defined. # TODO: check this # we only take the name of the first defined console, maybe we should show all names? if len(mainresources) == 0: return '' else: #return mainresources[0].items_dict['name'].storage.value return mainresources[0].getPassword() def getResourcesListByResType(self,restype): """ returns a list of resources of the specified resource type. """ reslist = [] for res in self.resources: if res.resourcetype == restype: reslist.append(res) return reslist def getResourceByName(self,name): """ returns a list of resources with name in parameter "name" """ for res in self.resources: #print res.items_dict['name'].storage.value , name if str(res.items_dict['name'].storage.value).strip('"') == name.strip('"'): return res return None def getReferencingResourcesListForResource(self,resource): """ return a list of resources referencing the given resource """ reslist = [] for res in self.resources: for item in res.items: referredType = item.name if referredType.endswith('pool'): #incrementalpool, differentialpool, etc #print "setting referredType to pool as", referredType referredType = 'pool' #print "checking:", referredType, resource.resourcetype # check for overrides in run entry for references if referredType == 'run': if hasattr(item.storage,'runentry'):# check for runentry in schedules #print item.storage.runentry.overrides for ov,val in item.storage.runentry.overrides.iteritems(): #print "checking: ",ov,val if val is None: continue #if ov.endswith('pool'): # ovcheck = 'pool' #else: ovcheck = val #print "OVERRIDE_CHECKING",ovcheck,resource.items_dict['name'].storage.value if ovcheck == resource.items_dict['name'].storage.value: #print "FOUND_OVERRIDE:", ovcheck,resource.items_dict['name'].storage.value reslist.append(res) if referredType == resource.resourcetype: #print "found:",item.name, referredType, resource.resourcetype #print "comparing", str(item.storage.value).strip('"'), str(resource.items_dict['name'].storage.value).strip('"') if item.storage.value != None: if str(item.storage.value).strip('"') == resource.items_dict['name'].storage.value.strip('"'): #print "FOUND:", item.storage.value, resource.items_dict['name'].storage.value reslist.append(res) #print item.name,item.storage.value, resource.resourcetype, resource.items_dict['name'].storage.value #if item.value == resource.items_dict['name']: return reslist def parse_configfile(self, configstring, callback): """ parses the configstring: removes the comment lines, """ cf = '' for line in configstring.splitlines(True): cf += line[:line.find('#')]+'\n' # remove comments blocks = config2blocks(cf) #b = 1 for block in blocks: #print "%d of %d blocks" % (b, len(blocks)) #b+=1 if block['level'] == 1: logger.debug( block['name'].lower() ) # TODO: can we call Resource() directly? newres = self.createResource( block['name'].lower() ) if callback is not None: callback("creating new " + block['name'].lower() + " resource" ) # call callback function after every resource if newres != None: newres.parse_string(cf[block['start']+1:block['end']]) if callback is not None: callback(newres.getName()) # call callback function after every resource else: logger.error('did not get a ressource back, maybe ressource not allowed in this configuration?') def __str__(self): """ creates the string representation of the configuration file by recursively calling the string representation of all resources """ s = '' # string representation now = time.asctime() # set verid string to the date # TODO: Make this configurable #dirconfs = self.getResourcesListByResType('director') # #for d in dirconfs: # try: # d.items_dict['verid'].storage.setValue('" | configuration file created on :' + now + '"' ) # except: # logger.error('verid setting impossible, maybe object not yet initialized?') s += '# config file written on ' + now + '\n' for v in self.resources: s += str(v) s += "\n" return s def selfcheck(self): """ selfcheck for valid conditions TODO documentation """ retval = True logger.debug(self.__class__.__name__ + "Selfcheck starts") # check if defined resources are allowed for res in self.resources: if res.resourcetype not in self.validresourcesset: logger.error( "Error: resource name " + res.resourcetype + " not allowed") retval = False else: logger.debug(res.resourcetype + " is allowed") # check if at least one of the needed resources is defined for res in self.validresourcesset: if self.getResourcesListByResType(res) == None: logger.error("Resourctype " + " is missing!") retval = False else: logger.debug(res + " is configured, OK") logger.debug(self.__class__.__name__ + "Selfcheck end") return retval def createResource(self, resourcetype ): ''' creates a ressource of type "ressourcetype" inside of this bacula configuration ''' if resourcetype not in self.validresourcesset: logger.error("ressource type \"" + resourcetype + "\" not allowed in this configuration") else: if self.onlinehelp.has_key(resourcetype): helpurl = self.onlinehelp[resourcetype] else: helpurl = '' newres = Resource( self, resourcetype, indentlevel = 0, onlinehelpURL = helpurl) self.resources.append( newres ) return newres def deleteResource(self, resource ): ''' delete given resource from own resources ''' try: self.resources.remove(resource) retval = True except: logger.error("could not remove resource") retval = False return retval def __create_validresources_set__(self): ''' Create a set of allowed ressources inside of this bacula configuration ''' self.validresourcesset = set() for validresource in self.validresources: self.validresourcesset.add(validresource[0]) # add name #print self.validresourcesset class DirectorConfig(BaculaConfig): ''' Class for configuration of one director ( bacula-dir.conf ) ''' def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None): self.shortname = 'dird' self.longname = 'director' self.onlinehelp = onlinehelp[self.longname] self.resources = [] BaculaConfig.__init__(self, configAsString, parentdatacenter, processEventsCallback, filename) class StorageDaemonConfig(BaculaConfig): ''' Class for configuration of one storage daemon ( bacula-sd.conf ) ''' def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None): self.shortname = 'stored' #self.longname = 'storagedaemon' self.longname = 'storage' self.onlinehelp = onlinehelp['storagedaemon'] self.resources = [] BaculaConfig.__init__(self, configAsString, parentdatacenter, processEventsCallback, filename) class FileDaemonConfig(BaculaConfig): ''' Class for configuration of one storage daemon ( bacula-fd.conf ) ''' def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None): self.shortname = 'filed' self.longname = 'filedaemon' self.onlinehelp = onlinehelp['client'] self.resources = [] BaculaConfig.__init__(self, configAsString, parentdatacenter , processEventsCallback, filename) def createFdConfigFromDirClient(self, dirClient = None): ''' create a Filedaemon Config from a Client Config in Director we have to set some required values to defaults TODO: we should be able to use different defaults for different classes of clients (windows, linux, solaris etc.) ''' directors = self.getResourcesListByResType('director') if len(directors) != 1: logger.error("here we should have one director!") return None messages = self.getResourcesListByResType('messages') if len(messages) != 1: logger.error("here we should have one messages!") return None filedaemons = self.getResourcesListByResType('filedaemon') # same as client if len(filedaemons) != 1: logger.error("here we should have one filedaemon!") return None dirDirector = dirClient.parentconfig.getResourcesListByResType('director')[0] # get password and dirname from director config directors[0].items_dict['name'].storage.value = dirDirector.items_dict['name'].storage.value directors[0].items_dict['password'].storage.value = dirClient.items_dict['password'].storage.value filedaemons[0].items_dict['name'].storage.value = dirClient.items_dict['name'].storage.value self.name = filedaemons[0].items_dict['name'].storage.value # TODO: make the following configurable filedaemons[0].items_dict['workingdirectory'].storage.value = '/var/lib/bacula' filedaemons[0].items_dict['piddirectory'].storage.value = '/var/run' messages[0].items_dict['name'].storage.value = 'Standard' messagesentry = directors[0].items_dict['name'].storage.value + ' = all, !skipped, !restored' messages[0].items_dict['director'].storage.setValue(messagesentry) #messages[0].items_dict['director'].storage.value = directors[0].items_dict['name'].storage.value + ' = all, !skipped, !restored' #filedaemons[0].items_dict['password'].storage.value = dirClient.items_dict['password'].storage.value #dirClient.parentconfig.getResourceByName(parentname).items_dict['name'] #self.items_dict['password'] = DirDirector.items_dict['password'] class ConsoleConfig(BaculaConfig): ''' Class for configuration of a console ( bconsole.conf ) ''' def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None): self.shortname = 'console' self.longname = 'console' self.onlinehelp = onlinehelp[self.longname] self.resources = [] BaculaConfig.__init__(self, configAsString, parentdatacenter, processEventsCallback, filename) class DataCenter(object): ''' A datacenter is the sum of all configurations that make a running bacula system, i.e. director, storaged, console and filed configurations A datacenter resides in an own subdirectory and has subdirs for consoles, directors, storagedaemons and filedaemons /datacenter/ /datacenter/consoles/ /datacenter/directors/ /datacenter/storagedaemons/ /datacenter/filedaemons/ this directory structure is hidden inside of a tar.gz file ''' def __createDataCenterDirs__(self, basedir): olddir = os.getcwdu() os.mkdir(basedir) #os.chdir(basedir) for subdir in self.subdirs: os.mkdir( os.path.normpath(basedir+'/'+subdir) ) #os.chdir(olddir) def __init__(self, targzfile, callbackfunction = None): ''' extract the targz file, open the *.conf file inside the subdirs and create the configurations accordingly ''' self.filename = targzfile self.subdirs = ['consoles','directors','storagedaemons','filedaemons'] self.directors = set() self.consoles = set() self.storagedaemons = set() self.filedaemons = set() tmpdir = tempfile.mkdtemp(prefix = 'dassmodus-') # TODO: delete this after use # if the targz file does not exist, then create an empty # datacenter file if not os.path.isfile(targzfile): print targzfile + ' does not exist, creating new file' self.name='datacenter' os.mkdir(tmpdir + '/' + self.name)# datacentername for subdir in self.subdirs: os.mkdir(tmpdir + '/' + self.name + '/' + subdir) # create a new tarfile tar = tarfile.open(targzfile, "w:gz") dirs = glob.glob(os.path.normpath(tmpdir + '/*/*')) for d in dirs: tar.add(d, arcname = d.replace(tmpdir + '/','')) tar.close() # extract subdirs inside of the tarfile into tmp dir tar = tarfile.open(targzfile, "r:gz") #olddir = os.getcwdu() #os.chdir(self.tmpdir) tar.extractall(path = tmpdir) tar.close basedir = os.listdir(tmpdir) datacenterdir = os.path.normpath(tmpdir + '/' + basedir[0]) #os.chdir(basedir[0]) # change into the self.name = basedir[0] for subdir in self.subdirs: files = glob.glob(os.path.normpath(datacenterdir + '/' + subdir + '/*.conf' ) ) for filename in files: if callbackfunction is not None: callbackfunction('parsing file ' + filename) if subdir == 'directors': configstring = open(filename).read() conf = DirectorConfig(configstring, parentdatacenter = self, filename = filename, processEventsCallback = callbackfunction ) self.directors.add(conf) elif subdir == 'storagedaemons': configstring = open(filename).read() conf = StorageDaemonConfig(configstring, parentdatacenter = self, filename = filename, processEventsCallback = callbackfunction) self.storagedaemons.add(conf) elif subdir == 'filedaemons': configstring = open(filename).read() conf = FileDaemonConfig(configstring, parentdatacenter = self, filename = filename ,processEventsCallback = callbackfunction) self.filedaemons.add(conf) elif subdir == 'consoles': configstring = open(filename).read() conf = ConsoleConfig(configstring, parentdatacenter = self, filename = filename, processEventsCallback = callbackfunction) self.consoles.add(conf) else: raise NameError('help me') # remove temporary files shutil.rmtree(tmpdir) #os.chdir(olddir) # restore working dir def getName(self): return self.name def setName(self, name): self.name = name def setFileName(self, filename): self.filename = filename self.name = os.path.basename(filename).split('.')[0] def importConfigurationFile(self, filename, configtype): ''' import a existing configuration file into this datacenter. allowed configtypes: filedaemon, storagedaemon, console, director ''' print "importing file " + filename + ' ' + configtype if configtype == 'filedaemon': configstring = open(filename).read() conf = FileDaemonConfig(configstring, filename = filename) self.filedaemons.add(conf) elif configtype == 'storagedaemon': configstring = open(filename).read() conf = StorageDaemonConfig(configstring, filename = filename) self.storagedaemons.add(conf) elif configtype == 'console': configstring = open(filename).read() conf = ConsoleConfig(configstring, filename = filename) self.consoles.add(conf) elif configtype == 'director': configstring = open(filename).read() conf = DirectorConfig(configstring, filename = filename) self.directors.add(conf) else: raise NameError('Unknown configtype, allowed : filedaemon, storagedaemon, console, director') return False def deleteConfiguration(self, configuration): ''' delete the refered configuration resource/configfile ''' for configtype in [self.directors, self.filedaemons, self.storagedaemons, self.consoles]: if configuration in configtype: configtype.remove(configuration) del configuration break def checkFileDaemonsIntegrity(self): ''' Possible configuration problems: 1a: - dirconf refers to filedaemon, but no filedaemon conf exists 1b: - dirconf refers to filedaemon, but filedaemon conf has wrong password for this director 2a: - filedaemon conf refers to Director that does not exist 2b: - filedaemon conf refers to existing Director, but password is wrong for this director 1b and 2b are the same this leads to 3 cases: - Dir does not exist, - Filedaemon does not exist, - Passwords do not match ''' s = '' # return string director_filedaemons_passwords = {} # dictionary of director -> filedaemonname -> password, extracted from dirconfs filedaemons_directors_passwords = {} # dictionary of filedaemon -> director -> password , extracted from fdconfs # collect info about all defined filedaemons/clients in ALL Director Configurations for dir in self.directors: dirfds = dir.getResourcesListByResType('client') director_filedaemons_passwords[dir.getName()] = {} for fd in dirfds: director_filedaemons_passwords[dir.getName()][fd.getName()] = fd.getPassword() # collect info about all definied Directors in all Filedaemon Configurations for fdconf in self.filedaemons: filedaemons_directors_passwords[fdconf.getName()] = {} for dir in fdconf.getResourcesListByResType('director'): filedaemons_directors_passwords[fdconf.getName()][dir.getName()] = dir.getPassword() #print "Filedaemon View:" # check from the view of filedaemons configurations for f,d in filedaemons_directors_passwords.iteritems(): for dir,pw in d.iteritems(): #print f,dir,pw if director_filedaemons_passwords.has_key(dir): # do we have a director config of this director? if director_filedaemons_passwords[dir].has_key(f): # has this dir a definition of this fd? if director_filedaemons_passwords[dir][f] == pw: # and is the password identical? s += "OK: Filedaemon %s has definition of Director %s and password is the same\n" % (f,dir) #s += "INFO: Filedaemon %s has definition of Director %s and password is the same: %s\n" % (f,dir,pw) else: s+= "ERROR: Filedaemon %s has definition of Director %s but password is different: %s != %s\n" % (f,dir,pw,director_filedaemons_passwords[dir][f]) else: s+= "ERROR: Filedaemon %s has not a definition of Director %s\n" % (dir,f) else: s+= "ERROR: Director configuration for %s is MISSING , it was referenced by configfile of %s\n" % (dir,f) #print "Director View:" # check from the view of director configurations for d,f in director_filedaemons_passwords.iteritems(): for fs,pwd in f.iteritems(): #print d,fs,pwd if filedaemons_directors_passwords.has_key(fs): # do we have a fd-conf for this filedaemon? if filedaemons_directors_passwords[fs].has_key(d): # does this fd-conf have a reference to this dir? if filedaemons_directors_passwords[fs][d] == pwd: # is the password identical? s+= "OK: Director %s is known by Filedaemon %s and passwords are the same.\n" %(d, fs) #s+= "OK: Director %s is known by Filedaemon %s and passwords are the same: %s\n" %(d, fs, pwd) else: s+= "ERROR: Director %s is known by Filedaemon %s, but passwords are NOT the same: %s != %s\n" % (d, fs, pwd,filedaemons_directors_passwords[fs][d]) else: s+= "WARNING: Director %s does not reference Filedaemon %s,that may be OK\n" % (dir, fs) # Not every Dir has to reference every Filedaemon else: s+= "ERROR: fd-conf is MISSING for Filedaemon %s : please import or create.\n" %(fs) return s def checkStorageDaemonsIntegrity(self): ''' Possible configuration problems: 1a: - dirconf refers to storage, but no storagedaemon conf exists with this storage 1b: - dirconf refers to storage, storagedaemon has this storage but has wrong password for this director 2a: - StorageDaemon conf refers to Director that does not exist 2b: - StorageDaemon conf refers to existing Director, but password is wrong for this director 3a: - dirconf refers to Device or Autochanger that is not defined in Storage Daemon 3b: - Storage Daemon defines Device or Autochanger that is not used by any Director (info, not problem, may be used in a autochanger) 4a: - dirconf refers to Device or Autochanger that is defined, but the *MediaType* is not the same ''' ''' Datastructures we need: # Director view directorName - storageName - password - Device - Media Type - AutoChanger? # Storage view storageName - directorName - Password - DeviceName - MediaType - AutochangerName[Devices] ''' director_storages = {} storage_directors = {} # collect info about all defined storages in ALL Director Configurations for dir in self.directors: dirstorages = dir.getResourcesListByResType('storage') director_storages[dir.getName()] = {} for storage in dirstorages: director_storages[dir.getName()]['password'] = storage.getPassword() director_storages[dir.getName()]['device'] = storage.getItemValue('device') director_storages[dir.getName()]['mediatype'] = storage.getItemValue('mediatype') # collect info about all definied Directors in all Filedaemon Configurations for sdconf in self.storagedaemons: storage_directors[sdconf.getName()] = {} for dir in sdconf.getResourcesListByResType('director'): storage_directors[sdconf.getName()][dir.getName()] = dir.getPassword() for device in sdconf.getResourcesListByResType('device'): storage_directors[sdconf.getName()][device.getName()] = device.getItemValue('mediatype') for autochanger in sdconf.getResourcesListByResType('autochanger'): storage_directors[sdconf.getName()][autochanger.getName()] = device.getItemValue('device') ''' TODO: implement the checks and output TODO: implement test cases for this function! ''' def checkConsolesIntegrity(self): ''' check if we have a console conf for every main dir in each dirconf (passwds) and if we have a console for every named console in each dir (names) including passwords check ''' ''' possible configuration problems: - no console.conf for director entry in director configuration (check passwds) - no console.conf for named console entries in director configurations - console.conf for named console entry but passwords not the same - console.conf, whose password does not match name/password of named consoles and not password of anonymous console - console.conf contains console entry that refers a director that is not defined inside of this console conf Datastructures we need: # director view: directorname - mainpassword # anonymous/default console - consolename - password # named console # console view: console - directorname - password # anonymous/default console - consolename - directorname # named console, refers to defined director inside of this configfile # consoles can have multiple director entries, and multiple console entries referring these directors ''' directors_consoles = {} consoles_directors = {} for dir in self.directors: # look in all directors of our DataCenter dirname = dir.getName() defaultconsolepassword = dir.getPassword() #dir.getResourcesListByResType('director')[0].getPassword() # we can only have 1 director entry per director conf directors_consoles[dirname]={} directors_consoles[dirname]['defaultconsole'] = defaultconsolepassword for console in dir.getResourcesListByResType('console'): directors_consoles[dirname][console.getName()] = console.getPassword() defconsoles = 0 for consoleconf in self.consoles: # check all consoles in DC #print consoleconf consolename = consoleconf.getName() if consolename == '': # we have an defaultconsole defconsoles += 1 consolename = '__defaultconsole__%d' % (defconsoles) consoles_directors[consolename]={} consoles_directors[consolename]['directors']= {} consoles_directors[consolename]['consoles'] = {} for dir in consoleconf.getResourcesListByResType('director'): consoles_directors[consolename]['directors'][dir.getName()] = dir.getPassword() for con in consoleconf.getResourcesListByResType('console'): consoles_directors[consolename]['consoles'][con.getItemValue('director')] = {} consoles_directors[consolename]['consoles'][con.getItemValue('director')]['name'] = con.getName() consoles_directors[consolename]['consoles'][con.getItemValue('director')]['password'] = con.getPassword() print "break" s = '' # check the directorconfig view for dir in directors_consoles: defaultpw = directors_consoles[dir]['defaultconsole'] for consolename, dirinfo in consoles_directors.iteritems(): # console resource in console config unreferred_dirs = set() # main directors in dirconfs -> Default Consoles if consolename.startswith('__defaultconsole__'): # we have a default console for d,pw in dirinfo['directors'].iteritems(): unreferred_dirs.add(d) # remember dirs if d == dir: unreferred_dirs.remove(d) s += "found defaultconsole in console.conf for dir %s" % ( d ) if defaultpw == pw: s += " and passwords are the same" else: s += " and passwords are different!" else: s+= "default console not configured for this dir" #print out unreferred main dir confs for unrefdir in unreferred_dirs: s+= "director %s not referred by any console.conf as default console" % (unrefdir) else: # named console print "found named console" ,consolename for c, consdir in dirinfo['consoles'].iteritems(): print c, consdir for d,pw in dirinfo['directors'].iteritems(): #print d,pw print d, pw, dirinfo['consoles'][d]['password'] #print consolename, dirinfo, try: print directors_consoles[dir][d] except: print "no named console for dir " , d # check if we have #unreferred_dirs.add(d) # remember dirs if d == dir: unreferred_dirs.remove(d) s += "found defaultconsole in console.conf for dir %s" % ( d ) if defaultpw == pw: s += " and passwords are the same" else: s += " and passwords are different!" else: s+= "default console not configured for this dir" #print out unreferred main dir confs for unrefdir in unreferred_dirs: s+= "director %s not referred by any console.conf as default console" % (unrefdir) #for conscons in consoles_directors # print conscons #referred_dirs.add(conscons[dir]) #for consdir in console['directors']: ''' now we have collected all info about defined dirs and consoles in this Datacenter Checks: Director view A: iterate over all directors, I:check if we have a anonymous console conf that refers to the given password II:iterate over every named console: 1.: do we have a director entry in the console conf with the correct name TODO:(address/port) 2.: we have a console entry for this named console with the correct name and dirname AND do we have a director with this dirname defined inside of this particular console conf Console view B: iterate over all consoles, I check if we have a director for every defined director in console (check names) II check if our console definitions refer to defined Director resources inside of this consoleconfig (If we have consoles) III check if we have a named console definition in dirconf of director DIRNAME with the Name of our console conf and the same password IV check for not by console entries referred directors if the console Director Passwd is identical to the Director default password ''' ''' TODO: implement the checks and output TODO: implement test cases for this function! ''' def safeDatacenter(self): ''' safe the Datacenter under the known filename ''' self.writeDatacenter(os.path.dirname(self.filename)) def writeDatacenter(self,targetdir): ''' create the subdirs write the configs create tarfile ''' tar = tarfile.open(targetdir + '/' + self.name + '.dmdz','w:gz') writedir = tempfile.mkdtemp(prefix = 'dassmodus-') writebasedir = writedir+'/' + self.name self.__createDataCenterDirs__(writebasedir) for director in self.directors: #print director.getName() writefile = open(writebasedir + '/' + 'directors' + '/' + director.getName() + '.conf','w') writefile.write(str(director)) writefile.close() for storagedaemon in self.storagedaemons: #print storagedaemon.getName() writefile = open(writebasedir + '/' + 'storagedaemons' + '/' + storagedaemon.getName() + '.conf','w') writefile.write(str(storagedaemon)) writefile.close() for filedaemon in self.filedaemons: #print storagedaemon.getName() writefile = open(writebasedir + '/' + 'filedaemons' + '/' + filedaemon.getName() + '.conf','w') writefile.write(str(filedaemon)) writefile.close() consolenr = 0 for console in self.consoles: #print storagedaemon.getName() name = console.getName() if name == '': consolenr += 1 name = "bconsole-%d" % ( consolenr ) writefile = open(writebasedir + '/' + 'consoles' + '/' + name + '.conf','w') writefile.write(str(console)) writefile.close() #os.chdir(writedir) dirs = glob.glob(os.path.normpath(writedir + '/*/*')) for d in dirs: tar.add(d, arcname = d.replace(writedir+'/','')) tar.close() # remove temp dir shutil.rmtree(writedir) if __name__ == "__main__": #msgsstore = store_msgs("root@localhost = mount") import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s \t (%(module)s:%(lineno)d) %(message)s ', #filename='vanHelsing.log', filemode='w') cfg = ''' Pool { LabelFormat = "${Year}-${Month:p/2/0/r}-${Day:p/2/0/r}" } ''' dconfig = DirectorConfig(cfg)