]>
git.ipfire.org Git - people/jschlag/pbs.git/blob - src/buildservice/builders.py
3 from __future__
import absolute_import
, division
15 from .decorators
import *
17 from .users
import generate_password_hash
, check_password_hash
, generate_random_string
19 class Builders(base
.Object
):
20 def _get_builder(self
, query
, *args
):
21 res
= self
.db
.get(query
, *args
)
24 return Builder(self
.backend
, res
.id, data
=res
)
26 def _get_builders(self
, query
, *args
):
27 res
= self
.db
.query(query
, *args
)
30 yield Builder(self
.backend
, row
.id, data
=row
)
33 builders
= self
._get
_builders
("SELECT * FROM builders \
34 WHERE deleted IS FALSE ORDER BY name")
38 def create(self
, name
, user
=None, log
=True):
40 Creates a new builder.
42 builder
= self
._get
_builder
("INSERT INTO builders(name) \
43 VALUES(%s) RETURNING *", name
)
45 # Generate a new passphrase.
46 passphrase
= builder
.regenerate_passphrase()
48 # Log what we have done.
50 builder
.log("created", user
=user
)
52 # The Builder object and the passphrase are returned.
53 return builder
, passphrase
55 def auth(self
, name
, passphrase
):
56 # If either name or passphrase is None, we don't check at all.
57 if None in (name
, passphrase
):
60 # Search for the hostname in the database.
61 builder
= self
._get
_builder
("SELECT * FROM builders \
62 WHERE name = %s AND deleted IS FALSE", name
)
64 # If the builder was not found or the passphrase does not match,
66 if not builder
or not builder
.validate_passphrase(passphrase
):
69 # Otherwise we return the Builder object.
72 def get_by_id(self
, builder_id
):
73 return self
._get
_builder
("SELECT * FROM builders WHERE id = %s", builder_id
)
75 def get_by_name(self
, name
):
76 return self
._get
_builder
("SELECT * FROM builders \
77 WHERE name = %s AND deleted IS FALSE", name
)
79 def get_history(self
, limit
=None, offset
=None, builder
=None, user
=None):
80 query
= "SELECT * FROM builders_history"
86 conditions
.append("builder_id = %s")
87 args
.append(builder
.id)
90 conditions
.append("user_id = %s")
94 query
+= " WHERE %s" % " AND ".join(conditions
)
96 query
+= " ORDER BY time DESC"
100 query
+= " LIMIT %s,%s"
101 args
+= [offset
, limit
,]
107 for entry
in self
.db
.query(query
, *args
):
108 entry
= logs
.BuilderLogEntry(self
.pakfire
, entry
)
109 entries
.append(entry
)
114 class Builder(base
.DataObject
):
117 def __eq__(self
, other
):
118 if isinstance(other
, self
.__class
__):
119 return self
.id == other
.id
121 def __lt__(self
, other
):
122 if isinstance(other
, self
.__class
__):
123 return self
.name
< other
.name
125 def log(self
, action
, user
=None):
130 self
.db
.execute("INSERT INTO builders_history(builder_id, action, user_id, time) \
131 VALUES(%s, %s, %s, NOW())", self
.id, action
, user_id
)
133 def regenerate_passphrase(self
):
135 Generates a new random passphrase and stores it as a salted hash
138 The new passphrase is returned to be sent to the user (once).
140 # Generate a random string with 40 chars.
141 passphrase
= generate_random_string(length
=40)
143 # Create salted hash.
144 passphrase_hash
= generate_password_hash(passphrase
)
146 # Store the hash in the database.
147 self
._set
_attribute
("passphrase", passphrase_hash
)
149 # Return the clear-text passphrase.
152 def validate_passphrase(self
, passphrase
):
154 Compare the given passphrase with the one stored in the database.
156 return check_password_hash(passphrase
, self
.data
.passphrase
)
160 def set_description(self
, description
):
161 self
._set
_attribute
("description", description
)
163 description
= property(lambda s
: s
.data
.description
or "", set_description
)
168 Returns time of last keepalive message from this host.
170 return self
.data
.time_keepalive
172 def update_keepalive(self
, loadavg1
=None, loadavg5
=None, loadavg15
=None,
173 mem_total
=None, mem_free
=None, swap_total
=None, swap_free
=None,
176 Update the keepalive timestamp of this machine.
178 self
.db
.execute("UPDATE builders SET time_keepalive = NOW(), \
179 loadavg1 = %s, loadavg5 = %s, loadavg15 = %s, space_free = %s, \
180 mem_total = %s, mem_free = %s, swap_total = %s, swap_free = %s \
181 WHERE id = %s", loadavg1
, loadavg5
, loadavg15
, space_free
,
182 mem_total
, mem_free
, swap_total
, swap_free
, self
.id)
184 def update_info(self
, cpu_model
=None, cpu_count
=None, cpu_arch
=None, cpu_bogomips
=None,
185 pakfire_version
=None, host_key
=None, os_name
=None):
186 # Update all the rest.
187 self
.db
.execute("UPDATE builders SET time_updated = NOW(), \
188 pakfire_version = %s, cpu_model = %s, cpu_count = %s, cpu_arch = %s, \
189 cpu_bogomips = %s, host_key_id = %s, os_name = %s WHERE id = %s",
190 pakfire_version
, cpu_model
, cpu_count
, cpu_arch
, cpu_bogomips
,
191 host_key
, os_name
, self
.id)
193 def set_enabled(self
, enabled
):
194 self
._set
_attribute
("enabled", enabled
)
196 enabled
= property(lambda s
: s
.data
.enabled
, set_enabled
)
200 return not self
.enabled
203 def native_arch(self
):
205 The native architecture of this builder
210 def supported_arches(self
):
211 # Every builder supports noarch
214 # We can always build our native architeture
216 arches
.append(self
.native_arch
)
218 # Get all compatible architectures
219 res
= self
.db
.query("SELECT build_arch FROM arches_compat \
220 WHERE native_arch = %s", self
.native_arch
)
223 if not row
.build_arch
in arches
:
224 arches
.append(row
.build_arch
)
226 return sorted(arches
)
228 def set_testmode(self
, testmode
):
229 self
._set
_attribute
("testmode", testmode
)
231 testmode
= property(lambda s
: s
.data
.testmode
, set_testmode
)
233 def set_max_jobs(self
, value
):
234 self
._set
_attribute
("max_jobs", value
)
236 max_jobs
= property(lambda s
: s
.data
.max_jobs
, set_max_jobs
)
240 return self
.data
.name
247 def passphrase(self
):
248 return self
.data
.passphrase
254 return ", ".join(["%.2f" % l
for l
in (self
.loadavg1
, self
.loadavg5
, self
.loadavg15
)])
258 return self
.data
.loadavg1
or 0.0
262 return self
.data
.loadavg5
or 0.0
266 return self
.data
.loadavg15
or 0.0
269 def pakfire_version(self
):
270 return self
.data
.pakfire_version
or ""
274 return self
.data
.os_name
or ""
278 return self
.data
.cpu_model
or ""
282 return self
.data
.cpu_count
286 return self
.data
.cpu_arch
289 def cpu_bogomips(self
):
290 return self
.data
.cpu_bogomips
or 0.0
293 def mem_percentage(self
):
294 if not self
.mem_total
:
297 return self
.mem_used
* 100 / self
.mem_total
301 return self
.data
.mem_total
305 if self
.mem_total
and self
.mem_free
:
306 return self
.mem_total
- self
.mem_free
310 return self
.data
.mem_free
313 def swap_percentage(self
):
314 if not self
.swap_total
:
317 return self
.swap_used
* 100 / self
.swap_total
320 def swap_total(self
):
321 return self
.data
.swap_total
325 if self
.swap_total
and self
.swap_free
:
326 return self
.swap_total
- self
.swap_free
330 return self
.data
.swap_free
333 def space_free(self
):
334 return self
.data
.space_free
337 def host_key_id(self
):
338 return self
.data
.host_key_id
345 if self
.data
.time_keepalive
is None:
348 #if self.data.updated >= 5*60:
354 def active_jobs(self
, *args
, **kwargs
):
355 return self
.pakfire
.jobs
.get_active(builder
=self
, *args
, **kwargs
)
358 def too_many_jobs(self
):
360 Tell if this host is already running enough or too many jobs.
362 return len(self
.active_jobs
) >= self
.max_jobs
366 return self
.backend
.jobqueue
.for_arches(self
.supported_arches
)
368 def get_next_job(self
):
370 Returns the next job in line for this builder.
372 # Don't send any jobs to disabled builders
376 # Don't return anything if the builder has already too many jobs running
377 if self
.too_many_jobs
:
380 for job
in self
.jobqueue
:
381 # Only allow building test jobs in test mode
382 if self
.testmode
and not job
.test
:
387 def get_history(self
, *args
, **kwargs
):
388 kwargs
["builder"] = self
390 return self
.pakfire
.builders
.get_history(*args
, **kwargs
)