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
RevLine 
[6ff0b91]1#!/usr/local/bin/python
2
[2729e48]3import os,sys
[6ff0b91]4
5from optparse import OptionParser
6
[5d3f239]7from federation import config_parser
8from federation.server import server, xmlrpc_handler, soap_handler
[62f3dd9]9from federation.util import fedd_ssl_context, file_expanding_opts
[5d3f239]10from federation.deter_impl import new_feddservice
[6ff0b91]11
[e087a7a]12from socket import error as socket_error
[6a0c9f4]13from threading import Lock, Thread
[11a08b0]14from signal import signal, pause, SIGINT, SIGTERM
[d199ced]15from select import select, error
[0ea11af]16from time import sleep
[50ef6e4]17
[11a08b0]18import logging
[50ef6e4]19import M2Crypto
[62f3dd9]20import os.path
[19cc408]21
[62f3dd9]22class fedd_opts(file_expanding_opts):
[6ff0b91]23    """Encapsulate option processing in this class, rather than in main"""
[62f3dd9]24
[6ff0b91]25    def __init__(self):
[62f3dd9]26        file_expanding_opts.__init__(self, 
27                usage="%prog [opts] (--help for details)",
[6ff0b91]28                version="0.1")
29
[a2da110]30        self.set_defaults(logfile=None, debug=0)
[6ff0b91]31
32        self.add_option("-d", "--debug", action="count", dest="debug", 
33                help="Set debug.  Repeat for more information")
[62f3dd9]34        self.add_option("-f", "--configfile", action="callback",
35                callback=self.expand_file, type='str',
[a2da110]36                default="/usr/local/etc/fedd.conf",
[62f3dd9]37                dest="configfile", help="Configuration file")
[11a08b0]38        self.add_option("-l", "--logfile", action="store", dest="logfile", 
39                help="File to send log messages to")
[6ff0b91]40        self.add_option("--trace", action="store_const", dest="tracefile", 
41                const=sys.stderr, help="Print SOAP exchange to stderr")
42
[0ea11af]43servers_active = True       # Sub-servers run while this is True
[ec4fb42]44servers = [ ]               # server instances instantiated from services
[0ea11af]45servers_lock = Lock()       # Lock to manipulate servers from sub-server threads
[11a08b0]46
47def shutdown(sig, frame):
[0ea11af]48    """
49    On a signal, stop running sub-servers. 
50   
51    This is connected to signals below
52    """
[11a08b0]53    global servers_active, flog
[d199ced]54
[11a08b0]55    servers_active = False
56    flog.info("Received signal %d, shutting down" % sig);
57
[a97394b]58def run_server(s):
[0ea11af]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    """
[11a08b0]66    global servers_active   # Not strictly needed: servers_active is only read
[0ea11af]67    global servers          # List of active servers
68    global servers_lock     # Lock to manipulate servers
[11a08b0]69
[0ea11af]70    while servers_active:
[d199ced]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
[a97394b]79
[0ea11af]80    # Done.  Remove us from the list
81    servers_lock.acquire()
82    servers.remove(s)
83    servers_lock.release()
[a97394b]84
[50ef6e4]85M2Crypto.threading.init()
[6ff0b91]86opts, args = fedd_opts().parse_args()
87
[0ea11af]88# Logging setup
[11a08b0]89flog = logging.getLogger("fedd")
[0ea11af]90ffmt = logging.Formatter("%(asctime)s %(name)s %(message)s",
91        '%d %b %y %H:%M:%S')
[11a08b0]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
[72ed6e4]106
[f92db31]107if not os.access(opts.configfile, os.R_OK):
108    sys.exit("Can't read config file %s" % opts.configfile)
[72ed6e4]109
[0ea11af]110# Initialize the implementation
[f92db31]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))
[6ff0b91]116
[72ed6e4]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
[2729e48]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:
[6ff0b91]128    sys.exit("Must supply certificate file (probably in config)")
129
[2729e48]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
[0ea11af]135# Create the SSL credentials
[2106ed1]136ctx = None
137while ctx == None:
[6ff0b91]138    try:
139        ctx = fedd_ssl_context(impl.cert_file, impl.trusted_certs, 
140                password=impl.cert_pwd)
[2729e48]141    except Exception, e:
[2106ed1]142        if str(e) != "bad decrypt" or impl.cert_pwd != None:
[6ff0b91]143            raise
144
[a2da110]145services = config.get("globals", "services", "23235")
[a97394b]146
[a2da110]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
[a97394b]168
[a2da110]169    t = t.lower()
[46a0f7a]170    sdebug = (opts.debug > 0)
[a2da110]171    try:
172        if t == 'soap':
[46a0f7a]173            servers.append(server((h, p), soap_handler, ctx, impl, sdebug))
[a2da110]174        elif t == 'xmlrpc':
[46a0f7a]175            servers.append(server((h, p), xmlrpc_handler, ctx, impl, sdebug))
[a2da110]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
[11a08b0]183
[0ea11af]184#  Make sure that there are no malformed servers in the list
[a2da110]185servers = [ s for s in servers if s ]
[0ea11af]186
187# Catch signals
[11a08b0]188signal(SIGINT, shutdown)
189signal(SIGTERM, shutdown)
[a97394b]190
[0ea11af]191# Start the servers
[a97394b]192for s in servers:
[0ea11af]193    Thread(target=run_server, args=(s,)).start()
194
195# Main thread waits for signals
196while servers_active:
[d199ced]197    sleep(1.0)
[0ea11af]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)
[11a08b0]208
Note: See TracBrowser for help on using the repository browser.