yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/huawei/loadbalancer.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 huawei
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"net/url"
    21  	"time"
    22  
    23  	"yunion.io/x/jsonutils"
    24  	"yunion.io/x/log"
    25  	"yunion.io/x/pkg/errors"
    26  
    27  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    28  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    29  	"yunion.io/x/cloudmux/pkg/multicloud"
    30  )
    31  
    32  var LB_ALGORITHM_MAP = map[string]string{
    33  	api.LB_SCHEDULER_WRR: "ROUND_ROBIN",
    34  	api.LB_SCHEDULER_WLC: "LEAST_CONNECTIONS",
    35  	api.LB_SCHEDULER_SCH: "SOURCE_IP",
    36  }
    37  
    38  var LB_PROTOCOL_MAP = map[string]string{
    39  	api.LB_LISTENER_TYPE_HTTP:  "HTTP",
    40  	api.LB_LISTENER_TYPE_HTTPS: "TERMINATED_HTTPS",
    41  	api.LB_LISTENER_TYPE_UDP:   "UDP",
    42  	api.LB_LISTENER_TYPE_TCP:   "TCP",
    43  }
    44  
    45  var LBBG_PROTOCOL_MAP = map[string]string{
    46  	api.LB_LISTENER_TYPE_HTTP:  "HTTP",
    47  	api.LB_LISTENER_TYPE_HTTPS: "HTTP",
    48  	api.LB_LISTENER_TYPE_UDP:   "UDP",
    49  	api.LB_LISTENER_TYPE_TCP:   "TCP",
    50  }
    51  
    52  var LB_STICKY_SESSION_MAP = map[string]string{
    53  	api.LB_STICKY_SESSION_TYPE_INSERT: "HTTP_COOKIE",
    54  	api.LB_STICKY_SESSION_TYPE_SERVER: "APP_COOKIE",
    55  }
    56  
    57  var LB_HEALTHCHECK_TYPE_MAP = map[string]string{
    58  	api.LB_HEALTH_CHECK_HTTP: "HTTP",
    59  	api.LB_HEALTH_CHECK_TCP:  "TCP",
    60  	api.LB_HEALTH_CHECK_UDP:  "UDP_CONNECT",
    61  }
    62  
    63  type SLoadbalancer struct {
    64  	multicloud.SResourceBase
    65  	HuaweiTags
    66  	region *SRegion
    67  	subnet *SNetwork
    68  	eip    *SEipAddress
    69  
    70  	Description        string     `json:"description"`
    71  	ProvisioningStatus string     `json:"provisioning_status"`
    72  	TenantID           string     `json:"tenant_id"`
    73  	ProjectID          string     `json:"project_id"`
    74  	AdminStateUp       bool       `json:"admin_state_up"`
    75  	Provider           string     `json:"provider"`
    76  	Pools              []Pool     `json:"pools"`
    77  	Listeners          []Listener `json:"listeners"`
    78  	VipPortID          string     `json:"vip_port_id"`
    79  	OperatingStatus    string     `json:"operating_status"`
    80  	VipAddress         string     `json:"vip_address"`
    81  	VipSubnetID        string     `json:"vip_subnet_id"`
    82  	ID                 string     `json:"id"`
    83  	Name               string     `json:"name"`
    84  	CreatedAt          time.Time  `json:"created_at"`
    85  	UpdatedAt          time.Time  `json:"updated_at"`
    86  }
    87  
    88  type Listener struct {
    89  	ID string `json:"id"`
    90  }
    91  
    92  type Pool struct {
    93  	ID string `json:"id"`
    94  }
    95  
    96  func (self *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) {
    97  	if self.GetEip() == nil {
    98  		return nil, nil
    99  	}
   100  
   101  	return self.eip, nil
   102  }
   103  
   104  func (self *SLoadbalancer) GetId() string {
   105  	return self.ID
   106  }
   107  
   108  func (self *SLoadbalancer) GetName() string {
   109  	return self.Name
   110  }
   111  
   112  func (self *SLoadbalancer) GetGlobalId() string {
   113  	return self.ID
   114  }
   115  
   116  func (self *SLoadbalancer) GetStatus() string {
   117  	return api.LB_STATUS_ENABLED
   118  }
   119  
   120  func (self *SLoadbalancer) Refresh() error {
   121  	lb, err := self.region.GetLoadbalancer(self.GetId())
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	return jsonutils.Update(self, lb)
   127  }
   128  
   129  func (self *SLoadbalancer) IsEmulated() bool {
   130  	return false
   131  }
   132  
   133  func (self *SLoadbalancer) GetProjectId() string {
   134  	return self.ProjectID
   135  }
   136  
   137  func (self *SLoadbalancer) GetAddress() string {
   138  	return self.VipAddress
   139  }
   140  
   141  // todo: api.LB_ADDR_TYPE_INTERNET?
   142  func (self *SLoadbalancer) GetAddressType() string {
   143  	return api.LB_ADDR_TYPE_INTRANET
   144  }
   145  
   146  func (self *SLoadbalancer) GetNetworkType() string {
   147  	return api.LB_NETWORK_TYPE_VPC
   148  }
   149  
   150  func (self *SLoadbalancer) GetNetworkIds() []string {
   151  	net := self.GetNetwork()
   152  	if net != nil {
   153  		return []string{net.GetId()}
   154  	}
   155  
   156  	return []string{}
   157  }
   158  
   159  func (self *SLoadbalancer) GetNetwork() *SNetwork {
   160  	if self.subnet == nil {
   161  		port, err := self.region.GetPort(self.VipPortID)
   162  		if err == nil {
   163  			net, err := self.region.getNetwork(port.NetworkID)
   164  			if err == nil {
   165  				self.subnet = net
   166  			} else {
   167  				log.Debugf("huawei.SLoadbalancer.getNetwork %s", err)
   168  			}
   169  		} else {
   170  			log.Debugf("huawei.SLoadbalancer.GetPort %s", err)
   171  		}
   172  	}
   173  
   174  	return self.subnet
   175  }
   176  
   177  func (self *SLoadbalancer) GetEip() *SEipAddress {
   178  	if self.eip == nil {
   179  		eips, _ := self.region.GetEips(self.VipPortID, nil)
   180  		for i := range eips {
   181  			self.eip = &eips[i]
   182  		}
   183  	}
   184  	return self.eip
   185  }
   186  
   187  func (self *SLoadbalancer) GetVpcId() string {
   188  	net := self.GetNetwork()
   189  	if net != nil {
   190  		return net.VpcID
   191  	}
   192  
   193  	return ""
   194  }
   195  
   196  func (self *SLoadbalancer) GetZoneId() string {
   197  	net := self.GetNetwork()
   198  	if net != nil {
   199  		z, err := self.region.getZoneById(net.AvailabilityZone)
   200  		if err != nil {
   201  			log.Infof("getZoneById %s %s", net.AvailabilityZone, err)
   202  			return ""
   203  		}
   204  
   205  		return z.GetGlobalId()
   206  	}
   207  
   208  	return ""
   209  }
   210  
   211  func (self *SLoadbalancer) GetZone1Id() string {
   212  	return ""
   213  }
   214  
   215  func (self *SLoadbalancer) GetLoadbalancerSpec() string {
   216  	return ""
   217  }
   218  
   219  func (self *SLoadbalancer) GetChargeType() string {
   220  	eip := self.GetEip()
   221  	if eip != nil {
   222  		return eip.GetInternetChargeType()
   223  	}
   224  
   225  	return api.EIP_CHARGE_TYPE_BY_TRAFFIC
   226  }
   227  
   228  func (self *SLoadbalancer) GetEgressMbps() int {
   229  	eip := self.GetEip()
   230  	if eip != nil {
   231  		return eip.GetBandwidth()
   232  	}
   233  
   234  	return 0
   235  }
   236  
   237  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0141008275.html
   238  func (self *SLoadbalancer) Delete(ctx context.Context) error {
   239  	return self.region.DeleteLoadBalancer(self.GetId())
   240  }
   241  
   242  func (self *SLoadbalancer) Start() error {
   243  	return nil
   244  }
   245  
   246  func (self *SLoadbalancer) Stop() error {
   247  	return cloudprovider.ErrNotSupported
   248  }
   249  
   250  func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) {
   251  	ret, err := self.region.GetLoadBalancerListeners(self.GetId())
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  
   256  	iret := make([]cloudprovider.ICloudLoadbalancerListener, 0)
   257  	for i := range ret {
   258  		listener := ret[i]
   259  		listener.lb = self
   260  		iret = append(iret, &listener)
   261  	}
   262  
   263  	return iret, nil
   264  }
   265  
   266  func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   267  	ret, err := self.region.GetLoadBalancerBackendGroups(self.GetId())
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  
   272  	iret := make([]cloudprovider.ICloudLoadbalancerBackendGroup, 0)
   273  	for i := range ret {
   274  		bg := ret[i]
   275  		bg.lb = self
   276  		bg.region = self.region
   277  		iret = append(iret, &bg)
   278  	}
   279  
   280  	return iret, nil
   281  }
   282  
   283  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561549.html
   284  func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   285  	ret, err := self.region.CreateLoadBalancerBackendGroup(group)
   286  	ret.lb = self
   287  	return &ret, err
   288  }
   289  
   290  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561563.html
   291  func (self *SLoadbalancer) CreateHealthCheck(backendGroupId string, healthcheck *cloudprovider.SLoadbalancerHealthCheck) error {
   292  	_, err := self.region.CreateLoadBalancerHealthCheck(backendGroupId, healthcheck)
   293  	return err
   294  }
   295  
   296  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561548.html
   297  func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
   298  	ret := &SElbBackendGroup{lb: self, region: self.region}
   299  	resp, err := self.region.lbGet("elb/pools/" + groupId)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  	return ret, resp.Unmarshal(ret, "pool")
   304  }
   305  
   306  func (self *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) {
   307  	ret, err := self.region.CreateLoadBalancerListener(listener)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  
   312  	ret.lb = self
   313  	return &ret, nil
   314  }
   315  
   316  func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
   317  	ret := &SElbListener{lb: self}
   318  	resp, err := self.region.lbGet("elb/listeners/" + listenerId)
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  	return ret, resp.Unmarshal(ret, "listener")
   323  }
   324  
   325  func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) {
   326  	resp, err := self.lbGet("elb/loadbalancers/" + id)
   327  	if err != nil {
   328  		return nil, err
   329  	}
   330  	ret := &SLoadbalancer{region: self}
   331  	return ret, resp.Unmarshal(ret, "loadbalancer")
   332  }
   333  
   334  func (self *SRegion) DeleteLoadBalancer(elbId string) error {
   335  	resource := fmt.Sprintf("elb/loadbalancers/%s", elbId)
   336  	_, err := self.lbDelete(resource)
   337  	return err
   338  }
   339  
   340  func (self *SRegion) GetLoadBalancerListeners(lbId string) ([]SElbListener, error) {
   341  	ret := []SElbListener{}
   342  	params := url.Values{}
   343  	if len(lbId) > 0 {
   344  		params.Set("loadbalancer_id", lbId)
   345  	}
   346  	return ret, self.lbListAll("elb/listeners", params, "listeners", &ret)
   347  }
   348  
   349  func (self *SRegion) CreateLoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (SElbListener, error) {
   350  	params := map[string]interface{}{
   351  		"name":            listener.Name,
   352  		"description":     listener.Description,
   353  		"protocol":        LB_PROTOCOL_MAP[listener.ListenerType],
   354  		"protocol_port":   listener.ListenerPort,
   355  		"loadbalancer_id": listener.LoadbalancerID,
   356  		"http2_enable":    listener.EnableHTTP2,
   357  	}
   358  	if len(listener.BackendGroupID) > 0 {
   359  		params["default_pool_id"] = listener.BackendGroupID
   360  	}
   361  
   362  	if listener.ListenerType == api.LB_LISTENER_TYPE_HTTPS {
   363  		params["default_tls_container_ref"] = listener.CertificateID
   364  	}
   365  
   366  	if listener.XForwardedFor {
   367  		params["insert_headers"] = map[string]interface{}{
   368  			"X-Forwarded-ELB-IP": listener.XForwardedFor,
   369  		}
   370  	}
   371  
   372  	ret := SElbListener{}
   373  	resp, err := self.lbCreate("lb/listeners", map[string]interface{}{"listener": params})
   374  	if err != nil {
   375  		return ret, err
   376  	}
   377  	return ret, resp.Unmarshal(&ret, "listener")
   378  }
   379  
   380  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html
   381  func (self *SRegion) GetLoadBalancerBackendGroups(elbId string) ([]SElbBackendGroup, error) {
   382  	query := url.Values{}
   383  	if len(elbId) > 0 {
   384  		query.Set("loadbalancer_id", elbId)
   385  	}
   386  
   387  	ret := []SElbBackendGroup{}
   388  	return ret, self.lbListAll("elb/pools", query, "pools", &ret)
   389  }
   390  
   391  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html
   392  func (self *SRegion) CreateLoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (SElbBackendGroup, error) {
   393  	ret := SElbBackendGroup{region: self}
   394  	var protocol, scheduler string
   395  	if s, ok := LB_ALGORITHM_MAP[group.Scheduler]; !ok {
   396  		return ret, fmt.Errorf("CreateILoadBalancerBackendGroup unsupported scheduler %s", group.Scheduler)
   397  	} else {
   398  		scheduler = s
   399  	}
   400  
   401  	if t, ok := LBBG_PROTOCOL_MAP[group.ListenType]; !ok {
   402  		return ret, fmt.Errorf("CreateILoadBalancerBackendGroup unsupported listener type %s", group.ListenType)
   403  	} else {
   404  		protocol = t
   405  	}
   406  
   407  	params := map[string]interface{}{
   408  		"project_id":   self.client.projectId,
   409  		"name":         group.Name,
   410  		"protocol":     protocol,
   411  		"lb_algorithm": scheduler,
   412  	}
   413  
   414  	if len(group.ListenerID) > 0 {
   415  		params["listener_id"] = group.ListenerID
   416  	} else if len(group.LoadbalancerID) > 0 {
   417  		params["loadbalancer_id"] = group.LoadbalancerID
   418  	} else {
   419  		return ret, fmt.Errorf("CreateLoadBalancerBackendGroup one of listener id / loadbalancer id must be specified")
   420  	}
   421  
   422  	if group.StickySession != nil {
   423  		s := map[string]interface{}{}
   424  		timeout := int64(group.StickySession.StickySessionCookieTimeout / 60)
   425  		if group.ListenType == api.LB_LISTENER_TYPE_UDP || group.ListenType == api.LB_LISTENER_TYPE_TCP {
   426  			s["type"] = "SOURCE_IP"
   427  			if timeout > 0 {
   428  				s["persistence_timeout"] = timeout
   429  			}
   430  		} else {
   431  			s["type"] = LB_STICKY_SESSION_MAP[group.StickySession.StickySessionType]
   432  			if len(group.StickySession.StickySessionCookie) > 0 {
   433  				s["cookie_name"] = group.StickySession.StickySessionCookie
   434  			} else {
   435  				if timeout > 0 {
   436  					s["persistence_timeout"] = timeout
   437  				}
   438  			}
   439  		}
   440  		params["session_persistence"] = s
   441  	}
   442  	resp, err := self.lbCreate("elb/pools", map[string]interface{}{"pool": params})
   443  	if err != nil {
   444  		return ret, err
   445  	}
   446  	err = resp.Unmarshal(&ret, "pool")
   447  	if err != nil {
   448  		return ret, err
   449  	}
   450  
   451  	if group.HealthCheck != nil {
   452  		_, err := self.CreateLoadBalancerHealthCheck(ret.GetId(), group.HealthCheck)
   453  		if err != nil {
   454  			return ret, err
   455  		}
   456  	}
   457  
   458  	return ret, nil
   459  }
   460  
   461  func (self *SRegion) CreateLoadBalancerHealthCheck(backendGroupID string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) {
   462  	params := map[string]interface{}{
   463  		"delay":       healthCheck.HealthCheckInterval,
   464  		"max_retries": healthCheck.HealthCheckRise,
   465  		"pool_id":     backendGroupID,
   466  		"timeout":     healthCheck.HealthCheckTimeout,
   467  		"type":        LB_HEALTHCHECK_TYPE_MAP[healthCheck.HealthCheckType],
   468  	}
   469  	if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP {
   470  		if len(healthCheck.HealthCheckDomain) > 0 {
   471  			params["domain_name"] = healthCheck.HealthCheckDomain
   472  		}
   473  
   474  		if len(healthCheck.HealthCheckURI) > 0 {
   475  			params["url_path"] = healthCheck.HealthCheckURI
   476  		}
   477  
   478  		if len(healthCheck.HealthCheckHttpCode) > 0 {
   479  			params["expected_codes"] = ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode)
   480  		}
   481  	}
   482  
   483  	ret := SElbHealthCheck{region: self}
   484  	resp, err := self.lbCreate("elb/healthmonitors", map[string]interface{}{"healthmonitor": params})
   485  	if err != nil {
   486  		return ret, err
   487  	}
   488  
   489  	return ret, resp.Unmarshal(&ret, "healthmonitor")
   490  }
   491  
   492  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561564.html
   493  func (self *SRegion) UpdateLoadBalancerHealthCheck(healthCheckID string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) {
   494  	params := map[string]interface{}{
   495  		"delay":       healthCheck.HealthCheckInterval,
   496  		"max_retries": healthCheck.HealthCheckRise,
   497  		"timeout":     healthCheck.HealthCheckTimeout,
   498  	}
   499  	if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP {
   500  		if len(healthCheck.HealthCheckDomain) > 0 {
   501  			params["domain_name"] = healthCheck.HealthCheckDomain
   502  		}
   503  
   504  		if len(healthCheck.HealthCheckURI) > 0 {
   505  			params["url_path"] = healthCheck.HealthCheckURI
   506  		}
   507  
   508  		if len(healthCheck.HealthCheckHttpCode) > 0 {
   509  			params["expected_codes"] = ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode)
   510  		}
   511  	}
   512  
   513  	ret := SElbHealthCheck{region: self}
   514  	resp, err := self.lbUpdate("elb/healthmonitors/"+healthCheckID, map[string]interface{}{"healthmonitor": params})
   515  	if err != nil {
   516  		return ret, err
   517  	}
   518  	return ret, resp.Unmarshal(&ret, "healthmonitor")
   519  }
   520  
   521  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561565.html
   522  func (self *SRegion) DeleteLoadbalancerHealthCheck(healthCheckID string) error {
   523  	_, err := self.lbDelete("elb/healthmonitors/" + healthCheckID)
   524  	return err
   525  }
   526  
   527  func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error {
   528  	return cloudprovider.ErrNotSupported
   529  }
   530  
   531  func (self *SRegion) lbList(resource string, query url.Values) (jsonutils.JSONObject, error) {
   532  	return self.client.lbList(self.ID, resource, query)
   533  }
   534  
   535  func (self *SRegion) vpcList(resource string, query url.Values) (jsonutils.JSONObject, error) {
   536  	return self.client.vpcList(self.ID, resource, query)
   537  }
   538  
   539  func (self *SRegion) vpcCreate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
   540  	return self.client.vpcCreate(self.ID, resource, params)
   541  }
   542  
   543  func (self *SRegion) vpcGet(resource string) (jsonutils.JSONObject, error) {
   544  	return self.client.vpcGet(self.ID, resource)
   545  }
   546  
   547  func (self *SRegion) vpcDelete(resource string) (jsonutils.JSONObject, error) {
   548  	return self.client.vpcDelete(self.ID, resource)
   549  }
   550  
   551  func (self *SRegion) vpcUpdate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
   552  	return self.client.vpcUpdate(self.ID, resource, params)
   553  }
   554  
   555  func (self *SRegion) lbListAll(resource string, query url.Values, respKey string, retVal interface{}) error {
   556  	ret := jsonutils.NewArray()
   557  	for {
   558  		resp, err := self.lbList(resource, query)
   559  		if err != nil {
   560  			return err
   561  		}
   562  		arr, err := resp.GetArray(respKey)
   563  		if err != nil {
   564  			return errors.Wrapf(err, "get %s", respKey)
   565  		}
   566  		ret.Add(arr...)
   567  		marker, _ := resp.GetString("page_info", "next_marker")
   568  		if len(marker) == 0 {
   569  			break
   570  		}
   571  		query.Set("marker", marker)
   572  	}
   573  	return ret.Unmarshal(retVal)
   574  }
   575  
   576  func (self *SRegion) lbGet(resource string) (jsonutils.JSONObject, error) {
   577  	return self.client.lbGet(self.ID, resource)
   578  }
   579  
   580  func (self *SRegion) lbDelete(resource string) (jsonutils.JSONObject, error) {
   581  	return self.client.lbDelete(self.ID, resource)
   582  }
   583  
   584  func (self *SRegion) lbCreate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
   585  	return self.client.lbCreate(self.ID, resource, params)
   586  }
   587  
   588  func (self *SRegion) lbUpdate(resource string, params map[string]interface{}) (jsonutils.JSONObject, error) {
   589  	return self.client.lbUpdate(self.ID, resource, params)
   590  }
   591  
   592  // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561535.html
   593  func (self *SRegion) CreateLoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (*SLoadbalancer, error) {
   594  	subnet, err := self.getNetwork(loadbalancer.NetworkIDs[0])
   595  	if err != nil {
   596  		return nil, errors.Wrap(err, "SRegion.CreateLoadBalancer.getNetwork")
   597  	}
   598  
   599  	params := map[string]interface{}{
   600  		"name":          loadbalancer.Name,
   601  		"vip_subnet_id": subnet.NeutronSubnetID,
   602  		"tenant_id":     self.client.projectId,
   603  	}
   604  	if len(loadbalancer.Address) > 0 {
   605  		params["vip_address"] = loadbalancer.Address
   606  	}
   607  	resp, err := self.lbCreate("elb/loadbalancers", map[string]interface{}{"loadbalancer": params})
   608  	if err != nil {
   609  		return nil, err
   610  	}
   611  	ret := &SLoadbalancer{region: self}
   612  	err = resp.Unmarshal(ret, "loadbalancer")
   613  	if err != nil {
   614  		return nil, errors.Wrapf(err, "resp.Unmarshal")
   615  	}
   616  
   617  	// 创建公网类型ELB
   618  	if len(loadbalancer.EipID) > 0 {
   619  		err := self.AssociateEipWithPortId(loadbalancer.EipID, ret.VipPortID)
   620  		if err != nil {
   621  			return ret, errors.Wrap(err, "SRegion.CreateLoadBalancer.AssociateEipWithPortId")
   622  		}
   623  	}
   624  	return ret, nil
   625  }