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:
|
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