Skip to content

YAML Configuration

sat edited this page Jan 3, 2026 · 6 revisions

YAML Configuration

This page explains the YAML configuration system in dot2net, including class definitions, template syntax, and configuration patterns.

Overview

YAML configuration files define the generalized configuration part of topology-driven configuration. While DOT files describe "what" the network topology is, YAML files describe "how" each type of device should be configured.

Top-Level Configuration

Global Settings

Global settings control project-wide behavior:

global:
  path: local  # "local" = files in config dir, "default" = working dir
  mountsourcepath: local  # "local" or "abs" for mount paths
  nodeautoname: true  # Enable automatic node renaming

Key attributes:

  • path: File path specification for config files referenced in YAML

    • "local": Paths relative to input.yaml directory
    • "default": Paths relative to current working directory (shell execution location)
  • mountsourcepath: Mount path format for TiNET/Containerlab file bindings

    • "local": Use relative paths for file mounts (standard)
    • "abs": Use absolute paths for file mounts
  • nodeautoname: Automatic node renaming

    • true: Ignore DOT file node names, generate names using NodeClass prefix settings
    • false: Use DOT file node names as-is
    • Uses same mechanism as automatic interface naming

Automatic Object Naming

dot2net supports automatic naming for multiple object types using prefix-based naming schemes. This ensures consistent, predictable object names across your network configuration.

Supported Object Types

Always Enabled:

  • Interfaces: All interfaces are automatically named using InterfaceClass prefix
  • Connections: All connections are automatically named using ConnectionClass prefix
  • Network Segments: All detected network segments are automatically named using SegmentClass prefix

Configurable:

  • Nodes: When nodeautoname: true in global settings, uses NodeClass prefix

Default Prefixes

Object Type Default Prefix Example Names
Interface "net" net0, net1, net2
Connection "conn" conn0, conn1, conn2
Segment "seg" seg0, seg1, seg2
Node "node" node0, node1, node2

Custom Prefix Configuration

You can customize naming prefixes in class definitions using the prefix attribute:

# Custom interface naming
interfaceclass:
  - name: mgmt_interface
    prefix: "mgmt"  # Generates: mgmt0, mgmt1, mgmt2...

# Custom connection naming
connectionclass:
  - name: vlan_trunk
    prefix: "trunk"  # Generates: trunk0, trunk1, trunk2...

# Custom segment naming
segmentclass:
  - name: network_segment
    prefix: "net"  # Generates: net0, net1, net2...

# Custom node naming (when nodeautoname: true)
nodeclass:
  - name: router
    prefix: "rtr"  # Generates: rtr0, rtr1, rtr2...

Template Name References

All automatically named objects can reference their assigned names in templates using {{ .name }}:

# Connection template using auto-assigned name
connectionclass:
  - name: vlan_connection
    prefix: "vlan"
    config:
      - name: connection_setup
        template:
          - "# Connection: {{ .name }}"  # Outputs: vlan0, vlan1, etc.

# Segment template using auto-assigned name
segmentclass:
  - name: network_segment
    prefix: "net"
    config:
      - name: network_entry
        template:
          - "- name: {{ .name }}"  # Outputs: net0, net1, etc.

Benefits

  • Consistency: Sequential numbering ensures predictable naming patterns
  • Template compatibility: All objects support {{ .name }} template references
  • Cross-object references: Reliable names enable stable inter-object relationships
  • Configuration portability: Generated names work consistently across environments

Files Configuration

Define output files and their properties. For detailed explanation, see File Output.

file:
  - name: frr.conf
    path: /etc/frr/frr.conf  # Target path on nodes
    scope: node  # "node" (per-node, default) or "network" (single file)
    format: frr_format  # Reference to format definition
    output: node  # "node" (subdirectory) or "root" (lab root)
    name_prefix: ""  # Prefix for output filename
    name_suffix: ""  # Suffix for output filename

File Formats (FormatStyle)

Define text formatting for generated files. dot2net uses a two-phase formatting approach:

