]>
Commit | Line | Data |
---|---|---|
2b60fce9 MT |
1 | #!/usr/bin/python |
2 | ############################################################################### | |
3 | # # | |
4 | # IPFire.org - A linux based firewall # | |
5 | # Copyright (C) 2008 Michael Tremer & Christian Schmidt # | |
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 | ||
22 | import os | |
23 | import sys | |
24 | import time | |
25 | import socket | |
10da7ad4 | 26 | import base64 |
f006517e | 27 | import shutil |
2b60fce9 MT |
28 | from pysqlite2 import dbapi2 as sqlite |
29 | ||
30 | sys.path.append(".") | |
31 | ||
32 | from constants import config | |
33 | ||
34 | class Database: | |
35 | def __init__(self, path): | |
36 | self.db = sqlite.connect(os.path.join(path, config["db_name"])) | |
37 | c = self.cursor() | |
38 | c.executescript(""" | |
39 | create table if not exists config(key, value, date); | |
40 | create table if not exists durations(duration); | |
41 | """) | |
42 | c.close() | |
43 | ||
44 | def __call__(self): | |
45 | return self.cursor() | |
46 | ||
47 | def __del__(self): | |
48 | self.commit() | |
49 | self.db.close() | |
50 | ||
51 | def cursor(self): | |
52 | return self.db.cursor() | |
53 | ||
54 | def commit(self): | |
55 | self.db.commit() | |
56 | ||
57 | class DatabaseConfig: | |
0359f8fc | 58 | def __init__(self, db, key, base64=0): |
2b60fce9 MT |
59 | self.db = db |
60 | self.key = key | |
61 | self.data = None | |
62 | self.date = None | |
0359f8fc | 63 | self.base64 = base64 |
2b60fce9 MT |
64 | |
65 | def get(self): | |
66 | if not self.data: | |
67 | c = self.db.cursor() | |
68 | c.execute("SELECT value FROM %(table)s WHERE key = '%(key)s'" \ | |
69 | % { "table" : "config", | |
70 | "key" : self.key, }) | |
71 | try: | |
72 | self.data = c.fetchone()[0] | |
73 | except TypeError: | |
74 | self.data = None | |
75 | c.close() | |
76 | return self.data | |
77 | ||
78 | __call__ = get | |
79 | ||
80 | def time(self): | |
81 | if not self.date: | |
82 | c = self.db.cursor() | |
83 | c.execute("SELECT date FROM %(table)s WHERE key = '%(key)s'" \ | |
84 | % { "table" : "config", | |
85 | "key" : self.key, }) | |
86 | try: | |
87 | self.date = float("%s" % c.fetchone()[0]) | |
88 | except TypeError: | |
89 | self.date = None | |
90 | c.close() | |
91 | return self.date or float(0) | |
92 | ||
93 | def set(self, value): | |
0359f8fc MT |
94 | if self.base64: |
95 | value = base64.b64decode(value) | |
2b60fce9 MT |
96 | #value = (value,) |
97 | c = self.db.cursor() | |
98 | if not self.get(): | |
99 | sql = "INSERT INTO %(table)s(key, value, date) VALUES('%(key)s', '%(value)s', '%(date)s')" \ | |
100 | % { "table" : "config", | |
101 | "key" : self.key, | |
102 | "value" : value, | |
103 | "date" : time.time(), } | |
104 | ||
105 | else: | |
106 | sql = "UPDATE %(table)s SET value='%(value)s', date='%(date)s' WHERE key='%(key)s'" \ | |
107 | % { "table" : "config", | |
108 | "key" : self.key, | |
109 | "value" : value, | |
110 | "date" : time.time(), } | |
111 | c.execute(sql) | |
112 | c.close() | |
113 | self.data = value | |
114 | self.db.commit() | |
76bb443a | 115 | return """Set "%s" to "%s".""" % (self.key, self.data,) |
2b60fce9 MT |
116 | |
117 | class DurationsConfig: | |
118 | def __init__(self, db): | |
119 | self.db = db | |
120 | ||
121 | def get(self, sort=0): | |
122 | c = self.db.cursor() | |
123 | c.execute("SELECT duration FROM durations") | |
1e0061eb MT |
124 | ret = [] |
125 | for value in c.fetchall(): | |
126 | value = int("%s" % value) | |
906c175d | 127 | if value < 900: # 15min |
1e0061eb MT |
128 | continue |
129 | ret.append(value) | |
2b60fce9 MT |
130 | c.close() |
131 | if sort: ret.sort() | |
132 | return ret | |
133 | ||
134 | def set(self, value): | |
135 | #value = (value,) | |
136 | c = self.db.cursor() | |
137 | c.execute("INSERT INTO %(table)s(duration) VALUES('%(value)s')" \ | |
138 | % { "table" : "durations", | |
139 | "value" : value, }) | |
140 | c.close() | |
141 | self.db.commit() | |
76bb443a | 142 | return """Accepted build duration of %s seconds.""" % (value,) |
2b60fce9 MT |
143 | |
144 | def get_avg(self): | |
145 | sum = 0 | |
146 | durations = self.get() | |
147 | if not len(durations): | |
148 | return None | |
149 | for value in durations: | |
1e0061eb | 150 | sum += value |
2b60fce9 MT |
151 | avg = sum / len(durations) |
152 | return avg | |
153 | ||
154 | def get_eta(self, timestamp): | |
155 | avg = self.get_avg() | |
156 | if not avg: | |
157 | return "N/A" | |
158 | eta = int(timestamp) + avg | |
159 | return time.ctime(eta) | |
160 | ||
161 | class DistccConfig(DatabaseConfig): | |
10da7ad4 | 162 | def __init__(self, db, key, hostname, jobs): |
2b60fce9 MT |
163 | DatabaseConfig.__init__(self, db, key) |
164 | self.hostname = hostname | |
10da7ad4 | 165 | self.jobs = jobs |
2b60fce9 MT |
166 | |
167 | def __str__(self): | |
fda98c60 | 168 | if not self.ping() or self.get() == "0": |
2b60fce9 | 169 | return "" |
10da7ad4 MT |
170 | return "%s:%s/%s,lzo \t# %s" % \ |
171 | (socket.gethostbyname(self.hostname), self.get(), self.jobs or "4", self.hostname) | |
2b60fce9 MT |
172 | |
173 | def ping(self): | |
174 | if not self.hostname: | |
175 | return False | |
176 | return not os.system("ping -c1 -w1 %s &>/dev/null" % self.hostname) | |
177 | ||
178 | def version(self): | |
179 | return os.popen("distcc --version").readlines() | |
180 | ||
10da7ad4 MT |
181 | class FileConfig: |
182 | def __init__(self, path, filetype): | |
183 | self.filename = os.path.join(path, config["path"][filetype]) | |
184 | ||
185 | # Create the file if not existant | |
186 | if not os.access(self.filename, os.R_OK): | |
187 | f = open(self.filename, "w") | |
188 | f.close() | |
189 | ||
190 | def get(self): | |
191 | ret = [] | |
192 | try: | |
193 | f = open(self.filename) | |
194 | ret = f.readlines() | |
195 | f.close() | |
196 | except: | |
197 | pass | |
198 | return ret or ["Log is empty."] | |
199 | ||
200 | __call__ = get | |
201 | ||
202 | def set(self, lines): | |
203 | f = open(self.filename, "w") | |
204 | for line in base64.b64decode(lines).split("\n"): | |
205 | f.write("%s\n" % line.rstrip("\n")) | |
206 | f.close() | |
76bb443a | 207 | return """Saved file content to %s.""" % (self.filename,) |
10da7ad4 | 208 | |
2b60fce9 MT |
209 | class Builder: |
210 | def __init__(self, config, uuid): | |
211 | self.uuid = uuid | |
212 | self.config = config | |
213 | self.path = os.path.join(self.config['path']['db'], self.uuid) | |
214 | ||
215 | if not os.access(self.path, os.R_OK): | |
216 | try: | |
217 | os.mkdir(self.path) | |
218 | except: | |
219 | pass | |
220 | ||
221 | self.db = Database(self.path) | |
10da7ad4 | 222 | |
2b60fce9 MT |
223 | self.hostname = DatabaseConfig(self.db, "hostname") |
224 | self.state = DatabaseConfig(self.db, "state") | |
225 | self.package = DatabaseConfig(self.db, "package") | |
0dc54d31 | 226 | self.target = DatabaseConfig(self.db, "target") |
10da7ad4 | 227 | |
2b60fce9 | 228 | self.duration = DurationsConfig(self.db) |
10da7ad4 MT |
229 | self.jobs = DatabaseConfig(self.db, "jobs") |
230 | self.distcc = DistccConfig(self.db, "distcc", self.hostname(), self.jobs()) | |
0359f8fc MT |
231 | self.cpu = DatabaseConfig(self.db, "cpu", base64=1) |
232 | self.machine = DatabaseConfig(self.db, "machine") | |
10da7ad4 MT |
233 | |
234 | self.log = FileConfig(self.path, "log") | |
f006517e MT |
235 | |
236 | # If host was longer than 3 days in state compiling we set it as idle. | |
237 | if self.state() == "compiling" and \ | |
238 | (time.time() - self.state.time()) > 3*24*60*60: | |
239 | self.state.set("idle") | |
240 | ||
241 | # If host is idle and distcc is not disabled we set it as distcc host. | |
242 | if self.state() == "idle" and self.distcc() != "0": | |
243 | self.state.set("distcc") | |
244 | ||
245 | # If host is longer than 24h in error state we set it as distcc host. | |
246 | if self.state() == "error" and \ | |
247 | (time.time() - self.state.time()) > 24*60*60: | |
248 | self.state.set("distcc") | |
249 | ||
250 | # If host was longer than two weeks in distcc state we set it as unknown. | |
251 | if self.state() == "error" and \ | |
252 | (time.time() - self.state.time()) > 2*7*24*60*60: | |
253 | self.state.set("unknown") | |
254 | ||
255 | # If host was longer than four weels in distcc state we delete it. | |
76bb443a | 256 | if self.state() in ("distcc", "unknown",) and \ |
f006517e MT |
257 | (time.time() - self.state.time()) > 4*7*24*60*60: |
258 | del self.db | |
259 | shutil.rmtree(self.path) | |
2b60fce9 MT |
260 | |
261 | def set(self, key, value): | |
76bb443a | 262 | return eval("self.%s.set(\"%s\")" % (key, value,)) |
2b60fce9 MT |
263 | |
264 | def get(self, key): | |
265 | return eval("self.%s.get()" % (key,)) | |
266 | ||
205adcfe | 267 | def getAllBuilders(age=0): |
2b60fce9 MT |
268 | builders = [] |
269 | for uuid in os.listdir(config["path"]["db"]): | |
270 | if uuid == "empty.txt": continue | |
271 | builder = Builder(config, uuid) | |
205adcfe MT |
272 | # If there was no activity since "age" days -> continue... |
273 | if age and (time.time() - builder.state.time()) > age*24*60*60: | |
274 | continue | |
2b60fce9 MT |
275 | builders.append(builder) |
276 | return builders |