yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/hcs/hcs.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 "context" 19 "fmt" 20 "net/http" 21 "net/url" 22 "strconv" 23 "strings" 24 "time" 25 26 "github.com/huaweicloud/huaweicloud-sdk-go-obs/obs" 27 28 "yunion.io/x/jsonutils" 29 "yunion.io/x/log" 30 "yunion.io/x/pkg/errors" 31 "yunion.io/x/pkg/gotypes" 32 "yunion.io/x/pkg/utils" 33 34 api "yunion.io/x/cloudmux/pkg/apis/compute" 35 "yunion.io/x/cloudmux/pkg/cloudprovider" 36 "yunion.io/x/cloudmux/pkg/multicloud/hcso/client/auth" 37 "yunion.io/x/onecloud/pkg/util/httputils" 38 ) 39 40 const ( 41 VERSION_SPEC = "x-version" 42 43 CLOUD_PROVIDER_HCS = api.CLOUD_PROVIDER_HCS 44 CLOUD_PROVIDER_HCS_CN = "华为云Stack" 45 CLOUD_PROVIDER_HCS_EN = "HCS" 46 47 HCS_API_VERSION = "" 48 ) 49 50 type HcsConfig struct { 51 cpcfg cloudprovider.ProviderConfig 52 authUrl string 53 54 projectId string // 华为云项目ID. 55 accessKey string 56 accessSecret string 57 58 debug bool 59 } 60 61 func NewHcsConfig(accessKey, accessSecret, projectId, url string) *HcsConfig { 62 cfg := &HcsConfig{ 63 projectId: projectId, 64 accessKey: accessKey, 65 accessSecret: accessSecret, 66 authUrl: url, 67 } 68 69 return cfg 70 } 71 72 func (cfg *HcsConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *HcsConfig { 73 cfg.cpcfg = cpcfg 74 return cfg 75 } 76 77 func (cfg *HcsConfig) Debug(debug bool) *HcsConfig { 78 cfg.debug = debug 79 return cfg 80 } 81 82 type SHcsClient struct { 83 *HcsConfig 84 85 signer auth.Signer 86 87 isMainProject bool // whether the project is the main project in the region 88 89 domainId string 90 projectName string 91 92 regions []SRegion 93 projects []SProject 94 buckets []SBucket 95 96 defaultRegion string 97 98 httpClient *http.Client 99 lock sThrottlingThreshold 100 } 101 102 func NewHcsClient(cfg *HcsConfig) (*SHcsClient, error) { 103 client := SHcsClient{ 104 HcsConfig: cfg, 105 lock: sThrottlingThreshold{locked: false, lockTime: time.Time{}}, 106 } 107 if len(client.projectId) > 0 { 108 project, err := client.GetProjectById(client.projectId) 109 if err != nil { 110 return nil, err 111 } 112 client.domainId = project.DomainId 113 client.projectName = project.Name 114 } 115 return &client, client.fetchRegions() 116 } 117 118 func (self *SHcsClient) GetAccountId() string { 119 return self.authUrl 120 } 121 122 func (self *SHcsClient) GetRegion(id string) (*SRegion, error) { 123 for i := range self.regions { 124 if self.regions[i].GetGlobalId() == id || self.regions[i].GetId() == id { 125 self.regions[i].client = self 126 return &self.regions[i], nil 127 } 128 } 129 return nil, cloudprovider.ErrNotFound 130 } 131 132 func (self *SHcsClient) GetCloudRegionExternalIdPrefix() string { 133 if len(self.projectId) > 0 { 134 if iregions := self.GetIRegions(); len(iregions) > 0 { 135 return iregions[0].GetGlobalId() 136 } 137 } 138 return CLOUD_PROVIDER_HCS 139 } 140 141 type akClient struct { 142 client *http.Client 143 signer *Signer 144 } 145 146 func (self *SHcsClient) getDefaultClient() *http.Client { 147 if self.httpClient != nil { 148 return self.httpClient 149 } 150 self.httpClient = self.cpcfg.AdaptiveTimeoutHttpClient() 151 ts, _ := self.httpClient.Transport.(*http.Transport) 152 self.httpClient.Transport = cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response), error) { 153 service, method, path := strings.Split(req.URL.Host, ".")[0], req.Method, req.URL.Path 154 respCheck := func(resp *http.Response) { 155 if resp.StatusCode == 403 { 156 if self.cpcfg.UpdatePermission != nil { 157 self.cpcfg.UpdatePermission(service, fmt.Sprintf("%s %s", method, path)) 158 } 159 } 160 } 161 if self.cpcfg.ReadOnly { 162 if req.Method == "GET" { 163 return respCheck, nil 164 } 165 return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path) 166 } 167 return respCheck, nil 168 }) 169 return self.httpClient 170 } 171 172 func (self *akClient) Do(req *http.Request) (*http.Response, error) { 173 req.Header.Del("Accept") 174 if req.Method == string(httputils.GET) || req.Method == string(httputils.DELETE) || req.Method == string(httputils.PATCH) { 175 req.Header.Del("Content-Length") 176 } 177 err := self.signer.Sign(req) 178 if err != nil { 179 return nil, errors.Wrapf(err, "Sign") 180 } 181 return self.client.Do(req) 182 } 183 184 func (self *SHcsClient) getAkClient() *akClient { 185 return &akClient{ 186 client: self.getDefaultClient(), 187 signer: &Signer{ 188 Key: self.accessKey, 189 Secret: self.accessSecret, 190 }, 191 } 192 } 193 194 type hcsError struct { 195 Url string 196 Params map[string]interface{} 197 Code int `json:"code,omitzero"` 198 Class string `json:"class,omitempty"` 199 ErrorMsg string `json:"error_msg,omitempty"` 200 Details string `json:"details,omitempty"` 201 } 202 203 func (ce *hcsError) Error() string { 204 return jsonutils.Marshal(ce).String() 205 } 206 207 func (ce *hcsError) ParseErrorFromJsonResponse(statusCode int, body jsonutils.JSONObject) error { 208 body.Unmarshal(ce) 209 if ce.Code == 0 { 210 ce.Code = statusCode 211 } 212 if len(ce.Class) == 0 { 213 ce.Class = http.StatusText(statusCode) 214 } 215 if len(ce.Details) == 0 { 216 ce.Details = body.String() 217 } 218 return ce 219 } 220 221 type sThrottlingThreshold struct { 222 locked bool 223 lockTime time.Time 224 } 225 226 func (t *sThrottlingThreshold) CheckingLock() { 227 if !t.locked { 228 return 229 } 230 231 for { 232 if t.lockTime.Sub(time.Now()).Seconds() < 0 { 233 return 234 } 235 log.Debugf("throttling threshold has been reached. release at %s", t.lockTime) 236 time.Sleep(5 * time.Second) 237 } 238 } 239 240 func (t *sThrottlingThreshold) Lock() { 241 // 锁定至少15秒 242 t.locked = true 243 t.lockTime = time.Now().Add(15 * time.Second) 244 } 245 246 func (self *SHcsClient) request(method httputils.THttpMethod, url string, query url.Values, params map[string]interface{}) (jsonutils.JSONObject, error) { 247 self.lock.CheckingLock() 248 client := self.getAkClient() 249 if len(query) > 0 { 250 url = fmt.Sprintf("%s?%s", url, query.Encode()) 251 } 252 url = strings.TrimPrefix(url, "http://") 253 if !strings.HasPrefix(url, "https://") { 254 url = fmt.Sprintf("https://%s", url) 255 } 256 var body jsonutils.JSONObject = nil 257 if len(params) > 0 { 258 body = jsonutils.Marshal(params) 259 } 260 header := http.Header{} 261 if len(self.projectId) > 0 && !strings.Contains(url, "v3/regions") { 262 header.Set("X-Project-Id", self.projectId) 263 } 264 if len(self.domainId) > 0 { 265 //header.Set("X-Domain-Id", self.domainId) 266 } 267 cli := httputils.NewJsonClient(client) 268 req := httputils.NewJsonRequest(method, url, body) 269 req.SetHeader(header) 270 var resp jsonutils.JSONObject 271 var err error 272 for i := 0; i < 4; i++ { 273 _, resp, err = cli.Send(context.Background(), req, &hcsError{Url: url, Params: params}, self.debug) 274 if err == nil { 275 break 276 } 277 if err != nil { 278 e, ok := err.(*hcsError) 279 if ok { 280 if e.Code == 404 { 281 return nil, errors.Wrapf(cloudprovider.ErrNotFound, err.Error()) 282 } 283 if e.Code == 429 { 284 log.Errorf("request %s %v try later", url, err) 285 self.lock.Lock() 286 time.Sleep(time.Second * 15) 287 continue 288 } 289 } 290 return nil, err 291 } 292 } 293 return resp, err 294 } 295 296 func (self *SHcsClient) iamGet(resource string, query url.Values) (jsonutils.JSONObject, error) { 297 url := fmt.Sprintf("iam-apigateway-proxy.%s/%s", self.authUrl, resource) 298 return self.request(httputils.GET, url, query, nil) 299 } 300 301 func (self *SHcsClient) fetchRegions() error { 302 if len(self.regions) > 0 { 303 return nil 304 } 305 resp, err := self.iamGet("v3/regions", nil) 306 if err != nil { 307 return err 308 } 309 self.regions = []SRegion{} 310 err = resp.Unmarshal(&self.regions, "regions") 311 if err != nil { 312 return err 313 } 314 for i := range self.regions { 315 self.defaultRegion = self.regions[i].Id 316 } 317 return nil 318 } 319 320 func (hcscli *SHcsClient) rdsList(regionId string, resource string, query url.Values, retVal interface{}) error { 321 return hcscli.list("rds", "v3", regionId, resource, query, retVal) 322 } 323 324 func (hcscli *SHcsClient) rdsGet(regionId string, resource string, retVal interface{}) error { 325 return hcscli.get("rds", "v3", regionId, resource, retVal) 326 } 327 328 func (hcscli *SHcsClient) rdsDelete(regionId string, resource string) error { 329 return hcscli._delete("rds", "v3", regionId, resource) 330 } 331 332 func (hcscli *SHcsClient) rdsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error { 333 return hcscli._create("rds", "v3", regionId, resource, body, retVal) 334 } 335 336 func (self *SHcsClient) rdsPerform(regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error { 337 return self._perform("rds", "v3", regionId, resource, action, params, retVal) 338 } 339 340 func (hcscli *SHcsClient) rdsJobGet(regionId string, resource string, query url.Values, retVal interface{}) error { 341 url := hcscli._url("rds", "v3", regionId, resource) 342 resp, err := hcscli.request(httputils.GET, url, query, nil) 343 if err != nil { 344 return err 345 } 346 err = resp.Unmarshal(retVal) 347 return err 348 } 349 350 func (hcscli *SHcsClient) rdsDBPrivvilegesDelete(regionId string, resource string, params map[string]interface{}) error { 351 return hcscli._deleteWithBody("rds", "v3", regionId, resource, params) 352 } 353 354 func (hcscli *SHcsClient) rdsDBPrivilegesGrant(regionId string, resource string, params map[string]interface{}, retVal interface{}) error { 355 url := hcscli._url("rds", "v3", regionId, resource) 356 resp, err := hcscli.request(httputils.GET, url, nil, params) 357 if err != nil { 358 return err 359 } 360 err = resp.Unmarshal(retVal) 361 return err 362 } 363 364 func (self *SHcsClient) ecsList(regionId string, resource string, query url.Values, retVal interface{}) error { 365 return self.list("ecs", "v2", regionId, resource, query, retVal) 366 } 367 368 func (self *SHcsClient) list(product, version, regionId string, resource string, query url.Values, retVal interface{}) error { 369 resp, err := self._list(product, version, regionId, resource, query) 370 if err != nil { 371 return errors.Wrapf(err, "_list") 372 } 373 return resp.Unmarshal(retVal) 374 } 375 376 func (self *SHcsClient) ecsGet(regionId string, resource string, retVal interface{}) error { 377 return self.get("ecs", "v2", regionId, resource, retVal) 378 } 379 380 func (self *SHcsClient) ecsDelete(regionId string, resource string) error { 381 return self._delete("ecs", "v2", regionId, resource) 382 } 383 384 func (self *SHcsClient) ecsPerform(regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error { 385 return self._perform("ecs", "v2", regionId, resource, action, params, retVal) 386 } 387 388 func (self *SHcsClient) evsDelete(regionId string, resource string) error { 389 return self._delete("evs", "v2", regionId, resource) 390 } 391 392 func (self *SHcsClient) evsList(regionId string, resource string, query url.Values, retVal interface{}) error { 393 return self.list("evs", "v2", regionId, resource, query, retVal) 394 } 395 396 func (self *SHcsClient) evsGet(regionId string, resource string, retVal interface{}) error { 397 return self.get("evs", "v2", regionId, resource, retVal) 398 } 399 400 func (self *SHcsClient) evsPerform(regionId string, resource, action string, params map[string]interface{}) error { 401 return self._perform("evs", "v2", regionId, resource, action, params, nil) 402 } 403 404 func (self *SHcsClient) ecsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error { 405 return self._create("ecs", "v2", regionId, resource, body, retVal) 406 } 407 408 func (self *SHcsClient) evsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error { 409 return self._create("evs", "v2", regionId, resource, body, retVal) 410 } 411 412 func (self *SHcsClient) vpcCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error { 413 return self._create("vpc", "v1", regionId, resource, body, retVal) 414 } 415 416 func (self *SHcsClient) vpcDelete(regionId string, resource string) error { 417 return self._delete("vpc", "v1", regionId, resource) 418 } 419 420 func (self *SHcsClient) vpcGet(regionId string, resource string, retVal interface{}) error { 421 return self.get("vpc", "v1", regionId, resource, retVal) 422 } 423 424 func (self *SHcsClient) vpcList(regionId string, resource string, query url.Values, retVal interface{}) error { 425 return self.list("vpc", "v1", regionId, resource, query, retVal) 426 } 427 428 func (self *SHcsClient) vpcUpdate(regionId string, resource string, body map[string]interface{}) error { 429 return self._update("vpc", "v1", regionId, resource, body) 430 } 431 432 func (self *SHcsClient) imsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error { 433 return self._create("ims", "v2", regionId, resource, body, retVal) 434 } 435 436 func (self *SHcsClient) imsDelete(regionId string, resource string) error { 437 return self._delete("ims", "v2", regionId, resource) 438 } 439 440 func (self *SHcsClient) imsGet(regionId string, resource string, retVal interface{}) error { 441 return self.get("ims", "v2", regionId, resource, retVal) 442 } 443 444 func (self *SHcsClient) imsList(regionId string, resource string, query url.Values, retVal interface{}) error { 445 return self.list("ims", "v2", regionId, resource, query, retVal) 446 } 447 448 func (self *SHcsClient) imsPerform(regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error { 449 return self._perform("ims", "v2", regionId, resource, action, params, retVal) 450 } 451 452 func (self *SHcsClient) imsUpdate(regionId string, resource string, body map[string]interface{}) error { 453 return self._update("ims", "v2", regionId, resource, body) 454 } 455 456 func (self *SHcsClient) dcsCreate(regionId string, resource string, body map[string]interface{}, retVal interface{}) error { 457 return self._create("dcs", "v1.0", regionId, resource, body, retVal) 458 } 459 460 func (self *SHcsClient) dcsDelete(regionId string, resource string) error { 461 return self._delete("dcs", "v1.0", regionId, resource) 462 } 463 464 func (self *SHcsClient) dcsGet(regionId string, resource string, retVal interface{}) error { 465 return self.get("dcs", "v1.0", regionId, resource, retVal) 466 } 467 468 func (self *SHcsClient) dcsList(regionId string, resource string, query url.Values, retVal interface{}) error { 469 return self.list("dcs", "v1.0", regionId, resource, query, retVal) 470 } 471 472 func (self *SHcsClient) dcsPerform(regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error { 473 return self._perform("dcs", "v1.0", regionId, resource, action, params, retVal) 474 } 475 476 func (self *SHcsClient) dcsUpdate(regionId string, resource string, body map[string]interface{}) error { 477 return self._update("dcs", "v1.0", regionId, resource, body) 478 } 479 480 func (self *SHcsClient) _url(product, version, regionId string, resource string) string { 481 url := fmt.Sprintf("%s.%s.%s/%s/%s/%s", product, regionId, self.authUrl, version, self.projectId, resource) 482 for _, prefix := range []string{ 483 "images", "cloudimages", "nat_gateways", 484 "lbaas", "products", "snat_rules", 485 "dnat_rules", "vpc/peerings", 486 } { 487 if strings.HasPrefix(resource, prefix) { 488 url = fmt.Sprintf("%s.%s.%s/%s/%s", product, regionId, self.authUrl, version, resource) 489 break 490 } 491 } 492 return url 493 } 494 495 func (self *SHcsClient) _list(product, version, regionId string, resource string, query url.Values) (*jsonutils.JSONArray, error) { 496 ret := jsonutils.NewArray() 497 url := self._url(product, version, regionId, resource) 498 offset, _ := strconv.Atoi(query.Get("offset")) 499 total := int64(0) 500 for { 501 resp, err := self.request(httputils.GET, url, query, nil) 502 if err != nil { 503 return nil, err 504 } 505 if gotypes.IsNil(resp) { 506 log.Warningf("%s return empty", resource) 507 return ret, nil 508 } 509 objMap, err := resp.GetMap() 510 if err != nil { 511 return nil, errors.Wrapf(err, "resp.GetMap") 512 } 513 next := false 514 for k, v := range objMap { 515 if k == "links" { 516 url, _ = v.GetString("next") 517 next = true 518 continue 519 } 520 if k == "count" { 521 offset++ 522 query.Set("offset", fmt.Sprintf("%d", offset)) 523 total, _ = v.Int() 524 next = true 525 continue 526 } 527 if strings.Contains(resource, k) || 528 strings.Contains(strings.ReplaceAll(resource, "-", "_"), k) || 529 utils.IsInStringArray(k, []string{ 530 "availabilityZoneInfo", 531 }) { 532 objs, err := v.GetArray() 533 if err != nil { 534 return nil, errors.Wrapf(err, "v.GetArray") 535 } 536 ret.Add(objs...) 537 } 538 } 539 if !next || len(url) == 0 || (ret.Length() == int(total)) { 540 break 541 } 542 } 543 return ret, nil 544 } 545 546 func (self *SHcsClient) get(product, version, regionId string, resource string, retVal interface{}) error { 547 resp, err := self._get(product, version, regionId, resource) 548 if err != nil { 549 return err 550 } 551 obj, err := resp.GetMap() 552 if err != nil { 553 return errors.Wrapf(err, "GetMap") 554 } 555 for v := range obj { 556 if len(obj) == 1 { 557 return obj[v].Unmarshal(retVal) 558 } 559 } 560 return resp.Unmarshal(retVal) 561 } 562 563 func (self *SHcsClient) _getJob(product, regionId string, jobId string) (jsonutils.JSONObject, error) { 564 url := self._url(product, "v1", regionId, fmt.Sprintf("jobs/%s", jobId)) 565 resp, err := self.request(httputils.GET, url, nil, nil) 566 if err != nil { 567 return nil, err 568 } 569 return resp, nil 570 } 571 572 func (self *SHcsClient) _get(product, version, regionId string, resource string) (jsonutils.JSONObject, error) { 573 url := self._url(product, version, regionId, resource) 574 resp, err := self.request(httputils.GET, url, nil, nil) 575 if err != nil { 576 return nil, err 577 } 578 return resp, nil 579 } 580 581 func (self *SHcsClient) delete(product, version, regionId string, resource string) error { 582 return self._delete(product, version, regionId, resource) 583 } 584 585 func (self *SHcsClient) _delete(product, version, regionId string, resource string) error { 586 url := self._url(product, version, regionId, resource) 587 resp, err := self.request(httputils.DELETE, url, nil, nil) 588 if !gotypes.IsNil(resp) && resp.Contains("job_id") { 589 jobId, _ := resp.GetString("job_id") 590 _, err := self.waitJobSuccess(product, regionId, jobId, time.Second*10, time.Hour*2) 591 if err != nil { 592 return errors.Wrapf(err, "wait create %s %s job", product, resource) 593 } 594 return nil 595 } 596 return err 597 } 598 599 func (self *SHcsClient) _deleteWithBody(product, version, regionId string, resource string, params map[string]interface{}) error { 600 url := self._url(product, version, regionId, resource) 601 _, err := self.request(httputils.DELETE, url, nil, params) 602 return err 603 } 604 605 func (self *SHcsClient) update(product, version, regionId string, resource string, params map[string]interface{}) error { 606 return self._update(product, version, regionId, resource, params) 607 } 608 609 func (self *SHcsClient) perform(product, version, regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error { 610 return self._perform(product, version, regionId, resource, action, params, retVal) 611 } 612 613 func (self *SHcsClient) _perform(product, version, regionId string, resource, action string, params map[string]interface{}, retVal interface{}) error { 614 url := self._url(product, version, regionId, resource+"/"+action) 615 resp, err := self.request(httputils.POST, url, nil, params) 616 if err != nil { 617 return err 618 } 619 if !gotypes.IsNil(resp) && resp.Contains("job_id") { 620 jobId, _ := resp.GetString("job_id") 621 job, err := self.waitJobSuccess(product, regionId, jobId, time.Second*10, time.Hour*2) 622 if err != nil { 623 return errors.Wrapf(err, "wait create %s %s job", product, resource) 624 } 625 if retVal != nil { 626 return jsonutils.Update(retVal, jsonutils.Marshal(job)) 627 } 628 return nil 629 } 630 if retVal != nil { 631 return resp.Unmarshal(retVal) 632 } 633 return nil 634 } 635 636 func (self *SHcsClient) create(product, version, regionId string, resource string, body map[string]interface{}, retVal interface{}) error { 637 return self._create(product, version, regionId, resource, body, retVal) 638 } 639 640 func (self *SHcsClient) _create(product, version, regionId string, resource string, body map[string]interface{}, retVal interface{}) error { 641 url := self._url(product, version, regionId, resource) 642 resp, err := self.request(httputils.POST, url, nil, body) 643 if err != nil { 644 return err 645 } 646 if resp.Contains("job_id") { 647 jobId, _ := resp.GetString("job_id") 648 job, err := self.waitJobSuccess(product, regionId, jobId, time.Second*10, time.Hour*2) 649 if err != nil { 650 return errors.Wrapf(err, "wait create %s %s job", product, resource) 651 } 652 if retVal != nil { 653 return jsonutils.Update(retVal, jsonutils.Marshal(job)) 654 } 655 return nil 656 } 657 obj, err := resp.GetMap() 658 if err != nil { 659 return errors.Wrapf(err, "GetMap") 660 } 661 for v := range obj { 662 if len(obj) == 1 && retVal != nil { 663 return obj[v].Unmarshal(retVal) 664 } 665 } 666 if retVal != nil { 667 return resp.Unmarshal(retVal) 668 } 669 return nil 670 } 671 672 func (self *SHcsClient) _update(product, version, regionId string, resource string, body map[string]interface{}) error { 673 url := self._url(product, version, regionId, resource) 674 _, err := self.request(httputils.PUT, url, nil, body) 675 return err 676 } 677 678 func (self *SHcsClient) GetRegions() []SRegion { 679 return self.regions 680 } 681 682 func (self *SHcsClient) GetIRegions() []cloudprovider.ICloudRegion { 683 ret := []cloudprovider.ICloudRegion{} 684 if len(self.projectId) == 0 { 685 for i := range self.regions { 686 self.regions[i].client = self 687 self.defaultRegion = self.regions[i].Id 688 ret = append(ret, &self.regions[i]) 689 } 690 return ret 691 } else { 692 for i := range self.regions { 693 if strings.Contains(self.projectName, self.regions[i].Id) { 694 self.defaultRegion = self.regions[i].Id 695 self.regions[i].client = self 696 ret = append(ret, &self.regions[i]) 697 } 698 if self.projectName == self.regions[i].Id { 699 self.isMainProject = true 700 } 701 } 702 } 703 return ret 704 } 705 706 func (self *SHcsClient) GetCapabilities() []string { 707 caps := []string{ 708 cloudprovider.CLOUD_CAPABILITY_PROJECT, 709 cloudprovider.CLOUD_CAPABILITY_COMPUTE, 710 cloudprovider.CLOUD_CAPABILITY_NETWORK, 711 cloudprovider.CLOUD_CAPABILITY_CACHE, 712 cloudprovider.CLOUD_CAPABILITY_RDS, 713 cloudprovider.CLOUD_CAPABILITY_LOADBALANCER, 714 cloudprovider.CLOUD_CAPABILITY_NAT, 715 } 716 // huawei objectstore is shared across projects(subscriptions) 717 // to avoid multiple project access the same bucket 718 // only main project is allow to access objectstore bucket 719 if self.isMainProject { 720 caps = append(caps, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE) 721 } 722 return caps 723 } 724 725 func (self *SHcsClient) getOBSEndpoint(regionId string) string { 726 return fmt.Sprintf("obs.%s.%s", regionId, self.authUrl) 727 } 728 729 func (self *SHcsClient) getOBSClient(regionId string) (*obs.ObsClient, error) { 730 endpoint := self.getOBSEndpoint(regionId) 731 ts, _ := self.httpClient.Transport.(*http.Transport) 732 conf := obs.WithHttpTransport(ts) 733 cli, err := obs.New(self.accessKey, self.accessSecret, endpoint, conf) 734 if err != nil { 735 return nil, err 736 } 737 return cli, nil 738 } 739 740 func (self *SHcsClient) GetBuckets() ([]SBucket, error) { 741 if len(self.buckets) > 0 { 742 return self.buckets, nil 743 } 744 obscli, err := self.getOBSClient(self.regions[0].Id) 745 if err != nil { 746 return nil, errors.Wrap(err, "getOBSClient") 747 } 748 input := &obs.ListBucketsInput{QueryLocation: true} 749 output, err := obscli.ListBuckets(input) 750 if err != nil { 751 return nil, errors.Wrap(err, "obscli.ListBuckets") 752 } 753 self.buckets = []SBucket{} 754 for i := range output.Buckets { 755 bInfo := output.Buckets[i] 756 region, err := self.GetRegion(bInfo.Location) 757 if err != nil { 758 log.Errorf("fail to find region %s", bInfo.Location) 759 continue 760 } 761 b := SBucket{ 762 region: region, 763 764 Name: bInfo.Name, 765 Location: bInfo.Location, 766 CreationDate: bInfo.CreationDate, 767 } 768 self.buckets = append(self.buckets, b) 769 } 770 return self.buckets, nil 771 }