qemu-block
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v2 23/25] python: remove the old QMP package


From: Beraldo Leal
Subject: Re: [PATCH v2 23/25] python: remove the old QMP package
Date: Thu, 16 Dec 2021 11:17:54 -0300

On Wed, Dec 15, 2021 at 02:39:37PM -0500, John Snow wrote:
> Thank you for your service!
> 
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>  python/PACKAGE.rst          |   4 +-
>  python/README.rst           |   2 +-
>  python/qemu/qmp/README.rst  |   9 -
>  python/qemu/qmp/__init__.py | 396 ------------------------------------
>  python/qemu/qmp/py.typed    |   0
>  python/setup.cfg            |   3 +-
>  6 files changed, 4 insertions(+), 410 deletions(-)
>  delete mode 100644 python/qemu/qmp/README.rst
>  delete mode 100644 python/qemu/qmp/__init__.py
>  delete mode 100644 python/qemu/qmp/py.typed
> 
> diff --git a/python/PACKAGE.rst b/python/PACKAGE.rst
> index b0b86cc4c3..ddfa9ba3f5 100644
> --- a/python/PACKAGE.rst
> +++ b/python/PACKAGE.rst
> @@ -8,11 +8,11 @@ to change at any time.
>  Usage
>  -----
>  
> -The ``qemu.qmp`` subpackage provides a library for communicating with
> +The ``qemu.aqmp`` subpackage provides a library for communicating with
>  QMP servers. The ``qemu.machine`` subpackage offers rudimentary
>  facilities for launching and managing QEMU processes. Refer to each
>  package's documentation
> -(``>>> help(qemu.qmp)``, ``>>> help(qemu.machine)``)
> +(``>>> help(qemu.aqmp)``, ``>>> help(qemu.machine)``)
>  for more information.
>  
>  Contributing
> diff --git a/python/README.rst b/python/README.rst
> index fcf74f69ea..eb5213337d 100644
> --- a/python/README.rst
> +++ b/python/README.rst
> @@ -3,7 +3,7 @@ QEMU Python Tooling
>  
>  This directory houses Python tooling used by the QEMU project to build,
>  configure, and test QEMU. It is organized by namespace (``qemu``), and
> -then by package (e.g. ``qemu/machine``, ``qemu/qmp``, etc).
> +then by package (e.g. ``qemu/machine``, ``qemu/aqmp``, etc).
>  
>  ``setup.py`` is used by ``pip`` to install this tooling to the current
>  environment. ``setup.cfg`` provides the packaging configuration used by
> diff --git a/python/qemu/qmp/README.rst b/python/qemu/qmp/README.rst
> deleted file mode 100644
> index 5bfb82535f..0000000000
> --- a/python/qemu/qmp/README.rst
> +++ /dev/null
> @@ -1,9 +0,0 @@
> -qemu.qmp package
> -================
> -
> -This package provides a library used for connecting to and communicating
> -with QMP servers. It is used extensively by iotests, vm tests,
> -avocado tests, and other utilities in the ./scripts directory. It is
> -not a fully-fledged SDK and is subject to change at any time.
> -
> -See the documentation in ``__init__.py`` for more information.
> diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py
> deleted file mode 100644
> index 4e08641154..0000000000
> --- a/python/qemu/qmp/__init__.py
> +++ /dev/null
> @@ -1,396 +0,0 @@
> -"""
> -QEMU Monitor Protocol (QMP) development library & tooling.
> -
> -This package provides a fairly low-level class for communicating to QMP
> -protocol servers, as implemented by QEMU, the QEMU Guest Agent, and the
> -QEMU Storage Daemon. This library is not intended for production use.
> -
> -`QEMUMonitorProtocol` is the primary class of interest, and all errors
> -raised derive from `QMPError`.
> -"""
> -
> -# Copyright (C) 2009, 2010 Red Hat Inc.
> -#
> -# Authors:
> -#  Luiz Capitulino <lcapitulino@redhat.com>
> -#
> -# This work is licensed under the terms of the GNU GPL, version 2.  See
> -# the COPYING file in the top-level directory.
> -
> -import errno
> -import json
> -import logging
> -import socket
> -import struct
> -from types import TracebackType
> -from typing import (
> -    Any,
> -    Dict,
> -    List,
> -    Optional,
> -    TextIO,
> -    Tuple,
> -    Type,
> -    TypeVar,
> -    Union,
> -    cast,
> -)
> -
> -
> -#: QMPMessage is an entire QMP message of any kind.
> -QMPMessage = Dict[str, Any]
> -
> -#: QMPReturnValue is the 'return' value of a command.
> -QMPReturnValue = object
> -
> -#: QMPObject is any object in a QMP message.
> -QMPObject = Dict[str, object]
> -
> -# QMPMessage can be outgoing commands or incoming events/returns.
> -# QMPReturnValue is usually a dict/json object, but due to QAPI's
> -# 'returns-whitelist', it can actually be anything.
> -#
> -# {'return': {}} is a QMPMessage,
> -# {} is the QMPReturnValue.
> -
> -
> -InternetAddrT = Tuple[str, int]
> -UnixAddrT = str
> -SocketAddrT = Union[InternetAddrT, UnixAddrT]
> -
> -
> -class QMPError(Exception):
> -    """
> -    QMP base exception
> -    """
> -
> -
> -class QMPConnectError(QMPError):
> -    """
> -    QMP connection exception
> -    """
> -
> -
> -class QMPCapabilitiesError(QMPError):
> -    """
> -    QMP negotiate capabilities exception
> -    """
> -
> -
> -class QMPTimeoutError(QMPError):
> -    """
> -    QMP timeout exception
> -    """
> -
> -
> -class QMPProtocolError(QMPError):
> -    """
> -    QMP protocol error; unexpected response
> -    """
> -
> -
> -class QMPResponseError(QMPError):
> -    """
> -    Represents erroneous QMP monitor reply
> -    """
> -    def __init__(self, reply: QMPMessage):
> -        try:
> -            desc = reply['error']['desc']
> -        except KeyError:
> -            desc = reply
> -        super().__init__(desc)
> -        self.reply = reply
> -
> -
> -class QEMUMonitorProtocol:
> -    """
> -    Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and 
> then
> -    allow to handle commands and events.
> -    """
> -
> -    #: Logger object for debugging messages
> -    logger = logging.getLogger('QMP')
> -
> -    def __init__(self, address: SocketAddrT,
> -                 server: bool = False,
> -                 nickname: Optional[str] = None):
> -        """
> -        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
> -        @param server: server mode listens on the socket (bool)
> -        @raise OSError on socket connection errors
> -        @note No connection is established, this is done by the connect() or
> -              accept() methods
> -        """
> -        self.__events: List[QMPMessage] = []
> -        self.__address = address
> -        self.__sock = self.__get_sock()
> -        self.__sockfile: Optional[TextIO] = None
> -        self._nickname = nickname
> -        if self._nickname:
> -            self.logger = logging.getLogger('QMP').getChild(self._nickname)
> -        if server:
> -            self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
> -            self.__sock.bind(self.__address)
> -            self.__sock.listen(1)
> -
> -    def __get_sock(self) -> socket.socket:
> -        if isinstance(self.__address, tuple):
> -            family = socket.AF_INET
> -        else:
> -            family = socket.AF_UNIX
> -        return socket.socket(family, socket.SOCK_STREAM)
> -
> -    def __negotiate_capabilities(self) -> QMPMessage:
> -        greeting = self.__json_read()
> -        if greeting is None or "QMP" not in greeting:
> -            raise QMPConnectError
> -        # Greeting seems ok, negotiate capabilities
> -        resp = self.cmd('qmp_capabilities')
> -        if resp and "return" in resp:
> -            return greeting
> -        raise QMPCapabilitiesError
> -
> -    def __json_read(self, only_event: bool = False) -> Optional[QMPMessage]:
> -        assert self.__sockfile is not None
> -        while True:
> -            data = self.__sockfile.readline()
> -            if not data:
> -                return None
> -            # By definition, any JSON received from QMP is a QMPMessage,
> -            # and we are asserting only at static analysis time that it
> -            # has a particular shape.
> -            resp: QMPMessage = json.loads(data)
> -            if 'event' in resp:
> -                self.logger.debug("<<< %s", resp)
> -                self.__events.append(resp)
> -                if not only_event:
> -                    continue
> -            return resp
> -
> -    def __get_events(self, wait: Union[bool, float] = False) -> None:
> -        """
> -        Check for new events in the stream and cache them in __events.
> -
> -        @param wait (bool): block until an event is available.
> -        @param wait (float): If wait is a float, treat it as a timeout value.
> -
> -        @raise QMPTimeoutError: If a timeout float is provided and the 
> timeout
> -                                period elapses.
> -        @raise QMPConnectError: If wait is True but no events could be
> -                                retrieved or if some other error occurred.
> -        """
> -
> -        # Current timeout and blocking status
> -        current_timeout = self.__sock.gettimeout()
> -
> -        # Check for new events regardless and pull them into the cache:
> -        self.__sock.settimeout(0)  # i.e. setblocking(False)
> -        try:
> -            self.__json_read()
> -        except OSError as err:
> -            # EAGAIN: No data available; not critical
> -            if err.errno != errno.EAGAIN:
> -                raise
> -        finally:
> -            self.__sock.settimeout(current_timeout)
> -
> -        # Wait for new events, if needed.
> -        # if wait is 0.0, this means "no wait" and is also implicitly false.
> -        if not self.__events and wait:
> -            if isinstance(wait, float):
> -                self.__sock.settimeout(wait)
> -            try:
> -                ret = self.__json_read(only_event=True)
> -            except socket.timeout as err:
> -                raise QMPTimeoutError("Timeout waiting for event") from err
> -            except Exception as err:
> -                msg = "Error while reading from socket"
> -                raise QMPConnectError(msg) from err
> -            finally:
> -                self.__sock.settimeout(current_timeout)
> -
> -            if ret is None:
> -                raise QMPConnectError("Error while reading from socket")
> -
> -    T = TypeVar('T')
> -
> -    def __enter__(self: T) -> T:
> -        # Implement context manager enter function.
> -        return self
> -
> -    def __exit__(self,
> -                 # pylint: disable=duplicate-code
> -                 # see https://github.com/PyCQA/pylint/issues/3619
> -                 exc_type: Optional[Type[BaseException]],
> -                 exc_val: Optional[BaseException],
> -                 exc_tb: Optional[TracebackType]) -> None:
> -        # Implement context manager exit function.
> -        self.close()
> -
> -    def connect(self, negotiate: bool = True) -> Optional[QMPMessage]:
> -        """
> -        Connect to the QMP Monitor and perform capabilities negotiation.
> -
> -        @return QMP greeting dict, or None if negotiate is false
> -        @raise OSError on socket connection errors
> -        @raise QMPConnectError if the greeting is not received
> -        @raise QMPCapabilitiesError if fails to negotiate capabilities
> -        """
> -        self.__sock.connect(self.__address)
> -        self.__sockfile = self.__sock.makefile(mode='r')
> -        if negotiate:
> -            return self.__negotiate_capabilities()
> -        return None
> -
> -    def accept(self, timeout: Optional[float] = 15.0) -> QMPMessage:
> -        """
> -        Await connection from QMP Monitor and perform capabilities 
> negotiation.
> -
> -        @param timeout: timeout in seconds (nonnegative float number, or
> -                        None). The value passed will set the behavior of the
> -                        underneath QMP socket as described in [1].
> -                        Default value is set to 15.0.
> -
> -        @return QMP greeting dict
> -        @raise OSError on socket connection errors
> -        @raise QMPConnectError if the greeting is not received
> -        @raise QMPCapabilitiesError if fails to negotiate capabilities
> -
> -        [1]
> -        
> https://docs.python.org/3/library/socket.html#socket.socket.settimeout
> -        """
> -        self.__sock.settimeout(timeout)
> -        self.__sock, _ = self.__sock.accept()
> -        self.__sockfile = self.__sock.makefile(mode='r')
> -        return self.__negotiate_capabilities()
> -
> -    def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
> -        """
> -        Send a QMP command to the QMP Monitor.
> -
> -        @param qmp_cmd: QMP command to be sent as a Python dict
> -        @return QMP response as a Python dict
> -        """
> -        self.logger.debug(">>> %s", qmp_cmd)
> -        self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8'))
> -        resp = self.__json_read()
> -        if resp is None:
> -            raise QMPConnectError("Unexpected empty reply from server")
> -        self.logger.debug("<<< %s", resp)
> -        return resp
> -
> -    def cmd(self, name: str,
> -            args: Optional[Dict[str, object]] = None,
> -            cmd_id: Optional[object] = None) -> QMPMessage:
> -        """
> -        Build a QMP command and send it to the QMP Monitor.
> -
> -        @param name: command name (string)
> -        @param args: command arguments (dict)
> -        @param cmd_id: command id (dict, list, string or int)
> -        """
> -        qmp_cmd: QMPMessage = {'execute': name}
> -        if args:
> -            qmp_cmd['arguments'] = args
> -        if cmd_id:
> -            qmp_cmd['id'] = cmd_id
> -        return self.cmd_obj(qmp_cmd)
> -
> -    def command(self, cmd: str, **kwds: object) -> QMPReturnValue:
> -        """
> -        Build and send a QMP command to the monitor, report errors if any
> -        """
> -        ret = self.cmd(cmd, kwds)
> -        if 'error' in ret:
> -            raise QMPResponseError(ret)
> -        if 'return' not in ret:
> -            raise QMPProtocolError(
> -                "'return' key not found in QMP response 
> '{}'".format(str(ret))
> -            )
> -        return cast(QMPReturnValue, ret['return'])
> -
> -    def pull_event(self,
> -                   wait: Union[bool, float] = False) -> Optional[QMPMessage]:
> -        """
> -        Pulls a single event.
> -
> -        @param wait (bool): block until an event is available.
> -        @param wait (float): If wait is a float, treat it as a timeout value.
> -
> -        @raise QMPTimeoutError: If a timeout float is provided and the 
> timeout
> -                                period elapses.
> -        @raise QMPConnectError: If wait is True but no events could be
> -                                retrieved or if some other error occurred.
> -
> -        @return The first available QMP event, or None.
> -        """
> -        self.__get_events(wait)
> -
> -        if self.__events:
> -            return self.__events.pop(0)
> -        return None
> -
> -    def get_events(self, wait: bool = False) -> List[QMPMessage]:
> -        """
> -        Get a list of available QMP events and clear all pending events.
> -
> -        @param wait (bool): block until an event is available.
> -        @param wait (float): If wait is a float, treat it as a timeout value.
> -
> -        @raise QMPTimeoutError: If a timeout float is provided and the 
> timeout
> -                                period elapses.
> -        @raise QMPConnectError: If wait is True but no events could be
> -                                retrieved or if some other error occurred.
> -
> -        @return The list of available QMP events.
> -        """
> -        self.__get_events(wait)
> -        events = self.__events
> -        self.__events = []
> -        return events
> -
> -    def clear_events(self) -> None:
> -        """
> -        Clear current list of pending events.
> -        """
> -        self.__events = []
> -
> -    def close(self) -> None:
> -        """
> -        Close the socket and socket file.
> -        """
> -        if self.__sock:
> -            self.__sock.close()
> -        if self.__sockfile:
> -            self.__sockfile.close()
> -
> -    def settimeout(self, timeout: Optional[float]) -> None:
> -        """
> -        Set the socket timeout.
> -
> -        @param timeout (float): timeout in seconds (non-zero), or None.
> -        @note This is a wrap around socket.settimeout
> -
> -        @raise ValueError: if timeout was set to 0.
> -        """
> -        if timeout == 0:
> -            msg = "timeout cannot be 0; this engages non-blocking mode."
> -            msg += " Use 'None' instead to disable timeouts."
> -            raise ValueError(msg)
> -        self.__sock.settimeout(timeout)
> -
> -    def send_fd_scm(self, fd: int) -> None:
> -        """
> -        Send a file descriptor to the remote via SCM_RIGHTS.
> -        """
> -        if self.__sock.family != socket.AF_UNIX:
> -            raise RuntimeError("Can't use SCM_RIGHTS on non-AF_UNIX socket.")
> -
> -        self.__sock.sendmsg(
> -            [b' '],
> -            [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))]
> -        )
> diff --git a/python/qemu/qmp/py.typed b/python/qemu/qmp/py.typed
> deleted file mode 100644
> index e69de29bb2..0000000000
> diff --git a/python/setup.cfg b/python/setup.cfg
> index 510df23698..5140a5b322 100644
> --- a/python/setup.cfg
> +++ b/python/setup.cfg
> @@ -24,10 +24,9 @@ classifiers =
>  [options]
>  python_requires = >= 3.6
>  packages =
> -    qemu.qmp
> +    qemu.aqmp
>      qemu.machine
>      qemu.utils
> -    qemu.aqmp
>  
>  [options.package_data]
>  * = py.typed

Reviewed-by: Beraldo Leal <bleal@redhat.com>

--
Beraldo




reply via email to

[Prev in Thread] Current Thread [Next in Thread]