Skip to content

Commit 709fb36

Browse files
committed
Set Reconcile Sync period for list volumes to 5m & Enhance ListVolume API in the wcp controller to look for volumeattachment status for Pod VM PVC & PV
1 parent 91e909b commit 709fb36

2 files changed

Lines changed: 169 additions & 12 deletions

File tree

pkg/csi/service/wcp/controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2413,7 +2413,7 @@ func (c *controller) ListVolumes(ctx context.Context, req *csi.ListVolumesReques
24132413
volumeIDs = append(volumeIDs, cnsVolumeIDs[i])
24142414
}
24152415

2416-
response, err := getVolumeIDToVMMap(ctx, volumeIDs, vmMoidToHostMoid, volumeIDToVMMap)
2416+
response, err := getVolumeIDToVMMap(ctx, c.k8sClient, volumeIDs, vmMoidToHostMoid, volumeIDToVMMap)
24172417
if err != nil {
24182418
log.Errorf("Error while generating ListVolume response, err:%v", err)
24192419
return nil, csifault.CSIInternalFault, status.Error(codes.Internal, "Error while generating ListVolume response")

pkg/csi/service/wcp/controller_helper.go

Lines changed: 168 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"google.golang.org/grpc/credentials/insecure"
3535
"google.golang.org/grpc/status"
3636
v1 "k8s.io/api/core/v1"
37+
storagev1 "k8s.io/api/storage/v1"
3738
apierrors "k8s.io/apimachinery/pkg/api/errors"
3839
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3940
"k8s.io/apimachinery/pkg/fields"
@@ -48,6 +49,7 @@ import (
4849
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common"
4950
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common/commonco"
5051
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger"
52+
csitypes "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/types"
5153
k8s "sigs.k8s.io/vsphere-csi-driver/v3/pkg/kubernetes"
5254
"sigs.k8s.io/vsphere-csi-driver/v3/pkg/syncer/k8scloudoperator"
5355
)
@@ -585,10 +587,129 @@ func (c *controller) GetVolumeToHostMapping(ctx context.Context,
585587
return vmMoIDToHostMoID, volumeIDVMMap, nil
586588
}
587589

588-
// getVolumeIDToVMMap returns the csi list volume response by computing the volumeID to nodeNames map for
589-
// fake attached volumes and non-fake attached volumes.
590-
func getVolumeIDToVMMap(ctx context.Context, volumeIDs []string, vmMoidToHostMoid,
591-
volumeIDToVMMap map[string]string) (*csi.ListVolumesResponse, error) {
590+
// getPublishedNodesFromVolumeAttachments queries the Kubernetes API for all
591+
// VolumeAttachment objects belonging to the vSphere CSI driver and returns a
592+
// map of CSI volumeHandle → list of node names on which the volume is
593+
// currently published (i.e. VA.Status.Attached == true).
594+
//
595+
// For Pod VM workloads the Kubernetes node that is recorded on the
596+
// VolumeAttachment is the supervisor node (ESXi host), not the individual
597+
// Pod VM. The VA.Status.AttachmentMetadata["vmUUID"] field carries the
598+
// instance UUID of the Pod VM that actually consumed the volume. Both cases
599+
// are captured here so that the ListVolumes response correctly reflects the
600+
// published node for every attach path:
601+
//
602+
// - Regular TKG / supervisor VMs → Spec.NodeName (ESXi host name)
603+
// - Pod VMs → AttachmentMetadata["vmUUID"] if present,
604+
// otherwise falls back to Spec.NodeName
605+
func getPublishedNodesFromVolumeAttachments(
606+
ctx context.Context,
607+
k8sClient kubernetes.Interface,
608+
volumeIDs []string,
609+
) (map[string][]string, error) {
610+
log := logger.GetLogger(ctx)
611+
612+
// Build a set of volumeIDs we care about for O(1) lookup.
613+
volumeIDSet := make(map[string]struct{}, len(volumeIDs))
614+
for _, id := range volumeIDs {
615+
volumeIDSet[id] = struct{}{}
616+
}
617+
618+
vaList, err := k8sClient.StorageV1().VolumeAttachments().List(ctx, metav1.ListOptions{})
619+
if err != nil {
620+
return nil, fmt.Errorf("failed to list VolumeAttachments: %v", err)
621+
}
622+
623+
volumeIDToNodes := make(map[string][]string)
624+
nodesSeen := make(map[string]map[string]struct{}) // volumeID → set of nodes (dedup)
625+
626+
for i := range vaList.Items {
627+
va := &vaList.Items[i]
628+
if !isPodVMVolumeAttachment(va) {
629+
continue
630+
}
631+
if !va.Status.Attached {
632+
continue
633+
}
634+
if va.Spec.Source.PersistentVolumeName == nil {
635+
continue
636+
}
637+
638+
// Resolve PV name → CSI volumeHandle via the PV object.
639+
pvName := *va.Spec.Source.PersistentVolumeName
640+
pv, err := k8sClient.CoreV1().PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{})
641+
if err != nil {
642+
log.Warnf("getPublishedNodesFromVolumeAttachments: failed to get PV %q: %v", pvName, err)
643+
continue
644+
}
645+
if pv.Spec.CSI == nil {
646+
continue
647+
}
648+
volumeHandle := pv.Spec.CSI.VolumeHandle
649+
if _, ok := volumeIDSet[volumeHandle]; !ok {
650+
// Not in the page of volumes we are building a response for.
651+
continue
652+
}
653+
654+
// Determine the node identifier to report.
655+
// For Pod VMs the AttachmentMetadata carries the VM instance UUID;
656+
// use that when present so the caller can correlate to the Pod VM.
657+
// For all other cases report the Kubernetes node name (ESXi host).
658+
nodeName := va.Spec.NodeName
659+
if vmUUID, ok := va.Status.AttachmentMetadata[common.AttributeVmUUID]; ok && vmUUID != "" {
660+
log.Debugf("getPublishedNodesFromVolumeAttachments: volume %q is attached to Pod VM UUID %q "+
661+
"on node %q", volumeHandle, vmUUID, nodeName)
662+
nodeName = vmUUID
663+
}
664+
665+
if _, exists := nodesSeen[volumeHandle]; !exists {
666+
nodesSeen[volumeHandle] = make(map[string]struct{})
667+
}
668+
if _, dup := nodesSeen[volumeHandle][nodeName]; !dup {
669+
nodesSeen[volumeHandle][nodeName] = struct{}{}
670+
volumeIDToNodes[volumeHandle] = append(volumeIDToNodes[volumeHandle], nodeName)
671+
}
672+
}
673+
674+
log.Debugf("getPublishedNodesFromVolumeAttachments: volumeID→nodes map: %v", volumeIDToNodes)
675+
return volumeIDToNodes, nil
676+
}
677+
678+
// isPodVMVolumeAttachment returns true when the VolumeAttachment was created
679+
// by the vSphere CSI driver for a Pod VM workload. A VA is considered to be a
680+
// Pod VM VA when:
681+
// - its attacher field matches the vSphere CSI driver name, AND
682+
// - its Status.AttachmentMetadata contains a "vmUUID" key (populated by the
683+
// driver during ControllerPublishVolume for Pod VMs).
684+
//
685+
// Note: regular supervisor-VM VAs also have the same attacher but will NOT
686+
// have the "vmUUID" metadata key, so this predicate is safe to use without
687+
// additional filtering. The caller must decide how to handle regular VAs.
688+
func isPodVMVolumeAttachment(va *storagev1.VolumeAttachment) bool {
689+
if va.Spec.Attacher != csitypes.Name {
690+
return false
691+
}
692+
_, hasPodVMUUID := va.Status.AttachmentMetadata[common.AttributeVmUUID]
693+
return hasPodVMUUID
694+
}
695+
696+
// getVolumeIDToVMMap returns the csi list volume response by computing the
697+
// volumeID to nodeNames map for fake-attached, regular, and Pod VM volumes.
698+
//
699+
// Pod VM PVCs are attached to ephemeral VMs that run inside the supervisor
700+
// cluster namespace. Their attachment path does not go through the standard
701+
// VM → ESXi host mapping used for regular TKG node VMs. Instead, each Pod VM
702+
// creates a VolumeAttachment whose Status.AttachmentMetadata["vmUUID"] holds
703+
// the instance UUID of the Pod VM. This function queries all VolumeAttachment
704+
// objects to discover Pod VM published nodes and merges them into the response
705+
// alongside the host-based published nodes for regular volumes.
706+
func getVolumeIDToVMMap(
707+
ctx context.Context,
708+
k8sClient kubernetes.Interface,
709+
volumeIDs []string,
710+
vmMoidToHostMoid map[string]string,
711+
volumeIDToVMMap map[string]string,
712+
) (*csi.ListVolumesResponse, error) {
592713
log := logger.GetLogger(ctx)
593714
response := &csi.ListVolumesResponse{}
594715

@@ -600,7 +721,7 @@ func getVolumeIDToVMMap(ctx context.Context, volumeIDs []string, vmMoidToHostMoi
600721
}
601722
}
602723

603-
// Process fake attached volumes
724+
// ── 1. Fake-attached volumes ──────────────────────────────────────────────
604725
log.Debugf("Fake attached volumes %v", fakeAttachedVolumes)
605726
volumeIDToNodesMap := commonco.ContainerOrchestratorUtility.GetNodesForVolumes(ctx, fakeAttachedVolumes)
606727
for volumeID, publishedNodeIDs := range volumeIDToNodesMap {
@@ -617,6 +738,42 @@ func getVolumeIDToVMMap(ctx context.Context, volumeIDs []string, vmMoidToHostMoi
617738
response.Entries = append(response.Entries, entry)
618739
}
619740

741+
// ── 2. Pod VM volumes (VolumeAttachment-based lookup) ────────────────────
742+
// Pod VMs are ephemeral VMs running inside supervisor namespaces. Their
743+
// disks do not appear in the host-VM hardware scan performed by
744+
// GetVolumeToHostMapping, so we must derive their published nodes directly
745+
// from the VolumeAttachment status.
746+
podVMPublishedNodes, err := getPublishedNodesFromVolumeAttachments(ctx, k8sClient, volumeIDs)
747+
if err != nil {
748+
// Non-fatal: log and continue. The regular VM path below will still
749+
// populate published nodes for non-Pod VM volumes.
750+
log.Warnf("getVolumeIDToVMMap: failed to get Pod VM published nodes from VolumeAttachments: %v", err)
751+
}
752+
753+
// Track which volumes have already been added via the Pod VM path so we
754+
// don't double-count them in the regular VM section below.
755+
podVMHandled := make(map[string]struct{}, len(podVMPublishedNodes))
756+
for volumeID, nodeIDs := range podVMPublishedNodes {
757+
podVMHandled[volumeID] = struct{}{}
758+
// Skip volumes that are already represented as fake-attached above.
759+
if _, isFake := allFakeAttachMarkedVolumes[volumeID]; isFake {
760+
continue
761+
}
762+
volume := &csi.Volume{
763+
VolumeId: volumeID,
764+
}
765+
volumeStatus := &csi.ListVolumesResponse_VolumeStatus{
766+
PublishedNodeIds: nodeIDs,
767+
}
768+
entry := &csi.ListVolumesResponse_Entry{
769+
Volume: volume,
770+
Status: volumeStatus,
771+
}
772+
log.Debugf("getVolumeIDToVMMap: Pod VM volume %q published on nodes %v", volumeID, nodeIDs)
773+
response.Entries = append(response.Entries, entry)
774+
}
775+
776+
// ── 3. Regular VM volumes (ESXi host-based lookup) ───────────────────────
620777
hostNames := commonco.ContainerOrchestratorUtility.GetNodeIDtoNameMap(ctx)
621778
if len(hostNames) == 0 {
622779
log.Errorf("no hostnames found in the NodeIDtoName map")
@@ -625,13 +782,14 @@ func getVolumeIDToVMMap(ctx context.Context, volumeIDs []string, vmMoidToHostMoi
625782

626783
for volumeID, VMMoID := range volumeIDToVMMap {
627784
isFakeAttached, exists := allFakeAttachMarkedVolumes[volumeID]
628-
// If we do not find this entry in the input list obtained from CNS
629-
//, then we do not bother adding it to the result since, CNS is not aware
630-
// of this volume. Also, if it is fake attached volume we have handled it
631-
// above so we will not add it to the response here.
785+
// Skip volumes CNS is not aware of, or already handled via fake-attach
786+
// or Pod VM paths.
632787
if !exists || isFakeAttached {
633788
continue
634789
}
790+
if _, handledByPodVM := podVMHandled[volumeID]; handledByPodVM {
791+
continue
792+
}
635793

636794
hostMoID, ok := vmMoidToHostMoid[VMMoID]
637795
if !ok {
@@ -642,8 +800,7 @@ func getVolumeIDToVMMap(ctx context.Context, volumeIDs []string, vmMoidToHostMoi
642800
if !ok {
643801
continue
644802
}
645-
publishedNodeIDs := make([]string, 0)
646-
publishedNodeIDs = append(publishedNodeIDs, hostName)
803+
publishedNodeIDs := []string{hostName}
647804
volume := &csi.Volume{
648805
VolumeId: volumeID,
649806
}

0 commit comments

Comments
 (0)