Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,32 @@ module: labn-munet-config
| +--rw initial-setup-cmd? string
| +--rw initial-setup-host-cmd? string
| +--rw networks* [name]
| | +--rw name string
| | +--rw ip? string
| | +--rw ipv6? string
| | +--rw external? boolean
| | +--rw name string
| | +--rw ip? string
| | +--rw ipv6? string
| | +--rw external? boolean
| | +--rw delay? uint64
| | +--rw jitter? uint64
| | +--rw jitter-correlation? decimal64
| | +--rw loss? uint64
| | +--rw loss-correlation? decimal64
| | +--rw rate
| | | +--rw rate? number64
| | | +--rw limit? number64
| | | +--rw burst? number64
| | +--rw connections* [to]
| | +--rw to string
| | +--rw name? string
| | +--rw remote-name? string
| | +--rw delay? uint64
| | +--rw jitter? uint64
| | +--rw jitter-correlation? decimal64
| | +--rw loss? uint64
| | +--rw loss-correlation? decimal64
| | +--rw rate
| | +--rw rate? number64
| | +--rw limit? number64
| | +--rw burst? number64
| +--rw nodes* [name]
| +--rw id? uint32
| +--rw kind? -> ../../../kinds/name
Expand Down Expand Up @@ -1103,6 +1125,34 @@ munet>
This is most useful when adding host interfaces to nodes as the
connection point.";
}

/* default network tc parameters */
uses intf-constraints;

list connections {
key to;
description
"Overriding network side configuration for connections to nodes from
this network. If the default network tc parameters are not being
overriden for a specific node connection then this configuration is
not required.";

leaf to {
type string;
description "The target of this connection.";
}
leaf name {
type string;
description "Name for the connection (interface name).";
}
leaf remote-name {
type string;
description
"The remote name of the connection. This is used for disambiguation
when there are multiple connections to the same remote node.";
}
uses intf-constraints;
}
}

