source: fedd/fedd.py @ ec4fb42

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

Clean up some names that start with fedd_ that are ugly with the new package
structure. A couple other bugs cleaned up, too.

  • Property mode set to 100755
File size: 5.5 KB
Line 
1#!/usr/local/bin/python
2
3import os,sys
4
5from optparse import OptionParser
6
7from fedd.server import server, xmlrpc_handler, soap_handler
8from fedd.config_parser import config_parser
9from fedd.util import fedd_ssl_context
10from fedd.fedd_deter_impl import new_feddservice
11
12from threading import *
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
99
100# Initialize the implementation
101if opts.configfile != None: 
102    try:
103        config= config_parser()
104        config.read(opts.configfile)
105    except Exception, e:
106        sys.exit("Cannot parse config file: %s" % e)
107else: 
108    sys.exit("--configfile is required")
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    try:
164        if t == 'soap':
165            servers.append(server((h, p), soap_handler, ctx, impl))
166        elif t == 'xmlrpc':
167            servers.append(server((h, p), xmlrpc_handler, ctx, impl))
168        else:
169            flog.error("Invalid transport specification (%s) in service %s" % \
170                    (t, s))
171            continue
172    except socket_error, e:
173        flog.error("Cannot create server for %s: %s" % (s, e[1]))
174        continue
175
176#  Make sure that there are no malformed servers in the list
177servers = [ s for s in servers if s ]
178
179# Catch signals
180signal(SIGINT, shutdown)
181signal(SIGTERM, shutdown)
182
183# Start the servers
184for s in servers:
185    Thread(target=run_server, args=(s,)).start()
186
187# Main thread waits for signals
188while servers_active:
189    sleep(1.0)
190
191#Once shutdown starts wait for all the servers to terminate.
192while True:
193    servers_lock.acquire()
194    if len(servers) == 0: 
195        servers_lock.release()
196        flog.info("All servers exited.  Terminating")
197        sys.exit(0)
198    servers_lock.release()
199    sleep(1)
200
Note: See TracBrowser for help on using the repository browser.