[PATCH v2 0/3] virDomain{Save,Restore}Params: More fixes

v2 of: https://listman.redhat.com/archives/libvir-list/2022-May/231307.html diff to v1: - Reworked 3/3, instead of promoting restore path to a regular argument, let's keep it in typed params as this is more future proof. But require it for now. Michal Prívozník (3): qemu: Separate out save code from qemuDomainManagedSave() lib: Repurpose virDomainSaveParams() with no VIR_DOMAIN_SAVE_PARAM_FILE virDomainRestoreFlags: Require VIR_DOMAIN_SAVE_PARAM_FILE for now src/libvirt-domain.c | 6 ++- src/qemu/qemu_driver.c | 117 +++++++++++++++++++++++++---------------- 2 files changed, 77 insertions(+), 46 deletions(-) -- 2.35.1

The code that actually does managed save within qemuDomainManagedSave() is going to be reused shortly. Move it out into a separate helper. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 82 ++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4b690a520b..7d8c7176d9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2760,6 +2760,53 @@ qemuDomainSaveInternal(virQEMUDriver *driver, } +static char * +qemuDomainManagedSavePath(virQEMUDriver *driver, virDomainObj *vm) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + + return g_strdup_printf("%s/%s.save", cfg->saveDir, vm->def->name); +} + + +static int +qemuDomainManagedSaveHelper(virQEMUDriver *driver, + virDomainObj *vm, + unsigned int flags) +{ + g_autoptr(virQEMUDriverConfig) cfg = NULL; + g_autoptr(virCommand) compressor = NULL; + g_autofree char *path = NULL; + int compressed; + + if (virDomainObjCheckActive(vm) < 0) + return -1; + + if (!vm->persistent) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot do managed save for transient domain")); + return -1; + } + + cfg = virQEMUDriverGetConfig(driver); + if ((compressed = qemuSaveImageGetCompressionProgram(cfg->saveImageFormat, + &compressor, + "save", false)) < 0) + return -1; + + path = qemuDomainManagedSavePath(driver, vm); + + VIR_INFO("Saving state of domain '%s' to '%s'", vm->def->name, path); + + if (qemuDomainSaveInternal(driver, vm, path, compressed, + compressor, NULL, flags) < 0) + return -1; + + vm->hasManagedSave = true; + return 0; +} + + static int qemuDomainSaveFlags(virDomainPtr dom, const char *path, const char *dxml, unsigned int flags) @@ -2860,23 +2907,12 @@ qemuDomainSaveParams(virDomainPtr dom, return ret; } -static char * -qemuDomainManagedSavePath(virQEMUDriver *driver, virDomainObj *vm) -{ - g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); - - return g_strdup_printf("%s/%s.save", cfg->saveDir, vm->def->name); -} static int qemuDomainManagedSave(virDomainPtr dom, unsigned int flags) { virQEMUDriver *driver = dom->conn->privateData; - g_autoptr(virQEMUDriverConfig) cfg = NULL; - int compressed; - g_autoptr(virCommand) compressor = NULL; virDomainObj *vm; - g_autofree char *name = NULL; int ret = -1; virCheckFlags(VIR_DOMAIN_SAVE_BYPASS_CACHE | @@ -2889,29 +2925,7 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags) if (virDomainManagedSaveEnsureACL(dom->conn, vm->def) < 0) goto cleanup; - if (virDomainObjCheckActive(vm) < 0) - goto cleanup; - - if (!vm->persistent) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("cannot do managed save for transient domain")); - goto cleanup; - } - - cfg = virQEMUDriverGetConfig(driver); - if ((compressed = qemuSaveImageGetCompressionProgram(cfg->saveImageFormat, - &compressor, - "save", false)) < 0) - goto cleanup; - - name = qemuDomainManagedSavePath(driver, vm); - - VIR_INFO("Saving state of domain '%s' to '%s'", vm->def->name, name); - - ret = qemuDomainSaveInternal(driver, vm, name, compressed, - compressor, NULL, flags); - if (ret == 0) - vm->hasManagedSave = true; + ret = qemuDomainManagedSaveHelper(driver, vm, flags); cleanup: virDomainObjEndAPI(&vm); -- 2.35.1

When no VIR_DOMAIN_SAVE_PARAM_FILE typed param is set when calling virDomainSaveParams() then in turn virQEMUFileOpenAs() tries to open a NULL path. We have two options now: 1) require the typed param, which in turn may be promoted to a regular argument, or 2) use this opportunity to make the API behave like virDomainManagedSave() and use typed params to pass extra arguments, instead of having to invent new managed save API with typed params. Let's go with option 2, as it is more future proof. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt-domain.c | 2 ++ src/qemu/qemu_driver.c | 33 ++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index d1d62daa71..208c2438fe 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -1007,6 +1007,8 @@ virDomainSaveFlags(virDomainPtr domain, const char *to, * @flags: bitwise-OR of virDomainSaveRestoreFlags * * This method extends virDomainSaveFlags by adding parameters. + * If VIR_DOMAIN_SAVE_PARAM_FILE is not provided then a managed save is + * performed (see virDomainManagedSave). * * Returns 0 in case of success and -1 in case of failure. * diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7d8c7176d9..0b31c73bb9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2772,6 +2772,7 @@ qemuDomainManagedSavePath(virQEMUDriver *driver, virDomainObj *vm) static int qemuDomainManagedSaveHelper(virQEMUDriver *driver, virDomainObj *vm, + const char *dxml, unsigned int flags) { g_autoptr(virQEMUDriverConfig) cfg = NULL; @@ -2799,7 +2800,7 @@ qemuDomainManagedSaveHelper(virQEMUDriver *driver, VIR_INFO("Saving state of domain '%s' to '%s'", vm->def->name, path); if (qemuDomainSaveInternal(driver, vm, path, compressed, - compressor, NULL, flags) < 0) + compressor, dxml, flags) < 0) return -1; vm->hasManagedSave = true; @@ -2853,17 +2854,18 @@ qemuDomainSave(virDomainPtr dom, const char *path) static int qemuDomainSaveParams(virDomainPtr dom, - virTypedParameterPtr params, int nparams, + virTypedParameterPtr params, + int nparams, unsigned int flags) { + virQEMUDriver *driver = dom->conn->privateData; + g_autoptr(virQEMUDriverConfig) cfg = NULL; + virDomainObj *vm = NULL; + g_autoptr(virCommand) compressor = NULL; const char *to = NULL; const char *dxml = NULL; - virQEMUDriver *driver = dom->conn->privateData; int compressed; - g_autoptr(virCommand) compressor = NULL; int ret = -1; - virDomainObj *vm = NULL; - g_autoptr(virQEMUDriverConfig) cfg = NULL; virCheckFlags(VIR_DOMAIN_SAVE_BYPASS_CACHE | VIR_DOMAIN_SAVE_RUNNING | @@ -2884,18 +2886,23 @@ qemuDomainSaveParams(virDomainPtr dom, VIR_DOMAIN_SAVE_PARAM_DXML, &dxml) < 0) return -1; - cfg = virQEMUDriverGetConfig(driver); - if ((compressed = qemuSaveImageGetCompressionProgram(cfg->saveImageFormat, - &compressor, - "save", false)) < 0) - goto cleanup; - if (!(vm = qemuDomainObjFromDomain(dom))) goto cleanup; if (virDomainSaveParamsEnsureACL(dom->conn, vm->def) < 0) goto cleanup; + if (!to) { + /* If no save path was provided then this behaves as managed save. */ + return qemuDomainManagedSaveHelper(driver, vm, dxml, flags); + } + + cfg = virQEMUDriverGetConfig(driver); + if ((compressed = qemuSaveImageGetCompressionProgram(cfg->saveImageFormat, + &compressor, + "save", false)) < 0) + goto cleanup; + if (virDomainObjCheckActive(vm) < 0) goto cleanup; @@ -2925,7 +2932,7 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags) if (virDomainManagedSaveEnsureACL(dom->conn, vm->def) < 0) goto cleanup; - ret = qemuDomainManagedSaveHelper(driver, vm, flags); + ret = qemuDomainManagedSaveHelper(driver, vm, NULL, flags); cleanup: virDomainObjEndAPI(&vm); -- 2.35.1