format:
  - name: frr_format
    # Format Phase (applied during config block generation)
    format_lineprefix: " "
    format_linesuffix: ""
    format_lineseparator: "\n"
    format_blockprefix: ""
    format_blocksuffix: "!"
    # Merge Phase (applied when combining multiple blocks)
    merge_blockseparator: "\n"
    merge_resultprefix: ""     # Optional: wrap entire merged result
    merge_resultsuffix: ""     # Optional: wrap entire merged result

Two-Phase Processing:

  1. Format Phase - Individual config block formatting:

    • format_lineprefix: Added before each line
    • format_linesuffix: Added after each line
    • format_lineseparator: Used to join lines (default: "\n")
    • format_blockprefix: Added before entire config block
    • format_blocksuffix: Added after entire config block
  2. Merge Phase - Combining multiple formatted blocks:

    • merge_blockseparator: Used to join multiple config blocks (default: "\n")
    • merge_resultprefix: Added before the entire merged result (optional)
    • merge_resultsuffix: Added after the entire merged result (optional)

Backward Compatibility (v0.6.x):

Legacy field names are still supported for compatibility but will be removed in v0.7.0:

  • lineprefixformat_lineprefix
  • linesuffixformat_linesuffix
  • lineseparatorformat_lineseparator
  • blockprefixformat_blockprefix
  • blocksuffixformat_blocksuffix
  • blockseparatormerge_blockseparator

Real-world examples:

# FRR vtysh command format
format:
  - name: frr_vtysh
    format_lineseparator: "\" -c \""
    format_blockprefix: "vtysh -c \"conf t\" -c \""
    format_blocksuffix: "\""

# Containerlab YAML array format
format:
  - name: clab_yaml
    merge_blockseparator: ", "

# Containerlab command list format
format:
  - name: clab_cmd
    format_lineprefix: "      - "
    merge_blockseparator: "\n"

Complete example demonstrating all attributes:

format:
  - name: comprehensive_format
    # Format Phase
    format_lineprefix: "  "           # Indent each line
    format_linesuffix: ";"            # Add semicolon to each line
    format_lineseparator: " \\\n"     # Line continuation with backslash
    format_blockprefix: "START {\n"   # Block opening
    format_blocksuffix: "\n} END"     # Block closing
    # Merge Phase
    merge_blockseparator: "\n---\n"   # Separator between blocks
    merge_resultprefix: "# BEGIN\n"   # Wrap entire result
    merge_resultsuffix: "\n# END"     # Wrap entire result

Processing flow example:

Input template: ["router ospf", "network 10.0.1.0/24 area 0"]

↓ Format Phase - formatConfigLines (comprehensive_format)
Step 1 - Apply line formatting (format_lineprefix/suffix/separator):
  "  router ospf; \\\n  network 10.0.1.0/24 area 0;"

↓ Format Phase - formatSingleConfigBlock
Step 2 - Apply block formatting (format_blockprefix/suffix):
  "START {\n  router ospf; \\\n  network 10.0.1.0/24 area 0;\n} END"

↓ Merge Phase - mergeConfigBlocks (multiple blocks)
Step 3 - Join blocks with merge_blockseparator:
  "Block1\n---\nBlock2\n---\nBlock3"

↓ Merge Phase - wrap result (merge_resultprefix/suffix)
Step 4 - Wrap entire result:
  "# BEGIN\nBlock1\n---\nBlock2\n---\nBlock3\n# END"

When Merge Phase is used:

  • Sorter templates (style: sort) that combine multiple config blocks
  • blocks.before / blocks.after features that merge child configs

Layers Configuration

Define protocol layers and IP policies:

layer:
  - name: ip
    default_connect: true  # Default layer for connections
    policy:
      - name: ip
        range: 10.0.0.0/16
        prefix: 24
      - name: lo
        type: loopback
        range: 10.0.255.0/24

