diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index 814c678e27..d3ac984b57 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -23,6 +23,7 @@ type ICloudProvider interface { GetId() string GetName() string GetSysInfo() (jsonutils.JSONObject, error) + GetVersion() string IsPublicCloud() bool IsOnPremiseInfrastructure() bool diff --git a/pkg/compute/models/cloudaccounts.go b/pkg/compute/models/cloudaccounts.go index 5316dc37be..66ca78f13b 100644 --- a/pkg/compute/models/cloudaccounts.go +++ b/pkg/compute/models/cloudaccounts.go @@ -51,9 +51,7 @@ type SCloudaccount struct { BalanceKey string `width:"256" charset:"ascii" nullable:"true" list:"admin" update:"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) + // Sysinfo jsonutils.JSONObject `get:"admin"` // Column(JSONEncodedDict, nullable=True) Provider string `width:"64" charset:"ascii" list:"admin" create:"admin_required"` } @@ -502,12 +500,12 @@ func getSubAccounts(name, accessUrl, account, secret, provider string) ([]cloudp return iprovider.GetSubAccounts() } -func (self *SCloudaccount) SaveSysInfo(info jsonutils.JSONObject) { +/*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) @@ -529,10 +527,113 @@ func (manager *SCloudaccountManager) FetchCloudaccountByIdOrName(accountId strin return providerObj.(*SCloudaccount) } +func (self *SCloudaccount) getProviderCount() int { + q := CloudproviderManager.Query().Equals("cloudaccount_id", self.Id) + return q.Count() +} + +func (self *SCloudaccount) getHostCount() int { + subq := CloudproviderManager.Query("id").Equals("cloudaccount_id", self.Id).SubQuery() + q := HostManager.Query().In("manager_id", subq) + return q.Count() +} + +func (self *SCloudaccount) getVpcCount() int { + subq := CloudproviderManager.Query("id").Equals("cloudaccount_id", self.Id).SubQuery() + q := VpcManager.Query().In("manager_id", subq) + return q.Count() +} + +func (self *SCloudaccount) getStorageCount() int { + subq := CloudproviderManager.Query("id").Equals("cloudaccount_id", self.Id).SubQuery() + q := StorageManager.Query().In("manager_id", subq) + return q.Count() +} + +func (self *SCloudaccount) getStoragecacheCount() int { + subq := CloudproviderManager.Query("id").Equals("cloudaccount_id", self.Id).SubQuery() + q := StoragecacheManager.Query().In("manager_id", subq) + return q.Count() +} + +func (self *SCloudaccount) getEipCount() int { + subq := CloudproviderManager.Query("id").Equals("cloudaccount_id", self.Id).SubQuery() + q := ElasticipManager.Query().In("manager_id", subq) + return q.Count() +} + +func (self *SCloudaccount) getRoutetableCount() int { + subq := CloudproviderManager.Query("id").Equals("cloudaccount_id", self.Id).SubQuery() + q := RouteTableManager.Query().In("manager_id", subq) + return q.Count() +} + +func (self *SCloudaccount) getGuestCount() int { + subsubq := CloudproviderManager.Query("id").Equals("cloudaccount_id", self.Id).SubQuery() + subq := HostManager.Query("id").In("manager_id", subsubq).SubQuery() + q := GuestManager.Query().In("host_id", subq) + return q.Count() +} + +func (self *SCloudaccount) getDiskCount() int { + subsubq := CloudproviderManager.Query("id").Equals("cloudaccount_id", self.Id).SubQuery() + subq := StorageManager.Query("id").In("manager_id", subsubq).SubQuery() + q := DiskManager.Query().In("storage_id", subq) + return q.Count() +} + +func (self *SCloudaccount) getProjectIds() []string { + q := CloudproviderManager.Query("tenant_id").Equals("cloudaccount_id", self.Id).Distinct() + rows, err := q.Rows() + if err != nil { + return nil + } + ret := make([]string, 0) + for rows.Next() { + var projId string + err := rows.Scan(&projId) + if err != nil { + return nil + } + ret = append(ret, projId) + } + return ret +} + +func (self *SCloudaccount) getVersion() string { + q := CloudproviderManager.Query("version").Equals("cloudaccount_id", self.Id).Distinct() + rows, err := q.Rows() + if err != nil { + return "" + } + ret := make([]string, 0) + for rows.Next() { + var projId string + err := rows.Scan(&projId) + if err != nil { + return "" + } + ret = append(ret, projId) + } + return strings.Join(ret, ",") +} + func (self *SCloudaccount) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict { - providers := self.GetCloudproviders() - extra.Add(jsonutils.NewInt(int64(len(providers))), "account_count") - extra.Add(jsonutils.Marshal(providers), "accounts") + extra.Add(jsonutils.NewInt(int64(self.getProviderCount())), "provider_count") + extra.Add(jsonutils.NewInt(int64(self.getHostCount())), "host_count") + extra.Add(jsonutils.NewInt(int64(self.getGuestCount())), "guest_count") + extra.Add(jsonutils.NewInt(int64(self.getDiskCount())), "disk_count") + extra.Add(jsonutils.NewString(self.getVersion()), "version") + projects := jsonutils.NewArray() + for _, projectId := range self.getProjectIds() { + if proj, _ := db.TenantCacheManager.FetchTenantById(context.Background(), projectId); proj != nil { + projJson := jsonutils.NewDict() + projJson.Add(jsonutils.NewString(proj.Name), "tenant") + projJson.Add(jsonutils.NewString(proj.Id), "tenant_id") + projects.Add(projJson) + } + } + extra.Add(projects, "projects") return extra } @@ -578,7 +679,7 @@ func migrateCloudprovider(cloudprovider *SCloudprovider) error { account.AccessUrl = cloudprovider.AccessUrl account.Account = mainAccount account.LastSync = cloudprovider.LastSync - account.Sysinfo = cloudprovider.Sysinfo + // account.Sysinfo = cloudprovider.Sysinfo account.Provider = cloudprovider.Provider account.Name = providerName account.Status = cloudprovider.Status @@ -704,3 +805,18 @@ func (self *SCloudaccount) GetVCenterAccessInfo(privateId string) (SVCenterAcces return info, nil } + +func (self *SCloudaccount) AllowPerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return db.IsAdminAllowPerform(userCred, self, "change-project") +} + +func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + providers := self.GetCloudproviders() + if len(providers) > 1 { + return nil, httperrors.NewInvalidStatusError("multiple subaccounts") + } + if len(providers) == 0 { + return nil, httperrors.NewInvalidStatusError("no subaccount") + } + return providers[0].PerformChangeProject(ctx, userCred, query, data) +} diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 759f3c51e1..63ae16a968 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -450,9 +450,10 @@ func (self *SCloudprovider) GetCloudaccount() *SCloudaccount { return CloudaccountManager.FetchCloudaccountById(self.CloudaccountId) } -func (self *SCloudprovider) SaveSysInfo(info jsonutils.JSONObject) { +func (self *SCloudprovider) SaveSysInfo(info jsonutils.JSONObject, version string) { self.GetModelManager().TableSpec().Update(self, func() error { self.Sysinfo = info + self.Version = version return nil }) } diff --git a/pkg/compute/tasks/cloud_provider_sync_info_task.go b/pkg/compute/tasks/cloud_provider_sync_info_task.go index 170c025529..4ae4161630 100644 --- a/pkg/compute/tasks/cloud_provider_sync_info_task.go +++ b/pkg/compute/tasks/cloud_provider_sync_info_task.go @@ -60,13 +60,14 @@ func (self *CloudProviderSyncInfoTask) OnInit(ctx context.Context, obj db.IStand return } + version := driver.GetVersion() sysinfo, err := driver.GetSysInfo() if err != nil { reason := fmt.Sprintf("provider get sysinfo error %s", err) taskFail(ctx, self, provider, reason) return } else { - provider.SaveSysInfo(sysinfo) + provider.SaveSysInfo(sysinfo, version) } syncRangeJson, _ := self.Params.Get("sync_range") diff --git a/pkg/util/aliyun/provider/provider.go b/pkg/util/aliyun/provider/provider.go index 2d5de59229..3cbc985c7b 100644 --- a/pkg/util/aliyun/provider/provider.go +++ b/pkg/util/aliyun/provider/provider.go @@ -79,6 +79,10 @@ func (self *SAliyunProvider) GetSysInfo() (jsonutils.JSONObject, error) { return info, nil } +func (self *SAliyunProvider) GetVersion() string { + return aliyun.ALIYUN_API_VERSION +} + func (self *SAliyunProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { return self.client.GetSubAccounts() } diff --git a/pkg/util/aws/provider/provider.go b/pkg/util/aws/provider/provider.go index 850cde5708..6dbb9a48e4 100644 --- a/pkg/util/aws/provider/provider.go +++ b/pkg/util/aws/provider/provider.go @@ -58,6 +58,10 @@ func (self *SAwsProvider) GetSysInfo() (jsonutils.JSONObject, error) { return info, nil } +func (self *SAwsProvider) GetVersion() string { + return aws.AWS_API_VERSION +} + func (self *SAwsProvider) IsPublicCloud() bool { return true } diff --git a/pkg/util/azure/provider/provider.go b/pkg/util/azure/provider/provider.go index a8a0784694..f4fa7bb6e5 100644 --- a/pkg/util/azure/provider/provider.go +++ b/pkg/util/azure/provider/provider.go @@ -61,6 +61,10 @@ func (self *SAzureProvider) GetSysInfo() (jsonutils.JSONObject, error) { return info, nil } +func (self *SAzureProvider) GetVersion() string { + return azure.AZURE_API_VERSION +} + func (self *SAzureProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { return self.client.GetSubAccounts() } diff --git a/pkg/util/esxi/manager.go b/pkg/util/esxi/manager.go index e3ef4f4252..7492eba052 100644 --- a/pkg/util/esxi/manager.go +++ b/pkg/util/esxi/manager.go @@ -106,6 +106,10 @@ func (cli *SESXiClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { return []cloudprovider.SSubAccount{subAccount}, nil } +func (cli *SESXiClient) GetVersion() string { + return cli.client.ServiceContent.About.Version +} + func (cli *SESXiClient) About() jsonutils.JSONObject { about := jsonutils.Marshal(&cli.client.ServiceContent.About) aboutDict := about.(*jsonutils.JSONDict) diff --git a/pkg/util/esxi/provider/provider.go b/pkg/util/esxi/provider/provider.go index 25eb84f786..82130232c1 100644 --- a/pkg/util/esxi/provider/provider.go +++ b/pkg/util/esxi/provider/provider.go @@ -88,6 +88,10 @@ func (self *SESXiProvider) GetSysInfo() (jsonutils.JSONObject, error) { return self.client.About(), nil } +func (self *SESXiProvider) GetVersion() string { + return self.client.GetVersion() +} + func (self *SESXiProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { return self.client.GetSubAccounts() } diff --git a/pkg/util/qcloud/provider/provider.go b/pkg/util/qcloud/provider/provider.go index d586895705..6a57047365 100644 --- a/pkg/util/qcloud/provider/provider.go +++ b/pkg/util/qcloud/provider/provider.go @@ -64,6 +64,10 @@ func (self *SQcloudProvider) GetSysInfo() (jsonutils.JSONObject, error) { return info, nil } +func (self *SQcloudProvider) GetVersion() string { + return qcloud.QCLOUD_API_VERSION +} + func (self *SQcloudProvider) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { return self.client.GetSubAccounts() } @@ -77,7 +81,11 @@ func (self *SQcloudProvider) GetIRegionById(id string) (cloudprovider.ICloudRegi } func (self *SQcloudProvider) GetBalance() (float64, error) { - return 0.0, nil + balance, err := self.client.QueryAccountBalance() + if err != nil { + return 0.0, err + } + return balance.AvailableAmount, nil } func (self *SQcloudProvider) GetOnPremiseIRegion() (cloudprovider.ICloudRegion, error) { diff --git a/pkg/util/qcloud/qcloud.go b/pkg/util/qcloud/qcloud.go index 7a1a5896a6..ea432a49d8 100644 --- a/pkg/util/qcloud/qcloud.go +++ b/pkg/util/qcloud/qcloud.go @@ -22,7 +22,8 @@ const ( QCLOUD_DEFAULT_REGION = "ap-beijing" - QCLOUD_API_VERSION = "2017-03-12" + QCLOUD_API_VERSION = "2017-03-12" + QCLOUD_BILLING_API_VERSION = "2018-07-09" ) type SQcloudClient struct { @@ -71,6 +72,11 @@ func cbsRequest(client *common.Client, apiName string, params map[string]string) return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params) } +func billingRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + domain := "billing.tencentcloudapi.com" + return _jsonRequest(client, domain, QCLOUD_BILLING_API_VERSION, apiName, params) +} + type QcloudResponse struct { *tchttp.BaseResponse Response *interface{} `json:"Response"` @@ -147,6 +153,14 @@ func (client *SQcloudClient) cbsRequest(apiName string, params map[string]string return cbsRequest(cli, apiName, params) } +func (client *SQcloudClient) billingRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := client.getDefaultClient() + if err != nil { + return nil, err + } + return billingRequest(cli, apiName, params) +} + func (client *SQcloudClient) jsonRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { cli, err := client.getDefaultClient() if err != nil { @@ -264,5 +278,14 @@ type SAccountBalance struct { } func (client *SQcloudClient) QueryAccountBalance() (*SAccountBalance, error) { - return nil, nil + balance := SAccountBalance{} + body, err := client.billingRequest("DescribeAccountBalance", nil) + if err != nil { + log.Errorf("DescribeAccountBalance fail %s", err) + return nil, err + } + log.Debugf("%s", body) + balanceCent, _ := body.Float("Balance") + balance.AvailableAmount = balanceCent / 100.0 + return &balance, nil }