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.py94
1 files changed, 73 insertions, 21 deletions
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index 398053374..892f2832a 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -19,14 +19,13 @@ import Bcfg2.Server.Statistics
import Bcfg2.Server.FileMonitor
from itertools import chain
from Bcfg2.Server.Cache import Cache
-from Bcfg2.Compat import xmlrpclib # pylint: disable=W0622
+from Bcfg2.Compat import xmlrpclib, wraps # pylint: disable=W0622
from Bcfg2.Server.Plugin.exceptions import * # pylint: disable=W0401,W0614
from Bcfg2.Server.Plugin.interfaces import * # pylint: disable=W0401,W0614
from Bcfg2.Server.Plugin import track_statistics
try:
from django.core.exceptions import ImproperlyConfigured
- from django.core import management
import django.conf
HAS_DJANGO = True
except ImportError:
@@ -74,6 +73,24 @@ def sort_xml(node, key=None):
node[:] = sorted_children
+def close_db_connection(func):
+ """ Decorator that closes the Django database connection at the end of
+ the function. This should decorate any exposed function that
+ might open a database connection. """
+ @wraps(func)
+ def inner(self, *args, **kwargs):
+ """ The decorated function """
+ rv = func(self, *args, **kwargs)
+ if self._database_available: # pylint: disable=W0212
+ from django import db
+ self.logger.debug("%s: Closing database connection" %
+ threading.current_thread().name)
+ db.close_connection()
+ return rv
+
+ return inner
+
+
class CoreInitError(Exception):
""" Raised when the server core cannot be initialized. """
pass
@@ -114,7 +131,8 @@ class Core(object):
Bcfg2.Options.Common.repository,
Bcfg2.Options.Common.filemonitor,
Bcfg2.Options.BooleanOption(
- cf=('server', 'fam_blocking'), default=False,
+ "--no-fam-blocking", cf=('server', 'fam_blocking'),
+ dest="fam_blocking", default=True,
help='FAM blocks on startup until all events are processed'),
Bcfg2.Options.BooleanOption(
cf=('logging', 'performance'), dest="perflog",
@@ -128,6 +146,10 @@ class Core(object):
default='off',
choices=['off', 'on', 'initial', 'cautious', 'aggressive'])]
+ #: The name of this server core. This can be overridden by core
+ #: implementations to provide a more specific name.
+ name = "Core"
+
def __init__(self): # pylint: disable=R0912,R0915
"""
.. automethod:: _run
@@ -196,6 +218,12 @@ class Core(object):
self.revision = '-1'
atexit.register(self.shutdown)
+ #: if :func:`Bcfg2.Server.Core.shutdown` is called explicitly,
+ #: then :mod:`atexit` calls it *again*, so it gets called
+ #: twice. This is potentially bad, so we use
+ #: :attr:`Bcfg2.Server.Core._running` as a flag to determine
+ #: if the core needs to be shutdown, and only do it once.
+ self._running = True
#: Threading event to signal worker threads (e.g.,
#: :attr:`fam_thread`) to shutdown
@@ -236,16 +264,16 @@ class Core(object):
self._database_available = False
if HAS_DJANGO:
try:
- management.call_command("syncdb", interactive=False,
- verbosity=0)
+ Bcfg2.DBSettings.sync_databases(interactive=False,
+ verbosity=0)
self._database_available = True
except ImproperlyConfigured:
- err = sys.exc_info()[1]
- self.logger.error("Django configuration problem: %s" % err)
+ self.logger.error("Django configuration problem: %s" %
+ sys.exc_info()[1])
except:
- err = sys.exc_info()[1]
self.logger.error("Updating database %s failed: %s" %
- (Bcfg2.Options.setup.db_name, err))
+ (Bcfg2.Options.setup.db_name,
+ sys.exc_info()[1]))
def __str__(self):
return self.__class__.__name__
@@ -332,7 +360,7 @@ class Core(object):
This does not start plugin threads; that is done later, in
:func:`Bcfg2.Server.Core.BaseCore.run` """
for plugin in Bcfg2.Options.setup.plugins:
- if not plugin in self.plugins:
+ if plugin not in self.plugins:
self.init_plugin(plugin)
# Remove blacklisted plugins
@@ -403,14 +431,22 @@ class Core(object):
def shutdown(self):
""" Perform plugin and FAM shutdown tasks. """
- self.logger.info("Shutting down core...")
+ if not self._running:
+ self.logger.debug("%s: Core already shut down" % self.name)
+ return
+ self.logger.info("%s: Shutting down core..." % self.name)
if not self.terminate.isSet():
self.terminate.set()
- self.fam.shutdown()
- self.logger.info("FAM shut down")
- for plugin in list(self.plugins.values()):
- plugin.shutdown()
- self.logger.info("All plugins shut down")
+ self._running = False
+ self.fam.shutdown()
+ self.logger.info("%s: FAM shut down" % self.name)
+ for plugin in list(self.plugins.values()):
+ plugin.shutdown()
+ self.logger.info("%s: All plugins shut down" % self.name)
+ if self._database_available:
+ from django import db
+ self.logger.info("%s: Closing database connection" % self.name)
+ db.close_connection()
@property
def metadata_cache_mode(self):
@@ -601,9 +637,10 @@ class Core(object):
del entry.attrib['realname']
return ret
except:
- self.logger.error("Failed binding entry %s:%s with altsrc %s" %
- (entry.tag, entry.get('realname'),
- entry.get('name')))
+ self.logger.error(
+ "Failed binding entry %s:%s with altsrc %s: %s" %
+ (entry.tag, entry.get('realname'), entry.get('name'),
+ sys.exc_info()[1]))
entry.set('name', oldname)
self.logger.error("Falling back to %s:%s" %
(entry.tag, entry.get('name')))
@@ -1052,6 +1089,7 @@ class Core(object):
@exposed
@track_statistics()
+ @close_db_connection
def DeclareVersion(self, address, version):
""" Declare the client version.
@@ -1074,6 +1112,7 @@ class Core(object):
return True
@exposed
+ @close_db_connection
def GetProbes(self, address):
""" Fetch probes for the client.
@@ -1099,6 +1138,7 @@ class Core(object):
(client, err))
@exposed
+ @close_db_connection
def RecvProbeData(self, address, probedata):
""" Receive probe data from clients.
@@ -1146,6 +1186,7 @@ class Core(object):
return True
@exposed
+ @close_db_connection
def AssertProfile(self, address, profile):
""" Set profile for a client.
@@ -1165,6 +1206,7 @@ class Core(object):
return True
@exposed
+ @close_db_connection
def GetConfig(self, address):
""" Build config for a client by calling
:func:`BuildConfiguration`.
@@ -1184,6 +1226,7 @@ class Core(object):
self.critical_error("Metadata consistency failure for %s" % client)
@exposed
+ @close_db_connection
def RecvStats(self, address, stats):
""" Act on statistics upload with :func:`process_statistics`.
@@ -1199,6 +1242,7 @@ class Core(object):
return True
@exposed
+ @close_db_connection
def GetDecisionList(self, address, mode):
""" Get the decision list for the client with :func:`GetDecisions`.
@@ -1326,8 +1370,16 @@ class NetworkCore(Core):
daemonized, etc."""
options = Core.options + [
Bcfg2.Options.Common.daemon, Bcfg2.Options.Common.syslog,
- Bcfg2.Options.Common.location, Bcfg2.Options.Common.ssl_key,
- Bcfg2.Options.Common.ssl_cert, Bcfg2.Options.Common.ssl_ca,
+ Bcfg2.Options.Common.location, Bcfg2.Options.Common.ssl_ca,
+ Bcfg2.Options.Common.protocol,
+ Bcfg2.Options.PathOption(
+ '--ssl-key', cf=('communication', 'key'), dest="key",
+ help='Path to SSL key',
+ default="/etc/pki/tls/private/bcfg2.key"),
+ Bcfg2.Options.PathOption(
+ cf=('communication', 'certificate'), dest="cert",
+ help='Path to SSL certificate',
+ default="/etc/pki/tls/certs/bcfg2.crt"),
Bcfg2.Options.BooleanOption(
'--listen-all', cf=('server', 'listen_all'), default=False,
help="Listen on all interfaces"),