Files
heartbeat/hbdclass.py
T
2016-05-03 02:39:26 +02:00

346 lines
7.8 KiB
Python

"""
host and connection class shared between hbd and
the websit's heartbeat.py
"""
import time
import json
import copy
num = 0
MAXRTTS = 10
#
class Connection:
# map of addrs to names
htab = {}
unknown = "unknown"
up = "up"
down = "down"
overdue = "overdue"
def __init__(self, host, cid, addr, afam):
self.host = host
self.cid = cid
self.addr = addr
self.afam = afam
self.rtts = [0]
self.lastbeat = time.time()
self.statetime = self.lastbeat
self.deltastatetime = 'computed'
self.state = Connection.unknown
if host:
r = "new addr %s" % (addr)
Connection.htab[addr] = self.host.name
if self.host.isDynDns():
Host.dnsQ.put((self.host.name, self.addr))
def registerDns(self):
Host.dnsQ.put((self.host.name, self.addr))
def statedict(self, Null=False):
d = {}
now = time.time()
if not Null:
d['addr'] = self.addr
if self.rtts[-1]:
d['rtt'] = "%0.1f" % self.rtts[-1]
else:
d['rtt'] = "?"
d['lastbeat'] = self.lastbeat
if self.state == Connection.overdue:
d['state'] = "<b>%s</b>" % self.state
else:
d['state'] = self.state
if self.state == Connection.up:
d['rttstate'] = d['rtt']
else:
d['rttstate'] = d['state']
d['statetime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.statetime))
delta = now - self.statetime
if delta > 86400:
# d['deltastatetime'] = time.strftime("%d %H:%M:%S", time.gmtime(delta))
d['deltastatetime'] = "%0.1f days" % (delta / 86400.)
elif delta > 3600:
# d['deltastatetime'] = time.strftime("%H:%M:%S", time.gmtime(delta))
d['deltastatetime'] = time.strftime("%k:%M hrs", time.gmtime(delta))
# d['deltastatetime'] = "%0.1f hrs" % (delta / 3600.)
elif delta > 60:
# d['deltastatetime'] = time.strftime("%M:%S", time.gmtime(delta))
d['deltastatetime'] = time.strftime("%M:%S mins", time.gmtime(delta))
# d['deltastatetime'] = "%0.1f mins" % (delta / 60.)
else:
# d['deltastatetime'] = time.strftime("%S", time.gmtime(delta))
d['deltastatetime'] = "%i secs" % (delta)
else:
d['addr'] = ''
d['rtt'] = ""
d['lastbeat'] = ''
d['state'] = ''
d['statetime'] = ''
d['deltastatetime'] = ''
d['rttstate'] = ''
return d
def headerdict(self, afam):
d = {}
d['addr'] = '%s Addr' % afam
d['rtt'] = 'Latencey'
d['lastbeat'] = 'Last Contact'
d['state'] = 'State'
d['statetime'] = 'Last State'
d['rttstate'] = 'Reach'
d['deltastatetime'] = 'Last State'
return d
def jsons(self):
return(json.dumps(self.__dict__))
# set new state, return number of secs in previous state
def newstate(self, state, now, when=0):
self.state = state
delta = now - when
s = delta - self.statetime
self.statetime = delta
return s
def getstate(self):
return self.state
def newaddr(self, addr, rtt, now):
self.lastbeat = now
self.rtts.append(rtt)
if len(self.rtts) > MAXRTTS:
del self.rtts[0]
if self.addr == addr:
r = None
else:
r = "changed from %s to %s" % (self.addr, addr)
try:
del Connection.htab[self.addr]
except:
pass
self.addr = addr
Connection.htab[addr] = self.host.name
if self.host.isDynDns():
Host.dnsQ.put((self.host.name, self.addr))
return r
#
class Host:
# Table of Hosts
hosts = {}
def __init__(self, name):
global num
self.name = name
if name:
num += 1
Host.hosts[name] = self
self.num = num
self.dyn = False
self.upcount = 0
self.interval = 0
self.doesack = -1
self.cmds = []
self.cver = 0
self.connections = {}
self.hdwcounts = [[0,0],[0,0],[0,0]]
def statedict(self):
d = {}
d['name'] = self.name
d['dyn'] = str(self.dyn)
d['ver'] = str(self.cver)
d['num'] = self.num
for c in ['IPv4', 'IPv6']:
if c in self.connections:
cs = self.connections[c].statedict()
else:
cs = ubConnection.statedict(True)
for csv in cs:
d['%s.%s' % (c, csv) ] = cs[csv]
return d
def headerdict(self):
d = {}
d['name'] = 'Name'
d['dyn'] = 'Dyn'
d['ver'] = 'Ver'
d['num'] = '??'
for c in ['IPv4', 'IPv6']:
cs = ubConnection.headerdict(c)
for csv in cs:
d['%s.%s' % (c, csv) ] = cs[csv]
return d
def registerDns(self):
for af in self.connections:
self.connections[af].registerDns()
def jsons(self):
ddict = {}
for d in self.__dict__:
if d == 'connections':
cl = []
for c in self.connections:
# dirty ugly hack: fix conn to host backpointer
cld = copy.deepcopy(self.connections[c].__dict__)
cld['host'] = cld['host'].name
cl.append(cld)
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.dyn
def isIPv4(self, addr):
if type(addr) == type(()):
return addr[0].find('.') > 0
else:
return addr.find('.') > 0
def conndata(self, cid, addr, rtt, now):
if self.isIPv4(addr):
afam = "IPv4"
else:
afam = "IPv6"
if afam not in self.connections:
self.connections[afam] = Connection(self, cid, addr, afam)
conn = self.connections[afam]
res = conn.newaddr(addr, rtt, now)
return conn, res
# called when reloading class from pickle, add new fields here
def fixup(self):
pass
def dispstate(self):
if self.state in ["down", "overdue"]:
state = "<b>%s</b>" % 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+= '<td align="right">%s</td>' % vs
return r
else:
return "<td>(%s)</td><td></td><td></td>" % (self.doesack)
return '<td align="right">N/A</td><td></td<td></td>>'
hostfields_long = ['name', 'IPv4.addr',
'IPv4.state', ('IPv4.rtt','style="text-align: right;"'),
('IPv4.statetime','style="text-align: right;"'), 'IPv6.addr',
'IPv6.state', ('IPv6.rtt', 'style="text-align: right;"'),
('IPv6.statetime','style="text-align: right;"'), 'ver']
hostfields_short = ['name',
('IPv4.rttstate','style="text-align: right;"'),
('IPv4.deltastatetime','style="text-align: right;"'),
('IPv6.rttstate','style="text-align: right;"'),
('IPv6.deltastatetime','style="text-align: right;"')]
def gene(self, tag, v, attrib=None):
if attrib:
a=" %s" % attrib
else:
a=""
return "<%s%s>%s</%s>" % (tag, a, v, tag)
def htmltable(self, tag, hd, short):
if short:
hostfields = Host.hostfields_short
else:
hostfields = Host.hostfields_long
h = []
for f in hostfields:
if type(f) == type(()):
h.append(self.gene(tag, hd[f[0]], f[1]))
else:
h.append(self.gene(tag, hd[f]))
return self.gene("tr", "\n".join(h))
def buildhosttable(self, short=False):
res = []
res.append('<table id="ntable" class="sortable">')
res.append(ubHost.htmltable('th', ubHost.headerdict(), short))
hosts_sorted = Host.hosts.keys()
if len(hosts_sorted):
hosts_sorted.sort()
for h in hosts_sorted:
res.append(ubHost.htmltable('td', Host.hosts[h].statedict(), short))
res.append("</table>")
return res
def buildmsgtable(self, msgs):
res = []
le = max(40 - len(Host.hosts), 3)
res.append("<h4>Log of Events</h4>")
for m in msgs[len(msgs)-le:]:
res.append("%s<BR>" % m)
return res
# create fake "unbound objects", remove in Python 3.0
ubHost = Host(None)
ubConnection = Connection(None, "", "", "")