Use storm in host database.
[amitay/build-farm.git] / buildfarm / hostdb.py
1 #!/usr/bin/python
2
3 # Samba.org buildfarm
4 # Copyright (C) 2008 Andrew Bartlett <abartlet@samba.org>
5 # Copyright (C) 2008-2010 Jelmer Vernooij <jelmer@samba.org>
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 from buildfarm import setup_db
22
23 import pysqlite2
24 from storm.database import create_database
25 from storm.locals import Bool, Int, Unicode, RawStr
26 from storm.store import Store
27 import time
28
29
30 class HostAlreadyExists(Exception):
31     """The specified host already exists."""
32
33     def __init__(self, name):
34         super(HostAlreadyExists, self).__init__()
35         self.name = name
36
37
38 class NoSuchHost(Exception):
39     """The specified host did not exist."""
40
41     def __init__(self, name):
42         super(NoSuchHost, self).__init__()
43         self.name = name
44
45
46 class Host(object):
47     """A host in the buildfarm."""
48
49     def __init__(self, name, owner=None, owner_email=None, password=None, platform=None,
50                  ssh_access=False, last_update=None, fqdn=None, join_time=None, permission=None):
51         self.name = name
52         if owner:
53             self.owner = (owner, owner_email)
54         else:
55             self.owner = None
56         if join_time is None:
57             self.join_time = time.time()
58         else:
59             self.join_time = join_time
60         self.permission = permission
61         self.password = password
62         self.platform = platform
63         self.ssh_access = ssh_access
64         self.last_update = last_update
65         self.fqdn = fqdn
66
67     def __cmp__(self, other):
68         return cmp(self.name, other.name)
69
70
71 class StormHost(Host):
72     __storm_table__ = "host"
73
74     name = Unicode(primary=True)
75     owner_name = Unicode(name="owner")
76     owner_email = Unicode()
77     password = Unicode()
78     ssh_access = Bool()
79     fqdn = RawStr()
80     platform = Unicode()
81     permission = Unicode()
82     last_dead_mail = Int()
83     join_time = Int()
84
85     def _set_owner(self, value):
86         if value is None:
87             self.owner_name = None
88             self.owner_email = None
89         else:
90             (self.owner_name, self.owner_email) = value
91
92     def _get_owner(self):
93         if self.owner_name is None:
94             return None
95         else:
96             return (self.owner_name, self.owner_email)
97
98     owner = property(_get_owner, _set_owner)
99
100
101 class HostDatabase(object):
102     """Host database."""
103
104     def __init__(self, filename=None):
105         if filename is None:
106             self.db = create_database("sqlite:")
107         else:
108             self.db = create_database("sqlite:" + filename)
109         self.store = Store(self.db)
110         setup_db(self.store)
111         self.store.flush()
112
113     def createhost(self, name, platform=None, owner=None, owner_email=None, password=None, permission=None):
114         newhost = StormHost(unicode(name), owner=owner, owner_email=owner_email, password=password, permission=permission, platform=platform)
115         try:
116             self.store.add(newhost)
117             self.store.flush()
118         except pysqlite2.dbapi2.IntegrityError:
119             raise HostAlreadyExists(name)
120
121     def deletehost(self, name):
122         host = self.host(name)
123         if host is None:
124             raise NoSuchHost(name)
125         self.store.remove(host)
126
127     def hosts(self):
128         return self.store.find(StormHost).order_by(StormHost.name)
129
130     def dead_hosts(self, age):
131         dead_time = time.time() - age
132         cursor = self.store.execute("SELECT host.name AS host, host.owner AS owner, host.owner_email AS owner_email, MAX(age) AS last_update FROM host LEFT JOIN build ON ( host.name == build.host) WHERE ifnull(last_dead_mail, 0) < %d AND ifnull(join_time, 0) < %d GROUP BY host.name having ifnull(MAX(age),0) < %d" % (dead_time, dead_time, dead_time))
133         for row in cursor:
134             yield Host(row[0], owner=row[1], owner_email=row[2], last_update=row[3])
135
136     def host_ages(self):
137         cursor = self.store.execute("SELECT host.name AS host, host.owner AS owner, host.owner_email AS owner_email, MAX(age) AS last_update FROM host LEFT JOIN build ON ( host.name == build.host) GROUP BY host.name ORDER BY age")
138         for row in cursor:
139             yield Host(row[0], owner=row[1], owner_email=row[2], last_update=row[3])
140
141     def sent_dead_mail(self, host):
142         self.store.execute("UPDATE host SET last_dead_mail = ? WHERE name = ?", (int(time.time()), host))
143         self.store.flush()
144
145     def host(self, name):
146         return self.hosts().find(StormHost.name==name).one()
147
148     def update_platform(self, name, new_platform):
149         host = self.host(unicode(name))
150         if host is None:
151             raise NoSuchHost(name)
152         host.platform = new_platform
153
154     def update_owner(self, name, new_owner, new_owner_email):
155         cursor = self.store.execute(
156             "UPDATE host SET owner = ?, owner_email = ? WHERE name = ?", (new_owner,
157             new_owner_email, name))
158         if cursor.rowcount == 0:
159             raise NoSuchHost(name)
160         self.store.flush()
161
162     def create_rsync_secrets(self):
163         """Write out the rsyncd.secrets"""
164         yield "# rsyncd.secrets file\n"
165         yield "# automatically generated by textfiles.pl. DO NOT EDIT!\n\n"
166
167         for host in self.hosts():
168             if host.owner:
169                 yield "# %s, owner: %s <%s>\n" % (host.name, host.owner[0], host.owner[1])
170             else:
171                 yield "# %s, owner unknown\n" % (host.name,);
172             if host.password:
173                 yield "%s:%s\n\n" % (host.name, host.password)
174             else:
175                 yield "# %s password is unknown\n\n" % host.name
176
177     def create_hosts_list(self):
178         """Write out the web/"""
179
180         for host in self.hosts():
181             yield "%s: %s\n" % (host.name.encode("utf-8"), host.platform.encode("utf-8"))