Much much changes

This commit is contained in:
2013-04-07 02:41:58 +02:00
parent 05b3f04c8c
commit 3917e799fb
4 changed files with 385 additions and 172 deletions

View File

@@ -1,12 +1,21 @@
import json
import subprocess
import os, errno
import threading
import time
from qmp import QEMUMonitorProtocol
class VMHelper:
def __init__(self):
self.config = json.load(open("/root/tmp/config.json"))
def __init__(self, filename):
self.config = json.load(open(filename))
def getPid(self,vmid):
def getVmIds(self):
if ('VMs' in self.config):
return [x for x , y in self.config['VMs'].items()]
else:
return []
def getPid(self,vmid) -> "int or None":
result = None
try:
with open(self.config['kvm']['pidfile'].replace("$VMID", vmid)) as pidfd:
@@ -17,23 +26,44 @@ class VMHelper:
return result
def process_running(self,vmid):
def process_running(self,vmid) -> bool:
pid = self.getPid(vmid)
if pid is None:
return False
result = None
try:
os.kill(pid,0)
except OSError as e:
result = e.errno != errno.ESRCH
return e.errno != errno.ESRCH
else:
result = True
return True
def getQMPStatus(self,vmid):
# cmd doku http://git.qemu.org/?p=qemu.git;a=blob;f=qmp-commands.hx;h=1e0e11ee32571209e2dfce41b5c18f01d6ad3880;hb=HEAD
proto = QEMUMonitorProtocol(self.config['kvm']['qmpsocket'].replace("$VMID", vmid))
proto.connect()
result = {}
queryKvm = proto.command("query-kvm")
result["kvm-enabled"] = queryKvm["enabled"]
result["kvm-present"] = queryKvm["present"]
result["status"] = proto.command("query-status")["status"]
proto.close()
return result
def autostartVMs(self):
if ('VMs' in self.config):
for vmid, vmcfg in self.config['VMs'].items():
if "autostart" in vmcfg and vmcfg["autostart"] and not self.process_running(vmid):
self.startVM(vmid)
else:
raise Exception("Missing VMs config section!")
def startVM(self, vmid):
self.setupNetwork(vmid)
cmd = []
@@ -46,12 +76,77 @@ class VMHelper:
cmd.append("-qmp")
cmd.append("unix:" + self.config['kvm']['qmpsocket'].replace("$VMID", vmid) + ",server,nowait")
cmd += ["-name", vmid]
default_args = self.config['kvm']['default_args'].replace("$VMID", vmid)
cmd += default_args.split()
cmd += self.createArguments(vmid).split()
#print(" ".join(cmd))
subprocess.Popen(cmd, stdout=open("/dev/null"), stderr=open("/dev/null"))
#subprocess.call(cmd)
#subprocess.call(cmd
def shutdownVMs(self,timeout,parallel=True, statusCallback=lambda vmid, st : None):
threads = []
for vmid in self.getVmIds():
if self.process_running(vmid):
# i=vmid: strange workaround for strange problem the it uses the wrong vmid...
def stopCallback(st, i=vmid): statusCallback(i, st)
thread = threading.Thread(target=lambda : self.stopVM(vmid, timeout, stopCallback))
thread.start()
if parallel:
threads.append(thread)
else:
thread.join()
for thread in threads:
thread.join()
def stopVM(self, vmid, timeout=None, statusCallback=lambda st : None, wait=False):
proto = QEMUMonitorProtocol(self.config['kvm']['qmpsocket'].replace("$VMID", vmid))
proto.connect()
statusCallback("send_powerdown")
proto.cmd("system_powerdown")
if timeout is None and not wait:
proto.close()
return
timeoutEvent = threading.Event()
def waitForShutdown():
shutDown = False
while not timeoutEvent.is_set() and not shutDown :
event = proto.pull_event(wait=True)
shutDown = event is not None and event["event"] == 'SHUTDOWN'
eventWaitThread = threading.Thread(target=waitForShutdown)
eventWaitThread.start()
if(timeout is None and wait):
eventWaitThread.join()
proto.close()
return
eventWaitThread.join(timeout)
timeoutEvent.set()
if eventWaitThread.is_alive():
statusCallback("send_quit")
proto.cmd("quit")
time.sleep(1)
if(self.process_running(vmid)):
statusCallback("kill_vm")
self.killVm(vmid, 9) #kill it with fire!
proto.close()
def killVm(self, vmid, signal=15):
pid = self.getPid(vmid)
if pid is not None:
os.kill(pid,signal)
def createArguments(self, vmid):
if ('VMs' in self.config) and (vmid in self.config['VMs']):

