]> git.pld-linux.org Git - projects/pld-builder.new.git/blob - PLD_Builder/rpm_builder.py
- define _specdir and _sourcedir
[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                 packagedir = "/home/users/builder/rpm/packages/%s" % b.spec[:-5]
177                 cmd = "cd rpm/SPECS; TMPDIR=%s nice -n %s rpmbuild -bb --define '_smp_mflags -j%d' --define '_specdir %s' --define '_sourcedir %s' %s %s" % \
178                             (tmpdir, config.nice, max_jobs, packagedir, packagedir, rpmbuild_opt, b.spec)
179                 b.log_line("building RPM using: %s" % cmd)
180                 begin_time = time.time()
181                 res = chroot.run(cmd, logfile = b.logfile)
182                 end_time = time.time()
183                 b.log_line("ended at: %s, done in %s" % (time.asctime(), time.strftime("%Hh %Mm %Ss", time.gmtime(end_time - begin_time))))
184                 if res:
185                     res = "FAIL"
186                 files = util.collect_files(b.logfile)
187                 if len(files) > 0:
188                     r.chroot_files.extend(files)
189                 else:
190                     b.log_line("error: No files produced.")
191                     last_section = util.find_last_section(b.logfile)
192                     if last_section == None:
193                         res = "FAIL"
194                     else:
195                         res = "FAIL_%s" % last_section.upper()
196                 b.files = files
197
198     chroot.run("rm -rf %s; cd rpm/SPECS; rpmbuild --nodeps --nobuild " \
199                          "--clean --rmspec --rmsource %s" % \
200                          (tmpdir, b.spec), logfile = b.logfile)
201     chroot.run("rm -rf $HOME/rpm/BUILD/*")
202
203     def ll(l):
204         util.append_to(b.logfile, l)
205  
206     if b.files != []:
207         rpm_cache_dir = config.rpm_cache_dir
208         if "test-build" not in r.flags:
209             # NOTE: copying to cache dir doesn't mean that build failed, so ignore result
210             b.log_line("copy rpm files to cache_dir: %s" % rpm_cache_dir)
211             chroot.run(
212                     "cp -f %s %s && poldek --mo=nodiff --mkidxz -s %s/" % \
213                         (string.join(b.files), rpm_cache_dir, rpm_cache_dir),
214                      logfile = b.logfile, user = "root"
215             )
216         else:
217             ll("test-build: not copying to " + rpm_cache_dir)
218         ll("Begin-PLD-Builder-Info")
219         if "upgrade" in r.flags:
220             b.upgraded = install.upgrade_from_batch(r, b)
221         else:
222             ll("not upgrading")
223         ll("End-PLD-Builder-Info")
224
225     for f in b.files:
226         local = r.tmp_dir + os.path.basename(f)
227         chroot.cp(f, outfile = local, rm = True)
228         ftp.add(local)
229
230     def uploadinfo(b):
231         c="file:SRPMS:%s\n" % b.src_rpm
232         for f in b.files:
233             c=c + "file:ARCH:%s\n" % os.path.basename(f)
234         c=c + "END\n"
235         return c
236
237     if config.gen_upinfo and b.files != [] and 'test-build' not in r.flags:
238         fname = r.tmp_dir + b.src_rpm + ".uploadinfo"
239         f = open(fname, "w")
240         f.write(uploadinfo(b))
241         f.close()
242         ftp.add(fname, "uploadinfo")
243
244     status.pop()
245
246     return res
247
248 def handle_request(r):
249     ftp.init(r)
250     buildlogs.init(r)
251     build.build_all(r, build_rpm)
252     report.send_report(r, is_src = False)
253     ftp.flush()
254     notify.send(r)
255
256 def check_load():
257     do_exit = 0
258     try:
259         f = open("/proc/loadavg")
260         if float(string.split(f.readline())[2]) > config.max_load:
261             do_exit = 1
262     except:
263         pass
264     if do_exit:
265         sys.exit(0)
266
267 def main_for(builder):
268     msg = ""
269
270     init_conf(builder)
271
272     q = B_Queue(path.queue_file + "-" + config.builder)
273     q.lock(0)
274     q.read()
275     if q.requests == []:
276         q.unlock()
277         return
278     req = pick_request(q)
279     q.unlock()
280
281     # high priority tasks have priority < 0, normal tasks >= 0
282     if req.priority >= 0:
283
284         # allow only one build in given builder at once
285         if not lock.lock("building-rpm-for-%s" % config.builder, non_block = 1):
286             return
287         # don't kill server
288         check_load()
289         # not more then job_slots builds at once
290         locked = 0
291         for slot in range(config.job_slots):
292             if lock.lock("building-rpm-slot-%d" % slot, non_block = 1):
293                 locked = 1
294                 break
295         if not locked:
296             return
297
298         # record fact that we got lock for this builder, load balancer
299         # will use it for fair-queuing
300         l = lock.lock("got-lock")
301         f = open(path.got_lock_file, "a")
302         f.write(config.builder + "\n")
303         f.close()
304         l.close()
305     else:
306         msg = "HIGH PRIORITY: "
307     
308     msg += "handling request %s (%d) for %s from %s, priority %s" \
309             % (req.id, req.no, config.builder, req.requester, req.priority)
310     log.notice(msg)
311     status.push(msg)
312     handle_request(req)
313     status.pop()
314
315     def otherreqs(r):
316         if r.no==req.no:
317             return False
318         else:
319             return True
320
321     q = B_Queue(path.queue_file + "-" + config.builder)
322     q.lock(0)
323     q.read()
324     previouslen=len(q.requests)
325     q.requests=filter(otherreqs, q.requests)
326     if len(q.requests)<previouslen:
327         q.write()
328     q.unlock()
329     
330 def main():
331     if len(sys.argv) < 2:
332         raise Exception, "fatal: need to have builder name as first arg"
333     return main_for(sys.argv[1])
334     
335 if __name__ == '__main__':
336     loop.run_loop(main)
This page took 0.045684 seconds and 4 git commands to generate.