diff --git a/pkg/apis/compute/elasticips_const.go b/pkg/apis/compute/elasticips_const.go index 2577bd3817..cd81470e8e 100644 --- a/pkg/apis/compute/elasticips_const.go +++ b/pkg/apis/compute/elasticips_const.go @@ -18,8 +18,9 @@ const ( EIP_MODE_INSTANCE_PUBLICIP = "public_ip" EIP_MODE_STANDALONE_EIP = "elastic_ip" - EIP_ASSOCIATE_TYPE_SERVER = "server" - EIP_ASSOCIATE_TYPE_NAT_GATEWAY = "natgateway" + EIP_ASSOCIATE_TYPE_SERVER = "server" + EIP_ASSOCIATE_TYPE_NAT_GATEWAY = "natgateway" + EIP_ASSOCIATE_TYPE_LOADBALANCER = "loadbalancer" EIP_STATUS_READY = "ready" EIP_STATUS_UNKNOWN = "unknown" diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 62d50768c4..8602c2a14b 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -460,6 +460,8 @@ type ICloudLoadbalancer interface { GetChargeType() string GetEgressMbps() int + GetIEIP() (ICloudEIP, error) + Delete() error Start() error diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index bca9a4b030..cf038c8cc0 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -725,6 +725,8 @@ func syncRegionLoadbalancers(ctx context.Context, userCred mcclient.TokenCredent lockman.LockObject(ctx, &localLbs[i]) defer lockman.ReleaseObject(ctx, &localLbs[i]) + syncLoadbalancerEip(ctx, userCred, provider, &localLbs[i], remoteLbs[i]) + syncLoadbalancerBackendgroups(ctx, userCred, syncResults, provider, &localLbs[i], remoteLbs[i], syncRange) syncLoadbalancerListeners(ctx, userCred, syncResults, provider, &localLbs[i], remoteLbs[i], syncRange) @@ -732,6 +734,21 @@ func syncRegionLoadbalancers(ctx context.Context, userCred mcclient.TokenCredent } } +func syncLoadbalancerEip(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, localLb *SLoadbalancer, remoteLb cloudprovider.ICloudLoadbalancer) { + eip, err := remoteLb.GetIEIP() + if err != nil { + msg := fmt.Sprintf("GetIEIP for Loadbalancer %s failed %s", remoteLb.GetName(), err) + log.Errorf(msg) + return + } + result := localLb.SyncLoadbalancerEip(ctx, userCred, provider, eip) + msg := result.Result() + log.Infof("SyncEip for Loadbalancer %s result: %s", localLb.Name, msg) + if result.IsError() { + return + } +} + func syncLoadbalancerListeners(ctx context.Context, userCred mcclient.TokenCredential, syncResults SSyncResultSet, provider *SCloudprovider, localLoadbalancer *SLoadbalancer, remoteLoadbalancer cloudprovider.ICloudLoadbalancer, syncRange *SSyncRange) { remoteListeners, err := remoteLoadbalancer.GetILoadBalancerListeners() if err != nil { diff --git a/pkg/compute/models/elasticips.go b/pkg/compute/models/elasticips.go index f66f9b358a..9e948578fd 100644 --- a/pkg/compute/models/elasticips.go +++ b/pkg/compute/models/elasticips.go @@ -503,6 +503,21 @@ func (self *SElasticip) GetAssociateVM() *SGuest { return nil } +func (self *SElasticip) GetAssociateLoadbalancer() *SLoadbalancer { + if self.AssociateType == api.EIP_ASSOCIATE_TYPE_LOADBALANCER && len(self.AssociateId) > 0 { + _lb, err := LoadbalancerManager.FetchById(self.AssociateId) + if err != nil { + return nil + } + lb := _lb.(*SLoadbalancer) + if lb.PendingDeleted { + return nil + } + return lb + } + return nil +} + func (self *SElasticip) GetAssociateNatGateway() *SNatGateway { if self.AssociateType == api.EIP_ASSOCIATE_TYPE_NAT_GATEWAY && len(self.AssociateId) > 0 { natGateway, err := NatGatewayManager.FetchById(self.AssociateId) @@ -520,6 +535,7 @@ func (self *SElasticip) Dissociate(ctx context.Context, userCred mcclient.TokenC } var vm *SGuest var nat *SNatGateway + var lb *SLoadbalancer switch self.AssociateType { case api.EIP_ASSOCIATE_TYPE_SERVER: vm = self.GetAssociateVM() @@ -531,6 +547,11 @@ func (self *SElasticip) Dissociate(ctx context.Context, userCred mcclient.TokenC if nat == nil { log.Errorf("dissociate Nat gateway not exists???") } + case api.EIP_ASSOCIATE_TYPE_LOADBALANCER: + lb = self.GetAssociateLoadbalancer() + if lb == nil { + log.Errorf("dissociate loadbalancer not exists???") + } } _, err := db.Update(self, func() error { @@ -553,12 +574,41 @@ func (self *SElasticip) Dissociate(ctx context.Context, userCred mcclient.TokenC db.OpsLog.LogEvent(nat, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred) } + if lb != nil { + db.OpsLog.LogDetachEvent(ctx, lb, self, userCred, self.GetShortDesc(ctx)) + db.OpsLog.LogEvent(self, db.ACT_EIP_DETACH, lb.GetShortDesc(ctx), userCred) + db.OpsLog.LogEvent(lb, db.ACT_EIP_DETACH, self.GetShortDesc(ctx), userCred) + } + if self.Mode == api.EIP_MODE_INSTANCE_PUBLICIP { self.Delete(ctx, userCred) } return nil } +func (self *SElasticip) AssociateLoadbalancer(ctx context.Context, userCred mcclient.TokenCredential, lb *SLoadbalancer) error { + if lb.PendingDeleted { + return fmt.Errorf("loadbalancer is deleted") + } + if len(self.AssociateType) > 0 { + return fmt.Errorf("EIP has been associated!!") + } + _, err := db.Update(self, func() error { + self.AssociateType = api.EIP_ASSOCIATE_TYPE_LOADBALANCER + self.AssociateId = lb.Id + return nil + }) + if err != nil { + return err + } + + db.OpsLog.LogAttachEvent(ctx, lb, self, userCred, self.GetShortDesc(ctx)) + db.OpsLog.LogEvent(self, db.ACT_EIP_ATTACH, lb.GetShortDesc(ctx), userCred) + db.OpsLog.LogEvent(lb, db.ACT_EIP_ATTACH, self.GetShortDesc(ctx), userCred) + + return nil +} + func (self *SElasticip) AssociateVM(ctx context.Context, userCred mcclient.TokenCredential, vm *SGuest) error { if vm.PendingDeleted || vm.Deleted { return fmt.Errorf("vm is deleted") diff --git a/pkg/compute/models/loadbalancers.go b/pkg/compute/models/loadbalancers.go index 3b8a5399df..945c7a5522 100644 --- a/pkg/compute/models/loadbalancers.go +++ b/pkg/compute/models/loadbalancers.go @@ -485,6 +485,12 @@ func (lb *SLoadbalancer) GetCustomizeColumns(ctx context.Context, userCred mccli } } + eip, _ := lb.GetEip() + if eip != nil { + extra.Set("eip", jsonutils.NewString(eip.IpAddr)) + extra.Set("eip_mode", jsonutils.NewString(eip.Mode)) + } + if lb.BackendGroupId != "" { lbbg, err := LoadbalancerBackendGroupManager.FetchById(lb.BackendGroupId) if err != nil { @@ -710,6 +716,105 @@ func (lb *SLoadbalancer) syncLoadbalancerNetwork(ctx context.Context, userCred m } } +func (self *SLoadbalancer) DeleteEip(ctx context.Context, userCred mcclient.TokenCredential) error { + eip, err := self.GetEip() + if err != nil { + log.Errorf("Delete eip fail for get Eip %s", err) + return err + } + if eip == nil { + return nil + } + if eip.Mode == api.EIP_MODE_INSTANCE_PUBLICIP { + err = eip.RealDelete(ctx, userCred) + if err != nil { + log.Errorf("Delete eip on delete server fail %s", err) + return err + } + } else { + err = eip.Dissociate(ctx, userCred) + if err != nil { + log.Errorf("Dissociate eip on delete server fail %s", err) + return err + } + } + return nil +} + +func (self *SLoadbalancer) GetEip() (*SElasticip, error) { + return ElasticipManager.getEipForInstance(api.EIP_ASSOCIATE_TYPE_LOADBALANCER, self.Id) +} + +func (self *SLoadbalancer) SyncLoadbalancerEip(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, extEip cloudprovider.ICloudEIP) compare.SyncResult { + result := compare.SyncResult{} + + eip, err := self.GetEip() + if err != nil { + result.Error(fmt.Errorf("getEip error %s", err)) + return result + } + + if eip == nil && extEip == nil { + // do nothing + } else if eip == nil && extEip != nil { + // add + neip, err := ElasticipManager.getEipByExtEip(ctx, userCred, extEip, provider, self.GetRegion(), provider.GetOwnerId()) + if err != nil { + log.Errorf("getEipByExtEip error %v", err) + result.AddError(err) + } else { + err = neip.AssociateLoadbalancer(ctx, userCred, self) + if err != nil { + log.Errorf("AssociateVM error %v", err) + result.AddError(err) + } else { + result.Add() + } + } + } else if eip != nil && extEip == nil { + // remove + err = eip.Dissociate(ctx, userCred) + if err != nil { + result.DeleteError(err) + } else { + result.Delete() + } + } else { + // sync + if eip.IpAddr != extEip.GetIpAddr() { + // remove then add + err = eip.Dissociate(ctx, userCred) + if err != nil { + // fail to remove + result.DeleteError(err) + } else { + result.Delete() + neip, err := ElasticipManager.getEipByExtEip(ctx, userCred, extEip, provider, self.GetRegion(), provider.GetOwnerId()) + if err != nil { + result.AddError(err) + } else { + err = neip.AssociateLoadbalancer(ctx, userCred, self) + if err != nil { + result.AddError(err) + } else { + result.Add() + } + } + } + } else { + // do nothing + err := eip.SyncWithCloudEip(ctx, userCred, provider, extEip, provider.GetOwnerId()) + if err != nil { + result.UpdateError(err) + } else { + result.Update() + } + } + } + + return result +} + func (lb *SLoadbalancer) SyncWithCloudLoadbalancer(ctx context.Context, userCred mcclient.TokenCredential, extLb cloudprovider.ICloudLoadbalancer, syncOwnerId mcclient.IIdentityProvider) error { lockman.LockObject(ctx, lb) defer lockman.ReleaseObject(ctx, lb) diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index 5007153de3..e4d0b07260 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -295,6 +295,11 @@ func (lb *SLoadbalancer) purge(ctx context.Context, userCred mcclient.TokenCrede return err } + err = lb.DeleteEip(ctx, userCred) + if err != nil { + return err + } + err = lb.purgeBackendGroups(ctx, userCred) if err != nil { return err diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index 6934eb87c0..171deb9af8 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -22,6 +22,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" @@ -176,6 +177,15 @@ func (self *SManagedVirtualizationRegionDriver) RequestCreateLoadbalancer(ctx co if err := lb.SyncWithCloudLoadbalancer(ctx, userCred, iLoadbalancer, nil); 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 diff --git a/pkg/compute/tasks/loadbalancer_delete_task.go b/pkg/compute/tasks/loadbalancer_delete_task.go index b76566c621..45046b4184 100644 --- a/pkg/compute/tasks/loadbalancer_delete_task.go +++ b/pkg/compute/tasks/loadbalancer_delete_task.go @@ -60,6 +60,7 @@ func (self *LoadbalancerDeleteTask) OnInit(ctx context.Context, obj db.IStandalo func (self *LoadbalancerDeleteTask) OnLoadbalancerDeleteComplete(ctx context.Context, lb *models.SLoadbalancer, data jsonutils.JSONObject) { db.OpsLog.LogEvent(lb, db.ACT_DELETE, lb.GetShortDesc(ctx), self.UserCred) logclient.AddActionLogWithStartable(self, lb, logclient.ACT_DELOCATE, nil, self.UserCred, true) + lb.DeleteEip(ctx, self.UserCred) lb.LBPendingDelete(ctx, self.GetUserCred()) self.SetStageComplete(ctx, nil) } diff --git a/pkg/multicloud/loadbalancer_base.go b/pkg/multicloud/loadbalancer_base.go new file mode 100644 index 0000000000..c152055c3d --- /dev/null +++ b/pkg/multicloud/loadbalancer_base.go @@ -0,0 +1,27 @@ +// 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 multicloud + +import ( + "yunion.io/x/onecloud/pkg/cloudprovider" +) + +type SLoadbalancerBase struct { + SResourceBase +} + +func (lb *SLoadbalancerBase) GetIEIP() (cloudprovider.ICloudEIP, error) { + return nil, nil +} diff --git a/pkg/util/aliyun/loadbalancer.go b/pkg/util/aliyun/loadbalancer.go index 3b6c128ee3..fb51560e87 100644 --- a/pkg/util/aliyun/loadbalancer.go +++ b/pkg/util/aliyun/loadbalancer.go @@ -17,6 +17,10 @@ package aliyun import ( "fmt" "strings" + "time" + + "yunion.io/x/onecloud/pkg/multicloud" + "yunion.io/x/pkg/errors" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -53,6 +57,7 @@ type BackendServers struct { } type SLoadbalancer struct { + multicloud.SLoadbalancerBase region *SRegion LoadBalancerId string //负载均衡实例ID。 @@ -68,14 +73,14 @@ type SLoadbalancer struct { ListenerPorts ListenerPorts ListenerPortsAndProtocol ListenerPortsAndProtocol BackendServers BackendServers - CreateTime string //负载均衡实例的创建时间。 - MasterZoneId string //实例的主可用区ID。 - SlaveZoneId string //实例的备可用区ID。 - InternetChargeType string //公网实例的计费方式。取值:paybybandwidth:按带宽计费 paybytraffic:按流量计费(默认值) 说明 当 PayType参数的值为PrePay时,只支持按带宽计费。 - PayType string //实例的计费类型,取值:PayOnDemand:按量付费 PrePay:预付费 - ResourceGroupId string //企业资源组ID。 - LoadBalancerSpec string //负载均衡实例的的性能规格 - Bandwidth int //按带宽计费的公网型实例的带宽峰值 + CreateTime time.Time //负载均衡实例的创建时间。 + MasterZoneId string //实例的主可用区ID。 + SlaveZoneId string //实例的备可用区ID。 + InternetChargeType TInternetChargeType //公网实例的计费方式。取值:paybybandwidth:按带宽计费 paybytraffic:按流量计费(默认值) 说明 当 PayType参数的值为PrePay时,只支持按带宽计费。 + PayType string //实例的计费类型,取值:PayOnDemand:按量付费 PrePay:预付费 + ResourceGroupId string //企业资源组ID。 + LoadBalancerSpec string //负载均衡实例的的性能规格 + Bandwidth int //按带宽计费的公网型实例的带宽峰值 } func (lb *SLoadbalancer) GetName() string { @@ -259,6 +264,10 @@ func (lb *SLoadbalancer) GetChargeType() string { return "unknown" } +func (lb *SLoadbalancer) GetCreatedAt() time.Time { + return lb.CreateTime +} + func (lb *SLoadbalancer) GetEgressMbps() int { if lb.Bandwidth < 1 { return 0 @@ -279,6 +288,32 @@ func (lb *SLoadbalancer) GetILoadBalancerBackendGroupById(groupId string) (cloud return nil, cloudprovider.ErrNotFound } +func (lb *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) { + if lb.AddressType == "internet" { + eip := SEipAddress{ + region: lb.region, + IpAddress: lb.Address, + InstanceId: lb.GetGlobalId(), + InstanceType: EIP_INTANNCE_TYPE_SLB, + Status: EIP_STATUS_INUSE, + AllocationId: lb.GetGlobalId(), + AllocationTime: lb.CreateTime, + Bandwidth: lb.Bandwidth, + InternetChargeType: lb.InternetChargeType, + } + return &eip, nil + } + eips, total, err := lb.region.GetEips("", lb.LoadBalancerId, 0, 1) + if err != nil { + return nil, errors.Wrapf(err, "lb.region.GetEips(%s)", lb.LoadBalancerId) + } + if total != 1 { + return nil, cloudprovider.ErrNotFound + } + eips[0].region = lb.region + return &eips[0], nil +} + func (region *SRegion) loadbalancerOperation(loadbalancerId, status string) error { params := map[string]string{} params["RegionId"] = region.RegionId diff --git a/pkg/util/qcloud/loadbalancer.go b/pkg/util/qcloud/loadbalancer.go index cb32097251..77409f589d 100644 --- a/pkg/util/qcloud/loadbalancer.go +++ b/pkg/util/qcloud/loadbalancer.go @@ -25,6 +25,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/multicloud" ) const ( @@ -47,6 +48,7 @@ todo: // https://cloud.tencent.com/document/api/214/30694#LoadBalancer type SLoadbalancer struct { + multicloud.SLoadbalancerBase region *SRegion Status int64 `json:"Status"` // 0:创建中,1:正常运行 @@ -343,6 +345,20 @@ func (self *SLoadbalancer) GetILoadBalancerBackendGroups() ([]cloudprovider.IClo return ibgs, nil } +func (self *SLoadbalancer) GetIEIP() (cloudprovider.ICloudEIP, error) { + if self.LoadBalancerType == "OPEN" && len(self.LoadBalancerVips) > 0 { + return &SEipAddress{ + region: self.region, + AddressId: self.LoadBalancerID, + AddressIp: self.LoadBalancerVips[0], + AddressType: EIP_STATUS_BIND, + InstanceId: self.LoadBalancerID, + CreatedTime: self.CreateTime, + }, nil + } + return nil, nil +} + func (self *SRegion) GetLoadbalancers(ids []string) ([]SLoadbalancer, error) { params := map[string]string{} for i, id := range ids {