Package buildbot :: Package status :: Package web :: Module builder
[frames] | no frames]

Source Code for Module buildbot.status.web.builder

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16   
 17  from twisted.web import html 
 18  import urllib, time 
 19  from twisted.python import log 
 20  from twisted.internet import defer 
 21  from buildbot import interfaces 
 22  from buildbot.status.web.base import HtmlResource, BuildLineMixin, \ 
 23      path_to_build, path_to_slave, path_to_builder, path_to_change, \ 
 24      path_to_root, ICurrentBox, build_get_class, \ 
 25      map_branches, path_to_authzfail, ActionResource, \ 
 26      getRequestCharset 
 27  from buildbot.schedulers.forcesched import ForceScheduler 
 28  from buildbot.schedulers.forcesched import InheritBuildParameter, NestedParameter 
 29  from buildbot.schedulers.forcesched import ValidationError 
 30  from buildbot.status.web.build import BuildsResource, StatusResourceBuild 
 31  from buildbot import util 
 32  import collections 
33 34 -class ForceAllBuildsActionResource(ActionResource):
35
36 - def __init__(self, status, selectedOrAll):
37 self.status = status 38 self.selectedOrAll = selectedOrAll 39 self.action = "forceAllBuilds"
40 41 @defer.inlineCallbacks
42 - def performAction(self, req):
43 authz = self.getAuthz(req) 44 res = yield authz.actionAllowed('forceAllBuilds', req) 45 46 if not res: 47 defer.returnValue(path_to_authzfail(req)) 48 return 49 50 builders = None 51 if self.selectedOrAll == 'all': 52 builders = self.status.getBuilderNames() 53 elif self.selectedOrAll == 'selected': 54 builders = [b for b in req.args.get("selected", []) if b] 55 56 for bname in builders: 57 builder_status = self.status.getBuilder(bname) 58 ar = ForceBuildActionResource(builder_status) 59 d = ar.performAction(req) 60 d.addErrback(log.err, "(ignored) while trying to force build") 61 # back to the welcome page 62 defer.returnValue(path_to_root(req))
63
64 -class StopAllBuildsActionResource(ActionResource):
65
66 - def __init__(self, status, selectedOrAll):
67 self.status = status 68 self.selectedOrAll = selectedOrAll 69 self.action = "stopAllBuilds"
70 71 @defer.inlineCallbacks
72 - def performAction(self, req):
73 authz = self.getAuthz(req) 74 res = yield authz.actionAllowed('stopAllBuilds', req) 75 if not res: 76 defer.returnValue(path_to_authzfail(req)) 77 return 78 79 builders = None 80 if self.selectedOrAll == 'all': 81 builders = self.status.getBuilderNames() 82 elif self.selectedOrAll == 'selected': 83 builders = [b for b in req.args.get("selected", []) if b] 84 85 for bname in builders: 86 builder_status = self.status.getBuilder(bname) 87 (state, current_builds) = builder_status.getState() 88 if state != "building": 89 continue 90 for b in current_builds: 91 build_status = builder_status.getBuild(b.number) 92 if not build_status: 93 continue 94 build = StatusResourceBuild(build_status) 95 build.stop(req, auth_ok=True) 96 97 # go back to the welcome page 98 defer.returnValue(path_to_root(req))
99
100 -class PingBuilderActionResource(ActionResource):
101
102 - def __init__(self, builder_status):
103 self.builder_status = builder_status 104 self.action = "pingBuilder"
105 106 @defer.inlineCallbacks
107 - def performAction(self, req):
108 log.msg("web ping of builder '%s'" % self.builder_status.getName()) 109 res = yield self.getAuthz(req).actionAllowed('pingBuilder', req, 110 self.builder_status) 111 if not res: 112 log.msg("..but not authorized") 113 defer.returnValue(path_to_authzfail(req)) 114 return 115 116 c = interfaces.IControl(self.getBuildmaster(req)) 117 bc = c.getBuilder(self.builder_status.getName()) 118 bc.ping() 119 # send the user back to the builder page 120 defer.returnValue(path_to_builder(req, self.builder_status))
121
122 -class ForceBuildActionResource(ActionResource):
123
124 - def __init__(self, builder_status):
125 self.builder_status = builder_status 126 self.action = "forceBuild"
127 128 @defer.inlineCallbacks
129 - def performAction(self, req):
130 # check if this is allowed 131 res = yield self.getAuthz(req).actionAllowed(self.action, req, 132 self.builder_status) 133 if not res: 134 log.msg("..but not authorized") 135 defer.returnValue(path_to_authzfail(req)) 136 return 137 138 master = self.getBuildmaster(req) 139 owner = self.getAuthz(req).getUsernameFull(req) 140 schedulername = req.args.get("forcescheduler", ["<unknown>"])[0] 141 if schedulername == "<unknown>": 142 defer.returnValue((path_to_builder(req, self.builder_status), 143 "forcescheduler arg not found")) 144 return 145 146 args = req.args.copy() 147 148 # decode all of the args 149 encoding = getRequestCharset(req) 150 for name, argl in args.iteritems(): 151 args[name] = [ arg.decode(encoding) for arg in argl ] 152 153 # damn html's ungeneric checkbox implementation... 154 for cb in args.get("checkbox", []): 155 args[cb] = True 156 157 builder_name = self.builder_status.getName() 158 159 for sch in master.allSchedulers(): 160 if schedulername == sch.name: 161 try: 162 yield sch.force(owner, builder_name, **args) 163 msg = "" 164 except ValidationError, e: 165 msg = html.escape(e.message.encode('ascii','ignore')) 166 break 167 168 # send the user back to the builder page 169 defer.returnValue((path_to_builder(req, self.builder_status), msg))
170
171 -def buildForceContextForField(req, default_props, sch, field, master, buildername):
172 pname = "%s.%s"%(sch.name, field.fullName) 173 174 default = field.default 175 if isinstance(field, InheritBuildParameter): 176 # yes, I know, its bad to overwrite the parameter attribute, 177 # but I dont have any other simple way of doing this atm. 178 field.choices = field.compatible_builds(master.status, buildername) 179 if field.choices: 180 default = field.choices[0] 181 182 default = req.args.get(pname, [default])[0] 183 if "bool" in field.type: 184 default_props[pname] = "checked" if default else "" 185 else: 186 # filter out unicode chars, and html stuff 187 if isinstance(default, unicode): 188 default = html.escape(default.encode('utf-8','ignore')) 189 default_props[pname] = default 190 191 if isinstance(field, NestedParameter): 192 for subfield in field.fields: 193 buildForceContextForField(req, default_props, sch, subfield, master, buildername)
194
195 -def buildForceContext(cxt, req, master, buildername=None):
196 force_schedulers = {} 197 default_props = collections.defaultdict(str) 198 for sch in master.allSchedulers(): 199 if isinstance(sch, ForceScheduler) and (buildername is None or(buildername in sch.builderNames)): 200 force_schedulers[sch.name] = sch 201 for field in sch.all_fields: 202 buildForceContextForField(req, default_props, sch, field, master, buildername) 203 204 cxt['force_schedulers'] = force_schedulers 205 cxt['default_props'] = default_props
206
207 # /builders/$builder 208 -class StatusResourceBuilder(HtmlResource, BuildLineMixin):
209 addSlash = True 210
211 - def __init__(self, builder_status):
212 HtmlResource.__init__(self) 213 self.builder_status = builder_status
214
215 - def getPageTitle(self, request):
216 return "Buildbot: %s" % self.builder_status.getName()
217
218 - def builder(self, build, req):
219 b = {} 220 221 b['num'] = build.getNumber() 222 b['link'] = path_to_build(req, build) 223 224 when = build.getETA() 225 if when is not None: 226 b['when'] = util.formatInterval(when) 227 b['when_time'] = time.strftime("%H:%M:%S", 228 time.localtime(time.time() + when)) 229 230 step = build.getCurrentStep() 231 # TODO: is this necessarily the case? 232 if not step: 233 b['current_step'] = "[waiting for Lock]" 234 else: 235 if step.isWaitingForLocks(): 236 b['current_step'] = "%s [waiting for Lock]" % step.getName() 237 else: 238 b['current_step'] = step.getName() 239 240 b['stop_url'] = path_to_build(req, build) + '/stop' 241 242 return b
243 244 @defer.inlineCallbacks
245 - def content(self, req, cxt):
246 b = self.builder_status 247 248 cxt['name'] = b.getName() 249 req.setHeader('Cache-Control', 'no-cache') 250 slaves = b.getSlaves() 251 connected_slaves = [s for s in slaves if s.isConnected()] 252 253 cxt['current'] = [self.builder(x, req) for x in b.getCurrentBuilds()] 254 255 cxt['pending'] = [] 256 statuses = yield b.getPendingBuildRequestStatuses() 257 for pb in statuses: 258 changes = [] 259 260 source = yield pb.getSourceStamp() 261 submitTime = yield pb.getSubmitTime() 262 bsid = yield pb.getBsid() 263 264 properties = yield \ 265 pb.master.db.buildsets.getBuildsetProperties(bsid) 266 267 if source.changes: 268 for c in source.changes: 269 changes.append({ 'url' : path_to_change(req, c), 270 'who' : c.who, 271 'revision' : c.revision, 272 'repo' : c.repository }) 273 274 cxt['pending'].append({ 275 'when': time.strftime("%b %d %H:%M:%S", 276 time.localtime(submitTime)), 277 'delay': util.formatInterval(util.now() - submitTime), 278 'id': pb.brid, 279 'changes' : changes, 280 'num_changes' : len(changes), 281 'properties' : properties, 282 }) 283 284 numbuilds = int(req.args.get('numbuilds', ['5'])[0]) 285 recent = cxt['recent'] = [] 286 for build in b.generateFinishedBuilds(num_builds=int(numbuilds)): 287 recent.append(self.get_line_values(req, build, False)) 288 289 sl = cxt['slaves'] = [] 290 connected_slaves = 0 291 for slave in slaves: 292 s = {} 293 sl.append(s) 294 s['link'] = path_to_slave(req, slave) 295 s['name'] = slave.getName() 296 c = s['connected'] = slave.isConnected() 297 if c: 298 s['admin'] = unicode(slave.getAdmin() or '', 'utf-8') 299 connected_slaves += 1 300 cxt['connected_slaves'] = connected_slaves 301 302 cxt['authz'] = self.getAuthz(req) 303 cxt['builder_url'] = path_to_builder(req, b) 304 buildForceContext(cxt, req, self.getBuildmaster(req), b.getName()) 305 template = req.site.buildbot_service.templates.get_template("builder.html") 306 defer.returnValue(template.render(**cxt))
307
308 - def ping(self, req):
309 return PingBuilderActionResource(self.builder_status)
310
311 - def getChild(self, path, req):
312 if path == "force": 313 return ForceBuildActionResource(self.builder_status) 314 if path == "ping": 315 return self.ping(req) 316 if path == "cancelbuild": 317 return CancelChangeResource(self.builder_status) 318 if path == "stopchange": 319 return StopChangeResource(self.builder_status) 320 if path == "builds": 321 return BuildsResource(self.builder_status) 322 323 return HtmlResource.getChild(self, path, req)
324
325 -class CancelChangeResource(ActionResource):
326
327 - def __init__(self, builder_status):
328 ActionResource.__init__(self) 329 self.builder_status = builder_status
330 331 @defer.inlineCallbacks
332 - def performAction(self, req):
333 try: 334 request_id = req.args.get("id", [None])[0] 335 if request_id == "all": 336 cancel_all = True 337 else: 338 cancel_all = False 339 request_id = int(request_id) 340 except: 341 request_id = None 342 343 authz = self.getAuthz(req) 344 if request_id: 345 c = interfaces.IControl(self.getBuildmaster(req)) 346 builder_control = c.getBuilder(self.builder_status.getName()) 347 348 brcontrols = yield builder_control.getPendingBuildRequestControls() 349 350 for build_req in brcontrols: 351 if cancel_all or (build_req.brid == request_id): 352 log.msg("Cancelling %s" % build_req) 353 res = yield authz.actionAllowed('cancelPendingBuild', req, 354 build_req) 355 if res: 356 build_req.cancel() 357 else: 358 defer.returnValue(path_to_authzfail(req)) 359 return 360 if not cancel_all: 361 break 362 363 defer.returnValue(path_to_builder(req, self.builder_status))
364
365 -class StopChangeMixin(object):
366 367 @defer.inlineCallbacks
368 - def stopChangeForBuilder(self, req, builder_status, auth_ok=False):
369 try: 370 request_change = req.args.get("change", [None])[0] 371 request_change = int(request_change) 372 except: 373 request_change = None 374 375 authz = self.getAuthz(req) 376 if request_change: 377 c = interfaces.IControl(self.getBuildmaster(req)) 378 builder_control = c.getBuilder(builder_status.getName()) 379 380 brcontrols = yield builder_control.getPendingBuildRequestControls() 381 build_controls = dict((x.brid, x) for x in brcontrols) 382 383 build_req_statuses = yield \ 384 builder_status.getPendingBuildRequestStatuses() 385 386 for build_req in build_req_statuses: 387 ss = yield build_req.getSourceStamp() 388 389 if not ss.changes: 390 continue 391 392 for change in ss.changes: 393 if change.number == request_change: 394 control = build_controls[build_req.brid] 395 log.msg("Cancelling %s" % control) 396 res = yield authz.actionAllowed('stopChange', req, control) 397 if (auth_ok or res): 398 control.cancel() 399 else: 400 defer.returnValue(False) 401 return 402 403 defer.returnValue(True)
404
405 406 -class StopChangeResource(StopChangeMixin, ActionResource):
407
408 - def __init__(self, builder_status):
409 ActionResource.__init__(self) 410 self.builder_status = builder_status
411 412 @defer.inlineCallbacks
413 - def performAction(self, req):
414 """Cancel all pending builds that include a given numbered change.""" 415 success = yield self.stopChangeForBuilder(req, self.builder_status) 416 417 if not success: 418 defer.returnValue(path_to_authzfail(req)) 419 else: 420 defer.returnValue(path_to_builder(req, self.builder_status))
421
422 423 -class StopChangeAllResource(StopChangeMixin, ActionResource):
424
425 - def __init__(self, status):
426 ActionResource.__init__(self) 427 self.status = status
428 429 @defer.inlineCallbacks
430 - def performAction(self, req):
431 """Cancel all pending builds that include a given numbered change.""" 432 authz = self.getAuthz(req) 433 res = yield authz.actionAllowed('stopChange', req) 434 if not res: 435 defer.returnValue(path_to_authzfail(req)) 436 return 437 438 for bname in self.status.getBuilderNames(): 439 builder_status = self.status.getBuilder(bname) 440 res = yield self.stopChangeForBuilder(req, builder_status, auth_ok=True) 441 if not res: 442 defer.returnValue(path_to_authzfail(req)) 443 return 444 445 defer.returnValue(path_to_root(req))
446
447 448 # /builders/_all 449 -class StatusResourceAllBuilders(HtmlResource, BuildLineMixin):
450
451 - def __init__(self, status):
452 HtmlResource.__init__(self) 453 self.status = status
454
455 - def getChild(self, path, req):
456 if path == "forceall": 457 return self.forceall(req) 458 if path == "stopall": 459 return self.stopall(req) 460 if path == "stopchangeall": 461 return StopChangeAllResource(self.status) 462 463 return HtmlResource.getChild(self, path, req)
464
465 - def forceall(self, req):
466 return ForceAllBuildsActionResource(self.status, 'all')
467
468 - def stopall(self, req):
469 return StopAllBuildsActionResource(self.status, 'all')
470
471 # /builders/_selected 472 -class StatusResourceSelectedBuilders(HtmlResource, BuildLineMixin):
473
474 - def __init__(self, status):
475 HtmlResource.__init__(self) 476 self.status = status
477
478 - def getChild(self, path, req):
479 if path == "forceselected": 480 return self.forceselected(req) 481 if path == "stopselected": 482 return self.stopselected(req) 483 484 return HtmlResource.getChild(self, path, req)
485
486 - def forceselected(self, req):
487 return ForceAllBuildsActionResource(self.status, 'selected')
488
489 - def stopselected(self, req):
490 return StopAllBuildsActionResource(self.status, 'selected')
491
492 # /builders 493 -class BuildersResource(HtmlResource):
494 pageTitle = "Builders" 495 addSlash = True 496 497 @defer.inlineCallbacks
498 - def content(self, req, cxt):
499 status = self.getStatus(req) 500 encoding = getRequestCharset(req) 501 502 builders = req.args.get("builder", status.getBuilderNames()) 503 branches = [ b.decode(encoding) 504 for b in req.args.get("branch", []) 505 if b ] 506 507 # get counts of pending builds for each builder 508 brstatus_ds = [] 509 brcounts = {} 510 def keep_count(statuses, builderName): 511 brcounts[builderName] = len(statuses)
512 for builderName in builders: 513 builder_status = status.getBuilder(builderName) 514 d = builder_status.getPendingBuildRequestStatuses() 515 d.addCallback(keep_count, builderName) 516 brstatus_ds.append(d) 517 yield defer.gatherResults(brstatus_ds) 518 519 cxt['branches'] = branches 520 bs = cxt['builders'] = [] 521 522 building = 0 523 online = 0 524 base_builders_url = path_to_root(req) + "builders/" 525 for bn in builders: 526 bld = { 'link': base_builders_url + urllib.quote(bn, safe=''), 527 'name': bn } 528 bs.append(bld) 529 530 builder = status.getBuilder(bn) 531 builds = list(builder.generateFinishedBuilds(map_branches(branches), 532 num_builds=1)) 533 if builds: 534 b = builds[0] 535 bld['build_url'] = (bld['link'] + "/builds/%d" % b.getNumber()) 536 label = None 537 all_got_revisions = b.getAllGotRevisions() 538 # If len = 1 then try if revision can be used as label. 539 if len(all_got_revisions) == 1: 540 label = all_got_revisions[all_got_revisions.keys()[0]] 541 if not label or len(str(label)) > 20: 542 label = "#%d" % b.getNumber() 543 544 bld['build_label'] = label 545 bld['build_text'] = " ".join(b.getText()) 546 bld['build_css_class'] = build_get_class(b) 547 548 current_box = ICurrentBox(builder).getBox(status, brcounts) 549 bld['current_box'] = current_box.td() 550 551 builder_status = builder.getState()[0] 552 if builder_status == "building": 553 building += 1 554 online += 1 555 elif builder_status != "offline": 556 online += 1 557 558 cxt['authz'] = self.getAuthz(req) 559 cxt['num_building'] = building 560 cxt['num_online'] = online 561 buildForceContext(cxt, req, self.getBuildmaster(req)) 562 template = req.site.buildbot_service.templates.get_template("builders.html") 563 defer.returnValue(template.render(**cxt))
564
565 - def getChild(self, path, req):
566 s = self.getStatus(req) 567 if path in s.getBuilderNames(): 568 builder_status = s.getBuilder(path) 569 return StatusResourceBuilder(builder_status) 570 if path == "_all": 571 return StatusResourceAllBuilders(self.getStatus(req)) 572 if path == "_selected": 573 return StatusResourceSelectedBuilders(self.getStatus(req)) 574 575 return HtmlResource.getChild(self, path, req)
576