509b024ef619bc34ecad72bfee07e73107705789
[build-farm.git] / buildfarm / __init__.py
1 #!/usr/bin/python
2 # Simple database query script for the buildfarm
3 #
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org>         2010
5 #
6 #   This program is free software; you can redistribute it and/or modify
7 #   it under the terms of the GNU General Public License as published by
8 #   the Free Software Foundation; either version 2 of the License, or
9 #   (at your option) any later version.
10 #
11 #   This program is distributed in the hope that it will be useful,
12 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #   GNU General Public License for more details.
15 #
16 #   You should have received a copy of the GNU General Public License
17 #   along with this program; if not, write to the Free Software
18 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 import ConfigParser
21 import os
22 import re
23
24 GIT_ROOT = "/data/git"
25
26
27 class Tree(object):
28     """A tree to build."""
29
30     def __init__(self, name, scm, repo, branch, subdir="", srcdir=""):
31         self.name = name
32         self.repo = repo
33         self.scm = scm
34         self.branch = branch
35         self.subdir = subdir
36         self.srcdir = srcdir
37         self.scm = scm
38
39     def get_branch(self):
40         if self.scm == "git":
41             from buildfarm.history import GitBranch
42             return GitBranch(os.path.join(GIT_ROOT, self.repo), self.branch)
43         else:
44             raise NotImplementedError(self.scm)
45
46     def __repr__(self):
47         return "<%s %r>" % (self.__class__.__name__, self.name)
48
49
50 def read_trees_from_conf(path):
51     """Read trees from a configuration file.
52
53     :param path: tree path
54     :return: Dictionary with trees
55     """
56     ret = {}
57     cfp = ConfigParser.ConfigParser()
58     cfp.read(path)
59     for s in cfp.sections():
60         ret[s] = Tree(name=s, **dict(cfp.items(s)))
61     return ret
62
63
64 def lcov_extract_percentage(text):
65     """Extract the coverage percentage from the lcov file."""
66     m = re.search('\<td class="headerItem".*?\>Code\&nbsp\;covered\:\<\/td\>.*?\n.*?\<td class="headerValue".*?\>([0-9.]+) \%', text)
67     if m:
68         return m.group(1)
69     else:
70         return None
71
72
73 class BuildFarm(object):
74
75     LCOVHOST = "magni"
76     OLDAGE = 60*60*4,
77     DEADAGE = 60*60*24*4
78
79     def __init__(self, path=None):
80         if path is None:
81             path = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
82         self.path = path
83         self.webdir = os.path.join(self.path, "web")
84         if not os.path.isdir(path):
85             raise Exception("web directory %s does not exist" % self.webdir)
86         self.trees = read_trees_from_conf(os.path.join(self.webdir, "trees.conf"))
87         self.builds = self._open_build_results()
88         self.upload_builds = self._open_upload_build_results()
89         self.hostdb = self._open_hostdb()
90         self.compilers = self._load_compilers()
91         self.lcovdir = os.path.join(self.path, "lcov/data")
92
93     def __repr__(self):
94         return "%s(%r)" % (self.__class__.__name__, self.path)
95
96     def _open_build_results(self):
97         from buildfarm import data
98         path = os.path.join(self.path, "data", "oldrevs")
99         return data.BuildResultStore(path)
100
101     def _open_upload_build_results(self):
102         from buildfarm import data
103         path = os.path.join(self.path, "data", "upload")
104         return data.UploadBuildResultStore(path)
105
106     def _open_hostdb(self):
107         from buildfarm import hostdb
108         return hostdb.PlainTextHostDatabase.from_file(os.path.join(self.webdir, "hosts.list"))
109
110     def _load_compilers(self):
111         from buildfarm import util
112         return set(util.load_list(os.path.join(self.webdir, "compilers.list")))
113
114     def commit(self):
115         pass
116
117     def lcov_status(self, tree):
118         """get status of build"""
119         from buildfarm import data, util
120         file = os.path.join(self.lcovdir, self.LCOVHOST, tree, "index.html")
121         try:
122             lcov_html = util.FileLoad(file)
123         except (OSError, IOError):
124             # File does not exist
125             raise data.NoSuchBuildError(tree, self.LCOVHOST, "lcov")
126
127         perc = lcov_extract_percentage(lcov_html)
128         if perc is None:
129             ret = ""
130         else:
131             ret = perc
132         return perc
133
134     def get_build(self, tree, host, compiler, rev=None, checksum=None):
135         if rev is not None:
136             return self.builds.get_build(tree, host, compiler, rev,
137                 checksum=checksum)
138         else:
139             return self.upload_builds.get_build(tree, host, compiler)
140
141     def get_new_builds(self):
142         hostnames = set([host.name for host in self.hostdb.hosts()])
143         for build in self.upload_builds.get_new_builds():
144             if (build.tree in self.trees and
145                 build.compiler in self.compilers and
146                 build.host in hostnames):
147                 yield build
148
149     def get_last_builds(self):
150         return sorted(self.get_new_builds(), reverse=True)
151
152     def get_tree_builds(self, tree):
153         ret = []
154         for build in self.builds.get_all_builds():
155             if build.tree == tree:
156                 ret.append(build)
157         ret.sort(reverse=True)
158         return ret
159
160     def host_last_build(self, host):
161         return max([build.upload_time for build in self.get_host_builds(host)])
162
163     def get_host_builds(self, host):
164         from buildfarm import data
165         ret = []
166         for compiler in self.compilers:
167             for tree in sorted(self.trees.keys()):
168                 try:
169                     ret.append(self.get_build(tree, host, compiler))
170                 except data.NoSuchBuildError:
171                     pass
172         ret.sort(reverse=True)
173         return ret