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