summaryrefslogtreecommitdiffstats
path: root/src/lib/Server/Admin/Pull.py
diff options
context:
space:
mode:
authorNarayan Desai <desai@mcs.anl.gov>2008-03-24 04:15:41 +0000
committerNarayan Desai <desai@mcs.anl.gov>2008-03-24 04:15:41 +0000
commit9e31f1dd76c29d99fb5a16f0a2d6752cf5ead1c9 (patch)
tree4ed0e043ce262a649bf247db27a6fdf5f3bab4b9 /src/lib/Server/Admin/Pull.py
parentd2a7c0a153e547ff327cfac29f10788f7926a06b (diff)
downloadbcfg2-9e31f1dd76c29d99fb5a16f0a2d6752cf5ead1c9.tar.gz
bcfg2-9e31f1dd76c29d99fb5a16f0a2d6752cf5ead1c9.tar.bz2
bcfg2-9e31f1dd76c29d99fb5a16f0a2d6752cf5ead1c9.zip
Rework bcfg2-admin pull
- forward port Cfg and SSHbase support - reimplement admin mode - add verbose flag, and implement initial interactive mode, also force mode git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@4446 ce84e21b-d406-0410-9b95-82705330c041
Diffstat (limited to 'src/lib/Server/Admin/Pull.py')
-rw-r--r--src/lib/Server/Admin/Pull.py128
1 files changed, 76 insertions, 52 deletions
diff --git a/src/lib/Server/Admin/Pull.py b/src/lib/Server/Admin/Pull.py
index 1071b5100..ede4271fb 100644
--- a/src/lib/Server/Admin/Pull.py
+++ b/src/lib/Server/Admin/Pull.py
@@ -1,89 +1,110 @@
-import binascii, lxml.etree, time
+import binascii, difflib, getopt, lxml.etree, time, ConfigParser
import Bcfg2.Server.Admin
class Pull(Bcfg2.Server.Admin.Mode):
'''Pull mode retrieves entries from clients and integrates the information into the repository'''
- __shorthelp__ = 'bcfg2-admin pull <client> <entry type> <entry name>'
+ __shorthelp__ = 'bcfg2-admin pull [-v] [-f] [-I] <client> <entry type> <entry name>'
__longhelp__ = __shorthelp__ + '\n\tIntegrate configuration information from clients into the server repository'
- def __init__(self):
- Bcfg2.Server.Admin.Mode.__init__(self)
-
+ def __init__(self, configfile):
+ Bcfg2.Server.Admin.Mode.__init__(self, configfile)
+ cp = ConfigParser.ConfigParser()
+ cp.read([configfile])
+ self.repo = cp.get('server', 'repository')
+ self.log = False
+ self.mode = 'interactive'
+
def __call__(self, args):
Bcfg2.Server.Admin.Mode.__call__(self, args)
- self.PullEntry(args[0], args[1], args[2])
+ try:
+ opts, gargs = getopt.getopt(args, 'vfI')
+ except:
+ print self.__shorthelp__
+ raise SystemExit(0)
+ for opt in opts:
+ if opt[0] == '-v':
+ self.log = True
+ elif opt[0] == '-f':
+ self.mode = 'force'
+ elif opt[0] == '-I':
+ self.mode == 'interactive'
+ self.PullEntry(gargs[0], gargs[1], gargs[2])
- def PullEntry(self, client, etype, ename):
- '''Make currently recorded client state correct for entry'''
- # FIXME Pull.py is _way_ too interactive
+ def BuildNewEntry(self, client, etype, ename):
+ '''construct a new full entry for given client/entry from statistics'''
+ new_entry = {'type':etype, 'name':ename}
sdata = self.load_stats(client)
- if sdata.xpath('.//Statistics[@state="dirty"]'):
- state = 'dirty'
- else:
- state = 'clean'
- # need to pull entry out of statistics
- sxpath = ".//Statistics[@state='%s']/Bad/ConfigFile[@name='%s']/../.." % (state, ename)
+ # no entries if state != dirty
+ sxpath = ".//Statistics[@state='dirty']/Bad/ConfigFile[@name='%s']/../.." % \
+ (ename)
sentries = sdata.xpath(sxpath)
if not len(sentries):
self.errExit("Found %d entries for %s:%s:%s" % \
(len(sentries), client, etype, ename))
else:
- print "Found %d entries for %s:%s:%s" % \
- (len(sentries), client, etype, ename)
+ if self.log:
+ print "Found %d entries for %s:%s:%s" % \
+ (len(sentries), client, etype, ename)
maxtime = max([time.strptime(stat.get('time')) for stat in sentries])
- print "Found entry from", time.strftime("%c", maxtime)
+ if self.log:
+ print "Found entry from", time.strftime("%c", maxtime)
statblock = [stat for stat in sentries \
if time.strptime(stat.get('time')) == maxtime]
entry = statblock[0].xpath('.//Bad/ConfigFile[@name="%s"]' % ename)
if not entry:
- self.errExit("Could not find state data for entry; rerun bcfg2 on client system")
+ self.errExit("Could not find state data for entry\n" \
+ "rerun bcfg2 on client system")
cfentry = entry[-1]
badfields = [field for field in ['perms', 'owner', 'group'] \
if cfentry.get(field) != cfentry.get('current_' + field) and \
cfentry.get('current_' + field)]
if badfields:
- m_updates = dict([(field, cfentry.get('current_' + field)) \
- for field in badfields])
- print "got metadata_updates", m_updates
- else:
- m_updates = {}
-
- if 'current_bdiff' in cfentry.attrib:
- data = False
+ for field in badfields:
+ new_entry[field] = cfentry.get('current_%s' % field)
+ # now metadata updates are in place
+ if 'current_bfile' in cfentry.attrib:
+ new_entry['text'] = binascii.a2b_base64(cfentry.get('current_bfile'))
+ elif 'current_bdiff' in cfentry.attrib:
diff = binascii.a2b_base64(cfentry.get('current_bdiff'))
- elif 'current_diff' in cfentry.attrib:
- data = False
- diff = cfentry.get('current_diff')
- elif 'current_bfile' in cfentry.attrib:
- data = binascii.a2b_base64(cfentry.get('current_bfile'))
- diff = False
+ new_entry['text'] = '\n'.join(difflib.restore(diff.split('\n'), 1))
else:
- if not m_updates:
- self.errExit("having trouble processing entry. Entry is:\n" \
- + lxml.etree.tostring(cfentry))
- else:
- data = False
- diff = False
+ print "found no data::"
+ print lxml.etree.tostring(cfentry)
+ raise SystemExit(1)
+ return new_entry
- if diff:
- print "Located diff:\n %s" % diff
- elif data:
- print "Found full (binary) file data"
- if m_updates:
- print "Found metadata updates"
+ def Choose(self, choices):
+ '''Determine where to put pull data'''
+ if self.mode == 'interactive':
+ # FIXME improve bcfg2-admin pull interactive mode to add new entries
+ print "Plugin returned choice:"
+ if choices[0].all:
+ print " => global entry"
+ elif choices[0].group:
+ print " => group entry: %s (prio %d)" % (choices[0].group, choices[0].prio)
+ else:
+ print " => host entry: %s" % (choices[0].hostname)
+ if raw_input("Use this entry? [yN]: ") in ['y', 'Y']:
+ return choices[0]
+ return False
+ else:
+ # mode == 'force'
+ return choices[0]
- if not diff and not data and not m_updates:
- self.errExit("Failed to locate diff or full data or metadata updates\nStatistics entry was:\n%s" % lxml.etree.tostring(cfentry))
+ def PullEntry(self, client, etype, ename):
+ '''Make currently recorded client state correct for entry'''
+ new_entry = self.BuildNewEntry(client, etype, ename)
try:
- bcore = Bcfg2.Server.Core.Core({}, self.configfile)
+ bcore = Bcfg2.Server.Core.Core(self.repo, [], ['Cfg', 'SSHbase', 'Metadata'],
+ 'foo', False)
except Bcfg2.Server.Core.CoreInitError, msg:
self.errExit("Core load failed because %s" % msg)
- [bcore.fam.Service() for _ in range(10)]
+ [bcore.fam.Service() for _ in range(5)]
while bcore.fam.Service():
pass
- m = bcore.metadata.get_metadata(client)
+ meta = bcore.metadata.get_metadata(client)
# find appropriate plugin in bcore
glist = [gen for gen in bcore.generators if
gen.Entries.get(etype, {}).has_key(ename)]
@@ -92,8 +113,11 @@ class Pull(Bcfg2.Server.Admin.Mode):
+ "%s" % ([g.__name__ for g in glist]))
plugin = glist[0]
try:
- plugin.AcceptEntry(m, 'ConfigFile', ename, diff, data, m_updates)
+ choices = plugin.AcceptChoices(new_entry, meta)
+ specific = self.Choose(choices)
+ if specific:
+ plugin.AcceptPullData(specific, new_entry, self.log)
except Bcfg2.Server.Plugin.PluginExecutionError:
self.errExit("Configuration upload not supported by plugin %s" \
% (plugin.__name__))
- # svn commit if running under svn
+ # FIXME svn commit if running under svn