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 default_rev_order = "asc" 185
186 - def content(self, request, cxt):
187 """This method builds the transposed grid display. 188 That is, build hosts across the top, build stamps down the left side 189 """ 190 191 # get url parameters 192 numBuilds = int(request.args.get("length", [5])[0]) 193 categories = request.args.get("category", []) 194 branch = request.args.get("branch", [ANYBRANCH])[0] 195 if branch == 'trunk': branch = None 196 197 rev_order = request.args.get("rev_order", [self.default_rev_order])[0] 198 if rev_order not in ["asc", "desc"]: 199 rev_order = self.default_rev_order 200 201 cxt['refresh'] = self.get_reload_time(request) 202 203 # and the data we want to render 204 status = self.getStatus(request) 205 stamps = self.getRecentSourcestamps(status, numBuilds, categories, branch) 206 207 cxt.update({'categories': categories, 208 'branch': branch, 209 'ANYBRANCH': ANYBRANCH, 210 'stamps': map(SourceStamp.asDict, stamps), 211 }) 212 213 sortedBuilderNames = status.getBuilderNames()[:] 214 sortedBuilderNames.sort() 215 216 cxt['sorted_builder_names'] = sortedBuilderNames 217 cxt['builder_builds'] = builder_builds = [] 218 cxt['builders'] = builders = [] 219 cxt['range'] = range(len(stamps)) 220 if rev_order == "desc": 221 cxt['range'].reverse() 222 223 for bn in sortedBuilderNames: 224 builds = [None] * len(stamps) 225 226 builder = status.getBuilder(bn) 227 if categories and builder.category not in categories: 228 continue 229 230 build = builder.getBuild(-1) 231 while build and None in builds: 232 ss = build.getSourceStamp(absolute=True) 233 key = self.getSourceStampKey(ss) 234 for i in range(len(stamps)): 235 if key == self.getSourceStampKey(stamps[i]) and builds[i] is None: 236 builds[i] = build 237 build = build.getPreviousBuild() 238 239 builders.append(self.builder_cxt(request, builder)) 240 builder_builds.append(map(lambda b: self.build_cxt(request, b), builds)) 241 242 template = request.site.buildbot_service.templates.get_template('grid_transposed.html') 243 data = template.render(**cxt) 244 return data
245