[PATCH v4 0/3] Introduce virDomainBlockRebaseParams API
Changes since last revision: - Removed mention of hypervisor-specific details from API docs - Refactored qemu driver implementation in the way that older API calls new one - Added virsh command Nikolai Barybin (3): api: remote: introduce virDomainBlockRebaseParams API qemu: implement driver support for domainBlockRebaseParams API tools: virsh: introduce blockrebase-params cmd docs/manpages/virsh.rst | 39 ++++ include/libvirt/libvirt-domain.h | 34 ++++ src/driver-hypervisor.h | 8 + src/libvirt-domain.c | 63 ++++++ src/libvirt_public.syms | 5 + src/qemu/qemu_driver.c | 327 +++++++++++++++++++------------ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 17 +- src/remote_protocol-structs | 10 + tools/virsh-domain.c | 160 +++++++++++++++ tools/vsh.c | 14 ++ tools/vsh.h | 2 + 12 files changed, 557 insertions(+), 123 deletions(-) -- 2.43.5
This new API is a combination of virDomainBlockPull and virDomainBlockRebase (without copy mode) which supports extendable list of virTypedParameterPtr params. It is designed to allow more configurable use of underlying 'block-stream' QMP command. If "base" == NULL and flags == 0 it is equivalent to virDomainBlockPull. Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- include/libvirt/libvirt-domain.h | 34 +++++++++++++++++ src/driver-hypervisor.h | 8 ++++ src/libvirt-domain.c | 63 ++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 17 ++++++++- src/remote_protocol-structs | 10 +++++ 7 files changed, 137 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 71bb49fe6c..7dae8987a2 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4459,6 +4459,40 @@ int virDomainBlockRebase(virDomainPtr dom, const char *disk, const char *base, unsigned long bandwidth, unsigned int flags); +/** + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE: + * Macro for the virDomainBlockRebaseParams parameter 'base'. + * Expects string specifying new base block device. + * + * Since: 11.8.0 + */ +#define VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE "base" + +/** + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH: + * Macro for the virDomainBlockRebaseParams parameter 'bandwidth'. + * Expects unsigned long long bandwidth value in Bytes/s. + * + * Since: 11.8.0 + */ +#define VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH "bandwidth" + +/** + * virDomainBlockRebaseParamsFlags: + * + * Flags available for virDomainBlockRebaseParams(). + * + * Since: 11.8.0 + */ +typedef enum { + /* Keep backing chain referenced using relative names (Since: 11.8.0) */ + VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE = 1 << 4, +} virDomainBlockRebaseParamsFlags; + +int virDomainBlockRebaseParams(virDomainPtr dom, const char *disk, + virTypedParameterPtr params, int nparams, + unsigned int flags); + /** * virDomainBlockCopyFlags: * diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 6a43688b0c..83cf5c6205 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1089,6 +1089,13 @@ typedef int unsigned long bandwidth, unsigned int flags); +typedef int +(*virDrvDomainBlockRebaseParams)(virDomainPtr dom, + const char *path, + virTypedParameterPtr params, + int nparams, + unsigned int flags); + typedef int (*virDrvDomainBlockCopy)(virDomainPtr dom, const char *path, @@ -1681,6 +1688,7 @@ struct _virHypervisorDriver { virDrvDomainBlockJobSetSpeed domainBlockJobSetSpeed; virDrvDomainBlockPull domainBlockPull; virDrvDomainBlockRebase domainBlockRebase; + virDrvDomainBlockRebaseParams domainBlockRebaseParams; virDrvDomainBlockCopy domainBlockCopy; virDrvDomainBlockCommit domainBlockCommit; virDrvConnectSetKeepAlive connectSetKeepAlive; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index ca110bdf85..27ba0bac45 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -11197,6 +11197,69 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk, } +/** + * virDomainBlockRebaseParams: + * @dom: pointer to domain object + * @disk: path to the block device, or device shorthand + * @params: pointer to block rebase parameter + * @nparams: number of block rebase parameters + * @flags: bitwise-OR of virDomainBlockRebaseParamsFlags + * + * Generelized version of virDomainBlockPull with more precise params setting. + * + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE in @params specifies intermidiate block device + * in a disk backing chain which will result in a new base for a given disk. If ommitted + * or NULL it is equavivalent to normal block pull, when whole backing chain is pulled + * into top image. + * + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH in @params specifies bandwidth in Bytes/s. + * + * If @flags contains VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE, the name recorded + * into the active disk as the location for @base will be kept relative. + * The operation will fail if libvirt can't infer the name. + * + * Returns 0 if the operation has started, -1 on failure. + * + * Since: 11.8.0 + */ +int +virDomainBlockRebaseParams(virDomainPtr dom, + const char *disk, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(dom, "disk=%s, nparams=%u, flags=0x%x", + disk, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + conn = dom->conn; + + virCheckReadOnlyGoto(conn->flags, error); + virCheckNonNullArgGoto(disk, error); + + if (conn->driver->domainBlockRebaseParams) { + int ret; + ret = conn->driver->domainBlockRebaseParams(dom, disk, params, nparams, + flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} + + /** * virDomainBlockCopy: * @dom: pointer to domain object diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index c506acd2ed..0b8365cd2c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -956,4 +956,9 @@ LIBVIRT_11.2.0 { virDomainDelThrottleGroup; } LIBVIRT_10.2.0; +LIBVIRT_11.8.0 { + global: + virDomainBlockRebaseParams; +} LIBVIRT_11.2.0; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ec71eaed87..d9ca026aef 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7865,6 +7865,7 @@ static virHypervisorDriver hypervisor_driver = { .domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = remoteDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = remoteDomainBlockRebase, /* 0.9.10 */ + .domainBlockRebaseParams = remoteDomainBlockRebaseParams, /* 11.8.0 */ .domainBlockCopy = remoteDomainBlockCopy, /* 1.2.9 */ .domainBlockCommit = remoteDomainBlockCommit, /* 0.10.2 */ .connectSetKeepAlive = remoteConnectSetKeepAlive, /* 0.9.8 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 3c93203210..762614b1fa 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -119,6 +119,9 @@ const REMOTE_DOMAIN_NUMA_PARAMETERS_MAX = 16; /* Upper limit on list of perf events. */ const REMOTE_DOMAIN_PERF_EVENTS_MAX = 64; +/* Upper limit on block_rebase_params tunable parameters. */ +const REMOTE_DOMAIN_BLOCK_REBASE_PARAMS_PARAMETERS_MAX = 16; + /* Upper limit on block copy tunable parameters. */ const REMOTE_DOMAIN_BLOCK_COPY_PARAMETERS_MAX = 16; @@ -1440,6 +1443,12 @@ struct remote_domain_block_rebase_args { unsigned hyper bandwidth; unsigned int flags; }; +struct remote_domain_block_rebase_params_args { + remote_nonnull_domain dom; + remote_nonnull_string path; + remote_typed_param params<REMOTE_DOMAIN_BLOCK_REBASE_PARAMS_PARAMETERS_MAX>; + unsigned int flags; +}; struct remote_domain_block_copy_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -7119,5 +7128,11 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 453 + REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 453, + + /** + * @generate: both + * @acl: domain:block_write + */ + REMOTE_PROC_DOMAIN_BLOCK_REBASE_PARAMS = 454 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 0f87d13a5a..ec0963550a 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1010,6 +1010,15 @@ struct remote_domain_block_rebase_args { uint64_t bandwidth; u_int flags; }; +struct remote_domain_block_rebase_params_args { + remote_nonnull_domain dom; + remote_nonnull_string path; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + u_int flags; +}; struct remote_domain_block_copy_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -3791,4 +3800,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_THROTTLE_GROUP = 451, REMOTE_PROC_DOMAIN_DEL_THROTTLE_GROUP = 452, REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 453, + REMOTE_PROC_DOMAIN_BLOCK_REBASE_PARAMS = 454, }; -- 2.43.5
On a Thursday in 2025, Nikolai Barybin via Devel wrote:
This new API is a combination of virDomainBlockPull and virDomainBlockRebase (without copy mode) which supports extendable list of virTypedParameterPtr params.
It is designed to allow more configurable use of underlying 'block-stream' QMP command.
If "base" == NULL and flags == 0 it is equivalent to virDomainBlockPull.
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- include/libvirt/libvirt-domain.h | 34 +++++++++++++++++ src/driver-hypervisor.h | 8 ++++ src/libvirt-domain.c | 63 ++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 17 ++++++++- src/remote_protocol-structs | 10 +++++ 7 files changed, 137 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 71bb49fe6c..7dae8987a2 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4459,6 +4459,40 @@ int virDomainBlockRebase(virDomainPtr dom, const char *disk, const char *base, unsigned long bandwidth, unsigned int flags);
+/** + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE: + * Macro for the virDomainBlockRebaseParams parameter 'base'. + * Expects string specifying new base block device. + * + * Since: 11.8.0 + */ +#define VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE "base" + +/** + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH: + * Macro for the virDomainBlockRebaseParams parameter 'bandwidth'. + * Expects unsigned long long bandwidth value in Bytes/s.
*bytes no need to capitalize the b
+ * + * Since: 11.8.0 + */ +#define VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH "bandwidth" + +/** + * virDomainBlockRebaseParamsFlags: + * + * Flags available for virDomainBlockRebaseParams(). + * + * Since: 11.8.0 + */ +typedef enum { + /* Keep backing chain referenced using relative names (Since: 11.8.0) */ + VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE = 1 << 4, +} virDomainBlockRebaseParamsFlags; + +int virDomainBlockRebaseParams(virDomainPtr dom, const char *disk, + virTypedParameterPtr params, int nparams, + unsigned int flags); + /** * virDomainBlockCopyFlags: * diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 6a43688b0c..83cf5c6205 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1089,6 +1089,13 @@ typedef int unsigned long bandwidth, unsigned int flags);
+typedef int +(*virDrvDomainBlockRebaseParams)(virDomainPtr dom, + const char *path, + virTypedParameterPtr params, + int nparams, + unsigned int flags); + typedef int (*virDrvDomainBlockCopy)(virDomainPtr dom, const char *path, @@ -1681,6 +1688,7 @@ struct _virHypervisorDriver { virDrvDomainBlockJobSetSpeed domainBlockJobSetSpeed; virDrvDomainBlockPull domainBlockPull; virDrvDomainBlockRebase domainBlockRebase; + virDrvDomainBlockRebaseParams domainBlockRebaseParams; virDrvDomainBlockCopy domainBlockCopy; virDrvDomainBlockCommit domainBlockCommit; virDrvConnectSetKeepAlive connectSetKeepAlive; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index ca110bdf85..27ba0bac45 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -11197,6 +11197,69 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk, }
+/** + * virDomainBlockRebaseParams: + * @dom: pointer to domain object + * @disk: path to the block device, or device shorthand + * @params: pointer to block rebase parameter
parameters
+ * @nparams: number of block rebase parameters + * @flags: bitwise-OR of virDomainBlockRebaseParamsFlags + * + * Generelized version of virDomainBlockPull with more precise params setting.
Generalized
+ * + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE in @params specifies intermidiate block device
intermediate
+ * in a disk backing chain which will result in a new base for a given disk. If ommitted
omitted
+ * or NULL it is equavivalent to normal block pull, when whole backing chain is pulled
equivalent when the whole
+ * into top image. + * + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH in @params specifies bandwidth in Bytes/s. + *
bytes
+ * If @flags contains VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE, the name recorded + * into the active disk as the location for @base will be kept relative. + * The operation will fail if libvirt can't infer the name. + * + * Returns 0 if the operation has started, -1 on failure. + * + * Since: 11.8.0 + */ +int +virDomainBlockRebaseParams(virDomainPtr dom, + const char *disk, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(dom, "disk=%s, nparams=%u, flags=0x%x", + disk, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + conn = dom->conn; + + virCheckReadOnlyGoto(conn->flags, error); + virCheckNonNullArgGoto(disk, error); + + if (conn->driver->domainBlockRebaseParams) { + int ret; + ret = conn->driver->domainBlockRebaseParams(dom, disk, params, nparams, + flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} + + /** * virDomainBlockCopy: * @dom: pointer to domain object diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index c506acd2ed..0b8365cd2c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -956,4 +956,9 @@ LIBVIRT_11.2.0 { virDomainDelThrottleGroup; } LIBVIRT_10.2.0;
+LIBVIRT_11.8.0 { + global: + virDomainBlockRebaseParams; +} LIBVIRT_11.2.0; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ec71eaed87..d9ca026aef 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7865,6 +7865,7 @@ static virHypervisorDriver hypervisor_driver = { .domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = remoteDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = remoteDomainBlockRebase, /* 0.9.10 */ + .domainBlockRebaseParams = remoteDomainBlockRebaseParams, /* 11.8.0 */
11.9.0 in the whole patch
.domainBlockCopy = remoteDomainBlockCopy, /* 1.2.9 */ .domainBlockCommit = remoteDomainBlockCommit, /* 0.10.2 */ .connectSetKeepAlive = remoteConnectSetKeepAlive, /* 0.9.8 */
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano
Factor out qemuDomainBlockPullCommon, reuse its code in qemuDomainBlockRebaseParams and call it from older API. Add qemuDomainBlockRebasePrepareParams to ease params list preparation. Now bandwidth unit of messurement is passed as param VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH in Bytes/s. Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_driver.c | 327 ++++++++++++++++++++++++++--------------- 1 file changed, 205 insertions(+), 122 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ac72ea5cb0..8133925ee1 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -13747,113 +13747,6 @@ qemuDomainOpenChannel(virDomainPtr dom, } -/* bandwidth in MiB/s per public API. Caller must lock vm beforehand, - * and not access it afterwards. */ -static int -qemuDomainBlockPullCommon(virDomainObj *vm, - const char *path, - const char *base, - unsigned long bandwidth, - unsigned int flags) -{ - qemuDomainObjPrivate *priv = vm->privateData; - virDomainDiskDef *disk; - virStorageSource *baseSource = NULL; - g_autofree char *backingPath = NULL; - unsigned long long speed = bandwidth; - qemuBlockJobData *job = NULL; - const char *nodebase = NULL; - int ret = -1; - - if (flags & VIR_DOMAIN_BLOCK_REBASE_RELATIVE && !base) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("flag VIR_DOMAIN_BLOCK_REBASE_RELATIVE is valid only with non-null base")); - goto cleanup; - } - - if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) - goto cleanup; - - if (virDomainObjCheckActive(vm) < 0) - goto endjob; - - if (!(disk = qemuDomainDiskByName(vm->def, path))) - goto endjob; - - if (qemuDomainDiskBlockJobIsActive(disk)) - goto endjob; - - if (!qemuDomainDiskBlockJobIsSupported(disk)) - goto endjob; - - if (base && - !(baseSource = virStorageSourceChainLookup(disk->src, disk->src, - base, disk->dst, NULL))) - goto endjob; - - if (baseSource) { - if (flags & VIR_DOMAIN_BLOCK_REBASE_RELATIVE) { - if (qemuBlockUpdateRelativeBacking(vm, disk->src, disk->src) < 0) - goto endjob; - - if (virStorageSourceGetRelativeBackingPath(disk->src->backingStore, - baseSource, - &backingPath) < 0) - goto endjob; - - if (!backingPath) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("can't keep relative backing relationship")); - goto endjob; - } - } - } - - /* Convert bandwidth MiB to bytes, if needed */ - if (!(flags & VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES)) { - if (speed > LLONG_MAX >> 20) { - virReportError(VIR_ERR_OVERFLOW, - _("bandwidth must be less than %1$llu"), - LLONG_MAX >> 20); - goto endjob; - } - speed <<= 20; - } - - if (!(job = qemuBlockJobDiskNewPull(vm, disk, baseSource, flags))) - goto endjob; - - if (baseSource) { - nodebase = qemuBlockStorageSourceGetEffectiveNodename(baseSource); - if (!backingPath && - !(backingPath = qemuBlockGetBackingStoreString(baseSource, false))) - goto endjob; - } - - qemuDomainObjEnterMonitor(vm); - ret = qemuMonitorBlockStream(priv->mon, - qemuBlockStorageSourceGetEffectiveNodename(disk->src), - job->name, - nodebase, - backingPath, - speed); - qemuDomainObjExitMonitor(vm); - - if (ret < 0) - goto endjob; - - qemuBlockJobStarted(job, vm); - - endjob: - virDomainObjEndJob(vm); - - cleanup: - qemuBlockJobStartupFinalize(vm, job); - virDomainObjEndAPI(&vm); - return ret; -} - - static int qemuDomainBlockJobAbort(virDomainPtr dom, const char *path, @@ -14239,6 +14132,179 @@ qemuDomainBlockCopyCommonValidateUserMirrorBackingStore(virStorageSource *mirror } +static int +qemuDomainBlockRebasePrepareParams(const char *base, + unsigned long bandwidth, + unsigned int flags, + int *nparams, + virTypedParameterPtr *params, + unsigned int *rebaseFlags) +{ + int maxparams = 0; + + /* Legacy support: convert bandwidth MiB to bytes, if needed */ + if (!(flags & VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES)) { + if (bandwidth > LLONG_MAX >> 20) { + virReportError(VIR_ERR_OVERFLOW, + _("bandwidth must be less than %1$llu"), + LLONG_MAX >> 20); + return -1; + } + bandwidth <<= 20; + } + + if (base && + virTypedParamsAddString(params, nparams, &maxparams, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE, + base) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to add param %1$s:%2$s to params list"), + VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE, base); + return -1; + } + + if (bandwidth > 0 && + virTypedParamsAddULLong(params, nparams, &maxparams, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH, + bandwidth) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to add param %1$s:%2$lu to params list"), + VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH, bandwidth); + return -1; + } + + if (flags & VIR_DOMAIN_BLOCK_REBASE_RELATIVE) + *rebaseFlags |= VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE; + + return 0; +} + + +static int +qemuDomainBlockRebaseParams(virDomainPtr dom, + const char *path, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virDomainObj *vm = NULL; + size_t i = 0; + unsigned long long bandwidth = 0; + const char *base = NULL; + virDomainDiskDef *disk; + virStorageSource *baseSource = NULL; + qemuDomainObjPrivate *priv = NULL; + g_autofree char *backingPath = NULL; + qemuBlockJobData *job = NULL; + const char *nodebase = NULL; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE, -1); + + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE, + VIR_TYPED_PARAM_STRING, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH, + VIR_TYPED_PARAM_ULLONG, + NULL) < 0) + return -1; + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + priv = vm->privateData; + + if (virDomainBlockRebaseParamsEnsureACL(dom->conn, vm->def) < 0) + return -1; + + for (i = 0; i < nparams; i++) { + virTypedParameterPtr param = ¶ms[i]; + + if (STREQ(param->field, VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH)) { + bandwidth = param->value.ul; + } else if (STREQ(param->field, VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE)) { + base = param->value.s; + } + } + + if (flags & VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE && !base) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("flag VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE is valid only with non-null base")); + goto cleanup; + } + + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + + if (!(disk = qemuDomainDiskByName(vm->def, path))) + goto endjob; + + if (qemuDomainDiskBlockJobIsActive(disk)) + goto endjob; + + if (!qemuDomainDiskBlockJobIsSupported(disk)) + goto endjob; + + if (base && + !(baseSource = virStorageSourceChainLookup(disk->src, disk->src, + base, disk->dst, NULL))) + goto endjob; + + if (baseSource) { + if (flags & VIR_DOMAIN_BLOCK_REBASE_RELATIVE) { + if (qemuBlockUpdateRelativeBacking(vm, disk->src, disk->src) < 0) + goto endjob; + + if (virStorageSourceGetRelativeBackingPath(disk->src->backingStore, + baseSource, + &backingPath) < 0) + goto endjob; + + if (!backingPath) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("can't keep relative backing relationship")); + goto endjob; + } + } + } + + if (!(job = qemuBlockJobDiskNewPull(vm, disk, baseSource, flags))) + goto endjob; + + if (baseSource) { + nodebase = qemuBlockStorageSourceGetEffectiveNodename(baseSource); + if (!backingPath && + !(backingPath = qemuBlockGetBackingStoreString(baseSource, false))) + goto endjob; + } + + qemuDomainObjEnterMonitor(vm); + ret = qemuMonitorBlockStream(priv->mon, + qemuBlockStorageSourceGetEffectiveNodename(disk->src), + job->name, + nodebase, + backingPath, + bandwidth); + qemuDomainObjExitMonitor(vm); + + if (ret < 0) + goto endjob; + + qemuBlockJobStarted(job, vm); + + endjob: + virDomainObjEndJob(vm); + + cleanup: + qemuBlockJobStartupFinalize(vm, job); + virDomainObjEndAPI(&vm); + return ret; +} + + /* bandwidth in bytes/s. Caller must lock vm beforehand, and not * access mirror afterwards. */ static int @@ -14579,17 +14645,30 @@ qemuDomainBlockRebase(virDomainPtr dom, VIR_DOMAIN_BLOCK_REBASE_COPY_DEV | VIR_DOMAIN_BLOCK_REBASE_BANDWIDTH_BYTES, -1); + /* For normal rebase (enhanced blockpull), the common code handles + * everything, including vm cleanup. */ + if (!(flags & VIR_DOMAIN_BLOCK_REBASE_COPY)) { + virTypedParameterPtr params = NULL; + int nparams = 0; + unsigned int rebaseFlags = 0; + + ret = qemuDomainBlockRebasePrepareParams(base, bandwidth, flags, + &nparams, ¶ms, &rebaseFlags); + if (ret == 0) + ret = qemuDomainBlockRebaseParams(dom, path, params, nparams, rebaseFlags); + + virTypedParamsFree(params, nparams); + params = NULL; + nparams = 0; + return ret; + } + if (!(vm = qemuDomainObjFromDomain(dom))) return -1; if (virDomainBlockRebaseEnsureACL(dom->conn, vm->def) < 0) goto cleanup; - /* For normal rebase (enhanced blockpull), the common code handles - * everything, including vm cleanup. */ - if (!(flags & VIR_DOMAIN_BLOCK_REBASE_COPY)) - return qemuDomainBlockPullCommon(vm, path, base, bandwidth, flags); - /* If we got here, we are doing a block copy rebase. */ dest = virStorageSourceNew(); if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_DEV) @@ -14722,19 +14801,22 @@ qemuDomainBlockPull(virDomainPtr dom, unsigned long bandwidth, unsigned int flags) { - virDomainObj *vm; - virCheckFlags(VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES, -1); + virTypedParameterPtr params = NULL; + int nparams = 0; + unsigned int rebaseFlags = 0; + int ret = 0; - if (!(vm = qemuDomainObjFromDomain(dom))) - return -1; + virCheckFlags(VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES, -1); - if (virDomainBlockPullEnsureACL(dom->conn, vm->def) < 0) { - virDomainObjEndAPI(&vm); - return -1; - } + ret = qemuDomainBlockRebasePrepareParams(NULL, bandwidth, flags, + &nparams, ¶ms, &rebaseFlags); + if (ret == 0) + ret = qemuDomainBlockRebaseParams(dom, path, params, nparams, rebaseFlags); - /* qemuDomainBlockPullCommon consumes the reference on @vm */ - return qemuDomainBlockPullCommon(vm, path, NULL, bandwidth, flags); + virTypedParamsFree(params, nparams); + params = NULL; + nparams = 0; + return ret; } @@ -20579,6 +20661,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ + .domainBlockRebaseParams = qemuDomainBlockRebaseParams, /* 11.8.0 */ .domainBlockCopy = qemuDomainBlockCopy, /* 1.2.9 */ .domainBlockCommit = qemuDomainBlockCommit, /* 1.0.0 */ .connectIsAlive = qemuConnectIsAlive, /* 0.9.8 */ -- 2.43.5
On a Thursday in 2025, Nikolai Barybin via Devel wrote:
Factor out qemuDomainBlockPullCommon, reuse its code in qemuDomainBlockRebaseParams and call it from older API.
Separating the factoring out of the function from functional changes makes both patches easier to read and review. Jano
Add qemuDomainBlockRebasePrepareParams to ease params list preparation.
Now bandwidth unit of messurement is passed as param VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH in Bytes/s.
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_driver.c | 327 ++++++++++++++++++++++++++--------------- 1 file changed, 205 insertions(+), 122 deletions(-)
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- docs/manpages/virsh.rst | 39 ++++++++++ tools/virsh-domain.c | 160 ++++++++++++++++++++++++++++++++++++++++ tools/vsh.c | 14 ++++ tools/vsh.h | 2 + 4 files changed, 215 insertions(+) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index bcb5495ed9..16c4f5d28d 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1626,6 +1626,45 @@ on the *bandwidth* argument see the corresponding section for the ``blockjob`` command. +blockrebase-params +------------------ + +**Syntax:** + +:: + + blockrebase-params domain path [bandwidth] [base] + [--wait [--verbose] [--timeout seconds] [--async]] + [--keep-relative] + +Populate a disk from its backing image chain. By default, this command +flattens the entire chain; but if *base* is specified, containing the +name of one of the backing files in the chain, then that file becomes +the new backing file and only the intermediate portion of the chain is +pulled. Once all requested data from the backing image chain has been +pulled, the disk no longer depends on that portion of the backing chain. + +By default, this command returns as soon as possible, and data for +the entire disk is pulled in the background; the progress of the +operation can be checked with ``blockjob``. However, if *--wait* is +specified, then this command will block until the operation completes, +or cancel the operation if the optional *timeout* in seconds elapses +or SIGINT is sent (usually with ``Ctrl-C``). Using *--verbose* along +with *--wait* will produce periodic status updates. If job cancellation +is triggered, *--async* will return control to the user as fast as +possible, otherwise the command may continue to block a little while +longer until the job is done cleaning up. + +Using the *--keep-relative* flag will keep the backing chain names +relative. + +*path* specifies fully-qualified path of the disk; it corresponds +to a unique target name (<target dev='name'/>) or source file (<source +file='name'/>) for one of the disk devices attached to *domain* (see +also ``domblklist`` for listing these names). +*bandwidth* specifies copying bandwidth limit in Bytes/s. + + blockresize ----------- diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 6e18d195e6..9023f8f72d 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -3306,6 +3306,160 @@ cmdBlockpull(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "blockrebase-params" command + */ +static const vshCmdInfo info_blockrebase_params = { + .help = N_("Populate a disk from its backing image."), + .desc = N_("Populate a disk from its backing image."), +}; + +static const vshCmdOptDef opts_blockrebase_params[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(VIR_CONNECT_LIST_DOMAINS_ACTIVE), + {.name = "path", + .type = VSH_OT_STRING, + .positional = true, + .required = true, + .completer = virshDomainDiskTargetCompleter, + .help = N_("fully-qualified path of disk") + }, + {.name = "bandwidth", + .type = VSH_OT_INT, + .unwanted_positional = true, + .help = N_("bandwidth limit in bytes/s") + }, + {.name = "base", + .type = VSH_OT_STRING, + .unwanted_positional = true, + .completer = virshDomainBlockjobBaseTopCompleter, + .help = N_("path of backing file in chain specifying a new base") + }, + {.name = "wait", + .type = VSH_OT_BOOL, + .help = N_("wait for job to finish") + }, + {.name = "verbose", + .type = VSH_OT_BOOL, + .help = N_("with --wait, display the progress") + }, + {.name = "timeout", + .type = VSH_OT_INT, + .unwanted_positional = true, + .help = N_("with --wait, abort if rebase exceeds timeout (in seconds)") + }, + {.name = "async", + .type = VSH_OT_BOOL, + .help = N_("with --wait, don't wait for cancel to finish") + }, + {.name = "keep-relative", + .type = VSH_OT_BOOL, + .help = N_("keep the backing chain relatively referenced") + }, + {.name = NULL} +}; + +static bool +cmdBlockrebaseParams(vshControl *ctl, const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + bool ret = false; + bool blocking = vshCommandOptBool(cmd, "wait"); + bool verbose = vshCommandOptBool(cmd, "verbose"); + bool async = vshCommandOptBool(cmd, "async"); + int timeout = 0; + const char *path = NULL; + const char *base = NULL; + unsigned long long bandwidth = 0; + unsigned int flags = 0; + virshBlockJobWaitData *bjWait = NULL; + int nparams = 0; + int maxparams = 0; + virTypedParameterPtr params = NULL; + + VSH_REQUIRE_OPTION("verbose", "wait"); + VSH_REQUIRE_OPTION("async", "wait"); + + if (vshCommandOptString(ctl, cmd, "path", &path) < 0) + return false; + + if (vshCommandOptString(ctl, cmd, "base", &base) < 0) + return false; + + if (vshBlockRebaseParamsOptionBandwidth(cmd, &bandwidth) < 0) + return false; + + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + return false; + + if (vshCommandOptBool(cmd, "keep-relative")) + flags |= VIR_DOMAIN_BLOCK_REBASE_PARAMS_RELATIVE; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (blocking && + !(bjWait = virshBlockJobWaitInit(ctl, dom, path, _("Block Rebase Params"), + verbose, timeout, async))) + goto cleanup; + + if (base && + virTypedParamsAddString(¶ms, &nparams, &maxparams, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE, + base) < 0) { + vshError(ctl, _("failed to add param %1$s:%2$s to params list"), + VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE, base); + goto cleanup; + } + + if (bandwidth > 0 && + virTypedParamsAddULLong(¶ms, &nparams, &maxparams, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH, + bandwidth) < 0) { + vshError(ctl, _("failed to add param %1$s:%2$llu to params list"), + VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH, bandwidth); + goto cleanup; + } + + if (virDomainBlockRebaseParams(dom, path, params, nparams, flags) < 0) + goto cleanup; + + if (!blocking) { + vshPrintExtra(ctl, "%s", _("Block Rebase started")); + ret = true; + goto cleanup; + } + + /* Execution continues here only if --wait or friends were specified */ + switch (virshBlockJobWait(bjWait)) { + case -1: + goto cleanup; + + case VIR_DOMAIN_BLOCK_JOB_CANCELED: + vshPrintExtra(ctl, "\n%s", _("Rebase aborted")); + goto cleanup; + break; + + case VIR_DOMAIN_BLOCK_JOB_FAILED: + vshPrintExtra(ctl, "\n%s", _("Rebase failed")); + goto cleanup; + break; + + case VIR_DOMAIN_BLOCK_JOB_READY: + case VIR_DOMAIN_BLOCK_JOB_COMPLETED: + vshPrintExtra(ctl, "\n%s", _("Rebase complete")); + break; + } + + ret = true; + + cleanup: + virshBlockJobWaitFree(bjWait); + virTypedParamsFree(params, nparams); + params = NULL; + nparams = 0; + return ret; +} + /* * "blockresize" command */ @@ -13976,6 +14130,12 @@ const vshCmdDef domManagementCmds[] = { .info = &info_blockpull, .flags = 0 }, + {.name = "blockrebase-params", + .handler = cmdBlockrebaseParams, + .opts = opts_blockrebase_params, + .info = &info_blockrebase_params, + .flags = 0 + }, {.name = "blockresize", .handler = cmdBlockresize, .opts = opts_blockresize, diff --git a/tools/vsh.c b/tools/vsh.c index 4aacc5feac..480b54b572 100644 --- a/tools/vsh.c +++ b/tools/vsh.c @@ -1346,6 +1346,20 @@ vshBlockJobOptionBandwidth(vshControl *ctl, } +int +vshBlockRebaseParamsOptionBandwidth(const vshCmd *cmd, + unsigned long long *bandwidth) +{ + vshCmdOpt *arg; + int ret; + + if ((ret = vshCommandOpt(cmd, "bandwidth", &arg, true)) <= 0) + return ret; + + return virStrToLong_ull(arg->data, NULL, 10, bandwidth); +} + + /** * vshCommandRun: * @ctl: virt shell data diff --git a/tools/vsh.h b/tools/vsh.h index bd2494e899..0f79c32d41 100644 --- a/tools/vsh.h +++ b/tools/vsh.h @@ -287,6 +287,8 @@ int vshBlockJobOptionBandwidth(vshControl *ctl, const vshCmd *cmd, bool bytes, unsigned long *bandwidth); +int vshBlockRebaseParamsOptionBandwidth(const vshCmd *cmd, + unsigned long long *bandwidth); bool vshCommandOptBool(const vshCmd *cmd, const char *name); int vshCommandRun(vshControl *ctl, const vshCmd *cmd); bool vshCommandStringParse(vshControl *ctl, char *cmdstr, -- 2.43.5
On a Thursday in 2025, Nikolai Barybin via Devel wrote:
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- docs/manpages/virsh.rst | 39 ++++++++++ tools/virsh-domain.c | 160 ++++++++++++++++++++++++++++++++++++++++ tools/vsh.c | 14 ++++ tools/vsh.h | 2 + 4 files changed, 215 insertions(+)
diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index bcb5495ed9..16c4f5d28d 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1626,6 +1626,45 @@ on the *bandwidth* argument see the corresponding section for the ``blockjob`` command.
+blockrebase-params
The -params suffix is not of any help for the virsh users. We only use the Params suffix when we need to add a new API with parameter support which has an older alternative without them (we need to keep the older one because we maintain ABI compatibility). For virsh, the latest version can be used to remotely connect to older versions of libvirt, so if whatever the user asked for can be satisfied with the usage of the older API, virsh tries that instead of the new one. If the functionality is sufficiently similar to the blockpull command, I suggest you put it there and only call the new API when the user requested the extra stuff. Jano
participants (2)
-
Ján Tomko -
Nikolai Barybin