[libvirt] [PATCH v8 12/21] backup: Implement virsh support for checkpoints

Peter Krempa pkrempa at redhat.com
Thu Apr 25 14:25:45 UTC 2019


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 at redhat.com>
> ---
>  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 at redhat.com>
> + *  Karel Zak <kzak at redhat.com>
> + *  Daniel P. Berrange <berrange at redhat.com>
> + *
> + */
> +
> +#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 at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20190425/298da917/attachment-0001.sig>


More information about the libvir-list mailing list