Jinja2 to silnik szablonów dla Pythona, który umożliwia dynamiczne generowanie treści. W prostych słowach, Jinja2 pozwala na na utworzenie szablonu w którym umieszczamy specjalne miejsca {{ }}
na dane pochodzące z zewnętrznych źródeł. Mogą to być statyczne pliki, ale również bazy danych czy aplikacje jak np. Netbox. To samo moglibyśmy osiągnąc
Dlaczego Jinja2?
Jinja2 charakteryzuje się prostą składnią i elastycznością, co ułatwia generowanie konfiguracji. W porównaniu do alternatyw, takich jak Mako czy Django Templates, Jinja2 oferuje lepszą wydajność oraz wsparcie dla bardziej złożonych struktur danych. Dodatkowo, jego integracja z narzędziami takimi jak Ansible oraz bogata dokumentacja czynią go doskonałym wyborem.
- Oszczędność czasu: Automatyzacja powtarzalnych zadań może drastycznie skrócić czas potrzebny na konfigurację urządzeń. To, co zajmowało godziny, może być teraz wykonane w ciągu minut.
- Redukcja błędów: Szablony eliminują ryzyko błędów typograficznych i niespójności konfiguracji. Gdy szablon jest poprawny, każda wygenerowana konfiguracja będzie bezbłędna.
- Skalowalność: W miarę rozrostu sieci, zarządzanie tysiącami urządzeń staje się proste. Jeden szablon może obsłużyć setki lub tysiące urządzeń.
- Standaryzacja: Jinja2 zapewnia spójność konfiguracji w całej infrastrukturze sieciowej. Wszystkie urządzenia są konfigurowane według tych samych standardów.
- Elastyczność: Szablony można łatwo dostosować do różnych scenariuszy i typów urządzeń, co jest nieocenione w heterogenicznych środowiskach sieciowych.
- Integracja: Jinja2 współpracuje z popularnymi narzędziami automatyzacji jak Ansible czy Nornir, co pozwala na tworzenie kompleksowych rozwiązań automatyzacyjnych.
Podstawy składni Jinja2
Szablony Jinja2 wykorzystują proste, ale pozwalające na sporą elastyczność elementy składni:
- Zmienne: Używane do wstawiania dynamicznych wartości w szablon. Zapisywane są w podwójnych nawiasach klamrowych. Przykład:
{{ zmienna }}
- Instrukcje warunkowe: Pozwalają na warunkowe generowanie konfiguracji. Używają składni podobnej do Pythona. Przykład:
{% if warunek %} ... {% elif inny_warunek %} ... {% else %} ... {% endif %}
- Pętle: Umożliwiają iterację przez listy lub słowniki, generując powtarzalne elementy konfiguracji. Przykład:
{% for element in lista %} ... {% endfor %}
- Filtry: Modyfikują wartości zmiennych przed ich wstawieniem do szablonu. Są potężnym narzędziem do formatowania danych. Przykład:
{{ zmienna | upper }}
przekształcizmienna
na wielkie litery. - Komentarze: Pozwalają na dodawanie notatek w szablonie, które nie są renderowane w końcowej konfiguracji. Przykład:
{# To jest komentarz #}
Przykładowy szablon Jinja2
Plik: template.j2
Szablon Jinja2, który generuje konfigurację na podstawie danych z pliku YAML.
{% for interface in interfaces -%}
interface {{ interface.name }}
{% if interface.type == 'access' -%}
switchport mode access
switchport access vlan {{ interface.vlan }}
{% elif interface.type == 'trunk' -%}
switchport mode trunk
switchport trunk allowed vlan {{ interface.vlan }}
{% endif -%}
description {{ interface.description| upper }}
!
{% endfor -%}
Jak widzisz nasz szablon jest stosunkowo krótki, ale pozwala na wygenerowanie spójnej i powtarzalnej konfiguracji dla jednego jak i stu interfejsów.
Pętla {% for interface in interfaces -%}
pozwala na wygenerowanie konfiguracji dla każdego interfejsu, który jest zdefiniowany w naszym sekcji interfaces
naszego pliku yaml
.
Następnie z pliku yaml
w miejsce zmiennych np. {{interface.name}}
podstawiamy odpowiednią wartość, w tym przypadku nazwę interfejsu.
W kolejnym kroku sprawdzamy czy nasz interfejs pracuje w trybie access {% if interface.type == 'access' -%}
czy jako trunk {% elif interface.type == 'trunk' -%}
. W Jinja2, elif
jest używane w konstrukcjach warunkowych, aby umożliwić sprawdzenie dodatkowych warunków, jeśli poprzedni warunek (if
) nie został spełniony.
{% endif -%}
oraz {% endfor -%}
zamykają konkretne konstrukcje warunkowe oraz pętle.
Ninja2 posiada również wbudowane mechanizmy do obsługi białych znaków. Jeśli dodasz znak minus (-)
na początku lub na końcu bloku {% endfor -%}
, komentarza lub zmiennej, białe znaki przed lub po tym bloku zostaną usunięte.
Plik: interfaces.yaml
Zawiera dane, które chcemy umieścić w naszym szablonie.
interfaces:
- name: GigabitEthernet0/1
type: access
vlan: 10
description: "Port do komputera biurowego"
- name: GigabitEthernet0/2
type: trunk
vlans: "10,20,30"
description: "Port trunkowy do innego przełącznika"
Plik: interfaces.py
Skrypt Pythona, który łączy te dwa elementy, aby wygenerować finalną konfigurację.
import yaml
from jinja2 import Environment, FileSystemLoader
# Wczytaj plik YAML
with open('interfaces.yaml') as file:
config = yaml.safe_load(file)
# Przygotuj środowisko Jinja2
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('template.j2')
# Generuj konfigurację
output = template.render(interfaces=config['interfaces'])
# Zapisz wynik do pliku
with open('configuration.txt', 'w') as output_file:
output_file.write(output)
print("Konfiguracja została wygenerowana i zapisana w pliku configuration.txt.")
Plik: configuration.txt
Zawiera wygenerowaną konfigurację.
interface GigabitEthernet0/1
switchport mode access
switchport access vlan 10
description PORT DO KOMPUTERA BIUROWEGO
!
interface GigabitEthernet0/2
switchport mode trunk
switchport trunk allowed vlan 10,20,30
description PORT TRUNKOWY DO INNEGO PRZEŁĄCZNIKA
!
Wszystkie pliki dostępne są na githubie:
Najlepsze praktyki przy używaniu Jinja2
Zachowaj prostotę
Unikaj skomplikowanych logik w szablonach. Proste szablony są łatwiejsze do zrozumienia i utrzymania.
Używaj komentarzy
Dokumentuj swoje szablony dla lepszej czytelności. Komentarze pomagają zrozumieć, co robi dany fragment kodu.
Stosuj modularność
Dziel duże szablony na mniejsze, wielokrotnego użytku. Modularne podejście ułatwia zarządzanie kodem oraz jego ponowne wykorzystanie.
Testuj szablony
Sprawdzaj wygenerowane konfiguracje przed wdrożeniem. Testowanie pozwala na wykrycie błędów i zapewnienie poprawności działania aplikacji.