]> git.pld-linux.org Git - projects/pld-builder.new.git/blame - PLD_Builder/rpm_builder.py
Switch to https for client/request handler server and between builders communication...
[projects/pld-builder.new.git] / PLD_Builder / rpm_builder.py
CommitLineData
dfff8bd5
MM
1# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
2
3ea4b156
MM
3import sys
4import os
5import atexit
6import time
8152253f 7import datetime
deda9a51 8import string
3458d3ee 9import urllib
10764658 10import urllib2
3ea4b156
MM
11
12from config import config, init_conf
13from bqueue import B_Queue
3ea4b156
MM
14import lock
15import util
3f446d8f 16import loop
3ea4b156
MM
17import path
18import status
19import log
20import chroot
21import ftp
e2cad913 22import buildlogs
59ce7cd6 23import notify
17f23d66
MM
24import build
25import report
73bdb36b 26import install
3ea4b156 27
53989cf3
MM
28# *HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*
29import socket
30
31socket.myorigsocket=socket.socket
32
33def mysocket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
dfff8bd5
MM
34 s=socket.myorigsocket(family, type, proto)
35 s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
36 return s
53989cf3
MM
37
38socket.socket=mysocket
39# *HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*
40
3ea4b156
MM
41# this code is duplicated in srpm_builder, but we
42# might want to handle some cases differently here
43def pick_request(q):
dfff8bd5
MM
44 def mycmp(r1, r2):
45 if r1.kind != 'group' or r2.kind != 'group':
939af6d7 46 raise Exception, "non-group requests"
dfff8bd5
MM
47 pri_diff = cmp(r1.priority, r2.priority)
48 if pri_diff == 0:
49 return cmp(r1.time, r2.time)
50 else:
bbed4c4d 51 return pri_diff
dfff8bd5
MM
52 q.requests.sort(mycmp)
53 ret = q.requests[0]
54 return ret
3ea4b156 55
5c91097f 56def check_skip_build(r, b):
6ce49c66 57 src_url = config.control_url + "/srpms/" + r.id + "/skipme"
79141e52 58 good = False
9143deab 59 b.log_line("checking if we should skip the build")
6ce49c66
AM
60 while not good:
61 try:
aa90a502
AM
62 headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }
63 req = urllib2.Request(url=src_url, headers=headers)
64 f = urllib2.urlopen(req)
29526c54 65 good = True
f1d6a788 66 except urllib2.HTTPError, error:
70f0fc62 67 return False
10764658
AM
68 except urllib2.URLError, error:
69 # see errno.h
70f0fc62
ER
70 try:
71 errno = error.errno
72 except AttributeError:
73 # python 2.4
74 errno = error.reason[0]
75
76 if errno in [-3, 60, 61, 110, 111]:
6ce49c66
AM
77 b.log_line("unable to connect... trying again")
78 continue
79 else:
80 return False
29526c54 81 f.close()
b8acd415 82 return True
6ce49c66
AM
83 return False
84
17f23d66 85def fetch_src(r, b):
3458d3ee 86 src_url = config.control_url + "/srpms/" + r.id + "/" + urllib.quote(b.src_rpm)
dfff8bd5
MM
87 b.log_line("fetching %s" % src_url)
88 start = time.time()
29526c54 89 good = False
dfff8bd5
MM
90 while not good:
91 try:
aa90a502
AM
92 headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }
93 req = urllib2.Request(url=src_url, headers=headers)
94 f = urllib2.urlopen(req)
29526c54 95 good = True
f1d6a788
AM
96 except urllib2.HTTPError, error:
97 # fail in a way where cron job will retry
deddea65 98 msg = "unable to fetch url %s, http code: %d" % (src_url, error.code)
f1d6a788 99 b.log_line(msg)
2a7e772f
AM
100 queue_time = time.time() - r.time
101 # 6 hours
02bb30ae 102 if error.code != 404 or (queue_time >= 0 and queue_time < (6 * 60 * 60)):
2a7e772f
AM
103 raise IOError, msg
104 else:
ec3ee349 105 msg = "in queue for more than 6 hours, download failing"
2a7e772f
AM
106 b.log_line(msg)
107 return False
10764658 108 except urllib2.URLError, error:
27f5cb59
AM
109 errno = 0
110 if isinstance(error.args[0], IOError):
111 errno = error.args[0].errno
70f0fc62
ER
112
113 if errno in [-3, 60, 61, 110, 111]:
deddea65 114 b.log_line("unable to connect to %s... trying again" % (src_url))
e0226a74 115 continue
dfff8bd5 116 else:
dac46a1f
JR
117 try:
118 print "error.errno: %s" % str(error.errno)
119 except Exception, e:
120 print "error.errno: exception %s" % e
121 try:
122 print "error.reason %s" % str(error.reason)
123 except Exception, e:
124 print "error.reason exception %s" % e
85d03545 125 raise
dfff8bd5
MM
126
127 o = chroot.popen("cat > %s" % b.src_rpm, mode = "w")
9e89ce60
AM
128
129 try:
130 bytes = util.sendfile(f, o)
131 except IOError, e:
00a1db68 132 b.log_line("error: unable to write to `%s': %s" % (b.src_rpm, e))
9e89ce60
AM
133 raise
134
dfff8bd5
MM
135 f.close()
136 o.close()
137 t = time.time() - start
138 if t == 0:
139 b.log_line("fetched %d bytes" % bytes)
140 else:
141 b.log_line("fetched %d bytes, %.1f K/s" % (bytes, bytes / 1024.0 / t))
3ea4b156 142
266325ca 143def prepare_env(logfile = None):
3cf6c853 144 chroot.run("""
efb9c5f7 145 test ! -f /proc/uptime && mount /proc 2>/dev/null
3cf6c853
ER
146 test ! -c /dev/full && rm -f /dev/full && mknod -m 666 /dev/full c 1 7
147 test ! -c /dev/null && rm -f /dev/null && mknod -m 666 /dev/null c 1 3
148 test ! -c /dev/random && rm -f /dev/random && mknod -m 644 /dev/random c 1 8
149 test ! -c /dev/urandom && rm -f /dev/urandom && mknod -m 644 /dev/urandom c 1 9
150 test ! -c /dev/zero && rm -f /dev/zero && mknod -m 666 /dev/zero c 1 5
151
cfbb8475 152 # need entry for "/" in mtab, for diskspace() to work in rpm
4a8b119a 153 [ -z $(awk '$2 == "/" {print $1; exit}' /etc/mtab) ] && mount -f -t rootfs rootfs /
cfbb8475 154
3cf6c853
ER
155 # make neccessary files readable for builder user
156 # TODO: see if they really aren't readable for builder
157 for db in Packages Name Basenames Providename Pubkeys; do
158 db=/var/lib/rpm/$db
159 test -f $db && chmod a+r $db
160 done
161
162 # try to limit network access for builder account
163 /bin/setfacl -m u:builder:--- /etc/resolv.conf
266325ca 164 """, 'root', logfile = logfile)
84c6175b 165
17f23d66 166def build_rpm(r, b):
266325ca
ER
167 packagename = b.get_package_name()
168 if not packagename:
4e14a9bc
ER
169 # should not really get here
170 b.log_line("error: No .spec not given of malformed: '%s'" % b.spec)
171 res = "FAIL_INTERNAL"
172 return res
173
4e14a9bc 174 status.push("building %s (%s)" % (b.spec, packagename))
dfff8bd5 175 b.log_line("request from: %s" % r.requester)
6ce49c66 176
5c91097f 177 if check_skip_build(r, b):
6ce49c66
AM
178 b.log_line("build skipped due to src builder request")
179 res = "SKIP_REQUESTED"
180 return res
181
dfff8bd5
MM
182 b.log_line("started at: %s" % time.asctime())
183 fetch_src(r, b)
184 b.log_line("installing srpm: %s" % b.src_rpm)
ffc633c5 185 res = chroot.run("""
4e14a9bc 186 set -ex;
266325ca 187 install -d %(topdir)s/{BUILD,RPMS};
f7d20c00 188 LC_ALL=en_US.UTF-8 rpm -qp --changelog %(src_rpm)s;
242d917b 189 rpm -Uhv --nodeps %(rpmdefs)s %(src_rpm)s;
4e14a9bc
ER
190 rm -f %(src_rpm)s;
191 """ % {
266325ca 192 'topdir' : b._topdir,
4e14a9bc
ER
193 'rpmdefs' : b.rpmbuild_opts(),
194 'src_rpm' : b.src_rpm
195 }, logfile = b.logfile)
dfff8bd5 196 b.files = []
508a95ef 197
266325ca 198 tmpdir = b.tmpdir()
dfff8bd5
MM
199 if res:
200 b.log_line("error: installing src rpm failed")
511ece79 201 res = "FAIL_SRPM_INSTALL"
deda9a51 202 else:
84c6175b 203 prepare_env()
f83b11d5 204 chroot.run("set -x; install -m 700 -d %s" % tmpdir, logfile=b.logfile)
be264f26 205 b.default_target(config.arch)
81c135f6 206 # check for build arch before filling BR
4e14a9bc 207 cmd = "set -ex; TMPDIR=%(tmpdir)s exec nice -n %(nice)s " \
266325ca 208 "rpmbuild -bp --short-circuit --nodeps %(rpmdefs)s --define 'prep exit 0' %(topdir)s/%(spec)s" % {
4e14a9bc
ER
209 'tmpdir': tmpdir,
210 'nice' : config.nice,
266325ca 211 'topdir' : b._topdir,
4e14a9bc 212 'rpmdefs' : b.rpmbuild_opts(),
4e14a9bc
ER
213 'spec': b.spec,
214 }
81c135f6 215 res = chroot.run(cmd, logfile = b.logfile)
4f2d26fb 216 if res:
511ece79 217 res = "UNSUPP"
a957e64f 218 b.log_line("error: build arch check (%s) failed" % cmd)
81c135f6
ER
219
220 if not res:
feb26777 221 if ("no-install-br" not in r.flags) and not install.uninstall_self_conflict(b):
511ece79 222 res = "FAIL_DEPS_UNINSTALL"
feb26777 223 if ("no-install-br" not in r.flags) and not install.install_br(r, b):
511ece79 224 res = "FAIL_DEPS_INSTALL"
73bdb36b 225 if not res:
1de1342f 226 max_jobs = max(min(int(os.sysconf('SC_NPROCESSORS_ONLN') + 1), config.max_jobs), 1)
85303822 227 if r.max_jobs > 0:
a1b28a50 228 max_jobs = max(min(config.max_jobs, r.max_jobs), 1)
4e14a9bc 229 cmd = "set -ex; : build-id: %(r_id)s; TMPDIR=%(tmpdir)s exec nice -n %(nice)s " \
a06220f8 230 "rpmbuild -bb --define '_smp_mflags -j%(max_jobs)d' --define '_make_opts -Otarget' --define '_pld_builder 1' %(rpmdefs)s %(topdir)s/%(spec)s" % {
4e14a9bc
ER
231 'r_id' : r.id,
232 'tmpdir': tmpdir,
233 'nice' : config.nice,
234 'rpmdefs' : b.rpmbuild_opts(),
266325ca 235 'topdir' : b._topdir,
4e14a9bc
ER
236 'max_jobs' : max_jobs,
237 'spec': b.spec,
238 }
81c135f6 239 b.log_line("building RPM using: %s" % cmd)
efb3621d 240 begin_time = time.time()
81c135f6 241 res = chroot.run(cmd, logfile = b.logfile)
efb3621d 242 end_time = time.time()
8152253f 243 b.log_line("ended at: %s, done in %s" % (time.asctime(), datetime.timedelta(0, end_time - begin_time)))
4f2d26fb
AM
244 if res:
245 res = "FAIL"
a73fab54 246 files = util.collect_files(b.logfile, basedir = b._topdir)
81c135f6
ER
247 if len(files) > 0:
248 r.chroot_files.extend(files)
249 else:
250 b.log_line("error: No files produced.")
2b9e7381
AM
251 last_section = util.find_last_section(b.logfile)
252 if last_section == None:
253 res = "FAIL"
254 else:
255 res = "FAIL_%s" % last_section.upper()
81c135f6
ER
256 b.files = files
257
b9b4ac17 258 # cleanup tmp and build files
4e14a9bc
ER
259 chroot.run("""
260 set -ex;
266325ca 261 chmod -R u+rwX %(topdir)s/BUILD;
b9b4ac17 262 rm -rf %(topdir)s/{tmp,BUILD}
266325ca 263 """ % {
266325ca 264 'topdir' : b._topdir,
266325ca 265 }, logfile = b.logfile)
dfff8bd5
MM
266
267 def ll(l):
268 util.append_to(b.logfile, l)
e6376553 269
dfff8bd5 270 if b.files != []:
fd499dc1 271 rpm_cache_dir = config.rpm_cache_dir
e936beda 272 if "test-build" not in r.flags:
adc1235e 273 # NOTE: copying to cache dir doesn't mean that build failed, so ignore result
90d9480e 274 b.log_line("copy rpm files to cache_dir: %s" % rpm_cache_dir)
adc1235e 275 chroot.run(
fd499dc1
ER
276 "cp -f %s %s && poldek --mo=nodiff --mkidxz -s %s/" % \
277 (string.join(b.files), rpm_cache_dir, rpm_cache_dir),
278 logfile = b.logfile, user = "root"
279 )
e936beda 280 else:
fd499dc1 281 ll("test-build: not copying to " + rpm_cache_dir)
dfff8bd5
MM
282 ll("Begin-PLD-Builder-Info")
283 if "upgrade" in r.flags:
73bdb36b 284 b.upgraded = install.upgrade_from_batch(r, b)
dfff8bd5
MM
285 else:
286 ll("not upgrading")
287 ll("End-PLD-Builder-Info")
deda9a51 288
1051562e 289 for f in b.files:
dfff8bd5 290 local = r.tmp_dir + os.path.basename(f)
3e689257 291 chroot.cp(f, outfile = local, rm = True)
dfff8bd5 292 ftp.add(local)
1051562e 293
b9b4ac17
ER
294 # cleanup all remains from this build
295 chroot.run("""
296 set -ex;
297 rm -rf %(topdir)s;
298 """ % {
299 'topdir' : b._topdir,
300 }, logfile = b.logfile)
301
dfff8bd5
MM
302 def uploadinfo(b):
303 c="file:SRPMS:%s\n" % b.src_rpm
304 for f in b.files:
305 c=c + "file:ARCH:%s\n" % os.path.basename(f)
306 c=c + "END\n"
307 return c
3ea4b156 308
71a06b82 309 if config.gen_upinfo and b.files != [] and 'test-build' not in r.flags:
dfff8bd5
MM
310 fname = r.tmp_dir + b.src_rpm + ".uploadinfo"
311 f = open(fname, "w")
312 f.write(uploadinfo(b))
313 f.close()
314 ftp.add(fname, "uploadinfo")
315
316 status.pop()
317
318 return res
3ea4b156 319
17f23d66 320def handle_request(r):
dfff8bd5
MM
321 ftp.init(r)
322 buildlogs.init(r)
323 build.build_all(r, build_rpm)
324 report.send_report(r, is_src = False)
325 ftp.flush()
63319e0a 326 notify.send(r)
3ea4b156
MM
327
328def check_load():
dfff8bd5
MM
329 do_exit = 0
330 try:
331 f = open("/proc/loadavg")
332 if float(string.split(f.readline())[2]) > config.max_load:
333 do_exit = 1
334 except:
335 pass
336 if do_exit:
337 sys.exit(0)
3ea4b156 338
e8ee9db8 339def main_for(builder):
b8ec18af
AM
340 msg = ""
341
dfff8bd5 342 init_conf(builder)
dfff8bd5 343
dfff8bd5
MM
344 q = B_Queue(path.queue_file + "-" + config.builder)
345 q.lock(0)
346 q.read()
347 if q.requests == []:
348 q.unlock()
349 return
350 req = pick_request(q)
7b16924c 351 q.unlock()
dfff8bd5 352
91112237
AM
353 # high priority tasks have priority < 0, normal tasks >= 0
354 if req.priority >= 0:
b8ec18af
AM
355
356 # allow only one build in given builder at once
357 if not lock.lock("building-rpm-for-%s" % config.builder, non_block = 1):
358 return
359 # don't kill server
360 check_load()
361 # not more then job_slots builds at once
362 locked = 0
363 for slot in range(config.job_slots):
364 if lock.lock("building-rpm-slot-%d" % slot, non_block = 1):
365 locked = 1
366 break
367 if not locked:
368 return
369
370 # record fact that we got lock for this builder, load balancer
371 # will use it for fair-queuing
372 l = lock.lock("got-lock")
373 f = open(path.got_lock_file, "a")
374 f.write(config.builder + "\n")
375 f.close()
376 l.close()
377 else:
8310d504
AM
378 # be able to avoid locking with very low priority
379 if req.priority > -1000:
88a664e8
AM
380 # don't kill server
381 check_load()
8310d504
AM
382 # allow only one build in given builder at once
383 if not lock.lock("building-high-priority-rpm-for-%s" % config.builder, non_block = 1):
384 return
385
b8ec18af 386 msg = "HIGH PRIORITY: "
e6376553 387
b8ec18af
AM
388 msg += "handling request %s (%d) for %s from %s, priority %s" \
389 % (req.id, req.no, config.builder, req.requester, req.priority)
dfff8bd5
MM
390 log.notice(msg)
391 status.push(msg)
392 handle_request(req)
393 status.pop()
394
395 def otherreqs(r):
396 if r.no==req.no:
397 return False
398 else:
399 return True
400
401 q = B_Queue(path.queue_file + "-" + config.builder)
402 q.lock(0)
403 q.read()
404 previouslen=len(q.requests)
405 q.requests=filter(otherreqs, q.requests)
406 if len(q.requests)<previouslen:
407 q.write()
408 q.unlock()
e6376553 409
e8ee9db8 410def main():
dfff8bd5 411 if len(sys.argv) < 2:
939af6d7 412 raise Exception, "fatal: need to have builder name as first arg"
dfff8bd5 413 return main_for(sys.argv[1])
e6376553 414
e8ee9db8 415if __name__ == '__main__':
dfff8bd5 416 loop.run_loop(main)
This page took 0.494286 seconds and 4 git commands to generate.