On 5/13/22 9:54 AM, Michal Privoznik wrote:
When no VIR_DOMAIN_SAVE_PARAM_FILE typed param is set when calling virDomainSaveParams() then in turn virQEMUFileOpenAs() tries to open a NULL path.
We have two options now: 1) require the typed param, which in turn may be promoted to a regular argument, or
2) use this opportunity to make the API behave like virDomainManagedSave() and use typed params to pass extra arguments, instead of having to invent new managed save API with typed params.
Let's go with option 2, as it is more future proof.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt-domain.c | 2 ++ src/qemu/qemu_driver.c | 33 ++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index d1d62daa71..208c2438fe 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -1007,6 +1007,8 @@ virDomainSaveFlags(virDomainPtr domain, const char *to, * @flags: bitwise-OR of virDomainSaveRestoreFlags * * This method extends virDomainSaveFlags by adding parameters. + * If VIR_DOMAIN_SAVE_PARAM_FILE is not provided then a managed save is + * performed (see virDomainManagedSave). * * Returns 0 in case of success and -1 in case of failure. * diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7d8c7176d9..0b31c73bb9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2772,6 +2772,7 @@ qemuDomainManagedSavePath(virQEMUDriver *driver, virDomainObj *vm) static int qemuDomainManagedSaveHelper(virQEMUDriver *driver, virDomainObj *vm, + const char *dxml, unsigned int flags) { g_autoptr(virQEMUDriverConfig) cfg = NULL; @@ -2799,7 +2800,7 @@ qemuDomainManagedSaveHelper(virQEMUDriver *driver, VIR_INFO("Saving state of domain '%s' to '%s'", vm->def->name, path);
if (qemuDomainSaveInternal(driver, vm, path, compressed, - compressor, NULL, flags) < 0) + compressor, dxml, flags) < 0) return -1;
vm->hasManagedSave = true; @@ -2853,17 +2854,18 @@ qemuDomainSave(virDomainPtr dom, const char *path)
static int qemuDomainSaveParams(virDomainPtr dom, - virTypedParameterPtr params, int nparams, + virTypedParameterPtr params, + int nparams, unsigned int flags) { + virQEMUDriver *driver = dom->conn->privateData; + g_autoptr(virQEMUDriverConfig) cfg = NULL; + virDomainObj *vm = NULL; + g_autoptr(virCommand) compressor = NULL; const char *to = NULL; const char *dxml = NULL; - virQEMUDriver *driver = dom->conn->privateData; int compressed; - g_autoptr(virCommand) compressor = NULL; int ret = -1; - virDomainObj *vm = NULL; - g_autoptr(virQEMUDriverConfig) cfg = NULL;
virCheckFlags(VIR_DOMAIN_SAVE_BYPASS_CACHE | VIR_DOMAIN_SAVE_RUNNING | @@ -2884,18 +2886,23 @@ qemuDomainSaveParams(virDomainPtr dom, VIR_DOMAIN_SAVE_PARAM_DXML, &dxml) < 0) return -1;
- cfg = virQEMUDriverGetConfig(driver); - if ((compressed = qemuSaveImageGetCompressionProgram(cfg->saveImageFormat, - &compressor, - "save", false)) < 0) - goto cleanup; - if (!(vm = qemuDomainObjFromDomain(dom))) goto cleanup;
if (virDomainSaveParamsEnsureACL(dom->conn, vm->def) < 0) goto cleanup;
+ if (!to) { + /* If no save path was provided then this behaves as managed save. */ + return qemuDomainManagedSaveHelper(driver, vm, dxml, flags); + }
Is this sufficient? In some cases I am seeing that omitted string parameters are passed as the empty string. In my multifd series I encountered this with the optional --parallel-compression argument, that in this case would mean: if (!to || !to[0]) Thanks, Claudio
+ + cfg = virQEMUDriverGetConfig(driver); + if ((compressed = qemuSaveImageGetCompressionProgram(cfg->saveImageFormat, + &compressor, + "save", false)) < 0) + goto cleanup; + if (virDomainObjCheckActive(vm) < 0) goto cleanup;
@@ -2925,7 +2932,7 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags) if (virDomainManagedSaveEnsureACL(dom->conn, vm->def) < 0) goto cleanup;
- ret = qemuDomainManagedSaveHelper(driver, vm, flags); + ret = qemuDomainManagedSaveHelper(driver, vm, NULL, flags);
cleanup: virDomainObjEndAPI(&vm);

