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