munet


Namemunet JSON
Version 0.15.4 PyPI version JSON
download
home_pagehttps://github.com/LabNConsulting/munet
SummaryA package to facilitate network simulations
upload_time2025-01-12 16:27:54
maintainerNone
docs_urlNone
authorChristian Hopps
requires_python<4.0,>=3.8
licenseGPL-2.0-or-later
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            #+STARTUP: indent
* μNET (munet)
#+html: <a href="https://github.com/LabNConsulting/munet/actions"><img src="https://github.com/LabNConsulting/munet/actions/workflows/ci.yml/badge.svg?branch=main"></a>
#+html: <a href="https://codecov.io/gh/LabNConsulting/munet" ><img src="https://codecov.io/gh/LabNConsulting/munet/branch/main/graph/badge.svg?token=FD2O4YGDTT"></a>
#+html: <a href="https://munet.readthedocs.io/en/latest/"><img src="https://readthedocs.org/projects/munet/badge?version=latest"></a>
#+html: <p></p>

For better documentation see: https://munet.readthedocs.io/en/latest/

A package for creating network topologies and running programs and containers
within them using linux namepsaces, podman containers and qemu virtual machines.

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 fully defined at the end
of this document. Below is an tree diagram of the overal format of a config file:

#+NAME: Munet standalone config YANG tree diagram
#+CALL: generate-tree(module=labn-munet-config)
# Remove the #+RESULTS: before pushing to git, github is broken and
# won't render it

#+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 interactive?   boolean
  |     +--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 ready-cmd?           string
  |  +--rw image?               string
  |  +--rw hostnet?             boolean
  |  +--rw server?              string
  |  +--rw server-port?         uint16
  |  +--rw ssh-identity-file?   string
  |  +--rw ssh-user?            string
  |  +--rw ssh-password?        string
  |  +--rw qemu
  |  |  +--rw bios?              string
  |  |  +--rw cloud-init?        boolean
  |  |  +--rw cloud-init-disk?   string
  |  |  +--rw disk?              string
  |  |  +--rw disk-driver?       string
  |  |  +--rw disk-template?     string
  |  |  +--rw initial-cmd?       string
  |  |  +--rw kernel?            string
  |  |  +--rw initrd?            string
  |  |  +--rw kvm?               boolean
  |  |  +--rw ncpu?              uint32
  |  |  +--rw memory?            string
  |  |  +--rw root?              string
  |  |  +--rw cmdline-extra?     string
  |  |  +--rw extra-args?        string
  |  |  +--rw console
  |  |     +--rw user?               string
  |  |     +--rw password?           string
  |  |     +--rw initial-password?   string
  |  |     +--rw expects*            string
  |  |     +--rw sends*              string
  |  |     +--rw timeout?            uint32
  |  +--rw connections* [to]
  |  |  +--rw to                    string
  |  |  +--rw ip?                   string
  |  |  +--rw ipv6?                 string
  |  |  +--rw name?                 string
  |  |  +--rw hostintf?             string
  |  |  +--rw physical?             string
  |  |  +--rw remote-name?          string
  |  |  +--rw driver?               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 gdb-cmd?             string
  |  +--rw gdb-target-cmds*     string
  |  +--rw gdb-run-cmds*        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 dns-network?              -> ../networks/name
  |  +--rw ipv6-enable?              boolean
  |  +--rw networks-autonumber?      boolean
  |  +--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 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 ready-cmd?           string
  |     +--rw image?               string
  |     +--rw hostnet?             boolean
  |     +--rw server?              string
  |     +--rw server-port?         uint16
  |     +--rw ssh-identity-file?   string
  |     +--rw ssh-user?            string
  |     +--rw ssh-password?        string
  |     +--rw qemu
  |     |  +--rw bios?              string
  |     |  +--rw cloud-init?        boolean
  |     |  +--rw cloud-init-disk?   string
  |     |  +--rw disk?              string
  |     |  +--rw disk-driver?       string
  |     |  +--rw disk-template?     string
  |     |  +--rw initial-cmd?       string
  |     |  +--rw kernel?            string
  |     |  +--rw initrd?            string
  |     |  +--rw kvm?               boolean
  |     |  +--rw ncpu?              uint32
  |     |  +--rw memory?            string
  |     |  +--rw root?              string
  |     |  +--rw cmdline-extra?     string
  |     |  +--rw extra-args?        string
  |     |  +--rw console
  |     |     +--rw user?               string
  |     |     +--rw password?           string
  |     |     +--rw initial-password?   string
  |     |     +--rw expects*            string
  |     |     +--rw sends*              string
  |     |     +--rw timeout?            uint32
  |     +--rw connections* [to]
  |     |  +--rw to                    string
  |     |  +--rw ip?                   string
  |     |  +--rw ipv6?                 string
  |     |  +--rw name?                 string
  |     |  +--rw hostintf?             string
  |     |  +--rw physical?             string
  |     |  +--rw remote-name?          string
  |     |  +--rw driver?               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 gdb-cmd?             string
  |     +--rw gdb-target-cmds*     string
  |     +--rw gdb-run-cmds*        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

** Examples
*** Two Hosts Topology

A very simple config with 2 hosts connected to a mgmt network.

In this config the networks are autonumbered which starts with
~10.0.0.0/24~. So, ~h1~ will have an ~eth0~ interface with IP
~10.0.0.1~ and ~h2~ will likewise have an ~eth0~ interface, and an IP
of ~10.0.0.2~.

#+begin_src yaml
  topology:
    networks-autonumber: true
    networks:
      - name: net0
    nodes:
      - name: h1
        connections:
          - to: net0
      - name: h2
        connections:
          - to: net0
#+end_src

*** Router VM

NOTE: This example is testing the boundaries of what munet can do
with a node. Its really here to document how to do this very complex
thing. Beginners should probably skip it.

Router VMs are very different from standard unix-like OSs. Munet does a lot of
automatic configuration assuming a unix-like (and mostly Linux) OS. Various
configuration parameters need to be set to tune the automatic configuration and
assumptions down. Here's an example munet config fragment that shows booting a
cisco VM using a nexos file system image.

#+begin_src yaml
  topology:
    networks-autonumber: true
    dns-network: "mgmt0"
    networks:
      - name: mgmt0
        ip: 192.168.0.254/24
        nat: true
      - name: net0
    nodes:
      # ...
      - name: r1
        kind: cisco
        connections:
          - to: "mgmt0"
            name: "eth1"
            driver: "e1000"
          - to: "net0"
            name: "eth2"
            driver: "e1000"
  kinds:
    - name: cisco
      shell: false
      cmd: |
        terminal terminal-type dumb
        terminal length 0
        terminal width 511
        terminal session 0
        conf t
        line console
        exec-timeout 0
        line vty
        exec-timeout 0
        int mgmt0
          ip address 192.168.0.2/24
        exit
        feature ssh
        feature telnet
        end
      qemu:
        unix-os: false
        disk-template: "%CONFIGDIR%/nexus9300v64.10.2.3.F.qcow2"
        disk-driver: "sata"
        bios: "open-firmware"
        memory: "8192M"
        smp: 2
        kvm: true
        console:
          stdio: false
          user: "admin"
          password: ""
          prompt: "(^|\r?\n\r?)switch(\\([^\\)]+\\))?#"
          expects:
            - "skip - bypass.*yes/skip/no\\)\\[no\\]:"
            - "loader > "
          sends:
            - "skip\n"
            - "boot nxos64-cs.10.2.3.F.bin\n"
          timeout: 900

  cli:
    commands:
      - name: ssh
        exec: "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null admin@%IPADDR%"
        kinds: ["cisco"]
        format: "ssh NODE [NODE ...]"
        top-level: true
        new-window: true
