From nobody Thu May 9 13:21:43 2024 Delivered-To: importer@patchew.org Received-SPF: none (zohomail.com: 8.43.85.245 is neither permitted nor denied by domain of lists.libvirt.org) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; spf=none (zohomail.com: 8.43.85.245 is neither permitted nor denied by domain of lists.libvirt.org) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=linux.microsoft.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1707212439766195.88951085591611; Tue, 6 Feb 2024 01:40:39 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 528BD1C06; Tue, 6 Feb 2024 04:40:38 -0500 (EST) Received: from lists.libvirt.org.85.43.8.in-addr.arpa (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id AAEF81D84; Tue, 6 Feb 2024 04:34:44 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id 4ADD01CE8; Tue, 6 Feb 2024 04:34:33 -0500 (EST) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by lists.libvirt.org (Postfix) with ESMTP id 060271C06 for ; Tue, 6 Feb 2024 04:34:30 -0500 (EST) Received: from paekkaladevi-dev-u22.gi4irqh5pfqublruu4yyku2wof.phxx.internal.cloudapp.net (unknown [20.125.125.171]) by linux.microsoft.com (Postfix) with ESMTPSA id C012C20B2001; Tue, 6 Feb 2024 01:34:29 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com C012C20B2001 From: Purna Pavan Chandra Aekkaladevi To: devel@lists.libvirt.org Subject: [PATCH 1/2] ch_driver: Add basic domain save & restore support Date: Tue, 6 Feb 2024 09:34:27 +0000 Message-Id: <20240206093428.18359-2-paekkaladevi@linux.microsoft.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206093428.18359-1-paekkaladevi@linux.microsoft.com> References: <20240206093428.18359-1-paekkaladevi@linux.microsoft.com> MIME-Version: 1.0 Message-ID-Hash: ZT7WMWS6575P66YQFLVYEWF534SFDXQD X-Message-ID-Hash: ZT7WMWS6575P66YQFLVYEWF534SFDXQD X-MailFrom: paekkaladevi@linux.microsoft.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: paekkaladevi@microsoft.com, prapal@linux.microsoft.com, kumarpraveen@linux.microsoft.com, Purna Pavan Chandra Aekkaladevi X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1707212440784100001 From: Purna Pavan Chandra Aekkaladevi 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 --- 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 =3D g_strdup_printf("%s/log/libvirt/ch", LOCALSTATEDIR= ); cfg->stateDir =3D g_strdup_printf("%s/libvirt/ch", RUNSTATEDIR); + cfg->saveDir =3D g_strdup_printf("%s/lib/libvirt/ch/save", LOCALST= ATEDIR); =20 } else { g_autofree char *rundir =3D NULL; g_autofree char *cachedir =3D NULL; + g_autofree char *configbasedir =3D NULL; =20 cachedir =3D virGetUserCacheDirectory(); =20 @@ -150,6 +152,9 @@ virCHDriverConfigNew(bool privileged) =20 rundir =3D virGetUserRuntimeDirectory(); cfg->stateDir =3D g_strdup_printf("%s/ch/run", rundir); + + configbasedir =3D virGetUserConfigDirectory(); + cfg->saveDir =3D g_strdup_printf("%s/ch/save", configbasedir); } =20 return cfg; @@ -166,6 +171,7 @@ virCHDriverConfigDispose(void *obj) { virCHDriverConfig *cfg =3D obj; =20 + 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 { =20 char *stateDir; char *logDir; + char *saveDir; =20 int cgroupControllers; =20 @@ -81,6 +82,17 @@ struct _virCHDriver ebtablesContext *ebtables; }; =20 +#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 @@ =20 #include =20 +#include + #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 co= nn) return xml; } =20 +static char * +chDomainManagedSavePath(virCHDriver *driver, virDomainObj *vm) +{ + g_autoptr(virCHDriverConfig) cfg =3D 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 =3D NULL; virDomainPtr dom =3D NULL; unsigned int parse_flags =3D VIR_DOMAIN_DEF_PARSE_INACTIVE; + g_autofree char *managed_save_path =3D NULL; =20 virCheckFlags(VIR_DOMAIN_START_VALIDATE, NULL); =20 @@ -218,6 +229,15 @@ chDomainCreateXML(virConnectPtr conn, NULL))) goto cleanup; =20 + /* cleanup if there's any stale managedsave dir */ + managed_save_path =3D 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; =20 @@ -242,6 +262,8 @@ chDomainCreateWithFlags(virDomainPtr dom, unsigned int = flags) { virCHDriver *driver =3D dom->conn->privateData; virDomainObj *vm; + virCHDomainObjPrivate *priv; + g_autofree char *managed_save_path =3D NULL; int ret =3D -1; =20 virCheckFlags(0, -1); @@ -255,8 +277,33 @@ chDomainCreateWithFlags(virDomainPtr dom, unsigned int= flags) if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) goto cleanup; =20 - ret =3D virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED); + if (vm->hasManagedSave) { + priv =3D vm->privateData; + managed_save_path =3D 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 m= anaged save")); + goto endjob; + } + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_RE= STORED); + if (virFileDeleteTree(managed_save_path) < 0) { + virReportSystemError(errno, + _("Failed to remove managed save path '%1= $s'"), + managed_save_path); + goto endjob; + } + vm->hasManagedSave =3D false; + ret =3D 0; + } else { + ret =3D virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED); + } =20 + endjob: virDomainObjEndJob(vm); =20 cleanup: @@ -277,6 +324,7 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *= xml, unsigned int flags) g_autoptr(virDomainDef) vmdef =3D NULL; virDomainObj *vm =3D NULL; virDomainPtr dom =3D NULL; + g_autofree char *managed_save_path =3D NULL; unsigned int parse_flags =3D VIR_DOMAIN_DEF_PARSE_INACTIVE; =20 virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL); @@ -299,6 +347,15 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char = *xml, unsigned int flags) 0, NULL))) goto cleanup; =20 + /* cleanup if there's any stale managedsave dir */ + managed_save_path =3D 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 =3D 1; =20 dom =3D virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id); @@ -621,6 +678,449 @@ chDomainDestroy(virDomainPtr dom) return chDomainDestroyFlags(dom, 0); } =20 +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 invoca= tion. + * @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 =3D virCHDriverGetConfig(driver); + virCHDomainObjPrivate *priv =3D vm->privateData; + CHSaveXMLHeader hdr; + g_autofree char *to =3D NULL; + g_autofree char *xml =3D NULL; + uint32_t xml_len; + VIR_AUTOCLOSE fd =3D -1; + int ret =3D -1; + + virDomainState domainState =3D virDomainObjGetState(vm, NULL); + if (domainState =3D=3D 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 !=3D 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"), t= o_dir); + goto end; + } + + to =3D g_strdup_printf("%s/%s", to_dir, CH_SAVE_XML); + if ((fd =3D virFileOpenAs(to, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUS= R, + cfg->user, cfg->group, 0)) < 0) { + virReportSystemError(-fd, + _("Failed to create/open domain save xml file= '%1$s'"), + to); + goto end; + } + + if ((xml =3D virDomainDefFormat(vm->def, driver->xmlopt, 0)) =3D=3D NU= LL) + goto end; + xml_len =3D strlen(xml) + 1; + + memset(&hdr, 0, sizeof(hdr)); + memcpy(hdr.magic, CH_SAVE_MAGIC, sizeof(hdr.magic)); + hdr.xmlLen =3D xml_len; + + if (safewrite(fd, &hdr, sizeof(hdr)) !=3D sizeof(hdr)) { + virReportSystemError(errno, "%s", _("Failed to write file header")= ); + goto end; + } + + if (safewrite(fd, xml, xml_len) !=3D xml_len) { + virReportSystemError(errno, "%s", _("Failed to write xml definitio= n")); + goto end; + } + + if (virCHMonitorSaveVM(priv->monitor, to_dir) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to save dom= ain")); + 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 =3D managed; + ret =3D 0; + + end: + return ret; +} + +static int +chDomainSaveFlags(virDomainPtr dom, const char *to, const char *dxml, unsi= gned int flags) +{ + virCHDriver *driver =3D dom->conn->privateData; + virDomainObj *vm =3D NULL; + int ret =3D -1; + + virCheckFlags(0, -1); + if (dxml) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("xml modification unsupported")); + return -1; + } + + if (!(vm =3D 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 =3D 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 =3D NULL; + CHSaveXMLHeader hdr; + + if (saferead(fd, &hdr, sizeof(hdr)) !=3D 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 <=3D 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("invalid XML length: %1$d"), hdr.xmlLen); + return NULL; + } + + xml =3D g_new0(char, hdr.xmlLen); + + if (saferead(fd, xml, hdr.xmlLen) !=3D 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 =3D virCHDriverGetConfig(driver); + g_autoptr(virDomainDef) def =3D NULL; + g_autofree char *from =3D NULL; + g_autofree char *xml =3D NULL; + VIR_AUTOCLOSE fd =3D -1; + int ret =3D -1; + + from =3D g_strdup_printf("%s/%s", path, CH_SAVE_XML); + if ((fd =3D 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 =3D chDomainSaveXMLRead(fd))) + goto end; + + if (!(def =3D virDomainDefParseString(xml, driver->xmlopt, NULL, + VIR_DOMAIN_DEF_PARSE_INACTIVE | + VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE= ))) + goto end; + + *ret_def =3D g_steal_pointer(&def); + ret =3D 0; + + end: + return ret; +} + +static char * +chDomainSaveImageGetXMLDesc(virConnectPtr conn, + const char *path, + unsigned int flags) +{ + virCHDriver *driver =3D conn->privateData; + g_autoptr(virDomainDef) def =3D NULL; + char *ret =3D 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 =3D 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 =3D conn->privateData; + virDomainObj *vm =3D NULL; + virCHDomainObjPrivate *priv; + g_autoptr(virDomainDef) def =3D NULL; + int ret =3D -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 =3D 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 =3D 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_RESTOR= ED); + ret =3D 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 =3D dom->conn->privateData; + virDomainObj *vm =3D NULL; + g_autofree char *to =3D NULL; + int ret =3D -1; + + virCheckFlags(0, -1); + + if (!(vm =3D 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 =3D chDomainManagedSavePath(driver, vm); + if (chDoDomainSave(driver, vm, to, true) < 0) + goto endjob; + + ret =3D 0; + + endjob: + virDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int +chDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags) +{ + virCHDriver *driver =3D dom->conn->privateData; + virDomainObj *vm; + int ret =3D -1; + g_autofree char *path =3D NULL; + + virCheckFlags(0, -1); + + if (!(vm =3D virCHDomainObjFromDomain(dom))) + return -1; + + if (virDomainManagedSaveRemoveEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + path =3D chDomainManagedSavePath(driver, vm); + + if (virFileDeleteTree(path) < 0) { + virReportSystemError(errno, + _("Failed to remove managed save path '%1$s'"= ), + path); + goto cleanup; + } + + vm->hasManagedSave =3D false; + ret =3D 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static char * +chDomainManagedSaveGetXMLDesc(virDomainPtr dom, unsigned int flags) +{ + virCHDriver *driver =3D dom->conn->privateData; + virDomainObj *vm =3D NULL; + g_autoptr(virDomainDef) def =3D NULL; + char *ret =3D NULL; + g_autofree char *path =3D NULL; + + virCheckFlags(VIR_DOMAIN_SAVE_IMAGE_XML_SECURE, NULL); + + if (!(vm =3D virCHDomainObjFromDomain(dom))) + goto cleanup; + + path =3D chDomainManagedSavePath(driver, vm); + if (chDomainSaveImageRead(driver, path, &def) < 0) + goto cleanup; + + if (virDomainManagedSaveGetXMLDescEnsureACL(dom->conn, def, flags) < 0) + goto cleanup; + + ret =3D virDomainDefFormat(def, driver->xmlopt, + virDomainDefFormatConvertXMLFlags(flags)); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int +chDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags) +{ + virDomainObj *vm =3D NULL; + int ret =3D -1; + + virCheckFlags(0, -1); + + if (!(vm =3D virCHDomainObjFromDomain(dom))) + return -1; + + if (virDomainHasManagedSaveImageEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + ret =3D vm->hasManagedSave; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + static virDomainPtr chDomainLookupByID(virConnectPtr conn, int id) { @@ -1734,6 +2234,15 @@ static virHypervisorDriver chHypervisorDriver =3D { .nodeGetCPUMap =3D chNodeGetCPUMap, /* 8.0.0 */ .domainSetNumaParameters =3D chDomainSetNumaParameters, /* 8.1.0 */ .domainGetNumaParameters =3D chDomainGetNumaParameters, /* 8.1.0 */ + .domainSave =3D chDomainSave, /* 10.1.0 */ + .domainSaveFlags =3D chDomainSaveFlags, /* 10.1.0 */ + .domainSaveImageGetXMLDesc =3D chDomainSaveImageGetXMLDesc, /* 10.1.= 0 */ + .domainRestore =3D chDomainRestore, /* 10.1.0 */ + .domainRestoreFlags =3D chDomainRestoreFlags, /* 10.1.0 */ + .domainManagedSave =3D chDomainManagedSave, /* 10.1.0 */ + .domainManagedSaveRemove =3D chDomainManagedSaveRemove, /* 10.1.0 */ + .domainManagedSaveGetXMLDesc =3D chDomainManagedSaveGetXMLDesc, /* 1= 0.1.0 */ + .domainHasManagedSaveImage =3D chDomainHasManagedSaveImage, /* 10.1.= 0 */ }; =20 static virConnectDriver chConnectDriver =3D { 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, virDomain= Def *vmdef, return 0; } =20 +static int +virCHMonitorBuildKeyValueStringJson(char **jsonstr, + const char *key, + const char *value) +{ + g_autoptr(virJSONValue) content =3D virJSONValueNewObject(); + + if (virJSONValueObjectAppendString(content, key, value) < 0) + return -1; + + if (!(*jsonstr =3D virJSONValueToString(content, false))) + return -1; + + return 0; +} + static int chMonitorCreateSocket(const char *socket_path) { @@ -500,10 +516,11 @@ chMonitorCreateSocket(const char *socket_path) } =20 virCHMonitor * -virCHMonitorNew(virDomainObj *vm, const char *socketdir) +virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg) { g_autoptr(virCHMonitor) mon =3D NULL; g_autoptr(virCommand) cmd =3D NULL; + char *socketdir =3D cfg->stateDir; int socket_fd =3D 0; =20 if (virCHMonitorInitialize() < 0) @@ -527,6 +544,13 @@ virCHMonitorNew(virDomainObj *vm, const char *socketdi= r) return NULL; } =20 + if (g_mkdir_with_parents(cfg->saveDir, 0777) < 0) { + virReportSystemError(errno, + _("Cannot create save directory '%1$s'"), + cfg->saveDir); + return NULL; + } + cmd =3D virCommandNew(vm->def->emulator); virCommandSetUmask(cmd, 0x002); socket_fd =3D chMonitorCreateSocket(mon->socketpath); @@ -883,6 +907,77 @@ virCHMonitorResumeVM(virCHMonitor *mon) return virCHMonitorPutNoContent(mon, URL_VM_RESUME); } =20 +static int +virCHMonitorSaveRestoreVM(virCHMonitor *mon, const char *path, bool save) +{ + g_autofree char *url =3D NULL; + int responseCode =3D 0; + int ret =3D -1; + g_autofree char *payload =3D NULL; + g_autofree char *path_url =3D NULL; + struct curl_slist *headers =3D NULL; + struct curl_data data =3D {0}; + + if (save) + url =3D g_strdup_printf("%s/%s", URL_ROOT, URL_VM_SAVE); + else + url =3D g_strdup_printf("%s/%s", URL_ROOT, URL_VM_RESTORE); + + headers =3D curl_slist_append(headers, "Accept: application/json"); + headers =3D curl_slist_append(headers, "Content-Type: application/json= "); + + path_url =3D g_strdup_printf("file://%s", path); + if (save) { + if (virCHMonitorBuildKeyValueStringJson(&payload, "destination_url= ", path_url) !=3D 0) + return -1; + } else { + if (virCHMonitorBuildKeyValueStringJson(&payload, "source_url", pa= th_url) !=3D 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->socke= tpath); + 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 =3D virCHMonitorCurlPerform(mon->handle); + } + + if (responseCode =3D=3D 200 || responseCode =3D=3D 204) { + ret =3D 0; + } else { + data.content =3D g_realloc(data.content, data.size + 1); + data.content[data.size] =3D 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" =20 #define VIRCH_THREAD_NAME_LEN 16 =20 @@ -99,7 +101,7 @@ struct _virCHMonitor { virCHMonitorThreadInfo *threads; }; =20 -virCHMonitor *virCHMonitorNew(virDomainObj *vm, const char *socketdir); +virCHMonitor *virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg); void virCHMonitorClose(virCHMonitor *mon); G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCHMonitor, virCHMonitorClose); =20 @@ -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); =20 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 =3D NULL; virCHDriverConfig *cfg =3D virCHDriverGetConfig(driver); =20 - monitor =3D virCHMonitorNew(vm, cfg->stateDir); + monitor =3D virCHMonitorNew(vm, cfg); =20 virObjectUnref(cfg); return monitor; @@ -453,6 +453,34 @@ virCHProcessSetupVcpus(virDomainObj *vm) return 0; } =20 +static int +virCHProcessSetup(virDomainObj *vm) +{ + virCHDomainObjPrivate *priv =3D 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; +} + =20 #define PKT_TIMEOUT_MS 500 /* ms */ =20 @@ -707,26 +735,9 @@ virCHProcessStart(virCHDriver *driver, goto cleanup; } =20 - 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; =20 - virCHProcessUpdateInfo(vm); virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason); =20 return 0; @@ -785,3 +796,55 @@ virCHProcessStop(virCHDriver *driver G_GNUC_UNUSED, =20 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 =3D vm->privateData; + g_autoptr(virCHDriverConfig) cfg =3D virCHDriverGetConfig(priv->driver= ); + + if (!priv->monitor) { + /* Get the first monitor connection if not already */ + if (!(priv->monitor =3D virCHProcessConnectMonitor(driver, vm))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create connection to CH socket")); + return -1; + } + } + + vm->pid =3D priv->monitor->pid; + vm->def->id =3D vm->pid; + priv->machineName =3D 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_SNA= PSHOT); + + 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, =20 int virCHProcessSetupVcpu(virDomainObj *vm, unsigned int vcpuid); + +int virCHProcessStartRestore(virCHDriver *driver, + virDomainObj *vm, + const char *from); --=20 2.34.1 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org From nobody Thu May 9 13:21:43 2024 Delivered-To: importer@patchew.org Received-SPF: none (zohomail.com: 8.43.85.245 is neither permitted nor denied by domain of lists.libvirt.org) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; spf=none (zohomail.com: 8.43.85.245 is neither permitted nor denied by domain of lists.libvirt.org) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=linux.microsoft.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1707212523572250.65361627902337; Tue, 6 Feb 2024 01:42:03 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 766141DEE; Tue, 6 Feb 2024 04:42:02 -0500 (EST) Received: from lists.libvirt.org.85.43.8.in-addr.arpa (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id A4FAE1E07; Tue, 6 Feb 2024 04:34:55 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id 8BFE21CE8; Tue, 6 Feb 2024 04:34:34 -0500 (EST) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by lists.libvirt.org (Postfix) with ESMTP id 528481CFC for ; Tue, 6 Feb 2024 04:34:31 -0500 (EST) Received: from paekkaladevi-dev-u22.gi4irqh5pfqublruu4yyku2wof.phxx.internal.cloudapp.net (unknown [20.125.125.171]) by linux.microsoft.com (Postfix) with ESMTPSA id 3B3C120B2002; Tue, 6 Feb 2024 01:34:30 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 3B3C120B2002 From: Purna Pavan Chandra Aekkaladevi To: devel@lists.libvirt.org Subject: [PATCH 2/2] NEWS: Mention save & restore support for ch driver Date: Tue, 6 Feb 2024 09:34:28 +0000 Message-Id: <20240206093428.18359-3-paekkaladevi@linux.microsoft.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240206093428.18359-1-paekkaladevi@linux.microsoft.com> References: <20240206093428.18359-1-paekkaladevi@linux.microsoft.com> MIME-Version: 1.0 Message-ID-Hash: F4E7MBEEKC5DLXSGWO7OGAYV4LWC2QHV X-Message-ID-Hash: F4E7MBEEKC5DLXSGWO7OGAYV4LWC2QHV X-MailFrom: paekkaladevi@linux.microsoft.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: paekkaladevi@microsoft.com, prapal@linux.microsoft.com, kumarpraveen@linux.microsoft.com, Purna Pavan Chandra Aekkaladevi X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1707212524937100001 Signed-off-by: Purna Pavan Chandra Aekkaladevi --- NEWS.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index e2796fd8b2..6a2c69b2c4 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -23,6 +23,12 @@ v10.1.0 (unreleased) Additionally, if CPU clusters are present in the host topology, they w= ill be reported as part of the capabilities XML. =20 + * ch: Basic save and restore support for ch driver + + The ch driver now supports basic save and restore operations. This i= s functional + on domains without any network, host device config defined. The `pat= h` parameter + for save and restore should be a directory. + * **Improvements** =20 * **Bug fixes** --=20 2.34.1 _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org