This adds initial support for virDomainGetBlockInfo() for the hyperv
driver. It currently supports:
 - physical disk drives that are assigned to a vm
 - virtual disk drives backed by a .VHD file that are local to the host
 - other drives backed by local files (e.g. cdrom with a .iso)

It will fail to get allocation and physical values for any drives backed
by files that are not local to the host (e.g. on network shares)

Signed-off-by: Jonathon Jongsma <[email protected]>
---
 src/hyperv/hyperv_driver.c            | 205 ++++++++++++++++++++++++++
 src/hyperv/hyperv_wmi.c               |  41 ++++++
 src/hyperv/hyperv_wmi.h               |   7 +
 src/hyperv/hyperv_wmi_generator.input | 109 ++++++++++++++
 4 files changed, 362 insertions(+)

diff --git a/src/hyperv/hyperv_driver.c b/src/hyperv/hyperv_driver.c
index 3cebab305b..e332b8a860 100644
--- a/src/hyperv/hyperv_driver.c
+++ b/src/hyperv/hyperv_driver.c
@@ -26,6 +26,9 @@
 
 #include "internal.h"
 #include "datatypes.h"
+#include "libvirt/libvirt.h"
+#include "libvirt/virterror.h"
+#include "virbuffer.h"
 #include "virdomainobjlist.h"
 #include "virauth.h"
 #include "viralloc.h"
@@ -3822,6 +3825,207 @@ hypervDomainInterfaceAddresses(virDomainPtr dom,
 }
 
 
