// Copyright 2019 Yunion // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package hcso import ( "context" "strings" "time" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" api "yunion.io/x/cloudmux/pkg/apis/compute" "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/cloudmux/pkg/multicloud" "yunion.io/x/cloudmux/pkg/multicloud/huawei" ) var LB_ALGORITHM_MAP = map[string]string{ api.LB_SCHEDULER_WRR: "ROUND_ROBIN", api.LB_SCHEDULER_WLC: "LEAST_CONNECTIONS", api.LB_SCHEDULER_SCH: "SOURCE_IP", } var LBBG_PROTOCOL_MAP = map[string]string{ api.LB_LISTENER_TYPE_HTTP: "HTTP", api.LB_LISTENER_TYPE_HTTPS: "HTTP", api.LB_LISTENER_TYPE_UDP: "UDP", api.LB_LISTENER_TYPE_TCP: "TCP", } var LB_STICKY_SESSION_MAP = map[string]string{ api.LB_STICKY_SESSION_TYPE_INSERT: "HTTP_COOKIE", api.LB_STICKY_SESSION_TYPE_SERVER: "APP_COOKIE", } var LB_HEALTHCHECK_TYPE_MAP = map[string]string{ api.LB_HEALTH_CHECK_HTTP: "HTTP", api.LB_HEALTH_CHECK_TCP: "TCP", api.LB_HEALTH_CHECK_UDP: "UDP_CONNECT", } type SLoadbalancer struct { multicloud.SLoadbalancerBase huawei.HuaweiTags region *SRegion subnet *SNetwork eip *SEipAddress Description string `json:"description"` ProvisioningStatus string `json:"provisioning_status"` TenantID string `json:"tenant_id"` ProjectID string `json:"project_id"` AdminStateUp bool `json:"admin_state_up"` Provider string `json:"provider"` Pools []Pool `json:"pools"` Listeners []Listener `json:"listeners"` VipPortID string `json:"vip_port_id"` OperatingStatus string `json:"operating_status"` VipAddress string `json:"vip_address"` VipSubnetID string `json:"vip_subnet_id"` ID string `json:"id"` Name string `json:"name"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type Listener struct { ID string `json:"id"` } type Pool struct { ID string `json:"id"` } func (self *SLoadbalancer) GetIEIPs() ([]cloudprovider.ICloudEIP, error) { if self.GetEip() == nil { return nil, nil } return []cloudprovider.ICloudEIP{self.eip}, nil } func (self *SLoadbalancer) GetId() string { return self.ID } func (self *SLoadbalancer) GetName() string { return self.Name } func (self *SLoadbalancer) GetGlobalId() string { return self.ID } func (self *SLoadbalancer) GetStatus() string { return api.LB_STATUS_ENABLED } func (self *SLoadbalancer) Refresh() error { lb, err := self.region.GetLoadbalancer(self.GetId()) if err != nil { return err } return jsonutils.Update(self, lb) } func (self *SLoadbalancer) IsEmulated() bool { return false } func (self *SLoadbalancer) GetProjectId() string { return self.ProjectID } func (self *SLoadbalancer) GetAddress() string { return self.VipAddress } // todo: api.LB_ADDR_TYPE_INTERNET? func (self *SLoadbalancer) GetAddressType() string { return api.LB_ADDR_TYPE_INTRANET } func (self *SLoadbalancer) GetNetworkType() string { return api.LB_NETWORK_TYPE_VPC } func (self *SLoadbalancer) GetNetworkIds() []string { net := self.GetNetwork() if net != nil { return []string{net.GetId()} } return []string{} } func (self *SLoadbalancer) GetNetwork() *SNetwork { if self.subnet == nil { port, err := self.region.GetPort(self.VipPortID) if err == nil { net, err := self.region.getNetwork(port.NetworkID) if err == nil { self.subnet = net } else { log.Debugf("huawei.SLoadbalancer.getNetwork %s", err) } } else { log.Debugf("huawei.SLoadbalancer.GetPort %s", err) } } return self.subnet } func (self *SLoadbalancer) GetEip() *SEipAddress { if self.eip == nil { eips, _ := self.region.GetEips() for i := range eips { eip := &eips[i] if eip.PortId == self.VipPortID { self.eip = eip } } } return self.eip } func (self *SLoadbalancer) GetVpcId() string { net := self.GetNetwork() if net != nil { return net.VpcID } return "" } func (self *SLoadbalancer) GetZoneId() string { net := self.GetNetwork() if net != nil { z, err := self.region.getZoneById(net.AvailabilityZone) if err != nil { log.Infof("getZoneById %s %s", net.AvailabilityZone, err) return "" } return z.GetGlobalId() } return "" } func (self *SLoadbalancer) GetZone1Id() string { return "" } func (self *SLoadbalancer) GetLoadbalancerSpec() string { return "" } func (self *SLoadbalancer) GetChargeType() string { eip := self.GetEip() if eip != nil { return eip.GetInternetChargeType() } return api.EIP_CHARGE_TYPE_BY_TRAFFIC } func (self *SLoadbalancer) GetEgressMbps() int { eip := self.GetEip() if eip != nil { return eip.GetBandwidth() } return 0 } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0141008275.html func (self *SLoadbalancer) Delete(ctx context.Context) error { for _, res := range self.Pools { backends, err := self.region.getLoadBalancerBackends(res.ID) if err != nil { return errors.Wrapf(err, "get backend group %s backends", res.ID) } for _, backend := range backends { err := self.region.RemoveLoadBalancerBackend(res.ID, backend.ID) if err != nil { return errors.Wrapf(err, "RemoveLoadBalancerBackend") } } pool, err := self.region.GetLoadBalancerBackendGroup(res.ID) if err != nil { return errors.Wrapf(err, "GetLoadBalancerBackendGroup") } if len(pool.HealthMonitorID) > 0 { err = self.region.DeleteLoadbalancerHealthCheck(pool.HealthMonitorID) if err != nil { return errors.Wrapf(err, "delete health check") } } err = self.region.DeleteLoadBalancerBackendGroup(res.ID) if err != nil { return errors.Wrapf(err, "delete backend group %s", res.ID) } } for _, lis := range self.Listeners { err := self.region.DeleteElbListener(lis.ID) if err != nil { return errors.Wrapf(err, "delete listener %s", lis.ID) } } return self.region.DeleteLoadBalancer(self.GetId()) } func (self *SLoadbalancer) Start() error { return nil } func (self *SLoadbalancer) Stop() error { return cloudprovider.ErrNotSupported } func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { ret, err := self.region.GetLoadBalancerListeners(self.GetId()) if err != nil { return nil, err } iret := make([]cloudprovider.ICloudLoadbalancerListener, 0) for i := range ret { listener := ret[i] listener.lb = self iret = append(iret, &listener) } return iret, nil } func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { ret, err := self.region.GetLoadBalancerBackendGroups(self.GetId()) if err != nil { return nil, err } iret := make([]cloudprovider.ICloudLoadbalancerBackendGroup, 0) for i := range ret { bg := ret[i] bg.lb = self iret = append(iret, &bg) } return iret, nil } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561549.html func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(opts *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { ret, err := self.region.CreateLoadBalancerBackendGroup(self.ID, opts) if err != nil { return nil, errors.Wrapf(err, "CreateLoadBalancerBackendGroup") } ret.lb = self return ret, err } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561563.html func (self *SLoadbalancer) CreateHealthCheck(backendGroupId string, healthcheck *cloudprovider.SLoadbalancerHealthCheck) error { _, err := self.region.CreateLoadBalancerHealthCheck(backendGroupId, healthcheck) return err } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561548.html func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { ret := &SElbBackendGroup{} err := DoGet(self.region.ecsClient.ElbBackendGroup.Get, groupId, nil, ret) if err != nil { return nil, err } ret.lb = self ret.region = self.region return ret, nil } func (self *SLoadbalancer) CreateILoadBalancerListener(ctx context.Context, listener *cloudprovider.SLoadbalancerListenerCreateOptions) (cloudprovider.ICloudLoadbalancerListener, error) { ret, err := self.region.CreateLoadBalancerListener(self.ID, listener) if err != nil { return nil, err } ret.lb = self return ret, nil } func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) { ret := &SElbListener{} err := DoGet(self.region.ecsClient.ElbListeners.Get, listenerId, nil, ret) if err != nil { return nil, err } ret.lb = self return ret, nil } func (self *SRegion) GetLoadbalancer(lgId string) (SLoadbalancer, error) { elb := SLoadbalancer{} err := DoGet(self.ecsClient.Elb.Get, lgId, nil, &elb) if err != nil { return elb, err } return elb, nil } func (self *SRegion) DeleteLoadBalancer(elbId string) error { return DoDelete(self.ecsClient.Elb.Delete, elbId, nil, nil) } func (self *SRegion) GetLoadBalancerListeners(lbId string) ([]SElbListener, error) { params := map[string]string{} if len(lbId) > 0 { params["loadbalancer_id"] = lbId } ret := make([]SElbListener, 0) err := doListAll(self.ecsClient.ElbListeners.List, params, &ret) if err != nil { return nil, err } return ret, nil } func (self *SRegion) CreateLoadBalancerListener(lbId string, opts *cloudprovider.SLoadbalancerListenerCreateOptions) (*SElbListener, error) { params := jsonutils.NewDict() listenerObj := jsonutils.NewDict() listenerObj.Set("name", jsonutils.NewString(opts.Name)) listenerObj.Set("description", jsonutils.NewString(opts.Description)) switch opts.ListenerType { case api.LB_LISTENER_TYPE_TCP, api.LB_LISTENER_TYPE_UDP, api.LB_LISTENER_TYPE_HTTP: opts.ListenerType = strings.ToUpper(opts.ListenerType) case api.LB_LISTENER_TYPE_HTTPS: opts.ListenerType = "TERMINATED_HTTPS" default: return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "protocol %s", opts.ListenerType) } listenerObj.Set("protocol", jsonutils.NewString(opts.ListenerType)) listenerObj.Set("protocol_port", jsonutils.NewInt(int64(opts.ListenerPort))) listenerObj.Set("loadbalancer_id", jsonutils.NewString(lbId)) listenerObj.Set("http2_enable", jsonutils.NewBool(opts.EnableHTTP2)) if len(opts.BackendGroupId) > 0 { listenerObj.Set("default_pool_id", jsonutils.NewString(opts.BackendGroupId)) } if opts.ListenerType == api.LB_LISTENER_TYPE_HTTPS { listenerObj.Set("default_tls_container_ref", jsonutils.NewString(opts.CertificateId)) } if opts.XForwardedFor { insertObj := jsonutils.NewDict() insertObj.Set("X-Forwarded-ELB-IP", jsonutils.NewBool(opts.XForwardedFor)) listenerObj.Set("insert_headers", insertObj) } params.Set("listener", listenerObj) ret := &SElbListener{} err := DoCreate(self.ecsClient.ElbListeners.Create, params, ret) if err != nil { return nil, err } return ret, nil } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html func (self *SRegion) GetLoadBalancerBackendGroups(elbId string) ([]SElbBackendGroup, error) { params := map[string]string{} if len(elbId) > 0 { params["loadbalancer_id"] = elbId } ret := make([]SElbBackendGroup, 0) err := doListAll(self.ecsClient.ElbBackendGroup.List, params, &ret) if err != nil { return nil, err } for i := range ret { ret[i].region = self } return ret, nil } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561547.html func (self *SRegion) CreateLoadBalancerBackendGroup(lbId string, opts *cloudprovider.SLoadbalancerBackendGroup) (*SElbBackendGroup, error) { params := jsonutils.NewDict() poolObj := jsonutils.NewDict() switch opts.Scheduler { case api.LB_SCHEDULER_WRR: opts.Scheduler = "ROUND_ROBIN" case api.LB_SCHEDULER_WLC: opts.Scheduler = "LEAST_CONNECTIONS" case api.LB_SCHEDULER_SCH: opts.Scheduler = "SOURCE_IP" default: return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "invalid scheduler %s", opts.Scheduler) } switch opts.Protocol { case api.LB_LISTENER_TYPE_TCP, api.LB_LISTENER_TYPE_UDP: opts.Protocol = strings.ToUpper(opts.Protocol) case api.LB_LISTENER_TYPE_HTTP, api.LB_LISTENER_TYPE_HTTPS: opts.Protocol = "HTTP" default: return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "invalid protocol %s", opts.Protocol) } poolObj.Set("project_id", jsonutils.NewString(self.client.projectId)) poolObj.Set("name", jsonutils.NewString(opts.Name)) poolObj.Set("protocol", jsonutils.NewString(opts.Protocol)) poolObj.Set("lb_algorithm", jsonutils.NewString(opts.Scheduler)) poolObj.Set("loadbalancer_id", jsonutils.NewString(lbId)) params.Set("pool", poolObj) ret := &SElbBackendGroup{region: self} err := DoCreate(self.ecsClient.ElbBackendGroup.Create, params, ret) if err != nil { return nil, err } return ret, nil } func (self *SRegion) CreateLoadBalancerHealthCheck(backendGroupID string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) { params := jsonutils.NewDict() healthObj := jsonutils.NewDict() healthObj.Set("delay", jsonutils.NewInt(int64(healthCheck.HealthCheckInterval))) healthObj.Set("max_retries", jsonutils.NewInt(int64(healthCheck.HealthCheckRise))) healthObj.Set("pool_id", jsonutils.NewString(backendGroupID)) healthObj.Set("timeout", jsonutils.NewInt(int64(healthCheck.HealthCheckTimeout))) healthObj.Set("type", jsonutils.NewString(LB_HEALTHCHECK_TYPE_MAP[healthCheck.HealthCheckType])) if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP { if len(healthCheck.HealthCheckDomain) > 0 { healthObj.Set("domain_name", jsonutils.NewString(healthCheck.HealthCheckDomain)) } if len(healthCheck.HealthCheckURI) > 0 { healthObj.Set("url_path", jsonutils.NewString(healthCheck.HealthCheckURI)) } if len(healthCheck.HealthCheckHttpCode) > 0 { healthObj.Set("expected_codes", jsonutils.NewString(ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode))) } } params.Set("healthmonitor", healthObj) ret := SElbHealthCheck{} err := DoCreate(self.ecsClient.ElbHealthCheck.Create, params, &ret) if err != nil { return ret, err } ret.region = self return ret, nil } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561564.html func (self *SRegion) UpdateLoadBalancerHealthCheck(healthCheckID string, healthCheck *cloudprovider.SLoadbalancerHealthCheck) (SElbHealthCheck, error) { params := jsonutils.NewDict() healthObj := jsonutils.NewDict() healthObj.Set("delay", jsonutils.NewInt(int64(healthCheck.HealthCheckInterval))) healthObj.Set("max_retries", jsonutils.NewInt(int64(healthCheck.HealthCheckRise))) healthObj.Set("timeout", jsonutils.NewInt(int64(healthCheck.HealthCheckTimeout))) if healthCheck.HealthCheckType == api.LB_HEALTH_CHECK_HTTP { if len(healthCheck.HealthCheckDomain) > 0 { healthObj.Set("domain_name", jsonutils.NewString(healthCheck.HealthCheckDomain)) } if len(healthCheck.HealthCheckURI) > 0 { healthObj.Set("url_path", jsonutils.NewString(healthCheck.HealthCheckURI)) } if len(healthCheck.HealthCheckHttpCode) > 0 { healthObj.Set("expected_codes", jsonutils.NewString(ToHuaweiHealthCheckHttpCode(healthCheck.HealthCheckHttpCode))) } } params.Set("healthmonitor", healthObj) ret := SElbHealthCheck{} err := DoUpdate(self.ecsClient.ElbHealthCheck.Update, healthCheckID, params, &ret) if err != nil { return ret, err } ret.region = self return ret, nil } // https://support.huaweicloud.com/api-elb/zh-cn_topic_0096561565.html func (self *SRegion) DeleteLoadbalancerHealthCheck(healthCheckID string) error { return DoDelete(self.ecsClient.ElbHealthCheck.Delete, healthCheckID, nil, nil) } func (self *SLoadbalancer) SetTags(tags map[string]string, replace bool) error { return cloudprovider.ErrNotSupported }