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  from twisted.web.util import Redirect 
 19  import urllib, time 
 20  from twisted.python import log 
 21  from twisted.internet import defer 
 22  from buildbot import interfaces 
 23  from buildbot.status.web.base import HtmlResource, BuildLineMixin, \ 
 24      path_to_build, path_to_slave, path_to_builder, path_to_change, \ 
 25      path_to_root, getAndCheckProperties, ICurrentBox, build_get_class, \ 
 26      map_branches, path_to_authfail, ActionResource 
 27   
 28  from buildbot.status.web.build import BuildsResource, StatusResourceBuild 
 29  from buildbot import util 
30 31 # /builders/$builder 32 -class StatusResourceBuilder(HtmlResource, BuildLineMixin):
33 addSlash = True 34
35 - def __init__(self, builder_status):
36 HtmlResource.__init__(self) 37 self.builder_status = builder_status
38
39 - def getPageTitle(self, request):
40 return "Buildbot: %s" % self.builder_status.getName()
41
42 - def builder(self, build, req):
43 b = {} 44 45 b['num'] = build.getNumber() 46 b['link'] = path_to_build(req, build) 47 48 when = build.getETA() 49 if when is not None: 50 b['when'] = util.formatInterval(when) 51 b['when_time'] = time.strftime("%H:%M:%S", 52 time.localtime(time.time() + when)) 53 54 step = build.getCurrentStep() 55 # TODO: is this necessarily the case? 56 if not step: 57 b['current_step'] = "[waiting for Lock]" 58 else: 59 if step.isWaitingForLocks(): 60 b['current_step'] = "%s [waiting for Lock]" % step.getName() 61 else: 62 b['current_step'] = step.getName() 63 64 b['stop_url'] = path_to_build(req, build) + '/stop' 65 66 return b
67 68 @defer.deferredGenerator
69 - def content(self, req, cxt):
70 b = self.builder_status 71 72 cxt['name'] = b.getName() 73 req.setHeader('Cache-Control', 'no-cache') 74 slaves = b.getSlaves() 75 connected_slaves = [s for s in slaves if s.isConnected()] 76 77 cxt['current'] = [self.builder(x, req) for x in b.getCurrentBuilds()] 78 79 cxt['pending'] = [] 80 wfd = defer.waitForDeferred( 81 b.getPendingBuildRequestStatuses()) 82 yield wfd 83 statuses = wfd.getResult() 84 for pb in statuses: 85 changes = [] 86 87 wfd = defer.waitForDeferred( 88 pb.getSourceStamp()) 89 yield wfd 90 source = wfd.getResult() 91 92 wfd = defer.waitForDeferred( 93 pb.getSubmitTime()) 94 yield wfd 95 submitTime = wfd.getResult() 96 97 if source.changes: 98 for c in source.changes: 99 changes.append({ 'url' : path_to_change(req, c), 100 'who' : c.who, 101 'revision' : c.revision, 102 'repo' : c.repository }) 103 104 cxt['pending'].append({ 105 'when': time.strftime("%b %d %H:%M:%S", 106 time.localtime(submitTime)), 107 'delay': util.formatInterval(util.now() - submitTime), 108 'id': pb.brid, 109 'changes' : changes, 110 'num_changes' : len(changes), 111 }) 112 113 numbuilds = int(req.args.get('numbuilds', ['5'])[0]) 114 recent = cxt['recent'] = [] 115 for build in b.generateFinishedBuilds(num_builds=int(numbuilds)): 116 recent.append(self.get_line_values(req, build, False)) 117 118 sl = cxt['slaves'] = [] 119 connected_slaves = 0 120 for slave in slaves: 121 s = {} 122 sl.append(s) 123 s['link'] = path_to_slave(req, slave) 124 s['name'] = slave.getName() 125 c = s['connected'] = slave.isConnected() 126 if c: 127 s['admin'] = unicode(slave.getAdmin() or '', 'utf-8') 128 connected_slaves += 1 129 cxt['connected_slaves'] = connected_slaves 130 131 cxt['authz'] = self.getAuthz(req) 132 cxt['builder_url'] = path_to_builder(req, b) 133 134 template = req.site.buildbot_service.templates.get_template("builder.html") 135 yield template.render(**cxt)
136
137 - def force(self, req, auth_ok=False):
138 name = req.args.get("username", ["<unknown>"])[0] 139 reason = req.args.get("comments", ["<no reason specified>"])[0] 140 branch = req.args.get("branch", [""])[0] 141 revision = req.args.get("revision", [""])[0] 142 repository = req.args.get("repository", [""])[0] 143 project = req.args.get("project", [""])[0] 144 145 log.msg("web forcebuild of builder '%s', branch='%s', revision='%s'," 146 " repository='%s', project='%s' by user '%s'" % ( 147 self.builder_status.getName(), branch, revision, repository, 148 project, name)) 149 150 # check if this is allowed 151 if not auth_ok: 152 if not self.getAuthz(req).actionAllowed('forceBuild', req, self.builder_status): 153 log.msg("..but not authorized") 154 return Redirect(path_to_authfail(req)) 155 156 master = self.getBuildmaster(req) 157 158 # keep weird stuff out of the branch revision, and property strings. 159 branch_validate = master.config.validation['branch'] 160 revision_validate = master.config.validation['revision'] 161 if not branch_validate.match(branch): 162 log.msg("bad branch '%s'" % branch) 163 return Redirect(path_to_builder(req, self.builder_status)) 164 if not revision_validate.match(revision): 165 log.msg("bad revision '%s'" % revision) 166 return Redirect(path_to_builder(req, self.builder_status)) 167 properties = getAndCheckProperties(req) 168 if properties is None: 169 return Redirect(path_to_builder(req, self.builder_status)) 170 if not branch: 171 branch = None 172 if not revision: 173 revision = None 174 175 d = master.db.sourcestamps.addSourceStamp(branch=branch, 176 revision=revision, project=project, repository=repository) 177 def make_buildset(ssid): 178 r = ("The web-page 'force build' button was pressed by '%s': %s\n" 179 % (html.escape(name), html.escape(reason))) 180 return master.addBuildset( 181 builderNames=[self.builder_status.getName()], 182 ssid=ssid, reason=r, properties=properties.asDict())
183 d.addCallback(make_buildset) 184 d.addErrback(log.err, "(ignored) while trying to force build") 185 # send the user back to the builder page 186 return Redirect(path_to_builder(req, self.builder_status))
187
188 - def ping(self, req):
189 log.msg("web ping of builder '%s'" % self.builder_status.getName()) 190 if not self.getAuthz(req).actionAllowed('pingBuilder', req, self.builder_status): 191 log.msg("..but not authorized") 192 return Redirect(path_to_authfail(req)) 193 c = interfaces.IControl(self.getBuildmaster(req)) 194 bc = c.getBuilder(self.builder_status.getName()) 195 bc.ping() 196 # send the user back to the builder page 197 return Redirect(path_to_builder(req, self.builder_status))
198
199 - def getChild(self, path, req):
200 if path == "force": 201 return self.force(req) 202 if path == "ping": 203 return self.ping(req) 204 if path == "cancelbuild": 205 return CancelChangeResource(self.builder_status) 206 if path == "stopchange": 207 return StopChangeResource(self.builder_status) 208 if path == "builds": 209 return BuildsResource(self.builder_status) 210 211 return HtmlResource.getChild(self, path, req)
212
213 -class CancelChangeResource(ActionResource):
214
215 - def __init__(self, builder_status):
216 ActionResource.__init__(self) 217 self.builder_status = builder_status
218 219 @defer.deferredGenerator
220 - def performAction(self, req):
221 try: 222 request_id = req.args.get("id", [None])[0] 223 if request_id == "all": 224 cancel_all = True 225 else: 226 cancel_all = False 227 request_id = int(request_id) 228 except: 229 request_id = None 230 231 authz = self.getAuthz(req) 232 if request_id: 233 c = interfaces.IControl(self.getBuildmaster(req)) 234 builder_control = c.getBuilder(self.builder_status.getName()) 235 236 wfd = defer.waitForDeferred( 237 builder_control.getPendingBuildRequestControls()) 238 yield wfd 239 brcontrols = wfd.getResult() 240 241 for build_req in brcontrols: 242 if cancel_all or (build_req.brid == request_id): 243 log.msg("Cancelling %s" % build_req) 244 if authz.actionAllowed('cancelPendingBuild', 245 req, build_req): 246 build_req.cancel() 247 else: 248 yield path_to_authfail(req) 249 return 250 if not cancel_all: 251 break 252 253 yield path_to_builder(req, self.builder_status)
254
255 -class StopChangeMixin(object):
256 257 @defer.deferredGenerator
258 - def stopChangeForBuilder(self, req, builder_status, auth_ok=False):
259 try: 260 request_change = req.args.get("change", [None])[0] 261 request_change = int(request_change) 262 except: 263 request_change = None 264 265 authz = self.getAuthz(req) 266 if request_change: 267 c = interfaces.IControl(self.getBuildmaster(req)) 268 builder_control = c.getBuilder(builder_status.getName()) 269 270 wfd = defer.waitForDeferred( 271 builder_control.getPendingBuildRequestControls()) 272 yield wfd 273 brcontrols = wfd.getResult() 274 275 build_controls = dict((x.brid, x) for x in brcontrols) 276 277 wfd = defer.waitForDeferred( 278 builder_status.getPendingBuildRequestStatuses()) 279 yield wfd 280 build_req_statuses = wfd.getResult() 281 282 for build_req in build_req_statuses: 283 wfd = defer.waitForDeferred( 284 build_req.getSourceStamp()) 285 yield wfd 286 ss = wfd.getResult() 287 288 if not ss.changes: 289 continue 290 291 for change in ss.changes: 292 if change.number == request_change: 293 control = build_controls[build_req.brid] 294 log.msg("Cancelling %s" % control) 295 if (auth_ok or authz.actionAllowed('stopChange', 296 req, control)): 297 control.cancel() 298 else: 299 yield False 300 return 301 302 yield True
303
304 305 -class StopChangeResource(StopChangeMixin, ActionResource):
306
307 - def __init__(self, builder_status):
308 ActionResource.__init__(self) 309 self.builder_status = builder_status
310 311 @defer.deferredGenerator
312 - def performAction(self, req):
313 """Cancel all pending builds that include a given numbered change.""" 314 wfd = defer.waitForDeferred( 315 self.stopChangeForBuilder(req, self.builder_status)) 316 yield wfd 317 success = wfd.getResult() 318 319 if not success: 320 yield path_to_authfail(req) 321 else: 322 yield path_to_builder(req, self.builder_status)
323
324 325 -class StopChangeAllResource(StopChangeMixin, ActionResource):
326
327 - def __init__(self, status):
328 ActionResource.__init__(self) 329 self.status = status
330 331 @defer.deferredGenerator
332 - def performAction(self, req):
333 """Cancel all pending builds that include a given numbered change.""" 334 authz = self.getAuthz(req) 335 if not authz.actionAllowed('stopChange', req): 336 yield path_to_authfail(req) 337 return 338 339 for bname in self.status.getBuilderNames(): 340 builder_status = self.status.getBuilder(bname) 341 wfd = defer.waitForDeferred( 342 self.stopChangeForBuilder(req, builder_status, auth_ok=True)) 343 yield wfd 344 if not wfd.getResult(): 345 yield path_to_authfail(req) 346 return 347 348 yield path_to_root(req)
349
350 351 # /builders/_all 352 -class StatusResourceAllBuilders(HtmlResource, BuildLineMixin):
353
354 - def __init__(self, status):
355 HtmlResource.__init__(self) 356 self.status = status
357
358 - def getChild(self, path, req):
359 if path == "forceall": 360 return self.forceall(req) 361 if path == "stopall": 362 return self.stopall(req) 363 if path == "stopchangeall": 364 return StopChangeAllResource(self.status) 365 366 return HtmlResource.getChild(self, path, req)
367
368 - def forceall(self, req):
369 authz = self.getAuthz(req) 370 if not authz.actionAllowed('forceAllBuilds', req): 371 return Redirect(path_to_authfail(req)) 372 373 for bname in self.status.getBuilderNames(): 374 builder_status = self.status.getBuilder(bname) 375 build = StatusResourceBuilder(builder_status) 376 build.force(req, auth_ok=True) # auth_ok because we already checked 377 # back to the welcome page 378 return Redirect(path_to_root(req))
379
380 - def stopall(self, req):
381 authz = self.getAuthz(req) 382 if not authz.actionAllowed('stopAllBuilds', req): 383 return Redirect(path_to_authfail(req)) 384 385 for bname in self.status.getBuilderNames(): 386 builder_status = self.status.getBuilder(bname) 387 (state, current_builds) = builder_status.getState() 388 if state != "building": 389 continue 390 for b in current_builds: 391 build_status = builder_status.getBuild(b.number) 392 if not build_status: 393 continue 394 build = StatusResourceBuild(build_status) 395 build.stop(req, auth_ok=True) 396 # go back to the welcome page 397 return Redirect(path_to_root(req))
398
399 400 # /builders/_selected 401 -class StatusResourceSelectedBuilders(HtmlResource, BuildLineMixin):
402
403 - def __init__(self, status):
404 HtmlResource.__init__(self) 405 self.status = status
406
407 - def getChild(self, path, req):
408 if path == "forceselected": 409 return self.forceselected(req) 410 if path == "stopselected": 411 return self.stopselected(req) 412 413 return HtmlResource.getChild(self, path, req)
414
415 - def forceselected(self, req):
416 authz = self.getAuthz(req) 417 if not authz.actionAllowed('forceAllBuilds', req): 418 return Redirect(path_to_authfail(req)) 419 420 for bname in [b for b in req.args.get("selected", []) if b]: 421 builder_status = self.status.getBuilder(bname) 422 build = StatusResourceBuilder(builder_status) 423 build.force(req, auth_ok=True) # auth_ok because we already checked 424 # back to the welcome page 425 return Redirect(path_to_root(req))
426
427 - def stopselected(self, req):
428 authz = self.getAuthz(req) 429 if not authz.actionAllowed('stopAllBuilds', req): 430 return Redirect(path_to_authfail(req)) 431 432 for bname in [b for b in req.args.get("selected", []) if b]: 433 builder_status = self.status.getBuilder(bname) 434 (state, current_builds) = builder_status.getState() 435 if state != "building": 436 continue 437 for b in current_builds: 438 build_status = builder_status.getBuild(b.number) 439 if not build_status: 440 continue 441 build = StatusResourceBuild(build_status) 442 build.stop(req, auth_ok=True) 443 # go back to the welcome page 444 return Redirect(path_to_root(req))
445
446 447 # /builders 448 -class BuildersResource(HtmlResource):
449 pageTitle = "Builders" 450 addSlash = True 451 452 @defer.deferredGenerator
453 - def content(self, req, cxt):
454 status = self.getStatus(req) 455 456 builders = req.args.get("builder", status.getBuilderNames()) 457 branches = [b for b in req.args.get("branch", []) if b] 458 459 # get counts of pending builds for each builder 460 brstatus_ds = [] 461 brcounts = {} 462 def keep_count(statuses, builderName): 463 brcounts[builderName] = len(statuses)
464 for builderName in builders: 465 builder_status = status.getBuilder(builderName) 466 d = builder_status.getPendingBuildRequestStatuses() 467 d.addCallback(keep_count, builderName) 468 brstatus_ds.append(d) 469 wfd = defer.waitForDeferred( 470 defer.gatherResults(brstatus_ds)) 471 yield wfd 472 wfd.getResult() 473 474 cxt['branches'] = branches 475 bs = cxt['builders'] = [] 476 477 building = 0 478 online = 0 479 base_builders_url = path_to_root(req) + "builders/" 480 for bn in builders: 481 bld = { 'link': base_builders_url + urllib.quote(bn, safe=''), 482 'name': bn } 483 bs.append(bld) 484 485 builder = status.getBuilder(bn) 486 builds = list(builder.generateFinishedBuilds(map_branches(branches), 487 num_builds=1)) 488 if builds: 489 b = builds[0] 490 bld['build_url'] = (bld['link'] + "/builds/%d" % b.getNumber()) 491 try: 492 label = b.getProperty("got_revision") 493 except KeyError: 494 label = None 495 if not label or len(str(label)) > 20: 496 label = "#%d" % b.getNumber() 497 498 bld['build_label'] = label 499 bld['build_text'] = " ".join(b.getText()) 500 bld['build_css_class'] = build_get_class(b) 501 502 current_box = ICurrentBox(builder).getBox(status, brcounts) 503 bld['current_box'] = current_box.td() 504 505 builder_status = builder.getState()[0] 506 if builder_status == "building": 507 building += 1 508 online += 1 509 elif builder_status != "offline": 510 online += 1 511 512 cxt['authz'] = self.getAuthz(req) 513 cxt['num_building'] = building 514 cxt['num_online'] = online 515 516 template = req.site.buildbot_service.templates.get_template("builders.html") 517 yield template.render(**cxt)
518
519 - def getChild(self, path, req):
520 s = self.getStatus(req) 521 if path in s.getBuilderNames(): 522 builder_status = s.getBuilder(path) 523 return StatusResourceBuilder(builder_status) 524 if path == "_all": 525 return StatusResourceAllBuilders(self.getStatus(req)) 526 if path == "_selected": 527 return StatusResourceSelectedBuilders(self.getStatus(req)) 528 529 return HtmlResource.getChild(self, path, req)
530