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

Source Code for Module buildbot.status.web.builder

  1   
  2  from twisted.web import html 
  3  from twisted.web.util import Redirect 
  4   
  5  import re, urllib, time 
  6  from twisted.python import log 
  7  from buildbot import interfaces 
  8  from buildbot.status.web.base import HtmlResource, BuildLineMixin, \ 
  9      path_to_build, path_to_slave, path_to_builder, path_to_change, \ 
 10      path_to_root, getAndCheckProperties, ICurrentBox, build_get_class, \ 
 11      map_branches, path_to_authfail 
 12  from buildbot.sourcestamp import SourceStamp 
 13   
 14  from buildbot.status.builder import BuildRequestStatus 
 15  from buildbot.status.web.build import BuildsResource, StatusResourceBuild 
 16  from buildbot import util 
 17   
 18  # /builders/$builder 
19 -class StatusResourceBuilder(HtmlResource, BuildLineMixin):
20 addSlash = True 21
22 - def __init__(self, builder_status):
23 HtmlResource.__init__(self) 24 self.builder_status = builder_status
25
26 - def getTitle(self, request):
27 return "Buildbot: %s" % self.builder_status.getName()
28
29 - def builder(self, build, req):
30 b = {} 31 32 b['num'] = build.getNumber() 33 b['link'] = path_to_build(req, build) 34 35 when = build.getETA() 36 if when is not None: 37 b['when'] = util.formatInterval(when) 38 b['when_time'] = time.strftime("%H:%M:%S", 39 time.localtime(time.time() + when)) 40 41 step = build.getCurrentStep() 42 # TODO: is this necessarily the case? 43 if not step: 44 b['current_step'] = "[waiting for Lock]" 45 else: 46 if step.isWaitingForLocks(): 47 b['current_step'] = "%s [waiting for Lock]" % step.getName() 48 else: 49 b['current_step'] = step.getName() 50 51 b['stop_url'] = path_to_build(req, build) + '/stop' 52 53 return b
54
55 - def content(self, req, cxt):
56 b = self.builder_status 57 58 cxt['name'] = b.getName() 59 req.setHeader('Cache-Control', 'no-cache') 60 slaves = b.getSlaves() 61 connected_slaves = [s for s in slaves if s.isConnected()] 62 63 cxt['current'] = [self.builder(x, req) for x in b.getCurrentBuilds()] 64 65 cxt['pending'] = [] 66 for pb in b.getPendingBuilds(): 67 source = pb.getSourceStamp() 68 changes = [] 69 70 if source.changes: 71 for c in source.changes: 72 changes.append({ 'url' : path_to_change(req, c), 73 'who' : c.who}) 74 if source.revision: 75 reason = source.revision 76 else: 77 reason = "no changes specified" 78 79 cxt['pending'].append({ 80 'when': time.strftime("%b %d %H:%M:%S", time.localtime(pb.getSubmitTime())), 81 'delay': util.formatInterval(util.now() - pb.getSubmitTime()), 82 'reason': reason, 83 'id': pb.brid, 84 'changes' : changes 85 }) 86 87 numbuilds = int(req.args.get('numbuilds', ['5'])[0]) 88 recent = cxt['recent'] = [] 89 for build in b.generateFinishedBuilds(num_builds=int(numbuilds)): 90 recent.append(self.get_line_values(req, build, False)) 91 92 sl = cxt['slaves'] = [] 93 connected_slaves = 0 94 for slave in slaves: 95 s = {} 96 sl.append(s) 97 s['link'] = path_to_slave(req, slave) 98 s['name'] = slave.getName() 99 c = s['connected'] = slave.isConnected() 100 if c: 101 s['admin'] = unicode(slave.getAdmin() or '', 'utf-8') 102 connected_slaves += 1 103 cxt['connected_slaves'] = connected_slaves 104 105 cxt['authz'] = self.getAuthz(req) 106 cxt['builder_url'] = path_to_builder(req, b) 107 108 template = req.site.buildbot_service.templates.get_template("builder.html") 109 return template.render(**cxt)
110
111 - def force(self, req, auth_ok=False):
112 name = req.args.get("username", ["<unknown>"])[0] 113 reason = req.args.get("comments", ["<no reason specified>"])[0] 114 branch = req.args.get("branch", [""])[0] 115 revision = req.args.get("revision", [""])[0] 116 repository = req.args.get("repository", [""])[0] 117 project = req.args.get("project", [""])[0] 118 119 r = "The web-page 'force build' button was pressed by '%s': %s\n" \ 120 % (html.escape(name), html.escape(reason)) 121 log.msg("web forcebuild of builder '%s', branch='%s', revision='%s'," 122 " repository='%s', project='%s' by user '%s'" % ( 123 self.builder_status.getName(), branch, revision, repository, 124 project, name)) 125 126 # check if this is allowed 127 if not auth_ok: 128 if not self.getAuthz(req).actionAllowed('forceBuild', req, self.builder_status): 129 log.msg("..but not authorized") 130 return Redirect(path_to_authfail(req)) 131 132 # keep weird stuff out of the branch revision, and property strings. 133 # TODO: centralize this somewhere. 134 if not re.match(r'^[\w.+/~-]*$', branch): 135 log.msg("bad branch '%s'" % branch) 136 return Redirect(path_to_builder(req, self.builder_status)) 137 if not re.match(r'^[ \w\.\-\/]*$', revision): 138 log.msg("bad revision '%s'" % revision) 139 return Redirect(path_to_builder(req, self.builder_status)) 140 properties = getAndCheckProperties(req) 141 if properties is None: 142 return Redirect(path_to_builder(req, self.builder_status)) 143 if not branch: 144 branch = None 145 if not revision: 146 revision = None 147 148 # TODO: if we can authenticate that a particular User pushed the 149 # button, use their name instead of None, so they'll be informed of 150 # the results. 151 # TODO2: we can authenticate that a particular User pushed the button 152 # now, so someone can write this support. but it requires a 153 # buildbot.changes.changes.Change instance which is tedious at this 154 # stage to compute 155 s = SourceStamp(branch=branch, revision=revision, project=project, repository=repository) 156 try: 157 c = interfaces.IControl(self.getBuildmaster(req)) 158 bc = c.getBuilder(self.builder_status.getName()) 159 bc.submitBuildRequest(s, r, properties) 160 except interfaces.NoSlaveError: 161 # TODO: tell the web user that their request could not be 162 # honored 163 pass 164 # send the user back to the builder page 165 return Redirect(path_to_builder(req, self.builder_status))
166
167 - def ping(self, req):
168 log.msg("web ping of builder '%s'" % self.builder_status.getName()) 169 if not self.getAuthz(req).actionAllowed('pingBuilder', req, self.builder_status): 170 log.msg("..but not authorized") 171 return Redirect(path_to_authfail(req)) 172 c = interfaces.IControl(self.getBuildmaster(req)) 173 bc = c.getBuilder(self.builder_status.getName()) 174 bc.ping() 175 # send the user back to the builder page 176 return Redirect(path_to_builder(req, self.builder_status))
177
178 - def cancelbuild(self, req):
179 try: 180 request_id = req.args.get("id", [None])[0] 181 if request_id == "all": 182 cancel_all = True 183 else: 184 cancel_all = False 185 request_id = int(request_id) 186 except: 187 request_id = None 188 189 authz = self.getAuthz(req) 190 if request_id: 191 c = interfaces.IControl(self.getBuildmaster(req)) 192 bc = c.getBuilder(self.builder_status.getName()) 193 for build_req in bc.getPendingBuilds(): 194 if cancel_all or (build_req.brid == request_id): 195 log.msg("Cancelling %s" % build_req) 196 if authz.actionAllowed('cancelPendingBuild', req, build_req): 197 build_req.cancel() 198 else: 199 return Redirect(path_to_authfail(req)) 200 if not cancel_all: 201 break 202 return Redirect(path_to_builder(req, self.builder_status))
203
204 - def stopchange(self, req, auth_ok=False):
205 """Cancel all pending builds that include a given numbered change.""" 206 try: 207 request_change = req.args.get("change", [None])[0] 208 request_change = int(request_change) 209 except: 210 request_change = None 211 212 authz = self.getAuthz(req) 213 if request_change: 214 # FIXME: Please, for the love of god one day make there only be 215 # one getPendingBuilds() with combined status info/controls 216 c = interfaces.IControl(self.getBuildmaster(req)) 217 builder_control = c.getBuilder(self.builder_status.getName()) 218 build_controls = dict((x.brid, x) for x in builder_control.getPendingBuilds()) 219 for build_req in self.builder_status.getPendingBuilds(): 220 ss = build_req.getSourceStamp() 221 if not ss.changes: 222 continue 223 for change in ss.changes: 224 if change.number == request_change: 225 control = build_controls[build_req.brid] 226 log.msg("Cancelling %s" % control) 227 if auth_ok or authz.actionAllowed('stopChange', req, control): 228 control.cancel() 229 else: 230 return Redirect(path_to_authfail(req)) 231 return Redirect(path_to_builder(req, self.builder_status))
232
233 - def getChild(self, path, req):
234 if path == "force": 235 return self.force(req) 236 if path == "ping": 237 return self.ping(req) 238 if path == "cancelbuild": 239 return self.cancelbuild(req) 240 if path == "stopchange": 241 return self.stopchange(req) 242 if path == "builds": 243 return BuildsResource(self.builder_status) 244 245 return HtmlResource.getChild(self, path, req)
246 247 248 # /builders/_all
249 -class StatusResourceAllBuilders(HtmlResource, BuildLineMixin):
250
251 - def __init__(self, status):
252 HtmlResource.__init__(self) 253 self.status = status
254
255 - def getChild(self, path, req):
256 if path == "forceall": 257 return self.forceall(req) 258 if path == "stopall": 259 return self.stopall(req) 260 if path == "stopchangeall": 261 return self.stopchangeall(req) 262 263 return HtmlResource.getChild(self, path, req)
264
265 - def forceall(self, req):
266 authz = self.getAuthz(req) 267 if not authz.actionAllowed('forceAllBuilds', req): 268 return Redirect(path_to_authfail(req)) 269 270 for bname in self.status.getBuilderNames(): 271 builder_status = self.status.getBuilder(bname) 272 build = StatusResourceBuilder(builder_status) 273 build.force(req, auth_ok=True) # auth_ok because we already checked 274 # back to the welcome page 275 return Redirect(path_to_root(req))
276
277 - def stopall(self, req):
278 authz = self.getAuthz(req) 279 if not authz.actionAllowed('stopAllBuilds', req): 280 return Redirect(path_to_authfail(req)) 281 282 for bname in self.status.getBuilderNames(): 283 builder_status = self.status.getBuilder(bname) 284 (state, current_builds) = builder_status.getState() 285 if state != "building": 286 continue 287 for b in current_builds: 288 build_status = builder_status.getBuild(b.number) 289 if not build_status: 290 continue 291 build = StatusResourceBuild(build_status) 292 build.stop(req, auth_ok=True) 293 # go back to the welcome page 294 return Redirect(path_to_root(req))
295
296 - def stopchangeall(self, req):
297 authz = self.getAuthz(req) 298 if not authz.actionAllowed('stopChange', req): 299 return Redirect(path_to_authfail(req)) 300 301 for bname in self.status.getBuilderNames(): 302 builder_status = self.status.getBuilder(bname) 303 build = StatusResourceBuilder(builder_status) 304 build.stopchange(req, auth_ok=True) 305 306 return Redirect(path_to_root(req))
307 308 309 # /builders
310 -class BuildersResource(HtmlResource):
311 title = "Builders" 312 addSlash = True 313
314 - def content(self, req, cxt):
315 status = self.getStatus(req) 316 317 builders = req.args.get("builder", status.getBuilderNames()) 318 branches = [b for b in req.args.get("branch", []) if b] 319 320 cxt['branches'] = branches 321 bs = cxt['builders'] = [] 322 323 building = 0 324 online = 0 325 base_builders_url = path_to_root(req) + "builders/" 326 for bn in builders: 327 bld = { 'link': base_builders_url + urllib.quote(bn, safe=''), 328 'name': bn } 329 bs.append(bld) 330 331 builder = status.getBuilder(bn) 332 builds = list(builder.generateFinishedBuilds(map_branches(branches), 333 num_builds=1)) 334 if builds: 335 b = builds[0] 336 bld['build_url'] = (bld['link'] + "/builds/%d" % b.getNumber()) 337 try: 338 label = b.getProperty("got_revision") 339 except KeyError: 340 label = None 341 if not label or len(str(label)) > 20: 342 label = "#%d" % b.getNumber() 343 344 bld['build_label'] = label 345 bld['build_text'] = " ".join(b.getText()) 346 bld['build_css_class'] = build_get_class(b) 347 348 current_box = ICurrentBox(builder).getBox(status) 349 bld['current_box'] = current_box.td() 350 351 builder_status = builder.getState()[0] 352 if builder_status == "building": 353 building += 1 354 online += 1 355 elif builder_status != "offline": 356 online += 1 357 358 cxt['authz'] = self.getAuthz(req) 359 cxt['num_building'] = building 360 cxt['num_online'] = online 361 362 template = req.site.buildbot_service.templates.get_template("builders.html") 363 return template.render(**cxt)
364
365 - def getChild(self, path, req):
366 s = self.getStatus(req) 367 if path in s.getBuilderNames(): 368 builder_status = s.getBuilder(path) 369 return StatusResourceBuilder(builder_status) 370 if path == "_all": 371 return StatusResourceAllBuilders(self.getStatus(req)) 372 373 return HtmlResource.getChild(self, path, req)
374