diff --git a/cmd/climc/shell/cloudaccounts.go b/cmd/climc/shell/cloudaccounts.go new file mode 100644 index 0000000000..0b2dd60c95 --- /dev/null +++ b/cmd/climc/shell/cloudaccounts.go @@ -0,0 +1,213 @@ +package shell + +import ( + "yunion.io/x/jsonutils" + + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/mcclient/options" +) + +func init() { + + type CloudaccountListOptions struct { + options.BaseListOptions + } + R(&CloudaccountListOptions{}, "cloud-account-list", "List cloud accounts", func(s *mcclient.ClientSession, args *CloudaccountListOptions) error { + var params *jsonutils.JSONDict + { + var err error + params, err = args.BaseListOptions.Params() + if err != nil { + return err + } + } + result, err := modules.Cloudaccounts.List(s, params) + if err != nil { + return err + } + printList(result, modules.Cloudaccounts.GetColumns(s)) + return nil + }) + + type CloudaccountCreateOptions struct { + NAME string `help:"Name of cloud account"` + ACCOUNT string `help:"Account to access the cloud account"` + SECRET string `help:"Secret to access the cloud account, clientId/clientScret for Azure"` + PROVIDER string `help:"Driver for cloud account" choices:"VMware|Aliyun|Azure|Qcloud"` + AccessURL string `helo:"hello" metavar:"Azure choices: "` + Desc string `help:"Description"` + Enabled bool `help:"Enabled the account automatically"` + EnableProject bool `help:"Enable the account with same name project"` + Import bool `help:"Import all sub account automatically"` + } + R(&CloudaccountCreateOptions{}, "cloud-account-create", "Create a cloud account", func(s *mcclient.ClientSession, args *CloudaccountCreateOptions) error { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(args.NAME), "name") + params.Add(jsonutils.NewString(args.ACCOUNT), "account") + params.Add(jsonutils.NewString(args.SECRET), "secret") + params.Add(jsonutils.NewString(args.PROVIDER), "provider") + if args.Enabled { + params.Add(jsonutils.JSONTrue, "enabled") + } + if args.EnableProject { + params.Add(jsonutils.JSONTrue, "enable_project") + } + if args.Import { + params.Add(jsonutils.JSONTrue, "import") + } + if len(args.AccessURL) > 0 { + params.Add(jsonutils.NewString(args.AccessURL), "access_url") + } + if len(args.Desc) > 0 { + params.Add(jsonutils.NewString(args.Desc), "description") + } + result, err := modules.Cloudaccounts.Create(s, params) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type CloudaccountUpdateOptions struct { + ID string `help:"ID or Name of cloud account"` + Name string `help:"New name to update"` + AccessUrl string `help:"New access url"` + Desc string `help:"Description"` + } + R(&CloudaccountUpdateOptions{}, "cloud-account-update", "Update a cloud account", func(s *mcclient.ClientSession, args *CloudaccountUpdateOptions) error { + params := jsonutils.NewDict() + if len(args.Name) > 0 { + params.Add(jsonutils.NewString(args.Name), "name") + } + if len(args.AccessUrl) > 0 { + params.Add(jsonutils.NewString(args.AccessUrl), "access_url") + } + if len(args.Desc) > 0 { + params.Add(jsonutils.NewString(args.Desc), "description") + } + if params.Size() == 0 { + return InvalidUpdateError() + } + result, err := modules.Cloudaccounts.Update(s, args.ID, params) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type CloudaccountShowOptions struct { + ID string `help:"ID or Name of cloud account"` + } + R(&CloudaccountShowOptions{}, "cloud-account-show", "Get details of a cloud account", func(s *mcclient.ClientSession, args *CloudaccountShowOptions) error { + result, err := modules.Cloudaccounts.Get(s, args.ID, nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + + R(&CloudaccountShowOptions{}, "cloud-account-delete", "Delete a cloud account", func(s *mcclient.ClientSession, args *CloudaccountShowOptions) error { + result, err := modules.Cloudaccounts.Delete(s, args.ID, nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + + R(&CloudaccountShowOptions{}, "cloud-account-enable", "Enable cloud account", func(s *mcclient.ClientSession, args *CloudaccountShowOptions) error { + result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "enable", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + + R(&CloudaccountShowOptions{}, "cloud-account-disable", "Disable cloud account", func(s *mcclient.ClientSession, args *CloudaccountShowOptions) error { + result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "disable", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + + R(&CloudaccountShowOptions{}, "cloud-account-balance", "Get balance", func(s *mcclient.ClientSession, args *CloudaccountShowOptions) error { + result, err := modules.Cloudaccounts.GetSpecific(s, args.ID, "balance", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type CloudaccountImportOptions struct { + ID string `help:"ID or Name of cloud account"` + Enabled bool `help:"Import sub accounts with enabled status"` + EnableProject bool `help:"Import sub account with project"` + } + R(&CloudaccountImportOptions{}, "cloud-account-import", "Import sub cloud account", func(s *mcclient.ClientSession, args *CloudaccountImportOptions) error { + result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "import", jsonutils.Marshal(map[string]bool{"enabled": args.Enabled, "enable_project": args.EnableProject})) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type CloudaccountUpdateCredentialOptions struct { + ID string `help:"ID or Name of cloud account"` + ACCOUNT string `help:"new account"` + SECRET string `help:"new secret"` + } + R(&CloudaccountUpdateCredentialOptions{}, "cloud-account-update-credential", "Update credential of a cloud account", func(s *mcclient.ClientSession, args *CloudaccountUpdateCredentialOptions) error { + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(args.ACCOUNT), "account") + params.Add(jsonutils.NewString(args.SECRET), "secret") + + result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "update-credential", params) + if err != nil { + return err + } + printObject(result) + return nil + }) + + type CloudaccountSyncOptions struct { + ID string `help:"ID or Name of cloud account"` + Force bool `help:"Force sync no matter what"` + FullSync bool `help:"Synchronize everything"` + Region []string `help:"region to sync"` + Zone []string `help:"region to sync"` + Host []string `help:"region to sync"` + } + R(&CloudaccountSyncOptions{}, "cloud-account-sync", "Sync of a cloud account account", func(s *mcclient.ClientSession, args *CloudaccountSyncOptions) error { + params := jsonutils.NewDict() + if args.Force { + params.Add(jsonutils.JSONTrue, "force") + } + if args.FullSync { + params.Add(jsonutils.JSONTrue, "full_sync") + } + if len(args.Region) > 0 { + params.Add(jsonutils.NewStringArray(args.Region), "region") + } + if len(args.Zone) > 0 { + params.Add(jsonutils.NewStringArray(args.Zone), "zone") + } + if len(args.Host) > 0 { + params.Add(jsonutils.NewStringArray(args.Host), "host") + } + result, err := modules.Cloudaccounts.PerformAction(s, args.ID, "sync", params) + if err != nil { + return err + } + printObject(result) + return nil + }) +} diff --git a/cmd/climc/shell/cloudproviders.go b/cmd/climc/shell/cloudproviders.go index f8eb5bf518..9ac65dfcf0 100644 --- a/cmd/climc/shell/cloudproviders.go +++ b/cmd/climc/shell/cloudproviders.go @@ -91,6 +91,19 @@ func init() { return nil }) + type CloudproviderChangeProjectOptions struct { + ID string `help:"ID or Name of cloud provider"` + TENANT string `help:"ID or Name of tenant"` + } + R(&CloudproviderChangeProjectOptions{}, "cloud-provider-change-project", "Change project for provider", func(s *mcclient.ClientSession, args *CloudproviderChangeProjectOptions) error { + result, err := modules.Cloudproviders.PerformAction(s, args.ID, "change-project", jsonutils.Marshal(map[string]string{"project": args.TENANT})) + if err != nil { + return err + } + printObject(result) + return nil + }) + type CloudproviderShowOptions struct { ID string `help:"ID or Name of cloud provider"` } diff --git a/pkg/compute/handlers.go b/pkg/compute/handlers.go index f44a9ac639..b3ba3f50a9 100644 --- a/pkg/compute/handlers.go +++ b/pkg/compute/handlers.go @@ -41,6 +41,7 @@ func InitHandlers(app *appsrv.Application) { for _, manager := range []db.IModelManager{ db.OpsLog, + models.CloudaccountManager, models.CloudproviderManager, models.CloudregionManager, models.ZoneManager, diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go new file mode 100644 index 0000000000..f2aff8ad1d --- /dev/null +++ b/pkg/compute/models/cloudaccounts.go @@ -0,0 +1,502 @@ +package models + +import ( + "context" + "database/sql" + "fmt" + "strings" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/util/timeutils" + "yunion.io/x/pkg/utils" + "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SCloudaccountManager struct { + db.SEnabledStatusStandaloneResourceBaseManager + SInfrastructureManager +} + +var CloudaccountManager *SCloudaccountManager + +func init() { + CloudaccountManager = &SCloudaccountManager{SEnabledStatusStandaloneResourceBaseManager: db.NewEnabledStatusStandaloneResourceBaseManager(SCloudaccount{}, "cloudaccounts_tbl", "cloudaccount", "cloudaccounts")} +} + +type SCloudaccount struct { + db.SEnabledStatusStandaloneResourceBase + SInfrastructure + + AccessUrl string `width:"64" charset:"ascii" nullable:"true" list:"admin" update:"admin" create:"admin_optional"` + + Account string `width:"128" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` // Column(VARCHAR(64, charset='ascii'), nullable=False) + Secret string `width:"256" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` // Column(VARCHAR(256, charset='ascii'), nullable=False) + + BalanceKey string `width:"256" charset:"ascii" nullable:"true" list:"admin" create:"admin_optional"` + LastSync time.Time `get:"admin" list:"admin"` // = Column(DateTime, nullable=True) + + Version string `width:"32" charset:"ascii" nullable:"true" list:"admin"` // Column(VARCHAR(32, charset='ascii'), nullable=True) + + Sysinfo jsonutils.JSONObject `get:"admin"` // Column(JSONEncodedDict, nullable=True) + + Provider string `width:"64" charset:"ascii" list:"admin" create:"admin_required"` +} + +func (self *SCloudaccount) GetCloudproviders() []SCloudprovider { + cloudproviders := []SCloudprovider{} + q := CloudproviderManager.Query().Equals("cloudaccount_id", self.Id) + if err := db.FetchModelObjects(CloudproviderManager, q, &cloudproviders); err != nil { + log.Errorf("getCloudproviders error: %v", err) + } + return cloudproviders +} + +func (self *SCloudaccount) ValidateDeleteCondition(ctx context.Context) error { + if self.Enabled { + return httperrors.NewInvalidStatusError("account is enabled") + } + if len(self.GetCloudproviders()) > 0 { + return httperrors.NewNotEmptyError("Not an empty cloud account") + } + return self.SEnabledStatusStandaloneResourceBase.ValidateDeleteCondition(ctx) +} + +func (self *SCloudaccount) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return self.SEnabledStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, data) +} + +func (self *SCloudaccountManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + // check provider + name, _ := data.GetString("name") + provider, _ := data.GetString("provider") + if !cloudprovider.IsSupported(provider) { + return nil, httperrors.NewInputParameterError("Unsupported provider %s", provider) + } + // check duplication + // url, account, provider must be unique + account, _ := data.GetString("account") + secret, _ := data.GetString("secret") + url, _ := data.GetString("access_url") + q := self.Query().Equals("provider", provider) + if len(account) > 0 { + q = q.Equals("account", account) + } + if len(url) > 0 { + q = q.Equals("access_url", url) + } + if q.Count() > 0 { + return nil, httperrors.NewConflictError("The account has been registered") + } + + if subAccount, err := GetSubAccounts(name, url, account, secret, provider); err != nil { + return nil, httperrors.NewInputParameterError("Invalidate account with secret") + } else if accounts, err := subAccount.GetArray("data"); err != nil { + return nil, err + } else { + data.Add(jsonutils.NewArray(accounts...), "accounts") + } + return self.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data) +} + +func (self *SCloudaccount) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) { + self.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerProjId, query, data) + self.savePassword(self.Secret) + _import, _ := data.Bool("import") + enableProject, _ := data.Bool("enable_project") + if subAccounts, err := data.GetArray("accounts"); err == nil && _import && len(subAccounts) > 0 { + for _, subAccount := range subAccounts { + name, _ := subAccount.GetString("name") + account, _ := subAccount.GetString("account") + if len(name) > 0 && len(account) > 0 { + if q := CloudproviderManager.Query().Equals("cloudaccount_id", self.Id).Equals("account", account); q.Count() > 0 { + log.Errorf("sub account conflict") + } else { + newCloudprovider := SCloudprovider{ + Account: account, + CloudaccountId: self.Id, + Provider: self.Provider, + } + newCloudprovider.SetModelManager(CloudproviderManager) + if err := CloudproviderManager.TableSpec().Insert(&newCloudprovider); err != nil { + log.Errorf("Create cloudprovider error: %v", err) + } else if _, err := CloudproviderManager.TableSpec().Update(&newCloudprovider, func() error { + newCloudprovider.Name = name + return nil + }); err != nil { + log.Errorf("Update cloudprovider error: %v", err) + } + if enableProject { + newCloudprovider.SyncProject() + } + } + } + } + } + + if self.Enabled { + self.startSyncCloudProviderInfoTask(ctx, userCred, nil, "") + } +} + +func (self *SCloudaccount) savePassword(secret string) error { + sec, err := utils.EncryptAESBase64(self.Id, secret) + if err != nil { + return err + } + + _, err = self.GetModelManager().TableSpec().Update(self, func() error { + self.Secret = sec + return nil + }) + return err +} + +func (self *SCloudaccount) getPassword() (string, error) { + return utils.DescryptAESBase64(self.Id, self.Secret) +} + +func (self *SCloudaccount) CanSync() bool { + if self.Status == CLOUD_PROVIDER_SYNCING { + if self.LastSync.IsZero() || time.Now().Sub(self.LastSync) > 900*time.Second { + return true + } else { + return false + } + } else { + return true + } +} + +func (self *SCloudaccount) AllowPerformSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SCloudaccount) PerformSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !self.Enabled { + return nil, httperrors.NewInvalidStatusError("Account disabled") + } + syncRange := SSyncRange{} + err := data.Unmarshal(&syncRange) + if err != nil { + return nil, httperrors.NewInputParameterError("invalid input %s", err) + } + if self.CanSync() || syncRange.Force { + err = self.startSyncCloudProviderInfoTask(ctx, userCred, &syncRange, "") + } + return nil, err +} + +func (self *SCloudaccount) AllowPerformUpdateCredential(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SCloudaccount) PerformUpdateCredential(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if !self.Enabled { + return nil, httperrors.NewInvalidStatusError("Account disabled") + } + + var err error + changed := false + secret, _ := data.GetString("secret") + account, _ := data.GetString("account") + accessUrl, _ := data.GetString("access_url") + if len(secret) > 0 || len(account) > 0 || len(accessUrl) > 0 { + // check duplication + q := self.GetModelManager().Query() + q = q.Equals("access_url", accessUrl) + q = q.Equals("account", account) + q = q.NotEquals("id", self.Id) + if q.Count() > 0 { + return nil, httperrors.NewConflictError("Access url and account conflict") + } + } + if len(secret) > 0 { + err = self.savePassword(secret) + if err != nil { + return nil, err + } + changed = true + } + if (len(account) > 0 && account != self.Account) || (len(accessUrl) > 0 && accessUrl != self.AccessUrl) { + if len(account) > 0 && account != self.Account { + for _, cloudprovider := range self.GetCloudproviders() { + if cloudprovider.Account == self.Account { + if _, err = cloudprovider.GetModelManager().TableSpec().Update(&cloudprovider, func() error { + cloudprovider.Account = account + return nil + }); err != nil { + return nil, err + } + } + } + } + _, err = self.GetModelManager().TableSpec().Update(self, func() error { + if len(account) > 0 { + self.Account = account + } + if len(accessUrl) > 0 { + self.AccessUrl = accessUrl + } + return nil + }) + if err != nil { + return nil, err + } + changed = true + } + if changed { + self.SetStatus(userCred, CLOUD_PROVIDER_INIT, "Change credential") + self.startSyncCloudProviderInfoTask(ctx, userCred, nil, "") + } + return nil, nil +} + +func (self *SCloudaccount) startSyncCloudProviderInfoTask(ctx context.Context, userCred mcclient.TokenCredential, syncRange *SSyncRange, parentTaskId string) error { + params := jsonutils.NewDict() + if syncRange != nil { + params.Add(jsonutils.Marshal(syncRange), "sync_range") + } + cloudproviders := self.GetCloudproviders() + + taskItems := make([]db.IStandaloneModel, 0) + for i := 0; i < len(cloudproviders); i++ { + if cloudproviders[i].Enabled { + taskItems = append(taskItems, &cloudproviders[i]) + } + } + + task, err := taskman.TaskManager.NewParallelTask(ctx, "CloudAccountSyncInfoTask", taskItems, userCred, params, "", "", nil) + if err != nil { + log.Errorf("CloudAccountSyncInfoTask newTask error %s", err) + } else { + task.ScheduleRun(nil) + } + return nil +} + +func (self *SCloudaccount) MarkStartSync(userCred mcclient.TokenCredential) { + _, err := self.GetModelManager().TableSpec().Update(self, func() error { + self.LastSync = timeutils.UtcNow() + return nil + }) + if err != nil { + log.Errorf("Fail tp update last_sync %s", err) + return + } + self.SetStatus(userCred, CLOUD_PROVIDER_START_SYNC, "") +} + +func (self *SCloudaccount) GetDriver() (cloudprovider.ICloudProvider, error) { + if !self.Enabled { + return nil, fmt.Errorf("Cloud provider is not enabled") + } + + secret, err := self.getPassword() + if err != nil { + return nil, fmt.Errorf("Invalid password %s", err) + } + // log.Debugf("XXXXX secret: %s", secret) + + return cloudprovider.GetProvider(self.Id, self.Name, self.AccessUrl, self.Account, secret, self.Provider) +} + +func (self *SCloudaccount) GetSubAccounts() (jsonutils.JSONObject, error) { + if secret, err := self.getPassword(); err != nil { + return nil, err + } else { + return GetSubAccounts(self.Name, self.AccessUrl, self.Account, secret, self.Provider) + } +} + +func (self *SCloudaccount) AllowPerformImport(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SCloudaccount) PerformImport(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if subAccounts, err := self.GetSubAccounts(); err != nil { + return nil, err + } else if accounts, err := subAccounts.GetArray("data"); err != nil { + return nil, err + } else { + enabled, _ := data.Bool("enabled") + enableProject, _ := data.Bool("enable_project") + for _, _account := range accounts { + name, _ := _account.GetString("name") + account, _ := _account.GetString("account") + if len(name) > 0 && len(account) > 0 { + if q := CloudproviderManager.Query().Equals("cloudaccount_id", self.Id).Equals("account", account); q.Count() > 0 { + log.Debugf("account %s has imported", account) + } else { + newCloudprovider := SCloudprovider{ + Account: account, + CloudaccountId: self.Id, + Provider: self.Provider, + } + newCloudprovider.SetModelManager(CloudproviderManager) + if err := CloudproviderManager.TableSpec().Insert(&newCloudprovider); err != nil { + log.Errorf("Create cloudprovider error: %v", err) + return nil, err + } else if _, err := CloudproviderManager.TableSpec().Update(&newCloudprovider, func() error { + newCloudprovider.Name = name + newCloudprovider.Enabled = true + return nil + }); err != nil { + log.Errorf("Update cloudprovider error: %v", err) + return nil, err + } + if enableProject { + newCloudprovider.SyncProject() + } + if enabled { + newCloudprovider.StartSyncCloudProviderInfoTask(ctx, userCred, &SSyncRange{FullSync: true}, "") + } + } + } + } + } + return nil, nil +} + +func GetSubAccounts(name, accessUrl, account, secret, provider string) (jsonutils.JSONObject, error) { + if provider, err := cloudprovider.GetProvider("", name, accessUrl, account, secret, provider); err != nil { + return nil, err + } else { + return provider.GetSubAccounts() + } +} + +func (self *SCloudaccount) SaveSysInfo(info jsonutils.JSONObject) { + self.GetModelManager().TableSpec().Update(self, func() error { + self.Sysinfo = info + return nil + }) +} + +func (manager *SCloudaccountManager) FetchCloudaccountById(accountId string) *SCloudaccount { + providerObj, err := manager.FetchById(accountId) + if err != nil { + log.Errorf("%s", err) + return nil + } + return providerObj.(*SCloudaccount) +} + +func (manager *SCloudaccountManager) FetchCloudaccountByIdOrName(accountId string) *SCloudaccount { + providerObj, err := manager.FetchByIdOrName(nil, accountId) + if err != nil { + if err != sql.ErrNoRows { + log.Errorf("%s", err) + } + return nil + } + return providerObj.(*SCloudaccount) +} + +func (self *SCloudaccount) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict { + extra.Add(jsonutils.Marshal(self.GetCloudproviders()), "accounts") + return extra +} + +func (self *SCloudaccount) GetCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { + extra := self.SEnabledStatusStandaloneResourceBase.GetCustomizeColumns(ctx, userCred, query) + return self.getMoreDetails(extra) +} + +func (self *SCloudaccount) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) *jsonutils.JSONDict { + extra := self.SEnabledStatusStandaloneResourceBase.GetExtraDetails(ctx, userCred, query) + return self.getMoreDetails(extra) +} + +func (manager *SCloudaccountManager) InitializeData() error { + cloudproviders := []SCloudprovider{} + q := CloudproviderManager.Query() + q = q.Filter(sqlchemy.OR(sqlchemy.IsEmpty(q.Field("cloudaccount_id")), sqlchemy.IsNull(q.Field("cloudaccount_id")))) + if err := db.FetchModelObjects(CloudproviderManager, q, &cloudproviders); err != nil { + return err + } + for i := 0; i < len(cloudproviders); i++ { + cloudprovider := cloudproviders[i] + Account, providerAccount, providerName := cloudprovider.Account, "", cloudprovider.Name + if cloudprovider.Provider == CLOUD_PROVIDER_AZURE { + if accountInfo := strings.Split(cloudprovider.Account, "/"); len(accountInfo) == 2 { + Account, providerAccount = accountInfo[0], accountInfo[1] + if len(cloudprovider.Description) > 0 { + providerName = cloudprovider.Description + } + } else { + log.Errorf("Error provider format %s", cloudprovider.Account) + continue + } + } + account := SCloudaccount{} + account.SetModelManager(CloudaccountManager) + if err := CloudaccountManager.Query(). + Equals("access_url", cloudprovider.AccessUrl). + Equals("account", Account). + Equals("provider", cloudprovider.Provider).First(&account); err != nil { + account = SCloudaccount{ + AccessUrl: cloudprovider.AccessUrl, + Account: Account, + LastSync: cloudprovider.LastSync, + Sysinfo: cloudprovider.Sysinfo, + Provider: cloudprovider.Provider, + } + if err := CloudaccountManager.TableSpec().Insert(&account); err != nil { + log.Errorf("Insert Account error: %v", err) + } + if _, err := CloudaccountManager.TableSpec().Update(&account, func() error { + account.Name = Account + account.Status = cloudprovider.Status + return nil + }); err != nil { + log.Errorf("Update Account %s error: %v", account.Id, err) + } + if secret, err := cloudprovider.getPassword(); err != nil { + log.Errorf("Get password from provider %s error %v", cloudprovider.Name, err) + } else if err := account.savePassword(secret); err != nil { + log.Errorf("Set password for account %s error %v", account.Name, err) + } + } + if _, err := CloudproviderManager.TableSpec().Update(&cloudprovider, func() error { + log.Errorf("update %s", cloudprovider.Name) + cloudprovider.CloudaccountId = account.Id + cloudprovider.Account = providerAccount + cloudprovider.Secret = "" + cloudprovider.Name = providerName + return nil + }); err != nil { + log.Errorf("Update provider %s error: %v", cloudprovider.Name, err) + } + } + return nil +} + +func (self *SCloudaccount) GetBalance() (float64, error) { + driver, err := self.GetDriver() + if err != nil { + return 0.0, err + } + return driver.GetBalance() +} + +func (self *SCloudaccount) AllowGetDetailsBalance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SCloudaccount) GetDetailsBalance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) { + balance, err := self.GetBalance() + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + ret := jsonutils.NewDict() + ret.Add(jsonutils.NewFloat(balance), "balance") + return ret, nil +} diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 78bdcc43b5..81774adc2e 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -8,8 +8,11 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/compute/options" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/auth" + "yunion.io/x/onecloud/pkg/mcclient/modules" "yunion.io/x/pkg/util/timeutils" "yunion.io/x/pkg/utils" @@ -51,6 +54,10 @@ type SCloudprovider struct { Account string `width:"128" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` // Column(VARCHAR(64, charset='ascii'), nullable=False) Secret string `width:"256" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"` // Column(VARCHAR(256, charset='ascii'), nullable=False) + CloudaccountId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required" key_index:"true"` + + ProjectId string `name:"tenant_id" width:"128" charset:"ascii" nullable:"true" list:"admin"` + LastSync time.Time `get:"admin" list:"admin"` // = Column(DateTime, nullable=True) Version string `width:"32" charset:"ascii" nullable:"true" list:"admin"` // Column(VARCHAR(32, charset='ascii'), nullable=True) @@ -101,58 +108,7 @@ func (self *SCloudprovider) ValidateUpdateData(ctx context.Context, userCred mcc } func (self *SCloudproviderManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { - // check provider - provider, _ := data.GetString("provider") - if !cloudprovider.IsSupported(provider) { - return nil, httperrors.NewInputParameterError("Unsupported provider %s", provider) - } - // check duplication - // url, account, provider must be unique - account, _ := data.GetString("account") - url, _ := data.GetString("access_url") - q := self.Query().Equals("provider", provider) - if len(account) > 0 { - q = q.Equals("account", account) - } - if len(url) > 0 { - q = q.Equals("access_url", url) - } - if q.Count() > 0 { - return nil, httperrors.NewConflictError("The account has been registered") - } - return self.SEnabledStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerProjId, query, data) -} - -func (self *SCloudprovider) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) { - self.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerProjId, query, data) - self.savePassword(self.Secret) - - if self.Enabled { - self.startSyncCloudProviderInfoTask(ctx, userCred, nil, "") - } -} - -func (self *SCloudprovider) savePassword(secret string) error { - sec, err := utils.EncryptAESBase64(self.Id, secret) - if err != nil { - return err - } - - /*log.Debugf("savePassword %s => %s", secret, sec) - newsec, err := utils.DescryptAESBase64(self.Id, sec) - if err != nil { - return err - } - if newsec != secret { - log.Errorf("Encrypt/Descrypt mismatch!!") - return fmt.Errorf("Encrypt/Descrypt mismatch!!") - }*/ - - _, err = self.GetModelManager().TableSpec().Update(self, func() error { - self.Secret = sec - return nil - }) - return err + return nil, httperrors.NewUnsupportOperationError("Not support create cloudprovider, please considir create cloudaccount") } func (self *SCloudprovider) getPassword() (string, error) { @@ -171,12 +127,37 @@ func (self *SCloudprovider) CanSync() bool { } } +func (self *SCloudprovider) SyncProject() (err error) { + projectId := "" + if len(self.ProjectId) == 0 && len(self.Name) > 0 { + if tenant, err := db.TenantCacheManager.FetchTenantByIdOrName(context.Background(), self.Name); err != nil { + s := auth.GetAdminSession(options.Options.Region, "") + if project, err := modules.Projects.Create(s, jsonutils.Marshal(map[string]string{"name": self.Name})); err != nil { + return err + } else if projectId, err = project.GetString("id"); err != nil { + return err + } + } else { + projectId = tenant.Id + } + } + if len(projectId) > 0 { + _, err := self.GetModelManager().TableSpec().Update(self, func() error { + self.ProjectId = projectId + return nil + }) + return err + } + return nil +} + type SSyncRange struct { - Force bool - FullSync bool - Region []string - Zone []string - Host []string + Force bool + FullSync bool + ProjectSync bool + Region []string + Zone []string + Host []string } func (sr *SSyncRange) NeedSyncInfo() bool { @@ -276,65 +257,12 @@ func (self *SCloudprovider) PerformSync(ctx context.Context, userCred mcclient.T return nil, httperrors.NewInputParameterError("invalid input %s", err) } if self.CanSync() || syncRange.Force { - err = self.startSyncCloudProviderInfoTask(ctx, userCred, &syncRange, "") + err = self.StartSyncCloudProviderInfoTask(ctx, userCred, &syncRange, "") } return nil, err } -func (self *SCloudprovider) AllowPerformUpdateCredential(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return userCred.IsSystemAdmin() -} - -func (self *SCloudprovider) PerformUpdateCredential(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - if !self.Enabled { - return nil, httperrors.NewInvalidStatusError("Cloudprovider disabled") - } - - var err error - changed := false - secret, _ := data.GetString("secret") - account, _ := data.GetString("account") - accessUrl, _ := data.GetString("access_url") - if len(secret) > 0 || len(account) > 0 || len(accessUrl) > 0 { - // check duplication - q := self.GetModelManager().Query() - q = q.Equals("access_url", accessUrl) - q = q.Equals("account", account) - q = q.NotEquals("id", self.Id) - if q.Count() > 0 { - return nil, httperrors.NewConflictError("Access url and account conflict") - } - } - if len(secret) > 0 { - err = self.savePassword(secret) - if err != nil { - return nil, err - } - changed = true - } - if (len(account) > 0 && account != self.Account) || (len(accessUrl) > 0 && accessUrl != self.AccessUrl) { - _, err = self.GetModelManager().TableSpec().Update(self, func() error { - if len(account) > 0 { - self.Account = account - } - if len(accessUrl) > 0 { - self.AccessUrl = accessUrl - } - return nil - }) - if err != nil { - return nil, err - } - changed = true - } - if changed { - self.SetStatus(userCred, CLOUD_PROVIDER_INIT, "Change credential") - self.startSyncCloudProviderInfoTask(ctx, userCred, nil, "") - } - return nil, nil -} - -func (self *SCloudprovider) startSyncCloudProviderInfoTask(ctx context.Context, userCred mcclient.TokenCredential, syncRange *SSyncRange, parentTaskId string) error { +func (self *SCloudprovider) StartSyncCloudProviderInfoTask(ctx context.Context, userCred mcclient.TokenCredential, syncRange *SSyncRange, parentTaskId string) error { params := jsonutils.NewDict() if syncRange != nil { params.Add(jsonutils.Marshal(syncRange), "sync_range") @@ -348,6 +276,26 @@ func (self *SCloudprovider) startSyncCloudProviderInfoTask(ctx context.Context, return nil } +func (self *SCloudprovider) AllowPerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SCloudprovider) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if project, err := data.GetString("project"); err != nil { + return nil, httperrors.NewInputParameterError("Missing project parameter") + } else if tenant, err := db.TenantCacheManager.FetchTenantByIdOrName(ctx, project); err != nil { + return nil, httperrors.NewNotFoundError("project %s not found", project) + } else if _, err := self.GetModelManager().TableSpec().Update(self, func() error { + self.ProjectId = tenant.Id + return nil + }); err != nil { + log.Errorf("Update cloudprovider error: %v", err) + return nil, err + } else { + return nil, self.StartSyncCloudProviderInfoTask(ctx, userCred, &SSyncRange{FullSync: true, ProjectSync: true}, "") + } +} + func (self *SCloudprovider) MarkStartSync(userCred mcclient.TokenCredential) { _, err := self.GetModelManager().TableSpec().Update(self, func() error { self.LastSync = timeutils.UtcNow() @@ -365,30 +313,42 @@ func (self *SCloudprovider) GetDriver() (cloudprovider.ICloudProvider, error) { return nil, fmt.Errorf("Cloud provider is not enabled") } - secret, err := self.getPassword() + account, err := self.getAccount() if err != nil { - return nil, fmt.Errorf("Invalid password %s", err) + return nil, err } - // log.Debugf("XXXXX secret: %s", secret) - - return cloudprovider.GetProvider(self.Id, self.Name, self.AccessUrl, self.Account, secret, self.Provider) + return cloudprovider.GetProvider(self.Id, self.Name, account.AccessUrl, account.Account, account.Secret, self.Provider) } -func (manager *SCloudproviderManager) AllowPerformGetSubAccounts(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return userCred.IsSystemAdmin() +type SAccount struct { + AccessUrl string + Account string + Secret string } -func (manager *SCloudproviderManager) PerformGetSubAccounts(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - name, _ := data.GetString("name") - accessUrl, _ := data.GetString("access_url") - account, _ := data.GetString("account") - secret, _ := data.GetString("secret") - _provider, _ := data.GetString("provider") - if provider, err := cloudprovider.GetProvider("", name, accessUrl, account, secret, _provider); err != nil { +func (self *SCloudprovider) getCloudaccount() (*SCloudaccount, error) { + if cloudaccount := CloudaccountManager.FetchCloudaccountById(self.CloudaccountId); cloudaccount != nil { + return cloudaccount, nil + } + return nil, fmt.Errorf("Failed to find cloud account for cloud provider %s", self.Name) +} + +func (self *SCloudprovider) getAccount() (*SAccount, error) { + if cloudaccount, err := self.getCloudaccount(); err != nil { return nil, err } else { - return provider.GetSubAccounts() + account := SAccount{AccessUrl: cloudaccount.AccessUrl, Account: cloudaccount.Account} + if passwd, err := cloudaccount.getPassword(); err != nil { + return nil, err + } else { + account.Secret = passwd + } + if len(self.Account) > 0 && self.Account != cloudaccount.Account { + account.Account = fmt.Sprintf("%s/%s", account.Account, self.Account) + } + return &account, nil } + } func (self *SCloudprovider) SaveSysInfo(info jsonutils.JSONObject) { diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index 3b73d9d382..cf7f0de3bb 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -651,7 +651,7 @@ func (manager *SDiskManager) getDisksByStorage(storage *SStorage) ([]SDisk, erro return disks, nil } -func (manager *SDiskManager) syncCloudDisk(ctx context.Context, userCred mcclient.TokenCredential, vdisk cloudprovider.ICloudDisk) (*SDisk, error) { +func (manager *SDiskManager) syncCloudDisk(ctx context.Context, userCred mcclient.TokenCredential, vdisk cloudprovider.ICloudDisk, projectId string, projectSync bool) (*SDisk, error) { diskObj, err := manager.FetchByExternalId(vdisk.GetGlobalId()) if err != nil { if err == sql.ErrNoRows { @@ -662,13 +662,13 @@ func (manager *SDiskManager) syncCloudDisk(ctx context.Context, userCred mcclien return nil, err } storage := storageObj.(*SStorage) - return manager.newFromCloudDisk(ctx, userCred, vdisk, storage) + return manager.newFromCloudDisk(ctx, userCred, vdisk, storage, projectId) } else { return nil, err } } else { disk := diskObj.(*SDisk) - err = disk.syncWithCloudDisk(ctx, userCred, vdisk) + err = disk.syncWithCloudDisk(ctx, userCred, vdisk, projectId, projectSync) if err != nil { return nil, err } @@ -676,7 +676,7 @@ func (manager *SDiskManager) syncCloudDisk(ctx context.Context, userCred mcclien } } -func (manager *SDiskManager) SyncDisks(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, disks []cloudprovider.ICloudDisk) ([]SDisk, []cloudprovider.ICloudDisk, compare.SyncResult) { +func (manager *SDiskManager) SyncDisks(ctx context.Context, userCred mcclient.TokenCredential, storage *SStorage, disks []cloudprovider.ICloudDisk, projectId string, projectSync bool) ([]SDisk, []cloudprovider.ICloudDisk, compare.SyncResult) { localDisks := make([]SDisk, 0) remoteDisks := make([]cloudprovider.ICloudDisk, 0) syncResult := compare.SyncResult{} @@ -708,7 +708,7 @@ func (manager *SDiskManager) SyncDisks(ctx context.Context, userCred mcclient.To } for i := 0; i < len(commondb); i += 1 { - err = commondb[i].syncWithCloudDisk(ctx, userCred, commonext[i]) + err = commondb[i].syncWithCloudDisk(ctx, userCred, commonext[i], projectId, projectSync) if err != nil { syncResult.UpdateError(err) } else { @@ -719,7 +719,7 @@ func (manager *SDiskManager) SyncDisks(ctx context.Context, userCred mcclient.To } for i := 0; i < len(added); i += 1 { - new, err := manager.newFromCloudDisk(ctx, userCred, added[i], storage) + new, err := manager.newFromCloudDisk(ctx, userCred, added[i], storage, projectId) if err != nil { syncResult.AddError(err) } else { @@ -732,7 +732,7 @@ func (manager *SDiskManager) SyncDisks(ctx context.Context, userCred mcclient.To return localDisks, remoteDisks, syncResult } -func (self *SDisk) syncWithCloudDisk(ctx context.Context, userCred mcclient.TokenCredential, extDisk cloudprovider.ICloudDisk) error { +func (self *SDisk) syncWithCloudDisk(ctx context.Context, userCred mcclient.TokenCredential, extDisk cloudprovider.ICloudDisk, projectId string, projectSync bool) error { _, err := self.GetModelManager().TableSpec().Update(self, func() error { extDisk.Refresh() self.Name = extDisk.GetName() @@ -751,7 +751,9 @@ func (self *SDisk) syncWithCloudDisk(ctx context.Context, userCred mcclient.Toke self.ExpiredAt = extDisk.GetExpiredAt() self.ProjectId = userCred.GetProjectId() - + if projectSync && len(projectId) > 0 { + self.ProjectId = projectId + } return nil }) if err != nil { @@ -775,7 +777,7 @@ func (self *SDisk) syncWithCloudDisk(ctx context.Context, userCred mcclient.Toke return nil } -func (manager *SDiskManager) newFromCloudDisk(ctx context.Context, userCred mcclient.TokenCredential, extDisk cloudprovider.ICloudDisk, storage *SStorage) (*SDisk, error) { +func (manager *SDiskManager) newFromCloudDisk(ctx context.Context, userCred mcclient.TokenCredential, extDisk cloudprovider.ICloudDisk, storage *SStorage, projectId string) (*SDisk, error) { disk := SDisk{} disk.SetModelManager(manager) @@ -784,7 +786,9 @@ func (manager *SDiskManager) newFromCloudDisk(ctx context.Context, userCred mccl disk.ExternalId = extDisk.GetGlobalId() disk.StorageId = storage.Id disk.ProjectId = userCred.GetProjectId() - + if len(projectId) > 0 { + disk.ProjectId = projectId + } disk.DiskFormat = extDisk.GetDiskFormat() disk.DiskSize = extDisk.GetDiskSizeMB() disk.AutoDelete = extDisk.GetIsAutoDelete() diff --git a/pkg/compute/models/elasticips.go b/pkg/compute/models/elasticips.go index c6a4a6d6e7..5d32ed6707 100644 --- a/pkg/compute/models/elasticips.go +++ b/pkg/compute/models/elasticips.go @@ -136,7 +136,7 @@ func (self *SElasticip) GetRegion() *SCloudregion { return CloudregionManager.FetchRegionById(self.CloudregionId) } -func (manager *SElasticipManager) SyncEips(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, region *SCloudregion, eips []cloudprovider.ICloudEIP) compare.SyncResult { +func (manager *SElasticipManager) SyncEips(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, region *SCloudregion, eips []cloudprovider.ICloudEIP, projectId string, projectSync bool) compare.SyncResult { // localEips := make([]SElasticip, 0) // remoteEips := make([]cloudprovider.ICloudEIP, 0) syncResult := compare.SyncResult{} @@ -167,7 +167,7 @@ func (manager *SElasticipManager) SyncEips(ctx context.Context, userCred mcclien } } for i := 0; i < len(commondb); i += 1 { - err = commondb[i].SyncWithCloudEip(userCred, commonext[i]) + err = commondb[i].SyncWithCloudEip(userCred, commonext[i], projectId, projectSync) if err != nil { syncResult.UpdateError(err) } else { @@ -175,7 +175,7 @@ func (manager *SElasticipManager) SyncEips(ctx context.Context, userCred mcclien } } for i := 0; i < len(added); i += 1 { - _, err := manager.newFromCloudEip(userCred, added[i], region) + _, err := manager.newFromCloudEip(userCred, added[i], region, projectId) if err != nil { syncResult.AddError(err) } else { @@ -221,7 +221,7 @@ func (self *SElasticip) SyncInstanceWithCloudEip(ctx context.Context, userCred m return nil } -func (self *SElasticip) SyncWithCloudEip(userCred mcclient.TokenCredential, ext cloudprovider.ICloudEIP) error { +func (self *SElasticip) SyncWithCloudEip(userCred mcclient.TokenCredential, ext cloudprovider.ICloudEIP, projectId string, projectSync bool) error { _, err := self.GetModelManager().TableSpec().Update(self, func() error { // self.Name = ext.GetName() @@ -232,7 +232,10 @@ func (self *SElasticip) SyncWithCloudEip(userCred mcclient.TokenCredential, ext self.ExternalId = ext.GetGlobalId() // self.ManagerId = ext.GetManagerId() self.IsEmulated = ext.IsEmulated() - // self.ProjectId = userCred.GetProjectId() + self.ProjectId = userCred.GetProjectId() + if projectSync && len(projectId) > 0 { + self.ProjectId = projectId + } self.ChargeType = ext.GetInternetChargeType() return nil @@ -243,7 +246,7 @@ func (self *SElasticip) SyncWithCloudEip(userCred mcclient.TokenCredential, ext return err } -func (manager *SElasticipManager) newFromCloudEip(userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, region *SCloudregion) (*SElasticip, error) { +func (manager *SElasticipManager) newFromCloudEip(userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, region *SCloudregion, projectId string) (*SElasticip, error) { eip := SElasticip{} eip.SetModelManager(manager) @@ -258,6 +261,9 @@ func (manager *SElasticipManager) newFromCloudEip(userCred mcclient.TokenCredent eip.ChargeType = extEip.GetInternetChargeType() eip.ProjectId = userCred.GetProjectId() + if len(projectId) > 0 { + eip.ProjectId = projectId + } err := manager.TableSpec().Insert(&eip) if err != nil { @@ -344,7 +350,7 @@ func (self *SElasticip) AssociateVM(userCred mcclient.TokenCredential, vm *SGues return nil } -func (manager *SElasticipManager) getEipByExtEip(userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, region *SCloudregion) (*SElasticip, error) { +func (manager *SElasticipManager) getEipByExtEip(userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, region *SCloudregion, projectId string) (*SElasticip, error) { eipObj, err := manager.FetchByExternalId(extEip.GetGlobalId()) if err == nil { return eipObj.(*SElasticip), nil @@ -354,7 +360,7 @@ func (manager *SElasticipManager) getEipByExtEip(userCred mcclient.TokenCredenti return nil, err } - return manager.newFromCloudEip(userCred, extEip, region) + return manager.newFromCloudEip(userCred, extEip, region, projectId) } func (manager *SElasticipManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 458ffb9b1e..dc07b3918f 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -1337,7 +1337,7 @@ func (self *SGuest) GetIsolatedDevices() []SIsolatedDevice { return IsolatedDeviceManager.findAttachedDevicesOfGuest(self) } -func (self *SGuest) syncWithCloudVM(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, extVM cloudprovider.ICloudVM) error { +func (self *SGuest) syncWithCloudVM(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, extVM cloudprovider.ICloudVM, projectId string, projectSync bool) error { metaData := extVM.GetMetadata() diff, err := GuestManager.TableSpec().Update(self, func() error { extVM.Refresh() @@ -1353,6 +1353,11 @@ func (self *SGuest) syncWithCloudVM(ctx context.Context, userCred mcclient.Token self.Machine = extVM.GetMachine() self.HostId = host.Id self.ProjectId = userCred.GetProjectId() + + if projectSync && len(projectId) > 0 { + self.ProjectId = projectId + } + self.Hypervisor = extVM.GetHypervisor() self.IsEmulated = extVM.IsEmulated() @@ -1396,7 +1401,7 @@ func (self *SGuest) syncWithCloudVM(ctx context.Context, userCred mcclient.Token return nil } -func (manager *SGuestManager) newCloudVM(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, extVM cloudprovider.ICloudVM) (*SGuest, error) { +func (manager *SGuestManager) newCloudVM(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, extVM cloudprovider.ICloudVM, projectId string) (*SGuest, error) { guest := SGuest{} guest.SetModelManager(manager) @@ -1420,7 +1425,11 @@ func (manager *SGuestManager) newCloudVM(ctx context.Context, userCred mcclient. guest.ExpiredAt = extVM.GetExpiredAt() guest.HostId = host.Id + guest.ProjectId = userCred.GetProjectId() + if len(projectId) > 0 { + guest.ProjectId = projectId + } metaData := extVM.GetMetadata() @@ -1893,7 +1902,7 @@ type sSyncDiskPair struct { vdisk cloudprovider.ICloudDisk } -func (self *SGuest) SyncVMDisks(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, vdisks []cloudprovider.ICloudDisk) compare.SyncResult { +func (self *SGuest) SyncVMDisks(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, vdisks []cloudprovider.ICloudDisk, projectId string, projectSync bool) compare.SyncResult { result := compare.SyncResult{} newdisks := make([]sSyncDiskPair, 0) @@ -1901,7 +1910,7 @@ func (self *SGuest) SyncVMDisks(ctx context.Context, userCred mcclient.TokenCred if len(vdisks[i].GetGlobalId()) == 0 { continue } - disk, err := DiskManager.syncCloudDisk(ctx, userCred, vdisks[i]) + disk, err := DiskManager.syncCloudDisk(ctx, userCred, vdisks[i], projectId, projectSync) if err != nil { result.Error(err) return result @@ -4275,7 +4284,7 @@ func (self *SGuest) GetEip() (*SElasticip, error) { return ElasticipManager.getEipForInstance("server", self.Id) } -func (self *SGuest) SyncVMEip(ctx context.Context, userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP) compare.SyncResult { +func (self *SGuest) SyncVMEip(ctx context.Context, userCred mcclient.TokenCredential, extEip cloudprovider.ICloudEIP, projectId string) compare.SyncResult { result := compare.SyncResult{} eip, err := self.GetEip() @@ -4288,7 +4297,7 @@ func (self *SGuest) SyncVMEip(ctx context.Context, userCred mcclient.TokenCreden // do nothing } else if eip == nil && extEip != nil { // add - neip, err := ElasticipManager.getEipByExtEip(userCred, extEip, self.getRegion()) + neip, err := ElasticipManager.getEipByExtEip(userCred, extEip, self.getRegion(), projectId) if err != nil { result.AddError(err) } else { @@ -4317,7 +4326,7 @@ func (self *SGuest) SyncVMEip(ctx context.Context, userCred mcclient.TokenCreden result.DeleteError(err) } else { result.Delete() - neip, err := ElasticipManager.getEipByExtEip(userCred, extEip, self.getRegion()) + neip, err := ElasticipManager.getEipByExtEip(userCred, extEip, self.getRegion(), projectId) if err != nil { result.AddError(err) } else { @@ -4331,7 +4340,7 @@ func (self *SGuest) SyncVMEip(ctx context.Context, userCred mcclient.TokenCreden } } else { // do nothing - err := eip.SyncWithCloudEip(userCred, extEip) + err := eip.SyncWithCloudEip(userCred, extEip, projectId, false) if err != nil { result.UpdateError(err) } else { diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index aa767108dd..b72fd3af84 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -1222,7 +1222,7 @@ func (self *SHost) newCloudHostWire(ctx context.Context, userCred mcclient.Token return err } -func (self *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCredential, vms []cloudprovider.ICloudVM) ([]SGuest, []cloudprovider.ICloudVM, compare.SyncResult) { +func (self *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCredential, vms []cloudprovider.ICloudVM, projectId string, projectSync bool) ([]SGuest, []cloudprovider.ICloudVM, compare.SyncResult) { localVMs := make([]SGuest, 0) remoteVMs := make([]cloudprovider.ICloudVM, 0) syncResult := compare.SyncResult{} @@ -1250,7 +1250,7 @@ func (self *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCrede } for i := 0; i < len(commondb); i += 1 { - err := commondb[i].syncWithCloudVM(ctx, userCred, self, commonext[i]) + err := commondb[i].syncWithCloudVM(ctx, userCred, self, commonext[i], projectId, projectSync) if err != nil { syncResult.UpdateError(err) } else { @@ -1261,7 +1261,7 @@ func (self *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCrede } for i := 0; i < len(added); i += 1 { - new, err := GuestManager.newCloudVM(ctx, userCred, self, added[i]) + new, err := GuestManager.newCloudVM(ctx, userCred, self, added[i], projectId) if err != nil { syncResult.AddError(err) } else { diff --git a/pkg/compute/models/initdb.go b/pkg/compute/models/initdb.go index 82f4e25e84..03a1fcd1d8 100644 --- a/pkg/compute/models/initdb.go +++ b/pkg/compute/models/initdb.go @@ -8,6 +8,7 @@ import ( func InitDB() error { for _, manager := range []db.IModelManager{ CloudproviderManager, + CloudaccountManager, CloudregionManager, ZoneManager, VpcManager, diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 89d8506db2..3b718978b6 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -413,7 +413,7 @@ func (manager *SNetworkManager) getNetworksByWire(wire *SWire) ([]SNetwork, erro return nets, nil } -func (manager *SNetworkManager) SyncNetworks(ctx context.Context, userCred mcclient.TokenCredential, wire *SWire, nets []cloudprovider.ICloudNetwork) ([]SNetwork, []cloudprovider.ICloudNetwork, compare.SyncResult) { +func (manager *SNetworkManager) SyncNetworks(ctx context.Context, userCred mcclient.TokenCredential, wire *SWire, nets []cloudprovider.ICloudNetwork, projectId string, projectSync bool) ([]SNetwork, []cloudprovider.ICloudNetwork, compare.SyncResult) { localNets := make([]SNetwork, 0) remoteNets := make([]cloudprovider.ICloudNetwork, 0) syncResult := compare.SyncResult{} @@ -455,7 +455,7 @@ func (manager *SNetworkManager) SyncNetworks(ctx context.Context, userCred mccli } } for i := 0; i < len(commondb); i += 1 { - err = commondb[i].SyncWithCloudNetwork(userCred, commonext[i]) + err = commondb[i].SyncWithCloudNetwork(userCred, commonext[i], projectId, projectSync) if err != nil { syncResult.UpdateError(err) } else { @@ -465,7 +465,7 @@ func (manager *SNetworkManager) SyncNetworks(ctx context.Context, userCred mccli } } for i := 0; i < len(added); i += 1 { - new, err := manager.newFromCloudNetwork(userCred, added[i], wire) + new, err := manager.newFromCloudNetwork(userCred, added[i], wire, projectId) if err != nil { syncResult.AddError(err) } else { @@ -478,7 +478,7 @@ func (manager *SNetworkManager) SyncNetworks(ctx context.Context, userCred mccli return localNets, remoteNets, syncResult } -func (self *SNetwork) SyncWithCloudNetwork(userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork) error { +func (self *SNetwork) SyncWithCloudNetwork(userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork, projectId string, projectSync bool) error { _, err := self.GetModelManager().TableSpec().Update(self, func() error { extNet.Refresh() self.Name = extNet.GetName() @@ -493,6 +493,9 @@ func (self *SNetwork) SyncWithCloudNetwork(userCred mcclient.TokenCredential, ex self.AllocTimoutSeconds = extNet.GetAllocTimeoutSeconds() self.ProjectId = userCred.GetProjectId() + if projectSync && len(projectId) > 0 { + self.ProjectId = projectId + } return nil }) if err != nil { @@ -501,7 +504,7 @@ func (self *SNetwork) SyncWithCloudNetwork(userCred mcclient.TokenCredential, ex return err } -func (manager *SNetworkManager) newFromCloudNetwork(userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork, wire *SWire) (*SNetwork, error) { +func (manager *SNetworkManager) newFromCloudNetwork(userCred mcclient.TokenCredential, extNet cloudprovider.ICloudNetwork, wire *SWire, projectId string) (*SNetwork, error) { net := SNetwork{} net.SetModelManager(manager) @@ -519,7 +522,9 @@ func (manager *SNetworkManager) newFromCloudNetwork(userCred mcclient.TokenCrede net.AllocTimoutSeconds = extNet.GetAllocTimeoutSeconds() net.ProjectId = userCred.GetProjectId() - + if len(projectId) > 0 { + net.ProjectId = projectId + } err := manager.TableSpec().Insert(&net) if err != nil { log.Errorf("newFromCloudZone fail %s", err) diff --git a/pkg/compute/models/snapshots.go b/pkg/compute/models/snapshots.go index 3367203ee4..c6fc77c914 100644 --- a/pkg/compute/models/snapshots.go +++ b/pkg/compute/models/snapshots.go @@ -417,9 +417,12 @@ func totalSnapshotCount(projectId string) int { } // Only sync snapshot status -func (self *SSnapshot) SyncWithCloudSnapshot(userCred mcclient.TokenCredential, ext cloudprovider.ICloudSnapshot) error { +func (self *SSnapshot) SyncWithCloudSnapshot(userCred mcclient.TokenCredential, ext cloudprovider.ICloudSnapshot, projectId string, projectSync bool) error { _, err := self.GetModelManager().TableSpec().Update(self, func() error { self.Status = ext.GetStatus() + if projectSync && len(projectId) > 0 { + self.ProjectId = projectId + } return nil }) if err != nil { @@ -428,7 +431,7 @@ func (self *SSnapshot) SyncWithCloudSnapshot(userCred mcclient.TokenCredential, return err } -func (manager *SSnapshotManager) newFromCloudSnapshot(userCred mcclient.TokenCredential, extSnapshot cloudprovider.ICloudSnapshot, region *SCloudregion) (*SSnapshot, error) { +func (manager *SSnapshotManager) newFromCloudSnapshot(userCred mcclient.TokenCredential, extSnapshot cloudprovider.ICloudSnapshot, region *SCloudregion, projectId string) (*SSnapshot, error) { snapshot := SSnapshot{} snapshot.SetModelManager(manager) @@ -449,6 +452,9 @@ func (manager *SSnapshotManager) newFromCloudSnapshot(userCred mcclient.TokenCre snapshot.CloudregionId = region.Id snapshot.ProjectId = userCred.GetProjectId() + if len(projectId) > 0 { + snapshot.ProjectId = projectId + } err := manager.TableSpec().Insert(&snapshot) if err != nil { log.Errorf("newFromCloudEip fail %s", err) @@ -470,7 +476,7 @@ func (manager *SSnapshotManager) getProviderSnapshotsByRegion(region *SCloudregi return snapshots, nil } -func (manager *SSnapshotManager) SyncSnapshots(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, region *SCloudregion, snapshots []cloudprovider.ICloudSnapshot) compare.SyncResult { +func (manager *SSnapshotManager) SyncSnapshots(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, region *SCloudregion, snapshots []cloudprovider.ICloudSnapshot, projectId string, projectSync bool) compare.SyncResult { syncResult := compare.SyncResult{} dbSnapshots, err := manager.getProviderSnapshotsByRegion(region, provider) if err != nil { @@ -496,7 +502,7 @@ func (manager *SSnapshotManager) SyncSnapshots(ctx context.Context, userCred mcc } } for i := 0; i < len(commondb); i += 1 { - err = commondb[i].SyncWithCloudSnapshot(userCred, commonext[i]) + err = commondb[i].SyncWithCloudSnapshot(userCred, commonext[i], projectId, projectSync) if err != nil { syncResult.UpdateError(err) } else { @@ -504,7 +510,7 @@ func (manager *SSnapshotManager) SyncSnapshots(ctx context.Context, userCred mcc } } for i := 0; i < len(added); i += 1 { - _, err := manager.newFromCloudSnapshot(userCred, added[i], region) + _, err := manager.newFromCloudSnapshot(userCred, added[i], region, projectId) if err != nil { syncResult.AddError(err) } else { diff --git a/pkg/compute/tasks/cloud_account_sync_task.go b/pkg/compute/tasks/cloud_account_sync_task.go new file mode 100644 index 0000000000..9b3062d12a --- /dev/null +++ b/pkg/compute/tasks/cloud_account_sync_task.go @@ -0,0 +1,85 @@ +package tasks + +import ( + "context" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type CloudAccountSyncInfoTask struct { + taskman.STask +} + +func init() { + taskman.RegisterTask(CloudAccountSyncInfoTask{}) +} + +func (self *CloudAccountSyncInfoTask) OnInit(ctx context.Context, objs []db.IStandaloneModel, body jsonutils.JSONObject) { + cloudproviders := make([]*models.SCloudprovider, 0) + for _, obj := range objs { + cloudprovider := obj.(*models.SCloudprovider) + if cloudprovider.Enabled { + cloudproviders = append(cloudproviders, cloudprovider) + } + } + + var account *models.SCloudaccount + if len(cloudproviders) > 0 { + if account = models.CloudaccountManager.FetchCloudaccountById(cloudproviders[0].CloudaccountId); account == nil { + account.SetStatus(self.UserCred, models.CLOUD_PROVIDER_CONNECTED, "") + self.SetStageComplete(ctx, nil) + return + } + } else { + account.SetStatus(self.UserCred, models.CLOUD_PROVIDER_CONNECTED, "") + self.SetStageComplete(ctx, nil) + return + } + if _, err := account.GetSubAccounts(); err != nil { + account.SetStatus(self.UserCred, models.CLOUD_PROVIDER_DISCONNECTED, "") + self.SetStageFailed(ctx, err.Error()) + return + } + + syncRange := models.SSyncRange{} + syncRangeJson, _ := self.Params.Get("sync_range") + if syncRangeJson != nil { + syncRangeJson.Unmarshal(&syncRange) + } + + if len(syncRange.Host) == 0 && !syncRange.FullSync { + account.SetStatus(self.UserCred, models.CLOUD_PROVIDER_CONNECTED, "") + self.SetStageComplete(ctx, nil) + return + } + // do sync + self.SetStage("on_cloudaccount_sync_complete", nil) + self.SyncCloudaccount(ctx, account, cloudproviders, &syncRange) +} + +func (self *CloudAccountSyncInfoTask) SyncCloudaccount(ctx context.Context, account *models.SCloudaccount, cloudproviders []*models.SCloudprovider, syncRange *models.SSyncRange) { + for _, cloudprovider := range cloudproviders { + self.SyncCloudprovider(ctx, cloudprovider, syncRange) + } +} + +func (self *CloudAccountSyncInfoTask) SyncCloudprovider(ctx context.Context, cloudprovider *models.SCloudprovider, syncRange *models.SSyncRange) { + lockman.LockObject(ctx, cloudprovider) + defer lockman.ReleaseObject(ctx, cloudprovider) + + cloudprovider.StartSyncCloudProviderInfoTask(ctx, self.UserCred, syncRange, self.GetId()) +} + +func (self *CloudAccountSyncInfoTask) OnCloudaccountSyncComplete(ctx context.Context, items []db.IStandaloneModel, data jsonutils.JSONObject) { + if len(items) > 0 { + cloudprovider := items[0].(*models.SCloudprovider) + if account := models.CloudaccountManager.FetchCloudaccountById(cloudprovider.CloudaccountId); account != nil { + account.SetStatus(self.UserCred, models.CLOUD_PROVIDER_CONNECTED, "") + } + } + self.SetStageComplete(ctx, nil) +} diff --git a/pkg/compute/tasks/cloud_provider_sync_info_task.go b/pkg/compute/tasks/cloud_provider_sync_info_task.go index 90bb917edd..2a53ccc4f3 100644 --- a/pkg/compute/tasks/cloud_provider_sync_info_task.go +++ b/pkg/compute/tasks/cloud_provider_sync_info_task.go @@ -108,11 +108,11 @@ func syncCloudProviderInfo(ctx context.Context, provider *models.SCloudprovider, if len(syncRange.Region) > 0 && !utils.IsInStringArray(localRegions[i].Id, syncRange.Region) { continue } - syncRegionEips(ctx, provider, task, &localRegions[i], remoteRegions[i]) + syncRegionEips(ctx, provider, task, &localRegions[i], remoteRegions[i], syncRange) localZones, remoteZones := syncRegionZones(ctx, provider, task, &localRegions[i], remoteRegions[i]) - syncRegionVPCs(ctx, provider, task, &localRegions[i], remoteRegions[i]) + syncRegionVPCs(ctx, provider, task, &localRegions[i], remoteRegions[i], syncRange) if localZones != nil && remoteZones != nil { for j := 0; j < len(localZones); j += 1 { @@ -120,15 +120,15 @@ func syncCloudProviderInfo(ctx context.Context, provider *models.SCloudprovider, if len(syncRange.Zone) > 0 && !utils.IsInStringArray(localZones[j].Id, syncRange.Zone) { continue } - syncZoneStorages(ctx, provider, task, &localZones[j], remoteZones[j]) + syncZoneStorages(ctx, provider, task, &localZones[j], remoteZones[j], syncRange) syncZoneHosts(ctx, provider, task, &localZones[j], remoteZones[j], syncRange) } } - syncRegionSnapshots(ctx, provider, task, &localRegions[i], remoteRegions[i]) + syncRegionSnapshots(ctx, provider, task, &localRegions[i], remoteRegions[i], syncRange) } } -func syncRegionSnapshots(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion) { +func syncRegionSnapshots(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion, syncRange *models.SSyncRange) { snapshots, err := remoteRegion.GetISnapshots() if err != nil { msg := fmt.Sprintf("GetISnapshots for region %s failed %s", remoteRegion.GetName(), err) @@ -137,7 +137,7 @@ func syncRegionSnapshots(ctx context.Context, provider *models.SCloudprovider, t return } - result := models.SnapshotManager.SyncSnapshots(ctx, task.GetUserCred(), provider, localRegion, snapshots) + result := models.SnapshotManager.SyncSnapshots(ctx, task.GetUserCred(), provider, localRegion, snapshots, provider.ProjectId, syncRange.ProjectSync) msg := result.Result() log.Infof("SyncSnapshots for region %s result: %s", localRegion.Name, msg) if result.IsError() { @@ -147,7 +147,7 @@ func syncRegionSnapshots(ctx context.Context, provider *models.SCloudprovider, t db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, task.GetUserCred()) } -func syncRegionEips(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion) { +func syncRegionEips(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion, syncRange *models.SSyncRange) { eips, err := remoteRegion.GetIEips() if err != nil { msg := fmt.Sprintf("GetIEips for region %s failed %s", remoteRegion.GetName(), err) @@ -156,7 +156,7 @@ func syncRegionEips(ctx context.Context, provider *models.SCloudprovider, task * return } - result := models.ElasticipManager.SyncEips(ctx, task.UserCred, provider, localRegion, eips) + result := models.ElasticipManager.SyncEips(ctx, task.UserCred, provider, localRegion, eips, provider.ProjectId, syncRange.ProjectSync) msg := result.Result() log.Infof("SyncEips for region %s result: %s", localRegion.Name, msg) if result.IsError() { @@ -187,7 +187,7 @@ func syncRegionZones(ctx context.Context, provider *models.SCloudprovider, task return localZones, remoteZones } -func syncRegionVPCs(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion) { +func syncRegionVPCs(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion, syncRange *models.SSyncRange) { vpcs, err := remoteRegion.GetIVpcs() if err != nil { msg := fmt.Sprintf("GetVpcs for region %s failed %s", remoteRegion.GetName(), err) @@ -207,7 +207,7 @@ func syncRegionVPCs(ctx context.Context, provider *models.SCloudprovider, task * db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, task.UserCred) logclient.AddActionLog(provider, getAction(task.Params), notes, task.UserCred, true) for j := 0; j < len(localVpcs); j += 1 { - syncVpcWires(ctx, provider, task, &localVpcs[j], remoteVpcs[j]) + syncVpcWires(ctx, provider, task, &localVpcs[j], remoteVpcs[j], syncRange) syncVpcSecGroup(ctx, provider, task, &localVpcs[j], remoteVpcs[j]) } } @@ -230,7 +230,7 @@ func syncVpcSecGroup(ctx context.Context, provider *models.SCloudprovider, task } } -func syncVpcWires(ctx context.Context, provider *models.SCloudprovider, task taskman.ITask, localVpc *models.SVpc, remoteVpc cloudprovider.ICloudVpc) { +func syncVpcWires(ctx context.Context, provider *models.SCloudprovider, task taskman.ITask, localVpc *models.SVpc, remoteVpc cloudprovider.ICloudVpc, syncRange *models.SSyncRange) { wires, err := remoteVpc.GetIWires() if err != nil { msg := fmt.Sprintf("GetIWires for vpc %s failed %s", remoteVpc.GetId(), err) @@ -249,11 +249,11 @@ func syncVpcWires(ctx context.Context, provider *models.SCloudprovider, task tas db.OpsLog.LogEvent(provider, db.ACT_SYNC_HOST_COMPLETE, msg, task.GetUserCred()) logclient.AddActionLog(provider, getAction(task.GetParams()), notes, task.GetUserCred(), true) for i := 0; i < len(localWires); i += 1 { - syncWireNetworks(ctx, provider, task, &localWires[i], remoteWires[i]) + syncWireNetworks(ctx, provider, task, &localWires[i], remoteWires[i], syncRange) } } -func syncWireNetworks(ctx context.Context, provider *models.SCloudprovider, task taskman.ITask, localWire *models.SWire, remoteWire cloudprovider.ICloudWire) { +func syncWireNetworks(ctx context.Context, provider *models.SCloudprovider, task taskman.ITask, localWire *models.SWire, remoteWire cloudprovider.ICloudWire, syncRange *models.SSyncRange) { nets, err := remoteWire.GetINetworks() if err != nil { msg := fmt.Sprintf("GetINetworks for wire %s failed %s", remoteWire.GetId(), err) @@ -261,7 +261,7 @@ func syncWireNetworks(ctx context.Context, provider *models.SCloudprovider, task logSyncFailed(provider, task, msg) return } - _, _, result := models.NetworkManager.SyncNetworks(ctx, task.GetUserCred(), localWire, nets) + _, _, result := models.NetworkManager.SyncNetworks(ctx, task.GetUserCred(), localWire, nets, provider.ProjectId, syncRange.ProjectSync) msg := result.Result() notes := fmt.Sprintf("SyncNetworks for wire %s result: %s", localWire.Name, msg) log.Infof(notes) @@ -273,7 +273,7 @@ func syncWireNetworks(ctx context.Context, provider *models.SCloudprovider, task logclient.AddActionLog(provider, getAction(task.GetParams()), notes, task.GetUserCred(), true) } -func syncZoneStorages(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localZone *models.SZone, remoteZone cloudprovider.ICloudZone) { +func syncZoneStorages(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localZone *models.SZone, remoteZone cloudprovider.ICloudZone, syncRange *models.SSyncRange) { storages, err := remoteZone.GetIStorages() if err != nil { msg := fmt.Sprintf("GetIStorages for zone %s failed %s", remoteZone.GetName(), err) @@ -294,7 +294,7 @@ func syncZoneStorages(ctx context.Context, provider *models.SCloudprovider, task for i := 0; i < len(localStorages); i += 1 { syncStorageCaches(ctx, provider, task, &localStorages[i], remoteStorages[i]) - syncStorageDisks(ctx, provider, task, &localStorages[i], remoteStorages[i]) + syncStorageDisks(ctx, provider, task, &localStorages[i], remoteStorages[i], syncRange) } } @@ -315,7 +315,7 @@ func syncStorageCaches(ctx context.Context, provider *models.SCloudprovider, tas } } -func syncStorageDisks(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localStorage *models.SStorage, remoteStorage cloudprovider.ICloudStorage) { +func syncStorageDisks(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localStorage *models.SStorage, remoteStorage cloudprovider.ICloudStorage, syncRange *models.SSyncRange) { disks, err := remoteStorage.GetIDisks() if err != nil { msg := fmt.Sprintf("GetIDisks for storage %s failed %s", remoteStorage.GetName(), err) @@ -323,7 +323,7 @@ func syncStorageDisks(ctx context.Context, provider *models.SCloudprovider, task logSyncFailed(provider, task, msg) return } - _, _, result := models.DiskManager.SyncDisks(ctx, task.UserCred, localStorage, disks) + _, _, result := models.DiskManager.SyncDisks(ctx, task.UserCred, localStorage, disks, provider.ProjectId, syncRange.ProjectSync) msg := result.Result() notes := fmt.Sprintf("SyncDisks for storage %s result: %s", localStorage.Name, msg) log.Infof(notes) @@ -359,7 +359,7 @@ func syncZoneHosts(ctx context.Context, provider *models.SCloudprovider, task *C } syncHostStorages(ctx, provider, task, &localHosts[i], remoteHosts[i]) syncHostWires(ctx, provider, task, &localHosts[i], remoteHosts[i]) - syncHostVMs(ctx, provider, task, &localHosts[i], remoteHosts[i]) + syncHostVMs(ctx, provider, task, &localHosts[i], remoteHosts[i], syncRange) } } @@ -403,7 +403,7 @@ func syncHostWires(ctx context.Context, provider *models.SCloudprovider, task ta logclient.AddActionLog(provider, getAction(task.GetParams()), notes, task.GetUserCred(), true) } -func syncHostVMs(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localHost *models.SHost, remoteHost cloudprovider.ICloudHost) { +func syncHostVMs(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, localHost *models.SHost, remoteHost cloudprovider.ICloudHost, syncRange *models.SSyncRange) { vms, err := remoteHost.GetIVMs() if err != nil { msg := fmt.Sprintf("GetIVMs for host %s failed %s", remoteHost.GetName(), err) @@ -411,7 +411,7 @@ func syncHostVMs(ctx context.Context, provider *models.SCloudprovider, task *Clo logSyncFailed(provider, task, msg) return } - localVMs, remoteVMs, result := localHost.SyncHostVMs(ctx, task.UserCred, vms) + localVMs, remoteVMs, result := localHost.SyncHostVMs(ctx, task.UserCred, vms, provider.ProjectId, syncRange.ProjectSync) msg := result.Result() notes := fmt.Sprintf("SyncHostVMs for host %s result: %s", localHost.Name, msg) log.Infof(notes) @@ -423,7 +423,7 @@ func syncHostVMs(ctx context.Context, provider *models.SCloudprovider, task *Clo logclient.AddActionLog(provider, getAction(task.Params), notes, task.UserCred, true) for i := 0; i < len(localVMs); i += 1 { syncVMNics(ctx, provider, task, localHost, &localVMs[i], remoteVMs[i]) - syncVMDisks(ctx, provider, task, localHost, &localVMs[i], remoteVMs[i]) + syncVMDisks(ctx, provider, task, localHost, &localVMs[i], remoteVMs[i], syncRange) syncVMEip(ctx, provider, task, &localVMs[i], remoteVMs[i]) } } @@ -448,7 +448,7 @@ func syncVMNics(ctx context.Context, provider *models.SCloudprovider, task *Clou logclient.AddActionLog(provider, getAction(task.Params), notes, task.UserCred, true) } -func syncVMDisks(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, host *models.SHost, localVM *models.SGuest, remoteVM cloudprovider.ICloudVM) { +func syncVMDisks(ctx context.Context, provider *models.SCloudprovider, task *CloudProviderSyncInfoTask, host *models.SHost, localVM *models.SGuest, remoteVM cloudprovider.ICloudVM, syncRange *models.SSyncRange) { disks, err := remoteVM.GetIDisks() if err != nil { msg := fmt.Sprintf("GetIDisks for VM %s failed %s", remoteVM.GetName(), err) @@ -456,7 +456,7 @@ func syncVMDisks(ctx context.Context, provider *models.SCloudprovider, task *Clo logSyncFailed(provider, task, msg) return } - result := localVM.SyncVMDisks(ctx, task.UserCred, host, disks) + result := localVM.SyncVMDisks(ctx, task.UserCred, host, disks, provider.ProjectId, syncRange.ProjectSync) msg := result.Result() notes := fmt.Sprintf("syncVMNics for VM %s result: %s", localVM.Name, msg) log.Infof(notes) @@ -476,7 +476,7 @@ func syncVMEip(ctx context.Context, provider *models.SCloudprovider, task *Cloud logSyncFailed(provider, task, msg) return } - result := localVM.SyncVMEip(ctx, task.UserCred, eip) + result := localVM.SyncVMEip(ctx, task.UserCred, eip, provider.ProjectId) msg := result.Result() log.Infof("syncVMEip for VM %s result: %s", localVM.Name, msg) if result.IsError() { diff --git a/pkg/compute/tasks/eip_allocate_task.go b/pkg/compute/tasks/eip_allocate_task.go index 0ea90fa603..da5a8ff982 100644 --- a/pkg/compute/tasks/eip_allocate_task.go +++ b/pkg/compute/tasks/eip_allocate_task.go @@ -39,7 +39,7 @@ func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel return } - err = eip.SyncWithCloudEip(self.UserCred, extEip) + err = eip.SyncWithCloudEip(self.UserCred, extEip, "", false) if err != nil { msg := fmt.Sprintf("sync eip fail %s", err) diff --git a/pkg/compute/tasks/eip_syncstatus_task.go b/pkg/compute/tasks/eip_syncstatus_task.go index 6df2bb0fa8..799d316bed 100644 --- a/pkg/compute/tasks/eip_syncstatus_task.go +++ b/pkg/compute/tasks/eip_syncstatus_task.go @@ -38,7 +38,7 @@ func (self *EipSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneMod return } - err = eip.SyncWithCloudEip(self.UserCred, extEip) + err = eip.SyncWithCloudEip(self.UserCred, extEip, "", false) if err != nil { msg := fmt.Sprintf("fail to sync eip status %s", err) eip.SetStatus(self.UserCred, models.EIP_STATUS_UNKNOWN, msg) diff --git a/pkg/compute/tasks/network_create_task.go b/pkg/compute/tasks/network_create_task.go index 76919660bf..f22fec944c 100644 --- a/pkg/compute/tasks/network_create_task.go +++ b/pkg/compute/tasks/network_create_task.go @@ -64,7 +64,7 @@ func (self *NetworkCreateTask) OnInit(ctx context.Context, obj db.IStandaloneMod return } - err = network.SyncWithCloudNetwork(self.UserCred, inet) + err = network.SyncWithCloudNetwork(self.UserCred, inet, "", false) if err != nil { self.taskFailed(ctx, network, "SyncWithCloudNetwork", err) diff --git a/pkg/compute/tasks/vpc_create_task.go b/pkg/compute/tasks/vpc_create_task.go index 7708bb04f4..f4342dcaf6 100644 --- a/pkg/compute/tasks/vpc_create_task.go +++ b/pkg/compute/tasks/vpc_create_task.go @@ -57,7 +57,7 @@ func (self *VpcCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, } provider := models.CloudproviderManager.FetchCloudproviderById(vpc.ManagerId) - syncVpcWires(ctx, provider, self, vpc, ivpc) + syncVpcWires(ctx, provider, self, vpc, ivpc, &models.SSyncRange{}) hosts := models.HostManager.GetHostsByManagerAndRegion(provider.Id, vpc.CloudregionId) if hosts != nil { diff --git a/pkg/mcclient/modules/mod_cloudaccounts.go b/pkg/mcclient/modules/mod_cloudaccounts.go new file mode 100644 index 0000000000..495f8b5cff --- /dev/null +++ b/pkg/mcclient/modules/mod_cloudaccounts.go @@ -0,0 +1,14 @@ +package modules + +var ( + Cloudaccounts ResourceManager +) + +func init() { + Cloudaccounts = NewComputeManager("cloudaccount", "cloudaccounts", + []string{"ID", "Name", "Enabled", "Status", "Access_url", "Account", + "Last_sync", "Provider"}, + []string{}) + + registerCompute(&Cloudaccounts) +} diff --git a/pkg/util/aliyun/aliyun.go b/pkg/util/aliyun/aliyun.go index e54ad842e1..21ed153df4 100644 --- a/pkg/util/aliyun/aliyun.go +++ b/pkg/util/aliyun/aliyun.go @@ -122,7 +122,17 @@ func (self *SAliyunClient) GetRegions() []SRegion { } func (self *SAliyunClient) GetSubAccounts() (jsonutils.JSONObject, error) { - return nil, nil + if err := self.fetchRegions(); err != nil { + return nil, err + } else { + result := jsonutils.NewDict() + data := jsonutils.NewArray() + account := jsonutils.Marshal(map[string]string{"account": self.accessKey, "name": self.providerName}) + data.Add(account) + result.Add(data, "data") + result.Add(jsonutils.NewInt(1), "total") + return result, nil + } } func (self *SAliyunClient) GetIRegions() []cloudprovider.ICloudRegion { diff --git a/pkg/util/azure/azure.go b/pkg/util/azure/azure.go index 80aacffa8b..056fb08955 100644 --- a/pkg/util/azure/azure.go +++ b/pkg/util/azure/azure.go @@ -163,9 +163,9 @@ func (self *SAzureClient) GetSubAccounts() (jsonutils.JSONObject, error) { } else { for _, value := range resp.Values() { data := jsonutils.NewDict() - data.Add(jsonutils.NewString(*value.SubscriptionID), "subscriptionId") + data.Add(jsonutils.NewString(*value.SubscriptionID), "account") data.Add(jsonutils.NewString(string(value.State)), "state") - data.Add(jsonutils.NewString(*value.DisplayName), "displayName") + data.Add(jsonutils.NewString(*value.DisplayName), "name") accounts.Add(data) } result.Add(accounts, "data") diff --git a/pkg/util/esxi/manager.go b/pkg/util/esxi/manager.go index ed1dd5d76d..5c1f8c229c 100644 --- a/pkg/util/esxi/manager.go +++ b/pkg/util/esxi/manager.go @@ -88,6 +88,20 @@ func (cli *SESXiClient) disconnect() error { return nil } +func (cli *SESXiClient) GetSubAccounts() (jsonutils.JSONObject, error) { + if err := cli.connect(); err != nil { + return nil, err + } else { + result := jsonutils.NewDict() + data := jsonutils.NewArray() + account := jsonutils.Marshal(map[string]string{"account": cli.account, "name": cli.providerName}) + data.Add(account) + result.Add(data, "data") + result.Add(jsonutils.NewInt(1), "total") + return result, nil + } +} + func (cli *SESXiClient) About() jsonutils.JSONObject { return jsonutils.Marshal(&cli.client.ServiceContent.About) } diff --git a/pkg/util/esxi/provider/provider.go b/pkg/util/esxi/provider/provider.go index 02c5434485..f7d5362740 100644 --- a/pkg/util/esxi/provider/provider.go +++ b/pkg/util/esxi/provider/provider.go @@ -88,7 +88,7 @@ func (self *SESXiProvider) GetSysInfo() (jsonutils.JSONObject, error) { } func (self *SESXiProvider) GetSubAccounts() (jsonutils.JSONObject, error) { - return nil, nil + return self.client.GetSubAccounts() } func (self *SESXiProvider) GetIRegions() []cloudprovider.ICloudRegion {