
On Fri, Oct 12, 2018 at 00:10:08 -0500, Eric Blake wrote:
Introduce a bunch of new public APIs related to backup checkpoints. Checkpoints are modeled heavily after virDomainSnapshotPtr (both represent a point in time of the guest), although a snapshot exists with the intent of rolling back to that state, while a checkpoint exists to make it possible to create an incremental backup at a later time.
The full list of new API: virDomainCheckpointCreateXML; virDomainCheckpointCurrent; virDomainCheckpointDelete; virDomainCheckpointFree; virDomainCheckpointGetConnect; virDomainCheckpointGetDomain; virDomainCheckpointGetParent; virDomainCheckpointGetXMLDesc; virDomainCheckpointHasMetadata; virDomainCheckpointIsCurrent; virDomainCheckpointListChildren; virDomainCheckpointLookupByName; virDomainCheckpointRef; virDomainHasCurrentCheckpoint; virDomainListCheckpoints; virDomainCheckpointGetName;
Signed-off-by: Eric Blake <eblake@redhat.com> --- include/libvirt/libvirt-domain-checkpoint.h | 156 +++++ include/libvirt/libvirt.h | 5 +- src/driver-hypervisor.h | 60 +- docs/Makefile.am | 3 + docs/apibuild.py | 2 + docs/docs.html.in | 1 + libvirt.spec.in | 1 + mingw-libvirt.spec.in | 2 + po/POTFILES | 1 + src/Makefile.am | 2 + src/libvirt-domain-checkpoint.c | 723 ++++++++++++++++++++ src/libvirt_public.syms | 20 + 12 files changed, 972 insertions(+), 4 deletions(-) create mode 100644 include/libvirt/libvirt-domain-checkpoint.h create mode 100644 src/libvirt-domain-checkpoint.c
diff --git a/include/libvirt/libvirt-domain-checkpoint.h b/include/libvirt/libvirt-domain-checkpoint.h new file mode 100644 index 0000000000..cfca8f4b7f --- /dev/null +++ b/include/libvirt/libvirt-domain-checkpoint.h @@ -0,0 +1,156 @@ +/* + * libvirt-domain-checkpoint.h + * Summary: APIs for management of domain checkpoints + * Description: Provides APIs for the management of domain checkpoints + * Author: Eric Blake <eblake@redhat.com> + * + * Copyright (C) 2006-2018 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/>. + */ + +#ifndef __VIR_LIBVIRT_DOMAIN_CHECKPOINT_H__ +# define __VIR_LIBVIRT_DOMAIN_CHECKPOINT_H__ + +# ifndef __VIR_LIBVIRT_H_INCLUDES__ +# error "Don't include this file directly, only use libvirt/libvirt.h" +# endif + +/** + * virDomainCheckpoint: + * + * A virDomainCheckpoint is a private structure representing a checkpoint of + * a domain. A checkpoint is useful for tracking which portions of the + * domain disks have been altered since a point in time, but by itself does + * not allow reverting back to that point in time. + */ +typedef struct _virDomainCheckpoint virDomainCheckpoint; + +/** + * virDomainCheckpointPtr: + * + * A virDomainCheckpointPtr is pointer to a virDomainCheckpoint + * private structure, and is the type used to reference a domain + * checkpoint in the API. + */ +typedef virDomainCheckpoint *virDomainCheckpointPtr; + +const char *virDomainCheckpointGetName(virDomainCheckpointPtr checkpoint); +virDomainPtr virDomainCheckpointGetDomain(virDomainCheckpointPtr checkpoint); +virConnectPtr virDomainCheckpointGetConnect(virDomainCheckpointPtr checkpoint); + +typedef enum { + VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE = (1 << 0), /* Restore or alter + metadata */ + VIR_DOMAIN_CHECKPOINT_CREATE_CURRENT = (1 << 1), /* With redefine, make + checkpoint current */ + VIR_DOMAIN_CHECKPOINT_CREATE_NO_METADATA = (1 << 2), /* Make checkpoint without + remembering it */
Is this worth adding? Will there be a way to start an incremental backup without a previously existing libvirt checkpoint (thus having metadata)?
+ /* TODO: VIR_DOMAIN_CHECKPOINT_CREATE_QUIESCE */ +} virDomainCheckpointCreateFlags; + +/* Create a checkpoint using the current VM state. */ +virDomainCheckpointPtr virDomainCheckpointCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags); + +typedef enum { + VIR_DOMAIN_CHECKPOINT_XML_SECURE = (1 << 0), /* Include sensitive data */ + VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN = (1 << 1), /* Suppress <domain> + subelement */ + VIR_DOMAIN_CHECKPOINT_XML_SIZE = (1 << 2), /* Include dynamic + per-<disk> size */ +} virDomainCheckpointXMLFlags; + +/* Dump the XML of a checkpoint */ +char *virDomainCheckpointGetXMLDesc(virDomainCheckpointPtr checkpoint, + unsigned int flags); + +/** + * virDomainCheckpointListFlags: + * + * Flags valid for virDomainListCheckpoints() and + * virDomainCheckpointListChildren(). Note that the interpretation of + * flag (1<<0) depends on which function it is passed to; but serves + * to toggle the per-call default of whether the listing is shallow or + * recursive. Remaining bits come in groups; if all bits from a group + * are 0, then that group is not used to filter results. */ +typedef enum { + VIR_DOMAIN_CHECKPOINT_LIST_ROOTS = (1 << 0), /* Filter by checkpoints + with no parents, when + listing a domain */ + VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS = (1 << 0), /* List all descendants, + not just children, when + listing a checkpoint */ + + VIR_DOMAIN_CHECKPOINT_LIST_LEAVES = (1 << 1), /* Filter by checkpoints + with no children */ + VIR_DOMAIN_CHECKPOINT_LIST_NO_LEAVES = (1 << 2), /* Filter by checkpoints + that have children */ + + VIR_DOMAIN_CHECKPOINT_LIST_METADATA = (1 << 3), /* Filter by checkpoints + which have metadata */ + VIR_DOMAIN_CHECKPOINT_LIST_NO_METADATA = (1 << 4), /* Filter by checkpoints + with no metadata */ +} virDomainCheckpointListFlags; + +/* Get all checkpoint objects for this domain */ +int virDomainListCheckpoints(virDomainPtr domain, + virDomainCheckpointPtr **checkpoints, + unsigned int flags); + +/* Get all checkpoint object children for this checkpoint */ +int virDomainCheckpointListChildren(virDomainCheckpointPtr checkpoint, + virDomainCheckpointPtr **children, + unsigned int flags); + +/* Get a handle to a named checkpoint */ +virDomainCheckpointPtr virDomainCheckpointLookupByName(virDomainPtr domain, + const char *name, + unsigned int flags); + +/* Check whether a domain has a checkpoint which is currently used */ +int virDomainHasCurrentCheckpoint(virDomainPtr domain, unsigned int flags); + +/* Get a handle to the current checkpoint */ +virDomainCheckpointPtr virDomainCheckpointCurrent(virDomainPtr domain, + unsigned int flags); + +/* Get a handle to the parent checkpoint, if one exists */ +virDomainCheckpointPtr virDomainCheckpointGetParent(virDomainCheckpointPtr checkpoint, + unsigned int flags); + +/* Determine if a checkpoint is the current checkpoint of its domain. */ +int virDomainCheckpointIsCurrent(virDomainCheckpointPtr checkpoint, + unsigned int flags); + +/* Determine if checkpoint has metadata that would prevent domain deletion. */ +int virDomainCheckpointHasMetadata(virDomainCheckpointPtr checkpoint, + unsigned int flags); + +/* Delete a checkpoint */ +typedef enum { + VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN = (1 << 0), /* Also delete children */ + VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY = (1 << 1), /* Delete just metadata */ + VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY = (1 << 2), /* Delete just children */ +} virDomainCheckpointDeleteFlags; + +int virDomainCheckpointDelete(virDomainCheckpointPtr checkpoint, + unsigned int flags); + +int virDomainCheckpointRef(virDomainCheckpointPtr checkpoint); +int virDomainCheckpointFree(virDomainCheckpointPtr checkpoint); + +#endif /* __VIR_LIBVIRT_DOMAIN_CHECKPOINT_H__ */ diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 26887a40e7..4e7da0afc4 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -4,7 +4,7 @@ * Description: Provides the interfaces of the libvirt library to handle * virtualized domains * - * Copyright (C) 2005-2006, 2010-2014 Red Hat, Inc. + * Copyright (C) 2005-2006, 2010-2018 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 @@ -36,8 +36,7 @@ extern "C" { # include <libvirt/libvirt-common.h> # include <libvirt/libvirt-host.h> # include <libvirt/libvirt-domain.h> -typedef struct _virDomainCheckpoint virDomainCheckpoint; -typedef virDomainCheckpoint *virDomainCheckpointPtr;
This should be part of the patch adding the data structure.
+# include <libvirt/libvirt-domain-checkpoint.h> # include <libvirt/libvirt-domain-snapshot.h> # include <libvirt/libvirt-event.h> # include <libvirt/libvirt-interface.h>
[...]
diff --git a/src/libvirt-domain-checkpoint.c b/src/libvirt-domain-checkpoint.c new file mode 100644 index 0000000000..8a7b5b3c56 --- /dev/null +++ b/src/libvirt-domain-checkpoint.c @@ -0,0 +1,723 @@ +/* + * libvirt-domain-checkpoint.c: entry points for virDomainCheckpointPtr APIs + * + * Copyright (C) 2006-2014, 2018 Red Hat, Inc.
For a new file?
+ * + * 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/>. + */ + +#include <config.h> + +#include "datatypes.h" +#include "virlog.h" + +VIR_LOG_INIT("libvirt.domain-checkpoint"); + +#define VIR_FROM_THIS VIR_FROM_DOMAIN_CHECKPOINT + +/** + * virDomainCheckpointGetName: + * @checkpoint: a checkpoint object + * + * Get the public name for that checkpoint + * + * Returns a pointer to the name or NULL, the string need not be deallocated + * as its lifetime will be the same as the checkpoint object. + */ +const char * +virDomainCheckpointGetName(virDomainCheckpointPtr checkpoint) +{ + VIR_DEBUG("checkpoint=%p", checkpoint); + + virResetLastError(); + + virCheckDomainCheckpointReturn(checkpoint, NULL); + + return checkpoint->name; +} + + +/** + * virDomainCheckpointGetDomain: + * @checkpoint: a checkpoint object + * + * Provides the domain pointer associated with a checkpoint. The + * reference counter on the domain is not increased by this + * call. + * + * Returns the domain or NULL. + */ +virDomainPtr +virDomainCheckpointGetDomain(virDomainCheckpointPtr checkpoint) +{ + VIR_DEBUG("checkpoint=%p", checkpoint); + + virResetLastError(); + + virCheckDomainCheckpointReturn(checkpoint, NULL); + + return checkpoint->domain; +} + + +/** + * virDomainCheckpointGetConnect: + * @checkpoint: a checkpoint object + * + * Provides the connection pointer associated with a checkpoint. The + * reference counter on the connection is not increased by this + * call. + * + * Returns the connection or NULL. + */ +virConnectPtr +virDomainCheckpointGetConnect(virDomainCheckpointPtr checkpoint)
This does not seem to be very useful. Users always can do a two step lookup.
+{ + VIR_DEBUG("checkpoint=%p", checkpoint); + + virResetLastError(); + + virCheckDomainCheckpointReturn(checkpoint, NULL); + + return checkpoint->domain->conn; +} + + +/** + * virDomainCheckpointCreateXML: + * @domain: a domain object + * @xmlDesc: description of the checkpoint to create + * @flags: bitwise-OR of supported virDomainCheckpointCreateFlags + * + * Create a new checkpoint using @xmlDesc on a running @domain. + * Typically, it is more common to create a new checkpoint as part of + * kicking off a backup job with virDomainBackupBegin(); however, it + * is also possible to start a checkpoint without a backup. + * + * See <a href=formatcheckpoint.html#CheckpointAttributes">Checkpoint XML</a> + * for more details on @xmlDesc. In particular, some hypervisors may require + * particular disk formats, such as qcow2, in order to support this + * command; where @xmlDesc can be used to limit the checkpoint to a working + * subset of the domain's disks. + * + * If @flags includes VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE, then this + * is a request to reinstate checkpoint metadata that was previously + * discarded, rather than creating a new checkpoint. When redefining + * checkpoint metadata, the current checkpoint will not be altered + * unless the VIR_DOMAIN_CHECKPOINT_CREATE_CURRENT flag is also + * present. It is an error to request the + * VIR_DOMAIN_CHECKPOINT_CREATE_CURRENT flag without + * VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE. + * + * If @flags includes VIR_DOMAIN_CHECKPOINT_CREATE_NO_METADATA, then + * the domain's disk images are modified according to @xmlDesc, but + * then the just-created checkpoint has its metadata deleted. This + * flag is incompatible with VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE. + * + * Returns an (opaque) new virDomainCheckpointPtr on success, or NULL + * on failure. + */ +virDomainCheckpointPtr +virDomainCheckpointCreateXML(virDomainPtr domain, + const char *xmlDesc, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "xmlDesc=%s, flags=0x%x", xmlDesc, flags); + + virResetLastError(); + + virCheckDomainReturn(domain, NULL); + conn = domain->conn; + + virCheckNonNullArgGoto(xmlDesc, error); + virCheckReadOnlyGoto(conn->flags, error); + + VIR_REQUIRE_FLAG_GOTO(VIR_DOMAIN_CHECKPOINT_CREATE_CURRENT, + VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE, + error); + + VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE, + VIR_DOMAIN_CHECKPOINT_CREATE_NO_METADATA, + error); + + if (conn->driver->domainCheckpointCreateXML) { + virDomainCheckpointPtr ret; + ret = conn->driver->domainCheckpointCreateXML(domain, xmlDesc, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virDomainCheckpointGetXMLDesc: + * @checkpoint: a domain checkpoint object + * @flags: bitwise-OR of supported virDomainCheckpointXMLFlags + * + * Provide an XML description of the domain checkpoint. + * + * No security-sensitive data will be included unless @flags contains + * VIR_DOMAIN_CHECKPOINT_XML_SECURE; this flag is rejected on read-only + * connections.
As discussed in other places, I'm not convinced that storing a domain is needed with backups.
+ * + * Normally, the XML description includes an element giving a full + * description of the domain at the time the snapshot was created; to
Missed reference to snapshots.
+ * reduce parsing time, it will be suppressed when @flags contains + * VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN. + * + * By default, the XML description contains only static information that + * does not change over time. However, when @flags contains + * VIR_DOMAIN_CHECKPOINT_XML_SIZE, each <disk> listing adds an additional + * attribute that shows an estimate of the current size in bytes that + * have been dirtied between the time the checkpoint was created and the + * current point in time. + * + * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error. + * the caller must free() the returned value. + */ +char * +virDomainCheckpointGetXMLDesc(virDomainCheckpointPtr checkpoint, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("checkpoint=%p, flags=0x%x", checkpoint, flags); + + virResetLastError(); + + virCheckDomainCheckpointReturn(checkpoint, NULL); + conn = checkpoint->domain->conn; + + if ((conn->flags & VIR_CONNECT_RO) && + (flags & VIR_DOMAIN_CHECKPOINT_XML_SECURE)) { + virReportError(VIR_ERR_OPERATION_DENIED, "%s", + _("virDomainCheckpointGetXMLDesc with secure flag")); + goto error; + } + + if (conn->driver->domainCheckpointGetXMLDesc) { + char *ret; + ret = conn->driver->domainCheckpointGetXMLDesc(checkpoint, flags); + if (!ret) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return NULL; +} + + +/** + * virDomainListCheckpoints: + * @domain: a domain object + * @checkpoints: pointer to variable to store the array containing checkpoint + * objects, or NULL if the list is not required (just returns + * number of checkpoints) + * @flags: bitwise-OR of supported virDomainCheckpoinListFlags
s/virDomainCheckpoinListFlags/virDomainCheckpointListFlags/
+ * + * Collect the list of domain checkpoints for the given domain, and allocate + * an array to store those objects. + * + * By default, this command covers all checkpoints; it is also possible to + * limit things to just checkpoints with no parents, when @flags includes + * VIR_DOMAIN_CHECKPOINT_LIST_ROOTS. Additional filters are provided in + * groups, where each group contains bits that describe mutually exclusive + * attributes of a checkpoint, and where all bits within a group describe + * all possible checkpoints. Some hypervisors might reject explicit bits + * from a group where the hypervisor cannot make a distinction. For a + * group supported by a given hypervisor, the behavior when no bits of a + * group are set is identical to the behavior when all bits in that group + * are set. When setting bits from more than one group, it is possible to + * select an impossible combination, in that case a hypervisor may return + * either 0 or an error. + * + * The first group of @flags is VIR_DOMAIN_CHECKPOINT_LIST_LEAVES and + * VIR_DOMAIN_CHECKPOINT_LIST_NO_LEAVES, to filter based on checkpoints that + * have no further children (a leaf checkpoint). + * + * The next group of @flags is VIR_DOMAIN_CHECKPOINT_LIST_METADATA and + * VIR_DOMAIN_CHECKPOINT_LIST_NO_METADATA, for filtering checkpoints based on + * whether they have metadata that would prevent the removal of the last + * reference to a domain. + * + * Returns the number of domain checkpoints found or -1 and sets @checkpoints + * to NULL in case of error. On success, the array stored into @checkpoints + * is guaranteed to have an extra allocated element set to NULL but not + * included in the return count, to make iteration easier. The caller is + * responsible for calling virDomainCheckpointFree() on each array element, + * then calling free() on @checkpoints. + */ +int +virDomainListCheckpoints(virDomainPtr domain, + virDomainCheckpointPtr **checkpoints, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "checkpoints=%p, flags=0x%x", checkpoints, flags); + + virResetLastError(); + + if (checkpoints) + *checkpoints = NULL; + + virCheckDomainReturn(domain, -1); + conn = domain->conn; + + if (conn->driver->domainListCheckpoints) { + int ret = conn->driver->domainListCheckpoints(domain, checkpoints, + flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +}
[...]
+/** + * virDomainCheckpointIsCurrent: + * @checkpoint: a checkpoint object + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Determine if the given checkpoint is the domain's current checkpoint. See + * also virDomainHasCurrentCheckpoint().
Depending on whether 'current checkpoint' makes sense this might not be needed.
+ * + * Returns 1 if current, 0 if not current, or -1 on error. + */ +int +virDomainCheckpointIsCurrent(virDomainCheckpointPtr checkpoint, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("checkpoint=%p, flags=0x%x", checkpoint, flags); + + virResetLastError(); + + virCheckDomainCheckpointReturn(checkpoint, -1); + conn = checkpoint->domain->conn; + + if (conn->driver->domainCheckpointIsCurrent) { + int ret; + ret = conn->driver->domainCheckpointIsCurrent(checkpoint, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} + + +/** + * virDomainCheckpointHasMetadata: + * @checkpoint: a checkpoint object + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Determine if the given checkpoint is associated with libvirt metadata + * that would prevent the deletion of the domain.
Given that we've never implemented metadata-less snapshots do we expect this to happen with checkpoints?
+ * Returns 1 if the checkpoint has metadata, 0 if the checkpoint exists without + * help from libvirt, or -1 on error. + */ +int +virDomainCheckpointHasMetadata(virDomainCheckpointPtr checkpoint, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("checkpoint=%p, flags=0x%x", checkpoint, flags); + + virResetLastError(); + + virCheckDomainCheckpointReturn(checkpoint, -1); + conn = checkpoint->domain->conn; + + if (conn->driver->domainCheckpointHasMetadata) { + int ret; + ret = conn->driver->domainCheckpointHasMetadata(checkpoint, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} + + +/** + * virDomainCheckpointDelete: + * @checkpoint: the checkpoint to remove + * @flags: not used yet, pass 0 + * @flags: bitwise-OR of supported virDomainCheckpointDeleteFlags
@flags present twice
+ * + * Removes a checkpoint from the domain. + * + * When removing a checkpoint, the record of which portions of the + * disk were dirtied after the checkpoint will be merged into the + * record tracked by the parent checkpoint, if any.
So as a high-level question, since from the APIs added here is clear that we expect tree-like topologies of checkpoints. Does merging of the data _into_ the parent invalidate any other children? This is a big problem we'll be facing when doing external snapshot reversion.
Likewise, if the + * checkpoint being deleted was the current checkpoint, the parent + * checkpoint becomes the new current checkpoint. + * + * If @flags includes VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY, then + * any checkpoint metadata tracked by libvirt is removed while keeping + * the checkpoint contents intact; if a hypervisor does not require + * any libvirt metadata to track checkpoints, then this flag is + * silently ignored. + * + * Returns 0 on success, -1 on error. + */ +int +virDomainCheckpointDelete(virDomainCheckpointPtr checkpoint, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("checkpoint=%p, flags=0x%x", checkpoint, flags); + + virResetLastError(); + + virCheckDomainCheckpointReturn(checkpoint, -1); + conn = checkpoint->domain->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN, + VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY, + error); + + if (conn->driver->domainCheckpointDelete) { + int ret = conn->driver->domainCheckpointDelete(checkpoint, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +}