1
0
mirror of https://gitlab.com/libvirt/libvirt-python.git synced 2025-12-04 20:23:46 +03:00

Implement virDomainQemuMonitorCommandWithFiles() override

With libvirt-8.2.0 there's a new API:
virDomainQemuMonitorCommandWithFiles(). Since the API has both
input and output arguments we need to provide an alternative
implementation. Moreover, since FD passing works only on
UNIX-like systems we can query the returned FDs for their flags
and construct mode for python File object.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Michal Privoznik
2022-03-04 10:09:46 +01:00
parent a80412bf8c
commit 050ed3e629
3 changed files with 149 additions and 1 deletions

View File

@@ -626,6 +626,7 @@ qemu_skip_function = {
# "virDomainQemuAttach",
'virConnectDomainQemuMonitorEventRegister', # overridden in -qemu.py
'virConnectDomainQemuMonitorEventDeregister', # overridden in -qemu.py
'virDomainQemuMonitorCommandWithFiles', # overridden in -qemu.py
}
# Generate C code, but skip python impl

View File

@@ -20,6 +20,9 @@
#include "typewrappers.h"
#include "libvirt-utils.h"
#include "build/libvirt-qemu.h"
#ifndef __CYGWIN__
# include <fcntl.h>
#endif
#ifndef __CYGWIN__
extern PyObject *PyInit_libvirtmod_qemu(void);
@@ -325,6 +328,118 @@ libvirt_qemu_virConnectDomainQemuMonitorEventDeregister(PyObject *self ATTRIBUTE
}
#endif /* LIBVIR_CHECK_VERSION(1, 2, 3) */
#if LIBVIR_CHECK_VERSION(8, 2, 0)
static PyObject *
libvirt_qemu_virDomainQemuMonitorCommandWithFiles(PyObject *self ATTRIBUTE_UNUSED,
PyObject *args)
{
PyObject *pyobj_domain;
const char *cmd;
PyObject *pyobj_files;
unsigned int flags;
virDomainPtr domain;
unsigned int ninfiles;
int *infiles = NULL;
unsigned int noutfiles = 0;
int *outfiles = NULL;
char *result = NULL;
ssize_t i;
PyObject *py_outfiles = NULL;
PyObject *py_retval = NULL;
int c_retval;
if (!PyArg_ParseTuple(args,
(char *) "Os|OI:virDomainQemuMonitorCommandWithFiles",
&pyobj_domain, &cmd, &pyobj_files, &flags))
return NULL;
domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain);
ninfiles = PyList_Size(pyobj_files);
if (VIR_ALLOC_N(infiles, ninfiles) < 0)
return PyErr_NoMemory();
for (i = 0; i < ninfiles; i++) {
PyObject *pyfd;
int fd;
pyfd = PyList_GetItem(pyobj_files, i);
if (libvirt_intUnwrap(pyfd, &fd) < 0)
goto cleanup;
infiles[i] = fd;
}
LIBVIRT_BEGIN_ALLOW_THREADS;
c_retval = virDomainQemuMonitorCommandWithFiles(domain, cmd, ninfiles, infiles,
&noutfiles, &outfiles, &result, flags);
LIBVIRT_END_ALLOW_THREADS;
if (c_retval < 0) {
py_retval = VIR_PY_NONE;
goto cleanup;
}
if (!(py_outfiles = PyList_New(0)) ||
!(py_retval = PyTuple_New(2))) {
goto error;
}
for (i = 0; i < noutfiles; i++) {
int fd = outfiles[i];
const char *mode = "r+b";
/* Since FD passing works only on UNIX-like systems, we can do this. */
#ifndef __CYGWIN__
int fflags;
if ((fflags = fcntl(fd, F_GETFL)) < 0)
goto error;
switch (fflags & (O_ACCMODE | O_APPEND)) {
case O_RDONLY:
mode = "rb";
break;
case O_WRONLY:
mode = "wb";
break;
case O_RDWR:
mode = "r+b";
break;
case O_WRONLY | O_APPEND:
mode = "ab";
break;
case O_RDWR | O_APPEND:
mode = "a+b";
break;
}
#endif
VIR_PY_LIST_APPEND_GOTO(py_outfiles, PyFile_FromFd(fd, NULL, mode, 0, NULL, NULL, NULL, 1), error);
}
VIR_PY_TUPLE_SET_GOTO(py_retval, 0, libvirt_charPtrWrap(result), error);
VIR_PY_TUPLE_SET_GOTO(py_retval, 1, py_outfiles, error);
/* stolen by py_retval */
py_outfiles = NULL;
cleanup:
Py_XDECREF(py_outfiles);
VIR_FREE(result);
VIR_FREE(outfiles);
VIR_FREE(infiles);
return py_retval;
error:
while (noutfiles > 0) {
VIR_FORCE_CLOSE(outfiles[--noutfiles]);
}
Py_CLEAR(py_retval);
goto cleanup;
}
#endif /* LIBVIR_CHECK_VERSION(8, 2, 0) */
/************************************************************************
* *
* The registration stuff *
@@ -340,6 +455,9 @@ static PyMethodDef libvirtQemuMethods[] = {
{(char *) "virConnectDomainQemuMonitorEventRegister", libvirt_qemu_virConnectDomainQemuMonitorEventRegister, METH_VARARGS, NULL},
{(char *) "virConnectDomainQemuMonitorEventDeregister", libvirt_qemu_virConnectDomainQemuMonitorEventDeregister, METH_VARARGS, NULL},
#endif /* LIBVIR_CHECK_VERSION(1, 2, 3) */
#if LIBVIR_CHECK_VERSION(8, 2, 0)
{(char *) "virDomainQemuMonitorCommandWithFiles", libvirt_qemu_virDomainQemuMonitorCommandWithFiles, METH_VARARGS, NULL},
#endif /* LIBVIR_CHECK_VERSION(8, 2, 0) */
{NULL, NULL, 0, NULL}
};

