summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Bcfg2/SSLServer.py118
-rw-r--r--src/lib/Bcfg2/Server/BuiltinCore.py40
-rw-r--r--src/lib/Bcfg2/Server/CherryPyCore.py41
3 files changed, 109 insertions, 90 deletions
diff --git a/src/lib/Bcfg2/SSLServer.py b/src/lib/Bcfg2/SSLServer.py
index fbcb0e347..5e3c6232a 100644
--- a/src/lib/Bcfg2/SSLServer.py
+++ b/src/lib/Bcfg2/SSLServer.py
@@ -1,8 +1,6 @@
-"""Bcfg2 SSL server."""
-
-__all__ = [
- "SSLServer", "XMLRPCRequestHandler", "XMLRPCServer",
-]
+""" Bcfg2 SSL server used by the builtin server core
+(:mod:`Bcfg2.Server.BuiltinCore`). This needs to be documented
+better. """
import os
import sys
@@ -18,6 +16,8 @@ from Bcfg2.Compat import xmlrpclib, SimpleXMLRPCServer, SocketServer, \
class XMLRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
+ """ An XML-RPC dispatcher. """
+
logger = logging.getLogger("Bcfg2.SSLServer.XMLRPCDispatcher")
def __init__(self, allow_none, encoding):
@@ -33,7 +33,6 @@ class XMLRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
self.encoding = encoding
def _marshaled_dispatch(self, address, data):
- method_func = None
params, method = xmlrpclib.loads(data)
try:
if '.' not in method:
@@ -62,15 +61,7 @@ class XMLRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
class SSLServer(SocketServer.TCPServer, object):
- """TCP server supporting SSL encryption.
-
- Methods:
- handshake -- perform a SSL/TLS handshake
-
- Properties:
- url -- A url pointing to this server.
-
- """
+ """ TCP server supporting SSL encryption. """
allow_reuse_address = True
logger = logging.getLogger("Bcfg2.SSLServer.SSLServer")
@@ -78,19 +69,23 @@ class SSLServer(SocketServer.TCPServer, object):
def __init__(self, listen_all, server_address, RequestHandlerClass,
keyfile=None, certfile=None, reqCert=False, ca=None,
timeout=None, protocol='xmlrpc/ssl'):
-
- """Initialize the SSL-TCP server.
-
- Arguments:
- server_address -- address to bind to the server
- RequestHandlerClass -- class to handle requests
-
- Keyword arguments:
- keyfile -- private encryption key filename (enables ssl encryption)
- certfile -- certificate file (enables ssl encryption)
- reqCert -- client must present certificate
- timeout -- timeout for non-blocking request handling
-
+ """
+ :param listen_all: Listen on all interfaces
+ :type listen_all: bool
+ :param server_address: Address to bind to the server
+ :param RequestHandlerClass: Request handler used by TCP server
+ :param keyfile: Full path to SSL encryption key file
+ :type keyfile: string
+ :param certfile: Full path to SSL certificate file
+ :type certfile: string
+ :param reqCert: Require client to present certificate
+ :type reqCert: bool
+ :param ca: Full path to SSL CA that signed the key and cert
+ :type ca: string
+ :param timeout: Timeout for non-blocking request handling
+ :param protocol: The protocol to serve. Supported values are
+ ``xmlrpc/ssl`` and ``xmlrpc/tlsv1``.
+ :type protocol: string
"""
# check whether or not we should listen on all interfaces
if listen_all:
@@ -183,19 +178,11 @@ class SSLServer(SocketServer.TCPServer, object):
class XMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
- """Component XML-RPC request handler.
+ """ XML-RPC request handler.
Adds support for HTTP authentication.
-
- Exceptions:
-
- CouldNotAuthenticate -- client did not present acceptable
- authentication information
-
- Methods:
- authenticate -- prompt a check of a client's provided username and password
- handle_one_request -- handle a single rpc (optionally authenticating)
"""
+
logger = logging.getLogger("Bcfg2.SSLServer.XMLRPCRequestHandler")
def authenticate(self):
@@ -325,50 +312,37 @@ class XMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
class XMLRPCServer(SocketServer.ThreadingMixIn, SSLServer,
XMLRPCDispatcher, object):
- """Component XMLRPCServer.
-
- Methods:
- serve_daemon -- serve_forever in a daemonized process
- serve_forever -- handle_one_request until not self.serve
- shutdown -- stop serve_forever (by setting self.serve = False)
- ping -- return all arguments received
-
- RPC methods:
- ping
-
- (additional system.* methods are inherited from base dispatcher)
-
- Properties:
- require_auth -- the request handler is requiring authorization
- credentials -- valid credentials being used for authentication
- """
+ """ Component XMLRPCServer. """
def __init__(self, listen_all, server_address, RequestHandlerClass=None,
keyfile=None, certfile=None, ca=None, protocol='xmlrpc/ssl',
- timeout=10,
- logRequests=False,
+ timeout=10, logRequests=False,
register=True, allow_none=True, encoding=None):
- """Initialize the XML-RPC server.
-
- Arguments:
- server_address -- address to bind to the server
- RequestHandlerClass -- request handler used by TCP server (optional)
-
- Keyword arguments:
- keyfile -- private encryption key filename
- certfile -- certificate file
- logRequests -- log all requests (default False)
- register -- presence should be reported to service-location
- (default True)
- allow_none -- allow None values in xml-rpc
- encoding -- encoding to use for xml-rpc (default UTF-8)
+ """
+ :param listen_all: Listen on all interfaces
+ :type listen_all: bool
+ :param server_address: Address to bind to the server
+ :param RequestHandlerClass: request handler used by TCP server
+ :param keyfile: Full path to SSL encryption key file
+ :type keyfile: string
+ :param certfile: Full path to SSL certificate file
+ :type certfile: string
+ :param ca: Full path to SSL CA that signed the key and cert
+ :type ca: string
+ :param logRequests: Log all requests
+ :type logRequests: bool
+ :param register: Presence should be reported to service-location
+ :type register: bool
+ :param allow_none: Allow None values in XML-RPC
+ :type allow_non: bool
+ :param encoding: Encoding to use for XML-RPC
"""
XMLRPCDispatcher.__init__(self, allow_none, encoding)
if not RequestHandlerClass:
# pylint: disable=E0102
- class RequestHandlerClass (XMLRPCRequestHandler):
+ class RequestHandlerClass(XMLRPCRequestHandler):
"""A subclassed request handler to prevent
class-attribute conflicts."""
# pylint: enable=E0102
diff --git a/src/lib/Bcfg2/Server/BuiltinCore.py b/src/lib/Bcfg2/Server/BuiltinCore.py
index 5b9c3302c..889e4d4d6 100644
--- a/src/lib/Bcfg2/Server/BuiltinCore.py
+++ b/src/lib/Bcfg2/Server/BuiltinCore.py
@@ -1,4 +1,4 @@
-""" the core of the builtin bcfg2 server """
+""" The core of the builtin Bcfg2 server. """
import os
import sys
@@ -12,8 +12,15 @@ from Bcfg2.SSLServer import XMLRPCServer
class PidFile(object):
- """ context handler to write the pid file """
+ """ Context handler used by :class:`daemon.DaemonContext` to write
+ the PID file. """
+
def __init__(self, pidfile):
+ """
+ :param pidfile: The full path to the PID file to write on
+ entering the context handler
+ :type pidfile: string
+ """
self.pidfile = pidfile
def __enter__(self):
@@ -29,18 +36,32 @@ class Core(BaseCore):
def __init__(self, setup):
BaseCore.__init__(self, setup)
+
+ #: The :class:`Bcfg2.SSLServer.XMLRPCServer` instance powering
+ #: this server core
self.server = None
+
+ #: The :class:`daemon.DaemonContext` used to drop privileges,
+ #: write the PID file (with :class:`PidFile`), and daemonize
+ #: this core.
self.context = \
daemon.DaemonContext(uid=self.setup['daemon_uid'],
gid=self.setup['daemon_gid'],
pidfile=PidFile(self.setup['daemon']))
+ __init__.__doc__ = BaseCore.__init__.__doc__.split('.. -----')[0]
def _dispatch(self, method, args, dispatch_dict):
- """Custom XML-RPC dispatcher for components.
-
- method -- XML-RPC method name
- args -- tuple of paramaters to method
+ """ Dispatch XML-RPC method calls
+ :param method: XML-RPC method name
+ :type method: string
+ :param args: Paramaters to pass to the method
+ :type args: tuple
+ :param dispatch_dict: A dict of method name -> function that
+ can be used to provide custom mappings
+ :type dispatch_dict: dict
+ :returns: The return value of the method call
+ :raises: :exc:`xmlrpclib.Fault`
"""
if method in dispatch_dict:
method_func = dispatch_dict[method]
@@ -55,7 +76,7 @@ class Core(BaseCore):
try:
method_start = time.time()
try:
- result = method_func(*args)
+ return method_func(*args)
finally:
Bcfg2.Statistics.stats.add_value(method,
time.time() - method_start)
@@ -66,13 +87,15 @@ class Core(BaseCore):
if getattr(err, "log", True):
self.logger.error(err, exc_info=True)
raise xmlrpclib.Fault(getattr(err, "fault_code", 1), str(err))
- return result
def _daemonize(self):
+ """ Open :attr:`context` to drop privileges, write the PID
+ file, and daemonize the server core. """
self.context.open()
self.logger.info("%s daemonized" % self.name)
def _run(self):
+ """ Create :attr:`server` to start the server listening. """
hostname, port = urlparse(self.setup['location'])[1].split(':')
server_address = socket.getaddrinfo(hostname,
port,
@@ -96,6 +119,7 @@ class Core(BaseCore):
return True
def _block(self):
+ """ Enter the blocking infinite loop. """
try:
self.server.serve_forever()
finally:
diff --git a/src/lib/Bcfg2/Server/CherryPyCore.py b/src/lib/Bcfg2/Server/CherryPyCore.py
index 53f3de018..1735fa5c7 100644
--- a/src/lib/Bcfg2/Server/CherryPyCore.py
+++ b/src/lib/Bcfg2/Server/CherryPyCore.py
@@ -1,4 +1,5 @@
-""" the core of the CherryPy-powered server """
+""" The core of the `CherryPy <http://www.cherrypy.org/>`_-powered
+server. """
import sys
import time
@@ -12,10 +13,12 @@ from cherrypy.process.plugins import Daemonizer, DropPrivileges, PIDFile
def on_error(*args, **kwargs): # pylint: disable=W0613
- """ define our own error handler that handles xmlrpclib.Fault
+ """ CherryPy error handler that handles :class:`xmlrpclib.Fault`
objects and so allows for the possibility of returning proper
- error codes. this obviates the need to use the builtin CherryPy
- xmlrpc tool """
+ error codes. This obviates the need to use
+ :func:`cherrypy.lib.xmlrpc.on_error`, the builtin CherryPy xmlrpc
+ tool, which does not handle xmlrpclib.Fault objects and returns
+ the same error code for every error."""
err = sys.exc_info()[1]
if not isinstance(err, xmlrpclib.Fault):
err = xmlrpclib.Fault(xmlrpclib.INTERNAL_ERROR, str(err))
@@ -25,8 +28,11 @@ cherrypy.tools.xmlrpc_error = ErrorTool(on_error)
class Core(BaseCore):
- """ The CherryPy-based server core """
+ """ The CherryPy-based server core. """
+ #: Base CherryPy config for this class. We enable the
+ #: ``xmlrpc_error`` tool created from :func:`on_error` and the
+ #: ``bcfg2_authn`` tool created from :func:`do_authn`.
_cp_config = {'tools.xmlrpc_error.on': True,
'tools.bcfg2_authn.on': True}
@@ -36,11 +42,15 @@ class Core(BaseCore):
cherrypy.tools.bcfg2_authn = cherrypy.Tool('on_start_resource',
self.do_authn)
+ #: List of exposed plugin RMI
self.rmi = self._get_rmi()
cherrypy.engine.subscribe('stop', self.shutdown)
+ __init__.__doc__ = BaseCore.__init__.__doc__.split('.. -----')[0]
def do_authn(self):
- """ perform authentication """
+ """ Perform authentication by calling
+ :func:`Bcfg2.Server.Core.BaseCore.authenticate`. This is
+ implemented as a CherryPy tool."""
try:
header = cherrypy.request.headers['Authorization']
except KeyError:
@@ -60,10 +70,11 @@ class Core(BaseCore):
@cherrypy.expose
def default(self, *args, **params): # pylint: disable=W0613
- """ needed to make enough changes to the stock
- XMLRPCController to support plugin.__rmi__ and prepending
- client address that we just rewrote. it clearly wasn't
- written with inheritance in mind :( """
+ """ Handle all XML-RPC calls. It was necessary to make enough
+ changes to the stock CherryPy
+ :class:`cherrypy._cptools.XMLRPCController` to support plugin
+ RMI and prepending the client address that we just rewrote it.
+ It clearly wasn't written with inheritance in mind."""
rpcparams, rpcmethod = xmlrpcutil.process_body()
if rpcmethod == 'ERRORMETHOD':
raise Exception("Unknown error processing XML-RPC request body")
@@ -92,12 +103,17 @@ class Core(BaseCore):
return cherrypy.serving.response.body
def _daemonize(self):
+ """ Drop privileges with
+ :class:`cherrypy.process.plugins.DropPrivileges`, daemonize
+ with :class:`cherrypy.process.plugins.Daemonizer`, and write a
+ PID file with :class:`cherrypy.process.plugins.PIDFile`. """
DropPrivileges(cherrypy.engine, uid=self.setup['daemon_uid'],
gid=self.setup['daemon_gid']).subscribe()
Daemonizer(cherrypy.engine).subscribe()
PIDFile(cherrypy.engine, self.setup['daemon']).subscribe()
def _run(self):
+ """ Start the server listening. """
hostname, port = urlparse(self.setup['location'])[1].split(':')
if self.setup['listen_all']:
hostname = '0.0.0.0'
@@ -117,4 +133,9 @@ class Core(BaseCore):
return True
def _block(self):
+ """ Enter the blocking infinite server
+ loop. :func:`Bcfg2.Server.Core.BaseCore.shutdown` is called on
+ exit by a :meth:`subscription
+ <cherrypy.process.wspbus.Bus.subscribe>` on the top-level
+ CherryPy engine."""
cherrypy.engine.block()