[libvirt PATCH v2 0/6] APIs for reporting tainting and deprecation messages

This is a follow up to https://listman.redhat.com/archives/libvir-list/2021-January/msg00988.html I pushed the first non-API parts of that series already. This posting takes a different approach to the APIs. Instead of separte APIs for tainting and deprecations, there is now one API for reporting general informational messages. This is explicitly only targetted at humans. v2: - Change formatting for better translation - Fix leak of strings Daniel P. Berrangé (6): conf: record deprecation messages against the domain qemu: record deprecation messages against the domain src: define virDomainGetMessages API remote: add RPC support for the virDomainGetMessages API qemu: implement virDomainGetMessages API tools: report messages for 'dominfo' command include/libvirt/libvirt-domain.h | 9 +++++ src/conf/domain_conf.c | 45 ++++++++++++++++++++-- src/conf/domain_conf.h | 5 +++ src/driver-hypervisor.h | 6 +++ src/libvirt-domain.c | 54 +++++++++++++++++++++++++++ src/libvirt_private.syms | 3 ++ src/libvirt_public.syms | 5 +++ src/qemu/qemu_domain.c | 5 +++ src/qemu/qemu_domain.h | 3 ++ src/qemu/qemu_driver.c | 58 +++++++++++++++++++++++++++++ src/qemu/qemu_process.c | 5 +++ src/remote/remote_daemon_dispatch.c | 45 ++++++++++++++++++++++ src/remote/remote_driver.c | 44 ++++++++++++++++++++++ src/remote/remote_protocol.x | 21 ++++++++++- src/remote_protocol-structs | 11 ++++++ tools/virsh-domain-monitor.c | 10 +++++ 16 files changed, 325 insertions(+), 4 deletions(-) -- 2.29.2

