[Libvir] save/restore support for KVM

Jim Paris jim at jtan.com
Thu Aug 9 21:26:43 UTC 2007


Hi,

I've implemented save/restore support for KVM using the live migration
feature.  First, a few patches to KVM to fix bugs:

  Subject: [PATCH 1/3] qemu: fix freed pointer dereference
  http://article.gmane.org/gmane.comp.emulators.kvm.devel/5572

  Subject: [PATCH 2/3] qemu: don't start a new migration if one is already in progress
  http://article.gmane.org/gmane.comp.emulators.kvm.devel/5575

  Subject: [PATCH 3/3] qemu: reset buffer pointers after CR/LF
  http://article.gmane.org/gmane.comp.emulators.kvm.devel/5573

(If compatibility with old KVM is wanted, it might be possible to work
around the kvm bugs in other ways, but I'm not sure).

Then, another patch to KVM to support inbound migration from a
filename.  It already supports migration from stdin, but adding this
seemed easier from a libvirt perspective.

  Subject: [PATCH] qemu: accept filename for incoming migration
  http://article.gmane.org/gmane.comp.emulators.kvm.devel/5590

Finally, the libvirt side.  Some notes:

- Arbitrary decisions about VM status: I pause the VM before starting
  migration, and destroy it once it's finished.  Neither is required
  by KVM but I'd be concerned about the disk image state otherwise.
  Also, after resuming, the VM is still paused.  I don't know how Xen
  behaves in this regard but the behavior here is easy enough to
  change.

- I append the domain's UUID at the end of the migration image. 
  This doesn't affect KVM at all (it ignores the extra data).
  Does that seem reasonable?  It's unclear how the saved image
  is supposed to get associated with a particular VM configuration
  without doing something like this.

- I added the migrateFrom field to the qemu_vm struct.  Dunno
  if that's the best place.  Could also add put it in qemu_vm_def,
  or add parameters to qemudStartVMDaemon, etc..

Patch against current CVS is below.

Signed-off-by: Jim Paris <jim at jtan.com>

-jim

Index: src/qemu_conf.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_conf.c,v
retrieving revision 1.10
diff -u -r1.10 qemu_conf.c
--- src/qemu_conf.c	7 Aug 2007 13:02:35 -0000	1.10
+++ src/qemu_conf.c	9 Aug 2007 21:15:10 -0000
@@ -1518,7 +1518,8 @@
         (vm->def->os.initrd[0] ? 2 : 0) + /* initrd */
         (vm->def->os.cmdline[0] ? 2 : 0) + /* cmdline */
         (vm->def->graphicsType == QEMUD_GRAPHICS_VNC ? 2 :
-         (vm->def->graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)); /* graphics */
+         (vm->def->graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)) + /* graphics */
+        (vm->migrateFrom[0] ? 3 : 0); /* migrateFrom */
 
     snprintf(memory, sizeof(memory), "%d", vm->def->memory/1024);
     snprintf(vcpus, sizeof(vcpus), "%d", vm->def->vcpus);
@@ -1767,6 +1768,15 @@
         /* SDL is the default. no args needed */
     }
 
+    if (vm->migrateFrom[0]) {
+        if (!((*argv)[++n] = strdup("-S")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup("-incoming")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(vm->migrateFrom)))
+            goto no_memory;
+    }
+
     (*argv)[++n] = NULL;
 
     return 0;
Index: src/qemu_conf.h
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_conf.h,v
retrieving revision 1.6
diff -u -r1.6 qemu_conf.h
--- src/qemu_conf.h	30 Jul 2007 09:59:06 -0000	1.6
+++ src/qemu_conf.h	9 Aug 2007 21:15:10 -0000
@@ -213,6 +213,7 @@
 
     char configFile[PATH_MAX];
     char autostartLink[PATH_MAX];
+    char migrateFrom[PATH_MAX];
 
     struct qemud_vm_def *def; /* The current definition */
     struct qemud_vm_def *newDef; /* New definition to activate at shutdown */
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.14
diff -u -r1.14 qemu_driver.c
--- src/qemu_driver.c	30 Jul 2007 09:59:06 -0000	1.14
+++ src/qemu_driver.c	9 Aug 2007 21:15:10 -0000
@@ -656,7 +656,7 @@
 
     if (virExecNonBlock(conn, argv, &vm->pid, &vm->stdout, &vm->stderr) == 0) {
         vm->id = driver->nextvmid++;
-        vm->state = VIR_DOMAIN_RUNNING;
+        vm->state = vm->migrateFrom[0] ? VIR_DOMAIN_PAUSED : VIR_DOMAIN_RUNNING;
 
         driver->ninactivevms--;
         driver->nactivevms++;
@@ -1852,29 +1852,151 @@
     return 0;
 }
 
