Browse Source

Merge branch 'master' of nicolopalazzini/labcalcoloctl into master

This update aims to fix some inefficiencies of the code, improve the performance and add functionalities. In particular: -fixed some code inconsistencies -added control to prevent VM start in LCM2 -display usage if no argument is parsed
Alessandro De Gennaro 5 years ago
parent
commit
36f112abc4
2 changed files with 191 additions and 144 deletions
  1. 1 1
      README.md
  2. 190 143
      labcalcoloctl

+ 1 - 1
README.md

@@ -1,7 +1,7 @@
 # labcalcoloctl
 Simple tool to handle LabCalcolo's VMs.
 
-usage: labcalcolo [{status,start,stop,doctor}] [options]
+usage: labcalcoloctl [{status,start,stop,doctor}] [--options]
 
 positional arguments:
 

+ 190 - 143
labcalcoloctl

@@ -1,7 +1,7 @@
 #!/usr/bin/python
 
 ##Author: Elisa Aliverti
-##Last edit: 22/02/2017 - Silva
+##Last edit: May 2019 - nicolopalazzini
 
 from time import time
 start = time()
@@ -12,25 +12,25 @@ import subprocess, sys
 from threading import Thread
 from getpass import getpass
 
-choices = ('status','start','stop', 'doctor') 
-parser = argparse.ArgumentParser(usage='labcalcolo {'+','.join(choices)+'} [options]')
+choices = ('status','start','stop', 'doctor')
+parser = argparse.ArgumentParser(description="Simple tool to handle LabCalcolo's VMs.", usage=sys.argv[0]+' {'+','.join(choices)+'} [--options]')
 
 ## positional arguments
-parser.add_argument( 'cmd', nargs="?", choices=choices, default='status', 
+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',  
+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 1.3', 
-                     help='Print program version' )	   
-## 
+parser.add_argument( '-v', '--version', action='version', version='%(prog)s 1.4',
+                     help='Print program version' )
+##
 args = parser.parse_args()
 
 def print_progressbar(index, num) :
@@ -39,137 +39,148 @@ def print_progressbar(index, num) :
                         + '>'*(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 sshcommand(self, command):
-      if self.isup():
-         ssh = subprocess.Popen( ["ssh", "%s" % self.hostname, command],
-       	         		 shell = False, 
-                                 stdout = subprocess.PIPE,
-                                 stderr = subprocess.PIPE )
-         return ssh
-      else:
-         print self.hostname + ' is not up.'
-         return False
-
-   def vmstart(self):
-      if not self.vmstatus():
-          startcmd = main_path + " 1"
-          self.sshcommand(startcmd)
-          print 'VM is now starting on ' + self.hostname
-      else:
-          print 'VM is already running on ' + self.hostname
-          
-   def vmstop(self):
-      stopcmd = main_path + " 0"
-      self.sshcommand(stopcmd)
-
-   def vmstatus(self):
-      statuscmd = "ps aux | grep qemu | grep -v grep"
-      if self.isup():
-         ssh = self.sshcommand(statuscmd)
-         result = [ l for l in ssh.stdout.readlines() if 'qemu' in l ]
-         if result == []:
-              self.running=False
-         else:
-              self.running=True
-      else:
-          self.up=False
-      return self.running
-
-   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.isup():
-        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 raw_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
+	# 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 )
+			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 raw_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')
+    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: "'+sys.argv[0]+' --help" to display help message.'
+	sys.exit(1)
+
 # Filter hostlist according to arguments
 if args.lcm:
     nodes  = Hosts
@@ -186,22 +197,24 @@ for n in nodes :
 
 running=[]
 down=[]
-if args.cmd == 'status':
-    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)}
 
+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
@@ -211,9 +224,43 @@ if args.cmd == 'status':
         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':
-   for i in nodes: i.vmstart()
+	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 = raw_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()
+    for i in nodes: i.vmstop()
+
 elif args.cmd == 'doctor' :
-   for i in nodes : i.vmdoctor()
+    for i in nodes : i.vmdoctor()