#!/usr/bin/env python '''Bcfg2 Client''' __revision__ = '$Revision$' from getopt import getopt, GetoptError from os import popen, chmod, unlink, _exit from signal import signal, SIGINT from sys import argv from tempfile import mktemp from ConfigParser import ConfigParser, NoSectionError, NoOptionError from time import time from lxml.etree import Element, XML, tostring def cb_sigint_handler(signum, frame): '''Exit upon CTRL-C''' _exit(1) def load_toolset(toolset, config, clientsetup): '''Import client toolset modules''' if toolset == 'debian': if clientsetup['verbose']: print 'Selected Debian Toolset...' mod = __import__("Bcfg2.Client.Debian", globals(), locals(), ['*']) return mod.Debian(config, clientsetup) elif toolset == 'rh': if setup['verbose']: print 'Selected RedHat Toolset...' mod = __import__("Bcfg2.Client.Redhat", globals(), locals(), ['*']) return mod.Redhat(config, clientsetup) elif toolset == 'solaris': if setup['verbose']: print "Selected Solaris Toolset" mod = __import__("Bcfg2.Client.Solaris", globals(), locals(), ['*']) return mod.Solaris(config, clientsetup) else: print "Got unsupported toolset %s from server." % (toolset) raise SystemExit, 1 def run_probe(probe): '''Execute probe''' ret = Element("probe-data", name=probe.attrib['name'], source=probe.attrib['source']) script = open(mktemp(), 'w+') script.write("#!%s\n"%(probe.attrib.get('interpreter', '/bin/sh'))) script.write(probe.text) script.close() chmod(script.name, 0755) ret.text = popen(script.name).read() unlink(script.name) return ret def dgetopt(arglist, opt, vopt): '''parse options into a dictionary''' ret = {} for optname in opt.values() + vopt.values(): ret[optname] = False gstr = "".join(opt.keys()) + "".join([optionkey + ':' for optionkey in vopt.keys()]) try: ginfo = getopt(arglist, gstr) except GetoptError, gerr: print gerr print "bcfg2 Usage:" for arg in opt.iteritems(): print " -%s %s" % arg for arg in vopt.iteritems(): print " -%s <%s>" % arg raise SystemExit, 1 for (gopt, garg) in ginfo[0]: option = gopt[1:] if opt.has_key(option): ret[opt[option]] = True else: ret[vopt[option]] = garg return ret if __name__ == '__main__': # parse command line options signal(SIGINT, cb_sigint_handler) options = {'v':'verbose', 'q':'quick', 'd':'debug', 'n':'dryrun', 'B':'build', 'P':'paranoid'} doptions = {'b':'bundle', 'f':'file', 'c':'cache', 'p':'profile', 'i':'image', 'r':'remove'} setup = dgetopt(argv[1:], options, doptions) timeinfo = Element("Times") comm = None if setup['file']: try: configfile = open(setup['file'], 'r') r = configfile.read() configfile.close() except IOError: print "Failed to read cached config file: %s" % (setup['file']) raise SystemExit, 1 else: try: cf = ConfigParser() cf.read('/etc/bcfg2.conf') proto = cf.get('communication', 'protocol') except (NoSectionError, NoOptionError): proto = 'sss' if proto == 'sss': from sss.ssslib import comm_lib elif proto == 'xmlrpc/ssl': from Bcfg2.Client.XMLRPCComm import comm_lib else: print "Unsupported protocol: %s" % proto raise SystemExit, 1 # connect to bcfg2d comm = comm_lib() try: h = comm.ClientInit("bcfg2") except: print "Failed to connect to bcfg2 server" raise SystemExit, 1 # get probes start = time() comm.SendMessage(h, "") data = comm.RecvMessage(h) timeinfo.set('probefetch', str(time() - start)) probes = XML(data) # execute probes cpd = Element("probe-data") [cpd.append(run_probe(x)) for x in probes.findall(".//probe")] # upload probe responses comm.SendMessage(h, tostring(cpd)) r = comm.RecvMessage(h) msg = Element("get-config") if setup['profile']: msg.attrib['profile'] = setup['profile'] if setup['image']: msg.attrib['image'] = setup['image'] # get config cstart = time() comm.SendMessage(h, tostring(msg)) r = comm.RecvMessage(h) timeinfo.set('config', str(time() - cstart )) if setup['cache']: try: open(setup['cache'], 'w').write(r) except IOError: print "failed to write config cache file %s" % (setup['cache']) try: pt = time() cfg = XML(r) timeinfo.set('parse', str(time() - pt)) except: print "got error from server" raise SystemExit, 1 if cfg.tag == 'error': print "got error from server" raise SystemExit, 1 # Get toolset from server cfg_toolset = cfg.get('toolset') if setup['bundle']: c = Element("Configuration", version='2.0') for child in cfg.getroot().getchildren(): if ((child.tag == 'Bundle') and (child.attrib['name'] == setup['bundle'])): c.append(child) cfg = c # Create toolset handle client = load_toolset(cfg_toolset, cfg, setup) istart = time() # verify state client.Inventory() timeinfo.set('inventory', str(time() - istart)) correct = client.states.values().count(True) total = len(client.states.values()) istart = time() if ((correct < total) or client.pkgwork['remove']): if client.pkgwork['remove']: client.CondPrint('verbose', "Extra packages detected") # summarize current state client.CondPrint('verbose', "--> %s of %s config elements correct" % (correct, total)) # install incorrect aspects of configuration client.Install() client.CondPrint('verbose', "--> %s of %s config elements correct" % (client.states.values().count(True), total)) failed = [key for key, value in client.states.iteritems() if not value] if failed: client.CondPrint('verbose', "Failing Entries:") [client.CondPrint('verbose', "%s:%s" % (key.tag, key.get('name'))) for key in failed if key.tag != 'Package'] [client.CondPrint('verbose', "%s:%s-%s" % (key.tag, key.get('name'), key.get('version', 'unset'))) for key in failed if key.tag == 'Package'] else: client.CondPrint("verbose", "All entries correct") timeinfo.set('install', str(time() - istart)) timeinfo.set('total', str(time() - start)) if not setup['file']: # upload statistics m = Element("upload-statistics") stats = client.GenerateStats(__revision__) stats.append(timeinfo) m.append(stats) comm.SendMessage(h, tostring(m)) r = comm.RecvMessage(h) # clean up comm.ClientClose(h)