source: fedd/fedd.py @ 4adc697

version-1.30
Last change on this file since 4adc697 was 46a0f7a, checked in by Ted Faber <faber@…>, 16 years ago

Add server debugging that prints stack traces for unanticipated internal failures.

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