Skip to main content

A package to facilitate network simulations

Project description

* μNET (munet)

[[ci-badge-main][https://github.com/LabNConsulting/munet/actions/workflows/ci.yml/badge.svg?branch=main]] [[https://codecov.io/gh/LabNConsulting/munet][https://codecov.io/gh/LabNConsulting/munet/branch/main/graph/badge.svg?token=FD2O4YGDTT]]

A package for creating network topologies and running programs and containers
within them using linux namepsaces.

Munet can be run in a standalone mode with a configuration file for launching
linux shell and container based topologies, as well as be used as a library from
within another application to provide the same functionality.

* Standalone Config

The standalone config can be provided in a number of formats, limited by the
available encode/decode libraries withing the python environment. As JSON is
built in to python that format is always supported. Additionally YAML and TOML
are supported if the corresponding packages are available (i.e., ~PyYAML~ and
~toml~).

The config itself is defined with a YANG model which is defined in the following
sections.

** Config Tree

#+NAME: Munet standalone config YANG tree diagram
#+HEADER: :var module=labn-munet-config
#+begin_src shell :results output verbatim replace :wrap example :exports results
[ -d /yang ] || DOCKER="docker run --net=host -v $(pwd):/work labn/org-rfc"
$DOCKER pyang --tree-line-length=69 -f tree ${module} 2> err.out;
#+end_src


#+RESULTS: Munet standalone config YANG tree diagram
#+begin_example
module: labn-munet-config
+--rw cli
| +--rw commands* [name]
| +--rw exec? string
| +--rw exec-kind* [kind]
| | +--rw kind string
| | +--rw exec? string
| +--rw format? string
| +--rw help? string
| +--rw kinds* -> ../../../kinds/name
| +--rw name string
| +--rw new-window? boolean
| +--rw top-level? boolean
+--rw kinds* [name]
| +--rw merge* string
| +--rw cap-add* string
| +--rw cap-remove* string
| +--rw cmd? string
| +--rw cleanup_cmd? string
| +--rw connections* [to]
| | +--rw to string
| | +--rw ip? string
| | +--rw name? string
| | +--rw hostintf? string
| | +--rw physical? 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 env* [name]
| | +--rw name string
| | +--rw value? string
| +--rw image? string
| +--rw init? union
| +--rw mounts* [destination]
| | +--rw destination string
| | +--rw source? string
| | +--rw tmpfs-size? string
| | +--rw type? string
| +--rw name string
| +--rw podman
| | +--rw extra-args* string
| +--rw privileged? boolean
| +--rw shell? union
| +--rw volumes* string
+--rw topology
| +--rw ipv6-enable? boolean
| +--rw networks-autonumber? boolean
| +--rw networks* [name]
| | +--rw name string
| | +--rw ip? string
| +--rw nodes* [name]
| +--rw id? uint32
| +--rw kind? -> ../../../kinds/name
| +--rw cap-add* string
| +--rw cap-remove* string
| +--rw cmd? string
| +--rw cleanup_cmd? string
| +--rw connections* [to]
| | +--rw to string
| | +--rw ip? string
| | +--rw name? string
| | +--rw hostintf? string
| | +--rw physical? 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 env* [name]
| | +--rw name string
| | +--rw value? string
| +--rw image? string
| +--rw init? union
| +--rw mounts* [destination]
| | +--rw destination string
| | +--rw source? string
| | +--rw tmpfs-size? string
| | +--rw type? string
| +--rw name string
| +--rw podman
| | +--rw extra-args* string
| +--rw privileged? boolean
| +--rw shell? union
| +--rw volumes* string
+--rw version? uint32
#+end_example


** Config Model

#+NAME: test-validate-module
#+begin_src emacs-lisp :exports none
(org-sbe "validate-module" (module labn-munet-config))
#+end_src

#+RESULTS: test-validate-module

#+NAME: labn-munet-config
#+HEADER: :var dep1=dep-babel :var dep2=fetch-yang-files
#+HEADER: :file labn-munet-config.yang :results output file silent :cache yes
#+begin_src yang :exports code
module labn-munet-config {
yang-version 1.1;
namespace "urn:labn:yang:labn-munet-config";
prefix c;

organization
"LabN Consulting, L.L.C.";

contact
"Author: Christian Hopps
<mailto:chopps@labn.net>";

description
"This module defines the configuration state for munet.";

revision 2021-12-18 {
description "Initial Revision";
reference "https://github.com/LabNConsulting/munet/blob/main/README.md";
}

typedef number64 {
type union {
type uint64;
type string {
pattern '[0-9]+([KMGTPE]i?)?';
}
}
description
"A number with optional suffix, where suffix means:
K -> value*10^3, Ki -> value*2^10,
M -> value*10^6, Mi -> value*2^20,
G -> value*10^9, Gi -> value*2^30,
T -> value*10^12, Gi -> value*2^40,
P -> value*10^15, Gi -> value*2^50,
E -> value*10^18, Gi -> value*2^60";
}

grouping intf-constraints {
description "traffic control based interface constraints";
leaf delay {
type uint64;
description "number of microseconds of delay";
}
leaf jitter {
type uint64;
must "../delay";
description "number of microseconds of possible jitter";
}
leaf jitter-correlation {
type decimal64 {
fraction-digits 16;
range "0..100";
}
must "../jitter";
description "percent correlation between consecutive jitter values";
}
leaf loss {
type uint64;
must "../delay";
description "number of microseconds of possible jitter";
}
leaf loss-correlation {
type decimal64 {
fraction-digits 16;
range "0..100";
}
must "../loss";
description "percent correlation between consecutive loss values";
}
container rate {
description "bits per second maximum rate with possible limit and burst";
leaf rate {
type number64;
description "bits per second maximum rate";
}
leaf limit {
type number64;
must "../rate";
description "bits per second maximum rate";
}
leaf burst {
type number64;
must "../rate";
description "bits per second maximum rate";
}
}
}

grouping common-node {
description "Common node properties";
leaf-list cap-add {
type string;
description "Capabilities to add to a container.";
reference "https://man7.org/linux/man-pages/man7/capabilities.7.html";
}
leaf-list cap-remove {
type string;
description "Capabilities to remove from a container.";
reference "https://man7.org/linux/man-pages/man7/capabilities.7.html";
}
leaf cmd {
type string;
description "Shell command[s] to execute when creating the node.";
}
leaf cleanup_cmd {
type string;
description "Shell command[s] to execute when deleting the node.";
}
list connections {
key to;
description "Connections to other networks or nodes from this node";

leaf to {
type string;
description "The target of this connection.";
}
leaf ip {
type string;
description "IP address for the connection (interface).";
}
leaf name {
type string;
description "Name for the connection (interface name).";
}
leaf hostintf {
type string;
description "Host interface name for wired connections";
}
leaf physical {
type string;
description "Physical interface name for wired connections";
}
leaf remote-name {
type string;
description
"The remote name of a p2p connection. This is used for disambiguation
when there are multiple point-to-point connections to the same
remote node.";
}
uses intf-constraints;
}
list env {
key name;
description
"List of environment variable to add to the `cmd` execution
environment";
leaf name {
type string;
description "Environment variable name.";
}
leaf value {
type string;
description "Environment variable value.";
}
}
leaf image {
type string;
description "Container image specification.";
}
leaf init {
type union {
type boolean;
type string;
}
description "Controls use of an init process.";
}
list mounts {
key destination;
description
"Mounts to be made inside the namespace. Currently only supported for
container based nodes.";

leaf destination {
type string;
description
"The inner mount point. If no source is given this will be a tmpfs
mount, otherwise the it is a bind mount from the `source`.";
}
leaf source {
type string;
description "The source of the bind mount.";
}
leaf tmpfs-size {
type string;
description "The size of the tmpfs.";
}
leaf type {
type string;
description "The type of the mount (currently bind or tmpfs).";
}
}
leaf name {
type string;
description "Name of this node or kind.";
}
container podman {
description "Configuration related to podman containers.";
leaf-list extra-args {
type string;
description "list of CLI arguments to add to the podman run command.";
}
}
leaf privileged {
type boolean;
description "Controls running the container in privileged mode.";
}
leaf shell {
type union {
type boolean;
type string;
}
description
"Controls use of an shell process for `cmd` execution. If 'false' then
`cmd` will be run directly with exec(1), otherwise a shell will be
used. If this value is `true` then the default shell will be used,
otherwise it is a string which specifies the path to the shell to
use.";
}
leaf-list volumes {
type string;
description
"Bind or tmpfs mounts. For bind mounts the format of the string is
<outer>:<inner>, for tmpfs it's simply the inner mount path.";
}
}

container cli {
description "CLI additions.";
list commands {
key name;
description "A command to add to the CLI.";

leaf exec {
type string;
description
"Command to execute when the CLI command is given. The string is
evaluated as a python f-string with `{host}` set to the current
host object (or None) `{unet}` set to the Munet object, and
`{user_input}` to any user input that follows the command (or '' if
none specified).";
}
list exec-kind {
key kind;
description "A kind specific execution formats.";

leaf kind {
type string;
description "Kind for which this command format should be used.";
}
leaf exec {
type string;
description
"Command to execute when the CLI command is given. The string is
evaluated as a python f-string with `{host}` set to the current
host object (or None) `{unet}` set to the Munet object, and
`{user_input}` to any user input that follows the command (or ''
if none specified).";
}
}
leaf format {
type string;
description
"The format of the command. Used to print help string for user.";
}
leaf help {
type string;
description
"The description of the command. Used to print help string for
user.";
}
leaf-list kinds {
type leafref {
path "../../../kinds/name";
}
description
"List of kinds for which this command should be restricted to running
on.";
}
leaf name {
type string;
description "CLI command name.";
}
leaf new-window {
type boolean;
description
"Controls if the command runs in the CLI window or opens a new
terminal window to run the command in.";
}
leaf top-level {
type boolean;
default false;
description
"If true the command is run in the top-level containing namespace. This is the
namespace from which each of the hosts allocated sub-namespaces from.
or a host namespace.";
}
}
}

list kinds {
key name;
description
"List of kinds used to group and share common node properities.";

leaf-list merge {
type string;
description
"List of properties which should be merged with their node specific
values, rather than being replaced by the node specific version.";
}
uses common-node;
}

container topology {
description "The topology munet should create.";

leaf ipv6-enable {
type boolean;
default false;
description
"Controls if IPv6 is enabled or disabled.";
}

leaf networks-autonumber {
type boolean;
description
"Controls if networks and node connections are given IP addresses if
not explicitly configured.";
}

list networks {
key name;
description "List of networks to create.";

leaf name {
type string {
length "1..11";
pattern "[-a-zA-Z0-9_]+";
}
description "Name of the network";
}
leaf ip {
type string;
description
"IP prefix for the network. If host bit's are set then the linux
bridge will be assigned that IP.";
}
}

list nodes {
key name;
description "Nodes in the topology.";

leaf id {
type uint32;
description "Explicitly set the ID for the node.";
}
leaf kind {
type leafref {
path "../../../kinds/name";
}
description
"Indicate the kind of this node, which pulls in the properies of that
`kind` for this node.";
}
uses common-node;
}
}
leaf version {
type uint32;
description "version of this config";
}
}
#+end_src

#+name: dep-babel
#+begin_src emacs-lisp :results none :exports none
(org-babel-do-load-languages 'org-babel-load-languages '((shell . t)))
(setq fill-column 69)
(setq org-confirm-babel-evaluate nil)
#+end_src

#+name: fetch-yang-files
#+begin_src shell :results none silent :exports none
curl -O https://raw.githubusercontent.com/YangModels/yang/master/standard/ietf/RFC/ietf-routing-types@2017-12-04.yang
curl -O https://raw.githubusercontent.com/YangModels/yang/master/standard/ietf/RFC/ietf-routing@2018-03-13.yang
#+end_src

#+NAME: generate-tree
#+HEADER: :var dep1=dep-babel
#+begin_src shell :results output verbatim replace :wrap example :exports results
[ -d /yang ] || DOCKER="docker run --net=host -v $(pwd):/work labn/org-rfc"
$DOCKER pyang --tree-line-length=69 -f tree ${module} 2> err.out;
#+end_src

#+NAME: validate-module
#+HEADER: :var dep1=dep-babel
#+begin_src bash :results output verbatim replace :wrap comment :exports none
[ -d /yang ] || DOCKER="docker run --net=host -v $(pwd):/work labn/org-rfc"
if ! $DOCKER pyang --lax-quote-checks -Werror --lint $module 2>&1; then echo FAIL; fi
#+end_src

#+NAME: validate-config
#+HEADER: :var dep1=dep-babel
#+begin_src bash :results output verbatim replace :wrap comment :exports none
[ -d /yang ] || DOCKER="docker run --net=host -v $(pwd):/work labn/org-rfc"
LINT="$DOCKER yanglint -p /yang-drafts -p /yang --strict -t config"
$LINT $extra $module ${file} 2>&1 || echo FAIL
#+end_src

#+NAME: validate-data
#+HEADER: :var dep1=dep-babel
#+begin_src bash :results output verbatim replace :wrap comment :exports none
[ -d /yang ] || DOCKER="docker run --net=host -v $(pwd):/work labn/org-rfc"
LINT="$DOCKER yanglint -p /yang-drafts -p /yang --strict -t data"
$LINT $extra $module ${file} 2>&1 || echo FAIL
#+end_src

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

munet-0.8.34.tar.gz (67.7 kB view hashes)

Uploaded Source

Built Distribution

munet-0.8.34-py3-none-any.whl (71.7 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page