Files
cloudpods/pkg/compute/models/loadbalanceropenstackcachedlbbg.go
2021-04-23 18:55:02 +08:00

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
}