python3 updates

This commit is contained in:
Ville Rantanen
2020-10-25 10:41:13 +02:00
parent ff71a588cc
commit cff1dfea3c
2 changed files with 341 additions and 182 deletions

191
nando
View File

@@ -1,44 +1,95 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import socket,sys,time,os,configobj import socket, sys, time, os, configobj
RC = "/etc/nandorc"
BLOCK_SIZE = 10240
RC="/etc/nandorc"
BLOCK_SIZE=10240
def setup_options(): def setup_options():
''' Setup the command line options ''' """ Setup the command line options """
from argparse import ArgumentParser from argparse import ArgumentParser
parser=ArgumentParser(description="Alive notifier. Options first read from /etc/nandorc, and overriden by switches.") parser = ArgumentParser(
parser.add_argument("--host",action='store', dest='HOST',type=str,default=None, description="Alive notifier. Options first read from /etc/nandorc, and overriden by switches."
help="Host name/IP of the server") )
parser.add_argument("--port", action='store', dest='PORT', default=None,type=int, parser.add_argument(
help="Port number of the server") "--host",
parser.add_argument("-t",action='store', dest='TIMEOUT',default=5, type=int, action="store",
help="Connection timeout") dest="HOST",
parser.add_argument("-i",action='store', dest='INTERVAL',default=0, type=int, type=str,
help="Send signal every i seconds. If 0, send only once and exit.") default=None,
parser.add_argument("-m",action='store', dest='MSGFILE',type=str,default=None, help="Host name/IP of the server",
help="Read a text file as description. Max length 512 chars") )
parser.add_argument("--query-ip",action='store', dest='QUERY_HOSTNAME',default=None,type=str, parser.add_argument(
help="Query the IP of a host name") "--port",
parser.add_argument("--query-host",action='store', dest='QUERY_IP',default=None,type=str, action="store",
help="Query the hostname of an IP") dest="PORT",
parser.add_argument("command",type=str,action="store",default="",nargs="?", default=None,
choices=['','list','alive','lost'], type=int,
help="Command to send. If empty, send standard alive-notice. Commands: list, alive") help="Port number of the server",
options=parser.parse_args() )
parser.add_argument(
"-t",
action="store",
dest="TIMEOUT",
default=5,
type=int,
help="Connection timeout",
)
parser.add_argument(
"-i",
action="store",
dest="INTERVAL",
default=0,
type=int,
help="Send signal every i seconds. If 0, send only once and exit.",
)
parser.add_argument(
"-m",
action="store",
dest="MSGFILE",
type=str,
default=None,
help="Read a text file as description. Max length 512 chars",
)
parser.add_argument(
"--query-ip",
action="store",
dest="QUERY_HOSTNAME",
default=None,
type=str,
help="Query the IP of a host name",
)
parser.add_argument(
"--query-host",
action="store",
dest="QUERY_IP",
default=None,
type=str,
help="Query the hostname of an IP",
)
parser.add_argument(
"command",
type=str,
action="store",
default="",
nargs="?",
choices=["", "list", "alive", "lost"],
help="Command to send. If empty, send standard alive-notice. Commands: list, alive",
)
options = parser.parse_args()
if os.path.exists(RC): if os.path.exists(RC):
rc=configobj.ConfigObj(RC) rc = configobj.ConfigObj(RC)
if options.HOST is None: if options.HOST is None:
if "HOST" in rc.keys(): if "HOST" in rc.keys():
options.HOST=rc['HOST'] options.HOST = rc["HOST"]
if options.PORT is None: if options.PORT is None:
if "PORT" in rc.keys(): if "PORT" in rc.keys():
options.PORT=int(rc['PORT']) options.PORT = int(rc["PORT"])
if options.MSGFILE is None: if options.MSGFILE is None:
if "MSGFILE" in rc.keys(): if "MSGFILE" in rc.keys():
options.MSGFILE=rc['MSGFILE'] options.MSGFILE = rc["MSGFILE"]
if options.HOST is None: if options.HOST is None:
parser.error("Host name required") parser.error("Host name required")
@@ -48,40 +99,53 @@ def setup_options():
def print_table(data): def print_table(data):
lengths=[0,0,0,0] lengths = [0, 0, 0, 0]
for row in data.split("\n"): for row in data.split("\n"):
cols=row.split("|",4) cols = row.split("|", 4)
if len(cols)!=4: if len(cols) != 4:
continue continue
for c in range(4): for c in range(4):
lengths[c]=max(lengths[c], len(cols[c])) lengths[c] = max(lengths[c], len(cols[c]))
lengths=[str(x) for x in lengths] lengths = [str(x) for x in lengths]
for row in data.split("\n"): for row in data.split("\n"):
cols=row.split("|",4) cols = row.split("|", 4)
if len(cols)!=4: if len(cols) != 4:
continue continue
print(("{0:<"+lengths[0]+"} | {1:<"+lengths[1]+"} | {2:>"+lengths[2]+"} | {3}").format(*cols)) print(
(
"{0:<"
+ lengths[0]
+ "} | {1:<"
+ lengths[1]
+ "} | {2:>"
+ lengths[2]
+ "} | {3}"
).format(*cols)
)
def query_ip(sock,opts):
def query_ip(sock, opts):
sock.sendto("list", (opts.HOST, opts.PORT)) sock.sendto("list", (opts.HOST, opts.PORT))
received = sock.recv(BLOCK_SIZE) received = sock.recv(BLOCK_SIZE)
for row in received.split("\n"): for row in received.split("\n"):
cols=row.split("|",4) cols = row.split("|", 4)
if cols[0]==opts.QUERY_HOSTNAME: if cols[0] == opts.QUERY_HOSTNAME:
sys.stdout.write(cols[1]) sys.stdout.write(cols[1])
sys.exit(0) sys.exit(0)
sys.exit(1) sys.exit(1)
def query_host(sock,opts):
def query_host(sock, opts):
sock.sendto("list", (opts.HOST, opts.PORT)) sock.sendto("list", (opts.HOST, opts.PORT))
received = sock.recv(BLOCK_SIZE) received = sock.recv(BLOCK_SIZE)
for row in received.split("\n"): for row in received.split("\n"):
cols=row.split("|",4) cols = row.split("|", 4)
if cols[1]==opts.QUERY_IP: if cols[1] == opts.QUERY_IP:
sys.stdout.write(cols[0]) sys.stdout.write(cols[0])
sys.exit(0) sys.exit(0)
sys.exit(1) sys.exit(1)
def read_desc(opts): def read_desc(opts):
if not opts.MSGFILE: if not opts.MSGFILE:
@@ -89,47 +153,48 @@ def read_desc(opts):
if not os.path.isfile(opts.MSGFILE): if not os.path.isfile(opts.MSGFILE):
return "" return ""
DESC=open(opts.MSGFILE,"rb").read(1024) DESC = open(opts.MSGFILE, "rb").read(1024)
DESC=''.join([ str(c) for c in DESC.decode('utf-8') if c not in ['\n','\r','|'] ]) DESC = "".join([str(c) for c in DESC.decode("utf-8") if c not in ["\n", "\r", "|"]])
return DESC return DESC
opts=setup_options()
errors=0 opts = setup_options()
errors = 0
while True: while True:
try: try:
MYNAME = socket.gethostname() MYNAME = socket.gethostname()
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(opts.TIMEOUT) sock.settimeout(opts.TIMEOUT)
sock.connect((opts.HOST,opts.PORT)) sock.connect((opts.HOST, opts.PORT))
MYIP=sock.getsockname()[0] MYIP = sock.getsockname()[0]
if opts.QUERY_HOSTNAME: if opts.QUERY_HOSTNAME:
query_ip(sock,opts) query_ip(sock, opts)
if opts.QUERY_IP: if opts.QUERY_IP:
query_host(sock,opts) query_host(sock, opts)
if opts.command=="": if opts.command == "":
DESC=read_desc(opts) DESC = read_desc(opts)
MSG="{0}|{1}|{2}\n".format(MYNAME,MYIP,DESC) MSG = "{0}|{1}|{2}\n".format(MYNAME, MYIP, DESC)
else: else:
MSG=opts.command MSG = opts.command
sock.sendto(MSG.encode('utf8'), (opts.HOST, opts.PORT)) sock.sendto(MSG.encode("utf8"), (opts.HOST, opts.PORT))
received = sock.recv(BLOCK_SIZE) received = sock.recv(BLOCK_SIZE)
if opts.command!="": if opts.command != "":
print_table(received.decode('utf8')) print_table(received.decode("utf8"))
if opts.INTERVAL==0 and opts.command=="": if opts.INTERVAL == 0 and opts.command == "":
sock.sendto("list".encode('utf8'), (opts.HOST, opts.PORT)) sock.sendto("list".encode("utf8"), (opts.HOST, opts.PORT))
received = sock.recv(BLOCK_SIZE) received = sock.recv(BLOCK_SIZE)
print_table(received.decode('utf8')) print_table(received.decode("utf8"))
errors=0 errors = 0
except socket.error: except socket.error:
print("Didn't get reply from {0}:{1}".format(opts.HOST,opts.PORT)) print("Didn't get reply from {0}:{1}".format(opts.HOST, opts.PORT))
errors=1 errors = 1
except IndexError: except IndexError:
print(received) print(received)
if opts.INTERVAL==0: if opts.INTERVAL == 0:
break break
time.sleep(opts.INTERVAL) time.sleep(opts.INTERVAL)

