The save file format consists of a header, XML for the domain, and the raw QEMU/KVM migration data stream. Signed-off-by: Jim Paris <jim@xxxxxxxx> --- src/qemu_driver.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 5d310fe..50ab702 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -1932,20 +1932,117 @@ static char *qemudEscapeShellArg(const char *in) } -static int qemudDomainSave(virDomainPtr dom, - const char *path ATTRIBUTE_UNUSED) { +#define QEMUD_SAVE_MAGIC "LibvirtQemudSave" +struct qemud_save_header { + char magic[sizeof(QEMUD_SAVE_MAGIC)-1]; + int xml_len; + int was_running; +}; + +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; + char *safe_path; + char *xml; + struct qemud_save_header header; + + memset(&header, 0, sizeof(header)); + memcpy(header.magic, QEMUD_SAVE_MAGIC, sizeof(header.magic)); + 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; + + /* Pause */ + if (vm->state == VIR_DOMAIN_RUNNING) { + header.was_running = 1; + if (qemudDomainSuspend(dom) != 0) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "failed to pause domain"); + return -1; + } + } + + /* Get XML for the domain */ + xml = qemudGenerateXML(dom->conn, driver, vm, vm->def, 0); + if (!xml) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "failed to get domain xml"); + return -1; + } + header.xml_len = strlen(xml); + + /* Write header to file, followed by XML */ + if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "failed to create '%s'", path); + free(xml); + return -1; + } + + if (write(fd, &header, sizeof(header)) != sizeof(header)) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "failed to write save header"); + close(fd); + free(xml); + return -1; + } + + if (write(fd, xml, header.xml_len) != header.xml_len) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "failed to write xml"); + close(fd); + free(xml); + return -1; + } + + close(fd); + free(xml); + + /* Migrate to file */ + safe_path = qemudEscapeShellArg(path); + if (!safe_path) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "out of memory"); + return -1; + } + if (asprintf (&command, "migrate \"exec:" + "dd of='%s' oflag=append conv=notrunc 2>/dev/null" + "\"\n", safe_path) == -1) { + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "out of memory"); + free(safe_path); + return -1; + } + free(safe_path); + + 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); + + /* Shut it down */ + qemudShutdownVMDaemon(dom->conn, driver, vm); + if (!vm->configFile[0]) + qemudRemoveInactiveVM(driver, vm); + + return 0; } -- 1.5.3.rc4 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list