mirror of https://github.com/k3s-io/k3s
242 lines
8.4 KiB
Go
242 lines
8.4 KiB
Go
/*
|
|
Copyright 2018 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"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/vmware/govmomi"
|
|
"github.com/vmware/govmomi/find"
|
|
"github.com/vmware/govmomi/object"
|
|
"github.com/vmware/govmomi/vim25/soap"
|
|
"github.com/vmware/govmomi/vim25/types"
|
|
"k8s.io/kubernetes/test/e2e/framework"
|
|
)
|
|
|
|
const (
|
|
VolDir = "kubevols"
|
|
DefaultDiskCapacityKB = 2097152
|
|
DefaultDiskFormat = "thin"
|
|
DefaultSCSIControllerType = "lsiLogic"
|
|
VirtualMachineType = "VirtualMachine"
|
|
)
|
|
|
|
// Represents a vSphere instance where one or more kubernetes nodes are running.
|
|
type VSphere struct {
|
|
Config *Config
|
|
Client *govmomi.Client
|
|
}
|
|
|
|
// VolumeOptions specifies various options for a volume.
|
|
type VolumeOptions struct {
|
|
Name string
|
|
CapacityKB int
|
|
DiskFormat string
|
|
SCSIControllerType string
|
|
Datastore string
|
|
}
|
|
|
|
// GetDatacenter returns the DataCenter Object for the given datacenterPath
|
|
func (vs *VSphere) GetDatacenter(ctx context.Context, datacenterPath string) (*object.Datacenter, error) {
|
|
Connect(ctx, vs)
|
|
finder := find.NewFinder(vs.Client.Client, false)
|
|
return finder.Datacenter(ctx, datacenterPath)
|
|
}
|
|
|
|
// GetDatacenter returns the DataCenter Object for the given datacenterPath
|
|
func (vs *VSphere) GetDatacenterFromObjectReference(ctx context.Context, dc object.Reference) *object.Datacenter {
|
|
Connect(ctx, vs)
|
|
return object.NewDatacenter(vs.Client.Client, dc.Reference())
|
|
}
|
|
|
|
// GetAllDatacenter returns all the DataCenter Objects
|
|
func (vs *VSphere) GetAllDatacenter(ctx context.Context) ([]*object.Datacenter, error) {
|
|
Connect(ctx, vs)
|
|
finder := find.NewFinder(vs.Client.Client, false)
|
|
return finder.DatacenterList(ctx, "*")
|
|
}
|
|
|
|
// GetVMByUUID gets the VM object Reference from the given vmUUID
|
|
func (vs *VSphere) GetVMByUUID(ctx context.Context, vmUUID string, dc object.Reference) (object.Reference, error) {
|
|
Connect(ctx, vs)
|
|
datacenter := vs.GetDatacenterFromObjectReference(ctx, dc)
|
|
s := object.NewSearchIndex(vs.Client.Client)
|
|
vmUUID = strings.ToLower(strings.TrimSpace(vmUUID))
|
|
return s.FindByUuid(ctx, datacenter, vmUUID, true, nil)
|
|
}
|
|
|
|
// GetFolderByPath gets the Folder Object Reference from the given folder path
|
|
// folderPath should be the full path to folder
|
|
func (vs *VSphere) GetFolderByPath(ctx context.Context, dc object.Reference, folderPath string) (vmFolderMor types.ManagedObjectReference, err error) {
|
|
Connect(ctx, vs)
|
|
datacenter := object.NewDatacenter(vs.Client.Client, dc.Reference())
|
|
finder := find.NewFinder(datacenter.Client(), false)
|
|
finder.SetDatacenter(datacenter)
|
|
vmFolder, err := finder.Folder(ctx, folderPath)
|
|
if err != nil {
|
|
framework.Logf("Failed to get the folder reference for %s. err: %+v", folderPath, err)
|
|
return vmFolderMor, err
|
|
}
|
|
return vmFolder.Reference(), nil
|
|
}
|
|
|
|
// CreateVolume creates a vsphere volume using given volume paramemters specified in VolumeOptions.
|
|
// If volume is created successfully the canonical disk path is returned else error is returned.
|
|
func (vs *VSphere) CreateVolume(volumeOptions *VolumeOptions, dataCenterRef types.ManagedObjectReference) (string, error) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
Connect(ctx, vs)
|
|
datacenter := object.NewDatacenter(vs.Client.Client, dataCenterRef)
|
|
var (
|
|
err error
|
|
directoryAlreadyPresent = false
|
|
)
|
|
if datacenter == nil {
|
|
return "", fmt.Errorf("datacenter is nil")
|
|
}
|
|
vs.initVolumeOptions(volumeOptions)
|
|
finder := find.NewFinder(datacenter.Client(), false)
|
|
finder.SetDatacenter(datacenter)
|
|
ds, err := finder.Datastore(ctx, volumeOptions.Datastore)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed while searching for datastore: %s. err: %+v", volumeOptions.Datastore, err)
|
|
}
|
|
directoryPath := filepath.Clean(ds.Path(VolDir)) + "/"
|
|
fileManager := object.NewFileManager(ds.Client())
|
|
err = fileManager.MakeDirectory(ctx, directoryPath, datacenter, false)
|
|
if err != nil {
|
|
if soap.IsSoapFault(err) {
|
|
soapFault := soap.ToSoapFault(err)
|
|
if _, ok := soapFault.VimFault().(types.FileAlreadyExists); ok {
|
|
directoryAlreadyPresent = true
|
|
framework.Logf("Directory with the path %+q is already present", directoryPath)
|
|
}
|
|
}
|
|
if !directoryAlreadyPresent {
|
|
framework.Logf("Cannot create dir %#v. err %s", directoryPath, err)
|
|
return "", err
|
|
}
|
|
}
|
|
framework.Logf("Created dir with path as %+q", directoryPath)
|
|
vmdkPath := directoryPath + volumeOptions.Name + ".vmdk"
|
|
|
|
// Create a virtual disk manager
|
|
vdm := object.NewVirtualDiskManager(ds.Client())
|
|
// Create specification for new virtual disk
|
|
vmDiskSpec := &types.FileBackedVirtualDiskSpec{
|
|
VirtualDiskSpec: types.VirtualDiskSpec{
|
|
AdapterType: volumeOptions.SCSIControllerType,
|
|
DiskType: volumeOptions.DiskFormat,
|
|
},
|
|
CapacityKb: int64(volumeOptions.CapacityKB),
|
|
}
|
|
// Create virtual disk
|
|
task, err := vdm.CreateVirtualDisk(ctx, vmdkPath, datacenter, vmDiskSpec)
|
|
if err != nil {
|
|
framework.Logf("Failed to create virtual disk: %s. err: %+v", vmdkPath, err)
|
|
return "", err
|
|
}
|
|
taskInfo, err := task.WaitForResult(ctx, nil)
|
|
if err != nil {
|
|
framework.Logf("Failed to complete virtual disk creation: %s. err: %+v", vmdkPath, err)
|
|
return "", err
|
|
}
|
|
volumePath := taskInfo.Result.(string)
|
|
canonicalDiskPath, err := getCanonicalVolumePath(ctx, datacenter, volumePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return canonicalDiskPath, nil
|
|
}
|
|
|
|
// DeleteVolume deletes the vmdk file specified in the volumePath.
|
|
// if an error is encountered while deleting volume, error is returned.
|
|
func (vs *VSphere) DeleteVolume(volumePath string, dataCenterRef types.ManagedObjectReference) error {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
Connect(ctx, vs)
|
|
|
|
datacenter := object.NewDatacenter(vs.Client.Client, dataCenterRef)
|
|
virtualDiskManager := object.NewVirtualDiskManager(datacenter.Client())
|
|
diskPath := removeStorageClusterORFolderNameFromVDiskPath(volumePath)
|
|
// Delete virtual disk
|
|
task, err := virtualDiskManager.DeleteVirtualDisk(ctx, diskPath, datacenter)
|
|
if err != nil {
|
|
framework.Logf("Failed to delete virtual disk. err: %v", err)
|
|
return err
|
|
}
|
|
err = task.Wait(ctx)
|
|
if err != nil {
|
|
framework.Logf("Failed to delete virtual disk. err: %v", err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsVMPresent checks if VM with the name specified in the vmName argument, is present in the vCenter inventory.
|
|
// if VM is present, function returns true else false.
|
|
func (vs *VSphere) IsVMPresent(vmName string, dataCenterRef types.ManagedObjectReference) (isVMPresent bool, err error) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
Connect(ctx, vs)
|
|
folderMor, err := vs.GetFolderByPath(ctx, dataCenterRef, vs.Config.Folder)
|
|
if err != nil {
|
|
return
|
|
}
|
|
vmFolder := object.NewFolder(vs.Client.Client, folderMor)
|
|
vmFoldersChildren, err := vmFolder.Children(ctx)
|
|
if err != nil {
|
|
framework.Logf("Failed to get children from Folder: %s. err: %+v", vmFolder.InventoryPath, err)
|
|
return
|
|
}
|
|
for _, vmFoldersChild := range vmFoldersChildren {
|
|
if vmFoldersChild.Reference().Type == VirtualMachineType {
|
|
if object.NewVirtualMachine(vs.Client.Client, vmFoldersChild.Reference()).Name() == vmName {
|
|
return true, nil
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// initVolumeOptions function sets default values for volumeOptions parameters if not set
|
|
func (vs *VSphere) initVolumeOptions(volumeOptions *VolumeOptions) {
|
|
if volumeOptions == nil {
|
|
volumeOptions = &VolumeOptions{}
|
|
}
|
|
if volumeOptions.Datastore == "" {
|
|
volumeOptions.Datastore = vs.Config.DefaultDatastore
|
|
}
|
|
if volumeOptions.CapacityKB == 0 {
|
|
volumeOptions.CapacityKB = DefaultDiskCapacityKB
|
|
}
|
|
if volumeOptions.Name == "" {
|
|
volumeOptions.Name = "e2e-vmdk-" + strconv.FormatInt(time.Now().UnixNano(), 10)
|
|
}
|
|
if volumeOptions.DiskFormat == "" {
|
|
volumeOptions.DiskFormat = DefaultDiskFormat
|
|
}
|
|
if volumeOptions.SCSIControllerType == "" {
|
|
volumeOptions.SCSIControllerType = DefaultSCSIControllerType
|
|
}
|
|
}
|