Key attributes:

  • name: Layer identifier (used in template variables)
  • default_connect: Default connection layer for IP segment discovery
    • true: ConnectionClasses without explicit layer specification are considered connected on this layer
    • false: Not used for default connections
    • Used by DefaultConnectionLayer() function for segment exploration
  • policy: Array of IP address policies for this layer

IP Policy attributes:

  • name: Policy identifier
  • type: Policy type ("ip" for normal interfaces, "loopback" for loopback interfaces)
  • range: IP address range (CIDR notation)
  • prefix: Default prefix length for subnets

Generated template variables: Each layer automatically provides template variables:

  • {{ .{name}_addr }} - IP address
  • {{ .{name}_net }} - Network address
  • {{ .{name}_plen }} - Prefix length
  • {{ .{name}_protocol }} - Protocol identifier
  • {{ .{name}_loopback }} - Loopback address

Example usage:

# Template can use:
template:
  - "interface {{ .name }}"
  - " ip address {{ .ip_addr }}/{{ .ip_plen }}"
  - " description Connected to {{ .ip_net }}"

Management Layer

Configure management network independent of main topology (optional):

mgmt_layer:
  name: mgmt                    # Layer name identifier
  range: 192.168.1.0/24         # IP address range for management interfaces
  gateway: 192.168.1.1          # External gateway (reserved from auto-assignment)
  interface_name: mgmt0         # Management interface name (default: "mgmt0")

Key attributes:

  • name: Management layer identifier (used in template variables)
  • range: IP address range for automatic assignment to management interfaces
  • gateway: External gateway address (automatically reserved from assignment pool)
  • interface_name: Name for management interface on each node (default: "mgmt0")

Behavior:

  • Independent of topology: Management interfaces are automatically created on every node
  • Separate IP space: Uses dedicated IP range independent of main network layers
  • External connectivity: Designed for out-of-band management (Containerlab external networks)
  • Automatic creation: Management interfaces added during addSpecialInterfaces() processing

Generated template variables:

  • {{ .{name}_addr }} - Management IP address
  • {{ .{name}_net }} - Management network address
  • {{ .{name}_plen }} - Management prefix length

Usage with Containerlab: Management layer integrates with Containerlab's external network connectivity for out-of-band device management.

Example:

mgmt_layer:
  name: mgmt
  range: 10.255.255.0/24
  gateway: 10.255.255.1
  interface_name: eth0

This creates eth0 management interface on each node with automatic IP assignment from 10.255.255.0/24 range.

Parameter Rules

Define automatic parameter assignment:

param_rule:
  - name: vlan_id
    assign: segment  # "object" (default), "segment", "connection"
    layer: ip        # Required when assign: "segment"
    type: integer    # "integer" (default) or "file"
    min: 100
    max: 1001
    header: vlan     # Prefix for generated values
    footer: ""       # Suffix for generated values
  - name: conn_id
    assign: connection
    header: conn
    min: 0
  - name: hostname_list
    type: file
    sourcefile: ./hostnames.txt  # Read values from file

Key attributes:

  • name: Parameter rule identifier (must match parameter names in class definitions)
  • assign: Assignment scope
    • "object" (default): Individual assignment per node/interface
    • "segment": Same value for interfaces in the same network segment
    • "connection": Same value for interfaces connected by the same connection
  • layer: Protocol layer specification (required when assign: "segment")
  • type: Parameter generation type
    • "integer" (default): Generate numeric sequences
    • "file": Read values from external file

Integer type attributes:

  • min: Starting value (default: 0)
  • max: Maximum value (optional, for validation)
  • header: Prefix string (default: "")
  • footer: Suffix string (default: "")
  • Generation pattern: {header}{min+i}{footer}

File type attributes:

  • sourcefile: Path to source file (relative to config file)
  • Format: One value per line, read sequentially

Assignment behavior examples:

# Example: VLAN assignment per segment
param_rule:
  - name: vlan_id
    assign: segment
    layer: ip
    min: 100
    header: vlan
