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

Source Code for Module buildbot.status.web.build

  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, DeferredResource 
 19  from twisted.internet import defer, reactor 
 20   
 21  import urllib, time 
 22  from twisted.python import log 
 23  from buildbot.status.web.base import HtmlResource, \ 
 24       css_classes, path_to_build, path_to_builder, path_to_slave, \ 
 25       getAndCheckProperties, path_to_authfail 
 26   
 27  from buildbot.status.web.step import StepsResource 
 28  from buildbot.status.web.tests import TestsResource 
 29  from buildbot import util, interfaces 
 30   
 31   
 32   
 33  # /builders/$builder/builds/$buildnum 
34 -class StatusResourceBuild(HtmlResource):
35 addSlash = True 36
37 - def __init__(self, build_status):
40
41 - def getPageTitle(self, request):
42 return ("Buildbot: %s Build #%d" % 43 (self.build_status.getBuilder().getName(), 44 self.build_status.getNumber()))
45
46 - def content(self, req, cxt):
47 b = self.build_status 48 status = self.getStatus(req) 49 req.setHeader('Cache-Control', 'no-cache') 50 51 cxt['b'] = b 52 cxt['path_to_builder'] = path_to_builder(req, b.getBuilder()) 53 54 if not b.isFinished(): 55 step = b.getCurrentStep() 56 if not step: 57 cxt['current_step'] = "[waiting for Lock]" 58 else: 59 if step.isWaitingForLocks(): 60 cxt['current_step'] = "%s [waiting for Lock]" % step.getName() 61 else: 62 cxt['current_step'] = step.getName() 63 when = b.getETA() 64 if when is not None: 65 cxt['when'] = util.formatInterval(when) 66 cxt['when_time'] = time.strftime("%H:%M:%S", 67 time.localtime(time.time() + when)) 68 69 else: 70 cxt['result_css'] = css_classes[b.getResults()] 71 if b.getTestResults(): 72 cxt['tests_link'] = req.childLink("tests") 73 74 ss = cxt['ss'] = b.getSourceStamp() 75 76 if ss.branch is None and ss.revision is None and ss.patch is None and not ss.changes: 77 cxt['most_recent_rev_build'] = True 78 79 80 got_revision = None 81 try: 82 got_revision = b.getProperty("got_revision") 83 except KeyError: 84 pass 85 if got_revision: 86 cxt['got_revision'] = str(got_revision) 87 88 try: 89 cxt['slave_url'] = path_to_slave(req, status.getSlave(b.getSlavename())) 90 except KeyError: 91 pass 92 93 cxt['steps'] = [] 94 95 for s in b.getSteps(): 96 step = {'name': s.getName() } 97 cxt['steps'].append(step) 98 99 if s.isFinished(): 100 step['css_class'] = css_classes[s.getResults()[0]] 101 (start, end) = s.getTimes() 102 step['time_to_run'] = util.formatInterval(end - start) 103 elif s.isStarted(): 104 if s.isWaitingForLocks(): 105 step['css_class'] = "waiting" 106 step['time_to_run'] = "waiting for locks" 107 else: 108 step['css_class'] = "running" 109 step['time_to_run'] = "running" 110 else: 111 step['css_class'] = "not_started" 112 step['time_to_run'] = "" 113 114 step['link'] = req.childLink("steps/%s" % urllib.quote(s.getName())) 115 step['text'] = " ".join(s.getText()) 116 step['urls'] = map(lambda x:dict(url=x[1],logname=x[0]), s.getURLs().items()) 117 118 step['logs']= [] 119 for l in s.getLogs(): 120 logname = l.getName() 121 step['logs'].append({ 'link': req.childLink("steps/%s/logs/%s" % 122 (urllib.quote(s.getName()), 123 urllib.quote(logname))), 124 'name': logname }) 125 126 ps = cxt['properties'] = [] 127 for name, value, source in b.getProperties().asList(): 128 value = unicode(value) 129 p = { 'name': name, 'value': value, 'source': source} 130 if len(value) > 500: 131 p['short_value'] = value[:500] 132 133 ps.append(p) 134 135 136 cxt['responsible_users'] = list(b.getResponsibleUsers()) 137 138 (start, end) = b.getTimes() 139 cxt['start'] = time.ctime(start) 140 if end: 141 cxt['end'] = time.ctime(end) 142 cxt['elapsed'] = util.formatInterval(end - start) 143 else: 144 now = util.now() 145 cxt['elapsed'] = util.formatInterval(now - start) 146 147 cxt['exactly'] = (ss.revision is not None) or b.getChanges() 148 149 cxt['build_url'] = path_to_build(req, b) 150 cxt['authz'] = self.getAuthz(req) 151 152 template = req.site.buildbot_service.templates.get_template("build.html") 153 return template.render(**cxt)
154
155 - def stop(self, req, auth_ok=False):
156 # check if this is allowed 157 if not auth_ok: 158 if not self.getAuthz(req).actionAllowed('stopBuild', req, self.build_status): 159 return Redirect(path_to_authfail(req)) 160 161 b = self.build_status 162 log.msg("web stopBuild of build %s:%s" % \ 163 (b.getBuilder().getName(), b.getNumber())) 164 name = req.args.get("username", ["<unknown>"])[0] 165 comments = req.args.get("comments", ["<no reason specified>"])[0] 166 # html-quote both the username and comments, just to be safe 167 reason = ("The web-page 'stop build' button was pressed by " 168 "'%s': %s\n" % (html.escape(name), html.escape(comments))) 169 170 c = interfaces.IControl(self.getBuildmaster(req)) 171 bldrc = c.getBuilder(self.build_status.getBuilder().getName()) 172 if bldrc: 173 bldc = bldrc.getBuild(self.build_status.getNumber()) 174 if bldc: 175 bldc.stopBuild(reason) 176 177 # we're at http://localhost:8080/svn-hello/builds/5/stop?[args] and 178 # we want to go to: http://localhost:8080/svn-hello 179 r = Redirect(path_to_builder(req, self.build_status.getBuilder())) 180 d = defer.Deferred() 181 reactor.callLater(1, d.callback, r) 182 return DeferredResource(d)
183
184 - def rebuild(self, req):
185 # check auth 186 if not self.getAuthz(req).actionAllowed('forceBuild', req, self.build_status.getBuilder()): 187 return Redirect(path_to_authfail(req)) 188 189 # get a control object 190 c = interfaces.IControl(self.getBuildmaster(req)) 191 bc = c.getBuilder(self.build_status.getBuilder().getName()) 192 193 b = self.build_status 194 builder_name = b.getBuilder().getName() 195 log.msg("web rebuild of build %s:%s" % (builder_name, b.getNumber())) 196 name = req.args.get("username", ["<unknown>"])[0] 197 comments = req.args.get("comments", ["<no reason specified>"])[0] 198 reason = ("The web-page 'rebuild' button was pressed by " 199 "'%s': %s\n" % (name, comments)) 200 extraProperties = getAndCheckProperties(req) 201 if not bc or not b.isFinished() or extraProperties is None: 202 log.msg("could not rebuild: bc=%s, isFinished=%s" 203 % (bc, b.isFinished())) 204 # TODO: indicate an error 205 else: 206 d = bc.rebuildBuild(b, reason, extraProperties) 207 d.addErrback(log.err, "while rebuilding a build") 208 # we're at 209 # http://localhost:8080/builders/NAME/builds/5/rebuild?[args] 210 # Where should we send them? 211 # 212 # Ideally it would be to the per-build page that they just started, 213 # but we don't know the build number for it yet (besides, it might 214 # have to wait for a current build to finish). The next-most 215 # preferred place is somewhere that the user can see tangible 216 # evidence of their build starting (or to see the reason that it 217 # didn't start). This should be the Builder page. 218 219 r = Redirect(path_to_builder(req, self.build_status.getBuilder())) 220 d = defer.Deferred() 221 reactor.callLater(1, d.callback, r) 222 return DeferredResource(d)
223
224 - def getChild(self, path, req):
225 if path == "stop": 226 return self.stop(req) 227 if path == "rebuild": 228 return self.rebuild(req) 229 if path == "steps": 230 return StepsResource(self.build_status) 231 if path == "tests": 232 return TestsResource(self.build_status) 233 234 return HtmlResource.getChild(self, path, req)
235 236 # /builders/$builder/builds
237 -class BuildsResource(HtmlResource):
238 addSlash = True 239
240 - def __init__(self, builder_status):
241 HtmlResource.__init__(self) 242 self.builder_status = builder_status
243
244 - def content(self, req, cxt):
245 return "subpages shows data for each build"
246
247 - def getChild(self, path, req):
248 try: 249 num = int(path) 250 except ValueError: 251 num = None 252 if num is not None: 253 build_status = self.builder_status.getBuild(num) 254 if build_status: 255 return StatusResourceBuild(build_status) 256 257 return HtmlResource.getChild(self, path, req)
258