qcloud.go 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package qcloud
  15. import (
  16. "bytes"
  17. "context"
  18. "fmt"
  19. "io"
  20. "net/http"
  21. "net/url"
  22. "strconv"
  23. "strings"
  24. "time"
  25. "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
  26. sdkerrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
  27. tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
  28. "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
  29. "github.com/tencentyun/cos-go-sdk-v5"
  30. "github.com/tencentyun/cos-go-sdk-v5/debug"
  31. "yunion.io/x/jsonutils"
  32. "yunion.io/x/log"
  33. "yunion.io/x/pkg/errors"
  34. "yunion.io/x/pkg/util/httputils"
  35. "yunion.io/x/pkg/util/timeutils"
  36. "yunion.io/x/pkg/utils"
  37. api "yunion.io/x/cloudmux/pkg/apis/compute"
  38. "yunion.io/x/cloudmux/pkg/cloudprovider"
  39. )
  40. const (
  41. CLOUD_PROVIDER_QCLOUD = api.CLOUD_PROVIDER_QCLOUD
  42. CLOUD_PROVIDER_QCLOUD_CN = "腾讯云"
  43. CLOUD_PROVIDER_QCLOUD_EN = "QCloud"
  44. QCLOUD_DEFAULT_REGION = "ap-beijing"
  45. QCLOUD_API_VERSION = "2017-03-12"
  46. QCLOUD_CLB_API_VERSION = "2018-03-17"
  47. QCLOUD_BILLING_API_VERSION = "2018-07-09"
  48. QCLOUD_AUDIT_API_VERSION = "2019-03-19"
  49. QCLOUD_CAM_API_VERSION = "2019-01-16"
  50. QCLOUD_CDB_API_VERSION = "2017-03-20"
  51. QCLOUD_MARIADB_API_VERSION = "2017-03-12"
  52. QCLOUD_POSTGRES_API_VERSION = "2017-03-12"
  53. QCLOUD_SQLSERVER_API_VERSION = "2018-03-28"
  54. QCLOUD_REDIS_API_VERSION = "2018-04-12"
  55. QCLOUD_MEMCACHED_API_VERSION = "2019-03-18"
  56. QCLOUD_SSL_API_VERSION = "2019-12-05"
  57. QCLOUD_CDN_API_VERSION = "2018-06-06"
  58. QCLOUD_MONGODB_API_VERSION = "2019-07-25"
  59. QCLOUD_ES_API_VERSION = "2018-04-16"
  60. QCLOUD_DCDB_API_VERSION = "2018-04-11"
  61. QCLOUD_KAFKA_API_VERSION = "2019-08-19"
  62. QCLOUD_TKE_API_VERSION = "2018-05-25"
  63. QCLOUD_DNS_API_VERSION = "2021-03-23"
  64. QCLOUD_STS_API_VERSION = "2018-08-13"
  65. QCLOUD_TAG_API_VERSION = "2018-08-13"
  66. QCLOUD_WAF_API_VERSION = "2018-01-25"
  67. QCLOUD_ORG_API_VERSION = "2021-03-31"
  68. )
  69. type QcloudClientConfig struct {
  70. cpcfg cloudprovider.ProviderConfig
  71. secretId string
  72. secretKey string
  73. accountId string
  74. appId string
  75. debug bool
  76. }
  77. func NewQcloudClientConfig(secretId, secretKey string) *QcloudClientConfig {
  78. cfg := &QcloudClientConfig{
  79. secretId: secretId,
  80. secretKey: secretKey,
  81. }
  82. return cfg
  83. }
  84. func (cfg *QcloudClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *QcloudClientConfig {
  85. cfg.cpcfg = cpcfg
  86. return cfg
  87. }
  88. func (cfg *QcloudClientConfig) AccountId(accountId string) *QcloudClientConfig {
  89. cfg.accountId = accountId
  90. return cfg
  91. }
  92. func (cfg *QcloudClientConfig) Debug(debug bool) *QcloudClientConfig {
  93. cfg.debug = debug
  94. return cfg
  95. }
  96. type SQcloudClient struct {
  97. *QcloudClientConfig
  98. masterAccountId string
  99. masterAppId string
  100. ownerId string
  101. ownerName string
  102. appId string
  103. iregions []cloudprovider.ICloudRegion
  104. ibuckets []cloudprovider.ICloudBucket
  105. }
  106. func NewQcloudClient(cfg *QcloudClientConfig) (*SQcloudClient, error) {
  107. client := SQcloudClient{
  108. QcloudClientConfig: cfg,
  109. }
  110. caller, err := client.GetCallerIdentity()
  111. if err != nil {
  112. return nil, errors.Wrapf(err, "GetCallerIdentity")
  113. }
  114. client.masterAppId, err = client.GetAppId()
  115. if err != nil {
  116. return nil, errors.Wrapf(err, "GetAppId")
  117. }
  118. client.masterAccountId = caller.AccountId
  119. err = client.fetchRegions()
  120. if err != nil {
  121. return nil, errors.Wrap(err, "fetchRegions")
  122. }
  123. return &client, nil
  124. }
  125. // 默认接口请求频率限制:20次/秒
  126. // 部分接口支持金融区地域。由于金融区和非金融区是隔离不互通的,因此当公共参数 Region 为金融区地域(例如 ap-shanghai-fsi)时,需要同时指定带金融区地域的域名,最好和 Region 的地域保持一致,例如:clb.ap-shanghai-fsi.tencentcloudapi.com
  127. // https://cloud.tencent.com/document/product/416/6479
  128. func apiDomain(product string, params map[string]string) string {
  129. regionId, _ := params["Region"]
  130. return apiDomainByRegion(product, regionId)
  131. }
  132. func apiDomainByRegion(product, regionId string) string {
  133. if strings.HasSuffix(regionId, "-fsi") {
  134. return product + "." + regionId + ".tencentcloudapi.com"
  135. } else {
  136. return product + ".tencentcloudapi.com"
  137. }
  138. }
  139. func jsonRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool, retry bool) (jsonutils.JSONObject, error) {
  140. domain := apiDomain("cvm", params)
  141. return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params, updateFunc, debug, retry)
  142. }
  143. func tkeRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  144. domain := "tke.tencentcloudapi.com"
  145. return _jsonRequest(client, domain, QCLOUD_TKE_API_VERSION, apiName, params, updateFunc, debug, true)
  146. }
  147. func vpcRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  148. domain := apiDomain("vpc", params)
  149. return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params, updateFunc, debug, true)
  150. }
  151. func orgRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  152. domain := "organization.tencentcloudapi.com"
  153. return _jsonRequest(client, domain, QCLOUD_ORG_API_VERSION, apiName, params, updateFunc, debug, true)
  154. }
  155. func auditRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  156. domain := apiDomain("cloudaudit", params)
  157. return _jsonRequest(client, domain, QCLOUD_AUDIT_API_VERSION, apiName, params, updateFunc, debug, true)
  158. }
  159. func cbsRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  160. domain := apiDomain("cbs", params)
  161. return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params, updateFunc, debug, true)
  162. }
  163. // es
  164. func esRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  165. domain := apiDomain("es", params)
  166. return _jsonRequest(client, domain, QCLOUD_ES_API_VERSION, apiName, params, updateFunc, debug, true)
  167. }
  168. // waf
  169. func wafRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  170. domain := apiDomain("waf", params)
  171. return _jsonRequest(client, domain, QCLOUD_WAF_API_VERSION, apiName, params, updateFunc, debug, true)
  172. }
  173. // kafka
  174. func kafkaRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  175. domain := apiDomain("ckafka", params)
  176. return _jsonRequest(client, domain, QCLOUD_KAFKA_API_VERSION, apiName, params, updateFunc, debug, true)
  177. }
  178. // redis
  179. func redisRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  180. domain := apiDomain("redis", params)
  181. return _jsonRequest(client, domain, QCLOUD_REDIS_API_VERSION, apiName, params, updateFunc, debug, true)
  182. }
  183. // tdsql
  184. func dcdbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  185. domain := apiDomain("dcdb", params)
  186. return _jsonRequest(client, domain, QCLOUD_DCDB_API_VERSION, apiName, params, updateFunc, debug, true)
  187. }
  188. // mongodb
  189. func mongodbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  190. domain := apiDomain("mongodb", params)
  191. return _jsonRequest(client, domain, QCLOUD_MONGODB_API_VERSION, apiName, params, updateFunc, debug, true)
  192. }
  193. // memcached
  194. func memcachedRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  195. domain := apiDomain("memcached", params)
  196. return _jsonRequest(client, domain, QCLOUD_MEMCACHED_API_VERSION, apiName, params, updateFunc, debug, true)
  197. }
  198. // loadbalancer服务 api 3.0
  199. func clbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  200. domain := apiDomain("clb", params)
  201. return _jsonRequest(client, domain, QCLOUD_CLB_API_VERSION, apiName, params, updateFunc, debug, true)
  202. }
  203. // cdb
  204. func cdbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  205. domain := apiDomain("cdb", params)
  206. return _jsonRequest(client, domain, QCLOUD_CDB_API_VERSION, apiName, params, updateFunc, debug, true)
  207. }
  208. // mariadb
  209. func mariadbRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  210. domain := apiDomain("mariadb", params)
  211. return _jsonRequest(client, domain, QCLOUD_MARIADB_API_VERSION, apiName, params, updateFunc, debug, true)
  212. }
  213. // postgres
  214. func postgresRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  215. domain := apiDomain("postgres", params)
  216. return _jsonRequest(client, domain, QCLOUD_POSTGRES_API_VERSION, apiName, params, updateFunc, debug, true)
  217. }
  218. // sqlserver
  219. func sqlserverRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  220. domain := apiDomain("sqlserver", params)
  221. return _jsonRequest(client, domain, QCLOUD_SQLSERVER_API_VERSION, apiName, params, updateFunc, debug, true)
  222. }
  223. // ssl 证书服务
  224. func sslRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  225. domain := "ssl.tencentcloudapi.com"
  226. return _jsonRequest(client, domain, QCLOUD_SSL_API_VERSION, apiName, params, updateFunc, debug, true)
  227. }
  228. // dnspod 解析服务
  229. func dnsRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  230. domain := "dnspod.tencentcloudapi.com"
  231. return _jsonRequest(client, domain, QCLOUD_DNS_API_VERSION, apiName, params, updateFunc, debug, true)
  232. }
  233. func billingRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  234. domain := "billing.tencentcloudapi.com"
  235. return _jsonRequest(client, domain, QCLOUD_BILLING_API_VERSION, apiName, params, updateFunc, debug, true)
  236. }
  237. func camRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  238. domain := "cam.tencentcloudapi.com"
  239. return _jsonRequest(client, domain, QCLOUD_CAM_API_VERSION, apiName, params, updateFunc, debug, true)
  240. }
  241. func monitorRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string),
  242. debug bool) (jsonutils.JSONObject, error) {
  243. domain := "monitor.tencentcloudapi.com"
  244. return _jsonRequest(client, domain, QCLOUD_API_VERSION_METRICS, apiName, params, updateFunc, debug, true)
  245. }
  246. func cdnRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string),
  247. debug bool) (jsonutils.JSONObject, error) {
  248. domain := "cdn.tencentcloudapi.com"
  249. return _jsonRequest(client, domain, QCLOUD_CDN_API_VERSION, apiName, params, updateFunc, debug, true)
  250. }
  251. func stsRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string),
  252. debug bool) (jsonutils.JSONObject, error) {
  253. domain := "sts.tencentcloudapi.com"
  254. return _jsonRequest(client, domain, QCLOUD_STS_API_VERSION, apiName, params, updateFunc, debug, true)
  255. }
  256. type qcloudResponse interface {
  257. tchttp.Response
  258. GetResponse() *interface{}
  259. }
  260. // 3.0版本通用response
  261. type QcloudResponse struct {
  262. *tchttp.BaseResponse
  263. Response *interface{} `json:"Response"`
  264. }
  265. func (r *QcloudResponse) GetResponse() *interface{} {
  266. return r.Response
  267. }
  268. func _jsonRequest(client *common.Client, domain string, version string, apiName string, params map[string]string, updateFun func(string, string), debug bool, retry bool) (jsonutils.JSONObject, error) {
  269. req := &tchttp.BaseRequest{}
  270. _profile := profile.NewClientProfile()
  271. _profile.SignMethod = common.SHA256
  272. client.WithProfile(_profile)
  273. service := strings.Split(domain, ".")[0]
  274. req.Init().WithApiInfo(service, version, apiName)
  275. req.SetDomain(domain)
  276. for k, v := range params {
  277. if strings.HasSuffix(k, "Ids.0") && len(v) == 0 {
  278. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s = %s", k, v)
  279. }
  280. req.GetParams()[k] = v
  281. }
  282. resp := &QcloudResponse{
  283. BaseResponse: &tchttp.BaseResponse{},
  284. }
  285. ret, err := _baseJsonRequest(client, req, resp, apiName, debug, retry)
  286. if err != nil {
  287. if errors.Cause(err) == cloudprovider.ErrNoPermission && updateFun != nil {
  288. updateFun(service, apiName)
  289. }
  290. return nil, err
  291. }
  292. return ret, nil
  293. }
  294. func _baseJsonRequest(client *common.Client, req tchttp.Request, resp qcloudResponse, apiName string, debug bool, retry bool) (jsonutils.JSONObject, error) {
  295. tryMax := 1
  296. if retry {
  297. tryMax = 3
  298. }
  299. var err error
  300. for i := 1; i <= tryMax; i++ {
  301. err = client.Send(req, resp)
  302. if err == nil {
  303. break
  304. }
  305. needRetry := false
  306. e, ok := err.(*sdkerrors.TencentCloudSDKError)
  307. if ok {
  308. if strings.HasPrefix(e.Code, "UnauthorizedOperation.") ||
  309. strings.HasPrefix(e.Code, "AuthFailure.") ||
  310. utils.IsInStringArray(e.Code, []string{
  311. "SecretidNotAuthAccessResource",
  312. "UnauthorizedOperation",
  313. "InvalidParameter.PermissionDenied",
  314. "AuthFailure",
  315. }) {
  316. return nil, errors.Wrapf(cloudprovider.ErrNoPermission, "%s", err.Error())
  317. }
  318. if utils.IsInStringArray(e.Code, []string{
  319. "AuthFailure.SecretIdNotFound",
  320. "AuthFailure.SignatureFailure",
  321. }) {
  322. return nil, errors.Wrapf(cloudprovider.ErrInvalidAccessKey, "%s", err.Error())
  323. }
  324. if utils.IsInStringArray(e.Code, []string{
  325. "InvalidParameter.RoleNotExist",
  326. "ResourceNotFound",
  327. "FailedOperation.CertificateNotFound",
  328. "ResourceNotFound.OrganizationNotExist",
  329. }) {
  330. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", err.Error())
  331. }
  332. if utils.IsInStringArray(e.Code, []string{
  333. "InvalidParameterValue.ZoneNotSupported",
  334. "UnsupportedRegion",
  335. }) {
  336. return nil, cloudprovider.ErrNotSupported
  337. }
  338. if e.Code == "InvalidParameterValue" && apiName == "GetMonitorData" && strings.Contains(e.Message, "the instance has been destroyed") {
  339. return nil, cloudprovider.ErrNotFound
  340. }
  341. if utils.IsInStringArray(e.Code, []string{
  342. "InternalError",
  343. "MutexOperation.TaskRunning", // Code=DesOperation.MutexTaskRunning, Message=Mutex task is running, please try later
  344. "InvalidInstance.NotSupported", // Code=InvalidInstance.NotSupported, Message=The request does not support the instances `ins-bg54517v` which are in operation or in a special state., 重装系统后立即关机有可能会引发 Code=InvalidInstance.NotSupported 错误, 重试可以避免任务失败
  345. "InvalidAddressId.StatusNotPermit", // Code=InvalidAddressId.StatusNotPermit, Message=The status `"UNBINDING"` for AddressId `"eip-m3kix9kx"` is not permit to do this operation., EIP删除需要重试
  346. "RequestLimitExceeded",
  347. "OperationDenied.InstanceOperationInProgress", // 调整配置后开机 Code=OperationDenied.InstanceOperationInProgress, Message=实例`['ins-nksicizg']`操作进行中,请等待, RequestId=c9951005-b22c-43c1-84aa-d923d49addcf
  348. }) {
  349. needRetry = true
  350. }
  351. }
  352. if !needRetry {
  353. for _, msg := range []string{
  354. "EOF",
  355. "TLS handshake timeout",
  356. "try later",
  357. "i/o timeout",
  358. } {
  359. if strings.Contains(err.Error(), msg) {
  360. needRetry = true
  361. break
  362. }
  363. }
  364. }
  365. if needRetry {
  366. log.Errorf("request url %s\nparams: %s\nerror: %v\ntry after %d seconds", req.GetDomain(), jsonutils.Marshal(req.GetParams()).PrettyString(), err, i*10)
  367. time.Sleep(time.Second * time.Duration(i*10))
  368. continue
  369. }
  370. log.Errorf("request url: %s\nparams: %s\nresponse: %v\nerror: %v", req.GetDomain(), jsonutils.Marshal(req.GetParams()).PrettyString(), resp.GetResponse(), err)
  371. return nil, err
  372. }
  373. if debug {
  374. log.Debugf("request: %s", req.GetParams())
  375. response := resp.GetResponse()
  376. if response != nil {
  377. log.Debugf("response: %s", jsonutils.Marshal(response).PrettyString())
  378. }
  379. }
  380. if err != nil {
  381. return nil, err
  382. }
  383. return jsonutils.Marshal(resp.GetResponse()), nil
  384. }
  385. func (client *SQcloudClient) GetRegions() []SRegion {
  386. regions := make([]SRegion, len(client.iregions))
  387. for i := 0; i < len(regions); i++ {
  388. region := client.iregions[i].(*SRegion)
  389. regions[i] = *region
  390. }
  391. return regions
  392. }
  393. func (client *SQcloudClient) getDefaultClient(params map[string]string) (*common.Client, error) {
  394. regionId := QCLOUD_DEFAULT_REGION
  395. if len(params) > 0 {
  396. if region, ok := params["Region"]; ok {
  397. regionId = region
  398. }
  399. }
  400. return client.getSdkClient(regionId)
  401. }
  402. func (client *SQcloudClient) isSubAccount() bool {
  403. return len(client.accountId) > 0 &&
  404. client.accountId != client.masterAccountId && len(client.masterAccountId) > 0 &&
  405. client.accountId != client.masterAppId && len(client.masterAppId) > 0
  406. }
  407. func (client *SQcloudClient) getSdkClient(regionId string) (*common.Client, error) {
  408. cli, err := common.NewClientWithSecretId(client.secretId, client.secretKey, regionId)
  409. if err != nil {
  410. return nil, err
  411. }
  412. if client.isSubAccount() {
  413. arn := fmt.Sprintf("qcs::cam::uin/%s:roleName/%s", client.accountId, "OrganizationAccessControlRole")
  414. sts := common.DefaultRoleArnProvider(client.secretId, client.secretKey, arn)
  415. cli, err = cli.WithProvider(sts)
  416. if err != nil {
  417. return nil, errors.Wrapf(err, "WithProvider")
  418. }
  419. }
  420. httpClient := client.cpcfg.AdaptiveTimeoutHttpClient()
  421. ts, _ := httpClient.Transport.(*http.Transport)
  422. cli.WithHttpTransport(cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response) error, error) {
  423. body, err := io.ReadAll(req.Body)
  424. if err != nil {
  425. return nil, errors.Wrapf(err, "ioutil.ReadAll")
  426. }
  427. req.Body = io.NopCloser(bytes.NewBuffer(body))
  428. params, err := url.ParseQuery(string(body))
  429. if err != nil {
  430. return nil, errors.Wrapf(err, "ParseQuery(%s)", string(body))
  431. }
  432. service := strings.Split(req.URL.Host, ".")[0]
  433. action := params.Get("Action")
  434. respCheck := func(resp *http.Response) error {
  435. if resp.ContentLength <= 0 {
  436. return nil
  437. }
  438. body, err := io.ReadAll(resp.Body)
  439. if err != nil {
  440. return errors.Wrapf(err, "ioutil.ReadAll")
  441. }
  442. resp.Body = io.NopCloser(bytes.NewBuffer(body))
  443. obj, _ := jsonutils.Parse(body)
  444. code := ""
  445. if obj != nil {
  446. code, _ = obj.GetString("Response", "Error", "Code")
  447. }
  448. if client.cpcfg.UpdatePermission != nil && code == "UnauthorizedOperation" {
  449. client.cpcfg.UpdatePermission(service, action)
  450. }
  451. return nil
  452. }
  453. if client.cpcfg.ReadOnly {
  454. for _, prefix := range []string{"Get", "List", "Describe", "LookUpEvents"} {
  455. if strings.HasPrefix(action, prefix) {
  456. return respCheck, nil
  457. }
  458. }
  459. return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s", action)
  460. }
  461. return respCheck, nil
  462. }))
  463. return cli, nil
  464. }
  465. func (client *SQcloudClient) tkeRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  466. cli, err := client.getDefaultClient(params)
  467. if err != nil {
  468. return nil, err
  469. }
  470. return tkeRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  471. }
  472. func (client *SQcloudClient) vpcRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  473. cli, err := client.getDefaultClient(params)
  474. if err != nil {
  475. return nil, err
  476. }
  477. return vpcRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  478. }
  479. func (client *SQcloudClient) orgRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  480. cli, err := client.getDefaultClient(params)
  481. if err != nil {
  482. return nil, err
  483. }
  484. return orgRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  485. }
  486. func (client *SQcloudClient) auditRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  487. cli, err := client.getDefaultClient(params)
  488. if err != nil {
  489. return nil, err
  490. }
  491. return auditRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  492. }
  493. func (client *SQcloudClient) cbsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  494. cli, err := client.getDefaultClient(params)
  495. if err != nil {
  496. return nil, err
  497. }
  498. return cbsRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  499. }
  500. func (client *SQcloudClient) tagRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  501. cli, err := client.getDefaultClient(params)
  502. if err != nil {
  503. return nil, err
  504. }
  505. return tagRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  506. }
  507. func tagRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
  508. domain := "tag.tencentcloudapi.com"
  509. return _jsonRequest(client, domain, QCLOUD_TAG_API_VERSION, apiName, params, updateFunc, debug, true)
  510. }
  511. func (client *SQcloudClient) clbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  512. cli, err := client.getDefaultClient(params)
  513. if err != nil {
  514. return nil, err
  515. }
  516. return clbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  517. }
  518. func (client *SQcloudClient) cdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  519. cli, err := client.getDefaultClient(params)
  520. if err != nil {
  521. return nil, err
  522. }
  523. return cdbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  524. }
  525. func (client *SQcloudClient) esRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  526. cli, err := client.getDefaultClient(params)
  527. if err != nil {
  528. return nil, err
  529. }
  530. return esRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  531. }
  532. func (client *SQcloudClient) wafRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  533. cli, err := client.getDefaultClient(params)
  534. if err != nil {
  535. return nil, err
  536. }
  537. return wafRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  538. }
  539. func (client *SQcloudClient) kafkaRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  540. cli, err := client.getDefaultClient(params)
  541. if err != nil {
  542. return nil, err
  543. }
  544. return kafkaRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  545. }
  546. func (client *SQcloudClient) redisRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  547. cli, err := client.getDefaultClient(params)
  548. if err != nil {
  549. return nil, err
  550. }
  551. return redisRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  552. }
  553. func (client *SQcloudClient) dcdbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  554. cli, err := client.getDefaultClient(params)
  555. if err != nil {
  556. return nil, err
  557. }
  558. return dcdbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  559. }
  560. func (client *SQcloudClient) mongodbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  561. cli, err := client.getDefaultClient(params)
  562. if err != nil {
  563. return nil, err
  564. }
  565. return mongodbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  566. }
  567. func (client *SQcloudClient) memcachedRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  568. cli, err := client.getDefaultClient(params)
  569. if err != nil {
  570. return nil, err
  571. }
  572. return memcachedRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  573. }
  574. func (client *SQcloudClient) mariadbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  575. cli, err := client.getDefaultClient(params)
  576. if err != nil {
  577. return nil, err
  578. }
  579. return mariadbRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  580. }
  581. func (client *SQcloudClient) postgresRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  582. cli, err := client.getDefaultClient(params)
  583. if err != nil {
  584. return nil, err
  585. }
  586. return postgresRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  587. }
  588. func (client *SQcloudClient) sqlserverRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  589. cli, err := client.getDefaultClient(params)
  590. if err != nil {
  591. return nil, err
  592. }
  593. return sqlserverRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  594. }
  595. func (client *SQcloudClient) sslRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  596. cli, err := client.getDefaultClient(params)
  597. if err != nil {
  598. return nil, err
  599. }
  600. return sslRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  601. }
  602. func (client *SQcloudClient) dnsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  603. cli, err := client.getDefaultClient(params)
  604. if err != nil {
  605. return nil, err
  606. }
  607. return dnsRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  608. }
  609. func (client *SQcloudClient) billingRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  610. cli, err := client.getDefaultClient(params)
  611. if err != nil {
  612. return nil, err
  613. }
  614. return billingRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  615. }
  616. func (client *SQcloudClient) camRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  617. cli, err := client.getDefaultClient(params)
  618. if err != nil {
  619. return nil, err
  620. }
  621. return camRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  622. }
  623. func (client *SQcloudClient) cdnRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  624. cli, err := client.getDefaultClient(params)
  625. if err != nil {
  626. return nil, err
  627. }
  628. return cdnRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  629. }
  630. func (client *SQcloudClient) stsRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
  631. cli, err := client.getDefaultClient(params)
  632. if err != nil {
  633. return nil, err
  634. }
  635. return stsRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
  636. }
  637. func (client *SQcloudClient) jsonRequest(apiName string, params map[string]string, retry bool) (jsonutils.JSONObject, error) {
  638. cli, err := client.getDefaultClient(params)
  639. if err != nil {
  640. return nil, err
  641. }
  642. return jsonRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug, retry)
  643. }
  644. func (client *SQcloudClient) fetchRegions() error {
  645. body, err := client.jsonRequest("DescribeRegions", nil, true)
  646. if err != nil {
  647. log.Errorf("fetchRegions fail %s", err)
  648. return err
  649. }
  650. regions := make([]SRegion, 0)
  651. err = body.Unmarshal(&regions, "RegionSet")
  652. if err != nil {
  653. log.Errorf("unmarshal json error %s", err)
  654. return err
  655. }
  656. client.iregions = make([]cloudprovider.ICloudRegion, len(regions))
  657. for i := 0; i < len(regions); i++ {
  658. regions[i].client = client
  659. client.iregions[i] = &regions[i]
  660. }
  661. return nil
  662. }
  663. func (client *SQcloudClient) getCosClient(bucket *SBucket) (*cos.Client, error) {
  664. var baseUrl *cos.BaseURL
  665. if bucket != nil {
  666. u, _ := url.Parse(bucket.getBucketUrl())
  667. baseUrl = &cos.BaseURL{
  668. BucketURL: u,
  669. }
  670. }
  671. ts := &http.Transport{
  672. Proxy: client.cpcfg.ProxyFunc,
  673. }
  674. cosClient := cos.NewClient(
  675. baseUrl,
  676. &http.Client{
  677. Transport: &cos.AuthorizationTransport{
  678. SecretID: client.secretId,
  679. SecretKey: client.secretKey,
  680. Transport: &debug.DebugRequestTransport{
  681. RequestHeader: client.debug,
  682. RequestBody: client.debug,
  683. ResponseHeader: client.debug,
  684. ResponseBody: client.debug,
  685. Transport: cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response) error, error) {
  686. method, path := req.Method, req.URL.Path
  687. respCheck := func(resp *http.Response) error {
  688. if resp.StatusCode == 403 {
  689. if client.cpcfg.UpdatePermission != nil {
  690. client.cpcfg.UpdatePermission("cos", fmt.Sprintf("%s %s", method, path))
  691. }
  692. }
  693. return nil
  694. }
  695. if client.cpcfg.ReadOnly {
  696. if req.Method == "GET" || req.Method == "HEAD" {
  697. return respCheck, nil
  698. }
  699. return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
  700. }
  701. return respCheck, nil
  702. }),
  703. },
  704. },
  705. },
  706. )
  707. return cosClient, nil
  708. }
  709. func (self *SQcloudClient) invalidateIBuckets() {
  710. self.ibuckets = nil
  711. }
  712. func (self *SQcloudClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) {
  713. if self.isSubAccount() {
  714. return nil, fmt.Errorf("cos not support sub account sync")
  715. }
  716. if self.ibuckets == nil {
  717. err := self.fetchBuckets()
  718. if err != nil {
  719. return nil, errors.Wrap(err, "fetchBuckets")
  720. }
  721. }
  722. return self.ibuckets, nil
  723. }
  724. func (client *SQcloudClient) verifyAppId() error {
  725. region, err := client.getDefaultRegion()
  726. if err != nil {
  727. return errors.Wrap(err, "getDefaultRegion")
  728. }
  729. bucket := SBucket{
  730. region: region,
  731. Name: "yuniondocument",
  732. }
  733. cli, err := client.getCosClient(&bucket)
  734. if err != nil {
  735. return errors.Wrap(err, "getCosClient")
  736. }
  737. resp, err := cli.Bucket.Head(context.Background())
  738. if resp != nil {
  739. defer httputils.CloseResponse(resp.Response)
  740. if resp.StatusCode < 400 || resp.StatusCode == 404 {
  741. return nil
  742. }
  743. return errors.Error(fmt.Sprintf("invalid AppId: %d", resp.StatusCode))
  744. }
  745. return errors.Wrap(err, "Head")
  746. }
  747. func (client *SQcloudClient) getOwnerName() string {
  748. if len(client.ownerName) > 0 {
  749. return client.ownerName
  750. }
  751. coscli, err := client.getCosClient(nil)
  752. if err != nil {
  753. return ""
  754. }
  755. s, _, err := coscli.Service.Get(context.Background())
  756. if err != nil {
  757. return ""
  758. }
  759. client.ownerId = s.Owner.ID
  760. client.ownerName = s.Owner.DisplayName
  761. return client.ownerName
  762. }
  763. func (client *SQcloudClient) fetchBuckets() error {
  764. coscli, err := client.getCosClient(nil)
  765. if err != nil {
  766. return errors.Wrap(err, "GetCosClient")
  767. }
  768. s, _, err := coscli.Service.Get(context.Background())
  769. if err != nil {
  770. return errors.Wrap(err, "coscli.Service.Get")
  771. }
  772. client.ownerId = s.Owner.ID
  773. client.ownerName = s.Owner.DisplayName
  774. ret := make([]cloudprovider.ICloudBucket, 0)
  775. for i := range s.Buckets {
  776. bInfo := s.Buckets[i]
  777. createAt, _ := timeutils.ParseTimeStr(bInfo.CreationDate)
  778. slashPos := strings.LastIndexByte(bInfo.Name, '-')
  779. appId := bInfo.Name[slashPos+1:]
  780. _appId, _ := client.GetAppId()
  781. if appId != _appId {
  782. log.Errorf("[%s %s] Inconsistent appId: %s expect %s", bInfo.Name, bInfo.Region, appId, _appId)
  783. }
  784. name := bInfo.Name[:slashPos]
  785. region, err := client.getIRegionByRegionId(bInfo.Region)
  786. var zone *SZone = nil
  787. if err != nil {
  788. log.Errorf("fail to find region %s", bInfo.Region)
  789. // possibly a zone, try zone
  790. regionStr := func() string {
  791. info := strings.Split(bInfo.Region, "-")
  792. if num, _ := strconv.Atoi(info[len(info)-1]); num > 0 {
  793. return strings.TrimSuffix(bInfo.Region, fmt.Sprintf("-%d", num))
  794. }
  795. return bInfo.Region
  796. }()
  797. region, err = client.getIRegionByRegionId(regionStr)
  798. if err != nil {
  799. log.Errorf("fail to find region %s", regionStr)
  800. continue
  801. }
  802. zoneId := bInfo.Region
  803. zone, err = region.(*SRegion).getZoneById(bInfo.Region)
  804. if err != nil {
  805. log.Errorf("fail to find zone %s", zoneId)
  806. continue
  807. }
  808. zoneId = zone.GetId()
  809. log.Debugf("find zonal bucket %s", zoneId)
  810. }
  811. b := SBucket{
  812. region: region.(*SRegion),
  813. appId: appId,
  814. Name: name,
  815. Location: bInfo.Region,
  816. CreateDate: createAt,
  817. }
  818. if zone != nil {
  819. b.zone = zone
  820. }
  821. ret = append(ret, &b)
  822. }
  823. client.ibuckets = ret
  824. return nil
  825. }
  826. func (client *SQcloudClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
  827. nodes, err := client.DescribeOrganizationMembers()
  828. if err != nil && errors.Cause(err) != cloudprovider.ErrNotFound && errors.Cause(err) != cloudprovider.ErrNoPermission {
  829. return nil, err
  830. }
  831. subAccount := cloudprovider.SSubAccount{}
  832. subAccount.Id = client.GetAccountId()
  833. subAccount.Name = client.cpcfg.Name
  834. subAccount.Account = client.secretId
  835. if len(client.appId) > 0 { // 兼容旧版本账号,避免订阅删除
  836. subAccount.Account = fmt.Sprintf("%s/%s", client.secretId, client.appId)
  837. }
  838. subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL
  839. subAccount.DefaultProjectId = "0"
  840. ret := []cloudprovider.SSubAccount{subAccount}
  841. for _, node := range nodes {
  842. uin := fmt.Sprintf("%d", node.MemberUin)
  843. if len(subAccount.Id) > 0 && uin != subAccount.Id {
  844. account := cloudprovider.SSubAccount{}
  845. account.Id = uin
  846. account.Name = node.Name
  847. account.Account = fmt.Sprintf("%s/%s", client.secretId, uin)
  848. account.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL
  849. account.DefaultProjectId = "0"
  850. ret = append(ret, account)
  851. }
  852. }
  853. return ret, nil
  854. }
  855. func (self *SQcloudClient) GetAccountId() string {
  856. if len(self.ownerId) == 0 {
  857. caller, err := self.GetCallerIdentity()
  858. if err == nil {
  859. self.ownerId = caller.AccountId
  860. }
  861. }
  862. return self.ownerId
  863. }
  864. func (client *SQcloudClient) GetIamLoginUrl() string {
  865. return fmt.Sprintf("https://cloud.tencent.com/login/subAccount")
  866. }
  867. func (client *SQcloudClient) GetIRegions() ([]cloudprovider.ICloudRegion, error) {
  868. return client.iregions, nil
  869. }
  870. func (client *SQcloudClient) getDefaultRegion() (*SRegion, error) {
  871. iregion, err := client.getIRegionByRegionId(QCLOUD_DEFAULT_REGION)
  872. if err != nil {
  873. return nil, errors.Wrap(err, "getIRegionByRegionId")
  874. }
  875. return iregion.(*SRegion), nil
  876. }
  877. func (client *SQcloudClient) getIRegionByRegionId(id string) (cloudprovider.ICloudRegion, error) {
  878. for i := 0; i < len(client.iregions); i++ {
  879. if client.iregions[i].GetId() == id {
  880. return client.iregions[i], nil
  881. }
  882. }
  883. return nil, cloudprovider.ErrNotFound
  884. }
  885. func (client *SQcloudClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
  886. for i := 0; i < len(client.iregions); i++ {
  887. if client.iregions[i].GetGlobalId() == id {
  888. return client.iregions[i], nil
  889. }
  890. }
  891. return nil, cloudprovider.ErrNotFound
  892. }
  893. func (client *SQcloudClient) GetRegion(regionId string) *SRegion {
  894. for i := 0; i < len(client.iregions); i++ {
  895. if client.iregions[i].GetId() == regionId {
  896. return client.iregions[i].(*SRegion)
  897. }
  898. }
  899. return nil
  900. }
  901. func (client *SQcloudClient) GetIHostById(id string) (cloudprovider.ICloudHost, error) {
  902. for i := 0; i < len(client.iregions); i++ {
  903. ihost, err := client.iregions[i].GetIHostById(id)
  904. if err == nil {
  905. return ihost, nil
  906. } else if errors.Cause(err) != cloudprovider.ErrNotFound {
  907. return nil, err
  908. }
  909. }
  910. return nil, cloudprovider.ErrNotFound
  911. }
  912. func (client *SQcloudClient) GetIVpcById(id string) (cloudprovider.ICloudVpc, error) {
  913. for i := 0; i < len(client.iregions); i++ {
  914. ihost, err := client.iregions[i].GetIVpcById(id)
  915. if err == nil {
  916. return ihost, nil
  917. } else if errors.Cause(err) != cloudprovider.ErrNotFound {
  918. return nil, err
  919. }
  920. }
  921. return nil, cloudprovider.ErrNotFound
  922. }
  923. func (client *SQcloudClient) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) {
  924. for i := 0; i < len(client.iregions); i++ {
  925. ihost, err := client.iregions[i].GetIStorageById(id)
  926. if err == nil {
  927. return ihost, nil
  928. } else if errors.Cause(err) != cloudprovider.ErrNotFound {
  929. return nil, err
  930. }
  931. }
  932. return nil, cloudprovider.ErrNotFound
  933. }
  934. type SAccountBalance struct {
  935. Balance float64
  936. Uin int64
  937. Currency string
  938. CashAccountBalance float64
  939. CreditAmount float64
  940. CreditBalance float64
  941. FreezeAmount float64
  942. }
  943. func (client *SQcloudClient) QueryAccountBalance() (*SAccountBalance, error) {
  944. body, err := client.billingRequest("DescribeAccountBalance", nil)
  945. if err != nil {
  946. if isError(err, []string{"UnauthorizedOperation.NotFinanceAuth"}) {
  947. return nil, cloudprovider.ErrNoBalancePermission
  948. }
  949. return nil, errors.Wrapf(err, "DescribeAccountBalance")
  950. }
  951. balance := &SAccountBalance{Currency: "CNY"}
  952. err = body.Unmarshal(balance)
  953. if err != nil {
  954. return nil, err
  955. }
  956. amount := balance.Balance / 100.0
  957. if balance.CreditAmount > 0 {
  958. amount = (balance.CashAccountBalance + balance.CreditAmount + balance.Balance - balance.FreezeAmount) / 100.0
  959. }
  960. balance.Balance = amount
  961. if balance.Uin >= 200000000000 {
  962. balance.Currency = "USD"
  963. }
  964. return balance, nil
  965. }
  966. type SBillSummary struct {
  967. Ready int `json:"Ready"`
  968. SummaryDetail []SBillSummaryItem `json:"SummaryDetail"`
  969. TotalAmount float64 `json:"TotalAmount"`
  970. }
  971. type SBillSummaryItem struct {
  972. Business []jsonutils.JSONObject `json:"Business"`
  973. CashPayAmount float64 `json:"CashPayAmount"`
  974. GroupKey string `json:"GroupKey"`
  975. GroupValue string `json:"GroupValue"`
  976. IncentivePayAmount float64 `json:"IncentivePayAmount"`
  977. RealTotalCost float64 `json:"RealTotalCost"`
  978. TotalCost float64 `json:"TotalCost"`
  979. TransferPayAmount float64 `json:"TransferPayAmount"`
  980. VoucherPayAmount float64 `json:"VoucherPayAmount"`
  981. }
  982. func (client *SQcloudClient) DescribeBillSummary(month string, uin string) (*SBillSummary, error) {
  983. params := make(map[string]string)
  984. params["Month"] = month
  985. params["GroupType"] = "business"
  986. if len(uin) > 0 {
  987. params["Uin"] = uin
  988. }
  989. body, err := client.billingRequest("DescribeBillSummary", params)
  990. if err != nil {
  991. return nil, errors.Wrapf(err, "DescribeBillSummary")
  992. }
  993. summary := SBillSummary{}
  994. err = body.Unmarshal(&summary)
  995. if err != nil {
  996. return nil, errors.Wrapf(err, "body.Unmarshal")
  997. }
  998. for _, item := range summary.SummaryDetail {
  999. summary.TotalAmount += item.RealTotalCost
  1000. }
  1001. return &summary, nil
  1002. }
  1003. func (client *SQcloudClient) GetIProjects() ([]cloudprovider.ICloudProject, error) {
  1004. projects := []SProject{}
  1005. for {
  1006. part, total, err := client.GetProjects(len(projects), 1000)
  1007. if err != nil {
  1008. return nil, errors.Wrap(err, "GetProjects")
  1009. }
  1010. projects = append(projects, part...)
  1011. if len(projects) >= total || len(part) == 0 {
  1012. break
  1013. }
  1014. }
  1015. projects = append(projects, SProject{ProjectId: "0", ProjectName: "默认项目"})
  1016. iprojects := []cloudprovider.ICloudProject{}
  1017. for i := 0; i < len(projects); i++ {
  1018. projects[i].client = client
  1019. iprojects = append(iprojects, &projects[i])
  1020. }
  1021. return iprojects, nil
  1022. }
  1023. func (client *SQcloudClient) GetAppId() (string, error) {
  1024. if len(client.appId) > 0 {
  1025. return client.appId, nil
  1026. }
  1027. resp, err := client.camRequest("GetUserAppId", map[string]string{})
  1028. if err != nil {
  1029. return "", errors.Wrapf(err, "GetUserAppId")
  1030. }
  1031. client.appId, err = resp.GetString("AppId")
  1032. return client.appId, err
  1033. }
  1034. func (self *SQcloudClient) GetISSLCertificates() ([]cloudprovider.ICloudSSLCertificate, error) {
  1035. rs, err := self.GetCertificates("", "", "")
  1036. if err != nil {
  1037. return nil, err
  1038. }
  1039. result := make([]cloudprovider.ICloudSSLCertificate, 0)
  1040. for i := range rs {
  1041. rs[i].client = self
  1042. result = append(result, &rs[i])
  1043. }
  1044. return result, nil
  1045. }
  1046. func (client *SQcloudClient) GetCapabilities() []string {
  1047. caps := []string{
  1048. cloudprovider.CLOUD_CAPABILITY_PROJECT,
  1049. cloudprovider.CLOUD_CAPABILITY_COMPUTE,
  1050. cloudprovider.CLOUD_CAPABILITY_NETWORK,
  1051. cloudprovider.CLOUD_CAPABILITY_SECURITY_GROUP,
  1052. cloudprovider.CLOUD_CAPABILITY_EIP,
  1053. cloudprovider.CLOUD_CAPABILITY_LOADBALANCER,
  1054. cloudprovider.CLOUD_CAPABILITY_RDS,
  1055. cloudprovider.CLOUD_CAPABILITY_CACHE,
  1056. cloudprovider.CLOUD_CAPABILITY_EVENT,
  1057. cloudprovider.CLOUD_CAPABILITY_CLOUDID,
  1058. cloudprovider.CLOUD_CAPABILITY_DNSZONE,
  1059. cloudprovider.CLOUD_CAPABILITY_PUBLIC_IP,
  1060. cloudprovider.CLOUD_CAPABILITY_SAML_AUTH,
  1061. cloudprovider.CLOUD_CAPABILITY_QUOTA + cloudprovider.READ_ONLY_SUFFIX,
  1062. cloudprovider.CLOUD_CAPABILITY_MONGO_DB + cloudprovider.READ_ONLY_SUFFIX,
  1063. cloudprovider.CLOUD_CAPABILITY_ES + cloudprovider.READ_ONLY_SUFFIX,
  1064. cloudprovider.CLOUD_CAPABILITY_KAFKA + cloudprovider.READ_ONLY_SUFFIX,
  1065. cloudprovider.CLOUD_CAPABILITY_CDN + cloudprovider.READ_ONLY_SUFFIX,
  1066. cloudprovider.CLOUD_CAPABILITY_CONTAINER + cloudprovider.READ_ONLY_SUFFIX,
  1067. cloudprovider.CLOUD_CAPABILITY_CERT,
  1068. cloudprovider.CLOUD_CAPABILITY_NAT + cloudprovider.READ_ONLY_SUFFIX,
  1069. cloudprovider.CLOUD_CAPABILITY_SNAPSHOT_POLICY,
  1070. cloudprovider.CLOUD_CAPABILITY_WAF + cloudprovider.READ_ONLY_SUFFIX,
  1071. }
  1072. // 官方cos sdk 未支持sts
  1073. if !client.isSubAccount() {
  1074. caps = append(caps, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE)
  1075. }
  1076. return caps
  1077. }