On Wed, Apr 17, 2019 at 09:09:12 -0500, Eric Blake wrote: > Introduce a bunch of new virsh commands for managing checkpoints > in isolation. More commands are needed for performing incremental > backups, but these commands were easy to implement by modeling > heavily after virsh-snapshot.c (no need for checkpoint-revert, > and checkpoint-list was a lot easier since we don't have to cater > to older libvirt API). > > Signed-off-by: Eric Blake <eblake@xxxxxxxxxx> > --- > tools/virsh-checkpoint.h | 29 + > tools/virsh-completer.h | 4 + > tools/virsh-util.h | 3 + > tools/virsh.h | 1 + > po/POTFILES | 1 + > tools/Makefile.am | 1 + > tools/virsh-checkpoint.c | 1370 ++++++++++++++++++++++++++++++++++ > tools/virsh-completer.c | 51 ++ > tools/virsh-domain-monitor.c | 23 + > tools/virsh-domain.c | 15 + > tools/virsh-util.c | 11 + > tools/virsh.c | 2 + > tools/virsh.pod | 238 +++++- > 13 files changed, 1742 insertions(+), 7 deletions(-) > create mode 100644 tools/virsh-checkpoint.h > create mode 100644 tools/virsh-checkpoint.c [...] > diff --git a/tools/virsh-checkpoint.c b/tools/virsh-checkpoint.c > new file mode 100644 > index 0000000000..3089383dc5 > --- /dev/null > +++ b/tools/virsh-checkpoint.c > @@ -0,0 +1,1370 @@ > +/* > + * virsh-checkpoint.c: Commands to manage domain checkpoints > + * > + * Copyright (C) 2005-2019 Red Hat, Inc. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library. If not, see > + * <http://www.gnu.org/licenses/>. > + * > + * Daniel Veillard <veillard@xxxxxxxxxx> > + * Karel Zak <kzak@xxxxxxxxxx> > + * Daniel P. Berrange <berrange@xxxxxxxxxx> > + * > + */ > + > +#include <config.h> > +#include "virsh-checkpoint.h" > + > +#include <assert.h> > + > +#include <libxml/parser.h> > +#include <libxml/tree.h> > +#include <libxml/xpath.h> > +#include <libxml/xmlsave.h> > + > +#include "internal.h" > +#include "virbuffer.h" > +#include "viralloc.h" > +#include "virfile.h" > +#include "virsh-util.h" > +#include "virstring.h" > +#include "virxml.h" > +#include "conf/checkpoint_conf.h" > + > +/* Helper for checkpoint-create and checkpoint-create-as */ > +static bool > +virshCheckpointCreate(vshControl *ctl, > + virDomainPtr dom, > + const char *buffer, > + unsigned int flags, > + const char *from) > +{ > + bool ret = false; > + virDomainCheckpointPtr checkpoint; > + const char *name = NULL; > + > + checkpoint = virDomainCheckpointCreateXML(dom, buffer, flags); > + > + if (checkpoint == NULL) > + goto cleanup; > + > + name = virDomainCheckpointGetName(checkpoint); > + if (!name) { > + vshError(ctl, "%s", _("Could not get snapshot name")); Some copypaste leftovers. > + goto cleanup; > + } > + > + if (from) > + vshPrintExtra(ctl, _("Domain checkpoint %s created from '%s'"), > + name, from); > + else > + vshPrintExtra(ctl, _("Domain checkpoint %s created"), name); > + > + ret = true; > + > + cleanup: > + virshDomainCheckpointFree(checkpoint); > + return ret; > +} > + > + > +/* [...] > + > +static bool > +cmdCheckpointCreate(vshControl *ctl, > + const vshCmd *cmd) > +{ > + virDomainPtr dom = NULL; > + bool ret = false; > + const char *from = NULL; > + char *buffer = NULL; > + unsigned int flags = 0; > + > + if (vshCommandOptBool(cmd, "redefine")) > + flags |= VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE; > + if (vshCommandOptBool(cmd, "current")) > + flags |= VIR_DOMAIN_CHECKPOINT_CREATE_CURRENT; > + if (vshCommandOptBool(cmd, "no-metadata")) > + flags |= VIR_DOMAIN_CHECKPOINT_CREATE_NO_METADATA; > + if (vshCommandOptBool(cmd, "quiesce")) > + flags |= VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE; leftovers > + > + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) > + goto cleanup; > + > + if (vshCommandOptStringReq(ctl, cmd, "xmlfile", &from) < 0) > + goto cleanup; > + if (!from) { > + buffer = vshStrdup(ctl, "<domaincheckpoint/>"); > + } else { > + if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0) { > + vshSaveLibvirtError(); > + goto cleanup; > + } > + } > + > + ret = virshCheckpointCreate(ctl, dom, buffer, flags, from); > + > + cleanup: > + VIR_FREE(buffer); > + virshDomainFree(dom); > + > + return ret; > +} > + > + [...] > +/* Helper for resolving {--current | --ARG name} into a checkpoint > + * belonging to DOM. If EXCLUSIVE, fail if both --current and arg are > + * present. On success, populate *CHK and *NAME, before returning 0. > + * On failure, return -1 after issuing an error message. */ > +static int > +virshLookupCheckpoint(vshControl *ctl, > + const vshCmd *cmd, > + const char *arg, > + bool exclusive, > + virDomainPtr dom, > + virDomainCheckpointPtr *chk, > + const char **name) > +{ > + bool current = vshCommandOptBool(cmd, "current"); > + const char *chkname = NULL; > + > + if (vshCommandOptStringReq(ctl, cmd, arg, &chkname) < 0) > + return -1; > + > + if (exclusive && current && chkname) { > + vshError(ctl, _("--%s and --current are mutually exclusive"), arg); > + return -1; > + } > + > + if (chkname) { > + *chk = virDomainCheckpointLookupByName(dom, chkname, 0); > + } else if (current) { > + *chk = virDomainCheckpointCurrent(dom, 0); > + } else { > + vshError(ctl, _("--%s or --current is required"), arg); > + return -1; > + } > + if (!*chk) { > + vshReportError(ctl); > + return -1; > + } > + > + *name = virDomainCheckpointGetName(*chk); > + return 0; > +} > + > + > +/* > + * "checkpoint-edit" command > + */ > +static const vshCmdInfo info_checkpoint_edit[] = { > + {.name = "help", > + .data = N_("edit XML for a checkpoint") > + }, > + {.name = "desc", > + .data = N_("Edit the domain checkpoint XML for a named checkpoint") > + }, > + {.name = NULL} > +}; > + > +static const vshCmdOptDef opts_checkpoint_edit[] = { > + VIRSH_COMMON_OPT_DOMAIN_FULL(0), > + {.name = "checkpointname", > + .type = VSH_OT_STRING, > + .help = N_("checkpoint name"), > + .completer = virshCheckpointNameCompleter, > + }, > + VIRSH_COMMON_OPT_CURRENT(N_("also set edited checkpoint as current")), > + {.name = "rename", > + .type = VSH_OT_BOOL, > + .help = N_("allow renaming an existing checkpoint") > + }, > + {.name = "clone", > + .type = VSH_OT_BOOL, > + .help = N_("allow cloning to new name") > + }, > + {.name = NULL} > +}; [...] > + > + > +/* > + * "checkpoint-current" command > + */ > +static const vshCmdInfo info_checkpoint_current[] = { > + {.name = "help", > + .data = N_("Get or set the current checkpoint") Set?!? I see we do that with snapshots, but that's pure insanity. It uses redefine to set the snapshot as current? But that defeats the purpose of the 'current' snapshot/checkpoint since it does not change the underlying state. I don't think we should copy this. > + }, > + {.name = "desc", > + .data = N_("Get or set the current checkpoint") > + }, > + {.name = NULL} > +}; [...] > +/* Helper function to get the name of a checkpoint's parent. Caller > + * must free the result. Returns 0 on success (including when it was > + * proven no parent exists), and -1 on failure with error reported > + * (such as no checkpoint support or domain deleted in meantime). */ > +static int > +virshGetCheckpointParent(vshControl *ctl, > + virDomainCheckpointPtr checkpoint, > + char **parent_name) > +{ > + virDomainCheckpointPtr parent = NULL; > + int ret = -1; > + > + *parent_name = NULL; > + > + parent = virDomainCheckpointGetParent(checkpoint, 0); > + if (parent) { > + /* API works, and virDomainCheckpointGetName will succeed */ There's no option when the API would not work. Only when it isn't supported, but then you have other problems. > + *parent_name = vshStrdup(ctl, virDomainCheckpointGetName(parent)); > + ret = 0; > + } else if (last_error->code == VIR_ERR_NO_DOMAIN_CHECKPOINT) { > + /* API works, and we found a root with no parent */ > + ret = 0; > + } > + > + if (ret < 0) { > + vshReportError(ctl); > + vshError(ctl, "%s", _("unable to determine if checkpoint has parent")); > + } else { > + vshResetLibvirtError(); > + } > + virshDomainCheckpointFree(parent); > + return ret; > +} > + > + > +/* > + * "checkpoint-info" command > + */ > +static const vshCmdInfo info_checkpoint_info[] = { > + {.name = "help", > + .data = N_("checkpoint information") > + }, > + {.name = "desc", > + .data = N_("Returns basic information about a checkpoint.") > + }, > + {.name = NULL} > +}; > + > +static const vshCmdOptDef opts_checkpoint_info[] = { > + VIRSH_COMMON_OPT_DOMAIN_FULL(0), > + {.name = "checkpointname", > + .type = VSH_OT_STRING, > + .help = N_("checkpoint name"), > + .completer = virshCheckpointNameCompleter, > + }, > + VIRSH_COMMON_OPT_CURRENT(N_("info on current checkpoint")), > + {.name = NULL} > +}; > + > + > +static bool > +cmdCheckpointInfo(vshControl *ctl, > + const vshCmd *cmd) > +{ > + virDomainPtr dom; > + virDomainCheckpointPtr checkpoint = NULL; > + const char *name; > + char *parent = NULL; > + bool ret = false; > + int count; > + unsigned int flags; > + int current; > + int metadata; > + > + dom = virshCommandOptDomain(ctl, cmd, NULL); > + if (dom == NULL) > + return false; > + > + if (virshLookupCheckpoint(ctl, cmd, "checkpointname", true, dom, > + &checkpoint, &name) < 0) > + goto cleanup; > + > + vshPrint(ctl, "%-15s %s\n", _("Name:"), name); > + vshPrint(ctl, "%-15s %s\n", _("Domain:"), virDomainGetName(dom)); > + > + /* Determine if checkpoint is current. */ > + current = virDomainCheckpointIsCurrent(checkpoint, 0); > + if (current < 0) { > + vshError(ctl, "%s", > + _("unexpected problem querying checkpoint state")); > + goto cleanup; > + } > + vshPrint(ctl, "%-15s %s\n", _("Current:"), > + current > 0 ? _("yes") : _("no")); > + > + if (virshGetCheckpointParent(ctl, checkpoint, &parent) < 0) { > + vshError(ctl, "%s", > + _("unexpected problem querying checkpoint state")); virshGetCheckpointParent already reports errors > + goto cleanup; > + } > + vshPrint(ctl, "%-15s %s\n", _("Parent:"), parent ? parent : "-"); > + > + /* Children, Descendants. */ > + flags = 0; > + count = virDomainCheckpointListAllChildren(checkpoint, NULL, flags); > + if (count < 0) { > + if (last_error->code == VIR_ERR_NO_SUPPORT) { > + vshResetLibvirtError(); Other calls are fatal. > + ret = true; > + } > + goto cleanup; > + } > + vshPrint(ctl, "%-15s %d\n", _("Children:"), count); > + flags = VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS; > + count = virDomainCheckpointListAllChildren(checkpoint, NULL, flags); > + if (count < 0) > + goto cleanup; > + vshPrint(ctl, "%-15s %d\n", _("Descendants:"), count); > + > + /* Metadata. */ > + metadata = virDomainCheckpointHasMetadata(checkpoint, 0); > + if (metadata >= 0) > + vshPrint(ctl, "%-15s %s\n", _("Metadata:"), > + metadata ? _("yes") : _("no")); This should be probably dropped altogether. I don't see a reason to have metadata-less checkpoints. > + > + ret = true; > + > + cleanup: > + VIR_FREE(parent); > + virshDomainCheckpointFree(checkpoint); > + virshDomainFree(dom); > + return ret; > +} [...] > + if (vshCommandOptBool(cmd, "topological")) > + flags |= VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL; > + > + if (roots) > + flags |= VIR_DOMAIN_CHECKPOINT_LIST_ROOTS; > + > + if (vshCommandOptBool(cmd, "metadata")) > + flags |= VIR_DOMAIN_CHECKPOINT_LIST_METADATA; > + > + if (vshCommandOptBool(cmd, "no-metadata")) > + flags |= VIR_DOMAIN_CHECKPOINT_LIST_NO_METADATA; > + > + if (vshCommandOptBool(cmd, "descendants")) { > + if (!from && !current) { > + vshError(ctl, "%s", > + _("--descendants requires either --from or --current")); > + return false; > + } > + flags |= VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS; > + } > + > + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) > + return false; > + > + if ((from || current) && > + virshLookupCheckpoint(ctl, cmd, "from", true, dom, &start, &from_chk) < 0) > + goto cleanup; > + > + if (!(chklist = virshCheckpointListCollect(ctl, dom, start, flags, tree))) > + goto cleanup; > + > + if (!tree && !name) { > + if (parent) > + vshPrintExtra(ctl, " %-20s %-25s %s", > + _("Name"), _("Creation Time"), _("Parent")); > + else > + vshPrintExtra(ctl, " %-20s %-25s", > + _("Name"), _("Creation Time")); > + vshPrintExtra(ctl, "\n" > + "------------------------------" > + "--------------\n"); We don't do manual tables for some time now. > + } > + > + if (tree) { > + for (i = 0; i < chklist->nchks; i++) { > + if (!chklist->chks[i].parent && > + vshTreePrint(ctl, virshCheckpointListLookup, chklist, > + chklist->nchks, i) < 0) > + goto cleanup; > + } > + ret = true; > + goto cleanup; > + } > + > + for (i = 0; i < chklist->nchks; i++) { > + const char *chk_name; > + > + /* free up memory from previous iterations of the loop */ > + VIR_FREE(parent_chk); > + xmlXPathFreeContext(ctxt); > + xmlFreeDoc(xml); > + VIR_FREE(doc); > + > + checkpoint = chklist->chks[i].chk; > + chk_name = virDomainCheckpointGetName(checkpoint); > + assert(chk_name); > + > + if (name) { > + /* just print the checkpoint name */ > + vshPrint(ctl, "%s\n", chk_name); > + continue; > + } > + > + if (!(doc = virDomainCheckpointGetXMLDesc(checkpoint, 0))) > + continue; > + > + if (!(xml = virXMLParseStringCtxt(doc, _("(domain_checkpoint)"), &ctxt))) > + continue; > + > + if (parent) > + parent_chk = virXPathString("string(/domaincheckpoint/parent/name)", > + ctxt); > + > + if (virXPathLongLong("string(/domaincheckpoint/creationTime)", ctxt, > + &creation_longlong) < 0) > + continue; > + creation_time_t = creation_longlong; > + if (creation_time_t != creation_longlong) { > + vshError(ctl, "%s", _("time_t overflow")); > + continue; > + } > + localtime_r(&creation_time_t, &time_info); > + strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S %z", > + &time_info); > + > + if (parent) > + vshPrint(ctl, " %-20s %-25s %s\n", > + chk_name, timestr, parent_chk ?: "-"); > + else > + vshPrint(ctl, " %-20s %-25s\n", chk_name, timestr); > + } > + > + ret = true; > + > + cleanup: > + /* this frees up memory from the last iteration of the loop */ > + virshCheckpointListFree(chklist); > + VIR_FREE(parent_chk); > + virshDomainCheckpointFree(start); > + xmlXPathFreeContext(ctxt); > + xmlFreeDoc(xml); > + VIR_FREE(doc); > + virshDomainFree(dom); > + > + return ret; > +} > + [...] > diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c > index d87475f6f6..59f3d7e6d9 100644 > --- a/tools/virsh-domain-monitor.c > +++ b/tools/virsh-domain-monitor.c [...] > @@ -1782,6 +1783,17 @@ virshDomainListCollect(vshControl *ctl, unsigned int flags) > goto remove_entry; > } We could make the checkpoint flags mandatory if the driver supports checkpoints. > + /* checkpoint filter */ > + if (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_CHECKPOINT)) { > + if ((nchk = virDomainListAllCheckpoints(dom, NULL, 0)) < 0) { > + vshError(ctl, "%s", _("Failed to get checkpoint count")); > + goto cleanup; > + } > + if (!((VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT) && nchk > 0) || > + (VSH_MATCH(VIR_CONNECT_LIST_DOMAINS_NO_CHECKPOINT) && nchk == 0))) > + goto remove_entry; > + } > + > /* the domain matched all filters, it may stay */ > continue; > [...] > diff --git a/tools/virsh.pod b/tools/virsh.pod > index afc1684db0..064a0a2fa3 100644 > --- a/tools/virsh.pod > +++ b/tools/virsh.pod > @@ -409,6 +409,7 @@ Inject NMI to the guest. > [I<--with-managed-save>] [I<--without-managed-save>] > [I<--autostart>] [I<--no-autostart>] > [I<--with-snapshot>] [I<--without-snapshot>] > + [I<--with-checkpoint>] [I<--without-checkpoint>] > [I<--state-running>] [I<--state-paused>] > [I<--state-shutoff>] [I<--state-other>] > > @@ -514,6 +515,11 @@ this feature disabled use I<--no-autostart>. > Domains that have snapshot images can be listed using flag I<--with-snapshot>, > domains without a snapshot I<--without-snapshot>. > > +=item B<Checkpoint existence> > + > +Domains that have checkpoints can be listed using flag I<--with-checkpoint>, > +domains without a checkpoint I<--without-checkpoint>. > + > =back > > When talking to older servers, this command is forced to use a series of API > @@ -809,7 +815,8 @@ can be restarted later. > If I<domain> is transient, then the metadata of any snapshots will > be lost once the guest stops running, but the snapshot contents still > exist, and a new domain with the same name and UUID can restore the > -snapshot metadata with B<snapshot-create>. > +snapshot metadata with B<snapshot-create>. Similarly, the metadata of > +any checkpoints will be lost, but can be restored with B<checkpoint-create>. > > If I<--graceful> is specified, don't resort to extreme measures > (e.g. SIGKILL) when the guest doesn't stop after a reasonable timeout; > @@ -1570,7 +1577,7 @@ Convert a domain Id (or UUID) to domain name > Rename a domain. This command changes current domain name to the new name > specified in the second argument. > > -B<Note>: Domain must be inactive and without snapshots. > +B<Note>: Domain must be inactive and without snapshots or checkpoints. > > =item B<domstate> I<domain> [I<--reason>] > > @@ -2811,10 +2818,11 @@ services must be shutdown in the domain. > The exact behavior of a domain when it shuts down is set by the > I<on_poweroff> parameter in the domain's XML definition. > > -If I<domain> is transient, then the metadata of any snapshots will > -be lost once the guest stops running, but the snapshot contents still > -exist, and a new domain with the same name and UUID can restore the > -snapshot metadata with B<snapshot-create>. > +If I<domain> is transient, then the metadata of any snapshots and > +checkpoints will be lost once the guest stops running, but the underlying > +contents still exist, and a new domain with the same name and UUID can > +restore the snapshot metadata with B<snapshot-create>, and the checkpoint > +metadata with B<checkpoint-create>. > > By default the hypervisor will try to pick a suitable shutdown > method. To specify an alternative method, the I<--mode> parameter > @@ -2891,7 +2899,7 @@ Output the device used for the TTY console of the domain. If the information > is not available the processes will provide an exit code of 1. > > =item B<undefine> I<domain> [I<--managed-save>] [I<--snapshots-metadata>] > -[I<--nvram>] [I<--keep-nvram>] > +[I<--checkpoints-metadata>] [I<--nvram>] [I<--keep-nvram>] > [ {I<--storage> B<volumes> | I<--remove-all-storage> [I<--delete-snapshots>]} > I<--wipe-storage>] > > @@ -2909,6 +2917,12 @@ domain. Without the flag, attempts to undefine an inactive domain with > snapshot metadata will fail. If the domain is active, this flag is > ignored. > > +The I<--checkpoints-metadata> flag guarantees that any checkpoints (see the > +B<checkpoint-list> command) are also cleaned up when undefining an inactive > +domain. Without the flag, attempts to undefine an inactive domain with > +checkpoint metadata will fail. If the domain is active, this flag is > +ignored. > + > I<--nvram> and I<--keep-nvram> specify accordingly to delete or keep nvram > (/domain/os/nvram/) file. If the domain has an nvram file and the flags are > omitted, the undefine will fail. > @@ -4876,6 +4890,216 @@ the data contents from that point in time. > > =back > > +=head1 CHECKPOINT COMMANDS > + > +The following commands manipulate domain checkpoints. Checkpoints serve as > +a point in time to identify which portions of a guest's disks have changed > +after that time, making it possible to perform incremental and differential > +backups. Checkpoints are identified with a unique name. See > +L<https://libvirt.org/formatcheckpoint.html> for documentation of the XML > +format used to represent properties of checkpoints. > + > +=over 4 > + > +=item B<checkpoint-create> I<domain> [I<xmlfile>] {[I<--redefine> > +{[I<--current>] | [I<--redefine-list>]}] | [I<--no-metadata>] [I<--quiesce>]} > + > +Create a checkpoint for domain I<domain> with the properties specified > +in I<xmlfile> describing a <domaincheckpoint> top-level element. If > +I<xmlfile> is completely omitted, then libvirt will create a > +checkpoint with a name based on the current time. The new checkpoint > +will become current, as listed by B<checkpoint-current>. > + > +If I<--redefine> is specified, then all XML elements produced by > +B<checkpoint-dumpxml> are valid; this can be used to migrate > +checkpoint hierarchy from one machine to another, to recreate > +hierarchy for the case of a transient domain that goes away and is > +later recreated with the same name and UUID, or to make slight > +alterations in the checkpoint metadata (such as host-specific aspects > +of the domain XML embedded in the checkpoint). When this flag is > +supplied, the I<xmlfile> argument is mandatory, and the domain's > +current snapshot will not be altered unless the I<--current> flag is > +also given. If I<--redefine-list> is specified, I<--redefine> is > +implied, I<--current> is rejected, and the XML changes from being a > +single <domaincheckpoint> to instead being a <checkpoints> element > +describing a list of checkpoints. List form only works if the domain > +has no currently-defined checkpoint metadata, and can be obtained as a > +subset of I<dumpxml --checkpoints> output. > + > +If I<--no-metadata> is specified, then the checkpoint data is created, > +but any metadata is immediately discarded (that is, libvirt does not > +treat the checkpoint as current, and cannot use the checkpoint for an > +incremental backup unless I<--redefine> is later used to teach libvirt > +about the metadata again). > + > +If I<--quiesce> is specified, libvirt will try to use guest agent > +to freeze and unfreeze domain's mounted file systems. However, > +if domain has no guest agent, checkpoint creation will fail. > + > +Existence of checkpoint metadata will prevent attempts to B<undefine> > +a persistent domain. However, for transient domains, checkpoint > +metadata is silently lost when the domain quits running (whether > +by command such as B<destroy> or by internal guest action). > + > +=item B<checkpoint-create-as> I<domain> {[I<--print-xml>] > +| [I<--no-metadata>]} [I<name>] [I<description>] [I<--quiesce>] > +[I<--diskspec>] B<diskspec>]... > + > +Create a checkpoint for domain I<domain> with the given <name> and > +<description>; if either value is omitted, libvirt will choose a > +value. If I<--print-xml> is specified, then XML appropriate for > +I<checkpoint-create> is output, rather than actually creating a > +checkpoint. > + > +The I<--diskspec> option can be used to control which guest disks participate in the checkpoint. This option can occur > +multiple times, according to the number of <disk> elements in the domain > +xml. Each <diskspec> is in the > +form B<disk[,checkpoint=type][,bitmap=name]>. A literal I<--diskspec> must precede each B<diskspec> unless > +all three of I<domain>, I<name>, and I<description> are also present. > +For example, a diskspec of "vda,checkpoint=bitmap,bitmap=map1" > +results in the following XML: > + <disk name='vda' checkpoint='bitmap' bitmap='map1'/> > + > +If I<--quiesce> is specified, libvirt will try to use guest agent > +to freeze and unfreeze domain's mounted file systems. However, > +if domain has no guest agent, checkpoint creation will fail. > + > +If I<--no-metadata> is specified, then the checkpoint data is created, > +but any metadata is immediately discarded (that is, libvirt does not > +treat the checkpoint as current, and cannot use the checkpoint for an > +incremental backup unless I<--redefine> is later used to teach libvirt > +about the metadata again). > + > +=item B<checkpoint-current> I<domain> {[I<--name>] | [I<--security-info>] > +[I<--no-domain>] [I<--size>] | [I<checkpointname>]} > + > +Without I<checkpointname>, this will output the checkpoint XML for the > +domain's current checkpoint (if any). If I<--name> is specified, > +output just the current checkpoint name instead of the full xml. > +Otherwise, using I<--security-info> will also include security > +sensitive information in the XML, using I<--size> will add XML > +indicating roughly how much guest data has changed since the > +checkpoint was created, and using I<--no-domain> will omit the > +<domain> element from the output for a more compact view. > + > +With I<checkpointname>, this is a request to make the existing named > +checkpoint become the current checkpoint. > + > +=item B<checkpoint-edit> I<domain> [I<checkpointname>] [I<--current>] > +{[I<--rename>] | [I<--clone>]} > + > +Edit the XML configuration file for I<checkpointname> of a domain. If > +both I<checkpointname> and I<--current> are specified, also force the > +edited checkpoint to become the current snapshot. If > +I<checkpointname> is omitted, then I<--current> must be supplied, to > +edit the current checkpoint. > + > +This is equivalent to: > + > + virsh checkpoint-dumpxml dom name > checkpoint.xml > + vi checkpoint.xml (or make changes with your other text editor) > + virsh checkpoint-create dom checkpoint.xml --redefine [--current] > + > +except that it does some error checking. > + > +The editor used can be supplied by the C<$VISUAL> or C<$EDITOR> environment > +variables, and defaults to C<vi>. > + > +If I<--rename> is specified, then the edits can change the checkpoint > +name. If I<--clone> is specified, then changing the snapshot name > +will create a clone of the checkpoint metadata. If neither is > +specified, then the edits must not change the checkpoint name. Note > +that changing a checkpoint name must be done with care, since some > +drivers may require the original checkpoint name for actually > +accessing changes since a point in time. > + > +=item B<checkpoint-info> I<domain> {I<checkpoint> | I<--current>} > + > +Output basic information about a named <checkpoint>, or the current > +checkpoint with I<--current>. > + > +=item B<checkpoint-list> I<domain> [I<--metadata>] [I<--no-metadata>] > +[{I<--parent> | I<--roots> | [{I<--tree> | I<--name>}]}] [I<--topological>] > +[{[I<--from>] B<checkpoint> | I<--current>} [I<--descendants>]] > +[I<--leaves>] [I<--no-leaves>] > + > +List all of the available checkpoints for the given domain, defaulting > +to show columns for the checkpoint name and creation time. > + > +Normally, table form output is sorted by checkpoint name; using > +I<--topological> instead sorts so that no child is listed before its > +ancestors (although there may be more than one possible ordering with > +this property). > + > +If I<--parent> is specified, add a column to the output table giving > +the name of the parent of each checkpoint. If I<--roots> is > +specified, the list will be filtered to just checkpoints that have no > +parents. If I<--tree> is specified, the output will be in a tree > +format, listing just checkpoint names. These three options are > +mutually exclusive. If I<--name> is specified only the checkpoint name > +is printed. This option is mutually exclusive with I<--tree>. > + > +If I<--from> is provided, filter the list to checkpoints which are > +children of the given B<checkpoint>; or if I<--current> is provided, > +start at the current checkpoint. When used in isolation or with > +I<--parent>, the list is limited to direct children unless > +I<--descendants> is also present. When used with I<--tree>, the use > +of I<--descendants> is implied. This option is not compatible with > +I<--roots>. Note that the starting point of I<--from> or I<--current> > +is not included in the list unless the I<--tree> option is also > +present. > + > +If I<--leaves> is specified, the list will be filtered to just > +checkpoints that have no children. Likewise, if I<--no-leaves> is > +specified, the list will be filtered to just checkpoints with > +children. (Note that omitting both options does no filtering, while > +providing both options will either produce the same list or error out > +depending on whether the server recognizes the flags). Filtering > +options are not compatible with I<--tree>. > + > +If I<--metadata> is specified, the list will be filtered to just > +checkpoints that involve libvirt metadata, and thus would prevent > +B<undefine> of a persistent domain, or be lost on B<destroy> of a > +transient domain. Likewise, if I<--no-metadata> is specified, the > +list will be filtered to just checkpoints that exist without the need > +for libvirt metadata. Hmmm. This actually reminds me that we should forbid checkpoints without metadata. It's a dead-end also with snapshots. > + > +=item B<checkpoint-dumpxml> I<domain> I<snapshot> [I<--security-info>] > +[I<--no-domain>] [I<--size>] > + > +Output the snapshot XML for the domain's checkpoint named > +I<checkpoint>. Using I<--security-info> will also include security > +sensitive information, using I<--size> will add XML indicating roughly > +how much guest data has changed since the checkpoint was created, and > +using I<--no-domain> will omit the <domain> element from the output > +for a more compact view. Use B<checkpoint-current> to easily access > +the XML of the current snapshot. To grab the XML for all checkpoints > +at once, use B<dumpxml --checkpoints>. > + > +=item B<checkpoint-parent> I<domain> {I<checkpoint> | I<--current>} > + > +Output the name of the parent checkpoint, if any, for the given > +I<checkpoint>, or for the current checkpoint with I<--current>. > + > +=item B<checkpoint-delete> I<domain> {I<checkpoint> | I<--current>} > +[I<--metadata>] [{I<--children> | I<--children-only>}] > + > +Delete the checkpoint for the domain named I<checkpoint>, or the > +current checkpoint with I<--current>. The record of which portions of > +the disk changed since the checkpoint are merged into the parent > +checkpoint (if any). If I<--children> is passed, then delete this > +checkpoint and any children of this checkpoint. If I<--children-only> > +is passed, then delete any children of this checkpoint, but leave this > +checkpoint intact. These two flags are mutually exclusive. > + > +If I<--metadata> is specified, then only delete the checkpoint > +metadata maintained by libvirt, while leaving the checkpoint contents > +intact for access by external tools; otherwise deleting a checkpoint > +also removes the ability to perform an incremental backup from that > +point in time. > + > +=back > + > =head1 NWFILTER COMMANDS > > The following commands manipulate network filters. Network filters allow > -- > 2.20.1 > > -- > libvir-list mailing list > libvir-list@xxxxxxxxxx > https://www.redhat.com/mailman/listinfo/libvir-list
Attachment:
signature.asc
Description: PGP signature
-- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list