diff options
Diffstat (limited to 'src/lib/Bcfg2/Server/Core.py')
-rw-r--r-- | src/lib/Bcfg2/Server/Core.py | 133 |
1 files changed, 118 insertions, 15 deletions
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py index 07f9e0588..c69e8b055 100644 --- a/src/lib/Bcfg2/Server/Core.py +++ b/src/lib/Bcfg2/Server/Core.py @@ -4,8 +4,9 @@ implementations inherit from. """ import os import sys import time -import select import atexit +import select +import signal import logging import inspect import threading @@ -119,13 +120,36 @@ class BaseCore(object): #: A :class:`logging.Logger` object for use by the core self.logger = logging.getLogger('bcfg2-server') + #: Log levels for the various logging handlers with debug True + #: and False. Each loglevel dict is a dict of ``logger name + #: => log level``; the logger names are set in + #: :mod:`Bcfg2.Logger`. The logger name ``default`` is + #: special, and will be used for any log handlers whose name + #: does not appear elsewhere in the dict. At a minimum, + #: ``default`` must be provided. + self._loglevels = {True: dict(default=logging.DEBUG), + False: dict(console=logging.INFO, + default=level)} + + #: Used to keep track of the current debug state of the core. + self.debug_flag = False + + # enable debugging on the core now. debugging is enabled on + # everything else later + if self.setup['debug']: + self.set_core_debug(None, setup['debug']) + if 'ignore' not in self.setup: self.setup.add_option('ignore', SERVER_FAM_IGNORE) self.setup.reparse() + famargs = dict(filemonitor=self.setup['filemonitor'], debug=self.setup['debug'], ignore=self.setup['ignore']) - if self.setup['filemonitor'] not in Bcfg2.Server.FileMonitor.available: + try: + filemonitor = \ + Bcfg2.Server.FileMonitor.available[setup['filemonitor']] + except KeyError: self.logger.error("File monitor driver %s not available; " "forcing to default" % self.setup['filemonitor']) famargs['filemonitor'] = 'default' @@ -281,6 +305,14 @@ class BaseCore(object): #: The CA that signed the server cert self.ca = self.setup['ca'] + def hdlr(sig, frame): # pylint: disable=W0613 + """ Handle SIGINT/Ctrl-C by shutting down the core and exiting + properly. """ + self.shutdown() + os._exit(1) # pylint: disable=W0212 + + signal.signal(signal.SIGINT, hdlr) + #: The FAM :class:`threading.Thread`, #: :func:`_file_monitor_thread` self.fam_thread = \ @@ -295,6 +327,10 @@ class BaseCore(object): #: metadata self.metadata_cache = Cache() + if self.debug_flag: + # enable debugging on everything else. + self.plugins[plugin].set_debug(self.debug_flag) + def plugins_by_type(self, base_cls): """ Return a list of loaded plugins that match the passed type. @@ -392,6 +428,7 @@ class BaseCore(object): def shutdown(self): """ Perform plugin and FAM shutdown tasks. """ + self.logger.debug("Shutting down core...") if not self.terminate.isSet(): self.terminate.set() self.fam.shutdown() @@ -427,6 +464,8 @@ class BaseCore(object): hook. :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata """ + self.logger.debug("Running %s hooks for %s" % (hook, + metadata.hostname)) start = time.time() try: for plugin in \ @@ -460,6 +499,7 @@ class BaseCore(object): client :type data: list of lxml.etree._Element objects """ + self.logger.debug("Validating structures for %s" % metadata.hostname) for plugin in \ self.plugins_by_type(Bcfg2.Server.Plugin.StructureValidator): try: @@ -486,6 +526,7 @@ class BaseCore(object): client :type data: list of lxml.etree._Element objects """ + self.logger.debug("Validating goals for %s" % metadata.hostname) for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.GoalValidator): try: plugin.validate_goals(metadata, data) @@ -506,6 +547,7 @@ class BaseCore(object): :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata :returns: list of :class:`lxml.etree._Element` objects """ + self.logger.debug("Getting structures for %s" % metadata.hostname) structures = list(chain(*[struct.BuildStructures(metadata) for struct in self.structures])) sbundles = [b.get('name') for b in structures if b.tag == 'Bundle'] @@ -528,6 +570,7 @@ class BaseCore(object): structures to. Modified in-place. :type config: lxml.etree._Element """ + self.logger.debug("Binding structures for %s" % metadata.hostname) for astruct in structures: try: self.BindStructure(astruct, metadata) @@ -544,6 +587,9 @@ class BaseCore(object): :param metadata: Client metadata to bind structure for :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata """ + self.logger.debug("Binding structure %s for %s" % + (structure.get("name", "unknown"), + metadata.hostname)) for entry in structure.getchildren(): if entry.tag.startswith("Bound"): entry.tag = entry.tag[5:] @@ -619,6 +665,7 @@ class BaseCore(object): :type client: string :returns: :class:`lxml.etree._Element` - A complete Bcfg2 configuration document """ + self.logger.debug("Building configuration for %s" % client) start = time.time() config = lxml.etree.Element("Configuration", version='2.0', revision=self.revision) @@ -718,6 +765,12 @@ class BaseCore(object): self.shutdown() raise + if self.setup['fam_blocking']: + time.sleep(1) + while self.fam.pending() != 0: + time.sleep(1) + + self.set_debug(None, self.debug_flag) self._block() def _daemonize(self): @@ -746,6 +799,7 @@ class BaseCore(object): :type mode: string :returns: list of Decision tuples ``(<entry tag>, <entry name>)`` """ + self.logger.debug("Getting decision list for %s" % metadata.hostname) result = [] for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.Decision): try: @@ -816,6 +870,7 @@ class BaseCore(object): else: imd = self.metadata_cache.get(client_name, None) if not imd: + self.logger.debug("Building metadata for %s" % client_name) imd = self.metadata.get_initial_metadata(client_name) for conn in self.connectors: grps = conn.get_additional_groups(imd) @@ -837,6 +892,7 @@ class BaseCore(object): :param statistics: The statistics document to process :type statistics: lxml.etree._Element """ + self.logger.debug("Processing statistics for %s" % client_name) meta = self.build_metadata(client_name) state = statistics.find(".//Statistics") if state.get('version') >= '2.0': @@ -907,10 +963,12 @@ class BaseCore(object): def _get_rmi(self): """ Get a list of RMI calls exposed by plugins """ 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) + for pname, pinst in list(self.plugins.items()): + for mname in pinst.__rmi__: + rmi["%s.%s" % (pname, mname)] = getattr(pinst, mname) + famname = self.fam.__class__.__name__ + for mname in self.fam.__rmi__: + rmi["%s.%s" % (famname, mname)] = getattr(self.fam, mname) return rmi def _resolve_exposed_method(self, method_name): @@ -964,6 +1022,7 @@ class BaseCore(object): return func.__doc__ @exposed + @track_statistics() def DeclareVersion(self, address, version): """ Declare the client version. @@ -974,7 +1033,9 @@ class BaseCore(object): :returns: bool - True on success :raises: :exc:`xmlrpclib.Fault` """ - client = self.resolve_client(address)[0] + client = self.resolve_client(address, metadata=False)[0] + self.logger.debug("%s is running Bcfg2 client version %s" % (client, + version)) try: self.metadata.set_version(client, version) except (Bcfg2.Server.Plugin.MetadataConsistencyError, @@ -996,6 +1057,7 @@ class BaseCore(object): """ resp = lxml.etree.Element('probes') client, metadata = self.resolve_client(address, cleanup_cache=True) + self.logger.debug("Getting probes for %s" % client) try: for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.Probing): for probe in plugin.GetProbes(metadata): @@ -1017,6 +1079,7 @@ class BaseCore(object): :raises: :exc:`xmlrpclib.Fault` """ client, metadata = self.resolve_client(address) + self.logger.debug("Receiving probe data from %s" % client) if self.metadata_cache_mode == 'cautious': # clear the metadata cache right after building the # metadata object; that way the cache is cleared for any @@ -1063,6 +1126,7 @@ class BaseCore(object): :raises: :exc:`xmlrpclib.Fault` """ client = self.resolve_client(address, metadata=False)[0] + self.logger.debug("%s sets its profile to %s" % (client, profile)) try: self.metadata.set_profile(client, profile, address) except (Bcfg2.Server.Plugin.MetadataConsistencyError, @@ -1171,22 +1235,35 @@ class BaseCore(object): :type address: tuple :returns: bool - The new debug state of the FAM """ - for plugin in self.plugins.values(): - plugin.toggle_debug() - return self.toggle_fam_debug(address) + return self.set_debug(address, not self.debug_flag) @exposed - def toggle_fam_debug(self, _): + def toggle_core_debug(self, address): + """ Toggle debug status of the server core + + :param address: Client (address, hostname) pair + :type address: tuple + :returns: bool - The new debug state of the FAM + """ + return self.set_core_debug(address, not self.debug_flag) + + @exposed + def toggle_fam_debug(self, address): """ Toggle debug status of the FAM :returns: bool - The new debug state of the FAM """ - return self.fam.toggle_debug() + self.logger.warning("Deprecated method set_fam_debug called by %s" % + address[0]) + return "This method is deprecated and will be removed in a future " + \ + "release\n%s" % self.fam.toggle_debug() @exposed def set_debug(self, address, debug): """ Explicitly set debug status of the FAM and all plugins + :param address: Client (address, hostname) pair + :type address: tuple :param debug: The new debug status. This can either be a boolean, or a string describing the state (e.g., "true" or "false"; case-insensitive) @@ -1197,10 +1274,33 @@ class BaseCore(object): debug = debug.lower() == "true" for plugin in self.plugins.values(): plugin.set_debug(debug) - return self.set_fam_debug(address, debug) + rv = self.set_core_debug(address, debug) + return self.fam.set_debug(debug) and rv + + @exposed + def set_core_debug(self, _, debug): + """ Explicity set debug status of the server core + + :param debug: The new debug status. This can either be a + boolean, or a string describing the state (e.g., + "true" or "false"; case-insensitive) + :type debug: bool or string + :returns: bool - The new debug state of the FAM + """ + if debug not in [True, False]: + debug = debug.lower() == "true" + self.debug_flag = debug + self.logger.info("Core: debug = %s" % debug) + levels = self._loglevels[self.debug_flag] + for handler in logging.root.handlers: + level = levels.get(handler.name, levels['default']) + self.logger.debug("Setting %s log handler to %s" % + (handler.name, logging.getLevelName(level))) + handler.setLevel(level) + return self.debug_flag @exposed - def set_fam_debug(self, _, debug): + def set_fam_debug(self, address, debug): """ Explicitly set debug status of the FAM :param debug: The new debug status of the FAM. This can @@ -1212,4 +1312,7 @@ class BaseCore(object): """ if debug not in [True, False]: debug = debug.lower() == "true" - return self.fam.set_debug(debug) + self.logger.warning("Deprecated method set_fam_debug called by %s" % + address[0]) + return "This method is deprecated and will be removed in a future " + \ + "release\n%s" % self.fam.set_debug(debug) |