Pull request

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAmFl/8cACgkQfe+BBqr8
 OQ7I8A/+K1vF5/TYME+9jPGJFrVVrT4TgMbzQlwxy6WOPXdO1prGmBR5cu9EqyfM
 PcQ4meYqKMGIWOYcxVYpDcsTkzT94nc4VuMQx0b3jw9W6PIKd4zZd0Kt10FqepAE
 akr8dzcgsClVvSwBHo9/9fXT5WWLGdeCttR42Lpv25ggMKSLuiL51w+/TmxJ8zuU
 pUn7tq2AjlQugqkZm+qtSq18Lu/0trmtPH5FyMCs0xxAPYP/h0QwaT6DizYIsLdz
 xZDH7ds/VsnQ41l5d+xG2/uh55ZTSzGQrCyVGoDbSI4jR8mWh3nj5uj1FQLWfKHT
 z7k/0oQZpp+9yFJ7M+yvPtW9L/narWuxW33qgGKT+CpicmEz1ZcEt9erWVitwbB9
 diP2z3AZzZlbxSOf+QKzl4TgYaKmFXcR6tCleitdvkwbUvOeSu0DyqmgMikdiRQw
 04BN3deuPjcZPWB007vQ/hQXdt2rQdv9E+E0qLszDyFBIVE2WIR4BNRl4AmsaSgz
 Fj8JrAERtdlaTW/iO34wboob3k+6e8de+USbqEMj1SgQBbsYRy5SGW0WaOe9r3lV
 D0ywZ8nB9G2Bb4iAvY38S80c04bAkgYwZuA4fpVjT0NpdLB0IcOBxLTuGwFrgfOF
 CBZ0e69pIMouWvRS56WPFUd6rXFCLtI0nXqiHxR/wunprB0/krU=
 =Yxqp
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/jsnow/tags/python-pull-request' into staging

Pull request

# gpg: Signature made Tue 12 Oct 2021 02:36:07 PM PDT
# gpg:                using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E
# gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [full]

* remotes/jsnow/tags/python-pull-request:
  python, iotests: remove socket_scm_helper
  python/qmp: add send_fd_scm directly to QEMUMonitorProtocol
  python/qmp: clear events on get_events() call
  python/aqmp: Disable logging messages by default
  python/aqmp: Reduce severity of EOFError-caused loop terminations
  python/aqmp: Add dict conversion method to Greeting object
  python/aqmp: add send_fd_scm
  python/aqmp: Return cleared events from EventListener.clear()
  python/aqmp: add .empty() method to EventListener
  python/aqmp: add greeting property to QMPClient

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2021-10-12 16:08:33 -07:00
commit ee26ce674a
15 changed files with 86 additions and 217 deletions

View File

