yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/elasticcache_instance.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 "strconv" 20 "strings" 21 "time" 22 23 "github.com/pkg/errors" 24 25 "yunion.io/x/jsonutils" 26 "yunion.io/x/log" 27 28 billing_api "yunion.io/x/cloudmux/pkg/apis/billing" 29 api "yunion.io/x/cloudmux/pkg/apis/compute" 30 "yunion.io/x/cloudmux/pkg/cloudprovider" 31 "yunion.io/x/cloudmux/pkg/multicloud" 32 "yunion.io/x/onecloud/pkg/util/billing" 33 ) 34 35 type SElasticcache struct { 36 multicloud.SElasticcacheBase 37 QcloudTags 38 region *SRegion 39 40 ClientLimit int 41 ClientLimitMax int 42 ClientLimitMin int 43 MaintenanceTime *MaintenanceTime `json:"maintenance_time"` 44 NoAuth *bool `json:"no_auth"` 45 NodeSet []NodeSet `json:"NodeSet"` 46 Appid int64 `json:"Appid"` 47 AutoRenewFlag int64 `json:"AutoRenewFlag"` 48 BillingMode int64 `json:"BillingMode"` 49 CloseTime string `json:"CloseTime"` 50 Createtime time.Time `json:"Createtime"` 51 DeadlineTime string `json:"DeadlineTime"` 52 Engine string `json:"Engine"` 53 InstanceID string `json:"InstanceId"` 54 InstanceName string `json:"InstanceName"` 55 InstanceNode []interface{} `json:"InstanceNode"` 56 InstanceTitle string `json:"InstanceTitle"` 57 OfflineTime string `json:"OfflineTime"` 58 Port int `json:"Port"` 59 PriceID int64 `json:"PriceId"` 60 ProductType string `json:"ProductType"` 61 ProjectID int `json:"ProjectId"` 62 RedisReplicasNum int `json:"RedisReplicasNum"` 63 RedisShardNum int64 `json:"RedisShardNum"` 64 RedisShardSize int64 `json:"RedisShardSize"` // MB 65 DiskSize int64 `json:"disk_size"` 66 RegionID int64 `json:"RegionId"` 67 Size int `json:"Size"` 68 SizeUsed int64 `json:"SizeUsed"` 69 SlaveReadWeight int64 `json:"SlaveReadWeight"` 70 Status int `json:"Status"` 71 SubStatus int64 `json:"SubStatus"` 72 SubnetID int64 `json:"SubnetId"` 73 Type int `json:"Type"` 74 UniqSubnetID string `json:"UniqSubnetId"` 75 UniqVpcID string `json:"UniqVpcId"` 76 VpcID int64 `json:"VpcId"` 77 WANIP string `json:"WanIp"` 78 ZoneID int `json:"ZoneId"` 79 } 80 81 func (self *SElasticcache) SetTags(tags map[string]string, replace bool) error { 82 return self.region.SetResourceTags("redis", "instance", []string{self.InstanceID}, tags, replace) 83 } 84 85 type MaintenanceTime struct { 86 StartTime string `json:"start_time"` 87 EndTime string `json:"end_time"` 88 } 89 90 type NodeSet struct { 91 NodeID int64 `json:"NodeId"` 92 NodeType int64 `json:"NodeType"` 93 ZoneID int64 `json:"ZoneId"` 94 } 95 96 var zoneMaps = map[int]string{ 97 100001: "ap-guangzhou-1", 98 100002: "ap-guangzhou-2", 99 100003: "ap-guangzhou-3", 100 100004: "ap-guangzhou-4", 101 100006: "ap-guangzhou-6", 102 110001: "ap-shenzhen-fsi-1", 103 110002: "ap-shenzhen-fsi-2", 104 110003: "ap-shenzhen-fsi-3", 105 200001: "ap-shanghai-1", 106 200002: "ap-shanghai-2", 107 200003: "ap-shanghai-3", 108 200004: "ap-shanghai-4", 109 200005: "ap-shanghai-5", 110 200006: "ap-shanghai-6", 111 200007: "ap-shanghai-7", 112 700001: "ap-shanghai-fsi-1", 113 700002: "ap-shanghai-fsi-2", 114 700003: "ap-shanghai-fsi-3", 115 330001: "ap-nanjing-1", 116 330002: "ap-nanjing-2", 117 330003: "ap-nanjing-3", 118 800001: "ap-beijing-1", 119 800002: "ap-beijing-2", 120 800003: "ap-beijing-3", 121 800004: "ap-beijing-4", 122 800005: "ap-beijing-5", 123 800006: "ap-beijing-6", 124 800007: "ap-beijing-7", 125 460001: "ap-beijing-fsi-1", 126 360001: "ap-tianjin-1", 127 360002: "ap-tianjin-2", 128 160001: "ap-chengdu-1", 129 160002: "ap-chengdu-2", 130 190001: "ap-chongqing-1", 131 300001: "ap-hongkong-1", 132 300002: "ap-hongkong-2", 133 300003: "ap-hongkong-3", 134 390001: "ap-taipei-1", 135 900001: "ap-singapore-1", 136 230001: "ap-bangkok-1", 137 210001: "ap-mumbai-1", 138 210002: "ap-mumbai-2", 139 180001: "ap-seoul-1", 140 180002: "ap-seoul-2", 141 250001: "ap-tokyo-1", 142 150001: "na-siliconvalley-1", 143 150002: "na-siliconvalley-2", 144 220001: "na-ashburn-1", 145 220002: "na-ashburn-2", 146 400001: "na-toronto-1", 147 170001: "eu-frankfurt-1", 148 240001: "eu-moscow-1", 149 } 150 151 var zoneIdMaps = map[string]int{ 152 "ap-guangzhou-1": 100001, 153 "ap-guangzhou-2": 100002, 154 "ap-guangzhou-3": 100003, 155 "ap-guangzhou-4": 100004, 156 "ap-guangzhou-6": 100006, 157 "ap-shenzhen-fsi-1": 110001, 158 "ap-shenzhen-fsi-2": 110002, 159 "ap-shenzhen-fsi-3": 110003, 160 "ap-shanghai-1": 200001, 161 "ap-shanghai-2": 200002, 162 "ap-shanghai-3": 200003, 163 "ap-shanghai-4": 200004, 164 "ap-shanghai-5": 200005, 165 "ap-shanghai-6": 200006, 166 "ap-shanghai-7": 200007, 167 "ap-shanghai-fsi-1": 700001, 168 "ap-shanghai-fsi-2": 700002, 169 "ap-shanghai-fsi-3": 700003, 170 "ap-nanjing-1": 330001, 171 "ap-nanjing-2": 330002, 172 "ap-nanjing-3": 330003, 173 "ap-beijing-1": 800001, 174 "ap-beijing-2": 800002, 175 "ap-beijing-3": 800003, 176 "ap-beijing-4": 800004, 177 "ap-beijing-5": 800005, 178 "ap-beijing-6": 800006, 179 "ap-beijing-7": 800007, 180 "ap-beijing-fsi-1": 460001, 181 "ap-tianjin-1": 360001, 182 "ap-tianjin-2": 360002, 183 "ap-chengdu-1": 160001, 184 "ap-chengdu-2": 160002, 185 "ap-chongqing-1": 190001, 186 "ap-hongkong-1": 300001, 187 "ap-hongkong-2": 300002, 188 "ap-hongkong-3": 300003, 189 "ap-taipei-1": 390001, 190 "ap-singapore-1": 900001, 191 "ap-bangkok-1": 230001, 192 "ap-mumbai-1": 210001, 193 "ap-mumbai-2": 210002, 194 "ap-seoul-1": 180001, 195 "ap-seoul-2": 180002, 196 "ap-tokyo-1": 250001, 197 "na-siliconvalley-1": 150001, 198 "na-siliconvalley-2": 150002, 199 "na-ashburn-1": 220001, 200 "na-ashburn-2": 220002, 201 "na-toronto-1": 400001, 202 "eu-frankfurt-1": 170001, 203 "eu-moscow-1": 240001, 204 } 205 206 func (self *SElasticcache) GetId() string { 207 return self.InstanceID 208 } 209 210 func (self *SElasticcache) GetName() string { 211 return self.InstanceName 212 } 213 214 func (self *SElasticcache) GetGlobalId() string { 215 return self.GetId() 216 } 217 218 // https://cloud.tencent.com/document/api/239/20022#InstanceSet 219 // 实例当前状态,0:待初始化;1:实例在流程中;2:实例运行中;-2:实例已隔离;-3:实例待删除 220 func (self *SElasticcache) GetStatus() string { 221 switch self.Status { 222 case 2: 223 return api.ELASTIC_CACHE_STATUS_RUNNING 224 case 0: 225 return api.ELASTIC_CACHE_STATUS_DEPLOYING 226 case -3: 227 return api.ELASTIC_CACHE_STATUS_RELEASING 228 case -2: 229 return api.ELASTIC_CACHE_STATUS_UNAVAILABLE 230 case 1: 231 return api.ELASTIC_CACHE_STATUS_CHANGING 232 } 233 234 return "" 235 } 236 237 func (self *SElasticcache) GetConnections() int { 238 return self.ClientLimit 239 } 240 241 func (self *SElasticcache) Refresh() error { 242 cache, err := self.region.GetIElasticcacheById(self.GetId()) 243 if err != nil { 244 return errors.Wrap(err, "Elasticcache.Refresh.GetElasticCache") 245 } 246 247 err = jsonutils.Update(self, cache) 248 if err != nil { 249 return errors.Wrap(err, "Elasticcache.Refresh.Update") 250 } 251 252 return nil 253 } 254 255 func (self *SElasticcache) IsEmulated() bool { 256 return false 257 } 258 259 func (self *SElasticcache) GetProjectId() string { 260 return strconv.Itoa(self.ProjectID) 261 } 262 263 func (self *SElasticcache) GetBillingType() string { 264 // 计费模式:0-按量计费,1-包年包月 265 if self.BillingMode == 1 { 266 return billing_api.BILLING_TYPE_PREPAID 267 } else { 268 return billing_api.BILLING_TYPE_POSTPAID 269 } 270 } 271 272 func (self *SElasticcache) GetCreatedAt() time.Time { 273 return self.Createtime 274 } 275 276 func (self *SElasticcache) GetExpiredAt() time.Time { 277 if self.DeadlineTime == "0000-00-00 00:00:00" || len(self.DeadlineTime) == 0 { 278 return time.Time{} 279 } 280 281 t, err := time.Parse("2006-01-02 15:04:05", self.DeadlineTime) 282 if err != nil { 283 log.Debugf("GetExpiredAt.Parse %s", err) 284 return time.Time{} 285 } 286 287 return t 288 } 289 290 // https://cloud.tencent.com/document/product/239/31785 291 func (self *SElasticcache) SetAutoRenew(bc billing.SBillingCycle) error { 292 params := map[string]string{} 293 params["Operation"] = "modifyAutoRenew" 294 params["InstanceIds.0"] = self.GetId() 295 if bc.AutoRenew { 296 params["AutoRenews.0"] = "1" 297 } else { 298 params["AutoRenews.0"] = "0" 299 } 300 301 _, err := self.region.redisRequest("ModifyInstance", params) 302 if err != nil { 303 return errors.Wrap(err, "ModifyInstance") 304 } 305 306 return nil 307 } 308 309 func (self *SElasticcache) IsAutoRenew() bool { 310 // 实例是否设置自动续费标识,1:设置自动续费;0:未设置自动续费 311 return self.AutoRenewFlag == 1 312 } 313 314 // redis:master:s1:r5:m1g:v4.0 315 // 实例类型:2 – Redis2.8内存版(标准架构),3 – CKV 3.2内存版(标准架构),4 – CKV 3.2内存版(集群架构) 316 // ,6 – Redis4.0内存版(标准架构),7 – Redis4.0内存版(集群架构), 317 // 8 – Redis5.0内存版(标准架构),9 – Redis5.0内存版(集群架构) 318 func (self *SElasticcache) GetInstanceType() string { 319 segs := make([]string, 6) 320 segs[0] = "redis" 321 switch self.Type { 322 case 2, 3, 6, 8: 323 segs[1] = "master" 324 case 4, 7, 9, 10: 325 segs[1] = "cluster" 326 case 5: 327 segs[1] = "single" 328 default: 329 segs[1] = strconv.Itoa(self.Type) 330 } 331 332 segs[2] = fmt.Sprintf("s%d", self.RedisShardNum) 333 segs[3] = fmt.Sprintf("r%d", self.RedisReplicasNum) 334 if self.DiskSize > 0 { 335 segs[4] = fmt.Sprintf("m%dg-d%dg", self.Size/1024, self.DiskSize) 336 } else { 337 segs[4] = fmt.Sprintf("m%dg", self.Size/1024) 338 } 339 segs[5] = fmt.Sprintf("v%s", self.GetEngineVersion()) 340 341 return strings.Join(segs, ":") 342 } 343 344 func (self *SElasticcache) GetCapacityMB() int { 345 return self.Size 346 } 347 348 func (self *SElasticcache) GetArchType() string { 349 // 产品类型:standalone – 标准版,cluster – 集群版 350 switch self.ProductType { 351 case "standalone": 352 return api.ELASTIC_CACHE_ARCH_TYPE_MASTER 353 case "cluster": 354 return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER 355 } 356 357 return "" 358 } 359 360 func (self *SElasticcache) GetNodeType() string { 361 // single(单副本) | double(双副本) 362 switch self.RedisReplicasNum { 363 case 1: 364 return "single" 365 case 2: 366 return "double" 367 case 3: 368 return "three" 369 case 4: 370 return "four" 371 case 5: 372 return "five" 373 case 6: 374 return "six" 375 } 376 377 return strconv.Itoa(self.RedisReplicasNum) 378 } 379 380 func (self *SElasticcache) GetEngine() string { 381 return api.ELASTIC_CACHE_ENGINE_REDIS 382 } 383 384 func (self *SElasticcache) GetEngineVersion() string { 385 switch self.Type { 386 case 1, 2, 5: 387 return "2.8" 388 case 3, 4: 389 return "3.2" 390 case 6, 7, 10: 391 return "4.0" 392 case 8, 9: 393 return "5.0" 394 } 395 396 return "unknown" 397 } 398 399 func (self *SElasticcache) GetVpcId() string { 400 return self.UniqVpcID 401 } 402 403 // https://cloud.tencent.com/document/product/239/4106 404 func (self *SElasticcache) GetZoneId() string { 405 zone := zoneMaps[self.ZoneID] 406 if len(zone) == 0 { 407 net, err := self.region.GetNetwork(self.UniqSubnetID) 408 if err != nil { 409 log.Warningf("zone %d not found. zone map needed update.", self.ZoneID) 410 log.Debugf("GetNetwork %s %s", self.UniqSubnetID, err) 411 return "" 412 } 413 414 zone = net.Zone 415 } 416 417 z, err := self.region.getZoneById(zone) 418 if err != nil { 419 log.Debugf("getZoneById %s", err) 420 return "" 421 } 422 423 return z.GetGlobalId() 424 } 425 426 func (self *SElasticcache) GetNetworkType() string { 427 if len(self.UniqVpcID) > 0 { 428 return api.LB_NETWORK_TYPE_VPC 429 } else { 430 return api.LB_NETWORK_TYPE_CLASSIC 431 } 432 } 433 434 func (self *SElasticcache) GetNetworkId() string { 435 return self.UniqSubnetID 436 } 437 438 func (self *SElasticcache) GetPrivateDNS() string { 439 return self.WANIP 440 } 441 442 func (self *SElasticcache) GetPrivateIpAddr() string { 443 return self.WANIP 444 } 445 446 func (self *SElasticcache) GetPrivateConnectPort() int { 447 return self.Port 448 } 449 450 func (self *SElasticcache) GetPublicDNS() string { 451 return "" 452 } 453 454 func (self *SElasticcache) GetPublicIpAddr() string { 455 return "" 456 } 457 458 func (self *SElasticcache) GetPublicConnectPort() int { 459 return self.Port 460 } 461 462 // https://cloud.tencent.com/document/api/239/46336 463 func (self *SElasticcache) getMaintenanceTime() error { 464 params := map[string]string{} 465 params["InstanceId"] = self.GetId() 466 params["Region"] = self.region.GetId() 467 resp, err := self.region.client.redisRequest("DescribeMaintenanceWindow", params) 468 if err != nil { 469 return errors.Wrap(err, "DescribeMaintenanceWindow") 470 } 471 472 self.MaintenanceTime = &MaintenanceTime{} 473 err = resp.Unmarshal(self.MaintenanceTime) 474 if err != nil { 475 return errors.Wrap(err, "err.MaintenanceTime") 476 } 477 478 return nil 479 } 480 481 func (self *SElasticcache) GetMaintainStartTime() string { 482 if self.MaintenanceTime == nil { 483 if err := self.getMaintenanceTime(); err != nil { 484 log.Debugf("getMaintenanceTime %s", err) 485 return "" 486 } 487 } 488 489 return self.MaintenanceTime.StartTime 490 } 491 492 func (self *SElasticcache) GetMaintainEndTime() string { 493 if self.MaintenanceTime == nil { 494 if err := self.getMaintenanceTime(); err != nil { 495 log.Debugf("getMaintenanceTime %s", err) 496 return "" 497 } 498 } 499 500 return self.MaintenanceTime.EndTime 501 } 502 503 func (self *SElasticcache) GetAuthMode() string { 504 if self.NoAuth != nil && *self.NoAuth == false { 505 return "on" 506 } 507 508 return "off" 509 } 510 511 func (self *SElasticcache) GetSecurityGroupIds() ([]string, error) { 512 ss, err := self.region.GetCloudElasticcacheSecurityGroups(self.GetId()) 513 if err != nil { 514 return nil, errors.Wrap(err, "GetCloudElasticcacheSecurityGroups") 515 } 516 517 ret := make([]string, len(ss)) 518 for i := range ss { 519 ret[i] = ss[i].SecurityGroupID 520 } 521 522 return ret, nil 523 } 524 525 func (self *SElasticcache) GetICloudElasticcacheAccounts() ([]cloudprovider.ICloudElasticcacheAccount, error) { 526 accounts, err := self.getCloudElasticcacheAccounts() 527 if err != nil { 528 return nil, errors.Wrap(err, "GetCloudElasticcacheAccounts") 529 } 530 531 iaccounts := make([]cloudprovider.ICloudElasticcacheAccount, len(accounts)) 532 for i := range accounts { 533 account := accounts[i] 534 account.cacheDB = self 535 iaccounts[i] = &account 536 } 537 538 return iaccounts, nil 539 } 540 541 func (self *SElasticcache) GetICloudElasticcacheAcls() ([]cloudprovider.ICloudElasticcacheAcl, error) { 542 return nil, cloudprovider.ErrNotSupported 543 } 544 545 func (self *SElasticcache) GetICloudElasticcacheBackups() ([]cloudprovider.ICloudElasticcacheBackup, error) { 546 backups, err := self.region.GetCloudElasticcacheBackups(self.GetId()) 547 if err != nil { 548 return nil, errors.Wrap(err, "GetCloudElasticcacheBackups") 549 } 550 551 ibackups := make([]cloudprovider.ICloudElasticcacheBackup, len(backups)) 552 for i := range backups { 553 backup := backups[i] 554 backup.cacheDB = self 555 ibackups[i] = &backup 556 } 557 558 return ibackups, nil 559 } 560 561 func (self *SElasticcache) GetICloudElasticcacheParameters() ([]cloudprovider.ICloudElasticcacheParameter, error) { 562 parameters, err := self.region.GetCloudElasticcacheParameters(self.GetId()) 563 if err != nil { 564 return nil, errors.Wrap(err, "GetCloudElasticcacheParameters") 565 } 566 567 ret := []cloudprovider.ICloudElasticcacheParameter{} 568 for i := range parameters { 569 parameter := parameters[i] 570 parameter.cacheDB = self 571 ret = append(ret, ¶meter) 572 } 573 574 return ret, nil 575 } 576 577 func (self *SElasticcache) GetICloudElasticcacheAccount(accountId string) (cloudprovider.ICloudElasticcacheAccount, error) { 578 accounts, err := self.getCloudElasticcacheAccounts() 579 if err != nil { 580 return nil, errors.Wrap(err, "GetCloudElasticcacheAccounts") 581 } 582 583 for i := range accounts { 584 account := accounts[i] 585 if account.GetGlobalId() == accountId { 586 account.cacheDB = self 587 return &account, nil 588 } 589 } 590 591 return nil, cloudprovider.ErrNotFound 592 } 593 594 func (self *SElasticcache) GetICloudElasticcacheAcl(aclId string) (cloudprovider.ICloudElasticcacheAcl, error) { 595 return nil, cloudprovider.ErrNotSupported 596 } 597 598 func (self *SElasticcache) GetICloudElasticcacheBackup(backupId string) (cloudprovider.ICloudElasticcacheBackup, error) { 599 backups, err := self.region.GetCloudElasticcacheBackups(self.InstanceID) 600 if err != nil { 601 return nil, errors.Wrap(err, "GetCloudElasticcacheBackups") 602 } 603 604 for i := range backups { 605 backup := backups[i] 606 if backup.GetGlobalId() == backupId { 607 backup.cacheDB = self 608 return &backup, nil 609 } 610 } 611 612 return nil, cloudprovider.ErrNotFound 613 } 614 615 func (self *SElasticcache) GetICloudElasticcacheBackupByReadmark(readmark string) (cloudprovider.ICloudElasticcacheBackup, error) { 616 backups, err := self.region.GetCloudElasticcacheBackups(self.InstanceID) 617 if err != nil { 618 return nil, errors.Wrap(err, "GetCloudElasticcacheBackups") 619 } 620 621 for i := range backups { 622 backup := backups[i] 623 if backup.Remark == readmark { 624 backup.cacheDB = self 625 return &backup, nil 626 } 627 } 628 629 return nil, cloudprovider.ErrNotFound 630 } 631 632 func (self *SElasticcache) Restart() error { 633 return errors.Wrap(cloudprovider.ErrNotSupported, "Restart") 634 } 635 636 // https://cloud.tencent.com/document/product/239/34440 637 func (self *SElasticcache) Delete() error { 638 requiredStatus := "" 639 if self.GetBillingType() == billing_api.BILLING_TYPE_POSTPAID { 640 if err := self.DestroyPostpaidInstance(); err != nil { 641 return errors.Wrap(err, "DestroyPostpaidInstance") 642 } 643 644 requiredStatus = api.ELASTIC_CACHE_STATUS_RELEASING 645 } else { 646 if err := self.DestroyPrepaidInstance(); err != nil { 647 return errors.Wrap(err, "DestroyPrepaidInstance") 648 } 649 650 requiredStatus = api.ELASTIC_CACHE_STATUS_UNAVAILABLE 651 } 652 653 err := cloudprovider.WaitStatus(self, requiredStatus, 5*time.Second, 300*time.Second) 654 if err != nil { 655 return errors.Wrap(err, fmt.Sprintf("WaitStatus.%s", requiredStatus)) 656 } 657 658 err = self.CleanupInstance() 659 if err != nil { 660 return errors.Wrap(err, "CleanupInstance") 661 } 662 663 return nil 664 } 665 666 // https://cloud.tencent.com/document/product/239/34439 667 func (self *SElasticcache) DestroyPrepaidInstance() error { 668 if self.GetStatus() == api.ELASTIC_CACHE_STATUS_UNAVAILABLE { 669 return nil 670 } 671 672 params := map[string]string{} 673 params["InstanceId"] = self.GetId() 674 _, err := self.region.redisRequest("DestroyPrepaidInstance", params) 675 if err != nil { 676 return errors.Wrap(err, "DestroyPrepaidInstance") 677 } 678 679 return nil 680 } 681 682 // https://cloud.tencent.com/document/product/239/34439 683 func (self *SElasticcache) DestroyPostpaidInstance() error { 684 if self.GetStatus() == api.ELASTIC_CACHE_STATUS_RELEASING { 685 return nil 686 } 687 688 params := map[string]string{} 689 params["InstanceId"] = self.GetId() 690 _, err := self.region.redisRequest("DestroyPostpaidInstance", params) 691 if err != nil { 692 return errors.Wrap(err, "DestroyPostpaidInstance") 693 } 694 695 return nil 696 } 697 698 // https://cloud.tencent.com/document/product/239/34442 699 func (self *SElasticcache) CleanupInstance() error { 700 params := map[string]string{} 701 params["InstanceId"] = self.GetId() 702 _, err := self.region.redisRequest("CleanUpInstance", params) 703 if err != nil { 704 return errors.Wrap(err, "CleanUpInstance") 705 } 706 707 return nil 708 } 709 710 // https://cloud.tencent.com/document/product/239/20013 711 func (self *SElasticcache) ChangeInstanceSpec(spec string) error { 712 s, err := parseLocalInstanceSpec(spec) 713 if err != nil { 714 return errors.Wrap(err, "parseLocalInstanceSpec") 715 } 716 717 params := map[string]string{} 718 params["InstanceId"] = self.GetId() 719 params["MemSize"] = fmt.Sprintf("%d", s.MemSizeMB) 720 params["RedisShardNum"] = s.RedisShardNum 721 params["RedisReplicasNum"] = s.RedisReplicasNum 722 _, err = self.region.redisRequest("UpgradeInstance", params) 723 if err != nil { 724 return errors.Wrap(err, "UpgradeInstance") 725 } 726 727 return nil 728 } 729 730 // https://cloud.tencent.com/document/product/239/46335 731 func (self *SElasticcache) SetMaintainTime(maintainStartTime, maintainEndTime string) error { 732 params := map[string]string{} 733 params["InstanceId"] = self.GetId() 734 params["StartTime"] = maintainStartTime 735 params["EndTime"] = maintainEndTime 736 _, err := self.region.redisRequest("ModifyMaintenanceWindow", params) 737 if err != nil { 738 return errors.Wrap(err, "ModifyMaintenanceWindow") 739 } 740 741 return nil 742 } 743 744 func (self *SElasticcache) AllocatePublicConnection(port int) (string, error) { 745 return "", errors.Wrap(cloudprovider.ErrNotSupported, "AllocatePublicConnection") 746 } 747 748 func (self *SElasticcache) ReleasePublicConnection() error { 749 return errors.Wrap(cloudprovider.ErrNotSupported, "ReleasePublicConnection") 750 } 751 752 // https://cloud.tencent.com/document/product/239/38926 753 func (self *SElasticcache) CreateAccount(account cloudprovider.SCloudElasticCacheAccountInput) (cloudprovider.ICloudElasticcacheAccount, error) { 754 params := map[string]string{} 755 params["InstanceId"] = self.GetId() 756 params["AccountName"] = account.AccountName 757 params["AccountPassword"] = account.AccountPassword 758 params["Privilege"] = account.AccountPrivilege 759 params["ReadonlyPolicy.0"] = "master" 760 if len(account.Description) > 0 { 761 params["Remark"] = account.Description 762 } 763 764 _, err := self.region.redisRequest("CreateInstanceAccount", params) 765 if err != nil { 766 return nil, errors.Wrap(err, "CreateInstanceAccount") 767 } 768 769 accountId := "" 770 cloudprovider.Wait(5*time.Second, 60*time.Second, func() (bool, error) { 771 accounts, err := self.GetICloudElasticcacheAccounts() 772 if err != nil { 773 log.Debugf("GetICloudElasticcacheAccounts %s", err) 774 return false, nil 775 } 776 777 for i := range accounts { 778 if accounts[i].GetName() == account.AccountName { 779 accountId = accounts[i].GetGlobalId() 780 return true, nil 781 } 782 } 783 784 return false, nil 785 }) 786 787 return self.GetICloudElasticcacheAccount(accountId) 788 } 789 790 func (self *SElasticcache) CreateAcl(aclName, securityIps string) (cloudprovider.ICloudElasticcacheAcl, error) { 791 return nil, errors.Wrap(cloudprovider.ErrNotSupported, "CreateAcl") 792 } 793 794 // https://cloud.tencent.com/document/product/239/20010 795 func (self *SElasticcache) CreateBackup(desc string) (cloudprovider.ICloudElasticcacheBackup, error) { 796 readmark := strings.Join([]string{desc, fmt.Sprintf("%d", time.Now().Unix())}, "@") 797 params := map[string]string{} 798 params["InstanceId"] = self.GetId() 799 params["Remark"] = readmark 800 resp, err := self.region.redisRequest("ManualBackupInstance", params) 801 if err != nil { 802 return nil, errors.Wrap(err, "ManualBackupInstance") 803 } 804 805 taskId := 0 806 err = resp.Unmarshal(&taskId, "TaskId") 807 if err != nil { 808 return nil, errors.Wrap(err, "Unmarshal") 809 } 810 811 var task *SElasticcacheTask 812 err = cloudprovider.Wait(5*time.Second, 900*time.Second, func() (bool, error) { 813 task, err = self.region.DescribeTaskInfo(fmt.Sprintf("%d", taskId)) 814 if err != nil { 815 return false, nil 816 } 817 818 if task != nil { 819 if task.Status == "failed" || task.Status == "error" { 820 return false, fmt.Errorf("CreateBackup failed %#v", task) 821 } 822 823 if task.Status == "succeed" { 824 return true, nil 825 } 826 } 827 828 return false, nil 829 }) 830 if err != nil { 831 return nil, errors.Wrap(err, "Wait.DescribeTaskInfo") 832 } 833 834 if task == nil { 835 return nil, fmt.Errorf("CreateBackup failed task is empty") 836 } 837 838 return self.GetICloudElasticcacheBackupByReadmark(readmark) 839 } 840 841 // https://cloud.tencent.com/document/product/239/20021 842 func (self *SElasticcache) FlushInstance(input cloudprovider.SCloudElasticCacheFlushInstanceInput) error { 843 params := map[string]string{} 844 params["InstanceId"] = self.GetId() 845 if self.NoAuth != nil && *self.NoAuth == false { 846 if len(input.Password) > 0 { 847 params["Password"] = input.Password 848 } else { 849 return fmt.Errorf("password required on auth mode while flush elastich cache instance") 850 } 851 } 852 _, err := self.region.redisRequest("ClearInstance", params) 853 if err != nil { 854 return errors.Wrap(err, "ClearInstance") 855 } 856 857 return nil 858 } 859 860 // https://cloud.tencent.com/document/product/239/38923 861 // https://cloud.tencent.com/document/product/239/20014 862 // true表示将主账号切换为免密账号,这里只适用于主账号,子账号不可免密 863 func (self *SElasticcache) UpdateAuthMode(noPasswordAccess bool, password string) error { 864 params := map[string]string{} 865 params["InstanceId"] = self.GetId() 866 if noPasswordAccess { 867 params["NoAuth"] = "true" 868 } else { 869 params["NoAuth"] = "false" 870 params["Password"] = password 871 } 872 873 _, err := self.region.redisRequest("ResetPassword", params) 874 if err != nil { 875 return errors.Wrap(err, "ResetPassword") 876 } 877 878 return nil 879 } 880 881 // https://cloud.tencent.com/document/product/239/34445 882 // todo: finish me 883 func (self *SElasticcache) UpdateInstanceParameters(config jsonutils.JSONObject) error { 884 params := map[string]string{} 885 params["InstanceId"] = self.GetId() 886 params["InstanceParams.0"] = config.String() 887 _, err := self.region.redisRequest("ModifyInstanceParams", params) 888 if err != nil { 889 return errors.Wrap(err, "ModifyInstanceParams") 890 } 891 892 return nil 893 } 894 895 func (self *SElasticcache) UpdateBackupPolicy(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error { 896 panic("implement me") 897 } 898 899 func (self *SElasticcache) getCloudElasticcacheAccounts() ([]SElasticcacheAccount, error) { 900 if self.GetEngineVersion() == "2.8" || (self.NodeSet != nil && len(self.NodeSet) > 0) { 901 account := SElasticcacheAccount{} 902 account.cacheDB = self 903 account.AccountName = "root" 904 account.InstanceID = self.GetId() 905 account.Privilege = "rw" 906 account.Status = 2 907 account.IsEmulate = true 908 909 return []SElasticcacheAccount{account}, nil 910 } 911 912 accounts, err := self.region.GetCloudElasticcacheAccounts(self.GetId()) 913 if err != nil { 914 return nil, errors.Wrap(err, "GetCloudElasticcacheAccounts") 915 } 916 917 for i := range accounts { 918 accounts[i].cacheDB = self 919 } 920 921 return accounts, nil 922 } 923 924 // https://cloud.tencent.com/document/api/239/38924 925 func (self *SElasticcache) getCloudElasticcacheAccount(accountName string) (*SElasticcacheAccount, error) { 926 accounts, err := self.getCloudElasticcacheAccounts() 927 if err != nil { 928 return nil, errors.Wrap(err, "GetCloudElasticcacheAccounts") 929 } 930 931 for i := range accounts { 932 if accounts[i].AccountName == accountName { 933 accounts[i].cacheDB = self 934 return &accounts[i], nil 935 } 936 } 937 938 return nil, cloudprovider.ErrNotFound 939 } 940 941 // https://cloud.tencent.com/document/api/239/41256 942 func (self *SElasticcache) UpdateSecurityGroups(secgroupIds []string) error { 943 params := map[string]string{} 944 params["InstanceId"] = self.GetId() 945 params["Product"] = "redis" 946 for i := range secgroupIds { 947 params[fmt.Sprintf("SecurityGroupIds.%d", i)] = secgroupIds[i] 948 } 949 950 _, err := self.region.redisRequest("ModifyDBInstanceSecurityGroups", params) 951 if err != nil { 952 return errors.Wrap(err, "ModifyDBInstanceSecurityGroups") 953 } 954 955 return nil 956 } 957 958 func (self *SElasticcache) Renew(bc billing.SBillingCycle) error { 959 month := bc.GetMonths() 960 if month <= 0 { 961 return errors.Wrap(fmt.Errorf("month should great than 0"), "GetMonths") 962 } 963 964 params := map[string]string{} 965 params["InstanceId"] = self.GetId() 966 params["Period"] = fmt.Sprintf("%d", month) 967 _, err := self.region.redisRequest("RenewInstance", params) 968 if err != nil { 969 return errors.Wrap(err, "RenewInstance") 970 } 971 972 return nil 973 } 974 975 // https://cloud.tencent.com/document/api/239/38924 976 func (self *SRegion) GetCloudElasticcacheAccounts(instanceId string) ([]SElasticcacheAccount, error) { 977 params := map[string]string{} 978 params["Region"] = self.GetId() 979 params["InstanceId"] = instanceId 980 params["Limit"] = "20" 981 params["Offset"] = "0" 982 983 ret := []SElasticcacheAccount{} 984 offset := 0 985 for { 986 resp, err := self.client.redisRequest("DescribeInstanceAccount", params) 987 if err != nil { 988 return nil, errors.Wrap(err, "DescribeInstanceAccount") 989 } 990 991 _ret := []SElasticcacheAccount{} 992 err = resp.Unmarshal(&_ret, "Accounts") 993 if err != nil { 994 return nil, errors.Wrap(err, "Unmarshal") 995 } else { 996 ret = append(ret, _ret...) 997 } 998 999 if len(_ret) < 20 { 1000 break 1001 } else { 1002 offset += 20 1003 params["Offset"] = strconv.Itoa(offset) 1004 } 1005 } 1006 1007 return ret, nil 1008 } 1009 1010 // https://cloud.tencent.com/document/api/239/20011 1011 func (self *SRegion) GetCloudElasticcacheBackups(instanceId string) ([]SElasticcacheBackup, error) { 1012 params := map[string]string{} 1013 params["Region"] = self.GetId() 1014 params["InstanceId"] = instanceId 1015 params["Limit"] = "20" 1016 params["Offset"] = "0" 1017 1018 ret := []SElasticcacheBackup{} 1019 offset := 0 1020 for { 1021 resp, err := self.client.redisRequest("DescribeInstanceBackups", params) 1022 if err != nil { 1023 return nil, errors.Wrap(err, "DescribeInstanceBackups") 1024 } 1025 1026 _ret := []SElasticcacheBackup{} 1027 err = resp.Unmarshal(&_ret, "BackupSet") 1028 if err != nil { 1029 return nil, errors.Wrap(err, "Unmarshal") 1030 } else { 1031 ret = append(ret, _ret...) 1032 } 1033 1034 if len(_ret) < 20 { 1035 break 1036 } else { 1037 offset += 20 1038 params["Offset"] = strconv.Itoa(offset) 1039 } 1040 } 1041 1042 return ret, nil 1043 } 1044 1045 // https://cloud.tencent.com/document/api/239/41259 1046 func (self *SRegion) GetCloudElasticcacheSecurityGroups(instanceId string) ([]SElasticcacheSecgroup, error) { 1047 params := map[string]string{} 1048 params["Region"] = self.GetId() 1049 params["InstanceId"] = instanceId 1050 params["Product"] = "redis" 1051 resp, err := self.client.redisRequest("DescribeDBSecurityGroups", params) 1052 if err != nil { 1053 return nil, errors.Wrap(err, "DescribeDBSecurityGroups") 1054 } 1055 1056 ret := []SElasticcacheSecgroup{} 1057 err = resp.Unmarshal(&ret, "Groups") 1058 if err != nil { 1059 return nil, errors.Wrap(err, "Unmarshal") 1060 } 1061 1062 return ret, nil 1063 } 1064 1065 // https://cloud.tencent.com/document/api/239/34448 1066 func (self *SRegion) GetCloudElasticcacheParameters(instanceId string) ([]SElasticcacheParameter, error) { 1067 params := map[string]string{} 1068 params["Region"] = self.GetId() 1069 params["InstanceId"] = instanceId 1070 resp, err := self.client.redisRequest("DescribeInstanceParams", params) 1071 if err != nil { 1072 return nil, errors.Wrap(err, "DescribeInstanceParams") 1073 } 1074 1075 ret1 := []SElasticcacheParameter{} 1076 err = resp.Unmarshal(&ret1, "InstanceEnumParam") 1077 if err != nil { 1078 return nil, errors.Wrap(err, "InstanceEnumParam") 1079 } 1080 1081 ret2 := []SElasticcacheParameter{} 1082 err = resp.Unmarshal(&ret2, "InstanceIntegerParam") 1083 if err != nil { 1084 return nil, errors.Wrap(err, "InstanceIntegerParam") 1085 } 1086 1087 ret3 := []SElasticcacheParameter{} 1088 err = resp.Unmarshal(&ret3, "InstanceMultiParam") 1089 if err != nil { 1090 return nil, errors.Wrap(err, "InstanceMultiParam") 1091 } 1092 1093 ret4 := []SElasticcacheParameter{} 1094 err = resp.Unmarshal(&ret4, "InstanceTextParam") 1095 if err != nil { 1096 return nil, errors.Wrap(err, "InstanceTextParam") 1097 } 1098 1099 ret := []SElasticcacheParameter{} 1100 ret = append(ret, ret1...) 1101 ret = append(ret, ret2...) 1102 ret = append(ret, ret3...) 1103 ret = append(ret, ret4...) 1104 return ret, nil 1105 } 1106 1107 // https://cloud.tencent.com/document/api/239/20018 1108 func (self *SRegion) GetCloudElasticcaches(instanceId string) ([]SElasticcache, error) { 1109 params := map[string]string{} 1110 params["Region"] = self.GetId() 1111 params["Limit"] = "20" 1112 params["Offset"] = "0" 1113 1114 if len(instanceId) > 0 { 1115 params["InstanceId"] = instanceId 1116 } 1117 1118 ret := []SElasticcache{} 1119 offset := 0 1120 for { 1121 resp, err := self.client.redisRequest("DescribeInstances", params) 1122 if err != nil { 1123 return nil, errors.Wrap(err, "DescribeInstances") 1124 } 1125 1126 _ret := []SElasticcache{} 1127 err = resp.Unmarshal(&_ret, "InstanceSet") 1128 if err != nil { 1129 return nil, errors.Wrap(err, "Unmarshal") 1130 } else { 1131 ret = append(ret, _ret...) 1132 } 1133 1134 if len(_ret) < 20 { 1135 break 1136 } else { 1137 offset += 20 1138 params["Offset"] = strconv.Itoa(offset) 1139 } 1140 } 1141 1142 return ret, nil 1143 } 1144 1145 type elasticcachInstanceSpec struct { 1146 TypeId string `json:"type_id"` 1147 MemSizeMB int `json:"mem_size_mb"` 1148 DiskSizeGB int `json:"disk_size_gb"` 1149 RedisShardNum string `json:"redis_shard_num"` 1150 RedisReplicasNum string `json:"redis_replicas_num"` 1151 } 1152 1153 // redis:master:s1:r5:m1g:v4.0 1154 func parseLocalInstanceSpec(s string) (elasticcachInstanceSpec, error) { 1155 ret := elasticcachInstanceSpec{} 1156 segs := strings.Split(s, ":") 1157 if len(segs) != 6 { 1158 return ret, fmt.Errorf("invalid instance spec %s", s) 1159 } 1160 if segs[1] == "master" { 1161 switch segs[5] { 1162 case "v2.8": 1163 ret.TypeId = "2" 1164 case "v3.2": 1165 ret.TypeId = "3" 1166 case "v4.0": 1167 ret.TypeId = "6" 1168 case "v5.0": 1169 ret.TypeId = "8" 1170 default: 1171 return ret, fmt.Errorf("unknown master elastic cache version %s", segs[1]) 1172 } 1173 } else if segs[1] == "cluster" { 1174 switch segs[5] { 1175 case "v3.0": 1176 ret.TypeId = "4" 1177 case "v4.0": 1178 ret.TypeId = "7" 1179 case "v5.0": 1180 if strings.Contains(segs[4], "-d") { 1181 ret.TypeId = "10" 1182 } else { 1183 ret.TypeId = "9" 1184 } 1185 1186 default: 1187 return ret, fmt.Errorf("unknown cluster elastic cache version %s", segs[1]) 1188 } 1189 } else if segs[1] == "single" { 1190 switch segs[5] { 1191 case "v2.8": 1192 ret.TypeId = "5" 1193 default: 1194 return ret, fmt.Errorf("unknown single elastic cache version %s", segs[1]) 1195 } 1196 } else { 1197 return ret, fmt.Errorf("unknown elastic cache type %s", segs[1]) 1198 } 1199 1200 // 1201 sizes := strings.Split(segs[4], "-") 1202 if len(sizes) == 2 { 1203 ms, err := strconv.Atoi(strings.Trim(sizes[1], "dg")) 1204 if err != nil { 1205 return ret, errors.Wrap(err, "Atoi") 1206 } 1207 1208 ret.DiskSizeGB = ms 1209 } 1210 1211 sn, err := strconv.Atoi(strings.Trim(segs[2], "s")) 1212 if err != nil { 1213 return ret, errors.Wrap(err, "RedisShardNum") 1214 } 1215 1216 ms, err := strconv.Atoi(strings.Trim(sizes[0], "mdg")) 1217 if err != nil { 1218 return ret, errors.Wrap(err, "Atoi") 1219 } 1220 if strings.HasSuffix(sizes[0], "g") { 1221 ret.MemSizeMB = ms * 1024 / sn 1222 } else if strings.HasSuffix(sizes[0], "m") { 1223 ret.MemSizeMB = ms / sn 1224 } 1225 1226 ret.RedisShardNum = strings.Trim(segs[2], "s") 1227 ret.RedisReplicasNum = strings.Trim(segs[3], "r") 1228 return ret, nil 1229 }