yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/region.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 "context" 19 "fmt" 20 "strconv" 21 "strings" 22 "time" 23 24 sdkerrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" 25 "github.com/tencentyun/cos-go-sdk-v5" 26 27 "yunion.io/x/jsonutils" 28 "yunion.io/x/log" 29 "yunion.io/x/pkg/errors" 30 "yunion.io/x/pkg/utils" 31 32 api "yunion.io/x/cloudmux/pkg/apis/compute" 33 "yunion.io/x/cloudmux/pkg/cloudprovider" 34 "yunion.io/x/cloudmux/pkg/multicloud" 35 ) 36 37 type SRegion struct { 38 multicloud.SRegion 39 40 client *SQcloudClient 41 42 izones []cloudprovider.ICloudZone 43 ivpcs []cloudprovider.ICloudVpc 44 45 storageCache *SStoragecache 46 47 instanceTypes []SInstanceType 48 49 Region string 50 RegionName string 51 RegionState string 52 53 Latitude float64 54 Longitude float64 55 fetchLocation bool 56 } 57 58 func (self *SRegion) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { 59 return nil, cloudprovider.ErrNotImplemented 60 } 61 62 func (self *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) { 63 return nil, cloudprovider.ErrNotImplemented 64 } 65 66 func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) { 67 lbs, err := self.GetLoadbalancers(nil) 68 if err != nil { 69 return nil, err 70 } 71 72 ilbs := make([]cloudprovider.ICloudLoadbalancer, len(lbs)) 73 for i := range lbs { 74 lbs[i].region = self 75 ilbs[i] = &lbs[i] 76 } 77 78 return ilbs, nil 79 } 80 81 // 腾讯云不支持acl 82 func (self *SRegion) GetILoadBalancerAcls() ([]cloudprovider.ICloudLoadbalancerAcl, error) { 83 return []cloudprovider.ICloudLoadbalancerAcl{}, nil 84 } 85 86 func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) { 87 certs, err := self.GetCertificates("", "", "") 88 if err != nil { 89 return nil, errors.Wrap(err, "GetCertificates") 90 } 91 92 icerts := make([]cloudprovider.ICloudLoadbalancerCertificate, len(certs)) 93 for i := range certs { 94 icerts[i] = &certs[i] 95 } 96 97 return icerts, nil 98 } 99 100 func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) { 101 cert, err := self.GetCertificate(certId) 102 if err != nil { 103 return nil, errors.Wrap(err, "GetCertificate") 104 } 105 106 return cert, nil 107 } 108 109 func (self *SRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) { 110 lbs, err := self.GetLoadbalancers([]string{loadbalancerId}) 111 if err != nil { 112 return nil, err 113 } 114 115 if len(lbs) == 1 { 116 lbs[0].region = self 117 return &lbs[0], nil 118 } else if len(lbs) == 0 { 119 return nil, cloudprovider.ErrNotFound 120 } else { 121 log.Debugf("GetILoadBalancerById %s %d loadbalancer found", loadbalancerId, len(lbs)) 122 return nil, cloudprovider.ErrNotFound 123 } 124 } 125 126 func (self *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloudLoadbalancerAcl, error) { 127 return nil, nil 128 } 129 130 // https://cloud.tencent.com/document/api/214/30692 131 // todo: 1. 支持跨地域绑定负载均衡 及 https://cloud.tencent.com/document/product/214/12014 132 // todo: 2. 支持指定Project。 ProjectId可以通过 DescribeProject 接口获取。不填则属于默认项目。 133 func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) { 134 params := map[string]string{ 135 "LoadBalancerName": loadbalancer.Name, 136 "VpcId": loadbalancer.VpcID, 137 } 138 139 LoadBalancerType := "INTERNAL" 140 if loadbalancer.AddressType == api.LB_ADDR_TYPE_INTERNET { 141 LoadBalancerType = "OPEN" 142 switch loadbalancer.ChargeType { 143 case api.LB_CHARGE_TYPE_BY_BANDWIDTH: 144 pkgs, _, err := self.GetBandwidthPackages([]string{}, 0, 50) 145 if err != nil { 146 return nil, errors.Wrapf(err, "GetBandwidthPackages") 147 } 148 bps := loadbalancer.EgressMbps 149 if bps == 0 { 150 bps = 200 151 } 152 if len(pkgs) > 0 { 153 pkgId := pkgs[0].BandwidthPackageId 154 for _, pkg := range pkgs { 155 if len(pkg.ResourceSet) < 100 { 156 pkgId = pkg.BandwidthPackageId 157 break 158 } 159 } 160 params["BandwidthPackageId"] = pkgId 161 params["InternetAccessible.InternetChargeType"] = "BANDWIDTH_PACKAGE" 162 params["InternetAccessible.InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bps) 163 } else { 164 params["InternetAccessible.InternetChargeType"] = "BANDWIDTH_POSTPAID_BY_HOUR" 165 params["InternetAccessible.InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bps) 166 } 167 default: 168 bps := loadbalancer.EgressMbps 169 if bps == 0 { 170 bps = 200 171 } 172 params["InternetAccessible.InternetChargeType"] = "TRAFFIC_POSTPAID_BY_HOUR" 173 params["InternetAccessible.InternetMaxBandwidthOut"] = fmt.Sprintf("%d", bps) 174 175 } 176 } 177 178 params["LoadBalancerType"] = LoadBalancerType 179 180 if len(loadbalancer.ProjectId) > 0 { 181 params["ProjectId"] = loadbalancer.ProjectId 182 } 183 184 if loadbalancer.AddressType != api.LB_ADDR_TYPE_INTERNET { 185 params["SubnetId"] = loadbalancer.NetworkIDs[0] 186 } else { 187 // 公网类型ELB可支持多可用区 188 if len(loadbalancer.ZoneID) > 0 { 189 if len(loadbalancer.SlaveZoneID) > 0 { 190 params["MasterZoneId"] = loadbalancer.ZoneID 191 } else { 192 params["ZoneId"] = loadbalancer.ZoneID 193 } 194 } 195 } 196 i := 0 197 for k, v := range loadbalancer.Tags { 198 params[fmt.Sprintf("Tags.%d.TagKey", i)] = k 199 params[fmt.Sprintf("Tags.%d.TagValue", i)] = v 200 i++ 201 } 202 203 resp, err := func() (jsonutils.JSONObject, error) { 204 _resp, err := self.clbRequest("CreateLoadBalancer", params) 205 if err != nil { 206 // 兼容不支持指定zone的账号 207 if e, ok := err.(*sdkerrors.TencentCloudSDKError); ok && e.Code == "InvalidParameterValue" { 208 delete(params, "ZoneId") 209 delete(params, "MasterZoneId") 210 return self.clbRequest("CreateLoadBalancer", params) 211 } 212 } 213 return _resp, err 214 }() 215 if err != nil { 216 return nil, err 217 } 218 219 requestId, err := resp.GetString("RequestId") 220 if err != nil { 221 return nil, err 222 } 223 224 lbs, err := resp.GetArray("LoadBalancerIds") 225 if err != nil || len(lbs) != 1 { 226 log.Debugf("CreateILoadBalancer %s", resp.String()) 227 return nil, err 228 } 229 230 err = self.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) 231 if err != nil { 232 return nil, err 233 } 234 235 lbId, err := lbs[0].GetString() 236 if err != nil { 237 return nil, err 238 } 239 240 return self.GetLoadbalancer(lbId) 241 } 242 243 func (self *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) { 244 return nil, cloudprovider.ErrNotSupported 245 } 246 247 // todo:目前onecloud端只能指定服务器端证书。需要兼容客户端证书? 248 // todo:支持指定Project。 249 // todo: 已过期的证书不能上传也不能关联资源 250 func (self *SRegion) CreateILoadBalancerCertificate(input *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) { 251 certId, err := self.CreateCertificate("", input.Certificate, input.PrivateKey, "SVR", input.Name) 252 if err != nil { 253 return nil, err 254 } 255 256 cert, err := self.GetCertificate(certId) 257 if err != nil { 258 log.Debugf("GetCertificate %s failed", certId) 259 return nil, err 260 } 261 262 return cert, nil 263 } 264 265 func (self *SRegion) GetId() string { 266 return self.Region 267 } 268 269 func (self *SRegion) GetName() string { 270 return self.RegionName 271 } 272 273 func (self *SRegion) GetI18n() cloudprovider.SModelI18nTable { 274 en := self.RegionName 275 table := cloudprovider.SModelI18nTable{} 276 table["name"] = cloudprovider.NewSModelI18nEntry(self.GetName()).CN(self.GetName()).EN(en) 277 return table 278 } 279 280 func (self *SRegion) GetGlobalId() string { 281 return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_QCLOUD, self.Region) 282 } 283 284 func (self *SRegion) IsEmulated() bool { 285 return false 286 } 287 288 func (self *SRegion) GetProvider() string { 289 return CLOUD_PROVIDER_QCLOUD 290 } 291 292 func (self *SRegion) GetCloudEnv() string { 293 return "" 294 } 295 296 func (self *SRegion) CreateIVpc(opts *cloudprovider.VpcCreateOptions) (cloudprovider.ICloudVpc, error) { 297 params := make(map[string]string) 298 if len(opts.CIDR) > 0 { 299 params["CidrBlock"] = opts.CIDR 300 } 301 if len(opts.NAME) > 0 { 302 params["VpcName"] = opts.NAME 303 } 304 body, err := self.vpcRequest("CreateVpc", params) 305 if err != nil { 306 return nil, err 307 } 308 vpcId, err := body.GetString("Vpc", "VpcId") 309 if err != nil { 310 return nil, err 311 } 312 err = self.fetchInfrastructure() 313 if err != nil { 314 return nil, err 315 } 316 return self.GetIVpcById(vpcId) 317 } 318 319 func (self *SRegion) GetCosClient(bucket *SBucket) (*cos.Client, error) { 320 return self.client.getCosClient(bucket) 321 } 322 323 func (self *SRegion) GetClient() *SQcloudClient { 324 return self.client 325 } 326 327 func (self *SRegion) GetIVMById(id string) (cloudprovider.ICloudVM, error) { 328 return self.GetInstance(id) 329 } 330 331 func (self *SRegion) GetIDiskById(id string) (cloudprovider.ICloudDisk, error) { 332 return self.GetDisk(id) 333 } 334 335 func (self *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) { 336 if len(eipId) == 0 { 337 return nil, cloudprovider.ErrNotFound 338 } 339 eips, total, err := self.GetEips(eipId, "", 0, 1) 340 if err != nil { 341 return nil, err 342 } 343 if total == 0 { 344 return nil, cloudprovider.ErrNotFound 345 } 346 if total > 1 { 347 return nil, cloudprovider.ErrDuplicateId 348 } 349 return &eips[0], nil 350 } 351 352 func (self *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { 353 eips, total, err := self.GetEips("", "", 0, 50) 354 if err != nil { 355 return nil, err 356 } 357 for len(eips) < total { 358 var parts []SEipAddress 359 parts, total, err = self.GetEips("", "", len(eips), 50) 360 if err != nil { 361 return nil, err 362 } 363 eips = append(eips, parts...) 364 } 365 ret := make([]cloudprovider.ICloudEIP, len(eips)) 366 for i := 0; i < len(eips); i++ { 367 ret[i] = &eips[i] 368 } 369 return ret, nil 370 } 371 372 func (self *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { 373 izones, err := self.GetIZones() 374 if err != nil { 375 return nil, err 376 } 377 for i := 0; i < len(izones); i += 1 { 378 ihost, err := izones[i].GetIHostById(id) 379 if err == nil { 380 return ihost, nil 381 } else if errors.Cause(err) != cloudprovider.ErrNotFound { 382 return nil, err 383 } 384 } 385 return nil, cloudprovider.ErrNotFound 386 } 387 388 func (self *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { 389 izones, err := self.GetIZones() 390 if err != nil { 391 return nil, err 392 } 393 for i := 0; i < len(izones); i += 1 { 394 istore, err := izones[i].GetIStorageById(id) 395 if err == nil { 396 return istore, nil 397 } else if errors.Cause(err) != cloudprovider.ErrNotFound { 398 return nil, err 399 } 400 } 401 return nil, cloudprovider.ErrNotFound 402 } 403 404 func (self *SRegion) GetIHosts() ([]cloudprovider.ICloudHost, error) { 405 iHosts := make([]cloudprovider.ICloudHost, 0) 406 407 izones, err := self.GetIZones() 408 if err != nil { 409 return nil, err 410 } 411 for i := 0; i < len(izones); i += 1 { 412 iZoneHost, err := izones[i].GetIHosts() 413 if err != nil { 414 return nil, err 415 } 416 iHosts = append(iHosts, iZoneHost...) 417 } 418 return iHosts, nil 419 } 420 421 func (self *SRegion) GetIStorages() ([]cloudprovider.ICloudStorage, error) { 422 iStores := make([]cloudprovider.ICloudStorage, 0) 423 424 izones, err := self.GetIZones() 425 if err != nil { 426 return nil, err 427 } 428 for i := 0; i < len(izones); i += 1 { 429 iZoneStores, err := izones[i].GetIStorages() 430 if err != nil { 431 return nil, err 432 } 433 iStores = append(iStores, iZoneStores...) 434 } 435 return iStores, nil 436 } 437 438 func (self *SRegion) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { 439 storageCache := self.getStoragecache() 440 if storageCache.GetGlobalId() == id { 441 return self.storageCache, nil 442 } 443 return nil, cloudprovider.ErrNotFound 444 } 445 446 func (self *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, error) { 447 storageCache := self.getStoragecache() 448 return []cloudprovider.ICloudStoragecache{storageCache}, nil 449 } 450 451 func (self *SRegion) updateInstance(instId string, name, desc, passwd, hostname string) error { 452 params := make(map[string]string) 453 params["InstanceId"] = instId 454 if len(name) > 0 { 455 params["InstanceName"] = name 456 } 457 if len(desc) > 0 { 458 params["Description"] = desc 459 } 460 if len(passwd) > 0 { 461 params["Password"] = passwd 462 } 463 if len(hostname) > 0 { 464 params["HostName"] = hostname 465 } 466 _, err := self.cvmRequest("ModifyInstanceAttribute", params, true) 467 return err 468 } 469 470 func (self *SRegion) UpdateInstancePassword(instId string, passwd string) error { 471 return self.updateInstance(instId, "", "", passwd, "") 472 } 473 474 func (self *SRegion) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) { 475 ivpcs, err := self.GetIVpcs() 476 if err != nil { 477 return nil, err 478 } 479 for i := 0; i < len(ivpcs); i++ { 480 if ivpcs[i].GetGlobalId() == id { 481 return ivpcs[i], nil 482 } 483 } 484 return nil, cloudprovider.ErrNotFound 485 } 486 487 func (self *SRegion) getZoneById(id string) (*SZone, error) { 488 izones, err := self.GetIZones() 489 if err != nil { 490 return nil, err 491 } 492 for i := 0; i < len(izones); i += 1 { 493 zone := izones[i].(*SZone) 494 if zone.Zone == id { 495 return zone, nil 496 } 497 } 498 return nil, fmt.Errorf("no such zone %s", id) 499 } 500 501 func (self *SRegion) GetIVpcs() ([]cloudprovider.ICloudVpc, error) { 502 if self.ivpcs == nil { 503 err := self.fetchInfrastructure() 504 if err != nil { 505 return nil, err 506 } 507 } 508 return self.ivpcs, nil 509 } 510 511 func (self *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) { 512 izones, err := self.GetIZones() 513 if err != nil { 514 return nil, err 515 } 516 for i := 0; i < len(izones); i += 1 { 517 if izones[i].GetGlobalId() == id { 518 return izones[i], nil 519 } 520 } 521 return nil, cloudprovider.ErrNotFound 522 } 523 524 func (self *SRegion) GetIZones() ([]cloudprovider.ICloudZone, error) { 525 if self.izones == nil { 526 var err error 527 err = self.fetchInfrastructure() 528 if err != nil { 529 return nil, err 530 } 531 } 532 return self.izones, nil 533 } 534 535 func (self *SRegion) _fetchZones() error { 536 params := make(map[string]string) 537 zones := make([]SZone, 0) 538 body, err := self.cvmRequest("DescribeZones", params, true) 539 if err != nil { 540 return err 541 } 542 err = body.Unmarshal(&zones, "ZoneSet") 543 if err != nil { 544 return err 545 } 546 self.izones = make([]cloudprovider.ICloudZone, len(zones)) 547 for i := 0; i < len(zones); i++ { 548 zones[i].region = self 549 self.izones[i] = &zones[i] 550 } 551 return nil 552 } 553 554 func (self *SRegion) fetchInfrastructure() error { 555 err := self._fetchZones() 556 if err != nil { 557 return err 558 } 559 err = self.fetchIVpcs() 560 if err != nil { 561 return err 562 } 563 for i := 0; i < len(self.ivpcs); i += 1 { 564 for j := 0; j < len(self.izones); j += 1 { 565 zone := self.izones[j].(*SZone) 566 vpc := self.ivpcs[i].(*SVpc) 567 wire := SWire{zone: zone, vpc: vpc} 568 zone.addWire(&wire) 569 vpc.addWire(&wire) 570 } 571 } 572 return nil 573 } 574 575 func (self *SRegion) DeleteVpc(vpcId string) error { 576 params := make(map[string]string) 577 params["VpcId"] = vpcId 578 579 _, err := self.vpcRequest("DeleteVpc", params) 580 return err 581 } 582 583 func (self *SRegion) getVpc(vpcId string) (*SVpc, error) { 584 vpcs, total, err := self.GetVpcs([]string{vpcId}, 0, 1) 585 if err != nil { 586 return nil, err 587 } 588 if total > 1 { 589 return nil, cloudprovider.ErrDuplicateId 590 } 591 if total == 0 { 592 return nil, cloudprovider.ErrNotFound 593 } 594 vpcs[0].region = self 595 return &vpcs[0], nil 596 } 597 598 func (self *SRegion) fetchIVpcs() error { 599 vpcs := make([]SVpc, 0) 600 for { 601 part, total, err := self.GetVpcs(nil, len(vpcs), 50) 602 if err != nil { 603 return err 604 } 605 vpcs = append(vpcs, part...) 606 if len(vpcs) >= total { 607 break 608 } 609 } 610 self.ivpcs = make([]cloudprovider.ICloudVpc, len(vpcs)) 611 for i := 0; i < len(vpcs); i += 1 { 612 vpcs[i].region = self 613 self.ivpcs[i] = &vpcs[i] 614 } 615 return nil 616 } 617 618 func (self *SRegion) GetVpcs(vpcIds []string, offset int, limit int) ([]SVpc, int, error) { 619 if limit > 50 || limit <= 0 { 620 limit = 50 621 } 622 params := make(map[string]string) 623 params["Limit"] = fmt.Sprintf("%d", limit) 624 params["Offset"] = fmt.Sprintf("%d", offset) 625 if vpcIds != nil && len(vpcIds) > 0 { 626 for index, vpcId := range vpcIds { 627 params[fmt.Sprintf("VpcIds.%d", index)] = vpcId 628 } 629 } 630 body, err := self.vpcRequest("DescribeVpcs", params) 631 if err != nil { 632 return nil, 0, err 633 } 634 vpcs := make([]SVpc, 0) 635 err = body.Unmarshal(&vpcs, "VpcSet") 636 if err != nil { 637 log.Errorf("Unmarshal vpc fail %s", err) 638 return nil, 0, err 639 } 640 total, _ := body.Float("TotalCount") 641 return vpcs, int(total), nil 642 } 643 644 func (self *SRegion) GetGeographicInfo() cloudprovider.SGeographicInfo { 645 if info, ok := LatitudeAndLongitude[self.Region]; ok { 646 return info 647 } 648 return cloudprovider.SGeographicInfo{} 649 } 650 651 func (self *SRegion) GetStatus() string { 652 if self.RegionState == "AVAILABLE" { 653 return api.CLOUD_REGION_STATUS_INSERVER 654 } 655 return api.CLOUD_REGION_STATUS_OUTOFSERVICE 656 } 657 658 func (self *SRegion) Refresh() error { 659 // do nothing 660 return nil 661 } 662 663 // 容器 664 func (self *SRegion) tkeRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 665 params["Region"] = self.Region 666 return self.client.tkeRequest(apiName, params) 667 } 668 669 func (self *SRegion) vpcRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 670 params["Region"] = self.Region 671 return self.client.vpcRequest(apiName, params) 672 } 673 674 func (self *SRegion) auditRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 675 params["Region"] = self.Region 676 return self.client.auditRequest(apiName, params) 677 } 678 679 func (self *SRegion) vpc2017Request(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 680 params["Region"] = self.Region 681 return self.client.vpc2017Request(apiName, params) 682 } 683 684 func (self *SRegion) cvmRequest(apiName string, params map[string]string, retry bool) (jsonutils.JSONObject, error) { 685 params["Region"] = self.Region 686 return self.client.jsonRequest(apiName, params, retry) 687 } 688 689 func (self *SRegion) accountRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 690 return self.client.accountRequestRequest(apiName, params) 691 } 692 693 func (self *SRegion) cbsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 694 params["Region"] = self.Region 695 return self.client.cbsRequest(apiName, params) 696 } 697 698 func (self *SRegion) clbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 699 params["Region"] = self.Region 700 return self.client.clbRequest(apiName, params) 701 } 702 703 func (self *SRegion) lbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 704 params["Region"] = self.Region 705 return self.client.lbRequest(apiName, params) 706 } 707 708 func (self *SRegion) cdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 709 params["Region"] = self.Region 710 return self.client.cdbRequest(apiName, params) 711 } 712 713 func (self *SRegion) mariadbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 714 params["Region"] = self.Region 715 return self.client.mariadbRequest(apiName, params) 716 } 717 718 func (self *SRegion) postgresRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 719 params["Region"] = self.Region 720 return self.client.postgresRequest(apiName, params) 721 } 722 723 func (self *SRegion) sqlserverRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 724 params["Region"] = self.Region 725 return self.client.sqlserverRequest(apiName, params) 726 } 727 728 // deprecated 729 func (self *SRegion) wssRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 730 return self.client.wssRequest(apiName, params) 731 } 732 733 func (self *SRegion) sslRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 734 return self.client.sslRequest(apiName, params) 735 } 736 737 func (self *SRegion) kafkaRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 738 params["Region"] = self.Region 739 return self.client.kafkaRequest(apiName, params) 740 } 741 742 func (self *SRegion) redisRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 743 params["Region"] = self.Region 744 return self.client.redisRequest(apiName, params) 745 } 746 747 func (self *SRegion) dcdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 748 params["Region"] = self.Region 749 return self.client.dcdbRequest(apiName, params) 750 } 751 752 func (self *SRegion) mongodbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 753 params["Region"] = self.Region 754 return self.client.mongodbRequest(apiName, params) 755 } 756 757 // Elasticsearch 758 func (self *SRegion) esRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 759 params["Region"] = self.Region 760 return self.client.esRequest(apiName, params) 761 } 762 763 func (self *SRegion) memcachedRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { 764 params["Region"] = self.Region 765 return self.client.memcachedRequest(apiName, params) 766 } 767 768 func (self *SRegion) GetNetworks(ids []string, vpcId string, offset int, limit int) ([]SNetwork, int, error) { 769 if limit > 50 || limit <= 0 { 770 limit = 50 771 } 772 params := make(map[string]string) 773 params["Limit"] = fmt.Sprintf("%d", limit) 774 params["Offset"] = fmt.Sprintf("%d", offset) 775 base := 0 776 if ids != nil && len(ids) > 0 { 777 for index, networkId := range ids { 778 params[fmt.Sprintf("SubnetIds.%d", index)] = networkId 779 } 780 base += len(ids) 781 } 782 if len(vpcId) > 0 { 783 params["Filters.0.Name"] = "vpc-id" 784 params["Filters.0.Values.0"] = vpcId 785 } 786 787 body, err := self.vpcRequest("DescribeSubnets", params) 788 if err != nil { 789 log.Errorf("DescribeSubnets fail %s", err) 790 return nil, 0, err 791 } 792 793 networks := make([]SNetwork, 0) 794 err = body.Unmarshal(&networks, "SubnetSet") 795 if err != nil { 796 log.Errorf("Unmarshal network fail %s", err) 797 return nil, 0, err 798 } 799 total, _ := body.Float("TotalCount") 800 return networks, int(total), nil 801 } 802 803 func (self *SRegion) GetNetwork(networkId string) (*SNetwork, error) { 804 networks, total, err := self.GetNetworks([]string{networkId}, "", 0, 1) 805 if err != nil { 806 return nil, err 807 } 808 if total > 1 { 809 return nil, cloudprovider.ErrDuplicateId 810 } 811 if total == 0 { 812 return nil, cloudprovider.ErrNotFound 813 } 814 return &networks[0], nil 815 } 816 817 func (self *SRegion) getStoragecache() *SStoragecache { 818 if self.storageCache == nil { 819 self.storageCache = &SStoragecache{region: self} 820 } 821 return self.storageCache 822 } 823 824 func (self *SRegion) GetMatchInstanceTypes(cpu int, memMB int, gpu int, zoneId string) ([]SInstanceType, error) { 825 if self.instanceTypes == nil { 826 types, err := self.GetInstanceTypes() 827 if err != nil { 828 log.Errorf("GetInstanceTypes %s", err) 829 return nil, err 830 } 831 self.instanceTypes = types 832 } 833 834 var available []string 835 if len(zoneId) > 0 { 836 zone, err := self.getZoneById(zoneId) 837 if err != nil { 838 return nil, err 839 } 840 available = zone.getAvaliableInstanceTypes() 841 } 842 ret := make([]SInstanceType, 0) 843 for _, t := range self.instanceTypes { 844 if t.CPU == cpu && memMB == t.memoryMB() && gpu == t.GPU { 845 if available == nil || utils.IsInStringArray(t.InstanceType, available) { 846 ret = append(ret, t) 847 } 848 } 849 } 850 return ret, nil 851 } 852 853 func (self *SRegion) CreateInstanceSimple(name string, imgId string, cpu int, memGB int, storageType string, dataDiskSizesGB []int, networkId string, passwd string, publicKey string, secgroup string, tags map[string]string) (*SInstance, error) { 854 izones, err := self.GetIZones() 855 if err != nil { 856 return nil, err 857 } 858 for i := 0; i < len(izones); i += 1 { 859 z := izones[i].(*SZone) 860 log.Debugf("Search in zone %s", z.Zone) 861 net := z.getNetworkById(networkId) 862 if net != nil { 863 desc := &cloudprovider.SManagedVMCreateConfig{ 864 Name: name, 865 ExternalImageId: imgId, 866 SysDisk: cloudprovider.SDiskInfo{SizeGB: 0, StorageType: storageType}, 867 Cpu: cpu, 868 MemoryMB: memGB * 1024, 869 ExternalNetworkId: networkId, 870 Password: passwd, 871 DataDisks: []cloudprovider.SDiskInfo{}, 872 PublicKey: publicKey, 873 874 Tags: tags, 875 876 ExternalSecgroupId: secgroup, 877 } 878 for _, sizeGB := range dataDiskSizesGB { 879 desc.DataDisks = append(desc.DataDisks, cloudprovider.SDiskInfo{SizeGB: sizeGB, StorageType: storageType}) 880 } 881 inst, err := z.getHost().CreateVM(desc) 882 if err != nil { 883 return nil, err 884 } 885 return inst.(*SInstance), nil 886 } 887 } 888 return nil, fmt.Errorf("cannot find network %s", networkId) 889 } 890 891 func (self *SRegion) instanceOperation(instanceId string, opname string, extra map[string]string, retry bool) error { 892 params := make(map[string]string) 893 params["InstanceIds.0"] = instanceId 894 if extra != nil && len(extra) > 0 { 895 for k, v := range extra { 896 params[k] = v 897 } 898 } 899 _, err := self.cvmRequest(opname, params, retry) 900 return err 901 } 902 903 func (self *SRegion) GetInstanceVNCUrl(instanceId string) (string, error) { 904 params := make(map[string]string) 905 params["InstanceId"] = instanceId 906 body, err := self.cvmRequest("DescribeInstanceVncUrl", params, true) 907 if err != nil { 908 return "", err 909 } 910 return body.GetString("InstanceVncUrl") 911 } 912 913 func (self *SRegion) GetInstanceStatus(instanceId string) (string, error) { 914 instance, err := self.GetInstance(instanceId) 915 if err != nil { 916 return "", err 917 } 918 return instance.InstanceState, nil 919 } 920 921 func (self *SRegion) QueryAccountBalance() (*SAccountBalance, error) { 922 return self.client.QueryAccountBalance() 923 } 924 925 func (self *SRegion) getCosEndpoint() string { 926 return fmt.Sprintf("cos.%s.myqcloud.com", self.GetId()) 927 } 928 929 func (self *SRegion) getCosWebsiteEndpoint() string { 930 return fmt.Sprintf("cos-website.%s.myqcloud.com", self.GetId()) 931 } 932 933 func (region *SRegion) GetIBuckets() ([]cloudprovider.ICloudBucket, error) { 934 iBuckets, err := region.client.getIBuckets() 935 if err != nil { 936 return nil, err 937 } 938 ret := make([]cloudprovider.ICloudBucket, 0) 939 for i := range iBuckets { 940 bucket := iBuckets[i].(*SBucket) 941 if bucket.region.GetId() != region.GetId() { 942 continue 943 } 944 ret = append(ret, iBuckets[i]) 945 } 946 return ret, nil 947 } 948 949 func (region *SRegion) CreateIBucket(name string, storageClassStr string, aclStr string) error { 950 bucket := &SBucket{ 951 region: region, 952 Name: name, 953 } 954 coscli, err := region.GetCosClient(bucket) 955 if err != nil { 956 return errors.Wrap(err, "GetCosClient") 957 } 958 opts := &cos.BucketPutOptions{} 959 if len(aclStr) > 0 { 960 if utils.IsInStringArray(aclStr, []string{ 961 "private", "public-read", "public-read-write", "authenticated-read", 962 }) { 963 opts.XCosACL = aclStr 964 } else { 965 return errors.Error("invalid acl") 966 } 967 } 968 _, err = coscli.Bucket.Put(context.Background(), opts) 969 if err != nil { 970 return errors.Wrap(err, "coscli.Bucket.Put") 971 } 972 region.client.invalidateIBuckets() 973 return nil 974 } 975 976 func cosHttpCode(err error) int { 977 if httpErr, ok := err.(*cos.ErrorResponse); ok { 978 return httpErr.Response.StatusCode 979 } 980 return -1 981 } 982 983 func (region *SRegion) DeleteIBucket(name string) error { 984 bucket := &SBucket{ 985 region: region, 986 Name: name, 987 } 988 coscli, err := region.GetCosClient(bucket) 989 if err != nil { 990 return errors.Wrap(err, "GetCosClient") 991 } 992 _, err = coscli.Bucket.Delete(context.Background()) 993 if err != nil { 994 if cosHttpCode(err) == 404 { 995 return nil 996 } 997 return errors.Wrap(err, "DeleteBucket") 998 } 999 return nil 1000 } 1001 1002 func (region *SRegion) IBucketExist(name string) (bool, error) { 1003 bucket := &SBucket{ 1004 region: region, 1005 Name: name, 1006 } 1007 coscli, err := region.GetCosClient(bucket) 1008 if err != nil { 1009 return false, errors.Wrap(err, "GetCosClient") 1010 } 1011 _, err = coscli.Bucket.Head(context.Background()) 1012 if err != nil { 1013 if cosHttpCode(err) == 404 { 1014 return false, nil 1015 } 1016 return false, errors.Wrap(err, "BucketExists") 1017 } 1018 return true, nil 1019 } 1020 1021 func (region *SRegion) GetIBucketById(name string) (cloudprovider.ICloudBucket, error) { 1022 return cloudprovider.GetIBucketById(region, name) 1023 } 1024 1025 func (region *SRegion) GetIBucketByName(name string) (cloudprovider.ICloudBucket, error) { 1026 return region.GetIBucketById(name) 1027 } 1028 1029 func (self *SRegion) GetISecurityGroupById(secgroupId string) (cloudprovider.ICloudSecurityGroup, error) { 1030 secgroups, total, err := self.GetSecurityGroups([]string{secgroupId}, "", "", 0, 1) 1031 if err != nil { 1032 return nil, errors.Wrapf(err, "GetSecurityGroups(%s)", secgroupId) 1033 } 1034 if total < 1 { 1035 return nil, cloudprovider.ErrNotFound 1036 } 1037 secgroups[0].region = self 1038 return &secgroups[0], nil 1039 } 1040 1041 func (self *SRegion) GetISecurityGroupByName(opts *cloudprovider.SecurityGroupFilterOptions) (cloudprovider.ICloudSecurityGroup, error) { 1042 secgroups, total, err := self.GetSecurityGroups([]string{}, opts.VpcId, opts.Name, 0, 0) 1043 if err != nil { 1044 return nil, err 1045 } 1046 if total == 0 { 1047 return nil, cloudprovider.ErrNotFound 1048 } 1049 if total > 1 { 1050 return nil, cloudprovider.ErrDuplicateId 1051 } 1052 return &secgroups[0], nil 1053 } 1054 1055 func (self *SRegion) CreateISecurityGroup(conf *cloudprovider.SecurityGroupCreateInput) (cloudprovider.ICloudSecurityGroup, error) { 1056 return self.CreateSecurityGroup(conf.Name, conf.ProjectId, conf.Desc) 1057 } 1058 1059 func (region *SRegion) GetCapabilities() []string { 1060 return region.client.GetCapabilities() 1061 } 1062 1063 func (region *SRegion) GetIElasticcaches() ([]cloudprovider.ICloudElasticcache, error) { 1064 caches, err := region.GetCloudElasticcaches("") 1065 if err != nil { 1066 return nil, errors.Wrap(err, "GetCloudElasticcaches") 1067 } 1068 1069 ret := []cloudprovider.ICloudElasticcache{} 1070 for i := range caches { 1071 cache := caches[i] 1072 cache.region = region 1073 ret = append(ret, &cache) 1074 } 1075 1076 mems := []SMemcached{} 1077 offset := 0 1078 for { 1079 part, total, err := region.GetMemcaches(nil, 100, offset) 1080 if err != nil { 1081 return nil, errors.Wrapf(err, "GetMemcaches") 1082 } 1083 mems = append(mems, part...) 1084 if len(mems) >= total { 1085 break 1086 } 1087 offset += len(part) 1088 } 1089 for i := range mems { 1090 mems[i].region = region 1091 ret = append(ret, &mems[i]) 1092 } 1093 1094 return ret, nil 1095 } 1096 1097 func (region *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) { 1098 if strings.HasPrefix(id, "cmem-") { 1099 memcacheds, _, err := region.GetMemcaches([]string{id}, 1, 0) 1100 if err != nil { 1101 return nil, errors.Wrapf(err, "GetMemcaches") 1102 } 1103 for i := range memcacheds { 1104 if memcacheds[i].GetGlobalId() == id { 1105 memcacheds[i].region = region 1106 return &memcacheds[i], nil 1107 } 1108 } 1109 return nil, errors.Wrapf(cloudprovider.ErrNotFound, id) 1110 } 1111 caches, err := region.GetCloudElasticcaches(id) 1112 if err != nil { 1113 return nil, errors.Wrap(err, "GetCloudElasticcaches") 1114 } 1115 1116 for i := range caches { 1117 if caches[i].GetGlobalId() == id { 1118 caches[i].region = region 1119 return &caches[i], nil 1120 } 1121 } 1122 1123 return nil, cloudprovider.ErrNotFound 1124 } 1125 1126 // DescribeProductInfo 可以查询在售可用区信息 1127 // https://cloud.tencent.com/document/product/239/20026 1128 func (r *SRegion) CreateIElasticcaches(ec *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) { 1129 params := map[string]string{} 1130 if len(ec.ZoneIds) == 0 { 1131 return nil, fmt.Errorf("CreateIElasticcaches zone id should not be empty.") 1132 } 1133 1134 zoneId, ok := zoneIdMaps[ec.ZoneIds[0]] 1135 if !ok { 1136 return nil, fmt.Errorf("can't convert zone %s to integer id", ec.ZoneIds[0]) 1137 } 1138 1139 if len(ec.ZoneIds) > 1 { 1140 for i := range ec.ZoneIds { 1141 if i == 0 { 1142 params[fmt.Sprintf("NodeSet.%d.NodeType", i)] = "0" 1143 params[fmt.Sprintf("NodeSet.%d.ZoneId", i)] = fmt.Sprintf("%d", zoneId) 1144 } else { 1145 _z, ok := zoneIdMaps[ec.ZoneIds[i]] 1146 if !ok { 1147 return nil, fmt.Errorf("can't convert zone %s to integer id", ec.ZoneIds[i]) 1148 } 1149 params[fmt.Sprintf("NodeSet.%d.NodeType", i)] = "1" 1150 params[fmt.Sprintf("NodeSet.%d.ZoneId", i)] = fmt.Sprintf("%d", _z) 1151 } 1152 } 1153 } 1154 1155 spec, err := parseLocalInstanceSpec(ec.InstanceType) 1156 if err != nil { 1157 return nil, errors.Wrap(err, "parseLocalInstanceSpec") 1158 } 1159 1160 params["InstanceName"] = ec.InstanceName 1161 if len(ec.ProjectId) > 0 { 1162 params["ProjectId"] = ec.ProjectId 1163 } 1164 params["ZoneId"] = fmt.Sprintf("%d", zoneId) 1165 params["TypeId"] = spec.TypeId 1166 params["MemSize"] = strconv.Itoa(spec.MemSizeMB) 1167 params["RedisShardNum"] = spec.RedisShardNum 1168 params["RedisReplicasNum"] = spec.RedisReplicasNum 1169 params["GoodsNum"] = "1" 1170 if ec.NetworkType == api.LB_NETWORK_TYPE_VPC { 1171 params["VpcId"] = ec.VpcId 1172 params["SubnetId"] = ec.NetworkId 1173 1174 for i := range ec.SecurityGroupIds { 1175 params[fmt.Sprintf("SecurityGroupIdList.%d", i)] = ec.SecurityGroupIds[i] 1176 } 1177 } 1178 params["Period"] = "1" 1179 params["BillingMode"] = "0" 1180 if ec.BillingCycle != nil && ec.BillingCycle.GetMonths() >= 1 { 1181 params["Period"] = strconv.Itoa(ec.BillingCycle.GetMonths()) 1182 params["BillingMode"] = "1" 1183 // 自动续费 1184 if ec.BillingCycle.AutoRenew { 1185 params["AutoRenew"] = "1" 1186 } 1187 } 1188 1189 if len(ec.Password) > 0 { 1190 params["NoAuth"] = "false" 1191 params["Password"] = ec.Password 1192 } else { 1193 params["NoAuth"] = "true" 1194 } 1195 1196 resp, err := r.redisRequest("CreateInstances", params) 1197 if err != nil { 1198 return nil, errors.Wrap(err, "CreateInstances") 1199 } 1200 1201 instanceId := "" 1202 if resp.Contains("InstanceIds") { 1203 ids := []string{} 1204 if err := resp.Unmarshal(&ids, "InstanceIds"); err != nil { 1205 log.Debugf("Unmarshal.InstanceIds %s", resp) 1206 } else { 1207 if len(ids) > 0 { 1208 instanceId = ids[0] 1209 } 1210 } 1211 } 1212 1213 // try to fetch instance id from deal id 1214 if len(instanceId) == 0 { 1215 dealId, err := resp.GetString("DealId") 1216 if err != nil { 1217 return nil, errors.Wrap(err, "dealId") 1218 } 1219 1220 // maybe is a dealId not a instance ID. 1221 if strings.HasPrefix(dealId, "crs-") { 1222 instanceId = dealId 1223 } else { 1224 err = cloudprovider.Wait(5*time.Second, 900*time.Second, func() (bool, error) { 1225 _realInstanceId, err := r.GetElasticcacheIdByDeal(dealId) 1226 if err != nil { 1227 return false, nil 1228 } 1229 1230 instanceId = _realInstanceId 1231 return true, nil 1232 }) 1233 if err != nil { 1234 return nil, errors.Wrap(err, "Wait.GetElasticcacheIdByDeal") 1235 } 1236 } 1237 } 1238 1239 err = r.SetResourceTags("redis", "instance", []string{instanceId}, ec.Tags, false) 1240 if err != nil { 1241 log.Errorf("SetResourceTags(redis:%s,error:%s)", instanceId, err) 1242 } 1243 return r.GetIElasticcacheById(instanceId) 1244 }