source: fedd/fedd.py @ 5a6b75b

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since 5a6b75b was f92db31, checked in by Ted Faber <faber@…>, 16 years ago

tweak configuration handling

  • Property mode set to 100755
File size: 5.6 KB
RevLine 
[6ff0b91]1#!/usr/local/bin/python
2
[2729e48]3import os,sys
[6ff0b91]4
5from optparse import OptionParser
6
[5d3f239]7from federation import config_parser
8from federation.server import server, xmlrpc_handler, soap_handler
9from federation.util import fedd_ssl_context
10from federation.deter_impl import new_feddservice
[6ff0b91]11
[6a0c9f4]12from threading import Lock, Thread
[11a08b0]13from signal import signal, pause, SIGINT, SIGTERM
[d199ced]14from select import select, error
[0ea11af]15from time import sleep
[11a08b0]16import logging
[19cc408]17
[6ff0b91]18class fedd_opts(OptionParser):
19    """Encapsulate option processing in this class, rather than in main"""
20    def __init__(self):
21        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
22                version="0.1")
23
[a2da110]24        self.set_defaults(logfile=None, debug=0)
[6ff0b91]25
26        self.add_option("-d", "--debug", action="count", dest="debug", 
27                help="Set debug.  Repeat for more information")
28        self.add_option("-f", "--configfile", action="store",
[a2da110]29                default="/usr/local/etc/fedd.conf",
[6ff0b91]30                dest="configfile", help="Configuration file (required)")
[11a08b0]31        self.add_option("-l", "--logfile", action="store", dest="logfile", 
32                help="File to send log messages to")
[6ff0b91]33        self.add_option("--trace", action="store_const", dest="tracefile", 
34                const=sys.stderr, help="Print SOAP exchange to stderr")
35
[0ea11af]36servers_active = True       # Sub-servers run while this is True
[ec4fb42]37servers = [ ]               # server instances instantiated from services
[0ea11af]38servers_lock = Lock()       # Lock to manipulate servers from sub-server threads
[11a08b0]39
40def shutdown(sig, frame):
[0ea11af]41    """
42    On a signal, stop running sub-servers. 
43   
44    This is connected to signals below
45    """
[11a08b0]46    global servers_active, flog
[d199ced]47
[11a08b0]48    servers_active = False
49    flog.info("Received signal %d, shutting down" % sig);
50
[a97394b]51def run_server(s):
[0ea11af]52    """
53    Operate a subserver, shutting down when servers_active is false.
54
55    Each server (that is host/port/transport triple) has a thread running this
56    function, so each can handle requests independently.  They all call in to
57    the same implementation, which must manage its own synchronization.
58    """
[11a08b0]59    global servers_active   # Not strictly needed: servers_active is only read
[0ea11af]60    global servers          # List of active servers
61    global servers_lock     # Lock to manipulate servers
[11a08b0]62
[0ea11af]63    while servers_active:
[d199ced]64        try:
65            i, o, e = select((s,), (), (), 1.0)
66            if s in i: s.handle_request()
67        except error:
68            # The select call seems to get interrupted by signals as well as
69            # the main thread.  This essentially ignores signals in this
70            # thread.
71            pass
[a97394b]72
[0ea11af]73    # Done.  Remove us from the list
74    servers_lock.acquire()
75    servers.remove(s)
76    servers_lock.release()
[a97394b]77
[6ff0b91]78opts, args = fedd_opts().parse_args()
79
[0ea11af]80# Logging setup
[11a08b0]81flog = logging.getLogger("fedd")
[0ea11af]82ffmt = logging.Formatter("%(asctime)s %(name)s %(message)s",
83        '%d %b %y %H:%M:%S')
[11a08b0]84
85if opts.logfile: fh = logging.FileHandler(opts.logfile)
86else: fh = logging.StreamHandler(sys.stdout)
87
88# The handler will print anything, setting the logger level will affect what
89# gets recorded.
90fh.setLevel(logging.DEBUG)
91
92if opts.debug: flog.setLevel(logging.DEBUG)
93else: flog.setLevel(logging.INFO)
94
95fh.setFormatter(ffmt)
96flog.addHandler(fh)
97
[72ed6e4]98
[f92db31]99if not os.access(opts.configfile, os.R_OK):
100    sys.exit("Can't read config file %s" % opts.configfile)
[72ed6e4]101
[0ea11af]102# Initialize the implementation
[f92db31]103try:
104    config= config_parser()
105    config.read(opts.configfile)
106except Exception, e:
107    sys.exit("Cannot parse config file %s: %s" % (opts.configfile, e))
[6ff0b91]108
[72ed6e4]109try:
110    impl = new_feddservice(config)
111except RuntimeError, e:
112    str = getattr(e, 'desc', None) or getattr(e,'message', None) or \
113            "No message"
114    sys.exit("Error configuring fedd: %s" % str)
115
[2729e48]116if impl.cert_file:
117    if not os.access(impl.cert_file, os.R_OK):
118        sys.exit("Cannot read certificate file: %s" % impl.cert_file)
119else:
[6ff0b91]120    sys.exit("Must supply certificate file (probably in config)")
121
[2729e48]122if impl.trusted_certs:
123    if not os.access(impl.trusted_certs, os.R_OK):
124        sys.exit("Cannot read trusted certificate file: %s" % \
125                impl.trusted_certs)
126
[0ea11af]127# Create the SSL credentials
[2106ed1]128ctx = None
129while ctx == None:
[6ff0b91]130    try:
131        ctx = fedd_ssl_context(impl.cert_file, impl.trusted_certs, 
132                password=impl.cert_pwd)
[2729e48]133    except Exception, e:
[2106ed1]134        if str(e) != "bad decrypt" or impl.cert_pwd != None:
[6ff0b91]135            raise
136
[a2da110]137services = config.get("globals", "services", "23235")
[a97394b]138
[a2da110]139for s in services.split(","):
140    s = s.strip()
141    colons = s.count(":")
142    try:
143        if colons == 0:
144            p = int(s)
145            h = ''
146            t = 'soap'
147        elif colons == 1:
148            p, t  = s.split(":")
149            p = int(p)
150            h = ''
151        elif colons == 2:
152            h, p, t  = s.split(":")
153            p = int(p)
154        else:
155            flog.error("Invalid service specification %s ignored." % s)
156            continue
157    except ValueError:
158        flog.error("Error converting port to integer in %s: spec ignored" % s)
159        continue
[a97394b]160
[a2da110]161    t = t.lower()
162    try:
163        if t == 'soap':
[ec4fb42]164            servers.append(server((h, p), soap_handler, ctx, impl))
[a2da110]165        elif t == 'xmlrpc':
[ec4fb42]166            servers.append(server((h, p), xmlrpc_handler, ctx, impl))
[a2da110]167        else:
168            flog.error("Invalid transport specification (%s) in service %s" % \
169                    (t, s))
170            continue
171    except socket_error, e:
172        flog.error("Cannot create server for %s: %s" % (s, e[1]))
173        continue
[11a08b0]174
[0ea11af]175#  Make sure that there are no malformed servers in the list
[a2da110]176servers = [ s for s in servers if s ]
[0ea11af]177
178# Catch signals
[11a08b0]179signal(SIGINT, shutdown)
180signal(SIGTERM, shutdown)
[a97394b]181
[0ea11af]182# Start the servers
[a97394b]183for s in servers:
[0ea11af]184    Thread(target=run_server, args=(s,)).start()
185
186# Main thread waits for signals
187while servers_active:
[d199ced]188    sleep(1.0)
[0ea11af]189
190#Once shutdown starts wait for all the servers to terminate.
191while True:
192    servers_lock.acquire()
193    if len(servers) == 0: 
194        servers_lock.release()
195        flog.info("All servers exited.  Terminating")
196        sys.exit(0)
197    servers_lock.release()
198    sleep(1)
[11a08b0]199
Note: See TracBrowser for help on using the repository browser.