Python patches
A few fixes to the Python CI tests, a few fixes to the (async) QMP library, and a set of patches that begin to shift us towards using the new qmp lib. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAmHrSt4ACgkQfe+BBqr8 OQ4BLg/5AdhcWsAtKq+xZj/nz2DNAbvXmrGJRuVln1yofrj22w2MYUTGfpQ0m8JO Ezu+LYtSTPQAaQw54WByaliD5h2ucYl5W8H13cDc8NPZkbsX+dD7G99u4XkSIY4I sSCYDMKi4j/P+4YR2MN1Iol4362fWbi3O3rsRX6PqNymIAaaklDrH3QZCGMiBMjl 2OAcgba31uguyXnMuM3WY8XAnnRsib3wZ/a+a3WWcEsEm1HAUC0pb8VmrRzH5Rv9 CpR3EBYoVc3of96jd/qLjucnoUL0+K2RVN2qPeie3+o7yEM2VYj5o+cG2H8pEG5p Fk/J7kqs0XWBOeX3A3IlGqKEXFPGDjEJZpcjwd8+IhgA2Y/MByTqRr1EvrOSO+bg q3njEg5DsORQS/xgZrnAidk5fdgLj7Cv39LfsxMnv77RBnlLubEAet7pT1XtprAv DI7STKknVpPu0VtYI8ALVjVhpeCkIt95DXACMtPZiSJ5X1NdoY5qubV1y8/vsExI RMDMepcS2A75Un2DA1bkStHTPN2PSUfM15fmUCebxbHp53FlJCh44gxAAsfj9j41 xUmwSz1c81bCU4m+jsMBdNrbtkpPz/gX/3ZS8KqGoZmWN0wDkh3vEYFj5Y/310HY xmzug6o+tR7OD3bBGxZ73k9rn86X3+1PsYxOZjvYM0wiJIisPk4= =D2kj -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/jsnow-gitlab/tags/python-pull-request' into staging Python patches A few fixes to the Python CI tests, a few fixes to the (async) QMP library, and a set of patches that begin to shift us towards using the new qmp lib. # gpg: Signature made Sat 22 Jan 2022 00:07:58 GMT # gpg: using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [full] # Primary key fingerprint: FAEB 9711 A12C F475 812F 18F2 88A9 064D 1835 61EB # Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76 CBD0 7DEF 8106 AAFC 390E * remotes/jsnow-gitlab/tags/python-pull-request: scripts/render-block-graph: switch to AQMP scripts/cpu-x86-uarch-abi: switch to AQMP scripts/cpu-x86-uarch-abi: fix CLI parsing python: move qmp-shell under the AQMP package python: move qmp utilities to python/qemu/utils python/qmp: switch qmp-shell to AQMP python/qmp: switch qom tools to AQMP python/qmp: switch qemu-ga-client to AQMP python/qemu-ga-client: don't use deprecated CLI syntax in usage comment python/aqmp: rename AQMPError to QMPError python/aqmp: add SocketAddrT to package root python/aqmp: copy type definitions from qmp python/aqmp: handle asyncio.TimeoutError on execute() python/aqmp: add __del__ method to legacy interface python/aqmp: fix docstring typo python: use avocado's "new" runner python: pin setuptools below v60.0.0 Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
aeb0ae95b7
@ -68,6 +68,8 @@ $(QEMU_VENV_DIR) $(QEMU_VENV_DIR)/bin/activate: setup.cfg
|
||||
echo "ACTIVATE $(QEMU_VENV_DIR)"; \
|
||||
. $(QEMU_VENV_DIR)/bin/activate; \
|
||||
echo "INSTALL qemu[devel] $(QEMU_VENV_DIR)"; \
|
||||
pip install --disable-pip-version-check \
|
||||
"setuptools<60.0.0" 1>/dev/null; \
|
||||
make develop 1>/dev/null; \
|
||||
)
|
||||
@touch $(QEMU_VENV_DIR)
|
||||
|
@ -59,7 +59,7 @@ Package installation also normally provides executable console scripts,
|
||||
so that tools like ``qmp-shell`` are always available via $PATH. To
|
||||
invoke them without installation, you can invoke e.g.:
|
||||
|
||||
``> PYTHONPATH=~/src/qemu/python python3 -m qemu.qmp.qmp_shell``
|
||||
``> PYTHONPATH=~/src/qemu/python python3 -m qemu.aqmp.qmp_shell``
|
||||
|
||||
The mappings between console script name and python module path can be
|
||||
found in ``setup.cfg``.
|
||||
|
@ -1,5 +1,5 @@
|
||||
[run]
|
||||
test_runner = runner
|
||||
test_runner = nrunner
|
||||
|
||||
[simpletests]
|
||||
# Don't show stdout/stderr in the test *summary*
|
||||
|
@ -6,7 +6,7 @@ asynchronously with QMP protocol servers, as implemented by QEMU, the
|
||||
QEMU Guest Agent, and the QEMU Storage Daemon.
|
||||
|
||||
`QMPClient` provides the main functionality of this package. All errors
|
||||
raised by this library dervive from `AQMPError`, see `aqmp.error` for
|
||||
raised by this library derive from `QMPError`, see `aqmp.error` for
|
||||
additional detail. See `aqmp.events` for an in-depth tutorial on
|
||||
managing QMP events.
|
||||
"""
|
||||
@ -23,10 +23,15 @@ managing QMP events.
|
||||
|
||||
import logging
|
||||
|
||||
from .error import AQMPError
|
||||
from .error import QMPError
|
||||
from .events import EventListener
|
||||
from .message import Message
|
||||
from .protocol import ConnectError, Runstate, StateError
|
||||
from .protocol import (
|
||||
ConnectError,
|
||||
Runstate,
|
||||
SocketAddrT,
|
||||
StateError,
|
||||
)
|
||||
from .qmp_client import ExecInterruptedError, ExecuteError, QMPClient
|
||||
|
||||
|
||||
@ -43,9 +48,12 @@ __all__ = (
|
||||
'Runstate',
|
||||
|
||||
# Exceptions, most generic to most explicit
|
||||
'AQMPError',
|
||||
'QMPError',
|
||||
'StateError',
|
||||
'ConnectError',
|
||||
'ExecuteError',
|
||||
'ExecInterruptedError',
|
||||
|
||||
# Type aliases
|
||||
'SocketAddrT',
|
||||
)
|
||||
|
@ -1,21 +1,21 @@
|
||||
"""
|
||||
AQMP Error Classes
|
||||
QMP Error Classes
|
||||
|
||||
This package seeks to provide semantic error classes that are intended
|
||||
to be used directly by clients when they would like to handle particular
|
||||
semantic failures (e.g. "failed to connect") without needing to know the
|
||||
enumeration of possible reasons for that failure.
|
||||
|
||||
AQMPError serves as the ancestor for all exceptions raised by this
|
||||
QMPError serves as the ancestor for all exceptions raised by this
|
||||
package, and is suitable for use in handling semantic errors from this
|
||||
library. In most cases, individual public methods will attempt to catch
|
||||
and re-encapsulate various exceptions to provide a semantic
|
||||
error-handling interface.
|
||||
|
||||
.. admonition:: AQMP Exception Hierarchy Reference
|
||||
.. admonition:: QMP Exception Hierarchy Reference
|
||||
|
||||
| `Exception`
|
||||
| +-- `AQMPError`
|
||||
| +-- `QMPError`
|
||||
| +-- `ConnectError`
|
||||
| +-- `StateError`
|
||||
| +-- `ExecInterruptedError`
|
||||
@ -31,11 +31,11 @@ error-handling interface.
|
||||
"""
|
||||
|
||||
|
||||
class AQMPError(Exception):
|
||||
class QMPError(Exception):
|
||||
"""Abstract error class for all errors originating from this package."""
|
||||
|
||||
|
||||
class ProtocolError(AQMPError):
|
||||
class ProtocolError(QMPError):
|
||||
"""
|
||||
Abstract error class for protocol failures.
|
||||
|
||||
|
@ -443,7 +443,7 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from .error import AQMPError
|
||||
from .error import QMPError
|
||||
from .message import Message
|
||||
|
||||
|
||||
@ -451,7 +451,7 @@ EventNames = Union[str, Iterable[str], None]
|
||||
EventFilter = Callable[[Message], bool]
|
||||
|
||||
|
||||
class ListenerError(AQMPError):
|
||||
class ListenerError(QMPError):
|
||||
"""
|
||||
Generic error class for `EventListener`-related problems.
|
||||
"""
|
||||
|
@ -6,7 +6,9 @@ This class pretends to be qemu.qmp.QEMUMonitorProtocol.
|
||||
|
||||
import asyncio
|
||||
from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
Dict,
|
||||
List,
|
||||
Optional,
|
||||
TypeVar,
|
||||
@ -14,11 +16,32 @@ from typing import (
|
||||
)
|
||||
|
||||
import qemu.qmp
|
||||
from qemu.qmp import QMPMessage, QMPReturnValue, SocketAddrT
|
||||
|
||||
from .error import QMPError
|
||||
from .protocol import Runstate, SocketAddrT
|
||||
from .qmp_client import QMPClient
|
||||
|
||||
|
||||
# (Temporarily) Re-export QMPBadPortError
|
||||
QMPBadPortError = qemu.qmp.QMPBadPortError
|
||||
|
||||
#: 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.
|
||||
|
||||
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
|
||||
@ -136,3 +159,19 @@ class QEMUMonitorProtocol(qemu.qmp.QEMUMonitorProtocol):
|
||||
|
||||
def send_fd_scm(self, fd: int) -> None:
|
||||
self._aqmp.send_fd_scm(fd)
|
||||
|
||||
def __del__(self) -> None:
|
||||
if self._aqmp.runstate == Runstate.IDLE:
|
||||
return
|
||||
|
||||
if not self._aloop.is_running():
|
||||
self.close()
|
||||
else:
|
||||
# Garbage collection ran while the event loop was running.
|
||||
# Nothing we can do about it now, but if we don't raise our
|
||||
# own error, the user will be treated to a lot of traceback
|
||||
# they might not understand.
|
||||
raise QMPError(
|
||||
"QEMUMonitorProtocol.close()"
|
||||
" was not called before object was garbage collected"
|
||||
)
|
||||
|
@ -29,7 +29,7 @@ from typing import (
|
||||
cast,
|
||||
)
|
||||
|
||||
from .error import AQMPError
|
||||
from .error import QMPError
|
||||
from .util import (
|
||||
bottom_half,
|
||||
create_task,
|
||||
@ -46,6 +46,10 @@ T = TypeVar('T')
|
||||
_U = TypeVar('_U')
|
||||
_TaskFN = Callable[[], Awaitable[None]] # aka ``async def func() -> None``
|
||||
|
||||
InternetAddrT = Tuple[str, int]
|
||||
UnixAddrT = str
|
||||
SocketAddrT = Union[UnixAddrT, InternetAddrT]
|
||||
|
||||
|
||||
class Runstate(Enum):
|
||||
"""Protocol session runstate."""
|
||||
@ -61,7 +65,7 @@ class Runstate(Enum):
|
||||
DISCONNECTING = 3
|
||||
|
||||
|
||||
class ConnectError(AQMPError):
|
||||
class ConnectError(QMPError):
|
||||
"""
|
||||
Raised when the initial connection process has failed.
|
||||
|
||||
@ -86,7 +90,7 @@ class ConnectError(AQMPError):
|
||||
return f"{self.error_message}: {cause}"
|
||||
|
||||
|
||||
class StateError(AQMPError):
|
||||
class StateError(QMPError):
|
||||
"""
|
||||
An API command (connect, execute, etc) was issued at an inappropriate time.
|
||||
|
||||
@ -257,7 +261,7 @@ class AsyncProtocol(Generic[T]):
|
||||
|
||||
@upper_half
|
||||
@require(Runstate.IDLE)
|
||||
async def accept(self, address: Union[str, Tuple[str, int]],
|
||||
async def accept(self, address: SocketAddrT,
|
||||
ssl: Optional[SSLContext] = None) -> None:
|
||||
"""
|
||||
Accept a connection and begin processing message queues.
|
||||
@ -275,7 +279,7 @@ class AsyncProtocol(Generic[T]):
|
||||
|
||||
@upper_half
|
||||
@require(Runstate.IDLE)
|
||||
async def connect(self, address: Union[str, Tuple[str, int]],
|
||||
async def connect(self, address: SocketAddrT,
|
||||
ssl: Optional[SSLContext] = None) -> None:
|
||||
"""
|
||||
Connect to the server and begin processing message queues.
|
||||
@ -337,7 +341,7 @@ class AsyncProtocol(Generic[T]):
|
||||
|
||||
@upper_half
|
||||
async def _new_session(self,
|
||||
address: Union[str, Tuple[str, int]],
|
||||
address: SocketAddrT,
|
||||
ssl: Optional[SSLContext] = None,
|
||||
accept: bool = False) -> None:
|
||||
"""
|
||||
@ -359,7 +363,7 @@ class AsyncProtocol(Generic[T]):
|
||||
This exception will wrap a more concrete one. In most cases,
|
||||
the wrapped exception will be `OSError` or `EOFError`. If a
|
||||
protocol-level failure occurs while establishing a new
|
||||
session, the wrapped error may also be an `AQMPError`.
|
||||
session, the wrapped error may also be an `QMPError`.
|
||||
"""
|
||||
assert self.runstate == Runstate.IDLE
|
||||
|
||||
@ -397,7 +401,7 @@ class AsyncProtocol(Generic[T]):
|
||||
@upper_half
|
||||
async def _establish_connection(
|
||||
self,
|
||||
address: Union[str, Tuple[str, int]],
|
||||
address: SocketAddrT,
|
||||
ssl: Optional[SSLContext] = None,
|
||||
accept: bool = False
|
||||
) -> None:
|
||||
@ -424,7 +428,7 @@ class AsyncProtocol(Generic[T]):
|
||||
await self._do_connect(address, ssl)
|
||||
|
||||
@upper_half
|
||||
async def _do_accept(self, address: Union[str, Tuple[str, int]],
|
||||
async def _do_accept(self, address: SocketAddrT,
|
||||
ssl: Optional[SSLContext] = None) -> None:
|
||||
"""
|
||||
Acting as the transport server, accept a single connection.
|
||||
@ -482,7 +486,7 @@ class AsyncProtocol(Generic[T]):
|
||||
self.logger.debug("Connection accepted.")
|
||||
|
||||
@upper_half
|
||||
async def _do_connect(self, address: Union[str, Tuple[str, int]],
|
||||
async def _do_connect(self, address: SocketAddrT,
|
||||
ssl: Optional[SSLContext] = None) -> None:
|
||||
"""
|
||||
Acting as the transport client, initiate a connection to a server.
|
||||
|
@ -20,7 +20,7 @@ from typing import (
|
||||
cast,
|
||||
)
|
||||
|
||||
from .error import AQMPError, ProtocolError
|
||||
from .error import ProtocolError, QMPError
|
||||
from .events import Events
|
||||
from .message import Message
|
||||
from .models import ErrorResponse, Greeting
|
||||
@ -66,7 +66,7 @@ class NegotiationError(_WrappedProtocolError):
|
||||
"""
|
||||
|
||||
|
||||
class ExecuteError(AQMPError):
|
||||
class ExecuteError(QMPError):
|
||||
"""
|
||||
Exception raised by `QMPClient.execute()` on RPC failure.
|
||||
|
||||
@ -87,7 +87,7 @@ class ExecuteError(AQMPError):
|
||||
self.error_class: str = error_response.error.class_
|
||||
|
||||
|
||||
class ExecInterruptedError(AQMPError):
|
||||
class ExecInterruptedError(QMPError):
|
||||
"""
|
||||
Exception raised by `execute()` (et al) when an RPC is interrupted.
|
||||
|
||||
@ -435,7 +435,11 @@ class QMPClient(AsyncProtocol[Message], Events):
|
||||
msg_id = msg['id']
|
||||
|
||||
self._pending[msg_id] = asyncio.Queue(maxsize=1)
|
||||
await self._outgoing.put(msg)
|
||||
try:
|
||||
await self._outgoing.put(msg)
|
||||
except:
|
||||
del self._pending[msg_id]
|
||||
raise
|
||||
|
||||
return msg_id
|
||||
|
||||
@ -452,9 +456,9 @@ class QMPClient(AsyncProtocol[Message], Events):
|
||||
was lost, or some other problem.
|
||||
"""
|
||||
queue = self._pending[msg_id]
|
||||
result = await queue.get()
|
||||
|
||||
try:
|
||||
result = await queue.get()
|
||||
if isinstance(result, ExecInterruptedError):
|
||||
raise result
|
||||
return result
|
||||
@ -637,7 +641,7 @@ class QMPClient(AsyncProtocol[Message], Events):
|
||||
sock = self._writer.transport.get_extra_info('socket')
|
||||
|
||||
if sock.family != socket.AF_UNIX:
|
||||
raise AQMPError("Sending file descriptors requires a UNIX socket.")
|
||||
raise QMPError("Sending file descriptors requires a UNIX socket.")
|
||||
|
||||
if not hasattr(sock, 'sendmsg'):
|
||||
# We need to void the warranty sticker.
|
||||
|
@ -95,8 +95,13 @@ from typing import (
|
||||
Sequence,
|
||||
)
|
||||
|
||||
from qemu import qmp
|
||||
from qemu.qmp import QMPMessage
|
||||
from qemu.aqmp import ConnectError, QMPError, SocketAddrT
|
||||
from qemu.aqmp.legacy import (
|
||||
QEMUMonitorProtocol,
|
||||
QMPBadPortError,
|
||||
QMPMessage,
|
||||
QMPObject,
|
||||
)
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -125,7 +130,7 @@ class QMPCompleter:
|
||||
return None
|
||||
|
||||
|
||||
class QMPShellError(qmp.QMPError):
|
||||
class QMPShellError(QMPError):
|
||||
"""
|
||||
QMP Shell Base error class.
|
||||
"""
|
||||
@ -153,7 +158,7 @@ class FuzzyJSON(ast.NodeTransformer):
|
||||
return node
|
||||
|
||||
|
||||
class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
class QMPShell(QEMUMonitorProtocol):
|
||||
"""
|
||||
QMPShell provides a basic readline-based QMP shell.
|
||||
|
||||
@ -161,7 +166,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
:param pretty: Pretty-print QMP messages.
|
||||
:param verbose: Echo outgoing QMP messages to console.
|
||||
"""
|
||||
def __init__(self, address: qmp.SocketAddrT,
|
||||
def __init__(self, address: SocketAddrT,
|
||||
pretty: bool = False, verbose: bool = False):
|
||||
super().__init__(address)
|
||||
self._greeting: Optional[QMPMessage] = None
|
||||
@ -237,7 +242,7 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
|
||||
def _cli_expr(self,
|
||||
tokens: Sequence[str],
|
||||
parent: qmp.QMPObject) -> None:
|
||||
parent: QMPObject) -> None:
|
||||
for arg in tokens:
|
||||
(key, sep, val) = arg.partition('=')
|
||||
if sep != '=':
|
||||
@ -403,7 +408,7 @@ class HMPShell(QMPShell):
|
||||
:param pretty: Pretty-print QMP messages.
|
||||
:param verbose: Echo outgoing QMP messages to console.
|
||||
"""
|
||||
def __init__(self, address: qmp.SocketAddrT,
|
||||
def __init__(self, address: SocketAddrT,
|
||||
pretty: bool = False, verbose: bool = False):
|
||||
super().__init__(address, pretty, verbose)
|
||||
self._cpu_index = 0
|
||||
@ -512,19 +517,17 @@ def main() -> None:
|
||||
|
||||
try:
|
||||
address = shell_class.parse_address(args.qmp_server)
|
||||
except qmp.QMPBadPortError:
|
||||
except QMPBadPortError:
|
||||
parser.error(f"Bad port number: {args.qmp_server}")
|
||||
return # pycharm doesn't know error() is noreturn
|
||||
|
||||
with shell_class(address, args.pretty, args.verbose) as qemu:
|
||||
try:
|
||||
qemu.connect(negotiate=not args.skip_negotiation)
|
||||
except qmp.QMPConnectError:
|
||||
die("Didn't get QMP greeting message")
|
||||
except qmp.QMPCapabilitiesError:
|
||||
die("Couldn't negotiate capabilities")
|
||||
except OSError as err:
|
||||
die(f"Couldn't connect to {args.qmp_server}: {err!s}")
|
||||
except ConnectError as err:
|
||||
if isinstance(err.exc, OSError):
|
||||
die(f"Couldn't connect to {args.qmp_server}: {err!s}")
|
||||
die(str(err))
|
||||
|
||||
for _ in qemu.repl():
|
||||
pass
|
@ -5,7 +5,7 @@ Usage:
|
||||
|
||||
Start QEMU with:
|
||||
|
||||
# qemu [...] -chardev socket,path=/tmp/qga.sock,server,wait=off,id=qga0 \
|
||||
# qemu [...] -chardev socket,path=/tmp/qga.sock,server=on,wait=off,id=qga0 \
|
||||
-device virtio-serial \
|
||||
-device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
|
||||
|
||||
@ -37,8 +37,8 @@ See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
|
||||
# the COPYING file in the top-level directory.
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import base64
|
||||
import errno
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
@ -50,8 +50,8 @@ from typing import (
|
||||
Sequence,
|
||||
)
|
||||
|
||||
from qemu import qmp
|
||||
from qemu.qmp import SocketAddrT
|
||||
from qemu.aqmp import ConnectError, SocketAddrT
|
||||
from qemu.aqmp.legacy import QEMUMonitorProtocol
|
||||
|
||||
|
||||
# This script has not seen many patches or careful attention in quite
|
||||
@ -61,7 +61,7 @@ from qemu.qmp import SocketAddrT
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
|
||||
class QemuGuestAgent(qmp.QEMUMonitorProtocol):
|
||||
class QemuGuestAgent(QEMUMonitorProtocol):
|
||||
def __getattr__(self, name: str) -> Callable[..., Any]:
|
||||
def wrapper(**kwds: object) -> object:
|
||||
return self.command('guest-' + name.replace('_', '-'), **kwds)
|
||||
@ -149,7 +149,7 @@ class QemuGuestAgentClient:
|
||||
self.qga.settimeout(timeout)
|
||||
try:
|
||||
self.qga.ping()
|
||||
except TimeoutError:
|
||||
except asyncio.TimeoutError:
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -172,7 +172,7 @@ class QemuGuestAgentClient:
|
||||
try:
|
||||
getattr(self.qga, 'suspend' + '_' + mode)()
|
||||
# On error exception will raise
|
||||
except TimeoutError:
|
||||
except asyncio.TimeoutError:
|
||||
# On success command will timed out
|
||||
return
|
||||
|
||||
@ -182,7 +182,7 @@ class QemuGuestAgentClient:
|
||||
|
||||
try:
|
||||
self.qga.shutdown(mode=mode)
|
||||
except TimeoutError:
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
|
||||
|
||||
@ -277,7 +277,7 @@ commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
|
||||
|
||||
def send_command(address: str, cmd: str, args: Sequence[str]) -> None:
|
||||
if not os.path.exists(address):
|
||||
print('%s not found' % address)
|
||||
print(f"'{address}' not found. (Is QEMU running?)")
|
||||
sys.exit(1)
|
||||
|
||||
if cmd not in commands:
|
||||
@ -287,10 +287,10 @@ def send_command(address: str, cmd: str, args: Sequence[str]) -> None:
|
||||
|
||||
try:
|
||||
client = QemuGuestAgentClient(address)
|
||||
except OSError as err:
|
||||
except ConnectError as err:
|
||||
print(err)
|
||||
if err.errno == errno.ECONNREFUSED:
|
||||
print('Hint: qemu is not running?')
|
||||
if isinstance(err.exc, ConnectionError):
|
||||
print('(Is QEMU running?)')
|
||||
sys.exit(1)
|
||||
|
||||
if cmd == 'fsfreeze' and args[0] == 'freeze':
|
@ -32,7 +32,8 @@ QOM commands:
|
||||
|
||||
import argparse
|
||||
|
||||
from . import QMPResponseError
|
||||
from qemu.aqmp import ExecuteError
|
||||
|
||||
from .qom_common import QOMCommand
|
||||
|
||||
|
||||
@ -233,7 +234,7 @@ class QOMTree(QOMCommand):
|
||||
rsp = self.qmp.command('qom-get', path=path,
|
||||
property=item.name)
|
||||
print(f" {item.name}: {rsp} ({item.type})")
|
||||
except QMPResponseError as err:
|
||||
except ExecuteError as err:
|
||||
print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})")
|
||||
print('')
|
||||
for item in items:
|
@ -27,7 +27,8 @@ from typing import (
|
||||
TypeVar,
|
||||
)
|
||||
|
||||
from . import QEMUMonitorProtocol, QMPError
|
||||
from qemu.aqmp import QMPError
|
||||
from qemu.aqmp.legacy import QEMUMonitorProtocol
|
||||
|
||||
|
||||
class ObjectPropertyInfo:
|
@ -48,7 +48,8 @@ from typing import (
|
||||
import fuse
|
||||
from fuse import FUSE, FuseOSError, Operations
|
||||
|
||||
from . import QMPResponseError
|
||||
from qemu.aqmp import ExecuteError
|
||||
|
||||
from .qom_common import QOMCommand
|
||||
|
||||
|
||||
@ -99,7 +100,7 @@ class QOMFuse(QOMCommand, Operations):
|
||||
try:
|
||||
self.qom_list(path)
|
||||
return True
|
||||
except QMPResponseError:
|
||||
except ExecuteError:
|
||||
return False
|
||||
|
||||
def is_property(self, path: str) -> bool:
|
||||
@ -112,7 +113,7 @@ class QOMFuse(QOMCommand, Operations):
|
||||
if item.name == prop:
|
||||
return True
|
||||
return False
|
||||
except QMPResponseError:
|
||||
except ExecuteError:
|
||||
return False
|
||||
|
||||
def is_link(self, path: str) -> bool:
|
||||
@ -125,7 +126,7 @@ class QOMFuse(QOMCommand, Operations):
|
||||
if item.name == prop and item.link:
|
||||
return True
|
||||
return False
|
||||
except QMPResponseError:
|
||||
except ExecuteError:
|
||||
return False
|
||||
|
||||
def read(self, path: str, size: int, offset: int, fh: IO[bytes]) -> bytes:
|
||||
@ -138,7 +139,7 @@ class QOMFuse(QOMCommand, Operations):
|
||||
try:
|
||||
data = str(self.qmp.command('qom-get', path=path, property=prop))
|
||||
data += '\n' # make values shell friendly
|
||||
except QMPResponseError as err:
|
||||
except ExecuteError as err:
|
||||
raise FuseOSError(EPERM) from err
|
||||
|
||||
if offset > len(data):
|
@ -60,14 +60,14 @@ tui =
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
qom = qemu.qmp.qom:main
|
||||
qom-set = qemu.qmp.qom:QOMSet.entry_point
|
||||
qom-get = qemu.qmp.qom:QOMGet.entry_point
|
||||
qom-list = qemu.qmp.qom:QOMList.entry_point
|
||||
qom-tree = qemu.qmp.qom:QOMTree.entry_point
|
||||
qom-fuse = qemu.qmp.qom_fuse:QOMFuse.entry_point [fuse]
|
||||
qemu-ga-client = qemu.qmp.qemu_ga_client:main
|
||||
qmp-shell = qemu.qmp.qmp_shell:main
|
||||
qom = qemu.utils.qom:main
|
||||
qom-set = qemu.utils.qom:QOMSet.entry_point
|
||||
qom-get = qemu.utils.qom:QOMGet.entry_point
|
||||
qom-list = qemu.utils.qom:QOMList.entry_point
|
||||
qom-tree = qemu.utils.qom:QOMTree.entry_point
|
||||
qom-fuse = qemu.utils.qom_fuse:QOMFuse.entry_point [fuse]
|
||||
qemu-ga-client = qemu.utils.qemu_ga_client:main
|
||||
qmp-shell = qemu.aqmp.qmp_shell:main
|
||||
aqmp-tui = qemu.aqmp.aqmp_tui:main [tui]
|
||||
|
||||
[flake8]
|
||||
@ -80,7 +80,7 @@ python_version = 3.6
|
||||
warn_unused_configs = True
|
||||
namespace_packages = True
|
||||
|
||||
[mypy-qemu.qmp.qom_fuse]
|
||||
[mypy-qemu.utils.qom_fuse]
|
||||
# fusepy has no type stubs:
|
||||
allow_subclassing_any = True
|
||||
|
||||
@ -163,6 +163,7 @@ deps =
|
||||
.[devel]
|
||||
.[fuse] # Workaround to trigger tox venv rebuild
|
||||
.[tui] # Workaround to trigger tox venv rebuild
|
||||
setuptools < 60 # Workaround, please see commit msg.
|
||||
commands =
|
||||
make check
|
||||
|
||||
|
@ -6,10 +6,10 @@
|
||||
# compatibility levels for each CPU model.
|
||||
#
|
||||
|
||||
from qemu import qmp
|
||||
from qemu.aqmp.legacy import QEMUMonitorProtocol
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 1:
|
||||
if len(sys.argv) != 2:
|
||||
print("syntax: %s QMP-SOCK\n\n" % __file__ +
|
||||
"Where QMP-SOCK points to a QEMU process such as\n\n" +
|
||||
" # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait " +
|
||||
@ -66,8 +66,7 @@ levels = [
|
||||
|
||||
|
||||
sock = sys.argv[1]
|
||||
cmd = sys.argv[2]
|
||||
shell = qmp.QEMUMonitorProtocol(sock)
|
||||
shell = QEMUMonitorProtocol(sock)
|
||||
shell.connect()
|
||||
|
||||
models = shell.cmd("query-cpu-definitions")
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp import qemu_ga_client
|
||||
from qemu.utils import qemu_ga_client
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp import qmp_shell
|
||||
from qemu.aqmp import qmp_shell
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp.qom_fuse import QOMFuse
|
||||
from qemu.utils.qom_fuse import QOMFuse
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp.qom import QOMGet
|
||||
from qemu.utils.qom import QOMGet
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp.qom import QOMList
|
||||
from qemu.utils.qom import QOMList
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp.qom import QOMSet
|
||||
from qemu.utils.qom import QOMSet
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,7 +4,7 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.qmp.qom import QOMTree
|
||||
from qemu.utils.qom import QOMTree
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -25,10 +25,8 @@ import json
|
||||
from graphviz import Digraph
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
|
||||
from qemu.qmp import (
|
||||
QEMUMonitorProtocol,
|
||||
QMPResponseError,
|
||||
)
|
||||
from qemu.aqmp import QMPError
|
||||
from qemu.aqmp.legacy import QEMUMonitorProtocol
|
||||
|
||||
|
||||
def perm(arr):
|
||||
@ -104,7 +102,7 @@ class LibvirtGuest():
|
||||
reply = json.loads(subprocess.check_output(ar))
|
||||
|
||||
if 'error' in reply:
|
||||
raise QMPResponseError(reply)
|
||||
raise QMPError(reply)
|
||||
|
||||
return reply['return']
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user