yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/redis.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 hcs 16 17 import ( 18 "fmt" 19 "net/url" 20 "strings" 21 "time" 22 23 "yunion.io/x/jsonutils" 24 "yunion.io/x/log" 25 "yunion.io/x/onecloud/pkg/util/billing" 26 "yunion.io/x/pkg/errors" 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 ) 33 34 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html 35 type SElasticcache struct { 36 multicloud.SElasticcacheBase 37 HcsTags 38 39 region *SRegion 40 41 Name string `json:"name"` 42 Engine string `json:"engine"` 43 CapacityGB int `json:"capacity"` 44 IP string `json:"ip"` 45 DomainName string `json:"domainName"` 46 Port int `json:"port"` 47 Status string `json:"status"` 48 Libos bool `json:"libos"` 49 Description string `json:"description"` 50 Task string `json:"task"` 51 MaxMemoryMB int `json:"max_memory"` 52 UsedMemoryMB int `json:"used_memory"` 53 InstanceId string `json:"instance_id"` 54 ResourceSpecCode string `json:"resource_spec_code"` 55 EngineVersion string `json:"engine_version"` 56 InternalVersion string `json:"internal_version"` 57 ChargingMode int `json:"charging_mode"` 58 CapacityMinor string `json:"capacity_minor"` 59 VpcId string `json:"vpc_id"` 60 VpcName string `json:"vpc_name"` 61 TaskStatus string `json:"task_status"` 62 CreatedAt string `json:"created_at"` 63 ErrorCode string `json:"error_code"` 64 UserId string `json:"user_id"` 65 UserName string `json:"user_name"` 66 MaintainBegin string `json:"maintain_begin"` 67 MaintainEnd string `json:"maintain_end"` 68 NoPasswordAccess string `json:"no_password_access"` 69 AccessUser string `json:"access_user"` 70 EnablePublicip bool `json:"enable_publicip"` 71 PublicipId string `json:"publicip_id"` 72 PublicipAddress string `json:"publicip_address"` 73 EnableSSL bool `json:"enable_ssl"` 74 ServiceUpgrade bool `json:"service_upgrade"` 75 ServiceTaskId string `json:"service_task_id"` 76 IsFree string `json:"is_free"` 77 EnterpriseProjectId string `json:"enterprise_project_id"` 78 AvailableZones []string `json:"available_zones"` 79 SubnetId string `json:"subnet_id"` 80 SecurityGroupId string `json:"security_group_id"` 81 BackendAddrs []string `json:"backend_addrs"` 82 ProductId string `json:"product_id"` 83 SecurityGroupName string `json:"security_group_name"` 84 SubnetName string `json:"subnet_name"` 85 OrderId string `json:"order_id"` 86 SubnetCIdR string `json:"subnet_cidr"` 87 InstanceBackupPolicy string `json:"instance_backup_policy"` 88 EnterpriseProjectName string `json:"enterprise_project_name"` 89 } 90 91 func (self *SElasticcache) GetId() string { 92 return self.InstanceId 93 } 94 95 func (self *SElasticcache) GetName() string { 96 return self.Name 97 } 98 99 func (self *SElasticcache) GetGlobalId() string { 100 return self.GetId() 101 } 102 103 func (self *SElasticcache) GetProjectId() string { 104 return self.EnterpriseProjectId 105 } 106 107 func (self *SElasticcache) Refresh() error { 108 cache, err := self.region.GetElasticCache(self.GetId()) 109 if err != nil { 110 return errors.Wrap(err, "Elasticcache.Refresh.GetElasticCache") 111 } 112 113 err = jsonutils.Update(self, cache) 114 if err != nil { 115 return errors.Wrap(err, "Elasticcache.Refresh.Update") 116 } 117 118 return nil 119 } 120 121 func (self *SElasticcache) GetStatus() string { 122 switch self.Status { 123 case "RUNNING": 124 return api.ELASTIC_CACHE_STATUS_RUNNING 125 case "CREATING": 126 return api.ELASTIC_CACHE_STATUS_DEPLOYING 127 case "CREATEFAILED": 128 return api.ELASTIC_CACHE_STATUS_CREATE_FAILED 129 case "ERROR": 130 return api.ELASTIC_CACHE_STATUS_ERROR 131 case "RESTARTING": 132 return api.ELASTIC_CACHE_STATUS_RESTARTING 133 case "FROZEN": 134 return api.ELASTIC_CACHE_STATUS_UNAVAILABLE 135 case "EXTENDING": 136 return api.ELASTIC_CACHE_STATUS_CHANGING 137 case "RESTORING": 138 return api.ELASTIC_CACHE_STATUS_TRANSFORMING // ? 139 case "FLUSHING": 140 return api.ELASTIC_CACHE_STATUS_FLUSHING 141 } 142 143 return "" 144 } 145 146 func (self *SElasticcache) GetBillingType() string { 147 return billing_api.BILLING_TYPE_POSTPAID 148 } 149 150 func (self *SElasticcache) GetCreatedAt() time.Time { 151 var createtime time.Time 152 if len(self.CreatedAt) > 0 { 153 createtime, _ = time.Parse("2006-01-02T15:04:05.000Z", self.CreatedAt) 154 } 155 156 return createtime 157 } 158 159 func (self *SElasticcache) GetInstanceType() string { 160 return self.ResourceSpecCode 161 } 162 163 func (self *SElasticcache) GetCapacityMB() int { 164 return self.CapacityGB * 1024 165 } 166 167 func (self *SElasticcache) GetArchType() string { 168 if strings.Contains(self.ResourceSpecCode, "single") { 169 return api.ELASTIC_CACHE_ARCH_TYPE_SINGLE 170 } 171 if strings.Contains(self.ResourceSpecCode, "ha") { 172 return api.ELASTIC_CACHE_ARCH_TYPE_MASTER 173 } 174 if strings.Contains(self.ResourceSpecCode, "cluster") { 175 return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER 176 } 177 if strings.Contains(self.ResourceSpecCode, "proxy") { 178 return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER 179 } 180 return "" 181 } 182 183 func (self *SElasticcache) GetNodeType() string { 184 // single(单副本) | double(双副本) 185 if strings.Contains(self.ResourceSpecCode, "single") { 186 return "single" 187 } 188 return "double" 189 } 190 191 func (self *SElasticcache) GetEngine() string { 192 return self.Engine 193 } 194 195 func (self *SElasticcache) GetEngineVersion() string { 196 return self.EngineVersion 197 } 198 199 func (self *SElasticcache) GetVpcId() string { 200 return self.VpcId 201 } 202 203 func (self *SElasticcache) GetZoneId() string { 204 if len(self.AvailableZones) > 0 { 205 zone, err := self.region.GetZoneById(self.AvailableZones[0]) 206 if err != nil { 207 log.Errorf("elasticcache.GetZoneId %s", err) 208 return "" 209 } 210 return zone.GetGlobalId() 211 } 212 return "" 213 } 214 215 func (self *SElasticcache) GetNetworkType() string { 216 return api.LB_NETWORK_TYPE_VPC 217 } 218 219 func (self *SElasticcache) GetNetworkId() string { 220 return self.SubnetId 221 } 222 223 func (self *SElasticcache) GetPrivateDNS() string { 224 return self.DomainName 225 } 226 227 func (self *SElasticcache) GetPrivateIpAddr() string { 228 return self.IP 229 } 230 231 func (self *SElasticcache) GetPrivateConnectPort() int { 232 return self.Port 233 } 234 235 func (self *SElasticcache) GetPublicDNS() string { 236 return self.PublicipAddress 237 } 238 239 func (self *SElasticcache) GetPublicIpAddr() string { 240 return self.PublicipAddress 241 } 242 243 func (self *SElasticcache) GetPublicConnectPort() int { 244 return self.Port 245 } 246 247 func (self *SElasticcache) GetMaintainStartTime() string { 248 return self.MaintainBegin 249 } 250 251 func (self *SElasticcache) GetMaintainEndTime() string { 252 return self.MaintainEnd 253 } 254 255 func (self *SElasticcache) GetICloudElasticcacheAccounts() ([]cloudprovider.ICloudElasticcacheAccount, error) { 256 iaccounts := []cloudprovider.ICloudElasticcacheAccount{} 257 iaccount := &SElasticcacheAccount{cacheDB: self} 258 iaccounts = append(iaccounts, iaccount) 259 return iaccounts, nil 260 } 261 262 func (self *SElasticcache) GetICloudElasticcacheAcls() ([]cloudprovider.ICloudElasticcacheAcl, error) { 263 // 华为云使用安全组做访问控制。目前未支持 264 return []cloudprovider.ICloudElasticcacheAcl{}, nil 265 } 266 267 func (self *SElasticcache) GetICloudElasticcacheBackups() ([]cloudprovider.ICloudElasticcacheBackup, error) { 268 start := self.GetCreatedAt().Format("20060102150405") 269 end := time.Now().Format("20060102150405") 270 backups, err := self.region.GetElasticCacheBackups(self.GetId(), start, end) 271 if err != nil { 272 return nil, err 273 } 274 275 ibackups := make([]cloudprovider.ICloudElasticcacheBackup, len(backups)) 276 for i := range backups { 277 backups[i].cacheDB = self 278 ibackups[i] = &backups[i] 279 } 280 281 return ibackups, nil 282 } 283 284 func (self *SElasticcache) GetICloudElasticcacheParameters() ([]cloudprovider.ICloudElasticcacheParameter, error) { 285 parameters, err := self.region.GetElasticCacheParameters(self.GetId()) 286 if err != nil { 287 return nil, err 288 } 289 290 iparameters := make([]cloudprovider.ICloudElasticcacheParameter, len(parameters)) 291 for i := range parameters { 292 parameters[i].cacheDB = self 293 iparameters[i] = ¶meters[i] 294 } 295 296 return iparameters, nil 297 } 298 299 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423035.html 300 func (self *SRegion) GetElasticCacheBackups(instanceId, startTime, endTime string) ([]SElasticcacheBackup, error) { 301 params := url.Values{} 302 params.Set("beginTime", startTime) 303 params.Set("endTime", endTime) 304 res := fmt.Sprintf("instances/%s/backups", instanceId) 305 ret := []SElasticcacheBackup{} 306 return ret, self.redisList(res, params, &ret) 307 } 308 309 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423027.html 310 func (self *SRegion) GetElasticCacheParameters(instanceId string) ([]SElasticcacheParameter, error) { 311 params := url.Values{} 312 res := fmt.Sprintf("instances/%s/configs", instanceId) 313 ret := []SElasticcacheParameter{} 314 return ret, self.redisList(res, params, &ret) 315 } 316 317 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423044.html 318 func (self *SRegion) GetElasticCaches() ([]SElasticcache, error) { 319 params := url.Values{} 320 ret := []SElasticcache{} 321 return ret, self.redisList("instances", params, &ret) 322 } 323 324 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html 325 func (self *SRegion) GetElasticCache(instanceId string) (*SElasticcache, error) { 326 cache := &SElasticcache{region: self} 327 res := fmt.Sprintf("instances/%s", instanceId) 328 return cache, self.redisGet(res, cache) 329 } 330 331 func (self *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) { 332 ec, err := self.GetElasticCache(id) 333 if err != nil { 334 return nil, errors.Wrapf(err, "GetElasticCache(%s)", id) 335 } 336 return ec, nil 337 } 338 339 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423047.html 340 func (self *SRegion) zoneNameToDcsZoneIds(zoneIds []string) ([]string, error) { 341 type Z struct { 342 Id string `json:"id"` 343 Code string `json:"code"` 344 Name string `json:"name"` 345 Port string `json:"port"` 346 ResourceAvailability string `json:"resource_availability"` 347 } 348 349 rs := []Z{} 350 err := self.redisList("availableZones", url.Values{}, &rs) 351 if err != nil { 352 return nil, errors.Wrapf(err, "availableZones") 353 } 354 zoneMap := map[string]string{} 355 for i := range rs { 356 if rs[i].ResourceAvailability == "true" { 357 zoneMap[rs[i].Code] = rs[i].Id 358 } 359 } 360 361 ret := []string{} 362 for _, zone := range zoneIds { 363 if id, ok := zoneMap[zone]; ok { 364 ret = append(ret, id) 365 } else { 366 return nil, errors.Wrap(fmt.Errorf("zone %s not found or not available", zone), "region.zoneNameToDcsZoneIds") 367 } 368 } 369 370 return ret, nil 371 } 372 373 func (self *SRegion) CreateElasticcache(opts *cloudprovider.SCloudElasticCacheInput) (*SElasticcache, error) { 374 params := map[string]interface{}{ 375 "name": opts.InstanceName, 376 "engine": opts.Engine, 377 "engine_version": opts.EngineVersion, 378 "capacity": opts.CapacityGB, 379 "vpc_id": opts.VpcId, 380 "subnet_id": opts.NetworkId, 381 "product_id": opts.InstanceType, 382 "spec_code": opts.InstanceType, 383 "available_zones": opts.ZoneIds, 384 "no_password_access": true, 385 "enable_publicip": false, 386 } 387 if len(opts.SecurityGroupIds) > 0 { 388 params["security_group_id"] = opts.SecurityGroupIds[0] 389 } 390 391 if len(opts.ProjectId) > 0 { 392 params["enterprise_project_id"] = opts.ProjectId 393 } 394 395 if len(opts.Password) > 0 { 396 params["no_password_access"] = false 397 params["password"] = opts.Password 398 if opts.Engine == "Memcache" { 399 params["access_user"] = opts.UserName 400 } 401 } 402 403 if len(opts.EipId) > 0 { 404 params["enable_publicip"] = true 405 params["publicip_id"] = opts.EipId 406 } 407 408 if len(opts.PrivateIpAddress) > 0 { 409 params["private_ip"] = opts.PrivateIpAddress 410 } 411 412 if len(opts.MaintainBegin) > 0 { 413 params["maintain_begin"] = opts.MaintainBegin 414 params["maintain_end"] = opts.MaintainEnd 415 } 416 417 ret := struct { 418 InstanceId string 419 }{} 420 err := self.redisCreate("instances", params, &ret) 421 if err != nil { 422 return nil, errors.Wrap(err, "redisCreate") 423 } 424 return self.GetElasticCache(ret.InstanceId) 425 } 426 427 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423019.html 428 func (self *SRegion) CreateIElasticcaches(opts *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) { 429 cache, err := self.CreateElasticcache(opts) 430 if err != nil { 431 return nil, err 432 } 433 return cache, nil 434 } 435 436 func (self *SElasticcache) Restart() error { 437 return nil 438 } 439 440 func (self *SElasticcache) Delete() error { 441 return self.region.DeleteElasticcache(self.InstanceId) 442 } 443 444 func (self *SRegion) DeleteElasticcache(id string) error { 445 res := fmt.Sprintf("instances/%s", id) 446 return self.redisDelete(res) 447 } 448 449 func (self *SElasticcache) ChangeInstanceSpec(spec string) error { 450 return cloudprovider.ErrNotImplemented 451 } 452 453 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423021.html 454 func (self *SElasticcache) SetMaintainTime(maintainStartTime, maintainEndTime string) error { 455 return cloudprovider.ErrNotImplemented 456 } 457 458 // https://support.huaweicloud.com/usermanual-dcs/dcs-zh-ug-180314001.html 459 // 目前只有Redis3.0版本密码模式的实例支持通过公网访问Redis实例,其他版本暂不支持公网访问。 460 // todo: 目前没找到api 461 func (self *SElasticcache) AllocatePublicConnection(port int) (string, error) { 462 return "", cloudprovider.ErrNotSupported 463 } 464 465 // todo: 目前没找到api 466 func (self *SElasticcache) ReleasePublicConnection() error { 467 return cloudprovider.ErrNotSupported 468 } 469 470 func (self *SElasticcache) CreateAccount(account cloudprovider.SCloudElasticCacheAccountInput) (cloudprovider.ICloudElasticcacheAccount, error) { 471 return nil, cloudprovider.ErrNotSupported 472 } 473 474 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423031.html 475 476 func (self *SElasticcache) CreateAcl(aclName, securityIps string) (cloudprovider.ICloudElasticcacheAcl, error) { 477 return nil, cloudprovider.ErrNotSupported 478 } 479 480 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423029.html 481 func (self *SElasticcache) UpdateInstanceParameters(config jsonutils.JSONObject) error { 482 return cloudprovider.ErrNotImplemented 483 } 484 485 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423033.html 486 func (self *SElasticcache) CreateBackup(desc string) (cloudprovider.ICloudElasticcacheBackup, error) { 487 return nil, cloudprovider.ErrNotSupported 488 } 489 490 func backupPeriodTrans(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) *jsonutils.JSONArray { 491 segs := strings.Split(config.PreferredBackupPeriod, ",") 492 ret := jsonutils.NewArray() 493 for _, seg := range segs { 494 switch seg { 495 case "Monday": 496 ret.Add(jsonutils.NewString("1")) 497 case "Tuesday": 498 ret.Add(jsonutils.NewString("2")) 499 case "Wednesday": 500 ret.Add(jsonutils.NewString("3")) 501 case "Thursday": 502 ret.Add(jsonutils.NewString("4")) 503 case "Friday": 504 ret.Add(jsonutils.NewString("5")) 505 case "Saturday": 506 ret.Add(jsonutils.NewString("6")) 507 case "Sunday": 508 ret.Add(jsonutils.NewString("7")) 509 } 510 } 511 512 return ret 513 } 514 515 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423021.html 516 func (self *SElasticcache) UpdateBackupPolicy(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error { 517 return nil 518 } 519 520 // https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423030.html 521 // 当前版本,只有DCS2.0实例支持清空数据功能,即flush操作。 522 func (self *SElasticcache) FlushInstance(input cloudprovider.SCloudElasticCacheFlushInstanceInput) error { 523 return nil 524 } 525 526 // SElasticcacheAccount => ResetPassword 527 func (self *SElasticcache) UpdateAuthMode(noPwdAccess bool, password string) error { 528 return cloudprovider.ErrNotSupported 529 } 530 531 func (self *SElasticcache) GetAuthMode() string { 532 switch self.NoPasswordAccess { 533 case "true": 534 return "off" 535 default: 536 return "on" 537 } 538 } 539 540 func (self *SElasticcache) GetSecurityGroupIds() ([]string, error) { 541 return nil, cloudprovider.ErrNotSupported 542 } 543 544 func (self *SElasticcache) GetICloudElasticcacheAccount(accountId string) (cloudprovider.ICloudElasticcacheAccount, error) { 545 accounts, err := self.GetICloudElasticcacheAccounts() 546 if err != nil { 547 return nil, errors.Wrap(err, "Elasticcache.GetICloudElasticcacheAccount.Accounts") 548 } 549 550 for i := range accounts { 551 account := accounts[i] 552 if account.GetGlobalId() == accountId { 553 return account, nil 554 } 555 } 556 557 return nil, cloudprovider.ErrNotFound 558 } 559 560 func (self *SElasticcache) GetICloudElasticcacheAcl(aclId string) (cloudprovider.ICloudElasticcacheAcl, error) { 561 return nil, cloudprovider.ErrNotSupported 562 } 563 564 func (self *SElasticcache) GetICloudElasticcacheBackup(backupId string) (cloudprovider.ICloudElasticcacheBackup, error) { 565 backups, err := self.GetICloudElasticcacheBackups() 566 if err != nil { 567 return nil, err 568 } 569 570 for _, backup := range backups { 571 if backup.GetId() == backupId { 572 return backup, nil 573 } 574 } 575 576 return nil, cloudprovider.ErrNotFound 577 } 578 579 func (instance *SElasticcache) SetTags(tags map[string]string, replace bool) error { 580 return cloudprovider.ErrNotImplemented 581 } 582 583 func (self *SElasticcache) UpdateSecurityGroups(secgroupIds []string) error { 584 return errors.Wrap(cloudprovider.ErrNotSupported, "UpdateSecurityGroups") 585 } 586 587 func (self *SElasticcache) Renew(bc billing.SBillingCycle) error { 588 return cloudprovider.ErrNotSupported 589 } 590 591 func (self *SElasticcache) SetAutoRenew(bc billing.SBillingCycle) error { 592 return cloudprovider.ErrNotSupported 593 } 594 595 func (self *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, error) { 596 caches, err := self.GetElasticCaches() 597 if err != nil { 598 return nil, err 599 } 600 601 ret := []cloudprovider.ICloudElasticcache{} 602 for i := range caches { 603 caches[i].region = self 604 ret = append(ret, &caches[i]) 605 } 606 607 return ret, nil 608 }