1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import os, sys, re, time
19 from twisted.python import usage
20
22 buildbot_tac = os.path.join(dir, "buildbot.tac")
23 if not os.path.isfile(buildbot_tac):
24 print "no buildbot.tac"
25 return False
26
27 contents = open(buildbot_tac, "r").read()
28 return "Application('buildslave')" in contents
29
30
31
32
33
34
35
42
51
53 path = os.path.join(self.basedir, "info")
54 if not os.path.exists(path):
55 if not self.quiet:
56 print "mkdir", path
57 os.mkdir(path)
58 created = False
59 admin = os.path.join(path, "admin")
60 if not os.path.exists(admin):
61 if not self.quiet:
62 print "Creating info/admin, you need to edit it appropriately"
63 f = open(admin, "wt")
64 f.write("Your Name Here <admin@youraddress.invalid>\n")
65 f.close()
66 created = True
67 host = os.path.join(path, "host")
68 if not os.path.exists(host):
69 if not self.quiet:
70 print "Creating info/host, you need to edit it appropriately"
71 f = open(host, "wt")
72 f.write("Please put a description of this build host here\n")
73 f.close()
74 created = True
75 access_uri = os.path.join(path, "access_uri")
76 if not os.path.exists(access_uri):
77 if not self.quiet:
78 print "Not creating info/access_uri - add it if you wish"
79 if created and not self.quiet:
80 print "Please edit the files in %s appropriately." % path
81
86
87 - def makeTAC(self, contents, secret=False):
88 tacfile = "buildbot.tac"
89 if os.path.exists(tacfile):
90 oldcontents = open(tacfile, "rt").read()
91 if oldcontents == contents:
92 if not self.quiet:
93 print "buildbot.tac already exists and is correct"
94 return
95 if not self.quiet:
96 print "not touching existing buildbot.tac"
97 print "creating buildbot.tac.new instead"
98 tacfile = "buildbot.tac.new"
99 f = open(tacfile, "wt")
100 f.write(contents)
101 f.close()
102 if secret:
103 os.chmod(tacfile, 0600)
104
105 slaveTACTemplate = ["""
106 import os
107
108 from twisted.application import service
109 from buildslave.bot import BuildSlave
110
111 basedir = r'%(basedir)s'
112 rotateLength = %(log-size)s
113 maxRotatedFiles = %(log-count)s
114
115 # if this is a relocatable tac file, get the directory containing the TAC
116 if basedir == '.':
117 import os.path
118 basedir = os.path.abspath(os.path.dirname(__file__))
119
120 # note: this line is matched against to check that this is a buildslave
121 # directory; do not edit it.
122 application = service.Application('buildslave')
123 """,
124 """
125 try:
126 from twisted.python.logfile import LogFile
127 from twisted.python.log import ILogObserver, FileLogObserver
128 logfile = LogFile.fromFullPath(os.path.join(basedir, "twistd.log"), rotateLength=rotateLength,
129 maxRotatedFiles=maxRotatedFiles)
130 application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
131 except ImportError:
132 # probably not yet twisted 8.2.0 and beyond, can't set log yet
133 pass
134 """,
135 """
136 buildmaster_host = '%(host)s'
137 port = %(port)d
138 slavename = '%(name)s'
139 passwd = '%(passwd)s'
140 keepalive = %(keepalive)d
141 usepty = %(usepty)d
142 umask = %(umask)s
143 maxdelay = %(maxdelay)d
144
145 s = BuildSlave(buildmaster_host, port, slavename, passwd, basedir,
146 keepalive, usepty, umask=umask, maxdelay=maxdelay)
147 s.setServiceParent(application)
148
149 """]
150
181
182
183
184 -def stop(config, signame="TERM", wait=False, returnFalseOnNotRunning=False):
185 import signal
186 basedir = config['basedir']
187 quiet = config['quiet']
188
189 if not isBuildslaveDir(config['basedir']):
190 print "not a buildslave directory"
191 sys.exit(1)
192
193 os.chdir(basedir)
194 try:
195 f = open("twistd.pid", "rt")
196 except:
197 if returnFalseOnNotRunning:
198 return False
199 if not quiet: print "buildslave not running."
200 sys.exit(0)
201 pid = int(f.read().strip())
202 signum = getattr(signal, "SIG"+signame)
203 timer = 0
204 try:
205 os.kill(pid, signum)
206 except OSError, e:
207 if e.errno != 3:
208 raise
209
210 if not wait:
211 if not quiet:
212 print "sent SIG%s to process" % signame
213 return
214 time.sleep(0.1)
215 while timer < 10:
216
217 try:
218 os.kill(pid, 0)
219 except OSError:
220 if not quiet:
221 print "buildslave process %d is dead" % pid
222 return
223 timer += 1
224 time.sleep(1)
225 if not quiet:
226 print "never saw process go away"
227
242
243
245 optFlags = [
246 ['help', 'h', "Display this message"],
247 ["quiet", "q", "Do not emit the commands being run"],
248 ]
249
250 longdesc = """
251 Operates upon the specified <basedir> (or the current directory, if not
252 specified).
253 """
254
255 opt_h = usage.Options.opt_help
256
258 if len(args) > 0:
259 self['basedir'] = args[0]
260 else:
261
262 self['basedir'] = os.getcwd()
263 if len(args) > 1:
264 raise usage.UsageError("I wasn't expecting so many arguments")
265
266 - def postOptions(self):
267 self['basedir'] = os.path.abspath(self['basedir'])
268
270 optFlags = [
271 ['quiet', 'q', "Don't display startup log messages"],
272 ['nodaemon', None, "Don't daemonize (stay in foreground)"],
273 ]
275 return "Usage: buildslave start [<basedir>]"
276
279 return "Usage: buildslave stop [<basedir>]"
280
282 optFlags = [
283 ['quiet', 'q', "Don't display startup log messages"],
284 ['nodaemon', None, "Don't daemonize (stay in foreground)"],
285 ]
287 return "Usage: buildslave restart [<basedir>]"
288
290 optFlags = [
291 ]
292 optParameters = [
293 ]
294
296 return "Usage: buildslave upgrade-slave [<basedir>]"
297
298 longdesc = """
299 This command takes an existing buildslave working directory and
300 upgrades it to the current version.
301 """
302
304 basedir = os.path.expanduser(config['basedir'])
305 buildbot_tac = open(os.path.join(basedir, "buildbot.tac")).read()
306 new_buildbot_tac = buildbot_tac.replace(
307 "from buildbot.slave.bot import BuildSlave",
308 "from buildslave.bot import BuildSlave")
309 if new_buildbot_tac != buildbot_tac:
310 open(os.path.join(basedir, "buildbot.tac"), "w").write(new_buildbot_tac)
311 print "buildbot.tac updated"
312 else:
313 print "No changes made"
314
315 return 0
316
317
319 optFlags = [
320 ["force", "f", "Re-use an existing directory"],
321 ["relocatable", "r",
322 "Create a relocatable buildbot.tac"],
323 ["no-logrotate", "n",
324 "Do not permit buildmaster rotate logs by itself"]
325 ]
326 optParameters = [
327 ["keepalive", "k", 600,
328 "Interval at which keepalives should be sent (in seconds)"],
329 ["usepty", None, 0,
330 "(1 or 0) child processes should be run in a pty (default 0)"],
331 ["umask", None, "None",
332 "controls permissions of generated files. Use --umask=022 to be world-readable"],
333 ["maxdelay", None, 300,
334 "Maximum time between connection attempts"],
335 ["log-size", "s", "10000000",
336 "size at which to rotate twisted log files"],
337 ["log-count", "l", "10",
338 "limit the number of kept old twisted log files (None for unlimited)"],
339 ]
340
341 longdesc = """
342 This command creates a buildslave working directory and buildbot.tac
343 file. The bot will use the <name> and <passwd> arguments to authenticate
344 itself when connecting to the master. All commands are run in a
345 build-specific subdirectory of <basedir>. <master> is a string of the
346 form 'hostname[:port]', and specifies where the buildmaster can be reached.
347 port defaults to 9989
348
349 The appropriate values for <name>, <passwd>, and <master> should be
350 provided to you by the buildmaster administrator. You must choose <basedir>
351 yourself.
352 """
353
355 return "Usage: buildslave create-slave [options] <basedir> <master> <name> <passwd>"
356
358 if len(args) < 4:
359 raise usage.UsageError("command needs more arguments")
360 basedir, master, name, passwd = args
361 if master[:5] == "http:":
362 raise usage.UsageError("<master> is not a URL - do not use URL")
363 self['basedir'] = basedir
364 self['master'] = master
365 self['name'] = name
366 self['passwd'] = passwd
367
368 - def postOptions(self):
369 MakerBase.postOptions(self)
370 self['usepty'] = int(self['usepty'])
371 self['keepalive'] = int(self['keepalive'])
372 self['maxdelay'] = int(self['maxdelay'])
373 if not re.match('^\d+$', self['log-size']):
374 raise usage.UsageError("log-size parameter needs to be an int")
375 if not re.match('^\d+$', self['log-count']) and \
376 self['log-count'] != 'None':
377 raise usage.UsageError("log-count parameter needs to be an int "+
378 " or None")
379
381 synopsis = "Usage: buildslave <command> [command options]"
382
383 subCommands = [
384
385 ['create-slave', None, SlaveOptions,
386 "Create and populate a directory for a new buildslave"],
387 ['upgrade-slave', None, UpgradeSlaveOptions,
388 "Upgrade an existing buildslave directory for the current version"],
389 ['start', None, StartOptions, "Start a buildslave"],
390 ['stop', None, StopOptions, "Stop a buildslave"],
391 ['restart', None, RestartOptions,
392 "Restart a buildslave"],
393 ]
394
399
401 from twisted.python import log
402 log.startLogging(sys.stderr)
403
404 - def postOptions(self):
405 if not hasattr(self, 'subOptions'):
406 raise usage.UsageError("must specify a command")
407
408
439