[libvirt] [PATCH v2 3/3] libxl: add hooks support

Cédric Bosdonnat cbosdonnat at suse.com
Thu Jul 28 09:57:42 UTC 2016


Introduce libxl hook and use it for start, prepare, started,
stop, stopped, migrate events.
---
 docs/hooks.html.in          | 53 ++++++++++++++++++++++++++++++--
 src/libxl/libxl_domain.c    | 74 +++++++++++++++++++++++++++++++++++++++++++++
 src/libxl/libxl_driver.c    | 22 ++++++++++++++
 src/libxl/libxl_migration.c | 57 ++++++++++++++++++++++++++++++++++
 src/util/virhook.c          | 16 +++++++++-
 src/util/virhook.h          | 13 ++++++++
 6 files changed, 232 insertions(+), 3 deletions(-)

diff --git a/docs/hooks.html.in b/docs/hooks.html.in
index d4f4ac3..11073cb 100644
--- a/docs/hooks.html.in
+++ b/docs/hooks.html.in
@@ -17,8 +17,10 @@
           (<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>
@@ -41,7 +43,7 @@
     <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
@@ -50,6 +52,9 @@
           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>
@@ -235,6 +240,50 @@
       </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,
diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c
index 886b40f..d99f903 100644
--- a/src/libxl/libxl_domain.c
+++ b/src/libxl/libxl_domain.c
@@ -32,6 +32,7 @@
 #include "viratomic.h"
 #include "virfile.h"
 #include "virerror.h"
+#include "virhook.h"
 #include "virlog.h"
 #include "virstring.h"
 #include "virtime.h"
@@ -737,6 +738,17 @@ libxlDomainCleanup(libxlDriverPrivatePtr driver,
     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);
 
@@ -788,6 +800,17 @@ libxlDomainCleanup(libxlDriverPrivatePtr driver,
         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;
@@ -1107,6 +1130,23 @@ libxlDomainStart(libxlDriverPrivatePtr driver,
     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,
@@ -1135,6 +1175,23 @@ libxlDomainStart(libxlDriverPrivatePtr driver,
                                        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);
@@ -1215,6 +1272,23 @@ libxlDomainStart(libxlDriverPrivatePtr driver,
     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 :
diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
index 823bc1a..f500892 100644
--- a/src/libxl/libxl_driver.c
+++ b/src/libxl/libxl_driver.c
@@ -42,6 +42,7 @@
 #include "virfile.h"
 #include "viralloc.h"
 #include "viruuid.h"
+#include "virhook.h"
 #include "vircommand.h"
 #include "libxl_domain.h"
 #include "libxl_driver.h"
@@ -415,6 +416,27 @@ libxlReconnectDomain(virDomainObjPtr vm,
     /* 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:
diff --git a/src/libxl/libxl_migration.c b/src/libxl/libxl_migration.c
index bf285a4..8be7877 100644
--- a/src/libxl/libxl_migration.c
+++ b/src/libxl/libxl_migration.c
@@ -36,6 +36,7 @@
 #include "virstring.h"
 #include "virobject.h"
 #include "virthread.h"
+#include "virhook.h"
 #include "rpc/virnetsocket.h"
 #include "libxl_domain.h"
 #include "libxl_driver.h"
@@ -490,9 +491,11 @@ libxlDomainMigrationPrepare(virConnectPtr dconn,
                             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;
@@ -500,6 +503,8 @@ libxlDomainMigrationPrepare(virConnectPtr dconn,
     size_t nsocks = 0;
     int nsocks_listen = 0;
     libxlMigrationDstArgs *args = NULL;
+    bool taint_hook = false;
+    libxlDomainObjPrivatePtr priv = NULL;
     size_t i;
     int ret = -1;
 
@@ -513,6 +518,50 @@ libxlDomainMigrationPrepare(virConnectPtr dconn,
         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 |
@@ -520,6 +569,12 @@ libxlDomainMigrationPrepare(virConnectPtr dconn,
                                    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) {
@@ -640,6 +695,7 @@ libxlDomainMigrationPrepare(virConnectPtr dconn,
     }
 
  done:
+    VIR_FREE(xmlout);
     libxlMigrationCookieFree(mig);
     if (!uri_in)
         VIR_FREE(hostname);
@@ -647,6 +703,7 @@ libxlDomainMigrationPrepare(virConnectPtr dconn,
         virURIFree(uri);
     if (vm)
         virObjectUnlock(vm);
+    virObjectUnref(cfg);
     return ret;
 }
 
diff --git a/src/util/virhook.c b/src/util/virhook.c
index a8422a2..facd74a 100644
--- a/src/util/virhook.c
+++ b/src/util/virhook.c
@@ -51,13 +51,15 @@ VIR_ENUM_DECL(virHookSubop)
 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",
@@ -96,6 +98,15 @@ VIR_ENUM_IMPL(virHookNetworkOp, VIR_HOOK_NETWORK_OP_LAST,
               "unplugged",
               "updated")
 
+VIR_ENUM_IMPL(virHookLibxlOp, VIR_HOOK_LIBXL_OP_LAST,
+              "start",
+              "stopped",
+              "prepare",
+              "release",
+              "migrate",
+              "started",
+              "reconnect")
+
 static int virHooksFound = -1;
 
 /**
@@ -261,6 +272,9 @@ virHookCall(int driver,
         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);
     }
diff --git a/src/util/virhook.h b/src/util/virhook.h
index 4015426..205249c 100644
--- a/src/util/virhook.h
+++ b/src/util/virhook.h
@@ -31,6 +31,7 @@ typedef enum {
     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;
@@ -87,6 +88,18 @@ typedef enum {
     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);
-- 
2.6.6




More information about the libvir-list mailing list