source file: /home/buildslave/tahoe/edgy/build/src/allmydata/scripts/startstop_node.py
file stats: 133 lines, 88 executed: 66.2% covered
   1. 
   2. import os, sys, signal, time
   3. from twisted.python import usage
   4. from allmydata.scripts.common import BasedirMixin
   5. from allmydata.util import fileutil, find_exe
   6. 
   7. class StartOptions(BasedirMixin, usage.Options):
   8.     optParameters = [
   9.         ["basedir", "C", None, "which directory to start the node in"],
  10.         ]
  11.     optFlags = [
  12.         ["profile", "p", "whether to run under the Python profiler, putting results in \"profiling_results.prof\""],
  13.         ]
  14. 
  15. class StopOptions(BasedirMixin, usage.Options):
  16.     optParameters = [
  17.         ["basedir", "C", None, "which directory to stop the node in"],
  18.         ]
  19. 
  20. class RestartOptions(BasedirMixin, usage.Options):
  21.     optParameters = [
  22.         ["basedir", "C", None, "which directory to restart the node in"],
  23.         ]
  24.     optFlags = [
  25.         ["force", "f", "if the node is not already running, start it "
  26.          "instead of complaining that you should have used 'start' instead "
  27.          "of 'restart'"],
  28.         ["profile", "p", "whether to run under the Python profiler, putting results in \"profiling_results.prof\""],
  29.         ]
  30. 
  31. class RunOptions(usage.Options):
  32.     optParameters = [
  33.         ["basedir", "C", None, "which directory to run the node in, CWD by default"],
  34.         ]
  35. 
  36. def do_start(basedir, profile=False, out=sys.stdout, err=sys.stderr):
  37.     print >>out, "STARTING", basedir
  38.     if not os.path.isdir(basedir):
  39.         print >>err, "%s does not look like a directory at all" % basedir
  40.         return 1
  41.     for fn in os.listdir(basedir):
  42.         if fn.endswith(".tac"):
  43.             tac = fn
  44.             break
  45.     else:
  46.         print >>err, "%s does not look like a node directory (no .tac file)" % basedir
  47.         return 1
  48.     if "client" in tac:
  49.         nodetype = "client"
  50.     elif "introducer" in tac:
  51.         nodetype = "introducer"
  52.     else:
  53.         nodetype = "unknown (%s)" % tac
  54. 
  55.     cmd = find_exe.find_exe('twistd')
  56.     if not cmd:
  57.         print "Can't find twistd (it comes with Twisted).  Aborting."
  58.         sys.exit(1)
  59. 
  60.     cmd.extend(["-y", tac])
  61.     if nodetype in ("client", "introducer"):
  62.         fileutil.make_dirs(os.path.join(basedir, "logs"))
  63.         cmd.extend(["--logfile", os.path.join("logs", "twistd.log")])
  64.     if profile:
  65.         cmd.extend(["--profile=profiling_results.prof", "--savestats",])
  66.     curdir = os.getcwd()
  67.     try:
  68.         os.chdir(basedir)
  69.         rc = os.system(' '.join(cmd))
  70.     finally:
  71.         os.chdir(curdir)
  72.     if rc == 0:
  73.         print >>out, "%s node probably started" % nodetype
  74.         return 0
  75.     else:
  76.         print >>err, "%s node probably not started" % nodetype
  77.         return 1
  78. 
  79. def do_stop(basedir, out=sys.stdout, err=sys.stderr):
  80.     print >>out, "STOPPING", basedir
  81.     pidfile = os.path.join(basedir, "twistd.pid")
  82.     if not os.path.exists(pidfile):
  83.         print >>err, "%s does not look like a running node directory (no twistd.pid)" % basedir
  84.         return 2
  85.     pid = open(pidfile, "r").read()
  86.     pid = int(pid)
  87. 
  88.     # kill it hard (SIGKILL), delete the twistd.pid file, then wait for the
  89.     # process itself to go away. If it hasn't gone away after 5 seconds, warn
  90.     # the user but keep waiting until they give up.
  91.     try:
  92.         os.kill(pid, signal.SIGKILL)
  93.     except OSError, oserr:
  94.         if oserr.errno == 3:
  95.             print oserr.strerror
  96.             # the process didn't exist, so wipe the pid file
  97.             os.remove(pidfile)
  98.             return 1
  99.         else:
 100.             raise
 101.     try:
 102.         os.remove(pidfile)
 103.     except EnvironmentError:
 104.         pass
 105.     start = time.time()
 106.     time.sleep(0.1)
 107.     wait = 5
 108.     first_time = True
 109.     while True:
 110.         # poll once per second until we see the process is no longer running
 111.         try:
 112.             os.kill(pid, 0)
 113.         except OSError:
 114.             print >>out, "process %d is dead" % pid
 115.             return
 116.         wait -= 1
 117.         if wait < 0:
 118.             if first_time:
 119.                 print >>err, ("It looks like pid %d is still running "
 120.                               "after %d seconds" % (pid,
 121.                                                     (time.time() - start)))
 122.                 print >>err, "I will keep watching it until you interrupt me."
 123.                 wait = 10
 124.                 first_time = False
 125.             else:
 126.                 print >>err, "pid %d still running after %d seconds" % \
 127.                       (pid, (time.time() - start))
 128.                 wait = 10
 129.         time.sleep(1)
 130.     return 1
 131. 
 132. def start(config, stdout, stderr):
 133.     rc = 0
 134.     for basedir in config['basedirs']:
 135.         rc = do_start(basedir, config['profile'], stdout, stderr) or rc
 136.     return rc
 137. 
 138. def stop(config, stdout, stderr):
 139.     rc = 0
 140.     for basedir in config['basedirs']:
 141.         rc = do_stop(basedir, stdout, stderr) or rc
 142.     return rc
 143. 
 144. def restart(config, stdout, stderr):
 145.     rc = 0
 146.     for basedir in config['basedirs']:
 147.         rc = do_stop(basedir, stdout, stderr) or rc
 148.     if rc == 2 and config['force']:
 149.         print >>stderr, "ignoring couldn't-stop"
 150.         rc = 0
 151.     if rc:
 152.         print >>stderr, "not restarting"
 153.         return rc
 154.     for basedir in config['basedirs']:
 155.         rc = do_start(basedir, config['profile'], stdout, stderr) or rc
 156.     return rc
 157. 
 158. def run(config, stdout, stderr):
 159.     from twisted.internet import reactor
 160.     from twisted.python import log, logfile
 161.     from allmydata import client
 162. 
 163.     basedir = config['basedir']
 164.     if basedir is None:
 165.         basedir = '.'
 166.     else:
 167.         os.chdir(basedir)
 168. 
 169.     # set up twisted logging. this will become part of the node rsn.
 170.     logdir = os.path.join(basedir, 'logs')
 171.     if not os.path.exists(logdir):
 172.         os.makedirs(logdir)
 173.     lf = logfile.LogFile('tahoesvc.log', logdir)
 174.     log.startLogging(lf)
 175. 
 176.     # run the node itself
 177.     c = client.Client(basedir)
 178.     reactor.callLater(0, c.startService) # after reactor startup
 179.     reactor.run()
 180. 
 181.     return 0
 182. 
 183. 
 184. subCommands = [
 185.     ["start", None, StartOptions, "Start a node (of any type)."],
 186.     ["stop", None, StopOptions, "Stop a node."],
 187.     ["restart", None, RestartOptions, "Restart a node."],
 188.     ["run", None, RunOptions, "Run a node synchronously."],
 189. ]
 190. 
 191. dispatch = {
 192.     "start": start,
 193.     "stop": stop,
 194.     "restart": restart,
 195.     "run": run,
 196.     }