whoall 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #!/usr/bin/python
  2. # - *- coding: utf- 8 - *-
  3. #
  4. # Python whoall: parallel, multi-threaded version
  5. # First author: jacopogh - nov 2006
  6. # Modified and updated by admins & 150 over time
  7. #
  8. # Requires python version 2.7 or above
  9. # Please read the wiki before editing this file
  10. # TODO
  11. # - improve means of separation between lcm1, lcm2, laur
  12. # - improve handling of options (e.g. "whoall -1 -m" should return
  13. # nodes in lcm1 which have mathematica installed)
  14. from time import time
  15. start = time()
  16. # Import required modules
  17. import argparse
  18. import collections
  19. import os
  20. import telnetlib
  21. import textwrap
  22. from threading import Thread
  23. from socket import error
  24. from sys import argv,exit,stdout
  25. # Some list definitions
  26. lcm1 = ['abe','crash','duke','glados','lara','link','king','pang','pong',
  27. 'snake','sonic','spyro','yoshi']
  28. lcm2 = ['actarus','elwood','gex','gin','jake','kirk','martini','picard','q',
  29. 'raziel','sarek','spock','tron','worf','zombie']
  30. laur = ['eskimo','orion','tilde']
  31. math4 = ['sonic','crash','duke','raziel','actarus','gin','kirk','martini',
  32. 'picard','sarek','tron','worf','zombie','q','elwood','jake']
  33. math5 = ['abe','glados','link','king','pang','pong','snake','spyro','yoshi']
  34. math = math4 + math5
  35. cuda = ['jacobi','yukawa','tesla']
  36. # Important variables: gods, hosts and term colors
  37. gods = ['root','andreatsh','andreamalerba','lorenzouboldi',
  38. 'stefanobalzan','eugeniothieme']
  39. # Former admins: bother them at your own risk
  40. chucknorris = [ 'agalli', 'ikki', 'buddino', 'alex', 'ema', 'ktf',
  41. 'davideg', 'jacopogh', 'lampo', 'gian', 'rbondesan',
  42. 'scolari', 'emanueleb', 'giani_matteo','gabryv','fran',
  43. 'alqahirah','giorgio_ruffa','palazzi','algebrato','blanc',
  44. 'blue','silviacotroneo']
  45. parser = argparse.ArgumentParser(
  46. epilog='Please, report bugs or unwanted behavior to: \
  47. working@lcm.mi.infn.it' )
  48. ## Optional arguments
  49. parser.add_argument('-1', '--lcm1', action='store_true', dest='lcm1',
  50. help='only display LCM1 hosts ')
  51. parser.add_argument('-2', '--lcm2', action='store_true', dest='lcm2',
  52. help='only display LCM2 hosts ')
  53. parser.add_argument('-l', '--laur', action='store_true', dest='laur',
  54. help='only display LAUR hosts ')
  55. parser.add_argument('-c', '--condor', action='store_true', dest='condor',
  56. help='only display condor nodes ')
  57. parser.add_argument('-C', '--cuda', action='store_true', dest='cuda',
  58. help='only display CUDA hosts ')
  59. parser.add_argument('-f', '--full', action='store_true', dest='full',
  60. help='display all nodes including empty, \
  61. unreachable and unavailable')
  62. parser.add_argument('-m', '--math', action='store_true', dest='math',
  63. help='only Mathematica hosts ')
  64. parser.add_argument('-n', action='store_true', dest='n',
  65. help='not display the progressbar and colors')
  66. parser.add_argument('-N', action='store_true', dest='N',
  67. help='also display number of logs')
  68. parser.add_argument('-v', '--version', action='version',
  69. version='%(prog)s 2.0', help='Print program version')
  70. ####
  71. args = parser.parse_args()
  72. hosts=[]
  73. if (( args.lcm1 and args.lcm2 ) or args.condor ) : hosts = lcm1 + lcm2
  74. elif args.lcm1: hosts += lcm1
  75. elif args.lcm2: hosts += lcm2
  76. elif args.laur: hosts += laur
  77. elif args.cuda: hosts += cuda
  78. elif args.math: hosts += math
  79. else: hosts = lcm1 + lcm2 + laur + cuda
  80. # FIXME it would be better not to rely on host names for splitting
  81. if args.n:
  82. splitters = [('jake','LCM1'),('gin','LCM2'),
  83. ('jacobi','CUDA'),('orion','LAUR')]
  84. progressbar = 0
  85. else:
  86. splitters = [('abe','\033[1;36mLCM1\033[0m'),
  87. ('actarus','\033[1;32mLCM2\033[0m'),
  88. ('jacobi','\033[1;35mCUDA\033[0m'),
  89. ('eskimo','\033[1;33mLAUR\033[0m')]
  90. progressbar = 1
  91. # Define port
  92. port = 79
  93. #############################################
  94. # Node class
  95. # The bulk of the program is done here
  96. #############################################
  97. class Node(Thread):
  98. # Constructor
  99. def __init__(self,nome,port):
  100. # Fork the thread first thing
  101. Thread.__init__(self)
  102. # Variables initialization
  103. self.hostname = nome
  104. self.local = []
  105. self.remote = []
  106. self.lgod = []
  107. self.rgod = []
  108. # Former admins
  109. self.chuck = []
  110. # Node status
  111. self.up = True
  112. self.aval = True
  113. self.port = port
  114. self.timeout = False
  115. # Ping the host to see if it's up
  116. def isup(self):
  117. # Is the host up?
  118. ping = os.popen("ping -w1 -c1 "+self.hostname,"r")
  119. if "0 received" in ping.read():
  120. self.up = False
  121. return False
  122. else:
  123. return True
  124. # Open telnet connection
  125. def connect(self):
  126. # Try to connect first
  127. try:
  128. self.conn = telnetlib.Telnet(self.hostname,self.port)
  129. # Inetd is down!
  130. except error, msg:
  131. self.aval = False
  132. return False
  133. return True
  134. # Read lcm_w ouput and fill users-gods lists accordingly
  135. def read(self):
  136. # Read data
  137. lista=''
  138. reachable=0
  139. try: lista = self.conn.read_until("EOF",1)
  140. except EOFError: reachable=1
  141. del self.conn
  142. if lista=='':
  143. if reachable == 0:
  144. self.timeout=True
  145. # Split lines and fields
  146. righe = lista.splitlines()
  147. # Needed later for isempty
  148. self.users = len(righe)
  149. # Local-remote-god user check
  150. for x in righe:
  151. fields = x.split()
  152. if "tty" in fields[1]:
  153. if fields[0] in gods:
  154. self.lgod.append(fields[0].lower())
  155. else:
  156. if fields[0] not in chucknorris:
  157. self.local.append(fields[0].lower())
  158. else:
  159. if fields[0] in gods:
  160. if fields[0] not in self.lgod:
  161. self.rgod.append(fields[0].lower())
  162. else:
  163. if fields[0] not in self.local:
  164. if fields[0] not in chucknorris:
  165. self.remote.append(fields[0].lower())
  166. # Former admins
  167. if fields[0] in chucknorris:
  168. self.chuck.append(fields[0].lower())
  169. # Run method
  170. # This is the part that every thread executes in parallel
  171. # If host is up -> open connection -> if connection -> read data
  172. def run(self):
  173. if self.isup():
  174. if self.connect():
  175. self.read()
  176. # Is host empty?
  177. def isempty(self):
  178. if (self.users > 0):
  179. return False
  180. else:
  181. return True
  182. # Print output giorgio-style, host based (after threads have finished)
  183. def printlist(self):
  184. # Uniq equivalent and string conversion
  185. strlocal = ""
  186. strremote = ""
  187. strlgod = ""
  188. strrgod = ""
  189. strchuck = ""
  190. if not args.N:
  191. # A set cannot have duplicate entries: uniq equivalent
  192. for item in set(self.local):
  193. strlocal += str(item) + ' '
  194. for item in set(self.remote):
  195. strremote += str(item) +' '
  196. for item in set(self.lgod):
  197. strlgod += str(item) +' '
  198. for item in set(self.rgod):
  199. strrgod += str(item) +' '
  200. for item in set(self.chuck):
  201. strchuck += str(item) +' '
  202. else:
  203. # Also print how many times users are logged (jp+blue)
  204. for item in set(self.local):
  205. strlocal += str(item) \
  206. + '(' \
  207. + str(collections.Counter(self.local).values()[
  208. collections.Counter(self.local).keys()
  209. .index(item)
  210. ]) \
  211. + ') '
  212. for item in set(self.remote):
  213. strremote += str(item) \
  214. + '(' \
  215. + str(collections.Counter(self.remote).values()[
  216. collections.Counter(self.remote).keys()
  217. .index(item)
  218. ]) \
  219. +') '
  220. for item in set(self.lgod):
  221. strlgod += str(item) \
  222. + '(' \
  223. + str(collections.Counter(self.lgod).values()[
  224. collections.Counter(self.lgod).keys()
  225. .index(item)
  226. ]) \
  227. + ') '
  228. for item in set(self.rgod):
  229. strrgod += str(item) \
  230. + '(' \
  231. + str(collections.Counter(self.rgod).values()[
  232. collections.Counter(self.rgod).keys()
  233. .index(item)
  234. ]) \
  235. + ') '
  236. for item in set(self.chuck):
  237. strchuck += str(item) \
  238. + '(' \
  239. + str(collections.Counter(self.chuck).values()[
  240. collections.Counter(self.chuck).keys()
  241. .index(item)
  242. ]) \
  243. + ') '
  244. # Print a tag [C] alongside CUDA hosts (jp)
  245. if self.hostname in cuda:
  246. self.hostname = self.hostname + ' '*(5-len(self.hostname))+'[C]'
  247. print ' ' + self.hostname + ' '*(15-len(self.hostname)) \
  248. + red + strlgod + normal + pink + strrgod + normal + green \
  249. + strlocal + normal + blue + strremote + normal \
  250. + turquoise + strchuck + normal
  251. ###################################################
  252. # Main
  253. ###################################################
  254. # Get user groups
  255. groups = os.popen("groups").readlines()[0].split()
  256. # Setting colors
  257. if progressbar > 0:
  258. red = "\033[1;31m"
  259. normal = "\033[0m"
  260. blue = "\033[1;34m"
  261. pink = "\033[1;35m"
  262. green = "\033[1;32m"
  263. turquoise ="\033[1;36m"
  264. else:
  265. red = ""
  266. normal = ""
  267. blue = ""
  268. pink = ""
  269. green = ""
  270. turquoise =""
  271. # Initialization of the four lists
  272. downlist = ""
  273. emptylist = ""
  274. unavlist = ""
  275. timeoutlist = ""
  276. # Initialize the threadlist
  277. threadlist = []
  278. # Number of hosts (for progressbar)
  279. num = len(hosts)
  280. # Start the threads
  281. for item in hosts:
  282. m=Node(item,port)
  283. threadlist.append(m)
  284. m.start()
  285. # Used for progressbar
  286. index = 0
  287. print ' Querying '+str(num)+' hosts...'
  288. # Rejoin them when their work is done
  289. for thread in threadlist:
  290. thread.join()
  291. if progressbar > 0:
  292. # Progessbar
  293. index += 1
  294. stdout.write('\r ['
  295. + '='*index
  296. + '>'*(1-int(index/num))
  297. + ' '*(num-index-1) + ']')
  298. stdout.flush()
  299. if progressbar > 0:
  300. # Newline
  301. print '\n Done... ( %(t).3f s)' % {'t': (time() - start)}
  302. # And now print!
  303. for thread in threadlist:
  304. # lcm1-lcm2-laur splitters
  305. for pair in splitters:
  306. if thread.hostname in pair[0]: print '-' + pair[1] + normal +'----'
  307. # See what they are up to and print accordingly
  308. if thread.hostname in cuda:
  309. thread.hostname +='[C]'
  310. if args.math :
  311. if thread.hostname in math4:
  312. thread.hostname +='[M4]'
  313. if thread.hostname in math5:
  314. thread.hostname +='[M5]'
  315. if not thread.up:
  316. # Host down
  317. downlist += thread.hostname + ' '
  318. else:
  319. if not thread.aval:
  320. # Host unavailable
  321. unavlist += thread.hostname + ' '
  322. continue
  323. if thread.timeout:
  324. # Thread has timed out
  325. timeoutlist += thread.hostname + ' '
  326. continue
  327. if thread.isempty():
  328. # Host is empty
  329. emptylist += thread.hostname + ' '
  330. else:
  331. thread.printlist()
  332. # Some final output
  333. if progressbar > 0:
  334. print
  335. print 'Legenda: ' + green + 'utente locale '+ normal \
  336. + blue + 'utente remoto ' + normal + red + 'admin locale ' + normal \
  337. + pink + 'admin remoto ' + normal + turquoise + 'nirvana' + normal
  338. print " I nodi contrassegnati con [C] sono i nodi CUDA."
  339. if args.math:
  340. print " I nodi contrassegnati con [M4] hanno Mathematica 4.0"
  341. print " I nodi contrassegnati con [M5] hanno Mathematica 5.2"
  342. print
  343. # Only in full mode
  344. #if ("-f" in str(params[0]))
  345. # or ("-F" in str(params[0]))
  346. # or ("-m" in str(params[0]))
  347. # or ("-C" in str(params[0])):
  348. if args.full or args.math or args.cuda:
  349. print red+'Empty: '+normal+emptylist
  350. print red+'Timeout: '+normal+timeoutlist
  351. print red+'Unreachable: '+normal+downlist
  352. print red+'Unavailable: '+normal+unavlist
  353. # Exit gracefully
  354. exit(0)