#!/usr/local/bin/python2.3
'''Bcfg2 Client'''
__revision__ = '$Revision: 3198 $'
import getopt, logging, os, signal, sys, tempfile, time, xmlrpclib
import Bcfg2.Options, Bcfg2.Client.XML, Bcfg2.Client.Frame, Bcfg2.Client.Tools
try:
import Bcfg2.Client.Proxy, Bcfg2.Logging
except KeyError:
print "Could not read options from configuration file"
raise SystemExit, 1
def cb_sigint_handler(signum, frame):
'''Exit upon CTRL-C'''
os._exit(1)
class Client:
''' The main bcfg2 client class '''
def __init__(self):
self.toolset = None
self.config = None
optinfo = {
# 'optname': (('-a', argdesc, optdesc),
# env, cfpath, default, boolean)),
'verbose':(('-v', False,"enable verbose output"),
False, False, False, True),
'extra':(('-e', False,"enable extra entry detailed output"),
False, False, False, True),
'quick':(('-q', False, "disable some checksum verification"),
False, False, False, True),
'debug':(('-d', False, "enable debugging output"),
False, False, False, True),
'drivers':(('-D', '<driver1>,<driver2>', "Specify tool driver set"),
False, ('client', 'drivers'), False, False),
'fingerprint':(('-F', '<server fingerprint>', "Server Fingerprint"),
False, ('communication', 'fingerprint'), False, False),
'dryrun':(('-n', False, "do not actually change the system"),
False, False, False, True),
'build': (('-B', False, "run in build mode"),
False, False, False, True),
'paranoid':(('-P', False, "make automatic backups of config files"),
False, False, False, True),
'bundle':(('-b', '<bundle>', "only configure the given bundle"),
False, False, False, False),
'file': (('-f', "<configspec>", "configure from a file rather than querying the server"),
False, False, False, False),
'interactive': (('-I', False, "prompt the user for each change"),
False, False, False, True),
'cache': (('-c', "<configspec>", "store the configuration in a file"),
False, False, False, False),
'profile': (('-p', '<profile>', "assert the given profile for the host"),
False, False, False, False),
'remove': (('-r', '(packages|services|all)', "force removal of additional configuration items"),
False, False, False, False),
'help': (('-h', False, "print this help message"),
False, False, False, True),
'setup': (('-C', '<configfile>', "use given config file (default /etc/bcfg2.conf)"),
False, False, '/etc/bcfg2.conf', False),
'server': (('-S', '<server url>', 'the server hostname to connect to'),
False, ('components', 'bcfg2'), 'https://localhost:6789', False),
'user': (('-u', '<user>', 'the user to provide for authentication'),
False, ('communication', 'user'), 'root', False),
'password': (('-x', '<password>', 'the password to provide for authentication'),
False, ('communication', 'password'), 'password', False),
'retries': (('-R', '<numretries>', 'the number of times to retry network communication'),
False, ('communication', 'retries'), '3', False),
'kevlar': (('-k', False, "run in kevlar (bulletproof) mode"),
False, False, False, True),
}
optparser = Bcfg2.Options.OptionParser('bcfg2', optinfo)
self.setup = optparser.parse()
if getopt.getopt(sys.argv[1:],
optparser.shortopt, optparser.longopt)[1]:
print "Bcfg2 takes no arguments, only options"
print optparser.helpmsg
raise SystemExit, 1
level = 30
if self.setup['verbose']:
level = 20
if self.setup['debug']:
level = 0
Bcfg2.Logging.setup_logging('bcfg2', to_syslog=False, level=level)
self.logger = logging.getLogger('bcfg2')
self.logger.debug(self.setup)
if 'drivers' in self.setup and self.setup['drivers'] == 'help':
self.logger.info("The following drivers are available:")
self.logger.info(Bcfg2.Client.Tools.drivers)
raise SystemExit, 0
if self.setup['remove'] and 'services' in self.setup['remove']:
self.logger.error("Service removal is nonsensical, disable services to get former behavior")
if self.setup['remove'] not in [False, 'all', 'services', 'packages']:
self.logger.error("Got unknown argument %s for -r" % (self.setup['remove']))
if (self.setup["file"] != False) and (self.setup["cache"] != False):
print "cannot use -f and -c together"
raise SystemExit, 1
def run_probe(self, probe):
'''Execute probe'''
name = probe.get('name')
ret = Bcfg2.Client.XML.Element("probe-data", name=name, source=probe.get('source'))
try:
script = open(tempfile.mktemp(), 'w+')
try:
script.write("#!%s\n" %
(probe.attrib.get('interpreter', '/bin/sh')))
script.write(probe.text)
script.close()
os.chmod(script.name, 0755)
ret.text = os.popen(script.name).read().strip()
finally:
os.unlink(script.name)
except:
self.logger.error("Failed to execute probe: %s" % (name), exc_info=1)
raise SystemExit, 1
return ret
def fatal_error(self, message):
'''Signal a fatal error'''
self.logger.error("Fatal error: %s" % (message))
raise SystemExit, 1
def get_config(self):
'''Either download the config from the server or read it from file'''
def run(self):
''' Perform client execution phase '''
times = {}
# begin configuration
times['start'] = time.time()
if self.setup['file']:
# read config from file
try:
self.logger.debug("reading cached configuration from %s" %
(self.setup['file']))
configfile = open(self.setup['file'], 'r')
rawconfig = configfile.read()
configfile.close()
except IOError:
self.fatal_error("failed to read cached configuration from: %s"
% (self.setup['file']))
else:
# retrieve config from server
try:
proxy = Bcfg2.Client.Proxy.bcfg2(self.setup)
except:
self.fatal_error("failed to instantiate proxy to server")
if self.setup['profile']:
try:
proxy.AssertProfile(self.setup['profile'])
except xmlrpclib.Fault:
self.fatal_error("Failed to set client profile")
try:
probe_data = proxy.GetProbes()
except xmlrpclib.Fault, flt:
self.logger.error("Failed to download probes from bcfg2")
self.logger.error(flt.faultString)
raise SystemExit, 1
times['probe_download'] = time.time()
try:
probes = Bcfg2.Client.XML.XML(probe_data)
except Bcfg2.Client.XML.ParseError, syntax_error:
self.fatal_error(
"server returned invalid probe requests: %s" %
(syntax_error))
# execute probes
try:
probedata = Bcfg2.Client.XML.Element("ProbeData")
[probedata.append(self.run_probe(probe))
for probe in probes.findall(".//probe")]
except:
self.logger.error("Failed to Execute probes")
raise SystemExit, 1
if len(probes.findall(".//probe")) > 0:
try:
# upload probe responses
proxy.RecvProbeData(Bcfg2.Client.XML.tostring(probedata))
except:
self.logger.error("Failed to upload probe data", exc_info=1)
raise SystemExit, 1
times['probe_upload'] = time.time()
try:
rawconfig = proxy.GetConfig()
except xmlrpclib.Fault:
self.logger.error("Failed to download configuration from bcfg2")
raise SystemExit, 2
times['config_download'] = time.time()
if self.setup['cache']:
try:
open(self.setup['cache'], 'w').write(rawconfig)
os.chmod(self.setup['cache'], 33152)
except IOError:
self.logger.warning("failed to write config cache file %s" %
(self.setup['cache']))
times['caching'] = time.time()
try:
self.config = Bcfg2.Client.XML.XML(rawconfig)
except Bcfg2.Client.XML.ParseError, syntax_error:
self.fatal_error("the configuration could not be parsed: %s" %
(syntax_error))
times['config_parse'] = time.time()
if self.config.tag == 'error':
self.fatal_error("server error: %s" % (self.config.text))
self.tools = Bcfg2.Client.Frame.Frame(self.config, self.setup, times)
self.tools.Execute()
if not self.setup['file']:
# upload statistics
feedback = self.tools.GenerateStats()
try:
proxy.RecvStats(Bcfg2.Client.XML.tostring(feedback))
except xmlrpclib.Fault:
self.logger.error("Failed to upload configuration statistics")
raise SystemExit, 2
if __name__ == '__main__':
signal.signal(signal.SIGINT, cb_sigint_handler)
client = Client()
client.run()
syntax highlighted by Code2HTML, v. 0.9.1