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