On Wed, Mar 27, 2019 at 05:10:47 -0500, Eric Blake wrote:
Introduce a few more new virsh commands for performing backup jobs,
as
well as creating a checkpoint atomically with a snapshot.
At this time, I did not opt for a convenience command
'backup-begin-as' that cobbles together appropriate XML from the
user's command line arguments, but that may be a viable future
extension. Similarly, since backup is a potentially long-running
operation, it might be nice to add some sugar that automatically
handles waiting for the job to end, rather than making the user have
to poll or figure out virsh event to do the same.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
tools/virsh-domain.c | 247 ++++++++++++++++++++++++++++++++++++++++-
tools/virsh-snapshot.c | 37 +++++-
tools/virsh.pod | 64 ++++++++++-
3 files changed, 337 insertions(+), 11 deletions(-)
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 1970710c07..4ae456146b 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -1,7 +1,7 @@
/*
* virsh-domain.c: Commands to manage domain
*
- * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
+ * Copyright (C) 2005-2019 Red Hat, Inc.
This seems wrong. It's adding dates in the past. Ideally you disable
that script for libvirt altogether as it creates pointless churn.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
[1] Using 'check' instead of 'checkpoint' in variable names may be
misleading below in multiple instances. It might evoke that it's
supposed to be checked rather than referring to a checkpoint.
@@ -14039,6 +14039,233 @@ cmdDomFSInfo(vshControl *ctl, const vshCmd
*cmd)
return ret;
}
+
+/*
+ * "backup-begin" command
+ */
+static const vshCmdInfo info_backup_begin[] = {
+ {.name = "help",
+ .data = N_("Start a disk backup of a live domain")
+ },
+ {.name = "desc",
+ .data = N_("Use XML to start a full or incremental disk backup of a live
"
+ "domain, optionally creating a checkpoint")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_backup_begin[] = {
+ VIRSH_COMMON_OPT_DOMAIN_FULL(0),
+ {.name = "xmlfile",
+ .type = VSH_OT_STRING,
+ .help = N_("domain backup XML"),
+ },
+ {.name = "checkpointxml",
+ .type = VSH_OT_STRING,
+ .help = N_("domain checkpoint XML"),
+ },
+ {.name = "no-metadata",
+ .type = VSH_OT_BOOL,
+ .help = N_("create checkpoint but don't track metadata"),
+ },
+ {.name = "quiesce",
+ .type = VSH_OT_BOOL,
+ .help = N_("quiesce guest's file systems"),
+ },
+ /* TODO: --wait/--verbose/--timeout flags for push model backups? */
+ {.name = NULL}
+};
+
+static bool
+cmdBackupBegin(vshControl *ctl,
+ const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ bool ret = false;
+ const char *backup_from = NULL;
+ char *backup_buffer = NULL;
+ const char *check_from = NULL;
+ char *check_buffer = NULL;
[1]
+ unsigned int flags = 0;
+ int id;
+
+ if (vshCommandOptBool(cmd, "no-metadata"))
+ flags |= VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA;
+ if (vshCommandOptBool(cmd, "quiesce"))
+ flags |= VIR_DOMAIN_BACKUP_BEGIN_QUIESCE;
+
+ if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
+ goto cleanup;
+
+ if (vshCommandOptStringReq(ctl, cmd, "xmlfile", &backup_from) < 0)
"backupxml"
+ goto cleanup;
+ if (!backup_from) {
+ backup_buffer = vshStrdup(ctl, "<domainbackup/>");
+ } else {
+ if (virFileReadAll(backup_from, VSH_MAX_XML_FILE, &backup_buffer) < 0) {
+ vshSaveLibvirtError();
+ goto cleanup;
+ }
+ }
+
+ if (vshCommandOptStringReq(ctl, cmd, "checkpointxml", &check_from)
< 0)
+ goto cleanup;
+ if (check_from) {
+ if (virFileReadAll(check_from, VSH_MAX_XML_FILE, &check_buffer) < 0) {
+ vshSaveLibvirtError();
+ goto cleanup;
+ }
+ }
+
+ id = virDomainBackupBegin(dom, backup_buffer, check_buffer, flags);
+
+ if (id < 0)
+ goto cleanup;
+
+ vshPrint(ctl, _("Backup id %d started\n"), id);
+ if (backup_from)
+ vshPrintExtra(ctl, _("backup used description from '%s'\n"),
+ backup_from);
+ if (check_buffer)
+ vshPrintExtra(ctl, _("checkpoint created from '%s'\n"),
check_from);
+
+ ret = true;
+
+ cleanup:
+ VIR_FREE(backup_buffer);
+ VIR_FREE(check_buffer);
+ virshDomainFree(dom);
+
+ return ret;
+}
+
+/* TODO: backup-begin-as? */
+
+/*
+ * "backup-dumpxml" command
+ */
+static const vshCmdInfo info_backup_dumpxml[] = {
+ {.name = "help",
+ .data = N_("Dump XML for an ongoing domain block backup job")
+ },
+ {.name = "desc",
+ .data = N_("Backup Dump XML")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_backup_dumpxml[] = {
+ VIRSH_COMMON_OPT_DOMAIN_FULL(0),
+ {.name = "id",
+ .type = VSH_OT_INT,
+ .help = N_("backup job id"),
+ /* TODO: Add API for listing active jobs, then adding a completer? */
+ },
+ /* TODO - worth adding this flag?
+ {.name = "checkpoint",
+ .type = VSH_OT_BOOL,
+ .help = N_("if the backup created a checkpoint, also dump that XML")
+ },
+ */
+ {.name = NULL}
+};
+
+static bool
+cmdBackupDumpXML(vshControl *ctl,
+ const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ bool ret = false;
+ char *xml = NULL;
+ unsigned int flags = 0;
+ int id = 0;
+
+ if (vshCommandOptBool(cmd, "security-info"))
This option is not mentioned in opts_backup_dumpxml.
+ flags |= VIR_DOMAIN_XML_SECURE;
+
+ if (vshCommandOptInt(ctl, cmd, "id", &id) < 0)
+ return false;
+
+ if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (!(xml = virDomainBackupGetXMLDesc(dom, id, flags)))
+ goto cleanup;
+
+ vshPrint(ctl, "%s", xml);
+ ret = true;
+
+ cleanup:
+ VIR_FREE(xml);
+ virshDomainFree(dom);
+
+ return ret;
+}
[...]
diff --git a/tools/virsh-snapshot.c b/tools/virsh-snapshot.c
index f6bb38bc96..57ab80251b 100644
--- a/tools/virsh-snapshot.c
+++ b/tools/virsh-snapshot.c
@@ -40,18 +40,26 @@
/* Helper for snapshot-create and snapshot-create-as */
static bool
-virshSnapshotCreate(vshControl *ctl, virDomainPtr dom, const char *buffer,
- unsigned int flags, const char *from)
+virshSnapshotCreate(vshControl *ctl,
+ virDomainPtr dom,
+ const char *buffer,
+ const char *check_buffer,
+ unsigned int flags,
+ const char *from)
{
bool ret = false;
virDomainSnapshotPtr snapshot;
bool halt = false;
const char *name = NULL;
- snapshot = virDomainSnapshotCreateXML(dom, buffer, flags);
+ if (check_buffer)
[1]
+ snapshot = virDomainSnapshotCreateXML2(dom, buffer,
check_buffer,
+ flags);
+ else
+ snapshot = virDomainSnapshotCreateXML(dom, buffer, flags);
/* Emulate --halt on older servers. */
- if (!snapshot && last_error->code == VIR_ERR_INVALID_ARG &&
+ if (!check_buffer && !snapshot && last_error->code ==
VIR_ERR_INVALID_ARG &&
(flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT)) {
int persistent;
@@ -117,6 +125,10 @@ static const vshCmdOptDef opts_snapshot_create[] = {
.type = VSH_OT_STRING,
.help = N_("domain snapshot XML")
},
+ {.name = "checkpointxml",
+ .type = VSH_OT_STRING,
+ .help = N_("domain checkpoint XML"),
+ },
{.name = "redefine",
.type = VSH_OT_BOOL,
.help = N_("redefine metadata for existing snapshot")
@@ -157,7 +169,9 @@ cmdSnapshotCreate(vshControl *ctl, const vshCmd *cmd)
bool ret = false;
const char *from = NULL;
char *buffer = NULL;
+ const char *check_from = NULL;
[...]
unsigned int flags = 0;
+ VIR_AUTOFREE(char *check_buffer) = NULL;
VIR_AUTOFREE and legacy code is used inconsistently in this patch.
if (vshCommandOptBool(cmd, "redefine"))
flags |= VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE;