diff --git a/hbd b/hbd
index b9b910f..b46cdbe 100755
--- a/hbd
+++ b/hbd
@@ -23,10 +23,13 @@ import httplib
import threading
import Queue
import md5
+import json
import zlib
from subprocess import Popen, STDOUT, PIPE
+from hbdclass import *
+
SEND_EMAIL=False
SEND_PUSHOVER=True
@@ -41,11 +44,6 @@ AEMAIL = ["andreas@wrede.ca"]
NAME = "heatbeat"
SMTPSERVER = "localhost"
-# Table of Hosts
-hosts = {}
-# map of addrs to names
-htab = {}
-
msgs = []
num = 0
@@ -64,6 +62,33 @@ os.environ['TZ'] = 'EST5EDT'
tsfm=["%H","%d","%U"]
lastfm=["","",""]
+
+tcss = """
+ """
+
+
def handler(signum, frame):
global running, sig
sig = signum
@@ -80,10 +105,6 @@ def shortname(name):
return r[0]
-def isIPv4(addr):
- return addr.find('.') > 0
-
-
class NullDevice:
def write(self, s):
pass
@@ -143,203 +164,6 @@ def oldmtodict(msg):
return stodict('HTB:'+msg)
-class Connection:
- def __init__(self, name, cid, addr):
- self.name = name
- self.cid = cid
- self.addr = addr
- self.rtts = [0]
-
-
-class Host:
- up = "up"
- down = "down"
- overdue = "overdue"
-
-
- def __init__(self, name):
- global num
- self.name = name
- self.addr4 = None
- self.addr6 = None
- self.num = num
- self.lastbeat = time.time()
- self.upcount = 0
- self.state = Host.up
- self.state = "up"
- self.statetime = self.lastbeat
- self.interval = 0
- self.doesack = -1
- self.cmds = []
- self.cver = 0
- self.connections = {}
- self.latency = 0
- self.hdwcounts = [[0,0],[0,0],[0,0]]
- num += 1
- hosts[name] = self
-
-
- def setcver(self, cver):
- self.cver = cver
-
-
- def isDynDns(self):
- return self.name in dyndnshosts
-
-
- def newaddr(self, addr):
- if isIPv4(addr):
- if self.addr4:
- if self.addr4 == addr:
- r = None
- else:
- r = "changed from %s to %s" % (self.addr4, addr)
- del htab[self.addr4]
- self.addr4 = addr
- htab[addr] = self.name
- if self.isDynDns():
- dnsQ.put((self.name, self.addr4))
- else:
- r = "new addr %s" % (addr)
- self.addr4 = addr
- htab[addr] = self.name
- if self.isDynDns():
- dnsQ.put((self.name, self.addr4))
- else:
- if self.addr6:
- if self.addr6 == addr:
- r = None
- else:
- r = "changed from %s to %s" % (self.addr6, addr)
- del htab[self.addr6]
- self.addr6 = addr
- htab[addr] = self.name
- if self.isDynDns():
- dnsQ.put((self.name, self.addr6))
- else:
- r = "new addr %s" % (addr)
- self.addr6 = addr
- htab[addr] = self.name
- if self.isDynDns():
- dnsQ.put((self.name, self.addr6))
- return r
-
-
- # called when reloading class from pickle, add new fields here
- def fixup(self):
- try:
- a=self.cmds
- except:
- self.cmds=[]
-
- try:
- a=self.hdwcounts
- except:
- self.hdwcounts = [[self.doesack,self.upcount],[self.doesack,self.upcount],[self.doesack,self.upcount]]
-
- try:
- self.addr=self.addr4
- except:
- pass
- try:
- self.addr=self.addr6
- except:
- pass
-
-
- try:
- a=self.addr4
- a=self.addr6
- except:
- print "fix %s: addr to addr4/6 fixup" % self.name
- if isIPv4(self.addr):
- self.addr4 = self.addr
- self.addr6 = None
- else:
- self.addr4 = None
- self.addr6 = self.addr
- del self.addr
-
- try:
- a=self.latency
- except:
- self.latency = 0
-
- try:
- a=self.cver
- except:
- self.cver = 0
-
- try:
- a=self.connections
- a.append([])
- except:
- self.connections={}
-
-
- def getstate(self):
- return self.state
-
-
- def dispstate(self):
- if self.state in ["down", "overdue"]:
- state = "%s" % self.state
- elif self.state in ["up", "UP"]:
- state = ""
- for x in self.connections.keys():
- try:
- state += " %5.1f" % (self.connections[x].rtts[-1])
- except:
- state += " %5s" % (self.connections[x].rtts[-1])
- else:
- state = "%s" % self.state
- return state
-
-
- def dispstats(self):
- return '
N/A | | '
- if self.doesack != -1:
- if self.upcount > 0:
-# return "(%0.1f%%) %s %s %s " % ((self.doesack * 100.0) / self.upcount, self.doesack, self.upcount, self.hdwcounts)
- r = ""
- for v in xrange(3):
- a,u = self.hdwcounts[v]
- if (self.upcount - u) != 0:
- vs = "%0.0f" % (100.0 - (((self.doesack - a) * 100.0) / (self.upcount - u)))
- if vs == "0":
- vs=""
- else:
- vs="-"
- r+= '%s | ' % vs
- return r
- else:
- return "(%s) | | | " % (self.doesack)
- return 'N/A | | >'
-
-
- def htmldisp(self, header=False):
- if header:
- return "| Host | State | Hr | Dy | Wk | IP4 Addr | IP6 Addr | Last change | Ver |
\n"
-
- else:
- ipv4addr = self.addr4 if self.addr4 else ""
- ipv6addr = self.addr6 if self.addr6 else ""
- lastts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.statetime))
- return "| %-24s | %-7s | %s%-16s | %-16s | %-17s | %s |
\n" % \
- (self.name, self.dispstate(), self.dispstats(), ipv4addr, ipv6addr, lastts, self.cver)
-
-
-
- # set new state, return number of secs in previous state
- def newstate(self, state, when=0):
- self.state = state
- now = time.time()-when
- s = now-self.statetime
- self.statetime = now
- return s
-
-
-
def email(s, msg):
if not SEND_EMAIL:
return
@@ -680,15 +504,22 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_HEAD(self):
+ self.setheaders()
+
+
+ def setheaders(self, headerdict=None):
+ if not headerdict:
+ headerdict = {"Content-Type": "text/html; charset = ISO-8859-1" }
self.send_response(200)
self.send_header("Last-Modified", time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now)))
# self.send_header("Accept-Ranges","bytes")
# self.send_header("Connection","close")
- self.send_header("Content-Type","text/html; charset = ISO-8859-1")
+ for h in headerdict:
+ self.send_header(h, headerdict[h])
self.end_headers()
- def buildhead(self, title="Heartbeat", refresh=None):
+ def buildhead(self, title="Heartbeat", refresh=None, extras=None):
res=[]
res.append('')
res.append("")
@@ -696,16 +527,18 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
res.append('%s' % (title))
if refresh:
res.append("\n" % refresh)
+ if extras:
+ res.append(extras)
res.append("")
res.append('')
return res
def buildpage(self):
- res=self.buildhead(refresh=60)
+ res=self.buildhead(refresh=60, extras=tcss)
res.append("Heartbeat status %s
%s (%s)
" % (VER, time.strftime("%H:%M:%S", time.localtime(now)), os.environ.get('TZ', 'CET-1CDT')))
- res.append("")
+ res.append('')
hosts_sorted = hosts.keys()
hosts_sorted.sort()
res.append(hosts[hosts_sorted[0]].htmldisp(True))
@@ -737,9 +570,8 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
global sig
xsig = 0
- self.do_HEAD()
headers=[]
-
+ headerdict = None
if DEBUG > 2: sys.stderr.write("handle\n")
uri = self.path
upar=string.split(uri,"?")
@@ -830,6 +662,16 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ elif upar[0] == "/api/0/hosts": # api access to host table
+ headerdict = {"Content-Type": "application/json; charset=utf-8" }
+ l=[]
+ for h in hosts:
+ l.append(hosts[h].jsons())
+ res=["[",",".join(l),"]"]
+ elif upar[0] == "/api/0/messages": # api access to host table
+ headerdict = {"Content-Type": "application/json; charset=utf-8" }
+ l=msgs[len(msgs)-30:]
+ res=[json.dumps(l)]
elif upar[0] == "/r": # restart
res=self.buildhead()
res.append("restart request")
@@ -851,6 +693,7 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# self.request.send("%s\r\n" % h)
# self.request.send("\r\n")
+ self.setheaders(headerdict)
tosend += res
self.wfile.write(string.join(tosend, "\n"))
diff --git a/hbdclass.py b/hbdclass.py
new file mode 100644
index 0000000..a53b4f3
--- /dev/null
+++ b/hbdclass.py
@@ -0,0 +1,233 @@
+
+"""
+host and connection class shared between hbd and
+the websit's heartbeat.py
+
+"""
+
+import time
+import json
+
+# Table of Hosts
+hosts = {}
+# map of addrs to names
+htab = {}
+
+
+def isIPv4(addr):
+ return addr.find('.') > 0
+
+
+
+class Connection:
+ def __init__(self, name, cid, addr):
+ self.name = name
+ self.cid = cid
+ self.addr = addr
+ self.rtts = [0]
+
+ def jsons(self):
+ return(json.dumps(self.__dict__))
+
+
+class Host:
+ up = "up"
+ down = "down"
+ overdue = "overdue"
+
+
+ def __init__(self, name):
+ global num
+ self.name = name
+ self.addr4 = None
+ self.addr6 = None
+ self.num = num
+ self.lastbeat = time.time()
+ self.upcount = 0
+ self.state = Host.up
+ self.state = "up"
+ self.statetime = self.lastbeat
+ self.interval = 0
+ self.doesack = -1
+ self.cmds = []
+ self.cver = 0
+ self.connections = {}
+ self.latency = 0
+ self.hdwcounts = [[0,0],[0,0],[0,0]]
+ num += 1
+ hosts[name] = self
+
+
+ def jsons(self):
+ ddict = {}
+ for d in self.__dict__:
+ if d == 'connections':
+ cl = []
+ for c in self.connections:
+ cl.append(self.connections[c].__dict__)
+ ddict[d] = cl
+ else:
+ ddict[d] = self.__dict__[d]
+ return json.dumps(ddict)
+
+
+ def setcver(self, cver):
+ self.cver = cver
+
+
+ def isDynDns(self):
+ return self.name in dyndnshosts
+
+
+ def newaddr(self, addr):
+ if isIPv4(addr):
+ if self.addr4:
+ if self.addr4 == addr:
+ r = None
+ else:
+ r = "changed from %s to %s" % (self.addr4, addr)
+ del htab[self.addr4]
+ self.addr4 = addr
+ htab[addr] = self.name
+ if self.isDynDns():
+ dnsQ.put((self.name, self.addr4))
+ else:
+ r = "new addr %s" % (addr)
+ self.addr4 = addr
+ htab[addr] = self.name
+ if self.isDynDns():
+ dnsQ.put((self.name, self.addr4))
+ else:
+ if self.addr6:
+ if self.addr6 == addr:
+ r = None
+ else:
+ r = "changed from %s to %s" % (self.addr6, addr)
+ del htab[self.addr6]
+ self.addr6 = addr
+ htab[addr] = self.name
+ if self.isDynDns():
+ dnsQ.put((self.name, self.addr6))
+ else:
+ r = "new addr %s" % (addr)
+ self.addr6 = addr
+ htab[addr] = self.name
+ if self.isDynDns():
+ dnsQ.put((self.name, self.addr6))
+ return r
+
+
+ # called when reloading class from pickle, add new fields here
+ def fixup(self):
+ try:
+ a=self.cmds
+ except:
+ self.cmds=[]
+
+ try:
+ a=self.hdwcounts
+ except:
+ self.hdwcounts = [[self.doesack,self.upcount],[self.doesack,self.upcount],[self.doesack,self.upcount]]
+
+ try:
+ self.addr=self.addr4
+ except:
+ pass
+ try:
+ self.addr=self.addr6
+ except:
+ pass
+
+
+ try:
+ a=self.addr4
+ a=self.addr6
+ except:
+ print "fix %s: addr to addr4/6 fixup" % self.name
+ if isIPv4(self.addr):
+ self.addr4 = self.addr
+ self.addr6 = None
+ else:
+ self.addr4 = None
+ self.addr6 = self.addr
+ del self.addr
+
+ try:
+ a=self.latency
+ except:
+ self.latency = 0
+
+ try:
+ a=self.cver
+ except:
+ self.cver = 0
+
+ try:
+ a=self.connections
+ a.append([])
+ except:
+ self.connections={}
+
+
+ def getstate(self):
+ return self.state
+
+
+ def dispstate(self):
+ if self.state in ["down", "overdue"]:
+ state = "%s" % self.state
+ elif self.state in ["up", "UP"]:
+ state = ""
+ for x in self.connections.keys():
+ try:
+ state += " %5.1f" % (self.connections[x].rtts[-1])
+ except:
+ state += " %5s" % (self.connections[x].rtts[-1])
+ else:
+ state = "%s" % self.state
+ return state
+
+
+ def dispstats(self):
+ if self.doesack != -1:
+ if self.upcount > 0:
+# return "(%0.1f%%) %s %s %s " % ((self.doesack * 100.0) / self.upcount, self.doesack, self.upcount, self.hdwcounts)
+ r = ""
+ for v in xrange(3):
+ a,u = self.hdwcounts[v]
+ if (self.upcount - u) != 0:
+ vs = "%0.0f" % (100.0 - (((self.doesack - a) * 100.0) / (self.upcount - u)))
+ if vs == "0":
+ vs=""
+ else:
+ vs="-"
+ r+= '| %s | ' % vs
+ return r
+ else:
+ return "(%s) | | | " % (self.doesack)
+ return 'N/A | | >'
+
+
+ def htmldisp(self, header=False):
+ if header:
+ return "| Host | State | Hr | Dy | Wk | IP4 Addr | IP6 Addr | Last change | Ver |
\n"
+
+ else:
+ ipv4addr = self.addr4 if self.addr4 else ""
+ ipv6addr = self.addr6 if self.addr6 else ""
+ lastts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.statetime))
+ return "| %-24s | %-7s | %s%-16s | %-16s | %-17s | %s |
\n" % \
+ (self.name, self.dispstate(), self.dispstats(), ipv4addr, ipv6addr, lastts, self.cver)
+
+
+
+ # set new state, return number of secs in previous state
+ def newstate(self, state, when=0):
+ self.state = state
+ now = time.time()-when
+ s = now-self.statetime
+ self.statetime = now
+ return s
+
+
+