yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/ucloud/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 ucloud
    16  
    17  import (
    18  	"encoding/base64"
    19  	"fmt"
    20  	"strconv"
    21  	"strings"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    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  
    36  	projectId string
    37  }
    38  
    39  func (self *SHost) GetId() string {
    40  	return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Id, self.zone.GetId())
    41  }
    42  
    43  func (self *SHost) GetName() string {
    44  	return fmt.Sprintf("%s-%s", self.zone.region.client.cpcfg.Name, self.zone.GetId())
    45  }
    46  
    47  func (self *SHost) GetGlobalId() string {
    48  	return self.GetId()
    49  }
    50  
    51  func (self *SHost) GetStatus() string {
    52  	return api.HOST_STATUS_RUNNING
    53  }
    54  
    55  func (self *SHost) Refresh() error {
    56  	return nil
    57  }
    58  
    59  func (self *SHost) IsEmulated() bool {
    60  	return true
    61  }
    62  
    63  func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
    64  	vms, err := self.zone.GetInstances()
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	ivms := make([]cloudprovider.ICloudVM, len(vms))
    70  	for i := 0; i < len(vms); i += 1 {
    71  		vms[i].host = self
    72  		ivms[i] = &vms[i]
    73  	}
    74  
    75  	return ivms, nil
    76  }
    77  
    78  func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
    79  	vm, err := self.zone.region.GetInstanceByID(id)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	vm.host = self
    85  	return &vm, nil
    86  }
    87  
    88  func (self *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) {
    89  	return self.zone.GetIWires()
    90  }
    91  
    92  func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
    93  	return self.zone.GetIStorages()
    94  }
    95  
    96  func (self *SHost) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
    97  	return self.zone.GetIStorageById(id)
    98  }
    99  
   100  func (self *SHost) GetEnabled() bool {
   101  	return true
   102  }
   103  
   104  func (self *SHost) GetHostStatus() string {
   105  	return api.HOST_ONLINE
   106  }
   107  
   108  func (self *SHost) GetAccessIp() string {
   109  	return ""
   110  }
   111  
   112  func (self *SHost) GetAccessMac() string {
   113  	return ""
   114  }
   115  
   116  func (self *SHost) GetSysInfo() jsonutils.JSONObject {
   117  	info := jsonutils.NewDict()
   118  	info.Add(jsonutils.NewString(CLOUD_PROVIDER_UCLOUD), "manufacture")
   119  	return info
   120  }
   121  
   122  func (self *SHost) GetSN() string {
   123  	return ""
   124  }
   125  
   126  func (self *SHost) GetCpuCount() int {
   127  	return 0
   128  }
   129  
   130  func (self *SHost) GetNodeCount() int8 {
   131  	return 0
   132  }
   133  
   134  func (self *SHost) GetCpuDesc() string {
   135  	return ""
   136  }
   137  
   138  func (self *SHost) GetCpuMhz() int {
   139  	return 0
   140  }
   141  
   142  func (self *SHost) GetMemSizeMB() int {
   143  	return 0
   144  }
   145  
   146  func (self *SHost) GetStorageSizeMB() int {
   147  	return 0
   148  }
   149  
   150  func (self *SHost) GetStorageType() string {
   151  	return api.DISK_TYPE_HYBRID
   152  }
   153  
   154  func (self *SHost) GetHostType() string {
   155  	return api.HOST_TYPE_UCLOUD
   156  }
   157  
   158  func (self *SHost) GetIsMaintenance() bool {
   159  	return false
   160  }
   161  
   162  func (self *SHost) GetVersion() string {
   163  	return UCLOUD_API_VERSION
   164  }
   165  
   166  // 不支持user data
   167  // 不支持指定keypair
   168  func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
   169  	vmId, err := self._createVM(desc.Name, desc.ExternalImageId, desc.SysDisk, desc.Cpu, desc.MemoryMB, desc.InstanceType, desc.ExternalNetworkId, desc.IpAddr, desc.Description, desc.Password, desc.DataDisks, desc.ExternalSecgroupId, desc.BillingCycle)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	vm, err := self.zone.region.GetInstanceByID(vmId)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  
   179  	vm.host = self
   180  	return &vm, err
   181  }
   182  
   183  func (self *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
   184  	return nil, cloudprovider.ErrNotSupported
   185  }
   186  
   187  type SInstanceType struct {
   188  	UHostType string
   189  	CPU       int
   190  	MemoryMB  int
   191  	GPU       int
   192  }
   193  
   194  func ParseInstanceType(instanceType string) (SInstanceType, error) {
   195  	i := SInstanceType{}
   196  	segs := strings.Split(instanceType, ".")
   197  	if len(segs) < 3 {
   198  		return i, fmt.Errorf("invalid instance type %s", instanceType)
   199  	} else if len(segs) >= 4 {
   200  		gpu, err := strconv.Atoi(strings.TrimLeft(segs[3], "g"))
   201  		if err != nil {
   202  			return i, err
   203  		}
   204  
   205  		i.GPU = gpu
   206  	}
   207  
   208  	cpu, err := strconv.Atoi(strings.TrimLeft(segs[1], "c"))
   209  	if err != nil {
   210  		return i, err
   211  	}
   212  
   213  	mem, err := strconv.Atoi(strings.TrimLeft(segs[2], "m"))
   214  	if err != nil {
   215  		return i, err
   216  	}
   217  
   218  	i.UHostType = segs[0]
   219  	i.CPU = cpu
   220  	i.MemoryMB = mem * 1024
   221  	return i, nil
   222  }
   223  
   224  func (self *SHost) _createVM(name, imgId string, sysDisk cloudprovider.SDiskInfo, cpu, memMB int, instanceType string,
   225  	networkId, ipAddr, desc, passwd string,
   226  	dataDisks []cloudprovider.SDiskInfo, secgroupId string, bc *billing.SBillingCycle) (string, error) {
   227  	// 网络配置及安全组绑定
   228  	net, _ := self.zone.region.getNetwork(networkId)
   229  	if net == nil {
   230  		return "", fmt.Errorf("invalid network ID %s", networkId)
   231  	}
   232  
   233  	if net.wire == nil {
   234  		log.Errorf("network's wire is empty")
   235  		return "", fmt.Errorf("network's wire is empty")
   236  	}
   237  
   238  	if net.wire.vpc == nil {
   239  		log.Errorf("wire's vpc is empty")
   240  		return "", fmt.Errorf("wire's vpc is empty")
   241  	}
   242  
   243  	if len(secgroupId) == 0 {
   244  		return "", fmt.Errorf("CreateVM no secgroupId specified")
   245  	}
   246  
   247  	if len(passwd) == 0 {
   248  		return "", fmt.Errorf("CreateVM password should not be emtpty")
   249  	}
   250  
   251  	// 镜像及硬盘配置
   252  	img, err := self.zone.region.GetImage(imgId)
   253  	if err != nil {
   254  		log.Errorf("GetImage %s fail %s", imgId, err)
   255  		return "", err
   256  	}
   257  	if img.GetStatus() != api.CACHED_IMAGE_STATUS_ACTIVE {
   258  		log.Errorf("image %s status %s, expect %s", imgId, img.GetStatus(), api.CACHED_IMAGE_STATUS_ACTIVE)
   259  		return "", fmt.Errorf("image not ready")
   260  	}
   261  
   262  	disks := make([]SDisk, len(dataDisks)+1)
   263  	disks[0].SizeGB = int(img.ImageSizeGB)
   264  	if sysDisk.SizeGB > 0 && sysDisk.SizeGB > int(img.ImageSizeGB) {
   265  		disks[0].SizeGB = sysDisk.SizeGB
   266  	}
   267  	disks[0].DiskType = sysDisk.StorageType
   268  
   269  	for i, dataDisk := range dataDisks {
   270  		disks[i+1].SizeGB = dataDisk.SizeGB
   271  		disks[i+1].DiskType = dataDisk.StorageType
   272  	}
   273  
   274  	// 创建实例
   275  	// https://docs.ucloud.cn/api/uhost-api/uhost_type
   276  	// https://docs.ucloud.cn/compute/uhost/introduction/uhost/type
   277  	var vmId string
   278  	i, err := ParseInstanceType(instanceType)
   279  	if err != nil {
   280  		if cpu <= 0 || memMB <= 0 {
   281  			return "", err
   282  		} else {
   283  			i.UHostType = "N2"
   284  			i.CPU = cpu
   285  			i.MemoryMB = memMB
   286  		}
   287  	}
   288  
   289  	vmId, err = self.zone.region.CreateInstance(name, imgId, i.UHostType, passwd, net.wire.vpc.GetId(), networkId, secgroupId, self.zone.ZoneId, desc, ipAddr, i.CPU, i.MemoryMB, i.GPU, disks, bc)
   290  	if err != nil {
   291  		return "", fmt.Errorf("Failed to create: %v", err)
   292  	}
   293  
   294  	return vmId, nil
   295  }
   296  
   297  // https://docs.ucloud.cn/api/uhost-api/create_uhost_instance
   298  // https://docs.ucloud.cn/api/uhost-api/specification
   299  // 支持8-30位字符, 不能包含[A-Z],[a-z],[0-9]和[()`~!@#$%^&*-+=_|{}[]:;'<>,.?/]之外的非法字符
   300  func (self *SRegion) CreateInstance(name, imageId, hostType, password, vpcId, SubnetId, securityGroupId,
   301  	zoneId, desc, ipAddr string, cpu, memMB, gpu int, disks []SDisk, bc *billing.SBillingCycle) (string, error) {
   302  	params := NewUcloudParams()
   303  	params.Set("Zone", zoneId)
   304  	params.Set("ImageId", imageId)
   305  	params.Set("Password", base64.StdEncoding.EncodeToString([]byte(password)))
   306  	params.Set("LoginMode", "Password")
   307  	params.Set("Name", name)
   308  	params.Set("UHostType", hostType)
   309  	params.Set("CPU", cpu)
   310  	params.Set("Memory", memMB)
   311  	params.Set("VPCId", vpcId)
   312  	params.Set("SubnetId", SubnetId)
   313  	params.Set("SecurityGroupId", securityGroupId)
   314  	if gpu > 0 {
   315  		params.Set("GPU", gpu)
   316  	}
   317  
   318  	if bc != nil && bc.GetMonths() >= 1 && bc.GetMonths() < 10 {
   319  		params.Set("ChargeType", "Month")
   320  		params.Set("Quantity", bc.GetMonths())
   321  	} else if bc != nil && bc.GetMonths() >= 10 && bc.GetMonths() < 12 {
   322  		params.Set("ChargeType", "Year")
   323  		params.Set("Quantity", 1)
   324  	} else if bc != nil && bc.GetYears() >= 1 {
   325  		params.Set("ChargeType", "Year")
   326  		params.Set("Quantity", bc.GetYears())
   327  	} else {
   328  		params.Set("ChargeType", "Dynamic")
   329  	}
   330  
   331  	// boot disk
   332  	params.Set("Disks.0.IsBoot", "True")
   333  	params.Set("Disks.0.Type", disks[0].DiskType)
   334  	params.Set("Disks.0.Size", disks[0].SizeGB)
   335  
   336  	// data disk
   337  	for i, disk := range disks[1:] {
   338  		N := i + 1
   339  		params.Set(fmt.Sprintf("Disks.%d.IsBoot", N), "False")
   340  		params.Set(fmt.Sprintf("Disks.%d.Type", N), disk.DiskType)
   341  		params.Set(fmt.Sprintf("Disks.%d.Size", N), disk.SizeGB)
   342  	}
   343  
   344  	type Ret struct {
   345  		UHostIds []string
   346  	}
   347  
   348  	ret := Ret{}
   349  	err := self.DoAction("CreateUHostInstance", params, &ret)
   350  	if err != nil {
   351  		return "", err
   352  	}
   353  
   354  	if len(ret.UHostIds) == 1 {
   355  		return ret.UHostIds[0], nil
   356  	}
   357  
   358  	return "", fmt.Errorf("CreateInstance %d instance created. %s", len(ret.UHostIds), ret.UHostIds)
   359  }