yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcso/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 hcso
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"yunion.io/x/jsonutils"
    22  	"yunion.io/x/log"
    23  	"yunion.io/x/pkg/errors"
    24  	"yunion.io/x/pkg/util/osprofile"
    25  
    26  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    27  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    28  	"yunion.io/x/cloudmux/pkg/multicloud"
    29  	"yunion.io/x/onecloud/pkg/util/billing"
    30  )
    31  
    32  type SHost struct {
    33  	multicloud.SHostBase
    34  	zone *SZone
    35  	vms  []SInstance
    36  
    37  	// 华为私有云没有直接列出host的接口,所有账号下的host都是通过VM反向解析出来的
    38  	// 当账号下没有虚拟机时,如果没有host,会导致调度找不到可用的HOST。
    39  	// 因此,为了避免上述情况始终会在每个zone下返回一台虚拟的host
    40  	IsFake    bool
    41  	projectId string
    42  	Id        string
    43  	Name      string
    44  }
    45  
    46  func (self *SHost) GetId() string {
    47  	return self.Id
    48  }
    49  
    50  func (self *SHost) GetName() string {
    51  	if len(self.Name) > 0 {
    52  		return self.Name
    53  	}
    54  
    55  	return self.Id
    56  }
    57  
    58  func (self *SHost) GetGlobalId() string {
    59  	return self.Id
    60  }
    61  
    62  func (self *SHost) GetStatus() string {
    63  	return api.HOST_STATUS_RUNNING
    64  }
    65  
    66  func (self *SHost) Refresh() error {
    67  	_, err := self.getVMs()
    68  	return errors.Wrap(err, "getVMs")
    69  }
    70  
    71  func (self *SHost) IsEmulated() bool {
    72  	return self.IsFake
    73  }
    74  
    75  func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
    76  	var vms []SInstance
    77  	var err error
    78  	if self.vms != nil {
    79  		vms = self.vms
    80  	} else {
    81  		vms, err = self.getVMs()
    82  		if err != nil {
    83  			return nil, err
    84  		}
    85  	}
    86  
    87  	ret := make([]cloudprovider.ICloudVM, len(vms))
    88  	for i := range vms {
    89  		vm := vms[i]
    90  		vm.host = self
    91  		ret[i] = &vm
    92  	}
    93  
    94  	return ret, nil
    95  }
    96  
    97  func (self *SHost) getVMs() ([]SInstance, error) {
    98  	vms, err := self.zone.region.GetInstances()
    99  	if err != nil {
   100  		return nil, errors.Wrap(err, "GetInstances")
   101  	}
   102  
   103  	ret := []SInstance{}
   104  	for i := range vms {
   105  		vm := vms[i]
   106  		if vm.OSEXTAZAvailabilityZone == self.GetId() && vm.HostID == self.GetId() {
   107  			vm.host = self
   108  			ret = append(ret, vm)
   109  		}
   110  	}
   111  
   112  	self.vms = ret
   113  	return ret, nil
   114  }
   115  
   116  func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
   117  	vm, err := self.zone.region.GetInstanceByID(id)
   118  	if vm.HostID != self.GetId() {
   119  		return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetInstanceByID")
   120  	}
   121  
   122  	vm.host = self
   123  	return &vm, err
   124  }
   125  
   126  func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
   127  	return self.zone.GetIWires()
   128  }
   129  
   130  func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
   131  	return self.zone.GetIStorages()
   132  }
   133  
   134  func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
   135  	return self.zone.GetIStorageById(id)
   136  }
   137  
   138  func (self *SHost) GetEnabled() bool {
   139  	return true
   140  }
   141  
   142  func (self *SHost) GetHostStatus() string {
   143  	return api.HOST_ONLINE
   144  }
   145  
   146  func (self *SHost) GetAccessIp() string {
   147  	return ""
   148  }
   149  
   150  func (self *SHost) GetAccessMac() string {
   151  	return ""
   152  }
   153  
   154  func (self *SHost) GetSysInfo() jsonutils.JSONObject {
   155  	info := jsonutils.NewDict()
   156  	info.Add(jsonutils.NewString(CLOUD_PROVIDER_HUAWEI), "manufacture")
   157  	info.Add(jsonutils.NewString(self.GetId()), "id")
   158  	info.Add(jsonutils.NewString(self.GetName()), "name")
   159  	return info
   160  }
   161  
   162  func (self *SHost) GetSN() string {
   163  	return ""
   164  }
   165  
   166  func (self *SHost) GetCpuCount() int {
   167  	return 0
   168  }
   169  
   170  func (self *SHost) GetNodeCount() int8 {
   171  	return 0
   172  }
   173  
   174  func (self *SHost) GetCpuDesc() string {
   175  	return ""
   176  }
   177  
   178  func (self *SHost) GetCpuMhz() int {
   179  	return 0
   180  }
   181  
   182  func (self *SHost) GetMemSizeMB() int {
   183  	return 0
   184  }
   185  
   186  func (self *SHost) GetStorageSizeMB() int {
   187  	return 0
   188  }
   189  
   190  func (self *SHost) GetStorageType() string {
   191  	return api.DISK_TYPE_HYBRID
   192  }
   193  
   194  func (self *SHost) GetHostType() string {
   195  	return api.HOST_TYPE_HCSO
   196  }
   197  
   198  func (self *SHost) GetIsMaintenance() bool {
   199  	return false
   200  }
   201  
   202  func (self *SHost) GetVersion() string {
   203  	return HUAWEI_API_VERSION
   204  }
   205  
   206  func (self *SHost) GetInstanceById(instanceId string) (*SInstance, error) {
   207  	instance, err := self.zone.region.GetInstanceByID(instanceId)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	if instance.HostID != self.GetId() {
   213  		return nil, errors.Wrap(cloudprovider.ErrNotFound, "GetInstanceByID")
   214  	}
   215  
   216  	instance.host = self
   217  	return &instance, nil
   218  }
   219  
   220  func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
   221  	vmId, err := self._createVM(
   222  		desc.Name, desc.ExternalImageId, desc.SysDisk,
   223  		desc.Cpu, desc.MemoryMB, desc.InstanceType,
   224  		desc.ExternalNetworkId, desc.IpAddr,
   225  		desc.Description, desc.Account,
   226  		desc.Password, desc.DataDisks,
   227  		desc.PublicKey, desc.ExternalSecgroupId,
   228  		desc.UserData, desc.BillingCycle, desc.ProjectId, desc.Tags)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  
   233  	// VM实际调度到的host, 可能不是当前host.因此需要改写host信息
   234  	vm, err := self.zone.region.GetIVMById(vmId)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  
   239  	return vm, err
   240  }
   241  
   242  func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
   243  	return nil, cloudprovider.ErrNotSupported
   244  }
   245  
   246  func (self *SHost) _createVM(name string, imgId string, sysDisk cloudprovider.SDiskInfo, cpu int, memMB int, instanceType string,
   247  	networkId string, ipAddr string, desc string, account string, passwd string,
   248  	diskSizes []cloudprovider.SDiskInfo, publicKey string, secgroupId string,
   249  	userData string, bc *billing.SBillingCycle, projectId string, tags map[string]string) (string, error) {
   250  	net := self.zone.getNetworkById(networkId)
   251  	if net == nil {
   252  		return "", fmt.Errorf("invalid network ID %s", networkId)
   253  	}
   254  
   255  	if net.wire == nil {
   256  		log.Errorf("network's wire is empty")
   257  		return "", fmt.Errorf("network's wire is empty")
   258  	}
   259  
   260  	if net.wire.vpc == nil {
   261  		log.Errorf("wire's vpc is empty")
   262  		return "", fmt.Errorf("wire's vpc is empty")
   263  	}
   264  
   265  	// 同步keypair
   266  	var err error
   267  	keypair := ""
   268  	if len(publicKey) > 0 {
   269  		keypair, err = self.zone.region.syncKeypair(publicKey)
   270  		if err != nil {
   271  			return "", err
   272  		}
   273  	}
   274  
   275  	//  镜像及硬盘配置
   276  	img, err := self.zone.region.GetImage(imgId)
   277  	if err != nil {
   278  		log.Errorf("getiamge %s fail %s", imgId, err)
   279  		return "", err
   280  	}
   281  	if img.Status != ImageStatusActive {
   282  		log.Errorf("image %s status %s", imgId, img.Status)
   283  		return "", fmt.Errorf("image not ready")
   284  	}
   285  	// passwd, windows机型直接使用密码比较方便
   286  	if strings.ToLower(img.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) && len(passwd) > 0 {
   287  		keypair = ""
   288  	}
   289  
   290  	if strings.ToLower(img.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) {
   291  		if u, err := updateWindowsUserData(userData, img.OSVersion, account, passwd); err == nil {
   292  			userData = u
   293  		} else {
   294  			return "", errors.Wrap(err, "SHost.CreateVM.updateWindowsUserData")
   295  		}
   296  	}
   297  
   298  	disks := make([]SDisk, len(diskSizes)+1)
   299  	disks[0].SizeGB = img.SizeGB
   300  	if sysDisk.SizeGB > 0 && sysDisk.SizeGB > img.SizeGB {
   301  		disks[0].SizeGB = sysDisk.SizeGB
   302  	}
   303  	disks[0].VolumeType = sysDisk.StorageType
   304  
   305  	for i, dataDisk := range diskSizes {
   306  		disks[i+1].SizeGB = dataDisk.SizeGB
   307  		disks[i+1].VolumeType = dataDisk.StorageType
   308  	}
   309  
   310  	_, err = self.zone.region.GetSecurityGroupDetails(secgroupId)
   311  	if err != nil {
   312  		return "", errors.Wrap(err, "SHost.CreateVM.GetSecurityGroupDetails")
   313  	}
   314  
   315  	// 创建实例
   316  	if len(instanceType) > 0 {
   317  		log.Debugf("Try instancetype : %s", instanceType)
   318  		vmId, err := self.zone.region.CreateInstance(name, imgId, instanceType, networkId, secgroupId, net.VpcID, self.zone.GetId(), desc, disks, ipAddr, keypair, publicKey, passwd, userData, bc, projectId, tags)
   319  		if err != nil {
   320  			log.Errorf("Failed for %s: %s", instanceType, err)
   321  			return "", fmt.Errorf("create %s failed:%s", instanceType, ErrMessage(err))
   322  		} else {
   323  			return vmId, nil
   324  		}
   325  	}
   326  
   327  	// 匹配实例类型
   328  	instanceTypes, err := self.zone.region.GetMatchInstanceTypes(cpu, memMB, self.zone.GetId())
   329  	if err != nil {
   330  		return "", err
   331  	}
   332  	if len(instanceTypes) == 0 {
   333  		return "", fmt.Errorf("instance type %dC%dMB not avaiable", cpu, memMB)
   334  	}
   335  
   336  	var vmId string
   337  	for _, instType := range instanceTypes {
   338  		instanceTypeId := instType.Name
   339  		log.Debugf("Try instancetype : %s", instanceTypeId)
   340  		vmId, err = self.zone.region.CreateInstance(name, imgId, instanceTypeId, networkId, secgroupId, net.VpcID, self.zone.GetId(), desc, disks, ipAddr, keypair, publicKey, passwd, userData, bc, projectId, tags)
   341  		if err != nil {
   342  			log.Errorf("Failed for %s: %s", instanceTypeId, err)
   343  		} else {
   344  			return vmId, nil
   345  		}
   346  	}
   347  
   348  	return "", fmt.Errorf("create failed: %s", ErrMessage(err))
   349  }