Folks.
I'm working on a scenario where I would like to provide my backup subsystems with a clean disk and ram state of any given VM, without necessitating shutting down the a VM.
Xen's 'xm save' function with the '-c' (checkpoint) argument made sense in that I could checkpoint save the VM, take a snapshot of the LV containing the domain and its 'save' dump then resume the domain. Unfortunately out-of-the-box the Xen implementation did not pause the domain after the checkpoint, but resumed it automatically. Fastforward to using kvm ...
Qemu/kvm with it's 'migrate' command by default left the domain paused after creating a state dump - just what I was looking for. A look at the libvirt code revealed that 'virsh save' carried out a migrate, then shut the domain down. So I have tried implementing a --checkpoint option into the save function using the --live option to the 'virsh migrate' function as a bit of a template.
I'm a sysadmin, not a coder, so please forgive any oversights and all the other coding rules that may be broken. As mentioned before I did try to simply copy how the migrate --live option is used throughout.
Patch (against 0.6.0) attached. I have also filed a feature request under bugzilla. If the additional feature is seen as warranted, please let me know what I can do to help implement it if the patch is not the way to go about it.
Thankyou very much for Libvirt
Regards
Matt McCowan
--- ./include/libvirt/libvirt.h.in 2009-01-20 22:48:27.000000000 +0900
+++ ../libvirt-0.6.0.1/./include/libvirt/libvirt.h.in 2009-02-11 14:34:38.000000000 +0900
@@ -464,8 +464,14 @@
int virDomainResume (virDomainPtr domain);
+/* Domain save flags. */
+typedef enum {
+ VIR_SAVE_CHECKPOINT = 1, /* checkpoint save */
+} virDomainSaveFlags;
+
/*
* Domain save/restore
*/
int virDomainSave (virDomainPtr domain,
+ unsigned long flags,
const char *to);
int virDomainRestore (virConnectPtr conn,
--- ./src/qemu_driver.c 2009-01-31 18:04:18.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/qemu_driver.c 2009-02-11 13:39:30.000000000 +0900
@@ -2357,4 +2357,5 @@
static int qemudDomainSave(virDomainPtr dom,
+ unsigned long flags ATTRIBUTE_UNUSED,
const char *path) {
struct qemud_driver *driver = dom->conn->privateData;
@@ -2470,13 +2471,15 @@
}
- /* Shut it down */
- qemudShutdownVMDaemon(dom->conn, driver, vm);
- event = virDomainEventNewFromObj(vm,
+ if (!(flags & VIR_SAVE_CHECKPOINT)) {
+ /* Shut it down */
+ qemudShutdownVMDaemon(dom->conn, driver, vm);
+ event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SAVED);
- if (!vm->persistent) {
- virDomainRemoveInactive(&driver->domains,
+ if (!vm->persistent) {
+ virDomainRemoveInactive(&driver->domains,
vm);
- vm = NULL;
+ vm = NULL;
+ }
}
ret = 0;
--- ./src/remote_internal.c 2009-01-31 18:04:18.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/remote_internal.c 2009-02-11 14:08:23.000000000 +0900
@@ -2070,5 +2070,5 @@
static int
-remoteDomainSave (virDomainPtr domain, const char *to)
+remoteDomainSave (virDomainPtr domain, unsigned long flags, const char *to)
{
int rv = -1;
@@ -2080,4 +2080,5 @@
make_nonnull_domain (&args.dom, domain);
args.to = (char *) to;
+ args.flags = flags;
if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_SAVE,
--- ./src/xend_internal.h 2008-12-19 21:51:12.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/xend_internal.h 2009-02-12 12:35:55.000000000 +0900
@@ -141,5 +141,6 @@
int xenDaemonDomainReboot(virDomainPtr domain, unsigned int flags);
int xenDaemonDomainDestroy(virDomainPtr domain);
-int xenDaemonDomainSave(virDomainPtr domain, const char *filename);
+int xenDaemonDomainSave(virDomainPtr domain, unsigned long flags,
+ const char *filename);
int xenDaemonDomainRestore(virConnectPtr conn, const char *filename);
int xenDaemonDomainSetMemory(virDomainPtr domain, unsigned long memory);
--- ./src/libvirt.c 2009-01-31 18:04:17.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/libvirt.c 2009-02-12 12:37:20.000000000 +0900
@@ -1931,4 +1931,5 @@
* virDomainSave:
* @domain: a domain object
+ * @flags: flags
* @to: path for the output file
*
@@ -1937,13 +1938,16 @@
* listed as running anymore (this may be a problem).
* Use virDomainRestore() to restore a domain after saving.
+ * Flags may be one of more of the following:
+ * VIR_SAVE_CHECKPOINT Don't exit after save
*
* Returns 0 in case of success and -1 in case of failure.
*/
int
-virDomainSave(virDomainPtr domain, const char *to)
+virDomainSave(virDomainPtr domain, unsigned long flags,
+ const char *to)
{
char filepath[4096];
virConnectPtr conn;
- DEBUG("domain=%p, to=%s", domain, to);
+ DEBUG("domain=%p, flags=%lu, to=%s", domain, flags, to);
virResetLastError();
@@ -1985,5 +1989,5 @@
if (conn->driver->domainSave) {
int ret;
- ret = conn->driver->domainSave (domain, to);
+ ret = conn->driver->domainSave (domain, flags, to);
if (ret < 0)
goto error;
--- ./src/test.c 2009-01-21 05:39:28.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/test.c 2009-02-12 12:37:40.000000000 +0900
@@ -1180,5 +1180,5 @@
#define TEST_SAVE_MAGIC "TestGuestMagic"
-static int testDomainSave(virDomainPtr domain,
+static int testDomainSave(virDomainPtr domain, unsigned long flags,
const char *path)
{
@@ -1210,6 +1210,6 @@
if ((fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) {
virReportSystemError(domain->conn, errno,
- _("saving domain '%s' to '%s': open failed"),
- domain->name, path);
+ _("saving domain '%s' with '%lu' to '%s': open failed"),
+ domain->name, flags, path);
goto cleanup;
}
@@ -1217,18 +1217,18 @@
if (safewrite(fd, TEST_SAVE_MAGIC, sizeof(TEST_SAVE_MAGIC)) < 0) {
virReportSystemError(domain->conn, errno,
- _("saving domain '%s' to '%s': write failed"),
- domain->name, path);
+ _("saving domain '%s' with '%lu' to '%s': write failed"),
+ domain->name, flags, path);
goto cleanup;
}
if (safewrite(fd, (char*)&len, sizeof(len)) < 0) {
virReportSystemError(domain->conn, errno,
- _("saving domain '%s' to '%s': write failed"),
- domain->name, path);
+ _("saving domain '%s' with '%lu' to '%s': write failed"),
+ domain->name, flags, path);
goto cleanup;
}
if (safewrite(fd, xml, len) < 0) {
virReportSystemError(domain->conn, errno,
- _("saving domain '%s' to '%s': write failed"),
- domain->name, path);
+ _("saving domain '%s' with '%lu' to '%s': write failed"),
+ domain->name, flags, path);
goto cleanup;
}
@@ -1236,6 +1236,6 @@
if (close(fd) < 0) {
virReportSystemError(domain->conn, errno,
- _("saving domain '%s' to '%s': write failed"),
- domain->name, path);
+ _("saving domain '%s' with '%lu' to '%s': write failed"),
+ domain->name, flags, path);
goto cleanup;
}
--- ./src/xen_unified.c 2009-01-31 18:04:18.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/xen_unified.c 2009-02-11 13:41:07.000000000 +0900
@@ -901,5 +901,5 @@
static int
-xenUnifiedDomainSave (virDomainPtr dom, const char *to)
+xenUnifiedDomainSave (virDomainPtr dom, unsigned long flags, const char *to)
{
GET_PRIVATE(dom->conn);
--- ./src/driver.h 2008-12-19 21:51:11.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/driver.h 2009-02-11 13:02:16.000000000 +0900
@@ -141,4 +141,5 @@
typedef int
(*virDrvDomainSave) (virDomainPtr domain,
+ unsigned long flags,
const char *to);
typedef int
--- ./src/virsh.c 2009-01-31 18:04:18.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/virsh.c 2009-02-12 12:19:53.000000000 +0900
@@ -1044,9 +1044,10 @@
static const vshCmdInfo info_save[] = {
{"help", gettext_noop("save a domain state to a file")},
- {"desc", gettext_noop("Save a running domain.")},
+ {"desc", gettext_noop("Save a running domain. Add --checkpoint to not quit")},
{NULL, NULL}
};
static const vshCmdOptDef opts_save[] = {
+ {"checkpoint", VSH_OT_BOOL, 0, gettext_noop("checkpoint save")},
{"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
{"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("where to save the data")},
@@ -1060,4 +1061,5 @@
char *name;
char *to;
+ int flags = 0;
int ret = TRUE;
@@ -1070,9 +1072,12 @@
if (!(dom = vshCommandOptDomain(ctl, cmd, &name)))
return FALSE;
+
+ if (vshCommandOptBool (cmd, "checkpoint"))
+ flags |= VIR_SAVE_CHECKPOINT;
- if (virDomainSave(dom, to) == 0) {
+ if (virDomainSave(dom, flags, to) == 0) {
vshPrint(ctl, _("Domain %s saved to %s\n"), name, to);
} else {
- vshError(ctl, FALSE, _("Failed to save domain %s to %s"), name, to);
+ vshError(ctl, FALSE, _("Failed to save domain %s with %lu to %s"), name, flags, to);
ret = FALSE;
}
--- ./src/xend_internal.c 2009-01-31 18:04:18.000000000 +0900
+++ ../libvirt-0.6.0.1/./src/xend_internal.c 2009-02-12 12:41:54.000000000 +0900
@@ -2974,4 +2974,5 @@
* xenDaemonDomainSave:
* @domain: pointer to the Domain block
+ * @flags: flags
* @filename: path for the output file
*
@@ -2985,5 +2986,7 @@
*/
int
-xenDaemonDomainSave(virDomainPtr domain, const char *filename)
+xenDaemonDomainSave(virDomainPtr domain,
+ unsigned long flags ATTRIBUTE_UNUSED,
+ const char *filename)
{
if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL) ||
--- ./qemud/remote_protocol.x 2008-12-19 21:51:11.000000000 +0900
+++ ../libvirt-0.6.0.1/./qemud/remote_protocol.x 2009-02-11 14:07:10.000000000 +0900
@@ -481,4 +481,5 @@
struct remote_domain_save_args {
remote_nonnull_domain dom;
+ unsigned hyper flags;
remote_nonnull_string to;
};
--- ./qemud/remote_protocol.h 2009-01-31 18:04:17.000000000 +0900
+++ ../libvirt-0.6.0.1/./qemud/remote_protocol.h 2009-02-11 14:05:38.000000000 +0900
@@ -447,4 +447,5 @@
struct remote_domain_save_args {
remote_nonnull_domain dom;
+ uint64_t flags;
remote_nonnull_string to;
};
--- ./qemud/remote_protocol.c 2009-01-31 18:04:17.000000000 +0900
+++ ../libvirt-0.6.0.1/./qemud/remote_protocol.c 2009-02-11 14:06:32.000000000 +0900
@@ -856,4 +856,6 @@
if (!xdr_remote_nonnull_domain (xdrs, &objp->dom))
return FALSE;
+ if (!xdr_uint64_t (xdrs, &objp->flags))
+ return FALSE;
if (!xdr_remote_nonnull_string (xdrs, &objp->to))
return FALSE;
--- ./qemud/remote.c 2009-01-31 18:04:17.000000000 +0900
+++ ../libvirt-0.6.0.1/./qemud/remote.c 2009-02-11 12:18:54.000000000 +0900
@@ -1857,5 +1857,5 @@
}
- if (virDomainSave (dom, args->to) == -1) {
+ if (virDomainSave (dom, args->flags, args->to) == -1) {
virDomainFree(dom);
remoteDispatchConnError(rerr, conn);
--- ./docs/libvirt-refs.xml 2009-01-31 18:20:13.000000000 +0900
+++ ../libvirt-0.6.0.1/./docs/libvirt-refs.xml 2009-02-11 10:25:58.000000000 +0900
@@ -2876,4 +2876,7 @@
<ref name='virConnectListStoragePools'/>
</word>
+ <word name='checkpoint'>
+ <ref name='virDomainSave'/>
+ </word>
<word name='choose'>
<ref name='virDomainMigrate'/>
--- ./docs/libvirt-api.xml 2009-01-31 18:20:13.000000000 +0900
+++ ../libvirt-0.6.0.1/./docs/libvirt-api.xml 2009-02-11 15:12:40.000000000 +0900
@@ -41,4 +41,5 @@
<exports symbol='VIR_DOMAIN_EVENT_UNDEFINED' type='enum'/>
<exports symbol='VIR_MIGRATE_LIVE' type='enum'/>
+ <exports symbol='VIR_SAVE_CHECKPOINT' type='enum'/>
<exports symbol='VIR_DOMAIN_EVENT_STOPPED_DESTROYED' type='enum'/>
<exports symbol='VIR_DOMAIN_EVENT_DEFINED_ADDED' type='enum'/>
@@ -594,4 +595,5 @@
<enum name='VIR_MEMORY_VIRTUAL' file='libvirt' value='1' type='virDomainMemoryFlags' info=' addresses are virtual addresses'/>
<enum name='VIR_MIGRATE_LIVE' file='libvirt' value='1' type='virDomainMigrateFlags' info=' live migration'/>
+ <enum name='VIR_SAVE_CHECKPOINT' file='libvirt' value='1' type='virDomainSaveFlags' info=' checkpoint save'/>
<enum name='VIR_STORAGE_POOL_BUILDING' file='libvirt' value='1' type='virStoragePoolState' info='Initializing pool, not available'/>
<enum name='VIR_STORAGE_POOL_BUILD_NEW' file='libvirt' value='0' type='virStoragePoolBuildFlags' info='Regular build from scratch'/>
@@ -1208,7 +1210,8 @@
</function>
<function name='virDomainSave' file='libvirt' module='libvirt'>
- <info>This method will suspend a domain and save its memory contents to a file on disk. After the call, if successful, the domain is not listed as running anymore (this may be a problem). Use virDomainRestore() to restore a domain after saving.</info>
+ <info>This method will suspend a domain and save its memory contents to a file on disk. Flags may be one of more of the following: VIR_SAVE_CHECKPOINT. After the call, if successful, the domain is not listed as running anymore (this may be a problem). Use virDomainRestore() to restore a domain after saving.</info>
<return type='int' info='0 in case of success and -1 in case of failure.'/>
<arg name='domain' type='virDomainPtr' info='a domain object'/>
+ <arg name='flags' type='unsigned long' info='save flags'/>
<arg name='to' type='const char *' info='path for the output file'/>
</function>
--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list