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