k8s.io/kubernetes@v1.29.3/test/e2e/storage/vsphere/vsphere.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package vsphere
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"path/filepath"
    23  	"strconv"
    24  	"strings"
    25  	"time"
    26  
    27  	"github.com/vmware/govmomi"
    28  	"github.com/vmware/govmomi/find"
    29  	"github.com/vmware/govmomi/object"
    30  	"github.com/vmware/govmomi/vim25/mo"
    31  	"github.com/vmware/govmomi/vim25/soap"
    32  	"github.com/vmware/govmomi/vim25/types"
    33  	"k8s.io/kubernetes/test/e2e/framework"
    34  )
    35  
    36  const (
    37  	volDir                    = "kubevols"
    38  	defaultDiskCapacityKB     = 2097152
    39  	defaultDiskFormat         = "thin"
    40  	defaultSCSIControllerType = "lsiLogic"
    41  	virtualMachineType        = "VirtualMachine"
    42  )
    43  
    44  // VSphere represents a vSphere instance where one or more kubernetes nodes are running.
    45  type VSphere struct {
    46  	Config *Config
    47  	Client *govmomi.Client
    48  }
    49  
    50  // VolumeOptions specifies various options for a volume.
    51  type VolumeOptions struct {
    52  	Name               string
    53  	CapacityKB         int
    54  	DiskFormat         string
    55  	SCSIControllerType string
    56  	Datastore          string
    57  }
    58  
    59  // GetDatacenter returns the DataCenter Object for the given datacenterPath
    60  func (vs *VSphere) GetDatacenter(ctx context.Context, datacenterPath string) (*object.Datacenter, error) {
    61  	Connect(ctx, vs)
    62  	finder := find.NewFinder(vs.Client.Client, false)
    63  	return finder.Datacenter(ctx, datacenterPath)
    64  }
    65  
    66  // GetDatacenterFromObjectReference returns the DataCenter Object for the given datacenter reference
    67  func (vs *VSphere) GetDatacenterFromObjectReference(ctx context.Context, dc object.Reference) *object.Datacenter {
    68  	Connect(ctx, vs)
    69  	return object.NewDatacenter(vs.Client.Client, dc.Reference())
    70  }
    71  
    72  // GetAllDatacenter returns all the DataCenter Objects
    73  func (vs *VSphere) GetAllDatacenter(ctx context.Context) ([]*object.Datacenter, error) {
    74  	Connect(ctx, vs)
    75  	finder := find.NewFinder(vs.Client.Client, false)
    76  	return finder.DatacenterList(ctx, "*")
    77  }
    78  
    79  // GetVMByUUID returns the VM object Reference from the given vmUUID
    80  func (vs *VSphere) GetVMByUUID(ctx context.Context, vmUUID string, dc object.Reference) (object.Reference, error) {
    81  	Connect(ctx, vs)
    82  	datacenter := vs.GetDatacenterFromObjectReference(ctx, dc)
    83  	s := object.NewSearchIndex(vs.Client.Client)
    84  	vmUUID = strings.ToLower(strings.TrimSpace(vmUUID))
    85  	return s.FindByUuid(ctx, datacenter, vmUUID, true, nil)
    86  }
    87  
    88  // GetHostFromVMReference returns host object reference of the host on which the specified VM resides
    89  func (vs *VSphere) GetHostFromVMReference(ctx context.Context, vm types.ManagedObjectReference) types.ManagedObjectReference {
    90  	Connect(ctx, vs)
    91  	var vmMo mo.VirtualMachine
    92  	vs.Client.RetrieveOne(ctx, vm, []string{"summary.runtime.host"}, &vmMo)
    93  	host := *vmMo.Summary.Runtime.Host
    94  	return host
    95  }
    96  
    97  // GetDatastoresMountedOnHost returns the datastore references of all the datastores mounted on the specified host
    98  func (vs *VSphere) GetDatastoresMountedOnHost(ctx context.Context, host types.ManagedObjectReference) []types.ManagedObjectReference {
    99  	Connect(ctx, vs)
   100  	var hostMo mo.HostSystem
   101  	vs.Client.RetrieveOne(ctx, host, []string{"datastore"}, &hostMo)
   102  	return hostMo.Datastore
   103  }
   104  
   105  // GetDatastoreRefFromName returns the datastore reference of the specified datastore
   106  func (vs *VSphere) GetDatastoreRefFromName(ctx context.Context, dc object.Reference, datastoreName string) (types.ManagedObjectReference, error) {
   107  	Connect(ctx, vs)
   108  	datacenter := object.NewDatacenter(vs.Client.Client, dc.Reference())
   109  	finder := find.NewFinder(vs.Client.Client, false)
   110  	finder.SetDatacenter(datacenter)
   111  	datastore, err := finder.Datastore(ctx, datastoreName)
   112  	return datastore.Reference(), err
   113  }
   114  
   115  // GetFolderByPath gets the Folder Object Reference from the given folder path
   116  // folderPath should be the full path to folder
   117  func (vs *VSphere) GetFolderByPath(ctx context.Context, dc object.Reference, folderPath string) (vmFolderMor types.ManagedObjectReference, err error) {
   118  	Connect(ctx, vs)
   119  	datacenter := object.NewDatacenter(vs.Client.Client, dc.Reference())
   120  	finder := find.NewFinder(datacenter.Client(), false)
   121  	finder.SetDatacenter(datacenter)
   122  	vmFolder, err := finder.Folder(ctx, folderPath)
   123  	if err != nil {
   124  		framework.Logf("Failed to get the folder reference for %s. err: %+v", folderPath, err)
   125  		return vmFolderMor, err
   126  	}
   127  	return vmFolder.Reference(), nil
   128  }
   129  
   130  // CreateVolume creates a vsphere volume using given volume parameters specified in VolumeOptions.
   131  // If volume is created successfully the canonical disk path is returned else error is returned.
   132  func (vs *VSphere) CreateVolume(volumeOptions *VolumeOptions, dataCenterRef types.ManagedObjectReference) (string, error) {
   133  	ctx, cancel := context.WithCancel(context.Background())
   134  	defer cancel()
   135  	Connect(ctx, vs)
   136  	datacenter := object.NewDatacenter(vs.Client.Client, dataCenterRef)
   137  	var (
   138  		err                     error
   139  		directoryAlreadyPresent = false
   140  	)
   141  	if datacenter == nil {
   142  		return "", fmt.Errorf("datacenter is nil")
   143  	}
   144  	vs.initVolumeOptions(volumeOptions)
   145  	finder := find.NewFinder(datacenter.Client(), false)
   146  	finder.SetDatacenter(datacenter)
   147  	ds, err := finder.Datastore(ctx, volumeOptions.Datastore)
   148  	if err != nil {
   149  		return "", fmt.Errorf("Failed while searching for datastore: %s. err: %+v", volumeOptions.Datastore, err)
   150  	}
   151  	directoryPath := filepath.Clean(ds.Path(volDir)) + "/"
   152  	fileManager := object.NewFileManager(ds.Client())
   153  	err = fileManager.MakeDirectory(ctx, directoryPath, datacenter, false)
   154  	if err != nil {
   155  		if soap.IsSoapFault(err) {
   156  			soapFault := soap.ToSoapFault(err)
   157  			if _, ok := soapFault.VimFault().(types.FileAlreadyExists); ok {
   158  				directoryAlreadyPresent = true
   159  				framework.Logf("Directory with the path %+q is already present", directoryPath)
   160  			}
   161  		}
   162  		if !directoryAlreadyPresent {
   163  			framework.Logf("Cannot create dir %#v. err %s", directoryPath, err)
   164  			return "", err
   165  		}
   166  	}
   167  	framework.Logf("Created dir with path as %+q", directoryPath)
   168  	vmdkPath := directoryPath + volumeOptions.Name + ".vmdk"
   169  
   170  	// Create a virtual disk manager
   171  	vdm := object.NewVirtualDiskManager(ds.Client())
   172  	// Create specification for new virtual disk
   173  	vmDiskSpec := &types.FileBackedVirtualDiskSpec{
   174  		VirtualDiskSpec: types.VirtualDiskSpec{
   175  			AdapterType: volumeOptions.SCSIControllerType,
   176  			DiskType:    volumeOptions.DiskFormat,
   177  		},
   178  		CapacityKb: int64(volumeOptions.CapacityKB),
   179  	}
   180  	// Create virtual disk
   181  	task, err := vdm.CreateVirtualDisk(ctx, vmdkPath, datacenter, vmDiskSpec)
   182  	if err != nil {
   183  		framework.Logf("Failed to create virtual disk: %s. err: %+v", vmdkPath, err)
   184  		return "", err
   185  	}
   186  	taskInfo, err := task.WaitForResult(ctx, nil)
   187  	if err != nil {
   188  		framework.Logf("Failed to complete virtual disk creation: %s. err: %+v", vmdkPath, err)
   189  		return "", err
   190  	}
   191  	volumePath := taskInfo.Result.(string)
   192  	canonicalDiskPath, err := getCanonicalVolumePath(ctx, datacenter, volumePath)
   193  	if err != nil {
   194  		return "", err
   195  	}
   196  	return canonicalDiskPath, nil
   197  }
   198  
   199  // DeleteVolume deletes the vmdk file specified in the volumePath.
   200  // if an error is encountered while deleting volume, error is returned.
   201  func (vs *VSphere) DeleteVolume(volumePath string, dataCenterRef types.ManagedObjectReference) error {
   202  	ctx, cancel := context.WithCancel(context.Background())
   203  	defer cancel()
   204  	Connect(ctx, vs)
   205  
   206  	datacenter := object.NewDatacenter(vs.Client.Client, dataCenterRef)
   207  	virtualDiskManager := object.NewVirtualDiskManager(datacenter.Client())
   208  	diskPath := removeStorageClusterORFolderNameFromVDiskPath(volumePath)
   209  	// Delete virtual disk
   210  	task, err := virtualDiskManager.DeleteVirtualDisk(ctx, diskPath, datacenter)
   211  	if err != nil {
   212  		framework.Logf("Failed to delete virtual disk. err: %v", err)
   213  		return err
   214  	}
   215  	err = task.Wait(ctx)
   216  	if err != nil {
   217  		framework.Logf("Failed to delete virtual disk. err: %v", err)
   218  		return err
   219  	}
   220  	return nil
   221  }
   222  
   223  // IsVMPresent checks if VM with the name specified in the vmName argument, is present in the vCenter inventory.
   224  // if VM is present, function returns true else false.
   225  func (vs *VSphere) IsVMPresent(vmName string, dataCenterRef types.ManagedObjectReference) (isVMPresent bool, err error) {
   226  	ctx, cancel := context.WithCancel(context.Background())
   227  	defer cancel()
   228  	Connect(ctx, vs)
   229  	folderMor, err := vs.GetFolderByPath(ctx, dataCenterRef, vs.Config.Folder)
   230  	if err != nil {
   231  		return
   232  	}
   233  	vmFolder := object.NewFolder(vs.Client.Client, folderMor)
   234  	vmFoldersChildren, err := vmFolder.Children(ctx)
   235  	if err != nil {
   236  		framework.Logf("Failed to get children from Folder: %s. err: %+v", vmFolder.InventoryPath, err)
   237  		return
   238  	}
   239  	for _, vmFoldersChild := range vmFoldersChildren {
   240  		if vmFoldersChild.Reference().Type == virtualMachineType {
   241  			if object.NewVirtualMachine(vs.Client.Client, vmFoldersChild.Reference()).Name() == vmName {
   242  				return true, nil
   243  			}
   244  		}
   245  	}
   246  	return
   247  }
   248  
   249  // initVolumeOptions function sets default values for volumeOptions parameters if not set
   250  func (vs *VSphere) initVolumeOptions(volumeOptions *VolumeOptions) {
   251  	if volumeOptions == nil {
   252  		volumeOptions = &VolumeOptions{}
   253  	}
   254  	if volumeOptions.Datastore == "" {
   255  		volumeOptions.Datastore = vs.Config.DefaultDatastore
   256  	}
   257  	if volumeOptions.CapacityKB == 0 {
   258  		volumeOptions.CapacityKB = defaultDiskCapacityKB
   259  	}
   260  	if volumeOptions.Name == "" {
   261  		volumeOptions.Name = "e2e-vmdk-" + strconv.FormatInt(time.Now().UnixNano(), 10)
   262  	}
   263  	if volumeOptions.DiskFormat == "" {
   264  		volumeOptions.DiskFormat = defaultDiskFormat
   265  	}
   266  	if volumeOptions.SCSIControllerType == "" {
   267  		volumeOptions.SCSIControllerType = defaultSCSIControllerType
   268  	}
   269  }