Automatyzacja sieci z Ansible i Pythonem: pierwsze kroki dla administratorów bez doświadczenia programistycznego

0
17
Rate this post

Nawigacja:

Dlaczego automatyzować sieć, nawet bez doświadczenia programistycznego

Codzienna rutyna administratora sieci a automatyzacja

Administrator sieci spędza zbyt dużo czasu na powtarzalnych zadaniach: kopiowaniu tych samych poleceń na kolejne urządzenia, ręcznym wklejaniu konfiguracji z notatnika, sprawdzaniu statusu interfejsów czy aktualizacji haseł. Każde takie zadanie osobno nie wygląda groźnie, ale w skali tygodnia potrafi zjeść wiele godzin.

Ręczne podejście oznacza też wyższe ryzyko błędu. Literówka w ACL, pominięty interfejs w VLAN-ie czy zły numer AS w BGP mogą wywołać problemy trudne do wychwycenia, bo konfiguracja powstawała „z palca” i nie ma jednego, powtarzalnego źródła prawdy.

Automatyzacja Ansible i prostym Pythonem przenosi ciężar pracy z ręcznego wykonywania komend na opisywanie, co ma być zrobione. Zamiast logować się na każde urządzenie i wklejać komendy, przygotowujesz plik z konfiguracją lub skrypt, który robi to za Ciebie, w ten sam sposób, za każdym razem.

Co daje automatyzacja sieci przy pomocy Ansible i Pythona

Szybkość wprowadzania zmian. Zmiana, która kiedyś zajmowała pół dnia, sprowadza się do uruchomienia jednego playbooka lub skryptu. W większych sieciach oszczędność czasu rośnie niemal liniowo z liczbą urządzeń.

Spójność konfiguracji. Te same szablony i zadania stosowane na wszystkich przełącznikach lub routerach eliminują różnice, które powstają przy ręcznym konfigurowaniu. To z kolei zmniejsza liczbę „dziwnych” problemów wynikających z drobnych rozbieżności.

Lepszy audyt i kontrola. Gdy konfiguracja jest zapisana jako pliki (infrastruktura jako kod), łatwiej prześledzić, kto i kiedy wprowadził daną zmianę. Systemy kontroli wersji (np. Git) pozwalają wrócić do poprzedniego stanu i obejrzeć historię.

Mniejszy stres przy zmianach. Zautomatyzowany proces jest powtarzalny. Jeśli playbook zadziałał na labie, jest duża szansa, że identycznie zadziała w produkcji. Ansible ma tryb „check mode”, a prosty Python może najpierw tylko zbierać informacje, zanim zacznie cokolwiek zmieniać.

Mit „muszę być programistą”, a praktyka pracy z Ansible i Pythonem

Wielu administratorów sieci zakłada, że automatyzacja wymaga pełnych umiejętności programistycznych. W rzeczywistości Ansible opiera się głównie na plikach YAML, czyli uporządkowanych listach i słownikach. Przypomina rozbudowaną, ale logiczną konfigurację – nie pełnoprawny kod.

Python w automatyzacji sieci też nie wymaga zaawansowanej wiedzy. W praktyce potrzebne są proste konstrukcje: pętle, warunki, odczyt plików i wywołanie kilku funkcji z bibliotek takich jak Netmiko czy NAPALM. To jest spokojnie w zasięgu osoby, która potrafi zrozumieć konfigurację routera z kilkuset liniami.

Kluczem nie jest „nauczyć się programować wszystko”, ale zbudować kilka małych narzędzi rozwiązujących konkretne problemy: zapisanie konfiguracji z wielu urządzeń, porównanie wersji OS, wdrożenie jednolitego banera czy NTP na wszystkich przełącznikach.

Przykład z życia: zmiana hasła na 20 przełącznikach

Przy podejściu ręcznym schemat wygląda tak: logujesz się na każdy przełącznik po SSH, przechodzisz do trybu konfiguracji, zmieniasz hasło, zapisujesz konfigurację, wylogowujesz się. Przy 20 urządzeniach, z przerwami i drobnymi pomyłkami, to łatwo ponad godzina pracy.

Z Ansible przygotowujesz jeden playbook, w którym w jednym miejscu definiujesz nowe hasło (najlepiej zaszyfrowane Ansible Vaultem). Wskazujesz grupę hostów „access-switches” i uruchamiasz zadanie. Całość trwa kilka minut, z pełnym logiem wykonania i informacją, czy gdzieś wystąpił błąd.

Takie proste doświadczenie mocno zmienia sposób myślenia o swojej pracy. Zamiast „20 razy to samo” robisz „raz dobrze przygotować, a potem wielokrotnie uruchomić”.

Minimalne podstawy, których potrzebuje administrator sieci

Fundamenty: terminal, SSH, praca z plikami

Automatyzacja sieci opiera się na pracy w terminalu. W praktyce oznacza to swobodne korzystanie z:

  • logowania po SSH do urządzeń i serwerów (np. ssh user@host),
  • podstawowych komend systemu Linux (ls, cd, cat, less, mkdir, rm, cp, mv),
  • prostego edytora tekstu (nano, vim, VS Code z WSL),
  • kopiowania plików (scp, sftp lub git clone / git pull).

Bez tego trudno płynnie poruszać się w świecie Ansible i Pythona. Na szczęście wszystko to można opanować w krótkich, praktycznych ćwiczeniach: tworzenie katalogu projektu, edycja pliku YAML, uruchomienie komendy, sprawdzenie wyników w logu.

Podstawy YAML, repozytoriów i wersjonowania konfiguracji

Ansible używa języka YAML do opisywania zadań i zmiennych. YAML to przejrzysta składnia oparta na wcięciach. Najważniejsze zasady:

  • spójne wcięcia (najczęściej 2 spacje),
  • listy oznaczone myślnikiem (-),
  • klucze i wartości w formacie klucz: wartość.

