Skip to content

Commit 221111a

Browse files
author
Wanru Liu
committed
MTV-5556: Warm precopy on Pure FlashArray vVol falls back to slow vmkfstools clone instead of array offload
When a precopy snapshot is active, the top-of-chain backing's filename is the child disk (foo-000001.vmdk) while forklift passes the base path (foo.vmdk). The old top-only path comparison missed this and the populator fell through to the VMDK/vmkfstools fallback. vmkfstools still produced correct bytes (it opens foo.vmdk on the host and VASA serves the snap-vVol), but at host-mediated speed. Walk the Parent chain and return the BackingObjectId of the node whose filename matches the caller's vmdkPath. Warm precopy now matches at the parent, identifies the snap-vVol, and copies it via Pure REST. Signed-off-by: Wanru Liu <waliu@purestorage.com>
1 parent 84d6968 commit 221111a

2 files changed

Lines changed: 60 additions & 27 deletions

File tree

cmd/vsphere-xcopy-volume-populator/internal/pure/flashArray.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -243,25 +243,43 @@ func (f *FlashArrayClonner) getSourceVolume(vsphereClient vmware.Client, vmId st
243243
if disk, ok := device.(*types.VirtualDisk); ok {
244244
if backing, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
245245
// Check if this is a VVol backing and matches our target VMDK
246-
if backing.BackingObjectId != "" && f.matchesVMDKPath(backing.FileName, vmDisk) {
247-
klog.Infof("Found VVol backing for VMDK %s with ID %s", vmDisk.VmdkFile, backing.BackingObjectId)
248-
249-
// Use REST client to find the volume by VVol ID
250-
volumeName, err := f.restClient.FindVolumeByVVolID(backing.BackingObjectId)
251-
if err != nil {
252-
klog.Warningf("Failed to find volume by VVol ID %s: %v", backing.BackingObjectId, err)
253-
continue
254-
}
246+
matched := f.findMatchingFlatBacking(backing, vmDisk)
247+
if matched == nil || matched.BackingObjectId == "" {
248+
continue
249+
}
250+
klog.Infof("Found VVol backing for VMDK %s with ID %s (matched at FileName %s)",
251+
vmDisk.VmdkFile, matched.BackingObjectId, matched.FileName)
255252

256-
return volumeName, nil
253+
// Use REST client to find the volume by VVol ID
254+
volumeName, err := f.restClient.FindVolumeByVVolID(matched.BackingObjectId)
255+
if err != nil {
256+
klog.Warningf("Failed to find volume by VVol ID %s: %v", matched.BackingObjectId, err)
257+
continue
257258
}
259+
return volumeName, nil
258260
}
259261
}
260262
}
261263

262264
return "", fmt.Errorf("VVol backing for VMDK %s not found", vmDisk.VmdkFile)
263265
}
264266