list nodes {
Expand Down Expand Up @@ -1153,4 +1203,3 @@ munet>
[ -d /yang ] || DOCKER="sudo podman run --net=host -v $(pwd):/work docker.io/labn/org-rfc"
if ! $DOCKER pyang -P build --lax-quote-checks -Werror --lint $module 2>&1; then echo FAIL; fi
#+end_src

37 changes: 33 additions & 4 deletions doc/source/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,39 @@ Networks

Tree diagram for network config::

+--rw topology
| +--rw networks* [name]
| | +--rw name string
| | +--rw ip? string
+--rw topology
+--rw networks* [name]
+--rw name string
+--rw ip? string
+--rw ipv6? string
+--rw external? boolean
+--rw delay? uint64
+--rw jitter? uint64
+--rw jitter-correlation? decimal64
+--rw loss? uint64
+--rw loss-correlation? decimal64
+--rw rate
| +--rw rate? number64
| +--rw limit? number64
| +--rw burst? number64
+--rw connections* [to]
+--rw to string
+--rw name? string
+--rw remote-name? string
+--rw delay? uint64
+--rw jitter? uint64
+--rw jitter-correlation? decimal64
+--rw loss? uint64
+--rw loss-correlation? decimal64
+--rw rate
+--rw rate? number64
+--rw limit? number64
+--rw burst? number64

The ``connections`` configuration is entirely optional, and is present to allow
overriding default network quality parameters (e.g., ``delay``, ``jitter``,
etc). If no override is required it is enough for the nodes to specify their
connection to the network.


Nodes
Expand Down
117 changes: 117 additions & 0 deletions munet/munet-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,123 @@
},
"external": {
"type": "boolean"
},
"delay": {
"type": "integer"
},
"jitter": {
"type": "integer"
},
"jitter-correlation": {
"type": "string"
},
"loss": {
"type": "integer"
},
"loss-correlation": {
"type": "string"
},
"rate": {
"type": "object",
"properties": {
"rate": {
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"limit": {
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"burst": {
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
}
}
},
"connections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"to": {
"type": "string"
},
"name": {
"type": "string"
},
"remote-name": {
"type": "string"
},
"delay": {
"type": "integer"
},
"jitter": {
"type": "integer"
},
"jitter-correlation": {
"type": "string"
},
"loss": {
"type": "integer"
},
"loss-correlation": {
"type": "string"
},
"rate": {
"type": "object",
"properties": {
"rate": {
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"limit": {
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"burst": {
"oneOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
}
}
}
}
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions munet/native.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import subprocess
import time

from copy import deepcopy
from pathlib import Path


Expand Down Expand Up @@ -3192,8 +3193,18 @@ async def _async_build(self, logger=None):
continue
to = cconf["to"]
if to in self.switches:
# Use tc params present in the root of the switch config as the
# default tc values for interfaces added to the bridge which aren't
# present in `connections`.
switch = self.switches[to]
swconf = find_matching_net_config(name, cconf, switch.config)
if not swconf:
# "name" most important key to leave out, so it gets generated
nontc = ("connections", "external", "ip", "ipv6", "name")
swconf = {
k: v for k, v in switch.config.items() if k not in nontc
}
swconf = deepcopy(swconf)
await self.add_native_link(switch, node, swconf, cconf)
elif cconf["name"] not in node.intfs:
# Only add the p2p interface if not already there.
Expand Down
39 changes: 39 additions & 0 deletions tests/config/qdisc/munet.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
version: 1
topology:
networks:
- name: net1
delay: 16
jitter: 30
loss: 40
rate:
rate: 800
connections:
- to: h2
delay: 18
jitter: 28
loss: 38
rate:
rate: 810
- to: h3
delay: 14

nodes:
- name: h1
connections:
- to: net1
delay: 200
jitter: 60
loss: 70
rate:
rate: 1600
- name: h2
connections:
- to: net1
delay: 210
jitter: 60
loss: 70
rate:
rate: 1600
- name: h3
connections:
- to: net1
57 changes: 57 additions & 0 deletions tests/config/qdisc/test_qdisc_cmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
# November 28 2025, Liam Brady <[email protected]>
#
# Copyright 2025, LabN Consulting, L.L.C.
#
"Testing that qdiscs are properly set on network/node interfaces"
import logging
import re

import pytest


# All tests are coroutines
pytestmark = pytest.mark.asyncio


async def test_config_cmd(unet_share):
unet = unet_share

output = unet.cmd_raises("tc q | grep 'dev net1-e0'")
logging.debug("qdisc for dev net1-e0 output found: %s", output)
logging.debug("expects delay='16', jitter='30', loss='40', rate='800'")
assert re.search(r"delay 16us\s+29us.*loss 40%.*rate 800bit", output, re.DOTALL)

output = unet.cmd_raises("tc q | grep 'dev net1-e1'")
logging.debug("qdisc for dev net1-e1 output found: %s", output)
logging.debug("expects delay='6', jitter='20', loss='50', rate='810'")
assert re.search(r"delay 17us\s+27us.*loss 38%.*rate 808bit", output, re.DOTALL)

output = unet.cmd_raises("tc q | grep 'dev net1-e2'")
logging.debug("qdisc for dev net1-e2 output found: %s", output)
logging.debug("expects delay='13'")
assert "delay 13us" in output
assert "loss" not in output
assert "rate" not in output

h1 = unet.hosts["h1"]
output = h1.cmd_raises("tc q | grep 'dev eth0'")
logging.debug("qdisc for dev eth0 output found: %s", output)
logging.debug("expects delay='200', jitter='60', loss='70', rate='1600'")
assert re.search(r"delay 200us\s+59us.*loss 70%.*rate 1600bit", output, re.DOTALL)

h2 = unet.hosts["h2"]
output = h2.cmd_raises("tc q | grep 'dev eth0'")
logging.debug("qdisc for dev eth0 output found: %s", output)
logging.debug("expects delay='209', jitter='60', loss='70', rate='1600'")
assert re.search(r"delay 209us\s+59us.*loss 70%.*rate 1600bit", output, re.DOTALL)

h3 = unet.hosts["h3"]
output = h3.cmd_raises("tc q | grep 'dev eth0'")
logging.debug("qdisc for dev eth0 output found: %s", output)
logging.debug("expects none")
assert "delay" not in output
assert "loss" not in output
assert "rate" not in output
Loading