summaryrefslogtreecommitdiffstats
path: root/build/scripts-2.7/bcfg2
diff options
context:
space:
mode:
Diffstat (limited to 'build/scripts-2.7/bcfg2')
-rwxr-xr-xbuild/scripts-2.7/bcfg2313
1 files changed, 313 insertions, 0 deletions
diff --git a/build/scripts-2.7/bcfg2 b/build/scripts-2.7/bcfg2
new file mode 100755
index 000000000..9f56481bf
--- /dev/null
+++ b/build/scripts-2.7/bcfg2
@@ -0,0 +1,313 @@
+#!/usr/bin/python
+
+"""Bcfg2 Client"""
+__revision__ = '$Revision$'
+
+import logging
+import os
+import signal
+import sys
+import tempfile
+import time
+import xmlrpclib
+import fcntl
+import Bcfg2.Options
+import Bcfg2.Client.XML
+import Bcfg2.Client.Frame
+import Bcfg2.Client.Tools
+
+import Bcfg2.Proxy
+import Bcfg2.Logger
+
+logger = logging.getLogger('bcfg2')
+
+def cb_sigint_handler(signum, frame):
+ """Exit upon CTRL-C."""
+ os._exit(1)
+
+DECISION_LIST = Bcfg2.Options.Option('Decision List', default=False,
+ cmd="--decision-list", odesc='<file>',
+ long_arg=True)
+
+
+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': Bcfg2.Options.VERBOSE,
+ 'extra': Bcfg2.Options.CLIENT_EXTRA_DISPLAY,
+ 'quick': Bcfg2.Options.CLIENT_QUICK,
+ 'debug': Bcfg2.Options.DEBUG,
+ 'lockfile': Bcfg2.Options.LOCKFILE,
+ 'drivers': Bcfg2.Options.CLIENT_DRIVERS,
+ 'dryrun': Bcfg2.Options.CLIENT_DRYRUN,
+ 'paranoid': Bcfg2.Options.CLIENT_PARANOID,
+ 'bundle': Bcfg2.Options.CLIENT_BUNDLE,
+ 'bundle-quick': Bcfg2.Options.CLIENT_BUNDLEQUICK,
+ 'indep': Bcfg2.Options.CLIENT_INDEP,
+ 'file': Bcfg2.Options.CLIENT_FILE,
+ 'interactive': Bcfg2.Options.INTERACTIVE,
+ 'cache': Bcfg2.Options.CLIENT_CACHE,
+ 'profile': Bcfg2.Options.CLIENT_PROFILE,
+ 'remove': Bcfg2.Options.CLIENT_REMOVE,
+ 'help': Bcfg2.Options.HELP,
+ 'setup': Bcfg2.Options.CFILE,
+ 'server': Bcfg2.Options.SERVER_LOCATION,
+ 'user': Bcfg2.Options.CLIENT_USER,
+ 'password': Bcfg2.Options.SERVER_PASSWORD,
+ 'retries': Bcfg2.Options.CLIENT_RETRIES,
+ 'kevlar': Bcfg2.Options.CLIENT_KEVLAR,
+ 'decision-list': DECISION_LIST,
+ 'encoding': Bcfg2.Options.ENCODING,
+ 'omit-lock-check': Bcfg2.Options.OMIT_LOCK_CHECK,
+ 'filelog': Bcfg2.Options.LOGGING_FILE_PATH,
+ 'decision': Bcfg2.Options.CLIENT_DLIST,
+ 'servicemode': Bcfg2.Options.CLIENT_SERVICE_MODE,
+ 'key': Bcfg2.Options.CLIENT_KEY,
+ 'certificate': Bcfg2.Options.CLIENT_CERT,
+ 'ca': Bcfg2.Options.CLIENT_CA,
+ 'serverCN': Bcfg2.Options.CLIENT_SCNS,
+ }
+
+ self.setup = Bcfg2.Options.OptionParser(optinfo)
+ self.setup.parse(sys.argv[1:])
+
+ if self.setup['args']:
+ print("Bcfg2 takes no arguments, only options")
+ print(self.setup.buildHelpMessage())
+ raise SystemExit(1)
+ level = 30
+ if self.setup['verbose']:
+ level = 20
+ if self.setup['debug']:
+ level = 0
+ Bcfg2.Logger.setup_logging('bcfg2',
+ to_syslog=False,
+ level=level,
+ to_file=self.setup['filelog'])
+ self.logger = logging.getLogger('bcfg2')
+ self.logger.debug(self.setup)
+ if self.setup['bundle-quick']:
+ if self.setup['bundle'] == []:
+ self.logger.error("-Q option requires -b")
+ raise SystemExit(1)
+ elif self.setup['remove'] != False:
+ self.logger.error("-Q option incompatible with -r")
+ raise SystemExit(1)
+ 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')
+ self.logger.info("Running probe %s" % name)
+ ret = Bcfg2.Client.XML.Element("probe-data",
+ name=name,
+ source=probe.get('source'))
+ try:
+ scripthandle, scriptname = tempfile.mkstemp()
+ script = open(scriptname, 'w+')
+ try:
+ script.write("#!%s\n" %
+ (probe.attrib.get('interpreter', '/bin/sh')))
+ script.write(probe.text)
+ script.close()
+ os.close(scripthandle)
+ os.chmod(script.name, 0755)
+ ret.text = os.popen(script.name).read().strip()
+ self.logger.info("Probe %s has result:\n%s" % (name, ret.text))
+ 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))
+ os._exit(1)
+
+ 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']))
+ return(1)
+ else:
+ # retrieve config from server
+ proxy = Bcfg2.Proxy.ComponentProxy(self.setup['server'],
+ self.setup['user'],
+ self.setup['password'],
+ key = self.setup['key'],
+ cert = self.setup['certificate'],
+ ca = self.setup['ca'],
+ allowedServerCNs = self.setup['serverCN'])
+
+ if self.setup['profile']:
+ try:
+ proxy.AssertProfile(self.setup['profile'])
+ except xmlrpclib.Fault:
+ self.fatal_error("Failed to set client profile")
+ return(1)
+
+ 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))
+ return(1)
+
+ # 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, encoding='UTF-8', xml_declaration=True))
+ except:
+ self.logger.error("Failed to upload probe data", exc_info=1)
+ raise SystemExit(1)
+
+ times['probe_upload'] = time.time()
+
+ if self.setup['decision'] in ['whitelist', 'blacklist']:
+ try:
+ self.setup['decision_list'] = proxy.GetDecisionList( \
+ self.setup['decision'])
+ self.logger.info("Got decision list from server:")
+ self.logger.info(self.setup['decision_list'])
+ except xmlrpclib.Fault, f:
+ if f.faultCode == 1:
+ print("GetDecisionList method not supported by server")
+ else:
+ self.logger.error("Failed to de", exc_info=1)
+ raise SystemExit(1)
+
+ 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.encode(self.setup['encoding']))
+ 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))
+ return(1)
+
+ times['config_parse'] = time.time()
+
+ if self.config.tag == 'error':
+ self.fatal_error("Server error: %s" % (self.config.text))
+ return(1)
+
+ if self.setup['bundle-quick']:
+ newconfig = Bcfg2.Client.XML.XML('<Configuration/>')
+ [newconfig.append(bundle) for bundle in self.config.getchildren() if \
+ bundle.tag == 'Bundle' and bundle.get('name') in self.setup['bundle']]
+ self.config = newconfig
+
+ self.tools = Bcfg2.Client.Frame.Frame(self.config,
+ self.setup,
+ times, self.setup['drivers'],
+ self.setup['dryrun'])
+
+ if not self.setup['omit-lock-check']:
+ #check lock here
+ try:
+ lockfile = open(self.setup['lockfile'], 'w')
+ try:
+ fcntl.lockf(lockfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except IOError:
+ #otherwise exit and give a warning to the user
+ self.fatal_error("An other instance of Bcfg2 is running. If you what to bypass the check, run with %s option" %
+ (Bcfg2.Options.OMIT_LOCK_CHECK.cmd))
+ except:
+ lockfile = None
+ self.logger.error("Failed to open lockfile")
+ # execute the said configuration
+ self.tools.Execute()
+
+ if not self.setup['omit-lock-check']:
+ #unlock here
+ if lockfile:
+ try:
+ fcntl.lockf(lockfile.fileno(), fcntl.LOCK_UN)
+ os.remove(self.setup['lockfile'])
+ except OSError:
+ self.logger.error("Failed to unlock lockfile %s" % lockfile.name)
+
+ if not self.setup['file'] and not self.setup['bundle-quick']:
+ # upload statistics
+ feedback = self.tools.GenerateStats()
+
+ try:
+ proxy.RecvStats(Bcfg2.Client.XML.tostring(feedback,
+ encoding='UTF-8',
+ xml_declaration=True))
+ 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()
+ spid = os.getpid()
+ client.run()