mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-21 09:58:10 +08:00
400 lines
14 KiB
Go
400 lines
14 KiB
Go
// 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/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/httperrors"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
)
|
|
|
|
// +onecloud:swagger-gen-ignore
|
|
type SOpenstackCachedLbbgManager struct {
|
|
SLoadbalancerLogSkipper
|
|
db.SVirtualResourceBaseManager
|
|
}
|
|
|
|
var OpenstackCachedLbbgManager *SOpenstackCachedLbbgManager
|
|
|
|
func init() {
|
|
OpenstackCachedLbbgManager = &SOpenstackCachedLbbgManager{
|
|
SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
|
|
SOpenstackCachedLbbg{},
|
|
"openstackcachedlbbgs_tbl",
|
|
"openstackcachedlbbg",
|
|
"openstackcachedlbbgs",
|
|
),
|
|
}
|
|
OpenstackCachedLbbgManager.SetVirtualObject(OpenstackCachedLbbgManager)
|
|
}
|
|
|
|
type SOpenstackCachedLbbg 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
|
|
ProtocolType string `width:"16" charset:"ascii" nullable:"false" list:"user" create:"required"` // 监听协议类型
|
|
}
|
|
|
|
func (lbbg *SOpenstackCachedLbbg) 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), nil
|
|
}
|
|
|
|
func (lbbg *SOpenstackCachedLbbg) GetLoadbalancer() (*SLoadbalancer, error) {
|
|
lb, err := LoadbalancerManager.FetchById(lbbg.LoadbalancerId)
|
|
if err != nil {
|
|
log.Errorf("failed to find loadbalancer for backendgroup %s", lbbg.Name)
|
|
return nil, errors.Wrap(err, "LoadbalancerManager.FetchById(lbbg.LoadbalancerId)")
|
|
}
|
|
return lb.(*SLoadbalancer), nil
|
|
}
|
|
|
|
func (lbbg *SOpenstackCachedLbbg) GetCachedBackends() ([]SOpenstackCachedLb, error) {
|
|
ret := []SOpenstackCachedLb{}
|
|
err := OpenstackCachedLbManager.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, errors.Wrap(err, `OpenstackCachedLbManager.Query().Equals("cached_backend_group_id", lbbg.GetId()).IsFalse("pending_deleted").All(&ret)`)
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (lbbg *SOpenstackCachedLbbg) GetICloudLoadbalancerBackendGroup() (cloudprovider.ICloudLoadbalancerBackendGroup, error) {
|
|
if len(lbbg.ExternalId) == 0 {
|
|
return nil, fmt.Errorf("backendgroup %s has no external id", lbbg.GetId())
|
|
}
|
|
|
|
lb, err := lbbg.GetLoadbalancer()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "lbbg.GetLoadbalancer()")
|
|
}
|
|
|
|
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 (man *SOpenstackCachedLbbgManager) GetUsableCachedBackendGroups(backendGroupId string, protocolType string) ([]SOpenstackCachedLbbg, error) {
|
|
ret := []SOpenstackCachedLbbg{}
|
|
err := man.Query().IsFalse("pending_deleted").Equals("backend_group_id", backendGroupId).Equals("protocol_type", protocolType).IsNullOrEmpty("associated_id").IsNotEmpty("external_id").All(&ret)
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (man *SOpenstackCachedLbbgManager) GetUsableCachedBackendGroup(backendGroupId string, protocolType string) (*SOpenstackCachedLbbg, error) {
|
|
ret, err := man.GetUsableCachedBackendGroups(backendGroupId, protocolType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(ret) > 0 {
|
|
return &ret[0], nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (man *SOpenstackCachedLbbgManager) GetCachedBackendGroupByAssociateId(associateId string) (*SOpenstackCachedLbbg, error) {
|
|
ret := &SOpenstackCachedLbbg{}
|
|
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 *SOpenstackCachedLbbgManager) GetCachedBackendGroups(backendGroupId string) ([]SOpenstackCachedLbbg, error) {
|
|
ret := []SOpenstackCachedLbbg{}
|
|
err := man.Query().IsFalse("pending_deleted").Equals("backend_group_id", backendGroupId).All(&ret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (man *SOpenstackCachedLbbgManager) GetListenerRuleCachedBackendGroups(listenerRuleId string) ([]SOpenstackCachedLbbg, error) {
|
|
ret := []SOpenstackCachedLbbg{}
|
|
err := man.Query().IsFalse("pending_deleted").Equals("associated_type", api.LB_ASSOCIATE_TYPE_RULE).Equals("associated_id", listenerRuleId).All(&ret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := 0; i < len(ret); i++ {
|
|
ret[i].SetModelManager(man, &ret[i])
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (man *SOpenstackCachedLbbgManager) GetListenerCachedBackendGroups(listenerId string) ([]SOpenstackCachedLbbg, error) {
|
|
ret := []SOpenstackCachedLbbg{}
|
|
err := man.Query().IsFalse("pending_deleted").Equals("associated_type", api.LB_ASSOCIATE_TYPE_LISTENER).Equals("associated_id", listenerId).All(&ret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := 0; i < len(ret); i++ {
|
|
ret[i].SetModelManager(man, &ret[i])
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (man *SOpenstackCachedLbbgManager) getLoadbalancerBackendgroupsByLoadbalancer(lb *SLoadbalancer) ([]SOpenstackCachedLbbg, error) {
|
|
lbbgs := []SOpenstackCachedLbbg{}
|
|
q := man.Query().IsFalse("pending_deleted").Equals("loadbalancer_id", lb.Id)
|
|
if err := db.FetchModelObjects(man, q, &lbbgs); err != nil {
|
|
return nil, errors.Wrap(err, "db.FetchModelObjects(man, q, &lbbgs)")
|
|
}
|
|
return lbbgs, nil
|
|
}
|
|
|
|
func (man *SOpenstackCachedLbbgManager) SyncLoadbalancerBackendgroups(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, lb *SLoadbalancer, lbbgs []cloudprovider.ICloudLoadbalancerBackendGroup, syncRange *SSyncRange) ([]SOpenstackCachedLbbg, []cloudprovider.ICloudLoadbalancerBackendGroup, compare.SyncResult) {
|
|
syncOwnerId := provider.GetOwnerId()
|
|
|
|
lockman.LockRawObject(ctx, "backendgroups", fmt.Sprintf("%s-%s", provider.Id, lb.Id))
|
|
defer lockman.ReleaseRawObject(ctx, "backendgroups", fmt.Sprintf("%s-%s", provider.Id, lb.Id))
|
|
|
|
localLbgs := []SOpenstackCachedLbbg{}
|
|
remoteLbbgs := []cloudprovider.ICloudLoadbalancerBackendGroup{}
|
|
syncResult := compare.SyncResult{}
|
|
|
|
dbLbbgs, err := man.getLoadbalancerBackendgroupsByLoadbalancer(lb)
|
|
if err != nil {
|
|
syncResult.Error(err)
|
|
return nil, nil, syncResult
|
|
}
|
|
|
|
removed := []SOpenstackCachedLbbg{}
|
|
commondb := []SOpenstackCachedLbbg{}
|
|
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(), provider)
|
|
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, provider)
|
|
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 (lbbg *SOpenstackCachedLbbg) 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
|
|
lbbg.SetStatus(userCred, api.LB_STATUS_UNKNOWN, "sync to delete")
|
|
return errors.Wrap(err, "lbbg.ValidateDeleteCondition(ctx)")
|
|
} else {
|
|
lbbg.SetModelManager(OpenstackCachedLbbgManager, lbbg)
|
|
err := db.DeleteModel(ctx, userCred, lbbg)
|
|
if err != nil {
|
|
return errors.Wrap(err, "db.DeleteModel(ctx, userCred, lbbg)")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (lbbg *SOpenstackCachedLbbg) 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 *SOpenstackCachedLbbg) SyncWithCloudLoadbalancerBackendgroup(ctx context.Context, userCred mcclient.TokenCredential, lb *SLoadbalancer, extLoadbalancerBackendgroup cloudprovider.ICloudLoadbalancerBackendGroup, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider) error {
|
|
lbbg.SetModelManager(OpenstackCachedLbbgManager, lbbg)
|
|
|
|
ibackends, err := extLoadbalancerBackendgroup.GetILoadbalancerBackends()
|
|
if err != nil {
|
|
return errors.Wrap(err, "OpenstackCachedLbbg.SyncWithCloudLoadbalancerBackendgroup.GetILoadbalancerBackends")
|
|
}
|
|
|
|
localLbbg, err := lbbg.GetLocalBackendGroup(ctx, userCred)
|
|
if err != nil {
|
|
return errors.Wrap(err, "OpenstackCachedLbbg.SyncWithCloudLoadbalancerBackendgroup.GetLocalBackendGroup")
|
|
}
|
|
|
|
backends, err := localLbbg.GetBackends()
|
|
if err != nil {
|
|
return errors.Wrap(err, "OpenstackCachedLbbg.SyncWithCloudLoadbalancerBackendgroup.GetBackends")
|
|
}
|
|
|
|
var newLocalLbbg *SLoadbalancerBackendGroup
|
|
if !lbbg.isBackendsMatch(backends, ibackends) {
|
|
newLocalLbbg, err = newLocalBackendgroupFromCloudLoadbalancerBackendgroup(ctx, userCred, lb, extLoadbalancerBackendgroup, syncOwnerId, provider)
|
|
if err != nil {
|
|
return errors.Wrap(err, "OpenstackCachedLbbg.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, provider.Id)
|
|
return err
|
|
}
|
|
|
|
func (man *SOpenstackCachedLbbgManager) newFromCloudLoadbalancerBackendgroup(ctx context.Context, userCred mcclient.TokenCredential, lb *SLoadbalancer, extLoadbalancerBackendgroup cloudprovider.ICloudLoadbalancerBackendGroup, syncOwnerId mcclient.IIdentityProvider, provider *SCloudprovider) (*SOpenstackCachedLbbg, error) {
|
|
LocalLbbg, err := newLocalBackendgroupFromCloudLoadbalancerBackendgroup(ctx, userCred, lb, extLoadbalancerBackendgroup, syncOwnerId, provider)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lbbg := &SOpenstackCachedLbbg{}
|
|
lbbg.SetModelManager(man, lbbg)
|
|
|
|
region := lb.GetRegion()
|
|
if region == nil {
|
|
return nil, errors.Wrap(httperrors.ErrInvalidStatus, "loadbalancer is not attached to any region")
|
|
}
|
|
|
|
lbbg.ManagerId = provider.Id
|
|
lbbg.CloudregionId = region.Id
|
|
lbbg.LoadbalancerId = lb.Id
|
|
lbbg.BackendGroupId = LocalLbbg.GetId()
|
|
lbbg.ExternalId = extLoadbalancerBackendgroup.GetGlobalId()
|
|
lbbg.ProtocolType = extLoadbalancerBackendgroup.GetProtocolType()
|
|
|
|
lbbg.Status = extLoadbalancerBackendgroup.GetStatus()
|
|
|
|
err = func() error {
|
|
lockman.LockRawObject(ctx, man.Keyword(), "name")
|
|
defer lockman.ReleaseRawObject(ctx, man.Keyword(), "name")
|
|
|
|
lbbg.Name, err = db.GenerateName(ctx, man, syncOwnerId, LocalLbbg.GetName())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return man.TableSpec().Insert(ctx, lbbg)
|
|
}()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
SyncCloudProject(userCred, lbbg, syncOwnerId, extLoadbalancerBackendgroup, provider.Id)
|
|
|
|
db.OpsLog.LogEvent(lbbg, db.ACT_CREATE, lbbg.GetShortDesc(ctx), userCred)
|
|
return lbbg, nil
|
|
}
|