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