123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- #!/usr/bin/env python3
- ##Author: Elisa Aliverti
- ##Last edit: January 2020 - Matteo Bonamassa
- from time import time
- start = time()
- import argparse
- import textwrap
- import os
- import subprocess, sys
- from threading import Thread
- from getpass import getpass
- choices = ('status','start','stop', 'doctor')
- parser = argparse.ArgumentParser(description="Simple tool to handle LabCalcolo's VMs.", usage='%(prog)s {'+','.join(choices)+'} [--options]')
- ## positional arguments
- parser.add_argument( 'cmd', nargs="?", choices=choices, default='status',
- help='Specify command to execute, default is \'status\'' )
- ## optional arguments
- parser.add_argument( '-a', '--all', action='store_true', dest='lcm',
- help='All LCM nodes are considered' )
- parser.add_argument( '-n', nargs='+', dest='node',
- help='Select one or more nodes (at least one)' )
- parser.add_argument( '-1', '--lcm1', action='store_true', dest='lcm1',
- help='LCM1 nodes are considered' )
- parser.add_argument( '-2', '--lcm2', action='store_true', dest='lcm2',
- help='LCM2 nodes are considered' )
- parser.add_argument( '-v', '--version', action='version', version='%(prog)s 2.0',
- help='Print program version' )
- ##
- args = parser.parse_args()
- def print_progressbar(index, num) :
- sys.stdout.write('\r ['
- + '='*index
- + '>'*(1-int(index/num))
- + ' '*(num-index-1) + ']')
- sys.stdout.flush()
- ## main path (you don't say)
- main_path = "/var/etc/vmctl"
- class Host(Thread):
- # Constructor
- def __init__(self, name, location):
- # Fork the thread first thing
- Thread.__init__(self)
- # Variables initialization
- self.hostname = name
- self.location = location
- self.running = False
- self.up = False
- # Run method called on Thread start. Check if host is up and if is running a VM
- def run(self):
- if self.isup() :
- self.up = True
- self.running=self.vmstatus()
- # Ping the host to see if it's up
- def isup(self):
- # Is the host up?
- ping = os.popen("ping -w1 -c1 " + self.hostname, "r")
- # print("pinging " self.hostname)
- if "0 received" in ping.read():
- return False
- else:
- return True
- def vmstatus(self):
- statuscmd = "ps aux | grep qemu | grep -v grep"
- ssh = self.sshcommand(statuscmd)
- result = [ l for l in ssh.stdout.readlines() if 'qemu' in l ]
- if result == []:
- return False
- else:
- return True
- def sshcommand(self, command):
- if self.up:
- ssh = subprocess.Popen( ["ssh", "%s" % self.hostname, command], shell = False, stdout = subprocess.PIPE, stderr = subprocess.PIPE, universal_newlines=True )
- return ssh
- else:
- print(self.hostname + ' is not up.')
- return False
- def vmstart(self):
- if self.up:
- if not self.running:
- startcmd = main_path + " 1"
- self.sshcommand(startcmd)
- print('VM is now starting on ' + self.hostname)
- else:
- print('VM is already running on ' + self.hostname)
- else:
- print(self.hostname + ' is not up.')
- def vmstop(self):
- if self.up:
- if self.running:
- stopcmd = main_path + " 0"
- self.sshcommand(stopcmd)
- print('VM stopped on ' + self.hostname)
- else:
- print('VM is not running on ' + self.hostname)
- else:
- print(self.hostname + ' is not up.')
- def vmdoctor(self) :
- to_search=['qemu', 'spicec']
- # It's ugly (and more or less useless), but it should not be necessary: passwords should not be visible from ps aux
- pw_remove=['s/,password=\w*$//', 's/-w \w*$//']
- # Build the status query with programs names and relative pw remove strings
- status_query="ps aux | grep -E '" + '|'.join(to_search) + "' | grep -v grep | sed '" + ';'.join(pw_remove)+ "'"
- if self.up:
- ssh = self.sshcommand(status_query).stdout.readlines()
- qemu_status = [ l for l in ssh if 'qemu' in l ] # Filter for a single command
- if len(qemu_status) :
- print("Qemu command running on", self.hostname+":\n", *qemu_status)
- else :
- print("Qemu is not running on", self.hostname)
- spicec_status = [ l for l in ssh if 'spicec' in l ]
- if not len(spicec_status) and len(qemu_status) :
- print("Spicec is not running on", self.hostname)
- if input("Do you want to start it now? [y/n] ")=="y" :
- pw=getpass("Vm password: ")
- spiceccmd="export DISPLAY=:4 ; spicec -f -h 127.0.0.1 -p 5900 -w "+pw+" &"
- self.sshcommand(spiceccmd) # How to check if everything went as expected?
- # Check if everything works fine now
- spicec_status = [ l for l in self.sshcommand(status_query).stdout.readlines() if 'spicec' in l ]
- if len(spicec_status) :
- print("Spicec command running on", self.hostname+": ", *spicec_status)
- ### end class Host
- ## Host list
- Hosts = [
- Host('abe', 'LCM1'),
- Host('crash', 'LCM1'),
- Host('duke', 'LCM1'),
- Host('glados', 'LCM1'),
- Host('lara', 'LCM1'),
- Host('link', 'LCM1'),
- Host('king', 'LCM1'),
- Host('pang', 'LCM1'),
- Host('pong', 'LCM1'),
- Host('snake', 'LCM1'),
- Host('sonic', 'LCM1'),
- Host('spyro', 'LCM1'),
- Host('yoshi', 'LCM1'),
- Host('actarus', 'LCM2'),
- Host('elwood', 'LCM2'),
- Host('gex', 'LCM2'),
- Host('gin', 'LCM2'),
- Host('jake', 'LCM2'),
- Host('kirk', 'LCM2'),
- Host('martini', 'LCM2'),
- Host('picard', 'LCM2'),
- Host('q', 'LCM2'),
- Host('raziel', 'LCM2'),
- Host('sarek', 'LCM2'),
- Host('spock', 'LCM2'),
- Host('tron', 'LCM2'),
- Host('worf', 'LCM2'),
- Host('zombie', 'LCM2')
- ]
- nodes = []
- # Show usage if no arguments
- if len(sys.argv) < 2:
- parser.print_usage()
- print("\nSimple tool to handle LabCalcolo's VMs.")
- print('\nTry: "labcalcoloctl --help" to display help message.')
- sys.exit(1)
- # Filter hostlist according to arguments
- if args.lcm:
- nodes = Hosts
- elif args.lcm1:
- nodes = [ host for host in Hosts if host.location == 'LCM1' ]
- elif args.lcm2:
- nodes = [ host for host in Hosts if host.location == 'LCM2' ]
- elif args.node:
- nodes = [ j for j in Hosts if j.hostname in args.node ]
- # Start the threads and run commands on nodes
- for n in nodes :
- n.start()
- running=[]
- down=[]
- num=len(nodes)
- index=0
- print(' Querying ' + str(num) + ' hosts...')
- for i in nodes:
- # Rejoin them when their work is done
- i.join()
- if i.running:
- running.append(i.hostname)
- if not i.up :
- down.append(i.hostname)
- index += 1
- print_progressbar(index, num)
- # New line after progress bar
- print('\n Done... (%(t).3f s)' % {'t': (time() - start)})
- if args.cmd == 'status':
- if len(running):
- print("VM(s) running on:")
- for i in running : print('\t',i)
- else :
- print("No VMs are running")
- if len(down):
- print("Down nodes:")
- for i in down : print('\t',i)
- #########################################################################################################
- ## Since lcm2 nodes have no VMs installed a control is added to make sure operator knows what he's doing.
- ## When future (or present?) admins will fix the issue this part should be deleted.
- elif args.cmd == 'start':
- yes = {'yes','y','ye'}
- no = {'no','n'}
- control = True
- parsed = False
- for n in nodes:
- if n.location == 'LCM2' and parsed == False :
- print('You are trying to start a VM on lcm2 nodes. Are you sure you want to continue? [yes/no]')
- while True:
- choice = input().lower()
- if choice in yes:
- parsed = True
- break
- elif choice in no:
- control = False
- parsed = True
- break
- else:
- sys.stdout.write("Be kind, respond with 'yes' or 'no'")
- if control == True:
- for i in nodes: i.vmstart()
- #########################################################################################################
- ## Uncomment this part when the lcm2 VMs issue will be fixed.
- #elif args.cmd == 'start':
- # for i in nodes: i.vmstart()
- #########################################################################################################
- elif args.cmd == 'stop':
- for i in nodes: i.vmstop()
- elif args.cmd == 'doctor' :
- for i in nodes : i.vmdoctor()
|