Developing Plugins¶
Plugins are either Python files or directories containing Python code plus configuration templates. netlab tries to locate them in the plugin search path (specified in the defaults.paths.plugin setting), which usually includes the user search path and the netsim/extra
networklab package directory.
Tip
You can inspect the plugin search path with the netlab show defaults paths.plugin command and modify it if needed.
The plugin name specifies either a Python file name (without the .py
extension) or a directory with the plugin.py
Python module, optional plugin defaults (defaults.yml
), and Jinja2 templates (one per supported netlab_device_type/ansible_network_os).
Warning
This is an underdocumented feature. Performing operations beyond simple data transformation might require digging through the source code. Before proceeding, you might want to open a discussion on netlab GitHub repository.
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 the topology.nodes
dictionary or the topology.links
list.
Plugins extending configuration modules might have to define additional module attributes. The module attribute lists must be extended before any module validation code is executed, either with the plugin defaults or in the plugin init function.
Plugin Metadata¶
A plugin can specify global variables that are used to influence the plugin behavior or order of execution:
_requires
: A list of prerequisite modules and plugins. netlab will abort if any prerequisite plugins are not listed in the topology.plugin list, or if any of the prerequisite modules are not used by at least one node._execute_after
: A list of plugins that should execute before the current plugin. For example, the ebgp.multihop plugin has to be executed after ebgp.utils plugin, and therefore defines_execute_after = [ 'ebgp.utils' ]
_config_name
: The name of extra configuration templates to add to the node config attribute when a node using the plugin functionality requires additional device configuration. This variable is set during the plugin initialization process, but it’s still recommended to define it in the plugin and set its value to a string to prevent mypy complaints.
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.
The topology file used in the BGP anycast example uses group node data 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:
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 bgp.anycast attribute is defined in the plugin defaults (anycast/defaults.yml
) (see Lab Topology Attribute Validation for details):
bgp.attributes.node.anycast:
type: ipv4
use: prefix
The plugin imports netsim.api module to get access to the plugin helper functions.
import sys
from box import Box
from netsim import api
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 also sets the config node parameter to deploy a custom configuration template that creates additional loopback interface with the anycast IP address.
def post_transform(topo: Box) -> None:
global _config_name
for node in topo.nodes.values():
if 'bgp' in node:
if 'anycast' in node.bgp:
...
api.node_config(node,_config_name)
Notes:
The global
_config_name
variable is set during the plugin initialization.api.node_config
appends the specified custom configuration template to the list of node configuration templates. While equivalent to…
node.config.append(template)
… the utility function handles edge cases like a missing config attribute or duplicate configuration templates.