yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/esxi/virtualmachine.go (about)

     1  // Copyright 2019 Yunion
     2  //
     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  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package esxi
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"reflect"
    21  	"sort"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/vmware/govmomi/nfc"
    26  	"github.com/vmware/govmomi/object"
    27  	"github.com/vmware/govmomi/vim25/methods"
    28  	"github.com/vmware/govmomi/vim25/mo"
    29  	"github.com/vmware/govmomi/vim25/soap"
    30  	"github.com/vmware/govmomi/vim25/types"
    31  
    32  	"yunion.io/x/jsonutils"
    33  	"yunion.io/x/log"
    34  	"yunion.io/x/pkg/errors"
    35  	"yunion.io/x/pkg/util/netutils"
    36  	"yunion.io/x/pkg/util/reflectutils"
    37  	"yunion.io/x/pkg/util/regutils"
    38  	"yunion.io/x/pkg/utils"
    39  
    40  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    41  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    42  	"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
    43  	cloudtypes "yunion.io/x/onecloud/pkg/cloudcommon/types"
    44  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    45  	"yunion.io/x/onecloud/pkg/httperrors"
    46  	"yunion.io/x/cloudmux/pkg/multicloud"
    47  	"yunion.io/x/onecloud/pkg/util/billing"
    48  	"yunion.io/x/onecloud/pkg/util/imagetools"
    49  	"yunion.io/x/onecloud/pkg/util/netutils2"
    50  	"yunion.io/x/onecloud/pkg/util/version"
    51  )
    52  
    53  var (
    54  	vmSummaryProps = []string{"summary.runtime.powerState", "summary.config.uuid", "summary.config.memorySizeMB", "summary.config.numCpu"}
    55  	// vmConfigProps   = []string{"config.template", "config.alternateGuestName", "config.hardware", "config.guestId", "config.guestFullName", "config.firmware", "config.version", "config.createDate"}
    56  	vmGuestProps    = []string{"guest.net", "guest.guestState", "guest.toolsStatus", "guest.toolsRunningStatus", "guest.toolsVersion"}
    57  	vmLayoutExProps = []string{"layoutEx.file"}
    58  )
    59  
    60  var VIRTUAL_MACHINE_PROPS = []string{"name", "parent", "resourcePool", "snapshot", "config"}
    61  
    62  func init() {
    63  	VIRTUAL_MACHINE_PROPS = append(VIRTUAL_MACHINE_PROPS, vmSummaryProps...)
    64  	// VIRTUAL_MACHINE_PROPS = append(VIRTUAL_MACHINE_PROPS, vmConfigProps...)
    65  	VIRTUAL_MACHINE_PROPS = append(VIRTUAL_MACHINE_PROPS, vmGuestProps...)
    66  }
    67  
    68  type SVirtualMachine struct {
    69  	multicloud.SInstanceBase
    70  	multicloud.STagBase
    71  	SManagedObject
    72  
    73  	vnics     []SVirtualNIC
    74  	vdisks    []SVirtualDisk
    75  	vga       SVirtualVGA
    76  	cdroms    []SVirtualCdrom
    77  	devs      map[int32]SVirtualDevice
    78  	ihost     cloudprovider.ICloudHost
    79  	snapshots []SVirtualMachineSnapshot
    80  
    81  	guestIps map[string]string
    82  
    83  	osInfo *imagetools.ImageInfo
    84  }
    85  
    86  type VMFetcher interface {
    87  	FetchNoTemplateVMs() ([]*SVirtualMachine, error)
    88  	FetchTemplateVMs() ([]*SVirtualMachine, error)
    89  	FetchFakeTempateVMs(string) ([]*SVirtualMachine, error)
    90  }
    91  
    92  type byDiskType []SVirtualDisk
    93  
    94  func (d byDiskType) Len() int      { return len(d) }
    95  func (d byDiskType) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
    96  func (d byDiskType) Less(i, j int) bool {
    97  	if d[i].GetDiskType() == api.DISK_TYPE_SYS {
    98  		return true
    99  	}
   100  	return false
   101  }
   102  
   103  func NewVirtualMachine(manager *SESXiClient, vm *mo.VirtualMachine, dc *SDatacenter) *SVirtualMachine {
   104  	svm := &SVirtualMachine{SManagedObject: newManagedObject(manager, vm, dc)}
   105  	err := svm.fetchHardwareInfo()
   106  	if err != nil {
   107  		log.Errorf("NewVirtualMachine: %v", err)
   108  		return nil
   109  	}
   110  	return svm
   111  }
   112  
   113  func (self *SVirtualMachine) GetSecurityGroupIds() ([]string, error) {
   114  	return []string{}, cloudprovider.ErrNotSupported
   115  }
   116  
   117  func (self *SVirtualMachine) GetSysTags() map[string]string {
   118  	meta := map[string]string{}
   119  	meta["datacenter"] = self.GetDatacenterPathString()
   120  	rp, _ := self.getResourcePool()
   121  	if rp != nil {
   122  		rpPath := rp.GetPath()
   123  		rpOffset := -1
   124  		for i := range rpPath {
   125  			if rpPath[i] == "Resources" {
   126  				if i > 0 {
   127  					meta["cluster"] = rpPath[i-1]
   128  					rpOffset = i
   129  				}
   130  			} else if rpOffset >= 0 && i > rpOffset {
   131  				meta[fmt.Sprintf("pool%d", i-rpOffset-1)] = rpPath[i]
   132  			}
   133  		}
   134  	}
   135  	return meta
   136  }
   137  
   138  func (self *SVirtualMachine) getVirtualMachine() *mo.VirtualMachine {
   139  	return self.object.(*mo.VirtualMachine)
   140  }
   141  
   142  func (self *SVirtualMachine) GetGlobalId() string {
   143  	return self.getUuid()
   144  }
   145  
   146  func (self *SVirtualMachine) GetHostname() string {
   147  	return self.GetName()
   148  }
   149  
   150  func (self *SVirtualMachine) GetStatus() string {
   151  	// err := self.CheckFileInfo(context.Background())
   152  	// if err != nil {
   153  	// 	return api.VM_UNKNOWN
   154  	// }
   155  	vm := object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self)
   156  	state, err := vm.PowerState(self.manager.context)
   157  	if err != nil {
   158  		return api.VM_UNKNOWN
   159  	}
   160  	switch state {
   161  	case types.VirtualMachinePowerStatePoweredOff:
   162  		return api.VM_READY
   163  	case types.VirtualMachinePowerStatePoweredOn:
   164  		return api.VM_RUNNING
   165  	case types.VirtualMachinePowerStateSuspended:
   166  		return api.VM_SUSPEND
   167  	default:
   168  		return api.VM_UNKNOWN
   169  	}
   170  }
   171  
   172  func (self *SVirtualMachine) Refresh() error {
   173  	base := self.SManagedObject
   174  	var moObj mo.VirtualMachine
   175  	err := self.manager.reference2Object(self.object.Reference(), VIRTUAL_MACHINE_PROPS, &moObj)
   176  	if err != nil {
   177  		return err
   178  	}
   179  	base.object = &moObj
   180  	*self = SVirtualMachine{}
   181  	self.SManagedObject = base
   182  	self.fetchHardwareInfo()
   183  	return nil
   184  }
   185  
   186  func (self *SVirtualMachine) IsEmulated() bool {
   187  	return false
   188  }
   189  
   190  func (self *SVirtualMachine) GetInstanceType() string {
   191  	return ""
   192  }
   193  
   194  func (self *SVirtualMachine) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
   195  	return cloudprovider.ErrNotImplemented
   196  }
   197  
   198  func (self *SVirtualMachine) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
   199  	return "", cloudprovider.ErrNotImplemented
   200  }
   201  
   202  func (self *SVirtualMachine) DoRebuildRoot(ctx context.Context, imagePath string, uuid string) error {
   203  	if len(self.vdisks) == 0 {
   204  		return errors.ErrNotFound
   205  	}
   206  	return self.rebuildDisk(ctx, &self.vdisks[0], imagePath)
   207  }
   208  
   209  func (self *SVirtualMachine) rebuildDisk(ctx context.Context, disk *SVirtualDisk, imagePath string) error {
   210  	uuid := disk.GetId()
   211  	sizeMb := disk.GetDiskSizeMB()
   212  	diskKey := disk.getKey()
   213  	ctlKey := disk.getControllerKey()
   214  	unitNumber := *disk.dev.GetVirtualDevice().UnitNumber
   215  
   216  	err := self.doDetachAndDeleteDisk(ctx, disk)
   217  	if err != nil {
   218  		return err
   219  	}
   220  	return self.createDiskInternal(ctx, SDiskConfig{
   221  		SizeMb:        int64(sizeMb),
   222  		Uuid:          uuid,
   223  		ControllerKey: ctlKey,
   224  		UnitNumber:    unitNumber,
   225  		Key:           diskKey,
   226  		ImagePath:     imagePath,
   227  		IsRoot:        len(imagePath) > 0,
   228  	}, false)
   229  }
   230  
   231  func (self *SVirtualMachine) UpdateVM(ctx context.Context, name string) error {
   232  	return cloudprovider.ErrNotImplemented
   233  }
   234  
   235  // TODO: detach disk to a separate directory, so as to keep disk independent of VM
   236  
   237  func (self *SVirtualMachine) DetachDisk(ctx context.Context, diskId string) error {
   238  	vdisk, err := self.GetIDiskById(diskId)
   239  	if err != nil {
   240  		return err
   241  	}
   242  	return self.doDetachDisk(ctx, vdisk.(*SVirtualDisk), false)
   243  }
   244  
   245  func (self *SVirtualMachine) AttachDisk(ctx context.Context, diskId string) error {
   246  	return cloudprovider.ErrNotImplemented
   247  }
   248  
   249  func (self *SVirtualMachine) getUuid() string {
   250  	return self.getVirtualMachine().Summary.Config.Uuid
   251  }
   252  
   253  func (self *SVirtualMachine) GetIHost() cloudprovider.ICloudHost {
   254  	if self.ihost == nil {
   255  		self.ihost = self.getIHost()
   256  	}
   257  	return self.ihost
   258  }
   259  
   260  func (self *SVirtualMachine) getIHost() cloudprovider.ICloudHost {
   261  	vm := self.getVmObj()
   262  
   263  	hostsys, err := vm.HostSystem(self.manager.context)
   264  	if err != nil {
   265  		log.Errorf("fail to find host system for vm %s", err)
   266  		return nil
   267  	}
   268  	ihost, err := self.manager.FindHostByMoId(moRefId(hostsys.Reference()))
   269  	if err != nil {
   270  		log.Errorf("fail to find host %s for vm %s???", hostsys.Name(), self.GetName())
   271  		return nil
   272  	}
   273  	return ihost
   274  }
   275  
   276  func (self *SVirtualMachine) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
   277  	idisks := make([]cloudprovider.ICloudDisk, len(self.vdisks))
   278  	for i := 0; i < len(self.vdisks); i += 1 {
   279  		idisks[i] = &(self.vdisks[i])
   280  	}
   281  	return idisks, nil
   282  }
   283  
   284  func (self *SVirtualMachine) GetIDiskById(idStr string) (cloudprovider.ICloudDisk, error) {
   285  	for i := 0; i < len(self.vdisks); i += 1 {
   286  		if self.vdisks[i].MatchId(idStr) {
   287  			return &self.vdisks[i], nil
   288  		}
   289  	}
   290  	return nil, cloudprovider.ErrNotFound
   291  }
   292  
   293  func (self *SVirtualMachine) GetINics() ([]cloudprovider.ICloudNic, error) {
   294  	inics := make([]cloudprovider.ICloudNic, len(self.vnics))
   295  	for i := 0; i < len(self.vnics); i += 1 {
   296  		inics[i] = &(self.vnics[i])
   297  	}
   298  	return inics, nil
   299  }
   300  
   301  func (self *SVirtualMachine) GetIEIP() (cloudprovider.ICloudEIP, error) {
   302  	return nil, nil
   303  }
   304  
   305  func (self *SVirtualMachine) GetVcpuCount() int {
   306  	return int(self.getVirtualMachine().Summary.Config.NumCpu)
   307  }
   308  
   309  func (self *SVirtualMachine) GetVmemSizeMB() int {
   310  	return int(self.getVirtualMachine().Summary.Config.MemorySizeMB)
   311  }
   312  
   313  func (self *SVirtualMachine) GetBootOrder() string {
   314  	return "cdn"
   315  }
   316  
   317  func (self *SVirtualMachine) GetVga() string {
   318  	return "vga"
   319  }
   320  
   321  func (self *SVirtualMachine) GetVdi() string {
   322  	return "vmrc"
   323  }
   324  
   325  func (self *SVirtualMachine) GetGuestFamily() string {
   326  	moVM := self.getVirtualMachine()
   327  	return moVM.Config.AlternateGuestName
   328  }
   329  
   330  func (self *SVirtualMachine) GetGuestId() string {
   331  	moVM := self.getVirtualMachine()
   332  	return moVM.Config.GuestId
   333  }
   334  
   335  func (self *SVirtualMachine) GetGuestFullName() string {
   336  	moVM := self.getVirtualMachine()
   337  	return moVM.Config.GuestFullName
   338  }
   339  
   340  func (self *SVirtualMachine) GetGuestState() string {
   341  	moVM := self.getVirtualMachine()
   342  	return moVM.Guest.GuestState
   343  }
   344  
   345  func (self *SVirtualMachine) GetGuestToolsStatus() string {
   346  	moVM := self.getVirtualMachine()
   347  	return string(moVM.Guest.ToolsStatus)
   348  }
   349  
   350  func (self *SVirtualMachine) isToolsOk() bool {
   351  	switch self.getVirtualMachine().Guest.ToolsStatus {
   352  	case types.VirtualMachineToolsStatusToolsNotInstalled:
   353  		return false
   354  	case types.VirtualMachineToolsStatusToolsNotRunning:
   355  		return false
   356  	}
   357  	return true
   358  }
   359  
   360  func (self *SVirtualMachine) GetGuestToolsRunningStatus() string {
   361  	moVM := self.getVirtualMachine()
   362  	return string(moVM.Guest.ToolsRunningStatus)
   363  }
   364  
   365  func (vm *SVirtualMachine) getNormalizedOsInfo() *imagetools.ImageInfo {
   366  	if vm.osInfo == nil {
   367  		if osInfo, ok := GuestOsInfo[vm.GetGuestId()]; ok {
   368  			osInfo := imagetools.NormalizeImageInfo("", string(osInfo.OsArch), string(osInfo.OsType), osInfo.OsDistribution, osInfo.OsVersion)
   369  			vm.osInfo = &osInfo
   370  		} else {
   371  			osInfo := imagetools.NormalizeImageInfo("", "", "", "", "")
   372  			vm.osInfo = &osInfo
   373  		}
   374  	}
   375  	return vm.osInfo
   376  }
   377  
   378  func (vm *SVirtualMachine) GetOsType() cloudprovider.TOsType {
   379  	return cloudprovider.TOsType(vm.getNormalizedOsInfo().OsType)
   380  }
   381  
   382  func (vm *SVirtualMachine) GetFullOsName() string {
   383  	return vm.getNormalizedOsInfo().GetFullOsName()
   384  }
   385  
   386  func (vm *SVirtualMachine) GetOsDist() string {
   387  	return vm.getNormalizedOsInfo().OsDistro
   388  }
   389  
   390  func (vm *SVirtualMachine) GetOsVersion() string {
   391  	return vm.getNormalizedOsInfo().OsVersion
   392  }
   393  
   394  func (vm *SVirtualMachine) GetOsLang() string {
   395  	return vm.getNormalizedOsInfo().OsLang
   396  }
   397  
   398  func (vm *SVirtualMachine) GetOsArch() string {
   399  	return vm.getNormalizedOsInfo().OsArch
   400  }
   401  
   402  func (vm *SVirtualMachine) GetBios() cloudprovider.TBiosType {
   403  	return cloudprovider.ToBiosType(vm.getBios())
   404  }
   405  
   406  func (vm *SVirtualMachine) getBios() string {
   407  	// self.obj.config.firmware
   408  	switch vm.getVirtualMachine().Config.Firmware {
   409  	case "efi":
   410  		return "UEFI"
   411  	case "bios":
   412  		return "BIOS"
   413  	default:
   414  		return "BIOS"
   415  	}
   416  }
   417  
   418  func (self *SVirtualMachine) GetMachine() string {
   419  	return "pc"
   420  }
   421  
   422  func (self *SVirtualMachine) GetHypervisor() string {
   423  	return api.HYPERVISOR_ESXI
   424  }
   425  
   426  func (self *SVirtualMachine) getVmObj() *object.VirtualMachine {
   427  	return object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self)
   428  }
   429  
   430  // ideopotent start
   431  func (self *SVirtualMachine) StartVM(ctx context.Context) error {
   432  	if self.GetStatus() == api.VM_RUNNING {
   433  		return nil
   434  	}
   435  	return self.startVM(ctx)
   436  }
   437  
   438  func (self *SVirtualMachine) startVM(ctx context.Context) error {
   439  	ihost := self.GetIHost()
   440  	if ihost == nil {
   441  		return errors.Wrap(httperrors.ErrInvalidStatus, "no valid host")
   442  	}
   443  
   444  	lockman.LockRawObject(ctx, "host", ihost.GetGlobalId())
   445  	defer lockman.ReleaseRawObject(ctx, "host", ihost.GetGlobalId())
   446  
   447  	err := self.makeNicsStartConnected(ctx)
   448  	if err != nil {
   449  		log.Errorf("self.makeNicsStartConnected %s", err)
   450  		return err
   451  	}
   452  
   453  	vm := self.getVmObj()
   454  
   455  	task, err := vm.PowerOn(ctx)
   456  	if err != nil {
   457  		log.Errorf("vm.PowerOn %s", err)
   458  		return err
   459  	}
   460  	err = task.Wait(ctx)
   461  	if err != nil {
   462  		log.Errorf("task.Wait %s", err)
   463  		return err
   464  	}
   465  	return nil
   466  }
   467  
   468  func (self *SVirtualMachine) makeNicsStartConnected(ctx context.Context) error {
   469  	spec := types.VirtualMachineConfigSpec{}
   470  	spec.DeviceChange = make([]types.BaseVirtualDeviceConfigSpec, len(self.vnics))
   471  	for i := 0; i < len(self.vnics); i += 1 {
   472  		spec.DeviceChange[i] = makeNicStartConnected(&self.vnics[i])
   473  	}
   474  
   475  	vm := self.getVmObj()
   476  
   477  	task, err := vm.Reconfigure(ctx, spec)
   478  	if err != nil {
   479  		return err
   480  	}
   481  	return task.Wait(ctx)
   482  }
   483  
   484  func makeNicStartConnected(nic *SVirtualNIC) *types.VirtualDeviceConfigSpec {
   485  	editSpec := types.VirtualDeviceConfigSpec{}
   486  	editSpec.Operation = types.VirtualDeviceConfigSpecOperationEdit
   487  	editSpec.FileOperation = ""
   488  	editSpec.Device = nic.dev
   489  	editSpec.Device.GetVirtualDevice().Connectable.StartConnected = true
   490  	return &editSpec
   491  }
   492  
   493  func (self *SVirtualMachine) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
   494  	if self.GetStatus() == api.VM_READY {
   495  		return nil
   496  	}
   497  	if !opts.IsForce && self.isToolsOk() {
   498  		return self.shutdownVM(ctx)
   499  	} else {
   500  		return self.poweroffVM(ctx)
   501  	}
   502  }
   503  
   504  func (self *SVirtualMachine) SuspendVM(ctx context.Context) error {
   505  	vm := self.getVmObj()
   506  	task, err := vm.Suspend(ctx)
   507  	if err != nil {
   508  		return err
   509  	}
   510  	return task.Wait(ctx)
   511  }
   512  
   513  func (self *SVirtualMachine) ResumeVM(ctx context.Context) error {
   514  	if self.GetStatus() == api.VM_RUNNING {
   515  		return nil
   516  	}
   517  	vm := self.getVmObj()
   518  
   519  	task, err := vm.PowerOn(ctx)
   520  	if err != nil {
   521  		return errors.Wrap(err, "VM.PowerOn")
   522  	}
   523  	err = task.Wait(ctx)
   524  	if err != nil {
   525  		return errors.Wrap(err, "Task.Wait after VM.PowerOn")
   526  	}
   527  	return nil
   528  }
   529  
   530  func (self *SVirtualMachine) poweroffVM(ctx context.Context) error {
   531  	vm := self.getVmObj()
   532  
   533  	task, err := vm.PowerOff(ctx)
   534  	if err != nil {
   535  		return err
   536  	}
   537  	return task.Wait(ctx)
   538  }
   539  
   540  func (self *SVirtualMachine) shutdownVM(ctx context.Context) error {
   541  	vm := self.getVmObj()
   542  
   543  	err := vm.ShutdownGuest(ctx)
   544  	if err != nil {
   545  		return err
   546  	}
   547  	return err
   548  }
   549  
   550  func (self *SVirtualMachine) doDestroy(ctx context.Context) error {
   551  	vm := self.getVmObj()
   552  	task, err := vm.Destroy(ctx)
   553  	if err != nil {
   554  		return errors.Wrap(err, "unable to destroy vm")
   555  	}
   556  	return task.Wait(ctx)
   557  }
   558  
   559  func (self *SVirtualMachine) doDelete(ctx context.Context) error {
   560  	// detach all disks first
   561  	for i := range self.vdisks {
   562  		err := self.doDetachAndDeleteDisk(ctx, &self.vdisks[i])
   563  		if err != nil {
   564  			return errors.Wrap(err, "doDetachAndDeteteDisk")
   565  		}
   566  	}
   567  
   568  	return self.doDestroy(ctx)
   569  }
   570  
   571  func (self *SVirtualMachine) doUnregister(ctx context.Context) error {
   572  	vm := self.getVmObj()
   573  
   574  	err := vm.Unregister(ctx)
   575  	if err != nil {
   576  		log.Errorf("vm.Unregister(ctx) fail %s", err)
   577  		return err
   578  	}
   579  	return nil
   580  }
   581  
   582  func (self *SVirtualMachine) DeleteVM(ctx context.Context) error {
   583  	err := self.CheckFileInfo(ctx)
   584  	if err != nil {
   585  		return self.doUnregister(ctx)
   586  	}
   587  	return self.doDestroy(ctx)
   588  }
   589  
   590  func (self *SVirtualMachine) doDetachAndDeleteDisk(ctx context.Context, vdisk *SVirtualDisk) error {
   591  	return self.doDetachDisk(ctx, vdisk, true)
   592  }
   593  
   594  func (self *SVirtualMachine) doDetachDisk(ctx context.Context, vdisk *SVirtualDisk, remove bool) error {
   595  	removeSpec := types.VirtualDeviceConfigSpec{}
   596  	removeSpec.Operation = types.VirtualDeviceConfigSpecOperationRemove
   597  	removeSpec.Device = vdisk.dev
   598  
   599  	spec := types.VirtualMachineConfigSpec{}
   600  	spec.DeviceChange = []types.BaseVirtualDeviceConfigSpec{&removeSpec}
   601  
   602  	vm := self.getVmObj()
   603  
   604  	task, err := vm.Reconfigure(ctx, spec)
   605  	if err != nil {
   606  		log.Errorf("vm.Reconfigure fail %s", err)
   607  		return err
   608  	}
   609  
   610  	err = task.Wait(ctx)
   611  	if err != nil {
   612  		log.Errorf("task.Wait(ctx) fail %s", err)
   613  		return err
   614  	}
   615  
   616  	if !remove {
   617  		return nil
   618  	}
   619  	return vdisk.Delete(ctx)
   620  }
   621  
   622  func (self *SVirtualMachine) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
   623  	hostVer := self.GetIHost().GetVersion()
   624  	if version.GE(hostVer, "6.5") {
   625  		info, err := self.acquireWebmksTicket("webmks")
   626  		if err == nil {
   627  			return info, nil
   628  		}
   629  	}
   630  	return self.acquireVmrcUrl()
   631  }
   632  
   633  func (self *SVirtualMachine) GetVmrcInfo() (*cloudprovider.ServerVncOutput, error) {
   634  	return self.acquireVmrcUrl()
   635  }
   636  
   637  func (self *SVirtualMachine) GetWebmksInfo() (*cloudprovider.ServerVncOutput, error) {
   638  	return self.acquireWebmksTicket("webmks")
   639  }
   640  
   641  func (self *SVirtualMachine) acquireWebmksTicket(ticketType string) (*cloudprovider.ServerVncOutput, error) {
   642  	vm := object.NewVirtualMachine(self.manager.client.Client, self.getVirtualMachine().Self)
   643  	ticket, err := vm.AcquireTicket(self.manager.context, ticketType)
   644  	if err != nil {
   645  		return nil, err
   646  	}
   647  
   648  	host := ticket.Host
   649  	if len(host) == 0 {
   650  		host = self.manager.host
   651  	}
   652  	port := ticket.Port
   653  	if port == 0 {
   654  		port = int32(self.manager.port)
   655  	}
   656  	if port == 0 {
   657  		port = 443
   658  	}
   659  	ret := &cloudprovider.ServerVncOutput{
   660  		Url:        fmt.Sprintf("wss://%s:%d/ticket/%s", host, port, ticket.Ticket),
   661  		Protocol:   "wmks",
   662  		Hypervisor: api.HYPERVISOR_ESXI,
   663  	}
   664  	return ret, nil
   665  }
   666  
   667  func (self *SVirtualMachine) acquireVmrcUrl() (*cloudprovider.ServerVncOutput, error) {
   668  	ticket, err := self.manager.acquireCloneTicket()
   669  	if err != nil {
   670  		return nil, err
   671  	}
   672  	port := self.manager.port
   673  	if port == 0 {
   674  		port = 443
   675  	}
   676  	ret := &cloudprovider.ServerVncOutput{
   677  		Url:      fmt.Sprintf("vmrc://clone:%s@%s:%d/?moid=%s", ticket, self.manager.host, port, self.GetId()),
   678  		Protocol: "vmrc",
   679  	}
   680  	return ret, nil
   681  }
   682  
   683  func (self *SVirtualMachine) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
   684  	return self.doChangeConfig(ctx, int32(config.Cpu), int64(config.MemoryMB), "", "")
   685  }
   686  
   687  func (self *SVirtualMachine) GetVersion() string {
   688  	return self.getVirtualMachine().Config.Version
   689  }
   690  
   691  func (self *SVirtualMachine) doChangeConfig(ctx context.Context, ncpu int32, vmemMB int64, guestId string, version string) error {
   692  	changed := false
   693  	configSpec := types.VirtualMachineConfigSpec{}
   694  	if int(ncpu) != self.GetVcpuCount() {
   695  		configSpec.NumCPUs = ncpu
   696  		changed = true
   697  	}
   698  	if int(vmemMB) != self.GetVmemSizeMB() {
   699  		configSpec.MemoryMB = vmemMB
   700  		changed = true
   701  	}
   702  	if len(guestId) > 0 && guestId != self.GetGuestId() {
   703  		configSpec.GuestId = guestId
   704  		changed = true
   705  	}
   706  	if len(version) > 0 && version != self.GetVersion() {
   707  		configSpec.Version = version
   708  		changed = true
   709  	}
   710  	if !changed {
   711  		return nil
   712  	}
   713  
   714  	vm := self.getVmObj()
   715  
   716  	task, err := vm.Reconfigure(ctx, configSpec)
   717  	if err != nil {
   718  		return err
   719  	}
   720  	err = task.Wait(ctx)
   721  	if err != nil {
   722  		return err
   723  	}
   724  	return self.Refresh()
   725  }
   726  
   727  func (self *SVirtualMachine) AssignSecurityGroup(secgroupId string) error {
   728  	return cloudprovider.ErrNotImplemented
   729  }
   730  
   731  func (self *SVirtualMachine) SetSecurityGroups(secgroupIds []string) error {
   732  	return cloudprovider.ErrNotImplemented
   733  }
   734  
   735  func (self *SVirtualMachine) GetBillingType() string {
   736  	return billing_api.BILLING_TYPE_POSTPAID
   737  }
   738  
   739  func (self *SVirtualMachine) GetCreatedAt() time.Time {
   740  	moVM := self.getVirtualMachine()
   741  	ctm := moVM.Config.CreateDate
   742  	if ctm != nil {
   743  		return *ctm
   744  	} else {
   745  		return time.Time{}
   746  	}
   747  }
   748  
   749  func (self *SVirtualMachine) GetExpiredAt() time.Time {
   750  	return time.Time{}
   751  }
   752  
   753  func (self *SVirtualMachine) UpdateUserData(userData string) error {
   754  	return nil
   755  }
   756  
   757  func (self *SVirtualMachine) fetchHardwareInfo() error {
   758  	self.vnics = make([]SVirtualNIC, 0)
   759  	self.vdisks = make([]SVirtualDisk, 0)
   760  	self.cdroms = make([]SVirtualCdrom, 0)
   761  	self.devs = make(map[int32]SVirtualDevice)
   762  
   763  	moVM := self.getVirtualMachine()
   764  
   765  	// MAX_TRIES := 3
   766  	// for tried := 0; tried < MAX_TRIES && (moVM == nil || moVM.Config == nil || moVM.Config.Hardware.Device == nil); tried += 1 {
   767  	// 	time.Sleep(time.Second)
   768  	// }
   769  
   770  	if moVM == nil || moVM.Config == nil || moVM.Config.Hardware.Device == nil {
   771  		return fmt.Errorf("invalid vm")
   772  	}
   773  
   774  	// sort devices via their Key
   775  	devices := moVM.Config.Hardware.Device
   776  	sort.Slice(devices, func(i, j int) bool {
   777  		return devices[i].GetVirtualDevice().Key < devices[j].GetVirtualDevice().Key
   778  	})
   779  	for i := 0; i < len(devices); i += 1 {
   780  		dev := devices[i]
   781  		devType := reflect.Indirect(reflect.ValueOf(dev)).Type()
   782  
   783  		etherType := reflect.TypeOf((*types.VirtualEthernetCard)(nil)).Elem()
   784  		diskType := reflect.TypeOf((*types.VirtualDisk)(nil)).Elem()
   785  		vgaType := reflect.TypeOf((*types.VirtualMachineVideoCard)(nil)).Elem()
   786  		cdromType := reflect.TypeOf((*types.VirtualCdrom)(nil)).Elem()
   787  
   788  		if reflectutils.StructContains(devType, etherType) {
   789  			self.vnics = append(self.vnics, NewVirtualNIC(self, dev, len(self.vnics)))
   790  		} else if reflectutils.StructContains(devType, diskType) {
   791  			self.vdisks = append(self.vdisks, NewVirtualDisk(self, dev, len(self.vdisks)))
   792  		} else if reflectutils.StructContains(devType, vgaType) {
   793  			self.vga = NewVirtualVGA(self, dev, 0)
   794  		} else if reflectutils.StructContains(devType, cdromType) {
   795  			self.cdroms = append(self.cdroms, NewVirtualCdrom(self, dev, len(self.cdroms)))
   796  		}
   797  		vdev := NewVirtualDevice(self, dev, 0)
   798  		self.devs[vdev.getKey()] = vdev
   799  	}
   800  	self.rigorous()
   801  	sort.Sort(byDiskType(self.vdisks))
   802  	return nil
   803  }
   804  
   805  func (self *SVirtualMachine) rigorous() {
   806  	hasRoot := false
   807  	for i := range self.vdisks {
   808  		if self.vdisks[i].IsRoot {
   809  			hasRoot = true
   810  			break
   811  		}
   812  	}
   813  	if !hasRoot && len(self.vdisks) > 0 {
   814  		self.vdisks[0].IsRoot = true
   815  	}
   816  }
   817  
   818  func (self *SVirtualMachine) getVdev(key int32) SVirtualDevice {
   819  	return self.devs[key]
   820  }
   821  
   822  func (self *SVirtualMachine) fetchGuestIps() map[string]string {
   823  	guestIps := make(map[string]string)
   824  	moVM := self.getVirtualMachine()
   825  	for _, net := range moVM.Guest.Net {
   826  		mac := netutils.FormatMacAddr(net.MacAddress)
   827  		for _, ip := range net.IpAddress {
   828  			if regutils.MatchIP4Addr(ip) {
   829  				if !vmIPV4Filter.Contains(ip) {
   830  					continue
   831  				}
   832  				guestIps[mac] = ip
   833  				break
   834  			}
   835  		}
   836  	}
   837  	return guestIps
   838  }
   839  
   840  func (self *SVirtualMachine) getGuestIps() map[string]string {
   841  	if self.guestIps == nil {
   842  		self.guestIps = self.fetchGuestIps()
   843  	}
   844  	return self.guestIps
   845  }
   846  
   847  func (self *SVirtualMachine) GetIps() []string {
   848  	ips := make([]string, 0)
   849  	for _, ip := range self.getGuestIps() {
   850  		ips = append(ips, ip)
   851  	}
   852  	return ips
   853  }
   854  
   855  func (self *SVirtualMachine) GetVGADevice() string {
   856  	return fmt.Sprintf("%s", self.vga.String())
   857  }
   858  
   859  var (
   860  	driverTable = map[string][]string{
   861  		"sata":   {"ahci"},
   862  		"scsi":   {"parascsi", "lsilogic", "lsilogicsas", "buslogic"},
   863  		"pvscsi": {"parascsi"},
   864  		"ide":    {"ide"},
   865  	}
   866  )
   867  
   868  func (self *SVirtualMachine) getDevsByDriver(driver string) []SVirtualDevice {
   869  	devs := make([]SVirtualDevice, 0)
   870  	for _, drv := range self.devs {
   871  		if strings.HasSuffix(drv.GetDriver(), fmt.Sprintf("%scontroller", driver)) {
   872  			devs = append(devs, drv)
   873  		}
   874  	}
   875  	return devs
   876  }
   877  
   878  func minDevKey(devs []SVirtualDevice) int32 {
   879  	var minKey int32 = -1
   880  	for i := 0; i < len(devs); i += 1 {
   881  		if minKey < 0 || minKey > devs[i].getKey() {
   882  			minKey = devs[i].getKey()
   883  		}
   884  	}
   885  	return minKey
   886  }
   887  
   888  func minDiskKey(devs []SVirtualDisk) int32 {
   889  	var minKey int32 = -1
   890  	for i := 0; i < len(devs); i += 1 {
   891  		if minKey < 0 || minKey > devs[i].getKey() {
   892  			minKey = devs[i].getKey()
   893  		}
   894  	}
   895  	return minKey
   896  }
   897  
   898  func (self *SVirtualMachine) FindController(ctx context.Context, driver string) ([]SVirtualDevice, error) {
   899  	aliasDrivers, ok := driverTable[driver]
   900  	if !ok {
   901  		return nil, fmt.Errorf("Unsupported disk driver %s", driver)
   902  	}
   903  	var devs []SVirtualDevice
   904  	for _, alias := range aliasDrivers {
   905  		devs = self.getDevsByDriver(alias)
   906  		if len(devs) > 0 {
   907  			break
   908  		}
   909  	}
   910  	return devs, nil
   911  }
   912  
   913  func (self *SVirtualMachine) FindDiskByDriver(drivers ...string) []SVirtualDisk {
   914  	disks := make([]SVirtualDisk, 0)
   915  	for i := range self.vdisks {
   916  		if utils.IsInStringArray(self.vdisks[i].GetDriver(), drivers) {
   917  			disks = append(disks, self.vdisks[i])
   918  		}
   919  	}
   920  	return disks
   921  }
   922  
   923  func (self *SVirtualMachine) devNumWithCtrlKey(ctrlKey int32) int {
   924  	n := 0
   925  	for _, dev := range self.devs {
   926  		if dev.getControllerKey() == ctrlKey {
   927  			n++
   928  		}
   929  	}
   930  	return n
   931  }
   932  
   933  func (self *SVirtualMachine) getLayoutEx() *types.VirtualMachineFileLayoutEx {
   934  	vm := self.getVirtualMachine()
   935  	if vm.LayoutEx != nil {
   936  		return vm.LayoutEx
   937  	}
   938  	var nvm mo.VirtualMachine
   939  	err := self.manager.reference2Object(vm.Self, vmLayoutExProps, &nvm)
   940  	if err != nil {
   941  		log.Errorf("unable to fetch LayoutEx.File from vc: %v", err)
   942  	}
   943  	vm.LayoutEx = nvm.LayoutEx
   944  	return vm.LayoutEx
   945  }
   946  
   947  func (self *SVirtualMachine) CreateDisk(ctx context.Context, opts *cloudprovider.GuestDiskCreateOptions) (string, error) {
   948  	if opts.Driver == "pvscsi" {
   949  		opts.Driver = "scsi"
   950  	}
   951  	var ds *SDatastore
   952  	var err error
   953  	if opts.StorageId != "" {
   954  		ihost := self.getIHost()
   955  		if ihost == nil {
   956  			return "", fmt.Errorf("unable to get host of virtualmachine %s", self.GetName())
   957  		}
   958  		ds, err = ihost.(*SHost).FindDataStoreById(opts.StorageId)
   959  		if err != nil {
   960  			return "", errors.Wrapf(err, "unable to find datastore %s", opts.StorageId)
   961  		}
   962  	}
   963  	devs, err := self.FindController(ctx, opts.Driver)
   964  	if err != nil {
   965  		return "", err
   966  	}
   967  	if len(devs) == 0 {
   968  		return "", self.createDriverAndDisk(ctx, ds, opts.SizeMb, opts.UUID, opts.Driver)
   969  	}
   970  	numDevBelowCtrl := make([]int, len(devs))
   971  	for i := range numDevBelowCtrl {
   972  		numDevBelowCtrl[i] = self.devNumWithCtrlKey(devs[i].getKey())
   973  	}
   974  
   975  	// find the min one
   976  	ctrlKey := devs[0].getKey()
   977  	unitNumber := numDevBelowCtrl[0]
   978  	for i := 1; i < len(numDevBelowCtrl); i++ {
   979  		if numDevBelowCtrl[i] >= unitNumber {
   980  			continue
   981  		}
   982  		ctrlKey = devs[i].getKey()
   983  		unitNumber = numDevBelowCtrl[i]
   984  	}
   985  	diskKey := self.FindMinDiffKey(2000)
   986  
   987  	// By default, the virtual SCSI controller is assigned to virtual device node (z:7),
   988  	// so that device node is unavailable for hard disks or other devices.
   989  	if unitNumber >= 7 && opts.Driver == "scsi" {
   990  		unitNumber++
   991  	}
   992  
   993  	return "", self.createDiskInternal(ctx, SDiskConfig{
   994  		SizeMb:        int64(opts.SizeMb),
   995  		Uuid:          opts.UUID,
   996  		UnitNumber:    int32(unitNumber),
   997  		ControllerKey: ctrlKey,
   998  		Key:           diskKey,
   999  		Datastore:     ds,
  1000  	}, true)
  1001  }
  1002  
  1003  // createDriverAndDisk will create a driver and disk associated with the driver
  1004  func (self *SVirtualMachine) createDriverAndDisk(ctx context.Context, ds *SDatastore, sizeMb int, uuid string, driver string) error {
  1005  	if driver != "scsi" && driver != "pvscsi" {
  1006  		return fmt.Errorf("Driver %s is not supported", driver)
  1007  	}
  1008  
  1009  	deviceChange := make([]types.BaseVirtualDeviceConfigSpec, 0, 2)
  1010  
  1011  	// find a suitable key for scsi or pvscsi driver
  1012  	scsiKey := self.FindMinDiffKey(1000)
  1013  	deviceChange = append(deviceChange, addDevSpec(NewSCSIDev(scsiKey, 100, driver)))
  1014  
  1015  	// find a suitable key for disk
  1016  	diskKey := self.FindMinDiffKey(2000)
  1017  
  1018  	if diskKey == scsiKey {
  1019  		// unarrivelable
  1020  		log.Errorf("there is no suitable key between 1000 and 2000???!")
  1021  	}
  1022  
  1023  	return self.createDiskWithDeviceChange(ctx, deviceChange,
  1024  		SDiskConfig{
  1025  			SizeMb:        int64(sizeMb),
  1026  			Uuid:          uuid,
  1027  			ControllerKey: scsiKey,
  1028  			UnitNumber:    0,
  1029  			Key:           scsiKey,
  1030  			ImagePath:     "",
  1031  			IsRoot:        false,
  1032  			Datastore:     ds,
  1033  		}, true)
  1034  }
  1035  
  1036  func (self *SVirtualMachine) copyRootDisk(ctx context.Context, imagePath string) (string, error) {
  1037  	layoutEx := self.getLayoutEx()
  1038  	if layoutEx == nil || len(layoutEx.File) == 0 {
  1039  		return "", fmt.Errorf("invalid LayoutEx")
  1040  	}
  1041  	file := layoutEx.File[0].Name
  1042  	// find stroage
  1043  	host := self.GetIHost()
  1044  	storages, err := host.GetIStorages()
  1045  	if err != nil {
  1046  		return "", errors.Wrap(err, "host.GetIStorages")
  1047  	}
  1048  	var datastore *SDatastore
  1049  	for i := range storages {
  1050  		ds := storages[i].(*SDatastore)
  1051  		if ds.HasFile(file) {
  1052  			datastore = ds
  1053  			break
  1054  		}
  1055  	}
  1056  	if datastore == nil {
  1057  		return "", fmt.Errorf("can't find storage associated with vm %q", self.GetName())
  1058  	}
  1059  	path := datastore.cleanPath(file)
  1060  	vmDir := strings.Split(path, "/")[0]
  1061  	// TODO find a non-conflicting path
  1062  	newImagePath := datastore.getPathString(fmt.Sprintf("%s/%s.vmdk", vmDir, vmDir))
  1063  
  1064  	fm := datastore.getDatastoreObj().NewFileManager(datastore.datacenter.getObjectDatacenter(), true)
  1065  	err = fm.Copy(ctx, imagePath, newImagePath)
  1066  	if err != nil {
  1067  		return "", errors.Wrap(err, "unable to copy system disk")
  1068  	}
  1069  	return newImagePath, nil
  1070  }
  1071  
  1072  func (self *SVirtualMachine) createDiskWithDeviceChange(ctx context.Context, deviceChange []types.BaseVirtualDeviceConfigSpec, config SDiskConfig, check bool) error {
  1073  	var err error
  1074  	// copy disk
  1075  	if len(config.ImagePath) > 0 {
  1076  		config.IsRoot = true
  1077  		config.ImagePath, err = self.copyRootDisk(ctx, config.ImagePath)
  1078  		if err != nil {
  1079  			return errors.Wrap(err, "unable to copyRootDisk")
  1080  		}
  1081  	}
  1082  
  1083  	devSpec := NewDiskDev(int64(config.SizeMb), config)
  1084  	spec := addDevSpec(devSpec)
  1085  	if len(config.ImagePath) == 0 {
  1086  		spec.FileOperation = types.VirtualDeviceConfigSpecFileOperationCreate
  1087  	}
  1088  	configSpec := types.VirtualMachineConfigSpec{}
  1089  	configSpec.DeviceChange = append(deviceChange, spec)
  1090  
  1091  	vmObj := self.getVmObj()
  1092  
  1093  	task, err := vmObj.Reconfigure(ctx, configSpec)
  1094  	if err != nil {
  1095  		return err
  1096  	}
  1097  	err = task.Wait(ctx)
  1098  	if err != nil {
  1099  		return err
  1100  	}
  1101  	if !check {
  1102  		return nil
  1103  	}
  1104  	oldDiskCnt := len(self.vdisks)
  1105  	maxTries := 60
  1106  	for tried := 0; tried < maxTries; tried += 1 {
  1107  		time.Sleep(time.Second)
  1108  		self.Refresh()
  1109  		if len(self.vdisks) > oldDiskCnt {
  1110  			return nil
  1111  		}
  1112  	}
  1113  	return cloudprovider.ErrTimeout
  1114  }
  1115  
  1116  func (self *SVirtualMachine) createDiskInternal(ctx context.Context, config SDiskConfig, check bool) error {
  1117  
  1118  	return self.createDiskWithDeviceChange(ctx, nil, config, check)
  1119  }
  1120  
  1121  func (self *SVirtualMachine) Renew(bc billing.SBillingCycle) error {
  1122  	return cloudprovider.ErrNotSupported
  1123  }
  1124  
  1125  func (self *SVirtualMachine) GetProjectId() string {
  1126  	pool, err := self.getResourcePool()
  1127  	if err != nil {
  1128  		return ""
  1129  	}
  1130  	if pool != nil {
  1131  		return pool.GetId()
  1132  	}
  1133  	return ""
  1134  }
  1135  
  1136  func (self *SVirtualMachine) GetError() error {
  1137  	return nil
  1138  }
  1139  
  1140  func (self *SVirtualMachine) getResourcePool() (*SResourcePool, error) {
  1141  	vm := self.getVirtualMachine()
  1142  	morp := mo.ResourcePool{}
  1143  	if vm.ResourcePool == nil {
  1144  		return nil, errors.Error("nil resource pool")
  1145  	}
  1146  	err := self.manager.reference2Object(*vm.ResourcePool, RESOURCEPOOL_PROPS, &morp)
  1147  	if err != nil {
  1148  		return nil, errors.Wrap(err, "self.manager.reference2Object")
  1149  	}
  1150  	rp := NewResourcePool(self.manager, &morp, self.datacenter)
  1151  	return rp, nil
  1152  }
  1153  
  1154  func (self *SVirtualMachine) CheckFileInfo(ctx context.Context) error {
  1155  	layoutEx := self.getLayoutEx()
  1156  	if layoutEx != nil && len(layoutEx.File) > 0 {
  1157  		file := layoutEx.File[0]
  1158  		host := self.GetIHost()
  1159  		storages, err := host.GetIStorages()
  1160  		if err != nil {
  1161  			return errors.Wrap(err, "host.GetIStorages")
  1162  		}
  1163  		for i := range storages {
  1164  			ds := storages[i].(*SDatastore)
  1165  			if ds.HasFile(file.Name) {
  1166  				_, err := ds.CheckFile(ctx, file.Name)
  1167  				if err != nil {
  1168  					return errors.Wrap(err, "ds.CheckFile")
  1169  				}
  1170  				break
  1171  			}
  1172  		}
  1173  	}
  1174  	return nil
  1175  }
  1176  
  1177  func (self *SVirtualMachine) DoRename(ctx context.Context, name string) error {
  1178  	task, err := self.getVmObj().Rename(ctx, name)
  1179  	if err != nil {
  1180  		return errors.Wrap(err, "object.VirtualMachine.Rename")
  1181  	}
  1182  	return task.Wait(ctx)
  1183  }
  1184  
  1185  func (self *SVirtualMachine) GetMoid() string {
  1186  	return self.getVirtualMachine().Self.Value
  1187  }
  1188  
  1189  func (self *SVirtualMachine) GetToolsVersion() string {
  1190  	return self.getVirtualMachine().Guest.ToolsVersion
  1191  }
  1192  
  1193  func (self *SVirtualMachine) DoCustomize(ctx context.Context, params jsonutils.JSONObject) error {
  1194  	spec := new(types.CustomizationSpec)
  1195  
  1196  	ipSettings := new(types.CustomizationGlobalIPSettings)
  1197  	domain := "local"
  1198  	if params.Contains("domain") {
  1199  		domain, _ = params.GetString("domain")
  1200  	}
  1201  	ipSettings.DnsSuffixList = []string{domain}
  1202  
  1203  	// deal nics
  1204  	nics, _ := params.GetArray("nics")
  1205  	serverNics := make([]cloudtypes.SServerNic, len(nics))
  1206  	for i := range nics {
  1207  		var nicType cloudtypes.SServerNic
  1208  		nics[i].Unmarshal(&nicType)
  1209  		serverNics[i] = nicType
  1210  	}
  1211  
  1212  	// find dnsServerList
  1213  	for i := range serverNics {
  1214  		dnsList := netutils2.GetNicDns(&serverNics[i])
  1215  		if len(dnsList) != 0 {
  1216  			ipSettings.DnsServerList = dnsList
  1217  		}
  1218  	}
  1219  	spec.GlobalIPSettings = *ipSettings
  1220  
  1221  	maps := make([]types.CustomizationAdapterMapping, 0, len(nics))
  1222  	for _, nic := range serverNics {
  1223  		conf := types.CustomizationAdapterMapping{}
  1224  		conf.MacAddress = nic.Mac
  1225  		if len(conf.MacAddress) == 0 {
  1226  			conf.MacAddress = "9e:46:27:21:a2:b2"
  1227  		}
  1228  
  1229  		conf.Adapter = types.CustomizationIPSettings{}
  1230  		fixedIp := new(types.CustomizationFixedIp)
  1231  		fixedIp.IpAddress = nic.Ip
  1232  		if len(fixedIp.IpAddress) == 0 {
  1233  			fixedIp.IpAddress = "10.168.26.23"
  1234  		}
  1235  		conf.Adapter.Ip = fixedIp
  1236  		maskLen := nic.Masklen
  1237  		if maskLen == 0 {
  1238  			maskLen = 24
  1239  		}
  1240  		mask := netutils2.Netlen2Mask(maskLen)
  1241  		conf.Adapter.SubnetMask = mask
  1242  
  1243  		if len(nic.Gateway) != 0 {
  1244  			conf.Adapter.Gateway = []string{nic.Gateway}
  1245  		}
  1246  		dnsList := netutils2.GetNicDns(&nic)
  1247  		if len(dnsList) != 0 {
  1248  			conf.Adapter.DnsServerList = dnsList
  1249  			dns := nic.Domain
  1250  			if len(dns) == 0 {
  1251  				dns = "local"
  1252  			}
  1253  			conf.Adapter.DnsDomain = dns
  1254  		}
  1255  		maps = append(maps, conf)
  1256  	}
  1257  	spec.NicSettingMap = maps
  1258  
  1259  	var (
  1260  		osName string
  1261  		name   = "yunionhost"
  1262  	)
  1263  	if params.Contains("os_name") {
  1264  		osName, _ = params.GetString("os_name")
  1265  	}
  1266  	if params.Contains("name") {
  1267  		name, _ = params.GetString("name")
  1268  	}
  1269  	if osName == "Linux" {
  1270  		linuxPrep := types.CustomizationLinuxPrep{
  1271  			HostName: &types.CustomizationFixedName{Name: name},
  1272  			Domain:   domain,
  1273  			TimeZone: "Asia/Shanghai",
  1274  		}
  1275  		spec.Identity = &linuxPrep
  1276  	} else if osName == "Windows" {
  1277  		sysPrep := types.CustomizationSysprep{
  1278  			GuiUnattended: types.CustomizationGuiUnattended{
  1279  				TimeZone:  210,
  1280  				AutoLogon: false,
  1281  			},
  1282  			UserData: types.CustomizationUserData{
  1283  				FullName:  "Administrator",
  1284  				OrgName:   "Yunion",
  1285  				ProductId: "",
  1286  				ComputerName: &types.CustomizationFixedName{
  1287  					Name: name,
  1288  				},
  1289  			},
  1290  			Identification: types.CustomizationIdentification{},
  1291  		}
  1292  		spec.Identity = &sysPrep
  1293  	}
  1294  	log.Infof("customize spec: %#v", spec)
  1295  	task, err := self.getVmObj().Customize(ctx, *spec)
  1296  	if err != nil {
  1297  		return errors.Wrap(err, "object.VirtualMachine.Customize")
  1298  	}
  1299  	return task.Wait(ctx)
  1300  }
  1301  
  1302  func (self *SVirtualMachine) ExportTemplate(ctx context.Context, idx int, diskPath string) error {
  1303  	lease, err := self.getVmObj().Export(ctx)
  1304  	if err != nil {
  1305  		return errors.Wrap(err, "esxi.SVirtualMachine.DoExportTemplate")
  1306  	}
  1307  	info, err := lease.Wait(ctx, nil)
  1308  	if err != nil {
  1309  		return errors.Wrap(err, "lease.Wait")
  1310  	}
  1311  
  1312  	u := lease.StartUpdater(ctx, info)
  1313  	defer u.Done()
  1314  
  1315  	if idx >= len(info.Items) {
  1316  		return errors.Error(fmt.Sprintf("No such Device whose index is %d", idx))
  1317  	}
  1318  
  1319  	lr := newLeaseLogger("download vmdk", 5)
  1320  	lr.Log()
  1321  	defer lr.End()
  1322  
  1323  	// filter vmdk item
  1324  	vmdkItems := make([]nfc.FileItem, 0, len(info.Items)/2)
  1325  	for i := range info.Items {
  1326  		if strings.HasSuffix(info.Items[i].Path, ".vmdk") {
  1327  			vmdkItems = append(vmdkItems, info.Items[i])
  1328  		} else {
  1329  			log.Infof("item.Path does not end in '.vmdk': %#v", info.Items[i])
  1330  		}
  1331  	}
  1332  
  1333  	log.Debugf("download to %s start...", diskPath)
  1334  	err = lease.DownloadFile(ctx, diskPath, vmdkItems[idx], soap.Download{Progress: lr})
  1335  	if err != nil {
  1336  		return errors.Wrap(err, "lease.DownloadFile")
  1337  	}
  1338  
  1339  	err = lease.Complete(ctx)
  1340  	if err != nil {
  1341  		return errors.Wrap(err, "lease.Complete")
  1342  	}
  1343  	log.Debugf("download to %s finish", diskPath)
  1344  	return nil
  1345  }
  1346  
  1347  func (self *SVirtualMachine) GetSerialOutput(port int) (string, error) {
  1348  	return "", cloudprovider.ErrNotImplemented
  1349  }
  1350  
  1351  func (self *SVirtualMachine) ConvertPublicIpToEip() error {
  1352  	return cloudprovider.ErrNotSupported
  1353  }
  1354  
  1355  func (self *SVirtualMachine) IsAutoRenew() bool {
  1356  	return false
  1357  }
  1358  
  1359  func (self *SVirtualMachine) SetAutoRenew(bc billing.SBillingCycle) error {
  1360  	return cloudprovider.ErrNotSupported
  1361  }
  1362  
  1363  func (self *SVirtualMachine) FindMinDiffKey(limit int32) int32 {
  1364  	if self.devs == nil {
  1365  		self.fetchHardwareInfo()
  1366  	}
  1367  	devKeys := make([]int32, 0, len(self.devs))
  1368  	for key := range self.devs {
  1369  		devKeys = append(devKeys, key)
  1370  	}
  1371  	sort.Slice(devKeys, func(i int, j int) bool {
  1372  		return devKeys[i] < devKeys[j]
  1373  	})
  1374  	for _, key := range devKeys {
  1375  		switch {
  1376  		case key < limit:
  1377  		case key == limit:
  1378  			limit += 1
  1379  		case key > limit:
  1380  			return limit
  1381  		}
  1382  	}
  1383  	return limit
  1384  }
  1385  
  1386  func (self *SVirtualMachine) relocate(hostId string) error {
  1387  	var targetHs *mo.HostSystem
  1388  	if hostId == "" {
  1389  		return errors.Wrap(fmt.Errorf("require hostId"), "relocate")
  1390  	}
  1391  	ihost, err := self.manager.GetIHostById(hostId)
  1392  	if err != nil {
  1393  		return errors.Wrap(err, "self.manager.GetIHostById(hostId)")
  1394  	}
  1395  	targetHs = ihost.(*SHost).object.(*mo.HostSystem)
  1396  	if len(targetHs.Datastore) < 1 {
  1397  		return errors.Wrap(fmt.Errorf("target host has no datastore"), "relocate")
  1398  	}
  1399  	ctx := self.manager.context
  1400  	config := types.VirtualMachineRelocateSpec{}
  1401  	hrs := targetHs.Reference()
  1402  	config.Host = &hrs
  1403  	config.Datastore = &targetHs.Datastore[0]
  1404  	task, err := self.getVmObj().Relocate(ctx, config, types.VirtualMachineMovePriorityDefaultPriority)
  1405  	if err != nil {
  1406  		log.Errorf("vm.Migrate %s", err)
  1407  		return errors.Wrap(err, "SVirtualMachine Migrate")
  1408  	}
  1409  	err = task.Wait(ctx)
  1410  	if err != nil {
  1411  		log.Errorf("task.Wait %s", err)
  1412  		return errors.Wrap(err, "task.wait")
  1413  	}
  1414  	return nil
  1415  }
  1416  
  1417  func (self *SVirtualMachine) MigrateVM(hostId string) error {
  1418  	return self.relocate(hostId)
  1419  }
  1420  
  1421  func (self *SVirtualMachine) LiveMigrateVM(hostId string) error {
  1422  	return self.relocate(hostId)
  1423  }
  1424  
  1425  func (self *SVirtualMachine) GetIHostId() string {
  1426  	ctx := self.manager.context
  1427  	hs, err := self.getVmObj().HostSystem(ctx)
  1428  	if err != nil {
  1429  		log.Errorf("get HostSystem %s", err)
  1430  		return ""
  1431  	}
  1432  	var moHost mo.HostSystem
  1433  	err = self.manager.reference2Object(hs.Reference(), HOST_SYSTEM_PROPS, &moHost)
  1434  	if err != nil {
  1435  		log.Errorf("hostsystem reference2Object %s", err)
  1436  		return ""
  1437  	}
  1438  	shost := NewHost(self.manager, &moHost, nil)
  1439  	return shost.GetGlobalId()
  1440  }
  1441  
  1442  func (self *SVirtualMachine) IsTemplate() bool {
  1443  	movm := self.getVirtualMachine()
  1444  	if tempalteNameRegex != nil && tempalteNameRegex.MatchString(self.GetName()) && movm.Summary.Runtime.PowerState == types.VirtualMachinePowerStatePoweredOff {
  1445  		return true
  1446  	}
  1447  	return movm.Config != nil && movm.Config.Template
  1448  }
  1449  
  1450  func (self *SVirtualMachine) fetchSnapshots() {
  1451  	movm := self.getVirtualMachine()
  1452  	if movm.Snapshot == nil {
  1453  		return
  1454  	}
  1455  	self.snapshots = self.extractSnapshots(movm.Snapshot.RootSnapshotList, make([]SVirtualMachineSnapshot, 0, len(movm.Snapshot.RootSnapshotList)))
  1456  }
  1457  
  1458  func (self *SVirtualMachine) extractSnapshots(tree []types.VirtualMachineSnapshotTree, snapshots []SVirtualMachineSnapshot) []SVirtualMachineSnapshot {
  1459  	for i := range tree {
  1460  		snapshots = append(snapshots, SVirtualMachineSnapshot{
  1461  			snapshotTree: tree[i],
  1462  			vm:           self,
  1463  		})
  1464  		snapshots = self.extractSnapshots(tree[i].ChildSnapshotList, snapshots)
  1465  	}
  1466  	return snapshots
  1467  }
  1468  
  1469  func (self *SVirtualMachine) GetInstanceSnapshots() ([]cloudprovider.ICloudInstanceSnapshot, error) {
  1470  	if self.snapshots == nil {
  1471  		self.fetchSnapshots()
  1472  	}
  1473  	ret := make([]cloudprovider.ICloudInstanceSnapshot, 0, len(self.snapshots))
  1474  	for i := range self.snapshots {
  1475  		ret = append(ret, &self.snapshots[i])
  1476  	}
  1477  	return ret, nil
  1478  }
  1479  
  1480  func (self *SVirtualMachine) GetInstanceSnapshot(idStr string) (cloudprovider.ICloudInstanceSnapshot, error) {
  1481  	if self.snapshots == nil {
  1482  		self.fetchSnapshots()
  1483  	}
  1484  	for i := range self.snapshots {
  1485  		if self.snapshots[i].GetGlobalId() == idStr {
  1486  			// copyone
  1487  			sp := self.snapshots[i]
  1488  			return &sp, nil
  1489  		}
  1490  	}
  1491  	return nil, errors.ErrNotFound
  1492  }
  1493  
  1494  func (self *SVirtualMachine) CreateInstanceSnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudInstanceSnapshot, error) {
  1495  	ovm := self.getVmObj()
  1496  	task, err := ovm.CreateSnapshot(ctx, name, desc, false, false)
  1497  	if err != nil {
  1498  		return nil, errors.Wrap(err, "CreateSnapshot")
  1499  	}
  1500  	info, err := task.WaitForResult(ctx, nil)
  1501  	if err != nil {
  1502  		return nil, errors.Wrap(err, "task.Wait")
  1503  	}
  1504  	sp := info.Result.(types.ManagedObjectReference)
  1505  	err = self.Refresh()
  1506  	if err != nil {
  1507  		return nil, errors.Wrap(err, "create successfully")
  1508  	}
  1509  	self.fetchSnapshots()
  1510  	for i := range self.snapshots {
  1511  		if self.snapshots[i].snapshotTree.Snapshot == sp {
  1512  			// copyone
  1513  			sp := self.snapshots[i]
  1514  			return &sp, nil
  1515  		}
  1516  	}
  1517  	return nil, errors.Wrap(errors.ErrNotFound, "create successfully")
  1518  }
  1519  
  1520  func (self *SVirtualMachine) ResetToInstanceSnapshot(ctx context.Context, idStr string) error {
  1521  	cloudIsp, err := self.GetInstanceSnapshot(idStr)
  1522  	if err != nil {
  1523  		return errors.Wrap(err, "GetInstanceSnapshot")
  1524  	}
  1525  	isp := cloudIsp.(*SVirtualMachineSnapshot)
  1526  	req := types.RevertToSnapshot_Task{
  1527  		This: isp.snapshotTree.Snapshot.Reference(),
  1528  	}
  1529  	res, err := methods.RevertToSnapshot_Task(ctx, self.manager.client.Client, &req)
  1530  	if err != nil {
  1531  		return errors.Wrap(err, "RevertToSnapshot_Task")
  1532  	}
  1533  	return object.NewTask(self.manager.client.Client, res.Returnval).Wait(ctx)
  1534  }