]> git.pld-linux.org Git - projects/pld-builder.new.git/blob - PLD_Builder/rpm_builder.py
- revert
[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 mor 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 ! -e /dev/full && mknod /dev/full c 1 7 && chmod 666 /dev/full", 'root')
127     chroot.run("test ! -e /dev/null && mknod /dev/null c 1 3 && chmod 666 /dev/null", 'root')
128     chroot.run("test ! -e /dev/random && mknod /dev/random c 1 8 && chmod 644 /dev/random", 'root')
129     chroot.run("test ! -e /dev/urandom && mknod /dev/urandom c 1 9 && chmod 644 /dev/urandom", 'root')
130     chroot.run("test ! -e /dev/zero && mknod /dev/zero c 1 5 && chmod 666 /dev/zero", 'root')
131     # try to limit network access for builder account
132     chroot.run("/bin/setfacl -m u:builder:--- /etc/resolv.conf", 'root')
133
134 def build_rpm(r, b):
135     status.push("building %s" % b.spec)
136     b.log_line("request from: %s" % r.requester)
137
138     if check_skip_build(r, b):
139         b.log_line("build skipped due to src builder request")
140         res = "SKIP_REQUESTED"
141         return res
142
143     b.log_line("started at: %s" % time.asctime())
144     fetch_src(r, b)
145     b.log_line("installing srpm: %s" % b.src_rpm)
146     res = chroot.run("rpm -U %s" % b.src_rpm, logfile = b.logfile)
147     chroot.run("rm -f %s" % b.src_rpm, logfile = b.logfile)
148     b.files = []
149     tmpdir = "/tmp/B." + b.b_id[0:6]
150     if res:
151         b.log_line("error: installing src rpm failed")
152         res = "FAIL_SRPM_INSTALL"
153     else:
154         prepare_env()
155         chroot.run("install -m 700 -d %s" % tmpdir)
156
157         b.default_target(config.arch)
158         rpmbuild_opt = "%s %s %s" % (b.target_string(), b.kernel_string(), b.bconds_string())
159         # check for build arch before filling BR
160         cmd = "cd rpm/SPECS; TMPDIR=%s nice -n %s rpmbuild -bp --short-circuit --nodeps --define 'prep exit 0' %s %s" % \
161             (tmpdir, config.nice, rpmbuild_opt, b.spec)
162         res = chroot.run(cmd, logfile = b.logfile)
163         if res:
164             res = "UNSUPP"
165             b.log_line("error: build arch check (%s) failed" % cmd)
166
167         if not res:
168             if ("no-install-br" not in r.flags) and not install.uninstall_self_conflict(b):
169                 res = "FAIL_DEPS_UNINSTALL"
170             if ("no-install-br" not in r.flags) and not install.install_br(r, b):
171                 res = "FAIL_DEPS_INSTALL"
172             if not res:
173                 max_jobs = max(min(int(os.sysconf('SC_NPROCESSORS_ONLN') * 1.5), config.max_jobs), 1)
174                 if r.max_jobs > 0:
175                     max_jobs = max(min(config.max_jobs, r.max_jobs), 1)
176                 cmd = "cd rpm/SPECS; TMPDIR=%s nice -n %s rpmbuild -bb --define '_smp_mflags -j%d' %s %s" % \
177                             (tmpdir, config.nice, max_jobs, rpmbuild_opt, b.spec)
178                 b.log_line("building RPM using: %s" % cmd)
179                 begin_time = time.time()
180                 res = chroot.run(cmd, logfile = b.logfile)
181                 end_time = time.time()
182                 b.log_line("ended at: %s, done in %s" % (time.asctime(), time.strftime("%Hh %Mm %Ss", time.gmtime(end_time - begin_time))))
183                 if res:
184                     res = "FAIL"
185                 files = util.collect_files(b.logfile)
186                 if len(files) > 0:
187                     r.chroot_files.extend(files)
188                 else:
189                     b.log_line("error: No files produced.")
190                     last_section = util.find_last_section(b.logfile)
191                     if last_section == None:
192                         res = "FAIL"
193                     else:
194                         res = "FAIL_%s" % last_section.upper()
195                 b.files = files
196
197     chroot.run("rm -rf %s; cd rpm/SPECS; rpmbuild --nodeps --nobuild " \
198                          "--clean --rmspec --rmsource %s" % \
199                          (tmpdir, b.spec), logfile = b.logfile)
200     chroot.run("rm -rf $HOME/rpm/BUILD/*")
201
202     def ll(l):
203         util.append_to(b.logfile, l)
204  
205     if b.files != []:
206         rpm_cache_dir = config.rpm_cache_dir
207         if "test-build" not in r.flags:
208             # NOTE: copying to cache dir doesn't mean that build failed, so ignore result
209             b.log_line("copy rpm files to cache_dir: %s" % rpm_cache_dir)
210             chroot.run(
211                     "cp -f %s %s && poldek --mo=nodiff --mkidxz -s %s/" % \
212                         (string.join(b.files), rpm_cache_dir, rpm_cache_dir),
213                      logfile = b.logfile, user = "root"
214             )
215         else:
216             ll("test-build: not copying to " + rpm_cache_dir)
217         ll("Begin-PLD-Builder-Info")
218         if "upgrade" in r.flags:
219             b.upgraded = install.upgrade_from_batch(r, b)
220         else:
221             ll("not upgrading")
222         ll("End-PLD-Builder-Info")
223
224     for f in b.files:
225         local = r.tmp_dir + os.path.basename(f)
226         chroot.cp(f, outfile = local, rm = True)
227         ftp.add(local)
228
229     def uploadinfo(b):
230         c="file:SRPMS:%s\n" % b.src_rpm
231         for f in b.files:
232             c=c + "file:ARCH:%s\n" % os.path.basename(f)
233         c=c + "END\n"
234         return c
235
236     if config.gen_upinfo and b.files != [] and 'test-build' not in r.flags:
237         fname = r.tmp_dir + b.src_rpm + ".uploadinfo"
238         f = open(fname, "w")
239         f.write(uploadinfo(b))
240         f.close()
241         ftp.add(fname, "uploadinfo")
242
243     status.pop()
244
245     return res
246
247 def handle_request(r):
248     ftp.init(r)
249     buildlogs.init(r)
250     build.build_all(r, build_rpm)
251     report.send_report(r, is_src = False)
252     ftp.flush()
253     notify.send(r)
254
255 def check_load():
256     do_exit = 0
257     try:
258         f = open("/proc/loadavg")
259         if float(string.split(f.readline())[2]) > config.max_load:
260             do_exit = 1
261     except:
262         pass
263     if do_exit:
264         sys.exit(0)
265
266 def main_for(builder):
267     msg = ""
268
269     init_conf(builder)
270
271     q = B_Queue(path.queue_file + "-" + config.builder)
272     q.lock(0)
273     q.read()
274     if q.requests == []:
275         q.unlock()
276         return
277     req = pick_request(q)
278     q.unlock()
279
280     # high priority tasks have priority < 0, normal tasks >= 0
281     if req.priority >= 0:
282
283         # allow only one build in given builder at once
284         if not lock.lock("building-rpm-for-%s" % config.builder, non_block = 1):
285             return
286         # don't kill server
287         check_load()
288         # not more then job_slots builds at once
289         locked = 0
290         for slot in range(config.job_slots):
291             if lock.lock("building-rpm-slot-%d" % slot, non_block = 1):
292                 locked = 1
293                 break
294         if not locked:
295             return
296
297         # record fact that we got lock for this builder, load balancer
298         # will use it for fair-queuing
299         l = lock.lock("got-lock")
300         f = open(path.got_lock_file, "a")
301         f.write(config.builder + "\n")
302         f.close()
303         l.close()
304     else:
305         msg = "HIGH PRIORITY: "
306     
307     msg += "handling request %s (%d) for %s from %s, priority %s" \
308             % (req.id, req.no, config.builder, req.requester, req.priority)
309     log.notice(msg)
310     status.push(msg)
311     handle_request(req)
312     status.pop()
313
314     def otherreqs(r):
315         if r.no==req.no:
316             return False
317         else:
318             return True
319
320     q = B_Queue(path.queue_file + "-" + config.builder)
321     q.lock(0)
322     q.read()
323     previouslen=len(q.requests)
324     q.requests=filter(otherreqs, q.requests)
325     if len(q.requests)<previouslen:
326         q.write()
327     q.unlock()
328     
329 def main():
330     if len(sys.argv) < 2:
331         raise Exception, "fatal: need to have builder name as first arg"
332     return main_for(sys.argv[1])
333     
334 if __name__ == '__main__':
335     loop.run_loop(main)
This page took 0.05211 seconds and 4 git commands to generate.