| OLD | NEW |
| (Empty) |
| 1 from twisted.python import log | |
| 2 from twisted.web import xmlrpc | |
| 3 from buildbot.status.builder import Results | |
| 4 from itertools import count | |
| 5 | |
| 6 class XMLRPCServer(xmlrpc.XMLRPC): | |
| 7 def __init__(self): | |
| 8 xmlrpc.XMLRPC.__init__(self) | |
| 9 | |
| 10 def render(self, req): | |
| 11 # extract the IStatus and IControl objects for later use, since they | |
| 12 # come from the request object. They'll be the same each time, but | |
| 13 # they aren't available until the first request arrives. | |
| 14 self.status = req.site.buildbot_service.getStatus() | |
| 15 self.control = req.site.buildbot_service.getControl() | |
| 16 return xmlrpc.XMLRPC.render(self, req) | |
| 17 | |
| 18 def xmlrpc_getAllBuilders(self): | |
| 19 """Return a list of all builder names | |
| 20 """ | |
| 21 log.msg("getAllBuilders") | |
| 22 return self.status.getBuilderNames() | |
| 23 | |
| 24 def xmlrpc_getLastBuildResults(self, builder_name): | |
| 25 """Return the result of the last build for the given builder | |
| 26 """ | |
| 27 builder = self.status.getBuilder(builder_name) | |
| 28 lastbuild = builder.getBuild(-1) | |
| 29 return Results[lastbuild.getResults()] | |
| 30 | |
| 31 def xmlrpc_getLastBuildsAllBuilders(self, num_builds): | |
| 32 """Return the last N completed builds for all builders. | |
| 33 | |
| 34 'num_builds' is the number of builds for each builder to | |
| 35 return | |
| 36 | |
| 37 """ | |
| 38 all_builds = [] | |
| 39 for name in self.status.getBuilderNames(): | |
| 40 all_builds.extend(self.xmlrpc_getLastBuilds(name, num_builds)) | |
| 41 return all_builds | |
| 42 | |
| 43 def xmlrpc_getLastBuilds(self, builder_name, num_builds): | |
| 44 """Return the last N completed builds for the given builder. | |
| 45 'builder_name' is the name of the builder to query | |
| 46 'num_builds' is the number of builds to return | |
| 47 | |
| 48 Each build is returned in the same form as xmlrpc_getAllBuildsInInterval | |
| 49 """ | |
| 50 log.msg("getLastBuilds: %s - %d" % (builder_name, num_builds)) | |
| 51 builder = self.status.getBuilder(builder_name) | |
| 52 all_builds = [] | |
| 53 for build_number in range(1, num_builds+1): | |
| 54 build = builder.getBuild(-build_number) | |
| 55 if not build: | |
| 56 break | |
| 57 if not build.isFinished(): | |
| 58 continue | |
| 59 (build_start, build_end) = build.getTimes() | |
| 60 | |
| 61 ss = build.getSourceStamp() | |
| 62 branch = ss.branch | |
| 63 if branch is None: | |
| 64 branch = "" | |
| 65 try: | |
| 66 revision = build.getProperty("got_revision") | |
| 67 except KeyError: | |
| 68 revision = "" | |
| 69 revision = str(revision) | |
| 70 | |
| 71 result = Results[build.getResults()] | |
| 72 if result == 'failure': | |
| 73 fail_names = result = build.getText()[1:] | |
| 74 reasons = [] | |
| 75 for s in build.getSteps(): | |
| 76 if s.getName() in fail_names: | |
| 77 reasons.append(s.getText()) | |
| 78 else: | |
| 79 result = build.getText() | |
| 80 reasons = [] | |
| 81 answer = (builder_name, | |
| 82 build.getNumber(), | |
| 83 build_start, | |
| 84 build_end, | |
| 85 branch, | |
| 86 revision, | |
| 87 Results[build.getResults()], | |
| 88 result, | |
| 89 reasons, | |
| 90 ) | |
| 91 all_builds.append((build_end, answer)) | |
| 92 | |
| 93 # now we've gotten all the builds we're interested in. Sort them by | |
| 94 # end time. | |
| 95 all_builds.sort(lambda a,b: cmp(a[0], b[0])) | |
| 96 # and remove the timestamps | |
| 97 all_builds = [t[1] for t in all_builds] | |
| 98 | |
| 99 log.msg("ready to go: %s" % (all_builds,)) | |
| 100 | |
| 101 return all_builds | |
| 102 | |
| 103 | |
| 104 def xmlrpc_getAllBuildsInInterval(self, start, stop): | |
| 105 """Return a list of builds that have completed after the 'start' | |
| 106 timestamp and before the 'stop' timestamp. This looks at all | |
| 107 Builders. | |
| 108 | |
| 109 The timestamps are integers, interpreted as standard unix timestamps | |
| 110 (seconds since epoch). | |
| 111 | |
| 112 Each Build is returned as a tuple in the form:: | |
| 113 (buildername, buildnumber, build_end, branchname, revision, | |
| 114 results, text) | |
| 115 | |
| 116 The buildnumber is an integer. 'build_end' is an integer (seconds | |
| 117 since epoch) specifying when the build finished. | |
| 118 | |
| 119 The branchname is a string, which may be an empty string to indicate | |
| 120 None (i.e. the default branch). The revision is a string whose | |
| 121 meaning is specific to the VC system in use, and comes from the | |
| 122 'got_revision' build property. The results are expressed as a string, | |
| 123 one of ('success', 'warnings', 'failure', 'exception'). The text is a | |
| 124 list of short strings that ought to be joined by spaces and include | |
| 125 slightly more data about the results of the build. | |
| 126 """ | |
| 127 #log.msg("start: %s %s %s" % (start, type(start), start.__class__)) | |
| 128 log.msg("getAllBuildsInInterval: %d - %d" % (start, stop)) | |
| 129 all_builds = [] | |
| 130 | |
| 131 for builder_name in self.status.getBuilderNames(): | |
| 132 builder = self.status.getBuilder(builder_name) | |
| 133 for build_number in count(1): | |
| 134 build = builder.getBuild(-build_number) | |
| 135 if not build: | |
| 136 break | |
| 137 if not build.isFinished(): | |
| 138 continue | |
| 139 (build_start, build_end) = build.getTimes() | |
| 140 # in reality, builds are mostly ordered by start time. For | |
| 141 # the purposes of this method, we pretend that they are | |
| 142 # strictly ordered by end time, so that we can stop searching | |
| 143 # when we start seeing builds that are outside the window. | |
| 144 if build_end > stop: | |
| 145 continue # keep looking | |
| 146 if build_end < start: | |
| 147 break # stop looking | |
| 148 | |
| 149 ss = build.getSourceStamp() | |
| 150 branch = ss.branch | |
| 151 if branch is None: | |
| 152 branch = "" | |
| 153 try: | |
| 154 revision = build.getProperty("got_revision") | |
| 155 except KeyError: | |
| 156 revision = "" | |
| 157 revision = str(revision) | |
| 158 | |
| 159 answer = (builder_name, | |
| 160 build.getNumber(), | |
| 161 build_end, | |
| 162 branch, | |
| 163 revision, | |
| 164 Results[build.getResults()], | |
| 165 build.getText(), | |
| 166 ) | |
| 167 all_builds.append((build_end, answer)) | |
| 168 # we've gotten all the builds that we care about from this | |
| 169 # particular builder, so now we can continue on the next builder | |
| 170 | |
| 171 # now we've gotten all the builds we're interested in. Sort them by | |
| 172 # end time. | |
| 173 all_builds.sort(lambda a,b: cmp(a[0], b[0])) | |
| 174 # and remove the timestamps | |
| 175 all_builds = [t[1] for t in all_builds] | |
| 176 | |
| 177 log.msg("ready to go: %s" % (all_builds,)) | |
| 178 | |
| 179 return all_builds | |
| 180 | |
| 181 def xmlrpc_getBuild(self, builder_name, build_number): | |
| 182 """Return information about a specific build. | |
| 183 | |
| 184 """ | |
| 185 builder = self.status.getBuilder(builder_name) | |
| 186 build = builder.getBuild(build_number) | |
| 187 info = {} | |
| 188 info['builder_name'] = builder.getName() | |
| 189 info['url'] = self.status.getURLForThing(build) or '' | |
| 190 info['reason'] = build.getReason() | |
| 191 info['slavename'] = build.getSlavename() | |
| 192 info['results'] = build.getResults() | |
| 193 info['text'] = build.getText() | |
| 194 info['reasons'] = [] | |
| 195 # Added to help out requests for build -N | |
| 196 info['number'] = build.number | |
| 197 ss = build.getSourceStamp() | |
| 198 branch = ss.branch | |
| 199 if branch is None: | |
| 200 branch = "" | |
| 201 info['branch'] = str(branch) | |
| 202 try: | |
| 203 revision = str(build.getProperty("got_revision")) | |
| 204 except KeyError: | |
| 205 revision = "" | |
| 206 info['revision'] = str(revision) | |
| 207 info['start'], info['end'] = build.getTimes() | |
| 208 | |
| 209 step_names = {} | |
| 210 | |
| 211 info_steps = [] | |
| 212 for s in build.getSteps(): | |
| 213 stepinfo = {} | |
| 214 stepinfo['name'] = s.getName() | |
| 215 stepinfo['start'], stepinfo['end'] = s.getTimes() | |
| 216 stepinfo['results'] = s.getResults() | |
| 217 stepinfo['text'] = s.getText() | |
| 218 info_steps.append(stepinfo) | |
| 219 if info['text'][0] == 'failed' and stepinfo['name'] in info['text']: | |
| 220 info['reasons'].append(stepinfo['text']) | |
| 221 step_names[stepinfo['name']] = stepinfo | |
| 222 info['steps'] = info_steps | |
| 223 | |
| 224 info_logs = [] | |
| 225 info['full_error'] = {} | |
| 226 for l in build.getLogs(): | |
| 227 loginfo = {} | |
| 228 name = l.getStep().getName() | |
| 229 loginfo['name'] = name + "/" + l.getName() | |
| 230 #loginfo['text'] = l.getText() | |
| 231 loginfo['text'] = "HUGE" | |
| 232 if step_names.get(name): | |
| 233 if step_names[name]['text'][-1] == 'failed': | |
| 234 info['full_error'][name] = l.getText() | |
| 235 info_logs.append(loginfo) | |
| 236 info['logs'] = info_logs | |
| 237 return info | |
| OLD | NEW |