From: "Daniel P. Berrange" <berrange(a)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