From 6c6b0ad8d13ddcea625dbd3130ea0b9d4bc00ba8 Mon Sep 17 00:00:00 2001 From: Narayan Desai Date: Tue, 3 Apr 2007 12:47:24 +0000 Subject: Move logic for repository interactions in bcfg2-admin pull into individual plugins (This change introduces all new infrastructure, and is in preparation for doing support in SSHbase) git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@3005 ce84e21b-d406-0410-9b95-82705330c041 --- src/lib/Server/Plugin.py | 10 ++++++++ src/lib/Server/Plugins/Cfg.py | 40 +++++++++++++++++++++++++++++- src/sbin/bcfg2-admin | 57 +++++++++++++------------------------------ 3 files changed, 66 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py index 9a3b73e82..3c32dcad7 100644 --- a/src/lib/Server/Plugin.py +++ b/src/lib/Server/Plugin.py @@ -59,6 +59,16 @@ class Plugin(object): '''This is the slow-path handler for configuration entry binding''' raise PluginExecutionError + def AcceptEntry(self, metadata, entry_type, entry_name, data): + '''This is the null per-plugin implementation + of bcfg2-admin pull''' + raise PluginExecutionError + + def CommitChanges(self): + '''Handle revctl commits, if needed''' + # not implemented yet + pass + # the rest of the file contains classes for coherent file caching class FileBacked(object): diff --git a/src/lib/Server/Plugins/Cfg.py b/src/lib/Server/Plugins/Cfg.py index d5829a016..c75afbc18 100644 --- a/src/lib/Server/Plugins/Cfg.py +++ b/src/lib/Server/Plugins/Cfg.py @@ -1,7 +1,8 @@ '''This module implements a config file repository''' __revision__ = '$Revision$' -import binascii, logging, os, re, stat, tempfile, Bcfg2.Server.Plugin, lxml.etree +import binascii, difflib, logging, os, re, stat, tempfile, \ + xml.sax.saxutils, Bcfg2.Server.Plugin, lxml.etree logger = logging.getLogger('Bcfg2.Plugins.Cfg') @@ -9,6 +10,12 @@ specific = re.compile('(.*/)(?P[\S\-.]+)\.((H_(?P\S+))|' + '(G(?P\d+)_(?P\S+)))$') probeData = {} +def update_file(path, diff): + '''Update file at path using diff''' + newdata = '\n'.join(difflib.restore(xml.sax.saxutils.unescape(diff).split('\n'), 1)) + print "writing file, %s" % path + open(path, 'w').write(newdata) + class SpecificityError(Exception): '''Thrown in case of filename parse failure''' pass @@ -352,3 +359,34 @@ class Cfg(Bcfg2.Server.Plugin.Plugin): logger.error("Got unknown event %s %s:%s" % (action, event.requestID, event.filename)) self.interpolate = len([entry for entry in self.entries.values() if entry.interpolate ]) > 0 + def AcceptEntry(self, meta, _, entry_name, diff): + '''per-plugin bcfg2-admin pull support''' + 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 this host of all hosts effected by file %s? (N/y): " % (basefile.name) + if ".H_%s" % (meta.hostname) in basefile.name: + answer = raw_input(hsq) + else: + answer = raw_input(gsq) + + if answer in 'Yy': + update_file(basefile.name, diff) + return + + if ".H_%s" % (meta.hostname) in basefile.name: + raise SystemExit, 1 + # figure out host-specific filename + if '.G_' in basefile.name: + idx = basefile.name.find(".G_") + newname = basefile.name[:idx] + ".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 'Yy': + update_file(newname, diff) diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin index 2539c42c1..3cd470f78 100755 --- a/src/sbin/bcfg2-admin +++ b/src/sbin/bcfg2-admin @@ -2,7 +2,6 @@ '''bcfg2-admin is a script that helps to administrate a bcfg2 deployment''' import getopt, difflib, logging, lxml.etree, os, popen2, re, socket, sys, ConfigParser -import xml.sax.saxutils import Bcfg2.Server.Core, Bcfg2.Logging, Bcfg2.tlslite.api log = logging.getLogger('bcfg-admin') @@ -298,10 +297,9 @@ def do_pull(cfile, repopath, client, etype, ename): (state, etype, ename)) if not entry: err_exit("Could not find state data for entry; rerun bcfg2 on client system") - - # fail for unsupported etypes - if etype not in ['ConfigFile', 'Package']: - err_exit("Unsupported entry type %s" % (etype)) + + diff = entry[0].get('current_diff') + try: bcore = Bcfg2.Server.Core.Core({}, cfile) except Bcfg2.Server.Core.CoreInitError, msg: @@ -311,39 +309,18 @@ def do_pull(cfile, repopath, client, etype, ename): while bcore.fam.Service(): pass m = bcore.metadata.get_metadata(client) - # find appropriate location in repo - if etype == 'ConfigFile': - rversion = lxml.etree.Element('ConfigFile', name=ename) - bcore.Bind(rversion, m) - current = rversion.text - diff = entry[0].get('current_diff') - basefile = [frag for frag in \ - bcore.plugins['Cfg'].entries[ename].fragments \ - if frag.applies(m)][-1] - if ".H_%s" % (m.hostname) in basefile.name: - answer = raw_input("Found host-specific file %s; Should it be updated (n/Y): ") - if answer in 'Yy': - update_file(basefile.name, diff) - else: - raise SystemExit, 1 - else: - # there are two possibilities - msg = "Should this change apply to this host of all hosts effected by file %s? (N/y): " % (basefile.name) - choice = raw_input(msg) - if choice in 'Yy': - newname = basefile.name - else: - # figure out host-specific filename - if '.G_' in basefile.name: - idx = basefile.name.find(".G_") - newname = basefile.name[:idx] + ".H_%s" % (m.hostname) - else: - newname = basefile.name + ".H_%s" % (m.hostname) - print "This file will be installed as file %s" % newname - if raw_input("Should it be installed? (N/y): ") in 'Yy': - update_file(newname, diff) - else: - err_exit("Don't support entry type %s yet" % etype) + # find appropriate plugin in bcore + glist = [gen for gen in bcore.generators if + gen.Entries.get(etype, {}).has_key(ename)] + if len(glist) != 1: + err_exit("Got wrong numbers of matching generators for entry:" \ + + "%s" % ([g.__name__ for g in glist])) + plugin = glist[0] + try: + plugin.AcceptEntry(m, 'ConfigFile', ename, diff) + except Bcfg2.Server.Plugin.PluginExecutionError: + err_exit("Configuration upload not supported by plugin %s" \ + % (plugin.__name__)) # svn commit if running under svn def do_minestruct(repopath, argdata): @@ -526,7 +503,7 @@ def do_client(repopath, args): print "Done" if __name__ == '__main__': - Bcfg2.Logging.setup_logging('bcfg2-admin', to_console=True) + Bcfg2.Logging.setup_logging('bcfg2-admin', to_console=False) # Some sensible defaults configfile = "/etc/bcfg2.conf" @@ -562,7 +539,7 @@ if __name__ == '__main__': if len(args) != 4: print usage raise SystemExit, 1 - do_pull(args[0], Repopath, args[1], args[2], args[3]) + do_pull(configfile, Repopath, args[1], args[2], args[3]) elif args[0] == 'minestruct': do_minestruct(Repopath, args[1:]) -- cgit v1.2.3-1-g7c22