2019-09-27 21:51:53 +00:00
/ *
Copyright 2016 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 vclib
import (
"context"
"fmt"
"github.com/vmware/govmomi/pbm"
2020-08-10 17:43:49 +00:00
"k8s.io/klog/v2"
2019-09-27 21:51:53 +00:00
pbmtypes "github.com/vmware/govmomi/pbm/types"
"github.com/vmware/govmomi/vim25"
)
// PbmClient is extending govmomi pbm, and provides functions to get compatible list of datastore for given policy
type PbmClient struct {
* pbm . Client
}
// NewPbmClient returns a new PBM Client object
func NewPbmClient ( ctx context . Context , client * vim25 . Client ) ( * PbmClient , error ) {
pbmClient , err := pbm . NewClient ( ctx , client )
if err != nil {
klog . Errorf ( "Failed to create new Pbm Client. err: %+v" , err )
return nil , err
}
return & PbmClient { pbmClient } , nil
}
// IsDatastoreCompatible check if the datastores is compatible for given storage policy id
// if datastore is not compatible with policy, fault message with the Datastore Name is returned
func ( pbmClient * PbmClient ) IsDatastoreCompatible ( ctx context . Context , storagePolicyID string , datastore * Datastore ) ( bool , string , error ) {
faultMessage := ""
placementHub := pbmtypes . PbmPlacementHub {
HubType : datastore . Reference ( ) . Type ,
HubId : datastore . Reference ( ) . Value ,
}
hubs := [ ] pbmtypes . PbmPlacementHub { placementHub }
req := [ ] pbmtypes . BasePbmPlacementRequirement {
& pbmtypes . PbmPlacementCapabilityProfileRequirement {
ProfileId : pbmtypes . PbmProfileId {
UniqueId : storagePolicyID ,
} ,
} ,
}
compatibilityResult , err := pbmClient . CheckRequirements ( ctx , hubs , nil , req )
if err != nil {
klog . Errorf ( "Error occurred for CheckRequirements call. err %+v" , err )
return false , "" , err
}
if compatibilityResult != nil && len ( compatibilityResult ) > 0 {
compatibleHubs := compatibilityResult . CompatibleDatastores ( )
if compatibleHubs != nil && len ( compatibleHubs ) > 0 {
return true , "" , nil
}
dsName , err := datastore . ObjectName ( ctx )
if err != nil {
klog . Errorf ( "Failed to get datastore ObjectName" )
return false , "" , err
}
if compatibilityResult [ 0 ] . Error [ 0 ] . LocalizedMessage == "" {
faultMessage = "Datastore: " + dsName + " is not compatible with the storage policy."
} else {
faultMessage = "Datastore: " + dsName + " is not compatible with the storage policy. LocalizedMessage: " + compatibilityResult [ 0 ] . Error [ 0 ] . LocalizedMessage + "\n"
}
return false , faultMessage , nil
}
return false , "" , fmt . Errorf ( "compatibilityResult is nil or empty" )
}
// 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
func ( pbmClient * PbmClient ) GetCompatibleDatastores ( ctx context . Context , storagePolicyID string , datastores [ ] * DatastoreInfo ) ( [ ] * DatastoreInfo , string , error ) {
var (
dsMorNameMap = getDsMorNameMap ( ctx , datastores )
localizedMessagesForNotCompatibleDatastores = ""
)
compatibilityResult , err := pbmClient . GetPlacementCompatibilityResult ( ctx , storagePolicyID , datastores )
if err != nil {
klog . Errorf ( "Error occurred while retrieving placement compatibility result for datastores: %+v with storagePolicyID: %s. err: %+v" , datastores , storagePolicyID , err )
return nil , "" , err
}
compatibleHubs := compatibilityResult . CompatibleDatastores ( )
var compatibleDatastoreList [ ] * DatastoreInfo
for _ , hub := range compatibleHubs {
compatibleDatastoreList = append ( compatibleDatastoreList , getDatastoreFromPlacementHub ( datastores , hub ) )
}
for _ , res := range compatibilityResult {
for _ , err := range res . Error {
dsName := dsMorNameMap [ res . Hub . HubId ]
localizedMessage := ""
if err . LocalizedMessage != "" {
localizedMessage = "Datastore: " + dsName + " not compatible with the storage policy. LocalizedMessage: " + err . LocalizedMessage + "\n"
} else {
localizedMessage = "Datastore: " + dsName + " not compatible with the storage policy. \n"
}
localizedMessagesForNotCompatibleDatastores += localizedMessage
}
}
// Return an error if there are no compatible datastores.
if len ( compatibleHubs ) < 1 {
klog . Errorf ( "No compatible datastores found that satisfy the storage policy requirements: %s" , storagePolicyID )
return nil , localizedMessagesForNotCompatibleDatastores , fmt . Errorf ( "No compatible datastores found that satisfy the storage policy requirements" )
}
return compatibleDatastoreList , localizedMessagesForNotCompatibleDatastores , nil
}
// GetPlacementCompatibilityResult gets placement compatibility result based on storage policy requirements.
func ( pbmClient * PbmClient ) GetPlacementCompatibilityResult ( ctx context . Context , storagePolicyID string , datastore [ ] * DatastoreInfo ) ( pbm . PlacementCompatibilityResult , error ) {
var hubs [ ] pbmtypes . PbmPlacementHub
for _ , ds := range datastore {
hubs = append ( hubs , pbmtypes . PbmPlacementHub {
HubType : ds . Reference ( ) . Type ,
HubId : ds . Reference ( ) . Value ,
} )
}
req := [ ] pbmtypes . BasePbmPlacementRequirement {
& pbmtypes . PbmPlacementCapabilityProfileRequirement {
ProfileId : pbmtypes . PbmProfileId {
UniqueId : storagePolicyID ,
} ,
} ,
}
res , err := pbmClient . CheckRequirements ( ctx , hubs , nil , req )
if err != nil {
klog . Errorf ( "Error occurred for CheckRequirements call. err: %+v" , err )
return nil , err
}
return res , nil
}
// getDataStoreForPlacementHub returns matching datastore associated with given pbmPlacementHub
func getDatastoreFromPlacementHub ( datastore [ ] * DatastoreInfo , pbmPlacementHub pbmtypes . PbmPlacementHub ) * DatastoreInfo {
for _ , ds := range datastore {
if ds . Reference ( ) . Type == pbmPlacementHub . HubType && ds . Reference ( ) . Value == pbmPlacementHub . HubId {
return ds
}
}
return nil
}
// getDsMorNameMap returns map of ds Mor and Datastore Object Name
func getDsMorNameMap ( ctx context . Context , datastores [ ] * DatastoreInfo ) map [ string ] string {
dsMorNameMap := make ( map [ string ] string )
for _ , ds := range datastores {
dsObjectName , err := ds . ObjectName ( ctx )
if err == nil {
dsMorNameMap [ ds . Reference ( ) . Value ] = dsObjectName
} else {
klog . Errorf ( "Error occurred while getting datastore object name. err: %+v" , err )
}
}
return dsMorNameMap
}