summaryrefslogtreecommitdiffstats
path: root/src/sbin
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2012-09-28 12:04:49 -0400
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2012-09-28 12:04:49 -0400
commite224ae0359840d54b9bd995a5231e4774321755a (patch)
tree30c47babc201d52397dc12cfa7df64c97d50aa69 /src/sbin
parent1bdb14055dd1b2395047793ee28c17bbae65c845 (diff)
downloadbcfg2-e224ae0359840d54b9bd995a5231e4774321755a.tar.gz
bcfg2-e224ae0359840d54b9bd995a5231e4774321755a.tar.bz2
bcfg2-e224ae0359840d54b9bd995a5231e4774321755a.zip
made client runs abort on probe failure, added option to disable that
Diffstat (limited to 'src/sbin')
-rwxr-xr-xsrc/sbin/bcfg2247
1 files changed, 133 insertions, 114 deletions
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2
index f41479d77..8d8521b05 100755
--- a/src/sbin/bcfg2
+++ b/src/sbin/bcfg2
@@ -11,31 +11,30 @@ import stat
import sys
import tempfile
import time
+import Bcfg2.Proxy
+import Bcfg2.Logger
import Bcfg2.Options
import Bcfg2.Client.XML
import Bcfg2.Client.Frame
import Bcfg2.Client.Tools
-# Compatibility imports
from Bcfg2.Compat import xmlrpclib
-
from Bcfg2.version import __version__
+from subprocess import Popen, PIPE
-import Bcfg2.Proxy
-import Bcfg2.Logger
-
-logger = logging.getLogger('bcfg2')
def cb_sigint_handler(signum, frame):
- """Exit upon CTRL-C."""
- os._exit(1)
+ """ Exit upon CTRL-C. """
+ raise SystemExit(1)
-class Client:
+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)
@@ -82,6 +81,15 @@ class Client:
if not self.setup['server'].startswith('https://'):
self.setup['server'] = 'https://' + self.setup['server']
+ 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')
@@ -91,78 +99,116 @@ class Client:
source=probe.get('source'))
try:
scripthandle, scriptname = tempfile.mkstemp()
- script = open(scriptname, 'w+')
+ script = os.fdopen(scripthandle, '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,
+ 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
- ret.text = os.popen(script.name).read().strip()
+ 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(script.name)
- except:
- self.logger.error("Failed to execute probe: %s" % (name), exc_info=1)
- raise SystemExit(1)
+ 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))
- os._exit(1)
+ 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()
- def run(self):
- """Perform client execution phase."""
- times = {}
+ try:
+ probes = Bcfg2.Client.XML.XML(str(self.proxy.GetProbes()))
+ except (Bcfg2.Proxy.ProxyError,
+ Bcfg2.Proxy.CertificateError,
+ socket.gaierror,
+ socket.error):
+ 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)
- # begin configuration
- times['start'] = time.time()
+ times['probe_download'] = time.time()
- self.logger.info("Starting Bcfg2 client run at %s" % times['start'])
+ # 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']))
- configfile = open(self.setup['file'], 'r')
- rawconfig = configfile.read()
- configfile.close()
+ 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']))
- 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'],
- timeout=self.setup['timeout'],
- retries=int(self.setup['retries']),
- delay=int(self.setup['retry_delay']))
-
if self.setup['profile']:
try:
- proxy.AssertProfile(self.setup['profile'])
+ self.proxy.AssertProfile(self.setup['profile'])
except Bcfg2.Proxy.ProxyError:
err = sys.exc_info()[1]
- self.fatal_error("Failed to set client profile")
- self.logger.error(str(err))
- raise SystemExit(1)
+ self.fatal_error("Failed to set client profile: %s" % err)
try:
- probe_data = proxy.DeclareVersion(__version__)
+ self.proxy.DeclareVersion(__version__)
except xmlrpclib.Fault:
err = sys.exc_info()[1]
if (err.faultCode == xmlrpclib.METHOD_NOT_FOUND or
@@ -179,69 +225,38 @@ class Client:
err = sys.exc_info()[1]
self.logger.error("Failed to declare version: %s" % err)
- try:
- probe_data = proxy.GetProbes()
- except (Bcfg2.Proxy.ProxyError,
- Bcfg2.Proxy.CertificateError,
- socket.gaierror,
- socket.error):
- err = sys.exc_info()[1]
- self.logger.error("Failed to download probes from bcfg2: %s" %
- err)
- raise SystemExit(1)
-
- times['probe_download'] = time.time()
-
- try:
- probes = Bcfg2.Client.XML.XML(str(probe_data))
- except Bcfg2.Client.XML.ParseError:
- syntax_error = sys.exc_info()[1]
- 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,
- xml_declaration=False).decode('UTF-8'))
- except Bcfg2.Proxy.ProxyError:
- err = sys.exc_info()[1]
- self.logger.error("Failed to upload probe data: %s" % err)
- raise SystemExit(1)
-
- times['probe_upload'] = time.time()
+ self.run_probes(times=times)
if self.setup['decision'] in ['whitelist', 'blacklist']:
try:
self.setup['decision_list'] = \
- proxy.GetDecisionList(self.setup['decision'])
+ 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.logger.error("Failed to get decision list: %s" % err)
- raise SystemExit(1)
+ self.fatal_error("Failed to get decision list: %s" % err)
try:
- rawconfig = proxy.GetConfig().encode('UTF-8')
+ rawconfig = self.proxy.GetConfig().encode('UTF-8')
except Bcfg2.Proxy.ProxyError:
err = sys.exc_info()[1]
- self.logger.error("Failed to download configuration from "
- "Bcfg2: %s" % err)
- raise SystemExit(2)
+ 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:
@@ -268,13 +283,13 @@ class Client:
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
- ((self.setup['bundle'] and
- bundle.get('name') in self.setup['bundle']) or
- (self.setup['skipbundle'] and
- bundle.get('name') not in self.setup['skipbundle'])))]
+ 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,
@@ -287,33 +302,38 @@ class Client:
try:
lockfile = open(self.setup['lockfile'], 'w')
try:
- fcntl.lockf(lockfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
+ 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:
+ # 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
+ # 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)
+ 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,
- xml_declaration=False).decode('UTF-8'))
+ 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: "
@@ -322,8 +342,7 @@ class Client:
self.logger.info("Finished Bcfg2 client run at %s" % time.time())
+
if __name__ == '__main__':
signal.signal(signal.SIGINT, cb_sigint_handler)
- client = Client()
- spid = os.getpid()
- client.run()
+ Client().run()