diff --git a/cmd/climc/shell/cloudproviders.go b/cmd/climc/shell/cloudproviders.go index 38e36bd697..1ca31235f0 100644 --- a/cmd/climc/shell/cloudproviders.go +++ b/cmd/climc/shell/cloudproviders.go @@ -130,6 +130,15 @@ func init() { return nil }) + R(&CloudproviderShowOptions{}, "cloud-provider-balance", "Get balance", func(s *mcclient.ClientSession, args *CloudproviderShowOptions) error { + result, err := modules.Cloudproviders.GetSpecific(s, args.ID, "balance", nil) + if err != nil { + return err + } + printObject(result) + return nil + }) + type CloudproviderUpdateCredentialOptions struct { ID string `help:"ID or Name of cloud provider"` ACCOUNT string `help:"new account"` diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index 048b9e0259..aba32cb980 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -25,6 +25,8 @@ type ICloudProvider interface { GetIVpcById(id string) (ICloudVpc, error) GetIStorageById(id string) (ICloudStorage, error) GetIStoragecacheById(id string) (ICloudStoragecache, error) + + GetBalance() (float64, error) } var providerTable map[string]ICloudProviderFactory diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 45da73b25a..d894456712 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -499,3 +499,25 @@ func (manager *SCloudproviderManager) migrateVCenterInfo(vc *SVCenter) error { return manager.TableSpec().Insert(&cp) } + +func (self *SCloudprovider) GetBalance() (float64, error) { + driver, err := self.GetDriver() + if err != nil { + return 0.0, err + } + return driver.GetBalance() +} + +func (self *SCloudprovider) AllowGetDetailsBalance(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return userCred.IsSystemAdmin() +} + +func (self *SCloudprovider) 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/util/aliyun/aliyun.go b/pkg/util/aliyun/aliyun.go index 9d002a46d1..c8a334a57c 100644 --- a/pkg/util/aliyun/aliyun.go +++ b/pkg/util/aliyun/aliyun.go @@ -8,6 +8,7 @@ import ( "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" + "time" ) const ( @@ -17,6 +18,8 @@ const ( ALIYUN_DEFAULT_REGION = "cn-hangzhou" ALIYUN_API_VERSION = "2014-05-26" + + ALIYUN_BSS_API_VERSION = "2017-12-14" ) type SAliyunClient struct { @@ -37,9 +40,17 @@ func NewAliyunClient(providerId string, providerName string, accessKey string, s } func jsonRequest(client *sdk.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + return _jsonRequest(client, "ecs.aliyuncs.com", ALIYUN_API_VERSION, apiName, params) +} + +func businessRequest(client *sdk.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + return _jsonRequest(client, "business.aliyuncs.com", ALIYUN_BSS_API_VERSION, apiName, params) +} + +func _jsonRequest(client *sdk.Client, domain string, version string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { req := requests.NewCommonRequest() - req.Domain = "ecs.aliyuncs.com" - req.Version = ALIYUN_API_VERSION + req.Domain = domain + req.Version = version req.ApiName = apiName if params != nil { for k, v := range params { @@ -82,6 +93,14 @@ func (self *SAliyunClient) jsonRequest(apiName string, params map[string]string) return jsonRequest(cli, apiName, params) } +func (self *SAliyunClient) businessRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := self.getDefaultClient() + if err != nil { + return nil, err + } + return businessRequest(cli, apiName, params) +} + func (self *SAliyunClient) fetchRegions() error { body, err := self.jsonRequest("DescribeRegions", nil) if err != nil { @@ -184,3 +203,85 @@ func (self *SAliyunClient) GetIStoragecacheById(id string) (cloudprovider.ICloud } return nil, cloudprovider.ErrNotFound } + +type SAccountBalance struct { + AvailableAmount float64 + AvailableCashAmount float64 + CreditAmount float64 + MybankCreditAmount float64 + Currency string +} + +type SCashCoupon struct { + ApplicableProducts string + ApplicableScenarios string + Balance float64 + CashCouponId string + CashCouponNo string + EffectiveTime time.Time + ExpiryTime time.Time + GrantedTime time.Time + NominalValue float64 + Status string +} + +type SPrepaidCard struct { + PrepaidCardId string + PrepaidCardNo string + GrantedTime time.Time + EffectiveTime time.Time + ExpiryTime time.Time + NominalValue float64 + Balance float64 + ApplicableProducts string + ApplicableScenarios string +} + +func (self *SAliyunClient) QueryAccountBalance() (*SAccountBalance, error) { + body, err := self.businessRequest("QueryAccountBalance", nil) + if err != nil { + log.Errorf("QueryAccountBalance fail %s", err) + return nil, err + } + balance := SAccountBalance{} + err = body.Unmarshal(&balance, "Data") + if err != nil { + log.Errorf("Unmarshal AccountBalance fail %s", err) + return nil, err + } + return &balance, nil +} + +func (self *SAliyunClient) QueryCashCoupons() ([]SCashCoupon, error) { + params := make(map[string]string) + params["EffectiveOrNot"] = "True" + body, err := self.businessRequest("QueryCashCoupons", params) + if err != nil { + log.Errorf("QueryCashCoupons fail %s", err) + return nil, err + } + coupons := make([]SCashCoupon, 0) + err = body.Unmarshal(&coupons, "Data", "CashCoupon") + if err != nil { + log.Errorf("Unmarshal fail %s", err) + return nil, err + } + return coupons, nil +} + +func (self *SAliyunClient) QueryPrepaidCards() ([]SPrepaidCard, error) { + params := make(map[string]string) + params["EffectiveOrNot"] = "True" + body, err := self.businessRequest("QueryPrepaidCards", params) + if err != nil { + log.Errorf("QueryPrepaidCards fail %s", err) + return nil, err + } + cards := make([]SPrepaidCard, 0) + err = body.Unmarshal(&cards, "Data", "PrepaidCard") + if err != nil { + log.Errorf("Unmarshal fail %s", err) + return nil, err + } + return cards, nil +} \ No newline at end of file diff --git a/pkg/util/aliyun/provider/provider.go b/pkg/util/aliyun/provider/provider.go index 563e4a2ed3..e9150ea9d7 100644 --- a/pkg/util/aliyun/provider/provider.go +++ b/pkg/util/aliyun/provider/provider.go @@ -87,3 +87,11 @@ func (self *SAliyunProvider) GetIStorageById(id string) (cloudprovider.ICloudSto func (self *SAliyunProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { return self.client.GetIStoragecacheById(id) } + +func (self *SAliyunProvider) GetBalance() (float64, error) { + balance, err := self.client.QueryAccountBalance() + if err != nil { + return 0.0, err + } + return balance.AvailableAmount, nil +} \ No newline at end of file diff --git a/pkg/util/aliyun/shell/business.go b/pkg/util/aliyun/shell/business.go new file mode 100644 index 0000000000..3145545955 --- /dev/null +++ b/pkg/util/aliyun/shell/business.go @@ -0,0 +1,31 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/aliyun" + "yunion.io/x/onecloud/pkg/util/shellutils" +) + +func init() { + type AccountBalanceOptions struct { + } + shellutils.R(&AccountBalanceOptions{}, "balance", "Get account balance", func(cli *aliyun.SRegion, args *AccountBalanceOptions) error { + result1, err := cli.GetClient().QueryAccountBalance() + if err != nil { + return err + } + printObject(result1) + + result2, err := cli.GetClient().QueryCashCoupons() + if err != nil { + return err + } + printList(result2, len(result2), 0, 0, nil) + + result3, err := cli.GetClient().QueryPrepaidCards() + if err != nil { + return err + } + printList(result3, len(result3), 0, 0, nil) + return nil + }) +} diff --git a/pkg/util/esxi/provider/provider.go b/pkg/util/esxi/provider/provider.go index 32137f8d7d..d8b83200b7 100644 --- a/pkg/util/esxi/provider/provider.go +++ b/pkg/util/esxi/provider/provider.go @@ -115,3 +115,7 @@ func (self *SESXiProvider) GetIStorageById(id string) (cloudprovider.ICloudStora func (self *SESXiProvider) GetIStoragecacheById(id string) (cloudprovider.ICloudStoragecache, error) { return nil, cloudprovider.ErrNotImplemented } + +func (self *SESXiProvider) GetBalance() (float64, error) { + return 0.0, nil +} \ No newline at end of file