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