summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/Logging.py87
-rw-r--r--src/lib/Server/Component.py88
-rw-r--r--src/lib/Server/Core.py79
-rw-r--r--src/lib/Server/Metadata.py31
-rw-r--r--src/lib/Server/Plugin.py37
-rw-r--r--src/lib/Server/Plugins/Account.py12
-rw-r--r--src/lib/Server/Plugins/Base.py2
-rw-r--r--src/lib/Server/Plugins/Bundler.py10
-rw-r--r--src/lib/Server/Plugins/Cfg.py62
-rw-r--r--src/lib/Server/Plugins/Chiba.py21
-rw-r--r--src/lib/Server/Plugins/Hostbase.py15
-rw-r--r--src/lib/Server/Plugins/Pkgmgr.py8
-rw-r--r--src/lib/Server/Plugins/SSHbase.py35
-rw-r--r--src/lib/Server/Plugins/TCheetah.py10
-rw-r--r--src/lib/Server/Statistics.py9
-rw-r--r--src/lib/__init__.py2
-rwxr-xr-xsrc/sbin/bcfg2-info26
-rwxr-xr-xsrc/sbin/bcfg2-server116
18 files changed, 326 insertions, 324 deletions
diff --git a/src/lib/Logging.py b/src/lib/Logging.py
new file mode 100644
index 000000000..992989ec2
--- /dev/null
+++ b/src/lib/Logging.py
@@ -0,0 +1,87 @@
+'''Bcfg2 logging support'''
+__revision__ = '$Revision: $'
+
+import copy, fcntl, logging, logging.handlers, math, struct, termios, types
+
+class TermiosFormatter(logging.Formatter):
+ '''The termios formatter displays output in a terminal-sensitive fashion'''
+
+ def __init__(self, fmt=None, datefmt=None):
+ logging.Formatter.__init__(self, fmt, datefmt)
+ # now get termios info
+ try:
+ self.height, self.width = struct.unpack('hhhh',
+ fcntl.ioctl(0, termios.TIOCGWINSZ,
+ "\000"*8))[0:2]
+ if self.height == 0 or self.width == 0:
+ self.height, self.width = (25, 80)
+ except:
+ self.height, self.width = (25, 80)
+
+ def format(self, record):
+ '''format a record for display'''
+ returns = []
+ line_len = self.width - len(record.name) - 2
+ if type(record.msg) in types.StringTypes:
+ for line in record.msg.split('\n'):
+ if len(line) <= line_len:
+ returns.append("%s: %s" % (record.name, line))
+ else:
+ inner_lines = int(math.floor(float(len(line)) / line_len))+1
+ for i in xrange(inner_lines):
+ returns.append("%s: %s" % (record.name, line[i*line_len:(i+1)*line_len]))
+ elif type(record.msg) == types.ListType:
+ record.msg.sort()
+ msgwidth = self.width - len(record.name) - 2
+ columnWidth = max([len(item) for item in record.msg])
+ columns = int(math.floor(float(msgwidth) / (columnWidth+2)))
+ lines = int(math.ceil(float(len(record.msg)) / columns))
+ for lineNumber in xrange(lines):
+ indices = [idx for idx in [(colNum * lines) + lineNumber
+ for colNum in range(columns)] if idx < len(record.msg)]
+ format = record.name + ':' + (len(indices) * (" %%-%ds " % columnWidth))
+ returns.append(format % tuple([record.msg[idx] for idx in indices]))
+ else:
+ # got unsupported type
+ returns.append(record.name + ':' + str(record.msg))
+ if record.exc_info:
+ returns.append(self.formatException(record.exc_info))
+ return '\n'.join(returns)
+
+class FragmentingSysLogHandler(logging.handlers.SysLogHandler):
+ '''This handler fragments messages into chunks smaller than 250 characters'''
+
+ def emit(self, record):
+ '''chunk and deliver records'''
+ if str(record.msg) > 250:
+ start = 0
+ msgdata = str(record.msg)
+ while start < len(msgdata):
+ newrec = copy.deepcopy(record)
+ newrec.msg = msgdata[start:start+250]
+ logging.handlers.SysLogHandler.emit(self, newrec)
+ start += 250
+ else:
+ logging.handlers.SysLogHandler.emit(self, newrec)
+
+def setup_logging(to_console=True, to_syslog=True, level=0):
+ '''setup logging for bcfg2 software'''
+ if hasattr(logging, 'enabled'):
+ return
+ console = logging.StreamHandler()
+ console.setLevel(logging.DEBUG)
+ # tell the handler to use this format
+ console.setFormatter(TermiosFormatter())
+ syslog = FragmentingSysLogHandler('/dev/log', 'local0')
+ syslog.setLevel(logging.DEBUG)
+ syslog.setFormatter(logging.Formatter('%(name)s[%(process)d]: %(message)s'))
+ # add the handler to the root logger
+ if to_console:
+ logging.root.addHandler(console)
+ if to_syslog:
+ logging.root.addHandler(syslog)
+ logging.root.level = level
+ logging.enabled = True
+
+
+
diff --git a/src/lib/Server/Component.py b/src/lib/Server/Component.py
index 5c19c3bdd..01b4e1b0a 100644
--- a/src/lib/Server/Component.py
+++ b/src/lib/Server/Component.py
@@ -1,25 +1,17 @@
'''Cobalt component base classes'''
__revision__ = '$Revision$'
-from ConfigParser import ConfigParser, NoOptionError
-from cPickle import loads, dumps
from M2Crypto import SSL
-from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
-from socket import gethostname
-from sys import exc_info
-import sys
-from syslog import openlog, syslog, LOG_INFO, LOG_ERR, LOG_LOCAL0
-from traceback import extract_tb
-from xmlrpclib import dumps, loads, Fault
-from urlparse import urlparse
-try:
- from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
-except ImportError:
- SimpleXMLRPCDispatcher = object
+import cPickle, logging, socket, urlparse, xmlrpclib, ConfigParser, SimpleXMLRPCServer
-class CobaltXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
+class CobaltXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
'''CobaltXMLRPCRequestHandler takes care of ssl xmlrpc requests'''
+ def __init__(self, request, client_address, server):
+ SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.__init__(self,
+ request, client_address, server)
+ self.logger = logging.getLogger('Bcfg2.Server.Handler')
+
def finish(self):
'''Finish HTTPS connections properly'''
self.request.set_shutdown(SSL.SSL_RECEIVED_SHUTDOWN | SSL.SSL_SENT_SHUTDOWN)
@@ -33,12 +25,7 @@ class CobaltXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
response = self.server._cobalt_marshalled_dispatch(data, self.client_address)
except: # This should only happen if the module is buggy
# internal error, report as HTTP server error
- (trace, val, trb) = exc_info()
- syslog(LOG_ERR, "Unexpected failure in handler")
- for line in extract_tb(trb):
- syslog(LOG_ERR, ' File "%s", line %i, in %s\n %s\n' % line)
- syslog(LOG_ERR, "%s: %s\n"%(trace, val))
- del trace, val, trb
+ self.logger.error("Unexpected failure in handler", exc_info=1)
self.send_response(500)
self.end_headers()
else:
@@ -54,7 +41,7 @@ class CobaltXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
self.connection.shutdown(1)
class Component(SSL.SSLServer,
- SimpleXMLRPCDispatcher):
+ SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
"""Cobalt component providing XML-RPC access"""
__name__ = 'Component'
__implementation__ = 'Generic'
@@ -63,8 +50,8 @@ class Component(SSL.SSLServer,
def __init__(self, setup):
# need to get addr
self.setup = setup
- self.cfile = ConfigParser()
- openlog(self.__implementation__, 0, LOG_LOCAL0)
+ self.cfile = ConfigParser.ConfigParser()
+ self.logger = logging.getLogger('Bcfg2.Server')
if setup['configfile']:
cfilename = setup['configfile']
else:
@@ -80,16 +67,16 @@ class Component(SSL.SSLServer,
if self.cfile._sections['components'].has_key(self.__name__):
self.static = True
- location = urlparse(self.cfile.get('components', self.__name__))[1].split(':')
+ location = urlparse.urlparse(self.cfile.get('components', self.__name__))[1].split(':')
location = (location[0], int(location[1]))
else:
- location = (gethostname(), 0)
+ location = (socket.gethostname(), 0)
self.password = self.cfile.get('communication', 'password')
sslctx = SSL.Context('sslv23')
try:
keyfile = self.cfile.get('communication', 'key')
- except NoOptionError:
+ except ConfigParser.NoOptionError:
print "No key specified in cobalt.conf"
raise SystemExit, 1
sslctx.load_cert_chain(keyfile)
@@ -102,10 +89,10 @@ class Component(SSL.SSLServer,
#sslctx.set_tmp_dh('dh1024.pem')
self.logRequests = 0
# setup unhandled request syslog handling
- SimpleXMLRPCDispatcher.__init__(self)
+ SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self)
SSL.SSLServer.__init__(self, location, CobaltXMLRPCRequestHandler, sslctx)
self.port = self.socket.socket.getsockname()[1]
- syslog(LOG_INFO, "Bound to port %s" % self.port)
+ self.logger.info("Bound to port %s" % self.port)
self.funcs.update({'HandleEvents':self.HandleEvents,
'system.listMethods':self.system_listMethods})
@@ -121,41 +108,34 @@ class Component(SSL.SSLServer,
"""Decode and dispatch XMLRPC requests. Overloaded to pass through
client address information
"""
- rawparams, method = loads(data)
+ rawparams, method = xmlrpclib.loads(data)
if len(rawparams) < 2:
- syslog(LOG_ERR, "No authentication included with request from %s" % address[0])
- return dumps(Fault(2, "No Authentication Info"))
+ self.logger.error("No authentication included with request from %s" % address[0])
+ return xmlrpclib.dumps(xmlrpclib.Fault(2, "No Authentication Info"))
user = rawparams[0]
password = rawparams[1]
params = rawparams[2:]
# check authentication
if not self._authenticate_connection(method, user, password, address):
- syslog(LOG_ERR, "Authentication failure from %s" % address[0])
- return dumps(Fault(3, "Authentication Failure"))
+ self.logger.error("Authentication failure from %s" % address[0])
+ return xmlrpclib.dumps(xmlrpclib.Fault(3, "Authentication Failure"))
# generate response
try:
# all handlers must take address as the first argument
response = self._dispatch(method, (address, ) + params)
# wrap response in a singleton tuple
response = (response,)
- response = dumps(response, methodresponse=1)
- except Fault, fault:
- response = dumps(fault)
- except TypeError, t:
- syslog(LOG_ERR, "Client %s called function %s with wrong argument count" %
+ response = xmlrpclib.dumps(response, methodresponse=1)
+ except xmlrpclib.Fault, fault:
+ response = xmlrpclib.dumps(fault)
+ except TypeError, terror:
+ self.logger.error("Client %s called function %s with wrong argument count" %
(address[0], method))
- response = dumps(Fault(4, t.args[0]))
+ response = xmlrpclib.dumps(xmlrpclib.Fault(4, terror.args[0]))
except:
- (trace, val, trb) = exc_info()
- syslog(LOG_ERR, "Unexpected failure in handler")
- for line in extract_tb(trb):
- syslog(LOG_ERR, ' File "%s", line %i, in %s\n %s\n' % line)
- syslog(LOG_ERR, "%s: %s\n"%(trace, val))
- del trace, val, trb
+ self.logger.error("Unexpected failure in handler", exc_info=1)
# report exception back to server
- response = dumps(Fault(1,
- "%s:%s" % (sys.exc_type, sys.exc_value)))
-
+ response = xmlrpclib.dumps(xmlrpclib.Fault(1, "handler failure"))
return response
def _authenticate_connection(self, method, user, password, address):
@@ -170,22 +150,22 @@ class Component(SSL.SSLServer,
try:
statefile = open("/var/spool/cobalt/%s" % self.__implementation__, 'w')
# need to flock here
- statefile.write(dumps(savedata))
+ statefile.write(cPickle.dumps(savedata))
except:
- syslog(LOG_INFO, "Statefile save failed; data persistence disabled")
+ self.logger.info("Statefile save failed; data persistence disabled")
self.__statefields__ = []
def load_state(self):
'''Load fields defined in __statefields__ from /var/spool/cobalt/__implementation__'''
if self.__statefields__:
try:
- loaddata = loads(open("/var/spool/cobalt/%s" % self.__implementation__).read())
+ loaddata = cPickle.loads(open("/var/spool/cobalt/%s" % self.__implementation__).read())
except:
- syslog(LOG_INFO, "Statefile load failed")
+ self.logger.info("Statefile load failed")
return
for field in self.__statefields__:
setattr(self, field, loaddata[self.__statefields__.index(field)])
def system_listMethods(self, address):
"""get rid of the address argument and call the underlying dispatcher method"""
- return SimpleXMLRPCDispatcher.system_listMethods(self)
+ return SimpleXMLRPCServer.SimpleXMLRPCDispatcher.system_listMethods(self)
diff --git a/src/lib/Server/Core.py b/src/lib/Server/Core.py
index 91da366b8..81e4188ed 100644
--- a/src/lib/Server/Core.py
+++ b/src/lib/Server/Core.py
@@ -1,27 +1,13 @@
'''Bcfg2.Server.Core provides the runtime support for bcfg2 modules'''
__revision__ = '$Revision$'
-from os import stat
-from stat import ST_MODE, S_ISDIR
-from sys import exc_info
-from syslog import syslog, LOG_ERR, LOG_INFO
-from traceback import extract_tb
from time import time
-from ConfigParser import ConfigParser
-from lxml.etree import Element
-
from Bcfg2.Server.Plugin import PluginInitError, PluginExecutionError
-
from Bcfg2.Server.Statistics import Statistics
-import Bcfg2.Server.Metadata
+import logging, lxml.etree, os, stat, Bcfg2.Server.Metadata, ConfigParser
-def log_failure(msg):
- syslog(LOG_ERR, "Unexpected failure in %s" % (msg))
- (trace, val, trb) = exc_info()
- for line in extract_tb(trb):
- syslog(LOG_ERR, ' File "%s", line %i, in %s\n %s\n'%line)
- syslog(LOG_ERR, "%s: %s\n"%(trace, val))
+logger = logging.getLogger('Bcfg2.Core')
class CoreInitError(Exception):
'''This error is raised when the core cannot be initialized'''
@@ -42,8 +28,8 @@ class FamFam(object):
def AddMonitor(self, path, obj):
'''add a monitor to path, installing a callback to obj.HandleEvent'''
- mode = stat(path)[ST_MODE]
- if S_ISDIR(mode):
+ mode = os.stat(path)[stat.ST_MODE]
+ if stat.S_ISDIR(mode):
handle = self.fm.monitorDirectory(path, None)
#print "adding callback for directory %s to %s, handle :%s:" % ( path, obj, handle.requestID())
else:
@@ -62,7 +48,7 @@ class FamFam(object):
try:
self.users[reqid].HandleEvent(event)
except:
- log_failure("handling event for file %s" % (event.filename))
+ logger.error("handling event for file %s" % (event.filename), exc_info=1)
def Service(self):
'''Handle all fam work'''
@@ -94,10 +80,10 @@ class FamFam(object):
try:
self.users[event.requestID].HandleEvent(event)
except:
- log_failure("handling event for file %s" % (event.filename))
+ logger.error("handling event for file %s" % (event.filename), exc_info=1)
end = time()
- syslog(LOG_INFO, "Processed %s fam events in %03.03f seconds. %s coalesced" %
- (count, (end - start), collapsed))
+ logger.info("Processed %s fam events in %03.03f seconds. %s coalesced" %
+ (count, (end - start), collapsed))
class GaminEvent(object):
'''This class provides an event analogous to python-fam events based on gamin sources'''
@@ -134,8 +120,8 @@ class GaminFam(object):
'''add a monitor to path, installing a callback to obj.HandleEvent'''
handle = self.counter
self.counter += 1
- mode = stat(path)[ST_MODE]
- if S_ISDIR(mode):
+ mode = os.stat(path)[stat.ST_MODE]
+ if stat.S_ISDIR(mode):
self.mon.watch_directory(path, self.queue, handle)
#print "adding callback for directory %s to %s, handle :%s:" % ( path, obj, handle.requestID())
else:
@@ -173,12 +159,13 @@ class GaminFam(object):
try:
self.handles[event.requestID].HandleEvent(event)
except:
- log_failure("handling of gamin event for %s" % (event.filename))
+ logger.error("error in handling of gamin event for %s" % (event.filename), exc_info=1)
else:
- syslog(LOG_INFO, "Got event for unexpected id %s, file %s" % (event.requestID, event.filename))
+ logger.info("Got event for unexpected id %s, file %s" %
+ (event.requestID, event.filename))
end = time()
- syslog(LOG_INFO, "Processed %s gamin events in %03.03f seconds. %s collapsed" %
- (count, (end - start), collapsed))
+ logger.info("Processed %s gamin events in %03.03f seconds. %s collapsed" %
+ (count, (end - start), collapsed))
try:
from gamin import WatchMonitor, GAMCreated, GAMExists, GAMEndExist, GAMChanged, GAMDeleted
@@ -196,7 +183,7 @@ class Core(object):
'''The Core object is the container for all Bcfg2 Server logic, and modules'''
def __init__(self, setup, configfile):
object.__init__(self)
- cfile = ConfigParser()
+ cfile = ConfigParser.ConfigParser()
cfile.read([configfile])
self.datastore = cfile.get('server','repository')
try:
@@ -227,26 +214,26 @@ class Core(object):
mod = getattr(__import__("Bcfg2.Server.Plugins.%s" %
(plugin)).Server.Plugins, plugin)
except ImportError:
- syslog(LOG_ERR, "Failed to load plugin %s" % (plugin))
+ logger.error("Failed to load plugin %s" % (plugin))
continue
struct = getattr(mod, plugin)
try:
self.plugins[plugin] = struct(self, self.datastore)
except PluginInitError:
- syslog(LOG_ERR, "Failed to instantiate plugin %s" % (plugin))
+ logger.error("Failed to instantiate plugin %s" % (plugin))
except:
- log_failure("Unexpected initiantiation failure for plugin %s" % (plugin))
+ logger.error("Unexpected initiantiation failure for plugin %s" % (plugin), exc_info=1)
for plugin in structures:
if self.plugins.has_key(plugin):
self.structures.append(self.plugins[plugin])
else:
- syslog(LOG_ERR, "Plugin %s not loaded. Not enabled as a Structure" % (plugin))
+ logger.error("Plugin %s not loaded. Not enabled as a Structure" % (plugin))
for plugin in generators:
if self.plugins.has_key(plugin):
self.generators.append(self.plugins[plugin])
else:
- syslog(LOG_ERR, "Plugin %s not loaded. Not enabled as a Generator" % (plugin))
+ logger.error("Plugin %s not loaded. Not enabled as a Generator" % (plugin))
def GetStructures(self, metadata):
'''Get all structures for client specified by metadata'''
@@ -259,7 +246,7 @@ class Core(object):
try:
self.Bind(entry, metadata)
except PluginExecutionError:
- syslog(LOG_ERR, "Failed to bind entry: %s %s" % (entry.tag, entry.get('name')))
+ logger.error("Failed to bind entry: %s %s" % (entry.tag, entry.get('name')))
def Bind(self, entry, metadata):
'''Bind an entry using the appropriate generator'''
@@ -269,34 +256,34 @@ class Core(object):
return glist[0].Entries[entry.tag][entry.get('name')](entry, metadata)
elif len(glist) > 1:
generators = ", ".join([gen.__name__ for gen in glist])
- syslog(LOG_ERR, "%s %s served by multiple generators: %s" % (entry.tag,
- entry.get('name'), generators))
+ logger.error("%s %s served by multiple generators: %s" % (entry.tag,
+ entry.get('name'), generators))
raise PluginExecutionError, (entry.tag, entry.get('name'))
def BuildConfiguration(self, client):
'''Build Configuration for client'''
start = time()
- config = Element("Configuration", version='2.0')
+ config = lxml.etree.Element("Configuration", version='2.0')
try:
meta = self.metadata.get_metadata(client)
except Bcfg2.Server.Metadata.MetadataConsistencyError:
- syslog(LOG_ERR, "Metadata consistency error for client %s" % client)
- return Element("error", type='metadata error')
+ logger.error("Metadata consistency error for client %s" % client)
+ return lxml.etree.Element("error", type='metadata error')
config.set('toolset', meta.toolset)
try:
structures = self.GetStructures(meta)
except:
- log_failure("GetStructures")
- return Element("error", type='structure error')
+ logger.error("error in GetStructures", exc_info=1)
+ return lxml.etree.Element("error", type='structure error')
for astruct in structures:
try:
self.BindStructure(astruct, meta)
config.append(astruct)
except:
- log_failure("BindStructure")
- syslog(LOG_INFO, "Generated config for %s in %s seconds"%(client, time() - start))
+ logger.error("error in BindStructure", exc_info=1)
+ logger.info("Generated config for %s in %s seconds"%(client, time() - start))
return config
def Service(self):
@@ -305,9 +292,9 @@ class Core(object):
try:
self.fam.HandleEvent()
except:
- log_failure("FamEvent")
+ logger.error("error in FamEvent", exc_info=1)
try:
self.stats.WriteBack()
except:
- log_failure("Statistics")
+ logger.error("error in Statistics", exc_info=1)
diff --git a/src/lib/Server/Metadata.py b/src/lib/Server/Metadata.py
index ecf636476..f62cef65a 100644
--- a/src/lib/Server/Metadata.py
+++ b/src/lib/Server/Metadata.py
@@ -1,9 +1,7 @@
'''This file stores persistent metadata for the BCFG Configuration Repository'''
__revision__ = '$Revision$'
-from syslog import syslog, LOG_ERR, LOG_INFO
-
-import lxml.etree, os, time, threading
+import logging, lxml.etree, os, time
class MetadataConsistencyError(Exception):
'''This error gets raised when metadata is internally inconsistent'''
@@ -41,6 +39,7 @@ class Metadata:
self.categories = {}
self.clientdata = None
self.default = None
+ self.logger = logging.getLogger('Bcfg2.Server.Metadata')
def HandleEvent(self, event):
'''Handle update events for data files'''
@@ -52,7 +51,7 @@ class Metadata:
try:
xdata = lxml.etree.parse("%s/%s" % (self.data, filename))
except lxml.etree.XMLSyntaxError:
- syslog(LOG_ERR, 'Metadata: Failed to parse %s' % (filename))
+ self.logger.error('Failed to parse %s' % (filename))
return
if filename == 'clients.xml':
self.clients = {}
@@ -106,9 +105,9 @@ class Metadata:
real = self.groups.keys()
for client in self.clients.keys():
if self.clients[client] not in real or self.clients[client] not in self.profiles:
- syslog(LOG_ERR, "Metadata: Client %s set as nonexistant or incomplete group %s" \
- % (client, self.clients[client]))
- syslog(LOG_ERR, "Metadata: Removing client mapping for %s" % (client))
+ self.logger.error("Client %s set as nonexistant or incomplete group %s" \
+ % (client, self.clients[client]))
+ self.logger.error("Removing client mapping for %s" % (client))
del self.clients[client]
def set_group(self, client, group):
@@ -116,12 +115,10 @@ class Metadata:
if False in self.states.values():
raise MetadataRuntimeError
if group not in self.public:
- syslog(LOG_ERR, "Metadata: Failed to set client %s to private group %s" % (client,
- group))
+ self.logger.error("Failed to set client %s to private group %s" % (client, group))
raise MetadataConsistencyError
if self.clients.has_key(client):
- syslog(LOG_INFO, "Metadata: Changing %s group from %s to %s" % (client,
- self.clients[client], group))
+ self.logger.info("Changing %s group from %s to %s" % (client, self.clients[client], group))
cli = self.clientdata.xpath('/Clients/Client[@name="%s"]' % (client))
cli[0].set('group', group)
else:
@@ -134,7 +131,7 @@ class Metadata:
try:
datafile = open("%s/%s" % (self.data, 'clients.xml'), 'w')
except IOError:
- syslog(LOG_ERR, "Metadata: Failed to write clients.xml")
+ self.logger.error("Failed to write clients.xml")
raise MetadataRuntimeError
datafile.write(lxml.etree.tostring(self.clientdata))
datafile.close()
@@ -145,10 +142,10 @@ class Metadata:
if len(tgroups) == 1:
return tgroups[0]
elif len(tgroups) == 0:
- syslog(LOG_ERR, "Metadata: Couldn't find toolset for client %s" % (client))
+ self.logger.error("Couldn't find toolset for client %s" % (client))
raise MetadataConsistencyError
else:
- syslog(LOG_ERR, "Metadata: Got goofy toolset result for client %s" % (client))
+ self.logger.error("Got goofy toolset result for client %s" % (client))
raise MetadataConsistencyError
def get_config_template(self, client):
@@ -163,14 +160,14 @@ class Metadata:
[bundles, groups] = self.groups[self.clients[client]]
else:
if self.default == None:
- syslog(LOG_ERR, "Cannot set group for client %s; no default group set" % (client))
+ self.logger.error("Cannot set group for client %s; no default group set" % (client))
raise MetadataConsistencyError
[bundles, groups] = self.groups[self.default]
toolinfo = [self.toolsets[group] for group in groups if self.toolsets.has_key(group)]
if len(toolinfo) > 1:
- syslog(LOG_ERR, "Metadata: Found multiple toolsets for client %s; choosing one" % (client))
+ self.logger.error("Found multiple toolsets for client %s; choosing one" % (client))
elif len(toolinfo) == 0:
- syslog(LOG_ERR, "Metadata: Cannot determine toolset for client %s" % (client))
+ self.logger.error("Cannot determine toolset for client %s" % (client))
raise MetadataConsistencyError
toolset = toolinfo[0]
return ClientMetadata(client, groups, bundles, toolset)
diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py
index 890084c98..be4d7ba23 100644
--- a/src/lib/Server/Plugin.py
+++ b/src/lib/Server/Plugin.py
@@ -1,13 +1,12 @@
'''This module provides the baseclass for Bcfg2 Server Plugins'''
__revision__ = '$Revision$'
-import lxml.etree
-import os
-import stat
-import syslog
+import logging, lxml.etree, os, stat
from lxml.etree import XML, XMLSyntaxError
+logger = logging.getLogger('Bcfg2.Plugin')
+
class PluginInitError(Exception):
'''Error raised in cases of Plugin initialization errors'''
pass
@@ -38,10 +37,7 @@ class Plugin(object):
self.Entries = {}
self.core = core
self.data = "%s/%s" % (datastore, self.__name__)
-
- def LogError(self, msg):
- '''Log error message tagged with Plugin name'''
- syslog.syslog(syslog.LOG_ERR, "%s: %s" % (self.__name__, msg))
+ self.logger = logging.getLogger('Bcfg2.Plugins.%s' % (self.__name__))
def BuildStructures(self, metadata):
'''Build a set of structures tailored to the client metadata'''
@@ -77,13 +73,13 @@ class FileBacked(object):
try:
self.mtime = os.stat(self.name)[stat.ST_MTIME]
except OSError:
- syslog.syslog(syslog.LOG_ERR, "Failed to stat file %s" % (self.name))
+ logger.error("Failed to stat file %s" % (self.name))
try:
self.data = file(self.name).read()
self.Index()
except IOError:
- syslog.syslog(syslog.LOG_ERR, "Failed to read file %s" % (self.name))
+ logger.error("Failed to read file %s" % (self.name))
def Index(self):
'''Update local data structures based on current file state'''
@@ -110,9 +106,9 @@ class DirectoryBacked(object):
def AddEntry(self, name):
'''Add new entry to data structures upon file creation'''
if name == '':
- syslog.syslog(syslog.LOG_INFO, "got add for empty name")
+ logger.info("got add for empty name")
elif self.entries.has_key(name):
- syslog.syslog(syslog.LOG_INFO, "got multiple adds for %s" % name)
+ logger.info("got multiple adds for %s" % name)
else:
if ((name[-1] == '~') or (name[:2] == '.#') or (name[-4:] == '.swp') or (name in ['SCCS', '.svn'])):
return
@@ -123,7 +119,7 @@ class DirectoryBacked(object):
'''Propagate fam events to underlying objects'''
action = event.code2str()
if event.filename == '':
- syslog.syslog(syslog.LOG_INFO, "Got event for blank filename")
+ logger.info("Got event for blank filename")
return
if action == 'exists':
if event.filename != self.name:
@@ -155,7 +151,7 @@ class XMLFileBacked(FileBacked):
try:
xdata = XML(self.data)
except XMLSyntaxError:
- syslog.syslog(syslog.LOG_ERR, "Failed to parse %s"%(self.name))
+ logger.error("Failed to parse %s"%(self.name))
return
self.label = xdata.attrib[self.__identifier__]
self.entries = xdata.getchildren()
@@ -180,7 +176,7 @@ class StructFile(XMLFileBacked):
try:
xdata = lxml.etree.XML(self.data)
except lxml.etree.XMLSyntaxError:
- syslog.syslog(syslog.LOG_ERR, "Failed to parse file %s" % self.name)
+ logger.error("Failed to parse file %s" % self.name)
return
self.fragments = {}
work = {lambda x:True: xdata.getchildren()}
@@ -255,8 +251,7 @@ class XMLSrc(XMLFileBacked):
if self.cache == None or self.cache[0] != metadata:
cache = (metadata, {})
if self.pnode == None:
- syslog.syslog(syslog.LOG_ERR,
- "Cache method called early for %s; forcing data load" % (self.name))
+ logger.error("Cache method called early for %s; forcing data load" % (self.name))
self.HandleEvent()
return
self.pnode.Match(metadata, cache[1])
@@ -274,7 +269,7 @@ class XMLPrioDir(Plugin, DirectoryBacked):
try:
DirectoryBacked.__init__(self, self.data, self.core.fam)
except OSError:
- self.LogError("Failed to load %s indices" % (self.__element__.lower()))
+ self.logger.error("Failed to load %s indices" % (self.__element__.lower()))
raise PluginInitError
def HandleEvent(self, event):
@@ -289,7 +284,7 @@ class XMLPrioDir(Plugin, DirectoryBacked):
[src.Cache(metadata) for src in self.entries.values()]
name = entry.get('name')
if not src.cache:
- self.LogError("Called before data loaded")
+ self.logger.error("Called before data loaded")
raise PluginExecutionError
matching = [src for src in self.entries.values()
if src.cache[1].has_key(name)]
@@ -300,8 +295,8 @@ class XMLPrioDir(Plugin, DirectoryBacked):
else:
prio = [int(src.priority) for src in matching]
if prio.count(max(prio)) > 1:
- self.LogError("Found multiple %s sources with same priority for %s, pkg %s" %
- (self.__element__.lower(), metadata.hostname, entry.get('name')))
+ self.logger.error("Found multiple %s sources with same priority for %s, pkg %s" %
+ (self.__element__.lower(), metadata.hostname, entry.get('name')))
raise PluginExecutionError
index = prio.index(max(prio))
diff --git a/src/lib/Server/Plugins/Account.py b/src/lib/Server/Plugins/Account.py
index 05174486d..076afa032 100644
--- a/src/lib/Server/Plugins/Account.py
+++ b/src/lib/Server/Plugins/Account.py
@@ -1,9 +1,9 @@
'''This handles authentication setup'''
__revision__ = '$Revision$'
-from Bcfg2.Server.Plugin import Plugin, PluginInitError, DirectoryBacked
+import Bcfg2.Server.Plugin
-class Account(Plugin):
+class Account(Bcfg2.Server.Plugin.Plugin):
'''This module generates account config files,
based on an internal data repo:
static.(passwd|group|limits.conf) -> static entries
@@ -17,16 +17,16 @@ class Account(Plugin):
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
- Plugin.__init__(self, core, datastore)
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
self.Entries = {'ConfigFile':{'/etc/passwd':self.from_yp_cb,
'/etc/group':self.from_yp_cb,
'/etc/security/limits.conf':self.gen_limits_cb,
'/root/.ssh/authorized_keys':self.gen_root_keys_cb}}
try:
- self.repository = DirectoryBacked(self.data, self.core.fam)
+ self.repository = Bcfg2.Server.Plugin.DirectoryBacked(self.data, self.core.fam)
except:
- self.LogError("Failed to load repos: %s, %s" % (self.data, "%s/ssh" % (self.data)))
- raise PluginInitError
+ self.logger.error("Failed to load repos: %s, %s" % (self.data, "%s/ssh" % (self.data)))
+ raise Bcfg2.Server.Plugin.PluginInitError
def from_yp_cb(self, entry, metadata):
'''Build password file from cached yp data'''
diff --git a/src/lib/Server/Plugins/Base.py b/src/lib/Server/Plugins/Base.py
index 3be30bc6a..5c32eb15a 100644
--- a/src/lib/Server/Plugins/Base.py
+++ b/src/lib/Server/Plugins/Base.py
@@ -19,7 +19,7 @@ class Base(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.DirectoryBacked):
try:
Bcfg2.Server.Plugin.DirectoryBacked.__init__(self, self.data, self.core.fam)
except OSError:
- self.LogError("Failed to load Base repository")
+ self.logger.error("Failed to load Base repository")
raise Bcfg2.Server.Plugin.PluginInitError
def BuildStructures(self, metadata):
diff --git a/src/lib/Server/Plugins/Bundler.py b/src/lib/Server/Plugins/Bundler.py
index cbbb6c671..59b0dead4 100644
--- a/src/lib/Server/Plugins/Bundler.py
+++ b/src/lib/Server/Plugins/Bundler.py
@@ -1,9 +1,7 @@
'''This provides bundle clauses with translation functionality'''
__revision__ = '$Revision$'
-import Bcfg2.Server.Plugin
-import copy
-import lxml.etree
+import copy, lxml.etree, Bcfg2.Server.Plugin
class Bundler(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.DirectoryBacked):
'''The bundler creates dependent clauses based on the bundle/translation scheme from bcfg1'''
@@ -17,7 +15,7 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.DirectoryBacked):
try:
Bcfg2.Server.Plugin.DirectoryBacked.__init__(self, self.data, self.core.fam)
except OSError:
- self.LogError("Failed to load Bundle repository")
+ self.logger.error("Failed to load Bundle repository")
raise Bcfg2.Server.Plugin.PluginInitError
def BuildStructures(self, metadata):
@@ -25,8 +23,8 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.DirectoryBacked):
bundleset = []
for bundlename in metadata.bundles:
if not self.entries.has_key("%s.xml"%(bundlename)):
- self.LogError("Client %s requested nonexistent bundle %s" % \
- (metadata.hostname, bundlename))
+ self.logger.error("Client %s requested nonexistent bundle %s" % \
+ (metadata.hostname, bundlename))
continue
bundle = lxml.etree.Element('Bundle', name=bundlename)
[bundle.append(copy.deepcopy(item))
diff --git a/src/lib/Server/Plugins/Cfg.py b/src/lib/Server/Plugins/Cfg.py
index b325144e5..157243e50 100644
--- a/src/lib/Server/Plugins/Cfg.py
+++ b/src/lib/Server/Plugins/Cfg.py
@@ -1,29 +1,23 @@
'''This module implements a config file repository'''
__revision__ = '$Revision$'
-from os import stat
-from re import compile as regcompile
-from stat import S_ISDIR, ST_MODE
-from syslog import syslog, LOG_INFO, LOG_ERR
+import binascii, exceptions, logging, os, re, stat, Bcfg2.Server.Plugin
-from Bcfg2.Server.Plugin import Plugin, PluginExecutionError, FileBacked
+logger = logging.getLogger('Bcfg2.Plugins.Cfg')
-import binascii
-import exceptions
-
-specific = regcompile('(.*/)(?P<filename>[\S\-.]+)\.((H_(?P<hostname>\S+))|' +
+specific = re.compile('(.*/)(?P<filename>[\S\-.]+)\.((H_(?P<hostname>\S+))|' +
'(G(?P<prio>\d+)_(?P<group>\S+)))$')
class SpecificityError(Exception):
'''Thrown in case of filename parse failure'''
pass
-class FileEntry(FileBacked):
+class FileEntry(Bcfg2.Server.Plugin.FileBacked):
'''The File Entry class pertains to the config files contained in a particular directory.
This includes :info, all base files and deltas'''
def __init__(self, myid, name):
- FileBacked.__init__(self, name)
+ Bcfg2.Server.Plugin.FileBacked.__init__(self, name)
self.name = name
self.identity = myid
self.all = False
@@ -39,7 +33,7 @@ class FileEntry(FileBacked):
else:
data = specific.match(name)
if not data:
- syslog(LOG_ERR, "Cfg: Failed to match %s" % name)
+ logger.error("Failed to match %s" % name)
raise SpecificityError
if data.group('hostname') != None:
self.hostname = data.group('hostname')
@@ -62,7 +56,7 @@ class FileEntry(FileBacked):
return 0
else:
pass
- syslog(LOG_ERR, "Cfg: Critical: Ran off of the end of the world sorting %s" % (self.name))
+ logger.critical("Ran off of the end of the world sorting %s" % (self.name))
def applies(self, metadata):
'''Predicate if fragment matches client metadata'''
@@ -75,7 +69,7 @@ class FileEntry(FileBacked):
class ConfigFileEntry(object):
'''ConfigFileEntry is a repository entry for a single file, containing
all data for all clients.'''
- info = regcompile('^owner:(\s)*(?P<owner>\w+)|group:(\s)*(?P<group>\w+)|' +
+ info = re.compile('^owner:(\s)*(?P<owner>\w+)|group:(\s)*(?P<group>\w+)|' +
'perms:(\s)*(?P<perms>\w+)|encoding:(\s)*(?P<encoding>\w+)|' +
'(?P<paranoid>paranoid(\s)*)$')
@@ -128,28 +122,28 @@ class ConfigFileEntry(object):
return self.read_info()
if event.filename != self.path.split('/')[-1]:
if not specific.match('/' + event.filename):
- syslog(LOG_INFO, 'Cfg: Suppressing event for bogus file %s' % event.filename)
+ logger.info('Suppressing event for bogus file %s' % event.filename)
return
entries = [entry for entry in self.fragments if
entry.name.split('/')[-1] == event.filename]
if len(entries) == 0:
- syslog(LOG_ERR, "Cfg: Failed to match entry for spec %s" % (event.filename))
+ logger.error("Failed to match entry for spec %s" % (event.filename))
elif len(entries) > 1:
- syslog(LOG_ERR, "Cfg: Matched multiple entries for spec %s" % (event.filename))
+ logger.error("Matched multiple entries for spec %s" % (event.filename))
if action == 'deleted':
- syslog(LOG_INFO, "Cfg: Removing entry %s" % event.filename)
+ logger.info("Removing entry %s" % event.filename)
for entry in entries:
- syslog(LOG_INFO, "Cfg: Removing entry %s" % (entry.name))
+ logger.info("Removing entry %s" % (entry.name))
self.fragments.remove(entry)
self.fragments.sort()
- syslog(LOG_INFO, "Cfg: Entry deletion completed")
+ logger.info("Entry deletion completed")
elif action in ['changed', 'exists', 'created']:
[entry.HandleEvent(event) for entry in entries]
else:
- syslog(LOG_ERR, "Cfg: Unhandled Action %s for file %s" % (action, event.filename))
+ logger.error("Unhandled Action %s for file %s" % (action, event.filename))
def GetConfigFile(self, entry, metadata):
'''Fetch config file from repository'''
@@ -159,8 +153,8 @@ class ConfigFileEntry(object):
try:
basefile = [bfile for bfile in self.fragments if bfile.applies(metadata) and not bfile.op][-1]
except IndexError:
- syslog(LOG_ERR, "Cfg: Failed to locate basefile for %s" % name)
- raise PluginExecutionError, ('basefile', name)
+ logger.error("Failed to locate basefile for %s" % name)
+ raise Bcfg2.Server.Plugin.PluginExecutionError, ('basefile', name)
filedata += basefile.data
for delta in [delta for delta in self.fragments if delta.applies(metadata) and delta.op]:
@@ -186,17 +180,17 @@ class ConfigFileEntry(object):
try:
entry.text = filedata
except exceptions.AttributeError:
- syslog(LOG_ERR, "Failed to marshall file %s. Mark it as base64" % (entry.get('name')))
+ logger.error("Failed to marshall file %s. Mark it as base64" % (entry.get('name')))
-class Cfg(Plugin):
+class Cfg(Bcfg2.Server.Plugin.Plugin):
'''This generator in the configuration file repository for bcfg2'''
__name__ = 'Cfg'
__version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
- tempfile = regcompile("^.*~$|^.*\.swp")
+ tempfile = re.compile("^.*~$|^.*\.swp")
def __init__(self, core, datastore):
- Plugin.__init__(self, core, datastore)
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
self.entries = {}
self.Entries = {'ConfigFile':{}}
self.famID = {}
@@ -209,9 +203,9 @@ class Cfg(Plugin):
'''Add new directory to FAM structures'''
if name not in self.directories:
try:
- stat(name)
+ os.stat(name)
except OSError:
- self.LogError("Failed to open directory %s" % (name))
+ logger.error("Failed to open directory %s" % (name))
return
reqid = self.core.fam.AddMonitor(name, self)
self.famID[reqid] = name
@@ -220,11 +214,11 @@ class Cfg(Plugin):
def AddEntry(self, name, event):
'''Add new entry to FAM structures'''
try:
- sdata = stat(name)[ST_MODE]
+ sdata = os.stat(name)[stat.ST_MODE]
except OSError:
return
- if S_ISDIR(sdata):
+ if stat.S_ISDIR(sdata):
self.AddDirectoryMonitor(name)
else:
# file entries shouldn't contain path-to-repo
@@ -240,7 +234,7 @@ class Cfg(Plugin):
'''Handle FAM updates'''
action = event.code2str()
if self.tempfile.match(event.filename):
- syslog(LOG_INFO, "Cfg: Suppressed event for file %s" % event.filename)
+ logger.info("Suppressed event for file %s" % event.filename)
return
if event.filename[0] != '/':
filename = "%s/%s" % (self.famID[event.requestID], event.filename)
@@ -258,11 +252,11 @@ class Cfg(Plugin):
if filename != self.data:
self.AddEntry(filename, event)
else:
- self.LogError("Ignoring event for %s"%(configfile))
+ logger.error("Ignoring event for %s"%(configfile))
elif action == 'deleted':
if self.entries.has_key(configfile):
self.entries[configfile].HandleEvent(event)
elif action in ['exists', 'endExist']:
pass
else:
- self.LogError("Got unknown event %s %s:%s" % (action, event.requestID, event.filename))
+ logger.error("Got unknown event %s %s:%s" % (action, event.requestID, event.filename))
diff --git a/src/lib/Server/Plugins/Chiba.py b/src/lib/Server/Plugins/Chiba.py
index e74036d1b..92ad05ab5 100644
--- a/src/lib/Server/Plugins/Chiba.py
+++ b/src/lib/Server/Plugins/Chiba.py
@@ -1,14 +1,13 @@
'''This module configures files in a Chiba City specific way'''
-__revision__ = '$Revision:$'
+__revision__ = '$Revision$'
-from socket import gethostbyname, gaierror
-from Bcfg2.Server.Plugin import Plugin, DirectoryBacked, SingleXMLFileBacked, PluginExecutionError
+import socket, Bcfg2.Server.Plugin
-class ChibaConf(SingleXMLFileBacked):
+class ChibaConf(Bcfg2.Server.Plugin.SingleXMLFileBacked):
'''This class encapsulates all information needed for all Chiba config ops'''
pass
-class Chiba(Plugin):
+class Chiba(Bcfg2.Server.Plugin.Plugin):
'''the Chiba generator builds the following files:
-> /etc/fstab
-> /etc/network/interfaces
@@ -20,8 +19,8 @@ class Chiba(Plugin):
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
- Plugin.__init__(self, core, datastore)
- self.repo = DirectoryBacked(self.data, self.core.fam)
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
+ self.repo = Bcfg2.Server.Plugin.DirectoryBacked(self.data, self.core.fam)
self.Entries = {'ConfigFile': {'/etc/network/interfaces':self.build_interfaces}}
def build_interfaces(self, entry, metadata):
@@ -32,9 +31,9 @@ class Chiba(Plugin):
try:
myriname = "%s-myr.%s" % (metadata.hostname.split('.')[0],
".".join(metadata.hostname.split('.')[1:]))
- myriaddr = gethostbyname(myriname)
- except gaierror:
- self.LogError("Failed to resolve %s"% myriname)
- raise PluginExecutionError, (myriname, 'lookup')
+ myriaddr = socket.gethostbyname(myriname)
+ except socket.gaierror:
+ self.logger.error("Failed to resolve %s"% myriname)
+ raise Bcfg2.Server.Plugin.PluginExecutionError, (myriname, 'lookup')
entry.text = self.repo.entries['interfaces-template'].data % myriaddr
diff --git a/src/lib/Server/Plugins/Hostbase.py b/src/lib/Server/Plugins/Hostbase.py
index e729030fb..96f215451 100644
--- a/src/lib/Server/Plugins/Hostbase.py
+++ b/src/lib/Server/Plugins/Hostbase.py
@@ -1,7 +1,6 @@
'''This file provides the Hostbase plugin. It manages dns/dhcp/nis host information'''
__revision__ = '$Revision$'
-from syslog import syslog, LOG_INFO
from lxml.etree import XML, SubElement
from Cheetah.Template import Template
from Bcfg2.Server.Plugin import Plugin, PluginExecutionError, PluginInitError, DirectoryBacked
@@ -9,6 +8,10 @@ from time import strftime
from sets import Set
import re
+import logging
+
+logger = logging.getLogger('Bcfg2.Plugins.Hostbase')
+
class DataNexus(DirectoryBacked):
'''DataNexus is an object that watches multiple files and
handles changes in an intelligent fashion.'''
@@ -23,7 +26,7 @@ class DataNexus(DirectoryBacked):
action = event.code2str()
if action in ['exists', 'created']:
if (event.filename != self.name) and (event.filename not in self.files):
- syslog(LOG_INFO, "%s:Got event for unexpected file %s" % (self.__name__, event.filename))
+ logger.info("%s:Got event for unexpected file %s" % (self.__name__, event.filename))
return
DirectoryBacked.HandleEvent(self, event)
if action != 'endExist' and event.filename != self.name:
@@ -48,7 +51,7 @@ class Hostbase(Plugin, DataNexus):
DataNexus.__init__(self, datastore + '/Hostbase/data',
files, self.core.fam)
except:
- self.LogError("Failed to load data directory")
+ logger.error("Failed to load data directory")
raise PluginInitError
self.xdata = {}
self.filedata = {}
@@ -96,12 +99,12 @@ class Hostbase(Plugin, DataNexus):
todaydate = (strftime('%Y%m%d'))
try:
if todaydate == zone.get('serial')[:8]:
- serial = atoi(zone.get('serial')) + 1
+ serial = int(zone.get('serial')) + 1
else:
- serial = atoi(todaydate) * 100
+ serial = int(todaydate) * 100
return str(serial)
except (KeyError):
- serial = atoi(todaydate) * 100
+ serial = int(todaydate) * 100
return str(serial)
if self.entries.has_key(event.filename) and not self.xdata.has_key(event.filename):
diff --git a/src/lib/Server/Plugins/Pkgmgr.py b/src/lib/Server/Plugins/Pkgmgr.py
index e77dd99e5..2367c7c22 100644
--- a/src/lib/Server/Plugins/Pkgmgr.py
+++ b/src/lib/Server/Plugins/Pkgmgr.py
@@ -1,9 +1,9 @@
'''This module implements a package management scheme for all images'''
__revision__ = '$Revision$'
-import re
-from syslog import syslog, LOG_ERR
-import Bcfg2.Server.Plugin
+import logging, re, Bcfg2.Server.Plugin
+
+logger = logging.getLogger('Bcfg2.Plugins.Pkgmgr')
class PNode(Bcfg2.Server.Plugin.LNode):
'''PNode has a list of packages available at a particular group intersection'''
@@ -29,7 +29,7 @@ class PNode(Bcfg2.Server.Plugin.LNode):
if self.splitters.has_key(pkg.get('type')):
mdata = self.splitters[pkg.get('type')].match(pkg.get('file'))
if not mdata:
- syslog(LOG_ERR, "Pkgmgr: Failed to match pkg %s" % pkg.get('file'))
+ logger.error("Failed to match pkg %s" % pkg.get('file'))
continue
pkgname = mdata.group('name')
self.contents[pkgname] = mdata.groupdict()
diff --git a/src/lib/Server/Plugins/SSHbase.py b/src/lib/Server/Plugins/SSHbase.py
index 6cab373b0..87e9225af 100644
--- a/src/lib/Server/Plugins/SSHbase.py
+++ b/src/lib/Server/Plugins/SSHbase.py
@@ -1,13 +1,9 @@
'''This module manages ssh key files for bcfg2'''
__revision__ = '$Revision$'
-from binascii import b2a_base64
-from os import system, popen
-from socket import gethostbyname, gaierror
+import binascii, os, socket, Bcfg2.Server.Plugin
-from Bcfg2.Server.Plugin import Plugin, DirectoryBacked, PluginExecutionError
-
-class SSHbase(Plugin):
+class SSHbase(Bcfg2.Server.Plugin.Plugin):
'''The sshbase generator manages ssh host keys (both v1 and v2)
for hosts. It also manages the ssh_known_hosts file. It can
integrate host keys from other management domains and similarly
@@ -35,8 +31,8 @@ class SSHbase(Plugin):
"ssh_host_rsa_key.H_%s", "ssh_host_key.H_%s"]
def __init__(self, core, datastore):
- Plugin.__init__(self, core, datastore)
- self.repository = DirectoryBacked(self.data, self.core.fam)
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
+ self.repository = Bcfg2.Server.Plugin.DirectoryBacked(self.data, self.core.fam)
try:
prefix = open("%s/prefix" % (self.data)).read().strip()
except IOError:
@@ -65,20 +61,20 @@ class SSHbase(Plugin):
else:
# need to add entry
try:
- ipaddr = gethostbyname(client)
+ ipaddr = socket.gethostbyname(client)
self.ipcache[client] = (ipaddr, client)
return (ipaddr, client)
- except gaierror:
- (client)
+ except socket.gaierror:
+ pass
try:
- ipaddr = popen("getent hosts %s" % client).read().strip().split()
+ ipaddr = os.popen("getent hosts %s" % client).read().strip().split()
except:
ipaddr = ''
if ipaddr:
self.ipcache[client] = (ipaddr, client)
return (ipaddr, client)
- self.LogError("Failed to find IP address for %s" % client)
- raise gaierror
+ self.logger.error("Failed to find IP address for %s" % client)
+ raise socket.gaierror
def cache_skn(self):
'''build memory cache of the ssh known hosts file'''
@@ -87,7 +83,7 @@ class SSHbase(Plugin):
hostname = pubkey.split('H_')[1]
try:
(ipaddr, fqdn) = self.get_ipcache_entry(hostname)
- except gaierror:
+ except socket.gaierror:
continue
shortname = hostname.split('.')[0]
self.static_skn += "%s,%s,%s %s" % (shortname, fqdn, ipaddr,
@@ -114,8 +110,8 @@ class SSHbase(Plugin):
if hasattr(self, 'static_skn'):
del self.static_skn
if not self.repository.entries.has_key(filename):
- self.LogError("%s still not registered" % filename)
- raise PluginExecutionError
+ self.logger.error("%s still not registered" % filename)
+ raise Bcfg2.Server.Plugin.PluginExecutionError
keydata = self.repository.entries[filename].data
permdata = {'owner':'root', 'group':'root'}
permdata['perms'] = '0600'
@@ -124,7 +120,7 @@ class SSHbase(Plugin):
[entry.attrib.__setitem__(x, permdata[x]) for x in permdata]
if "ssh_host_key.H_" == filename[:15]:
entry.attrib['encoding'] = 'base64'
- entry.text = b2a_base64(keydata)
+ entry.text = binascii.b2a_base64(keydata)
else:
entry.text = keydata
@@ -143,7 +139,8 @@ class SSHbase(Plugin):
fileloc = "%s/%s" % (self.data, hostkey)
publoc = self.data + '/' + ".".join([hostkey.split('.')[0]]+['pub', "H_%s" % client])
temploc = "/tmp/%s" % hostkey
- system('ssh-keygen -q -f %s -N "" -t %s -C root@%s < /dev/null' % (temploc, keytype, client))
+ os.system('ssh-keygen -q -f %s -N "" -t %s -C root@%s < /dev/null' %
+ (temploc, keytype, client))
open(fileloc, 'w').write(open(temploc).read())
open(publoc, 'w').write(open("%s.pub" % temploc).read())
self.repository.AddEntry(hostkey)
diff --git a/src/lib/Server/Plugins/TCheetah.py b/src/lib/Server/Plugins/TCheetah.py
index 1ebdb6c94..beff8e869 100644
--- a/src/lib/Server/Plugins/TCheetah.py
+++ b/src/lib/Server/Plugins/TCheetah.py
@@ -2,11 +2,14 @@
__revision__ = '$Revision$'
from posixpath import isdir
-from syslog import syslog, LOG_ERR
from Bcfg2.Server.Plugin import Plugin, PluginExecutionError, FileBacked, SingleXMLFileBacked
from lxml.etree import XML, XMLSyntaxError
from Cheetah.Template import Template
+import logging
+
+logger = logging.getLogger('Bcfg2.Plugins.TCheetah')
+
class TemplateFile(FileBacked):
'''Template file creates Cheetah template structures for the loaded file'''
def __init__(self, name, properties):
@@ -25,7 +28,7 @@ class TemplateFile(FileBacked):
try:
entry.text = str(self.template)
except:
- syslog(LOG_ERR, "TCheetah: Failed to template %s" % entry.get('name'))
+ logger.error("Failed to template %s" % entry.get('name'))
raise PluginExecutionError
perms = {'owner':'root', 'group':'root', 'perms':'0644'}
[entry.attrib.__setitem__(key, value) for (key, value) in perms.iteritems()]
@@ -38,7 +41,7 @@ class CheetahProperties(SingleXMLFileBacked):
self.properties = XML(self.data)
del self.data
except XMLSyntaxError:
- syslog(LOG_ERR, "TCheetah: Failed to parse properties")
+ logger.error("Failed to parse properties")
class TCheetah(Plugin):
'''The TCheetah generator implements a templating mechanism for configuration files'''
@@ -80,7 +83,6 @@ class TCheetah(Plugin):
self.entries[identifier].HandleEvent(event)
self.Entries['ConfigFile'][identifier] = self.BuildEntry
#except:
- # syslog(LOG_ERR, "TCheetah: bad format for file %s" % identifier)
elif action == 'changed':
if self.entries.has_key(identifier):
self.entries[identifier].HandleEvent(event)
diff --git a/src/lib/Server/Statistics.py b/src/lib/Server/Statistics.py
index 15f1586ef..4a86cdf35 100644
--- a/src/lib/Server/Statistics.py
+++ b/src/lib/Server/Statistics.py
@@ -2,9 +2,10 @@
__revision__ = '$Revision$'
from lxml.etree import XML, SubElement, Element, XMLSyntaxError
-from syslog import syslog, LOG_ERR
from time import asctime, localtime, time
+import logging
+
class Statistics(object):
'''Manages the memory and file copy of statistics collected about client runs'''
__min_write_delay__ = 30
@@ -15,6 +16,7 @@ class Statistics(object):
self.dirty = 0
self.lastwrite = 0
self.ReadFromFile()
+ self.logger = logging.getLogger('Bcfg2.Server.Statistics')
def pretty_print(self, element, level=0):
'''Produce a pretty-printed text representation of element'''
@@ -53,12 +55,11 @@ class Statistics(object):
self.dirty = 0
#syslog(LOG_INFO, "Statistics: Read in statistics.xml")
except (IOError, XMLSyntaxError):
- syslog(LOG_ERR, "Statistics: Failed to parse %s"%(self.filename))
+ self.logger.error("Failed to parse %s"%(self.filename))
self.element = Element('ConfigStatistics')
self.WriteBack()
self.dirty = 0
-
def updateStats(self, xml, client):
'''Updates the statistics of a current node with new data'''
@@ -91,7 +92,7 @@ class Statistics(object):
node.remove(elem)
else:
# Shouldn't be reached
- syslog(LOG_ERR, "Statistics: Duplicate node entry for %s"%(client))
+ self.logger.error("Duplicate node entry for %s"%(client))
# Set current time for stats
newstat.set('time', asctime(localtime()))
diff --git a/src/lib/__init__.py b/src/lib/__init__.py
index 6fe48add9..241355fdd 100644
--- a/src/lib/__init__.py
+++ b/src/lib/__init__.py
@@ -1,4 +1,4 @@
'''base modules definition'''
__revision__ = '$Revision$'
-all = ['Server', 'Client']
+all = ['Server', 'Client', 'Logging']
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index e325b2fd0..7479f85a1 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -2,10 +2,7 @@
'''This tool loads the Bcfg2 core into an interactive debugger'''
__revision__ = '$Revision$'
-from sys import argv
-from time import sleep
-from Bcfg2.Server.Core import Core, CoreInitError
-from lxml.etree import tostring
+import logging, lxml.etree, sys, time, Bcfg2.Logging, Bcfg2.Server.Core
def print_tabular(rows):
'''print data in tabular format'''
@@ -28,7 +25,7 @@ def do_build(cmd, core):
'''build client configuration'''
if len(cmd) == 3:
output = open(cmd[2], 'w')
- output.write(tostring(core.BuildConfiguration(cmd[1])))
+ output.write(lxml.etree.tostring(core.BuildConfiguration(cmd[1])))
output.close()
else:
print 'Usage: build <hostname> <output file>'
@@ -126,29 +123,36 @@ def do_version(cmd, core):
print __revision__
if __name__ == '__main__':
+ Bcfg2.Logging.setup_logging(to_syslog=False)
+ logger = logging.getLogger('bcfg2-info')
dispatch = {'build': do_build, 'bundles': do_bundles, 'clients': do_clients,
'generators': do_generators, 'groups': do_groups,
'help': do_help, 'mappings': do_mappings, 'quit': do_quit,
'update': do_update, 'version': do_version}
- if '-c' in argv:
- cfile = argv[-1]
+ if '-c' in sys.argv:
+ cfile = sys.argv[-1]
else:
cfile = '/etc/bcfg2.conf'
try:
- bcore = Core({}, cfile)
- except CoreInitError, msg:
+ bcore = Bcfg2.Server.Core.Core({}, cfile)
+ except Bcfg2.Server.Core.CoreInitError, msg:
print "Core load failed because %s" % msg
raise SystemExit, 1
for i in range(25):
bcore.fam.Service()
- sleep(0.5)
+ time.sleep(0.5)
ucmd = get_input()
while True:
if ucmd[0] == 'debug':
break
else:
if dispatch.has_key(ucmd[0]):
- dispatch[ucmd[0]](ucmd, bcore)
+ try:
+ dispatch[ucmd[0]](ucmd, bcore)
+ except SystemExit, code:
+ raise SystemExit, code
+ except:
+ logger.error("command failure", exc_info=1)
else:
print "Unknown command %s" % ucmd[0]
ucmd = get_input()
diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server
index 2da6ff325..e3a73ab7b 100755
--- a/src/sbin/bcfg2-server
+++ b/src/sbin/bcfg2-server
@@ -3,22 +3,15 @@
'''The XML-RPC Bcfg2 Server'''
__revision__ = '$Revision$'
-from getopt import getopt, GetoptError
-from sys import argv, exc_info
-from syslog import openlog, LOG_LOCAL0, syslog, LOG_INFO, LOG_ERR
from Bcfg2.Server.Core import Core, CoreInitError
from Bcfg2.Server.Metadata import MetadataConsistencyError
-from Bcfg2.Server.Component import Component
-from threading import Lock
-from select import select, error as selecterror
-from signal import signal, SIGINT, SIGTERM
-from traceback import extract_tb
from xmlrpclib import Fault
-from socket import gethostbyaddr, herror
from lxml.etree import XML, Element, tostring
-from M2Crypto.SSL import SSLError
-import os, sys
+import getopt, logging, os, select, signal, socket, sys
+import Bcfg2.Logging, Bcfg2.Server.Component, M2Crypto.SSL
+
+logger = logging.getLogger('bcfg2-server')
def daemonize(filename):
'''Do the double fork/setsession dance'''
@@ -41,61 +34,33 @@ def daemonize(filename):
os.dup2(null.fileno(), sys.__stdout__.fileno())
os.dup2(null.fileno(), sys.__stderr__.fileno())
-
def critical_error(operation):
- '''Print tracebacks in unexpected cases'''
- syslog(LOG_ERR, "Traceback information (please include in any bug report):")
- (ttype, value, trace) = exc_info()
- for line in extract_tb(trace):
- syslog(LOG_ERR, "File %s, line %i, in %s\n %s" % (line))
- syslog(LOG_ERR, "%s: %s" % (ttype, value))
- warning_error("An unexpected failure occurred in %s" % (operation) )
+ '''Log and err, traceback and return an xmlrpc fault to client'''
+ logger.error(operation, exc_info=1)
raise Fault, (7, "Critical unexpected failure: %s" % (operation))
def fatal_error(message):
'''Signal a fatal error'''
- syslog(LOG_ERR, "Fatal error: %s" % (message))
+ logger.critical("Fatal error: %s" % (message))
raise SystemExit, 1
-def warning_error(message):
- '''Warn about a problem but continue'''
- syslog(LOG_ERR,"Warning: %s\n" % (message))
-
-def usage_error(message, opt, vopt, descs, argDescs):
- '''Die because script was called the wrong way'''
- print "Usage error: %s" % (message)
- print_usage(opt, vopt, descs, argDescs)
+def usage(message, opts, vopts, odescs, vargDescs):
+ logger.critical(message)
+ [logger.critical(" -%s\t\t\t%s" % (arg, odescs[arg])) for arg in opts]
+ [logger.critical(" -%s %s\t%s" % (arg, vargDescs[arg], odescs[arg])) for arg in vopts]
raise SystemExit, 2
-verboseMode = False
-
-def verbose(message):
- '''Conditionally output information in verbose mode'''
- global verboseMode
-
- if(verboseMode == True):
- syslog(LOG_INFO, "%s" % (message))
-
-def print_usage(opt, vopt, descs, argDescs):
- print "bcfg2-server usage:"
- for arg in opt.iteritems():
- print " -%s\t\t\t%s" % (arg[0], descs[arg[0]])
- for arg in vopt.iteritems():
- print " -%s %s\t%s" % (arg[0], argDescs[arg[0]], descs[arg[0]])
-
def dgetopt(arglist, opt, vopt, descs, argDescs):
'''parse options into a dictionary'''
- global verboseMode
-
ret = {}
for optname in opt.values() + vopt.values():
ret[optname] = False
gstr = "".join(opt.keys()) + "".join([optionkey + ':' for optionkey in vopt.keys()])
try:
- ginfo = getopt(arglist, gstr)
- except GetoptError, gerr:
- usage_error(gerr, opt, vopt, descs, argDescs)
+ ginfo = getopt.getopt(arglist, gstr)
+ except getopt.GetoptError, gerr:
+ usage("Usage error: %s" % gett, opt, vopt, descs, argsDescs)
for (gopt, garg) in ginfo[0]:
option = gopt[1:]
@@ -105,15 +70,11 @@ def dgetopt(arglist, opt, vopt, descs, argDescs):
ret[vopt[option]] = garg
if ret["help"] == True:
- print_usage(opt, vopt, descs, argDescs)
- raise SystemExit, 0
+ print_usage("Usage information", opt, vopt, descs, argDescs)
- if ret["verbose"] == True:
- verboseMode = True
-
return ret
-class Bcfg2(Component):
+class Bcfg2(Bcfg2.Server.Component.Component):
"""The Bcfg2 Server component providing XML-RPC access to Bcfg methods"""
__name__ = 'bcfg2'
__implementation__ = 'bcfg2'
@@ -121,14 +82,13 @@ class Bcfg2(Component):
request_queue_size = 15
def __init__(self, setup):
- Component.__init__(self, setup)
+ Bcfg2.Server.Component.Component.__init__(self, setup)
self.shut = False
# set shutdown handlers for sigint and sigterm
- signal(SIGINT, self.start_shutdown)
- signal(SIGTERM, self.start_shutdown)
+ signal.signal(signal.SIGINT, self.start_shutdown)
+ signal.signal(signal.SIGTERM, self.start_shutdown)
try:
self.Core = Core(setup, setup['configfile'])
- self.CoreLock = Lock()
except CoreInitError, msg:
fatal_error(msg)
@@ -149,11 +109,11 @@ class Bcfg2(Component):
famfd = self.Core.fam.fileno()
while self.socket not in rsockinfo:
if self.shut:
- raise SSLError
+ raise M2Crypto.SSL.SSLError
try:
- rsockinfo = select([self.socket, famfd], [], [], 15)[0]
- except selecterror:
- raise SSLError
+ rsockinfo = select.select([self.socket, famfd], [], [], 15)[0]
+ except select.error:
+ raise M2Crypto.SSL.SSLError
if famfd in rsockinfo:
self.Core.fam.Service()
@@ -179,10 +139,10 @@ class Bcfg2(Component):
if self.setup['client']:
return self.setup['client']
try:
- return gethostbyaddr(client)[0]
- except herror:
+ return socket.gethostbyaddr(client)[0]
+ except socket.herror:
warning = "host resolution error for %s" % (client)
- warning_error(warning)
+ self.logger.warning(warning)
raise Fault, (5, warning)
def Bcfg2GetProbes(self, address):
@@ -199,11 +159,10 @@ class Bcfg2(Component):
return tostring(resp)
except MetadataConsistencyError:
warning = 'metadata consistency error'
- warning_error(warning)
+ self.logger.warning(warning)
raise Fault, (6, warning)
except:
- critical_error("determining client probes")
-
+ critical_error("error determining client probes")
def Bcfg2RecvProbeData(self, address, probedata):
'''Receive probe data from clients'''
@@ -214,9 +173,9 @@ class Bcfg2(Component):
[generator] = [gen for gen in self.Core.generators if gen.__name__ == data.get('source')]
generator.ReceiveData(client, data)
except IndexError:
- warning_error("Failed to locate plugin %s" % (data.get('source')))
+ self.logger.warning("Failed to locate plugin %s" % (data.get('source')))
except:
- critical_error("probe data receipt")
+ critical_error('error in probe data receipt')
return True
def Bcfg2GetConfig(self, address, image=False, profile=False):
@@ -228,7 +187,7 @@ class Bcfg2(Component):
self.Core.metadata.set_group(client, profile)
except MetadataConsistencyError:
warning = 'metadata consistency error'
- warning_error(warning)
+ self.logger.warning(warning)
raise Fault, (6, warning)
return tostring(self.Core.BuildConfiguration(client))
@@ -243,12 +202,12 @@ class Bcfg2(Component):
# Update statistics
self.Core.stats.updateStats(sdata, client)
- syslog(LOG_INFO, "Client %s reported state %s" %
- (client, state.attrib['state']))
+ self.logger.info("Client %s reported state %s" %
+ (client, state.attrib['state']))
return "<ok/>"
if __name__ == '__main__':
- openlog("Bcfg2", 0, LOG_LOCAL0)
+ Bcfg2.Logging.setup_logging()
options = {
'v':'verbose',
'd':'debug',
@@ -275,7 +234,7 @@ if __name__ == '__main__':
'C': "<client hostname>"
}
- ssetup = dgetopt(argv[1:], options, doptions,
+ ssetup = dgetopt(sys.argv[1:], options, doptions,
descriptions, argDescriptions)
if ssetup['daemon']:
daemonize(ssetup['daemon'])
@@ -286,6 +245,5 @@ if __name__ == '__main__':
try:
s.serve_forever()
except:
- critical_error("service loop")
-
- syslog(LOG_INFO, "Shutting down")
+ critical_error('error in service loop')
+ logger.info("Shutting down")