💾

Završni praktični zadatak – Modul 24 – Automatizacija mreža

Cilj

Samostalno napraviti mali, bezbedan i dokumentovan skup alata za automatizaciju laboratorijske mreže sa tri rutera, koji svakodnevno proverava, čuva i usklađuje konfiguraciju — objedinjujući sve veštine ovog modula.

Scenario

Firma „Nova Mreža" planira da automatizuje upravljanje mrežom. Pre bilo kakvog rada u produkciji, uprava traži da se u laboratoriji dokaže da su alati pouzdani, bezbedni i razumljivi i drugim administratorima. Laboratorija ima tri VyOS rutera: dva u „centrali" i jedan u „poslovnici".

Zahtevi zadatka

  1. Laboratorija: tri VyOS rutera (VYOS1, VYOS2, VYOS3) i Ubuntu Server kontrolni računar u Host-only mreži, sa nalogom netauto na svim ruterima.
  2. Izvor istine: uredjaji.yaml sa svim ruterima i poljima ime, host, device_type, lokacija (centrala ili poslovnica) i loopback.
  3. Provera dostupnosti: skripta koja proverava ping i SSH i pravi JSON izveštaj sa vremenom provere; uređaje proverava paralelno.
  4. Backup: skripta koja čuva konfiguracije sa datumom i vremenom, prikazuje razlike u odnosu na prethodni backup i završava se greškom (izlazni kod različit od 0) ako backup nije uspeo za bar jedan uređaj.
  5. Standard: Ansible playbook koji na svim ruterima podešava vremensku zonu, poruku pre prijave, LLDP i opis upravljačkog interfejsa, sa lozinkom u Ansible Vault-u. Dokažite idempotentnost (drugo pokretanje: changed=0).
  6. Provera usklađenosti: Python skripta koja na svakom ruteru proverava da li show configuration commands sadrži sve linije standarda, i pravi JSON izveštaj sa listom linija koje nedostaju po uređaju. Skripta samo izveštava — ne menja konfiguraciju.
  7. Bezbedno uvođenje: dokumentujte uvođenje standarda redosledom --check --diff → --limit na jedan ruter poslovnice → svi ruteri. Na jednom ruteru demonstrirajte commit-confirm sa automatskim povratkom.
  8. Git i dokumentacija: projekat u Git repozitorijumu sa najmanje pet smislenih commit-ova, .gitignore-om i README.md fajlom koji objašnjava namenu, pokretanje svake skripte i playbook-a i način zadavanja kredencijala.

Ograničenja i pravila

  • Nijedna lozinka ni API ključ ne sme se naći u kodu, YAML fajlovima (osim šifrovanog Vault fajla) ni u Git istoriji.
  • Sve skripte moraju nastaviti rad kada jedan uređaj nije dostupan i jasno prijaviti koji uređaj nije obrađen.
  • Rad isključivo nad sopstvenim laboratorijskim uređajima.

Šta predati

  • Ceo projekat (Git repozitorijum bez foldera venv/ i backup/).
  • Izlaze pokretanja svih skripti i playbook-a (tekstualni fajl ili snimci ekrana), uključujući drugo pokretanje playbook-a (changed=0).
  • JSON izveštaje o dostupnosti i usklađenosti — jedan u kome je sve usklađeno, i jedan posle namerne ručne izmene na jednom ruteru.
  • Kratak opis demonstracije commit-confirm-a.

Kriterijumi ocenjivanja

KriterijumBroj poena
Laboratorija i izvor istine (uredjaji.yaml)10
Paralelna provera dostupnosti sa JSON izveštajem10
Backup sa razlikama i ispravnim izlaznim kodom15
Ansible playbook sa Vault-om i dokazanom idempotentnošću20
Skripta za proveru usklađenosti20
Bezbedno uvođenje (--check, --limit, commit-confirm)10
Git, .gitignore, README.md, bez tajni u kodu15
Ukupno100

Rešenje

Kompletno rešenje dostupno je u /resenja/modul-24/08-zavrsni-zadatak-resenje.md.

📖 Prikaži rešenje

Rešenje završnog zadatka – Modul 24

Ovo je jedno ispravno rešenje. Prihvataju se i drugačija imena fajlova i struktura koda, ako su ispunjeni svi zahtevi i pravila o kredencijalima.

Struktura projekta

nova-mreza-automatizacija/
├── .gitignore
├── README.md
├── uredjaji.yaml
├── zajednicko.py
├── provera_dostupnosti.py
├── backup.py
├── uskladjenost.py
└── ansible/
    ├── ansible.cfg
    ├── inventory.ini
    ├── group_vars/vyos/vars.yml
    ├── group_vars/vyos/vault.yml   (šifrovano)
    └── standard.yml

