Unfortunately Hyper-V does not enforce any uniqueness constraints on
snapshot names (called ElementName in Hyper-V). So it's possible for
multiple snapshots of the same domain to have identical ElementNames.
Since libvirt uses the domain and snapshot name as a unique key to
reference a snapshot, we can't use the hyperv ElementName as the
snapshot name in libvirt.

So instead I've decided to use the InstanceId of the snapshot as the
snapshot name and use the ElementName as the snapshot description. This
results in a worse user experience (since the snapshot names end up
being something like "Microsoft:$(UUID)"), but guarantees that we will
be able to uniquely reference every snapshot.

Signed-off-by: Jonathon Jongsma <[email protected]>
---
 src/hyperv/hyperv_driver.c | 53 ++++++++++++++++++++++++++++++++++++++
 src/hyperv/hyperv_wmi.h    |  3 +++
 2 files changed, 56 insertions(+)

diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c
index 906f6e5d19..44b671b45e 100644
--- a/src/hyperv/hyperv_driver.c
+++ b/src/hyperv/hyperv_driver.c
@@ -4077,6 +4077,58 @@ hypervDomainGetBlockInfo(virDomainPtr domain,
 }
 
 
+static Msvm_VirtualSystemSettingData*
+hypervDomainLookupSnapshotSD(virDomainPtr domain, const char *snapshot)
+{
+    hypervPrivate *priv = domain->conn->privateData;
+    g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
+    char domain_uuid_string[VIR_UUID_STRING_BUFLEN];
+    g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
+
+    virUUIDFormat(domain->uuid, domain_uuid_string);
+
+    /* Hyper-V does not enforce unique snapshot names per domain, so we don't
+     * use the Hyper-V snapshot's ElementName field as the libvirt snapshot 
name.
+     * Instead we use the unique InstanceID as the name, even though it is not 
as
+     * user-friendly */
+    virBufferEscapeSQL(&query,
+                       MSVM_VIRTUALSYSTEMSETTINGDATA_WQL_SELECT
+                       "WHERE InstanceID='%s'",
+                       snapshot);
+    virBufferEscapeSQL(&query, "AND VirtualSystemIdentifier='%s'", 
domain_uuid_string);
+    virBufferAddLit(&query, "AND VirtualSystemType='"
+                    MSVM_VIRTUALSYSTEMSETTINGDATA_VIRTUALTYPE_SNAPSHOT "'");
+
+    if (hypervGetWmiClass(Msvm_VirtualSystemSettingData, &vssd) < 0)
+        return NULL;
+
+    if (!vssd) {
+        virReportError(VIR_ERR_NO_DOMAIN_SNAPSHOT,
+                       _("no domain snapshot with matching name '%1$s'"), 
snapshot);
+        return NULL;
+    }
+
+    return g_steal_pointer(&vssd);
+}
+
+
+static virDomainSnapshotPtr
+hypervDomainSnapshotLookupByName(virDomainPtr domain,
+                                 const char *name,
+                                 unsigned int flags)
+{
+    g_autoptr(Msvm_VirtualSystemSettingData) vssd = NULL;
+
+    virCheckFlags(0, NULL);
+
+    vssd = hypervDomainLookupSnapshotSD(domain, name);
+    if (vssd == NULL)
+        return NULL;
+
+    return virGetDomainSnapshot(domain, name);
+}
+
+
 static virHypervisorDriver hypervHypervisorDriver = {
     .name = "Hyper-V",
     .connectOpen = hypervConnectOpen, /* 0.9.5 */
@@ -4143,6 +4195,7 @@ static virHypervisorDriver hypervHypervisorDriver = {
     .connectIsAlive = hypervConnectIsAlive, /* 0.9.8 */
     .domainInterfaceAddresses = hypervDomainInterfaceAddresses, /* 12.1.0 */
     .domainGetBlockInfo = hypervDomainGetBlockInfo, /* 12.1.0 */
+    .domainSnapshotLookupByName = hypervDomainSnapshotLookupByName, /* 12.2.0 
*/
 };
 
 
diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h
index 65b1211b89..d577dbcecb 100644
--- a/src/hyperv/hyperv_wmi.h
+++ b/src/hyperv/hyperv_wmi.h
@@ -38,6 +38,9 @@
 #define MSVM_IMAGEMANAGEMENTSERVICE_SELECTOR \
     "CreationClassName=Msvm_ImageManagementService"
 
+#define MSVM_VIRTUALSYSTEMSETTINGDATA_VIRTUALTYPE_SNAPSHOT \
+    "Microsoft:Hyper-V:Snapshot:Realized"
+
 int hypervVerifyResponse(WsManClient *client, WsXmlDocH response,
                          const char *detail);
 
-- 
2.53.0

Reply via email to