You're reading the development documentation. Release version of the same page is here.

Custom Plugins

netlab supports dynamically loadable plugins allowing you to implement custom data model transformations without adding nerd knobs to the core topology transformation. You might want to write your own plugins or use plugins shipped with netlab:

Using Plugins

Plugins needed by a topology file are listed in the plugin top-level element, for example:

plugin: [ ebgp.utils ]

module: [ ospf, bgp ]

You can specify additional (system-wide) plugins in system defaults (defaults.plugin) or as a CLI parameter in netlab create or netlab up commands.

Plugins can define their own netlab attributes that you can use to configure plugin-provided functionality. For example, the EBGP utilities defines bgp.password attribute that can be used to enable MD5 authentication of EBGP sessions:

provider: clab
defaults.device: eos
module: [ bgp ]
plugin: [ ebgp.utils ]

  r1: 65101
  r2: 65000

- r1:
  bgp.password: Test

Plugins providing support for additional networking features usually rely on Jinja2 templates to configure those features, limiting their use to a subset of supported platforms. Please check the plugin documentation for more details.

Developing Plugins

Plugins are either Python files or directories containing Python code plus configuration templates. They are loaded from the current directory or netsim/extra directory.


This is an underdocumented feature. A few commonly-used functions are defined in netsim.api; performing operations beyond simple data transformation might require digging through the source code. You might want to open a discussion on netsim-tools GitHub repository before proceeding.

For simple plugins, the plugin name specifies the file name (without the .py extension). For plugin packages, the plugin name specifies the directory with Python module and one or more Jinja2 templates (one per supported netlab_device_type/ansible_network_os).

Plugins can define well-known functions that are invoked during the topology transformation process which includes these steps:

  • execute plugin init function

  • check topology top-level elements

  • adjust global parameters (defaults), node list, link list, and address pools

  • execute plugin pre_transform function

  • execute module pre_transform function

  • adjust groups (including setting node data from node_data)

  • execute plugin pre_node_transform function

  • transform node data

  • execute plugin post_node_transform function

  • execute plugin pre_link_transform function

  • transform link data

  • execute plugin post_link_transform function

  • execute module post_transform function

  • execute plugin post_transform function

Every plugin function is called with a single topology argument: the current topology data structure. The node- or link-manipulation functions must iterate over topology.nodes or topology.links lists.

Plugins extending configuration modules might have to define additional module attributes. The module attribute lists have to be extended in the init function before any module validation code is executed.

Sample Plugin

All anycast servers in a BGP anycast topology should have the same AS number, but do not need IBGP sessions between themselves. A custom plugin deletes IBGP sessions for any node with bgp.anycast attribute.

This is the topology file used in BGP anycast example. It uses node_data attribute on a BGP AS group to set bgp.anycast node attribute on any node in AS 65101

plugin: [ bgp-anycast ]

module: [ ospf, bgp ]

  device: iosv

      members: [ l1, l2, l3, s1 ]
      rr: [ s1 ]
      members: [ a1,a2,a3 ]


  [ l1, l2, l3, s1, a1, a2, a3 ]

links: [ s1-l1, s1-l2, s1-l3, l2-a1, l2-a2, l3-a3 ]

The plugin imports common netsim module to create error messages, and api module to get common utility functions.

import sys
from box import Box
from netsim import common
from netsim import api

The initialization function adds anycast attribute to bgp node attributes:

def init(topo: Box) -> None:

The custom transformation is executed as the last step of the topology transformation – the post_transform function removes IBGP neighbors from all nodes with bgp.anycast attribute.

def post_transform(topo: Box) -> None:
  for node in topo.nodes.values():
    if 'bgp' in node:
      if 'anycast' in node.bgp:
        node.bgp.advertise_loopback = False
        node.bgp.neighbors = [
          n for n in node.bgp.neighbors
            if n.type != 'ibgp' ]

The post_transform function should also set the config node parameter to deploy custom configuration template that creates additional loopback interface with the anycast IP address.

def post_transform(topo: Box) -> None:
  config_name = api.get_config_name(globals())
  for node in topo.nodes.values():
    if 'bgp' in node:
      if 'anycast' in node.bgp:


  • api.get_config_name gets the config_name plugin attribute (set to the plugin directory during the plugin initialization process) and reports an error if the plugin calling it isn’t a part of a plugin package.

  • api.node_config appends the specified custom configuration template to the list of node configuration templates. While equivalent to…

    node.config = node.get('config',[]).append(template)

    … the utility function handles edge cases like missing config attribute or duplicate configuration templates.