]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/scripts/runner.py
runner.py: spoke too soon. Really fix #51 this time.
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / scripts / runner.py
1 #! /usr/bin/env python
2
3 import os, subprocess, sys, signal, time
4 from twisted.python import usage
5
6 from twisted.python.procutils import which
7
8 def testtwistd(loc):
9     try:
10         return subprocess.call(["python", loc,], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
11     except:
12         return -1
13     
14 twistd = None
15 if not twistd:
16     for maybetwistd in which("twistd"):
17         ret = testtwistd(maybetwistd)
18         if ret == 0:
19             twistd = maybetwistd
20             break
21
22 if not twistd:
23     for maybetwistd in which("twistd.py"):
24         ret = testtwistd(maybetwistd)
25         if ret == 0:
26             twistd = maybetwistd
27             break
28
29 if not twistd:
30     maybetwistd = os.path.join(sys.prefix, 'Scripts', 'twistd')
31     ret = testtwistd(maybetwistd)
32     if ret == 0:
33         twistd = maybetwistd
34
35 if not twistd:
36     maybetwistd = os.path.join(sys.prefix, 'Scripts', 'twistd.py')
37     ret = testtwistd(maybetwistd)
38     if ret == 0:
39         twistd = maybetwistd
40
41 if not twistd:
42     print "Can't find twistd (it comes with Twisted).  Aborting."
43     sys.exit(1)
44
45 class BasedirMixin:
46     def postOptions(self):
47         if self['basedir'] is None:
48             raise usage.UsageError("<basedir> parameter is required")
49         self['basedir'] = os.path.abspath(os.path.expanduser(self['basedir']))
50
51 class StartOptions(BasedirMixin, usage.Options):
52     optParameters = [
53         ["basedir", "C", ".", "which directory to start the node in"],
54         ]
55
56 class StopOptions(BasedirMixin, usage.Options):
57     optParameters = [
58         ["basedir", "C", ".", "which directory to stop the node in"],
59         ]
60
61 class RestartOptions(BasedirMixin, usage.Options):
62     optParameters = [
63         ["basedir", "C", ".", "which directory to restart the node in"],
64         ]
65
66 class CreateClientOptions(BasedirMixin, usage.Options):
67     optParameters = [
68         ["basedir", "C", None, "which directory to create the client in"],
69         ]
70     optFlags = [
71         ["quiet", "q", "operate silently"],
72         ]
73
74     def parseArgs(self, *args):
75         if len(args) > 0:
76             self['basedir'] = args[0]
77         if len(args) > 1:
78             raise usage.UsageError("I wasn't expecting so many arguments")
79
80 class CreateIntroducerOptions(BasedirMixin, usage.Options):
81     optParameters = [
82         ["basedir", "C", None, "which directory to create the introducer in"],
83         ]
84     optFlags = [
85         ["quiet", "q", "operate silently"],
86         ]
87
88     def parseArgs(self, *args):
89         if len(args) > 0:
90             self['basedir'] = args[0]
91         if len(args) > 1:
92             raise usage.UsageError("I wasn't expecting so many arguments")
93
94 client_tac = """
95 # -*- python -*-
96
97 from allmydata import client
98 from twisted.application import service
99
100 c = client.Client()
101
102 application = service.Application("allmydata_client")
103 c.setServiceParent(application)
104 """
105
106 introducer_tac = """
107 # -*- python -*-
108
109 from allmydata import introducer_and_vdrive
110 from twisted.application import service
111
112 c = introducer_and_vdrive.IntroducerAndVdrive()
113
114 application = service.Application("allmydata_introducer")
115 c.setServiceParent(application)
116 """
117
118 class Options(usage.Options):
119     synopsis = "Usage:  allmydata <command> [command options]"
120
121     subCommands = [
122         ["create-client", None, CreateClientOptions, "Create a client node."],
123         ["create-introducer", None, CreateIntroducerOptions, "Create a introducer node."],
124         ["start", None, StartOptions, "Start a node (of any type)."],
125         ["stop", None, StopOptions, "Stop a node."],
126         ["restart", None, RestartOptions, "Restart a node."],
127         ]
128
129     def postOptions(self):
130         if not hasattr(self, 'subOptions'):
131             raise usage.UsageError("must specify a command")
132
133 def runner(argv, run_by_human=True):
134     config = Options()
135     try:
136         config.parseOptions(argv)
137     except usage.error, e:
138         if not run_by_human:
139             raise
140         print "%s:  %s" % (sys.argv[0], e)
141         print
142         c = getattr(config, 'subOptions', config)
143         print str(c)
144         return 1
145
146     command = config.subCommand
147     so = config.subOptions
148
149     if command == "create-client":
150         rc = create_client(so)
151     elif command == "create-introducer":
152         rc = create_introducer(so)
153     elif command == "start":
154         rc = start(so)
155     elif command == "stop":
156         rc = stop(so)
157     elif command == "restart":
158         rc = restart(so)
159     rc = rc or 0
160     return rc
161
162 def run():
163     rc = runner(sys.argv[1:])
164     sys.exit(rc)
165
166 def create_client(config):
167     basedir = config['basedir']
168     if os.path.exists(basedir):
169         if os.listdir(basedir):
170             print "The base directory already exists: %s" % basedir
171             print "To avoid clobbering anything, I am going to quit now"
172             print "Please use a different directory, or delete this one"
173             return -1
174         # we're willing to use an empty directory
175     else:
176         os.mkdir(basedir)
177     f = open(os.path.join(basedir, "client.tac"), "w")
178     f.write(client_tac)
179     f.close()
180     if not config['quiet']:
181         print "client created in %s" % basedir
182         print " please copy introducer.furl and vdrive.furl into the directory"
183
184 def create_introducer(config):
185     basedir = config['basedir']
186     if os.path.exists(basedir):
187         if os.listdir(basedir):
188             print "The base directory already exists: %s" % basedir
189             print "To avoid clobbering anything, I am going to quit now"
190             print "Please use a different directory, or delete this one"
191             return -1
192         # we're willing to use an empty directory
193     else:
194         os.mkdir(basedir)
195     f = open(os.path.join(basedir, "introducer.tac"), "w")
196     f.write(introducer_tac)
197     f.close()
198     if not config['quiet']:
199         print "introducer created in %s" % basedir
200
201 def start(config):
202     basedir = config['basedir']
203     if os.path.exists(os.path.join(basedir, "client.tac")):
204         tac = "client.tac"
205         type = "client"
206     elif os.path.exists(os.path.join(basedir, "introducer.tac")):
207         tac = "introducer.tac"
208         type = "introducer"
209     else:
210         print "%s does not look like a node directory" % basedir
211         if not os.path.isdir(basedir):
212             print " in fact, it doesn't look like a directory at all!"
213         sys.exit(1)
214     os.chdir(basedir)
215     rc = subprocess.call(["python", twistd, "-y", tac,])
216     if rc == 0:
217         print "%s node probably started" % type
218         return 0
219     else:
220         print "%s node probably not started" % type
221         return 1
222
223 def stop(config):
224     basedir = config['basedir']
225     pidfile = os.path.join(basedir, "twistd.pid")
226     if not os.path.exists(pidfile):
227         print "%s does not look like a running node directory (no twistd.pid)" % basedir
228         return 1
229     pid = open(pidfile, "r").read()
230     pid = int(pid)
231
232     timer = 0
233     os.kill(pid, signal.SIGTERM)
234     time.sleep(0.1)
235     while timer < 5:
236         # poll once per second until twistd.pid goes away, up to 5 seconds
237         try:
238             os.kill(pid, 0)
239         except OSError:
240             print "process %d is dead" % pid
241             return
242         timer += 1
243         time.sleep(1)
244     print "never saw process go away"
245     return 1
246
247 def restart(config):
248     rc = stop(config)
249     if rc:
250         print "not restarting"
251         return rc
252     return start(config)