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

Source Code for Module buildbot.status.web.grid

  1  from __future__ import generators 
  2   
  3  from buildbot.status.web.base import HtmlResource 
  4  from buildbot.status.web.base import build_get_class, path_to_builder, path_to_build 
  5  from buildbot.sourcestamp import SourceStamp 
  6   
7 -class ANYBRANCH: pass # a flag value, used below
8
9 -class GridStatusMixin(object):
10 - def getTitle(self, request):
11 status = self.getStatus(request) 12 p = status.getProjectName() 13 if p: 14 return "BuildBot: %s" % p 15 else: 16 return "BuildBot"
17 18 # handle reloads through an http header 19 # TODO: send this as a real header, rather than a tag
20 - def get_reload_time(self, request):
21 if "reload" in request.args: 22 try: 23 reload_time = int(request.args["reload"][0]) 24 return max(reload_time, 15) 25 except ValueError: 26 pass 27 return None
28
29 - def build_cxt(self, request, build):
30 if not build: 31 return {} 32 33 if build.isFinished(): 34 # get the text and annotate the first line with a link 35 text = build.getText() 36 if not text: text = [ "(no information)" ] 37 if text == [ "build", "successful" ]: text = [ "OK" ] 38 else: 39 text = [ 'building' ] 40 41 name = build.getBuilder().getName() 42 43 cxt = {} 44 cxt['name'] = name 45 cxt['url'] = path_to_build(request, build) 46 cxt['text'] = text 47 cxt['class'] = build_get_class(build) 48 return cxt
49
50 - def builder_cxt(self, request, builder):
51 state, builds = builder.getState() 52 53 # look for upcoming builds. We say the state is "waiting" if the 54 # builder is otherwise idle and there is a scheduler which tells us a 55 # build will be performed some time in the near future. TODO: this 56 # functionality used to be in BuilderStatus.. maybe this code should 57 # be merged back into it. 58 upcoming = [] 59 builderName = builder.getName() 60 for s in self.getStatus(request).getSchedulers(): 61 if builderName in s.listBuilderNames(): 62 upcoming.extend(s.getPendingBuildTimes()) 63 if state == "idle" and upcoming: 64 state = "waiting" 65 66 # TODO: for now, this pending/upcoming stuff is in the "current 67 # activity" box, but really it should go into a "next activity" row 68 # instead. The only times it should show up in "current activity" is 69 # when the builder is otherwise idle. 70 71 cxt = { 'url': path_to_builder(request, builder), 72 'name': builder.getName(), 73 'state': state, 74 'n_pending': len(builder.getPendingBuilds()) } 75 76 return cxt
77
78 - def getSourceStampKey(self, ss):
79 """Given two source stamps, we want to assign them to the same row if 80 they are the same version of code, even if they differ in minor detail. 81 82 This function returns an appropriate comparison key for that. 83 """ 84 return (ss.branch, ss.revision, ss.patch)
85
86 - def getRecentSourcestamps(self, status, numBuilds, categories, branch):
87 """ 88 get a list of the most recent NUMBUILDS SourceStamp tuples, sorted 89 by the earliest start we've seen for them 90 """ 91 # TODO: use baseweb's getLastNBuilds? 92 sourcestamps = { } # { ss-tuple : earliest time } 93 for bn in status.getBuilderNames(): 94 builder = status.getBuilder(bn) 95 if categories and builder.category not in categories: 96 continue 97 build = builder.getBuild(-1) 98 while build: 99 ss = build.getSourceStamp(absolute=True) 100 start = build.getTimes()[0] 101 build = build.getPreviousBuild() 102 103 # skip un-started builds 104 if not start: continue 105 106 # skip non-matching branches 107 if branch != ANYBRANCH and ss.branch != branch: continue 108 109 key= self.getSourceStampKey(ss) 110 if key not in sourcestamps or sourcestamps[key][1] > start: 111 sourcestamps[key] = (ss, start) 112 113 # now sort those and take the NUMBUILDS most recent 114 sourcestamps = sourcestamps.values() 115 sourcestamps.sort(lambda x, y: cmp(x[1], y[1])) 116 sourcestamps = map(lambda tup : tup[0], sourcestamps) 117 sourcestamps = sourcestamps[-numBuilds:] 118 119 return sourcestamps
120
121 -class GridStatusResource(HtmlResource, GridStatusMixin):
122 # TODO: docs 123 status = None 124 changemaster = None 125
126 - def content(self, request, cxt):
127 """This method builds the regular grid display. 128 That is, build stamps across the top, build hosts down the left side 129 """ 130 131 # get url parameters 132 numBuilds = int(request.args.get("width", [5])[0]) 133 categories = request.args.get("category", []) 134 branch = request.args.get("branch", [ANYBRANCH])[0] 135 if branch == 'trunk': branch = None 136 137 # and the data we want to render 138 status = self.getStatus(request) 139 stamps = self.getRecentSourcestamps(status, numBuilds, categories, branch) 140 141 cxt['refresh'] = self.get_reload_time(request) 142 143 cxt.update({'categories': categories, 144 'branch': branch, 145 'ANYBRANCH': ANYBRANCH, 146 'stamps': map(SourceStamp.asDict, stamps) 147 }) 148 149 sortedBuilderNames = status.getBuilderNames()[:] 150 sortedBuilderNames.sort() 151 152 cxt['builders'] = [] 153 154 for bn in sortedBuilderNames: 155 builds = [None] * len(stamps) 156 157 builder = status.getBuilder(bn) 158 if categories and builder.category not in categories: 159 continue 160 161 build = builder.getBuild(-1) 162 while build and None in builds: 163 ss = build.getSourceStamp(absolute=True) 164 key= self.getSourceStampKey(ss) 165 for i in range(len(stamps)): 166 if key == self.getSourceStampKey(stamps[i]) and builds[i] is None: 167 builds[i] = build 168 build = build.getPreviousBuild() 169 170 b = self.builder_cxt(request, builder) 171 b['builds'] = [] 172 for build in builds: 173 b['builds'].append(self.build_cxt(request, build)) 174 cxt['builders'].append(b) 175 176 template = request.site.buildbot_service.templates.get_template("grid.html") 177 return template.render(**cxt)
178 179
180 -class TransposedGridStatusResource(HtmlResource, GridStatusMixin):
181 # TODO: docs 182 status = None 183 changemaster = None 184
185 - def content(self, request, cxt):
186 """This method builds the transposed grid display. 187 That is, build hosts across the top, build stamps down the left side 188 """ 189 190 # get url parameters 191 numBuilds = int(request.args.get("length", [5])[0]) 192 categories = request.args.get("category", []) 193 branch = request.args.get("branch", [ANYBRANCH])[0] 194 if branch == 'trunk': branch = None 195 196 cxt['refresh'] = self.get_reload_time(request) 197 198 # and the data we want to render 199 status = self.getStatus(request) 200 stamps = self.getRecentSourcestamps(status, numBuilds, categories, branch) 201 202 cxt.update({'categories': categories, 203 'branch': branch, 204 'ANYBRANCH': ANYBRANCH, 205 'stamps': map(SourceStamp.asDict, stamps), 206 }) 207 208 sortedBuilderNames = status.getBuilderNames()[:] 209 sortedBuilderNames.sort() 210 211 cxt['sorted_builder_names'] = sortedBuilderNames 212 cxt['builder_builds'] = builder_builds = [] 213 cxt['builders'] = builders = [] 214 cxt['range'] = range(len(stamps)) 215 216 for bn in sortedBuilderNames: 217 builds = [None] * len(stamps) 218 219 builder = status.getBuilder(bn) 220 if categories and builder.category not in categories: 221 continue 222 223 build = builder.getBuild(-1) 224 while build and None in builds: 225 ss = build.getSourceStamp(absolute=True) 226 key = self.getSourceStampKey(ss) 227 for i in range(len(stamps)): 228 if key == self.getSourceStampKey(stamps[i]) and builds[i] is None: 229 builds[i] = build 230 build = build.getPreviousBuild() 231 232 builders.append(self.builder_cxt(request, builder)) 233 builder_builds.append(map(lambda b: self.build_cxt(request, b), builds)) 234 235 template = request.site.buildbot_service.templates.get_template('grid_transposed.html') 236 data = template.render(**cxt) 237 return data
238