Merge pull request #1017 in YUNIONIO/onecloud from ~TANGBIN/onecloud:feature/tb-qcloud-loadbalance-sync to release/2.6.0

* commit '8fdc9ec93d7be882b988899617c403ae90af7f4d':
  add qcloud driver lb sync method && add comment
  bugfix & comment fix
  backend group sync bugfix
  make fmt
  add sync
  add sync
  getNetworkOfIPOnHost fix
  getNetworkOfIPOnHost fix
  add loadbalancer operation method && qcloud GetINetwork fix
  add loadbalancer operation method
  create init
  impement method fix
  impement method fix
  impement method fix
  qcloud loadbalance sync
This commit is contained in:
邱剑
2019-02-19 15:35:12 +08:00
21 changed files with 2918 additions and 103 deletions
+1 -1
View File
@@ -2133,7 +2133,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
return host.getNetworkOfIPOnHost(vnic.GetIP())
return host.getNetworkOfIPOnHost(ip)
}
}
localNetObj, err := NetworkManager.FetchByExternalId(vnet.GetGlobalId())
+8 -8
View File
@@ -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 && network != 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) {
+2
View File
@@ -181,6 +181,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"
@@ -189,6 +190,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,
+49 -16
View File
@@ -177,27 +177,54 @@ 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
}
// 返回值 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())
}
}
@@ -362,7 +389,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()
}
@@ -397,8 +428,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
+5 -1
View File
@@ -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 {
@@ -367,6 +368,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
}
@@ -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
}
@@ -116,9 +117,17 @@ 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 := lbbg.RefCount()
if count > 0 {
return nil, fmt.Errorf("backendgroup already related with other listener/rule")
}
}
}
}
err := loadbalancerListenerRuleCheckUniqueness(ctx, listener, domainV.Value, pathV.Value)
@@ -334,6 +343,18 @@ 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 {
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", lbbg.GetId(), err)
}
}
}
if backendgroup, err := LoadbalancerBackendGroupManager.FetchByExternalId(groupId); err == nil {
lbr.BackendGroupId = backendgroup.GetId()
}
+40 -17
View File
@@ -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
}
@@ -218,9 +221,17 @@ 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 := lbbg.RefCount()
if count > 0 {
return nil, fmt.Errorf("backendgroup aready related with other listener/rule")
}
}
}
}
{
@@ -763,6 +774,18 @@ 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 {
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", lbbg.GetId(), err)
}
}
}
if len(groupId) == 0 {
lblis.BackendGroupId = lb.BackendGroupId
} else if group, err := LoadbalancerBackendGroupManager.FetchByExternalId(groupId); err == nil {
+11 -1
View File
@@ -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
@@ -66,7 +67,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_optional"`
}
func (man *SLoadbalancerManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
@@ -551,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
@@ -581,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
}
+278
View File
@@ -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
}
+2 -2
View File
@@ -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"
}
+118
View File
@@ -0,0 +1,118 @@
package qcloud
import (
"fmt"
"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
}
// 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
}
return resp.GetString("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}
_, err := self.wssRequest("CertDelete", params)
if err != nil {
return err
}
return nil
}
+1 -1
View File
@@ -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
+579
View File
@@ -0,0 +1,579 @@
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"
)
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)
)
/*
todo:
1.统一LB 证书fingerprint算法.另外缺少一个回写指纹算法到数据库的方法。
2.需要同步腾讯云LB 所在的project
*/
// 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) GetLoadbalancerSpec() string {
return ""
}
func (self *SLoadbalancer) GetChargeType() string {
return models.LB_CHARGE_TYPE_BY_HOUR
}
// https://cloud.tencent.com/document/product/214/30689
func (self *SLoadbalancer) Delete() error {
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)
}
// 腾讯云loadbalance不支持启用/禁用
func (self *SLoadbalancer) Start() error {
return cloudprovider.ErrNotSupported
}
func (self *SLoadbalancer) Stop() error {
return cloudprovider.ErrNotSupported
}
// 腾讯云无后端服务器组
// todo: 是否返回一个fake的后端服务器组
func (self *SLoadbalancer) CreateILoadBalancerBackendGroup(group *cloudprovider.SLoadbalancerBackendGroup) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
return nil, cloudprovider.ErrNotSupported
}
func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
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 onecloudHealthCodeToQcloud(codes string) int {
qcode := 0
for i, code := range HTTP_CODES {
if strings.Contains(code, codes) {
// 按位或然后再赋值qcode
qcode |= 1 << uint(i)
}
}
return qcode
}
// https://cloud.tencent.com/document/product/214/30693
// 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)
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
}
time.Sleep(3 * time.Second)
return self.GetILoadBalancerListenerById(listenId)
}
func (self *SLoadbalancer) GetILoadBalancerListenerById(listenerId string) (cloudprovider.ICloudLoadbalancerListener, error) {
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 {
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
}
// 腾讯云当前不支持一个LB绑定多个ip,每个LB只支持绑定一个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, protocal)
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{}
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 += len(parts)
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 {
return nil, err
}
switch len(lbs) {
case 0:
return nil, cloudprovider.ErrNotFound
case 1:
return &lbs[0], nil
default:
return nil, fmt.Errorf("GetLoadbalancer %s found %d", id, len(lbs))
}
}
/*
返回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")
}
/*
返回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 特性是什么??
*/
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")
}
params := map[string]string{
"LoadBalancerId": lbid,
"Ports.0": strconv.Itoa(port),
"Protocol": protocol,
}
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
}
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 {
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/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) {
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
}
_status, err := status.Float()
return fmt.Sprintf("%1.f", _status), 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
}
+41
View File
@@ -0,0 +1,41 @@
package qcloud
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/compute/models"
)
// 腾讯云没有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 models.LB_BOOL_OFF
}
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
}
+171
View File
@@ -0,0 +1,171 @@
package qcloud
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"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"`
}
// ==========================================================
// backend InstanceID + protocol +Port + ip + rip全局唯一
func (self *SLBBackend) GetId() string {
return fmt.Sprintf("%s/%s-%d", self.group.GetId(), self.InstanceID, self.Port)
}
func (self *SLBBackend) GetName() string {
return self.GetId()
}
func (self *SLBBackend) GetGlobalId() string {
return self.GetId()
}
func (self *SLBBackend) GetStatus() string {
return ""
}
func (self *SLBBackend) Refresh() error {
backends, err := self.group.GetBackends()
if err != nil {
return err
}
for _, backend := range backends {
if backend.GetId() == self.GetId() {
return jsonutils.Update(self, backend)
}
}
return cloudprovider.ErrNotFound
}
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
}
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
}
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 for http/https listener %s must specific rule id", listenerId)
}
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)
}
}
+223
View File
@@ -0,0 +1,223 @@
package qcloud
import (
"fmt"
"strconv"
"time"
"strings"
"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
}
// 返回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(),
"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) {
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 strings.HasSuffix(backend.GetId(), fmt.Sprintf("%s-%d", serverId, port)) {
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 {
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 {
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 fmt.Errorf("Please remove related listener/rule frist")
}
// 腾讯云无后端服务器组
func (self *SLBBackendGroup) Sync(name string) error {
return nil
}
func backendGroupIdGen(lbid string, secondId string) string {
if len(secondId) > 0 {
return fmt.Sprintf("%s/%s", lbid, secondId)
} else {
return lbid
}
}
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())
} 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 models.LB_STATUS_ENABLED
}
func (self *SLBBackendGroup) Refresh() error {
return nil
}
// note: model没有更新这个字段?
func (self *SLBBackendGroup) IsEmulated() bool {
return true
}
func (self *SLBBackendGroup) GetMetadata() *jsonutils.JSONDict {
return nil
}
func (self *SLBBackendGroup) IsDefault() bool {
return false
}
func (self *SLBBackendGroup) GetType() string {
return models.LB_BACKENDGROUP_TYPE_NORMAL
}
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 {
// 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
}
}
for i := range backends {
backends[i].group = self
}
return backends, nil
}
+83
View File
@@ -0,0 +1,83 @@
package qcloud
import (
"crypto/sha1"
"fmt"
"strconv"
"strings"
"time"
"yunion.io/x/jsonutils"
)
type SLBCertificate struct {
region *SRegion
SCertificate
}
// 证书不能修改
func (self *SLBCertificate) Sync(name, privateKey, publickKey string) error {
return nil
}
func (self *SLBCertificate) Delete() error {
return self.region.DeleteCertificate(self.GetId())
}
func (self *SLBCertificate) GetId() string {
return self.ID
}
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("Expecting 1 certificate, got %d", 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
}
func (self *SLBCertificate) GetCommonName() string {
return self.Domain
}
func (self *SLBCertificate) GetSubjectAlternativeNames() string {
return strings.Join(self.SubjectAltName, ",")
}
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
}
+782
View File
@@ -0,0 +1,782 @@
package qcloud
import (
"fmt"
"strconv"
"strings"
"time"
"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 int `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。
}
// 腾讯云后端端口不是与listener绑定的
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) {
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) {
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 {
return nil
}
func (self *SLBListener) Stop() error {
return cloudprovider.ErrNotSupported
}
// https://cloud.tencent.com/document/product/214/30677
func (self *SLBListener) Sync(listener *cloudprovider.SLoadbalancerListener) error {
hc := getHealthCheck(listener)
cert := getCertificate(listener)
requestId, err := self.lb.region.UpdateLoadbalancerListener(
self.lb.Forward,
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 {
requestId, err := self.lb.region.DeleteLoadbalancerListener(self.lb.Forward, 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
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 int `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 {
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 {
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 models.LB_BOOL_OFF
}
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_BOOL_OFF
} else {
return models.LB_BOOL_ON
}
}
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
}
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
}
// 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 ""
}
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
}
// 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 ""
}
// 负载均衡能力说明 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
}
// 返回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")
}
// 返回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 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,
}
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(LB_TYPE_APPLICATION, params, healthCheck, "HealthCheck.")
params = certificateParams(LB_TYPE_APPLICATION, params, cert, "Certificate.")
resp, err := self.clbRequest("ModifyListener", params)
if err != nil {
return "", err
}
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_BOOL_ON {
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 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 {
case models.LB_SCHEDULER_WRR:
sch = "WRR"
case models.LB_SCHEDULER_WLC:
sch = "LEAST_CONN"
case models.LB_SCHEDULER_SCH:
sch = "IP_HASH"
default:
return nil
}
return &sch
}
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[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(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[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
}
+138
View File
@@ -0,0 +1,138 @@
package qcloud
import (
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/cloudprovider"
"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"`
}
// https://cloud.tencent.com/document/api/214/30688
func (self *SLBListenerRule) Delete() error {
_, 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 {
return self.LocationID
}
func (self *SLBListenerRule) GetName() string {
return self.LocationID
}
func (self *SLBListenerRule) GetGlobalId() string {
return self.LocationID
}
func (self *SLBListenerRule) GetStatus() string {
return ""
}
func (self *SLBListenerRule) Refresh() error {
err := self.listener.Refresh()
if err != nil {
return err
}
for _, rule := range self.listener.Rules {
if rule.GetId() == self.GetId() {
rule.listener = self.listener
return jsonutils.Update(self, rule)
}
}
return cloudprovider.ErrNotFound
}
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()
}
// 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")
}
+181 -14
View File
@@ -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,153 @@ 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服务 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 _phpJsonRequest(client, &wssJsonResponse{}, 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)
}
// ============phpJsonRequest============
type qcloudResponse interface {
tchttp.Response
GetResponse() *interface{}
}
type phpJsonRequest struct {
tchttp.BaseRequest
Path string
}
func (r *phpJsonRequest) GetUrl() string {
url := r.BaseRequest.GetUrl()
if url == "" {
return url
}
index := strings.Index(url, "?")
if index == -1 {
// POST request
return strings.TrimSuffix(url, "/") + r.Path
}
p1, p2 := url[:index], url[index:]
p1 = strings.TrimSuffix(p1, "/")
return p1 + r.Path + p2
}
func (r *phpJsonRequest) GetPath() string {
return r.Path
}
// SSL证书专用response
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
}
// 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"`
}
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 +214,36 @@ func _jsonRequest(client *common.Client, domain string, version string, apiName
}
req.GetParams()[k] = v
}
resp := &QcloudResponse{
BaseResponse: &tchttp.BaseResponse{},
}
return _baseJsonRequest(client, req, resp)
}
// 老版本腾讯云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)
}
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
}
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 +261,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 +296,30 @@ 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) 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 {
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 {
+182 -40
View File
@@ -2,6 +2,7 @@ package qcloud
import (
"fmt"
"time"
"github.com/nelsonken/cos-go-sdk-v5/cos"
@@ -33,6 +34,173 @@ type SRegion struct {
fetchLocation bool
}
func (self *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
return nil, cloudprovider.ErrNotImplemented
}
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 (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 && !utils.IsInStringArray(listener.Certificate.CERTID, certIds) {
certIds = append(certIds, listener.Certificate.CERTID)
}
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 && !utils.IsInStringArray(rule.Certificate.CERTID, certIds) {
certIds = append(certIds, rule.Certificate.CERTID)
}
if len(rule.Certificate.CERTCAID) > 0 && !utils.IsInStringArray(rule.Certificate.CERTCAID, certIds) {
certIds = append(certIds, rule.Certificate.CERTCAID)
}
}
}
for _, cid := range certIds {
icert, err := self.GetILoadBalancerCertificateById(cid)
if err != nil {
return nil, err
}
icerts = append(icerts, icert)
}
}
return icerts, nil
}
func (self *SRegion) GetILoadBalancerCertificateById(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)
}
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) {
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
}
// https://cloud.tencent.com/document/api/214/30692
// 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" {
LoadBalancerType = "OPEN"
}
params := map[string]string{
"LoadBalancerType": LoadBalancerType,
"LoadBalancerName": loadbalancer.Name,
"VpcId": loadbalancer.VpcID,
"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) {
return nil, cloudprovider.ErrNotSupported
}
// todo:目前onecloud端只能指定服务器端证书。需要兼容客户端证书?
func (self *SRegion) CreateILoadBalancerCertificate(cert *cloudprovider.SLoadbalancerCertificate) (cloudprovider.ICloudLoadbalancerCertificate, error) {
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 {
return self.Region
}
@@ -439,6 +607,20 @@ 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) 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)
}
func (self *SRegion) GetNetworks(ids []string, vpcId string, offset int, limit int) ([]SNetwork, int, error) {
if limit > 50 || limit <= 0 {
limit = 50
@@ -591,43 +773,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
}
func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) {
return nil, cloudprovider.ErrNotImplemented
}