[PATCH 1/2] ch_driver: Add basic domain save & restore support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Purna Pavan Chandra Aekkaladevi <paekkaladevi@xxxxxxxxxxxxx>

save, managedsave and restore is supported for domains without any
network, hostdev config defined. The `path` input to the save command
should be a directory path since cloud-hypervisor expects directory path.

Signed-off-by: Purna Pavan Chandra Aekkaladevi <paekkaladevi@xxxxxxxxxxxxxxxxxxx>
---
 src/ch/ch_conf.c    |   6 +
 src/ch/ch_conf.h    |  12 ++
 src/ch/ch_driver.c  | 511 +++++++++++++++++++++++++++++++++++++++++++-
 src/ch/ch_monitor.c |  97 ++++++++-
 src/ch/ch_monitor.h |   6 +-
 src/ch/ch_process.c | 101 +++++++--
 src/ch/ch_process.h |   4 +
 7 files changed, 715 insertions(+), 22 deletions(-)

diff --git a/src/ch/ch_conf.c b/src/ch/ch_conf.c
index f421af5121..c109721a83 100644
--- a/src/ch/ch_conf.c
+++ b/src/ch/ch_conf.c
@@ -139,10 +139,12 @@ virCHDriverConfigNew(bool privileged)
     if (privileged) {
         cfg->logDir = g_strdup_printf("%s/log/libvirt/ch", LOCALSTATEDIR);
         cfg->stateDir = g_strdup_printf("%s/libvirt/ch", RUNSTATEDIR);
+        cfg->saveDir = g_strdup_printf("%s/lib/libvirt/ch/save", LOCALSTATEDIR);
 
     } else {
         g_autofree char *rundir = NULL;
         g_autofree char *cachedir = NULL;
+        g_autofree char *configbasedir = NULL;
 
         cachedir = virGetUserCacheDirectory();
 
@@ -150,6 +152,9 @@ virCHDriverConfigNew(bool privileged)
 
         rundir = virGetUserRuntimeDirectory();
         cfg->stateDir = g_strdup_printf("%s/ch/run", rundir);
+
+        configbasedir = virGetUserConfigDirectory();
+        cfg->saveDir = g_strdup_printf("%s/ch/save", configbasedir);
     }
 
     return cfg;
@@ -166,6 +171,7 @@ virCHDriverConfigDispose(void *obj)
 {
     virCHDriverConfig *cfg = obj;
 
+    g_free(cfg->saveDir);
     g_free(cfg->stateDir);
     g_free(cfg->logDir);
 }
diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h
index 579eca894e..a77cad7a2a 100644
--- a/src/ch/ch_conf.h
+++ b/src/ch/ch_conf.h
@@ -37,6 +37,7 @@ struct _virCHDriverConfig {
 
     char *stateDir;
     char *logDir;
+    char *saveDir;
 
     int cgroupControllers;
 
@@ -81,6 +82,17 @@ struct _virCHDriver
     ebtablesContext *ebtables;
 };
 
+#define CH_SAVE_MAGIC "libvirt-xml\n \0 \r"
+#define CH_SAVE_XML "libvirt-save.xml"
+
+typedef struct _CHSaveXMLHeader CHSaveXMLHeader;
+struct _CHSaveXMLHeader {
+    char magic[sizeof(CH_SAVE_MAGIC)-1];
+    uint32_t xmlLen;
+    /* 20 bytes used, pad up to 64 bytes */
+    uint32_t unused[11];
+};
+
 virCaps *virCHDriverCapsInit(void);
 virCaps *virCHDriverGetCapabilities(virCHDriver *driver,
                                       bool refresh);
diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c
index 96de5044ac..4413abfa79 100644
--- a/src/ch/ch_driver.c
+++ b/src/ch/ch_driver.c
@@ -20,6 +20,8 @@
 
 #include <config.h>
 
+#include <fcntl.h>
+
 #include "ch_capabilities.h"
 #include "ch_conf.h"
 #include "ch_domain.h"
@@ -32,6 +34,7 @@
 #include "viraccessapicheck.h"
 #include "virchrdev.h"
 #include "virerror.h"
