Use any host that mounts the datastore to create Volume

Also, This change makes zone to work per datacenter and cleans up dummy vms.
There can be multiple datastores found for a given name. The datastore name is
unique only within a datacenter. So this commit returns a list of datastores
for a given datastore name in FindDatastoreByName() method. The calles are
responsible to handle or find the right datastore to use among those returned.
This commit is contained in:
Subramanian Neelakantan 2019-03-08 15:23:08 +05:30
parent 0230664642
commit 18922a3c44
12 changed files with 272 additions and 174 deletions

View File

@ -33,6 +33,7 @@ go_library(
"//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library", "//staging/src/k8s.io/cloud-provider/volume/helpers:go_default_library",
"//staging/src/k8s.io/legacy-cloud-providers/vsphere/vclib:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/vsphere/vclib:go_default_library",
"//staging/src/k8s.io/legacy-cloud-providers/vsphere/vclib/diskmanagers:go_default_library", "//staging/src/k8s.io/legacy-cloud-providers/vsphere/vclib/diskmanagers:go_default_library",
"//vendor/github.com/vmware/govmomi/find:go_default_library",
"//vendor/github.com/vmware/govmomi/object:go_default_library", "//vendor/github.com/vmware/govmomi/object:go_default_library",
"//vendor/github.com/vmware/govmomi/property:go_default_library", "//vendor/github.com/vmware/govmomi/property:go_default_library",
"//vendor/github.com/vmware/govmomi/vapi/rest:go_default_library", "//vendor/github.com/vmware/govmomi/vapi/rest:go_default_library",

View File

@ -39,6 +39,11 @@ type NodeInfo struct {
zone *cloudprovider.Zone zone *cloudprovider.Zone
} }
func (n NodeInfo) String() string {
return fmt.Sprintf("{datacenter: %v, vm: %v, vcServer: %s, vmUUID: %s, zone: %v}",
*n.dataCenter, n.vm.Reference(), n.vcServer, n.vmUUID, *n.zone)
}
type NodeManager struct { type NodeManager struct {
// TODO: replace map with concurrent map when k8s supports go v1.9 // TODO: replace map with concurrent map when k8s supports go v1.9

View File

@ -52,6 +52,7 @@ const (
DatacenterType = "Datacenter" DatacenterType = "Datacenter"
ClusterComputeResourceType = "ClusterComputeResource" ClusterComputeResourceType = "ClusterComputeResource"
HostSystemType = "HostSystem" HostSystemType = "HostSystem"
NameProperty = "name"
) )
// Test Constants // Test Constants

View File

@ -187,6 +187,28 @@ func (dc *Datacenter) GetDatastoreByName(ctx context.Context, name string) (*Dat
return &datastore, nil return &datastore, nil
} }
// GetDatastoreInfoByName gets the Datastore object for the given datastore name
func (dc *Datacenter) GetDatastoreInfoByName(ctx context.Context, name string) (*DatastoreInfo, error) {
finder := getFinder(dc)
ds, err := finder.Datastore(ctx, name)
if err != nil {
klog.Errorf("Failed while searching for datastore: %s. err: %+v", name, err)
return nil, err
}
datastore := Datastore{ds, dc}
var dsMo mo.Datastore
pc := property.DefaultCollector(dc.Client())
properties := []string{DatastoreInfoProperty}
err = pc.RetrieveOne(ctx, ds.Reference(), properties, &dsMo)
if err != nil {
klog.Errorf("Failed to get Datastore managed objects from datastore reference."+
" dsRef: %+v, err: %+v", ds.Reference(), err)
return nil, err
}
klog.V(9).Infof("Result dsMo: %+v", dsMo)
return &DatastoreInfo{Datastore: &datastore, Info: dsMo.Info.GetDatastoreInfo()}, nil
}
// GetResourcePool gets the resource pool for the given path // GetResourcePool gets the resource pool for the given path
func (dc *Datacenter) GetResourcePool(ctx context.Context, resourcePoolPath string) (*object.ResourcePool, error) { func (dc *Datacenter) GetResourcePool(ctx context.Context, resourcePoolPath string) (*object.ResourcePool, error) {
finder := getFinder(dc) finder := getFinder(dc)

View File

@ -96,8 +96,14 @@ func (ds *Datastore) GetDatastoreHostMounts(ctx context.Context) ([]types.Manage
return nil, err return nil, err
} }
hosts := make([]types.ManagedObjectReference, len(dsMo.Host)) hosts := make([]types.ManagedObjectReference, len(dsMo.Host))
for _, dsHostMount := range dsMo.Host { for i, dsHostMount := range dsMo.Host {
hosts = append(hosts, dsHostMount.Key) hosts[i] = dsHostMount.Key
} }
return hosts, nil return hosts, nil
} }
// Exists returns whether the given file exists in this datastore
func (ds *Datastore) Exists(ctx context.Context, file string) bool {
_, err := ds.Datastore.Stat(ctx, file)
return err == nil
}

View File

