backup: Implement virsh support for checkpoints

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. There is no need for checkpoint-revert or
checkpoint-current since those snapshot APIs have no checkpoint
counterpart.  Similarly, it is not necessary to change which
checkpoint is current when redefining from XML, since until we
integrate checkpoints with snapshots, there is only a linear chain
(and you can deduce the current checkpoint by instead using
'checkpoint-list --leaves').  Other aspects of checkpoint-list are
also a bit simpler than the snapshot counterpart, in part because we
don't have to cater to back-compat to older API.

Upcoming patches will test these interfaces once the test driver
supports checkpoints.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Eric Blake 2019-03-13 16:04:51 -05:00
parent cfe0920906
commit 689beaa47c
13 changed files with 1455 additions and 7 deletions

View File

@ -296,6 +296,7 @@ src/xenconfig/xen_xl.c
src/xenconfig/xen_xm.c
tests/virpolkittest.c
tools/libvirt-guests.sh.in
tools/virsh-checkpoint.c
tools/virsh-console.c
tools/virsh-domain-monitor.c
tools/virsh-domain.c

View File

@ -216,6 +216,7 @@ virt_login_shell_CFLAGS = \
virsh_SOURCES = \
virsh.c virsh.h \
virsh-checkpoint.c virsh-checkpoint.h \
virsh-completer.c virsh-completer.h \
virsh-console.c virsh-console.h \
virsh-domain.c virsh-domain.h \

1148
tools/virsh-checkpoint.c Normal file

File diff suppressed because it is too large Load Diff

25
tools/virsh-checkpoint.h Normal file
View File

@ -0,0 +1,25 @@
/*
* virsh-checkpoint.h: 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/>.
*/
#pragma once
#include "virsh.h"
extern const vshCmdDef checkpointCmds[];

View File

@ -693,6 +693,57 @@ virshSecretUUIDCompleter(vshControl *ctl,
}
char **
virshCheckpointNameCompleter(vshControl *ctl,
const vshCmd *cmd,
unsigned int flags)
{
virshControlPtr priv = ctl->privData;
virDomainPtr dom = NULL;
virDomainCheckpointPtr *checkpoints = NULL;
int ncheckpoints = 0;
size_t i = 0;
char **ret = NULL;
virCheckFlags(0, NULL);
if (!priv->conn || virConnectIsAlive(priv->conn) <= 0)
return NULL;
if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
return NULL;
if ((ncheckpoints = virDomainListAllCheckpoints(dom, &checkpoints,
flags)) < 0)
goto error;
if (VIR_ALLOC_N(ret, ncheckpoints + 1) < 0)
goto error;
for (i = 0; i < ncheckpoints; i++) {
const char *name = virDomainCheckpointGetName(checkpoints[i]);
if (VIR_STRDUP(ret[i], name) < 0)
goto error;
virshDomainCheckpointFree(checkpoints[i]);
}
VIR_FREE(checkpoints);
virshDomainFree(dom);
return ret;
error:
for (; i < ncheckpoints; i++)
virshDomainCheckpointFree(checkpoints[i]);
VIR_FREE(checkpoints);
for (i = 0; i < ncheckpoints; i++)
VIR_FREE(ret[i]);
VIR_FREE(ret);
virshDomainFree(dom);
return NULL;
}
char **
virshSnapshotNameCompleter(vshControl *ctl,
const vshCmd *cmd,

View File

@ -78,6 +78,10 @@ char ** virshSecretUUIDCompleter(vshControl *ctl,
const vshCmd *cmd,
unsigned int flags);
char ** virshCheckpointNameCompleter(vshControl *ctl,
const vshCmd *cmd,
unsigned int flags);
char ** virshSnapshotNameCompleter(vshControl *ctl,
const vshCmd *cmd,
unsigned int flags);

View File

@ -1621,6 +1621,7 @@ virshDomainListCollect(vshControl *ctl, unsigned int flags)
int autostart;
int state;
int nsnap;
int nchk;
int mansave;
virshControlPtr priv = ctl->privData;
@ -1788,6 +1789,17 @@ virshDomainListCollect(vshControl *ctl, unsigned int flags)
goto remove_entry;
}
/* 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;
@ -1849,6 +1861,14 @@ static const vshCmdOptDef opts_list[] = {
.type = VSH_OT_BOOL,
.help = N_("list domains without a snapshot")
},
{.name = "with-checkpoint",
.type = VSH_OT_BOOL,
.help = N_("list domains with existing checkpoint")
},
{.name = "without-checkpoint",
.type = VSH_OT_BOOL,
.help = N_("list domains without a checkpoint")
},
{.name = "state-running",
.type = VSH_OT_BOOL,
.help = N_("list domains in running state")
@ -1948,6 +1968,9 @@ cmdList(vshControl *ctl, const vshCmd *cmd)
FILTER("with-snapshot", VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT);
FILTER("without-snapshot", VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT);
FILTER("with-checkpoint", VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT);
FILTER("without-checkpoint", VIR_CONNECT_LIST_DOMAINS_NO_CHECKPOINT);
FILTER("state-running", VIR_CONNECT_LIST_DOMAINS_RUNNING);
FILTER("state-paused", VIR_CONNECT_LIST_DOMAINS_PAUSED);
FILTER("state-shutoff", VIR_CONNECT_LIST_DOMAINS_SHUTOFF);

View File

@ -3633,6 +3633,10 @@ static const vshCmdOptDef opts_undefine[] = {
.type = VSH_OT_BOOL,
.help = N_("remove all domain snapshot metadata (vm must be inactive)")
},
{.name = "checkpoints-metadata",
.type = VSH_OT_BOOL,
.help = N_("remove all domain checkpoint metadata (vm must be inactive)")
},
{.name = "nvram",
.type = VSH_OT_BOOL,
.help = N_("remove nvram file, if inactive")
@ -3662,6 +3666,7 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
/* User-requested actions. */
bool managed_save = vshCommandOptBool(cmd, "managed-save");
bool snapshots_metadata = vshCommandOptBool(cmd, "snapshots-metadata");
bool checkpoints_metadata = vshCommandOptBool(cmd, "checkpoints-metadata");
bool wipe_storage = vshCommandOptBool(cmd, "wipe-storage");
bool remove_all_storage = vshCommandOptBool(cmd, "remove-all-storage");
bool delete_snapshots = vshCommandOptBool(cmd, "delete-snapshots");
@ -3716,6 +3721,8 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
flags |= VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA;
snapshots_safe = true;
}
if (checkpoints_metadata)
flags |= VIR_DOMAIN_UNDEFINE_CHECKPOINTS_METADATA;
if (nvram)
flags |= VIR_DOMAIN_UNDEFINE_NVRAM;
if (keep_nvram)

