+++ /dev/null
-#!/usr/bin/python
-###############################################################################
-# #
-# IPFire.org - A linux based firewall #
-# Copyright (C) 2008 Michael Tremer & Christian Schmidt #
-# #
-# This program is free software: you can redistribute it and/or modify #
-# it under the terms of the GNU General Public License as published by #
-# the Free Software Foundation, either version 3 of the License, or #
-# (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program. If not, see <http://www.gnu.org/licenses/>. #
-# #
-###############################################################################
-
-import os
-import sys
-import time
-import socket
-import base64
-import shutil
-from pysqlite2 import dbapi2 as sqlite
-
-sys.path.append(".")
-
-from constants import config
-
-class Database:
- def __init__(self, path):
- self.db = sqlite.connect(os.path.join(path, config["db_name"]))
- c = self.cursor()
- c.executescript("""
- create table if not exists config(key, value, date);
- create table if not exists durations(duration);
- """)
- c.close()
-
- def __call__(self):
- return self.cursor()
-
- def __del__(self):
- self.commit()
- self.db.close()
-
- def cursor(self):
- return self.db.cursor()
-
- def commit(self):
- self.db.commit()
-
-class DatabaseConfig:
- def __init__(self, db, key, base64=0):
- self.db = db
- self.key = key
- self.data = None
- self.date = None
- self.base64 = base64
-
- def get(self):
- if not self.data:
- c = self.db.cursor()
- c.execute("SELECT value FROM %(table)s WHERE key = '%(key)s'" \
- % { "table" : "config",
- "key" : self.key, })
- try:
- self.data = c.fetchone()[0]
- except TypeError:
- self.data = None
- c.close()
- return self.data
-
- __call__ = get
-
- def time(self):
- if not self.date:
- c = self.db.cursor()
- c.execute("SELECT date FROM %(table)s WHERE key = '%(key)s'" \
- % { "table" : "config",
- "key" : self.key, })
- try:
- self.date = float("%s" % c.fetchone()[0])
- except TypeError:
- self.date = None
- c.close()
- return self.date or float(0)
-
- def set(self, value):
- if self.base64:
- value = base64.b64decode(value)
- #value = (value,)
- c = self.db.cursor()
- if not self.get():
- sql = "INSERT INTO %(table)s(key, value, date) VALUES('%(key)s', '%(value)s', '%(date)s')" \
- % { "table" : "config",
- "key" : self.key,
- "value" : value,
- "date" : time.time(), }
-
- else:
- sql = "UPDATE %(table)s SET value='%(value)s', date='%(date)s' WHERE key='%(key)s'" \
- % { "table" : "config",
- "key" : self.key,
- "value" : value,
- "date" : time.time(), }
- c.execute(sql)
- c.close()
- self.data = value
- self.db.commit()
- return """Set "%s" to "%s".""" % (self.key, self.data,)
-
-class DurationsConfig:
- def __init__(self, db):
- self.db = db
-
- def get(self, sort=0):
- c = self.db.cursor()
- c.execute("SELECT duration FROM durations")
- ret = []
- for value in c.fetchall():
- value = int("%s" % value)
- if value < 900: # 15min
- continue
- ret.append(value)
- c.close()
- if sort: ret.sort()
- return ret
-
- def set(self, value):
- #value = (value,)
- c = self.db.cursor()
- c.execute("INSERT INTO %(table)s(duration) VALUES('%(value)s')" \
- % { "table" : "durations",
- "value" : value, })
- c.close()
- self.db.commit()
- return """Accepted build duration of %s seconds.""" % (value,)
-
- def get_avg(self):
- sum = 0
- durations = self.get()
- if not len(durations):
- return None
- for value in durations:
- sum += value
- avg = sum / len(durations)
- return avg
-
- def get_eta(self, timestamp):
- avg = self.get_avg()
- if not avg:
- return "N/A"
- eta = int(timestamp) + avg
- return time.ctime(eta)
-
-class DistccConfig(DatabaseConfig):
- def __init__(self, db, key, hostname, jobs):
- DatabaseConfig.__init__(self, db, key)
- self.hostname = hostname
- self.jobs = jobs
-
- def __str__(self):
- if not self.ping() or self.get() == "0":
- return ""
- return "%s:%s/%s,lzo" % \
- (self.hostname, self.get(), self.jobs or "4",)
-
- def ping(self):
- if not self.hostname:
- return False
- return not os.system("ping -c1 -w1 %s &>/dev/null" % self.hostname)
-
- def version(self):
- return os.popen("distcc --version").readlines()
-
-class FileConfig:
- def __init__(self, path, filetype):
- self.filename = os.path.join(path, config["path"][filetype])
-
- # Create the file if not existant
- if not os.access(self.filename, os.R_OK):
- f = open(self.filename, "w")
- f.close()
-
- def get(self):
- ret = []
- try:
- f = open(self.filename)
- ret = f.readlines()
- f.close()
- except:
- pass
- return ret or ["Log is empty."]
-
- __call__ = get
-
- def set(self, lines):
- f = open(self.filename, "w")
- for line in base64.b64decode(lines).split("\n"):
- f.write("%s\n" % line.rstrip("\n"))
- f.close()
- return """Saved file content to %s.""" % (self.filename,)
-
-class Builder:
- def __init__(self, config, uuid):
- self.uuid = uuid
- self.config = config
- self.path = os.path.join(self.config['path']['db'], self.uuid)
-
- if not os.access(self.path, os.R_OK):
- try:
- os.mkdir(self.path)
- except:
- pass
-
- self.db = Database(self.path)
-
- self.hostname = DatabaseConfig(self.db, "hostname")
- self.state = DatabaseConfig(self.db, "state")
- self.package = DatabaseConfig(self.db, "package")
- self.target = DatabaseConfig(self.db, "target")
- self.toolchain= DatabaseConfig(self.db, "toolchain")
-
- self.duration = DurationsConfig(self.db)
- self.jobs = DatabaseConfig(self.db, "jobs")
- self.distcc = DistccConfig(self.db, "distcc", self.hostname(), self.jobs())
- self.cpu = DatabaseConfig(self.db, "cpu", base64=1)
- self.machine = DatabaseConfig(self.db, "machine")
- self.system = DatabaseConfig(self.db, "system", base64=1)
-
- self.log = FileConfig(self.path, "log")
-
- # If host was longer than 3 days in state compiling we set it as idle.
- if self.state() == "compiling" and \
- (time.time() - self.state.time()) > 3*24*60*60:
- self.state.set("idle")
-
- # If host is idle and distcc is not disabled we set it as distcc host.
- if self.state() == "idle" and self.distcc() != "0":
- self.state.set("distcc")
-
- # If host is longer than 24h in error state we set it as distcc host.
- if self.state() == "error" and \
- (time.time() - self.state.time()) > 24*60*60:
- self.state.set("distcc")
-
- # If host was longer than two weeks in distcc state we set it as unknown.
- if self.state() == "error" and \
- (time.time() - self.state.time()) > 2*7*24*60*60:
- self.state.set("unknown")
-
- # If host was longer than four weels in distcc state we delete it.
- if self.state() in ("distcc", "unknown",) and \
- (time.time() - self.state.time()) > 4*7*24*60*60:
- del self.db
- shutil.rmtree(self.path)
-
- def set(self, key, value):
- return eval("self.%s.set(\"%s\")" % (key, value,))
-
- def get(self, key):
- return eval("self.%s.get()" % (key,))
-
-def getAllBuilders(age=0):
- builders = []
- for uuid in os.listdir(config["path"]["db"]):
- if uuid == "empty.txt": continue
- builder = Builder(config, uuid)
- # If there was no activity since "age" days -> continue...
- if age and (time.time() - builder.state.time()) > age*24*60*60:
- continue
- builders.append(builder)
- return builders
+++ /dev/null
-#!/usr/bin/python
-###############################################################################
-# #
-# IPFire.org - A linux based firewall #
-# Copyright (C) 2008 Michael Tremer & Christian Schmidt #
-# #
-# This program is free software: you can redistribute it and/or modify #
-# it under the terms of the GNU General Public License as published by #
-# the Free Software Foundation, either version 3 of the License, or #
-# (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program. If not, see <http://www.gnu.org/licenses/>. #
-# #
-###############################################################################
-
-import os
-import time
-
-POINTS_UNKNOWN = 0
-POINTS_IDLE = 1
-POINTS_DISTCC = 2
-POINTS_ERROR = 4
-POINTS_COMPILING = 8
-
-config = {
- "title" : "IPFire - Builder",
- "nightly_url" : ("http://ftp.ipfire.org/pub/nightly-builds/", "http://www.rowie.at/upload/ipfire/builds/",),
- "path" : { "db" : "db", "log" : "error.log", },
- "script" : os.environ['SCRIPT_NAME'],
- "db_name" : "builder.db",
-}
-
-statedesc = {
- None : "",
- "unknown" : "Dunno what the host is doing at the moment...",
- "compiling" : "The host is really hard working at the moment...",
- "error" : "Oops! The host had an error...",
- "idle" : "The host is idle at the moment...",
- "distcc" : "This host is waiting for distcc requests...",
-}
-
-ping2class = {
- True : "online",
- False : "offline",
-}
-
-state2style = {
- None : "",
- "compiling" : "background: #8C8; border: 1px solid #0e0;",
- "distcc" : "background: #58c; border: 1px solid #8ac;",
- "error" : "background: #c33; border: 1px solid #e00;",
- "idle" : "background: #ddd; border: 1px solid #eee;",
-}
+++ /dev/null
-#!/usr/bin/python
-###############################################################################
-# #
-# IPFire.org - A linux based firewall #
-# Copyright (C) 2008 Michael Tremer & Christian Schmidt #
-# #
-# This program is free software: you can redistribute it and/or modify #
-# it under the terms of the GNU General Public License as published by #
-# the Free Software Foundation, either version 3 of the License, or #
-# (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program. If not, see <http://www.gnu.org/licenses/>. #
-# #
-###############################################################################
-
-import os
-import sys
-import time
-
-sys.path.append(".")
-
-from builder import Builder, getAllBuilders
-from constants import *
-
-def format_time(seconds, use_hours=1):
- if seconds is None or seconds < 0:
- if use_hours: return '--:--:--'
- else: return '--:--'
- else:
- seconds = int(seconds)
- minutes = seconds / 60
- seconds = seconds % 60
- if use_hours:
- hours = minutes / 60
- minutes = minutes % 60
- return '%02i:%02i:%02i' % (hours, minutes, seconds)
- else:
- return '%02i:%02i' % (minutes, seconds)
-
-class Site:
- def __init__(self, config):
- self.builders = None
- self.config = config
- print "Content-type: text/html"
- print
-
- def __call__(self, builders=None):
- self.builders = builders
- print """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
- <head>
- <title>%(title)s</title>
- <style type="text/css">
- * {
- margin: auto;
- padding: 0;
- text-decoration: none;
- outline: none;
- }
- body {
- font-family: Verdana;
- font-size: 90%%;
- background-color:#f9f9f9;
- }
- h1 {
- text-decoration: underline;
- }
- #header {
- width: 800px;
- text-align: center;
- background: #E0E0E0;
- border: 1px solid #999;
- padding-top: 10px;
- padding-bottom: 5px;
- }
- #content {
- padding-top: 10px;
- width: 800px;
- }
- div.box {
- padding: 5px;
- margin: 10px 0 10px 0;
- /* height: 80px; */
- border: 1px solid;
- }
- div.infobox {
- float: right;
- width: 240px;
- }
- div.log {
- background: #e55;
- border: 1px dotted;
- margin-top: 12px;
- /* visibility: hidden; */
- height: 150px;
- overflow: auto;
- }
- div.log p {
- font-family: Courier New;
- }
- div.footer {
- }
- div.footer p {
- text-align: center;
- font-size: 5px;
- }
- p {
- margin: 2px;
- }
- p.boxheader {
- font-weight: bold;
- }
- p.online {
- color: green;
- }
- p.offline {
- color: red;
- }
- p.package {
- font: bold;
- font-size: 150%%;
- }
- img.right {
- float: right;
- }
- p.desc {
- text-decoration: bold;
- }
- a:link {
- color: black; text-decoration: none;
- }
- a:visited {
- color: black; text-decoration: none;
- }
- </style>
- <meta http-equiv="refresh" content="90; URL=%(script)s" />
- </head>
- <body>
- <div id="header">
- <h1>IPFire Builder</h1>""" % self.config
- for i in self.config["nightly_url"]:
- print """\
- <p><a href="%s" target="_blank">%s</a></p>""" % (i, i,)
- print """\
- </div>"""
-
- self.content()
-
- print "\t\t</body>\n</html>"
-
- def content(self):
- if self.builders:
- print """\
- <div id="content">"""
- for builder in self.builders:
- builder()
- print """\
- </div>"""
-
-class Box:
- def __init__(self, builder):
- self.builder = builder
- self.points = POINTS_UNKNOWN
-
- def __cmp__(self, other):
- if self.points > other.points:
- return -1
- elif self.points == other.points:
- return 0
- elif self.points < other.points:
- return 1
-
- def __str__(self):
- return """<a href="#%(hostname)s">%(hostname)s</a>""" % { "hostname" : self.builder.hostname(), }
-
- def open_bigbox(self):
- print """<a name="%s"></a><div class="box" style="%s">""" \
- % (self.builder.hostname(), state2style[self.builder.state()],)
-
- def open_infobox(self):
- print """<div class="infobox">"""
-
- def close_bigbox(self):
- print """</div>"""
-
- close_infobox = close_bigbox
-
- def header(self):
- print """<p class="boxheader">%(hostname)s <span>[%(uuid)s]</span></p>""" \
- % { "hostname" : self.builder.hostname(),
- "state" : self.builder.state(),
- "uuid" : self.builder.uuid, }
-
- def package(self):
- if self.builder.state() in [ "compiling", "error", ]:
- print """\
- <p class="package">%s</p>"""\
- % self.builder.package()
-
- def time(self):
- print """<p>%s</p>""" \
- % time.ctime(float(self.builder.state.time()))
-
- def stateinfo(self):
- print """<p class="desc">%s</p>""" \
- % statedesc[self.builder.state()]
-
- def durations(self):
- print """<p>Average Build Duration: %s</p>""" \
- % format_time(self.builder.duration.get_avg())
- if self.builder.state() == "compiling":
- print """<p>ETA: %s</p>""" \
- % self.builder.duration.get_eta(self.builder.state.time())
-
- def distccinfo(self):
- state = self.builder.distcc.ping()
- port = self.builder.distcc()
- if port == "0":
- state = False
- port = "disabled"
- print """<p class="%s">Distcc: %s</p>""" \
- % (ping2class[state], port,)
-
- def log(self):
- log = self.builder.log()
- if log:
- print """<div class="log"><p>"""
- print "<br />".join(log)
- print """</p></div>"""
-
- def footer(self):
- print """<div class="footer"><p>system: %s - cpu: %s <br /> machine: %s - target: %s - jobs: %s - toolchain: %s</p></div>""" \
- % (self.builder.system(), self.builder.cpu(), self.builder.machine(), self.builder.target(), self.builder.jobs(), self.builder.toolchain(),)
-
-class BoxCompiling(Box):
- def __init__(self, builder):
- Box.__init__(self, builder)
- self.points = POINTS_COMPILING
-
- def __call__(self):
- self.open_bigbox()
- self.open_infobox()
- self.distccinfo()
- self.package()
- self.time()
- self.close_infobox()
- self.header()
- self.stateinfo()
- self.durations()
- self.footer()
- self.close_bigbox()
-
-class BoxError(Box):
- def __init__(self, builder):
- Box.__init__(self, builder)
- self.points = POINTS_ERROR
-
- def __call__(self):
- self.open_bigbox()
- self.open_infobox()
- self.distccinfo()
- self.package()
- self.time()
- self.close_infobox()
- self.header()
- self.stateinfo()
- self.durations()
- self.log()
- self.footer()
- self.close_bigbox()
-
-class BoxDistcc(Box):
- def __init__(self, builder):
- Box.__init__(self, builder)
- self.points = POINTS_DISTCC
-
- def __call__(self):
- self.open_bigbox()
- self.open_infobox()
- self.distccinfo()
- self.time()
- self.close_infobox()
- self.header()
- self.stateinfo()
- self.durations()
- self.footer()
- self.close_bigbox()
-
-class BoxIdle(Box):
- def __init__(self, builder):
- Box.__init__(self, builder)
- self.points = POINTS_IDLE
-
- def __call__(self):
- self.open_bigbox()
- self.open_infobox()
- self.distccinfo()
- self.time()
- self.close_infobox()
- self.header()
- self.stateinfo()
- self.durations()
- self.footer()
- self.close_bigbox()
-
-site = Site(config)
-
-boxes = []
-for builder in getAllBuilders():
- box = None
- if builder.state() == "compiling":
- box = BoxCompiling(builder)
- elif builder.state() == "error":
- box = BoxError(builder)
- elif builder.state() == "idle":
- box = BoxIdle(builder)
- elif builder.state() == "distcc":
- if builder.distcc() == "0":
- continue
- box = BoxDistcc(builder)
- if box:
- boxes.append(box)
-
-boxes.sort()
-site(boxes)
+++ /dev/null
-#!/usr/bin/python
-###############################################################################
-# #
-# IPFire.org - A linux based firewall #
-# Copyright (C) 2008,2009 Michael Tremer & Christian Schmidt #
-# #
-# This program is free software: you can redistribute it and/or modify #
-# it under the terms of the GNU General Public License as published by #
-# the Free Software Foundation, either version 3 of the License, or #
-# (at your option) any later version. #
-# #
-# This program is distributed in the hope that it will be useful, #
-# but WITHOUT ANY WARRANTY; without even the implied warranty of #
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
-# GNU General Public License for more details. #
-# #
-# You should have received a copy of the GNU General Public License #
-# along with this program. If not, see <http://www.gnu.org/licenses/>. #
-# #
-###############################################################################
-
-import os
-import sys
-import cgi
-
-sys.path.append(".")
-
-from builder import Builder, getAllBuilders
-from constants import config
-
-ALLOWED_ACTIONS_SET = ( "distcc", "duration", "hostname", "jobs", "log", "state",
- "package", "target", "toolchain", "cpu", "machine", "system",)
-ALLOWED_ACTIONS_GET = ( "distcc",)
-
-def run(uuid, action):
- myself = Builder(config, uuid)
-
- if action == "get":
- for key in ALLOWED_ACTIONS_GET:
- if key == "distcc":
- for value in data.getlist(key):
- if value == "raw":
- builders = getAllBuilders()
- print "--randomize"
- for builder in builders:
- # Print "localhost" for ourself
- if myself.uuid == builder.uuid:
- print "localhost"
- else:
- if ((myself.toolchain() == builder.toolchain()) and \
- (myself.machine() == builder.machine()) and \
- (myself.target() == builder.target())):
- print "%s" % (builder.distcc,)
-
- elif action == "set":
- for key in ALLOWED_ACTIONS_SET:
- for value in data.getlist(key):
- print myself.set(key, value)
-
-data = cgi.FieldStorage()
-
-print "Status: 200 - OK" # We always send okay.
-print
-
-try:
- uuid = data.getfirst("uuid")
- action = data.getfirst("action")
- if uuid and action:
- run(uuid, action)
-except SystemExit:
- pass