1
2
3 import os, sys, stat, re, time
4 import traceback
5 from twisted.python import usage, util, runtime
6
8 buildbot_tac = os.path.join(dir, "buildbot.tac")
9 if not os.path.isfile(buildbot_tac):
10 print "no buildbot.tac"
11 return False
12
13 contents = open(buildbot_tac, "r").read()
14 return "Application('buildslave')" in contents
15
16
17
18
19
20
21
28
37
39 path = os.path.join(self.basedir, "info")
40 if not os.path.exists(path):
41 if not self.quiet:
42 print "mkdir", path
43 os.mkdir(path)
44 created = False
45 admin = os.path.join(path, "admin")
46 if not os.path.exists(admin):
47 if not self.quiet:
48 print "Creating info/admin, you need to edit it appropriately"
49 f = open(admin, "wt")
50 f.write("Your Name Here <admin@youraddress.invalid>\n")
51 f.close()
52 created = True
53 host = os.path.join(path, "host")
54 if not os.path.exists(host):
55 if not self.quiet:
56 print "Creating info/host, you need to edit it appropriately"
57 f = open(host, "wt")
58 f.write("Please put a description of this build host here\n")
59 f.close()
60 created = True
61 access_uri = os.path.join(path, "access_uri")
62 if not os.path.exists(access_uri):
63 if not self.quiet:
64 print "Not creating info/access_uri - add it if you wish"
65 if created and not self.quiet:
66 print "Please edit the files in %s appropriately." % path
67
72
73 - def makeTAC(self, contents, secret=False):
74 tacfile = "buildbot.tac"
75 if os.path.exists(tacfile):
76 oldcontents = open(tacfile, "rt").read()
77 if oldcontents == contents:
78 if not self.quiet:
79 print "buildbot.tac already exists and is correct"
80 return
81 if not self.quiet:
82 print "not touching existing buildbot.tac"
83 print "creating buildbot.tac.new instead"
84 tacfile = "buildbot.tac.new"
85 f = open(tacfile, "wt")
86 f.write(contents)
87 f.close()
88 if secret:
89 os.chmod(tacfile, 0600)
90
91 slaveTAC = """
92 import os
93
94 from twisted.application import service
95 from buildslave.bot import BuildSlave
96
97 basedir = r'%(basedir)s'
98 rotateLength = %(log-size)s
99 maxRotatedFiles = %(log-count)s
100
101 # if this is a relocatable tac file, get the directory containing the TAC
102 if basedir == '.':
103 import os.path
104 basedir = os.path.abspath(os.path.dirname(__file__))
105
106 # note: this line is matched against to check that this is a buildslave
107 # directory; do not edit it.
108 application = service.Application('buildslave')
109 try:
110 from twisted.python.logfile import LogFile
111 from twisted.python.log import ILogObserver, FileLogObserver
112 logfile = LogFile.fromFullPath(os.path.join(basedir, "twistd.log"), rotateLength=rotateLength,
113 maxRotatedFiles=maxRotatedFiles)
114 application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
115 except ImportError:
116 # probably not yet twisted 8.2.0 and beyond, can't set log yet
117 pass
118
119 buildmaster_host = '%(host)s'
120 port = %(port)d
121 slavename = '%(name)s'
122 passwd = '%(passwd)s'
123 keepalive = %(keepalive)d
124 usepty = %(usepty)d
125 umask = %(umask)s
126 maxdelay = %(maxdelay)d
127
128 s = BuildSlave(buildmaster_host, port, slavename, passwd, basedir,
129 keepalive, usepty, umask=umask, maxdelay=maxdelay)
130 s.setServiceParent(application)
131
132 """
133
156
157
158
159 -def stop(config, signame="TERM", wait=False, returnFalseOnNotRunning=False):
160 import signal
161 basedir = config['basedir']
162 quiet = config['quiet']
163
164 if not isBuildslaveDir(config['basedir']):
165 print "not a buildslave directory"
166 sys.exit(1)
167
168 os.chdir(basedir)
169 try:
170 f = open("twistd.pid", "rt")
171 except:
172 if returnFalseOnNotRunning:
173 return False
174 print "buildslave not running."
175 sys.exit(1)
176 pid = int(f.read().strip())
177 signum = getattr(signal, "SIG"+signame)
178 timer = 0
179 try:
180 os.kill(pid, signum)
181 except OSError, e:
182 if e.errno != 3:
183 raise
184
185 if not wait:
186 if not quiet:
187 print "sent SIG%s to process" % signame
188 return
189 time.sleep(0.1)
190 while timer < 10:
191
192 try:
193 os.kill(pid, 0)
194 except OSError:
195 if not quiet:
196 print "buildslave process %d is dead" % pid
197 return
198 timer += 1
199 time.sleep(1)
200 if not quiet:
201 print "never saw process go away"
202
218
219
221 optFlags = [
222 ['help', 'h', "Display this message"],
223 ["quiet", "q", "Do not emit the commands being run"],
224 ]
225
226 longdesc = """
227 Operates upon the specified <basedir> (or the current directory, if not
228 specified).
229 """
230
231 opt_h = usage.Options.opt_help
232
234 if len(args) > 0:
235 self['basedir'] = args[0]
236 else:
237
238 self['basedir'] = os.getcwd()
239 if len(args) > 1:
240 raise usage.UsageError("I wasn't expecting so many arguments")
241
242 - def postOptions(self):
243 self['basedir'] = os.path.abspath(self['basedir'])
244
246 optFlags = [
247 ['quiet', 'q', "Don't display startup log messages"],
248 ]
250 return "Usage: buildslave start [<basedir>]"
251
254 return "Usage: buildslave stop [<basedir>]"
255
257 optFlags = [
258 ['quiet', 'q', "Don't display startup log messages"],
259 ]
261 return "Usage: buildslave restart [<basedir>]"
262
264 optFlags = [
265 ["force", "f", "Re-use an existing directory"],
266 ["relocatable", "r",
267 "Create a relocatable buildbot.tac"],
268 ]
269 optParameters = [
270 ["keepalive", "k", 600,
271 "Interval at which keepalives should be sent (in seconds)"],
272 ["usepty", None, 0,
273 "(1 or 0) child processes should be run in a pty (default 0)"],
274 ["umask", None, "None",
275 "controls permissions of generated files. Use --umask=022 to be world-readable"],
276 ["maxdelay", None, 300,
277 "Maximum time between connection attempts"],
278 ["log-size", "s", "1000000",
279 "size at which to rotate twisted log files"],
280 ["log-count", "l", "None",
281 "limit the number of kept old twisted log files"],
282 ]
283
284 longdesc = """
285 This command creates a buildslave working directory and buildbot.tac
286 file. The bot will use the <name> and <passwd> arguments to authenticate
287 itself when connecting to the master. All commands are run in a
288 build-specific subdirectory of <basedir>. <master> is a string of the
289 form 'hostname:port', and specifies where the buildmaster can be reached.
290
291 <name>, <passwd>, and <master> will be provided by the buildmaster
292 administrator for your bot. You must choose <basedir> yourself.
293 """
294
296 return "Usage: buildslave create-slave [options] <basedir> <master> <name> <passwd>"
297
299 if len(args) < 4:
300 raise usage.UsageError("command needs more arguments")
301 basedir, master, name, passwd = args
302 if master[:5] == "http:":
303 raise usage.UsageError("<master> is not a URL - do not use URL")
304 self['basedir'] = basedir
305 self['master'] = master
306 self['name'] = name
307 self['passwd'] = passwd
308
309 - def postOptions(self):
310 MakerBase.postOptions(self)
311 self['usepty'] = int(self['usepty'])
312 self['keepalive'] = int(self['keepalive'])
313 self['maxdelay'] = int(self['maxdelay'])
314 if self['master'].find(":") == -1:
315 raise usage.UsageError("master must be in the form host:portnum")
316 if not re.match('^\d+$', self['log-size']):
317 raise usage.UsageError("log-size parameter needs to be an int")
318 if not re.match('^\d+$', self['log-count']) and \
319 self['log-count'] != 'None':
320 raise usage.UsageError("log-count parameter needs to be an int "+
321 " or None")
322
324 synopsis = "Usage: buildslave <command> [command options]"
325
326 subCommands = [
327
328 ['create-slave', None, SlaveOptions,
329 "Create and populate a directory for a new buildslave"],
330 ['start', None, StartOptions, "Start a buildslave"],
331 ['stop', None, StopOptions, "Stop a buildslave"],
332 ['restart', None, RestartOptions,
333 "Restart a buildslave"],
334 ]
335
340
342 from twisted.python import log
343 log.startLogging(sys.stderr)
344
345 - def postOptions(self):
346 if not hasattr(self, 'subOptions'):
347 raise usage.UsageError("must specify a command")
348
349
378