Przykład prostego fragmentu YAML:

routers:
  - name: r1
    ip: 10.0.0.1
  - name: r2
    ip: 10.0.0.2

Drugim filarem jest repozytorium, czyli miejsce, w którym trzymasz pliki projektu (playbooki, skrypty, szablony). Najczęściej będzie to repozytorium Git na serwerze (GitLab, GitHub) lub w katalogu współdzielonym. Dzięki wersjonowaniu łatwiej porównać zmianę sprzed tygodnia i w razie potrzeby przywrócić poprzednią wersję.

Ważnym nawykiem jest traktowanie konfiguracji sieci jako kodu – zamiast modyfikować „na żywym organizmie”, najpierw edytujesz plik, zapisujesz go w repozytorium, uruchamiasz playbook lub skrypt z tego pliku, a dopiero potem konfiguracja trafia na urządzenia.

Jednorazowy skrypt kontra powtarzalny proces

Jednorazowy skrypt robi „jeden strzał”: napisany szybko, często bez dokumentacji i walidacji, ratuje sytuację, ale za pół roku nikt nie pamięta, jak działał. Taki kod zazwyczaj trudno uruchomić ponownie, na przykład z uwagi na ścieżki absolutne czy zaszyte w środku wartości.

Powtarzalny proces to mały, ale uporządkowany projekt. Ma strukturę katalogów, plik z instrukcją (choćby kilka zdań w README), parametry przeniesione do plików YAML lub JSON i jasno wydzieloną część zmienną (dane) od stałej (logika). Można go uruchomić ponownie po miesiącu bez zgadywania.

Warto od początku celować w tę drugą formę. Nawet najmniejszy playbook Ansible czy krótki skrypt Python powinny mieć:

  • osobny plik z danymi (lista urządzeń, hasła zaszyfrowane, parametry),
  • krótki opis w komentarzu, co robią,
  • wariant „tylko odczyt” przed wprowadzaniem zmian.

Jak się uczyć: krótkie sesje zamiast rzadkich „projektów”

Nauka automatyzacji jest skuteczniejsza w małych porcjach. Zamiast jednej, wielkiej akcji raz na kilka miesięcy, lepiej codziennie lub co drugi dzień poświęcić 20–30 minut na:

  • uruchomienie jednego playbooka na labie,
  • modyfikację prostego skryptu Python,
  • dodanie nowej zmiennej w YAML i użycie jej w zadaniu.

Po kilku tygodniach takie drobne kroki złożą się na zestaw umiejętności wystarczający do realnych zadań w sieci produkcyjnej.

Kolorowy kod JavaScript na monitorze ilustrujący programowanie
Źródło: Pexels | Autor: Rashed Paykary

Przygotowanie bezpiecznego środowiska do nauki (lab)

Dlaczego nie testować automatyzacji na produkcji

Automatyzacja zwiększa zasięg zmian. Jednym poleceniem możesz dotknąć kilkunastu czy kilkudziesięciu urządzeń. To zaleta, ale i zagrożenie. Błąd w szablonie czy playbooku może powielić złą konfigurację wszędzie naraz.

Dlatego pierwsze kroki z Ansible i Pythonem należy wykonywać w odseparowanym laboratorium. Nawet jeśli czujesz się pewnie z konfiguracją pojedynczego routera, automatyczne wprowadzanie zmian to inna skala odpowiedzialności.

Bezpieczny lab pozwala na popełnianie błędów bez konsekwencji dla użytkowników. Można tam eksperymentować z kolejnymi wersjami skryptów i uczyć się czytać wyniki, zanim cokolwiek trafi do środowiska produkcyjnego.

Opcje labu: fizyczne urządzenia, wirtualki i emulatory

Najprostsze warianty środowiska testowego to:

  • Stare lub zapasowe urządzenia fizyczne – kilka przełączników i routerów odłączonych od produkcji, połączonych w mini-sieć.
  • GNS3 lub EVE-NG – popularne platformy do wirtualizacji routerów i przełączników różnych producentów, z możliwością integracji z Ansible i Pythonem.
  • Emulatory producentów – wirtualne obrazy (np. vIOS, CSR1000v, vSRX, vMX, NX-OSv), często dostępne do celów labowych.
  • Wirtualne routery Linux z FRRouting/Quagga – jeśli celem jest nauka routingu i ogólnej automatyzacji.

Ważne, by lab umożliwiał połączenie po SSH z maszyny, na której będzie zainstalowany Ansible i Python. Wtedy cała mechanika automatyzacji będzie w praktyce identyczna jak w produkcji.

Przykładowy scenariusz labowy dla automatyzacji sieci

Na początek wystarczy prosty topologia:

  • 2–3 routery połączone w małą sieć z OSPF lub statycznym routingiem,
  • 2–4 przełączniki z kilkoma VLAN-ami,
  • jeden „serwer” z Linuxem (fizyczny lub VM) jako stacja z Ansible i Pythonem.

Taki lab pozwala przetestować typowe zadania:

  • ustawienie banera logowania i serwerów NTP na wszystkich urządzeniach,
  • zmianę adresów IP na interfejsach,
  • konfigurację VLAN-ów i interfejsów trunk/access,
  • aktualizację hasła administracyjnego na grupie przełączników.

W kolejnych etapach można dorzucić ACL, BGP, QoS czy zdalny syslog, ale nie ma sensu zaczynać od zbyt skomplikowanych scenariuszy.

Oddzielenie labu od produkcji i przełączanie kontekstów

W projektach Ansible kluczową rolę odgrywa inventory, czyli lista hostów. Najbezpieczniej jest od razu założyć osobne inwentarze dla labu i produkcji, na przykład:

  • inventories/lab/hosts.yml
  • inventories/prod/hosts.yml

