(<span class="since">since 0.8.0</span>)<br/><br/></li>
<li>A QEMU guest is started or stopped
(<span class="since">since 0.8.0</span>)<br/><br/></li>
- <li>An LXC guest is started or stopped
+ <li>An LXC guest is started or stopped
(<span class="since">since 0.8.0</span>)<br/><br/></li>
+ <li>A libxl-handled Xen guest is started or stopped
+ (<span class="since">since 2.1.0</span>)<br/><br/></li>
<li>A network is started or stopped or an interface is
plugged/unplugged to/from the network
(<span class="since">since 1.2.2</span>)<br/><br/></li>
<br/>
<h2><a name="names">Script names</a></h2>
- <p>At present, there are three hook scripts that can be called:</p>
+ <p>At present, there are five hook scripts that can be called:</p>
<ul>
<li><code>/etc/libvirt/hooks/daemon</code><br/><br/>
Executed when the libvirt daemon is started, stopped, or reloads
Executed when a QEMU guest is started, stopped, or migrated<br/><br/></li>
<li><code>/etc/libvirt/hooks/lxc</code><br /><br/>
Executed when an LXC guest is started or stopped</li>
+ <li><code>/etc/libvirt/hooks/libxl</code><br/><br/>
+ Executed when a libxl-handled Xen guest is started, stopped, or
+ migrated<br/><br/></li>
<li><code>/etc/libvirt/hooks/network</code><br/><br/>
Executed when a network is started or stopped or an
interface is plugged/unplugged to/from the network</li>
</li>
</ul>
+ <h5><a name="libxl">/etc/libvirt/hooks/libxl</a></h5>
+ <ul>
+ <li>Before a Xen guest is started using libxl driver, the libxl hook
+ script is called in three locations; if any location fails, the guest
+ is not started. The first location, <span class="since">since
+ 2.1.0</span>, is before libvirt performs any resource
+ labeling, and the hook can allocate resources not managed by
+ libvirt. This is called as:<br/>
+ <pre>/etc/libvirt/hooks/libxl guest_name prepare begin -</pre>
+ The second location, available <span class="since">Since
+ 2.1.0</span>, occurs after libvirt has finished labeling
+ all resources, but has not yet started the guest, called as:<br/>
+ <pre>/etc/libvirt/hooks/libxl guest_name start begin -</pre>
+ The third location, <span class="since">2.1.0</span>,
+ occurs after the domain has successfully started up:<br/>
+ <pre>/etc/libvirt/hooks/libxl guest_name started begin -</pre>
+ </li>
+ <li>When a libxl-handled Xen guest is stopped, the libxl hook script
+ is called in two locations, to match the startup.
+ First, <span class="since">since 2.1.0</span>, the hook is
+ called before libvirt restores any labels:<br/>
+ <pre>/etc/libvirt/hooks/libxl guest_name stopped end -</pre>
+ Then, after libvirt has released all resources, the hook is
+ called again, <span class="since">since 2.1.0</span>, to allow
+ any additional resource cleanup:<br/>
+ <pre>/etc/libvirt/hooks/libxl guest_name release end -</pre></li>
+ <li><span class="since">Since 2.1.0</span>, the libxl hook script
+ is also called at the beginning of incoming migration. It is called
+ as: <pre>/etc/libvirt/hooks/libxl guest_name migrate begin -</pre>
+ with domain XML sent to standard input of the script. In this case,
+ the script acts as a filter and is supposed to modify the domain
+ XML and print it out on its standard output. Empty output is
+ identical to copying the input XML without changing it. In case the
+ 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 2.1.0</span>, the libxl hook script
+ is also called when the libvirtd daemon restarts and reconnects
+ to previously running Xen domains. If the script fails, the
+ existing Xen domains will be killed off. It is called as:
+ <pre>/etc/libvirt/hooks/libxl guest_name reconnect begin -</pre>
+ </li>
+ </ul>
+
<h5><a name="network">/etc/libvirt/hooks/network</a></h5>
<ul>
<li><span class="since">Since 1.2.2</span>, before a network is started,
#include "viratomic.h"
#include "virfile.h"
#include "virerror.h"
+#include "virhook.h"
#include "virlog.h"
#include "virstring.h"
#include "virtime.h"
hostdev_flags |= VIR_HOSTDEV_SP_USB;
#endif
+ /* now that we know it's stopped call the hook if present */
+ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL)) {
+ char *xml = virDomainDefFormat(vm->def, cfg->caps, 0);
+
+ /* we can't stop the operation even if the script raised an error */
+ ignore_value(virHookCall(VIR_HOOK_DRIVER_LIBXL, vm->def->name,
+ VIR_HOOK_LIBXL_OP_STOPPED, VIR_HOOK_SUBOP_END,
+ NULL, xml, NULL));
+ VIR_FREE(xml);
+ }
+
virHostdevReAttachDomainDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
vm->def, hostdev_flags, NULL);
VIR_FREE(file);
}
+ /* The "release" hook cleans up additional resources */
+ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL)) {
+ char *xml = virDomainDefFormat(vm->def, cfg->caps, 0);
+
+ /* we can't stop the operation even if the script raised an error */
+ ignore_value(virHookCall(VIR_HOOK_DRIVER_LIBXL, vm->def->name,
+ VIR_HOOK_LIBXL_OP_RELEASE, VIR_HOOK_SUBOP_END,
+ NULL, xml, NULL);
+ VIR_FREE(xml);
+ }
+
if (vm->newDef) {
virDomainDefFree(vm->def);
vm->def = vm->newDef;
if (virDomainObjSetDefTransient(cfg->caps, driver->xmlopt, vm) < 0)
goto cleanup;
+ /* Run an early hook to set-up missing devices */
+ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL)) {
+ char *xml = virDomainDefFormat(vm->def, cfg->caps, 0);
+ int hookret;
+
+ hookret = virHookCall(VIR_HOOK_DRIVER_LIBXL, vm->def->name,
+ VIR_HOOK_LIBXL_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_dom;
+ }
+
if (virDomainLockProcessStart(driver->lockManager,
"xen:///system",
vm,
vm->def, hostdev_flags) < 0)
goto cleanup_dom;
+ /* now that we know it is about to start call the hook if present */
+ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL)) {
+ char *xml = virDomainDefFormat(vm->def, cfg->caps, 0);
+ int hookret;
+
+ hookret = virHookCall(VIR_HOOK_DRIVER_LIBXL, vm->def->name,
+ VIR_HOOK_LIBXL_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_dom;
+ }
+
if (priv->hookRun) {
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(vm->def->uuid, uuidstr);
if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
driver->inhibitCallback(true, driver->inhibitOpaque);
+ /* finally we can call the 'started' hook script if any */
+ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL)) {
+ char *xml = virDomainDefFormat(vm->def, cfg->caps, 0);
+ int hookret;
+
+ hookret = virHookCall(VIR_HOOK_DRIVER_LIBXL, vm->def->name,
+ VIR_HOOK_LIBXL_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_dom;
+ }
+
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED,
restore_fd < 0 ?
VIR_DOMAIN_EVENT_STARTED_BOOTED :
#include "virfile.h"
#include "viralloc.h"
#include "viruuid.h"
+#include "virhook.h"
#include "vircommand.h"
#include "libxl_domain.h"
#include "libxl_driver.h"
/* Enable domain death events */
libxl_evenable_domain_death(cfg->ctx, vm->def->id, 0, &priv->deathW);
+ /* now that we know it's reconnected call the hook if present */
+ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL) &&
+ STRNEQ("Domain-0", vm->def->name)) {
+ char *xml = virDomainDefFormat(vm->def, cfg->caps, 0);
+ int hookret;
+
+ /* we can't stop the operation even if the script raised an error */
+ hookret = virHookCall(VIR_HOOK_DRIVER_LIBXL, vm->def->name,
+ VIR_HOOK_LIBXL_OP_RECONNECT, VIR_HOOK_SUBOP_BEGIN,
+ NULL, xml, NULL);
+ VIR_FREE(xml);
+ if (hookret < 0) {
+ /* Stop the domain if the hook failed */
+ if (virDomainObjIsActive(vm)) {
+ libxlDomainDestroyInternal(driver, vm);
+ virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_FAILED);
+ }
+ goto error;
+ }
+ }
+
ret = 0;
cleanup:
#include "virstring.h"
#include "virobject.h"
#include "virthread.h"
+#include "virhook.h"
#include "rpc/virnetsocket.h"
#include "libxl_domain.h"
#include "libxl_driver.h"
unsigned int flags)
{
libxlDriverPrivatePtr driver = dconn->privateData;
+ libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
libxlMigrationCookiePtr mig = NULL;
virDomainObjPtr vm = NULL;
char *hostname = NULL;
+ char *xmlout = NULL;
unsigned short port;
char portstr[100];
virURIPtr uri = NULL;
size_t nsocks = 0;
int nsocks_listen = 0;
libxlMigrationDstArgs *args = NULL;
+ bool taint_hook = false;
+ libxlDomainObjPrivatePtr priv = NULL;
size_t i;
int ret = -1;
goto error;
}
+ /* Let migration hook filter domain XML */
+ if (virHookPresent(VIR_HOOK_DRIVER_LIBXL)) {
+ char *xml;
+ int hookret;
+
+ if (!(xml = virDomainDefFormat(*def, cfg->caps,
+ VIR_DOMAIN_XML_SECURE |
+ VIR_DOMAIN_XML_MIGRATABLE)))
+ goto error;
+
+ hookret = virHookCall(VIR_HOOK_DRIVER_LIBXL, (*def)->name,
+ VIR_HOOK_LIBXL_OP_MIGRATE, VIR_HOOK_SUBOP_BEGIN,
+ NULL, xml, &xmlout);
+ VIR_FREE(xml);
+
+ if (hookret < 0) {
+ goto error;
+ } else if (hookret == 0) {
+ if (virStringIsEmpty(xmlout)) {
+ VIR_DEBUG("Migrate hook filter returned nothing; using the"
+ " original XML");
+ } else {
+ virDomainDefPtr newdef;
+
+ VIR_DEBUG("Using hook-filtered domain XML: %s", xmlout);
+ newdef = virDomainDefParseString(xmlout, cfg->caps, driver->xmlopt,
+ VIR_DOMAIN_DEF_PARSE_INACTIVE |
+ VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
+ if (!newdef)
+ goto error;
+
+ /* TODO At some stage we will want to have some check of what the user
+ * did in the hook. */
+
+ virDomainDefFree(*def);
+ *def = newdef;
+ /* We should taint the domain here. However, @vm and therefore
+ * privateData too are still NULL, so just notice the fact and
+ * taint it later. */
+ taint_hook = true;
+ }
+ }
+ }
+
if (!(vm = virDomainObjListAdd(driver->domains, *def,
driver->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
NULL)))
goto error;
*def = NULL;
+ priv = vm->privateData;
+
+ if (taint_hook) {
+ /* Domain XML has been altered by a hook script. */
+ priv->hookRun = true;
+ }
/* Create socket connection to receive migration data */
if (!uri_in) {
}
done:
+ VIR_FREE(xmlout);
libxlMigrationCookieFree(mig);
if (!uri_in)
VIR_FREE(hostname);
virURIFree(uri);
if (vm)
virObjectUnlock(vm);
+ virObjectUnref(cfg);
return ret;
}
VIR_ENUM_DECL(virHookQemuOp)
VIR_ENUM_DECL(virHookLxcOp)
VIR_ENUM_DECL(virHookNetworkOp)
+VIR_ENUM_DECL(virHookLibxlOp)
VIR_ENUM_IMPL(virHookDriver,
VIR_HOOK_DRIVER_LAST,
"daemon",
"qemu",
"lxc",
- "network")
+ "network",
+ "libxl")
VIR_ENUM_IMPL(virHookDaemonOp, VIR_HOOK_DAEMON_OP_LAST,
"start",
"unplugged",
"updated")
+VIR_ENUM_IMPL(virHookLibxlOp, VIR_HOOK_LIBXL_OP_LAST,
+ "start",
+ "stopped",
+ "prepare",
+ "release",
+ "migrate",
+ "started",
+ "reconnect")
+
static int virHooksFound = -1;
/**
case VIR_HOOK_DRIVER_LXC:
opstr = virHookLxcOpTypeToString(op);
break;
+ case VIR_HOOK_DRIVER_LIBXL:
+ opstr = virHookLibxlOpTypeToString(op);
+ break;
case VIR_HOOK_DRIVER_NETWORK:
opstr = virHookNetworkOpTypeToString(op);
}
VIR_HOOK_DRIVER_QEMU, /* QEmu domains related events */
VIR_HOOK_DRIVER_LXC, /* LXC domains related events */
VIR_HOOK_DRIVER_NETWORK, /* network related events */
+ VIR_HOOK_DRIVER_LIBXL, /* Xen libxl domains related events */
VIR_HOOK_DRIVER_LAST,
} virHookDriverType;
VIR_HOOK_NETWORK_OP_LAST,
} virHookNetworkOpType;
+typedef enum {
+ VIR_HOOK_LIBXL_OP_START, /* domain is about to start */
+ VIR_HOOK_LIBXL_OP_STOPPED, /* domain has stopped */
+ VIR_HOOK_LIBXL_OP_PREPARE, /* domain startup initiated */
+ VIR_HOOK_LIBXL_OP_RELEASE, /* domain destruction is over */
+ VIR_HOOK_LIBXL_OP_MIGRATE, /* domain is being migrated */
+ VIR_HOOK_LIBXL_OP_STARTED, /* domain has started */
+ VIR_HOOK_LIBXL_OP_RECONNECT, /* domain is being reconnected by libvirt */
+
+ VIR_HOOK_LIBXL_OP_LAST,
+} virHookLibxlOpType;
+
int virHookInitialize(void);
int virHookPresent(int driver);