]> git.pld-linux.org Git - projects/pld-builder.new.git/blob - PLD_Builder/rpm_builder.py
- check for file existence too
[projects/pld-builder.new.git] / PLD_Builder / rpm_builder.py
1 # vi: encoding=utf-8 ts=8 sts=4 sw=4 et
2
3 import sys
4 import os
5 import atexit
6 import time
7 import string
8 import urllib2
9
10 from config import config, init_conf
11 from bqueue import B_Queue
12 import lock
13 import util
14 import loop
15 import path
16 import status
17 import log
18 import chroot
19 import ftp
20 import buildlogs
21 import notify
22 import build
23 import report
24 import install
25
26 # *HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*
27 import socket
28
29 socket.myorigsocket=socket.socket
30
31 def mysocket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
32     s=socket.myorigsocket(family, type, proto)
33     s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
34     return s
35
36 socket.socket=mysocket
37 # *HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*HACK*
38
39 # this code is duplicated in srpm_builder, but we
40 # might want to handle some cases differently here
41 def pick_request(q):
42     def mycmp(r1, r2):
43         if r1.kind != 'group' or r2.kind != 'group':
44             raise Exception, "non-group requests"
45         pri_diff = cmp(r1.priority, r2.priority)
46         if pri_diff == 0:
47             return cmp(r1.time, r2.time)
48         else:
49             return pri_diff
50     q.requests.sort(mycmp)
51     ret = q.requests[0]
52     return ret
53
54 def check_skip_build(r, b):
55     src_url = config.control_url + "/srpms/" + r.id + "/skipme"
56     good  = False
57     b.log_line("checking if we should skip the build")
58     while not good:
59         try:
60             headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }
61             req = urllib2.Request(url=src_url, headers=headers)
62             f = urllib2.urlopen(req)
63             good = True
64         except urllib2.HTTPError, error:
65                 return False
66         except urllib2.URLError, error:
67             # see errno.h
68             if error.errno in [-3, 60, 61, 110, 111]:
69                 b.log_line("unable to connect... trying again")
70                 continue
71             else:
72                 return False
73         return True
74         f.close()
75     return False
76
77 def fetch_src(r, b):
78     src_url = config.control_url + "/srpms/" + r.id + "/" + b.src_rpm
79     b.log_line("fetching %s" % src_url)
80     start = time.time()
81     good = False
82     while not good:
83         try:
84             headers = { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }
85             req = urllib2.Request(url=src_url, headers=headers)
86             f = urllib2.urlopen(req)
87             good = True
88         except urllib2.HTTPError, error:
89             # fail in a way where cron job will retry
90             msg = "unable to fetch file, http code: %d" % error.code
91             b.log_line(msg)
92             queue_time = time.time() - r.time
93             # 6 hours
94             if error.code != 404 or (queue_time >= 0 and queue_time < (6 * 60 * 60)):
95                 raise IOError, msg
96             else:
97                 msg = "in queue for more than 6 hours, download failing"
98                 b.log_line(msg)
99                 return False
100         except urllib2.URLError, error:
101             # see errno.h
102             if error.errno in [-3, 60, 61, 110, 111]:
103                 b.log_line("unable to connect... trying again")
104                 continue
105             else:
106                 raise
107
108     o = chroot.popen("cat > %s" % b.src_rpm, mode = "w")
109
110     try:
111         bytes = util.sendfile(f, o)
112     except IOError, e:
113         b.log_line("error: unable to write to `%s': %s" % (b.src_rpm, e))
114         raise
115
116     f.close()
117     o.close()
118     t = time.time() - start
119     if t == 0:
120         b.log_line("fetched %d bytes" % bytes)
121     else:
122         b.log_line("fetched %d bytes, %.1f K/s" % (bytes, bytes / 1024.0 / t))
123
124 def prepare_env():
125     chroot.run("test ! -f /proc/uptime && mount /proc", 'root')
126     chroot.run("test ! -c /dev/full && rm -f /dev/full && mknod -m 666 /dev/full c 1 7", 'root')
127     chroot.run("test ! -c /dev/null && rm -f /dev/null && mknod -m 666 /dev/null c 1 3", 'root')
128     chroot.run("test ! -c /dev/random && rm -f /dev/random && mknod -m 644 /dev/random c 1 8", 'root')
129     chroot.run("test ! -c /dev/urandom && rm -f /dev/urandom && mknod -m 644 /dev/urandom c 1 9", 'root')
130     chroot.run("test ! -c /dev/zero && rm -f /dev/zero && mknod -m 666 /dev/zero c 1 5", 'root')
131     # make neccessary files readable for builder user
132     # TODO: see if they really aren't readable for builder
133     chroot.run("for db in Packages Name Basenames Providename Pubkeys; do db=/var/lib/rpm/$db; test -f $db && chmod a+r $db; done", 'root')
134     # try to limit network access for builder account
135     chroot.run("/bin/setfacl -m u:builder:--- /etc/resolv.conf", 'root')
136
137 def build_rpm(r, b):
138     status.push("building %s" % b.spec)
139     b.log_line("request from: %s" % r.requester)
140
141     if check_skip_build(r, b):
142         b.log_line("build skipped due to src builder request")
143         res = "SKIP_REQUESTED"
144         return res
145
146     b.log_line("started at: %s" % time.asctime())
147     fetch_src(r, b)
148     b.log_line("installing srpm: %s" % b.src_rpm)
149     res = chroot.run("rpm -U %s" % b.src_rpm, logfile = b.logfile)
150     chroot.run("rm -f %s" % b.src_rpm, logfile = b.logfile)
151     b.files = []
152     tmpdir = "/tmp/B." + b.b_id[0:6]
153     if res:
154         b.log_line("error: installing src rpm failed")
155         res = "FAIL_SRPM_INSTALL"
156     else:
157         prepare_env()
158         chroot.run("install -m 700 -d %s" % tmpdir)
159
160         b.default_target(config.arch)
161         rpmbuild_opt = "%s %s %s" % (b.target_string(), b.kernel_string(), b.bconds_string())
162         # check for build arch before filling BR
163         cmd = "cd rpm/SPECS; TMPDIR=%s nice -n %s rpmbuild -bp --short-circuit --nodeps --define 'prep exit 0' %s %s" % \
164             (tmpdir, config.nice, rpmbuild_opt, b.spec)
165         res = chroot.run(cmd, logfile = b.logfile)
166         if res:
167             res = "UNSUPP"
168             b.log_line("error: build arch check (%s) failed" % cmd)
169
170         if not res:
171             if ("no-install-br" not in r.flags) and not install.uninstall_self_conflict(b):
172                 res = "FAIL_DEPS_UNINSTALL"
173             if ("no-install-br" not in r.flags) and not install.install_br(r, b):
174                 res = "FAIL_DEPS_INSTALL"
175             if not res:
176                 max_jobs = max(min(int(os.sysconf('SC_NPROCESSORS_ONLN') * 1.5), config.max_jobs), 1)
177                 if r.max_jobs > 0:
178                     max_jobs = max(min(config.max_jobs, r.max_jobs), 1)
179                 cmd = "cd rpm/SPECS; TMPDIR=%s nice -n %s rpmbuild -bb --define '_smp_mflags -j%d' %s %s" % \
180                             (tmpdir, config.nice, max_jobs, rpmbuild_opt, b.spec)
181                 b.log_line("building RPM using: %s" % cmd)
182                 begin_time = time.time()
183                 res = chroot.run(cmd, logfile = b.logfile)
184                 end_time = time.time()
185                 b.log_line("ended at: %s, done in %s" % (time.asctime(), time.strftime("%Hh %Mm %Ss", time.gmtime(end_time - begin_time))))
186                 if res:
187                     res = "FAIL"
188                 files = util.collect_files(b.logfile)
189                 if len(files) > 0:
190                     r.chroot_files.extend(files)
191                 else:
192                     b.log_line("error: No files produced.")
193                     last_section = util.find_last_section(b.logfile)
194                     if last_section == None:
195                         res = "FAIL"
196                     else:
197                         res = "FAIL_%s" % last_section.upper()
198                 b.files = files
199
200     chroot.run("rm -rf %s; cd rpm/SPECS; rpmbuild --nodeps --nobuild " \
201                          "--clean --rmspec --rmsource %s" % \
202                          (tmpdir, b.spec), logfile = b.logfile)
203     chroot.run("rm -rf $HOME/rpm/BUILD/*")
204
205     def ll(l):
206         util.append_to(b.logfile, l)
207  
208     if b.files != []:
209         rpm_cache_dir = config.rpm_cache_dir
210         if "test-build" not in r.flags:
211             # NOTE: copying to cache dir doesn't mean that build failed, so ignore result
212             b.log_line("copy rpm files to cache_dir: %s" % rpm_cache_dir)
213             chroot.run(
214                     "cp -f %s %s && poldek --mo=nodiff --mkidxz -s %s/" % \
215                         (string.join(b.files), rpm_cache_dir, rpm_cache_dir),
216                      logfile = b.logfile, user = "root"
217             )
218         else:
219             ll("test-build: not copying to " + rpm_cache_dir)
220         ll("Begin-PLD-Builder-Info")
221         if "upgrade" in r.flags:
222             b.upgraded = install.upgrade_from_batch(r, b)
223         else:
224             ll("not upgrading")
225         ll("End-PLD-Builder-Info")
226
227     for f in b.files:
228         local = r.tmp_dir + os.path.basename(f)
229         chroot.cp(f, outfile = local, rm = True)
230         ftp.add(local)
231
232     def uploadinfo(b):
233         c="file:SRPMS:%s\n" % b.src_rpm
234         for f in b.files:
235             c=c + "file:ARCH:%s\n" % os.path.basename(f)
236         c=c + "END\n"
237         return c
238
239     if config.gen_upinfo and b.files != [] and 'test-build' not in r.flags:
240         fname = r.tmp_dir + b.src_rpm + ".uploadinfo"
241         f = open(fname, "w")
242         f.write(uploadinfo(b))
243         f.close()
244         ftp.add(fname, "uploadinfo")
245
246     status.pop()
247
248     return res
249
250 def handle_request(r):
251     ftp.init(r)
252     buildlogs.init(r)
253     build.build_all(r, build_rpm)
254     report.send_report(r, is_src = False)
255     ftp.flush()
256     notify.send(r)
257
258 def check_load():
259     do_exit = 0
260     try:
261         f = open("/proc/loadavg")
262         if float(string.split(f.readline())[2]) > config.max_load:
263             do_exit = 1
264     except:
265         pass
266     if do_exit:
267         sys.exit(0)
268
269 def main_for(builder):
270     msg = ""
271
272     init_conf(builder)
273
274     q = B_Queue(path.queue_file + "-" + config.builder)
275     q.lock(0)
276     q.read()
277     if q.requests == []:
278         q.unlock()
279         return
280     req = pick_request(q)
281     q.unlock()
282
283     # high priority tasks have priority < 0, normal tasks >= 0
284     if req.priority >= 0:
285
286         # allow only one build in given builder at once
287         if not lock.lock("building-rpm-for-%s" % config.builder, non_block = 1):
288             return
289         # don't kill server
290         check_load()
291         # not more then job_slots builds at once
292         locked = 0
293         for slot in range(config.job_slots):
294             if lock.lock("building-rpm-slot-%d" % slot, non_block = 1):
295                 locked = 1
296                 break
297         if not locked:
298             return
299
300         # record fact that we got lock for this builder, load balancer
301         # will use it for fair-queuing
302         l = lock.lock("got-lock")
303         f = open(path.got_lock_file, "a")
304         f.write(config.builder + "\n")
305         f.close()
306         l.close()
307     else:
308         msg = "HIGH PRIORITY: "
309     
310     msg += "handling request %s (%d) for %s from %s, priority %s" \
311             % (req.id, req.no, config.builder, req.requester, req.priority)
312     log.notice(msg)
313     status.push(msg)
314     handle_request(req)
315     status.pop()
316
317     def otherreqs(r):
318         if r.no==req.no:
319             return False
320         else:
321             return True
322
323     q = B_Queue(path.queue_file + "-" + config.builder)
324     q.lock(0)
325     q.read()
326     previouslen=len(q.requests)
327     q.requests=filter(otherreqs, q.requests)
328     if len(q.requests)<previouslen:
329         q.write()
330     q.unlock()
331     
332 def main():
333     if len(sys.argv) < 2:
334         raise Exception, "fatal: need to have builder name as first arg"
335     return main_for(sys.argv[1])
336     
337 if __name__ == '__main__':
338     loop.run_loop(main)
This page took 0.165044 seconds and 4 git commands to generate.