#+end_src


** Development

*** Dependencies

μNET requires the following packages:

  python3 python3-venv

Automate tests require the following system packages:

  podman

Ensure you have poetry setup, the following instructions work around some bugs
with poetry:

#+begin_src shell
  python3 -m venv ~/.poetrybin
  source ~/.poetrybin/bin/activate
  pip install poetry
  pip uninstall keyring
#+end_src

NOTE: add "~/.poetrybin/bin" to your $PATH

Install μNET with dependencies:

  poetry install --all-extras

*** Check your install

 make

*** Run an example

The following uses FRR (see https://frrouting.org)

   sudo poetry run munet -c examples/frr/ospf/ospf/munet.yaml

For example:
#+begin_src shell
munet$ sudo poetry run munet -c examples/frr/ospf/ospf/munet.yaml
2022-09-16 13:37:05,603: INFO: Loaded logging config /home/lberger/Code/github/labn/munet/munet/logconf.yaml
2022-09-16 13:37:05,609: INFO: Loaded config from /home/lberger/Code/github/labn/munet/examples/frr/ospf/ospf/munet.yaml
2022-09-16 13:37:05,623: INFO: Loaded kinds config /home/lberger/Code/github/labn/munet/munet/kinds.yaml
2022-09-16 13:37:05,745: INFO: Munet(munet): created
2022-09-16 13:37:05,926: INFO: L3NamespaceNode(r1): created
2022-09-16 13:37:06,086: INFO: L3NamespaceNode(r2): created
2022-09-16 13:37:06,247: INFO: L3NamespaceNode(r3): created
2022-09-16 13:37:06,778: INFO: Topology up: rundir: /tmp/unet-root

--- Munet CLI Starting ---


munet>
munet> help

Basic Commands:
  cli   :: open a secondary CLI window
  help  :: this help
  hosts :: list hosts
  quit  :: quit the cli

  HOST can be a host or one of the following:
    - '*' for all hosts
    - '.' for the parent munet
    - a regex specified between '/' (e.g., '/rtr.*/')

New Window Commands:
  hterm HOST [HOST ...] :: open terminal[s] on HOST[S] (outside containers), * for all
  pcap NETWORK  :: capture packets from NETWORK into file capture-NETWORK.pcap the command is run within a new window which also shows packet summaries
  stdout HOST [HOST ...]        :: tail -f on the stdout of the cmd for this node
  stdout HOST [HOST ...]        :: tail -f on the stdout of the cmd for this node
  term HOST [HOST ...]  :: open terminal[s] (TMUX or XTerm) on HOST[S], * for all
  vtysh ROUTER [ROUTER ...]     ::
  xterm HOST [HOST ...] :: open XTerm[s] on HOST[S], * for all
Inline Commands:
  [ROUTER ...] COMMAND  :: execute vtysh COMMAND on the router[s]
  [HOST ...] sh <SHELL-COMMAND> :: execute <SHELL-COMMAND> on hosts
  [HOST ...] shi <INTERACTIVE-COMMAND>  :: execute <INTERACTIVE-COMMAND> on HOST[s]
munet> show ip ospf neighbor
2022-09-16 13:43:13,172: INFO: Filtering hosts to kinds: ['frr']
2022-09-16 13:43:13,172: INFO: Filtered hosts: ['r1', 'r2', 'r3']
------ Host: r1 ------

Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
172.16.0.2        1 Full/DR         5m21s             33.727s 10.0.1.2        eth0:10.0.1.1                        0     0     0
172.16.0.3        1 Full/DR         5m26s             33.735s 10.0.2.3        eth1:10.0.2.1                        0     0     0

------- End: r1 ------
------ Host: r2 ------

Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
172.16.0.1        1 Full/Backup     5m21s             33.707s 10.0.1.1        eth0:10.0.1.2                        0     0     0
172.16.0.3        1 Full/DR         5m26s             33.715s 10.0.3.3        eth1:10.0.3.2                        0     0     0

------- End: r2 ------
------ Host: r3 ------

Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
172.16.0.1        1 Full/Backup     5m26s             33.707s 10.0.2.1        eth0:10.0.2.3                        0     0     0
172.16.0.2        1 Full/Backup     5m26s             33.706s 10.0.3.2        eth1:10.0.3.3                        0     0     0

------- End: r3 ------
munet> r1 show ip ospf neighbor
2022-09-16 13:43:18,073: INFO: Filtering hosts to kinds: ['frr']
2022-09-16 13:43:18,075: INFO: Filtered hosts: ['r1']

Neighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL
172.16.0.2        1 Full/DR         5m26s             38.788s 10.0.1.2        eth0:10.0.1.1                        0     0     0
172.16.0.3        1 Full/DR         5m31s             38.795s 10.0.2.3        eth1:10.0.2.1                        0     0     0

munet>

#+end_src


** Config Model
#+NAME: test-validate-module
#+CALL: validate-module(module=labn-munet-config)

#+NAME: labn-munet-config
#+HEADER: :var dep1=dep-babel
#+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.

           NOTE: With container nodes, the cleanup-cmd will be run
           prior to the `cmd` being killed, so that the container is
           present. For Qemu/VM nodes the cleanup command is run prior
           to the VM being brought down.";
      }
      leaf ready-cmd {
        type string;
        description
          "Shell command[s] to execute to determine if the node is ready";
      }
      leaf image {
        type string;
        must "not(../hostnet) and not(../qemu) and not(../server)" {
          error-message "Can only have one of hostnet, image, server or qemu";
        }
        description "Container image specification.";
      }
      leaf hostnet {
        type boolean;
        must "not(../image) and not(../qemu) and not(../server)" {
          error-message "Can only have one of hostnet, image, server or qemu";
        }
        description
          "Node that runs commands in the host network namespace. For this
           to work correclty the munet object should not be created with
           unshare inline.";
      }
      leaf server {
        type string;
        must "not(../hostnet) and not(../image) and not(../qemu)" {
          error-message "Can only have one of hostnet, image, server or qemu";
        }
        description
          "Name of server for SSHRemote node functionality. If using
           within pytest make sure you utilize the `unet_share` fixture
           instead of the normal `unet` one, otherwise ssh may not
           work as it is executing inside the munet namespace.";
      }
      leaf server-port {
        type uint16;
        must "../server" {
          error-message "server-port requires a server";
        }
        default 22;
        description
          "SSH port to connect to server on";
      }
      leaf ssh-identity-file {
        type string;
        description
          "Path to an SSH private key file for logging into either a remote ssh
           `server` or a qemu node with a running ssh server.";
      }
      leaf ssh-user {
        type string;
        description
          "The user to use when logging into either a remote ssh `server` or a
           qemu node with a running ssh server.";
      }
      leaf ssh-password {
        type string;
        description
          "The password to use when creating a 'console' to a remote ssh
           `server` node.";
      }
      container qemu {
        must "not(../hostnet) and not(../image) and not(../server)" {
          error-message "Can only have one of hostnet, image, server or qemu";
        }
        description "Specify parameters for Qemu VM node";
        leaf bios {
          type string;
          description
            "'open-firmare' to use open firmware bios, or a path to
             bios image file";
        }
        leaf cloud-init {
          type boolean;
          default false;
          description
            "Use a cloud-init disk to initialize image. Normally a
             ./cloud-init-disk is not specified, so one will be generated";
        }
        leaf cloud-init-disk {
          type string;
          must "./cloud-init";
          description
            "Path to a custom cloud-init disk image to configure the VM";
        }
        leaf disk {
          type string;
          description
            "Path to disk image possibly to boot from. If this is a relative path
             it will be relative to the configuration directory";
        }
        leaf disk-driver {
          type string;
          default "virtio";
          description
            "Disk driver to use, either 'sata' or 'virtio'. Some router images
             only work with 'sata', normally this should not be specified so that
             the default 'virtio' is used";
        }
        leaf disk-template {
          type string;
          description
            "Path to disk image template. If a ./disk image is not specified, or
             does not yet exist. Then this template is used to create a new disk
             image. If ./disk is not specified then the disk image path will be
             %RUNDIR%/%NAME%-<disk-template-basename>";
        }
        leaf initial-cmd {
          type string;
          description
            "Shell command[s] to execute when creating the node from a disk
             template. These commands are run prior to the standard ../../cmd
             when a disk is first created from a disk template";
        }
        leaf kernel {
          type string;
          description "path to kernel image (e.g,. bzImage) to boot";
        }
        leaf initrd {
          type string;
          description "path to initrd image (e.g,. rootfs.ext2) to boot";
        }
        leaf kvm {
          type boolean;
          default true;
          description "Run with HW acceleration";
        }
        leaf ncpu {
          type uint32;
          default 1;
          description "Number of cores";
        }
        leaf memory {
          type string;
          default "512M";
          description "ammount of memory for VM.";
        }
        leaf root {
          type string;
          default "/dev/ram0";
          description "root file system passed in cmdline as root=<value>";
        }
        leaf cmdline-extra {
          type string;
          description "string to add to the kernel cmdline (qemu -append)";
        }
        leaf extra-args {
          type string;
          description "extra qemu args passed when launching";
        }
        container console {
          description "Configuration for console handling";
          leaf user {
            type string;
	    default "root";
            description "User to login to console with";
          }
          leaf password {
            type string;
	    default "admin";
            description "Password to login to console with";
          }
          leaf initial-password {
            type string;
            description
              "The initial password. If the VM disk is newly created from a
               template, this value can be used to specify an initial password
               for the user. Often part of the bring-up process will set a new
               password and that should then be stored in the ../password leaf.";
          }
          leaf-list expects {
            type string;
            description "Strings to expect for logging into the console";
          }
          leaf-list sends {
            type string;
            description
              "Strings paired to `expects` for logging into the
               console. These are sent to the console when the
               corresponding expect is seen, zero length strings are
               allowed which indicate send nothing. An Expect with a
               send nothing could be used to reset the timeout timer on
               long boots";
          }
          leaf timeout {
            type uint32;
            description "Timeout for logging into the console";
          }
        }
      }
      list connections {
        must "not(../hostnet) and not(../server)" {
          error-message "SSHRemote and hostnet nodes have no munet 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 "IPv4 address and mask for the connection (interface).";
        }
        leaf ipv6 {
          type string;
          description "IPv6 address and mask for the connection (interface).";
        }
        leaf name {
          type string;
          description "Name for the connection (interface name).";
        }
        leaf hostintf {
          type string;
          description
            "Host interface for wired connections. This will move the given host
             interface into the namespace. The value is the name of the
             interface on the host (e.g., 'enp216s0f0v0') it will be renamed
             inside the namespace accordingly (either using automatic naming
             (e.g., 'eth1') or the name specified in ../name leaf.";
        }
        leaf physical {
          type string;
          description
            "Physical PCI interface address for wired connections. This is the
             PCI address of the form xxxx:xx:xx.x (e.g., 0000:1b:02.0) this will
             detach the given PCI device from it's native driver and reattach it
             to the vfio-dev PCI driver. This is used primarily by Qemu nodes;
             however, it can also be used by user processes that directly
             control physical devices such as DPDK, TREX, or VPP";
        }
        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.";
        }
        leaf driver {
          type string;
          default "virtio-net-pc";
          description "driver name for qemu based interfaces";
        }
        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 gdb-cmd {
        type string;
        description "Command to execute when --gdb option specified" ;
      }
      leaf-list gdb-target-cmds {
        type string;
        description
          "GDB commands to execute to prior to setting breakpoints" ;
      }
      leaf-list gdb-run-cmds {
        type string;
        description
          "GDB commands to send to gdb (e.g., to start the process running)" ;
      }
      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 interactive {
          type boolean;
          description
            "Run the command in interactive pty.";
        }
        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. Multple hosts are still allowed and their
             variables will be substituted accordingly.";
        }
      }
    }

    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 dns-network {
        type leafref {
          path "../networks/name";
        }
        description "network used for DNS addresses of hosts in hosts files.";
      }

      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.";
      }

      leaf initial-setup-cmd {
        type string;
        description
          "Shell command[s] to execute in the new namespace prior to bringing up
           the topology. These are run after any ./initial-setup-host-cmd
           commands.";
      }

      leaf initial-setup-host-cmd {
        type string;
        description
          "Shell command[s] to execute on the host prior to bringing up the
           topology. These are run prior to ./initial-setup-cmd commands.";
      }

      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;
          must "not (../external)";
          description
            "IPv4 prefix for the network. If host bit's are set then the linux
             bridge will be assigned that IP.";
        }
        leaf ipv6 {
          type string;
          must "not (../external)";
          description
            "IPv6 prefix for the network. If host bit's are set then the linux
             bridge will be assigned that IP.";
        }
        leaf external {
          type boolean;
          default false;
          description
            "This is a placeholder network for an externally defined network.
             This is most useful when adding host interfaces to nodes as the
             connection point.";
        }
      }

      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

