fix(region): support lb health check (#23008)

This commit is contained in:
屈轩
2025-08-01 18:38:29 +08:00
committed by GitHub
parent 05913ab0bf
commit fd35536bd7
45 changed files with 1225 additions and 63 deletions
@@ -0,0 +1,61 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/onecloud/pkg/apis"
)
type LoadbalancerHealthCheckDetails struct {
apis.VirtualResourceDetails
ManagedResourceInfo
CloudregionResourceInfo
}
type LoadbalancerHealthCheckListInput struct {
apis.VirtualResourceListInput
apis.ExternalizedResourceBaseListInput
ManagedResourceListInput
RegionalFilterListInput
}
type SLoadbalancerHealthCheckCreateInput struct {
apis.VirtualResourceCreateInput
CloudregionResourceInput
CloudproviderResourceInput
HealthCheckType string
HealthCheckDomain string
HealthCheckURI string
HealthCheckHttpCode string
HealthCheckMethod string
HealthCheckPort int
HealthCheckRise int
HealthCheckFall int
HealthCheckTimeout int
HealthCheckInterval int
HealthCheckReq string
HealthCheckExp string
}
type SLoadbalancerHealthCheckUpdateInput struct {
apis.VirtualResourceBaseUpdateInput
}
+3 -1
View File
@@ -69,7 +69,9 @@ type LoadbalancerBackendGroupCreateInput struct {
//swagger:ignore
Loadbalancer string `json:"loadbalancer" yunion-deprecated-by:"loadbalancer_id"`
// 负载均衡ID
LoadbalancerId string `json:"loadbalancer_id"`
LoadbalancerId string `json:"loadbalancer_id"`
Scheduler string `json:"scheduler"`
LoadbalancerHealthCheckId string `json:"loadbalancer_health_check_id"`
Type string `json:"type"`
+3
View File
@@ -2348,6 +2348,9 @@ func syncPublicCloudProviderInfo(
if syncRange.IsNotSkipSyncResource(LoadbalancerCertificateManager) {
syncRegionLoadbalancerCertificates(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange)
}
if syncRange.IsNotSkipSyncResource(LoadbalancerHealthCheckManager) {
syncRegionLoadbalancerHealthChecks(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange)
}
if syncRange.IsNotSkipSyncResource(LoadbalancerManager) {
syncRegionLoadbalancers(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange)
}
+32
View File
@@ -60,6 +60,38 @@ func syncRegionLoadbalancerCertificates(
}
}
func syncRegionLoadbalancerHealthChecks(
ctx context.Context,
userCred mcclient.TokenCredential,
syncResults SSyncResultSet,
provider *SCloudprovider,
localRegion *SCloudregion,
remoteRegion cloudprovider.ICloudRegion,
syncRange *SSyncRange,
) {
healthChecks, err := func() ([]cloudprovider.ICloudLoadbalancerHealthCheck, error) {
defer syncResults.AddRequestCost(LoadbalancerHealthCheckManager)()
return remoteRegion.GetILoadBalancerHealthChecks()
}()
if err != nil {
msg := fmt.Sprintf("GetILoadBalancerHealthChecks for region %s failed %s", remoteRegion.GetName(), err)
log.Errorln(msg)
return
}
result := func() compare.SyncResult {
defer syncResults.AddSqlCost(LoadbalancerHealthCheckManager)()
return localRegion.SyncLoadbalancerHealthChecks(ctx, userCred, provider, healthChecks)
}()
syncResults.Add(LoadbalancerHealthCheckManager, result)
msg := result.Result()
log.Infof("SyncLoadbalancerHealthChecks for region %s result: %s", localRegion.Name, msg)
if result.IsError() {
return
}
}
func syncRegionLoadbalancerAcls(
ctx context.Context,
userCred mcclient.TokenCredential,
@@ -0,0 +1,433 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/compare"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
// +onecloud:swagger-gen-model-singular=loadbalancer_health_check
// +onecloud:swagger-gen-model-plural=loadbalancerhealthchecks
type SLoadbalancerHealthCheckManager struct {
SLoadbalancerLogSkipper
db.SVirtualResourceBaseManager
db.SExternalizedResourceBaseManager
SManagedResourceBaseManager
SCloudregionResourceBaseManager
}
var LoadbalancerHealthCheckManager *SLoadbalancerHealthCheckManager
func init() {
LoadbalancerHealthCheckManager = &SLoadbalancerHealthCheckManager{
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
SLoadbalancerHealthCheck{},
"loadbalancer_health_checks_tbl",
"loadbalancer_health_check",
"loadbalancer_health_checks",
),
}
LoadbalancerHealthCheckManager.SetVirtualObject(LoadbalancerHealthCheckManager)
}
type SLoadbalancerHealthCheck struct {
db.SVirtualResourceBase
db.SExternalizedResourceBase
SManagedResourceBase
SCloudregionResourceBase
SLoadbalancerHealthChecker
}
// 健康检查列表
func (man *SLoadbalancerHealthCheckManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.LoadbalancerHealthCheckListInput,
) (*sqlchemy.SQuery, error) {
q, err := man.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
}
q, err = man.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
}
q, err = man.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
}
q, err = man.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, query.RegionalFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
}
return q, nil
}
func (man *SLoadbalancerHealthCheckManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
query api.LoadbalancerHealthCheckListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = man.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
}
q, err = man.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
}
return q, nil
}
func (man *SLoadbalancerHealthCheckManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
var err error
q, err = man.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = man.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
func (man *SLoadbalancerHealthCheckManager) ValidateCreateData(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
input *api.SLoadbalancerHealthCheckCreateInput,
) (*api.SLoadbalancerHealthCheckCreateInput, error) {
var err error
input.VirtualResourceCreateInput, err = man.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ValidateCreateData")
}
input.Status = apis.STATUS_CREATING
regionObj, err := validators.ValidateModel(ctx, userCred, CloudregionManager, &input.CloudregionId)
if err != nil {
return nil, err
}
region := regionObj.(*SCloudregion)
if len(input.CloudproviderId) > 0 {
providerObj, err := validators.ValidateModel(ctx, userCred, CloudproviderManager, &input.CloudproviderId)
if err != nil {
return nil, err
}
input.ManagerId = input.CloudproviderId
provider := providerObj.(*SCloudprovider)
if provider.Provider != region.Provider {
return nil, httperrors.NewConflictError("conflict region %s and cloudprovider %s", region.Name, provider.Name)
}
}
return input, nil
}
func (hc *SLoadbalancerHealthCheck) ValidateUpdateData(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input *api.SLoadbalancerHealthCheckUpdateInput,
) (*api.SLoadbalancerHealthCheckUpdateInput, error) {
var err error
input.VirtualResourceBaseUpdateInput, err = hc.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData")
}
return nil, cloudprovider.ErrNotImplemented
}
func (hc *SLoadbalancerHealthCheck) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
hc.SVirtualResourceBase.PostUpdate(ctx, userCred, query, data)
keys, _ := jsonutils.Marshal(SLoadbalancerHealthChecker{}).(*jsonutils.JSONDict).GetMap()
needUpdate := false
for key := range keys {
if data.Contains(key) {
needUpdate = true
break
}
}
if needUpdate {
hc.StartLoadBalancerHealthCheckUpdateTask(ctx, userCred, "")
}
}
func (hc *SLoadbalancerHealthCheck) StartLoadBalancerHealthCheckUpdateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
hc.SetStatus(ctx, userCred, api.LB_SYNC_CONF, "")
task, err := taskman.TaskManager.NewTask(ctx, "LoadbalancerHealthCheckUpdateTask", hc, userCred, nil, parentTaskId, "", nil)
if err != nil {
return err
}
return task.ScheduleRun(nil)
}
func (hc *SLoadbalancerHealthCheck) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
hc.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
hc.SetStatus(ctx, userCred, api.LB_CREATING, "")
err := hc.StartLoadBalancerHealthCheckCreateTask(ctx, userCred, "")
if err != nil {
log.Errorf("Failed to create loadbalancer backend error: %v", err)
}
}
func (manager *SLoadbalancerHealthCheckManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []api.LoadbalancerHealthCheckDetails {
rows := make([]api.LoadbalancerHealthCheckDetails, len(objs))
stdRows := manager.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
for i := range rows {
rows[i] = api.LoadbalancerHealthCheckDetails{
VirtualResourceDetails: stdRows[i],
ManagedResourceInfo: managerRows[i],
CloudregionResourceInfo: regionRows[i],
}
}
return rows
}
func (hc *SLoadbalancerHealthCheck) StartLoadBalancerHealthCheckCreateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "LoadbalancerHealthCheckCreateTask", hc, userCred, nil, parentTaskId, "", nil)
if err != nil {
return errors.Wrapf(err, "NewTask")
}
return task.ScheduleRun(nil)
}
func (hc *SLoadbalancerHealthCheck) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
return nil
}
func (hc *SLoadbalancerHealthCheck) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
return hc.SVirtualResourceBase.Delete(ctx, userCred)
}
func (hc *SLoadbalancerHealthCheck) GetIRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) {
region, err := hc.GetRegion()
if err != nil {
return nil, errors.Wrapf(err, "GetRegion")
}
provider, err := hc.GetDriver(ctx)
if err != nil {
return nil, errors.Wrapf(err, "GetDriver")
}
return provider.GetIRegionById(region.ExternalId)
}
func (hc *SLoadbalancerHealthCheck) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
hc.SetStatus(ctx, userCred, api.LB_STATUS_DELETING, "")
return hc.StartLoadBalancerHealthCheckDeleteTask(ctx, userCred, jsonutils.NewDict(), "")
}
func (hc *SLoadbalancerHealthCheck) StartLoadBalancerHealthCheckDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "LoadbalancerHealthCheckDeleteTask", hc, userCred, params, parentTaskId, "", nil)
if err != nil {
return err
}
return task.ScheduleRun(nil)
}
func (region *SCloudregion) GetLoadbalancerHealthChecks(managerId string) ([]SLoadbalancerHealthCheck, error) {
q := LoadbalancerHealthCheckManager.Query().Equals("manager_id", managerId).Equals("cloudregion_id", region.Id)
ret := []SLoadbalancerHealthCheck{}
err := db.FetchModelObjects(LoadbalancerHealthCheckManager, q, &ret)
if err != nil {
return nil, errors.Wrap(err, "FetchModelObjects")
}
return ret, nil
}
func (region *SCloudregion) SyncLoadbalancerHealthChecks(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, exts []cloudprovider.ICloudLoadbalancerHealthCheck) compare.SyncResult {
lockman.LockRawObject(ctx, LoadbalancerHealthCheckManager.Keyword(), region.Id)
defer lockman.ReleaseRawObject(ctx, LoadbalancerHealthCheckManager.Keyword(), region.Id)
result := compare.SyncResult{}
dbRes, err := region.GetLoadbalancerHealthChecks(provider.Id)
if err != nil {
result.Error(err)
return result
}
removed := []SLoadbalancerHealthCheck{}
commondb := []SLoadbalancerHealthCheck{}
commonext := []cloudprovider.ICloudLoadbalancerHealthCheck{}
added := []cloudprovider.ICloudLoadbalancerHealthCheck{}
err = compare.CompareSets(dbRes, exts, &removed, &commondb, &commonext, &added)
if err != nil {
result.Error(err)
return result
}
for i := 0; i < len(removed); i++ {
err = removed[i].syncRemove(ctx, userCred)
if err != nil {
result.DeleteError(err)
continue
}
result.Delete()
}
for i := 0; i < len(commondb); i++ {
err = commondb[i].SyncWithCloudLoadbalancerHealthCheck(ctx, userCred, commonext[i], provider)
if err != nil {
result.UpdateError(err)
continue
}
result.Update()
}
for i := 0; i < len(added); i++ {
err := region.newFromCloudLoadbalancerHealthCheck(ctx, userCred, added[i], provider)
if err != nil {
result.AddError(err)
continue
}
result.Add()
}
return result
}
func (hc *SLoadbalancerHealthCheck) SyncWithCloudLoadbalancerHealthCheck(
ctx context.Context,
userCred mcclient.TokenCredential,
ext cloudprovider.ICloudLoadbalancerHealthCheck,
provider *SCloudprovider,
) error {
diff, err := db.UpdateWithLock(ctx, hc, func() error {
hc.HealthCheck = ext.GetHealthCheck()
hc.HealthCheckType = ext.GetHealthCheckType()
hc.HealthCheckDomain = ext.GetHealthCheckDomain()
hc.HealthCheckURI = ext.GetHealthCheckURI()
hc.HealthCheckHttpCode = ext.GetHealthCheckCode()
hc.HealthCheckMethod = ext.GetHealthCheckMethod()
hc.HealthCheckPort = ext.GetHealthCheckPort()
hc.HealthCheckRise = ext.GetHealthCheckRise()
hc.HealthCheckFall = ext.GetHealthCheckFail()
hc.HealthCheckTimeout = ext.GetHealthCheckTimeout()
hc.HealthCheckInterval = ext.GetHealthCheckInterval()
hc.HealthCheckReq = ext.GetHealthCheckReq()
hc.HealthCheckExp = ext.GetHealthCheckExp()
return nil
})
if err != nil {
return err
}
syncVirtualResourceMetadata(ctx, userCred, hc, ext, false)
SyncCloudProject(ctx, userCred, hc, provider.GetOwnerId(), ext, provider)
if len(diff) > 0 {
db.OpsLog.LogSyncUpdate(hc, diff, userCred)
}
return nil
}
func (region *SCloudregion) newFromCloudLoadbalancerHealthCheck(
ctx context.Context,
userCred mcclient.TokenCredential,
ext cloudprovider.ICloudLoadbalancerHealthCheck,
provider *SCloudprovider,
) error {
hc := &SLoadbalancerHealthCheck{}
hc.SetModelManager(LoadbalancerHealthCheckManager, hc)
hc.Name = ext.GetName()
hc.Status = ext.GetStatus()
hc.ManagerId = provider.Id
hc.CloudregionId = region.Id
hc.ExternalId = ext.GetGlobalId()
hc.HealthCheck = ext.GetHealthCheck()
hc.HealthCheckType = ext.GetHealthCheckType()
hc.HealthCheckDomain = ext.GetHealthCheckDomain()
hc.HealthCheckURI = ext.GetHealthCheckURI()
hc.HealthCheckHttpCode = ext.GetHealthCheckCode()
hc.HealthCheckMethod = ext.GetHealthCheckMethod()
hc.HealthCheckPort = ext.GetHealthCheckPort()
hc.HealthCheckRise = ext.GetHealthCheckRise()
hc.HealthCheckFall = ext.GetHealthCheckFail()
hc.HealthCheckTimeout = ext.GetHealthCheckTimeout()
hc.HealthCheckInterval = ext.GetHealthCheckInterval()
hc.HealthCheckReq = ext.GetHealthCheckReq()
hc.HealthCheckExp = ext.GetHealthCheckExp()
hc.DomainId = provider.DomainId
hc.ProjectId = provider.ProjectId
err := LoadbalancerHealthCheckManager.TableSpec().Insert(ctx, hc)
if err != nil {
return errors.Wrap(err, "Insert")
}
syncVirtualResourceMetadata(ctx, userCred, hc, ext, false)
SyncCloudProject(ctx, userCred, hc, provider.GetOwnerId(), ext, provider)
return nil
}
func (hc *SLoadbalancerHealthCheck) syncRemove(ctx context.Context, userCred mcclient.TokenCredential) error {
lockman.LockObject(ctx, hc)
defer lockman.ReleaseObject(ctx, hc)
return hc.RealDelete(ctx, userCred)
}
func (manager *SLoadbalancerHealthCheckManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemExportKeys")
}
return q, nil
}
@@ -68,6 +68,8 @@ type SLoadbalancerBackendGroup struct {
db.SExternalizedResourceBase
SLoadbalancerResourceBase `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
LoadbalancerHealthCheckId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
Scheduler string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"`
Type string `width:"36" charset:"ascii" nullable:"false" list:"user" default:"normal" create:"optional"`
}
@@ -220,6 +222,12 @@ func (man *SLoadbalancerBackendGroupManager) ValidateCreateData(ctx context.Cont
if err != nil {
return nil, err
}
if len(input.LoadbalancerHealthCheckId) > 0 {
_, err := validators.ValidateModel(ctx, userCred, LoadbalancerHealthCheckManager, &input.LoadbalancerHealthCheckId)
if err != nil {
return nil, err
}
}
region, err := lb.GetRegion()
if err != nil {
@@ -712,6 +720,15 @@ func (lbbg *SLoadbalancerBackendGroup) SyncWithCloudLoadbalancerBackendgroup(
diff, err := db.UpdateWithLock(ctx, lbbg, func() error {
lbbg.Type = ext.GetType()
lbbg.Status = ext.GetStatus()
lbbg.Scheduler = ext.GetScheduler()
if hcId := ext.GetHealthCheckId(); hcId != "" {
hc, err := db.FetchByExternalIdAndManagerId(LoadbalancerHealthCheckManager, hcId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("manager_id", lb.ManagerId)
})
if err == nil {
lbbg.LoadbalancerHealthCheckId = hc.GetId()
}
}
return nil
})
if err != nil {
@@ -755,7 +772,16 @@ func (lb *SLoadbalancer) newFromCloudLoadbalancerBackendgroup(
lbbg.Type = ext.GetType()
lbbg.Status = ext.GetStatus()
lbbg.Scheduler = ext.GetScheduler()
lbbg.Name = ext.GetName()
if hcId := ext.GetHealthCheckId(); hcId != "" {
hc, err := db.FetchByExternalIdAndManagerId(LoadbalancerHealthCheckManager, hcId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.Equals("manager_id", lb.ManagerId)
})
if err == nil {
lbbg.LoadbalancerHealthCheckId = hc.GetId()
}
}
err := LoadbalancerBackendGroupManager.TableSpec().Insert(ctx, lbbg)
if err != nil {
@@ -781,6 +807,14 @@ func (lb *SLoadbalancer) newFromCloudLoadbalancerBackendgroup(
return lbbg, nil
}
func (lbbg *SLoadbalancerBackendGroup) GetHealthCheck() (*SLoadbalancerHealthCheck, error) {
obj, err := db.FetchById(LoadbalancerHealthCheckManager, lbbg.LoadbalancerHealthCheckId)
if err != nil {
return nil, errors.Wrap(err, "FetchById")
}
return obj.(*SLoadbalancerHealthCheck), nil
}
func (manager *SLoadbalancerBackendGroupManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
@@ -84,7 +84,7 @@ type SLoadbalancerListenerRule struct {
RedirectPool *api.ListenerRuleRedirectPool `list:"user" update:"user" create:"optional"`
SLoadbalancerHealthCheck // 目前只有腾讯云HTTP、HTTPS类型的健康检查是和规则绑定的。
SLoadbalancerHealthChecker // 目前只有腾讯云HTTP、HTTPS类型的健康检查是和规则绑定的。
SLoadbalancerHTTPRateLimiter
SLoadbalancerHTTPRedirect
}
+62 -30
View File
@@ -66,29 +66,46 @@ func init() {
}
type SLoadbalancerHTTPRateLimiter struct {
HTTPRequestRate int `nullable:"true" list:"user" create:"optional" update:"user"` // 限定监听接收请示速率
HTTPRequestRatePerSrc int `nullable:"true" list:"user" create:"optional" update:"user"` // 源IP监听请求最大速率
// 限定监听接收请示速率
HTTPRequestRate int `nullable:"true" list:"user" create:"optional" update:"user"`
// 源IP监听请求最大速率
HTTPRequestRatePerSrc int `nullable:"true" list:"user" create:"optional" update:"user"`
}
type SLoadbalancerRateLimiter struct {
EgressMbps int `nullable:"true" list:"user" get:"user" create:"optional" update:"user" json:"egress_mbps"`
}
type SLoadbalancerHealthCheck struct {
HealthCheck string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"` // 健康检查开启状态 on|off
HealthCheckType string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"` // 健康检查协议 HTTP|TCP
type SLoadbalancerHealthChecker struct {
// 健康检查开启状态 on|off
HealthCheck string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
// 健康检查协议 HTTP|TCP
HealthCheckType string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
HealthCheckDomain string `charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"` // 健康检查域名 yunion.cn
HealthCheckURI string `charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"` // 健康检查路径 /
HealthCheckHttpCode string `charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"` // HTTP正常状态码 http_2xx,http_3xx
// 健康检查域名 yunion.cn
HealthCheckDomain string `charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
// 健康检查路径 /
HealthCheckURI string `charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
// HTTP正常状态码 http_2xx,http_3xx
HealthCheckHttpCode string `charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
// 健康检查方法 GET|POST|HEAD
HealthCheckMethod string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
// 健康检查端口 80
HealthCheckPort int `nullable:"true" list:"user" create:"optional" update:"user"`
HealthCheckRise int `nullable:"true" list:"user" create:"optional" update:"user"` // 健康检查健康阈值 3秒
HealthCheckFall int `nullable:"true" list:"user" create:"optional" update:"user"` // 健康检查不健康阈值 15秒
HealthCheckTimeout int `nullable:"true" list:"user" create:"optional" update:"user"` // 健康检查超时时间 10
HealthCheckInterval int `nullable:"true" list:"user" create:"optional" update:"user"` // 健康检查间隔时间 5秒
// 健康检查健康阈值 3秒
HealthCheckRise int `nullable:"true" list:"user" create:"optional" update:"user"`
// 健康检查不健康阈值 15
HealthCheckFall int `nullable:"true" list:"user" create:"optional" update:"user"`
// 健康检查超时时间 10秒
HealthCheckTimeout int `nullable:"true" list:"user" create:"optional" update:"user"`
// 健康检查间隔时间 5秒
HealthCheckInterval int `nullable:"true" list:"user" create:"optional" update:"user"`
HealthCheckReq string `list:"user" create:"optional" update:"user"` // UDP监听健康检查的请求串
HealthCheckExp string `list:"user" create:"optional" update:"user"` // UDP监听健康检查的响应串
// UDP监听健康检查的请求串
HealthCheckReq string `list:"user" create:"optional" update:"user"`
// UDP监听健康检查的响应串
HealthCheckExp string `list:"user" create:"optional" update:"user"`
}
type SLoadbalancerTCPListener struct{}
@@ -96,21 +113,32 @@ type SLoadbalancerUDPListener struct{}
// TODO sensible default for knobs
type SLoadbalancerHTTPListener struct {
StickySession string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"` // 会话保持开启状态 on|off
StickySessionType string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"` // Cookie处理方式 insert(植入cookie)|server(重写cookie)
StickySessionCookie string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"` // Cookie名称
StickySessionCookieTimeout int `nullable:"true" list:"user" create:"optional" update:"user"` // 会话超时时间
// 会话保持开启状态 on|off
StickySession string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
// Cookie处理方式 insert(植入cookie)|server(重写cookie)
StickySessionType string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
// Cookie名称
StickySessionCookie string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
// 会话超时时间
StickySessionCookieTimeout int `nullable:"true" list:"user" create:"optional" update:"user"`
XForwardedFor bool `nullable:"true" list:"user" create:"optional" update:"user"` // 获取客户端真实IP
Gzip bool `nullable:"true" list:"user" create:"optional" update:"user"` // Gzip数据压缩
// 获取客户端真实IP
XForwardedFor bool `nullable:"true" list:"user" create:"optional" update:"user"`
// Gzip数据压缩
Gzip bool `nullable:"true" list:"user" create:"optional" update:"user"`
}
type SLoadbalancerHTTPRedirect struct {
Redirect string `width:"16" nullable:"true" list:"user" create:"optional" update:"user" default:"off"` // 跳转类型
RedirectCode int `nullable:"true" list:"user" create:"optional" update:"user"` // 跳转HTTP code
RedirectScheme string `width:"16" nullable:"true" list:"user" create:"optional" update:"user"` // 跳转uri scheme
RedirectHost string `nullable:"true" list:"user" create:"optional" update:"user"` // 跳转时变更Host
RedirectPath string `nullable:"true" list:"user" create:"optional" update:"user"` // 跳转时变更Path
// 跳转类型
Redirect string `width:"16" nullable:"true" list:"user" create:"optional" update:"user" default:"off"`
// 跳转HTTP code
RedirectCode int `nullable:"true" list:"user" create:"optional" update:"user"`
// 跳转uri scheme
RedirectScheme string `width:"16" nullable:"true" list:"user" create:"optional" update:"user"`
// 跳转时变更Host
RedirectHost string `nullable:"true" list:"user" create:"optional" update:"user"`
// 跳转时变更Path
RedirectPath string `nullable:"true" list:"user" create:"optional" update:"user"`
}
// TODO
@@ -141,10 +169,14 @@ type SLoadbalancerListener struct {
SendProxy string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"optional" update:"user" default:"off"`
ClientRequestTimeout int `nullable:"true" list:"user" create:"optional" update:"user"` // 连接请求超时时间
ClientIdleTimeout int `nullable:"true" list:"user" create:"optional" update:"user"` // 连接空闲超时时间
BackendConnectTimeout int `nullable:"true" list:"user" create:"optional" update:"user"` // 后端连接超时时间
BackendIdleTimeout int `nullable:"true" list:"user" create:"optional" update:"user"` // 后端连接空闲时间
// 连接请求超时时间
ClientRequestTimeout int `nullable:"true" list:"user" create:"optional" update:"user"`
// 连接空闲超时时间
ClientIdleTimeout int `nullable:"true" list:"user" create:"optional" update:"user"`
// 后端连接超时时间
BackendConnectTimeout int `nullable:"true" list:"user" create:"optional" update:"user"`
// 后端连接空闲时间
BackendIdleTimeout int `nullable:"true" list:"user" create:"optional" update:"user"`
AclStatus string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
AclType string `width:"16" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
@@ -157,7 +189,7 @@ type SLoadbalancerListener struct {
SLoadbalancerHTTPListener
SLoadbalancerHTTPSListener
SLoadbalancerHealthCheck
SLoadbalancerHealthChecker
SLoadbalancerHTTPRateLimiter
SLoadbalancerHTTPRedirect
}
@@ -472,8 +472,12 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancerBackend
group := &cloudprovider.SLoadbalancerBackendGroup{
Name: lbbg.Name,
GroupType: lbbg.Type,
Scheduler: lbbg.Scheduler,
Backends: []cloudprovider.SLoadbalancerBackend{},
}
if hc, err := lbbg.GetHealthCheck(); err == nil {
group.HealthCheckId = hc.ExternalId
}
backends, err := lbbg.GetBackends()
if err != nil {
return nil, errors.Wrapf(err, "GetBackends")
+1
View File
@@ -160,6 +160,7 @@ func InitHandlers(app *appsrv.Application) {
models.LoadbalancerAclManager,
models.LoadbalancerAgentManager,
models.LoadbalancerClusterManager,
models.LoadbalancerHealthCheckManager,
models.RouteTableManager,
models.RouteTableAssociationManager,
models.RouteTableRouteSetManager,
@@ -0,0 +1,88 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loadbalancer
import (
"context"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type LoadbalancerHealthCheckCreateTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(LoadbalancerHealthCheckCreateTask{})
}
func (self *LoadbalancerHealthCheckCreateTask) taskFail(ctx context.Context, hc *models.SLoadbalancerHealthCheck, err error) {
hc.SetStatus(ctx, self.GetUserCred(), api.LB_CREATE_FAILED, err.Error())
db.OpsLog.LogEvent(hc, db.ACT_ALLOCATE_FAIL, err, self.UserCred)
logclient.AddActionLogWithStartable(self, hc, logclient.ACT_CREATE, err, self.UserCred, false)
notifyclient.NotifySystemErrorWithCtx(ctx, hc.Id, hc.Name, api.LB_CREATE_FAILED, err.Error())
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}
func (self *LoadbalancerHealthCheckCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
hc := obj.(*models.SLoadbalancerHealthCheck)
iRegion, err := hc.GetIRegion(ctx)
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "GetIRegion"))
return
}
opts := &cloudprovider.SLoadbalancerHealthCheck{
Name: hc.Name,
HealthCheckType: hc.HealthCheckType,
HealthCheckDomain: hc.HealthCheckDomain,
HealthCheckURI: hc.HealthCheckURI,
HealthCheckHttpCode: hc.HealthCheckHttpCode,
HealthCheckMethod: hc.HealthCheckMethod,
HealthCheckPort: hc.HealthCheckPort,
HealthCheckTimeout: hc.HealthCheckTimeout,
HealthCheckInterval: hc.HealthCheckInterval,
HealthCheckRise: hc.HealthCheckRise,
HealthCheckReq: hc.HealthCheckReq,
HealthCheckExp: hc.HealthCheckExp,
}
iHc, err := iRegion.CreateILoadBalancerHealthCheck(opts)
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "CreateILoadBalancerHealthCheck"))
return
}
_, err = db.Update(hc, func() error {
hc.ExternalId = iHc.GetGlobalId()
hc.Status = apis.STATUS_AVAILABLE
return nil
})
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "Update"))
}
self.SetStageComplete(ctx, nil)
}
@@ -0,0 +1,80 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loadbalancer
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type LoadbalancerHealthCheckDeleteTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(LoadbalancerHealthCheckDeleteTask{})
}
func (self *LoadbalancerHealthCheckDeleteTask) taskFail(ctx context.Context, hc *models.SLoadbalancerHealthCheck, err error) {
hc.SetStatus(ctx, self.GetUserCred(), api.LB_STATUS_DELETE_FAILED, err.Error())
db.OpsLog.LogEvent(hc, db.ACT_DELOCATE_FAIL, err.Error(), self.UserCred)
logclient.AddActionLogWithStartable(self, hc, logclient.ACT_DELOCATE, err.Error(), self.UserCred, false)
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}
func (self *LoadbalancerHealthCheckDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
hc := obj.(*models.SLoadbalancerHealthCheck)
if len(hc.ExternalId) == 0 {
self.taskComplete(ctx, hc)
return
}
iRegion, err := hc.GetIRegion(ctx)
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "GetIRegion"))
return
}
hcs, err := iRegion.GetILoadBalancerHealthChecks()
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "GetLoadbalancerHealthChecks"))
return
}
for i := range hcs {
if hcs[i].GetGlobalId() == hc.ExternalId {
err = hcs[i].Delete()
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "Delete"))
return
}
self.taskComplete(ctx, hc)
return
}
}
self.taskComplete(ctx, hc)
}
func (self *LoadbalancerHealthCheckDeleteTask) taskComplete(ctx context.Context, hc *models.SLoadbalancerHealthCheck) {
hc.RealDelete(ctx, self.GetUserCred())
self.SetStageComplete(ctx, nil)
}
@@ -0,0 +1,93 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loadbalancer
import (
"context"
"yunion.io/x/cloudmux/pkg/cloudprovider"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/logclient"
)
type LoadbalancerHealthCheckUpdateTask struct {
taskman.STask
}
func init() {
taskman.RegisterTask(LoadbalancerHealthCheckUpdateTask{})
}
func (self *LoadbalancerHealthCheckUpdateTask) taskFail(ctx context.Context, hc *models.SLoadbalancerHealthCheck, err error) {
hc.SetStatus(ctx, self.GetUserCred(), api.LB_SYNC_CONF_FAILED, err.Error())
db.OpsLog.LogEvent(hc, db.ACT_SYNC_CONF, err.Error(), self.UserCred)
logclient.AddActionLogWithStartable(self, hc, logclient.ACT_SYNC_CONF, err.Error(), self.UserCred, false)
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
}
func (self *LoadbalancerHealthCheckUpdateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
hc := obj.(*models.SLoadbalancerHealthCheck)
iRegion, err := hc.GetIRegion(ctx)
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "GetIRegion"))
return
}
hcs, err := iRegion.GetILoadBalancerHealthChecks()
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "GetLoadbalancerHealthChecks"))
return
}
for i := range hcs {
if hcs[i].GetGlobalId() == hc.ExternalId {
opts := &cloudprovider.SLoadbalancerHealthCheck{
Name: hc.Name,
HealthCheckType: hc.HealthCheckType,
HealthCheckDomain: hc.HealthCheckDomain,
HealthCheckURI: hc.HealthCheckURI,
HealthCheckHttpCode: hc.HealthCheckHttpCode,
HealthCheckMethod: hc.HealthCheckMethod,
HealthCheckPort: hc.HealthCheckPort,
HealthCheckTimeout: hc.HealthCheckTimeout,
HealthCheckInterval: hc.HealthCheckInterval,
HealthCheckRise: hc.HealthCheckRise,
HealthCheckReq: hc.HealthCheckReq,
HealthCheckExp: hc.HealthCheckExp,
}
err := hcs[i].Update(ctx, opts)
if err != nil {
self.taskFail(ctx, hc, errors.Wrapf(err, "Update"))
return
}
self.taskComplete(ctx, hc)
return
}
}
self.taskFail(ctx, hc, errors.Wrapf(errors.ErrNotFound, "LoadbalancerHealthCheck %s not found", hc.ExternalId))
}
func (self *LoadbalancerHealthCheckUpdateTask) taskComplete(ctx context.Context, hc *models.SLoadbalancerHealthCheck) {
hc.SetStatus(ctx, self.GetUserCred(), apis.STATUS_AVAILABLE, "")
self.SetStageComplete(ctx, nil)
}
@@ -0,0 +1,47 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
"yunion.io/x/onecloud/pkg/mcclient/modules"
)
type LoadbalancerHealthCheckManager struct {
modulebase.ResourceManager
}
var (
LoadbalancerHealthChecks LoadbalancerHealthCheckManager
)
func init() {
LoadbalancerHealthChecks = LoadbalancerHealthCheckManager{
modules.NewComputeManager(
"loadbalancer_health_check",
"loadbalancer_health_checks",
[]string{
"id",
"name",
"status",
"cloudregion",
"manager",
"project",
},
[]string{"tenant"},
),
}
modules.RegisterCompute(&LoadbalancerHealthChecks)
}
@@ -0,0 +1,50 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compute
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
type LoadbalancerHealthCheckListOptions struct {
options.BaseListOptions
Cloudregion string
}
func (opts *LoadbalancerHealthCheckListOptions) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(opts)
}
type LoadbalancerHealthCheckIdOptions struct {
options.BaseIdOptions
}
type LoadbalancerHealthCheckCreateOptions struct {
options.SharableProjectizedResourceBaseCreateInput
NAME string
Manager string
Region string
}
func (opts *LoadbalancerHealthCheckCreateOptions) Params() (jsonutils.JSONObject, error) {
return options.StructToParams(opts)
}
type LoadbalancerHealthCheckUpdateOptions struct {
LoadbalancerHealthCheckIdOptions
}