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 &regions[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, &regions[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  }