#!/usr/bin/env python

__author__ = "Miklos Vajna <vmiklos@frugalware.org>"

from mod_python import apache
from email.Utils import formatdate
import marshal, telnetlib, time, os

def ping(host, timeout = 1):
        cmd = "ping -c 1 -W %d %s >/dev/null" % (timeout, host)
        return not os.system(cmd)

class Rss:
        def __init__(self, req, title, link, desc):
                self.req = req
                self.title = title
                self.desc = desc
                self.link = link
                self.items = []
        def additem(self, title, author, pubDate):
                self.items.append([title, author, pubDate])
        def output(self):
                self.req.content_type = 'application/xml'
                self.req.write("""<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>%s</title>
<description>%s</description>
<link>%s</link>\n""" % (self.title, self.desc, self.link))
                for title, author, pubDate in self.items:
                        self.req.write("""<item>
        <title>%s</title>\n""" % title)
                        if author:
                                self.req.write("\t<author>%s</author>\n" % author)
                        if pubDate:
                                self.req.write("\t<pubDate>%s</pubDate>\n" % formatdate(pubDate, True))
                        self.req.write("</item>\n")
                self.req.write("</channel>\n</rss>")
                return apache.OK

class HostList:
        def __init__(self, hostlist):
                self.list = []

                for k, v in hostlist.items():
                        if ping(v):
                                self.list.append(k)

        def compare(self, list):
                joined = []
                left = list[:]
                for i in self.list:
                        try:
                                left.remove(i)
                        except ValueError:
                                joined.append(i)
                return joined, left

class HostChange:
        """type: true if join, false if part"""
        def __init__(self, host, type):
                self.host = host
                self.type = type
                self.date = time.mktime(time.localtime())

class HostChanges:
        def __init__(self):
                self.state = []
                self.changes = []
                self.limit = 10
        def addjoins(self, joined):
                self.additems(joined, True)
        def addlefts(self, lefts):
                self.additems(lefts, False)
        def additems(self, list, type):
                for i in list:
                        self.changes.append(HostChange(i, type))
        def debug(self):
                print "current state"
                print self.state
                print "recent events"
                for i in self.changes:
                        if i.type:
                                print "%s: %s joined" % (time.asctime(time.localtime(i.date)), i.host)
                        else:
                                print "%s: %s left" % (time.asctime(time.localtime(i.date)), i.host)
        def dump(self, socket):
                if len(self.changes) > self.limit:
                        self.changes = self.changes[len(self.changes)-self.limit:]
                marshal.dump(self, socket)
        def torss(self, selfurl, req):
                rss = Rss(req, "Host monitor", selfurl,
                                "This feed monitors host changes.")
                revchans = self.changes[:]
                revchans.reverse()
                for i in revchans:
                        if i.type:
                                rss.additem("online", i.host, i.date)
                        else:
                                rss.additem("offline", i.host, i.date)
                return rss.output()

class HostMon:
        def __init__(self, hostlist, dumpfile, myurl):
                self.hostlist = hostlist
                self.myurl = myurl
                try:
                        socket = open(dumpfile, "r")
                        changes = marshal.load(socket)
                        socket.close()
                except IOError:
                        changes = HostChanges()
                self.changes = changes

                hosts = HostList(hostlist)
                joined, left = hosts.compare(changes.state)

                changes.addjoins(joined)
                changes.addlefts(left)
                changes.state = hosts.list

                try:
                        socket = open(dumpfile, "w")
                        changes.dump(socket)
                        socket.close()
                except IOError:
                        pass

        def dorss(self, req):
                return self.changes.torss(self.myurl, req)

def handler(req):
        mon = HostMon({"foo":"foo.com",
                "bar":"bar.com",
                "baz":"baz.com",
                "blah":"blah.com"},
                "/home/vmiklos/public_html/ping2rss/changes",
                "http://frugalware.org/~vmiklos/ping2rss")
        return mon.dorss(req)