yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/esxi/host.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  	"regexp"
    21  	"strings"
    22  
    23  	"github.com/vmware/govmomi/object"
    24  	"github.com/vmware/govmomi/vim25/mo"
    25  	"github.com/vmware/govmomi/vim25/types"
    26  
    27  	"yunion.io/x/jsonutils"
    28  	"yunion.io/x/log"
    29  	"yunion.io/x/pkg/errors"
    30  	"yunion.io/x/pkg/util/netutils"
    31  	"yunion.io/x/pkg/util/regutils"
    32  
    33  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    34  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    35  	"yunion.io/x/cloudmux/pkg/multicloud"
    36  )
    37  
    38  var (
    39  	hostConfigProps   = []string{"config.network", "config.storageDevice"}
    40  	hostSummaryProps  = []string{"summary.runtime", "summary.hardware", "summary.config.product", "summary.managementServerIp"}
    41  	hostHardWareProps = []string{"hardware.systemInfo"}
    42  )
    43  
    44  var HOST_SYSTEM_PROPS []string
    45  
    46  func init() {
    47  	HOST_SYSTEM_PROPS = []string{"name", "parent", "vm", "datastore", "network"}
    48  	HOST_SYSTEM_PROPS = append(HOST_SYSTEM_PROPS, hostConfigProps...)
    49  	HOST_SYSTEM_PROPS = append(HOST_SYSTEM_PROPS, hostSummaryProps...)
    50  	HOST_SYSTEM_PROPS = append(HOST_SYSTEM_PROPS, hostHardWareProps...)
    51  }
    52  
    53  type SHostStorageAdapterInfo struct {
    54  	Device    string
    55  	Model     string
    56  	Driver    string
    57  	Pci       string
    58  	Drivers   []*SHostStorageDriverInfo
    59  	Enclosure int
    60  }
    61  
    62  type SHostStorageDriverInfo struct {
    63  	CN       string
    64  	Name     string
    65  	Model    string
    66  	Vendor   string
    67  	Revision string
    68  	Status   string
    69  	SSD      bool
    70  	Dev      string
    71  	Size     int
    72  	Slot     int
    73  }
    74  
    75  type SHostStorageEnclosureInfo struct {
    76  	CN       string
    77  	Name     string
    78  	Model    string
    79  	Vendor   string
    80  	Revision string
    81  	Status   string
    82  }
    83  
    84  type SHostStorageInfo struct {
    85  	Adapter int
    86  	Driver  string
    87  	Index   int
    88  	Model   string
    89  	Rotate  bool
    90  	Status  string
    91  	Size    int
    92  }
    93  
    94  type SHost struct {
    95  	multicloud.SHostBase
    96  	SManagedObject
    97  
    98  	masterIp string
    99  
   100  	nicInfo      []SHostNicInfo
   101  	storageInfo  []SHostStorageInfo
   102  	datastores   []cloudprovider.ICloudStorage
   103  	storageCache *SDatastoreImageCache
   104  	vms          []cloudprovider.ICloudVM
   105  	parent       *mo.ComputeResource
   106  	networks     []IVMNetwork
   107  	tempalteVMs  []*SVirtualMachine
   108  }
   109  
   110  func NewHost(manager *SESXiClient, host *mo.HostSystem, dc *SDatacenter) *SHost {
   111  	if host.Config == nil {
   112  		log.Errorf("empty host config %s", host.Name)
   113  		return nil
   114  	}
   115  	return &SHost{SManagedObject: newManagedObject(manager, host, dc)}
   116  }
   117  
   118  var (
   119  	ip4addrPattern = regexp.MustCompile(`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`)
   120  )
   121  
   122  func formatName(name string) string {
   123  	if ip4addrPattern.MatchString(name) {
   124  		return strings.Replace(name, ".", "-", -1)
   125  	} else {
   126  		dotPos := strings.IndexByte(name, '.')
   127  		if dotPos > 0 && !regutils.MatchIP4Addr(name) {
   128  			name = name[:dotPos]
   129  		}
   130  		return name
   131  	}
   132  }
   133  
   134  func (self *SHost) GetName() string {
   135  	return formatName(self.SManagedObject.GetName())
   136  }
   137  
   138  func (self *SHost) GetSchedtags() ([]string, error) {
   139  	clusters, err := self.datacenter.listClusters()
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	cpName := self.datacenter.manager.cpcfg.Name
   144  	reference := self.GetoHostSystem().Reference()
   145  	tags := make([]string, 0, 1)
   146  	oDatacenter := self.datacenter.getDatacenter()
   147  Loop:
   148  	for i := range clusters {
   149  		oc := clusters[i].getoCluster()
   150  		if len(oc.Host) == 0 {
   151  			continue
   152  		}
   153  		for _, h := range oc.Host {
   154  			if h == reference {
   155  				tags = append(tags, fmt.Sprintf("cluster:/%s/%s/%s", cpName, oDatacenter.Name, oc.Name))
   156  				continue Loop
   157  			}
   158  		}
   159  	}
   160  	return tags, nil
   161  }
   162  
   163  func (self *SHost) getHostSystem() *mo.HostSystem {
   164  	return self.object.(*mo.HostSystem)
   165  }
   166  
   167  func (self *SHost) GetGlobalId() string {
   168  	return self.GetAccessIp()
   169  }
   170  
   171  func (self *SHost) GetStatus() string {
   172  	/*
   173  		HostSystemPowerStatePoweredOn  = HostSystemPowerState("poweredOn")
   174  		HostSystemPowerStatePoweredOff = HostSystemPowerState("poweredOff")
   175  		HostSystemPowerStateStandBy    = HostSystemPowerState("standBy")
   176  		HostSystemPowerStateUnknown    = HostSystemPowerState("unknown")
   177  	*/
   178  	switch self.getHostSystem().Summary.Runtime.PowerState {
   179  	case types.HostSystemPowerStatePoweredOn:
   180  		return api.HOST_STATUS_RUNNING
   181  	case types.HostSystemPowerStatePoweredOff:
   182  		return api.HOST_STATUS_READY
   183  	default:
   184  		return api.HOST_STATUS_UNKNOWN
   185  	}
   186  }
   187  
   188  func (self *SHost) Refresh() error {
   189  	base := self.SManagedObject
   190  	var moObj mo.HostSystem
   191  	err := self.manager.reference2Object(self.object.Reference(), HOST_SYSTEM_PROPS, &moObj)
   192  	if err != nil {
   193  		return err
   194  	}
   195  	base.object = &moObj
   196  	*self = SHost{}
   197  	self.SManagedObject = base
   198  	return nil
   199  }
   200  
   201  func (self *SHost) IsEmulated() bool {
   202  	return false
   203  }
   204  
   205  func (self *SHost) fetchVMs(all bool) error {
   206  	if self.vms != nil {
   207  		return nil
   208  	}
   209  
   210  	dc, err := self.GetDatacenter()
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	var vms []*SVirtualMachine
   216  	hostVms := self.getHostSystem().Vm
   217  	if len(hostVms) == 0 {
   218  		// log.Errorf("host VMs are nil!!!!!")
   219  		return nil
   220  	}
   221  
   222  	vms, err = dc.fetchVms(hostVms, all)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	for _, vm := range vms {
   227  		if vm.IsTemplate() {
   228  			self.tempalteVMs = append(self.tempalteVMs, vm)
   229  		} else {
   230  			self.vms = append(self.vms, vm)
   231  		}
   232  	}
   233  	return nil
   234  }
   235  
   236  func (self *SHost) GetIVMs2() ([]cloudprovider.ICloudVM, error) {
   237  	err := self.fetchVMs(true)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  	return self.vms, nil
   242  }
   243  
   244  func (self *SHost) GetTemplateVMs() ([]*SVirtualMachine, error) {
   245  	err := self.fetchVMs(false)
   246  	if err != nil {
   247  		return nil, errors.Wrap(err, "SHost.fetchVMs")
   248  	}
   249  	return self.tempalteVMs, nil
   250  }
   251  
   252  func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
   253  	err := self.fetchVMs(false)
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  	return self.vms, nil
   258  }
   259  
   260  func (self *SHost) GetTemplateVMById(id string) (*SVirtualMachine, error) {
   261  	id = self.manager.getPrivateId(id)
   262  	temVms, err := self.GetTemplateVMs()
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	for i := range temVms {
   267  		if temVms[i].GetGlobalId() == id {
   268  			return temVms[i], nil
   269  		}
   270  	}
   271  	return nil, cloudprovider.ErrNotFound
   272  }
   273  
   274  func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
   275  	id = self.manager.getPrivateId(id)
   276  
   277  	vms, err := self.GetIVMs()
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  	for i := 0; i < len(vms); i += 1 {
   282  		if vms[i].GetGlobalId() == id {
   283  			return vms[i], nil
   284  		}
   285  	}
   286  	return nil, cloudprovider.ErrNotFound
   287  }
   288  
   289  func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
   290  	return nil, cloudprovider.ErrNotImplemented
   291  }
   292  
   293  func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
   294  	return self.GetDataStores()
   295  }
   296  
   297  func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
   298  	istorages, err := self.GetIStorages()
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  	for i := 0; i < len(istorages); i += 1 {
   303  		if istorages[i].GetGlobalId() == id {
   304  			return istorages[i], nil
   305  		}
   306  	}
   307  	return nil, cloudprovider.ErrNotFound
   308  }
   309  
   310  func (self *SHost) GetEnabled() bool {
   311  	if self.getHostSystem().Summary.Runtime.InMaintenanceMode {
   312  		return false
   313  	}
   314  	return true
   315  }
   316  
   317  func (self *SHost) GetHostStatus() string {
   318  	/*
   319  		HostSystemConnectionStateConnected     = HostSystemConnectionState("connected")
   320  		HostSystemConnectionStateNotResponding = HostSystemConnectionState("notResponding")
   321  		HostSystemConnectionStateDisconnected  = HostSystemConnectionState("disconnected")
   322  	*/
   323  	if self.getHostSystem().Summary.Runtime.InMaintenanceMode {
   324  		return api.HOST_OFFLINE
   325  	}
   326  	switch self.getHostSystem().Summary.Runtime.ConnectionState {
   327  	case types.HostSystemConnectionStateConnected:
   328  		return api.HOST_ONLINE
   329  	default:
   330  		return api.HOST_OFFLINE
   331  	}
   332  }
   333  
   334  func findHostNicByMac(nicInfoList []SHostNicInfo, mac string) *SHostNicInfo {
   335  	for i := 0; i < len(nicInfoList); i += 1 {
   336  		if nicInfoList[i].Mac == mac {
   337  			return &nicInfoList[i]
   338  		}
   339  	}
   340  	return nil
   341  }
   342  
   343  func (self *SHost) getAdminNic() *SHostNicInfo {
   344  	nics := self.getNicInfo(false)
   345  	for i := 0; i < len(nics); i += 1 {
   346  		if nics[i].NicType == api.NIC_TYPE_ADMIN {
   347  			return &nics[i]
   348  		}
   349  	}
   350  	for i := 0; i < len(nics); i += 1 {
   351  		if len(nics[i].IpAddr) > 0 {
   352  			return &nics[i]
   353  		}
   354  	}
   355  	return nil
   356  }
   357  
   358  func (self *SHost) getNicInfo(debug bool) []SHostNicInfo {
   359  	if self.nicInfo == nil {
   360  		self.nicInfo = self.fetchNicInfo(debug)
   361  	}
   362  	return self.nicInfo
   363  }
   364  
   365  func mask2len(mask string) int8 {
   366  	maskAddr, _ := netutils.NewIPV4Addr(mask)
   367  	return netutils.Mask2Len(maskAddr)
   368  }
   369  
   370  func (self *SHost) isVnicAdmin(nic types.HostVirtualNic) bool {
   371  	if len(self.masterIp) > 0 {
   372  		if self.masterIp == nic.Spec.Ip.IpAddress {
   373  			return true
   374  		} else {
   375  			return false
   376  		}
   377  	}
   378  	exist, err := self.manager.IsHostIpExists(nic.Spec.Ip.IpAddress)
   379  	if err != nil {
   380  		log.Errorf("IsHostIpExists %s fail %s", nic.Spec.Ip.IpAddress, err)
   381  		return false
   382  	}
   383  	if exist {
   384  		self.masterIp = nic.Spec.Ip.IpAddress
   385  		return true
   386  	}
   387  	return false
   388  }
   389  
   390  func (self *SHost) fetchNicInfo(debug bool) []SHostNicInfo {
   391  	moHost := self.getHostSystem()
   392  
   393  	if moHost.Config == nil || moHost.Config.Network == nil {
   394  		return nil
   395  	}
   396  
   397  	if debug {
   398  		log.Debugf("%s", jsonutils.Marshal(moHost.Config.Network).PrettyString())
   399  	}
   400  
   401  	nicInfoList := make([]SHostNicInfo, 0)
   402  
   403  	for i, nic := range moHost.Config.Network.Pnic {
   404  		info := SHostNicInfo{}
   405  		info.Dev = nic.Device
   406  		info.Driver = nic.Driver
   407  		info.Mac = netutils.FormatMacAddr(nic.Mac)
   408  		info.Index = int8(i)
   409  		info.LinkUp = false
   410  		nicInfoList = append(nicInfoList, info)
   411  	}
   412  
   413  	vnics := make([]types.HostVirtualNic, 0)
   414  	if len(moHost.Config.Network.Vnic) > 0 {
   415  		vnics = append(vnics, moHost.Config.Network.Vnic...)
   416  	}
   417  	if len(moHost.Config.Network.ConsoleVnic) > 0 {
   418  		vnics = append(vnics, moHost.Config.Network.ConsoleVnic...)
   419  	}
   420  
   421  	for _, nic := range vnics {
   422  		mac := netutils.FormatMacAddr(nic.Spec.Mac)
   423  		pnic := findHostNicByMac(nicInfoList, mac)
   424  		if pnic != nil {
   425  			// findMaster = true
   426  			pnic.IpAddr = nic.Spec.Ip.IpAddress
   427  			pnic.IpAddrPrefixLen = mask2len(nic.Spec.Ip.SubnetMask)
   428  			if nic.Spec.Ip.IpV6Config != nil && len(nic.Spec.Ip.IpV6Config.IpV6Address) > 0 {
   429  				pnic.IpAddr6 = nic.Spec.Ip.IpV6Config.IpV6Address[0].IpAddress
   430  				pnic.IpAddr6PrefixLen = int8(nic.Spec.Ip.IpV6Config.IpV6Address[0].PrefixLength)
   431  			}
   432  			if self.isVnicAdmin(nic) {
   433  				pnic.NicType = api.NIC_TYPE_ADMIN
   434  			}
   435  			pnic.LinkUp = true
   436  			pnic.Mtu = nic.Spec.Mtu
   437  			if nic.Spec.DistributedVirtualPort != nil {
   438  				pnic.DVPortGroup = nic.Spec.DistributedVirtualPort.PortgroupKey
   439  			}
   440  		} else {
   441  			info := SHostNicInfo{}
   442  			info.Dev = nic.Device
   443  			info.Driver = "vmkernel"
   444  			info.Mac = mac
   445  			info.Index = int8(len(nicInfoList))
   446  			info.LinkUp = true
   447  			info.IpAddr = nic.Spec.Ip.IpAddress
   448  			info.IpAddrPrefixLen = mask2len(nic.Spec.Ip.SubnetMask)
   449  			if nic.Spec.Ip.IpV6Config != nil && len(nic.Spec.Ip.IpV6Config.IpV6Address) > 0 {
   450  				info.IpAddr6 = nic.Spec.Ip.IpV6Config.IpV6Address[0].IpAddress
   451  				info.IpAddr6PrefixLen = int8(nic.Spec.Ip.IpV6Config.IpV6Address[0].PrefixLength)
   452  			}
   453  			info.Mtu = nic.Spec.Mtu
   454  			if self.isVnicAdmin(nic) {
   455  				info.NicType = api.NIC_TYPE_ADMIN
   456  			}
   457  			if nic.Spec.DistributedVirtualPort != nil {
   458  				info.DVPortGroup = nic.Spec.DistributedVirtualPort.PortgroupKey
   459  			}
   460  			nicInfoList = append(nicInfoList, info)
   461  		}
   462  	}
   463  
   464  	return nicInfoList
   465  }
   466  
   467  func (self *SHost) GetAccessIp() string {
   468  	adminNic := self.getAdminNic()
   469  	if adminNic != nil {
   470  		return adminNic.IpAddr
   471  	}
   472  	return ""
   473  }
   474  
   475  func (self *SHost) GetAccessMac() string {
   476  	adminNic := self.getAdminNic()
   477  	if adminNic != nil {
   478  		return adminNic.Mac
   479  	}
   480  	return ""
   481  }
   482  
   483  type SSysInfo struct {
   484  	Manufacture  string
   485  	Model        string
   486  	SerialNumber string
   487  }
   488  
   489  func (self *SHost) GetSysInfo() jsonutils.JSONObject {
   490  	sysinfo := SSysInfo{}
   491  	host := self.getHostSystem()
   492  	sysinfo.Manufacture = host.Summary.Hardware.Vendor
   493  	sysinfo.Model = host.Summary.Hardware.Model
   494  	if host.Hardware != nil {
   495  		sysinfo.SerialNumber = host.Hardware.SystemInfo.SerialNumber
   496  	}
   497  	return jsonutils.Marshal(&sysinfo)
   498  }
   499  
   500  func (self *SHost) GetSN() string {
   501  	host := self.getHostSystem()
   502  	if host.Hardware != nil {
   503  		return host.Hardware.SystemInfo.SerialNumber
   504  	}
   505  	return ""
   506  }
   507  
   508  func (self *SHost) GetCpuCount() int {
   509  	return int(self.getHostSystem().Summary.Hardware.NumCpuThreads)
   510  }
   511  
   512  func (self *SHost) GetNodeCount() int8 {
   513  	return int8(self.getHostSystem().Summary.Hardware.NumCpuPkgs)
   514  }
   515  
   516  func (self *SHost) GetCpuDesc() string {
   517  	return self.getHostSystem().Summary.Hardware.CpuModel
   518  }
   519  
   520  func (self *SHost) GetCpuMhz() int {
   521  	return int(self.getHostSystem().Summary.Hardware.CpuMhz)
   522  }
   523  
   524  func (self *SHost) GetMemSizeMB() int {
   525  	return int(self.getHostSystem().Summary.Hardware.MemorySize / 1024 / 1024)
   526  }
   527  
   528  func (self *SHost) GetStorageInfo() []SHostStorageInfo {
   529  	if self.storageInfo == nil {
   530  		self.storageInfo = self.getStorageInfo()
   531  	}
   532  	return self.storageInfo
   533  }
   534  
   535  func (self *SHost) getStorageInfo() []SHostStorageInfo {
   536  	diskSlots := make(map[int]SHostStorageInfo)
   537  	list := self.getStorages()
   538  	for i := 0; i < len(list); i += 1 {
   539  		for j := 0; j < len(list[i].Drivers); j += 1 {
   540  			drv := list[i].Drivers[j]
   541  			info := SHostStorageInfo{
   542  				Adapter: 0,
   543  				Driver:  "Linux",
   544  				Index:   drv.Slot,
   545  				Model:   strings.TrimSpace(fmt.Sprintf("%s %s", drv.Vendor, drv.Model)),
   546  				Rotate:  !drv.SSD,
   547  				Status:  drv.Status,
   548  				Size:    drv.Size,
   549  			}
   550  			diskSlots[info.Index] = info
   551  		}
   552  	}
   553  	disks := make([]SHostStorageInfo, 0)
   554  	idx := 0
   555  	for {
   556  		if info, ok := diskSlots[idx]; ok {
   557  			disks = append(disks, info)
   558  			idx += 1
   559  		} else {
   560  			break
   561  		}
   562  	}
   563  	return disks
   564  }
   565  
   566  func (self *SHost) getStorages() []*SHostStorageAdapterInfo {
   567  	adapterList := make([]*SHostStorageAdapterInfo, 0)
   568  	adapterTable := make(map[string]*SHostStorageAdapterInfo)
   569  	driversTable := make(map[string]*SHostStorageDriverInfo, 0)
   570  	enclosuresTable := make(map[string]*SHostStorageEnclosureInfo, 0)
   571  	moHost := self.getHostSystem()
   572  
   573  	for i := 0; i < len(moHost.Config.StorageDevice.HostBusAdapter); i += 1 {
   574  		ad := moHost.Config.StorageDevice.HostBusAdapter[i]
   575  		adinfo := ad.GetHostHostBusAdapter()
   576  		if adinfo == nil {
   577  			log.Errorf("fail to GetHostHostBusAdapter")
   578  			continue
   579  		}
   580  		info := SHostStorageAdapterInfo{}
   581  		info.Device = adinfo.Device
   582  		info.Model = strings.TrimSpace(adinfo.Model)
   583  		info.Driver = adinfo.Driver
   584  		info.Pci = adinfo.Pci
   585  		info.Drivers = make([]*SHostStorageDriverInfo, 0)
   586  		info.Enclosure = -1
   587  
   588  		adapterTable[adinfo.Key] = &info
   589  		adapterList = append(adapterList, &info)
   590  	}
   591  
   592  	for i := 0; i < len(moHost.Config.StorageDevice.ScsiLun); i += 1 {
   593  		drv := moHost.Config.StorageDevice.ScsiLun[i]
   594  		lunInfo := drv.GetScsiLun()
   595  		if lunInfo == nil {
   596  			log.Errorf("fail to GetScsiLun")
   597  			continue
   598  		}
   599  
   600  		if lunInfo.DeviceType == "disk" {
   601  			scsiDisk := drv.(*types.HostScsiDisk)
   602  			info := SHostStorageDriverInfo{}
   603  			info.CN = scsiDisk.CanonicalName
   604  			info.Name = scsiDisk.DisplayName
   605  			info.Model = strings.TrimSpace(scsiDisk.Model)
   606  			info.Vendor = strings.TrimSpace(scsiDisk.Vendor)
   607  			info.Revision = scsiDisk.Revision
   608  			info.Status = scsiDisk.OperationalState[0]
   609  			if scsiDisk.Ssd != nil && *scsiDisk.Ssd {
   610  				info.SSD = true
   611  			}
   612  			info.Dev = scsiDisk.DevicePath
   613  			info.Size = int(int64(scsiDisk.Capacity.BlockSize) * scsiDisk.Capacity.Block / 1024 / 1024)
   614  
   615  			driversTable[scsiDisk.Key] = &info
   616  		} else if lunInfo.DeviceType == "enclosure" {
   617  			enclosuresTable[lunInfo.Key] = &SHostStorageEnclosureInfo{
   618  				CN:       lunInfo.CanonicalName,
   619  				Name:     lunInfo.DisplayName,
   620  				Model:    strings.TrimSpace(lunInfo.Model),
   621  				Vendor:   strings.TrimSpace(lunInfo.Vendor),
   622  				Revision: lunInfo.Revision,
   623  				Status:   lunInfo.OperationalState[0],
   624  			}
   625  		}
   626  	}
   627  	for i := 0; i < len(moHost.Config.StorageDevice.ScsiTopology.Adapter); i += 1 {
   628  		ad := moHost.Config.StorageDevice.ScsiTopology.Adapter[i]
   629  		adapter := adapterTable[ad.Adapter]
   630  		for j := 0; j < len(ad.Target); j += 1 {
   631  			t := ad.Target[j]
   632  			key := t.Lun[0].ScsiLun
   633  			if _, ok := enclosuresTable[key]; ok {
   634  				adapter.Enclosure = int(t.Target)
   635  			} else if _, ok := driversTable[key]; ok {
   636  				driver := driversTable[key]
   637  				driver.Slot = int(t.Target)
   638  				adapter.Drivers = append(adapter.Drivers, driver)
   639  			}
   640  		}
   641  	}
   642  	return adapterList
   643  }
   644  
   645  func (self *SHost) GetStorageSizeMB() int {
   646  	storages, err := self.GetIStorages()
   647  	if err != nil {
   648  		log.Errorf("SHost.GetStorageSizeMB: SHost.GetIStorages: %s", err)
   649  		return 0
   650  	}
   651  	var size int64
   652  	for _, stor := range storages {
   653  		size += stor.GetCapacityMB()
   654  	}
   655  	return int(size)
   656  }
   657  
   658  func (self *SHost) GetStorageType() string {
   659  	ssd := 0
   660  	rotate := 0
   661  	storages := self.GetStorageInfo()
   662  	for i := 0; i < len(storages); i += 1 {
   663  		if storages[i].Rotate {
   664  			rotate += 1
   665  		} else {
   666  			ssd += 1
   667  		}
   668  	}
   669  	if ssd == 0 && rotate > 0 {
   670  		return api.DISK_TYPE_ROTATE
   671  	} else if ssd > 0 && rotate == 0 {
   672  		return api.DISK_TYPE_SSD
   673  	} else {
   674  		return api.DISK_TYPE_HYBRID
   675  	}
   676  }
   677  
   678  func (self *SHost) GetHostType() string {
   679  	return api.HOST_TYPE_ESXI
   680  }
   681  
   682  func (self *SHost) GetIsMaintenance() bool {
   683  	moHost := self.getHostSystem()
   684  	return moHost.Summary.Runtime.InMaintenanceMode
   685  }
   686  
   687  func (self *SHost) GetVersion() string {
   688  	moHost := self.getHostSystem()
   689  	about := moHost.Summary.Config.Product
   690  	return fmt.Sprintf("%s-%s", about.Version, about.Build)
   691  }
   692  
   693  func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
   694  	return nil, cloudprovider.ErrNotImplemented
   695  }
   696  
   697  type SCreateVMParam struct {
   698  	Name                 string
   699  	Uuid                 string
   700  	OsName               string
   701  	Cpu                  int
   702  	Mem                  int
   703  	Bios                 string
   704  	Cdrom                SCdromInfo
   705  	Disks                []SDiskInfo
   706  	Nics                 []jsonutils.JSONObject
   707  	ResourcePool         string
   708  	InstanceSnapshotInfo SEsxiInstanceSnapshotInfo
   709  }
   710  
   711  type SEsxiInstanceSnapshotInfo struct {
   712  	InstanceSnapshotId string
   713  	InstanceId         string
   714  }
   715  
   716  type SCdromInfo struct {
   717  	ImageId string
   718  	Path    string
   719  	Name    string
   720  	Size    string
   721  }
   722  
   723  type SDiskInfo struct {
   724  	ImagePath string
   725  	Size      int64
   726  	DiskId    string
   727  	Driver    string
   728  	ImageInfo SEsxiImageInfo
   729  	StorageId string
   730  }
   731  
   732  type SEsxiImageInfo struct {
   733  	ImageType          string
   734  	ImageExternalId    string
   735  	StorageCacheHostIp string
   736  }
   737  
   738  func (self *SHost) CreateVM2(ctx context.Context, ds *SDatastore, params SCreateVMParam) (needDeploy bool, vm *SVirtualMachine, err error) {
   739  	needDeploy = true
   740  	var temvm *SVirtualMachine
   741  	if len(params.InstanceSnapshotInfo.InstanceSnapshotId) > 0 {
   742  		temvm, err = self.manager.SearchVM(params.InstanceSnapshotInfo.InstanceId)
   743  		if err != nil {
   744  			err = errors.Wrapf(err, "can't find vm %q, please sync status for vm or sync cloudaccount", params.InstanceSnapshotInfo.InstanceId)
   745  		}
   746  		var isp cloudprovider.ICloudInstanceSnapshot
   747  		isp, err = temvm.GetInstanceSnapshot(params.InstanceSnapshotInfo.InstanceSnapshotId)
   748  		if err != nil {
   749  			err = errors.Wrap(err, "unable to GetInstanceSnapshot")
   750  			return
   751  		}
   752  		sp := isp.(*SVirtualMachineSnapshot)
   753  		vm, err = self.CloneVM(ctx, temvm, &sp.snapshotTree.Snapshot, ds, params)
   754  		return
   755  	}
   756  	if len(params.Disks) == 0 {
   757  		err = errors.Error("empty disk config")
   758  		return
   759  	}
   760  	imageInfo := params.Disks[0].ImageInfo
   761  	if imageInfo.ImageType == string(cloudprovider.ImageTypeSystem) {
   762  		temvm, err = self.manager.SearchTemplateVM(imageInfo.ImageExternalId)
   763  		if err != nil {
   764  			err = errors.Wrapf(err, "SEsxiClient.SearchTemplateVM for image %q", imageInfo.ImageExternalId)
   765  			return
   766  		}
   767  		vm, err = self.CloneVM(ctx, temvm, nil, ds, params)
   768  		return
   769  	}
   770  	return self.DoCreateVM(ctx, ds, params)
   771  }
   772  
   773  func (self *SHost) needScsi(disks []SDiskInfo) bool {
   774  	if len(disks) == 0 {
   775  		return false
   776  	}
   777  	for i := range disks {
   778  		driver := disks[i].Driver
   779  		if driver == "" || driver == "scsi" || driver == "pvscsi" {
   780  			return true
   781  		}
   782  	}
   783  	return false
   784  }
   785  
   786  func (self *SHost) addDisks(ctx context.Context, dc *SDatacenter, ds *SDatastore, disks []SDiskInfo, uuid string, objectVm *object.VirtualMachine) (*SVirtualMachine, error) {
   787  	getVM := func() (*SVirtualMachine, error) {
   788  		var moVM mo.VirtualMachine
   789  		err := self.manager.reference2Object(objectVm.Reference(), VIRTUAL_MACHINE_PROPS, &moVM)
   790  		if err != nil {
   791  			return nil, errors.Wrap(err, "fail to fetch virtual machine just created")
   792  		}
   793  
   794  		evm := NewVirtualMachine(self.manager, &moVM, self.datacenter)
   795  		if evm == nil {
   796  			return nil, errors.Error("create successfully but unable to NewVirtualMachine")
   797  		}
   798  		return evm, nil
   799  	}
   800  
   801  	if len(disks) == 0 {
   802  		return getVM()
   803  	}
   804  
   805  	var (
   806  		scsiIdx    = 0
   807  		ideIdx     = 0
   808  		ide1un     = 0
   809  		ide2un     = 1
   810  		unitNumber = 0
   811  		ctrlKey    = 0
   812  	)
   813  	deviceChange := make([]types.BaseVirtualDeviceConfigSpec, 0, 1)
   814  	// add disks
   815  	var rootDiskSizeMb int64
   816  	for i, disk := range disks {
   817  		imagePath := disk.ImagePath
   818  		var size = disk.Size
   819  		if len(imagePath) == 0 {
   820  			if size == 0 {
   821  				size = 30 * 1024
   822  			}
   823  		} else {
   824  			var err error
   825  			imagePath, err = self.FileUrlPathToDsPath(imagePath)
   826  			if err != nil {
   827  				return nil, errors.Wrapf(err, "SHost.FileUrlPathToDsPath")
   828  			}
   829  			newImagePath := fmt.Sprintf("[%s] %s/%s.vmdk", ds.GetRelName(), uuid, uuid)
   830  
   831  			err = self.copyVirtualDisk(imagePath, newImagePath, disk.Driver)
   832  			if err != nil {
   833  				return nil, err
   834  			}
   835  			imagePath = newImagePath
   836  			rootDiskSizeMb = size
   837  		}
   838  		uuid, driver := disk.DiskId, "scsi"
   839  		if len(disk.Driver) > 0 {
   840  			driver = disk.Driver
   841  		}
   842  		if driver == "scsi" || driver == "pvscsi" {
   843  			if self.isVersion50() {
   844  				driver = "scsi"
   845  			}
   846  			ctrlKey = 1000
   847  			unitNumber = scsiIdx
   848  			scsiIdx += 1
   849  			if scsiIdx == 7 {
   850  				scsiIdx++
   851  			}
   852  		} else {
   853  			ideno := ideIdx % 2
   854  			if ideno == 0 {
   855  				unitNumber = ideIdx/2 + ide1un
   856  			} else {
   857  				unitNumber = ideIdx/2 + ide2un
   858  			}
   859  			ctrlKey = 200 + ideno
   860  			ideIdx += 1
   861  		}
   862  		var tds *SDatastore
   863  		var err error
   864  		if disk.StorageId != "" {
   865  			tds, err = self.FindDataStoreById(disk.StorageId)
   866  			if err != nil {
   867  				return nil, errors.Wrapf(err, "unable to find ds %s from host %s", disk.StorageId, self.masterIp)
   868  			}
   869  		} else {
   870  			tds = ds
   871  		}
   872  		log.Debugf("ds: %s, size: %d, image path: %s, uuid: %s, index: %d, ctrlKey: %d, driver: %s, key: %d.", tds.getDatastoreObj().String(), size, imagePath, uuid, unitNumber, ctrlKey, disk.Driver, 2000+i)
   873  		spec := addDevSpec(NewDiskDev(size, SDiskConfig{
   874  			SizeMb:        size,
   875  			Uuid:          uuid,
   876  			ControllerKey: int32(ctrlKey),
   877  			UnitNumber:    int32(unitNumber),
   878  			Key:           int32(2000 + i),
   879  			ImagePath:     imagePath,
   880  			IsRoot:        i == 0,
   881  			Datastore:     tds,
   882  		}))
   883  		if len(imagePath) == 0 {
   884  			spec.FileOperation = "create"
   885  		}
   886  		deviceChange = append(deviceChange, spec)
   887  	}
   888  	log.Infof("deviceChange: %s", jsonutils.Marshal(deviceChange))
   889  
   890  	configSpec := types.VirtualMachineConfigSpec{}
   891  	configSpec.DeviceChange = deviceChange
   892  	task, err := objectVm.Reconfigure(ctx, configSpec)
   893  	if err != nil {
   894  		return nil, errors.Wrap(err, "unable to reconfigure")
   895  	}
   896  	err = task.Wait(ctx)
   897  	if err != nil {
   898  		return nil, errors.Wrap(err, "task.Wait")
   899  	}
   900  
   901  	evm, err := getVM()
   902  	if err != nil {
   903  		return nil, err
   904  	}
   905  
   906  	// resize root disk
   907  	if rootDiskSizeMb > 0 && int64(evm.vdisks[0].GetDiskSizeMB()) != rootDiskSizeMb {
   908  		err = evm.vdisks[0].Resize(ctx, rootDiskSizeMb)
   909  		if err != nil {
   910  			return evm, errors.Wrap(err, "resize for root disk")
   911  		}
   912  	}
   913  	return evm, nil
   914  }
   915  
   916  func (self *SHost) copyVirtualDisk(srcPath, dstPath, diskDriver string) error {
   917  	dm := object.NewVirtualDiskManager(self.manager.client.Client)
   918  	spec := &types.VirtualDiskSpec{
   919  		DiskType: "thin",
   920  	}
   921  	switch diskDriver {
   922  	case "", "scsi", "pvscsi":
   923  		spec.AdapterType = "lsiLogic"
   924  	default:
   925  		spec.AdapterType = "ide"
   926  	}
   927  	task, err := dm.CopyVirtualDisk(self.manager.context, srcPath, self.datacenter.getDcObj(), dstPath, self.datacenter.getDcObj(), spec, true)
   928  	if err != nil {
   929  		return errors.Wrap(err, "unable to CopyVirtualDisk")
   930  	}
   931  	err = task.Wait(self.manager.context)
   932  	if err == nil {
   933  		return nil
   934  	}
   935  	errStr := strings.ToLower(err.Error())
   936  	if !strings.Contains(errStr, "the requested operation is not implemented by the server") {
   937  		return errors.Wrap(err, "wait CopyVirtualDiskTask")
   938  	}
   939  	task, err = dm.CopyVirtualDisk(self.manager.context, srcPath, self.datacenter.getDcObj(), dstPath, self.datacenter.getDcObj(), nil, true)
   940  	if err != nil {
   941  		return errors.Wrap(err, "unable to CopyVirtualDisk")
   942  	}
   943  	err = task.Wait(self.manager.context)
   944  	if err != nil {
   945  		return errors.Wrap(err, "wait CopyVirtualDiskTask")
   946  	}
   947  	return nil
   948  }
   949  
   950  func (self *SHost) DoCreateVM(ctx context.Context, ds *SDatastore, params SCreateVMParam) (needDeploy bool, vm *SVirtualMachine, err error) {
   951  	needDeploy = true
   952  	deviceChange := make([]types.BaseVirtualDeviceConfigSpec, 0, 5)
   953  
   954  	// uuid first
   955  	name := params.Name
   956  	if len(params.Uuid) != 0 {
   957  		name = params.Uuid
   958  	}
   959  	datastorePath := fmt.Sprintf("[%s] ", ds.GetRelName())
   960  
   961  	firmware := ""
   962  	if len(params.Bios) != 0 {
   963  		if params.Bios == "BIOS" {
   964  			firmware = "bios"
   965  		} else if params.Bios == "UEFI" {
   966  			firmware = "efi"
   967  		}
   968  	}
   969  
   970  	guestId := "rhel6_64Guest"
   971  	if params.OsName == "Windows" {
   972  		guestId = "windows7Server64Guest"
   973  	}
   974  
   975  	version := "vmx-10"
   976  	if self.isVersion50() {
   977  		version = "vmx-08"
   978  	}
   979  
   980  	spec := types.VirtualMachineConfigSpec{
   981  		Name:     name,
   982  		Version:  version,
   983  		Uuid:     params.Uuid,
   984  		GuestId:  guestId,
   985  		NumCPUs:  int32(params.Cpu),
   986  		MemoryMB: int64(params.Mem),
   987  		Firmware: firmware,
   988  	}
   989  	spec.Files = &types.VirtualMachineFileInfo{
   990  		VmPathName: datastorePath,
   991  	}
   992  
   993  	deviceChange = append(deviceChange, addDevSpec(NewIDEDev(200, 0)))
   994  	deviceChange = append(deviceChange, addDevSpec(NewIDEDev(200, 1)))
   995  	deviceChange = append(deviceChange, addDevSpec(NewSVGADev(500, 100)))
   996  
   997  	if self.needScsi(params.Disks) {
   998  		driver := "pvscsi"
   999  		if self.isVersion50() {
  1000  			driver = "scsi"
  1001  		}
  1002  		deviceChange = append(deviceChange, addDevSpec(NewSCSIDev(1000, 100, driver)))
  1003  	}
  1004  	cdromPath := params.Cdrom.Path
  1005  	if len(cdromPath) > 0 {
  1006  		needDeploy = false
  1007  		cdromPath, err = self.FileUrlPathToDsPath(cdromPath)
  1008  		if err != nil {
  1009  			err = errors.Wrapf(err, "SHost.FileUrlPathToDsPath for cdrom path")
  1010  			return
  1011  		}
  1012  	}
  1013  	deviceChange = append(deviceChange, addDevSpec(NewCDROMDev(cdromPath, 16000, 201)))
  1014  
  1015  	// add usb to support mouse
  1016  	usbController := addDevSpec(NewUSBController(nil))
  1017  	deviceChange = append(deviceChange, usbController)
  1018  
  1019  	nics := params.Nics
  1020  	for _, nic := range nics {
  1021  		index, _ := nic.Int("index")
  1022  		mac, _ := nic.GetString("mac")
  1023  		bridge, _ := nic.GetString("bridge")
  1024  		driver := "e1000"
  1025  		if nic.Contains("driver") {
  1026  			driver, _ = nic.GetString("driver")
  1027  		}
  1028  		if self.isVersion50() {
  1029  			driver = "e1000"
  1030  		}
  1031  		var vlanId int64 = 1
  1032  		if nic.Contains("vlan") {
  1033  			vlanId, _ = nic.Int("vlan")
  1034  		}
  1035  		dev, err := NewVNICDev(self, mac, driver, bridge, int32(vlanId), 4000, 100, int32(index))
  1036  		if err != nil {
  1037  			return needDeploy, nil, errors.Wrap(err, "NewVNICDev")
  1038  		}
  1039  		deviceChange = append(deviceChange, addDevSpec(dev))
  1040  	}
  1041  
  1042  	spec.DeviceChange = deviceChange
  1043  	dc, err := self.GetDatacenter()
  1044  	if err != nil {
  1045  		err = errors.Wrapf(err, "SHost.GetDatacenter for host '%s'", self.GetId())
  1046  		return
  1047  	}
  1048  	// get vmFloder
  1049  	folders, err := dc.getObjectDatacenter().Folders(ctx)
  1050  	if err != nil {
  1051  		err = errors.Wrap(err, "object.DataCenter.Folders")
  1052  		return
  1053  	}
  1054  	vmFolder := folders.VmFolder
  1055  	resourcePool, err := self.SyncResourcePool(params.ResourcePool)
  1056  	if err != nil {
  1057  		err = errors.Wrap(err, "SyncResourcePool")
  1058  		return
  1059  	}
  1060  	task, err := vmFolder.CreateVM(ctx, spec, resourcePool, self.GetoHostSystem())
  1061  	if err != nil {
  1062  		err = errors.Wrap(err, "VmFolder.Create")
  1063  		return
  1064  	}
  1065  
  1066  	info, err := task.WaitForResult(ctx, nil)
  1067  	if err != nil {
  1068  		err = errors.Wrap(err, "Task.WaitForResult")
  1069  		return
  1070  	}
  1071  	vmRef := info.Result.(types.ManagedObjectReference)
  1072  	objectVM := object.NewVirtualMachine(self.manager.client.Client, vmRef)
  1073  	vm, err = self.addDisks(ctx, dc, ds, params.Disks, params.Uuid, objectVM)
  1074  	return
  1075  }
  1076  
  1077  // If snapshot is not nil, params.Disks will be ignored
  1078  func (host *SHost) CloneVM(ctx context.Context, from *SVirtualMachine, snapshot *types.ManagedObjectReference, ds *SDatastore, params SCreateVMParam) (*SVirtualMachine, error) {
  1079  	ovm := from.getVmObj()
  1080  
  1081  	deviceChange := make([]types.BaseVirtualDeviceConfigSpec, 0, 3)
  1082  
  1083  	addDeviceChange := make([]types.BaseVirtualDeviceConfigSpec, 0, 3)
  1084  
  1085  	// change nic if set
  1086  	if params.Nics != nil && len(params.Nics) > 0 {
  1087  		// get origin nics
  1088  		originNics := make([]types.BaseVirtualDevice, 0, 1)
  1089  		for _, nic := range from.vnics {
  1090  			originNics = append(originNics, nic.getVirtualEthernetCard())
  1091  		}
  1092  		nicIndex := 0
  1093  		nics := params.Nics
  1094  		for _, nic := range nics {
  1095  			index, _ := nic.Int("index")
  1096  			mac, _ := nic.GetString("mac")
  1097  			bridge, _ := nic.GetString("bridge")
  1098  			driver := "e1000"
  1099  			if nic.Contains("driver") {
  1100  				driver, _ = nic.GetString("driver")
  1101  			}
  1102  			if host.isVersion50() {
  1103  				driver = "e1000"
  1104  			}
  1105  			var vlanId int64 = 1
  1106  			if nic.Contains("vlan") {
  1107  				vlanId, _ = nic.Int("vlan")
  1108  			}
  1109  			dev, err := NewVNICDev(host, mac, driver, bridge, int32(vlanId), 4000, 100, int32(index))
  1110  			if err != nil {
  1111  				return nil, errors.Wrap(err, "NewVNICDev")
  1112  			}
  1113  			op := types.VirtualDeviceConfigSpecOperationAdd
  1114  			if nicIndex < len(originNics) {
  1115  				// edit
  1116  				op = types.VirtualDeviceConfigSpecOperationEdit
  1117  				host.changeNic(originNics[nicIndex], dev)
  1118  				dev = originNics[nicIndex]
  1119  				deviceChange = append(deviceChange, &types.VirtualDeviceConfigSpec{
  1120  					Operation: op,
  1121  					Device:    dev,
  1122  				})
  1123  			} else {
  1124  				addDeviceChange = append(addDeviceChange, &types.VirtualDeviceConfigSpec{
  1125  					Operation: op,
  1126  					Device:    dev,
  1127  				})
  1128  			}
  1129  			nicIndex += 1
  1130  		}
  1131  	}
  1132  
  1133  	if len(params.Disks) > 0 && snapshot == nil {
  1134  		driver := params.Disks[0].Driver
  1135  		if driver == "scsi" || driver == "pvscsi" {
  1136  			scsiDevs, err := from.FindController(ctx, "scsi")
  1137  			if err != nil {
  1138  				return nil, errors.Wrap(err, "SVirtualMachine.FindController")
  1139  			}
  1140  			if len(scsiDevs) == 0 {
  1141  				key := from.FindMinDiffKey(1000)
  1142  				driver := "pvscsi"
  1143  				if host.isVersion50() {
  1144  					driver = "scsi"
  1145  				}
  1146  				addDeviceChange = append(deviceChange, addDevSpec(NewSCSIDev(key, 100, driver)))
  1147  			}
  1148  		} else {
  1149  			ideDevs, err := from.FindController(ctx, "ide")
  1150  			if err != nil {
  1151  				return nil, errors.Wrap(err, "SVirtualMachine.FindController")
  1152  			}
  1153  			if len(ideDevs) == 0 {
  1154  				// add ide driver
  1155  				addDeviceChange = append(deviceChange, addDevSpec(NewIDEDev(200, 0)))
  1156  				addDeviceChange = append(deviceChange, addDevSpec(NewIDEDev(200, 1)))
  1157  			}
  1158  		}
  1159  	}
  1160  
  1161  	dc, err := host.GetDatacenter()
  1162  	if err != nil {
  1163  		return nil, errors.Wrapf(err, "SHost.GetDatacenter for host '%s'", host.GetId())
  1164  	}
  1165  	// get vmFloder
  1166  	folders, err := dc.getObjectDatacenter().Folders(ctx)
  1167  	if err != nil {
  1168  		return nil, errors.Wrap(err, "object.DataCenter.Folders")
  1169  	}
  1170  	resourcePool, err := host.SyncResourcePool(params.ResourcePool)
  1171  	if err != nil {
  1172  		return nil, errors.Wrap(err, "SyncResourcePool")
  1173  	}
  1174  
  1175  	folderref := folders.VmFolder.Reference()
  1176  	poolref := resourcePool.Reference()
  1177  	hostref := host.GetoHostSystem().Reference()
  1178  	dsref := ds.getDatastoreObj().Reference()
  1179  	relocateSpec := types.VirtualMachineRelocateSpec{
  1180  		DeviceChange: deviceChange,
  1181  		Folder:       &folderref,
  1182  		Pool:         &poolref,
  1183  		Host:         &hostref,
  1184  		Datastore:    &dsref,
  1185  	}
  1186  	cloneSpec := &types.VirtualMachineCloneSpec{
  1187  		PowerOn:  false,
  1188  		Template: false,
  1189  		Location: relocateSpec,
  1190  		Snapshot: snapshot,
  1191  	}
  1192  
  1193  	// uuid first
  1194  	name := params.Name
  1195  	if len(params.Uuid) != 0 {
  1196  		name = params.Uuid
  1197  	}
  1198  	spec := types.VirtualMachineConfigSpec{
  1199  		Name:     name,
  1200  		Uuid:     params.Uuid,
  1201  		NumCPUs:  int32(params.Cpu),
  1202  		MemoryMB: int64(params.Mem),
  1203  	}
  1204  	cloneSpec.Config = &spec
  1205  	task, err := ovm.Clone(ctx, folders.VmFolder, name, *cloneSpec)
  1206  	if err != nil {
  1207  		return nil, errors.Wrap(err, "object.VirtualMachine.Clone")
  1208  	}
  1209  	info, err := task.WaitForResult(ctx, nil)
  1210  	if err != nil {
  1211  		return nil, errors.Wrap(err, "Task.WaitForResult")
  1212  	}
  1213  
  1214  	var moVM mo.VirtualMachine
  1215  	err = host.manager.reference2Object(info.Result.(types.ManagedObjectReference), VIRTUAL_MACHINE_PROPS, &moVM)
  1216  	if err != nil {
  1217  		return nil, errors.Wrap(err, "fail to fetch virtual machine just created")
  1218  	}
  1219  
  1220  	vm := NewVirtualMachine(host.manager, &moVM, host.datacenter)
  1221  	if vm == nil {
  1222  		return nil, errors.Error("clone successfully but unable to NewVirtualMachine")
  1223  	}
  1224  
  1225  	if snapshot != nil {
  1226  		return vm, nil
  1227  	}
  1228  
  1229  	deviceChange = addDeviceChange
  1230  	// adjust disk
  1231  	var i int
  1232  	if len(params.Disks) > 0 {
  1233  		// resize system disk
  1234  		sysDiskSize := params.Disks[0].Size
  1235  		if sysDiskSize == 0 {
  1236  			sysDiskSize = 30 * 1024
  1237  		}
  1238  		if int64(vm.vdisks[0].GetDiskSizeMB()) != sysDiskSize {
  1239  			vdisk := vm.vdisks[0].getVirtualDisk()
  1240  			originSize := vdisk.CapacityInKB
  1241  			vdisk.CapacityInKB = sysDiskSize * 1024
  1242  			spec := &types.VirtualDeviceConfigSpec{}
  1243  			spec.Operation = types.VirtualDeviceConfigSpecOperationEdit
  1244  			spec.Device = vdisk
  1245  			deviceChange = append(deviceChange, spec)
  1246  			log.Infof("resize system disk: %dGB => %dGB", originSize/1024/1024, sysDiskSize/1024)
  1247  		}
  1248  		// resize existed disk
  1249  		for i = 1; i < len(params.Disks); i++ {
  1250  			if i >= len(vm.vdisks) {
  1251  				break
  1252  			}
  1253  			wantDisk := params.Disks[i]
  1254  			vdisk := vm.vdisks[i]
  1255  			modisk := vdisk.getVirtualDisk()
  1256  			if wantDisk.Size <= int64(vdisk.GetDiskSizeMB()) {
  1257  				continue
  1258  			}
  1259  			originSize := modisk.CapacityInKB
  1260  			modisk.CapacityInKB = wantDisk.Size * 1024
  1261  			spec := &types.VirtualDeviceConfigSpec{}
  1262  			spec.Operation = types.VirtualDeviceConfigSpecOperationEdit
  1263  			spec.Device = vdisk.dev
  1264  			deviceChange = append(deviceChange, spec)
  1265  			log.Infof("resize No.%d data disk: %dGB => %dGB", i, originSize/1024/1024, wantDisk.Size/1024)
  1266  		}
  1267  		// remove extra disk
  1268  		for ; i < len(vm.vdisks); i++ {
  1269  			vdisk := vm.vdisks[i]
  1270  			spec := &types.VirtualDeviceConfigSpec{}
  1271  			spec.Operation = types.VirtualDeviceConfigSpecOperationRemove
  1272  			spec.Device = vdisk.dev
  1273  			spec.FileOperation = types.VirtualDeviceConfigSpecFileOperationDestroy
  1274  			deviceChange = append(deviceChange, spec)
  1275  			log.Infof("remove No.%d data disk", i)
  1276  		}
  1277  		if len(deviceChange) > 0 {
  1278  			spec = types.VirtualMachineConfigSpec{}
  1279  			spec.DeviceChange = deviceChange
  1280  			task, err = vm.getVmObj().Reconfigure(ctx, spec)
  1281  			if err != nil {
  1282  				return vm, errors.Wrap(err, "Reconfigure to resize disks")
  1283  			}
  1284  			err = task.Wait(ctx)
  1285  			if err != nil {
  1286  				return vm, errors.Wrap(err, "Wait task to resize disks")
  1287  			}
  1288  		}
  1289  	}
  1290  	// add data disk
  1291  	for ; i < len(params.Disks); i++ {
  1292  		size := params.Disks[i].Size
  1293  		if size == 0 {
  1294  			size = 30 * 1024
  1295  		}
  1296  		uuid := params.Disks[i].DiskId
  1297  		driver := params.Disks[i].Driver
  1298  		opts := &cloudprovider.GuestDiskCreateOptions{
  1299  			SizeMb:    int(size),
  1300  			UUID:      uuid,
  1301  			Driver:    driver,
  1302  			StorageId: params.Disks[i].StorageId,
  1303  		}
  1304  		_, err := vm.CreateDisk(ctx, opts)
  1305  		if err != nil {
  1306  			log.Errorf("unable to add No.%d disk for vm %s", i, vm.GetId())
  1307  			return vm, nil
  1308  		}
  1309  	}
  1310  	return vm, nil
  1311  }
  1312  
  1313  func (host *SHost) changeNic(device types.BaseVirtualDevice, update types.BaseVirtualDevice) {
  1314  	current := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
  1315  	changed := update.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
  1316  
  1317  	current.Backing = changed.Backing
  1318  	if changed.MacAddress != "" {
  1319  		current.MacAddress = changed.MacAddress
  1320  	}
  1321  	if changed.AddressType != "" {
  1322  		current.AddressType = changed.AddressType
  1323  	}
  1324  }
  1325  
  1326  func (host *SHost) isVersion50() bool {
  1327  	version := host.GetVersion()
  1328  	if strings.HasPrefix(version, "5.") {
  1329  		return true
  1330  	}
  1331  	return false
  1332  }
  1333  
  1334  func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
  1335  	return host.GetIHostNicsInternal(false)
  1336  }
  1337  
  1338  func (host *SHost) GetIHostNicsInternal(debug bool) ([]cloudprovider.ICloudHostNetInterface, error) {
  1339  	nics := host.getNicInfo(debug)
  1340  	inics := make([]cloudprovider.ICloudHostNetInterface, len(nics))
  1341  	for i := 0; i < len(nics); i += 1 {
  1342  		inics[i] = &nics[i]
  1343  	}
  1344  	return inics, nil
  1345  }
  1346  
  1347  func (host *SHost) getLocalStorageCache() (*SDatastoreImageCache, error) {
  1348  	if host.storageCache == nil {
  1349  		sc, err := host.newLocalStorageCache()
  1350  		if err != nil {
  1351  			return nil, err
  1352  		}
  1353  		host.storageCache = sc
  1354  	}
  1355  	return host.storageCache, nil
  1356  }
  1357  
  1358  func (host *SHost) newLocalStorageCache() (*SDatastoreImageCache, error) {
  1359  	ctx := context.Background()
  1360  
  1361  	istorages, err := host.GetIStorages()
  1362  	if err != nil {
  1363  		return nil, err
  1364  	}
  1365  	var errmsg string
  1366  	var cacheDs *SDatastore
  1367  	var maxDs *SDatastore
  1368  	var maxCapacity int64
  1369  	for i := 0; i < len(istorages); i += 1 {
  1370  		ds := istorages[i].(*SDatastore)
  1371  		if !ds.isLocalVMFS() {
  1372  			continue
  1373  		}
  1374  		_, err := ds.CheckFile(ctx, IMAGE_CACHE_DIR_NAME)
  1375  		if err != nil {
  1376  			if errors.Cause(err) != cloudprovider.ErrNotFound {
  1377  				// return nil, err
  1378  				if len(errmsg) > 0 {
  1379  					errmsg += ","
  1380  				}
  1381  				errmsg += err.Error()
  1382  			} else if maxCapacity < ds.GetCapacityMB() {
  1383  				maxCapacity = ds.GetCapacityMB()
  1384  				maxDs = ds
  1385  			}
  1386  		} else {
  1387  			cacheDs = ds
  1388  			break
  1389  		}
  1390  	}
  1391  	if cacheDs == nil {
  1392  		// if no existing image cache dir found, use the one with maximal capacilty
  1393  		cacheDs = maxDs
  1394  	}
  1395  
  1396  	if cacheDs == nil {
  1397  		return nil, fmt.Errorf(errmsg)
  1398  	}
  1399  
  1400  	return &SDatastoreImageCache{
  1401  		datastore: cacheDs,
  1402  		host:      host,
  1403  	}, nil
  1404  }
  1405  
  1406  func (host *SHost) GetManagementServerIp() string {
  1407  	return host.getHostSystem().Summary.ManagementServerIp
  1408  }
  1409  
  1410  func (host *SHost) IsManagedByVCenter() bool {
  1411  	return len(host.getHostSystem().Summary.ManagementServerIp) > 0
  1412  }
  1413  
  1414  func (host *SHost) FindDataStoreById(id string) (*SDatastore, error) {
  1415  	datastores, err := host.GetDataStores()
  1416  	if err != nil {
  1417  		return nil, err
  1418  	}
  1419  	for i := range datastores {
  1420  		if datastores[i].GetGlobalId() == id {
  1421  			return datastores[i].(*SDatastore), nil
  1422  		}
  1423  	}
  1424  	return nil, fmt.Errorf("no such datastore %s", id)
  1425  }
  1426  
  1427  func (host *SHost) GetDataStores() ([]cloudprovider.ICloudStorage, error) {
  1428  	err := host.fetchDatastores()
  1429  	if err != nil {
  1430  		return nil, err
  1431  	}
  1432  	return host.datastores, nil
  1433  }
  1434  
  1435  func (host *SHost) fetchDatastores() error {
  1436  	if host.datastores != nil {
  1437  		return nil
  1438  	}
  1439  
  1440  	dc, err := host.GetDatacenter()
  1441  	if err != nil {
  1442  		return err
  1443  	}
  1444  	dss := host.getHostSystem().Datastore
  1445  	var datastores []mo.Datastore
  1446  	err = host.manager.references2Objects(dss, DATASTORE_PROPS, &datastores)
  1447  	if err != nil {
  1448  		return err
  1449  	}
  1450  	host.datastores = make([]cloudprovider.ICloudStorage, 0)
  1451  	for i := 0; i < len(datastores); i += 1 {
  1452  		ds := NewDatastore(host.manager, &datastores[i], dc)
  1453  		dsId := ds.GetGlobalId()
  1454  		if len(dsId) > 0 {
  1455  			host.datastores = append(host.datastores, ds)
  1456  		}
  1457  	}
  1458  	return nil
  1459  }
  1460  
  1461  func (host *SHost) FileUrlPathToDsPath(path string) (string, error) {
  1462  	var newPath string
  1463  	dss, err := host.GetDataStores()
  1464  	if err != nil {
  1465  		return newPath, err
  1466  	}
  1467  	for _, ds := range dss {
  1468  		rds := ds.(*SDatastore)
  1469  		log.Debugf("rds: %s", rds.GetUrl())
  1470  		if strings.HasPrefix(path, rds.GetUrl()) {
  1471  			newPath = fmt.Sprintf("[%s] %s", rds.GetRelName(), path[len(rds.GetUrl()):])
  1472  			break
  1473  		}
  1474  	}
  1475  	if len(newPath) == 0 {
  1476  		return newPath, fmt.Errorf("path '%s' don't belong any datastore of host '%s'", path, host.GetName())
  1477  	}
  1478  	return newPath, nil
  1479  }
  1480  
  1481  func (host *SHost) FindNetworkByVlanID(vlanID int32) (IVMNetwork, error) {
  1482  	if host.IsActiveVlanID(vlanID) {
  1483  		net, err := host.findBasicNetwork(vlanID)
  1484  		if err != nil {
  1485  			return nil, errors.Wrap(err, "findBasicNetwork error")
  1486  		}
  1487  		if net != nil {
  1488  			return net, nil
  1489  		}
  1490  
  1491  		// no found in basic network
  1492  		dvpg, err := host.findVlanDVPG(vlanID)
  1493  		if err != nil {
  1494  			return nil, errors.Wrap(err, "findVlanDVPG")
  1495  		}
  1496  		return dvpg, nil
  1497  	}
  1498  	n, err := host.findBasicNetwork(vlanID)
  1499  	if err != nil {
  1500  		return nil, errors.Wrap(err, "find Basic network")
  1501  	}
  1502  	if n != nil {
  1503  		return n, err
  1504  	}
  1505  	return host.findNovlanDVPG()
  1506  }
  1507  
  1508  // IsActiveVlanID will detect if vlanID is active that means vlanID in (1, 4095).
  1509  func (host *SHost) IsActiveVlanID(vlanID int32) bool {
  1510  	if vlanID > 1 && vlanID < 4095 {
  1511  		return true
  1512  	}
  1513  	return false
  1514  }
  1515  
  1516  func (host *SHost) findBasicNetwork(vlanID int32) (IVMNetwork, error) {
  1517  	nets, err := host.getBasicNetworks()
  1518  	if err != nil {
  1519  		return nil, err
  1520  	}
  1521  	if len(nets) == 0 {
  1522  		return nil, nil
  1523  	}
  1524  	if !host.IsActiveVlanID(vlanID) {
  1525  		return nets[0], nil
  1526  	}
  1527  	for i := range nets {
  1528  		if nets[i].GetVlanId() == vlanID {
  1529  			return nets[i], nil
  1530  		}
  1531  	}
  1532  	return nil, nil
  1533  }
  1534  
  1535  func (host *SHost) getBasicNetworks() ([]IVMNetwork, error) {
  1536  	nets, err := host.GetNetworks()
  1537  	if err != nil {
  1538  		return nil, errors.Wrap(err, "GetNetworks")
  1539  	}
  1540  	ret := make([]IVMNetwork, 0)
  1541  	for i := range nets {
  1542  		if net, ok := nets[i].(*SNetwork); ok {
  1543  			ret = append(ret, net)
  1544  		}
  1545  	}
  1546  	return ret, nil
  1547  }
  1548  
  1549  func (host *SHost) GetNetworks() ([]IVMNetwork, error) {
  1550  	if host.networks != nil {
  1551  		return host.networks, nil
  1552  	}
  1553  	netMobs := host.getHostSystem().Network
  1554  	netPortMobs := make([]types.ManagedObjectReference, 0)
  1555  	netNetMobs := make([]types.ManagedObjectReference, 0)
  1556  
  1557  	for i := range netMobs {
  1558  		log.Debugf("type: %s value: %s", netMobs[i].Type, netMobs[i].Value)
  1559  		if netMobs[i].Type == "DistributedVirtualPortgroup" {
  1560  			netPortMobs = append(netPortMobs, netMobs[i])
  1561  		} else {
  1562  			netNetMobs = append(netNetMobs, netMobs[i])
  1563  		}
  1564  	}
  1565  
  1566  	nets := make([]IVMNetwork, 0)
  1567  
  1568  	if len(netPortMobs) > 0 {
  1569  		moPorts := make([]mo.DistributedVirtualPortgroup, 0)
  1570  		err := host.manager.references2Objects(netPortMobs, DVPORTGROUP_PROPS, &moPorts)
  1571  		if err != nil {
  1572  			return nil, errors.Wrap(err, "references2Objects")
  1573  		}
  1574  		for i := range moPorts {
  1575  			port := NewDistributedVirtualPortgroup(host.manager, &moPorts[i], host.datacenter)
  1576  			nets = append(nets, port)
  1577  		}
  1578  	}
  1579  	if len(netNetMobs) > 0 {
  1580  		moNets := make([]mo.Network, 0)
  1581  		err := host.manager.references2Objects(netNetMobs, NETWORK_PROPS, &moNets)
  1582  		if err != nil {
  1583  			return nil, errors.Wrap(err, "references2Objects")
  1584  		}
  1585  		for i := range moNets {
  1586  			net := NewNetwork(host.manager, &moNets[i], host.datacenter)
  1587  			nets = append(nets, net)
  1588  		}
  1589  	}
  1590  
  1591  	// network map
  1592  	netMap := make(map[string]IVMNetwork)
  1593  	for i := range nets {
  1594  		netMap[nets[i].GetName()] = nets[i]
  1595  	}
  1596  
  1597  	// fetch all portgroup
  1598  	portgroups := host.getHostSystem().Config.Network.Portgroup
  1599  	for _, pg := range portgroups {
  1600  		net, ok := netMap[pg.Spec.Name]
  1601  		if !ok {
  1602  			log.Infof("SNetwork corresponding to the portgroup whose name is %s could not be found", pg.Spec.Name)
  1603  			continue
  1604  		}
  1605  		net.SetHostPortGroup(pg)
  1606  	}
  1607  	host.networks = nets
  1608  	return host.networks, nil
  1609  }
  1610  
  1611  func (host *SHost) findNovlanDVPG() (*SDistributedVirtualPortgroup, error) {
  1612  	nets, err := host.datacenter.GetNetworks()
  1613  	if err != nil {
  1614  		return nil, errors.Wrap(err, "SHost.datacenter.GetNetworks")
  1615  	}
  1616  	for _, net := range nets {
  1617  		dvpg, ok := net.(*SDistributedVirtualPortgroup)
  1618  		if !ok || !dvpg.ContainHost(host) || len(dvpg.GetActivePorts()) == 0 {
  1619  			continue
  1620  		}
  1621  		nvlan := dvpg.GetVlanId()
  1622  		if !host.IsActiveVlanID(nvlan) {
  1623  			return dvpg, nil
  1624  		}
  1625  	}
  1626  	return nil, nil
  1627  }
  1628  
  1629  func (host *SHost) findDVPGById(id string) (*SDistributedVirtualPortgroup, error) {
  1630  	nets, err := host.datacenter.GetNetworks()
  1631  	if err != nil {
  1632  		return nil, errors.Wrap(err, "SHost.datacenter.GetNetworks")
  1633  	}
  1634  	for _, net := range nets {
  1635  		if dvpg, ok := net.(*SDistributedVirtualPortgroup); ok && dvpg.GetId() == id {
  1636  			return dvpg, nil
  1637  		}
  1638  	}
  1639  	return nil, nil
  1640  }
  1641  
  1642  func (host *SHost) findVlanDVPG(vlanId int32) (*SDistributedVirtualPortgroup, error) {
  1643  	nets, err := host.datacenter.GetNetworks()
  1644  	if err != nil {
  1645  		return nil, errors.Wrap(err, "SHost.datacenter.GetNetworks")
  1646  	}
  1647  	for _, net := range nets {
  1648  		dvpg, ok := net.(*SDistributedVirtualPortgroup)
  1649  		if !ok || len(dvpg.GetActivePorts()) == 0 {
  1650  			continue
  1651  		}
  1652  		nvlan := dvpg.GetVlanId()
  1653  		if nvlan == vlanId {
  1654  			if dvpg.ContainHost(host) {
  1655  				return dvpg, nil
  1656  			}
  1657  			msg := "Find dvpg with correct vlan but it didn't contain this host"
  1658  			log.Debugf(msg)
  1659  			// add host to dvg
  1660  			// err := dvpg.AddHostToDVS(host)
  1661  			// if err != nil {
  1662  			//     return nil, errors.Wrapf(err, "dvpg %s add host to dvs error", dvpg.GetName())
  1663  			// }
  1664  			continue
  1665  		}
  1666  	}
  1667  	return nil, nil
  1668  }
  1669  
  1670  func (host *SHost) GetoHostSystem() *object.HostSystem {
  1671  	return object.NewHostSystem(host.manager.client.Client, host.getHostSystem().Reference())
  1672  }
  1673  
  1674  func (host *SHost) GetResourcePool() (*object.ResourcePool, error) {
  1675  	var err error
  1676  	if host.parent == nil {
  1677  		host.parent, err = host.getParent()
  1678  		if err != nil {
  1679  			return nil, err
  1680  		}
  1681  	}
  1682  	return object.NewResourcePool(host.manager.client.Client, *host.parent.ResourcePool), nil
  1683  }
  1684  
  1685  func (host *SHost) getParent() (*mo.ComputeResource, error) {
  1686  	var mcr *mo.ComputeResource
  1687  	var parent interface{}
  1688  
  1689  	moHost := host.getHostSystem()
  1690  
  1691  	switch moHost.Parent.Type {
  1692  	case "ComputeResource":
  1693  		mcr = new(mo.ComputeResource)
  1694  		parent = mcr
  1695  	case "ClusterComputeResource":
  1696  		mcc := new(mo.ClusterComputeResource)
  1697  		mcr = &mcc.ComputeResource
  1698  		parent = mcc
  1699  	default:
  1700  		return nil, errors.Error(fmt.Sprintf("unknown host parent type: %s", moHost.Parent.Type))
  1701  	}
  1702  
  1703  	err := host.manager.reference2Object(*moHost.Parent, []string{"name", "resourcePool"}, parent)
  1704  	if err != nil {
  1705  		return nil, errors.Wrap(err, "SESXiClient.reference2Object")
  1706  	}
  1707  	return mcr, nil
  1708  }
  1709  
  1710  func (host *SHost) GetResourcePools() ([]mo.ResourcePool, error) {
  1711  	cluster, err := host.GetCluster()
  1712  	if err != nil {
  1713  		return nil, errors.Wrap(err, "GetCluster")
  1714  	}
  1715  	return cluster.ListResourcePools()
  1716  }
  1717  
  1718  func (host *SHost) GetCluster() (*SCluster, error) {
  1719  	cluster, err := host.getCluster()
  1720  	if err != nil {
  1721  		return nil, errors.Wrap(err, "getCluster")
  1722  	}
  1723  	return NewCluster(host.manager, cluster, host.datacenter), nil
  1724  }
  1725  
  1726  func (host *SHost) SyncResourcePool(name string) (*object.ResourcePool, error) {
  1727  	cluster, err := host.GetCluster()
  1728  	if err != nil {
  1729  		log.Errorf("failed to get host %s cluster info: %v", host.GetName(), err)
  1730  		return host.GetResourcePool()
  1731  	}
  1732  	pool, err := cluster.SyncResourcePool(name)
  1733  	if err != nil {
  1734  		log.Errorf("failed to sync resourcePool(%s) for cluster %s error: %v", name, cluster.GetName(), err)
  1735  		return host.GetResourcePool()
  1736  	}
  1737  	return object.NewResourcePool(host.manager.client.Client, pool.Reference()), nil
  1738  }
  1739  
  1740  func (host *SHost) getCluster() (*mo.ClusterComputeResource, error) {
  1741  	moHost := host.getHostSystem()
  1742  	if moHost.Parent.Type != "ClusterComputeResource" {
  1743  		return nil, fmt.Errorf("host %s parent is not the cluster resource", host.GetName())
  1744  	}
  1745  	cluster := &mo.ClusterComputeResource{}
  1746  	err := host.manager.reference2Object(*moHost.Parent, []string{"name", "resourcePool"}, cluster)
  1747  	if err != nil {
  1748  		return nil, errors.Wrap(err, "SESXiClient.reference2Object")
  1749  	}
  1750  	return cluster, nil
  1751  }
  1752  
  1753  func (host *SHost) GetSiblingHosts() ([]*SHost, error) {
  1754  	rp, err := host.getParent()
  1755  	if err != nil {
  1756  		return nil, err
  1757  	}
  1758  	moHosts := make([]mo.HostSystem, 0, len(rp.Host))
  1759  	err = host.manager.references2Objects(rp.Host, HOST_SYSTEM_PROPS, &moHosts)
  1760  	if err != nil {
  1761  		return nil, errors.Wrap(err, "SESXiClient.references2Objects")
  1762  	}
  1763  
  1764  	ret := make([]*SHost, len(moHosts))
  1765  	for i := range moHosts {
  1766  		ret[i] = NewHost(host.manager, &moHosts[i], host.datacenter)
  1767  	}
  1768  	return ret, nil
  1769  }