Przy uruchamianiu playbooka zawsze jawnie wskazujesz, z którego inwentarza chcesz korzystać, np. -i inventories/lab/hosts.yml. Dodatkowo można użyć osobnych plików ansible.cfg dla labu i produkcji lub parametrów środowiskowych, aby ograniczyć ryzyko pomyłki.

Dobrym nawykiem jest też wyraźne oznaczanie nazw hostów. Przykładowo, w labie: r1-lab, sw1-lab, a w produkcji: r1-prod, sw1-core. W połączeniu z osobnymi inwentarzami daje to dodatkowy „bezpiecznik” w głowie.

Pierwsze kroki z Ansible dla administratora sieci

Ansible w sieciach: agentless i moduły sieciowe

Ansible to narzędzie do automatyzacji, które nie wymaga instalowania agentów na urządzeniach. Łączy się z nimi przez SSH lub API, wysyła komendy i analizuje odpowiedzi. Dla administratora sieci to duża zaleta – większość routerów i przełączników już ma SSH/NETCONF/REST.

Świat sieciowy w Ansible opiera się na modułach specyficznych dla producentów (np. cisco.ios.ios_config, arista.eos.eos_config, junipernetworks.junos.junos_config) albo na modułach ogólnych (ansible.netcommon). Moduły znają sposób wejścia do trybu konfiguracji, zapisania zmian czy pobrania danych.

Zamiast pisać skrytpy do logowania się i wysyłania komend, opisujesz w playbooku, co chcesz mieć w konfiguracji lub jakie komendy „show” uruchomić. Resztą zajmuje się silnik Ansible i moduły dostosowane do danego systemu operacyjnego.

Instalacja Ansible na Linux/WSL

Najbezpieczniejszą drogą jest użycie systemu Linux (np. Ubuntu) jako stacji roboczej lub WSL (Windows Subsystem for Linux) pod Windows. Ogólny schemat instalacji na Ubuntu:

  1. Aktualizacja pakietów: sudo apt update
  2. Instalacja Pythona i pip (jeśli brak): sudo apt install python3 python3-pip -y
  3. Instalacja Ansible, najlepiej z pip: pip3 install ansible

Po instalacji sprawdzasz wersję:

Sprawdzenie działania Ansible i pierwsze połączenie SSH

Po instalacji szybko weryfikujesz, czy Ansible działa:

ansible --version

Jeśli komenda zwróci wersję i ścieżki do modułów, można iść dalej. Kolejny krok to prosty test połączenia SSH do jednego z urządzeń w labie.

Tworzysz plik inwentarza, np. inventories/lab/hosts.yml:

all:
  hosts:
    r1-lab:
      ansible_host: 10.0.0.11
      ansible_user: admin
      ansible_network_os: cisco.ios.ios

Następnie uruchamiasz moduł ping dla hostów sieciowych (moduł nie wysyła ICMP, tylko robi prostą operację przez SSH):

ansible all -i inventories/lab/hosts.yml -m ansible.netcommon.network_cli -c network_cli

Jeśli konfiguracja SSH i hasła jest poprawna, powinieneś zobaczyć status „ok” dla urządzenia. W razie błędów najpierw popraw połączenie SSH „ręcznie”, dopiero potem wracaj do Ansible.

Podstawowa struktura katalogów dla małego projektu sieciowego

Nawet mały projekt dobrze jest poukładać. Minimalny szkielet:

network-automation/
  inventories/
    lab/
      hosts.yml
  playbooks/
    get-facts.yml
  group_vars/
  host_vars/
  ansible.cfg

Plik ansible.cfg w katalogu projektu pozwala ustawić domyślny inwentarz i zachowanie połączeń. Prosty przykład:

[defaults]
inventory = inventories/lab/hosts.yml
host_key_checking = False
retry_files_enabled = False

[connection]
pipelining = True

Dzięki temu nie musisz za każdym razem podawać -i inventories/lab/hosts.yml, a konfiguracja projektu nie wpływa na inne katalogi.

Pierwszy playbook: odczyt faktów z urządzenia

Dobrym startem jest playbook, który niczego nie zmienia, tylko zbiera informacje z routera lub przełącznika. Np. z użyciem modułu ios_facts z kolekcji cisco.ios.

Instalacja kolekcji:

ansible-galaxy collection install cisco.ios

Prosty playbook w playbooks/get-facts.yml:

---
- name: Pobierz fakty z routerów labowych
  hosts: r1-lab
  gather_facts: no
  connection: network_cli

  tasks:
    - name: Zbierz fakty z urządzenia
      cisco.ios.ios_facts:
      register: device_facts

    - name: Wyświetl wersję IOS
      debug:
        msg: "Wersja systemu: {{ device_facts.ansible_facts.ansible_net_version }}"

Uruchomienie:

ansible-playbook playbooks/get-facts.yml

To pierwszy moment, kiedy widzisz konkretny efekt: wersję systemu, hostname, informacje o interfejsach wyciągnięte automatycznie z urządzenia.

Konfiguracja poświadczeń i Ansible Vault

Hasła i klucze wygodniej trzymać w zmiennych niż wklejać do inwentarza. Najprostsza forma to plik w group_vars:

group_vars/
  all.yml
ansible_user: admin
ansible_password: "bardzo_tajne_haslo"
ansible_become: yes
ansible_become_method: enable
ansible_become_password: "haslo_enable"

Żeby nie przechowywać haseł w postaci jawnej, użyj ansible-vault:

ansible-vault encrypt group_vars/all.yml

Przy uruchamianiu playbooka Ansible poprosi o hasło do Vaulta lub użyjesz pliku z hasłem. Dzięki temu pliki repozytorium mogą trafić do Gita bez obaw o wyciek haseł.

