QMP: add server mode to QEMUMonitorProtocol

QEMU supports socket chardevs that establish connections like a server
or a client.  The QEMUMonitorProtocol class only supports connecting as
a client.  It is not possible to connect race-free when launching QEMU
since trying to connect before QEMU has bound and is listening on the
socket results in failure.

Add the QEMUMonitorProtocol(server=True) argument to bind and listen on
the socket.  The QEMU process can then be launched and connects to the
already existing QMP socket without a race condition:

  qmp = qmp.QEMUMonitorProtocol(monitor_path, server=True)
  popen = subprocess.Popen(args)
  qmp.accept()

Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2011-05-25 19:48:01 +01:00 committed by Luiz Capitulino
parent 91b8eddf41
commit 37628f11c6
1 changed files with 32 additions and 11 deletions

View File

@ -22,19 +22,24 @@ class QMPCapabilitiesError(QMPError):
pass pass
class QEMUMonitorProtocol: class QEMUMonitorProtocol:
def __init__(self, address): 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
@note No connection is established, this is done by the connect() method @param server: server mode listens on the socket (bool)
@raise socket.error on socket connection errors
@note No connection is established, this is done by the connect() or
accept() methods
""" """
self.__events = [] self.__events = []
self.__address = address self.__address = address
self.__sock = self.__get_sock() self.__sock = self.__get_sock()
self.__sockfile = self.__sock.makefile() if server:
self.__sock.bind(self.__address)
self.__sock.listen(1)
def __get_sock(self): def __get_sock(self):
if isinstance(self.__address, tuple): if isinstance(self.__address, tuple):
@ -43,6 +48,17 @@ class QEMUMonitorProtocol:
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):
self.__sockfile = self.__sock.makefile()
greeting = self.__json_read()
if greeting is None or not greeting.has_key('QMP'):
raise QMPConnectError
# Greeting seems ok, negotiate capabilities
resp = self.cmd('qmp_capabilities')
if "return" in resp:
return greeting
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()
@ -67,14 +83,19 @@ class QEMUMonitorProtocol:
@raise QMPCapabilitiesError if fails to negotiate capabilities @raise QMPCapabilitiesError if fails to negotiate capabilities
""" """
self.__sock.connect(self.__address) self.__sock.connect(self.__address)
greeting = self.__json_read() return self.__negotiate_capabilities()
if greeting is None or not greeting.has_key('QMP'):
raise QMPConnectError def accept(self):
# Greeting seems ok, negotiate capabilities """
resp = self.cmd('qmp_capabilities') Await connection from QMP Monitor and perform capabilities negotiation.
if "return" in resp:
return greeting @return QMP greeting dict
raise QMPCapabilitiesError @raise socket.error on socket connection errors
@raise QMPConnectError if the greeting is not received
@raise QMPCapabilitiesError if fails to negotiate capabilities
"""
self.__sock, _ = self.__sock.accept()
return self.__negotiate_capabilities()
def cmd_obj(self, qmp_cmd): def cmd_obj(self, qmp_cmd):
""" """