4 # Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
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 3 of the License, or
9 # (at your option) any later version.
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.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from buildfarm import (
23 from buildfarm.data import (
28 from buildfarm.hostdb import (
39 from pysqlite2 import dbapi2 as sqlite3
40 from storm.database import create_database
41 from storm.locals import Bool, Desc, Int, Unicode, RawStr
42 from storm.store import Store
45 class StormBuild(Build):
46 __storm_table__ = "build"
48 id = Int(primary=True)
56 commit_revision = RawStr()
59 super(StormBuild, self).remove()
60 Store.of(self).remove(self)
63 class StormHost(Host):
64 __storm_table__ = "host"
66 name = Unicode(primary=True)
67 owner_name = Unicode(name="owner")
68 owner_email = Unicode()
73 permission = Unicode()
74 last_dead_mail = Int()
77 def _set_owner(self, value):
79 self.owner_name = None
80 self.owner_email = None
82 (self.owner_name, self.owner_email) = value
85 if self.owner_name is None:
88 return (self.owner_name, self.owner_email)
90 owner = property(_get_owner, _set_owner)
93 class StormHostDatabase(HostDatabase):
95 def __init__(self, store=None):
97 self.store = memory_store()
101 def createhost(self, name, platform=None, owner=None, owner_email=None, password=None, permission=None):
102 newhost = StormHost(unicode(name), owner=owner, owner_email=owner_email, password=password, permission=permission, platform=platform)
104 self.store.add(newhost)
106 except sqlite3.IntegrityError:
107 raise HostAlreadyExists(name)
110 def deletehost(self, name):
112 host = self.host(name)
113 self.store.remove(host)
116 """Retrieve an iterable over all hosts."""
117 return self.store.find(StormHost).order_by(StormHost.name)
119 def host(self, name):
120 ret = self.hosts().find(StormHost.name==name).one()
122 raise NoSuchHost(name)
129 class StormCachingBuildResultStore(BuildResultStore):
131 def __init__(self, basedir, store=None):
132 super(StormCachingBuildResultStore, self).__init__(basedir)
135 store = memory_store()
139 def get_previous_revision(self, tree, host, compiler, revision):
140 result = self.store.find(StormBuild,
141 StormBuild.tree == unicode(tree),
142 StormBuild.host == unicode(host),
143 StormBuild.compiler == unicode(compiler),
144 StormBuild.commit_revision == revision)
145 cur_build = result.any()
146 if cur_build is None:
147 raise NoSuchBuildError(tree, host, compiler, revision)
149 result = self.store.find(StormBuild,
150 StormBuild.tree == unicode(tree),
151 StormBuild.host == unicode(host),
152 StormBuild.compiler == unicode(compiler),
153 StormBuild.commit_revision != revision,
154 StormBuild.id < cur_build.id)
155 result = result.order_by(Desc(StormBuild.id))
156 prev_build = result.first()
157 if prev_build is None:
158 raise NoSuchBuildError(tree, host, compiler, revision)
159 return prev_build.commit_revision
161 def get_latest_revision(self, tree, host, compiler):
162 result = self.store.find(StormBuild,
163 StormBuild.tree == unicode(tree),
164 StormBuild.host == unicode(host),
165 StormBuild.compiler == unicode(compiler))
166 result = result.order_by(Desc(StormBuild.id))
167 build = result.first()
169 raise NoSuchBuildError(tree, host, compiler)
170 return build.revision
172 def upload_build(self, build):
173 super(StormCachingBuildResultStore, self).upload_build(build)
174 rev, timestamp = build.revision_details()
175 result = self.store.find(StormBuild, StormBuild.checksum == build.log_checksum())
176 existing_build = result.one()
177 if existing_build is not None:
179 assert build.tree == existing_build.tree
180 assert build.host == existing_build.host
181 assert build.compiler == existing_build.compiler
182 assert rev == existing_build.revision
184 new_basename = self.build_fname(build.tree, build.host, build.compiler, rev)
185 new_build = StormBuild(new_basename, unicode(build.tree), unicode(build.host), unicode(build.compiler), rev)
186 new_build.checksum = build.log_checksum()
187 new_build.age = build.age_mtime()
188 new_build.status = unicode(str(build.status()))
189 self.store.add(new_build)
192 class StormCachingBuildFarm(BuildFarm):
194 def __init__(self, path=None, store=None):
196 super(StormCachingBuildFarm, self).__init__(path)
198 def _get_store(self):
199 if self.store is not None:
202 db = create_database("sqlite:" + os.path.join(self.path, "hostdb.sqlite"))
203 self.store = Store(db)
204 setup_schema(self.store)
207 def _open_hostdb(self):
208 return StormHostDatabase(self._get_store())
210 def _open_build_results(self):
211 return StormCachingBuildResultStore(os.path.join(self.path, "data", "oldrevs"),
218 def setup_schema(db):
219 db.execute("CREATE TABLE IF NOT EXISTS host (name text, owner text, owner_email text, password text, ssh_access int, fqdn text, platform text, permission text, last_dead_mail int, join_time int);", noresult=True)
220 db.execute("CREATE UNIQUE INDEX IF NOT EXISTS unique_hostname ON host (name);", noresult=True)
221 db.execute("CREATE TABLE IF NOT EXISTS build (id integer primary key autoincrement, tree text, revision text, host text, compiler text, checksum text, age int, status text, commit_revision text);", noresult=True)
222 db.execute("CREATE UNIQUE INDEX IF NOT EXISTS unique_checksum ON build (checksum);", noresult=True)
226 db = create_database("sqlite:")