Tryb „check” i „diff” jako zabezpieczenie przed błędami

Przed pierwszym wprowadzeniem zmian dobrze jest zobaczyć, co Ansible zamierza zrobić. W tym celu służy tryb sprawdzający:

ansible-playbook playbooks/change-banner.yml --check --diff

Parametr --check powoduje „suchy bieg” – Ansible symuluje zmiany, ale ich nie wprowadza. --diff pokazuje różnice w konfiguracji (jeśli moduł je obsługuje). To mechanizm, który często ratuje przed nadpisaniem ręcznie zmodyfikowanej konfiguracji.

Zbliżenie kolorowego kodu JavaScript na monitorze komputerowym
Źródło: Pexels | Autor: Rashed Paykary

Podstawy playbooków Ansible pod sieci: od prostego zadania do roli

Struktura playbooka dla urządzeń sieciowych

Playbook dla sieci jest podobny do tego dla serwerów, ale zwykle ustawia się inne parametry połączeń. Szkielet z jednym zadaniem:

---
- name: Konfiguracja banera logowania
  hosts: all
  gather_facts: no
  connection: network_cli

  tasks:
    - name: Ustaw baner MOTD
      cisco.ios.ios_banner:
        banner: motd
        text: |
          Dostep tylko dla upowaznionego personelu.
          Wszystkie akcje sa logowane.
        state: present

Tu od razu widać kilka elementów: nazwę playbooka, grupę hostów, sposób połączenia oraz listę zadań.

Grupy hostów i zmienne grupowe

Gdy lab się rozrośnie, pojawi się potrzeba rozdzielenia routerów i przełączników. W inwentarzu możesz zdefiniować grupy:

all:
  children:
    routers:
      hosts:
        r1-lab:
          ansible_host: 10.0.0.11
        r2-lab:
          ansible_host: 10.0.0.12
    switches:
      hosts:
        sw1-lab:
          ansible_host: 10.0.0.21
        sw2-lab:
          ansible_host: 10.0.0.22

W katalogu group_vars możesz wtedy mieć różne pliki:

  • group_vars/routers.yml – zmienne wspólne dla routerów,
  • group_vars/switches.yml – osobne parametry dla przełączników.

Playbook może wskazywać tylko wybraną grupę, np. hosts: routers, dzięki czemu zadania nie dotkną przełączników.

Szablony Jinja2 do generowania konfiguracji

Dla bardziej złożonych fragmentów konfiguracji (np. VLAN-y, BGP) wygodnie użyć szablonów Jinja2. Szablon to plik tekstowy z wstawkami w klamrach. Przykład prostego szablonu VLAN-ów templates/vlans.j2:

{% for vlan in vlans %}
vlan {{ vlan.id }}
 name {{ vlan.name }}
{% endfor %}

Zmienne VLAN-ów definiujesz np. w host_vars/sw1-lab.yml:

vlans:
  - id: 10
    name: USERS
  - id: 20
    name: SERVERS

Playbook używający szablonu z modułem ios_config:

---
- name: Konfiguracja VLAN-ow ze wzorca
  hosts: switches
  gather_facts: no
  connection: network_cli

  tasks:
    - name: Wygeneruj i zastosuj konfiguracje VLAN
      cisco.ios.ios_config:
        src: "templates/vlans.j2"

Ten wzorzec łatwo rozszerzać. Dodajesz VLAN do listy w YAML, odpalasz playbooka i konfiguracja pojawia się na przełączniku.

Od pojedynczego playbooka do roli

Gdy liczba zadań rośnie, pojedynczy plik playbooka staje się mało czytelny. Dużo wygodniejsze są role, czyli uporządkowany zestaw zadań, szablonów i zmiennych do jednego celu, np. „bazowa konfiguracja routera”.

Minimalna rola roles/base-router może wyglądać tak:

roles/
  base-router/
    tasks/
      main.yml
    templates/
      base-config.j2
    defaults/
      main.yml

Przykładowy plik tasks/main.yml:

---
- name: Zastosuj bazowa konfiguracje routera
  cisco.ios.ios_config:
    src: "base-config.j2"

W defaults/main.yml możesz zdefiniować domyślne zmienne, np. NTP, serwer syslog, domenę:

---
ntp_servers:
  - 192.0.2.1
  - 192.0.2.2
domain_name: lab.local
logging_server: 192.0.2.10

Playbook korzystający z roli jest wtedy bardzo prosty:

---
- name: Bazowa konfiguracja routerow
  hosts: routers
  gather_facts: no
  connection: network_cli

  roles:
    - base-router

Dzięki rolom nie kopiujesz tych samych zadań między playbookami. Projekty stają się modularne: jedna rola dla bazowej konfiguracji, druga dla OSPF, trzecia dla BGP i tak dalej.

Parametryzowanie ról dla różnych środowisk

Ta sama rola może pracować inaczej w labie i w produkcji. Różnice można wynieść do zmiennych inwentarza. Na przykład w inventories/lab/group_vars/routers.yml:

ntp_servers:
  - 10.255.0.1
domain_name: lab.local

A w inventories/prod/group_vars/routers.yml:

ntp_servers:
  - 172.16.0.10
  - 172.16.0.11
domain_name: corp.local

Ten sam playbook i ta sama rola, inne dane – konfiguracja dopasowana do środowiska bez zmian w logice.

Wprowadzenie do Pythona w automatyzacji sieci (bez „bycia programistą”)

Zakres Pythona potrzebny administratorowi sieci

