yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/incloudsphere/sphere.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 incloudsphere 16 17 import ( 18 "context" 19 "fmt" 20 "net/http" 21 "net/url" 22 "strings" 23 24 "yunion.io/x/jsonutils" 25 "yunion.io/x/log" 26 "yunion.io/x/pkg/errors" 27 28 api "yunion.io/x/cloudmux/pkg/apis/compute" 29 "yunion.io/x/cloudmux/pkg/cloudprovider" 30 "yunion.io/x/onecloud/pkg/util/httputils" 31 ) 32 33 const ( 34 CLOUD_PROVIDER_INCLOUD_SPHERE = api.CLOUD_PROVIDER_INCLOUD_SPHERE 35 AUTH_ADDR = "system/user/login" 36 ) 37 38 type SphereClient struct { 39 *SphereClientConfig 40 } 41 42 type SphereClientConfig struct { 43 cpcfg cloudprovider.ProviderConfig 44 accessKey string 45 accessSecret string 46 host string 47 authURL string 48 49 sessionId string 50 51 debug bool 52 } 53 54 func NewSphereClientConfig(host, accessKey, accessSecret string) *SphereClientConfig { 55 return &SphereClientConfig{ 56 host: host, 57 authURL: fmt.Sprintf("https://%s", host), 58 accessKey: accessKey, 59 accessSecret: accessSecret, 60 } 61 } 62 63 func (self *SphereClientConfig) Debug(debug bool) *SphereClientConfig { 64 self.debug = debug 65 return self 66 } 67 68 func (self *SphereClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderConfig) *SphereClientConfig { 69 self.cpcfg = cpcfg 70 return self 71 } 72 73 func NewSphereClient(cfg *SphereClientConfig) (*SphereClient, error) { 74 client := &SphereClient{ 75 SphereClientConfig: cfg, 76 } 77 return client, client.auth() 78 } 79 80 func (self *SphereClient) auth() error { 81 params := map[string]interface{}{ 82 "username": self.accessKey, 83 "password": self.accessSecret, 84 "domain": "internal", 85 "locale": "cn", 86 } 87 ret, err := self.__jsonRequest(httputils.POST, AUTH_ADDR, params) 88 if err != nil { 89 return errors.Wrapf(err, "post") 90 } 91 if ret.Contains("sessonId") { 92 self.sessionId, err = ret.GetString("sessonId") 93 if err != nil { 94 return errors.Wrapf(err, "get sessionId") 95 } 96 return nil 97 } 98 return fmt.Errorf(ret.String()) 99 } 100 101 func (self *SphereClient) GetRegion() (*SRegion, error) { 102 region := &SRegion{client: self} 103 return region, nil 104 } 105 106 func (self *SphereClient) GetRegions() ([]SRegion, error) { 107 ret := []SRegion{} 108 ret = append(ret, SRegion{client: self}) 109 return ret, nil 110 } 111 112 type SphereError struct { 113 Message string 114 Code int 115 Params []string 116 } 117 118 func (self SphereError) Error() string { 119 return fmt.Sprintf("[%d] %s with params %s", self.Code, self.Message, self.Params) 120 } 121 122 func (ce *SphereError) ParseErrorFromJsonResponse(statusCode int, body jsonutils.JSONObject) error { 123 if body != nil { 124 body.Unmarshal(ce) 125 log.Errorf("error: %v", body.PrettyString()) 126 } 127 if ce.Code == 0 && statusCode > 0 { 128 ce.Code = statusCode 129 } 130 if ce.Code == 404 || ce.Code == 20027 { 131 return errors.Wrap(cloudprovider.ErrNotFound, ce.Error()) 132 } 133 return ce 134 } 135 136 func (cli *SphereClient) getDefaultClient() *http.Client { 137 client := httputils.GetAdaptiveTimeoutClient() 138 httputils.SetClientProxyFunc(client, cli.cpcfg.ProxyFunc) 139 ts, _ := client.Transport.(*http.Transport) 140 client.Transport = cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response), error) { 141 if cli.cpcfg.ReadOnly { 142 if req.Method == "GET" || req.Method == "HEAD" { 143 return nil, nil 144 } 145 // 认证 146 if req.Method == "POST" && (strings.HasSuffix(req.URL.Path, "/authentication") || strings.HasSuffix(req.URL.Path, "/system/user/login")) { 147 return nil, nil 148 } 149 return nil, errors.Wrapf(cloudprovider.ErrAccountReadOnly, "%s %s", req.Method, req.URL.Path) 150 } 151 return nil, nil 152 }) 153 return client 154 } 155 156 func (cli *SphereClient) post(res string, params interface{}) (jsonutils.JSONObject, error) { 157 return cli._jsonRequest(httputils.POST, res, params) 158 } 159 160 func (cli *SphereClient) get(res string, params url.Values, retVal interface{}) error { 161 if params != nil { 162 res = fmt.Sprintf("%s?%s", res, params.Encode()) 163 } 164 resp, err := cli._jsonRequest(httputils.GET, res, nil) 165 if err != nil { 166 return err 167 } 168 return resp.Unmarshal(retVal) 169 } 170 171 func (cli *SphereClient) put(res string, params url.Values, body jsonutils.JSONObject, retVal interface{}) error { 172 if params != nil { 173 res = fmt.Sprintf("%s?%s", res, params.Encode()) 174 } 175 resp, err := cli._jsonRequest(httputils.PUT, res, body) 176 if err != nil { 177 return err 178 } 179 if !resp.Contains("taskId") { 180 return resp.Unmarshal(retVal) 181 } 182 taskId, _ := resp.GetString("taskId") 183 _, err = cli.waitTask(taskId) 184 return err 185 } 186 187 func (cli *SphereClient) del(res string, params url.Values, retVal interface{}) error { 188 if params != nil { 189 res = fmt.Sprintf("%s?%s", res, params.Encode()) 190 } 191 resp, err := cli._jsonRequest(httputils.DELETE, res, nil) 192 if err != nil { 193 return err 194 } 195 if !resp.Contains("taskId") { 196 return resp.Unmarshal(retVal) 197 } 198 taskId, _ := resp.GetString("taskId") 199 _, err = cli.waitTask(taskId) 200 return err 201 } 202 203 func (cli *SphereClient) list(res string, params url.Values, retVal interface{}) error { 204 if params == nil { 205 params = url.Values{} 206 } 207 page, items := 1, jsonutils.NewArray() 208 params.Set("pageSize", "100") 209 for { 210 params.Set("currentPage", fmt.Sprintf("%d", page)) 211 resp, err := cli._list(res, params) 212 if err != nil { 213 return errors.Wrapf(err, "list(%s)", res) 214 } 215 totalSize, _ := resp.Int("totalSize") 216 array := []jsonutils.JSONObject{} 217 if resp.Contains("items") { 218 array, err = resp.GetArray("items") 219 if err != nil { 220 return errors.Wrapf(err, "get items") 221 } 222 items.Add(array...) 223 } 224 if totalSize <= int64(items.Length()) || len(array) == 0 { 225 break 226 } 227 page++ 228 } 229 return items.Unmarshal(retVal) 230 } 231 232 func (cli *SphereClient) _list(res string, params url.Values) (jsonutils.JSONObject, error) { 233 if params != nil { 234 res = fmt.Sprintf("%s?%s", res, params.Encode()) 235 } 236 return cli._jsonRequest(httputils.GET, res, nil) 237 } 238 239 func (cli *SphereClient) _jsonRequest(method httputils.THttpMethod, res string, params interface{}) (jsonutils.JSONObject, error) { 240 ret, err := cli.__jsonRequest(method, res, params) 241 if err != nil { 242 if e, ok := err.(*SphereError); ok && e.Code == 107001 { 243 cli.auth() 244 return cli.__jsonRequest(method, res, params) 245 } 246 return ret, err 247 } 248 return ret, nil 249 } 250 251 func (cli *SphereClient) __jsonRequest(method httputils.THttpMethod, res string, params interface{}) (jsonutils.JSONObject, error) { 252 client := httputils.NewJsonClient(cli.getDefaultClient()) 253 url := fmt.Sprintf("%s/%s", cli.authURL, strings.TrimPrefix(res, "/")) 254 req := httputils.NewJsonRequest(method, url, params) 255 header := http.Header{} 256 if len(cli.sessionId) > 0 && res != AUTH_ADDR { 257 header.Set("Authorization", cli.sessionId) 258 } 259 header.Set("Version", "5.8") 260 req.SetHeader(header) 261 oe := &SphereError{} 262 _, resp, err := client.Send(context.Background(), req, oe, cli.debug) 263 if err != nil { 264 return nil, err 265 } 266 if resp.Contains("code") && resp.Contains("message") { 267 return nil, oe.ParseErrorFromJsonResponse(0, resp) 268 } 269 return resp, nil 270 } 271 272 func (self *SphereClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) { 273 subAccount := cloudprovider.SSubAccount{} 274 subAccount.Name = self.cpcfg.Name 275 subAccount.Account = self.accessKey 276 subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL 277 return []cloudprovider.SSubAccount{subAccount}, nil 278 } 279 280 func (self *SphereClient) GetAccountId() string { 281 return self.host 282 } 283 284 func (self *SphereClient) GetIRegions() []cloudprovider.ICloudRegion { 285 ret := []cloudprovider.ICloudRegion{} 286 region, _ := self.GetRegion() 287 ret = append(ret, region) 288 return ret 289 } 290 291 func (self *SphereClient) GetCapabilities() []string { 292 ret := []string{ 293 cloudprovider.CLOUD_CAPABILITY_COMPUTE, 294 cloudprovider.CLOUD_CAPABILITY_NETWORK + cloudprovider.READ_ONLY_SUFFIX, 295 } 296 return ret 297 }