Much much changes
This commit is contained in:
@@ -1,12 +1,21 @@
|
|||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import os, errno
|
import os, errno
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from qmp import QEMUMonitorProtocol
|
||||||
|
|
||||||
class VMHelper:
|
class VMHelper:
|
||||||
def __init__(self):
|
def __init__(self, filename):
|
||||||
self.config = json.load(open("/root/tmp/config.json"))
|
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
|
result = None
|
||||||
try:
|
try:
|
||||||
with open(self.config['kvm']['pidfile'].replace("$VMID", vmid)) as pidfd:
|
with open(self.config['kvm']['pidfile'].replace("$VMID", vmid)) as pidfd:
|
||||||
@@ -17,23 +26,44 @@ class VMHelper:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def process_running(self,vmid):
|
def process_running(self,vmid) -> bool:
|
||||||
|
|
||||||
pid = self.getPid(vmid)
|
pid = self.getPid(vmid)
|
||||||
|
|
||||||
if pid is None:
|
if pid is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
result = None
|
|
||||||
try:
|
try:
|
||||||
os.kill(pid,0)
|
os.kill(pid,0)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
result = e.errno != errno.ESRCH
|
return e.errno != errno.ESRCH
|
||||||
else:
|
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
|
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):
|
def startVM(self, vmid):
|
||||||
self.setupNetwork(vmid)
|
self.setupNetwork(vmid)
|
||||||
cmd = []
|
cmd = []
|
||||||
@@ -46,12 +76,77 @@ class VMHelper:
|
|||||||
cmd.append("-qmp")
|
cmd.append("-qmp")
|
||||||
cmd.append("unix:" + self.config['kvm']['qmpsocket'].replace("$VMID", vmid) + ",server,nowait")
|
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)
|
default_args = self.config['kvm']['default_args'].replace("$VMID", vmid)
|
||||||
cmd += default_args.split()
|
cmd += default_args.split()
|
||||||
cmd += self.createArguments(vmid).split()
|
cmd += self.createArguments(vmid).split()
|
||||||
#print(" ".join(cmd))
|
#print(" ".join(cmd))
|
||||||
subprocess.Popen(cmd, stdout=open("/dev/null"), stderr=open("/dev/null"))
|
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):
|
def createArguments(self, vmid):
|
||||||
if ('VMs' in self.config) and (vmid in self.config['VMs']):
|
if ('VMs' in self.config) and (vmid in self.config['VMs']):
|
||||||
|
|||||||
@@ -28,7 +28,8 @@
|
|||||||
"keyboard" : "de",
|
"keyboard" : "de",
|
||||||
"kernel": "/home/markus/kernel-3.8.5",
|
"kernel": "/home/markus/kernel-3.8.5",
|
||||||
"append": "root=/dev/vda",
|
"append": "root=/dev/vda",
|
||||||
"owner": "markus"
|
"owner": "markus",
|
||||||
|
"autostart" : false
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
"cpu": "kvm64",
|
"cpu": "kvm64",
|
||||||
@@ -45,7 +46,28 @@
|
|||||||
"display": 2
|
"display": 2
|
||||||
},
|
},
|
||||||
"keyboard" : "de",
|
"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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,33 +2,107 @@
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
from VMHelper import VMHelper
|
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):
|
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))
|
print('Starting VM {0.vmid}.'.format(args))
|
||||||
helper.startVM(args.vmid)
|
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):
|
def vmm_stop(args):
|
||||||
print('Stopping VM {0.vmid}.'.format(args))
|
if(not helper.process_running(args.vmid)):
|
||||||
helper.stopVM(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):
|
def vmm_status(args):
|
||||||
if args.v >= 1:
|
if args.v >= 1:
|
||||||
print('Gathering status information for VM {0.vmid}.'.format(args))
|
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("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):
|
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))
|
print('Cleaning up for VM {0.vmid}.'.format(args))
|
||||||
helper.teardownNetwork(args.vmid)
|
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():
|
def main():
|
||||||
global helper
|
#maybe we need to create a lockfile
|
||||||
|
|
||||||
print("MC VM Manager v0.1\nCopyright (c) M. Hauschild and P. Dahlberg 2013.\n")
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog='mcvmm', description='Manages VMs')
|
parser = argparse.ArgumentParser(prog='mcvmm', description='Manages VMs')
|
||||||
subparsers = parser.add_subparsers(title='subcommands')
|
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.add_argument('vmid', action='store', help='the ID of the VM')
|
||||||
parser_start.set_defaults(func=vmm_start)
|
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('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_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 = subparsers.add_parser('status', help='status a VM')
|
||||||
parser_status.add_argument('-v', action='count', help='increase verbosity')
|
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')
|
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.add_argument('vmid', action='store', default=0, help='the ID of the VM')
|
||||||
parser_cleanup.set_defaults(func=vmm_cleanup)
|
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()
|
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)
|
args.func(args)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# Authors:
|
# Authors:
|
||||||
# Luiz Capitulino <lcapitulino@redhat.com>
|
# Luiz Capitulino <lcapitulino@redhat.com>
|
||||||
#
|
#
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2. See
|
# This work is licensed under the terms of the GNU GPL, version 2. See
|
||||||
# the COPYING file in the top-level directory.
|
# the COPYING file in the top-level directory.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -13,178 +13,182 @@ import errno
|
|||||||
import socket
|
import socket
|
||||||
|
|
||||||
class QMPError(Exception):
|
class QMPError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class QMPConnectError(QMPError):
|
class QMPConnectError(QMPError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class QMPCapabilitiesError(QMPError):
|
class QMPCapabilitiesError(QMPError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class QEMUMonitorProtocol:
|
class QEMUMonitorProtocol:
|
||||||
def __init__(self, address, server=False):
|
def __init__(self, address, server=False):
|
||||||
"""
|
"""
|
||||||
Create a QEMUMonitorProtocol class.
|
Create a QEMUMonitorProtocol class.
|
||||||
|
|
||||||
@param address: QEMU address, can be either a unix socket path (string)
|
@param address: QEMU address, can be either a unix socket path (string)
|
||||||
or a tuple in the form ( address, port ) for a TCP
|
or a tuple in the form ( address, port ) for a TCP
|
||||||
connection
|
connection
|
||||||
@param server: server mode listens on the socket (bool)
|
@param server: server mode listens on the socket (bool)
|
||||||
@raise socket.error on socket connection errors
|
@raise socket.error on socket connection errors
|
||||||
@note No connection is established, this is done by the connect() or
|
@note No connection is established, this is done by the connect() or
|
||||||
accept() methods
|
accept() methods
|
||||||
"""
|
"""
|
||||||
self.__events = []
|
self.__events = []
|
||||||
self.__address = address
|
self.__address = address
|
||||||
self.__sock = self.__get_sock()
|
self.__sock = self.__get_sock()
|
||||||
if server:
|
if server:
|
||||||
self.__sock.bind(self.__address)
|
self.__sock.bind(self.__address)
|
||||||
self.__sock.listen(1)
|
self.__sock.listen(1)
|
||||||
|
|
||||||
def __get_sock(self):
|
def __get_sock(self):
|
||||||
if isinstance(self.__address, tuple):
|
if isinstance(self.__address, tuple):
|
||||||
family = socket.AF_INET
|
family = socket.AF_INET
|
||||||
else:
|
else:
|
||||||
family = socket.AF_UNIX
|
family = socket.AF_UNIX
|
||||||
return socket.socket(family, socket.SOCK_STREAM)
|
return socket.socket(family, socket.SOCK_STREAM)
|
||||||
|
|
||||||
def __negotiate_capabilities(self):
|
def __negotiate_capabilities(self):
|
||||||
greeting = self.__json_read()
|
greeting = self.__json_read()
|
||||||
if greeting is None or 'QMP' not in greeting:
|
if greeting is None or 'QMP' not in greeting:
|
||||||
raise QMPConnectError
|
raise QMPConnectError
|
||||||
# Greeting seems ok, negotiate capabilities
|
# Greeting seems ok, negotiate capabilities
|
||||||
resp = self.cmd('qmp_capabilities')
|
resp = self.cmd('qmp_capabilities')
|
||||||
if "return" in resp:
|
if "return" in resp:
|
||||||
return greeting
|
return greeting
|
||||||
raise QMPCapabilitiesError
|
raise QMPCapabilitiesError
|
||||||
|
|
||||||
def __json_read(self, only_event=False):
|
def __json_read(self, only_event=False):
|
||||||
while True:
|
while True:
|
||||||
data = self.__sockfile.readline()
|
data = self.__sockfile.readline()
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
resp = json.loads(data)
|
resp = json.loads(data)
|
||||||
if 'event' in resp:
|
if 'event' in resp:
|
||||||
self.__events.append(resp)
|
self.__events.append(resp)
|
||||||
if not only_event:
|
if not only_event:
|
||||||
continue
|
continue
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
error = socket.error
|
error = socket.error
|
||||||
|
|
||||||
def connect(self, negotiate=True):
|
def connect(self, negotiate=True):
|
||||||
"""
|
"""
|
||||||
Connect to the QMP Monitor and perform capabilities negotiation.
|
Connect to the QMP Monitor and perform capabilities negotiation.
|
||||||
|
|
||||||
@return QMP greeting dict
|
@return QMP greeting dict
|
||||||
@raise socket.error on socket connection errors
|
@raise socket.error on socket connection errors
|
||||||
@raise QMPConnectError if the greeting is not received
|
@raise QMPConnectError if the greeting is not received
|
||||||
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
||||||
"""
|
"""
|
||||||
self.__sock.connect(self.__address)
|
self.__sock.connect(self.__address)
|
||||||
self.__sockfile = self.__sock.makefile()
|
self.__sockfile = self.__sock.makefile()
|
||||||
if negotiate:
|
if negotiate:
|
||||||
return self.__negotiate_capabilities()
|
return self.__negotiate_capabilities()
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
"""
|
"""
|
||||||
Await connection from QMP Monitor and perform capabilities negotiation.
|
Await connection from QMP Monitor and perform capabilities negotiation.
|
||||||
|
|
||||||
@return QMP greeting dict
|
@return QMP greeting dict
|
||||||
@raise socket.error on socket connection errors
|
@raise socket.error on socket connection errors
|
||||||
@raise QMPConnectError if the greeting is not received
|
@raise QMPConnectError if the greeting is not received
|
||||||
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
@raise QMPCapabilitiesError if fails to negotiate capabilities
|
||||||
"""
|
"""
|
||||||
self.__sock, _ = self.__sock.accept()
|
self.__sock, _ = self.__sock.accept()
|
||||||
self.__sockfile = self.__sock.makefile()
|
self.__sockfile = self.__sock.makefile()
|
||||||
return self.__negotiate_capabilities()
|
return self.__negotiate_capabilities()
|
||||||
|
|
||||||
def cmd_obj(self, qmp_cmd):
|
def cmd_obj(self, qmp_cmd):
|
||||||
"""
|
"""
|
||||||
Send a QMP command to the QMP Monitor.
|
Send a QMP command to the QMP Monitor.
|
||||||
|
|
||||||
@param qmp_cmd: QMP command to be sent as a Python dict
|
@param qmp_cmd: QMP command to be sent as a Python dict
|
||||||
@return QMP response as a Python dict or None if the connection has
|
@return QMP response as a Python dict or None if the connection has
|
||||||
been closed
|
been closed
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.__sock.sendall(bytes(json.dumps(qmp_cmd), 'UTF-8'))
|
self.__sock.sendall(bytes(json.dumps(qmp_cmd), 'UTF-8'))
|
||||||
except socket.error as err:
|
except socket.error as err:
|
||||||
if err[0] == errno.EPIPE:
|
if err[0] == errno.EPIPE:
|
||||||
return
|
return
|
||||||
raise socket.error(err)
|
raise socket.error(err)
|
||||||
return self.__json_read()
|
return self.__json_read()
|
||||||
|
|
||||||
def cmd(self, name, args=None, id=None):
|
def cmd(self, name, args=None, id=None):
|
||||||
"""
|
"""
|
||||||
Build a QMP command and send it to the QMP Monitor.
|
Build a QMP command and send it to the QMP Monitor.
|
||||||
|
|
||||||
@param name: command name (string)
|
@param name: command name (string)
|
||||||
@param args: command arguments (dict)
|
@param args: command arguments (dict)
|
||||||
@param id: command id (dict, list, string or int)
|
@param id: command id (dict, list, string or int)
|
||||||
"""
|
"""
|
||||||
qmp_cmd = { 'execute': name }
|
qmp_cmd = { 'execute': name }
|
||||||
if args:
|
if args:
|
||||||
qmp_cmd['arguments'] = args
|
qmp_cmd['arguments'] = args
|
||||||
if id:
|
if id:
|
||||||
qmp_cmd['id'] = id
|
qmp_cmd['id'] = id
|
||||||
return self.cmd_obj(qmp_cmd)
|
return self.cmd_obj(qmp_cmd)
|
||||||
|
|
||||||
def command(self, cmd, **kwds):
|
def command(self, cmd, **kwds):
|
||||||
ret = self.cmd(cmd, kwds)
|
ret = self.cmd(cmd, kwds)
|
||||||
if 'error' in ret:
|
if 'error' in ret:
|
||||||
raise Exception(ret['error']['desc'])
|
raise Exception(ret['error']['desc'])
|
||||||
return ret['return']
|
return ret['return']
|
||||||
|
|
||||||
def pull_event(self, wait=False):
|
def pull_event(self, wait=False):
|
||||||
"""
|
"""
|
||||||
Get and delete the first available QMP event.
|
Get and delete the first available QMP event.
|
||||||
|
|
||||||
@param wait: block until an event is available (bool)
|
@param wait: block until an event is available (bool)
|
||||||
"""
|
"""
|
||||||
self.__sock.setblocking(0)
|
self.__sock.setblocking(0)
|
||||||
try:
|
try:
|
||||||
self.__json_read()
|
self.__json_read()
|
||||||
except socket.error as err:
|
except socket.error as err:
|
||||||
if err[0] == errno.EAGAIN:
|
if err[0] == errno.EAGAIN:
|
||||||
# No data available
|
# No data available
|
||||||
pass
|
pass
|
||||||
self.__sock.setblocking(1)
|
self.__sock.setblocking(1)
|
||||||
if not self.__events and wait:
|
if not self.__events and wait:
|
||||||
self.__json_read(only_event=True)
|
self.__json_read(only_event=True)
|
||||||
event = self.__events[0]
|
|
||||||
del self.__events[0]
|
|
||||||
return event
|
|
||||||
|
|
||||||
def get_events(self, wait=False):
|
if len(self.__events) == 0:
|
||||||
"""
|
return None
|
||||||
Get a list of available QMP events.
|
else:
|
||||||
|
event = self.__events[0]
|
||||||
|
del self.__events[0]
|
||||||
|
return event
|
||||||
|
|
||||||
@param wait: block until an event is available (bool)
|
def get_events(self, wait=False):
|
||||||
"""
|
"""
|
||||||
self.__sock.setblocking(0)
|
Get a list of available QMP events.
|
||||||
try:
|
|
||||||
self.__json_read()
|
|
||||||
except socket.error as err:
|
|
||||||
if err[0] == errno.EAGAIN:
|
|
||||||
# No data available
|
|
||||||
pass
|
|
||||||
self.__sock.setblocking(1)
|
|
||||||
if not self.__events and wait:
|
|
||||||
self.__json_read(only_event=True)
|
|
||||||
return self.__events
|
|
||||||
|
|
||||||
def clear_events(self):
|
@param wait: block until an event is available (bool)
|
||||||
"""
|
"""
|
||||||
Clear current list of pending events.
|
self.__sock.setblocking(0)
|
||||||
"""
|
try:
|
||||||
self.__events = []
|
self.__json_read()
|
||||||
|
except socket.error as err:
|
||||||
|
if err[0] == errno.EAGAIN:
|
||||||
|
# No data available
|
||||||
|
pass
|
||||||
|
self.__sock.setblocking(1)
|
||||||
|
if not self.__events and wait:
|
||||||
|
self.__json_read(only_event=True)
|
||||||
|
return self.__events
|
||||||
|
|
||||||
def close(self):
|
def clear_events(self):
|
||||||
self.__sock.close()
|
"""
|
||||||
self.__sockfile.close()
|
Clear current list of pending events.
|
||||||
|
"""
|
||||||
|
self.__events = []
|
||||||
|
|
||||||
timeout = socket.timeout
|
def close(self):
|
||||||
|
self.__sock.close()
|
||||||
|
self.__sockfile.close()
|
||||||
|
|
||||||
def settimeout(self, timeout):
|
timeout = socket.timeout
|
||||||
self.__sock.settimeout(timeout)
|
|
||||||
|
def settimeout(self, timeout):
|
||||||
|
self.__sock.settimeout(timeout)
|
||||||
|
|||||||
Reference in New Issue
Block a user