Add support for saving the state of lxc domain into a set of files.
Usage:
virsh save [domain-name or domain-uuid] [path]
virsh restore [path]
Example:
virsh -c lxc:// save container1 /tmp/container1-dump
virsh -c lxc:// restore /tmp/container1-dump
Signed-off-by: Radostin Stoyanov <rstoyanov1(a)gmail.com>
---
src/lxc/lxc_driver.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 234 insertions(+)
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index f52085ebf..871e853a0 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -81,6 +81,7 @@
#include "virhostdev.h"
#include "netdev_bandwidth_conf.h"
+#include "lxc_criu.h"
#define VIR_FROM_THIS VIR_FROM_LXC
VIR_LOG_INIT("lxc.lxc_driver");
@@ -3198,6 +3199,235 @@ static int lxcDomainResume(virDomainPtr dom)
return ret;
}
+static int
+lxcDoDomainSave(virLXCDriverPtr driver, virDomainObjPtr vm,
+ const char *to)
+{
+ int ret = -1;
+ virCapsPtr caps = NULL;
+ uint32_t xml_len = -1;
+ char *xml = NULL;
+ char xmlLen[33];
+ char *xml_image_path = NULL;
+ char *str = NULL;
+
+ if (!(caps = virLXCDriverGetCapabilities(driver, false)))
+ goto cleanup;
+
+ if ((xml = virDomainDefFormat(vm->def, caps, 0)) == NULL)
+ goto cleanup;
+
+ if ((ret = lxcCriuDump(driver, vm, to)) != 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Failed to checkpoint domain with CRIU"));
+ goto cleanup;
+ }
+
+ virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF,
+ VIR_DOMAIN_SHUTOFF_SAVED);
+
+ if (virAsprintf(&xml_image_path, "%s/xml-image", to) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Failed to write image path"));
+ goto cleanup;
+ }
+
+ xml_len = strlen(xml) + 1;
+ snprintf(xmlLen, sizeof(xmlLen), "%d", xml_len);
+ VIR_DEBUG("xmlLen = %d %s", xml_len, xmlLen);
+
+ if (virAsprintf(&str, "%s\n%s", xmlLen, xml) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Failed to write xml info to string"));
+ goto cleanup;
+ }
+
+ if (virFileWriteStr(xml_image_path, str, 0666) < 0) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("Failed to write xml description to %s"),
+ xml_image_path);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(xml);
+ VIR_FREE(xml_image_path);
+ VIR_FREE(str);
+ return ret != 0 ? -1 : ret;
+}
+
+static int
+lxcDomainSaveFlags(virDomainPtr dom, const char *to, const char *dxml,
+ unsigned int flags)
+{
+ int ret = -1;
+ virLXCDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ bool remove_dom = false;
+
+ virCheckFlags(0, -1);
+ if (dxml) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("xml modification unsupported"));
+ return -1;
+ }
+
+ if (!(vm = lxcDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainSaveFlagsEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (virLXCDomainObjBeginJob(driver, vm, LXC_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("Domain is not running"));
+ goto endjob;
+ }
+
+ if (lxcDoDomainSave(driver, vm, to) < 0)
+ goto endjob;
+
+ if (!vm->persistent)
+ remove_dom = true;
+
+ ret = 0;
+
+ endjob:
+ virLXCDomainObjEndJob(driver, vm);
+
+ cleanup:
+ if (remove_dom && vm)
+ virDomainObjListRemove(driver->domains, vm);
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int
+lxcDomainSave(virDomainPtr dom, const char *to)
+{
+ return lxcDomainSaveFlags(dom, to, NULL, 0);
+}
+
+static int
+lxcDomainRestoreFlags(virConnectPtr conn, const char *from,
+ const char* dxml, unsigned int flags)
+{
+ virLXCDriverPtr driver = conn->privateData;
+ virDomainObjPtr vm = NULL;
+ virDomainDefPtr def = NULL;
+ virCapsPtr caps = NULL;
+ int ret = -1;
+ int restorefd;
+ char *xml = NULL;
+ u_int32_t xmlLen = 0;
+ int fd;
+ char *xml_image_path = NULL;
+ char c;
+
+ virCheckFlags(0, -1);
+ if (dxml) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("xml modification unsupported"));
+ goto out;
+ }
+
+ if (virAsprintf(&xml_image_path, "%s/xml-image", from) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Failed to write xml image path"));
+ goto out;
+ }
+
+ if ((fd = virFileOpenAs(xml_image_path, O_RDONLY, 0, -1, -1, 0)) < 0) {
+ virReportSystemError(-fd,
+ _("Failed to open domain image file
'%s'"),
+ xml_image_path);
+ goto out;
+ }
+
+ while ((saferead(fd, &c, 1) == 1) && c != '\n')
+ xmlLen = xmlLen*10 + c - '0';
+ xmlLen--;
+
+ if (VIR_ALLOC_N(xml, xmlLen) < 0)
+ goto cleanup;
+
+ if (saferead(fd, xml, xmlLen) != xmlLen) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to read
XML"));
+ goto cleanup;
+ }
+
+ if (!xml) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("no domain XML parsed"));
+ goto cleanup;
+ }
+
+ if (!(caps = virLXCDriverGetCapabilities(driver, false)))
+ goto cleanup;
+
+ if (!(def = virDomainDefParseString(xml, caps, driver->xmlopt,
+ NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE)))
+ 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;
+ def = NULL;
+
+ if (virLXCDomainObjBeginJob(driver, vm, LXC_JOB_MODIFY) < 0) {
+ if (!vm->persistent)
+ virDomainObjListRemove(driver->domains, vm);
+ goto cleanup;
+ }
+
+ restorefd = open(from, O_DIRECTORY);
+ if (restorefd < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Can't open images dir"));
+ if (!vm->persistent)
+ virDomainObjListRemove(driver->domains, vm);
+ virLXCDomainObjEndJob(driver, vm);
+ goto cleanup;
+ }
+
+ ret = virLXCProcessStart(conn, driver, vm,
+ 0, NULL,
+ 0, restorefd,
+ VIR_DOMAIN_RUNNING_RESTORED);
+
+ VIR_FORCE_CLOSE(restorefd);
+
+ if (ret < 0 && !vm->persistent)
+ virDomainObjListRemove(driver->domains, vm);
+
+ virLXCDomainObjEndJob(driver, vm);
+ cleanup:
+ VIR_FORCE_CLOSE(fd);
+ out:
+ virDomainDefFree(def);
+ VIR_FREE(xml_image_path);
+ VIR_FREE(xml);
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int
+lxcDomainRestore(virConnectPtr conn, const char *from)
+{
+ return lxcDomainRestoreFlags(conn, from, NULL, 0);
+}
+
static int
lxcDomainOpenConsole(virDomainPtr dom,
const char *dev_name,
@@ -5550,6 +5780,10 @@ static virHypervisorDriver lxcHypervisorDriver = {
.domainLookupByName = lxcDomainLookupByName, /* 0.4.2 */
.domainSuspend = lxcDomainSuspend, /* 0.7.2 */
.domainResume = lxcDomainResume, /* 0.7.2 */
+ .domainSave = lxcDomainSave, /* x.x.x */
+ .domainSaveFlags = lxcDomainSaveFlags, /* x.x.x */
+ .domainRestore = lxcDomainRestore, /* x.x.x */
+ .domainRestoreFlags = lxcDomainRestoreFlags, /* x.x.x */
.domainDestroy = lxcDomainDestroy, /* 0.4.4 */
.domainDestroyFlags = lxcDomainDestroyFlags, /* 0.9.4 */
.domainGetOSType = lxcDomainGetOSType, /* 0.4.2 */
--
2.14.3