# Result: vlan100, vlan101, vlan102... (same value for all interfaces in each segment)

# Example: Connection ID per connection
param_rule:
  - name: conn_id
    assign: connection
    header: conn
    min: 0
# Result: conn0, conn1, conn2... (same value for both ends of each connection)

# Example: Individual interface numbering
param_rule:
  - name: if_num
    assign: object  # or omit (default)
    min: 1
# Result: 1, 2, 3... (unique value per interface)

Value Class (Attach Mode)

The Value class allows attaching multiple parameter sets to a single object. This is useful for scenarios like:

  • Multiple VLAN configurations per switch
  • Multiple static routes per router
  • Dynamic file mount configurations

Basic Structure:

param_rule:
  - name: vlan_ids
    mode: attach           # Required for Value class
    source:
      type: range          # "range", "sequence", "list", or "file"
      start: 100
      end: 103
    param_format:
      vlan_id: "{{ .value }}"
    config:
      - name: vlan_entry
        template:
          - "vlan {{ .vlan_id }}"

Mode:

  • distribute (default): One value per object (legacy behavior)
  • attach: Multiple Values attached to one object

Source Types:

Type Description Fields
range Numeric range start, end
sequence Formatted sequence start, end, format
list Explicit value list values (array)
file Values from file path

Template Reference:

In class config templates, reference Values using values_ prefix:

node_class:
  - name: switch
    params: [vlan_ids]    # Reference the param_rule
    config:
      - name: startup
        template:
          - "! VLAN Configuration"
          - "{{ .values_vlan_entry }}"  # Reference formatted Value output

Complete Example:

# Define param_rule with attach mode
param_rule:
  - name: static_routes
    mode: attach
    source:
      type: list
      values:
        - { network: "10.0.0.0/8", gateway: "192.168.1.1" }
        - { network: "172.16.0.0/12", gateway: "192.168.1.2" }
    config:
      - name: route_entry
        template:
          - "ip route {{ .network }} {{ .gateway }}"

# Reference in node class
node_class:
  - name: router
    params: [static_routes]
    config:
      - file: router.conf
        template:
          - "! Static Routes"
          - "{{ .values_route_entry }}"

Module Generators:

Modules can provide generators for dynamic Value creation:

# Containerlab module provides clab.filemounts generator
# TiNET module provides tinet.filemounts generator
# These are used internally for bind mount generation

Module Configuration

Load external modules. For details on how modules work, see Module System.

module:
  - tinet        # TiNET spec.yaml generation
  - containerlab # Containerlab topo.yaml generation
  - frr          # FRR configuration helpers

Class System

dot2net uses a comprehensive class system to organize network objects and their configurations:

NetworkClass Definition

NetworkClass defines network-wide configuration:

networkclass:
  - name: main_network
    values:
      project: "example_network"
      version: "1.0"
    config:
      - file: network_info.txt
        template:
          - "# Network: {{ .project }} v{{ .version }}"

NodeClass Definition

NodeClass defines device types and their basic properties:

nodeclass:
  - name: router
    virtual: false  # true = exclude from final output files
    values:
      kind: linux
      image: quay.io/frrouting/frr:8.5.0
    interface_policy: [ip]
    params: [lo]
    config:
      - file: frr.conf
        template:
          - "router ospf"
          - " ospf router-id {{ .ip_loopback }}"

Key attributes:

  • virtual: true - Exclude from deployment files (useful for topology modeling)
  • values - Default parameter values
  • interface_policy - IP assignment policies for interfaces
  • params - Parameter rules for automatic assignment
  • config - Configuration template blocks

InterfaceClass Definition

InterfaceClass defines interface types and behaviors:

interfaceclass:
  - name: vlan
    layers: [ip]  # Restrict to specific layers
    params: [vlan]
    config:
      - group: params.txt
        priority: -1  # Processing priority (lower = earlier)
        template:
          - "vlan {{ .vlan }} for {{ .node_name }}.{{ .name }}"

