[Zodb-checkins] CVS: ZODB3/zdaemon - zdctl.py:1.2
Guido van Rossum
guido@python.org
Mon, 25 Nov 2002 14:58:33 -0500
Update of /cvs-repository/ZODB3/zdaemon
In directory cvs.zope.org:/tmp/cvs-serv8289
Modified Files:
zdctl.py
Log Message:
Make it a lot more real. There's help, a config file, more actions,
etc.
Still needed: more actions (e.g. "debug", "logtail"); kill the daemon
manager more often (?); abbreviated commands; config params to pass
more options to zdaemon; user testing.
=== ZODB3/zdaemon/zdctl.py 1.1 => 1.2 ===
--- ZODB3/zdaemon/zdctl.py:1.1 Mon Nov 25 08:18:22 2002
+++ ZODB3/zdaemon/zdctl.py Mon Nov 25 14:58:33 2002
@@ -1,9 +1,29 @@
#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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
+#
+##############################################################################
+"""zdctl -- control an application run by zdaemon.
+
+Usage: python zdctl.py -C config-file [action [arguments]]
+
+Options:
+-C/--configuration URL -- configuration file or URL
+action [arguments] -- see below
-"""
-zdctl -- control an application run by zdaemon.
+If no action is specified on the command line, a "shell" interpreting
+actions typed interactively is started.
-Usage: python zdctl.py config-file [command [arguments]]
+Use the action "help" to find out about available actions.
"""
import os
@@ -11,26 +31,81 @@
import cmd
import sys
import time
+import signal
import socket
+if __name__ == "__main__":
+ # Add the parent of the script directory to the module search path
+ from os.path import dirname, abspath, normpath
+ sys.path.append(dirname(dirname(normpath(abspath(sys.argv[0])))))
+
+from ZEO.runsvr import Options
+
+
+class ZDOptions(Options):
+
+ # Where's python?
+ python = sys.executable
+
+ # Where's zdaemon?
+ if __name__ == "__main__":
+ _file = __file__
+ else:
+ _file = sys.argv[0]
+ _file = os.path.normpath(os.path.abspath(_file))
+ _dir = os.path.dirname(_file)
+ zdaemon = os.path.join(_dir, "zdaemon.py")
+
+ # Options for zdaemon
+ backofflimit = 10 # -b SECONDS
+ forever = 0 # -f
+ sockname = os.path.abspath("zdsock") # -s SOCKET
+ exitcodes = [0, 2] # -x LIST
+ user = None # -u USER
+ zdirectory = "/" # -z DIRECTORY
+
+ # Program (and arguments) for zdaemon
+ program = None
+
+ def load_configuration(self):
+ Options.load_configuration(self) # Sets self.rootconf
+ if not self.rootconf:
+ self.usage("a configuration file is required; use -C")
+ # XXX Should allow overriding more zdaemon options here
+ if self.program is None:
+ self.program = self.rootconf.getlist("program")
+ if self.program is None:
+ self.usage("no program specified in configuration")
+
+
class ZDCmd(cmd.Cmd):
prompt = "(zdctl) "
def __init__(self, options):
+ print "program:", " ".join(options.program)
self.options = options
cmd.Cmd.__init__(self)
- self.zdstatus()
+ self.do_status()
+ if self.zd_status:
+ m = re.search("(?m)^args=(.*)$", self.zd_status)
+ if m:
+ s = m.group(1)
+ args = eval(s, {"__builtins__": {}})
+ if args != self.options.program:
+ print "WARNING! zdaemon is managing a different program!"
+ print "our program =", self.options.program
+ print "daemon's args =", args
- def zdcommand(self, command):
- """Send a command to the zdaemon server and return the response.
+ def send_action(self, action):
+ """Send an action to the zdaemon server and return the response.
Return None if the server is not up or any other error happened.
"""
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock.connect(self.options.sockname)
- sock.send(command + "\n")
+ sock.send(action + "\n")
sock.shutdown(1) # We're not writing any more
response = ""
while 1:
@@ -39,15 +114,16 @@
break
response += data
sock.close()
+ print "response =", `response`
return response
except socket.error, msg:
return None
- def zdstatus(self):
+ def get_status(self):
self.zd_up = 0
self.zd_pid = 0
self.zd_status = None
- resp = self.zdcommand("status")
+ resp = self.send_action("status")
if not resp:
return
m = re.search("(?m)^application=(\d+)$", resp)
@@ -57,110 +133,136 @@
self.zd_pid = int(m.group(1))
self.zd_status = resp
+ def awhile(self, cond, msg):
+ try:
+ self.get_status()
+ while not cond():
+ sys.stdout.write(". ")
+ sys.stdout.flush()
+ time.sleep(1)
+ self.get_status()
+ except KeyboardInterrupt:
+ print "^C"
+ else:
+ print msg % self.__dict__
+
+ def help_help(self):
+ print "help -- Print a list of available actions."
+ print "help <action> -- Print help for <action>."
+
def do_start(self, arg):
if not self.zd_up:
- command = [self.options.python,
- self.options.zdaemon,
- "-d",
- "-s", self.options.sockname,
- self.options.program] + self.options.program_arguments
- print command
- os.spawnvp(os.P_WAIT, command[0], command)
- else:
- self.zdcommand("start")
- self.zdstatus()
- while not self.zd_pid:
- sys.stdout.write(". ")
- sys.stdout.flush()
- time.sleep(1)
- self.zdstatus()
- print "started, pid=%d" % self.zd_pid
+ args = [
+ self.options.python,
+ self.options.zdaemon,
+ "-b", str(self.options.backofflimit),
+ "-d",
+ "-s", self.options.sockname,
+ "-x", ",".join(map(str, self.options.exitcodes)),
+ "-z", self.options.zdirectory,
+ ]
+ if self.options.forever:
+ args.append("-f")
+ if self.options.user:
+ argss.extend(["-u", str(self.options.user)])
+ args.extend(self.options.program)
+ print args
+ os.spawnvp(os.P_WAIT, args[0], args)
+ else:
+ self.send_action("start")
+ self.awhile(lambda: self.zd_pid, "started, pid=%(zd_pid)d")
+
+ def help_start(self):
+ print "start -- Start the daemon process."
+ print " If it is already running, do nothing."
def do_stop(self, arg):
- self.zdstatus()
+ self.get_status()
if not self.zd_up:
print "daemon manager not running"
elif not self.zd_pid:
- print "daemon not running"
+ print "program not running"
else:
- self.zdcommand("stop")
- self.zdstatus()
- while self.zd_pid:
- sys.stdout.write(". ")
- sys.stdout.flush()
- time.sleep(1)
- self.zdstatus()
- print "daemon stopped"
+ self.send_action("stop")
+ self.awhile(lambda: not self.zd_pid, "program stopped")
+
+ def help_stop(self):
+ print "stop -- Stop the daemon process."
+ print " If it is not running, do nothing."
def do_restart(self, arg):
- self.zdstatus()
+ self.get_status()
pid = self.zd_pid
if not pid:
self.do_start(arg)
else:
- self.zdcommand("restart")
- self.zdstatus()
- while self.zd_pid in (0, pid):
- sys.stdout.write(". ")
- sys.stdout.flush()
- time.sleep(1)
- self.zdstatus()
- print "daemon restarted, pid=%d" % self.zd_pid
+ self.send_action("restart")
+ self.awhile(lambda: self.zd_pid not in (0, pid),
+ "program restarted, pid=%(zd_pid)d")
+
+ def help_restart(self):
+ print "restart -- Stop and then start the daemon process."
+
+ def do_kill(self, arg):
+ if not arg:
+ sig = signal.SIGTERM
+ else:
+ try:
+ sig = int(arg)
+ except: # int() can raise any number of exceptions
+ print "invalid signal number", `arg`
+ return
+ self.get_status()
+ if not self.zd_pid:
+ print "program not running"
+ return
+ print "kill(%d, %d)" % (self.zd_pid, sig)
+ try:
+ os.kill(self.zd_pid, sig)
+ except os.error, msg:
+ print "Error:", msg
+ else:
+ print "signal %d sent to process %d" % (sig, self.zd_pid)
+
+ def help_kill(self):
+ print "kill [sig] -- Send signal sig to the daemon process."
+ print " The default signal is SIGTERM."
+
+ def do_wait(self, arg):
+ self.awhile(lambda: not self.zd_pid, "program stopped")
+ self.do_status()
+
+ def help_wait(self):
+ print "wait -- Wait for the daemon process to exit."
- def do_status(self, arg):
- self.zdstatus()
+ def do_status(self, arg=""):
+ self.get_status()
if not self.zd_up:
print "daemon manager not running"
elif not self.zd_pid:
- print "daemon not running"
+ print "daemon manager running; program not running"
else:
- print "daemon running: pid=%d" % self.zd_pid
+ print "program running; pid=%d" % self.zd_pid
+ if arg == "-l" and self.zd_status:
+ print self.zd_status
+
+ def help_status(self):
+ print "status [-l] -- Print status for the daemon process."
+ print " With -l, show raw status output as well."
def do_quit(self, arg):
- self.zdstatus()
+ self.get_status()
if not self.zd_pid:
- self.zdcommand("exit")
- self.zdstatus()
- while self.zd_up:
- sys.stdout.write(". ")
- sys.stdout.flush()
- time.sleep(1)
- self.zdstatus()
- print "daemon not running; daemon manager stopped"
+ print "program not running; stopping daemon manager"
+ self.send_action("exit")
+ self.awhile(lambda: not self.zd_up, "daemon manager stopped")
else:
- print "daemon and daemon manager still running"
+ print "program and daemon manager still running"
return 1
-class ZDOptions:
-
- python = sys.executable
- if __name__ == "__main__":
- _file = __file__
- else:
- _file = sys.argv[0]
- _file = os.path.normpath(os.path.abspath(_file))
- _dir = os.path.dirname(_file)
- zdaemon = os.path.join(_dir, "zdaemon.py")
-
- backofflimit = 10 # -b SECONDS
- isclient = 0 # -c
- daemon = 0 # -d
- forever = 0 # -f
- sockname = "zdsock" # -s SOCKET
- exitcodes = [0, 2] # -x LIST
- user = None # -u USER
- zdirectory = "/" # -z DIRECTORY
-
- program = "sleep"
- program_arguments = ["100"]
-
-## program = os.path.join(_dir, "tests/nokill.py")
-## program_arguments = []
-
- def __init__(self, args=None):
- if args is None:
- args = sys.argv[1:]
- self.args = args
+ def help_quit(self):
+ print "quit -- Exit the zdctl shell."
+ print " If the program is not running, stop the daemon manager."
def main(args=None):
options = ZDOptions(args)