diff --git a/pkg/apis/compute/cloudaccount.go b/pkg/apis/compute/cloudaccount.go index 8f8a73cb5c..2cbd9948ac 100644 --- a/pkg/apis/compute/cloudaccount.go +++ b/pkg/apis/compute/cloudaccount.go @@ -513,6 +513,10 @@ type CloudaccountSyncSkusInput struct { } type CloudaccountProjectMappingInput struct { + AutoCreateProject bool `json:"auto_create_project"` + + ProjectId string `json:"project_id"` + // 同步策略Id, 若不传此参数则解绑 // 绑定同步策略要求当前云账号此刻未绑定其他同步策略 ProjectMappingId string `json:"project_mapping_id"` diff --git a/pkg/cloudcommon/db/tenantcache.go b/pkg/cloudcommon/db/tenantcache.go index f35a27155b..35451fa666 100644 --- a/pkg/cloudcommon/db/tenantcache.go +++ b/pkg/cloudcommon/db/tenantcache.go @@ -410,7 +410,7 @@ func (tenant *STenant) GetDomainId() string { }*/ func (manager *STenantCacheManager) findFirstProjectOfDomain(domainId string) (*STenant, error) { - q := manager.Query().Equals("domain_id", domainId) + q := manager.Query().Equals("domain_id", domainId).Asc("created_at") tenant := STenant{} tenant.SetModelManager(manager, &tenant) err := q.First(&tenant) diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go index 545f832d06..1f5cbbadf6 100644 --- a/pkg/compute/models/cloudaccounts.go +++ b/pkg/compute/models/cloudaccounts.go @@ -1474,6 +1474,32 @@ func (manager *SCloudaccountManager) initializeVMWareAccountId() error { return nil } +func (manager *SCloudaccountManager) initializeDefaultTenantId() error { + // init accountid + q := manager.Query().IsNullOrEmpty("tenant_id") + cloudaccounts := make([]SCloudaccount, 0) + err := db.FetchModelObjects(manager, q, &cloudaccounts) + if err != nil { + return errors.Wrap(err, "fetch empty defaullt tenant_id fail") + } + for i := range cloudaccounts { + account := cloudaccounts[i] + // auto fix accounts without default project + defaultTenant, err := db.TenantCacheManager.FindFirstProjectOfDomain(context.Background(), account.DomainId) + if err != nil { + return errors.Wrapf(err, "FindFirstProjectOfDomain(%s)", account.DomainId) + } + _, err = db.Update(&account, func() error { + account.ProjectId = defaultTenant.Id + return nil + }) + if err != nil { + return errors.Wrap(err, "db.Update for account") + } + } + return nil +} + func (manager *SCloudaccountManager) InitializeData() error { cloudproviders := []SCloudprovider{} q := CloudproviderManager.Query() @@ -1505,6 +1531,10 @@ func (manager *SCloudaccountManager) InitializeData() error { if err != nil { return errors.Wrap(err, "initializePublicScope") } + err = manager.initializeDefaultTenantId() + if err != nil { + return errors.Wrap(err, "initializeDefaultTenantId") + } return nil } @@ -1570,15 +1600,15 @@ func (account *SCloudaccount) PerformChangeOwner(ctx context.Context, userCred m return nil, errors.Wrap(httperrors.ErrForbidden, "can't change domain owner of cloudaccount, use PerformChangeProject instead") } -func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) { - if self.IsShared() { +func (account *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) { + if account.IsShared() { return nil, errors.Wrap(httperrors.ErrInvalidStatus, "cannot change owner when shared!") } project := input.ProjectId domain := input.ProjectDomainId if len(domain) == 0 { - domain = self.DomainId + domain = account.DomainId } tenant, err := db.TenantCacheManager.FetchTenantByIdOrNameInDomain(ctx, project, domain) @@ -1586,34 +1616,34 @@ func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mc return nil, httperrors.NewNotFoundError("project %s not found", project) } - if tenant.Id == self.ProjectId { + if tenant.Id == account.ProjectId { return nil, nil } - providers := self.GetCloudproviders() - if len(self.ProjectId) > 0 { + providers := account.GetCloudproviders() + if len(account.ProjectId) > 0 { if len(providers) > 0 { for i := range providers { - if providers[i].ProjectId != self.ProjectId { + if providers[i].ProjectId != account.ProjectId { return nil, errors.Wrap(httperrors.ErrConflict, "cloudproviders' project is different from cloudaccount's") } } } } - if tenant.DomainId != self.DomainId { + if tenant.DomainId != account.DomainId { // do change domainId input2 := apis.PerformChangeDomainOwnerInput{} input2.ProjectDomainId = tenant.DomainId - _, err := self.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input2) + _, err := account.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input2) if err != nil { return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformChangeOwner") } } // save project_id change - diff, err := db.Update(self, func() error { - self.ProjectId = tenant.Id + diff, err := db.Update(account, func() error { + account.ProjectId = tenant.Id return nil }) @@ -1621,7 +1651,7 @@ func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mc return nil, errors.Wrap(err, "db.Update ProjectId") } - db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred) + db.OpsLog.LogEvent(account, db.ACT_UPDATE, diff, userCred) if len(providers) > 0 { for i := range providers { @@ -1898,6 +1928,7 @@ func (account *SCloudaccount) probeAccountStatus(ctx context.Context, userCred m } } } + version := manager.GetVersion() sysInfo, err := manager.GetSysInfo() if err != nil { @@ -1921,6 +1952,7 @@ func (account *SCloudaccount) probeAccountStatus(ctx context.Context, userCred m account.Version = version account.Sysinfo = sysInfo account.IamLoginUrl = iamLoginUrl + return nil }) if err != nil { @@ -2831,28 +2863,43 @@ func (cd *SCloudaccount) GetHost2Wire(ctx context.Context, userCred mcclient.Tok } // 绑定同步策略 -func (self *SCloudaccount) PerformProjectMapping(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountProjectMappingInput) (jsonutils.JSONObject, error) { +func (account *SCloudaccount) PerformProjectMapping(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountProjectMappingInput) (jsonutils.JSONObject, error) { if len(input.ProjectMappingId) > 0 { _, err := validators.ValidateModel(userCred, ProjectMappingManager, &input.ProjectMappingId) if err != nil { - return nil, err + return nil, errors.Wrap(err, "ValidateModel") } - if len(self.ProjectMappingId) > 0 && self.ProjectMappingId != input.ProjectMappingId { - return nil, httperrors.NewInputParameterError("account %s has aleady bind project mapping %s", self.Name, self.ProjectMappingId) + /*if len(account.ProjectMappingId) > 0 && account.ProjectMappingId != input.ProjectMappingId { + return nil, httperrors.NewInputParameterError("account %s has aleady bind project mapping %s", account.Name, account.ProjectMappingId) + }*/ + if (input.EnableProjectSync == nil || !*input.EnableProjectSync) && (input.EnableResourceSync == nil || !*input.EnableResourceSync) { + return nil, errors.Wrap(httperrors.ErrInputParameter, "either enable_project_sync or enable_resource_sync must be set") } } - _, err := db.Update(self, func() error { - self.ProjectMappingId = input.ProjectMappingId + + if len(input.ProjectId) == 0 { + return nil, errors.Wrap(httperrors.ErrInputParameter, "empty project_id") + } + t, err := db.TenantCacheManager.FetchTenantByIdOrNameInDomain(ctx, input.ProjectId, account.DomainId) + if err != nil { + return nil, errors.Wrap(err, "FetchTenantByIdOrNameInDomain") + } + input.ProjectId = t.Id + + _, err = db.Update(account, func() error { + account.ProjectId = input.ProjectId + account.AutoCreateProject = input.AutoCreateProject + account.ProjectMappingId = input.ProjectMappingId if input.EnableProjectSync != nil { - self.EnableProjectSync = tristate.NewFromBool(*input.EnableProjectSync) + account.EnableProjectSync = tristate.NewFromBool(*input.EnableProjectSync) } if input.EnableResourceSync != nil { - self.EnableResourceSync = tristate.NewFromBool(*input.EnableResourceSync) + account.EnableResourceSync = tristate.NewFromBool(*input.EnableResourceSync) } return nil }) if err != nil { - return nil, err + return nil, errors.Wrap(err, "update") } return nil, refreshPmCaches() } diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 3a0b950dfd..d99cb14754 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -483,9 +483,9 @@ func createTenant(ctx context.Context, name, domainId, desc string) (string, str return domainId, projectId, nil } -func (self *SCloudaccount) getOrCreateTenant(ctx context.Context, name, domainId, projectId, desc string) (string, string, error) { +func (account *SCloudaccount) getOrCreateTenant(ctx context.Context, name, domainId, projectId, desc string) (string, string, error) { if len(domainId) == 0 { - domainId = self.DomainId + domainId = account.DomainId } ctx = context.WithValue(ctx, time.Now().String(), utils.GenRequestId(20)) uuid := stringutils.UUID4() @@ -499,8 +499,8 @@ func (self *SCloudaccount) getOrCreateTenant(ctx context.Context, name, domainId } return createTenant(ctx, name, domainId, desc) } - share := self.GetSharedInfo() - if tenant.DomainId == self.DomainId || (share.PublicScope == rbacscope.ScopeSystem || + share := account.GetSharedInfo() + if tenant.DomainId == account.DomainId || (share.PublicScope == rbacscope.ScopeSystem || (share.PublicScope == rbacscope.ScopeDomain && utils.IsInStringArray(tenant.DomainId, share.SharedDomains))) { return tenant.DomainId, tenant.Id, nil } @@ -1117,27 +1117,47 @@ func (manager *SCloudproviderManager) FetchCustomizeColumns( return rows } -func (manager *SCloudproviderManager) InitializeData() error { - // fill empty projectId with system project ID +func (manager *SCloudproviderManager) initializeDefaultTenantId() error { + // init accountid + q := manager.Query().IsNullOrEmpty("tenant_id") providers := make([]SCloudprovider, 0) - q := CloudproviderManager.Query() - q = q.Filter(sqlchemy.OR(sqlchemy.IsEmpty(q.Field("tenant_id")), sqlchemy.IsNull(q.Field("tenant_id")))) - err := db.FetchModelObjects(CloudproviderManager, q, &providers) + err := db.FetchModelObjects(manager, q, &providers) if err != nil { - log.Errorf("query cloudproviders with empty tenant_id fail %s", err) - return err + return errors.Wrap(err, "fetch empty defaullt tenant_id fail") } - for i := 0; i < len(providers); i += 1 { - _, err := db.Update(&providers[i], func() error { - providers[i].DomainId = auth.AdminCredential().GetProjectDomainId() - providers[i].ProjectId = auth.AdminCredential().GetProjectId() + for i := range providers { + provider := providers[i] + domainId := provider.DomainId + if len(domainId) == 0 { + account, err := provider.GetCloudaccount() + if err != nil { + log.Errorf("GetCloudaccount fail %s", err) + continue + } + domainId = account.DomainId + } + // auto fix accounts without default project + defaultTenant, err := db.TenantCacheManager.FindFirstProjectOfDomain(context.Background(), domainId) + if err != nil { + return errors.Wrapf(err, "FindFirstProjectOfDomain(%s)", provider.DomainId) + } + _, err = db.Update(&provider, func() error { + provider.ProjectId = defaultTenant.Id + provider.DomainId = defaultTenant.DomainId return nil }) if err != nil { - log.Errorf("update cloudprovider project fail %s", err) - return err + return errors.Wrap(err, "db.Update for account") } } + return nil +} + +func (manager *SCloudproviderManager) InitializeData() error { + err := manager.initializeDefaultTenantId() + if err != nil { + log.Errorf("initializeDefaultTenantId %s", err) + } return nil } diff --git a/pkg/compute/models/initdb.go b/pkg/compute/models/initdb.go index 933ba3c464..7d13110b7d 100644 --- a/pkg/compute/models/initdb.go +++ b/pkg/compute/models/initdb.go @@ -32,8 +32,8 @@ func InitDB() error { QuotaManager, - CloudproviderManager, CloudaccountManager, + CloudproviderManager, CloudregionManager, ZoneManager, VpcManager, diff --git a/pkg/mcclient/options/cloudaccounts.go b/pkg/mcclient/options/cloudaccounts.go index 48c472f3cc..e8000f1421 100644 --- a/pkg/mcclient/options/cloudaccounts.go +++ b/pkg/mcclient/options/cloudaccounts.go @@ -1123,13 +1123,15 @@ func (opts *SubscriptionCreateOptions) Params() (jsonutils.JSONObject, error) { type ClouaccountProjectMappingOptions struct { SCloudAccountIdOptions + ProjectId string `json:"project_id" help:"default project id"` + AutoCreateProject bool `help:"auto create project"` ProjectMappingId string `json:"project_mapping_id" help:"project mapping id"` EnableProjectSync bool EnableResourceSync bool } func (opts *ClouaccountProjectMappingOptions) Params() (jsonutils.JSONObject, error) { - return jsonutils.Marshal(map[string]string{"project_mapping_id": opts.ProjectMappingId}), nil + return jsonutils.Marshal(opts), nil } type SNutanixCloudAccountCreateOptions struct {