1 # vi: encoding=utf-8 ts=8 sts=4 sw=4 et
3 from xml.dom.minidom import *
6 import xml.sax.saxutils
15 from config import config
16 from subprocess import call
18 __all__ = ['parse_request', 'parse_requests']
22 for n in e.childNodes:
23 if n.nodeType != Element.TEXT_NODE:
24 log.panic("xml: text expected in <%s>, got %d" % (e.nodeName, n.nodeType))
28 def attr(e, a, default = None):
30 return e.attributes[a].value
37 return xml.sax.saxutils.escape(s)
40 return e.nodeType == Element.TEXT_NODE and string.strip(e.nodeValue) == ""
43 def __init__(self, e):
46 self.id = attr(e, "id")
47 self.no = int(attr(e, "no"))
49 self.time = time.time()
52 self.requester_email = ""
53 self.flags = string.split(attr(e, "flags", ""))
54 for c in e.childNodes:
55 if is_blank(c): continue
57 if c.nodeType != Element.ELEMENT_NODE:
58 log.panic("xml: evil group child %d" % c.nodeType)
59 if c.nodeName == "batch":
60 self.batches.append(Batch(c))
61 elif c.nodeName == "requester":
62 self.requester = text(c)
63 self.requester_email = attr(c, "email", "")
64 elif c.nodeName == "priority":
65 self.priority = int(text(c))
66 elif c.nodeName == "time":
67 self.time = int(text(c))
68 elif c.nodeName == "maxjobs":
69 self.max_jobs = int(text(c))
71 log.panic("xml: evil group child (%s)" % c.nodeName)
72 # note that we also check that group is sorted WRT deps
74 for b in self.batches:
77 for dep in b.depends_on:
80 if id(m[dep]) != id(b):
83 log.panic("xml: dependency not found in group")
85 if self.requester_email == "" and self.requester != "":
86 self.requester_email = acl.user(self.requester).mail_to()
89 f.write("group: %d (id=%s pri=%d)\n" % (self.no, self.id, self.priority))
90 f.write(" from: %s\n" % self.requester)
91 f.write(" flags: %s\n" % string.join(self.flags))
92 f.write(" time: %s\n" % time.asctime(time.localtime(self.time)))
93 for b in self.batches:
97 def dump_html(self, f):
99 "<div id=\"%(no)d\" class=\"%(flags)s\">\n"
100 "<a href=\"#%(no)d\">%(no)d</a>. <span id=\"tz\">%(time)s</span> from <b>%(requester)s</b> "
101 "<small>%(id)s, prio=%(priority)d, jobs=%(max_jobs)d, %(flags)s</small>\n"
104 'id': '<a href="srpms/%(id)s">%(id)s</a>' % {'id': self.id},
105 'time': escape(time.strftime("%a %b %d %Y %H:%M:%S %z", time.localtime(self.time))),
106 'requester': escape(self.requester),
107 'priority': self.priority,
108 'max_jobs': self.max_jobs,
109 'flags': string.join(self.flags)
112 for b in self.batches:
113 b.dump_html(f, self.id)
117 def write_to(self, f):
119 <group id="%s" no="%d" flags="%s">
120 <requester email='%s'>%s</requester>
122 <priority>%d</priority>
123 <maxjobs>%d</maxjobs>\n""" % (self.id, self.no, string.join(self.flags),
124 escape(self.requester_email), escape(self.requester),
125 self.time, self.priority, self.max_jobs))
126 for b in self.batches:
128 f.write(" </group>\n\n")
132 for b in self.batches:
138 def __init__(self, e):
139 self.bconds_with = []
140 self.bconds_without = []
142 self.builders_status = {}
143 self.builders_status_time = {}
144 self.builders_status_buildtime = {}
152 self.command_flags = []
155 self.b_id = attr(e, "id")
156 self.depends_on = string.split(attr(e, "depends-on"))
158 for c in e.childNodes:
159 if is_blank(c): continue
161 if c.nodeType != Element.ELEMENT_NODE:
162 log.panic("xml: evil batch child %d" % c.nodeType)
163 if c.nodeName == "src-rpm":
164 self.src_rpm = text(c)
165 elif c.nodeName == "spec":
166 # normalize specname, specname is used as buildlog and we don't
167 # want to be exposed to directory traversal attacks
168 self.spec = text(c).split('/')[-1]
169 elif c.nodeName == "command":
170 self.spec = "COMMAND"
171 self.command = text(c).strip()
172 self.command_flags = string.split(attr(c, "flags", ""))
173 elif c.nodeName == "info":
175 elif c.nodeName == "kernel":
176 self.kernel = text(c)
177 elif c.nodeName == "target":
178 self.target.append(text(c))
179 elif c.nodeName == "skip":
180 self.skip.append(text(c))
181 elif c.nodeName == "branch":
182 self.branch = text(c)
183 elif c.nodeName == "builder":
185 self.builders.append(key)
186 self.builders_status[key] = attr(c, "status", "?")
187 self.builders_status_time[key] = attr(c, "time", "0")
188 self.builders_status_buildtime[key] = "0" #attr(c, "buildtime", "0")
189 elif c.nodeName == "with":
190 self.bconds_with.append(text(c))
191 elif c.nodeName == "without":
192 self.bconds_without.append(text(c))
194 log.panic("xml: evil batch child (%s)" % c.nodeName)
198 for b in self.builders:
199 s = self.builders_status[b]
200 if not s.startswith("OK") and not s.startswith("SKIP") and not s.startswith("UNSUPP") and not s.startswith("FAIL"):
205 f.write(" batch: %s/%s\n" % (self.src_rpm, self.spec))
206 f.write(" info: %s\n" % self.info)
207 f.write(" kernel: %s\n" % self.kernel)
208 f.write(" target: %s\n" % self.target_string())
209 f.write(" branch: %s\n" % self.branch)
210 f.write(" bconds: %s\n" % self.bconds_string())
212 for b in self.builders:
213 builders.append("%s:%s" % (b, self.builders_status[b]))
214 f.write(" builders: %s\n" % string.join(builders))
216 def is_command(self):
217 return self.command != ""
219 def dump_html(self, f, rid):
221 if self.is_command():
222 desc = "SH: <pre>%s</pre> flags: [%s]" % (self.command, ' '.join(self.command_flags))
224 cmd = "/usr/bin/git ls-remote --heads git://git.tld-linux.org/packages/%s 1>/dev/null 2>&1" % (self.spec[:-5])
225 r = call(cmd, shell=True)
230 package_url = "http://git.%(dist)s-linux.org/?p=packages/%(package)s.git;a=blob;f=%(spec)s;hb=%(branch)s" % {
233 'branch': self.branch,
234 'package': self.spec[:-5],
236 desc = "%(src_rpm)s (<a href=\"%(package_url)s\">%(spec)s -r %(branch)s</a>%(bconds)s)" % {
237 'src_rpm': self.src_rpm,
239 'branch': self.branch,
240 'bconds': self.bconds_string() + self.kernel_string() + self.target_string(),
241 'package_url': package_url,
243 f.write("%s <small>[" % desc)
245 for b in self.builders:
246 s = self.builders_status[b]
247 if s.startswith("OK"):
249 elif s.startswith("FAIL"):
251 elif s.startswith("SKIP"):
253 elif s.startswith("UNSUPP"):
259 if (s.startswith("OK") or s.startswith("SKIP") or s.startswith("UNSUPP") or s.startswith("FAIL")) and len(self.spec) > 5:
260 if self.is_command():
263 bl_name = self.spec[:len(self.spec)-5]
264 lin_ar = b.replace('noauto-','')
265 path = "/%s/%s/%s,%s.bz2" % (lin_ar.replace('-','/'), s, bl_name, rid)
267 if s.startswith("OK"):
269 bld = lin_ar.split('-')
270 tree_name = '-'.join(bld[:-1])
271 tree_arch = '-'.join(bld[-1:])
272 link_pre = "<a href=\"http://buildlogs.tld-linux.org/index.php?dist=%s&arch=%s&name=%s&id=%s&action=download\">" \
273 % (urllib.quote(tree_name), urllib.quote(tree_arch), urllib.quote(bl_name), urllib.quote(rid))
279 return time.asctime(time.localtime(t))
283 tooltip = "last update: %(time)s\nbuild time: %(buildtime)s" % {
284 'time' : ftime(self.builders_status_time[b]),
285 'buildtime' : ftime(self.builders_status_buildtime[b]),
287 builders.append(link_pre +
288 "<font color='%(color)s'><b title=\"%(tooltip)s\">%(builder)s:%(status)s</b></font>" % {
292 'tooltip' : cgi.escape(tooltip, True),
295 f.write("%s]</small></li>\n" % string.join(builders))
297 def rpmbuild_opts(self):
299 return all rpmbuild options related to this build
301 bconds = self.bconds_string() + self.kernel_string() + self.target_string()
303 "--define '_topdir %(echo $HOME/rpm)' " \
304 "--define '_specdir %{_topdir}/packages/%{name}' " \
305 "--define '_sourcedir %{_specdir}' " \
306 "--define '_builddir %{_topdir}/BUILD/%{name}' "
307 return rpmdefs + bconds
309 def kernel_string(self):
311 if self.kernel != "":
312 r = " --define 'alt_kernel " + self.kernel + "'"
315 def target_string(self):
316 if len(self.target) > 0:
317 return " --target " + ",".join(self.target)
321 def bconds_string(self):
323 for b in self.bconds_with:
324 r = r + " --with " + b
325 for b in self.bconds_without:
326 r = r + " --without " + b
329 def default_target(self, arch):
330 self.target.append("%s-tld-linux" % arch)
332 def write_to(self, f):
334 <batch id='%s' depends-on='%s'>
335 <src-rpm>%s</src-rpm>
336 <command flags="%s">%s</command>
339 <info>%s</info>\n""" % (self.b_id,
340 string.join(map(lambda (b): b.b_id, self.depends_on)),
341 escape(self.src_rpm),
342 escape(' '.join(self.command_flags)), escape(self.command),
343 escape(self.spec), escape(self.branch), escape(self.info)))
344 if self.kernel != "":
345 f.write(" <kernel>%s</kernel>\n" % escape(self.kernel))
346 for b in self.bconds_with:
347 f.write(" <with>%s</with>\n" % escape(b))
348 for b in self.target:
349 f.write(" <target>%s</target>\n" % escape(b))
350 for b in self.bconds_without:
351 f.write(" <without>%s</without>\n" % escape(b))
352 for b in self.builders:
353 if self.builders_status_buildtime.has_key(b):
354 t = self.builders_status_buildtime[b]
357 f.write(" <builder status='%s' time='%s' buildtime='%s'>%s</builder>\n" % \
358 (escape(self.builders_status[b]), self.builders_status_time[b], t, escape(b)))
359 f.write(" </batch>\n")
361 def log_line(self, l):
363 if self.logfile != None:
364 util.append_to(self.logfile, l)
366 def expand_builders(batch, all_builders):
368 for bld in batch.builders:
370 for my_bld in all_builders:
371 if fnmatch.fnmatch(my_bld, bld):
380 def __init__(self, e):
382 self.kind = 'notification'
383 self.group_id = attr(e, "group-id")
384 self.builder = attr(e, "builder")
386 self.batches_buildtime = {}
387 for c in e.childNodes:
388 if is_blank(c): continue
389 if c.nodeType != Element.ELEMENT_NODE:
390 log.panic("xml: evil notification child %d" % c.nodeType)
391 if c.nodeName == "batch":
393 status = attr(c, "status")
394 buildtime = attr(c, "buildtime", "0")
395 if not status.startswith("OK") and not status.startswith("SKIP") and not status.startswith("UNSUPP") and not status.startswith("FAIL"):
396 log.panic("xml notification: bad status: %s" % status)
397 self.batches[id] = status
398 self.batches_buildtime[id] = buildtime
400 log.panic("xml: evil notification child (%s)" % c.nodeName)
402 def apply_to(self, q):
404 if r.kind == "group":
406 if self.batches.has_key(b.b_id):
407 b.builders_status[self.builder] = self.batches[b.b_id]
408 b.builders_status_time[self.builder] = time.time()
409 b.builders_status_buildtime[self.builder] = "0" #self.batches_buildtime[b.b_id]
411 def build_request(e):
412 if e.nodeType != Element.ELEMENT_NODE:
413 log.panic("xml: evil request element")
414 if e.nodeName == "group":
416 elif e.nodeName == "notification":
417 return Notification(e)
418 elif e.nodeName == "command":
422 log.panic("xml: evil request [%s]" % e.nodeName)
424 def parse_request(f):
426 return build_request(d.documentElement)
428 def parse_requests(f):
431 for r in d.documentElement.childNodes:
432 if is_blank(r): continue
433 res.append(build_request(r))