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