[libvirt] [libvirt-dbus] [PATCH 2/2] Implement snapshots APIs

Simon Kobyda skobyda at redhat.com
Mon Oct 7 15:22:34 UTC 2019


Signed-off-by: Simon Kobyda <skobyda at redhat.com>
---
 data/org.libvirt.Domain.xml         |  26 ++++
 data/org.libvirt.DomainSnapshot.xml |  34 +++++
 src/domain.c                        | 158 +++++++++++++++++++++
 src/domainsnapshot.c                | 204 +++++++++++++++++++++++++++-
 tests/Makefile.am                   |   1 +
 tests/libvirttest.py                |  14 ++
 tests/test_domain.py                |   8 ++
 tests/test_snapshot.py              |  43 ++++++
 tests/xmldata.py                    |   6 +
 9 files changed, 493 insertions(+), 1 deletion(-)
 create mode 100755 tests/test_snapshot.py

diff --git a/data/org.libvirt.Domain.xml b/data/org.libvirt.Domain.xml
index b4ed495..f03faf8 100644
--- a/data/org.libvirt.Domain.xml
+++ b/data/org.libvirt.Domain.xml
@@ -350,6 +350,12 @@
       <arg name="flags" type="u" direction="in"/>
       <arg name="ifaces" type="a(ssa(isu))" direction="out"/>
     </method>
+    <method name="ListDomainSnapshots">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainListAllSnapshots"/>
+      <arg name="flags" type="u" direction="in"/>
+      <arg name="snaps" type="ao" direction="out"/>
+    </method>
     <method name="ManagedSave">
       <annotation name="org.gtk.GDBus.DocString"
         value="See https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainManagedSave"/>
@@ -589,6 +595,26 @@
         value="See https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainShutdownFlags"/>
       <arg name="flags" type="u" direction="in"/>
     </method>
+    <method name="SnapshotCurrent">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainSnapshotCurrent"/>
+      <arg name="flags" type="u" direction="in"/>
+      <arg name="domainSnapshot" type="o" direction="out"/>
+    </method>
+    <method name="SnapshotCreateXML">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainSnapshotCreateXML"/>
+      <arg name="xml" type="s" direction="in"/>
+      <arg name="flags" type="u" direction="in"/>
+      <arg name="domainSnapshot" type="o" direction="out"/>
+    </method>
+    <method name="SnapshotLookupByName">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainSnapshotLookupByName"/>
+      <arg name="name" type="s" direction="in"/>
+      <arg name="flags" type="u" direction="in"/>
+      <arg name="domainSnapshot" type="o" direction="out"/>
+    </method>
     <method name="Suspend">
       <annotation name="org.gtk.GDBus.DocString"
         value="See https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainSuspend"/>
diff --git a/data/org.libvirt.DomainSnapshot.xml b/data/org.libvirt.DomainSnapshot.xml
index 80f61c6..463654f 100644
--- a/data/org.libvirt.DomainSnapshot.xml
+++ b/data/org.libvirt.DomainSnapshot.xml
@@ -3,5 +3,39 @@
 
 <node name="/org/libvirt/snapshot">
   <interface name="org.libvirt.DomainSnapshot">
+    <method name="Delete">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainSnapshotDelete"/>
+      <arg name="flags" type="u" direction="in"/>
+    </method>
+    <method name="GetParent">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainSnapshotGetParent"/>
+      <arg name="flags" type="u" direction="in"/>
+      <arg name="parent" type="o" direction="out"/>
+    </method>
+    <method name="GetXMLDesc">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainSnapshotGetXMLDesc"/>
+      <arg name="flags" type="u" direction="in"/>
+      <arg name="xml" type="s" direction="out"/>
+    </method>
+    <method name="IsCurrent">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainSnapshotIsCurrent"/>
+      <arg name="flags" type="u" direction="in"/>
+      <arg name="current" type="b" direction="out"/>
+    </method>
+    <method name="ListChildren">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainSnapshotListAllChildren"/>
+      <arg name="flags" type="u" direction="in"/>
+      <arg name="snaps" type="ao" direction="out"/>
+    </method>
+    <method name="Revert">
+      <annotation name="org.gtk.GDBus.DocString"
+        value="See https://libvirt.org/html/libvirt-libvirt-domain-snapshot.html#virDomainRevertToSnapshot"/>
+      <arg name="flags" type="u" direction="in"/>
+    </method>
   </interface>
 </node>
