From 243ee69506a9606c1b73ae9bd57f334c29e8cd52 Mon Sep 17 00:00:00 2001 From: Ville Rantanen Date: Mon, 13 Jul 2015 10:03:42 +0300 Subject: [PATCH] new project --- nando | 116 ++++++++++++++++++++++++++++++ nando-desc | 43 +++++++++++ nandod | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 363 insertions(+) create mode 100755 nando create mode 100755 nando-desc create mode 100755 nandod diff --git a/nando b/nando new file mode 100755 index 0000000..a906369 --- /dev/null +++ b/nando @@ -0,0 +1,116 @@ +#!/usr/bin/env python + +import socket,sys,time,os + +def setup_options(): + ''' Setup the command line options ''' + from argparse import ArgumentParser + + parser=ArgumentParser(description="Alive notifier.") + parser.add_argument("--host",action='store', dest='HOST',type=str,required=True, + help="Host name/IP of the server") + parser.add_argument("--port", action='store', dest='PORT', default=13370,type=int, + help="Port number of the server") + 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=False, + 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() + return options + +def print_table(data): + lengths=[0,0,0,0] + for row in data.split("\n"): + cols=row.split("|",4) + if len(cols)!=4: + continue + for c in range(4): + lengths[c]=max(lengths[c], len(cols[c])) + lengths=[str(x) for x in lengths] + for row in data.split("\n"): + cols=row.split("|",4) + if len(cols)!=4: + continue + print(("{0:<"+lengths[0]+"} | {1:<"+lengths[1]+"} | {2:>"+lengths[2]+"} | {3:<"+lengths[3]+"}").format(*cols)) + +def query_ip(sock,opts): + sock.sendto("list", (opts.HOST, opts.PORT)) + received = sock.recv(1024) + for row in received.split("\n"): + cols=row.split("|",4) + if cols[0]==opts.QUERY_HOSTNAME: + sys.stdout.write(cols[1]) + sys.exit(0) + sys.exit(1) + +def query_host(sock,opts): + sock.sendto("list", (opts.HOST, opts.PORT)) + received = sock.recv(1024) + for row in received.split("\n"): + cols=row.split("|",4) + if cols[1]==opts.QUERY_IP: + sys.stdout.write(cols[0]) + sys.exit(0) + sys.exit(1) + +def read_desc(opts): + + if not opts.MSGFILE: + return "" + if not os.path.isfile(opts.MSGFILE): + return "" + + DESC=open(opts.MSGFILE,"rb").read(512) + DESC=''.join([ c for c in DESC if c not in ['\n','\r','|'] ]) + return DESC + +opts=setup_options() + +errors=0 +while True: + try: + MYNAME = socket.gethostname() + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.settimeout(opts.TIMEOUT) + sock.connect((opts.HOST,opts.PORT)) + MYIP=sock.getsockname()[0] + + if opts.QUERY_HOSTNAME: + query_ip(sock,opts) + if opts.QUERY_IP: + query_host(sock,opts) + + if opts.command=="": + DESC=read_desc(opts) + MSG="{0}|{1}|{2}\n".format(MYNAME,MYIP,DESC) + else: + MSG=opts.command + + sock.sendto(MSG, (opts.HOST, opts.PORT)) + received = sock.recv(1024) + if opts.command!="": + print_table(received) + if opts.INTERVAL==0 and opts.command=="": + sock.sendto("list", (opts.HOST, opts.PORT)) + received = sock.recv(1024) + print_table(received) + errors=0 + except socket.error: + print("Didn't get reply from {0}:{1}".format(opts.HOST,opts.PORT)) + errors=1 + except IndexError: + print(received) + if opts.INTERVAL==0: + break + time.sleep(opts.INTERVAL) + +sys.exit(errors) diff --git a/nando-desc b/nando-desc new file mode 100755 index 0000000..abbdc81 --- /dev/null +++ b/nando-desc @@ -0,0 +1,43 @@ +#!/bin/bash + +#~ [ $UID = 0 ] || { + #~ echo Must be run as root + #~ exit 1 +#~ } +function filesize { +# Return a human readable size from integer of bytes +# Usage: filesize 10000 + + [ "$1" = "0" ] && { + echo "0 B" + return 0 + } + + awk 'BEGIN{ x = '$1' + split("B KB MB GB TB PB",type) + for(i=5;y < 1;i--) + y = x / (2^(10*i)) + str=int(y*10)/10 " " type[i+2] + if (x==0) { str = "0 B" } + print str + }' || return $? +} + + +# Gather list of last user of machine + +LASTUSER=$( who | tail -n 1 | awk '{ print $1 }' ) +FULLNAME=$( getent passwd "$LASTUSER" | cut -d ':' -f 5 ) + +DESC_CORES=$( grep -c ^processor /proc/cpuinfo | tr -d -c [:digit:] ) +DESC_TYPE=$( grep -m 1 "model name" /proc/cpuinfo | cut -d: -f2 | sed -e 's/^ *//' ) +DESC_MEM=$( grep MemTotal /proc/meminfo | cut -f2 -d: | tr -d -c [:digit:] ) +DESC_MEM=$( filesize $(( ${DESC_MEM}*1024 )) ) +DESC_VERS=$( lsb_release -r | awk '{print $2}' ) +DESC_UP=$( uptime | sed s/,.*// ) +DESC_SERIAL=$( dmidecode | grep "Serial Number:" | head -n 1 | cut -d: -f2 | sed -e 's/^ *//' ) +DESC="$LASTUSER/$FULLNAME/${DESC_CORES}x ${DESC_TYPE}/RAM ${DESC_MEM}/v.${DESC_VERS}/${DESC_UP}/SN ${DESC_SERIAL}" +echo "$DESC" + +# exit okay +true diff --git a/nandod b/nandod new file mode 100755 index 0000000..e9e7935 --- /dev/null +++ b/nandod @@ -0,0 +1,204 @@ +#!/usr/bin/env python + +import SocketServer,threading +import sqlite3 +import time,os,sys + +ALIVE=60*30 +TABLE_HEAD="Host|IP|Age(d h:m:s)|Description\n" + +def setup_options(): + ''' Setup the command line options ''' + from argparse import ArgumentParser + + parser=ArgumentParser(description="Alive notifier.") + parser.add_argument("--host",action='store', dest='HOST',default='0.0.0.0',type=str, + help="Bind to address") + parser.add_argument("--port", action='store', dest='PORT', default=13370,type=int, + help="Bind to port") + parser.add_argument("--db",action='store', dest='DB',default='/tmp/nando.sqlite',type=str, + help="Sqlite file for database.") + parser.add_argument("--quiet","-q",action='store_true', dest='quiet',default=False, + help="Quiet operation.") + options=parser.parse_args() + return options + + +class UDPHandler(SocketServer.BaseRequestHandler): + def handle(self): + data = self.request[0].strip() + socket = self.request[1] + if not opts.quiet: + print "{0} wrote: {1}".format(self.client_address[0],data) + if data=="list": + reply=DB.list_all() + elif data=="alive": + reply=DB.list_alive() + elif data=="lost": + reply=DB.list_lost() + else: + DB.update(data) + reply="OK" + socket.sendto(reply, self.client_address) + +class TCPHandler(SocketServer.BaseRequestHandler): + def handle(self): + if not opts.quiet: + print "TCP request: {0}".format(self.client_address[0]) + self.request.send('HTTP/1.0 200 OK\r\n') + self.request.send("Content-Type: text/html\r\n\r\n") + self.request.sendall(HTMLDB.HTML_list()) + self.request.close() + +class DataBase: + def __init__(self,DB): + self.DBfile=DB + self.conn=None + self.db=None + if not os.path.exists(self.DBfile): + self.createDB() + self.conn_init() + + def conn_init(self): + self.conn=sqlite3.connect(self.DBfile) + self.db=self.conn.cursor() + self.conn.text_factory=str + + def conn_end(self): + self.conn.commit() + #self.conn.close() + + def createDB(self): + self.conn_init() + self.db.execute('CREATE TABLE alive (id TEXT PRIMARY KEY,\ + ip TEXT,\ + desc TEXT,\ + date INTEGER)') + self.conn_end() + + def update(self,id): + try: + host,ip,desc=id.split("|",3) + desc=''.join([ c for c in desc if c not in ['\n','\r','|'] ]) + if len(ip.split("."))!=4: + return 1 + except: + return + self.db=self.conn.cursor() + self.db.execute("INSERT OR REPLACE INTO alive(id,ip,date,desc) \ + VALUES(?,?,?,?)",(host,ip,int(time.time()),desc)) + self.conn_end() + + def list_all(self): + self.db=self.conn.cursor() + self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id") + alive=[] + lost=[] + for row in self.db: + age=int(time.time())-row[2] + if age=ALIVE: + msg.append("{0}|{1}|{2}|{3}".format(row[0],row[1], humanize_time(age),row[3])) + return TABLE_HEAD+"\n".join(msg) + + def HTML_list(self): + msg=[] + msg.append(''' + + + +