@ -212,7 +212,7 @@ func (vmdisk vmDiskManager) createDummyVM(ctx context.Context, datacenter *vclib
} }
// CleanUpDummyVMs deletes stale dummyVM's // CleanUpDummyVMs deletes stale dummyVM's
func CleanUpDummyVMs(ctx context.Context, folder *vclib.Folder, dc *vclib.Datacenter) error { func CleanUpDummyVMs(ctx context.Context, folder *vclib.Folder) error {
vmList, err := folder.GetVirtualMachines(ctx) vmList, err := folder.GetVirtualMachines(ctx)
if err != nil { if err != nil {
klog.V(4).Infof("Failed to get virtual machines in the kubernetes cluster: %s, err: %+v", folder.InventoryPath, err) klog.V(4).Infof("Failed to get virtual machines in the kubernetes cluster: %s, err: %+v", folder.InventoryPath, err)
@ -231,7 +231,7 @@ func CleanUpDummyVMs(ctx context.Context, folder *vclib.Folder, dc *vclib.Datace
continue continue
} }
if strings.HasPrefix(vmName, vclib.DummyVMPrefixName) { if strings.HasPrefix(vmName, vclib.DummyVMPrefixName) {
vmObj := vclib.VirtualMachine{VirtualMachine: object.NewVirtualMachine(dc.Client(), vm.Reference()), Datacenter: dc} vmObj := vclib.VirtualMachine{VirtualMachine: object.NewVirtualMachine(folder.Client(), vm.Reference())}
dummyVMList = append(dummyVMList, &vmObj) dummyVMList = append(dummyVMList, &vmObj)
} }
} }

View File

@ -85,7 +85,7 @@ func (pbmClient *PbmClient) IsDatastoreCompatible(ctx context.Context, storagePo
// GetCompatibleDatastores filters and returns compatible list of datastores for given storage policy id // GetCompatibleDatastores filters and returns compatible list of datastores for given storage policy id
// For Non Compatible Datastores, fault message with the Datastore Name is also returned // For Non Compatible Datastores, fault message with the Datastore Name is also returned
func (pbmClient *PbmClient) GetCompatibleDatastores(ctx context.Context, dc *Datacenter, storagePolicyID string, datastores []*DatastoreInfo) ([]*DatastoreInfo, string, error) { func (pbmClient *PbmClient) GetCompatibleDatastores(ctx context.Context, storagePolicyID string, datastores []*DatastoreInfo) ([]*DatastoreInfo, string, error) {
var ( var (
dsMorNameMap = getDsMorNameMap(ctx, datastores) dsMorNameMap = getDsMorNameMap(ctx, datastores)
localizedMessagesForNotCompatibleDatastores = "" localizedMessagesForNotCompatibleDatastores = ""

View File

@ -233,7 +233,7 @@ func (vm *VirtualMachine) GetAllAccessibleDatastores(ctx context.Context) ([]*Da
var dsMoList []mo.Datastore var dsMoList []mo.Datastore
pc := property.DefaultCollector(vm.Client()) pc := property.DefaultCollector(vm.Client())
properties := []string{DatastoreInfoProperty} properties := []string{DatastoreInfoProperty, NameProperty}
err = pc.Retrieve(ctx, dsRefList, properties, &dsMoList) err = pc.Retrieve(ctx, dsRefList, properties, &dsMoList)
if err != nil { if err != nil {
klog.Errorf("Failed to get Datastore managed objects from datastore objects."+ klog.Errorf("Failed to get Datastore managed objects from datastore objects."+
@ -241,12 +241,19 @@ func (vm *VirtualMachine) GetAllAccessibleDatastores(ctx context.Context) ([]*Da
return nil, err return nil, err
} }
klog.V(9).Infof("Result dsMoList: %+v", dsMoList) klog.V(9).Infof("Result dsMoList: %+v", dsMoList)
finder := getFinder(vm.Datacenter)
var dsObjList []*DatastoreInfo var dsObjList []*DatastoreInfo
for _, dsMo := range dsMoList { for _, dsMo := range dsMoList {
// use the finder so that InventoryPath is set correctly in ds
ds, err := finder.Datastore(ctx, dsMo.Name)
if err != nil {
klog.Errorf("Failed finding datastore: %s. err: %+v", dsMo.Name, err)
return nil, err
}
datastore := Datastore{ds, vm.Datacenter}
dsObjList = append(dsObjList, dsObjList = append(dsObjList,
&DatastoreInfo{ &DatastoreInfo{
&Datastore{object.NewDatastore(vm.Client(), dsMo.Reference()), &datastore,
vm.Datacenter},
dsMo.Info.GetDatastoreInfo()}) dsMo.Info.GetDatastoreInfo()})
} }
return dsObjList, nil return dsObjList, nil

View File

@ -1165,15 +1165,9 @@ func (vs *VSphere) DisksAreAttached(nodeVolumes map[k8stypes.NodeName][]string)
func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVolumePath string, err error) { func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVolumePath string, err error) {
klog.V(1).Infof("Starting to create a vSphere volume with volumeOptions: %+v", volumeOptions) klog.V(1).Infof("Starting to create a vSphere volume with volumeOptions: %+v", volumeOptions)
createVolumeInternal := func(volumeOptions *vclib.VolumeOptions) (canonicalVolumePath string, err error) { createVolumeInternal := func(volumeOptions *vclib.VolumeOptions) (canonicalVolumePath string, err error) {
var datastore string var datastoreInfo *vclib.DatastoreInfo
var dsList []*vclib.DatastoreInfo var dsList []*vclib.DatastoreInfo
// If datastore not specified, then use default datastore
if volumeOptions.Datastore == "" {
datastore = vs.cfg.Workspace.DefaultDatastore
} else {
datastore = volumeOptions.Datastore
}
datastore = strings.TrimSpace(datastore)
// Create context // Create context
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -1181,16 +1175,29 @@ func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVo
if err != nil { if err != nil {
return "", err return "", err
} }
dc, err := vclib.GetDatacenter(ctx, vsi.conn, vs.cfg.Workspace.Datacenter) // If datastore not specified, then use default datastore
datastoreName := strings.TrimSpace(volumeOptions.Datastore)
if datastoreName == "" {
datastoreName = strings.TrimSpace(vs.cfg.Workspace.DefaultDatastore)
}
// The given datastoreName may be present in more than one datacenter
candidateDatastoreInfos, err := vs.FindDatastoreByName(ctx, datastoreName)
if err != nil { if err != nil {
return "", err return "", err
} }
// Each of the datastores found is a candidate for Volume creation.
// One of these will be selected based on given policy and/or zone.
candidateDatastores := make(map[string]*vclib.DatastoreInfo)
for _, dsInfo := range candidateDatastoreInfos {
candidateDatastores[dsInfo.Info.Url] = dsInfo
}
var vmOptions *vclib.VMOptions var vmOptions *vclib.VMOptions
if volumeOptions.VSANStorageProfileData != "" || volumeOptions.StoragePolicyName != "" { if volumeOptions.VSANStorageProfileData != "" || volumeOptions.StoragePolicyName != "" {
// If datastore and zone are specified, first validate if the datastore is in the provided zone. // If datastore and zone are specified, first validate if the datastore is in the provided zone.
if len(volumeOptions.Zone) != 0 && volumeOptions.Datastore != "" { if len(volumeOptions.Zone) != 0 && volumeOptions.Datastore != "" {
klog.V(4).Infof("Specified zone : %s, datastore : %s", volumeOptions.Zone, volumeOptions.Datastore) klog.V(4).Infof("Specified zone : %s, datastore : %s", volumeOptions.Zone, volumeOptions.Datastore)
dsList, err = getDatastoresForZone(ctx, dc, vs.nodeManager, volumeOptions.Zone) dsList, err = getDatastoresForZone(ctx, vs.nodeManager, volumeOptions.Zone)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -1198,8 +1205,7 @@ func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVo
// Validate if the datastore provided belongs to the zone. If not, fail the operation. // Validate if the datastore provided belongs to the zone. If not, fail the operation.
found := false found := false
for _, ds := range dsList { for _, ds := range dsList {
if ds.Info.Name == volumeOptions.Datastore { if datastoreInfo, found = candidateDatastores[ds.Info.Url]; found {
found = true
break break
} }
} }
@ -1221,19 +1227,14 @@ func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVo
cleanUpRoutineInitialized = true cleanUpRoutineInitialized = true
} }
cleanUpRoutineInitLock.Unlock() cleanUpRoutineInitLock.Unlock()
vmOptions, err = vs.setVMOptions(ctx, dc, vs.cfg.Workspace.ResourcePoolPath)
if err != nil {
klog.Errorf("Failed to set VM options requires to create a vsphere volume. err: %+v", err)
return "", err
}
} }
if volumeOptions.StoragePolicyName != "" && volumeOptions.Datastore == "" { if volumeOptions.StoragePolicyName != "" && volumeOptions.Datastore == "" {
if len(volumeOptions.Zone) == 0 { if len(volumeOptions.Zone) == 0 {
klog.V(4).Infof("Selecting a shared datastore as per the storage policy %s", volumeOptions.StoragePolicyName) klog.V(4).Infof("Selecting a shared datastore as per the storage policy %s", volumeOptions.StoragePolicyName)
datastore, err = getPbmCompatibleDatastore(ctx, dc, volumeOptions.StoragePolicyName, vs.nodeManager) datastoreInfo, err = getPbmCompatibleDatastore(ctx, vsi.conn.Client, volumeOptions.StoragePolicyName, vs.nodeManager)
} else { } else {
// If zone is specified, first get the datastores in the zone. // If zone is specified, first get the datastores in the zone.
dsList, err = getDatastoresForZone(ctx, dc, vs.nodeManager, volumeOptions.Zone) dsList, err = getDatastoresForZone(ctx, vs.nodeManager, volumeOptions.Zone)
if err != nil { if err != nil {
klog.Errorf("Failed to find a shared datastore matching zone %s. err: %+v", volumeOptions.Zone, err) klog.Errorf("Failed to find a shared datastore matching zone %s. err: %+v", volumeOptions.Zone, err)
@ -1249,18 +1250,18 @@ func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVo
klog.V(4).Infof("Specified zone : %s. Picking a datastore as per the storage policy %s among the zoned datastores : %s", volumeOptions.Zone, klog.V(4).Infof("Specified zone : %s. Picking a datastore as per the storage policy %s among the zoned datastores : %s", volumeOptions.Zone,
volumeOptions.StoragePolicyName, dsList) volumeOptions.StoragePolicyName, dsList)
// Among the compatible datastores, select the one based on the maximum free space. // Among the compatible datastores, select the one based on the maximum free space.
datastore, err = getPbmCompatibleZonedDatastore(ctx, dc, volumeOptions.StoragePolicyName, dsList) datastoreInfo, err = getPbmCompatibleZonedDatastore(ctx, vsi.conn.Client, volumeOptions.StoragePolicyName, dsList)
} }
klog.V(1).Infof("Datastore selected as per policy : %s", datastore)
if err != nil { if err != nil {
klog.Errorf("Failed to get pbm compatible datastore with storagePolicy: %s. err: %+v", volumeOptions.StoragePolicyName, err) klog.Errorf("Failed to get pbm compatible datastore with storagePolicy: %s. err: %+v", volumeOptions.StoragePolicyName, err)
return "", err return "", err
} }
klog.V(1).Infof("Datastore selected as per policy : %s", datastoreInfo.Info.Name)
} else { } else {
// If zone is specified, pick the datastore in the zone with maximum free space within the zone. // If zone is specified, pick the datastore in the zone with maximum free space within the zone.
if volumeOptions.Datastore == "" && len(volumeOptions.Zone) != 0 { if volumeOptions.Datastore == "" && len(volumeOptions.Zone) != 0 {
klog.V(4).Infof("Specified zone : %s", volumeOptions.Zone) klog.V(4).Infof("Specified zone : %s", volumeOptions.Zone)
dsList, err = getDatastoresForZone(ctx, dc, vs.nodeManager, volumeOptions.Zone) dsList, err = getDatastoresForZone(ctx, vs.nodeManager, volumeOptions.Zone)
if err != nil { if err != nil {
klog.Errorf("Failed to find a shared datastore matching zone %s. err: %+v", volumeOptions.Zone, err) klog.Errorf("Failed to find a shared datastore matching zone %s. err: %+v", volumeOptions.Zone, err)
@ -1273,40 +1274,40 @@ func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVo
return "", err return "", err
} }
datastore, err = getMostFreeDatastoreName(ctx, nil, dsList) datastoreInfo, err = getMostFreeDatastore(ctx, nil, dsList)
if err != nil { if err != nil {
klog.Errorf("Failed to get shared datastore: %+v", err) klog.Errorf("Failed to get shared datastore: %+v", err)
return "", err return "", err
} }
klog.V(1).Infof("Specified zone : %s. Selected datastore : %s", volumeOptions.StoragePolicyName, datastore) klog.V(1).Infof("Specified zone : %s. Selected datastore : %s", volumeOptions.Zone, datastoreInfo.Info.Name)
} else { } else {
var sharedDsList []*vclib.DatastoreInfo var sharedDsList []*vclib.DatastoreInfo
var err error var err error
if len(volumeOptions.Zone) == 0 { if len(volumeOptions.Zone) == 0 {
// If zone is not provided, get the shared datastore across all node VMs. // If zone is not provided, get the shared datastore across all node VMs.
klog.V(4).Infof("Validating if datastore %s is shared across all node VMs", datastore) klog.V(4).Infof("Validating if datastore %s is shared across all node VMs", datastoreName)
sharedDsList, err = getSharedDatastoresInK8SCluster(ctx, dc, vs.nodeManager) sharedDsList, err = getSharedDatastoresInK8SCluster(ctx, vs.nodeManager)
if err != nil { if err != nil {
klog.Errorf("Failed to get shared datastore: %+v", err) klog.Errorf("Failed to get shared datastore: %+v", err)
return "", err return "", err
} }
// Prepare error msg to be used later, if required. // Prepare error msg to be used later, if required.
err = fmt.Errorf("The specified datastore %s is not a shared datastore across node VMs", datastore) err = fmt.Errorf("The specified datastore %s is not a shared datastore across node VMs", datastoreName)
} else { } else {
// If zone is provided, get the shared datastores in that zone. // If zone is provided, get the shared datastores in that zone.
klog.V(4).Infof("Validating if datastore %s is in zone %s ", datastore, volumeOptions.Zone) klog.V(4).Infof("Validating if datastore %s is in zone %s ", datastoreName, volumeOptions.Zone)
sharedDsList, err = getDatastoresForZone(ctx, dc, vs.nodeManager, volumeOptions.Zone) sharedDsList, err = getDatastoresForZone(ctx, vs.nodeManager, volumeOptions.Zone)
if err != nil { if err != nil {
klog.Errorf("Failed to find a shared datastore matching zone %s. err: %+v", volumeOptions.Zone, err) klog.Errorf("Failed to find a shared datastore matching zone %s. err: %+v", volumeOptions.Zone, err)
return "", err return "", err
} }
// Prepare error msg to be used later, if required. // Prepare error msg to be used later, if required.
err = fmt.Errorf("The specified datastore %s does not match the provided zones : %s", datastore, volumeOptions.Zone) err = fmt.Errorf("The specified datastore %s does not match the provided zones : %s", datastoreName, volumeOptions.Zone)
} }
found := false found := false
// Check if the selected datastore belongs to the list of shared datastores computed. // Check if the selected datastore belongs to the list of shared datastores computed.
for _, sharedDs := range sharedDsList { for _, sharedDs := range sharedDsList {
if datastore == sharedDs.Info.Name { if datastoreInfo, found = candidateDatastores[sharedDs.Info.Url]; found {
klog.V(4).Infof("Datastore validation succeeded") klog.V(4).Infof("Datastore validation succeeded")
found = true found = true
break break
@ -1318,11 +1319,19 @@ func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVo
} }
} }
} }
ds, err := dc.GetDatastoreByName(ctx, datastore)
// if datastoreInfo is still not determined, it is an error condition
if datastoreInfo == nil {
klog.Errorf("Ambigous datastore name %s, cannot be found among: %v", datastoreName, candidateDatastoreInfos)
return "", fmt.Errorf("Ambigous datastore name %s", datastoreName)
}
ds := datastoreInfo.Datastore
volumeOptions.Datastore = datastoreInfo.Info.Name
vmOptions, err = vs.setVMOptions(ctx, vsi.conn, ds)
if err != nil { if err != nil {
klog.Errorf("Failed to set VM options required to create a vsphere volume. err: %+v", err)
return "", err return "", err
} }
volumeOptions.Datastore = datastore
kubeVolsPath := filepath.Clean(ds.Path(VolDir)) + "/" kubeVolsPath := filepath.Clean(ds.Path(VolDir)) + "/"
err = ds.CreateDirectory(ctx, kubeVolsPath, false) err = ds.CreateDirectory(ctx, kubeVolsPath, false)
if err != nil && err != vclib.ErrFileAlreadyExist { if err != nil && err != vclib.ErrFileAlreadyExist {
@ -1337,18 +1346,18 @@ func (vs *VSphere) CreateVolume(volumeOptions *vclib.VolumeOptions) (canonicalVo
} }
volumePath, err = disk.Create(ctx, ds) volumePath, err = disk.Create(ctx, ds)
if err != nil { if err != nil {
klog.Errorf("Failed to create a vsphere volume with volumeOptions: %+v on datastore: %s. err: %+v", volumeOptions, datastore, err) klog.Errorf("Failed to create a vsphere volume with volumeOptions: %+v on datastore: %s. err: %+v", volumeOptions, ds, err)
return "", err return "", err
} }
// Get the canonical path for the volume path. // Get the canonical path for the volume path.
canonicalVolumePath, err = getcanonicalVolumePath(ctx, dc, volumePath) canonicalVolumePath, err = getcanonicalVolumePath(ctx, datastoreInfo.Datacenter, volumePath)
if err != nil { if err != nil {
klog.Errorf("Failed to get canonical vsphere volume path for volume: %s with volumeOptions: %+v on datastore: %s. err: %+v", volumePath, volumeOptions, datastore, err) klog.Errorf("Failed to get canonical vsphere volume path for volume: %s with volumeOptions: %+v on datastore: %s. err: %+v", volumePath, volumeOptions, ds, err)
return "", err return "", err
} }
if filepath.Base(datastore) != datastore { if filepath.Base(datastoreName) != datastoreName {
// If datastore is within cluster, add cluster path to the volumePath // If datastore is within cluster, add cluster path to the volumePath
canonicalVolumePath = strings.Replace(canonicalVolumePath, filepath.Base(datastore), datastore, 1) canonicalVolumePath = strings.Replace(canonicalVolumePath, filepath.Base(datastoreName), datastoreName, 1)
} }
return canonicalVolumePath, nil return canonicalVolumePath, nil
} }
@ -1577,12 +1586,29 @@ func (vs *VSphere) GetVolumeLabels(volumePath string) (map[string]string, error)
return nil, nil return nil, nil
} }
// Find the datastore on which this volume resides
datastorePathObj, err := vclib.GetDatastorePathObjFromVMDiskPath(volumePath) datastorePathObj, err := vclib.GetDatastorePathObjFromVMDiskPath(volumePath)
if err != nil { if err != nil {
klog.Errorf("Failed to get datastore for volume: %v: %+v", volumePath, err) klog.Errorf("Failed to get datastore for volume: %v: %+v", volumePath, err)
return nil, err return nil, err
} }
dsZones, err := vs.GetZonesForDatastore(ctx, datastorePathObj.Datastore) dsInfos, err := vs.FindDatastoreByName(ctx, datastorePathObj.Datastore)
if err != nil {
klog.Errorf("Failed to get datastore by name: %v: %+v", datastorePathObj.Datastore, err)
return nil, err
}
var datastore *vclib.Datastore
for _, dsInfo := range dsInfos {
if dsInfo.Datastore.Exists(ctx, datastorePathObj.Path) {
datastore = dsInfo.Datastore
}
}
if datastore == nil {
klog.Errorf("Could not find %s among %v", volumePath, dsInfos)
return nil, fmt.Errorf("Could not find the datastore for volume: %s", volumePath)
}
dsZones, err := vs.GetZonesForDatastore(ctx, datastore)
if err != nil { if err != nil {
klog.Errorf("Failed to get zones for datastore %v: %+v", datastorePathObj.Datastore, err) klog.Errorf("Failed to get zones for datastore %v: %+v", datastorePathObj.Datastore, err)
return nil, err return nil, err
@ -1620,25 +1646,16 @@ func (vs *VSphere) collapseZonesInRegion(ctx context.Context, zones []cloudprovi
} }
// GetZonesForDatastore returns all the zones from which this datastore is visible // GetZonesForDatastore returns all the zones from which this datastore is visible
func (vs *VSphere) GetZonesForDatastore(ctx context.Context, datastore string) ([]cloudprovider.Zone, error) { func (vs *VSphere) GetZonesForDatastore(ctx context.Context, datastore *vclib.Datastore) ([]cloudprovider.Zone, error) {
vsi, err := vs.getVSphereInstanceForServer(vs.cfg.Workspace.VCenterIP, ctx) vsi, err := vs.getVSphereInstanceForServer(vs.cfg.Workspace.VCenterIP, ctx)
if err != nil { if err != nil {
klog.Errorf("Failed to get vSphere instance: %+v", err) klog.Errorf("Failed to get vSphere instance: %+v", err)
return nil, err return nil, err
} }
dc, err := vclib.GetDatacenter(ctx, vsi.conn, vs.cfg.Workspace.Datacenter)
if err != nil {
klog.Errorf("Failed to get datacenter: %+v", err)
return nil, err
}
// get the hosts mounted on this datastore // get the hosts mounted on this datastore
// datastore -> ["host-1", "host-2", "host-3", ...] // datastore -> ["host-1", "host-2", "host-3", ...]
ds, err := dc.GetDatastoreByName(ctx, datastore) dsHosts, err := datastore.GetDatastoreHostMounts(ctx)
if err != nil {
klog.Errorf("Failed to get datastore by name: %v: %+v", datastore, err)
return nil, err
}
dsHosts, err := ds.GetDatastoreHostMounts(ctx)
if err != nil { if err != nil {
klog.Errorf("Failed to get datastore host mounts for %v: %+v", datastore, err) klog.Errorf("Failed to get datastore host mounts for %v: %+v", datastore, err)
return nil, err return nil, err

View File

@ -20,20 +20,22 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"math/rand"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object" "github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property" "github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap" "github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types" "github.com/vmware/govmomi/vim25/types"
v1 "k8s.io/api/core/v1"
"k8s.io/klog" "k8s.io/klog"
"k8s.io/api/core/v1"
k8stypes "k8s.io/apimachinery/pkg/types" k8stypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/version"
"k8s.io/legacy-cloud-providers/vsphere/vclib" "k8s.io/legacy-cloud-providers/vsphere/vclib"
@ -43,6 +45,7 @@ import (
const ( const (
DatastoreProperty = "datastore" DatastoreProperty = "datastore"
DatastoreInfoProperty = "info" DatastoreInfoProperty = "info"
DatastoreNameProperty = "name"
Folder = "Folder" Folder = "Folder"
VirtualMachine = "VirtualMachine" VirtualMachine = "VirtualMachine"
DummyDiskName = "kube-dummyDisk.vmdk" DummyDiskName = "kube-dummyDisk.vmdk"
@ -121,7 +124,7 @@ func getAccessibleDatastores(ctx context.Context, nodeVmDetail *NodeDetails, nod
} }
// Get all datastores accessible for the virtual machine object. // Get all datastores accessible for the virtual machine object.
func getSharedDatastoresInK8SCluster(ctx context.Context, dc *vclib.Datacenter, nodeManager *NodeManager) ([]*vclib.DatastoreInfo, error) { func getSharedDatastoresInK8SCluster(ctx context.Context, nodeManager *NodeManager) ([]*vclib.DatastoreInfo, error) {
nodeVmDetails, err := nodeManager.GetNodeDetails() nodeVmDetails, err := nodeManager.GetNodeDetails()
if err != nil { if err != nil {
klog.Errorf("Error while obtaining Kubernetes node nodeVmDetail details. error : %+v", err) klog.Errorf("Error while obtaining Kubernetes node nodeVmDetail details. error : %+v", err)
@ -155,12 +158,6 @@ func getSharedDatastoresInK8SCluster(ctx context.Context, dc *vclib.Datacenter,
} }
} }
klog.V(9).Infof("sharedDatastores : %+v", sharedDatastores) klog.V(9).Infof("sharedDatastores : %+v", sharedDatastores)
sharedDatastores, err = getDatastoresForEndpointVC(ctx, dc, sharedDatastores)
if err != nil {
klog.Errorf("Failed to get shared datastores from endpoint VC. err: %+v", err)
return nil, err
}
klog.V(9).Infof("sharedDatastores at endpoint VC: %+v", sharedDatastores)
return sharedDatastores, nil return sharedDatastores, nil
} }
@ -182,7 +179,7 @@ func intersect(list1 []*vclib.DatastoreInfo, list2 []*vclib.DatastoreInfo) []*vc
} }
// getMostFreeDatastore gets the best fit compatible datastore by free space. // getMostFreeDatastore gets the best fit compatible datastore by free space.
func getMostFreeDatastoreName(ctx context.Context, client *vim25.Client, dsInfoList []*vclib.DatastoreInfo) (string, error) { func getMostFreeDatastore(ctx context.Context, client *vim25.Client, dsInfoList []*vclib.DatastoreInfo) (*vclib.DatastoreInfo, error) {
var curMax int64 var curMax int64
curMax = -1 curMax = -1
var index int var index int
@ -193,65 +190,46 @@ func getMostFreeDatastoreName(ctx context.Context, client *vim25.Client, dsInfoL
index = i index = i
} }
} }
return dsInfoList[index].Info.GetDatastoreInfo().Name, nil return dsInfoList[index], nil
} }
// Returns the datastores in the given datacenter by performing lookup based on datastore URL. func getPbmCompatibleDatastore(ctx context.Context, vcClient *vim25.Client, storagePolicyName string, nodeManager *NodeManager) (*vclib.DatastoreInfo, error) {
func getDatastoresForEndpointVC(ctx context.Context, dc *vclib.Datacenter, sharedDsInfos []*vclib.DatastoreInfo) ([]*vclib.DatastoreInfo, error) { pbmClient, err := vclib.NewPbmClient(ctx, vcClient)
var datastores []*vclib.DatastoreInfo
allDsInfoMap, err := dc.GetAllDatastores(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, sharedDsInfo := range sharedDsInfos {
dsInfo, ok := allDsInfoMap[sharedDsInfo.Info.Url]
if ok {
datastores = append(datastores, dsInfo)
} else {
klog.V(4).Infof("Warning: Shared datastore with URL %s does not exist in endpoint VC", sharedDsInfo.Info.Url)
}
}
klog.V(9).Infof("Datastore from endpoint VC: %+v", datastores)
return datastores, nil
}
func getPbmCompatibleDatastore(ctx context.Context, dc *vclib.Datacenter, storagePolicyName string, nodeManager *NodeManager) (string, error) {
pbmClient, err := vclib.NewPbmClient(ctx, dc.Client())
if err != nil {
return "", err
}
storagePolicyID, err := pbmClient.ProfileIDByName(ctx, storagePolicyName) storagePolicyID, err := pbmClient.ProfileIDByName(ctx, storagePolicyName)
if err != nil { if err != nil {
klog.Errorf("Failed to get Profile ID by name: %s. err: %+v", storagePolicyName, err) klog.Errorf("Failed to get Profile ID by name: %s. err: %+v", storagePolicyName, err)
return "", err return nil, err
} }
sharedDs, err := getSharedDatastoresInK8SCluster(ctx, dc, nodeManager) sharedDs, err := getSharedDatastoresInK8SCluster(ctx, nodeManager)
if err != nil { if err != nil {
klog.Errorf("Failed to get shared datastores. err: %+v", err) klog.Errorf("Failed to get shared datastores. err: %+v", err)
return "", err return nil, err
} }
if len(sharedDs) == 0 { if len(sharedDs) == 0 {
msg := "No shared datastores found in the endpoint virtual center" msg := "No shared datastores found in the endpoint virtual center"
klog.Errorf(msg) klog.Errorf(msg)
return "", errors.New(msg) return nil, errors.New(msg)
} }
compatibleDatastores, _, err := pbmClient.GetCompatibleDatastores(ctx, dc, storagePolicyID, sharedDs) compatibleDatastores, _, err := pbmClient.GetCompatibleDatastores(ctx, storagePolicyID, sharedDs)
if err != nil { if err != nil {
klog.Errorf("Failed to get compatible datastores from datastores : %+v with storagePolicy: %s. err: %+v", klog.Errorf("Failed to get compatible datastores from datastores : %+v with storagePolicy: %s. err: %+v",
sharedDs, storagePolicyID, err) sharedDs, storagePolicyID, err)
return "", err return nil, err
} }
klog.V(9).Infof("compatibleDatastores : %+v", compatibleDatastores) klog.V(9).Infof("compatibleDatastores : %+v", compatibleDatastores)
datastore, err := getMostFreeDatastoreName(ctx, dc.Client(), compatibleDatastores) datastore, err := getMostFreeDatastore(ctx, vcClient, compatibleDatastores)
if err != nil { if err != nil {
klog.Errorf("Failed to get most free datastore from compatible datastores: %+v. err: %+v", compatibleDatastores, err) klog.Errorf("Failed to get most free datastore from compatible datastores: %+v. err: %+v", compatibleDatastores, err)
return "", err return nil, err
} }
klog.V(4).Infof("Most free datastore : %+s", datastore) klog.V(4).Infof("Most free datastore : %+s", datastore.Info.Name)
return datastore, err return datastore, err
} }
func getDatastoresForZone(ctx context.Context, dc *vclib.Datacenter, nodeManager *NodeManager, selectedZones []string) ([]*vclib.DatastoreInfo, error) { func getDatastoresForZone(ctx context.Context, nodeManager *NodeManager, selectedZones []string) ([]*vclib.DatastoreInfo, error) {
var sharedDatastores []*vclib.DatastoreInfo var sharedDatastores []*vclib.DatastoreInfo
@ -279,7 +257,7 @@ func getDatastoresForZone(ctx context.Context, dc *vclib.Datacenter, nodeManager
var dsMoList []mo.Datastore var dsMoList []mo.Datastore
pc := property.DefaultCollector(host.Client()) pc := property.DefaultCollector(host.Client())
properties := []string{DatastoreInfoProperty} properties := []string{DatastoreInfoProperty, DatastoreNameProperty}
err = pc.Retrieve(ctx, dsRefList, properties, &dsMoList) err = pc.Retrieve(ctx, dsRefList, properties, &dsMoList)
if err != nil { if err != nil {
klog.Errorf("Failed to get Datastore managed objects from datastore objects."+ klog.Errorf("Failed to get Datastore managed objects from datastore objects."+
@ -288,12 +266,37 @@ func getDatastoresForZone(ctx context.Context, dc *vclib.Datacenter, nodeManager
} }
klog.V(9).Infof("Datastore mo details: %+v", dsMoList) klog.V(9).Infof("Datastore mo details: %+v", dsMoList)
// find the Datacenter parent for this host
mes, err := mo.Ancestors(ctx, host.Client(), pc.Reference(), host.Reference())
if err != nil {
return nil, err
}
var dcMoref *types.ManagedObjectReference
for i := len(mes) - 1; i > 0; i-- {
if mes[i].Self.Type == "Datacenter" {
dcMoref = &mes[i].Self
break
}
}
if dcMoref == nil {
return nil, fmt.Errorf("Failed to find the Datacenter of host %s", host)
}
dc := object.NewDatacenter(host.Client(), *dcMoref)
finder := find.NewFinder(host.Client(), false)
finder.SetDatacenter(dc)
var dsObjList []*vclib.DatastoreInfo var dsObjList []*vclib.DatastoreInfo
for _, dsMo := range dsMoList { for _, dsMo := range dsMoList {
// use the finder so that InventoryPath is set correctly in dsObj
dsObj, err := finder.Datastore(ctx, dsMo.Name)
if err != nil {
klog.Errorf("Failed to find datastore named %s in datacenter %s", dsMo.Name, dc)
return nil, err
}
dsObjList = append(dsObjList, dsObjList = append(dsObjList,
&vclib.DatastoreInfo{ &vclib.DatastoreInfo{
Datastore: &vclib.Datastore{Datastore: object.NewDatastore(host.Client(), dsMo.Reference()), Datastore: &vclib.Datastore{Datastore: dsObj,
Datacenter: nil}, Datacenter: &vclib.Datacenter{Datacenter: dc}},
Info: dsMo.Info.GetDatastoreInfo()}) Info: dsMo.Info.GetDatastoreInfo()})
} }
@ -317,40 +320,48 @@ func getDatastoresForZone(ctx context.Context, dc *vclib.Datacenter, nodeManager
return sharedDatastores, nil return sharedDatastores, nil
} }
func getPbmCompatibleZonedDatastore(ctx context.Context, dc *vclib.Datacenter, storagePolicyName string, zonedDatastores []*vclib.DatastoreInfo) (string, error) { func getPbmCompatibleZonedDatastore(ctx context.Context, vcClient *vim25.Client, storagePolicyName string, zonedDatastores []*vclib.DatastoreInfo) (*vclib.DatastoreInfo, error) {
pbmClient, err := vclib.NewPbmClient(ctx, dc.Client()) pbmClient, err := vclib.NewPbmClient(ctx, vcClient)
if err != nil { if err != nil {
return "", err return nil, err
} }
storagePolicyID, err := pbmClient.ProfileIDByName(ctx, storagePolicyName) storagePolicyID, err := pbmClient.ProfileIDByName(ctx, storagePolicyName)
if err != nil { if err != nil {
klog.Errorf("Failed to get Profile ID by name: %s. err: %+v", storagePolicyName, err) klog.Errorf("Failed to get Profile ID by name: %s. err: %+v", storagePolicyName, err)
return "", err return nil, err
} }
compatibleDatastores, _, err := pbmClient.GetCompatibleDatastores(ctx, dc, storagePolicyID, zonedDatastores) compatibleDatastores, _, err := pbmClient.GetCompatibleDatastores(ctx, storagePolicyID, zonedDatastores)
if err != nil { if err != nil {
klog.Errorf("Failed to get compatible datastores from datastores : %+v with storagePolicy: %s. err: %+v", klog.Errorf("Failed to get compatible datastores from datastores : %+v with storagePolicy: %s. err: %+v",
zonedDatastores, storagePolicyID, err) zonedDatastores, storagePolicyID, err)
return "", err return nil, err
} }
klog.V(9).Infof("compatibleDatastores : %+v", compatibleDatastores) klog.V(9).Infof("compatibleDatastores : %+v", compatibleDatastores)
datastore, err := getMostFreeDatastoreName(ctx, dc.Client(), compatibleDatastores) datastore, err := getMostFreeDatastore(ctx, vcClient, compatibleDatastores)
if err != nil { if err != nil {
klog.Errorf("Failed to get most free datastore from compatible datastores: %+v. err: %+v", compatibleDatastores, err) klog.Errorf("Failed to get most free datastore from compatible datastores: %+v. err: %+v", compatibleDatastores, err)
return "", err return nil, err
} }
klog.V(4).Infof("Most free datastore : %+s", datastore) klog.V(4).Infof("Most free datastore : %+s", datastore.Info.Name)
return datastore, err return datastore, err
} }
func (vs *VSphere) setVMOptions(ctx context.Context, dc *vclib.Datacenter, resourcePoolPath string) (*vclib.VMOptions, error) { func (vs *VSphere) setVMOptions(ctx context.Context, connection *vclib.VSphereConnection, ds *vclib.Datastore) (*vclib.VMOptions, error) {
var vmOptions vclib.VMOptions var vmOptions vclib.VMOptions
resourcePool, err := dc.GetResourcePool(ctx, resourcePoolPath) dsHosts, err := ds.GetDatastoreHostMounts(ctx)
if err != nil { if err != nil {
klog.Errorf("Failed to get datastore host mounts for %v: %+v", ds, err)
return nil, err return nil, err
} }
klog.V(9).Infof("Resource pool path %s, resourcePool %+v", resourcePoolPath, resourcePool) // pick a host at random to use for Volume creation
folder, err := dc.GetFolderByPath(ctx, vs.cfg.Workspace.Folder) dsHostMoref := dsHosts[rand.New(rand.NewSource(time.Now().UnixNano())).Intn(len(dsHosts))]
dummyVMHost := object.NewHostSystem(connection.Client, dsHostMoref)
resourcePool, err := dummyVMHost.ResourcePool(ctx)
if err != nil {
klog.Errorf("Failed to get resource pool from host %v", dummyVMHost)
return nil, err
}
folder, err := ds.Datacenter.GetFolderByPath(ctx, vs.cfg.Workspace.Folder)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -366,16 +377,13 @@ func (vs *VSphere) cleanUpDummyVMs(dummyVMPrefix string) {
defer cancel() defer cancel()
for { for {
time.Sleep(CleanUpDummyVMRoutineInterval * time.Minute) time.Sleep(CleanUpDummyVMRoutineInterval * time.Minute)
vsi, err := vs.getVSphereInstanceForServer(vs.cfg.Workspace.VCenterIP, ctx) datacenters, err := vs.GetWorkspaceDatacenters(ctx)
if err != nil { if err != nil {
klog.V(4).Infof("Failed to get VSphere instance with err: %+v. Retrying again...", err) klog.V(4).Infof("Failed to get datacenters from VC. err: %+v", err)
continue
}
dc, err := vclib.GetDatacenter(ctx, vsi.conn, vs.cfg.Workspace.Datacenter)
if err != nil {
klog.V(4).Infof("Failed to get the datacenter: %s from VC. err: %+v", vs.cfg.Workspace.Datacenter, err)
continue continue
} }
// Clean up dummy VMs in each datacenter
for _, dc := range datacenters {
// Get the folder reference for global working directory where the dummy VM needs to be created. // Get the folder reference for global working directory where the dummy VM needs to be created.
vmFolder, err := dc.GetFolderByPath(ctx, vs.cfg.Workspace.Folder) vmFolder, err := dc.GetFolderByPath(ctx, vs.cfg.Workspace.Folder)
if err != nil { if err != nil {
@ -386,13 +394,14 @@ func (vs *VSphere) cleanUpDummyVMs(dummyVMPrefix string) {
cleanUpDummyVMs := func() { cleanUpDummyVMs := func() {
cleanUpDummyVMLock.Lock() cleanUpDummyVMLock.Lock()
defer cleanUpDummyVMLock.Unlock() defer cleanUpDummyVMLock.Unlock()
err = diskmanagers.CleanUpDummyVMs(ctx, vmFolder, dc) err = diskmanagers.CleanUpDummyVMs(ctx, vmFolder)
if err != nil { if err != nil {
klog.V(4).Infof("Unable to clean up dummy VM's in the kubernetes cluster: %q. err: %+v", vs.cfg.Workspace.Folder, err) klog.V(4).Infof("Unable to clean up dummy VM's in the kubernetes cluster: %q. err: %+v", vs.cfg.Workspace.Folder, err)
} }
} }
cleanUpDummyVMs() cleanUpDummyVMs()
} }
}
} }
// Get canonical volume path for volume Path. // Get canonical volume path for volume Path.
@ -586,43 +595,6 @@ func (vs *VSphere) checkDiskAttached(ctx context.Context, nodes []k8stypes.NodeN
return nodesToRetry, nil return nodesToRetry, nil
} }
func (vs *VSphere) IsDummyVMPresent(vmName string) (bool, error) {
isDummyVMPresent := false
// Create context
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
vsi, err := vs.getVSphereInstanceForServer(vs.cfg.Workspace.VCenterIP, ctx)
if err != nil {
return isDummyVMPresent, err
}
dc, err := vclib.GetDatacenter(ctx, vsi.conn, vs.cfg.Workspace.Datacenter)
if err != nil {
return isDummyVMPresent, err
}
vmFolder, err := dc.GetFolderByPath(ctx, vs.cfg.Workspace.Folder)
if err != nil {
return isDummyVMPresent, err
}
vms, err := vmFolder.GetVirtualMachines(ctx)
if err != nil {
return isDummyVMPresent, err
}
for _, vm := range vms {
if vm.Name() == vmName {
isDummyVMPresent = true
break
}
}
return isDummyVMPresent, nil
}
func (vs *VSphere) GetNodeNameFromProviderID(providerID string) (string, error) { func (vs *VSphere) GetNodeNameFromProviderID(providerID string) (string, error) {
var nodeName string var nodeName string
nodes, err := vs.nodeManager.GetNodeDetails() nodes, err := vs.nodeManager.GetNodeDetails()
@ -698,3 +670,63 @@ func GetVMUUID() (string, error) {
uuid = fmt.Sprintf("%s-%s-%s-%s-%s", uuid[0:8], uuid[8:12], uuid[12:16], uuid[16:20], uuid[20:32]) uuid = fmt.Sprintf("%s-%s-%s-%s-%s", uuid[0:8], uuid[8:12], uuid[12:16], uuid[16:20], uuid[20:32])
return uuid, nil return uuid, nil
} }
// GetWorkspaceDatacenters returns the Datacenter objects that VCP has access to.
// User can configure the list of datacenters in vsphere.conf. Otherwise all the
// Datacenters in the configured list of VCs are returned.
func (vs *VSphere) GetWorkspaceDatacenters(ctx context.Context) ([]*vclib.Datacenter, error) {
var datacenterObjs []*vclib.Datacenter
for vc, vsi := range vs.vsphereInstanceMap {
// ensure connection to VC
err := vs.nodeManager.vcConnect(ctx, vsi)
if err != nil {
return nil, err
}
if vsi.cfg.Datacenters == "" {
vcDatacenterObjs, err := vclib.GetAllDatacenter(ctx, vsi.conn)
if err != nil {
klog.Errorf("Error fetching list of datacenters from VC %s: %+v", vc, err)
return nil, err
}
datacenterObjs = append(datacenterObjs, vcDatacenterObjs...)
} else {
datacenters := strings.Split(vsi.cfg.Datacenters, ",")
for _, dc := range datacenters {
dc = strings.TrimSpace(dc)
if dc == "" {
continue
}
datacenterObj, err := vclib.GetDatacenter(ctx, vsi.conn, dc)
if err != nil {
klog.Errorf("Error fetching datacenter %s from VC %s: %+v", dc, vc, err)
return nil, err
}
datacenterObjs = append(datacenterObjs, datacenterObj)
}
}
}
return datacenterObjs, nil
}
// FindDatastoreByName looks for the given datastore by name across all available datacenters.
// If more than one Datacenter has a Datastore with the given name, then returns reference to all of them.
func (vs *VSphere) FindDatastoreByName(ctx context.Context, datastoreName string) ([]*vclib.DatastoreInfo, error) {
datacenters, err := vs.GetWorkspaceDatacenters(ctx)
if err != nil {
return nil, err
}
var datastoreInfos []*vclib.DatastoreInfo
for _, dc := range datacenters {
datastoreInfo, err := dc.GetDatastoreInfoByName(ctx, datastoreName)
if err != nil {
klog.V(9).Infof("Did not find datastore %s in datacenter %s, still looking.", datastoreName, dc.Name())
continue
}
datastoreInfos = append(datastoreInfos, datastoreInfo)
}
if len(datastoreInfos) == 0 {
return nil, fmt.Errorf("Datastore '%s' not found", datastoreName)
}
klog.V(4).Infof("Found datastore infos %v for datastore %s", datastoreInfos, datastoreName)
return datastoreInfos, nil
}

View File

@ -70,7 +70,7 @@ var _ = utils.SIGDescribe("Volume Provisioning on Datastore [Feature:vsphere]",
scParameters[DiskFormat] = ThinDisk scParameters[DiskFormat] = ThinDisk
err := invokeInvalidDatastoreTestNeg(client, namespace, scParameters) err := invokeInvalidDatastoreTestNeg(client, namespace, scParameters)
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
errorMsg := `Failed to provision volume with StorageClass \"` + DatastoreSCName + `\": The specified datastore ` + InvalidDatastore + ` is not a shared datastore across node VMs` errorMsg := `Failed to provision volume with StorageClass \"` + DatastoreSCName + `\": Datastore ` + InvalidDatastore + ` not found`
if !strings.Contains(err.Error(), errorMsg) { if !strings.Contains(err.Error(), errorMsg) {
framework.ExpectNoError(err, errorMsg) framework.ExpectNoError(err, errorMsg)
} }

View File

@ -176,6 +176,13 @@ var _ = utils.SIGDescribe("Zone Support", func() {
verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones) verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones)
}) })
It("Verify a pod is created on a non-Workspace zone and attached to a dynamically created PV, based on the allowed zones and storage policy specified in storage class", func() {
By(fmt.Sprintf("Creating storage class with zone :%s and storage policy :%s", zoneB, compatPolicy))
scParameters[SpbmStoragePolicy] = compatPolicy
zones = append(zones, zoneB)
verifyPVCAndPodCreationSucceeds(client, namespace, scParameters, zones)
})
It("Verify PVC creation with incompatible storagePolicy and zone combination specified in storage class fails", func() { It("Verify PVC creation with incompatible storagePolicy and zone combination specified in storage class fails", func() {
By(fmt.Sprintf("Creating storage class with zone :%s and storage policy :%s", zoneA, nonCompatPolicy)) By(fmt.Sprintf("Creating storage class with zone :%s and storage policy :%s", zoneA, nonCompatPolicy))
scParameters[SpbmStoragePolicy] = nonCompatPolicy scParameters[SpbmStoragePolicy] = nonCompatPolicy