Key attributes:

  • layers - Specify which protocol layers this class applies to
  • priority - Control processing order within groups
  • group - Accumulate configuration blocks for later merging
  • neighbors - Reference adjacent interfaces for iterative configuration
  • classmembers - Reference objects in the same class for member-based configuration

ConnectionClass Definition

ConnectionClass defines connection types and properties (new specification):

connectionclass:
  - name: vlan_conn
    prefix: "vlan_trunk"  # Auto-naming prefix for connections
    params: [conn_id, vlan_id]  # Auto-assigned by parameter rules
    config:
      - group: network_config.txt
        priority: -2
        template:
          - "# VLAN Connection: {{ .name }} (ID: {{ .conn_id }}, VLAN: {{ .vlan_id }})"
          - "connection {{ .name }} type vlan_trunk vlan {{ .vlan_id }}"

Key attributes:

  • prefix - Automatic naming prefix (generates names like vlan_trunk0, vlan_trunk1)
  • params - Parameter rules for automatic value assignment
  • Connection templates use {{ .name }} for self-reference

SegmentClass Definition

SegmentClass defines network segment configurations (new specification):

segmentclass:
  - name: network_segment
    layer: ip
    prefix: "net"  # Auto-naming prefix for segments
    params: [segment_id]  # Auto-assigned by parameter rules
    config:
      - name: network_entry
        template:
          - "- name: {{ .name }}"  # Auto-assigned segment name
          - "  vlan: auto"
          - "  nodes:"
          - "{{ .interfaces_segment_nodes }}"

