| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package ucloud
- import (
- "context"
- "encoding/base64"
- "fmt"
- "strings"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/billing"
- "yunion.io/x/pkg/util/imagetools"
- "yunion.io/x/pkg/utils"
- billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
- api "yunion.io/x/cloudmux/pkg/apis/compute"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/cloudmux/pkg/multicloud"
- )
- type SInstance struct {
- multicloud.SInstanceBase
- UcloudTags
- host *SHost
- osInfo *imagetools.ImageInfo
- UHostID string `json:"UHostId"`
- Zone string `json:"Zone"`
- LifeCycle string `json:"LifeCycle"`
- OSName string `json:"OsName"`
- ImageID string `json:"ImageId"`
- BasicImageID string `json:"BasicImageId"`
- BasicImageName string `json:"BasicImageName"`
- Tag string `json:"Tag"`
- Name string `json:"Name"`
- Remark string `json:"Remark"`
- State string `json:"State"`
- NetworkState string `json:"NetworkState"`
- HostType string `json:"HostType"`
- StorageType string `json:"StorageType"`
- TotalDiskSpace int `json:"TotalDiskSpace"`
- DiskSet []DiskSet `json:"DiskSet"`
- NetCapability string `json:"NetCapability"`
- IPSet []IPSet `json:"IPSet"`
- SubnetType string `json:"SubnetType"`
- ChargeType string `json:"ChargeType"`
- ExpireTime int64 `json:"ExpireTime"`
- AutoRenew string `json:"AutoRenew"`
- IsExpire string `json:"IsExpire"`
- UHostType string `json:"UHostType"`
- OSType string `json:"OsType"`
- CreateTime int64 `json:"CreateTime"`
- CPU int `json:"CPU"`
- GPU int `json:"GPU"`
- MemoryMB int `json:"Memory"`
- TimemachineFeature string `json:"TimemachineFeature"`
- HotplugFeature bool `json:"HotplugFeature"`
- NetCapFeature bool `json:"NetCapFeature"`
- BootDiskState string `json:"BootDiskState"`
- }
- func (self *SInstance) GetSecurityGroupIds() ([]string, error) {
- secgroups, err := self.GetSecurityGroups()
- if err != nil {
- log.Errorln(err)
- }
- secgroupIds := make([]string, 0)
- for _, secgroup := range secgroups {
- secgroupIds = append(secgroupIds, secgroup.GetId())
- }
- return secgroupIds, nil
- }
- func (self *SInstance) GetProjectId() string {
- return self.host.zone.region.client.projectId
- }
- func (self *SInstance) GetError() error {
- return nil
- }
- type DiskSet struct {
- DiskID string `json:"DiskId"`
- DiskType string `json:"DiskType"`
- Drive string `json:"Drive"`
- IsBoot bool `json:"IsBoot"`
- Size int `json:"Size"`
- Encrypted string `json:"Encrypted"`
- Type string `json:"Type"`
- }
- type IPSet struct {
- Type string `json:"Type"`
- IP string `json:"IP"`
- IPId string `json:"IPId"` // IP资源ID (内网IP无对应的资源ID)
- MAC string `json:"Mac"`
- VPCID string `json:"VPCId"`
- SubnetID string `json:"SubnetId"`
- }
- type SVncInfo struct {
- VNCIP string `json:"VncIP"`
- VNCPassword string `json:"VncPassword"`
- UHostID string `json:"UHostId"`
- Action string `json:"Action"`
- VNCPort int64 `json:"VncPort"`
- }
- func (self *SInstance) GetId() string {
- return self.UHostID
- }
- func (self *SInstance) GetName() string {
- if len(self.Name) == 0 {
- return self.GetId()
- }
- return self.Name
- }
- func (self *SInstance) GetHostname() string {
- return ""
- }
- func (self *SInstance) GetGlobalId() string {
- return self.GetId()
- }
- // 实例状态,枚举值:
- // >初始化: Initializing;
- // >启动中: Starting;
- // > 运行中: Running;
- // > 关机中: Stopping;
- // >关机: Stopped
- // >安装失败: Install Fail;
- // >重启中: Rebooting
- func (self *SInstance) GetStatus() string {
- switch self.State {
- case "Running":
- return api.VM_RUNNING
- case "Stopped":
- return api.VM_READY
- case "Rebooting":
- return api.VM_STOPPING
- case "Initializing":
- return api.VM_INIT
- case "Starting":
- return api.VM_STARTING
- case "Stopping":
- return api.VM_STOPPING
- case "Install Fail":
- return api.VM_CREATE_FAILED
- default:
- return api.VM_UNKNOWN
- }
- }
- func (self *SInstance) Refresh() error {
- new, err := self.host.zone.region.GetInstanceByID(self.GetId())
- if err != nil {
- return err
- }
- new.host = self.host
- return jsonutils.Update(self, new)
- }
- // 计费模式,枚举值为: Year,按年付费; Month,按月付费; Dynamic,按需付费(需开启权限);
- func (self *SInstance) GetBillingType() string {
- switch self.ChargeType {
- case "Year", "Month":
- return billing_api.BILLING_TYPE_PREPAID
- default:
- return billing_api.BILLING_TYPE_POSTPAID
- }
- }
- func (self *SInstance) GetCreatedAt() time.Time {
- return time.Unix(self.CreateTime, 0)
- }
- func (self *SInstance) GetExpiredAt() time.Time {
- if self.AutoRenew != "Yes" {
- return time.Unix(self.ExpireTime, 0)
- }
- return time.Time{}
- }
- func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
- return self.host
- }
- func (self *SInstance) GetLocalDisk(diskId, storageType string, sizeGB int, isBoot bool) SDisk {
- diskType := ""
- if isBoot {
- diskType = "SystemDisk"
- }
- disk := SDisk{
- SDisk: multicloud.SDisk{},
- Status: "Available",
- UHostID: self.GetId(),
- Name: diskId,
- Zone: self.host.zone.GetId(),
- DiskType: diskType,
- UDiskID: diskId,
- UHostName: self.GetName(),
- CreateTime: self.CreateTime,
- SizeGB: sizeGB,
- }
- disk.storage = &SStorage{zone: self.host.zone, storageType: storageType}
- return disk
- }
- func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
- localDisks := make([]SDisk, 0)
- diskIds := make([]string, 0)
- for _, disk := range self.DiskSet {
- if utils.IsInStringArray(disk.DiskType, []string{api.STORAGE_UCLOUD_LOCAL_NORMAL, api.STORAGE_UCLOUD_LOCAL_SSD}) {
- localDisks = append(localDisks, self.GetLocalDisk(disk.DiskID, disk.DiskType, disk.Size, disk.IsBoot))
- } else {
- diskIds = append(diskIds, disk.DiskID)
- }
- }
- disks := []SDisk{}
- var err error
- if len(diskIds) > 0 {
- disks, err = self.host.zone.region.GetDisks("", "", diskIds)
- if err != nil {
- return nil, err
- }
- }
- disks = append(disks, localDisks...)
- idisks := make([]cloudprovider.ICloudDisk, len(disks))
- for i := 0; i < len(disks); i += 1 {
- if disks[i].storage == nil {
- var category string
- if strings.Contains(disks[i].DiskType, "SSD") {
- category = api.STORAGE_UCLOUD_CLOUD_SSD
- } else {
- category = api.STORAGE_UCLOUD_CLOUD_NORMAL
- }
- storage, err := self.host.zone.getStorageByCategory(category)
- if err != nil {
- return nil, err
- }
- disks[i].storage = storage
- }
- idisks[i] = &disks[i]
- // 将系统盘放到第0个位置
- if disks[i].GetDiskType() == api.DISK_TYPE_SYS {
- idisks[0], idisks[i] = idisks[i], idisks[0]
- }
- }
- return idisks, nil
- }
- func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
- nics := make([]cloudprovider.ICloudNic, 0)
- for _, ip := range self.IPSet {
- if len(ip.SubnetID) == 0 {
- continue
- }
- nic := SInstanceNic{
- instance: self,
- ipAddr: ip.IP,
- macAddr: ip.MAC,
- }
- nics = append(nics, &nic)
- }
- return nics, nil
- }
- // 国际: Internation,BGP: BGP,内网: Private
- func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
- for _, ip := range self.IPSet {
- if len(ip.IPId) > 0 {
- eip, err := self.host.zone.region.GetEipById(ip.IPId)
- if err != nil {
- return nil, err
- }
- return &eip, nil
- }
- }
- return nil, nil
- }
- func (self *SInstance) GetVcpuCount() int {
- return self.CPU
- }
- func (self *SInstance) GetVmemSizeMB() int {
- return self.MemoryMB
- }
- func (self *SInstance) GetBootOrder() string {
- return "dcn"
- }
- func (self *SInstance) GetVga() string {
- return "std"
- }
- func (self *SInstance) GetVdi() string {
- return "vnc"
- }
- func (ins *SInstance) getNormalizedOsInfo() *imagetools.ImageInfo {
- if ins.osInfo == nil {
- osInfo := imagetools.NormalizeImageInfo(ins.OSName, "", ins.OSType, "", "")
- ins.osInfo = &osInfo
- }
- return ins.osInfo
- }
- func (ins *SInstance) GetOsType() cloudprovider.TOsType {
- return cloudprovider.TOsType(ins.getNormalizedOsInfo().OsType)
- }
- func (ins *SInstance) GetFullOsName() string {
- return ins.OSName
- }
- func (ins *SInstance) GetBios() cloudprovider.TBiosType {
- return cloudprovider.ToBiosType(ins.getNormalizedOsInfo().OsBios)
- }
- func (ins *SInstance) GetOsArch() string {
- return ins.getNormalizedOsInfo().OsArch
- }
- func (ins *SInstance) GetOsDist() string {
- return ins.getNormalizedOsInfo().OsDistro
- }
- func (ins *SInstance) GetOsVersion() string {
- return ins.getNormalizedOsInfo().OsVersion
- }
- func (ins *SInstance) GetOsLang() string {
- return ins.getNormalizedOsInfo().OsLang
- }
- func (self *SInstance) GetMachine() string {
- return "pc"
- }
- func (self *SInstance) GetInstanceType() string {
- // C1.c8.m24
- if strings.HasPrefix(self.HostType, "G") {
- return fmt.Sprintf("%s.c%d.m%d.g%d", self.HostType, self.CPU, self.MemoryMB/1014, self.GPU)
- } else {
- return fmt.Sprintf("%s.c%d.m%d", self.HostType, self.CPU, self.MemoryMB/1014)
- }
- }
- // https://docs.ucloud.cn/api/unet-api/grant_firewall
- func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
- if len(secgroupIds) == 0 {
- return fmt.Errorf("SetSecurityGroups secgroup id should not be empty")
- } else if len(secgroupIds) > 1 {
- return fmt.Errorf("SetSecurityGroups only allowed to assign one secgroup id. %d given", len(secgroupIds))
- }
- return self.host.zone.region.assignSecurityGroups(self.GetId(), secgroupIds[0])
- }
- func (self *SInstance) GetHypervisor() string {
- return api.HYPERVISOR_UCLOUD
- }
- func (self *SInstance) StartVM(ctx context.Context) error {
- err := self.host.zone.region.StartVM(self.GetId())
- if err != nil {
- return err
- }
- err = cloudprovider.WaitStatusWithDelay(self, api.VM_RUNNING, 10*time.Second, 10*time.Second, 600*time.Second)
- if err != nil {
- return errors.Wrap(err, "StartVM")
- }
- return nil
- }
- func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
- err := self.host.zone.region.StopVM(self.GetId())
- if err != nil {
- return err
- }
- err = cloudprovider.WaitStatusWithDelay(self, api.VM_READY, 10*time.Second, 10*time.Second, 600*time.Second)
- if err != nil {
- return errors.Wrap(err, "StopVM")
- }
- return nil
- }
- func (self *SInstance) DeleteVM(ctx context.Context) error {
- return self.host.zone.region.DeleteVM(self.GetId())
- }
- func (self *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
- return self.host.zone.region.UpdateVM(self.GetId(), input.NAME)
- }
- func (self *SInstance) UpdateUserData(userData string) error {
- return cloudprovider.ErrNotSupported
- }
- // https://docs.ucloud.cn/api/uhost-api/reinstall_uhost_instance
- // 1.请确认在重新安装之前,该实例已被关闭;
- // 2.请确认该实例未挂载UDisk;
- // todo:// 3.将原系统重装为不同类型的系统时(Linux->Windows),不可选择保留数据盘;
- // 4.重装不同版本的系统时(CentOS6->CentOS7),若选择保留数据盘,请注意数据盘的文件系统格式;
- // 5.若主机CPU低于2核,不可重装为Windows系统。
- func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
- if len(desc.PublicKey) > 0 {
- return "", fmt.Errorf("DeployVM not support assign ssh keypair")
- }
- if self.GetStatus() != api.VM_READY {
- return "", fmt.Errorf("DeployVM instance status %s , expected %s.", self.GetStatus(), api.VM_READY)
- }
- if len(self.DiskSet) > 1 {
- for _, disk := range self.DiskSet {
- if disk.Type == "Data" {
- err := self.host.zone.region.DetachDisk(self.host.zone.GetId(), self.GetId(), disk.DiskID)
- if err != nil {
- return "", fmt.Errorf("RebuildRoot detach disk %s", err)
- }
- defer self.host.zone.region.AttachDisk(self.host.zone.GetId(), self.GetId(), disk.DiskID)
- }
- }
- }
- err := self.host.zone.region.RebuildRoot(self.GetId(), desc.ImageId, desc.Password)
- if err != nil {
- return "", err
- }
- err = cloudprovider.WaitStatusWithDelay(self, api.VM_RUNNING, 10*time.Second, 15*time.Second, 300*time.Second)
- if err != nil {
- return "", errors.Wrap(err, "RebuildRoot")
- }
- disks, err := self.GetIDisks()
- if len(disks) > 0 {
- return disks[0].GetId(), nil
- } else {
- return "", fmt.Errorf("RebuildRoot %s", err)
- }
- }
- func (self *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
- if len(opts.PublicKey) > 0 {
- return fmt.Errorf("DeployVM not support assign ssh keypair")
- }
- if opts.DeleteKeypair {
- return fmt.Errorf("DeployVM not support delete ssh keypair")
- }
- if self.GetStatus() != api.VM_READY {
- return fmt.Errorf("DeployVM instance status %s , expected %s.", self.GetStatus(), api.VM_READY)
- }
- if len(opts.Password) > 0 {
- err := self.host.zone.region.ResetVMPasswd(self.GetId(), opts.Password)
- if err != nil {
- return err
- }
- }
- err := cloudprovider.WaitStatus(self, api.VM_READY, 10*time.Second, 300*time.Second)
- if err != nil {
- return errors.Wrap(err, "DeployVM")
- }
- return nil
- }
- func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
- if len(config.InstanceType) > 0 {
- return self.ChangeConfig2(ctx, config.InstanceType)
- }
- return self.host.zone.region.ResizeVM(self.GetId(), config.Cpu, config.MemoryMB)
- }
- func (self *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error {
- i, err := ParseInstanceType(instanceType)
- if err != nil {
- return err
- }
- return self.host.zone.region.ResizeVM(self.GetId(), i.CPU, i.MemoryMB)
- }
- func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
- return self.host.zone.region.GetInstanceVNCUrl(self.GetId())
- }
- func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
- return self.host.zone.region.AttachDisk(self.host.zone.GetId(), self.GetId(), diskId)
- }
- func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
- err := self.host.zone.region.DetachDisk(self.host.zone.GetId(), self.GetId(), diskId)
- if err != nil {
- return err
- }
- disk, err := self.host.zone.region.GetDisk(diskId)
- if err != nil {
- return err
- }
- disk.storage = &SStorage{zone: self.host.zone, storageType: disk.GetStorageType()}
- err = cloudprovider.WaitStatusWithDelay(disk, api.DISK_READY, 10*time.Second, 10*time.Second, 60*time.Second)
- if err != nil {
- return errors.Wrap(err, "DetachDisk")
- }
- return nil
- }
- func (self *SInstance) Renew(bc billing.SBillingCycle) error {
- // return self.host.zone.region
- return self.host.zone.region.RenewInstance(self.GetId(), bc)
- }
- func (self *SInstance) GetSecurityGroups() ([]SSecurityGroup, error) {
- return self.host.zone.region.GetSecurityGroups("", self.GetId(), "")
- }
- // https://docs.ucloud.cn/api/uhost-api/get_uhost_instance_vnc_info
- func (self *SRegion) GetInstanceVNCUrl(instanceId string) (*cloudprovider.ServerVncOutput, error) {
- params := NewUcloudParams()
- params.Set("UHostId", instanceId)
- vnc := SVncInfo{}
- err := self.DoAction("GetUHostInstanceVncInfo", params, &vnc)
- if err != nil {
- return nil, err
- }
- ret := &cloudprovider.ServerVncOutput{
- Host: vnc.VNCIP,
- Port: vnc.VNCPort,
- Password: vnc.VNCPassword,
- Hypervisor: api.HYPERVISOR_UCLOUD,
- }
- return ret, nil
- }
- // https://docs.ucloud.cn/api/unet-api/grant_firewall
- func (self *SRegion) assignSecurityGroups(instanceId string, secgroupId string) error {
- params := NewUcloudParams()
- params.Set("FWId", secgroupId)
- params.Set("ResourceType", "uhost")
- params.Set("ResourceId", instanceId)
- return self.DoAction("GrantFirewall", params, nil)
- }
- // https://docs.ucloud.cn/api/uhost-api/start_uhost_instance
- func (self *SRegion) StartVM(instanceId string) error {
- params := NewUcloudParams()
- params.Set("UHostId", instanceId)
- return self.DoAction("StartUHostInstance", params, nil)
- }
- // https://docs.ucloud.cn/api/uhost-api/stop_uhost_instance
- func (self *SRegion) StopVM(instanceId string) error {
- params := NewUcloudParams()
- params.Set("UHostId", instanceId)
- return self.DoAction("StopUHostInstance", params, nil)
- }
- // https://docs.ucloud.cn/api/uhost-api/terminate_uhost_instance
- func (self *SRegion) DeleteVM(instanceId string) error {
- params := NewUcloudParams()
- params.Set("UHostId", instanceId)
- params.Set("Destroy", 1) // 跳过回收站,直接删除
- return self.DoAction("TerminateUHostInstance", params, nil)
- }
- // https://docs.ucloud.cn/api/uhost-api/modify_uhost_instance_name
- func (self *SRegion) UpdateVM(instanceId, name string) error {
- params := NewUcloudParams()
- params.Set("UHostId", instanceId)
- params.Set("Name", name)
- return self.DoAction("ModifyUHostInstanceName", params, nil)
- }
- // ChargeType : Dynamic(按需)/Month(按月)/Year(按年)
- func (self *SRegion) RenewInstance(instanceId string, bc billing.SBillingCycle) error {
- params := NewUcloudParams()
- params.Set("ResourceId", instanceId)
- params.Set("ResourceType", "Host")
- if bc.GetMonths() >= 10 && bc.GetMonths() < 12 {
- params.Set("ChargeType", "Year")
- params.Set("Quantity", 1)
- } else if bc.GetYears() >= 1 {
- params.Set("ChargeType", "Year")
- params.Set("Quantity", bc.GetYears())
- } else {
- params.Set("ChargeType", "Month")
- params.Set("Quantity", bc.GetMonths())
- }
- return self.DoAction("CreateRenew", params, nil)
- }
- // https://docs.ucloud.cn/api/uhost-api/reset_uhost_instance_password
- // 该操作需要UHost实例处于关闭状态。
- func (self *SRegion) ResetVMPasswd(instanceId, password string) error {
- params := NewUcloudParams()
- params.Set("UHostId", instanceId)
- params.Set("Password", base64.StdEncoding.EncodeToString([]byte(password)))
- return self.DoAction("ResetUHostInstancePassword", params, nil)
- }
- // https://docs.ucloud.cn/api/uhost-api/reinstall_uhost_instance
- // (密码格式使用BASE64编码;LoginMode不可变更)
- func (self *SRegion) RebuildRoot(instanceId, imageId, password string) error {
- params := NewUcloudParams()
- params.Set("UHostId", instanceId)
- params.Set("Password", base64.StdEncoding.EncodeToString([]byte(password)))
- params.Set("ImageId", imageId)
- return self.DoAction("ReinstallUHostInstance", params, nil)
- }
- // https://docs.ucloud.cn/api/uhost-api/resize_uhost_instance
- func (self *SRegion) ResizeVM(instanceId string, cpu, memoryMB int) error {
- params := NewUcloudParams()
- params.Set("UHostId", instanceId)
- params.Set("CPU", cpu)
- params.Set("Memory", memoryMB)
- return self.DoAction("ResizeUHostInstance", params, nil)
- }
|