Do prostych automatyzacji wystarczy wąski zestaw umiejętności:

  • uruchamianie skryptu: python3 skrypt.py,
  • zmienne, listy, słowniki,
  • instrukcje if, pętla for,
  • podstawowe operacje na plikach (odczyt/zapis tekstu, JSON, YAML),
  • korzystanie z zewnętrznych bibliotek (Netmiko, NAPALM).

Nie trzeba znać wzorców projektowych, klas ani zaawansowanej składni. W praktyce większość skryptów sieciowych to kilkadziesiąt linijek.

Instalacja bibliotek sieciowych dla Pythona

Na tej samej maszynie, gdzie masz Ansible, zainstaluj Netmiko i NAPALM:

pip3 install netmiko napalm

Dobrą praktyką jest użycie wirtualnego środowiska Pythona, np. venv, żeby nie mieszać bibliotek systemowych z projektowymi:

python3 -m venv venv
source venv/bin/activate
pip install netmiko napalm

W każdym nowym terminalu przed pracą aktywujesz środowisko komendą source venv/bin/activate.

Prosty skrypt Python do wykonania komendy „show”

Najczęstsze zadanie na start: zalogować się na router i wykonać komendę show. Netmiko upraszcza to do kilku linijek kodu. Przykład scripts/show_version.py:

from netmiko import ConnectHandler

device = {
    "device_type": "cisco_ios",
    "host": "10.0.0.11",
    "username": "admin",
    "password": "bardzo_tajne_haslo",
}

with ConnectHandler(**device) as conn:
    output = conn.send_command("show version")
    print(output)

Skrypt uruchamiasz:

python3 scripts/show_version.py

To odpowiednik ręcznego logowania się przez SSH, ale wynik od razu ląduje w pliku lub na ekranie. W dalszych krokach możesz dodać prostą analizę tekstu, np. wyszukanie wersji IOS.

Wydzielenie danych urządzeń do pliku YAML

Żeby nie trzymać adresów i haseł w środku skryptu, lepiej przenieść je do pliku YAML, np. devices.yml:

devices:
  - name: r1-lab
    host: 10.0.0.11
    username: admin
    password: bardzo_tajne_haslo
    device_type: cisco_ios
  - name: r2-lab
    host: 10.0.0.12
    username: admin
    password: bardzo_tajne_haslo
    device_type: cisco_ios

Skrypt z użyciem biblioteki PyYAML:

import yaml
from netmiko import ConnectHandler

with open("devices.yml") as f:
    data = yaml.safe_load(f)

for dev in data["devices"]:
    print(f"Polaczenie z {dev['name']} ({dev['host']})")
    with ConnectHandler(
        device_type=dev["device_type"],
        host=dev["host"],
        username=dev["username"],
        password=dev["password"],
    ) as conn:
        output = conn.send_command("show ip interface brief")
        filename = f"outputs/{dev['name']}_interfaces.txt"
        with open(filename, "w") as out:
            out.write(output)

Taki skrypt przechodzi po liście urządzeń, wykonuje komendę i zapisuje wynik do osobnych plików. Struktura jest już bardziej zbliżona do narzędzia, które można uruchamiać cyklicznie.

NAPALM jako warstwa abstrakcji nad różnymi vendorami

NAPALM ujednolica sposób pracy z różnymi systemami (IOS, Junos, EOS). Zamiast pisać osobną logikę dla każdego producenta, korzystasz z jednego interfejsu. Przykład prostego skryptu pobierającego fakty:

Przykład użycia NAPALM do pobierania faktów z urządzenia

from napalm import get_network_driver

driver = get_network_driver("ios")

device = driver(
    hostname="10.0.0.11",
    username="admin",
    password="bardzo_tajne_haslo",
)

device.open()

facts = device.get_facts()
interfaces = device.get_interfaces()

device.close()

print("Podstawowe informacje:")
for key, value in facts.items():
    print(f"{key}: {value}")

print("nInterfejsy:")
for name, data in interfaces.items():
    print(f"{name}: is_up={data['is_up']}, description={data['description']}")

Struktura zwracanych danych jest słownikowa, więc można ją łatwo zapisać do JSON lub przefiltrować. Ten sam skrypt, po zmianie typu sterownika np. na junos, będzie działał z Juniperem (przy spełnieniu wymagań transportu).

Zapisywanie danych z NAPALM do JSON

JSON dobrze współpracuje z innymi narzędziami (np. systemami CMDB, Elastic, narzędziami do raportów). Przykład rozszerzenia poprzedniego skryptu:

import json
from napalm import get_network_driver

driver = get_network_driver("ios")

device = driver(
    hostname="10.0.0.11",
    username="admin",
    password="bardzo_tajne_haslo",
)

device.open()

facts = device.get_facts()
interfaces = device.get_interfaces()

device.close()

result = {
    "facts": facts,
    "interfaces": interfaces,
}

with open("outputs/r1-lab_napalm.json", "w") as f:
    json.dump(result, f, indent=2)

Takie pliki można później porównywać, wersjonować w Gicie lub podawać do kolejnych skryptów kontrolnych.

Wywoływanie wielu urządzeń z NAPALM i prosty raport

Przykład skryptu, który przechodzi po liście urządzeń z YAML i buduje prosty raport o wersji systemu:

import yaml
from napalm import get_network_driver

with open("devices.yml") as f:
    data = yaml.safe_load(f)

for dev in data["devices"]:
    print(f"n=== {dev['name']} ({dev['host']}) ===")
    driver = get_network_driver("ios")
    device = driver(
        hostname=dev["host"],
        username=dev["username"],
        password=dev["password"],
    )
    device.open()
    facts = device.get_facts()
    device.close()

    print(f"Vendor: {facts.get('vendor')}")
    print(f"Model: {facts.get('model')}")
    print(f"OS version: {facts.get('os_version')}")
    print(f"Serial: {facts.get('serial')}")

