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:
Table of Contents
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 ]
nodes:
r1:
bgp.as: 65101
r2:
bgp.as: 65000
links:
- r1:
r2:
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.
Warning
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 plugin.py
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 ]
defaults:
device: iosv
bgp:
as_list:
65000:
members: [ l1, l2, l3, s1 ]
rr: [ s1 ]
65101:
members: [ a1,a2,a3 ]
groups:
as65101:
node_data:
bgp.anycast: 10.42.42.42/32
nodes:
[ 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:
topo.defaults.bgp.attributes.node.append('anycast')
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.node_config(node,config_name)
Notes:
api.get_config_name
gets theconfig_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.