* Appendix: Org Babel Functions

#+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: generate-tree
#+HEADER: :var dep1=dep-babel
#+begin_src shell :results output verbatim replace :wrap example :exports results
  [ -d /yang ] || DOCKER="sudo podman run --net=host -v $(pwd):/work docker.io/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="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


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/LabNConsulting/munet",
    "name": "munet",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.8",
    "maintainer_email": null,
    "keywords": null,
    "author": "Christian Hopps",
    "author_email": "chopps@labn.net",
    "download_url": "https://files.pythonhosted.org/packages/eb/05/af0f07d2803b1a083f2d228301841585b30dff2b42d99131ed6448fdb360/munet-0.15.4.tar.gz",
    "platform": null,
    "description": "#+STARTUP: indent\n* \u03bcNET (munet)\n#+html: <a href=\"https://github.com/LabNConsulting/munet/actions\"><img src=\"https://github.com/LabNConsulting/munet/actions/workflows/ci.yml/badge.svg?branch=main\"></a>\n#+html: <a href=\"https://codecov.io/gh/LabNConsulting/munet\" ><img src=\"https://codecov.io/gh/LabNConsulting/munet/branch/main/graph/badge.svg?token=FD2O4YGDTT\"></a>\n#+html: <a href=\"https://munet.readthedocs.io/en/latest/\"><img src=\"https://readthedocs.org/projects/munet/badge?version=latest\"></a>\n#+html: <p></p>\n\nFor better documentation see: https://munet.readthedocs.io/en/latest/\n\nA package for creating network topologies and running programs and containers\nwithin them using linux namepsaces, podman containers and qemu virtual machines.\n\nMunet can be run in a standalone mode with a configuration file for launching\nlinux shell and container based topologies, as well as be used as a library from\nwithin another application to provide the same functionality.\n\n** Standalone Config\n\nThe standalone config can be provided in a number of formats, limited by the\navailable encode/decode libraries withing the python environment. As JSON is\nbuilt in to python that format is always supported. Additionally YAML and TOML\nare supported if the corresponding packages are available (i.e., ~PyYAML~ and\n~toml~).\n\nThe config itself is defined with a YANG model which is fully defined at the end\nof this document. Below is an tree diagram of the overal format of a config file:\n\n#+NAME: Munet standalone config YANG tree diagram\n#+CALL: generate-tree(module=labn-munet-config)\n# Remove the #+RESULTS: before pushing to git, github is broken and\n# won't render it\n\n#+begin_example\nmodule: labn-munet-config\n  +--rw cli\n  |  +--rw commands* [name]\n  |     +--rw exec?          string\n  |     +--rw exec-kind* [kind]\n  |     |  +--rw kind    string\n  |     |  +--rw exec?   string\n  |     +--rw format?        string\n  |     +--rw help?          string\n  |     +--rw interactive?   boolean\n  |     +--rw kinds*         -> ../../../kinds/name\n  |     +--rw name           string\n  |     +--rw new-window?    boolean\n  |     +--rw top-level?     boolean\n  +--rw kinds* [name]\n  |  +--rw merge*               string\n  |  +--rw cap-add*             string\n  |  +--rw cap-remove*          string\n  |  +--rw cmd?                 string\n  |  +--rw cleanup-cmd?         string\n  |  +--rw ready-cmd?           string\n  |  +--rw image?               string\n  |  +--rw hostnet?             boolean\n  |  +--rw server?              string\n  |  +--rw server-port?         uint16\n  |  +--rw ssh-identity-file?   string\n  |  +--rw ssh-user?            string\n  |  +--rw ssh-password?        string\n  |  +--rw qemu\n  |  |  +--rw bios?              string\n  |  |  +--rw cloud-init?        boolean\n  |  |  +--rw cloud-init-disk?   string\n  |  |  +--rw disk?              string\n  |  |  +--rw disk-driver?       string\n  |  |  +--rw disk-template?     string\n  |  |  +--rw initial-cmd?       string\n  |  |  +--rw kernel?            string\n  |  |  +--rw initrd?            string\n  |  |  +--rw kvm?               boolean\n  |  |  +--rw ncpu?              uint32\n  |  |  +--rw memory?            string\n  |  |  +--rw root?              string\n  |  |  +--rw cmdline-extra?     string\n  |  |  +--rw extra-args?        string\n  |  |  +--rw console\n  |  |     +--rw user?               string\n  |  |     +--rw password?           string\n  |  |     +--rw initial-password?   string\n  |  |     +--rw expects*            string\n  |  |     +--rw sends*              string\n  |  |     +--rw timeout?            uint32\n  |  +--rw connections* [to]\n  |  |  +--rw to                    string\n  |  |  +--rw ip?                   string\n  |  |  +--rw ipv6?                 string\n  |  |  +--rw name?                 string\n  |  |  +--rw hostintf?             string\n  |  |  +--rw physical?             string\n  |  |  +--rw remote-name?          string\n  |  |  +--rw driver?               string\n  |  |  +--rw delay?                uint64\n  |  |  +--rw jitter?               uint64\n  |  |  +--rw jitter-correlation?   decimal64\n  |  |  +--rw loss?                 uint64\n  |  |  +--rw loss-correlation?     decimal64\n  |  |  +--rw rate\n  |  |     +--rw rate?    number64\n  |  |     +--rw limit?   number64\n  |  |     +--rw burst?   number64\n  |  +--rw env* [name]\n  |  |  +--rw name     string\n  |  |  +--rw value?   string\n  |  +--rw gdb-cmd?             string\n  |  +--rw gdb-target-cmds*     string\n  |  +--rw gdb-run-cmds*        string\n  |  +--rw init?                union\n  |  +--rw mounts* [destination]\n  |  |  +--rw destination    string\n  |  |  +--rw source?        string\n  |  |  +--rw tmpfs-size?    string\n  |  |  +--rw type?          string\n  |  +--rw name                 string\n  |  +--rw podman\n  |  |  +--rw extra-args*   string\n  |  +--rw privileged?          boolean\n  |  +--rw shell?               union\n  |  +--rw volumes*             string\n  +--rw topology\n  |  +--rw dns-network?              -> ../networks/name\n  |  +--rw ipv6-enable?              boolean\n  |  +--rw networks-autonumber?      boolean\n  |  +--rw initial-setup-cmd?        string\n  |  +--rw initial-setup-host-cmd?   string\n  |  +--rw networks* [name]\n  |  |  +--rw name        string\n  |  |  +--rw ip?         string\n  |  |  +--rw ipv6?       string\n  |  |  +--rw external?   boolean\n  |  +--rw nodes* [name]\n  |     +--rw id?                  uint32\n  |     +--rw kind?                -> ../../../kinds/name\n  |     +--rw cap-add*             string\n  |     +--rw cap-remove*          string\n  |     +--rw cmd?                 string\n  |     +--rw cleanup-cmd?         string\n  |     +--rw ready-cmd?           string\n  |     +--rw image?               string\n  |     +--rw hostnet?             boolean\n  |     +--rw server?              string\n  |     +--rw server-port?         uint16\n  |     +--rw ssh-identity-file?   string\n  |     +--rw ssh-user?            string\n  |     +--rw ssh-password?        string\n  |     +--rw qemu\n  |     |  +--rw bios?              string\n  |     |  +--rw cloud-init?        boolean\n  |     |  +--rw cloud-init-disk?   string\n  |     |  +--rw disk?              string\n  |     |  +--rw disk-driver?       string\n  |     |  +--rw disk-template?     string\n  |     |  +--rw initial-cmd?       string\n  |     |  +--rw kernel?            string\n  |     |  +--rw initrd?            string\n  |     |  +--rw kvm?               boolean\n  |     |  +--rw ncpu?              uint32\n  |     |  +--rw memory?            string\n  |     |  +--rw root?              string\n  |     |  +--rw cmdline-extra?     string\n  |     |  +--rw extra-args?        string\n  |     |  +--rw console\n  |     |     +--rw user?               string\n  |     |     +--rw password?           string\n  |     |     +--rw initial-password?   string\n  |     |     +--rw expects*            string\n  |     |     +--rw sends*              string\n  |     |     +--rw timeout?            uint32\n  |     +--rw connections* [to]\n  |     |  +--rw to                    string\n  |     |  +--rw ip?                   string\n  |     |  +--rw ipv6?                 string\n  |     |  +--rw name?                 string\n  |     |  +--rw hostintf?             string\n  |     |  +--rw physical?             string\n  |     |  +--rw remote-name?          string\n  |     |  +--rw driver?               string\n  |     |  +--rw delay?                uint64\n  |     |  +--rw jitter?               uint64\n  |     |  +--rw jitter-correlation?   decimal64\n  |     |  +--rw loss?                 uint64\n  |     |  +--rw loss-correlation?     decimal64\n  |     |  +--rw rate\n  |     |     +--rw rate?    number64\n  |     |     +--rw limit?   number64\n  |     |     +--rw burst?   number64\n  |     +--rw env* [name]\n  |     |  +--rw name     string\n  |     |  +--rw value?   string\n  |     +--rw gdb-cmd?             string\n  |     +--rw gdb-target-cmds*     string\n  |     +--rw gdb-run-cmds*        string\n  |     +--rw init?                union\n  |     +--rw mounts* [destination]\n  |     |  +--rw destination    string\n  |     |  +--rw source?        string\n  |     |  +--rw tmpfs-size?    string\n  |     |  +--rw type?          string\n  |     +--rw name                 string\n  |     +--rw podman\n  |     |  +--rw extra-args*   string\n  |     +--rw privileged?          boolean\n  |     +--rw shell?               union\n  |     +--rw volumes*             string\n  +--rw version?    uint32\n#+end_example\n\n** Examples\n*** Two Hosts Topology\n\nA very simple config with 2 hosts connected to a mgmt network.\n\nIn this config the networks are autonumbered which starts with\n~10.0.0.0/24~. So, ~h1~ will have an ~eth0~ interface with IP\n~10.0.0.1~ and ~h2~ will likewise have an ~eth0~ interface, and an IP\nof ~10.0.0.2~.\n\n#+begin_src yaml\n  topology:\n    networks-autonumber: true\n    networks:\n      - name: net0\n    nodes:\n      - name: h1\n        connections:\n          - to: net0\n      - name: h2\n        connections:\n          - to: net0\n#+end_src\n\n*** Router VM\n\nNOTE: This example is testing the boundaries of what munet can do\nwith a node. Its really here to document how to do this very complex\nthing. Beginners should probably skip it.\n\nRouter VMs are very different from standard unix-like OSs. Munet does a lot of\nautomatic configuration assuming a unix-like (and mostly Linux) OS. Various\nconfiguration parameters need to be set to tune the automatic configuration and\nassumptions down. Here's an example munet config fragment that shows booting a\ncisco VM using a nexos file system image.\n\n#+begin_src yaml\n  topology:\n    networks-autonumber: true\n    dns-network: \"mgmt0\"\n    networks:\n      - name: mgmt0\n        ip: 192.168.0.254/24\n        nat: true\n      - name: net0\n    nodes:\n      # ...\n      - name: r1\n        kind: cisco\n        connections:\n          - to: \"mgmt0\"\n            name: \"eth1\"\n            driver: \"e1000\"\n          - to: \"net0\"\n            name: \"eth2\"\n            driver: \"e1000\"\n  kinds:\n    - name: cisco\n      shell: false\n      cmd: |\n        terminal terminal-type dumb\n        terminal length 0\n        terminal width 511\n        terminal session 0\n        conf t\n        line console\n        exec-timeout 0\n        line vty\n        exec-timeout 0\n        int mgmt0\n          ip address 192.168.0.2/24\n        exit\n        feature ssh\n        feature telnet\n        end\n      qemu:\n        unix-os: false\n        disk-template: \"%CONFIGDIR%/nexus9300v64.10.2.3.F.qcow2\"\n        disk-driver: \"sata\"\n        bios: \"open-firmware\"\n        memory: \"8192M\"\n        smp: 2\n        kvm: true\n        console:\n          stdio: false\n          user: \"admin\"\n          password: \"\"\n          prompt: \"(^|\\r?\\n\\r?)switch(\\\\([^\\\\)]+\\\\))?#\"\n          expects:\n            - \"skip - bypass.*yes/skip/no\\\\)\\\\[no\\\\]:\"\n            - \"loader > \"\n          sends:\n            - \"skip\\n\"\n            - \"boot nxos64-cs.10.2.3.F.bin\\n\"\n          timeout: 900\n\n  cli:\n    commands:\n      - name: ssh\n        exec: \"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null admin@%IPADDR%\"\n        kinds: [\"cisco\"]\n        format: \"ssh NODE [NODE ...]\"\n        top-level: true\n        new-window: true\n#+end_src\n\n\n** Development\n\n*** Dependencies\n\n\u03bcNET requires the following packages:\n\n  python3 python3-venv\n\nAutomate tests require the following system packages:\n\n  podman\n\nEnsure you have poetry setup, the following instructions work around some bugs\nwith poetry:\n\n#+begin_src shell\n  python3 -m venv ~/.poetrybin\n  source ~/.poetrybin/bin/activate\n  pip install poetry\n  pip uninstall keyring\n#+end_src\n\nNOTE: add \"~/.poetrybin/bin\" to your $PATH\n\nInstall \u03bcNET with dependencies:\n\n  poetry install --all-extras\n\n*** Check your install\n\n make\n\n*** Run an example\n\nThe following uses FRR (see https://frrouting.org)\n\n   sudo poetry run munet -c examples/frr/ospf/ospf/munet.yaml\n\nFor example:\n#+begin_src shell\nmunet$ sudo poetry run munet -c examples/frr/ospf/ospf/munet.yaml\n2022-09-16 13:37:05,603: INFO: Loaded logging config /home/lberger/Code/github/labn/munet/munet/logconf.yaml\n2022-09-16 13:37:05,609: INFO: Loaded config from /home/lberger/Code/github/labn/munet/examples/frr/ospf/ospf/munet.yaml\n2022-09-16 13:37:05,623: INFO: Loaded kinds config /home/lberger/Code/github/labn/munet/munet/kinds.yaml\n2022-09-16 13:37:05,745: INFO: Munet(munet): created\n2022-09-16 13:37:05,926: INFO: L3NamespaceNode(r1): created\n2022-09-16 13:37:06,086: INFO: L3NamespaceNode(r2): created\n2022-09-16 13:37:06,247: INFO: L3NamespaceNode(r3): created\n2022-09-16 13:37:06,778: INFO: Topology up: rundir: /tmp/unet-root\n\n--- Munet CLI Starting ---\n\n\nmunet>\nmunet> help\n\nBasic Commands:\n  cli   :: open a secondary CLI window\n  help  :: this help\n  hosts :: list hosts\n  quit  :: quit the cli\n\n  HOST can be a host or one of the following:\n    - '*' for all hosts\n    - '.' for the parent munet\n    - a regex specified between '/' (e.g., '/rtr.*/')\n\nNew Window Commands:\n  hterm HOST [HOST ...] :: open terminal[s] on HOST[S] (outside containers), * for all\n  pcap NETWORK  :: capture packets from NETWORK into file capture-NETWORK.pcap the command is run within a new window which also shows packet summaries\n  stdout HOST [HOST ...]        :: tail -f on the stdout of the cmd for this node\n  stdout HOST [HOST ...]        :: tail -f on the stdout of the cmd for this node\n  term HOST [HOST ...]  :: open terminal[s] (TMUX or XTerm) on HOST[S], * for all\n  vtysh ROUTER [ROUTER ...]     ::\n  xterm HOST [HOST ...] :: open XTerm[s] on HOST[S], * for all\nInline Commands:\n  [ROUTER ...] COMMAND  :: execute vtysh COMMAND on the router[s]\n  [HOST ...] sh <SHELL-COMMAND> :: execute <SHELL-COMMAND> on hosts\n  [HOST ...] shi <INTERACTIVE-COMMAND>  :: execute <INTERACTIVE-COMMAND> on HOST[s]\nmunet> show ip ospf neighbor\n2022-09-16 13:43:13,172: INFO: Filtering hosts to kinds: ['frr']\n2022-09-16 13:43:13,172: INFO: Filtered hosts: ['r1', 'r2', 'r3']\n------ Host: r1 ------\n\nNeighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL\n172.16.0.2        1 Full/DR         5m21s             33.727s 10.0.1.2        eth0:10.0.1.1                        0     0     0\n172.16.0.3        1 Full/DR         5m26s             33.735s 10.0.2.3        eth1:10.0.2.1                        0     0     0\n\n------- End: r1 ------\n------ Host: r2 ------\n\nNeighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL\n172.16.0.1        1 Full/Backup     5m21s             33.707s 10.0.1.1        eth0:10.0.1.2                        0     0     0\n172.16.0.3        1 Full/DR         5m26s             33.715s 10.0.3.3        eth1:10.0.3.2                        0     0     0\n\n------- End: r2 ------\n------ Host: r3 ------\n\nNeighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL\n172.16.0.1        1 Full/Backup     5m26s             33.707s 10.0.2.1        eth0:10.0.2.3                        0     0     0\n172.16.0.2        1 Full/Backup     5m26s             33.706s 10.0.3.2        eth1:10.0.3.3                        0     0     0\n\n------- End: r3 ------\nmunet> r1 show ip ospf neighbor\n2022-09-16 13:43:18,073: INFO: Filtering hosts to kinds: ['frr']\n2022-09-16 13:43:18,075: INFO: Filtered hosts: ['r1']\n\nNeighbor ID     Pri State           Up Time         Dead Time Address         Interface                        RXmtL RqstL DBsmL\n172.16.0.2        1 Full/DR         5m26s             38.788s 10.0.1.2        eth0:10.0.1.1                        0     0     0\n172.16.0.3        1 Full/DR         5m31s             38.795s 10.0.2.3        eth1:10.0.2.1                        0     0     0\n\nmunet>\n\n#+end_src\n\n\n** Config Model\n#+NAME: test-validate-module\n#+CALL: validate-module(module=labn-munet-config)\n\n#+NAME: labn-munet-config\n#+HEADER: :var dep1=dep-babel\n#+HEADER: :file labn-munet-config.yang :results output file silent :cache yes\n#+begin_src yang :exports code\n  module labn-munet-config {\n    yang-version 1.1;\n    namespace \"urn:labn:yang:labn-munet-config\";\n    prefix c;\n\n    organization\n      \"LabN Consulting, L.L.C.\";\n\n    contact\n      \"Author: Christian Hopps\n               <mailto:chopps@labn.net>\";\n\n    description\n      \"This module defines the configuration state for munet.\";\n\n    revision 2021-12-18 {\n      description \"Initial Revision\";\n      reference \"https://github.com/LabNConsulting/munet/blob/main/README.md\";\n    }\n\n    typedef number64 {\n      type union {\n        type uint64;\n        type string {\n          pattern '[0-9]+([KMGTPE]i?)?';\n        }\n      }\n      description\n        \"A number with optional suffix, where suffix means:\n           K -> value*10^3, Ki -> value*2^10,\n           M -> value*10^6, Mi -> value*2^20,\n           G -> value*10^9, Gi -> value*2^30,\n           T -> value*10^12, Gi -> value*2^40,\n           P -> value*10^15, Gi -> value*2^50,\n           E -> value*10^18, Gi -> value*2^60\";\n    }\n\n    grouping intf-constraints {\n      description \"traffic control based interface constraints\";\n      leaf delay {\n        type uint64;\n        description \"number of microseconds of delay\";\n      }\n      leaf jitter {\n        type uint64;\n        must \"../delay\";\n        description \"number of microseconds of possible jitter\";\n      }\n      leaf jitter-correlation {\n        type decimal64 {\n          fraction-digits 16;\n          range \"0..100\";\n        }\n        must \"../jitter\";\n        description \"percent correlation between consecutive jitter values\";\n      }\n      leaf loss {\n        type uint64;\n        must \"../delay\";\n        description \"number of microseconds of possible jitter\";\n      }\n      leaf loss-correlation {\n        type decimal64 {\n          fraction-digits 16;\n          range \"0..100\";\n        }\n        must \"../loss\";\n        description \"percent correlation between consecutive loss values\";\n      }\n      container rate {\n        description \"bits per second maximum rate with possible limit and burst\";\n        leaf rate {\n          type number64;\n          description \"bits per second maximum rate\";\n        }\n        leaf limit {\n          type number64;\n          must \"../rate\";\n          description \"bits per second maximum rate\";\n        }\n        leaf burst {\n          type number64;\n          must \"../rate\";\n          description \"bits per second maximum rate\";\n        }\n      }\n    }\n\n    grouping common-node {\n      description \"Common node properties\";\n      leaf-list cap-add {\n        type string;\n        description \"Capabilities to add to a container.\";\n        reference \"https://man7.org/linux/man-pages/man7/capabilities.7.html\";\n      }\n      leaf-list cap-remove {\n        type string;\n        description \"Capabilities to remove from a container.\";\n        reference \"https://man7.org/linux/man-pages/man7/capabilities.7.html\";\n      }\n      leaf cmd {\n        type string;\n        description \"Shell command[s] to execute when creating the node.\";\n      }\n      leaf cleanup-cmd {\n        type string;\n        description\n          \"Shell command[s] to execute when deleting the node.\n\n           NOTE: With container nodes, the cleanup-cmd will be run\n           prior to the `cmd` being killed, so that the container is\n           present. For Qemu/VM nodes the cleanup command is run prior\n           to the VM being brought down.\";\n      }\n      leaf ready-cmd {\n        type string;\n        description\n          \"Shell command[s] to execute to determine if the node is ready\";\n      }\n      leaf image {\n        type string;\n        must \"not(../hostnet) and not(../qemu) and not(../server)\" {\n          error-message \"Can only have one of hostnet, image, server or qemu\";\n        }\n        description \"Container image specification.\";\n      }\n      leaf hostnet {\n        type boolean;\n        must \"not(../image) and not(../qemu) and not(../server)\" {\n          error-message \"Can only have one of hostnet, image, server or qemu\";\n        }\n        description\n          \"Node that runs commands in the host network namespace. For this\n           to work correclty the munet object should not be created with\n           unshare inline.\";\n      }\n      leaf server {\n        type string;\n        must \"not(../hostnet) and not(../image) and not(../qemu)\" {\n          error-message \"Can only have one of hostnet, image, server or qemu\";\n        }\n        description\n          \"Name of server for SSHRemote node functionality. If using\n           within pytest make sure you utilize the `unet_share` fixture\n           instead of the normal `unet` one, otherwise ssh may not\n           work as it is executing inside the munet namespace.\";\n      }\n      leaf server-port {\n        type uint16;\n        must \"../server\" {\n          error-message \"server-port requires a server\";\n        }\n        default 22;\n        description\n          \"SSH port to connect to server on\";\n      }\n      leaf ssh-identity-file {\n        type string;\n        description\n          \"Path to an SSH private key file for logging into either a remote ssh\n           `server` or a qemu node with a running ssh server.\";\n      }\n      leaf ssh-user {\n        type string;\n        description\n          \"The user to use when logging into either a remote ssh `server` or a\n           qemu node with a running ssh server.\";\n      }\n      leaf ssh-password {\n        type string;\n        description\n          \"The password to use when creating a 'console' to a remote ssh\n           `server` node.\";\n      }\n      container qemu {\n        must \"not(../hostnet) and not(../image) and not(../server)\" {\n          error-message \"Can only have one of hostnet, image, server or qemu\";\n        }\n        description \"Specify parameters for Qemu VM node\";\n        leaf bios {\n          type string;\n          description\n            \"'open-firmare' to use open firmware bios, or a path to\n             bios image file\";\n        }\n        leaf cloud-init {\n          type boolean;\n          default false;\n          description\n            \"Use a cloud-init disk to initialize image. Normally a\n             ./cloud-init-disk is not specified, so one will be generated\";\n        }\n        leaf cloud-init-disk {\n          type string;\n          must \"./cloud-init\";\n          description\n            \"Path to a custom cloud-init disk image to configure the VM\";\n        }\n        leaf disk {\n          type string;\n          description\n            \"Path to disk image possibly to boot from. If this is a relative path\n             it will be relative to the configuration directory\";\n        }\n        leaf disk-driver {\n          type string;\n          default \"virtio\";\n          description\n            \"Disk driver to use, either 'sata' or 'virtio'. Some router images\n             only work with 'sata', normally this should not be specified so that\n             the default 'virtio' is used\";\n        }\n        leaf disk-template {\n          type string;\n          description\n            \"Path to disk image template. If a ./disk image is not specified, or\n             does not yet exist. Then this template is used to create a new disk\n             image. If ./disk is not specified then the disk image path will be\n             %RUNDIR%/%NAME%-<disk-template-basename>\";\n        }\n        leaf initial-cmd {\n          type string;\n          description\n            \"Shell command[s] to execute when creating the node from a disk\n             template. These commands are run prior to the standard ../../cmd\n             when a disk is first created from a disk template\";\n        }\n        leaf kernel {\n          type string;\n          description \"path to kernel image (e.g,. bzImage) to boot\";\n        }\n        leaf initrd {\n          type string;\n          description \"path to initrd image (e.g,. rootfs.ext2) to boot\";\n        }\n        leaf kvm {\n          type boolean;\n          default true;\n          description \"Run with HW acceleration\";\n        }\n        leaf ncpu {\n          type uint32;\n          default 1;\n          description \"Number of cores\";\n        }\n        leaf memory {\n          type string;\n          default \"512M\";\n          description \"ammount of memory for VM.\";\n        }\n        leaf root {\n          type string;\n          default \"/dev/ram0\";\n          description \"root file system passed in cmdline as root=<value>\";\n        }\n        leaf cmdline-extra {\n          type string;\n          description \"string to add to the kernel cmdline (qemu -append)\";\n        }\n        leaf extra-args {\n          type string;\n          description \"extra qemu args passed when launching\";\n        }\n        container console {\n          description \"Configuration for console handling\";\n          leaf user {\n            type string;\n\t    default \"root\";\n            description \"User to login to console with\";\n          }\n          leaf password {\n            type string;\n\t    default \"admin\";\n            description \"Password to login to console with\";\n          }\n          leaf initial-password {\n            type string;\n            description\n              \"The initial password. If the VM disk is newly created from a\n               template, this value can be used to specify an initial password\n               for the user. Often part of the bring-up process will set a new\n               password and that should then be stored in the ../password leaf.\";\n          }\n          leaf-list expects {\n            type string;\n            description \"Strings to expect for logging into the console\";\n          }\n          leaf-list sends {\n            type string;\n            description\n              \"Strings paired to `expects` for logging into the\n               console. These are sent to the console when the\n               corresponding expect is seen, zero length strings are\n               allowed which indicate send nothing. An Expect with a\n               send nothing could be used to reset the timeout timer on\n               long boots\";\n          }\n          leaf timeout {\n            type uint32;\n            description \"Timeout for logging into the console\";\n          }\n        }\n      }\n      list connections {\n        must \"not(../hostnet) and not(../server)\" {\n          error-message \"SSHRemote and hostnet nodes have no munet connections.\";\n        }\n        key to;\n        description \"Connections to other networks or nodes from this node\";\n\n        leaf to {\n          type string;\n          description \"The target of this connection.\";\n        }\n        leaf ip {\n          type string;\n          description \"IPv4 address and mask for the connection (interface).\";\n        }\n        leaf ipv6 {\n          type string;\n          description \"IPv6 address and mask for the connection (interface).\";\n        }\n        leaf name {\n          type string;\n          description \"Name for the connection (interface name).\";\n        }\n        leaf hostintf {\n          type string;\n          description\n            \"Host interface for wired connections. This will move the given host\n             interface into the namespace. The value is the name of the\n             interface on the host (e.g., 'enp216s0f0v0') it will be renamed\n             inside the namespace accordingly (either using automatic naming\n             (e.g., 'eth1') or the name specified in ../name leaf.\";\n        }\n        leaf physical {\n          type string;\n          description\n            \"Physical PCI interface address for wired connections. This is the\n             PCI address of the form xxxx:xx:xx.x (e.g., 0000:1b:02.0) this will\n             detach the given PCI device from it's native driver and reattach it\n             to the vfio-dev PCI driver. This is used primarily by Qemu nodes;\n             however, it can also be used by user processes that directly\n             control physical devices such as DPDK, TREX, or VPP\";\n        }\n        leaf remote-name {\n          type string;\n          description\n            \"The remote name of a p2p connection. This is used for disambiguation\n             when there are multiple point-to-point connections to the same\n             remote node.\";\n        }\n        leaf driver {\n          type string;\n          default \"virtio-net-pc\";\n          description \"driver name for qemu based interfaces\";\n        }\n        uses intf-constraints;\n      }\n      list env {\n        key name;\n        description\n          \"List of environment variable to add to the `cmd` execution\n           environment\";\n        leaf name {\n          type string;\n          description \"Environment variable name.\";\n        }\n        leaf value {\n          type string;\n          description \"Environment variable value.\";\n        }\n      }\n      leaf gdb-cmd {\n        type string;\n        description \"Command to execute when --gdb option specified\" ;\n      }\n      leaf-list gdb-target-cmds {\n        type string;\n        description\n          \"GDB commands to execute to prior to setting breakpoints\" ;\n      }\n      leaf-list gdb-run-cmds {\n        type string;\n        description\n          \"GDB commands to send to gdb (e.g., to start the process running)\" ;\n      }\n      leaf init {\n        type union {\n          type boolean;\n          type string;\n        }\n        description \"Controls use of an init process.\";\n      }\n      list mounts {\n        key destination;\n        description\n          \"Mounts to be made inside the namespace. Currently only supported for\n           container based nodes.\";\n\n        leaf destination {\n          type string;\n          description\n            \"The inner mount point. If no source is given this will be a tmpfs\n             mount, otherwise the it is a bind mount from the `source`.\";\n        }\n        leaf source {\n          type string;\n          description \"The source of the bind mount.\";\n        }\n        leaf tmpfs-size {\n          type string;\n          description \"The size of the tmpfs.\";\n        }\n        leaf type {\n          type string;\n          description \"The type of the mount (currently bind or tmpfs).\";\n        }\n      }\n      leaf name {\n        type string;\n        description \"Name of this node or kind.\";\n      }\n      container podman {\n        description \"Configuration related to podman containers.\";\n        leaf-list extra-args {\n          type string;\n          description \"list of CLI arguments to add to the podman run command.\";\n        }\n      }\n      leaf privileged {\n        type boolean;\n        description \"Controls running the container in privileged mode.\";\n      }\n      leaf shell {\n        type union {\n          type boolean;\n          type string;\n        }\n        description\n          \"Controls use of an shell process for `cmd` execution. If 'false' then\n           `cmd` will be run directly with exec(1), otherwise a shell will be\n           used. If this value is `true` then the default shell will be used,\n           otherwise it is a string which specifies the path to the shell to\n           use.\";\n      }\n      leaf-list volumes {\n        type string;\n        description\n          \"Bind or tmpfs mounts. For bind mounts the format of the string is\n           <outer>:<inner>, for tmpfs it's simply the inner mount path.\";\n      }\n    }\n\n    container cli {\n      description \"CLI additions.\";\n      list commands {\n        key name;\n        description \"A command to add to the CLI.\";\n\n        leaf exec {\n          type string;\n          description\n            \"Command to execute when the CLI command is given. The string is\n             evaluated as a python f-string with `{host}` set to the current\n             host object (or None) `{unet}` set to the Munet object, and\n             `{user_input}` to any user input that follows the command (or '' if\n             none specified).\";\n        }\n        list exec-kind {\n          key kind;\n          description \"A kind specific execution formats.\";\n\n          leaf kind {\n            type string;\n            description \"Kind for which this command format should be used.\";\n          }\n          leaf exec {\n            type string;\n            description\n              \"Command to execute when the CLI command is given. The string is\n               evaluated as a python f-string with `{host}` set to the current\n               host object (or None) `{unet}` set to the Munet object, and\n               `{user_input}` to any user input that follows the command (or ''\n               if none specified).\";\n          }\n        }\n        leaf format {\n          type string;\n          description\n            \"The format of the command. Used to print help string for user.\";\n        }\n        leaf help {\n          type string;\n          description\n            \"The description of the command. Used to print help string for\n             user.\";\n        }\n        leaf interactive {\n          type boolean;\n          description\n            \"Run the command in interactive pty.\";\n        }\n        leaf-list kinds {\n          type leafref {\n            path \"../../../kinds/name\";\n          }\n          description\n            \"List of kinds for which this command should be restricted to\n             running on.\";\n        }\n        leaf name {\n          type string;\n          description \"CLI command name.\";\n        }\n        leaf new-window {\n          type boolean;\n          description\n            \"Controls if the command runs in the CLI window or opens a new\n             terminal window to run the command in.\";\n        }\n        leaf top-level {\n          type boolean;\n          default false;\n          description\n            \"If true the command is run in the top-level containing namespace.\n             This is the namespace from which each of the hosts allocated\n             sub-namespaces from. Multple hosts are still allowed and their\n             variables will be substituted accordingly.\";\n        }\n      }\n    }\n\n    list kinds {\n      key name;\n      description\n        \"List of kinds used to group and share common node properities.\";\n\n      leaf-list merge {\n        type string;\n        description\n          \"List of properties which should be merged with their node specific\n           values, rather than being replaced by the node specific version.\";\n      }\n      uses common-node;\n    }\n\n    container topology {\n      description \"The topology munet should create.\";\n\n      leaf dns-network {\n        type leafref {\n          path \"../networks/name\";\n        }\n        description \"network used for DNS addresses of hosts in hosts files.\";\n      }\n\n      leaf ipv6-enable {\n        type boolean;\n        default false;\n        description\n          \"Controls if IPv6 is enabled or disabled.\";\n      }\n\n      leaf networks-autonumber {\n        type boolean;\n        description\n          \"Controls if networks and node connections are given IP addresses if\n           not explicitly configured.\";\n      }\n\n      leaf initial-setup-cmd {\n        type string;\n        description\n          \"Shell command[s] to execute in the new namespace prior to bringing up\n           the topology. These are run after any ./initial-setup-host-cmd\n           commands.\";\n      }\n\n      leaf initial-setup-host-cmd {\n        type string;\n        description\n          \"Shell command[s] to execute on the host prior to bringing up the\n           topology. These are run prior to ./initial-setup-cmd commands.\";\n      }\n\n      list networks {\n        key name;\n        description \"List of networks to create.\";\n\n        leaf name {\n          type string {\n            length \"1..11\";\n            pattern \"[-a-zA-Z0-9_]+\";\n          }\n          description \"Name of the network\";\n        }\n        leaf ip {\n          type string;\n          must \"not (../external)\";\n          description\n            \"IPv4 prefix for the network. If host bit's are set then the linux\n             bridge will be assigned that IP.\";\n        }\n        leaf ipv6 {\n          type string;\n          must \"not (../external)\";\n          description\n            \"IPv6 prefix for the network. If host bit's are set then the linux\n             bridge will be assigned that IP.\";\n        }\n        leaf external {\n          type boolean;\n          default false;\n          description\n            \"This is a placeholder network for an externally defined network.\n             This is most useful when adding host interfaces to nodes as the\n             connection point.\";\n        }\n      }\n\n      list nodes {\n        key name;\n        description \"Nodes in the topology.\";\n\n        leaf id {\n          type uint32;\n          description \"Explicitly set the ID for the node.\";\n        }\n        leaf kind {\n          type leafref {\n            path \"../../../kinds/name\";\n          }\n          description\n            \"Indicate the kind of this node, which pulls in the properies of that\n             `kind` for this node.\";\n        }\n        uses common-node;\n      }\n    }\n    leaf version {\n      type uint32;\n      description \"version of this config\";\n    }\n  }\n#+end_src\n\n* Appendix: Org Babel Functions\n\n#+name: dep-babel\n#+begin_src emacs-lisp :results none :exports none\n    (org-babel-do-load-languages 'org-babel-load-languages '((shell . t)))\n    (setq fill-column 69)\n    (setq org-confirm-babel-evaluate nil)\n#+end_src\n\n#+NAME: generate-tree\n#+HEADER: :var dep1=dep-babel\n#+begin_src shell :results output verbatim replace :wrap example :exports results\n  [ -d /yang ] || DOCKER=\"sudo podman run --net=host -v $(pwd):/work docker.io/labn/org-rfc\" #\n  $DOCKER pyang --tree-line-length=69 -f tree ${module} 2> err.out;\n#+end_src\n\n#+NAME: validate-module\n#+HEADER: :var dep1=dep-babel\n#+begin_src bash :results output verbatim replace :wrap comment :exports none\n  [ -d /yang ] || DOCKER=\"sudo podman run --net=host -v $(pwd):/work docker.io/labn/org-rfc\"\n  if ! $DOCKER pyang -P build --lax-quote-checks -Werror --lint $module 2>&1; then echo FAIL; fi\n#+end_src\n\n",
    "bugtrack_url": null,
    "license": "GPL-2.0-or-later",
    "summary": "A package to facilitate network simulations",
    "version": "0.15.4",
    "project_urls": {
        "Homepage": "https://github.com/LabNConsulting/munet",
        "Repository": "https://github.com/LabNConsulting/munet"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "fa9cad19f5c11b1a64702ad08537a2784b885916f63730af2debda9f8dfb58e0",
                "md5": "b2c7c9cb39604d68e40e498661d68b63",
                "sha256": "c567d7c86d801c1351411cf7bafe45ba6dfb23f94e78a28b87421cbdb2e654bd"
            },
            "downloads": -1,
            "filename": "munet-0.15.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b2c7c9cb39604d68e40e498661d68b63",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.8",
            "size": 138865,
            "upload_time": "2025-01-12T16:27:51",
            "upload_time_iso_8601": "2025-01-12T16:27:51.829373Z",
            "url": "https://files.pythonhosted.org/packages/fa/9c/ad19f5c11b1a64702ad08537a2784b885916f63730af2debda9f8dfb58e0/munet-0.15.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "eb05af0f07d2803b1a083f2d228301841585b30dff2b42d99131ed6448fdb360",
                "md5": "c680469c1d0d740594201f2521a0bae5",
                "sha256": "0dba63a13bec88b6d15ae3ebf618fc1a990d0b507010fecbbbae90b5bd32b7b6"
            },
            "downloads": -1,
            "filename": "munet-0.15.4.tar.gz",
            "has_sig": false,
            "md5_digest": "c680469c1d0d740594201f2521a0bae5",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.8",
            "size": 134871,
            "upload_time": "2025-01-12T16:27:54",
            "upload_time_iso_8601": "2025-01-12T16:27:54.428424Z",
            "url": "https://files.pythonhosted.org/packages/eb/05/af0f07d2803b1a083f2d228301841585b30dff2b42d99131ed6448fdb360/munet-0.15.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-01-12 16:27:54",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "LabNConsulting",
    "github_project": "munet",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "munet"
}
        
Elapsed time: 1.27184s