These messages will be stored in the live status XML. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 28 +++++++++++++++++++++++++--- src/conf/domain_conf.h | 4 ++++ src/libvirt_private.syms | 1 + 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 07e6f39256..a873c0ada2 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1788,6 +1788,15 @@ bool virDomainObjTaint(virDomainObjPtr obj, } +void virDomainObjDeprecation(virDomainObjPtr obj, + const char *msg) +{ + obj->deprecations = g_renew(char *, obj->deprecations, + obj->ndeprecations + 1); + obj->deprecations[obj->ndeprecations++] = g_strdup(msg); +} + + static void virDomainGraphicsAuthDefClear(virDomainGraphicsAuthDefPtr def) { @@ -21225,7 +21234,8 @@ virDomainObjParseXML(xmlDocPtr xml, int reason = 0; void *parseOpaque = NULL; g_autofree char *tmp = NULL; - g_autofree xmlNodePtr *nodes = NULL; + g_autofree xmlNodePtr *taintNodes = NULL; + g_autofree xmlNodePtr *depNodes = NULL; if (!(obj = virDomainObjNew(xmlopt))) return NULL; @@ -21272,10 +21282,10 @@ virDomainObjParseXML(xmlDocPtr xml, } obj->pid = (pid_t)val; - if ((n = virXPathNodeSet("./taint", ctxt, &nodes)) < 0) + if ((n = virXPathNodeSet("./taint", ctxt, &taintNodes)) < 0) goto error; for (i = 0; i < n; i++) { - char *str = virXMLPropString(nodes[i], "flag"); + char *str = virXMLPropString(taintNodes[i], "flag"); if (str) { int flag = virDomainTaintTypeFromString(str); if (flag < 0) { @@ -21289,6 +21299,13 @@ virDomainObjParseXML(xmlDocPtr xml, } } + if ((n = virXPathNodeSet("./deprecation", ctxt, &depNodes)) < 0) + goto error; + for (i = 0; i < n; i++) { + g_autofree char *str = virXMLNodeContentString(depNodes[i]); + virDomainObjDeprecation(obj, str); + } + if (xmlopt->privateData.parse && xmlopt->privateData.parse(ctxt, obj, &xmlopt->config) < 0) goto error; @@ -29122,6 +29139,11 @@ virDomainObjFormat(virDomainObjPtr obj, virDomainTaintTypeToString(i)); } + for (i = 0; i < obj->ndeprecations; i++) { + virBufferEscapeString(&buf, "<deprecation>%s</deprecation>\n", + obj->deprecations[i]); + } + if (xmlopt->privateData.format && xmlopt->privateData.format(&buf, obj) < 0) return NULL; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8b1c8643be..ea6370c03d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2801,6 +2801,8 @@ struct _virDomainObj { void (*privateDataFreeFunc)(void *); int taint; + size_t ndeprecations; + char **deprecations; unsigned long long original_memlock; /* Original RLIMIT_MEMLOCK, zero if no * restore will be required later */ @@ -3058,6 +3060,8 @@ void virDomainObjEndAPI(virDomainObjPtr *vm); bool virDomainObjTaint(virDomainObjPtr obj, virDomainTaintFlags taint); +void virDomainObjDeprecation(virDomainObjPtr obj, + const char *msg); void virDomainObjBroadcast(virDomainObjPtr vm); int virDomainObjWait(virDomainObjPtr vm); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0636b0d8c9..512da526fc 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -547,6 +547,7 @@ virDomainObjAssignDef; virDomainObjBroadcast; virDomainObjCheckActive; virDomainObjCopyPersistentDef; +virDomainObjDeprecation; virDomainObjEndAPI; virDomainObjFormat; virDomainObjGetDefs; -- 2.29.2

These messages are only valid while the domain is running. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/qemu/qemu_domain.c | 5 +++++ src/qemu/qemu_process.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0f09e321fb..d362764060 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -6236,6 +6236,11 @@ void qemuDomainObjTaintMsg(virQEMUDriverPtr driver, va_end(args); } + if (taint == VIR_DOMAIN_TAINT_DEPRECATED_CONFIG && + extramsg) { + virDomainObjDeprecation(obj, extramsg); + } + VIR_WARN("Domain id=%d name='%s' uuid=%s is tainted: %s%s%s%s", obj->def->id, obj->def->name, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 30cfa4d485..91f74b95bb 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7857,6 +7857,11 @@ void qemuProcessStop(virQEMUDriverPtr driver, } } + for (i = 0; i < vm->ndeprecations; i++) + g_free(vm->deprecations[i]); + g_free(vm->deprecations); + vm->ndeprecations = 0; + vm->deprecations = NULL; vm->taint = 0; vm->pid = -1; virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason); -- 2.29.2

This API allows fetching a list of informational messages recorded against the domain. This provides a way to give information about tainting of the guest due to undesirable actions/configs, as well as provide details of deprecated features. The output of this API is explicitly targetted at humans, not machines, so it is inappropriate to attempt to pattern match on the strings and take action off them, not least because the messages are marked for translation. Should there be a demand for machine targetted information, this would have to be addressed via a new API, and is not planned at this point in time. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- include/libvirt/libvirt-domain.h | 9 ++++++ src/driver-hypervisor.h | 6 ++++ src/libvirt-domain.c | 54 ++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ 4 files changed, 74 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index de2456812c..8011cf9fe1 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5119,4 +5119,13 @@ int virDomainAuthorizedSSHKeysSet(virDomainPtr domain, unsigned int nkeys, unsigned int flags); +typedef enum { + VIR_DOMAIN_MESSAGE_DEPRECATION = (1 << 0), + VIR_DOMAIN_MESSAGE_TAINTING = (1 << 1), +} virDomainMessageType; + +int virDomainGetMessages(virDomainPtr domain, + char ***msgs, + unsigned int flags); + #endif /* LIBVIRT_DOMAIN_H */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 9e8fe89921..05d7dfb5c7 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1400,6 +1400,11 @@ typedef int unsigned int nkeys, unsigned int flags); +typedef int +(*virDrvDomainGetMessages)(virDomainPtr domain, + char ***msgs, + unsigned int flags); + typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1665,4 +1670,5 @@ struct _virHypervisorDriver { virDrvDomainBackupGetXMLDesc domainBackupGetXMLDesc; virDrvDomainAuthorizedSSHKeysGet domainAuthorizedSSHKeysGet; virDrvDomainAuthorizedSSHKeysSet domainAuthorizedSSHKeysSet; + virDrvDomainGetMessages domainGetMessages; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index dba89a7d3a..ae318f4a1a 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -13102,3 +13102,57 @@ virDomainAuthorizedSSHKeysSet(virDomainPtr domain, virDispatchError(conn); return -1; } + + +/** + * virDomainGetMessages: + * @domain: a domain object + * @msgs: pointer to a variable to store messages + * @flags: zero or more virDomainMessageType flags + * + * Fetch a list of all messages recorded against the VM and + * store them into @msgs array which is allocated upon + * successful return and is NULL terminated. The caller is + * responsible for freeing @msgs when no longer needed. + * + * If @flags is zero then all messages are reported. The + * virDomainMessageType constants can be used to restrict + * results to certain types of message. + * + * Note it is hypervisor dependant whether messages are + * available for shutoff guests, or running guests, or + * both. Thus a client should be prepared to re-fetch + * messages when a guest transitions between running + * and shutoff states. + * + * Returns: number of messages stored in @msgs, + * -1 otherwise. + */ +int +virDomainGetMessages(virDomainPtr domain, + char ***msgs, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "msgs=%p, flags=0x%x", msgs, flags); + + virResetLastError(); + + virCheckDomainReturn(domain, -1); + conn = domain->conn; + virCheckNonNullArgGoto(msgs, error); + + if (conn->driver->domainGetMessages) { + int ret; + ret = conn->driver->domainGetMessages(domain, msgs, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index cf31f937d5..d851333eb0 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -879,4 +879,9 @@ LIBVIRT_6.10.0 { virDomainAuthorizedSSHKeysSet; } LIBVIRT_6.0.0; +LIBVIRT_7.1.0 { + global: + virDomainGetMessages; +} LIBVIRT_6.10.0; + # .... define new API here using predicted next version number .... -- 2.29.2

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/remote/remote_daemon_dispatch.c | 45 +++++++++++++++++++++++++++++ src/remote/remote_driver.c | 44 ++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 21 +++++++++++++- src/remote_protocol-structs | 11 +++++++ 4 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index b7ef1f4b26..e9f2a0ce5b 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -7463,3 +7463,48 @@ remoteDispatchDomainAuthorizedSshKeysSet(virNetServerPtr server G_GNUC_UNUSED, return rv; } + +static int +remoteDispatchDomainGetMessages(virNetServerPtr server G_GNUC_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg G_GNUC_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_get_messages_args *args, + remote_domain_get_messages_ret *ret) +{ + int rv = -1; + virConnectPtr conn = remoteGetHypervisorConn(client); + int nmsgs = 0; + char **msgs = NULL; + virDomainPtr dom = NULL; + + if (!conn) + goto cleanup; + + if (!(dom = get_nonnull_domain(conn, args->dom))) + goto cleanup; + + if ((nmsgs = virDomainGetMessages(dom, &msgs, args->flags)) < 0) + goto cleanup; + + if (nmsgs > REMOTE_DOMAIN_MESSAGES_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Number of msgs %d, which exceeds max limit: %d"), + nmsgs, REMOTE_DOMAIN_MESSAGES_MAX); + goto cleanup; + } + + ret->msgs.msgs_val = g_steal_pointer(&msgs); + ret->msgs.msgs_len = nmsgs; + + rv = nmsgs; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (nmsgs > 0) + virStringListFreeCount(msgs, nmsgs); + virObjectUnref(dom); + + return rv; +} diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8d6790ccf2..a83cd866e7 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8098,6 +8098,49 @@ remoteDomainAuthorizedSSHKeysSet(virDomainPtr domain, } +static int +remoteDomainGetMessages(virDomainPtr domain, + char ***msgs, + unsigned int flags) +{ + int rv = -1; + size_t i; + struct private_data *priv = domain->conn->privateData; + remote_domain_get_messages_args args; + remote_domain_get_messages_ret ret; + + remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, domain); + args.flags = flags; + memset(&ret, 0, sizeof(ret)); + + if (call(domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_MESSAGES, + (xdrproc_t) xdr_remote_domain_get_messages_args, (char *)&args, + (xdrproc_t) xdr_remote_domain_get_messages_ret, (char *)&ret) == -1) { + goto cleanup; + } + + if (ret.msgs.msgs_len > REMOTE_DOMAIN_MESSAGES_MAX) { + virReportError(VIR_ERR_RPC, "%s", + _("remoteDomainGetMessages: " + "returned number of msgs exceeds limit")); + goto cleanup; + } + + *msgs = g_new0(char *, ret.msgs.msgs_len + 1); + for (i = 0; i < ret.msgs.msgs_len; i++) + (*msgs)[i] = g_strdup(ret.msgs.msgs_val[i]); + + rv = ret.msgs.msgs_len; + + cleanup: + remoteDriverUnlock(priv); + xdr_free((xdrproc_t)xdr_remote_domain_get_messages_ret, + (char *) &ret); + return rv; +} + /* get_nonnull_domain and get_nonnull_network turn an on-wire * (name, uuid) pair into virDomainPtr or virNetworkPtr object. * These can return NULL if underlying memory allocations fail, @@ -8531,6 +8574,7 @@ static virHypervisorDriver hypervisor_driver = { .domainBackupGetXMLDesc = remoteDomainBackupGetXMLDesc, /* 6.0.0 */ .domainAuthorizedSSHKeysGet = remoteDomainAuthorizedSSHKeysGet, /* 6.10.0 */ .domainAuthorizedSSHKeysSet = remoteDomainAuthorizedSSHKeysSet, /* 6.10.0 */ + .domainGetMessages = remoteDomainGetMessages, /* 7.1.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 2df38cef77..d3724bc305 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -283,6 +283,9 @@ const REMOTE_NETWORK_PORT_PARAMETERS_MAX = 16; /* Upper limit on number of SSH keys */ const REMOTE_DOMAIN_AUTHORIZED_SSH_KEYS_MAX = 2048; +/* Upper limit on number of messages */ +const REMOTE_DOMAIN_MESSAGES_MAX = 2048; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -3799,6 +3802,16 @@ struct remote_domain_authorized_ssh_keys_set_args { unsigned int flags; }; +struct remote_domain_get_messages_args { + remote_nonnull_domain dom; + unsigned int flags; +}; + +struct remote_domain_get_messages_ret { + remote_nonnull_string msgs<REMOTE_DOMAIN_MESSAGES_MAX>; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -6714,5 +6727,11 @@ enum remote_procedure { * @generate: none * @acl: domain:write */ - REMOTE_PROC_DOMAIN_AUTHORIZED_SSH_KEYS_SET = 425 + REMOTE_PROC_DOMAIN_AUTHORIZED_SSH_KEYS_SET = 425, + + /** + * @generate: none + * @acl: domain:read + */ + REMOTE_PROC_DOMAIN_GET_MESSAGES = 426 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 9bcd14603d..c0c034ac6a 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -3162,6 +3162,16 @@ struct remote_domain_authorized_ssh_keys_set_args { } keys; u_int flags; }; +struct remote_domain_get_messages_args { + remote_nonnull_domain dom; + u_int flags; +}; +struct remote_domain_get_messages_ret { + struct { + u_int msgs_len; + remote_nonnull_string * msgs_val; + } msgs; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3588,4 +3598,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE = 423, REMOTE_PROC_DOMAIN_AUTHORIZED_SSH_KEYS_GET = 424, REMOTE_PROC_DOMAIN_AUTHORIZED_SSH_KEYS_SET = 425, + REMOTE_PROC_DOMAIN_GET_MESSAGES = 426, }; -- 2.29.2

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 17 ++++++++++++ src/conf/domain_conf.h | 1 + src/libvirt_private.syms | 2 ++ src/qemu/qemu_domain.h | 3 +++ src/qemu/qemu_driver.c | 58 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a873c0ada2..f78fc992c1 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -90,6 +90,23 @@ VIR_ENUM_IMPL(virDomainTaint, "deprecated-config", ); +VIR_ENUM_IMPL(virDomainTaintMessage, + VIR_DOMAIN_TAINT_LAST, + N_("custom configuration parameters specified"), + N_("custom monitor control commands issued"), + N_("running with undesirable elevated privileges"), + N_("network configuration using opaque shell scripts"), + N_("potentially unsafe disk format probing"), + N_("managing externally launched configuration"), + N_("potentially unsafe use of host CPU passthrough"), + N_("configuration potentially modified by hook script"), + N_("use of host cdrom passthrough"), + N_("custom device tree blob used"), + N_("custom guest agent control commands issued"), + N_("hypervisor feature autodetection override"), + N_("use of deprecated configuration settings"), +); + VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST, "none", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ea6370c03d..1ef4266d13 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3631,6 +3631,7 @@ bool virDomainVsockDefEquals(const virDomainVsockDef *a, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; VIR_ENUM_DECL(virDomainTaint); +VIR_ENUM_DECL(virDomainTaintMessage); VIR_ENUM_DECL(virDomainVirt); VIR_ENUM_DECL(virDomainBoot); VIR_ENUM_DECL(virDomainFeature); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 512da526fc..730289a1f8 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -628,6 +628,8 @@ virDomainStateTypeToString; virDomainStorageNetworkParseHost; virDomainStorageSourceParse; virDomainStorageSourceParseBase; +virDomainTaintMessageTypeFromString; +virDomainTaintMessageTypeToString; virDomainTaintTypeFromString; virDomainTaintTypeToString; virDomainTimerModeTypeFromString; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 7453881a31..42b6fda91a 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -572,6 +572,9 @@ void qemuDomainObjTaintMsg(virQEMUDriverPtr driver, const char *msg, ...) G_GNUC_PRINTF(5, 6); +char **qemuDomainObjGetTainting(virQEMUDriverPtr driver, + virDomainObjPtr obj); + void qemuDomainObjCheckTaint(virQEMUDriverPtr driver, virDomainObjPtr obj, qemuDomainLogContextPtr logCtxt, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c34af6b7d1..5790fa5356 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20367,6 +20367,63 @@ qemuDomainAuthorizedSSHKeysSet(virDomainPtr dom, } +static int +qemuDomainGetMessages(virDomainPtr dom, + char ***msgs, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + int rv = -1; + size_t i, n; + int nmsgs; + + virCheckFlags(VIR_DOMAIN_MESSAGE_DEPRECATION | + VIR_DOMAIN_MESSAGE_TAINTING, -1); + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + if (virDomainGetMessagesEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + *msgs = NULL; + nmsgs = 0; + n = 0; + + if (!flags || (flags & VIR_DOMAIN_MESSAGE_TAINTING)) { + nmsgs += __builtin_popcount(vm->taint); + *msgs = g_renew(char *, *msgs, nmsgs+1); + + for (i = 0; i < VIR_DOMAIN_TAINT_LAST; i++) { + if (vm->taint & (1 << i)) { + (*msgs)[n++] = g_strdup_printf( + _("tainted: %s"), + _(virDomainTaintMessageTypeToString(i))); + } + } + } + + if (!flags || (flags & VIR_DOMAIN_MESSAGE_DEPRECATION)) { + nmsgs += vm->ndeprecations; + *msgs = g_renew(char *, *msgs, nmsgs+1); + + for (i = 0; i < vm->ndeprecations; i++) { + (*msgs)[n++] = g_strdup_printf( + _("deprecated configuration: %s"), + vm->deprecations[i]); + } + } + + (*msgs)[nmsgs] = NULL; + + rv = nmsgs; + + cleanup: + virDomainObjEndAPI(&vm); + return rv; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectURIProbe = qemuConnectURIProbe, @@ -20608,6 +20665,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainBackupGetXMLDesc = qemuDomainBackupGetXMLDesc, /* 6.0.0 */ .domainAuthorizedSSHKeysGet = qemuDomainAuthorizedSSHKeysGet, /* 6.10.0 */ .domainAuthorizedSSHKeysSet = qemuDomainAuthorizedSSHKeysSet, /* 6.10.0 */ + .domainGetMessages = qemuDomainGetMessages, /* 7.1.0 */ }; -- 2.29.2

On 2/9/21 11:01 AM, Daniel P. Berrangé wrote:
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- src/conf/domain_conf.c | 17 ++++++++++++ src/conf/domain_conf.h | 1 + src/libvirt_private.syms | 2 ++ src/qemu/qemu_domain.h | 3 +++ src/qemu/qemu_driver.c | 58 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+)
[...]
+static int +qemuDomainGetMessages(virDomainPtr dom, + char ***msgs, + unsigned int flags) +{ + virDomainObjPtr vm = NULL; + int rv = -1; + size_t i, n; + int nmsgs; + + virCheckFlags(VIR_DOMAIN_MESSAGE_DEPRECATION | + VIR_DOMAIN_MESSAGE_TAINTING, -1); + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + if (virDomainGetMessagesEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + *msgs = NULL; + nmsgs = 0; + n = 0; + + if (!flags || (flags & VIR_DOMAIN_MESSAGE_TAINTING)) { + nmsgs += __builtin_popcount(vm->taint); + *msgs = g_renew(char *, *msgs, nmsgs+1); + + for (i = 0; i < VIR_DOMAIN_TAINT_LAST; i++) { + if (vm->taint & (1 << i)) { + (*msgs)[n++] = g_strdup_printf( + _("tainted: %s"), + _(virDomainTaintMessageTypeToString(i))); + } + } + } + + if (!flags || (flags & VIR_DOMAIN_MESSAGE_DEPRECATION)) { + nmsgs += vm->ndeprecations; + *msgs = g_renew(char *, *msgs, nmsgs+1); + + for (i = 0; i < vm->ndeprecations; i++) { + (*msgs)[n++] = g_strdup_printf( + _("deprecated configuration: %s"), + vm->deprecations[i]); + } + } + + (*msgs)[nmsgs] = NULL;
FYI: Coverity got grumpy right here as it believes *msgs could be NULL because of the !flags || condition and not realizing that flags could only be one of the two or 0. Wasn't sure whether being safe and adding a if (*msgs) is desired, but figured I'd at least note it. John
+ + rv = nmsgs; + + cleanup: + virDomainObjEndAPI(&vm); + return rv; +} +
[...]

$ virsh dominfo demo Id: 2 Name: demo UUID: eadf8ef0-bf14-4c5f-9708-4a19bacf9e81 OS Type: hvm State: running CPU(s): 2 CPU time: 15.8s Max memory: 1536000 KiB Used memory: 1536000 KiB Persistent: yes Autostart: disable Managed save: no Security model: selinux Security DOI: 0 Security label: unconfined_u:unconfined_r:svirt_t:s0:c443,c956 (permissive) Messages: tainted: custom monitor control commands issued tainted: use of deprecated configuration settings deprecated configuration: machine type 'pc-1.2' Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> --- tools/virsh-domain-monitor.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c index 02ff1fbd62..897339b6f9 100644 --- a/tools/virsh-domain-monitor.c +++ b/tools/virsh-domain-monitor.c @@ -1291,6 +1291,7 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd) char *str, uuid[VIR_UUID_STRING_BUFLEN]; int has_managed_save = 0; virshControlPtr priv = ctl->privData; + g_auto(GStrv) messages = NULL; if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) return false; @@ -1391,6 +1392,15 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd) VIR_FREE(seclabel); } } + + if (virDomainGetMessages(dom, &messages, 0) > 0) { + size_t i; + for (i = 0; messages[i] != NULL; i++) { + vshPrint(ctl, "%-15s %s\n", + i == 0 ? _("Messages:") : "", messages[i]); + } + } + virshDomainFree(dom); return ret; } -- 2.29.2

On 2/9/21 5:01 PM, Daniel P. Berrangé wrote:
This is a follow up to
https://listman.redhat.com/archives/libvir-list/2021-January/msg00988.html
I pushed the first non-API parts of that series already.
This posting takes a different approach to the APIs. Instead of separte APIs for tainting and deprecations, there is now one API for reporting general informational messages. This is explicitly only targetted at humans.
v2:
- Change formatting for better translation - Fix leak of strings
Daniel P. Berrangé (6): conf: record deprecation messages against the domain qemu: record deprecation messages against the domain src: define virDomainGetMessages API remote: add RPC support for the virDomainGetMessages API qemu: implement virDomainGetMessages API tools: report messages for 'dominfo' command
include/libvirt/libvirt-domain.h | 9 +++++ src/conf/domain_conf.c | 45 ++++++++++++++++++++-- src/conf/domain_conf.h | 5 +++ src/driver-hypervisor.h | 6 +++ src/libvirt-domain.c | 54 +++++++++++++++++++++++++++ src/libvirt_private.syms | 3 ++ src/libvirt_public.syms | 5 +++ src/qemu/qemu_domain.c | 5 +++ src/qemu/qemu_domain.h | 3 ++ src/qemu/qemu_driver.c | 58 +++++++++++++++++++++++++++++ src/qemu/qemu_process.c | 5 +++ src/remote/remote_daemon_dispatch.c | 45 ++++++++++++++++++++++ src/remote/remote_driver.c | 44 ++++++++++++++++++++++ src/remote/remote_protocol.x | 21 ++++++++++- src/remote_protocol-structs | 11 ++++++ tools/virsh-domain-monitor.c | 10 +++++ 16 files changed, 325 insertions(+), 4 deletions(-)
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal
participants (3)
-
Daniel P. Berrangé
-
John Ferlan
-
Michal Privoznik