Skip to content

Commit

Permalink
Merge branch 'srl-labs:main' into vscode-containerlab-topoviewer-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
asadarafat authored Feb 14, 2025
2 parents ed66714 + 6495894 commit 34e1853
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 58 deletions.
88 changes: 81 additions & 7 deletions cmd/tools_netem.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ package cmd

import (
"context"
"encoding/json"
"fmt"
"math"
"net"
"os"
"os/exec"
"strconv"
"time"

Expand All @@ -24,6 +26,7 @@ import (
"github.com/srl-labs/containerlab/internal/tc"
"github.com/srl-labs/containerlab/links"
"github.com/srl-labs/containerlab/runtime"
"github.com/srl-labs/containerlab/types"
"github.com/vishvananda/netlink"
)

Expand All @@ -35,6 +38,7 @@ var (
netemLoss float64
netemRate uint64
netemCorruption float64
netemFormat string
)

func init() {
Expand All @@ -58,6 +62,7 @@ func init() {

netemCmd.AddCommand(netemShowCmd)
netemShowCmd.Flags().StringVarP(&netemNode, "node", "n", "", "node to apply impairment to")
netemShowCmd.Flags().StringVarP(&netemFormat, "format", "f", "table", "output format (table, json)")
}

var netemCmd = &cobra.Command{
Expand All @@ -83,6 +88,11 @@ var netemShowCmd = &cobra.Command{
}

func netemSetFn(_ *cobra.Command, _ []string) error {
// Ensure that the sch_netem kernel module is loaded (for Fedora/RHEL compatibility)
if err := exec.Command("modprobe", "sch_netem").Run(); err != nil {
log.Warn("failed to load sch_netem kernel module (expected on OrbStack machines)", "err", err)
}

// Get the runtime initializer.
_, rinit, err := clab.RuntimeInitializer(common.Runtime)
if err != nil {
Expand Down Expand Up @@ -249,6 +259,58 @@ func qdiscToTableData(qdisc gotc.Object) tableWriter.Row {
}
}

// qdiscToJSONData converts the full qdisc object to a simplified view.
func qdiscToJSONData(qdisc gotc.Object) types.ImpairmentData {
link, err := netlink.LinkByIndex(int(qdisc.Ifindex))
if err != nil {
log.Errorf("could not get netlink interface by index: %v", err)
}

var delay, jitter string
var loss, corruption float64
var rate int

ifDisplayName := link.Attrs().Name
if link.Attrs().Alias != "" {
ifDisplayName += fmt.Sprintf(" (%s)", link.Attrs().Alias)
}

// Return "N/A" values when netem is not set.
if qdisc.Netem == nil {
return types.ImpairmentData{
Interface: ifDisplayName,
}
}

if qdisc.Netem.Latency64 != nil && *qdisc.Netem.Latency64 != 0 {
delay = (time.Duration(*qdisc.Netem.Latency64) * time.Nanosecond).String()
}
if qdisc.Netem.Jitter64 != nil && *qdisc.Netem.Jitter64 != 0 {
jitter = (time.Duration(*qdisc.Netem.Jitter64) * time.Nanosecond).String()
}
if qdisc.Netem.Rate != nil && int(qdisc.Netem.Rate.Rate) != 0 {
rate = int(qdisc.Netem.Rate.Rate * 8 / 1000)
}
if qdisc.Netem.Corrupt != nil && qdisc.Netem.Corrupt.Probability != 0 {
// round to 2 decimal places
corruption = math.Round((float64(qdisc.Netem.Corrupt.Probability)/
float64(math.MaxUint32)*100)*100) / 100
}
if qdisc.Netem.Qopt.Loss != 0 {
// round to 2 decimal places
loss = math.Round((float64(qdisc.Netem.Qopt.Loss)/float64(math.MaxUint32)*100)*100) / 100
}

return types.ImpairmentData{
Interface: ifDisplayName,
Delay: delay,
Jitter: jitter,
PacketLoss: loss,
Rate: rate,
Corruption: corruption,
}
}

func netemShowFn(_ *cobra.Command, _ []string) error {
// Get the runtime initializer.
_, rinit, err := clab.RuntimeInitializer(common.Runtime)
Expand All @@ -258,8 +320,6 @@ func netemShowFn(_ *cobra.Command, _ []string) error {

// init the runtime
rt := rinit()

// init runtime with timeout
err = rt.Init(
runtime.WithConfig(
&runtime.RuntimeConfig{
Expand All @@ -274,14 +334,13 @@ func netemShowFn(_ *cobra.Command, _ []string) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// retrieve the containers NSPath
// retrieve the container's NSPath
nodeNsPath, err := rt.GetNSPath(ctx, netemNode)
if err != nil {
return err
}

var nodeNs ns.NetNS

if nodeNs, err = ns.GetNS(nodeNsPath); err != nil {
return err
}
Expand All @@ -290,10 +349,9 @@ func netemShowFn(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}

defer func() {
if err := tcnl.Close(); err != nil {
log.Errorf("could not close rtnetlink socket: %v\n", err)
log.Errorf("could not close rtnetlink socket: %v", err)
}
}()

Expand All @@ -303,7 +361,23 @@ func netemShowFn(_ *cobra.Command, _ []string) error {
return err
}

printImpairments(qdiscs)
if netemFormat == "json" {
var impairments []types.ImpairmentData
for _, q := range qdiscs {
impairments = append(impairments, qdiscToJSONData(q))
}
// Structure output as a map keyed by the node name.
outputData := map[string][]types.ImpairmentData{
netemNode: impairments,
}
jsonData, err := json.MarshalIndent(outputData, "", " ")
if err != nil {
return fmt.Errorf("error marshalling JSON: %v", err)
}
fmt.Println(string(jsonData))
} else {
printImpairments(qdiscs)
}

return nil
})
Expand Down
67 changes: 67 additions & 0 deletions docs/cmd/tools/netem/show.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ containerlab tools netem show [local-flags]

With the mandatory `--node | -n` flag a user specifies the name of the containerlab node to show link impairments on.

### format

The optional --format | -f flag can be used to choose the output format. The default value is table, which displays the output in a formatted table. Specifying json returns the link impairment details in JSON format.

## Examples

### Showing link impairments for a node
Expand All @@ -30,3 +34,66 @@ containerlab tools netem show -n clab-netem-r1
| eth1 | 15ms | 2ms | 0.00 | 0 |
+-----------+-------+--------+-------------+-------------+
```

### Showing link impairments for a node in json format

When displaying the netem details in json format, the fields have the following types:

* delay - string with the time suffix (ms, s, etc)
* jitter - string with the time suffix (ms, s, etc)
* packet_loss - a value with a floating point and 2 decimal places
* rate - an integer value expressed in kbit/s
* corruption - a value with a floating point and 2 decimal places

```bash
containerlab tools netem show -n srl --format json
```

<div class="embed-result">
```json
{
"srl": [
{
"interface": "lo",
"delay": "",
"jitter": "",
"packet_loss": 0,
"rate": 0,
"corruption": 0
},
{
"interface": "mgmt0",
"delay": "1s",
"jitter": "5ms",
"packet_loss": 0.1,
"rate": 0,
"corruption": 0.2
},
{
"interface": "gway-2800",
"delay": "",
"jitter": "",
"packet_loss": 0,
"rate": 0,
"corruption": 0
},
{
"interface": "monit_in",
"delay": "",
"jitter": "",
"packet_loss": 0,
"rate": 0,
"corruption": 0
},
{
"interface": "mgmt0-0 (mgmt0.0)",
"delay": "",
"jitter": "",
"packet_loss": 0,
"rate": 0,
"corruption": 0
}
]
}
```
</div>
2 changes: 1 addition & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ sudo chmod u+s `which containerlab`
# Create clab_admins Unix group
sudo groupadd -r clab_admins
# Add current user to clab_admins group
sudo usermod -aG "$USER" clab_admins
sudo usermod -aG clab_admins "$USER"
```

Users who manage their Containerlab installation via `deb/yum/dnf` package managers will have the sudo-less functionality automatically enabled during the first upgrade from pre-`0.63.0` versions.
Expand Down
2 changes: 1 addition & 1 deletion docs/lab-examples/ixiacone-srl.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
| **Resource requirements**[^1] | :fontawesome-solid-microchip: 2 <br/>:fontawesome-solid-memory: 2 GB |
| **Topology file** | [ixiacone-srl.clab.yaml][topofile] |
| **Name** | ixiac01 |
| **Version information**[^2] | `containerlab:0.46.2`, `ixia-c-one:1.19.0-18`, `srlinux:23.10.1`, `docker-ce:20.10.2` |
| **Version information**[^2] | `containerlab:0.46.2`, `ixia-c-one:1.20.0-6`, `srlinux:23.10.1`, `docker-ce:20.10.2` |

## Description

Expand Down
2 changes: 1 addition & 1 deletion get.sh
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ installFile() {
runAsRoot install -m 4755 "$TMP_ROOT/$BINARY_NAME" "$BIN_INSTALL_DIR/$BINARY_NAME"
# Add clab admins group and add current user to it
runAsRoot groupadd -r clab_admins
runAsRoot usermod -aG "$SUDO_USER" clab_admins
runAsRoot usermod -aG clab_admins "$SUDO_USER"
runAsRoot touch /etc/containerlab/suid_setup_done
fi
echo "$BINARY_NAME installed into $BIN_INSTALL_DIR/$BINARY_NAME"
Expand Down
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ require (
github.com/vishvananda/netlink v1.3.0
github.com/weaveworks/ignite v0.10.0
go.uber.org/mock v0.5.0
golang.org/x/crypto v0.32.0
golang.org/x/crypto v0.33.0
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329
golang.org/x/sys v0.29.0
golang.org/x/term v0.28.0
golang.org/x/sys v0.30.0
golang.org/x/term v0.29.0
gopkg.in/yaml.v2 v2.4.0
sigs.k8s.io/kind v0.26.0
)
Expand Down Expand Up @@ -224,10 +224,10 @@ require (
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
golang.org/x/mod v0.22.0
golang.org/x/mod v0.23.0
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.28.0 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
Expand Down
22 changes: 12 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1123,8 +1123,8 @@ golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand All @@ -1144,8 +1144,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -1209,8 +1209,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -1282,8 +1283,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand All @@ -1294,8 +1295,8 @@ golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -1308,8 +1309,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
14 changes: 7 additions & 7 deletions lab-examples/ixiac01/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ go 1.22.7

toolchain go1.23.0

require github.com/open-traffic-generator/snappi/gosnappi v1.19.0
require github.com/open-traffic-generator/snappi/gosnappi v1.20.0

require (
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/grpc v1.69.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Loading

0 comments on commit 34e1853

Please sign in to comment.