#!/usr/bin/env python '''Bcfg2 Client''' __revision__ = '$Revision$' from getopt import getopt, GetoptError from os import popen, chmod, unlink from sys import argv from tempfile import mktemp from elementtree.ElementTree import Element, XML, tostring from sss.ssslib import comm_lib 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) else: print "Toolset not correctly provided by the server." 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 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) 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: # connect to bcfg2d comm = comm_lib() try: h = comm.ClientInit("bcfg2") except: print "Failed to connect to bcfg2 server" raise SystemExit, 1 # get probes comm.SendMessage(h, "") data = comm.RecvMessage(h) #if setup['verbose']: print data 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 comm.SendMessage(h, tostring(msg)) r = comm.RecvMessage(h) if setup['cache']: try: open(setup['cache'], 'w').write(r) except IOError: print "failed to write config cache file %s" % (setup['cache']) try: cfg = XML(r) 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) # verify state client.Inventory() correct = client.states.values().count(True) total = len(client.states.values()) 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") if not setup['file']: # upload statistics m = Element("upload-statistics") stats = client.GenerateStats(__revision__) m.append(stats) comm.SendMessage(h, tostring(m)) r = comm.RecvMessage(h) # clean up comm.ClientClose(h)