From ea5f72563beef76efb1a2ae86636ac7d129299c5 Mon Sep 17 00:00:00 2001 From: Jian Qiu Date: Sun, 29 May 2022 23:51:28 +0800 Subject: [PATCH] fix: vpc eip misc fixes: 1. eip filter 2. lb filter 3. create eip when (#14343) lb creating Co-authored-by: Qiu Jian --- pkg/apis/compute/loadbalancer.go | 16 ++++ pkg/compute/models/elasticips.go | 10 ++ pkg/compute/models/loadbalancers.go | 95 +++++++++++++++++-- pkg/compute/regiondrivers/kvm.go | 36 ++++++- pkg/compute/tasks/loadbalancer_delete_task.go | 3 +- pkg/mcclient/options/compute/loadbalancers.go | 6 ++ 6 files changed, 158 insertions(+), 8 deletions(-) diff --git a/pkg/apis/compute/loadbalancer.go b/pkg/apis/compute/loadbalancer.go index d10bd43b2c..310fc86a21 100644 --- a/pkg/apis/compute/loadbalancer.go +++ b/pkg/apis/compute/loadbalancer.go @@ -92,6 +92,12 @@ type LoadbalancerListInput struct { ChargeType []string `json:"charge_type"` // 套餐名称 LoadbalancerSpec []string `json:"loadbalancer_spec"` + + // filter for EIP + WithEip *bool `json:"with_eip"` + WithoutEip *bool `json:"without_eip"` + EipAssociable *bool `json:"eip_associable"` + UsableLoadbalancerForEip string `json:"usable_loadbalancer_for_eip"` } type LoadbalancerAgentListInput struct { @@ -253,6 +259,16 @@ type LoadbalancerCreateInput struct { // 套餐名称 LoadbalancerSpec string `json:"loadbalancer_spec"` + // 弹性公网IP带宽 + // 指定此参数后会创建新的弹性公网IP并绑定到新建的负载均衡 + EipBw int `json:"eip_bw,omitzero"` + // 弹性公网IP线路类型 + EipBgpType string `json:"eip_bgp_type,omitzero"` + // 弹性公网IP计费类型 + EipChargeType string `json:"eip_charge_type,omitempty"` + // 是否跟随主机删除而自动释放 + EipAutoDellocate bool `json:"eip_auto_dellocate,omitempty"` + // EIP ID Eip string `json:"eip"` diff --git a/pkg/compute/models/elasticips.go b/pkg/compute/models/elasticips.go index 73bb84c746..5312c11769 100644 --- a/pkg/compute/models/elasticips.go +++ b/pkg/compute/models/elasticips.go @@ -231,6 +231,16 @@ func (manager *SElasticipManager) ListItemFilter( q = q.Equals("cloudregion_id", lb.CloudregionId) if len(lb.ManagerId) > 0 { q = q.Equals("manager_id", lb.ManagerId) + } else { + zone, _ := lb.GetZone() + networks := NetworkManager.Query().SubQuery() + wires := WireManager.Query().SubQuery() + + sq := networks.Query(networks.Field("id")).Join(wires, sqlchemy.Equals(wires.Field("id"), networks.Field("wire_id"))). + Filter(sqlchemy.Equals(wires.Field("zone_id"), zone.Id)).SubQuery() + q = q.Filter(sqlchemy.In(q.Field("network_id"), sq)) + gns := LoadbalancernetworkManager.Query("network_id").Equals("loadbalancer_id", lb.Id).SubQuery() + q = q.Filter(sqlchemy.NotIn(q.Field("network_id"), gns)) } q = q.IsNullOrEmpty("associate_type") default: diff --git a/pkg/compute/models/loadbalancers.go b/pkg/compute/models/loadbalancers.go index f397a781e6..3733822b64 100644 --- a/pkg/compute/models/loadbalancers.go +++ b/pkg/compute/models/loadbalancers.go @@ -203,6 +203,79 @@ func (man *SLoadbalancerManager) ListItemFilter( q = q.Filter(sqlchemy.OR(c1, c2)) } + // eip filters + usableLbForEipFilter := query.UsableLoadbalancerForEip + if len(usableLbForEipFilter) > 0 { + eipObj, err := ElasticipManager.FetchByIdOrName(userCred, usableLbForEipFilter) + if err != nil { + if errors.Cause(err) == sql.ErrNoRows { + return nil, httperrors.NewResourceNotFoundError("eip %s not found", usableLbForEipFilter) + } + return nil, httperrors.NewGeneralError(err) + } + eip := eipObj.(*SElasticip) + + if len(eip.NetworkId) > 0 { + // kvm + sq := LoadbalancernetworkManager.Query("loadbalancer_id").Equals("network_id", eip.NetworkId).SubQuery() + q = q.NotIn("id", sq) + if cp := eip.GetCloudprovider(); cp == nil || cp.Provider == api.CLOUD_PROVIDER_ONECLOUD { + gnq := LoadbalancernetworkManager.Query().SubQuery() + nq := NetworkManager.Query().SubQuery() + wq := WireManager.Query().SubQuery() + vq := VpcManager.Query().SubQuery() + q.Join(gnq, sqlchemy.Equals(gnq.Field("loadbalancer_id"), q.Field("id"))) + q.Join(nq, sqlchemy.Equals(nq.Field("id"), gnq.Field("network_id"))) + q.Join(wq, sqlchemy.Equals(wq.Field("id"), nq.Field("wire_id"))) + q.Join(vq, sqlchemy.Equals(vq.Field("id"), wq.Field("vpc_id"))) + q.Filter(sqlchemy.IsNullOrEmpty(gnq.Field("eip_id"))) + q.Filter(sqlchemy.NotEquals(vq.Field("id"), api.DEFAULT_VPC_ID)) + // vpc provider thing will be handled ok below + } + } + + if eip.ManagerId != "" { + q = q.Equals("manager_id", eip.ManagerId) + } else { + q = q.IsNullOrEmpty("manager_id") + } + region, err := eip.GetRegion() + if err != nil { + return nil, httperrors.NewGeneralError(errors.Wrapf(err, "eip.GetRegion")) + } + q = q.Equals("cloudregion_id", region.Id) + } + + withEip := (query.WithEip != nil && *query.WithEip) + withoutEip := (query.WithoutEip != nil && *query.WithoutEip) || (query.EipAssociable != nil && *query.EipAssociable) + if withEip || withoutEip { + eips := ElasticipManager.Query().SubQuery() + sq := eips.Query(eips.Field("associate_id")).Equals("associate_type", api.EIP_ASSOCIATE_TYPE_LOADBALANCER) + sq = sq.IsNotNull("associate_id").IsNotEmpty("associate_id") + + if withEip { + q = q.In("id", sq) + } else if withoutEip { + q = q.NotIn("id", sq) + } + } + + if query.EipAssociable != nil { + sq1 := NetworkManager.Query("id") + sq2 := WireManager.Query().SubQuery() + sq3 := VpcManager.Query().SubQuery() + sq1 = sq1.Join(sq2, sqlchemy.Equals(sq1.Field("wire_id"), sq2.Field("id"))) + sq1 = sq1.Join(sq3, sqlchemy.Equals(sq2.Field("vpc_id"), sq3.Field("id"))) + cond1 := []string{api.VPC_EXTERNAL_ACCESS_MODE_EIP, api.VPC_EXTERNAL_ACCESS_MODE_EIP_DISTGW} + if *query.EipAssociable { + sq1 = sq1.Filter(sqlchemy.In(sq3.Field("external_access_mode"), cond1)) + } else { + sq1 = sq1.Filter(sqlchemy.NotIn(sq3.Field("external_access_mode"), cond1)) + } + sq := LoadbalancernetworkManager.Query("loadbalancer_id").In("network_id", sq1) + q = q.In("id", sq) + } + if len(query.AddressType) > 0 { q = q.In("address_type", query.AddressType) } @@ -359,6 +432,9 @@ func (man *SLoadbalancerManager) ValidateCreateData( quotaKeys := fetchRegionalQuotaKeys(rbacutils.ScopeProject, ownerId, region, cloudprovider) pendingUsage := SRegionQuota{Loadbalancer: 1} + if input.EipBw > 0 && len(input.Eip) == 0 { + pendingUsage.Eip = 1 + } pendingUsage.SetKeys(quotaKeys) if err := quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage); err != nil { return nil, httperrors.NewOutOfQuotaError("%s", err) @@ -565,11 +641,13 @@ func (lb *SLoadbalancer) StartLoadBalancerDeleteTask(ctx context.Context, userCr } func (lb *SLoadbalancer) StartLoadBalancerCreateTask(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict, parentTaskId string) error { - taskData := jsonutils.NewDict() - eipId, _ := data.GetString("eip_id") - if len(eipId) > 0 { - taskData.Set("eip_id", jsonutils.NewString(eipId)) // for huawei internet elb - } + taskData := data.CopyIncludes( + "eip_id", // for huawei internet elb + "eip_bw", + "eip_bgp_type", + "eip_charge_type", + "eip_auto_dellocate", + ) task, err := taskman.TaskManager.NewTask(ctx, "LoadbalancerCreateTask", lb, userCred, taskData, parentTaskId, "", nil) if err != nil { return err @@ -743,7 +821,12 @@ func (lb *SLoadbalancer) validatePurgeCondition(ctx context.Context) error { func (lb *SLoadbalancer) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error { lb.SetStatus(userCred, api.LB_STATUS_DELETING, "") - return lb.StartLoadBalancerDeleteTask(ctx, userCred, jsonutils.NewDict(), "") + params := jsonutils.NewDict() + deleteEip := jsonutils.QueryBoolean(data, "delete_eip", false) + if deleteEip { + params.Set("delete_eip", jsonutils.JSONTrue) + } + return lb.StartLoadBalancerDeleteTask(ctx, userCred, params, "") } func (lb *SLoadbalancer) GetLoadbalancerListeners() ([]SLoadbalancerListener, error) { diff --git a/pkg/compute/regiondrivers/kvm.go b/pkg/compute/regiondrivers/kvm.go index 57f0a438e8..9c49527395 100644 --- a/pkg/compute/regiondrivers/kvm.go +++ b/pkg/compute/regiondrivers/kvm.go @@ -32,6 +32,7 @@ import ( hostapi "yunion.io/x/onecloud/pkg/apis/host" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" + "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudcommon/validators" "yunion.io/x/onecloud/pkg/cloudprovider" @@ -797,7 +798,40 @@ func (self *SKVMRegionDriver) RequestCreateLoadbalancer(ctx context.Context, use } // bind eip eipId, _ := task.GetParams().GetString("eip_id") - if len(eipId) > 0 { + eipBw, _ := task.GetParams().Int("eip_bw") + if eipBw > 0 && len(eipId) == 0 { + // create eip first + bgpType, _ := task.GetParams().GetString("eip_bgp_type") + chargeType, _ := task.GetParams().GetString("eip_charge_type") + autoDellocate := jsonutils.QueryBoolean(task.GetParams(), "eip_auto_dellocate", false) + + eipPendingUsage := &models.SRegionQuota{Eip: 1} + eipPendingUsage.SetKeys(lb.GetQuotaKeys()) + eip, err := models.ElasticipManager.NewEipForVMOnHost(ctx, userCred, &models.NewEipForVMOnHostArgs{ + Bandwidth: int(eipBw), + BgpType: bgpType, + ChargeType: chargeType, + AutoDellocate: autoDellocate, + + Loadbalancer: lb, + PendingUsage: eipPendingUsage, + }) + if err != nil { + log.Errorf("NewEipForVMOnHost fail %s", err) + quotas.CancelPendingUsage(ctx, userCred, eipPendingUsage, eipPendingUsage, false) + } else { + opts := api.ElasticipAssociateInput{ + InstanceId: lb.Id, + InstanceExternalId: lb.ExternalId, + InstanceType: api.EIP_ASSOCIATE_TYPE_LOADBALANCER, + } + + err = eip.AllocateAndAssociateInstance(ctx, userCred, lb, opts, "") + if err != nil { + return nil, errors.Wrap(err, "AllocateAndAssociateInstance") + } + } + } else if len(eipId) > 0 { _eip, err := models.ElasticipManager.FetchById(eipId) if err != nil { return nil, errors.Wrapf(err, "ElasticipManager.FetchById(%s)", eipId) diff --git a/pkg/compute/tasks/loadbalancer_delete_task.go b/pkg/compute/tasks/loadbalancer_delete_task.go index 99a1fbb1e8..56f228c7a0 100644 --- a/pkg/compute/tasks/loadbalancer_delete_task.go +++ b/pkg/compute/tasks/loadbalancer_delete_task.go @@ -68,7 +68,8 @@ func (self *LoadbalancerDeleteTask) OnLoadbalancerDeleteComplete(ctx context.Con Obj: lb, Action: notifyclient.ActionDelete, }) - lb.DeleteEip(ctx, self.UserCred, false) + deleteEip := jsonutils.QueryBoolean(self.Params, "delete_eip", false) + lb.DeleteEip(ctx, self.UserCred, deleteEip) lb.LBPendingDelete(ctx, self.GetUserCred()) self.SetStageComplete(ctx, nil) } diff --git a/pkg/mcclient/options/compute/loadbalancers.go b/pkg/mcclient/options/compute/loadbalancers.go index d4cd66f8c3..47d2e9dbd0 100644 --- a/pkg/mcclient/options/compute/loadbalancers.go +++ b/pkg/mcclient/options/compute/loadbalancers.go @@ -38,6 +38,12 @@ type LoadbalancerCreateOptions struct { Cluster string `json:"cluster_id"` Manager string Tags []string `help:"Tags info,prefix with 'user:', eg: user:project=default" json:"-"` + + Eip string `json:"eip" help:"Id or name of EIP to associate with"` + EipBw int `json:"eip_bw"` + EipChargeType string `json:"eip_charge_type"` + EipBgpType string `json:"eip_bgp_type"` + EipAutoDellocate *bool `json:"eip_auto_dellocate"` } func (opts *LoadbalancerCreateOptions) Params() (jsonutils.JSONObject, error) {