307 lines
8.8 KiB
Go
307 lines
8.8 KiB
Go
/*
|
|
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"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"fmt"
|
|
|
|
"github.com/vmware/govmomi"
|
|
"github.com/vmware/govmomi/find"
|
|
"github.com/vmware/govmomi/object"
|
|
"github.com/vmware/govmomi/pbm"
|
|
"github.com/vmware/govmomi/property"
|
|
"github.com/vmware/govmomi/vim25/mo"
|
|
"github.com/vmware/govmomi/vim25/types"
|
|
|
|
pbmtypes "github.com/vmware/govmomi/pbm/types"
|
|
)
|
|
|
|
const (
|
|
DatastoreProperty = "datastore"
|
|
DatastoreInfoProperty = "info"
|
|
Folder = "Folder"
|
|
VirtualMachine = "VirtualMachine"
|
|
)
|
|
|
|
// Reads vSphere configuration from system environment and construct vSphere object
|
|
func GetVSphere() (*VSphere, error) {
|
|
cfg := getVSphereConfig()
|
|
client, err := GetgovmomiClient(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vs := &VSphere{
|
|
client: client,
|
|
cfg: cfg,
|
|
localInstanceID: "",
|
|
}
|
|
runtime.SetFinalizer(vs, logout)
|
|
return vs, nil
|
|
}
|
|
|
|
func getVSphereConfig() *VSphereConfig {
|
|
var cfg VSphereConfig
|
|
cfg.Global.VCenterIP = os.Getenv("VSPHERE_VCENTER")
|
|
cfg.Global.User = os.Getenv("VSPHERE_USER")
|
|
cfg.Global.Password = os.Getenv("VSPHERE_PASSWORD")
|
|
cfg.Global.Datacenter = os.Getenv("VSPHERE_DATACENTER")
|
|
cfg.Global.Datastore = os.Getenv("VSPHERE_DATASTORE")
|
|
cfg.Global.WorkingDir = os.Getenv("VSPHERE_WORKING_DIR")
|
|
cfg.Global.VMName = os.Getenv("VSPHERE_VM_NAME")
|
|
cfg.Global.InsecureFlag = false
|
|
if strings.ToLower(os.Getenv("VSPHERE_INSECURE")) == "true" {
|
|
cfg.Global.InsecureFlag = true
|
|
}
|
|
return &cfg
|
|
}
|
|
|
|
func GetgovmomiClient(cfg *VSphereConfig) (*govmomi.Client, error) {
|
|
if cfg == nil {
|
|
cfg = getVSphereConfig()
|
|
}
|
|
client, err := newClient(context.TODO(), cfg)
|
|
return client, err
|
|
}
|
|
|
|
// Get placement compatibility result based on storage policy requirements.
|
|
func (vs *VSphere) GetPlacementCompatibilityResult(ctx context.Context, pbmClient *pbm.Client, storagePolicyID string) (pbm.PlacementCompatibilityResult, error) {
|
|
datastores, err := vs.getSharedDatastoresInK8SCluster(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var hubs []pbmtypes.PbmPlacementHub
|
|
for _, ds := range datastores {
|
|
hubs = append(hubs, pbmtypes.PbmPlacementHub{
|
|
HubType: ds.Type,
|
|
HubId: ds.Value,
|
|
})
|
|
}
|
|
req := []pbmtypes.BasePbmPlacementRequirement{
|
|
&pbmtypes.PbmPlacementCapabilityProfileRequirement{
|
|
ProfileId: pbmtypes.PbmProfileId{
|
|
UniqueId: storagePolicyID,
|
|
},
|
|
},
|
|
}
|
|
res, err := pbmClient.CheckRequirements(ctx, hubs, nil, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// Verify if the user specified datastore is in the list of non-compatible datastores.
|
|
// If yes, return the non compatible datastore reference.
|
|
func (vs *VSphere) IsUserSpecifiedDatastoreNonCompatible(ctx context.Context, compatibilityResult pbm.PlacementCompatibilityResult, dsName string) (bool, *types.ManagedObjectReference) {
|
|
dsMoList := vs.GetNonCompatibleDatastoresMo(ctx, compatibilityResult)
|
|
for _, ds := range dsMoList {
|
|
if ds.Info.GetDatastoreInfo().Name == dsName {
|
|
dsMoRef := ds.Reference()
|
|
return true, &dsMoRef
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func GetNonCompatibleDatastoreFaultMsg(compatibilityResult pbm.PlacementCompatibilityResult, dsMoref types.ManagedObjectReference) string {
|
|
var faultMsg string
|
|
for _, res := range compatibilityResult {
|
|
if res.Hub.HubId == dsMoref.Value {
|
|
for _, err := range res.Error {
|
|
faultMsg = faultMsg + err.LocalizedMessage
|
|
}
|
|
}
|
|
}
|
|
return faultMsg
|
|
}
|
|
|
|
// Get the best fit compatible datastore by free space.
|
|
func GetMostFreeDatastore(dsMo []mo.Datastore) mo.Datastore {
|
|
var curMax int64
|
|
curMax = -1
|
|
var index int
|
|
for i, ds := range dsMo {
|
|
dsFreeSpace := ds.Info.GetDatastoreInfo().FreeSpace
|
|
if dsFreeSpace > curMax {
|
|
curMax = dsFreeSpace
|
|
index = i
|
|
}
|
|
}
|
|
return dsMo[index]
|
|
}
|
|
|
|
func (vs *VSphere) GetCompatibleDatastoresMo(ctx context.Context, compatibilityResult pbm.PlacementCompatibilityResult) ([]mo.Datastore, error) {
|
|
compatibleHubs := compatibilityResult.CompatibleDatastores()
|
|
// Return an error if there are no compatible datastores.
|
|
if len(compatibleHubs) < 1 {
|
|
return nil, fmt.Errorf("There are no compatible datastores that satisfy the storage policy requirements")
|
|
}
|
|
dsMoList, err := vs.getDatastoreMo(ctx, compatibleHubs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return dsMoList, nil
|
|
}
|
|
|
|
func (vs *VSphere) GetNonCompatibleDatastoresMo(ctx context.Context, compatibilityResult pbm.PlacementCompatibilityResult) []mo.Datastore {
|
|
nonCompatibleHubs := compatibilityResult.NonCompatibleDatastores()
|
|
// Return an error if there are no compatible datastores.
|
|
if len(nonCompatibleHubs) < 1 {
|
|
return nil
|
|
}
|
|
dsMoList, err := vs.getDatastoreMo(ctx, nonCompatibleHubs)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return dsMoList
|
|
}
|
|
|
|
// Get the datastore managed objects for the place hubs using property collector.
|
|
func (vs *VSphere) getDatastoreMo(ctx context.Context, hubs []pbmtypes.PbmPlacementHub) ([]mo.Datastore, error) {
|
|
var dsMoRefs []types.ManagedObjectReference
|
|
for _, hub := range hubs {
|
|
dsMoRefs = append(dsMoRefs, types.ManagedObjectReference{
|
|
Type: hub.HubType,
|
|
Value: hub.HubId,
|
|
})
|
|
}
|
|
|
|
pc := property.DefaultCollector(vs.client.Client)
|
|
var dsMoList []mo.Datastore
|
|
err := pc.Retrieve(ctx, dsMoRefs, []string{DatastoreInfoProperty}, &dsMoList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return dsMoList, nil
|
|
}
|
|
|
|
// Get all datastores accessible for the virtual machine object.
|
|
func (vs *VSphere) getSharedDatastoresInK8SCluster(ctx context.Context) ([]types.ManagedObjectReference, error) {
|
|
f := find.NewFinder(vs.client.Client, true)
|
|
dc, err := f.Datacenter(ctx, vs.cfg.Global.Datacenter)
|
|
f.SetDatacenter(dc)
|
|
vmFolder, err := f.Folder(ctx, strings.TrimSuffix(vs.cfg.Global.WorkingDir, "/"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
vmMoList, err := vs.GetVMsInsideFolder(ctx, vmFolder, []string{NameProperty})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
index := 0
|
|
var sharedDs []string
|
|
for _, vmMo := range vmMoList {
|
|
if !strings.HasPrefix(vmMo.Name, DummyVMPrefixName) {
|
|
accessibleDatastores, err := vs.getAllAccessibleDatastores(ctx, vmMo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if index == 0 {
|
|
sharedDs = accessibleDatastores
|
|
} else {
|
|
sharedDs = intersect(sharedDs, accessibleDatastores)
|
|
if len(sharedDs) == 0 {
|
|
return nil, fmt.Errorf("No shared datastores found in the Kubernetes cluster")
|
|
}
|
|
}
|
|
index++
|
|
}
|
|
}
|
|
var sharedDSMorefs []types.ManagedObjectReference
|
|
for _, ds := range sharedDs {
|
|
sharedDSMorefs = append(sharedDSMorefs, types.ManagedObjectReference{
|
|
Value: ds,
|
|
Type: "Datastore",
|
|
})
|
|
}
|
|
return sharedDSMorefs, nil
|
|
}
|
|
|
|
func intersect(list1 []string, list2 []string) []string {
|
|
var sharedList []string
|
|
for _, val1 := range list1 {
|
|
// Check if val1 is found in list2
|
|
for _, val2 := range list2 {
|
|
if val1 == val2 {
|
|
sharedList = append(sharedList, val1)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return sharedList
|
|
}
|
|
|
|
// Get the VM list inside a folder.
|
|
func (vs *VSphere) GetVMsInsideFolder(ctx context.Context, vmFolder *object.Folder, properties []string) ([]mo.VirtualMachine, error) {
|
|
vmFolders, err := vmFolder.Children(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pc := property.DefaultCollector(vs.client.Client)
|
|
var vmRefs []types.ManagedObjectReference
|
|
var vmMoList []mo.VirtualMachine
|
|
for _, vmFolder := range vmFolders {
|
|
if vmFolder.Reference().Type == VirtualMachine {
|
|
vmRefs = append(vmRefs, vmFolder.Reference())
|
|
}
|
|
}
|
|
err = pc.Retrieve(ctx, vmRefs, properties, &vmMoList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return vmMoList, nil
|
|
}
|
|
|
|
// Get the datastores accessible for the virtual machine object.
|
|
func (vs *VSphere) getAllAccessibleDatastores(ctx context.Context, vmMo mo.VirtualMachine) ([]string, error) {
|
|
f := find.NewFinder(vs.client.Client, true)
|
|
dc, err := f.Datacenter(ctx, vs.cfg.Global.Datacenter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
f.SetDatacenter(dc)
|
|
vmRegex := vs.cfg.Global.WorkingDir + vmMo.Name
|
|
vmObj, err := f.VirtualMachine(ctx, vmRegex)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
host, err := vmObj.HostSystem(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var hostSystemMo mo.HostSystem
|
|
s := object.NewSearchIndex(vs.client.Client)
|
|
err = s.Properties(ctx, host.Reference(), []string{DatastoreProperty}, &hostSystemMo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var dsRefValues []string
|
|
for _, dsRef := range hostSystemMo.Datastore {
|
|
dsRefValues = append(dsRefValues, dsRef.Value)
|
|
}
|
|
return dsRefValues, nil
|
|
}
|