Add some missing hook functions

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
This commit is contained in:
Daniel P. Berrange 2012-05-28 15:04:31 +01:00
parent 5d490603a6
commit 6510c97bf5
5 changed files with 190 additions and 25 deletions

View File

@ -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 any 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 any 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/>

View File

@ -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);

View File

@ -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;
@ -3770,6 +3787,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);
@ -4280,6 +4314,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);

View File

@ -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;

View File

@ -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,
};