Taki raport często zastępuje ręczne przeklikiwanie się przez kilkanaście urządzeń przed planowaną aktualizacją.

Bezpieczne obchodzenie się z hasłami w skryptach Python

Najgorszy wariant to trzymanie haseł wprost w kodzie. Lepsze podejścia:

  • odczyt hasła z zmiennej środowiskowej,
  • użycie pliku YAML zaszyfrowanego Ansible Vault,
  • interaktywne pytanie o hasło, jeśli skrypt uruchamiany jest ręcznie.

Przykład z wykorzystaniem zmiennej środowiskowej:

import os
from netmiko import ConnectHandler

password = os.environ.get("NET_PASSWORD")
if not password:
    raise SystemExit("Brak zmiennej NET_PASSWORD")

device = {
    "device_type": "cisco_ios",
    "host": "10.0.0.11",
    "username": "admin",
    "password": password,
}

with ConnectHandler(**device) as conn:
    print(conn.send_command("show hostname"))

W praktyce można połączyć to z managerem haseł lub mechanizmem CI/CD, który wstrzykuje zmienne środowiskowe tylko na czas zadania.

Zbliżenie stanowiska admina z ekranem kodu, laptopem i smartfonem
Źródło: Pexels | Autor: Firos nv

Proste skrypty Python + Netmiko/NAPALM do pracy z urządzeniami

Netmiko do wprowadzania konfiguracji liniowej

Dla krótkich zmian konfiguracyjnych (np. opis interfejsu, ACL na jednym porcie) Netmiko bywa szybsze niż przygotowywanie roli Ansible. Przykład aktualizacji opisu na interfejsie:

from netmiko import ConnectHandler

device = {
    "device_type": "cisco_ios",
    "host": "10.0.0.21",
    "username": "admin",
    "password": "bardzo_tajne_haslo",
}

commands = [
    "interface GigabitEthernet0/1",
    "description Uplink do rdzenia",
]

with ConnectHandler(**device) as conn:
    output = conn.send_config_set(commands)
    print(output)

Netmiko sam przechodzi w tryb konfiguracji, wysyła polecenia i wraca do trybu wykonywania.

Netmiko i obsługa wielu komend oraz zapis kopii konfiguracji

Przykład skryptu, który pobiera bieżącą konfigurację i zapisuje ją lokalnie jako kopię:

import os
from datetime import datetime
from netmiko import ConnectHandler

device = {
    "device_type": "cisco_ios",
    "host": "10.0.0.11",
    "username": "admin",
    "password": "bardzo_tajne_haslo",
}

backup_dir = "backups"
os.makedirs(backup_dir, exist_ok=True)

with ConnectHandler(**device) as conn:
    hostname = conn.send_command("show running-config | include hostname").split()[1]
    running = conn.send_command("show running-config")

timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
filename = os.path.join(backup_dir, f"{hostname}_running_{timestamp}.cfg")

with open(filename, "w") as f:
    f.write(running)

print(f"Zapisano kopie konfiguracji do {filename}")

Taki skrypt można zintegrować z cronem lub narzędziem orkiestrującym zadania i mieć codzienne backupy bez dodatkowego sprzętu.

Prosty „compliance check” w Pythonie

Częsty scenariusz: sprawdzenie, czy na wszystkich urządzeniach skonfigurowano np. serwery NTP i logging. Minimalny wariant opary na wyszukiwaniu linii w running-config:

import yaml
from netmiko import ConnectHandler

REQUIRED_LINES = [
    "ntp server 192.0.2.1",
    "logging host 192.0.2.10",
]

with open("devices.yml") as f:
    data = yaml.safe_load(f)

for dev in data["devices"]:
    print(f"n=== Sprawdzanie {dev['name']} ===")
    with ConnectHandler(
        device_type=dev["device_type"],
        host=dev["host"],
        username=dev["username"],
        password=dev["password"],
    ) as conn:
        running = conn.send_command("show running-config")

    for line in REQUIRED_LINES:
        if line in running:
            print(f"[OK] {line}")
        else:
            print(f"[BRAK] {line}")

To nie jest idealny compliance w stylu narzędzi klasy enterprise, ale w małych środowiskach rozwiązuje 80% problemów.

NAPALM i wprowadzanie zmian z mechanizmem „diff”

NAPALM potrafi wgrać konfigurację w trybie „merge” lub „replace” i pokazać różnicę przed zatwierdzeniem. To bezpieczniejsze niż ślepe wysyłanie linii konfiguracyjnych.

from napalm import get_network_driver

driver = get_network_driver("ios")

device = driver(
    hostname="10.0.0.11",
    username="admin",
    password="bardzo_tajne_haslo",
)

device.open()

device.load_merge_candidate(filename="configs/r1-lab_snmp.cfg")

diff = device.compare_config()
if diff:
    print("Proponowane zmiany:")
    print(diff)
    confirm = input("Zatwierdzic zmiany? [y/N]: ").lower()
    if confirm == "y":
        device.commit_config()
        print("Zmiany zatwierdzone.")
    else:
        device.discard_config()
        print("Zmiany odrzucone.")
else:
    print("Brak roznic, nic do zrobienia.")

device.close()

Taki schemat przypomina pracę na Junosie: najpierw przegląd różnic, potem świadome zatwierdzenie.

Łączenie Netmiko i NAPALM w jednym skrypcie

Typowy wzorzec: Netmiko do prostych komend typu „show”, NAPALM do szerszych zmian konfiguracyjnych. Przykład: najpierw szybka kontrola stanu BGP, potem ewentualne wgranie nowej polityki:

from netmiko import ConnectHandler
from napalm import get_network_driver

device_params = {
    "host": "10.0.0.11",
    "username": "admin",
    "password": "bardzo_tajne_haslo",
}