+static int
+hypervGetFileSize(hypervPrivate *priv,
+                  const char *filePath,
+                  unsigned long long *fileSize)
+{
+    g_autoptr(CIM_DataFile) dataFile = NULL;
+    g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
+    g_autofree char *escapedPath = NULL;
+
+    virBufferAddLit(&query, CIM_DATAFILE_WQL_SELECT);
+    virBufferEscapeSQL(&query, "WHERE Name='%s'", filePath);
+
+    if (hypervGetWmiClass(CIM_DataFile, &dataFile) < 0 || !dataFile) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Could not query file size for '%1$s'"), filePath);
+        return -1;
+    }
+
+    *fileSize = dataFile->data->FileSize;
+    return 0;
+}
+
+
+static int
+hypervGetPhysicalDiskBlockInfo(hypervPrivate *priv,
+                               unsigned int driveNumber,
+                               virDomainBlockInfoPtr info)
+{
+    g_autoptr(Win32_DiskDrive) diskDrive = NULL;
+    g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&query, WIN32_DISKDRIVE_WQL_SELECT "WHERE Index=%u", 
driveNumber);
+
+    if (hypervGetWmiClass(Win32_DiskDrive, &diskDrive) < 0 || !diskDrive) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("Could not find physical disk with drive number 
%1$u"), driveNumber);
+        return -1;
+    }
+
+    info->capacity = info->allocation = info->physical = diskDrive->data->Size;
+    return 0;
+}
+
+
+static int
+hypervGetVHDCapacity(hypervPrivate *priv,
+                     const char *path,
+                     unsigned long long *capacity)
+{
+    g_auto(WsXmlDocH) settingDataDoc = NULL;
+    g_autofree char *maxInternalSizeStr = NULL;
+
+    if (hypervImageManagementServiceGetVHDSD(priv, path, &settingDataDoc) < 0)
+        return -1;
+
+    maxInternalSizeStr = ws_xml_get_xpath_value(settingDataDoc,
+        (char *)"//PROPERTY[@NAME='MaxInternalSize']/VALUE");
+
+    if (!maxInternalSizeStr) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Could not find MaxInternalSize in VHD SettingData 
for '%1$s'"), path);
+        return -1;
+    }
+
+    if (virStrToLong_ull(maxInternalSizeStr, NULL, 10, capacity) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to parse MaxInternalSize '%1$s' for '%2$s'"),
+                       maxInternalSizeStr, path);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+hypervGetVirtualDiskBlockInfo(hypervPrivate *priv,
+                              const char *diskpath,
+                              virDomainBlockInfoPtr info)
+{
+    unsigned long long capacity = 0;
+    unsigned long long allocation = 0;
+    /* This might fail if diskpath is not a vhd file, but continue anyway as it
+     * might be e.g. an ISO that is not supported by the 
ImageManagementService */
+    int rcapacity = hypervGetVHDCapacity(priv, diskpath, &capacity);
+
+    /* querying actual file allocation only works for local files, so may fail
+     * for files on network shares */
+    int rallocation = hypervGetFileSize(priv, diskpath, &allocation);
+
+    /* if both queries were unsuccessful, just return an error */
+    if (rcapacity < 0 && rallocation < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to get info for disk 
'%s'"), diskpath);
+        return -1;
+    }
+
+    /* if we failed to get the capacity from the ImageManagementService (i.e.
+     * the disk path wasn't a vhd file), just use the file size */
+    if (capacity == 0)
+        capacity = allocation;
+
+    info->capacity = capacity;
+    info->physical = info->allocation = allocation;
+    return 0;
+}
+
+static int
+hypervDomainGetBlockInfo(virDomainPtr domain,
+                         const char *path,
+                         virDomainBlockInfoPtr info,
+                         unsigned int flags)
+{
+    hypervPrivate *priv = domain->conn->privateData;
+    char uuid_string[VIR_UUID_STRING_BUFLEN];
+    g_autoptr(Msvm_ResourceAllocationSettingData) resource_settings = NULL;
+    g_autoptr(Msvm_StorageAllocationSettingData) storage_settings = NULL;
+    g_autoptr(Msvm_VirtualSystemSettingData) system_settings = NULL;
+    g_autoptr(virDomainDef) def = NULL;
+    virDomainDiskDef *disk = NULL;
+    const char *diskpath = NULL;
+
+    virCheckFlags(0, -1);
+
+    virUUIDFormat(domain->uuid, uuid_string);
+
+    if (hypervGetMsvmVirtualSystemSettingDataFromUUID(priv, uuid_string, 
&system_settings) < 0) {
+        virReportError(VIR_ERR_NO_DOMAIN, _("No domain with UUID %1$s"), 
uuid_string);
+        return -1;
+    }
+
+    if (hypervGetResourceAllocationSD(priv,
+                                      system_settings->data->InstanceID,
+                                      &resource_settings) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to get resource allocation settings data"));
+        return -1;
+    }
+
+    if (hypervGetStorageAllocationSD(priv,
+                                     system_settings->data->InstanceID,
+                                     &storage_settings) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to get storage allocation settings data"));
+        return -1;
+    }
+
+    if (!(def = virDomainDefNew(priv->xmlopt))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Failed to create a new virDomainDef"));
+        return -1;
+    }
+
+    /* Process storage and resources to get disk names */
+    if (hypervDomainDefParseStorage(priv, def, resource_settings, 
storage_settings) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Failed to parse storage"));
+        return -1;
+    }
+
+    disk = virDomainDiskByName(def, path, false);
+    if (!disk) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("invalid path %1$s not assigned to domain"), path);
+        return -1;
+    }
+
+    diskpath = virDomainDiskGetSource(disk);
+    if (!diskpath) {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+                       _("disk '%1$s' has no source path"), path);
+        return -1;
+    }
+
+    if (virDomainDiskGetType(disk) == VIR_STORAGE_TYPE_BLOCK) {
+        unsigned int driveNumber = 0;
+        g_autoptr(Msvm_DiskDrive) diskdrive = NULL;
+
+        /* BLOCK type disks have their source path set to the windows drive 
number */
+        if (virStrToLong_ui(diskpath, NULL, 10, &driveNumber) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Invalid drive number '%1$s' for physical disk"), 
diskpath);
+            return -1;
+        }
+
+        if (hypervGetPhysicalDiskBlockInfo(priv, driveNumber, info) < 0)
+            return -1;
+    } else if (virDomainDiskGetType(disk) == VIR_STORAGE_TYPE_FILE) {
+        /* first try querying the disk via the image management service which 
supports .vhd(x) files */
+        if (hypervGetVirtualDiskBlockInfo(priv, diskpath, info) < 0)
+            return -1;
+    } else {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
+                       _("Unsupported disk type %1$d for disk '%2$s'"),
+                       virDomainDiskGetType(disk), path);
+        return -1;
+    }
+
+    return 0;
+}
+
+
 static virHypervisorDriver hypervHypervisorDriver = {
     .name = "Hyper-V",
     .connectOpen = hypervConnectOpen, /* 0.9.5 */
@@ -3886,6 +4090,7 @@ static virHypervisorDriver hypervHypervisorDriver = {
     .domainSendKey = hypervDomainSendKey, /* 3.6.0 */
     .connectIsAlive = hypervConnectIsAlive, /* 0.9.8 */
     .domainInterfaceAddresses = hypervDomainInterfaceAddresses, /* 12.1.0 */
+    .domainGetBlockInfo = hypervDomainGetBlockInfo, /* 12.1.0 */
 };
 
 
diff --git a/src/hyperv/hyperv_wmi.c b/src/hyperv/hyperv_wmi.c
index eee42b5c70..90bbc42b6d 100644
--- a/src/hyperv/hyperv_wmi.c
+++ b/src/hyperv/hyperv_wmi.c
@@ -31,6 +31,7 @@
 #include <u/syslog.h>
 
 #include "internal.h"
+#include "libvirt/virterror.h"
 #include "virerror.h"
 #include "datatypes.h"
 #include "viralloc.h"
@@ -1507,6 +1508,46 @@ hypervGetEthernetPortAllocationSD(hypervPrivate *priv,
 }
 
 
+int
+hypervImageManagementServiceGetVHDSD(hypervPrivate *priv,
+                                     const char *vhdPath,
+                                     WsXmlDocH *settingDataDoc)
+{
+    hypervInvokeParamsList *params = NULL;
+    g_auto(WsXmlDocH) response = NULL;
+    g_autofree char *settingDataXmlStr = NULL;
+
+    params = hypervCreateInvokeParamsList("GetVirtualHardDiskSettingData",
+                                          MSVM_IMAGEMANAGEMENTSERVICE_SELECTOR,
+                                          Msvm_ImageManagementService_WmiInfo);
+    if (hypervAddSimpleParam(params, "Path", vhdPath) < 0)
+        return -1;
+
+    if (hypervInvokeMethod(priv, &params, &response) < 0)
+        return -1;
+
+    settingDataXmlStr = ws_xml_get_xpath_value(response,
+        (char 
*)"/s:Envelope/s:Body/p:GetVirtualHardDiskSettingData_OUTPUT/p:SettingData");
+
+    if (!settingDataXmlStr) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Could not extract SettingData from response for 
'%1$s'"), vhdPath);
+        return -1;
+    }
+
+    /* the method returns an embedded CIM-XML document as a string, so we need
+     * to parse it as xml */
+    *settingDataDoc = ws_xml_read_memory(settingDataXmlStr, 
strlen(settingDataXmlStr), "UTF-8", 0);
+    if (!*settingDataDoc) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Could not parse VHD SettingData XML for '%1$s'"), 
vhdPath);
+        return -1;
+    }
+
+    return 0;
+}
+
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Msvm_VirtualSystemManagementService
  */
