summaryrefslogtreecommitdiffstats
path: root/src/lib/Server/Plugins
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/Plugins
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/Plugins')
-rw-r--r--src/lib/Server/Plugins/Cfg.py144
-rw-r--r--src/lib/Server/Plugins/SSHbase.py31
2 files changed, 79 insertions, 96 deletions
diff --git a/src/lib/Server/Plugins/Cfg.py b/src/lib/Server/Plugins/Cfg.py
index aebce6188..f3e485517 100644
--- a/src/lib/Server/Plugins/Cfg.py
+++ b/src/lib/Server/Plugins/Cfg.py
@@ -1,8 +1,7 @@
'''This module implements a config file repository'''
__revision__ = '$Revision$'
-import binascii, difflib, logging, os, re, tempfile, \
- xml.sax.saxutils, Bcfg2.Server.Plugin, lxml.etree
+import binascii, logging, os, re, tempfile, Bcfg2.Server.Plugin
logger = logging.getLogger('Bcfg2.Plugins.Cfg')
@@ -53,13 +52,15 @@ class CfgEntry(object):
if entry.get('encoding') == 'base64':
entry.text = binascii.b2a_base64(self.data)
else:
- entry.text = self.data
+ entry.text = self.data
+ if not entry.text:
+ entry.set('empty', 'true')
class CfgMatcher:
def __init__(self, fname):
name = re.escape(fname)
- self.basefile_reg = re.compile('^%s(|\\.H_(?P<hostname>\S+)|.G(?P<prio>\d+)_(?P<group>\S+))$' % name)
- self.delta_reg = re.compile('^%s(|\\.H_(?P<hostname>\S+)|\\.G(?P<prio>\d+)_(?P<group>\S+))\\.(?P<delta>(cat|diff))$' % fname)
+ self.basefile_reg = re.compile('^(?P<basename>%s)(|\\.H_(?P<hostname>\S+)|.G(?P<prio>\d+)_(?P<group>\S+))$' % name)
+ self.delta_reg = re.compile('^(?P<basename>%s)(|\\.H_(?P<hostname>\S+)|\\.G(?P<prio>\d+)_(?P<group>\S+))\\.(?P<delta>(cat|diff))$' % fname)
self.cat_count = fname.count(".cat")
self.diff_count = fname.count(".diff")
@@ -76,28 +77,66 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
def sort_by_specific(self, one, other):
return cmp(one.specific, other.specific)
-
- def bind_entry(self, entry, metadata):
+
+ def get_pertinent_entries(self, metadata):
+ '''return a list of all entries pertinent to a client => [base, delta1, delta2]'''
matching = [ent for ent in self.entries.values() if \
ent.specific.matches(metadata)]
- if [ent for ent in matching if ent.specific.delta]:
- self.bind_info_to_entry(entry, metadata)
- matching.sort(self.sort_by_specific)
- base = min([matching.index(ent) for ent in matching
- if not ent.specific.delta])
- used = matching[:base+1]
- used.reverse()
- # used is now [base, delta1, delta2]
- basefile = used.pop()
- data = basefile.data
- for delta in used:
- data = process_delta(data, delta)
- if entry.get('encoding') == 'base64':
- entry.text = binascii.b2a_base64(data)
- else:
- entry.text = data
+ matching.sort(self.sort_by_specific)
+ base = min([matching.index(ent) for ent in matching
+ if not ent.specific.delta])
+ used = matching[:base+1]
+ used.reverse()
+ return used
+
+ def bind_entry(self, entry, metadata):
+ self.bind_info_to_entry(entry, metadata)
+ used = self.get_pertinent_entries(metadata)
+ basefile = used.pop()
+ data = basefile.data
+ for delta in used:
+ data = process_delta(data, delta)
+ if entry.get('encoding') == 'base64':
+ entry.text = binascii.b2a_base64(data)
else:
- Bcfg2.Server.Plugin.EntrySet.bind_entry(self, entry, metadata)
+ entry.text = data
+
+ def list_accept_choices(self, metadata):
+ '''return a list of candidate pull locations'''
+ used = self.get_pertinent_entries(metadata)
+ if len(used) > 1:
+ return []
+ return [used[0].specific]
+
+ def build_filename(self, specific):
+ bfname = self.path + '/' + self.path.split('/')[-1]
+ if specific.all:
+ return bfname
+ elif specific.group:
+ return "%s.G%d_%s" % (bfname, specific.group, specific.prio)
+ elif specific.hostname:
+ return "%s.H_%s" % (bfname, specific.hostname)
+
+ def write_update(self, specific, new_entry, log):
+ name = self.build_filename(specific)
+ open(name, 'w').write(new_entry['text'])
+ if log:
+ logger.info("Wrote file %s" % name)
+ badattr = [attr for attr in ['owner', 'group', 'perms'] if attr in new_entry]
+ if badattr:
+ if hasattr(self.entries[name.split('/')[-1]], 'infoxml'):
+ print "InfoXML support not yet implemented"
+ return
+ metadata_updates = {}
+ metadata_updates.update(self.metadata)
+ for attr in badattr:
+ metadata_updates[attr] = new_entry.get('attr')
+ infofile = open(self.path + '/:info', 'w')
+ for x in metadata_updates.iteritems():
+ infofile.write("%s: %s\n" % x)
+ infofile.close()
+ if log:
+ logger.info("Wrote file %s" % infofile.name)
class Cfg(Bcfg2.Server.Plugin.GroupSpool):
'''This generator in the configuration file repository for bcfg2'''
@@ -108,56 +147,9 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool):
es_cls = CfgEntrySet
es_child_cls = CfgEntry
- def AcceptEntry(self, meta, _, entry_name, diff, fulldata, metadata_updates={}):
- '''per-plugin bcfg2-admin pull support'''
- if metadata_updates:
- if hasattr(self.Entries['ConfigFile'][entry_name], 'infoxml'):
- print "InfoXML support not yet implemented"
- elif raw_input("Should metadata updates apply to all hosts? (n/Y) ") in ['Y', 'y']:
- self.entries[entry_name].metadata.update(metadata_updates)
- infofile = open(self.entries[entry_name].repopath + '/:info', 'w')
- for x in self.entries[entry_name].metadata.iteritems():
- infofile.write("%s: %s\n" % x)
- infofile.close()
- if not diff and not fulldata:
- raise SystemExit, 0
-
- hsq = "Found host-specific file %s; Should it be updated (n/Y): "
- repo_vers = lxml.etree.Element('ConfigFile', name=entry_name)
- self.Entries['ConfigFile'][entry_name](repo_vers, meta)
- repo_curr = repo_vers.text
- # find the file fragment
- basefile = [frag for frag in \
- self.entries[entry_name].fragments \
- if frag.applies(meta)][-1]
- gsq = "Should this change apply to all hosts effected by file %s? (N/y): " % (basefile.name)
- if ".H_%s" % (meta.hostname) in basefile.name:
- answer = raw_input(hsq % basefile.name)
- else:
- answer = raw_input(gsq)
-
- if answer in ['Y', 'y']:
- print "writing file, %s" % basefile.name
- if fulldata:
- newdata = fulldata
- else:
- newdata = '\n'.join(difflib.restore(diff.split('\n'), 1))
- open(basefile.name, 'w').write(newdata)
- return
+ def AcceptChoices(self, entry, metadata):
+ return self.entries[entry.get('name')].list_accept_choices(metadata)
+
+ def AcceptPullData(self, specific, new_entry, log):
+ return self.entries[new_entry.get('name')].write_update(specific, new_entry, log)
- if ".H_%s" % (meta.hostname) in basefile.name:
- raise SystemExit, 1
- # figure out host-specific filename
- reg = re.compile("(.*)\.G\d+.*")
- if reg.match(basefile.name):
- newname = reg.match(basefile.name).group(1) + ".H_%s" % (meta.hostname)
- else:
- newname = basefile.name + ".H_%s" % (meta.hostname)
- print "This file will be installed as file %s" % newname
- if raw_input("Should it be installed? (N/y): ") in ['Y', 'y']:
- print "writing file, %s" % newname
- if fulldata:
- newdata = fulldata
- else:
- newdata = '\n'.join(difflib.restore(diff.split('\n'), 1))
- open(newname, 'w').write(newdata)
diff --git a/src/lib/Server/Plugins/SSHbase.py b/src/lib/Server/Plugins/SSHbase.py
index 4254ad6d9..89767cf85 100644
--- a/src/lib/Server/Plugins/SSHbase.py
+++ b/src/lib/Server/Plugins/SSHbase.py
@@ -1,15 +1,9 @@
'''This module manages ssh key files for bcfg2'''
__revision__ = '$Revision$'
-import binascii, difflib, os, socket, xml.sax.saxutils
+import binascii, os, socket
import Bcfg2.Server.Plugin
-def update_file(path, diff):
- '''Update file at path using diff'''
- newdata = '\n'.join(difflib.restore(diff.split('\n'), 1))
- print "writing file, %s" % path
- open(path, 'w').write(newdata)
-
class SSHbase(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.DirectoryBacked):
'''The sshbase generator manages ssh host keys (both v1 and v2)
for hosts. It also manages the ssh_known_hosts file. It can
@@ -190,17 +184,14 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.DirectoryBacked):
except OSError:
self.logger.error("Failed to unlink temporary ssh keys")
- def AcceptEntry(self, meta, _, entry_name, diff, fulldata, metadata_updates={}):
- '''per-plugin bcfg2-admin pull support'''
- filename = "%s/%s.H_%s" % (self.data, entry_name.split('/')[-1],
- meta.hostname)
- print "This file will be installed as file %s" % filename
- if raw_input("Should it be installed? (N/y): ") in ['Y', 'y']:
- print "writing file, %s" % filename
- if fulldata:
- newdata = fulldata
- else:
- newdata = '\n'.join(difflib.restore(diff.split('\n'), 1))
- open(filename, 'w').write(newdata)
+ def AcceptChoices(self, _, metadata):
+ return Bcfg2.Server.Plugin.Specificity(hostname=metadata.hostname)
-
+ def AcceptPullData(self, specific, entry, log):
+ '''per-plugin bcfg2-admin pull support'''
+ # specific will always be host specific
+ filename = "%s/%s.H_%s" % (self.data, entry['name'].split('/')[-1],
+ specific.hostname)
+ open(filename, 'w').write(entry['text'])
+ if log:
+ print "Wrote file %s" % filename