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
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 threading import Lock, Thread
13from signal import signal, pause, SIGINT, SIGTERM
14from select import select, error
15from time import sleep
16import logging
17
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
24        self.set_defaults(logfile=None, debug=0)
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",
29                default="/usr/local/etc/fedd.conf",
30                dest="configfile", help="Configuration file (required)")
31        self.add_option("-l", "--logfile", action="store", dest="logfile", 
32                help="File to send log messages to")
33        self.add_option("--trace", action="store_const", dest="tracefile", 
34                const=sys.stderr, help="Print SOAP exchange to stderr")
35
36servers_active = True       # Sub-servers run while this is True
37servers = [ ]               # server instances instantiated from services
38servers_lock = Lock()       # Lock to manipulate servers from sub-server threads
39
40def shutdown(sig, frame):
41    """
42    On a signal, stop running sub-servers. 
43   
44    This is connected to signals below
45    """
46    global servers_active, flog
47
48    servers_active = False
49    flog.info("Received signal %d, shutting down" % sig);
50
51def run_server(s):
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    """
59    global servers_active   # Not strictly needed: servers_active is only read
60    global servers          # List of active servers
61    global servers_lock     # Lock to manipulate servers
62
63    while servers_active:
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
72
73    # Done.  Remove us from the list
74    servers_lock.acquire()
75    servers.remove(s)
76    servers_lock.release()
77
78opts, args = fedd_opts().parse_args()
79
80# Logging setup
81flog = logging.getLogger("fedd")
82ffmt = logging.Formatter("%(asctime)s %(name)s %(message)s",
83        '%d %b %y %H:%M:%S')
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
98
99if not os.access(opts.configfile, os.R_OK):
100    sys.exit("Can't read config file %s" % opts.configfile)
101
102# Initialize the implementation
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))
108
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
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:
120    sys.exit("Must supply certificate file (probably in config)")
121
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
127# Create the SSL credentials
128ctx = None
129while ctx == None:
130    try:
131        ctx = fedd_ssl_context(impl.cert_file, impl.trusted_certs, 
132                password=impl.cert_pwd)
133    except Exception, e:
134        if str(e) != "bad decrypt" or impl.cert_pwd != None:
135            raise
136
137services = config.get("globals", "services", "23235")
138
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
160
161    t = t.lower()
162    try:
163        if t == 'soap':
164            servers.append(server((h, p), soap_handler, ctx, impl))
165        elif t == 'xmlrpc':
166            servers.append(server((h, p), xmlrpc_handler, ctx, impl))
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
174
175#  Make sure that there are no malformed servers in the list
176servers = [ s for s in servers if s ]
177
178# Catch signals
179signal(SIGINT, shutdown)
180signal(SIGTERM, shutdown)
181
182# Start the servers
183for s in servers:
184    Thread(target=run_server, args=(s,)).start()
185
186# Main thread waits for signals
187while servers_active:
188    sleep(1.0)
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)
199
Note: See TracBrowser for help on using the repository browser.