On Sat, Mar 21, 2026 at 15:09:03 +0100, Guy Godfroy wrote: > Currently, the hook script with the "migrate" operation is only called > on the destination host at the beginning of incoming migration. This > makes it impossible for the source host to prepare for migration, for > example to change storage locks from exclusive to shared mode when using > shared LVM storage with lvmlockd. > > Add a hook call on the source host at the beginning of outgoing > migration using the new "migrate-outgoing" operation. The hook is called > with "source" as the extra argument and "begin" as the sub-operation.
I don't think having the 'source' extra argument makes sense once we have the whole separate 'migrate-outgoing' operation. > The script is not used as a filter; its output is ignored. If the script > returns failure, the migration is canceled. > > The same change is also applied to the libxl driver for consistency. > > Signed-off-by: Guy Godfroy <[email protected]> > --- > docs/hooks.rst | 32 ++++++++++++++++++++++++++++++-- > src/libxl/libxl_migration.c | 17 +++++++++++++++++ > src/qemu/qemu_migration.c | 17 +++++++++++++++++ > 3 files changed, 64 insertions(+), 2 deletions(-) > > diff --git a/docs/hooks.rst b/docs/hooks.rst > index 0310e0d82c..cff1711536 100644 > --- a/docs/hooks.rst > +++ b/docs/hooks.rst > @@ -257,6 +257,18 @@ operation. There is no specific operation to indicate a > "restart" is occurring. > is not valid, incoming migration will be canceled. This hook may be used, > e.g., to change location of disk images for incoming domains. > > +- :since:`Since 12.3.0`, the qemu hook script is also called at the > beginning > + of outgoing migration on the **source** host. It is called as: > + > + :: > + > + /etc/libvirt/hooks/qemu guest_name migrate-outgoing begin source > + > + with domain XML sent to standard input of the script. Unlike the > destination > + hook, this script does not act as a filter and any output is ignored. If > the > + script returns failure, the migration will be canceled. This hook may be > used, > + e.g., to change storage lock mode from exclusive to shared before > migration. I'd refrain from giving any suggestions how to use the script. It's not the only way to do things nor the suggested way to do thihgs. > + > - :since:`Since 1.2.9`, the qemu hook script is also called when restoring a > saved image either via the API or automatically when restoring a managed > save > machine. It is called as: > @@ -430,6 +442,18 @@ operation. There is no specific operation to indicate a > "restart" is occurring. > is not valid, incoming migration will be canceled. This hook may be used, > e.g., to change location of disk images for incoming domains. > > +- :since:`Since 12.3.0`, the libxl hook script is also called at the > beginning > + of outgoing migration on the **source** host. It is called as: > + > + :: > + > + /etc/libvirt/hooks/libxl guest_name migrate-outgoing begin source > + > + with domain XML sent to standard input of the script. Unlike the > destination > + hook, this script does not act as a filter and any output is ignored. If > the > + script returns failure, the migration will be canceled. This hook may be > used, > + e.g., to change storage lock mode from exclusive to shared before > migration. > + > - :since:`Since 6.5.0`, you can also place several hook scripts in the > directory ``/etc/libvirt/hooks/libxl.d/``. They are executed in > alphabetical > order after main script. In this case each script also acts as filter and > can > @@ -566,10 +590,14 @@ Migration of a QEMU guest involves running hook scripts > on both the source and > destination hosts: > > #. At the beginning of the migration, the *qemu* hook script on the > - **destination** host is executed with the "migrate" operation. > + **source** host is executed with the "migrate-outgoing" operation and > + "source" as the extra argument (:since:`since 12.3.0`). This allows the > + source host to prepare for migration, e.g., changing storage locks. > +#. Then, the *qemu* hook script on the **destination** host is executed > + with the "migrate" operation. > #. Before QEMU process is spawned, the two operations ("prepare" and "start") > called for domain start are executed on **destination** host. > -#. If both of these hook script executions exit successfully (exit status 0), > +#. If all of these hook script executions exit successfully (exit status 0), > the migration continues. Any other exit code indicates failure, and the > migration is aborted. > #. The QEMU guest is then migrated to the destination host. > diff --git a/src/libxl/libxl_migration.c b/src/libxl/libxl_migration.c > index f5dee7627b..be51cfd316 100644 > --- a/src/libxl/libxl_migration.c > +++ b/src/libxl/libxl_migration.c > @@ -411,6 +411,23 @@ libxlDomainMigrationSrcBegin(virConnectPtr conn, > if (!libxlDomainMigrationIsAllowed(def)) > goto endjob; > > + /* Call hook to allow source host to prepare for migration */ > + if (virHookPresent(VIR_HOOK_DRIVER_LIBXL)) { > + g_autofree char *hookxml = NULL; > + int hookret; > + > + if (!(hookxml = virDomainDefFormat(def, driver->xmlopt, > + VIR_DOMAIN_DEF_FORMAT_SECURE))) > + goto endjob; > + > + hookret = virHookCall(VIR_HOOK_DRIVER_LIBXL, def->name, > + VIR_HOOK_LIBXL_OP_MIGRATE_OUTGOING, > VIR_HOOK_SUBOP_BEGIN, > + "source", hookxml, NULL); the libxl driver has 'libxlDomainHookRun'. Any reason you've open-coded this? Additionally VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_MIGRATABLE, are used to format the XML and you picked only _SECURE. > + > + if (hookret < 0) > + goto endjob; > + } > + > xml = virDomainDefFormat(def, driver->xmlopt, > VIR_DOMAIN_DEF_FORMAT_SECURE); > /* Valid xml means success! EndJob in the confirm phase */ > if (xml) > diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c > index fec808ccfb..e9ce2d8b8b 100644 > --- a/src/qemu/qemu_migration.c > +++ b/src/qemu/qemu_migration.c > @@ -2870,6 +2870,23 @@ qemuMigrationSrcBeginPhase(virQEMUDriver *driver, > vm->newDef && !qemuDomainVcpuHotplugIsInOrder(vm->newDef))) > cookieFlags |= QEMU_MIGRATION_COOKIE_CPU_HOTPLUG; > > + /* Call hook to allow source host to prepare for migration */ > + if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) { > + g_autofree char *xml = NULL; > + int hookret; > + > + if (!(xml = qemuDomainDefFormatLive(driver, priv->qemuCaps, vm->def, > + priv->origCPU, false, true))) > + return NULL; This looks okay. > + > + hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, vm->def->name, > + VIR_HOOK_QEMU_OP_MIGRATE_OUTGOING, > VIR_HOOK_SUBOP_BEGIN, > + "source", xml, NULL); > + > + if (hookret < 0) > + return NULL; > + } > + > if (qemuBlockNodesEnsureActive(vm, vm->job->asyncJob) < 0) There's a trivial merge conflict with patches I've posted. > return NULL; With the changes: Reviewed-by: Peter Krempa <[email protected]>
