Skip to content

Commit

Permalink
Add jitter support to netem qos parameters (#2476)
Browse files Browse the repository at this point in the history
* Add jitter support to netem qos parameters

* Fix patch error caused by a conflicting change to the NEWS file

* Fix windows api for SetNetemQos for the jitter parameter addition
  • Loading branch information
lime008 committed Mar 15, 2023
1 parent b15fc51 commit 47a7055
Show file tree
Hide file tree
Showing 13 changed files with 54 additions and 31 deletions.
2 changes: 2 additions & 0 deletions dist/images/Dockerfile.base
Expand Up @@ -24,6 +24,8 @@ RUN cd /usr/src/ && \
curl -s https://github.com/kubeovn/ovs/commit/403fbd0f6561c8985302734608c2de659671c563.patch | git apply && \
# ovsdb-tool: add optional server id parameter for "join-cluster" command
curl -s https://github.com/kubeovn/ovs/commit/9a81b91368b27afda97657a8864b729dc2e029e2.patch | git apply && \
# Add jitter parameter patch for netem qos
curl -s https://github.com/openvswitch/ovs/commit/a6195e2c4236cbe16b3649940fac3b08493eabb2.patch | git apply --whitespace=fix --exclude=NEWS && \
# compile without avx512
if [ "$ARCH" = "amd64" -a "$NO_AVX512" = "true" ]; then curl -s https://github.com/kubeovn/ovs/commit/97f9f85a277e5372af6e3cd899cab1b63f6f6b44.patch | git apply; fi && \
./boot.sh && \
Expand Down
3 changes: 2 additions & 1 deletion dist/images/Dockerfile.base-dpdk
Expand Up @@ -25,13 +25,14 @@ RUN cd /usr/src/ && \
ninja -C build install && \
ldconfig


RUN cd /usr/src/ && \
git clone https://github.com/openvswitch/ovs.git && \
cd ovs && \
git checkout 1570924c3f83851f39f56e3363050b70ba1aafb0 && \
curl -s https://github.com/kubeovn/ovs/commit/22ea22c40b46ee5adeae977ff6cfca81b3ff25d7.patch | git apply && \
curl -s https://github.com/openvswitch/ovs/commit/a432b1eb496cc1606873068c26716977a02029e2.patch | git apply && \
# Add jitter parameter patch
curl -s https://github.com/openvswitch/ovs/commit/a6195e2c4236cbe16b3649940fac3b08493eabb2.patch | git apply --whitespace=fix --exclude=NEWS && \
# compile without avx512
if [ "$ARCH" = "amd64" -a "$NO_AVX512" = "true" ]; then curl -s https://github.com/kubeovn/ovs/commit/38c59e078d69b343f56ab0f380fb9f42b94b7c02.patch | git apply; fi && \
./boot.sh && \
Expand Down
4 changes: 3 additions & 1 deletion docs/qos.md
Expand Up @@ -84,11 +84,13 @@ When the subnet specifies the htbqos parameter and pod sets the QoS priority ann
**The previous annotation `ovn.kubernetes.io/ingress_rate` and `ovn.kubernetes.io/egress_rate`, can still be used to control the bidirectional bandwidth of pod.**

## linux-netem QoS
New annotations added for pod, `ovn.kubernetes.io/latency``ovn.kubernetes.io/limit` and
New annotations added for pod, `ovn.kubernetes.io/latency``ovn.kubernetes.io/jitter``ovn.kubernetes.io/limit` and
`ovn.kubernetes.io/loss`, used for setting QoS parameters of linux-netem type.

`latency` is used for traffic delay. The value is an integer value, and the unit is `ms`.

`jitter` is used for traffic delay jitter. The value is an integer value, and the unit is `ms`.

`limit` is the maximum number of packets the qdisc may hold queued at a time. The value is an integer value, such as 1000.

`loss` is an independent loss probability to the packets outgoing from the chosen network interface, the valid range for this field is from 0 to 100. For example, if the value is 20, the packet loss probability is 20%.
Expand Down
2 changes: 2 additions & 0 deletions pkg/daemon/controller.go
Expand Up @@ -467,6 +467,7 @@ func (c *Controller) enqueuePod(old, new interface{}) {
if oldPod.Annotations[util.IngressRateAnnotation] != newPod.Annotations[util.IngressRateAnnotation] ||
oldPod.Annotations[util.EgressRateAnnotation] != newPod.Annotations[util.EgressRateAnnotation] ||
oldPod.Annotations[util.NetemQosLatencyAnnotation] != newPod.Annotations[util.NetemQosLatencyAnnotation] ||
oldPod.Annotations[util.NetemQosJitterAnnotation] != newPod.Annotations[util.NetemQosJitterAnnotation] ||
oldPod.Annotations[util.NetemQosLimitAnnotation] != newPod.Annotations[util.NetemQosLimitAnnotation] ||
oldPod.Annotations[util.NetemQosLossAnnotation] != newPod.Annotations[util.NetemQosLossAnnotation] ||
oldPod.Annotations[util.MirrorControlAnnotation] != newPod.Annotations[util.MirrorControlAnnotation] {
Expand All @@ -489,6 +490,7 @@ func (c *Controller) enqueuePod(old, new interface{}) {
if oldPod.Annotations[fmt.Sprintf(util.IngressRateAnnotationTemplate, provider)] != newPod.Annotations[fmt.Sprintf(util.IngressRateAnnotationTemplate, provider)] ||
oldPod.Annotations[fmt.Sprintf(util.EgressRateAnnotationTemplate, provider)] != newPod.Annotations[fmt.Sprintf(util.EgressRateAnnotationTemplate, provider)] ||
oldPod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, provider)] != newPod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, provider)] ||
oldPod.Annotations[fmt.Sprintf(util.NetemQosJitterAnnotationTemplate, provider)] != newPod.Annotations[fmt.Sprintf(util.NetemQosJitterAnnotationTemplate, provider)] ||
oldPod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, provider)] != newPod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, provider)] ||
oldPod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, provider)] != newPod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, provider)] ||
oldPod.Annotations[fmt.Sprintf(util.MirrorControlAnnotationTemplate, provider)] != newPod.Annotations[fmt.Sprintf(util.MirrorControlAnnotationTemplate, provider)] {
Expand Down
5 changes: 3 additions & 2 deletions pkg/daemon/controller_linux.go
Expand Up @@ -413,6 +413,7 @@ func (c *Controller) getPolicyRouting(subnet *kubeovnv1.Subnet) ([]netlink.Rule,

return rules, routes, nil
}

func (c *Controller) handlePod(key string) error {
namespace, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
Expand Down Expand Up @@ -451,7 +452,7 @@ func (c *Controller) handlePod(key string) error {
return err
}
// set linux-netem qos
err = ovs.SetNetemQos(podName, pod.Namespace, ifaceID, pod.Annotations[util.NetemQosLatencyAnnotation], pod.Annotations[util.NetemQosLimitAnnotation], pod.Annotations[util.NetemQosLossAnnotation])
err = ovs.SetNetemQos(podName, pod.Namespace, ifaceID, pod.Annotations[util.NetemQosLatencyAnnotation], pod.Annotations[util.NetemQosLimitAnnotation], pod.Annotations[util.NetemQosLossAnnotation], pod.Annotations[util.NetemQosJitterAnnotation])
if err != nil {
return err
}
Expand All @@ -477,7 +478,7 @@ func (c *Controller) handlePod(key string) error {
if err != nil {
return err
}
err = ovs.SetNetemQos(podName, pod.Namespace, ifaceID, pod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, provider)])
err = ovs.SetNetemQos(podName, pod.Namespace, ifaceID, pod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosJitterAnnotationTemplate, provider)])
if err != nil {
return err
}
Expand Down
5 changes: 2 additions & 3 deletions pkg/daemon/controller_windows.go
Expand Up @@ -17,8 +17,7 @@ import (
)

