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