| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719 |
- // 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 ctyun
- import (
- "context"
- "fmt"
- "strings"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/billing"
- "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
- CtyunTags
- multicloud.SBillingBase
- host *SHost
- image *SImage
- AzName string
- ExpiredTime time.Time
- CreatedTime time.Time
- ProjectId string
- AttachedVolume []string
- InstanceId string
- DisplayName string
- InstanceName string
- OsType int
- InstanceStatus string
- OnDemand bool
- KeypairName string
- Addresses []struct {
- VpcName string
- AddressList []struct {
- Addr string
- Version int
- Type string
- }
- }
- SecGroupList []struct {
- SecurityGroupName string
- SecurityGroupId string
- }
- VipInfoList []interface{}
- AffinityGroup string
- Image struct {
- ImageId string
- ImageName string
- }
- Flavor struct {
- FlavorId string
- FlavorName string
- FlavorCPU int
- FlavorRAM int
- GpuType string
- GpuCount string
- GpuVendor string
- VideoMemSize string
- }
- ResourceId string
- UpdatedTime time.Time
- AvailableDay int
- ZabbixName string
- PrivateIP string
- PrivateIPv6 string
- VipCount int
- VpcId string
- VpcName string
- SubnetIDList []string
- FixedIPList []string
- FloatingIP string
- NetworkCardList []SInstanceNic
- }
- func (self *SInstance) GetBillingType() string {
- if !self.OnDemand {
- return billing_api.BILLING_TYPE_PREPAID
- }
- return billing_api.BILLING_TYPE_POSTPAID
- }
- func (self *SInstance) GetCreatedAt() time.Time {
- return self.CreatedTime
- }
- func (self *SInstance) GetExpiredAt() time.Time {
- return self.ExpiredTime
- }
- func (self *SInstance) GetId() string {
- return self.InstanceId
- }
- func (self *SInstance) GetName() string {
- return self.DisplayName
- }
- func (self *SInstance) GetHostname() string {
- return self.InstanceName
- }
- func (self *SInstance) GetGlobalId() string {
- return self.GetId()
- }
- func (self *SInstance) GetStatus() string {
- switch self.InstanceStatus {
- case "backingup":
- return api.VM_BACKUP_CREATING
- case "creating", "master_order_creating":
- return api.VM_DEPLOYING
- case "expired", "freezing", "stopped":
- return api.VM_READY
- case "stopping":
- return api.VM_STOPPING
- case "rebuild":
- return api.VM_REBUILD_ROOT
- case "restarting", "starting":
- return api.VM_STARTING
- case "running":
- return api.VM_RUNNING
- default:
- return api.VM_UNKNOWN
- }
- }
- func (self *SInstance) Refresh() error {
- vm, err := self.host.zone.region.GetInstance(self.GetId())
- if err != nil {
- return err
- }
- return jsonutils.Update(self, vm)
- }
- func (self *SInstance) GetProjectId() string {
- return self.ProjectId
- }
- func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
- return self.host
- }
- func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
- storages, err := self.host.zone.GetStorages()
- if err != nil {
- return nil, err
- }
- ret := []cloudprovider.ICloudDisk{}
- for _, diskId := range self.AttachedVolume {
- disk, err := self.host.zone.region.GetDisk(diskId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetDisk %s", diskId)
- }
- find := false
- for i := range storages {
- if disk.DiskType == storages[i].storageType {
- disk.storage = &storages[i]
- find = true
- break
- }
- }
- if !find {
- return nil, fmt.Errorf("failed to found disk storage type %s", disk.DiskType)
- }
- ret = append(ret, disk)
- }
- return ret, nil
- }
- func (self *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
- ret := []cloudprovider.ICloudNic{}
- for i := range self.NetworkCardList {
- self.NetworkCardList[i].instance = self
- ret = append(ret, &self.NetworkCardList[i])
- }
- return ret, nil
- }
- func (self *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
- if len(self.FloatingIP) == 0 {
- return nil, nil
- }
- eips, err := self.host.zone.region.GetEips("ACTIVE")
- if err != nil {
- return nil, err
- }
- for i := range eips {
- if eips[i].EipAddress == self.FloatingIP {
- eips[i].region = self.host.zone.region
- return &eips[i], nil
- }
- }
- return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", self.FloatingIP)
- }
- func (self *SInstance) GetVcpuCount() int {
- return self.Flavor.FlavorCPU
- }
- func (self *SInstance) GetVmemSizeMB() int {
- return self.Flavor.FlavorRAM
- }
- func (self *SInstance) GetBootOrder() string {
- return "dcn"
- }
- func (self *SInstance) GetVga() string {
- return "std"
- }
- func (self *SInstance) GetVdi() string {
- return "vnc"
- }
- func (self *SInstance) GetImage() (*SImage, error) {
- if self.image != nil {
- return self.image, nil
- }
- var err error
- self.image, err = self.host.zone.region.GetImage(self.Image.ImageId)
- return self.image, err
- }
- func (self *SInstance) GetOsType() cloudprovider.TOsType {
- image, err := self.GetImage()
- if err != nil {
- return cloudprovider.OsTypeLinux
- }
- return image.GetOsType()
- }
- func (self *SInstance) GetFullOsName() string {
- image, err := self.GetImage()
- if err != nil {
- return self.image.ImageName
- }
- return image.GetFullOsName()
- }
- func (self *SInstance) GetBios() cloudprovider.TBiosType {
- image, err := self.GetImage()
- if err != nil {
- return cloudprovider.BIOS
- }
- return image.GetBios()
- }
- func (self *SInstance) GetOsArch() string {
- image, err := self.GetImage()
- if err != nil {
- return ""
- }
- return image.GetOsArch()
- }
- func (self *SInstance) GetOsDist() string {
- image, err := self.GetImage()
- if err != nil {
- return ""
- }
- return image.GetOsDist()
- }
- func (self *SInstance) GetOsVersion() string {
- image, err := self.GetImage()
- if err != nil {
- return ""
- }
- return image.GetOsVersion()
- }
- func (self *SInstance) GetOsLang() string {
- image, err := self.GetImage()
- if err != nil {
- return ""
- }
- return image.GetOsLang()
- }
- func (self *SInstance) GetMachine() string {
- return "pc"
- }
- func (self *SInstance) GetInstanceType() string {
- return self.Flavor.FlavorName
- }
- func (self *SInstance) GetSecurityGroupIds() ([]string, error) {
- ret := []string{}
- for _, sec := range self.SecGroupList {
- ret = append(ret, sec.SecurityGroupId)
- }
- return ret, nil
- }
- func (self *SInstance) SetSecurityGroups(secgroupIds []string) error {
- currentIds, err := self.GetSecurityGroupIds()
- if err != nil {
- return errors.Wrap(err, "GetSecurityGroupIds")
- }
- adds := []string{}
- for i := range secgroupIds {
- if !utils.IsInStringArray(secgroupIds[i], currentIds) {
- adds = append(adds, secgroupIds[i])
- }
- }
- for i := range adds {
- err := self.host.zone.region.AssignSecurityGroup(self.GetId(), adds[i])
- if err != nil {
- return errors.Wrap(err, "Instance.SetSecurityGroups")
- }
- }
- removes := []string{}
- for i := range currentIds {
- if !utils.IsInStringArray(currentIds[i], secgroupIds) {
- removes = append(removes, currentIds[i])
- }
- }
- for i := range removes {
- err := self.host.zone.region.UnsignSecurityGroup(self.GetId(), removes[i])
- if err != nil {
- return errors.Wrap(err, "Instance.SetSecurityGroups")
- }
- }
- return nil
- }
- func (self *SInstance) GetHypervisor() string {
- return api.HYPERVISOR_CTYUN
- }
- func (self *SInstance) StartVM(ctx context.Context) error {
- return self.host.zone.region.StartVM(self.GetId())
- }
- func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
- return self.host.zone.region.StopVM(self.GetId())
- }
- func (self *SInstance) DeleteVM(ctx context.Context) error {
- return self.host.zone.region.DeleteVM(self.GetId())
- }
- func (self *SRegion) UpdateVM(vmId string, opts cloudprovider.SInstanceUpdateOptions) error {
- params := map[string]interface{}{
- "instanceID": vmId,
- "displayName": opts.NAME,
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/update-instance", params)
- return err
- }
- func (self *SInstance) UpdateVM(ctx context.Context, input cloudprovider.SInstanceUpdateOptions) error {
- if self.DisplayName == input.NAME {
- return nil
- }
- return self.host.zone.region.UpdateVM(self.InstanceId, input)
- }
- func (self *SInstance) UpdateUserData(userData string) error {
- return cloudprovider.ErrNotSupported
- }
- func (self *SRegion) RebuildRoot(vmId string, opts *cloudprovider.SManagedVMRebuildRootConfig) error {
- params := map[string]interface{}{
- "instanceID": vmId,
- "password": opts.Password,
- "imageID": opts.ImageId,
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/rebuild-instance", params)
- return err
- }
- func (self *SInstance) RebuildRoot(ctx context.Context, opts *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
- oldDiskId := self.AttachedVolume[0]
- err := self.host.zone.region.RebuildRoot(self.InstanceId, opts)
- if err != nil {
- return "", err
- }
- cloudprovider.Wait(time.Second*10, time.Minute*5, func() (bool, error) {
- self.Refresh()
- if oldDiskId != self.AttachedVolume[0] {
- return true, nil
- }
- return false, nil
- })
- return self.AttachedVolume[0], nil
- }
- func (self *SRegion) AttachKeypair(vmId, keyName string) error {
- params := map[string]interface{}{
- "instanceID": vmId,
- "keyPairName": keyName,
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/keypair/attach-instance", params)
- return err
- }
- func (self *SRegion) DetachKeypair(vmId, keyName string) error {
- params := map[string]interface{}{
- "instanceID": vmId,
- "keyPairName": keyName,
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/keypair/detach-instance", params)
- return err
- }
- func (self *SInstance) DeployVM(ctx context.Context, opts *cloudprovider.SInstanceDeployOptions) error {
- if len(opts.Password) > 0 {
- return self.host.zone.region.ResetVMPassword(self.GetId(), opts.Password)
- }
- if len(opts.PublicKey) > 0 {
- keypair, err := self.host.zone.region.syncKeypair(opts.Password)
- if err != nil {
- return errors.Wrapf(err, "syncKeypair")
- }
- return self.host.zone.region.AttachKeypair(self.InstanceId, keypair.KeyPairName)
- }
- if opts.DeleteKeypair && len(self.KeypairName) > 0 {
- return self.host.zone.region.DetachKeypair(self.InstanceId, self.KeypairName)
- }
- return nil
- }
- func (self *SRegion) ChangeVMConfig(id, instanceType string) error {
- skus, err := self.GetServerSkus("")
- if err != nil {
- return errors.Wrapf(err, "GetServerSkus")
- }
- for i := range skus {
- if skus[i].FlavorName == instanceType {
- instanceType = skus[i].FlavorId
- break
- }
- }
- params := map[string]interface{}{
- "instanceID": id,
- "clientToken": utils.GenRequestId(20),
- "flavorID": instanceType,
- }
- _, err = self.post(SERVICE_ECS, "/v4/ecs/update-flavor-spec", params)
- return err
- }
- func (self *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
- return self.host.zone.region.ChangeVMConfig(self.GetId(), config.InstanceType)
- }
- func (self *SRegion) GetInstanceVnc(vmId string) (string, error) {
- params := map[string]interface{}{
- "instanceID": vmId,
- }
- resp, err := self.list(SERVICE_ECS, "/v4/ecs/vnc/details", params)
- if err != nil {
- return "", err
- }
- return resp.GetString("returnObj", "token")
- }
- func (self *SInstance) GetVNCInfo(input *cloudprovider.ServerVncInput) (*cloudprovider.ServerVncOutput, error) {
- url, err := self.host.zone.region.GetInstanceVnc(self.InstanceId)
- if err != nil {
- return nil, err
- }
- protocol := "ctyun"
- if strings.HasPrefix(url, "wss") {
- protocol = "vnc"
- }
- ret := &cloudprovider.ServerVncOutput{
- Url: url,
- Protocol: protocol,
- InstanceId: self.GetId(),
- Hypervisor: api.HYPERVISOR_CTYUN,
- }
- return ret, nil
- }
- func (self *SRegion) AttachDisk(id, diskId string) error {
- params := map[string]interface{}{
- "diskID": diskId,
- "instanceID": id,
- }
- _, err := self.post(SERVICE_EBS, "/v4/ebs/attach-ebs", params)
- return err
- }
- func (self *SInstance) AttachDisk(ctx context.Context, diskId string) error {
- return self.host.zone.region.AttachDisk(self.InstanceId, diskId)
- }
- func (self *SRegion) DetachDisk(id, diskId string) error {
- params := map[string]interface{}{
- "diskID": diskId,
- "instanceID": id,
- }
- _, err := self.post(SERVICE_EBS, "/v4/ebs/detach-ebs", params)
- return err
- }
- func (self *SInstance) DetachDisk(ctx context.Context, diskId string) error {
- return self.host.zone.region.DetachDisk(self.InstanceId, diskId)
- }
- func (self *SInstance) Renew(bc billing.SBillingCycle) error {
- _, err := self.host.zone.region.RenewVM(self.GetId(), &bc)
- if err != nil {
- return errors.Wrap(err, "Instance.Renew.RenewVM")
- }
- return nil
- }
- func (self *SInstance) GetError() error {
- return nil
- }
- func (self *SRegion) CreateInstance(zoneId string, opts *cloudprovider.SManagedVMCreateConfig) (string, error) {
- image, err := self.GetImage(opts.ExternalImageId)
- if err != nil {
- return "", errors.Wrapf(err, "GetImage %s", opts.ExternalImageId)
- }
- if image.DiskSize > opts.SysDisk.SizeGB {
- opts.SysDisk.SizeGB = image.DiskSize
- }
- if opts.SysDisk.SizeGB < 40 {
- opts.SysDisk.SizeGB = 40
- }
- skus, err := self.GetServerSkus("")
- if err != nil {
- return "", errors.Wrapf(err, "GetServerSkus")
- }
- for i := range skus {
- if skus[i].FlavorName == opts.InstanceType {
- opts.InstanceType = skus[i].FlavorId
- break
- }
- }
- disks := []map[string]interface{}{}
- for _, disk := range opts.DataDisks {
- disks = append(disks, map[string]interface{}{
- "diskName": disk.Name,
- "diskSize": disk.SizeGB,
- "diskType": disk.StorageType,
- })
- }
- nets := map[string]interface{}{
- "subnetID": opts.ExternalNetworkId,
- "isMaster": true,
- }
- if len(opts.IpAddr) > 0 {
- nets["fixedIP"] = opts.IpAddr
- }
- imageType, _ := map[string]int{
- "private": 0,
- "public": 1,
- "shared": 2,
- "safe": 3,
- "community": 4,
- }[image.Visibility]
- params := map[string]interface{}{
- "clientToken": utils.GenRequestId(20),
- "userPassword": opts.Password,
- "imageID": opts.ExternalImageId,
- "imageType": imageType,
- "userData": opts.UserData,
- "instanceName": opts.Hostname,
- "displayName": opts.Name,
- "flavorID": opts.InstanceType,
- "onDemand": true,
- "extIP": "0",
- "vpcID": opts.ExternalVpcId,
- "bootDiskType": opts.SysDisk.StorageType,
- "bootDiskSize": opts.SysDisk.SizeGB,
- "secGroupList": opts.ExternalSecgroupIds,
- "azName": zoneId,
- "dataDiskList": disks,
- "networkCardList": []map[string]interface{}{nets},
- }
- if len(opts.PublicKey) > 0 {
- keypair, err := self.syncKeypair(opts.PublicKey)
- if err != nil {
- return "", errors.Wrapf(err, "syncKeypair")
- }
- params["keyPairID"] = keypair.KeyPairId
- }
- if opts.BillingCycle != nil {
- params["onDemand"] = false
- if opts.BillingCycle.AutoRenew {
- params["autoRenewStatus"] = "true"
- }
- if opts.BillingCycle.GetYears() > 0 {
- params["cycleType"] = "YEAR"
- params["cycleCount"] = opts.BillingCycle.GetYears()
- } else if opts.BillingCycle.GetMonths() > 0 {
- params["cycleType"] = "MONTH"
- params["cycleCount"] = opts.BillingCycle.GetMonths()
- }
- }
- resp, err := self.post(SERVICE_ECS, "/v4/ecs/create-instance", params)
- if err != nil {
- return "", err
- }
- orderId, err := resp.GetString("returnObj", "masterOrderID")
- if err != nil {
- return "", err
- }
- return self.GetResourceId(orderId)
- }
- func (self *SRegion) GetResourceId(orderId string) (string, error) {
- params := map[string]interface{}{
- "masterOrderId": orderId,
- }
- for i := 0; i < 10; i++ {
- resp, err := self.list(SERVICE_ECS, "/v4/order/query-uuid", params)
- if err != nil {
- return "", err
- }
- ids := []string{}
- resp.Unmarshal(&ids, "returnObj", "resourceUUID")
- for i := range ids {
- return ids[i], nil
- }
- time.Sleep(time.Second * 10)
- }
- return "", errors.Wrapf(cloudprovider.ErrNotFound, "failed get uuid by order id: %s", orderId)
- }
- func (self *SRegion) AssignSecurityGroup(vmId, groupId string) error {
- params := map[string]interface{}{
- "securityGroupID": groupId,
- "instanceID": vmId,
- "action": "joinSecurityGroup",
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/vpc/join-security-group", params)
- return err
- }
- func (self *SRegion) UnsignSecurityGroup(vmId, groupId string) error {
- params := map[string]interface{}{
- "securityGroupID": groupId,
- "instanceID": vmId,
- "action": "joinSecurityGroup",
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/vpc/leave-security-group", params)
- return err
- }
- func (self *SRegion) StartVM(id string) error {
- params := map[string]interface{}{
- "instanceID": id,
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/start-instance", params)
- return err
- }
- func (self *SRegion) StopVM(id string) error {
- params := map[string]interface{}{
- "instanceID": id,
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/stop-instance", params)
- return err
- }
- func (self *SRegion) DeleteVM(id string) error {
- params := map[string]interface{}{
- "instanceID": id,
- "clientToken": utils.GenRequestId(20),
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/unsubscribe-instance", params)
- return err
- }
- func (self *SRegion) RenewVM(vmId string, bc *billing.SBillingCycle) ([]string, error) {
- return nil, cloudprovider.ErrNotImplemented
- }
- func (self *SRegion) ResetVMPassword(id, password string) error {
- params := map[string]interface{}{
- "instanceID": id,
- "newPassword": password,
- }
- _, err := self.post(SERVICE_ECS, "/v4/ecs/reset-password", params)
- return err
- }
|