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