diff --git a/src/hyperv/hyperv_wmi.h b/src/hyperv/hyperv_wmi.h
index 9093aec455..11d61edfc5 100644
--- a/src/hyperv/hyperv_wmi.h
+++ b/src/hyperv/hyperv_wmi.h
@@ -36,6 +36,9 @@
 #define MSVM_VIRTUALSYSTEMMANAGEMENTSERVICE_SELECTOR \
     "CreationClassName=Msvm_VirtualSystemManagementService"
 
+#define MSVM_IMAGEMANAGEMENTSERVICE_SELECTOR \
+    "CreationClassName=Msvm_ImageManagementService"
+
 int hypervVerifyResponse(WsManClient *client, WsXmlDocH response,
                          const char *detail);
 
@@ -263,6 +266,10 @@ int hypervGetEthernetPortAllocationSD(hypervPrivate *priv,
                                       const char *id,
                                       Msvm_EthernetPortAllocationSettingData 
**data);
 
+int hypervImageManagementServiceGetVHDSD(hypervPrivate *priv,
+                                         const char *vhdPath,
+                                         WsXmlDocH *settingDataDoc);
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * Msvm_VirtualSystemManagementService
  */
diff --git a/src/hyperv/hyperv_wmi_generator.input 
b/src/hyperv/hyperv_wmi_generator.input
index e6053267f8..017b7a0fa5 100644
--- a/src/hyperv/hyperv_wmi_generator.input
+++ b/src/hyperv/hyperv_wmi_generator.input
@@ -1143,3 +1143,112 @@ class Msvm_VideoHead
     uint32   CurrentNumberOfColumns
     uint64   CurrentNumberOfColors
 end
