From 54309f95f306decc1712e344b6a93a70c21f1fd7 Mon Sep 17 00:00:00 2001 From: Jian Qiu Date: Thu, 16 Apr 2026 15:15:43 +0800 Subject: [PATCH] feature: support asymmmetric bandwidth limit (#24679) Co-authored-by: Qiu Jian --- cmd/climc/shell/compute/servernetworks.go | 7 +- cmd/climc/shell/compute/servers.go | 2 + pkg/apis/compute/api.go | 6 + pkg/apis/compute/elasticip.go | 25 ++++ pkg/apis/compute/guestnetwork.go | 2 + pkg/apis/compute/guests.go | 3 + pkg/apis/compute/host_tap.go | 2 + pkg/cloudcommon/cmdline/parser.go | 12 ++ pkg/compute/guestdrivers/baremetals.go | 2 + .../guestdrivers/cloudpods-baremetals.go | 2 + pkg/compute/guestdrivers/virtualization.go | 2 + pkg/compute/models/elasticips.go | 67 ++++++++--- pkg/compute/models/guest_actions.go | 23 +++- pkg/compute/models/guestnetworks.go | 44 +++++++ pkg/compute/models/guests.go | 23 +++- pkg/compute/models/host_taps.go | 4 + pkg/compute/models/hosts.go | 7 ++ .../tasks/eip/eip_change_bandwidth_task.go | 12 +- .../tasks/guest/guest_batch_create_task.go | 4 +- pkg/hostman/hostinfo/hostpinger/hostpinger.go | 12 ++ pkg/mcclient/options/compute/servers.go | 6 + pkg/vpcagent/ovn/keeper.go | 110 +++++++++++------- 22 files changed, 300 insertions(+), 77 deletions(-) diff --git a/cmd/climc/shell/compute/servernetworks.go b/cmd/climc/shell/compute/servernetworks.go index 664dbc7441..7ea46af623 100644 --- a/cmd/climc/shell/compute/servernetworks.go +++ b/cmd/climc/shell/compute/servernetworks.go @@ -144,7 +144,10 @@ func init() { type ServerNetworkBWOptions struct { SERVER string `help:"ID or Name of server"` MACORIP string `help:"IP, Mac, or Index of NIC"` - BW int64 `help:"Bandwidth in Mbps"` + + BW int64 `help:"Bandwidth in Mbps"` + Tx int64 `help:"Tx bandwidth in Mbps"` + Rx int64 `help:"Rx bandwidth in Mbps"` } R(&ServerNetworkBWOptions{}, "server-change-bandwidth", "Change server network bandwidth in Mbps", func(s *mcclient.ClientSession, args *ServerNetworkBWOptions) error { params := jsonutils.NewDict() @@ -162,6 +165,8 @@ func init() { return fmt.Errorf("Please specify Ip or Mac") } params.Add(jsonutils.NewInt(args.BW), "bandwidth") + params.Add(jsonutils.NewInt(args.Tx), "tx_bw_limit") + params.Add(jsonutils.NewInt(args.Rx), "rx_bw_limit") server, err := modules.Servers.PerformAction(s, args.SERVER, "change-bandwidth", params) if err != nil { return err diff --git a/cmd/climc/shell/compute/servers.go b/cmd/climc/shell/compute/servers.go index 6f79b92c60..fd1496e30a 100644 --- a/cmd/climc/shell/compute/servers.go +++ b/cmd/climc/shell/compute/servers.go @@ -221,6 +221,8 @@ func init() { params.Eip = opts.Eip params.EipChargeType = billing_api.ParseNetChargeType(opts.EipChargeType) params.EipBw = opts.EipBw + params.EipTxBw = opts.EipTxBw + params.EipRxBw = opts.EipRxBw server, err := modules.Servers.Create(s, params.JSON(params)) if err != nil { diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index 8a4adb9cae..942a1dcc84 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -99,6 +99,8 @@ type NetworkConfig struct { // 若指定镜像的网络驱动方式,此参数会被覆盖 Driver string `json:"driver"` BwLimit int `json:"bw_limit"` + TxBwLimit int `json:"tx_bw_limit"` + RxBwLimit int `json:"rx_bw_limit"` Vip bool `json:"vip"` Reserved bool `json:"reserved"` NumQueues int `json:"num_queues"` @@ -644,6 +646,10 @@ type ServerCreateInput struct { // 指定此参数后会创建新的弹性公网IP并绑定到新建的虚拟机 // 此参数优先级低于public_ip EipBw int `json:"eip_bw,omitzero"` + // 弹性公网IP上行带宽 + EipTxBw int `json:"eip_tx_bw,omitzero"` + // 弹性公网IP下行带宽 + EipRxBw int `json:"eip_rx_bw,omitzero"` // 弹性公网IP线路类型 EipBgpType string `json:"eip_bgp_type,omitzero"` // 弹性公网IP计费类型 diff --git a/pkg/apis/compute/elasticip.go b/pkg/apis/compute/elasticip.go index bb1c08cfdb..ce2311a97d 100644 --- a/pkg/apis/compute/elasticip.go +++ b/pkg/apis/compute/elasticip.go @@ -15,6 +15,8 @@ package compute import ( + "errors" + "yunion.io/x/onecloud/pkg/apis" billing_api "yunion.io/x/onecloud/pkg/apis/billing" ) @@ -152,3 +154,26 @@ type ElasticipRemoteUpdateInput struct { // 是否覆盖替换所有标签 ReplaceTags *bool `json:"replace_tags" help:"replace all remote tags"` } + +type ElasticipChangeBandwidthInput struct { + // 带宽限制,单位mbps + // swagger:ignore + // Deprecated + Bandwidth int64 `json:"bandwidth" yunion-deprecated-by:"bandwidth_mb"` + // 带宽限制,单位mbps + BandwidthMb int64 `json:"bandwidth_mb"` + // 下行带宽限制,单位mbps + RxBwLimitMb int64 `json:"rx_bw_limit_mb"` + // 上行带宽限制,单位mbps + TxBwLimitMb int64 `json:"tx_bw_limit_mb"` +} + +func (input ElasticipChangeBandwidthInput) Validate() error { + if input.BandwidthMb <= 0 && input.RxBwLimitMb <= 0 { + return errors.New("bandwidth_mb or rx_bw_limit_mb must be greater than 0") + } + if input.BandwidthMb <= 0 && input.TxBwLimitMb <= 0 { + return errors.New("bandwidth_mb or tx_bw_limit_mb must be greater than 0") + } + return nil +} diff --git a/pkg/apis/compute/guestnetwork.go b/pkg/apis/compute/guestnetwork.go index e089eae87f..c748d25e0c 100644 --- a/pkg/apis/compute/guestnetwork.go +++ b/pkg/apis/compute/guestnetwork.go @@ -147,6 +147,8 @@ type GuestnetworkBaseDesc struct { Masklen int8 `json:"masklen"` Vlan int `json:"vlan"` Bw int `json:"bw"` + RxBwLimit int `json:"rx_bw_limit"` + TxBwLimit int `json:"tx_bw_limit"` Mtu int16 `json:"mtu"` Index int `json:"index"` RxTrafficLimit int64 `json:"rx_traffic_limit"` diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index 6bdd824439..71262d81eb 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -1463,6 +1463,9 @@ type ServerChangeBandwidthInput struct { Bandwidth int `json:"bandwidth"` + TxBwLimit int `json:"tx_bw_limit"` + RxBwLimit int `json:"rx_bw_limit"` + NoSync *bool `json:"no_sync"` } diff --git a/pkg/apis/compute/host_tap.go b/pkg/apis/compute/host_tap.go index ffa889bc2e..6de121db74 100644 --- a/pkg/apis/compute/host_tap.go +++ b/pkg/apis/compute/host_tap.go @@ -15,6 +15,8 @@ package compute const ( + TapConfigFileName = "tap-config.json" + HostVpcBridge = "__vpc_bridge__" HostTapBridge = "__tap_bridge__" HostLocalBridge = "brlocal" diff --git a/pkg/cloudcommon/cmdline/parser.go b/pkg/cloudcommon/cmdline/parser.go index ce64592903..7bc27ef6f5 100644 --- a/pkg/cloudcommon/cmdline/parser.go +++ b/pkg/cloudcommon/cmdline/parser.go @@ -376,6 +376,18 @@ func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) { return nil, err } netConfig.BwLimit = bw + } else if strings.HasPrefix(p, "rx-bw=") { + bw, err := fileutils.GetSizeMb(p[len("rx-bw="):], 'M', 1000) + if err != nil { + return nil, err + } + netConfig.RxBwLimit = bw + } else if strings.HasPrefix(p, "tx-bw=") { + bw, err := fileutils.GetSizeMb(p[len("tx-bw="):], 'M', 1000) + if err != nil { + return nil, err + } + netConfig.TxBwLimit = bw } else if p == "vip" { netConfig.Vip = true } else if strings.HasPrefix(p, "sriov-nic-id=") { diff --git a/pkg/compute/guestdrivers/baremetals.go b/pkg/compute/guestdrivers/baremetals.go index 504f37bbc0..648d972a88 100644 --- a/pkg/compute/guestdrivers/baremetals.go +++ b/pkg/compute/guestdrivers/baremetals.go @@ -265,6 +265,8 @@ func (self *SBaremetalGuestDriver) Attach2RandomNetwork(guest *models.SGuest, ct Ip6Addr: address6, NicDriver: netConfig.Driver, BwLimit: netConfig.BwLimit, + RxBwLimit: netConfig.RxBwLimit, + TxBwLimit: netConfig.TxBwLimit, Virtual: netConfig.Vip, TryReserved: false, AllocDir: api.IPAllocationStepup, diff --git a/pkg/compute/guestdrivers/cloudpods-baremetals.go b/pkg/compute/guestdrivers/cloudpods-baremetals.go index b717ffd2ba..bcd9828cf7 100644 --- a/pkg/compute/guestdrivers/cloudpods-baremetals.go +++ b/pkg/compute/guestdrivers/cloudpods-baremetals.go @@ -265,6 +265,8 @@ func (self *SCloudpodsBaremetalGuestDriver) Attach2RandomNetwork(guest *models.S Ip6Addr: address6, NicDriver: netConfig.Driver, BwLimit: netConfig.BwLimit, + RxBwLimit: netConfig.RxBwLimit, + TxBwLimit: netConfig.TxBwLimit, Virtual: netConfig.Vip, TryReserved: false, AllocDir: api.IPAllocationStepup, diff --git a/pkg/compute/guestdrivers/virtualization.go b/pkg/compute/guestdrivers/virtualization.go index 902e5aeaa7..1d41ee7879 100644 --- a/pkg/compute/guestdrivers/virtualization.go +++ b/pkg/compute/guestdrivers/virtualization.go @@ -204,6 +204,8 @@ func (self *SVirtualizedGuestDriver) Attach2RandomNetwork(guest *models.SGuest, Ip6Addr: netConfig.Address6, NicDriver: netConfig.Driver, BwLimit: netConfig.BwLimit, + RxBwLimit: netConfig.RxBwLimit, + TxBwLimit: netConfig.TxBwLimit, Virtual: netConfig.Vip, TryReserved: netConfig.Reserved, AllocDir: api.IPAllocationDefault, diff --git a/pkg/compute/models/elasticips.go b/pkg/compute/models/elasticips.go index 3550ba0421..b8189286d3 100644 --- a/pkg/compute/models/elasticips.go +++ b/pkg/compute/models/elasticips.go @@ -47,6 +47,7 @@ import ( "yunion.io/x/onecloud/pkg/compute/options" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/logclient" "yunion.io/x/onecloud/pkg/util/rbacutils" "yunion.io/x/onecloud/pkg/util/stringutils2" ) @@ -118,6 +119,11 @@ type SElasticip struct { // 区域Id // CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"` + + // 下行带宽限制,单位mbps + RxBwLimit int `nullable:"false" default:"0" list:"user"` + // 上行带宽限制,单位mbps + TxBwLimit int `nullable:"false" default:"0" list:"user"` } // 弹性公网IP列表 @@ -397,6 +403,8 @@ func (self *SElasticip) GetShortDesc(ctx context.Context) *jsonutils.JSONDict { // desc.Add(jsonutils.NewString(self.ChargeType), "charge_type") desc.Add(jsonutils.NewInt(int64(self.Bandwidth)), "bandwidth") + desc.Add(jsonutils.NewInt(int64(self.RxBwLimit)), "rx_bw_limit") + desc.Add(jsonutils.NewInt(int64(self.TxBwLimit)), "tx_bw_limit") desc.Add(jsonutils.NewString(self.Mode), "mode") desc.Add(jsonutils.NewString(self.IpAddr), "ip_addr") desc.Add(jsonutils.NewString(self.BgpType), "bgp_type") @@ -599,6 +607,8 @@ func (self *SElasticip) SyncWithCloudEip(ctx context.Context, userCred mcclient. } if bandwidth := ext.GetBandwidth(); bandwidth != 0 { self.Bandwidth = bandwidth + self.RxBwLimit = bandwidth + self.TxBwLimit = bandwidth } self.IpAddr = ext.GetIpAddr() self.Mode = ext.GetMode() @@ -682,6 +692,8 @@ func (manager *SElasticipManager) newFromCloudEip(ctx context.Context, userCred eip.ChargeType = billing_api.NET_CHARGE_TYPE_BY_TRAFFIC } eip.Bandwidth = extEip.GetBandwidth() + eip.RxBwLimit = extEip.GetBandwidth() + eip.TxBwLimit = extEip.GetBandwidth() if networkId := extEip.GetINetworkId(); len(networkId) > 0 { network, err := db.FetchByExternalIdAndManagerId(NetworkManager, networkId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery { wire := WireManager.Query().SubQuery() @@ -1625,6 +1637,8 @@ func (a SEipNetworks) Less(i, j int) bool { type NewEipForVMOnHostArgs struct { Bandwidth int + RxBwLimit int + TxBwLimit int BgpType string ChargeType billing_api.TNetChargeType AutoDellocate bool @@ -1640,7 +1654,6 @@ type NewEipForVMOnHostArgs struct { func (manager *SElasticipManager) NewEipForVMOnHost(ctx context.Context, userCred mcclient.TokenCredential, args *NewEipForVMOnHostArgs) (*SElasticip, error) { var ( - bw = args.Bandwidth bgpType = args.BgpType chargeType = args.ChargeType autoDellocate = args.AutoDellocate @@ -1682,7 +1695,10 @@ func (manager *SElasticipManager) NewEipForVMOnHost(ctx context.Context, userCre eip.Mode = api.EIP_MODE_STANDALONE_EIP // do not implicitly auto dellocate EIP, should be set by user explicitly // eip.AutoDellocate = tristate.True - eip.Bandwidth = bw + eip.Bandwidth = args.Bandwidth + eip.RxBwLimit = args.RxBwLimit + eip.TxBwLimit = args.TxBwLimit + eip.ChargeType = billing_api.TNetChargeType(chargeType) eip.BgpType = args.BgpType eip.AutoDellocate = tristate.NewFromBool(autoDellocate) @@ -1845,14 +1861,13 @@ func (eip *SElasticip) AllocateAndAssociateInstance(ctx context.Context, userCre return eip.startEipAllocateTask(ctx, userCred, params, parentTaskId) } -func (self *SElasticip) PerformChangeBandwidth(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - if self.Status != api.EIP_STATUS_READY { - return nil, httperrors.NewInvalidStatusError("cannot change bandwidth in status %s", self.Status) +func (self *SElasticip) PerformChangeBandwidth(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ElasticipChangeBandwidthInput) (jsonutils.JSONObject, error) { + if err := input.Validate(); err != nil { + return nil, httperrors.NewInputParameterError("%v", err) } - bandwidth, err := data.Int("bandwidth") - if err != nil || bandwidth <= 0 { - return nil, httperrors.NewInputParameterError("Invalid bandwidth") + if self.Status != api.EIP_STATUS_READY { + return nil, httperrors.NewInvalidStatusError("cannot change bandwidth in status %s", self.Status) } if self.IsManaged() { @@ -1861,24 +1876,23 @@ func (self *SElasticip) PerformChangeBandwidth(ctx context.Context, userCred mcc return nil, err } - if err := factory.ValidateChangeBandwidth(self.AssociateId, bandwidth); err != nil { + if err := factory.ValidateChangeBandwidth(self.AssociateId, input.BandwidthMb); err != nil { return nil, httperrors.NewInputParameterError("%v", err) } } - err = self.StartEipChangeBandwidthTask(ctx, userCred, bandwidth) + err := self.StartEipChangeBandwidthTask(ctx, userCred, input) if err != nil { return nil, httperrors.NewGeneralError(err) } return nil, nil } -func (self *SElasticip) StartEipChangeBandwidthTask(ctx context.Context, userCred mcclient.TokenCredential, bandwidth int64) error { +func (self *SElasticip) StartEipChangeBandwidthTask(ctx context.Context, userCred mcclient.TokenCredential, input api.ElasticipChangeBandwidthInput) error { self.SetStatus(ctx, userCred, api.EIP_STATUS_CHANGE_BANDWIDTH, "change bandwidth") - params := jsonutils.NewDict() - params.Add(jsonutils.NewInt(bandwidth), "bandwidth") + params := jsonutils.Marshal(input).(*jsonutils.JSONDict) task, err := taskman.TaskManager.NewTask(ctx, "EipChangeBandwidthTask", self, userCred, params, "", "", nil) if err != nil { @@ -1889,24 +1903,35 @@ func (self *SElasticip) StartEipChangeBandwidthTask(ctx context.Context, userCre return nil } -func (self *SElasticip) DoChangeBandwidth(ctx context.Context, userCred mcclient.TokenCredential, bandwidth int) error { +func (self *SElasticip) DoChangeBandwidth(ctx context.Context, userCred mcclient.TokenCredential, input api.ElasticipChangeBandwidthInput) error { changes := jsonutils.NewDict() + obw := api.ElasticipChangeBandwidthInput{ + BandwidthMb: int64(self.Bandwidth), + RxBwLimitMb: int64(self.RxBwLimit), + TxBwLimitMb: int64(self.TxBwLimit), + } changes.Add(jsonutils.NewInt(int64(self.Bandwidth)), "obw") + changes.Add(jsonutils.Marshal(obw), "obw_details") _, err := db.Update(self, func() error { - self.Bandwidth = bandwidth + self.Bandwidth = int(input.BandwidthMb) + self.RxBwLimit = int(input.RxBwLimitMb) + self.TxBwLimit = int(input.TxBwLimitMb) return nil }) - - self.SetStatus(ctx, userCred, api.EIP_STATUS_READY, "finish change bandwidth") - if err != nil { + self.SetStatus(ctx, userCred, api.EIP_STATUS_READY, "change bandwidth failed") + logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CHANGE_BANDWIDTH, changes, userCred, false) log.Errorf("DoChangeBandwidth update fail %s", err) return err } - changes.Add(jsonutils.NewInt(int64(bandwidth)), "nbw") + self.SetStatus(ctx, userCred, api.EIP_STATUS_READY, "finish change bandwidth") + + changes.Add(jsonutils.NewInt(int64(input.BandwidthMb)), "nbw") + changes.Add(jsonutils.Marshal(input), "nbw_details") db.OpsLog.LogEvent(self, db.ACT_CHANGE_BANDWIDTH, changes, userCred) + logclient.AddActionLogWithContext(ctx, self, logclient.ACT_CHANGE_BANDWIDTH, changes, userCred, true) return nil } @@ -1916,6 +1941,8 @@ type EipUsage struct { PublicIpBandwidth int EIPCount int EipBandwidth int + EipRxBwLimit int + EipTxBwLimit int EIPUsedCount int } @@ -1962,6 +1989,8 @@ func (manager *SElasticipManager) TotalCount( q2 := q2sq.Query( sqlchemy.COUNT("eip_count", q2sq.Field("id")), sqlchemy.SUM("eip_bandwidth", q2sq.Field("bandwidth")), + sqlchemy.SUM("eip_rx_bw_limit", q2sq.Field("rx_bw_limit")), + sqlchemy.SUM("eip_tx_bw_limit", q2sq.Field("tx_bw_limit")), ).Equals("mode", api.EIP_MODE_STANDALONE_EIP) q2 = manager.usageQ(ctx, scope, ownerId, q2, rangeObjs, providers, brands, cloudEnv, policyResult) q3sq := manager.Query().SubQuery() diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 731c7e618b..037a953281 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -781,6 +781,8 @@ func (self *SGuest) PerformClone(ctx context.Context, userCred mcclient.TokenCre createInput.AutoStart = cloneInput.AutoStart createInput.EipBw = cloneInput.EipBw + createInput.EipTxBw = cloneInput.EipTxBw + createInput.EipRxBw = cloneInput.EipRxBw createInput.Eip = cloneInput.Eip createInput.EipChargeType = cloneInput.EipChargeType if err := GuestManager.validateEip(ctx, userCred, createInput, createInput.PreferRegion, createInput.PreferManager); err != nil { @@ -2831,6 +2833,12 @@ func (self *SGuest) PerformChangeIpaddr( if conf.BwLimit == 0 { conf.BwLimit = gn.BwLimit } + if conf.TxBwLimit == 0 { + conf.TxBwLimit = gn.TxBwLimit + } + if conf.RxBwLimit == 0 { + conf.RxBwLimit = gn.RxBwLimit + } if conf.Index == 0 { conf.Index = int(gn.Index) } @@ -3365,9 +3373,8 @@ func (guest *SGuest) PerformChangeBandwidth( return nil, httperrors.NewBadRequestError("Cannot change bandwidth in status %s", guest.Status) } - bandwidth := input.Bandwidth - if bandwidth < 0 { - return nil, httperrors.NewBadRequestError("Bandwidth must be non-negative") + if input.Bandwidth < 0 && input.TxBwLimit < 0 && input.RxBwLimit < 0 { + return nil, httperrors.NewBadRequestError("Bandwidth, tx_bw_limit and rx_bw_limit must be non-negative") } guestnic, err := guest.findGuestnetworkByInfo(input.ServerNetworkInfo) @@ -3375,10 +3382,14 @@ func (guest *SGuest) PerformChangeBandwidth( return nil, errors.Wrap(err, "findGuestnetworkByInfo") } - if guestnic.BwLimit != int(bandwidth) { + if guestnic.BwLimit != int(input.Bandwidth) || guestnic.TxBwLimit != int(input.TxBwLimit) || guestnic.RxBwLimit != int(input.RxBwLimit) { oldBw := guestnic.BwLimit + oldTxBw := guestnic.TxBwLimit + oldRxBw := guestnic.RxBwLimit _, err := db.Update(guestnic, func() error { - guestnic.BwLimit = int(bandwidth) + guestnic.BwLimit = int(input.Bandwidth) + guestnic.TxBwLimit = int(input.TxBwLimit) + guestnic.RxBwLimit = int(input.RxBwLimit) return nil }) if err != nil { @@ -3386,6 +3397,8 @@ func (guest *SGuest) PerformChangeBandwidth( } eventDesc := guestnic.GetShortDesc(ctx) eventDesc.Add(jsonutils.NewInt(int64(oldBw)), "old_bw_limit_mbps") + eventDesc.Add(jsonutils.NewInt(int64(oldTxBw)), "old_tx_bw_limit_mbps") + eventDesc.Add(jsonutils.NewInt(int64(oldRxBw)), "old_rx_bw_limit_mbps") db.OpsLog.LogEvent(guest, db.ACT_CHANGE_BANDWIDTH, eventDesc, userCred) logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_VM_CHANGE_BANDWIDTH, eventDesc, userCred, true) if guest.Status == api.VM_READY || (input.NoSync != nil && *input.NoSync) { diff --git a/pkg/compute/models/guestnetworks.go b/pkg/compute/models/guestnetworks.go index e6b7c39827..a57108e98c 100644 --- a/pkg/compute/models/guestnetworks.go +++ b/pkg/compute/models/guestnetworks.go @@ -127,6 +127,11 @@ type SGuestnetwork struct { SBillingTypeBase `billing_type->default:"prepaid"` SBillingChargeTypeBase `charge_type->default:"bandwidth"` + + // 下行带宽限制,单位mbps + RxBwLimit int `nullable:"false" default:"0" list:"user"` + // 上行带宽限制,单位mbps + TxBwLimit int `nullable:"false" default:"0" list:"user"` } func (gn SGuestnetwork) GetIP() string { @@ -271,6 +276,8 @@ type newGuestNetworkArgs struct { ifname string macAddr string bwLimit int + rxBwLimit int + txBwLimit int nicDriver string numQueues int teamWithMac string @@ -301,6 +308,8 @@ func (manager *SGuestnetworkManager) newGuestNetwork( driver = args.nicDriver numQueues = args.numQueues bwLimit = args.bwLimit + rxBwLimit = args.rxBwLimit + txBwLimit = args.txBwLimit virtual = args.virtual reserved = args.tryReserved allocDir = args.allocDir @@ -329,6 +338,12 @@ func (manager *SGuestnetworkManager) newGuestNetwork( if bwLimit >= 0 { gn.BwLimit = bwLimit } + if rxBwLimit >= 0 { + gn.RxBwLimit = rxBwLimit + } + if txBwLimit >= 0 { + gn.TxBwLimit = txBwLimit + } gn.PortMappings = args.portMappings gn.BillingType = args.billingType @@ -723,6 +738,8 @@ func (gn *SGuestnetwork) getJsonDesc() *api.GuestnetworkJsonDesc { desc.TxTrafficLimit = gn.TxTrafficLimit desc.Vlan = net.VlanId desc.Bw = gn.getBandwidth(net, wire) + desc.RxBwLimit = gn.getRxBwLimit(net, wire) + desc.TxBwLimit = gn.getTxBwLimit(net, wire) desc.Mtu = gn.getMtu(net, wire) desc.Index = gn.Index desc.VirtualIps = gn.GetVirtualIPs() @@ -906,6 +923,8 @@ func (gn *SGuestnetwork) GetDetailedString() string { network.Name, gn.Driver, fmt.Sprintf("%d", gn.getBandwidth(network, nil)), + fmt.Sprintf("rx%d", gn.getRxBwLimit(network, nil)), + fmt.Sprintf("tx%d", gn.getTxBwLimit(network, nil)), fmt.Sprintf("%d", naCount), ) return fmt.Sprintf("eth%d:%s", gn.Index, strings.Join(parts, "/")) @@ -1113,7 +1132,11 @@ type GuestnicsCount struct { ExternalNicCount int ExternalVirtualNicCount int InternalBandwidth int + InternalRxBwLimit int + InternalTxBwLimit int ExternalBandwidth int + ExternalRxBwLimit int + ExternalTxBwLimit int } func calculateNics(q *sqlchemy.SQuery) GuestnicsCount { @@ -1131,6 +1154,8 @@ func calculateNics(q *sqlchemy.SQuery) GuestnicsCount { cnt.ExternalNicCount += 1 } cnt.ExternalBandwidth += gn.BwLimit + cnt.ExternalRxBwLimit += gn.RxBwLimit + cnt.ExternalTxBwLimit += gn.TxBwLimit } else { if gn.Virtual { cnt.InternalVirtualNicCount += 1 @@ -1138,6 +1163,8 @@ func calculateNics(q *sqlchemy.SQuery) GuestnicsCount { cnt.InternalNicCount += 1 } cnt.InternalBandwidth += gn.BwLimit + cnt.InternalRxBwLimit += gn.RxBwLimit + cnt.InternalTxBwLimit += gn.TxBwLimit } } return cnt @@ -1181,6 +1208,20 @@ func (gn *SGuestnetwork) getBandwidth(net *SNetwork, wire *SWire) int { } } +func (gn *SGuestnetwork) getRxBwLimit(net *SNetwork, wire *SWire) int { + if gn.RxBwLimit > 0 && gn.RxBwLimit <= api.MAX_BANDWIDTH { + return gn.RxBwLimit + } + return gn.getBandwidth(net, wire) +} + +func (gn *SGuestnetwork) getTxBwLimit(net *SNetwork, wire *SWire) int { + if gn.TxBwLimit > 0 && gn.TxBwLimit <= api.MAX_BANDWIDTH { + return gn.TxBwLimit + } + return gn.getBandwidth(net, wire) +} + func (gn *SGuestnetwork) getMtu(net *SNetwork, wire *SWire) int16 { return net.getMtu(wire) } @@ -1430,6 +1471,9 @@ func (gn *SGuestnetwork) ToNetworkConfig() *api.NetworkConfig { Ifname: gn.Ifname, NetType: net.ServerType, Exit: net.IsExitNetwork(), + + RxBwLimit: gn.RxBwLimit, + TxBwLimit: gn.TxBwLimit, } return ret } diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 46eee4e6cd..67720d54f3 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -2548,8 +2548,7 @@ func (manager *SGuestManager) validateEip(ctx context.Context, userCred mcclient return nil } eipStr := input.Eip - eipBw := input.EipBw - if len(eipStr) > 0 || eipBw > 0 { + if len(eipStr) > 0 || input.EipTxBw > 0 || input.EipRxBw > 0 || input.EipBw > 0 { if !driver.IsSupportEip() { return httperrors.NewNotImplementedError("eip not supported for %s", input.Hypervisor) } @@ -2710,7 +2709,9 @@ func getGuestResourceRequirements( eipCnt := 0 eipBw := input.EipBw - if eipBw > 0 { + eipTxBw := input.EipTxBw + eipRxBw := input.EipRxBw + if eipBw > 0 || eipTxBw > 0 || eipRxBw > 0 { eipCnt = 1 } @@ -3869,6 +3870,8 @@ type Attach2NetworkArgs struct { StrictIPv6 bool BwLimit int + RxBwLimit int + TxBwLimit int NicDriver string NumQueues int RxTrafficLimit int64 @@ -3903,6 +3906,8 @@ func (args *Attach2NetworkArgs) onceArgs(i int) attach2NetworkOnceArgs { strictIPv6: args.StrictIPv6, bwLimit: args.BwLimit, + rxBwLimit: args.RxBwLimit, + txBwLimit: args.TxBwLimit, nicDriver: args.NicDriver, numQueues: args.NumQueues, txTrafficLimit: args.TxTrafficLimit, @@ -3949,6 +3954,8 @@ type attach2NetworkOnceArgs struct { strictIPv6 bool bwLimit int + rxBwLimit int + txBwLimit int nicDriver string numQueues int nicConf SNicConfig @@ -4033,6 +4040,8 @@ func (self *SGuest) attach2NetworkOnce( ifname: args.nicConf.Ifname, macAddr: args.nicConf.Mac, bwLimit: args.bwLimit, + rxBwLimit: args.rxBwLimit, + txBwLimit: args.txBwLimit, nicDriver: nicDriver, numQueues: args.numQueues, teamWithMac: args.teamWithMac, @@ -4852,6 +4861,8 @@ func (self *SGuest) attach2NamedNetworkDesc(ctx context.Context, userCred mcclie NicDriver: netConfig.Driver, NumQueues: netConfig.NumQueues, BwLimit: netConfig.BwLimit, + RxBwLimit: netConfig.RxBwLimit, + TxBwLimit: netConfig.TxBwLimit, RxTrafficLimit: netConfig.RxTrafficLimit, TxTrafficLimit: netConfig.TxTrafficLimit, Virtual: netConfig.Vip, @@ -6874,6 +6885,8 @@ func (self *SGuest) ToCreateInput(ctx context.Context, userCred mcclient.TokenCr userInput.SecgroupId = genInput.SecgroupId userInput.KeypairId = genInput.KeypairId userInput.EipBw = genInput.EipBw + userInput.EipTxBw = genInput.EipTxBw + userInput.EipRxBw = genInput.EipRxBw userInput.EipChargeType = genInput.EipChargeType drv, _ := self.GetDriver() if drv != nil && drv.IsSupportPublicIp() { @@ -6948,6 +6961,8 @@ func (self *SGuest) toCreateInput() *api.ServerCreateInput { switch eip.Mode { case api.EIP_MODE_STANDALONE_EIP: r.EipBw = eip.Bandwidth + r.EipTxBw = eip.TxBwLimit + r.EipRxBw = eip.RxBwLimit r.EipChargeType = eip.ChargeType case api.EIP_MODE_INSTANCE_PUBLICIP: drv, _ := self.GetDriver() @@ -7032,6 +7047,8 @@ func (self *SGuest) ToNetworksConfig() []*api.NetworkConfig { // netConf.Reserved netConf.Driver = guestNetwork.Driver netConf.BwLimit = guestNetwork.BwLimit + netConf.RxBwLimit = guestNetwork.RxBwLimit + netConf.TxBwLimit = guestNetwork.TxBwLimit netConf.RequireTeaming = requireTeaming // netConf.NetType ret = append(ret, netConf) diff --git a/pkg/compute/models/host_taps.go b/pkg/compute/models/host_taps.go index 5ab9ecbc65..34ea060885 100644 --- a/pkg/compute/models/host_taps.go +++ b/pkg/compute/models/host_taps.go @@ -26,6 +26,10 @@ import ( ) func (h *SHost) GetDetailsTapConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (api.SHostTapConfig, error) { + return h.getTapConfig() +} + +func (h *SHost) getTapConfig() (api.SHostTapConfig, error) { conf := api.SHostTapConfig{} srvs, err := NetTapServiceManager.getEnabledTapServiceOnHost(h.Id) diff --git a/pkg/compute/models/hosts.go b/pkg/compute/models/hosts.go index 2beaff150b..27114772fc 100644 --- a/pkg/compute/models/hosts.go +++ b/pkg/compute/models/hosts.go @@ -5431,6 +5431,13 @@ func (hh *SHost) PerformPing(ctx context.Context, userCred mcclient.TokenCredent return nil, errors.Wrap(err, "get host files") } result.Set("host_files", jsonutils.Marshal(hostFiles)) + // get tap config + tapConfig, err := hh.getTapConfig() + if err != nil { + log.Errorf("get tap config error %s", err) + } else { + result.Set("tap_config", jsonutils.Marshal(tapConfig)) + } appParams := appsrv.AppContextGetParams(ctx) if appParams != nil { diff --git a/pkg/compute/tasks/eip/eip_change_bandwidth_task.go b/pkg/compute/tasks/eip/eip_change_bandwidth_task.go index 321b1d1bb2..42c01aa8fa 100644 --- a/pkg/compute/tasks/eip/eip_change_bandwidth_task.go +++ b/pkg/compute/tasks/eip/eip_change_bandwidth_task.go @@ -44,9 +44,10 @@ func (self *EipChangeBandwidthTask) TaskFail(ctx context.Context, eip *models.SE func (self *EipChangeBandwidthTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { eip := obj.(*models.SElasticip) - bandwidth, _ := self.Params.Int("bandwidth") - if bandwidth <= 0 { - self.TaskFail(ctx, eip, errors.Errorf("nvalid bandwidth %d", bandwidth)) + input := api.ElasticipChangeBandwidthInput{} + err := self.Params.Unmarshal(&input) + if err != nil { + self.TaskFail(ctx, eip, errors.Wrapf(err, "Unmarshal")) return } @@ -57,15 +58,14 @@ func (self *EipChangeBandwidthTask) OnInit(ctx context.Context, obj db.IStandalo return } - err = extEip.ChangeBandwidth(int(bandwidth)) + err = extEip.ChangeBandwidth(int(input.BandwidthMb)) if err != nil { self.TaskFail(ctx, eip, errors.Wrapf(err, "ChangeBandwidth")) return } - } - if err := eip.DoChangeBandwidth(ctx, self.UserCred, int(bandwidth)); err != nil { + if err := eip.DoChangeBandwidth(ctx, self.UserCred, input); err != nil { self.TaskFail(ctx, eip, errors.Wrapf(err, "DoChangeBandwidth")) return } diff --git a/pkg/compute/tasks/guest/guest_batch_create_task.go b/pkg/compute/tasks/guest/guest_batch_create_task.go index 99b6a509c2..b3e68df70a 100644 --- a/pkg/compute/tasks/guest/guest_batch_create_task.go +++ b/pkg/compute/tasks/guest/guest_batch_create_task.go @@ -195,13 +195,15 @@ func (task *GuestBatchCreateTask) allocateGuestOnHost(ctx context.Context, guest } if input.PublicIpBw > 0 { - input.Eip, input.EipBw = "", 0 + input.Eip, input.EipBw, input.EipTxBw, input.EipRxBw = "", 0, 0, 0 } // allocate eips if input.EipBw > 0 { eip, err := models.ElasticipManager.NewEipForVMOnHost(ctx, task.UserCred, &models.NewEipForVMOnHostArgs{ Bandwidth: input.EipBw, + TxBwLimit: input.EipTxBw, + RxBwLimit: input.EipRxBw, BgpType: input.EipBgpType, ChargeType: input.EipChargeType, AutoDellocate: input.EipAutoDellocate, diff --git a/pkg/hostman/hostinfo/hostpinger/hostpinger.go b/pkg/hostman/hostinfo/hostpinger/hostpinger.go index 86d4086c91..a843e81314 100644 --- a/pkg/hostman/hostinfo/hostpinger/hostpinger.go +++ b/pkg/hostman/hostinfo/hostpinger/hostpinger.go @@ -16,6 +16,8 @@ package hostpinger import ( "context" + "os" + "path/filepath" "time" "github.com/shirou/gopsutil/cpu" @@ -175,6 +177,16 @@ func (p *SHostPingTask) ping(div int, hostId string) error { log.Errorf("on host files changed failed %s", err) } } + + if res.Contains("tap_config") { + tapConfJson, err := res.Get("tap_config") + if err != nil { + log.Errorf("get tap config from res %s: %v", res.String(), err) + } else { + tagConfPath := filepath.Join(options.HostOptions.ServersPath, api.TapConfigFileName) + os.WriteFile(tagConfPath, []byte(tapConfJson.String()), 0644) + } + } } return nil } diff --git a/pkg/mcclient/options/compute/servers.go b/pkg/mcclient/options/compute/servers.go index c5c8145fe6..42add1944c 100644 --- a/pkg/mcclient/options/compute/servers.go +++ b/pkg/mcclient/options/compute/servers.go @@ -438,6 +438,8 @@ type ServerCreateFromInstanceSnapshot struct { AllowDelete bool `help:"Unlock server to allow deleting"` EipBw int `help:"allocate EIP with bandwidth in MB when server is created" json:"eip_bw,omitzero"` + EipTxBw int `help:"allocate EIP with上行带宽 in MB when server is created" json:"eip_tx_bw,omitzero"` + EipRxBw int `help:"allocate EIP with下行带宽 in MB when server is created" json:"eip_rx_bw,omitzero"` EipChargeType string `help:"newly allocated EIP charge type" choices:"traffic|bandwidth" json:"eip_charge_type,omitempty"` Eip string `help:"associate with an existing EIP when server is created" json:"eip,omitempty"` } @@ -504,6 +506,8 @@ type ServerCreateOptionalOptions struct { GenerateName bool `help:"name is generated by pattern" json:"-"` EipBw int `help:"allocate EIP with bandwidth in MB when server is created" json:"eip_bw,omitzero"` + EipTxBw int `help:"allocate EIP with上行带宽 in MB when server is created" json:"eip_tx_bw,omitzero"` + EipRxBw int `help:"allocate EIP with下行带宽 in MB when server is created" json:"eip_rx_bw,omitzero"` EipBgpType string `help:"desired BGP type of newly alloated EIP" json:"eip_bgp_type,omitzero"` EipChargeType string `help:"newly allocated EIP charge type" choices:"traffic|bandwidth" json:"eip_charge_type,omitempty"` Eip string `help:"associate with an existing EIP when server is created" json:"eip,omitempty"` @@ -592,6 +596,8 @@ func (opts *ServerCreateOptionalOptions) OptionalParams() (*computeapi.ServerCre AutoRenew: opts.AutoRenew, AutoPrepaidRecycle: opts.AutoPrepaidRecycle, EipBw: opts.EipBw, + EipTxBw: opts.EipTxBw, + EipRxBw: opts.EipRxBw, EipBgpType: opts.EipBgpType, EipChargeType: billing_api.TNetChargeType(opts.EipChargeType), PublicIpBw: opts.PublicIpBw, diff --git a/pkg/vpcagent/ovn/keeper.go b/pkg/vpcagent/ovn/keeper.go index e617ca8f3b..e8a5f53b6f 100644 --- a/pkg/vpcagent/ovn/keeper.go +++ b/pkg/vpcagent/ovn/keeper.go @@ -705,19 +705,25 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn } var qosVif []*ovn_nb.QoS - if bwMbps := guestnetwork.BwLimit; bwMbps > 0 { - var ( - kbps = int64(bwMbps * 1000) - kbur = int64(kbps * 2) - ) + if guestnetwork.BwLimit > 0 || guestnetwork.RxBwLimit > 0 || guestnetwork.TxBwLimit > 0 { + kbpsRx := int64(guestnetwork.RxBwLimit * 1000) + if kbpsRx == 0 { + kbpsRx = int64(guestnetwork.BwLimit * 1000) + } + kburRx := int64(kbpsRx * 2) + kbpsTx := int64(guestnetwork.TxBwLimit * 1000) + if kbpsTx == 0 { + kbpsTx = int64(guestnetwork.BwLimit * 1000) + } + kburTx := int64(kbpsTx * 2) qosVif = []*ovn_nb.QoS{ { Priority: 2000, Direction: "from-lport", Match: fmt.Sprintf("inport == %q", lportName), Bandwidth: map[string]int64{ - "rate": kbps, - "burst": kbur, + "rate": kbpsTx, + "burst": kburTx, }, ExternalIds: map[string]string{ externalKeyOcRef: ocQosRef, @@ -728,8 +734,8 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn Direction: "to-lport", Match: fmt.Sprintf("outport == %q", lportName), Bandwidth: map[string]int64{ - "rate": kbps, - "burst": kbur, + "rate": kbpsRx, + "burst": kburRx, }, ExternalIds: map[string]string{ externalKeyOcRef: ocQosRef, @@ -757,20 +763,27 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn externalKeyOcRef: ocGnrDefaultRef, }, } - if bwMbps := eip.Bandwidth; bwMbps > 0 { - var ( - kbps = int64(bwMbps * 1000) - kbur = int64(kbps * 2) - eipgwVip = apis.VpcEipGatewayIP3().String() - ) + if eip.Bandwidth > 0 || eip.TxBwLimit > 0 || eip.RxBwLimit > 0 { + kbpsTx := int64(eip.TxBwLimit * 1000) + if kbpsTx == 0 { + kbpsTx = int64(eip.Bandwidth * 1000) + } + kburTx := int64(kbpsTx * 2) + kbpsRx := int64(eip.RxBwLimit * 1000) + if kbpsRx == 0 { + kbpsRx = int64(eip.Bandwidth * 1000) + } + kburRx := int64(kbpsRx * 2) + eipgwVip := apis.VpcEipGatewayIP3().String() + hasQoSEip = true qosEipIn = &ovn_nb.QoS{ Priority: 2000, - Direction: "from-lport", + Direction: "to-lport", Match: fmt.Sprintf("inport == %q && ip4 && ip4.dst == %s", vpcEipLspName(vpc.Id, eipgwVip), guestnetwork.IpAddr), Bandwidth: map[string]int64{ - "rate": kbps, - "burst": kbur, + "rate": kbpsRx, + "burst": kburRx, }, ExternalIds: map[string]string{ externalKeyOcRef: ocQosEipRef, @@ -781,8 +794,8 @@ func (keeper *OVNNorthboundKeeper) ClaimGuestnetwork(ctx context.Context, guestn Direction: "from-lport", Match: fmt.Sprintf("inport == %q && ip4 && ip4.src == %s", vpcErpName(vpc.Id), guestnetwork.IpAddr), Bandwidth: map[string]int64{ - "rate": kbps, - "burst": kbur, + "rate": kbpsTx, + "burst": kburTx, }, ExternalIds: map[string]string{ externalKeyOcRef: ocQosEipRef, @@ -971,20 +984,27 @@ func (keeper *OVNNorthboundKeeper) ClaimLoadbalancerNetwork(ctx context.Context, externalKeyOcRef: ocLnrDefaultRef, }, } - if bwMbps := eip.Bandwidth; bwMbps > 0 { - var ( - kbps = int64(bwMbps * 1000) - kbur = int64(kbps * 2) - eipgwVip = apis.VpcEipGatewayIP3().String() - ) + if eip.Bandwidth > 0 || eip.TxBwLimit > 0 || eip.RxBwLimit > 0 { + kbpsTx := int64(eip.TxBwLimit * 1000) + if kbpsTx == 0 { + kbpsTx = int64(eip.Bandwidth * 1000) + } + kburTx := int64(kbpsTx * 2) + kbpsRx := int64(eip.RxBwLimit * 1000) + if kbpsRx == 0 { + kbpsRx = int64(eip.Bandwidth * 1000) + } + kburRx := int64(kbpsRx * 2) + eipgwVip := apis.VpcEipGatewayIP3().String() + hasQoSEip = true qosEipIn = &ovn_nb.QoS{ Priority: 2000, - Direction: "from-lport", + Direction: "to-lport", Match: fmt.Sprintf("inport == %q && ip4 && ip4.dst == %s", vpcEipLspName(vpcId, eipgwVip), lbIntIp), Bandwidth: map[string]int64{ - "rate": kbps, - "burst": kbur, + "rate": kbpsRx, + "burst": kburRx, }, ExternalIds: map[string]string{ externalKeyOcRef: ocQosEipRef, @@ -995,8 +1015,8 @@ func (keeper *OVNNorthboundKeeper) ClaimLoadbalancerNetwork(ctx context.Context, Direction: "from-lport", Match: fmt.Sprintf("inport == %q", lportName), Bandwidth: map[string]int64{ - "rate": kbps, - "burst": kbur, + "rate": kbpsTx, + "burst": kburTx, }, ExternalIds: map[string]string{ externalKeyOcRef: ocQosEipRef, @@ -1208,20 +1228,26 @@ func (keeper *OVNNorthboundKeeper) ClaimGroupnetwork(ctx context.Context, groupn externalKeyOcRef: ocGnrDefaultRef, }, } - if bwMbps := eip.Bandwidth; bwMbps > 0 { - var ( - kbps = int64(bwMbps * 1000) - kbur = int64(kbps * 2) - eipgwVip = apis.VpcEipGatewayIP3().String() - ) + if eip.Bandwidth > 0 || eip.TxBwLimit > 0 || eip.RxBwLimit > 0 { + kbpsTx := int64(eip.TxBwLimit * 1000) + if kbpsTx == 0 { + kbpsTx = int64(eip.Bandwidth * 1000) + } + kburTx := int64(kbpsTx * 2) + kbpsRx := int64(eip.RxBwLimit * 1000) + if kbpsRx == 0 { + kbpsRx = int64(eip.Bandwidth * 1000) + } + kburRx := int64(kbpsRx * 2) + eipgwVip := apis.VpcEipGatewayIP3().String() hasQoSEip = true qosEipIn = &ovn_nb.QoS{ Priority: 2000, - Direction: "from-lport", + Direction: "to-lport", Match: fmt.Sprintf("inport == %q && ip4 && ip4.dst == %s", vpcEipLspName(vpc.Id, eipgwVip), groupnetwork.IpAddr), Bandwidth: map[string]int64{ - "rate": kbps, - "burst": kbur, + "rate": kbpsRx, + "burst": kburRx, }, ExternalIds: map[string]string{ externalKeyOcRef: ocQosEipRef, @@ -1232,8 +1258,8 @@ func (keeper *OVNNorthboundKeeper) ClaimGroupnetwork(ctx context.Context, groupn Direction: "from-lport", Match: fmt.Sprintf("inport == %q && ip4 && ip4.src == %s", vpcErpName(vpc.Id), groupnetwork.IpAddr), Bandwidth: map[string]int64{ - "rate": kbps, - "burst": kbur, + "rate": kbpsTx, + "burst": kburTx, }, ExternalIds: map[string]string{ externalKeyOcRef: ocQosEipRef,