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 <jjongsma@redhat.com>
---
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, ¶ms, &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
On 2/19/26 10:31 PM, Jonathon Jongsma via Devel wrote:
> 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 <jjongsma@redhat.com>
> ---
> 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(+)
Squash this in:
diff --git i/src/hyperv/hyperv_driver.c w/src/hyperv/hyperv_driver.c
index e332b8a860..a363c58a3c 100644
--- i/src/hyperv/hyperv_driver.c
+++ w/src/hyperv/hyperv_driver.c
@@ -26,8 +26,6 @@
#include "internal.h"
#include "datatypes.h"
-#include "libvirt/libvirt.h"
-#include "libvirt/virterror.h"
#include "virbuffer.h"
#include "virdomainobjlist.h"
#include "virauth.h"
@@ -3917,7 +3915,9 @@ hypervGetVirtualDiskBlockInfo(hypervPrivate *priv,
/* 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);
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to get info for disk '%1$s'"),
+ diskpath);
return -1;
}
diff --git i/src/hyperv/hyperv_wmi.c w/src/hyperv/hyperv_wmi.c
index 90bbc42b6d..4611833142 100644
--- i/src/hyperv/hyperv_wmi.c
+++ w/src/hyperv/hyperv_wmi.c
@@ -31,7 +31,6 @@
#include <u/syslog.h>
#include "internal.h"
-#include "libvirt/virterror.h"
#include "virerror.h"
#include "datatypes.h"
#include "viralloc.h"
Michal
© 2016 - 2026 Red Hat, Inc.