.gitignore:

venv/
backup/
izlazi/
*.json
.env

Izvor istine – uredjaji.yaml

---
uredjaji:
  - ime: VYOS1
    host: 192.168.56.11
    device_type: vyos
    lokacija: centrala
    loopback: 10.255.255.11/32
  - ime: VYOS2
    host: 192.168.56.12
    device_type: vyos
    lokacija: centrala
    loopback: 10.255.255.12/32
  - ime: VYOS3
    host: 192.168.56.13
    device_type: vyos
    lokacija: poslovnica
    loopback: 10.255.255.13/32

zajednicko.py je isti kao u laboratorijskoj vežbi (Korak 5).

Paralelna provera dostupnosti – provera_dostupnosti.py

import json
import socket
import subprocess
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime

from zajednicko import ucitaj_uredjaje


def ping(ip):
    rezultat = subprocess.run(["ping", "-c", "2", "-W", "1", ip], capture_output=True)
    return rezultat.returncode == 0


def port_otvoren(ip, port=22, timeout=2):
    try:
        with socket.create_connection((ip, port), timeout=timeout):
            return True
    except OSError:
        return False


def proveri(u):
    return {"ime": u["ime"], "host": u["host"], "lokacija": u["lokacija"],
            "ping": ping(u["host"]), "ssh": port_otvoren(u["host"])}


with ThreadPoolExecutor(max_workers=10) as izvrsilac:
    rezultati = list(izvrsilac.map(proveri, ucitaj_uredjaje()))

for r in rezultati:
    print(f"{r['ime']:6} {r['lokacija']:11} ping={'OK' if r['ping'] else 'NE'}  ssh={'OK' if r['ssh'] else 'NE'}")

with open("dostupnost.json", "w", encoding="utf-8") as f:
    json.dump({"vreme": datetime.now().isoformat(timespec="seconds"), "uredjaji": rezultati},
              f, indent=2, ensure_ascii=False)

Backup – backup.py

Isti kao u laboratorijskoj vežbi (Korak 8). Ispunjava zahteve: vremenska oznaka u nazivu, difflib razlike u odnosu na poslednji prethodni backup, nastavak rada kada uređaj nije dostupan, i raise SystemExit(...) na kraju. SystemExit sa tekstom završava proces sa izlaznim kodom 1. Provera: python backup.py; echo $?.

Ansible standard – ansible/standard.yml

ansible.cfg, inventory.ini (sa vyos3), group_vars/vyos/vars.yml i šifrovani vault.yml su kao u Koraku 11 vežbe.

---
- name: Standard Nova Mreža
  hosts: vyos
  gather_facts: false
  tasks:
    - name: Standardna podešavanja
      vyos.vyos.vyos_config:
        lines:
          - set system time-zone 'Europe/Belgrade'
          - set system login banner pre-login 'Samo za ovlascene korisnike'
          - set service lldp interface all
          - set interfaces ethernet eth0 description 'MGMT-HOSTONLY'
        save: true

Provera usklađenosti – uskladjenost.py

import json
from datetime import datetime

from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException

from zajednicko import parametri_veze, ucitaj_uredjaje

STANDARD = [
    "set system time-zone 'Europe/Belgrade'",
    "set system login banner pre-login 'Samo za ovlascene korisnike'",
    "set service lldp interface all",
    "set interfaces ethernet eth0 description 'MGMT-HOSTONLY'",
]


def normalizuj(red):
    return red.replace("'", "").strip()


izvestaj = {"vreme": datetime.now().isoformat(timespec="seconds"), "uredjaji": {}}

for u in ucitaj_uredjaje():
    try:
        with ConnectHandler(**parametri_veze(u)) as veza:
            izlaz = veza.send_command("show configuration commands")
    except (NetmikoTimeoutException, NetmikoAuthenticationException) as greska:
        izvestaj["uredjaji"][u["ime"]] = {"greska": type(greska).__name__}
        print(f"{u['ime']}: NIJE PROVEREN — {type(greska).__name__}")
        continue

    konfiguracija = {normalizuj(red) for red in izlaz.splitlines()}
    nedostaje = [linija for linija in STANDARD if normalizuj(linija) not in konfiguracija]
    izvestaj["uredjaji"][u["ime"]] = {"uskladjen": not nedostaje, "nedostaje": nedostaje}
    print(f"{u['ime']}: {'USKLAĐEN' if not nedostaje else 'NEDOSTAJE ' + str(len(nedostaje)) + ' linija'}")

