| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993 |
- // 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 apsara
- import (
- "context"
- "fmt"
- "sort"
- "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/util/osprofile"
- "yunion.io/x/pkg/util/seclib"
- "yunion.io/x/pkg/utils"
- api "yunion.io/x/cloudmux/pkg/apis/compute"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/cloudmux/pkg/multicloud"
- )
- const (
- // Running:运行中
- //Starting:启动中
- //Stopping:停止中
- //Stopped:已停止
- InstanceStatusStopped = "Stopped"
- InstanceStatusRunning = "Running"
- InstanceStatusStopping = "Stopping"
- InstanceStatusStarting = "Starting"
- )
- type SDedicatedHostAttribute struct {
- DedicatedHostId string
- DedicatedHostName string
- }
- type SIpAddress struct {
- IpAddress []string
- }
- type SNetworkInterfaces struct {
- NetworkInterface []SNetworkInterface
- }
- type SOperationLocks struct {
- LockReason []string
- }
- type SSecurityGroupIds struct {
- SecurityGroupId []string
- }
- // {"NatIpAddress":"","PrivateIpAddress":{"IpAddress":["192.168.220.214"]},"VSwitchId":"vsw-2ze9cqwza4upoyujq1thd","VpcId":"vpc-2zer4jy8ix3i8f0coc5uw"}
- type SVpcAttributes struct {
- NatIpAddress string
- PrivateIpAddress SIpAddress
- VSwitchId string
- VpcId string
- }
- type SInstance struct {
- multicloud.SInstanceBase
- ApsaraTags
- host *SHost
- osInfo *imagetools.ImageInfo
- // idisks []cloudprovider.ICloudDisk
- AutoReleaseTime string
- ClusterId string
- Cpu int
- CreationTime time.Time
- DedicatedHostAttribute SDedicatedHostAttribute
- Description string
- DeviceAvailable bool
- EipAddress SEipAddress
- ExpiredTime time.Time
- GPUAmount int
- GPUSpec string
- HostName string
- ImageId string
- InnerIpAddress SIpAddress
- InstanceChargeType TChargeType
- InstanceId string
- InstanceName string
- InstanceNetworkType string
- InstanceType string
- InstanceTypeFamily string
- InternetChargeType TInternetChargeType
- InternetMaxBandwidthIn int
- InternetMaxBandwidthOut int
- IoOptimized bool
- KeyPairName string
- Memory int
- NetworkInterfaces SNetworkInterfaces
- OSName string
- OSType string
- OperationLocks SOperationLocks
- PublicIpAddress SIpAddress
- Recyclable bool
- RegionId string
- SaleCycle string
- SecurityGroupIds SSecurityGroupIds
- SerialNumber string
- SpotPriceLimit string
- SpotStrategy string
- StartTime time.Time
- Status string
- StoppedMode string
- VlanId string
- VpcAttributes SVpcAttributes
- ZoneId string
- DepartmentInfo
- }
- // {"AutoReleaseTime":"","ClusterId":"","Cpu":1,"CreationTime":"2018-05-23T07:58Z","DedicatedHostAttribute":{"DedicatedHostId":"","DedicatedHostName":""},"Description":"","DeviceAvailable":true,"EipAddress":{"AllocationId":"","InternetChargeType":"","IpAddress":""},"ExpiredTime":"2018-05-30T16:00Z","GPUAmount":0,"GPUSpec":"","HostName":"iZ2ze57isp1ali72tzkjowZ","ImageId":"centos_7_04_64_20G_alibase_201701015.vhd","InnerIpAddress":{"IpAddress":[]},"InstanceChargeType":"PrePaid","InstanceId":"i-2ze57isp1ali72tzkjow","InstanceName":"gaoxianqi-test-7days","InstanceNetworkType":"vpc","InstanceType":"ecs.t5-lc2m1.nano","InstanceTypeFamily":"ecs.t5","InternetChargeType":"PayByBandwidth","InternetMaxBandwidthIn":-1,"InternetMaxBandwidthOut":0,"IoOptimized":true,"Memory":512,"NetworkInterfaces":{"NetworkInterface":[{"MacAddress":"00:16:3e:10:f0:c9","NetworkInterfaceId":"eni-2zecqsagtpztl6x5hu2r","PrimaryIpAddress":"192.168.220.214"}]},"OSName":"CentOS 7.4 64位","OSType":"linux","OperationLocks":{"LockReason":[]},"PublicIpAddress":{"IpAddress":[]},"Recyclable":false,"RegionId":"cn-beijing","ResourceGroupId":"","SaleCycle":"Week","SecurityGroupIds":{"SecurityGroupId":["sg-2zecqsagtpztl6x9zynl"]},"SerialNumber":"df05d9b4-df3d-4400-88d1-5f843f0dd088","SpotPriceLimit":0.000000,"SpotStrategy":"NoSpot","StartTime":"2018-05-23T07:58Z","Status":"Running","StoppedMode":"Not-applicable","VlanId":"","VpcAttributes":{"NatIpAddress":"","PrivateIpAddress":{"IpAddress":["192.168.220.214"]},"VSwitchId":"vsw-2ze9cqwza4upoyujq1thd","VpcId":"vpc-2zer4jy8ix3i8f0coc5uw"},"ZoneId":"cn-beijing-f"}
- func (self *SRegion) GetInstances(zoneId string, ids []string, offset int, limit int) ([]SInstance, int, error) {
- if limit > 50 || limit <= 0 {
- limit = 50
- }
- params := make(map[string]string)
- params["RegionId"] = self.RegionId
- params["PageSize"] = fmt.Sprintf("%d", limit)
- params["PageNumber"] = fmt.Sprintf("%d", (offset/limit)+1)
- if len(zoneId) > 0 {
- params["ZoneId"] = zoneId
- }
- if ids != nil && len(ids) > 0 {
- params["InstanceIds"] = jsonutils.Marshal(ids).String()
- }
- body, err := self.ecsRequest("DescribeInstances", params)
- if err != nil {
- log.Errorf("GetInstances fail %s", err)
- return nil, 0, err
- }
- instances := make([]SInstance, 0)
- err = body.Unmarshal(&instances, "Instances", "Instance")
- if err != nil {
- log.Errorf("Unmarshal security group details fail %s", err)
- return nil, 0, err
- }
- total, _ := body.Int("TotalCount")
- return instances, int(total), nil
- }
- func (self *SInstance) GetSecurityGroupIds() ([]string, error) {
- return self.SecurityGroupIds.SecurityGroupId, nil
- }
- func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
- return self.host
- }
- func (self *SInstance) GetId() string {
- return self.InstanceId
- }
- func (self *SInstance) GetName() string {
- if len(self.InstanceName) > 0 {
- return self.InstanceName
- }
- return self.InstanceId
- }
- func (self *SInstance) GetHostname() string {
- return self.HostName
- }
- func (self *SInstance) GetGlobalId() string {
- return self.InstanceId
- }
- func (self *SInstance) IsEmulated() bool {
- return false
- }
- func (self *SInstance) GetInstanceType() string {
- return self.InstanceType
- }
- func (self *SInstance) GetInternetMaxBandwidthOut() int {
- return self.InternetMaxBandwidthOut
- }
- func (self *SInstance) getVpc() (*SVpc, error) {
- return self.host.zone.region.getVpc(self.VpcAttributes.VpcId)
- }
- type byAttachedTime []SDisk
- func (a byAttachedTime) Len() int { return len(a) }
- func (a byAttachedTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
- func (a byAttachedTime) Less(i, j int) bool {
- switch a[i].GetDiskType() {
- case api.DISK_TYPE_SYS:
- return true
- case api.DISK_TYPE_SWAP:
- switch a[j].GetDiskType() {
- case api.DISK_TYPE_SYS:
- return false
- case api.DISK_TYPE_DATA:
- return true
- }
- case api.DISK_TYPE_DATA:
- if a[j].GetDiskType() != api.DISK_TYPE_DATA {
- return false
- }
- }
- return a[i].AttachedTime.Before(a[j].AttachedTime)
- }
- func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
- disks := []SDisk{}
- disks, err := self.host.zone.region.GetDisks(self.InstanceId, "", "", nil, "")
- if err != nil {
- return nil, errors.Wrapf(err, "GetDisks for %s", self.InstanceId)
- }
- sort.Sort(byAttachedTime(disks))
- idisks := make([]cloudprovider.ICloudDisk, len(disks))
- for i := 0; i < len(disks); i += 1 {
- store, err := self.host.zone.getStorageByCategory(disks[i].Category)
- if err != nil {
- return nil, err
- }
- disks[i].storage = store
- idisks[i] = &disks[i]
- }
- return idisks, nil
- }
- func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
- var (
- networkInterfaces = self.NetworkInterfaces.NetworkInterface
- nics []cloudprovider.ICloudNic
- )
- for _, networkInterface := range networkInterfaces {
- nic := SInstanceNic{
- instance: self,
- id: networkInterface.NetworkInterfaceId,
- ipAddr: networkInterface.PrimaryIpAddress,
- macAddr: networkInterface.MacAddress,
- }
- nics = append(nics, &nic)
- }
- return nics, nil
- }
- func (self *SInstance) GetVcpuCount() int {
- return self.Cpu
- }
- func (self *SInstance) GetVmemSizeMB() int {
- return self.Memory
- }
- func (self *SInstance) GetBootOrder() string {
- return "dcn"
- }
- func (self *SInstance) GetVga() string {
- return "std"
- }
- func (self *SInstance) GetVdi() string {
- return "vnc"
- }
- func (self *SInstance) GetOsType() cloudprovider.TOsType {
- return cloudprovider.TOsType(osprofile.NormalizeOSType(self.OSType))
- }
- func (self *SInstance) GetFullOsName() string {
- return self.OSName
- }
- 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) 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) GetStatus() string {
- // Running:运行中
- //Starting:启动中
- //Stopping:停止中
- //Stopped:已停止
- switch self.Status {
- case InstanceStatusRunning:
- return api.VM_RUNNING
- case InstanceStatusStarting:
- return api.VM_STARTING
- case InstanceStatusStopping:
- return api.VM_STOPPING
- case InstanceStatusStopped:
- return api.VM_READY
- default:
- return api.VM_UNKNOWN
- }
- }
- func (self *SInstance) Refresh() error {
- new, err := self.host.zone.region.GetInstance(self.InstanceId)
- if err != nil {
- return err
- }
- return jsonutils.Update(self, new)
- }
- func (self *SInstance) GetHypervisor() string {
- return api.HYPERVISOR_APSARA
- }
- func (self *SInstance) StartVM(ctx context.Context) error {
- timeout := 300 * time.Second
- interval := 15 * time.Second
- startTime := time.Now()
- for time.Now().Sub(startTime) < timeout {
- err := self.Refresh()
- if err != nil {
- return err
- }
- log.Debugf("status %s expect %s", self.GetStatus(), api.VM_RUNNING)
- if self.GetStatus() == api.VM_RUNNING {
- return nil
- } else if self.GetStatus() == api.VM_READY {
- err := self.host.zone.region.StartVM(self.InstanceId)
- if err != nil {
- return err
- }
- }
- time.Sleep(interval)
- }
- return cloudprovider.ErrTimeout
- }
- func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
- err := self.host.zone.region.StopVM(self.InstanceId, opts.IsForce)
- if err != nil {
- return err
- }
- return cloudprovider.WaitStatus(self, api.VM_READY, 10*time.Second, 300*time.Second) // 5mintues
- }
- func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
- url, err := self.host.zone.region.GetInstanceVNCUrl(self.InstanceId)
- if err != nil {
- return nil, err
- }
- passwd := seclib.RandomPassword(6)
- err = self.host.zone.region.ModifyInstanceVNCUrlPassword(self.InstanceId, passwd)
- if err != nil {
- return nil, err
- }
- ret := &cloudprovider.ServerVncOutput{
- Url: url,
- Password: passwd,
- Protocol: "apsara",
- InstanceId: self.InstanceId,
- Hypervisor: api.HYPERVISOR_APSARA,
- }
- return ret, nil
- }
- func (self *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
- return self.host.zone.region.UpdateVM(self.InstanceId, input)
- }
- func (self *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
- return self.host.zone.region.DeployVM(self.InstanceId, opts)
- }
- func (self *SInstance) RebuildRoot(ctx context.Context, desc *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
- keypair := ""
- if len(desc.PublicKey) > 0 {
- var err error
- keypair, err = self.host.zone.region.syncKeypair(desc.PublicKey)
- if err != nil {
- return "", err
- }
- }
- diskId, err := self.host.zone.region.ReplaceSystemDisk(self.InstanceId, desc.ImageId, desc.Password, keypair, desc.SysSizeGB)
- if err != nil {
- return "", err
- }
- return diskId, nil
- }
- func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
- if len(config.InstanceType) > 0 {
- return self.host.zone.region.ChangeVMConfig2(self.ZoneId, self.InstanceId, config.InstanceType, nil)
- }
- return self.host.zone.region.ChangeVMConfig(self.ZoneId, self.InstanceId, config.Cpu, config.MemoryMB, nil)
- }
- func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
- return self.host.zone.region.AttachDisk(self.InstanceId, diskId)
- }
- func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
- return cloudprovider.RetryOnError(
- func() error {
- return self.host.zone.region.DetachDisk(self.InstanceId, diskId)
- },
- []string{
- `"Code":"InvalidOperation.Conflict"`,
- },
- 4)
- }
- func (self *SRegion) GetInstance(instanceId string) (*SInstance, error) {
- instances, _, err := self.GetInstances("", []string{instanceId}, 0, 1)
- if err != nil {
- return nil, err
- }
- if len(instances) == 0 {
- return nil, cloudprovider.ErrNotFound
- }
- return &instances[0], nil
- }
- func (self *SRegion) CreateInstance(name, hostname string, imageId string, instanceType string, securityGroupIds []string,
- zoneId string, desc string, passwd string, disks []SDisk, vSwitchId string, ipAddr string,
- keypair string, userData string, bc *billing.SBillingCycle, projectId, osType string,
- tags map[string]string,
- ) (string, error) {
- params := make(map[string]string)
- params["RegionId"] = self.RegionId
- params["ImageId"] = imageId
- params["InstanceType"] = instanceType
- for _, id := range securityGroupIds {
- params["SecurityGroupId"] = id
- }
- params["ZoneId"] = zoneId
- params["InstanceName"] = name
- if len(hostname) > 0 {
- params["HostName"] = hostname
- }
- params["Description"] = desc
- params["InternetChargeType"] = "PayByTraffic"
- params["InternetMaxBandwidthIn"] = "200"
- params["InternetMaxBandwidthOut"] = "100"
- if len(passwd) > 0 {
- params["Password"] = passwd
- } else {
- params["PasswordInherit"] = "True"
- }
- if len(projectId) > 0 {
- params["ResourceGroupId"] = projectId
- }
- //{"Code":"InvalidSystemDiskCategory.ValueNotSupported","HostId":"ecs.apsaracs.com","Message":"The specified parameter 'SystemDisk.Category' is not support IoOptimized Instance. Valid Values: cloud_efficiency;cloud_ssd. ","RequestId":"9C9A4E99-5196-42A2-80B6-4762F8F75C90"}
- params["IoOptimized"] = "optimized"
- for i, d := range disks {
- if i == 0 {
- params["SystemDisk.Category"] = d.Category
- if d.Category == api.STORAGE_CLOUD_ESSD_PL2 {
- params["SystemDisk.Category"] = api.STORAGE_CLOUD_ESSD
- params["SystemDisk.PerformanceLevel"] = "PL2"
- }
- if d.Category == api.STORAGE_CLOUD_ESSD_PL3 {
- params["SystemDisk.Category"] = api.STORAGE_CLOUD_ESSD
- params["SystemDisk.PerformanceLevel"] = "PL3"
- }
- params["SystemDisk.Size"] = fmt.Sprintf("%d", d.Size)
- params["SystemDisk.DiskName"] = d.GetName()
- params["SystemDisk.Description"] = d.Description
- } else {
- params[fmt.Sprintf("DataDisk.%d.Size", i)] = fmt.Sprintf("%d", d.Size)
- params[fmt.Sprintf("DataDisk.%d.Category", i)] = d.Category
- if d.Category == api.STORAGE_CLOUD_ESSD_PL2 {
- params[fmt.Sprintf("DataDisk.%d.Category", i)] = api.STORAGE_CLOUD_ESSD
- params[fmt.Sprintf("DataDisk.%d..PerformanceLevel", i)] = "PL2"
- }
- if d.Category == api.STORAGE_CLOUD_ESSD_PL3 {
- params[fmt.Sprintf("DataDisk.%d.Category", i)] = api.STORAGE_CLOUD_ESSD
- params[fmt.Sprintf("DataDisk.%d..PerformanceLevel", i)] = "PL3"
- }
- params[fmt.Sprintf("DataDisk.%d.DiskName", i)] = d.GetName()
- params[fmt.Sprintf("DataDisk.%d.Description", i)] = d.Description
- params[fmt.Sprintf("DataDisk.%d.Encrypted", i)] = "false"
- }
- }
- params["VSwitchId"] = vSwitchId
- params["PrivateIpAddress"] = ipAddr
- if len(keypair) > 0 {
- params["KeyPairName"] = keypair
- }
- if len(userData) > 0 {
- params["UserData"] = userData
- }
- if len(tags) > 0 {
- tagIdx := 0
- for k, v := range tags {
- params[fmt.Sprintf("Tag.%d.Key", tagIdx)] = k
- params[fmt.Sprintf("Tag.%d.Value", tagIdx)] = v
- tagIdx += 1
- }
- }
- if bc != nil {
- params["InstanceChargeType"] = "PrePaid"
- err := billingCycle2Params(bc, params)
- if err != nil {
- return "", err
- }
- if bc.AutoRenew {
- params["AutoRenew"] = "true"
- params["AutoRenewPeriod"] = "1"
- } else {
- params["AutoRenew"] = "False"
- }
- } else {
- params["InstanceChargeType"] = "PostPaid"
- params["SpotStrategy"] = "NoSpot"
- }
- params["ClientToken"] = utils.GenRequestId(20)
- body, err := self.ecsRequest("CreateInstance", params)
- if err != nil {
- log.Errorf("CreateInstance fail %s", err)
- return "", err
- }
- instanceId, _ := body.GetString("InstanceId")
- return instanceId, nil
- }
- func (self *SRegion) doStartVM(instanceId string) error {
- return self.instanceOperation(instanceId, "StartInstance", nil)
- }
- func (self *SRegion) doStopVM(instanceId string, isForce bool) error {
- params := make(map[string]string)
- if isForce {
- params["ForceStop"] = "true"
- } else {
- params["ForceStop"] = "false"
- }
- params["StoppedMode"] = "KeepCharging"
- return self.instanceOperation(instanceId, "StopInstance", params)
- }
- func (self *SRegion) doDeleteVM(instanceId string) error {
- params := make(map[string]string)
- params["TerminateSubscription"] = "true" // terminate expired prepaid instance
- params["Force"] = "true"
- return self.instanceOperation(instanceId, "DeleteInstance", params)
- }
- /*func (self *SRegion) waitInstanceStatus(instanceId string, target string, interval time.Duration, timeout time.Duration) error {
- startTime := time.Now()
- for time.Now().Sub(startTime) < timeout {
- status, err := self.GetInstanceStatus(instanceId)
- if err != nil {
- return err
- }
- if status == target {
- return nil
- }
- time.Sleep(interval)
- }
- return cloudprovider.ErrTimeout
- }
- func (self *SInstance) waitStatus(target string, interval time.Duration, timeout time.Duration) error {
- return self.host.zone.region.waitInstanceStatus(self.InstanceId, target, interval, timeout)
- }*/
- func (self *SRegion) StartVM(instanceId string) error {
- status, err := self.GetInstanceStatus(instanceId)
- if err != nil {
- log.Errorf("Fail to get instance status on StartVM: %s", err)
- return err
- }
- if status != InstanceStatusStopped {
- log.Errorf("StartVM: vm status is %s expect %s", status, InstanceStatusStopped)
- return cloudprovider.ErrInvalidStatus
- }
- return self.doStartVM(instanceId)
- // if err != nil {
- // return err
- // }
- // return self.waitInstanceStatus(instanceId, InstanceStatusRunning, time.Second*5, time.Second*180) // 3 minutes to timeout
- }
- func (self *SRegion) StopVM(instanceId string, isForce bool) error {
- status, err := self.GetInstanceStatus(instanceId)
- if err != nil {
- log.Errorf("Fail to get instance status on StopVM: %s", err)
- return err
- }
- if status == InstanceStatusStopped {
- return nil
- }
- if status != InstanceStatusRunning {
- log.Errorf("StopVM: vm status is %s expect %s", status, InstanceStatusRunning)
- return cloudprovider.ErrInvalidStatus
- }
- return self.doStopVM(instanceId, isForce)
- // if err != nil {
- // return err
- // }
- // return self.waitInstanceStatus(instanceId, InstanceStatusStopped, time.Second*10, time.Second*300) // 5 minutes to timeout
- }
- func (self *SRegion) DeleteVM(instanceId string) error {
- status, err := self.GetInstanceStatus(instanceId)
- if err != nil {
- log.Errorf("Fail to get instance status on DeleteVM: %s", err)
- return err
- }
- log.Debugf("Instance status on delete is %s", status)
- if status != InstanceStatusStopped {
- log.Warningf("DeleteVM: vm status is %s expect %s", status, InstanceStatusStopped)
- }
- return self.doDeleteVM(instanceId)
- // if err != nil {
- // return err
- // }
- // err = self.waitInstanceStatus(instanceId, InstanceStatusRunning, time.Second*10, time.Second*300) // 5 minutes to timeout
- // if err == cloudprovider.ErrNotFound {
- // return nil
- // } else if err == nil {
- // return cloudprovider.ErrTimeout
- // } else {
- // return err
- // }
- }
- func (self *SRegion) DeployVM(instanceId string, opts *cloudprovider.SInstanceDeployOptions) error {
- instance, err := self.GetInstance(instanceId)
- if err != nil {
- return err
- }
- // 修改密钥时直接返回
- if opts.DeleteKeypair {
- err = self.DetachKeyPair(instanceId, instance.KeyPairName)
- if err != nil {
- return err
- }
- }
- var keypairName string
- if len(opts.PublicKey) > 0 {
- var err error
- keypairName, err = self.syncKeypair(opts.PublicKey)
- if err != nil {
- return err
- }
- err = self.AttachKeypair(instanceId, keypairName)
- if err != nil {
- return err
- }
- }
- // 指定密码的情况下,使用指定的密码
- if len(opts.Password) > 0 {
- params := make(map[string]string)
- params["Password"] = opts.Password
- return self.modifyInstanceAttribute(instanceId, params)
- }
- return nil
- }
- func (self *SInstance) DeleteVM(ctx context.Context) error {
- for {
- err := self.host.zone.region.DeleteVM(self.InstanceId)
- if err != nil {
- if isError(err, "IncorrectInstanceStatus.Initializing") {
- log.Infof("The instance is initializing, try later ...")
- time.Sleep(10 * time.Second)
- } else {
- log.Errorf("DeleteVM fail: %s", err)
- return err
- }
- } else {
- break
- }
- }
- return cloudprovider.WaitDeleted(self, 10*time.Second, 300*time.Second) // 5minutes
- }
- func (self *SRegion) UpdateVM(instanceId string, input cloudprovider.SInstanceUpdateOptions) error {
- /*
- api: ModifyInstanceAttribute
- https://help.apsara.com/document_detail/25503.html?spm=a2c4g.11186623.4.1.DrgpjW
- */
- params := make(map[string]string)
- params["InstanceName"] = input.NAME
- params["Description"] = input.Description
- return self.modifyInstanceAttribute(instanceId, params)
- }
- func (self *SRegion) modifyInstanceAttribute(instanceId string, params map[string]string) error {
- params["x-acs-instanceid"] = instanceId
- return self.instanceOperation(instanceId, "ModifyInstanceAttribute", params)
- }
- func (self *SRegion) ReplaceSystemDisk(instanceId string, imageId string, passwd string, keypairName string, sysDiskSizeGB int) (string, error) {
- params := make(map[string]string)
- params["RegionId"] = self.RegionId
- params["InstanceId"] = instanceId
- params["ImageId"] = imageId
- if len(passwd) > 0 {
- params["Password"] = passwd
- } else {
- params["PasswordInherit"] = "True"
- }
- if len(keypairName) > 0 {
- params["KeyPairName"] = keypairName
- }
- if sysDiskSizeGB > 0 {
- params["SystemDisk.Size"] = fmt.Sprintf("%d", sysDiskSizeGB)
- }
- params["x-acs-instanceid"] = instanceId
- body, err := self.ecsRequest("ReplaceSystemDisk", params)
- if err != nil {
- return "", err
- }
- // log.Debugf("%s", body.String())
- return body.GetString("DiskId")
- }
- func (self *SRegion) ChangeVMConfig(zoneId string, instanceId string, ncpu int, vmem int, disks []*SDisk) error {
- // todo: support change disk config?
- params := make(map[string]string)
- instanceTypes, e := self.GetMatchInstanceTypes(ncpu, vmem, 0, zoneId)
- if e != nil {
- return e
- }
- for _, instancetype := range instanceTypes {
- params["InstanceType"] = instancetype.InstanceTypeId
- params["ClientToken"] = utils.GenRequestId(20)
- if err := self.instanceOperation(instanceId, "ModifyInstanceSpec", params); err != nil {
- log.Errorf("Failed for %s: %s", instancetype.InstanceTypeId, err)
- } else {
- return nil
- }
- }
- return fmt.Errorf("Failed to change vm config, specification not supported")
- }
- func (self *SRegion) ChangeVMConfig2(zoneId string, instanceId string, instanceType string, disks []*SDisk) error {
- // todo: support change disk config?
- params := make(map[string]string)
- params["InstanceType"] = instanceType
- params["ClientToken"] = utils.GenRequestId(20)
- if err := self.instanceOperation(instanceId, "ModifyInstanceSpec", params); err != nil {
- log.Errorf("Failed for %s: %s", instanceType, err)
- return fmt.Errorf("Failed to change vm config, specification not supported")
- } else {
- return nil
- }
- }
- func (self *SRegion) DetachDisk(instanceId string, diskId string) error {
- params := make(map[string]string)
- params["InstanceId"] = instanceId
- params["DiskId"] = diskId
- log.Infof("Detach instance %s disk %s", instanceId, diskId)
- params["x-acs-instanceid"] = instanceId
- _, err := self.ecsRequest("DetachDisk", params)
- if err != nil {
- if strings.Contains(err.Error(), "The specified disk has not been attached on the specified instance") {
- return nil
- }
- return errors.Wrap(err, "DetachDisk")
- }
- return nil
- }
- func (self *SRegion) AttachDisk(instanceId string, diskId string) error {
- params := make(map[string]string)
- params["InstanceId"] = instanceId
- params["DiskId"] = diskId
- params["x-acs-instanceid"] = instanceId
- _, err := self.ecsRequest("AttachDisk", params)
- if err != nil {
- log.Errorf("AttachDisk %s to %s fail %s", diskId, instanceId, err)
- return err
- }
- return nil
- }
- func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
- if len(self.EipAddress.IpAddress) > 0 {
- return self.host.zone.region.GetEip(self.EipAddress.AllocationId)
- }
- if len(self.PublicIpAddress.IpAddress) > 0 {
- eip := SEipAddress{}
- eip.region = self.host.zone.region
- eip.IpAddress = self.PublicIpAddress.IpAddress[0]
- eip.InstanceId = self.InstanceId
- eip.InstanceType = EIP_INSTANCE_TYPE_ECS
- eip.Status = EIP_STATUS_INUSE
- eip.AllocationId = self.InstanceId // fixed
- eip.AllocationTime = self.CreationTime
- eip.Bandwidth = self.InternetMaxBandwidthOut
- eip.InternetChargeType = self.InternetChargeType
- return &eip, nil
- }
- return nil, nil
- }
- func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
- return self.host.zone.region.SetSecurityGroups(secgroupIds, self.InstanceId)
- }
- func (self *SInstance) GetBillingType() string {
- return convertChargeType(self.InstanceChargeType)
- }
- func (self *SInstance) GetCreatedAt() time.Time {
- return self.CreationTime
- }
- func (self *SInstance) UpdateUserData(userData string) error {
- return self.host.zone.region.updateInstance(self.InstanceId, "", "", "", "", userData)
- }
- func (self *SInstance) Renew(bc billing.SBillingCycle) error {
- return self.host.zone.region.RenewInstance(self.InstanceId, bc)
- }
- func billingCycle2Params(bc *billing.SBillingCycle, params map[string]string) error {
- if bc.GetMonths() > 0 {
- params["PeriodUnit"] = "Month"
- params["Period"] = fmt.Sprintf("%d", bc.GetMonths())
- } else if bc.GetWeeks() > 0 {
- params["PeriodUnit"] = "Week"
- params["Period"] = fmt.Sprintf("%d", bc.GetWeeks())
- } else {
- return fmt.Errorf("invalid renew time period %s", bc.String())
- }
- return nil
- }
- func (region *SRegion) RenewInstance(instanceId string, bc billing.SBillingCycle) error {
- params := make(map[string]string)
- params["InstanceId"] = instanceId
- err := billingCycle2Params(&bc, params)
- if err != nil {
- return err
- }
- params["ClientToken"] = utils.GenRequestId(20)
- params["x-acs-instanceid"] = instanceId
- _, err = region.ecsRequest("RenewInstance", params)
- if err != nil {
- log.Errorf("RenewInstance fail %s", err)
- return err
- }
- return nil
- }
- func (self *SInstance) GetError() error {
- return nil
- }
- func (region *SRegion) ConvertPublicIpToEip(instanceId string) error {
- params := make(map[string]string)
- params["InstanceId"] = instanceId
- params["RegionId"] = region.RegionId
- _, err := region.ecsRequest("ConvertNatPublicIpToEip", params)
- return err
- }
- func (self *SInstance) ConvertPublicIpToEip() error {
- return self.host.zone.region.ConvertPublicIpToEip(self.InstanceId)
- }
- func (region *SRegion) SetInstanceAutoRenew(instanceId string, autoRenew bool) error {
- params := make(map[string]string)
- params["InstanceId"] = instanceId
- params["RegionId"] = region.RegionId
- if autoRenew {
- params["RenewalStatus"] = "AutoRenewal"
- params["Duration"] = "1"
- } else {
- params["RenewalStatus"] = "Normal"
- }
- params["x-acs-instanceid"] = instanceId
- _, err := region.ecsRequest("ModifyInstanceAutoRenewAttribute", params)
- return err
- }
- type SAutoRenewAttr struct {
- Duration int
- AutoRenewEnabled bool
- RenewalStatus string
- PeriodUnit string
- }
- func (region *SRegion) GetInstanceAutoRenewAttribute(instanceId string) (*SAutoRenewAttr, error) {
- params := make(map[string]string)
- params["InstanceId"] = instanceId
- params["RegionId"] = region.RegionId
- resp, err := region.ecsRequest("DescribeInstanceAutoRenewAttribute", params)
- if err != nil {
- return nil, errors.Wrap(err, "DescribeInstanceAutoRenewAttribute")
- }
- attr := []SAutoRenewAttr{}
- err = resp.Unmarshal(&attr, "InstanceRenewAttributes", "InstanceRenewAttribute")
- if err != nil {
- return nil, errors.Wrap(err, "resp.Unmarshal")
- }
- if len(attr) == 1 {
- return &attr[0], nil
- }
- return nil, fmt.Errorf("get %d auto renew info", len(attr))
- }
- func (self *SInstance) IsAutoRenew() bool {
- attr, err := self.host.zone.region.GetInstanceAutoRenewAttribute(self.InstanceId)
- if err != nil {
- log.Errorf("failed to get instance %s auto renew info", self.InstanceId)
- return false
- }
- return attr.AutoRenewEnabled
- }
- func (self *SInstance) SetAutoRenew(bc billing.SBillingCycle) error {
- return self.host.zone.region.SetInstanceAutoRenew(self.InstanceId, bc.AutoRenew)
- }
- func (self *SInstance) SetTags(tags map[string]string, replace bool) error {
- return self.host.zone.region.SetResourceTags("ecs", "instance", []string{self.InstanceId}, tags, replace)
- }
|