On 5/13/22 14:57, Claudio Fontana wrote:
On 5/13/22 9:54 AM, Michal Privoznik wrote:
When no VIR_DOMAIN_SAVE_PARAM_FILE typed param is set when calling virDomainSaveParams() then in turn virQEMUFileOpenAs() tries to open a NULL path.
We have two options now: 1) require the typed param, which in turn may be promoted to a regular argument, or
2) use this opportunity to make the API behave like virDomainManagedSave() and use typed params to pass extra arguments, instead of having to invent new managed save API with typed params.
Let's go with option 2, as it is more future proof.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt-domain.c | 2 ++ src/qemu/qemu_driver.c | 33 ++++++++++++++++++++------------- 2 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index d1d62daa71..208c2438fe 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -1007,6 +1007,8 @@ virDomainSaveFlags(virDomainPtr domain, const char *to, * @flags: bitwise-OR of virDomainSaveRestoreFlags * * This method extends virDomainSaveFlags by adding parameters. + * If VIR_DOMAIN_SAVE_PARAM_FILE is not provided then a managed save is + * performed (see virDomainManagedSave). * * Returns 0 in case of success and -1 in case of failure. * diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7d8c7176d9..0b31c73bb9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2772,6 +2772,7 @@ qemuDomainManagedSavePath(virQEMUDriver *driver, virDomainObj *vm) static int qemuDomainManagedSaveHelper(virQEMUDriver *driver, virDomainObj *vm, + const char *dxml, unsigned int flags) { g_autoptr(virQEMUDriverConfig) cfg = NULL; @@ -2799,7 +2800,7 @@ qemuDomainManagedSaveHelper(virQEMUDriver *driver, VIR_INFO("Saving state of domain '%s' to '%s'", vm->def->name, path);
if (qemuDomainSaveInternal(driver, vm, path, compressed, - compressor, NULL, flags) < 0) + compressor, dxml, flags) < 0) return -1;
vm->hasManagedSave = true; @@ -2853,17 +2854,18 @@ qemuDomainSave(virDomainPtr dom, const char *path)
static int qemuDomainSaveParams(virDomainPtr dom, - virTypedParameterPtr params, int nparams, + virTypedParameterPtr params, + int nparams, unsigned int flags) { + virQEMUDriver *driver = dom->conn->privateData; + g_autoptr(virQEMUDriverConfig) cfg = NULL; + virDomainObj *vm = NULL; + g_autoptr(virCommand) compressor = NULL; const char *to = NULL; const char *dxml = NULL; - virQEMUDriver *driver = dom->conn->privateData; int compressed; - g_autoptr(virCommand) compressor = NULL; int ret = -1; - virDomainObj *vm = NULL; - g_autoptr(virQEMUDriverConfig) cfg = NULL;
virCheckFlags(VIR_DOMAIN_SAVE_BYPASS_CACHE | VIR_DOMAIN_SAVE_RUNNING | @@ -2884,18 +2886,23 @@ qemuDomainSaveParams(virDomainPtr dom, VIR_DOMAIN_SAVE_PARAM_DXML, &dxml) < 0) return -1;
- cfg = virQEMUDriverGetConfig(driver); - if ((compressed = qemuSaveImageGetCompressionProgram(cfg->saveImageFormat, - &compressor, - "save", false)) < 0) - goto cleanup; - if (!(vm = qemuDomainObjFromDomain(dom))) goto cleanup;
if (virDomainSaveParamsEnsureACL(dom->conn, vm->def) < 0) goto cleanup;
+ if (!to) { + /* If no save path was provided then this behaves as managed save. */ + return qemuDomainManagedSaveHelper(driver, vm, dxml, flags); + }
Is this sufficient? In some cases I am seeing that omitted string parameters are passed as the empty string. In my multifd series I encountered this with the optional --parallel-compression argument,
that in this case would mean:
if (!to || !to[0])
That's likely caused by virsh. I haven't checked your patches yet, but depending on how the argument is queried for in virsh you may get a NULL or an empty string. However, empty string doesn't matter as it produces sensible error message: No such file or directory: '' Michal

Calling virDomainRestoreFlags() with no typed params results in an error in open() because it tries to open a NULL path. Obviously, this is wrong and path to restore from must be provided, at least for now until other sources of restore are introduced. Then this limitation can be relaxed. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt-domain.c | 4 +++- src/qemu/qemu_driver.c | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 208c2438fe..a32630a6e9 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -1190,7 +1190,9 @@ virDomainRestoreFlags(virConnectPtr conn, const char *from, const char *dxml, * @nparams: number of restore parameters * @flags: bitwise-OR of virDomainSaveRestoreFlags * - * This method extends virDomainRestoreFlags by adding parameters. + * This method extends virDomainRestoreFlags by adding parameters. For + * now, VIR_DOMAIN_SAVE_PARAM_FILE is required but this requirement may + * be lifted in the future. * * Returns 0 in case of success and -1 in case of failure. * diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0b31c73bb9..702fd0239c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5967,6 +5967,12 @@ qemuDomainRestoreParams(virConnectPtr conn, VIR_DOMAIN_SAVE_PARAM_DXML, &dxml) < 0) return -1; + if (!path) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing path to restore from")); + return -1; + } + ret = qemuDomainRestoreInternal(conn, path, dxml, flags, virDomainRestoreParamsEnsureACL); return ret; -- 2.35.1

On 5/13/22 9:54 AM, Michal Privoznik wrote:
Calling virDomainRestoreFlags() with no typed params results in an error in open() because it tries to open a NULL path. Obviously, this is wrong and path to restore from must be provided, at least for now until other sources of restore are introduced. Then this limitation can be relaxed.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt-domain.c | 4 +++- src/qemu/qemu_driver.c | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 208c2438fe..a32630a6e9 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -1190,7 +1190,9 @@ virDomainRestoreFlags(virConnectPtr conn, const char *from, const char *dxml, * @nparams: number of restore parameters * @flags: bitwise-OR of virDomainSaveRestoreFlags * - * This method extends virDomainRestoreFlags by adding parameters. + * This method extends virDomainRestoreFlags by adding parameters. For + * now, VIR_DOMAIN_SAVE_PARAM_FILE is required but this requirement may + * be lifted in the future. * * Returns 0 in case of success and -1 in case of failure. * diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0b31c73bb9..702fd0239c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5967,6 +5967,12 @@ qemuDomainRestoreParams(virConnectPtr conn, VIR_DOMAIN_SAVE_PARAM_DXML, &dxml) < 0) return -1;
+ if (!path) {
Similarly to before, should the check be (!path || !path[0]) ? Thanks C
+ virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing path to restore from")); + return -1; + } + ret = qemuDomainRestoreInternal(conn, path, dxml, flags, virDomainRestoreParamsEnsureACL); return ret;

On Fri, May 13, 2022 at 09:54:25AM +0200, Michal Privoznik wrote:
v2 of:
https://listman.redhat.com/archives/libvir-list/2022-May/231307.html
diff to v1: - Reworked 3/3, instead of promoting restore path to a regular argument, let's keep it in typed params as this is more future proof. But require it for now.
Michal Prívozník (3): qemu: Separate out save code from qemuDomainManagedSave() lib: Repurpose virDomainSaveParams() with no VIR_DOMAIN_SAVE_PARAM_FILE virDomainRestoreFlags: Require VIR_DOMAIN_SAVE_PARAM_FILE for now
Reviewed-by: Martin Kletzander <mkletzan@redhat.com> even though I don't particularly like it, but it seems this was the intention of Daniel and Claudio. Maybe we should mandate all new APIs to accept only typed params? /s
src/libvirt-domain.c | 6 ++- src/qemu/qemu_driver.c | 117 +++++++++++++++++++++++++---------------- 2 files changed, 77 insertions(+), 46 deletions(-)
-- 2.35.1
participants (4)
-
Claudio Fontana
-
Martin Kletzander
-
Michal Privoznik
-
Michal Prívozník