332
nandod
View File

@@ -1,140 +1,197 @@
#!/usr/bin/env python #!/usr/bin/env python3
import SocketServer,threading import socketserver, threading
import sqlite3 import sqlite3
import time,os,sys import time, os, sys
ALIVE = 60 * 30
TABLE_HEAD = "Host|IP|Age(d h:m:s)|Description\n"
ALIVE=60*30
TABLE_HEAD="Host|IP|Age(d h:m:s)|Description\n"
def setup_options(): def setup_options():
''' Setup the command line options ''' """ Setup the command line options """
from argparse import ArgumentParser from argparse import ArgumentParser
parser=ArgumentParser(description="Alive notifier.") parser = ArgumentParser(description="Alive notifier.")
parser.add_argument("--host",action='store', dest='HOST',default='0.0.0.0',type=str, parser.add_argument(
help="Bind to address: %(default)s") "--host",
parser.add_argument("--port", action='store', dest='PORT', default=13370,type=int, action="store",
help="Bind to port: %(default)s") dest="HOST",
parser.add_argument("--html-port", action='store', dest='WEBPORT', default=13370,type=int, default="0.0.0.0",
help="Bind www server to port. 0 to disable, default: %(default)s") type=str,
parser.add_argument("--db",action='store', dest='DB',default='/tmp/nando.sqlite',type=str, help="Bind to address: %(default)s",
help="Sqlite file for database: %(default)s") )
parser.add_argument("--quiet","-q",action='store_true', dest='quiet',default=False, parser.add_argument(
help="Quiet operation: %(default)s") "--port",
options=parser.parse_args() action="store",
dest="PORT",
default=13370,
type=int,
help="Bind to port: %(default)s",
)
parser.add_argument(
"--html-port",
action="store",
dest="WEBPORT",
default=13370,
type=int,
help="Bind www server to port. 0 to disable, default: %(default)s",
)
parser.add_argument(
"--db",
action="store",
dest="DB",
default="/tmp/nando.sqlite",
type=str,
help="Sqlite file for database: %(default)s",
)
parser.add_argument(
"--quiet",
"-q",
action="store_true",
dest="quiet",
default=False,
help="Quiet operation: %(default)s",
)
options = parser.parse_args()
return options return options
class UDPHandler(SocketServer.BaseRequestHandler): class UDPHandler(socketserver.BaseRequestHandler):
def handle(self): def handle(self):
data = self.request[0].strip() DB = DataBase(opts.DB)
data = self.request[0].decode("utf-8").strip()
socket = self.request[1] socket = self.request[1]
if not opts.quiet: if not opts.quiet:
print "{0} wrote: {1}".format(self.client_address[0],data) print(("{0} wrote: {1}".format(self.client_address[0], data)))
if data=="list": if data == "list":
reply=DB.list_all() reply = DB.list_all()
elif data=="alive": elif data == "alive":
reply=DB.list_alive() reply = DB.list_alive()
elif data=="lost": elif data == "lost":
reply=DB.list_lost() reply = DB.list_lost()
else: else:
DB.update(data) DB.update(data)
reply="OK" reply = "OK"
socket.sendto(reply, self.client_address) socket.sendto(reply.encode("utf-8"), self.client_address)
DB.close()
class TCPHandler(SocketServer.BaseRequestHandler):
class TCPHandler(socketserver.BaseRequestHandler):
def handle(self): def handle(self):
HTMLDB = DataBase(opts.DB)
if not opts.quiet: if not opts.quiet:
print "TCP request: {0}".format(self.client_address[0]) print(("TCP request: {0}".format(self.client_address[0])))
self.data = self.request.recv(1024).strip() self.data = self.request.recv(1024).decode("utf-8").strip()
method, path, _ = self.data.splitlines()[0].split() method, path, _ = self.data.splitlines()[0].split()
self.path = path.lstrip("/") self.path = path.lstrip("/")
self.request.send('HTTP/1.0 200 OK\r\n') self.request.send(b"HTTP/1.0 200 OK\r\n")
self.request.send("Content-Type: text/html\r\n\r\n") self.request.send(b"Content-Type: text/html\r\n\r\n")
self.request.sendall(HTMLDB.HTML_list(self.path)) self.request.sendall(HTMLDB.HTML_list(self.path).encode("utf-8"))
self.request.close() self.request.close()
HTMLDB.close()
class DataBase: class DataBase:
def __init__(self,DB): def __init__(self, DB):
self.DBfile=DB self.DBfile = DB
self.conn=None self.conn = None
self.db=None self.db = None
if not os.path.exists(self.DBfile): if not os.path.exists(self.DBfile):
self.createDB() self.createDB()
self.conn_init() self.conn_init()
def close(self):
self.conn.close()
def conn_init(self): def conn_init(self):
self.conn=sqlite3.connect(self.DBfile) self.conn = sqlite3.connect(self.DBfile)
self.db=self.conn.cursor() self.db = self.conn.cursor()
self.conn.text_factory=str self.conn.text_factory = str
def conn_end(self): def conn_end(self):
self.conn.commit() self.conn.commit()
#self.conn.close() # self.conn.close()
def createDB(self): def createDB(self):
self.conn_init() self.conn_init()
self.db.execute('CREATE TABLE alive (id TEXT PRIMARY KEY,\ self.db.execute(
"CREATE TABLE alive (id TEXT PRIMARY KEY,\
ip TEXT,\ ip TEXT,\
desc TEXT,\ desc TEXT,\
date INTEGER)') date INTEGER)"
)
self.conn_end() self.conn_end()
def update(self,id): def update(self, id):
try: try:
host,ip,desc=id.split("|",3) host, ip, desc = id.split("|", 3)
desc=''.join([ c for c in desc if c not in ['\n','\r','|'] ]) desc = "".join([c for c in desc if c not in ["\n", "\r", "|"]])
if len(ip.split("."))!=4: if len(ip.split(".")) != 4:
return 1 return 1
except: except:
return return
self.db=self.conn.cursor() self.db = self.conn.cursor()
if desc is "": if desc is "":
self.db.execute("INSERT OR REPLACE INTO alive(id,ip,date) \ self.db.execute(
VALUES(?,?,?)",(host,ip,int(time.time()))) "INSERT OR REPLACE INTO alive(id,ip,date) \
VALUES(?,?,?)",
(host, ip, int(time.time())),
)
else: else:
self.db.execute("INSERT OR REPLACE INTO alive(id,ip,date,desc) \ self.db.execute(
VALUES(?,?,?,?)",(host,ip,int(time.time()),desc)) "INSERT OR REPLACE INTO alive(id,ip,date,desc) \
VALUES(?,?,?,?)",
(host, ip, int(time.time()), desc),
)
self.conn_end() self.conn_end()
def list_all(self): def list_all(self):
self.db=self.conn.cursor() self.db = self.conn.cursor()
self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id") self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY date DESC")
alive=[] alive = []
lost=[] lost = []
for row in self.db: for row in self.db:
age=int(time.time())-row[2] age = int(time.time()) - row[2]
if age<ALIVE: if age < ALIVE:
alive.append("{0}|{1}|{2}|{3}".format(row[0],row[1], humanize_time(age),row[3])) alive.append(
"{0}|{1}|{2}|{3}".format(row[0], row[1], humanize_time(age), row[3])
)
else: else:
lost.append( "{0}|{1}|{2}|{3}".format(row[0],row[1], humanize_time(age),row[3])) lost.append(
return TABLE_HEAD+"\n".join(alive)+"\n---|---|---|---\n"+"\n".join(lost) "{0}|{1}|{2}|{3}".format(row[0], row[1], humanize_time(age), row[3])
)
return TABLE_HEAD + "\n".join(alive) + "\n---|---|---|---\n" + "\n".join(lost)
def list_alive(self): def list_alive(self):
self.db=self.conn.cursor() self.db = self.conn.cursor()
self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY date DESC") self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY date DESC")
msg=[] msg = []
for row in self.db: for row in self.db:
age=int(time.time())-row[2] age = int(time.time()) - row[2]
if age<ALIVE: if age < ALIVE:
msg.append("{0}|{1}|{2}|{3}".format(row[0],row[1], humanize_time(age),row[3])) msg.append(
return TABLE_HEAD+"\n".join(msg) "{0}|{1}|{2}|{3}".format(row[0], row[1], humanize_time(age), row[3])
)
return TABLE_HEAD + "\n".join(msg)
def list_lost(self): def list_lost(self):
self.db=self.conn.cursor() self.db = self.conn.cursor()
self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY date") self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY date")
msg=[] msg = []
for row in self.db: for row in self.db:
age=int(time.time())-row[2] age = int(time.time()) - row[2]
if age>=ALIVE: if age >= ALIVE:
msg.append("{0}|{1}|{2}|{3}".format(row[0],row[1], humanize_time(age),row[3])) msg.append(
return TABLE_HEAD+"\n".join(msg) "{0}|{1}|{2}|{3}".format(row[0], row[1], humanize_time(age), row[3])
)
return TABLE_HEAD + "\n".join(msg)
def HTML_list(self, name): def HTML_list(self, name):
# name = '' means do not filter by name # name = '' means do not filter by name
msg=[] msg = []
msg.append('''<html> msg.append(
"""<html>
<head> <head>
<style> <style>
body { body {
@@ -153,78 +210,115 @@ td, th {
} }
</style> </style>
</head> </head>
<body><h1>Alive</h1><table>''') <body><h1>Alive</h1><table>"""
self.db=self.conn.cursor() )
self.db = self.conn.cursor()
self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id") self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id")
msg.append('<tr><th>{0}</th><th>{1}</th><th class="right">{2}</th><th>{3}</th></tr>'.format('Host','IP','Age[d h:m:s]','Description')) msg.append(
'<tr><th>{0}</th><th>{1}</th><th class="right">{2}</th><th>{3}</th></tr>'.format(
"Host", "IP", "Age[d h:m:s]", "Description"
)
)
for row in self.db: for row in self.db:
if name != '': if name != "":
if name != row[0]: if name != row[0]:
continue continue
age=int(time.time())-row[2] age = int(time.time()) - row[2]
if age < ALIVE: if age < ALIVE:
msg.append('<tr><td>{0}</td><td>{1}</td><td class="right">{2}</td><td>{3}</td></tr>'.format(row[0],row[1],humanize_time(age),row[3])) msg.append(
msg.append('</table><h1>Lost</h1><table>') '<tr><td>{0}</td><td>{1}</td><td class="right">{2}</td><td>{3}</td></tr>'.format(
msg.append('<tr><th>{0}</th><th>{1}</th><th class="right">{2}</th><th>{3}</th></tr>'.format('Host','IP','Age[d h:m:s]','Description')) row[0], row[1], humanize_time(age), row[3]
)
)
msg.append("</table><h1>Lost</h1><table>")
msg.append(
'<tr><th>{0}</th><th>{1}</th><th class="right">{2}</th><th>{3}</th></tr>'.format(
"Host", "IP", "Age[d h:m:s]", "Description"
)
)
self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id") self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id")
for row in self.db: for row in self.db:
if name != '': if name != "":
if name != row[0]: if name != row[0]:
continue continue
age=int(time.time())-row[2] age = int(time.time()) - row[2]
if age >= ALIVE: if age >= ALIVE:
msg.append('<tr><td>{0}</td><td>{1}</td><td class="right">{2}</td><td>{3}</td></tr>'.format(row[0],row[1],humanize_time(age),row[3])) msg.append(
msg.append('</table></body></html>') '<tr><td>{0}</td><td>{1}</td><td class="right">{2}</td><td>{3}</td></tr>'.format(
row[0], row[1], humanize_time(age), row[3]
)
)
msg.append("</table></body></html>")
return "\n\r".join(msg) return "\n\r".join(msg)
def UDPserve(): class UDPServe:
global DB def __init__(self, opts):
global server self.server = socketserver.UDPServer((opts.HOST, opts.PORT), UDPHandler)
DB=DataBase(opts.DB)
server = SocketServer.UDPServer((opts.HOST, opts.PORT), UDPHandler) def start(self):
server.serve_forever() thread = threading.Thread(target=self.serve)
def TCPserve(): thread.daemon = True
global HTMLDB thread.start()
global HTMLserver
time.sleep(2) def serve(self):
try: self.server.serve_forever()
HTMLDB=DataBase(opts.DB)
HTMLserver = SocketServer.TCPServer((opts.HOST, opts.WEBPORT), TCPHandler) def stop(self):
HTMLserver.serve_forever() self.server.shutdown()
except: self.server.server_close()
sys.exit(1)
class TCPServe:
def __init__(self, opts):
self.server = socketserver.TCPServer((opts.HOST, opts.WEBPORT), TCPHandler)
def start(self):
thread = threading.Thread(target=self.serve)
thread.daemon = True
thread.start()
def serve(self):
time.sleep(2)
self.server.serve_forever()
def stop(self):
self.server.shutdown()
self.server.server_close()
def humanize_time(secs): def humanize_time(secs):
mins, secs = divmod(secs, 60) mins, secs = divmod(secs, 60)
hours, mins = divmod(mins, 60) hours, mins = divmod(mins, 60)
if hours < 24: if hours < 24:
return '%02d:%02d:%02d' % (hours, mins, secs) return "%02d:%02d:%02d" % (hours, mins, secs)
else: else:
days, hours = divmod(hours, 24) days, hours = divmod(hours, 24)
return '%dd %02d:%02d:%02d' % (days, hours, mins, secs) return "%dd %02d:%02d:%02d" % (days, hours, mins, secs)
if __name__ == "__main__": if __name__ == "__main__":
opts=setup_options() opts = setup_options()
if not opts.quiet: if not opts.quiet:
print("Starting NandoD UDP:{0}:{1}, TCP:{0}:{2}".format( print(
opts.HOST, (
opts.PORT, "Starting NandoD UDP:{0}:{1}, TCP:{0}:{2}".format(
opts.WEBPORT opts.HOST, opts.PORT, opts.WEBPORT
)) )
UDP=threading.Thread(target=UDPserve) )
UDP.daemon=True )
UDP = UDPServe(opts)
UDP.start() UDP.start()
if opts.WEBPORT > 0: if opts.WEBPORT > 0:
TCP=threading.Thread(target=TCPserve) TCP = TCPServe(opts)
TCP.daemon=True
TCP.start() TCP.start()
while True: while True:
try: try:
time.sleep(1) time.sleep(1)
except KeyboardInterrupt: except KeyboardInterrupt:
print("Exiting..") print("Exiting..")
server.shutdown() UDP.stop()
if opts.WEBPORT > 0: if opts.WEBPORT > 0:
HTMLserver.shutdown() TCP.stop()
sys.exit(0) sys.exit(0)