+#include "virfile.h"
 #include "virlog.h"
 #include "virobject.h"
 #include "virtypedparam.h"
@@ -176,6 +179,13 @@ static char *chConnectGetCapabilities(virConnectPtr conn)
     return xml;
 }
 
+static char *
+chDomainManagedSavePath(virCHDriver *driver, virDomainObj *vm)
+{
+    g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(driver);
+    return g_strdup_printf("%s/%s.save", cfg->saveDir, vm->def->name);
+}
+
 /**
  * chDomainCreateXML:
  * @conn: pointer to connection
@@ -196,6 +206,7 @@ chDomainCreateXML(virConnectPtr conn,
     virDomainObj *vm = NULL;
     virDomainPtr dom = NULL;
     unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
+    g_autofree char *managed_save_path = NULL;
 
     virCheckFlags(VIR_DOMAIN_START_VALIDATE, NULL);
 
@@ -218,6 +229,15 @@ chDomainCreateXML(virConnectPtr conn,
                                    NULL)))
         goto cleanup;
 
+    /* cleanup if there's any stale managedsave dir */
+    managed_save_path = chDomainManagedSavePath(driver, vm);
+    if (virFileDeleteTree(managed_save_path) < 0) {
+        virReportSystemError(errno,
+                             _("Failed to cleanup stale managed save dir '%1$s'"),
+                             managed_save_path);
+        goto cleanup;
+    }
+
     if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
         goto cleanup;
 
@@ -242,6 +262,8 @@ chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
 {
     virCHDriver *driver = dom->conn->privateData;
     virDomainObj *vm;
+    virCHDomainObjPrivate *priv;
+    g_autofree char *managed_save_path = NULL;
     int ret = -1;
 
     virCheckFlags(0, -1);
@@ -255,8 +277,33 @@ chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
     if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
         goto cleanup;
 
-    ret = virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED);
+    if (vm->hasManagedSave) {
+        priv = vm->privateData;
+        managed_save_path = chDomainManagedSavePath(driver, vm);
+        if (virCHProcessStartRestore(driver, vm, managed_save_path) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to restore domain from managed save"));
+            goto endjob;
+        }
+        if (virCHMonitorResumeVM(priv->monitor) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to resume domain after restore from managed save"));
+            goto endjob;
+        }
+        virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_RESTORED);
+        if (virFileDeleteTree(managed_save_path) < 0) {
+            virReportSystemError(errno,
+                                 _("Failed to remove managed save path '%1$s'"),
+                                 managed_save_path);
+            goto endjob;
+        }
+        vm->hasManagedSave = false;
+        ret = 0;
+    } else {
+        ret = virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED);
+    }
 
+ endjob:
     virDomainObjEndJob(vm);
 
  cleanup:
@@ -277,6 +324,7 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
     g_autoptr(virDomainDef) vmdef = NULL;
     virDomainObj *vm = NULL;
     virDomainPtr dom = NULL;
+    g_autofree char *managed_save_path = NULL;
     unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
 
     virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
@@ -299,6 +347,15 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
                                    0, NULL)))
         goto cleanup;
 
+    /* cleanup if there's any stale managedsave dir */
+    managed_save_path = chDomainManagedSavePath(driver, vm);
+    if (virFileDeleteTree(managed_save_path) < 0) {
+        virReportSystemError(errno,
+                             _("Failed to cleanup stale managed save dir '%1$s'"),
+                             managed_save_path);
+        goto cleanup;
+    }
+
     vm->persistent = 1;
 
     dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
@@ -621,6 +678,449 @@ chDomainDestroy(virDomainPtr dom)
     return chDomainDestroyFlags(dom, 0);
 }
 
