]> git.ipfire.org Git - ipfire.org.git/commitdiff
Imported other web components.
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 17 Jun 2008 15:28:32 +0000 (17:28 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 17 Jun 2008 15:28:32 +0000 (17:28 +0200)
12 files changed:
build/build.py [new file with mode: 0644]
build/distcc.py [new file with mode: 0644]
build/hosts [new file with mode: 0644]
build/images/compiling.png [new file with mode: 0644]
build/images/error.png [new file with mode: 0644]
build/images/idle.png [new file with mode: 0644]
build/images/unknown.png [new file with mode: 0644]
build/probe.c [new file with mode: 0644]
download/getfile.py [new file with mode: 0644]
download/mirrorlist [new file with mode: 0644]
pakfire/counter.py [new file with mode: 0644]
pakfire/show.py [new file with mode: 0644]

diff --git a/build/build.py b/build/build.py
new file mode 100644 (file)
index 0000000..edaf98c
--- /dev/null
@@ -0,0 +1,379 @@
+#!/usr/bin/python
+
+import os
+import time
+import cgi
+
+from stat import ST_MTIME
+from rhpl.simpleconfig import SimpleConfigFile
+
+DB_DIR = "db"
+
+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)
+
+def format_number(number, SI=0, space=' '):
+       """Turn numbers into human-readable metric-like numbers"""
+       symbols = ['',  # (none)
+                  'k', # kilo
+                  'M', # mega
+                  'G', # giga
+                  'T', # tera
+                  'P', # peta
+                  'E', # exa
+                  'Z', # zetta
+                  'Y'] # yotta
+    
+       if SI: step = 1000.0
+       else: step = 1024.0
+
+       thresh = 999
+       depth = 0
+       max_depth = len(symbols) - 1
+    
+       # we want numbers between 0 and thresh, but don't exceed the length
+       # of our list.  In that event, the formatting will be screwed up,
+       # but it'll still show the right number.
+       while number > thresh and depth < max_depth:
+               depth  = depth + 1
+               number = number / step
+
+       if type(number) == type(1) or type(number) == type(1L):
+               # it's an int or a long, which means it didn't get divided,
+               # which means it's already short enough
+               format = '%i%s%s'
+       elif number < 9.95:
+               # must use 9.95 for proper sizing.  For example, 9.99 will be
+               # rounded to 10.0 with the .1f format string (which is too long)
+               format = '%.1f%s%s'
+       else:
+               format = '%.0f%s%s'
+        
+       return(format % (float(number or 0), space, symbols[depth]))
+
+stage2desc = {
+       "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...",
+}
+
+sys2desc = {
+       "CPU_NAME" : "CPU model",
+       "CPU_MIPS" : "Bogomips",
+       "CPU_MHZ"  : "CPU MHz",
+       "MEM_SIZE" : "Memory size",
+} 
+
+class Site:
+       def __init__(self):
+               print "Content-type: text/html"
+               print   
+       
+       def __call__(self):
+               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>IPFire - Builder</title>
+                       <style type="text/css">
+                               body { font-family: Verdana; font-size: 9pt; background-color:#f0f0f0; }
+                               a:link { color: black; text-decoration: none; }
+                               a:visited { color: black; text-decoration: none; }
+                               table { font-family: Verdana; font-size: 8pt; border: 1px solid; }
+                               td.header { background-color: #a0a0a0; text-align: left; color: white; 
+                                               font-size: 11pt; }
+                               td.date { text-align: center; }
+                               font.installed { color: green; }
+                               font.deleted { color: red; }
+                               font.update { color: blue; }
+                               font.error { color: red; }
+                               td.log { background: pink; }
+                               td.update { background: darkgrey; }
+                               /* td.distcc { background: darkgrey; } */
+                               td.online { background: green; color: white; text-align: center; }
+                               td.offline { background: red; color: white; text-align: center; }
+                       </style>
+                       <meta http-equiv="refresh" content="90; URL=%s" />
+               </head>
+               <body>
+                       <h1>IPFire Builder</h1>
+                       <p>This site is for monitoring the build machines...</p>
+                       <p>Made: %s</p>''' % (os.environ['SCRIPT_NAME'], time.ctime())
+
+               self.content()
+
+               print "\t\t</body>\n</html>"
+       
+       def content(self):
+               for builder in builders:
+                       builder()
+
+
+class Builder:
+       def __init__(self, uuid=None):
+               self.uuid = uuid
+               self.db = os.path.join(DB_DIR, self.uuid)
+               
+               if not os.path.isdir(self.db):
+                       os.mkdir(self.db)
+               
+               self.fn = {
+                       'state' : os.path.join(self.db, "state"),
+                       'distcc' : os.path.join(self.db, "distcc"),
+                       'hostname' : os.path.join(self.db, "hostname"),
+                       'build_durations' : os.path.join(self.db, "build_durations"),
+                       'log' : os.path.join(self.db, "log"),
+                       'profile' : os.path.join(self.db, "profile"),
+               }
+               
+               self.sys = SimpleConfigFile()
+               self.sys.read(self.fn['profile'])
+               
+               self.state = self._state()
+       
+       def _state(self, state="unknown"):
+               """State says what the host is doing at the moment.
+                       This can be:
+                        - unknown      -       Didn't talk with host for more than two hours.
+                        - compiling    -       Host is working
+                        - error        -       Host had an error
+                        - idle         -       Host is idle
+                        - hidden       -       Host was idle or unknown for more than 12 hours"""
+               
+               if not state == "unknown":
+                       f = open(self.fn['state'], "w")
+                       f.write("%s\n" % state)
+                       f.close()
+
+               if not os.access(self.fn['state'], os.R_OK):
+                       return state
+               
+               six_h_ago = int(time.time()) - (3600 * 6)
+               twelve_h_ago = int(time.time()) - (3600 * 12)
+               mtime = os.stat(self.fn['state'])[ST_MTIME]
+               
+               if mtime > six_h_ago:
+                       try:
+                               f = open(self.fn['state'])
+                               state = f.readline().rstrip("\n")
+                               f.close()
+                       except:
+                               pass
+               elif mtime > twelve_h_ago:
+                       state = "hidden"
+
+               return state
+       
+       def _build_durations(self, duration=None):
+               """This returns a 4x-tupel:
+                       First value is best build duration.
+                       Second value is average build duration.
+                       Third value is worst build duration.
+                       Fourth value is the whole build duration."""
+               
+               ## set duration
+               if duration:
+                       f = open(self.fn['build_durations'], "a")
+                       f.write("%s\n" % int(duration))
+                       f.close()
+                       return
+               
+               ## get duration
+               durations = []
+               all_build_duration = 0
+               try:
+                       f = open(self.fn['build_durations'])
+               except IOError:
+                       return (None, None, None, None)
+               else:
+                       while True:
+                               duration = f.readline().rstrip("\n")
+                               if not duration:
+                                       break # EOF
+                               durations.append(int(duration))
+                       f.close()
+                       durations.sort()
+                       for duration in durations:
+                               if duration < 3600: continue
+                               all_build_duration += duration
+                       
+                       avg = all_build_duration / len(durations)
+                       
+                       return (durations[0], avg, durations[-1], all_build_duration)
+       
+       def _log(self, log=[]):
+               
+               if log:
+                       f = open(self.fn['log'], "w")
+                       f.write("%s\n" % log)
+                       f.close()
+                       return
+               
+               try:
+                       f = open(self.fn['log'])
+                       log = f.readlines()
+                       f.close()
+               except:
+                       pass
+               
+               return log
+       
+       def _last_update(self):
+               if not os.access(self.fn['state'], os.R_OK):
+                       return
+
+               return os.stat(self.fn['state'])[ST_MTIME]
+       
+       def _profile_get(self, what=None):
+               data = self.sys.get(what)
+               if data and what.endswith("_SIZE"):
+                       data = format_number(int(data))
+               return data or None
+       
+       def _profile_set(self, what):
+               self.sys.set(what)
+               self.sys.write(self.fn['profile'])
+
+       def __repr__(self):
+               return "<Builder %s>" % self.uuid
+       
+       def __call__(self):
+               if self.state == "hidden":
+                       return
+               
+               self.hostname = self._profile_get("HOSTNAME")
+               self.distcc = self._profile_get("DISTCC")
+               self.build_durations = self._build_durations()
+               self.last_update = self._last_update()
+
+               print "<table width='66%'>"
+               
+               ## give hostname or uuid
+               print "\t<tr>"
+               print "\t\t<td class='header' colspan='3' width='100%'>",
+               if self.hostname:
+                       print self.hostname,
+               else:
+                       print self.uuid,
+               print "</td>"
+               print "\t</tr>"
+               
+               ## give state
+               print "\t<tr>"
+               print "\t\t<td class='state' colspan='2' width='80%'><b>",
+               print stage2desc[self.state],
+               print "</b></td>"
+               print "\t\t<td class='state' rowspan='7' width='20%'>",
+               print "<img alt='%s' width='128px' height='128px' src='/images/%s.png' />" % (self.state, self.state,),
+               print "</td>"
+               print "\t</tr>"
+               
+               ## give sys info
+               for key in [ "CPU_NAME", "CPU_MHZ", "CPU_MIPS", "MEM_SIZE", ]:
+                       print "\t<tr>"
+                       print "\t\t<td class='sys' width='60%'><b>",
+                       print sys2desc[key]
+                       print "</b></td>"
+                       print "\t\t<td class='durations' width='40%'>",
+                       print self._profile_get(key) or "N/A"
+                       print "</td>"
+                       print "\t</tr>"
+               
+               ## give durations
+               (min, avg, max, all) = self.build_durations
+               print "\t<tr>"
+               print "\t\t<td class='durations' width='60%'>",
+               print "<b>Build durations</b>",
+               print "</td>"
+               print "\t\t<td class='durations' width='40%'>",
+               if avg:
+                       print "<b>Average:</b> %s h<br />" % format_time(avg),
+               if min:
+                       print "<b>Minimum:</b> %s h<br />" % format_time(min),
+               if max:
+                       print "<b>Maximum:</b> %s h<br />" % format_time(max),
+               if all:
+                       print "<b>As a whole:</b> %s h" % format_time(all),
+               
+               if not avg and not min and not max and not all:
+                       print "N/A",
+               
+               print "</td>"
+               print "\t</tr>"
+               
+               ## give distcc
+               print "\t<tr>"
+               print "\t\t<td class='distcc' width='60%'>",
+               print "<b>Distcc capable</b>",
+               print "</td>"
+               print "\t\t<td class='distcc' width='40%'>",
+               if self.distcc == None or self.distcc == "0":
+                       print "No",
+               else:
+                       print "Yes (port: %s)" \
+                               % (self.distcc),
+               print "</td>"
+               print "\t</tr>"
+               
+               ## give log
+               if self.state == "error":
+                       print "\t<tr>"
+                       print "\t\t<td class='log' colspan='3' width='100%'>",
+                       for line in self._log():
+                               print "%s<br/>" % (line,)
+                       print "</td>"
+                       print "\t</tr>"
+               
+               ## give lastupdate
+               if self.last_update:
+                       print "\t<tr>"
+                       print "\t\t<td class='update' colspan='3' width='100%'>",
+                       print "Last update:",
+                       print time.strftime("%Y-%b-%d %I:%M %p", time.localtime(self.last_update)),
+                       print " - ",
+                       print format_time(int(time.time() - self.last_update)),
+                       print "ago </td>"
+                       print "\t</tr>"
+               
+               print "</table>"
+               
+               print "<br />"
+
+form = cgi.FieldStorage()
+action  = form.getfirst('action')
+
+if action in [ "compiling", "error", "idle", "set" ]:
+       builder = Builder(form.getfirst('uuid'))
+       if not action == "set":
+               builder._state(action)
+       if action == "set":
+               key = form.getfirst('key')
+               val = form.getfirst('val')
+               if key == "duration":
+                       builder._build_durations(val)
+               else:
+                       builder._profile_set((key, val))
+       elif action == "error":
+               log = form.getfirst('log')
+               builder._log(log)
+else:
+       builders = []
+       for uuid in os.listdir(DB_DIR):
+               if not os.path.isdir(os.path.join(DB_DIR, uuid)): continue
+               builders.append(Builder(uuid))
+       
+       site = Site()
+       site()
diff --git a/build/distcc.py b/build/distcc.py
new file mode 100644 (file)
index 0000000..5373e5a
--- /dev/null
@@ -0,0 +1,123 @@
+#!/usr/bin/python
+
+import cgitb, os, time, cgi, re, random, socket, DNS
+cgitb.enable()
+
+def print_http_header():
+       print "Content-type: text/html"
+       print
+
+def print_header():
+       print_http_header()     
+       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>IPFire - DistccWatch</title>
+               <style type="text/css">
+                       body { font-family: Verdana; font-size: 9pt; background-color:#f0f0f0; }
+                       a:link { color: black; text-decoration: none; }
+                       a:visited { color: black; text-decoration: none; }
+                       table { font-family: Verdana; font-size: 8pt; border: 1px solid; }
+                       td.header { background-color: #a0a0a0; text-align: center; color: white; }
+                       td.date { text-align: center; }
+                       font.installed { color: green; }
+                       font.deleted { color: red; }
+                       font.update { color: blue; }
+                       font.error { color: red; }
+                       td.online { background: green; color: white; text-align: center; }
+                       td.offline { background: red; color: white; text-align: center; }
+               </style>
+               <meta http-equiv="refresh" content="60; URL=%s" />
+       </head>
+       <body>
+               <h1>IPFire DistccWatch</h1>
+               <p>Made: %s</p>''' % (os.environ['SCRIPT_NAME'], time.ctime())
+               
+def print_footer():
+       print "\t</body>\n</html>"
+               
+def print_dcc_info():
+       print "<pre>",
+       print os.popen("distcc --version").read(),
+       print "</pre>"
+
+def read_hosts():
+       f = open("hosts")
+       hosts = []
+       while True:
+               line = f.readline()
+               if len(line) == 0:
+                       hosts.sort()
+                       return hosts
+                       break #EOF
+               
+               if not line.startswith("#"):
+                       hosts.append(line.rstrip("\n"))
+
+def process_hosts(hosts=""):
+       if not hosts:
+               print "You need to specify the hosts you want to check in the config file."
+               return
+               
+       print '''<table width='66%'>
+       <tr>
+                       <td class='header' width='68%'>Host</td>
+                       <td class='header' width='16%'>State</td>
+                       <td class='header' width='16%'>Probe</td>
+       </tr>'''
+       
+       for hostname in hosts:
+       
+               state = "DOWN"
+               time = "&nbsp;"
+               command = "HOME=tmp DISTCC_HOSTS=\"%s\" DISTCC_VERBOSE=1 distcc probe.c -S -o /dev/null 2>&1" % hostname
+               state_pattern = re.compile("on %s completed ok" % hostname)
+               time_pattern = re.compile("elapsed compilation time ")
+
+               for line in os.popen(command).readlines():
+                       state_result = re.search(state_pattern, line)
+                       time_result = re.search(time_pattern, line)
+                       if not state_result == None:
+                               state = "UP"
+                       if not time_result == None:
+                               if state == "UP":
+                                       time = time_result.string[-10:]
+                       
+               print "<tr><td><pre>%s</pre></td><td>%s</td><td>%s</td></tr>" % (hostname, state, time)
+       
+       print "</table>"
+
+def mixup_hosts(hosts=""):
+       string = ""
+       while True:
+               if len(hosts) == 0:
+                       break
+                       
+               rand = random.randint(0, len(hosts)-1)
+               host, options = re.split(re.compile(':'),hosts[rand])
+               string += socket.gethostbyname(host) + ":" + options + " "
+               hosts.pop(rand)
+               
+       print string
+       
+form = cgi.FieldStorage()
+action  = form.getfirst('action')
+
+if action == "raw":
+       print_http_header()
+       
+       hosts = read_hosts()
+       
+       mixup_hosts(hosts)
+
+else:
+
+       print_header()
+       print_dcc_info()
+       
+       hosts = read_hosts()
+       
+       process_hosts(hosts)
+       
+       print_footer()
diff --git a/build/hosts b/build/hosts
new file mode 100644 (file)
index 0000000..5f25c81
--- /dev/null
@@ -0,0 +1,5 @@
+#mirror1.ipfire.org:3632,lzo
+#mirror2.ipfire.org:3632,lzo
+#mirror3.ipfire.org:3632,lzo
+compiler02.ipfire.org:3630/10,lzo
+compiler02.ipfire.org:3631/10,lzo
diff --git a/build/images/compiling.png b/build/images/compiling.png
new file mode 100644 (file)
index 0000000..903a27e
Binary files /dev/null and b/build/images/compiling.png differ
diff --git a/build/images/error.png b/build/images/error.png
new file mode 100644 (file)
index 0000000..9d67d8a
Binary files /dev/null and b/build/images/error.png differ
diff --git a/build/images/idle.png b/build/images/idle.png
new file mode 100644 (file)
index 0000000..8df47a5
Binary files /dev/null and b/build/images/idle.png differ
diff --git a/build/images/unknown.png b/build/images/unknown.png
new file mode 100644 (file)
index 0000000..61400e2
Binary files /dev/null and b/build/images/unknown.png differ
diff --git a/build/probe.c b/build/probe.c
new file mode 100644 (file)
index 0000000..dbec910
--- /dev/null
@@ -0,0 +1,3 @@
+int main () {
+       return 0;
+}
diff --git a/download/getfile.py b/download/getfile.py
new file mode 100644 (file)
index 0000000..d6c1e64
--- /dev/null
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+
+# This is a short script for spread our files...
+
+import cgi, random, urllib2, sys
+
+def redirect(url):
+       print 'Status: 302 Moved Temporarily'
+       print 'Location:', url
+       print 'Pragma: no-cache'
+       print 'Content-Type: text/html'
+       print
+
+def notfound():
+       print 'Status: 404 Not Found'
+       print 'Pragma: no-cache'
+       print
+       
+def selectserver(filename):
+       servers = []
+       f = open("mirrorlist")
+       while True:
+               line = f.readline()
+               if len(line) == 0:
+                       break # EOF
+               line = line.rstrip('\n')
+               servers.append(line)
+       f.close()
+       
+       while True:
+               if len(servers) == 0:
+                       url = "None"
+                       break
+               rand = random.randint(0, len(servers)-1)
+               url = "%s/%s" % (servers[rand], cgi.escape(filename))
+               try:                    
+                       req = urllib2.Request(url)
+                       req.add_header('User-Agent', 'IPFire/DownloadScript-1.0')
+                       urllib2.urlopen(req)
+               except urllib2.HTTPError, e:
+                       servers.pop(rand)
+               except urllib2.URLError, e:
+                       servers.pop(rand)
+               else:
+                       break
+       return url
+
+form = cgi.FieldStorage()
+filename  = form.getfirst('file')
+
+url = selectserver(filename)
+
+if url == "None":
+       notfound()
+else:
+       redirect(url)
diff --git a/download/mirrorlist b/download/mirrorlist
new file mode 100644 (file)
index 0000000..8805668
--- /dev/null
@@ -0,0 +1,6 @@
+http://mirror1.ipfire.org
+http://www.rowie.at/ipfire
+http://ipfire.earl-net.com
+http://ipfire.kbarthel.de
+http://ipfire.1l0v3u.com
+http://hex2.sv-altikon.ch/ipfire
diff --git a/pakfire/counter.py b/pakfire/counter.py
new file mode 100644 (file)
index 0000000..fcaa2be
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+
+import cgitb, sys, os, time, cgi
+cgitb.enable()
+
+### HTTP-Header
+#
+print "Content-type: text/html"
+print
+
+form = cgi.FieldStorage()
+
+uuid = form.getfirst('uuid')
+ver  = form.getfirst('ver')
+ipak = form.getfirst('ipak')
+dpak = form.getfirst('dpak')
+upak = form.getfirst('upak')
+ret  = form.getfirst('return')
+
+if not uuid or not ver:
+       sys.exit(1) # Exit when the arguments are not set
+
+if not os.path.exists("version/"+ver):
+       os.mkdir("version/"+ver)
+
+zeit = time.time()
+string = "%s %d" % (os.environ['REMOTE_ADDR'], zeit)
+
+if ipak:
+       string += " installed %s %s" % (ipak, ret)
+elif dpak:
+       string += " deleted %s %s" % (dpak, ret)
+elif upak:
+       string += " upgraded %s %s" % (upak, ret)
+else:
+       string += " update"
+
+string += "\n"
+
+f = open("version/"+ver+"/"+uuid, "a")
+f.write(string)
+f.close()
+
+for file in os.listdir("version/"+ver):
+       time_diff = zeit - os.path.getmtime("version/"+ver+"/"+file)
+       if time_diff > 259200:
+               os.remove("version/"+ver+"/"+file)
+
+print "200 OK"
diff --git a/pakfire/show.py b/pakfire/show.py
new file mode 100644 (file)
index 0000000..6a17605
--- /dev/null
@@ -0,0 +1,217 @@
+#!/usr/bin/python
+
+import cgitb, os, time, socket, cgi, DNS, GeoIP
+cgitb.enable()
+
+#gi = GeoIP.new(GeoIP.GEOIP_STANDARD)
+gi = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)
+
+def print_header():
+       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>IPFire - Distribution</title>
+               <style type="text/css">
+                       body { font-family: Verdana; font-size: 9pt; background-color:#f0f0f0; }
+                       a:link { color: black; text-decoration: none; }
+                       a:visited { color: black; text-decoration: none; }
+                       table { font-family: Verdana; font-size: 8pt; border: 1px solid; }
+                       td.header { background-color: #a0a0a0; text-align: center; color: white; }
+                       td.date { text-align: center; }
+                       font.installed { color: green; }
+                       font.deleted { color: red; }
+                       font.update { color: blue; }
+                       font.error { color: red; }
+                       td.online { background: green; color: white; text-align: center; }
+                       td.offline { background: red; color: white; text-align: center; }
+               </style>
+               <meta http-equiv="refresh" content="60; URL=%s?ver=%s&amp;uuid=%s" />
+       </head>
+       <body>
+               <h1>IPFire Distribution</h1>
+               <p>Made: %s</p>''' % (os.environ['SCRIPT_NAME'], ver, uuid, time.ctime())
+
+def beautify_ip(ip):
+       try:
+               hostname = socket.gethostbyaddr(ip)
+               string = "<b>%s</b> (%s - %s)" % (hostname[0], ip, gi.country_code_by_addr(ip))
+       except socket.herror, e:
+               error = "Couldn't look up name: %s" % e
+               string = "<b>%s</b> (%s)" % (error, ip)
+       return string
+       
+def beautify_time(timestamp):
+       return time.strftime("%d-%m-%Y - %H:%M", time.localtime(float(timestamp)))
+
+def get_attributes(line):
+       status = ""
+       pak = ""
+       command = ""
+       args = line.split(" ")
+       ip = args[0]
+       timestamp = args[1]
+       if len(args) > 2:
+               command = args[2]
+               if len(args) > 3:
+                       pak = args[3]
+                       if len(args) > 4:
+                               status = args[4]
+
+       return ip, timestamp, command, pak, status
+
+def showuuid(uuid, ver):
+       print "<h3><a href='%s?ver=%s&amp;uuid=%s#%s'>%s</a></h3>" % (os.environ['SCRIPT_NAME'], dir, uuid, uuid, uuid)
+       
+def summurize_addons():
+       addons={}
+       installed={}
+       upgraded={}
+       deleted={}
+       oldest="9999999999999"
+       newest="0000000000000"
+       for dir in os.listdir("version/"):
+#              print dir+"<br />"
+               for uuid in os.listdir("version/"+dir):
+#                      print uuid+"<br />"
+                       f = open("version/"+dir+"/"+uuid)
+                       while True:
+                               line = f.readline()
+                               if len(line) == 0:
+                                       break # EOF
+                               status = ""
+                               pak = ""
+                               command = ""
+                               args = line.split(" ")
+                               if oldest > args[1]:
+                                       oldest = args[1]
+                               if newest < args[1]:
+                                       newest = args[1]
+                               if len(args) > 2:
+                                       command = args[2]
+#                                      print command
+                               if len(args) > 3:
+                                       pak = args[3]
+#                                      print pak
+                               if len(args) > 4:
+                                       status = args[4]
+#                                      print status+"<br />"
+                               if (status == "0\n") and (command == "installed") and (dir == "2.1"):
+                                       addons[pak] = addons.get(pak,0)+1
+                                       installed[pak] = installed.get(pak,0)+1
+                               if (status == "0\n") and (command == "deleted") and (dir == "2.1"):
+                                       addons[pak] = addons.get(pak,0)-1
+                                       deleted[pak] = deleted.get(pak,0)+1
+                               if (status == "0\n") and (command == "upgraded") and (dir == "2.1"):
+                                       upgraded[pak] = upgraded.get(pak,0)+1
+                       f.close()
+       print "Oldest one installed - %s" % beautify_time(oldest)
+       for x in range(10):
+                       print "&nbsp;"
+       print "Latest action done - %s" % beautify_time(newest)
+       print "<br /><br /><table width='50%'><tr>"
+
+       for x in range(1,31):
+                       if ( x % 8 ):
+                                print "<td align='center'>Core %s - %s</td>" % (x,upgraded.get("core-upgrade-" + str(x),0))
+                       else:
+                                print "<td align='center'>Core %s - %s</td></tr><tr>" % (x,upgraded.get("core-upgrade-" + str(x),0))
+       print "</table><br /><br /><table width='50%'>"
+
+       print "<tr><td class='header'>Addon</td><td class='header'>Anzahl</td><td class='header'>Installiert</td><td class='header'>Deinstalliert</td></tr>"
+       for name, nummer in sorted(addons.items()):
+               print "<tr><td align='center'>"
+               print name
+               print "</td><td align='center'>"
+               print nummer
+               print "</td><td align='center'><font color=green>+"
+               print installed.get(name, 0)
+               print "</fond></td><td align='center'><font color=red>-"
+               print deleted.get(name, 0)
+               print "</td></tr>"
+       print "</table>"
+
+def showdetails(uuid, ver):
+       f = open("version/"+dir+"/"+uuid)
+       print "<a name='"+uuid+"' />\n<h3>"+uuid+"</h3>"
+       print "<table width='70%'>"
+       print "<tr><td class='header' width='70%'>IP-address<td class='header' width='30%'>Updates"
+       while True:
+               line = f.readline()
+               if len(line) == 0:
+                       break # EOF
+               
+               ip, timestamp, command, pak, status = get_attributes(line)
+               
+               if command == "update\n":
+                       last_update = timestamp
+                       continue
+
+               string = "<tr><td>"
+               string += beautify_ip(ip)
+
+               timestamp = beautify_time(timestamp)
+               
+               
+               if command:
+                       string += " - <font class='%s'>%s</font> - %s" % (command, command, pak)
+                       if not status == "0\n":
+                               string += " <font class='error'>%s</font>" % status
+                                                                                               
+               string += "</td><td class='date'>%s</td></tr>" % timestamp
+               
+               print string
+
+       print "<tr><td>Last update:</td><td align='center'>%s" % beautify_time(timestamp)                       
+       print "</table>"
+       f.close()
+
+
+def summary(type):
+       print "<table width='50%'>"
+       print "<tr><td class='header' colspan='2'>Summary</td></tr>"
+       if type == "global":
+               print "<tr><td>Versions available:</td><td>",
+               print os.listdir("version/"),
+               print "</td></tr>"
+               
+               print "<tr><td>Number of total hosts:</td><td>",
+               count = 0
+               for dir in os.listdir("version/"):
+                       count += len(os.listdir("version/"+dir))
+               print count,
+               print "</td></tr>"
+
+               
+       print "</table>"
+
+### HTTP-Header
+#
+print "Content-type: text/html"
+print
+
+form = cgi.FieldStorage()
+
+ver  = form.getfirst('ver')
+uuid = form.getfirst('uuid')
+
+if not uuid:
+       uuid = ""
+
+print_header()
+
+summary("global")
+
+for dir in os.listdir("version/"):
+
+       print "<h2><a href='%s?ver=%s&amp;uuid='>%s</a></h2>" % (os.environ['SCRIPT_NAME'], dir, dir)
+       if ver == dir:
+               for i in os.listdir("version/"+dir):
+                       if i == uuid:
+                               showdetails(i, dir)
+                       else:
+                               showuuid(i, dir)
+
+summurize_addons()
+
+print "</body></html>"