[libvirt] [PATCH] Add some missing hook functions

From: "Daniel P. Berrange" <berrange@redhat.com> A core use case of the hook scripts is to be able to do things to a guest's network configuration. It is possible to hook into the 'start' operation for a QEMU guest which runs just before the guest is started. The TAP devices will exist at this point, but the QEMU process will not. It can be desirable to have a 'started' hook too, which runs once QEMU has started. If libvirtd is restarted it will re-populate firewall rules, but there is no QEMU hook to trigger for existing domains. This is solved with a 'reconnect' hook. Finally, if attaching to an external QEMU process there needs to be an 'attach' hook script. This all also applies to the LXC driver * docs/hooks.html.in: Document new operations * src/util/hooks.c, src/util/hooks.c: Add 'started', 'reconnect' and 'attach' operations for QEMU. Add 'prepare', 'started', 'release' and 'reconnect' operations for LXC * src/lxc/lxc_driver.c: Add hooks for 'prepare', 'started', 'release' and 'reconnect' operations * src/qemu/qemu_process.c: Add hooks for 'started', 'reconnect' and 'reconnect' operations --- docs/hooks.html.in | 52 +++++++++++++++++++++++--- src/lxc/lxc_driver.c | 94 ++++++++++++++++++++++++++++++++++++++--------- src/qemu/qemu_process.c | 51 +++++++++++++++++++++++++ src/util/hooks.c | 11 +++++- src/util/hooks.h | 7 ++++ 5 files changed, 190 insertions(+), 25 deletions(-) diff --git a/docs/hooks.html.in b/docs/hooks.html.in index ab16db2..2d64d42 100644 --- a/docs/hooks.html.in +++ b/docs/hooks.html.in @@ -101,7 +101,7 @@ <h5><a name="qemu">/etc/libvirt/hooks/qemu</a></h5> <ul> <li>Before a QEMU guest is started, the qemu hook script is - called in two locations; if either location fails, the guest + called in three locations; if either location fails, the guest is not started. The first location, <span class="since">since 0.9.0</span>, is before libvirt performs any resource labeling, and the hook can allocate resources not managed by @@ -110,7 +110,11 @@ The second location, available <span class="since">Since 0.8.0</span>, occurs after libvirt has finished labeling all resources, but has not yet started the guest, called as:<br/> - <pre>/etc/libvirt/hooks/qemu guest_name start begin -</pre></li> + <pre>/etc/libvirt/hooks/qemu guest_name start begin -</pre> + The third location, <span class="since">0.9.13</span>, + occurs after the QEMU process has successfully started up:<br/> + <pre>/etc/libvirt/hooks/qemu guest_name started begin -</pre> + </li> <li>When a QEMU guest is stopped, the qemu hook script is called in two locations, to match the startup. First, <span class="since">since 0.8.0</span>, the hook is @@ -130,15 +134,51 @@ script returns failure or the output XML is not valid, incoming migration will be canceled. This hook may be used, e.g., to change location of disk images for incoming domains.</li> + <li><span class="since">Since 0.9.13</span>, the qemu hook script + is also called when the libvirtd daemon restarts and reconnects + to previously running QEMU processes. If the script fails, the + existing QEMU process will be killed off. It is called as: + <pre>/etc/libvirt/hooks/qemu guest_name reconnect begin -</pre> + </li> + <li><span class="since">Since 0.9.13</span>, the qemu hook script + is also called when the QEMU driver is told to attach to an + externally launched QEMU process. It is called as: + <pre>/etc/libvirt/hooks/qemu guest_name attach begin -</pre> + </li> </ul> <h5><a name="lxc">/etc/libvirt/hooks/lxc</a></h5> <ul> - <li>When an LXC guest is started, the lxc hook script is called as:<br/> - <pre>/etc/libvirt/hooks/lxc guest_name start begin -</pre></li> + <li>Before a LXC guest is started, the lxc hook script is + called in three locations; if either location fails, the guest + is not started. The first location, <span class="since">since + 0.9.13</span>, is before libvirt performs any resource + labeling, and the hook can allocate resources not managed by + libvirt such as DRBD or missing bridges. This is called as:<br/> + <pre>/etc/libvirt/hooks/lxc guest_name prepare begin -</pre> + The second location, available <span class="since">Since + 0.8.0</span>, occurs after libvirt has finished labeling + all resources, but has not yet started the guest, called as:<br/> + <pre>/etc/libvirt/hooks/lxc guest_name start begin -</pre> + The third location, <span class="since">0.9.13</span>, + occurs after the LXC process has successfully started up:<br/> + <pre>/etc/libvirt/hooks/lxc guest_name started begin -</pre> + </li> <li>When a LXC guest is stopped, the lxc hook script is called - as:<br/> - <pre>/etc/libvirt/hooks/lxc guest_name stopped end -</pre></li> + in two locations, to match the startup. + First, <span class="since">since 0.8.0</span>, the hook is + called before libvirt restores any labels:<br/> + <pre>/etc/libvirt/hooks/lxc guest_name stopped end -</pre> + Then, after libvirt has released all resources, the hook is + called again, <span class="since">since 0.9.0</span>, to allow + any additional resource cleanup:<br/> + <pre>/etc/libvirt/hooks/lxc guest_name release end -</pre></li> + <li><span class="since">Since 0.9.13</span>, the lxc hook script + is also called when the libvirtd daemon restarts and reconnects + to previously running LXC processes. If the script fails, the + existing LXC process will be killed off. It is called as: + <pre>/etc/libvirt/hooks/lxc guest_name reconnect begin -</pre> + </li> </ul> <br/> diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 9aea556..0669c17 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1152,6 +1152,17 @@ static void lxcVmCleanup(lxc_driver_t *driver, virCgroupFree(&cgroup); } + /* now that we know it's stopped call the hook if present */ + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) { + char *xml = virDomainDefFormat(vm->def, 0); + + /* we can't stop the operation even if the script raised an error */ + virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name, + VIR_HOOK_LXC_OP_RELEASE, VIR_HOOK_SUBOP_END, + NULL, xml, NULL); + VIR_FREE(xml); + } + if (vm->newDef) { virDomainDefFree(vm->def); vm->def = vm->newDef; @@ -1625,23 +1636,6 @@ lxcBuildControllerCmd(lxc_driver_t *driver, virCommandAddArgList(cmd, "--veth", veths[i], NULL); } - /* now that we know it is about to start call the hook if present */ - if (virHookPresent(VIR_HOOK_DRIVER_LXC)) { - char *xml = virDomainDefFormat(vm->def, 0); - int hookret; - - hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name, - VIR_HOOK_LXC_OP_START, VIR_HOOK_SUBOP_BEGIN, - NULL, xml, NULL); - VIR_FREE(xml); - - /* - * If the script raised an error abort the launch - */ - if (hookret < 0) - goto cleanup; - } - virCommandPreserveFD(cmd, handshakefd); return cmd; @@ -1803,6 +1797,23 @@ static int lxcVmStart(virConnectPtr conn, if (virDomainObjSetDefTransient(driver->caps, vm, true) < 0) goto cleanup; + /* Run an early hook to set-up missing devices */ + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) { + char *xml = virDomainDefFormat(vm->def, 0); + int hookret; + + hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name, + VIR_HOOK_LXC_OP_PREPARE, VIR_HOOK_SUBOP_BEGIN, + NULL, xml, NULL); + VIR_FREE(xml); + + /* + * If the script raised an error abort the launch + */ + if (hookret < 0) + goto cleanup; + } + /* Here we open all the PTYs we need on the host OS side. * The LXC controller will open the guest OS side PTYs * and forward I/O between them. @@ -1887,6 +1898,23 @@ static int lxcVmStart(virConnectPtr conn, virCommandSetOutputFD(cmd, &logfd); virCommandSetErrorFD(cmd, &logfd); + /* now that we know it is about to start call the hook if present */ + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) { + char *xml = virDomainDefFormat(vm->def, 0); + int hookret; + + hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name, + VIR_HOOK_LXC_OP_START, VIR_HOOK_SUBOP_BEGIN, + NULL, xml, NULL); + VIR_FREE(xml); + + /* + * If the script raised an error abort the launch + */ + if (hookret < 0) + goto cleanup; + } + /* Log timestamp */ if ((timestamp = virTimeStringNow()) == NULL) { virReportOOMError(); @@ -1965,6 +1993,23 @@ static int lxcVmStart(virConnectPtr conn, if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) goto error; + /* finally we can call the 'started' hook script if any */ + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) { + char *xml = virDomainDefFormat(vm->def, 0); + int hookret; + + hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name, + VIR_HOOK_LXC_OP_STARTED, VIR_HOOK_SUBOP_BEGIN, + NULL, xml, NULL); + VIR_FREE(xml); + + /* + * If the script raised an error abort the launch + */ + if (hookret < 0) + goto error; + } + rc = 0; cleanup: @@ -2512,6 +2557,21 @@ lxcReconnectVM(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque) if (virSecurityManagerReserveLabel(driver->securityManager, vm->def, vm->pid) < 0) goto error; + + /* now that we know it's reconnected call the hook if present */ + if (virHookPresent(VIR_HOOK_DRIVER_LXC)) { + char *xml = virDomainDefFormat(vm->def, 0); + int hookret; + + /* we can't stop the operation even if the script raised an error */ + hookret = virHookCall(VIR_HOOK_DRIVER_LXC, vm->def->name, + VIR_HOOK_LXC_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN, + NULL, xml, NULL); + VIR_FREE(xml); + if (hookret < 0) + goto error; + } + } else { vm->def->id = -1; VIR_FORCE_CLOSE(priv->monitor); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 58ba5bf..b79944f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3108,6 +3108,23 @@ qemuProcessReconnect(void *opaque) if (virDomainSaveStatus(driver->caps, driver->stateDir, obj) < 0) goto error; + /* Run an hook to allow admins to do some magic */ + if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) { + char *xml = qemuDomainDefFormatXML(driver, obj->def, 0, false); + int hookret; + + hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, obj->def->name, + VIR_HOOK_QEMU_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN, + NULL, xml, NULL); + VIR_FREE(xml); + + /* + * If the script raised an error abort the launch + */ + if (hookret < 0) + goto error; + } + if (obj->def->id >= driver->nextvmid) driver->nextvmid = obj->def->id + 1; @@ -3747,6 +3764,23 @@ int qemuProcessStart(virConnectPtr conn, if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) goto cleanup; + /* finally we can call the 'started' hook script if any */ + if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) { + char *xml = qemuDomainDefFormatXML(driver, vm->def, 0, false); + int hookret; + + hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, vm->def->name, + VIR_HOOK_QEMU_OP_STARTED, VIR_HOOK_SUBOP_BEGIN, + NULL, xml, NULL); + VIR_FREE(xml); + + /* + * If the script raised an error abort the launch + */ + if (hookret < 0) + goto cleanup; + } + virCommandFree(cmd); VIR_FORCE_CLOSE(logfile); @@ -4255,6 +4289,23 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) goto cleanup; + /* Run an hook to allow admins to do some magic */ + if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) { + char *xml = qemuDomainDefFormatXML(driver, vm->def, 0, false); + int hookret; + + hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, vm->def->name, + VIR_HOOK_QEMU_OP_ATTACH, VIR_HOOK_SUBOP_BEGIN, + NULL, xml, NULL); + VIR_FREE(xml); + + /* + * If the script raised an error abort the launch + */ + if (hookret < 0) + goto cleanup; + } + VIR_FORCE_CLOSE(logfile); VIR_FREE(seclabel); diff --git a/src/util/hooks.c b/src/util/hooks.c index ce60b43..f89a40f 100644 --- a/src/util/hooks.c +++ b/src/util/hooks.c @@ -74,11 +74,18 @@ VIR_ENUM_IMPL(virHookQemuOp, VIR_HOOK_QEMU_OP_LAST, "stopped", "prepare", "release", - "migrate") + "migrate", + "started", + "reconnect", + "attach") VIR_ENUM_IMPL(virHookLxcOp, VIR_HOOK_LXC_OP_LAST, "start", - "stopped") + "stopped", + "prepare", + "release", + "started", + "reconnect") static int virHooksFound = -1; diff --git a/src/util/hooks.h b/src/util/hooks.h index 7fd29f6..1af7c04 100644 --- a/src/util/hooks.h +++ b/src/util/hooks.h @@ -57,6 +57,9 @@ enum virHookQemuOpType { VIR_HOOK_QEMU_OP_PREPARE, /* domain startup initiated */ VIR_HOOK_QEMU_OP_RELEASE, /* domain destruction is over */ VIR_HOOK_QEMU_OP_MIGRATE, /* domain is being migrated */ + VIR_HOOK_QEMU_OP_STARTED, /* domain has started */ + VIR_HOOK_QEMU_OP_RECONNECT, /* domain is being reconnected by libvirt */ + VIR_HOOK_QEMU_OP_ATTACH, /* domain is being attached to be libvirt */ VIR_HOOK_QEMU_OP_LAST, }; @@ -64,6 +67,10 @@ enum virHookQemuOpType { enum virHookLxcOpType { VIR_HOOK_LXC_OP_START, /* domain is about to start */ VIR_HOOK_LXC_OP_STOPPED, /* domain has stopped */ + VIR_HOOK_LXC_OP_PREPARE, /* domain startup initiated */ + VIR_HOOK_LXC_OP_RELEASE, /* domain destruction is over */ + VIR_HOOK_LXC_OP_STARTED, /* domain has started */ + VIR_HOOK_LXC_OP_RECONNECT, /* domain is being reconnected by libvirt */ VIR_HOOK_LXC_OP_LAST, }; -- 1.7.10.1

On 05/28/2012 08:20 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
A core use case of the hook scripts is to be able to do things to a guest's network configuration. It is possible to hook into the 'start' operation for a QEMU guest which runs just before the guest is started. The TAP devices will exist at this point, but the QEMU process will not. It can be desirable to have a 'started' hook too, which runs once QEMU has started.
If libvirtd is restarted it will re-populate firewall rules, but there is no QEMU hook to trigger for existing domains. This is solved with a 'reconnect' hook.
Finally, if attaching to an external QEMU process there needs to be an 'attach' hook script.
This all also applies to the LXC driver
* docs/hooks.html.in: Document new operations * src/util/hooks.c, src/util/hooks.c: Add 'started', 'reconnect' and 'attach' operations for QEMU. Add 'prepare', 'started', 'release' and 'reconnect' operations for LXC * src/lxc/lxc_driver.c: Add hooks for 'prepare', 'started', 'release' and 'reconnect' operations * src/qemu/qemu_process.c: Add hooks for 'started', 'reconnect' and 'reconnect' operations --- docs/hooks.html.in | 52 +++++++++++++++++++++++--- src/lxc/lxc_driver.c | 94 ++++++++++++++++++++++++++++++++++++++--------- src/qemu/qemu_process.c | 51 +++++++++++++++++++++++++ src/util/hooks.c | 11 +++++- src/util/hooks.h | 7 ++++ 5 files changed, 190 insertions(+), 25 deletions(-)
diff --git a/docs/hooks.html.in b/docs/hooks.html.in index ab16db2..2d64d42 100644 --- a/docs/hooks.html.in +++ b/docs/hooks.html.in @@ -101,7 +101,7 @@ <h5><a name="qemu">/etc/libvirt/hooks/qemu</a></h5> <ul> <li>Before a QEMU guest is started, the qemu hook script is - called in two locations; if either location fails, the guest + called in three locations; if either location fails, the guest
s/either/any/
<h5><a name="lxc">/etc/libvirt/hooks/lxc</a></h5> <ul> - <li>When an LXC guest is started, the lxc hook script is called as:<br/> - <pre>/etc/libvirt/hooks/lxc guest_name start begin -</pre></li> + <li>Before a LXC guest is started, the lxc hook script is + called in three locations; if either location fails, the guest
and again ACK with those nits fixed. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 05/28/2012 10:20 AM, Daniel P. Berrange wrote:
From: "Daniel P. Berrange" <berrange@redhat.com>
A core use case of the hook scripts is to be able to do things to a guest's network configuration. It is possible to hook into the 'start' operation for a QEMU guest which runs just before the guest is started. The TAP devices will exist at this point, but the QEMU process will not. It can be desirable to have a 'started' hook too, which runs once QEMU has started.
If libvirtd is restarted it will re-populate firewall rules, but there is no QEMU hook to trigger for existing domains. This is solved with a 'reconnect' hook.
As a test of its usefulness, I applied this patch to my tree and tried it out with a modified version of http://wiki.libvirt.org/page/Networking#Forwarding_Incoming_Connections which deletes and re-adds the DNAT and FORWARD rules when libvirtd is restarted. As expected, the port forwarding to the guest continues to work across restarts of libvirtd, so having these new hooks is a definite win. I also applied the final patch in Stefan's dhcp snooping series, and checked out the xml handed to the hook script for presence of the guest's learned IP address. It turns out that (for Fedora 14 guest anyway) the IP address *is* present in the domain XML by the time the "started" hook is called, but I believe this is just a coincidence. As I mentioned in the thread about libvirt-1.0, I think it would be really useful for odd networking setups if there was a hook that was called any time something notable in the network setup of a guest changed (such as a new DHCP lease acquisition being detected) (really, in the general sense, it would be nice to have a hook called any time something happened which made any actionable change in the contents of a dumpxml of the domain). None of that should detract from the usefulness of this patch as it stands, though - it definitely has my "functional ACK" :-)
participants (3)
-
Daniel P. Berrange
-
Eric Blake
-
Laine Stump