[libvirt] [PATCH 5/8] backup: Introduce virDomainCheckpoint APIs

John Ferlan jferlan at redhat.com
Mon Jun 25 23:16:27 UTC 2018



On 06/13/2018 12:42 PM, 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.
> 
> Signed-off-by: Eric Blake <eblake at redhat.com>
> ---
>  docs/Makefile.am                            |   3 +
>  docs/apibuild.py                            |   2 +
>  docs/docs.html.in                           |   1 +
>  include/libvirt/libvirt-domain-checkpoint.h | 147 ++++++
>  include/libvirt/libvirt.h                   |   5 +-
>  libvirt.spec.in                             |   1 +
>  mingw-libvirt.spec.in                       |   2 +
>  po/POTFILES                                 |   1 +
>  src/Makefile.am                             |   2 +
>  src/driver-hypervisor.h                     |  60 ++-
>  src/libvirt-domain-checkpoint.c             | 708 ++++++++++++++++++++++++++++
>  src/libvirt_public.syms                     |  16 +
>  12 files changed, 944 insertions(+), 4 deletions(-)
>  create mode 100644 include/libvirt/libvirt-domain-checkpoint.h
>  create mode 100644 src/libvirt-domain-checkpoint.c
> 

In a word... Overwhelming!

I have concerns related to committing the API before everyone is sure
about the underlying hypervisor code. No sense in baking in an API only
to find out later needs/issues. It seems we

I see checkpoint code borrows the domain connection - is that similar to
snapshots?

I won't go too in depth here - mostly just scan to look for obvious issues.

