[Zope-Checkins] CVS: Zope/utilities - zctl_lib.py:1.1.4.1

Chris McDonough chrism@zope.com
Thu, 22 Aug 2002 00:30:17 -0400


Update of /cvs-repository/Zope/utilities
In directory cvs.zope.org:/tmp/cvs-serv26817/utilities

Added Files:
      Tag: chrism-install-branch
	zctl_lib.py 
Log Message:
A branch which allows for a ./configure; make; make install Zope installation.

Here is a sample session to get a flavor for things:

[chrism@james InstallerBranch]$ ./configure --prefix=/home/chrism/opt/zoptest

Configuring Zope installation

Testing for an acceptable Python interpreter...
  The optimimum Python version (2.1.3) was found at /home/chrism/bin/python.

Configuring...
  SOFTWARE_HOME (Zope binary directory) will be /home/chrism/opt/zoptest.
  Makefile written.
  'makeinstance' script written.

[chrism@james InstallerBranch]$ make
/home/chrism/bin/python /home/chrism/software/InstallerBranch/inst/compile_all.py

------------------------------------------------------------------------------
Compiling python modules
Building extension modules
/home/chrism/bin/python setup.py build_ext -i
running build_ext
building 'AccessControl.cAccessControl' extension
gcc -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -g -O2 -Wall -Wstrict-prototypes -fPIC -I/home/chrism/software/InstallerBranch/lib/Components/ExtensionClass/src -I/home/chrism/opt/Python2.1.3/include/python2.1 -c AccessControl/cAccessControl.c -o build/temp.linux-i686-2.1/cAccessControl.o

... and so on ...

chmod 0711 /home/chrism/software/InstallerBranch/var
------------------------------------------------------------------------------

/bin/touch /home/chrism/software/InstallerBranch/build
Zope built. Next, do make install.