+static int
+chDomainSaveAdditionalValidation(virDomainDef *vmdef)
+{
+    /*
+    SAVE and RESTORE are functional only without any networking and
+    device passthrough configuration
+    */
+    if (vmdef->nnets > 0) {
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                       _("cannot save domain with network interfaces"));
+        return -1;
+    }
+    if  (vmdef->nhostdevs > 0) {
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                       _("cannot save domain with host devices"));
+        return -1;
+    }
+    return 0;
+}
+
+/**
+ * chDoDomainSave:
+ * @driver: pointer to driver structure
+ * @vm: pointer to virtual machine structure. Must be locked before invocation.
+ * @to_dir: directory path (CH needs directory input) to save the domain
+ * @managed: whether the VM is managed or not
+ *
+ * Checks if the domain is running or paused, then suspends it and saves it
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+static int
+chDoDomainSave(virCHDriver *driver,
+               virDomainObj *vm,
+               const char *to_dir,
+               bool managed)
+{
+    g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(driver);
+    virCHDomainObjPrivate *priv = vm->privateData;
+    CHSaveXMLHeader hdr;
+    g_autofree char *to = NULL;
+    g_autofree char *xml = NULL;
+    uint32_t xml_len;
+    VIR_AUTOCLOSE fd = -1;
+    int ret = -1;
+
+    virDomainState domainState = virDomainObjGetState(vm, NULL);
+    if (domainState == VIR_DOMAIN_RUNNING) {
+        if (virCHMonitorSuspendVM(priv->monitor) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to suspend domain before saving"));
+            goto end;
+        }
+        virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_SAVE);
+    } else if (domainState != VIR_DOMAIN_PAUSED) {
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                       _("only can save running/paused domain"));
+        goto end;
+    }
+
+    if (chDomainSaveAdditionalValidation(vm->def) < 0)
+        goto end;
+
+    if (virDirCreate(to_dir, 0770, cfg->user, cfg->group,
+                     VIR_DIR_CREATE_ALLOW_EXIST) < 0) {
+        virReportSystemError(errno, _("Failed to create SAVE dir %1$s"), to_dir);
+        goto end;
+    }
+
+    to = g_strdup_printf("%s/%s", to_dir, CH_SAVE_XML);
+    if ((fd = virFileOpenAs(to, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR,
+                            cfg->user, cfg->group, 0)) < 0) {
+        virReportSystemError(-fd,
+                             _("Failed to create/open domain save xml file '%1$s'"),
+                             to);
+        goto end;
+    }
+
+    if ((xml = virDomainDefFormat(vm->def, driver->xmlopt, 0)) == NULL)
+        goto end;
+    xml_len = strlen(xml) + 1;
+
+    memset(&hdr, 0, sizeof(hdr));
+    memcpy(hdr.magic, CH_SAVE_MAGIC, sizeof(hdr.magic));
+    hdr.xmlLen = xml_len;
+
+    if (safewrite(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+        virReportSystemError(errno, "%s", _("Failed to write file header"));
+        goto end;
+    }
+
+    if (safewrite(fd, xml, xml_len) != xml_len) {
+        virReportSystemError(errno, "%s", _("Failed to write xml definition"));
+        goto end;
+    }
+
+    if (virCHMonitorSaveVM(priv->monitor, to_dir) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to save domain"));
+        goto end;
+    }
+
+    if (virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_SAVED) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Failed to shutoff after domain save"));
+        goto end;
+    }
+
+    vm->hasManagedSave = managed;
+    ret = 0;
+
+ end:
+    return ret;
+}
+
+static int
+chDomainSaveFlags(virDomainPtr dom, const char *to, const char *dxml, unsigned int flags)
+{
+    virCHDriver *driver = dom->conn->privateData;
+    virDomainObj *vm = NULL;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+    if (dxml) {
+        virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                       _("xml modification unsupported"));
+        return -1;
+    }
+
+    if (!(vm = virCHDomainObjFromDomain(dom)))
+        goto cleanup;
+
+    if (virDomainSaveFlagsEnsureACL(dom->conn, vm->def) < 0)
+        goto cleanup;
+
+    if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
+        goto cleanup;
+
+    if (virDomainObjCheckActive(vm) < 0)
+        goto endjob;
+
+    if (chDoDomainSave(driver, vm, to, false) < 0)
+        goto endjob;
+
+    /* Remove if VM is not persistent */
+    virCHDomainRemoveInactive(driver, vm);
+    ret = 0;
+
+ endjob:
+    virDomainObjEndJob(vm);
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return ret;
+}
+
+static int
+chDomainSave(virDomainPtr dom, const char *to)
+{
+    return chDomainSaveFlags(dom, to, NULL, 0);
+}
+
+static char *
+chDomainSaveXMLRead(int fd)
+{
+    g_autofree char *xml = NULL;
+    CHSaveXMLHeader hdr;
+
+    if (saferead(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                       _("failed to read CHSaveXMLHeader header"));
+        return NULL;
+    }
+
+    if (memcmp(hdr.magic, CH_SAVE_MAGIC, sizeof(hdr.magic))) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("save image magic is incorrect"));
+        return NULL;
+    }
+
+    if (hdr.xmlLen <= 0) {
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       _("invalid XML length: %1$d"), hdr.xmlLen);
+        return NULL;
+    }
+
+    xml = g_new0(char, hdr.xmlLen);
+
+    if (saferead(fd, xml, hdr.xmlLen) != hdr.xmlLen) {
+        virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                       _("failed to read XML"));
+        return NULL;
+    }
+
+    return g_steal_pointer(&xml);
+}
+
+static int chDomainSaveImageRead(virCHDriver *driver,
+                                 const char *path,
+                                 virDomainDef **ret_def)
+{
+    g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(driver);
+    g_autoptr(virDomainDef) def = NULL;
+    g_autofree char *from = NULL;
+    g_autofree char *xml = NULL;
+    VIR_AUTOCLOSE fd = -1;
+    int ret = -1;
+
+    from = g_strdup_printf("%s/%s", path, CH_SAVE_XML);
+    if ((fd = virFileOpenAs(from, O_RDONLY, 0, cfg->user, cfg->group, 0)) < 0) {
+        virReportSystemError(errno,
+                             _("Failed to open domain save file '%1$s'"),
+                             from);
+        goto end;
+    }
+
+    if (!(xml = chDomainSaveXMLRead(fd)))
+        goto end;
+
+    if (!(def = virDomainDefParseString(xml, driver->xmlopt, NULL,
+                                        VIR_DOMAIN_DEF_PARSE_INACTIVE |
+                                        VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
+        goto end;
+
+    *ret_def = g_steal_pointer(&def);
+    ret = 0;
+
+ end:
+    return ret;
+}
+
+static char *
+chDomainSaveImageGetXMLDesc(virConnectPtr conn,
+                            const char *path,
+                            unsigned int flags)
+{
+    virCHDriver *driver = conn->privateData;
+    g_autoptr(virDomainDef) def = NULL;
+    char *ret = NULL;
+
+    virCheckFlags(VIR_DOMAIN_SAVE_IMAGE_XML_SECURE, NULL);
+
+    if (chDomainSaveImageRead(driver, path, &def) < 0)
+        goto cleanup;
+
+    if (virDomainSaveImageGetXMLDescEnsureACL(conn, def) < 0)
+        goto cleanup;
+
+    ret = virDomainDefFormat(def, driver->xmlopt,
+                             virDomainDefFormatConvertXMLFlags(flags));
+
+ cleanup:
+    return ret;
+}
+
+static int
+chDomainRestoreFlags(virConnectPtr conn,
+                     const char *from,
+                     const char *dxml,
+                     unsigned int flags)
+{
+    virCHDriver *driver = conn->privateData;
+    virDomainObj *vm = NULL;
+    virCHDomainObjPrivate *priv;
+    g_autoptr(virDomainDef) def = NULL;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    if (dxml) {
+        virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                       _("xml modification unsupported"));
+        return -1;
+    }
+
+    if (chDomainSaveImageRead(driver, from, &def) < 0)
+        goto cleanup;
+
+    if (virDomainRestoreFlagsEnsureACL(conn, def) < 0)
+        goto cleanup;
+
+    if (!(vm = virDomainObjListAdd(driver->domains, &def,
+                                   driver->xmlopt,
+                                   VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
+                                   VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
+                                   NULL)))
+        goto cleanup;
+
+    if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
+        goto cleanup;
+
+    if (virCHProcessStartRestore(driver, vm, from) < 0)
+        goto endjob;
+
+    priv = vm->privateData;
+    if (virCHMonitorResumeVM(priv->monitor) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to resume domain after restore"));
+        goto endjob;
+    }
+    virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_RESTORED);
+    ret = 0;
+
+ endjob:
+    virDomainObjEndJob(vm);
+
+ cleanup:
+    if (vm && ret < 0)
+        virCHDomainRemoveInactive(driver, vm);
+    virDomainObjEndAPI(&vm);
+    return ret;
+}
+
+static int
+chDomainRestore(virConnectPtr conn, const char *from)
+{
+    return chDomainRestoreFlags(conn, from, NULL, 0);
+}
+
+static int
+chDomainManagedSave(virDomainPtr dom, unsigned int flags)
+{
+    virCHDriver *driver = dom->conn->privateData;
+    virDomainObj *vm = NULL;
+    g_autofree char *to = NULL;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    if (!(vm = virCHDomainObjFromDomain(dom)))
+        goto cleanup;
+
+    if (virDomainManagedSaveEnsureACL(dom->conn, vm->def) < 0)
+        goto cleanup;
+
+    if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0)
+        goto cleanup;
+
+    if (virDomainObjCheckActive(vm) < 0)
+        goto endjob;
+
+    if (!vm->persistent) {
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                       _("cannot do managed save for transient domain"));
+        goto endjob;
+    }
+
+    to = chDomainManagedSavePath(driver, vm);
+    if (chDoDomainSave(driver, vm, to, true) < 0)
+        goto endjob;
+
+    ret = 0;
+
+ endjob:
+    virDomainObjEndJob(vm);
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return ret;
+}
+
+static int
+chDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags)
+{
+    virCHDriver *driver = dom->conn->privateData;
+    virDomainObj *vm;
+    int ret = -1;
+    g_autofree char *path = NULL;
+
+    virCheckFlags(0, -1);
+
+    if (!(vm = virCHDomainObjFromDomain(dom)))
+        return -1;
+
+    if (virDomainManagedSaveRemoveEnsureACL(dom->conn, vm->def) < 0)
+        goto cleanup;
+
+    path = chDomainManagedSavePath(driver, vm);
+
+    if (virFileDeleteTree(path) < 0) {
+        virReportSystemError(errno,
+                             _("Failed to remove managed save path '%1$s'"),
+                             path);
+        goto cleanup;
+    }
+
+    vm->hasManagedSave = false;
+    ret = 0;
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return ret;
+}
+
+static char *
+chDomainManagedSaveGetXMLDesc(virDomainPtr dom, unsigned int flags)
+{
+    virCHDriver *driver = dom->conn->privateData;
+    virDomainObj *vm = NULL;
+    g_autoptr(virDomainDef) def = NULL;
+    char *ret = NULL;
+    g_autofree char *path = NULL;
+
+    virCheckFlags(VIR_DOMAIN_SAVE_IMAGE_XML_SECURE, NULL);
+
+    if (!(vm = virCHDomainObjFromDomain(dom)))
+        goto cleanup;
+
+    path = chDomainManagedSavePath(driver, vm);
+    if (chDomainSaveImageRead(driver, path, &def) < 0)
+        goto cleanup;
+
+    if (virDomainManagedSaveGetXMLDescEnsureACL(dom->conn, def, flags) < 0)
+        goto cleanup;
+
+    ret = virDomainDefFormat(def, driver->xmlopt,
+                             virDomainDefFormatConvertXMLFlags(flags));
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return ret;
+}
+
+static int
+chDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
+{
+    virDomainObj *vm = NULL;
+    int ret = -1;
+
+    virCheckFlags(0, -1);
+
+    if (!(vm = virCHDomainObjFromDomain(dom)))
+        return -1;
+
+    if (virDomainHasManagedSaveImageEnsureACL(dom->conn, vm->def) < 0)
+        goto cleanup;
+
+    ret = vm->hasManagedSave;
+
+ cleanup:
+    virDomainObjEndAPI(&vm);
+    return ret;
+}
+
 static virDomainPtr chDomainLookupByID(virConnectPtr conn,
                                        int id)
 {
@@ -1734,6 +2234,15 @@ static virHypervisorDriver chHypervisorDriver = {
     .nodeGetCPUMap = chNodeGetCPUMap,                       /* 8.0.0 */
     .domainSetNumaParameters = chDomainSetNumaParameters,   /* 8.1.0 */
     .domainGetNumaParameters = chDomainGetNumaParameters,   /* 8.1.0 */
+    .domainSave = chDomainSave,                             /* 10.1.0 */
+    .domainSaveFlags = chDomainSaveFlags,                   /* 10.1.0 */
+    .domainSaveImageGetXMLDesc = chDomainSaveImageGetXMLDesc,   /* 10.1.0 */
+    .domainRestore = chDomainRestore,                       /* 10.1.0 */
+    .domainRestoreFlags = chDomainRestoreFlags,             /* 10.1.0 */
+    .domainManagedSave = chDomainManagedSave,               /* 10.1.0 */
+    .domainManagedSaveRemove = chDomainManagedSaveRemove,   /* 10.1.0 */
+    .domainManagedSaveGetXMLDesc = chDomainManagedSaveGetXMLDesc,   /* 10.1.0 */
+    .domainHasManagedSaveImage = chDomainHasManagedSaveImage,   /* 10.1.0 */
 };
 
 static virConnectDriver chConnectDriver = {
diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c
index 62ba72bb82..bb55548516 100644
--- a/src/ch/ch_monitor.c
+++ b/src/ch/ch_monitor.c
@@ -444,6 +444,22 @@ virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef *vmdef,
     return 0;
 }
 
+static int
+virCHMonitorBuildKeyValueStringJson(char **jsonstr,
+                                    const char *key,
+                                    const char *value)
+{
+    g_autoptr(virJSONValue) content = virJSONValueNewObject();
+
+    if (virJSONValueObjectAppendString(content, key, value) < 0)
+        return -1;
+
+    if (!(*jsonstr = virJSONValueToString(content, false)))
+        return -1;
+
+    return 0;
+}
+
 static int
 chMonitorCreateSocket(const char *socket_path)
 {
@@ -500,10 +516,11 @@ chMonitorCreateSocket(const char *socket_path)
 }
 
 virCHMonitor *
-virCHMonitorNew(virDomainObj *vm, const char *socketdir)
+virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg)
 {
     g_autoptr(virCHMonitor) mon = NULL;
     g_autoptr(virCommand) cmd = NULL;
+    char *socketdir = cfg->stateDir;
     int socket_fd = 0;
 
     if (virCHMonitorInitialize() < 0)
@@ -527,6 +544,13 @@ virCHMonitorNew(virDomainObj *vm, const char *socketdir)
         return NULL;
     }
 
