From b9669ec051d3c249a808bf39802ed10b5c4b491b Mon Sep 17 00:00:00 2001 From: neerajdixit-msft2 <123779900+neerajdixit-msft2@users.noreply.github.com> Date: Fri, 12 Dec 2025 16:01:26 -0800 Subject: [PATCH] Merge pull request #185 from microsoft/users/ndixit/ReconnectFixes (#186) Fixes for VM reconnect Co-authored-by: Jia Yu --- .../disk/virtualharddisksettingdata.go | 37 +++++++++++++++ .../storage/service/ImageManagementService.go | 46 ++++++++++++++++++- .../service/ImageManagementService_test.go | 6 ++- .../core/virtualsystem/virtualmachine.go | 23 ++++++++-- 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/pkg/virtualization/core/storage/disk/virtualharddisksettingdata.go b/pkg/virtualization/core/storage/disk/virtualharddisksettingdata.go index 5d4ef3d3e..74c9f088f 100644 --- a/pkg/virtualization/core/storage/disk/virtualharddisksettingdata.go +++ b/pkg/virtualization/core/storage/disk/virtualharddisksettingdata.go @@ -33,6 +33,16 @@ const ( VirtualHardDiskFormat_2 = 3 ) +// These constants represent the disk type returned by WMI method ImageManagementService.GetVirtualHardDiskSettingData +type VirtualHardDiskTypeSetting uint16 + +const ( + VirtualHardDiskTypeSetting_Unknown = 0 + VirtualHardDiskTypeSetting_Fixed = 2 + VirtualHardDiskTypeSetting_Dynamic = 3 + VirtualHardDiskTypeSetting_Differencing = 4 +) + type INSTANCE struct { XMLName xml.Name `xml:"INSTANCE"` Text string `xml:",chardata"` @@ -98,6 +108,7 @@ func GetVirtualHardDiskSettingDataFromXml(whost *host.WmiHost, xmlInstance strin pSectorSize uint32, format uint16, virtualDiskId string, + dynamic bool, err error) { log.Printf("Decoding WMI response [%s]\n", xmlInstance) @@ -141,6 +152,32 @@ func GetVirtualHardDiskSettingDataFromXml(whost *host.WmiHost, xmlInstance strin format = uint16(tempvar) case "VirtualDiskId": virtualDiskId = property.VALUE + case "Type": + tempvar, err = strconv.ParseUint(property.VALUE, 10, 16) + if err != nil { + return + } + dynamic = uint16(tempvar) == uint16(VirtualHardDiskTypeSetting_Dynamic) + } + } + + return +} + +func GetVirtualHardDiskPathsFromXml(whost *host.WmiHost, xmlInstance string) (path string, parentPath string, err error) { + log.Printf("Decoding WMI response [%s]\n", xmlInstance) + var diskData INSTANCE + err = xml.Unmarshal([]byte(xmlInstance), &diskData) + if err != nil { + return + } + + for _, property := range diskData.PROPERTY { + switch property.NAME { + case "Path": + path = property.VALUE + case "ParentPath": + parentPath = property.VALUE } } diff --git a/pkg/virtualization/core/storage/service/ImageManagementService.go b/pkg/virtualization/core/storage/service/ImageManagementService.go index 29c58bb0a..657626a7e 100644 --- a/pkg/virtualization/core/storage/service/ImageManagementService.go +++ b/pkg/virtualization/core/storage/service/ImageManagementService.go @@ -180,6 +180,7 @@ func (ims *ImageManagementService) GetVirtualHardDiskConfig(path string) (size u pSectorSize uint32, format uint16, virtualDiskId string, + dynamic bool, err error) { method, err := ims.GetWmiMethod("GetVirtualHardDiskSettingData") if err != nil { @@ -205,7 +206,7 @@ func (ims *ImageManagementService) GetVirtualHardDiskConfig(path string) (size u } val, ok := result.OutMethodParams["SettingData"] if ok && val.Value != nil { - size, blockSize, lSectorSize, pSectorSize, format, virtualDiskId, err = disk.GetVirtualHardDiskSettingDataFromXml(ims.GetWmiHost(), val.Value.(string)) + size, blockSize, lSectorSize, pSectorSize, format, virtualDiskId, dynamic, err = disk.GetVirtualHardDiskSettingDataFromXml(ims.GetWmiHost(), val.Value.(string)) if err != nil { return } @@ -228,3 +229,46 @@ func (ims *ImageManagementService) GetVirtualHardDiskConfig(path string) (size u err = job.WaitForJobCompletion(result.ReturnValue, -1) return } + +// This method recursively calls the checkpoint chain until the parent path is empty which represents the base disk +func (ims *ImageManagementService) GetBaseVirtualHardDiskPath(vhdpath string) (path string, parentPath string, err error) { + method, err := ims.GetWmiMethod("GetVirtualHardDiskSettingData") + if err != nil { + return + } + defer method.Close() + + inparams := wmi.WmiMethodParamCollection{} + inparams = append(inparams, wmi.NewWmiMethodParam("Path", vhdpath)) + + outparams := wmi.WmiMethodParamCollection{} + outparams = append(outparams, wmi.NewWmiMethodParam("SettingData", nil)) + outparams = append(outparams, wmi.NewWmiMethodParam("Job", nil)) + + result, err := method.Execute(inparams, outparams) + if err != nil { + return + } + + if result.ReturnValue != 0 { + err = errors.Wrapf(errors.Failed, "GetVirtualHardDiskSettingData method failed with [%d]", result.ReturnValue) + return + } + + val, ok := result.OutMethodParams["SettingData"] + if !ok || val.Value == nil { + err = errors.Wrapf(errors.Failed, "GetVirtualHardDiskSettingData method returned unexpected result") + return + } + + path, parentPath, err = disk.GetVirtualHardDiskPathsFromXml(ims.GetWmiHost(), val.Value.(string)) + if err != nil { + return + } + + if parentPath == "" { + return + } + + return ims.GetBaseVirtualHardDiskPath(parentPath) +} diff --git a/pkg/virtualization/core/storage/service/ImageManagementService_test.go b/pkg/virtualization/core/storage/service/ImageManagementService_test.go index 7b002522f..cc6f53cd8 100644 --- a/pkg/virtualization/core/storage/service/ImageManagementService_test.go +++ b/pkg/virtualization/core/storage/service/ImageManagementService_test.go @@ -95,7 +95,7 @@ func TestGetVirtualHardDiskConfig(t *testing.T) { } defer os.RemoveAll(path) - readSize, _, readLsectorSize, readPsectorSize, _, readVirtualDiskId, err := ims.GetVirtualHardDiskConfig(path) + readSize, _, readLsectorSize, readPsectorSize, _, readVirtualDiskId, dynamic, err := ims.GetVirtualHardDiskConfig(path) if err != nil { t.Fatal("Get vhd configuration failed " + err.Error()) } @@ -116,6 +116,10 @@ func TestGetVirtualHardDiskConfig(t *testing.T) { t.Fatal("Get vhd configuration virtual disk id is empty") } + if !dynamic { + t.Fatal("Get vhd configuration dynamic flag mismatch") + } + t.Logf("Virtual Disk Id: %s", readVirtualDiskId) hostId := strings.ReplaceAll(readVirtualDiskId, "-", "") if len(hostId) != 32 { diff --git a/pkg/virtualization/core/virtualsystem/virtualmachine.go b/pkg/virtualization/core/virtualsystem/virtualmachine.go index 6c8a895ba..c88e5f037 100644 --- a/pkg/virtualization/core/virtualsystem/virtualmachine.go +++ b/pkg/virtualization/core/virtualsystem/virtualmachine.go @@ -31,6 +31,7 @@ import ( "github.com/microsoft/wmi/pkg/virtualization/core/storage/controller" "github.com/microsoft/wmi/pkg/virtualization/core/storage/disk" "github.com/microsoft/wmi/pkg/virtualization/core/storage/drive" + "github.com/microsoft/wmi/pkg/virtualization/core/storage/service" "github.com/microsoft/wmi/pkg/virtualization/network/switchport" na "github.com/microsoft/wmi/pkg/virtualization/network/virtualnetworkadapter" wmi "github.com/microsoft/wmi/pkg/wmiinstance" @@ -1231,12 +1232,16 @@ func (vm *VirtualMachine) GetVirtualHardDiskByPath(path string) (vhd *disk.Virtu err = err1 return } + vhdpath, err1 := tmpvhd.GetPath() - if err != nil { + if err1 != nil { err = err1 return } - if vhdpath == path { + + // filepath.Clean handles cross-platform path comparison issues like OS platform specific separators + // strings.ToLower handles windows server paths which are case-insensitive + if filepath.Clean(strings.ToLower(vhdpath)) == filepath.Clean(strings.ToLower(path)) { vhdclone, err1 := tmpvhd.Clone() if err1 != nil { err = err1 @@ -1257,6 +1262,11 @@ func (vm *VirtualMachine) GetAttachedVirtualHardDisks() (vhdPaths []string, err } defer col.Close() + ims, err := service.GetImageManagementService(vm.GetWmiHost()) + if err != nil { + return + } + for _, inst := range col { retVhd, err1 := disk.NewVirtualHardDisk(inst.WmiInstance) if err1 != nil { @@ -1269,7 +1279,14 @@ func (vm *VirtualMachine) GetAttachedVirtualHardDisks() (vhdPaths []string, err err = fmt.Errorf("unable to read HostResource field from disk WMI %s", err1) return } - vhdPaths = append(vhdPaths, vhdpath[0]) + + baseVhdPath, _, err1 := ims.GetBaseVirtualHardDiskPath(vhdpath[0]) + if err1 != nil { + err = err1 + return + } + + vhdPaths = append(vhdPaths, baseVhdPath) } return