From 9eccfbc8b3c05c4ad1fec531ad3ef8bb401f877e Mon Sep 17 00:00:00 2001
From: Benoit Sigoure <tsuna@lrde.epita.fr>
Date: Thu, 8 Nov 2007 17:25:13 +0100
Subject: [PATCH 06/11] Add force/stop buttons that affect all builders.
The `Latest Build' and `Recent Builds' pages now have a button to
force or stop a build on all builders. It's more convenient to
trigger a build on all builders (a feature that has frequently been
asked).
* status/web/baseweb.py (OneLinePerBuild.body): Print forms to
force/stop builds on all builders, depending on the state of
builders.
(OneBoxPerBuilder.body): Ditto.
(WebStatus): Document the new `_all/{force,stop}' URLs.
* status/web/builder.py (StatusResourceAllBuilders): New class.
(BuildersResource.getChild): Handle the special child `_all' that
dispatches commands on all builders.
Signed-off-by: Benoit Sigoure <tsuna@lrde.epita.fr>
---
buildbot/status/web/baseweb.py | 41 ++++++++++++++++++++++++++-
buildbot/status/web/builder.py | 60 ++++++++++++++++++++++++++++++++++++++--
2 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/buildbot/status/web/baseweb.py b/buildbot/status/web/baseweb.py
index 9475334..aa9551b 100644
|
a
|
b
|
|
| 11 | 11 | from buildbot.interfaces import IControl, IStatusReceiver |
| 12 | 12 | |
| 13 | 13 | from buildbot.status.web.base import HtmlResource, Box, \ |
| 14 | | build_get_class, ICurrentBox, OneLineMixin, map_branches |
| | 14 | build_get_class, ICurrentBox, OneLineMixin, map_branches, \ |
| | 15 | make_stop_form, make_force_build_form |
| 15 | 16 | from buildbot.status.web.waterfall import WaterfallStatusResource |
| 16 | 17 | from buildbot.status.web.changes import ChangesResource |
| 17 | 18 | from buildbot.status.web.builder import BuildersResource |
| … |
… |
|
| 105 | 106 | data += ("<p>of builders: %s</p>\n" % (", ".join(builders))) |
| 106 | 107 | data += "<ul>\n" |
| 107 | 108 | got = 0 |
| | 109 | building = False |
| | 110 | online = 0 |
| 108 | 111 | for build in g: |
| 109 | 112 | got += 1 |
| 110 | 113 | data += " <li>" + self.make_line(req, build) + "</li>\n" |
| | 114 | builder_status = build.getBuilder().getState()[0] |
| | 115 | if builder_status == "building": |
| | 116 | building = True |
| | 117 | online += 1 |
| | 118 | elif builder_status != "offline": |
| | 119 | online += 1 |
| 111 | 120 | if not got: |
| 112 | 121 | data += " <li>No matching builds found</li>\n" |
| 113 | 122 | data += "</ul>\n" |
| | 123 | |
| | 124 | if building: |
| | 125 | stopURL = "builders/_all/stop" |
| | 126 | data += make_stop_form(stopURL) |
| | 127 | if online: |
| | 128 | forceURL = "builders/_all/force" |
| | 129 | data += make_force_build_form(forceURL) |
| | 130 | |
| 114 | 131 | return data |
| 115 | 132 | |
| 116 | 133 | |
| … |
… |
|
| 165 | 182 | |
| 166 | 183 | def body(self, req): |
| 167 | 184 | status = self.getStatus(req) |
| 168 | | |
| | 185 | |
| 169 | 186 | builders = req.args.get("builder", status.getBuilderNames()) |
| 170 | 187 | branches = [b for b in req.args.get("branch", []) if b] |
| 171 | 188 | |
| … |
… |
|
| 173 | 190 | |
| 174 | 191 | data += "<h2>Latest builds: %s</h2>\n" % ", ".join(branches) |
| 175 | 192 | data += "<table>\n" |
| | 193 | |
| | 194 | building = False |
| | 195 | online = 0 |
| 176 | 196 | for bn in builders: |
| 177 | 197 | builder = status.getBuilder(bn) |
| 178 | 198 | data += "<tr>\n" |
| … |
… |
|
| 200 | 220 | data += '<td class="LastBuild box" >no build</td>\n' |
| 201 | 221 | current_box = ICurrentBox(builder).getBox(status) |
| 202 | 222 | data += current_box.td(align="center") |
| | 223 | |
| | 224 | builder_status = builder.getState()[0] |
| | 225 | if builder_status == "building": |
| | 226 | building = True |
| | 227 | online += 1 |
| | 228 | elif builder_status != "offline": |
| | 229 | online += 1 |
| | 230 | |
| 203 | 231 | data += "</table>\n" |
| | 232 | |
| | 233 | if building: |
| | 234 | stopURL = "builders/_all/stop" |
| | 235 | data += make_stop_form(stopURL) |
| | 236 | if online: |
| | 237 | forceURL = "builders/_all/force" |
| | 238 | data += make_force_build_form(forceURL) |
| | 239 | |
| 204 | 240 | return data |
| 205 | 241 | |
| 206 | 242 | |
| … |
… |
|
| 254 | 290 | /builders/BUILDERNAME/builds/NUM/steps/STEPNAME/logs/LOGNAME: a StatusLog |
| 255 | 291 | /builders/BUILDERNAME/builds/NUM/tests : summarize test results |
| 256 | 292 | /builders/BUILDERNAME/builds/NUM/tests/TEST.NAME: results of one test |
| | 293 | /builders/_all/{force,stop}: force a build/stop building on all builders. |
| 257 | 294 | /changes : summarize all ChangeSources |
| 258 | 295 | /changes/CHANGENUM: a page describing a single Change |
| 259 | 296 | /schedulers/SCHEDULERNAME: a page describing a Scheduler, including |
diff --git a/buildbot/status/web/builder.py b/buildbot/status/web/builder.py
index f67fb45..4ea47cf 100644
|
a
|
b
|
|
| 11 | 11 | from buildbot.process.base import BuildRequest |
| 12 | 12 | from buildbot.sourcestamp import SourceStamp |
| 13 | 13 | |
| 14 | | from buildbot.status.web.build import BuildsResource |
| | 14 | from buildbot.status.web.build import BuildsResource, StatusResourceBuild |
| 15 | 15 | |
| 16 | 16 | # /builders/$builder |
| 17 | 17 | class StatusResourceBuilder(HtmlResource, OneLineMixin): |
| … |
… |
|
| 114 | 114 | pingURL = urllib.quote(req.childLink("ping")) |
| 115 | 115 | data += """ |
| 116 | 116 | <form action="%s" class='command pingbuilder'> |
| 117 | | <p>To ping the buildslave(s), push the 'Ping' button</p> |
| | 117 | <p>To ping the buildslave(s), click the 'Ping' button</p> |
| 118 | 118 | |
| 119 | 119 | <input type="submit" value="Ping Builder" /> |
| 120 | 120 | </form> |
| … |
… |
|
| 151 | 151 | if not revision: |
| 152 | 152 | revision = None |
| 153 | 153 | |
| 154 | | # TODO: if we can authenticate that a particular User pushed the |
| | 154 | # TODO: if we can authenticate that a particular User clicked the |
| 155 | 155 | # button, use their name instead of None, so they'll be informed of |
| 156 | 156 | # the results. |
| 157 | 157 | s = SourceStamp(branch=branch, revision=revision) |
| … |
… |
|
| 200 | 200 | return HtmlResource.getChild(self, path, req) |
| 201 | 201 | |
| 202 | 202 | |
| | 203 | # /builders/_all |
| | 204 | class StatusResourceAllBuilders(HtmlResource, OneLineMixin): |
| | 205 | |
| | 206 | def __init__(self, status, control): |
| | 207 | HtmlResource.__init__(self) |
| | 208 | self.status = status |
| | 209 | self.control = control |
| | 210 | |
| | 211 | def getChild(self, path, req): |
| | 212 | if path == "force": |
| | 213 | return self.force(req) |
| | 214 | if path == "stop": |
| | 215 | return self.stop(req) |
| | 216 | |
| | 217 | return HtmlResource.getChild(self, path, req) |
| | 218 | |
| | 219 | def force(self, req): |
| | 220 | for bname in self.status.getBuilderNames(): |
| | 221 | builder_status = self.status.getBuilder(bname) |
| | 222 | builder_control = None |
| | 223 | c = self.getControl(req) |
| | 224 | if c: |
| | 225 | builder_control = c.getBuilder(bname) |
| | 226 | build = StatusResourceBuilder(builder_status, builder_control) |
| | 227 | build.force(req) |
| | 228 | return Redirect("../../waterfall") |
| | 229 | |
| | 230 | def stop(self, req): |
| | 231 | for bname in self.status.getBuilderNames(): |
| | 232 | builder_status = self.status.getBuilder(bname) |
| | 233 | builder_control = None |
| | 234 | c = self.getControl(req) |
| | 235 | if c: |
| | 236 | builder_control = c.getBuilder(bname) |
| | 237 | (state, current_builds) = builder_status.getState() |
| | 238 | if state != "building": |
| | 239 | continue |
| | 240 | for b in current_builds: |
| | 241 | build_status = builder_status.getBuild(b.number) |
| | 242 | if not build_status: |
| | 243 | continue |
| | 244 | if builder_control: |
| | 245 | build_control = builder_control.getBuild(b.number) |
| | 246 | else: |
| | 247 | build_control = None |
| | 248 | build = StatusResourceBuild(build_status, build_control, |
| | 249 | builder_control) |
| | 250 | build.stop(req) |
| | 251 | return Redirect("../../waterfall") |
| | 252 | |
| | 253 | |
| 203 | 254 | # /builders |
| 204 | 255 | class BuildersResource(HtmlResource): |
| 205 | 256 | title = "Builders" |
| … |
… |
|
| 231 | 282 | if c: |
| 232 | 283 | builder_control = c.getBuilder(path) |
| 233 | 284 | return StatusResourceBuilder(builder_status, builder_control) |
| | 285 | if path == "_all": |
| | 286 | return StatusResourceAllBuilders(self.getStatus(req), |
| | 287 | self.getControl(req)) |
| 234 | 288 | |
| 235 | 289 | return HtmlResource.getChild(self, path, req) |
| 236 | 290 | |