Files
cloudpods/pkg/compute/regiondrivers/openstack.go
T

2095 lines
76 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 regiondrivers
import (
"context"
"database/sql"
"fmt"
"regexp"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/secrules"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/validators"
"yunion.io/x/onecloud/pkg/cloudprovider"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/rand"
"yunion.io/x/onecloud/pkg/util/rbacutils"
)
type SOpenStackRegionDriver struct {
SManagedVirtualizationRegionDriver
}
func init() {
driver := SOpenStackRegionDriver{}
models.RegisterRegionDriver(&driver)
}
func (self *SOpenStackRegionDriver) GetDefaultSecurityGroupInRule() cloudprovider.SecurityRule {
return cloudprovider.SecurityRule{SecurityRule: *secrules.MustParseSecurityRule("in:deny any")}
}
func (self *SOpenStackRegionDriver) GetDefaultSecurityGroupOutRule() cloudprovider.SecurityRule {
return cloudprovider.SecurityRule{SecurityRule: *secrules.MustParseSecurityRule("out:deny any")}
}
func (self *SOpenStackRegionDriver) GetSecurityGroupRuleMaxPriority() int {
return 0
}
func (self *SOpenStackRegionDriver) GetSecurityGroupRuleMinPriority() int {
return 0
}
func (self *SOpenStackRegionDriver) IsOnlySupportAllowRules() bool {
return true
}
func (self *SOpenStackRegionDriver) GetSecurityGroupPublicScope(service string) rbacutils.TRbacScope {
return rbacutils.ScopeProject
}
func (self *SOpenStackRegionDriver) GetProvider() string {
return api.CLOUD_PROVIDER_OPENSTACK
}
func (self *SOpenStackRegionDriver) IsVpcCreateNeedInputCidr() bool {
return false
}
func (self *SOpenStackRegionDriver) ValidateCreateLoadbalancerData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
//zoneV := validators.NewModelIdOrNameValidator("zone", "zone", ownerId)
managerIdV := validators.NewModelIdOrNameValidator("manager", "cloudprovider", ownerId)
addressTypeV := validators.NewStringChoicesValidator("address_type", api.LB_ADDR_TYPES)
networkV := validators.NewModelIdOrNameValidator("network", "network", ownerId)
keyV := map[string]validators.IValidator{
"status": validators.NewStringChoicesValidator("status", api.LB_STATUS_SPEC).Default(api.LB_STATUS_ENABLED),
"address_type": addressTypeV.Default(api.LB_ADDR_TYPE_INTRANET),
"network": networkV,
//"zone": zoneV,
"manager": managerIdV,
}
if err := RunValidators(keyV, data, false); err != nil {
return nil, err
}
// 检查网络可用
network := networkV.Model.(*models.SNetwork)
_, _, vpc, _, err := network.ValidateElbNetwork(nil)
if err != nil {
return nil, err
}
if managerIdV.Model.GetId() != vpc.ManagerId {
return nil, httperrors.NewInputParameterError("Loadbalancer's manager (%s(%s)) does not match vpc's(%s(%s)) (%s)", managerIdV.Model.GetName(), managerIdV.Model.GetId(), vpc.GetName(), vpc.GetId(), vpc.ManagerId)
}
// 公网ELB需要指定EIP
if addressTypeV.Value == api.LB_ADDR_TYPE_INTERNET {
eipV := validators.NewModelIdOrNameValidator("eip", "eip", nil)
if err := eipV.Validate(data); err != nil {
return nil, err
}
eip := eipV.Model.(*models.SElasticip)
if eip.Status != api.EIP_STATUS_READY {
return nil, fmt.Errorf("eip status not ready")
}
if len(eip.ExternalId) == 0 {
return nil, fmt.Errorf("eip external id is empty")
}
data.Set("eip_id", jsonutils.NewString(eip.ExternalId))
}
// region := zoneV.Model.(*models.SZone).GetRegion()
region := networkV.Model.(*models.SNetwork).GetRegion()
if region == nil {
return nil, fmt.Errorf("getting region failed")
}
// data.Set("network_type", jsonutils.NewString(api.LB_NETWORK_TYPE_VPC))
data.Set("cloudregion_id", jsonutils.NewString(region.GetId()))
data.Set("vpc_id", jsonutils.NewString(vpc.GetId()))
return self.SManagedVirtualizationRegionDriver.ValidateCreateLoadbalancerData(ctx, userCred, ownerId, data)
}
func (self *SOpenStackRegionDriver) RequestCreateLoadbalancer(ctx context.Context, userCred mcclient.TokenCredential, lb *models.SLoadbalancer, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
iRegion, err := lb.GetIRegion()
if err != nil {
return nil, err
}
params, err := lb.GetCreateLoadbalancerParams(iRegion)
if err != nil {
return nil, err
}
Scloudprovider := lb.GetCloudprovider()
params.ProjectId, err = Scloudprovider.SyncProject(ctx, userCred, lb.ProjectId)
if err != nil {
log.Errorf("failed to sync project %s for create %s lb %s error: %v", lb.ProjectId, Scloudprovider.Provider, lb.Name, err)
}
iLoadbalancer, err := iRegion.CreateILoadBalancer(params)
if err != nil {
return nil, err
}
if err := db.SetExternalId(lb, userCred, iLoadbalancer.GetGlobalId()); err != nil {
return nil, err
}
//wait async create result
err = cloudprovider.WaitMultiStatus(iLoadbalancer, []string{api.LB_STATUS_ENABLED, api.LB_STATUS_UNKNOWN}, 10*time.Second, 8*time.Minute)
if err != nil {
return nil, errors.Wrap(err, "cloudprovider.WaitMultiStatus")
}
if iLoadbalancer.GetStatus() == api.LB_STATUS_UNKNOWN {
return nil, errors.Wrap(fmt.Errorf("status error"), "check status")
}
if err := lb.SyncWithCloudLoadbalancer(ctx, userCred, iLoadbalancer, nil, lb.GetCloudprovider(), lb.GetRegion()); err != nil {
return nil, err
}
// 公网lb,需要同步public ip
if lb.AddressType == api.LB_ADDR_TYPE_INTERNET {
publicIp, err := iLoadbalancer.GetIEIP()
if err != nil {
return nil, errors.Wrap(err, "iLoadbalancer.GetIEIP()")
}
lb.SyncLoadbalancerEip(ctx, userCred, lb.GetCloudprovider(), publicIp)
}
lbbgs, err := iLoadbalancer.GetILoadBalancerBackendGroups()
if err != nil {
return nil, err
}
if len(lbbgs) > 0 {
provider := lb.GetCloudprovider()
if provider == nil {
return nil, fmt.Errorf("failed to find cloudprovider for lb %s", lb.Name)
}
models.LoadbalancerBackendGroupManager.SyncLoadbalancerBackendgroups(ctx, userCred, provider, lb, lbbgs, &models.SSyncRange{})
}
return nil, nil
})
return nil
}
func (self *SOpenStackRegionDriver) ValidateDeleteLoadbalancerCondition(ctx context.Context, lb *models.SLoadbalancer) error {
listeners, err := lb.GetLoadbalancerListeners()
if err != nil {
return err
}
if len(listeners) > 0 {
return httperrors.NewConflictError("loadbalancer is using by %d listener.", len(listeners))
}
lbbgs, err := lb.GetLoadbalancerBackendgroups()
if err != nil {
return err
}
if len(lbbgs) > 0 {
return httperrors.NewConflictError("loadbalancer is using by %d backendgroup.", len(lbbgs))
}
return nil
}
func (self *SOpenStackRegionDriver) ValidateCreateLoadbalancerAclData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
// 访问控制: 与listener是1v1的
// 关系,创建时即需要与具体的listener绑定,不能再变更listner。
// required: listener_id, acl_type: "white", acl_status: "on", manager,cloudregion,acl_entries
data, err := self.SManagedVirtualizationRegionDriver.ValidateCreateLoadbalancerAclData(ctx, userCred, data)
if err != nil {
return data, err
}
listenerV := validators.NewModelIdOrNameValidator("listener", "loadbalancerlistener", nil)
err = listenerV.Validate(data)
if err != nil {
return data, err
}
return data, nil
}
func (self *SOpenStackRegionDriver) RequestCreateLoadbalancerAcl(ctx context.Context, userCred mcclient.TokenCredential, lbacl *models.SCachedLoadbalancerAcl, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
return self.createLoadbalancerAcl(ctx, userCred, lbacl)
})
return nil
}
func (self *SOpenStackRegionDriver) ValidateCreateLoadbalancerCertificateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
return nil, httperrors.NewNotImplementedError("%s does not currently support creating loadbalancer certificate", self.GetProvider())
}
func (self *SOpenStackRegionDriver) ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, input *api.SElasticipCreateInput) error {
if len(input.NetworkId) == 0 {
return httperrors.NewMissingParameterError("network_id")
}
_network, err := models.NetworkManager.FetchByIdOrName(userCred, input.NetworkId)
if err != nil {
if err == sql.ErrNoRows {
return httperrors.NewResourceNotFoundError2("network", input.NetworkId)
}
return httperrors.NewGeneralError(err)
}
network := _network.(*models.SNetwork)
input.NetworkId = network.Id
vpc := network.GetVpc()
if vpc == nil {
return httperrors.NewInputParameterError("failed to found vpc for network %s(%s)", network.Name, network.Id)
}
input.ManagerId = vpc.ManagerId
region, err := vpc.GetRegion()
if err != nil {
return err
}
if region.Id != input.CloudregionId {
return httperrors.NewUnsupportOperationError("network %s(%s) does not belong to %s", network.Name, network.Id, self.GetProvider())
}
return nil
}
func (self *SOpenStackRegionDriver) RequestPullLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, syncResults models.SSyncResultSet, provider *models.SCloudprovider, localLoadbalancer *models.SLoadbalancer, remoteLoadbalancer cloudprovider.ICloudLoadbalancer, syncRange *models.SSyncRange) error {
models.SyncOpenstackLoadbalancerBackendgroups(ctx, userCred, syncResults, provider, localLoadbalancer, remoteLoadbalancer, syncRange)
return nil
}
func (self *SOpenStackRegionDriver) ValidateCreateLoadbalancerListenerData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict, lb *models.SLoadbalancer, backendGroup db.IModel) (*jsonutils.JSONDict, error) {
listenerTypeV := validators.NewStringChoicesValidator("listener_type", api.LB_LISTENER_TYPES)
listenerPortV := validators.NewPortValidator("listener_port")
aclStatusV := validators.NewStringChoicesValidator("acl_status", api.LB_BOOL_VALUES)
aclTypeV := validators.NewStringChoicesValidator("acl_type", api.LB_ACL_TYPES)
aclV := validators.NewModelIdOrNameValidator("acl", "loadbalanceracl", ownerId)
keyV := map[string]validators.IValidator{
"status": validators.NewStringChoicesValidator("status", api.LB_STATUS_SPEC).Default(api.LB_STATUS_ENABLED),
"listener_type": listenerTypeV,
"listener_port": listenerPortV,
"send_proxy": validators.NewStringChoicesValidator("send_proxy", api.LB_SENDPROXY_CHOICES).Default(api.LB_SENDPROXY_OFF),
"acl_status": aclStatusV.Default(api.LB_BOOL_OFF),
"acl_type": aclTypeV.Optional(true),
"acl": aclV.Optional(true),
"scheduler": validators.NewStringChoicesValidator("scheduler", api.LB_SCHEDULER_TYPES),
"sticky_session": validators.NewStringChoicesValidator("sticky_session", api.LB_BOOL_VALUES).Default(api.LB_BOOL_OFF),
"sticky_session_type": validators.NewStringChoicesValidator("sticky_session_type", api.LB_STICKY_SESSION_TYPES).Default(api.LB_STICKY_SESSION_TYPE_INSERT),
"sticky_session_cookie": validators.NewRegexpValidator("sticky_session_cookie", regexp.MustCompile(`\w+`)).Optional(true),
"sticky_session_cookie_timeout": validators.NewNonNegativeValidator("sticky_session_cookie_timeout").Optional(true),
"x_forwarded_for": validators.NewBoolValidator("x_forwarded_for").Default(true),
// "gzip": validators.NewBoolValidator("gzip").Default(false),
}
if err := RunValidators(keyV, data, false); err != nil {
return nil, err
}
// listener uniqueness
listenerType := listenerTypeV.Value
err := models.LoadbalancerListenerManager.CheckListenerUniqueness(ctx, lb, listenerType, listenerPortV.Value)
if err != nil {
return nil, err
}
// backendgroup check
if lbbg, ok := backendGroup.(*models.SLoadbalancerBackendGroup); ok && lbbg.LoadbalancerId != lb.Id {
return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s",
lbbg.Name, lbbg.Id, lbbg.LoadbalancerId, lb.Id)
}
// https additional certificate check
if listenerType == api.LB_LISTENER_TYPE_TERMINATED_HTTPS {
certV := validators.NewModelIdOrNameValidator("certificate", "loadbalancercertificate", ownerId)
tlsCipherPolicyV := validators.NewStringChoicesValidator("tls_cipher_policy", api.LB_TLS_CIPHER_POLICIES).Default(api.LB_TLS_CIPHER_POLICY_1_2)
httpsV := map[string]validators.IValidator{
"certificate": certV,
"tls_cipher_policy": tlsCipherPolicyV,
//"enable_http2": validators.NewBoolValidator("enable_http2").Default(true),
}
if err := RunValidators(httpsV, data, false); err != nil {
return nil, err
}
}
// health check default depends on input parameters
checkTypeV := models.LoadbalancerListenerManager.CheckTypeV(listenerType)
keyVHealth := map[string]validators.IValidator{
"health_check": validators.NewStringChoicesValidator("health_check", api.LB_BOOL_VALUES).Default(api.LB_BOOL_ON),
"health_check_type": checkTypeV,
"health_check_domain": validators.NewDomainNameValidator("health_check_domain").AllowEmpty(true).Default(""),
"health_check_path": validators.NewURLPathValidator("health_check_path").Default(""),
"health_check_uri": validators.NewURLPathValidator("health_check_uri").Default(""),
"health_check_http_code": validators.NewStringMultiChoicesValidator("health_check_http_code", api.LB_HEALTH_CHECK_HTTP_CODES).Sep(",").Default(api.LB_HEALTH_CHECK_HTTP_CODE_DEFAULT),
"health_check_rise": validators.NewRangeValidator("health_check_rise", 1, 10).Default(3),
"health_check_timeout": validators.NewRangeValidator("health_check_timeout", 1, 50).Default(5),
"health_check_interval": validators.NewRangeValidator("health_check_interval", 1, 50).Default(10),
}
if err := RunValidators(keyVHealth, data, false); err != nil {
return nil, err
}
if t, _ := data.Int("health_check_rise"); t > 0 {
data.Set("health_check_fall", jsonutils.NewInt(t))
}
interval, _ := data.Int("health_check_interval")
timeout, _ := data.Int("health_check_timeout")
if timeout >= interval {
data.Set("health_check_interval", jsonutils.NewInt(interval+timeout))
}
// acl check
if err := models.LoadbalancerListenerManager.ValidateAcl(aclStatusV, aclTypeV, aclV, data, api.CLOUD_PROVIDER_OPENSTACK); err != nil {
return nil, err
}
return self.SManagedVirtualizationRegionDriver.ValidateCreateLoadbalancerListenerData(ctx, userCred, ownerId, data, lb, backendGroup)
}
func (self *SOpenStackRegionDriver) RequestCreateLoadbalancerListener(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 := lblis.GetCloudprovider()
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.Wrapf(err, "LoadbalancerCertificateManager.FetchById(%s)", certId)
}
lbcert, err := models.CachedLoadbalancerCertificateManager.GetOrCreateCachedCertificate(ctx, userCred, provider, lblis, cert.(*models.SLoadbalancerCertificate))
if err != nil {
return nil, errors.Wrap(err, "CachedLoadbalancerCertificateManager.GetOrCreateCachedCertificate")
}
if len(lbcert.ExternalId) == 0 {
_, err = self.createLoadbalancerCertificate(ctx, userCred, lbcert)
if err != nil {
return nil, errors.Wrap(err, "createLoadbalancerCertificate")
}
}
_, err = db.Update(lblis, func() error {
lblis.CachedCertificateId = lbcert.GetId()
return nil
})
if err != nil {
return nil, errors.Wrap(err, "regionDriver.RequestCreateLoadbalancerListener.UpdateCachedCertificateId")
}
}
}
params, err := lblis.GetLoadbalancerListenerParams()
if err != nil {
return nil, errors.Wrapf(err, "lblis.GetLoadbalancerListenerParams")
}
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, "loadbalancer.GetIRegion")
}
iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId)
if err != nil {
return nil, errors.Wrapf(err, "iRegion.GetILoadBalancerById(%s)", loadbalancer.ExternalId)
}
iListener, err := iLoadbalancer.CreateILoadBalancerListener(ctx, params)
if err != nil {
return nil, errors.Wrap(err, "iLoadbalancer.CreateILoadBalancerListener")
}
if err := db.SetExternalId(lblis, userCred, iListener.GetGlobalId()); err != nil {
return nil, errors.Wrap(err, "db.SetExternalId")
}
// wait async result
err = cloudprovider.WaitMultiStatus(iListener, []string{api.LB_STATUS_ENABLED, api.LB_STATUS_UNKNOWN}, 10*time.Second, 8*time.Minute)
if err != nil {
return nil, errors.Wrap(err, "cloudprovider.WaitMultiStatus")
}
if iListener.GetStatus() == api.LB_STATUS_UNKNOWN {
return nil, errors.Wrap(fmt.Errorf("status error"), "check status")
}
{
aclId, _ := task.GetParams().GetString("acl_id")
if len(aclId) > 0 {
provider := lblis.GetCloudprovider()
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, errors.Wrap(err, "LoadbalancerAclManager.FetchById")
}
lbacl, err := models.CachedLoadbalancerAclManager.GetOrCreateCachedAcl(ctx, userCred, provider, lblis, acl.(*models.SLoadbalancerAcl))
if err != nil {
return nil, errors.Wrap(err, "CachedLoadbalancerAclManager.GetOrCreateCachedAcl")
}
if len(lbacl.ExternalId) == 0 {
_, err = self.createLoadbalancerAcl(ctx, userCred, lbacl)
if err != nil {
return nil, errors.Wrap(err, "createLoadbalancerAcl")
}
}
_, err = db.Update(lblis, func() error {
lblis.CachedAclId = lbacl.GetId()
return nil
})
if err != nil {
return nil, errors.Wrap(err, "regionDriver.RequestCreateLoadbalancerListener.UpdateCachedAclId")
}
}
}
return nil, lblis.SyncWithCloudLoadbalancerListener(ctx, userCred, loadbalancer, iListener, loadbalancer.GetOwnerId(), lblis.GetCloudprovider())
})
return nil
}
func (self *SOpenStackRegionDriver) createLoadbalancerAcl(ctx context.Context, userCred mcclient.TokenCredential, lbacl *models.SCachedLoadbalancerAcl) (jsonutils.JSONObject, error) {
iRegion, err := lbacl.GetIRegion()
if err != nil {
return nil, err
}
listener, err := lbacl.GetListener()
if err != nil {
return nil, err
}
acl := &cloudprovider.SLoadbalancerAccessControlList{
ListenerId: listener.GetExternalId(),
Name: lbacl.Name,
Entrys: []cloudprovider.SLoadbalancerAccessControlListEntry{},
AccessControlEnable: listener.AclStatus == api.LB_BOOL_ON,
}
_originAcl, err := db.FetchById(models.LoadbalancerAclManager, lbacl.AclId)
if err != nil {
return nil, errors.Wrap(err, "regionDriver.FetchAcl")
}
originAcl := _originAcl.(*models.SLoadbalancerAcl)
if originAcl.AclEntries != nil {
for _, entry := range *originAcl.AclEntries {
acl.Entrys = append(acl.Entrys, cloudprovider.SLoadbalancerAccessControlListEntry{CIDR: entry.Cidr, Comment: entry.Comment})
}
}
iLoadbalancerAcl, err := iRegion.CreateILoadBalancerAcl(acl)
if err != nil {
return nil, err
}
lbacl.SetModelManager(models.CachedLoadbalancerAclManager, lbacl)
if err := db.SetExternalId(lbacl, userCred, iLoadbalancerAcl.GetGlobalId()); err != nil {
return nil, err
}
return nil, lbacl.SyncWithCloudLoadbalancerAcl(ctx, userCred, iLoadbalancerAcl, lbacl.GetOwnerId())
}
func (self *SOpenStackRegionDriver) ValidateUpdateLoadbalancerListenerData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, lblis *models.SLoadbalancerListener, backendGroup db.IModel) (*jsonutils.JSONDict, error) {
if lblis.Status == api.LB_SYNC_CONF {
return nil, httperrors.NewResourceNotFoundError("loadbalancer listener %s is already updating", lblis.Name)
}
ownerId := lblis.GetOwnerId()
// todo: fix me here
aclStatusV := validators.NewStringChoicesValidator("acl_status", api.LB_BOOL_VALUES)
aclStatusV.Default(lblis.AclStatus)
aclTypeV := validators.NewStringChoicesValidator("acl_type", api.LB_ACL_TYPES)
if api.LB_ACL_TYPES.Has(lblis.AclType) {
aclTypeV.Default(lblis.AclType)
}
aclV := validators.NewModelIdOrNameValidator("acl", "loadbalanceracl", ownerId)
if len(lblis.AclId) > 0 {
aclV.Default(lblis.AclId)
}
certV := validators.NewModelIdOrNameValidator("certificate", "loadbalancercertificate", ownerId)
tlsCipherPolicyV := validators.NewStringChoicesValidator("tls_cipher_policy", api.LB_TLS_CIPHER_POLICIES).Default(api.LB_TLS_CIPHER_POLICY_1_2)
keyV := map[string]validators.IValidator{
"send_proxy": validators.NewStringChoicesValidator("send_proxy", api.LB_SENDPROXY_CHOICES),
"scheduler": validators.NewStringChoicesValidator("scheduler", api.LB_SCHEDULER_TYPES),
"acl_status": aclStatusV,
"acl_type": aclTypeV,
"acl": aclV,
"sticky_session": validators.NewStringChoicesValidator("sticky_session", api.LB_BOOL_VALUES),
"sticky_session_type": validators.NewStringChoicesValidator("sticky_session_type", api.LB_STICKY_SESSION_TYPES),
"sticky_session_cookie": validators.NewRegexpValidator("sticky_session_cookie", regexp.MustCompile(`\w+`)),
"sticky_session_cookie_timeout": validators.NewNonNegativeValidator("sticky_session_cookie_timeout"),
"health_check": validators.NewStringChoicesValidator("health_check", api.LB_BOOL_VALUES),
"health_check_type": models.LoadbalancerListenerManager.CheckTypeV(lblis.ListenerType),
"health_check_domain": validators.NewDomainNameValidator("health_check_domain").AllowEmpty(true).Default(""),
"health_check_path": validators.NewURLPathValidator("health_check_path").Default(""),
"health_check_uri": validators.NewURLPathValidator("health_check_uri").Default(""),
"health_check_http_code": validators.NewStringMultiChoicesValidator("health_check_http_code", api.LB_HEALTH_CHECK_HTTP_CODES).Sep(",").Default(api.LB_HEALTH_CHECK_HTTP_CODE_DEFAULT),
"health_check_rise": validators.NewRangeValidator("health_check_rise", 1, 10).Default(3),
"health_check_timeout": validators.NewRangeValidator("health_check_timeout", 1, 50).Default(10),
"health_check_interval": validators.NewRangeValidator("health_check_interval", 1, 50).Default(5),
"x_forwarded_for": validators.NewBoolValidator("x_forwarded_for"),
"certificate": certV,
"tls_cipher_policy": tlsCipherPolicyV,
}
if err := RunValidators(keyV, data, true); err != nil {
return nil, err
}
if t, _ := data.Int("health_check_rise"); t > 0 {
data.Set("health_check_fall", jsonutils.NewInt(t))
}
{
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)
}
}
return self.SManagedVirtualizationRegionDriver.ValidateUpdateLoadbalancerListenerData(ctx, userCred, data, lblis, backendGroup)
}
func (self *SOpenStackRegionDriver) 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 := lblis.GetCloudprovider()
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, err
}
lbcert, err := models.CachedLoadbalancerCertificateManager.GetOrCreateCachedCertificate(ctx, userCred, provider, lblis, cert.(*models.SLoadbalancerCertificate))
if err != nil {
return nil, err
}
if len(lbcert.ExternalId) == 0 {
_, err = self.createLoadbalancerCertificate(ctx, userCred, lbcert)
if err != nil {
return nil, err
}
}
_, err = db.Update(lblis, func() error {
lblis.CachedCertificateId = lbcert.GetId()
return nil
})
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerListener.UpdateCachedCertificateId")
}
}
}
params, err := lblis.GetOpenstackLoadbalancerListenerParams()
if err != nil {
return nil, err
}
loadbalancer := lblis.GetLoadbalancer()
if loadbalancer == nil {
return nil, fmt.Errorf("failed to find loadbalancer for lblis %s", lblis.Name)
}
iRegion, err := loadbalancer.GetIRegion()
if err != nil {
return nil, err
}
iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId)
if err != nil {
return nil, err
}
iListener, err := iLoadbalancer.GetILoadBalancerListenerById(lblis.ExternalId)
if err != nil {
return nil, err
}
if err := iListener.Sync(ctx, params); err != nil {
return nil, err
}
{
aclId, _ := task.GetParams().GetString("acl_id")
if len(aclId) > 0 {
provider := lblis.GetCloudprovider()
if provider == nil {
return nil, fmt.Errorf("failed to find provider for lblis %s", lblis.Name)
}
var lbacl *models.SCachedLoadbalancerAcl
// 先读取缓存,缓存不存在的情况下,从ACL表中取数据创建缓存
if _lbacl, err := models.CachedLoadbalancerAclManager.FetchById(aclId); err == nil && _lbacl != nil {
lbacl = _lbacl.(*models.SCachedLoadbalancerAcl)
} else {
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.createLoadbalancerAcl(ctx, userCred, lbacl)
if err != nil {
return nil, err
}
} else {
_, err = self.syncLoadbalancerAcl(ctx, userCred, lbacl)
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, "openstackRegionDriver.RequestSyncLoadbalancerListener.UpdateCachedAclId")
}
}
}
if err := iListener.Refresh(); err != nil {
return nil, err
}
return nil, lblis.SyncWithCloudLoadbalancerListener(ctx, userCred, loadbalancer, iListener, lblis.GetOwnerId(), lblis.GetCloudprovider())
})
return nil
}
func (self *SOpenStackRegionDriver) RequestDeleteLoadbalancerListener(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
if jsonutils.QueryBoolean(task.GetParams(), "purge", false) {
return nil, nil
}
loadbalancer := lblis.GetLoadbalancer()
if loadbalancer == nil {
return nil, fmt.Errorf("failed to find loadbalancer for lblis %s", lblis.Name)
}
iRegion, err := loadbalancer.GetIRegion()
if err != nil {
return nil, err
}
if len(loadbalancer.ExternalId) == 0 {
return nil, nil
}
if len(lblis.ExternalId) == 0 {
return nil, nil
}
iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
return nil, nil
}
return nil, err
}
iListener, err := iLoadbalancer.GetILoadBalancerListenerById(lblis.ExternalId)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
return nil, nil
}
return nil, err
}
// 取消服务器组关联
backendgroupId := iListener.GetBackendGroupId()
if len(backendgroupId) > 0 {
// 删除后端服务器组
ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(backendgroupId)
if err != nil {
return nil, errors.Wrap(err, "OpenStackRegionDriver.RequestDeleteLoadbalancerListener.GetILoadBalancerBackendGroup")
}
err = deleteOpenstackLoadbalancerBackendGroup(ctx, userCred, iLoadbalancer, ilbbg)
if err != nil {
return nil, errors.Wrap(err, "OpenStackRegionDriver.RequestDeleteLoadbalancerListener.DeleteBackendGroup")
}
}
err = deleteOpenstackCachedLbbg(ctx, userCred, lblis.GetId())
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.RequestDeleteLoadbalancerListener.deleteOpenstackCachedLbbg")
}
// 删除访问控制
aclId := iListener.GetAclId()
if len(aclId) > 0 {
iAcl, err := iRegion.GetILoadBalancerAclById(aclId)
if err != nil {
return nil, err
}
err = iAcl.Delete()
if err != nil {
return nil, err
}
acl := lblis.GetCachedLoadbalancerAcl()
if acl != nil {
err := db.DeleteModel(ctx, userCred, acl)
if err != nil {
return nil, err
}
}
}
// remove rules
irules, err := iListener.GetILoadbalancerListenerRules()
if err != nil {
return nil, errors.Wrap(err, "OpenStackRegionDriver.GetILoadbalancerListenerRules")
}
for i := range irules {
irule := irules[i]
err = deleteOpenstackLoadbalancerListenerRule(ctx, userCred, iLoadbalancer, irule)
if err != nil {
return nil, errors.Wrap(err, "OpenStackRegionDriver.deleteOpenStackLoadbalancerListenerRule")
}
}
rules, err := lblis.GetLoadbalancerListenerRules()
if err != nil && err != sql.ErrNoRows {
return nil, errors.Wrap(err, "OpenStackRegionDriver.GetLoadbalancerListenerRules")
}
for i := range rules {
rule := rules[i]
if err := deleteOpenstackLblisRule(ctx, userCred, rule.GetId()); err != nil {
return nil, errors.Wrap(err, "OpenStackRegionDriver.Rule.deleteOpenstackCachedLbbg")
}
}
return nil, iListener.Delete(ctx)
})
return nil
}
func (self *SOpenStackRegionDriver) syncLoadbalancerAcl(ctx context.Context, userCred mcclient.TokenCredential, lbacl *models.SCachedLoadbalancerAcl) (jsonutils.JSONObject, error) {
iRegion, err := lbacl.GetIRegion()
if err != nil {
return nil, err
}
acl := &cloudprovider.SLoadbalancerAccessControlList{
Name: lbacl.Name,
Entrys: []cloudprovider.SLoadbalancerAccessControlListEntry{},
}
lblis, err := lbacl.GetListener()
if err == nil {
if api.LB_BOOL_ON == lblis.AclStatus {
acl.AccessControlEnable = true
}
} else {
return nil, fmt.Errorf("SOpenStackRegionDriver.syncLoadbalancerAcl %s", err)
}
_localAcl, err := db.FetchById(models.LoadbalancerAclManager, lbacl.AclId)
if err != nil {
return nil, errors.Wrap(err, "regionDriver.FetchById.LoaclAcl")
}
localAcl := _localAcl.(*models.SLoadbalancerAcl)
if localAcl.AclEntries != nil {
for _, entry := range *localAcl.AclEntries {
acl.Entrys = append(acl.Entrys, cloudprovider.SLoadbalancerAccessControlListEntry{CIDR: entry.Cidr, Comment: entry.Comment})
}
}
lockman.LockRawObject(ctx, "acl", lbacl.Id)
defer lockman.ReleaseRawObject(ctx, "acl", lbacl.Id)
iLoadbalancerAcl, err := iRegion.GetILoadBalancerAclById(lbacl.ExternalId)
if err != nil {
return nil, err
}
return nil, iLoadbalancerAcl.Sync(acl)
}
func (self *SOpenStackRegionDriver) IsSupportLoadbalancerListenerRuleRedirect() bool {
return true
}
func (self *SOpenStackRegionDriver) ValidateCreateLoadbalancerListenerRuleData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, data *jsonutils.JSONDict, backendGroup db.IModel) (*jsonutils.JSONDict, error) {
var (
listenerV = validators.NewModelIdOrNameValidator("listener", "loadbalancerlistener", ownerId)
domainV = validators.NewHostPortValidator("domain").OptionalPort(true)
pathV = validators.NewURLPathValidator("path")
redirectV = validators.NewStringChoicesValidator("redirect", api.LB_REDIRECT_TYPES)
redirectCodeV = validators.NewIntChoicesValidator("redirect_code", api.LB_REDIRECT_CODES)
redirectSchemeV = validators.NewStringChoicesValidator("redirect_scheme", api.LB_REDIRECT_SCHEMES)
redirectHostV = validators.NewHostPortValidator("redirect_host").OptionalPort(true)
redirectPathV = validators.NewURLPathValidator("redirect_path")
)
keyV := map[string]validators.IValidator{
"status": validators.NewStringChoicesValidator("status", api.LB_STATUS_SPEC).Default(api.LB_STATUS_ENABLED),
"listener": listenerV,
"domain": domainV.AllowEmpty(true).Default(""),
"path": pathV.Default(""),
"http_request_rate": validators.NewNonNegativeValidator("http_request_rate").Default(0),
"http_request_rate_per_src": validators.NewNonNegativeValidator("http_request_rate_per_src").Default(0),
"redirect": redirectV.Default(api.LB_REDIRECT_OFF),
"redirect_code": redirectCodeV.Default(api.LB_REDIRECT_CODE_302),
"redirect_scheme": redirectSchemeV.Optional(true),
"redirect_host": redirectHostV.AllowEmpty(true).Optional(true),
"redirect_path": redirectPathV.AllowEmpty(true).Optional(true),
}
if err := RunValidators(keyV, data, false); err != nil {
return nil, err
}
listener := listenerV.Model.(*models.SLoadbalancerListener)
listenerType := listener.ListenerType
if listenerType != api.LB_LISTENER_TYPE_HTTP && listenerType != api.LB_LISTENER_TYPE_HTTPS {
return nil, httperrors.NewInputParameterError("listener type must be http/https, got %s", listenerType)
}
redirectType := redirectV.Value
if redirectType != api.LB_REDIRECT_OFF {
if redirectType == api.LB_REDIRECT_RAW {
scheme, host, path := redirectSchemeV.Value, redirectHostV.Value, redirectPathV.Value
if (scheme == "" || scheme == listenerType) && host == "" && path == "" {
return nil, httperrors.NewInputParameterError("redirect must have at least one of scheme, host, path changed")
}
if scheme == "" {
data.Set("redirect_scheme", jsonutils.NewString(listenerType))
}
if host == "" {
data.Set("redirect_host", jsonutils.NewString(domainV.Value))
}
}
}
{
if redirectV.Value == api.LB_REDIRECT_OFF {
if backendGroup == nil {
return nil, httperrors.NewInputParameterError("backend_group argument is missing")
}
}
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)
}
}
err := models.LoadbalancerListenerRuleCheckUniqueness(ctx, listener, domainV.Value, pathV.Value)
if err != nil {
return nil, err
}
data.Set("cloudregion_id", jsonutils.NewString(listener.GetRegionId()))
data.Set("manager_id", jsonutils.NewString(listener.GetCloudproviderId()))
return data, nil
}
func (self *SOpenStackRegionDriver) ValidateUpdateLoadbalancerListenerRuleData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, backendGroup db.IModel) (*jsonutils.JSONDict, error) {
var (
lbr = ctx.Value("lbr").(*models.SLoadbalancerListenerRule)
domainV = validators.NewHostPortValidator("domain").OptionalPort(true)
pathV = validators.NewURLPathValidator("path")
redirectV = validators.NewStringChoicesValidator("redirect", api.LB_REDIRECT_TYPES)
redirectCodeV = validators.NewIntChoicesValidator("redirect_code", api.LB_REDIRECT_CODES)
redirectSchemeV = validators.NewStringChoicesValidator("redirect_scheme", api.LB_REDIRECT_SCHEMES)
redirectHostV = validators.NewHostPortValidator("redirect_host").OptionalPort(true)
redirectPathV = validators.NewURLPathValidator("redirect_path")
)
if lbr.Redirect != "" {
redirectV.Default(lbr.Redirect)
}
if lbr.RedirectCode > 0 {
redirectCodeV.Default(int64(lbr.RedirectCode))
}
if lbr.RedirectScheme != "" {
redirectSchemeV.Default(lbr.RedirectScheme)
}
if lbr.RedirectHost != "" {
redirectHostV.Default(lbr.RedirectHost)
}
if lbr.RedirectPath != "" {
redirectPathV.Default(lbr.RedirectPath)
}
keyV := map[string]validators.IValidator{
"domain": domainV.AllowEmpty(true).Default(lbr.Domain),
"path": pathV.Default(lbr.Path),
"http_request_rate": validators.NewNonNegativeValidator("http_request_rate"),
"http_request_rate_per_src": validators.NewNonNegativeValidator("http_request_rate_per_src"),
"redirect": redirectV,
"redirect_code": redirectCodeV,
"redirect_scheme": redirectSchemeV,
"redirect_host": redirectHostV.AllowEmpty(true),
"redirect_path": redirectPathV.AllowEmpty(true),
}
for _, v := range keyV {
v.Optional(true)
if err := v.Validate(data); err != nil {
return nil, err
}
}
var (
redirectType = redirectV.Value
)
if redirectType != api.LB_REDIRECT_OFF {
if redirectType == api.LB_REDIRECT_RAW {
var (
lblis = lbr.GetLoadbalancerListener()
listenerType = lblis.ListenerType
)
scheme, host, path := redirectSchemeV.Value, redirectHostV.Value, redirectPathV.Value
if (scheme == "" || scheme == listenerType) && host == "" && path == "" {
return nil, httperrors.NewInputParameterError("redirect must have at least one of scheme, host, path changed")
}
}
}
if redirectType == api.LB_REDIRECT_OFF && backendGroup == nil {
return nil, httperrors.NewInputParameterError("non redirect lblistener rule must have backend_group set")
}
if backendGroup, ok := backendGroup.(*models.SLoadbalancerBackendGroup); ok && backendGroup.Id != lbr.BackendGroupId {
listenerM, err := models.LoadbalancerListenerManager.FetchById(lbr.ListenerId)
if err != nil {
return nil, httperrors.NewInputParameterError("loadbalancerlistenerrule %s(%s): fetching listener %s failed",
lbr.Name, lbr.Id, lbr.ListenerId)
}
listener := listenerM.(*models.SLoadbalancerListener)
if backendGroup.LoadbalancerId != listener.LoadbalancerId {
return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s",
backendGroup.Name, backendGroup.Id, backendGroup.LoadbalancerId, listener.LoadbalancerId)
}
}
return data, nil
}
func (self *SOpenStackRegionDriver) RequestCreateLoadbalancerListenerRule(ctx context.Context, userCred mcclient.TokenCredential, lbr *models.SLoadbalancerListenerRule, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
listener := lbr.GetLoadbalancerListener()
if listener == nil {
return nil, fmt.Errorf("failed to find listener for listnener rule %s", lbr.Name)
}
loadbalancer := listener.GetLoadbalancer()
if loadbalancer == nil {
return nil, fmt.Errorf("failed to find loadbalancer for listener %s", listener.Name)
}
iRegion, err := loadbalancer.GetIRegion()
if err != nil {
return nil, err
}
iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId)
if err != nil {
return nil, err
}
iListener, err := iLoadbalancer.GetILoadBalancerListenerById(listener.ExternalId)
if err != nil {
return nil, err
}
rule := &cloudprovider.SLoadbalancerListenerRule{
Name: lbr.Name,
Domain: lbr.Domain,
Path: lbr.Path,
Redirect: lbr.Redirect,
RedirectCode: lbr.RedirectCode,
RedirectScheme: lbr.RedirectScheme,
RedirectHost: lbr.RedirectHost,
RedirectPath: lbr.RedirectPath,
}
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)
}
cachedLbbg, err := models.OpenstackCachedLbbgManager.GetCachedBackendGroupByAssociateId(lbr.GetId())
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestCreateLoadbalancerListenerRule.GetCachedBackendGroupByAssociateId")
}
if cachedLbbg == nil {
return nil, fmt.Errorf("usable cached backend group not found")
}
rule.BackendGroupID = cachedLbbg.ExternalId
rule.BackendGroupType = group.Type
}
iListenerRule, err := iListener.CreateILoadBalancerListenerRule(rule)
if err != nil {
return nil, err
}
if err := db.SetExternalId(lbr, userCred, iListenerRule.GetGlobalId()); err != nil {
return nil, err
}
return nil, lbr.SyncWithCloudLoadbalancerListenerRule(ctx, userCred, iListenerRule, listener.GetOwnerId(), loadbalancer.GetCloudprovider())
})
return nil
}
func (self *SOpenStackRegionDriver) RequestDeleteLoadbalancerListenerRule(ctx context.Context, userCred mcclient.TokenCredential, lbr *models.SLoadbalancerListenerRule, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
if jsonutils.QueryBoolean(task.GetParams(), "purge", false) {
return nil, nil
}
listener := lbr.GetLoadbalancerListener()
if listener == nil {
return nil, fmt.Errorf("failed to find listener for listnener rule %s", lbr.Name)
}
loadbalancer := listener.GetLoadbalancer()
if loadbalancer == nil {
return nil, fmt.Errorf("failed to find loadbalancer for listener %s", listener.Name)
}
iRegion, err := loadbalancer.GetIRegion()
if err != nil {
return nil, err
}
iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId)
if err != nil {
return nil, err
}
iListener, err := iLoadbalancer.GetILoadBalancerListenerById(listener.ExternalId)
if err != nil {
return nil, err
}
if len(lbr.ExternalId) == 0 {
return nil, nil
}
iListenerRule, err := iListener.GetILoadBalancerListenerRuleById(lbr.ExternalId)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
return nil, nil
}
return nil, err
}
err = iListenerRule.Delete(ctx)
if err != nil {
return nil, errors.Wrap(err, "iListenerRule.Delete")
}
//delete associated backendgroup
cachedlbbgs, err := models.OpenstackCachedLbbgManager.GetListenerRuleCachedBackendGroups(lbr.GetId())
if err != nil {
if err != sql.ErrNoRows {
return nil, errors.Wrap(fmt.Errorf("provider is nil"), "OpenstackRegionDriver.RequestSyncLoadbalancerBackendGroup.GetCachedBackendGroupByAssociateId")
}
}
for i := 0; i < len(cachedlbbgs); i++ {
// delete outdated cache
if err := deleteOpenstackCachedLbbsByLbbg(ctx, userCred, cachedlbbgs[i].GetId()); err != nil {
return nil, errors.Wrap(err, "deleteOpenstackCachedLbbg.deleteOpenstackCachedLbbsByLbbg")
}
err = db.DeleteModel(ctx, userCred, &cachedlbbgs[i])
if err != nil {
return nil, errors.Wrap(err, "deleteOpenstackCachedLbbg.DeleteModel")
}
// delete ilbbg
ilbbg, err := iLoadbalancer.GetILoadBalancerBackendGroupById(cachedlbbgs[i].ExternalId)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
ilbbg = nil
} else {
return nil, errors.Wrapf(err, "GetILoadBalancerBackendGroupById(%s)", cachedlbbgs[i].ExternalId)
}
} else {
err := ilbbg.Delete(ctx)
if err != nil {
return nil, errors.Wrap(err, "iLoadbalancerBackendGroup.Delete(ctx)")
}
}
}
return nil, nil
})
return nil
}
func (self *SOpenStackRegionDriver) createCachedLbbg(ctx context.Context, lb *models.SLoadbalancer, lblis *models.SLoadbalancerListener, lbr *models.SLoadbalancerListenerRule, lbbg *models.SLoadbalancerBackendGroup) (*models.SOpenstackCachedLbbg, error) {
// create loadbalancer backendgroup cache
cachedLbbg := &models.SOpenstackCachedLbbg{}
cachedLbbg.ManagerId = lb.GetCloudproviderId()
cachedLbbg.CloudregionId = lb.GetRegionId()
cachedLbbg.LoadbalancerId = lb.GetId()
cachedLbbg.BackendGroupId = lbbg.GetId()
if lbr != nil {
cachedLbbg.AssociatedType = api.LB_ASSOCIATE_TYPE_RULE
cachedLbbg.AssociatedId = lbr.GetId()
cachedLbbg.ProtocolType = lblis.ListenerType
} else {
cachedLbbg.AssociatedType = api.LB_ASSOCIATE_TYPE_LISTENER
cachedLbbg.AssociatedId = lblis.GetId()
cachedLbbg.ProtocolType = lblis.ListenerType
}
err := models.OpenstackCachedLbbgManager.TableSpec().Insert(ctx, cachedLbbg)
if err != nil {
return nil, err
}
cachedLbbg.SetModelManager(models.OpenstackCachedLbbgManager, cachedLbbg)
return cachedLbbg, nil
}
func (self *SOpenStackRegionDriver) syncCloudlbbs(ctx context.Context, userCred mcclient.TokenCredential, lb *models.SLoadbalancer, cachedLbbg *models.SOpenstackCachedLbbg, extlbbg cloudprovider.ICloudLoadbalancerBackendGroup, backends []cloudprovider.SLoadbalancerBackend) error {
ibackends, err := extlbbg.GetILoadbalancerBackends()
if err != nil {
return errors.Wrap(err, "OpenstackRegionDriver.syncCloudLoadbalancerBackends.GetILoadbalancerBackends")
}
m_ibackends := make(map[string]cloudprovider.ICloudLoadbalancerBackend)
m_backends := make(map[string]*cloudprovider.SLoadbalancerBackend)
for _, v := range ibackends {
m_ibackends[v.GetBackendId()] = v
}
for i, v := range backends {
m_backends[v.ExternalID] = &backends[i]
}
for _, ibackend := range ibackends {
mbackend, _ := m_backends[ibackend.GetBackendId()]
if mbackend == nil || mbackend.Port != ibackend.GetPort() {
err = extlbbg.RemoveBackendServer(ibackend.GetId(), ibackend.GetWeight(), ibackend.GetPort())
if err != nil {
return errors.Wrap(err, "OpenstackRegionDriver.syncCloudLoadbalancerBackends.RemoveBackendServer")
}
} else {
if ibackend.GetWeight() != mbackend.Weight {
ibackend.SyncConf(ctx, mbackend.Port, mbackend.Weight)
}
}
}
for _, backend := range backends {
ibackend, _ := m_ibackends[backend.ExternalID]
if ibackend == nil {
_, err = extlbbg.AddBackendServer(backend.ExternalID, backend.Weight, backend.Port)
if err != nil {
return errors.Wrap(err, "OpenstackRegionDriver.syncCloudLoadbalancerBackends.AddBackendServer")
}
}
}
return nil
}
func (self *SOpenStackRegionDriver) syncCachedLbbs(ctx context.Context, userCred mcclient.TokenCredential, lb *models.SLoadbalancer, lbbg *models.SOpenstackCachedLbbg, extlbbg cloudprovider.ICloudLoadbalancerBackendGroup) error {
iBackends, err := extlbbg.GetILoadbalancerBackends()
if err != nil {
return errors.Wrap(err, "OpenstackRegionDriver.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.OpenstackCachedLbManager.SyncLoadbalancerBackends(ctx, userCred, provider, lbbg, iBackends, &models.SSyncRange{})
if result.IsError() {
return errors.Wrap(result.AllError(), "OpenstackRegionDriver.syncLoadbalancerBackendCaches.SyncLoadbalancerBackends")
}
}
return nil
}
func (self *SOpenStackRegionDriver) updateCachedLbbg(ctx context.Context, cachedLbbg *models.SOpenstackCachedLbbg, backendGroupId string, externalBackendGroupId string, asscoicateId string, asscoicateType string) error {
_, err := db.UpdateWithLock(ctx, cachedLbbg, func() error {
if len(backendGroupId) > 0 {
cachedLbbg.BackendGroupId = backendGroupId
}
if len(externalBackendGroupId) > 0 {
cachedLbbg.ExternalId = externalBackendGroupId
}
if len(asscoicateId) > 0 {
cachedLbbg.AssociatedId = asscoicateId
}
if len(asscoicateType) > 0 {
cachedLbbg.AssociatedType = asscoicateType
}
return nil
})
if err != nil {
return err
}
return nil
}
func (self *SOpenStackRegionDriver) removeCachedLbbg(ctx context.Context, userCred mcclient.TokenCredential, lbbg *models.SOpenstackCachedLbbg) error {
backends, err := lbbg.GetCachedBackends()
if err != nil && err != sql.ErrNoRows {
return errors.Wrap(err, "openstackRegionDriver.GetCachedBackends")
}
for i := range backends {
err = db.DeleteModel(ctx, userCred, &backends[i])
if err != nil {
return errors.Wrap(err, "openstackRegionDriver.DeleteModel")
}
}
err = db.DeleteModel(ctx, userCred, lbbg)
if err != nil {
return errors.Wrap(err, "openstackRegionDriver.DeleteModel")
}
return nil
}
func (self *SOpenStackRegionDriver) createLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lblis *models.SLoadbalancerListener, lbr *models.SLoadbalancerListenerRule, lbbg *models.SLoadbalancerBackendGroup, backends []cloudprovider.SLoadbalancerBackend) (jsonutils.JSONObject, error) {
if len(lblis.ListenerType) == 0 {
return nil, fmt.Errorf("loadbalancer backendgroup missing protocol type")
}
iRegion, err := lbbg.GetIRegion()
if err != nil {
return nil, err
}
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, err
}
cachedLbbg, err := self.createCachedLbbg(ctx, lb, lblis, lbr, lbbg)
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.createLoadbalancerBackendGroupCache")
}
group, err := lbbg.GetOpenstackBackendGroupParams(lblis, nil)
if err != nil {
return nil, err
}
// 避免和defaultpool冲突
if lbr != nil {
group.ListenerID = ""
}
iLoadbalancerBackendGroup, err := iLoadbalancer.CreateILoadBalancerBackendGroup(group)
if err != nil {
return nil, err
}
if err := db.SetExternalId(cachedLbbg, userCred, iLoadbalancerBackendGroup.GetGlobalId()); err != nil {
return nil, err
}
err = self.syncCloudlbbs(ctx, userCred, lb, cachedLbbg, iLoadbalancerBackendGroup, backends)
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.createLoadbalancerBackendGroup.syncCloudLoadbalancerBackends")
}
err = self.syncCachedLbbs(ctx, userCred, lb, cachedLbbg, iLoadbalancerBackendGroup)
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.createLoadbalancerBackendGroup.syncLoadbalancerBackendCaches")
}
return nil, nil
}
func deleteOpenstackLoadbalancerListenerRule(ctx context.Context, userCred mcclient.TokenCredential, ilb cloudprovider.ICloudLoadbalancer, irule cloudprovider.ICloudLoadbalancerListenerRule) error {
err := irule.Refresh()
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
return nil
}
return errors.Wrap(err, "OpenStackRegionDriver.Rule.Refresh")
}
lbbgId := irule.GetBackendGroupId()
err = irule.Delete(ctx)
if err != nil && err != cloudprovider.ErrNotFound {
return errors.Wrap(err, "OpenStackRegionDriver.Rule.Delete")
}
// delete backendgroup
if len(lbbgId) > 0 {
ilbbg, err := ilb.GetILoadBalancerBackendGroupById(lbbgId)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
return nil
}
return errors.Wrap(err, "OpenStackRegionDriver.Rule.GetILoadBalancerBackendGroupById")
}
err = deleteOpenstackLoadbalancerBackendGroup(ctx, userCred, ilb, ilbbg)
if err != nil {
return errors.Wrap(err, "OpenStackRegionDriver.Rule.deleteOpenstackLoadbalancerBackendGroup")
}
}
return nil
}
func deleteOpenstackLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, ilb cloudprovider.ICloudLoadbalancer, ilbbg cloudprovider.ICloudLoadbalancerBackendGroup) error {
err := ilbbg.Refresh()
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
return nil
}
return errors.Wrap(err, "OpenStackRegionDriver.BackendGroup.Refresh")
}
ibackends, err := ilbbg.GetILoadbalancerBackends()
if err != nil {
return errors.Wrap(err, "OpenStackRegionDriver.BackendGroup.GetILoadbalancerBackends")
}
for i := range ibackends {
ilbb := ibackends[i]
err = deleteOpenstackLoadbalancerBackend(ctx, userCred, ilb, ilbbg, ilbb)
if err != nil {
return errors.Wrap(err, "OpenStackRegionDriver.BackendGroup.deleteOpenstackLoadbalancerBackend")
}
}
err = ilbbg.Delete(ctx)
if err != nil && err != cloudprovider.ErrNotFound {
return errors.Wrap(err, "OpenStackRegionDriver.BackendGroup.Delete")
}
return nil
}
func deleteOpenstackLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, ilb cloudprovider.ICloudLoadbalancer, ilbbg cloudprovider.ICloudLoadbalancerBackendGroup, ilbb cloudprovider.ICloudLoadbalancerBackend) error {
err := ilbbg.RemoveBackendServer(ilbb.GetId(), ilbb.GetWeight(), ilbb.GetPort())
if err != nil && err != cloudprovider.ErrNotFound {
return errors.Wrap(err, "OpenStackRegionDriver.Backend.Delete")
}
return nil
}
func deleteOpenstackLblisRule(ctx context.Context, userCred mcclient.TokenCredential, ruleId string) error {
rule, err := db.FetchById(models.LoadbalancerListenerRuleManager, ruleId)
if err != nil {
if err == sql.ErrNoRows {
return nil
}
return errors.Wrap(err, "deleteOpenstackLblisRule.FetchById")
}
err = deleteOpenstackCachedLbbg(ctx, userCred, ruleId)
if err != nil {
return errors.Wrap(err, "deleteOpenstackLblisRule.deleteOpenstackCachedLbbg")
}
err = db.DeleteModel(ctx, userCred, rule)
if err != nil {
return errors.Wrap(err, "deleteOpenstackLblisRule.DeleteModel")
}
return nil
}
func deleteOpenstackCachedLbbg(ctx context.Context, userCred mcclient.TokenCredential, associatedId string) error {
lbbg, err := models.OpenstackCachedLbbgManager.GetCachedBackendGroupByAssociateId(associatedId)
if err != nil {
if err == sql.ErrNoRows {
return nil
}
return errors.Wrap(err, "deleteOpenstackCachedLbbg.GetCachedBackendGroupByAssociateId")
}
if err := deleteOpenstackCachedLbbsByLbbg(ctx, userCred, lbbg.GetId()); err != nil {
return errors.Wrap(err, "deleteOpenstackCachedLbbg.deleteOpenstackCachedLbbsByLbbg")
}
err = db.DeleteModel(ctx, userCred, lbbg)
if err != nil {
return errors.Wrap(err, "deleteOpenstackCachedLbbg.DeleteModel")
}
return nil
}
func deleteOpenstackCachedLbbsByLbbg(ctx context.Context, userCred mcclient.TokenCredential, cachedLbbgId string) error {
cachedLbbs := []models.SOpenstackCachedLb{}
q := models.OpenstackCachedLbManager.Query().IsFalse("pending_deleted").Equals("cached_backend_group_id", cachedLbbgId)
err := db.FetchModelObjects(models.OpenstackCachedLbManager, q, &cachedLbbs)
if err != nil {
if err == sql.ErrNoRows {
return nil
}
return errors.Wrap(err, "deleteOpenstackCachedLbbsByLbbg.FetchModelObjects")
}
for i := range cachedLbbs {
cachedLbb := cachedLbbs[i]
err = db.DeleteModel(ctx, userCred, &cachedLbb)
if err != nil {
return errors.Wrap(err, "deleteOpenstackCachedLbbsByLbbg.DeleteModel")
}
}
return nil
}
func (self *SOpenStackRegionDriver) RequestCreateLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lbbg *models.SLoadbalancerBackendGroup, backends []cloudprovider.SLoadbalancerBackend, task taskman.ITask) error {
// 未指定listenerId或ruleId情况下,跳过远端创建步骤
listenerId, _ := task.GetParams().GetString("listenerId")
ruleId, _ := task.GetParams().GetString("ruleId")
if len(listenerId) == 0 && len(ruleId) == 0 {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
return nil, nil
})
return nil
}
var rule *models.SLoadbalancerListenerRule
var listener *models.SLoadbalancerListener
if len(ruleId) > 0 {
_rule, err := db.FetchById(models.LoadbalancerListenerRuleManager, ruleId)
if err != nil {
return err
}
rule = _rule.(*models.SLoadbalancerListenerRule)
listener = rule.GetLoadbalancerListener()
} else {
_listener, err := db.FetchById(models.LoadbalancerListenerManager, listenerId)
if err != nil {
return err
}
listener = _listener.(*models.SLoadbalancerListener)
}
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
return self.createLoadbalancerBackendGroup(ctx, userCred, listener, rule, lbbg, backends)
})
return nil
}
func (self *SOpenStackRegionDriver) validatorCachedLbbgConflict(ilb cloudprovider.ICloudLoadbalancer, lblis *models.SLoadbalancerListener, olbbg *models.SOpenstackCachedLbbg, nlbbg *models.SOpenstackCachedLbbg, rlbbg *models.SOpenstackCachedLbbg) error {
if rlbbg == nil {
return nil
}
if len(rlbbg.AssociatedId) > 0 && lblis.GetId() != rlbbg.AssociatedId {
return fmt.Errorf("sync required, backend group aready assoicate with %s %s", rlbbg.AssociatedType, rlbbg.AssociatedId)
}
if olbbg != nil && len(rlbbg.AssociatedId) > 0 && rlbbg.AssociatedId != olbbg.AssociatedId {
// 与本地关联状态不一致
return fmt.Errorf("sync required, backend group aready assoicate with %s %s", rlbbg.AssociatedType, rlbbg.AssociatedId)
}
if nlbbg != nil && len(nlbbg.ExternalId) > 0 {
// 需绑定的服务器组,被人为删除了
_, err := ilb.GetILoadBalancerBackendGroupById(nlbbg.GetExternalId())
if err != nil {
return fmt.Errorf("sync required, validatorCachedLbbgConflict.GetILoadBalancerBackendGroupById %s", err)
}
// 需绑定的服务器组,被人为关联了其他监听或者监听规则
listeners, err := ilb.GetILoadBalancerListeners()
if err != nil {
return fmt.Errorf("sync required, validatorCachedLbbgConflict.GetILoadBalancerListeners %s", err)
}
for i := range listeners {
listener := listeners[i]
if bgId := listener.GetBackendGroupId(); len(bgId) > 0 && bgId == nlbbg.ExternalId && listener.GetGlobalId() != lblis.GetExternalId() {
return fmt.Errorf("sync required, backend group aready assoicate with listener %s(external id )", listener.GetGlobalId())
}
rules, err := listener.GetILoadbalancerListenerRules()
if err != nil {
return fmt.Errorf("sync required, validatorCachedLbbgConflict.GetILoadbalancerListenerRules %s", err)
}
for j := range rules {
rule := rules[j]
if bgId := rule.GetBackendGroupId(); len(bgId) > 0 && bgId == nlbbg.ExternalId {
return fmt.Errorf("sync required, backend group aready assoicate with rule %s(external id )", rule.GetGlobalId())
}
}
}
}
return nil
}
func (self *SOpenStackRegionDriver) 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, "OpenstackRegionDriver.RequestSyncLoadbalancerbackendGroup.GetLoadbalancerBackendGroup")
}
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
}
provider := lb.GetCloudprovider()
if provider == nil {
return nil, errors.Wrap(fmt.Errorf("provider is nil"), "OpenstackRegionDriver.RequestSyncLoadbalancerBackendGroup.GetCloudprovider")
}
// new group params
groupInput, err := lbbg.GetOpenstackBackendGroupParams(lblis, nil)
if err != nil {
return nil, errors.Wrap(err, "GetOpenstackBackendGroupParams")
}
// new backends params
backendsInput, err := lbbg.GetBackendsParams()
if err != nil {
return nil, err
}
cachedlbbgs, err := models.OpenstackCachedLbbgManager.GetListenerCachedBackendGroups(lblis.GetId())
if err != nil {
if err != sql.ErrNoRows {
return nil, errors.Wrap(fmt.Errorf("provider is nil"), "OpenstackRegionDriver.RequestSyncLoadbalancerBackendGroup.GetCachedBackendGroupByAssociateId")
}
}
var cachedlbbg *models.SOpenstackCachedLbbg
ibackendGroups := make(map[string]cloudprovider.ICloudLoadbalancerBackendGroup)
for i := 0; i < len(cachedlbbgs); i++ {
// fetch ilbbg
ilbbg, err := ilb.GetILoadBalancerBackendGroupById(cachedlbbgs[i].ExternalId)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
ilbbg = nil
} else {
return nil, errors.Wrapf(err, "GetILoadBalancerBackendGroupById(%s)", cachedlbbgs[i].ExternalId)
}
}
ibackendGroups[cachedlbbgs[i].Id] = ilbbg
if cachedlbbgs[i].BackendGroupId == lblis.BackendGroupId && ibackendGroups[cachedlbbgs[i].Id] != nil {
cachedlbbg = &cachedlbbgs[i]
} else {
// delete outdated cache,不保留游离的后端组
if err := deleteOpenstackCachedLbbsByLbbg(ctx, userCred, cachedlbbgs[i].GetId()); err != nil {
return nil, errors.Wrap(err, "deleteOpenstackCachedLbbg.deleteOpenstackCachedLbbsByLbbg")
}
err = db.DeleteModel(ctx, userCred, &cachedlbbgs[i])
if err != nil {
return nil, errors.Wrap(err, "deleteOpenstackCachedLbbg.DeleteModel")
}
//关联listener的可以直接删除
if ibackendGroups[cachedlbbgs[i].Id] != nil {
err := ibackendGroups[cachedlbbgs[i].Id].Delete(ctx)
if err != nil {
return nil, errors.Wrap(err, "iLoadbalancerBackendGroup.Delete(ctx)")
}
}
}
}
// create or update
if cachedlbbg == nil {
// 新创建前需要确保listener没有defaultpool(外部操作)
{
iListener, err := ilb.GetILoadBalancerListenerById(lblis.ExternalId)
if err != nil {
return nil, err
}
ilbbgId := iListener.GetBackendGroupId()
if len(ilbbgId) > 0 {
ilbbg, err := ilb.GetILoadBalancerBackendGroupById(ilbbgId)
if err != nil {
if errors.Cause(err) != cloudprovider.ErrNotFound {
return nil, errors.Wrapf(err, "GetILoadBalancerBackendGroupById(%s)", ilbbgId)
}
} else {
err = ilbbg.Delete(ctx)
if err != nil {
return nil, errors.Wrap(err, "iLoadbalancerBackendGroup.Delete(ctx)")
}
}
}
}
_, err = self.createLoadbalancerBackendGroup(ctx, task.GetUserCred(), lblis, nil, lbbg, backendsInput)
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.Sync.Case1.createLoadbalancerBackendGroup")
}
} else {
err = self.syncCloudlbbs(ctx, userCred, lb, cachedlbbg, ibackendGroups[cachedlbbg.Id], backendsInput)
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.Sync.Case6.syncCloudlbbs")
}
err = self.syncCachedLbbs(ctx, userCred, lb, cachedlbbg, ibackendGroups[cachedlbbg.Id])
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.Sync.Case6.syncCloudlbbs")
}
}
// continue here
cachedLbbg, err := models.OpenstackCachedLbbgManager.GetCachedBackendGroupByAssociateId(lblis.GetId())
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.Sync.GetCachedBackendGroupByAssociateId")
}
ilbbg, err := ilb.GetILoadBalancerBackendGroupById(cachedLbbg.GetExternalId())
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.Sync.GetILoadBalancerBackendGroupById")
}
err = ilbbg.Sync(ctx, groupInput)
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.Sync.LoadbalancerBackendGroup")
}
if err := cachedLbbg.SyncWithCloudLoadbalancerBackendgroup(ctx, task.GetUserCred(), lb, ilbbg, lb.GetOwnerId(), lb.GetCloudprovider()); err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.Sync.SyncWithCloudLoadbalancerBackendgroup")
}
return nil, nil
})
return nil
}
func (self *SOpenStackRegionDriver) ValidateDeleteLoadbalancerBackendGroupCondition(ctx context.Context, lbbg *models.SLoadbalancerBackendGroup) error {
// pool不能被l7policy关联,若要解除关联关系,可通过更新转发策略将转测策略的redirect_pool_id更新为null。
count, err := lbbg.RefCount()
if err != nil {
return err
}
if count != 0 {
return fmt.Errorf("backendgroup is binding with loadbalancer/listener/listenerrule.")
}
return nil
}
func (self *SOpenStackRegionDriver) RequestDeleteLoadbalancerBackendGroup(ctx context.Context, userCred mcclient.TokenCredential, lbbg *models.SLoadbalancerBackendGroup, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
if jsonutils.QueryBoolean(task.GetParams(), "purge", false) {
return nil, nil
}
iRegion, err := lbbg.GetIRegion()
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.")
}
loadbalancer := lbbg.GetLoadbalancer()
if loadbalancer == nil {
return nil, fmt.Errorf("failed to find loadbalancer for backendgroup %s", lbbg.Name)
}
iLoadbalancer, err := iRegion.GetILoadBalancerById(loadbalancer.ExternalId)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
return nil, nil
}
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.GetILoadBalancerById")
}
cachedLbbgs, err := models.OpenstackCachedLbbgManager.GetCachedBackendGroups(lbbg.GetId())
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.GetCachedBackendGroups")
}
for _, cachedLbbg := range cachedLbbgs {
if len(cachedLbbg.ExternalId) == 0 {
continue
}
iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(cachedLbbg.ExternalId)
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
if err := deleteOpenstackCachedLbbg(ctx, userCred, cachedLbbg.AssociatedId); err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.deleteOpenstackCachedLbbg")
}
continue
}
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.GetILoadBalancerBackendGroupById")
}
ilbbs, err := iLoadbalancerBackendGroup.GetILoadbalancerBackends()
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.GetILoadbalancerBackends")
}
for _, ilbb := range ilbbs {
iLoadbalancerBackendGroup.RemoveBackendServer(ilbb.GetId(), ilbb.GetWeight(), ilbb.GetPort())
_cachedLbb, err := db.FetchByExternalId(models.OpenstackCachedLbManager, ilbb.GetGlobalId())
if err != nil {
if err != sql.ErrNoRows {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.FetchByExternalId")
}
continue
}
cachedLbb := _cachedLbb.(*models.SOpenstackCachedLb)
err = db.DeleteModel(ctx, userCred, cachedLbb)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.DeleteModel")
}
}
err = iLoadbalancerBackendGroup.Delete(ctx)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.Delete")
}
cachedLbbg.SetModelManager(models.OpenstackCachedLbbgManager, &cachedLbbg)
err = db.DeleteModel(ctx, userCred, &cachedLbbg)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestDeleteLoadbalancerBackendGroup.DeleteModel")
}
}
return nil, nil
})
return nil
}
func (self *SOpenStackRegionDriver) ValidateCreateLoadbalancerBackendData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, backendType string, lb *models.SLoadbalancer, backendGroup *models.SLoadbalancerBackendGroup, backend db.IModel) (*jsonutils.JSONDict, error) {
ownerId := ctx.Value("ownerId").(mcclient.IIdentityProvider)
man := models.LoadbalancerBackendManager
backendTypeV := validators.NewStringChoicesValidator("backend_type", api.LB_BACKEND_TYPES)
keyV := map[string]validators.IValidator{
"backend_type": backendTypeV,
"weight": validators.NewRangeValidator("weight", 0, 100).Default(10),
"port": validators.NewPortValidator("port"),
"send_proxy": validators.NewStringChoicesValidator("send_proxy", api.LB_SENDPROXY_CHOICES).Default(api.LB_SENDPROXY_OFF),
}
if err := RunValidators(keyV, data, false); err != nil {
return nil, err
}
var basename string
switch backendType {
case api.LB_BACKEND_GUEST:
backendV := validators.NewModelIdOrNameValidator("backend", "server", ownerId)
err := backendV.Validate(data)
if err != nil {
return nil, err
}
guest := backendV.Model.(*models.SGuest)
err = man.ValidateBackendVpc(lb, guest, backendGroup)
if err != nil {
return nil, err
}
address, err := models.LoadbalancerBackendManager.GetGuestAddress(guest)
if err != nil {
return nil, errors.Wrap(err, "OpenstackRegionDriver.ValidateCreateLoadbalancerBackendData.GetGuestAddress")
}
data.Set("address", jsonutils.NewString(address))
basename = guest.Name
backend = backendV.Model
case api.LB_BACKEND_HOST:
if !db.IsAdminAllowCreate(userCred, man) {
return nil, fmt.Errorf("only sysadmin can specify host as backend")
}
backendV := validators.NewModelIdOrNameValidator("backend", "host", userCred)
err := backendV.Validate(data)
if err != nil {
return nil, err
}
host := backendV.Model.(*models.SHost)
{
if len(host.AccessIp) == 0 {
return nil, fmt.Errorf("host %s has no access ip", host.GetId())
}
data.Set("address", jsonutils.NewString(host.AccessIp))
}
basename = host.Name
backend = backendV.Model
case api.LB_BACKEND_IP:
if !db.IsAdminAllowCreate(userCred, man) {
return nil, fmt.Errorf("only sysadmin can specify ip address as backend")
}
backendV := validators.NewIPv4AddrValidator("backend")
err := backendV.Validate(data)
if err != nil {
return nil, err
}
ip := backendV.IP.String()
data.Set("address", jsonutils.NewString(ip))
basename = ip
default:
return nil, fmt.Errorf("internal error: unexpected backend type %s", backendType)
}
name, _ := data.GetString("name")
if name == "" {
name = fmt.Sprintf("%s-%s-%s-%s", backendGroup.Name, backendType, basename, rand.String(4))
}
data.Set("name", jsonutils.NewString(name))
data.Set("manager_id", jsonutils.NewString(lb.GetCloudproviderId()))
data.Set("cloudregion_id", jsonutils.NewString(lb.GetRegionId()))
return data, nil
}
func (self *SOpenStackRegionDriver) ValidateUpdateLoadbalancerBackendData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, lbbg *models.SLoadbalancerBackendGroup) (*jsonutils.JSONDict, error) {
keyV := map[string]validators.IValidator{
"weight": validators.NewRangeValidator("weight", 0, 100).Optional(true),
"port": validators.NewPortValidator("port").Optional(true),
"send_proxy": validators.NewStringChoicesValidator("send_proxy", api.LB_SENDPROXY_CHOICES).Optional(true),
}
if err := RunValidators(keyV, data, true); err != nil {
return nil, err
}
// 只能更新权重。不能更新端口
port, err := data.Int("port")
if err == nil && port != 0 {
return data, fmt.Errorf("can not update backend port.")
}
return data, nil
}
func (self *SOpenStackRegionDriver) RequestCreateLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, lbb *models.SLoadbalancerBackend, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
lbbg := lbb.GetLoadbalancerBackendGroup()
if lbbg == nil {
return nil, fmt.Errorf("failed to find lbbg for backend %s", lbb.Name)
}
lb := lbbg.GetLoadbalancer()
if lb == nil {
return nil, fmt.Errorf("failed to find lb for backendgroup %s", lbbg.Name)
}
cachedlbbgs, err := models.OpenstackCachedLbbgManager.GetCachedBackendGroups(lbbg.GetId())
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestCreateLoadbalancerBackend.GetCachedBackendGroups")
}
guest := lbb.GetGuest()
if guest == nil {
return nil, fmt.Errorf("failed to find guest for lbb %s", lbb.Name)
}
var ibackend cloudprovider.ICloudLoadbalancerBackend
for _, cachedLbbg := range cachedlbbgs {
iLoadbalancerBackendGroup, err := cachedLbbg.GetICloudLoadbalancerBackendGroup()
if err != nil {
if errors.Cause(err) == cloudprovider.ErrNotFound {
if err := deleteOpenstackCachedLbbg(ctx, userCred, cachedLbbg.AssociatedId); err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestCreateLoadbalancerBackend.deleteOpenstackCachedLbbg")
}
continue
}
return nil, errors.Wrap(err, "openstackRegionDriver.RequestCreateLoadbalancerBackend.GetICloudLoadbalancerBackendGroup")
}
ibackend, err = iLoadbalancerBackendGroup.AddBackendServer(guest.ExternalId, lbb.Weight, lbb.Port)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestCreateLoadbalancerBackend.AddBackendServer")
}
_, err = models.OpenstackCachedLbManager.CreateOpenstackCachedLb(ctx, userCred, lbb, &cachedLbbg, ibackend, cachedLbbg.GetOwnerId())
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestCreateLoadbalancerBackend.CreateOpenstackCachedLb")
}
}
if ibackend != nil {
if err := lbb.SyncWithCloudLoadbalancerBackend(ctx, userCred, ibackend, lbbg.GetOwnerId(), lb.GetCloudprovider()); err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestCreateLoadbalancerBackend.SyncWithCloudLoadbalancerBackend")
}
}
return nil, nil
})
return nil
}
func (self *SOpenStackRegionDriver) RequestSyncLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, lbb *models.SLoadbalancerBackend, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
cachedlbbs, err := models.OpenstackCachedLbManager.GetBackendsByLocalBackendId(lbb.GetId())
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerBackend.GetBackendsByLocalBackendId")
}
for _, cachedlbb := range cachedlbbs {
cachedlbbg, _ := cachedlbb.GetCachedBackendGroup()
if cachedlbbg == nil {
return nil, fmt.Errorf("failed to find lbbg for backend %s", cachedlbb.Name)
}
lb, err := cachedlbbg.GetLoadbalancer()
if err != nil {
return nil, errors.Wrap(err, "cachedlbbg.GetLoadbalancer()")
}
iRegion, err := lb.GetIRegion()
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerBackend.GetIRegion")
}
iLoadbalancer, err := iRegion.GetILoadBalancerById(lb.ExternalId)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerBackend.GetILoadBalancerById")
}
iLoadbalancerBackendGroup, err := iLoadbalancer.GetILoadBalancerBackendGroupById(cachedlbbg.ExternalId)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerBackend.GetILoadBalancerBackendGroupById")
}
iBackend, err := iLoadbalancerBackendGroup.GetILoadbalancerBackendById(cachedlbb.ExternalId)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerBackend.GetILoadbalancerBackendById")
}
err = iBackend.SyncConf(ctx, lbb.Port, lbb.Weight)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerBackend.SyncConf")
}
iBackend, err = iLoadbalancerBackendGroup.GetILoadbalancerBackendById(cachedlbb.ExternalId)
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerBackend.GetILoadbalancerBackendById")
}
err = cachedlbb.SyncWithCloudLoadbalancerBackend(ctx, userCred, iBackend, lbb.GetOwnerId())
if err != nil {
return nil, errors.Wrap(err, "openstackRegionDriver.RequestSyncLoadbalancerBackend.SyncWithCloudLoadbalancerBackend")
}
}
return nil, nil
})
return nil
}
func (self *SOpenStackRegionDriver) RequestDeleteLoadbalancerBackend(ctx context.Context, userCred mcclient.TokenCredential, lbb *models.SLoadbalancerBackend, task taskman.ITask) error {
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
if jsonutils.QueryBoolean(task.GetParams(), "purge", false) {
return nil, nil
}
cachedlbbs, err := models.OpenstackCachedLbManager.GetBackendsByLocalBackendId(lbb.GetId())
if err != nil {
return nil, err
}
for _, cachedlbb := range cachedlbbs {
cachedlbbg, _ := cachedlbb.GetCachedBackendGroup()
if cachedlbbg == nil {
log.Warningf("failed to find lbbg for backend %s", cachedlbb.Name)
continue
}
lb, err := cachedlbbg.GetLoadbalancer()
if err != nil {
return nil, errors.Wrap(err, "cachedlbbg.GetLoadbalancer()")
}
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(cachedlbbg.ExternalId)
if err != nil {
return nil, err
}
err = iLoadbalancerBackendGroup.RemoveBackendServer(cachedlbb.ExternalId, lbb.Weight, lbb.Port)
if err != nil {
return nil, err
}
err = db.DeleteModel(ctx, userCred, &cachedlbb)
if err != nil {
return nil, err
}
}
return nil, nil
})
return nil
}