fix(region): refactor huawei api for support organization (#19068)

This commit is contained in:
屈轩
2023-12-20 19:31:18 +08:00
committed by GitHub
parent 9b0648fca8
commit 7a922aeede
118 changed files with 2256 additions and 7152 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ require (
k8s.io/client-go v0.19.3
k8s.io/cluster-bootstrap v0.19.3
moul.io/http2curl/v2 v2.3.0
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20231220031827-28bfe13ab842
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20231220111524-ab10dde9c9b0
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32
yunion.io/x/jsonutils v1.0.1-0.20230613121553-0f3b41e2ef19
yunion.io/x/log v1.0.1-0.20230411060016-feb3f46ab361
+2 -2
View File
@@ -1201,8 +1201,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20231220031827-28bfe13ab842 h1:4AjLgwOnaAexsd+nrUodqg4m6cm4sB+HaA9deBkd9Tc=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20231220031827-28bfe13ab842/go.mod h1:aj1gR9PPb6eqqKOwvANe26CoZFY8ydmXy0fuvgKYXH0=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20231220111524-ab10dde9c9b0 h1:2Sj/zBlEgWaQGNxQXh1ZYLlNzZh0DAsTe3J/+eYBXZE=
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20231220111524-ab10dde9c9b0/go.mod h1:aj1gR9PPb6eqqKOwvANe26CoZFY8ydmXy0fuvgKYXH0=
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 h1:v7POYkQwo1XzOxBoIoRVr/k0V9Y5JyjpshlIFa9raug=
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
@@ -590,6 +590,9 @@ func (drv *SManagedVirtualizedGuestDriver) RemoteDeployGuestForCreate(ctx contex
if err == nil || !options.Options.EnableAutoSwitchServerSku {
return iVM, err
}
if errors.Cause(err) != cloudprovider.ErrInvalidSku {
return iVM, err
}
skus, e := models.ServerSkuManager.GetSkus(host.GetProviderName(), guest.VcpuCount, guest.VmemSize)
if e != nil {
return iVM, errors.Wrapf(e, "GetSkus")
+19 -6
View File
@@ -102,7 +102,7 @@ type SElasticcache struct {
CapacityMB int `nullable:"false" list:"user" create:"optional" json:"capacity_mb"`
// 对应Sku
LocalCategory string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" json:"local_category"`
LocalCategory string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" json:"local_category"`
// 类型
// single(单副本) | double(双副本) | readone (单可读) | readthree 3可读) | readfive5只读)
@@ -212,22 +212,23 @@ func (manager *SElasticcacheManager) FetchCustomizeColumns(
virtRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
manRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
regRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
zoneRows := manager.SZoneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
netIds := make([]string, len(objs))
cacheIds := make([]string, len(objs))
vpcIds := make([]string, len(objs))
zoneIds := []string{}
for i := range rows {
rows[i] = api.ElasticcacheDetails{
VirtualResourceDetails: virtRows[i],
VpcResourceInfo: vpcRows[i],
ZoneResourceInfoBase: zoneRows[i].ZoneResourceInfoBase,
}
rows[i].ManagedResourceInfo = manRows[i]
rows[i].CloudregionResourceInfo = regRows[i]
netIds[i] = objs[i].(*SElasticcache).NetworkId
cacheIds[i] = objs[i].(*SElasticcache).Id
cache := objs[i].(*SElasticcache)
netIds[i] = cache.NetworkId
cacheIds[i] = cache.Id
vpcIds[i] = cache.VpcId
sz := strings.Split(objs[i].(*SElasticcache).SlaveZones, ",")
for j := range sz {
@@ -237,8 +238,15 @@ func (manager *SElasticcacheManager) FetchCustomizeColumns(
}
}
vpcs := make(map[string]SVpc)
err := db.FetchStandaloneObjectsByIds(VpcManager, vpcIds, vpcs)
if err != nil {
log.Errorf("FetchStandaloneObjectsByIds fail %s", err)
return nil
}
networks := make(map[string]SNetwork)
err := db.FetchStandaloneObjectsByIds(NetworkManager, netIds, &networks)
err = db.FetchStandaloneObjectsByIds(NetworkManager, netIds, &networks)
if err != nil {
log.Errorf("FetchStandaloneObjectsByIds fail %s", err)
return rows
@@ -248,6 +256,11 @@ func (manager *SElasticcacheManager) FetchCustomizeColumns(
if net, ok := networks[netIds[i]]; ok {
rows[i].Network = net.Name
}
if vpc, ok := vpcs[vpcIds[i]]; ok {
rows[i].Vpc = vpc.Name
rows[i].VpcExtId = vpc.ExternalId
rows[i].IsDefaultVpc = vpc.IsDefault
}
}
if len(fields) == 0 || fields.Contains("secgroups") || fields.Contains("secgroup") {
@@ -354,11 +354,15 @@ func (self *SVpcPeeringConnection) syncRemove(ctx context.Context, userCred mccl
return self.RealDelete(ctx, userCred)
}
func (self *SVpcPeeringConnection) SyncWithCloudPeerConnection(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudVpcPeeringConnection, provider *SCloudprovider) error {
func (self *SVpcPeeringConnection) SyncWithCloudPeerConnection(ctx context.Context, userCred mcclient.TokenCredential, ext cloudprovider.ICloudVpcPeeringConnection) error {
vpc, err := self.GetVpc()
if err != nil {
return errors.Wrapf(err, "GetVpc")
}
provider := vpc.GetCloudprovider()
if provider == nil {
return errors.Wrapf(cloudprovider.ErrNotFound, "cloudprovider for vpc %s", vpc.Name)
}
_, err = db.Update(self, func() error {
self.Status = ext.GetStatus()
self.ExternalId = ext.GetGlobalId()
+1 -1
View File
@@ -1742,7 +1742,7 @@ func (svpc *SVpc) SyncVpcPeeringConnections(
if !xor {
for i := 0; i < len(commondb); i += 1 {
err = commondb[i].SyncWithCloudPeerConnection(ctx, userCred, commonext[i], provider)
err = commondb[i].SyncWithCloudPeerConnection(ctx, userCred, commonext[i])
if err != nil {
result.UpdateError(err)
continue
@@ -90,7 +90,7 @@ func (self *VpcPeeringConnectionCreateTask) OnInit(ctx context.Context, obj db.I
}
iPeerConnection.Refresh()
err = peer.SyncWithCloudPeerConnection(ctx, self.GetUserCred(), iPeerConnection, nil)
err = peer.SyncWithCloudPeerConnection(ctx, self.GetUserCred(), iPeerConnection)
if err != nil {
self.taskFailed(ctx, peer, errors.Wrapf(err, "SyncWithCloudPeerConnection"))
return
@@ -69,7 +69,7 @@ func (self *VpcPeeringConnectionSyncstatusTask) OnInit(ctx context.Context, obj
return
}
err = peer.SyncWithCloudPeerConnection(ctx, self.UserCred, ipeer, nil)
err = peer.SyncWithCloudPeerConnection(ctx, self.UserCred, ipeer)
if err != nil {
self.taskFail(ctx, peer, errors.Wrapf(err, "SyncWithCloudPeerConnection(ctx, self.UserCred, %s, nil)", jsonutils.Marshal(ipeer).String()))
return
+1 -9
View File
@@ -1465,7 +1465,7 @@ sigs.k8s.io/structured-merge-diff/v4/value
# sigs.k8s.io/yaml v1.2.0
## explicit; go 1.12
sigs.k8s.io/yaml
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20231220031827-28bfe13ab842
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20231220111524-ab10dde9c9b0
## explicit; go 1.18
yunion.io/x/cloudmux/pkg/apis
yunion.io/x/cloudmux/pkg/apis/billing
@@ -1509,14 +1509,6 @@ yunion.io/x/cloudmux/pkg/multicloud/hcso/client/requests
yunion.io/x/cloudmux/pkg/multicloud/hcso/client/responses
yunion.io/x/cloudmux/pkg/multicloud/hcso/provider
yunion.io/x/cloudmux/pkg/multicloud/huawei
yunion.io/x/cloudmux/pkg/multicloud/huawei/client
yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth
yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/credentials
yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/signers
yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager
yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules
yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests
yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses
yunion.io/x/cloudmux/pkg/multicloud/huawei/obs
yunion.io/x/cloudmux/pkg/multicloud/huawei/provider
yunion.io/x/cloudmux/pkg/multicloud/jdcloud
+1
View File
@@ -29,6 +29,7 @@ const (
ErrNoPermission = errors.Error("NoPermission")
ErrNoSuchProvder = errors.Error("no such provider")
ErrTooManyRequests = errors.Error("TooManyRequests")
ErrInvalidSku = errors.Error("InvalidSku")
ErrNotFound = errors.ErrNotFound
ErrDuplicateId = errors.ErrDuplicateId
+41 -9
View File
@@ -23,13 +23,45 @@ import (
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/hcso/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/hcso/client/requests"
hw_mod "yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules"
)
type SCloudEyeManager struct {
SResourceManager
}
type SMetricDimension struct {
Name string `json:"name"`
Value string `json:"value"`
}
type SDatapoint struct {
Timestamp int64 `json:"timestamp"`
Max float64 `json:"max,omitzero"`
Min float64 `json:"min,omitzero"`
Average float64 `json:"average,omitzero"`
Sum float64 `json:"sum,omitzero"`
Variance float64 `json:"variance,omitzero"`
}
type SMetricData struct {
SMetricMeta
Datapoints []SDatapoint
}
type SMetricMeta struct {
SMetric
Unit string `json:"unit"`
}
type SMetric struct {
MetricName string `json:"metric_name"`
Namespace string `json:"namespace"`
Dimensions []SMetricDimension `json:"dimensions"`
}
func NewCloudEyeManager(cfg manager.IManagerConfig) *SCloudEyeManager {
return &SCloudEyeManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
@@ -43,8 +75,8 @@ func NewCloudEyeManager(cfg manager.IManagerConfig) *SCloudEyeManager {
}}
}
func (ces *SCloudEyeManager) ListMetrics() ([]hw_mod.SMetricMeta, error) {
metrics := make([]hw_mod.SMetricMeta, 0)
func (ces *SCloudEyeManager) ListMetrics() ([]SMetricMeta, error) {
metrics := make([]SMetricMeta, 0)
next := ""
for {
marker, data, err := ces.listMetricsInternal(next)
@@ -60,7 +92,7 @@ func (ces *SCloudEyeManager) ListMetrics() ([]hw_mod.SMetricMeta, error) {
return metrics, nil
}
func (ces *SCloudEyeManager) listMetricsInternal(start string) (string, []hw_mod.SMetricMeta, error) {
func (ces *SCloudEyeManager) listMetricsInternal(start string) (string, []SMetricMeta, error) {
request := requests.NewResourceRequest(ces.GetEndpoint(), "GET", string(ces.ServiceName), ces.version, ces.Region, ces.ProjectId, ces.ResourceKeyword)
request.AddQueryParam("limit", "1000")
if len(start) > 0 {
@@ -71,7 +103,7 @@ func (ces *SCloudEyeManager) listMetricsInternal(start string) (string, []hw_mod
return "", nil, errors.Wrap(err, "ces.jsonRequest")
}
marker, _ := resp.GetString("meta_data", "marker")
metrics := make([]hw_mod.SMetricMeta, 0)
metrics := make([]SMetricMeta, 0)
err = resp.Unmarshal(&metrics, "metrics")
if err != nil {
return "", nil, errors.Wrap(err, "resp.Unmarshal metrics")
@@ -80,7 +112,7 @@ func (ces *SCloudEyeManager) listMetricsInternal(start string) (string, []hw_mod
}
type SBatchQueryMetricDataInput struct {
Metrics []hw_mod.SMetric `json:"metrics"`
Metrics []SMetric `json:"metrics"`
From int64 `json:"from"`
To int64 `json:"to"`
@@ -88,11 +120,11 @@ type SBatchQueryMetricDataInput struct {
Filter string `json:"filter"`
}
func (ces *SCloudEyeManager) GetMetricsData(metrics []hw_mod.SMetricMeta, since time.Time, until time.Time) ([]hw_mod.SMetricData, error) {
func (ces *SCloudEyeManager) GetMetricsData(metrics []SMetricMeta, since time.Time, until time.Time) ([]SMetricData, error) {
if len(metrics) > 10 {
return nil, errors.Wrap(cloudprovider.ErrTooLarge, "request more than 10 metrics")
}
metricReq := make([]hw_mod.SMetric, len(metrics))
metricReq := make([]SMetric, len(metrics))
for i := range metrics {
metricReq[i] = metrics[i].SMetric
}
@@ -111,7 +143,7 @@ func (ces *SCloudEyeManager) GetMetricsData(metrics []hw_mod.SMetricMeta, since
return nil, errors.Wrap(err, "ces.jsonRequest")
}
//log.Debugf("%s", resp)
result := make([]hw_mod.SMetricData, 0)
result := make([]SMetricData, 0)
err = resp.Unmarshal(&result, "metrics")
if err != nil {
return nil, errors.Wrap(err, "resp.Unmarshal")
+1 -1
View File
@@ -25,7 +25,7 @@ import (
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules"
"yunion.io/x/cloudmux/pkg/multicloud/hcso/client/modules"
)
type MetricData struct {
@@ -1,18 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
type Credential interface {
}
@@ -1,27 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package credentials
type AccessKeyCredential struct {
AccessKeyId string
AccessKeySecret string
}
func NewAccessKeyCredential(accessKeyId, accessKeySecret string) *AccessKeyCredential {
return &AccessKeyCredential{
AccessKeyId: accessKeyId,
AccessKeySecret: accessKeySecret,
}
}
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package credentials // import "yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/credentials"
-15
View File
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth // import "yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth"
-215
View File
@@ -1,215 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"
"sort"
"strings"
"sync"
"time"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/credentials"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/signers"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests"
)
type Signer interface {
GetName() string // 签名算法名称
GetAccessKeyId() (accessKeyId string, err error) // 签名access key
GetSecretKey() (secretKey string, err error) // access secret
Sign(stringToSign, secretSuffix string) string // 生成签名结果
}
func NewSignerWithCredential(credential Credential) (signer Signer, err error) {
switch instance := credential.(type) {
case *credentials.AccessKeyCredential:
return signers.NewAccessKeySigner(instance), nil
default:
return nil, fmt.Errorf("unsupported credential error")
}
}
// 对request进行签名
func Sign(request requests.IRequest, signer Signer) (err error) {
return signRequest(request, signer)
}
func signRequest(request requests.IRequest, signer Signer) error {
// https://support.huaweicloud.com/api-dis/dis_02_0508.html
// requestTime
reqTime := time.Now()
// 添加 必须的Headers
fillRequiredHeaders(request, reqTime)
// 计算CanonicalRequest
canonicalRequest := canonicalRequest(request)
// stringToSign
credentialScope := strings.Join([]string{
formattedSignTime(reqTime, "Date"),
request.GetRegionId(),
request.GetProduct(),
"sdk_request",
}, "/")
stringToSign := strings.Join([]string{"SDK-HMAC-SHA256",
formattedSignTime(reqTime, "DateTime"),
credentialScope,
hashSha256([]byte(canonicalRequest)),
}, "\n")
// 计算SigningKey
secret, _ := signer.GetSecretKey()
signKey := getSigningKey(secret, formattedSignTime(reqTime, "Date"),
request.GetRegionId(), request.GetProduct())
// 计算Signature
signature := signer.Sign(stringToSign, signKey)
accesskey, _ := signer.GetAccessKeyId()
addAuthorizationHeader(request, accesskey, credentialScope, signature)
return nil
}
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0132456728.html
// X-Project-Id 如果是专属云场景采用AK/SK 认证方式的接口请求或者多project场景采用AK/SK认证的接口请求则该字段必选。
func fillRequiredHeaders(request requests.IRequest, t time.Time) {
request.AddHeaderParam("HOST", request.GetHost())
request.AddHeaderParam("X-Sdk-Date", formattedSignTime(t, "DateTime"))
if len(request.GetProjectId()) > 0 {
request.AddHeaderParam("X-Project-Id", request.GetProjectId())
}
return
}
func buildRequestStringToSign(request requests.IRequest) string {
return ""
}
func formattedSignTime(t time.Time, format string) string {
switch format {
case "Date":
return t.UTC().Format("20060102")
case "DateTime":
return t.UTC().Format("20060102T150405Z")
default:
return t.UTC().Format("20060102T150405Z")
}
}
func contentSha256(request requests.IRequest) string {
method := strings.ToUpper(request.GetMethod())
content := []byte{}
body := request.GetBodyReader()
content, _ = ioutil.ReadAll(body)
if method == "POST" {
if len(content) == 0 {
// other http method use query as content
content = []byte(request.BuildQueries())
}
}
return hashSha256(content)
}
func hashSha256(msg []byte) string {
sh256 := sha256.New()
sh256.Write(msg)
return hex.EncodeToString(sh256.Sum(nil))
}
func canonicalRequest(request requests.IRequest) string {
sha256 := contentSha256(request)
uri := request.GetURI()
if !strings.HasSuffix(uri, "/") {
uri = uri + "/"
}
return strings.Join([]string{
request.GetMethod(),
uri,
canonicalQueryString(request),
canonicalHeaders(request),
canonicalHeaderNames(request),
sha256,
}, "\n")
}
func sortedHeaderNames(request requests.IRequest) []string {
headers := request.GetHeaders()
keys := make([]string, 0)
for k := range headers {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return strings.ToLower(keys[i]) < strings.ToLower(keys[j])
})
return keys
}
func canonicalQueryString(request requests.IRequest) string {
if strings.ToUpper(request.GetMethod()) != "POST" {
return request.BuildQueries()
}
return ""
}
func canonicalHeaders(request requests.IRequest) string {
keys := sortedHeaderNames(request)
headers := request.GetHeaders()
ret := []string{}
for _, k := range keys {
ret = append(ret, strings.ToLower(k)+":"+strings.TrimSpace(headers[k]))
}
return strings.Join(ret, "\n") + "\n"
}
func canonicalHeaderNames(request requests.IRequest) string {
keys := sortedHeaderNames(request)
ret := strings.Join(keys, ";")
return strings.ToLower(ret)
}
var SigningKeyCache sync.Map // map[string][]byte{}
func getSigningKey(secretKey, date, regionId, service string) string {
joinedKey := strings.Join([]string{secretKey, date, regionId, service}, "")
cacheKey := fmt.Sprintf("%x", md5.Sum([]byte(joinedKey)))
if v, ok := SigningKeyCache.Load(cacheKey); ok {
return v.(string)
}
ret := []byte("SDK" + secretKey)
for _, k := range []string{date, regionId, service, "sdk_request"} {
ret = signers.HmacSha256(k, ret)
}
SigningKeyCache.Store(cacheKey, string(ret))
return string(ret)
}
func addAuthorizationHeader(request requests.IRequest, accessKey, credentialScope, signature string) {
auth := "SDK-HMAC-SHA256" + " " + strings.Join([]string{
"Credential=" + accessKey + "/" + credentialScope,
"SignedHeaders=" + canonicalHeaderNames(request),
"Signature=" + signature,
}, ", ")
request.AddHeaderParam("Authorization", auth)
}
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signers // import "yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/signers"
@@ -1,55 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package signers
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/credentials"
)
type AccessKeySigner struct {
credential *credentials.AccessKeyCredential
}
func (signer *AccessKeySigner) GetName() string {
return "HmacSha256"
}
func (signer *AccessKeySigner) GetAccessKeyId() (accessKeyId string, err error) {
return signer.credential.AccessKeyId, nil
}
func (signer *AccessKeySigner) GetSecretKey() (secretKey string, err error) {
return signer.credential.AccessKeySecret, nil
}
func (signer *AccessKeySigner) Sign(stringToSign, secretSuffix string) string {
return hex.EncodeToString(HmacSha256(stringToSign, []byte(secretSuffix)))
}
func HmacSha256(data string, key []byte) []byte {
mac := hmac.New(sha256.New, key)
mac.Write([]byte(data))
return mac.Sum(nil)
}
func NewAccessKeySigner(credential *credentials.AccessKeyCredential) *AccessKeySigner {
return &AccessKeySigner{
credential: credential,
}
}
-232
View File
@@ -1,232 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client
import (
"net/http"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/credentials"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules"
)
type Client struct {
cfg *SClientConfig
// 标记初始化状态
init bool
Credentials *modules.SCredentialManager
Disks *modules.SDiskManager
Domains *modules.SDomainManager
Elasticcache *modules.SElasticcacheManager
DcsAvailableZone *modules.SDcsAvailableZoneManager
Flavors *modules.SFlavorManager
Images *modules.SImageManager
OpenStackImages *modules.SImageManager
Interface *modules.SInterfaceManager
Jobs *modules.SJobManager
Orders *modules.SOrderManager
Port *modules.SPortManager
Projects *modules.SProjectManager
Regions *modules.SRegionManager
Servers *modules.SServerManager
ServersV2 *modules.SServerManager
NovaServers *modules.SServerManager
Snapshots *modules.SSnapshotManager
OsSnapshots *modules.SSnapshotManager
Subnets *modules.SSubnetManager
Users *modules.SUserManager
Vpcs *modules.SVpcManager
Zones *modules.SZoneManager
VpcRoutes *modules.SVpcRouteManager
SNatRules *modules.SNatSRuleManager
DNatRules *modules.SNatDRuleManager
NatGateways *modules.SNatGatewayManager
VpcPeerings *modules.SVpcPeeringManager
DBInstance *modules.SDBInstanceManager
DBInstanceBackup *modules.SDBInstanceBackupManager
DBInstanceFlavor *modules.SDBInstanceFlavorManager
DBInstanceJob *modules.SDBInstanceJobManager
Traces *modules.STraceManager
CloudEye *modules.SCloudEyeManager
EnterpriseProjects *modules.SEnterpriseProjectManager
Roles *modules.SRoleManager
Groups *modules.SGroupManager
SAMLProviders *modules.SAMLProviderManager
SAMLProviderMappings *modules.SAMLProviderMappingManager
SfsTurbos *modules.SfsTurboManager
RouteTables *modules.SRouteTableManager
}
type SClientConfig struct {
signer auth.Signer
endpoint string // myhuaweicloud.com
regionId string
domainId string
projectId string
debug bool
}
func (self *SClientConfig) GetSigner() auth.Signer {
return self.signer
}
func (self *SClientConfig) GetEndpoint() string {
return self.endpoint
}
func (self *SClientConfig) GetRegionId() string {
return self.regionId
}
func (self *SClientConfig) GetDomainId() string {
return self.domainId
}
func (self *SClientConfig) GetProjectId() string {
return self.projectId
}
func (self *SClientConfig) GetDebug() bool {
return self.debug
}
func (self *Client) SetHttpClient(httpClient *http.Client) {
self.Credentials.SetHttpClient(httpClient)
self.Servers.SetHttpClient(httpClient)
self.ServersV2.SetHttpClient(httpClient)
self.NovaServers.SetHttpClient(httpClient)
self.Snapshots.SetHttpClient(httpClient)
self.OsSnapshots.SetHttpClient(httpClient)
self.Images.SetHttpClient(httpClient)
self.OpenStackImages.SetHttpClient(httpClient)
self.Projects.SetHttpClient(httpClient)
self.Regions.SetHttpClient(httpClient)
self.Zones.SetHttpClient(httpClient)
self.Vpcs.SetHttpClient(httpClient)
self.Elasticcache.SetHttpClient(httpClient)
self.DcsAvailableZone.SetHttpClient(httpClient)
self.Disks.SetHttpClient(httpClient)
self.Domains.SetHttpClient(httpClient)
self.Orders.SetHttpClient(httpClient)
self.Subnets.SetHttpClient(httpClient)
self.Users.SetHttpClient(httpClient)
self.Interface.SetHttpClient(httpClient)
self.Jobs.SetHttpClient(httpClient)
self.Port.SetHttpClient(httpClient)
self.Flavors.SetHttpClient(httpClient)
self.VpcRoutes.SetHttpClient(httpClient)
self.SNatRules.SetHttpClient(httpClient)
self.DNatRules.SetHttpClient(httpClient)
self.NatGateways.SetHttpClient(httpClient)
self.DBInstance.SetHttpClient(httpClient)
self.DBInstanceBackup.SetHttpClient(httpClient)
self.DBInstanceFlavor.SetHttpClient(httpClient)
self.DBInstanceJob.SetHttpClient(httpClient)
self.Traces.SetHttpClient(httpClient)
self.CloudEye.SetHttpClient(httpClient)
self.EnterpriseProjects.SetHttpClient(httpClient)
self.Roles.SetHttpClient(httpClient)
self.Groups.SetHttpClient(httpClient)
self.SAMLProviders.SetHttpClient(httpClient)
self.SAMLProviderMappings.SetHttpClient(httpClient)
self.SfsTurbos.SetHttpClient(httpClient)
self.RouteTables.SetHttpClient(httpClient)
}
func (self *Client) InitWithAccessKey(endpoint, regionId, domainId, projectId, accessKey, secretKey string, debug bool) error {
// accessKey signer
credential := &credentials.AccessKeyCredential{
AccessKeyId: accessKey,
AccessKeySecret: secretKey,
}
// 从signer中初始化
signer, err := auth.NewSignerWithCredential(credential)
if err != nil {
return err
}
self.cfg = &SClientConfig{
signer: signer,
endpoint: endpoint,
regionId: regionId,
domainId: domainId,
projectId: projectId,
debug: debug,
}
// 初始化 resource manager
self.initManagers()
return err
}
func (self *Client) initManagers() {
if !self.init {
self.Servers = modules.NewServerManager(self.cfg)
self.ServersV2 = modules.NewServerV2Manager(self.cfg)
self.NovaServers = modules.NewNovaServerManager(self.cfg)
self.Snapshots = modules.NewSnapshotManager(self.cfg)
self.OsSnapshots = modules.NewOsSnapshotManager(self.cfg)
self.Images = modules.NewImageManager(self.cfg)
self.OpenStackImages = modules.NewOpenstackImageManager(self.cfg)
self.Projects = modules.NewProjectManager(self.cfg)
self.Regions = modules.NewRegionManager(self.cfg)
self.Zones = modules.NewZoneManager(self.cfg)
self.Vpcs = modules.NewVpcManager(self.cfg)
self.Elasticcache = modules.NewElasticcacheManager(self.cfg)
self.DcsAvailableZone = modules.NewDcsAvailableZoneManager(self.cfg)
self.Disks = modules.NewDiskManager(self.cfg)
self.Domains = modules.NewDomainManager(self.cfg)
self.Orders = modules.NewOrderManager(self.cfg)
self.Subnets = modules.NewSubnetManager(self.cfg)
self.Users = modules.NewUserManager(self.cfg)
self.Interface = modules.NewInterfaceManager(self.cfg)
self.Jobs = modules.NewJobManager(self.cfg)
self.Credentials = modules.NewCredentialManager(self.cfg)
self.Port = modules.NewPortManager(self.cfg)
self.Flavors = modules.NewFlavorManager(self.cfg)
self.VpcRoutes = modules.NewVpcRouteManager(self.cfg)
self.SNatRules = modules.NewNatSManager(self.cfg)
self.DNatRules = modules.NewNatDManager(self.cfg)
self.NatGateways = modules.NewNatGatewayManager(self.cfg)
self.VpcPeerings = modules.NewVpcPeeringManager(self.cfg)
self.DBInstance = modules.NewDBInstanceManager(self.cfg)
self.DBInstanceBackup = modules.NewDBInstanceBackupManager(self.cfg)
self.DBInstanceFlavor = modules.NewDBInstanceFlavorManager(self.cfg)
self.DBInstanceJob = modules.NewDBInstanceJobManager(self.cfg)
self.Traces = modules.NewTraceManager(self.cfg)
self.CloudEye = modules.NewCloudEyeManager(self.cfg)
self.EnterpriseProjects = modules.NewEnterpriseProjectManager(self.cfg)
self.Roles = modules.NewRoleManager(self.cfg)
self.Groups = modules.NewGroupManager(self.cfg)
self.SAMLProviders = modules.NewSAMLProviderManager(self.cfg)
self.SAMLProviderMappings = modules.NewSAMLProviderMappingManager(self.cfg)
self.SfsTurbos = modules.NewSfsTurboManager(self.cfg)
self.RouteTables = modules.NewRouteTableManager(self.cfg)
}
self.init = true
}
func NewClientWithAccessKey(endpoint, regionId, domainId, projectId, accessKey, secretKey string, debug bool) (*Client, error) {
c := &Client{}
err := c.InitWithAccessKey(endpoint, regionId, domainId, projectId, accessKey, secretKey, debug)
return c, err
}
func NewPublicCloudClientWithAccessKey(regionId, domainId, projectId, accessKey, secretKey string, debug bool) (*Client, error) {
return NewClientWithAccessKey("myhuaweicloud.com", regionId, domainId, projectId, accessKey, secretKey, debug)
}
-15
View File
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client // import "yunion.io/x/cloudmux/pkg/multicloud/huawei/client"
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package manager // import "yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
@@ -1,81 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package manager
import (
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type IManagerContext interface {
GetPath() string
}
type IBaseManager interface {
Version() string
KeyString() string
ServiceType() string
GetColumns() []string
}
type IManager interface {
IBaseManager
// 获取资源列表 GET <base_url>/cloudservers/?<queries>
List(queries map[string]string) (*responses.ListResult, error)
// 根据上文获取资源列表 GET <base_url>/cloudservers/<cloudserver_id>/nics?<queries>
ListInContext(ctx IManagerContext, queries map[string]string) (*responses.ListResult, error)
ListInContextWithSpec(ctx IManagerContext, spec string, queries map[string]string, responseKey string) (*responses.ListResult, error)
// 查询单个资源 GET <base_url>/cloudservers/<cloudserver_id>?<queries>
Get(id string, queries map[string]string) (jsonutils.JSONObject, error)
// 根据上文获取资源查询单个资源 GET <base_url>/cloudservers/<cloudserver_id>/nics/<nic_id>?<queries>
GetInContext(ctx IManagerContext, id string, queries map[string]string) (jsonutils.JSONObject, error)
// 创建单个资源 POST <base_url>/cloudservers
Create(params jsonutils.JSONObject) (jsonutils.JSONObject, error)
// 根据上文创建单个资源 POST <base_url>/cloudservers/<cloudserver_id>/nics/<nic_id>
CreateInContext(ctx IManagerContext, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
// 异步任务创建 POST <base_url>/cloudservers. 返回异步任务 job_id。 todo:// 后续考虑返回一个task对象
AsyncCreate(params jsonutils.JSONObject) (string, error)
// 更新单个资源 PUT <base_url>/cloudservers/<cloudserver_id>
Update(id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
// 根据上文更新单个资源 PUT <base_url>/cloudservers/<cloudserver_id>/nics/<nic_id>
UpdateInContext(ctx IManagerContext, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
// 根据上文和spec更新单个资源 PUT /v2.1/{project_id}/servers/{server_id}/os-reset-password
UpdateInContextWithSpec(ctx IManagerContext, id string, spec string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error)
// 删除单个资源 DELETE <base_url>/cloudservers/<cloudserver_id>
Delete(id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
// 根据上文删除单个资源 DELETE <base_url>/cloudservers/<cloudserver_id>/nics/<nic_id>
DeleteInContext(ctx IManagerContext, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
// 根据上文和spec删除单个资源
DeleteInContextWithSpec(ctx IManagerContext, id string, spec string, queries map[string]string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error)
// 批量执行操作 POST <base_url>/cloudservers/<action>
// BatchPerformAction(action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
// 执行操作 POST <base_url>/cloudservers/<cloudserver_id>/<action>
PerformAction(action string, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
}
type IManagerConfig interface {
GetSigner() auth.Signer
GetEndpoint() string
GetRegionId() string
GetDomainId() string
GetProjectId() string
GetDebug() bool
}
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules // import "yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules"
@@ -1,271 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/httputils"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type IRequestHook interface {
Process(r requests.IRequest)
}
type SBaseManager struct {
cfg manager.IManagerConfig
httpClient *http.Client
requestHook IRequestHook // 用于对request做特殊处理。非必要请不要使用!!!。目前只有port接口用到。
columns []string
debug bool
}
type sThrottlingThreshold struct {
locked bool
lockTime time.Time
}
func (t *sThrottlingThreshold) CheckingLock() {
if !t.locked {
return
}
for {
if t.lockTime.Sub(time.Now()).Seconds() < 0 {
return
}
log.Debugf("throttling threshold has been reached. release at %s", t.lockTime)
time.Sleep(5 * time.Second)
}
}
func (t *sThrottlingThreshold) Lock() {
// 锁定至少15秒
t.locked = true
t.lockTime = time.Now().Add(15 * time.Second)
}
var ThrottlingLock = sThrottlingThreshold{locked: false, lockTime: time.Time{}}
func NewBaseManager2(cfg manager.IManagerConfig, requesthk IRequestHook) SBaseManager {
return SBaseManager{
cfg: cfg,
httpClient: httputils.GetDefaultClient(),
debug: cfg.GetDebug(),
requestHook: requesthk,
}
}
func NewBaseManager(cfg manager.IManagerConfig) SBaseManager {
return NewBaseManager2(cfg, nil)
}
func (self *SBaseManager) GetEndpoint() string {
return self.cfg.GetEndpoint()
}
func (self *SBaseManager) GetColumns() []string {
return self.columns
}
func (self *SBaseManager) SetHttpClient(httpClient *http.Client) {
self.httpClient = httpClient
}
func (self *SBaseManager) _list(request requests.IRequest, responseKey string) (*responses.ListResult, error) {
_, body, err := self.jsonRequest(request)
if err != nil {
return nil, err
}
if body == nil {
log.Warningf("empty response")
return &responses.ListResult{}, nil
}
rets, err := body.GetArray(responseKey)
if err != nil {
return nil, errors.Wrapf(err, "body.GetArray %s", responseKey)
}
total, _ := body.Int("count")
// if err != nil {
// total = int64(len(rets))
//}
//if total == 0 {
// total = int64(len(rets))
//}
limit := 0
if v, exists := request.GetQueryParams()["limit"]; exists {
limit, _ = strconv.Atoi(v)
}
offset := 0
if v, exists := request.GetQueryParams()["offset"]; exists {
offset, _ = strconv.Atoi(v)
}
return &responses.ListResult{
Data: rets,
Total: int(total),
Limit: limit,
Offset: offset,
}, nil
}
func (self *SBaseManager) _do(request requests.IRequest, responseKey string) (jsonutils.JSONObject, error) {
_, resp, e := self.jsonRequest(request)
if e != nil {
return nil, e
}
if resp == nil { // no reslt
return jsonutils.NewDict(), nil
}
if len(responseKey) == 0 {
return resp, nil
}
ret, e := resp.Get(responseKey)
if e != nil {
return nil, e
}
return ret, nil
}
func (self *SBaseManager) _get(request requests.IRequest, responseKey string) (jsonutils.JSONObject, error) {
return self._do(request, responseKey)
}
type HuaweiClientError struct {
Code int
Errorcode []string
err error
Details string
ErrorCode string
}
func (ce *HuaweiClientError) Error() string {
return jsonutils.Marshal(ce).String()
}
func (ce *HuaweiClientError) ParseErrorFromJsonResponse(statusCode int, status string, body jsonutils.JSONObject) error {
if body != nil {
body.Unmarshal(ce)
}
if ce.Code == 0 {
ce.Code = statusCode
}
if len(ce.Details) == 0 && body != nil {
ce.Details = body.String()
}
return ce
}
func (self *SBaseManager) jsonRequest(request requests.IRequest) (http.Header, jsonutils.JSONObject, error) {
ThrottlingLock.CheckingLock()
ctx := context.Background()
// hook request
if self.requestHook != nil {
self.requestHook.Process(request)
}
// 拼接、编译、签名 requests here。
err := self.buildRequestWithSigner(request, self.cfg.GetSigner())
if err != nil {
return nil, nil, err
}
header := http.Header{}
for k, v := range request.GetHeaders() {
header.Set(k, v)
}
var jsonBody jsonutils.JSONObject
content := request.GetContent()
if len(content) > 0 {
jsonBody, err = jsonutils.Parse(content)
if err != nil {
return nil, nil, fmt.Errorf("not a json body")
}
}
client := httputils.NewJsonClient(self.httpClient)
req := httputils.NewJsonRequest(httputils.THttpMethod(request.GetMethod()), request.BuildUrl(), jsonBody)
req.SetHeader(header)
resp := &HuaweiClientError{}
const MAX_RETRY = 3
retry := MAX_RETRY
for {
h, b, e := client.Send(ctx, req, resp, self.debug)
if e == nil {
return h, b, nil
}
log.Errorf("[%s] %s body: %v error: %v", req.GetHttpMethod(), req.GetUrl(), jsonBody, e)
switch err := e.(type) {
case *HuaweiClientError:
if err.ErrorCode == "APIGW.0301" {
return h, b, errors.Wrapf(cloudprovider.ErrInvalidAccessKey, e.Error())
} else if err.Code == 499 && retry > 0 && request.GetMethod() == "GET" {
retry -= 1
time.Sleep(3 * time.Second * time.Duration(MAX_RETRY-retry))
} else if (err.Code == 404 || strings.Contains(err.Details, "could not be found") ||
strings.Contains(err.Error(), "Not Found") ||
strings.Contains(err.Details, "does not exist")) && request.GetMethod() != "POST" {
return h, b, errors.Wrap(cloudprovider.ErrNotFound, err.Error())
} else if err.Code == 429 && retry > 0 {
// 当前请求过多。
ThrottlingLock.Lock()
retry -= 1
time.Sleep(15 * time.Second)
} else {
return h, b, e
}
default:
return h, b, e
}
}
}
func (self *SBaseManager) rawRequest(request requests.IRequest) (*http.Response, error) {
ctx := context.Background()
// 拼接、编译requests here。
header := http.Header{}
for k, v := range request.GetHeaders() {
header.Set(k, v)
}
return httputils.Request(self.httpClient, ctx, httputils.THttpMethod(request.GetMethod()), request.BuildUrl(), header, request.GetBodyReader(), self.debug)
}
func (self *SBaseManager) buildRequestWithSigner(request requests.IRequest, signer auth.Signer) error {
return auth.Sign(request, signer)
}
@@ -1,298 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"net/url"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type ServiceNameType string
const HuaWeiDefaultDomain = "myhuaweicloud.com"
const (
ServiceNameECS ServiceNameType = "ecs" // 弹性云服务
ServiceNameCCE ServiceNameType = "cce" // 云容器服务
ServiceNameAS ServiceNameType = "as" // 弹性伸缩服务
ServiceNameIAM ServiceNameType = "iam" // 统一身份认证服务
ServiceNameIMS ServiceNameType = "ims" // 镜像服务
ServiceNameCSBS ServiceNameType = "csbs" // 云服务器备份服务
ServiceNameCCI ServiceNameType = "cci" // 云容器实例 CCI
ServiceNameBMS ServiceNameType = "bms" // 裸金属服务器
ServiceNameEVS ServiceNameType = "evs" // 云硬盘 EVS
ServiceNameVBS ServiceNameType = "vbs" // 云硬盘备份 VBS
ServiceNameOBS ServiceNameType = "obs" // 对象存储服务 OBS
ServiceNameVPC ServiceNameType = "vpc" // 虚拟私有云 VPC
ServiceNameELB ServiceNameType = "elb" // 弹性负载均衡 ELB
ServiceNameBSS ServiceNameType = "bss" // 合作伙伴运营能力
ServiceNameNAT ServiceNameType = "nat" // Nat网关 NAT
ServiceNameDCS ServiceNameType = "dcs" // 分布式缓存服务
ServiceNameRDS ServiceNameType = "rds" // 关系型数据库 RDS
ServiceNameCTS ServiceNameType = "cts" // 云审计服务
ServiceNameCES ServiceNameType = "ces" // 监控服务 CloudEye
ServiceNameEPS ServiceNameType = "eps" // 企业项目
ServiceNameModelarts ServiceNameType = "modelarts" // AI ModelArts
ServiceNameSFSTurbo ServiceNameType = "sfs-turbo" // 文件系统
)
type SManagerContext struct {
InstanceManager manager.IManager
InstanceId string
}
func (self *SManagerContext) GetPath() string {
path := self.InstanceManager.KeyString()
if len(self.InstanceId) > 0 {
path += fmt.Sprintf("/%s", url.PathEscape(self.InstanceId))
}
return path
}
type SResourceManager struct {
SBaseManager
ctx manager.IManagerContext
ServiceName ServiceNameType // 服务名称: ecs
Region string // 区域: cn-north-1
DomainId string
ProjectId string // 项目ID uuid
version string // api 版本号
Keyword string // 资源名称单数。构建URL时使用
KeywordPlural string // 资源名称复数形式。构建URL时使用
ResourceKeyword string // 资源名称。url中使用
}
func getContent(params jsonutils.JSONObject) string {
if params == nil {
return ""
}
return params.String()
}
func (self *SResourceManager) Version() string {
return self.version
}
func (self *SResourceManager) KeyString() string {
return self.ResourceKeyword
}
func (self *SResourceManager) ServiceType() string {
return string(self.ServiceName)
}
func (self *SResourceManager) GetColumns() []string {
return []string{}
}
func (self *SResourceManager) SetDomainId(domainId string) {
self.DomainId = domainId
}
func (self *SResourceManager) getReourcePath(ctx manager.IManagerContext, rid string, spec string) string {
segs := []string{}
if ctx != nil {
segs = append(segs, ctx.GetPath())
}
segs = append(segs, self.KeyString())
if len(rid) > 0 {
segs = append(segs, url.PathEscape(rid))
}
if len(spec) > 0 {
specSegs := strings.Split(spec, "/")
for _, specSeg := range specSegs {
segs = append(segs, url.PathEscape(specSeg))
}
}
return strings.Join(segs, "/")
}
func (self *SResourceManager) newRequest(method, rid, spec string, ctx manager.IManagerContext) *requests.SRequest {
resourcePath := self.getReourcePath(ctx, rid, spec)
return requests.NewResourceRequest(self.GetEndpoint(), method, string(self.ServiceName), self.version, self.Region, self.ProjectId, resourcePath)
}
func (self *SResourceManager) List(queries map[string]string) (*responses.ListResult, error) {
return self.ListInContext(self.ctx, queries)
}
func (self *SResourceManager) ListInContext(ctx manager.IManagerContext, queries map[string]string) (*responses.ListResult, error) {
return self.ListInContextWithSpec(ctx, "", queries, self.KeywordPlural)
}
func (self *SResourceManager) ListInContextWithSpec(ctx manager.IManagerContext, spec string, queries map[string]string, responseKey string) (*responses.ListResult, error) {
request := self.newRequest("GET", "", spec, ctx)
for k, v := range queries {
request.AddQueryParam(k, v)
}
if len(self.DomainId) > 0 {
request.AddHeaderParam("X-Domain-Id", self.DomainId)
}
return self._list(request, responseKey)
}
func (self *SResourceManager) Get(id string, queries map[string]string) (jsonutils.JSONObject, error) {
return self.GetInContext(self.ctx, id, queries)
}
func (self *SResourceManager) GetInContext(ctx manager.IManagerContext, id string, queries map[string]string) (jsonutils.JSONObject, error) {
return self.GetInContextWithSpec(ctx, id, "", queries, self.Keyword)
}
func (self *SResourceManager) GetInContextWithSpec(ctx manager.IManagerContext, id string, spec string, queries map[string]string, responseKey string) (jsonutils.JSONObject, error) {
request := self.newRequest("GET", id, spec, ctx)
for k, v := range queries {
request.AddQueryParam(k, v)
}
if len(self.DomainId) > 0 {
request.AddHeaderParam("X-Domain-Id", self.DomainId)
}
return self._get(request, responseKey)
}
func (self *SResourceManager) Create(params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.CreateInContext(self.ctx, params)
}
func (self *SResourceManager) CreateInContext(ctx manager.IManagerContext, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.CreateInContextWithSpec(ctx, "", params, self.Keyword)
}
func (self *SResourceManager) CreateInContextWithSpec(ctx manager.IManagerContext, spec string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error) {
request := self.newRequest("POST", "", spec, ctx)
request.SetContent([]byte(params.String()))
if len(self.DomainId) > 0 {
request.AddHeaderParam("X-Domain-Id", self.DomainId)
}
return self._do(request, responseKey)
}
func (self *SResourceManager) AsyncCreate(params jsonutils.JSONObject) (string, error) {
return "", fmt.Errorf("not supported")
}
func (self *SResourceManager) Update(id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.UpdateInContext(self.ctx, id, params)
}
func (self *SResourceManager) UpdateInContext(ctx manager.IManagerContext, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.UpdateInContextWithSpec(ctx, id, "", params, self.Keyword)
}
func (self *SResourceManager) UpdateInContextWithSpec(ctx manager.IManagerContext, id string, spec string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error) {
request := self.newRequest("PUT", id, spec, ctx)
content := getContent(params)
if len(content) > 0 {
request.SetContent([]byte(content))
}
if len(self.DomainId) > 0 {
request.AddHeaderParam("X-Domain-Id", self.DomainId)
}
return self._do(request, responseKey)
}
func (self *SResourceManager) Patch(id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.PatchInContext(self.ctx, id, params)
}
func (self *SResourceManager) PatchInContext(ctx manager.IManagerContext, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.PatchInContextWithSpec(ctx, id, "", params, self.Keyword)
}
func (self *SResourceManager) PatchInContextWithSpec(ctx manager.IManagerContext, id string, spec string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error) {
request := self.newRequest("PATCH", id, spec, ctx)
content := getContent(params)
if len(content) > 0 {
request.SetContent([]byte(content))
}
if len(self.DomainId) > 0 {
request.AddHeaderParam("X-Domain-Id", self.DomainId)
}
return self._do(request, responseKey)
}
func (self *SResourceManager) Delete(id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.DeleteInContext(self.ctx, id, params)
}
func (self *SResourceManager) DeleteInContext(ctx manager.IManagerContext, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.DeleteInContextWithSpec(ctx, id, "", nil, params, "")
}
func (self *SResourceManager) DeleteInContextWithSpec(ctx manager.IManagerContext, id string, spec string, queries map[string]string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error) {
request := self.newRequest("DELETE", id, spec, ctx)
for k, v := range queries {
request.AddQueryParam(k, v)
}
content := getContent(params)
if len(content) > 0 {
request.SetContent([]byte(content))
}
if len(self.DomainId) > 0 {
request.AddHeaderParam("X-Domain-Id", self.DomainId)
}
return self._do(request, responseKey)
}
func (self *SResourceManager) PerformAction(action string, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.PerformAction2(action, id, params, self.Keyword)
}
func (self *SResourceManager) PerformAction2(action string, id string, params jsonutils.JSONObject, responseKey string) (jsonutils.JSONObject, error) {
request := self.newRequest("POST", id, action, nil)
request.SetContent([]byte(getContent(params)))
if len(self.DomainId) > 0 {
request.AddHeaderParam("X-Domain-Id", self.DomainId)
}
return self._do(request, responseKey)
}
func (self *SResourceManager) SetVersion(v string) {
self.version = v
}
func (self *SResourceManager) versionedURL(path string) string {
return ""
}
// todo: Init a manager with environment variables
func (self *SResourceManager) Init() error {
return nil
}
@@ -1,152 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests"
)
type SCloudEyeManager struct {
SResourceManager
}
func NewCloudEyeManager(cfg manager.IManagerConfig) *SCloudEyeManager {
return &SCloudEyeManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameCES,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "V1.0",
Keyword: "",
KeywordPlural: "metrics",
ResourceKeyword: "metrics",
}}
}
type SMetricDimension struct {
Name string `json:"name"`
Value string `json:"value"`
}
type SDatapoint struct {
Timestamp int64 `json:"timestamp"`
Max float64 `json:"max,omitzero"`
Min float64 `json:"min,omitzero"`
Average float64 `json:"average,omitzero"`
Sum float64 `json:"sum,omitzero"`
Variance float64 `json:"variance,omitzero"`
}
type SMetricData struct {
SMetricMeta
Datapoints []SDatapoint
}
type SMetricMeta struct {
SMetric
Unit string `json:"unit"`
}
type SMetric struct {
MetricName string `json:"metric_name"`
Namespace string `json:"namespace"`
Dimensions []SMetricDimension `json:"dimensions"`
}
func (ces *SCloudEyeManager) ListMetrics() ([]SMetricMeta, error) {
metrics := make([]SMetricMeta, 0)
next := ""
for {
marker, data, err := ces.listMetricsInternal(next)
if err != nil {
return nil, errors.Wrap(err, "ces.listMetricsInternal")
}
if len(data) == 0 {
break
}
metrics = append(metrics, data...)
next = marker
}
return metrics, nil
}
func (ces *SCloudEyeManager) listMetricsInternal(start string) (string, []SMetricMeta, error) {
request := requests.NewResourceRequest(ces.GetEndpoint(), "GET", string(ces.ServiceName), ces.version, ces.Region, ces.ProjectId, ces.ResourceKeyword)
request.AddQueryParam("limit", "1000")
if len(start) > 0 {
request.AddQueryParam("start", start)
}
_, resp, err := ces.jsonRequest(request)
if err != nil {
return "", nil, errors.Wrap(err, "ces.jsonRequest")
}
marker, _ := resp.GetString("meta_data", "marker")
metrics := make([]SMetricMeta, 0)
err = resp.Unmarshal(&metrics, "metrics")
if err != nil {
return "", nil, errors.Wrap(err, "resp.Unmarshal metrics")
}
return marker, metrics, nil
}
type SBatchQueryMetricDataInput struct {
Metrics []SMetric `json:"metrics"`
From int64 `json:"from"`
To int64 `json:"to"`
Period string `json:"period"`
Filter string `json:"filter"`
}
func (ces *SCloudEyeManager) GetMetricsData(metrics []SMetricMeta, since time.Time, until time.Time) ([]SMetricData, error) {
if len(metrics) > 10 {
return nil, errors.Wrap(cloudprovider.ErrTooLarge, "request more than 10 metrics")
}
metricReq := make([]SMetric, len(metrics))
for i := range metrics {
metricReq[i] = metrics[i].SMetric
}
request := requests.NewResourceRequest(ces.GetEndpoint(), "POST", string(ces.ServiceName), ces.version, ces.Region, ces.ProjectId, "batch-query-metric-data")
input := SBatchQueryMetricDataInput{
Metrics: metricReq,
From: since.Unix() * 1000,
To: until.Unix() * 1000,
Period: "1",
Filter: "average",
}
body := jsonutils.Marshal(&input).String()
request.SetContent([]byte(body))
_, resp, err := ces.jsonRequest(request)
if err != nil {
return nil, errors.Wrap(err, "ces.jsonRequest")
}
//log.Debugf("%s", resp)
result := make([]SMetricData, 0)
err = resp.Unmarshal(&result, "metrics")
if err != nil {
return nil, errors.Wrap(err, "resp.Unmarshal")
}
return result, nil
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SCredentialManager struct {
SResourceManager
}
func NewCredentialManager(cfg manager.IManagerConfig) *SCredentialManager {
return &SCredentialManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3.0",
Keyword: "credential",
KeywordPlural: "credentials",
ResourceKeyword: "OS-CREDENTIAL/credentials",
}}
}
@@ -1,103 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SDBInstanceManager struct {
SResourceManager
}
func NewDBInstanceManager(cfg manager.IManagerConfig) *SDBInstanceManager {
return &SDBInstanceManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameRDS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v3",
Keyword: "",
KeywordPlural: "instances",
ResourceKeyword: "instances",
}}
}
func (self *SDBInstanceManager) Get(id string, querys map[string]string) (jsonutils.JSONObject, error) {
if len(id) == 0 {
return nil, cloudprovider.ErrNotFound
}
resp, err := self.GetInContextWithSpec(nil, "", "", map[string]string{"id": id}, "")
if err != nil {
return nil, err
}
instances, err := resp.GetArray("instances")
if err != nil {
return nil, err
}
if len(instances) == 0 {
return nil, cloudprovider.ErrNotFound
} else if len(instances) == 1 {
return instances[0], nil
}
return nil, cloudprovider.ErrDuplicateId
}
func (self *SDBInstanceManager) ListParameters(queries map[string]string) (*responses.ListResult, error) {
id, _ := queries["instance_id"]
if len(id) == 0 {
return nil, fmt.Errorf("SDBInstanceManager.ListParameters missing parameter instance_id")
}
delete(queries, "instance_id")
return self.ListInContextWithSpec(nil, fmt.Sprintf("%s/configurations", id), queries, "configuration_parameters")
}
func (self *SDBInstanceManager) ListDatabases(queries map[string]string) (*responses.ListResult, error) {
id, _ := queries["instance_id"]
if len(id) == 0 {
return nil, fmt.Errorf("SDBInstanceManager.ListDatabases missing parameter instance_id")
}
delete(queries, "instance_id")
return self.ListInContextWithSpec(nil, fmt.Sprintf("%s/database/detail", id), queries, "databases")
}
func (self *SDBInstanceManager) ListAccounts(queries map[string]string) (*responses.ListResult, error) {
id, _ := queries["instance_id"]
if len(id) == 0 {
return nil, fmt.Errorf("SDBInstanceManager.ListAccounts missing parameter instance_id")
}
delete(queries, "instance_id")
return self.ListInContextWithSpec(nil, fmt.Sprintf("%s/db_user/detail", id), queries, "users")
}
func (self *SDBInstanceManager) ListPrivileges(queries map[string]string) (*responses.ListResult, error) {
id, _ := queries["instance_id"]
if len(id) == 0 {
return nil, fmt.Errorf("SDBInstanceManager.ListPrivileges missing parameter instance_id")
}
delete(queries, "instance_id")
return self.ListInContextWithSpec(nil, fmt.Sprintf("%s/db_user/database", id), queries, "databases")
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SDBInstanceBackupManager struct {
SResourceManager
}
func NewDBInstanceBackupManager(cfg manager.IManagerConfig) *SDBInstanceBackupManager {
return &SDBInstanceBackupManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameRDS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v3",
Keyword: "backup",
KeywordPlural: "backups",
ResourceKeyword: "backups",
}}
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SDBInstanceFlavorManager struct {
SResourceManager
}
func NewDBInstanceFlavorManager(cfg manager.IManagerConfig) *SDBInstanceFlavorManager {
return &SDBInstanceFlavorManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameRDS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v3",
Keyword: "flavor",
KeywordPlural: "flavor",
ResourceKeyword: "flavors",
}}
}
@@ -1,43 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SDBInstanceJobManager struct {
SResourceManager
}
func NewDBInstanceJobManager(cfg manager.IManagerConfig) *SDBInstanceJobManager {
return &SDBInstanceJobManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameRDS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v3",
Keyword: "",
KeywordPlural: "",
ResourceKeyword: "jobs",
}}
}
func (self *SDBInstanceJobManager) Get(id string, querys map[string]string) (jsonutils.JSONObject, error) {
return self.GetInContextWithSpec(nil, "", "", map[string]string{"id": id}, "job")
}
@@ -1,76 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SDiskManager struct {
SResourceManager
}
func NewDiskManager(cfg manager.IManagerConfig) *SDiskManager {
return &SDiskManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameEVS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v2",
Keyword: "volume",
KeywordPlural: "volumes",
ResourceKeyword: "cloudvolumes",
}}
}
func (self *SDiskManager) List(querys map[string]string) (*responses.ListResult, error) {
return self.ListInContextWithSpec(nil, "detail", querys, self.KeywordPlural)
}
// https://support.huaweicloud.com/api-evs/evs_04_2003.html
func (self *SDiskManager) AsyncCreate(params jsonutils.JSONObject) (string, error) {
origin_version := self.version
self.version = "v2.1"
defer func() { self.version = origin_version }()
ret, err := self.CreateInContextWithSpec(nil, "", params, "")
if err != nil {
log.Debugf("AsyncCreate %s", err)
return "", err
}
log.Debugf("AsyncCreate result %s", ret.String())
// 按需机器
jobId, err := ret.GetString("job_id")
if err == nil {
return jobId, nil
}
// 包年包月机器
return ret.GetString("order_id")
}
// https://support.huaweicloud.com/api-evs/evs_04_2003.html
func (self *SDiskManager) GetDiskTypes() (*responses.ListResult, error) {
originKeyword := self.ResourceKeyword
self.ResourceKeyword = ""
defer func() { self.ResourceKeyword = originKeyword }()
return self.ListInContextWithSpec(self.ctx, "types", nil, "volume_types")
}
@@ -1,41 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SNatDRuleManager struct {
SResourceManager
}
func NewNatDManager(cfg manager.IManagerConfig) *SNatDRuleManager {
man := &SNatDRuleManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameNAT,
Region: cfg.GetRegionId(),
ProjectId: "",
version: "v2.0",
Keyword: "dnat_rule",
KeywordPlural: "dnat_rules",
ResourceKeyword: "dnat_rules",
}}
if len(cfg.GetProjectId()) > 0 {
man.requestHook = &sProjectHook{cfg.GetProjectId()}
}
return man
}
@@ -1,95 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SDomainManager struct {
SResourceManager
}
func NewDomainManager(cfg manager.IManagerConfig) *SDomainManager {
m := &SDomainManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3/auth",
Keyword: "domain",
KeywordPlural: "domains",
ResourceKeyword: "domains",
}}
m.SetDomainId(cfg.GetDomainId())
return m
}
func (manager *SDomainManager) DeleteRole(domainId string, groupId, roleId string) error {
if len(domainId) == 0 {
return fmt.Errorf("missing domainId")
}
if len(groupId) == 0 {
return fmt.Errorf("missing groupId")
}
if len(roleId) == 0 {
return fmt.Errorf("missing roleId")
}
manager.SetVersion("v3")
defer manager.SetVersion("v3/auth")
res := fmt.Sprintf("groups/%s/roles/%s", groupId, roleId)
_, err := manager.DeleteInContextWithSpec(nil, domainId, res, nil, nil, "")
if err != nil && errors.Cause(err) == cloudprovider.ErrNotFound {
return nil
}
return err
}
func (manager *SDomainManager) ListRoles(domainId string, groupId string) (*responses.ListResult, error) {
if len(domainId) == 0 {
return nil, fmt.Errorf("missing domainId")
}
if len(groupId) == 0 {
return nil, fmt.Errorf("missing groupId")
}
manager.SetVersion("v3")
defer manager.SetVersion("v3/auth")
res := fmt.Sprintf("%s/groups/%s/roles", domainId, groupId)
return manager.ListInContextWithSpec(nil, res, nil, "roles")
}
func (manager *SDomainManager) AddRole(domainId string, groupId, roleId string) error {
if len(domainId) == 0 {
return fmt.Errorf("missing domainId")
}
if len(groupId) == 0 {
return fmt.Errorf("missing groupId")
}
if len(roleId) == 0 {
return fmt.Errorf("missing roleId")
}
manager.SetVersion("v3")
defer manager.SetVersion("v3/auth")
res := fmt.Sprintf("groups/%s/roles/%s", groupId, roleId)
_, err := manager.UpdateInContextWithSpec(nil, domainId, res, nil, "")
return err
}
@@ -1,118 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SElasticcacheManager struct {
SResourceManager
}
type SDcsAvailableZoneManager struct {
SResourceManager
}
func NewElasticcacheManager(cfg manager.IManagerConfig) *SElasticcacheManager {
return &SElasticcacheManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameDCS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1.0",
Keyword: "",
KeywordPlural: "instances",
ResourceKeyword: "instances",
}}
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423035.html
func (self *SElasticcacheManager) ListBackups(queries map[string]string) (*responses.ListResult, error) {
var spec string
if id, _ := queries["instance_id"]; len(id) == 0 {
return nil, fmt.Errorf("SElasticcacheManager.ListBackups missing parameter instance_id")
} else {
spec = fmt.Sprintf("%s/backups", id)
}
delete(queries, "instance_id")
return self.ListInContextWithSpec(nil, spec, queries, "backup_record_response")
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423027.html
func (self *SElasticcacheManager) ListParameters(queries map[string]string) (*responses.ListResult, error) {
var spec string
if id, _ := queries["instance_id"]; len(id) == 0 {
return nil, fmt.Errorf("SElasticcacheManager.ListParameters missing parameter instance_id")
} else {
spec = fmt.Sprintf("%s/configs", id)
}
delete(queries, "instance_id")
return self.ListInContextWithSpec(nil, spec, queries, "redis_config")
}
func (self *SElasticcacheManager) Restart(instanceId string) (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewArray(jsonutils.NewString(instanceId)), "instances")
params.Add(jsonutils.NewString("restart"), "action")
return self.UpdateInContextWithSpec(nil, "", "status", params, "")
}
// 当前版本,只有DCS2.0实例支持清空数据功能,即flush操作。
func (self *SElasticcacheManager) Flush(instanceId string) (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewArray(jsonutils.NewString(instanceId)), "instances")
params.Add(jsonutils.NewString("flush"), "action")
return self.UpdateInContextWithSpec(nil, "", "status", params, "")
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423034.html
func (self *SElasticcacheManager) RestoreInstance(instanceId string, backupId string) (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewArray(jsonutils.NewString(backupId)), "backup_id")
return self.CreateInContextWithSpec(nil, fmt.Sprintf("%s/restores", instanceId), params, "")
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423024.html
func (self *SElasticcacheManager) ChangeInstanceSpec(instanceId string, specCode string, newCapacity int64) (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Set("new_capacity", jsonutils.NewInt(newCapacity))
params.Set("spec_code", jsonutils.NewString(specCode))
return self.CreateInContextWithSpec(nil, fmt.Sprintf("%s/extend", instanceId), params, "")
}
func NewDcsAvailableZoneManager(cfg manager.IManagerConfig) *SDcsAvailableZoneManager {
return &SDcsAvailableZoneManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameDCS,
Region: cfg.GetRegionId(),
ProjectId: "",
version: "v1.0",
Keyword: "available_zone",
KeywordPlural: "available_zones",
ResourceKeyword: "availableZones",
}}
}
@@ -1,39 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SEnterpriseProjectManager struct {
SResourceManager
}
func NewEnterpriseProjectManager(cfg manager.IManagerConfig) *SEnterpriseProjectManager {
m := &SEnterpriseProjectManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameEPS,
Region: "",
ProjectId: "",
version: "v1.0",
Keyword: "enterprise_project",
KeywordPlural: "enterprise_projects",
ResourceKeyword: "enterprise-projects",
}}
m.SetDomainId(cfg.GetDomainId())
return m
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SFlavorManager struct {
SResourceManager
}
func NewFlavorManager(cfg manager.IManagerConfig) *SFlavorManager {
return &SFlavorManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameECS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1",
Keyword: "flavor",
KeywordPlural: "flavors",
ResourceKeyword: "cloudservers/flavors", // 这个接口有点特殊,实际只用到了list一个方法。为了简便直接把cloudservers附上。
}}
}
@@ -1,39 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SGroupManager struct {
SResourceManager
}
func NewGroupManager(cfg manager.IManagerConfig) *SGroupManager {
m := &SGroupManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3",
Keyword: "group",
KeywordPlural: "groups",
ResourceKeyword: "groups",
}}
m.SetDomainId(cfg.GetDomainId())
return m
}
@@ -1,102 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests"
)
type SImageManager struct {
SResourceManager
}
type imageProject struct {
projectId string
}
// image创建接口若非默认project,需要在header中指定X-Project-ID。url中未携带project信息(与其他接口相比有一点特殊)
// 绕过了ResourceManager中的projectid。直接在发送json请求前注入X-Project-ID
func (self *imageProject) Process(request requests.IRequest) {
request.AddHeaderParam("X-Project-Id", self.projectId)
}
func NewImageManager(cfg manager.IManagerConfig) *SImageManager {
var requestHook imageProject
if len(cfg.GetProjectId()) > 0 {
requestHook = imageProject{projectId: cfg.GetProjectId()}
}
return &SImageManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager2(cfg, &requestHook),
ServiceName: ServiceNameIMS,
Region: cfg.GetRegionId(),
ProjectId: "",
version: "v2",
Keyword: "image",
KeywordPlural: "images",
ResourceKeyword: "cloudimages",
}}
}
//https://support.huaweicloud.com/api-ims/zh-cn_topic_0020091566.html
func (self *SImageManager) Get(id string, querys map[string]string) (jsonutils.JSONObject, error) {
if querys == nil {
querys = make(map[string]string, 0)
}
querys["id"] = id
// 这里默认使用private
// if t, exists := querys["__imagetype"]; !exists || len(t) == 0 {
// querys["__imagetype"] = "private"
// }
ret, err := self.ListInContext(nil, querys)
if err != nil {
return nil, err
}
if ret.Data == nil || len(ret.Data) == 0 {
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "image %s not found", id)
}
return ret.Data[0], nil
}
// https://support.huaweicloud.com/api-ims/zh-cn_topic_0020092108.html
// 删除image只能用这个manager
func NewOpenstackImageManager(cfg manager.IManagerConfig) *SImageManager {
var requestHook imageProject
if len(cfg.GetProjectId()) > 0 {
requestHook = imageProject{projectId: cfg.GetProjectId()}
}
return &SImageManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager2(cfg, &requestHook),
ServiceName: ServiceNameIMS,
Region: cfg.GetRegionId(),
ProjectId: "",
version: "v2",
Keyword: "image",
KeywordPlural: "images",
ResourceKeyword: "images",
}}
}
@@ -1,38 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SInterfaceManager struct {
SResourceManager
}
// 不建议使用
func NewInterfaceManager(cfg manager.IManagerConfig) *SInterfaceManager {
return &SInterfaceManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameECS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v2",
Keyword: "interfaceAttachment",
KeywordPlural: "interfaceAttachments",
ResourceKeyword: "os-interface",
}}
}
@@ -1,71 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SJobManager struct {
SResourceManager
}
func NewJobManager(cfg manager.IManagerConfig) *SJobManager {
return &SJobManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: "",
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1",
Keyword: "",
KeywordPlural: "",
ResourceKeyword: "jobs",
}}
}
func (self *SJobManager) Get(id string, querys map[string]string) (jsonutils.JSONObject, error) {
processedQuery, err := self.processQueryParam(querys)
if err != nil {
return nil, err
}
return self.GetInContext(nil, id, processedQuery)
}
func (self *SJobManager) List(querys map[string]string) (*responses.ListResult, error) {
processedQuery, err := self.processQueryParam(querys)
if err != nil {
return nil, err
}
return self.ListInContext(nil, processedQuery)
}
// 兼容查询不同ServiceName服务的Job做的特殊处理。
func (self *SJobManager) processQueryParam(querys map[string]string) (map[string]string, error) {
service_type, exists := querys["service_type"]
if !exists {
return querys, fmt.Errorf("must specific query parameter `service_type`. e.g. ecs|ims|iam")
}
self.ServiceName = ServiceNameType(service_type)
delete(querys, "service_type")
return querys, nil
}
@@ -1,39 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SAMLProviderMappingManager struct {
SResourceManager
}
func NewSAMLProviderMappingManager(cfg manager.IManagerConfig) *SAMLProviderMappingManager {
m := &SAMLProviderMappingManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3/OS-FEDERATION",
Keyword: "mapping",
KeywordPlural: "mappings",
ResourceKeyword: "mappings",
}}
m.SetDomainId(cfg.GetDomainId())
return m
}
@@ -1,50 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests"
)
type SNatGatewayManager struct {
SResourceManager
}
type sProjectHook struct {
projectId string
}
func (self *sProjectHook) Process(request requests.IRequest) {
request.AddHeaderParam("X-Project-Id", self.projectId)
}
func NewNatGatewayManager(cfg manager.IManagerConfig) *SNatGatewayManager {
man := &SNatGatewayManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameNAT,
Region: cfg.GetRegionId(),
ProjectId: "",
version: "v2.0",
Keyword: "nat_gateway",
KeywordPlural: "nat_gateways",
ResourceKeyword: "nat_gateways",
}}
if len(cfg.GetProjectId()) > 0 {
man.requestHook = &sProjectHook{cfg.GetProjectId()}
}
return man
}
@@ -1,104 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
// domian 客户账号ID https://support.huaweicloud.com/oce_faq/zh-cn_topic_0113714840.html
type SOrderManager struct {
orderCtx manager.IManagerContext
SResourceManager
}
type orderCtx struct {
domainId string
}
// {domain_id}/common/
// 这个manager非常特殊。url hardcode
func (self *orderCtx) GetPath() string {
return fmt.Sprintf("%s/common", self.domainId)
}
// 客户运营能力API的Endpoint为“bss.cn-north-1.myhuaweicloud.com”。该Endpoint为全局Endpoint,中国站所有区域均可使用。
// https://support.huaweicloud.com/api-oce/zh-cn_topic_0084961226.html
func NewOrderManager(cfg manager.IManagerConfig) *SOrderManager {
return &SOrderManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameBSS,
Region: "cn-north-1",
ProjectId: "",
version: "v1.0",
Keyword: "",
KeywordPlural: "",
ResourceKeyword: "order-mgr",
}}
}
func (self *SOrderManager) SetDomainId(domainId string) error {
if len(domainId) == 0 {
return fmt.Errorf("SetDomainId domain id should not be emtpy")
}
self.orderCtx = &orderCtx{domainId: domainId}
return nil
}
// 查询客户包周期资源列表 https://support.huaweicloud.com/api-oce/zh-cn_topic_0084961226.html
func (self *SOrderManager) List(querys map[string]string) (*responses.ListResult, error) {
return nil, fmt.Errorf("Not Suppport List Order")
}
// 查询订单的资源开通详情 https://support.huaweicloud.com/api-oce/api_order_00001.html
func (self *SOrderManager) Get(id string, querys map[string]string) (jsonutils.JSONObject, error) {
if self.orderCtx == nil {
return nil, fmt.Errorf("domainId is emtpy.Use SetDomainId method to set.")
}
// !!!特殊调用
return self.GetInContextWithSpec(self.orderCtx, "orders-resource", id, querys, "")
}
func (self *SOrderManager) PerformAction(action string, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
request := self.newRequest("POST", id, action, self.orderCtx)
request.SetContent([]byte(getContent(params)))
return self._do(request, "")
}
func (self *SOrderManager) GetPeriodResourceList(querys map[string]string) (*responses.ListResult, error) {
if self.orderCtx == nil {
return nil, fmt.Errorf("domainId is emtpy.Use SetDomainId method to set.")
}
return self.ListInContextWithSpec(self.orderCtx, "resources/detail", querys, "data")
}
// https://support.huaweicloud.com/api-bpconsole/zh-cn_topic_0082522029.html
func (self *SOrderManager) RenewPeriodResource(params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if self.orderCtx == nil {
return nil, fmt.Errorf("domainId is emtpy.Use SetDomainId method to set.")
}
return self.CreateInContextWithSpec(self.orderCtx, "resources/renew", params, "order_ids")
}
@@ -1,53 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests"
)
type SPortManager struct {
SResourceManager
}
type portProject struct {
projectId string
}
// port接口查询时若非默认project,需要在header中指定X-Project-ID。url中未携带project信息(与其他接口相比有一点特殊)
// 绕过了ResourceManager中的projectid。直接在发送json请求前注入X-Project-ID
func (self *portProject) Process(request requests.IRequest) {
request.AddHeaderParam("X-Project-Id", self.projectId)
}
func NewPortManager(cfg manager.IManagerConfig) *SPortManager {
var requestHook portProject
if len(cfg.GetProjectId()) > 0 {
requestHook = portProject{projectId: cfg.GetProjectId()}
}
return &SPortManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager2(cfg, &requestHook),
ServiceName: ServiceNameVPC,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1",
Keyword: "port",
KeywordPlural: "ports",
ResourceKeyword: "ports",
}}
}
@@ -1,89 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SProjectManager struct {
SResourceManager
}
func NewProjectManager(cfg manager.IManagerConfig) *SProjectManager {
m := &SProjectManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3",
Keyword: "project",
KeywordPlural: "projects",
ResourceKeyword: "projects",
}}
m.SetDomainId(cfg.GetDomainId())
return m
}
func (manager *SProjectManager) ListRoles(projectId, groupId string) (*responses.ListResult, error) {
if len(projectId) == 0 {
return nil, fmt.Errorf("missing projectId")
}
if len(groupId) == 0 {
return nil, fmt.Errorf("missing groupId")
}
res := fmt.Sprintf("%s/groups/%s/roles", projectId, groupId)
return manager.ListInContextWithSpec(nil, res, nil, "roles")
}
func (manager *SProjectManager) DeleteProjectRole(projectId, groupId, roleId string) error {
if len(projectId) == 0 {
return fmt.Errorf("missing projectId")
}
if len(groupId) == 0 {
return fmt.Errorf("missing groupId")
}
if len(roleId) == 0 {
return fmt.Errorf("missing roleId")
}
res := fmt.Sprintf("groups/%s/roles/%s", groupId, roleId)
_, err := manager.DeleteInContextWithSpec(nil, projectId, res, nil, nil, "")
if err != nil && errors.Cause(err) == cloudprovider.ErrNotFound {
return nil
}
return err
}
func (manager *SProjectManager) AddProjectRole(projectId string, groupId, roleId string) error {
if len(projectId) == 0 {
return fmt.Errorf("missing projectId")
}
if len(groupId) == 0 {
return fmt.Errorf("missing groupId")
}
if len(roleId) == 0 {
return fmt.Errorf("missing roleId")
}
res := fmt.Sprintf("groups/%s/roles/%s", groupId, roleId)
_, err := manager.UpdateInContextWithSpec(nil, projectId, res, nil, "")
return err
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SRegionManager struct {
SResourceManager
}
func NewRegionManager(cfg manager.IManagerConfig) *SRegionManager {
return &SRegionManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3",
Keyword: "region",
KeywordPlural: "regions",
ResourceKeyword: "regions",
}}
}
@@ -1,39 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SRoleManager struct {
SResourceManager
}
func NewRoleManager(cfg manager.IManagerConfig) *SRoleManager {
m := &SRoleManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3",
Keyword: "role",
KeywordPlural: "roles",
ResourceKeyword: "roles",
}}
m.SetDomainId(cfg.GetDomainId())
return m
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SRouteTableManager struct {
SResourceManager
}
func NewRouteTableManager(cfg manager.IManagerConfig) *SRouteTableManager {
return &SRouteTableManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameVPC,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1",
Keyword: "routetable",
KeywordPlural: "routetables",
ResourceKeyword: "routetables",
}}
}
@@ -1,39 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SAMLProviderManager struct {
SResourceManager
}
func NewSAMLProviderManager(cfg manager.IManagerConfig) *SAMLProviderManager {
m := &SAMLProviderManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3/OS-FEDERATION",
Keyword: "identity_provider",
KeywordPlural: "identity_providers",
ResourceKeyword: "identity_providers",
}}
m.SetDomainId(cfg.GetDomainId())
return m
}
@@ -1,124 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"strconv"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SServerManager struct {
SResourceManager
}
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212668.html
// v.1.1 新增支持创建包年/包月的弹性云服务器。!!但是不支持查询等调用 https://support.huaweicloud.com/api-ecs/zh-cn_topic_0093055772.html
func NewServerManager(cfg manager.IManagerConfig) *SServerManager {
return &SServerManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameECS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1",
Keyword: "server",
KeywordPlural: "servers",
ResourceKeyword: "cloudservers",
}}
}
func (self *SServerManager) List(querys map[string]string) (*responses.ListResult, error) {
if offset, exists := querys["offset"]; !exists {
// 华为云分页参数各式各样。cloudserver offset从1开始。部分其他接口从0开始。
// 另外部分接口使用pager分页 或者 maker分页
querys["offset"] = "1"
} else {
n, err := strconv.Atoi(offset)
if err != nil {
return nil, fmt.Errorf("offset is invalid: %s", offset)
}
querys["offset"] = strconv.Itoa(n + 1)
}
return self.ListInContextWithSpec(nil, "detail", querys, self.KeywordPlural)
}
/*
返回job id 或者 order id
https://support.huaweicloud.com/api-ecs/zh-cn_topic_0093055772.html
创建按需的弹性云服务 > job_id 任务ID 返回数据uuid举例"70a599e0-31e7-49b7-b260-868f441e862b"
包年包月机器 --> order_id (返回数据举例 "CS1711152257C60TL")
*/
func (self *SServerManager) AsyncCreate(params jsonutils.JSONObject) (string, error) {
origin_version := self.version
self.version = "v1.1"
defer func() { self.version = origin_version }()
ret, err := self.CreateInContextWithSpec(nil, "", params, "")
if err != nil {
log.Debugf("AsyncCreate %s", err)
return "", err
}
log.Debugf("AsyncCreate result %s", ret.String())
// 按需机器
jobId, err := ret.GetString("job_id")
if err == nil {
return jobId, nil
}
// 包年包月机器
return ret.GetString("order_id")
}
func (self *SServerManager) Create(params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return nil, fmt.Errorf("not supported.please use AsyncCreate")
}
// 不推荐使用这个manager
func NewNovaServerManager(cfg manager.IManagerConfig) *SServerManager {
return &SServerManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameECS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v2.1",
Keyword: "server",
KeywordPlural: "servers",
ResourceKeyword: "servers",
}}
}
// 重装弹性云服务器操作系统(安装Cloud-init,请用这个manager
func NewServerV2Manager(cfg manager.IManagerConfig) *SServerManager {
return &SServerManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameECS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v2",
Keyword: "server",
KeywordPlural: "servers",
ResourceKeyword: "cloudservers",
}}
}
@@ -1,48 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SfsTurboManager struct {
SResourceManager
}
func NewSfsTurboManager(cfg manager.IManagerConfig) *SfsTurboManager {
return &SfsTurboManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameSFSTurbo,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1",
Keyword: "",
KeywordPlural: "shares",
ResourceKeyword: "sfs-turbo/shares",
}}
}
func (self *SfsTurboManager) List(querys map[string]string) (*responses.ListResult, error) {
return self.ListInContextWithSpec(nil, "detail", querys, self.KeywordPlural)
}
func (self *SfsTurboManager) Create(params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return self.CreateInContextWithSpec(self.ctx, "", params, "")
}
@@ -1,59 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SSnapshotManager struct {
SResourceManager
}
func NewSnapshotManager(cfg manager.IManagerConfig) *SSnapshotManager {
return &SSnapshotManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameEVS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v2",
Keyword: "snapshot",
KeywordPlural: "snapshots",
ResourceKeyword: "snapshots",
}}
}
func (self *SSnapshotManager) List(querys map[string]string) (*responses.ListResult, error) {
return self.ListInContextWithSpec(nil, "detail", querys, self.KeywordPlural)
}
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0051408629.html
// 回滚快照只能用这个manger。其他情况请不要使用
// 另外,香港-亚太还支持另外一个接口。https://support.huaweicloud.com/api-evs/zh-cn_topic_0142374138.html
func NewOsSnapshotManager(cfg manager.IManagerConfig) *SSnapshotManager {
return &SSnapshotManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameEVS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v2",
Keyword: "snapshot",
KeywordPlural: "snapshots",
ResourceKeyword: "os-vendor-snapshots",
}}
}
@@ -1,41 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SNatSRuleManager struct {
SResourceManager
}
func NewNatSManager(cfg manager.IManagerConfig) *SNatSRuleManager {
man := &SNatSRuleManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameNAT,
Region: cfg.GetRegionId(),
ProjectId: "",
version: "v2.0",
Keyword: "snat_rule",
KeywordPlural: "snat_rules",
ResourceKeyword: "snat_rules",
}}
if len(cfg.GetProjectId()) > 0 {
man.requestHook = &sProjectHook{cfg.GetProjectId()}
}
return man
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SSubnetManager struct {
SResourceManager
}
func NewSubnetManager(cfg manager.IManagerConfig) *SSubnetManager {
return &SSubnetManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameVPC,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1",
Keyword: "subnet",
KeywordPlural: "subnets",
ResourceKeyword: "subnets",
}}
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type STraceManager struct {
SResourceManager
}
func NewTraceManager(cfg manager.IManagerConfig) *STraceManager {
return &STraceManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameCTS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v2.0",
Keyword: "trace",
KeywordPlural: "traces",
ResourceKeyword: "system/trace",
}}
}
@@ -1,69 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
)
type SUserManager struct {
SResourceManager
}
func NewUserManager(cfg manager.IManagerConfig) *SUserManager {
user := &SUserManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameIAM,
Region: "",
ProjectId: "",
version: "v3.0/OS-USER",
Keyword: "user",
KeywordPlural: "users",
ResourceKeyword: "users",
}}
user.SetDomainId(cfg.GetDomainId())
return user
}
func (self *SUserManager) List(querys map[string]string) (*responses.ListResult, error) {
self.SetVersion("v3")
return self.SResourceManager.List(querys)
}
func (self *SUserManager) Delete(id string) (jsonutils.JSONObject, error) {
self.SetVersion("v3")
return self.SResourceManager.Delete(id, nil)
}
func (self *SUserManager) ResetPassword(id, password string) error {
params := map[string]interface{}{
"user": map[string]string{
"password": password,
},
}
_, err := self.SResourceManager.Update(id, jsonutils.Marshal(params))
return err
}
func (self *SUserManager) ListGroups(userId string) (*responses.ListResult, error) {
self.SetVersion("v3")
return self.SResourceManager.ListInContextWithSpec(nil, fmt.Sprintf("%s/groups", userId), nil, "groups")
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SVpcPeeringManager struct {
SResourceManager
}
func NewVpcPeeringManager(cfg manager.IManagerConfig) *SVpcPeeringManager {
return &SVpcPeeringManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameVPC,
Region: cfg.GetRegionId(),
ProjectId: "",
version: "v2.0",
Keyword: "peering",
KeywordPlural: "peerings",
ResourceKeyword: "vpc/peerings",
}}
}
@@ -1,38 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SVpcRouteManager struct {
SResourceManager
}
func NewVpcRouteManager(cfg manager.IManagerConfig) *SVpcRouteManager {
return &SVpcRouteManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameVPC,
Region: cfg.GetRegionId(),
// the url should not include the field "projectid" in huawei cloud api v2.0
ProjectId: "",
version: "v2.0",
Keyword: "route",
KeywordPlural: "routes",
ResourceKeyword: "vpc/routes",
}}
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SVpcManager struct {
SResourceManager
}
func NewVpcManager(cfg manager.IManagerConfig) *SVpcManager {
return &SVpcManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameVPC,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v1",
Keyword: "vpc",
KeywordPlural: "vpcs",
ResourceKeyword: "vpcs",
}}
}
@@ -1,37 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package modules
import (
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/manager"
)
type SZoneManager struct {
SResourceManager
}
func NewZoneManager(cfg manager.IManagerConfig) *SZoneManager {
return &SZoneManager{SResourceManager: SResourceManager{
SBaseManager: NewBaseManager(cfg),
ServiceName: ServiceNameECS,
Region: cfg.GetRegionId(),
ProjectId: cfg.GetProjectId(),
version: "v2",
Keyword: "availabilityZoneInfo",
KeywordPlural: "availabilityZoneInfo",
ResourceKeyword: "os-availability-zone",
}}
}
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package requests // import "yunion.io/x/cloudmux/pkg/multicloud/huawei/client/requests"
@@ -1,276 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package requests
import (
"fmt"
"io"
"net/url"
"sort"
"strings"
)
type IRequest interface {
GetScheme() string
GetMethod() string
GetDomain() string
GetPort() string
GetRegionId() string
GetProjectId() string
GetHost() string
GetURI() string
GetHeaders() map[string]string
GetQueryParams() map[string]string
GetFormParams() map[string]string
GetContent() []byte
GetBodyReader() io.Reader
GetProduct() string
GetVersion() string
SetStringToSign(stringToSign string)
GetStringToSign() string
SetDomain(domain string)
SetContent(content []byte)
SetScheme(scheme string)
BuildUrl() string
BuildQueries() string
AddHeaderParam(key, value string)
AddQueryParam(key, value string)
AddFormParam(key, value string)
}
type SRequest struct {
Scheme string // HTTP、HTTPS
Method string // GET、PUT、DELETE、POST、PATCH
Domain string // myhuaweicloud.com
Port string // 80
RegionId string // cn-north-1
product string // 弹性云服务 ECS : ecs
version string // API版本: v2
projectId string // 项目ID: 43cbe5e77aaf4665bbb962062dc1fc9d.可以为空。
resourcePath string // /users/{user_id}/groups
QueryParams map[string]string
Headers map[string]string
FormParams map[string]string
Content []byte
queries string
stringToSign string
}
func (self *SRequest) GetProjectId() string {
return self.projectId
}
func (self *SRequest) GetScheme() string {
return self.Scheme
}
func (self *SRequest) GetMethod() string {
return self.Method
}
func (self *SRequest) GetDomain() string {
return self.Domain
}
func (self *SRequest) GetPort() string {
return self.Port
}
func (self *SRequest) GetRegionId() string {
return self.RegionId
}
func (self *SRequest) GetHeaders() map[string]string {
return self.Headers
}
func (self *SRequest) GetQueryParams() map[string]string {
return self.QueryParams
}
func (self *SRequest) GetFormParams() map[string]string {
return self.FormParams
}
func (self *SRequest) GetContent() []byte {
return self.Content
}
func (self *SRequest) GetBodyReader() io.Reader {
if self.FormParams != nil && len(self.FormParams) > 0 {
formData := GetUrlFormedMap(self.FormParams)
return strings.NewReader(formData)
} else {
return strings.NewReader(string(self.Content))
}
}
func (self *SRequest) GetProduct() string {
return self.product
}
func (self *SRequest) GetVersion() string {
return self.version
}
func (self *SRequest) SetStringToSign(stringToSign string) {
self.stringToSign = stringToSign
}
func (self *SRequest) GetStringToSign() string {
return self.stringToSign
}
func (self *SRequest) SetDomain(domain string) {
self.Domain = domain
}
func (self *SRequest) SetContent(content []byte) {
self.Content = content
}
func (self *SRequest) SetScheme(scheme string) {
self.Scheme = scheme
}
func (self *SRequest) BuildUrl() string {
scheme := strings.ToLower(self.Scheme)
baseUrl := fmt.Sprintf("%s://%s", scheme, self.GetHost())
queries := self.BuildQueries()
if len(queries) > 0 {
return baseUrl + self.GetURI() + "?" + queries
} else {
return baseUrl + self.GetURI()
}
}
func (self *SRequest) GetHost() string {
scheme := strings.ToLower(self.Scheme)
host := self.getEndpoint()
if len(self.Port) > 0 {
if (scheme == "http" && self.Port == "80") || (scheme == "https" && self.Port == "443") {
host = fmt.Sprintf("%s:%s", host, self.Port)
}
}
return host
}
func (self *SRequest) GetURI() string {
// URI
uri := ""
for _, m := range []string{self.version, self.projectId} {
if len(m) > 0 {
uri += fmt.Sprintf("/%s", m)
}
}
if len(self.resourcePath) > 0 {
s := ""
if !strings.HasPrefix(self.resourcePath, "/") {
s = "/"
}
if strings.HasSuffix(self.resourcePath, "/") {
strings.TrimSuffix(s, "/")
}
uri = uri + s + self.resourcePath
}
return uri
}
func (self *SRequest) getEndpoint() string {
// ecs.cn-north-1.myhuaweicloud.com
items := []string{}
for _, item := range []string{self.product, self.RegionId, self.Domain} {
if len(item) > 0 {
items = append(items, item)
}
}
return strings.Join(items, ".")
}
func (self *SRequest) BuildQueries() string {
self.queries = GetUrlFormedMap(self.QueryParams)
return self.queries
}
func (self *SRequest) AddHeaderParam(key, value string) {
self.Headers[key] = value
}
func (self *SRequest) AddQueryParam(key, value string) {
self.QueryParams[key] = value
}
func (self *SRequest) AddFormParam(key, value string) {
self.FormParams[key] = value
}
func GetUrlFormedMap(source map[string]string) string {
// 按key排序后编译
keys := make([]string, 0)
for k := range source {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return strings.ToLower(keys[i]) < strings.ToLower(keys[j])
})
urlEncoder := url.Values{}
for _, k := range keys {
urlEncoder.Add(k, source[k])
}
return urlEncoder.Encode()
}
func defaultRequest() (request *SRequest) {
request = &SRequest{
Scheme: "HTTPS",
Method: "GET",
QueryParams: make(map[string]string),
Headers: map[string]string{},
FormParams: make(map[string]string),
}
return
}
func NewResourceRequest(domain, method, product, version, region, project, resourcePath string) *SRequest {
return &SRequest{
Scheme: "HTTPS",
Method: method,
Domain: domain,
product: product,
RegionId: region,
version: version,
projectId: project,
resourcePath: resourcePath,
QueryParams: make(map[string]string),
Headers: map[string]string{},
FormParams: make(map[string]string),
}
}
@@ -1,15 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package responses // import "yunion.io/x/cloudmux/pkg/multicloud/huawei/client/responses"
@@ -1,77 +0,0 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package responses
import (
"regexp"
"strings"
"yunion.io/x/jsonutils"
)
type ListResult struct {
Data []jsonutils.JSONObject
Total int
Limit int
Offset int
NextLink string
}
func ListResult2JSONWithKey(result *ListResult, key string) jsonutils.JSONObject {
obj := jsonutils.NewDict()
if result.Total > 0 {
obj.Add(jsonutils.NewInt(int64(result.Total)), "total")
}
if result.Limit > 0 {
obj.Add(jsonutils.NewInt(int64(result.Limit)), "limit")
}
if result.Offset > 0 {
obj.Add(jsonutils.NewInt(int64(result.Offset)), "offset")
}
arr := jsonutils.NewArray(result.Data...)
obj.Add(arr, key)
return obj
}
func ListResult2JSON(result *ListResult) jsonutils.JSONObject {
return ListResult2JSONWithKey(result, "data")
}
func JSON2ListResult(result jsonutils.JSONObject) *ListResult {
total, _ := result.Int("total")
limit, _ := result.Int("limit")
offset, _ := result.Int("offset")
data, _ := result.GetArray("data")
return &ListResult{Data: data, Total: int(total), Limit: int(limit), Offset: int(offset)}
}
// 将key中的冒号替换成
func TransColonToDot(obj jsonutils.JSONObject) (jsonutils.JSONObject, error) {
re, _ := regexp.Compile("[a-zA-Z0-9](:+)[^\"]+\"\\s*:\\s*")
if obj == nil {
return obj, nil
}
newStr := re.ReplaceAllStringFunc(obj.String(), func(s string) string {
count := strings.Count(s, ":")
if count > 1 {
return strings.Replace(s, ":", ".", count-1)
}
return s
})
return jsonutils.ParseString(newStr)
}
+111 -102
View File
@@ -16,9 +16,9 @@ package huawei
import (
"fmt"
"net/url"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
@@ -134,24 +134,24 @@ func (group *SCloudgroup) GetICloudusers() ([]cloudprovider.IClouduser, error) {
return ret, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneListGroups
func (self *SHuaweiClient) GetGroups(domainId, name string) ([]SCloudgroup, error) {
params := map[string]string{}
query := url.Values{}
if len(domainId) > 0 {
params["domain_id"] = self.ownerId
query.Set("domain_id", self.ownerId)
}
if len(name) > 0 {
params["name"] = name
}
client, err := self.newGeneralAPIClient()
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
query.Set("name", name)
}
groups := []SCloudgroup{}
err = doListAllWithNextLink(client.Groups.List, params, &groups)
resp, err := self.list(SERVICE_IAM_V3, "", "groups", query)
if err != nil {
return nil, errors.Wrap(err, "doListAllWithOffset")
return nil, err
}
err = resp.Unmarshal(&groups, "groups")
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
return groups, nil
}
@@ -171,59 +171,53 @@ func (self *SHuaweiClient) GetICloudgroups() ([]cloudprovider.ICloudgroup, error
return ret, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneListUsersForGroupByAdmin
func (self *SHuaweiClient) GetGroupUsers(groupId string) ([]SClouduser, error) {
client, err := self.newGeneralAPIClient()
resp, err := self.list(SERVICE_IAM_V3, "", fmt.Sprintf("groups/%s/users", groupId), nil)
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
}
resp, err := client.Groups.ListInContextWithSpec(nil, fmt.Sprintf("%s/users", groupId), nil, "users")
if err != nil {
return nil, errors.Wrap(err, "")
return nil, errors.Wrapf(err, "list group users")
}
users := []SClouduser{}
err = jsonutils.Update(&users, resp.Data)
err = resp.Unmarshal(&users, "users")
if err != nil {
return nil, errors.Wrap(err, "jsonutils.Update")
return nil, errors.Wrap(err, "Unmarshal")
}
return users, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneCheckDomainPermissionForGroup
func (self *SHuaweiClient) GetGroupRoles(groupId string) ([]SRole, error) {
client, err := self.newGeneralAPIClient()
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
}
resp, err := client.Domains.ListRoles(self.ownerId, groupId)
res := fmt.Sprintf("domains/%s/groups/%s/roles", self.ownerId, groupId)
resp, err := self.list(SERVICE_IAM_V3, "", res, nil)
if err != nil {
return nil, errors.Wrap(err, "ListRoles")
}
roles := []SRole{}
err = jsonutils.Update(&roles, resp.Data)
err = resp.Unmarshal(&roles, "roles")
if err != nil {
return nil, errors.Wrap(err, "jsonutils.Update")
return nil, errors.Wrap(err, "Unmarshal")
}
return roles, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneCreateGroup
func (self *SHuaweiClient) CreateGroup(name, desc string) (*SCloudgroup, error) {
params := map[string]string{
params := map[string]interface{}{
"name": name,
}
if len(desc) > 0 {
params["description"] = desc
}
client, err := self.newGeneralAPIClient()
resp, err := self.post(SERVICE_IAM_V3, "", "groups", map[string]interface{}{"group": params})
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
return nil, err
}
group := SCloudgroup{client: self}
err = DoCreate(client.Groups.Create, jsonutils.Marshal(map[string]interface{}{"group": params}), &group)
group := &SCloudgroup{client: self}
err = resp.Unmarshal(group, "group")
if err != nil {
return nil, errors.Wrap(err, "DoCreate")
return nil, errors.Wrapf(err, "Unmarshal")
}
return &group, nil
return group, nil
}
func (self *SHuaweiClient) CreateICloudgroup(name, desc string) (cloudprovider.ICloudgroup, error) {
@@ -234,12 +228,10 @@ func (self *SHuaweiClient) CreateICloudgroup(name, desc string) (cloudprovider.I
return group, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneDeleteGroup
func (self *SHuaweiClient) DeleteGroup(id string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
return DoDeleteWithSpec(client.Groups.DeleteInContextWithSpec, nil, id, "", nil, nil)
_, err := self.delete(SERVICE_IAM_V3, "", "groups/"+id)
return err
}
func (self *SHuaweiClient) GetICloudgroupByName(name string) (cloudprovider.ICloudgroup, error) {
@@ -257,41 +249,46 @@ func (self *SHuaweiClient) GetICloudgroupByName(name string) (cloudprovider.IClo
return &groups[0], nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneAddUserToGroup
func (self *SHuaweiClient) AddUserToGroup(groupId, userId string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
_, err = client.Groups.UpdateInContextWithSpec(nil, groupId, fmt.Sprintf("users/%s", userId), nil, "")
_, err := self.put(SERVICE_IAM_V3, "", fmt.Sprintf("groups/%s/users/%s", groupId, userId), nil)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneRemoveUserFromGroup
func (self *SHuaweiClient) RemoveUserFromGroup(groupId, userId string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
_, err = client.Groups.DeleteInContextWithSpec(nil, groupId, fmt.Sprintf("users/%s", userId), nil, nil, "")
_, err := self.delete(SERVICE_IAM_V3, "", fmt.Sprintf("groups/%s/users/%s", groupId, userId))
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=ListCustomPolicies
func (self *SHuaweiClient) GetCustomRoles() ([]SRole, error) {
params := map[string]string{}
client, err := self.newGeneralAPIClient()
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
query := url.Values{}
query.Set("per_page", "300")
page := 1
query.Set("page", fmt.Sprintf("%d", page))
ret := []SRole{}
for {
resp, err := self.list(SERVICE_IAM, "", "OS-ROLE/roles", query)
if err != nil {
return nil, err
}
part := struct {
Roles []SRole
TotalNumber int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.Roles...)
if len(ret) >= part.TotalNumber || len(part.Roles) == 0 {
break
}
page++
query.Set("page", fmt.Sprintf("%d", page))
}
client.Roles.SetVersion("v3.0/OS-ROLE")
defer client.Roles.SetVersion("v3.0")
roles := []SRole{}
err = doListAllWithNextLink(client.Roles.List, params, &roles)
if err != nil {
return nil, errors.Wrap(err, "doListAllWithOffset")
}
return roles, nil
return ret, nil
}
func (self *SHuaweiClient) GetCustomRole(name string) (*SRole, error) {
@@ -321,23 +318,19 @@ func (self *SHuaweiClient) GetRole(name string) (*SRole, error) {
}
func (self *SHuaweiClient) DetachGroupRole(groupId, roleId string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
role, err := self.GetRole(roleId)
if err != nil {
return errors.Wrapf(err, "GetRole(%s)", roleId)
}
if role.Type == "AX" || role.Type == "AA" {
err = client.Domains.AddRole(self.ownerId, groupId, role.Id)
err := self.KeystoneRemoveDomainPermissionFromGroup(self.ownerId, groupId, roleId)
if err != nil {
return errors.Wrapf(err, "AddRole")
return errors.Wrapf(err, "remove domain role")
}
if strings.Contains(strings.ToLower(role.Policy.String()), "obs") {
err = client.Projects.AddProjectRole(self.GetMosProjectId(), groupId, role.Id)
err := self.KeystoneRemoveProjectPermissionFromGroup(self.GetMosProjectId(), groupId, role.Id)
if err != nil {
return errors.Wrapf(err, "AddProjectRole")
return errors.Wrapf(err, "remove project role ")
}
}
}
@@ -347,33 +340,57 @@ func (self *SHuaweiClient) DetachGroupRole(groupId, roleId string) error {
return errors.Wrapf(err, "GetProjects")
}
for _, project := range projects {
err = client.Projects.AddProjectRole(project.ID, groupId, role.Id)
err := self.KeystoneRemoveProjectPermissionFromGroup(project.ID, groupId, role.Id)
if err != nil {
return errors.Wrapf(err, "AddProjectRole")
return errors.Wrapf(err, "remove project role ")
}
}
}
return nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneRemoveProjectPermissionFromGroup
func (self *SHuaweiClient) KeystoneRemoveProjectPermissionFromGroup(projectId, groupId, roleId string) error {
res := fmt.Sprintf("projects/%s/groups/%s/roles/%s", projectId, groupId, roleId)
_, err := self.delete(SERVICE_IAM_V3, "", res)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneAssociateGroupWithProjectPermission
func (self *SHuaweiClient) KeystoneAssociateGroupWithProjectPermission(projectId, groupId, roleId string) error {
res := fmt.Sprintf("projects/%s/groups/%s/roles/%s", self.GetMosProjectId(), groupId, roleId)
_, err := self.put(SERVICE_IAM_V3, "", res, nil)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneAssociateGroupWithDomainPermission
func (self *SHuaweiClient) KeystoneAssociateGroupWithDomainPermission(domainId, groupId, roleId string) error {
res := fmt.Sprintf("domains/%s/groups/%s/roles/%s", domainId, groupId, roleId)
_, err := self.put(SERVICE_IAM_V3, "", res, nil)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneRemoveDomainPermissionFromGroup
func (self *SHuaweiClient) KeystoneRemoveDomainPermissionFromGroup(domainId, groupId, roleId string) error {
res := fmt.Sprintf("domains/%s/groups/%s/roles/%s", domainId, groupId, roleId)
_, err := self.delete(SERVICE_IAM_V3, "", res)
return err
}
func (self *SHuaweiClient) AttachGroupRole(groupId, roleId string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
role, err := self.GetRole(roleId)
if err != nil {
return errors.Wrapf(err, "GetRole(%s)", roleId)
}
if role.Type == "AX" || role.Type == "AA" {
err = client.Domains.AddRole(self.ownerId, groupId, role.Id)
err := self.KeystoneAssociateGroupWithDomainPermission(self.ownerId, groupId, roleId)
if err != nil {
return errors.Wrapf(err, "AddRole")
}
if strings.Contains(strings.ToLower(role.Policy.String()), "obs") {
err = client.Projects.AddProjectRole(self.GetMosProjectId(), groupId, role.Id)
err := self.KeystoneAssociateGroupWithProjectPermission(self.GetMosProjectId(), groupId, role.Id)
if err != nil {
return errors.Wrapf(err, "AddProjectRole")
return errors.Wrapf(err, "add project role ")
}
}
}
@@ -383,9 +400,9 @@ func (self *SHuaweiClient) AttachGroupRole(groupId, roleId string) error {
return errors.Wrapf(err, "GetProjects")
}
for _, project := range projects {
err = client.Projects.AddProjectRole(project.ID, groupId, role.Id)
err := self.KeystoneAssociateGroupWithProjectPermission(project.ID, groupId, role.Id)
if err != nil {
return errors.Wrapf(err, "AddProjectRole")
return errors.Wrapf(err, "add project role ")
}
}
}
@@ -393,23 +410,19 @@ func (self *SHuaweiClient) AttachGroupRole(groupId, roleId string) error {
}
func (self *SHuaweiClient) AttachGroupCustomRole(groupId, roleId string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
role, err := self.GetCustomRole(roleId)
if err != nil {
return errors.Wrapf(err, "GetRole(%s)", roleId)
}
if role.Type == "AX" || role.Type == "AA" {
err = client.Domains.AddRole(self.ownerId, groupId, role.Id)
err := self.KeystoneAssociateGroupWithDomainPermission(self.ownerId, groupId, roleId)
if err != nil {
return errors.Wrapf(err, "AddRole")
}
if strings.Contains(strings.ToLower(role.Policy.String()), "obs") {
err = client.Projects.AddProjectRole(self.GetMosProjectId(), groupId, role.Id)
err = self.KeystoneAssociateGroupWithProjectPermission(self.GetMosProjectId(), groupId, role.Id)
if err != nil {
return errors.Wrapf(err, "AddProjectRole")
return errors.Wrapf(err, "add project role ")
}
}
}
@@ -419,9 +432,9 @@ func (self *SHuaweiClient) AttachGroupCustomRole(groupId, roleId string) error {
return errors.Wrapf(err, "GetProjects")
}
for _, project := range projects {
err = client.Projects.AddProjectRole(project.ID, groupId, role.Id)
err := self.KeystoneAssociateGroupWithProjectPermission(project.ID, groupId, role.Id)
if err != nil {
return errors.Wrapf(err, "AddProjectRole")
return errors.Wrapf(err, "add project role ")
}
}
}
@@ -429,23 +442,19 @@ func (self *SHuaweiClient) AttachGroupCustomRole(groupId, roleId string) error {
}
func (self *SHuaweiClient) DetachGroupCustomRole(groupId, roleId string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
role, err := self.GetCustomRole(roleId)
if err != nil {
return errors.Wrapf(err, "GetCustomRole(%s)", roleId)
}
if role.Type == "AX" || role.Type == "AA" {
err = client.Domains.DeleteRole(self.ownerId, groupId, role.Id)
err := self.KeystoneRemoveDomainPermissionFromGroup(self.ownerId, groupId, roleId)
if err != nil {
return errors.Wrapf(err, "DeleteRole")
}
if strings.Contains(strings.ToLower(role.Policy.String()), "obs") {
err = client.Projects.DeleteProjectRole(self.GetMosProjectId(), groupId, role.Id)
err := self.KeystoneRemoveProjectPermissionFromGroup(self.GetMosProjectId(), groupId, role.Id)
if err != nil {
return errors.Wrapf(err, "DeleteProjectRole")
return errors.Wrapf(err, "remove project role ")
}
}
}
@@ -455,9 +464,9 @@ func (self *SHuaweiClient) DetachGroupCustomRole(groupId, roleId string) error {
return errors.Wrapf(err, "GetProjects")
}
for _, project := range projects {
err = client.Projects.DeleteProjectRole(project.ID, groupId, role.Id)
err := self.KeystoneRemoveProjectPermissionFromGroup(project.ID, groupId, role.Id)
if err != nil {
return errors.Wrapf(err, "DeleteProjectRole")
return errors.Wrapf(err, "remove project role ")
}
}
}
+43 -40
View File
@@ -15,14 +15,14 @@
package huawei
import (
"fmt"
"net/url"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules"
)
type SLink struct {
@@ -111,45 +111,40 @@ func (user *SClouduser) ResetPassword(password string) error {
return user.client.ResetClouduserPassword(user.Id, password)
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneDeleteUser
func (self *SHuaweiClient) DeleteClouduser(id string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
_, err = client.Users.Delete(id)
_, err := self.delete(SERVICE_IAM_V3, "", "users/"+id)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneListGroupsForUser
func (self *SHuaweiClient) ListUserGroups(userId string) ([]SCloudgroup, error) {
client, err := self.newGeneralAPIClient()
resp, err := self.list(SERVICE_IAM_V3, "", fmt.Sprintf("users/%s/groups", userId), nil)
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
}
result, err := client.Users.ListGroups(userId)
if err != nil {
return nil, errors.Wrap(err, "Users.ListGroups")
return nil, err
}
groups := []SCloudgroup{}
err = jsonutils.Update(&groups, result.Data)
err = resp.Unmarshal(&groups, "groups")
if err != nil {
return nil, errors.Wrap(err, "jsonutils.Update")
return nil, err
}
return groups, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneListUsers
func (self *SHuaweiClient) GetCloudusers(name string) ([]SClouduser, error) {
params := map[string]string{}
params := url.Values{}
if len(name) > 0 {
params["name"] = name
params.Set("name", name)
}
resp, err := self.list(SERVICE_IAM_V3, "", "users", params)
if err != nil {
return nil, err
}
users := []SClouduser{}
client, err := self.newGeneralAPIClient()
err = resp.Unmarshal(&users, "users")
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
}
err = doListAllWithOffset(client.Users.List, params, &users)
if err != nil {
return nil, errors.Wrap(err, "doListAllWithOffset")
return nil, err
}
return users, nil
}
@@ -188,8 +183,9 @@ func (self *SHuaweiClient) CreateIClouduser(conf *cloudprovider.SClouduserCreate
return self.CreateClouduser(conf.Name, conf.Password, conf.Desc)
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=CreateUser
func (self *SHuaweiClient) CreateClouduser(name, password, desc string) (*SClouduser, error) {
params := map[string]string{
params := map[string]interface{}{
"name": name,
"domain_id": self.ownerId,
}
@@ -199,28 +195,29 @@ func (self *SHuaweiClient) CreateClouduser(name, password, desc string) (*SCloud
if len(desc) > 0 {
params["description"] = desc
}
client, err := self.newGeneralAPIClient()
resp, err := self.post(SERVICE_IAM, "", "OS-USER/users", map[string]interface{}{"user": params})
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
}
user := SClouduser{client: self}
err = DoCreate(client.Users.Create, jsonutils.Marshal(map[string]interface{}{"user": params}), &user)
if err != nil {
ce, ok := err.(*modules.HuaweiClientError)
if ok && len(ce.Errorcode) > 0 && ce.Errorcode[0] == "1101" {
if strings.Contains(err.Error(), "1101") {
return nil, errors.Wrap(err, `IAM user name. The length is between 5 and 32. The first digit is not a number. Special characters can only contain the '_' '-' or ' '`) //https://support.huaweicloud.com/api-iam/iam_08_0015.html
}
return nil, errors.Wrap(err, "DoCreate")
return nil, err
}
return &user, nil
user := &SClouduser{client: self}
err = resp.Unmarshal(user, "user")
if err != nil {
return nil, err
}
return user, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=UpdateUser
func (self *SHuaweiClient) ResetClouduserPassword(id, password string) error {
client, err := self.newGeneralAPIClient()
if err != nil {
return errors.Wrap(err, "newGeneralAPIClient")
}
return client.Users.ResetPassword(id, password)
_, err := self.put(SERVICE_IAM, "", "OS-USER/users/"+id, map[string]interface{}{
"user": map[string]interface{}{
"password": password,
},
})
return err
}
type SAccessKey struct {
@@ -233,6 +230,7 @@ type SAccessKey struct {
CreatedAt time.Time `json:"create_time"`
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=ListPermanentAccessKeys
func (self *SHuaweiClient) GetAKSK(id string) ([]cloudprovider.SAccessKey, error) {
query := url.Values{}
query.Set("user_id", id)
@@ -253,6 +251,7 @@ func (self *SHuaweiClient) GetAKSK(id string) ([]cloudprovider.SAccessKey, error
return res, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=CreatePermanentAccessKey
func (self *SHuaweiClient) CreateAKSK(id, name string) (*cloudprovider.SAccessKey, error) {
params := map[string]interface{}{
"credential": map[string]interface{}{
@@ -265,7 +264,10 @@ func (self *SHuaweiClient) CreateAKSK(id, name string) (*cloudprovider.SAccessKe
return nil, errors.Wrap(err, "SHuaweiClient.createAKSK")
}
ak := SAccessKey{}
obj.Unmarshal(&ak, "credential")
err = obj.Unmarshal(&ak, "credential")
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
res := cloudprovider.SAccessKey{
Name: ak.Description,
AccessKey: ak.AccessKey,
@@ -274,6 +276,7 @@ func (self *SHuaweiClient) CreateAKSK(id, name string) (*cloudprovider.SAccessKe
return &res, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=DeletePermanentAccessKey
func (self *SHuaweiClient) DeleteAKSK(accessKey string) error {
_, err := self.delete(SERVICE_IAM, "", "OS-CREDENTIAL/credentials/"+accessKey)
return err
+107 -56
View File
@@ -17,6 +17,7 @@ package huawei
import (
"context"
"fmt"
"net/url"
"time"
"yunion.io/x/jsonutils"
@@ -97,20 +98,46 @@ type SDBInstance struct {
EnterpriseProjectId string
}
func (region *SRegion) GetDBInstances() ([]SDBInstance, error) {
params := map[string]string{}
dbinstances := []SDBInstance{}
err := doListAllWithOffset(region.ecsClient.DBInstance.List, params, &dbinstances)
return dbinstances, err
func (region *SRegion) GetDBInstances(id string) ([]SDBInstance, error) {
query := url.Values{}
if len(id) > 0 {
query.Set("id", id)
}
ret := []SDBInstance{}
for {
resp, err := region.list(SERVICE_RDS, "instances", query)
if err != nil {
return nil, errors.Wrapf(err, "list instances")
}
part := struct {
Instances []SDBInstance
TotalCount int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.Instances...)
if len(ret) >= part.TotalCount || len(part.Instances) == 0 {
break
}
query.Set("offset", fmt.Sprintf("%d", len(ret)))
}
return ret, nil
}
func (region *SRegion) GetDBInstance(instanceId string) (*SDBInstance, error) {
if len(instanceId) == 0 {
return nil, cloudprovider.ErrNotFound
ret, err := region.GetDBInstances(instanceId)
if err != nil {
return nil, err
}
instance := SDBInstance{region: region}
err := DoGet(region.ecsClient.DBInstance.Get, instanceId, nil, &instance)
return &instance, err
for i := range ret {
if ret[i].Id == instanceId {
ret[i].region = region
return &ret[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, instanceId)
}
func (rds *SDBInstance) GetName() string {
@@ -160,8 +187,12 @@ func (rds *SDBInstance) GetStatus() string {
}
func (rds *SDBInstance) GetBillingType() string {
_, err := rds.region.GetOrderResourceDetail(fmt.Sprintf("%s.vm", rds.Id))
orders, err := rds.region.client.GetOrderResources()
if err != nil {
return billing_api.BILLING_TYPE_PREPAID
}
_, ok := orders[rds.Id]
if ok {
return billing_api.BILLING_TYPE_POSTPAID
}
return billing_api.BILLING_TYPE_PREPAID
@@ -184,11 +215,15 @@ func (rds *SDBInstance) fetchFlavor() error {
}
func (rds *SDBInstance) GetExpiredAt() time.Time {
order, err := rds.region.GetOrderResourceDetail(fmt.Sprintf("%s.vm", rds.Id))
orders, err := rds.region.client.GetOrderResources()
if err != nil {
return time.Time{}
}
return order.ExpireTime
order, ok := orders[rds.Id]
if ok {
return order.ExpireTime
}
return time.Time{}
}
func (rds *SDBInstance) GetStorageType() string {
@@ -353,7 +388,7 @@ func (region *SRegion) GetIDBInstanceById(instanceId string) (cloudprovider.IClo
}
func (region *SRegion) GetIDBInstances() ([]cloudprovider.ICloudDBInstance, error) {
instances, err := region.GetDBInstances()
instances, err := region.GetDBInstances("")
if err != nil {
return nil, errors.Wrapf(err, "region.GetDBInstances()")
}
@@ -420,7 +455,7 @@ func (rds *SDBInstance) Delete() error {
}
func (region *SRegion) DeleteDBInstance(instanceId string) error {
_, err := region.ecsClient.DBInstance.Delete(instanceId, nil)
_, err := region.delete(SERVICE_RDS, "instances/"+instanceId)
return err
}
@@ -506,7 +541,7 @@ func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceC
}
params["flavor_ref"] = desc.InstanceType
params["availability_zone"] = desc.ZoneId
resp, err := region.ecsClient.DBInstance.Create(jsonutils.Marshal(params))
resp, err := region.post(SERVICE_RDS, "instances", params)
if err != nil {
return nil, errors.Wrapf(err, "Create")
}
@@ -518,20 +553,15 @@ func (region *SRegion) CreateIDBInstance(desc *cloudprovider.SManagedDBInstanceC
}
if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
err = cloudprovider.Wait(10*time.Second, 20*time.Minute, func() (bool, error) {
job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
job, err := region.GetDBInstanceJob(jobId)
if err != nil {
return false, nil
}
status, _ := job.GetString("status")
process, _ := job.GetString("process")
log.Debugf("create dbinstance job %s status: %s process: %s", jobId, status, process)
if status == "Completed" {
region.UpdateVM(instance.Id, cloudprovider.SInstanceUpdateOptions{
Description: desc.Description,
})
log.Debugf("create dbinstance job %s status: %s process: %s", jobId, job.Status, job.Process)
if job.Status == "Completed" {
return true, nil
}
if status == "Failed" {
if job.Status == "Failed" {
return false, fmt.Errorf("create failed")
}
return false, nil
@@ -554,25 +584,43 @@ func (rds *SDBInstance) ClosePublicConnection() error {
//return rds.region.PublicConnectionAction(rds.Id, "closeRC")
}
type SDBInstanceJob struct {
Status string
Process string
}
func (self *SRegion) GetDBInstanceJob(id string) (*SDBInstanceJob, error) {
query := url.Values{}
query.Set("id", id)
resp, err := self.list(SERVICE_RDS, "jobs", query)
if err != nil {
return nil, err
}
ret := &SDBInstanceJob{}
err = resp.Unmarshal(ret, "job")
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
return ret, nil
}
func (region *SRegion) PublicConnectionAction(instanceId string, action string) error {
resp, err := region.ecsClient.DBInstance.PerformAction2(action, instanceId, nil, "")
resp, err := region.post(SERVICE_RDS, fmt.Sprintf("instances/%s/%s", instanceId, action), nil)
if err != nil {
return errors.Wrapf(err, "rds.%s", action)
}
if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
job, err := region.GetDBInstanceJob(jobId)
if err != nil {
log.Errorf("failed to get job %s info error: %v", jobId, err)
return false
}
status, _ := job.GetString("status")
process, _ := job.GetString("process")
if status == "Completed" {
if job.Status == "Completed" {
return true
}
log.Debugf("%s dbinstance job %s status: %s process: %s", action, jobId, status, process)
log.Debugf("%s dbinstance job %s status: %s process: %s", action, jobId, job.Status, job.Process)
return false
})
}
@@ -582,26 +630,23 @@ func (region *SRegion) PublicConnectionAction(instanceId string, action string)
}
func (region *SRegion) RebootDBInstance(instanceId string) error {
params := jsonutils.Marshal(map[string]interface{}{
params := map[string]interface{}{
"restart": map[string]string{},
})
resp, err := region.ecsClient.DBInstance.PerformAction2("action", instanceId, params, "")
}
resp, err := region.post(SERVICE_RDS, fmt.Sprintf("instances/%s/action", instanceId), params)
if err != nil {
return err
}
if jobId, _ := resp.GetString("job_id"); len(jobId) > 0 {
err = cloudprovider.WaitCreated(10*time.Second, 20*time.Minute, func() bool {
job, err := region.ecsClient.DBInstanceJob.Get(jobId, map[string]string{"id": jobId})
job, err := region.GetDBInstanceJob(jobId)
if err != nil {
log.Errorf("failed to get job %s info error: %v", jobId, err)
return false
}
status, _ := job.GetString("status")
process, _ := job.GetString("process")
if status == "Completed" {
if job.Status == "Completed" {
return true
}
log.Debugf("reboot dbinstance job %s status: %s process: %s", jobId, status, process)
log.Debugf("reboot dbinstance job %s status: %s process: %s", jobId, job.Status, job.Process)
return false
})
}
@@ -616,12 +661,20 @@ type SDBInstanceFlavor struct {
}
func (region *SRegion) GetDBInstanceFlavors(engine string, version string) ([]SDBInstanceFlavor, error) {
flavors := []SDBInstanceFlavor{}
resp, err := region.ecsClient.DBInstanceFlavor.ListInContextWithSpec(nil, engine, map[string]string{"version_name": version}, "flavors")
query := url.Values{}
if len(version) > 0 {
query.Set("version_name", version)
}
resp, err := region.list(SERVICE_RDS, "flavors/"+engine, query)
if err != nil {
return nil, err
}
return flavors, jsonutils.Update(&flavors, resp.Data)
flavors := []SDBInstanceFlavor{}
err = resp.Unmarshal(&flavors, "flavors")
if err != nil {
return nil, err
}
return flavors, nil
}
func (rds *SDBInstance) CreateAccount(conf *cloudprovider.SDBInstanceAccountCreateConfig) error {
@@ -629,11 +682,11 @@ func (rds *SDBInstance) CreateAccount(conf *cloudprovider.SDBInstanceAccountCrea
}
func (region *SRegion) CreateDBInstanceAccount(instanceId, account, password string) error {
params := map[string]string{
params := map[string]interface{}{
"name": account,
"password": password,
}
_, err := region.ecsClient.DBInstance.CreateInContextWithSpec(nil, fmt.Sprintf("%s/db_user", instanceId), jsonutils.Marshal(params), "")
_, err := region.post(SERVICE_RDS, fmt.Sprintf("instances/%s/db_user", instanceId), params)
return err
}
@@ -642,11 +695,11 @@ func (rds *SDBInstance) CreateDatabase(conf *cloudprovider.SDBInstanceDatabaseCr
}
func (region *SRegion) CreateDBInstanceDatabase(instanceId, database, characterSet string) error {
params := map[string]string{
params := map[string]interface{}{
"name": database,
"character_set": characterSet,
}
_, err := region.ecsClient.DBInstance.CreateInContextWithSpec(nil, fmt.Sprintf("%s/database", instanceId), jsonutils.Marshal(params), "")
_, err := region.post(SERVICE_RDS, fmt.Sprintf("instances/%s/database", instanceId), params)
return err
}
@@ -661,24 +714,24 @@ func (region *SRegion) ChangeDBInstanceConfig(instanceId string, instanceType st
}
if len(instanceType) > 0 {
params := map[string]map[string]string{
params := map[string]interface{}{
"resize_flavor": map[string]string{
"spec_code": instanceType,
},
}
_, err := region.ecsClient.DBInstance.PerformAction("action", instanceId, jsonutils.Marshal(params))
_, err := region.post(SERVICE_RDS, fmt.Sprintf("instances/%s/action", instanceId), params)
if err != nil {
return errors.Wrap(err, "resize_flavor")
}
cloudprovider.WaitStatus(instance, api.DBINSTANCE_RUNNING, time.Second*5, time.Minute*30)
}
if diskSizeGb > 0 {
params := map[string]map[string]int{
params := map[string]interface{}{
"enlarge_volume": map[string]int{
"size": diskSizeGb,
},
}
_, err := region.ecsClient.DBInstance.PerformAction("action", instanceId, jsonutils.Marshal(params))
_, err := region.post(SERVICE_RDS, fmt.Sprintf("instances/%s/action", instanceId), params)
if err != nil {
return errors.Wrap(err, "enlarge_volume")
}
@@ -726,8 +779,7 @@ func (self *SRegion) DeleteRdsTags(instanceId string, tagsKey []string) error {
tagsObj = append(tagsObj, map[string]string{"key": k})
}
params["tags"] = tagsObj
_, err := self.ecsClient.DBInstance.PerformAction2("tags/action", instanceId, jsonutils.Marshal(params), "")
_, err := self.post(SERVICE_RDS, fmt.Sprintf("instances/%s/tags/action", instanceId), params)
return err
}
@@ -741,8 +793,7 @@ func (self *SRegion) CreateRdsTags(instanceId string, tags map[string]string) er
tagsObj = append(tagsObj, map[string]string{"key": k, "value": v})
}
params["tags"] = tagsObj
_, err := self.ecsClient.DBInstance.PerformAction2("tags/action", instanceId, jsonutils.Marshal(params), "")
_, err := self.post(SERVICE_RDS, fmt.Sprintf("instances/%s/tags/action", instanceId), params)
return err
}
@@ -770,7 +821,7 @@ func (region *SRegion) RecoveryDBInstanceFromBackup(target, origin string, backu
"instance_id": target,
},
}
_, err := region.ecsClient.DBInstance.PerformAction("", "recovery", jsonutils.Marshal(params))
_, err := region.post(SERVICE_RDS, "instances/recovery", params)
if err != nil {
return errors.Wrap(err, "dbinstance.recovery")
}
+66 -24
View File
@@ -16,12 +16,12 @@ package huawei
import (
"fmt"
"yunion.io/x/jsonutils"
"net/url"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
"yunion.io/x/pkg/util/httputils"
)
type SDBInstanceAccount struct {
@@ -40,11 +40,12 @@ func (account *SDBInstanceAccount) Delete() error {
}
func (region *SRegion) DeleteDBInstanceAccount(instanceId string, account string) error {
return DoDeleteWithSpec(region.ecsClient.DBInstance.DeleteInContextWithSpec, nil, instanceId, fmt.Sprintf("db_user/%s", account), nil, nil)
_, err := region.delete(SERVICE_RDS, fmt.Sprintf("instances/%s/db_user/%s", instanceId, account))
return err
}
func (account *SDBInstanceAccount) GetIDBInstanceAccountPrivileges() ([]cloudprovider.ICloudDBInstanceAccountPrivilege, error) {
privileges, err := account.instance.region.GetDBInstancePrivvileges(account.instance.Id, account.Name)
privileges, err := account.instance.region.GetDBInstancePrivileges(account.instance.Id, account.Name)
if err != nil {
return nil, err
}
@@ -57,28 +58,63 @@ func (account *SDBInstanceAccount) GetIDBInstanceAccountPrivileges() ([]cloudpro
}
func (region *SRegion) GetDBInstanceAccounts(instanceId string) ([]SDBInstanceAccount, error) {
params := map[string]string{
"instance_id": instanceId,
params := url.Values{}
params.Set("limit", "100")
page := 1
params.Set("page", fmt.Sprintf("%d", page))
ret := []SDBInstanceAccount{}
for {
resp, err := region.list(SERVICE_RDS, fmt.Sprintf("instances/%s/db_user/detail", instanceId), params)
if err != nil {
return nil, err
}
part := struct {
Users []SDBInstanceAccount
TotalCount int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.Users...)
if len(ret) >= part.TotalCount || len(part.Users) == 0 {
break
}
page++
params.Set("page", fmt.Sprintf("%d", page))
}
accounts := []SDBInstanceAccount{}
err := doListAllWithPage(region.ecsClient.DBInstance.ListAccounts, params, &accounts)
if err != nil {
return nil, err
}
return accounts, nil
return ret, nil
}
func (region *SRegion) GetDBInstancePrivvileges(instanceId string, username string) ([]SDatabasePrivilege, error) {
params := map[string]string{
"instance_id": instanceId,
"user-name": username,
func (region *SRegion) GetDBInstancePrivileges(instanceId string, username string) ([]SDatabasePrivilege, error) {
params := url.Values{}
params.Set("instance_id", instanceId)
params.Set("user-name", username)
params.Set("limit", "100")
page := 1
params.Set("page", fmt.Sprintf("%d", page))
ret := []SDatabasePrivilege{}
for {
resp, err := region.list(SERVICE_RDS, fmt.Sprintf("instances/%s/db_user/database", instanceId), params)
if err != nil {
return nil, err
}
part := struct {
Databases []SDatabasePrivilege
TotalCount int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.Databases...)
if len(ret) >= part.TotalCount || len(part.Databases) == 0 {
break
}
page++
params.Set("page", fmt.Sprintf("%d", page))
}
privileges := []SDatabasePrivilege{}
err := doListAllWithPage(region.ecsClient.DBInstance.ListPrivileges, params, &privileges)
if err != nil {
return nil, err
}
return privileges, nil
return ret, nil
}
func (account *SDBInstanceAccount) RevokePrivilege(database string) error {
@@ -94,7 +130,13 @@ func (region *SRegion) RevokeDBInstancePrivilege(instanceId string, account, dat
},
},
}
return DoDeleteWithSpec(region.ecsClient.DBInstance.DeleteInContextWithSpec, nil, instanceId, "db_privilege", nil, jsonutils.Marshal(params))
resource := fmt.Sprintf("instances/%s/db_privilege", instanceId)
url, err := region.client.getUrl(SERVICE_RDS, region.ID, resource, httputils.DELETE, nil)
if err != nil {
return err
}
_, err = region.client.request(httputils.DELETE, url, nil, params)
return err
}
func (account *SDBInstanceAccount) GrantPrivilege(database, privilege string) error {
@@ -119,7 +161,7 @@ func (region *SRegion) GrantDBInstancePrivilege(instanceId string, account, data
},
},
}
_, err := region.ecsClient.DBInstance.PerformAction("db_privilege", instanceId, jsonutils.Marshal(params))
_, err := region.post(SERVICE_RDS, fmt.Sprintf("instances/%s/db_privilege", instanceId), params)
return err
}
+33 -20
View File
@@ -15,6 +15,8 @@
package huawei
import (
"fmt"
"net/url"
"time"
"yunion.io/x/jsonutils"
@@ -115,7 +117,7 @@ func (backup *SDBInstanceBackup) Delete() error {
}
func (region *SRegion) DeleteDBInstanceBackup(backupId string) error {
_, err := region.ecsClient.DBInstanceBackup.Delete(backupId, nil)
_, err := region.delete(SERVICE_RDS, "backups/"+backupId)
return err
}
@@ -124,16 +126,30 @@ func (backup *SDBInstanceBackup) GetDBInstanceId() string {
}
func (region *SRegion) GetDBInstanceBackups(instanceId, backupId string) ([]SDBInstanceBackup, error) {
params := map[string]string{
"instance_id": instanceId,
}
params := url.Values{}
params.Set("instance_id", instanceId)
if len(backupId) > 0 {
params["backup_id"] = backupId
params.Set("backup_id", backupId)
}
backups := []SDBInstanceBackup{}
err := doListAllWithOffset(region.ecsClient.DBInstanceBackup.List, params, &backups)
if err != nil {
return nil, err
for {
resp, err := region.list(SERVICE_RDS, "backups", params)
if err != nil {
return nil, err
}
part := struct {
Backups []SDBInstanceBackup
TotalCount int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
backups = append(backups, part.Backups...)
if len(backups) >= part.TotalCount || len(part.Backups) == 0 {
break
}
params.Set("offset", fmt.Sprintf("%d", len(backups)))
}
return backups, nil
}
@@ -194,19 +210,15 @@ func (backup *SDBInstanceBackup) Refresh() error {
}
func (rds *SDBInstance) CreateIBackup(conf *cloudprovider.SDBInstanceBackupCreateConfig) (string, error) {
backupId, err := rds.region.CreateDBInstanceBackup(rds.Id, conf.Name, conf.Description, conf.Databases)
backup, err := rds.region.CreateDBInstanceBackup(rds.Id, conf.Name, conf.Description, conf.Databases)
if err != nil {
return "", err
}
backup, err := rds.region.GetIDBInstanceBackupById(backupId)
if err != nil {
return "", errors.Wrap(err, "region.GetIDBInstanceBackupById")
}
cloudprovider.WaitStatus(backup, api.DBINSTANCE_BACKUP_READY, time.Second*3, time.Minute*30)
return backupId, nil
return backup.GetGlobalId(), nil
}
func (region *SRegion) CreateDBInstanceBackup(instanceId string, name string, descrition string, databases []string) (string, error) {
func (region *SRegion) CreateDBInstanceBackup(instanceId string, name string, descrition string, databases []string) (*SDBInstanceBackup, error) {
params := map[string]interface{}{
"instance_id": instanceId,
"name": name,
@@ -219,15 +231,16 @@ func (region *SRegion) CreateDBInstanceBackup(instanceId string, name string, de
}
params["databases"] = dbs
}
resp, err := region.ecsClient.DBInstanceBackup.Create(jsonutils.Marshal(params))
resp, err := region.post(SERVICE_RDS, "backups", params)
if err != nil {
return "", errors.Wrap(err, "DBInstanceBackup.Create")
return nil, errors.Wrap(err, "DBInstanceBackup.Create")
}
backupId, err := resp.GetString("id")
ret := &SDBInstanceBackup{region: region}
err = resp.Unmarshal(ret, "backup")
if err != nil {
return "", errors.Wrap(err, "resp.GetBackupId")
return nil, err
}
return backupId, nil
return ret, nil
}
func (self *SDBInstanceBackup) CreateICloudDBInstance(opts *cloudprovider.SManagedDBInstanceCreateConfig) (cloudprovider.ICloudDBInstance, error) {
+28 -9
View File
@@ -16,6 +16,7 @@ package huawei
import (
"fmt"
"net/url"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/multicloud"
@@ -55,18 +56,36 @@ func (database *SDBInstanceDatabase) Delete() error {
}
func (region *SRegion) DeleteDBInstanceDatabase(instanceId, database string) error {
_, err := region.ecsClient.DBInstance.DeleteInContextWithSpec(nil, instanceId, fmt.Sprintf("database/%s", database), nil, nil, "")
_, err := region.delete(SERVICE_RDS, fmt.Sprintf("instances/%s/database/%s", instanceId, database))
return err
}
func (region *SRegion) GetDBInstanceDatabases(instanceId string) ([]SDBInstanceDatabase, error) {
params := map[string]string{
"instance_id": instanceId,
params := url.Values{}
params.Set("instance_id", instanceId)
params.Set("limit", "100")
page := 1
params.Set("page", fmt.Sprintf("%d", page))
ret := []SDBInstanceDatabase{}
for {
resp, err := region.list(SERVICE_RDS, fmt.Sprintf("instances/%s/database/detail", instanceId), params)
if err != nil {
return nil, err
}
part := struct {
Databases []SDBInstanceDatabase
TotalCount int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.Databases...)
if len(ret) >= part.TotalCount || len(part.Databases) == 0 {
break
}
page++
params.Set("page", fmt.Sprintf("%d", page))
}
databases := []SDBInstanceDatabase{}
err := doListAllWithPage(region.ecsClient.DBInstance.ListDatabases, params, &databases)
if err != nil {
return nil, err
}
return databases, nil
return ret, nil
}
+13 -6
View File
@@ -14,6 +14,12 @@
package huawei
import (
"fmt"
"yunion.io/x/pkg/errors"
)
type SDBInstanceParameter struct {
instance *SDBInstance
@@ -27,15 +33,16 @@ type SDBInstanceParameter struct {
}
func (region *SRegion) GetDBInstanceParameters(dbinstanceId string) ([]SDBInstanceParameter, error) {
params := map[string]string{
"instance_id": dbinstanceId,
}
paramters := []SDBInstanceParameter{}
err := doListAll(region.ecsClient.DBInstance.ListParameters, params, &paramters)
resp, err := region.list(SERVICE_RDS, fmt.Sprintf("instances/%s/configurations", dbinstanceId), nil)
if err != nil {
return nil, err
}
return paramters, nil
ret := []SDBInstanceParameter{}
err = resp.Unmarshal(&ret, "configuration_parameters")
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
return ret, nil
}
func (param *SDBInstanceParameter) GetGlobalId() string {
+101 -158
View File
@@ -16,6 +16,8 @@ package huawei
import (
"context"
"fmt"
"net/url"
"strings"
"time"
@@ -59,6 +61,7 @@ type DiskMeta struct {
ResourceType string `json:"resourceType"`
AttachedMode string `json:"attached_mode"`
Readonly string `json:"readonly"`
OrderId string `json:"orderID"`
}
type VolumeImageMetadata struct {
@@ -92,7 +95,6 @@ type SDisk struct {
storage *SStorage
multicloud.SDisk
HuaweiDiskTags
details *SResourceDetail
ID string `json:"id"`
Name string `json:"name"`
@@ -136,7 +138,6 @@ func (self *SDisk) GetGlobalId() string {
}
func (self *SDisk) GetStatus() string {
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0051803385.html
switch self.Status {
case "creating", "downloading":
return api.DISK_ALLOCATING
@@ -174,39 +175,18 @@ func (self *SDisk) GetStatus() string {
}
func (self *SDisk) Refresh() error {
new, err := self.storage.zone.region.GetDisk(self.GetId())
disk, err := self.storage.zone.region.GetDisk(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SDisk) IsEmulated() bool {
return false
}
func (self *SDisk) getResourceDetails() *SResourceDetail {
if self.details != nil {
return self.details
}
res, err := self.storage.zone.region.GetOrderResourceDetail(self.GetId())
if err != nil {
log.Debugln(err)
return nil
}
self.details = &res
return self.details
return jsonutils.Update(self, disk)
}
func (self *SDisk) GetBillingType() string {
details := self.getResourceDetails()
if details == nil {
return billing_api.BILLING_TYPE_POSTPAID
} else {
if len(self.Metadata.OrderId) > 0 {
return billing_api.BILLING_TYPE_PREPAID
}
return billing_api.BILLING_TYPE_POSTPAID
}
func (self *SDisk) GetCreatedAt() time.Time {
@@ -214,13 +194,15 @@ func (self *SDisk) GetCreatedAt() time.Time {
}
func (self *SDisk) GetExpiredAt() time.Time {
var expiredTime time.Time
details := self.getResourceDetails()
if details != nil {
expiredTime = details.ExpireTime
orders, err := self.storage.zone.region.client.GetOrderResources()
if err != nil {
return time.Time{}
}
return expiredTime
order, ok := orders[self.ID]
if ok {
return order.ExpireTime
}
return time.Time{}
}
func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
@@ -228,7 +210,6 @@ func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
}
func (self *SDisk) GetDiskFormat() string {
// self.volume_type ?
return "vhd"
}
@@ -241,7 +222,7 @@ func (self *SDisk) checkAutoDelete(attachments []Attachment) bool {
for _, attach := range attachments {
if len(attach.ServerID) > 0 {
// todo : 忽略错误??
vm, err := self.storage.zone.region.GetInstanceByID(attach.ServerID)
vm, err := self.storage.zone.region.GetInstance(attach.ServerID)
if err != nil {
volumes := vm.OSExtendedVolumesVolumesAttached
for _, vol := range volumes {
@@ -270,14 +251,11 @@ func (self *SDisk) GetTemplateId() string {
return self.VolumeImageMetadata.ImageID
}
// Bootable 表示硬盘是否为启动盘。
// 启动盘 != 系统盘(必须是启动盘且挂载在root device上)
func (self *SDisk) GetDiskType() string {
if self.Bootable == "true" {
return api.DISK_TYPE_SYS
} else {
return api.DISK_TYPE_DATA
}
return api.DISK_TYPE_DATA
}
func (self *SDisk) GetFsFormat() string {
@@ -289,9 +267,6 @@ func (self *SDisk) GetIsNonPersistent() bool {
}
func (self *SDisk) GetDriver() string {
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0058762431.html
// scsi or vbd?
// todo: implement me
return "scsi"
}
@@ -340,28 +315,19 @@ func (self *SDisk) Delete(ctx context.Context) error {
}
func (self *SDisk) CreateISnapshot(ctx context.Context, name string, desc string) (cloudprovider.ICloudSnapshot, error) {
if snapshotId, err := self.storage.zone.region.CreateSnapshot(self.GetId(), name, desc); err != nil {
log.Errorf("createSnapshot fail %s", err)
return nil, err
} else if snapshot, err := self.getSnapshot(snapshotId); err != nil {
return nil, err
} else {
snapshot.region = self.storage.zone.region
if err := cloudprovider.WaitStatus(snapshot, api.SNAPSHOT_READY, 15*time.Second, 3600*time.Second); err != nil {
return nil, err
}
return snapshot, nil
snapshot, err := self.storage.zone.region.CreateSnapshot(self.GetId(), name, desc)
if err != nil {
return nil, errors.Wrapf(err, "CreateSnapshot")
}
}
func (self *SDisk) getSnapshot(snapshotId string) (*SSnapshot, error) {
snapshot, err := self.storage.zone.region.GetSnapshotById(snapshotId)
return &snapshot, err
return snapshot, nil
}
func (self *SDisk) GetISnapshot(snapshotId string) (cloudprovider.ICloudSnapshot, error) {
snapshot, err := self.getSnapshot(snapshotId)
return snapshot, err
snapshot, err := self.storage.zone.region.GetSnapshot(snapshotId)
if err != nil {
return nil, err
}
return snapshot, nil
}
func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
@@ -378,17 +344,11 @@ func (self *SDisk) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) {
}
func (self *SDisk) Resize(ctx context.Context, newSizeMB int64) error {
err := cloudprovider.WaitStatus(self, api.DISK_READY, 5*time.Second, 60*time.Second)
if err != nil {
return err
}
sizeGb := newSizeMB / 1024
err = self.storage.zone.region.resizeDisk(self.GetId(), sizeGb)
err := self.storage.zone.region.ResizeDisk(self.GetId(), sizeGb)
if err != nil {
return err
}
return cloudprovider.WaitStatusWithDelay(self, api.DISK_READY, 15*time.Second, 5*time.Second, 60*time.Second)
}
@@ -417,140 +377,123 @@ func (self *SDisk) Detach() error {
func (self *SDisk) Attach(device string) error {
err := self.storage.zone.region.AttachDisk(self.GetMountServerId(), self.GetId(), device)
if err != nil {
log.Debugf("attach server %s disk %s failed: %s", self.GetMountServerId(), self.GetId(), err)
return err
return errors.Wrapf(err, "AttachDisk")
}
return cloudprovider.WaitStatusWithDelay(self, api.DISK_READY, 10*time.Second, 5*time.Second, 60*time.Second)
}
// 在线卸载磁盘 https://support.huaweicloud.com/usermanual-ecs/zh-cn_topic_0036046828.html
// 对于挂载在系统盘盘位(也就是“/dev/sda”或“/dev/vda”挂载点)上的磁盘,当前仅支持离线卸载
func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
mountpoint := self.GetMountpoint()
if len(mountpoint) > 0 {
err := self.Detach()
if err != nil {
return "", err
}
}
diskId, err := self.storage.zone.region.resetDisk(self.GetId(), snapshotId)
if err != nil {
return diskId, err
}
err = cloudprovider.WaitStatus(self, api.DISK_READY, 5*time.Second, 300*time.Second)
if err != nil {
return "", err
}
if len(mountpoint) > 0 {
err := self.Attach(mountpoint)
if err != nil {
return "", err
}
}
return diskId, nil
return "", cloudprovider.ErrNotSupported
}
// 华为云不支持重置
func (self *SDisk) Rebuild(ctx context.Context) error {
return cloudprovider.ErrNotSupported
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EVS/doc?api=ShowVolume
func (self *SRegion) GetDisk(diskId string) (*SDisk, error) {
if len(diskId) == 0 {
return nil, cloudprovider.ErrNotFound
resp, err := self.list(SERVICE_EVS, "cloudvolumes/"+diskId, nil)
if err != nil {
return nil, errors.Wrapf(err, "show volume")
}
var disk SDisk
err := DoGet(self.ecsClient.Disks.Get, diskId, nil, &disk)
return &disk, err
ret := &SDisk{}
err = resp.Unmarshal(ret, "volume")
if err != nil {
return nil, err
}
return ret, nil
}
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0058762430.html
// https://console.huaweicloud.com/apiexplorer/#/openapi/EVS/doc?api=ListVolumes
func (self *SRegion) GetDisks(zoneId string) ([]SDisk, error) {
queries := map[string]string{}
ret := []SDisk{}
query := url.Values{}
if len(zoneId) > 0 {
queries["availability_zone"] = zoneId
query.Set("availability_zone", zoneId)
}
disks := make([]SDisk, 0)
err := doListAllWithOffset(self.ecsClient.Disks.List, queries, &disks)
return disks, err
for {
resp, err := self.list(SERVICE_EVS, "cloudvolumes/detail", query)
if err != nil {
return nil, errors.Wrapf(err, "list volumes")
}
part := struct {
Volumes []SDisk
Count int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
ret = append(ret, part.Volumes...)
if len(ret) >= part.Count || len(part.Volumes) == 0 {
break
}
query.Set("offset", fmt.Sprintf("%d", len(ret)))
}
return ret, nil
}
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0058762427.html
// https://console.huaweicloud.com/apiexplorer/#/openapi/EVS/doc?api=CreateVolume
func (self *SRegion) CreateDisk(zoneId string, category string, name string, sizeGb int, snapshotId string, desc string, projectId string) (string, error) {
params := jsonutils.NewDict()
volumeObj := jsonutils.NewDict()
volumeObj.Add(jsonutils.NewString(name), "name")
volumeObj.Add(jsonutils.NewString(zoneId), "availability_zone")
volumeObj.Add(jsonutils.NewString(desc), "description")
volumeObj.Add(jsonutils.NewString(category), "volume_type")
volumeObj.Add(jsonutils.NewInt(int64(sizeGb)), "size")
params := map[string]interface{}{
"name": name,
"availability_zone": zoneId,
"description": desc,
"volume_type": category,
"size": sizeGb,
}
if len(snapshotId) > 0 {
volumeObj.Add(jsonutils.NewString(snapshotId), "snapshot_id")
params["snapshot_id"] = snapshotId
}
if len(projectId) > 0 {
volumeObj.Add(jsonutils.NewString(projectId), "enterprise_project_id")
params["enterprise_project_id"] = projectId
}
params.Add(volumeObj, "volume")
// 目前只支持创建按需资源,返回job id。 如果创建包年包月资源则返回order id
_id, err := self.ecsClient.Disks.AsyncCreate(params)
resp, err := self.post(SERVICE_EVS_V2_1, "cloudvolumes", map[string]interface{}{"volume": params})
if err != nil {
log.Debugf("AsyncCreate with params: %s", params)
return "", errors.Wrap(err, "AsyncCreate")
return "", errors.Wrapf(err, "create volume")
}
ret := struct {
JobId string
OrderId string
}{}
err = resp.Unmarshal(&ret)
if err != nil {
return "", errors.Wrapf(err, "Unmarshal")
}
id := ret.JobId + ret.OrderId
// 按需计费
volumeId, err := self.GetTaskEntityID(self.ecsClient.Disks.ServiceType(), _id, "volume_id")
volumeId, err := self.GetTaskEntityID(SERVICE_EVS_V1, id, "volume_id")
if err != nil {
return "", errors.Wrap(err, "GetAllSubTaskEntityIDs")
}
if len(volumeId) == 0 {
return "", errors.Errorf("CreateInstance job %s result is emtpy", _id)
} else {
return volumeId, nil
return "", errors.Errorf("CreateInstance job %s result is emtpy", id)
}
return volumeId, nil
}
// https://support.huaweicloud.com/api-evs/zh-cn_topic_0058762428.html
// 默认删除云硬盘关联的所有快照
// https://console.huaweicloud.com/apiexplorer/#/openapi/EVS/doc?api=DeleteVolume
func (self *SRegion) DeleteDisk(diskId string) error {
return DoDeleteWithSpec(self.ecsClient.Disks.DeleteInContextWithSpec, nil, diskId, "", nil, nil)
}
/*
扩容状态为available的云硬盘时没有约束限制
扩容状态为in-use的云硬盘时有以下约束
不支持共享云硬盘即multiattach参数值必须为false
云硬盘所挂载的云服务器状态必须为ACTIVEPAUSEDSUSPENDEDSHUTOFF才支持扩容
*/
func (self *SRegion) resizeDisk(diskId string, sizeGB int64) error {
params := jsonutils.NewDict()
osExtendObj := jsonutils.NewDict()
osExtendObj.Add(jsonutils.NewInt(sizeGB), "new_size") // GB
params.Add(osExtendObj, "os-extend")
_, err := self.ecsClient.Disks.PerformAction2("action", diskId, params, "")
res := fmt.Sprintf("cloudvolumes/%s", diskId)
_, err := self.delete(SERVICE_EVS, res)
return err
}
/*
https://support.huaweicloud.com/api-evs/zh-cn_topic_0051408629.html
只支持快照回滚到源云硬盘不支持快照回滚到其它指定云硬盘
只有云硬盘状态处于availableerror_rollbacking状态才允许快照回滚到源云硬盘
*/
func (self *SRegion) resetDisk(diskId, snapshotId string) (string, error) {
params := jsonutils.NewDict()
rollbackObj := jsonutils.NewDict()
rollbackObj.Add(jsonutils.NewString(diskId), "volume_id")
params.Add(rollbackObj, "rollback")
_, err := self.ecsClient.OsSnapshots.PerformAction2("rollback", snapshotId, params, "")
return diskId, err
// https://console.huaweicloud.com/apiexplorer/#/openapi/EVS/doc?api=ResizeVolume
func (self *SRegion) ResizeDisk(diskId string, sizeGB int64) error {
params := map[string]interface{}{
"os-extend": map[string]interface{}{
"new_size": sizeGB,
},
}
_, err := self.post(SERVICE_EVS_V2_1, fmt.Sprintf("cloudvolumes/%s/action", diskId), params)
return err
}
func (self *SDisk) GetProjectId() string {
+11 -4
View File
@@ -26,11 +26,18 @@ type SDomain struct {
Tagflag int `json:"tagflag"`
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneListAuthDomains
func (self *SHuaweiClient) GetDomains() ([]SDomain, error) {
huawei, _ := self.newGeneralAPIClient()
domains := make([]SDomain, 0)
err := doListAll(huawei.Domains.List, nil, &domains)
return domains, err
resp, err := self.list(SERVICE_IAM_V3, "", "auth/domains", nil)
if err != nil {
return nil, err
}
ret := []SDomain{}
err = resp.Unmarshal(&ret, "domains")
if err != nil {
return nil, err
}
return ret, nil
}
func (self *SHuaweiClient) getEnabledDomains() ([]SDomain, error) {
+35 -17
View File
@@ -22,7 +22,6 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/httputils"
billing_api "yunion.io/x/cloudmux/pkg/apis/billing"
api "yunion.io/x/cloudmux/pkg/apis/compute"
@@ -219,6 +218,7 @@ func (self *SRegion) GetInstancePortId(instanceId string) (string, error) {
return ports[0].ID, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v2&api=CreatePublicip
func (self *SRegion) AllocateEIP(opts *cloudprovider.SEip) (*SEipAddress, error) {
if len(opts.BGPType) == 0 {
switch self.GetId() {
@@ -270,8 +270,9 @@ func (self *SRegion) AllocateEIP(opts *cloudprovider.SEip) (*SEipAddress, error)
return eip, resp.Unmarshal(eip, "publicip")
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v3&api=ShowPublicip
func (self *SRegion) GetEip(eipId string) (*SEipAddress, error) {
resp, err := self.list(SERVICE_VPC, "eip/publicips/"+eipId, nil)
resp, err := self.list(SERVICE_VPC_V3, "eip/publicips/"+eipId, nil)
if err != nil {
return nil, err
}
@@ -279,11 +280,13 @@ func (self *SRegion) GetEip(eipId string) (*SEipAddress, error) {
return eip, resp.Unmarshal(eip, "publicip")
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v2&api=DeletePublicip
func (self *SRegion) DeallocateEIP(eipId string) error {
_, err := self.delete(SERVICE_VPC, "publicips/"+eipId)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v3&api=AssociatePublicips
func (self *SRegion) AssociateEip(eipId string, associateId, associateType string) error {
params := map[string]interface{}{
"publicip": map[string]interface{}{
@@ -292,16 +295,18 @@ func (self *SRegion) AssociateEip(eipId string, associateId, associateType strin
},
}
res := fmt.Sprintf("eip/publicips/%s/associate-instance", eipId)
_, err := self.post(SERVICE_VPC, res, params)
_, err := self.post(SERVICE_VPC_V3, res, params)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v3&api=DisassociatePublicips
func (self *SRegion) DissociateEip(eipId string) error {
res := fmt.Sprintf("eip/publicips/%s/disassociate-instance", eipId)
_, err := self.post(SERVICE_VPC, res, nil)
_, err := self.post(SERVICE_VPC_V3, res, nil)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v2&api=UpdateBandwidth
func (self *SRegion) UpdateEipBandwidth(bandwidthId string, bw int) error {
params := map[string]interface{}{
"bandwidth": map[string]interface{}{
@@ -316,6 +321,7 @@ func (self *SEipAddress) GetProjectId() string {
return self.EnterpriseProjectId
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v3&api=ListPublicips
func (self *SRegion) GetEips(portId string, addrs []string) ([]SEipAddress, error) {
query := url.Values{}
for _, addr := range addrs {
@@ -326,7 +332,7 @@ func (self *SRegion) GetEips(portId string, addrs []string) ([]SEipAddress, erro
}
eips := []SEipAddress{}
for {
resp, err := self.list(SERVICE_VPC, "eip/publicips", query)
resp, err := self.list(SERVICE_VPC_V3, "eip/publicips", query)
if err != nil {
return nil, err
}
@@ -350,6 +356,28 @@ func (self *SRegion) GetEips(portId string, addrs []string) ([]SEipAddress, erro
return eips, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v2&api=DeletePublicipTag
func (self *SRegion) DeletePublicipTag(eipId string, key string) error {
res := fmt.Sprintf("publicips/%s/tags/%s", eipId, key)
_, err := self.delete(SERVICE_VPC_V2_0, res)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/EIP/doc?version=v2&api=CreatePublicipTag
func (self *SRegion) CreatePublicipTag(eipId string, tags map[string]string) error {
params := map[string]interface{}{
"action": "create",
}
add := []map[string]string{}
for k, v := range tags {
add = append(add, map[string]string{"key": k, "value": v})
}
params["tags"] = add
res := fmt.Sprintf("publicips/%s/tags/action", eipId)
_, err := self.post(SERVICE_VPC_V2_0, res, params)
return err
}
func (self *SRegion) setEipTags(id string, existedTags, tags map[string]string, replace bool) error {
deleteTagsKey := []string{}
for k := range existedTags {
@@ -363,24 +391,14 @@ func (self *SRegion) setEipTags(id string, existedTags, tags map[string]string,
}
if len(deleteTagsKey) > 0 {
for _, k := range deleteTagsKey {
url := fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v2.0/%s/publicips/%s/tags/%s", self.ID, self.client.projectId, id, k)
_, err := self.client.request(httputils.DELETE, url, nil, nil)
err := self.DeletePublicipTag(self.ID, k)
if err != nil {
return errors.Wrapf(err, "remove tags")
}
}
}
if len(tags) > 0 {
params := map[string]interface{}{
"action": "create",
}
add := []map[string]string{}
for k, v := range tags {
add = append(add, map[string]string{"key": k, "value": v})
}
params["tags"] = add
url := fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v2.0/%s/publicips/%s/tags/action", self.ID, self.client.projectId, id)
_, err := self.client.request(httputils.POST, url, nil, params)
err := self.CreatePublicipTag(self.ID, tags)
if err != nil {
return errors.Wrapf(err, "add tags")
}
+5 -27
View File
@@ -17,9 +17,6 @@ package huawei
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
@@ -60,35 +57,16 @@ func (self *SElasticcacheAccount) GetAccountPrivilege() string {
return api.ELASTIC_CACHE_ACCOUNT_PRIVILEGE_WRITE
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423031.html
// 未找到关闭密码的开放api, 不支持开启/关闭密码访问
// https://console.huaweicloud.com/dcs/rest/v2/41f6bfe48d7f4455b7754f7c1b11ae34/instances/26db46e2-c7d8-4b5e-bd36-b5278d2fe17c/password/reset
// new_password: "26db46e2!"
// no_password_access: false
func (self *SElasticcacheAccount) ResetPassword(input cloudprovider.SCloudElasticCacheAccountResetPasswordInput) error {
if input.OldPassword == nil {
return fmt.Errorf("elasticcacheAccount.ResetPassword.input OldPassword should not be empty")
}
type ResetPasswordResult struct {
Result string `json:"result"`
Message string `json:"message"`
params := map[string]interface{}{
"old_password": *input.OldPassword,
"new_password": input.NewPassword,
}
result := ResetPasswordResult{}
params := jsonutils.NewDict()
params.Set("old_password", jsonutils.NewString(*input.OldPassword))
params.Set("new_password", jsonutils.NewString(input.NewPassword))
err := DoUpdateWithSpec2(self.cacheDB.region.ecsClient.Elasticcache.UpdateInContextWithSpec, self.cacheDB.GetId(), "password", params, &result)
if err != nil {
return errors.Wrap(err, "elasticcacheAccount.ResetPassword")
}
if result.Result != "success" {
return errors.Wrap(fmt.Errorf(result.Message), "elasticcacheAccount.ResetPassword")
}
return nil
_, err := self.cacheDB.region.put(SERVICE_DCS, fmt.Sprintf("instances/%s/password", self.cacheDB.InstanceID), params)
return err
}
func (self *SElasticcacheAccount) UpdateAccount(input cloudprovider.SCloudElasticCacheAccountUpdateInput) error {
+5 -6
View File
@@ -15,6 +15,7 @@
package huawei
import (
"fmt"
"time"
"yunion.io/x/jsonutils"
@@ -127,12 +128,10 @@ func (self *SElasticcacheBackup) Delete() error {
return cloudprovider.ErrNotSupported
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423034.html
func (self *SElasticcacheBackup) RestoreInstance(instanceId string) error {
_, err := self.cacheDB.region.ecsClient.Elasticcache.RestoreInstance(instanceId, self.GetId())
if err != nil {
return nil
params := map[string]interface{}{
"backup_id": self.BackupID,
}
return nil
_, err := self.cacheDB.region.post(SERVICE_DCS, fmt.Sprintf("instances/%s/restores", self.cacheDB.InstanceID), params)
return err
}
+198 -285
View File
@@ -16,7 +16,7 @@ package huawei
import (
"fmt"
"strconv"
"net/url"
"strings"
"time"
@@ -31,7 +31,6 @@ import (
"yunion.io/x/cloudmux/pkg/multicloud"
)
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html
type SElasticcache struct {
multicloud.SElasticcacheBase
HuaweiTags
@@ -162,21 +161,18 @@ func (self *SElasticcache) GetCreatedAt() time.Time {
}
func (self *SElasticcache) GetExpiredAt() time.Time {
var expiredTime time.Time
if self.ChargingMode == 1 {
res, err := self.region.GetOrderResourceDetail(self.GetId())
if err != nil {
log.Debugln(err)
}
expiredTime = res.ExpireTime
orders, err := self.region.client.GetOrderResources()
if err != nil {
return time.Time{}
}
return expiredTime
order, ok := orders[self.InstanceID]
if ok {
return order.ExpireTime
}
return time.Time{}
}
func (self *SElasticcache) GetInstanceType() string {
// todo: ??
return self.ResourceSpecCode
}
@@ -185,33 +181,27 @@ func (self *SElasticcache) GetCapacityMB() int {
}
func (self *SElasticcache) GetArchType() string {
/*
资源规格标识
dcs.single_node表示实例类型为单机
dcs.master_standby表示实例类型为主备
dcs.cluster表示实例类型为集群
*/
if strings.Contains(self.ResourceSpecCode, "single") {
return api.ELASTIC_CACHE_ARCH_TYPE_SINGLE
} else if strings.Contains(self.ResourceSpecCode, "ha") {
}
if strings.Contains(self.ResourceSpecCode, "ha") {
return api.ELASTIC_CACHE_ARCH_TYPE_MASTER
} else if strings.Contains(self.ResourceSpecCode, "cluster") {
return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER
} else if strings.Contains(self.ResourceSpecCode, "proxy") {
}
if strings.Contains(self.ResourceSpecCode, "cluster") {
return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER
}
return ""
if strings.Contains(self.ResourceSpecCode, "proxy") {
return api.ELASTIC_CACHE_ARCH_TYPE_CLUSTER
}
return self.ResourceSpecCode
}
func (self *SElasticcache) GetNodeType() string {
// single(单副本) | double(双副本)
if strings.Contains(self.ResourceSpecCode, "single") {
return "single"
} else {
return "double"
}
return "double"
}
func (self *SElasticcache) GetEngine() string {
@@ -324,69 +314,83 @@ func (self *SElasticcache) GetICloudElasticcacheParameters() ([]cloudprovider.IC
return iparameters, nil
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423035.html
func (self *SRegion) GetElasticCacheBackups(instanceId, startTime, endTime string) ([]SElasticcacheBackup, error) {
params := make(map[string]string)
params["instance_id"] = instanceId
params["beginTime"] = startTime
params["endTime"] = endTime
params := url.Values{}
params.Set("begin_time", startTime)
params.Set("end_time", endTime)
backups := make([]SElasticcacheBackup, 0)
err := doListAll(self.ecsClient.Elasticcache.ListBackups, params, &backups)
if err != nil {
return nil, err
}
return backups, nil
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423027.html
func (self *SRegion) GetElasticCacheParameters(instanceId string) ([]SElasticcacheParameter, error) {
params := make(map[string]string)
params["instance_id"] = instanceId
parameters := make([]SElasticcacheParameter, 0)
err := doListAll(self.ecsClient.Elasticcache.ListParameters, params, &parameters)
if err != nil {
return nil, err
}
return parameters, nil
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423044.html
func (self *SRegion) GetElasticCaches() ([]SElasticcache, error) {
params := make(map[string]string)
caches := make([]SElasticcache, 0)
err := doListAll(self.ecsClient.Elasticcache.List, params, &caches)
if err != nil {
return nil, errors.Wrap(err, "region.GetElasticCaches")
}
for i := range caches {
cache, err := self.GetElasticCache(caches[i].GetId())
ret := make([]SElasticcacheBackup, 0)
for {
resp, err := self.list(SERVICE_DCS, fmt.Sprintf("instances/%s/backups", instanceId), params)
if err != nil {
return nil, err
} else {
caches[i] = *cache
}
caches[i].region = self
part := struct {
BackupRecordResponse []SElasticcacheBackup
TotalNum int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.BackupRecordResponse...)
if len(ret) >= part.TotalNum || len(part.BackupRecordResponse) == 0 {
break
}
params.Set("offset", fmt.Sprintf("%d", len(ret)))
}
return caches, nil
return ret, nil
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423020.html
func (self *SRegion) GetElasticCache(instanceId string) (*SElasticcache, error) {
cache := SElasticcache{}
err := DoGet(self.ecsClient.Elasticcache.Get, instanceId, nil, &cache)
func (self *SRegion) GetElasticCacheParameters(instanceId string) ([]SElasticcacheParameter, error) {
resp, err := self.list(SERVICE_DCS, fmt.Sprintf("instances/%s/configs", instanceId), nil)
if err != nil {
return nil, errors.Wrapf(err, "region.GetElasticCache %s", instanceId)
return nil, err
}
ret := make([]SElasticcacheParameter, 0)
err = resp.Unmarshal(&ret, "redis_config")
if err != nil {
return nil, err
}
return ret, nil
}
cache.region = self
return &cache, nil
func (self *SRegion) GetElasticCaches() ([]SElasticcache, error) {
ret := make([]SElasticcache, 0)
query := url.Values{}
for {
resp, err := self.list(SERVICE_DCS, "instances", query)
if err != nil {
return nil, err
}
part := struct {
Instances []SElasticcache
InstanceNum int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.Instances...)
if len(ret) >= part.InstanceNum || len(part.Instances) == 0 {
break
}
query.Set("offset", fmt.Sprintf("%d", len(ret)))
}
return ret, nil
}
func (self *SRegion) GetElasticCache(instanceId string) (*SElasticcache, error) {
resp, err := self.list(SERVICE_DCS, "instances/"+instanceId, nil)
if err != nil {
return nil, err
}
ret := &SElasticcache{region: self}
err = resp.Unmarshal(ret)
if err != nil {
return nil, err
}
return ret, nil
}
func (self *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasticcache, error) {
@@ -398,188 +402,124 @@ func (self *SRegion) GetIElasticcacheById(id string) (cloudprovider.ICloudElasti
return ec, nil
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423047.html
func (self *SRegion) zoneNameToDcsZoneIds(zoneIds []string) ([]string, error) {
type Z struct {
ID string `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
Port string `json:"port"`
ResourceAvailability string `json:"resource_availability"`
}
rs := []Z{}
err := doListAll(self.ecsClient.DcsAvailableZone.List, nil, &rs)
if err != nil {
return nil, errors.Wrap(err, "region.zoneNameToDcsZoneIds")
}
zoneMap := map[string]string{}
for i := range rs {
if rs[i].ResourceAvailability == "true" {
zoneMap[rs[i].Code] = rs[i].ID
}
}
ret := []string{}
for _, zone := range zoneIds {
if id, ok := zoneMap[zone]; ok {
ret = append(ret, id)
} else {
return nil, errors.Wrap(fmt.Errorf("zone %s not found or not available", zone), "region.zoneNameToDcsZoneIds")
}
}
return ret, nil
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423019.html
func (self *SRegion) CreateIElasticcaches(ec *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) {
params := jsonutils.NewDict()
params.Set("name", jsonutils.NewString(ec.InstanceName))
params.Set("engine", jsonutils.NewString(ec.Engine))
params.Set("engine_version", jsonutils.NewString(ec.EngineVersion))
params.Set("capacity", jsonutils.NewInt(ec.CapacityGB))
params.Set("vpc_id", jsonutils.NewString(ec.VpcId))
if len(ec.SecurityGroupIds) > 0 {
params.Set("security_group_id", jsonutils.NewString(ec.SecurityGroupIds[0]))
}
params.Set("subnet_id", jsonutils.NewString(ec.NetworkId))
params.Set("product_id", jsonutils.NewString(ec.InstanceType))
zones, err := self.zoneNameToDcsZoneIds(ec.ZoneIds)
func (self *SRegion) CreateIElasticcaches(opts *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) {
ret, err := self.CreateElasticcache(opts)
if err != nil {
return nil, err
}
params.Set("available_zones", jsonutils.NewStringArray(zones))
if len(ec.ProjectId) > 0 {
params.Set("enterprise_project_id", jsonutils.NewString(ec.ProjectId))
}
if len(ec.Password) > 0 {
params.Set("no_password_access", jsonutils.NewString("false"))
params.Set("password", jsonutils.NewString(ec.Password))
// todo: 这里换成常量
if ec.Engine == "Memcache" {
params.Set("access_user", jsonutils.NewString(ec.UserName))
}
} else {
params.Set("no_password_access", jsonutils.NewString("true"))
}
if len(ec.EipId) > 0 {
params.Set("enable_publicip", jsonutils.NewString("true"))
params.Set("publicip_id", jsonutils.NewString(ec.EipId))
// enable_ssl
} else {
params.Set("enable_publicip", jsonutils.NewString("false"))
}
if len(ec.PrivateIpAddress) > 0 {
params.Set("private_ip", jsonutils.NewString(ec.PrivateIpAddress))
}
if len(ec.MaintainBegin) > 0 {
params.Set("maintain_begin", jsonutils.NewString(ec.MaintainBegin))
params.Set("maintain_end", jsonutils.NewString(ec.MaintainEnd))
}
if ec.BillingCycle != nil {
bssParam := jsonutils.NewDict()
bssParam.Set("charging_mode", jsonutils.NewString("prePaid"))
bssParam.Set("is_auto_pay", jsonutils.NewString("true"))
bssParam.Set("is_auto_renew", jsonutils.NewString(fmt.Sprintf("%v", ec.BillingCycle.AutoRenew)))
if ec.BillingCycle.GetMonths() >= 1 && ec.BillingCycle.GetMonths() >= 9 {
bssParam.Set("period_type", jsonutils.NewString("month"))
bssParam.Set("period_num", jsonutils.NewInt(int64(ec.BillingCycle.GetMonths())))
} else if ec.BillingCycle.GetYears() >= 1 && ec.BillingCycle.GetYears() <= 3 {
bssParam.Set("period_type", jsonutils.NewString("year"))
bssParam.Set("period_num", jsonutils.NewInt(int64(ec.BillingCycle.GetYears())))
} else {
return nil, fmt.Errorf("region.CreateIElasticcaches invalid billing cycle.reqired month (1~9) or year(1~3)")
}
params.Set("bss_param", bssParam)
}
ret := &SElasticcache{}
err = DoCreate(self.ecsClient.Elasticcache.Create, params, ret)
if err != nil {
return nil, errors.Wrap(err, "region.CreateIElasticcaches")
}
ret.region = self
return ret, nil
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423030.html
func (self *SElasticcache) Restart() error {
resp, err := self.region.ecsClient.Elasticcache.Restart(self.GetId())
if err != nil {
return errors.Wrap(err, "elasticcache.Restart")
func (self *SRegion) CreateElasticcache(opts *cloudprovider.SCloudElasticCacheInput) (cloudprovider.ICloudElasticcache, error) {
params := map[string]interface{}{
"name": opts.InstanceName,
"engine": opts.Engine,
"engine_version": opts.EngineVersion,
"capacity": opts.CapacityGB,
"vpc_id": opts.VpcId,
"subnet_id": opts.NetworkId,
"product_id": opts.InstanceType,
"zone_codes": opts.ZoneIds,
"no_password_access": true,
"enable_publicip": false,
}
if len(opts.SecurityGroupIds) > 0 {
params["security_group_id"] = opts.SecurityGroupIds[0]
}
rets, err := resp.GetArray("results")
if err != nil {
return errors.Wrap(err, "elasticcache.results")
if len(opts.ProjectId) > 0 {
params["enterprise_project_id"] = opts.ProjectId
}
for _, r := range rets {
if ret, _ := r.GetString("result"); ret != "success" {
return fmt.Errorf("elasticcache.Restart failed")
if len(opts.Password) > 0 {
params["no_password_access"] = false
params["password"] = opts.Password
if opts.Engine == "Memcache" {
params["access_user"] = opts.UserName
}
}
return nil
if len(opts.EipId) > 0 {
params["enable_publicip"] = true
params["publicip_id"] = opts.EipId
}
if len(opts.PrivateIpAddress) > 0 {
params["private_ip"] = opts.PrivateIpAddress
}
if len(opts.MaintainBegin) > 0 {
params["maintain_begin"] = opts.MaintainBegin
params["maintain_end"] = opts.MaintainEnd
}
if opts.BillingCycle != nil {
bssParam := map[string]interface{}{
"charging_mode": "prePaid",
"is_auto_pay": true,
"is_auto_renew": opts.BillingCycle.AutoRenew,
}
if opts.BillingCycle.GetMonths() >= 1 && opts.BillingCycle.GetMonths() >= 9 {
bssParam["period_type"] = "month"
bssParam["period_num"] = opts.BillingCycle.GetMonths()
} else if opts.BillingCycle.GetYears() >= 1 && opts.BillingCycle.GetYears() <= 3 {
bssParam["period_type"] = "year"
bssParam["period_num"] = opts.BillingCycle.GetYears()
} else {
return nil, fmt.Errorf("region.CreateIElasticcaches invalid billing cycle.reqired month (1~9) or year(1~3)")
}
params["bss_param"] = bssParam
}
resp, err := self.post(SERVICE_DCS, "instances", params)
if err != nil {
return nil, err
}
ret := struct {
OrderId string
Instances []SElasticcache
}{}
err = resp.Unmarshal(&ret)
if err != nil {
return nil, err
}
for i := range ret.Instances {
ret.Instances[i].region = self
return &ret.Instances[i], nil
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, "after created %s", resp.String())
}
func (self *SElasticcache) Restart() error {
params := map[string]interface{}{
"instances": []string{self.InstanceID},
"action": "restart",
}
_, err := self.region.put(SERVICE_DCS, "instances/status", params)
return err
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423022.html
func (self *SElasticcache) Delete() error {
err := DoDelete(self.region.ecsClient.Elasticcache.Delete, self.GetId(), nil, nil)
if err != nil {
return errors.Wrap(err, "elasticcache.Delete")
}
return nil
_, err := self.region.delete(SERVICE_DCS, "instances/"+self.GetId())
return err
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423024.html
func (self *SElasticcache) ChangeInstanceSpec(spec string) error {
segs := strings.Split(spec, ":")
if len(segs) < 2 {
return fmt.Errorf("elasticcache.ChangeInstanceSpec invalid sku %s", spec)
params := map[string]interface{}{
"spec_code": spec,
"new_capacity": self.CapacityGB,
}
if !strings.HasPrefix(segs[1], "m") || !strings.HasSuffix(segs[1], "g") {
return fmt.Errorf("elasticcache.ChangeInstanceSpec sku %s memeory size is invalid.", spec)
}
newCapacity := segs[1][1 : len(segs[1])-1]
capacity, err := strconv.Atoi(newCapacity)
if err != nil {
return errors.Wrap(fmt.Errorf("invalid sku capacity %s", spec), "Elasticcache.ChangeInstanceSpec")
}
_, err = self.region.ecsClient.Elasticcache.ChangeInstanceSpec(self.GetId(), segs[0], int64(capacity))
if err != nil {
return errors.Wrap(err, "elasticcache.ChangeInstanceSpec")
}
return nil
_, err := self.region.post(SERVICE_DCS, fmt.Sprintf("instances/%s/resize", self.InstanceID), params)
return err
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423021.html
func (self *SElasticcache) SetMaintainTime(maintainStartTime, maintainEndTime string) error {
params := jsonutils.NewDict()
params.Set("maintain_begin", jsonutils.NewString(maintainStartTime))
params.Set("maintain_end", jsonutils.NewString(maintainEndTime))
err := DoUpdate(self.region.ecsClient.Elasticcache.Update, self.GetId(), params, nil)
if err != nil {
return errors.Wrap(err, "elasticcache.SetMaintainTime")
params := map[string]interface{}{
"maintain_begin": maintainStartTime,
"maintain_end": maintainEndTime,
}
return nil
_, err := self.region.put(SERVICE_DCS, "instances/"+self.InstanceID, params)
return err
}
// https://support.huaweicloud.com/usermanual-dcs/dcs-zh-ug-180314001.html
@@ -598,22 +538,16 @@ func (self *SElasticcache) CreateAccount(account cloudprovider.SCloudElasticCach
return nil, cloudprovider.ErrNotSupported
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423031.html
func (self *SElasticcache) CreateAcl(aclName, securityIps string) (cloudprovider.ICloudElasticcacheAcl, error) {
return nil, cloudprovider.ErrNotSupported
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423029.html
func (self *SElasticcache) UpdateInstanceParameters(config jsonutils.JSONObject) error {
params := jsonutils.NewDict()
params.Set("redis_config", config)
err := DoUpdateWithSpec(self.region.ecsClient.Elasticcache.UpdateInContextWithSpec, self.GetId(), "configs", params)
if err != nil {
return errors.Wrap(err, "elasticcache.UpdateInstanceParameters")
params := map[string]interface{}{
"redis_config": config,
}
return nil
_, err := self.region.put(SERVICE_DCS, fmt.Sprintf("instances/%s/async-configs", self.InstanceID), params)
return err
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423033.html
@@ -646,48 +580,27 @@ func backupPeriodTrans(config cloudprovider.SCloudElasticCacheBackupPolicyUpdate
return ret
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423021.html
func (self *SElasticcache) UpdateBackupPolicy(config cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error {
params := jsonutils.NewDict()
policy := jsonutils.NewDict()
policy.Set("save_days", jsonutils.NewInt(int64(config.BackupReservedDays)))
policy.Set("backup_type", jsonutils.NewString(config.BackupType))
plan := jsonutils.NewDict()
backTime := strings.ReplaceAll(config.PreferredBackupTime, "Z", "")
backupPeriod := backupPeriodTrans(config)
plan.Set("begin_at", jsonutils.NewString(backTime))
plan.Set("period_type", jsonutils.NewString("weekly"))
plan.Set("backup_at", backupPeriod)
policy.Set("periodical_backup_plan", plan)
params.Set("instance_backup_policy", policy)
err := DoUpdateWithSpec(self.region.ecsClient.Elasticcache.UpdateInContextWithSpec, self.GetId(), "configs", params)
if err != nil {
return errors.Wrap(err, "elasticcache.UpdateInstanceParameters")
func (self *SElasticcache) UpdateBackupPolicy(opts cloudprovider.SCloudElasticCacheBackupPolicyUpdateInput) error {
params := map[string]interface{}{
"save_days": opts.BackupReservedDays,
"backup_type": opts.BackupType,
"periodical_backup_plan": map[string]interface{}{
"begin_at": strings.ReplaceAll(opts.PreferredBackupTime, "Z", ""),
"period_type": "weekly",
"backup_at": backupPeriodTrans(opts),
},
}
return nil
_, err := self.region.put(SERVICE_DCS, "instances/"+self.InstanceID, map[string]interface{}{"instance_backup_policy": params})
return err
}
// https://support.huaweicloud.com/api-dcs/dcs-zh-api-180423030.html
// 当前版本,只有DCS2.0实例支持清空数据功能,即flush操作。
func (self *SElasticcache) FlushInstance(input cloudprovider.SCloudElasticCacheFlushInstanceInput) error {
resp, err := self.region.ecsClient.Elasticcache.Flush(self.GetId())
if err != nil {
return errors.Wrap(err, "elasticcache.FlushInstance")
params := map[string]interface{}{
"instances": []string{self.InstanceID},
"action": "flush",
}
rets, err := resp.GetArray("results")
if err != nil {
return errors.Wrap(err, "elasticcache.FlushInstance")
}
for _, r := range rets {
if ret, _ := r.GetString("result"); ret != "success" {
return fmt.Errorf("elasticcache.FlushInstance failed")
}
}
return nil
_, err := self.region.put(SERVICE_DCS, "instances/status", params)
return err
}
// SElasticcacheAccount => ResetPassword
+28 -20
View File
@@ -15,10 +15,11 @@
package huawei
import (
"fmt"
"net/url"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/cloudmux/pkg/apis/compute"
@@ -26,7 +27,6 @@ import (
"yunion.io/x/cloudmux/pkg/multicloud"
)
// https://support.huaweicloud.com/api-em/zh-cn_topic_0121230880.html
type SEnterpriseProject struct {
multicloud.SProjectBase
HuaweiTags
@@ -40,16 +40,28 @@ type SEnterpriseProject struct {
}
func (self *SHuaweiClient) GetEnterpriseProjects() ([]SEnterpriseProject, error) {
projects := []SEnterpriseProject{}
client, err := self.newGeneralAPIClient()
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
ret := []SEnterpriseProject{}
query := url.Values{}
for {
resp, err := self.list(SERVICE_EPS, "", "enterprise-projects", query)
if err != nil {
return nil, errors.Wrapf(err, "list")
}
part := struct {
EnterpriseProjects []SEnterpriseProject
TotalCount int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.EnterpriseProjects...)
if len(part.EnterpriseProjects) == 0 || len(ret) > part.TotalCount {
break
}
query.Set("offset", fmt.Sprintf("%d", len(ret)))
}
err = doListAllWithOffset(client.EnterpriseProjects.List, map[string]string{}, &projects)
if err != nil {
return nil, errors.Wrap(err, "doListAllWithOffset")
}
return projects, nil
return ret, nil
}
func (ep *SEnterpriseProject) GetId() string {
@@ -72,17 +84,13 @@ func (ep *SEnterpriseProject) GetName() string {
}
func (self *SHuaweiClient) CreateExterpriseProject(name, desc string) (*SEnterpriseProject, error) {
client, err := self.newGeneralAPIClient()
if err != nil {
return nil, errors.Wrap(err, "newGeneralAPIClient")
}
params := map[string]string{
params := map[string]interface{}{
"name": name,
}
if len(desc) > 0 {
params["description"] = desc
}
resp, err := client.EnterpriseProjects.Create(jsonutils.Marshal(params))
resp, err := self.post(SERVICE_EPS, "", "enterprise-projects", params)
if err != nil {
if strings.Contains(err.Error(), "EPS.0004") {
return nil, cloudprovider.ErrNotSupported
@@ -92,12 +100,12 @@ func (self *SHuaweiClient) CreateExterpriseProject(name, desc string) (*SEnterpr
}
return nil, errors.Wrap(err, "EnterpriseProjects.Create")
}
project := &SEnterpriseProject{}
err = resp.Unmarshal(&project)
ret := &SEnterpriseProject{}
err = resp.Unmarshal(&ret, "enterprise_project")
if err != nil {
return nil, errors.Wrap(err, "resp.Unmarshal")
}
return project, nil
return ret, nil
}
func (self *SHuaweiClient) CreateIProject(name string) (cloudprovider.ICloudProject, error) {
+47 -104
View File
@@ -16,13 +16,11 @@ package huawei
import (
"fmt"
"strings"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/billing"
"yunion.io/x/pkg/util/osprofile"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
@@ -61,30 +59,28 @@ func (self *SHost) IsEmulated() bool {
}
func (self *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) {
vms, err := self.zone.region.GetInstances()
vms, err := self.zone.region.GetInstances("")
if err != nil {
return nil, err
}
filtedVms := make([]SInstance, 0)
ret := []cloudprovider.ICloudVM{}
for i := range vms {
if vms[i].OSEXTAZAvailabilityZone == self.zone.GetId() {
filtedVms = append(filtedVms, vms[i])
vms[i].host = self
ret = append(ret, &vms[i])
}
}
ivms := make([]cloudprovider.ICloudVM, len(filtedVms))
for i := 0; i < len(filtedVms); i += 1 {
filtedVms[i].host = self
ivms[i] = &filtedVms[i]
}
return ivms, nil
return ret, nil
}
func (self *SHost) GetIVMById(id string) (cloudprovider.ICloudVM, error) {
vm, err := self.zone.region.GetInstanceByID(id)
vm, err := self.zone.region.GetInstance(id)
if err != nil {
return nil, err
}
vm.host = self
return &vm, err
return vm, err
}
func (self *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) {
@@ -161,35 +157,29 @@ func (self *SHost) GetVersion() string {
return HUAWEI_API_VERSION
}
func (self *SHost) GetInstanceById(instanceId string) (*SInstance, error) {
instance, err := self.zone.region.GetInstanceByID(instanceId)
func (self *SHost) CreateVM(opts *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
vmId, err := self._createVM(opts)
if err != nil {
return nil, err
}
instance.host = self
return &instance, nil
}
cloudprovider.Wait(time.Second*5, time.Minute, func() (bool, error) {
_, err := self.zone.region.GetInstance(vmId)
if err == nil {
return true, nil
}
if errors.Cause(err) == cloudprovider.ErrNotFound {
return false, nil
}
return false, err
})
func (self *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) {
vmId, err := self._createVM(
desc.Name, desc.ExternalImageId, desc.SysDisk,
desc.Cpu, desc.MemoryMB, desc.InstanceType,
desc.ExternalNetworkId, desc.IpAddr,
desc.Description, desc.Account,
desc.Password, desc.DataDisks,
desc.PublicKey, desc.ExternalSecgroupIds,
desc.UserData, desc.BillingCycle, desc.ProjectId, desc.Tags)
vm, err := self.zone.region.GetInstance(vmId)
if err != nil {
return nil, err
}
vm, err := self.GetInstanceById(vmId)
if err != nil {
return nil, err
}
return vm, err
vm.host = self
return vm, nil
}
func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) {
@@ -200,102 +190,55 @@ func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error
return cloudprovider.GetHostNetifs(host, wires), nil
}
func (self *SHost) _createVM(name string, imgId string, sysDisk cloudprovider.SDiskInfo, cpu int, memMB int, instanceType string,
networkId string, ipAddr string, desc string, account string, passwd string,
diskSizes []cloudprovider.SDiskInfo, publicKey string, secgroupIds []string,
userData string, bc *billing.SBillingCycle, projectId string, tags map[string]string) (string, error) {
net := self.zone.getNetworkById(networkId)
if net == nil {
return "", fmt.Errorf("invalid network ID %s", networkId)
}
if net.wire == nil {
log.Errorf("network's wire is empty")
return "", fmt.Errorf("network's wire is empty")
}
if net.wire.vpc == nil {
log.Errorf("wire's vpc is empty")
return "", fmt.Errorf("wire's vpc is empty")
}
func (self *SHost) _createVM(opts *cloudprovider.SManagedVMCreateConfig) (string, error) {
// 同步keypair
var err error
keypair := ""
if len(publicKey) > 0 {
keypair, err = self.zone.region.syncKeypair(publicKey)
var err error
if len(opts.PublicKey) > 0 {
keypair, err = self.zone.region.syncKeypair(opts.PublicKey)
if err != nil {
return "", err
}
}
// 镜像及硬盘配置
img, err := self.zone.region.GetImage(imgId)
img, err := self.zone.region.GetImage(opts.ExternalImageId)
if err != nil {
log.Errorf("getiamge %s fail %s", imgId, err)
return "", err
return "", errors.Wrapf(err, "GetImage")
}
if img.Status != ImageStatusActive {
log.Errorf("image %s status %s", imgId, img.Status)
return "", fmt.Errorf("image not ready")
}
// passwd, windows机型直接使用密码比较方便
if strings.ToLower(img.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) && len(passwd) > 0 {
keypair = ""
}
if strings.ToLower(img.Platform) == strings.ToLower(osprofile.OS_TYPE_WINDOWS) {
if u, err := updateWindowsUserData(userData, img.OSVersion, account, passwd); err == nil {
userData = u
} else {
return "", errors.Wrap(err, "SHost.CreateVM.updateWindowsUserData")
}
}
disks := make([]SDisk, len(diskSizes)+1)
disks[0].SizeGB = img.SizeGB
if sysDisk.SizeGB > 0 && sysDisk.SizeGB > img.SizeGB {
disks[0].SizeGB = sysDisk.SizeGB
}
disks[0].VolumeType = sysDisk.StorageType
for i, dataDisk := range diskSizes {
disks[i+1].SizeGB = dataDisk.SizeGB
disks[i+1].VolumeType = dataDisk.StorageType
if img.SizeGB > opts.SysDisk.SizeGB {
opts.SysDisk.SizeGB = img.SizeGB
}
// 创建实例
if len(instanceType) > 0 {
log.Debugf("Try instancetype : %s", instanceType)
vmId, err := self.zone.region.CreateInstance(name, imgId, instanceType, networkId, secgroupIds, net.VpcID, self.zone.GetId(), desc, disks, ipAddr, keypair, publicKey, passwd, userData, bc, projectId, tags)
if len(opts.InstanceType) > 0 {
log.Debugf("Try instancetype : %s", opts.InstanceType)
vmId, err := self.zone.region.CreateInstance(keypair, self.zone.ZoneName, opts)
if err != nil {
log.Errorf("Failed for %s: %s", instanceType, err)
return "", fmt.Errorf("create %s failed:%s", instanceType, ErrMessage(err))
} else {
return vmId, nil
return "", errors.Wrapf(err, "CreateInstance")
}
return vmId, nil
}
// 匹配实例类型
instanceTypes, err := self.zone.region.GetMatchInstanceTypes(cpu, memMB, self.zone.GetId())
instanceTypes, err := self.zone.region.GetMatchInstanceTypes(opts.Cpu, opts.MemoryMB, self.zone.GetId())
if err != nil {
return "", err
}
if len(instanceTypes) == 0 {
return "", fmt.Errorf("instance type %dC%dMB not avaiable", cpu, memMB)
return "", fmt.Errorf("instance type %dC%dMB not avaiable", opts.Cpu, opts.MemoryMB)
}
var vmId string
for _, instType := range instanceTypes {
instanceTypeId := instType.Name
log.Debugf("Try instancetype : %s", instanceTypeId)
vmId, err = self.zone.region.CreateInstance(name, imgId, instanceTypeId, networkId, secgroupIds, net.VpcID, self.zone.GetId(), desc, disks, ipAddr, keypair, publicKey, passwd, userData, bc, projectId, tags)
opts.InstanceType = instType.Name
log.Debugf("Try instancetype : %s", opts.InstanceType)
vmId, err = self.zone.region.CreateInstance(keypair, self.zone.ZoneName, opts)
if err != nil {
log.Errorf("Failed for %s: %s", instanceTypeId, err)
} else {
return vmId, nil
log.Errorf("Failed for %s: %s", opts.InstanceType, err)
}
return vmId, nil
}
return "", fmt.Errorf("create failed: %s", ErrMessage(err))
return "", errors.Wrapf(err, "CreateInstance")
}
+66 -131
View File
@@ -35,20 +35,9 @@ import (
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/auth/credentials"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/obs"
)
/*
待解决问题
1.同步的子账户中有一条空记录.需要查原因
2.安全组同步需要进一步确认
3.实例接口需要进一步确认
4.BGP type 目前是hard code在代码中需要考虑从cloudmeta服务中查询
*/
const (
CLOUD_PROVIDER_HUAWEI = api.CLOUD_PROVIDER_HUAWEI
CLOUD_PROVIDER_HUAWEI_CN = "华为云"
@@ -62,13 +51,21 @@ const (
HUAWEI_API_VERSION = "2018-12-25"
SERVICE_IAM = "iam"
SERVICE_IAM_V3 = "iam_v3"
SERVICE_IAM_V3_EXT = "iam_v3_ext"
SERVICE_ELB = "elb"
SERVICE_VPC = "vpc"
SERVICE_VPC_V2_0 = "vpc_v2.0"
SERVICE_VPC_V3 = "vpc_v3"
SERVICE_CES = "ces"
SERVICE_RDS = "rds"
SERVICE_ECS = "ecs"
SERVICE_ECS_V1_1 = "ecs_v1.1"
SERVICE_ECS_V2_1 = "ecs_v2.1"
SERVICE_EPS = "eps"
SERVICE_EVS = "evs"
SERVICE_EVS_V1 = "evs_v1"
SERVICE_EVS_V2_1 = "evs_v2.1"
SERVICE_BSS = "bss"
SERVICE_SFS = "sfs-turbo"
SERVICE_CTS = "cts"
@@ -77,10 +74,12 @@ const (
SERVICE_CCI = "cci"
SERVICE_CSBS = "csbs"
SERVICE_IMS = "ims"
SERVICE_IMS_V1 = "ims_v1"
SERVICE_AS = "as"
SERVICE_CCE = "cce"
SERVICE_DCS = "dcs"
SERVICE_MODELARTS = "modelarts"
SERVICE_MODELARTS_V1 = "modelarts_v1"
SERVICE_SCM = "scm"
SERVICE_CDN = "cdn"
SERVICE_GAUSSDB = "gaussdb"
@@ -100,17 +99,15 @@ type HuaweiClientConfig struct {
cpcfg cloudprovider.ProviderConfig
projectId string // 华为云项目ID.
cloudEnv string // 服务区域 ChinaCloud | InternationalCloud
accessKey string
accessSecret string
debug bool
}
func NewHuaweiClientConfig(cloudEnv, accessKey, accessSecret, projectId string) *HuaweiClientConfig {
func NewHuaweiClientConfig(accessKey, accessSecret, projectId string) *HuaweiClientConfig {
cfg := &HuaweiClientConfig{
projectId: projectId,
cloudEnv: cloudEnv,
accessKey: accessKey,
accessSecret: accessSecret,
}
@@ -130,8 +127,6 @@ func (cfg *HuaweiClientConfig) Debug(debug bool) *HuaweiClientConfig {
type SHuaweiClient struct {
*HuaweiClientConfig
signer auth.Signer
isMainProject bool // whether the project is the main project in the region
clientRegion string
@@ -147,6 +142,8 @@ type SHuaweiClient struct {
regions []SRegion
httpClient *http.Client
orders map[string]SOrderResource
}
// 进行资源操作时参数account 对应数据库cloudprovider表中的account字段,由accessKey和projectID两部分组成,通过"/"分割。
@@ -169,10 +166,6 @@ func (self *SHuaweiClient) init() error {
if err != nil {
return err
}
err = self.initSigner()
if err != nil {
return errors.Wrap(err, "initSigner")
}
err = self.initOwner()
if err != nil {
return errors.Wrap(err, "fetchOwner")
@@ -183,16 +176,6 @@ func (self *SHuaweiClient) init() error {
return nil
}
func (self *SHuaweiClient) initSigner() error {
var err error
cred := credentials.NewAccessKeyCredential(self.accessKey, self.accessKey)
self.signer, err = auth.NewSignerWithCredential(cred)
if err != nil {
return err
}
return nil
}
func (self *SHuaweiClient) getDefaultClient() *http.Client {
if self.httpClient != nil {
return self.httpClient
@@ -221,30 +204,10 @@ func (self *SHuaweiClient) getDefaultClient() *http.Client {
return self.httpClient
}
func (self *SHuaweiClient) newRegionAPIClient(regionId string) (*client.Client, error) {
projectId := self.projectId
if len(regionId) == 0 {
projectId = ""
}
cli, err := client.NewPublicCloudClientWithAccessKey(regionId, self.ownerId, projectId, self.accessKey, self.accessSecret, self.debug)
if err != nil {
return nil, err
}
httpClient := self.getDefaultClient()
cli.SetHttpClient(httpClient)
return cli, nil
}
type sPageInfo struct {
NextMarker string
}
func (self *SHuaweiClient) newGeneralAPIClient() (*client.Client, error) {
return self.newRegionAPIClient("")
}
type akClient struct {
client *http.Client
aksk aksk.SignOptions
@@ -287,6 +250,7 @@ func (self *SHuaweiClient) request(method httputils.THttpMethod, url string, que
}
if (strings.Contains(url, "/OS-CREDENTIAL/") ||
strings.Contains(url, "/users") ||
strings.Contains(url, "/groups") ||
strings.Contains(url, "eps.myhuaweicloud.com")) && len(self.ownerId) > 0 {
header.Set("X-Domain-Id", self.ownerId)
}
@@ -300,8 +264,8 @@ func (self *SHuaweiClient) request(method httputils.THttpMethod, url string, que
return resp, err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneListRegions
func (self *SHuaweiClient) fetchRegions() error {
huawei, _ := self.newGeneralAPIClient()
if self.regions == nil {
userId, err := self.GetUserId()
if err != nil {
@@ -309,12 +273,15 @@ func (self *SHuaweiClient) fetchRegions() error {
}
if regionsCache, ok := HUAWEI_REGION_CACHES.Load(userId); !ok || regionsCache.(*userRegionsCache).ExpireAt.Sub(time.Now()).Seconds() > 0 {
regions := make([]SRegion, 0)
err := doListAll(huawei.Regions.List, nil, &regions)
resp, err := self.list(SERVICE_IAM_V3, "", "regions", nil)
if err != nil {
return errors.Wrap(err, "Regions.List")
return errors.Wrapf(err, "list regions")
}
regions := make([]SRegion, 0)
err = resp.Unmarshal(&regions, "regions")
if err != nil {
return errors.Wrapf(err, "Unmarshal")
}
HUAWEI_REGION_CACHES.Store(userId, &userRegionsCache{ExpireAt: time.Now().Add(24 * time.Hour), UserId: userId, Regions: regions})
}
@@ -350,10 +317,6 @@ func (self *SHuaweiClient) fetchRegions() error {
self.iregions = make([]cloudprovider.ICloudRegion, len(filtedRegions))
for i := 0; i < len(filtedRegions); i += 1 {
filtedRegions[i].client = self
_, err := filtedRegions[i].getECSClient()
if err != nil {
return err
}
self.iregions[i] = &filtedRegions[i]
}
return nil
@@ -448,16 +411,6 @@ func (self *SHuaweiClient) GetCloudRegionExternalIdPrefix() string {
}
}
func (self *SHuaweiClient) UpdateAccount(accessKey, secret string) error {
if self.accessKey != accessKey || self.accessSecret != secret {
self.accessKey = accessKey
self.accessSecret = secret
return self.fetchRegions()
} else {
return nil
}
}
func (self *SHuaweiClient) GetRegions() []SRegion {
regions := make([]SRegion, len(self.iregions))
for i := 0; i < len(regions); i += 1 {
@@ -597,7 +550,6 @@ func (self *SHuaweiClient) GetIStorageById(id string) (cloudprovider.ICloudStora
return nil, cloudprovider.ErrNotFound
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/BSS/debug?api=ShowCustomerAccountBalances
type SBalance struct {
Amount float64 `json:"amount"`
Currency string `json:"currency"`
@@ -608,7 +560,7 @@ type SBalance struct {
MeasureUnit int64 `json:"measure_unit"`
}
// 这里的余额指的是所有租户的总余额
// https://console.huaweicloud.com/apiexplorer/#/openapi/BSS/doc?api=ShowCustomerAccountBalances
func (self *SHuaweiClient) QueryAccountBalance() (*SBalance, error) {
resp, err := self.list(SERVICE_BSS, "", "accounts/customer-accounts/balances", nil)
if err != nil {
@@ -645,17 +597,6 @@ func (self *SHuaweiClient) GetVersion() string {
return HUAWEI_API_VERSION
}
func (self *SHuaweiClient) GetAccessEnv() string {
switch self.cloudEnv {
case HUAWEI_INTERNATIONAL_CLOUDENV:
return api.CLOUD_ACCESS_ENV_HUAWEI_GLOBAL
case HUAWEI_CHINA_CLOUDENV:
return api.CLOUD_ACCESS_ENV_HUAWEI_CHINA
default:
return api.CLOUD_ACCESS_ENV_HUAWEI_GLOBAL
}
}
func (self *SHuaweiClient) GetCapabilities() []string {
caps := []string{
cloudprovider.CLOUD_CAPABILITY_PROJECT,
@@ -686,54 +627,56 @@ func (self *SHuaweiClient) GetCapabilities() []string {
return caps
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=ShowPermanentAccessKey
func (self *SHuaweiClient) GetUserId() (string, error) {
if len(self.userId) > 0 {
return self.userId, nil
}
client, err := self.newGeneralAPIClient()
if err != nil {
return "", errors.Wrap(err, "SHuaweiClient.GetUserId.newGeneralAPIClient")
}
type cred struct {
UserId string `json:"user_id"`
}
ret := &cred{}
err = DoGet(client.Credentials.Get, self.accessKey, nil, ret)
resp, err := self.list(SERVICE_IAM, "", "OS-CREDENTIAL/credentials/"+self.accessKey, nil)
if err != nil {
return "", errors.Wrap(err, "SHuaweiClient.GetUserId.DoGet")
return "", errors.Wrapf(err, "show credential")
}
err = resp.Unmarshal(ret, "credential")
if err != nil {
return "", errors.Wrapf(err, "Unmarshal")
}
self.userId = ret.UserId
return self.userId, nil
}
// owner id == domain_id == account id
// https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=ShowUser
func (self *SHuaweiClient) GetOwnerId() (string, error) {
if len(self.ownerId) > 0 {
return self.ownerId, nil
}
userId, err := self.GetUserId()
if err != nil {
return "", errors.Wrap(err, "SHuaweiClient.GetOwnerId.GetUserId")
}
client, err := self.newGeneralAPIClient()
if err != nil {
return "", errors.Wrap(err, "SHuaweiClient.GetOwnerId.newGeneralAPIClient")
}
type user struct {
DomainId string `json:"domain_id"`
Name string `json:"name"`
CreateTime string
}
ret := &user{}
err = DoGet(client.Users.Get, userId, nil, ret)
resp, err := self.list(SERVICE_IAM, "", "OS-USER/users/"+userId, nil)
if err != nil {
return "", errors.Wrap(err, "SHuaweiClient.GetOwnerId.DoGet")
return "", errors.Wrapf(err, "show user")
}
ret := &user{}
err = resp.Unmarshal(ret, "user")
if err != nil {
return "", errors.Wrapf(err, "Unmarshal")
}
self.ownerName = ret.Name
// 2021-02-02 02:43:28.0
self.ownerCreateTime, _ = timeutils.ParseTimeStr(strings.TrimSuffix(ret.CreateTime, ".0"))
@@ -820,65 +763,57 @@ func (self *SHuaweiClient) getUrl(service, regionId, resource string, method htt
switch service {
case SERVICE_IAM:
url = fmt.Sprintf("https://iam.myhuaweicloud.com/v3.0/%s", resource)
if !strings.HasPrefix(resource, "OS-") {
url = fmt.Sprintf("https://iam.myhuaweicloud.com/v3/%s", resource)
}
case SERVICE_IAM_V3:
url = fmt.Sprintf("https://iam.myhuaweicloud.com/v3/%s", resource)
case SERVICE_IAM_V3_EXT:
url = fmt.Sprintf("https://iam.myhuaweicloud.com/v3-ext/%s", resource)
case SERVICE_ELB:
url = fmt.Sprintf("https://elb.%s.myhuaweicloud.com/v3/%s/%s", regionId, self.projectId, resource)
case SERVICE_VPC:
version := "v1"
if strings.HasPrefix(resource, "vpc/") || strings.HasPrefix(resource, "eip/") {
version = "v3"
url = fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource)
case SERVICE_VPC_V2_0:
url = fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v2.0/%s/%s", regionId, self.projectId, resource)
if strings.Contains(resource, "/peerings") {
url = fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v2.0/%s", regionId, resource)
}
url = fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/%s/%s/%s", regionId, version, self.projectId, resource)
case SERVICE_VPC_V3:
url = fmt.Sprintf("https://vpc.%s.myhuaweicloud.com/v3/%s/%s", regionId, self.projectId, resource)
case SERVICE_CES:
url = fmt.Sprintf("https://ces.%s.myhuaweicloud.com/v1.0/%s/%s", regionId, self.projectId, resource)
case SERVICE_MODELARTS:
url = fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v2/%s/%s", regionId, self.projectId, resource)
if strings.HasPrefix(resource, "networks") || strings.HasPrefix(resource, "resourceflavors") {
url = fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource)
}
case SERVICE_MODELARTS_V1:
url = fmt.Sprintf("https://modelarts.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource)
case SERVICE_RDS:
url = fmt.Sprintf("https://rds.%s.myhuaweicloud.com/v3/%s/%s", regionId, self.projectId, resource)
case SERVICE_ECS:
version := "v1"
for _, prefix := range []string{
"os-availability-zone",
"servers",
"os-keypairs",
} {
if strings.HasPrefix(resource, prefix) || strings.Contains(resource, "os-security-groups") {
version = "v2.1"
break
}
}
if strings.HasSuffix(resource, "action") && !gotypes.IsNil(params) {
for _, k := range []string{"addSecurityGroup", "removeSecurityGroup"} {
_, ok := params[k]
if ok {
version = "v2.1"
break
}
}
}
url = fmt.Sprintf("https://ecs.%s.myhuaweicloud.com/%s/%s/%s", regionId, version, self.projectId, resource)
url = fmt.Sprintf("https://ecs.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource)
case SERVICE_ECS_V1_1:
url = fmt.Sprintf("https://ecs.%s.myhuaweicloud.com/v1.1/%s/%s", regionId, self.projectId, resource)
case SERVICE_ECS_V2_1:
url = fmt.Sprintf("https://ecs.%s.myhuaweicloud.com/v2.1/%s/%s", regionId, self.projectId, resource)
case SERVICE_EPS:
url = fmt.Sprintf("https://eps.myhuaweicloud.com/v1.0/%s", resource)
case SERVICE_EVS_V1:
url = fmt.Sprintf("https://evs.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource)
case SERVICE_EVS:
version := "v2"
url = fmt.Sprintf("https://evs.%s.myhuaweicloud.com/%s/%s/%s", regionId, version, self.projectId, resource)
url = fmt.Sprintf("https://evs.%s.myhuaweicloud.com/v2/%s/%s", regionId, self.projectId, resource)
case SERVICE_EVS_V2_1:
url = fmt.Sprintf("https://evs.%s.myhuaweicloud.com/v2.1/%s/%s", regionId, self.projectId, resource)
case SERVICE_BSS:
url = fmt.Sprintf("https://bss.myhuaweicloud.com/v2/%s", resource)
case SERVICE_SFS:
url = fmt.Sprintf("https://sfs-turbo.%s.myhuaweicloud.com/v1/%s/%s", regionId, self.projectId, resource)
case SERVICE_IMS:
url = fmt.Sprintf("https://ims.%s.myhuaweicloud.com/v2/%s", regionId, resource)
case SERVICE_IMS_V1:
url = fmt.Sprintf("https://ims.%s.myhuaweicloud.com/v1/%s", regionId, resource)
case SERVICE_DCS:
url = fmt.Sprintf("https://dcs.%s.myhuaweicloud.com/v2/%s/%s", regionId, self.projectId, resource)
case SERVICE_CTS:
url = fmt.Sprintf("https://cts.%s.myhuaweicloud.com/v3/%s/%s", regionId, self.projectId, resource)
case SERVICE_NAT:
url = fmt.Sprintf("https://nat.%s.myhuaweicloud.com/v2/%s/%s", regionId, self.projectId, resource)
url = fmt.Sprintf("https://nat.%s.myhuaweicloud.com/v3/%s/%s", regionId, self.projectId, resource)
case SERVICE_SCM:
url = fmt.Sprintf("https://scm.cn-north-4.myhuaweicloud.com/v3/%s", resource)
case SERVICE_CDN:
+88 -147
View File
@@ -17,6 +17,7 @@ package huawei
import (
"context"
"fmt"
"net/url"
"strings"
"time"
@@ -31,17 +32,6 @@ import (
"yunion.io/x/cloudmux/pkg/multicloud"
)
type TImageOwnerType string
const (
ImageOwnerPublic TImageOwnerType = "gold" // 公共镜像:gold
ImageOwnerSelf TImageOwnerType = "private" // 私有镜像:private
ImageOwnerShared TImageOwnerType = "shared" // 共享镜像:shared
EnvFusionCompute = "FusionCompute"
EnvIronic = "Ironic"
)
const (
ImageStatusQueued = "queued" // queued:表示镜像元数据已经创建成功,等待上传镜像文件。
ImageStatusSaving = "saving" // saving:表示镜像正在上传文件到后端存储。
@@ -50,7 +40,6 @@ const (
ImageStatusActive = "active" // active:表示镜像可以正常使用
)
// https://support.huaweicloud.com/api-ims/zh-cn_topic_0020091565.html
type SImage struct {
multicloud.SImageBase
HuaweiTags
@@ -141,11 +130,11 @@ func (self *SImage) GetImageStatus() string {
}
func (self *SImage) Refresh() error {
new, err := self.storageCache.region.GetImage(self.GetId())
image, err := self.storageCache.region.GetImage(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
return jsonutils.Update(self, image)
}
func (self *SImage) GetImageType() cloudprovider.TImageType {
@@ -218,10 +207,6 @@ func (self *SImage) GetCreatedAt() time.Time {
return self.CreatedAt
}
func (self *SImage) IsEmulated() bool {
return false
}
func (self *SImage) Delete(ctx context.Context) error {
return self.storageCache.region.DeleteImage(self.GetId())
}
@@ -231,145 +216,101 @@ func (self *SImage) GetIStoragecache() cloudprovider.ICloudStoragecache {
}
func (self *SRegion) GetImage(imageId string) (*SImage, error) {
image := &SImage{}
err := DoGet(self.ecsClient.Images.Get, imageId, nil, image)
if err != nil {
return nil, errors.Wrap(err, "DoGet")
}
return image, nil
}
func excludeImage(image SImage) bool {
if image.VirtualEnvType == "Ironic" {
return true
}
if len(image.SupportDiskIntensive) > 0 {
return true
}
if len(image.SupportKVMFPGAType) > 0 || len(image.SupportKVMAscend310) > 0 {
return true
}
if len(image.SupportKVMGPUType) > 0 {
return true
}
if len(image.SupportKVMNVMEHIGHIO) > 0 {
return true
}
if len(image.SupportGPUT4) > 0 {
return true
}
if len(image.SupportXENGPUType) > 0 {
return true
}
if len(image.SupportHighPerformance) > 0 {
return true
}
if len(image.SupportArm) > 0 {
return true
}
return false
}
// https://support.huaweicloud.com/api-ims/zh-cn_topic_0060804959.html
func (self *SRegion) GetImages(status string, imagetype TImageOwnerType, name string, envType string) ([]SImage, error) {
queries := map[string]string{}
if len(status) > 0 {
queries["status"] = status
}
if len(imagetype) > 0 {
queries["__imagetype"] = string(imagetype)
if imagetype == ImageOwnerPublic {
queries["protected"] = "True"
}
}
if len(envType) > 0 {
queries["virtual_env_type"] = envType
}
if len(name) > 0 {
queries["name"] = name
}
images := make([]SImage, 0)
err := doListAllWithMarker(self.ecsClient.Images.List, queries, &images)
// 排除掉需要特定镜像才能创建的实例类型
// https://support.huaweicloud.com/eu-west-0-api-ims/zh-cn_topic_0031617666.html#ZH-CN_TOPIC_0031617666__table48545918250
// https://support.huaweicloud.com/productdesc-ecs/zh-cn_topic_0088142947.html
filtedImages := make([]SImage, 0)
for i := range images {
if !excludeImage(images[i]) {
filtedImages = append(filtedImages, images[i])
}
}
return filtedImages, err
}
func (self *SRegion) DeleteImage(imageId string) error {
return DoDelete(self.ecsClient.OpenStackImages.Delete, imageId, nil, nil)
}
func (self *SRegion) GetImageByName(name string) (*SImage, error) {
if len(name) == 0 {
return nil, fmt.Errorf("image name should not be empty")
}
images, err := self.GetImages("", TImageOwnerType(""), name, "")
images, err := self.GetImages(imageId, "", "", "")
if err != nil {
return nil, err
}
if len(images) == 0 {
return nil, cloudprovider.ErrNotFound
for i := range images {
if images[i].ID == imageId {
return &images[i], nil
}
}
log.Debugf("%d image found match name %s", len(images), name)
return &images[0], nil
return nil, errors.Wrapf(cloudprovider.ErrNotFound, imageId)
}
/* https://support.huaweicloud.com/api-ims/zh-cn_topic_0020092109.html
os version 取值范围 https://support.huaweicloud.com/api-ims/zh-cn_topic_0031617666.html
用于创建私有镜像的源云服务器系统盘大小大于等于40GB且不超过1024GB
目前支持vhdzvhdrawqcow2
todo: 考虑使用镜像快速导入 https://support.huaweicloud.com/api-ims/zh-cn_topic_0133188204.html
使用OBS文件创建镜像
// https://console.huaweicloud.com/apiexplorer/#/openapi/IMS/doc?api=ListImages
func (self *SRegion) GetImages(id, status string, imagetype string, name string) ([]SImage, error) {
query := url.Values{}
query.Set("virtual_env_type", "FusionCompute")
if len(status) > 0 {
query.Set("status", status)
}
if len(id) > 0 {
query.Set("id", id)
}
if len(imagetype) > 0 {
query.Set("__imagetype", string(imagetype))
if imagetype == "gold" {
query.Set("protected", "True")
}
}
if len(name) > 0 {
query.Set("name", name)
}
ret := []SImage{}
for {
resp, err := self.list(SERVICE_IMS, "cloudimages", query)
if err != nil {
return nil, err
}
part := struct {
Images []SImage
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
ret = append(ret, part.Images...)
if len(part.Images) == 0 {
break
}
query.Set("marker", part.Images[len(part.Images)-1].ID)
}
return ret, nil
}
func (self *SRegion) DeleteImage(imageId string) error {
return cloudprovider.ErrNotImplemented
}
func (self *SRegion) GetImageByName(name string) (*SImage, error) {
images, err := self.GetImages("", "", "", name)
if err != nil {
return nil, err
}
for i := range images {
if images[i].Name == name {
return &images[i], nil
}
}
return nil, errors.Wrapf(cloudprovider.ErrNotFound, name)
}
* openstack原生接口支持的格式https://support.huaweicloud.com/api-ims/zh-cn_topic_0031615566.html
*/
func (self *SRegion) ImportImageJob(name string, osDist string, osVersion string, osArch string, bucket string, key string, minDiskGB int64) (string, error) {
os_version, err := stdVersion(osDist, osVersion, osArch)
log.Debugf("%s %s %s: %s.min_disk %d GB", osDist, osVersion, osArch, os_version, minDiskGB)
osVersion, err := stdVersion(osDist, osVersion, osArch)
log.Debugf("%s %s %s: %s.min_disk %d GB", osDist, osVersion, osArch, osVersion, minDiskGB)
imageUrl := fmt.Sprintf("%s:%s", bucket, key)
params := map[string]interface{}{
"name": name,
"image_url": imageUrl,
"is_config_init": true,
"is_config": true,
"min_disk": minDiskGB,
}
if len(osVersion) > 0 {
params["os_version"] = osVersion
}
resp, err := self.post(SERVICE_IMS, "cloudimages/quickimport/action", params)
if err != nil {
log.Debugln(err)
return "", errors.Wrapf(err, "import image")
}
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(name), "name")
image_url := fmt.Sprintf("%s:%s", bucket, key)
params.Add(jsonutils.NewString(image_url), "image_url")
if len(os_version) > 0 {
params.Add(jsonutils.NewString(os_version), "os_version")
}
params.Add(jsonutils.NewBool(true), "is_config_init")
params.Add(jsonutils.NewBool(true), "is_config")
params.Add(jsonutils.NewInt(minDiskGB), "min_disk")
ret, err := self.ecsClient.Images.PerformAction2("action", "", params, "")
if err != nil {
return "", err
}
return ret.GetString("job_id")
return resp.GetString("job_id")
}
func formatVersion(osDist string, osVersion string) (string, error) {
File diff suppressed because it is too large Load Diff
+22 -52
View File
@@ -15,46 +15,13 @@
package huawei
import (
"yunion.io/x/log"
"fmt"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules"
)
// ===========================================
type Interface struct {
PortState string `json:"port_state"`
FixedIPS []FixedIP `json:"fixed_ips"`
NetID string `json:"net_id"` // 网络ID. 与 SNetwork里的ID对应。统一使用这个ID
PortID string `json:"port_id"`
MACAddr string `json:"mac_addr"`
}
/*
subnet: {id: "b09877fc-90d4-4fc8-b343-e6e00cb2b233", name: "subnet-149c", cidr: "192.168.0.0/24",}
availability_zone: "cn-north-1b"
cidr: "192.168.0.0/24"
dhcp_enable: true
dnsList: ["100.125.1.250", "100.125.21.250"]
gateway_ip: "192.168.0.1"
id: "b09877fc-90d4-4fc8-b343-e6e00cb2b233"
ipv6_enable: false
name: "subnet-149c"
neutron_network_id: "b09877fc-90d4-4fc8-b343-e6e00cb2b233"
neutron_subnet_id: "81fcfaa0-8e73-4472-9eba-3b2b7736d3a7"
primary_dns: "100.125.1.250"
secondary_dns: "100.125.21.250"
status: "ACTIVE"
tags: []
vpc_id: "877f1feb-3dc8-4c2d-92e9-0d94fd7d79dd"}
*/
type FixedIP struct {
SubnetID string `json:"subnet_id"` // 子网ID, 与SNetwork中的 neutron_subnet_id对应. 注意!!! 并不是SNetwork ID。
IPAddress string `json:"ip_address"`
}
// ===========================================
type SInstanceNic struct {
instance *SInstance
ipAddr string
@@ -89,28 +56,31 @@ func (self *SInstanceNic) InClassicNetwork() bool {
}
func (self *SInstanceNic) GetINetworkId() string {
instanceId := self.instance.GetId()
subnets, err := self.instance.host.zone.region.getSubnetIdsByInstanceId(instanceId)
if err != nil || len(subnets) == 0 {
log.Errorf("getSubnetIdsByInstanceId error: %s", err.Error())
return ""
subnets, _ := self.instance.host.zone.region.getSubnetIdsByInstanceId(self.instance.GetId())
for _, id := range subnets {
return id
}
return subnets[0]
return ""
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=ListServerInterfaces
func (self *SRegion) getSubnetIdsByInstanceId(instanceId string) ([]string, error) {
ctx := &modules.SManagerContext{InstanceManager: self.ecsClient.NovaServers, InstanceId: instanceId}
interfaces := make([]Interface, 0)
err := DoListInContext(self.ecsClient.Interface.ListInContext, ctx, nil, &interfaces)
resp, err := self.list(SERVICE_ECS, fmt.Sprintf("cloudservers/%s/os-interface", instanceId), nil)
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "list os interface")
}
subnets := make([]string, 0)
for _, i := range interfaces {
subnets = append(subnets, i.NetID)
ret := struct {
InterfaceAttachments []struct {
NetId string
}
}{}
err = resp.Unmarshal(&ret)
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
subnets := []string{}
for _, att := range ret.InterfaceAttachments {
subnets = append(subnets, att.NetId)
}
return subnets, nil
}
+19 -11
View File
@@ -15,10 +15,12 @@
package huawei
import (
"net/url"
"strconv"
"yunion.io/x/pkg/errors"
)
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212656.html
type SInstanceType struct {
ID string `json:"id"`
Name string `json:"name"`
@@ -31,27 +33,33 @@ type OSExtraSpecs struct {
EcsPerformancetype string `json:"ecs:performancetype"`
}
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212656.html
func (self *SRegion) fetchInstanceTypes(zoneId string) ([]SInstanceType, error) {
querys := map[string]string{}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=ListFlavors
func (self *SRegion) GetInstanceTypes(zoneId string) ([]SInstanceType, error) {
query := url.Values{}
if len(zoneId) > 0 {
querys["availability_zone"] = zoneId
query.Set("available_zone", zoneId)
}
instanceTypes := make([]SInstanceType, 0)
err := doListAll(self.ecsClient.Flavors.List, querys, &instanceTypes)
return instanceTypes, err
resp, err := self.list(SERVICE_ECS, "cloudservers/flavors", query)
if err != nil {
return nil, errors.Wrapf(err, "list flavors")
}
ret := []SInstanceType{}
err = resp.Unmarshal(&ret, "flavors")
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
return ret, nil
}
func (self *SRegion) GetMatchInstanceTypes(cpu int, memMB int, zoneId string) ([]SInstanceType, error) {
instanceTypes, err := self.fetchInstanceTypes(zoneId)
instanceTypes, err := self.GetInstanceTypes(zoneId)
if err != nil {
return nil, err
}
ret := make([]SInstanceType, 0)
for _, t := range instanceTypes {
// cpu & mem & disk都匹配才行
// cpu & mem 都匹配才行
if t.Vcpus == strconv.Itoa(cpu) && t.RamMB == memMB {
ret = append(ret, t)
}
+3 -2
View File
@@ -45,10 +45,11 @@ func (self *SRegion) getFingerprint(publicKey string) (string, error) {
return fingerprint, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ECS/doc?api=NovaListKeypairs
func (self *SRegion) GetKeypairs() ([]SKeypair, error) {
ret := []SKeypair{}
query := url.Values{}
resp, err := self.list(SERVICE_ECS, "os-keypairs", query)
resp, err := self.list(SERVICE_ECS_V2_1, "os-keypairs", query)
if err != nil {
return nil, errors.Wrapf(err, "list os-keypairs")
}
@@ -85,7 +86,7 @@ func (self *SRegion) ImportKeypair(name, publicKey string) (*SKeypair, error) {
"name": name,
"public_key": publicKey,
}
resp, err := self.post(SERVICE_ECS, "os-keypairs", map[string]interface{}{"keypair": params})
resp, err := self.post(SERVICE_ECS_V2_1, "os-keypairs", map[string]interface{}{"keypair": params})
if err != nil {
return nil, errors.Wrapf(err, "create os-keypairs")
}
+19 -12
View File
@@ -167,7 +167,7 @@ func (self *SLoadbalancer) GetNetwork() *SNetwork {
if self.subnet == nil {
port, err := self.region.GetPort(self.VipPortId)
if err == nil {
net, err := self.region.getNetwork(port.NetworkID)
net, err := self.region.GetNetwork(port.NetworkID)
if err == nil {
self.subnet = net
} else {
@@ -225,7 +225,7 @@ func (self *SLoadbalancer) GetEgressMbps() int {
return 0
}
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0141008275.html
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=DeleteLoadBalancer
func (self *SLoadbalancer) Delete(ctx context.Context) error {
for _, res := range self.Pools {
backends, err := self.region.getLoadBalancerBackends(res.Id)
@@ -312,13 +312,12 @@ func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(opts *cloudprovider.S
return ret, err
}
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561563.html
func (self *SLoadbalancer) CreateHealthCheck(backendGroupId string, healthcheck *cloudprovider.SLoadbalancerHealthCheck) error {
_, err := self.region.CreateLoadBalancerHealthCheck(backendGroupId, healthcheck)
return err
}
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561548.html
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ShowPool
func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
ret := &SElbBackendGroup{lb: self, region: self.region}
resp, err := self.region.list(SERVICE_ELB, "elb/pools/"+groupId, nil)
@@ -337,6 +336,7 @@ func (self *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, opts
return ret, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ShowListener
func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
ret := &SElbListener{lb: self}
resp, err := self.region.list(SERVICE_ELB, "elb/listeners/"+listenerId, nil)
@@ -346,6 +346,7 @@ func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (clou
return ret, resp.Unmarshal(ret, "listener")
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ShowLoadBalancer
func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) {
resp, err := self.list(SERVICE_ELB, "elb/loadbalancers/"+id, nil)
if err != nil {
@@ -355,12 +356,14 @@ func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) {
return ret, resp.Unmarshal(ret, "loadbalancer")
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=DeleteLoadBalancer
func (self *SRegion) DeleteLoadBalancer(elbId string) error {
resource := fmt.Sprintf("elb/loadbalancers/%s", elbId)
_, err := self.delete(SERVICE_ELB, resource)
return err
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ListListeners
func (self *SRegion) GetLoadBalancerListeners(lbId string) ([]SElbListener, error) {
ret := []SElbListener{}
params := url.Values{}
@@ -370,6 +373,7 @@ func (self *SRegion) GetLoadBalancerListeners(lbId string) ([]SElbListener, erro
return ret, self.lbListAll("elb/listeners", params, "listeners", &ret)
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=CreateLoadBalancer
func (self *SRegion) CreateLoadBalancerListener(listener *cloudprovider.SLoadbalancerListenerCreateOptions, lbId string) (*SElbListener, error) {
params := map[string]interface{}{
"name": listener.Name,
@@ -408,7 +412,7 @@ func (self *SRegion) CreateLoadBalancerListener(listener *cloudprovider.SLoadbal
return ret, resp.Unmarshal(&ret, "listener")
}
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=ListPools
func (self *SRegion) GetLoadBalancerBackendGroups(elbId string) ([]SElbBackendGroup, error) {
query := url.Values{}
if len(elbId) > 0 {
@@ -419,6 +423,7 @@ func (self *SRegion) GetLoadBalancerBackendGroups(elbId string) ([]SElbBackendGr
return ret, self.lbListAll("elb/pools", query, "pools", &ret)
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=CreatePool
func (self *SRegion) CreateLoadBalancerBackendGroup(lbId string, opts *cloudprovider.SLoadbalancerBackendGroup) (*SElbBackendGroup, error) {
params := map[string]interface{}{
"name": opts.Name,
@@ -455,6 +460,7 @@ func (self *SRegion) CreateLoadBalancerBackendGroup(lbId string, opts *cloudprov
return ret, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=CreateHealthMonitor
func (self *SRegion) CreateLoadBalancerHealthCheck(backendGroupId string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) {
params := map[string]interface{}{
"delay": healthCheck.HealthCheckInterval,
@@ -486,7 +492,7 @@ func (self *SRegion) CreateLoadBalancerHealthCheck(backendGroupId string, health
return ret, resp.Unmarshal(&ret, "healthmonitor")
}
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561564.html
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=UpdateHealthMonitor
func (self *SRegion) UpdateLoadBalancerHealthCheck(healthCheckId string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) {
params := map[string]interface{}{
"delay": healthCheck.HealthCheckInterval,
@@ -515,7 +521,7 @@ func (self *SRegion) UpdateLoadBalancerHealthCheck(healthCheckId string, healthC
return ret, resp.Unmarshal(&ret, "healthmonitor")
}
// https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561565.html
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=DeleteHealthMonitor
func (self *SRegion) DeleteLoadbalancerHealthCheck(healthCheckId string) error {
_, err := self.delete(SERVICE_ELB, "elb/healthmonitors/"+healthCheckId)
return err
@@ -546,8 +552,9 @@ func (self *SRegion) lbListAll(resource string, query url.Values, respKey string
return ret.Unmarshal(retVal)
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/ELB/doc?version=v3&api=CreateLoadBalancer
func (self *SRegion) CreateLoadBalancer(opts *cloudprovider.SLoadbalancerCreateOptions) (*SLoadbalancer, error) {
subnet, err := self.getNetwork(opts.NetworkIds[0])
subnet, err := self.GetNetwork(opts.NetworkIds[0])
if err != nil {
return nil, errors.Wrap(err, "getNetwork")
}
@@ -581,13 +588,13 @@ func (self *SRegion) CreateLoadBalancer(opts *cloudprovider.SLoadbalancerCreateO
if len(opts.EipId) > 0 {
params["publicip_ids"] = []string{opts.EipId}
}
err = self.fetchZones()
zones, err := self.GetZones()
if err != nil {
return nil, errors.Wrapf(err, "fetchZones")
return nil, errors.Wrapf(err, "GetZones")
}
zoneIds := []string{}
for i := range self.izones {
zone := self.izones[i].(*SZone)
for i := range zones {
zone := zones[i]
zoneIds = append(zoneIds, zone.ZoneName)
}
params["availability_zone_list"] = zoneIds
+4 -34
View File
@@ -22,7 +22,6 @@ import (
"yunion.io/x/log"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
)
@@ -94,15 +93,13 @@ func (self *SElbBackend) GetBackendRole() string {
}
func (self *SElbBackend) GetBackendId() string {
i, err := self.lb.region.getInstanceByIP(self.Address)
vms, err := self.region.GetInstances(self.Address)
if err != nil {
log.Errorf("ElbBackend GetBackendId %s", err)
return ""
}
if i != nil {
return i.GetId()
for _, vm := range vms {
return vm.GetId()
}
return ""
}
@@ -123,33 +120,6 @@ func (self *SElbBackend) SyncConf(ctx context.Context, port, weight int) error {
return err
}
func (self *SRegion) getInstanceByIP(privateIP string) (*SInstance, error) {
queries := make(map[string]string)
if len(self.client.projectId) > 0 {
queries["project_id"] = self.client.projectId
}
if len(privateIP) > 0 {
queries["ip"] = privateIP
}
instances := make([]SInstance, 0)
err := doListAllWithOffset(self.ecsClient.Servers.List, queries, &instances)
if err != nil {
return nil, err
}
if len(instances) == 1 {
return &instances[0], nil
} else if len(instances) > 1 {
log.Warningf("SRegion.getInstanceByIP %s result: multiple server find", privateIP)
return &instances[0], nil
}
return nil, cloudprovider.ErrNotFound
}
func (self *SRegion) GetElbBackend(pool, id string) (*SElbBackend, error) {
res := fmt.Sprintf("elb/pools/%s/members/%s", pool, id)
resp, err := self.list(SERVICE_ELB, res, nil)
@@ -249,7 +249,7 @@ func (self *SElbBackendGroup) GetILoadbalancerBackendById(serverId string) (clou
}
func (self *SElbBackendGroup) AddBackendServer(serverId string, weight int, port int) (cloudprovider.ICloudLoadbalancerBackend, error) {
instance, err := self.lb.region.GetInstanceByID(serverId)
instance, err := self.lb.region.GetInstance(serverId)
if err != nil {
return nil, err
}
@@ -268,7 +268,7 @@ func (self *SElbBackendGroup) AddBackendServer(serverId string, weight int, port
return nil, fmt.Errorf("AddBackendServer %s no subnet found", serverId)
}
net, err := self.lb.region.getNetwork(subnets[0])
net, err := self.lb.region.GetNetwork(subnets[0])
if err != nil {
return nil, err
}
+3 -3
View File
@@ -156,7 +156,7 @@ func (self *SRegion) CreateIModelartsPool(args *cloudprovider.ModelartsPoolCreat
}
netId, _ = createNetObj.GetString("metadata", "name")
for i := 0; i < 10; i++ {
netDetailObj, err := self.list(SERVICE_MODELARTS, "networks/"+netId, nil)
netDetailObj, err := self.list(SERVICE_MODELARTS_V1, "networks/"+netId, nil)
if err != nil {
return nil, errors.Wrap(err, "SHuaweiClient.NetworkDetail")
}
@@ -291,7 +291,7 @@ type ModelartsStatistics struct {
}
func (self *SHuaweiClient) GetPoolNetworks(poolName string) (jsonutils.JSONObject, error) {
return self.list(SERVICE_MODELARTS, self.clientRegion, "networks", nil)
return self.list(SERVICE_MODELARTS_V1, self.clientRegion, "networks", nil)
}
func (self *SHuaweiClient) CreatePoolNetworks(cidr string) (jsonutils.JSONObject, error) {
@@ -308,7 +308,7 @@ func (self *SHuaweiClient) CreatePoolNetworks(cidr string) (jsonutils.JSONObject
"cidr": cidr,
},
}
return self.post(SERVICE_MODELARTS, self.clientRegion, "networks", params)
return self.post(SERVICE_MODELARTS_V1, self.clientRegion, "networks", params)
}
func (self *SModelartsPool) GetCreatedAt() time.Time {
+1 -1
View File
@@ -62,7 +62,7 @@ type SModelartsResourceflavorsStatus struct {
func (self *SRegion) GetIModelartsPoolSku() ([]cloudprovider.ICloudModelartsPoolSku, error) {
resourceflavors := make([]SModelartsPoolSku, 0)
obj, err := self.list(SERVICE_MODELARTS, "resourceflavors", nil)
obj, err := self.list(SERVICE_MODELARTS_V1, "resourceflavors", nil)
if err != nil {
return nil, errors.Wrap(err, "list")
}
-9
View File
@@ -25,17 +25,8 @@ import (
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules"
)
func (r *SRegion) GetMetrics() ([]modules.SMetricMeta, error) {
return r.ecsClient.CloudEye.ListMetrics()
}
func (r *SRegion) GetMetricsData(metrics []modules.SMetricMeta, since time.Time, until time.Time) ([]modules.SMetricData, error) {
return r.ecsClient.CloudEye.GetMetricsData(metrics, since, until)
}
type MetricData struct {
Namespace string
MetricName string
+30 -27
View File
@@ -15,8 +15,9 @@
package huawei
import (
"net/url"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/multicloud"
)
@@ -91,41 +92,43 @@ func (gateway *SNatGateway) getNatDTable() ([]SNatDEntry, error) {
return ret, nil
}
func (region *SRegion) GetNatDTable(natGatewayID string) ([]SNatDEntry, error) {
queuies := map[string]string{
"nat_gateway_id": natGatewayID,
func (region *SRegion) GetNatDTable(natId string) ([]SNatDEntry, error) {
query := url.Values{}
if len(natId) > 0 {
query.Set("gateway_id", natId)
}
dNatSTableEntries := make([]SNatDEntry, 0, 2)
// can't make true that restapi support marker para in Huawei Cloud
err := doListAllWithMarker(region.ecsClient.DNatRules.List, queuies, &dNatSTableEntries)
if err != nil {
return nil, errors.Wrapf(err, `get dnat rule of gateway %q`, natGatewayID)
}
for i := range dNatSTableEntries {
nat := &dNatSTableEntries[i]
if len(nat.InternalIP) == 0 {
port, err := region.GetPort(nat.PortID)
if err != nil {
return nil, errors.Wrapf(err, `get port info for transfer to ip of port_id %q error`, nat.PortID)
}
nat.InternalIP = port.FixedIps[0].IpAddress
ret := []SNatDEntry{}
for {
resp, err := region.list(SERVICE_NAT, "private-nat/dnat-rules", query)
if err != nil {
return nil, err
}
part := struct {
DnatRules []SNatDEntry
PageInfo sPageInfo
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.DnatRules...)
if len(part.PageInfo.NextMarker) == 0 || len(part.DnatRules) == 0 {
break
}
query.Set("marker", part.PageInfo.NextMarker)
}
return dNatSTableEntries, nil
return ret, nil
}
func (region *SRegion) DeleteNatDEntry(entryID string) error {
_, err := region.ecsClient.DNatRules.Delete(entryID, nil)
if err != nil {
return errors.Wrapf(err, `delete dnat rule %q failed`, entryID)
}
return nil
func (region *SRegion) DeleteNatDEntry(id string) error {
_, err := region.delete(SERVICE_NAT, "private-nat/dnat-rules/"+id)
return err
}
func (nat *SNatDEntry) Refresh() error {
new, err := nat.gateway.region.GetNatDEntryByID(nat.ID)
ret, err := nat.gateway.region.GetNatDEntryByID(nat.ID)
if err != nil {
return err
}
return jsonutils.Update(nat, new)
return jsonutils.Update(nat, ret)
}
+80 -58
View File
@@ -15,6 +15,7 @@
package huawei
import (
"net/url"
"strings"
"time"
@@ -155,7 +156,7 @@ func (gateway *SNatGateway) CreateINatDEntry(rule cloudprovider.SNatDRule) (clou
return nil, err
}
dnat.gateway = gateway
return &dnat, nil
return dnat, nil
}
func (gateway *SNatGateway) CreateINatSEntry(rule cloudprovider.SNatSRule) (cloudprovider.ICloudNatSEntry, error) {
@@ -164,7 +165,7 @@ func (gateway *SNatGateway) CreateINatSEntry(rule cloudprovider.SNatSRule) (clou
return nil, err
}
snat.gateway = gateway
return &snat, nil
return snat, nil
}
func (gateway *SNatGateway) GetINatDEntryByID(id string) (cloudprovider.ICloudNatDEntry, error) {
@@ -173,7 +174,7 @@ func (gateway *SNatGateway) GetINatDEntryByID(id string) (cloudprovider.ICloudNa
return nil, err
}
dnat.gateway = gateway
return &dnat, nil
return dnat, nil
}
func (gateway *SNatGateway) GetINatSEntryByID(id string) (cloudprovider.ICloudNatSEntry, error) {
@@ -182,29 +183,41 @@ func (gateway *SNatGateway) GetINatSEntryByID(id string) (cloudprovider.ICloudNa
return nil, err
}
snat.gateway = gateway
return &snat, nil
return snat, nil
}
func (region *SRegion) GetNatGateways(vpcID, natGatewayID string) ([]SNatGateway, error) {
queues := make(map[string]string)
if len(natGatewayID) != 0 {
queues["id"] = natGatewayID
func (region *SRegion) GetNatGateways(vpcId, id string) ([]SNatGateway, error) {
query := url.Values{}
if len(id) != 0 {
query.Set("id", id)
}
if len(vpcID) != 0 {
queues["router_id"] = vpcID
if len(vpcId) != 0 {
query.Set("vpc_id", vpcId)
}
natGateways := make([]SNatGateway, 0, 2)
err := doListAllWithMarker(region.ecsClient.NatGateways.List, queues, &natGateways)
if err != nil {
return nil, errors.Wrapf(err, "get nat gateways error by natgatewayid")
ret := []SNatGateway{}
for {
resp, err := region.list(SERVICE_NAT, "private-nat/gateways", query)
if err != nil {
return nil, err
}
part := struct {
Gateways []SNatGateway
PageInfo sPageInfo
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.Gateways...)
if len(part.PageInfo.NextMarker) == 0 || len(part.Gateways) == 0 {
break
}
query.Set("marker", part.PageInfo.NextMarker)
}
for i := range natGateways {
natGateways[i].region = region
}
return natGateways, nil
return ret, nil
}
func (region *SRegion) CreateNatDEntry(rule cloudprovider.SNatDRule, gatewayID string) (SNatDEntry, error) {
func (region *SRegion) CreateNatDEntry(rule cloudprovider.SNatDRule, gatewayID string) (*SNatDEntry, error) {
params := make(map[string]interface{})
params["nat_gateway_id"] = gatewayID
params["private_ip"] = rule.InternalIP
@@ -213,21 +226,21 @@ func (region *SRegion) CreateNatDEntry(rule cloudprovider.SNatDRule, gatewayID s
params["external_service_port"] = rule.ExternalPort
params["protocol"] = rule.Protocol
packParams := map[string]map[string]interface{}{
"dnat_rule": params,
}
ret := SNatDEntry{}
err := DoCreate(region.ecsClient.DNatRules.Create, jsonutils.Marshal(packParams), &ret)
resp, err := region.post(SERVICE_NAT, "private-nat/dnat-rules", map[string]interface{}{"dnat_rule": params})
if err != nil {
return SNatDEntry{}, errors.Wrapf(err, `create dnat rule of nat gateway %q failed`, gatewayID)
return nil, errors.Wrapf(err, "create dnat")
}
ret := &SNatDEntry{}
err = resp.Unmarshal(ret, "dnat_rule")
if err != nil {
return nil, err
}
return ret, nil
}
func (region *SRegion) CreateNatSEntry(rule cloudprovider.SNatSRule, gatewayID string) (SNatSEntry, error) {
func (region *SRegion) CreateNatSEntry(rule cloudprovider.SNatSRule, gatewayId string) (*SNatSEntry, error) {
params := make(map[string]interface{})
params["nat_gateway_id"] = gatewayID
params["nat_gateway_id"] = gatewayId
if len(rule.NetworkID) != 0 {
params["network_id"] = rule.NetworkID
}
@@ -235,36 +248,42 @@ func (region *SRegion) CreateNatSEntry(rule cloudprovider.SNatSRule, gatewayID s
params["cidr"] = rule.SourceCIDR
}
params["floating_ip_id"] = rule.ExternalIPID
packParams := map[string]map[string]interface{}{
"snat_rule": params,
}
ret := SNatSEntry{}
err := DoCreate(region.ecsClient.SNatRules.Create, jsonutils.Marshal(packParams), &ret)
resp, err := region.post(SERVICE_NAT, "private-nat/snat-rules", map[string]interface{}{"snat_rule": params})
if err != nil {
return SNatSEntry{}, errors.Wrapf(err, `create snat rule of nat gateway %q failed`, gatewayID)
return nil, errors.Wrapf(err, "create snat")
}
ret := &SNatSEntry{}
err = resp.Unmarshal(ret, "snat_rule")
if err != nil {
return nil, err
}
return ret, nil
}
func (region *SRegion) GetNatDEntryByID(id string) (SNatDEntry, error) {
dnat := SNatDEntry{}
err := DoGet(region.ecsClient.DNatRules.Get, id, map[string]string{}, &dnat)
func (region *SRegion) GetNatDEntryByID(id string) (*SNatDEntry, error) {
resp, err := region.list(SERVICE_NAT, "private-nat/dnat-rules/"+id, nil)
if err != nil {
return SNatDEntry{}, err
return nil, err
}
return dnat, nil
ret := &SNatDEntry{}
err = resp.Unmarshal(ret, "dnat_rule")
if err != nil {
return nil, err
}
return ret, nil
}
func (region *SRegion) GetNatSEntryByID(id string) (SNatSEntry, error) {
snat := SNatSEntry{}
err := DoGet(region.ecsClient.SNatRules.Get, id, map[string]string{}, &snat)
func (region *SRegion) GetNatSEntryByID(id string) (*SNatSEntry, error) {
resp, err := region.list(SERVICE_NAT, "private-nat/snat-rules/"+id, nil)
if err != nil {
return SNatSEntry{}, cloudprovider.ErrNotFound
return nil, err
}
return snat, nil
ret := &SNatSEntry{}
err = resp.Unmarshal(ret, "snat_rule")
if err != nil {
return nil, err
}
return ret, nil
}
func NatResouceStatusTransfer(status string) string {
@@ -283,13 +302,16 @@ func NatResouceStatusTransfer(status string) string {
}
func (self *SRegion) GetNatGateway(id string) (*SNatGateway, error) {
resp, err := self.ecsClient.NatGateways.Get(id, nil)
resp, err := self.list(SERVICE_NAT, "private-nat/gateways/"+id, nil)
if err != nil {
return nil, errors.Wrapf(err, "NatGateways.Get(%s)", id)
return nil, err
}
nat := &SNatGateway{region: self}
err = resp.Unmarshal(nat)
return nat, errors.Wrap(err, "resp.Unmarshal")
err = resp.Unmarshal(nat, "gateway")
if err != nil {
return nil, err
}
return nat, nil
}
func (self *SVpc) CreateINatGateway(opts *cloudprovider.NatGatewayCreateOptions) (cloudprovider.ICloudNatGateway, error) {
@@ -312,7 +334,7 @@ func (self *SRegion) CreateNatGateway(opts *cloudprovider.NatGatewayCreateOption
case api.NAT_SPEC_XLARGE:
spec = "4"
}
params := jsonutils.Marshal(map[string]map[string]interface{}{
params := map[string]interface{}{
"nat_gateway": map[string]interface{}{
"name": opts.Name,
"description": opts.Desc,
@@ -320,13 +342,13 @@ func (self *SRegion) CreateNatGateway(opts *cloudprovider.NatGatewayCreateOption
"internal_network_id": opts.NetworkId,
"spec": spec,
},
})
resp, err := self.ecsClient.NatGateways.Create(params)
}
resp, err := self.post(SERVICE_NAT, "private-nat/gateways", params)
if err != nil {
return nil, errors.Wrap(err, "AsyncCreate")
return nil, errors.Wrap(err, "create nat")
}
nat := &SNatGateway{region: self}
err = resp.Unmarshal(nat)
err = resp.Unmarshal(nat, "gateway")
if err != nil {
return nil, errors.Wrapf(err, "resp.Unmarshal")
}
@@ -334,6 +356,6 @@ func (self *SRegion) CreateNatGateway(opts *cloudprovider.NatGatewayCreateOption
}
func (self *SRegion) DeleteNatGateway(id string) error {
_, err := self.ecsClient.NatGateways.Delete(id, nil)
return errors.Wrapf(err, "NatGateways.Delete(%s)", id)
_, err := self.delete(SERVICE_NAT, "private-nat/gateways/"+id)
return err
}
+29 -28
View File
@@ -15,8 +15,9 @@
package huawei
import (
"net/url"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/cloudmux/pkg/multicloud"
)
@@ -68,7 +69,6 @@ func (nat *SNatSEntry) Delete() error {
return nat.gateway.region.DeleteNatSEntry(nat.GetId())
}
// getNatSTable return all snat rules of gateway
func (gateway *SNatGateway) getNatSTable() ([]SNatSEntry, error) {
ret, err := gateway.region.GetNatSTable(gateway.GetId())
if err != nil {
@@ -80,42 +80,43 @@ func (gateway *SNatGateway) getNatSTable() ([]SNatSEntry, error) {
return ret, nil
}
func (region *SRegion) GetNatSTable(natGatewayID string) ([]SNatSEntry, error) {
queuies := map[string]string{
"nat_gateway_id": natGatewayID,
func (region *SRegion) GetNatSTable(natId string) ([]SNatSEntry, error) {
query := url.Values{}
if len(natId) > 0 {
query.Set("gateway_id", natId)
}
sNatSTableEntris := make([]SNatSEntry, 0, 2)
err := doListAllWithMarker(region.ecsClient.SNatRules.List, queuies, &sNatSTableEntris)
if err != nil {
return nil, errors.Wrapf(err, `get snat rule of gateway %q`, natGatewayID)
}
for i := range sNatSTableEntris {
nat := &sNatSTableEntris[i]
if len(nat.SourceCIDR) != 0 {
continue
}
subnet := SNetwork{}
err := DoGet(region.ecsClient.Subnets.Get, nat.NetworkID, map[string]string{}, &subnet)
ret := []SNatSEntry{}
for {
resp, err := region.list(SERVICE_NAT, "private-nat/snat-rules", query)
if err != nil {
return nil, errors.Wrapf(err, `get cidr of subnet %q`, nat.NetworkID)
return nil, err
}
nat.SourceCIDR = subnet.CIDR
part := struct {
SnatRules []SNatSEntry
PageInfo sPageInfo
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
ret = append(ret, part.SnatRules...)
if len(part.PageInfo.NextMarker) == 0 || len(part.SnatRules) == 0 {
break
}
query.Set("marker", part.PageInfo.NextMarker)
}
return sNatSTableEntris, nil
return ret, nil
}
func (region *SRegion) DeleteNatSEntry(entryID string) error {
_, err := region.ecsClient.SNatRules.Delete(entryID, nil)
if err != nil {
return errors.Wrapf(err, `delete snat rule %q failed`, entryID)
}
return nil
func (region *SRegion) DeleteNatSEntry(id string) error {
_, err := region.delete(SERVICE_NAT, "private-nat/snat-rules/"+id)
return err
}
func (nat *SNatSEntry) Refresh() error {
new, err := nat.gateway.region.GetNatSEntryByID(nat.ID)
ret, err := nat.gateway.region.GetNatSEntryByID(nat.ID)
if err != nil {
return err
}
return jsonutils.Update(nat, new)
return jsonutils.Update(nat, ret)
}
+48 -27
View File
@@ -15,15 +15,17 @@
package huawei
import (
"fmt"
"net/url"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/rbacscope"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/cloudmux/pkg/multicloud"
"yunion.io/x/cloudmux/pkg/multicloud/huawei/client/modules"
)
/*
@@ -81,16 +83,11 @@ func (self *SNetwork) GetStatus() string {
}
func (self *SNetwork) Refresh() error {
log.Debugf("network refresh %s", self.GetId())
new, err := self.wire.region.getNetwork(self.GetId())
net, err := self.wire.vpc.region.GetNetwork(self.GetId())
if err != nil {
return err
}
return jsonutils.Update(self, new)
}
func (self *SNetwork) IsEmulated() bool {
return false
return jsonutils.Update(self, net)
}
func (self *SNetwork) GetIWire() cloudprovider.ICloudWire {
@@ -139,34 +136,58 @@ func (self *SNetwork) GetPublicScope() rbacscope.TRbacScope {
}
func (self *SNetwork) Delete() error {
return self.wire.region.deleteNetwork(self.VpcID, self.GetId())
return self.wire.vpc.region.deleteNetwork(self.VpcID, self.GetId())
}
func (self *SNetwork) GetAllocTimeoutSeconds() int {
return 120 // 2 minutes
}
func (self *SRegion) getNetwork(networkId string) (*SNetwork, error) {
network := SNetwork{}
err := DoGet(self.ecsClient.Subnets.Get, networkId, nil, &network)
return &network, err
}
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090592.html
func (self *SRegion) GetNetwroks(vpcId string) ([]SNetwork, error) {
querys := map[string]string{}
if len(vpcId) > 0 {
querys["vpc_id"] = vpcId
// https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?version=v2&api=ShowSubnet
func (self *SRegion) GetNetwork(networkId string) (*SNetwork, error) {
resp, err := self.list(SERVICE_VPC, "subnets/"+networkId, nil)
if err != nil {
return nil, errors.Wrapf(err, "show subnet")
}
networks := make([]SNetwork, 0)
err := doListAllWithMarker(self.ecsClient.Subnets.List, querys, &networks)
return networks, err
network := &SNetwork{}
err = resp.Unmarshal(&network, "subnet")
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
return network, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?version=v2&api=ListSubnets
func (self *SRegion) GetNetworks(vpcId string) ([]SNetwork, error) {
ret := []SNetwork{}
query := url.Values{}
if len(vpcId) > 0 {
query.Set("vpc_id", vpcId)
}
for {
resp, err := self.list(SERVICE_VPC, "subnets", query)
if err != nil {
return nil, err
}
part := []SNetwork{}
err = resp.Unmarshal(&part, "subnets")
if err != nil {
return nil, err
}
ret = append(ret, part...)
if len(part) == 0 {
break
}
query.Set("marker", part[len(part)-1].ID)
}
return ret, nil
}
// https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?version=v2&api=DeleteSubnet
func (self *SRegion) deleteNetwork(vpcId string, networkId string) error {
ctx := &modules.SManagerContext{InstanceId: vpcId, InstanceManager: self.ecsClient.Vpcs}
return DoDeleteWithSpec(self.ecsClient.Subnets.DeleteInContextWithSpec, ctx, networkId, "", nil, nil)
res := fmt.Sprintf("vpcs/%s/subnets/%s", vpcId, networkId)
_, err := self.delete(SERVICE_VPC, res)
return err
}
func (self *SNetwork) GetProjectId() string {
+36 -168
View File
@@ -14,180 +14,48 @@
package huawei
import (
"fmt"
"strings"
"time"
import "time"
"yunion.io/x/log"
"yunion.io/x/cloudmux/pkg/cloudprovider"
)
type SOrder struct {
ErrorCode *string `json:"error_code"` // 只有失败时才返回此参数
ErrorMsg *string `json:"error_msg"` //只有失败时才返回此参数
TotalSize int `json:"totalSize"` // 只有成功时才返回此参数
Resources []SResource `json:"resources"`
type SOrderResource struct {
Id string
ResourceId string
ExpireTime time.Time
}
type SResource struct {
ResourceID string `json:"resourceId"`
CloudServiceType string `json:"cloudServiceType"`
RegionCode string `json:"regionCode"`
ResourceType string `json:"resourceType"`
ResourceSpecCode string `json:"resourceSpecCode"`
Status int64 `json:"status"`
}
type SResourceDetail struct {
ID string `json:"id"`
Status int64 `json:"status"`
ResourceID string `json:"resource_id"`
ResourceName string `json:"resource_name"`
RegionCode string `json:"region_code"`
CloudServiceTypeCode string `json:"cloud_service_type_code"`
ResourceTypeCode string `json:"resource_type_code"`
ResourceSpecCode string `json:"resource_spec_code"`
ProjectCode string `json:"project_code"`
ProductID string `json:"product_id"`
MainResourceID string `json:"main_resource_id"`
IsMainResource int64 `json:"is_main_resource"`
ValidTime time.Time `json:"valid_time"`
ExpireTime time.Time `json:"expire_time"`
NextOperationPolicy string `json:"next_operation_policy"`
}
func (self *SRegion) getDomianId() (string, error) {
domains, err := self.client.getEnabledDomains()
if err != nil {
return "", err
func (self *SHuaweiClient) GetOrderResources() (map[string]SOrderResource, error) {
if len(self.orders) > 0 {
return self.orders, nil
}
if domains == nil || len(domains) == 0 {
return "", fmt.Errorf("GetAllResByOrderId domain is empty")
} else if len(domains) > 1 {
// not supported??
return "", fmt.Errorf("GetAllResByOrderId mutliple domain(%d) found", len(domains))
params := map[string]interface{}{
"limit": 500,
"only_main_resource": 1,
"status_list": []int{
2, 4,
},
}
return domains[0].ID, nil
}
/*
获取订单信息 https://support.huaweicloud.com/api-oce/api_order_00001.html
*/
func (self *SRegion) GetOrder(orderId string) (SOrder, error) {
var order SOrder
domain, err := self.getDomianId()
if err != nil {
return order, err
}
err = self.ecsClient.Orders.SetDomainId(domain)
if err != nil {
return order, err
}
err = DoGet(self.ecsClient.Orders.Get, orderId, nil, &order)
return order, err
}
/*
获取订单资源详情列表 https://support.huaweicloud.com/api-oce/zh-cn_topic_0084961226.html
*/
func (self *SRegion) GetOrderResources(orderId string, resource_ids []string, only_main_resource bool) ([]SResourceDetail, error) {
domain, err := self.getDomianId()
if err != nil {
return nil, err
}
err = self.ecsClient.Orders.SetDomainId(domain)
if err != nil {
return nil, err
}
resources := make([]SResourceDetail, 0)
queries := map[string]string{"customer_id": domain}
if len(orderId) > 0 {
queries["order_id"] = orderId
}
if len(resource_ids) > 0 {
queries["resource_ids"] = strings.Join(resource_ids, ",")
}
if only_main_resource {
queries["only_main_resource"] = "1"
}
err = doListAll(self.ecsClient.Orders.GetPeriodResourceList, queries, &resources)
return resources, err
}
/*
获取资源详情 https://support.huaweicloud.com/api-oce/zh-cn_topic_0084961226.html
*/
func (self *SRegion) GetOrderResourceDetail(resourceId string) (SResourceDetail, error) {
var res SResourceDetail
if len(resourceId) == 0 {
return res, fmt.Errorf("GetOrderResourceDetail resource id should not be empty")
}
resources, err := self.GetOrderResources("", []string{resourceId}, false)
if err != nil {
return res, err
}
switch len(resources) {
case 0:
return res, cloudprovider.ErrNotFound
case 1:
return resources[0], nil
default:
return res, fmt.Errorf("%d resources with id %s found, Expect 1", len(resources), resourceId)
}
}
func (self *SRegion) GetAllResByOrderId(orderId string) ([]SResource, error) {
order, err := self.GetOrder(orderId)
if err != nil {
return nil, err
}
log.Debugf("GetAllResByOrderId %#v", order.Resources)
return order.Resources, nil
}
func (self *SRegion) getAllResByType(orderId string, resourceType string) ([]SResource, error) {
res, err := self.GetAllResByOrderId(orderId)
if err != nil {
return nil, err
}
ret := make([]SResource, 0)
for i := range res {
r := res[i]
if r.ResourceType == resourceType {
ret = append(ret, r)
ret, cnt := map[string]SOrderResource{}, 0
for {
resp, err := self.post(SERVICE_BSS, "", "orders/suscriptions/resources/query", params)
if err != nil {
return nil, err
}
}
return ret, nil
}
func (self *SRegion) getAllResIdsByType(orderId string, resourceType string) ([]string, error) {
res, err := self.getAllResByType(orderId, resourceType)
if err != nil {
return nil, err
}
ids := make([]string, 0)
for _, r := range res {
if len(r.ResourceID) > 0 {
ids = append(ids, r.ResourceID)
part := struct {
Data []SOrderResource
TotalCount int
}{}
err = resp.Unmarshal(&part)
if err != nil {
return nil, err
}
cnt += len(part.Data)
for _, order := range part.Data {
ret[order.Id] = order
}
if cnt >= part.TotalCount || len(part.Data) == 0 {
break
}
params["offset"] = cnt
}
return ids, nil
self.orders = ret
return self.orders, nil
}
+38 -14
View File
@@ -15,8 +15,10 @@
package huawei
import (
"net/url"
"strings"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
api "yunion.io/x/cloudmux/pkg/apis/compute"
@@ -142,20 +144,42 @@ func (region *SRegion) GetINetworkInterfaces() ([]cloudprovider.ICloudNetworkInt
return ret, nil
}
func (self *SRegion) GetPort(portId string) (Port, error) {
port := Port{}
err := DoGet(self.ecsClient.Port.Get, portId, nil, &port)
return port, err
}
// https://support.huaweicloud.com/api-vpc/zh-cn_topic_0133195888.html
func (self *SRegion) GetPorts(instanceId string) ([]Port, error) {
ports := make([]Port, 0)
querys := map[string]string{}
if len(instanceId) > 0 {
querys["device_id"] = instanceId
// https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?version=v2&api=ShowPort
func (self *SRegion) GetPort(id string) (*Port, error) {
resp, err := self.list(SERVICE_VPC, "ports/"+id, nil)
if err != nil {
return nil, errors.Wrapf(err, "get port")
}
port := &Port{}
err = resp.Unmarshal(port, "port")
if err != nil {
return nil, err
}
return port, nil
}
err := doListAllWithMarker(self.ecsClient.Port.List, querys, &ports)
return ports, err
// https://console.huaweicloud.com/apiexplorer/#/openapi/VPC/doc?version=v2&api=ListPorts
func (self *SRegion) GetPorts(instanceId string) ([]Port, error) {
ret := []Port{}
query := url.Values{}
if len(instanceId) > 0 {
query.Set("device_id", instanceId)
}
for {
resp, err := self.list(SERVICE_VPC, "ports", query)
if err != nil {
return nil, err
}
part := []Port{}
err = resp.Unmarshal(&part, "ports")
if err != nil {
return nil, err
}
ret = append(ret, part...)
if len(part) == 0 {
break
}
query.Set("marker", part[len(part)-1].ID)
}
return ret, nil
}
+12 -9
View File
@@ -19,6 +19,7 @@ import (
"strings"
api "yunion.io/x/cloudmux/pkg/apis/compute"
"yunion.io/x/pkg/errors"
)
// https://support.huaweicloud.com/api-iam/zh-cn_topic_0057845625.html
@@ -51,18 +52,20 @@ func (self *SProject) GetHealthStatus() string {
}
func (self *SHuaweiClient) fetchProjects() ([]SProject, error) {
if self.projects != nil {
if len(self.projects) > 0 {
return self.projects, nil
}
huawei, _ := self.newGeneralAPIClient()
projects := make([]SProject, 0)
err := doListAll(huawei.Projects.List, nil, &projects)
if err == nil {
self.projects = projects
projects := []SProject{}
resp, err := self.list(SERVICE_IAM_V3, "", "auth/projects", nil)
if err != nil {
return nil, errors.Wrapf(err, "list projects")
}
return projects, err
err = resp.Unmarshal(&projects, "projects")
if err != nil {
return nil, errors.Wrapf(err, "Unmarshal")
}
self.projects = projects
return self.projects, nil
}
// obs 权限必须赋予到mos project之上

Some files were not shown because too many files have changed in this diff Show More