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