# Szybki podglad stanu BGP przez Netmiko
with ConnectHandler(device_type="cisco_ios", **device_params) as conn:
    bgp_summary = conn.send_command("show ip bgp summary")
    print(bgp_summary)

if "Idle" in bgp_summary:
    print("Sesja BGP w stanie Idle, przerwij lub wprowadz zmiany z ostroznoscia.")
    exit(1)

# Zmiana polityki BGP przez NAPALM
driver = get_network_driver("ios")
device = driver(**device_params)
device.open()
device.load_merge_candidate(filename="configs/r1-lab_bgp_policy.cfg")
diff = device.compare_config()

if diff:
    print("Zmiany BGP:")
    print(diff)
    confirm = input("Zatwierdzic zmiany? [y/N]: ").lower()
    if confirm == "y":
        device.commit_config()
    else:
        device.discard_config()

device.close()

Takie podejście daje szybki podgląd stanu przed i po zmianie, a jednocześnie korzysta z wygodnego mechanizmu „diff” z NAPALM.

Łączenie Ansible i Pythona w praktycznym workflow sieciowym

Generowanie danych wejściowych dla Ansible skryptem Python

Czasem dane o urządzeniach są w CSV, bazie danych lub API CMDB. Python dobrze nadaje się do ich przetworzenia do formatu zrozumiałego dla Ansible (inwentarz YAML lub JSON).

Przykład skryptu, który z pliku CSV tworzy plik hosts.yml dla Ansible:

import csv
import yaml

inventory = {"all": {"hosts": {}}}

with open("devices.csv") as f:
    reader = csv.DictReader(f)
    for row in reader:
        name = row["name"]
        inventory["all"]["hosts"][name] = {
            "ansible_host": row["ip"],
            "device_role": row["role"],
        }

with open("hosts.yml", "w") as f:
    yaml.dump(inventory, f, default_flow_style=False)

Skrypt można uruchamiać po każdej zmianie w CMDB, a Ansible zawsze będzie miało aktualny inwentarz.

Użycie modułu ansible.builtin.script do wywołania Pythona z playbooka

Ansible umożliwia uruchamianie skryptów lokalnych na maszynie „control node”. Dzięki temu można wpleść logikę Pythona w szerszy proces.

Przykładowy playbook, który przed konfiguracją urządzeń generuje raport Pythonem:

---
- name: Raport wstepny przed konfiguracja
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Generuj raport Netmiko
      ansible.builtin.script: "scripts/precheck_report.py"

- name: Konfiguracja routerow po prechecku
  hosts: routers
  gather_facts: no
  connection: network_cli

  roles:
    - base-router

Skrypt precheck_report.py może korzystać z tego samego pliku devices.yml, którego używasz ręcznie. W ten sposób jedna logika analizy stanu jest używana zarówno interaktywnie, jak i w pipeline.

Ansible jako scheduler dla skryptów Python

Jeśli w środowisku jest już Ansible Tower/AWX lub inny scheduler, nie trzeba budować osobnej infrastruktury dla Pythona. Playbook może po prostu uruchamiać skrypty jako zadania.

Przykład prostej roli roles/run-python-scripts:

roles/
  run-python-scripts/
    tasks/
      main.yml
---
# roles/run-python-scripts/tasks/main.yml
- name: Wykonaj skrypt zbierajacy dane
  ansible.builtin.script: "scripts/collect_napalm_facts.py"

- name: Wykonaj skrypt compliance
  ansible.builtin.script: "scripts/check_compliance.py"

Playbook uruchamiający rolę:

---
- name: Zadania Pythonowe z Ansible
  hosts: localhost
  gather_facts: no

  roles:
    - run-python-scripts

Całość można potem planować w Tower/AWX o dowolnych godzinach.

Python jako filtr niestandardowy w Ansible

Jeśli filtrów Jinja2 i wbudowanych funkcji nie wystarcza, można napisać własny filtr w Pythonie. To pozwala wykonywać bardziej złożone transformacje danych, zanim trafią do szablonów.

Struktura katalogu z filtrami:

plugins/
  filter/
    net_filters.py

Przykład prostego filtra, który usuwa interfejsy typu „Loopback” z listy:

# plugins/filter/net_filters.py
def exclude_loopbacks(interfaces):
    return [i for i in interfaces if not i.startswith("Loopback")]

class FilterModule(object):
    def filters(self):
        return {
            "exclude_loopbacks": exclude_loopbacks,
        }

Użycie filtra w szablonie Jinja2:

{% for intf in interfaces | exclude_loopbacks %}
interface {{ intf }}
 description Zarzadzanie
{% endfor %}

Taki filtr można łatwo rozbudować o inne reguły (np. wyłączenie portów według wzorca nazwy).

Python jako moduł Ansible (moduł akcji)

Jeśli dany typ zadania powtarza się w wielu playbookach, można go zamknąć w module Ansible napisanym w Pythonie. Wymaga to trochę więcej pracy, ale pozwala zyskać czystsze playbooki.

Prosty lokalny moduł, który zapisuje dane do pliku JSON:

library/
  save_json.py
#!/usr/bin/python

from ansible.module_utils.basic import AnsibleModule
import json

def run_module():
    module_args = dict(
        path=dict(type="str", required=True),
        data=dict(type="dict", required=True),
    )

    result = dict(changed=False)

    module = AnsibleModule(
        argument_spec=module_args,
        supports_check_mode=True,
    )

    if module.check_mode:
        module.exit_json(**result)

    with open(module.params["path"], "w") as f:
        json.dump(module.params["data"], f, indent=2)

    result["changed"] = True
    module.exit_json(**result)

def main():
    run_module()

if __name__ == "__main__":
    main()

Użycie modułu w playbooku:

Najczęściej zadawane pytania (FAQ)

