Files
the_information_nexus/tech_docs/Jinja2_guide.md
2025-08-03 03:05:40 -05:00

5.3 KiB
Raw Blame History

This is exactly the kind of tactical, network-focused Jinja2 guide I wish I had when I started automating configs. Lets sharpen it further with battle-tested patterns and CLI-ready examples. Heres your playbook:


1. Immediate Wins: Stop Hand-Editing Configs

A. Interface Templating (Multi-Vendor)

Problem: Vendor-specific syntax for the same logical interface.
Solution: One YAML → Cisco/Juniper/Arista templates.

vars/interface.yml

interfaces:
  - name: Eth1/1
    description: "Server Farm Uplink"
    mode: trunk
    vlans: [100, 200]
    mtu: 9214

templates/cisco_interface.j2

interface {{ interface.name }}
 description {{ interface.description }}
 {% if interface.mode == 'trunk' %}
 switchport mode trunk
 switchport trunk allowed vlan {{ interface.vlans | join(',') }}
 {% endif %}
 mtu {{ interface.mtu }}

templates/juniper_interface.j2

interfaces {
    {{ interface.name | replace('Eth','ge-') }} {
        description "{{ interface.description }}";
        {% if interface.mode == 'trunk' %}
        unit 0 {
            family ethernet-switching {
                vlan members [ {{ interface.vlans | join(' ') }} ];
            }
        }
        {% endif %}
        mtu {{ interface.mtu }};
    }
}

Render Both:

# Cisco
jinja2 templates/cisco_interface.j2 vars/interface.yml
# Juniper
jinja2 templates/juniper_interface.j2 vars/interface.yml

B. BGP Config Generation (With Error-Prone Logic)

Problem: Complex BGP configs with neighbor policies.
Solution: Template + YAML with validation.

vars/bgp.yml

bgp:
  asn: 65001
  neighbors:
    - ip: 10.0.0.2
      remote_as: 65002
      policies: [ "PREVENT_LEAK" ]
    - ip: 192.168.1.1
      remote_as: 65123
      policies: [ "CUSTOMER_ROUTES" ]

templates/bgp.j2

router bgp {{ bgp.asn }}
  {% for neighbor in bgp.neighbors %}
  neighbor {{ neighbor.ip }} remote-as {{ neighbor.remote_as }}
  {% if "PREVENT_LEAK" in neighbor.policies %}
  neighbor {{ neighbor.ip }} route-map BLOCK_DEFAULT in
  {% endif %}
  {% endfor %}

Key Trick: Use | selectattr to filter neighbors:

{% for neighbor in bgp.neighbors | selectattr("policies", "contains", "CUSTOMER_ROUTES") %}
...customer-specific config...
{% endfor %}

2. Advanced: Network-as-Code Patterns

A. Generate Device-Specific Configs from NetBox API

Problem: NetBox has device data, but CLI configs are manual.
Solution: Fetch NetBox data → Jinja2.

fetch_netbox_data.py

import requests
import json

devices = requests.get("https://netbox/api/dcim/devices/").json()
with open('vars/netbox_devices.yml', 'w') as f:
    json.dump(devices, f)

templates/netbox_cisco.j2

hostname {{ device.name }}
{% for iface in device.interfaces %}
interface {{ iface.name }}
 description {{ iface.description }}
{% endfor %}

Render All Devices:

python fetch_netbox_data.py
jinja2 templates/netbox_cisco.j2 vars/netbox_devices.yml

B. Auto-Generate Port-Channel Configs (LACP)

Problem: Port-channel members change frequently.
Solution: Dynamic YAML + template.

vars/portchannel.yml

portchannel:
  id: 10
  members: [ "Eth1/1", "Eth1/2" ]
  mode: "active"
  vlan: 100

templates/portchannel.j2

interface Port-channel{{ portchannel.id }}
  channel-group {{ portchannel.id }} mode {{ portchannel.mode }}
  switchport access vlan {{ portchannel.vlan }}

{% for member in portchannel.members %}
interface {{ member }}
  channel-group {{ portchannel.id }} mode {{ portchannel.mode }}
{% endfor %}

3. Pro Tooling: Network-Specific Tricks

A. Ansible + Jinja2 for Zero-Touch Deployment

ansible_playbook.yml

- name: Push Interface Configs
  hosts: switches
  tasks:
    - name: Generate Config
      template:
        src: templates/cisco_interface.j2
        dest: /tmp/{{ inventory_hostname }}.cfg
    - name: Deploy Config
      cisco.ios.ios_config:
        src: /tmp/{{ inventory_hostname }}.cfg

B. Validate Configs Before Deployment

# Check for missing variables
jinja2 --format vars:yaml templates/interface.j2 vars/device.yml

# Dry-run with Ansible
ansible-playbook playbook.yml --check

C. Secret Management (Ansible Vault)

# Encrypted vars.yml
ansible-vault encrypt vars/secrets.yml

Template:

username {{ vaulted_username }}
password {{ vaulted_password }}

4. Your Battle Plan

  1. Start Tonight:

    • Pick 1 repetitive config (VLANs/ACLs) → template it.
    • Render with jinja2-cli locally.
  2. Next Week:

    • Integrate with NetBox/Python to auto-generate configs.
    • Use Ansible to push to devices.
  3. Go Pro:

    • Build a GitLab pipeline that auto-generates configs on NetBox changes.

Example Repo Structure:

network-automation/
├── templates/       # .j2 files
├── vars/            # .yml device vars
├── scripts/         # Python/Ansible helpers
└── outputs/         # Rendered configs

Key Takeaway: Jinja2 turns network configs from manual CLI work into version-controlled code. The more you template now, the more time you save later.