source: fedd/fedd.py @ b4e5366

axis_examplecompt_changesinfo-ops
Last change on this file since b4e5366 was 62f3dd9, checked in by Ted Faber <faber@…>, 14 years ago

allow command line progams to expand tildes. Added a class derived from OptionParser? to make that easily available.

  • Property mode set to 100755
File size: 5.8 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, 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()
84
85M2Crypto.threading.init()
86opts, args = fedd_opts().parse_args()
87
88# Logging setup
89flog = logging.getLogger("fedd")
90ffmt = logging.Formatter("%(asctime)s %(name)s %(message)s",
91        '%d %b %y %H:%M:%S')
92
93if opts.logfile: fh = logging.FileHandler(opts.logfile)
94else: fh = logging.StreamHandler(sys.stdout)
95
96# The handler will print anything, setting the logger level will affect what
97# gets recorded.
98fh.setLevel(logging.DEBUG)
99
100if opts.debug: flog.setLevel(logging.DEBUG)
101else: flog.setLevel(logging.INFO)
102
103fh.setFormatter(ffmt)
104flog.addHandler(fh)
105
106
107if not os.access(opts.configfile, os.R_OK):
108    sys.exit("Can't read config file %s" % opts.configfile)
109
110# Initialize the implementation
111try:
112    config= config_parser()
113    config.read(opts.configfile)
114except Exception, e:
115    sys.exit("Cannot parse config file %s: %s" % (opts.configfile, e))
116
117try:
118    impl = new_feddservice(config)
119except RuntimeError, e:
120    str = getattr(e, 'desc', None) or getattr(e,'message', None) or \
121            "No message"
122    sys.exit("Error configuring fedd: %s" % str)
123
124if impl.cert_file:
125    if not os.access(impl.cert_file, os.R_OK):
126        sys.exit("Cannot read certificate file: %s" % impl.cert_file)
127else:
128    sys.exit("Must supply certificate file (probably in config)")
129
130if impl.trusted_certs:
131    if not os.access(impl.trusted_certs, os.R_OK):
132        sys.exit("Cannot read trusted certificate file: %s" % \
133                impl.trusted_certs)
134
135# Create the SSL credentials
136ctx = None
137while ctx == None:
138    try:
139        ctx = fedd_ssl_context(impl.cert_file, impl.trusted_certs, 
140                password=impl.cert_pwd)
141    except Exception, e:
142        if str(e) != "bad decrypt" or impl.cert_pwd != None:
143            raise
144
145services = config.get("globals", "services", "23235")
146
147for s in services.split(","):
148    s = s.strip()
149    colons = s.count(":")
150    try:
151        if colons == 0:
152            p = int(s)
153            h = ''
154            t = 'soap'
155        elif colons == 1:
156            p, t  = s.split(":")
157            p = int(p)
158            h = ''
159        elif colons == 2:
160            h, p, t  = s.split(":")
161            p = int(p)
162        else:
163            flog.error("Invalid service specification %s ignored." % s)
164            continue
165    except ValueError:
166        flog.error("Error converting port to integer in %s: spec ignored" % s)
167        continue
168
169    t = t.lower()
170    sdebug = (opts.debug > 0)
171    try:
172        if t == 'soap':
173            servers.append(server((h, p), soap_handler, ctx, impl, sdebug))
174        elif t == 'xmlrpc':
175            servers.append(server((h, p), xmlrpc_handler, ctx, impl, sdebug))
176        else:
177            flog.error("Invalid transport specification (%s) in service %s" % \
178                    (t, s))
179            continue
180    except socket_error, e:
181        flog.error("Cannot create server for %s: %s" % (s, e[1]))
182        continue
183
184#  Make sure that there are no malformed servers in the list
185servers = [ s for s in servers if s ]
186
187# Catch signals
188signal(SIGINT, shutdown)
189signal(SIGTERM, shutdown)
190
191# Start the servers
192for s in servers:
193    Thread(target=run_server, args=(s,)).start()
194
195# Main thread waits for signals
196while servers_active:
197    sleep(1.0)
198
199#Once shutdown starts wait for all the servers to terminate.
200while True:
201    servers_lock.acquire()
202    if len(servers) == 0: 
203        servers_lock.release()
204        flog.info("All servers exited.  Terminating")
205        sys.exit(0)
206    servers_lock.release()
207    sleep(1)
208
Note: See TracBrowser for help on using the repository browser.