izctl.py

    Script for controlling Zope and ZEO servers. This was the original server control script which introduced command-line server debugging.

    #!/home/klm/work/zope/Zope/bin/python
    # XXX ^^ Above, put the path to the bin/python of your Zope software home ^^
    # -*- python -*-
    
    """Control Zope and ZEO server if it's configured
    
    Usage:
    
        %(zctl)s start [confname ...] [--zeo -arg ... [--zope]] -arg ...
    
        %(zctl)s stop [confname ...]
    
        %(zctl)s start_zeo
    
        %(zctl)s stop_zeo
    
        %(zctl)s status [confname ...]
    
        %(zctl)s make_cgi [filename]
    
        %(zctl)s debug
    
        %(zctl)s test filename
    
        %(zctl)s do [-i] [-f filename]* commands
    
    The file 'zope.conf' must exist in the INSTANCE_HOME where '%(zctl)s' lives.
    It contains configuration information for Zope, and optionally for ZEO.
    Additional Zope configurations may be defined by creating subdirectories
    of the INSTANCE_HOME containing 'conf.py'.  If you specify the name of
    one or more of these subdirectories in a '%(zctl)s start' or '%(zctl)s stop',
    that configuration will be started or stopped instead of the default.
    
    '%(zctl)s make_cgi' will write PCGI settings to the file you specify, or to
    'Zope.cgi' if the name is omitted.
    
    '%(zctl)s debug' will launch an interactive Python session with the Zope
    application object loaded as 'app'.
    """ % {'zctl': 'i1zctl.py'}
    
    import sys, os, socket, time, string
    from os.path import isfile, isdir, abspath
    pjoin = os.path.join
    psplit = os.path.split
    env = os.environ
    run = os.system
    
    # Find out where we are, and where Python is.
    if not sys.argv[0]: HERE = '.'
    else: HERE = psplit(sys.argv[0])[0]
    HERE = abspath(HERE)
    PYTHON = '"%s"' % sys.executable
    ZOPE_ENV = {}
    ZEO = {} # ZEO is off by default
    
    # Load configuration data into global variables
    ZOPE_CONFIG = pjoin(HERE, 'zope.conf')
    if not isfile(ZOPE_CONFIG):
        print 'Zope configuration file "zope.conf" not found.'
        sys.exit(1)
    execfile(ZOPE_CONFIG, globals())
    
    # Poke environment variables needed by Zope and ZEO
    env['INSTANCE_HOME'] = HERE
    ZOPE_HOME = abspath(ZOPE_HOME)
    for k, v in ZEO.items():
        env[k] = str(v)
    PYTHONPATH = env.get('PYTHONPATH', '')
    PYTHONPATH = "%s:%s/lib/python%s%s" % (ZOPE_HOME, ZOPE_HOME,
                                           PYTHONPATH and '', PYTHONPATH)
    
    # Commands
    
    def test(args):
        print PYTHON
        print HERE
        print ZOPE_HOME
        print ZEO
    
    def start(args):
        """Start Zope when ZEO is reachable, starting ZEO first if necessary."""
        clients = []
        zeo_args = []
        zope_args = []
        while args and args[0][:1] != '-':
            clients.append(args.pop(0))
        while args:
            if args[0] == '--zeo':
                args.pop(0)
                while args and args[0][:2] != '--':
                    zeo_args.append(args.pop(0))
            else:
                if args[0] == '--zope':
                    args.pop(0)
                while args and args[0][:2] != '--':
                    zope_args.append(args.pop(0))
        if not clients:
            clients.append('default')
    
        cmd = '%s "%s/z2.py" %%s' % (PYTHON, ZOPE_HOME)
        global ZOPE_PORT, ZOPE_LOG, ZOPE_OPTS, ZOPE_ENV, CLIENT_HOME
        for client in clients:
            args = list(zope_args)
            if client != 'default':
                ZOPE_PORT = ZOPE_LOG = ZOPE_OPTS = None
                CLIENT_HOME = pjoin(HERE, client)
                conf = pjoin(CLIENT_HOME, 'conf.py')
                if not (isdir(CLIENT_HOME) and isfile(conf)):
                    print 'Client configuration file "%s" was not found.' % conf
                    continue
                execfile(conf, globals())
                args.append('"CLIENT_HOME=%s"' % CLIENT_HOME)
            if ZOPE_OPTS is not None:
                args.insert(0, ZOPE_OPTS)
            if ZOPE_PORT is not None:
                args.insert(0, '-P %s' % ZOPE_PORT)
            if ZOPE_LOG is not None:
                args.append('"STUPID_LOG_FILE=%s"' % ZOPE_LOG)
            for k,v in ZOPE_ENV.items():
                env[k] = str(v)
            if ZEO:
                start_zeo(zeo_args)
            print 'Starting %s (%s) Zope...' % (client, ZOPE_PORT or 80)
            run(cmd % string.join(args))
            
    def start_zeo(args):
        """Try to start ZEO."""
        host = ZEO.get('ZEO_SERVER_NAME', 'localhost')
        port = ZEO['ZEO_SERVER_PORT']
        if host == 'localhost' and not _check_for_service(host, port):
            stop_zeo(None)
            print "Starting ZEO server on", port
            cmd = 'PYTHONPATH=%s %s %s/lib/python/ZEO/start.py -p %s %s &' % (
                PYTHONPATH, PYTHON, ZOPE_HOME, port, string.join(args)
                )
            run(cmd)
        count = 0
        while not _check_for_service(host, port):
            count = count + 1
            if count > ZEO_WAIT_BAILOUT:
                print ("ZEO connect failure, on port %s after %d seconds"
                       % (port, ZEO_WAIT_BAILOUT))
                sys.exit(1)
            print "waiting for ZEO server to start %d/%d" % (count,
                                                             ZEO_WAIT_BAILOUT)
            time.sleep(1)
        print "ZEO server ready."
    
    def stop_zeo(args):
        """Stop the ZEO server."""
        try:
            pids = open('%s/var/ZEO_SERVER.pid' % HERE, 'r').read()
        except:
            return
        print "Stopping ZEO pid: %s" % pids
        run('kill %s' % pids)
    
    def stop(args):
        """Stop client(s)."""
        if not args:
            args = ['var']
        for client in args:
            pidf = pjoin(HERE, client, 'Z2.pid')
            if not isfile(pidf):
                print '"%s" was not found' % pidf
                continue
            run('kill %s' % open(pidf, 'r').read())
    
    def status(args):
        """Print status."""
        print "NAME\tPORT\tPIDS"
        if ZEO:
            host = ZEO.get('ZEO_SERVER_NAME', 'localhost')
            port = ZEO['ZEO_SERVER_PORT']
            pids = ''
            if _check_for_service(host, port):
                if host == 'localhost':
                    pids = open('%s/var/ZEO_SERVER.pid' % HERE, 'r').read()
                else:
                    pids = 'unknown'
            print "ZEO\t%s\t%s" % (port, pids)
        if not args:
            import glob
            for client in glob.glob('%s/*/conf.py' % HERE):
                args.append(psplit(psplit(client)[0])[1])
        for client in args:
            pidf = pjoin(HERE, client, 'Z2.pid')
            if isfile(pidf):
                pids = open(pidf, 'r').read()
            else:
                pids = 'not found'
            print '%s\t%s\t%s' % (client, '', pids)
    
    def make_cgi(args):
        """Create a PCGI parameter file."""
        if args:
            fname = args.pop(0)
        else:
            fname = 'Zope.cgi'
        write = open(fname, 'w').write
        write('''\
    #!%(ZOPE_HOME)s/pcgi/pcgi-wrapper
    PCGI_NAME=Zope
    PCGI_MODULE_PATH=%(ZOPE_HOME)s/lib/python/Zope
    PCGI_PUBLISHER=%(ZOPE_HOME)s/pcgi/pcgi_publisher.py
    PCGI_EXE=%(PYTHON)s
    PCGI_SOCKET_FILE=%(HERE)s/var/pcgi.soc
    PCGI_PID_FILE=%(HERE)s/var/pcgi.pid
    PCGI_ERROR_LOG=%(HERE)s/var/pcgi.log
    PCGI_DISPLAY_ERRORS=1
    BOBO_REALM=Zope
    BOBO_DEBUG_MODE=1
    INSTANCE_HOME=%(HERE)s
    ''' % globals())
    
    def do(args):
        """Execute python commands"""
        if ZEO:
            start_zeo([])
        for k,v in ZOPE_ENV.items():
            env[k] = str(v)
        os.chdir(pjoin(ZOPE_HOME, 'lib', 'python'))
        msg = """
              Zope debugging session for %s
              The root application object is bound to name 'app'.
              To let other people see your changes, you must:
                get_transaction().commit()
              To see other people's changes, you must:
                app._p_jar.sync()""" % HERE
        options = "-c"
        if not args or '-i' in args:
            options = '-i ' + options
            if args:
                args.remove('-i')
            print msg
        while '-f' in args:
            fpos = args.index('-f')
            args.pop(fpos)
            args[fpos] = 'execfile("%s");' % args[fpos]
        cmd = ("%s %s 'import Zope, sys; app=Zope.app(); %s'"
               % (PYTHON, options, string.join(args, " ")))
        run(cmd)
    
    def debug(args):
        """Start an interactive debugging session"""
        args.insert(0, '-i')
        do(args)
    
    def test(args):
        """Run tests"""
        args[0] = os.path.abspath(args[0])
        args.insert(0, '-f')
        do(args)
    
    # Internal helper functions
    
    def _check_for_service(host, port):
        """Return 1 if server is found at (host, port), 0 otherwise."""
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect
      
      
        
          
          host, int(port[+]
        
      
    
    )
            return 1
        except socket.error:
            return 0
    
    def _dispatch():
        """Dispatch command line invocation."""
        if len(sys.argv) == 1:
            print """\
        start [confname ...] [--zeo -arg ... [--zope]] -arg ...
        stop [confname ...]
        start_zeo
        stop_zeo
        status [confname ...]
        make_cgi [filename]
        debug
        test filename
        do [-i] [-f filename]* commands
    """
            args = string.split(raw_input('command: '))
        else:
            args = sys.argv[1:]
        action = string.lower(args.pop(0))
        if action[:1] == '_':
            print 'Invalid action "%s"' % action
            sys.exit(1)
        globals()[action](args)
    
    if __name__ == "__main__":
        _dispatch()