]> git.pld-linux.org Git - projects/pld-builder.new.git/blame - PLD_Builder/request.py
Create tmpdir with chroot command
[projects/pld-builder.new.git] / PLD_Builder / request.py
CommitLineData
dfff8bd5
MM
1# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
2
51a6a3a6
MM
3from xml.dom.minidom import *
4import string
94169186 5import time
2ba00366
MM
6import xml.sax.saxutils
7import fnmatch
482da2b1 8import os
73bfd962 9import urllib
7d73f518 10import cgi
51a6a3a6 11
30dbf6a3 12import util
17f23d66 13import log
e2cad913 14from acl import acl
b3c8d962 15from config import config
30dbf6a3 16
94169186 17__all__ = ['parse_request', 'parse_requests']
0d52c382 18
51a6a3a6 19def text(e):
dfff8bd5
MM
20 res = ""
21 for n in e.childNodes:
22 if n.nodeType != Element.TEXT_NODE:
23 log.panic("xml: text expected in <%s>, got %d" % (e.nodeName, n.nodeType))
24 res += n.nodeValue
25 return res
51a6a3a6 26
30dbf6a3 27def attr(e, a, default = None):
dfff8bd5
MM
28 try:
29 return e.attributes[a].value
30 except:
31 if default != None:
32 return default
33 raise
51a6a3a6 34
cf1741af 35def escape(s):
dfff8bd5 36 return xml.sax.saxutils.escape(s)
cf1741af 37
04ce7f54
ER
38# return timestamp with timezone information
39# so we could parse it in javascript
40def tzdate(t):
41 # as strftime %z is unofficial, and does not work, need to make it numeric ourselves
42# date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t))
c0e5c8cc 43 date = time.strftime("%a %b %d %Y %H:%M:%S", time.localtime(t))
04ce7f54 44 # NOTE: the altzone is showing CURRENT timezone, not what the "t" reflects
6e2aab74 45 # NOTE: when DST is off timezone gets it right, altzone not
42c9621c
ER
46 if time.daylight:
47 tzoffset = time.altzone
48 else:
3f85c9bc 49 tzoffset = time.timezone
ba569b5d 50 tz = '%+05d' % (-tzoffset / 3600 * 100)
04ce7f54
ER
51 return date + ' ' + tz
52
51a6a3a6 53def is_blank(e):
dfff8bd5 54 return e.nodeType == Element.TEXT_NODE and string.strip(e.nodeValue) == ""
0d52c382 55
51a6a3a6 56class Group:
dfff8bd5
MM
57 def __init__(self, e):
58 self.batches = []
59 self.kind = 'group'
60 self.id = attr(e, "id")
61 self.no = int(attr(e, "no"))
62 self.priority = 2
63 self.time = time.time()
64 self.requester = ""
85303822 65 self.max_jobs = 0
dfff8bd5
MM
66 self.requester_email = ""
67 self.flags = string.split(attr(e, "flags", ""))
68 for c in e.childNodes:
69 if is_blank(c): continue
b051f064 70
dfff8bd5
MM
71 if c.nodeType != Element.ELEMENT_NODE:
72 log.panic("xml: evil group child %d" % c.nodeType)
73 if c.nodeName == "batch":
74 self.batches.append(Batch(c))
75 elif c.nodeName == "requester":
b4d7e94a 76 self.requester = text(c)
dfff8bd5
MM
77 self.requester_email = attr(c, "email", "")
78 elif c.nodeName == "priority":
b4d7e94a 79 self.priority = int(text(c))
dfff8bd5 80 elif c.nodeName == "time":
b4d7e94a 81 self.time = int(text(c))
b3c8d962 82 elif c.nodeName == "maxjobs":
b4d7e94a 83 self.max_jobs = int(text(c))
dfff8bd5
MM
84 else:
85 log.panic("xml: evil group child (%s)" % c.nodeName)
86 # note that we also check that group is sorted WRT deps
87 m = {}
88 for b in self.batches:
89 deps = []
90 m[b.b_id] = b
91 for dep in b.depends_on:
92 if m.has_key(dep):
93 # avoid self-deps
94 if id(m[dep]) != id(b):
95 deps.append(m[dep])
96 else:
97 log.panic("xml: dependency not found in group")
98 b.depends_on = deps
99 if self.requester_email == "" and self.requester != "":
100 self.requester_email = acl.user(self.requester).mail_to()
51a6a3a6 101
dfff8bd5
MM
102 def dump(self, f):
103 f.write("group: %d (id=%s pri=%d)\n" % (self.no, self.id, self.priority))
104 f.write(" from: %s\n" % self.requester)
105 f.write(" flags: %s\n" % string.join(self.flags))
106 f.write(" time: %s\n" % time.asctime(time.localtime(self.time)))
107 for b in self.batches:
108 b.dump(f)
109 f.write("\n")
cf1741af 110
dfff8bd5 111 def dump_html(self, f):
0d52c382 112 f.write(
76627b0c 113 "<div id=\"%(no)d\" class=\"%(flags)s\">\n"
64c5a6cb 114 "<a href=\"#%(no)d\">%(no)d</a>. <span id=\"tz\">%(time)s</span> from <b>%(requester)s</b> "
0d52c382
ER
115 "<small>%(id)s, prio=%(priority)d, jobs=%(max_jobs)d, %(flags)s</small>\n"
116 % {
51a8fa53 117 'no': self.no,
dac6c47c 118 'id': '<a href="srpms/%(id)s">%(id)s</a>' % {'id': self.id},
04ce7f54 119 'time': escape(tzdate(self.time)),
0d52c382
ER
120 'requester': escape(self.requester),
121 'priority': self.priority,
122 'max_jobs': self.max_jobs,
123 'flags': string.join(self.flags)
124 })
dfff8bd5
MM
125 f.write("<ul>\n")
126 for b in self.batches:
eda57100 127 b.dump_html(f, self.id)
dfff8bd5 128 f.write("</ul>\n")
0d52c382 129 f.write("</div>\n")
0cdc1fef 130
dfff8bd5
MM
131 def write_to(self, f):
132 f.write("""
0638381b 133 <group id="%s" no="%d" flags="%s">
e2cad913 134 <requester email='%s'>%s</requester>
cf1741af 135 <time>%d</time>
b3c8d962
AM
136 <priority>%d</priority>
137 <maxjobs>%d</maxjobs>\n""" % (self.id, self.no, string.join(self.flags),
0d52c382 138 escape(self.requester_email), escape(self.requester),
b3c8d962 139 self.time, self.priority, self.max_jobs))
dfff8bd5
MM
140 for b in self.batches:
141 b.write_to(f)
142 f.write(" </group>\n\n")
51a6a3a6 143
dfff8bd5
MM
144 def is_done(self):
145 ok = 1
146 for b in self.batches:
147 if not b.is_done():
148 ok = 0
149 return ok
59ce7cd6 150
51a6a3a6 151class Batch:
dfff8bd5
MM
152 def __init__(self, e):
153 self.bconds_with = []
154 self.bconds_without = []
155 self.builders = []
156 self.builders_status = {}
1432ffd5 157 self.builders_status_time = {}
b4d7e94a 158 self.builders_status_buildtime = {}
aab485ec 159 self.kernel = ""
d6139a25 160 self.defines = {}
be264f26 161 self.target = []
dfff8bd5
MM
162 self.branch = ""
163 self.src_rpm = ""
164 self.info = ""
165 self.spec = ""
166 self.command = ""
167 self.command_flags = []
db286098 168 self.skip = []
dfff8bd5
MM
169 self.gb_id = ""
170 self.b_id = attr(e, "id")
171 self.depends_on = string.split(attr(e, "depends-on"))
be44c85d 172 self.upgraded = True
266325ca
ER
173
174 self.parse_xml(e)
175
a73fab54 176 self._topdir = '/tmp/B.%s' % self.b_id
266325ca
ER
177
178 def parse_xml(self, e):
dfff8bd5
MM
179 for c in e.childNodes:
180 if is_blank(c): continue
b4d7e94a 181
dfff8bd5
MM
182 if c.nodeType != Element.ELEMENT_NODE:
183 log.panic("xml: evil batch child %d" % c.nodeType)
184 if c.nodeName == "src-rpm":
b4d7e94a 185 self.src_rpm = text(c)
dfff8bd5 186 elif c.nodeName == "spec":
8ecc8666
ER
187 # normalize specname, specname is used as buildlog and we don't
188 # want to be exposed to directory traversal attacks
b4d7e94a 189 self.spec = text(c).split('/')[-1]
dfff8bd5
MM
190 elif c.nodeName == "command":
191 self.spec = "COMMAND"
b4d7e94a 192 self.command = text(c).strip()
dfff8bd5
MM
193 self.command_flags = string.split(attr(c, "flags", ""))
194 elif c.nodeName == "info":
b4d7e94a 195 self.info = text(c)
aab485ec 196 elif c.nodeName == "kernel":
b4d7e94a 197 self.kernel = text(c)
d6139a25
ER
198 elif c.nodeName == "define":
199 define = attr(c, "name")
200 self.defines[define] = text(c)
be264f26 201 elif c.nodeName == "target":
b4d7e94a 202 self.target.append(text(c))
db286098 203 elif c.nodeName == "skip":
b4d7e94a 204 self.skip.append(text(c))
dfff8bd5 205 elif c.nodeName == "branch":
b4d7e94a 206 self.branch = text(c)
dfff8bd5 207 elif c.nodeName == "builder":
b4d7e94a 208 key = text(c)
b051f064
ER
209 self.builders.append(key)
210 self.builders_status[key] = attr(c, "status", "?")
211 self.builders_status_time[key] = attr(c, "time", "0")
b4d7e94a 212 self.builders_status_buildtime[key] = "0" #attr(c, "buildtime", "0")
dfff8bd5 213 elif c.nodeName == "with":
b4d7e94a 214 self.bconds_with.append(text(c))
dfff8bd5 215 elif c.nodeName == "without":
b4d7e94a 216 self.bconds_without.append(text(c))
dfff8bd5
MM
217 else:
218 log.panic("xml: evil batch child (%s)" % c.nodeName)
0d52c382 219
266325ca
ER
220 def get_package_name(self):
221 if len(self.spec) <= 5:
222 return None
223 return self.spec[:-5]
224
225 def tmpdir(self):
226 """
227 return tmpdir for this batch job building
228 """
229 # it's better to have TMPDIR and BUILD dir on same partition:
230 # + /usr/bin/bzip2 -dc /home/services/builder/rpm/packages/kernel/patch-2.6.27.61.bz2
231 # patch: **** Can't rename file /tmp/B.a1b1d3/poKWwRlp to drivers/scsi/hosts.c : No such file or directory
232 path = os.path.join(self._topdir, 'BUILD', 'tmp')
233 return path
234
dfff8bd5
MM
235 def is_done(self):
236 ok = 1
237 for b in self.builders:
238 s = self.builders_status[b]
61af514e 239 if not s.startswith("OK") and not s.startswith("SKIP") and not s.startswith("UNSUPP") and not s.startswith("FAIL"):
dfff8bd5
MM
240 ok = 0
241 return ok
0d52c382 242
dfff8bd5
MM
243 def dump(self, f):
244 f.write(" batch: %s/%s\n" % (self.src_rpm, self.spec))
245 f.write(" info: %s\n" % self.info)
aab485ec 246 f.write(" kernel: %s\n" % self.kernel)
d6139a25 247 f.write(" defines: %s\n" % self.defines_string())
be264f26 248 f.write(" target: %s\n" % self.target_string())
dfff8bd5
MM
249 f.write(" branch: %s\n" % self.branch)
250 f.write(" bconds: %s\n" % self.bconds_string())
251 builders = []
252 for b in self.builders:
253 builders.append("%s:%s" % (b, self.builders_status[b]))
254 f.write(" builders: %s\n" % string.join(builders))
51a6a3a6 255
dfff8bd5
MM
256 def is_command(self):
257 return self.command != ""
6953ce5d 258
eda57100 259 def dump_html(self, f, rid):
dfff8bd5 260 f.write("<li>\n")
bd080018 261 if self.is_command():
0c2e0afa 262 desc = "SH: <pre>%s</pre> flags: [%s]" % (self.command, ' '.join(self.command_flags))
dfff8bd5 263 else:
64c5a6cb 264 package_url = "http://git.pld-linux.org/gitweb.cgi?p=packages/%(package)s.git;f=%(spec)s;h=%(branch)s;a=shortlog" % {
59b3448a
ER
265 'spec': self.spec,
266 'branch': self.branch,
267 'package': self.spec[:-5],
268 }
d6139a25 269 desc = "%(src_rpm)s (<a href=\"%(package_url)s\">%(spec)s -r %(branch)s</a>%(rpmopts)s)" % {
59b3448a
ER
270 'src_rpm': self.src_rpm,
271 'spec': self.spec,
272 'branch': self.branch,
d6139a25 273 'rpmopts': self.bconds_string() + self.kernel_string() + self.target_string() + self.defines_string(),
59b3448a
ER
274 'package_url': package_url,
275 }
dfff8bd5
MM
276 f.write("%s <small>[" % desc)
277 builders = []
dfff8bd5
MM
278 for b in self.builders:
279 s = self.builders_status[b]
2fec103c 280 if s.startswith("OK"):
dfff8bd5 281 c = "green"
2b9e7381 282 elif s.startswith("FAIL"):
dfff8bd5 283 c = "red"
2fec103c 284 elif s.startswith("SKIP"):
dfff8bd5 285 c = "blue"
2fec103c 286 elif s.startswith("UNSUPP"):
769856bc 287 c = "fuchsia"
dfff8bd5
MM
288 else:
289 c = "black"
290 link_pre = ""
291 link_post = ""
61af514e 292 if (s.startswith("OK") or s.startswith("SKIP") or s.startswith("UNSUPP") or s.startswith("FAIL")) and len(self.spec) > 5:
dfff8bd5
MM
293 if self.is_command():
294 bl_name = "command"
295 else:
296 bl_name = self.spec[:len(self.spec)-5]
7169155e 297 lin_ar = b.replace('noauto-','')
eda57100 298 path = "/%s/%s/%s,%s.bz2" % (lin_ar.replace('-','/'), s, bl_name, rid)
dfff8bd5 299 is_ok = 0
2fec103c 300 if s.startswith("OK"):
428b18a2 301 is_ok = 1
7169155e 302 bld = lin_ar.split('-')
e40ffefe 303 tree_name = '-'.join(bld[:-1])
7edf20a9 304 tree_arch = '-'.join(bld[-1:])
7a8e28a8
JR
305 link_pre = "<a href=\"%s/index.php?dist=%s&arch=%s&ok=%d&name=%s&id=%s&action=tail\">" \
306 % (config.buildlogs, urllib.quote(tree_name), urllib.quote(tree_arch), is_ok, urllib.quote(bl_name), urllib.quote(rid))
dfff8bd5 307 link_post = "</a>"
b4d7e94a
ER
308
309 def ftime(s):
310 t = float(s)
311 if t > 0:
312 return time.asctime(time.localtime(t))
313 else:
314 return 'N/A'
315
316 tooltip = "last update: %(time)s\nbuild time: %(buildtime)s" % {
317 'time' : ftime(self.builders_status_time[b]),
318 'buildtime' : ftime(self.builders_status_buildtime[b]),
7d73f518
ER
319 }
320 builders.append(link_pre +
321 "<font color='%(color)s'><b title=\"%(tooltip)s\">%(builder)s:%(status)s</b></font>" % {
322 'color' : c,
323 'builder' : b,
324 'status' : s,
325 'tooltip' : cgi.escape(tooltip, True),
326 }
327 + link_post)
dfff8bd5 328 f.write("%s]</small></li>\n" % string.join(builders))
0cdc1fef 329
4e14a9bc
ER
330 def rpmbuild_opts(self):
331 """
332 return all rpmbuild options related to this build
333 """
5c4d43c0 334 rpmopts = self.bconds_string() + self.kernel_string() + self.target_string() + self.defines_string()
4e14a9bc 335 rpmdefs = \
266325ca
ER
336 "--define '_topdir %s' " % self._topdir + \
337 "--define '_specdir %{_topdir}' " \
4e14a9bc 338 "--define '_sourcedir %{_specdir}' " \
266325ca 339 "--define '_rpmdir %{_topdir}/RPMS' " \
b9b4ac17 340 "--define '_builddir %{_topdir}/BUILD' "
d6139a25 341 return rpmdefs + rpmopts
4e14a9bc 342
aab485ec 343 def kernel_string(self):
344 r = ""
345 if self.kernel != "":
346 r = " --define 'alt_kernel " + self.kernel + "'"
347 return r
348
be264f26
ER
349 def target_string(self):
350 if len(self.target) > 0:
351 return " --target " + ",".join(self.target)
352 else:
353 return ""
354
dfff8bd5
MM
355 def bconds_string(self):
356 r = ""
357 for b in self.bconds_with:
358 r = r + " --with " + b
359 for b in self.bconds_without:
360 r = r + " --without " + b
361 return r
0d52c382 362
d6139a25
ER
363 def defines_string(self):
364 r = ""
365 for key,value in self.defines.items():
366 r += " --define '%s %s'" % (key, value)
367 return r
368
369 def defines_xml(self):
370 r = ""
371 for key,value in self.defines.items():
372 r += "<define name='%s'>%s</define>\n" % (escape(key), escape(value))
373 return r
374
4e14a9bc
ER
375 def default_target(self, arch):
376 self.target.append("%s-pld-linux" % arch)
377
dfff8bd5
MM
378 def write_to(self, f):
379 f.write("""
30dbf6a3 380 <batch id='%s' depends-on='%s'>
cf1741af 381 <src-rpm>%s</src-rpm>
6953ce5d 382 <command flags="%s">%s</command>
cf1741af 383 <spec>%s</spec>
57b6e61d 384 <branch>%s</branch>
0d52c382 385 <info>%s</info>\n""" % (self.b_id,
dfff8bd5 386 string.join(map(lambda (b): b.b_id, self.depends_on)),
0d52c382 387 escape(self.src_rpm),
dfff8bd5 388 escape(' '.join(self.command_flags)), escape(self.command),
655fbfb2 389 escape(self.spec), escape(self.branch), escape(self.info)))
390 if self.kernel != "":
98ff6b42 391 f.write(" <kernel>%s</kernel>\n" % escape(self.kernel))
dfff8bd5
MM
392 for b in self.bconds_with:
393 f.write(" <with>%s</with>\n" % escape(b))
be264f26
ER
394 for b in self.target:
395 f.write(" <target>%s</target>\n" % escape(b))
dfff8bd5
MM
396 for b in self.bconds_without:
397 f.write(" <without>%s</without>\n" % escape(b))
d6139a25
ER
398 if self.defines:
399 f.write(" %s\n" % self.defines_xml())
dfff8bd5 400 for b in self.builders:
b4d7e94a
ER
401 if self.builders_status_buildtime.has_key(b):
402 t = self.builders_status_buildtime[b]
403 else:
404 t = "0"
405 f.write(" <builder status='%s' time='%s' buildtime='%s'>%s</builder>\n" % \
406 (escape(self.builders_status[b]), self.builders_status_time[b], t, escape(b)))
dfff8bd5 407 f.write(" </batch>\n")
0d52c382 408
dfff8bd5
MM
409 def log_line(self, l):
410 log.notice(l)
411 if self.logfile != None:
412 util.append_to(self.logfile, l)
cf1741af 413
dfff8bd5
MM
414 def expand_builders(batch, all_builders):
415 all = []
416 for bld in batch.builders:
417 res = []
418 for my_bld in all_builders:
419 if fnmatch.fnmatch(my_bld, bld):
420 res.append(my_bld)
421 if res != []:
422 all.extend(res)
423 else:
424 all.append(bld)
425 batch.builders = all
1089bec4 426
59ce7cd6 427class Notification:
dfff8bd5
MM
428 def __init__(self, e):
429 self.batches = []
430 self.kind = 'notification'
431 self.group_id = attr(e, "group-id")
432 self.builder = attr(e, "builder")
433 self.batches = {}
b4d7e94a 434 self.batches_buildtime = {}
dfff8bd5
MM
435 for c in e.childNodes:
436 if is_blank(c): continue
437 if c.nodeType != Element.ELEMENT_NODE:
438 log.panic("xml: evil notification child %d" % c.nodeType)
439 if c.nodeName == "batch":
440 id = attr(c, "id")
441 status = attr(c, "status")
b4d7e94a 442 buildtime = attr(c, "buildtime", "0")
2fec103c 443 if not status.startswith("OK") and not status.startswith("SKIP") and not status.startswith("UNSUPP") and not status.startswith("FAIL"):
b6c5ef3e 444 log.panic("xml notification: bad status: %s" % status)
dfff8bd5 445 self.batches[id] = status
b4d7e94a 446 self.batches_buildtime[id] = buildtime
dfff8bd5
MM
447 else:
448 log.panic("xml: evil notification child (%s)" % c.nodeName)
59ce7cd6 449
dfff8bd5
MM
450 def apply_to(self, q):
451 for r in q.requests:
452 if r.kind == "group":
453 for b in r.batches:
454 if self.batches.has_key(b.b_id):
455 b.builders_status[self.builder] = self.batches[b.b_id]
1432ffd5 456 b.builders_status_time[self.builder] = time.time()
b4d7e94a 457 b.builders_status_buildtime[self.builder] = "0" #self.batches_buildtime[b.b_id]
59ce7cd6 458
51a6a3a6 459def build_request(e):
dfff8bd5
MM
460 if e.nodeType != Element.ELEMENT_NODE:
461 log.panic("xml: evil request element")
462 if e.nodeName == "group":
463 return Group(e)
464 elif e.nodeName == "notification":
465 return Notification(e)
466 elif e.nodeName == "command":
467 # FIXME
468 return Command(e)
469 else:
9ef8e784 470 log.panic("xml: evil request [%s]" % e.nodeName)
51a6a3a6 471
94169186 472def parse_request(f):
5180bf1f 473 d = parseString(f)
dfff8bd5 474 return build_request(d.documentElement)
0d52c382 475
94169186 476def parse_requests(f):
5180bf1f 477 d = parseString(f)
dfff8bd5
MM
478 res = []
479 for r in d.documentElement.childNodes:
480 if is_blank(r): continue
481 res.append(build_request(r))
482 return res
This page took 0.501268 seconds and 4 git commands to generate.