+#define QEMUD_SAVE_MAGIC "LibvirtQemudUUID"
 
-static int qemudDomainSave(virDomainPtr dom,
-                    const char *path ATTRIBUTE_UNUSED) {
+static int qemudDomainSave(virDomainPtr dom, 
+                           const char *path) {
     struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
     struct qemud_vm *vm = qemudFindVMByID(driver, dom->id);
+    char *command, *info;
+    int fd;
+    
     if (!vm) {
-        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", dom->id);
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "no domain with matching id %d", dom->id);
         return -1;
     }
+
     if (!qemudIsActiveVM(vm)) {
-        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "domain is not running");
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "domain is not running");
         return -1;
     }
-    qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, "save is not supported");
-    return -1;
+    
+    if (strchr(path, '\'') || strchr(path, '\\') ) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "invalid filename");
+        return -1;
+    }
+
+    /* Pause */
+    if (qemudDomainSuspend(dom) != 0) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "failed to pause domain");
+        return -1;
+    }
+
+    /* Migrate to file. */
+    if (asprintf (&command, "migrate \"exec:dd of='%s' 2>/dev/null\"\n",
+                  path) == -1) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, 
+                         "out of memory");
+        return -1;
+    }
+
+    if (qemudMonitorCommand(driver, vm, command, &info) < 0) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, 
+                         "migrate operation failed");
+        free(command);
+        return -1;
+    }
+
+    free(info);
+    free(command);
+
+    /* Append the UUID, which we'll need in order to restore the domain. */
+    if ((fd = open(path, O_APPEND|O_WRONLY)) < 0) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "failed to open '%s'", path);
+        return -1;
+    }
+
+    if (write(fd, QEMUD_SAVE_MAGIC, sizeof(QEMUD_SAVE_MAGIC)) !=
+        sizeof(QEMUD_SAVE_MAGIC)) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "failed to write magic");
+        close(fd);
+        return -1;
+    }
+
+    if (write(fd, dom->uuid, VIR_UUID_BUFLEN) != VIR_UUID_BUFLEN) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "failed to write uuid");
+        close(fd);
+        return -1;
+    }
+    close(fd);
+
+    /* Shut it down */
+    qemudShutdownVMDaemon(dom->conn, driver, vm);
+    if (!vm->configFile[0])
+        qemudRemoveInactiveVM(driver, vm);
+
+    return 0;
 }
 
 
 static int qemudDomainRestore(virConnectPtr conn,
-                       const char *path ATTRIBUTE_UNUSED) {
-    /*struct qemud_driver *driver = (struct qemud_driver *)conn->privateData;*/
-    qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, "restore is not supported");
-    return -1;
+                       const char *path) {
+    struct qemud_driver *driver = (struct qemud_driver *)conn->privateData;
+    unsigned char uuid[VIR_UUID_BUFLEN];
+    unsigned char magic[sizeof(QEMUD_SAVE_MAGIC)];
+    virDomainPtr dom;
+    struct qemud_vm *vm;
+    int fd;
+
+    /* Find the UUID */
+    if ((fd = open(path, O_RDONLY)) < 0) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         "cannot read domain image");
+        return -1;
+    }
+    if (lseek(fd, 0 - (sizeof(magic) + sizeof(uuid)), SEEK_END) == -1) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         "failed to seek to end of domain image");
+        close(fd);
+        return -1;
+    }        
+    if (read(fd, magic, sizeof(magic)) != sizeof(magic)) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         "failed to read domain image");
+        close(fd);
+        return -1;
+    }
+    if (memcmp(magic, QEMUD_SAVE_MAGIC, sizeof(magic))) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         "image magic is not correct");
+        close(fd);
+        return -1;
+    }
+    if (read(fd, uuid, sizeof(uuid)) != sizeof(uuid)) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         "failed to read domain UUID from image");
+        close(fd);
+        return -1;
+    }
+    close(fd);
+    
+    /* Find the domain & vm */
+    dom = qemudDomainLookupByUUID(conn, uuid);
+    if (!dom) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "no domain with matching uuid");
+        return -1;
+    }
+
+    vm = qemudFindVMByUUID(driver, dom->uuid);
+    if (!vm) {
+        qemudReportError(conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "no vm with matching uuid");
+        return -1;
+    }
+
+    /* Set the migration source and start it up. */
+    snprintf(vm->migrateFrom, sizeof(vm->migrateFrom), "file://%s", path);
+    vm->migrateFrom[sizeof(vm->migrateFrom)-1] = '\0';
+
+    return qemudStartVMDaemon(dom->conn, driver, vm);
 }
 
 
@@ -1931,6 +2053,7 @@
         return -1;
     }
 
+    vm->migrateFrom[0] = '\0';
     return qemudStartVMDaemon(dom->conn, driver, vm);
 }
 




More information about the libvir-list mailing list