2017-01-31 17:26:02 +00:00
/ *
Copyright 2017 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package vsphere
import (
"context"
2017-08-02 15:05:23 +00:00
"errors"
2018-09-07 00:43:54 +00:00
"fmt"
2017-01-31 17:26:02 +00:00
"os"
2018-09-07 00:43:54 +00:00
"path/filepath"
2017-01-31 17:26:02 +00:00
"strings"
2017-08-02 15:05:23 +00:00
"time"
2017-05-22 18:21:22 +00:00
2019-01-03 11:06:21 +00:00
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
2017-08-02 15:05:23 +00:00
"github.com/vmware/govmomi/vim25"
2017-11-16 06:24:23 +00:00
"github.com/vmware/govmomi/vim25/mo"
2019-01-19 23:54:14 +00:00
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
2018-11-09 18:49:10 +00:00
"k8s.io/klog"
2018-09-07 00:43:54 +00:00
2018-04-19 21:24:40 +00:00
"k8s.io/api/core/v1"
2017-11-16 06:24:23 +00:00
k8stypes "k8s.io/apimachinery/pkg/types"
2018-09-21 06:03:05 +00:00
"k8s.io/apimachinery/pkg/util/version"
2017-08-02 15:05:23 +00:00
"k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere/vclib"
"k8s.io/kubernetes/pkg/cloudprovider/providers/vsphere/vclib/diskmanagers"
2017-05-22 18:21:22 +00:00
)
const (
DatastoreProperty = "datastore"
DatastoreInfoProperty = "info"
Folder = "Folder"
VirtualMachine = "VirtualMachine"
2017-09-18 19:01:48 +00:00
DummyDiskName = "kube-dummyDisk.vmdk"
2018-02-02 23:11:49 +00:00
ProviderPrefix = "vsphere://"
2018-01-18 23:29:27 +00:00
vSphereConfFileEnvVar = "VSPHERE_CONF_FILE"
2017-01-31 17:26:02 +00:00
)
2017-08-02 15:05:23 +00:00
// GetVSphere reads vSphere configuration from system environment and construct vSphere object
2017-01-31 17:26:02 +00:00
func GetVSphere ( ) ( * VSphere , error ) {
2018-01-18 23:29:27 +00:00
cfg , err := getVSphereConfig ( )
if err != nil {
return nil , err
}
vs , err := newControllerNode ( * cfg )
2017-02-10 06:33:37 +00:00
if err != nil {
return nil , err
}
return vs , nil
}
2018-01-18 23:29:27 +00:00
func getVSphereConfig ( ) ( * VSphereConfig , error ) {
confFileLocation := os . Getenv ( vSphereConfFileEnvVar )
if confFileLocation == "" {
return nil , fmt . Errorf ( "Env variable 'VSPHERE_CONF_FILE' is not set." )
}
confFile , err := os . Open ( confFileLocation )
if err != nil {
return nil , err
}
2019-01-26 21:14:54 +00:00
defer func ( ) {
if err := confFile . Close ( ) ; err != nil {
klog . Errorf ( "failed to close config file: %v" , err )
}
} ( )
2018-01-18 23:29:27 +00:00
cfg , err := readConfig ( confFile )
if err != nil {
return nil , err
}
return & cfg , nil
2017-02-10 06:33:37 +00:00
}
2017-11-16 06:24:23 +00:00
// Returns the accessible datastores for the given node VM.
func getAccessibleDatastores ( ctx context . Context , nodeVmDetail * NodeDetails , nodeManager * NodeManager ) ( [ ] * vclib . DatastoreInfo , error ) {
accessibleDatastores , err := nodeVmDetail . vm . GetAllAccessibleDatastores ( ctx )
2017-05-22 18:21:22 +00:00
if err != nil {
2017-11-16 06:24:23 +00:00
// Check if the node VM is not found which indicates that the node info in the node manager is stale.
// If so, rediscover the node and retry.
if vclib . IsManagedObjectNotFoundError ( err ) {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "error %q ManagedObjectNotFound for node %q. Rediscovering..." , err , nodeVmDetail . NodeName )
2017-11-16 06:24:23 +00:00
err = nodeManager . RediscoverNode ( convertToK8sType ( nodeVmDetail . NodeName ) )
if err == nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "Discovered node %s successfully" , nodeVmDetail . NodeName )
2017-11-16 06:24:23 +00:00
nodeInfo , err := nodeManager . GetNodeInfo ( convertToK8sType ( nodeVmDetail . NodeName ) )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "error %q getting node info for node %+v" , err , nodeVmDetail )
2017-11-16 06:24:23 +00:00
return nil , err
}
accessibleDatastores , err = nodeInfo . vm . GetAllAccessibleDatastores ( ctx )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "error %q getting accessible datastores for node %+v" , err , nodeVmDetail )
2017-11-16 06:24:23 +00:00
return nil , err
}
} else {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "error %q rediscovering node %+v" , err , nodeVmDetail )
2017-11-16 06:24:23 +00:00
return nil , err
}
} else {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "error %q getting accessible datastores for node %+v" , err , nodeVmDetail )
2017-11-16 06:24:23 +00:00
return nil , err
}
2017-05-22 18:21:22 +00:00
}
2017-11-16 06:24:23 +00:00
return accessibleDatastores , nil
}
// Get all datastores accessible for the virtual machine object.
func getSharedDatastoresInK8SCluster ( ctx context . Context , dc * vclib . Datacenter , nodeManager * NodeManager ) ( [ ] * vclib . DatastoreInfo , error ) {
2017-11-21 05:38:20 +00:00
nodeVmDetails , err := nodeManager . GetNodeDetails ( )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Error while obtaining Kubernetes node nodeVmDetail details. error : %+v" , err )
2017-11-21 05:38:20 +00:00
return nil , err
}
if len ( nodeVmDetails ) == 0 {
2017-11-16 06:24:23 +00:00
msg := fmt . Sprintf ( "Kubernetes node nodeVmDetail details is empty. nodeVmDetails : %+v" , nodeVmDetails )
2018-11-09 18:49:10 +00:00
klog . Error ( msg )
2017-11-16 06:24:23 +00:00
return nil , fmt . Errorf ( msg )
2017-05-22 18:21:22 +00:00
}
2017-11-16 06:24:23 +00:00
var sharedDatastores [ ] * vclib . DatastoreInfo
2017-12-01 18:31:48 +00:00
for _ , nodeVmDetail := range nodeVmDetails {
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "Getting accessible datastores for node %s" , nodeVmDetail . NodeName )
2017-11-16 06:24:23 +00:00
accessibleDatastores , err := getAccessibleDatastores ( ctx , & nodeVmDetail , nodeManager )
2017-08-02 15:05:23 +00:00
if err != nil {
2017-12-01 18:31:48 +00:00
if err == vclib . ErrNoVMFound {
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "Got NoVMFound error for node %s" , nodeVmDetail . NodeName )
2017-12-01 18:31:48 +00:00
continue
}
2017-08-02 15:05:23 +00:00
return nil , err
}
2017-12-01 18:31:48 +00:00
if len ( sharedDatastores ) == 0 {
2017-11-16 06:24:23 +00:00
sharedDatastores = accessibleDatastores
} else {
sharedDatastores = intersect ( sharedDatastores , accessibleDatastores )
if len ( sharedDatastores ) == 0 {
return nil , fmt . Errorf ( "No shared datastores found in the Kubernetes cluster for nodeVmDetails: %+v" , nodeVmDetails )
2017-05-22 18:21:22 +00:00
}
}
}
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "sharedDatastores : %+v" , sharedDatastores )
2017-11-21 05:38:20 +00:00
sharedDatastores , err = getDatastoresForEndpointVC ( ctx , dc , sharedDatastores )
2017-11-16 06:24:23 +00:00
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to get shared datastores from endpoint VC. err: %+v" , err )
2017-11-16 06:24:23 +00:00
return nil , err
}
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "sharedDatastores at endpoint VC: %+v" , sharedDatastores )
2017-08-02 15:05:23 +00:00
return sharedDatastores , nil
2017-05-22 18:21:22 +00:00
}
2017-11-16 06:24:23 +00:00
func intersect ( list1 [ ] * vclib . DatastoreInfo , list2 [ ] * vclib . DatastoreInfo ) [ ] * vclib . DatastoreInfo {
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "list1: %+v" , list1 )
klog . V ( 9 ) . Infof ( "list2: %+v" , list2 )
2017-11-16 06:24:23 +00:00
var sharedDs [ ] * vclib . DatastoreInfo
2017-05-22 18:21:22 +00:00
for _ , val1 := range list1 {
// Check if val1 is found in list2
for _ , val2 := range list2 {
2017-11-16 06:24:23 +00:00
// Intersection is performed based on the datastoreUrl as this uniquely identifies the datastore.
if val1 . Info . Url == val2 . Info . Url {
2017-08-02 15:05:23 +00:00
sharedDs = append ( sharedDs , val1 )
2017-05-22 18:21:22 +00:00
break
}
}
}
2017-08-02 15:05:23 +00:00
return sharedDs
2017-05-22 18:21:22 +00:00
}
2017-08-02 15:05:23 +00:00
// getMostFreeDatastore gets the best fit compatible datastore by free space.
2017-11-16 06:24:23 +00:00
func getMostFreeDatastoreName ( ctx context . Context , client * vim25 . Client , dsInfoList [ ] * vclib . DatastoreInfo ) ( string , error ) {
2017-08-02 15:05:23 +00:00
var curMax int64
curMax = - 1
var index int
2017-11-16 06:24:23 +00:00
for i , dsInfo := range dsInfoList {
dsFreeSpace := dsInfo . Info . GetDatastoreInfo ( ) . FreeSpace
2017-08-02 15:05:23 +00:00
if dsFreeSpace > curMax {
curMax = dsFreeSpace
index = i
}
}
2017-11-16 06:24:23 +00:00
return dsInfoList [ index ] . Info . GetDatastoreInfo ( ) . Name , nil
2017-05-22 18:21:22 +00:00
}
2017-11-16 06:24:23 +00:00
// Returns the datastores in the given datacenter by performing lookup based on datastore URL.
func getDatastoresForEndpointVC ( ctx context . Context , dc * vclib . Datacenter , sharedDsInfos [ ] * vclib . DatastoreInfo ) ( [ ] * vclib . DatastoreInfo , error ) {
var datastores [ ] * vclib . DatastoreInfo
allDsInfoMap , err := dc . GetAllDatastores ( ctx )
if err != nil {
return nil , err
}
for _ , sharedDsInfo := range sharedDsInfos {
dsInfo , ok := allDsInfoMap [ sharedDsInfo . Info . Url ]
if ok {
datastores = append ( datastores , dsInfo )
} else {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "Warning: Shared datastore with URL %s does not exist in endpoint VC" , sharedDsInfo . Info . Url )
2017-11-16 06:24:23 +00:00
}
}
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "Datastore from endpoint VC: %+v" , datastores )
2017-11-16 06:24:23 +00:00
return datastores , nil
}
func getPbmCompatibleDatastore ( ctx context . Context , dc * vclib . Datacenter , storagePolicyName string , nodeManager * NodeManager ) ( string , error ) {
pbmClient , err := vclib . NewPbmClient ( ctx , dc . Client ( ) )
2017-05-22 18:21:22 +00:00
if err != nil {
2017-08-02 15:05:23 +00:00
return "" , err
2017-05-22 18:21:22 +00:00
}
2017-08-02 15:05:23 +00:00
storagePolicyID , err := pbmClient . ProfileIDByName ( ctx , storagePolicyName )
2017-05-22 18:21:22 +00:00
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to get Profile ID by name: %s. err: %+v" , storagePolicyName , err )
2017-08-02 15:05:23 +00:00
return "" , err
}
2017-11-16 06:24:23 +00:00
sharedDs , err := getSharedDatastoresInK8SCluster ( ctx , dc , nodeManager )
2017-08-02 15:05:23 +00:00
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to get shared datastores. err: %+v" , err )
2017-08-02 15:05:23 +00:00
return "" , err
}
2017-11-16 06:24:23 +00:00
if len ( sharedDs ) == 0 {
msg := "No shared datastores found in the endpoint virtual center"
2018-11-09 18:49:10 +00:00
klog . Errorf ( msg )
2017-11-16 06:24:23 +00:00
return "" , errors . New ( msg )
}
compatibleDatastores , _ , err := pbmClient . GetCompatibleDatastores ( ctx , dc , storagePolicyID , sharedDs )
2017-08-02 15:05:23 +00:00
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to get compatible datastores from datastores : %+v with storagePolicy: %s. err: %+v" ,
2017-11-16 06:24:23 +00:00
sharedDs , storagePolicyID , err )
2017-08-02 15:05:23 +00:00
return "" , err
2017-05-22 18:21:22 +00:00
}
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "compatibleDatastores : %+v" , compatibleDatastores )
2017-11-16 06:24:23 +00:00
datastore , err := getMostFreeDatastoreName ( ctx , dc . Client ( ) , compatibleDatastores )
2019-01-03 11:06:21 +00:00
if err != nil {
klog . Errorf ( "Failed to get most free datastore from compatible datastores: %+v. err: %+v" , compatibleDatastores , err )
return "" , err
}
klog . V ( 4 ) . Infof ( "Most free datastore : %+s" , datastore )
return datastore , err
}
func getDatastoresForZone ( ctx context . Context , dc * vclib . Datacenter , nodeManager * NodeManager , selectedZones [ ] string ) ( [ ] * vclib . DatastoreInfo , error ) {
var sharedDatastores [ ] * vclib . DatastoreInfo
for _ , zone := range selectedZones {
var sharedDatastoresPerZone [ ] * vclib . DatastoreInfo
hosts , err := nodeManager . GetHostsInZone ( ctx , zone )
if err != nil {
return nil , err
}
klog . V ( 4 ) . Infof ( "Hosts in zone %s : %s" , zone , hosts )
for _ , host := range hosts {
var hostSystemMo mo . HostSystem
2019-02-19 08:17:52 +00:00
err = host . Properties ( ctx , host . Reference ( ) , [ ] string { "datastore" } , & hostSystemMo )
if err != nil {
klog . Errorf ( "Failed to get datastore property for host %s. err : %+v" , host , err )
return nil , err
}
2019-01-03 11:06:21 +00:00
klog . V ( 4 ) . Infof ( "Datastores mounted on host %s : %s" , host , hostSystemMo . Datastore )
var dsRefList [ ] types . ManagedObjectReference
for _ , dsRef := range hostSystemMo . Datastore {
dsRefList = append ( dsRefList , dsRef )
}
var dsMoList [ ] mo . Datastore
pc := property . DefaultCollector ( host . Client ( ) )
properties := [ ] string { DatastoreInfoProperty }
err = pc . Retrieve ( ctx , dsRefList , properties , & dsMoList )
if err != nil {
klog . Errorf ( "Failed to get Datastore managed objects from datastore objects." +
2019-02-19 08:17:52 +00:00
" dsObjList: %+v, properties: %+v, err: %+v" , dsRefList , properties , err )
2019-01-03 11:06:21 +00:00
return nil , err
}
klog . V ( 9 ) . Infof ( "Datastore mo details: %+v" , dsMoList )
var dsObjList [ ] * vclib . DatastoreInfo
for _ , dsMo := range dsMoList {
dsObjList = append ( dsObjList ,
& vclib . DatastoreInfo {
Datastore : & vclib . Datastore { Datastore : object . NewDatastore ( host . Client ( ) , dsMo . Reference ( ) ) ,
Datacenter : nil } ,
Info : dsMo . Info . GetDatastoreInfo ( ) } )
}
klog . V ( 9 ) . Infof ( "DatastoreInfo details : %s" , dsObjList )
if len ( sharedDatastoresPerZone ) == 0 {
sharedDatastoresPerZone = dsObjList
} else {
sharedDatastoresPerZone = intersect ( sharedDatastoresPerZone , dsObjList )
}
klog . V ( 9 ) . Infof ( "Shared datastore list after processing host %s : %s" , host , sharedDatastoresPerZone )
}
klog . V ( 4 ) . Infof ( "Shared datastore per zone %s is %s" , zone , sharedDatastoresPerZone )
if len ( sharedDatastores ) == 0 {
sharedDatastores = sharedDatastoresPerZone
} else {
sharedDatastores = intersect ( sharedDatastores , sharedDatastoresPerZone )
}
}
klog . V ( 1 ) . Infof ( "Returning selected datastores : %s" , sharedDatastores )
return sharedDatastores , nil
}
func getPbmCompatibleZonedDatastore ( ctx context . Context , dc * vclib . Datacenter , storagePolicyName string , zonedDatastores [ ] * vclib . DatastoreInfo ) ( string , error ) {
pbmClient , err := vclib . NewPbmClient ( ctx , dc . Client ( ) )
if err != nil {
return "" , err
}
storagePolicyID , err := pbmClient . ProfileIDByName ( ctx , storagePolicyName )
if err != nil {
klog . Errorf ( "Failed to get Profile ID by name: %s. err: %+v" , storagePolicyName , err )
return "" , err
}
compatibleDatastores , _ , err := pbmClient . GetCompatibleDatastores ( ctx , dc , storagePolicyID , zonedDatastores )
if err != nil {
klog . Errorf ( "Failed to get compatible datastores from datastores : %+v with storagePolicy: %s. err: %+v" ,
zonedDatastores , storagePolicyID , err )
return "" , err
}
klog . V ( 9 ) . Infof ( "compatibleDatastores : %+v" , compatibleDatastores )
datastore , err := getMostFreeDatastoreName ( ctx , dc . Client ( ) , compatibleDatastores )
2017-08-02 15:05:23 +00:00
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to get most free datastore from compatible datastores: %+v. err: %+v" , compatibleDatastores , err )
2017-08-02 15:05:23 +00:00
return "" , err
}
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "Most free datastore : %+s" , datastore )
2017-08-02 15:05:23 +00:00
return datastore , err
}
2017-05-22 18:21:22 +00:00
2017-11-16 06:24:23 +00:00
func ( vs * VSphere ) setVMOptions ( ctx context . Context , dc * vclib . Datacenter , resourcePoolPath string ) ( * vclib . VMOptions , error ) {
2017-08-02 15:05:23 +00:00
var vmOptions vclib . VMOptions
2017-11-16 06:24:23 +00:00
resourcePool , err := dc . GetResourcePool ( ctx , resourcePoolPath )
2017-05-22 18:21:22 +00:00
if err != nil {
return nil , err
}
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "Resource pool path %s, resourcePool %+v" , resourcePoolPath , resourcePool )
2017-11-16 06:24:23 +00:00
folder , err := dc . GetFolderByPath ( ctx , vs . cfg . Workspace . Folder )
2017-08-02 15:05:23 +00:00
if err != nil {
return nil , err
}
vmOptions . VMFolder = folder
vmOptions . VMResourcePool = resourcePool
return & vmOptions , nil
}
2017-05-22 18:21:22 +00:00
2017-08-02 15:05:23 +00:00
// A background routine which will be responsible for deleting stale dummy VM's.
func ( vs * VSphere ) cleanUpDummyVMs ( dummyVMPrefix string ) {
// Create context
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
for {
time . Sleep ( CleanUpDummyVMRoutineInterval * time . Minute )
2017-11-16 06:24:23 +00:00
vsi , err := vs . getVSphereInstanceForServer ( vs . cfg . Workspace . VCenterIP , ctx )
2017-08-02 15:05:23 +00:00
if err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "Failed to get VSphere instance with err: %+v. Retrying again..." , err )
2017-08-02 15:05:23 +00:00
continue
}
2017-11-16 06:24:23 +00:00
dc , err := vclib . GetDatacenter ( ctx , vsi . conn , vs . cfg . Workspace . Datacenter )
2017-08-02 15:05:23 +00:00
if err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "Failed to get the datacenter: %s from VC. err: %+v" , vs . cfg . Workspace . Datacenter , err )
2017-08-02 15:05:23 +00:00
continue
}
// Get the folder reference for global working directory where the dummy VM needs to be created.
2017-11-16 06:24:23 +00:00
vmFolder , err := dc . GetFolderByPath ( ctx , vs . cfg . Workspace . Folder )
2017-08-02 15:05:23 +00:00
if err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "Unable to get the kubernetes folder: %q reference. err: %+v" , vs . cfg . Workspace . Folder , err )
2017-08-02 15:05:23 +00:00
continue
}
// A write lock is acquired to make sure the cleanUp routine doesn't delete any VM's created by ongoing PVC requests.
2018-09-07 00:43:54 +00:00
cleanUpDummyVMs := func ( ) {
cleanUpDummyVMLock . Lock ( )
defer cleanUpDummyVMLock . Unlock ( )
err = diskmanagers . CleanUpDummyVMs ( ctx , vmFolder , dc )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "Unable to clean up dummy VM's in the kubernetes cluster: %q. err: %+v" , vs . cfg . Workspace . Folder , err )
2018-09-07 00:43:54 +00:00
}
2017-08-02 15:05:23 +00:00
}
2018-09-07 00:43:54 +00:00
cleanUpDummyVMs ( )
2017-05-22 18:21:22 +00:00
}
}
2017-09-18 19:01:48 +00:00
// Get canonical volume path for volume Path.
// Example1: The canonical path for volume path - [vsanDatastore] kubevols/volume.vmdk will be [vsanDatastore] 25d8b159-948c-4b73-e499-02001ad1b044/volume.vmdk
// Example2: The canonical path for volume path - [vsanDatastore] 25d8b159-948c-4b73-e499-02001ad1b044/volume.vmdk will be same as volume Path.
func getcanonicalVolumePath ( ctx context . Context , dc * vclib . Datacenter , volumePath string ) ( string , error ) {
var folderID string
var folderExists bool
canonicalVolumePath := volumePath
dsPathObj , err := vclib . GetDatastorePathObjFromVMDiskPath ( volumePath )
if err != nil {
return "" , err
}
dsPath := strings . Split ( strings . TrimSpace ( dsPathObj . Path ) , "/" )
if len ( dsPath ) <= 1 {
return canonicalVolumePath , nil
}
datastore := dsPathObj . Datastore
dsFolder := dsPath [ 0 ]
folderNameIDMap , datastoreExists := datastoreFolderIDMap [ datastore ]
if datastoreExists {
folderID , folderExists = folderNameIDMap [ dsFolder ]
}
// Get the datastore folder ID if datastore or folder doesn't exist in datastoreFolderIDMap
if ! datastoreExists || ! folderExists {
if ! vclib . IsValidUUID ( dsFolder ) {
dummyDiskVolPath := "[" + datastore + "] " + dsFolder + "/" + DummyDiskName
// Querying a non-existent dummy disk on the datastore folder.
// It would fail and return an folder ID in the error message.
_ , err := dc . GetVirtualDiskPage83Data ( ctx , dummyDiskVolPath )
2019-01-19 23:54:14 +00:00
canonicalVolumePath , err = getPathFromFileNotFound ( err )
2017-09-18 19:01:48 +00:00
if err != nil {
2019-01-19 23:54:14 +00:00
return "" , fmt . Errorf ( "failed to get path from dummy request: %v" , err )
2017-09-18 19:01:48 +00:00
}
}
diskPath := vclib . GetPathFromVMDiskPath ( canonicalVolumePath )
if diskPath == "" {
return "" , fmt . Errorf ( "Failed to parse canonicalVolumePath: %s in getcanonicalVolumePath method" , canonicalVolumePath )
}
folderID = strings . Split ( strings . TrimSpace ( diskPath ) , "/" ) [ 0 ]
setdatastoreFolderIDMap ( datastoreFolderIDMap , datastore , dsFolder , folderID )
}
canonicalVolumePath = strings . Replace ( volumePath , dsFolder , folderID , 1 )
return canonicalVolumePath , nil
}
2019-01-19 23:54:14 +00:00
// getPathFromFileNotFound returns the path from a fileNotFound error
func getPathFromFileNotFound ( err error ) ( string , error ) {
if soap . IsSoapFault ( err ) {
fault := soap . ToSoapFault ( err )
f , ok := fault . VimFault ( ) . ( types . FileNotFound )
if ! ok {
return "" , fmt . Errorf ( "%v is not a FileNotFound error" , err )
}
return f . File , nil
}
return "" , fmt . Errorf ( "%v is not a soap fault" , err )
}
2017-09-18 19:01:48 +00:00
func setdatastoreFolderIDMap (
datastoreFolderIDMap map [ string ] map [ string ] string ,
datastore string ,
folderName string ,
folderID string ) {
folderNameIDMap := datastoreFolderIDMap [ datastore ]
if folderNameIDMap == nil {
folderNameIDMap = make ( map [ string ] string )
datastoreFolderIDMap [ datastore ] = folderNameIDMap
}
folderNameIDMap [ folderName ] = folderID
}
2017-11-16 06:24:23 +00:00
func convertVolPathToDevicePath ( ctx context . Context , dc * vclib . Datacenter , volPath string ) ( string , error ) {
volPath = vclib . RemoveStorageClusterORFolderNameFromVDiskPath ( volPath )
// Get the canonical volume path for volPath.
canonicalVolumePath , err := getcanonicalVolumePath ( ctx , dc , volPath )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to get canonical vsphere volume path for volume: %s. err: %+v" , volPath , err )
2017-11-16 06:24:23 +00:00
return "" , err
}
// Check if the volume path contains .vmdk extension. If not, add the extension and update the nodeVolumes Map
if len ( canonicalVolumePath ) > 0 && filepath . Ext ( canonicalVolumePath ) != ".vmdk" {
canonicalVolumePath += ".vmdk"
}
return canonicalVolumePath , nil
}
// convertVolPathsToDevicePaths removes cluster or folder path from volPaths and convert to canonicalPath
func ( vs * VSphere ) convertVolPathsToDevicePaths ( ctx context . Context , nodeVolumes map [ k8stypes . NodeName ] [ ] string ) ( map [ k8stypes . NodeName ] [ ] string , error ) {
vmVolumes := make ( map [ k8stypes . NodeName ] [ ] string )
for nodeName , volPaths := range nodeVolumes {
nodeInfo , err := vs . nodeManager . GetNodeInfo ( nodeName )
if err != nil {
return nil , err
}
_ , err = vs . getVSphereInstanceForServer ( nodeInfo . vcServer , ctx )
if err != nil {
return nil , err
}
for i , volPath := range volPaths {
deviceVolPath , err := convertVolPathToDevicePath ( ctx , nodeInfo . dataCenter , volPath )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to convert vsphere volume path %s to device path for volume %s. err: %+v" , volPath , deviceVolPath , err )
2017-11-16 06:24:23 +00:00
return nil , err
}
volPaths [ i ] = deviceVolPath
}
vmVolumes [ nodeName ] = volPaths
}
return vmVolumes , nil
}
// checkDiskAttached verifies volumes are attached to the VMs which are in same vCenter and Datacenter
// Returns nodes if exist any for which VM is not found in that vCenter and Datacenter
func ( vs * VSphere ) checkDiskAttached ( ctx context . Context , nodes [ ] k8stypes . NodeName , nodeVolumes map [ k8stypes . NodeName ] [ ] string , attached map [ string ] map [ string ] bool , retry bool ) ( [ ] k8stypes . NodeName , error ) {
var nodesToRetry [ ] k8stypes . NodeName
var vmList [ ] * vclib . VirtualMachine
var nodeInfo NodeInfo
var err error
for _ , nodeName := range nodes {
nodeInfo , err = vs . nodeManager . GetNodeInfo ( nodeName )
if err != nil {
return nodesToRetry , err
}
vmList = append ( vmList , nodeInfo . vm )
}
// Making sure session is valid
_ , err = vs . getVSphereInstanceForServer ( nodeInfo . vcServer , ctx )
if err != nil {
return nodesToRetry , err
}
// If any of the nodes are not present property collector query will fail for entire operation
vmMoList , err := nodeInfo . dataCenter . GetVMMoList ( ctx , vmList , [ ] string { "config.hardware.device" , "name" , "config.uuid" } )
if err != nil {
if vclib . IsManagedObjectNotFoundError ( err ) && ! retry {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "checkDiskAttached: ManagedObjectNotFound for property collector query for nodes: %+v vms: %+v" , nodes , vmList )
2017-11-16 06:24:23 +00:00
// Property Collector Query failed
// VerifyVolumePaths per VM
for _ , nodeName := range nodes {
nodeInfo , err := vs . nodeManager . GetNodeInfo ( nodeName )
if err != nil {
return nodesToRetry , err
}
devices , err := nodeInfo . vm . VirtualMachine . Device ( ctx )
if err != nil {
if vclib . IsManagedObjectNotFoundError ( err ) {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "checkDiskAttached: ManagedObjectNotFound for Kubernetes node: %s with vSphere Virtual Machine reference: %v" , nodeName , nodeInfo . vm )
2017-11-16 06:24:23 +00:00
nodesToRetry = append ( nodesToRetry , nodeName )
continue
}
return nodesToRetry , err
}
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "Verifying Volume Paths by devices for node %s and VM %s" , nodeName , nodeInfo . vm )
2017-11-16 06:24:23 +00:00
vclib . VerifyVolumePathsForVMDevices ( devices , nodeVolumes [ nodeName ] , convertToString ( nodeName ) , attached )
}
}
return nodesToRetry , err
}
vmMoMap := make ( map [ string ] mo . VirtualMachine )
for _ , vmMo := range vmMoList {
if vmMo . Config == nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Config is not available for VM: %q" , vmMo . Name )
2017-11-16 06:24:23 +00:00
continue
}
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "vmMoMap vmname: %q vmuuid: %s" , vmMo . Name , strings . ToLower ( vmMo . Config . Uuid ) )
2017-11-16 06:24:23 +00:00
vmMoMap [ strings . ToLower ( vmMo . Config . Uuid ) ] = vmMo
}
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "vmMoMap: +%v" , vmMoMap )
2017-11-16 06:24:23 +00:00
for _ , nodeName := range nodes {
node , err := vs . nodeManager . GetNode ( nodeName )
if err != nil {
return nodesToRetry , err
}
2018-04-19 21:24:40 +00:00
nodeUUID , err := GetNodeUUID ( & node )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Node Discovery failed to get node uuid for node %s with error: %v" , node . Name , err )
2018-04-19 21:24:40 +00:00
return nodesToRetry , err
}
nodeUUID = strings . ToLower ( nodeUUID )
2018-11-09 18:49:10 +00:00
klog . V ( 9 ) . Infof ( "Verifying volume for node %s with nodeuuid %q: %v" , nodeName , nodeUUID , vmMoMap )
2018-02-02 23:11:49 +00:00
vclib . VerifyVolumePathsForVM ( vmMoMap [ nodeUUID ] , nodeVolumes [ nodeName ] , convertToString ( nodeName ) , attached )
2017-11-16 06:24:23 +00:00
}
return nodesToRetry , nil
}
2018-01-18 23:29:27 +00:00
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
}
2018-02-02 23:11:49 +00:00
2018-07-05 21:07:41 +00:00
func ( vs * VSphere ) GetNodeNameFromProviderID ( providerID string ) ( string , error ) {
var nodeName string
nodes , err := vs . nodeManager . GetNodeDetails ( )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Error while obtaining Kubernetes node nodeVmDetail details. error : %+v" , err )
2018-07-05 21:07:41 +00:00
return "" , err
}
for _ , node := range nodes {
// ProviderID is UUID for nodes v1.9.3+
if node . VMUUID == GetUUIDFromProviderID ( providerID ) || node . NodeName == providerID {
nodeName = node . NodeName
break
}
}
if nodeName == "" {
msg := fmt . Sprintf ( "Error while obtaining Kubernetes nodename for providerID %s." , providerID )
return "" , errors . New ( msg )
}
return nodeName , nil
}
2018-02-02 23:11:49 +00:00
func GetUUIDFromProviderID ( providerID string ) string {
return strings . TrimPrefix ( providerID , ProviderPrefix )
}
2018-04-19 21:24:40 +00:00
func IsUUIDSupportedNode ( node * v1 . Node ) ( bool , error ) {
newVersion , err := version . ParseSemantic ( "v1.9.4" )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to determine whether node %+v is old with error %v" , node , err )
2018-04-19 21:24:40 +00:00
return false , err
}
nodeVersion , err := version . ParseSemantic ( node . Status . NodeInfo . KubeletVersion )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to determine whether node %+v is old with error %v" , node , err )
2018-04-19 21:24:40 +00:00
return false , err
}
if nodeVersion . LessThan ( newVersion ) {
return true , nil
}
return false , nil
}
func GetNodeUUID ( node * v1 . Node ) ( string , error ) {
oldNode , err := IsUUIDSupportedNode ( node )
if err != nil {
2018-11-09 18:49:10 +00:00
klog . Errorf ( "Failed to get node UUID for node %+v with error %v" , node , err )
2018-04-19 21:24:40 +00:00
return "" , err
}
if oldNode {
return node . Status . NodeInfo . SystemUUID , nil
}
return GetUUIDFromProviderID ( node . Spec . ProviderID ) , nil
}