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  }