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 getTitle(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 = str(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 bc.rebuildBuild(b, reason, extraProperties) 207 # we're at 208 # http://localhost:8080/builders/NAME/builds/5/rebuild?[args] 209 # Where should we send them? 210 # 211 # Ideally it would be to the per-build page that they just started, 212 # but we don't know the build number for it yet (besides, it might 213 # have to wait for a current build to finish). The next-most 214 # preferred place is somewhere that the user can see tangible 215 # evidence of their build starting (or to see the reason that it 216 # didn't start). This should be the Builder page. 217 218 r = Redirect(path_to_builder(req, self.build_status.getBuilder())) 219 d = defer.Deferred() 220 reactor.callLater(1, d.callback, r) 221 return DeferredResource(d)
222
223 - def getChild(self, path, req):
224 if path == "stop": 225 return self.stop(req) 226 if path == "rebuild": 227 return self.rebuild(req) 228 if path == "steps": 229 return StepsResource(self.build_status) 230 if path == "tests": 231 return TestsResource(self.build_status) 232 233 return HtmlResource.getChild(self, path, req)
234 235 # /builders/$builder/builds
236 -class BuildsResource(HtmlResource):
237 addSlash = True 238
239 - def __init__(self, builder_status):
240 HtmlResource.__init__(self) 241 self.builder_status = builder_status
242
243 - def content(self, req, cxt):
244 return "subpages shows data for each build"
245
246 - def getChild(self, path, req):
247 try: 248 num = int(path) 249 except ValueError: 250 num = None 251 if num is not None: 252 build_status = self.builder_status.getBuild(num) 253 if build_status: 254 return StatusResourceBuild(build_status) 255 256 return HtmlResource.getChild(self, path, req)
257