virtual_machine.go 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081
  1. /*
  2. Copyright (c) 2015-2023 VMware, Inc. All Rights Reserved.
  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 object
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "net"
  19. "path"
  20. "strings"
  21. "github.com/vmware/govmomi/nfc"
  22. "github.com/vmware/govmomi/property"
  23. "github.com/vmware/govmomi/vim25"
  24. "github.com/vmware/govmomi/vim25/methods"
  25. "github.com/vmware/govmomi/vim25/mo"
  26. "github.com/vmware/govmomi/vim25/types"
  27. )
  28. const (
  29. PropRuntimePowerState = "summary.runtime.powerState"
  30. PropConfigTemplate = "summary.config.template"
  31. )
  32. type VirtualMachine struct {
  33. Common
  34. }
  35. // extractDiskLayoutFiles is a helper function used to extract file keys for
  36. // all disk files attached to the virtual machine at the current point of
  37. // running.
  38. func extractDiskLayoutFiles(diskLayoutList []types.VirtualMachineFileLayoutExDiskLayout) []int {
  39. var result []int
  40. for _, layoutExDisk := range diskLayoutList {
  41. for _, link := range layoutExDisk.Chain {
  42. for i := range link.FileKey { // diskDescriptor, diskExtent pairs
  43. result = append(result, int(link.FileKey[i]))
  44. }
  45. }
  46. }
  47. return result
  48. }
  49. // removeKey is a helper function for removing a specific file key from a list
  50. // of keys associated with disks attached to a virtual machine.
  51. func removeKey(l *[]int, key int) {
  52. for i, k := range *l {
  53. if k == key {
  54. *l = append((*l)[:i], (*l)[i+1:]...)
  55. break
  56. }
  57. }
  58. }
  59. func NewVirtualMachine(c *vim25.Client, ref types.ManagedObjectReference) *VirtualMachine {
  60. return &VirtualMachine{
  61. Common: NewCommon(c, ref),
  62. }
  63. }
  64. func (v VirtualMachine) PowerState(ctx context.Context) (types.VirtualMachinePowerState, error) {
  65. var o mo.VirtualMachine
  66. err := v.Properties(ctx, v.Reference(), []string{PropRuntimePowerState}, &o)
  67. if err != nil {
  68. return "", err
  69. }
  70. return o.Summary.Runtime.PowerState, nil
  71. }
  72. func (v VirtualMachine) IsTemplate(ctx context.Context) (bool, error) {
  73. var o mo.VirtualMachine
  74. err := v.Properties(ctx, v.Reference(), []string{PropConfigTemplate}, &o)
  75. if err != nil {
  76. return false, err
  77. }
  78. return o.Summary.Config.Template, nil
  79. }
  80. func (v VirtualMachine) PowerOn(ctx context.Context) (*Task, error) {
  81. req := types.PowerOnVM_Task{
  82. This: v.Reference(),
  83. }
  84. res, err := methods.PowerOnVM_Task(ctx, v.c, &req)
  85. if err != nil {
  86. return nil, err
  87. }
  88. return NewTask(v.c, res.Returnval), nil
  89. }
  90. func (v VirtualMachine) PowerOff(ctx context.Context) (*Task, error) {
  91. req := types.PowerOffVM_Task{
  92. This: v.Reference(),
  93. }
  94. res, err := methods.PowerOffVM_Task(ctx, v.c, &req)
  95. if err != nil {
  96. return nil, err
  97. }
  98. return NewTask(v.c, res.Returnval), nil
  99. }
  100. func (v VirtualMachine) PutUsbScanCodes(ctx context.Context, spec types.UsbScanCodeSpec) (int32, error) {
  101. req := types.PutUsbScanCodes{
  102. This: v.Reference(),
  103. Spec: spec,
  104. }
  105. res, err := methods.PutUsbScanCodes(ctx, v.c, &req)
  106. if err != nil {
  107. return 0, err
  108. }
  109. return res.Returnval, nil
  110. }
  111. func (v VirtualMachine) Reset(ctx context.Context) (*Task, error) {
  112. req := types.ResetVM_Task{
  113. This: v.Reference(),
  114. }
  115. res, err := methods.ResetVM_Task(ctx, v.c, &req)
  116. if err != nil {
  117. return nil, err
  118. }
  119. return NewTask(v.c, res.Returnval), nil
  120. }
  121. func (v VirtualMachine) Suspend(ctx context.Context) (*Task, error) {
  122. req := types.SuspendVM_Task{
  123. This: v.Reference(),
  124. }
  125. res, err := methods.SuspendVM_Task(ctx, v.c, &req)
  126. if err != nil {
  127. return nil, err
  128. }
  129. return NewTask(v.c, res.Returnval), nil
  130. }
  131. func (v VirtualMachine) ShutdownGuest(ctx context.Context) error {
  132. req := types.ShutdownGuest{
  133. This: v.Reference(),
  134. }
  135. _, err := methods.ShutdownGuest(ctx, v.c, &req)
  136. return err
  137. }
  138. func (v VirtualMachine) StandbyGuest(ctx context.Context) error {
  139. req := types.StandbyGuest{
  140. This: v.Reference(),
  141. }
  142. _, err := methods.StandbyGuest(ctx, v.c, &req)
  143. return err
  144. }
  145. func (v VirtualMachine) RebootGuest(ctx context.Context) error {
  146. req := types.RebootGuest{
  147. This: v.Reference(),
  148. }
  149. _, err := methods.RebootGuest(ctx, v.c, &req)
  150. return err
  151. }
  152. func (v VirtualMachine) Destroy(ctx context.Context) (*Task, error) {
  153. req := types.Destroy_Task{
  154. This: v.Reference(),
  155. }
  156. res, err := methods.Destroy_Task(ctx, v.c, &req)
  157. if err != nil {
  158. return nil, err
  159. }
  160. return NewTask(v.c, res.Returnval), nil
  161. }
  162. func (v VirtualMachine) Clone(ctx context.Context, folder *Folder, name string, config types.VirtualMachineCloneSpec) (*Task, error) {
  163. req := types.CloneVM_Task{
  164. This: v.Reference(),
  165. Folder: folder.Reference(),
  166. Name: name,
  167. Spec: config,
  168. }
  169. res, err := methods.CloneVM_Task(ctx, v.c, &req)
  170. if err != nil {
  171. return nil, err
  172. }
  173. return NewTask(v.c, res.Returnval), nil
  174. }
  175. func (v VirtualMachine) InstantClone(ctx context.Context, config types.VirtualMachineInstantCloneSpec) (*Task, error) {
  176. req := types.InstantClone_Task{
  177. This: v.Reference(),
  178. Spec: config,
  179. }
  180. res, err := methods.InstantClone_Task(ctx, v.c, &req)
  181. if err != nil {
  182. return nil, err
  183. }
  184. return NewTask(v.c, res.Returnval), nil
  185. }
  186. func (v VirtualMachine) Customize(ctx context.Context, spec types.CustomizationSpec) (*Task, error) {
  187. req := types.CustomizeVM_Task{
  188. This: v.Reference(),
  189. Spec: spec,
  190. }
  191. res, err := methods.CustomizeVM_Task(ctx, v.c, &req)
  192. if err != nil {
  193. return nil, err
  194. }
  195. return NewTask(v.c, res.Returnval), nil
  196. }
  197. func (v VirtualMachine) Relocate(ctx context.Context, config types.VirtualMachineRelocateSpec, priority types.VirtualMachineMovePriority) (*Task, error) {
  198. req := types.RelocateVM_Task{
  199. This: v.Reference(),
  200. Spec: config,
  201. Priority: priority,
  202. }
  203. res, err := methods.RelocateVM_Task(ctx, v.c, &req)
  204. if err != nil {
  205. return nil, err
  206. }
  207. return NewTask(v.c, res.Returnval), nil
  208. }
  209. func (v VirtualMachine) Reconfigure(ctx context.Context, config types.VirtualMachineConfigSpec) (*Task, error) {
  210. req := types.ReconfigVM_Task{
  211. This: v.Reference(),
  212. Spec: config,
  213. }
  214. res, err := methods.ReconfigVM_Task(ctx, v.c, &req)
  215. if err != nil {
  216. return nil, err
  217. }
  218. return NewTask(v.c, res.Returnval), nil
  219. }
  220. func (v VirtualMachine) RefreshStorageInfo(ctx context.Context) error {
  221. req := types.RefreshStorageInfo{
  222. This: v.Reference(),
  223. }
  224. _, err := methods.RefreshStorageInfo(ctx, v.c, &req)
  225. return err
  226. }
  227. // WaitForIP waits for the VM guest.ipAddress property to report an IP address.
  228. // Waits for an IPv4 address if the v4 param is true.
  229. func (v VirtualMachine) WaitForIP(ctx context.Context, v4 ...bool) (string, error) {
  230. var ip string
  231. p := property.DefaultCollector(v.c)
  232. err := property.Wait(ctx, p, v.Reference(), []string{"guest.ipAddress"}, func(pc []types.PropertyChange) bool {
  233. for _, c := range pc {
  234. if c.Name != "guest.ipAddress" {
  235. continue
  236. }
  237. if c.Op != types.PropertyChangeOpAssign {
  238. continue
  239. }
  240. if c.Val == nil {
  241. continue
  242. }
  243. ip = c.Val.(string)
  244. if len(v4) == 1 && v4[0] {
  245. if net.ParseIP(ip).To4() == nil {
  246. return false
  247. }
  248. }
  249. return true
  250. }
  251. return false
  252. })
  253. if err != nil {
  254. return "", err
  255. }
  256. return ip, nil
  257. }
  258. // WaitForNetIP waits for the VM guest.net property to report an IP address for all VM NICs.
  259. // Only consider IPv4 addresses if the v4 param is true.
  260. // By default, wait for all NICs to get an IP address, unless 1 or more device is given.
  261. // A device can be specified by the MAC address or the device name, e.g. "ethernet-0".
  262. // Returns a map with MAC address as the key and IP address list as the value.
  263. func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...string) (map[string][]string, error) {
  264. macs := make(map[string][]string)
  265. eths := make(map[string]string)
  266. p := property.DefaultCollector(v.c)
  267. // Wait for all NICs to have a MacAddress, which may not be generated yet.
  268. err := property.Wait(ctx, p, v.Reference(), []string{"config.hardware.device"}, func(pc []types.PropertyChange) bool {
  269. for _, c := range pc {
  270. if c.Op != types.PropertyChangeOpAssign {
  271. continue
  272. }
  273. devices := VirtualDeviceList(c.Val.(types.ArrayOfVirtualDevice).VirtualDevice)
  274. for _, d := range devices {
  275. if nic, ok := d.(types.BaseVirtualEthernetCard); ok {
  276. // Convert to lower so that e.g. 00:50:56:83:3A:5D is treated the
  277. // same as 00:50:56:83:3a:5d
  278. mac := strings.ToLower(nic.GetVirtualEthernetCard().MacAddress)
  279. if mac == "" {
  280. return false
  281. }
  282. macs[mac] = nil
  283. eths[devices.Name(d)] = mac
  284. }
  285. }
  286. }
  287. return true
  288. })
  289. if err != nil {
  290. return nil, err
  291. }
  292. if len(device) != 0 {
  293. // Only wait for specific NIC(s)
  294. macs = make(map[string][]string)
  295. for _, mac := range device {
  296. if eth, ok := eths[mac]; ok {
  297. mac = eth // device name, e.g. "ethernet-0"
  298. }
  299. macs[mac] = nil
  300. }
  301. }
  302. err = property.Wait(ctx, p, v.Reference(), []string{"guest.net"}, func(pc []types.PropertyChange) bool {
  303. for _, c := range pc {
  304. if c.Op != types.PropertyChangeOpAssign {
  305. continue
  306. }
  307. nics := c.Val.(types.ArrayOfGuestNicInfo).GuestNicInfo
  308. for _, nic := range nics {
  309. // Convert to lower so that e.g. 00:50:56:83:3A:5D is treated the
  310. // same as 00:50:56:83:3a:5d
  311. mac := strings.ToLower(nic.MacAddress)
  312. if mac == "" || nic.IpConfig == nil {
  313. continue
  314. }
  315. for _, ip := range nic.IpConfig.IpAddress {
  316. if _, ok := macs[mac]; !ok {
  317. continue // Ignore any that don't correspond to a VM device
  318. }
  319. if v4 && net.ParseIP(ip.IpAddress).To4() == nil {
  320. continue // Ignore non IPv4 address
  321. }
  322. macs[mac] = append(macs[mac], ip.IpAddress)
  323. }
  324. }
  325. }
  326. for _, ips := range macs {
  327. if len(ips) == 0 {
  328. return false
  329. }
  330. }
  331. return true
  332. })
  333. if err != nil {
  334. return nil, err
  335. }
  336. return macs, nil
  337. }
  338. // Device returns the VirtualMachine's config.hardware.device property.
  339. func (v VirtualMachine) Device(ctx context.Context) (VirtualDeviceList, error) {
  340. var o mo.VirtualMachine
  341. err := v.Properties(ctx, v.Reference(), []string{"config.hardware.device", "summary.runtime.connectionState"}, &o)
  342. if err != nil {
  343. return nil, err
  344. }
  345. // Quoting the SDK doc:
  346. // The virtual machine configuration is not guaranteed to be available.
  347. // For example, the configuration information would be unavailable if the server
  348. // is unable to access the virtual machine files on disk, and is often also unavailable
  349. // during the initial phases of virtual machine creation.
  350. if o.Config == nil {
  351. return nil, fmt.Errorf("%s Config is not available, connectionState=%s",
  352. v.Reference(), o.Summary.Runtime.ConnectionState)
  353. }
  354. return VirtualDeviceList(o.Config.Hardware.Device), nil
  355. }
  356. func (v VirtualMachine) EnvironmentBrowser(ctx context.Context) (*EnvironmentBrowser, error) {
  357. var vm mo.VirtualMachine
  358. err := v.Properties(ctx, v.Reference(), []string{"environmentBrowser"}, &vm)
  359. if err != nil {
  360. return nil, err
  361. }
  362. return NewEnvironmentBrowser(v.c, vm.EnvironmentBrowser), nil
  363. }
  364. func (v VirtualMachine) HostSystem(ctx context.Context) (*HostSystem, error) {
  365. var o mo.VirtualMachine
  366. err := v.Properties(ctx, v.Reference(), []string{"summary.runtime.host"}, &o)
  367. if err != nil {
  368. return nil, err
  369. }
  370. host := o.Summary.Runtime.Host
  371. if host == nil {
  372. return nil, errors.New("VM doesn't have a HostSystem")
  373. }
  374. return NewHostSystem(v.c, *host), nil
  375. }
  376. func (v VirtualMachine) ResourcePool(ctx context.Context) (*ResourcePool, error) {
  377. var o mo.VirtualMachine
  378. err := v.Properties(ctx, v.Reference(), []string{"resourcePool"}, &o)
  379. if err != nil {
  380. return nil, err
  381. }
  382. rp := o.ResourcePool
  383. if rp == nil {
  384. return nil, errors.New("VM doesn't have a resourcePool")
  385. }
  386. return NewResourcePool(v.c, *rp), nil
  387. }
  388. func diskFileOperation(op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, device types.BaseVirtualDevice) types.VirtualDeviceConfigSpecFileOperation {
  389. if disk, ok := device.(*types.VirtualDisk); ok {
  390. // Special case to attach an existing disk
  391. if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 && disk.CapacityInBytes == 0 {
  392. childDisk := false
  393. if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
  394. childDisk = b.Parent != nil
  395. }
  396. if !childDisk {
  397. fop = "" // existing disk
  398. }
  399. }
  400. return fop
  401. }
  402. return ""
  403. }
  404. func (v VirtualMachine) configureDevice(ctx context.Context, op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, devices ...types.BaseVirtualDevice) error {
  405. spec := types.VirtualMachineConfigSpec{}
  406. for _, device := range devices {
  407. config := &types.VirtualDeviceConfigSpec{
  408. Device: device,
  409. Operation: op,
  410. FileOperation: diskFileOperation(op, fop, device),
  411. }
  412. spec.DeviceChange = append(spec.DeviceChange, config)
  413. }
  414. task, err := v.Reconfigure(ctx, spec)
  415. if err != nil {
  416. return err
  417. }
  418. return task.Wait(ctx)
  419. }
  420. // AddDevice adds the given devices to the VirtualMachine
  421. func (v VirtualMachine) AddDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
  422. return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationAdd, types.VirtualDeviceConfigSpecFileOperationCreate, device...)
  423. }
  424. // EditDevice edits the given (existing) devices on the VirtualMachine
  425. func (v VirtualMachine) EditDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
  426. return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationEdit, types.VirtualDeviceConfigSpecFileOperationReplace, device...)
  427. }
  428. // RemoveDevice removes the given devices on the VirtualMachine
  429. func (v VirtualMachine) RemoveDevice(ctx context.Context, keepFiles bool, device ...types.BaseVirtualDevice) error {
  430. fop := types.VirtualDeviceConfigSpecFileOperationDestroy
  431. if keepFiles {
  432. fop = ""
  433. }
  434. return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationRemove, fop, device...)
  435. }
  436. // AttachDisk attaches the given disk to the VirtualMachine
  437. func (v VirtualMachine) AttachDisk(ctx context.Context, id string, datastore *Datastore, controllerKey int32, unitNumber int32) error {
  438. req := types.AttachDisk_Task{
  439. This: v.Reference(),
  440. DiskId: types.ID{Id: id},
  441. Datastore: datastore.Reference(),
  442. ControllerKey: controllerKey,
  443. UnitNumber: &unitNumber,
  444. }
  445. res, err := methods.AttachDisk_Task(ctx, v.c, &req)
  446. if err != nil {
  447. return err
  448. }
  449. task := NewTask(v.c, res.Returnval)
  450. return task.Wait(ctx)
  451. }
  452. // DetachDisk detaches the given disk from the VirtualMachine
  453. func (v VirtualMachine) DetachDisk(ctx context.Context, id string) error {
  454. req := types.DetachDisk_Task{
  455. This: v.Reference(),
  456. DiskId: types.ID{Id: id},
  457. }
  458. res, err := methods.DetachDisk_Task(ctx, v.c, &req)
  459. if err != nil {
  460. return err
  461. }
  462. task := NewTask(v.c, res.Returnval)
  463. return task.Wait(ctx)
  464. }
  465. // BootOptions returns the VirtualMachine's config.bootOptions property.
  466. func (v VirtualMachine) BootOptions(ctx context.Context) (*types.VirtualMachineBootOptions, error) {
  467. var o mo.VirtualMachine
  468. err := v.Properties(ctx, v.Reference(), []string{"config.bootOptions"}, &o)
  469. if err != nil {
  470. return nil, err
  471. }
  472. return o.Config.BootOptions, nil
  473. }
  474. // SetBootOptions reconfigures the VirtualMachine with the given options.
  475. func (v VirtualMachine) SetBootOptions(ctx context.Context, options *types.VirtualMachineBootOptions) error {
  476. spec := types.VirtualMachineConfigSpec{}
  477. spec.BootOptions = options
  478. task, err := v.Reconfigure(ctx, spec)
  479. if err != nil {
  480. return err
  481. }
  482. return task.Wait(ctx)
  483. }
  484. // Answer answers a pending question.
  485. func (v VirtualMachine) Answer(ctx context.Context, id, answer string) error {
  486. req := types.AnswerVM{
  487. This: v.Reference(),
  488. QuestionId: id,
  489. AnswerChoice: answer,
  490. }
  491. _, err := methods.AnswerVM(ctx, v.c, &req)
  492. if err != nil {
  493. return err
  494. }
  495. return nil
  496. }
  497. func (v VirtualMachine) AcquireTicket(ctx context.Context, kind string) (*types.VirtualMachineTicket, error) {
  498. req := types.AcquireTicket{
  499. This: v.Reference(),
  500. TicketType: kind,
  501. }
  502. res, err := methods.AcquireTicket(ctx, v.c, &req)
  503. if err != nil {
  504. return nil, err
  505. }
  506. return &res.Returnval, nil
  507. }
  508. // CreateSnapshot creates a new snapshot of a virtual machine.
  509. func (v VirtualMachine) CreateSnapshot(ctx context.Context, name string, description string, memory bool, quiesce bool) (*Task, error) {
  510. req := types.CreateSnapshot_Task{
  511. This: v.Reference(),
  512. Name: name,
  513. Description: description,
  514. Memory: memory,
  515. Quiesce: quiesce,
  516. }
  517. res, err := methods.CreateSnapshot_Task(ctx, v.c, &req)
  518. if err != nil {
  519. return nil, err
  520. }
  521. return NewTask(v.c, res.Returnval), nil
  522. }
  523. // RemoveAllSnapshot removes all snapshots of a virtual machine
  524. func (v VirtualMachine) RemoveAllSnapshot(ctx context.Context, consolidate *bool) (*Task, error) {
  525. req := types.RemoveAllSnapshots_Task{
  526. This: v.Reference(),
  527. Consolidate: consolidate,
  528. }
  529. res, err := methods.RemoveAllSnapshots_Task(ctx, v.c, &req)
  530. if err != nil {
  531. return nil, err
  532. }
  533. return NewTask(v.c, res.Returnval), nil
  534. }
  535. type snapshotMap map[string][]types.ManagedObjectReference
  536. func (m snapshotMap) add(parent string, tree []types.VirtualMachineSnapshotTree) {
  537. for i, st := range tree {
  538. sname := st.Name
  539. names := []string{sname, st.Snapshot.Value}
  540. if parent != "" {
  541. sname = path.Join(parent, sname)
  542. // Add full path as an option to resolve duplicate names
  543. names = append(names, sname)
  544. }
  545. for _, name := range names {
  546. m[name] = append(m[name], tree[i].Snapshot)
  547. }
  548. m.add(sname, st.ChildSnapshotList)
  549. }
  550. }
  551. // SnapshotSize calculates the size of a given snapshot in bytes. If the
  552. // snapshot is current, disk files not associated with any parent snapshot are
  553. // included in size calculations. This allows for measuring and including the
  554. // growth from the last fixed snapshot to the present state.
  555. func SnapshotSize(info types.ManagedObjectReference, parent *types.ManagedObjectReference, vmlayout *types.VirtualMachineFileLayoutEx, isCurrent bool) int {
  556. var fileKeyList []int
  557. var parentFiles []int
  558. var allSnapshotFiles []int
  559. diskFiles := extractDiskLayoutFiles(vmlayout.Disk)
  560. for _, layout := range vmlayout.Snapshot {
  561. diskLayout := extractDiskLayoutFiles(layout.Disk)
  562. allSnapshotFiles = append(allSnapshotFiles, diskLayout...)
  563. if layout.Key.Value == info.Value {
  564. fileKeyList = append(fileKeyList, int(layout.DataKey)) // The .vmsn file
  565. fileKeyList = append(fileKeyList, diskLayout...) // The .vmdk files
  566. } else if parent != nil && layout.Key.Value == parent.Value {
  567. parentFiles = append(parentFiles, diskLayout...)
  568. }
  569. }
  570. for _, parentFile := range parentFiles {
  571. removeKey(&fileKeyList, parentFile)
  572. }
  573. for _, file := range allSnapshotFiles {
  574. removeKey(&diskFiles, file)
  575. }
  576. fileKeyMap := make(map[int]types.VirtualMachineFileLayoutExFileInfo)
  577. for _, file := range vmlayout.File {
  578. fileKeyMap[int(file.Key)] = file
  579. }
  580. size := 0
  581. for _, fileKey := range fileKeyList {
  582. file := fileKeyMap[fileKey]
  583. if parent != nil ||
  584. (file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskDescriptor) &&
  585. file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskExtent)) {
  586. size += int(file.Size)
  587. }
  588. }
  589. if isCurrent {
  590. for _, diskFile := range diskFiles {
  591. file := fileKeyMap[diskFile]
  592. size += int(file.Size)
  593. }
  594. }
  595. return size
  596. }
  597. // FindSnapshot supports snapshot lookup by name, where name can be:
  598. // 1) snapshot ManagedObjectReference.Value (unique)
  599. // 2) snapshot name (may not be unique)
  600. // 3) snapshot tree path (may not be unique)
  601. func (v VirtualMachine) FindSnapshot(ctx context.Context, name string) (*types.ManagedObjectReference, error) {
  602. var o mo.VirtualMachine
  603. err := v.Properties(ctx, v.Reference(), []string{"snapshot"}, &o)
  604. if err != nil {
  605. return nil, err
  606. }
  607. if o.Snapshot == nil || len(o.Snapshot.RootSnapshotList) == 0 {
  608. return nil, errors.New("no snapshots for this VM")
  609. }
  610. m := make(snapshotMap)
  611. m.add("", o.Snapshot.RootSnapshotList)
  612. s := m[name]
  613. switch len(s) {
  614. case 0:
  615. return nil, fmt.Errorf("snapshot %q not found", name)
  616. case 1:
  617. return &s[0], nil
  618. default:
  619. return nil, fmt.Errorf("%q resolves to %d snapshots", name, len(s))
  620. }
  621. }
  622. // RemoveSnapshot removes a named snapshot
  623. func (v VirtualMachine) RemoveSnapshot(ctx context.Context, name string, removeChildren bool, consolidate *bool) (*Task, error) {
  624. snapshot, err := v.FindSnapshot(ctx, name)
  625. if err != nil {
  626. return nil, err
  627. }
  628. req := types.RemoveSnapshot_Task{
  629. This: snapshot.Reference(),
  630. RemoveChildren: removeChildren,
  631. Consolidate: consolidate,
  632. }
  633. res, err := methods.RemoveSnapshot_Task(ctx, v.c, &req)
  634. if err != nil {
  635. return nil, err
  636. }
  637. return NewTask(v.c, res.Returnval), nil
  638. }
  639. // RevertToCurrentSnapshot reverts to the current snapshot
  640. func (v VirtualMachine) RevertToCurrentSnapshot(ctx context.Context, suppressPowerOn bool) (*Task, error) {
  641. req := types.RevertToCurrentSnapshot_Task{
  642. This: v.Reference(),
  643. SuppressPowerOn: types.NewBool(suppressPowerOn),
  644. }
  645. res, err := methods.RevertToCurrentSnapshot_Task(ctx, v.c, &req)
  646. if err != nil {
  647. return nil, err
  648. }
  649. return NewTask(v.c, res.Returnval), nil
  650. }
  651. // RevertToSnapshot reverts to a named snapshot
  652. func (v VirtualMachine) RevertToSnapshot(ctx context.Context, name string, suppressPowerOn bool) (*Task, error) {
  653. snapshot, err := v.FindSnapshot(ctx, name)
  654. if err != nil {
  655. return nil, err
  656. }
  657. req := types.RevertToSnapshot_Task{
  658. This: snapshot.Reference(),
  659. SuppressPowerOn: types.NewBool(suppressPowerOn),
  660. }
  661. res, err := methods.RevertToSnapshot_Task(ctx, v.c, &req)
  662. if err != nil {
  663. return nil, err
  664. }
  665. return NewTask(v.c, res.Returnval), nil
  666. }
  667. // IsToolsRunning returns true if VMware Tools is currently running in the guest OS, and false otherwise.
  668. func (v VirtualMachine) IsToolsRunning(ctx context.Context) (bool, error) {
  669. var o mo.VirtualMachine
  670. err := v.Properties(ctx, v.Reference(), []string{"guest.toolsRunningStatus"}, &o)
  671. if err != nil {
  672. return false, err
  673. }
  674. return o.Guest.ToolsRunningStatus == string(types.VirtualMachineToolsRunningStatusGuestToolsRunning), nil
  675. }
  676. // Wait for the VirtualMachine to change to the desired power state.
  677. func (v VirtualMachine) WaitForPowerState(ctx context.Context, state types.VirtualMachinePowerState) error {
  678. p := property.DefaultCollector(v.c)
  679. err := property.Wait(ctx, p, v.Reference(), []string{PropRuntimePowerState}, func(pc []types.PropertyChange) bool {
  680. for _, c := range pc {
  681. if c.Name != PropRuntimePowerState {
  682. continue
  683. }
  684. if c.Val == nil {
  685. continue
  686. }
  687. ps := c.Val.(types.VirtualMachinePowerState)
  688. if ps == state {
  689. return true
  690. }
  691. }
  692. return false
  693. })
  694. return err
  695. }
  696. func (v VirtualMachine) MarkAsTemplate(ctx context.Context) error {
  697. req := types.MarkAsTemplate{
  698. This: v.Reference(),
  699. }
  700. _, err := methods.MarkAsTemplate(ctx, v.c, &req)
  701. if err != nil {
  702. return err
  703. }
  704. return nil
  705. }
  706. func (v VirtualMachine) MarkAsVirtualMachine(ctx context.Context, pool ResourcePool, host *HostSystem) error {
  707. req := types.MarkAsVirtualMachine{
  708. This: v.Reference(),
  709. Pool: pool.Reference(),
  710. }
  711. if host != nil {
  712. ref := host.Reference()
  713. req.Host = &ref
  714. }
  715. _, err := methods.MarkAsVirtualMachine(ctx, v.c, &req)
  716. if err != nil {
  717. return err
  718. }
  719. return nil
  720. }
  721. func (v VirtualMachine) Migrate(ctx context.Context, pool *ResourcePool, host *HostSystem, priority types.VirtualMachineMovePriority, state types.VirtualMachinePowerState) (*Task, error) {
  722. req := types.MigrateVM_Task{
  723. This: v.Reference(),
  724. Priority: priority,
  725. State: state,
  726. }
  727. if pool != nil {
  728. ref := pool.Reference()
  729. req.Pool = &ref
  730. }
  731. if host != nil {
  732. ref := host.Reference()
  733. req.Host = &ref
  734. }
  735. res, err := methods.MigrateVM_Task(ctx, v.c, &req)
  736. if err != nil {
  737. return nil, err
  738. }
  739. return NewTask(v.c, res.Returnval), nil
  740. }
  741. func (v VirtualMachine) Unregister(ctx context.Context) error {
  742. req := types.UnregisterVM{
  743. This: v.Reference(),
  744. }
  745. _, err := methods.UnregisterVM(ctx, v.Client(), &req)
  746. return err
  747. }
  748. func (v VirtualMachine) MountToolsInstaller(ctx context.Context) error {
  749. req := types.MountToolsInstaller{
  750. This: v.Reference(),
  751. }
  752. _, err := methods.MountToolsInstaller(ctx, v.Client(), &req)
  753. return err
  754. }
  755. func (v VirtualMachine) UnmountToolsInstaller(ctx context.Context) error {
  756. req := types.UnmountToolsInstaller{
  757. This: v.Reference(),
  758. }
  759. _, err := methods.UnmountToolsInstaller(ctx, v.Client(), &req)
  760. return err
  761. }
  762. func (v VirtualMachine) UpgradeTools(ctx context.Context, options string) (*Task, error) {
  763. req := types.UpgradeTools_Task{
  764. This: v.Reference(),
  765. InstallerOptions: options,
  766. }
  767. res, err := methods.UpgradeTools_Task(ctx, v.Client(), &req)
  768. if err != nil {
  769. return nil, err
  770. }
  771. return NewTask(v.c, res.Returnval), nil
  772. }
  773. func (v VirtualMachine) Export(ctx context.Context) (*nfc.Lease, error) {
  774. req := types.ExportVm{
  775. This: v.Reference(),
  776. }
  777. res, err := methods.ExportVm(ctx, v.Client(), &req)
  778. if err != nil {
  779. return nil, err
  780. }
  781. return nfc.NewLease(v.c, res.Returnval), nil
  782. }
  783. func (v VirtualMachine) UpgradeVM(ctx context.Context, version string) (*Task, error) {
  784. req := types.UpgradeVM_Task{
  785. This: v.Reference(),
  786. Version: version,
  787. }
  788. res, err := methods.UpgradeVM_Task(ctx, v.Client(), &req)
  789. if err != nil {
  790. return nil, err
  791. }
  792. return NewTask(v.c, res.Returnval), nil
  793. }
  794. // UUID is a helper to get the UUID of the VirtualMachine managed object.
  795. // This method returns an empty string if an error occurs when retrieving UUID from the VirtualMachine object.
  796. func (v VirtualMachine) UUID(ctx context.Context) string {
  797. var o mo.VirtualMachine
  798. err := v.Properties(ctx, v.Reference(), []string{"config.uuid"}, &o)
  799. if err != nil {
  800. return ""
  801. }
  802. if o.Config != nil {
  803. return o.Config.Uuid
  804. }
  805. return ""
  806. }
  807. func (v VirtualMachine) QueryChangedDiskAreas(ctx context.Context, baseSnapshot, curSnapshot *types.ManagedObjectReference, disk *types.VirtualDisk, offset int64) (types.DiskChangeInfo, error) {
  808. var noChange types.DiskChangeInfo
  809. var err error
  810. if offset > disk.CapacityInBytes {
  811. return noChange, fmt.Errorf("offset is greater than the disk size (%#x and %#x)", offset, disk.CapacityInBytes)
  812. } else if offset == disk.CapacityInBytes {
  813. return types.DiskChangeInfo{StartOffset: offset, Length: 0}, nil
  814. }
  815. var b mo.VirtualMachineSnapshot
  816. err = v.Properties(ctx, baseSnapshot.Reference(), []string{"config.hardware"}, &b)
  817. if err != nil {
  818. return noChange, fmt.Errorf("failed to fetch config.hardware of snapshot %s: %s", baseSnapshot, err)
  819. }
  820. var changeId *string
  821. for _, vd := range b.Config.Hardware.Device {
  822. d := vd.GetVirtualDevice()
  823. if d.Key != disk.Key {
  824. continue
  825. }
  826. // As per VDDK programming guide, these are the four types of disks
  827. // that support CBT, see "Gathering Changed Block Information".
  828. if b, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
  829. changeId = &b.ChangeId
  830. break
  831. }
  832. if b, ok := d.Backing.(*types.VirtualDiskSparseVer2BackingInfo); ok {
  833. changeId = &b.ChangeId
  834. break
  835. }
  836. if b, ok := d.Backing.(*types.VirtualDiskRawDiskMappingVer1BackingInfo); ok {
  837. changeId = &b.ChangeId
  838. break
  839. }
  840. if b, ok := d.Backing.(*types.VirtualDiskRawDiskVer2BackingInfo); ok {
  841. changeId = &b.ChangeId
  842. break
  843. }
  844. return noChange, fmt.Errorf("disk %d has backing info without .ChangeId: %t", disk.Key, d.Backing)
  845. }
  846. if changeId == nil || *changeId == "" {
  847. return noChange, fmt.Errorf("CBT is not enabled on disk %d", disk.Key)
  848. }
  849. req := types.QueryChangedDiskAreas{
  850. This: v.Reference(),
  851. Snapshot: curSnapshot,
  852. DeviceKey: disk.Key,
  853. StartOffset: offset,
  854. ChangeId: *changeId,
  855. }
  856. res, err := methods.QueryChangedDiskAreas(ctx, v.Client(), &req)
  857. if err != nil {
  858. return noChange, err
  859. }
  860. return res.Returnval, nil
  861. }
  862. // ExportSnapshot exports all VMDK-files up to (but not including) a specified snapshot. This
  863. // is useful when exporting a running VM.
  864. func (v *VirtualMachine) ExportSnapshot(ctx context.Context, snapshot *types.ManagedObjectReference) (*nfc.Lease, error) {
  865. req := types.ExportSnapshot{
  866. This: *snapshot,
  867. }
  868. resp, err := methods.ExportSnapshot(ctx, v.Client(), &req)
  869. if err != nil {
  870. return nil, err
  871. }
  872. return nfc.NewLease(v.c, resp.Returnval), nil
  873. }