vsphere.go 9.7 KB

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