[libvirt] [PATCH 04/16] Pass graphics setup from dst back to src via migration cookies

Daniel P. Berrange berrange at redhat.com
Wed May 11 09:09:50 UTC 2011


Extend the QEMU migration cookie structure to allow information
about the destination host graphics setup to be passed by to
the source host. This will enable seamless migration of any
connected graphics clients

* src/qemu/qemu_migration.c: Add graphics info to migration
  cookies
* daemon/libvirtd.c: Always initialize gnutls to enable
  x509 cert parsing in QEMU
---
 cfg.mk                    |    1 +
 daemon/libvirtd.c         |    8 +-
 src/qemu/qemu_migration.c |  264 +++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 261 insertions(+), 12 deletions(-)

diff --git a/cfg.mk b/cfg.mk
index b142b6d..aed6a8f 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -81,6 +81,7 @@ useless_free_options =				\
   --name=VIR_FREE				\
   --name=qemuCapsFree				\
   --name=qemuMigrationCookieFree                \
+  --name=qemuMigrationCookieGraphicsFree        \
   --name=sexpr_free				\
   --name=virBitmapFree                          \
   --name=virCPUDefFree				\
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index 42cbe5d..ae04078 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -317,9 +317,6 @@ remoteInitializeGnuTLS (void)
 {
     int err;
 
-    /* Initialise GnuTLS. */
-    gnutls_global_init ();
-
     err = gnutls_certificate_allocate_credentials (&x509_cred);
     if (err) {
         VIR_ERROR(_("gnutls_certificate_allocate_credentials: %s"),
@@ -3310,6 +3307,11 @@ int main(int argc, char **argv) {
         goto error;
     }
 
+    /* Initialise GnuTLS. Required even if we don't use TLS
+     * for libvirtd, because QEMU driver needs to be able to
+     * parse x590 certificates for seemless migration */
+    gnutls_global_init();
+
     if (!(server = qemudInitialize())) {
         ret = VIR_DAEMON_ERR_INIT;
         goto error;
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 594100c..5fc09f7 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -47,6 +47,20 @@
 
 #define timeval_to_ms(tv)       (((tv).tv_sec * 1000ull) + ((tv).tv_usec / 1000))
 
+enum qemuMigrationCookieFlags {
+    QEMU_MIGRATION_COOKIE_GRAPHICS = (1 << 0),
+};
+
+typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics;
+typedef qemuMigrationCookieGraphics *qemuMigrationCookieGraphicsPtr;
+struct _qemuMigrationCookieGraphics {
+    int type;
+    int port;
+    int tlsPort;
+    char *listen;
+    char *tlsSubject;
+};
+
 typedef struct _qemuMigrationCookie qemuMigrationCookie;
 typedef qemuMigrationCookie *qemuMigrationCookiePtr;
 struct _qemuMigrationCookie {
@@ -59,20 +73,142 @@ struct _qemuMigrationCookie {
     /* Guest properties */
     unsigned char uuid[VIR_UUID_BUFLEN];
     char *name;
+
+    /* If (flags & QEMU_MIGRATION_COOKIE_GRAPHICS) */
+    qemuMigrationCookieGraphicsPtr graphics;
 };
 
+static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
+{
+    if (!grap)
+        return;
+    VIR_FREE(grap->listen);
+    VIR_FREE(grap->tlsSubject);
+    VIR_FREE(grap);
+}
+
 
 static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
 {
     if (!mig)
         return;
 
+    if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS)
+        qemuMigrationCookieGraphicsFree(mig->graphics);
+
     VIR_FREE(mig->hostname);
     VIR_FREE(mig->name);
     VIR_FREE(mig);
 }
 
 
+static char *
+qemuDomainExtractTLSSubject(const char *certdir)
+{
+    char *certfile = NULL;
+    char *subject = NULL;
+    char *pemdata = NULL;
+    gnutls_datum_t pemdatum;
+    gnutls_x509_crt_t cert;
+    int ret;
+    size_t subjectlen;
+
+    if (virAsprintf(&certfile, "%s/server-cert.pem", certdir) < 0)
+        goto no_memory;
+
+    if (virFileReadAll(certfile, 8192, &pemdata) < 0) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("unable to read server cert %s"), certfile);
+        goto error;
+    }
+
+    ret = gnutls_x509_crt_init(&cert);
+    if (ret < 0) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("cannot initialize cert object: %s"),
+                        gnutls_strerror(ret));
+        goto error;
+    }
+
+    pemdatum.data = (unsigned char *)pemdata;
+    pemdatum.size = strlen(pemdata);
+
+    ret = gnutls_x509_crt_import(cert, &pemdatum, GNUTLS_X509_FMT_PEM);
+    if (ret < 0) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("cannot load cert data from %s: %s"),
+                        certfile, gnutls_strerror(ret));
+        goto error;
+    }
+
+    subjectlen = 1024;
+    if (VIR_ALLOC_N(subject, subjectlen+1) < 0)
+        goto no_memory;
+
+    gnutls_x509_crt_get_dn(cert, subject, &subjectlen);
+    subject[subjectlen] = '\0';
+
+    VIR_FREE(certfile);
+    VIR_FREE(pemdata);
+
+    return subject;
+
+no_memory:
+    virReportOOMError();
+error:
+    VIR_FREE(certfile);
+    VIR_FREE(pemdata);
+    return NULL;
+}
+
+
+static qemuMigrationCookieGraphicsPtr
+qemuMigrationCookieGraphicsAlloc(struct qemud_driver *driver,
+                                 virDomainGraphicsDefPtr def)
+{
+    qemuMigrationCookieGraphicsPtr mig = NULL;
+    const char *listenAddr;
+
+    if (VIR_ALLOC(mig) < 0)
+        goto no_memory;
+
+    mig->type = def->type;
+    if (mig->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
+        mig->port = def->data.vnc.port;
+        listenAddr = def->data.vnc.listenAddr;
+        if (!listenAddr)
+            listenAddr = driver->vncListen;
+
+        if (driver->vncTLS &&
+            !(mig->tlsSubject = qemuDomainExtractTLSSubject(driver->vncTLSx509certdir)))
+            goto error;
+    } else {
+        mig->port = def->data.spice.port;
+        if (driver->spiceTLS)
+            mig->tlsPort = def->data.spice.tlsPort;
+        else
+            mig->tlsPort = -1;
+        listenAddr = def->data.spice.listenAddr;
+        if (!listenAddr)
+            listenAddr = driver->spiceListen;
+
+        if (driver->spiceTLS &&
+            !(mig->tlsSubject = qemuDomainExtractTLSSubject(driver->spiceTLSx509certdir)))
+            goto error;
+    }
+    if (!(mig->listen = strdup(listenAddr)))
+        goto no_memory;
+
+    return mig;
+
+no_memory:
+    virReportOOMError();
+error:
+    qemuMigrationCookieGraphicsFree(mig);
+    return NULL;
+}
+
+
 static qemuMigrationCookiePtr
 qemuMigrationCookieNew(virDomainObjPtr dom)
 {
@@ -103,6 +239,47 @@ error:
 }
 
 
