X-Git-Url: https://git.tld-linux.org/?p=TLD.git;a=blobdiff_plain;f=pld-builder.new%2FPLD_Builder%2Frequest.py;fp=pld-builder.new%2FPLD_Builder%2Frequest.py;h=c87d644defef8159036158926c41931b4b03a312;hp=0000000000000000000000000000000000000000;hb=90809c8fec988489786ce00247d9a4150070748b;hpb=ab3934fab858112cd552359b18cb980ea07c310b diff --git a/pld-builder.new/PLD_Builder/request.py b/pld-builder.new/PLD_Builder/request.py new file mode 100644 index 0000000..c87d644 --- /dev/null +++ b/pld-builder.new/PLD_Builder/request.py @@ -0,0 +1,441 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +from xml.dom.minidom import * +import string +import time +import xml.sax.saxutils +import fnmatch +import os +import urllib +import cgi + +import util +import log +from acl import acl +from config import config + +__all__ = ['parse_request', 'parse_requests'] + +def text(e): + res = "" + for n in e.childNodes: + if n.nodeType != Element.TEXT_NODE: + log.panic("xml: text expected in <%s>, got %d" % (e.nodeName, n.nodeType)) + res += n.nodeValue + return res + +def attr(e, a, default = None): + try: + return e.attributes[a].value + except: + if default != None: + return default + raise + +def escape(s): + return xml.sax.saxutils.escape(s) + +# return timestamp with timezone information +# so we could parse it in javascript +def tzdate(t): + # as strftime %z is unofficial, and does not work, need to make it numeric ourselves +# date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t)) + date = time.strftime("%a %b %d %Y %H:%M:%S", time.localtime(t)) + # NOTE: the altzone is showing CURRENT timezone, not what the "t" reflects + # NOTE: when DST is off timezone gets it right, altzone not + if time.daylight: + tzoffset = time.altzone + else: + tzoffset = time.timezone + tz = '%+05d' % (-tzoffset / 3600 * 100) + return date + ' ' + tz + +def is_blank(e): + return e.nodeType == Element.TEXT_NODE and string.strip(e.nodeValue) == "" + +class Group: + def __init__(self, e): + self.batches = [] + self.kind = 'group' + self.id = attr(e, "id") + self.no = int(attr(e, "no")) + self.priority = 2 + self.time = time.time() + self.requester = "" + self.max_jobs = 0 + self.requester_email = "" + self.flags = string.split(attr(e, "flags", "")) + for c in e.childNodes: + if is_blank(c): continue + + if c.nodeType != Element.ELEMENT_NODE: + log.panic("xml: evil group child %d" % c.nodeType) + if c.nodeName == "batch": + self.batches.append(Batch(c)) + elif c.nodeName == "requester": + self.requester = text(c) + self.requester_email = attr(c, "email", "") + elif c.nodeName == "priority": + self.priority = int(text(c)) + elif c.nodeName == "time": + self.time = int(text(c)) + elif c.nodeName == "maxjobs": + self.max_jobs = int(text(c)) + else: + log.panic("xml: evil group child (%s)" % c.nodeName) + # note that we also check that group is sorted WRT deps + m = {} + for b in self.batches: + deps = [] + m[b.b_id] = b + for dep in b.depends_on: + if m.has_key(dep): + # avoid self-deps + if id(m[dep]) != id(b): + deps.append(m[dep]) + else: + log.panic("xml: dependency not found in group") + b.depends_on = deps + if self.requester_email == "" and self.requester != "": + self.requester_email = acl.user(self.requester).mail_to() + + def dump(self, f): + f.write("group: %d (id=%s pri=%d)\n" % (self.no, self.id, self.priority)) + f.write(" from: %s\n" % self.requester) + f.write(" flags: %s\n" % string.join(self.flags)) + f.write(" time: %s\n" % time.asctime(time.localtime(self.time))) + for b in self.batches: + b.dump(f) + f.write("\n") + + def dump_html(self, f): + f.write( + "
\n" + "%(no)d. %(time)s from %(requester)s " + "%(id)s, prio=%(priority)d, jobs=%(max_jobs)d, %(flags)s\n" + % { + 'no': self.no, + 'id': '%(id)s' % {'id': self.id}, + 'time': escape(tzdate(self.time)), + 'requester': escape(self.requester), + 'priority': self.priority, + 'max_jobs': self.max_jobs, + 'flags': string.join(self.flags) + }) + f.write("\n") + f.write("
\n") + + def write_to(self, f): + f.write(""" + + %s + + %d + %d\n""" % (self.id, self.no, string.join(self.flags), + escape(self.requester_email), escape(self.requester), + self.time, self.priority, self.max_jobs)) + for b in self.batches: + b.write_to(f) + f.write(" \n\n") + + def is_done(self): + ok = 1 + for b in self.batches: + if not b.is_done(): + ok = 0 + return ok + +class Batch: + def __init__(self, e): + self.bconds_with = [] + self.bconds_without = [] + self.builders = [] + self.builders_status = {} + self.builders_status_time = {} + self.builders_status_buildtime = {} + self.kernel = "" + self.target = [] + self.branch = "" + self.src_rpm = "" + self.info = "" + self.spec = "" + self.command = "" + self.command_flags = [] + self.skip = [] + self.gb_id = "" + self.b_id = attr(e, "id") + self.depends_on = string.split(attr(e, "depends-on")) + self.upgraded = True + for c in e.childNodes: + if is_blank(c): continue + + if c.nodeType != Element.ELEMENT_NODE: + log.panic("xml: evil batch child %d" % c.nodeType) + if c.nodeName == "src-rpm": + self.src_rpm = text(c) + elif c.nodeName == "spec": + # normalize specname, specname is used as buildlog and we don't + # want to be exposed to directory traversal attacks + self.spec = text(c).split('/')[-1] + elif c.nodeName == "command": + self.spec = "COMMAND" + self.command = text(c).strip() + self.command_flags = string.split(attr(c, "flags", "")) + elif c.nodeName == "info": + self.info = text(c) + elif c.nodeName == "kernel": + self.kernel = text(c) + elif c.nodeName == "target": + self.target.append(text(c)) + elif c.nodeName == "skip": + self.skip.append(text(c)) + elif c.nodeName == "branch": + self.branch = text(c) + elif c.nodeName == "builder": + key = text(c) + self.builders.append(key) + self.builders_status[key] = attr(c, "status", "?") + self.builders_status_time[key] = attr(c, "time", "0") + self.builders_status_buildtime[key] = "0" #attr(c, "buildtime", "0") + elif c.nodeName == "with": + self.bconds_with.append(text(c)) + elif c.nodeName == "without": + self.bconds_without.append(text(c)) + else: + log.panic("xml: evil batch child (%s)" % c.nodeName) + + def is_done(self): + ok = 1 + for b in self.builders: + s = self.builders_status[b] + if not s.startswith("OK") and not s.startswith("SKIP") and not s.startswith("UNSUPP") and not s.startswith("FAIL"): + ok = 0 + return ok + + def dump(self, f): + f.write(" batch: %s/%s\n" % (self.src_rpm, self.spec)) + f.write(" info: %s\n" % self.info) + f.write(" kernel: %s\n" % self.kernel) + f.write(" target: %s\n" % self.target_string()) + f.write(" branch: %s\n" % self.branch) + f.write(" bconds: %s\n" % self.bconds_string()) + builders = [] + for b in self.builders: + builders.append("%s:%s" % (b, self.builders_status[b])) + f.write(" builders: %s\n" % string.join(builders)) + + def is_command(self): + return self.command != "" + + def dump_html(self, f, rid): + f.write("
  • \n") + if self.is_command(): + desc = "SH:
    %s
    flags: [%s]" % (self.command, ' '.join(self.command_flags)) + else: + package_url = "http://cvs.pld-linux.org/packages/%(package)s/%(spec)s?only_with_tag=%(branch)s" % { + 'spec': self.spec, + 'branch': self.branch, + 'package': self.spec[:-5], + } + desc = "%(src_rpm)s (%(spec)s -r %(branch)s%(bconds)s)" % { + 'src_rpm': self.src_rpm, + 'spec': self.spec, + 'branch': self.branch, + 'bconds': self.bconds_string() + self.kernel_string() + self.target_string(), + 'package_url': package_url, + } + f.write("%s [" % desc) + builders = [] + for b in self.builders: + s = self.builders_status[b] + if s.startswith("OK"): + c = "green" + elif s.startswith("FAIL"): + c = "red" + elif s.startswith("SKIP"): + c = "blue" + elif s.startswith("UNSUPP"): + c = "fuchsia" + else: + c = "black" + link_pre = "" + link_post = "" + if (s.startswith("OK") or s.startswith("SKIP") or s.startswith("UNSUPP") or s.startswith("FAIL")) and len(self.spec) > 5: + if self.is_command(): + bl_name = "command" + else: + bl_name = self.spec[:len(self.spec)-5] + lin_ar = b.replace('noauto-','') + path = "/%s/%s/%s,%s.bz2" % (lin_ar.replace('-','/'), s, bl_name, rid) + is_ok = 0 + if s.startswith("OK"): + is_ok = 1 + bld = lin_ar.split('-') + tree_name = '-'.join(bld[:-1]) + tree_arch = '-'.join(bld[-1:]) + link_pre = "" \ + % (urllib.quote(tree_name), urllib.quote(tree_arch), is_ok, urllib.quote(bl_name), urllib.quote(rid)) + link_post = "" + + def ftime(s): + t = float(s) + if t > 0: + return time.asctime(time.localtime(t)) + else: + return 'N/A' + + tooltip = "last update: %(time)s\nbuild time: %(buildtime)s" % { + 'time' : ftime(self.builders_status_time[b]), + 'buildtime' : ftime(self.builders_status_buildtime[b]), + } + builders.append(link_pre + + "%(builder)s:%(status)s" % { + 'color' : c, + 'builder' : b, + 'status' : s, + 'tooltip' : cgi.escape(tooltip, True), + } + + link_post) + f.write("%s]
  • \n" % string.join(builders)) + + def rpmbuild_opts(self): + """ + return all rpmbuild options related to this build + """ + bconds = self.bconds_string() + self.kernel_string() + self.target_string() + rpmdefs = \ + "--define '_topdir %(echo $HOME/rpm)' " \ + "--define '_specdir %{_topdir}/packages/%{name}' " \ + "--define '_sourcedir %{_specdir}' " \ + "--define '_builddir %{_topdir}/BUILD/%{name}' " + return rpmdefs + bconds + + def kernel_string(self): + r = "" + if self.kernel != "": + r = " --define 'alt_kernel " + self.kernel + "'" + return r + + def target_string(self): + if len(self.target) > 0: + return " --target " + ",".join(self.target) + else: + return "" + + def bconds_string(self): + r = "" + for b in self.bconds_with: + r = r + " --with " + b + for b in self.bconds_without: + r = r + " --without " + b + return r + + def default_target(self, arch): + self.target.append("%s-pld-linux" % arch) + + def write_to(self, f): + f.write(""" + + %s + %s + %s + %s + %s\n""" % (self.b_id, + string.join(map(lambda (b): b.b_id, self.depends_on)), + escape(self.src_rpm), + escape(' '.join(self.command_flags)), escape(self.command), + escape(self.spec), escape(self.branch), escape(self.info))) + if self.kernel != "": + f.write(" %s\n" % escape(self.kernel)) + for b in self.bconds_with: + f.write(" %s\n" % escape(b)) + for b in self.target: + f.write(" %s\n" % escape(b)) + for b in self.bconds_without: + f.write(" %s\n" % escape(b)) + for b in self.builders: + if self.builders_status_buildtime.has_key(b): + t = self.builders_status_buildtime[b] + else: + t = "0" + f.write(" %s\n" % \ + (escape(self.builders_status[b]), self.builders_status_time[b], t, escape(b))) + f.write(" \n") + + def log_line(self, l): + log.notice(l) + if self.logfile != None: + util.append_to(self.logfile, l) + + def expand_builders(batch, all_builders): + all = [] + for bld in batch.builders: + res = [] + for my_bld in all_builders: + if fnmatch.fnmatch(my_bld, bld): + res.append(my_bld) + if res != []: + all.extend(res) + else: + all.append(bld) + batch.builders = all + +class Notification: + def __init__(self, e): + self.batches = [] + self.kind = 'notification' + self.group_id = attr(e, "group-id") + self.builder = attr(e, "builder") + self.batches = {} + self.batches_buildtime = {} + for c in e.childNodes: + if is_blank(c): continue + if c.nodeType != Element.ELEMENT_NODE: + log.panic("xml: evil notification child %d" % c.nodeType) + if c.nodeName == "batch": + id = attr(c, "id") + status = attr(c, "status") + buildtime = attr(c, "buildtime", "0") + if not status.startswith("OK") and not status.startswith("SKIP") and not status.startswith("UNSUPP") and not status.startswith("FAIL"): + log.panic("xml notification: bad status: %s" % status) + self.batches[id] = status + self.batches_buildtime[id] = buildtime + else: + log.panic("xml: evil notification child (%s)" % c.nodeName) + + def apply_to(self, q): + for r in q.requests: + if r.kind == "group": + for b in r.batches: + if self.batches.has_key(b.b_id): + b.builders_status[self.builder] = self.batches[b.b_id] + b.builders_status_time[self.builder] = time.time() + b.builders_status_buildtime[self.builder] = "0" #self.batches_buildtime[b.b_id] + +def build_request(e): + if e.nodeType != Element.ELEMENT_NODE: + log.panic("xml: evil request element") + if e.nodeName == "group": + return Group(e) + elif e.nodeName == "notification": + return Notification(e) + elif e.nodeName == "command": + # FIXME + return Command(e) + else: + log.panic("xml: evil request [%s]" % e.nodeName) + +def parse_request(f): + d = parseString(f) + return build_request(d.documentElement) + +def parse_requests(f): + d = parseString(f) + res = [] + for r in d.documentElement.childNodes: + if is_blank(r): continue + res.append(build_request(r)) + return res