cloudpods.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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 cloudpods
  15. import (
  16. "context"
  17. "fmt"
  18. "net/http"
  19. "strings"
  20. api "yunion.io/x/cloudmux/pkg/apis/compute"
  21. "yunion.io/x/cloudmux/pkg/cloudprovider"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/util/printutils"
  25. "yunion.io/x/onecloud/pkg/mcclient"
  26. "yunion.io/x/onecloud/pkg/mcclient/modules"
  27. "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  28. )
  29. const (
  30. CLOUD_PROVIDER_CLOUDPODS = api.CLOUD_PROVIDER_CLOUDPODS
  31. CLOUDPODS_DEFAULT_REGION = "default"
  32. )
  33. var (
  34. defaultParams map[string]interface{} = map[string]interface{}{
  35. "details": true,
  36. "show_emulated": true,
  37. "scope": "system",
  38. "cloud_env": "onpremise",
  39. }
  40. )
  41. type ModelManager interface {
  42. List(session *mcclient.ClientSession, params jsonutils.JSONObject) (*printutils.ListResult, error)
  43. Create(session *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
  44. Delete(session *mcclient.ClientSession, id string, param jsonutils.JSONObject) (jsonutils.JSONObject, error)
  45. DeleteWithParam(session *mcclient.ClientSession, id string, query jsonutils.JSONObject, body jsonutils.JSONObject) (jsonutils.JSONObject, error)
  46. PerformAction(session *mcclient.ClientSession, id string, action string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
  47. Get(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
  48. Update(session *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error)
  49. GetKeyword() string
  50. }
  51. type CloudpodsClientConfig struct {
  52. cpcfg cloudprovider.ProviderConfig
  53. authURL string
  54. region string
  55. accessKey string
  56. accessSecret string
  57. adminProjectId string
  58. debug bool
  59. }
  60. func NewCloudpodsClientConfig(authURL, accessKey, accessSecret string) *CloudpodsClientConfig {
  61. cfg := &CloudpodsClientConfig{
  62. authURL: authURL,
  63. accessKey: accessKey,
  64. accessSecret: accessSecret,
  65. }
  66. return cfg
  67. }
  68. func (cfg *CloudpodsClientConfig) Debug(debug bool) *CloudpodsClientConfig {
  69. cfg.debug = debug
  70. return cfg
  71. }
  72. func (cfg *CloudpodsClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *CloudpodsClientConfig {
  73. cfg.cpcfg = cpcfg
  74. return cfg
  75. }
  76. type SCloudpodsClient struct {
  77. *CloudpodsClientConfig
  78. s *mcclient.ClientSession
  79. }
  80. func (self *SCloudpodsClient) auth() error {
  81. client := mcclient.NewClient(self.authURL, 0, self.debug, true, "", "")
  82. client.SetHttpTransportProxyFunc(self.cpcfg.ProxyFunc)
  83. ts, _ := client.GetClient().Transport.(*http.Transport)
  84. client.SetTransport(cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response) error, error) {
  85. if self.cpcfg.ReadOnly {
  86. if req.Method == "GET" || req.Method == "HEAD" {
  87. return nil, nil
  88. }
  89. // 认证
  90. if req.Method == "POST" && req.URL.Path == "/v3/auth/tokens" {
  91. return nil, nil
  92. }
  93. return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path)
  94. }
  95. return nil, nil
  96. }))
  97. token, err := client.AuthenticateByAccessKey(self.accessKey, self.accessSecret, "cli")
  98. if err != nil {
  99. if errors.Cause(err) == cloudprovider.ErrUnauthorized {
  100. return errors.Wrapf(cloudprovider.ErrInvalidAccessKey, "%v", err)
  101. }
  102. return err
  103. }
  104. serviceRegion, endpoints := "", 0
  105. for _, region := range token.GetRegions() {
  106. if len(token.GetEndpoints(region, "")) > endpoints {
  107. serviceRegion = region
  108. }
  109. }
  110. endpoint := "publicURL"
  111. if strings.Contains(self.authURL, "api/s/identity/v3") {
  112. endpoint = "apigateway"
  113. }
  114. self.s = client.NewSession(context.Background(), serviceRegion, "", endpoint, token)
  115. if !self.s.GetToken().HasSystemAdminPrivilege() {
  116. return fmt.Errorf("no system admin privilege")
  117. }
  118. if self.s.GetProjectId() == self.adminProjectId {
  119. return fmt.Errorf("You can't manage yourself environment")
  120. }
  121. return nil
  122. }
  123. func NewCloudpodsClient(cfg *CloudpodsClientConfig) (*SCloudpodsClient, error) {
  124. cli := &SCloudpodsClient{
  125. CloudpodsClientConfig: cfg,
  126. }
  127. return cli, cli.auth()
  128. }
  129. func (self *SCloudpodsClient) GetRegion(regionId string) (*SRegion, error) {
  130. ret := &SRegion{cli: self}
  131. return ret, self.get(&compute.Cloudregions, regionId, nil, ret)
  132. }
  133. func (self *SCloudpodsClient) get(manager ModelManager, id string, params map[string]string, retVal interface{}) error {
  134. if len(id) == 0 {
  135. return errors.Wrap(cloudprovider.ErrNotFound, "empty id")
  136. }
  137. body := jsonutils.NewDict()
  138. for k, v := range params {
  139. body.Set(k, jsonutils.NewString(v))
  140. }
  141. resp, err := manager.Get(self.s, id, body)
  142. if err != nil {
  143. if strings.Contains(err.Error(), "NotFoundError") {
  144. return errors.Wrapf(cloudprovider.ErrNotFound, "%v", err)
  145. }
  146. return errors.Wrapf(err, "Get(%s)", id)
  147. }
  148. obj := resp.(*jsonutils.JSONDict)
  149. if manager.GetKeyword() == compute.Servers.GetKeyword() {
  150. obj.Remove("cdrom")
  151. }
  152. return obj.Unmarshal(retVal)
  153. }
  154. func (self *SCloudpodsClient) perform(manager ModelManager, id, action string, params interface{}) (jsonutils.JSONObject, error) {
  155. return manager.PerformAction(self.s, id, action, jsonutils.Marshal(params))
  156. }
  157. func (self *SCloudpodsClient) delete(manager ModelManager, id string) error {
  158. if len(id) == 0 {
  159. return nil
  160. }
  161. params := map[string]interface{}{"override_pending_delete": true}
  162. _, err := manager.DeleteWithParam(self.s, id, jsonutils.Marshal(params), nil)
  163. return err
  164. }
  165. func (self *SCloudpodsClient) update(manager ModelManager, id string, params interface{}) error {
  166. _, err := manager.Update(self.s, id, jsonutils.Marshal(params))
  167. return err
  168. }
  169. func (self *SCloudpodsClient) GetAccountId() string {
  170. return self.authURL
  171. }
  172. func (self *SCloudpodsClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
  173. return []cloudprovider.SSubAccount{
  174. {
  175. Name: self.cpcfg.Name,
  176. Account: self.cpcfg.Account,
  177. HealthStatus: api.CLOUD_PROVIDER_HEALTH_NORMAL,
  178. },
  179. }, nil
  180. }
  181. func (self *SCloudpodsClient) GetVersion() string {
  182. version, err := modules.GetVersion(self.s, "compute_v2")
  183. if err != nil {
  184. return ""
  185. }
  186. return version
  187. }
  188. func (self *SCloudpodsClient) create(manager ModelManager, params interface{}, retVal interface{}) error {
  189. resp, err := manager.Create(self.s, jsonutils.Marshal(params))
  190. if err != nil {
  191. return err
  192. }
  193. obj := resp.(*jsonutils.JSONDict)
  194. if manager.GetKeyword() == compute.Servers.GetKeyword() {
  195. obj.Remove("cdrom")
  196. }
  197. return obj.Unmarshal(retVal)
  198. }
  199. func (self *SCloudpodsClient) list(manager ModelManager, params map[string]interface{}, retVal interface{}) error {
  200. if params == nil {
  201. params = map[string]interface{}{}
  202. }
  203. for k, v := range defaultParams {
  204. if _, ok := params[k]; !ok {
  205. params[k] = v
  206. }
  207. }
  208. ret := []jsonutils.JSONObject{}
  209. for {
  210. params["offset"] = len(ret)
  211. part, err := manager.List(self.s, jsonutils.Marshal(params))
  212. if err != nil {
  213. return errors.Wrapf(err, "list")
  214. }
  215. for i := range part.Data {
  216. data := part.Data[i].(*jsonutils.JSONDict)
  217. if manager.GetKeyword() == compute.Servers.GetKeyword() {
  218. data.Remove("cdrom")
  219. }
  220. ret = append(ret, data)
  221. }
  222. if len(ret) >= part.Total {
  223. break
  224. }
  225. }
  226. return jsonutils.Update(retVal, ret)
  227. }
  228. func (self *SCloudpodsClient) GetIRegionById(id string) (cloudprovider.ICloudRegion, error) {
  229. regions, err := self.GetRegions()
  230. if err != nil {
  231. return nil, err
  232. }
  233. for i := range regions {
  234. regions[i].cli = self
  235. if regions[i].GetGlobalId() == id {
  236. return &regions[i], nil
  237. }
  238. }
  239. return nil, errors.Wrapf(cloudprovider.ErrNotFound, "%s", id)
  240. }
  241. func (self *SCloudpodsClient) GetIRegions() ([]cloudprovider.ICloudRegion, error) {
  242. regions, err := self.GetRegions()
  243. if err != nil {
  244. return nil, err
  245. }
  246. ret := []cloudprovider.ICloudRegion{}
  247. for i := range regions {
  248. regions[i].cli = self
  249. ret = append(ret, &regions[i])
  250. }
  251. return ret, nil
  252. }
  253. func (self *SCloudpodsClient) GetRegions() ([]SRegion, error) {
  254. ret := []SRegion{}
  255. return ret, self.list(&compute.Cloudregions, nil, &ret)
  256. }
  257. func (self *SCloudpodsClient) GetCloudRegionExternalIdPrefix() string {
  258. return fmt.Sprintf("%s/%s", api.CLOUD_PROVIDER_CLOUDPODS, self.cpcfg.Id)
  259. }
  260. func (self *SCloudpodsClient) GetCapabilities() []string {
  261. return []string{
  262. cloudprovider.CLOUD_CAPABILITY_PROJECT + cloudprovider.READ_ONLY_SUFFIX,
  263. cloudprovider.CLOUD_CAPABILITY_COMPUTE,
  264. cloudprovider.CLOUD_CAPABILITY_NETWORK,
  265. cloudprovider.CLOUD_CAPABILITY_SECURITY_GROUP,
  266. cloudprovider.CLOUD_CAPABILITY_EIP,
  267. }
  268. }