Ticket #293: amf.py

File amf.py, 9.7 KB (added by thijs, 3 years ago)

buildbot.status.web.amf

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