267+
// findMatchingFlatBacking returns the first node in b's Parent chain whose
268+
// filename matches vmDisk, or nil if none does.
269+
//
270+
// While a snapshot is active the chain is "child disk (foo-000001.vmdk) ->
271+
// parent (foo.vmdk)", and VASA reports a different BackingObjectId per level:
272+
// child -> base vVol (current writes), parent -> snap-vVol (frozen). The
273+
// caller's vmdkPath picks which level matches.
274+
func (f *FlashArrayClonner) findMatchingFlatBacking(b *types.VirtualDiskFlatVer2BackingInfo, vmDisk populator.VMDisk) *types.VirtualDiskFlatVer2BackingInfo {
275+
for cur := b; cur != nil; cur = cur.Parent {
276+
if f.matchesVMDKPath(cur.FileName, vmDisk) {
277+
return cur
278+
}
279+
}
280+
return nil
281+
}
282+
265283
// matchesVMDKPath checks if a vSphere VVol filename matches the target VMDK
266284
func (f *FlashArrayClonner) matchesVMDKPath(fileName string, vmDisk populator.VMDisk) bool {
267285
fileBase := filepath.Base(fileName)

cmd/vsphere-xcopy-volume-populator/internal/vmware/client.go

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ package vmware
33
import (
44
"context"
55
"encoding/xml"
6+
"fmt"
67
"net/url"
78
"strings"
89

9-
"fmt"
10-
1110
"github.com/vmware/govmomi"
1211
"github.com/vmware/govmomi/cli/esx"
1312
"github.com/vmware/govmomi/find"
@@ -24,7 +23,7 @@ type Client interface {
2423
GetEsxByVm(ctx context.Context, vmName string) (*object.HostSystem, error)
2524
RunEsxCommand(ctx context.Context, host *object.HostSystem, command []string) ([]esx.Values, error)
2625
GetDatastore(ctx context.Context, dc *object.Datacenter, datastore string) (*object.Datastore, error)
27-
// GetVMDiskBacking returns disk backing information for detecting disk type (VVol, RDM, VMDK)
26+
// GetVMDiskBacking returns disk backing information for detecting disk type (VVol, RDM, VMDK).
2827
GetVMDiskBacking(ctx context.Context, vmId string, vmdkPath string) (*DiskBacking, error)
2928
}
3029

@@ -193,31 +192,28 @@ func (c *VSphereClient) GetVMDiskBacking(ctx context.Context, vmId string, vmdkP
193192
// Check different backing types
194193
switch backing := disk.Backing.(type) {
195194
case *types.VirtualDiskFlatVer2BackingInfo:
196-
// Check if this disk matches the requested path
197-
if !strings.Contains(strings.ToLower(backing.FileName), normalizedPath) &&
198-
!strings.Contains(normalizedPath, strings.ToLower(backing.FileName)) {
199-
// Try to match by extracting datastore and path
200-
if !diskPathMatches(backing.FileName, vmdkPath) {
201-
continue
202-
}
195+
// Walk the chain to handle warm precopy. While a precopy snapshot is active,
196+
// vSphere adds a child disk (foo-000001.vmdk) on top of the original
197+
// (foo.vmdk). vmdkPath is the base path, so the match lives at the parent
198+
// level, not the top.
199+
matched := findMatchingFlatBacking(backing, normalizedPath, vmdkPath)
200+
if matched == nil {
201+
continue
203202
}
204203

205-
// Check for VVol backing
206-
if backing.BackingObjectId != "" {
204+
if matched.BackingObjectId != "" {
207205
klog.V(2).Infof("Disk %s is VVol-backed (BackingObjectId: %s)", vmdkPath, backing.BackingObjectId)
208206
return &DiskBacking{
209-
VVolId: backing.BackingObjectId,
207+
VVolId: matched.BackingObjectId,
210208
IsRDM: false,
211-
DeviceName: backing.FileName,
209+
DeviceName: matched.FileName,
212210
}, nil
213211
}
214-
215-
// Regular VMDK
216212
klog.V(2).Infof("Disk %s is VMDK-backed", vmdkPath)
217213
return &DiskBacking{
218214
VVolId: "",
219215
IsRDM: false,
220-
DeviceName: backing.FileName,
216+
DeviceName: matched.FileName,
221217
}, nil
222218

223219
case *types.VirtualDiskRawDiskMappingVer1BackingInfo:
@@ -247,6 +243,25 @@ func (c *VSphereClient) GetVMDiskBacking(ctx context.Context, vmId string, vmdkP
247243
}, nil
248244
}
249245

246+
// findMatchingFlatBacking returns the first node in b's Parent chain whose
247+
// filename matches vmdkPath, or nil if none does.
248+
//
249+
// While a snapshot is active the chain is "child disk (foo-000001.vmdk) ->
250+
// parent (foo.vmdk)", and VASA reports a different BackingObjectId per level:
251+
// child -> base vVol (current writes), parent -> snap-vVol (frozen). The
252+
// caller's vmdkPath picks which level matches.
253+
func findMatchingFlatBacking(b *types.VirtualDiskFlatVer2BackingInfo, normalizedPath, vmdkPath string) *types.VirtualDiskFlatVer2BackingInfo {
254+
for cur := b; cur != nil; cur = cur.Parent {
255+
fn := strings.ToLower(cur.FileName)
256+
if strings.Contains(fn, normalizedPath) ||
257+
strings.Contains(normalizedPath, fn) ||
258+
diskPathMatches(cur.FileName, vmdkPath) {
259+
return cur
260+
}
261+
}
262+
return nil
263+
}
264+
250265
// diskPathMatches compares two VMDK paths accounting for different formats
251266
func diskPathMatches(path1, path2 string) bool {
252267
// Extract datastore and filename from both paths

0 commit comments

Comments
 (0)