yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/zone.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 qcloud
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"time"
    21  
    22  	"yunion.io/x/log"
    23  	"yunion.io/x/pkg/errors"
    24  	"yunion.io/x/pkg/utils"
    25  
    26  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    27  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    28  	"yunion.io/x/cloudmux/pkg/multicloud"
    29  )
    30  
    31  type InstanceChargeType string
    32  
    33  const (
    34  	PrePaidInstanceChargeType  InstanceChargeType = "PREPAID"
    35  	PostPaidInstanceChargeType InstanceChargeType = "POSTPAID_BY_HOUR"
    36  	CdhPaidInstanceChargeType  InstanceChargeType = "CDHPAID"
    37  	DefaultInstanceChargeType                     = PostPaidInstanceChargeType
    38  )
    39  
    40  type SZone struct {
    41  	multicloud.SResourceBase
    42  	QcloudTags
    43  	region *SRegion
    44  
    45  	iwires []cloudprovider.ICloudWire
    46  
    47  	host *SHost
    48  
    49  	istorages []cloudprovider.ICloudStorage
    50  
    51  	instanceTypes []string
    52  	refreshTime   time.Time
    53  	localstorages []string
    54  	cloudstorages []string
    55  
    56  	Zone      string
    57  	ZoneName  string
    58  	ZoneState string
    59  }
    60  
    61  func (self *SZone) GetId() string {
    62  	return self.Zone
    63  }
    64  
    65  func (self *SZone) GetName() string {
    66  	return self.ZoneName
    67  }
    68  
    69  func (self *SZone) GetI18n() cloudprovider.SModelI18nTable {
    70  	en := self.ZoneName
    71  	table := cloudprovider.SModelI18nTable{}
    72  	table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName()).EN(en)
    73  	return table
    74  }
    75  
    76  func (self *SZone) GetGlobalId() string {
    77  	return fmt.Sprintf("%s/%s", self.region.GetGlobalId(), self.Zone)
    78  }
    79  
    80  func (self *SZone) IsEmulated() bool {
    81  	return false
    82  }
    83  
    84  func (self *SZone) Refresh() error {
    85  	// do nothing
    86  	return nil
    87  }
    88  
    89  func (self *SZone) GetStatus() string {
    90  	if self.ZoneState == "AVAILABLE" {
    91  		return api.ZONE_ENABLE
    92  	}
    93  	return api.ZONE_SOLDOUT
    94  }
    95  
    96  func (self *SZone) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
    97  	host := self.getHost()
    98  	if host.GetGlobalId() == id {
    99  		return host, nil
   100  	}
   101  	return nil, cloudprovider.ErrNotFound
   102  }
   103  
   104  func (self *SZone) GetIHosts() ([]cloudprovider.ICloudHost, error) {
   105  	return []cloudprovider.ICloudHost{self.getHost()}, nil
   106  }
   107  
   108  func (self *SZone) getHost() *SHost {
   109  	if self.host == nil {
   110  		self.host = &SHost{zone: self}
   111  	}
   112  	return self.host
   113  }
   114  
   115  func (self *SZone) GetIRegion() cloudprovider.ICloudRegion {
   116  	return self.region
   117  }
   118  
   119  type SDiskConfigSet struct {
   120  	Available      bool
   121  	DeviceClass    string
   122  	DiskChargeType string
   123  	DiskType       string
   124  	DiskUsage      string
   125  	InstanceFamily string
   126  	MaxDiskSize    int
   127  	MinDiskSize    int
   128  	Zone           string
   129  }
   130  
   131  func (self *SRegion) GetDiskConfigSet(zoneName string) ([]SDiskConfigSet, error) {
   132  	params := map[string]string{}
   133  	params["Region"] = self.Region
   134  	params["Zones.0"] = zoneName
   135  	params["InquiryType"] = "INQUIRY_CBS_CONFIG"
   136  	body, err := self.cbsRequest("DescribeDiskConfigQuota", params)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	diskConfigSet := []SDiskConfigSet{}
   141  	return diskConfigSet, body.Unmarshal(&diskConfigSet, "DiskConfigSet")
   142  }
   143  
   144  func (self *SZone) fetchStorages() error {
   145  	self.istorages = []cloudprovider.ICloudStorage{}
   146  	diskConfigSet, err := self.region.GetDiskConfigSet(self.Zone)
   147  	if err != nil {
   148  		return err
   149  	}
   150  	self.cloudstorages = []string{}
   151  	for _, diskConfig := range diskConfigSet {
   152  		if !utils.IsInStringArray(strings.ToUpper(diskConfig.DiskType), self.cloudstorages) {
   153  			self.cloudstorages = append(self.cloudstorages, strings.ToUpper(diskConfig.DiskType))
   154  			storage := SStorage{zone: self, storageType: diskConfig.DiskType, available: diskConfig.Available}
   155  			self.istorages = append(self.istorages, &storage)
   156  		}
   157  	}
   158  	for _, storageType := range []string{"CLOUD_PREMIUM", "CLOUD_SSD", "CLOUD_BASIC"} {
   159  		if !utils.IsInStringArray(storageType, self.cloudstorages) {
   160  			self.cloudstorages = append(self.cloudstorages, storageType)
   161  			storage := SStorage{zone: self, storageType: storageType, available: false}
   162  			self.istorages = append(self.istorages, &storage)
   163  		}
   164  	}
   165  	self.localstorages, err = self.region.GetZoneLocalStorages(self.Zone)
   166  	if err != nil {
   167  		log.Errorf("falied to fetch local storage for zone %s", self.Zone)
   168  	}
   169  	for _, localstorageType := range self.localstorages {
   170  		storage := SLocalStorage{zone: self, storageType: localstorageType, available: true}
   171  		self.istorages = append(self.istorages, &storage)
   172  	}
   173  	for _, storageType := range QCLOUD_LOCAL_STORAGE_TYPES {
   174  		if !utils.IsInStringArray(storageType, self.localstorages) {
   175  			storage := SLocalStorage{zone: self, storageType: storageType, available: false}
   176  			self.istorages = append(self.istorages, &storage)
   177  		}
   178  	}
   179  	return nil
   180  }
   181  
   182  func (self *SZone) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
   183  	if self.istorages == nil {
   184  		err := self.fetchStorages()
   185  		if err != nil {
   186  			return nil, errors.Wrapf(err, "fetchStorages")
   187  		}
   188  	}
   189  	return self.istorages, nil
   190  }
   191  
   192  func (self *SZone) getLocalStorageByCategory(category string) (*SLocalStorage, error) {
   193  	if len(self.localstorages) == 0 {
   194  		err := self.fetchStorages()
   195  		if err != nil {
   196  			return nil, errors.Wrap(err, "fetchStorages")
   197  		}
   198  	}
   199  	if utils.IsInStringArray(strings.ToUpper(category), self.localstorages) {
   200  		return &SLocalStorage{zone: self, storageType: strings.ToUpper(category)}, nil
   201  	}
   202  	return nil, fmt.Errorf("No such storage %s", category)
   203  }
   204  
   205  func (self *SZone) validateStorageType(category string) error {
   206  	if len(self.localstorages) == 0 || len(self.cloudstorages) == 0 {
   207  		err := self.fetchStorages()
   208  		if err != nil {
   209  			return errors.Wrap(err, "fetchStorages")
   210  		}
   211  	}
   212  	if utils.IsInStringArray(strings.ToUpper(category), self.localstorages) || utils.IsInStringArray(strings.ToUpper(category), self.cloudstorages) {
   213  		return nil
   214  	}
   215  	return fmt.Errorf("No such storage %s", category)
   216  }
   217  
   218  func (self *SZone) getStorageByCategory(category string) (*SStorage, error) {
   219  	storages, err := self.GetIStorages()
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	for i := 0; i < len(storages); i++ {
   224  		if utils.IsInStringArray(storages[i].GetStorageType(), self.localstorages) {
   225  			continue
   226  			//return &SStorage{zone: self, storageType: strings.ToUpper(storages[i].GetStorageType())}, nil
   227  		}
   228  		storage := storages[i].(*SStorage)
   229  		if strings.ToLower(storage.storageType) == strings.ToLower(category) {
   230  			return storage, nil
   231  		}
   232  	}
   233  	return nil, fmt.Errorf("No such storage %s", category)
   234  }
   235  
   236  func (self *SZone) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
   237  	if self.istorages == nil {
   238  		err := self.fetchStorages()
   239  		if err != nil {
   240  			return nil, errors.Wrapf(err, "fetchStorages")
   241  		}
   242  	}
   243  	for i := 0; i < len(self.istorages); i += 1 {
   244  		if self.istorages[i].GetGlobalId() == id {
   245  			return self.istorages[i], nil
   246  		}
   247  	}
   248  	return nil, cloudprovider.ErrNotFound
   249  }
   250  
   251  func (self *SZone) addWire(wire *SWire) {
   252  	if self.iwires == nil {
   253  		self.iwires = make([]cloudprovider.ICloudWire, 0)
   254  	}
   255  	self.iwires = append(self.iwires, wire)
   256  }
   257  
   258  func (self *SZone) GetIWires() ([]cloudprovider.ICloudWire, error) {
   259  	return self.iwires, nil
   260  }
   261  
   262  func (self *SZone) getNetworkById(networkId string) *SNetwork {
   263  	log.Debugf("Search in wires %d", len(self.iwires))
   264  	for i := 0; i < len(self.iwires); i += 1 {
   265  		log.Debugf("Search in wire %s", self.iwires[i].GetName())
   266  		wire := self.iwires[i].(*SWire)
   267  		net := wire.getNetworkById(networkId)
   268  		if net != nil {
   269  			return net
   270  		}
   271  	}
   272  	return nil
   273  }
   274  
   275  func (self *SZone) fetchInstanceTypes() {
   276  	self.instanceTypes = []string{}
   277  	params := map[string]string{}
   278  	params["Region"] = self.region.Region
   279  	params["Filters.0.Name"] = "zone"
   280  	params["Filters.0.Values.0"] = self.Zone
   281  	if body, err := self.region.cvmRequest("DescribeInstanceTypeConfigs", params, true); err != nil {
   282  		log.Errorf("DescribeInstanceTypeConfigs error: %v", err)
   283  	} else if configSet, err := body.GetArray("InstanceTypeConfigSet"); err != nil {
   284  		log.Errorf("Get InstanceTypeConfigSet error: %v", err)
   285  	} else {
   286  		for _, config := range configSet {
   287  			if instanceType, err := config.GetString("InstanceType"); err == nil && !utils.IsInStringArray(instanceType, self.instanceTypes) {
   288  				self.instanceTypes = append(self.instanceTypes, instanceType)
   289  			}
   290  		}
   291  		self.refreshTime = time.Now()
   292  	}
   293  }
   294  
   295  func (self *SZone) getAvaliableInstanceTypes() []string {
   296  	if self.instanceTypes == nil || len(self.instanceTypes) == 0 || time.Now().Sub(self.refreshTime).Hours() > refreshHours() {
   297  		self.fetchInstanceTypes()
   298  	}
   299  	return self.instanceTypes
   300  }
   301  
   302  func refreshHours() float64 {
   303  	return 5
   304  }
   305  
   306  func (self *SZone) getCosEndpoint() string {
   307  	return fmt.Sprintf("cos.%s.myqcloud.com", self.GetId())
   308  }
   309  
   310  func (self *SZone) getCosWebsiteEndpoint() string {
   311  	return fmt.Sprintf("cos-website.%s.myqcloud.com", self.GetId())
   312  }