#!/usr/bin/env python import sys,os,glob from datetime import datetime from datetime import timedelta import re,signal,time import subprocess #,threading VERSION=2 W= '30' R= '31' G= '32' Y= '33' B= '34' M= '35' C= '36' S= '1' E= '0' BR= '41' CLR = '\033[2J' SAVE = '\033[s' LOAD = '\033[u' CLRLN = '\033[K' CLRBLN = '\033[1K' DOWN = '\033[1B' SORTKEY = lambda key: (key[2].split('/')[-1].lower()) def setup_options(): ''' Setup the command line options ''' from argparse import ArgumentParser import argparse parser=ArgumentParser(description=''' Tool to clean up and colorize the output of Anduril. Example: anduril run yourscript.and | %(prog)s You can tap in to an existing log with: tail -f -n +0 log/_global | %(prog)s''',formatter_class=argparse.RawTextHelpFormatter) parser.add_argument("--no-colors",'--nc',action="store_false",dest="colors",default=True, help="Disable colored output") parser.add_argument("-n",type=int,dest="delay",default=3, help="Refresh delay") parser.add_argument("-1",action="store_true",dest="once",default=False, help="Run once and exit") parser.add_argument("--version",action='version', version=VERSION) options=parser.parse_args() return options def c(attribs): ''' ANSI colorizer ''' if not options.colors: return "" return '\033['+';'.join(attribs)+'m' def pos(y,x): ''' ANSI absolute position set ''' return "\033["+str(y)+";"+str(x)+"H" def colorize(string): ''' colorizes a string based on color_match ''' if not options.colors: return string for co in color_match: string=color_match[co][0].sub(color_match[co][1],string) return string def count_running(string, stats): ''' Counts the running executions ''' spl=[i for i in " ".join(string.split()).split(' ')] if len(spl)!=7: return stats if spl[6] in stats['files']: index=stats['files'].index(spl[6]) speed_history=stats['running'][index][1][1:] speed_history.append((int(spl[4])*1024-(stats['running'][index][0]))/stats['delay']) stats['running'][index]=(int(spl[4])*1024, # free space speed_history, # change speed spl[6], # mount point stats['running'][index][3], # free space program start spl[5], # usage in % spl[1], # mount type int(spl[2])*1024 # total space ) else: stats['running'].append((int(spl[4])*1024, [int(0)]*5, spl[6], int(spl[4])*1024, spl[5], spl[1], int(spl[2])*1024 )) stats['running'].sort(key=SORTKEY) stats['files']=[i[2] for i in stats['running']] totalfree=sum([i[0] for i in stats['running']]) total=sum([i[6] for i in stats['running']]) stats['totals']=[totalfree, total] return stats class EndProgram( Exception ): ''' Nice way of exiting the program ''' pass def is_number(s): ''' Check if string is float ''' try: out=float(s) return True except: return False def str_short(s,stats): ''' shorten text to fit screen ''' maxL=stats['size'][1] - 16 if len(s)95: return c((S,R))+" "+string+c((E)) if usage<80: return c((S,G))+" "+string+c((E)) return c((S,Y))+" "+string+c((E)) def mean_speed(history): speed=sum(history)/len(history) return int(speed) def human_time(dt=False): if not dt: dt=datetime.now() return dt.strftime("%H:%M:%S") def human_size(size,precision=1): if size==None: return 'nan' sign="" if size<0: sign="-" size=-size suffixes=['B','KB','MB','GB','TB','PB','EB','ZB'] suffixIndex = 0 defPrecision=0 while size > 1024: suffixIndex += 1 size = size/1024.0 defPrecision=precision return "%s%.*f%s"%(sign,defPrecision,size,suffixes[suffixIndex]) def readinput(lf): try: line = lf.stdout.readline() #line=lf.readline() return line except: return "CleanerTimeout" def termsize(): try: rows, columns = os.popen('stty size', 'r').read().split() except: (rows,columns)=(25,80) return (int(rows),int(columns)) options=setup_options() color_match={#'line_ends':(re.compile('$'),c.END), 'err':(re.compile('(Failed)'),c([R,S])+'\\1'+c([E])), 'done':(re.compile('(Done)'),c([G,S])+'\\1'+c([E])), 'percent':(re.compile('([0-9]+%)'),c([Y,S])+'\\1'+c([E])), } stats={'time':datetime.now(), 'running':[], 'files':[], 'totals':[], 'size': termsize(), 'delay': options.delay } if not options.once: sys.stdout.write(CLR+pos(0,0)+"Launching...") while 1: try: proc = subprocess.Popen(['df','-x','tmpfs','-x','devtmpfs','-T'],stdout=subprocess.PIPE) # set a 5 second timeout for the line read. #~ signal.signal(signal.SIGALRM, transfers.readline) #~ signal.alarm(5) stdout,stderr=proc.communicate() if not stdout: raise EndProgram for line in stdout.split('\n')[1:]: stats=count_running(line,stats) if options.once: print_stats_once(stats) sys.exit(0) print_stats(stats) sys.stdout.flush() time.sleep(options.delay) except EndProgram,KeyboardInterrupt: sys.stdout.write(DOWN+'\n') sys.stdout.flush() sys.exit(0)