View File

@@ -28,7 +28,8 @@
"keyboard" : "de",
"kernel": "/home/markus/kernel-3.8.5",
"append": "root=/dev/vda",
"owner": "markus"
"owner": "markus",
"autostart" : false
},
"bar": {
"cpu": "kvm64",
@@ -45,7 +46,28 @@
"display": 2
},
"keyboard" : "de",
"owner": "markus"
"owner": "markus",
"autostart" : true
},
"baz": {
"cpu": "kvm64",
"smp": 2,
"memory": 2048,
"cdrom": "/root/tmp/install-amd64-minimal-20130207.iso",
"network": {
"hw": "virtio",
"dev": "tap-baz",
"mac": "54:52:00:00:01:03",
"ip": ["192.0.2.26", "192.0.2.27"]
},
"vnc": {
"display": 3
},
"keyboard" : "de",
"owner": "markus",
"autostart" : true
}
}
}

View File

@@ -2,33 +2,107 @@
import argparse
import sys
import time
from VMHelper import VMHelper
helper = None
#### Constants ###
CONFIG_FILENAME_DEFAULT = "/root/tmp/config.json"
SHUTDOWN_TIMEOUT = 180
#### --------- ###
helper = VMHelper(CONFIG_FILENAME_DEFAULT)
stopStatusToMessage = {
"send_powerdown" : "Sending ACPI poweroff Event.",
"send_quit" : "Shoutdown has timed out! Quitting forcefully!",
"kill_vm" : "Killing process!"
}
def vmm_list(args):
print("Available VMs:")
print()
for vmid in helper.getVmIds():
s = "ID: {0} -- {1}"
state = None
if helper.process_running(vmid):
state = helper.getQMPStatus(vmid)["status"]
else :
state = "stopped"
print(s.format(vmid,state))
def vmm_start(args):
if(helper.process_running(args.vmid)):
print("VM {0} is already running!".format(args.vmid))
return
print('Starting VM {0.vmid}.'.format(args))
helper.startVM(args.vmid)
#print("Successfully started: " + ("yes" if helper.process_running(args.vmid) else "no"))
def vmm_shutdown(args):
timeout = SHUTDOWN_TIMEOUT if args.t is None or not args.t.isdigit() else int(args.t)
stCallback = lambda vmid, st : print("VM {0}: {1}".format(vmid, stopStatusToMessage[st]))
helper.shutdownVMs(timeout, args.s is None or args.s < 1, statusCallback=stCallback)
def vmm_stop(args):
print('Stopping VM {0.vmid}.'.format(args))
helper.stopVM(args['vmid'])
if(not helper.process_running(args.vmid)):
print("VM {0} is not running!".format(args.vmid))
return
if args.k is not None and args.k >= 1:
helper.killVm(args.vmid)
time.sleep(0.3)
if helper.process_running(args.vmid):
time.sleep(2)
if helper.process_running(args.vmid):
helper.killVm(args.vmid , 9)
print("VM killed successfully: " + ("yes" if not helper.process_running(args.vmid) else "no"))
return
if args.t is not None and not args.t.isdigit():
print ("timeout must be positve integer but is " + args.t + "!")
return
timeout = int(args.t) if args.t is not None else None
print('Stopping VM {0.vmid} with timeout {1}.'.format(args,timeout))
helper.stopVM(args.vmid, timeout, lambda x: print(stopStatusToMessage[x]), wait=args.w is not None)
def vmm_status(args):
if args.v >= 1:
print('Gathering status information for VM {0.vmid}.'.format(args))
proc_running = helper.process_running(args.vmid)
print("VM ID: {0}".format(args.vmid))
print("Qemu process is running: " + ("yes (pid: {0})".format(helper.getPid(args.vmid)) if helper.process_running(args.vmid) else "no"))
print("Qemu process is running: " + ("yes (pid: {0})".format(helper.getPid(args.vmid)) if proc_running else "no"))
#TODO write some decoder with status descriptions plaintext
for key,val in helper.getQMPStatus(args.vmid).items():
print("{0}: {1}".format(key,val))
def vmm_cleanup(args):
if(helper.process_running(args.vmid)):
print("VM {0} is running, nothing to clean up!".format(args.vmid))
return
print('Cleaning up for VM {0.vmid}.'.format(args))
helper.teardownNetwork(args.vmid)
#TODO remove pidfile and qmpfile
def vmm_version(args):
print("MC VM Manager v0.1\nCopyright (c) M. Hauschild and P. Dahlberg 2013.\n")
def vmm_autostart(args):
helper.autostartVMs()
def main():
global helper
print("MC VM Manager v0.1\nCopyright (c) M. Hauschild and P. Dahlberg 2013.\n")
#maybe we need to create a lockfile
parser = argparse.ArgumentParser(prog='mcvmm', description='Manages VMs')
subparsers = parser.add_subparsers(title='subcommands')
@@ -38,10 +112,18 @@ def main():
parser_start.add_argument('vmid', action='store', help='the ID of the VM')
parser_start.set_defaults(func=vmm_start)
parser_stop = subparsers.add_parser('stop', help='stop a VM')
parser_stop = subparsers.add_parser('stop', help='Shutdown VM with ACPI poweroff')
parser_stop.add_argument('vmid', action='store', help='the ID of the VM')
parser_stop.add_argument('-t', action='store', help='forcefully quit after given timeout value (signed integer), implies -w')
parser_stop.add_argument('-k', action='count', help='Kill the qemu process')
parser_stop.add_argument('-w', action='count', help='wait until the VM has stopped')
parser_stop.set_defaults(func=vmm_stop)
parser_shutdown = subparsers.add_parser('shutdown', help='Shutdown all VMs ACPI poweroff and quit forcefully after timeout')
parser_shutdown.add_argument('-t', action='store', help='forcefully quit after given timeout value (signed integer, default: {0}'.format(SHUTDOWN_TIMEOUT))
parser_shutdown.add_argument('-s', action='count', help='sequencial mode, timeout for each vm')
parser_shutdown.set_defaults(func=vmm_shutdown)
parser_status = subparsers.add_parser('status', help='status a VM')
parser_status.add_argument('-v', action='count', help='increase verbosity')
parser_status.add_argument('vmid', action='store', default=0, help='the ID of the VM')
@@ -51,11 +133,21 @@ def main():
parser_cleanup.add_argument('vmid', action='store', default=0, help='the ID of the VM')
parser_cleanup.set_defaults(func=vmm_cleanup)
helper = VMHelper()
parser_list = subparsers.add_parser('list', help='list available vms')
parser_list.set_defaults(func=vmm_list)
parser_version = subparsers.add_parser('version', help='Prints version and authors')
parser_version.set_defaults(func=vmm_version)
parser_autostart = subparsers.add_parser('autostart', help='Start VMs configured with autostart')
parser_autostart.set_defaults(func=vmm_autostart)
args = parser.parse_args()
#TODO abfrage ob vmid existiert
if "vmid" in args and args.vmid not in helper.getVmIds():
print("VM {0} does not exist!".format(args.vmid))
return
args.func(args)
if __name__ == '__main__':

View File

@@ -152,6 +152,10 @@ class QEMUMonitorProtocol:
self.__sock.setblocking(1)
if not self.__events and wait:
self.__json_read(only_event=True)
if len(self.__events) == 0:
return None
else:
event = self.__events[0]
del self.__events[0]
return event