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