source: fedd/fedd.py @ dffa585

Last change on this file since dffa585 was 6bedbdba, checked in by Ted Faber <faber@…>, 13 years ago

Split topdl and fedid out to different packages. Add differential
installs

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