From d4f7a60093ae8cc32feaf4da5a6b922af3ffcc95 Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 31 Jan 2019 18:07:12 +0800 Subject: [PATCH 01/15] qcloud loadbalance sync --- pkg/compute/models/loadbalancer_const.go | 6 + .../models/loadbalancerlistenerrules.go | 1 + pkg/compute/models/loadbalancerlisteners.go | 33 +- pkg/compute/models/loadbalancers.go | 3 +- pkg/hostman/guestfs/fsdriver/windows.go | 18 +- pkg/util/qcloud/certificate.go | 81 ++++ pkg/util/qcloud/loadbalancer.go | 253 +++++++++++ pkg/util/qcloud/loadbalancer_acl.go | 40 ++ pkg/util/qcloud/loadbalancer_backend.go | 163 +++++++ pkg/util/qcloud/loadbalancer_backendgroup.go | 113 +++++ pkg/util/qcloud/loadbalancer_certificate.go | 77 ++++ pkg/util/qcloud/loadbalancer_listener.go | 405 ++++++++++++++++++ pkg/util/qcloud/loadbalancer_listenerrule.go | 82 ++++ pkg/util/qcloud/qcloud.go | 152 ++++++- pkg/util/qcloud/region.go | 9 + pkg/util/winutils/winutils.go | 20 +- 16 files changed, 1407 insertions(+), 49 deletions(-) create mode 100755 pkg/util/qcloud/certificate.go create mode 100755 pkg/util/qcloud/loadbalancer.go create mode 100755 pkg/util/qcloud/loadbalancer_acl.go create mode 100755 pkg/util/qcloud/loadbalancer_backend.go create mode 100755 pkg/util/qcloud/loadbalancer_backendgroup.go create mode 100755 pkg/util/qcloud/loadbalancer_certificate.go create mode 100755 pkg/util/qcloud/loadbalancer_listener.go create mode 100755 pkg/util/qcloud/loadbalancer_listenerrule.go diff --git a/pkg/compute/models/loadbalancer_const.go b/pkg/compute/models/loadbalancer_const.go index f6c7b597ba..5f75879e0d 100644 --- a/pkg/compute/models/loadbalancer_const.go +++ b/pkg/compute/models/loadbalancer_const.go @@ -159,6 +159,11 @@ var LB_STICKY_SESSION_TYPES = validators.NewChoices( ) // TODO maybe https check when field need comes ;) +const ( + LB_HEALTH_CHECK_ENABLE = "on" + LB_HEALTH_CHECK_DISABLE = "off" +) + const ( LB_HEALTH_CHECK_TCP = "tcp" LB_HEALTH_CHECK_UDP = "udp" @@ -181,6 +186,7 @@ var LB_HEALTH_CHECK_TYPES_UDP = validators.NewChoices( ) const ( + LB_HEALTH_CHECK_HTTP_CODE_1xx = "http_1xx" LB_HEALTH_CHECK_HTTP_CODE_2xx = "http_2xx" LB_HEALTH_CHECK_HTTP_CODE_3xx = "http_3xx" LB_HEALTH_CHECK_HTTP_CODE_4xx = "http_4xx" diff --git a/pkg/compute/models/loadbalancerlistenerrules.go b/pkg/compute/models/loadbalancerlistenerrules.go index 0dae971fac..2c3cb6ef66 100644 --- a/pkg/compute/models/loadbalancerlistenerrules.go +++ b/pkg/compute/models/loadbalancerlistenerrules.go @@ -45,6 +45,7 @@ type SLoadbalancerListenerRule struct { Domain string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"optional"` Path string `width:"128" charset:"ascii" nullable:"false" list:"user" create:"optional"` + SLoadbalancerHealthCheck // 目前只有腾讯云HTTP、HTTPS类型的健康检查是和规则绑定的。 SLoadbalancerHTTPRateLimiter } diff --git a/pkg/compute/models/loadbalancerlisteners.go b/pkg/compute/models/loadbalancerlisteners.go index 6dc26364f8..0d3e42168f 100644 --- a/pkg/compute/models/loadbalancerlisteners.go +++ b/pkg/compute/models/loadbalancerlisteners.go @@ -42,6 +42,23 @@ type SLoadbalancerHTTPRateLimiter struct { HTTPRequestRatePerSrc int `nullable:"false" list:"user" create:"optional" update:"user"` } +type SLoadbalancerHealthCheck struct { + HealthCheck string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` + HealthCheckType string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` + + HealthCheckDomain string `charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` + HealthCheckURI string `charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` + HealthCheckHttpCode string `charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` + + HealthCheckRise int `nullable:"false" list:"user" create:"optional" update:"user"` + HealthCheckFall int `nullable:"false" list:"user" create:"optional" update:"user"` + HealthCheckTimeout int `nullable:"false" list:"user" create:"optional" update:"user"` + HealthCheckInterval int `nullable:"false" list:"user" create:"optional" update:"user"` + + HealthCheckReq string `list:"user" create:"optional" update:"user"` + HealthCheckExp string `list:"user" create:"optional" update:"user"` +} + type SLoadbalancerTCPListener struct{} type SLoadbalancerUDPListener struct{} @@ -90,26 +107,12 @@ type SLoadbalancerListener struct { AclType string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` AclId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` - HealthCheck string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` - HealthCheckType string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` - - HealthCheckDomain string `charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` - HealthCheckURI string `charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` - HealthCheckHttpCode string `charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user"` - - HealthCheckRise int `nullable:"false" list:"user" create:"optional" update:"user"` - HealthCheckFall int `nullable:"false" list:"user" create:"optional" update:"user"` - HealthCheckTimeout int `nullable:"false" list:"user" create:"optional" update:"user"` - HealthCheckInterval int `nullable:"false" list:"user" create:"optional" update:"user"` - - HealthCheckReq string `list:"user" create:"optional" update:"user"` - HealthCheckExp string `list:"user" create:"optional" update:"user"` - SLoadbalancerTCPListener SLoadbalancerUDPListener SLoadbalancerHTTPListener SLoadbalancerHTTPSListener + SLoadbalancerHealthCheck SLoadbalancerHTTPRateLimiter } diff --git a/pkg/compute/models/loadbalancers.go b/pkg/compute/models/loadbalancers.go index be072ab187..da6ec63157 100644 --- a/pkg/compute/models/loadbalancers.go +++ b/pkg/compute/models/loadbalancers.go @@ -66,7 +66,8 @@ type SLoadbalancer struct { ChargeType string `list:"user" get:"user" create:"optional"` LoadbalancerSpec string `list:"user" get:"user" create:"optional"` - BackendGroupId string `width:"36" charset:"ascii" nullable:"true" list:"user" update:"user" update:"user"` + BackendGroupId string `width:"36" charset:"ascii" nullable:"true" list:"user" update:"user" update:"user"` + LBInfo jsonutils.JSONObject `charset:"utf8" nullable:"true" list:"user" update:"admin" create:"admin_required"` } func (man *SLoadbalancerManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { diff --git a/pkg/hostman/guestfs/fsdriver/windows.go b/pkg/hostman/guestfs/fsdriver/windows.go index 4554fbcf3b..fd8347ffb6 100644 --- a/pkg/hostman/guestfs/fsdriver/windows.go +++ b/pkg/hostman/guestfs/fsdriver/windows.go @@ -97,7 +97,7 @@ func (w *SWindowsRootFs) GetLoginAccount(rootFs IDiskPartition, defaultRootUser if _, ok := users[admin]; ok && windowsDefaultAdminUser { selUsr = admin } else { - for user, _ := range users { + for user := range users { if user != admin && (len(selUsr) == 0 || len(selUsr) > len(user)) { selUsr = user } @@ -402,14 +402,14 @@ func (w *SWindowsRootFs) deploySetupCompleteScripts(uname, passwd string) bool { "Net stop wuauserv", } for _, v := range [][3]string{ - [3]string{"AUOptions", "REG_DWORD", "3"}, - [3]string{"NoAutoUpdate", "REG_DWORD", "0"}, - [3]string{"ScheduledInstallDay", "REG_DWORD", "0"}, - [3]string{"ScheduledInstallTime", "REG_DWORD", "4"}, - [3]string{"AutoInstallMinorUpdates", "REG_DWORD", "1"}, - [3]string{"NoAutoRebootWithLoggedOnUsers", "REG_DWORD", "1"}, - [3]string{"IncludeRecommendedUpdates", "REG_DWORD", "0"}, - [3]string{"EnableFeaturedSoftware", "REG_DWORD", "1"}, + {"AUOptions", "REG_DWORD", "3"}, + {"NoAutoUpdate", "REG_DWORD", "0"}, + {"ScheduledInstallDay", "REG_DWORD", "0"}, + {"ScheduledInstallTime", "REG_DWORD", "4"}, + {"AutoInstallMinorUpdates", "REG_DWORD", "1"}, + {"NoAutoRebootWithLoggedOnUsers", "REG_DWORD", "1"}, + {"IncludeRecommendedUpdates", "REG_DWORD", "0"}, + {"EnableFeaturedSoftware", "REG_DWORD", "1"}, } { cmds = append(cmds, `REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v %s /t %s /d %s /f`, v[0], v[1], v[2]) diff --git a/pkg/util/qcloud/certificate.go b/pkg/util/qcloud/certificate.go new file mode 100755 index 0000000000..3a8e1e898d --- /dev/null +++ b/pkg/util/qcloud/certificate.go @@ -0,0 +1,81 @@ +package qcloud + +import ( + "strconv" + "time" +) + +type projectInfo struct { + ProjectID string `json:"projectId"` + OwnerUin int64 `json:"ownerUin"` + Name string `json:"name"` + CreatorUin int64 `json:"creatorUin"` + CreateTime string `json:"createTime"` + Info string `json:"info"` +} + +// https://cloud.tencent.com/document/api/400/13675 +type SCertificate struct { + OwnerUin string `json:"ownerUin"` + ProjectID string `json:"projectId"` + From string `json:"from"` + Type int `json:"type"` + Cert string `json:"cert"` + CERTType string `json:"certType"` + ProductZhName string `json:"productZhName"` + Domain string `json:"domain"` + Alias string `json:"alias"` + Status int `json:"status"` + VulnerabilityStatus string `json:"vulnerability_status"` + VerifyType string `json:"verifyType"` + CERTBeginTime time.Time `json:"certBeginTime"` + CERTEndTime time.Time `json:"certEndTime"` + ValidityPeriod string `json:"validityPeriod"` + InsertTime string `json:"insertTime"` + ProjectInfo projectInfo `json:"projectInfo"` + ID string `json:"id"` // 证书Id + SubjectAltName []string `json:"subjectAltName"` + TypeName string `json:"type_name"` + StatusName string `json:"status_name"` + IsVip bool `json:"is_vip"` + IsDv bool `json:"is_dv"` + IsWildcard bool `json:"is_wildcard"` + IsVulnerability bool `json:"is_vulnerability"` +} + +func (self *SRegion) GetCertificates(id string, withCert bool, limit int, page int) ([]SCertificate, int, error) { + params := map[string]string{} + if withCert { + params["withCert"] = "1" + } + + if len(id) > 0 { + params["id"] = id + } + + if limit > 0 { + params["count"] = strconv.Itoa(limit) + } + + if page > 0 { + params["page"] = strconv.Itoa(page) + } + + resp, err := self.wssRequest("CertGetList", params) + if err != nil { + return nil, 0, err + } + + certs := []SCertificate{} + err = resp.Unmarshal(&certs, "list") + if err != nil { + return nil, 0, err + } + + total, err := resp.Float("totalNum") + if err != nil { + return nil, 0, err + } + + return certs, int(total), nil +} diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go new file mode 100755 index 0000000000..900d708e10 --- /dev/null +++ b/pkg/util/qcloud/loadbalancer.go @@ -0,0 +1,253 @@ +package qcloud + +import ( + "fmt" + "strconv" + "time" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +const ( + LB_ADDR_TYPE_INTERNAL = "INTERNAL" + LB_ADDR_TYPE_OPEN = "OPEN" +) + +type LB_TYPE int64 + +const ( + LB_TYPE_CLASSIC = LB_TYPE(0) + LB_TYPE_APPLICATION = LB_TYPE(1) +) + +/* +目前存在的问题: +1.HTTP\HTTPS监听调度算法没同步成功 +2.后端服务器端口权重没同步成功 +3.后端服务器组待确认 + +*/ +// https://cloud.tencent.com/document/api/214/30694#LoadBalancer +type SLoadbalancer struct { + region *SRegion + + Status int64 `json:"Status"` // 0:创建中,1:正常运行 + Domain string `json:"Domain"` + VpcID string `json:"VpcId"` + Log string `json:"Log"` + ProjectID int64 `json:"ProjectId"` + Snat bool `json:"Snat"` + LoadBalancerID string `json:"LoadBalancerId"` + LoadBalancerVips []string `json:"LoadBalancerVips"` + LoadBalancerType string `json:"LoadBalancerType"` // 负载均衡实例的网络类型: OPEN:公网属性, INTERNAL:内网属性。 + LoadBalancerName string `json:"LoadBalancerName"` + Forward LB_TYPE `json:"Forward"` // 应用型负载均衡标识,1:应用型负载均衡,0:传统型的负载均衡。 + StatusTime time.Time `json:"StatusTime"` + OpenBGP int64 `json:"OpenBgp"` // 高防 LB 的标识,1:高防负载均衡 0:非高防负载均衡。 + CreateTime time.Time `json:"CreateTime"` + Isolation int64 `json:"Isolation"` // 0:表示未被隔离,1:表示被隔离。 + SubnetId string `json:"SubnetId"` +} + +func (self *SLoadbalancer) GetId() string { + return self.LoadBalancerID +} + +func (self *SLoadbalancer) GetName() string { + return self.LoadBalancerName +} + +// add region? +func (self *SLoadbalancer) GetGlobalId() string { + return self.LoadBalancerID +} + +func (self *SLoadbalancer) GetStatus() string { + switch self.Status { + case 0: + return models.LB_STATUS_INIT + case 1: + return models.LB_STATUS_ENABLED + default: + return models.LB_STATUS_UNKNOWN + } +} + +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) GetMetadata() *jsonutils.JSONDict { + meta := jsonutils.NewDict() + meta.Add(jsonutils.NewInt(int64(self.Forward)), "Forward") + meta.Add(jsonutils.NewInt(self.OpenBGP), "OpenBGP") + meta.Add(jsonutils.NewString(self.Domain), "Domain") + meta.Add(jsonutils.NewInt(self.ProjectID), "ProjectID") + + return meta +} + +// todo: 腾讯云支持绑定多个地址。目前未找到相关文档描述。需要提工单询问。 +// 目前先当作只能绑定一个IP处理 +func (self *SLoadbalancer) GetAddress() string { + return self.LoadBalancerVips[0] +} + +func (self *SLoadbalancer) GetAddressType() string { + switch self.LoadBalancerType { + case LB_ADDR_TYPE_INTERNAL: + return models.LB_ADDR_TYPE_INTRANET + case LB_ADDR_TYPE_OPEN: + return models.LB_ADDR_TYPE_INTERNET + default: + return "" + } +} + +func (self *SLoadbalancer) GetNetworkType() string { + return models.LB_NETWORK_TYPE_VPC +} + +func (self *SLoadbalancer) GetNetworkId() string { + return self.SubnetId +} + +func (self *SLoadbalancer) GetVpcId() string { + return self.VpcID +} + +func (self *SLoadbalancer) GetZoneId() string { + return "" +} + +func (self *SLoadbalancer) GetLoadbalancerListeners(protocal string) ([]SLBListener, error) { + listeners, err := self.region.GetLoadbalancerListeners(self.GetId(), self.Forward, "") + if err != nil { + return nil, err + } + + for i := range listeners { + listeners[i].lb = self + } + + return listeners, nil +} + +func (self *SLoadbalancer) GetILoadbalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { + listeners, err := self.GetLoadbalancerListeners("") + if err != nil { + return nil, err + } + + ilisteners := make([]cloudprovider.ICloudLoadbalancerListener, len(listeners)) + for i := range listeners { + l := listeners[i] + ilisteners[i] = &l + } + + return ilisteners, nil +} + +func (self *SLoadbalancer) GetILoadbalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { + listeners, err := self.GetLoadbalancerListeners("") + if err != nil { + return nil, err + } + + bgs := []cloudprovider.ICloudLoadbalancerBackendGroup{} + for i := range listeners { + listener := listeners[i] + t := listener.GetListenerType() + if t == models.LB_LISTENER_TYPE_HTTP || t == models.LB_LISTENER_TYPE_HTTPS { + rules := listener.Rules + for i := range rules { + rule := rules[i] + rule.listener = &listener + bg := rule.GetBackendGroup() + bgs = append(bgs, bg) + } + } else { + bg := listener.GetBackendGroup() + bgs = append(bgs, bg) + } + } + + ibgs := make([]cloudprovider.ICloudLoadbalancerBackendGroup, len(bgs)) + for i := range bgs { + ibgs[i] = bgs[i] + } + + return ibgs, nil +} + +func (self *SRegion) GetLoadbalancers(ids []string) ([]SLoadbalancer, error) { + params := map[string]string{} + if ids != nil { + for i, id := range ids { + params[fmt.Sprintf("LoadBalancerIds.%d", i)] = id + } + } + + offset := 0 + limit := 100 + lbs := make([]SLoadbalancer, 0) + for { + params["Limit"] = strconv.Itoa(limit) + params["Offset"] = strconv.Itoa(offset) + + resp, err := self.clbRequest("DescribeLoadBalancers", params) + if err != nil { + return nil, err + } + + parts := make([]SLoadbalancer, 0) + err = resp.Unmarshal(&parts, "LoadBalancerSet") + if err != nil { + return nil, err + } + + _total, err := resp.Float("TotalCount") + if err != nil { + return nil, err + } + + total := int(_total) + if err != nil { + return nil, err + } + + lbs = append(lbs, parts...) + offset += limit + if offset >= total { + for i := range lbs { + lbs[i].region = self + } + + return lbs, err + } + } +} + +func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) { + if len(id) == 0 { + return nil, fmt.Errorf("GetLoadbalancer id should not empty") + } + + lbs, err := self.GetLoadbalancers([]string{id}) + if err != nil && len(lbs) == 1 { + return &lbs[0], nil + } + + return nil, err +} diff --git a/pkg/util/qcloud/loadbalancer_acl.go b/pkg/util/qcloud/loadbalancer_acl.go new file mode 100755 index 0000000000..aaa936ea53 --- /dev/null +++ b/pkg/util/qcloud/loadbalancer_acl.go @@ -0,0 +1,40 @@ +package qcloud + +import ( + "yunion.io/x/jsonutils" +) + +// todo: 腾讯云没有LB ACL +type SLBACL struct{} + +func (self *SLBACL) GetId() string { + return "" +} + +func (self *SLBACL) GetName() string { + return "" +} + +func (self *SLBACL) GetGlobalId() string { + return "" +} + +func (self *SLBACL) GetStatus() string { + return "" +} + +func (self *SLBACL) Refresh() error { + return nil +} + +func (self *SLBACL) IsEmulated() bool { + return false +} + +func (self *SLBACL) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SLBACL) GetAclEntries() *jsonutils.JSONArray { + return nil +} diff --git a/pkg/util/qcloud/loadbalancer_backend.go b/pkg/util/qcloud/loadbalancer_backend.go new file mode 100755 index 0000000000..24acfcbcc3 --- /dev/null +++ b/pkg/util/qcloud/loadbalancer_backend.go @@ -0,0 +1,163 @@ +package qcloud + +import ( + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SLBBackend struct { + group *SLBBackendGroup + + PublicIPAddresses []string `json:"PublicIpAddresses"` + Weight int `json:"Weight"` + InstanceID string `json:"InstanceId"` + InstanceName string `json:"InstanceName"` + PrivateIPAddresses []string `json:"PrivateIpAddresses"` + RegisteredTime string `json:"RegisteredTime"` + Type string `json:"Type"` + Port int `json:"Port"` +} + +// ========================================================== +type SListenerBackend struct { + Rules []rule `json:"Rules"` + Targets []SLBBackend `json:"Targets"` + Protocol string `json:"Protocol"` + ListenerID string `json:"ListenerId"` + Port int64 `json:"Port"` +} + +type rule struct { + URL string `json:"Url"` + Domain string `json:"Domain"` + LocationID string `json:"LocationId"` + Targets []SLBBackend `json:"Targets"` +} + +// ========================================================== + +func (self *SLBBackend) GetId() string { + return self.InstanceID +} + +func (self *SLBBackend) GetName() string { + return fmt.Sprintf("%s/%s", self.group.GetId(), self.GetId()) +} + +func (self *SLBBackend) GetGlobalId() string { + return self.GetId() +} + +// todo: status ?? +func (self *SLBBackend) GetStatus() string { + return "" +} + +func (self *SLBBackend) Refresh() error { + panic("implement me") +} + +func (self *SLBBackend) IsEmulated() bool { + return false +} + +func (self *SLBBackend) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SLBBackend) GetWeight() int { + return self.Weight +} + +func (self *SLBBackend) GetPort() int { + return self.Port +} + +// todo: self.Type ?? +func (self *SLBBackend) GetBackendType() string { + return models.LB_BACKEND_GUEST +} + +func (self *SLBBackend) GetBackendRole() string { + return models.LB_BACKEND_ROLE_DEFAULT +} + +func (self *SLBBackend) GetBackendId() string { + return self.InstanceID +} + +// 传统型: https://cloud.tencent.com/document/product/214/31790 +func (self *SRegion) getClassicBackends(lbId, listenerId string) ([]SLBBackend, error) { + params := map[string]string{"LoadBalancerId": lbId} + + resp, err := self.clbRequest("DescribeClassicalLBTargets", params) + if err != nil { + return nil, err + } + + backends := []SLBBackend{} + err = resp.Unmarshal(&backends, "Targets") + if err != nil { + return nil, err + } + return backends, nil +} + +// 应用型: https://cloud.tencent.com/document/product/214/30684 +func (self *SRegion) getBackends(lbId, listenerId, ruleId string) ([]SLBBackend, error) { + params := map[string]string{"LoadBalancerId": lbId} + + if len(listenerId) > 0 { + params["ListenerIds.0"] = listenerId + } + + if len(listenerId) > 0 { + params["ListenerIds.0"] = listenerId + } + resp, err := self.clbRequest("DescribeTargets", params) + if err != nil { + return nil, err + } + + lbackends := []SListenerBackend{} + err = resp.Unmarshal(&lbackends, "Listeners") + if err != nil { + return nil, err + } + + for _, entry := range lbackends { + if (entry.Protocol == "HTTP" || entry.Protocol == "HTTPS") && len(ruleId) == 0 { + return nil, fmt.Errorf("GetBackends http、https listener must specific rule id") + } + + if len(ruleId) > 0 { + for _, r := range entry.Rules { + if r.LocationID == ruleId { + return r.Targets, nil + } + } + } else { + return entry.Targets, nil + } + } + + // todo: 这里是返回空列表还是404? + return []SLBBackend{}, nil +} + +// 注意http、https监听器必须指定ruleId +func (self *SRegion) GetLBBackends(t LB_TYPE, lbId, listenerId, ruleId string) ([]SLBBackend, error) { + if len(lbId) == 0 { + return nil, fmt.Errorf("GetLBBackends loadbalancer id should not be empty") + } + + if t == LB_TYPE_APPLICATION { + return self.getBackends(lbId, listenerId, ruleId) + } else if t == LB_TYPE_CLASSIC { + return self.getClassicBackends(lbId, listenerId) + } else { + return nil, fmt.Errorf("GetLBBackends unsupported loadbalancer type %d", t) + } +} diff --git a/pkg/util/qcloud/loadbalancer_backendgroup.go b/pkg/util/qcloud/loadbalancer_backendgroup.go new file mode 100755 index 0000000000..528977980d --- /dev/null +++ b/pkg/util/qcloud/loadbalancer_backendgroup.go @@ -0,0 +1,113 @@ +package qcloud + +import ( + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SLBBackendGroup struct { + lb *SLoadbalancer // 必须不能为nil + listener *SLBListener // 必须不能为nil + rule *SLBListenerRule // tcp、udp、tcp_ssl监听rule 为nil +} + +func (self *SLBBackendGroup) AddBackendServer(serverId string, weight int, port int) (cloudprovider.ICloudLoadbalancerBackend, error) { + panic("implement me") +} + +func (self *SLBBackendGroup) RemoveBackendServer(serverId string, weight int, port int) error { + panic("implement me") +} + +func (self *SLBBackendGroup) Delete() error { + panic("implement me") +} + +func (self *SLBBackendGroup) Sync(name string) error { + panic("implement me") +} + +func backendGroupIdGen(lbid string, secondId string) string { + if len(secondId) > 0 { + return fmt.Sprintf("%s/%s", lbid, secondId) + } else { + return lbid + } + +} + +// http https 后端服务器只与规则绑定 +func (self *SLBBackendGroup) GetId() string { + t := self.listener.GetListenerType() + if t == models.LB_LISTENER_TYPE_HTTP || t == models.LB_LISTENER_TYPE_HTTPS { + return backendGroupIdGen(self.lb.GetId(), self.rule.GetId()) + } else if self.lb.Forward == LB_TYPE_APPLICATION { + return backendGroupIdGen(self.lb.GetId(), self.listener.GetId()) + } else { + // 传统型lb 所有监听共用一个后端服务器组 + return backendGroupIdGen(self.lb.GetId(), "") + } +} + +func (self *SLBBackendGroup) GetName() string { + return self.GetId() +} + +func (self *SLBBackendGroup) GetGlobalId() string { + return self.GetId() +} + +func (self *SLBBackendGroup) GetStatus() string { + return "" +} + +func (self *SLBBackendGroup) Refresh() error { + return nil +} + +// todo: model没有更新这个字段? +func (self *SLBBackendGroup) IsEmulated() bool { + return true +} + +func (self *SLBBackendGroup) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SLBBackendGroup) IsDefault() bool { + return false +} + +// todo: ?? +func (self *SLBBackendGroup) GetType() string { + return models.LB_BACKENDGROUP_TYPE_NORMAL +} + +func (self *SLBBackendGroup) GetILoadbalancerBackends() ([]cloudprovider.ICloudLoadbalancerBackend, error) { + backends := []SLBBackend{} + var err error + if self.rule != nil { + // http、https监听 + backends, err = self.lb.region.GetLBBackends(self.lb.Forward, self.lb.GetId(), self.listener.GetId(), self.rule.GetId()) + if err != nil { + return nil, err + } + } else { + // tcp,udp,tcp_ssl监听 + backends, err = self.lb.region.GetLBBackends(self.lb.Forward, self.lb.GetId(), self.listener.GetId(), "") + if err != nil { + return nil, err + } + } + + ibackends := make([]cloudprovider.ICloudLoadbalancerBackend, len(backends)) + for i := range backends { + backends[i].group = self + ibackends[i] = &backends[i] + } + + return ibackends, nil +} diff --git a/pkg/util/qcloud/loadbalancer_certificate.go b/pkg/util/qcloud/loadbalancer_certificate.go new file mode 100755 index 0000000000..055fa8f53c --- /dev/null +++ b/pkg/util/qcloud/loadbalancer_certificate.go @@ -0,0 +1,77 @@ +package qcloud + +import ( + "crypto/sha1" + "fmt" + "strconv" + "strings" + "time" + + "yunion.io/x/jsonutils" +) + +type SLBCertificate struct { + region *SRegion + + SCertificate +} + +func (self *SLBCertificate) GetId() string { + return self.ID +} + +// todo: ?? +func (self *SLBCertificate) GetName() string { + return self.Alias +} + +func (self *SLBCertificate) GetGlobalId() string { + return self.ID +} + +// todo: 貌似目前onecloud没有记录状态 +func (self *SLBCertificate) GetStatus() string { + return strconv.Itoa(self.Status) +} + +func (self *SLBCertificate) Refresh() error { + certs, total, err := self.region.GetCertificates(self.GetId(), true, 0, 0) + if err != nil { + return err + } + + if total != 1 { + return fmt.Errorf("%d Certificate found", total) + } + + cert := SLBCertificate{region: self.region, SCertificate: certs[0]} + return jsonutils.Update(self, cert) +} + +func (self *SLBCertificate) IsEmulated() bool { + return false +} + +func (self *SLBCertificate) GetMetadata() *jsonutils.JSONDict { + return nil +} + +// todo: ?? +func (self *SLBCertificate) GetCommonName() string { + return self.Domain +} + +func (self *SLBCertificate) GetSubjectAlternativeNames() string { + return strings.Join(self.SubjectAltName, ",") +} + +// todo: ?? +func (self *SLBCertificate) GetFingerprint() string { + _fp := sha1.Sum([]byte(self.Cert)) + fp := fmt.Sprintf("sha1:% x", _fp) + return strings.Replace(fp, " ", ":", -1) +} + +func (self *SLBCertificate) GetExpireTime() time.Time { + return self.CERTEndTime +} diff --git a/pkg/util/qcloud/loadbalancer_listener.go b/pkg/util/qcloud/loadbalancer_listener.go new file mode 100755 index 0000000000..4235ba621a --- /dev/null +++ b/pkg/util/qcloud/loadbalancer_listener.go @@ -0,0 +1,405 @@ +package qcloud + +import ( + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/compute/models" +) + +var HTTP_CODES = []string{ + models.LB_HEALTH_CHECK_HTTP_CODE_1xx, + models.LB_HEALTH_CHECK_HTTP_CODE_2xx, + models.LB_HEALTH_CHECK_HTTP_CODE_3xx, + models.LB_HEALTH_CHECK_HTTP_CODE_4xx, + models.LB_HEALTH_CHECK_HTTP_CODE_5xx, +} + +type certificate struct { + SSLMode string `json:"SSLMode"` + CERTCAID string `json:"CertCaId"` + CERTID string `json:"CertId"` +} + +/* +健康检查状态码(仅适用于HTTP/HTTPS转发规则)。可选值:1~31,默认 31。 +1 表示探测后返回值 1xx 表示健康,2 表示返回 2xx 表示健康,4 表示返回 3xx 表示健康,8 表示返回 4xx 表示健康,16 表示返回 5xx 表示健康。 +若希望多种码都表示健康,则将相应的值相加。 +*/ +type healthCheck struct { + HTTPCheckDomain string `json:"HttpCheckDomain"` + HealthSwitch int64 `json:"HealthSwitch"` + HTTPCheckPath string `json:"HttpCheckPath"` + HTTPCheckMethod string `json:"HttpCheckMethod"` + UnHealthNum int `json:"UnHealthNum"` + IntervalTime int `json:"IntervalTime"` + HTTPCode int `json:"HttpCode"` // 健康检查状态码(仅适用于HTTP/HTTPS转发规则)。可选值:1~31,默认 31。 + HealthNum int `json:"HealthNum"` + TimeOut int `json:"TimeOut"` +} + +type SLBListener struct { + lb *SLoadbalancer + + Protocol string `json:"Protocol"` // 监听器协议类型,取值 TCP | UDP | HTTP | HTTPS | TCP_SSL + Certificate certificate `json:"Certificate"` + SniSwitch int64 `json:"SniSwitch"` // 是否开启SNI特性(本参数仅对于HTTPS监听器有意义) + HealthCheck healthCheck `json:"HealthCheck"` // 仅适用于TCP/UDP/TCP_SSL监听器 + ListenerID string `json:"ListenerId"` + ListenerName string `json:"ListenerName"` + Rules []SLBListenerRule `json:"Rules"` // 监听器下的全部转发规则(本参数仅对于HTTP/HTTPS监听器有意义) + Scheduler string `json:"Scheduler"` + SessionExpireTime int `json:"SessionExpireTime"` // 会话保持时间,单位:秒。可选值:30~3600,默认 0,表示不开启。此参数仅适用于TCP/UDP监听器。 + Port int `json:"Port"` + ClassicListener bool // 这个字段是在qcloud返回字段基础上,额外增加的字段。用于区分listener 是否是classic。 +} + +func (self *SLBListener) GetBackendServerPort() int { + panic("implement me") +} + +func (self *SLBListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) { + panic("implement me") +} + +func (self *SLBListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudprovider.ICloudLoadbalancerListenerRule, error) { + panic("implement me") +} + +func (self *SLBListener) Start() error { + panic("implement me") +} + +func (self *SLBListener) Stop() error { + panic("implement me") +} + +func (self *SLBListener) Sync(listener *cloudprovider.SLoadbalancerListener) error { + panic("implement me") +} + +func (self *SLBListener) Delete() error { + panic("implement me") +} + +// https://cloud.tencent.com/document/api/214/30694#ClassicalListener +type SLBClassicListener struct { + InstancePort int64 `json:"InstancePort"` + CERTCAID string `json:"CertCaId"` + Status int64 `json:"Status"` + CERTID string `json:"CertId"` + Protocol string `json:"Protocol"` + TimeOut int `json:"TimeOut"` + HTTPHash string `json:"HttpHash"` // 公网固定IP型的 HTTP、HTTPS 协议监听器的轮询方法。wrr 表示按权重轮询,ip_hash 表示根据访问的源 IP 进行一致性哈希方式来分发 + UnhealthNum int `json:"UnhealthNum"` + IntervalTime int `json:"IntervalTime"` + ListenerID string `json:"ListenerId"` + ListenerPort int `json:"ListenerPort"` + HTTPCheckPath string `json:"HttpCheckPath"` + HealthNum int `json:"HealthNum"` + ListenerName string `json:"ListenerName"` + HealthSwitch int64 `json:"HealthSwitch"` + SSLMode string `json:"SSLMode"` + SessionExpire int `json:"SessionExpire"` + HTTPCode int `json:"HttpCode"` +} + +func (self *SLBClassicListener) ToLBListener() SLBListener { + // 转换之后丢弃了 InstancePort、Status、HttpHash + return SLBListener{ + Protocol: self.Protocol, + Certificate: certificate{ + SSLMode: self.SSLMode, + CERTCAID: self.CERTCAID, + CERTID: self.CERTID, + }, + HealthCheck: healthCheck{ + HTTPCheckDomain: "", + HealthSwitch: self.HealthSwitch, + HTTPCheckPath: self.HTTPCheckPath, + HTTPCheckMethod: "", + UnHealthNum: self.UnhealthNum, + IntervalTime: self.IntervalTime, + HTTPCode: self.HTTPCode, + HealthNum: self.HealthNum, + TimeOut: self.TimeOut, + }, + ListenerID: self.ListenerID, + ListenerName: self.ListenerName, + Rules: nil, + Scheduler: self.HTTPHash, + SessionExpireTime: self.SessionExpire, + Port: self.ListenerPort, + ClassicListener: true, + } +} + +func (self *SLBListener) GetId() string { + return self.ListenerID +} + +func (self *SLBListener) GetName() string { + return self.ListenerName +} + +func (self *SLBListener) GetGlobalId() string { + return self.ListenerID +} + +// 腾讯云负载均衡没有启用禁用操作 +func (self *SLBListener) GetStatus() string { + return models.LB_STATUS_ENABLED +} + +func (self *SLBListener) Refresh() error { + panic("implement me") +} + +func (self *SLBListener) IsEmulated() bool { + return false +} + +func (self *SLBListener) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SLBListener) GetListenerType() string { + switch self.Protocol { + case "TCP": + return models.LB_LISTENER_TYPE_TCP + case "UDP": + return models.LB_LISTENER_TYPE_UDP + case "HTTP": + return models.LB_LISTENER_TYPE_HTTP + case "HTTPS": + return models.LB_LISTENER_TYPE_HTTPS + case "TCP_SSL": + return models.LB_LISTENER_TYPE_TCP + default: + return "" + } +} + +func (self *SLBListener) GetListenerPort() int { + return self.Port +} + +func (self *SLBListener) GetScheduler() string { + switch strings.ToLower(self.Scheduler) { + case "wrr": + return models.LB_SCHEDULER_WRR + case "ip_hash": + return models.LB_SCHEDULER_SCH + case "least_conn": + return models.LB_SCHEDULER_WLC + default: + return "" + } +} + +func (self *SLBListener) GetAclStatus() string { + return "" +} + +func (self *SLBListener) GetAclType() string { + return "" +} + +func (self *SLBListener) GetAclId() string { + return "" +} + +func (self *SLBListener) GetHealthCheck() string { + if self.HealthCheck.HealthSwitch == 0 { + return models.LB_HEALTH_CHECK_DISABLE + } else { + return models.LB_HEALTH_CHECK_ENABLE + } +} + +// todo: ?待确认 +func (self *SLBListener) GetHealthCheckType() string { + if len(self.HealthCheck.HTTPCheckMethod) > 0 { + return models.LB_HEALTH_CHECK_HTTP + } else { + return models.LB_HEALTH_CHECK_TCP + } +} + +func (self *SLBListener) GetHealthCheckTimeout() int { + return self.HealthCheck.TimeOut +} + +func (self *SLBListener) GetHealthCheckInterval() int { + return self.HealthCheck.IntervalTime +} + +func (self *SLBListener) GetHealthCheckRise() int { + return self.HealthCheck.HealthNum +} + +func (self *SLBListener) GetHealthCheckFail() int { + return self.HealthCheck.UnHealthNum +} + +func (self *SLBListener) GetHealthCheckReq() string { + return "" +} + +func (self *SLBListener) GetHealthCheckExp() string { + return "" +} + +func (self *SLBListener) GetBackendGroup() *SLBBackendGroup { + t := self.GetListenerType() + // http、https类型的监听不能直接绑定服务器 + if t == models.LB_LISTENER_TYPE_HTTP || t == models.LB_LISTENER_TYPE_HTTPS { + return nil + } else { + return &SLBBackendGroup{lb: self.lb, listener: self} + } +} + +func (self *SLBListener) GetBackendGroupId() string { + bg := self.GetBackendGroup() + if bg == nil { + return "" + } + + return bg.GetId() +} + +func (self *SLBListener) GetHealthCheckDomain() string { + return self.HealthCheck.HTTPCheckDomain +} + +func (self *SLBListener) GetHealthCheckURI() string { + return self.HealthCheck.HTTPCheckPath +} + +func (self *SLBListener) GetHealthCheckCode() string { + codes := []string{} + for i := uint8(0); i < 5; i++ { + n := 1 << i + if (self.HealthCheck.HTTPCode & n) == n { + codes = append(codes, HTTP_CODES[i]) + } + } + + return strings.Join(codes, ",") +} + +// 仅http、https类型监听包含rules +func (self *SLBListener) GetILoadbalancerListenerRules() ([]cloudprovider.ICloudLoadbalancerListenerRule, error) { + rules := self.Rules + iRules := []cloudprovider.ICloudLoadbalancerListenerRule{} + for i := 0; i < len(rules); i++ { + rules[i].listener = self + iRules = append(iRules, &rules[i]) + } + return iRules, nil +} + +// todo: ?? +func (self *SLBListener) GetStickySession() string { + if self.SessionExpireTime == 0 { + return models.LB_BOOL_OFF + } else { + return models.LB_BOOL_ON + } +} + +// 支持基于 cookie 插入的会话保持能力 https://cloud.tencent.com/document/product/214/6154 +func (self *SLBListener) GetStickySessionType() string { + return models.LB_STICKY_SESSION_TYPE_INSERT +} + +// todo: 腾讯云不支持? +func (self *SLBListener) GetStickySessionCookie() string { + return "" +} + +func (self *SLBListener) GetStickySessionCookieTimeout() int { + return self.SessionExpireTime +} + +/* +7层负载均衡系统提供 X-Forwarded-For 的方式获取访问者真实 IP,LB 侧默认开启 + +https://cloud.tencent.com/document/product/214/6151 +七层转发获取来访真实IP的方法 https://cloud.tencent.com/document/product/214/3728 +*/ +func (self *SLBListener) XForwardedForEnabled() bool { + switch self.GetListenerType() { + case models.LB_LISTENER_TYPE_HTTP, models.LB_LISTENER_TYPE_HTTPS: + return true + default: + return false + } +} + +// HTTP/HTTPS协议默认支持用户开启gzip压缩功能 +// 负载均衡开启Gzip配置及检测方法说明 https://cloud.tencent.com/document/product/214/5404 +func (self *SLBListener) GzipEnabled() bool { + switch self.GetListenerType() { + case models.LB_LISTENER_TYPE_HTTP, models.LB_LISTENER_TYPE_HTTPS: + return true + default: + return false + } +} + +func (self *SLBListener) GetCertificateId() string { + return self.Certificate.CERTID +} + +// todo: ?? +func (self *SLBListener) GetTLSCipherPolicy() string { + return "" +} + +// todo: ? +// 负载均衡能力说明 https://cloud.tencent.com/document/product/214/6534 +func (self *SLBListener) HTTP2Enabled() bool { + return true +} + +func (self *SRegion) GetLoadbalancerListeners(lbid string, t LB_TYPE, protocol string) ([]SLBListener, error) { + params := map[string]string{"LoadBalancerId": lbid} + if len(protocol) > 0 { + params["Protocol"] = protocol + } + + listeners := []SLBListener{} + if t == LB_TYPE_CLASSIC { + resp, err := self.clbRequest("DescribeClassicalLBListeners", params) + if err != nil { + return nil, err + } + + clisteners := []SLBClassicListener{} + err = resp.Unmarshal(&clisteners, "Listeners") + if err != nil { + return nil, err + } + + for _, l := range clisteners { + listeners = append(listeners, l.ToLBListener()) + } + + return listeners, nil + } + + resp, err := self.clbRequest("DescribeListeners", params) + if err != nil { + return nil, err + } + + err = resp.Unmarshal(&listeners, "Listeners") + if err != nil { + return nil, err + } + + return listeners, nil +} diff --git a/pkg/util/qcloud/loadbalancer_listenerrule.go b/pkg/util/qcloud/loadbalancer_listenerrule.go new file mode 100755 index 0000000000..bcce799477 --- /dev/null +++ b/pkg/util/qcloud/loadbalancer_listenerrule.go @@ -0,0 +1,82 @@ +package qcloud + +import ( + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/compute/models" +) + +type SLBListenerRule struct { + listener *SLBListener + + Domain string `json:"Domain"` + Certificate certificate `json:"Certificate"` + URL string `json:"Url"` + HealthCheck healthCheck `json:"HealthCheck"` + LocationID string `json:"LocationId"` + Scheduler string `json:"Scheduler"` + SessionExpireTime int64 `json:"SessionExpireTime"` +} + +func (self *SLBListenerRule) Delete() error { + panic("implement me") +} + +func (self *SLBListenerRule) GetId() string { + return self.LocationID +} + +func (self *SLBListenerRule) GetName() string { + return self.LocationID +} + +func (self *SLBListenerRule) GetGlobalId() string { + return self.LocationID +} + +// todo: rule status?? +func (self *SLBListenerRule) GetStatus() string { + return "" +} + +func (self *SLBListenerRule) Refresh() error { + return nil +} + +func (self *SLBListenerRule) IsEmulated() bool { + return false +} + +func (self *SLBListenerRule) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (self *SLBListenerRule) GetDomain() string { + return self.Domain +} + +func (self *SLBListenerRule) GetPath() string { + return self.URL +} + +func (self *SLBListenerRule) GetBackendGroup() *SLBBackendGroup { + t := self.listener.GetListenerType() + if t == models.LB_LISTENER_TYPE_HTTP || t == models.LB_LISTENER_TYPE_HTTPS { + return &SLBBackendGroup{ + lb: self.listener.lb, + listener: self.listener, + rule: self, + } + } + + return nil +} + +// 只有http、https协议监听规则有backendgroupid +func (self *SLBListenerRule) GetBackendGroupId() string { + bg := self.GetBackendGroup() + if bg == nil { + return "" + } + + return bg.GetId() +} diff --git a/pkg/util/qcloud/qcloud.go b/pkg/util/qcloud/qcloud.go index f50a3638d4..32b9ab10d5 100644 --- a/pkg/util/qcloud/qcloud.go +++ b/pkg/util/qcloud/qcloud.go @@ -1,11 +1,13 @@ package qcloud import ( + "encoding/json" "fmt" "strings" "time" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" @@ -23,6 +25,7 @@ const ( QCLOUD_DEFAULT_REGION = "ap-beijing" QCLOUD_API_VERSION = "2017-03-12" + QCLOUD_CLB_API_VERSION = "2018-03-17" QCLOUD_BILLING_API_VERSION = "2018-07-09" ) @@ -48,40 +51,117 @@ func NewQcloudClient(providerId string, providerName string, secretID string, se return &client, nil } -func jsonRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { - domain := "cvm.tencentcloudapi.com" - if region, ok := params["Region"]; ok && strings.HasSuffix(region, "-fsi") { - domain = "cvm." + region + ".tencentcloudapi.com" +// 默认接口请求频率限制:20次/秒 +// 部分接口支持金融区地域。由于金融区和非金融区是隔离不互通的,因此当公共参数 Region 为金融区地域(例如 ap-shanghai-fsi)时,需要同时指定带金融区地域的域名,最好和 Region 的地域保持一致,例如:clb.ap-shanghai-fsi.tencentcloudapi.com +// https://cloud.tencent.com/document/product/416/6479 +func apiDomain(product string, params map[string]string) string { + region, ok := params["Region"] + if ok && strings.HasSuffix(region, "-fsi") { + return product + "." + region + ".tencentcloudapi.com" + } else { + return product + ".tencentcloudapi.com" } +} + +func jsonRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + domain := apiDomain("cvm", params) return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params) } func vpcRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { - domain := "vpc.tencentcloudapi.com" - if region, ok := params["Region"]; ok && strings.HasSuffix(region, "-fsi") { - domain = "vpc." + region + ".tencentcloudapi.com" - } + domain := apiDomain("vpc", params) return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params) } func cbsRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { - domain := "cbs.tencentcloudapi.com" - if region, ok := params["Region"]; ok && strings.HasSuffix(region, "-fsi") { - domain = "cbs." + region + ".tencentcloudapi.com" - } + domain := apiDomain("cbs", params) return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params) } +// loadbalancer服务 +func clbRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + domain := apiDomain("clb", params) + return _jsonRequest(client, domain, QCLOUD_CLB_API_VERSION, apiName, params) +} + +// ssl 证书服务 +func wssRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + domain := "wss.api.qcloud.com" + return _wssJsonRequest(client, domain, "/v2/index.php", "", apiName, params) +} + func billingRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { domain := "billing.tencentcloudapi.com" return _jsonRequest(client, domain, QCLOUD_BILLING_API_VERSION, apiName, params) } +// ============wssJsonRequest============ +type qcloudResponse interface { + tchttp.Response + GetResponse() *interface{} +} + +type wssJsonRequest struct { + tchttp.BaseRequest + Path string +} + +func (r *wssJsonRequest) GetUrl() string { + url := r.BaseRequest.GetUrl() + if url == "" { + return url + } + + index := strings.Index(url, "?") + if index == -1 { + // POST request + url = strings.TrimSuffix(url, "/") + r.Path + } + + p1, p2 := url[:index], url[index:] + p1 = strings.TrimSuffix(p1, "/") + return p1 + r.Path + p2 +} + +func (r *wssJsonRequest) GetPath() string { + return r.Path +} + +type wssJsonResponse struct { + Code int `json:"code"` + CodeDesc string `json:"codeDesc"` + Message string `json:"message"` + Response *interface{} `json:"data"` +} + +func (r *wssJsonResponse) ParseErrorFromHTTPResponse(body []byte) (err error) { + resp := &wssJsonResponse{} + err = json.Unmarshal(body, resp) + if err != nil { + return + } + if resp.Code != 0 { + return errors.NewTencentCloudSDKError(resp.CodeDesc, resp.Message, "") + } + + return nil +} + +func (r *wssJsonResponse) GetResponse() *interface{} { + return r.Response +} + +// ================================== + type QcloudResponse struct { *tchttp.BaseResponse Response *interface{} `json:"Response"` } +func (r *QcloudResponse) GetResponse() *interface{} { + return r.Response +} + func _jsonRequest(client *common.Client, domain string, version string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { req := &tchttp.BaseRequest{} if region, ok := params["Region"]; ok { @@ -98,9 +178,37 @@ func _jsonRequest(client *common.Client, domain string, version string, apiName } req.GetParams()[k] = v } + resp := &QcloudResponse{ BaseResponse: &tchttp.BaseResponse{}, } + + return _baseJsonRequest(client, req, resp) +} + +// wss 专用的 +func _wssJsonRequest(client *common.Client, domain string, path string, version string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + req := &wssJsonRequest{Path: path} + if region, ok := params["Region"]; ok { + client = client.Init(region) + } + client.WithProfile(profile.NewClientProfile()) + service := strings.Split(domain, ".")[0] + req.Init().WithApiInfo(service, version, apiName) + req.SetDomain(domain) + + for k, v := range params { + if strings.HasSuffix(k, "Ids.0") && len(v) == 0 { + return nil, cloudprovider.ErrNotFound + } + req.GetParams()[k] = v + } + + resp := &wssJsonResponse{} + return _baseJsonRequest(client, req, resp) +} + +func _baseJsonRequest(client *common.Client, req tchttp.Request, resp qcloudResponse) (jsonutils.JSONObject, error) { for i := 1; i <= 3; i++ { err := client.Send(req, resp) if err == nil { @@ -118,10 +226,10 @@ func _jsonRequest(client *common.Client, domain string, version string, apiName time.Sleep(time.Second * time.Duration(i*10)) continue } - log.Errorf("request url: %s\nparams: %s\nresponse: %s\nerror: %v", req.GetDomain(), jsonutils.Marshal(req.GetParams()).PrettyString(), resp.Response, err) + log.Errorf("request url: %s\nparams: %s\nresponse: %s\nerror: %v", req.GetDomain(), jsonutils.Marshal(req.GetParams()).PrettyString(), resp.GetResponse(), err) return nil, err } - return jsonutils.Marshal(resp.Response), nil + return jsonutils.Marshal(resp.GetResponse()), nil } func (client *SQcloudClient) GetRegions() []SRegion { @@ -153,6 +261,22 @@ func (client *SQcloudClient) cbsRequest(apiName string, params map[string]string return cbsRequest(cli, apiName, params) } +func (client *SQcloudClient) clbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := client.getDefaultClient() + if err != nil { + return nil, err + } + return clbRequest(cli, apiName, params) +} + +func (client *SQcloudClient) wssRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := client.getDefaultClient() + if err != nil { + return nil, err + } + return wssRequest(cli, apiName, params) +} + func (client *SQcloudClient) billingRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { cli, err := client.getDefaultClient() if err != nil { diff --git a/pkg/util/qcloud/region.go b/pkg/util/qcloud/region.go index 95bc0dffaf..9ae3e5e29f 100644 --- a/pkg/util/qcloud/region.go +++ b/pkg/util/qcloud/region.go @@ -439,6 +439,15 @@ func (self *SRegion) cbsRequest(apiName string, params map[string]string) (jsonu return self.client.cbsRequest(apiName, params) } +func (self *SRegion) clbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + params["Region"] = self.Region + return self.client.clbRequest(apiName, params) +} + +func (self *SRegion) wssRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + return self.client.wssRequest(apiName, params) +} + func (self *SRegion) GetNetworks(ids []string, vpcId string, offset int, limit int) ([]SNetwork, int, error) { if limit > 50 || limit <= 0 { limit = 50 diff --git a/pkg/util/winutils/winutils.go b/pkg/util/winutils/winutils.go index 5f53856247..dd8b8b74fe 100644 --- a/pkg/util/winutils/winutils.go +++ b/pkg/util/winutils/winutils.go @@ -647,12 +647,12 @@ func (w *SWinRegTool) installGpeditStartScript(script, scriptPath string) { w.MkdirP(scriptPath + `\Shutdown`) dsname := "Local Group Policy" kvts := [][3]string{ - [3]string{"GPO-ID", "LocalGPO", "REG_SZ"}, - [3]string{"SOM-ID", "Local", "REG_SZ"}, - [3]string{"FileSysPath", `C:\Windows\System32\GroupPolicy\Machine`, "REG_SZ"}, - [3]string{"DisplayName", dsname, "REG_SZ"}, - [3]string{"GPOName", dsname, "REG_SZ"}, - [3]string{"PSScriptOrder", "1", "REG_DWORD"}, + {"GPO-ID", "LocalGPO", "REG_SZ"}, + {"SOM-ID", "Local", "REG_SZ"}, + {"FileSysPath", `C:\Windows\System32\GroupPolicy\Machine`, "REG_SZ"}, + {"DisplayName", dsname, "REG_SZ"}, + {"GPOName", dsname, "REG_SZ"}, + {"PSScriptOrder", "1", "REG_DWORD"}, } for _, kvt := range kvts { w.SetRegistry(fmt.Sprintf(`%s\Startup\0\%s`, scriptPath, kvt[0]), kvt[1], kvt[2]) @@ -665,10 +665,10 @@ func (w *SWinRegTool) installGpeditStartScript(script, scriptPath string) { } kvts := [][3]string{ - [3]string{"Script", script, "REG_SZ"}, - [3]string{"Parameters", "", "REG_SZ"}, - [3]string{"ExecTime", "", "REG_QWORD"}, - [3]string{"IsPowershell", "0", "REG_DWORD"}, + {"Script", script, "REG_SZ"}, + {"Parameters", "", "REG_SZ"}, + {"ExecTime", "", "REG_QWORD"}, + {"IsPowershell", "0", "REG_DWORD"}, } for _, kvt := range kvts { w.SetRegistry(fmt.Sprintf(`%s\Startup\0\%d\%s`, scriptPath, idx, kvt[0]), kvt[1], kvt[2]) From 3884ef8c5f6b28554afd536023b4d7c088d5a401 Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 31 Jan 2019 18:28:39 +0800 Subject: [PATCH 02/15] impement method fix --- pkg/util/qcloud/loadbalancer.go | 44 +++++ pkg/util/qcloud/loadbalancer_backendgroup.go | 1 - pkg/util/qcloud/loadbalancer_certificate.go | 8 + pkg/util/qcloud/region.go | 171 +++++++++++++++---- 4 files changed, 187 insertions(+), 37 deletions(-) diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index 900d708e10..23cf141a49 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -51,6 +51,50 @@ type SLoadbalancer struct { SubnetId string `json:"SubnetId"` } +func (self *SLoadbalancer) GetLoadbalancerSpec() string { + panic("implement me") +} + +func (self *SLoadbalancer) GetChargeType() string { + panic("implement me") +} + +func (self *SLoadbalancer) Delete() error { + panic("implement me") +} + +func (self *SLoadbalancer) Start() error { + panic("implement me") +} + +func (self *SLoadbalancer) Stop() error { + panic("implement me") +} + +func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { + panic("implement me") +} + +func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { + panic("implement me") +} + +func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { + panic("implement me") +} + +func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { + panic("implement me") +} + +func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { + panic("implement me") +} + +func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) { + panic("implement me") +} + func (self *SLoadbalancer) GetId() string { return self.LoadBalancerID } diff --git a/pkg/util/qcloud/loadbalancer_backendgroup.go b/pkg/util/qcloud/loadbalancer_backendgroup.go index 528977980d..afc3df92e0 100755 --- a/pkg/util/qcloud/loadbalancer_backendgroup.go +++ b/pkg/util/qcloud/loadbalancer_backendgroup.go @@ -36,7 +36,6 @@ func backendGroupIdGen(lbid string, secondId string) string { } else { return lbid } - } // http https 后端服务器只与规则绑定 diff --git a/pkg/util/qcloud/loadbalancer_certificate.go b/pkg/util/qcloud/loadbalancer_certificate.go index 055fa8f53c..76f4fe8b58 100755 --- a/pkg/util/qcloud/loadbalancer_certificate.go +++ b/pkg/util/qcloud/loadbalancer_certificate.go @@ -16,6 +16,14 @@ type SLBCertificate struct { SCertificate } +func (self *SLBCertificate) Sync(name, privateKey, publickKey string) error { + panic("implement me") +} + +func (self *SLBCertificate) Delete() error { + panic("implement me") +} + func (self *SLBCertificate) GetId() string { return self.ID } diff --git a/pkg/util/qcloud/region.go b/pkg/util/qcloud/region.go index 9ae3e5e29f..7e9d4d18df 100644 --- a/pkg/util/qcloud/region.go +++ b/pkg/util/qcloud/region.go @@ -33,6 +33,141 @@ type SRegion struct { fetchLocation bool } +// todo: 确认这个方法是不是有问题 +func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) { + certs, err := self.GetILoadBalancerCertificatesById(certId) + if err != nil { + return nil, err + } + + if len(certs) == 1 { + return certs[0], nil + } else { + return nil, fmt.Errorf("GetILoadBalancerCertificateById %d certificate found", len(certs)) + } +} + +func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) { + lbs, err := self.GetLoadbalancers(nil) + if err != nil { + return nil, err + } + + ilbs := make([]cloudprovider.ICloudLoadbalancer, len(lbs)) + for i := range lbs { + lbs[i].region = self + ilbs[i] = &lbs[i] + } + + return ilbs, nil +} + +// 腾讯云不支持acl +func (self *SRegion) GetILoadBalancerAcls() ([]cloudprovider.ICloudLoadbalancerAcl, error) { + return []cloudprovider.ICloudLoadbalancerAcl{}, nil +} + +func inList(lst []string, entry string) bool { + for _, m := range lst { + if entry == m { + return true + } + } + + return false +} + +func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) { + lbs, err := self.GetLoadbalancers(nil) + if err != nil { + return nil, err + } + + icerts := []cloudprovider.ICloudLoadbalancerCertificate{} + for _, lb := range lbs { + listeners, err := lb.GetLoadbalancerListeners("HTTPS") + if err != nil { + return nil, err + } + + certIds := []string{} + for _, listener := range listeners { + if len(listener.Certificate.CERTID) > 0 && inList(certIds, listener.Certificate.CERTID) { + certIds = append(certIds, listener.Certificate.CERTID) + } + + if len(listener.Certificate.CERTCAID) > 0 && inList(certIds, listener.Certificate.CERTCAID) { + certIds = append(certIds, listener.Certificate.CERTCAID) + } + + for _, rule := range listener.Rules { + if len(rule.Certificate.CERTID) > 0 && inList(certIds, rule.Certificate.CERTID) { + certIds = append(certIds, rule.Certificate.CERTID) + } + + if len(rule.Certificate.CERTCAID) > 0 && inList(certIds, rule.Certificate.CERTCAID) { + certIds = append(certIds, rule.Certificate.CERTCAID) + } + } + } + + for _, cid := range certIds { + parts, err := self.GetILoadBalancerCertificatesById(cid) + if err != nil { + return nil, err + } + + icerts = append(icerts, parts...) + } + } + + return icerts, nil +} + +func (self *SRegion) GetILoadBalancerCertificatesById(certId string) ([]cloudprovider.ICloudLoadbalancerCertificate, error) { + certs, _, err := self.GetCertificates(certId, true, 0, 0) + if err != nil { + return nil, err + } + + icerts := []cloudprovider.ICloudLoadbalancerCertificate{} + for i := 0; i < len(certs); i++ { + cert := SLBCertificate{region: self, SCertificate: certs[i]} + icerts = append(icerts, &cert) + } + return icerts, nil +} + +func (self *SRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) { + lbs, err := self.GetLoadbalancers([]string{loadbalancerId}) + if err != nil { + return nil, err + } + + if len(lbs) == 1 { + lbs[0].region = self + return &lbs[0], nil + } else { + return nil, fmt.Errorf("GetILoadBalancerById %d loadbalancer found", len(lbs)) + } +} + +func (self *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloudLoadbalancerAcl, error) { + return nil, nil +} + +func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) { + panic("implement me") +} + +func (self *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) { + panic("implement me") +} + +func (self *SRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) { + panic("implement me") +} + func (self *SRegion) GetId() string { return self.Region } @@ -600,39 +735,3 @@ func (self *SRegion) GetInstanceStatus(instanceId string) (string, error) { } return instance.InstanceState, nil } - -func (region *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloudLoadbalancerAcl, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) GetILoadBalancerAcls() ([]cloudprovider.ICloudLoadbalancerAcl, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) { - return nil, cloudprovider.ErrNotImplemented -} - -func (region *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) { - return nil, cloudprovider.ErrNotImplemented -} From 7d5b4c290adcfbd69d2aee0919bdaff8db3e1195 Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 31 Jan 2019 18:34:54 +0800 Subject: [PATCH 03/15] impement method fix --- pkg/util/qcloud/loadbalancer.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index 23cf141a49..b19debdfa1 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -52,11 +52,12 @@ type SLoadbalancer struct { } func (self *SLoadbalancer) GetLoadbalancerSpec() string { - panic("implement me") + return "" } +// todo: 实际是按小时计费 func (self *SLoadbalancer) GetChargeType() string { - panic("implement me") + return "traffic" } func (self *SLoadbalancer) Delete() error { From e7a9f10c10aa7aef0844bd97c3640cc5f7ca63d1 Mon Sep 17 00:00:00 2001 From: TangBin Date: Thu, 31 Jan 2019 18:50:47 +0800 Subject: [PATCH 04/15] impement method fix --- pkg/util/qcloud/loadbalancer.go | 38 ++++++++++++++++-------- pkg/util/qcloud/loadbalancer_listener.go | 16 ++++++++-- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index b19debdfa1..ab248109be 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -72,20 +72,23 @@ func (self *SLoadbalancer) Stop() error { panic("implement me") } -func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { - panic("implement me") -} - -func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { - panic("implement me") -} - func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { panic("implement me") } func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { - panic("implement me") + groups, err := self.GetILoadBalancerBackendGroups() + if err != nil { + return nil, err + } + + for _, group := range groups { + if group.GetId() == groupId { + return group, nil + } + } + + return nil, cloudprovider.ErrNotFound } func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { @@ -93,7 +96,18 @@ func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.S } func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) { - panic("implement me") + listeners, err := self.GetLoadbalancerListeners("") + if err != nil { + return nil, err + } + + for _, listener := range listeners { + if listener.GetId() == listenerId { + return &listener, nil + } + } + + return nil, cloudprovider.ErrNotFound } func (self *SLoadbalancer) GetId() string { @@ -189,7 +203,7 @@ func (self *SLoadbalancer) GetLoadbalancerListeners(protocal string) ([]SLBListe return listeners, nil } -func (self *SLoadbalancer) GetILoadbalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { +func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLoadbalancerListener, error) { listeners, err := self.GetLoadbalancerListeners("") if err != nil { return nil, err @@ -204,7 +218,7 @@ func (self *SLoadbalancer) GetILoadbalancerListeners() ([]cloudprovider.ICloudLo return ilisteners, nil } -func (self *SLoadbalancer) GetILoadbalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { +func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { listeners, err := self.GetLoadbalancerListeners("") if err != nil { return nil, err diff --git a/pkg/util/qcloud/loadbalancer_listener.go b/pkg/util/qcloud/loadbalancer_listener.go index 4235ba621a..28643fe64e 100755 --- a/pkg/util/qcloud/loadbalancer_listener.go +++ b/pkg/util/qcloud/loadbalancer_listener.go @@ -55,8 +55,9 @@ type SLBListener struct { ClassicListener bool // 这个字段是在qcloud返回字段基础上,额外增加的字段。用于区分listener 是否是classic。 } +// todo: 腾讯云后端端口不是与listener绑定的 func (self *SLBListener) GetBackendServerPort() int { - panic("implement me") + return 0 } func (self *SLBListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) { @@ -64,7 +65,18 @@ func (self *SLBListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLo } func (self *SLBListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudprovider.ICloudLoadbalancerListenerRule, error) { - panic("implement me") + rules, err := self.GetILoadbalancerListenerRules() + if err != nil { + return nil, err + } + + for _, rule := range rules { + if rule.GetId() == ruleId { + return rule, nil + } + } + + return nil, cloudprovider.ErrNotFound } func (self *SLBListener) Start() error { From 6ff18716e9f773e9245b88352c3b73ff8f705a27 Mon Sep 17 00:00:00 2001 From: TangBin Date: Fri, 1 Feb 2019 10:10:50 +0800 Subject: [PATCH 05/15] create init --- pkg/util/qcloud/loadbalancer.go | 76 ++++++++++++++++++-- pkg/util/qcloud/loadbalancer_backendgroup.go | 7 +- pkg/util/qcloud/loadbalancer_listener.go | 7 +- 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index ab248109be..36883a3a16 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -60,20 +60,29 @@ func (self *SLoadbalancer) GetChargeType() string { return "traffic" } +// https://cloud.tencent.com/document/product/214/30689 func (self *SLoadbalancer) Delete() error { - panic("implement me") + _, err := self.region.DeleteLoadbalancer(self.GetId()) + if err != nil { + return err + } + + return cloudprovider.WaitDeleted(self, 5*time.Second, 60*time.Second) } +// 腾讯云loadbalance不支持启用/禁用 func (self *SLoadbalancer) Start() error { - panic("implement me") + return cloudprovider.ErrNotSupported } func (self *SLoadbalancer) Stop() error { - panic("implement me") + return cloudprovider.ErrNotSupported } +// 腾讯云无后端服务器组 +// todo: 是否返回一个fake的后端服务器组 func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { - panic("implement me") + return nil, cloudprovider.ErrNotSupported } func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) { @@ -91,7 +100,9 @@ func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (clo return nil, cloudprovider.ErrNotFound } +// https://cloud.tencent.com/document/product/214/30693 func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { + panic("implement me") } @@ -304,9 +315,66 @@ func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) { } lbs, err := self.GetLoadbalancers([]string{id}) + if err == nil && len(lbs) == 0 { + return nil, cloudprovider.ErrNotFound + } + if err != nil && len(lbs) == 1 { return &lbs[0], nil } return nil, err } + +/* +返回requstid 用于异步任务查询 +https://cloud.tencent.com/document/product/214/30689 +*/ +func (self *SRegion) DeleteLoadbalancer(lbid string) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("loadbalancer id should not be empty") + } + + params := map[string]string{"LoadBalancerIds.0": lbid} + resp, err := self.clbRequest("DeleteLoadBalancer", params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} + +/* +https://cloud.tencent.com/document/product/214/30693 +SNI 特性是什么?? +todo: finish me +Ports.N 是 Array of Integer 要将监听器创建到哪些端口,每个端口对应一个新的监听器 +Protocol 是 String 监听器协议:HTTP | HTTPS | TCP | TCP_SSL +ListenerNames.N 否 Array of String 要创建的监听器名称列表,名称与Ports数组按序一一对应,如不需立即命名,则无需提供此参数 +HealthCheck 否 HealthCheck 健康检查相关参数,此参数仅适用于TCP/UDP/TCP_SSL监听器 +Certificate 否 CertificateInput 证书相关信息,此参数仅适用于HTTPS/TCP_SSL监听器 +SessionExpireTime 否 Integer 会话保持时间,单位:秒。可选值:30~3600,默认 0,表示不开启。此参数仅适用于TCP/UDP监听器。 +Scheduler 否 String 监听器转发的方式。可选值:WRR、LEAST_CONN分别表示按权重轮询、最小连接数, 默认为 WRR。此参数仅适用于TCP/UDP/TCP_SSL监听器。 +SniSwitch 否 Integer 是否开启SNI特性,此参数仅适用于HTTPS监听器。 +*/ +func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, port int) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("loadbalancer id should not be empty") + } + + params := map[string]string{ + "LoadBalancerId": lbid, + "Ports.0": strconv.Itoa(port), + "Protocol": protocol, + } + + if len(name) > 0 { + params["ListenerNames.0"] = name + } + resp, err := self.clbRequest("CreateListener", params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} diff --git a/pkg/util/qcloud/loadbalancer_backendgroup.go b/pkg/util/qcloud/loadbalancer_backendgroup.go index afc3df92e0..e6f3aacf90 100755 --- a/pkg/util/qcloud/loadbalancer_backendgroup.go +++ b/pkg/util/qcloud/loadbalancer_backendgroup.go @@ -14,16 +14,21 @@ type SLBBackendGroup struct { rule *SLBListenerRule // tcp、udp、tcp_ssl监听rule 为nil } +// https://cloud.tencent.com/document/product/214/30676 +// https://cloud.tencent.com/document/product/214/31789 func (self *SLBBackendGroup) AddBackendServer(serverId string, weight int, port int) (cloudprovider.ICloudLoadbalancerBackend, error) { panic("implement me") } +// https://cloud.tencent.com/document/product/214/30687 +// https://cloud.tencent.com/document/product/214/31794 func (self *SLBBackendGroup) RemoveBackendServer(serverId string, weight int, port int) error { panic("implement me") } +// 腾讯云无后端服务器组 func (self *SLBBackendGroup) Delete() error { - panic("implement me") + return cloudprovider.ErrNotSupported } func (self *SLBBackendGroup) Sync(name string) error { diff --git a/pkg/util/qcloud/loadbalancer_listener.go b/pkg/util/qcloud/loadbalancer_listener.go index 28643fe64e..6873fe3d3d 100755 --- a/pkg/util/qcloud/loadbalancer_listener.go +++ b/pkg/util/qcloud/loadbalancer_listener.go @@ -60,6 +60,8 @@ func (self *SLBListener) GetBackendServerPort() int { return 0 } +// https://cloud.tencent.com/document/product/214/30691 +// func (self *SLBListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) { panic("implement me") } @@ -80,13 +82,14 @@ func (self *SLBListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudp } func (self *SLBListener) Start() error { - panic("implement me") + return cloudprovider.ErrNotSupported } func (self *SLBListener) Stop() error { - panic("implement me") + return cloudprovider.ErrNotSupported } +// https://cloud.tencent.com/document/product/214/30677 func (self *SLBListener) Sync(listener *cloudprovider.SLoadbalancerListener) error { panic("implement me") } From 9010e9cbc147c9f825befe5a0615af8cfe02d53f Mon Sep 17 00:00:00 2001 From: TangBin Date: Fri, 1 Feb 2019 16:10:18 +0800 Subject: [PATCH 06/15] add loadbalancer operation method --- pkg/util/qcloud/loadbalancer.go | 177 +++++++++++++++++-- pkg/util/qcloud/loadbalancer_backend.go | 17 +- pkg/util/qcloud/loadbalancer_backendgroup.go | 108 ++++++++++- pkg/util/qcloud/loadbalancer_listener.go | 109 +++++++++++- pkg/util/qcloud/loadbalancer_listenerrule.go | 64 ++++++- pkg/util/qcloud/region.go | 43 ++++- 6 files changed, 488 insertions(+), 30 deletions(-) diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index 36883a3a16..66056cd69b 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -3,9 +3,11 @@ package qcloud import ( "fmt" "strconv" + "strings" "time" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" ) @@ -100,10 +102,79 @@ func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (clo return nil, cloudprovider.ErrNotFound } -// https://cloud.tencent.com/document/product/214/30693 -func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { +func onecloudHealthCodeToQcloud(codes string) int { + qcode := 0 + for i, code := range HTTP_CODES { + if strings.Contains(code, codes) { + c := 1 << uint(i) + qcode += c + } + } - panic("implement me") + return qcode +} + +// https://cloud.tencent.com/document/product/214/30693 +// Onecloud 不支持双向证书 +/* +todo: 限制比较多必须加参数校验 +HealthSwitch Integer 否 是否开启健康检查:1(开启)、0(关闭)。 +TimeOut Integer 否 健康检查的响应超时时间,可选值:2~60,默认值:2,单位:秒。响应超时时间要小于检查间隔时间。 +IntervalTime Integer 否 健康检查探测间隔时间,默认值:5,可选值:5~300,单位:秒。 +HealthNum Integer 否 健康阈值,默认值:3,表示当连续探测三次健康则表示该转发正常,可选值:2~10,单位:次。 +UnHealthNum Integer 否 不健康阈值,默认值:3,表示当连续探测三次不健康则表示该转发异常,可选值:2~10,单位:次。 +HttpCode Integer 否 健康检查状态码(仅适用于HTTP/HTTPS转发规则)。可选值:1~31,默认 31。 +1 表示探测后返回值 1xx 表示健康,2 表示返回 2xx 表示健康,4 表示返回 3xx 表示健康,8 表示返回 4xx 表示健康,16 表示返回 5xx 表示健康。若希望多种码都表示健康,则将相应的值相加。 +HttpCheckPath String 否 健康检查路径(仅适用于HTTP/HTTPS转发规则)。 +HttpCheckDomain String 否 健康检查域名(仅适用于HTTP/HTTPS转发规则)。 +HttpCheckMethod String 否 健康检查方法(仅适用于HTTP/HTTPS转发规则),取值为HEAD或GET。 + +SSLMode String 是 认证类型,UNIDIRECTIONAL:单向认证,MUTUAL:双向认证 +CertId String 否 服务端证书的 ID,如果不填写此项则必须上传证书,包括 CertContent,CertKey,CertName。 +CertCaId String 否 客户端证书的 ID,如果 SSLMode=mutual,监听器如果不填写此项则必须上传客户端证书,包括 CertCaContent,CertCaName。 +*/ +func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { + sniSwitch := 0 + var hc healthCheck + if listener.HealthCheck == models.LB_HEALTH_CHECK_ENABLE { + hc := healthCheck{ + HTTPCheckDomain: listener.HealthCheckDomain, + HealthSwitch: 1, + HTTPCheckPath: listener.HealthCheckURI, + HTTPCheckMethod: listener.HealthCheck, + UnHealthNum: listener.HealthCheckFail, + IntervalTime: listener.HealthCheckInterval, + HealthNum: listener.HealthCheckRise, + TimeOut: listener.HealthCheckTimeout, + } + + httpCode := onecloudHealthCodeToQcloud(listener.HealthCheckHttpCode) + if httpCode > 0 { + hc.HTTPCode = httpCode + } + } + + cert := certificate{ + SSLMode: "UNIDIRECTIONAL", + CERTCAID: listener.CertificateID, + CERTID: "", + } + + listenId, err := self.region.CreateLoadbalancerListener(self.GetId(), + listener.Name, + listener.ListenerType, + listener.ListenerPort, + &listener.Scheduler, + &listener.StickySessionCookieTimeout, + &sniSwitch, + &hc, + &cert) + + if err != nil { + return nil, err + } + + return self.GetILoadBalancerListenerById(listenId) } func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) { @@ -347,17 +418,8 @@ func (self *SRegion) DeleteLoadbalancer(lbid string) (string, error) { /* https://cloud.tencent.com/document/product/214/30693 SNI 特性是什么?? -todo: finish me -Ports.N 是 Array of Integer 要将监听器创建到哪些端口,每个端口对应一个新的监听器 -Protocol 是 String 监听器协议:HTTP | HTTPS | TCP | TCP_SSL -ListenerNames.N 否 Array of String 要创建的监听器名称列表,名称与Ports数组按序一一对应,如不需立即命名,则无需提供此参数 -HealthCheck 否 HealthCheck 健康检查相关参数,此参数仅适用于TCP/UDP/TCP_SSL监听器 -Certificate 否 CertificateInput 证书相关信息,此参数仅适用于HTTPS/TCP_SSL监听器 -SessionExpireTime 否 Integer 会话保持时间,单位:秒。可选值:30~3600,默认 0,表示不开启。此参数仅适用于TCP/UDP监听器。 -Scheduler 否 String 监听器转发的方式。可选值:WRR、LEAST_CONN分别表示按权重轮询、最小连接数, 默认为 WRR。此参数仅适用于TCP/UDP/TCP_SSL监听器。 -SniSwitch 否 Integer 是否开启SNI特性,此参数仅适用于HTTPS监听器。 */ -func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, port int) (string, error) { +func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, port int, scheduler *string, sessionExpireTime, sniSwitch *int, healthCheck *healthCheck, cert *certificate) (string, error) { if len(lbid) == 0 { return "", fmt.Errorf("loadbalancer id should not be empty") } @@ -371,10 +433,97 @@ func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, por if len(name) > 0 { params["ListenerNames.0"] = name } + + if sniSwitch != nil { + params["SniSwitch"] = strconv.Itoa(*sniSwitch) + } + + if sessionExpireTime != nil { + params["SessionExpireTime"] = strconv.Itoa(*sessionExpireTime) + } + + if scheduler != nil && len(*scheduler) > 0 { + params["Scheduler"] = *scheduler + } + + if healthCheck != nil { + params["HealthCheck.HealthSwitch"] = strconv.Itoa(healthCheck.HealthSwitch) + params["HealthCheck.TimeOut"] = strconv.Itoa(healthCheck.TimeOut) + params["HealthCheck.IntervalTime"] = strconv.Itoa(healthCheck.IntervalTime) + params["HealthCheck.HealthNum"] = strconv.Itoa(healthCheck.HealthNum) + params["HealthCheck.UnHealthNum"] = strconv.Itoa(healthCheck.UnHealthNum) + params["HealthCheck.HttpCode"] = strconv.Itoa(healthCheck.HTTPCode) + params["HealthCheck.HttpCheckPath"] = healthCheck.HTTPCheckPath + params["HealthCheck.HttpCheckDomain"] = healthCheck.HTTPCheckDomain + params["HealthCheck.HttpCheckMethod"] = healthCheck.HTTPCheckMethod + } + + if cert != nil { + params["Certificate.SSLMode"] = cert.SSLMode + params["Certificate.CertId"] = cert.CERTID + if len(cert.CERTCAID) > 0 { + params["Certificate.CertCaId"] = cert.CERTCAID + } + } + resp, err := self.clbRequest("CreateListener", params) if err != nil { return "", err } - return resp.GetString("RequestId") + listeners, err := resp.GetArray("ListenerIds") + if err != nil { + return "", err + } + + if len(listeners) == 0 { + return "", fmt.Errorf("CreateLoadbalancerListener no listener id returned: %s", resp.String()) + } else if len(listeners) == 1 { + return listeners[0].String(), nil + } else { + return "", fmt.Errorf("CreateLoadbalancerListener mutliple listener id returned: %s", resp.String()) + } +} + +// https://cloud.tencent.com/document/product/214/30683 +// 任务的当前状态。 0:成功,1:失败,2:进行中 +func (self *SRegion) GetLBTaskStatus(requestId string) (string, error) { + if len(requestId) == 0 { + return "", fmt.Errorf("WaitTaskSuccess requestId should not be emtpy") + } + + params := map[string]string{"TaskId": requestId} + resp, err := self.clbRequest("DescribeTaskStatus", params) + if err != nil { + return "", err + } + + status, err := resp.Get("Status") + if err != nil { + log.Debugf("WaitTaskSuccess failed %s: %s", err, resp.String()) + return "", err + } + + return status.String(), err +} + +func (self *SRegion) WaitLBTaskSuccess(requestId string, interval time.Duration, timeout time.Duration) error { + startTime := time.Now() + for time.Now().Sub(startTime) < timeout { + status, err := self.GetLBTaskStatus(requestId) + if err != nil { + return err + } + if status == "0" { + return nil + } + + if status == "1" { + return fmt.Errorf("Task %s failed.", requestId) + } + + time.Sleep(interval) + } + + return cloudprovider.ErrTimeout } diff --git a/pkg/util/qcloud/loadbalancer_backend.go b/pkg/util/qcloud/loadbalancer_backend.go index 24acfcbcc3..c14c8ed4f8 100755 --- a/pkg/util/qcloud/loadbalancer_backend.go +++ b/pkg/util/qcloud/loadbalancer_backend.go @@ -4,6 +4,7 @@ import ( "fmt" "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" ) @@ -56,7 +57,21 @@ func (self *SLBBackend) GetStatus() string { } func (self *SLBBackend) Refresh() error { - panic("implement me") + backends, err := self.group.GetBackends() + if err != nil { + return err + } + + for _, backend := range backends { + if backend.GetId() == self.GetId() { + err := jsonutils.Update(self, backend) + if err != nil { + return err + } + } + } + + return cloudprovider.ErrNotFound } func (self *SLBBackend) IsEmulated() bool { diff --git a/pkg/util/qcloud/loadbalancer_backendgroup.go b/pkg/util/qcloud/loadbalancer_backendgroup.go index e6f3aacf90..6888af82df 100755 --- a/pkg/util/qcloud/loadbalancer_backendgroup.go +++ b/pkg/util/qcloud/loadbalancer_backendgroup.go @@ -2,6 +2,8 @@ package qcloud import ( "fmt" + "strconv" + "time" "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -14,16 +16,100 @@ type SLBBackendGroup struct { rule *SLBListenerRule // tcp、udp、tcp_ssl监听rule 为nil } +// 返回requestid +func (self *SLBBackendGroup) appLBBackendServer(action string, serverId string, weight int, port int) (string, error) { + params := map[string]string{ + "LoadBalancerId": self.lb.GetId(), + "ListenerId": self.listener.GetId(), + "Targets.0.InstanceId": serverId, + "Targets.0.Port": strconv.Itoa(port), + "Targets.0.Weight": strconv.Itoa(weight), + } + + if self.rule != nil { + params["LocationId"] = self.rule.GetId() + } + + resp, err := self.lb.region.clbRequest(action, params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} + +// 返回requestid +func (self *SLBBackendGroup) classicLBBackendServer(action string, serverId string, weight int, port int) (string, error) { + // 传统型负载均衡忽略了port参数 + params := map[string]string{ + "LoadBalancerId": self.lb.GetId(), + "Targets.0.InstanceId": serverId, + "Targets.0.Weight": strconv.Itoa(weight), + } + + resp, err := self.lb.region.clbRequest(action, params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} + // https://cloud.tencent.com/document/product/214/30676 // https://cloud.tencent.com/document/product/214/31789 func (self *SLBBackendGroup) AddBackendServer(serverId string, weight int, port int) (cloudprovider.ICloudLoadbalancerBackend, error) { - panic("implement me") + var requestId string + var err error + if self.lb.Forward == LB_TYPE_APPLICATION { + requestId, err = self.appLBBackendServer("RegisterTargets", serverId, weight, port) + } else { + requestId, err = self.classicLBBackendServer("RegisterTargetsWithClassicalLB", serverId, weight, port) + } + + if err != nil { + return nil, err + } + + err = self.lb.region.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) + if err != nil { + return nil, err + } + + err = self.Refresh() + if err != nil { + return nil, err + } + + backends, err := self.GetBackends() + if err != nil { + return nil, err + } + + for _, backend := range backends { + if backend.GetBackendId() == serverId { + return &backend, nil + } + } + + return nil, cloudprovider.ErrNotFound } // https://cloud.tencent.com/document/product/214/30687 // https://cloud.tencent.com/document/product/214/31794 func (self *SLBBackendGroup) RemoveBackendServer(serverId string, weight int, port int) error { - panic("implement me") + var requestId string + var err error + if self.lb.Forward == LB_TYPE_APPLICATION { + requestId, err = self.appLBBackendServer("DeregisterTargets", serverId, weight, port) + } else { + requestId, err = self.classicLBBackendServer("DeregisterTargetsFromClassicalLB", serverId, weight, port) + } + + if err != nil { + return err + } + + return self.lb.region.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) } // 腾讯云无后端服务器组 @@ -91,6 +177,20 @@ func (self *SLBBackendGroup) GetType() string { } func (self *SLBBackendGroup) GetILoadbalancerBackends() ([]cloudprovider.ICloudLoadbalancerBackend, error) { + backends, err := self.GetBackends() + if err != nil { + return nil, err + } + + ibackends := make([]cloudprovider.ICloudLoadbalancerBackend, len(backends)) + for i := range backends { + ibackends[i] = &backends[i] + } + + return ibackends, nil +} + +func (self *SLBBackendGroup) GetBackends() ([]SLBBackend, error) { backends := []SLBBackend{} var err error if self.rule != nil { @@ -107,11 +207,9 @@ func (self *SLBBackendGroup) GetILoadbalancerBackends() ([]cloudprovider.ICloudL } } - ibackends := make([]cloudprovider.ICloudLoadbalancerBackend, len(backends)) for i := range backends { backends[i].group = self - ibackends[i] = &backends[i] } - return ibackends, nil + return backends, nil } diff --git a/pkg/util/qcloud/loadbalancer_listener.go b/pkg/util/qcloud/loadbalancer_listener.go index 6873fe3d3d..7302243a03 100755 --- a/pkg/util/qcloud/loadbalancer_listener.go +++ b/pkg/util/qcloud/loadbalancer_listener.go @@ -1,7 +1,10 @@ package qcloud import ( + "fmt" + "strconv" "strings" + "time" "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -29,7 +32,7 @@ type certificate struct { */ type healthCheck struct { HTTPCheckDomain string `json:"HttpCheckDomain"` - HealthSwitch int64 `json:"HealthSwitch"` + HealthSwitch int `json:"HealthSwitch"` HTTPCheckPath string `json:"HttpCheckPath"` HTTPCheckMethod string `json:"HttpCheckMethod"` UnHealthNum int `json:"UnHealthNum"` @@ -61,9 +64,35 @@ func (self *SLBListener) GetBackendServerPort() int { } // https://cloud.tencent.com/document/product/214/30691 -// +// todo: 调度规则原用监听器的Scheduler ok? https 协议怎么兼容? func (self *SLBListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) { - panic("implement me") + requestId, err := self.lb.region.CreateLoadbalancerListenerRule(self.lb.GetId(), + self.GetId(), + rule.Domain, + rule.Path, + &self.Scheduler, + &self.SessionExpireTime) + if err != nil { + return nil, err + } + + err = self.lb.region.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) + if err != nil { + return nil, err + } + + err = self.Refresh() + if err != nil { + return nil, err + } + + for _, r := range self.Rules { + if r.GetPath() == rule.Path { + return &r, nil + } + } + + return nil, cloudprovider.ErrNotFound } func (self *SLBListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudprovider.ICloudLoadbalancerListenerRule, error) { @@ -95,7 +124,12 @@ func (self *SLBListener) Sync(listener *cloudprovider.SLoadbalancerListener) err } func (self *SLBListener) Delete() error { - panic("implement me") + requestId, err := self.lb.region.DeleteLoadbalancerListener(self.lb.GetId(), self.GetId()) + if err != nil { + return err + } + + return self.lb.region.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) } // https://cloud.tencent.com/document/api/214/30694#ClassicalListener @@ -114,7 +148,7 @@ type SLBClassicListener struct { HTTPCheckPath string `json:"HttpCheckPath"` HealthNum int `json:"HealthNum"` ListenerName string `json:"ListenerName"` - HealthSwitch int64 `json:"HealthSwitch"` + HealthSwitch int `json:"HealthSwitch"` SSLMode string `json:"SSLMode"` SessionExpire int `json:"SessionExpire"` HTTPCode int `json:"HttpCode"` @@ -168,7 +202,22 @@ func (self *SLBListener) GetStatus() string { } func (self *SLBListener) Refresh() error { - panic("implement me") + listeners, err := self.lb.region.GetLoadbalancerListeners(self.lb.GetId(), self.lb.Forward, "") + if err != nil { + return err + } + + for _, listener := range listeners { + if listener.GetId() == self.GetId() { + listener.lb = self.lb + err := jsonutils.Update(self, listener) + if err != nil { + return err + } + } + } + + return cloudprovider.ErrNotFound } func (self *SLBListener) IsEmulated() bool { @@ -418,3 +467,51 @@ func (self *SRegion) GetLoadbalancerListeners(lbid string, t LB_TYPE, protocol s return listeners, nil } + +// 返回requestID +func (self *SRegion) CreateLoadbalancerListenerRule(lbid string, listenerId string, domain string, url string, scheduler *string, sessionExpireTime *int) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("loadbalancer id should not be empty") + } + + params := map[string]string{ + "LoadBalancerId": lbid, + "ListenerId": listenerId, + "Rules.0.Domain": domain, + "Rules.0.Url": url, + } + + if scheduler != nil && len(*scheduler) > 0 { + params["Rules.0.Scheduler"] = *scheduler + } + + if sessionExpireTime != nil { + params["Rules.0.SessionExpireTime"] = strconv.Itoa(*sessionExpireTime) + } + + resp, err := self.clbRequest("CreateRule", params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} + +// 返回requestID +func (self *SRegion) DeleteLoadbalancerListener(lbid string, listenerId string) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("loadbalancer id should not be empty") + } + + params := map[string]string{ + "LoadBalancerId": lbid, + "ListenerId": listenerId, + } + + resp, err := self.clbRequest("DeleteListener", params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} diff --git a/pkg/util/qcloud/loadbalancer_listenerrule.go b/pkg/util/qcloud/loadbalancer_listenerrule.go index bcce799477..4be8bf7b48 100755 --- a/pkg/util/qcloud/loadbalancer_listenerrule.go +++ b/pkg/util/qcloud/loadbalancer_listenerrule.go @@ -1,7 +1,11 @@ package qcloud import ( + "fmt" + "time" + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" ) @@ -17,8 +21,14 @@ type SLBListenerRule struct { SessionExpireTime int64 `json:"SessionExpireTime"` } +// https://cloud.tencent.com/document/api/214/30688 func (self *SLBListenerRule) Delete() error { - panic("implement me") + _, err := self.listener.lb.region.DeleteLBListenerRule(self.listener.lb.GetId(), self.listener.GetId(), self.GetId()) + if err != nil { + return err + } + + return cloudprovider.WaitDeleted(self, 5*time.Second, 60*time.Second) } func (self *SLBListenerRule) GetId() string { @@ -39,7 +49,22 @@ func (self *SLBListenerRule) GetStatus() string { } func (self *SLBListenerRule) Refresh() error { - return nil + err := self.listener.Refresh() + if err != nil { + return err + } + + for _, rule := range self.listener.Rules { + if rule.GetId() == self.GetId() { + rule.listener = self.listener + err := jsonutils.Update(self, rule) + if err != nil { + return err + } + } + } + + return cloudprovider.ErrNotFound } func (self *SLBListenerRule) IsEmulated() bool { @@ -80,3 +105,38 @@ func (self *SLBListenerRule) GetBackendGroupId() string { return bg.GetId() } + +// https://cloud.tencent.com/document/api/214/30688 +// 返回requestId及error +func (self *SRegion) DeleteLBListenerRule(lbid, listenerId, ruleId string) (string, error) { + if len(ruleId) == 0 { + return "", fmt.Errorf("DeleteLBListenerRule rule id should not be empty") + } + return self.DeleteLBListenerRules(lbid, listenerId, []string{ruleId}) +} + +func (self *SRegion) DeleteLBListenerRules(lbid, listenerId string, ruleIds []string) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("DeleteLBListenerRules loadbalancer id should not be empty") + } + + if len(listenerId) == 0 { + return "", fmt.Errorf("DeleteLBListenerRules listener id should not be empty") + } + + if len(ruleIds) == 0 { + return "", fmt.Errorf("DeleteLBListenerRules rule id should not be empty") + } + + params := map[string]string{"LoadBalancerId": lbid, "ListenerId": listenerId} + for i, ruleId := range ruleIds { + params[fmt.Sprintf("LocationIds.%d", i)] = ruleId + } + + resp, err := self.clbRequest("DeleteRule", params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} diff --git a/pkg/util/qcloud/region.go b/pkg/util/qcloud/region.go index 7e9d4d18df..1b8a6ace4c 100644 --- a/pkg/util/qcloud/region.go +++ b/pkg/util/qcloud/region.go @@ -2,6 +2,7 @@ package qcloud import ( "fmt" + "time" "github.com/nelsonken/cos-go-sdk-v5/cos" @@ -156,12 +157,50 @@ func (self *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloud return nil, nil } +// https://cloud.tencent.com/document/api/214/30692 +/* +todo: +Forward 否 Integer 负载均衡实例。1:应用型,0:传统型,默认为应用型负载均衡实例。 +ProjectId 否 Integer 负载均衡实例所属的项目 ID,可以通过 DescribeProject 接口获取。不填则属于默认项目。 +*/ func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) { - panic("implement me") + LoadBalancerType := "INTERNAL" + if loadbalancer.AddressType == "public" { + LoadBalancerType = "OPEN" + } + params := map[string]string{ + "LoadBalancerType": LoadBalancerType, + "LoadBalancerName": loadbalancer.Name, + "VpcId": loadbalancer.VpcID, // todo: vpc id ok?? + "SubnetId": loadbalancer.NetworkID, + } + + resp, err := self.clbRequest("CreateLoadBalancer", params) + if err != nil { + return nil, err + } + + requestId, err := resp.GetString("RequestId") + if err != nil { + return nil, err + } + + lbs, err := resp.GetArray("LoadBalancerIds") + if err != nil || len(lbs) != 1 { + log.Debugf("CreateILoadBalancer %s", resp.String()) + return nil, err + } + + err = self.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) + if err != nil { + return nil, err + } + + return self.GetLoadbalancer(lbs[0].String()) } func (self *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAccessControlList) (cloudprovider.ICloudLoadbalancerAcl, error) { - panic("implement me") + return nil, cloudprovider.ErrNotSupported } func (self *SRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) { From b460044f3a77c6b8918c8d9351cc740341757e6e Mon Sep 17 00:00:00 2001 From: TangBin Date: Fri, 1 Feb 2019 17:20:28 +0800 Subject: [PATCH 07/15] add loadbalancer operation method && qcloud GetINetwork fix --- pkg/compute/models/guests.go | 9 ++- pkg/util/qcloud/certificate.go | 62 +++++++++++++++++++++ pkg/util/qcloud/instancenic.go | 2 +- pkg/util/qcloud/loadbalancer_certificate.go | 2 +- pkg/util/qcloud/region.go | 14 ++++- 5 files changed, 85 insertions(+), 4 deletions(-) diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index e91d18c4c6..6c71fabb05 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -2115,7 +2115,14 @@ func getCloudNicNetwork(vnic cloudprovider.ICloudNic, host *SHost) (*SNetwork, e return nil, fmt.Errorf("Cannot find inetwork for vnics %s %s", vnic.GetMAC(), vnic.GetIP()) } else { // find network by IP - return host.getNetworkOfIPOnHost(vnic.GetIP()) + netInterfaces := host.GetNetInterfaces() + for _, netInterface := range netInterfaces { + network, err := netInterface.GetCandidateNetworkForIp(auth.AdminCredential(), ip) + if err == nil { + return network, nil + } + } + return nil, fmt.Errorf("cannot find network for ip %s", ip) } } localNetObj, err := NetworkManager.FetchByExternalId(vnet.GetGlobalId()) diff --git a/pkg/util/qcloud/certificate.go b/pkg/util/qcloud/certificate.go index 3a8e1e898d..d1eb8b9afc 100755 --- a/pkg/util/qcloud/certificate.go +++ b/pkg/util/qcloud/certificate.go @@ -1,7 +1,9 @@ package qcloud import ( + "fmt" "strconv" + "strings" "time" ) @@ -79,3 +81,63 @@ func (self *SRegion) GetCertificates(id string, withCert bool, limit int, page i return certs, int(total), nil } + +/* +cert 是 String 证书内容 +certType 是 String 证书类型(目前支持:CA为客户端证书,SVR为服务器证书) +key 否 String 证书私钥,certType=SVR时必填) +alias 否 String 证书备注 +*/ +// https://cloud.tencent.com/document/api/400/9078 +// 返回证书ID +func (self *SRegion) CreateCertificate(cert, certType, key, desc string) (string, error) { + + params := map[string]string{ + "cert": cert, + "certType": certType, + "alias": desc, + } + + if certType == "SVR" { + params["key"] = key + } + + resp, err := self.wssRequest("CertUpload", params) + if err != nil { + return "", err + } + + code, err := resp.Get("code") + if err != nil { + return "", err + } + + if code.String() != "0" { + return "", fmt.Errorf("CreateCertificate failed : %s", resp.String()) + } + + return resp.GetString("data", "id") +} + +func (self *SRegion) DeleteCertificate(id string) error { + if len(id) == 0 { + return fmt.Errorf("DelteCertificate certificate id should not be empty") + } + + params := map[string]string{"id": id} + resp, err := self.wssRequest("CertDelete", params) + if err != nil { + return err + } + + status, err := resp.GetString("codeDesc") + if err != nil { + return err + } + + if strings.ToLower(status) == "success" { + return nil + } else { + return fmt.Errorf("DelteCertificate failed: %s", resp.String()) + } +} diff --git a/pkg/util/qcloud/instancenic.go b/pkg/util/qcloud/instancenic.go index f9c71f4d27..5ac0522562 100644 --- a/pkg/util/qcloud/instancenic.go +++ b/pkg/util/qcloud/instancenic.go @@ -25,7 +25,7 @@ func (self *SInstanceNic) GetDriver() string { } func (self *SInstanceNic) GetINetwork() cloudprovider.ICloudNetwork { - networkId := self.instance.VirtualPrivateCloud.VpcId + networkId := self.instance.VirtualPrivateCloud.SubnetId wires, err := self.instance.host.GetIWires() if err != nil { return nil diff --git a/pkg/util/qcloud/loadbalancer_certificate.go b/pkg/util/qcloud/loadbalancer_certificate.go index 76f4fe8b58..d103b7e60a 100755 --- a/pkg/util/qcloud/loadbalancer_certificate.go +++ b/pkg/util/qcloud/loadbalancer_certificate.go @@ -21,7 +21,7 @@ func (self *SLBCertificate) Sync(name, privateKey, publickKey string) error { } func (self *SLBCertificate) Delete() error { - panic("implement me") + return self.region.DeleteCertificate(self.GetId()) } func (self *SLBCertificate) GetId() string { diff --git a/pkg/util/qcloud/region.go b/pkg/util/qcloud/region.go index 1b8a6ace4c..87c0a8d87f 100644 --- a/pkg/util/qcloud/region.go +++ b/pkg/util/qcloud/region.go @@ -203,8 +203,20 @@ func (self *SRegion) CreateILoadBalancerAcl(acl *cloudprovider.SLoadbalancerAcce return nil, cloudprovider.ErrNotSupported } +// todo:目前onecloud端只能指定服务器端证书。需要兼容客户端证书? func (self *SRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) { - panic("implement me") + certId, err := self.CreateCertificate(cert.Certificate, "SVR", cert.PrivateKey, cert.Name) + if err != nil { + return nil, err + } + + certs, _, err := self.GetCertificates(certId, false, 10, 0) + if len(certs) != 1 || err != nil { + log.Debugf("CreateILoadBalancerCertificate failed. %d certificate matched", len(certs)) + return nil, err + } + + return &SLBCertificate{region: self, SCertificate: certs[0]}, nil } func (self *SRegion) GetId() string { From 03e36c4c54881d478c41f3731efb66ad10622ab7 Mon Sep 17 00:00:00 2001 From: TangBin Date: Sat, 2 Feb 2019 13:17:32 +0800 Subject: [PATCH 08/15] getNetworkOfIPOnHost fix --- pkg/compute/models/guests.go | 9 +-------- pkg/compute/models/hosts.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 6c71fabb05..c6998f261c 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -2115,14 +2115,7 @@ func getCloudNicNetwork(vnic cloudprovider.ICloudNic, host *SHost) (*SNetwork, e return nil, fmt.Errorf("Cannot find inetwork for vnics %s %s", vnic.GetMAC(), vnic.GetIP()) } else { // find network by IP - netInterfaces := host.GetNetInterfaces() - for _, netInterface := range netInterfaces { - network, err := netInterface.GetCandidateNetworkForIp(auth.AdminCredential(), ip) - if err == nil { - return network, nil - } - } - return nil, fmt.Errorf("cannot find network for ip %s", ip) + return host.getNetworkOfIPOnHost(ip) } } localNetObj, err := NetworkManager.FetchByExternalId(vnet.GetGlobalId()) diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 727c07285f..35d1fb6841 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -1670,15 +1670,15 @@ func (self *SHost) SyncHostVMs(ctx context.Context, userCred mcclient.TokenCrede } func (self *SHost) getNetworkOfIPOnHost(ipAddr string) (*SNetwork, error) { - net, err := NetworkManager.GetNetworkOfIP(ipAddr, "", tristate.None) - if err != nil { - return nil, err + netInterfaces := self.GetNetInterfaces() + for _, netInterface := range netInterfaces { + network, err := netInterface.GetCandidateNetworkForIp(auth.AdminCredential(), ipAddr) + if err == nil { + return network, nil + } } - hw := self.getHostwireOfId(net.WireId) - if hw == nil { - return nil, fmt.Errorf("IP %s not reachable on this host", ipAddr) - } - return net, nil + + return nil, fmt.Errorf("IP %s not reachable on this host", ipAddr) } func (self *SHost) GetNetinterfaceWithIdAndCredential(netId string, userCred mcclient.TokenCredential, reserved bool) (*SNetInterface, *SNetwork) { From f42c97762b82d962418b8dded701851a3b0f658b Mon Sep 17 00:00:00 2001 From: TangBin Date: Sat, 2 Feb 2019 13:19:09 +0800 Subject: [PATCH 09/15] getNetworkOfIPOnHost fix --- pkg/compute/models/hosts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 35d1fb6841..6b634513d9 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -1673,7 +1673,7 @@ func (self *SHost) getNetworkOfIPOnHost(ipAddr string) (*SNetwork, error) { netInterfaces := self.GetNetInterfaces() for _, netInterface := range netInterfaces { network, err := netInterface.GetCandidateNetworkForIp(auth.AdminCredential(), ipAddr) - if err == nil { + if err == nil && network != nil { return network, nil } } From f287c8e091a1209175ad43d96ec629dd45c35d4f Mon Sep 17 00:00:00 2001 From: TangBin Date: Mon, 11 Feb 2019 18:57:02 +0800 Subject: [PATCH 10/15] add sync --- pkg/compute/models/loadbalancer_const.go | 1 + pkg/compute/models/loadbalancers.go | 1 + pkg/util/aliyun/loadbalancer.go | 4 +- pkg/util/qcloud/loadbalancer.go | 66 ++------ pkg/util/qcloud/loadbalancer_acl.go | 3 +- pkg/util/qcloud/loadbalancer_backend.go | 2 - pkg/util/qcloud/loadbalancer_backendgroup.go | 6 +- pkg/util/qcloud/loadbalancer_certificate.go | 6 +- pkg/util/qcloud/loadbalancer_listener.go | 157 ++++++++++++++++++- 9 files changed, 177 insertions(+), 69 deletions(-) diff --git a/pkg/compute/models/loadbalancer_const.go b/pkg/compute/models/loadbalancer_const.go index 5f75879e0d..e436637da5 100644 --- a/pkg/compute/models/loadbalancer_const.go +++ b/pkg/compute/models/loadbalancer_const.go @@ -195,6 +195,7 @@ const ( ) var LB_HEALTH_CHECK_HTTP_CODES = validators.NewChoices( + LB_HEALTH_CHECK_HTTP_CODE_1xx, LB_HEALTH_CHECK_HTTP_CODE_2xx, LB_HEALTH_CHECK_HTTP_CODE_3xx, LB_HEALTH_CHECK_HTTP_CODE_4xx, diff --git a/pkg/compute/models/loadbalancers.go b/pkg/compute/models/loadbalancers.go index da6ec63157..51adf578d8 100644 --- a/pkg/compute/models/loadbalancers.go +++ b/pkg/compute/models/loadbalancers.go @@ -39,6 +39,7 @@ func init() { const ( LB_CHARGE_TYPE_BY_TRAFFIC = "traffic" LB_CHARGE_TYPE_BY_BANDWIDTH = "bandwidth" + LB_CHARGE_TYPE_BY_HOUR = "hour" ) // TODO build errors on pkg/httperrors/errors.go diff --git a/pkg/util/aliyun/loadbalancer.go b/pkg/util/aliyun/loadbalancer.go index b1a0b974bd..e65fac4134 100644 --- a/pkg/util/aliyun/loadbalancer.go +++ b/pkg/util/aliyun/loadbalancer.go @@ -237,9 +237,9 @@ func (lb *SLoadbalancer) GetLoadbalancerSpec() string { func (lb *SLoadbalancer) GetChargeType() string { switch lb.InternetChargeType { case "paybybandwidth": - return "bandwidth" + return models.LB_CHARGE_TYPE_BY_BANDWIDTH case "paybytraffic": - return "traffic" + return models.LB_CHARGE_TYPE_BY_TRAFFIC } return "unknown" } diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index 66056cd69b..c4cf26b506 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -57,9 +57,8 @@ func (self *SLoadbalancer) GetLoadbalancerSpec() string { return "" } -// todo: 实际是按小时计费 func (self *SLoadbalancer) GetChargeType() string { - return "traffic" + return models.LB_CHARGE_TYPE_BY_HOUR } // https://cloud.tencent.com/document/product/214/30689 @@ -135,45 +134,24 @@ CertCaId String 否 客户端证书的 ID,如果 SSLMode=mutual,监听器如 */ func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { sniSwitch := 0 - var hc healthCheck - if listener.HealthCheck == models.LB_HEALTH_CHECK_ENABLE { - hc := healthCheck{ - HTTPCheckDomain: listener.HealthCheckDomain, - HealthSwitch: 1, - HTTPCheckPath: listener.HealthCheckURI, - HTTPCheckMethod: listener.HealthCheck, - UnHealthNum: listener.HealthCheckFail, - IntervalTime: listener.HealthCheckInterval, - HealthNum: listener.HealthCheckRise, - TimeOut: listener.HealthCheckTimeout, - } - - httpCode := onecloudHealthCodeToQcloud(listener.HealthCheckHttpCode) - if httpCode > 0 { - hc.HTTPCode = httpCode - } - } - - cert := certificate{ - SSLMode: "UNIDIRECTIONAL", - CERTCAID: listener.CertificateID, - CERTID: "", - } + hc := getHealthCheck(listener) + cert := getCertificate(listener) listenId, err := self.region.CreateLoadbalancerListener(self.GetId(), listener.Name, - listener.ListenerType, + getProtocol(listener), listener.ListenerPort, - &listener.Scheduler, + getScheduler(listener), &listener.StickySessionCookieTimeout, &sniSwitch, - &hc, - &cert) + hc, + cert) if err != nil { return nil, err } + time.Sleep(3*time.Second) return self.GetILoadBalancerListenerById(listenId) } @@ -273,7 +251,7 @@ func (self *SLoadbalancer) GetZoneId() string { } func (self *SLoadbalancer) GetLoadbalancerListeners(protocal string) ([]SLBListener, error) { - listeners, err := self.region.GetLoadbalancerListeners(self.GetId(), self.Forward, "") + listeners, err := self.region.GetLoadbalancerListeners(self.GetId(), self.Forward, protocal) if err != nil { return nil, err } @@ -446,25 +424,8 @@ func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, por params["Scheduler"] = *scheduler } - if healthCheck != nil { - params["HealthCheck.HealthSwitch"] = strconv.Itoa(healthCheck.HealthSwitch) - params["HealthCheck.TimeOut"] = strconv.Itoa(healthCheck.TimeOut) - params["HealthCheck.IntervalTime"] = strconv.Itoa(healthCheck.IntervalTime) - params["HealthCheck.HealthNum"] = strconv.Itoa(healthCheck.HealthNum) - params["HealthCheck.UnHealthNum"] = strconv.Itoa(healthCheck.UnHealthNum) - params["HealthCheck.HttpCode"] = strconv.Itoa(healthCheck.HTTPCode) - params["HealthCheck.HttpCheckPath"] = healthCheck.HTTPCheckPath - params["HealthCheck.HttpCheckDomain"] = healthCheck.HTTPCheckDomain - params["HealthCheck.HttpCheckMethod"] = healthCheck.HTTPCheckMethod - } - - if cert != nil { - params["Certificate.SSLMode"] = cert.SSLMode - params["Certificate.CertId"] = cert.CERTID - if len(cert.CERTCAID) > 0 { - params["Certificate.CertCaId"] = cert.CERTCAID - } - } + params = healthCheckParams(params, healthCheck) + params = certificateParams(params, cert) resp, err := self.clbRequest("CreateListener", params) if err != nil { @@ -479,7 +440,7 @@ func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, por if len(listeners) == 0 { return "", fmt.Errorf("CreateLoadbalancerListener no listener id returned: %s", resp.String()) } else if len(listeners) == 1 { - return listeners[0].String(), nil + return listeners[0].GetString() } else { return "", fmt.Errorf("CreateLoadbalancerListener mutliple listener id returned: %s", resp.String()) } @@ -504,7 +465,8 @@ func (self *SRegion) GetLBTaskStatus(requestId string) (string, error) { return "", err } - return status.String(), err + _status, err := status.Float() + return fmt.Sprintf("%1.f", _status), err } func (self *SRegion) WaitLBTaskSuccess(requestId string, interval time.Duration, timeout time.Duration) error { diff --git a/pkg/util/qcloud/loadbalancer_acl.go b/pkg/util/qcloud/loadbalancer_acl.go index aaa936ea53..71a874b25d 100755 --- a/pkg/util/qcloud/loadbalancer_acl.go +++ b/pkg/util/qcloud/loadbalancer_acl.go @@ -2,6 +2,7 @@ package qcloud import ( "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/compute/models" ) // todo: 腾讯云没有LB ACL @@ -20,7 +21,7 @@ func (self *SLBACL) GetGlobalId() string { } func (self *SLBACL) GetStatus() string { - return "" + return models.LB_BOOL_OFF } func (self *SLBACL) Refresh() error { diff --git a/pkg/util/qcloud/loadbalancer_backend.go b/pkg/util/qcloud/loadbalancer_backend.go index c14c8ed4f8..db42298705 100755 --- a/pkg/util/qcloud/loadbalancer_backend.go +++ b/pkg/util/qcloud/loadbalancer_backend.go @@ -51,7 +51,6 @@ func (self *SLBBackend) GetGlobalId() string { return self.GetId() } -// todo: status ?? func (self *SLBBackend) GetStatus() string { return "" } @@ -90,7 +89,6 @@ func (self *SLBBackend) GetPort() int { return self.Port } -// todo: self.Type ?? func (self *SLBBackend) GetBackendType() string { return models.LB_BACKEND_GUEST } diff --git a/pkg/util/qcloud/loadbalancer_backendgroup.go b/pkg/util/qcloud/loadbalancer_backendgroup.go index 6888af82df..cb3b518bf3 100755 --- a/pkg/util/qcloud/loadbalancer_backendgroup.go +++ b/pkg/util/qcloud/loadbalancer_backendgroup.go @@ -112,13 +112,14 @@ func (self *SLBBackendGroup) RemoveBackendServer(serverId string, weight int, po return self.lb.region.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) } -// 腾讯云无后端服务器组 +// 腾讯云无后端服务器组。不支持该操作 func (self *SLBBackendGroup) Delete() error { return cloudprovider.ErrNotSupported } +// 腾讯云无后端服务器组 func (self *SLBBackendGroup) Sync(name string) error { - panic("implement me") + return nil } func backendGroupIdGen(lbid string, secondId string) string { @@ -171,7 +172,6 @@ func (self *SLBBackendGroup) IsDefault() bool { return false } -// todo: ?? func (self *SLBBackendGroup) GetType() string { return models.LB_BACKENDGROUP_TYPE_NORMAL } diff --git a/pkg/util/qcloud/loadbalancer_certificate.go b/pkg/util/qcloud/loadbalancer_certificate.go index d103b7e60a..06caebc9a8 100755 --- a/pkg/util/qcloud/loadbalancer_certificate.go +++ b/pkg/util/qcloud/loadbalancer_certificate.go @@ -16,8 +16,9 @@ type SLBCertificate struct { SCertificate } +// 证书不能修改 func (self *SLBCertificate) Sync(name, privateKey, publickKey string) error { - panic("implement me") + return nil } func (self *SLBCertificate) Delete() error { @@ -28,7 +29,6 @@ func (self *SLBCertificate) GetId() string { return self.ID } -// todo: ?? func (self *SLBCertificate) GetName() string { return self.Alias } @@ -64,7 +64,6 @@ func (self *SLBCertificate) GetMetadata() *jsonutils.JSONDict { return nil } -// todo: ?? func (self *SLBCertificate) GetCommonName() string { return self.Domain } @@ -73,7 +72,6 @@ func (self *SLBCertificate) GetSubjectAlternativeNames() string { return strings.Join(self.SubjectAltName, ",") } -// todo: ?? func (self *SLBCertificate) GetFingerprint() string { _fp := sha1.Sum([]byte(self.Cert)) fp := fmt.Sprintf("sha1:% x", _fp) diff --git a/pkg/util/qcloud/loadbalancer_listener.go b/pkg/util/qcloud/loadbalancer_listener.go index 7302243a03..c57f213aaf 100755 --- a/pkg/util/qcloud/loadbalancer_listener.go +++ b/pkg/util/qcloud/loadbalancer_listener.go @@ -111,7 +111,7 @@ func (self *SLBListener) GetILoadBalancerListenerRuleById(ruleId string) (cloudp } func (self *SLBListener) Start() error { - return cloudprovider.ErrNotSupported + return nil } func (self *SLBListener) Stop() error { @@ -120,7 +120,21 @@ func (self *SLBListener) Stop() error { // https://cloud.tencent.com/document/product/214/30677 func (self *SLBListener) Sync(listener *cloudprovider.SLoadbalancerListener) error { - panic("implement me") + hc := getHealthCheck(listener) + cert := getCertificate(listener) + requestId, err := self.lb.region.UpdateLoadbalancerListener( + self.lb.GetId(), + self.GetId(), + &listener.Name, + getScheduler(listener), + &listener.StickySessionCookieTimeout, + hc, + cert) + if err != nil { + return err + } + + return self.lb.region.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) } func (self *SLBListener) Delete() error { @@ -263,7 +277,7 @@ func (self *SLBListener) GetScheduler() string { } func (self *SLBListener) GetAclStatus() string { - return "" + return models.LB_BOOL_OFF } func (self *SLBListener) GetAclType() string { @@ -365,7 +379,6 @@ func (self *SLBListener) GetILoadbalancerListenerRules() ([]cloudprovider.ICloud return iRules, nil } -// todo: ?? func (self *SLBListener) GetStickySession() string { if self.SessionExpireTime == 0 { return models.LB_BOOL_OFF @@ -423,7 +436,6 @@ func (self *SLBListener) GetTLSCipherPolicy() string { return "" } -// todo: ? // 负载均衡能力说明 https://cloud.tencent.com/document/product/214/6534 func (self *SLBListener) HTTP2Enabled() bool { return true @@ -515,3 +527,138 @@ func (self *SRegion) DeleteLoadbalancerListener(lbid string, listenerId string) return resp.GetString("RequestId") } + +// https://cloud.tencent.com/document/product/214/30681 +func (self *SRegion) UpdateLoadbalancerListener(lbid string, listenerId string,listenerName *string, scheduler *string, sessionExpireTime *int, healthCheck *healthCheck,cert *certificate) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("loadbalancer id should not be empty") + } + + if len(listenerId) == 0 { + return "", fmt.Errorf("loadbalancer listener id should not be empty") + } + + params := map[string]string{ + "LoadBalancerId": lbid, + "ListenerId": listenerId, + } + + if listenerName != nil && len(*listenerName) > 0 { + params["ListenerName"] = *listenerName + } + + if scheduler != nil && len(*scheduler) > 0 { + params["Scheduler"] = *scheduler + } + + if sessionExpireTime != nil { + params["SessionExpireTime"] = strconv.Itoa(*sessionExpireTime) + } + + params = healthCheckParams(params, healthCheck) + params = certificateParams(params, cert) + + resp, err := self.clbRequest("ModifyListener", params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} + +func getHealthCheck(listener *cloudprovider.SLoadbalancerListener) *healthCheck { + var hc *healthCheck + if listener.HealthCheck == models.LB_HEALTH_CHECK_ENABLE { + hc = &healthCheck{ + HealthSwitch: 1, + UnHealthNum: listener.HealthCheckFail, + IntervalTime: listener.HealthCheckInterval, + HealthNum: listener.HealthCheckRise, + TimeOut: listener.HealthCheckTimeout, + } + + httpCode := onecloudHealthCodeToQcloud(listener.HealthCheckHttpCode) + if httpCode > 0 { + hc.HTTPCode = httpCode + hc.HTTPCheckMethod = "HEAD" // todo: add column HttpCheckMethod in model + hc.HTTPCheckDomain = listener.HealthCheckDomain + hc.HTTPCheckPath = listener.HealthCheckURI + } + } + + return hc +} + +func getCertificate(listener *cloudprovider.SLoadbalancerListener) *certificate { + var cert *certificate + if len(listener.CertificateID) > 0 { + cert = &certificate{ + SSLMode: "UNIDIRECTIONAL", + CERTCAID: listener.CertificateID, + CERTID: "", + } + } + + return cert +} + +func getProtocol(listener *cloudprovider.SLoadbalancerListener) string { + switch listener.ListenerType { + case models.LB_LISTENER_TYPE_HTTPS: + return "HTTPS" + case models.LB_LISTENER_TYPE_HTTP: + return "HTTP" + case models.LB_LISTENER_TYPE_TCP: + return "TCP" + case models.LB_LISTENER_TYPE_UDP: + return "UDP" + case "tcp_ssl": + return "TCP_SSL" + default: + return "" + } +} + +func getScheduler(listener *cloudprovider.SLoadbalancerListener) *string { + var sch string + switch listener.Scheduler { + case models.LB_SCHEDULER_WRR: + sch = "WRR" + case models.LB_SCHEDULER_WLC: + sch = "LEAST_CONN" + default: + return nil + } + + return &sch +} + +func healthCheckParams(params map[string]string,hc *healthCheck) map[string]string { + if hc != nil { + params["HealthCheck.HealthSwitch"] = strconv.Itoa(hc.HealthSwitch) + params["HealthCheck.TimeOut"] = strconv.Itoa(hc.TimeOut) + params["HealthCheck.IntervalTime"] = strconv.Itoa(hc.IntervalTime) + params["HealthCheck.HealthNum"] = strconv.Itoa(hc.HealthNum) + params["HealthCheck.UnHealthNum"] = strconv.Itoa(hc.UnHealthNum) + if hc.HTTPCode > 0 { + params["HealthCheck.HttpCode"] = strconv.Itoa(hc.HTTPCode) + params["HealthCheck.HttpCheckPath"] = hc.HTTPCheckPath + params["HealthCheck.HttpCheckDomain"] = hc.HTTPCheckDomain + params["HealthCheck.HttpCheckMethod"] = hc.HTTPCheckMethod + } + } + + return params +} + +func certificateParams(params map[string]string,cert *certificate) map[string]string { + if cert != nil { + params["Certificate.SSLMode"] = cert.SSLMode + params["Certificate.CertId"] = cert.CERTID + if len(cert.CERTCAID) > 0 { + params["Certificate.CertCaId"] = cert.CERTCAID + } + } + + return params +} \ No newline at end of file From 8bf555851d5c2568024ddc79ee6ff43ae06a0089 Mon Sep 17 00:00:00 2001 From: TangBin Date: Tue, 12 Feb 2019 20:02:05 +0800 Subject: [PATCH 11/15] add sync --- .../models/loadbalancerbackendgroups.go | 7 ++ pkg/compute/models/loadbalancerbackends.go | 12 +++ .../models/loadbalancerlistenerrules.go | 25 +++++++ pkg/compute/models/loadbalancerlisteners.go | 25 +++++++ pkg/compute/models/loadbalancers.go | 2 +- pkg/compute/regiondrivers/managedvirtual.go | 75 ++++++++++++++++++- pkg/util/qcloud/loadbalancer.go | 8 +- pkg/util/qcloud/loadbalancer_backendgroup.go | 6 +- pkg/util/qcloud/loadbalancer_listener.go | 24 +++--- 9 files changed, 161 insertions(+), 23 deletions(-) diff --git a/pkg/compute/models/loadbalancerbackendgroups.go b/pkg/compute/models/loadbalancerbackendgroups.go index a7e7ace419..06c97aba3e 100644 --- a/pkg/compute/models/loadbalancerbackendgroups.go +++ b/pkg/compute/models/loadbalancerbackendgroups.go @@ -177,6 +177,13 @@ func (lbbg *SLoadbalancerBackendGroup) GetIRegion() (cloudprovider.ICloudRegion, return nil, fmt.Errorf("failed to find loadbalancer for backendgroup %s", lbbg.Name) } +func (lbbg *SLoadbalancerBackendGroup) GetBackends() ([]SLoadbalancerBackend, error) { + backends := make([]SLoadbalancerBackend, 0) + q := LoadbalancerBackendManager.Query() + err := q.Equals("backend_group_id", lbbg.GetId()).All(&backends) + return backends, err +} + func (lbbg *SLoadbalancerBackendGroup) AllowPerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return false } diff --git a/pkg/compute/models/loadbalancerbackends.go b/pkg/compute/models/loadbalancerbackends.go index 291b30f4b0..abaa5769b5 100644 --- a/pkg/compute/models/loadbalancerbackends.go +++ b/pkg/compute/models/loadbalancerbackends.go @@ -360,6 +360,18 @@ func (lbb *SLoadbalancerBackend) SyncWithCloudLoadbalancerBackend(ctx context.Co return err } +func (lbb *SLoadbalancerBackend) UpdateCloudLoadbalancerBackendExternalId(ctx context.Context, userCred mcclient.TokenCredential,externalId string, projectId string, projectSync bool) error { + _, err := lbb.GetModelManager().TableSpec().Update(lbb, func() error { + if projectSync && len(projectId) > 0 { + lbb.ProjectId = projectId + } + + lbb.ExternalId = externalId + return nil + }) + return err +} + func (man *SLoadbalancerBackendManager) newFromCloudLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, loadbalancerBackendgroup *SLoadbalancerBackendGroup, extLoadbalancerBackend cloudprovider.ICloudLoadbalancerBackend, projectId string) (*SLoadbalancerBackend, error) { lbb := &SLoadbalancerBackend{} lbb.SetModelManager(man) diff --git a/pkg/compute/models/loadbalancerlistenerrules.go b/pkg/compute/models/loadbalancerlistenerrules.go index 2c3cb6ef66..0ef85d6269 100644 --- a/pkg/compute/models/loadbalancerlistenerrules.go +++ b/pkg/compute/models/loadbalancerlistenerrules.go @@ -120,6 +120,15 @@ func (man *SLoadbalancerListenerRuleManager) ValidateCreateData(ctx context.Cont if backendGroup, ok := backendGroupV.Model.(*SLoadbalancerBackendGroup); ok && backendGroup.LoadbalancerId != listener.LoadbalancerId { return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", backendGroup.Name, backendGroup.Id, backendGroup.LoadbalancerId, listener.LoadbalancerId) + } else { + // 腾讯云backend group只能1v1关联 + if listener.GetProviderName() == CLOUD_PROVIDER_QCLOUD { + count := LoadbalancerListenerRuleManager.TableSpec().Query().Equals("backend_group_id", backendGroup.GetId()).Count() + count += man.TableSpec().Query().Equals("backend_group_id", backendGroup.GetId()).Count() + if count > 0 { + return nil, fmt.Errorf("backendgroup aready related with other listener/rule") + } + } } } err := loadbalancerListenerRuleCheckUniqueness(ctx, listener, domainV.Value, pathV.Value) @@ -335,6 +344,22 @@ func (lbr *SLoadbalancerListenerRule) constructFieldsFromCloudListenerRule(extRu lbr.Domain = extRule.GetDomain() lbr.Path = extRule.GetPath() if groupId := extRule.GetBackendGroupId(); len(groupId) > 0 { + // 腾讯云兼容代码。主要目的是在关联listener rule时回写一个fake的backend group external id + if len(groupId) > 0 && len(lbr.BackendGroupId) > 0 { + igroup, err := LoadbalancerBackendGroupManager.FetchById(lbr.BackendGroupId) + group := igroup.(*SLoadbalancerBackendGroup) + if err == nil && (len(group.ExternalId) == 0 || group.ExternalId != groupId) { + _, err := LoadbalancerBackendGroupManager.TableSpec().Update(group, func() error { + group.ExternalId = groupId + return nil + }) + if err != nil { + log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s",group.GetId(), err) + } + } + } + // =========== + if backendgroup, err := LoadbalancerBackendGroupManager.FetchByExternalId(groupId); err == nil { lbr.BackendGroupId = backendgroup.GetId() } diff --git a/pkg/compute/models/loadbalancerlisteners.go b/pkg/compute/models/loadbalancerlisteners.go index 0d3e42168f..8c53b9fd9a 100644 --- a/pkg/compute/models/loadbalancerlisteners.go +++ b/pkg/compute/models/loadbalancerlisteners.go @@ -224,6 +224,15 @@ func (man *SLoadbalancerListenerManager) ValidateCreateData(ctx context.Context, if backendGroup, ok := backendGroupV.Model.(*SLoadbalancerBackendGroup); ok && backendGroup.LoadbalancerId != lb.Id { return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", backendGroup.Name, backendGroup.Id, backendGroup.LoadbalancerId, lb.Id) + } else { + // 腾讯云backend group只能1v1关联 + if lb.GetProviderName() == CLOUD_PROVIDER_QCLOUD { + count := LoadbalancerListenerRuleManager.TableSpec().Query().Equals("backend_group_id", backendGroup.GetId()).Count() + count += man.TableSpec().Query().Equals("backend_group_id", backendGroup.GetId()).Count() + if count > 0 { + return nil, fmt.Errorf("backendgroup aready related with other listener/rule") + } + } } } { @@ -766,6 +775,22 @@ func (lblis *SLoadbalancerListener) constructFieldsFromCloudListener(lb *SLoadba lblis.Gzip = extListener.GzipEnabled() } groupId := extListener.GetBackendGroupId() + // 腾讯云兼容代码。主要目的是在关联listen时回写一个fake的backend group external id + if len(groupId) > 0 && len(lblis.BackendGroupId) > 0 { + igroup, err := LoadbalancerBackendGroupManager.FetchById(lblis.BackendGroupId) + group := igroup.(*SLoadbalancerBackendGroup) + if err == nil && (len(group.ExternalId) == 0 || group.ExternalId != groupId) { + _, err := LoadbalancerBackendGroupManager.TableSpec().Update(group, func() error { + group.ExternalId = groupId + return nil + }) + if err != nil { + log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s", group.GetId(), err) + } + } + } + // =========== + if len(groupId) == 0 { lblis.BackendGroupId = lb.BackendGroupId } else if group, err := LoadbalancerBackendGroupManager.FetchByExternalId(groupId); err == nil { diff --git a/pkg/compute/models/loadbalancers.go b/pkg/compute/models/loadbalancers.go index 51adf578d8..e8f20555c3 100644 --- a/pkg/compute/models/loadbalancers.go +++ b/pkg/compute/models/loadbalancers.go @@ -39,7 +39,7 @@ func init() { const ( LB_CHARGE_TYPE_BY_TRAFFIC = "traffic" LB_CHARGE_TYPE_BY_BANDWIDTH = "bandwidth" - LB_CHARGE_TYPE_BY_HOUR = "hour" + LB_CHARGE_TYPE_BY_HOUR = "hour" ) // TODO build errors on pkg/httperrors/errors.go diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index ab721fa324..9735ab3211 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -332,6 +332,13 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend if err != nil { return nil, err } + + // ========= 腾讯云本身没有后端服务器组,因此不需要在qcloud端执行创建操作 ========= + if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { + return nil, nil + } + // ============================================ + loadbalancer := lbbg.GetLoadbalancer() if loadbalancer == nil { return nil, fmt.Errorf("failed to find loadbalancer for backendgroup %s", lbbg.Name) @@ -403,6 +410,19 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend if lbbg == nil { return nil, fmt.Errorf("failed to find lbbg for backend %s", lbb.Name) } + + guest := lbb.GetGuest() + if guest == nil { + return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name) + } + + // ==========兼容腾讯云,在fake的backend group 关联具体的转发策略之前。不需要同步服务器========== + // 联具体的转发策略之前ExternalId为空 + if len(lbbg.ExternalId) == 0 { + return nil, lbb.UpdateCloudLoadbalancerBackendExternalId(ctx, userCred, guest.ExternalId, "", false) + } + // ============q + lb := lbbg.GetLoadbalancer() if lb == nil { return nil, fmt.Errorf("failed to find lb for backendgroup %s", lbbg.Name) @@ -419,10 +439,6 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend if err != nil { return nil, err } - guest := lbb.GetGuest() - if guest == nil { - return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name) - } iLoadbalancerBackend, err := iLoadbalancerBackendGroup.AddBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) if err != nil { return nil, err @@ -494,6 +510,35 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerListene if err := lblis.SetExternalId(iListener.GetGlobalId()); err != nil { return nil, err } + + // ====腾讯云添加后端服务器===== + group := lblis.GetLoadbalancerBackendGroup() + if group != nil { + backends, err := group.GetBackends() + if err != nil { + return nil, fmt.Errorf("failed to find backends for backend group %s: %s", group.GetId(), err) + } + + extBgID := iListener.GetBackendGroupId() + if len(extBgID) == 0 { + return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s", lblis.GetId()) + } + + ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(extBgID) + if err != nil { + return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s: %s", lblis.GetId(), err) + } + + for _, backend := range backends { + _, err := ilbbg.AddBackendServer(backend.ExternalId, backend.Weight, backend.Port) + if err != nil { + return nil, err + } + } + + } + // ========================== + return nil, lblis.SyncWithCloudLoadbalancerListener(ctx, userCred, loadbalancer, iListener, "", false) }) return nil @@ -676,6 +721,28 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerListene if err := lbr.SetExternalId(iListenerRule.GetGlobalId()); err != nil { return nil, err } + // ====腾讯云添加后端服务器===== + if len(rule.BackendGroupID) > 0 { + ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(rule.BackendGroupID) + if err != nil { + return nil, fmt.Errorf("failed to find backend group for listener rule %s: %s", lbr.Name, err) + } + + group := lbr.GetLoadbalancerBackendGroup() + backends, err := group.GetBackends() + if err != nil { + return nil, fmt.Errorf("failed to find backends for backend group %s: %s", group.GetId(), err) + } + + for _, backend := range backends { + _, err := ilbbg.AddBackendServer(backend.ExternalId, backend.Weight, backend.Port) + if err != nil { + return nil, err + } + } + + } + // ========================== return nil, lbr.SyncWithCloudLoadbalancerListenerRule(ctx, userCred, iListenerRule, "", false) }) return nil diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index c4cf26b506..9362593ccf 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -26,10 +26,8 @@ const ( /* 目前存在的问题: -1.HTTP\HTTPS监听调度算法没同步成功 -2.后端服务器端口权重没同步成功 -3.后端服务器组待确认 - +1.HTTP\HTTPS监听调度算法没同步成功,原因是http、https监听并不关联转发策略,因此本身就没有调度算法 +2.不能创建后端服务器组。原因是没办法找到关联的listener及生成external id */ // https://cloud.tencent.com/document/api/214/30694#LoadBalancer type SLoadbalancer struct { @@ -151,7 +149,7 @@ func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.S return nil, err } - time.Sleep(3*time.Second) + time.Sleep(3 * time.Second) return self.GetILoadBalancerListenerById(listenId) } diff --git a/pkg/util/qcloud/loadbalancer_backendgroup.go b/pkg/util/qcloud/loadbalancer_backendgroup.go index cb3b518bf3..819bc33238 100755 --- a/pkg/util/qcloud/loadbalancer_backendgroup.go +++ b/pkg/util/qcloud/loadbalancer_backendgroup.go @@ -18,6 +18,10 @@ type SLBBackendGroup struct { // 返回requestid func (self *SLBBackendGroup) appLBBackendServer(action string, serverId string, weight int, port int) (string, error) { + if len(serverId) == 0 { + return "", fmt.Errorf("loadbalancer backend instance id should not be empty.") + } + params := map[string]string{ "LoadBalancerId": self.lb.GetId(), "ListenerId": self.listener.GetId(), @@ -152,7 +156,7 @@ func (self *SLBBackendGroup) GetGlobalId() string { } func (self *SLBBackendGroup) GetStatus() string { - return "" + return models.LB_STATUS_ENABLED } func (self *SLBBackendGroup) Refresh() error { diff --git a/pkg/util/qcloud/loadbalancer_listener.go b/pkg/util/qcloud/loadbalancer_listener.go index c57f213aaf..5cf051a414 100755 --- a/pkg/util/qcloud/loadbalancer_listener.go +++ b/pkg/util/qcloud/loadbalancer_listener.go @@ -529,7 +529,7 @@ func (self *SRegion) DeleteLoadbalancerListener(lbid string, listenerId string) } // https://cloud.tencent.com/document/product/214/30681 -func (self *SRegion) UpdateLoadbalancerListener(lbid string, listenerId string,listenerName *string, scheduler *string, sessionExpireTime *int, healthCheck *healthCheck,cert *certificate) (string, error) { +func (self *SRegion) UpdateLoadbalancerListener(lbid string, listenerId string, listenerName *string, scheduler *string, sessionExpireTime *int, healthCheck *healthCheck, cert *certificate) (string, error) { if len(lbid) == 0 { return "", fmt.Errorf("loadbalancer id should not be empty") } @@ -570,19 +570,19 @@ func getHealthCheck(listener *cloudprovider.SLoadbalancerListener) *healthCheck var hc *healthCheck if listener.HealthCheck == models.LB_HEALTH_CHECK_ENABLE { hc = &healthCheck{ - HealthSwitch: 1, - UnHealthNum: listener.HealthCheckFail, - IntervalTime: listener.HealthCheckInterval, - HealthNum: listener.HealthCheckRise, - TimeOut: listener.HealthCheckTimeout, + HealthSwitch: 1, + UnHealthNum: listener.HealthCheckFail, + IntervalTime: listener.HealthCheckInterval, + HealthNum: listener.HealthCheckRise, + TimeOut: listener.HealthCheckTimeout, } httpCode := onecloudHealthCodeToQcloud(listener.HealthCheckHttpCode) if httpCode > 0 { hc.HTTPCode = httpCode - hc.HTTPCheckMethod = "HEAD" // todo: add column HttpCheckMethod in model - hc.HTTPCheckDomain = listener.HealthCheckDomain - hc.HTTPCheckPath = listener.HealthCheckURI + hc.HTTPCheckMethod = "HEAD" // todo: add column HttpCheckMethod in model + hc.HTTPCheckDomain = listener.HealthCheckDomain + hc.HTTPCheckPath = listener.HealthCheckURI } } @@ -633,7 +633,7 @@ func getScheduler(listener *cloudprovider.SLoadbalancerListener) *string { return &sch } -func healthCheckParams(params map[string]string,hc *healthCheck) map[string]string { +func healthCheckParams(params map[string]string, hc *healthCheck) map[string]string { if hc != nil { params["HealthCheck.HealthSwitch"] = strconv.Itoa(hc.HealthSwitch) params["HealthCheck.TimeOut"] = strconv.Itoa(hc.TimeOut) @@ -651,7 +651,7 @@ func healthCheckParams(params map[string]string,hc *healthCheck) map[string]stri return params } -func certificateParams(params map[string]string,cert *certificate) map[string]string { +func certificateParams(params map[string]string, cert *certificate) map[string]string { if cert != nil { params["Certificate.SSLMode"] = cert.SSLMode params["Certificate.CertId"] = cert.CERTID @@ -661,4 +661,4 @@ func certificateParams(params map[string]string,cert *certificate) map[string]st } return params -} \ No newline at end of file +} From b4e4cbad28212286a5e81d6247110da06e1b9cc8 Mon Sep 17 00:00:00 2001 From: TangBin Date: Tue, 12 Feb 2019 20:11:12 +0800 Subject: [PATCH 12/15] make fmt --- pkg/compute/models/loadbalancerbackends.go | 2 +- pkg/compute/models/loadbalancerlistenerrules.go | 2 +- pkg/util/qcloud/loadbalancer.go | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/compute/models/loadbalancerbackends.go b/pkg/compute/models/loadbalancerbackends.go index abaa5769b5..6a11363a39 100644 --- a/pkg/compute/models/loadbalancerbackends.go +++ b/pkg/compute/models/loadbalancerbackends.go @@ -360,7 +360,7 @@ func (lbb *SLoadbalancerBackend) SyncWithCloudLoadbalancerBackend(ctx context.Co return err } -func (lbb *SLoadbalancerBackend) UpdateCloudLoadbalancerBackendExternalId(ctx context.Context, userCred mcclient.TokenCredential,externalId string, projectId string, projectSync bool) error { +func (lbb *SLoadbalancerBackend) UpdateCloudLoadbalancerBackendExternalId(ctx context.Context, userCred mcclient.TokenCredential, externalId string, projectId string, projectSync bool) error { _, err := lbb.GetModelManager().TableSpec().Update(lbb, func() error { if projectSync && len(projectId) > 0 { lbb.ProjectId = projectId diff --git a/pkg/compute/models/loadbalancerlistenerrules.go b/pkg/compute/models/loadbalancerlistenerrules.go index 0ef85d6269..64bd36858c 100644 --- a/pkg/compute/models/loadbalancerlistenerrules.go +++ b/pkg/compute/models/loadbalancerlistenerrules.go @@ -354,7 +354,7 @@ func (lbr *SLoadbalancerListenerRule) constructFieldsFromCloudListenerRule(extRu return nil }) if err != nil { - log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s",group.GetId(), err) + log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s", group.GetId(), err) } } } diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index 9362593ccf..19794e946d 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -27,7 +27,6 @@ const ( /* 目前存在的问题: 1.HTTP\HTTPS监听调度算法没同步成功,原因是http、https监听并不关联转发策略,因此本身就没有调度算法 -2.不能创建后端服务器组。原因是没办法找到关联的listener及生成external id */ // https://cloud.tencent.com/document/api/214/30694#LoadBalancer type SLoadbalancer struct { From 2d4166e8412bb9a8ed34e69c580e75f0f4d2de88 Mon Sep 17 00:00:00 2001 From: tangbin Date: Tue, 12 Feb 2019 23:32:38 +0800 Subject: [PATCH 13/15] backend group sync bugfix --- .../models/loadbalancerbackendgroups.go | 10 ++- pkg/compute/models/loadbalancerbackends.go | 3 + pkg/compute/regiondrivers/managedvirtual.go | 69 ++++++++++++------- pkg/util/qcloud/loadbalancer_backend.go | 5 +- pkg/util/qcloud/loadbalancer_backendgroup.go | 10 ++- 5 files changed, 65 insertions(+), 32 deletions(-) diff --git a/pkg/compute/models/loadbalancerbackendgroups.go b/pkg/compute/models/loadbalancerbackendgroups.go index 06c97aba3e..e3407949ed 100644 --- a/pkg/compute/models/loadbalancerbackendgroups.go +++ b/pkg/compute/models/loadbalancerbackendgroups.go @@ -369,7 +369,11 @@ func (man *SLoadbalancerBackendGroupManager) SyncLoadbalancerBackendgroups(ctx c } func (lbbg *SLoadbalancerBackendGroup) constructFieldsFromCloudBackendgroup(lb *SLoadbalancer, extLoadbalancerBackendgroup cloudprovider.ICloudLoadbalancerBackendGroup) { - lbbg.Name = extLoadbalancerBackendgroup.GetName() + // ============兼容腾讯云,backend group 名字以本地为准================== + if lbbg.GetProviderName() != CLOUD_PROVIDER_QCLOUD || len(lbbg.Name) == 0 { + lbbg.Name = extLoadbalancerBackendgroup.GetName() + } + // ================================================================== lbbg.Type = extLoadbalancerBackendgroup.GetType() lbbg.Status = extLoadbalancerBackendgroup.GetStatus() } @@ -404,8 +408,10 @@ func (man *SLoadbalancerBackendGroupManager) newFromCloudLoadbalancerBackendgrou lbbg.LoadbalancerId = lb.Id lbbg.ExternalId = extLoadbalancerBackendgroup.GetGlobalId() - lbbg.constructFieldsFromCloudBackendgroup(lb, extLoadbalancerBackendgroup) + lbbg.CloudregionId = lb.CloudregionId + lbbg.ManagerId = lb.ManagerId + lbbg.constructFieldsFromCloudBackendgroup(lb, extLoadbalancerBackendgroup) lbbg.ProjectId = userCred.GetProjectId() if len(projectId) > 0 { lbbg.ProjectId = projectId diff --git a/pkg/compute/models/loadbalancerbackends.go b/pkg/compute/models/loadbalancerbackends.go index 6a11363a39..92a71e9082 100644 --- a/pkg/compute/models/loadbalancerbackends.go +++ b/pkg/compute/models/loadbalancerbackends.go @@ -379,6 +379,9 @@ func (man *SLoadbalancerBackendManager) newFromCloudLoadbalancerBackend(ctx cont lbb.BackendGroupId = loadbalancerBackendgroup.Id lbb.ExternalId = extLoadbalancerBackend.GetGlobalId() + lbb.CloudregionId = loadbalancerBackendgroup.CloudregionId + lbb.ManagerId = loadbalancerBackendgroup.ManagerId + if err := lbb.constructFieldsFromCloudLoadbalancerBackend(extLoadbalancerBackend); err != nil { return nil, err } diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 9735ab3211..f561cae07d 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -417,9 +417,9 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend } // ==========兼容腾讯云,在fake的backend group 关联具体的转发策略之前。不需要同步服务器========== - // 联具体的转发策略之前ExternalId为空 + // 关联具体的转发策略之前ExternalId为空 if len(lbbg.ExternalId) == 0 { - return nil, lbb.UpdateCloudLoadbalancerBackendExternalId(ctx, userCred, guest.ExternalId, "", false) + return nil, lbb.UpdateCloudLoadbalancerBackendExternalId(ctx, userCred, "", "", false) } // ============q @@ -468,6 +468,16 @@ func (self *SManagedVirtualizationRegionDriver) RequestDeleteLoadbalancerBackend if err != nil { return nil, err } + + // ===========兼容腾讯云,未关联具体转发规则时,直接删除本地数据即可=============== + if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { + count := models.LoadbalancerListenerManager.TableSpec().Query().Equals("backend_group_id", lbbg.GetId()).Count() + count += models.LoadbalancerListenerRuleManager.TableSpec().Query().Equals("backend_group_id", lbbg.GetId()).Count() + if count == 0 { + return nil, nil + } + } + // =================================== iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) if err != nil { return nil, err @@ -512,30 +522,35 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerListene } // ====腾讯云添加后端服务器===== - group := lblis.GetLoadbalancerBackendGroup() - if group != nil { - backends, err := group.GetBackends() - if err != nil { - return nil, fmt.Errorf("failed to find backends for backend group %s: %s", group.GetId(), err) - } - - extBgID := iListener.GetBackendGroupId() - if len(extBgID) == 0 { - return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s", lblis.GetId()) - } - - ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(extBgID) - if err != nil { - return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s: %s", lblis.GetId(), err) - } - - for _, backend := range backends { - _, err := ilbbg.AddBackendServer(backend.ExternalId, backend.Weight, backend.Port) + if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { + group := lblis.GetLoadbalancerBackendGroup() + if group != nil { + backends, err := group.GetBackends() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to find backends for backend group %s: %s", group.GetId(), err) + } + + extBgID := iListener.GetBackendGroupId() + if len(extBgID) == 0 { + return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s", lblis.GetId()) + } + + ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(extBgID) + if err != nil { + return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s: %s", lblis.GetId(), err) + } + + for _, backend := range backends { + guest := backend.GetGuest() + if guest == nil { + return nil, fmt.Errorf("failed to find instance for loadbalancer backend %s", backend.GetId()) + } + _, err := ilbbg.AddBackendServer(guest.GetExternalId(), backend.Weight, backend.Port) + if err != nil { + return nil, err + } } } - } // ========================== @@ -722,7 +737,7 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerListene return nil, err } // ====腾讯云添加后端服务器===== - if len(rule.BackendGroupID) > 0 { + if listener.GetProviderName() == models.CLOUD_PROVIDER_QCLOUD && len(rule.BackendGroupID) > 0 { ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(rule.BackendGroupID) if err != nil { return nil, fmt.Errorf("failed to find backend group for listener rule %s: %s", lbr.Name, err) @@ -735,7 +750,11 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerListene } for _, backend := range backends { - _, err := ilbbg.AddBackendServer(backend.ExternalId, backend.Weight, backend.Port) + guest := backend.GetGuest() + if guest == nil { + return nil, fmt.Errorf("failed to find instance for loadbalancer backend %s", backend.GetId()) + } + _, err := ilbbg.AddBackendServer(guest.GetExternalId(), backend.Weight, backend.Port) if err != nil { return nil, err } diff --git a/pkg/util/qcloud/loadbalancer_backend.go b/pkg/util/qcloud/loadbalancer_backend.go index db42298705..e2a6730572 100755 --- a/pkg/util/qcloud/loadbalancer_backend.go +++ b/pkg/util/qcloud/loadbalancer_backend.go @@ -39,12 +39,13 @@ type rule struct { // ========================================================== +// todo: 待确认。backend InstanceID + Port在lb中是不是全局唯一。 func (self *SLBBackend) GetId() string { - return self.InstanceID + return fmt.Sprintf("%s/%s-%d", self.group.lb.GetId(), self.InstanceID, self.Port) } func (self *SLBBackend) GetName() string { - return fmt.Sprintf("%s/%s", self.group.GetId(), self.GetId()) + return self.GetId() } func (self *SLBBackend) GetGlobalId() string { diff --git a/pkg/util/qcloud/loadbalancer_backendgroup.go b/pkg/util/qcloud/loadbalancer_backendgroup.go index 819bc33238..c5e10ad5de 100755 --- a/pkg/util/qcloud/loadbalancer_backendgroup.go +++ b/pkg/util/qcloud/loadbalancer_backendgroup.go @@ -8,6 +8,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" + "strings" ) type SLBBackendGroup struct { @@ -90,7 +91,7 @@ func (self *SLBBackendGroup) AddBackendServer(serverId string, weight int, port } for _, backend := range backends { - if backend.GetBackendId() == serverId { + if strings.HasSuffix(backend.GetId(), fmt.Sprintf("%s-%d", serverId, port)) { return &backend, nil } } @@ -110,15 +111,18 @@ func (self *SLBBackendGroup) RemoveBackendServer(serverId string, weight int, po } if err != nil { + if strings.Contains(err.Error(), "not registered") { + return nil + } return err } return self.lb.region.WaitLBTaskSuccess(requestId, 5*time.Second, 60*time.Second) } -// 腾讯云无后端服务器组。不支持该操作 +// 腾讯云无后端服务器组。 func (self *SLBBackendGroup) Delete() error { - return cloudprovider.ErrNotSupported + return fmt.Errorf("Please remove related listener/rule frist") } // 腾讯云无后端服务器组 From 11f5b40b633197d86fee9ec51d3520fe7c1ea124 Mon Sep 17 00:00:00 2001 From: TangBin Date: Wed, 13 Feb 2019 09:35:14 +0800 Subject: [PATCH 14/15] bugfix & comment fix --- pkg/compute/models/loadbalancer_const.go | 5 - .../models/loadbalancerbackendgroups.go | 52 +++-- pkg/compute/models/loadbalancerbackends.go | 15 +- .../models/loadbalancerlistenerrules.go | 23 +-- pkg/compute/models/loadbalancerlisteners.go | 21 +- pkg/compute/models/loadbalancers.go | 10 +- pkg/compute/regiondrivers/managedvirtual.go | 18 +- pkg/util/qcloud/certificate.go | 31 +-- pkg/util/qcloud/loadbalancer.go | 187 +++++++++++++----- pkg/util/qcloud/loadbalancer_acl.go | 2 +- pkg/util/qcloud/loadbalancer_backend.go | 14 +- pkg/util/qcloud/loadbalancer_backendgroup.go | 6 +- pkg/util/qcloud/loadbalancer_certificate.go | 2 +- pkg/util/qcloud/loadbalancer_listener.go | 186 +++++++++++++---- pkg/util/qcloud/loadbalancer_listenerrule.go | 6 +- pkg/util/qcloud/qcloud.go | 69 +++++-- pkg/util/qcloud/region.go | 59 ++---- 17 files changed, 449 insertions(+), 257 deletions(-) diff --git a/pkg/compute/models/loadbalancer_const.go b/pkg/compute/models/loadbalancer_const.go index e436637da5..6712149363 100644 --- a/pkg/compute/models/loadbalancer_const.go +++ b/pkg/compute/models/loadbalancer_const.go @@ -159,11 +159,6 @@ var LB_STICKY_SESSION_TYPES = validators.NewChoices( ) // TODO maybe https check when field need comes ;) -const ( - LB_HEALTH_CHECK_ENABLE = "on" - LB_HEALTH_CHECK_DISABLE = "off" -) - const ( LB_HEALTH_CHECK_TCP = "tcp" LB_HEALTH_CHECK_UDP = "udp" diff --git a/pkg/compute/models/loadbalancerbackendgroups.go b/pkg/compute/models/loadbalancerbackendgroups.go index e3407949ed..9fdf4a5363 100644 --- a/pkg/compute/models/loadbalancerbackendgroups.go +++ b/pkg/compute/models/loadbalancerbackendgroups.go @@ -184,27 +184,47 @@ func (lbbg *SLoadbalancerBackendGroup) GetBackends() ([]SLoadbalancerBackend, er return backends, err } +// 返回值 TotalRef +func (lbbg *SLoadbalancerBackendGroup) RefCount() int { + men := lbbg.getRefManagers() + var count int + for _, m := range men { + count += lbbg.refCount(m) + } + + return count +} + +func (lbbg *SLoadbalancerBackendGroup) refCount(men db.IModelManager) int { + t := men.TableSpec().Instance() + pdF := t.Field("pending_deleted") + return t.Query(). + Equals("backend_group_id", lbbg.Id). + Filter(sqlchemy.OR(sqlchemy.IsNull(pdF), sqlchemy.IsFalse(pdF))). + Count() +} + +func (lbbg *SLoadbalancerBackendGroup) getRefManagers() []db.IModelManager { + // 引用Backend Group的数据库 + return []db.IModelManager{ + LoadbalancerManager, + LoadbalancerListenerManager, + LoadbalancerListenerRuleManager, + } + +} + func (lbbg *SLoadbalancerBackendGroup) AllowPerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { return false } func (lbbg *SLoadbalancerBackendGroup) ValidateDeleteCondition(ctx context.Context) error { - men := []db.IModelManager{ - LoadbalancerManager, - LoadbalancerListenerManager, - LoadbalancerListenerRuleManager, - } - lbbgId := lbbg.Id - for _, man := range men { - t := man.TableSpec().Instance() - pdF := t.Field("pending_deleted") - n := t.Query(). - Equals("backend_group_id", lbbgId). - Filter(sqlchemy.OR(sqlchemy.IsNull(pdF), sqlchemy.IsFalse(pdF))). - Count() + men := lbbg.getRefManagers() + for _, m := range men { + n := lbbg.refCount(m) if n > 0 { return fmt.Errorf("backend group %s is still referred to by %d %s", - lbbgId, n, man.KeywordPlural()) + lbbg.Id, n, m.KeywordPlural()) } } @@ -369,11 +389,11 @@ func (man *SLoadbalancerBackendGroupManager) SyncLoadbalancerBackendgroups(ctx c } func (lbbg *SLoadbalancerBackendGroup) constructFieldsFromCloudBackendgroup(lb *SLoadbalancer, extLoadbalancerBackendgroup cloudprovider.ICloudLoadbalancerBackendGroup) { - // ============兼容腾讯云,backend group 名字以本地为准================== + // 对于腾讯云,backend group 名字以本地为准 if lbbg.GetProviderName() != CLOUD_PROVIDER_QCLOUD || len(lbbg.Name) == 0 { lbbg.Name = extLoadbalancerBackendgroup.GetName() } - // ================================================================== + lbbg.Type = extLoadbalancerBackendgroup.GetType() lbbg.Status = extLoadbalancerBackendgroup.GetStatus() } diff --git a/pkg/compute/models/loadbalancerbackends.go b/pkg/compute/models/loadbalancerbackends.go index 92a71e9082..4b7bdf33e2 100644 --- a/pkg/compute/models/loadbalancerbackends.go +++ b/pkg/compute/models/loadbalancerbackends.go @@ -251,6 +251,7 @@ func (lbb *SLoadbalancerBackend) StartLoadBalancerBackendDeleteTask(ctx context. func (man *SLoadbalancerBackendManager) getLoadbalancerBackendsByLoadbalancerBackendgroup(loadbalancerBackendgroup *SLoadbalancerBackendGroup) ([]SLoadbalancerBackend, error) { loadbalancerBackends := []SLoadbalancerBackend{} q := man.Query().Equals("backend_group_id", loadbalancerBackendgroup.Id) + q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("pending_deleted")), sqlchemy.IsFalse(q.Field("pending_deleted")))) if err := db.FetchModelObjects(man, q, &loadbalancerBackends); err != nil { return nil, err } @@ -298,7 +299,7 @@ func (man *SLoadbalancerBackendManager) SyncLoadbalancerBackends(ctx context.Con syncResult.Delete() } } else { - err = removed[i].Delete(ctx, userCred) + err = removed[i].PendingDelete() if err != nil { syncResult.DeleteError(err) } else { @@ -360,18 +361,6 @@ func (lbb *SLoadbalancerBackend) SyncWithCloudLoadbalancerBackend(ctx context.Co return err } -func (lbb *SLoadbalancerBackend) UpdateCloudLoadbalancerBackendExternalId(ctx context.Context, userCred mcclient.TokenCredential, externalId string, projectId string, projectSync bool) error { - _, err := lbb.GetModelManager().TableSpec().Update(lbb, func() error { - if projectSync && len(projectId) > 0 { - lbb.ProjectId = projectId - } - - lbb.ExternalId = externalId - return nil - }) - return err -} - func (man *SLoadbalancerBackendManager) newFromCloudLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, loadbalancerBackendgroup *SLoadbalancerBackendGroup, extLoadbalancerBackend cloudprovider.ICloudLoadbalancerBackend, projectId string) (*SLoadbalancerBackend, error) { lbb := &SLoadbalancerBackend{} lbb.SetModelManager(man) diff --git a/pkg/compute/models/loadbalancerlistenerrules.go b/pkg/compute/models/loadbalancerlistenerrules.go index 64bd36858c..08084298cd 100644 --- a/pkg/compute/models/loadbalancerlistenerrules.go +++ b/pkg/compute/models/loadbalancerlistenerrules.go @@ -117,16 +117,15 @@ func (man *SLoadbalancerListenerRuleManager) ValidateCreateData(ctx context.Cont return nil, fmt.Errorf("listener type must be http/https, got %s", listenerType) } { - if backendGroup, ok := backendGroupV.Model.(*SLoadbalancerBackendGroup); ok && backendGroup.LoadbalancerId != listener.LoadbalancerId { + if lbbg, ok := backendGroupV.Model.(*SLoadbalancerBackendGroup); ok && lbbg.LoadbalancerId != listener.LoadbalancerId { return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", - backendGroup.Name, backendGroup.Id, backendGroup.LoadbalancerId, listener.LoadbalancerId) + lbbg.Name, lbbg.Id, lbbg.LoadbalancerId, listener.LoadbalancerId) } else { // 腾讯云backend group只能1v1关联 if listener.GetProviderName() == CLOUD_PROVIDER_QCLOUD { - count := LoadbalancerListenerRuleManager.TableSpec().Query().Equals("backend_group_id", backendGroup.GetId()).Count() - count += man.TableSpec().Query().Equals("backend_group_id", backendGroup.GetId()).Count() + count := lbbg.RefCount() if count > 0 { - return nil, fmt.Errorf("backendgroup aready related with other listener/rule") + return nil, fmt.Errorf("backendgroup already related with other listener/rule") } } } @@ -346,19 +345,15 @@ func (lbr *SLoadbalancerListenerRule) constructFieldsFromCloudListenerRule(extRu if groupId := extRule.GetBackendGroupId(); len(groupId) > 0 { // 腾讯云兼容代码。主要目的是在关联listener rule时回写一个fake的backend group external id if len(groupId) > 0 && len(lbr.BackendGroupId) > 0 { - igroup, err := LoadbalancerBackendGroupManager.FetchById(lbr.BackendGroupId) - group := igroup.(*SLoadbalancerBackendGroup) - if err == nil && (len(group.ExternalId) == 0 || group.ExternalId != groupId) { - _, err := LoadbalancerBackendGroupManager.TableSpec().Update(group, func() error { - group.ExternalId = groupId - return nil - }) + ilbbg, err := LoadbalancerBackendGroupManager.FetchById(lbr.BackendGroupId) + lbbg := ilbbg.(*SLoadbalancerBackendGroup) + if err == nil && (len(lbbg.ExternalId) == 0 || lbbg.ExternalId != groupId) { + err = lbbg.SetExternalId(groupId) if err != nil { - log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s", group.GetId(), err) + log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s", lbbg.GetId(), err) } } } - // =========== if backendgroup, err := LoadbalancerBackendGroupManager.FetchByExternalId(groupId); err == nil { lbr.BackendGroupId = backendgroup.GetId() diff --git a/pkg/compute/models/loadbalancerlisteners.go b/pkg/compute/models/loadbalancerlisteners.go index 8c53b9fd9a..10d29cfdef 100644 --- a/pkg/compute/models/loadbalancerlisteners.go +++ b/pkg/compute/models/loadbalancerlisteners.go @@ -221,14 +221,13 @@ func (man *SLoadbalancerListenerManager) ValidateCreateData(ctx context.Context, } } { - if backendGroup, ok := backendGroupV.Model.(*SLoadbalancerBackendGroup); ok && backendGroup.LoadbalancerId != lb.Id { + if lbbg, ok := backendGroupV.Model.(*SLoadbalancerBackendGroup); ok && lbbg.LoadbalancerId != lb.Id { return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", - backendGroup.Name, backendGroup.Id, backendGroup.LoadbalancerId, lb.Id) + lbbg.Name, lbbg.Id, lbbg.LoadbalancerId, lb.Id) } else { // 腾讯云backend group只能1v1关联 if lb.GetProviderName() == CLOUD_PROVIDER_QCLOUD { - count := LoadbalancerListenerRuleManager.TableSpec().Query().Equals("backend_group_id", backendGroup.GetId()).Count() - count += man.TableSpec().Query().Equals("backend_group_id", backendGroup.GetId()).Count() + count := lbbg.RefCount() if count > 0 { return nil, fmt.Errorf("backendgroup aready related with other listener/rule") } @@ -777,19 +776,15 @@ func (lblis *SLoadbalancerListener) constructFieldsFromCloudListener(lb *SLoadba groupId := extListener.GetBackendGroupId() // 腾讯云兼容代码。主要目的是在关联listen时回写一个fake的backend group external id if len(groupId) > 0 && len(lblis.BackendGroupId) > 0 { - igroup, err := LoadbalancerBackendGroupManager.FetchById(lblis.BackendGroupId) - group := igroup.(*SLoadbalancerBackendGroup) - if err == nil && (len(group.ExternalId) == 0 || group.ExternalId != groupId) { - _, err := LoadbalancerBackendGroupManager.TableSpec().Update(group, func() error { - group.ExternalId = groupId - return nil - }) + ilbbg, err := LoadbalancerBackendGroupManager.FetchById(lblis.BackendGroupId) + lbbg := ilbbg.(*SLoadbalancerBackendGroup) + if err == nil && (len(lbbg.ExternalId) == 0 || lbbg.ExternalId != groupId) { + err = lbbg.SetExternalId(groupId) if err != nil { - log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s", group.GetId(), err) + log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s", lbbg.GetId(), err) } } } - // =========== if len(groupId) == 0 { lblis.BackendGroupId = lb.BackendGroupId diff --git a/pkg/compute/models/loadbalancers.go b/pkg/compute/models/loadbalancers.go index e8f20555c3..7a77c7c984 100644 --- a/pkg/compute/models/loadbalancers.go +++ b/pkg/compute/models/loadbalancers.go @@ -68,7 +68,7 @@ type SLoadbalancer struct { LoadbalancerSpec string `list:"user" get:"user" create:"optional"` BackendGroupId string `width:"36" charset:"ascii" nullable:"true" list:"user" update:"user" update:"user"` - LBInfo jsonutils.JSONObject `charset:"utf8" nullable:"true" list:"user" update:"admin" create:"admin_required"` + LBInfo jsonutils.JSONObject `charset:"utf8" nullable:"true" list:"user" update:"admin" create:"admin_optional"` } func (man *SLoadbalancerManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) { @@ -553,6 +553,10 @@ func (man *SLoadbalancerManager) newFromCloudLoadbalancer(ctx context.Context, u lb.ProjectId = provider.ProjectId } + if extLb.GetMetadata() != nil { + lb.LBInfo = extLb.GetMetadata() + } + if err := man.TableSpec().Insert(&lb); err != nil { log.Errorf("newFromCloudRegion fail %s", err) return nil, err @@ -583,6 +587,10 @@ func (lb *SLoadbalancer) SyncWithCloudLoadbalancer(ctx context.Context, userCred lb.LoadbalancerSpec = extLb.GetLoadbalancerSpec() lb.ChargeType = extLb.GetChargeType() + if extLb.GetMetadata() != nil { + lb.LBInfo = extLb.GetMetadata() + } + if projectSync && len(projectId) > 0 { lb.ProjectId = projectId } diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index f561cae07d..978f063b5d 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -333,11 +333,10 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend return nil, err } - // ========= 腾讯云本身没有后端服务器组,因此不需要在qcloud端执行创建操作 ========= + // 腾讯云本身没有后端服务器组,因此不需要在qcloud端执行创建操作 if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { return nil, nil } - // ============================================ loadbalancer := lbbg.GetLoadbalancer() if loadbalancer == nil { @@ -416,12 +415,10 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name) } - // ==========兼容腾讯云,在fake的backend group 关联具体的转发策略之前。不需要同步服务器========== - // 关联具体的转发策略之前ExternalId为空 - if len(lbbg.ExternalId) == 0 { - return nil, lbb.UpdateCloudLoadbalancerBackendExternalId(ctx, userCred, "", "", false) + // 兼容腾讯云,在fake的backend group 关联具体的转发策略之前。不需要同步后端服务器 + if lbbg.GetProviderName() == models.CLOUD_PROVIDER_QCLOUD && lbbg.RefCount() == 0 { + return nil, nil } - // ============q lb := lbbg.GetLoadbalancer() if lb == nil { @@ -471,13 +468,12 @@ func (self *SManagedVirtualizationRegionDriver) RequestDeleteLoadbalancerBackend // ===========兼容腾讯云,未关联具体转发规则时,直接删除本地数据即可=============== if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { - count := models.LoadbalancerListenerManager.TableSpec().Query().Equals("backend_group_id", lbbg.GetId()).Count() - count += models.LoadbalancerListenerRuleManager.TableSpec().Query().Equals("backend_group_id", lbbg.GetId()).Count() + count := lbbg.RefCount() if count == 0 { return nil, nil } } - // =================================== + iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) if err != nil { return nil, err @@ -532,7 +528,7 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerListene extBgID := iListener.GetBackendGroupId() if len(extBgID) == 0 { - return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s", lblis.GetId()) + return nil, fmt.Errorf("the backend group external id of loadbalancer listener %s is empty", lblis.GetId()) } ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(extBgID) diff --git a/pkg/util/qcloud/certificate.go b/pkg/util/qcloud/certificate.go index d1eb8b9afc..6bccdb553e 100755 --- a/pkg/util/qcloud/certificate.go +++ b/pkg/util/qcloud/certificate.go @@ -3,7 +3,6 @@ package qcloud import ( "fmt" "strconv" - "strings" "time" ) @@ -82,12 +81,6 @@ func (self *SRegion) GetCertificates(id string, withCert bool, limit int, page i return certs, int(total), nil } -/* -cert 是 String 证书内容 -certType 是 String 证书类型(目前支持:CA为客户端证书,SVR为服务器证书) -key 否 String 证书私钥,certType=SVR时必填) -alias 否 String 证书备注 -*/ // https://cloud.tencent.com/document/api/400/9078 // 返回证书ID func (self *SRegion) CreateCertificate(cert, certType, key, desc string) (string, error) { @@ -107,16 +100,7 @@ func (self *SRegion) CreateCertificate(cert, certType, key, desc string) (string return "", err } - code, err := resp.Get("code") - if err != nil { - return "", err - } - - if code.String() != "0" { - return "", fmt.Errorf("CreateCertificate failed : %s", resp.String()) - } - - return resp.GetString("data", "id") + return resp.GetString("id") } func (self *SRegion) DeleteCertificate(id string) error { @@ -125,19 +109,10 @@ func (self *SRegion) DeleteCertificate(id string) error { } params := map[string]string{"id": id} - resp, err := self.wssRequest("CertDelete", params) + _, err := self.wssRequest("CertDelete", params) if err != nil { return err } - status, err := resp.GetString("codeDesc") - if err != nil { - return err - } - - if strings.ToLower(status) == "success" { - return nil - } else { - return fmt.Errorf("DelteCertificate failed: %s", resp.String()) - } + return nil } diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index 19794e946d..8e7fd79611 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -24,10 +24,6 @@ const ( LB_TYPE_APPLICATION = LB_TYPE(1) ) -/* -目前存在的问题: -1.HTTP\HTTPS监听调度算法没同步成功,原因是http、https监听并不关联转发策略,因此本身就没有调度算法 -*/ // https://cloud.tencent.com/document/api/214/30694#LoadBalancer type SLoadbalancer struct { region *SRegion @@ -60,9 +56,16 @@ func (self *SLoadbalancer) GetChargeType() string { // https://cloud.tencent.com/document/product/214/30689 func (self *SLoadbalancer) Delete() error { - _, err := self.region.DeleteLoadbalancer(self.GetId()) - if err != nil { - return err + if self.Forward == LB_TYPE_APPLICATION { + _, err := self.region.DeleteLoadbalancer(self.GetId()) + if err != nil { + return err + } + } else { + _, err := self.region.DeleteClassicLoadbalancer(self.GetId()) + if err != nil { + return err + } } return cloudprovider.WaitDeleted(self, 5*time.Second, 60*time.Second) @@ -102,8 +105,8 @@ func onecloudHealthCodeToQcloud(codes string) int { qcode := 0 for i, code := range HTTP_CODES { if strings.Contains(code, codes) { - c := 1 << uint(i) - qcode += c + // 按位或然后再赋值qcode + qcode |= 1 << uint(i) } } @@ -111,38 +114,47 @@ func onecloudHealthCodeToQcloud(codes string) int { } // https://cloud.tencent.com/document/product/214/30693 -// Onecloud 不支持双向证书 -/* -todo: 限制比较多必须加参数校验 -HealthSwitch Integer 否 是否开启健康检查:1(开启)、0(关闭)。 -TimeOut Integer 否 健康检查的响应超时时间,可选值:2~60,默认值:2,单位:秒。响应超时时间要小于检查间隔时间。 -IntervalTime Integer 否 健康检查探测间隔时间,默认值:5,可选值:5~300,单位:秒。 -HealthNum Integer 否 健康阈值,默认值:3,表示当连续探测三次健康则表示该转发正常,可选值:2~10,单位:次。 -UnHealthNum Integer 否 不健康阈值,默认值:3,表示当连续探测三次不健康则表示该转发异常,可选值:2~10,单位:次。 -HttpCode Integer 否 健康检查状态码(仅适用于HTTP/HTTPS转发规则)。可选值:1~31,默认 31。 -1 表示探测后返回值 1xx 表示健康,2 表示返回 2xx 表示健康,4 表示返回 3xx 表示健康,8 表示返回 4xx 表示健康,16 表示返回 5xx 表示健康。若希望多种码都表示健康,则将相应的值相加。 -HttpCheckPath String 否 健康检查路径(仅适用于HTTP/HTTPS转发规则)。 -HttpCheckDomain String 否 健康检查域名(仅适用于HTTP/HTTPS转发规则)。 -HttpCheckMethod String 否 健康检查方法(仅适用于HTTP/HTTPS转发规则),取值为HEAD或GET。 - -SSLMode String 是 认证类型,UNIDIRECTIONAL:单向认证,MUTUAL:双向认证 -CertId String 否 服务端证书的 ID,如果不填写此项则必须上传证书,包括 CertContent,CertKey,CertName。 -CertCaId String 否 客户端证书的 ID,如果 SSLMode=mutual,监听器如果不填写此项则必须上传客户端证书,包括 CertCaContent,CertCaName。 -*/ +// todo: 1.限制比较多必须加参数校验 2.Onecloud 不支持双向证书可能存在兼容性问题 +// 应用型负载均衡 https监听默认开启SNI。传统型不支持设置SNI func (self *SLoadbalancer) CreateILoadBalancerListener(listener *cloudprovider.SLoadbalancerListener) (cloudprovider.ICloudLoadbalancerListener, error) { sniSwitch := 0 + if listener.ListenerType == models.LB_LISTENER_TYPE_HTTPS { + sniSwitch = 1 + } + hc := getHealthCheck(listener) cert := getCertificate(listener) - listenId, err := self.region.CreateLoadbalancerListener(self.GetId(), - listener.Name, - getProtocol(listener), - listener.ListenerPort, - getScheduler(listener), - &listener.StickySessionCookieTimeout, - &sniSwitch, - hc, - cert) + var listenId string + var err error + if self.Forward == LB_TYPE_APPLICATION { + listenId, err = self.region.CreateLoadbalancerListener(self.GetId(), + listener.Name, + getProtocol(listener), + listener.ListenerPort, + getScheduler(listener), + &listener.StickySessionCookieTimeout, + &sniSwitch, + hc, + cert) + } else { + // 传统型内网属性负载均衡不支持指定scheduler + var scheduler *string + if self.LoadBalancerType == "OPEN" { + scheduler = getScheduler(listener) + } + + listenId, err = self.region.CreateClassicLoadbalancerListener(self.GetId(), + listener.Name, + getClassicLBProtocol(listener), + listener.ListenerPort, + listener.BackendServerPort, + scheduler, + &listener.StickySessionCookieTimeout, + &sniSwitch, + hc, + cert) + } if err != nil { return nil, err @@ -214,8 +226,7 @@ func (self *SLoadbalancer) GetMetadata() *jsonutils.JSONDict { return meta } -// todo: 腾讯云支持绑定多个地址。目前未找到相关文档描述。需要提工单询问。 -// 目前先当作只能绑定一个IP处理 +// 腾讯云当前不支持一个LB绑定多个ip,每个LB只支持绑定一个ip func (self *SLoadbalancer) GetAddress() string { return self.LoadBalancerVips[0] } @@ -309,10 +320,8 @@ func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.IClo func (self *SRegion) GetLoadbalancers(ids []string) ([]SLoadbalancer, error) { params := map[string]string{} - if ids != nil { - for i, id := range ids { - params[fmt.Sprintf("LoadBalancerIds.%d", i)] = id - } + for i, id := range ids { + params[fmt.Sprintf("LoadBalancerIds.%d", i)] = id } offset := 0 @@ -344,7 +353,7 @@ func (self *SRegion) GetLoadbalancers(ids []string) ([]SLoadbalancer, error) { } lbs = append(lbs, parts...) - offset += limit + offset += len(parts) if offset >= total { for i := range lbs { lbs[i].region = self @@ -361,15 +370,18 @@ func (self *SRegion) GetLoadbalancer(id string) (*SLoadbalancer, error) { } lbs, err := self.GetLoadbalancers([]string{id}) - if err == nil && len(lbs) == 0 { + if err != nil { + return nil, err + } + + switch len(lbs) { + case 0: return nil, cloudprovider.ErrNotFound - } - - if err != nil && len(lbs) == 1 { + case 1: return &lbs[0], nil + default: + return nil, fmt.Errorf("GetLoadbalancer %s found %d", id, len(lbs)) } - - return nil, err } /* @@ -390,6 +402,24 @@ func (self *SRegion) DeleteLoadbalancer(lbid string) (string, error) { return resp.GetString("RequestId") } +/* +返回requstid 用于异步任务查询 +https://cloud.tencent.com/document/product/214/30689 +*/ +func (self *SRegion) DeleteClassicLoadbalancer(lbid string) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("loadbalancer id should not be empty") + } + + params := map[string]string{"loadBalancerIds.n": lbid} + resp, err := self.lbRequest("DeleteLoadBalancers", params) + if err != nil { + return "", err + } + + return resp.GetString("requestId") +} + /* https://cloud.tencent.com/document/product/214/30693 SNI 特性是什么?? @@ -421,8 +451,8 @@ func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, por params["Scheduler"] = *scheduler } - params = healthCheckParams(params, healthCheck) - params = certificateParams(params, cert) + params = healthCheckParams(LB_TYPE_APPLICATION, params, healthCheck, "HealthCheck.") + params = certificateParams(LB_TYPE_APPLICATION, params, cert, "Certificate.") resp, err := self.clbRequest("CreateListener", params) if err != nil { @@ -443,6 +473,61 @@ func (self *SRegion) CreateLoadbalancerListener(lbid, name, protocol string, por } } +// https://cloud.tencent.com/document/api/214/1255 +// 不支持sniSwitch +// todo: 待测试 +func (self *SRegion) CreateClassicLoadbalancerListener(lbid, name string, protocol, port, backendServerPort int, scheduler *string, sessionExpireTime, sniSwitch *int, healthCheck *healthCheck, cert *certificate) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("loadbalancer id should not be empty") + } + + // 负载均衡实例监听器协议类型 1:HTTP,2:TCP,3:UDP,4:HTTPS。 + // todo: 待测试 。 这里没有判断是否为公网负载均衡,可能存在问题.内网传统型负载均衡监听协议只支持TCP、UDP,并且不能指定调度算法 + params := map[string]string{ + "loadBalancerId": lbid, + "listeners.0.loadBalancerPort": strconv.Itoa(port), + "listeners.0.instancePort": strconv.Itoa(backendServerPort), + "listeners.0.protocol": strconv.Itoa(protocol), + } + + if len(name) > 0 { + params["listeners.0.listenerName"] = name + } + + if sessionExpireTime != nil { + params["listeners.0.sessionExpire"] = strconv.Itoa(*sessionExpireTime) + } + + if scheduler != nil && len(*scheduler) > 0 && (protocol == 2 || protocol == 3) { + params["listeners.0.scheduler"] = strings.ToLower(*scheduler) + } + + if scheduler != nil && len(*scheduler) > 0 && (protocol == 1 || protocol == 4) { + params["listeners.0.httpHash"] = strings.ToLower(*scheduler) + } + + params = healthCheckParams(LB_TYPE_CLASSIC, params, healthCheck, "listeners.0.") + params = certificateParams(LB_TYPE_CLASSIC, params, cert, "listeners.0.") + + resp, err := self.lbRequest("CreateLoadBalancerListeners", params) + if err != nil { + return "", err + } + + listeners, err := resp.GetArray("listenerIds") + if err != nil { + return "", err + } + + if len(listeners) == 0 { + return "", fmt.Errorf("CreateLoadbalancerListener no listener id returned: %s", resp.String()) + } else if len(listeners) == 1 { + return listeners[0].GetString() + } else { + return "", fmt.Errorf("CreateLoadbalancerListener mutliple listener id returned: %s", resp.String()) + } +} + // https://cloud.tencent.com/document/product/214/30683 // 任务的当前状态。 0:成功,1:失败,2:进行中 func (self *SRegion) GetLBTaskStatus(requestId string) (string, error) { diff --git a/pkg/util/qcloud/loadbalancer_acl.go b/pkg/util/qcloud/loadbalancer_acl.go index 71a874b25d..61fe1f7da6 100755 --- a/pkg/util/qcloud/loadbalancer_acl.go +++ b/pkg/util/qcloud/loadbalancer_acl.go @@ -5,7 +5,7 @@ import ( "yunion.io/x/onecloud/pkg/compute/models" ) -// todo: 腾讯云没有LB ACL +// 腾讯云没有LB ACL type SLBACL struct{} func (self *SLBACL) GetId() string { diff --git a/pkg/util/qcloud/loadbalancer_backend.go b/pkg/util/qcloud/loadbalancer_backend.go index e2a6730572..357f337358 100755 --- a/pkg/util/qcloud/loadbalancer_backend.go +++ b/pkg/util/qcloud/loadbalancer_backend.go @@ -39,9 +39,9 @@ type rule struct { // ========================================================== -// todo: 待确认。backend InstanceID + Port在lb中是不是全局唯一。 +// backend InstanceID + protocol +Port + ip + rip全局唯一 func (self *SLBBackend) GetId() string { - return fmt.Sprintf("%s/%s-%d", self.group.lb.GetId(), self.InstanceID, self.Port) + return fmt.Sprintf("%s/%s-%d", self.group.GetId(), self.InstanceID, self.Port) } func (self *SLBBackend) GetName() string { @@ -64,10 +64,7 @@ func (self *SLBBackend) Refresh() error { for _, backend := range backends { if backend.GetId() == self.GetId() { - err := jsonutils.Update(self, backend) - if err != nil { - return err - } + return jsonutils.Update(self, backend) } } @@ -127,9 +124,6 @@ func (self *SRegion) getBackends(lbId, listenerId, ruleId string) ([]SLBBackend, params["ListenerIds.0"] = listenerId } - if len(listenerId) > 0 { - params["ListenerIds.0"] = listenerId - } resp, err := self.clbRequest("DescribeTargets", params) if err != nil { return nil, err @@ -143,7 +137,7 @@ func (self *SRegion) getBackends(lbId, listenerId, ruleId string) ([]SLBBackend, for _, entry := range lbackends { if (entry.Protocol == "HTTP" || entry.Protocol == "HTTPS") && len(ruleId) == 0 { - return nil, fmt.Errorf("GetBackends http、https listener must specific rule id") + return nil, fmt.Errorf("GetBackends for http/https listener %s must specific rule id", listenerId) } if len(ruleId) > 0 { diff --git a/pkg/util/qcloud/loadbalancer_backendgroup.go b/pkg/util/qcloud/loadbalancer_backendgroup.go index c5e10ad5de..7ec3f60c0e 100755 --- a/pkg/util/qcloud/loadbalancer_backendgroup.go +++ b/pkg/util/qcloud/loadbalancer_backendgroup.go @@ -5,10 +5,10 @@ import ( "strconv" "time" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" - "strings" ) type SLBBackendGroup struct { @@ -138,10 +138,10 @@ func backendGroupIdGen(lbid string, secondId string) string { } } -// http https 后端服务器只与规则绑定 func (self *SLBBackendGroup) GetId() string { t := self.listener.GetListenerType() if t == models.LB_LISTENER_TYPE_HTTP || t == models.LB_LISTENER_TYPE_HTTPS { + // http https 后端服务器只与规则绑定 return backendGroupIdGen(self.lb.GetId(), self.rule.GetId()) } else if self.lb.Forward == LB_TYPE_APPLICATION { return backendGroupIdGen(self.lb.GetId(), self.listener.GetId()) @@ -167,7 +167,7 @@ func (self *SLBBackendGroup) Refresh() error { return nil } -// todo: model没有更新这个字段? +// note: model没有更新这个字段? func (self *SLBBackendGroup) IsEmulated() bool { return true } diff --git a/pkg/util/qcloud/loadbalancer_certificate.go b/pkg/util/qcloud/loadbalancer_certificate.go index 06caebc9a8..1ec5df50c1 100755 --- a/pkg/util/qcloud/loadbalancer_certificate.go +++ b/pkg/util/qcloud/loadbalancer_certificate.go @@ -49,7 +49,7 @@ func (self *SLBCertificate) Refresh() error { } if total != 1 { - return fmt.Errorf("%d Certificate found", total) + return fmt.Errorf("Expecting 1 certificate, got %d", total) } cert := SLBCertificate{region: self.region, SCertificate: certs[0]} diff --git a/pkg/util/qcloud/loadbalancer_listener.go b/pkg/util/qcloud/loadbalancer_listener.go index 5cf051a414..7d38492210 100755 --- a/pkg/util/qcloud/loadbalancer_listener.go +++ b/pkg/util/qcloud/loadbalancer_listener.go @@ -58,13 +58,12 @@ type SLBListener struct { ClassicListener bool // 这个字段是在qcloud返回字段基础上,额外增加的字段。用于区分listener 是否是classic。 } -// todo: 腾讯云后端端口不是与listener绑定的 +// 腾讯云后端端口不是与listener绑定的 func (self *SLBListener) GetBackendServerPort() int { return 0 } // https://cloud.tencent.com/document/product/214/30691 -// todo: 调度规则原用监听器的Scheduler ok? https 协议怎么兼容? func (self *SLBListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) { requestId, err := self.lb.region.CreateLoadbalancerListenerRule(self.lb.GetId(), self.GetId(), @@ -123,6 +122,7 @@ func (self *SLBListener) Sync(listener *cloudprovider.SLoadbalancerListener) err hc := getHealthCheck(listener) cert := getCertificate(listener) requestId, err := self.lb.region.UpdateLoadbalancerListener( + self.lb.Forward, self.lb.GetId(), self.GetId(), &listener.Name, @@ -138,7 +138,7 @@ func (self *SLBListener) Sync(listener *cloudprovider.SLoadbalancerListener) err } func (self *SLBListener) Delete() error { - requestId, err := self.lb.region.DeleteLoadbalancerListener(self.lb.GetId(), self.GetId()) + requestId, err := self.lb.region.DeleteLoadbalancerListener(self.lb.Forward, self.lb.GetId(), self.GetId()) if err != nil { return err } @@ -290,13 +290,12 @@ func (self *SLBListener) GetAclId() string { func (self *SLBListener) GetHealthCheck() string { if self.HealthCheck.HealthSwitch == 0 { - return models.LB_HEALTH_CHECK_DISABLE + return models.LB_BOOL_OFF } else { - return models.LB_HEALTH_CHECK_ENABLE + return models.LB_BOOL_ON } } -// todo: ?待确认 func (self *SLBListener) GetHealthCheckType() string { if len(self.HealthCheck.HTTPCheckMethod) > 0 { return models.LB_HEALTH_CHECK_HTTP @@ -392,8 +391,13 @@ func (self *SLBListener) GetStickySessionType() string { return models.LB_STICKY_SESSION_TYPE_INSERT } -// todo: 腾讯云不支持? +// https://cloud.tencent.com/document/product/214/2736 +// 经测试应用型负载均衡返回都是 tgw_l7_route。 func (self *SLBListener) GetStickySessionCookie() string { + if self.GetListenerType() == models.LB_LISTENER_TYPE_HTTPS { + return "tgw_l7_route" + } + return "" } @@ -431,7 +435,7 @@ func (self *SLBListener) GetCertificateId() string { return self.Certificate.CERTID } -// todo: ?? +// https://cloud.tencent.com/document/product/214/5412#2.-https.E6.94.AF.E6.8C.81.E5.93.AA.E4.BA.9B.E7.89.88.E6.9C.AC.E7.9A.84ssl.2Ftls.E5.AE.89.E5.85.A8.E5.8D.8F.E8.AE.AE.EF.BC.9F func (self *SLBListener) GetTLSCipherPolicy() string { return "" } @@ -510,7 +514,7 @@ func (self *SRegion) CreateLoadbalancerListenerRule(lbid string, listenerId stri } // 返回requestID -func (self *SRegion) DeleteLoadbalancerListener(lbid string, listenerId string) (string, error) { +func (self *SRegion) deleteLoadbalancerListener(lbid string, listenerId string) (string, error) { if len(lbid) == 0 { return "", fmt.Errorf("loadbalancer id should not be empty") } @@ -528,16 +532,45 @@ func (self *SRegion) DeleteLoadbalancerListener(lbid string, listenerId string) return resp.GetString("RequestId") } -// https://cloud.tencent.com/document/product/214/30681 -func (self *SRegion) UpdateLoadbalancerListener(lbid string, listenerId string, listenerName *string, scheduler *string, sessionExpireTime *int, healthCheck *healthCheck, cert *certificate) (string, error) { +// 返回requestID +func (self *SRegion) deleteClassicLoadbalancerListener(lbid string, listenerId string) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("classic loadbalancer id should not be empty") + } + + params := map[string]string{ + "loadBalancerId": lbid, + "listenerIds.0": listenerId, + } + + resp, err := self.lbRequest("DeleteLoadBalancerListeners", params) + if err != nil { + return "", err + } + + _requestId, err := resp.Float("requestId") + if err != nil { + return "", err + } + + return fmt.Sprintf("%.f", _requestId), nil +} + +// 返回requestID +func (self *SRegion) DeleteLoadbalancerListener(t LB_TYPE, lbid string, listenerId string) (string, error) { if len(lbid) == 0 { return "", fmt.Errorf("loadbalancer id should not be empty") } - if len(listenerId) == 0 { - return "", fmt.Errorf("loadbalancer listener id should not be empty") + if t == LB_TYPE_APPLICATION { + return self.deleteLoadbalancerListener(lbid, listenerId) + } else { + return self.deleteClassicLoadbalancerListener(lbid, listenerId) } +} +// https://cloud.tencent.com/document/product/214/30681 +func (self *SRegion) updateLoadbalancerListener(lbid string, listenerId string, listenerName *string, scheduler *string, sessionExpireTime *int, healthCheck *healthCheck, cert *certificate) (string, error) { params := map[string]string{ "LoadBalancerId": lbid, "ListenerId": listenerId, @@ -555,8 +588,8 @@ func (self *SRegion) UpdateLoadbalancerListener(lbid string, listenerId string, params["SessionExpireTime"] = strconv.Itoa(*sessionExpireTime) } - params = healthCheckParams(params, healthCheck) - params = certificateParams(params, cert) + params = healthCheckParams(LB_TYPE_APPLICATION, params, healthCheck, "HealthCheck.") + params = certificateParams(LB_TYPE_APPLICATION, params, cert, "Certificate.") resp, err := self.clbRequest("ModifyListener", params) if err != nil { @@ -566,9 +599,55 @@ func (self *SRegion) UpdateLoadbalancerListener(lbid string, listenerId string, return resp.GetString("RequestId") } +// https://cloud.tencent.com/document/api/214/3601 +func (self *SRegion) updateClassicLoadbalancerListener(lbid string, listenerId string, listenerName *string, scheduler *string, sessionExpireTime *int, healthCheck *healthCheck, cert *certificate) (string, error) { + params := map[string]string{ + "loadBalancerId": lbid, + "listenerId": listenerId, + } + + if listenerName != nil && len(*listenerName) > 0 { + params["listenerName"] = *listenerName + } + + if scheduler != nil && len(*scheduler) > 0 { + params["scheduler"] = strings.ToLower(*scheduler) + } + + if sessionExpireTime != nil { + params["sessionExpire"] = strconv.Itoa(*sessionExpireTime) + } + + params = healthCheckParams(LB_TYPE_APPLICATION, params, healthCheck, "listeners.0.") + params = certificateParams(LB_TYPE_APPLICATION, params, cert, "listeners.0.") + + resp, err := self.lbRequest("ModifyLoadBalancerListener", params) + if err != nil { + return "", err + } + + return resp.GetString("RequestId") +} + +func (self *SRegion) UpdateLoadbalancerListener(t LB_TYPE, lbid string, listenerId string, listenerName *string, scheduler *string, sessionExpireTime *int, healthCheck *healthCheck, cert *certificate) (string, error) { + if len(lbid) == 0 { + return "", fmt.Errorf("loadbalancer id should not be empty") + } + + if len(listenerId) == 0 { + return "", fmt.Errorf("loadbalancer listener id should not be empty") + } + + if t == LB_TYPE_APPLICATION { + return self.updateLoadbalancerListener(lbid, listenerId, listenerName, scheduler, sessionExpireTime, healthCheck, cert) + } else { + return self.updateClassicLoadbalancerListener(lbid, listenerId, listenerName, scheduler, sessionExpireTime, healthCheck, cert) + } +} + func getHealthCheck(listener *cloudprovider.SLoadbalancerListener) *healthCheck { var hc *healthCheck - if listener.HealthCheck == models.LB_HEALTH_CHECK_ENABLE { + if listener.HealthCheck == models.LB_BOOL_ON { hc = &healthCheck{ HealthSwitch: 1, UnHealthNum: listener.HealthCheckFail, @@ -619,6 +698,21 @@ func getProtocol(listener *cloudprovider.SLoadbalancerListener) string { } } +func getClassicLBProtocol(listener *cloudprovider.SLoadbalancerListener) int { + switch listener.ListenerType { + case models.LB_LISTENER_TYPE_HTTP: + return 1 + case models.LB_LISTENER_TYPE_HTTPS: + return 4 + case models.LB_LISTENER_TYPE_TCP: + return 2 + case models.LB_LISTENER_TYPE_UDP: + return 3 + default: + return 0 // 非法值 + } +} + func getScheduler(listener *cloudprovider.SLoadbalancerListener) *string { var sch string switch listener.Scheduler { @@ -626,6 +720,8 @@ func getScheduler(listener *cloudprovider.SLoadbalancerListener) *string { sch = "WRR" case models.LB_SCHEDULER_WLC: sch = "LEAST_CONN" + case models.LB_SCHEDULER_SCH: + sch = "IP_HASH" default: return nil } @@ -633,32 +729,54 @@ func getScheduler(listener *cloudprovider.SLoadbalancerListener) *string { return &sch } -func healthCheckParams(params map[string]string, hc *healthCheck) map[string]string { - if hc != nil { - params["HealthCheck.HealthSwitch"] = strconv.Itoa(hc.HealthSwitch) - params["HealthCheck.TimeOut"] = strconv.Itoa(hc.TimeOut) - params["HealthCheck.IntervalTime"] = strconv.Itoa(hc.IntervalTime) - params["HealthCheck.HealthNum"] = strconv.Itoa(hc.HealthNum) - params["HealthCheck.UnHealthNum"] = strconv.Itoa(hc.UnHealthNum) +func healthCheckParams(t LB_TYPE, params map[string]string, hc *healthCheck, paramPrefix string) map[string]string { + if hc == nil { + return params + } + + if t == LB_TYPE_APPLICATION { + params[paramPrefix+"HealthSwitch"] = strconv.Itoa(hc.HealthSwitch) + params[paramPrefix+"TimeOut"] = strconv.Itoa(hc.TimeOut) + params[paramPrefix+"IntervalTime"] = strconv.Itoa(hc.IntervalTime) + params[paramPrefix+"HealthNum"] = strconv.Itoa(hc.HealthNum) + params[paramPrefix+"UnHealthNum"] = strconv.Itoa(hc.UnHealthNum) if hc.HTTPCode > 0 { - params["HealthCheck.HttpCode"] = strconv.Itoa(hc.HTTPCode) - params["HealthCheck.HttpCheckPath"] = hc.HTTPCheckPath - params["HealthCheck.HttpCheckDomain"] = hc.HTTPCheckDomain - params["HealthCheck.HttpCheckMethod"] = hc.HTTPCheckMethod + params[paramPrefix+"HttpCode"] = strconv.Itoa(hc.HTTPCode) + params[paramPrefix+"HttpCheckPath"] = hc.HTTPCheckPath + params[paramPrefix+"HttpCheckDomain"] = hc.HTTPCheckDomain + params[paramPrefix+"HttpCheckMethod"] = hc.HTTPCheckMethod + } + } else { + params[paramPrefix+"healthSwitch"] = strconv.Itoa(hc.HealthSwitch) + params[paramPrefix+"timeOut"] = strconv.Itoa(hc.TimeOut) + params[paramPrefix+"intervalTime"] = strconv.Itoa(hc.IntervalTime) + params[paramPrefix+"healthNum"] = strconv.Itoa(hc.HealthNum) + params[paramPrefix+"unHealthNum"] = strconv.Itoa(hc.UnHealthNum) + if hc.HTTPCode > 0 { + params[paramPrefix+"httpCode"] = strconv.Itoa(hc.HTTPCode) + params[paramPrefix+"httpCheckPath"] = hc.HTTPCheckPath } } - return params } -func certificateParams(params map[string]string, cert *certificate) map[string]string { - if cert != nil { - params["Certificate.SSLMode"] = cert.SSLMode - params["Certificate.CertId"] = cert.CERTID +func certificateParams(t LB_TYPE, params map[string]string, cert *certificate, paramPrefix string) map[string]string { + if cert == nil { + return params + } + + if t == LB_TYPE_APPLICATION { + params[paramPrefix+"SSLMode"] = cert.SSLMode + params[paramPrefix+"CertId"] = cert.CERTID if len(cert.CERTCAID) > 0 { - params["Certificate.CertCaId"] = cert.CERTCAID + params[paramPrefix+"CertCaId"] = cert.CERTCAID + } + } else { + params[paramPrefix+"SSLMode"] = strings.ToLower(cert.SSLMode) + params[paramPrefix+"certId"] = cert.CERTID + if len(cert.CERTCAID) > 0 { + params[paramPrefix+"certCaId"] = cert.CERTCAID } } - return params } diff --git a/pkg/util/qcloud/loadbalancer_listenerrule.go b/pkg/util/qcloud/loadbalancer_listenerrule.go index 4be8bf7b48..069c6abea3 100755 --- a/pkg/util/qcloud/loadbalancer_listenerrule.go +++ b/pkg/util/qcloud/loadbalancer_listenerrule.go @@ -43,7 +43,6 @@ func (self *SLBListenerRule) GetGlobalId() string { return self.LocationID } -// todo: rule status?? func (self *SLBListenerRule) GetStatus() string { return "" } @@ -57,10 +56,7 @@ func (self *SLBListenerRule) Refresh() error { for _, rule := range self.listener.Rules { if rule.GetId() == self.GetId() { rule.listener = self.listener - err := jsonutils.Update(self, rule) - if err != nil { - return err - } + return jsonutils.Update(self, rule) } } diff --git a/pkg/util/qcloud/qcloud.go b/pkg/util/qcloud/qcloud.go index 32b9ab10d5..5695735f55 100644 --- a/pkg/util/qcloud/qcloud.go +++ b/pkg/util/qcloud/qcloud.go @@ -78,16 +78,22 @@ func cbsRequest(client *common.Client, apiName string, params map[string]string) return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params) } -// loadbalancer服务 +// loadbalancer服务 api 3.0 func clbRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { domain := apiDomain("clb", params) return _jsonRequest(client, domain, QCLOUD_CLB_API_VERSION, apiName, params) } +// loadbalancer服务 api 2017 +func lbRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + domain := "lb.api.qcloud.com" + return _phpJsonRequest(client, &lbJsonResponse{}, domain, "/v2/index.php", "", apiName, params) +} + // ssl 证书服务 func wssRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { domain := "wss.api.qcloud.com" - return _wssJsonRequest(client, domain, "/v2/index.php", "", apiName, params) + return _phpJsonRequest(client, &wssJsonResponse{}, domain, "/v2/index.php", "", apiName, params) } func billingRequest(client *common.Client, apiName string, params map[string]string) (jsonutils.JSONObject, error) { @@ -95,18 +101,18 @@ func billingRequest(client *common.Client, apiName string, params map[string]str return _jsonRequest(client, domain, QCLOUD_BILLING_API_VERSION, apiName, params) } -// ============wssJsonRequest============ +// ============phpJsonRequest============ type qcloudResponse interface { tchttp.Response GetResponse() *interface{} } -type wssJsonRequest struct { +type phpJsonRequest struct { tchttp.BaseRequest Path string } -func (r *wssJsonRequest) GetUrl() string { +func (r *phpJsonRequest) GetUrl() string { url := r.BaseRequest.GetUrl() if url == "" { return url @@ -115,7 +121,7 @@ func (r *wssJsonRequest) GetUrl() string { index := strings.Index(url, "?") if index == -1 { // POST request - url = strings.TrimSuffix(url, "/") + r.Path + return strings.TrimSuffix(url, "/") + r.Path } p1, p2 := url[:index], url[index:] @@ -123,10 +129,11 @@ func (r *wssJsonRequest) GetUrl() string { return p1 + r.Path + p2 } -func (r *wssJsonRequest) GetPath() string { +func (r *phpJsonRequest) GetPath() string { return r.Path } +// SSL证书专用response type wssJsonResponse struct { Code int `json:"code"` CodeDesc string `json:"codeDesc"` @@ -151,8 +158,37 @@ func (r *wssJsonResponse) GetResponse() *interface{} { return r.Response } -// ================================== +// 2017版负载均衡API专用response +type lbJsonResponse struct { + Response map[string]interface{} +} +func (r *lbJsonResponse) ParseErrorFromHTTPResponse(body []byte) (err error) { + resp := &wssJsonResponse{} + err = json.Unmarshal(body, resp) + if err != nil { + return + } + if resp.Code != 0 { + return errors.NewTencentCloudSDKError(resp.CodeDesc, resp.Message, "") + } + + // hook 由于目前只能从这个方法中拿到原始的body.这里将原始body hook 到 Response + err = json.Unmarshal(body, &r.Response) + if err != nil { + return + } + + return nil +} + +func (r *lbJsonResponse) GetResponse() *interface{} { + return func(resp interface{}) *interface{} { + return &resp + }(r.Response) +} + +// 3.0版本通用response type QcloudResponse struct { *tchttp.BaseResponse Response *interface{} `json:"Response"` @@ -182,13 +218,13 @@ func _jsonRequest(client *common.Client, domain string, version string, apiName resp := &QcloudResponse{ BaseResponse: &tchttp.BaseResponse{}, } - return _baseJsonRequest(client, req, resp) } -// wss 专用的 -func _wssJsonRequest(client *common.Client, domain string, path string, version string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { - req := &wssJsonRequest{Path: path} +// 老版本腾讯云api。 适用于类似 https://cvm.api.qcloud.com/v2/index.php 这样的带/v2/index.php路径的接口 +// todo: 添加自定义response参数 +func _phpJsonRequest(client *common.Client, resp qcloudResponse, domain string, path string, version string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + req := &phpJsonRequest{Path: path} if region, ok := params["Region"]; ok { client = client.Init(region) } @@ -204,7 +240,6 @@ func _wssJsonRequest(client *common.Client, domain string, path string, version req.GetParams()[k] = v } - resp := &wssJsonResponse{} return _baseJsonRequest(client, req, resp) } @@ -269,6 +304,14 @@ func (client *SQcloudClient) clbRequest(apiName string, params map[string]string return clbRequest(cli, apiName, params) } +func (client *SQcloudClient) lbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cli, err := client.getDefaultClient() + if err != nil { + return nil, err + } + return lbRequest(cli, apiName, params) +} + func (client *SQcloudClient) wssRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { cli, err := client.getDefaultClient() if err != nil { diff --git a/pkg/util/qcloud/region.go b/pkg/util/qcloud/region.go index 87c0a8d87f..c4c9050ed4 100644 --- a/pkg/util/qcloud/region.go +++ b/pkg/util/qcloud/region.go @@ -34,20 +34,6 @@ type SRegion struct { fetchLocation bool } -// todo: 确认这个方法是不是有问题 -func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) { - certs, err := self.GetILoadBalancerCertificatesById(certId) - if err != nil { - return nil, err - } - - if len(certs) == 1 { - return certs[0], nil - } else { - return nil, fmt.Errorf("GetILoadBalancerCertificateById %d certificate found", len(certs)) - } -} - func (self *SRegion) GetILoadBalancers() ([]cloudprovider.ICloudLoadbalancer, error) { lbs, err := self.GetLoadbalancers(nil) if err != nil { @@ -68,16 +54,6 @@ func (self *SRegion) GetILoadBalancerAcls() ([]cloudprovider.ICloudLoadbalancerA return []cloudprovider.ICloudLoadbalancerAcl{}, nil } -func inList(lst []string, entry string) bool { - for _, m := range lst { - if entry == m { - return true - } - } - - return false -} - func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadbalancerCertificate, error) { lbs, err := self.GetLoadbalancers(nil) if err != nil { @@ -93,39 +69,39 @@ func (self *SRegion) GetILoadBalancerCertificates() ([]cloudprovider.ICloudLoadb certIds := []string{} for _, listener := range listeners { - if len(listener.Certificate.CERTID) > 0 && inList(certIds, listener.Certificate.CERTID) { + if len(listener.Certificate.CERTID) > 0 && !utils.IsInStringArray(listener.Certificate.CERTID, certIds) { certIds = append(certIds, listener.Certificate.CERTID) } - if len(listener.Certificate.CERTCAID) > 0 && inList(certIds, listener.Certificate.CERTCAID) { + if len(listener.Certificate.CERTCAID) > 0 && !utils.IsInStringArray(listener.Certificate.CERTCAID, certIds) { certIds = append(certIds, listener.Certificate.CERTCAID) } for _, rule := range listener.Rules { - if len(rule.Certificate.CERTID) > 0 && inList(certIds, rule.Certificate.CERTID) { + if len(rule.Certificate.CERTID) > 0 && !utils.IsInStringArray(rule.Certificate.CERTID, certIds) { certIds = append(certIds, rule.Certificate.CERTID) } - if len(rule.Certificate.CERTCAID) > 0 && inList(certIds, rule.Certificate.CERTCAID) { + if len(rule.Certificate.CERTCAID) > 0 && !utils.IsInStringArray(rule.Certificate.CERTCAID, certIds) { certIds = append(certIds, rule.Certificate.CERTCAID) } } } for _, cid := range certIds { - parts, err := self.GetILoadBalancerCertificatesById(cid) + icert, err := self.GetILoadBalancerCertificateById(cid) if err != nil { return nil, err } - icerts = append(icerts, parts...) + icerts = append(icerts, icert) } } return icerts, nil } -func (self *SRegion) GetILoadBalancerCertificatesById(certId string) ([]cloudprovider.ICloudLoadbalancerCertificate, error) { +func (self *SRegion) GetILoadBalancerCertificateById(certId string) (cloudprovider.ICloudLoadbalancerCertificate, error) { certs, _, err := self.GetCertificates(certId, true, 0, 0) if err != nil { return nil, err @@ -136,7 +112,12 @@ func (self *SRegion) GetILoadBalancerCertificatesById(certId string) ([]cloudpro cert := SLBCertificate{region: self, SCertificate: certs[i]} icerts = append(icerts, &cert) } - return icerts, nil + + if len(certs) == 1 { + return icerts[0], nil + } else { + return nil, fmt.Errorf("GetILoadBalancerCertificateById %d certificate found, expect 1", len(certs)) + } } func (self *SRegion) GetILoadBalancerById(loadbalancerId string) (cloudprovider.ICloudLoadbalancer, error) { @@ -158,11 +139,8 @@ func (self *SRegion) GetILoadBalancerAclById(aclId string) (cloudprovider.ICloud } // https://cloud.tencent.com/document/api/214/30692 -/* -todo: -Forward 否 Integer 负载均衡实例。1:应用型,0:传统型,默认为应用型负载均衡实例。 -ProjectId 否 Integer 负载均衡实例所属的项目 ID,可以通过 DescribeProject 接口获取。不填则属于默认项目。 -*/ +// todo: 1. 支持跨地域绑定负载均衡 及 https://cloud.tencent.com/document/product/214/12014 +// todo: 2. 支持指定Project。 ProjectId可以通过 DescribeProject 接口获取。不填则属于默认项目。 func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalancer) (cloudprovider.ICloudLoadbalancer, error) { LoadBalancerType := "INTERNAL" if loadbalancer.AddressType == "public" { @@ -171,7 +149,7 @@ func (self *SRegion) CreateILoadBalancer(loadbalancer *cloudprovider.SLoadbalanc params := map[string]string{ "LoadBalancerType": LoadBalancerType, "LoadBalancerName": loadbalancer.Name, - "VpcId": loadbalancer.VpcID, // todo: vpc id ok?? + "VpcId": loadbalancer.VpcID, "SubnetId": loadbalancer.NetworkID, } @@ -630,6 +608,11 @@ func (self *SRegion) clbRequest(apiName string, params map[string]string) (jsonu return self.client.clbRequest(apiName, params) } +func (self *SRegion) lbRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { + params["Region"] = self.Region + return self.client.lbRequest(apiName, params) +} + func (self *SRegion) wssRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) { return self.client.wssRequest(apiName, params) } From 119516736c5014905247002a4d07cc0df5aa2ad9 Mon Sep 17 00:00:00 2001 From: TangBin Date: Mon, 18 Feb 2019 17:46:24 +0800 Subject: [PATCH 15/15] add qcloud driver lb sync method && add comment --- pkg/compute/regiondrivers/managedvirtual.go | 90 +------ pkg/compute/regiondrivers/qcloud.go | 278 ++++++++++++++++++++ pkg/util/qcloud/loadbalancer.go | 6 + 3 files changed, 288 insertions(+), 86 deletions(-) diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 978f063b5d..ab721fa324 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -332,12 +332,6 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend if err != nil { return nil, err } - - // 腾讯云本身没有后端服务器组,因此不需要在qcloud端执行创建操作 - if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { - return nil, nil - } - loadbalancer := lbbg.GetLoadbalancer() if loadbalancer == nil { return nil, fmt.Errorf("failed to find loadbalancer for backendgroup %s", lbbg.Name) @@ -409,17 +403,6 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend if lbbg == nil { return nil, fmt.Errorf("failed to find lbbg for backend %s", lbb.Name) } - - guest := lbb.GetGuest() - if guest == nil { - return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name) - } - - // 兼容腾讯云,在fake的backend group 关联具体的转发策略之前。不需要同步后端服务器 - if lbbg.GetProviderName() == models.CLOUD_PROVIDER_QCLOUD && lbbg.RefCount() == 0 { - return nil, nil - } - lb := lbbg.GetLoadbalancer() if lb == nil { return nil, fmt.Errorf("failed to find lb for backendgroup %s", lbbg.Name) @@ -436,6 +419,10 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend if err != nil { return nil, err } + guest := lbb.GetGuest() + if guest == nil { + return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name) + } iLoadbalancerBackend, err := iLoadbalancerBackendGroup.AddBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) if err != nil { return nil, err @@ -465,15 +452,6 @@ func (self *SManagedVirtualizationRegionDriver) RequestDeleteLoadbalancerBackend if err != nil { return nil, err } - - // ===========兼容腾讯云,未关联具体转发规则时,直接删除本地数据即可=============== - if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { - count := lbbg.RefCount() - if count == 0 { - return nil, nil - } - } - iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) if err != nil { return nil, err @@ -516,40 +494,6 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerListene if err := lblis.SetExternalId(iListener.GetGlobalId()); err != nil { return nil, err } - - // ====腾讯云添加后端服务器===== - if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { - group := lblis.GetLoadbalancerBackendGroup() - if group != nil { - backends, err := group.GetBackends() - if err != nil { - return nil, fmt.Errorf("failed to find backends for backend group %s: %s", group.GetId(), err) - } - - extBgID := iListener.GetBackendGroupId() - if len(extBgID) == 0 { - return nil, fmt.Errorf("the backend group external id of loadbalancer listener %s is empty", lblis.GetId()) - } - - ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(extBgID) - if err != nil { - return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s: %s", lblis.GetId(), err) - } - - for _, backend := range backends { - guest := backend.GetGuest() - if guest == nil { - return nil, fmt.Errorf("failed to find instance for loadbalancer backend %s", backend.GetId()) - } - _, err := ilbbg.AddBackendServer(guest.GetExternalId(), backend.Weight, backend.Port) - if err != nil { - return nil, err - } - } - } - } - // ========================== - return nil, lblis.SyncWithCloudLoadbalancerListener(ctx, userCred, loadbalancer, iListener, "", false) }) return nil @@ -732,32 +676,6 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerListene if err := lbr.SetExternalId(iListenerRule.GetGlobalId()); err != nil { return nil, err } - // ====腾讯云添加后端服务器===== - if listener.GetProviderName() == models.CLOUD_PROVIDER_QCLOUD && len(rule.BackendGroupID) > 0 { - ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(rule.BackendGroupID) - if err != nil { - return nil, fmt.Errorf("failed to find backend group for listener rule %s: %s", lbr.Name, err) - } - - group := lbr.GetLoadbalancerBackendGroup() - backends, err := group.GetBackends() - if err != nil { - return nil, fmt.Errorf("failed to find backends for backend group %s: %s", group.GetId(), err) - } - - for _, backend := range backends { - guest := backend.GetGuest() - if guest == nil { - return nil, fmt.Errorf("failed to find instance for loadbalancer backend %s", backend.GetId()) - } - _, err := ilbbg.AddBackendServer(guest.GetExternalId(), backend.Weight, backend.Port) - if err != nil { - return nil, err - } - } - - } - // ========================== return nil, lbr.SyncWithCloudLoadbalancerListenerRule(ctx, userCred, iListenerRule, "", false) }) return nil diff --git a/pkg/compute/regiondrivers/qcloud.go b/pkg/compute/regiondrivers/qcloud.go index 6aed19e9be..c4617cf423 100644 --- a/pkg/compute/regiondrivers/qcloud.go +++ b/pkg/compute/regiondrivers/qcloud.go @@ -1,7 +1,14 @@ package regiondrivers import ( + "context" + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/mcclient" ) type SQcloudRegionDriver struct { @@ -16,3 +23,274 @@ func init() { func (self *SQcloudRegionDriver) GetProvider() string { return models.CLOUD_PROVIDER_QCLOUD } + +func (self *SQcloudRegionDriver) RequestCreateLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lbbg *models.SLoadbalancerBackendGroup, backends []cloudprovider.SLoadbalancerBackend, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + iRegion, err := lbbg.GetIRegion() + if err != nil { + return nil, err + } + + // 腾讯云本身没有后端服务器组,因此不需要在qcloud端执行创建操作 + if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { + return nil, nil + } + + loadbalancer := lbbg.GetLoadbalancer() + if loadbalancer == nil { + return nil, fmt.Errorf("failed to find loadbalancer for backendgroup %s", lbbg.Name) + } + iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId) + if err != nil { + return nil, err + } + group := &cloudprovider.SLoadbalancerBackendGroup{ + Name: lbbg.Name, + GroupType: lbbg.Type, + Backends: backends, + } + iLoadbalancerBackendGroup, err := iLoadbalancer.CreateILoadBalancerBackendGroup(group) + if err != nil { + return nil, err + } + if err := lbbg.SetExternalId(iLoadbalancerBackendGroup.GetGlobalId()); err != nil { + return nil, err + } + iBackends, err := iLoadbalancerBackendGroup.GetILoadbalancerBackends() + if err != nil { + return nil, err + } + if len(iBackends) > 0 { + provider := loadbalancer.GetCloudprovider() + if provider == nil { + return nil, fmt.Errorf("failed to find cloudprovider for lb %s", loadbalancer.Name) + } + models.LoadbalancerBackendManager.SyncLoadbalancerBackends(ctx, userCred, provider, lbbg, iBackends, &models.SSyncRange{}) + } + return nil, nil + }) + return nil +} + +func (self *SQcloudRegionDriver) RequestCreateLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, lbb *models.SLoadbalancerBackend, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + lbbg := lbb.GetLoadbalancerBackendGroup() + if lbbg == nil { + return nil, fmt.Errorf("failed to find lbbg for backend %s", lbb.Name) + } + + guest := lbb.GetGuest() + if guest == nil { + return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name) + } + + // 兼容腾讯云,在fake的backend group 关联具体的转发策略之前。不需要同步后端服务器 + if lbbg.GetProviderName() == models.CLOUD_PROVIDER_QCLOUD && lbbg.RefCount() == 0 { + return nil, nil + } + + lb := lbbg.GetLoadbalancer() + if lb == nil { + return nil, fmt.Errorf("failed to find lb for backendgroup %s", lbbg.Name) + } + iRegion, err := lb.GetIRegion() + if err != nil { + return nil, err + } + iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) + if err != nil { + return nil, err + } + iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(lbbg.ExternalId) + if err != nil { + return nil, err + } + iLoadbalancerBackend, err := iLoadbalancerBackendGroup.AddBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) + if err != nil { + return nil, err + } + if err := lbb.SetExternalId(iLoadbalancerBackend.GetGlobalId()); err != nil { + return nil, err + } + return nil, lbb.SyncWithCloudLoadbalancerBackend(ctx, userCred, iLoadbalancerBackend, "", false) + }) + return nil +} + +func (self *SQcloudRegionDriver) RequestDeleteLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, lbb *models.SLoadbalancerBackend, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + if jsonutils.QueryBoolean(task.GetParams(), "purge", false) { + return nil, nil + } + lbbg := lbb.GetLoadbalancerBackendGroup() + if lbbg == nil { + return nil, fmt.Errorf("failed to find lbbg for backend %s", lbb.Name) + } + lb := lbbg.GetLoadbalancer() + if lb == nil { + return nil, fmt.Errorf("failed to find lb for backendgroup %s", lbbg.Name) + } + iRegion, err := lb.GetIRegion() + if err != nil { + return nil, err + } + + // ===========兼容腾讯云,未关联具体转发规则时,直接删除本地数据即可=============== + if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { + count := lbbg.RefCount() + if count == 0 { + return nil, nil + } + } + + iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) + if err != nil { + return nil, err + } + iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(lbbg.ExternalId) + if err != nil { + return nil, err + } + guest := lbb.GetGuest() + if guest == nil { + return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name) + } + return nil, iLoadbalancerBackendGroup.RemoveBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) + }) + return nil +} + +func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListener(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + params, err := lblis.GetLoadbalancerListenerParams() + if err != nil { + return nil, err + } + loadbalancer := lblis.GetLoadbalancer() + if loadbalancer == nil { + return nil, fmt.Errorf("failed to find loadbalancer for lblis %s", lblis.Name) + } + iRegion, err := loadbalancer.GetIRegion() + if err != nil { + return nil, err + } + iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId) + if err != nil { + return nil, err + } + iListener, err := iLoadbalancer.CreateILoadBalancerListener(params) + if err != nil { + return nil, err + } + if err := lblis.SetExternalId(iListener.GetGlobalId()); err != nil { + return nil, err + } + + // ====腾讯云添加后端服务器===== + if iRegion.GetProvider() == models.CLOUD_PROVIDER_QCLOUD { + group := lblis.GetLoadbalancerBackendGroup() + if group != nil { + backends, err := group.GetBackends() + if err != nil { + return nil, fmt.Errorf("failed to find backends for backend group %s: %s", group.GetId(), err) + } + + extBgID := iListener.GetBackendGroupId() + if len(extBgID) == 0 { + return nil, fmt.Errorf("the backend group external id of loadbalancer listener %s is empty", lblis.GetId()) + } + + ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(extBgID) + if err != nil { + return nil, fmt.Errorf("failed to find backend group for loadbalancer listener %s: %s", lblis.GetId(), err) + } + + for _, backend := range backends { + guest := backend.GetGuest() + if guest == nil { + return nil, fmt.Errorf("failed to find instance for loadbalancer backend %s", backend.GetId()) + } + _, err := ilbbg.AddBackendServer(guest.GetExternalId(), backend.Weight, backend.Port) + if err != nil { + return nil, err + } + } + } + } + + return nil, lblis.SyncWithCloudLoadbalancerListener(ctx, userCred, loadbalancer, iListener, "", false) + }) + return nil +} + +func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListenerRule(ctx context.Context, userCred mcclient.TokenCredential, lbr *models.SLoadbalancerListenerRule, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + listener := lbr.GetLoadbalancerListener() + if listener == nil { + return nil, fmt.Errorf("failed to find listener for listnener rule %s", lbr.Name) + } + loadbalancer := listener.GetLoadbalancer() + if loadbalancer == nil { + return nil, fmt.Errorf("failed to find loadbalancer for listener %s", listener.Name) + } + iRegion, err := loadbalancer.GetIRegion() + if err != nil { + return nil, err + } + iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId) + if err != nil { + return nil, err + } + iListener, err := iLoadbalancer.GetILoadBalancerListenerById(listener.ExternalId) + if err != nil { + return nil, err + } + rule := &cloudprovider.SLoadbalancerListenerRule{ + Name: lbr.Name, + Domain: lbr.Domain, + Path: lbr.Path, + } + if len(lbr.BackendGroupId) > 0 { + group := lbr.GetLoadbalancerBackendGroup() + if group == nil { + return nil, fmt.Errorf("failed to find backend group for listener rule %s", lbr.Name) + } + rule.BackendGroupID = group.ExternalId + rule.BackendGroupType = group.Type + } + iListenerRule, err := iListener.CreateILoadBalancerListenerRule(rule) + if err != nil { + return nil, err + } + if err := lbr.SetExternalId(iListenerRule.GetGlobalId()); err != nil { + return nil, err + } + // ====腾讯云添加后端服务器===== + if listener.GetProviderName() == models.CLOUD_PROVIDER_QCLOUD && len(rule.BackendGroupID) > 0 { + ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(rule.BackendGroupID) + if err != nil { + return nil, fmt.Errorf("failed to find backend group for listener rule %s: %s", lbr.Name, err) + } + + group := lbr.GetLoadbalancerBackendGroup() + backends, err := group.GetBackends() + if err != nil { + return nil, fmt.Errorf("failed to find backends for backend group %s: %s", group.GetId(), err) + } + + for _, backend := range backends { + guest := backend.GetGuest() + if guest == nil { + return nil, fmt.Errorf("failed to find instance for loadbalancer backend %s", backend.GetId()) + } + _, err := ilbbg.AddBackendServer(guest.GetExternalId(), backend.Weight, backend.Port) + if err != nil { + return nil, err + } + } + + } + return nil, lbr.SyncWithCloudLoadbalancerListenerRule(ctx, userCred, iListenerRule, "", false) + }) + return nil +} diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index 8e7fd79611..1af91e969d 100755 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -24,6 +24,12 @@ const ( LB_TYPE_APPLICATION = LB_TYPE(1) ) +/* +todo: +1.统一LB 证书fingerprint算法.另外缺少一个回写指纹算法到数据库的方法。 +2.需要同步腾讯云LB 所在的project +*/ + // https://cloud.tencent.com/document/api/214/30694#LoadBalancer type SLoadbalancer struct { region *SRegion