Update tech_docs/Jinja2_guide.md
This commit is contained in:
@@ -1,3 +1,172 @@
|
||||
Below is a “cheat-sheet” style upgrade to the playbook you already have.
|
||||
It is organized by *pain points* I see teams hit once they move past the first 5–10 devices.
|
||||
Everything is copy-paste ready (CLI one-liners, tiny Python/Go helpers, Ansible snippets).
|
||||
If you only have 15 min right now, jump to the “Tonight” section at the end.
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
1. YAML → Jinja2 → Device in <10 s
|
||||
────────────────────────────────────────────────────────
|
||||
Problem: you still open an editor to change a variable file.
|
||||
Fix: keep YAML inside Git and treat it as the *single source of truth*.
|
||||
|
||||
A. Git-based variable loader (avoids “where did I save that file?”)
|
||||
```bash
|
||||
# vars live in ./inventory/host_vars/$hostname.yml
|
||||
git ls-files -z '*.yml' | xargs -0 yamllint # fast lint
|
||||
```
|
||||
B. One-shot render + diff
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
host=$1
|
||||
jinja2 templates/ios_base.j2 \
|
||||
inventory/host_vars/${host}.yml \
|
||||
--format yaml \
|
||||
-o /tmp/${host}.cfg
|
||||
colordiff -u <(ssh $host show run) /tmp/${host}.cfg | less -R
|
||||
```
|
||||
C. Atomic push with Ansible “deploy_if_changed”
|
||||
```yaml
|
||||
- name: Deploy only if rendered differs
|
||||
ios_config:
|
||||
src: "/tmp/{{ inventory_hostname }}.cfg"
|
||||
vars:
|
||||
ansible_check_mode: false
|
||||
when: >
|
||||
lookup('pipe', 'diff -q /tmp/{{ inventory_hostname }}.cfg
|
||||
<(ssh {{ inventory_hostname }} show run)')
|
||||
is not search('identical')
|
||||
```
|
||||
────────────────────────────────────────────────────────
|
||||
2. Multi-vendor, one variable set
|
||||
────────────────────────────────────────────────────────
|
||||
Trick: store *capability* flags instead of vendor syntax.
|
||||
|
||||
vars/host_vars/leaf01.yml
|
||||
```yaml
|
||||
interfaces:
|
||||
- name: Ethernet1/1
|
||||
port_type: access
|
||||
vlan: 200
|
||||
poe: true
|
||||
mtu: 9214
|
||||
```
|
||||
|
||||
templates/shared/interface_capabilities.j2
|
||||
```jinja2
|
||||
{# Works on IOS-XE, Junos, EOS #}
|
||||
{% set vendor = hostvars[inventory_hostname].vendor %}
|
||||
{% for i in interfaces %}
|
||||
interface {{ i.name | re_replace('^Eth','') if vendor=='arista' else
|
||||
i.name | re_replace('^Eth','ge-') if vendor=='juniper' else
|
||||
i.name }}
|
||||
{% if i.port_type == 'access' %}
|
||||
switchport mode access
|
||||
switchport access vlan {{ i.vlan }}
|
||||
{% endif %}
|
||||
{% if i.poe and (vendor == 'cisco' or vendor == 'arista') %}
|
||||
power inline never
|
||||
{% endif %}
|
||||
mtu {{ i.mtu }}
|
||||
!
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
3. NetBox as a live inventory
|
||||
────────────────────────────────────────────────────────
|
||||
Inventory plugin (ships with Ansible ≥2.10) in `ansible.cfg`:
|
||||
```
|
||||
[inventory]
|
||||
enable_plugins = netbox.netbox.nb_inventory
|
||||
```
|
||||
Group by site + role:
|
||||
```yaml
|
||||
# group_vars/netbox_site_edge.yml
|
||||
ntp_servers: ["10.0.0.10", "10.0.0.11"]
|
||||
```
|
||||
Now zero extra YAML for new sites.
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
4. Rego in CI to block bad configs
|
||||
────────────────────────────────────────────────────────
|
||||
`.gitlab-ci.yml` (runs in <5 s):
|
||||
```yaml
|
||||
validate:
|
||||
image: openpolicyagent/opa:0.59
|
||||
script:
|
||||
- opa test policies/
|
||||
- >
|
||||
find . -name "*.cfg" -print0 |
|
||||
xargs -0 -I{} opa eval --fail-defined --format pretty
|
||||
--data policies/network.rego --input {} "data.network.deny[_]"
|
||||
```
|
||||
|
||||
Example policy (`policies/network.rego`):
|
||||
```rego
|
||||
package network
|
||||
deny[msg] {
|
||||
input.kind == "interface"
|
||||
input.mtu > 9216
|
||||
msg := sprintf("MTU %d too high on %s", [input.mtu, input.name])
|
||||
}
|
||||
```
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
5. Secrets without Ansible Vault pain
|
||||
────────────────────────────────────────────────────────
|
||||
Use `sops` + age (age is easier than GPG).
|
||||
|
||||
```bash
|
||||
# encrypt vars/secrets.yml
|
||||
sops --age $(cat ~/.age/key.txt | grep public | cut -d' ' -f4) \
|
||||
--encrypt vars/secrets.yml > vars/secrets.enc.yml
|
||||
```
|
||||
Ansible automatically decrypts when `ansible_sops_enabled=true`.
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
6. Tonight → Next week → Go Pro (15 min each)
|
||||
────────────────────────────────────────────────────────
|
||||
Tonight (literally 15 min):
|
||||
```bash
|
||||
pipx install jinja2-cli yq
|
||||
git init network-auto && cd network-auto
|
||||
mkdir templates vars
|
||||
cat > vars/lab.yml <<EOF
|
||||
vlans:
|
||||
- id: 100
|
||||
name: STAFF
|
||||
EOF
|
||||
cat > templates/ios_vlan.j2 <<'EOF'
|
||||
{% for v in vlans %}
|
||||
vlan {{ v.id }}
|
||||
name {{ v.name }}
|
||||
{% endfor %}
|
||||
EOF
|
||||
jinja2 templates/ios_vlan.j2 vars/lab.yml
|
||||
```
|
||||
You now have version-controlled VLANs.
|
||||
|
||||
Next week:
|
||||
- Replace static `vars/lab.yml` with NetBox inventory plugin.
|
||||
- Add a single playbook that runs against “site:lab” and pushes the VLANs.
|
||||
|
||||
Go Pro:
|
||||
- Add Rego policy to reject VLAN 1 or duplicate names.
|
||||
- GitLab CI job auto-pushes when MR merges (use Ansible’s `ios_config` with `backup: yes` so you can rollback).
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
Quick reference card
|
||||
────────────────────────────────────────────────────────
|
||||
Render: jinja2 tpl.j2 vars.yml --format yaml
|
||||
Lint: yamllint vars/
|
||||
Test: opa test policies/
|
||||
Encrypt: sops -e vars/secrets.yml
|
||||
Diff: colordiff -u live.cfg new.cfg
|
||||
|
||||
Copy the snippets above into your repo and you’re already ahead of most teams.
|
||||
|
||||
---
|
||||
|
||||
This is **exactly** the kind of tactical, network-focused Jinja2 guide I wish I had when I started automating configs. Let’s sharpen it further with battle-tested patterns and CLI-ready examples. Here’s your playbook:
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user