summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Core.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Bcfg2/Server/Core.py')
-rw-r--r--src/lib/Bcfg2/Server/Core.py458
1 files changed, 303 insertions, 155 deletions
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index 8482925b7..f39453edd 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -1,35 +1,21 @@
"""Bcfg2.Server.Core provides the runtime support for Bcfg2 modules."""
+import os
import atexit
import logging
import select
import sys
import threading
import time
+import inspect
+import lxml.etree
from traceback import format_exc
-
-try:
- import lxml.etree
-except ImportError:
- print("Failed to import lxml dependency. Shutting down server.")
- raise SystemExit(1)
-
-from Bcfg2.Component import Component, exposed
-from Bcfg2.Server.Plugin import PluginInitError, PluginExecutionError
+import Bcfg2.settings
+import Bcfg2.Server
+import Bcfg2.Logger
import Bcfg2.Server.FileMonitor
-import Bcfg2.Server.Plugins.Metadata
-# Compatibility imports
-from Bcfg2.Bcfg2Py3k import xmlrpclib
-if sys.hexversion >= 0x03000000:
- from functools import reduce
-
-logger = logging.getLogger('Bcfg2.Server.Core')
-
-
-def critical_error(operation):
- """Log and err, traceback and return an xmlrpc fault to client."""
- logger.error(operation, exc_info=1)
- raise xmlrpclib.Fault(7, "Critical unexpected failure: %s" % (operation))
+from Bcfg2.Bcfg2Py3k import xmlrpclib, reduce
+from Bcfg2.Server.Plugin import PluginInitError, PluginExecutionError
try:
import psyco
@@ -37,6 +23,11 @@ try:
except:
pass
+os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings'
+
+def exposed(func):
+ func.exposed = True
+ return func
def sort_xml(node, key=None):
for child in node:
@@ -54,88 +45,134 @@ class CoreInitError(Exception):
pass
-class Core(Component):
+class BaseCore(object):
"""The Core object is the container for all
Bcfg2 Server logic and modules.
"""
- name = 'bcfg2-server'
- implementation = 'bcfg2-server'
-
- def __init__(self, repo, plugins, password, encoding,
- cfile='/etc/bcfg2.conf', ca=None, setup=None,
- filemonitor='default', start_fam_thread=False):
- Component.__init__(self)
- self.datastore = repo
- if filemonitor not in Bcfg2.Server.FileMonitor.available:
- logger.error("File monitor driver %s not available; "
- "forcing to default" % filemonitor)
- filemonitor = 'default'
+
+ def __init__(self, setup, start_fam_thread=False):
+ self.datastore = setup['repo']
+
+ if setup['debug']:
+ level = logging.DEBUG
+ elif setup['verbose']:
+ level = logging.INFO
+ else:
+ level = logging.WARNING
+ # we set a higher log level for the console by default. we
+ # assume that if someone is running bcfg2-server in such a way
+ # that it _can_ log to console, they want more output. if
+ # level is set to DEBUG, that will get handled by
+ # setup_logging and the console will get DEBUG output.
+ Bcfg2.Logger.setup_logging('bcfg2-server',
+ to_console=logging.INFO,
+ to_syslog=setup['syslog'],
+ to_file=setup['logging'],
+ level=level)
+ self.logger = logging.getLogger('bcfg2-server')
+
try:
- self.fam = Bcfg2.Server.FileMonitor.available[filemonitor]()
+ fm = Bcfg2.Server.FileMonitor.available[setup['filemonitor']]
+ except KeyError:
+ self.logger.error("File monitor driver %s not available; "
+ "forcing to default" % filemonitor)
+ fm = Bcfg2.Server.FileMonitor.available['default']
+ famargs = dict(ignore=[], debug=False)
+ if 'ignore' in setup:
+ famargs['ignore'] = setup['ignore']
+ if 'debug' in setup:
+ famargs['debug'] = setup['debug']
+ try:
+ self.fam = fm(**famargs)
except IOError:
- logger.error("Failed to instantiate fam driver %s" % filemonitor,
- exc_info=1)
- raise CoreInitError("failed to instantiate fam driver (used %s)" % \
- filemonitor)
+ msg = "Failed to instantiate fam driver %s" % setup['filemonitor']
+ self.logger.error(msg, exc_info=1)
+ raise CoreInitError(msg)
self.pubspace = {}
- self.cfile = cfile
+ self.cfile = setup['configfile']
self.cron = {}
self.plugins = {}
self.plugin_blacklist = {}
self.revision = '-1'
- self.password = password
- self.encoding = encoding
+ self.password = setup['password']
+ self.encoding = setup['encoding']
self.setup = setup
atexit.register(self.shutdown)
# Create an event to signal worker threads to shutdown
self.terminate = threading.Event()
- if '' in plugins:
- plugins.remove('')
+ # generate Django ORM settings. this must be done _before_ we
+ # load plugins
+ Bcfg2.settings.read_config(cfile=self.setup['web_configfile'],
+ repo=self.datastore)
- for plugin in plugins:
+ self._database_available = False
+ # verify our database schema
+ try:
+ from Bcfg2.Server.SchemaUpdater import update_database, UpdaterError
+ try:
+ update_database()
+ self._database_available = True
+ except UpdaterError:
+ err = sys.exc_info()[1]
+ self.logger.error("Failed to update database schema: %s" % err)
+ except ImportError:
+ # assume django is not installed
+ pass
+ except Exception:
+ inst = sys.exc_info()[1]
+ self.logger.error("Failed to update database schema")
+ self.logger.error(str(inst))
+ self.logger.error(str(type(inst)))
+ raise CoreInitError
+
+ if '' in setup['plugins']:
+ setup['plugins'].remove('')
+
+ for plugin in setup['plugins']:
if not plugin in self.plugins:
self.init_plugins(plugin)
# Remove blacklisted plugins
for p, bl in list(self.plugin_blacklist.items()):
if len(bl) > 0:
- logger.error("The following plugins conflict with %s;"
- "Unloading %s" % (p, bl))
+ self.logger.error("The following plugins conflict with %s;"
+ "Unloading %s" % (p, bl))
for plug in bl:
del self.plugins[plug]
# This section logs the experimental plugins
expl = [plug for (name, plug) in list(self.plugins.items())
if plug.experimental]
if expl:
- logger.info("Loading experimental plugin(s): %s" % \
- (" ".join([x.name for x in expl])))
- logger.info("NOTE: Interfaces subject to change")
+ self.logger.info("Loading experimental plugin(s): %s" %
+ (" ".join([x.name for x in expl])))
+ self.logger.info("NOTE: Interfaces subject to change")
# This section logs the deprecated plugins
depr = [plug for (name, plug) in list(self.plugins.items())
if plug.deprecated]
if depr:
- logger.info("Loading deprecated plugin(s): %s" % \
- (" ".join([x.name for x in depr])))
+ self.logger.info("Loading deprecated plugin(s): %s" %
+ (" ".join([x.name for x in depr])))
mlist = self.plugins_by_type(Bcfg2.Server.Plugin.Metadata)
if len(mlist) == 1:
self.metadata = mlist[0]
else:
- logger.error("No Metadata Plugin loaded; failed to instantiate Core")
+ self.logger.error("No Metadata Plugin loaded; "
+ "failed to instantiate Core")
raise CoreInitError("No Metadata Plugin")
self.statistics = self.plugins_by_type(Bcfg2.Server.Plugin.Statistics)
self.pull_sources = self.plugins_by_type(Bcfg2.Server.Plugin.PullSource)
self.generators = self.plugins_by_type(Bcfg2.Server.Plugin.Generator)
self.structures = self.plugins_by_type(Bcfg2.Server.Plugin.Structure)
self.connectors = self.plugins_by_type(Bcfg2.Server.Plugin.Connector)
- self.ca = ca
- self.fam_thread = threading.Thread(target=self._file_monitor_thread)
+ self.ca = setup['ca']
+ self.fam_thread = \
+ threading.Thread(name="%sFAMThread" % setup['filemonitor'],
+ target=self._file_monitor_thread)
+ self.lock = threading.Lock()
+
if start_fam_thread:
self.fam_thread.start()
- self.monitor_cfile()
-
- def monitor_cfile(self):
- if self.setup:
self.fam.AddMonitor(self.cfile, self.setup)
def plugins_by_type(self, base_cls):
@@ -171,16 +208,21 @@ class Core(Component):
def init_plugins(self, plugin):
"""Handling for the plugins."""
+ self.logger.debug("Loading plugin %s" % plugin)
try:
mod = getattr(__import__("Bcfg2.Server.Plugins.%s" %
(plugin)).Server.Plugins, plugin)
except ImportError:
try:
- mod = __import__(plugin)
+ mod = __import__(plugin, globals(), locals(), [plugin.split('.')[-1]])
except:
- logger.error("Failed to load plugin %s" % (plugin))
+ self.logger.error("Failed to load plugin %s" % plugin)
return
- plug = getattr(mod, plugin)
+ try:
+ plug = getattr(mod, plugin.split('.')[-1])
+ except AttributeError:
+ self.logger.error("Failed to load plugin %s (AttributeError)" % plugin)
+ return
# Blacklist conflicting plugins
cplugs = [conflict for conflict in plug.conflicts
if conflict in self.plugins]
@@ -188,18 +230,35 @@ class Core(Component):
try:
self.plugins[plugin] = plug(self, self.datastore)
except PluginInitError:
- logger.error("Failed to instantiate plugin %s" % (plugin))
+ self.logger.error("Failed to instantiate plugin %s" % plugin,
+ exc_info=1)
except:
- logger.error("Unexpected instantiation failure for plugin %s" %
- (plugin), exc_info=1)
+ self.logger.error("Unexpected instantiation failure for plugin %s" %
+ plugin, exc_info=1)
def shutdown(self):
"""Shutting down the plugins."""
if not self.terminate.isSet():
self.terminate.set()
+ self.fam.shutdown()
for plugin in list(self.plugins.values()):
plugin.shutdown()
+ def client_run_hook(self, hook, metadata):
+ """Checks the data structure."""
+ for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.ClientRunHooks):
+ try:
+ getattr(plugin, hook)(metadata)
+ except AttributeError:
+ err = sys.exc_info()[1]
+ self.logger.error("Unknown attribute: %s" % err)
+ raise
+ except:
+ err = sys.exc_info()[1]
+ self.logger.error("%s: Error invoking hook %s: %s" % (plugin,
+ hook,
+ err))
+
def validate_structures(self, metadata, data):
"""Checks the data structure."""
for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.StructureValidator):
@@ -207,12 +266,12 @@ class Core(Component):
plugin.validate_structures(metadata, data)
except Bcfg2.Server.Plugin.ValidationError:
err = sys.exc_info()[1]
- logger.error("Plugin %s structure validation failed: %s" \
- % (plugin.name, err.message))
+ self.logger.error("Plugin %s structure validation failed: %s" %
+ (plugin.name, err))
raise
except:
- logger.error("Plugin %s: unexpected structure validation failure" \
- % (plugin.name), exc_info=1)
+ self.logger.error("Plugin %s: unexpected structure validation "
+ "failure" % plugin.name, exc_info=1)
def validate_goals(self, metadata, data):
"""Checks that the config matches the goals enforced by the plugins."""
@@ -221,23 +280,23 @@ class Core(Component):
plugin.validate_goals(metadata, data)
except Bcfg2.Server.Plugin.ValidationError:
err = sys.exc_info()[1]
- logger.error("Plugin %s goal validation failed: %s" \
- % (plugin.name, err.message))
+ self.logger.error("Plugin %s goal validation failed: %s" %
+ (plugin.name, err.message))
raise
except:
- logger.error("Plugin %s: unexpected goal validation failure" \
- % (plugin.name), exc_info=1)
+ self.logger.error("Plugin %s: unexpected goal validation "
+ "failure" % plugin.name, exc_info=1)
def GetStructures(self, metadata):
"""Get all structures for client specified by metadata."""
structures = reduce(lambda x, y: x + y,
- [struct.BuildStructures(metadata) for struct \
- in self.structures], [])
+ [struct.BuildStructures(metadata)
+ for struct in self.structures], [])
sbundles = [b.get('name') for b in structures if b.tag == 'Bundle']
missing = [b for b in metadata.bundles if b not in sbundles]
if missing:
- logger.error("Client %s configuration missing bundles: %s" \
- % (metadata.hostname, ':'.join(missing)))
+ self.logger.error("Client %s configuration missing bundles: %s" %
+ (metadata.hostname, ':'.join(missing)))
return structures
def BindStructure(self, structure, metadata):
@@ -252,14 +311,14 @@ class Core(Component):
exc = sys.exc_info()[1]
if 'failure' not in entry.attrib:
entry.set('failure', 'bind error: %s' % format_exc())
- logger.error("Failed to bind entry %s:%s: %s" %
- (entry.tag, entry.get('name'), exc))
+ self.logger.error("Failed to bind entry %s:%s: %s" %
+ (entry.tag, entry.get('name'), exc))
except Exception:
exc = sys.exc_info()[1]
if 'failure' not in entry.attrib:
entry.set('failure', 'bind error: %s' % format_exc())
- logger.error("Unexpected failure in BindStructure: %s %s" \
- % (entry.tag, entry.get('name')), exc_info=1)
+ self.logger.error("Unexpected failure in BindStructure: %s %s" %
+ (entry.tag, entry.get('name')), exc_info=1)
def Bind(self, entry, metadata):
"""Bind an entry using the appropriate generator."""
@@ -275,11 +334,11 @@ class Core(Component):
return ret
except:
entry.set('name', oldname)
- logger.error("Failed binding entry %s:%s with altsrc %s" \
- % (entry.tag, entry.get('name'),
- entry.get('altsrc')))
- logger.error("Falling back to %s:%s" % (entry.tag,
- entry.get('name')))
+ self.logger.error("Failed binding entry %s:%s with altsrc %s" %
+ (entry.tag, entry.get('name'),
+ entry.get('altsrc')))
+ self.logger.error("Falling back to %s:%s" % (entry.tag,
+ entry.get('name')))
glist = [gen for gen in self.generators if
entry.get('name') in gen.Entries.get(entry.tag, {})]
@@ -288,8 +347,8 @@ class Core(Component):
metadata)
elif len(glist) > 1:
generators = ", ".join([gen.name for gen in glist])
- logger.error("%s %s served by multiple generators: %s" % \
- (entry.tag, entry.get('name'), generators))
+ self.logger.error("%s %s served by multiple generators: %s" %
+ (entry.tag, entry.get('name'), generators))
g2list = [gen for gen in self.generators if
gen.HandlesEntry(entry, metadata)]
if len(g2list) == 1:
@@ -301,18 +360,21 @@ class Core(Component):
def BuildConfiguration(self, client):
"""Build configuration for clients."""
start = time.time()
- config = lxml.etree.Element("Configuration", version='2.0', \
+ config = lxml.etree.Element("Configuration", version='2.0',
revision=self.revision)
try:
meta = self.build_metadata(client)
- except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError:
- logger.error("Metadata consistency error for client %s" % client)
+ except Bcfg2.Server.Plugin.MetadataConsistencyError:
+ self.logger.error("Metadata consistency error for client %s" %
+ client)
return lxml.etree.Element("error", type='metadata error')
+ self.client_run_hook("start_client_run", meta)
+
try:
structures = self.GetStructures(meta)
except:
- logger.error("error in GetStructures", exc_info=1)
+ self.logger.error("error in GetStructures", exc_info=1)
return lxml.etree.Element("error", type='structure error')
self.validate_structures(meta, structures)
@@ -324,7 +386,8 @@ class Core(Component):
key = (entry.tag, entry.get('name'))
if key in esrcs:
if esrcs[key] != entry.get('altsrc'):
- logger.error("Found inconsistent altsrc mapping for entry %s:%s" % key)
+ self.logger.error("Found inconsistent altsrc mapping "
+ "for entry %s:%s" % key)
else:
esrcs[key] = entry.get('altsrc', None)
del esrcs
@@ -334,15 +397,49 @@ class Core(Component):
self.BindStructure(astruct, meta)
config.append(astruct)
except:
- logger.error("error in BindStructure", exc_info=1)
+ self.logger.error("error in BindStructure", exc_info=1)
self.validate_goals(meta, config)
+ self.client_run_hook("end_client_run", meta)
+
sort_xml(config, key=lambda e: e.get('name'))
- logger.info("Generated config for %s in %.03f seconds" % \
- (client, time.time() - start))
+ self.logger.info("Generated config for %s in %.03f seconds" %
+ (client, time.time() - start))
return config
+ def run(self, **kwargs):
+ """ run the server core """
+ raise NotImplementedError
+
+ def _daemonize(self):
+ child_pid = os.fork()
+ if child_pid != 0:
+ return
+
+ os.setsid()
+
+ child_pid = os.fork()
+ if child_pid != 0:
+ os._exit(0)
+
+ redirect_file = open("/dev/null", "w+")
+ os.dup2(redirect_file.fileno(), sys.__stdin__.fileno())
+ os.dup2(redirect_file.fileno(), sys.__stdout__.fileno())
+ os.dup2(redirect_file.fileno(), sys.__stderr__.fileno())
+
+ os.chdir(os.sep)
+
+ pidfile = open(self.setup['daemon'] or "/dev/null", "w")
+ pidfile.write("%s\n" % os.getpid())
+ pidfile.close()
+
+ return os.getpid()
+
+ def critical_error(self, operation):
+ """ this should be overridden by child classes """
+ self.logger.fatal(operation, exc_info=1)
+
def GetDecisions(self, metadata, mode):
"""Get data for the decision list."""
result = []
@@ -350,15 +447,15 @@ class Core(Component):
try:
result += plugin.GetDecisions(metadata, mode)
except:
- logger.error("Plugin: %s failed to generate decision list" \
- % plugin.name, exc_info=1)
+ self.logger.error("Plugin: %s failed to generate decision list"
+ % plugin.name, exc_info=1)
return result
def build_metadata(self, client_name):
"""Build the metadata structure."""
if not hasattr(self, 'metadata'):
# some threads start before metadata is even loaded
- raise Bcfg2.Server.Plugins.Metadata.MetadataRuntimeError
+ raise Bcfg2.Server.Plugin.MetadataRuntimeError
imd = self.metadata.get_initial_metadata(client_name)
for conn in self.connectors:
grps = conn.get_additional_groups(imd)
@@ -378,102 +475,147 @@ class Core(Component):
try:
plugin.process_statistics(meta, statistics)
except:
- logger.error("Plugin %s failed to process stats from %s" \
- % (plugin.name, meta.hostname),
- exc_info=1)
+ self.logger.error("Plugin %s failed to process stats from "
+ "%s" % (plugin.name, meta.hostname),
+ exc_info=1)
+
+ self.logger.info("Client %s reported state %s" % (client_name,
+ state.get('state')))
+ self.client_run_hook("end_statistics", meta)
+
+ def resolve_client(self, address, cleanup_cache=False, metadata=True):
+ try:
+ client = self.metadata.resolve_client(address,
+ cleanup_cache=cleanup_cache)
+ if metadata:
+ meta = self.build_metadata(client)
+ else:
+ meta = None
+ except Bcfg2.Server.Plugin.MetadataConsistencyError:
+ err = sys.exc_info()[1]
+ self.critical_error("Client metadata resolution error for %s: %s" %
+ (address[0], err))
+ except Bcfg2.Server.Plugin.MetadataRuntimeError:
+ err = sys.exc_info()[1]
+ self.critical_error('Metadata system runtime failure for %s: %s' %
+ (address[0], err))
+ return (client, meta)
+
+ def critical_error(self, operation):
+ """Log and err, traceback and return an xmlrpc fault to client."""
+ self.logger.error(operation, exc_info=1)
+ raise xmlrpclib.Fault(xmlrpclib.APPLICATION_ERROR,
+ "Critical failure: %s" % operation)
+
+ def _get_rmi(self):
+ rmi = dict()
+ if self.plugins:
+ for pname, pinst in list(self.plugins.items()):
+ for mname in pinst.__rmi__:
+ rmi["%s.%s" % (pname, mname)] = getattr(pinst, mname)
+ return rmi
- logger.info("Client %s reported state %s" % (client_name,
- state.get('state')))
# XMLRPC handlers start here
+ @exposed
+ def listMethods(self, address):
+ methods = [name
+ for name, func in inspect.getmembers(self, callable)
+ if getattr(func, "exposed", False)]
+ methods.extend(self._get_rmi().keys())
+ return methods
+
+ @exposed
+ def methodHelp(self, address, method_name):
+ raise NotImplementedError
+
+ @exposed
+ def DeclareVersion(self, address, version):
+ """ declare the client version """
+ client, metadata = self.resolve_client(address)
+ try:
+ self.metadata.set_version(client, version)
+ except (Bcfg2.Server.Plugin.MetadataConsistencyError,
+ Bcfg2.Server.Plugin.MetadataRuntimeError):
+ err = sys.exc_info()[1]
+ self.critical_error("Unable to set version for %s: %s" %
+ (client, err))
+ return True
@exposed
def GetProbes(self, address):
"""Fetch probes for a particular client."""
resp = lxml.etree.Element('probes')
+ client, metadata = self.resolve_client(address, cleanup_cache=True)
try:
- name = self.metadata.resolve_client(address, cleanup_cache=True)
- meta = self.build_metadata(name)
-
for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.Probing):
- for probe in plugin.GetProbes(meta):
+ for probe in plugin.GetProbes(metadata):
resp.append(probe)
- return lxml.etree.tostring(resp, encoding='UTF-8',
- xml_declaration=True)
- except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError:
- warning = 'Client metadata resolution error for %s' % address[0]
- self.logger.warning(warning)
- raise xmlrpclib.Fault(6, warning + "; check server log")
- except Bcfg2.Server.Plugins.Metadata.MetadataRuntimeError:
- err_msg = 'Metadata system runtime failure'
- self.logger.error(err_msg)
- raise xmlrpclib.Fault(6, err_msg)
+ return lxml.etree.tostring(resp,
+ xml_declaration=False).decode('UTF-8')
except:
- critical_error("Error determining client probes")
+ err = sys.exc_info()[1]
+ self.critical_error("Error determining probes for %s: %s" %
+ (client, err))
@exposed
def RecvProbeData(self, address, probedata):
"""Receive probe data from clients."""
+ client, metadata = self.resolve_client(address)
try:
- name = self.metadata.resolve_client(address)
- meta = self.build_metadata(name)
- except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError:
- warning = 'Metadata consistency error'
- self.logger.warning(warning)
- raise xmlrpclib.Fault(6, warning)
- # clear dynamic groups
- self.metadata.cgroups[meta.hostname] = []
- try:
- xpdata = lxml.etree.XML(probedata.encode('utf-8'))
+ xpdata = lxml.etree.XML(probedata.encode('utf-8'),
+ parser=Bcfg2.Server.XMLParser)
except:
- self.logger.error("Failed to parse probe data from client %s" % \
- (address[0]))
- return False
+ err = sys.exc_info()[1]
+ self.critical_error("Failed to parse probe data from client %s: %s"
+ % (client, err))
sources = []
[sources.append(data.get('source')) for data in xpdata
if data.get('source') not in sources]
for source in sources:
if source not in self.plugins:
- self.logger.warning("Failed to locate plugin %s" % (source))
+ self.logger.warning("Failed to locate plugin %s" % source)
continue
dl = [data for data in xpdata if data.get('source') == source]
try:
- self.plugins[source].ReceiveData(meta, dl)
+ self.plugins[source].ReceiveData(metadata, dl)
except:
- logger.error("Failed to process probe data from client %s" % \
- (address[0]), exc_info=1)
+ err = sys.exc_info()[1]
+ self.critical_error("Failed to process probe data from client "
+ "%s: %s" %
+ (client, err))
return True
@exposed
def AssertProfile(self, address, profile):
"""Set profile for a client."""
+ client = self.resolve_client(address, metadata=False)[0]
try:
- client = self.metadata.resolve_client(address)
self.metadata.set_profile(client, profile, address)
- except (Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError,
- Bcfg2.Server.Plugins.Metadata.MetadataRuntimeError):
- warning = 'Metadata consistency error'
- self.logger.warning(warning)
- raise xmlrpclib.Fault(6, warning)
+ except (Bcfg2.Server.Plugin.MetadataConsistencyError,
+ Bcfg2.Server.Plugin.MetadataRuntimeError):
+ err = sys.exc_info()[1]
+ self.critical_error("Unable to assert profile for %s: %s" %
+ (client, err))
return True
@exposed
def GetConfig(self, address, checksum=False):
"""Build config for a client."""
+ client = self.resolve_client(address)[0]
try:
- client = self.metadata.resolve_client(address)
config = self.BuildConfiguration(client)
- return lxml.etree.tostring(config, encoding='UTF-8',
- xml_declaration=True)
- except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError:
- self.logger.warning("Metadata consistency failure for %s" % (address))
- raise xmlrpclib.Fault(6, "Metadata consistency failure")
+ return lxml.etree.tostring(config,
+ xml_declaration=False).decode('UTF-8')
+ except Bcfg2.Server.Plugin.MetadataConsistencyError:
+ self.critical_error("Metadata consistency failure for %s" % client)
@exposed
def RecvStats(self, address, stats):
"""Act on statistics upload."""
- sdata = lxml.etree.XML(stats.encode('utf-8'))
- client = self.metadata.resolve_client(address)
+ client = self.resolve_client(address)[0]
+ sdata = lxml.etree.XML(stats.encode('utf-8'),
+ parser=Bcfg2.Server.XMLParser)
self.process_statistics(client, sdata)
return "<ok/>"
@@ -483,11 +625,17 @@ class Core(Component):
else:
# No ca, so no cert validation can be done
acert = None
- return self.metadata.AuthenticateConnection(acert, user, password, address)
+ return self.metadata.AuthenticateConnection(acert, user, password,
+ address)
@exposed
def GetDecisionList(self, address, mode):
"""Get the data of the decision list."""
- client = self.metadata.resolve_client(address)
- meta = self.build_metadata(client)
- return self.GetDecisions(meta, mode)
+ client, metadata = self.resolve_client(address)
+ return self.GetDecisions(metadata, mode)
+
+ @property
+ def database_available(self):
+ """Is the database configured and available"""
+ return self._database_available
+