diff --git a/pkg/cloudprovider/loadbalancerlistener.go b/pkg/cloudprovider/loadbalancerlistener.go index 5e11342796..923aeaa4fa 100644 --- a/pkg/cloudprovider/loadbalancerlistener.go +++ b/pkg/cloudprovider/loadbalancerlistener.go @@ -70,4 +70,18 @@ type SLoadbalancerListenerRule struct { BackendGroupType string Condition string // for aws only + + Scheduler string // for qcloud only + HealthCheck string // for qcloud only + HealthCheckType string // for qcloud only + HealthCheckTimeout int // for qcloud only + HealthCheckDomain string // for qcloud only + HealthCheckHttpCode string // for qcloud only + HealthCheckURI string // for qcloud only + HealthCheckInterval int // for qcloud only + + HealthCheckRise int // for qcloud only + HealthCheckFail int // for qcloud only + + StickySessionCookieTimeout int // for qcloud only } diff --git a/pkg/compute/models/cloudsyncelb.go b/pkg/compute/models/cloudsyncelb.go index 56c4feeb22..0649aba645 100644 --- a/pkg/compute/models/cloudsyncelb.go +++ b/pkg/compute/models/cloudsyncelb.go @@ -294,3 +294,48 @@ func syncAwsLoadbalancerBackends(ctx context.Context, userCred mcclient.TokenCre return } } + +/*qcloud elb sync*/ +func SyncQcloudLoadbalancerBackendgroups(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localLoadbalancer *SLoadbalancer, remoteLoadbalancer cloudprovider.ICloudLoadbalancer, syncRange *SSyncRange) { + remoteBackendgroups, err := remoteLoadbalancer.GetILoadBalancerBackendGroups() + if err != nil { + msg := fmt.Sprintf("GetILoadBalancerBackendGroups for loadbalancer %s failed %s", localLoadbalancer.Name, err) + log.Errorln(msg) + return + } + localLbbgs, remoteLbbgs, result := QcloudCachedLbbgManager.SyncLoadbalancerBackendgroups(ctx, userCred, provider, localLoadbalancer, remoteBackendgroups, syncRange) + + syncResults.Add(QcloudCachedLbbgManager, result) + + msg := result.Result() + log.Infof("SyncLoadbalancerBackendgroups for loadbalancer %s result: %s", localLoadbalancer.Name, msg) + if result.IsError() { + return + } + for i := 0; i < len(localLbbgs); i++ { + func() { + lockman.LockObject(ctx, &localLbbgs[i]) + defer lockman.ReleaseObject(ctx, &localLbbgs[i]) + + syncQcloudLoadbalancerBackends(ctx, userCred, syncResults, provider, &localLbbgs[i], remoteLbbgs[i], syncRange) + }() + } +} + +func syncQcloudLoadbalancerBackends(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localLbbg *SQcloudCachedLbbg, remoteLbbg cloudprovider.ICloudLoadbalancerBackendGroup, syncRange *SSyncRange) { + remoteLbbs, err := remoteLbbg.GetILoadbalancerBackends() + if err != nil { + msg := fmt.Sprintf("GetILoadbalancerBackends for lbbg %s failed %s", localLbbg.Name, err) + log.Errorln(msg) + return + } + result := QcloudCachedLbManager.SyncLoadbalancerBackends(ctx, userCred, provider, localLbbg, remoteLbbs, syncRange) + + syncResults.Add(LoadbalancerBackendManager, result) + + msg := result.Result() + log.Infof("SyncLoadbalancerBackends for LoadbalancerBackendgroup %s result: %s", localLbbg.Name, msg) + if result.IsError() { + return + } +} diff --git a/pkg/compute/models/loadbalancerbackendgroups.go b/pkg/compute/models/loadbalancerbackendgroups.go index 5c2ff23829..fd1ba63412 100644 --- a/pkg/compute/models/loadbalancerbackendgroups.go +++ b/pkg/compute/models/loadbalancerbackendgroups.go @@ -603,6 +603,17 @@ func (lbbg *SLoadbalancerBackendGroup) GetHuaweiCachedlbbg() ([]SHuaweiCachedLbb return ret, nil } +func (lbbg *SLoadbalancerBackendGroup) GetQcloudCachedlbbg() ([]SQcloudCachedLbbg, error) { + ret := []SQcloudCachedLbbg{} + q := QcloudCachedLbbgManager.Query().Equals("backend_group_id", lbbg.GetId()) + err := db.FetchModelObjects(QcloudCachedLbbgManager, q, &ret) + if err != nil { + return nil, errors.Wrap(err, "loadbalancerBackendGroup.GetQcloudCachedlbbg") + } + + return ret, nil +} + func (lbbg *SLoadbalancerBackendGroup) GetAwsBackendGroupParams(lblis *SLoadbalancerListener, lbr *SLoadbalancerListenerRule) (*cloudprovider.SLoadbalancerBackendGroup, error) { ret, err := lbbg.GetBackendGroupParams() if err != nil { @@ -643,6 +654,21 @@ func (lbbg *SLoadbalancerBackendGroup) GetAwsBackendGroupParams(lblis *SLoadbala return ret, nil } +func (lbbg *SLoadbalancerBackendGroup) GetQcloudBackendGroupParams(lblis *SLoadbalancerListener, lbr *SLoadbalancerListenerRule) (*cloudprovider.SLoadbalancerBackendGroup, error) { + ret, err := lbbg.GetBackendGroupParams() + if err != nil { + return ret, err + } + + if lbr != nil { + ret.ListenerID = lbr.GetExternalId() + } else { + ret.ListenerID = lblis.GetExternalId() + } + + return ret, nil +} + func (lbbg *SLoadbalancerBackendGroup) GetBackendsParams() ([]cloudprovider.SLoadbalancerBackend, error) { backends, err := lbbg.GetBackends() if err != nil { diff --git a/pkg/compute/models/loadbalancerlistenerrules.go b/pkg/compute/models/loadbalancerlistenerrules.go index 7a4709889c..9501ed86f6 100644 --- a/pkg/compute/models/loadbalancerlistenerrules.go +++ b/pkg/compute/models/loadbalancerlistenerrules.go @@ -624,18 +624,6 @@ func (lbr *SLoadbalancerListenerRule) constructFieldsFromCloudListenerRule(userC lbr.Status = extRule.GetStatus() lbr.Condition = extRule.GetCondition() if groupId := extRule.GetBackendGroupId(); len(groupId) > 0 { - // 腾讯云兼容代码。主要目的是在关联listener rule时回写一个fake的backend group external id - if lbr.GetProviderName() == api.CLOUD_PROVIDER_QCLOUD && 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 = db.SetExternalId(lbbg, userCred, groupId) - if err != nil { - log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s", lbbg.GetId(), err) - } - } - } - if lbr.GetProviderName() == api.CLOUD_PROVIDER_HUAWEI { group, err := db.FetchByExternalId(HuaweiCachedLbbgManager, groupId) if err != nil { @@ -655,6 +643,16 @@ func (lbr *SLoadbalancerListenerRule) constructFieldsFromCloudListenerRule(userC lbr.BackendGroupId = group.(*SAwsCachedLbbg).BackendGroupId } + } else if lbr.GetProviderName() == api.CLOUD_PROVIDER_QCLOUD { + group, err := db.FetchByExternalId(QcloudCachedLbbgManager, groupId) + if err != nil { + if err == sql.ErrNoRows { + lbr.BackendGroupId = "" + } + log.Errorf("Fetch qcloud loadbalancer backendgroup by external id %s failed: %s", groupId, err) + } + + lbr.BackendGroupId = group.(*SQcloudCachedLbbg).BackendGroupId } else if backendgroup, err := db.FetchByExternalId(LoadbalancerBackendGroupManager, groupId); err == nil { lbr.BackendGroupId = backendgroup.GetId() } @@ -686,7 +684,29 @@ func (lbr *SLoadbalancerListenerRule) updateCachedLoadbalancerBackendGroupAssoci return nil }) if err != nil { - return errors.Wrap(err, "LoadbalancerListener.updateCachedLoadbalancerBackendGroupAssociate") + return errors.Wrap(err, "LoadbalancerListener.updateCachedLoadbalancerBackendGroupAssociate.huawei") + } + } + } + case api.CLOUD_PROVIDER_QCLOUD: + _group, err := db.FetchByExternalId(QcloudCachedLbbgManager, exteralLbbgId) + if err != nil { + if err == sql.ErrNoRows { + lbr.BackendGroupId = "" + } + return fmt.Errorf("Fetch qcloud loadbalancer backendgroup by external id %s failed: %s", exteralLbbgId, err) + } + + if _group != nil { + group := _group.(*SQcloudCachedLbbg) + if group.AssociatedId != lbr.Id { + _, err := db.UpdateWithLock(ctx, group, func() error { + group.AssociatedId = lbr.Id + group.AssociatedType = api.LB_ASSOCIATE_TYPE_RULE + return nil + }) + if err != nil { + return errors.Wrap(err, "LoadbalancerListener.updateCachedLoadbalancerBackendGroupAssociate.qcloud") } } } diff --git a/pkg/compute/models/loadbalancerlisteners.go b/pkg/compute/models/loadbalancerlisteners.go index 8a0936b852..0e38ba8380 100644 --- a/pkg/compute/models/loadbalancerlisteners.go +++ b/pkg/compute/models/loadbalancerlisteners.go @@ -662,6 +662,29 @@ func (lblis *SLoadbalancerListener) GetAwsLoadbalancerListenerParams() (*cloudpr return listener, nil } +func (lblis *SLoadbalancerListener) GetQcloudLoadbalancerListenerParams() (*cloudprovider.SLoadbalancerListener, error) { + listener, err := lblis.GetLoadbalancerListenerParams() + if err != nil { + return nil, err + } + + if backendgroup := lblis.GetLoadbalancerBackendGroup(); backendgroup != nil { + cachedLbbg, err := QcloudCachedLbbgManager.GetCachedBackendGroupByAssociateId(lblis.GetId()) + if err != nil { + if err != sql.ErrNoRows { + return nil, errors.Wrap(err, "loadbalancerListener.GetCachedBackendGroupByAssociateId") + } else { + log.Debugf("loadbalancerListener.GetCachedBackendGroupByAssociateId %s not found", lblis.GetId()) + } + } else { + listener.BackendGroupID = cachedLbbg.ExternalId + listener.BackendGroupType = backendgroup.Type + } + } + + return listener, nil +} + func (lblis *SLoadbalancerListener) GetLoadbalancerCertificate() (*SCachedLoadbalancerCertificate, error) { if len(lblis.CachedCertificateId) == 0 { return nil, nil @@ -877,27 +900,28 @@ func (lblis *SLoadbalancerListener) constructFieldsFromCloudListener(userCred mc fallthrough case api.LB_LISTENER_TYPE_HTTP: if len(extListener.GetStickySessionType()) > 0 { - lblis.StickySession = extListener.GetStickySession() - lblis.StickySessionType = extListener.GetStickySessionType() - lblis.StickySessionCookie = extListener.GetStickySessionCookie() - lblis.StickySessionCookieTimeout = extListener.GetStickySessionCookieTimeout() + if lblis.GetProviderName() == api.CLOUD_PROVIDER_QCLOUD && utils.IsInStringArray(lblis.ListenerType, []string{api.LB_LISTENER_TYPE_HTTP, api.LB_LISTENER_TYPE_HTTPS}) { + // 腾讯云http&https监听, 没有会话保持,不需要同步 + } else { + // deprecated ??? + lblis.StickySession = extListener.GetStickySession() + lblis.StickySessionType = extListener.GetStickySessionType() + lblis.StickySessionCookie = extListener.GetStickySessionCookie() + lblis.StickySessionCookieTimeout = extListener.GetStickySessionCookieTimeout() + } } lblis.XForwardedFor = extListener.XForwardedForEnabled() lblis.Gzip = extListener.GzipEnabled() } - groupId := extListener.GetBackendGroupId() - // 腾讯云兼容代码。主要目的是在关联listen时回写一个fake的backend group external id - if lblis.GetProviderName() == api.CLOUD_PROVIDER_QCLOUD && 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 = db.SetExternalId(lbbg, userCred, groupId) - if err != nil { - log.Errorf("Update loadbalancer BackendGroup(%s) external id failed: %s", lbbg.GetId(), err) - } - } + + if utils.IsInStringArray(extListener.GetStickySession(), []string{api.LB_BOOL_ON, api.LB_BOOL_OFF}) { + lblis.StickySession = extListener.GetStickySession() + lblis.StickySessionType = extListener.GetStickySessionType() + lblis.StickySessionCookie = extListener.GetStickySessionCookie() + lblis.StickySessionCookieTimeout = extListener.GetStickySessionCookieTimeout() } + groupId := extListener.GetBackendGroupId() switch lblis.GetProviderName() { case api.CLOUD_PROVIDER_HUAWEI: if len(groupId) > 0 { @@ -931,6 +955,24 @@ func (lblis *SLoadbalancerListener) constructFieldsFromCloudListener(userCred mc } } } + case api.CLOUD_PROVIDER_QCLOUD: + if len(groupId) > 0 { + lb := lblis.GetLoadbalancer() + if forward, _ := lb.LBInfo.Int("Forward"); forward == 1 { + // 应用型负载均衡 + group, err := db.FetchByExternalId(QcloudCachedLbbgManager, groupId) + if err != nil { + log.Errorf("Fetch qcloud loadbalancer backendgroup by external id %s failed: %s", groupId, err) + } else { + lblis.BackendGroupId = group.(*SQcloudCachedLbbg).BackendGroupId + } + } else { + // 传统型负载均衡 + if group, err := db.FetchByExternalId(LoadbalancerBackendGroupManager, groupId); err == nil { + lblis.BackendGroupId = group.GetId() + } + } + } default: if len(lblis.BackendGroupId) == 0 && len(groupId) == 0 { lblis.BackendGroupId = lb.BackendGroupId @@ -966,7 +1008,33 @@ func (lblis *SLoadbalancerListener) updateCachedLoadbalancerBackendGroupAssociat return nil }) if err != nil { - return errors.Wrap(err, "LoadbalancerListener.updateCachedLoadbalancerBackendGroupAssociate") + return errors.Wrap(err, "LoadbalancerListener.updateCachedLoadbalancerBackendGroupAssociate.huawei") + } + } + } + case api.CLOUD_PROVIDER_QCLOUD: + lb := lblis.GetLoadbalancer() + if forward, _ := lb.LBInfo.Int("Forward"); forward == 1 { + _group, err := db.FetchByExternalId(QcloudCachedLbbgManager, exteralLbbgId) + if err != nil { + if err == sql.ErrNoRows { + lblis.BackendGroupId = "" + } else { + return fmt.Errorf("Fetch qcloud loadbalancer backendgroup by external id %s failed: %s", exteralLbbgId, err) + } + } + + if _group != nil { + group := _group.(*SQcloudCachedLbbg) + if group.AssociatedId != lblis.Id { + _, err := db.UpdateWithLock(ctx, group, func() error { + group.AssociatedId = lblis.Id + group.AssociatedType = api.LB_ASSOCIATE_TYPE_LISTENER + return nil + }) + if err != nil { + return errors.Wrap(err, "LoadbalancerListener.updateCachedLoadbalancerBackendGroupAssociate.qcloud") + } } } } diff --git a/pkg/compute/models/loadbalancerqcloudcachedlbb.go b/pkg/compute/models/loadbalancerqcloudcachedlbb.go new file mode 100644 index 0000000000..d8fbd38a26 --- /dev/null +++ b/pkg/compute/models/loadbalancerqcloudcachedlbb.go @@ -0,0 +1,288 @@ +// 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" + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/compare" + + 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/cloudprovider" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SQcloudCachedLbManager struct { + SLoadbalancerLogSkipper + db.SVirtualResourceBaseManager +} + +var QcloudCachedLbManager *SQcloudCachedLbManager + +func init() { + QcloudCachedLbManager = &SQcloudCachedLbManager{ + SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager( + SQcloudCachedLb{}, + "qcloudcachedlbbs_tbl", + "qcloudcachedlbb", + "qcloudcachedlbbs", + ), + } + QcloudCachedLbManager.SetVirtualObject(QcloudCachedLbManager) +} + +type SQcloudCachedLb struct { + db.SVirtualResourceBase + db.SExternalizedResourceBase + + SManagedResourceBase + SCloudregionResourceBase + + BackendServerId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` // 后端服务器 实例ID + BackendId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` // 本地loadbalancebackend id + CachedBackendGroupId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` +} + +func (lbb *SQcloudCachedLb) GetCustomizeColumns(context.Context, mcclient.TokenCredential, jsonutils.JSONObject) *jsonutils.JSONDict { + return nil +} + +func (lbb *SQcloudCachedLb) GetCachedBackendGroup() (*SQcloudCachedLbbg, error) { + lbbg, err := db.FetchById(QcloudCachedLbbgManager, lbb.CachedBackendGroupId) + if err != nil { + return nil, err + } + + return lbbg.(*SQcloudCachedLbbg), nil +} + +func (lbb *SQcloudCachedLb) syncRemoveCloudLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, lbb) + defer lockman.ReleaseObject(ctx, lbb) + + err := lbb.ValidateDeleteCondition(ctx) + if err != nil { // cannot delete + err = lbb.SetStatus(userCred, api.LB_STATUS_UNKNOWN, "sync to delete") + } else { + lbb.SetModelManager(QcloudCachedLbManager, lbb) + err := db.DeleteModel(ctx, userCred, lbb) + if err != nil { + return err + } + } + return err +} + +func (lbb *SQcloudCachedLb) constructFieldsFromCloudLoadbalancerBackend(extLoadbalancerBackend cloudprovider.ICloudLoadbalancerBackend) error { + lbb.Status = extLoadbalancerBackend.GetStatus() + + instance, err := db.FetchByExternalId(GuestManager, extLoadbalancerBackend.GetBackendId()) + if err != nil { + return err + } + guest := instance.(*SGuest) + + lbb.BackendServerId = guest.Id + lbb.ExternalId = extLoadbalancerBackend.GetGlobalId() + return nil +} + +func (lbb *SQcloudCachedLb) SyncWithCloudLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, extLoadbalancerBackend cloudprovider.ICloudLoadbalancerBackend, syncOwnerId mcclient.IIdentityProvider) error { + lbb.SetModelManager(QcloudCachedLbManager, lbb) + cacheLbbg, err := lbb.GetCachedBackendGroup() + if err != nil { + return errors.Wrap(err, "QcloudCachedLb.SyncWithCloudLoadbalancerBackend.GetCachedBackendGroup") + } + + localLbbg, err := cacheLbbg.GetLocalBackendGroup(ctx, userCred) + if err != nil { + return errors.Wrap(err, "QcloudCachedLb.SyncWithCloudLoadbalancerBackend.GetLocalBackendGroup") + } + + locallbb, err := newLocalBackendFromCloudLoadbalancerBackend(ctx, userCred, localLbbg, extLoadbalancerBackend, syncOwnerId) + if err != nil { + return errors.Wrap(err, "QcloudCachedLb.SyncWithCloudLoadbalancerBackend.newLocalBackendFromCloudLoadbalancerBackend") + } + + diff, err := db.UpdateWithLock(ctx, lbb, func() error { + if locallbb != nil { + lbb.BackendId = locallbb.GetId() + } + + return lbb.constructFieldsFromCloudLoadbalancerBackend(extLoadbalancerBackend) + }) + if err != nil { + return err + } + db.OpsLog.LogSyncUpdate(lbb, diff, userCred) + + SyncCloudProject(userCred, lbb, syncOwnerId, extLoadbalancerBackend, lbb.ManagerId) + + return nil +} + +func (man *SQcloudCachedLbManager) newFromCloudLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, loadbalancerBackendgroup *SQcloudCachedLbbg, extLoadbalancerBackend cloudprovider.ICloudLoadbalancerBackend, syncOwnerId mcclient.IIdentityProvider) (*SQcloudCachedLb, error) { + localBackendGroup, err := loadbalancerBackendgroup.GetLocalBackendGroup(ctx, userCred) + if err != nil { + return nil, err + } else if localBackendGroup == nil { + return nil, fmt.Errorf("newFromCloudLoadbalancerBackend localBackendGroup is nil") + } + + locallbb, err := newLocalBackendFromCloudLoadbalancerBackend(ctx, userCred, localBackendGroup, extLoadbalancerBackend, syncOwnerId) + if err != nil { + return nil, err + } + lbb := &SQcloudCachedLb{} + lbb.SetModelManager(man, lbb) + + lbb.CloudregionId = loadbalancerBackendgroup.CloudregionId + lbb.ManagerId = loadbalancerBackendgroup.ManagerId + lbb.CachedBackendGroupId = loadbalancerBackendgroup.Id + lbb.BackendId = locallbb.GetId() + lbb.ExternalId = extLoadbalancerBackend.GetGlobalId() + + newName, err := db.GenerateName(man, syncOwnerId, extLoadbalancerBackend.GetName()) + if err != nil { + return nil, err + } + lbb.Name = newName + + if err := lbb.constructFieldsFromCloudLoadbalancerBackend(extLoadbalancerBackend); err != nil { + return nil, err + } + + err = man.TableSpec().Insert(lbb) + + if err != nil { + return nil, err + } + + SyncCloudProject(userCred, lbb, syncOwnerId, extLoadbalancerBackend, loadbalancerBackendgroup.ManagerId) + + db.OpsLog.LogEvent(lbb, db.ACT_CREATE, lbb.GetShortDesc(ctx), userCred) + + return lbb, nil +} + +func (man *SQcloudCachedLbManager) getLoadbalancerBackendsByLoadbalancerBackendgroup(loadbalancerBackendgroup *SQcloudCachedLbbg) ([]SQcloudCachedLb, error) { + loadbalancerBackends := []SQcloudCachedLb{} + q := man.Query().Equals("cached_backend_group_id", loadbalancerBackendgroup.Id) + if err := db.FetchModelObjects(man, q, &loadbalancerBackends); err != nil { + return nil, err + } + return loadbalancerBackends, nil +} + +func (man *SQcloudCachedLbManager) SyncLoadbalancerBackends(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, loadbalancerBackendgroup *SQcloudCachedLbbg, lbbs []cloudprovider.ICloudLoadbalancerBackend, syncRange *SSyncRange) compare.SyncResult { + syncOwnerId := provider.GetOwnerId() + + lockman.LockClass(ctx, man, db.GetLockClassKey(man, syncOwnerId)) + defer lockman.ReleaseClass(ctx, man, db.GetLockClassKey(man, syncOwnerId)) + + syncResult := compare.SyncResult{} + + dbLbbs, err := man.getLoadbalancerBackendsByLoadbalancerBackendgroup(loadbalancerBackendgroup) + if err != nil { + syncResult.Error(err) + return syncResult + } + + removed := []SQcloudCachedLb{} + commondb := []SQcloudCachedLb{} + commonext := []cloudprovider.ICloudLoadbalancerBackend{} + added := []cloudprovider.ICloudLoadbalancerBackend{} + + err = compare.CompareSets(dbLbbs, lbbs, &removed, &commondb, &commonext, &added) + if err != nil { + syncResult.Error(err) + return syncResult + } + + for i := 0; i < len(removed); i++ { + err = removed[i].syncRemoveCloudLoadbalancerBackend(ctx, userCred) + if err != nil { + syncResult.DeleteError(err) + } else { + syncResult.Delete() + } + } + for i := 0; i < len(commondb); i++ { + err = commondb[i].SyncWithCloudLoadbalancerBackend(ctx, userCred, commonext[i], syncOwnerId) + if err != nil { + syncResult.UpdateError(err) + } else { + syncMetadata(ctx, userCred, &commondb[i], commonext[i]) + syncResult.Update() + } + } + for i := 0; i < len(added); i++ { + local, err := man.newFromCloudLoadbalancerBackend(ctx, userCred, loadbalancerBackendgroup, added[i], syncOwnerId) + if err != nil { + syncResult.AddError(err) + } else { + syncMetadata(ctx, userCred, local, added[i]) + syncResult.Add() + } + } + return syncResult +} + +func (man *SQcloudCachedLbManager) GetBackendsByLocalBackendId(backendId string) ([]SQcloudCachedLb, error) { + loadbalancerBackends := []SQcloudCachedLb{} + q := man.Query().IsFalse("pending_deleted").Equals("backend_id", backendId) + if err := db.FetchModelObjects(man, q, &loadbalancerBackends); err != nil { + return nil, err + } + return loadbalancerBackends, nil +} + +func (man *SQcloudCachedLbManager) CreateQcloudCachedLb(ctx context.Context, userCred mcclient.TokenCredential, lbb *SLoadbalancerBackend, cachedLbbg *SQcloudCachedLbbg, extLoadbalancerBackend cloudprovider.ICloudLoadbalancerBackend, syncOwnerId mcclient.IIdentityProvider) (*SQcloudCachedLb, error) { + cachedlbb := &SQcloudCachedLb{} + cachedlbb.SetModelManager(man, cachedlbb) + + cachedlbb.CloudregionId = cachedLbbg.CloudregionId + cachedlbb.ManagerId = cachedLbbg.ManagerId + cachedlbb.CachedBackendGroupId = cachedLbbg.GetId() + cachedlbb.BackendId = lbb.GetId() + cachedlbb.ExternalId = extLoadbalancerBackend.GetGlobalId() + + newName, err := db.GenerateName(man, syncOwnerId, extLoadbalancerBackend.GetName()) + if err != nil { + return nil, err + } + cachedlbb.Name = newName + + if err := cachedlbb.constructFieldsFromCloudLoadbalancerBackend(extLoadbalancerBackend); err != nil { + return nil, err + } + + err = man.TableSpec().Insert(cachedlbb) + + if err != nil { + return nil, err + } + + SyncCloudProject(userCred, lbb, syncOwnerId, extLoadbalancerBackend, cachedLbbg.ManagerId) + + db.OpsLog.LogEvent(cachedlbb, db.ACT_CREATE, lbb.GetShortDesc(ctx), userCred) + + return cachedlbb, nil +} diff --git a/pkg/compute/models/loadbalancerqcloudcachedlbbg.go b/pkg/compute/models/loadbalancerqcloudcachedlbbg.go new file mode 100644 index 0000000000..b01045d8d3 --- /dev/null +++ b/pkg/compute/models/loadbalancerqcloudcachedlbbg.go @@ -0,0 +1,347 @@ +// 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" + "fmt" + "strconv" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/compare" + "yunion.io/x/pkg/utils" + + 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/cloudprovider" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SQcloudCachedLbbgManager struct { + SLoadbalancerLogSkipper + db.SVirtualResourceBaseManager +} + +var QcloudCachedLbbgManager *SQcloudCachedLbbgManager + +func init() { + QcloudCachedLbbgManager = &SQcloudCachedLbbgManager{ + SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager( + SQcloudCachedLbbg{}, + "qcloudcachedlbbgs_tbl", + "qcloudcachedlbbg", + "qcloudcachedlbbgs", + ), + } + QcloudCachedLbbgManager.SetVirtualObject(QcloudCachedLbbgManager) +} + +type SQcloudCachedLbbg struct { + db.SVirtualResourceBase + db.SExternalizedResourceBase + + SManagedResourceBase + SCloudregionResourceBase + + LoadbalancerId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` + BackendGroupId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` + AssociatedId string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` // 关联ID + AssociatedType string `width:"36" charset:"ascii" nullable:"true" list:"user" create:"optional"` // 关联类型, listener || rule +} + +func (lbb *SQcloudCachedLbbg) GetCustomizeColumns(context.Context, mcclient.TokenCredential, jsonutils.JSONObject) *jsonutils.JSONDict { + return nil +} + +func (lbbg *SQcloudCachedLbbg) GetLocalBackendGroup(ctx context.Context, userCred mcclient.TokenCredential) (*SLoadbalancerBackendGroup, error) { + if len(lbbg.BackendGroupId) == 0 { + return nil, fmt.Errorf("GetLocalBackendGroup no related local backendgroup") + } + + locallbbg, err := db.FetchById(LoadbalancerBackendGroupManager, lbbg.BackendGroupId) + if err != nil { + return nil, err + } + + return locallbbg.(*SLoadbalancerBackendGroup), err +} + +func (lbbg *SQcloudCachedLbbg) GetLoadbalancer() *SLoadbalancer { + lb, err := LoadbalancerManager.FetchById(lbbg.LoadbalancerId) + if err != nil { + log.Errorf("failed to find loadbalancer for backendgroup %s", lbbg.Name) + return nil + } + return lb.(*SLoadbalancer) +} + +func (lbbg *SQcloudCachedLbbg) GetCachedBackends() ([]SQcloudCachedLb, error) { + ret := []SQcloudCachedLb{} + err := QcloudCachedLbManager.Query().Equals("cached_backend_group_id", lbbg.GetId()).IsFalse("pending_deleted").All(&ret) + if err != nil { + log.Errorf("failed to get cached backends for backendgroup %s", lbbg.Name) + return nil, err + } + + return ret, nil +} + +func (lbbg *SQcloudCachedLbbg) GetICloudLoadbalancerBackendGroup() (cloudprovider.ICloudLoadbalancerBackendGroup, error) { + if len(lbbg.ExternalId) == 0 { + return nil, fmt.Errorf("backendgroup %s has no external id", lbbg.GetId()) + } + + lb := lbbg.GetLoadbalancer() + if lb == nil { + return nil, fmt.Errorf("backendgroup %s releated loadbalancer not found", lbbg.GetId()) + } + + iregion, err := lb.GetIRegion() + if err != nil { + return nil, err + } + + ilb, err := iregion.GetILoadBalancerById(lb.GetExternalId()) + if err != nil { + return nil, err + } + + ilbbg, err := ilb.GetILoadBalancerBackendGroupById(lbbg.ExternalId) + if err != nil { + return nil, err + } + + return ilbbg, nil +} + +func (lbbg *SQcloudCachedLbbg) syncRemoveCloudLoadbalancerBackendgroup(ctx context.Context, userCred mcclient.TokenCredential) error { + lockman.LockObject(ctx, lbbg) + defer lockman.ReleaseObject(ctx, lbbg) + + err := lbbg.ValidateDeleteCondition(ctx) + if err != nil { // cannot delete + err = lbbg.SetStatus(userCred, api.LB_STATUS_UNKNOWN, "sync to delete") + } else { + lbbg.SetModelManager(QcloudCachedLbbgManager, lbbg) + _, err := db.Update(lbbg, func() error { + return lbbg.MarkDelete() + }) + if err != nil { + return err + } + } + + return err +} + +func (lbbg *SQcloudCachedLbbg) isBackendsMatch(backends []SLoadbalancerBackend, ibackends []cloudprovider.ICloudLoadbalancerBackend) bool { + if len(ibackends) != len(backends) { + return false + } + + locals := []string{} + remotes := []string{} + + for i := range backends { + guest := backends[i].GetGuest() + seg := strings.Join([]string{guest.ExternalId, strconv.Itoa(backends[i].Weight), strconv.Itoa(backends[i].Port)}, "/") + locals = append(locals, seg) + } + + for i := range ibackends { + ibackend := ibackends[i] + seg := strings.Join([]string{ibackend.GetBackendId(), strconv.Itoa(ibackend.GetWeight()), strconv.Itoa(ibackend.GetPort())}, "/") + remotes = append(remotes, seg) + } + + for i := range remotes { + if !utils.IsInStringArray(remotes[i], locals) { + return false + } + } + + return true +} + +func (lbbg *SQcloudCachedLbbg) SyncWithCloudLoadbalancerBackendgroup(ctx context.Context, userCred mcclient.TokenCredential, lb *SLoadbalancer, extLoadbalancerBackendgroup cloudprovider.ICloudLoadbalancerBackendGroup, syncOwnerId mcclient.IIdentityProvider) error { + lbbg.SetModelManager(QcloudCachedLbbgManager, lbbg) + + ibackends, err := extLoadbalancerBackendgroup.GetILoadbalancerBackends() + if err != nil { + return errors.Wrap(err, "QcloudCachedLbbg.SyncWithCloudLoadbalancerBackendgroup.GetILoadbalancerBackends") + } + + localLbbg, err := lbbg.GetLocalBackendGroup(ctx, userCred) + if err != nil { + return errors.Wrap(err, "QcloudCachedLbbg.SyncWithCloudLoadbalancerBackendgroup.GetLocalBackendGroup") + } + + backends, err := localLbbg.GetBackends() + if err != nil { + return errors.Wrap(err, "QcloudCachedLbbg.SyncWithCloudLoadbalancerBackendgroup.GetBackends") + } + + var newLocalLbbg *SLoadbalancerBackendGroup + if !lbbg.isBackendsMatch(backends, ibackends) { + newLocalLbbg, err = newLocalBackendgroupFromCloudLoadbalancerBackendgroup(ctx, userCred, lb, extLoadbalancerBackendgroup, syncOwnerId) + if err != nil { + return errors.Wrap(err, "QcloudCachedLbbg.SyncWithCloudLoadbalancerBackendgroup.newLocalBackendgroupFromCloudLoadbalancerBackendgroup") + } + } + + diff, err := db.UpdateWithLock(ctx, lbbg, func() error { + lbbg.Status = extLoadbalancerBackendgroup.GetStatus() + if newLocalLbbg != nil { + lbbg.BackendGroupId = newLocalLbbg.GetId() + } + return nil + }) + if err != nil { + return err + } + db.OpsLog.LogSyncUpdate(lbbg, diff, userCred) + + SyncCloudProject(userCred, lbbg, syncOwnerId, extLoadbalancerBackendgroup, lb.ManagerId) + return err +} + +func (man *SQcloudCachedLbbgManager) GetCachedBackendGroupByAssociateId(associateId string) (*SQcloudCachedLbbg, error) { + ret := &SQcloudCachedLbbg{} + err := man.Query().IsFalse("pending_deleted").Equals("associated_id", associateId).First(ret) + if err != nil { + return nil, err + } + + ret.SetModelManager(man, ret) + return ret, nil +} + +func (man *SQcloudCachedLbbgManager) GetCachedBackendGroups(backendGroupId string) ([]SQcloudCachedLbbg, error) { + ret := []SQcloudCachedLbbg{} + err := man.Query().IsFalse("pending_deleted").Equals("backend_group_id", backendGroupId).All(&ret) + if err != nil { + return nil, err + } + + return ret, nil +} + +func (man *SQcloudCachedLbbgManager) getLoadbalancerBackendgroupsByLoadbalancer(lb *SLoadbalancer) ([]SQcloudCachedLbbg, error) { + lbbgs := []SQcloudCachedLbbg{} + q := man.Query().IsFalse("pending_deleted").Equals("loadbalancer_id", lb.Id) + if err := db.FetchModelObjects(man, q, &lbbgs); err != nil { + log.Errorf("failed to get lbbgs for lb: %s error: %v", lb.Name, err) + return nil, err + } + return lbbgs, nil +} + +func (man *SQcloudCachedLbbgManager) SyncLoadbalancerBackendgroups(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, lb *SLoadbalancer, lbbgs []cloudprovider.ICloudLoadbalancerBackendGroup, syncRange *SSyncRange) ([]SQcloudCachedLbbg, []cloudprovider.ICloudLoadbalancerBackendGroup, compare.SyncResult) { + syncOwnerId := provider.GetOwnerId() + + lockman.LockClass(ctx, man, db.GetLockClassKey(man, syncOwnerId)) + defer lockman.ReleaseClass(ctx, man, db.GetLockClassKey(man, syncOwnerId)) + + localLbgs := []SQcloudCachedLbbg{} + remoteLbbgs := []cloudprovider.ICloudLoadbalancerBackendGroup{} + syncResult := compare.SyncResult{} + + dbLbbgs, err := man.getLoadbalancerBackendgroupsByLoadbalancer(lb) + if err != nil { + syncResult.Error(err) + return nil, nil, syncResult + } + + removed := []SQcloudCachedLbbg{} + commondb := []SQcloudCachedLbbg{} + commonext := []cloudprovider.ICloudLoadbalancerBackendGroup{} + added := []cloudprovider.ICloudLoadbalancerBackendGroup{} + + err = compare.CompareSets(dbLbbgs, lbbgs, &removed, &commondb, &commonext, &added) + if err != nil { + syncResult.Error(err) + return nil, nil, syncResult + } + + for i := 0; i < len(removed); i++ { + err = removed[i].syncRemoveCloudLoadbalancerBackendgroup(ctx, userCred) + if err != nil { + syncResult.DeleteError(err) + } else { + syncResult.Delete() + } + } + for i := 0; i < len(commondb); i++ { + err = commondb[i].SyncWithCloudLoadbalancerBackendgroup(ctx, userCred, lb, commonext[i], provider.GetOwnerId()) + if err != nil { + syncResult.UpdateError(err) + } else { + syncMetadata(ctx, userCred, &commondb[i], commonext[i]) + localLbgs = append(localLbgs, commondb[i]) + remoteLbbgs = append(remoteLbbgs, commonext[i]) + syncResult.Update() + } + } + for i := 0; i < len(added); i++ { + new, err := man.newFromCloudLoadbalancerBackendgroup(ctx, userCred, lb, added[i], syncOwnerId) + if err != nil { + syncResult.AddError(err) + } else { + syncMetadata(ctx, userCred, new, added[i]) + localLbgs = append(localLbgs, *new) + remoteLbbgs = append(remoteLbbgs, added[i]) + syncResult.Add() + } + } + return localLbgs, remoteLbbgs, syncResult +} + +func (man *SQcloudCachedLbbgManager) newFromCloudLoadbalancerBackendgroup(ctx context.Context, userCred mcclient.TokenCredential, lb *SLoadbalancer, extLoadbalancerBackendgroup cloudprovider.ICloudLoadbalancerBackendGroup, syncOwnerId mcclient.IIdentityProvider) (*SQcloudCachedLbbg, error) { + LocalLbbg, err := newLocalBackendgroupFromCloudLoadbalancerBackendgroup(ctx, userCred, lb, extLoadbalancerBackendgroup, syncOwnerId) + if err != nil { + return nil, err + } + + lbbg := &SQcloudCachedLbbg{} + lbbg.SetModelManager(man, lbbg) + + lbbg.ManagerId = lb.ManagerId + lbbg.CloudregionId = lb.CloudregionId + lbbg.LoadbalancerId = lb.Id + lbbg.BackendGroupId = LocalLbbg.GetId() + lbbg.ExternalId = extLoadbalancerBackendgroup.GetGlobalId() + + newName, err := db.GenerateName(man, syncOwnerId, LocalLbbg.GetName()) + if err != nil { + return nil, err + } + + lbbg.Name = newName + lbbg.Status = extLoadbalancerBackendgroup.GetStatus() + + err = man.TableSpec().Insert(lbbg) + if err != nil { + return nil, err + } + + SyncCloudProject(userCred, lbbg, syncOwnerId, extLoadbalancerBackendgroup, lb.ManagerId) + + db.OpsLog.LogEvent(lbbg, db.ACT_CREATE, lbbg.GetShortDesc(ctx), userCred) + return lbbg, nil +} diff --git a/pkg/compute/models/regiondrivers.go b/pkg/compute/models/regiondrivers.go index cd8423d628..bd66872dad 100644 --- a/pkg/compute/models/regiondrivers.go +++ b/pkg/compute/models/regiondrivers.go @@ -60,7 +60,7 @@ type IRegionDriver interface { RequestCreateLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lbbg *SLoadbalancerBackendGroup, backends []cloudprovider.SLoadbalancerBackend, task taskman.ITask) error RequestDeleteLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lbbg *SLoadbalancerBackendGroup, task taskman.ITask) error ValidateDeleteLoadbalancerBackendGroupCondition(ctx context.Context, lbbb *SLoadbalancerBackendGroup) error - RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *SLoadbalancerListener, lbbg *SLoadbalancerBackendGroup, task taskman.ITask) error + RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *SLoadbalancerListener, task taskman.ITask) error RequestPullRegionLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localRegion *SCloudregion, remoteRegion cloudprovider.ICloudRegion, syncRange *SSyncRange) error RequestPullLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localLoadbalancer *SLoadbalancer, remoteLoadbalancer cloudprovider.ICloudLoadbalancer, syncRange *SSyncRange) error GetBackendStatusForAdd() []string diff --git a/pkg/compute/regiondrivers/aws.go b/pkg/compute/regiondrivers/aws.go index 4e9926bc38..a8f4011641 100644 --- a/pkg/compute/regiondrivers/aws.go +++ b/pkg/compute/regiondrivers/aws.go @@ -1327,7 +1327,13 @@ func (self *SAwsRegionDriver) RequestSyncLoadbalancerListener(ctx context.Contex return nil } -func (self *SAwsRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, lbbg *models.SLoadbalancerBackendGroup, task taskman.ITask) error { +func (self *SAwsRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, task taskman.ITask) error { + lbbg := lblis.GetLoadbalancerBackendGroup() + if lbbg == nil { + err := fmt.Errorf("failed to find lbbg for lblis %s", lblis.Name) + return errors.Wrap(err, "AwsRegionDriver.RequestSyncLoadbalancerBackendGroup.GetLoadbalancerBackendGroup") + } + lb := lblis.GetLoadbalancer() if lb == nil { return errors.Wrap(fmt.Errorf("listener %s related loadbalancer not found", lblis.GetId()), "AwsRegionDriver.RequestSyncLoadbalancerBackendGroup.GetLoadbalancer") diff --git a/pkg/compute/regiondrivers/huawei.go b/pkg/compute/regiondrivers/huawei.go index 4339a8a4e1..ce1d7f1b4b 100644 --- a/pkg/compute/regiondrivers/huawei.go +++ b/pkg/compute/regiondrivers/huawei.go @@ -862,8 +862,14 @@ func (self *SHuaWeiRegionDriver) removeCachedLbbg(ctx context.Context, userCred return nil } -func (self *SHuaWeiRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, lbbg *models.SLoadbalancerBackendGroup, task taskman.ITask) error { +func (self *SHuaWeiRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, task taskman.ITask) error { taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + lbbg := lblis.GetLoadbalancerBackendGroup() + if lbbg == nil { + err := fmt.Errorf("failed to find lbbg for lblis %s", lblis.Name) + return nil, errors.Wrap(err, "HuaWeiRegionDriver.RequestSyncLoadbalancerbackendGroup.GetLoadbalancerBackendGroup") + } + iRegion, err := lbbg.GetIRegion() if err != nil { return nil, err diff --git a/pkg/compute/regiondrivers/kvm.go b/pkg/compute/regiondrivers/kvm.go index f918448a68..cf85eda299 100644 --- a/pkg/compute/regiondrivers/kvm.go +++ b/pkg/compute/regiondrivers/kvm.go @@ -662,7 +662,7 @@ func (self *SKVMRegionDriver) RequestDeleteLoadbalancerAcl(ctx context.Context, return nil } -func (self *SKVMRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, lbbg *models.SLoadbalancerBackendGroup, task taskman.ITask) error { +func (self *SKVMRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, task taskman.ITask) error { task.ScheduleRun(nil) return nil } diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 0315a0287a..bb711004c3 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -548,7 +548,7 @@ func (self *SManagedVirtualizationRegionDriver) RequestDeleteLoadbalancerBackend return nil } -func (self *SManagedVirtualizationRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, lbbg *models.SLoadbalancerBackendGroup, task taskman.ITask) error { +func (self *SManagedVirtualizationRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, task taskman.ITask) error { task.ScheduleRun(nil) return nil } diff --git a/pkg/compute/regiondrivers/qcloud.go b/pkg/compute/regiondrivers/qcloud.go index 64249a7852..fb94f9fd60 100644 --- a/pkg/compute/regiondrivers/qcloud.go +++ b/pkg/compute/regiondrivers/qcloud.go @@ -16,13 +16,14 @@ package regiondrivers import ( "context" - "database/sql" "fmt" "regexp" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/utils" + "yunion.io/x/sqlchemy" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" @@ -132,22 +133,38 @@ func (self *SQcloudRegionDriver) ValidateCreateLoadbalancerListenerData(ctx cont return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", lbbg.Name, lbbg.Id, lbbg.LoadbalancerId, lb.Id) } else { - if lbbg != nil { - count, err := lbbg.RefCount() - if err != nil { - return nil, httperrors.NewInternalServerError("get lbbg RefCount fail %s", err) - } - if count > 0 { - return nil, httperrors.NewResourceBusyError("backendgroup aready related with other listener/rule") + if utils.IsInStringArray(listenerType, []string{api.LB_LISTENER_TYPE_TCP, api.LB_LISTENER_TYPE_UDP}) { + if lbbg == nil { + return nil, httperrors.NewMissingParameterError("backend_group_id") } - if utils.IsInStringArray(listenerType, []string{api.LB_LISTENER_TYPE_UDP, api.LB_LISTENER_TYPE_TCP}) { - if err := lbbg.AllBackendsUnique(); err != nil { - return nil, httperrors.NewConflictError(err.Error()) + // listener check + q := models.LoadbalancerListenerManager.Query() + q = q.Equals("loadbalancer_id", lb.GetId()) + q = q.Equals("listener_type", listenerType) + q = q.Equals("backend_group_id", lbbg.GetId()) + q = q.IsFalse("pending_deleted") + count, err := q.CountWithError() + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + + if count > 0 { + return nil, httperrors.NewConflictError("loadbalancer backendgroup aready associate with other %s listener", listenerType) + } + + // lbbg backend check + lbbs, err := lbbg.GetBackends() + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + + for i := range lbbs { + err = checkQcloudBackendGroupUsable("", listenerType, lbbs[i].BackendId, lbbs[i].Port) + if err != nil { + return nil, err } } - - data.Set("backend_group_id", jsonutils.NewString(lbbg.GetId())) } } @@ -196,7 +213,7 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerBackendGroup(ctx conte taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { iRegion, err := lbbg.GetIRegion() if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackendGroup.GetIRegion") } // 腾讯云本身没有后端服务器组,因此不需要在qcloud端执行创建操作 @@ -210,7 +227,7 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerBackendGroup(ctx conte } iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId) if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackendGroup.GetILoadBalancerById") } group := &cloudprovider.SLoadbalancerBackendGroup{ Name: lbbg.Name, @@ -219,14 +236,14 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerBackendGroup(ctx conte } iLoadbalancerBackendGroup, err := iLoadbalancer.CreateILoadBalancerBackendGroup(group) if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackendGroup.CreateILoadBalancerBackendGroup") } if err := db.SetExternalId(lbbg, userCred, iLoadbalancerBackendGroup.GetGlobalId()); err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackendGroup.GetGlobalId") } iBackends, err := iLoadbalancerBackendGroup.GetILoadbalancerBackends() if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackendGroup.GetILoadbalancerBackends") } if len(iBackends) > 0 { provider := loadbalancer.GetCloudprovider() @@ -246,47 +263,49 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerBackend(ctx context.Co 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) + } + + cachedlbbgs, err := models.QcloudCachedLbbgManager.GetCachedBackendGroups(lbbg.GetId()) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackend.GetCachedBackendGroups") + } 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() == api.CLOUD_PROVIDER_QCLOUD { - cnt, err := lbbg.RefCount() + var ibackend cloudprovider.ICloudLoadbalancerBackend + for _, cachedLbbg := range cachedlbbgs { + iLoadbalancerBackendGroup, err := cachedLbbg.GetICloudLoadbalancerBackendGroup() if err != nil { - return nil, err + if err == cloudprovider.ErrNotFound { + continue + } + + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackend.GetICloudLoadbalancerBackendGroup") } - if cnt == 0 { - return nil, nil + + ibackend, err = iLoadbalancerBackendGroup.AddBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackend.AddBackendServer") + } + + _, err = models.QcloudCachedLbManager.CreateQcloudCachedLb(ctx, userCred, lbb, &cachedLbbg, ibackend, cachedLbbg.GetOwnerId()) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackend.CreateQcloudCachedLb") } } - lb := lbbg.GetLoadbalancer() - if lb == nil { - return nil, fmt.Errorf("failed to find lb for backendgroup %s", lbbg.Name) + if ibackend != nil { + if err := lbb.SyncWithCloudLoadbalancerBackend(ctx, userCred, ibackend, nil); err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerBackend.SyncWithCloudLoadbalancerBackend") + } } - 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 := db.SetExternalId(lbb, userCred, iLoadbalancerBackend.GetGlobalId()); err != nil { - return nil, err - } - return nil, lbb.SyncWithCloudLoadbalancerBackend(ctx, userCred, iLoadbalancerBackend, nil) + return nil, nil }) return nil } @@ -296,106 +315,48 @@ func (self *SQcloudRegionDriver) RequestDeleteLoadbalancerBackend(ctx context.Co 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, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.GetIRegion") - } - // ===========兼容腾讯云,未关联具体转发规则时,直接删除本地数据即可=============== - count, err := lbbg.RefCount() + cachedlbbs, err := models.QcloudCachedLbManager.GetBackendsByLocalBackendId(lbb.GetId()) if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.RefCount") - } - if count == 0 { - return nil, nil - } - - iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) - if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.GetLoadbalancerListeners") + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestDeleteLoadbalancerBackend.GetBackendsByLocalBackendId") } guest := lbb.GetGuest() if guest == nil { - return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name) + return nil, fmt.Errorf("loadbalancer backend %s related server not found", lbb.GetName()) } - // delete - if forword, _ := iLoadbalancer.GetMetadata().Int("Forward"); forword == 0 { - iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(lbbg.ExternalId) + for _, cachedlbb := range cachedlbbs { + cachedlbbg, _ := cachedlbb.GetCachedBackendGroup() + if cachedlbbg == nil { + log.Warningf("failed to find lbbg for backend %s", cachedlbb.Name) + continue + } + lb := cachedlbbg.GetLoadbalancer() + if lb == nil { + return nil, fmt.Errorf("failed to find lb for backendgroup %s", cachedlbbg.Name) + } + iRegion, err := lb.GetIRegion() if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.GetLoadbalancerListenerRules") + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestDeleteLoadbalancerBackend.GetIRegion") + } + iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestDeleteLoadbalancerBackend.GetILoadBalancerById") + } + iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(cachedlbbg.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestDeleteLoadbalancerBackend.GetILoadBalancerBackendGroupById") } - return nil, iLoadbalancerBackendGroup.RemoveBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) - } else { - lblis, err := lbbg.GetLoadbalancerListeners() + err = iLoadbalancerBackendGroup.RemoveBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) if err != nil { - if err != sql.ErrNoRows { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.GetLoadbalancerListeners") - } - } else { - for i := range lblis { - _lblis := lblis[i] - if utils.IsInStringArray(_lblis.ListenerType, []string{api.LB_LISTENER_TYPE_UDP, api.LB_LISTENER_TYPE_TCP}) { - external_id := fmt.Sprintf("%s/%s", lb.ExternalId, _lblis.ExternalId) - iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(external_id) - if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.GetILoadBalancerBackendGroupById") - } - backends, err := iLoadbalancerBackendGroup.GetILoadbalancerBackends() - if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.listener.GetILoadbalancerBackends") - } - - for i := range backends { - if backends[i].GetBackendId() == guest.ExternalId && backends[i].GetPort() == lbb.Port && backends[i].GetWeight() == lbb.Weight { - err = iLoadbalancerBackendGroup.RemoveBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) - if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.listener.RemoveBackendServer") - } - } - } - } - } + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestDeleteLoadbalancerBackend.RemoveBackendServer") } - lbrs, err := lbbg.GetLoadbalancerListenerRules() + err = db.DeleteModel(ctx, userCred, &cachedlbb) if err != nil { - if err != sql.ErrNoRows { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.GetLoadbalancerListenerRules") - } - } else { - for i := range lbrs { - lbr := lbrs[i] - external_id := fmt.Sprintf("%s/%s", lb.ExternalId, lbr.ExternalId) - iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(external_id) - if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.GetILoadBalancerBackendGroupById") - } - - backends, err := iLoadbalancerBackendGroup.GetILoadbalancerBackends() - if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.listener.GetILoadbalancerBackends") - } - - for i := range backends { - if backends[i].GetBackendId() == guest.ExternalId && backends[i].GetPort() == lbb.Port && backends[i].GetWeight() == lbb.Weight { - err = iLoadbalancerBackendGroup.RemoveBackendServer(guest.ExternalId, lbb.Weight, lbb.Port) - if err != nil { - return nil, errors.Wrap(err, "qcloudRegion.RequestDeleteLoadbalancerBackend.rule.RemoveBackendServer") - } - } - } - } + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestDeleteLoadbalancerBackend.DeleteModel") } } @@ -404,6 +365,149 @@ func (self *SQcloudRegionDriver) RequestDeleteLoadbalancerBackend(ctx context.Co return nil } +func (self *SQcloudRegionDriver) createCachedLbbg(lb *models.SLoadbalancer, lblis *models.SLoadbalancerListener, lbr *models.SLoadbalancerListenerRule, lbbg *models.SLoadbalancerBackendGroup) (*models.SQcloudCachedLbbg, error) { + // create loadbalancer backendgroup cache + cachedLbbg := &models.SQcloudCachedLbbg{} + cachedLbbg.ManagerId = lb.ManagerId + cachedLbbg.CloudregionId = lb.CloudregionId + cachedLbbg.LoadbalancerId = lb.GetId() + cachedLbbg.BackendGroupId = lbbg.GetId() + if lbr != nil { + cachedLbbg.AssociatedType = api.LB_ASSOCIATE_TYPE_RULE + cachedLbbg.AssociatedId = lbr.GetId() + } else { + cachedLbbg.AssociatedType = api.LB_ASSOCIATE_TYPE_LISTENER + cachedLbbg.AssociatedId = lblis.GetId() + } + + err := models.QcloudCachedLbbgManager.TableSpec().Insert(cachedLbbg) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createCachedLbbg.Insert") + } + + cachedLbbg.SetModelManager(models.QcloudCachedLbbgManager, cachedLbbg) + return cachedLbbg, nil +} + +func (self *SQcloudRegionDriver) syncCloudlbbs(ctx context.Context, userCred mcclient.TokenCredential, lb *models.SLoadbalancer, cachedLbbg *models.SQcloudCachedLbbg, extlbbg cloudprovider.ICloudLoadbalancerBackendGroup, backends []cloudprovider.SLoadbalancerBackend) error { + ibackends, err := extlbbg.GetILoadbalancerBackends() + if err != nil { + return errors.Wrap(err, "QcloudRegionDriver.syncCloudLoadbalancerBackends.GetILoadbalancerBackends") + } + + for i := range ibackends { + ibackend := ibackends[i] + err = extlbbg.RemoveBackendServer(ibackend.GetId(), ibackend.GetWeight(), ibackend.GetPort()) + if err != nil { + return errors.Wrap(err, "QcloudRegionDriver.syncCloudLoadbalancerBackends.RemoveBackendServer") + } + } + + for _, backend := range backends { + _, err = extlbbg.AddBackendServer(backend.ExternalID, backend.Weight, backend.Port) + if err != nil { + return errors.Wrap(err, "QcloudRegionDriver.syncCloudLoadbalancerBackends.AddBackendServer") + } + } + + return nil +} + +func (self *SQcloudRegionDriver) syncCachedLbbs(ctx context.Context, userCred mcclient.TokenCredential, lb *models.SLoadbalancer, lbbg *models.SQcloudCachedLbbg, extlbbg cloudprovider.ICloudLoadbalancerBackendGroup) error { + iBackends, err := extlbbg.GetILoadbalancerBackends() + if err != nil { + return errors.Wrap(err, "QcloudRegionDriver.syncLoadbalancerBackendCaches.GetILoadbalancerBackends") + } + + if len(iBackends) > 0 { + provider := lb.GetCloudprovider() + if provider == nil { + return fmt.Errorf("failed to find cloudprovider for lb %s", lb.Name) + } + + result := models.QcloudCachedLbManager.SyncLoadbalancerBackends(ctx, userCred, provider, lbbg, iBackends, &models.SSyncRange{}) + if result.IsError() { + return errors.Wrap(result.AllError(), "QcloudRegionDriver.syncLoadbalancerBackendCaches.SyncLoadbalancerBackends") + } + } + + return nil +} + +func (self *SQcloudRegionDriver) createLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, lbr *models.SLoadbalancerListenerRule, lbbg *models.SLoadbalancerBackendGroup, backends []cloudprovider.SLoadbalancerBackend) (jsonutils.JSONObject, error) { + iRegion, err := lbbg.GetIRegion() + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createLoadbalancerBackendGroup.GetIRegion") + } + lb := lbbg.GetLoadbalancer() + if lb == nil { + return nil, fmt.Errorf("failed to find loadbalancer for backendgroup %s", lbbg.Name) + } + iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createLoadbalancerBackendGroup.GetILoadBalancerById") + } + + var ilbbg cloudprovider.ICloudLoadbalancerBackendGroup + if lbr != nil { + l := lbr.GetLoadbalancerListener() + if l == nil { + return nil, fmt.Errorf("could not create loadbalancer backendgroup, loadbalancer listener rule %s related listener not found", lbr.GetName()) + } + + ilblis, err := iLoadbalancer.GetILoadBalancerListenerById(l.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createLoadbalancerBackendGroup.GetILoadBalancerListenerById") + } + + ilbr, err := ilblis.GetILoadBalancerListenerRuleById(lbr.GetExternalId()) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createLoadbalancerBackendGroup.GetILoadBalancerListenerRuleById") + } + + extLbbgId := ilbr.GetBackendGroupId() + ilbbg, err = iLoadbalancer.GetILoadBalancerBackendGroupById(extLbbgId) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createLoadbalancerBackendGroup.GetILoadBalancerBackendGroupById") + } + } else if lblis != nil { + ilblis, err := iLoadbalancer.GetILoadBalancerListenerById(lblis.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createLoadbalancerBackendGroup.Listener.GetILoadBalancerListenerById") + } + + extLbbgId := ilblis.GetBackendGroupId() + ilbbg, err = iLoadbalancer.GetILoadBalancerBackendGroupById(extLbbgId) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createLoadbalancerBackendGroup.Listener.GetILoadBalancerBackendGroupById") + } + } else { + return nil, fmt.Errorf("could not create loadbalancer backendgroup, loadbalancer listener & rule are nil") + } + + cachedLbbg, err := self.createCachedLbbg(lb, lblis, lbr, lbbg) + if err != nil { + return nil, errors.Wrap(err, "QcloudRegionDriver.createLoadbalancerBackendGroupCache") + } + + if err := db.SetExternalId(cachedLbbg, userCred, ilbbg.GetGlobalId()); err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.createLoadbalancerBackendGroup.SetExternalId") + } + + err = self.syncCloudlbbs(ctx, userCred, lb, cachedLbbg, ilbbg, backends) + if err != nil { + return nil, errors.Wrap(err, "QcloudRegionDriver.createLoadbalancerBackendGroup.syncCloudLoadbalancerBackends") + } + + err = self.syncCachedLbbs(ctx, userCred, lb, cachedLbbg, ilbbg) + if err != nil { + return nil, errors.Wrap(err, "QcloudRegionDriver.createLoadbalancerBackendGroup.syncLoadbalancerBackendCaches") + } + + return nil, 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) { { @@ -416,18 +520,18 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListener(ctx context.C cert, err := models.LoadbalancerCertificateManager.FetchById(certId) if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListener.FetchById") } lbcert, err := models.CachedLoadbalancerCertificateManager.GetOrCreateCachedCertificate(ctx, userCred, provider, lblis, cert.(*models.SLoadbalancerCertificate)) if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListener.GetOrCreateCachedCertificate") } if len(lbcert.ExternalId) == 0 { err = self.RequestCreateLoadbalancerCertificate(ctx, userCred, lbcert, task) if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListener.RequestCreateLoadbalancerCertificate") } } @@ -441,44 +545,9 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListener(ctx context.C } } - { - aclId, _ := task.GetParams().GetString("acl_id") - if len(aclId) > 0 { - provider := models.CloudproviderManager.FetchCloudproviderById(lblis.ManagerId) - if provider == nil { - return nil, fmt.Errorf("failed to find provider for lblis %s", lblis.Name) - } - - acl, err := models.LoadbalancerAclManager.FetchById(aclId) - if err != nil { - return nil, err - } - - lbacl, err := models.CachedLoadbalancerAclManager.GetOrCreateCachedAcl(ctx, userCred, provider, lblis, acl.(*models.SLoadbalancerAcl)) - if err != nil { - return nil, err - } - - if len(lbacl.ExternalId) == 0 { - err = self.RequestCreateLoadbalancerAcl(ctx, userCred, lbacl, task) - if err != nil { - return nil, err - } - } - - _, err = db.Update(lblis, func() error { - lblis.CachedAclId = lbacl.GetId() - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestCreateLoadbalancerListener.UpdateCachedAclId") - } - } - } - - params, err := lblis.GetLoadbalancerListenerParams() + params, err := lblis.GetQcloudLoadbalancerListenerParams() if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListener.GetQcloudLoadbalancerListenerParams") } loadbalancer := lblis.GetLoadbalancer() if loadbalancer == nil { @@ -486,49 +555,36 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListener(ctx context.C } iRegion, err := loadbalancer.GetIRegion() if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListener.GetIRegion") } iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId) if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListener.GetILoadBalancerById") } iListener, err := iLoadbalancer.CreateILoadBalancerListener(params) if err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListener.CreateILoadBalancerListener") } if err := db.SetExternalId(lblis, userCred, iListener.GetGlobalId()); err != nil { - return nil, err + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListener.SetExternalId") } // ====腾讯云添加后端服务器===== - if iRegion.GetProvider() == api.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) - } + if !utils.IsInStringArray(lblis.ListenerType, []string{api.LB_LISTENER_TYPE_HTTP, api.LB_LISTENER_TYPE_HTTPS}) { + lbbg := lblis.GetLoadbalancerBackendGroup() + if lbbg == nil { + err := fmt.Errorf("loadbalancer listener %s related backendgroup not found", lblis.GetName()) + return nil, errors.Wrap(err, "SQcloudRegionDriver.RequestCreateLoadbalancerListener.GetLoadbalancerBackendGroup") + } - extBgID := iListener.GetBackendGroupId() - if len(extBgID) == 0 { - return nil, fmt.Errorf("the backend group external id of loadbalancer listener %s is empty", lblis.GetId()) - } + backends, err := lbbg.GetBackendsParams() + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.RequestCreateLoadbalancerListener.GetBackendsParams") + } - 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 - } - } + _, err = self.createLoadbalancerBackendGroup(ctx, userCred, lblis, nil, lbbg, backends) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.RequestCreateLoadbalancerListener.createLoadbalancerBackendGroup") } } @@ -537,6 +593,48 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListener(ctx context.C return nil } +func (self *SQcloudRegionDriver) GetLoadbalancerListenerRuleInputParams(lblis *models.SLoadbalancerListener, lbr *models.SLoadbalancerListenerRule) *cloudprovider.SLoadbalancerListenerRule { + scheduler := "" + switch lblis.Scheduler { + case api.LB_SCHEDULER_WRR: + scheduler = "WRR" + case api.LB_SCHEDULER_WLC: + scheduler = "LEAST_CONN" + case api.LB_SCHEDULER_SCH: + scheduler = "IP_HASH" + default: + scheduler = "WRR" + } + + sessionTimeout := 0 + if lblis.StickySession == api.LB_BOOL_ON { + sessionTimeout = lblis.StickySessionCookieTimeout + } + + rule := &cloudprovider.SLoadbalancerListenerRule{ + Name: lbr.Name, + Domain: lbr.Domain, + Path: lbr.Path, + + Scheduler: scheduler, + + HealthCheck: lblis.HealthCheck, + HealthCheckType: lblis.HealthCheckType, + HealthCheckTimeout: lblis.HealthCheckTimeout, + HealthCheckDomain: lblis.HealthCheckDomain, + HealthCheckHttpCode: lblis.HealthCheckHttpCode, + HealthCheckURI: lblis.HealthCheckURI, + HealthCheckInterval: lblis.HealthCheckInterval, + + HealthCheckRise: lblis.HealthCheckRise, + HealthCheckFail: lblis.HealthCheckFall, + + StickySessionCookieTimeout: sessionTimeout, + } + + return rule +} + 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() @@ -559,19 +657,7 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListenerRule(ctx conte if err != nil { return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListenerRule.GetILoadBalancerListenerById") } - 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 - } + rule := self.GetLoadbalancerListenerRuleInputParams(listener, lbr) iListenerRule, err := iListener.CreateILoadBalancerListenerRule(rule) if err != nil { return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListenerRule.CreateILoadBalancerListenerRule") @@ -580,30 +666,22 @@ func (self *SQcloudRegionDriver) RequestCreateLoadbalancerListenerRule(ctx conte return nil, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListenerRule.UpdateListenerRule") } // ====腾讯云添加后端服务器===== - if len(rule.BackendGroupID) > 0 { - ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(iListenerRule.GetBackendGroupId()) - 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, errors.Wrap(err, "qcloudRegionDriver.RequestCreateLoadbalancerListenerRule.AddBackendServer") - } - } - + lbbg := lbr.GetLoadbalancerBackendGroup() + if lbbg == nil { + err := fmt.Errorf("loadbalancer listener rule %s related backendgroup not found", lbr.GetName()) + return nil, errors.Wrap(err, "SQcloudRegionDriver.RequestCreateLoadbalancerListener.GetLoadbalancerBackendGroup") } + + backends, err := lbbg.GetBackendsParams() + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.RequestCreateLoadbalancerListener.GetBackendsParams") + } + + _, err = self.createLoadbalancerBackendGroup(ctx, userCred, nil, lbr, lbbg, backends) + if err != nil { + return nil, errors.Wrap(err, "SQcloudRegionDriver.RequestCreateLoadbalancerListener.createLoadbalancerBackendGroup") + } + return nil, lbr.SyncWithCloudLoadbalancerListenerRule(ctx, userCred, iListenerRule, nil) }) return nil @@ -671,6 +749,40 @@ func (self *SQcloudRegionDriver) ValidateUpdateLoadbalancerListenerData(ctx cont if lbbg, ok := backendGroup.(*models.SLoadbalancerBackendGroup); ok && lbbg.LoadbalancerId != lblis.LoadbalancerId { return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", lbbg.Name, lbbg.Id, lbbg.LoadbalancerId, lblis.LoadbalancerId) + } else { + if utils.IsInStringArray(lblis.ListenerType, []string{api.LB_LISTENER_TYPE_TCP, api.LB_LISTENER_TYPE_UDP}) { + cachedLbbgs, err := lbbg.GetQcloudCachedlbbg() + if err != nil { + return nil, err + } + + if len(cachedLbbgs) > 0 { + for i := range cachedLbbgs { + if cachedLbbgs[i].AssociatedType == api.LB_ASSOCIATE_TYPE_LISTENER && cachedLbbgs[i].AssociatedId != lblis.GetId() { + _lblis, err := db.FetchById(models.LoadbalancerListenerManager, cachedLbbgs[i].AssociatedId) + if err != nil { + return nil, err + } + + if _lblis.(*models.SLoadbalancerListener).ListenerType == lblis.ListenerType { + return nil, httperrors.NewConflictError("loadbalancer aready associated with fourth layer listener %s", cachedLbbgs[i].AssociatedId) + } + } + } + } + + lbbs, err := lbbg.GetBackends() + if err != nil { + return nil, err + } + + for i := range lbbs { + err = checkQcloudBackendGroupUsable(lblis.BackendGroupId, lblis.ListenerType, lbbs[i].BackendId, lbbs[i].Port) + if err != nil { + return nil, err + } + } + } } } @@ -737,6 +849,12 @@ func (self *SQcloudRegionDriver) ValidateCreateLoadbalancerListenerRuleData(ctx if lbbg, ok := backendGroup.(*models.SLoadbalancerBackendGroup); ok && lbbg.LoadbalancerId != listener.LoadbalancerId { return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", lbbg.Name, lbbg.Id, lbbg.LoadbalancerId, listener.LoadbalancerId) + } else { + if lbbg == nil { + return nil, httperrors.NewMissingParameterError("backend_group_id") + } + + data.Set("backend_group_id", jsonutils.NewString(lbbg.GetId())) } err = models.LoadbalancerListenerRuleCheckUniqueness(ctx, listener, domainV.Value, pathV.Value) @@ -767,8 +885,8 @@ func (self *SQcloudRegionDriver) ValidateUpdateLoadbalancerListenerRuleData(ctx } // validate backend port unique -func CheckQcloudBackendPortUnique(backendGroupId string, backendId string, port int) error { - q1 := models.LoadbalancerBackendManager.Query("backend_group_id").Equals("port", port).Equals("backend_id", backendId).IsFalse("pending_deleted").Distinct() +func CheckQcloudBackendPortUnique(backendGroupId string, backendServerId string, port int) error { + q1 := models.LoadbalancerBackendManager.Query("backend_group_id").Equals("port", port).Equals("backend_id", backendServerId).IsFalse("pending_deleted").Distinct() count, err := q1.CountWithError() if err != nil { return err @@ -778,34 +896,75 @@ func CheckQcloudBackendPortUnique(backendGroupId string, backendId string, port return nil } - q2 := models.LoadbalancerBackendManager.Query().Equals("backend_group_id", backendGroupId).Equals("port", port).Equals("backend_id", backendId).IsFalse("pending_deleted").Distinct() + q2 := models.LoadbalancerBackendManager.Query().Equals("backend_group_id", backendGroupId).Equals("port", port).Equals("backend_id", backendServerId).IsFalse("pending_deleted").Distinct() count, err = q2.CountWithError() if err != nil { return err } if count > 0 { - return fmt.Errorf("backend %s with port %d already in used in backendgroup", backendId, port) + return httperrors.NewConflictError("server %s with port %d already in used", backendServerId, port) } - q3 := models.LoadbalancerListenerManager.Query().Equals("backend_group_id", backendGroupId).In("listener_type", []string{api.LB_LISTENER_TYPE_TCP, api.LB_LISTENER_TYPE_UDP}).IsFalse("pending_deleted") - count, err = q3.CountWithError() - if err != nil { - return err - } - - if count == 0 { - return nil - } - - q4 := models.LoadbalancerListenerManager.Query().In("backend_group_id", q1.SubQuery()).In("listener_type", []string{api.LB_LISTENER_TYPE_TCP, api.LB_LISTENER_TYPE_UDP}).IsFalse("pending_deleted") - count, err = q4.CountWithError() + // 检查 当前服务器组没有backend with port记录,但是其他backendgroup存在backend with port记录的情况 + q3 := models.LoadbalancerBackendGroupManager.Query().IsFalse("pending_deleted") + subLblis := models.LoadbalancerListenerManager.Query().SubQuery() + q3 = q3.Join(subLblis, sqlchemy.Equals(subLblis.Field("backend_group_id"), q3.Field("id"))) + q3 = q3.Equals("id", backendGroupId) + count, err = q3.Filter(sqlchemy.Equals(subLblis.Field("listener_type"), api.LB_LISTENER_TYPE_TCP)).CountWithError() if err != nil { return err } if count > 0 { - return fmt.Errorf("backend %s with port %d already in used in other backendgroup", backendId, port) + err = checkQcloudBackendGroupUsable(backendGroupId, api.LB_LISTENER_TYPE_TCP, backendServerId, port) + if err != nil { + return err + } + } + + count, err = q3.Filter(sqlchemy.Equals(subLblis.Field("listener_type"), api.LB_LISTENER_TYPE_UDP)).CountWithError() + if err != nil { + return err + } + + if count > 0 { + err = checkQcloudBackendGroupUsable(backendGroupId, api.LB_LISTENER_TYPE_UDP, backendServerId, port) + if err != nil { + return err + } + } + + return nil +} + +func checkQcloudBackendGroupUsable(fromBackendGroup string, listenerType string, backendServerId string, port int) error { + q := models.QcloudCachedLbManager.Query() + subLbb := models.LoadbalancerBackendManager.Query().SubQuery() + subCachedLbbg := models.QcloudCachedLbbgManager.Query().SubQuery() + subLbbg := models.LoadbalancerBackendGroupManager.Query().SubQuery() + subLblis := models.LoadbalancerListenerManager.Query().SubQuery() + q = q.Join(subLbb, sqlchemy.Equals(q.Field("backend_id"), subLbb.Field("id"))) + q = q.Join(subCachedLbbg, sqlchemy.Equals(q.Field("cached_backend_group_id"), subCachedLbbg.Field("id"))) + q = q.Join(subLbbg, sqlchemy.Equals(subCachedLbbg.Field("backend_group_id"), subLbbg.Field("id"))) + q = q.Join(subLblis, sqlchemy.Equals(subCachedLbbg.Field("associated_id"), subLblis.Field("id"))) + + q = q.Filter(sqlchemy.Equals(subCachedLbbg.Field("associated_type"), api.LB_ASSOCIATE_TYPE_LISTENER)) + q = q.Filter(sqlchemy.Equals(subLblis.Field("listener_type"), listenerType)) + q = q.Filter(sqlchemy.IsFalse(subLblis.Field("pending_deleted"))) + if len(fromBackendGroup) > 0 { + q = q.Filter(sqlchemy.NotEquals(subLbbg.Field("id"), fromBackendGroup)) + } + q = q.Filter(sqlchemy.Equals(subLbb.Field("backend_id"), backendServerId)) + q = q.Filter(sqlchemy.Equals(subLbb.Field("port"), port)) + q = q.Filter(sqlchemy.IsFalse(subLbb.Field("pending_deleted"))) + count, err := q.CountWithError() + if err != nil { + return httperrors.NewGeneralError(err) + } + + if count > 0 { + return httperrors.NewConflictError("server %s with port %d aready used by other %s listener", backendServerId, port, listenerType) } return nil @@ -901,123 +1060,155 @@ func (self *SQcloudRegionDriver) ValidateCreateLoadbalancerBackendData(ctx conte func (self *SQcloudRegionDriver) RequestSyncLoadbalancerBackend(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) - } - lb := lbbg.GetLoadbalancer() - if lb == nil { - return nil, fmt.Errorf("failed to find lb for backendgroup %s", lbbg.Name) - } - iRegion, err := lb.GetIRegion() + cachedlbbs, err := models.QcloudCachedLbManager.GetBackendsByLocalBackendId(lbb.GetId()) if err != nil { - return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerBackend.GetIRegion") - } - iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) - if err != nil { - return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerBackend.GetILoadBalancerById") - } - iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(lbbg.ExternalId) - if err != nil { - return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerBackend.GetILoadBalancerBackendGroupById") + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestSyncLoadbalancerBackend.GetBackendsByLocalBackendId") } - iBackend, err := iLoadbalancerBackendGroup.GetILoadbalancerBackendById(lbb.ExternalId) - if err != nil { - return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerBackend.GetILoadbalancerBackendById") + for _, cachedlbb := range cachedlbbs { + cachedlbbg, _ := cachedlbb.GetCachedBackendGroup() + if cachedlbbg == nil { + return nil, fmt.Errorf("failed to find lbbg for backend %s", cachedlbb.Name) + } + lb := cachedlbbg.GetLoadbalancer() + if lb == nil { + return nil, fmt.Errorf("failed to find lb for backendgroup %s", cachedlbbg.Name) + } + iRegion, err := lb.GetIRegion() + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestSyncLoadbalancerBackend.GetIRegion") + } + iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestSyncLoadbalancerBackend.GetILoadBalancerById") + } + iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(cachedlbbg.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestSyncLoadbalancerBackend.GetILoadBalancerBackendGroupById") + } + + iBackend, err := iLoadbalancerBackendGroup.GetILoadbalancerBackendById(cachedlbb.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestSyncLoadbalancerBackend.GetILoadbalancerBackendById") + } + + err = iBackend.SyncConf(lbb.Port, lbb.Weight) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestSyncLoadbalancerBackend.SyncConf") + } + + err = iBackend.Refresh() + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestSyncLoadbalancerBackend.Refresh") + } + + err = cachedlbb.SyncWithCloudLoadbalancerBackend(ctx, userCred, iBackend, nil) + if err != nil { + return nil, errors.Wrap(err, "qcloudRegionDriver.RequestSyncLoadbalancerBackend.SyncWithCloudLoadbalancerBackend") + } } - err = iBackend.SyncConf(lbb.Port, lbb.Weight) - if err != nil { - return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerBackend.SyncConf") - } - - iBackend, err = iLoadbalancerBackendGroup.GetILoadbalancerBackendById(iBackend.GetGlobalId()) - if err != nil { - return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerBackend.GetILoadbalancerBackendById") - } - - _, err = db.Update(lbb, func() error { - lbb.ExternalId = iBackend.GetGlobalId() - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerBackend.UpdateGlobalId") - } - - return nil, lbb.SyncWithCloudLoadbalancerBackend(ctx, userCred, iBackend, nil) + return nil, nil }) return nil } -func (self *SQcloudRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, lbbg *models.SLoadbalancerBackendGroup, task taskman.ITask) error { - taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { - iRegion, err := lbbg.GetIRegion() - if err != nil { - return nil, err - } - - lb := lbbg.GetLoadbalancer() - ilb, err := iRegion.GetILoadBalancerById(lb.GetExternalId()) - if err != nil { - return nil, err - } - - ilisten, err := ilb.GetILoadBalancerListenerById(lblis.GetExternalId()) - if err != nil { - return nil, err - } - - ilbbg, err := ilb.GetILoadBalancerBackendGroupById(ilisten.GetBackendGroupId()) - if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.GetILoadBalancerBackendGroupById") - } - - // todo: fix me ibackends is empty here +// 目前只支持应用型负载均衡 +func (self *SQcloudRegionDriver) syncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lb *models.SLoadbalancer, lbbg *models.SLoadbalancerBackendGroup, ilbbg cloudprovider.ICloudLoadbalancerBackendGroup) error { + if ilbbg != nil { ibackends, err := ilbbg.GetILoadbalancerBackends() if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.GetILoadbalancerBackends") + return errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.GetILoadbalancerBackends") } for _, ibackend := range ibackends { err = ilbbg.RemoveBackendServer(ibackend.GetBackendId(), ibackend.GetWeight(), ibackend.GetPort()) if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.RemoveBackendServer") + return errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.RemoveBackendServer") } } backends, err := lbbg.GetBackendsParams() if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.GetBackendsParams") + return errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.GetBackendsParams") } for _, backend := range backends { _, err = ilbbg.AddBackendServer(backend.ExternalID, backend.Weight, backend.Port) if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.AddBackendServer") + return errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.AddBackendServer") } } - _olbbg, err := db.FetchByExternalId(models.LoadbalancerBackendGroupManager, ilbbg.GetGlobalId()) + _olbbg, err := db.FetchByExternalId(models.QcloudCachedLbbgManager, ilbbg.GetGlobalId()) if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.FetchByExternalId") + return errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.FetchByExternalId") } - olbbg := _olbbg.(*models.SLoadbalancerBackendGroup) + olbbg := _olbbg.(*models.SQcloudCachedLbbg) _, err = db.Update(olbbg, func() error { olbbg.ExternalId = ilbbg.GetGlobalId() + olbbg.BackendGroupId = lbbg.GetId() return nil }) if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.UpdateListenerBackendGroup") + return errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.UpdateListenerBackendGroup") } - _, err = db.Update(lbbg, func() error { - lbbg.ExternalId = ilbbg.GetGlobalId() + err = self.syncCachedLbbs(ctx, userCred, lb, olbbg, ilbbg) + if err != nil { + return errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.syncCachedLbbs") + } + } + + return nil +} + +func (self *SQcloudRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + if utils.IsInStringArray(lblis.ListenerType, []string{api.LB_LISTENER_TYPE_HTTP, api.LB_LISTENER_TYPE_HTTPS}) { + return nil, nil + } + + lbbg := lblis.GetLoadbalancerBackendGroup() + if lbbg == nil { + err := fmt.Errorf("failed to find lbbg for lblis %s", lblis.Name) + return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerbackendGroup.GetLoadbalancerBackendGroup") + } + + iRegion, err := lbbg.GetIRegion() + if err != nil { + return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerbackendGroup.GetIRegion") + } + + lb := lbbg.GetLoadbalancer() + ilb, err := iRegion.GetILoadBalancerById(lb.GetExternalId()) + if err != nil { + return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerbackendGroup.GetILoadBalancerById") + } + + ilisten, err := ilb.GetILoadBalancerListenerById(lblis.GetExternalId()) + if err != nil { + return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerbackendGroup.GetILoadBalancerListenerById") + } + + ilbbg, err := ilb.GetILoadBalancerBackendGroupById(ilisten.GetBackendGroupId()) + if err != nil && errors.Cause(err) != cloudprovider.ErrNotFound { + return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.GetILoadBalancerBackendGroupById") + } + + // listener lbbg sync + err = self.syncLoadbalancerBackendGroup(ctx, userCred, lb, lbbg, ilbbg) + if err != nil { + return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.syncLoadbalancerBackendGroup") + } + + _, err = db.UpdateWithLock(ctx, lblis, func() error { + lblis.BackendGroupId = lbbg.GetId() return nil }) if err != nil { - return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.UpdateListenerBackendGroup") + return nil, errors.Wrap(err, "QcloudRegionDriver.RequestSyncLoadbalancerBackendGroup.UpdateListenBackendGroupId") } return nil, nil @@ -1026,6 +1217,26 @@ func (self *SQcloudRegionDriver) RequestSyncLoadbalancerBackendGroup(ctx context return nil } +func (self *SQcloudRegionDriver) RequestPullRegionLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, syncResults models.SSyncResultSet, provider *models.SCloudprovider, localRegion *models.SCloudregion, remoteRegion cloudprovider.ICloudRegion, syncRange *models.SSyncRange) error { + return nil +} + +func (self *SQcloudRegionDriver) RequestPullLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, syncResults models.SSyncResultSet, provider *models.SCloudprovider, localLoadbalancer *models.SLoadbalancer, remoteLoadbalancer cloudprovider.ICloudLoadbalancer, syncRange *models.SSyncRange) error { + meta := remoteLoadbalancer.GetMetadata() + if meta == nil { + return fmt.Errorf("") + + } + + // 经典型负载均衡只有一个后端服务器组,全局共享 + if forward, _ := meta.Int("Forward"); forward == 1 { + models.SyncQcloudLoadbalancerBackendgroups(ctx, userCred, syncResults, provider, localLoadbalancer, remoteLoadbalancer, syncRange) + return nil + } else { + return self.SManagedVirtualizationRegionDriver.RequestPullLoadbalancerBackendGroup(ctx, userCred, syncResults, provider, localLoadbalancer, remoteLoadbalancer, syncRange) + } +} + func (self *SQcloudRegionDriver) RequestPreSnapshotPolicyApply(ctx context.Context, userCred mcclient. TokenCredential, task taskman.ITask, disk *models.SDisk, sp *models.SSnapshotPolicy, data jsonutils.JSONObject) error { @@ -1051,3 +1262,77 @@ func (self *SQcloudRegionDriver) RequestPreSnapshotPolicyApply(ctx context.Conte }) return nil } + +func (self *SQcloudRegionDriver) RequestSyncLoadbalancerListener(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, task taskman.ITask) error { + taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) { + { + certId, _ := task.GetParams().GetString("certificate_id") + if len(certId) > 0 { + provider := models.CloudproviderManager.FetchCloudproviderById(lblis.ManagerId) + if provider == nil { + return nil, fmt.Errorf("failed to find provider for lblis %s", lblis.Name) + } + + cert, err := models.LoadbalancerCertificateManager.FetchById(certId) + if err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.FetchCert") + } + + lbcert, err := models.CachedLoadbalancerCertificateManager.GetOrCreateCachedCertificate(ctx, userCred, provider, lblis, cert.(*models.SLoadbalancerCertificate)) + if err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.GetCert") + } + + if len(lbcert.ExternalId) == 0 { + _, err = self.createLoadbalancerCertificate(ctx, userCred, lbcert) + if err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.CreateCert") + } + } + + _, err = db.Update(lblis, func() error { + lblis.CachedCertificateId = lbcert.GetId() + return nil + }) + if err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.UpdateCachedCertificateId") + } + } + } + + params, err := lblis.GetLoadbalancerListenerParams() + if err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.GetParams") + } + 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, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.GetIRegion") + } + iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.GetILoadbalancer") + } + iListener, err := iLoadbalancer.GetILoadBalancerListenerById(lblis.ExternalId) + if err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.GetIListener") + } + if err := iListener.Sync(params); err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.SyncListener") + } + if err := iListener.Refresh(); err != nil { + return nil, errors.Wrap(err, "regionDriver.RequestSyncLoadbalancerListener.RefreshListener") + } + + if utils.IsInStringArray(lblis.ListenerType, []string{api.LB_LISTENER_TYPE_UDP, api.LB_LISTENER_TYPE_TCP}) { + return nil, lblis.SyncWithCloudLoadbalancerListener(ctx, userCred, loadbalancer, iListener, nil) + } else { + // http&https listener 变更不会同步到监听规则 + return nil, nil + } + }) + return nil +} diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index e9d4b5b76b..d8dbc461d9 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -129,6 +129,8 @@ func InitHandlers(app *appsrv.Application) { models.HuaweiCachedLbbgManager, models.AwsCachedLbManager, models.AwsCachedLbbgManager, + models.QcloudCachedLbManager, + models.QcloudCachedLbbgManager, models.RouteTableManager, models.SchedpolicyManager, diff --git a/pkg/compute/tasks/loadbalancer_listener_sync_task.go b/pkg/compute/tasks/loadbalancer_listener_sync_task.go index 14682071e2..b97f6d60ce 100644 --- a/pkg/compute/tasks/loadbalancer_listener_sync_task.go +++ b/pkg/compute/tasks/loadbalancer_listener_sync_task.go @@ -52,16 +52,10 @@ func (self *LoadbalancerListenerSyncTask) OnInit(ctx context.Context, obj db.ISt return } - lbbg := lblis.GetLoadbalancerBackendGroup() - if lbbg == nil { - self.taskFail(ctx, lblis, fmt.Sprintf("failed to find lbbg for lblis %s", lblis.Name)) - return - } - self.SetStage("OnLoadbalancerBackendgroupSyncComplete", nil) driver := region.GetDriver() userCred := self.GetUserCred() - err := driver.RequestSyncLoadbalancerBackendGroup(ctx, userCred, lblis, lbbg, self) + err := driver.RequestSyncLoadbalancerBackendGroup(ctx, userCred, lblis, self) if err != nil { self.taskFail(ctx, lblis, err.Error()) } diff --git a/pkg/multicloud/qcloud/loadbalancer.go b/pkg/multicloud/qcloud/loadbalancer.go index 44b5bfc5f3..48906e7fae 100644 --- a/pkg/multicloud/qcloud/loadbalancer.go +++ b/pkg/multicloud/qcloud/loadbalancer.go @@ -132,7 +132,7 @@ func (self *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (clo func onecloudHealthCodeToQcloud(codes string) int { qcode := 0 for i, code := range HTTP_CODES { - if strings.Contains(code, codes) { + if strings.Contains(codes, code) { // 按位或然后再赋值qcode qcode |= 1 << uint(i) } @@ -311,6 +311,11 @@ func (self *SLoadbalancer) GetILoadBalancerListeners() ([]cloudprovider.ICloudLo } func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.ICloudLoadbalancerBackendGroup, error) { + if self.Forward == LB_TYPE_CLASSIC { + bg := SLBBackendGroup{lb: self} + return []cloudprovider.ICloudLoadbalancerBackendGroup{&bg}, nil + } + listeners, err := self.GetLoadbalancerListeners("") if err != nil { return nil, err diff --git a/pkg/multicloud/qcloud/loadbalancer_backendgroup.go b/pkg/multicloud/qcloud/loadbalancer_backendgroup.go index ae76b473d5..a7608ffe84 100644 --- a/pkg/multicloud/qcloud/loadbalancer_backendgroup.go +++ b/pkg/multicloud/qcloud/loadbalancer_backendgroup.go @@ -29,7 +29,7 @@ import ( type SLBBackendGroup struct { lb *SLoadbalancer // 必须不能为nil - listener *SLBListener // 必须不能为nil + listener *SLBListener // 可能为nil rule *SLBListenerRule // tcp、udp、tcp_ssl监听rule 为nil } @@ -57,6 +57,14 @@ func (self *SLBBackendGroup) GetStickySession() (*cloudprovider.SLoadbalancerSti return nil, nil } +func (self *SLBBackendGroup) GetListenerId() string { + if self.listener != nil { + return self.listener.GetId() + } + + return "" +} + // 返回requestid func (self *SLBBackendGroup) appLBBackendServer(action string, serverId string, weight int, port int) (string, error) { if len(serverId) == 0 { @@ -65,7 +73,7 @@ func (self *SLBBackendGroup) appLBBackendServer(action string, serverId string, params := map[string]string{ "LoadBalancerId": self.lb.GetId(), - "ListenerId": self.listener.GetId(), + "ListenerId": self.GetListenerId(), "Targets.0.InstanceId": serverId, "Targets.0.Port": strconv.Itoa(port), "Targets.0.Weight": strconv.Itoa(weight), @@ -91,7 +99,7 @@ func (self *SLBBackendGroup) appLBSeventhBackendServer(action string, serverId s params := map[string]string{ "loadBalancerId": self.lb.GetId(), - "listenerId": self.listener.GetId(), + "listenerId": self.GetListenerId(), "backends.0.InstanceId": serverId, "backends.0.Port": strconv.Itoa(port), "backends.0.Weight": strconv.Itoa(weight), @@ -117,7 +125,7 @@ func (self *SLBBackendGroup) updateBackendServerWeight(action string, serverId s params := map[string]string{ "LoadBalancerId": self.lb.GetId(), - "ListenerId": self.listener.GetId(), + "ListenerId": self.GetListenerId(), "Targets.0.InstanceId": serverId, "Targets.0.Port": strconv.Itoa(port), "Targets.0.Weight": strconv.Itoa(weight), @@ -143,7 +151,7 @@ func (self *SLBBackendGroup) updateBackendServerPort(action string, serverId str params := map[string]string{ "LoadBalancerId": self.lb.GetId(), - "ListenerId": self.listener.GetId(), + "ListenerId": self.GetListenerId(), "Targets.0.InstanceId": serverId, "Targets.0.Port": strconv.Itoa(oldPort), "NewPort": strconv.Itoa(newPort), @@ -221,8 +229,12 @@ func (self *SLBBackendGroup) AddBackendServer(serverId string, weight int, port // 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 { + _, err := self.lb.region.GetInstance(serverId) + if err == cloudprovider.ErrNotFound { + return nil + } + var requestId string - var err error if self.lb.Forward == LB_TYPE_APPLICATION { requestId, err = self.appLBBackendServer("DeregisterTargets", serverId, weight, port) } else { @@ -298,19 +310,23 @@ func (self *SLBBackendGroup) Sync(group *cloudprovider.SLoadbalancerBackendGroup func backendGroupIdGen(lbid string, secondId string) string { if len(secondId) > 0 { - return fmt.Sprintf("%s/%s", lbid, secondId) + return fmt.Sprintf("%s", secondId) } else { return lbid } } func (self *SLBBackendGroup) GetId() string { - t := self.listener.GetListenerType() + t := "" + if self.listener != nil { + t = self.listener.GetListenerType() + } + if t == api.LB_LISTENER_TYPE_HTTP || t == api.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()) + return backendGroupIdGen(self.lb.GetId(), self.GetListenerId()) } else { // 传统型lb 所有监听共用一个后端服务器组 return backendGroupIdGen(self.lb.GetId(), "") @@ -383,13 +399,13 @@ func (self *SLBBackendGroup) GetBackends() ([]SLBBackend, error) { 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()) + backends, err = self.lb.region.GetLBBackends(self.lb.Forward, self.lb.GetId(), self.GetListenerId(), 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(), "") + backends, err = self.lb.region.GetLBBackends(self.lb.Forward, self.lb.GetId(), self.GetListenerId(), "") if err != nil { return nil, err } diff --git a/pkg/multicloud/qcloud/loadbalancer_listener.go b/pkg/multicloud/qcloud/loadbalancer_listener.go index 151e556e1d..f086730a68 100644 --- a/pkg/multicloud/qcloud/loadbalancer_listener.go +++ b/pkg/multicloud/qcloud/loadbalancer_listener.go @@ -88,12 +88,14 @@ func (self *SLBListener) GetBackendServerPort() int { // https://cloud.tencent.com/document/product/214/30691 func (self *SLBListener) CreateILoadBalancerListenerRule(rule *cloudprovider.SLoadbalancerListenerRule) (cloudprovider.ICloudLoadbalancerListenerRule, error) { + hc := getListenerRuleHealthCheck(rule) requestId, err := self.lb.region.CreateLoadbalancerListenerRule(self.lb.GetId(), self.GetId(), rule.Domain, rule.Path, - &self.Scheduler, - &self.SessionExpireTime) + rule.Scheduler, + rule.StickySessionCookieTimeout, + hc) if err != nil { return nil, err } @@ -515,7 +517,7 @@ func (self *SRegion) GetLoadbalancerListeners(lbid string, t LB_TYPE, protocol s } // 返回requestID -func (self *SRegion) CreateLoadbalancerListenerRule(lbid string, listenerId string, domain string, url string, scheduler *string, sessionExpireTime *int) (string, error) { +func (self *SRegion) CreateLoadbalancerListenerRule(lbid string, listenerId string, domain string, url string, scheduler string, sessionExpireTime int, hc *healthCheck) (string, error) { if len(lbid) == 0 { return "", fmt.Errorf("loadbalancer id should not be empty") } @@ -527,12 +529,19 @@ func (self *SRegion) CreateLoadbalancerListenerRule(lbid string, listenerId stri "Rules.0.Url": url, } - if scheduler != nil && len(*scheduler) > 0 { - params["Rules.0.Scheduler"] = *scheduler - } + params["Rules.0.Scheduler"] = scheduler + params["Rules.0.SessionExpireTime"] = strconv.Itoa(sessionExpireTime) - if sessionExpireTime != nil { - params["Rules.0.SessionExpireTime"] = strconv.Itoa(*sessionExpireTime) + // health check + params["Rules.0.HealthCheck.HealthSwitch"] = strconv.Itoa(hc.HealthSwitch) + params["Rules.0.HealthCheck.IntervalTime"] = strconv.Itoa(hc.IntervalTime) + params["Rules.0.HealthCheck.HealthNum"] = strconv.Itoa(hc.HealthNum) + params["Rules.0.HealthCheck.UnHealthNum"] = strconv.Itoa(hc.UnHealthNum) + if hc.HTTPCode > 0 { + params["Rules.0.HealthCheck.HttpCode"] = strconv.Itoa(hc.HTTPCode) + params["Rules.0.HealthCheck.HttpCheckPath"] = hc.HTTPCheckPath + params["Rules.0.HealthCheck.HttpCheckDomain"] = hc.HTTPCheckDomain + params["Rules.0.HealthCheck.HttpCheckMethod"] = hc.HTTPCheckMethod } resp, err := self.clbRequest("CreateRule", params) @@ -706,6 +715,37 @@ func getHealthCheck(listener *cloudprovider.SLoadbalancerListener) *healthCheck return hc } +func getListenerRuleHealthCheck(rule *cloudprovider.SLoadbalancerListenerRule) *healthCheck { + var hc *healthCheck + if rule.HealthCheck == api.LB_BOOL_ON { + hc = &healthCheck{ + HealthSwitch: 1, + UnHealthNum: rule.HealthCheckFail, + IntervalTime: rule.HealthCheckInterval, + HealthNum: rule.HealthCheckRise, + TimeOut: rule.HealthCheckTimeout, + } + + httpCode := onecloudHealthCodeToQcloud(rule.HealthCheckHttpCode) + if httpCode > 0 { + hc.HTTPCode = httpCode + hc.HTTPCheckMethod = "HEAD" // todo: add column HttpCheckMethod in model + hc.HTTPCheckDomain = rule.HealthCheckDomain + hc.HTTPCheckPath = rule.HealthCheckURI + } + } else { + hc = &healthCheck{ + HealthSwitch: 0, + UnHealthNum: 3, + IntervalTime: 5, + HealthNum: 3, + TimeOut: 2, + } + } + + return hc +} + func getCertificate(listener *cloudprovider.SLoadbalancerListener) *certificate { var cert *certificate if len(listener.CertificateID) > 0 {