[PATCH v2 6/9] backup: Introduce virDomainCheckpoint APIs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
---
 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@xxxxxxxxxx>
+ *
+ * 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 */
+    /* 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;
+# 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/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/docs/Makefile.am b/docs/Makefile.am
index bd7bc1a431..88c1e83275 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 \
@@ -316,6 +317,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 \
@@ -332,6 +334,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 4914e7dbed..596bc1613d 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/libvirt.spec.in b/libvirt.spec.in
index 5c30a45f62..887d18990e 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -1894,6 +1894,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 65a48ed960..11237b8ab7 100644
--- a/mingw-libvirt.spec.in
+++ b/mingw-libvirt.spec.in
@@ -272,6 +272,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
@@ -361,6 +362,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
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 33ff280d78..4ec1b78a46 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -175,6 +175,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 \
@@ -726,6 +727,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/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.
+ *
+ * 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)
+{
+    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.
+ *
+ * Normally, the XML description includes an element giving a full
+ * description of the domain at the time the snapshot was created; to
+ * 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
+ *
+ * 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;
+}
+
+
+/**
+ * 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
+ *            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.
+ *
+ * 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
+ * all possible snapshots.  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 @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;
+        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;
+        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.
+ *
+ * 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 d4cdbd8b32..fb58b95344 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -809,4 +809,24 @@ LIBVIRT_4.5.0 {
         virNWFilterBindingGetFilterName;
 } LIBVIRT_4.4.0;

+LIBVIRT_4.9.0 {
+    global:
+        virDomainCheckpointCreateXML;
+        virDomainCheckpointCurrent;
+        virDomainCheckpointDelete;
+        virDomainCheckpointFree;
+        virDomainCheckpointGetConnect;
+        virDomainCheckpointGetDomain;
+        virDomainCheckpointGetName;
+        virDomainCheckpointGetParent;
+        virDomainCheckpointGetXMLDesc;
+        virDomainCheckpointHasMetadata;
+        virDomainCheckpointIsCurrent;
+        virDomainCheckpointListChildren;
+        virDomainCheckpointLookupByName;
+        virDomainCheckpointRef;
+        virDomainHasCurrentCheckpoint;
+        virDomainListCheckpoints;
+} LIBVIRT_4.5.0;
+
 # .... define new API here using predicted next version number ....
-- 
2.17.1

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list



[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux