""" host and connection class shared between hbd and the websit's heartbeat.py """ import time import json import copy import queue num = 0 MAXRTTS = 10 DEBUG = 2 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: 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.0) 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) if self.state == Connection.UNKNOWN and now - self.lastbeat > 86400 * 10: d["state"] = "" d["addr"] = "" d["rttstate"] = "" d["statetime"] = "" d["lastbeat"] = "" 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 = {} dnsQ = queue.Queue() def __init__(self, name): global num self.name = name if name: num += 1 Host.hosts[name] = self self.num = num self.dyn = False self.watched = 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"] += "*" if self.watched: d["name"] = "%s" % 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 stateinfo(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 ddict def jsons(self): return json.dumps(self.stateinfo()) def setcver(self, cver): self.cver = cver def isDynDns(self): return self.dyn def isIPv4(self, addr): if isinstance(addr, tuple): 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 += '