yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/ucloud/instance.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  	"context"
    19  	"encoding/base64"
    20  	"fmt"
    21  	"strings"
    22  	"time"
    23  
    24  	"yunion.io/x/jsonutils"
    25  	"yunion.io/x/log"
    26  	"yunion.io/x/pkg/errors"
    27  	"yunion.io/x/pkg/utils"
    28  
    29  	billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
    30  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    31  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    32  	"yunion.io/x/cloudmux/pkg/multicloud"
    33  	"yunion.io/x/onecloud/pkg/util/billing"
    34  	"yunion.io/x/onecloud/pkg/util/imagetools"
    35  )
    36  
    37  type SInstance struct {
    38  	multicloud.SInstanceBase
    39  	UcloudTags
    40  
    41  	host *SHost
    42  
    43  	osInfo *imagetools.ImageInfo
    44  
    45  	UHostID            string    `json:"UHostId"`
    46  	Zone               string    `json:"Zone"`
    47  	LifeCycle          string    `json:"LifeCycle"`
    48  	OSName             string    `json:"OsName"`
    49  	ImageID            string    `json:"ImageId"`
    50  	BasicImageID       string    `json:"BasicImageId"`
    51  	BasicImageName     string    `json:"BasicImageName"`
    52  	Tag                string    `json:"Tag"`
    53  	Name               string    `json:"Name"`
    54  	Remark             string    `json:"Remark"`
    55  	State              string    `json:"State"`
    56  	NetworkState       string    `json:"NetworkState"`
    57  	HostType           string    `json:"HostType"`
    58  	StorageType        string    `json:"StorageType"`
    59  	TotalDiskSpace     int       `json:"TotalDiskSpace"`
    60  	DiskSet            []DiskSet `json:"DiskSet"`
    61  	NetCapability      string    `json:"NetCapability"`
    62  	IPSet              []IPSet   `json:"IPSet"`
    63  	SubnetType         string    `json:"SubnetType"`
    64  	ChargeType         string    `json:"ChargeType"`
    65  	ExpireTime         int64     `json:"ExpireTime"`
    66  	AutoRenew          string    `json:"AutoRenew"`
    67  	IsExpire           string    `json:"IsExpire"`
    68  	UHostType          string    `json:"UHostType"`
    69  	OSType             string    `json:"OsType"`
    70  	CreateTime         int64     `json:"CreateTime"`
    71  	CPU                int       `json:"CPU"`
    72  	GPU                int       `json:"GPU"`
    73  	MemoryMB           int       `json:"Memory"`
    74  	TimemachineFeature string    `json:"TimemachineFeature"`
    75  	HotplugFeature     bool      `json:"HotplugFeature"`
    76  	NetCapFeature      bool      `json:"NetCapFeature"`
    77  	BootDiskState      string    `json:"BootDiskState"`
    78  }
    79  
    80  func (self *SInstance) GetSecurityGroupIds() ([]string, error) {
    81  	secgroups, err := self.GetSecurityGroups()
    82  	if err != nil {
    83  		log.Errorln(err)
    84  	}
    85  
    86  	secgroupIds := make([]string, 0)
    87  	for _, secgroup := range secgroups {
    88  		secgroupIds = append(secgroupIds, secgroup.GetId())
    89  	}
    90  
    91  	return secgroupIds, nil
    92  }
    93  
    94  func (self *SInstance) GetProjectId() string {
    95  	return self.host.zone.region.client.projectId
    96  }
    97  
    98  func (self *SInstance) GetError() error {
    99  	return nil
   100  }
   101  
   102  type DiskSet struct {
   103  	DiskID    string `json:"DiskId"`
   104  	DiskType  string `json:"DiskType"`
   105  	Drive     string `json:"Drive"`
   106  	IsBoot    bool   `json:"IsBoot"`
   107  	Size      int    `json:"Size"`
   108  	Encrypted string `json:"Encrypted"`
   109  	Type      string `json:"Type"`
   110  }
   111  
   112  type IPSet struct {
   113  	Type     string `json:"Type"`
   114  	IP       string `json:"IP"`
   115  	IPId     string `json:"IPId"` // IP资源ID (内网IP无对应的资源ID)
   116  	MAC      string `json:"Mac"`
   117  	VPCID    string `json:"VPCId"`
   118  	SubnetID string `json:"SubnetId"`
   119  }
   120  
   121  type SVncInfo struct {
   122  	VNCIP       string `json:"VncIP"`
   123  	VNCPassword string `json:"VncPassword"`
   124  	UHostID     string `json:"UHostId"`
   125  	Action      string `json:"Action"`
   126  	VNCPort     int64  `json:"VncPort"`
   127  }
   128  
   129  func (self *SInstance) GetId() string {
   130  	return self.UHostID
   131  }
   132  
   133  func (self *SInstance) GetName() string {
   134  	if len(self.Name) == 0 {
   135  		return self.GetId()
   136  	}
   137  	return self.Name
   138  }
   139  
   140  func (self *SInstance) GetHostname() string {
   141  	return self.GetName()
   142  }
   143  
   144  func (self *SInstance) GetGlobalId() string {
   145  	return self.GetId()
   146  }
   147  
   148  // 实例状态,枚举值:
   149  // >初始化: Initializing;
   150  // >启动中: Starting;
   151  // > 运行中: Running;
   152  // > 关机中: Stopping;
   153  // >关机: Stopped
   154  // >安装失败: Install Fail;
   155  // >重启中: Rebooting
   156  func (self *SInstance) GetStatus() string {
   157  	switch self.State {
   158  	case "Running":
   159  		return api.VM_RUNNING
   160  	case "Stopped":
   161  		return api.VM_READY
   162  	case "Rebooting":
   163  		return api.VM_STOPPING
   164  	case "Initializing":
   165  		return api.VM_INIT
   166  	case "Starting":
   167  		return api.VM_STARTING
   168  	case "Stopping":
   169  		return api.VM_STOPPING
   170  	case "Install Fail":
   171  		return api.VM_CREATE_FAILED
   172  	default:
   173  		return api.VM_UNKNOWN
   174  	}
   175  }
   176  
   177  func (self *SInstance) Refresh() error {
   178  	new, err := self.host.zone.region.GetInstanceByID(self.GetId())
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	new.host = self.host
   184  	return jsonutils.Update(self, new)
   185  }
   186  
   187  func (self *SInstance) IsEmulated() bool {
   188  	return false
   189  }
   190  
   191  func (self *SInstance) GetSysTags() map[string]string {
   192  	data := map[string]string{}
   193  	// todo: add price key here
   194  	data["zone_ext_id"] = self.host.zone.GetGlobalId()
   195  	if len(self.BasicImageID) > 0 {
   196  		if image, err := self.host.zone.region.GetImage(self.BasicImageID); err != nil {
   197  			log.Errorf("Failed to find image %s for instance %s", self.BasicImageID, self.GetName())
   198  		} else {
   199  			meta := image.GetSysTags()
   200  			for k, v := range meta {
   201  				data[k] = v
   202  			}
   203  		}
   204  	}
   205  
   206  	return data
   207  }
   208  
   209  // 计费模式,枚举值为: Year,按年付费; Month,按月付费; Dynamic,按需付费(需开启权限);
   210  func (self *SInstance) GetBillingType() string {
   211  	switch self.ChargeType {
   212  	case "Year", "Month":
   213  		return billing_api.BILLING_TYPE_PREPAID
   214  	default:
   215  		return billing_api.BILLING_TYPE_POSTPAID
   216  	}
   217  }
   218  
   219  func (self *SInstance) GetCreatedAt() time.Time {
   220  	return time.Unix(self.CreateTime, 0)
   221  }
   222  
   223  func (self *SInstance) GetExpiredAt() time.Time {
   224  	if self.AutoRenew != "Yes" {
   225  		return time.Unix(self.ExpireTime, 0)
   226  	}
   227  
   228  	return time.Time{}
   229  }
   230  
   231  func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
   232  	return self.host
   233  }
   234  
   235  func (self *SInstance) GetLocalDisk(diskId, storageType string, sizeGB int, isBoot bool) SDisk {
   236  	diskType := ""
   237  	if isBoot {
   238  		diskType = "SystemDisk"
   239  	}
   240  
   241  	disk := SDisk{
   242  		SDisk:      multicloud.SDisk{},
   243  		Status:     "Available",
   244  		UHostID:    self.GetId(),
   245  		Name:       diskId,
   246  		Zone:       self.host.zone.GetId(),
   247  		DiskType:   diskType,
   248  		UDiskID:    diskId,
   249  		UHostName:  self.GetName(),
   250  		CreateTime: self.CreateTime,
   251  		SizeGB:     sizeGB,
   252  	}
   253  
   254  	disk.storage = &SStorage{zone: self.host.zone, storageType: storageType}
   255  	return disk
   256  }
   257  
   258  func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
   259  	localDisks := make([]SDisk, 0)
   260  	diskIds := make([]string, 0)
   261  	for _, disk := range self.DiskSet {
   262  		if utils.IsInStringArray(disk.DiskType, []string{api.STORAGE_UCLOUD_LOCAL_NORMAL, api.STORAGE_UCLOUD_LOCAL_SSD}) {
   263  			localDisks = append(localDisks, self.GetLocalDisk(disk.DiskID, disk.DiskType, disk.Size, disk.IsBoot))
   264  		} else {
   265  			diskIds = append(diskIds, disk.DiskID)
   266  		}
   267  	}
   268  
   269  	disks := []SDisk{}
   270  	var err error
   271  	if len(diskIds) > 0 {
   272  		disks, err = self.host.zone.region.GetDisks("", "", diskIds)
   273  		if err != nil {
   274  			return nil, err
   275  		}
   276  	}
   277  
   278  	disks = append(disks, localDisks...)
   279  	idisks := make([]cloudprovider.ICloudDisk, len(disks))
   280  	for i := 0; i < len(disks); i += 1 {
   281  		if disks[i].storage == nil {
   282  			var category string
   283  			if strings.Contains(disks[i].DiskType, "SSD") {
   284  				category = api.STORAGE_UCLOUD_CLOUD_SSD
   285  			} else {
   286  				category = api.STORAGE_UCLOUD_CLOUD_NORMAL
   287  			}
   288  			storage, err := self.host.zone.getStorageByCategory(category)
   289  			if err != nil {
   290  				return nil, err
   291  			}
   292  			disks[i].storage = storage
   293  		}
   294  		idisks[i] = &disks[i]
   295  		// 将系统盘放到第0个位置
   296  		if disks[i].GetDiskType() == api.DISK_TYPE_SYS {
   297  			idisks[0], idisks[i] = idisks[i], idisks[0]
   298  		}
   299  	}
   300  
   301  	return idisks, nil
   302  }
   303  
   304  func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
   305  	nics := make([]cloudprovider.ICloudNic, 0)
   306  
   307  	for _, ip := range self.IPSet {
   308  		if len(ip.SubnetID) == 0 {
   309  			continue
   310  		}
   311  
   312  		nic := SInstanceNic{
   313  			instance: self,
   314  			ipAddr:   ip.IP,
   315  			macAddr:  ip.MAC,
   316  		}
   317  		nics = append(nics, &nic)
   318  	}
   319  
   320  	return nics, nil
   321  }
   322  
   323  // 国际: Internation,BGP: BGP,内网: Private
   324  func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
   325  	for _, ip := range self.IPSet {
   326  		if len(ip.IPId) > 0 {
   327  			eip, err := self.host.zone.region.GetEipById(ip.IPId)
   328  			if err != nil {
   329  				return nil, err
   330  			}
   331  
   332  			return &eip, nil
   333  		}
   334  	}
   335  
   336  	return nil, nil
   337  }
   338  
   339  func (self *SInstance) GetVcpuCount() int {
   340  	return self.CPU
   341  }
   342  
   343  func (self *SInstance) GetVmemSizeMB() int {
   344  	return self.MemoryMB
   345  }
   346  
   347  func (self *SInstance) GetBootOrder() string {
   348  	return "dcn"
   349  }
   350  
   351  func (self *SInstance) GetVga() string {
   352  	return "std"
   353  }
   354  
   355  func (self *SInstance) GetVdi() string {
   356  	return "vnc"
   357  }
   358  
   359  func (ins *SInstance) getNormalizedOsInfo() *imagetools.ImageInfo {
   360  	if ins.osInfo == nil {
   361  		osInfo := imagetools.NormalizeImageInfo(ins.OSName, "", ins.OSType, "", "")
   362  		ins.osInfo = &osInfo
   363  	}
   364  	return ins.osInfo
   365  }
   366  
   367  func (ins *SInstance) GetOsType() cloudprovider.TOsType {
   368  	return cloudprovider.TOsType(ins.getNormalizedOsInfo().OsType)
   369  }
   370  
   371  func (ins *SInstance) GetFullOsName() string {
   372  	return ins.OSName
   373  }
   374  
   375  func (ins *SInstance) GetBios() cloudprovider.TBiosType {
   376  	return cloudprovider.ToBiosType(ins.getNormalizedOsInfo().OsBios)
   377  }
   378  
   379  func (ins *SInstance) GetOsArch() string {
   380  	return ins.getNormalizedOsInfo().OsArch
   381  }
   382  
   383  func (ins *SInstance) GetOsDist() string {
   384  	return ins.getNormalizedOsInfo().OsDistro
   385  }
   386  
   387  func (ins *SInstance) GetOsVersion() string {
   388  	return ins.getNormalizedOsInfo().OsVersion
   389  }
   390  
   391  func (ins *SInstance) GetOsLang() string {
   392  	return ins.getNormalizedOsInfo().OsLang
   393  }
   394  
   395  func (self *SInstance) GetMachine() string {
   396  	return "pc"
   397  }
   398  
   399  func (self *SInstance) GetInstanceType() string {
   400  	// C1.c8.m24
   401  	if strings.HasPrefix(self.HostType, "G") {
   402  		return fmt.Sprintf("%s.c%d.m%d.g%d", self.HostType, self.CPU, self.MemoryMB/1014, self.GPU)
   403  	} else {
   404  		return fmt.Sprintf("%s.c%d.m%d", self.HostType, self.CPU, self.MemoryMB/1014)
   405  	}
   406  }
   407  
   408  func (self *SInstance) AssignSecurityGroup(secgroupId string) error {
   409  	return self.host.zone.region.assignSecurityGroups(self.GetId(), secgroupId)
   410  }
   411  
   412  // https://docs.ucloud.cn/api/unet-api/grant_firewall
   413  func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
   414  	if len(secgroupIds) == 0 {
   415  		return fmt.Errorf("SetSecurityGroups secgroup id should not be empty")
   416  	} else if len(secgroupIds) > 1 {
   417  		return fmt.Errorf("SetSecurityGroups only allowed to assign one secgroup id. %d given", len(secgroupIds))
   418  	}
   419  
   420  	return self.host.zone.region.assignSecurityGroups(self.GetId(), secgroupIds[0])
   421  }
   422  
   423  func (self *SInstance) GetHypervisor() string {
   424  	return api.HYPERVISOR_UCLOUD
   425  }
   426  
   427  func (self *SInstance) StartVM(ctx context.Context) error {
   428  	err := self.host.zone.region.StartVM(self.GetId())
   429  	if err != nil {
   430  		return err
   431  	}
   432  	err = cloudprovider.WaitStatusWithDelay(self, api.VM_RUNNING, 10*time.Second, 10*time.Second, 600*time.Second)
   433  	if err != nil {
   434  		return errors.Wrap(err, "StartVM")
   435  	}
   436  
   437  	return nil
   438  }
   439  
   440  func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
   441  	err := self.host.zone.region.StopVM(self.GetId())
   442  	if err != nil {
   443  		return err
   444  	}
   445  	err = cloudprovider.WaitStatusWithDelay(self, api.VM_READY, 10*time.Second, 10*time.Second, 600*time.Second)
   446  	if err != nil {
   447  		return errors.Wrap(err, "StopVM")
   448  	}
   449  
   450  	return nil
   451  }
   452  
   453  func (self *SInstance) DeleteVM(ctx context.Context) error {
   454  	return self.host.zone.region.DeleteVM(self.GetId())
   455  }
   456  
   457  func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
   458  	return self.host.zone.region.UpdateVM(self.GetId(), name)
   459  }
   460  
   461  func (self *SInstance) UpdateUserData(userData string) error {
   462  	return cloudprovider.ErrNotSupported
   463  }
   464  
   465  // https://docs.ucloud.cn/api/uhost-api/reinstall_uhost_instance
   466  // 1.请确认在重新安装之前,该实例已被关闭;
   467  // 2.请确认该实例未挂载UDisk;
   468  // todo:// 3.将原系统重装为不同类型的系统时(Linux-&gt;Windows),不可选择保留数据盘;
   469  // 4.重装不同版本的系统时(CentOS6-&gt;CentOS7),若选择保留数据盘,请注意数据盘的文件系统格式;
   470  // 5.若主机CPU低于2核,不可重装为Windows系统。
   471  func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
   472  	if len(desc.PublicKey) > 0 {
   473  		return "", fmt.Errorf("DeployVM not support assign ssh keypair")
   474  	}
   475  
   476  	if self.GetStatus() != api.VM_READY {
   477  		return "", fmt.Errorf("DeployVM instance status %s , expected %s.", self.GetStatus(), api.VM_READY)
   478  	}
   479  
   480  	if len(self.DiskSet) > 1 {
   481  		for _, disk := range self.DiskSet {
   482  			if disk.Type == "Data" {
   483  				err := self.host.zone.region.DetachDisk(self.host.zone.GetId(), self.GetId(), disk.DiskID)
   484  				if err != nil {
   485  					return "", fmt.Errorf("RebuildRoot detach disk %s", err)
   486  				}
   487  
   488  				defer self.host.zone.region.AttachDisk(self.host.zone.GetId(), self.GetId(), disk.DiskID)
   489  			}
   490  
   491  		}
   492  	}
   493  
   494  	err := self.host.zone.region.RebuildRoot(self.GetId(), desc.ImageId, desc.Password)
   495  	if err != nil {
   496  		return "", err
   497  	}
   498  
   499  	err = cloudprovider.WaitStatusWithDelay(self, api.VM_RUNNING, 10*time.Second, 15*time.Second, 300*time.Second)
   500  	if err != nil {
   501  		return "", errors.Wrap(err, "RebuildRoot")
   502  	}
   503  
   504  	disks, err := self.GetIDisks()
   505  	if len(disks) > 0 {
   506  		return disks[0].GetId(), nil
   507  	} else {
   508  		return "", fmt.Errorf("RebuildRoot %s", err)
   509  	}
   510  }
   511  
   512  func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
   513  	if len(publicKey) > 0 {
   514  		return fmt.Errorf("DeployVM not support assign ssh keypair")
   515  	}
   516  
   517  	if deleteKeypair {
   518  		return fmt.Errorf("DeployVM not support delete ssh keypair")
   519  	}
   520  
   521  	if self.GetStatus() != api.VM_READY {
   522  		return fmt.Errorf("DeployVM instance status %s , expected %s.", self.GetStatus(), api.VM_READY)
   523  	}
   524  
   525  	if len(password) > 0 {
   526  		err := self.host.zone.region.ResetVMPasswd(self.GetId(), password)
   527  		if err != nil {
   528  			return err
   529  		}
   530  	}
   531  
   532  	err := cloudprovider.WaitStatus(self, api.VM_READY, 10*time.Second, 300*time.Second)
   533  	if err != nil {
   534  		return errors.Wrap(err, "DeployVM")
   535  	}
   536  
   537  	return nil
   538  }
   539  
   540  func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
   541  	if len(config.InstanceType) > 0 {
   542  		return self.ChangeConfig2(ctx, config.InstanceType)
   543  	}
   544  	return self.host.zone.region.ResizeVM(self.GetId(), config.Cpu, config.MemoryMB)
   545  }
   546  
   547  func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error {
   548  	i, err := ParseInstanceType(instanceType)
   549  	if err != nil {
   550  		return err
   551  	}
   552  
   553  	return self.host.zone.region.ResizeVM(self.GetId(), i.CPU, i.MemoryMB)
   554  }
   555  
   556  func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
   557  	return self.host.zone.region.GetInstanceVNCUrl(self.GetId())
   558  }
   559  
   560  func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
   561  	return self.host.zone.region.AttachDisk(self.host.zone.GetId(), self.GetId(), diskId)
   562  }
   563  
   564  func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
   565  	err := self.host.zone.region.DetachDisk(self.host.zone.GetId(), self.GetId(), diskId)
   566  	if err != nil {
   567  		return err
   568  	}
   569  
   570  	disk, err := self.host.zone.region.GetDisk(diskId)
   571  	if err != nil {
   572  		return err
   573  	}
   574  
   575  	disk.storage = &SStorage{zone: self.host.zone, storageType: disk.GetStorageType()}
   576  	err = cloudprovider.WaitStatusWithDelay(disk, api.DISK_READY, 10*time.Second, 10*time.Second, 60*time.Second)
   577  	if err != nil {
   578  		return errors.Wrap(err, "DetachDisk")
   579  	}
   580  
   581  	return nil
   582  }
   583  
   584  func (self *SInstance) Renew(bc billing.SBillingCycle) error {
   585  	// return self.host.zone.region
   586  	return self.host.zone.region.RenewInstance(self.GetId(), bc)
   587  }
   588  
   589  func (self *SInstance) GetSecurityGroups() ([]SSecurityGroup, error) {
   590  	return self.host.zone.region.GetSecurityGroups("", self.GetId(), "")
   591  }
   592  
   593  // https://docs.ucloud.cn/api/uhost-api/get_uhost_instance_vnc_info
   594  func (self *SRegion) GetInstanceVNCUrl(instanceId string) (*cloudprovider.ServerVncOutput, error) {
   595  	params := NewUcloudParams()
   596  	params.Set("UHostId", instanceId)
   597  	vnc := SVncInfo{}
   598  	err := self.DoAction("GetUHostInstanceVncInfo", params, &vnc)
   599  	if err != nil {
   600  		return nil, err
   601  	}
   602  
   603  	ret := &cloudprovider.ServerVncOutput{
   604  		Host:       vnc.VNCIP,
   605  		Port:       vnc.VNCPort,
   606  		Password:   vnc.VNCPassword,
   607  		Hypervisor: api.HYPERVISOR_UCLOUD,
   608  	}
   609  	return ret, nil
   610  }
   611  
   612  // https://docs.ucloud.cn/api/unet-api/grant_firewall
   613  func (self *SRegion) assignSecurityGroups(instanceId string, secgroupId string) error {
   614  	params := NewUcloudParams()
   615  	params.Set("FWId", secgroupId)
   616  	params.Set("ResourceType", "uhost")
   617  	params.Set("ResourceId", instanceId)
   618  
   619  	return self.DoAction("GrantFirewall", params, nil)
   620  }
   621  
   622  // https://docs.ucloud.cn/api/uhost-api/start_uhost_instance
   623  func (self *SRegion) StartVM(instanceId string) error {
   624  	params := NewUcloudParams()
   625  	params.Set("UHostId", instanceId)
   626  
   627  	return self.DoAction("StartUHostInstance", params, nil)
   628  }
   629  
   630  // https://docs.ucloud.cn/api/uhost-api/stop_uhost_instance
   631  func (self *SRegion) StopVM(instanceId string) error {
   632  	params := NewUcloudParams()
   633  	params.Set("UHostId", instanceId)
   634  
   635  	return self.DoAction("StopUHostInstance", params, nil)
   636  }
   637  
   638  // https://docs.ucloud.cn/api/uhost-api/terminate_uhost_instance
   639  func (self *SRegion) DeleteVM(instanceId string) error {
   640  	params := NewUcloudParams()
   641  	params.Set("UHostId", instanceId)
   642  	params.Set("Destroy", 1) // 跳过回收站,直接删除
   643  
   644  	return self.DoAction("TerminateUHostInstance", params, nil)
   645  }
   646  
   647  // https://docs.ucloud.cn/api/uhost-api/modify_uhost_instance_name
   648  func (self *SRegion) UpdateVM(instanceId, name string) error {
   649  	params := NewUcloudParams()
   650  	params.Set("UHostId", instanceId)
   651  	params.Set("Name", name)
   652  
   653  	return self.DoAction("ModifyUHostInstanceName", params, nil)
   654  }
   655  
   656  // ChargeType : Dynamic(按需)/Month(按月)/Year(按年)
   657  func (self *SRegion) RenewInstance(instanceId string, bc billing.SBillingCycle) error {
   658  	params := NewUcloudParams()
   659  	params.Set("ResourceId", instanceId)
   660  	params.Set("ResourceType", "Host")
   661  
   662  	if bc.GetMonths() >= 10 && bc.GetMonths() < 12 {
   663  		params.Set("ChargeType", "Year")
   664  		params.Set("Quantity", 1)
   665  	} else if bc.GetYears() >= 1 {
   666  		params.Set("ChargeType", "Year")
   667  		params.Set("Quantity", bc.GetYears())
   668  	} else {
   669  		params.Set("ChargeType", "Month")
   670  		params.Set("Quantity", bc.GetMonths())
   671  	}
   672  
   673  	return self.DoAction("CreateRenew", params, nil)
   674  }
   675  
   676  // https://docs.ucloud.cn/api/uhost-api/reset_uhost_instance_password
   677  // 该操作需要UHost实例处于关闭状态。
   678  func (self *SRegion) ResetVMPasswd(instanceId, password string) error {
   679  	params := NewUcloudParams()
   680  	params.Set("UHostId", instanceId)
   681  	params.Set("Password", base64.StdEncoding.EncodeToString([]byte(password)))
   682  
   683  	return self.DoAction("ResetUHostInstancePassword", params, nil)
   684  }
   685  
   686  // https://docs.ucloud.cn/api/uhost-api/reinstall_uhost_instance
   687  // (密码格式使用BASE64编码;LoginMode不可变更)
   688  func (self *SRegion) RebuildRoot(instanceId, imageId, password string) error {
   689  	params := NewUcloudParams()
   690  	params.Set("UHostId", instanceId)
   691  	params.Set("Password", base64.StdEncoding.EncodeToString([]byte(password)))
   692  	params.Set("ImageId", imageId)
   693  
   694  	return self.DoAction("ReinstallUHostInstance", params, nil)
   695  }
   696  
   697  // https://docs.ucloud.cn/api/uhost-api/resize_uhost_instance
   698  func (self *SRegion) ResizeVM(instanceId string, cpu, memoryMB int) error {
   699  	params := NewUcloudParams()
   700  	params.Set("UHostId", instanceId)
   701  	params.Set("CPU", cpu)
   702  	params.Set("Memory", memoryMB)
   703  
   704  	return self.DoAction("ResizeUHostInstance", params, nil)
   705  }