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 "HostStateHrDyWkIP4 AddrIP6 AddrLast changeVer\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+= '' % vs + return r + else: + return "" % (self.doesack) + return '>' + + + def htmldisp(self, header=False): + if header: + return "\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 "%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 + + +
%s(%s)N/A
HostStateHrDyWkIP4 AddrIP6 AddrLast changeVer
%-24s%-7s%-16s%-16s%-17s%s