+static int
+qemuMigrationCookieAddGraphics(qemuMigrationCookiePtr mig,
+                               struct qemud_driver *driver,
+                               virDomainObjPtr dom)
+{
+    if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("Migration graphics data already present"));
+        return -1;
+    }
+
+    if (dom->def->ngraphics == 1 &&
+        (dom->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC ||
+         dom->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) &&
+        !(mig->graphics = qemuMigrationCookieGraphicsAlloc(driver, dom->def->graphics[0])))
+        return -1;
+
+    mig->flags |= QEMU_MIGRATION_COOKIE_GRAPHICS;
+
+    return 0;
+}
+
+
+static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
+                                                 qemuMigrationCookieGraphicsPtr grap)
+{
+    virBufferAsprintf(buf, "  <graphics type='%s' port='%d' listen='%s'",
+                      virDomainGraphicsTypeToString(grap->type),
+                      grap->port, grap->listen);
+    if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
+        virBufferAsprintf(buf, " tlsPort='%d'", grap->tlsPort);
+    if (grap->tlsSubject) {
+        virBufferAddLit(buf, ">\n");
+        virBufferEscapeString(buf, "    <cert info='subject' value='%s'/>\n", grap->tlsSubject);
+        virBufferAddLit(buf, "  </graphics>\n");
+    } else {
+        virBufferAddLit(buf, "/>\n");
+    }
+}
+
+
 static void qemuMigrationCookieXMLFormat(virBufferPtr buf,
                                          qemuMigrationCookiePtr mig)
 {
@@ -117,6 +294,10 @@ static void qemuMigrationCookieXMLFormat(virBufferPtr buf,
     virBufferAsprintf(buf, "  <uuid>%s</uuid>\n", uuidstr);
     virBufferEscapeString(buf, "  <hostname>%s</hostname>\n", mig->hostname);
     virBufferAsprintf(buf, "  <hostuuid>%s</hostuuid>\n", hostuuidstr);
+
+    if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS)
+        qemuMigrationCookieGraphicsXMLFormat(buf, mig->graphics);
+
     virBufferAddLit(buf, "</qemu-migration>\n");
 }
 
@@ -136,10 +317,61 @@ static char *qemuMigrationCookieXMLFormatStr(qemuMigrationCookiePtr mig)
 }
 
 
