1 | #!/usr/bin/env python
|
---|
2 | # -*- coding: utf-8 -*-
|
---|
3 |
|
---|
4 | # $Id: bacresources.py 12826 2011-04-03 16:49:56Z pstorz $
|
---|
5 |
|
---|
6 | import time
|
---|
7 |
|
---|
8 | #from directive import *
|
---|
9 | #from configrules import *
|
---|
10 | from auto_configrules import *
|
---|
11 |
|
---|
12 | import logging
|
---|
13 | from auto_onlinehelp import *
|
---|
14 | logger = logging.getLogger('bacresources')
|
---|
15 |
|
---|
16 | import os
|
---|
17 | import tarfile
|
---|
18 | import tempfile
|
---|
19 | import glob
|
---|
20 | import shutil
|
---|
21 | import string
|
---|
22 | import random
|
---|
23 |
|
---|
24 | #TODO: we have a problem if inside of a password there is a special character like {}
|
---|
25 |
|
---|
26 |
|
---|
27 | def 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 |
|
---|
34 | def 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 | '''
|
---|
101 | Class for ressources inside of configurations
|
---|
102 | hierarchy:
|
---|
103 | Config -> Resource -> Items+
|
---|
104 | |-> Resource -> Items
|
---|
105 | '''
|
---|
106 |
|
---|
107 | class 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 |
|
---|
357 | class 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 |
|
---|
621 | class 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 |
|
---|
634 | class 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['storagedaemon']
|
---|
643 | self.resources = []
|
---|
644 | BaculaConfig.__init__(self, configAsString, parentdatacenter, processEventsCallback, filename)
|
---|
645 |
|
---|
646 |
|
---|
647 | class 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 |
|
---|
712 | class 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 |
|
---|
728 | class 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 |
|
---|
1240 | if __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)
|
---|