yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/aliyun/mongodb.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 aliyun
    16  
    17  import (
    18  	"fmt"
    19  	"regexp"
    20  	"strconv"
    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  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    30  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    31  	"yunion.io/x/cloudmux/pkg/multicloud"
    32  )
    33  
    34  type SMOngoDBAttribute struct {
    35  	// 实例最大IOPS
    36  	MaxIops     int
    37  	ReplicaSets struct {
    38  		ReplicaSet []struct {
    39  			ConnectionDomain string `json:"ConnectionDomain"`
    40  		}
    41  	}
    42  	MaxConnections int
    43  }
    44  
    45  type SMongoDB struct {
    46  	region *SRegion
    47  	AliyunTags
    48  	multicloud.SBillingBase
    49  	multicloud.SResourceBase
    50  
    51  	ConnectionDomain string `json:"ConnectionDomain"`
    52  	NetworkAddress   string
    53  	ChargeType       TChargeType `json:"ChargeType"`
    54  	LockMode         string      `json:"LockMode"`
    55  	DBInstanceClass  string      `json:"DBInstanceClass"`
    56  	ResourceGroupId  string      `json:"ResourceGroupId"`
    57  	DBInstanceId     string      `json:"DBInstanceId"`
    58  	ZoneId           string      `json:"ZoneId"`
    59  	MongosList       struct {
    60  		MongosAttribute []struct {
    61  			NodeId    string `json:"NodeId"`
    62  			NodeClass string `json:"NodeClass"`
    63  		} `json:"MongosAttribute"`
    64  	} `json:"MongosList"`
    65  	DBInstanceDescription string    `json:"DBInstanceDescription"`
    66  	Engine                string    `json:"Engine"`
    67  	CreationTime          time.Time `json:"CreationTime"`
    68  	NetworkType           string    `json:"NetworkType"`
    69  	ExpireTime            time.Time `json:"ExpireTime"`
    70  	DBInstanceType        string    `json:"DBInstanceType"`
    71  	RegionId              string    `json:"RegionId"`
    72  	ShardList             struct {
    73  		ShardAttribute []struct {
    74  			NodeId      string `json:"NodeId"`
    75  			NodeClass   string `json:"NodeClass"`
    76  			NodeStorage int    `json:"NodeStorage"`
    77  		} `json:"ShardAttribute"`
    78  	} `json:"ShardList"`
    79  	EngineVersion    string `json:"EngineVersion"`
    80  	DBInstanceStatus string `json:"DBInstanceStatus"`
    81  
    82  	DBInstanceStorage int    `json:"DBInstanceStorage"`
    83  	MaintainStartTime string `json:"MaintainStartTime"`
    84  	MaintainEndTime   string `json:"MaintainEndTime"`
    85  	StorageEngine     string `json:"StorageEngine"`
    86  	VpcId             string `json:"VPCId"`
    87  	VSwitchId         string `json:"VSwitchId"`
    88  	VpcAuthMode       string `json:"VpcAuthMode"`
    89  	ReplicationFactor string `json:"ReplicationFactor"`
    90  }
    91  
    92  var mongoSpec = map[string]struct {
    93  	VcpuCount  int
    94  	VmemSizeGb int
    95  }{}
    96  
    97  func (self *SMongoDB) GetName() string {
    98  	if len(self.DBInstanceDescription) > 0 {
    99  		return self.DBInstanceDescription
   100  	}
   101  	return self.DBInstanceId
   102  }
   103  
   104  func (self *SMongoDB) GetId() string {
   105  	return self.DBInstanceId
   106  }
   107  
   108  func (self *SMongoDB) GetGlobalId() string {
   109  	return self.DBInstanceId
   110  }
   111  
   112  func (self *SMongoDB) GetStatus() string {
   113  	switch self.DBInstanceStatus {
   114  	case "Creating":
   115  		return api.MONGO_DB_STATUS_CREATING
   116  	case "DBInstanceClassChanging":
   117  		return api.MONGO_DB_STATUS_CHANGE_CONFIG
   118  	case "DBInstanceNetTypeChanging", "EngineVersionUpgrading", "GuardSwitching", "HASwitching", "Importing", "ImportingFromOthers", "LinkSwitching", "MinorVersionUpgrading", "NET_CREATING", "NET_DELETING", "NodeCreating", "NodeDeleting", "Restoring", "SSLModifying", "TempDBInstanceCreating", "Transing", "TransingToOthers":
   119  		return api.MONGO_DB_STATUS_DEPLOY
   120  	case "Deleting":
   121  		return api.MONGO_DB_STATUS_DELETING
   122  	case "Rebooting":
   123  		return api.MONGO_DB_STATUS_REBOOTING
   124  	case "Running":
   125  		return api.MONGO_DB_STATUS_RUNNING
   126  	default:
   127  		return strings.ToLower(self.DBInstanceStatus)
   128  	}
   129  }
   130  
   131  func (self *SMongoDB) GetProjectId() string {
   132  	return self.ResourceGroupId
   133  }
   134  
   135  func (self *SMongoDB) Refresh() error {
   136  	db, err := self.region.GetMongoDB(self.DBInstanceId)
   137  	if err != nil {
   138  		return errors.Wrapf(err, "GetMongoDB")
   139  	}
   140  	return jsonutils.Update(self, db)
   141  }
   142  
   143  func (self *SMongoDB) GetCreatedAt() time.Time {
   144  	return self.CreationTime
   145  }
   146  
   147  func (self *SMongoDB) GetExpiredAt() time.Time {
   148  	return self.ExpireTime
   149  }
   150  
   151  func (self *SMongoDB) GetIpAddr() string {
   152  	return ""
   153  }
   154  
   155  func (self *SMongoDB) GetEngine() string {
   156  	if len(self.StorageEngine) == 0 {
   157  		self.Refresh()
   158  	}
   159  	return self.StorageEngine
   160  }
   161  
   162  func (self *SMongoDB) GetEngineVersion() string {
   163  	return self.EngineVersion
   164  }
   165  
   166  func (self *SMongoDB) GetVpcId() string {
   167  	if self.NetworkType != "VPC" {
   168  		return ""
   169  	}
   170  	if len(self.VpcId) == 0 {
   171  		self.Refresh()
   172  	}
   173  	return self.VpcId
   174  }
   175  
   176  func (self *SMongoDB) GetNetworkId() string {
   177  	if self.NetworkType != "VPC" {
   178  		return ""
   179  	}
   180  	if len(self.VSwitchId) == 0 {
   181  		self.Refresh()
   182  	}
   183  	return self.VSwitchId
   184  }
   185  
   186  func (self *SMongoDB) GetZoneId() string {
   187  	if !strings.Contains(self.ZoneId, ",") {
   188  		return self.ZoneId
   189  	}
   190  	if index := strings.Index(self.ZoneId, ",") - 1; index > 0 {
   191  		return fmt.Sprintf("%s-%s", self.region.RegionId, string(self.ZoneId[index]))
   192  	}
   193  	return ""
   194  }
   195  
   196  func (self *SMongoDB) Delete() error {
   197  	return self.region.DeleteMongoDB(self.DBInstanceId)
   198  }
   199  
   200  func (self *SMongoDB) GetBillingType() string {
   201  	return convertChargeType(self.ChargeType)
   202  }
   203  
   204  func (self *SMongoDB) GetCategory() string {
   205  	return self.DBInstanceType
   206  }
   207  
   208  func (self *SMongoDB) GetDiskSizeMb() int {
   209  	if self.DBInstanceStorage == 0 {
   210  		self.Refresh()
   211  	}
   212  	return self.DBInstanceStorage * 1024
   213  }
   214  
   215  func (self *SMongoDB) GetInstanceType() string {
   216  	return self.DBInstanceClass
   217  }
   218  
   219  func (self *SMongoDB) GetMaintainTime() string {
   220  	return fmt.Sprintf("%s-%s", self.MaintainStartTime, self.MaintainEndTime)
   221  }
   222  
   223  func (self *SMongoDB) GetPort() int {
   224  	return 3717
   225  }
   226  
   227  func (self *SMongoDB) GetReplicationNum() int {
   228  	if len(self.ReplicationFactor) == 0 {
   229  		self.Refresh()
   230  	}
   231  	num, _ := strconv.Atoi(self.ReplicationFactor)
   232  	return int(num)
   233  }
   234  
   235  func (self *SMongoDB) GetVcpuCount() int {
   236  	self.region.GetchMongoSkus()
   237  	sku, ok := self.region.mongoSkus[self.DBInstanceClass]
   238  	if ok {
   239  		return sku.CpuCount
   240  	}
   241  	return 0
   242  }
   243  
   244  func (self *SMongoDB) GetVmemSizeMb() int {
   245  	self.region.GetchMongoSkus()
   246  	sku, ok := self.region.mongoSkus[self.DBInstanceClass]
   247  	if ok {
   248  		return sku.MemSizeGb * 1024
   249  	}
   250  	return 0
   251  }
   252  
   253  func (self *SMongoDB) GetIops() int {
   254  	iops, _ := self.region.GetIops(self.DBInstanceId)
   255  	return iops
   256  }
   257  
   258  func (self *SMongoDB) GetMaxConnections() int {
   259  	maxConnection, _ := self.region.GetMaxConnections(self.DBInstanceId)
   260  	return maxConnection
   261  }
   262  
   263  func (self *SMongoDB) GetNetworkAddress() string {
   264  	addr, _ := self.region.GetNetworkAddress(self.DBInstanceId)
   265  	return addr
   266  }
   267  
   268  func (self *SRegion) GetMongoDBsByType(mongoType string) ([]SMongoDB, error) {
   269  	dbs := []SMongoDB{}
   270  	for {
   271  		part, total, err := self.GetMongoDBs(mongoType, 100, len(dbs)/100)
   272  		if err != nil {
   273  			return nil, errors.Wrapf(err, "GetMongoDB")
   274  		}
   275  		dbs = append(dbs, part...)
   276  		if len(dbs) >= total {
   277  			break
   278  		}
   279  	}
   280  	return dbs, nil
   281  }
   282  
   283  func (self *SMongoDB) SetTags(tags map[string]string, replace bool) error {
   284  	return self.region.SetResourceTags(ALIYUN_SERVICE_MONGO_DB, "INSTANCE", self.GetId(), tags, replace)
   285  }
   286  
   287  func (self *SRegion) GetICloudMongoDBById(id string) (cloudprovider.ICloudMongoDB, error) {
   288  	db, err := self.GetMongoDB(id)
   289  	if err != nil {
   290  		return nil, errors.Wrapf(err, "GetMongoDB(%s)", id)
   291  	}
   292  	return db, nil
   293  }
   294  
   295  func (self *SRegion) GetICloudMongoDBs() ([]cloudprovider.ICloudMongoDB, error) {
   296  	dbs := []SMongoDB{}
   297  	for _, mongoType := range []string{"sharding", "replicate", "serverless"} {
   298  		part, err := self.GetMongoDBsByType(mongoType)
   299  		if err != nil {
   300  			return nil, err
   301  		}
   302  		dbs = append(dbs, part...)
   303  	}
   304  	ret := []cloudprovider.ICloudMongoDB{}
   305  	for i := range dbs {
   306  		dbs[i].region = self
   307  		ret = append(ret, &dbs[i])
   308  	}
   309  	return ret, nil
   310  }
   311  
   312  func (self *SRegion) GetIops(id string) (int, error) {
   313  	ret, err := self.GetMongoDBAttribute(id)
   314  	if err != nil {
   315  		return 0, errors.Wrapf(err, "DescribeDBInstanceAttribute err")
   316  	}
   317  	if len(ret) == 0 {
   318  		return 0, errors.Wrapf(err, "ret missing err")
   319  	}
   320  	return ret[0].MaxIops, nil
   321  }
   322  
   323  func (self *SRegion) GetMaxConnections(id string) (int, error) {
   324  	ret, err := self.GetMongoDBAttribute(id)
   325  	if err != nil {
   326  		return 0, errors.Wrapf(err, "DescribeDBInstanceAttribute err")
   327  	}
   328  	if len(ret) == 0 {
   329  		return 0, errors.Wrapf(err, "ret missing err")
   330  	}
   331  	return ret[0].MaxConnections, nil
   332  }
   333  
   334  func (self *SRegion) GetNetworkAddress(id string) (string, error) {
   335  	ret, err := self.GetMongoDBAttribute(id)
   336  	if err != nil {
   337  		return "", errors.Wrapf(err, "DescribeDBInstanceAttribute err")
   338  	}
   339  	addrList := make([]string, 0)
   340  	for _, v := range ret[0].ReplicaSets.ReplicaSet {
   341  		addrList = append(addrList, v.ConnectionDomain)
   342  	}
   343  	addrs := strings.Join(addrList, ",")
   344  	return addrs, nil
   345  }
   346  
   347  func (self *SRegion) GetMongoDBAttribute(id string) ([]SMOngoDBAttribute, error) {
   348  	params := map[string]string{
   349  		"Action":       "DescribeDBInstanceAttribute",
   350  		"DBInstanceId": id,
   351  	}
   352  	resp, err := self.mongodbRequest("DescribeDBInstanceAttribute", params)
   353  	if err != nil {
   354  		return nil, errors.Wrapf(err, "DescribeDBInstanceAttribute err")
   355  	}
   356  	ret := []SMOngoDBAttribute{}
   357  	err = resp.Unmarshal(&ret, "DBInstances", "DBInstance")
   358  	if err != nil {
   359  		return nil, errors.Wrapf(err, "unmarshal err")
   360  	}
   361  	return ret, err
   362  }
   363  
   364  func (self *SRegion) GetMongoDBs(mongoType string, pageSize int, pageNum int) ([]SMongoDB, int, error) {
   365  	if pageSize < 1 || pageSize > 100 {
   366  		pageSize = 100
   367  	}
   368  	if pageNum < 1 {
   369  		pageNum = 1
   370  	}
   371  
   372  	params := map[string]string{
   373  		"PageSize":   fmt.Sprintf("%d", pageSize),
   374  		"PageNumber": fmt.Sprintf("%d", pageNum),
   375  	}
   376  	if len(mongoType) > 0 {
   377  		params["DBInstanceType"] = mongoType
   378  	}
   379  	resp, err := self.mongodbRequest("DescribeDBInstances", params)
   380  	if err != nil {
   381  		return nil, 0, errors.Wrapf(err, "DescribeDBInstances")
   382  	}
   383  	ret := []SMongoDB{}
   384  	err = resp.Unmarshal(&ret, "DBInstances", "DBInstance")
   385  	if err != nil {
   386  		return nil, 0, errors.Wrapf(err, "resp.Unmarshal")
   387  	}
   388  	totalCount, _ := resp.Int("TotalCount")
   389  	return ret, int(totalCount), nil
   390  }
   391  
   392  func (self *SRegion) GetMongoDB(id string) (*SMongoDB, error) {
   393  	params := map[string]string{
   394  		"DBInstanceId": id,
   395  	}
   396  	resp, err := self.mongodbRequest("DescribeDBInstanceAttribute", params)
   397  	if err != nil {
   398  		return nil, errors.Wrapf(err, "DescribeDBInstanceAttribute")
   399  	}
   400  	ret := []SMongoDB{}
   401  	err = resp.Unmarshal(&ret, "DBInstances", "DBInstance")
   402  	if err != nil {
   403  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   404  	}
   405  	if len(ret) == 1 {
   406  		ret[0].region = self
   407  		return &ret[0], nil
   408  	}
   409  	return nil, errors.Wrapf(cloudprovider.ErrNotFound, id)
   410  }
   411  
   412  func (self *SRegion) DeleteMongoDB(id string) error {
   413  	params := map[string]string{
   414  		"DBInstanceId": id,
   415  		"ClientToken":  utils.GenRequestId(20),
   416  	}
   417  	_, err := self.mongodbRequest("DeleteDBInstance", params)
   418  	return errors.Wrapf(err, "DeleteDBInstance")
   419  }
   420  
   421  type SMongoDBAvaibaleResource struct {
   422  	SupportedDBTypes struct {
   423  		SupportedDBType []struct {
   424  			DbType         string
   425  			AvailableZones struct {
   426  				AvailableZone []struct {
   427  					ZoneId                  string
   428  					RegionId                string
   429  					SupportedEngineVersions struct {
   430  						SupportedEngineVersion []struct {
   431  							Version          string
   432  							SupportedEngines struct {
   433  								SupportedEngine []struct {
   434  									SupportedNodeTypes struct {
   435  										SupportedNodeType []struct {
   436  											NetworkTypes       string
   437  											NodeType           string
   438  											AvailableResources struct {
   439  												AvailableResource []struct {
   440  													InstanceClassRemark string
   441  													InstanceClass       string
   442  												}
   443  											}
   444  										}
   445  									}
   446  								}
   447  							}
   448  						}
   449  					}
   450  				}
   451  			}
   452  		}
   453  	}
   454  }
   455  
   456  func (self *SRegion) GetchMongoSkus() (map[string]struct {
   457  	CpuCount  int
   458  	MemSizeGb int
   459  }, error) {
   460  	if len(self.mongoSkus) > 0 {
   461  		return self.mongoSkus, nil
   462  	}
   463  	self.mongoSkus = map[string]struct {
   464  		CpuCount  int
   465  		MemSizeGb int
   466  	}{}
   467  	res, err := self.GetMongoDBAvailableResource()
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  	for _, dbType := range res.SupportedDBTypes.SupportedDBType {
   472  		for _, zone := range dbType.AvailableZones.AvailableZone {
   473  			for _, version := range zone.SupportedEngineVersions.SupportedEngineVersion {
   474  				for _, engine := range version.SupportedEngines.SupportedEngine {
   475  					for _, nodeType := range engine.SupportedNodeTypes.SupportedNodeType {
   476  						for _, sku := range nodeType.AvailableResources.AvailableResource {
   477  							_, ok := self.mongoSkus[sku.InstanceClass]
   478  							if !ok {
   479  								self.mongoSkus[sku.InstanceClass] = getMongoDBSkuDetails(sku.InstanceClassRemark)
   480  							}
   481  						}
   482  					}
   483  				}
   484  			}
   485  		}
   486  	}
   487  	return self.mongoSkus, nil
   488  }
   489  
   490  func getMongoDBSkuDetails(remark string) struct {
   491  	CpuCount  int
   492  	MemSizeGb int
   493  } {
   494  	ret := struct {
   495  		CpuCount  int
   496  		MemSizeGb int
   497  	}{}
   498  	r, _ := regexp.Compile(`(\d{1,3})核(\d{1,3})G+`)
   499  	result := r.FindSubmatch([]byte(remark))
   500  	if len(result) > 2 {
   501  		cpu, _ := strconv.Atoi(string(result[1]))
   502  		ret.CpuCount = int(cpu)
   503  		mem, _ := strconv.Atoi(string(result[2]))
   504  		ret.MemSizeGb = int(mem)
   505  	} else {
   506  		log.Warningf("not match sku remark %s", remark)
   507  	}
   508  	return ret
   509  }
   510  
   511  func (self *SRegion) GetMongoDBAvailableResource() (*SMongoDBAvaibaleResource, error) {
   512  	params := map[string]string{}
   513  	resp, err := self.mongodbRequest("DescribeAvailableResource", params)
   514  	if err != nil {
   515  		return nil, errors.Wrapf(err, "DescribeAvailableResource")
   516  	}
   517  	ret := &SMongoDBAvaibaleResource{}
   518  	err = resp.Unmarshal(ret)
   519  	if err != nil {
   520  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   521  	}
   522  	return ret, nil
   523  }