@ -21,6 +21,7 @@ managing QMP events.
# 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 logging
import warnings import warnings
from .error import AQMPError from .error import AQMPError
@ -41,6 +42,9 @@ Proceed with caution!
warnings.warn(_WMSG, FutureWarning) warnings.warn(_WMSG, FutureWarning)
# Suppress logging unless an application engages it.
logging.getLogger('qemu.aqmp').addHandler(logging.NullHandler())
# The order of these fields impact the Sphinx documentation order. # The order of these fields impact the Sphinx documentation order.
__all__ = ( __all__ = (

View File

@ -556,7 +556,13 @@ class EventListener:
""" """
return await self._queue.get() return await self._queue.get()
def clear(self) -> None: def empty(self) -> bool:
"""
Return `True` if there are no pending events.
"""
return self._queue.empty()
def clear(self) -> List[Message]:
""" """
Clear this listener of all pending events. Clear this listener of all pending events.
@ -564,17 +570,22 @@ class EventListener:
pending FIFO queue synchronously. It can be also be used to pending FIFO queue synchronously. It can be also be used to
manually clear any pending events, if desired. manually clear any pending events, if desired.
:return: The cleared events, if any.
.. warning:: .. warning::
Take care when discarding events. Cleared events will be Take care when discarding events. Cleared events will be
silently tossed on the floor. All events that were ever silently tossed on the floor. All events that were ever
accepted by this listener are visible in `history()`. accepted by this listener are visible in `history()`.
""" """
events = []
while True: while True:
try: try:
self._queue.get_nowait() events.append(self._queue.get_nowait())
except asyncio.QueueEmpty: except asyncio.QueueEmpty:
break break
return events
def __aiter__(self) -> AsyncIterator[Message]: def __aiter__(self) -> AsyncIterator[Message]:
return self return self

View File

@ -8,8 +8,10 @@ data to make sure it conforms to spec.
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
from collections import abc from collections import abc
import copy
from typing import ( from typing import (
Any, Any,
Dict,
Mapping, Mapping,
Optional, Optional,
Sequence, Sequence,
@ -66,6 +68,17 @@ class Greeting(Model):
self._check_member('QMP', abc.Mapping, "JSON object") self._check_member('QMP', abc.Mapping, "JSON object")
self.QMP = QMPGreeting(self._raw['QMP']) self.QMP = QMPGreeting(self._raw['QMP'])
def _asdict(self) -> Dict[str, object]:
"""
For compatibility with the iotests sync QMP wrapper.
The legacy QMP interface needs Greetings as a garden-variety Dict.
This interface is private in the hopes that it will be able to
be dropped again in the near-future. Caller beware!
"""
return dict(copy.deepcopy(self._raw))
class QMPGreeting(Model): class QMPGreeting(Model):
""" """

View File

@ -721,8 +721,11 @@ class AsyncProtocol(Generic[T]):
self.logger.debug("Task.%s: cancelled.", name) self.logger.debug("Task.%s: cancelled.", name)
return return
except BaseException as err: except BaseException as err:
self.logger.error("Task.%s: %s", self.logger.log(
name, exception_summary(err)) logging.INFO if isinstance(err, EOFError) else logging.ERROR,
"Task.%s: %s",
name, exception_summary(err)
)
self.logger.debug("Task.%s: failure:\n%s\n", self.logger.debug("Task.%s: failure:\n%s\n",
name, pretty_traceback()) name, pretty_traceback())
self._schedule_disconnect() self._schedule_disconnect()

View File

@ -9,6 +9,8 @@ accept an incoming connection from that server.
import asyncio import asyncio
import logging import logging
import socket
import struct
from typing import ( from typing import (
Dict, Dict,
List, List,
@ -224,6 +226,11 @@ class QMPClient(AsyncProtocol[Message], Events):
'asyncio.Queue[QMPClient._PendingT]' 'asyncio.Queue[QMPClient._PendingT]'
] = {} ] = {}
@property
def greeting(self) -> Optional[Greeting]:
"""The `Greeting` from the QMP server, if any."""
return self._greeting
@upper_half @upper_half
async def _establish_session(self) -> None: async def _establish_session(self) -> None:
""" """
@ -619,3 +626,23 @@ class QMPClient(AsyncProtocol[Message], Events):
""" """
msg = self.make_execute_msg(cmd, arguments, oob=oob) msg = self.make_execute_msg(cmd, arguments, oob=oob)
return await self.execute_msg(msg) return await self.execute_msg(msg)
@upper_half
@require(Runstate.RUNNING)
def send_fd_scm(self, fd: int) -> None:
"""
Send a file descriptor to the remote via SCM_RIGHTS.
"""
assert self._writer is not None
sock = self._writer.transport.get_extra_info('socket')
if sock.family != socket.AF_UNIX:
raise AQMPError("Sending file descriptors requires a UNIX socket.")
# Void the warranty sticker.
# Access to sendmsg in asyncio is scheduled for removal in Python 3.11.
sock = sock._sock # pylint: disable=protected-access
sock.sendmsg(
[b' '],
[(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))]
)

View File

@ -98,7 +98,6 @@ class QEMUMachine:
name: Optional[str] = None, name: Optional[str] = None,
base_temp_dir: str = "/var/tmp", base_temp_dir: str = "/var/tmp",
monitor_address: Optional[SocketAddrT] = None, monitor_address: Optional[SocketAddrT] = None,
socket_scm_helper: Optional[str] = None,
sock_dir: Optional[str] = None, sock_dir: Optional[str] = None,
drain_console: bool = False, drain_console: bool = False,
console_log: Optional[str] = None, console_log: Optional[str] = None,
@ -113,7 +112,6 @@ class QEMUMachine:
@param name: prefix for socket and log file names (default: qemu-PID) @param name: prefix for socket and log file names (default: qemu-PID)
@param base_temp_dir: default location where temp files are created @param base_temp_dir: default location where temp files are created
@param monitor_address: address for QMP monitor @param monitor_address: address for QMP monitor
@param socket_scm_helper: helper program, required for send_fd_scm()
@param sock_dir: where to create socket (defaults to base_temp_dir) @param sock_dir: where to create socket (defaults to base_temp_dir)
@param drain_console: (optional) True to drain console socket to buffer @param drain_console: (optional) True to drain console socket to buffer
@param console_log: (optional) path to console log file @param console_log: (optional) path to console log file
@ -134,7 +132,6 @@ class QEMUMachine:
self._base_temp_dir = base_temp_dir self._base_temp_dir = base_temp_dir
self._sock_dir = sock_dir or self._base_temp_dir self._sock_dir = sock_dir or self._base_temp_dir
self._log_dir = log_dir self._log_dir = log_dir
self._socket_scm_helper = socket_scm_helper
if monitor_address is not None: if monitor_address is not None:
self._monitor_address = monitor_address self._monitor_address = monitor_address
@ -213,48 +210,22 @@ class QEMUMachine:
def send_fd_scm(self, fd: Optional[int] = None, def send_fd_scm(self, fd: Optional[int] = None,
file_path: Optional[str] = None) -> int: file_path: Optional[str] = None) -> int:
""" """
Send an fd or file_path to socket_scm_helper. Send an fd or file_path to the remote via SCM_RIGHTS.
Exactly one of fd and file_path must be given. Exactly one of fd and file_path must be given. If it is
If it is file_path, the helper will open that file and pass its own fd. file_path, the file will be opened read-only and the new file
descriptor will be sent to the remote.
""" """
# In iotest.py, the qmp should always use unix socket.
assert self._qmp.is_scm_available()
if self._socket_scm_helper is None:
raise QEMUMachineError("No path to socket_scm_helper set")
if not os.path.exists(self._socket_scm_helper):
raise QEMUMachineError("%s does not exist" %
self._socket_scm_helper)
# This did not exist before 3.4, but since then it is
# mandatory for our purpose
if hasattr(os, 'set_inheritable'):
os.set_inheritable(self._qmp.get_sock_fd(), True)
if fd is not None:
os.set_inheritable(fd, True)
fd_param = ["%s" % self._socket_scm_helper,
"%d" % self._qmp.get_sock_fd()]
if file_path is not None: if file_path is not None:
assert fd is None assert fd is None
fd_param.append(file_path) with open(file_path, "rb") as passfile:
fd = passfile.fileno()
self._qmp.send_fd_scm(fd)
else: else:
assert fd is not None assert fd is not None
fd_param.append(str(fd)) self._qmp.send_fd_scm(fd)
proc = subprocess.run( return 0
fd_param,
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
check=False,
close_fds=False,
)
if proc.stdout:
LOG.debug(proc.stdout)
return proc.returncode
@staticmethod @staticmethod
def _remove_if_exists(path: str) -> None: def _remove_if_exists(path: str) -> None:
@ -631,7 +602,6 @@ class QEMUMachine:
events = self._qmp.get_events(wait=wait) events = self._qmp.get_events(wait=wait)
events.extend(self._events) events.extend(self._events)
del self._events[:] del self._events[:]
self._qmp.clear_events()
return events return events
@staticmethod @staticmethod

View File

@ -115,7 +115,6 @@ class QEMUQtestMachine(QEMUMachine):
wrapper: Sequence[str] = (), wrapper: Sequence[str] = (),
name: Optional[str] = None, name: Optional[str] = None,
base_temp_dir: str = "/var/tmp", base_temp_dir: str = "/var/tmp",
socket_scm_helper: Optional[str] = None,
sock_dir: Optional[str] = None, sock_dir: Optional[str] = None,
qmp_timer: Optional[float] = None): qmp_timer: Optional[float] = None):
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
@ -126,7 +125,6 @@ class QEMUQtestMachine(QEMUMachine):
sock_dir = base_temp_dir sock_dir = base_temp_dir
super().__init__(binary, args, wrapper=wrapper, name=name, super().__init__(binary, args, wrapper=wrapper, name=name,
base_temp_dir=base_temp_dir, base_temp_dir=base_temp_dir,
socket_scm_helper=socket_scm_helper,
sock_dir=sock_dir, qmp_timer=qmp_timer) sock_dir=sock_dir, qmp_timer=qmp_timer)
self._qtest: Optional[QEMUQtestProtocol] = None self._qtest: Optional[QEMUQtestProtocol] = None
self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock")

View File

@ -21,6 +21,7 @@ import errno
import json import json
import logging import logging
import socket import socket
import struct
from types import TracebackType from types import TracebackType
from typing import ( from typing import (
Any, Any,
@ -361,7 +362,7 @@ class QEMUMonitorProtocol:
def get_events(self, wait: bool = False) -> List[QMPMessage]: def get_events(self, wait: bool = False) -> List[QMPMessage]:
""" """
Get a list of available QMP events. Get a list of available QMP events and clear all pending events.
@param wait (bool): block until an event is available. @param wait (bool): block until an event is available.
@param wait (float): If wait is a float, treat it as a timeout value. @param wait (float): If wait is a float, treat it as a timeout value.
@ -374,7 +375,9 @@ class QEMUMonitorProtocol:
@return The list of available QMP events. @return The list of available QMP events.
""" """
self.__get_events(wait) self.__get_events(wait)
return self.__events events = self.__events
self.__events = []
return events
def clear_events(self) -> None: def clear_events(self) -> None:
""" """
@ -406,18 +409,14 @@ class QEMUMonitorProtocol:
raise ValueError(msg) raise ValueError(msg)
self.__sock.settimeout(timeout) self.__sock.settimeout(timeout)
def get_sock_fd(self) -> int: def send_fd_scm(self, fd: int) -> None:
""" """
Get the socket file descriptor. 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.")
@return The file descriptor number. self.__sock.sendmsg(
""" [b' '],
return self.__sock.fileno() [(socket.SOL_SOCKET, socket.SCM_RIGHTS, struct.pack('@i', fd))]
)
def is_scm_available(self) -> bool:
"""
Check if the socket allows for SCM_RIGHTS.
@return True if SCM_RIGHTS is available, otherwise False.
"""
return self.__sock.family == socket.AF_UNIX

View File

@ -381,7 +381,6 @@ class QMPShell(qmp.QEMUMonitorProtocol):
if cmdline == '': if cmdline == '':
for event in self.get_events(): for event in self.get_events():
print(event) print(event)
self.clear_events()
return True return True
return self._execute_cmd(cmdline) return self._execute_cmd(cmdline)

View File

@ -148,7 +148,6 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images
check: check:
ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy) ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy)
QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF)
check: check-block check: check-block
export PYTHON export PYTHON
check-block: $(SRC_PATH)/tests/check-block.sh qemu-img$(EXESUF) \ check-block: $(SRC_PATH)/tests/check-block.sh qemu-img$(EXESUF) \

View File

@ -67,10 +67,6 @@ if have_tools and 'CONFIG_VHOST_USER' in config_host and 'CONFIG_LINUX' in confi
dependencies: [qemuutil, vhost_user]) dependencies: [qemuutil, vhost_user])
endif endif
if have_system and 'CONFIG_POSIX' in config_host
subdir('qemu-iotests')
endif
test('decodetree', sh, test('decodetree', sh,
args: [ files('decode/check.sh'), config_host['PYTHON'], files('../scripts/decodetree.py') ], args: [ files('decode/check.sh'), config_host['PYTHON'], files('../scripts/decodetree.py') ],
workdir: meson.current_source_dir() / 'decode', workdir: meson.current_source_dir() / 'decode',

View File

@ -107,8 +107,6 @@ if os.environ.get('VALGRIND_QEMU') == "y" and \
qemu_valgrind = ['valgrind', valgrind_logfile, '--error-exitcode=99'] qemu_valgrind = ['valgrind', valgrind_logfile, '--error-exitcode=99']
socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
luks_default_secret_object = 'secret,id=keysec0,data=' + \ luks_default_secret_object = 'secret,id=keysec0,data=' + \
os.environ.get('IMGKEYSECRET', '') os.environ.get('IMGKEYSECRET', '')
luks_default_key_secret_opt = 'key-secret=keysec0' luks_default_key_secret_opt = 'key-secret=keysec0'
@ -598,7 +596,6 @@ class VM(qtest.QEMUQtestMachine):
super().__init__(qemu_prog, qemu_opts, wrapper=wrapper, super().__init__(qemu_prog, qemu_opts, wrapper=wrapper,
name=name, name=name,
base_temp_dir=test_dir, base_temp_dir=test_dir,
socket_scm_helper=socket_scm_helper,
sock_dir=sock_dir, qmp_timer=timer) sock_dir=sock_dir, qmp_timer=timer)
self._num_drives = 0 self._num_drives = 0

View File

@ -1,5 +0,0 @@
if 'CONFIG_LINUX' in config_host
socket_scm_helper = executable('socket_scm_helper', 'socket_scm_helper.c')
else
socket_scm_helper = []
endif

View File

@ -1,136 +0,0 @@
/*
* SCM_RIGHTS with unix socket help program for test
*
* Copyright IBM, Inc. 2013
*
* Authors:
* Wenchao Xia <xiawenc@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include <sys/socket.h>
#include <sys/un.h>
/* #define SOCKET_SCM_DEBUG */
/*
* @fd and @fd_to_send will not be checked for validation in this function,
* a blank will be sent as iov data to notify qemu.
*/
static int send_fd(int fd, int fd_to_send)
{
struct msghdr msg;
struct iovec iov[1];
int ret;
char control[CMSG_SPACE(sizeof(int))];
struct cmsghdr *cmsg;
memset(&msg, 0, sizeof(msg));
memset(control, 0, sizeof(control));
/* Send a blank to notify qemu */
iov[0].iov_base = (void *)" ";
iov[0].iov_len = 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int));
do {
ret = sendmsg(fd, &msg, 0);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
fprintf(stderr, "Failed to send msg, reason: %s\n", strerror(errno));
}
return ret;
}
/* Convert string to fd number. */
static int get_fd_num(const char *fd_str, bool silent)
{
int sock;
char *err;
errno = 0;
sock = strtol(fd_str, &err, 10);
if (errno) {
if (!silent) {
fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n",
strerror(errno));
}
return -1;
}
if (!*fd_str || *err || sock < 0) {
if (!silent) {
fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str);
}
return -1;
}
return sock;
}
/*
* To make things simple, the caller needs to specify:
* 1. socket fd.
* 2. path of the file to be sent.
*/
int main(int argc, char **argv, char **envp)
{
int sock, fd, ret;
#ifdef SOCKET_SCM_DEBUG
int i;
for (i = 0; i < argc; i++) {
fprintf(stderr, "Parameter %d: %s\n", i, argv[i]);
}
#endif
if (argc != 3) {
fprintf(stderr,
"Usage: %s < socket-fd > < file-path >\n",
argv[0]);
return EXIT_FAILURE;
}
sock = get_fd_num(argv[1], false);
if (sock < 0) {
return EXIT_FAILURE;
}
fd = get_fd_num(argv[2], true);
if (fd < 0) {
/* Now only open a file in readonly mode for test purpose. If more
precise control is needed, use python script in file operation, which
is supposed to fork and exec this program. */
fd = open(argv[2], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Failed to open file '%s'\n", argv[2]);
return EXIT_FAILURE;
}
}
ret = send_fd(sock, fd);
if (ret < 0) {
close(fd);
return EXIT_FAILURE;
}
close(fd);
return EXIT_SUCCESS;
}

View File

@ -68,7 +68,7 @@ class TestEnv(ContextManager['TestEnv']):
env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR', env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR',
'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG', 'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG',
'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_PROG', 'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_PROG',
'SOCKET_SCM_HELPER', 'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS', 'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS',
'QEMU_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT', 'QEMU_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT',
'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO', 'QEMU_NBD_OPTIONS', 'IMGOPTS', 'IMGFMT', 'IMGPROTO',
'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU', 'AIOMODE', 'CACHEMODE', 'VALGRIND_QEMU',
@ -140,7 +140,6 @@ class TestEnv(ContextManager['TestEnv']):
"""Init binary path variables: """Init binary path variables:
PYTHON (for bash tests) PYTHON (for bash tests)
QEMU_PROG, QEMU_IMG_PROG, QEMU_IO_PROG, QEMU_NBD_PROG, QSD_PROG QEMU_PROG, QEMU_IMG_PROG, QEMU_IO_PROG, QEMU_NBD_PROG, QSD_PROG
SOCKET_SCM_HELPER
""" """
self.python = sys.executable self.python = sys.executable
@ -174,10 +173,6 @@ class TestEnv(ContextManager['TestEnv']):
if not isxfile(b): if not isxfile(b):
sys.exit('Not executable: ' + b) sys.exit('Not executable: ' + b)
helper_path = os.path.join(self.build_iotests, 'socket_scm_helper')
if isxfile(helper_path):
self.socket_scm_helper = helper_path # SOCKET_SCM_HELPER
def __init__(self, imgfmt: str, imgproto: str, aiomode: str, def __init__(self, imgfmt: str, imgproto: str, aiomode: str,
cachemode: Optional[str] = None, cachemode: Optional[str] = None,
imgopts: Optional[str] = None, imgopts: Optional[str] = None,
@ -303,7 +298,6 @@ IMGPROTO -- {IMGPROTO}
PLATFORM -- {platform} PLATFORM -- {platform}
TEST_DIR -- {TEST_DIR} TEST_DIR -- {TEST_DIR}
SOCK_DIR -- {SOCK_DIR} SOCK_DIR -- {SOCK_DIR}
SOCKET_SCM_HELPER -- {SOCKET_SCM_HELPER}
GDB_OPTIONS -- {GDB_OPTIONS} GDB_OPTIONS -- {GDB_OPTIONS}
VALGRIND_QEMU -- {VALGRIND_QEMU} VALGRIND_QEMU -- {VALGRIND_QEMU}
PRINT_QEMU_OUTPUT -- {PRINT_QEMU} PRINT_QEMU_OUTPUT -- {PRINT_QEMU}