The new virDomainReloadTlsCertificates API is used to notify domain reload
its certificates without restart, and avoid service interruption.
Currently, only QEMU VNC TLS certificates are supported, but parameters and
flags are also reserved for subsequent scenarios.
Take reload QEMU VNC TLS certificates as an example, we can call:
virDomainReloadTlsCertificates(domain,
VIR_DOMAIN_TLS_CERT_GRAPHICS_VNC,
NULL, 0, 0);
Then the specified QMP message would be send to QEMU:
{"execute": "display-reload", "arguments":{"type": "vnc", "tls-certs": true}}
Refers:
https://gitlab.com/qemu-project/qemu/-/commit/9cc07651655ee86eca41059f5ead8c4e5607c734
The qmp is introduced in QEMU 6.0. A qemu compatibility flag should be added to check if target qemu supports
---
include/libvirt/libvirt-domain.h | 20 +++++++++++
src/driver-hypervisor.h | 8 +++++
src/libvirt-domain.c | 58 ++++++++++++++++++++++++++++++++
src/libvirt_public.syms | 5 +++
src/qemu/qemu_driver.c | 40 ++++++++++++++++++++++
src/qemu/qemu_hotplug.c | 17 ++++++++++
src/qemu/qemu_hotplug.h | 4 +++
src/qemu/qemu_monitor.c | 27 +++++++++++++++
src/qemu/qemu_monitor.h | 3 ++
src/qemu/qemu_monitor_json.c | 27 +++++++++++++++
src/qemu/qemu_monitor_json.h | 4 +++
src/remote/remote_driver.c | 1 +
src/remote/remote_protocol.x | 15 ++++++++-
src/remote_protocol-structs | 10 ++++++
14 files changed, 238 insertions(+), 1 deletion(-)
Please add a virsh sub-command for this new API.
It's better to split the big patch info pieces, for example:
- A patch for qemu compatibilities
- A patch for qemu driver internal
- A patch for remote driver
- A patch for libvirt pub API
- A patch for virsh
At last, a patch for news after these above are merged.
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index e99bfb7654..357d3598a6 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -5152,4 +5152,24 @@ int virDomainStartDirtyRateCalc(virDomainPtr domain,
int seconds,
unsigned int flags);
+/**
+ * virDomainTlsCertificateType:
+ * the used scene of TLS certificates for doamin
+ */
+typedef enum {
+ VIR_DOMAIN_TLS_CERT_GRAPHICS_VNC = 0,
+ VIR_DOMAIN_TLS_CERT_GRAPHICS_SPICE = 1,
+
+# ifdef VIR_ENUM_SENTINELS
+ VIR_DOMAIN_TLS_CERT_LAST
+# endif
+} virDomainTlsCertificateType;
+
+int
+virDomainReloadTlsCertificates(virDomainPtr domain,
+ unsigned int type,
+ virTypedParameterPtr params,
+ int nparams,
+ unsigned int flags);
+
#endif /* LIBVIRT_DOMAIN_H */
diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h
index d642af8a37..d0d4976441 100644
--- a/src/driver-hypervisor.h
+++ b/src/driver-hypervisor.h
@@ -1410,6 +1410,13 @@ typedef int
int seconds,
unsigned int flags);
+typedef int
+(*virDrvDomainReloadTlsCertificates)(virDomainPtr domain,
+ unsigned int type,
+ virTypedParameterPtr params,
+ int nparams,
+ unsigned int flags);
+
typedef struct _virHypervisorDriver virHypervisorDriver;
/**
@@ -1676,4 +1683,5 @@ struct _virHypervisorDriver {
virDrvDomainAuthorizedSSHKeysSet domainAuthorizedSSHKeysSet;
virDrvDomainGetMessages domainGetMessages;
virDrvDomainStartDirtyRateCalc domainStartDirtyRateCalc;
+ virDrvDomainReloadTlsCertificates domainReloadTlsCertificates;
};
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c
index 42c75f6cc5..1b6889ab49 100644
--- a/src/libvirt-domain.c
+++ b/src/libvirt-domain.c
@@ -13218,3 +13218,61 @@ virDomainStartDirtyRateCalc(virDomainPtr domain,
virDispatchError(conn);
return -1;
}
+
+/**
+ * virDomainReloadTlsCertificates:
+ * @domain: a domain object.
+ * @type: a value of virDomainTlsCertificateType
+ * @params: pointer to TLS Certs parameter objects, must be NULL if not used
+ * @nparams: number of TLS Certs parameter objects, must be 0 if not used
+ * @flags: extra flags; not used yet, so callers should always pass 0
+ *
+ * Notify domain reload its certificates with specified 'type'
+ *
+ * Returns 0 in case of success, -1 otherwise.
+ */
+int
+virDomainReloadTlsCertificates(virDomainPtr domain,
+ unsigned int type,
+ virTypedParameterPtr params,
+ int nparams,
+ unsigned int flags)
+{
+ virConnectPtr conn;
+
+ VIR_DOMAIN_DEBUG(domain, "certificate type=%u, params=%p, nparams=%d, flags=%x",
+ type, params, nparams, flags);
+
+ virResetLastError();
+
+ virCheckDomainReturn(domain, -1);
+ conn = domain->conn;
+
+ virCheckReadOnlyGoto(conn->flags, error);
+ virCheckNonNegativeArgGoto(nparams, error);
+
+ if (type >= VIR_DOMAIN_TLS_CERT_LAST) {
+ virReportInvalidArg(type,
+ _("type must be less than %d"),
+ VIR_DOMAIN_TLS_CERT_LAST);
+ goto error;
+ }
+
+ if (conn->driver->domainReloadTlsCertificates) {
+ int ret;
+ ret = conn->driver->domainReloadTlsCertificates(domain,
+ type,
+ params,
+ nparams,
+ flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virReportUnsupportedError();
+
+ error:
+ virDispatchError(domain->conn);
+ return -1;
+}
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 5678a13cda..30ff012958 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -896,4 +896,9 @@ LIBVIRT_7.3.0 {
virNodeDeviceCreate;
} LIBVIRT_7.2.0;
+LIBVIRT_7.4.0 {
+ global:
+ virDomainReloadTlsCertificates;
+} LIBVIRT_7.3.0;
+
# .... define new API here using predicted next version number ....
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index c90d52edc0..422a350c65 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -20449,6 +20449,45 @@ qemuDomainStartDirtyRateCalc(virDomainPtr dom,
return ret;
}
+static int
+qemuDomainReloadTlsCertificates(virDomainPtr domain,
+ unsigned int type,
+ virTypedParameterPtr params,
+ int nparams,
+ unsigned int flags)
+{
+ int ret = -1;
+ virQEMUDriver *driver = domain->conn->privateData;
+ virDomainObj *vm = qemuDomObjFromDomain(domain);
+
+ if (!vm)
+ goto cleanup;
+
+ virCheckNonNullArgGoto(params, cleanup);
+ if (nparams != 0) {
+ virReportInvalidZeroArg(nparams);
+ goto cleanup;
+ }
+ virCheckFlagsGoto(0, cleanup);
+
+ if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto endjob;
+ }
+
+ ret = qemuDomainReloadTLSCerts(driver, vm, type);
+
+ endjob:
+ qemuDomainObjEndJob(driver, vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
static virHypervisorDriver qemuHypervisorDriver = {
.name = QEMU_DRIVER_NAME,
@@ -20693,6 +20732,7 @@ static virHypervisorDriver qemuHypervisorDriver = {
.domainAuthorizedSSHKeysSet = qemuDomainAuthorizedSSHKeysSet, /* 6.10.0 */
.domainGetMessages = qemuDomainGetMessages, /* 7.1.0 */
.domainStartDirtyRateCalc = qemuDomainStartDirtyRateCalc, /* 7.2.0 */
+ .domainReloadTlsCertificates = qemuDomainReloadTlsCertificates, /* 7.4.0 */
};
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index a64cddb9e7..34dc035d73 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -6704,3 +6704,20 @@ qemuDomainSetVcpuInternal(virQEMUDriver *driver,
virBitmapFree(livevcpus);
return ret;
}
+
+int qemuDomainReloadTLSCerts(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ unsigned int type)
+{
+ int ret = -1;
+ qemuDomainObjPrivate *priv = vm->privateData;
+
+ qemuDomainObjEnterMonitor(driver, vm);
+
+ ret = qemuMonitorDisplayReloadTLSCerts(priv->mon, type);
+
+ if (qemuDomainObjExitMonitor(driver, vm) < 0)
+ ret = -1;
+
+ return ret;
+}
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h
index df8f76f8d6..411741a688 100644
--- a/src/qemu/qemu_hotplug.h
+++ b/src/qemu/qemu_hotplug.h
@@ -160,3 +160,7 @@ int qemuHotplugAttachDBusVMState(virQEMUDriver *driver,
int qemuHotplugRemoveDBusVMState(virQEMUDriver *driver,
virDomainObj *vm,
qemuDomainAsyncJob asyncJob);
+
+int qemuDomainReloadTLSCerts(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ unsigned int type);
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 1e6f892e08..11f8cc8670 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -4746,3 +4746,30 @@ qemuMonitorQueryDirtyRate(qemuMonitor *mon,
return qemuMonitorJSONQueryDirtyRate(mon, info);
}
+
+static const char *
+qemuMonitorTLSCertificateTypeToString(unsigned int type)
+{
+ switch (type) {
+ /* for now, only VNC is supported */
+ case VIR_DOMAIN_TLS_CERT_GRAPHICS_VNC:
+ return "vnc";
+ default:
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("unsupported qemu certificate type %u"),
+ type);
+ return NULL;
+ }
+}
+
+int
+qemuMonitorDisplayReloadTLSCerts(qemuMonitorPtr mon, unsigned int type)
+{
+ const char *certType = qemuMonitorTLSCertificateTypeToString(type);
+ if (!certType)
+ return -1;
+
+ QEMU_CHECK_MONITOR(mon);
+
+ return qemuMonitorJSONDisplayReload(mon, certType, true);
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 6a25def78b..f26f92fb51 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -1496,3 +1496,6 @@ struct _qemuMonitorDirtyRateInfo {
int
qemuMonitorQueryDirtyRate(qemuMonitor *mon,
qemuMonitorDirtyRateInfo *info);
+
+int qemuMonitorDisplayReloadTLSCerts(qemuMonitorPtr mon,
+ unsigned int type);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 46aa3330a8..9934613cc2 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -9446,3 +9446,30 @@ qemuMonitorJSONQueryDirtyRate(qemuMonitor *mon,
return qemuMonitorJSONExtractDirtyRateInfo(data, info);
}
+
+int qemuMonitorJSONDisplayReload(qemuMonitorPtr mon,
+ const char *type,
+ bool tlsCerts)
+{
+ int ret = -1;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("display-reload",
+ "s:type", type,
+ "b:tls-certs", tlsCerts,
+ NULL);
+ if (!cmd)
+ return -1;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 01a3ba25f1..73761d54f8 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -706,3 +706,7 @@ qemuMonitorJSONStartDirtyRateCalc(qemuMonitor *mon,
int
qemuMonitorJSONQueryDirtyRate(qemuMonitor *mon,
qemuMonitorDirtyRateInfo *info);
+
+int qemuMonitorJSONDisplayReload(qemuMonitorPtr mon,
+ const char *type,
+ bool tlsCerts);
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 0c72d69933..0e6e4e3007 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -8566,6 +8566,7 @@ static virHypervisorDriver hypervisor_driver = {
.domainAuthorizedSSHKeysSet = remoteDomainAuthorizedSSHKeysSet, /* 6.10.0 */
.domainGetMessages = remoteDomainGetMessages, /* 7.1.0 */
.domainStartDirtyRateCalc = remoteDomainStartDirtyRateCalc, /* 7.2.0 */
+ .domainReloadTlsCertificates = remoteDomainReloadTlsCertificates, /* 7.4.0 */
};
static virNetworkDriver network_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index de69704b68..c13b3e1f5c 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -286,6 +286,8 @@ const REMOTE_DOMAIN_AUTHORIZED_SSH_KEYS_MAX = 2048;
/* Upper limit on number of messages */
const REMOTE_DOMAIN_MESSAGES_MAX = 2048;
+/* Upper limit on list of TLS certificate parameters */
+const REMOTE_DOMAIN_RELOAD_TLS_CERT_PARAMETERS_MAX = 16;
/* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */
typedef opaque remote_uuid[VIR_UUID_BUFLEN];
@@ -3836,6 +3838,12 @@ struct remote_domain_start_dirty_rate_calc_args {
unsigned int flags;
};
+struct remote_domain_reload_tls_certificates_args {
+ remote_nonnull_domain dom;
+ unsigned int type;
+ remote_typed_param params<REMOTE_DOMAIN_RELOAD_TLS_CERT_PARAMETERS_MAX>;
+ unsigned int flags;
+};
/*----- Protocol. -----*/
@@ -6784,6 +6792,11 @@ enum remote_procedure {
* @priority: high
* @acl: node_device:start
*/
- REMOTE_PROC_NODE_DEVICE_CREATE = 430
+ REMOTE_PROC_NODE_DEVICE_CREATE = 430,
+ /**
+ * @generate: both
+ * @acl: domain:write
+ */
+ REMOTE_PROC_DOMAIN_RELOAD_TLS_CERTIFICATES = 431
};
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index 6b46328adc..799a8596ea 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -3192,6 +3192,15 @@ struct remote_domain_start_dirty_rate_calc_args {
int seconds;
u_int flags;
};
+struct remote_domain_reload_tls_certificates_args {
+ remote_nonnull_domain dom;
+ u_int type;
+ struct {
+ u_int params_len;
+ remote_typed_param * params_val;
+ } params;
+ u_int flags;
+};
enum remote_procedure {
REMOTE_PROC_CONNECT_OPEN = 1,
REMOTE_PROC_CONNECT_CLOSE = 2,
@@ -3623,4 +3632,5 @@ enum remote_procedure {
REMOTE_PROC_NODE_DEVICE_DEFINE_XML = 428,
REMOTE_PROC_NODE_DEVICE_UNDEFINE = 429,
REMOTE_PROC_NODE_DEVICE_CREATE = 430,
+ REMOTE_PROC_DOMAIN_RELOAD_TLS_CERTIFICATES = 431,
};
--
2.25.1