+    if (g_mkdir_with_parents(cfg->saveDir, 0777) < 0) {
+        virReportSystemError(errno,
+                             _("Cannot create save directory '%1$s'"),
+                             cfg->saveDir);
+        return NULL;
+    }
+
     cmd = virCommandNew(vm->def->emulator);
     virCommandSetUmask(cmd, 0x002);
     socket_fd = chMonitorCreateSocket(mon->socketpath);
@@ -883,6 +907,77 @@ virCHMonitorResumeVM(virCHMonitor *mon)
     return virCHMonitorPutNoContent(mon, URL_VM_RESUME);
 }
 
+static int
+virCHMonitorSaveRestoreVM(virCHMonitor *mon, const char *path, bool save)
+{
+    g_autofree char *url = NULL;
+    int responseCode = 0;
+    int ret = -1;
+    g_autofree char *payload = NULL;
+    g_autofree char *path_url = NULL;
+    struct curl_slist *headers = NULL;
+    struct curl_data data = {0};
+
+    if (save)
+        url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_SAVE);
+    else
+        url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_RESTORE);
+
+    headers = curl_slist_append(headers, "Accept: application/json");
+    headers = curl_slist_append(headers, "Content-Type: application/json");
+
+    path_url = g_strdup_printf("file://%s", path);
+    if (save) {
+        if (virCHMonitorBuildKeyValueStringJson(&payload, "destination_url", path_url) != 0)
+            return -1;
+    } else {
+        if (virCHMonitorBuildKeyValueStringJson(&payload, "source_url", path_url) != 0)
+            return -1;
+    }
+
+    VIR_WITH_OBJECT_LOCK_GUARD(mon) {
+        /* reset all options of a libcurl session handle at first */
+        curl_easy_reset(mon->handle);
+
+        curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpath);
+        curl_easy_setopt(mon->handle, CURLOPT_URL, url);
+        curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT");
+        curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers);
+        curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload);
+        curl_easy_setopt(mon->handle, CURLOPT_WRITEFUNCTION, curl_callback);
+        curl_easy_setopt(mon->handle, CURLOPT_WRITEDATA, (void *)&data);
+
+        responseCode = virCHMonitorCurlPerform(mon->handle);
+    }
+
+    if (responseCode == 200 || responseCode == 204) {
+        ret = 0;
+    } else {
+        data.content = g_realloc(data.content, data.size + 1);
+        data.content[data.size] = 0;
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       data.content);
+        g_free(data.content);
+    }
+
+    /* reset the libcurl handle to avoid leaking a stack pointer to data */
+    curl_easy_reset(mon->handle);
+    curl_slist_free_all(headers);
+    return ret;
+}
+
+int
+virCHMonitorSaveVM(virCHMonitor *mon, const char *to)
+{
+    return virCHMonitorSaveRestoreVM(mon, to, true);
+}
+
+int
+virCHMonitorRestoreVM(virCHMonitor *mon, const char *from)
+{
+    return virCHMonitorSaveRestoreVM(mon, from, false);
+}
+
 /**
  * virCHMonitorGetInfo:
  * @mon: Pointer to the monitor
diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h
index 47b4e7abbd..ea6b2a771b 100644
--- a/src/ch/ch_monitor.h
+++ b/src/ch/ch_monitor.h
@@ -37,6 +37,8 @@
 #define URL_VM_Suspend "vm.pause"
 #define URL_VM_RESUME "vm.resume"
 #define URL_VM_INFO "vm.info"
+#define URL_VM_SAVE "vm.snapshot"
+#define URL_VM_RESTORE "vm.restore"
 
 #define VIRCH_THREAD_NAME_LEN   16
 
@@ -99,7 +101,7 @@ struct _virCHMonitor {
     virCHMonitorThreadInfo *threads;
 };
 
-virCHMonitor *virCHMonitorNew(virDomainObj *vm, const char *socketdir);
+virCHMonitor *virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg);
 void virCHMonitorClose(virCHMonitor *mon);
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCHMonitor, virCHMonitorClose);
 
@@ -110,6 +112,8 @@ int virCHMonitorShutdownVM(virCHMonitor *mon);
 int virCHMonitorRebootVM(virCHMonitor *mon);
 int virCHMonitorSuspendVM(virCHMonitor *mon);
 int virCHMonitorResumeVM(virCHMonitor *mon);
+int virCHMonitorSaveVM(virCHMonitor *mon, const char *to);
+int virCHMonitorRestoreVM(virCHMonitor *mon, const char *from);
 int virCHMonitorGetInfo(virCHMonitor *mon, virJSONValue **info);
 
 void virCHMonitorCPUInfoFree(virCHMonitorCPUInfo *cpus);
diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c
index 86d3190324..3f0816a513 100644
--- a/src/ch/ch_process.c
+++ b/src/ch/ch_process.c
@@ -51,7 +51,7 @@ virCHProcessConnectMonitor(virCHDriver *driver,
     virCHMonitor *monitor = NULL;
     virCHDriverConfig *cfg = virCHDriverGetConfig(driver);
 
-    monitor = virCHMonitorNew(vm, cfg->stateDir);
+    monitor = virCHMonitorNew(vm, cfg);
 
     virObjectUnref(cfg);
     return monitor;
@@ -453,6 +453,34 @@ virCHProcessSetupVcpus(virDomainObj *vm)
     return 0;
 }
 
+static int
+virCHProcessSetup(virDomainObj *vm)
+{
+    virCHDomainObjPrivate *priv = vm->privateData;
+
+    virCHDomainRefreshThreadInfo(vm);
+
+    VIR_DEBUG("Setting emulator tuning/settings");
+    if (virCHProcessSetupEmulatorThreads(vm) < 0)
+        return -1;
+
+    VIR_DEBUG("Setting iothread tuning/settings");
+    if (virCHProcessSetupIOThreads(vm) < 0)
+        return -1;
+
+    VIR_DEBUG("Setting global CPU cgroup (if required)");
+    if (virDomainCgroupSetupGlobalCpuCgroup(vm,
+                                            priv->cgroup) < 0)
+        return -1;
+
+    VIR_DEBUG("Setting vCPU tuning/settings");
+    if (virCHProcessSetupVcpus(vm) < 0)
+        return -1;
+
+    virCHProcessUpdateInfo(vm);
+    return 0;
+}
+
 
 #define PKT_TIMEOUT_MS 500 /* ms */
 