diff --git a/src/domain.c b/src/domain.c
index 10fa8de..3fc5bab 100644
--- a/src/domain.c
+++ b/src/domain.c
@@ -1857,6 +1857,50 @@ virtDBusDomainInjectNMI(GVariant *inArgs,
         virtDBusUtilSetLastVirtError(error);
 }
 
+static void
+virtDBusDomainListDomainSnapshots(GVariant *inArgs,
+                                  GUnixFDList *inFDs G_GNUC_UNUSED,
+                                  const gchar *objectPath,
+                                  gpointer userData,
+                                  GVariant **outArgs G_GNUC_UNUSED,
+                                  GUnixFDList **outFDs G_GNUC_UNUSED,
+                                  GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autoptr(virDomain) domain = NULL;
+    g_autoptr(virDomainSnapshotPtr) domainSnapshots = NULL;
+    guint flags;
+    GVariantBuilder builder;
+    GVariant *gdomainSnapshots;
+
+    g_variant_get(inArgs, "(u)", &flags);
+
+    domain = virtDBusDomainGetVirDomain(connect, objectPath,
+                                        error);
+    if (!domain)
+        return;
+
+    if (!virtDBusConnectOpen(connect, error))
+        return;
+
+    if (virDomainListAllSnapshots(domain, &domainSnapshots, flags) < 0)
+        return virtDBusUtilSetLastVirtError(error);
+
+    g_variant_builder_init(&builder, G_VARIANT_TYPE("ao"));
+
+    for (gint i = 0; domainSnapshots[i]; i++) {
+        g_autofree gchar *path = NULL;
+        path = virtDBusUtilBusPathForVirDomainSnapshot(domain,
+                                                       domainSnapshots[i],
+                                                       connect->domainSnapshotPath);
+
+        g_variant_builder_add(&builder, "o", path);
+    }
+
+    gdomainSnapshots = g_variant_builder_end(&builder);
+    *outArgs = g_variant_new_tuple(&gdomainSnapshots, 1);
+}
+
 static void
 virtDBusDomainInterfaceAddresses(GVariant *inArgs,
                                  GUnixFDList *inFDs G_GNUC_UNUSED,
@@ -2966,6 +3010,116 @@ virtDBusDomainShutdown(GVariant *inArgs,
         virtDBusUtilSetLastVirtError(error);
 }
 
+static void
+virtDBusDomainSnapshotCurrent(GVariant *inArgs,
+                              GUnixFDList *inFDs G_GNUC_UNUSED,
+                              const gchar *objectPath,
+                              gpointer userData,
+                              GVariant **outArgs G_GNUC_UNUSED,
+                              GUnixFDList **outFDs G_GNUC_UNUSED,
+                              GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autofree gchar *path = NULL;
+    g_autoptr(virDomain) domain = NULL;
+    g_autoptr(virDomainSnapshot) snapshot = NULL;
+    guint flags;
+
+    g_variant_get(inArgs, "(u)", &flags);
+
+    domain = virtDBusDomainGetVirDomain(connect, objectPath,
+                                        error);
+    if (!domain)
+        return;
+
+    if (!virtDBusConnectOpen(connect, error))
+        return;
+
+    snapshot = virDomainSnapshotCurrent(domain, flags);
+    if (!snapshot)
+        return virtDBusUtilSetLastVirtError(error);
+
+    path = virtDBusUtilBusPathForVirDomainSnapshot(domain,
+                                                   snapshot,
+                                                   connect->domainSnapshotPath);
+
+    *outArgs = g_variant_new("(o)", path);
+}
+
+static void
+virtDBusDomainSnapshotCreateXML(GVariant *inArgs,
+                                GUnixFDList *inFDs G_GNUC_UNUSED,
+                                const gchar *objectPath,
+                                gpointer userData,
+                                GVariant **outArgs G_GNUC_UNUSED,
+                                GUnixFDList **outFDs G_GNUC_UNUSED,
+                                GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autofree gchar *path = NULL;
+    g_autoptr(virDomain) domain = NULL;
+    g_autoptr(virDomainSnapshot) snapshot = NULL;
+    guint flags;
+    const gchar *xml;
+
+    g_variant_get(inArgs, "(su)", &xml, &flags);
+
+    domain = virtDBusDomainGetVirDomain(connect, objectPath,
+                                        error);
+    if (!domain)
+        return;
+
+    if (!virtDBusConnectOpen(connect, error))
+        return;
+
+    snapshot = virDomainSnapshotCreateXML(domain, xml, flags);
+    if (!snapshot)
+        return virtDBusUtilSetLastVirtError(error);
+
+    path = virtDBusUtilBusPathForVirDomainSnapshot(domain,
+                                                   snapshot,
+                                                   connect->domainSnapshotPath);
+
+    *outArgs = g_variant_new("(o)", path);
+}
+
+static void
+virtDBusDomainSnapshotLookupByName(GVariant *inArgs,
+                                   GUnixFDList *inFDs G_GNUC_UNUSED,
+                                   const gchar *objectPath,
+                                   gpointer userData,
+                                   GVariant **outArgs G_GNUC_UNUSED,
+                                   GUnixFDList **outFDs G_GNUC_UNUSED,
+                                   GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autofree gchar *path = NULL;
+    g_autoptr(virDomain) domain = NULL;
+    g_autoptr(virDomainSnapshot) snapshot = NULL;
+    guint flags;
+    const gchar *name;
+
+    g_variant_get(inArgs, "(su)", &name, &flags);
+
+    domain = virtDBusDomainGetVirDomain(connect, objectPath,
+                                        error);
+    if (!domain)
+        return;
+
+    if (!virtDBusConnectOpen(connect, error))
+        return;
+
+    snapshot = virDomainSnapshotLookupByName(domain, name, flags);
+    if (!snapshot)
+        return virtDBusUtilSetLastVirtError(error);
+
+    path = virtDBusUtilBusPathForVirDomainSnapshot(domain,
+                                                   snapshot,
+                                                   connect->domainSnapshotPath);
+
+    *outArgs = g_variant_new("(o)", path);
+}
+
 static void
 virtDBusDomainSuspend(GVariant *inArgs G_GNUC_UNUSED,
                       GUnixFDList *inFDs G_GNUC_UNUSED,
@@ -3095,6 +3249,7 @@ static virtDBusGDBusMethodTable virtDBusDomainMethodTable[] = {
     { "HasManagedSaveImage", virtDBusDomainHasManagedSaveImage },
     { "InjectNMI", virtDBusDomainInjectNMI },
     { "InterfaceAddresses", virtDBusDomainInterfaceAddresses },
+    { "ListDomainSnapshots", virtDBusDomainListDomainSnapshots },
     { "ManagedSave", virtDBusDomainManagedSave },
     { "ManagedSaveRemove", virtDBusDomainManagedSaveRemove },
     { "MemoryPeek", virtDBusDomainMemoryPeek },
@@ -3132,6 +3287,9 @@ static virtDBusGDBusMethodTable virtDBusDomainMethodTable[] = {
     { "SetSchedulerParameters", virtDBusDomainSetSchedulerParameters },
     { "SetTime", virtDBusDomainSetTime },
     { "SetUserPassword", virtDBusDomainSetUserPassword },
+    { "SnapshotCurrent", virtDBusDomainSnapshotCurrent },
+    { "SnapshotCreateXML", virtDBusDomainSnapshotCreateXML },
+    { "SnapshotLookupByName", virtDBusDomainSnapshotLookupByName },
     { "Shutdown", virtDBusDomainShutdown },
     { "Suspend", virtDBusDomainSuspend },
     { "Undefine", virtDBusDomainUndefine },
diff --git a/src/domainsnapshot.c b/src/domainsnapshot.c
index 590cbef..4bffe5d 100644
--- a/src/domainsnapshot.c
+++ b/src/domainsnapshot.c
@@ -1,14 +1,216 @@
 #include "domainsnapshot.h"
+#include "domain.h"
 #include "util.h"
 
 #include <libvirt/libvirt.h>
 
+static void
+virtDBusDomainSnapshotDelete(GVariant *inArgs,
+                             GUnixFDList *inFDs G_GNUC_UNUSED,
+                             const gchar *objectPath,
+                             gpointer userData,
+                             GVariant **outArgs G_GNUC_UNUSED,
+                             GUnixFDList **outFDs G_GNUC_UNUSED,
+                             GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autoptr(virDomainSnapshot) domainSnapshot = NULL;
+    guint flags;
+
+    g_variant_get(inArgs, "(u)", &flags);
+
+    domainSnapshot = virtDBusUtilVirDomainSnapshotFromBusPath(connect->connection,
+                                                              objectPath,
+                                                              connect->domainSnapshotPath);
+    if (!domainSnapshot)
+        return;
+
+    if (virDomainSnapshotDelete(domainSnapshot, flags) < 0)
+        return virtDBusUtilSetLastVirtError(error);
+}
+
+static void
+virtDBusDomainSnapshotGetParent(GVariant *inArgs,
+                                 GUnixFDList *inFDs G_GNUC_UNUSED,
+                                 const gchar *objectPath,
+                                 gpointer userData,
+                                 GVariant **outArgs G_GNUC_UNUSED,
+                                 GUnixFDList **outFDs G_GNUC_UNUSED,
+                                 GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autoptr(virDomainSnapshot) domainSnapshot = NULL;
+    g_autoptr(virDomainSnapshot) parent = NULL;
+    guint flags;
+    g_autofree gchar *domainName = NULL;
+    g_autoptr(virDomain) domain = NULL;
+    gsize prefixLen = strlen(connect->domainSnapshotPath) + 1;
+    gchar** strings = g_strsplit(objectPath + prefixLen, "_", 2);
+    g_autofree gchar *parentPath = NULL;
+
+    g_variant_get(inArgs, "(u)", &flags);
+
+    domainSnapshot = virtDBusUtilVirDomainSnapshotFromBusPath(connect->connection,
+                                                              objectPath,
+                                                              connect->domainSnapshotPath);
+    if (!domainSnapshot)
+        return;
+
+    parent = virDomainSnapshotGetParent(domainSnapshot, flags);
+    if (!parent)
+        return virtDBusUtilSetLastVirtError(error);
+
+    domainName = virtDBusUtilDecodeStr(strings[0]);
+    domain = virDomainLookupByName(connect->connection, domainName);
+    parentPath = virtDBusUtilBusPathForVirDomainSnapshot(domain,
+                                                         parent,
+                                                         connect->domainSnapshotPath);
+
+    *outArgs = g_variant_new("(o)", parentPath);
+}
+
+static void
+virtDBusDomainSnapshotGetXMLDesc(GVariant *inArgs,
+                                 GUnixFDList *inFDs G_GNUC_UNUSED,
+                                 const gchar *objectPath,
+                                 gpointer userData,
+                                 GVariant **outArgs G_GNUC_UNUSED,
+                                 GUnixFDList **outFDs G_GNUC_UNUSED,
+                                 GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autoptr(virDomainSnapshot) domainSnapshot = NULL;
+    guint flags;
+    g_autofree gchar *xml = NULL;
+
+    g_variant_get(inArgs, "(u)", &flags);
+
+    domainSnapshot = virtDBusUtilVirDomainSnapshotFromBusPath(connect->connection,
+                                                              objectPath,
+                                                              connect->domainSnapshotPath);
+    if (!domainSnapshot)
+        return;
+
+    xml = virDomainSnapshotGetXMLDesc(domainSnapshot, flags);
+    if (!xml)
+        return virtDBusUtilSetLastVirtError(error);
+
+    *outArgs = g_variant_new("(s)", xml);
+}
+
+static void
+virtDBusDomainSnapshotIsCurrent(GVariant *inArgs,
+                                GUnixFDList *inFDs G_GNUC_UNUSED,
+                                const gchar *objectPath,
+                                gpointer userData,
+                                GVariant **outArgs G_GNUC_UNUSED,
+                                GUnixFDList **outFDs G_GNUC_UNUSED,
+                                GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autoptr(virDomainSnapshot) domainSnapshot = NULL;
+    guint flags;
+    gint isCurrent;
+
+    g_variant_get(inArgs, "(u)", &flags);
+
+    domainSnapshot = virtDBusUtilVirDomainSnapshotFromBusPath(connect->connection,
+                                                              objectPath,
+                                                              connect->domainSnapshotPath);
+    if (!domainSnapshot)
+        return;
+
+    isCurrent = virDomainSnapshotIsCurrent(domainSnapshot, flags);
+    if (isCurrent < 0)
+        return virtDBusUtilSetLastVirtError(error);
+
+    *outArgs = g_variant_new("(b)", !!isCurrent);
+}
+
+static void
+virtDBusDomainSnapshotListAllChildren(GVariant *inArgs,
+                                      GUnixFDList *inFDs G_GNUC_UNUSED,
+                                      const gchar *objectPath,
+                                      gpointer userData,
+                                      GVariant **outArgs G_GNUC_UNUSED,
+                                      GUnixFDList **outFDs G_GNUC_UNUSED,
+                                      GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autoptr(virDomainSnapshot) domainSnapshot = NULL;
+    g_autoptr(virDomainSnapshotPtr) snaps = NULL;
+    guint flags;
+    GVariantBuilder builder;
+    GVariant *gdomainSnapshots;
+    g_autofree gchar *domainName = NULL;
+    g_autoptr(virDomain) domain = NULL;
+    gsize prefixLen = strlen(connect->domainSnapshotPath) + 1;
+    gchar** strings = g_strsplit(objectPath + prefixLen, "_", 2);
+
+    g_variant_get(inArgs, "(u)", &flags);
+
+    domainSnapshot = virtDBusUtilVirDomainSnapshotFromBusPath(connect->connection,
+                                                              objectPath,
+                                                              connect->domainSnapshotPath);
+    if (!domainSnapshot)
+        return;
+
+    if (virDomainSnapshotListAllChildren(domainSnapshot, &snaps, flags) < 0)
+        return virtDBusUtilSetLastVirtError(error);
+
+    g_variant_builder_init(&builder, G_VARIANT_TYPE("ao"));
+
+    domainName = virtDBusUtilDecodeStr(strings[0]);
+    domain = virDomainLookupByName(connect->connection, domainName);
+    for (gint i = 0; snaps[i]; i++) {
+        g_autofree gchar *path = NULL;
+        path = virtDBusUtilBusPathForVirDomainSnapshot(domain,
+                                                       snaps[i],
+                                                       connect->domainSnapshotPath);
+
+        g_variant_builder_add(&builder, "o", path);
+    }
+
+    gdomainSnapshots = g_variant_builder_end(&builder);
+    *outArgs = g_variant_new_tuple(&gdomainSnapshots, 1);
+}
+
+static void
+virtDBusDomainSnapshotRevert(GVariant *inArgs,
+                             GUnixFDList *inFDs G_GNUC_UNUSED,
+                             const gchar *objectPath,
+                             gpointer userData,
+                             GVariant **outArgs G_GNUC_UNUSED,
+                             GUnixFDList **outFDs G_GNUC_UNUSED,
+                             GError **error)
+{
+    virtDBusConnect *connect = userData;
+    g_autoptr(virDomainSnapshot) domainSnapshot = NULL;
+    guint flags;
+
+    g_variant_get(inArgs, "(u)", &flags);
+
+    domainSnapshot = virtDBusUtilVirDomainSnapshotFromBusPath(connect->connection,
+                                                              objectPath,
+                                                              connect->domainSnapshotPath);
+    if (!domainSnapshot)
+        return;
+
+    if (virDomainRevertToSnapshot(domainSnapshot, flags) < 0)
+        return virtDBusUtilSetLastVirtError(error);
+}
+
 static virtDBusGDBusPropertyTable virtDBusDomainSnapshotPropertyTable[] = {
     { 0 }
 };
 
 static virtDBusGDBusMethodTable virtDBusDomainSnapshotMethodTable[] = {
-    { 0 }
+    { "Delete", virtDBusDomainSnapshotDelete },
+    { "GetParent", virtDBusDomainSnapshotGetParent },
+    { "GetXMLDesc", virtDBusDomainSnapshotGetXMLDesc },
+    { "IsCurrent", virtDBusDomainSnapshotIsCurrent }, // Needs to be method since it takes 'flags' parameter
+    { "ListChildren", virtDBusDomainSnapshotListAllChildren },
+    { "Revert", virtDBusDomainSnapshotRevert },
 };
 
 static gchar **
diff --git a/tests/Makefile.am b/tests/Makefile.am
index cd1fbd7..7757429 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -14,6 +14,7 @@ test_programs = \
 	test_interface.py \
 	test_network.py \
 	test_nodedev.py \
+	test_snapshot.py \
 	test_storage.py \
 	$(NULL)
 
diff --git a/tests/libvirttest.py b/tests/libvirttest.py
index a442196..8462fc3 100644
--- a/tests/libvirttest.py
+++ b/tests/libvirttest.py
@@ -112,6 +112,20 @@ class BaseTestClass():
         """
         return self.node_device_create()
 
+    @pytest.fixture
+    def snapshot_create(self):
+        """ Fixture to create simple snapshot the test driver
+
+        This fixture should be used in the setup of every test manipulating
+        with snaphots.
+        """
+        _, test_domain = self.get_test_domain()
+        interface_obj = dbus.Interface(test_domain, 'org.libvirt.Domain')
+        path = interface_obj.SnapshotCreateXML(xmldata.minimal_snapshot_xml, 0)
+        obj = self.bus.get_object('org.libvirt', path)
+        interface_obj = dbus.Interface(obj, 'org.libvirt.DomainSnapshot')
+        return interface_obj
+
     @pytest.fixture
     def storage_volume_create(self):
         """ Fixture to create dummy storage volume on the test driver
diff --git a/tests/test_domain.py b/tests/test_domain.py
index b5879b4..fdb5aa4 100755
--- a/tests/test_domain.py
+++ b/tests/test_domain.py
@@ -2,6 +2,7 @@
 
 import dbus
 import libvirttest
+import xmldata
 
 DBUS_EXCEPTION_MISSING_FUNCTION = 'this function is not supported by the connection driver'
 
@@ -160,6 +161,13 @@ class TestDomain(libvirttest.BaseTestClass):
         pinInfo = domain.GetVcpuPinInfo(0)
         assert pinInfo == pinInfo_expected
 
+    def test_snapshot(self):
+        obj, domain = self.get_test_domain()
+        domain.SnapshotCreateXML(xmldata.minimal_snapshot_xml, 0)
+        assert isinstance(domain.SnapshotCurrent(0), dbus.ObjectPath)
+        assert isinstance(domain.SnapshotLookupByName("my_snapshot", 0), dbus.ObjectPath)
+        assert isinstance(domain.ListDomainSnapshots(0), dbus.Array)
 
 if __name__ == '__main__':
     libvirttest.run()
+
diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py
new file mode 100755
index 0000000..e9cc5b9
--- /dev/null
+++ b/tests/test_snapshot.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+import dbus
+import libvirttest
+import pytest
+
+EXCEPTION_NO_PARENT = 'does not have a parent'
+
+ at pytest.mark.usefixtures("snapshot_create")
+class TestSnapshot(libvirttest.BaseTestClass):
+    """ Tests for methods and properties of the Snapshot snapshot
+    """
+
+    def test_snapshot_delete(self, snapshot_create):
+        snapshot_obj = snapshot_create
+        snapshot_obj.Delete(0)
+
+    def test_snapshot_get_parent(self, snapshot_create):
+        snapshot_obj = snapshot_create
+        try:
+            snapshot_obj.GetParent(0)
+        except dbus.exceptions.DBusException as e:
+            if not any(EXCEPTION_NO_PARENT in arg for arg in e.args):
+                raise e
+
+    def test_snapshot_get_xml(self, snapshot_create):
+        snapshot_obj = snapshot_create
+        assert isinstance(snapshot_obj.GetXMLDesc(0), dbus.String)
+
+    def test_snapshot_get_is_current(self, snapshot_create):
+        snapshot_obj = snapshot_create
+        assert isinstance(snapshot_obj.IsCurrent(0), dbus.Boolean)
+
+    def test_snapshot_list_children(self, snapshot_create):
+        snapshot_obj = snapshot_create
+        assert isinstance(snapshot_obj.ListChildren(0), dbus.Array)
+
+    def test_snapshot_revert(self, snapshot_create):
+        snapshot_obj = snapshot_create
+        snapshot_obj.Revert(0)
+
+if __name__ == '__main__':
+    libvirttest.run()
diff --git a/tests/xmldata.py b/tests/xmldata.py
index 8075052..0146ccf 100644
--- a/tests/xmldata.py
+++ b/tests/xmldata.py
@@ -54,6 +54,12 @@ minimal_node_device_xml = '''
 </device>
 '''
 
+minimal_snapshot_xml = '''
+<domainsnapshot>
+    <name>my_snapshot</name>
+</domainsnapshot>
+'''
+
 minimal_storage_pool_xml = '''
 <pool type='dir'>
   <name>foo</name>
-- 
2.21.0




More information about the libvir-list mailing list