> diff --git a/docs/Makefile.am b/docs/Makefile.am
> index 9620587a77..0df8ebbd64 100644
> --- a/docs/Makefile.am
> +++ b/docs/Makefile.am
> @@ -25,6 +25,7 @@ apihtml = \
>  apihtml_generated = \
>    html/libvirt-libvirt-common.html \
>    html/libvirt-libvirt-domain.html \
> +  html/libvirt-libvirt-domain-checkpoint.html \
>    html/libvirt-libvirt-domain-snapshot.html \
>    html/libvirt-libvirt-event.html \
>    html/libvirt-libvirt-host.html \
> @@ -318,6 +319,7 @@ $(python_generated_files): $(APIBUILD_STAMP)
>  $(APIBUILD_STAMP): $(srcdir)/apibuild.py \
>  		$(top_srcdir)/include/libvirt/libvirt.h \
>  		$(top_srcdir)/include/libvirt/libvirt-common.h.in \
> +		$(top_srcdir)/include/libvirt/libvirt-domain-checkpoint.h \
>  		$(top_srcdir)/include/libvirt/libvirt-domain-snapshot.h \
>  		$(top_srcdir)/include/libvirt/libvirt-domain.h \
>  		$(top_srcdir)/include/libvirt/libvirt-event.h \
> @@ -334,6 +336,7 @@ $(APIBUILD_STAMP): $(srcdir)/apibuild.py \
>  		$(top_srcdir)/include/libvirt/libvirt-admin.h \
>  		$(top_srcdir)/include/libvirt/virterror.h \
>  		$(top_srcdir)/src/libvirt.c \
> +		$(top_srcdir)/src/libvirt-domain-checkpoint.c \
>  		$(top_srcdir)/src/libvirt-domain-snapshot.c \
>  		$(top_srcdir)/src/libvirt-domain.c \
>  		$(top_srcdir)/src/libvirt-host.c \
> diff --git a/docs/apibuild.py b/docs/apibuild.py
> index 5e218a9ad0..471547cea7 100755
> --- a/docs/apibuild.py
> +++ b/docs/apibuild.py
> @@ -26,6 +26,7 @@ debugsym = None
>  included_files = {
>    "libvirt-common.h": "header with general libvirt API definitions",
>    "libvirt-domain.h": "header with general libvirt API definitions",
> +  "libvirt-domain-checkpoint.h": "header with general libvirt API definitions",
>    "libvirt-domain-snapshot.h": "header with general libvirt API definitions",
>    "libvirt-event.h": "header with general libvirt API definitions",
>    "libvirt-host.h": "header with general libvirt API definitions",
> @@ -39,6 +40,7 @@ included_files = {
>    "virterror.h": "header with error specific API definitions",
>    "libvirt.c": "Main interfaces for the libvirt library",
>    "libvirt-domain.c": "Domain interfaces for the libvirt library",
> +  "libvirt-domain-checkpoint.c": "Domain checkpoint interfaces for the libvirt library",
>    "libvirt-domain-snapshot.c": "Domain snapshot interfaces for the libvirt library",
>    "libvirt-host.c": "Host interfaces for the libvirt library",
>    "libvirt-interface.c": "Interface interfaces for the libvirt library",
> diff --git a/docs/docs.html.in b/docs/docs.html.in
> index 11dfd27ba6..63dbdf7755 100644
> --- a/docs/docs.html.in
> +++ b/docs/docs.html.in
> @@ -97,6 +97,7 @@
>          <dd>Reference manual for the C public API, split in
>            <a href="html/libvirt-libvirt-common.html">common</a>,
>            <a href="html/libvirt-libvirt-domain.html">domain</a>,
> +          <a href="html/libvirt-libvirt-domain-checkpoint.html">domain checkpoint</a>,
>            <a href="html/libvirt-libvirt-domain-snapshot.html">domain snapshot</a>,
>            <a href="html/libvirt-virterror.html">error</a>,
>            <a href="html/libvirt-libvirt-event.html">event</a>,
> diff --git a/include/libvirt/libvirt-domain-checkpoint.h b/include/libvirt/libvirt-domain-checkpoint.h
> new file mode 100644
> index 0000000000..4a7dc73089
> --- /dev/null
> +++ b/include/libvirt/libvirt-domain-checkpoint.h
> @@ -0,0 +1,147 @@
> +/*
> + * libvirt-domain-checkpoint.h
> + * Summary: APIs for management of domain checkpoints
> + * Description: Provides APIs for the management of domain checkpoints
> + * Author: Eric Blake <eblake at redhat.com>
> + *
> + * Copyright (C) 2006-2018 Red Hat, Inc.

Since it's created in 2018 - shouldn't it just list that? Not my area of
expertise by any stretch of the imagination though.

> + *
> + * 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 */
> +} virDomainCheckpointCreateFlags;
> +
> +/* Create a checkpoint using the current VM state. */
> +virDomainCheckpointPtr virDomainCheckpointCreateXML(virDomainPtr domain,
> +                                                    const char *xmlDesc,
> +                                                    unsigned int flags);
> +
> +/* 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.  */
                                                           ^^
There's an extra space here

> +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 */

There's two "1 << 0" entries - ironically the doc page render lists
these in the opposite order.  Still I see this is essentially a copy of
the SnapshotListFlags. So do we really need to keep that when there's
only 2 API's?

> +
> +    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;

Not quite sure where/how metadata comes into play. What metadata?
Where/when was that introduced?

> +
> +/* 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);

Two lines for arguments. (consistency)

> +
> +/* 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;

Again, not sure what the metadata entails here.  Although this perhaps
answer a different question I had about converging bitmaps by deleting
"middle" checkpoints.

> +
> +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;
> +# 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/libvirt.spec.in b/libvirt.spec.in
> index 50bd79a7d7..a82e97f3db 100644
> --- a/libvirt.spec.in
> +++ b/libvirt.spec.in
> @@ -2102,6 +2102,7 @@ exit 0
>  %{_includedir}/libvirt/libvirt-admin.h
>  %{_includedir}/libvirt/libvirt-common.h
>  %{_includedir}/libvirt/libvirt-domain.h
> +%{_includedir}/libvirt/libvirt-domain-checkpoint.h
>  %{_includedir}/libvirt/libvirt-domain-snapshot.h
>  %{_includedir}/libvirt/libvirt-event.h
>  %{_includedir}/libvirt/libvirt-host.h
> diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in
> index 6912527cf7..3b41e69661 100644
> --- a/mingw-libvirt.spec.in
> +++ b/mingw-libvirt.spec.in
> @@ -269,6 +269,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh
>  %{mingw32_includedir}/libvirt/libvirt.h
>  %{mingw32_includedir}/libvirt/libvirt-common.h
>  %{mingw32_includedir}/libvirt/libvirt-domain.h
> +%{mingw32_includedir}/libvirt/libvirt-domain-checkpoint.h
>  %{mingw32_includedir}/libvirt/libvirt-domain-snapshot.h
>  %{mingw32_includedir}/libvirt/libvirt-event.h
>  %{mingw32_includedir}/libvirt/libvirt-host.h
> @@ -355,6 +356,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh
>  %{mingw64_includedir}/libvirt/libvirt.h
>  %{mingw64_includedir}/libvirt/libvirt-common.h
>  %{mingw64_includedir}/libvirt/libvirt-domain.h
> +%{mingw64_includedir}/libvirt/libvirt-domain-checkpoint.h
>  %{mingw64_includedir}/libvirt/libvirt-domain-snapshot.h
>  %{mingw64_includedir}/libvirt/libvirt-event.h
>  %{mingw64_includedir}/libvirt/libvirt-host.h

Not an area I know well, but as long as the various make with rpm
options is tested, then great. Seems we always seem to forget something
related to some obscure option every time we add something new!

> diff --git a/po/POTFILES b/po/POTFILES
> index be2874487c..d246657188 100644
> --- a/po/POTFILES
> +++ b/po/POTFILES
> @@ -69,6 +69,7 @@ src/interface/interface_backend_netcf.c
>  src/interface/interface_backend_udev.c
>  src/internal.h
>  src/libvirt-admin.c
> +src/libvirt-domain-checkpoint.c
>  src/libvirt-domain-snapshot.c
>  src/libvirt-domain.c
>  src/libvirt-host.c
> diff --git a/src/Makefile.am b/src/Makefile.am
> index db8c8ebd1a..d20d65574e 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -174,6 +174,7 @@ DRIVER_SOURCES += \
>  		$(DATATYPES_SOURCES) \
>  		libvirt.c libvirt_internal.h \
>  		libvirt-domain.c \
> +		libvirt-domain-checkpoint.c \
>  		libvirt-domain-snapshot.c \
>  		libvirt-host.c \
>  		libvirt-interface.c \
> @@ -728,6 +729,7 @@ libvirt_setuid_rpc_client_la_SOURCES = \
>  		datatypes.c \
>  		libvirt.c \
>  		libvirt-domain.c \
> +		libvirt-domain-checkpoint.c \
>  		libvirt-domain-snapshot.c \
>  		libvirt-host.c \
>  		libvirt-interface.c \
> diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h
> index eef31eb1f0..ee8a9a3e0e 100644
> --- a/src/driver-hypervisor.h
> +++ b/src/driver-hypervisor.h
> @@ -1,7 +1,7 @@
>  /*
>   * driver-hypervisor.h: entry points for hypervisor drivers
>   *
> - * Copyright (C) 2006-2015 Red Hat, Inc.
> + * 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
> @@ -1321,6 +1321,53 @@ typedef int
>                                          int *nparams,
>                                          unsigned int flags);
> 
> +typedef virDomainCheckpointPtr
> +(*virDrvDomainCheckpointCreateXML)(virDomainPtr domain,
> +                                   const char *xmlDesc,
> +                                   unsigned int flags);
> +
> +typedef char *
> +(*virDrvDomainCheckpointGetXMLDesc)(virDomainCheckpointPtr checkpoint,
> +                                    unsigned int flags);
> +
> +typedef int
> +(*virDrvDomainListCheckpoints)(virDomainPtr domain,
> +                               virDomainCheckpointPtr **checkpoints,
> +                               unsigned int flags);
> +
> +typedef int
> +(*virDrvDomainCheckpointListChildren)(virDomainCheckpointPtr checkpoint,
> +                                      virDomainCheckpointPtr **children,
> +                                      unsigned int flags);
> +
> +typedef virDomainCheckpointPtr
> +(*virDrvDomainCheckpointLookupByName)(virDomainPtr domain,
> +                                      const char *name,
> +                                      unsigned int flags);
> +
> +typedef int
> +(*virDrvDomainHasCurrentCheckpoint)(virDomainPtr domain,
> +                                    unsigned int flags);
> +
> +typedef virDomainCheckpointPtr
> +(*virDrvDomainCheckpointGetParent)(virDomainCheckpointPtr checkpoint,
> +                                   unsigned int flags);
> +
> +typedef virDomainCheckpointPtr
> +(*virDrvDomainCheckpointCurrent)(virDomainPtr domain,
> +                                 unsigned int flags);
> +
> +typedef int
> +(*virDrvDomainCheckpointIsCurrent)(virDomainCheckpointPtr checkpoint,
> +                                   unsigned int flags);
> +
> +typedef int
> +(*virDrvDomainCheckpointHasMetadata)(virDomainCheckpointPtr checkpoint,
> +                                     unsigned int flags);
> +
> +typedef int
> +(*virDrvDomainCheckpointDelete)(virDomainCheckpointPtr checkpoint,
> +                                unsigned int flags);
> 
>  typedef struct _virHypervisorDriver virHypervisorDriver;
>  typedef virHypervisorDriver *virHypervisorDriverPtr;
> @@ -1572,6 +1619,17 @@ struct _virHypervisorDriver {
>      virDrvConnectBaselineHypervisorCPU connectBaselineHypervisorCPU;
>      virDrvNodeGetSEVInfo nodeGetSEVInfo;
>      virDrvDomainGetLaunchSecurityInfo domainGetLaunchSecurityInfo;
> +    virDrvDomainCheckpointCreateXML domainCheckpointCreateXML;
> +    virDrvDomainCheckpointGetXMLDesc domainCheckpointGetXMLDesc;
> +    virDrvDomainListCheckpoints domainListCheckpoints;
> +    virDrvDomainCheckpointListChildren domainCheckpointListChildren;
> +    virDrvDomainCheckpointLookupByName domainCheckpointLookupByName;
> +    virDrvDomainHasCurrentCheckpoint domainHasCurrentCheckpoint;
> +    virDrvDomainCheckpointGetParent domainCheckpointGetParent;
> +    virDrvDomainCheckpointCurrent domainCheckpointCurrent;
> +    virDrvDomainCheckpointIsCurrent domainCheckpointIsCurrent;
> +    virDrvDomainCheckpointHasMetadata domainCheckpointHasMetadata;
> +    virDrvDomainCheckpointDelete domainCheckpointDelete;
>  };
> 
> 
> diff --git a/src/libvirt-domain-checkpoint.c b/src/libvirt-domain-checkpoint.c
> new file mode 100644
> index 0000000000..12511a13ee
> --- /dev/null
> +++ b/src/libvirt-domain-checkpoint.c
> @@ -0,0 +1,708 @@
> +/*
> + * libvirt-domain-checkpoint.c: entry points for virDomainCheckpointPtr APIs
> + *
> + * Copyright (C) 2006-2014, 2018 Red Hat, Inc.

Similar year thing

> + *
> + * 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.

Seems call could fit on previous line.

> + *
> + * 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.

Previous line again.

> + *
> + * Returns the connection or NULL.
> + */
> +virConnectPtr
> +virDomainCheckpointGetConnect(virDomainCheckpointPtr checkpoint)
> +{
> +    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.

Should we state that the domain needs to have an open connection already
or is that for free (too lazy to check right now ;-))

> + *
> + * See formatcheckpoint.html#CheckpointAttributes document for more
> + * details on @xmlDesc.
> + *
> + * 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.
> + */

Not sure I quite yet understand the "metadata" reference... Not sure how
much of this is cut-n-paste from the snapshot world.

> +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 subset of virDomainXMLFlags
> + *
> + * Provide an XML description of the domain checkpoint.
> + *
> + * No security-sensitive data will be included unless @flags contains
> + * VIR_DOMAIN_XML_SECURE; this flag is rejected on read-only
> + * connections.  For this API, @flags should not contain either
> + * VIR_DOMAIN_XML_INACTIVE or VIR_DOMAIN_XML_UPDATE_CPU.

New paragraph for the last sentence and perhaps just state "This API
does not support the xx or xx flags.

> + *
> + * 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_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

s/objects,/objects/

> + *               number of checkpoints)
> + * @flags: bitwise-OR of supported virDomainCheckpoinListFlags

CheckpointListFlags

> + *
> + * 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.

Huh, what?  This is really a confusing statement.

Considering "other factors" - rather than returning multiple levels of
data, maybe we ought to consider simplifying our lives and only return
the main/top level checkpoint object. Forcing the consumer to handle all
the iteration logic if they so desire. The concern being you end up 100
levels deep, too much data, and timeouts (think backingStore issues).
Of course that alters the name of the API a bit.

Assuming the driver level could easily return a count of checkpoints,
that allows the consumer all the ammunition they need I would think.

The way logic works now is that none or all type thing. Let the consumer
decide how far they want to chase.

> + *
> + * 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;
> +}
> +
> +
> +/**
> + * virDomainCheckpointListChildren:
> + * @checkpoint: a domain checkpoint object
> + * @children: pointer to variable to store the array containing checkpoint
> + *            objects, or NULL if the list is not required (just returns

s/objects,/objects

> + *            number of checkpoints)
> + * @flags: bitwise-OR of supported virDomainCheckpointListFlags
> + *
> + * Collect the list of domain checkpoints that are children of the given
> + * checkpoint, and allocate an array to store those objects.

assuming using the open domain connection pointer for the provided
checkpoint.

> + *
> + * By default, this command covers only direct children; it is also possible
> + * to expand things to cover all descendants, when @flags includes
> + * VIR_DOMAIN_CHECKPOINT_LIST_DESCENDANTS.  Also, some filters are provided in
> + * groups, where each group contains bits that describe mutually exclusive
> + * attributes of a snapshot, and where all bits within a group describe

s/snapshot/checkpoint

> + * all possible snapshots.  Some hypervisors might reject explicit bits

s/snapshots/checkpoints

> + * 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.

Similar traverse logic concerns here (with similar name change Children
to Child).

> + *
> + * 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 @children to
> + * NULL in case of error.  On success, the array stored into @children 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 @children.
> + */
> +int
> +virDomainCheckpointListChildren(virDomainCheckpointPtr checkpoint,
> +                                virDomainCheckpointPtr **children,
> +                                unsigned int flags)
> +{
> +    virConnectPtr conn;
> +
> +    VIR_DEBUG("checkpoint=%p, children=%p, flags=0x%x",
> +              checkpoint, children, flags);
> +
> +    virResetLastError();
> +
> +    if (children)
> +        *children = NULL;
> +
> +    virCheckDomainCheckpointReturn(checkpoint, -1);
> +    conn = checkpoint->domain->conn;
> +
> +    if (conn->driver->domainCheckpointListChildren) {
> +        int ret = conn->driver->domainCheckpointListChildren(checkpoint,
> +                                                             children, flags);
> +        if (ret < 0)
> +            goto error;
> +        return ret;
> +    }
> +
> +    virReportUnsupportedError();
> + error:
> +    virDispatchError(conn);
> +    return -1;
> +}
> +
> +
> +/**
> + * virDomainCheckpointLookupByName:
> + * @domain: a domain object
> + * @name: name for the domain checkpoint
> + * @flags: extra flags; not used yet, so callers should always pass 0
> + *
> + * Try to lookup a domain checkpoint based on its name.
> + *
> + * Returns a domain checkpoint object or NULL in case of failure.  If the
> + * domain checkpoint cannot be found, then the VIR_ERR_NO_DOMAIN_CHECKPOINT
> + * error is raised.
> + */
> +virDomainCheckpointPtr
> +virDomainCheckpointLookupByName(virDomainPtr domain,
> +                                const char *name,
> +                                unsigned int flags)
> +{
> +    virConnectPtr conn;
> +
> +    VIR_DOMAIN_DEBUG(domain, "name=%s, flags=0x%x", name, flags);
> +
> +    virResetLastError();
> +
> +    virCheckDomainReturn(domain, NULL);
> +    conn = domain->conn;
> +
> +    virCheckNonNullArgGoto(name, error);
> +
> +    if (conn->driver->domainCheckpointLookupByName) {
> +        virDomainCheckpointPtr dom;
> +        dom = conn->driver->domainCheckpointLookupByName(domain, name, flags);
> +        if (!dom)
> +            goto error;
> +        return dom;
> +    }
> +
> +    virReportUnsupportedError();
> + error:
> +    virDispatchError(conn);
> +    return NULL;
> +}
> +
> +
> +/**
> + * virDomainHasCurrentCheckpoint:
> + * @domain: pointer to the domain object
> + * @flags: extra flags; not used yet, so callers should always pass 0
> + *
> + * Determine if the domain has a current checkpoint.
> + *
> + * Returns 1 if such checkpoint exists, 0 if it doesn't, -1 on error.
> + */
> +int
> +virDomainHasCurrentCheckpoint(virDomainPtr domain, unsigned int flags)
> +{
> +    virConnectPtr conn;
> +
> +    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);
> +
> +    virResetLastError();
> +
> +    virCheckDomainReturn(domain, -1);
> +    conn = domain->conn;
> +
> +    if (conn->driver->domainHasCurrentCheckpoint) {
> +        int ret = conn->driver->domainHasCurrentCheckpoint(domain, flags);
> +        if (ret < 0)
> +            goto error;
> +        return ret;
> +    }
> +
> +    virReportUnsupportedError();
> + error:
> +    virDispatchError(conn);
> +    return -1;
> +}
> +
> +
> +/**
> + * virDomainCheckpointCurrent:
> + * @domain: a domain object
> + * @flags: extra flags; not used yet, so callers should always pass 0
> + *
> + * Get the current checkpoint for a domain, if any.
> + *
> + * virDomainCheckpointFree should be used to free the resources after the
> + * checkpoint object is no longer needed.
> + *
> + * Returns a domain checkpoint object or NULL in case of failure.  If the
> + * current domain checkpoint cannot be found, then the
> + * VIR_ERR_NO_DOMAIN_CHECKPOINT error is raised.
> + */
> +virDomainCheckpointPtr
> +virDomainCheckpointCurrent(virDomainPtr domain,
> +                           unsigned int flags)
> +{
> +    virConnectPtr conn;
> +
> +    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);
> +
> +    virResetLastError();
> +
> +    virCheckDomainReturn(domain, NULL);
> +    conn = domain->conn;
> +
> +    if (conn->driver->domainCheckpointCurrent) {
> +        virDomainCheckpointPtr snap;

s/snap/checkpoint

> +        snap = conn->driver->domainCheckpointCurrent(domain, flags);
> +        if (!snap)
> +            goto error;
> +        return snap;
> +    }
> +
> +    virReportUnsupportedError();
> + error:
> +    virDispatchError(conn);
> +    return NULL;
> +}
> +
> +
> +/**
> + * virDomainCheckpointGetParent:
> + * @checkpoint: a checkpoint object
> + * @flags: extra flags; not used yet, so callers should always pass 0
> + *
> + * Get the parent checkpoint for @checkpoint, if any.
> + *
> + * virDomainCheckpointFree should be used to free the resources after the
> + * checkpoint object is no longer needed.
> + *
> + * Returns a domain checkpoint object or NULL in case of failure.  If the
> + * given checkpoint is a root (no parent), then the VIR_ERR_NO_DOMAIN_CHECKPOINT
> + * error is raised.
> + */
> +virDomainCheckpointPtr
> +virDomainCheckpointGetParent(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->driver->domainCheckpointGetParent) {
> +        virDomainCheckpointPtr snap;

s/snap/checkpoint

> +        snap = conn->driver->domainCheckpointGetParent(checkpoint, flags);
> +        if (!snap)
> +            goto error;
> +        return snap;
> +    }
> +
> +    virReportUnsupportedError();
> + error:
> +    virDispatchError(conn);
> +    return NULL;
> +}
> +
> +
> +/**
> + * 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().
> + *
> + * 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.
> + *
> + * 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
> + *
> + * 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.  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;
> +}
> +
> +
> +/**
> + * virDomainCheckpointRef:
> + * @checkpoint: the checkpoint to hold a reference on
> + *
> + * Increment the reference count on the checkpoint. For each
> + * additional call to this method, there shall be a corresponding
> + * call to virDomainCheckpointFree to release the reference count, once
> + * the caller no longer needs the reference to this object.
> + *
> + * This method is typically useful for applications where multiple
> + * threads are using a connection, and it is required that the
> + * connection and domain remain open until all threads have finished
> + * using the checkpoint. ie, each new thread using a checkpoint would
> + * increment the reference count.

Kind of the "gray area" when checkpoints use domain objects which would
own the connection.  Almost makes me wonder if by creating a checkpoint
object, then should we just make the domm->conn ref to ensure that conn
doesn't get free'd inadvertently.

John

> + *
> + * Returns 0 in case of success and -1 in case of failure.
> + */
> +int
> +virDomainCheckpointRef(virDomainCheckpointPtr checkpoint)
> +{
> +    VIR_DEBUG("checkpoint=%p, refs=%d", checkpoint,
> +              checkpoint ? checkpoint->parent.u.s.refs : 0);
> +
> +    virResetLastError();
> +
> +    virCheckDomainCheckpointReturn(checkpoint, -1);
> +
> +    virObjectRef(checkpoint);
> +    return 0;
> +}
> +
> +
> +/**
> + * virDomainCheckpointFree:
> + * @checkpoint: a domain checkpoint object
> + *
> + * Free the domain checkpoint object.  The checkpoint itself is not modified.
> + * The data structure is freed and should not be used thereafter.
> + *
> + * Returns 0 in case of success and -1 in case of failure.
> + */
> +int
> +virDomainCheckpointFree(virDomainCheckpointPtr checkpoint)
> +{
> +    VIR_DEBUG("checkpoint=%p", checkpoint);
> +
> +    virResetLastError();
> +
> +    virCheckDomainCheckpointReturn(checkpoint, -1);
> +
> +    virObjectUnref(checkpoint);
> +    return 0;
> +}
> diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
> index 3bf3c3f916..a3e12b9a12 100644
> --- a/src/libvirt_public.syms
> +++ b/src/libvirt_public.syms
> @@ -794,6 +794,22 @@ LIBVIRT_4.4.0 {
> 
>  LIBVIRT_4.5.0 {
>      global:
> +        virDomainCheckpointCreateXML;
> +        virDomainCheckpointCurrent;
> +        virDomainCheckpointDelete;
> +        virDomainCheckpointFree;
> +        virDomainCheckpointGetConnect;
> +        virDomainCheckpointGetDomain;
> +        virDomainCheckpointGetParent;
> +        virDomainCheckpointGetXMLDesc;
> +        virDomainCheckpointHasMetadata;
> +        virDomainCheckpointIsCurrent;
> +        virDomainCheckpointListChildren;
> +        virDomainCheckpointLookupByName;
> +        virDomainCheckpointRef;
> +        virDomainHasCurrentCheckpoint;
> +        virDomainListCheckpoints;
> +        virDomainCheckpointGetName;
>          virGetLastErrorCode;
>          virGetLastErrorDomain;
>          virNodeGetSEVInfo;
> 




More information about the libvir-list mailing list