From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list