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(a)jtan.com>
---
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