-
Notifications
You must be signed in to change notification settings - Fork 1
Module System
This page explains how to use modules in dot2net.
Modules extend dot2net with platform-specific functionality. Loading a module adds:
- File definitions - Platform-specific output files (e.g., topo.yaml, spec.yaml)
- FormatStyles - Pre-defined formatting rules for specific config formats
- Internal classes - Network/node/interface classes for platform integration
- Requirements - Expected config blocks or parameters from user configuration
Modules are loaded via the module section in YAML configuration:
module:
- containerlab
- frrEach module defines its output files. For example:
| Module | File | Description |
|---|---|---|
| containerlab | topo.yaml | Containerlab topology file |
| tinet | spec.yaml | TiNET specification file |
These files are generated automatically based on the network model.
Modules may provide FormatStyles for specific configuration formats:
# FRR module provides these FormatStyles
interfaceclass:
- name: ospf
config:
- name: ospf_config
format: FRRVtyshCLI # Provided by frr module
template:
- "router ospf"
- " network {{ .ip_network }} area 0"| Module | FormatStyles |
|---|---|
| frr | FRRVtyshCLI |
| containerlab | clabCmd |
| tinet | tinetSpecCmd |
Modules may require specific config blocks or parameters:
| Module | Required Parameters |
|---|---|
| containerlab |
image, kind
|
| tinet | image |
| kathara |
image (a device without one takes Kathara's base image) |
| frr | - |
If requirements are not met, dot2net reports an error.
A module and a scenario have to pass names to each other, and which names may cross is a short list rather than a growing one. Two kinds, and no others:
| What crosses | Where the scenario writes it | What it means |
|---|---|---|
| the class names a module publishes | use: |
"give this node what that class carries" |
| the value names a module publishes | values: |
"and set it up like this" |
| the hook names dot2net itself owns | config: - name: |
"run this, wherever this platform runs such things" |
A module's own block names are its business. A scenario naming one would be reaching into a module's insides, and the ways the two can talk to each other would multiply with every module and every feature until nobody could say what they are.
| Hook | When | Where each platform puts it |
|---|---|---|
startup |
once the node is up | containerlab exec:, TiNET cmds:, Kathara <device>.startup
|
teardown |
while the node is still up, before the lab is destroyed | the entry script runs them |
nodeclass:
- name: router
config:
- name: startup
template:
- "ip link set lo up"A class pulled in with use: can define a template under a hook name too, and
what the module has to do is merged ahead of what the scenario asked for
there — a scenario's commands run on ground the module has prepared. So a
scenario writes one line and nothing else:
nodeclass:
- name: router
use: [frrLogFile] # the module adds its own commands to this node's startupTwo classes of the scenario's own naming one hook is still rejected: nothing would say which of them wins.
A module's internal classes are named _clabNode, _katharaInterface and so
on. The underscore says the same thing the rule above says: these are not for a
scenario to name.
A scenario is written once, but the platforms are not alike, and where they differ dot2net either hides it or reports it. This table is what was measured while the modules were built; it is here because the differences decide what a scenario can ask for.
| containerlab | TiNET | Kathara | |
|---|---|---|---|
| Entry file |
topo.yaml at the output root |
spec.yaml at the output root |
kathara/lab.conf — the lab is a directory |
| How a file reaches a container | bind mount, per file | bind mount, per file |
volume, per directory only — hence mount_dirs
|
| Mount source paths | relative, resolved against the topology file's directory |
absolute: the output is piped to a shell, so $PWD/ is prefixed |
relative, resolved against the lab directory |
| Where startup commands go | exec: |
cmds: |
<device>.startup |
| Are commands run through a shell |
no — the line is split into words, so &&, pipes and redirection do not work |
no | yes — the startup file is a shell script |
| Management network | attached by default, off in dot2net's output unless asked for | none | none |
| Interface names | dot2net's own | dot2net's own |
ethN only, derived from the index in lab.conf
|
| A shared medium | a bridge node | a switches: entry |
a collision domain, which has no line of its own |
| More than one machine | one topology file per machine | one spec file per machine | cannot be expressed |
| A place to describe tests | none | a test: section in spec.yaml, which dot2net does not write yet |
ltest was removed in 3.8.0, replaced by the separate kathara-lab-checker |
The one that catches people is the shell. A startup command with && in it
works on Kathara, whose startup file is a shell script, and goes wrong on the
other two in different ways: containerlab splits the line into words and runs it
directly, so everything after && becomes an argument and the command still
exits 0; TiNET's output is piped to a shell on the host, so the part after
&& runs there instead of in the container. Write one command per line.
A module must supply UpdateConfig, where it registers what it adds to the
configuration — file definitions, classes, format styles, parameter rules. The
rest are optional, and a module implements only the ones it needs. Each runs at
a fixed point while the model is built, which is what decides what it can see.
| Interface | When it runs | What it is for |
|---|---|---|
UpdateConfig (required) |
before the model exists, with the scenario's configuration already loaded | register the module's own files, classes and formats. A module can read the scenario here — which is how the containerlab module knows whether to write one topology file or one per machine |
ObjectClassifier |
after the topology is built, before class labels are resolved | attach the module's classes to the objects that need them, and reshape the topology if it must. A label attached later would never become a class |
ParameterProvider |
after objects are named, before addresses and param_rule are assigned |
supply parameters that depend on the model — the endpoint names containerlab needs, the collision domain each Kathara interface sits on |
RequirementChecker |
after the model is complete, before files are generated | report what the platform cannot express, in the scenario's own words: a device name Kathara rejects, a directory it was not told it owns |
ParameterGenerator |
while param_rules of attach mode are evaluated |
produce the value sets behind a rule the module registered, such as the bind mounts of a node |
assert checks that classes the scenario expects to matter really are applied.
A class that nothing carries produces nothing, and a class label that never
matched is invisible — a typo in a DOT file looks exactly like a class with
nothing to say.
module:
- assert
nodeclass:
- name: router
values:
assert_used: "true" # at least one object must carry this classIf no object carries it, dot2net reports it rather than generating a lab with a
piece silently missing. The mark lives in values so that no core vocabulary is
spent on a checking concern.
Multiple modules can be loaded together. Each module's files are generated simultaneously:
module:
- containerlab # Generates topo.yaml
- frr # Provides FormatStyles for FRR configThis generates:
-
topo.yaml- Containerlab topology with node definitions -
r1/etc/frr/frr.conf,r2/etc/frr/frr.conf- FRR configuration files (user-defined)
module:
- containerlab
- frr
file:
- name: frr.conf
path: /etc/frr/frr.conf
nodeclass:
- name: router
values:
image: quay.io/frrouting/frr:8.5.4
kind: linux
config:
- file: frr.conf # From frr module
template:
- "hostname {{ .name }}"
- "!"
- name: startup
template:
- "vtysh -b"Result:
output/
├── topo.yaml # From containerlab module
├── r1/
│ └── frr.conf # User-defined
└── r2/
└── frr.conf
| Module | Purpose | Documentation |
|---|---|---|
| containerlab | Containerlab topology generation | Module: Containerlab |
| tinet | TiNET specification generation | Module: TiNET |
| kathara | Kathara lab.conf generation |
Module: Kathara |
| frr | FRR FormatStyle and file logging | Module: FRR |
| assert | Checks that classes the scenario expects really are applied | Module System |
- YAML Configuration - Module Configuration
- FormatStyle Design - How FormatStyles work
- File Output - File generation control