diff --git a/pkg/compute/guestdrivers/esxi.go b/pkg/compute/guestdrivers/esxi.go index dd5562f28e..62a585f3a5 100644 --- a/pkg/compute/guestdrivers/esxi.go +++ b/pkg/compute/guestdrivers/esxi.go @@ -418,7 +418,7 @@ func (self *SESXiGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gues return errors.Wrapf(err, "GetExternalProjectsByProjectIdOrName(%s,%s)", project.Id, project.Name) } - extProj := account.GetAvailableExternalProject(project, projects) + extProj := models.GetAvailableExternalProject(project, projects) if extProj != nil { config.Add(jsonutils.NewString(extProj.Name), "desc", "resource_pool") } else { diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go index 11eab645a0..23a550a534 100644 --- a/pkg/compute/models/cloudaccounts.go +++ b/pkg/compute/models/cloudaccounts.go @@ -2574,7 +2574,7 @@ func (account *SCloudaccount) GetUsages() []db.IUsage { } } -func (self *SCloudaccount) GetAvailableExternalProject(local *db.STenant, projects []SExternalProject) *SExternalProject { +func GetAvailableExternalProject(local *db.STenant, projects []SExternalProject) *SExternalProject { var ret *SExternalProject = nil for i := 0; i < len(projects); i++ { if projects[i].Status == api.EXTERNAL_PROJECT_STATUS_AVAILABLE { @@ -2615,7 +2615,7 @@ func (self *SCloudaccount) SyncProject(ctx context.Context, userCred mcclient.To return "", errors.Wrapf(err, "GetExternalProjectsByProjectIdOrName(%s,%s)", projectId, project.Name) } - extProj := self.GetAvailableExternalProject(project, projects) + extProj := GetAvailableExternalProject(project, projects) if extProj != nil { return extProj.ExternalId, nil } diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 6d1ce20100..d74e8b4de7 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -1735,6 +1735,9 @@ func (provider *SCloudprovider) GetChangeOwnerCandidateDomainIds() []string { } func (self *SCloudprovider) SyncProject(ctx context.Context, userCred mcclient.TokenCredential, id string) (string, error) { + if self.Provider == api.CLOUD_PROVIDER_AZURE { + return self.SyncAzureProject(ctx, userCred, id) + } account, err := self.GetCloudaccount() if err != nil { return "", errors.Wrapf(err, "GetCloudaccount") @@ -1742,6 +1745,95 @@ func (self *SCloudprovider) SyncProject(ctx context.Context, userCred mcclient.T return account.SyncProject(ctx, userCred, id) } +func (self *SCloudprovider) GetExternalProjectsByProjectIdOrName(projectId, name string) ([]SExternalProject, error) { + projects := []SExternalProject{} + q := ExternalProjectManager.Query().Equals("manager_id", self.Id) + q = q.Filter( + sqlchemy.OR( + sqlchemy.Equals(q.Field("name"), name), + sqlchemy.Equals(q.Field("tenant_id"), projectId), + ), + ) + err := db.FetchModelObjects(ExternalProjectManager, q, &projects) + if err != nil { + return nil, errors.Wrap(err, "db.FetchModelObjects") + } + return projects, nil +} + +func (self *SCloudprovider) SyncAzureProject(ctx context.Context, userCred mcclient.TokenCredential, id string) (string, error) { + lockman.LockRawObject(ctx, "projects", self.Id) + defer lockman.ReleaseRawObject(ctx, "projects", self.Id) + + account, err := self.GetCloudaccount() + if err != nil { + return "", errors.Wrapf(err, "GetCloudaccount") + } + + provider, err := self.GetProvider(ctx) + if err != nil { + return "", errors.Wrap(err, "GetProvider") + } + + project, err := db.TenantCacheManager.FetchTenantById(ctx, id) + if err != nil { + return "", errors.Wrapf(err, "FetchTenantById(%s)", id) + } + + projects, err := self.GetExternalProjectsByProjectIdOrName(id, project.Name) + if err != nil { + return "", errors.Wrapf(err, "GetExternalProjectsByProjectIdOrName(%s,%s)", id, project.Name) + } + + extProj := GetAvailableExternalProject(project, projects) + if extProj != nil { + idx := strings.Index(extProj.ExternalId, "/") + if idx > -1 { + return extProj.ExternalId[idx+1:], nil + } + return extProj.ExternalId, nil + } + + retry := 1 + if len(projects) > 0 { + retry = 10 + } + + var iProject cloudprovider.ICloudProject = nil + projectName := project.Name + for i := 0; i < retry; i++ { + iProject, err = provider.CreateIProject(projectName) + if err == nil { + break + } + projectName = fmt.Sprintf("%s-%d", project.Name, i) + } + if err != nil { + if errors.Cause(err) != cloudprovider.ErrNotImplemented && errors.Cause(err) != cloudprovider.ErrNotSupported { + logclient.AddSimpleActionLog(self, logclient.ACT_CREATE, err, userCred, false) + } + return "", errors.Wrapf(err, "CreateIProject(%s)", projectName) + } + + extProj, err = ExternalProjectManager.newFromCloudProject(ctx, userCred, account, project, iProject) + if err != nil { + return "", errors.Wrap(err, "newFromCloudProject") + } + + db.Update(extProj, func() error { + extProj.ManagerId = self.Id + return nil + }) + + idx := strings.Index(extProj.ExternalId, "/") + if idx > -1 { + return extProj.ExternalId[idx+1:], nil + } + + return extProj.ExternalId, nil + +} + func (self *SCloudprovider) GetSchedtags() []SSchedtag { return GetSchedtags(CloudproviderschedtagManager, self.Id) } diff --git a/pkg/multicloud/azure/azure.go b/pkg/multicloud/azure/azure.go index 135924830a..6022e38b2b 100644 --- a/pkg/multicloud/azure/azure.go +++ b/pkg/multicloud/azure/azure.go @@ -976,29 +976,27 @@ func getResourceGroup(id string) string { } } if idx > 0 && idx < len(info)-1 { - return info[idx] + return fmt.Sprintf("%s/%s", info[1], info[idx]) } return "" } func (self *SAzureClient) GetIProjects() ([]cloudprovider.ICloudProject, error) { subscriptionId := self.subscriptionId - groups := map[string]*SResourceGroup{} + groups := []SResourceGroup{} for _, sub := range self.subscriptions { self.subscriptionId = sub.SubscriptionId resourceGroups, err := self.ListResourceGroups() if err != nil { return nil, errors.Wrapf(err, "ListResourceGroups") } - for i := range resourceGroups { - groups[strings.ToLower(resourceGroups[i].Name)] = &resourceGroups[i] - } + groups = append(groups, resourceGroups...) } self.subscriptionId = subscriptionId iprojects := []cloudprovider.ICloudProject{} - for _, group := range groups { - group.client = self - iprojects = append(iprojects, group) + for i := range groups { + groups[i].client = self + iprojects = append(iprojects, &groups[i]) } return iprojects, nil } diff --git a/pkg/multicloud/azure/region.go b/pkg/multicloud/azure/region.go index e0642eccee..8e68f2f4c8 100644 --- a/pkg/multicloud/azure/region.go +++ b/pkg/multicloud/azure/region.go @@ -638,12 +638,16 @@ func (self *SRegion) checkResourceGroup(resourceGroup string) (string, error) { if len(resourceGroup) == 0 { resourceGroup = "Default" } - projs, err := self.client.GetIProjects() + resourceGroups, err := self.client.ListResourceGroups() if err != nil { - return "", errors.Wrapf(err, "GetIProjects") + return "", errors.Wrapf(err, "ListResourceGroups") } - for _, proj := range projs { - if strings.ToLower(proj.GetGlobalId()) == strings.ToLower(resourceGroup) { + + for i := range resourceGroups { + proj := resourceGroups[i] + if strings.ToLower(proj.GetName()) == strings.ToLower(resourceGroup) || + proj.GetGlobalId() == resourceGroup || + (strings.Contains(resourceGroup, "/") && strings.HasSuffix(proj.GetGlobalId(), resourceGroup)) { return resourceGroup, nil } } @@ -680,7 +684,7 @@ func (self *SRegion) create(resourceGroup string, _body jsonutils.JSONObject, re } resourceGroup, err = self.checkResourceGroup(resourceGroup) if err != nil { - return errors.Wrapf(err, "checkResourceGroup") + return errors.Wrapf(err, "checkResourceGroup(%s)", resourceGroup) } info.Name, err = self.client.getUniqName(resourceGroup, info.Type, info.Name) if err != nil { diff --git a/pkg/multicloud/azure/resourcegroup.go b/pkg/multicloud/azure/resourcegroup.go index c57f531dbd..863846dd15 100644 --- a/pkg/multicloud/azure/resourcegroup.go +++ b/pkg/multicloud/azure/resourcegroup.go @@ -43,21 +43,21 @@ type SResourceGroup struct { } func (self *SRegion) GetResourceGroupDetail(groupName string) (*SResourceGroup, error) { - resourceGroup := SResourceGroup{} + resourceGroup := SResourceGroup{client: self.client, subId: self.client._subscriptionId()} idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.client._subscriptionId(), groupName) return &resourceGroup, self.get(idStr, url.Values{}, &resourceGroup) } // not support update, resource group name is immutable??? func (self *SRegion) UpdateResourceGroup(groupName string, newName string) error { - resourceGroup := SResourceGroup{Name: newName} + resourceGroup := SResourceGroup{Name: newName, client: self.client, subId: self.client._subscriptionId()} resource := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.client.subscriptionId, groupName) _, err := self.client.patch(resource, jsonutils.Marshal(&resourceGroup)) return err } func (self *SRegion) CreateResourceGroup(groupName string) (jsonutils.JSONObject, error) { - resourceGroup := SResourceGroup{Location: self.Name} + resourceGroup := SResourceGroup{Location: self.Name, client: self.client, subId: self.client._subscriptionId()} idStr := fmt.Sprintf("subscriptions/%s/resourcegroups/%s", self.client._subscriptionId(), groupName) return self.client.put(idStr, jsonutils.Marshal(resourceGroup)) } @@ -80,7 +80,7 @@ func (self *SResourceGroup) GetAccountId() string { } func (r *SResourceGroup) GetGlobalId() string { - return strings.ToLower(r.Name) + return strings.ToLower(fmt.Sprintf("%s/%s", r.subId, r.Name)) } func (r *SResourceGroup) GetStatus() string {