View File

@ -228,6 +228,17 @@ virshDomainFree(virDomainPtr dom)
}
void
virshDomainCheckpointFree(virDomainCheckpointPtr chk)
{
if (!chk)
return;
vshSaveLibvirtHelperError();
virDomainCheckpointFree(chk); /* sc_prohibit_obj_free_apis_in_virsh */
}
void
virshDomainSnapshotFree(virDomainSnapshotPtr snap)
{

View File

@ -42,6 +42,9 @@ virshCommandOptDomain(vshControl *ctl,
void
virshDomainFree(virDomainPtr dom);
void
virshDomainCheckpointFree(virDomainCheckpointPtr chk);
void
virshDomainSnapshotFree(virDomainSnapshotPtr snap);

View File

@ -47,6 +47,7 @@
#include "virstring.h"
#include "virgettext.h"
#include "virsh-checkpoint.h"
#include "virsh-console.h"
#include "virsh-domain.h"
#include "virsh-domain-monitor.h"
@ -829,6 +830,7 @@ static const vshCmdGrp cmdGroups[] = {
{VIRSH_CMD_GRP_DOM_MANAGEMENT, "domain", domManagementCmds},
{VIRSH_CMD_GRP_DOM_MONITORING, "monitor", domMonitoringCmds},
{VIRSH_CMD_GRP_HOST_AND_HV, "host", hostAndHypervisorCmds},
{VIRSH_CMD_GRP_CHECKPOINT, "checkpoint", checkpointCmds},
{VIRSH_CMD_GRP_IFACE, "interface", ifaceCmds},
{VIRSH_CMD_GRP_NWFILTER, "filter", nwfilterCmds},
{VIRSH_CMD_GRP_NETWORK, "network", networkCmds},

View File

@ -40,6 +40,7 @@
/*
* Command group types
*/
#define VIRSH_CMD_GRP_CHECKPOINT "Checkpoint"
#define VIRSH_CMD_GRP_DOM_MANAGEMENT "Domain Management"
#define VIRSH_CMD_GRP_DOM_MONITORING "Domain Monitoring"
#define VIRSH_CMD_GRP_STORAGE_POOL "Storage Pool"

View File

@ -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;
@ -1574,7 +1581,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>]
@ -2815,10 +2822,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
@ -2895,7 +2903,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-storage-volume-snapshots>]} I<--wipe-storage>]
@ -2913,6 +2921,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.
@ -4890,6 +4904,163 @@ 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<--quiesce>]}
Create a checkpoint for domain I<domain> with the properties specified
in I<xmlfile> describing a <domaincheckpoint> top-level element. The
format of the input XML file will be validated against an internal RNG
schema (idential to using the L<virt-xml-validate(1)> tool). If
I<xmlfile> is completely omitted, then libvirt will create a
checkpoint with a name based on the current time.
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.
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<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.
=item B<checkpoint-edit> I<domain> I<checkpointname>
Edit the XML configuration file for I<checkpointname> of a domain.
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
except that it does some error checking, including that the edits
should not attempt to change the checkpoint name.
The editor used can be supplied by the C<$VISUAL> or C<$EDITOR> environment
variables, and defaults to C<vi>.
=item B<checkpoint-info> I<domain> I<checkpoint>
Output basic information about a named <checkpoint>.
=item B<checkpoint-list> I<domain> [{I<--parent> | I<--roots> |
[{I<--tree> | I<--name>}]}] [I<--topological>]
[[I<--from>] B<checkpoint> | [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>. 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>
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>.
=item B<checkpoint-dumpxml> I<domain> I<checkpoint>
[I<--security-info>] [I<--no-domain>] [I<--size>]
Output the checkpoint 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 the current size in bytes of
guest data that has changed since the checkpoint was created (although
remember that guest activity between a size check and actually
creating a backup can result in the backup needing slightly more
space). Using I<--no-domain> will omit the <domain> element from the
output for a more compact view.
=item B<checkpoint-parent> I<domain> I<checkpoint>
Output the name of the parent checkpoint, if any, for the given
I<checkpoint>.
=item B<checkpoint-delete> I<domain> I<checkpoint>
[I<--metadata>] [{I<--children> | I<--children-only>}]
Delete the checkpoint for the domain named I<checkpoint>. 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