@@ -707,26 +735,9 @@ virCHProcessStart(virCHDriver *driver,
         goto cleanup;
     }
 
-    virCHDomainRefreshThreadInfo(vm);
-
-    VIR_DEBUG("Setting emulator tuning/settings");
-    if (virCHProcessSetupEmulatorThreads(vm) < 0)
-        goto cleanup;
-
-    VIR_DEBUG("Setting iothread tuning/settings");
-    if (virCHProcessSetupIOThreads(vm) < 0)
-        goto cleanup;
-
-    VIR_DEBUG("Setting global CPU cgroup (if required)");
-    if (virDomainCgroupSetupGlobalCpuCgroup(vm,
-                                            priv->cgroup) < 0)
-        goto cleanup;
-
-    VIR_DEBUG("Setting vCPU tuning/settings");
-    if (virCHProcessSetupVcpus(vm) < 0)
+    if (virCHProcessSetup(vm) < 0)
         goto cleanup;
 
-    virCHProcessUpdateInfo(vm);
     virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
 
     return 0;
@@ -785,3 +796,55 @@ virCHProcessStop(virCHDriver *driver G_GNUC_UNUSED,
 
     return 0;
 }
+
+/**
+ * virCHProcessStartRestore:
+ * @driver: pointer to driver structure
+ * @vm: pointer to virtual machine structure
+ * @from: directory path to restore the VM from
+ *
+ * Starts Cloud-Hypervisor process with the restored VM
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+int
+virCHProcessStartRestore(virCHDriver *driver, virDomainObj *vm, const char *from)
+{
+    virCHDomainObjPrivate *priv = vm->privateData;
+    g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(priv->driver);
+
+    if (!priv->monitor) {
+        /* Get the first monitor connection if not already */
+        if (!(priv->monitor = virCHProcessConnectMonitor(driver, vm))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("failed to create connection to CH socket"));
+            return -1;
+        }
+    }
+
+    vm->pid = priv->monitor->pid;
+    vm->def->id = vm->pid;
+    priv->machineName = virCHDomainGetMachineName(vm);
+
+    if (virCHMonitorRestoreVM(priv->monitor, from) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to restore domain"));
+        return -1;
+    }
+
+    if (virDomainCgroupSetupCgroup("ch", vm,
+                                   0, NULL, /* nnicindexes, nicindexes */
+                                   &priv->cgroup,
+                                   cfg->cgroupControllers,
+                                   0, /*maxThreadsPerProc*/
+                                   priv->driver->privileged,
+                                   priv->machineName) < 0)
+        return -1;
+
+    if (virCHProcessSetup(vm) < 0)
+        return -1;
+
+    virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_FROM_SNAPSHOT);
+
+    return 0;
+}
diff --git a/src/ch/ch_process.h b/src/ch/ch_process.h
index 800e3f4e23..38bfce3b7f 100644
--- a/src/ch/ch_process.h
+++ b/src/ch/ch_process.h
@@ -32,3 +32,7 @@ int virCHProcessStop(virCHDriver *driver,
 
 int virCHProcessSetupVcpu(virDomainObj *vm,
                           unsigned int vcpuid);
+
+int virCHProcessStartRestore(virCHDriver *driver,
+                         virDomainObj *vm,
+                         const char *from);
-- 
2.34.1
_______________________________________________
Devel mailing list -- devel@xxxxxxxxxxxxxxxxx
To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxx




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux