From afa7a650a39eb9b47d709bc3bf913885b3aa0d33 Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Thu, 28 Oct 2021 19:17:41 +0800 Subject: [PATCH] fix(region): cloud account sync optimized --- pkg/apis/compute/cloudaccount.go | 6 ++ pkg/cloudprovider/cloudprovider.go | 9 +-- pkg/cloudprovider/subaccount.go | 37 ++++++++- pkg/compute/models/cloudaccounts.go | 106 ++++++++++++++++++++++++-- pkg/mcclient/options/cloudaccounts.go | 4 +- 5 files changed, 148 insertions(+), 14 deletions(-) diff --git a/pkg/apis/compute/cloudaccount.go b/pkg/apis/compute/cloudaccount.go index eed438db8e..54c1a660ea 100644 --- a/pkg/apis/compute/cloudaccount.go +++ b/pkg/apis/compute/cloudaccount.go @@ -198,6 +198,12 @@ type CloudaccountCreateInput struct { // VMware 账号有zone属性 Zone string `json:"zone"` + + // 仅当show_sub_accounts=true并且dry_run=true时才返回sub accounts 信息, 且不会创建云账号 + ShowSubAccounts bool `json:"show_sub_accounts"` + + // swagger:ignore + SubAccounts *cloudprovider.SubAccounts } type CloudaccountShareModeInput struct { diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index 0afc492d48..8b6881a841 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -411,17 +411,16 @@ func IsSupported(provider string) bool { return ok } -func IsValidCloudAccount(cfg ProviderConfig) (string, error) { +func IsValidCloudAccount(cfg ProviderConfig) (ICloudProvider, string, error) { factory, ok := providerTable[cfg.Vendor] if ok { provider, err := factory.GetProvider(cfg) if err != nil { - return "", err + return nil, "", err } - return provider.GetAccountId(), nil - } else { - return "", ErrNoSuchProvder + return provider, provider.GetAccountId(), nil } + return nil, "", ErrNoSuchProvder } type SBaseProvider struct { diff --git a/pkg/cloudprovider/subaccount.go b/pkg/cloudprovider/subaccount.go index a6a805c12a..c8edfdc7e1 100644 --- a/pkg/cloudprovider/subaccount.go +++ b/pkg/cloudprovider/subaccount.go @@ -14,9 +14,44 @@ package cloudprovider +import ( + "reflect" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/gotypes" +) + type SSubAccount struct { - Name string + // 若Account不为空,可不传 + Name string + // 输入必填,若为空,需要指定子账号名称 Account string HealthStatus string // 云端服务健康状态。例如欠费、项目冻结都属于不健康状态。 DefaultProjectId string // 默认云订阅项目Id } + +type SubAccounts struct { + // 若输出则是全量子账号列表,若输入,代表允许同步的子账号 + Accounts []SSubAccount + // 若输出是云账号查询到的区域列表,若输入,代表允许同步的区域 + Cloudregions []struct { + // 输入必填 + Id string + Name string + Status string + } +} + +func (self SubAccounts) IsZero() bool { + return len(self.Accounts) == 0 +} + +func (self SubAccounts) String() string { + return jsonutils.Marshal(self).String() +} + +func init() { + gotypes.RegisterSerializable(reflect.TypeOf(&SubAccounts{}), func() gotypes.ISerializable { + return &SubAccounts{} + }) +} diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go index 97cc5810f1..77281ee7ef 100644 --- a/pkg/compute/models/cloudaccounts.go +++ b/pkg/compute/models/cloudaccounts.go @@ -165,6 +165,9 @@ type SCloudaccount struct { vmwareHostWireCache map[string][]SVs2Wire SProjectMappingResourceBase + + // 设置允许同步的账号及订阅 + SubAccounts *cloudprovider.SubAccounts `nullable:"true" get:"user" create:"optional"` } func (self *SCloudaccount) GetCloudproviders() []SCloudprovider { @@ -323,7 +326,7 @@ func (self *SCloudaccount) ValidateUpdateData( // updated proxy setting, so do the check proxyFunc := proxySetting.HttpTransportProxyFunc() secret, _ := self.getPassword() - _, err := cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{ + _, _, err := cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{ Vendor: self.Provider, URL: self.AccessUrl, Account: self.Account, @@ -500,7 +503,8 @@ func (manager *SCloudaccountManager) validateCreateData( } proxyFunc = proxySetting.HttpTransportProxyFunc() } - accountId, err := cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{ + provider, accountId, err := cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{ + Name: input.Name, Vendor: input.Provider, URL: input.AccessUrl, Account: input.Account, @@ -515,6 +519,27 @@ func (manager *SCloudaccountManager) validateCreateData( } return input, httperrors.NewGeneralError(err) } + if input.DryRun && input.ShowSubAccounts { + input.SubAccounts = &cloudprovider.SubAccounts{} + input.SubAccounts.Accounts, err = provider.GetSubAccounts() + if err != nil { + return input, err + } + regions := provider.GetIRegions() + for _, region := range regions { + input.SubAccounts.Cloudregions = append(input.SubAccounts.Cloudregions, struct { + Id string + Name string + Status string + }{ + Id: region.GetGlobalId(), + Name: region.GetName(), + Status: region.GetStatus(), + }) + } + } + + log.Errorf("subaccounts: %s", jsonutils.Marshal(input.SubAccounts).PrettyString()) // check accountId uniqueness if len(accountId) > 0 { @@ -657,7 +682,7 @@ func (self *SCloudaccount) PerformTestConnectivity(ctx context.Context, userCred return nil, err } - _, err = cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{ + _, _, err = cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{ URL: self.AccessUrl, Vendor: self.Provider, Account: account.Account, @@ -736,7 +761,7 @@ func (self *SCloudaccount) PerformUpdateCredential(ctx context.Context, userCred } } - accountId, err := cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{ + _, accountId, err := cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{ Vendor: self.Provider, URL: self.AccessUrl, Account: account.Account, @@ -2023,6 +2048,74 @@ func (account *SCloudaccount) importAllSubaccounts(ctx context.Context, userCred return existProviders } +func (self *SCloudaccount) setSubAccountStatus() error { + if self.SubAccounts == nil || len(self.SubAccounts.Accounts) == 0 || len(self.SubAccounts.Cloudregions) == 0 { + return nil + } + accounts := []string{} + accountNames := []string{} + regionIds := []string{} + for _, account := range self.SubAccounts.Accounts { + if len(account.Account) > 0 { + accounts = append(accounts, account.Account) + } else if len(account.Name) > 0 { + accountNames = append(accountNames, account.Name) + } + } + for _, region := range self.SubAccounts.Cloudregions { + if len(region.Id) > 0 { + regionIds = append(regionIds, region.Id) + } + } + + q := CloudproviderRegionManager.Query() + providerQ := CloudproviderManager.Query().SubQuery() + conditions := []sqlchemy.ICondition{} + if len(accounts) > 0 { + conditions = append(conditions, sqlchemy.In(providerQ.Field("account"), accounts)) + } + if len(accountNames) > 0 { + conditions = append(conditions, sqlchemy.In(providerQ.Field("name"), accountNames)) + } + q = q.Join(providerQ, sqlchemy.Equals(providerQ.Field("id"), q.Field("cloudprovider_id"))).Filter( + sqlchemy.NOT( + sqlchemy.OR( + conditions..., + ), + ), + ) + accountQ := CloudaccountManager.Query().SubQuery() + q = q.Join(accountQ, sqlchemy.Equals(providerQ.Field("cloudaccount_id"), accountQ.Field("id"))).Filter( + sqlchemy.Equals(accountQ.Field("id"), self.Id), + ) + regionQ := CloudregionManager.Query().SubQuery() + q = q.Join(regionQ, sqlchemy.Equals(regionQ.Field("id"), q.Field("cloudregion_id"))).Filter( + sqlchemy.NOT( + sqlchemy.In(regionQ.Field("external_id"), regionIds), + ), + ) + + cpcrs := []SCloudproviderregion{} + err := db.FetchModelObjects(CloudproviderRegionManager, q, &cpcrs) + if err != nil { + return errors.Wrapf(err, "db.FetchModelObjects") + } + + for i := range cpcrs { + db.Update(&cpcrs[i], func() error { + cpcrs[i].Enabled = false + return nil + }) + } + + _, err = db.Update(self, func() error { + self.SubAccounts = nil + return nil + }) + + return err +} + func (account *SCloudaccount) syncAccountStatus(ctx context.Context, userCred mcclient.TokenCredential) error { account.MarkSyncing(userCred) subaccounts, err := account.probeAccountStatus(ctx, userCred) @@ -2037,12 +2130,11 @@ func (account *SCloudaccount) syncAccountStatus(ctx context.Context, userCred mc if providers[i].GetEnabled() { _, err := providers[i].prepareCloudproviderRegions(ctx, userCred) if err != nil { - log.Errorf("syncCloudproviderRegion fail %s", err) - return errors.Wrap(err, "providers[i].prepareCloudproviderRegions") + return errors.Wrapf(err, "prepareCloudproviderRegions for provider %s", providers[i].Name) } } } - return nil + return account.setSubAccountStatus() } func (account *SCloudaccount) markAutoSync(userCred mcclient.TokenCredential) error { diff --git a/pkg/mcclient/options/cloudaccounts.go b/pkg/mcclient/options/cloudaccounts.go index 1fb4573576..ebbc93496d 100644 --- a/pkg/mcclient/options/cloudaccounts.go +++ b/pkg/mcclient/options/cloudaccounts.go @@ -110,7 +110,9 @@ type SCloudAccountCreateBaseOptions struct { SamlAuth string `help:"Enable or disable saml auth" choices:"true|false"` - ProxySetting string `help:"proxy setting id or name" json:"proxy_setting"` + ProxySetting string `help:"proxy setting id or name" json:"proxy_setting"` + DryRun bool `help:"test create cloudaccount params"` + ShowSubAccounts bool `help:"test and show subaccount info"` } type SVMwareCloudAccountCreateOptions struct {