View File

@@ -1,5 +1,5 @@
# Manually written part of python bindings for libvirt-qemu
from typing import Any, Callable, Dict
from typing import Any, Callable, Dict, List, IO
def _dispatchQemuMonitorEventCallback(conn: libvirt.virConnect, dom: libvirt.virDomain, event: str, seconds: int, micros: int, details: str, cbData: Dict[str, Any]) -> int:
@@ -38,3 +38,32 @@ def qemuMonitorEventRegister(conn: libvirt.virConnect, dom: libvirt.virDomain, e
raise libvirt.libvirtError('virConnectDomainQemuMonitorEventRegister() failed')
conn.qemuMonitorEventCallbackID[ret] = opaque # type: ignore
return ret
def qemuMonitorCommandWithFiles(domain: libvirt.virDomain, cmd: str, files: List[int] = [], flags: int = 0) -> (str, List[IO]):
"""This API is QEMU specific, so it will only work with hypervisor
connections to the QEMU driver with local connections using the unix
socket.
Send an arbitrary monitor command @cmd with file descriptors @files to
domain through the QEMU monitor and optionally return a list of files
in the returned tuple. There are several requirements to safely
and successfully use this API:
- A @cmd that queries state without making any modifications is safe
- A @cmd that alters state that is also tracked by libvirt is unsafe,
and may cause libvirtd to crash
- A @cmd that alters state not tracked by the current version of
libvirt is possible as a means to test new qemu features before
they have support in libvirt, but no guarantees are made to safety
If VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP is set, the command is considered to
be a human monitor command and libvirt will automatically convert it into
QMP if needed. In that case the @result will also be converted back from
QMP.
Returns a tuple consisting of the string output from @cmd and a list of
files respectively."""
ret = libvirtmod_qemu.virDomainQemuMonitorCommandWithFiles(domain._o, cmd, files, flags)
if ret is None:
raise libvirt.libvirtError('virDomainQemuMonitorCommandWithFiles() failed')
return ret