split out classes into a module

This commit is contained in:
2016-04-25 19:49:32 +02:00
parent 7a62c3c61b
commit bd17ecc7cc
2 changed files with 288 additions and 212 deletions
+55 -212
View File
@@ -23,10 +23,13 @@ import httplib
import threading import threading
import Queue import Queue
import md5 import md5
import json
import zlib import zlib
from subprocess import Popen, STDOUT, PIPE from subprocess import Popen, STDOUT, PIPE
from hbdclass import *
SEND_EMAIL=False SEND_EMAIL=False
SEND_PUSHOVER=True SEND_PUSHOVER=True
@@ -41,11 +44,6 @@ AEMAIL = ["andreas@wrede.ca"]
NAME = "heatbeat" NAME = "heatbeat"
SMTPSERVER = "localhost" SMTPSERVER = "localhost"
# Table of Hosts
hosts = {}
# map of addrs to names
htab = {}
msgs = [] msgs = []
num = 0 num = 0
@@ -64,6 +62,33 @@ os.environ['TZ'] = 'EST5EDT'
tsfm=["%H","%d","%U"] tsfm=["%H","%d","%U"]
lastfm=["","",""] lastfm=["","",""]
tcss = """<script src="https://home.wrede.ca/pr/sorttable.js"></script>
<style>
#ntable {
border-collapse: collapse;
width: 100%;
}
#ntable td, #ntable th {
border: 1px solid #ddd;
text-align: left;
padding: 1px;
}
#ntable tr:nth-child(even){background-color: #f2f2f2}
#ntable tr:hover {background-color: #ddd;}
#ntable th {
padding-top: 12px;
padding-bottom: 12px;
background-color: #9d9d9d;
color: white;
}
</style> """
def handler(signum, frame): def handler(signum, frame):
global running, sig global running, sig
sig = signum sig = signum
@@ -80,10 +105,6 @@ def shortname(name):
return r[0] return r[0]
def isIPv4(addr):
return addr.find('.') > 0
class NullDevice: class NullDevice:
def write(self, s): def write(self, s):
pass pass
@@ -143,203 +164,6 @@ def oldmtodict(msg):
return stodict('HTB:'+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 = "<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):
return '<td align="right">N/A</td><td></td<td></td>'
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>>'
def htmldisp(self, header=False):
if header:
return "<tr><th>Host</th><th>State</th><th>Hr</th><th>Dy</th><th>Wk</th><th>IP4 Addr</th><th>IP6 Addr</th><th>Last change</th><th>Ver</th></tr>\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 "<tr><td>%-24s</td><td>%-7s</td>%s<td>%-16s</td><td>%-16s</td><td>%-17s</td><td>%s</td></tr>\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): def email(s, msg):
if not SEND_EMAIL: if not SEND_EMAIL:
return return
@@ -680,15 +504,22 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_HEAD(self): 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_response(200)
self.send_header("Last-Modified", time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(now))) 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("Accept-Ranges","bytes")
# self.send_header("Connection","close") # 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() self.end_headers()
def buildhead(self, title="Heartbeat", refresh=None): def buildhead(self, title="Heartbeat", refresh=None, extras=None):
res=[] res=[]
res.append('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">') res.append('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">')
res.append("<html>") res.append("<html>")
@@ -696,16 +527,18 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
res.append('<title>%s</title>' % (title)) res.append('<title>%s</title>' % (title))
if refresh: if refresh:
res.append("<meta http-equiv = Refresh content = %d>\n" % refresh) res.append("<meta http-equiv = Refresh content = %d>\n" % refresh)
if extras:
res.append(extras)
res.append("</head>") res.append("</head>")
res.append('<body BGCOLOR = "#FFFFFF" LINK = "#008000" VLINK = "#008000">') res.append('<body BGCOLOR = "#FFFFFF" LINK = "#008000" VLINK = "#008000">')
return res return res
def buildpage(self): def buildpage(self):
res=self.buildhead(refresh=60) res=self.buildhead(refresh=60, extras=tcss)
res.append("<H2>Heartbeat status %s</h2><h4> %s (%s)</H4>" % (VER, time.strftime("%H:%M:%S", time.localtime(now)), os.environ.get('TZ', 'CET-1CDT'))) res.append("<H2>Heartbeat status %s</h2><h4> %s (%s)</H4>" % (VER, time.strftime("%H:%M:%S", time.localtime(now)), os.environ.get('TZ', 'CET-1CDT')))
res.append("<table>") res.append('<table id="ntable" class="sortable">')
hosts_sorted = hosts.keys() hosts_sorted = hosts.keys()
hosts_sorted.sort() hosts_sorted.sort()
res.append(hosts[hosts_sorted[0]].htmldisp(True)) res.append(hosts[hosts_sorted[0]].htmldisp(True))
@@ -737,9 +570,8 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self): def do_GET(self):
global sig global sig
xsig = 0 xsig = 0
self.do_HEAD()
headers=[] headers=[]
headerdict = None
if DEBUG > 2: sys.stderr.write("handle\n") if DEBUG > 2: sys.stderr.write("handle\n")
uri = self.path uri = self.path
upar=string.split(uri,"?") 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 elif upar[0] == "/r": # restart
res=self.buildhead() res=self.buildhead()
res.append("restart request") res.append("restart request")
@@ -851,6 +693,7 @@ class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# self.request.send("%s\r\n" % h) # self.request.send("%s\r\n" % h)
# self.request.send("\r\n") # self.request.send("\r\n")
self.setheaders(headerdict)
tosend += res tosend += res
self.wfile.write(string.join(tosend, "\n")) self.wfile.write(string.join(tosend, "\n"))
+233
View File
@@ -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 = "<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>>'
def htmldisp(self, header=False):
if header:
return "<tr><th>Host</th><th>State</th><th>Hr</th><th>Dy</th><th>Wk</th><th>IP4 Addr</th><th>IP6 Addr</th><th>Last change</th><th>Ver</th></tr>\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 "<tr><td>%-24s</td><td>%-7s</td>%s<td>%-16s</td><td>%-16s</td><td>%-17s</td><td>%s</td></tr>\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