[chrism@james InstallerBranch]$ make install
/bin/mkdir -p /home/chrism/opt/zoptest
/bin/cp -r -u /home/chrism/software/InstallerBranch/* /home/chrism/opt/zoptest
... and so on ...
Zope binaries installed successfully.
Now run /home/chrism/opt/zoptest/makeinstance.

[chrism@james InstallerBranch]$ /home/chrism/opt/zoptest/makeinstance
The instance home is the directory from which you will run Zope.
Several instance homes, each with their own data and
configuration, can share one copy of Zope.

Instance home [/home/chrism/opt/zoptest]: /home/chrism/opt/instance
'/home/chrism/opt/instance' does not exist.
Shall I create it [y]? 
Created.
Created 'Extensions' directory.
Created 'import' directory.
Created 'Products' directory.
Created 'var' directory.
Created default database
Created zctl.py
------------------------------------------------------------------------------

Now to create a starting user. Leave the username blank if you want to skip this step.

------------------------------------------------------------------------------
Please choose a username and password.
This will create the initial user with which you manage Zope.
Username: admin
Password: 
Verify password: 
chmod 0644 /home/chrism/opt/instance/inituser
Done!  Use "/home/chrism/opt/instance/zctl.py start" to start Zope.

[chrism@james InstallerBranch]$ cd ~
[chrism@james chrism]$ cd opt/instance/
[chrism@james instance]$ ./zctl.py start
Starting Zope, using:
"/home/chrism/bin/python" "/home/chrism/opt/zoptest/z2.py" 
[chrism@james instance]$ 



=== Added File Zope/utilities/zctl_lib.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
def zctl_usage(args):
    print """Control Zope, and ZEO server if it's configured

Usage:

    zctl.py start [--zeo -arg ... [--zope]] -arg ...

      Start Zope, checking that ZEO is up and starting it, if necessary.

    zctl.py stop

      Stop Zope.  Does not stop ZEO.

    zctl.py debug

      Open an interactive Python session with Zope loaded.

    zctl.py test filename

    zctl.py script filename

      Load Zope environment, then execute file as a Python program.

    zctl.py do [-i] [-f filename]* commands

      Execute Python commands and zero or more files.

    zctl.py stop_all

      Stop Zope, and run ZEO stop script if possible.

    zctl.py start_zeo

      Run ZEO start script if possible.

    zctl.py stop_zeo

      Run ZEO stop script if possible.

    zctl.py status

      Show the status of Zope, and possibly ZEO.

    zctl.py make_cgi [filename]

      Write a Zope.cgi file for use with PCGI.

    zctl.py usage | --help

      Print this information.

'zctl.py' contains configuration information for Zope, and optionally
for connecting to ZEO, for a single Zope instance.  The directory
containing the file 'zctl.py' is the INSTANCE_HOME.  More than one
INSTANCE_HOME can be set up to use the same copy of the Zope software,
and each one can have its own Zope database(s), Products, and
Extensions.

If an instance uses a ZEO server, then you can run a 'zctl.py debug'
session without stopping the Zope instance.
"""

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 = os.curdir()
else: HERE = psplit(sys.argv[0])[0]
HERE = abspath(HERE)
ZOPE_HOME = abspath(ZOPE_HOME)
CLIENT_HOME = pjoin(HERE, 'var')

PYTHON = '"%s"' % sys.executable
ZOPE = {}
ZOPE_ENV = {}
ZEO = {} # ZEO is off by default

# Define 'constants'
OPTS, PORT, LOG, WAIT, SOCKET, CONTROL = (
  'OPTS', 'PORT', 'LOG', 'WAIT', 'SOCKET', 'CONTROL')

# Commands

def zctl_start(args):
    """Start Zope when ZEO is reachable, starting ZEO first if necessary."""
    pidf = pjoin(CLIENT_HOME, 'Z2.pid')
    running = _check_pids(pidf)
    if running:
        for pid, up in running.items():
            if up:
                print 'There appears to be a Zope instance already running.'
                print 'Either stop it, or delete "%s".' % pidf
                return

    zeo_args = []
    zope_args = []

    app = zope_args.append
    while args:
        arg = args.pop(0)
        if arg[:2] == '--':
            argmap = {'zeo': zeo_args, 'zope': zope_args}.get(arg[2:])
            if argmap is None:
                app(arg)
            else:
                app = argmap.append
        else:
            app(arg)

    cmd = '%s "%s/z2.py" %%s' % (PYTHON, ZOPE_HOME)
    args = list(zope_args)
    if ZOPE.get(OPTS):
        args.insert(0, ZOPE[OPTS])
    if ZOPE.get(PORT):
        args.insert(0, '-P %s' % ZOPE[PORT])
    if ZOPE.get(LOG):
        args.append('"STUPID_LOG_FILE=%s"' % ZOPE[LOG])
    for k,v in ZOPE_ENV.items():
        env[k] = str(v)
    env['INSTANCE_HOME'] = HERE
    if ZEO:
        zctl_start_zeo(zeo_args)
        env['ZEO_SOCKET'] = ZEO[SOCKET]
    cmd = cmd % string.join(args)
    print 'Starting Zope, using:'
    print cmd
    run(cmd)
        
def zctl_start_zeo(args):
    """Try to start ZEO."""
    zeosocket = ZEO.get(SOCKET)
    if zeosocket is None:
        print 'Error: ZEO[SOCKET] is not defined.'
        return
    elif ':' in zeosocket:
        zeosocket = tuple(string.split(zeosocket, ':'))
        host, port = zeosocket

    if ZEO.get(CONTROL) and not _check_for_service(zeosocket):
        run(ZEO[CONTROL] + ' start ' + ZEO.get(OPTS, ''))

    count = 0
    ZEO_WAIT_BAILOUT = ZEO.get(WAIT, 60)
    while not _check_for_service(zeosocket):
        count = count + 1
        if count > ZEO_WAIT_BAILOUT:
            print ("ZEO connect failure, on %s after %d seconds"
                   % (zeosocket, ZEO_WAIT_BAILOUT))
            sys.exit(1)
        print "waiting for ZEO server to start %d/%d" % (count,
                                                         ZEO_WAIT_BAILOUT)
        time.sleep(1)

    if host == 'localhost' and not _check_for_service(host, port):
        print "Starting ZEO server on", port
        cmd = '%s %s/lib/python/ZEO/start.py -p %s %s &' % (
            PYTHON, ZOPE_HOME, port, string.join(args)
            )
        run(cmd)

def zctl_stop_zeo(args):
    """Stop the ZEO server."""
    if not ZEO.get(CONTROL):
        print 'Error: ZEO[CONTROL] is not defined.'
        return
    run(ZEO[CONTROL] + ' stop')

def zctl_stop(args):
    """Stop client."""
    pidf = pjoin(CLIENT_HOME, 'Z2.pid')
    if not isfile(pidf):
        print '"%s" was not found' % pidf
        return
    for pid in string.split(open(pidf).read()):
        print 'Stopping Zope process %s' % pid
        os.kill(int(pid), 15)

def zctl_stop_all(args):
    "Stop client and ZEO server"
    zctl_stop(args)
    zctl_stop_zeo(args)

def zctl_status(args):
    """Print status."""
    pidf = pjoin(CLIENT_HOME, 'Z2.pid')
    running = _check_pids(pidf)
    if running is None:
        print 'No Zope process found.'
        return

    for pid, up in running.items():
        if up is None:
            print '%s is in pid file.' % pid
        elif up:
            print '%s is running.' % pid
        else:
            print '%s is in pid file, but not running.' % pid

def zctl_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=%(CLIENT_HOME)s/pcgi.soc
PCGI_PID_FILE=%(CLIENT_HOME)s/pcgi.pid
PCGI_ERROR_LOG=%(CLIENT_HOME)s/pcgi.log
PCGI_DISPLAY_ERRORS=1
BOBO_REALM=Zope
BOBO_DEBUG_MODE=1
INSTANCE_HOME=%(HERE)s
''' % globals())
    
def zctl_do(args):
    """Execute python commands"""
    if ZEO:
        start_zeo([])
    for k,v in ZOPE_ENV.items():
        env[k] = str(v)
    env['INSTANCE_HOME'] = HERE
    cwd = os.getcwd()
    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]
    imports = 'import Zope'
    if not (os.path.samefile(cwd, os.getcwd()) or
            os.path.samefile(cwd, HERE)):
        imports = '%s; import sys; sys.path.append("%s")' % (
            imports, cwd)
    cmd = ("%s %s '%s; app=Zope.app(); %s'"
           % (PYTHON, options, imports, string.join(args, " ")))
    run(cmd)

def zctl_debug(args):
    """Start an interactive debugging session"""
    args.insert(0, '-i')
    zctl_do(args)

def zctl_script(args):
    """Execute a Python script"""
    args[0] = os.path.abspath(args[0])
    args.insert(0, '-f')
    zctl_do(args)

def zctl_test(args):
    """Run a test"""
    zctl_script(args)

# Internal helper functions

def _check_pids(pidfile):
    """Return dictionary of pids, or None if pidfile not found.

    The value for each pid indicates whether the process is running.
    """
    if isfile(pidfile):
        pids = string.split(open(pidfile, 'r').read())
    else:
        return
    running = {}
    if isdir('/proc'):
        for pid in pids:
            running[pid] = isdir('/proc/%s' % pid)
    else:
        for pid in pids:
            running[pid] = None
    return running

def _check_for_service(ssock):
    """Return 1 if server is found, 0 otherwise."""
    try:
        if type(ssock) is type(()):
            stype = socket.AF_INET
        else:
            stype = socket.AF_UNIX
        s = socket.socket(stype, socket.SOCK_STREAM)
        s.connect(ssock)
        return 1
    except socket.error:
        return 0

def main():
    """Dispatch command line invocation."""
    if len(sys.argv) == 1:
        print """\
    start [--zeo -arg ... [--zope]] -arg ...
    stop
    stop_all
    start_zeo
    stop_zeo
    status
    make_cgi [filename]
    debug
    test filename
    do [-i] [-f filename]* commands
    script filename
    usage
"""
        args = string.split(raw_input('command: '))
    else:
        args = sys.argv[1:]
    action = string.lower(args.pop(0))
    if action == '--help':
        action = 'usage'
    if not globals().has_key('zctl_'+action):
        print 'Unknown command "%s"' % action
        sys.exit(1)
    for k, v in ZEO.items():
        env['ZEO_'+k] = str(v)
    globals()['zctl_'+action](args)