// ControllerRuntime represents runtime specific controller members
type ControllerRuntime struct {
}
type ControllerRuntime struct{}

func (c *Controller) initRuntime() error {
return nil
Expand Down Expand Up @@ -215,7 +214,7 @@ func (c *Controller) handlePod(key string) error {
if err != nil {
return err
}
err = ovs.SetNetemQos(podName, pod.Namespace, ifaceID, pod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, provider)])
err = ovs.SetNetemQos(podName, pod.Namespace, ifaceID, pod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, provider)], pod.Annotations[fmt.Sprintf(util.NetemQosJitterAnnotationTemplate, provider)])
if err != nil {
return err
}
Expand Down
9 changes: 5 additions & 4 deletions pkg/daemon/handler.go
Expand Up @@ -86,7 +86,7 @@ func (csh cniServerHandler) handleAdd(req *restful.Request, resp *restful.Respon
}

var gatewayCheckMode int
var macAddr, ip, ipAddr, cidr, gw, subnet, ingress, egress, providerNetwork, ifName, nicType, podNicName, vmName, latency, limit, loss, u2oInterconnectionIP string
var macAddr, ip, ipAddr, cidr, gw, subnet, ingress, egress, providerNetwork, ifName, nicType, podNicName, vmName, latency, limit, loss, jitter, u2oInterconnectionIP string
var routes []request.Route
var isDefaultRoute bool
var pod *v1.Pod
Expand Down Expand Up @@ -125,6 +125,7 @@ func (csh cniServerHandler) handleAdd(req *restful.Request, resp *restful.Respon
latency = pod.Annotations[fmt.Sprintf(util.NetemQosLatencyAnnotationTemplate, podRequest.Provider)]
limit = pod.Annotations[fmt.Sprintf(util.NetemQosLimitAnnotationTemplate, podRequest.Provider)]
loss = pod.Annotations[fmt.Sprintf(util.NetemQosLossAnnotationTemplate, podRequest.Provider)]
jitter = pod.Annotations[fmt.Sprintf(util.NetemQosJitterAnnotationTemplate, podRequest.Provider)]
providerNetwork = pod.Annotations[fmt.Sprintf(util.ProviderNetworkTemplate, podRequest.Provider)]
vmName = pod.Annotations[fmt.Sprintf(util.VmTemplate, podRequest.Provider)]
ipAddr = util.GetIpAddrWithMask(ip, cidr)
Expand Down Expand Up @@ -225,7 +226,7 @@ func (csh cniServerHandler) handleAdd(req *restful.Request, resp *restful.Respon
u2oInterconnectionIP = podSubnet.Status.U2OInterconnectionIP
}

//skip ping check gateway for pods during live migration
// skip ping check gateway for pods during live migration
if pod.Annotations[fmt.Sprintf(util.LiveMigrationAnnotationTemplate, podRequest.Provider)] != "true" {
if podSubnet.Spec.Vlan != "" && !podSubnet.Spec.LogicalGateway {
if podSubnet.Spec.DisableGatewayCheck {
Expand Down Expand Up @@ -272,12 +273,12 @@ func (csh cniServerHandler) handleAdd(req *restful.Request, resp *restful.Respon
klog.Infof("create container interface %s mac %s, ip %s, cidr %s, gw %s, custom routes %v", ifName, macAddr, ipAddr, cidr, gw, routes)
detectIPConflict := podSubnet.Spec.Vlan != ""
if nicType == util.InternalType {
podNicName, err = csh.configureNicWithInternalPort(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, ifName, macAddr, mtu, ipAddr, gw, isDefaultRoute, detectIPConflict, routes, podRequest.DNS.Nameservers, podRequest.DNS.Search, ingress, egress, podRequest.DeviceID, nicType, latency, limit, loss, gatewayCheckMode, u2oInterconnectionIP)
podNicName, err = csh.configureNicWithInternalPort(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, ifName, macAddr, mtu, ipAddr, gw, isDefaultRoute, detectIPConflict, routes, podRequest.DNS.Nameservers, podRequest.DNS.Search, ingress, egress, podRequest.DeviceID, nicType, latency, limit, loss, jitter, gatewayCheckMode, u2oInterconnectionIP)
} else if nicType == util.DpdkType {
err = csh.configureDpdkNic(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, ifName, macAddr, mtu, ipAddr, gw, ingress, egress, getShortSharedDir(pod.UID, podRequest.VhostUserSocketVolumeName), podRequest.VhostUserSocketName)
} else {
podNicName = ifName
err = csh.configureNic(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, podRequest.VfDriver, ifName, macAddr, mtu, ipAddr, gw, isDefaultRoute, detectIPConflict, routes, podRequest.DNS.Nameservers, podRequest.DNS.Search, ingress, egress, podRequest.DeviceID, nicType, latency, limit, loss, gatewayCheckMode, u2oInterconnectionIP)
err = csh.configureNic(podRequest.PodName, podRequest.PodNamespace, podRequest.Provider, podRequest.NetNs, podRequest.ContainerID, podRequest.VfDriver, ifName, macAddr, mtu, ipAddr, gw, isDefaultRoute, detectIPConflict, routes, podRequest.DNS.Nameservers, podRequest.DNS.Search, ingress, egress, podRequest.DeviceID, nicType, latency, limit, loss, jitter, gatewayCheckMode, u2oInterconnectionIP)
}
if err != nil {
errMsg := fmt.Errorf("configure nic failed %v", err)
Expand Down
9 changes: 4 additions & 5 deletions pkg/daemon/ovs_linux.go
Expand Up @@ -62,7 +62,7 @@ func (csh cniServerHandler) configureDpdkNic(podName, podNamespace, provider, ne
return nil
}

func (csh cniServerHandler) configureNic(podName, podNamespace, provider, netns, containerID, vfDriver, ifName, mac string, mtu int, ip, gateway string, isDefaultRoute, detectIPConflict bool, routes []request.Route, dnsServer, dnsSuffix []string, ingress, egress, DeviceID, nicType, latency, limit, loss string, gwCheckMode int, u2oInterconnectionIP string) error {
func (csh cniServerHandler) configureNic(podName, podNamespace, provider, netns, containerID, vfDriver, ifName, mac string, mtu int, ip, gateway string, isDefaultRoute, detectIPConflict bool, routes []request.Route, dnsServer, dnsSuffix []string, ingress, egress, DeviceID, nicType, latency, limit, loss, jitter string, gwCheckMode int, u2oInterconnectionIP string) error {
var err error
var hostNicName, containerNicName string
if DeviceID == "" {
Expand Down Expand Up @@ -105,7 +105,7 @@ func (csh cniServerHandler) configureNic(podName, podNamespace, provider, netns,
return err
}

if err = ovs.SetNetemQos(podName, podNamespace, ifaceID, latency, limit, loss); err != nil {
if err = ovs.SetNetemQos(podName, podNamespace, ifaceID, latency, limit, loss, jitter); err != nil {
return err
}

Expand Down Expand Up @@ -230,7 +230,6 @@ func configureContainerNic(nicName, ifName string, ipAddr, gateway string, isDef
}

return ns.WithNetNSPath(netns.Path(), func(_ ns.NetNS) error {

if nicType != util.InternalType {
if err = netlink.LinkSetName(containerLink, ifName); err != nil {
return err
Expand Down Expand Up @@ -1206,7 +1205,7 @@ func renameLink(curName, newName string) error {
return nil
}

func (csh cniServerHandler) configureNicWithInternalPort(podName, podNamespace, provider, netns, containerID, ifName, mac string, mtu int, ip, gateway string, isDefaultRoute, detectIPConflict bool, routes []request.Route, dnsServer, dnsSuffix []string, ingress, egress, DeviceID, nicType, latency, limit, loss string, gwCheckMode int, u2oInterconnectionIP string) (string, error) {
func (csh cniServerHandler) configureNicWithInternalPort(podName, podNamespace, provider, netns, containerID, ifName, mac string, mtu int, ip, gateway string, isDefaultRoute, detectIPConflict bool, routes []request.Route, dnsServer, dnsSuffix []string, ingress, egress, DeviceID, nicType, latency, limit, loss, jitter string, gwCheckMode int, u2oInterconnectionIP string) (string, error) {
_, containerNicName := generateNicName(containerID, ifName)
ipStr := util.GetIpWithoutMask(ip)
ifaceID := ovs.PodNameToPortName(podName, podNamespace, provider)
Expand Down Expand Up @@ -1234,7 +1233,7 @@ func (csh cniServerHandler) configureNicWithInternalPort(podName, podNamespace,
return containerNicName, err
}

if err = ovs.SetNetemQos(podName, podNamespace, ifaceID, latency, limit, loss); err != nil {
if err = ovs.SetNetemQos(podName, podNamespace, ifaceID, latency, limit, loss, jitter); err != nil {
return containerNicName, err
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/daemon/ovs_windows.go
Expand Up @@ -22,11 +22,11 @@ func (csh cniServerHandler) configureDpdkNic(podName, podNamespace, provider, ne
return errors.New("DPDK is not supported on Windows")
}

func (csh cniServerHandler) configureNicWithInternalPort(podName, podNamespace, provider, netns, containerID, ifName, mac string, mtu int, ip, gateway string, isDefaultRoute, detectIPConflict bool, routes []request.Route, dnsServer, dnsSuffix []string, ingress, egress, DeviceID, nicType, latency, limit, loss string, gwCheckMode int, u2oInterconnectionIP string) (string, error) {
return ifName, csh.configureNic(podName, podNamespace, provider, netns, containerID, "", ifName, mac, mtu, ip, gateway, isDefaultRoute, detectIPConflict, routes, dnsServer, dnsSuffix, ingress, egress, DeviceID, nicType, latency, limit, loss, gwCheckMode, u2oInterconnectionIP)
func (csh cniServerHandler) configureNicWithInternalPort(podName, podNamespace, provider, netns, containerID, ifName, mac string, mtu int, ip, gateway string, isDefaultRoute, detectIPConflict bool, routes []request.Route, dnsServer, dnsSuffix []string, ingress, egress, DeviceID, nicType, latency, limit, loss, jitter string, gwCheckMode int, u2oInterconnectionIP string) (string, error) {
return ifName, csh.configureNic(podName, podNamespace, provider, netns, containerID, "", ifName, mac, mtu, ip, gateway, isDefaultRoute, detectIPConflict, routes, dnsServer, dnsSuffix, ingress, egress, DeviceID, nicType, latency, limit, loss, jitter, gwCheckMode, u2oInterconnectionIP)
}

func (csh cniServerHandler) configureNic(podName, podNamespace, provider, netns, containerID, vfDriver, ifName, mac string, mtu int, ip, gateway string, isDefaultRoute, detectIPConflict bool, routes []request.Route, dnsServer, dnsSuffix []string, ingress, egress, DeviceID, nicType, latency, limit, loss string, gwCheckMode int, u2oInterconnectionIP string) error {
func (csh cniServerHandler) configureNic(podName, podNamespace, provider, netns, containerID, vfDriver, ifName, mac string, mtu int, ip, gateway string, isDefaultRoute, detectIPConflict bool, routes []request.Route, dnsServer, dnsSuffix []string, ingress, egress, DeviceID, nicType, latency, limit, loss, jitter string, gwCheckMode int, u2oInterconnectionIP string) error {
if DeviceID != "" {
return errors.New("SR-IOV is not supported on Windows")
}
Expand Down
25 changes: 16 additions & 9 deletions pkg/ovs/ovs-vsctl_linux.go
Expand Up @@ -204,9 +204,11 @@ func SetQosQueueBinding(podName, podNamespace, ifName, iface, queueUid string, q
}

// The latency value expressed in us.
func SetNetemQos(podName, podNamespace, iface, latency, limit, loss string) error {
func SetNetemQos(podName, podNamespace, iface, latency, limit, loss, jitter string) error {
latencyMs, _ := strconv.Atoi(latency)
latencyUs := latencyMs * 1000
jitterMs, _ := strconv.Atoi(jitter)
jitterUs := jitterMs * 1000
limitPkts, _ := strconv.Atoi(limit)
lossPercent, _ := strconv.ParseFloat(loss, 64)

Expand All @@ -225,13 +227,16 @@ func SetNetemQos(podName, podNamespace, iface, latency, limit, loss string) erro
if latencyMs > 0 {
qosCommandValues = append(qosCommandValues, fmt.Sprintf("other_config:latency=%d", latencyUs))
}
if jitterMs > 0 {
qosCommandValues = append(qosCommandValues, fmt.Sprintf("other_config:jitter=%d", jitterUs))
}
if limitPkts > 0 {
qosCommandValues = append(qosCommandValues, fmt.Sprintf("other_config:limit=%d", limitPkts))
}
if lossPercent > 0 {
qosCommandValues = append(qosCommandValues, fmt.Sprintf("other_config:loss=%v", lossPercent))
}
if latencyMs > 0 || limitPkts > 0 || lossPercent > 0 {
if latencyMs > 0 || limitPkts > 0 || lossPercent > 0 || jitterMs > 0 {
if len(qosList) == 0 {
qosCommandValues = append(qosCommandValues, "type=linux-netem", fmt.Sprintf(`external-ids:iface-id="%s"`, iface))
if podNamespace != "" && podName != "" {
Expand All @@ -257,13 +262,13 @@ func SetNetemQos(podName, podNamespace, iface, latency, limit, loss string) erro
return nil
}

latencyVal, lossVal, limitVal, err := getNetemQosConfig(qos)
latencyVal, lossVal, limitVal, jitterVal, err := getNetemQosConfig(qos)
if err != nil {
klog.Errorf("failed to get other_config for qos %s: %v", qos, err)
return err
}

if latencyVal == strconv.Itoa(latencyUs) && limitVal == limit && lossVal == loss {
if latencyVal == strconv.Itoa(latencyUs) && limitVal == limit && lossVal == loss && jitterVal == strconv.Itoa(jitterUs) {
klog.Infof("no value changed for netem qos, ignore")
continue
}
Expand Down Expand Up @@ -302,16 +307,16 @@ func SetNetemQos(podName, podNamespace, iface, latency, limit, loss string) erro
return nil
}

func getNetemQosConfig(qosId string) (string, string, string, error) {
var latency, loss, limit string
func getNetemQosConfig(qosId string) (string, string, string, string, error) {
var latency, loss, limit, jitter string

config, err := ovsGet("qos", qosId, "other_config", "")
if err != nil {
klog.Errorf("failed to get other_config for qos %s: %v", qosId, err)
return latency, loss, limit, err
return latency, loss, limit, jitter, err
}
if len(config) == 0 {
return latency, loss, limit, nil
return latency, loss, limit, jitter, nil
}

values := strings.Split(strings.Trim(config, "{}"), ",")
Expand All @@ -324,9 +329,11 @@ func getNetemQosConfig(qosId string) (string, string, string, error) {
loss = strings.TrimSpace(records[1])
case "limit":
limit = strings.TrimSpace(records[1])
case "jitter":
jitter = strings.TrimSpace(records[1])
}
}
return latency, loss, limit, nil
return latency, loss, limit, jitter, nil
}

func deleteNetemQosById(qosId, iface, podName, podNamespace string) error {
Expand Down

0 comments on commit 47a7055

Please sign in to comment.