Commit 37628f11 authored by Stefan Hajnoczi's avatar Stefan Hajnoczi Committed by Luiz Capitulino
Browse files

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: default avatarStefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Signed-off-by: default avatarLuiz Capitulino <lcapitulino@redhat.com>
parent 91b8eddf
Loading
Loading
Loading
Loading
+32 −11
Original line number Diff line number Diff line
@@ -22,19 +22,24 @@ class QMPCapabilitiesError(QMPError):
    pass

class QEMUMonitorProtocol:
    def __init__(self, address):
    def __init__(self, address, server=False):
        """
        Create a QEMUMonitorProtocol class.

        @param address: QEMU address, can be either a unix socket path (string)
                        or a tuple in the form ( address, port ) for a TCP
                        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.__address = address
        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):
        if isinstance(self.__address, tuple):
@@ -43,6 +48,17 @@ class QEMUMonitorProtocol:
            family = socket.AF_UNIX
        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):
        while True:
            data = self.__sockfile.readline()
@@ -67,14 +83,19 @@ class QEMUMonitorProtocol:
        @raise QMPCapabilitiesError if fails to negotiate capabilities
        """
        self.__sock.connect(self.__address)
        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
        return self.__negotiate_capabilities()

    def accept(self):
        """
        Await connection from QMP Monitor and perform capabilities negotiation.

        @return QMP greeting dict
        @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):
        """