""" host and connection class shared between hbd and the websit's heartbeat.py """ import time import json import copy num = 0 MAXRTTS = 10 DEBUG=1 def log(host, m): if DEBUG: print("class log: %s %s" % (host, m)) 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(): log(self.host.name, "dns update %s" % self.addr) 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] elif self.state == Connection.unknown: d['rtt'] = "" else: d['rtt'] = "?" d['lastbeat'] = self.lastbeat if self.state == Connection.overdue: d['state'] = "%s" % self.state else: d['state'] = self.state if self.state == Connection.up: d['rttstate'] = d['rtt'] elif self.state == Connection.overdue: d['rttstate'] = '' else: d['rttstate'] = d['state'] d['statetime'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.statetime)) delta = now - self.statetime if self.state == Connection.unknown: d['deltastatetime'] = '' elif 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 if self.dyn: d['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 = "%s" % self.state elif self.state in ["up", "UP"]: state = "" for x in list(self.connections.keys()): try: state += " %5.1f" % (self.connections[x].rtts[-1]) except: state += " %5s" % (self.connections[x].rtts[-1]) elif self.state in ["unknown", "UNKNOWN"]: state = "" 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 range(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>' 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" % (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('') res.append(ubHost.htmltable('th', ubHost.headerdict(), short)) hosts_sorted = list(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("
") return res def buildmsgtable(self, msgs): res = [] le = max(40 - len(Host.hosts), 3) res.append("

Log of Events

") for m in msgs[len(msgs)-le:]: res.append("%s
" % m) return res # create fake "unbound objects", remove in Python 3.0 ubHost = Host(None) ubConnection = Connection(None, "", "", "")