diff options
author | Chris St. Pierre <chris.a.st.pierre@gmail.com> | 2012-10-17 11:21:27 -0400 |
---|---|---|
committer | Chris St. Pierre <chris.a.st.pierre@gmail.com> | 2012-10-17 11:21:41 -0400 |
commit | 7c3368f78e7c5042932a4fb58cea4b2ac3130358 (patch) | |
tree | 39ea7d392fe118b91df702cce6bf0a0e3963c11e /src/sbin | |
parent | 6683f2e8d48b1cbf8e4c1ad096c6b0c98eefe527 (diff) | |
download | bcfg2-7c3368f78e7c5042932a4fb58cea4b2ac3130358.tar.gz bcfg2-7c3368f78e7c5042932a4fb58cea4b2ac3130358.tar.bz2 bcfg2-7c3368f78e7c5042932a4fb58cea4b2ac3130358.zip |
added bcfg2_local.py, a tool to run bcfg2 against a local specification
Diffstat (limited to 'src/sbin')
-rwxr-xr-x | src/sbin/bcfg2 | 344 | ||||
-rwxr-xr-x | src/sbin/bcfg2-info | 2 |
2 files changed, 13 insertions, 333 deletions
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2 index f7c36cff2..91d09b959 100755 --- a/src/sbin/bcfg2 +++ b/src/sbin/bcfg2 @@ -1,25 +1,10 @@ #!/usr/bin/env python - """Bcfg2 Client""" -import fcntl -import logging -import os -import signal -import socket -import stat import sys -import tempfile -import time -import Bcfg2.Proxy -import Bcfg2.Logger +import signal import Bcfg2.Options -import Bcfg2.Client.XML -import Bcfg2.Client.Frame -import Bcfg2.Client.Tools -from Bcfg2.Compat import xmlrpclib -from Bcfg2.version import __version__ -from subprocess import Popen, PIPE +from Bcfg2.Client import Client def cb_sigint_handler(signum, frame): @@ -27,323 +12,18 @@ def cb_sigint_handler(signum, frame): raise SystemExit(1) -class Client(object): - """The main bcfg2 client class""" - - def __init__(self): - self.toolset = None - self.tools = None - self.config = None - self._proxy = None - - optinfo = Bcfg2.Options.CLIENT_COMMON_OPTIONS - 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) - if self.setup['debug']: - level = logging.DEBUG - elif self.setup['verbose']: - level = logging.INFO - else: - level = logging.WARNING - Bcfg2.Logger.setup_logging('bcfg2', - to_syslog=self.setup['syslog'], - level=level, - to_file=self.setup['logging']) - self.logger = logging.getLogger('bcfg2') - self.logger.debug(self.setup) - if self.setup['bundle_quick']: - if not self.setup['bundle'] and not self.setup['skipbundle']: - self.logger.error("-Q option requires -b or -B") - raise SystemExit(1) - elif self.setup['remove']: - 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'].lower(): - self.logger.error("Service removal is nonsensical; " - "removed services will only be disabled") - if (self.setup['remove'] and - self.setup['remove'].lower() not in ['all', 'services', - 'packages']): - self.logger.error("Got unknown argument %s for -r" % - self.setup['remove']) - if self.setup["file"] and self.setup["cache"]: - print("cannot use -f and -c together") - raise SystemExit(1) - if not self.setup['server'].startswith('https://'): - self.setup['server'] = 'https://' + self.setup['server'] +def main(): + optinfo = Bcfg2.Options.CLIENT_COMMON_OPTIONS + setup = Bcfg2.Options.OptionParser(optinfo) + setup.parse(sys.argv[1:]) - def _probe_failure(self, probename, msg): - """ handle failure of a probe in the way the user wants us to - (exit or continue) """ - message = "Failed to execute probe %s: %s" % (probename, msg) - if self.setup['probe_exit']: - self.fatal_error(message) - else: - self.logger.error(message) - - 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 = os.fdopen(scripthandle, 'w') - try: - script.write("#!%s\n" % - (probe.attrib.get('interpreter', '/bin/sh'))) - script.write(probe.text) - script.close() - os.chmod(scriptname, - stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | - stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | - stat.S_IWUSR) # 0755 - proc = Popen(scriptname, stdin=PIPE, stdout=PIPE, stderr=PIPE) - ret.text, err = proc.communicate() - rv = proc.wait() - if err: - self.logger.warning("Probe %s has error output: %s" % - (name, err)) - if rv: - self._probe_failure(name, "Return value %s" % rv) - self.logger.info("Probe %s has result:" % name) - self.logger.info(ret.text) - finally: - os.unlink(scriptname) - except: # pylint: disable=W0702 - self._probe_failure(name, sys.exc_info()[1]) - return ret - - def fatal_error(self, message): - """Signal a fatal error.""" - self.logger.error("Fatal error: %s" % (message)) + if setup['args']: + print("Bcfg2 takes no arguments, only options") + print(setup.buildHelpMessage()) raise SystemExit(1) - @property - def proxy(self): - """ get an XML-RPC proxy to the server """ - if self._proxy is None: - self._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'], - timeout=self.setup['timeout'], - retries=int(self.setup['retries']), - delay=int(self.setup['retry_delay'])) - return self._proxy - - def run_probes(self, times=None): - """ run probes and upload probe data """ - if times is None: - times = dict() - - try: - probes = Bcfg2.Client.XML.XML(str(self.proxy.GetProbes())) - except (Bcfg2.Proxy.ProxyError, - Bcfg2.Proxy.CertificateError, - socket.gaierror, - socket.error): - err = sys.exc_info()[1] - self.fatal_error("Failed to download probes from bcfg2: %s" % err) - except Bcfg2.Client.XML.ParseError: - err = sys.exc_info()[1] - self.fatal_error("Server returned invalid probe requests: %s" % - err) - - times['probe_download'] = time.time() - - # execute probes - probedata = Bcfg2.Client.XML.Element("ProbeData") - for probe in probes.findall(".//probe"): - probedata.append(self.run_probe(probe)) - - if len(probes.findall(".//probe")) > 0: - try: - # upload probe responses - self.proxy.RecvProbeData(Bcfg2.Client.XML.tostring( - probedata, - xml_declaration=False).decode('UTF-8')) - except Bcfg2.Proxy.ProxyError: - err = sys.exc_info()[1] - self.fatal_error("Failed to upload probe data: %s" % err) - - times['probe_upload'] = time.time() - - def get_config(self, times=None): - """ load the configuration, either from the cached - configuration file (-f), or from the server """ - if times is None: - times = dict() - - if self.setup['file']: - # read config from file - try: - self.logger.debug("Reading cached configuration from %s" % - self.setup['file']) - return open(self.setup['file'], 'r').read() - except IOError: - self.fatal_error("Failed to read cached configuration from: %s" - % (self.setup['file'])) - else: - # retrieve config from server - if self.setup['profile']: - try: - self.proxy.AssertProfile(self.setup['profile']) - except Bcfg2.Proxy.ProxyError: - err = sys.exc_info()[1] - self.fatal_error("Failed to set client profile: %s" % err) - - try: - self.proxy.DeclareVersion(__version__) - except xmlrpclib.Fault: - err = sys.exc_info()[1] - if (err.faultCode == xmlrpclib.METHOD_NOT_FOUND or - (err.faultCode == 7 and - err.faultString.startswith("Unknown method"))): - self.logger.debug("Server does not support declaring " - "client version") - else: - self.logger.error("Failed to declare version: %s" % err) - except (Bcfg2.Proxy.ProxyError, - Bcfg2.Proxy.CertificateError, - socket.gaierror, - socket.error): - err = sys.exc_info()[1] - self.logger.error("Failed to declare version: %s" % err) - - self.run_probes(times=times) - - if self.setup['decision'] in ['whitelist', 'blacklist']: - try: - self.setup['decision_list'] = \ - self.proxy.GetDecisionList(self.setup['decision']) - self.logger.info("Got decision list from server:") - self.logger.info(self.setup['decision_list']) - except Bcfg2.Proxy.ProxyError: - err = sys.exc_info()[1] - self.fatal_error("Failed to get decision list: %s" % err) - - try: - rawconfig = self.proxy.GetConfig().encode('UTF-8') - except Bcfg2.Proxy.ProxyError: - err = sys.exc_info()[1] - self.fatal_error("Failed to download configuration from " - "Bcfg2: %s" % err) - - times['config_download'] = time.time() - return rawconfig - - def run(self): - """Perform client execution phase.""" - times = {} - - # begin configuration - times['start'] = time.time() - - self.logger.info("Starting Bcfg2 client run at %s" % times['start']) - - rawconfig = self.get_config(times=times) - - 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 = sys.exc_info()[1] - 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/>') - for bundle in self.config.getchildren(): - if (bundle.tag == 'Bundle' and - ((self.setup['bundle'] and - bundle.get('name') in self.setup['bundle']) or - (self.setup['skipbundle'] and - bundle.get('name') not in self.setup['skipbundle']))): - newconfig.append(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: # pylint: disable=W0702 - 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: - self.proxy.RecvStats(Bcfg2.Client.XML.tostring( - feedback, - xml_declaration=False).decode('UTF-8')) - except Bcfg2.Proxy.ProxyError: - err = sys.exc_info()[1] - self.logger.error("Failed to upload configuration statistics: " - "%s" % err) - raise SystemExit(2) - - self.logger.info("Finished Bcfg2 client run at %s" % time.time()) - + signal.signal(signal.SIGINT, cb_sigint_handler) + return Client(setup).run() if __name__ == '__main__': - signal.signal(signal.SIGINT, cb_sigint_handler) - Client().run() + sys.exit(main()) diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info index d6d529dec..4c5d8f785 100755 --- a/src/sbin/bcfg2-info +++ b/src/sbin/bcfg2-info @@ -733,7 +733,7 @@ Bcfg2 client itself.""") def _run(self): pass - + def _block(self): pass |