+static qemuMigrationCookieGraphicsPtr
+qemuMigrationCookieGraphicsXMLParse(xmlXPathContextPtr ctxt)
+{
+    qemuMigrationCookieGraphicsPtr grap;
+    char *tmp;
+
+    if (VIR_ALLOC(grap) < 0)
+        goto no_memory;
+
+    if (!(tmp = virXPathString("string(./graphics/@type)", ctxt))) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        "%s", _("missing type attribute in migration data"));
+        goto error;
+    }
+    if ((grap->type = virDomainGraphicsTypeFromString(tmp)) < 0) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("unknown graphics type %s"), tmp);
+        VIR_FREE(tmp);
+        goto error;
+    }
+    if (virXPathInt("string(./graphics/@port)", ctxt, &grap->port) < 0) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        "%s", _("missing port attribute in migration data"));
+        goto error;
+    }
+    if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
+        if (virXPathInt("string(./graphics/@tlsPort)", ctxt, &grap->tlsPort) < 0) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            "%s", _("missing tlsPort attribute in migration data"));
+            goto error;
+        }
+    }
+    if (!(grap->listen = virXPathString("string(./graphics/@listen)", ctxt))) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        "%s", _("missing listen attribute in migration data"));
+        goto error;
+    }
+    /* Optional */
+    grap->tlsSubject = virXPathString("string(./graphics/cert[ info='subject']/@value)", ctxt);
+
+
+    return grap;
+
+no_memory:
+    virReportOOMError();
+error:
+    qemuMigrationCookieGraphicsFree(grap);
+    return NULL;
+}
+
+
 static int
 qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
                             xmlXPathContextPtr ctxt,
-                            int flags ATTRIBUTE_UNUSED)
+                            int flags)
 {
     char uuidstr[VIR_UUID_STRING_BUFLEN];
     char *tmp;
@@ -206,6 +438,11 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
     }
     VIR_FREE(tmp);
 
+    if ((flags & QEMU_MIGRATION_COOKIE_GRAPHICS) &&
+        virXPathBoolean("count(./graphics) > 0", ctxt) &&
+        (!(mig->graphics = qemuMigrationCookieGraphicsXMLParse(ctxt))))
+        goto error;
+
     return 0;
 
 error:
@@ -247,11 +484,11 @@ cleanup:
 
 static int
 qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
-                        struct qemud_driver *driver ATTRIBUTE_UNUSED,
-                        virDomainObjPtr dom ATTRIBUTE_UNUSED,
+                        struct qemud_driver *driver,
+                        virDomainObjPtr dom,
                         char **cookieout,
                         int *cookieoutlen,
-                        int flags ATTRIBUTE_UNUSED)
+                        int flags)
 {
     if (!cookieout || !cookieoutlen) {
         qemuReportError(VIR_ERR_INVALID_ARG, "%s",
@@ -261,6 +498,10 @@ qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
 
     *cookieoutlen = 0;
 
+    if (flags & QEMU_MIGRATION_COOKIE_GRAPHICS &&
+        qemuMigrationCookieAddGraphics(mig, driver, dom) < 0)
+        return -1;
+
     if (!(*cookieout = qemuMigrationCookieXMLFormatStr(mig)))
         return -1;
 
@@ -559,7 +800,8 @@ qemuMigrationPrepareTunnel(struct qemud_driver *driver,
     def = NULL;
     priv = vm->privateData;
 
-    if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen, 0)))
+    if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen,
+                                       QEMU_MIGRATION_COOKIE_GRAPHICS)))
         goto cleanup;
 
     if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
@@ -613,7 +855,8 @@ qemuMigrationPrepareTunnel(struct qemud_driver *driver,
                                      VIR_DOMAIN_EVENT_STARTED,
                                      VIR_DOMAIN_EVENT_STARTED_MIGRATED);
 
-    if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen, 0) < 0) {
+    if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen,
+                                QEMU_MIGRATION_COOKIE_GRAPHICS) < 0) {
         /* We could tear down the whole guest here, but
          * cookie data is (so far) non-critical, so that
          * seems a little harsh. We'll just warn for now..
@@ -787,7 +1030,8 @@ qemuMigrationPrepareDirect(struct qemud_driver *driver,
     def = NULL;
     priv = vm->privateData;
 
-    if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen, 0)))
+    if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen,
+                                       QEMU_MIGRATION_COOKIE_GRAPHICS)))
         goto cleanup;
 
     if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
@@ -815,7 +1059,8 @@ qemuMigrationPrepareDirect(struct qemud_driver *driver,
         goto endjob;
     }
 
-    if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen, 0) < 0) {
+    if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen,
+                                QEMU_MIGRATION_COOKIE_GRAPHICS) < 0) {
         /* We could tear down the whole guest here, but
          * cookie data is (so far) non-critical, so that
          * seems a little harsh. We'll just warn for now..
@@ -880,7 +1125,8 @@ static int doNativeMigrate(struct qemud_driver *driver,
     unsigned int background_flags = QEMU_MONITOR_MIGRATE_BACKGROUND;
     qemuMigrationCookiePtr mig = NULL;
 
-    if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen, 0)))
+    if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen,
+                                       QEMU_MIGRATION_COOKIE_GRAPHICS)))
         goto cleanup;
 
     /* Issue the migrate command. */
-- 
1.7.4.4




More information about the libvir-list mailing list