source: dassmodus/trunk/dassmodus/nosferatu/nosferatu/bacresources.py@ 959

Last change on this file since 959 was 959, checked in by pstorz, on Oct 18, 2011 at 4:56:32 PM

fixed onlinehelp index

File size: 46.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# $Id: bacresources.py 12826 2011-04-03 16:49:56Z pstorz $
5
6import time
7
8#from directive import *
9#from configrules import *
10from auto_configrules import *
11
12import logging
13from auto_onlinehelp import *
14logger = logging.getLogger('bacresources')
15
16import os
17import tarfile
18import tempfile
19import glob
20import shutil
21import string
22import random
23
24#TODO: we have a problem if inside of a password there is a special character like {}
25
26
27def genPw(self,length=32, chars=string.letters + string.digits):
28 # from http://code.activestate.com/recipes/59873-random-password-generation/
29 return ''.join([random.choice(chars) for i in range(length)])
30
31
32
33
34def config2blocks(configstring, processEventsCallback = None):
35 """
36 Parses the configstring and returns a list of blocks.
37 Each block is represented by its start- and end characterindex
38 saved in 'start' and 'end'
39 the configstring is expected without comment lines
40 """
41 blockdepth = 0
42 blockstack = []
43 blocks = []
44 instring = False # are we in a string between (") ?
45 lastcharbackslash = False
46
47 for i in range(0, len(configstring)):
48
49 #if processEventsCallback is not None and not (i%100):
50 ## #print "calling processEventsCallback"
51 # processEventsCallback(i)
52
53 chr = configstring[i]
54 if chr == '\\':
55 lastcharbackslash = True
56 #print 'foundbackslash'
57 else:
58 lastcharbackslash = False
59
60 if chr == '"' and not lastcharbackslash: # we may have a brace in a password, but can escape with \"
61 instring = not instring # TODO:password with { or } inside this still make problems!
62
63 if chr == '{' and not instring:
64 blockdepth += 1
65 #print '\n ',blockdepth,' **{** ->',configstring[i-10:i+20],'<-',
66 b = {}
67 b['start'] = i
68 b['level'] = blockdepth
69 blockstack.append(b)
70
71 elif chr == '}' and not instring:
72 blockdepth -= 1
73 b = blockstack.pop()
74 b['end'] = i
75 #print '\n ', blockdepth , ' **}** ->',configstring[i-10:i+1],'<-'
76 # find name just before the block beginning
77 s = configstring[:b['start']]
78 m = RXP_RESTYPE.search(s)
79 #logger.debug( "searching for ResType in >" + s + "<" )
80 if m:
81 b['namestart'] = b['start'] - len(m.group('resourcetype')) - 1 # start of resname......{ instead of {
82 b['name'] = m.group('resourcetype').strip(' =\n').replace(' ','') # there are also resname = {xxx}
83 #b['name'] = m.group('resourcetype').strip(' =\n') # there are also resname = {xxx}
84 if b['name'].endswith('\n'):
85 print '>>>>>>>>>>>>>>>>>>>>>>>>><>' + b['name'] + '<'
86 blocks.append(b)
87 else:
88 b['name'] = 'name not found!'
89 logger.error('Problem parsing the config file, regex did not match')
90
91 if blockdepth != 0:
92 # TODO: throw exception
93 print "unbalanced braces in config2blocks"
94 return "unbalanced braces in config2blocks"
95 return blocks
96
97
98
99
100'''
101Class for ressources inside of configurations
102hierarchy:
103Config -> Resource -> Items+
104 |-> Resource -> Items
105'''
106
107class Resource(object):
108 """
109 This class represents a bacula resource .
110 On creation, it is automatically filled with the possible configuration items
111 for the specific resource type.
112 Some items can exist multiple times (like file=, plugin= etc.),
113 These are created one time and set to their default value
114 """
115 def __init__(self, parentconfig, resourcetype, indentlevel = 1, printall = False, onlinehelpURL = ''):
116 #def __init__(self, parentconfig, resourcetype, name = None, indentlevel = 1, printall = False):
117 self.onlinehelpURL = onlinehelpURL
118 self.indentlevel = indentlevel
119 #print resourcetype, type(self.indentlevel) ,self.indentlevel
120 self.shortname = res2shrtn[resourcetype]
121 self.parentconfig = parentconfig # what configuration does this resource belong to?
122 #print "my parentconfig is" + repr(parentconfig)
123 self.printall = printall
124 self.resources = []
125 self.items = []
126 self.reflist = [] # what resources reference this resource?
127 if resourcetype == 'runscript':
128 items = eval ( "dird_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works
129 self.items = copy.deepcopy(items)
130 logging.debug('eval: ' + " dird_"+ res2shrtn[resourcetype] + "_items[:]")
131 elif resourcetype == 'messages':
132 items = eval ( "lib_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works
133 self.items = copy.deepcopy(items)
134 logging.debug('eval: ' + " lib_"+ res2shrtn[resourcetype] + "_items[:]")
135 elif resourcetype == 'options':
136 items = eval ( "inc_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works
137 self.items = copy.deepcopy(items)
138 logging.debug('eval: ' + "inc_"+ res2shrtn[resourcetype] + "_items[:]")
139 else:
140 #print resourcetype
141 #self.items = eval ( self.parentconfig.shortname+"_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works
142 items = eval ( self.parentconfig.shortname+"_"+ res2shrtn[resourcetype] + "_items[:]") #TODO : ensure this works
143 self.items = copy.deepcopy(items)
144 logging.debug('eval: ' + self.parentconfig.shortname+"_"+ res2shrtn[resourcetype] + "_items[:]" )
145
146 #for item in self.items:
147 # logger.debug(item.name + ' id: '+ str(id(item)))
148 # if item.name in MULTIPLE_ALLOWED_ITEMS_SET:
149 # del item
150
151
152
153 # set parent resource for all own items
154 for item in self.items:
155 item.parentresource = self
156 item.indentlevel = 1
157
158
159
160 self.__create_valid_items_set__()
161 self.__create_items_dict___()
162 self.resourcetype = resourcetype # director, filedaemon ...
163 #self.name = name # bacula-dir, bacula-fd ...
164 #self.items.sort()
165 logger.debug('Created ressource ' + resourcetype + ' with id #' + str(id(self)) )
166
167 def getResourcesListByResType(self,restype):
168 """
169 returns a list of resources of the specified resource type.
170 """
171 reslist = []
172 if restype == 'client' or restype == 'filedaemon':
173 for res in self.resources:
174 if res.resourcetype == 'client' or res.resourcetype == 'filedaemon':
175 reslist.append(res)
176 else:
177 for res in self.resources:
178 if res.resourcetype == restype:
179 reslist.append(res)
180 return reslist
181
182 def getItemsListByItemName(self,itemname):
183 '''
184 returns a list of items of the specified item name
185 '''
186 itemlist = []
187 for item in self.items:
188 if item.name == itemname:
189 itemlist.append(item)
190 return itemlist
191
192
193 def getName(self):
194 '''
195 gets the value of the item "name"
196 '''
197 if self.items_dict.has_key('name'):
198 return self.items_dict['name'].storage.value.strip('"')
199 else:
200 return ''
201
202 def setName(self,name):
203 '''
204 gets the value of the item "name"
205 '''
206 if self.items_dict.has_key('name'):
207 self.items_dict['name'].setValue(name)
208 else:
209 print "could not set name " + name
210
211 def getPassword(self):
212 '''
213 gets the value of the item "password"
214 '''
215 if self.items_dict.has_key('password'):
216 return self.items_dict['password'].storage.value.strip('"')
217 else:
218 return "ERROR: no password defined"
219
220 def getItemValue(self, itemname):
221 '''
222 gets the value of the itemname
223 '''
224 if self.items_dict.has_key(itemname):
225 return self.items_dict[itemname].storage.value
226 else:
227 return "ERROR: no item " + itemname + "defined"
228
229 def setItemValue(self, itemname, newValue):
230 '''
231 gets the value of the itemname
232 '''
233 if self.items_dict.has_key(itemname):
234 self.items_dict[itemname].setValue(newValue)
235 else:
236 print "ERROR: no item " + itemname + "defined"
237
238 def __create_valid_items_set__(self):
239 """
240 create a set of the valid items for this resource.
241 """
242 self.validitemsset = set()
243 for item in self.items:
244 self.validitemsset.add(item.name)
245
246 def __create_items_dict___(self):
247 """
248 create a dict with the name of the items as key
249 for easier access
250 """
251 self.items_dict = {}
252 for item in self.items:
253 self.items_dict[item.name] = item
254 item.indentlevel = self.indentlevel + 1
255
256 def __str__(self):
257 """
258 create a string representation of the resource
259 including subresources and items
260 """
261 if self.resourcetype in RESSOURCES_WITH_EQUALSIGN:
262 s = "%s%s = { \n" % (self.indentlevel * INDENTSTRING, prettyName(self.resourcetype))#, self.__class__.__name__ +' id: '+ str(id(self)) )
263 else:
264 s = "%s%s { \n" % (self.indentlevel * INDENTSTRING, prettyName(self.resourcetype))#, self.__class__.__name__ +' id: '+ str(id(self)) )
265
266
267
268 # print own items
269 for item in self.items:
270 item.printall = self.printall
271 if str(item) != '':
272 s += str(item)
273
274 # print subresources
275 for v in self.resources:
276 s += str(v)
277
278 s += '%s}\n' % ( self.indentlevel * INDENTSTRING )
279
280 return s
281
282 def parse_string(self, configstring):
283 '''
284 parse resource configuration string and
285 set the resource items accordingly.
286 '''
287 logger.debug( self.resourcetype + " : parse_string called with \n\"" + configstring + '\"' )
288
289 #TODO REMOVE THE FOUND SUBBLOCK FROM THE ANALYSIS INSIND OF THE PARENTBLOCK:
290 # search for subblocks
291 block_to_check = configstring
292 blocks = config2blocks(configstring)
293 for block in blocks:
294 if block['level'] == 1:
295 # find containing blocks and only parse the block without the subblocks (resources)
296 #logger.debug( "found block:" + str(block['start']) + ':' + str(block['end']) )
297 #logger.debug( "block now :" + block_to_check )
298 block_to_del = configstring[block['namestart']:block['end']+1]
299 logger.debug( "delete:"+ block_to_del + 'in' + block_to_check)
300 tmp = block_to_check.replace(block_to_del,'') # remove the subblock/resource
301 block_to_check = tmp
302
303 #logger.debug(self.resourcetype +':' + block['name'].lower() )
304 # parentconfig, resourcetype, name = None, indentlevel = 1, printall = False
305 newres = Resource(self, block['name'].lower(), self.indentlevel + 1 )
306 if newres != None:
307 newres.parse_string(configstring[block['start']+1:block['end']])
308 #newres.parse_string(configstring[block['start']+1:block['end']])
309 self.resources.append(newres)
310 logger.debug( "just appended resource :" + str(id(newres)) )
311 else:
312 logger.error('did not get a resource back, maybe resource not allowed in this configuration?')
313
314
315 ''' find all occurrences of "simple" item = value lines'''
316 for line in RXP_SIMPLE_ITEM.finditer(block_to_check):
317 logger.debug("checking for simple_items: " +block_to_check)
318 #logger.debug( line.groups())
319 logger.debug('SIMPLE_ITEM_MATCH:*' + self.resourcetype + '* >' + line.group('itemname') + '< >' + line.group('value') + '<')
320 normalized_itemname = line.group('itemname').strip().lower().replace(' ','')
321 #logger.debug(str(id(self))+ '*' + str(self))
322 if normalized_itemname not in self.validitemsset:
323 logger.error( normalized_itemname + ' not allowed in this ressource')
324 else:
325 logger.debug( normalized_itemname + ' ok in this ressource')
326
327 #if normalized_itemname == 'commandacl':
328 # self.items_dict[normalized_itemname].ACLDict = commands.keys();
329
330 if normalized_itemname not in MULTIPLE_ALLOWED_ITEMS_SET:
331 self.items_dict[normalized_itemname].setValue(line.group('value').strip())
332
333
334 elif normalized_itemname == 'file':
335 newitem = Item('file', 0, 0, False, 'store_dir', False, indentlevel = self.indentlevel + 1) # "File" Items can exist multiple times
336 logger.debug( 'created new \"' + normalized_itemname + '\" Item with id #' + str(id(newitem)) )
337 newitem.setValue(line.group('value'))
338 self.items.append(newitem)
339 newitem.parentresource = self
340 #logger.debug('appended #' + str(id(newitem)) + str(newitem) + ' to #' + str(id(self)) + self.name + ' ' + self.resourcetype)
341 #logger.debug(self.items)
342 elif normalized_itemname == 'run':
343 newitem = Item('run', '', 0, False, 'store_run', False, indentlevel = self.indentlevel + 1) # "run" Items can exist multiple times
344 logger.debug( 'created new \"' + normalized_itemname + '\" Item with id #' + str(id(newitem)) )
345 newitem.setValue(line.group('value'))
346 self.items.append(newitem)
347 newitem.parentresource = self
348
349 else:
350 logger.debug( 'please create logic for multiple item type ' + normalized_itemname )
351
352
353
354
355
356
357class BaculaConfig(object):
358 '''
359 base class for bacula config file (console, dir, sd, fd)
360 if processEventsCallback is given,it is called at regular basis to enable the
361 update of the gui.
362 '''
363 def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None):
364 self.filename = filename
365 self.parentdatacenter = parentdatacenter
366 self.validresources = eval(self.shortname+'_resources') # TODO: Try/Except
367 #self.onlinehelp = {}
368 if not self.resources:
369 self.resources = []
370
371 self.name = '' # Name of director, filedaemon, storage daemon, console
372 self.__create_validresources_set__()
373
374 if len(configAsString) > 0 : # if configstring is not empty
375 self.parse_configfile(configAsString, processEventsCallback)
376 else:
377 for r in self.validresourcesset: # if configstring is empty -> initialize needed resources
378 if (self.__class__.__name__ == 'FileDaemonConfig'
379 and r == 'client'): # Filedaemon and Client are the same, we only need one
380 continue
381 logger.debug("initializing new resource:" + r)
382 self.createResource(r)
383
384 logger.debug( 'Created BaculaConfig ' + self.__class__.__name__ )
385
386 def getName(self):
387 '''
388 get name from own main resource:
389 in director config the name of the director resource,
390 other resource files respectively
391 '''
392 mainresources = self.getResourcesListByResType(self.longname)
393 logger.debug(mainresources, self.longname)
394 #assert len(mainresources) < 2
395 # this assertion can break on console.confs, where multiple consoles can be defined.
396 # TODO: check this
397 # we only take the name of the first defined console, maybe we should show all names?
398 if len(mainresources) == 0:
399 return ''
400 elif len(mainresources) == 1:
401 return mainresources[0].getName()
402 else:
403 namelist = []
404 for res in mainresources:
405 namelist.append(res.getName())
406 return ','.join(namelist)
407
408
409
410 def getPassword(self):
411 '''
412 get password from own main resource:
413 in director config the name of the director resource,
414 other resource files respectively
415 '''
416 mainresources = self.getResourcesListByResType(self.longname)
417 logger.debug(mainresources, self.longname)
418 #assert len(mainresources) < 2
419 # this assertion can break on console.confs, where multiple consoles can be defined.
420 # TODO: check this
421 # we only take the name of the first defined console, maybe we should show all names?
422 if len(mainresources) == 0:
423 return ''
424 else:
425 #return mainresources[0].items_dict['name'].storage.value
426 return mainresources[0].getPassword()
427
428 def getResourcesListByResType(self,restype):
429 """
430 returns a list of resources of the specified resource type.
431 """
432 reslist = []
433 for res in self.resources:
434 if res.resourcetype == restype:
435 reslist.append(res)
436 return reslist
437
438 def getResourceByName(self,name):
439 """
440 returns a list of resources with name in parameter "name"
441 """
442 for res in self.resources:
443 #print res.items_dict['name'].storage.value , name
444 if str(res.items_dict['name'].storage.value).strip('"') == name.strip('"'):
445 return res
446 return None
447
448 def getReferencingResourcesListForResource(self,resource):
449 """
450 return a list of resources referencing the given resource
451 """
452 reslist = []
453 for res in self.resources:
454 for item in res.items:
455 referredType = item.name
456 if referredType.endswith('pool'): #incrementalpool, differentialpool, etc
457 #print "setting referredType to pool as", referredType
458 referredType = 'pool'
459 #print "checking:", referredType, resource.resourcetype
460
461 # check for overrides in run entry for references
462 if referredType == 'run':
463 if hasattr(item.storage,'runentry'):# check for runentry in schedules
464 #print item.storage.runentry.overrides
465 for ov,val in item.storage.runentry.overrides.iteritems():
466 #print "checking: ",ov,val
467 if val is None:
468 continue
469 #if ov.endswith('pool'):
470 # ovcheck = 'pool'
471 #else:
472 ovcheck = val
473 #print "OVERRIDE_CHECKING",ovcheck,resource.items_dict['name'].storage.value
474 if ovcheck == resource.items_dict['name'].storage.value:
475 #print "FOUND_OVERRIDE:", ovcheck,resource.items_dict['name'].storage.value
476 reslist.append(res)
477
478 if referredType == resource.resourcetype:
479 #print "found:",item.name, referredType, resource.resourcetype
480 #print "comparing", str(item.storage.value).strip('"'), str(resource.items_dict['name'].storage.value).strip('"')
481 if item.storage.value != None:
482 if str(item.storage.value).strip('"') == resource.items_dict['name'].storage.value.strip('"'):
483 #print "FOUND:", item.storage.value, resource.items_dict['name'].storage.value
484 reslist.append(res)
485 #print item.name,item.storage.value, resource.resourcetype, resource.items_dict['name'].storage.value
486
487
488 #if item.value == resource.items_dict['name']:
489
490 return reslist
491
492 def parse_configfile(self, configstring, callback):
493 """
494 parses the configstring: removes the comment lines,
495 """
496
497 cf = ''
498 for line in configstring.splitlines(True):
499 cf += line[:line.find('#')]+'\n' # remove comments
500
501 blocks = config2blocks(cf)
502
503 #b = 1
504 for block in blocks:
505 #print "%d of %d blocks" % (b, len(blocks))
506 #b+=1
507 if block['level'] == 1:
508 logger.debug( block['name'].lower() )
509 # TODO: can we call Resource() directly?
510
511 newres = self.createResource( block['name'].lower() )
512 if callback is not None:
513 callback("creating new " + block['name'].lower() + " resource" ) # call callback function after every resource
514 if newres != None:
515 newres.parse_string(cf[block['start']+1:block['end']])
516 if callback is not None:
517 callback(newres.getName()) # call callback function after every resource
518 else:
519 logger.error('did not get a ressource back, maybe ressource not allowed in this configuration?')
520
521
522
523 def __str__(self):
524 """
525 creates the string representation of the configuration file
526 by recursively calling the string representation of all resources
527 """
528 s = '' # string representation
529 now = time.asctime()
530
531 # set verid string to the date
532 # TODO: Make this configurable
533 #dirconfs = self.getResourcesListByResType('director')
534 #
535 #for d in dirconfs:
536 # try:
537 # d.items_dict['verid'].storage.setValue('" | configuration file created on :' + now + '"' )
538 # except:
539 # logger.error('verid setting impossible, maybe object not yet initialized?')
540
541
542
543 s += '# config file written on ' + now + '\n'
544 for v in self.resources:
545 s += str(v)
546 s += "\n"
547 return s
548
549
550 def selfcheck(self):
551 """
552 selfcheck for valid conditions
553 TODO documentation
554 """
555 retval = True
556 logger.debug(self.__class__.__name__ + "Selfcheck starts")
557 # check if defined resources are allowed
558 for res in self.resources:
559 if res.resourcetype not in self.validresourcesset:
560 logger.error( "Error: resource name " + res.resourcetype + " not allowed")
561 retval = False
562 else:
563 logger.debug(res.resourcetype + " is allowed")
564
565 # check if at least one of the needed resources is defined
566 for res in self.validresourcesset:
567 if self.getResourcesListByResType(res) == None:
568 logger.error("Resourctype " + " is missing!")
569 retval = False
570 else:
571 logger.debug(res + " is configured, OK")
572 logger.debug(self.__class__.__name__ + "Selfcheck end")
573 return retval
574
575
576
577 def createResource(self, resourcetype ):
578 '''
579 creates a ressource of type "ressourcetype"
580 inside of this bacula configuration
581 '''
582 if resourcetype not in self.validresourcesset:
583 logger.error("ressource type \"" + resourcetype + "\" not allowed in this configuration")
584 else:
585 if self.onlinehelp.has_key(resourcetype):
586 helpurl = self.onlinehelp[resourcetype]
587 else:
588 helpurl = ''
589 newres = Resource( self, resourcetype, indentlevel = 0, onlinehelpURL = helpurl)
590 self.resources.append( newres )
591
592 return newres
593
594 def deleteResource(self, resource ):
595 '''
596 delete given resource from own resources
597 '''
598 try:
599 self.resources.remove(resource)
600 retval = True
601 except:
602 logger.error("could not remove resource")
603 retval = False
604 return retval
605
606
607
608 def __create_validresources_set__(self):
609 '''
610 Create a set of allowed ressources
611 inside of this bacula configuration
612 '''
613 self.validresourcesset = set()
614 for validresource in self.validresources:
615 self.validresourcesset.add(validresource[0]) # add name
616 #print self.validresourcesset
617
618
619
620
621class DirectorConfig(BaculaConfig):
622 '''
623 Class for configuration of one director ( bacula-dir.conf )
624 '''
625 def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None):
626 self.shortname = 'dird'
627 self.longname = 'director'
628 self.onlinehelp = onlinehelp[self.longname]
629 self.resources = []
630 BaculaConfig.__init__(self, configAsString, parentdatacenter, processEventsCallback, filename)
631
632
633
634class StorageDaemonConfig(BaculaConfig):
635 '''
636 Class for configuration of one storage daemon ( bacula-sd.conf )
637 '''
638 def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None):
639 self.shortname = 'stored'
640 #self.longname = 'storagedaemon'
641 self.longname = 'storage'
642 self.onlinehelp = onlinehelp['storage']
643 self.resources = []
644 BaculaConfig.__init__(self, configAsString, parentdatacenter, processEventsCallback, filename)
645
646
647class FileDaemonConfig(BaculaConfig):
648 '''
649 Class for configuration of one storage daemon ( bacula-fd.conf )
650 '''
651 def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None):
652 self.shortname = 'filed'
653 self.longname = 'filedaemon'
654 self.onlinehelp = onlinehelp['client']
655 self.resources = []
656 BaculaConfig.__init__(self, configAsString, parentdatacenter , processEventsCallback, filename)
657
658
659
660 def createFdConfigFromDirClient(self, dirClient = None):
661 '''
662 create a Filedaemon Config from a
663 Client Config in Director
664 we have to set some required values to defaults
665 TODO: we should be able to use different defaults for
666 different classes of clients (windows, linux, solaris etc.)
667 '''
668
669 directors = self.getResourcesListByResType('director')
670 if len(directors) != 1:
671 logger.error("here we should have one director!")
672 return None
673
674 messages = self.getResourcesListByResType('messages')
675 if len(messages) != 1:
676 logger.error("here we should have one messages!")
677 return None
678
679 filedaemons = self.getResourcesListByResType('filedaemon') # same as client
680 if len(filedaemons) != 1:
681 logger.error("here we should have one filedaemon!")
682 return None
683
684 dirDirector = dirClient.parentconfig.getResourcesListByResType('director')[0]
685
686 # get password and dirname from director config
687 directors[0].items_dict['name'].storage.value = dirDirector.items_dict['name'].storage.value
688 directors[0].items_dict['password'].storage.value = dirClient.items_dict['password'].storage.value
689 filedaemons[0].items_dict['name'].storage.value = dirClient.items_dict['name'].storage.value
690
691 self.name = filedaemons[0].items_dict['name'].storage.value
692
693 # TODO: make the following configurable
694 filedaemons[0].items_dict['workingdirectory'].storage.value = '/var/lib/bacula'
695 filedaemons[0].items_dict['piddirectory'].storage.value = '/var/run'
696
697 messages[0].items_dict['name'].storage.value = 'Standard'
698 messagesentry = directors[0].items_dict['name'].storage.value + ' = all, !skipped, !restored'
699 messages[0].items_dict['director'].storage.setValue(messagesentry)
700 #messages[0].items_dict['director'].storage.value = directors[0].items_dict['name'].storage.value + ' = all, !skipped, !restored'
701
702 #filedaemons[0].items_dict['password'].storage.value = dirClient.items_dict['password'].storage.value
703
704 #dirClient.parentconfig.getResourceByName(parentname).items_dict['name']
705
706
707
708 #self.items_dict['password'] = DirDirector.items_dict['password']
709
710
711
712class ConsoleConfig(BaculaConfig):
713 '''
714 Class for configuration of a console ( bconsole.conf )
715 '''
716 def __init__(self, configAsString, parentdatacenter = None, processEventsCallback = None, filename = None):
717 self.shortname = 'console'
718 self.longname = 'console'
719 self.onlinehelp = onlinehelp[self.longname]
720 self.resources = []
721 BaculaConfig.__init__(self, configAsString, parentdatacenter, processEventsCallback, filename)
722
723
724
725
726
727
728class DataCenter(object):
729 '''
730 A datacenter is the sum of all configurations that
731 make a running bacula system, i.e. director, storaged,
732 console and filed configurations
733 A datacenter resides in an own subdirectory
734 and has subdirs for consoles, directors, storagedaemons and filedaemons
735 /datacenter/
736 /datacenter/consoles/
737 /datacenter/directors/
738 /datacenter/storagedaemons/
739 /datacenter/filedaemons/
740
741 this directory structure is hidden inside of a tar.gz file
742 '''
743
744 def __createDataCenterDirs__(self, basedir):
745 olddir = os.getcwdu()
746 os.mkdir(basedir)
747 #os.chdir(basedir)
748 for subdir in self.subdirs:
749 os.mkdir( os.path.normpath(basedir+'/'+subdir) )
750 #os.chdir(olddir)
751
752
753
754 def __init__(self, targzfile, callbackfunction = None):
755 '''
756 extract the targz file,
757 open the *.conf file inside the
758 subdirs and create the configurations accordingly
759 '''
760 self.filename = targzfile
761 self.subdirs = ['consoles','directors','storagedaemons','filedaemons']
762 self.directors = set()
763 self.consoles = set()
764 self.storagedaemons = set()
765 self.filedaemons = set()
766 tmpdir = tempfile.mkdtemp(prefix = 'dassmodus-')
767 # TODO: delete this after use
768
769 # if the targz file does not exist, then create an empty
770 # datacenter file
771 if not os.path.isfile(targzfile):
772 print targzfile + ' does not exist, creating new file'
773 self.name='datacenter'
774 os.mkdir(tmpdir + '/' + self.name)# datacentername
775 for subdir in self.subdirs:
776 os.mkdir(tmpdir + '/' + self.name + '/' + subdir)
777
778 # create a new tarfile
779 tar = tarfile.open(targzfile, "w:gz")
780 dirs = glob.glob(os.path.normpath(tmpdir + '/*/*'))
781 for d in dirs:
782 tar.add(d, arcname = d.replace(tmpdir + '/',''))
783 tar.close()
784
785
786 # extract subdirs inside of the tarfile into tmp dir
787 tar = tarfile.open(targzfile, "r:gz")
788
789 #olddir = os.getcwdu()
790 #os.chdir(self.tmpdir)
791 tar.extractall(path = tmpdir)
792 tar.close
793
794 basedir = os.listdir(tmpdir)
795 datacenterdir = os.path.normpath(tmpdir + '/' + basedir[0])
796 #os.chdir(basedir[0]) # change into the
797
798 self.name = basedir[0]
799
800
801
802 for subdir in self.subdirs:
803 files = glob.glob(os.path.normpath(datacenterdir + '/' + subdir + '/*.conf' ) )
804 for filename in files:
805 if callbackfunction is not None:
806 callbackfunction('parsing file ' + filename)
807
808 if subdir == 'directors':
809 configstring = open(filename).read()
810 conf = DirectorConfig(configstring, parentdatacenter = self, filename = filename, processEventsCallback = callbackfunction )
811 self.directors.add(conf)
812
813 elif subdir == 'storagedaemons':
814 configstring = open(filename).read()
815 conf = StorageDaemonConfig(configstring, parentdatacenter = self, filename = filename, processEventsCallback = callbackfunction)
816 self.storagedaemons.add(conf)
817
818 elif subdir == 'filedaemons':
819 configstring = open(filename).read()
820 conf = FileDaemonConfig(configstring, parentdatacenter = self, filename = filename ,processEventsCallback = callbackfunction)
821 self.filedaemons.add(conf)
822
823 elif subdir == 'consoles':
824 configstring = open(filename).read()
825 conf = ConsoleConfig(configstring, parentdatacenter = self, filename = filename, processEventsCallback = callbackfunction)
826 self.consoles.add(conf)
827 else:
828 raise NameError('help me')
829
830
831 # remove temporary files
832 shutil.rmtree(tmpdir)
833 #os.chdir(olddir) # restore working dir
834
835
836 def getName(self):
837 return self.name
838
839 def setName(self, name):
840 self.name = name
841
842 def setFileName(self, filename):
843 self.filename = filename
844 self.name = os.path.basename(filename).split('.')[0]
845
846 def importConfigurationFile(self, filename, configtype):
847 '''
848 import a existing configuration file into this
849 datacenter.
850 allowed configtypes: filedaemon, storagedaemon, console, director
851 '''
852 print "importing file " + filename + ' ' + configtype
853 if configtype == 'filedaemon':
854 configstring = open(filename).read()
855 conf = FileDaemonConfig(configstring, filename = filename)
856 self.filedaemons.add(conf)
857
858 elif configtype == 'storagedaemon':
859 configstring = open(filename).read()
860 conf = StorageDaemonConfig(configstring, filename = filename)
861 self.storagedaemons.add(conf)
862
863 elif configtype == 'console':
864 configstring = open(filename).read()
865 conf = ConsoleConfig(configstring, filename = filename)
866 self.consoles.add(conf)
867
868 elif configtype == 'director':
869 configstring = open(filename).read()
870 conf = DirectorConfig(configstring, filename = filename)
871 self.directors.add(conf)
872
873 else:
874 raise NameError('Unknown configtype, allowed : filedaemon, storagedaemon, console, director')
875 return False
876
877
878 def deleteConfiguration(self, configuration):
879 '''
880 delete the refered configuration resource/configfile
881 '''
882 for configtype in [self.directors, self.filedaemons, self.storagedaemons, self.consoles]:
883 if configuration in configtype:
884 configtype.remove(configuration)
885 del configuration
886 break
887
888
889
890
891 def checkFileDaemonsIntegrity(self):
892 '''
893 Possible configuration problems:
894 1a: - dirconf refers to filedaemon, but no filedaemon conf exists
895 1b: - dirconf refers to filedaemon, but filedaemon conf has wrong password for this director
896 2a: - filedaemon conf refers to Director that does not exist
897 2b: - filedaemon conf refers to existing Director, but password is wrong for this director
898 1b and 2b are the same
899
900 this leads to
901 3 cases:
902 - Dir does not exist,
903 - Filedaemon does not exist,
904 - Passwords do not match
905 '''
906
907 s = '' # return string
908 director_filedaemons_passwords = {} # dictionary of director -> filedaemonname -> password, extracted from dirconfs
909 filedaemons_directors_passwords = {} # dictionary of filedaemon -> director -> password , extracted from fdconfs
910
911 # collect info about all defined filedaemons/clients in ALL Director Configurations
912 for dir in self.directors:
913 dirfds = dir.getResourcesListByResType('client')
914 director_filedaemons_passwords[dir.getName()] = {}
915 for fd in dirfds:
916 director_filedaemons_passwords[dir.getName()][fd.getName()] = fd.getPassword()
917
918 # collect info about all definied Directors in all Filedaemon Configurations
919 for fdconf in self.filedaemons:
920 filedaemons_directors_passwords[fdconf.getName()] = {}
921 for dir in fdconf.getResourcesListByResType('director'):
922 filedaemons_directors_passwords[fdconf.getName()][dir.getName()] = dir.getPassword()
923
924 #print "Filedaemon View:"
925 # check from the view of filedaemons configurations
926 for f,d in filedaemons_directors_passwords.iteritems():
927 for dir,pw in d.iteritems():
928 #print f,dir,pw
929 if director_filedaemons_passwords.has_key(dir): # do we have a director config of this director?
930 if director_filedaemons_passwords[dir].has_key(f): # has this dir a definition of this fd?
931 if director_filedaemons_passwords[dir][f] == pw: # and is the password identical?
932 s += "OK: Filedaemon %s has definition of Director %s and password is the same\n" % (f,dir)
933 #s += "INFO: Filedaemon %s has definition of Director %s and password is the same: %s\n" % (f,dir,pw)
934 else:
935 s+= "ERROR: Filedaemon %s has definition of Director %s but password is different: %s != %s\n" % (f,dir,pw,director_filedaemons_passwords[dir][f])
936 else:
937 s+= "ERROR: Filedaemon %s has not a definition of Director %s\n" % (dir,f)
938 else:
939 s+= "ERROR: Director configuration for %s is MISSING , it was referenced by configfile of %s\n" % (dir,f)
940
941 #print "Director View:"
942 # check from the view of director configurations
943 for d,f in director_filedaemons_passwords.iteritems():
944 for fs,pwd in f.iteritems():
945 #print d,fs,pwd
946 if filedaemons_directors_passwords.has_key(fs): # do we have a fd-conf for this filedaemon?
947 if filedaemons_directors_passwords[fs].has_key(d): # does this fd-conf have a reference to this dir?
948 if filedaemons_directors_passwords[fs][d] == pwd: # is the password identical?
949 s+= "OK: Director %s is known by Filedaemon %s and passwords are the same.\n" %(d, fs)
950 #s+= "OK: Director %s is known by Filedaemon %s and passwords are the same: %s\n" %(d, fs, pwd)
951 else:
952 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])
953 else:
954 s+= "WARNING: Director %s does not reference Filedaemon %s,that may be OK\n" % (dir, fs)
955 # Not every Dir has to reference every Filedaemon
956 else:
957 s+= "ERROR: fd-conf is MISSING for Filedaemon %s : please import or create.\n" %(fs)
958
959 return s
960
961
962
963
964
965 def checkStorageDaemonsIntegrity(self):
966 '''
967 Possible configuration problems:
968 1a: - dirconf refers to storage, but no storagedaemon conf exists with this storage
969 1b: - dirconf refers to storage, storagedaemon has this storage but has wrong password for this director
970
971 2a: - StorageDaemon conf refers to Director that does not exist
972 2b: - StorageDaemon conf refers to existing Director, but password is wrong for this director
973
974 3a: - dirconf refers to Device or Autochanger that is not defined in Storage Daemon
975 3b: - Storage Daemon defines Device or Autochanger that is not used by any Director
976 (info, not problem, may be used in a autochanger)
977 4a: - dirconf refers to Device or Autochanger that is defined, but the *MediaType* is not the same
978 '''
979
980 '''
981 Datastructures we need:
982
983 # Director view
984 directorName - storageName - password
985 - Device
986 - Media Type
987 - AutoChanger?
988
989 # Storage view
990 storageName - directorName - Password
991 - DeviceName - MediaType
992 - AutochangerName[Devices]
993 '''
994
995 director_storages = {}
996 storage_directors = {}
997
998 # collect info about all defined storages in ALL Director Configurations
999 for dir in self.directors:
1000 dirstorages = dir.getResourcesListByResType('storage')
1001 director_storages[dir.getName()] = {}
1002 for storage in dirstorages:
1003 director_storages[dir.getName()]['password'] = storage.getPassword()
1004 director_storages[dir.getName()]['device'] = storage.getItemValue('device')
1005 director_storages[dir.getName()]['mediatype'] = storage.getItemValue('mediatype')
1006
1007 # collect info about all definied Directors in all Filedaemon Configurations
1008 for sdconf in self.storagedaemons:
1009 storage_directors[sdconf.getName()] = {}
1010
1011 for dir in sdconf.getResourcesListByResType('director'):
1012 storage_directors[sdconf.getName()][dir.getName()] = dir.getPassword()
1013
1014 for device in sdconf.getResourcesListByResType('device'):
1015 storage_directors[sdconf.getName()][device.getName()] = device.getItemValue('mediatype')
1016
1017 for autochanger in sdconf.getResourcesListByResType('autochanger'):
1018 storage_directors[sdconf.getName()][autochanger.getName()] = device.getItemValue('device')
1019
1020 '''
1021 TODO: implement the checks and output
1022 TODO: implement test cases for this function!
1023 '''
1024
1025
1026 def checkConsolesIntegrity(self):
1027 '''
1028 check if we have a console conf for every main dir in each dirconf (passwds)
1029 and if we have a console for every named console in each dir (names)
1030 including passwords check
1031 '''
1032 '''
1033 possible configuration problems:
1034 - no console.conf for director entry in director configuration (check passwds)
1035 - no console.conf for named console entries in director configurations
1036 - console.conf for named console entry but passwords not the same
1037 - console.conf, whose password does not match name/password of named consoles and
1038 not password of anonymous console
1039 - console.conf contains console entry that refers a director that is not defined inside of this console conf
1040
1041 Datastructures we need:
1042 # director view:
1043 directorname - mainpassword # anonymous/default console
1044 - consolename - password # named console
1045 # console view:
1046 console - directorname - password # anonymous/default console
1047 - consolename - directorname # named console, refers to defined director inside of this configfile
1048 # consoles can have multiple director entries, and multiple console entries referring these directors
1049 '''
1050
1051 directors_consoles = {}
1052 consoles_directors = {}
1053
1054 for dir in self.directors: # look in all directors of our DataCenter
1055 dirname = dir.getName()
1056 defaultconsolepassword = dir.getPassword()
1057 #dir.getResourcesListByResType('director')[0].getPassword() # we can only have 1 director entry per director conf
1058 directors_consoles[dirname]={}
1059 directors_consoles[dirname]['defaultconsole'] = defaultconsolepassword
1060
1061 for console in dir.getResourcesListByResType('console'):
1062 directors_consoles[dirname][console.getName()] = console.getPassword()
1063
1064
1065 defconsoles = 0
1066 for consoleconf in self.consoles: # check all consoles in DC
1067 #print consoleconf
1068 consolename = consoleconf.getName()
1069 if consolename == '': # we have an defaultconsole
1070 defconsoles += 1
1071 consolename = '__defaultconsole__%d' % (defconsoles)
1072 consoles_directors[consolename]={}
1073 consoles_directors[consolename]['directors']= {}
1074 consoles_directors[consolename]['consoles'] = {}
1075
1076 for dir in consoleconf.getResourcesListByResType('director'):
1077 consoles_directors[consolename]['directors'][dir.getName()] = dir.getPassword()
1078
1079 for con in consoleconf.getResourcesListByResType('console'):
1080 consoles_directors[consolename]['consoles'][con.getItemValue('director')] = {}
1081 consoles_directors[consolename]['consoles'][con.getItemValue('director')]['name'] = con.getName()
1082 consoles_directors[consolename]['consoles'][con.getItemValue('director')]['password'] = con.getPassword()
1083 print "break"
1084
1085
1086 s = ''
1087 # check the directorconfig view
1088 for dir in directors_consoles:
1089 defaultpw = directors_consoles[dir]['defaultconsole']
1090 for consolename, dirinfo in consoles_directors.iteritems(): # console resource in console config
1091 unreferred_dirs = set() # main directors in dirconfs -> Default Consoles
1092 if consolename.startswith('__defaultconsole__'): # we have a default console
1093 for d,pw in dirinfo['directors'].iteritems():
1094 unreferred_dirs.add(d) # remember dirs
1095 if d == dir:
1096 unreferred_dirs.remove(d)
1097 s += "found defaultconsole in console.conf for dir %s" % ( d )
1098 if defaultpw == pw:
1099 s += " and passwords are the same"
1100 else:
1101 s += " and passwords are different!"
1102 else:
1103 s+= "default console not configured for this dir"
1104
1105 #print out unreferred main dir confs
1106 for unrefdir in unreferred_dirs:
1107 s+= "director %s not referred by any console.conf as default console" % (unrefdir)
1108
1109 else: # named console
1110 print "found named console" ,consolename
1111 for c, consdir in dirinfo['consoles'].iteritems():
1112 print c, consdir
1113
1114 for d,pw in dirinfo['directors'].iteritems():
1115 #print d,pw
1116 print d, pw, dirinfo['consoles'][d]['password']
1117 #print consolename, dirinfo,
1118 try:
1119 print directors_consoles[dir][d]
1120 except:
1121 print "no named console for dir " , d
1122
1123
1124
1125 # check if we have
1126
1127
1128
1129
1130 #unreferred_dirs.add(d) # remember dirs
1131 if d == dir:
1132 unreferred_dirs.remove(d)
1133 s += "found defaultconsole in console.conf for dir %s" % ( d )
1134 if defaultpw == pw:
1135 s += " and passwords are the same"
1136 else:
1137 s += " and passwords are different!"
1138 else:
1139 s+= "default console not configured for this dir"
1140
1141 #print out unreferred main dir confs
1142 for unrefdir in unreferred_dirs:
1143 s+= "director %s not referred by any console.conf as default console" % (unrefdir)
1144
1145
1146
1147 #for conscons in consoles_directors
1148 # print conscons
1149 #referred_dirs.add(conscons[dir])
1150
1151 #for consdir in console['directors']:
1152 ''' now we have collected all info about defined dirs and consoles in this Datacenter
1153 Checks:
1154 Director view
1155 A: iterate over all directors,
1156 I:check if we have a anonymous console conf that refers to the given password
1157 II:iterate over every named console:
1158 1.: do we have a director entry in the console conf with the correct name TODO:(address/port)
1159 2.: we have a console entry for this named console with the correct name and dirname
1160 AND do we have a director with this dirname defined inside of this particular console conf
1161
1162
1163 Console view
1164 B: iterate over all consoles,
1165 I check if we have a director for every defined director in console (check names)
1166 II check if our console definitions refer to defined Director resources inside of this consoleconfig (If we have consoles)
1167 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
1168 IV check for not by console entries referred directors if the console Director Passwd is identical to the Director default password
1169
1170 '''
1171
1172 '''
1173 TODO: implement the checks and output
1174 TODO: implement test cases for this function!
1175 '''
1176
1177
1178
1179 def safeDatacenter(self):
1180 '''
1181 safe the Datacenter under the known filename
1182 '''
1183 self.writeDatacenter(os.path.dirname(self.filename))
1184
1185 def writeDatacenter(self,targetdir):
1186 '''
1187 create the subdirs
1188 write the configs
1189 create tarfile
1190 '''
1191 tar = tarfile.open(targetdir + '/' + self.name + '.dmdz','w:gz')
1192 writedir = tempfile.mkdtemp(prefix = 'dassmodus-')
1193 writebasedir = writedir+'/' + self.name
1194 self.__createDataCenterDirs__(writebasedir)
1195
1196 for director in self.directors:
1197 #print director.getName()
1198 writefile = open(writebasedir + '/' + 'directors' + '/' + director.getName() + '.conf','w')
1199 writefile.write(str(director))
1200 writefile.close()
1201
1202 for storagedaemon in self.storagedaemons:
1203 #print storagedaemon.getName()
1204 writefile = open(writebasedir + '/' + 'storagedaemons' + '/' + storagedaemon.getName() + '.conf','w')
1205 writefile.write(str(storagedaemon))
1206 writefile.close()
1207
1208 for filedaemon in self.filedaemons:
1209 #print storagedaemon.getName()
1210 writefile = open(writebasedir + '/' + 'filedaemons' + '/' + filedaemon.getName() + '.conf','w')
1211 writefile.write(str(filedaemon))
1212 writefile.close()
1213
1214 consolenr = 0
1215 for console in self.consoles:
1216 #print storagedaemon.getName()
1217 name = console.getName()
1218 if name == '':
1219 consolenr += 1
1220 name = "bconsole-%d" % ( consolenr )
1221 writefile = open(writebasedir + '/' + 'consoles' + '/' + name + '.conf','w')
1222 writefile.write(str(console))
1223 writefile.close()
1224
1225
1226 #os.chdir(writedir)
1227
1228 dirs = glob.glob(os.path.normpath(writedir + '/*/*'))
1229 for d in dirs:
1230 tar.add(d, arcname = d.replace(writedir+'/',''))
1231 tar.close()
1232
1233 # remove temp dir
1234 shutil.rmtree(writedir)
1235
1236
1237
1238
1239
1240if __name__ == "__main__":
1241 #msgsstore = store_msgs("root@localhost = mount")
1242 import logging
1243 logging.basicConfig(level=logging.DEBUG,
1244 format='%(asctime)s %(levelname)s \t (%(module)s:%(lineno)d) %(message)s ',
1245 #filename='vanHelsing.log',
1246 filemode='w')
1247 cfg = '''
1248 Pool {
1249 LabelFormat = "${Year}-${Month:p/2/0/r}-${Day:p/2/0/r}"
1250}
1251'''
1252 dconfig = DirectorConfig(cfg)
Note: See TracBrowser for help on using the repository browser.