This page lists guidelines for developing Python code or Jinja2 templates in the AVD context. These rules apply to creating or updating any Python or Jinja2 file available in a aristanetworks/avd
repository.
For the Ansible collection arista.avd.
, we are required to follow guidelines from the official Ansible documentation for all Python code.
For PyAVD and other areas we follow common good practices as enforced by Ruff and Pylint.
The CI Pipeline (& pre-commit) for AVD enforces the following:
Configurations for the above tools can be found in:
- pyproject.toml
- .pre-commit-config.yaml
- pylintrc for the Ansible Collection
- pylintrc for everything else
- Jinja2 template syntax is enforced by j2lint.
- Additional guidelines are also provided and will be enforced by maintainers.
-
Description
A single space shall be added between Jinja2 curly brackets and a variable's name.
-
Example
{{ ethernet_interface }}
-
Description
When variables are used in combination with a filter,
|
shall be enclosed by space. -
Example
{{ my_value | to_json }}
-
Description
The nested jinja code block shall follow the following rules:
- All J2 statements must be enclosed by one space
- All J2 statements must be indented by four more spaces within the jinja delimiter
- To close a control, the end tag must have the same indentation level as the opening tag
- Indentation must be four spaces and NOT tabulation
-
Example
{# Initial block indentation #} {% if my_variable is arista.avd.defined %} {# Nested block indentation #} {% for ethernet_interface in ethernet_interfaces %} {% if ethernet_interface.name is arista.avd.defined %} {% set result = ethernet_interface.name %} {# ..... output truncated ..... #} {% endif %} {% endfor %}
-
Description
Instead of doing a for loop on a single line, the
join
filter should be leveraged as much as possible. -
Example
{{ ciphers | join(", ") }}
-
Description
To test if a variable is part of a list, the
in
operator should be used as much as possible to avoid longif/elif/else
block. -
Example
{% if underlay_routing_protocol is arista.avd.defined and underlay_routing_protocol in ['isis', 'ospf'] %}
-
Description
When a long CLI with multiple options must be built, use pure J2 logic and print.
-
Example
{% for ip_helper in vlan_interfaces[vlan_interface].ip_helpers | arista.avd.natural_sort %} {% set ip_helper_cli = "ip helper-address " ~ ip_helper %} {% if vlan_interfaces[vlan_interface].ip_helpers[ip_helper].vrf is arista.avd.defined %} {% set ip_helper_cli = ip_helper_cli ~ " vrf " ~ vlan_interfaces[vlan_interface].ip_helpers[ip_helper].vrf %} {% endif %} {% if vlan_interfaces[vlan_interface].ip_helpers[ip_helper].source_interface is arista.avd.defined %} {% set ip_helper_cli = ip_helper_cli ~ " source-interface " ~ vlan_interfaces[vlan_interface].ip_helpers[ip_helper].source_interface %} {% endif %} {{ ip_helper_cli }} {% endfor %}
-
Description
All variables shall use lowercase.
-
Example
{{ variable }}
-
Description
An underscore
_
should be used as a separator for a multi-word variable. -
Example
{{ my_variable_name }}
-
Description
For an iterable variable, the plural form shall be used.
-
Example
{{ ethernet_interfaces }}
-
Description
For variables in a for loop, the singular form shall be used.
-
Example
{{ ethernet_interfaces[ethernet_interface] }}
-
Description
Tilde
~
should be used for string concatenation as it automatically converts variables to a string. -
Example
{% set ip_helper_cli = ip_helper_cli ~ " source-interface " ~ vlan_interfaces[vlan_interface].ip_helpers[ip_helper].source_interface %}
-
Description
To test the type of a variable, it's recommended to use
is
/is not
keywords. -
Example
{# Test if variable is string #} {% if ethernet_interface is string %} {# Test if variable is not a string #} {% if ethernet_interface is not string %}
-
Description
To test the content of a variable, it's recommended to use
==
/!=
keywords. -
Example
{# Test if variable is equal to 'Ethernet1' #} {% if ethernet_interface == 'Ethernet1' %} {# Test if variable is not equal to 'Ethernet1' #} {% if ethernet_interface != 'Ethernet1' %}
!!! info PLUGIN-2 can do a test if the variable is defined and has a specific value
-
Description
All strings should be compared based on lowercase format.
-
Example
{% if underlay_routing_protocol is arista.avd.defined and underlay_routing_protocol | lower in ['isis', 'ospf'] %}
See the menu on the left for Ansible Collection Plugins.
-
Description
All tests to check if a variable is defined shall be done with
arista.avd.defined
. This test also does a deep test and doesn't require a test at an upper level. -
Example
{# Simple test #} {% if ethernet_interfaces is arista.avd.defined %} {# Deep test #} {% if router_bgp.vrfs[vrf].rd is arista.avd.defined %}
-
Description
The test
arista.avd.defined
shall be used to test if a variable is defined and has a specific value. -
Example
{% if vlan.name is arista.avd.defined('test') %}
-
Description
If a default value must be used, the
arista.avd.default
plugin shall be used instead of anif/else
block. The plugin can be used to fallback to different values until one of them is defined and valid. -
Example
{# Simple default test with one default value #} {{ vlan.name | arista.avd.default('test_vlan') }} {# Default test with a list of default options #} {{ vlan.name | arista.avd.default(default.vlan.name, 'test_vlan') }}