+
+
+class Msvm_ImageManagementService
+    string   InstanceID
+    string   Caption
+    string   Description
+    string   ElementName
+    datetime InstallDate
+    string   Name
+    uint16   OperationalStatus[]
+    string   StatusDescriptions[]
+    string   Status
+    uint16   HealthState
+    uint16   CommunicationStatus
+    uint16   DetailedStatus
+    uint16   OperatingStatus
+    uint16   PrimaryStatus
+    uint16   EnabledState
+    string   OtherEnabledState
+    uint16   RequestedState
+    uint16   EnabledDefault
+    datetime TimeOfLastStateChange
+    uint16   AvailableRequestedStates[]
+    uint16   TransitioningToState
+    string   SystemCreationClassName
+    string   SystemName
+    string   CreationClassName
+    string   PrimaryOwnerName
+    string   PrimaryOwnerContact
+    string   StartMode
+    boolean  Started
+end
+
+
+class Msvm_VirtualHardDiskSettingData
+    string   InstanceID
+    string   Caption
+    string   Description
+    string   ElementName
+    uint16   Type
+    uint16   Format
+    string   Path
+    string   ParentPath
+    datetime ParentTimestamp
+    string   ParentIdentifier
+    uint64   MaxInternalSize
+    uint32   BlockSize
+    uint32   LogicalSectorSize
+    uint32   PhysicalSectorSize
+    string   VirtualDiskId
+    uint64   DataAlignment
+    uint16   PmemAddressAbstractionType
+    boolean  IsPmemCompatible
+end
+
+
+class Win32_DiskDrive
+    uint16   Availability
+    uint32   BytesPerSector
+    uint16   Capabilities[]
+    string   CapabilityDescriptions[]
+    string   Caption
+    string   CompressionMethod
+    uint32   ConfigManagerErrorCode
+    boolean  ConfigManagerUserConfig
+    string   CreationClassName
+    uint64   DefaultBlockSize
+    string   Description
+    string   DeviceID
+    boolean  ErrorCleared
+    string   ErrorDescription
+    string   ErrorMethodology
+    string   FirmwareRevision
+    uint32   Index
+    datetime InstallDate
+    string   InterfaceType
+    uint32   LastErrorCode
+    string   Manufacturer
+    uint64   MaxBlockSize
+    uint64   MaxMediaSize
+    boolean  MediaLoaded
+    string   MediaType
+    uint64   MinBlockSize
+    string   Model
+    string   Name
+    boolean  NeedsCleaning
+    uint32   NumberOfMediaSupported
+    uint32   Partitions
+    string   PNPDeviceID
+    uint16   PowerManagementCapabilities[]
+    boolean  PowerManagementSupported
+    uint32   SCSIBus
+    uint16   SCSILogicalUnit
+    uint16   SCSIPort
+    uint16   SCSITargetId
+    uint32   SectorsPerTrack
+    string   SerialNumber
+    uint32   Signature
+    uint64   Size
+    string   Status
+    uint16   StatusInfo
+    string   SystemCreationClassName
+    string   SystemName
+    uint64   TotalCylinders
+    uint32   TotalHeads
+    uint64   TotalSectors
+    uint64   TotalTracks
+    uint32   TracksPerCylinder
+end
-- 
2.53.0

Reply via email to