Czy mogę automatyzować sieć Ansible i Pythonem, jeśli nie umiem programować?

Tak. Do podstawowej automatyzacji sieci nie są potrzebne pełne umiejętności programistyczne. Ansible opiera się na plikach YAML, które przypominają uporządkowaną konfigurację, a nie klasyczny kod.

W Pythonie w praktyce używa się prostych elementów: pętli, warunków, czytania plików i kilku funkcji z bibliotek sieciowych (np. Netmiko, NAPALM). Osoba, która swobodnie czyta konfigurację routera lub przełącznika, jest w stanie opanować ten poziom Pythona.

Od czego zacząć naukę automatyzacji sieci z Ansible i Pythonem?

Najpierw opanuj podstawy pracy w terminalu: SSH do urządzeń, podstawowe komendy Linuksa, edycję prostych plików tekstowych oraz kopiowanie plików (scp, sftp, git). Bez tego trudno będzie sprawnie uruchamiać playbooki i skrypty.

Kolejny krok to YAML i proste repozytorium Git: jeden katalog z playbookami, szablonami i plikami z danymi. Zacznij od jednego, bardzo prostego zadania, np. zebrania informacji „show version” z kilku urządzeń.

Jakie są realne korzyści z automatyzacji sieci dla małego środowiska?

Nawet przy kilkunastu urządzeniach zyskujesz czas i spójność konfiguracji. Zmiany, które ręcznie zajmowały godzinę, sprowadzają się do jednego uruchomienia playbooka, z powtarzalnym wynikiem.

Druga rzecz to kontrola: konfiguracja zapisana w plikach, wersjonowana w Git, daje historię zmian i łatwiejszy „rollback”. Znika też efekt „na tym switchu ktoś kiedyś coś dopisał z palca”.

Czy automatyzacja sieci jest bezpieczna? Jak uniknąć awarii?

Automatyzacja jest bezpieczna, jeśli zmiany najpierw testujesz w labie, a nie na produkcji. Błąd w szablonie może powielić złą konfigurację na wszystkich urządzeniach, więc środowisko testowe jest kluczowe.

Pomaga też ostrożny tryb pracy: tryb „check mode” w Ansible, skrypty Pythona zaczynające od samego odczytu (zbieranie konfiguracji, statusów) i dopiero później wykonywanie zmian. Do tego małe, częste wdrożenia zamiast jednej ogromnej modyfikacji.

Czym różni się „jednorazowy skrypt” od porządnego procesu automatyzacji?

Jednorazowy skrypt to często plik napisany „na szybko”, z zaszytymi na sztywno adresami, loginami i bez dokumentacji. Ratuje sytuację, ale po kilku miesiącach nikt nie pamięta, jak go uruchomić i czy jest jeszcze aktualny.

Powtarzalny proces ma prostą strukturę katalogów, osobne pliki z danymi (np. lista urządzeń, zmienne w YAML), krótki opis w README i rozdzielenie logiki od konfiguracji. Taki projekt można uruchomić po dłuższym czasie bez zgadywania.

Jak wygląda prosty, praktyczny przykład automatyzacji z Ansible?

Klasyczny przypadek to zmiana hasła na wielu przełącznikach. Zamiast logować się po kolei na każde urządzenie i ręcznie wpisywać te same komendy, przygotowujesz playbook, w którym w jednym miejscu definiujesz nowe hasło (zaszyfrowane Ansible Vaultem).

Następnie wskazujesz grupę hostów, np. „access-switches”, i uruchamiasz zadanie. Po kilku minutach masz zaktualizowane hasła, zapis logów i jasną informację, gdzie zmiana się nie udała.

Ile czasu dziennie przeznaczyć na naukę Ansible i Pythona jako administrator sieci?

W praktyce lepsze efekty daje 20–30 minut dziennie niż wielogodzinny „projekt” raz na kilka miesięcy. W takiej krótkiej sesji możesz uruchomić jeden playbook na labie, poprawić fragment skryptu lub dodać nową zmienną w YAML.

Po kilku tygodniach takich małych kroków zwykle da się już pisać własne proste playbooki i skrypty, które realnie oszczędzają czas w produkcyjnej sieci.

Najważniejsze wnioski

  • Automatyzacja z Ansible i Pythonem usuwa ręczne „klepanie” tych samych komend na wielu urządzeniach, oszczędza godziny pracy tygodniowo i ogranicza literówki oraz pomyłki w konfiguracji.
  • Wykorzystanie playbooków i skryptów daje spójną konfigurację w całej sieci, mniej „dziwnych” incydentów i czytelny log zmian, który można łatwo prześledzić i odtworzyć dzięki Gitowi.
  • Automatyzacja zmniejsza stres przy wdrażaniu zmian: ten sam proces da się przetestować w labie, uruchomić w trybie „check” i dopiero potem zastosować w produkcji.
  • Do startu nie trzeba być programistą – Ansible opiera się na prostym YAML, a w Pythonie wystarczą pętle, warunki i użycie gotowych bibliotek (Netmiko, NAPALM) do konkretnych zadań sieciowych.
  • Największy efekt dają małe, praktyczne narzędzia: np. playbook do zmiany hasła na kilkudziesięciu przełącznikach czy skrypt zbierający konfiguracje z całej sieci jednym poleceniem.
  • Podstawą pracy jest biegłość w terminalu i SSH oraz wygodna obsługa plików i edytora tekstu, bo cała automatyzacja kręci się wokół uruchamiania komend i edycji plików konfiguracyjnych.
  • Konfiguracja traktowana jako kod (pliki YAML, repozytorium Git, wersjonowanie) zamienia jednorazowe „skrypty ratunkowe” w powtarzalny proces, który da się uruchamiać, rozwijać i audytować latami.