Alive

''') + self.db=self.conn.cursor() + self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id") + msg.append(''.format('Host','IP','Age[d h:m:s]','Description')) + for row in self.db: + age=int(time.time())-row[2] + if age '.format(row[0],row[1],humanize_time(age),row[3])) + msg.append('
{0}{1}{2}{3}
{0}{1}{2}{3}

Lost

') + msg.append(''.format('Host','IP','Age[d h:m:s]','Description')) + self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id") + for row in self.db: + age=int(time.time())-row[2] + if age >=ALIVE: + msg.append(''.format(row[0],row[1],humanize_time(age),row[3])) + msg.append('
{0}{1}{2}{3}
{0}{1}{2}{3}
') + + return "\n\r".join(msg) + +def UDPserve(): + global DB + global server + DB=DataBase(opts.DB) + server = SocketServer.UDPServer((opts.HOST, opts.PORT), UDPHandler) + server.serve_forever() +def TCPserve(): + global HTMLDB + global HTMLserver + HTMLDB=DataBase(opts.DB) + HTMLserver = SocketServer.TCPServer((opts.HOST, opts.PORT), TCPHandler) + HTMLserver.serve_forever() + +def humanize_time(secs): + mins, secs = divmod(secs, 60) + hours, mins = divmod(mins, 60) + if hours < 24: + return '%02d:%02d:%02d' % (hours, mins, secs) + else: + days, hours = divmod(hours, 24) + return '%dd %02d:%02d:%02d' % (days, hours, mins, secs) + +if __name__ == "__main__": + opts=setup_options() + if not opts.quiet: + print("Starting NandoD {0}:{1}".format(opts.HOST,opts.PORT)) + UDP=threading.Thread(target=UDPserve) + TCP=threading.Thread(target=TCPserve) + UDP.daemon=True + TCP.daemon=True + UDP.start() + TCP.start() + while True: + try: + time.sleep(1) + except KeyboardInterrupt: + print("Exiting..") + server.shutdown() + HTMLserver.shutdown() + sys.exit(0)