with open("uskladjenost.json", "w", encoding="utf-8") as f:
    json.dump(izvestaj, f, indent=2, ensure_ascii=False)

Skripta samo čita konfiguraciju (send_command) i nikada ne šalje izmene. Normalizacija uklanja navodnike, kao što to radi i modul vyos_config, pa se ista linija sa navodnicima i bez njih smatra jednakom.

Primer izveštaja posle ručnog brisanja LLDP-a na VYOS3:

{
  "vreme": "2026-09-25T14:02:11",
  "uredjaji": {
    "VYOS1": {"uskladjen": true, "nedostaje": []},
    "VYOS2": {"uskladjen": true, "nedostaje": []},
    "VYOS3": {"uskladjen": false, "nedostaje": ["set service lldp interface all"]}
  }
}

Bezbedno uvođenje – dokumentovan redosled

  1. ansible-playbook standard.yml --ask-vault-pass --check --diff — pregled linija koje bi bile poslate na svaki ruter; ništa nije promenjeno.
  2. ansible-playbook standard.yml --ask-vault-pass --limit vyos3 — primena samo na ruter poslovnice; provera uskladjenost.py (VYOS3 usklađen) i ručna SSH prijava (pristup radi).
  3. ansible-playbook standard.yml --ask-vault-pass — primena na sve; changed=0 za vyos3, changed=1 za vyos1 i vyos2.
  4. Ponovljeno pokretanje: changed=0 za sva tri — idempotentnost dokazana.

Demonstracija commit-confirm (konzola VYOS2):

configure
set interfaces ethernet eth0 address 192.168.56.99/24
delete interfaces ethernet eth0 address 192.168.56.12/24
commit-confirm 2

Upravljačka adresa je namerno promenjena, pa kontrolni računar gubi pristup na 192.168.56.12 (provera_dostupnosti.py: VYOS2 ping=NE). Izmena nije potvrđena (confirm) niti sačuvana (save). Posle dva minuta VYOS2 se vraća na prethodnu konfiguraciju (zavisno od verzije restartom ili ponovnim učitavanjem), pa provera_dostupnosti.py ponovo prikazuje ping=OK i ssh=OK.

README.md projekta (skraćeno)

# Automatizacija laboratorijske mreže Nova Mreža

Samo za laboratorijske uređaje iz `uredjaji.yaml`.

## Priprema
python3 -m venv venv && source venv/bin/activate
pip install netmiko pyyaml ansible
export NET_USER=netauto
read -s -p "Lozinka: " NET_PASS; echo; export NET_PASS

## Skripte
- provera_dostupnosti.py — ping i SSH za sve uređaje, izveštaj dostupnost.json
- backup.py — backup u backup/ sa razlikama; izlazni kod 1 ako neki uređaj nije obrađen
- uskladjenost.py — samo čita konfiguraciju; izveštaj uskladjenost.json

## Ansible
cd ansible
ansible-playbook standard.yml --ask-vault-pass --check --diff
ansible-playbook standard.yml --ask-vault-pass --limit <ruter>
ansible-playbook standard.yml --ask-vault-pass

## Kredencijali
Lozinke se ne upisuju u fajlove. Lozinka za Ansible je u ansible/group_vars/vyos/vault.yml (Ansible Vault).

Primer Git istorije

a1c3e02 Dodat .gitignore i README
b7d41f9 Izvor istine: uredjaji.yaml sa tri rutera
c2e9a10 Paralelna provera dostupnosti sa JSON izveštajem
d58f3b4 Backup sa razlikama i izlaznim kodom
e91a7c6 Ansible inventory, Vault i standard.yml
f03b8d2 Provera usklađenosti sa standardom

Provera da tajne nisu u istoriji: git log -p | grep -i -E "pass|lozinka|key" ne sme pokazati nijednu vrednost lozinke ili ključa (dozvoljena su imena promenljivih poput NET_PASS i šifrovan sadržaj vault.yml).

Napomena za ocenjivača

Ključno je da:

  • skripte nastavljaju rad kada jedan uređaj nije dostupan;
  • backup.py vraća izlazni kod različit od 0 kada backup nije uspeo za bar jedan uređaj;
  • provera usklađenosti ne menja konfiguraciju;
  • drugo pokretanje playbook-a ima changed=0;
  • u repozitorijumu i Git istoriji nema nijedne lozinke ni API ključa kao običnog teksta.