Key attributes:

  • layer - Protocol layer for this segment type
  • prefix - Automatic naming prefix (generates names like net0, net1)
  • params - Parameter rules for automatic value assignment
  • Segments are automatically detected from network topology
  • Segment templates use {{ .name }} for self-reference
  • Used with relational class labels (segment#class_name)

GroupClass Definition

GroupClass defines logical groupings (subnets, ASes, clusters):

groupclass:
  - name: backbone_area
    virtual: false  # true = exclude from final output files
    params: [ospf_area_id]  # Parameter rules for automatic assignment
    values:
      area_type: "backbone"
      priority: "high"
    config:
      - file: area_config.txt
        template:
          - "# OSPF Area {{ .ospf_area_id }} ({{ .area_type }})"
          - "area {{ .ospf_area_id }} authentication"

Key attributes:

  • virtual - Exclude from final output if true
  • params - Parameter rules for automatic assignment
  • values - Default parameter values
  • Used for logical network organization (DOT subgraphs)

Template Description Styles

dot2net supports two template description styles for assembling configuration templates. For conceptual understanding of when to use each approach, see Template System - Template Assembly Approaches.

Overview: ConfigTemplate Coordination

ConfigTemplates can be coordinated in several ways:

Mechanism Purpose Description
Hierarchical Template embedding Embed child templates via {{ .self_xxx }} or {{ .interfaces_xxx }}
Sort Group aggregation Collect templates into groups, then merge with sorting
blocks.before/after Output ordering Control position of blocks relative to other templates
required_params Conditional output Skip entire block if parameters are missing
depends Processing order Required for same-object template embedding (e.g., {{ .self_xxx }})

Typical combinations:

  • Hierarchical + blocks: Use Hierarchical for embedding, blocks for cross-class ordering (e.g., interface configs before router section)
  • Sort + blocks: Aggregate by priority, with additional ordering constraints
  • Any style + required_params: Add conditional output to optional configuration sections

Hierarchical Style (Standard)

Default approach with strict template relationship management:

config:
  - file: ospfd.conf
    template:
      - "router ospf"
      - " ospf router-id {{ .RouterID }}"
      - " {{ .interfaces_ospf_network }}"  # Embedded template position

Characteristics:

  • Strict ordering: Template embedding positions must be explicitly defined
  • Precise relationships: Template dependencies are strictly managed
  • Complex control: Supports intricate dependency relationships

Advantages:

  • Strict order control
  • Clear template relationships
  • Accurate expression of complex dependencies

Disadvantages:

  • More complex to write
  • Requires detailed position specification

Sort Style

Alternative approach using loose relationship management with Group Templates + Sorter Templates:

Group Templates

Accumulate configuration blocks for later processing:

config:
  - group: "ospf6d.conf"
    priority: -1  # Order control via priority only
    template:
      - "log file /var/log/frr.log"

Sorter Templates

Merge and output accumulated group configurations:

config:
  - file: "ospf6d.conf"
    style: sort
    sort_group: "ospf6d.conf"  # References group name
    template:
      - "router ospf6"
      - " ospf6 router-id {{ .RouterID }}"

Characteristics:

  • Simple addition: Just add templates to defined groups
  • Loose management: Template relationships are loosely controlled
  • Priority-based ordering: Order control through priority values only

Advantages:

  • Simple description
  • Easy template addition
  • Flexible management approach

Disadvantages:

  • Difficult to express strict dependencies
  • Not suitable for complex order control

Named Templates (Hierarchical Mechanism)

Core mechanism for template embedding relationships in Hierarchical style:

config:
  - name: "base_config"
    template:
      - "base settings"
  - name: "advanced_config"
    depends: ["base_config"]  # Required for same-object template dependencies
    template:
      - "advanced settings"
      - "{{ .base_config }}"  # Template embedding

Dependency Requirements:

  1. Cross-object embedding (Parent-Child relationships):

    # NodeClass template embedding InterfaceClass templates
    template:
      - "interface configuration:"
      - "{{ .interfaces_config }}"  # No depends needed - hierarchy is clear
  2. Same-object embedding (Same-level templates):

    # Templates within the same object class
    config:
      - name: "base"
        template: ["base settings"]
      - name: "extended"
        depends: ["base"]  # Required - processing order matters
        template: ["{{ .base }}", "extended settings"]

Purpose:

  • Essential for Hierarchical style: All template relationships require named templates
  • depends required only for same-object template dependencies (reorderConfigTemplates processing)
  • Parent-child class relationships have implicit dependencies through object hierarchy

Conditional Template Output (required_params)

The required_params field allows templates to be conditionally skipped when specified parameters are missing or empty:

nodeclass:
  - name: router
    config:
      # Always output
      - name: base_config
        template: ["hostname {{ .name }}"]

      # Only output if 'mem' parameter exists and is non-empty
      - name: memory_config
        required_params: [mem]
        template: ["memory {{ .mem }}"]

      # Only output if both 'cpus' AND 'sysctl' exist
      - name: resource_config
        required_params: [cpus, sysctl]
        template:
          - "cpus {{ .cpus }}"
          - "sysctl {{ .sysctl }}"

Behavior:

  • If all parameters in required_params exist and are non-empty → template is processed
  • If any parameter is missing or empty → entire template block is skipped
  • Empty string values are treated as "not exists"

Use cases:

  • Optional parameters (mem, cpus, sysctl) in container configs
  • Module-provided templates that depend on user-defined parameters
  • Avoiding if-statements in templates for cleaner, declarative configuration

Comparison with template if-statements:

Approach Pros Cons
required_params Declarative, no template logic Block-level only
{{ if .param }} Fine-grained control Template becomes complex

Note: required_params checks both regular parameters and relative parameters (e.g., self_startup, values_*).

Config Block Ordering (blocks.before/after)

The blocks field controls where a template's output is positioned relative to other templates:

nodeclass:
  - name: router
    config:
      # Main router config - other blocks position relative to this
      - name: router_base
        file: frr.conf
        template:
          - "router ospf"
          - " router-id {{ .ip_loopback }}"

interfaceclass:
  - name: default
    config:
      # Interface config should appear BEFORE router_base in frr.conf
      - name: interface_config
        file: frr.conf
        blocks:
          before: [router_base]
        template:
          - "interface {{ .name }}"
          - " ip address {{ .ip_addr }}/{{ .ip_plen }}"

      # Network statements should appear AFTER router_base
      - name: ospf_network
        file: frr.conf
        blocks:
          after: [router_base]
        template:
          - " network {{ .ip_net }} area 0"

Result in frr.conf:

interface net0
 ip address 10.0.0.1/24
interface net1
 ip address 10.0.0.5/24
router ospf
 router-id 10.255.0.1
 network 10.0.0.0/24 area 0
 network 10.0.0.4/24 area 0

Behavior:

  • blocks.before: [name1, name2] - This template's output appears before the listed templates
  • blocks.after: [name1, name2] - This template's output appears after the listed templates
  • Multiple templates can be listed; ordering is determined by dependency resolution
  • Works across different object classes (e.g., InterfaceClass referencing NodeClass templates)

Use cases:

  • FRR/Quagga configs where interface definitions must precede router sections
  • Any config format requiring specific section ordering
  • Separating logical config sections while maintaining correct output order

Relationship with depends:

  • depends controls processing order (template A must be evaluated before template B)
  • blocks.before/after controls output order (template A's output appears before/after template B's output)
  • Both can be used together when needed

Template Variables and Cross-Object References

Basic Parameters

Templates have access to a symmetric parameter namespace:

  • {{ .name }} - Object name
  • {{ .ip_addr }} - IP address
  • {{ .ip_plen }} - IP prefix length
  • {{ .vlan_id }} - VLAN identifier

Cross-Object References (New Specification)

Templates can reference related objects with consistent prefixes:

Interface Templates Referencing Connections

template:
  - "interface {{ .name }}"
  - " description Connected via {{ .conn_name }}"
  - " vlan {{ .conn_vlan_id }}"

Interface Templates Referencing Nodes

template:
  - "interface {{ .name }}"
  - " description Interface on {{ .node_name }}"
  - " ip address {{ .ip_addr }}/{{ .ip_plen }}"

Connection Templates (Self-Reference)

template:
  - "connection {{ .name }}"
  - " type vlan_trunk"
  - " vlan {{ .vlan_id }}"

Variable Naming Rules

  • Connection templates: Use {{ .name }} for self-reference
  • Interface templates: Use {{ .conn_name }} for connection reference
  • Consistent prefixing: All connection references use conn_ prefix, node references use node_ prefix

Configuration Processing Flow

1. Parameter Assignment Order

Network → Node → Connection → Interface → Group → Segment

Critical: Connection parameters must be assigned before Interface parameters to enable cross-object references.

2. Template Processing Order

  1. Dependency Resolution: reorderConfigTemplates resolves template dependencies
  2. Group Processing: Group templates accumulate configuration blocks
  3. Sorter Processing: Sorter templates merge and output final configurations
  4. Priority Control: Lower priority values (-3, -2, -1) process first

3. Cross-Object Reference Setup

  • Interface namespace includes connection parameters with conn_ prefix
  • Interface namespace includes node parameters with node_ prefix
  • References are established during parameter assignment phase

Best Practices

Class Design

  1. Virtual Nodes: Use virtual: true for topology modeling without deployment
  2. Modular Extension: Use multiple classes for additional functionality
  3. Clear Naming: Use descriptive class names that reflect their purpose

Template Design

  1. Deterministic Templates: Avoid control syntax (for, if) - use parameter symmetry instead
  2. Consistent Prefixing: Use conn_ and node_ prefixes for cross-object references
  3. Priority Management: Use negative priorities for foundational configurations

Parameter Management

  1. Automatic Assignment: Leverage parameter rules for consistent value assignment
  2. Manual Override: Use direct value assignment in DOT files when needed
  3. Cross-Object Consistency: Maintain consistent naming across related objects

Example: Complete VLAN Configuration

YAML Configuration

nodeclass:
  - name: router
    values:
      kind: linux
      image: quay.io/frrouting/frr:8.5.0
    params: [lo]

connectionclass:
  - name: vlan_conn
    prefix: "vlan_trunk"
    params: [conn_id, vlan_id]
    config:
      - group: network_config.txt
        priority: -2
        template:
          - "# Connection: {{ .name }} VLAN {{ .vlan_id }}"

interfaceclass:
  - name: trunk_interface
    config:
      - group: interface_config.txt
        priority: -1
        template:
          - "interface {{ .name }}"
          - " description Trunk to {{ .opp_node_name }} via {{ .conn_name }}"
          - " switchport mode trunk"
          - " switchport trunk allowed vlan {{ .conn_vlan_id }}"

segmentclass:
  - name: trunk_segment
    layer: ip
    config:
      - group: network_config.txt
        priority: -3
        template:
          - "# Trunk Segment: {{ .segment_name }} with {{ .segment_interface_count }} interfaces"

DOT Usage

digraph {
    r1 [xlabel="router"];
    r2 [xlabel="router"];

    r1 -> r2 [
        dir="none",
        class="vlan_conn; segment#trunk_segment",
        taillabel="trunk_interface",
        headlabel="trunk_interface"
    ];
}

This configuration demonstrates the complete workflow from class definition to cross-object references, showcasing dot2net's powerful template and parameter system.

Advanced Object References

Neighbor References (Interface-only)

Interfaces can reference adjacent interfaces through neighbors configuration for iterative template processing:

interfaceclass:
  - name: ospf_interface
    neighbors:
      - layer: ip  # Process neighbors on IP layer
        config:
          - name: neighbor_config
            template:
              - "# Neighbor: {{ .opp_node_name }} via {{ .conn_name }}"
              - "neighbor {{ .opp_ip_addr }} area {{ .ospf_area }}"

Key features:

  • Interface-specific: Only InterfaceClass supports neighbors
  • Layer-based: Process neighbors on specific protocol layers
  • Automatic iteration: Template executes for each adjacent interface
  • Neighbor parameters: Access {{ .opp_* }} parameters for opposite interface

Class Member References (Multi-object)

Multiple object types can reference other objects in the same class through classmembers:

Node Class Members

nodeclass:
  - name: router_cluster
    classmembers:
      - nodes: ["router"]  # Reference other nodes with class "router"
        config:
          - name: cluster_peers
            template:
              - "# Cluster peer: {{ .name }} at {{ .ip_loopback }}"
              - "peer {{ .ip_loopback }} cluster-member"

Interface Class Members

interfaceclass:
  - name: trunk_ports
    classmembers:
      - interfaces: ["trunk_interface"]  # Reference interfaces in same class
        include_self: false  # Exclude current interface from iteration
        config:
          - name: trunk_aggregation
            template:
              - "# Trunk peer: {{ .node_name }}.{{ .name }}"
              - "trunk-peer {{ .node_name }} interface {{ .name }}"

Connection Class Members

connectionclass:
  - name: vlan_connection
    classmembers:
      - connections: ["vlan_conn"]  # Reference connections in same class
        config:
          - name: vlan_coordination
            template:
              - "# VLAN peer connection: {{ .name }} VLAN {{ .vlan_id }}"

Segment Class Members

segmentclass:
  - name: network_segment
    classmembers:
      - nodes: ["router"]  # Reference nodes within the segment
        config:
          - name: segment_routing
            template:
              - "# Segment node: {{ .name }} in segment {{ .segment_name }}"
              - "segment-id {{ .segment_id }} node {{ .name }}"

Supported object types for classmembers:

  • NodeClass: Can reference nodes, interfaces, connections
  • InterfaceClass: Can reference nodes, interfaces, connections
  • ConnectionClass: Can reference nodes, interfaces, connections
  • SegmentClass: Can reference nodes, interfaces, connections

Key attributes:

  • nodes / interfaces / connections - Specify which object classes to reference
  • node / interface / connection - Single class name (alternative syntax)
  • include_self - Whether to include the current object in iteration (default: true)
  • config - Template blocks that execute for each referenced object

Clone this wiki locally