@@ -545,6 +545,30 @@ func (c *controller) createBlockVolume(ctx context.Context, req *csi.CreateVolum
545
545
}
546
546
// Initiate TKGs HA workflow when the topology requirement contains zone labels only.
547
547
log .Infof ("Topology aware environment detected with requirement: %+v" , topologyRequirement )
548
+
549
+ // if volume is created from snapshot, get the datastore accessible topology from the snapshot
550
+ if req .GetVolumeContentSource () != nil {
551
+ snapshotID := ""
552
+ if req .GetVolumeContentSource ().GetSnapshot () != nil {
553
+ snapshotID = req .GetVolumeContentSource ().GetSnapshot ().GetSnapshotId ()
554
+ }
555
+ log .Infof ("Volume %s is created from snapshot %s, get the datastore accessible topology from the snapshot" ,
556
+ req .Name , snapshotID )
557
+ datastoreAccessibleTopology , err := c .getDatastoreAccessibleTopologyForSnapshot (ctx ,
558
+ req .GetVolumeContentSource ().GetSnapshot ().GetSnapshotId (), storageTopologyType ,
559
+ topologyRequirement , topoSegToDatastoresMap )
560
+ if err != nil {
561
+ return nil , csifault .CSIInternalFault , logger .LogNewErrorCodef (log , codes .Internal ,
562
+ "failed to get datastore accessible topology. Error: %v" , err )
563
+ }
564
+ topologyRequirement = & csi.TopologyRequirement {
565
+ Preferred : datastoreAccessibleTopology ,
566
+ Requisite : datastoreAccessibleTopology ,
567
+ }
568
+ log .Infof ("Replaced with topologyRequirement %+v for creating volume %s from snapshot %s" ,
569
+ topologyRequirement , req .Name , snapshotID )
570
+ }
571
+
548
572
sharedDatastores , err = c .topologyMgr .GetSharedDatastoresInTopology (ctx ,
549
573
commoncotypes.WCPTopologyFetchDSParams {
550
574
TopologyRequirement : topologyRequirement ,
@@ -953,6 +977,80 @@ func (c *controller) createBlockVolume(ctx context.Context, req *csi.CreateVolum
953
977
return resp , "" , nil
954
978
}
955
979
980
+ func (c * controller ) getDatastoreAccessibleTopologyForSnapshot (ctx context.Context , contentSourceSnapshotID string ,
981
+ storageTopologyType string , topologyRequirement * csi.TopologyRequirement ,
982
+ topoSegToDatastoresMap map [string ][]* cnsvsphere.DatastoreInfo ) ([]* csi.Topology , error ) {
983
+ log := logger .GetLogger (ctx )
984
+
985
+ log .Debugf ("getDatastoreAccessibleTopologyForSnapshot: contentSourceSnapshotID: %s, storageTopologyType: %s, " +
986
+ "topologyRequirement %+v, topoSegToDatastoresMap %+v" ,
987
+ contentSourceSnapshotID , storageTopologyType , topologyRequirement , topoSegToDatastoresMap )
988
+
989
+ vc , err := c .manager .VcenterManager .GetVirtualCenter (ctx , c .manager .VcenterConfig .Host )
990
+ if err != nil {
991
+ return nil , logger .LogNewErrorCodef (log , codes .Internal ,
992
+ "failed to get vCenter. Error: %+v" , err )
993
+ }
994
+
995
+ if c .topologyMgr == nil {
996
+ return nil , logger .LogNewErrorCode (log , codes .Internal ,
997
+ "topology manager not initialized." )
998
+ }
999
+
1000
+ // Query the datastore of snapshot. By design, snapshot is always located at the same datastore
1001
+ // as the source volume
1002
+ querySelection := cnstypes.CnsQuerySelection {
1003
+ Names : []string {string (cnstypes .QuerySelectionNameTypeDataStoreUrl )},
1004
+ }
1005
+
1006
+ // Parse contentSourceSnapshotID into CNS VolumeID and CNS SnapshotID using "+" as the delimiter
1007
+ cnsVolumeID , _ , err := common .ParseCSISnapshotID (contentSourceSnapshotID )
1008
+ if err != nil {
1009
+ return nil , logger .LogNewErrorCodef (log , codes .Internal ,
1010
+ "failed to parse snapshot id. Error: %+v" , err )
1011
+ }
1012
+ cnsVolumeInfo , err := common .QueryVolumeByID (ctx , c .manager .VolumeManager , cnsVolumeID , & querySelection )
1013
+ if err != nil {
1014
+ return nil , logger .LogNewErrorf (log ,
1015
+ "failed to query datastore for the volume %s with error %+v" ,
1016
+ cnsVolumeID , err )
1017
+ }
1018
+
1019
+ selectedDatastore := cnsVolumeInfo .DatastoreUrl
1020
+ log .Debugf ("getDatastoreAccessibleTopologyForSnapshot: selectedDatastore: %s" , selectedDatastore )
1021
+
1022
+ if selectedDatastore == "" {
1023
+ return nil , logger .LogNewErrorCodef (log , codes .Internal ,
1024
+ "failed to get datastore for volume %q. Error: %+v" ,
1025
+ cnsVolumeID , err )
1026
+ }
1027
+
1028
+ // Calculate accessible topology for the provisioned volume.
1029
+ datastoreAccessibleTopologySegments , err := c .topologyMgr .GetTopologyInfoFromNodes (ctx ,
1030
+ commoncotypes.WCPRetrieveTopologyInfoParams {
1031
+ DatastoreURL : selectedDatastore ,
1032
+ StorageTopologyType : storageTopologyType ,
1033
+ TopologyRequirement : topologyRequirement ,
1034
+ Vc : vc ,
1035
+ TopoSegToDatastoresMap : topoSegToDatastoresMap })
1036
+ if err != nil {
1037
+ return nil , logger .LogNewErrorCodef (log , codes .Internal ,
1038
+ "failed to find accessible topologies for volume %q. Error: %+v" ,
1039
+ cnsVolumeID , err )
1040
+ }
1041
+
1042
+ // Convert []map[string]string to []*csi.Topology
1043
+ var datastoreAccessibleTopology []* csi.Topology
1044
+ for _ , topoSegments := range datastoreAccessibleTopologySegments {
1045
+ volumeTopology := & csi.Topology {
1046
+ Segments : topoSegments ,
1047
+ }
1048
+ datastoreAccessibleTopology = append (datastoreAccessibleTopology , volumeTopology )
1049
+ }
1050
+ log .Infof ("getDatastoreAccessibleTopologyForSnapshot: returning topology %+v" , datastoreAccessibleTopology )
1051
+ return datastoreAccessibleTopology , nil
1052
+ }
1053
+
956
1054
// createFileVolume creates a file volume based on the CreateVolumeRequest.
957
1055
func (c * controller ) createFileVolume (ctx context.Context , req * csi.CreateVolumeRequest ,
958
1056
isWorkloadDomainIsolationEnabled bool ) (
0 commit comments