Ansible: Automatyzacja sieci
 

Ansible świetnie nadaje się do zarządzania konfiguracją urządzeń sieciowych. Dotyczy to zarówno prostych, jak i bardziej zaawansowanych zmian, które można przeprowadzać bezpośrednio lub za pośrednictwem różnych kontrolerów, jak Cisco NSO (Network Services Orchestrator), Cisco ACI (Application Centric Infrastructure), Cisco SD-WAN czy Cisco DNA (Digital Network Architecture). W tym artykule skupimy się na bezpośredniej automatyzacji wybranych urządzeń sieciowych firmy Cisco Systems.

Kiedy używamy Ansible do zarządzania hostami z systemem GNU/Linux, Ansible kopiuje moduł napisany w Python do zarządzanego hosta i na nim go uruchamia. Ma to dużą zaletę, szczególnie kiedy wykonanie zadań jest złożone i czasochłonne. Dzięki temu, węzeł używany do zarządzania nie jest prawie w ogóle obciążony i przy niewielkich zasobach może obsługiwać bardzo dużą ilość hostów. W przypadku automatyzacji sieci wygląda to nieco inaczej, przynajmniej od środka, bo z punktu widzenia użytkowego nic się nie zmienia.

Powodem tego jest kilka rzeczy. Moduł działający na hoście najczęściej działa bezpośrednio na jego plikach konfiguracyjnych. W przypadku urządzeń sieciowych nie pracuje się bezpośrednio na plikach. Konfiguracja dostępna jest poprzez odpowiedni interfejs, jak CLI over SSH, XML over SSH, czy API over HTTP/HTTPS. Moduł działający na hoście tworzy dodatkowo różne katalogi i pliki, co nie jest często albo możliwe albo wydajne na urządzeniach sieciowych, ze względu na rodzaj stosowanych w nich pamięci. Moduł też wymaga do działania Python-a, czego niestety większość starszych urządzeń sieciowych nie posiada. To prowadzi do tego, że najwygodniej jest wykonywać moduł Ansible lokalnie, a następnie nawiązywać połączenie poprzez udostępniany interfejs.

Ansible domyślnie nawiązuje połączenie SSH i próbuje wysłać w nim swój moduł do zdalnego węzła. Aby to zmienić, musimy użyć jednego z trzech typów dostępnych połączeń:

  • network_cli, które korzysta CLI over SSH (trwałe połączenie).
  • netconf, które korzysta z XML over SSH (trwałe połączenie).
  • httpapi, które korzysta z API over HTTP/HTTPS (trwałe połączenie).

Dawniej do tego celu był wykorzystywany typ połączenia "local", którym wskazywaliśmy Ansible, że moduł ma zostać uruchomiony lokalnie, a do węzła nawiązywane połączenie w sposób określony wewnątrz słownika "provider". Niestety, nie odbywało się to w sposób wydajny, gdyż dla każdego zadania było na nowo otwierane i zamykane połączenie. Obecnie nie zaleca się korzystania z typu połączenia "local".

W przypadku automatyzacji sieci musimy zakładać, że moduł będzie wykonywał się bezpośrednio na węźle zarządzającym, a nie urządzeniach sieciowych. Często nawet będzie wysyłał jakieś polecenia do urządzeń sieciowych, czekał na odebrane informacje, po ich odebraniu wykonywał coś lokalnie, a następnie znowu coś do nich wysyłał. Dlatego przy dużej ilości urządzeń sieciowych wymagane może być wykorzystanie większej ilości węzłów z Ansible. Takimi klastrami da się zarządzać z jednego miejsca za pomocą platformy Red Hat Ansible Automation.


Dostępna jest bardzo duża ilość modułów Ansible do automatyzacji urządzeń sieciowych. Przy ich wyborze warto pamiętać o wspomnianej wcześniej idempotentności. Dlatego najlepiej wybierać w pierwszej kolejności moduły wyspecjalizowane do obsługi konkretnych zadań, a dopiero kiedy nie możemy takich znaleźć, korzystać z tych bardziej uniwersalnych, za pomocą których można zrobić prawie wszystko. O ile listę dostępnych modułów sieciowych można znaleźć w dokumentacji Ansible, to należy pamiętać o Red Hat Automation Hub, gdzie dostępne są dodatkowe moduły, role i całe kolekcje niedostępne w standardzie. Dostęp do Red Hat Automation Hub można uzyskać po wykupieniu subskrypcji dla platformy Red Hat Ansible Automation.


Poszczególne zadania playbook-a, dla każdego z węzłów wykonywane są sekwencyjnie według kolejności ich ułożenia. Natomiast mogą być one wykonywane równolegle na wielu węzłach w tym samym czasie. To na ilu, zależne jest od konfiguracji - domyślnie 5. Do modyfikacji tego służy opcja "-f" lub "--fork" polecenia "ansible" i "ansible-playbook" albo parametr "forks" pliku "ansible.cfg". Parametr ten określa ilość równoległych procesów, jaka będzie dostępna do wykonywania zadań. W ten sposób główny proces dzieli się lub inaczej mówiąc rozwidla się (ang. fork) na wiele mniejszych. Domyślnie, zanim Ansible zacznie wykonywać nowe zadanie na pierwszym z węzłów, czeka aż wszystkie pozostałe węzły zakończą wykonywania poprzedniego zadania. Nawet, kiedy pozostałe procesy są wolne i nic nie robią. To domyślne zachowanie wynika z domyślnego ustawienia parametru "strategy" na wartość "linear".

Kiedy jakaś niewielka grupa urządzeń jest znacząco wolniejsza od reszty, warto rozważyć zmianę tego parametru. Po przestawieniu wartości parametru "strategy" na "free", Ansible będzie wykonywał kolejne zadania tak szybko jak to możliwe. Czyli, jak tylko pojawią się wolne do użycia procesy. Nadal będzie wykonywał zadania w odpowiedniej kolejności, ale kiedy w ramach bieżącego zadania przestaną być potrzebne kolejne procesy, to zaczną się one zajmować obsługą kolejnych zadań na tych węzłach, które zakończyły już poprzednie zadanie. Dzięki temu wykorzystanie dostępnych procesów jest efektywniejsze, co przekłada się na poprawę całościowej wydajności.

Dodatkowo, jeżeli zależy nam na wydajności, to powinniśmy unikać wydawania polecenia "show running-config". Jest to jedno z najbardziej zasobożernych poleceń, jakie można wysłać do urządzenia sieciowego. Jego przetworzenie trwa na tyle długo, że odradzamy wykonywania jakichkolwiek poleceń na jego bazie na dużej ilości urządzeń sieciowych oraz wykorzystywania go w playbook-ach.


Przejdźmy teraz przez kilka przykładów wykorzystania Ansible do automatyzacji urządzeń Cisco Systems. Do tego celu wykorzystamy dwa przełączniki:

  • Cisco Nexus 3172TQ z oprogramowaniem NXOS w wersji 7 (moduły Ansible z prefiksem "nxos_").
  • Cisco Catalyst 9300 z oprogramowaniem IOS-XE w wersji 16. (moduły Ansible z prefiksem "ios_").

O ile my będziemy wykonywać nasze zadania tylko na dwóch różnych przełącznikach, to równie dobrze mogłoby ich być znacznie, znacznie więcej. Dlatego o ile są to proste czynności, to kiedy mówimy o wykonaniu ich na większej ilości urządzeń lub za każdym razem kiedy kreowana lub usuwana jest jakaś usługa, to robienie tego ręcznie jest mało efektywne, czasochłonne i podatne na błędy czy pominięcia.

Nasz plik inwentarza został nieco zmodyfikowany. Za pomocą "ansible_network_os" wskazaliśmy systemy operacyjne, jakie są używane przez nasze urządzenia sieciowe. Użyliśmy także najprostszego sposobu logowania do nich, za pomocą użytkownika i jawnie podanego hasła. Ansible umożliwia przechowywanie poufnych informacji, jak m.in. hasła w specjalnych pojemnikach poświadczeń, dzięki czemu nie są one dostępne w postaci jawnego tekstu. W związku z tym, że naszym celem tutaj jest pokazanie jak w najprostszy sposób zarządzać urządzeniami sieciowymi, a też tej metody nawiązywania połączenia jeszcze nie pokazywaliśmy, to właśnie tutaj z niej skorzystamy.

[msleczek@vm0-net projekt_A]$ cat inventory 
[all:vars]
ansible_user=admin
ansible_ssh_pass=networkers.pl
ansible_connection=network_cli
ansible_become=no

[catalyst]
10.8.100.65 ansible_network_os=ios

[nexus]
10.8.100.66 ansible_network_os=nxos

[msleczek@vm0-net projekt_A]$


Do nawiązywania połączeń będziemy korzystać z metody "network_cli". Zaczniemy od czegoś prostego i użytecznego, czyli zebrania konfiguracji ze wszystkich urządzeń sieciowych. Do tego celu posłużymy się parametrem "backup" modułów "ios_config" i "nxos_config".

[msleczek@vm0-net projekt_A]$ cat backup.yml 
---
- hosts: catalyst
gather_facts: no
tasks:
- name: IOS/IOS-XE Backup Configuration
ios_config:
backup: yes

- hosts: nexus
gather_facts: no
tasks:
- name: NXOS Backup Configuration
nxos_config:
backup: yes

[msleczek@vm0-net projekt_A]$ ansible-playbook backup.yml

PLAY [catalyst] ******************************************************************

TASK [IOS/IOS-XE Backup Configuration] *******************************************
changed: [10.8.100.65]

PLAY [nexus] *********************************************************************

TASK [NXOS Backup Configuration] *************************************************
changed: [10.8.100.66]

PLAY RECAP ***********************************************************************
10.8.100.65 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.8.100.66 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[msleczek@vm0-net projekt_A]$
 


Jeżeli nie wskażemy jawnie gdzie mają być składowane kopie zapasowe oraz nie podamy nazw ich plików, to w katalogu bieżący Ansible utworzy katalog o nazwie "backup/" i w nim umieści konfiguracje z urządzeń sieciowych.

[msleczek@vm0-net projekt_A]$ ls -l backup
total 20
-rw-rw-r--. 1 msleczek msleczek 5535 May 14 13:05 10.8.100.65_config.2020-05-14@13:05:13
-rw-rw-r--. 1 msleczek msleczek 9962 May 14 13:05 10.8.100.66_config.2020-05-14@13:05:18
[msleczek@vm0-net projekt_A]$


Zrobimy teraz coś bardziej wymagającego. Na przełączniku Cisco Catalyst 9300 zabezpieczymy linie VTY, poprzez które odbywa się zarządzanie przełącznikiem. Będzie to możliwe tylko z dwóch adresów IP oraz tylko za pomocą SSH. Dodatkowo, wszyscy będą automatycznie wylogowywani po 15-minutach braku aktywności. Jeżeli doszło do zmiany w konfiguracji, to zostanie ona trwale zapisana.

Polecenia CLI dostępne w systemach Cisco IOS-XE oraz Cisco NXOS powiązane są z sekcją czy kontekstem w jakim się je wydaje. Oznacza to, że to samo polecenie może służyć do całkiem różnych rzeczy, w zależności od tego, w jakim miejscu konfiguracji zostanie wydane. Polecenia, które chcemy wydać umieszcza się na liście "lines". Natomiast parametr "parents" umożliwia ustawienie stosownego kontekstu czy sekcji w jakiej zostaną one wydane.

Zdarza się też, że przed wydaniem poleceń chcemy usunąć jakieś poprzednie. Do tego celu służy parametr "before". W naszym przykładzie pierw usuwamy całą listę ACL, a następnie tworzymy nową od zera. Polecenia na liście "before" zostaną wydane tylko, jeżeli moduł musi wprowadzić zmiany.

Do weryfikacji tego służy parametr "match", które porównuje aktualną konfigurację z tym co chcemy osiągnąć w ramach danego kontekstu. Parametr ten może przyjmować wartości:

  • "none" - nie sprawdzamy nic w konfiguracji bieżącej, więc polecenia zawsze zostaną wprowadzone.
  • "strict" - sprawdza, czy w konfiguracji bieżącej żądane polecenia są w odpowiedniej kolejności.
  • "exact" - podobnie jak strict, ale dana sekcja nie może zawierać nic więcej.
  • "line" - sprawdza linia po linii każde polecenie z konfiguracją, jak jest, to nic nie robi, a jak nie, to je dodaje.

W naszym przykładzie został on ustawiony na "strict", co oznacza że polecenia z listy "lines" będą sprawdzane nie tylko pod względem wystąpienia, ale też pozycji w jakiej powinny się znaleźć, co przy listach ACL jest kluczowe.

Podobna sytuacja ma miejsce dalej, gdzie dzięki zastosowaniu parametru "parents", polecenia z listy "lines", których celem jest zabezpieczenie linii VTY od 0 do 15 zostaną wprowadzone w trybie konfiguracji tych linii.

[msleczek@vm0-net projekt_A]$ cat configure_switches.yml 
---
- hosts: catalyst
gather_facts: false
tasks:
- name: Create ACL
ios_config:
match: strict
before: no ip access-list extended MGMT-ACL
parents: ip access-list extended MGMT-ACL
lines:
- permit ip host 10.8.232.120 any
- permit ip host 10.8.100.100 any
- name: Harden VTY lines
ios_config:
parents:
- line vty 0 15
lines:
- exec-timeout 15 0
- transport input ssh
- access-class MGMT-ACL in
- name: Save Configuration
ios_config:
save_when: changed

- hosts: nexus
gather_facts: false
tasks:
- name: Configure hostname and domain name
nxos_system:
hostname: nx3172-lab
domain_name: networkers.pl
- name: Configure DNS Servers
nxos_system:
name_servers:
- 1.1.1.1
- 8.8.4.4
- name: Banner MOTD
nxos_banner:
banner: motd
text: "{{ lookup('file', 'banner.txt') }}"
state: present
- name: VLAN creation
nxos_vlans:
config:
- vlan_id: 123
name: ANSIBLE-123
enabled: yes
state: active
- name: Save Configuration
nxos_config:
save_when: changed

[msleczek@vm0-net projekt_A]$


Na przełączniku Cisco Nexus 3172TQ ustawimy nazwę hostname, nazwę domenową, serwery DNS, komunikat MOTD (Message Of The Day), jaki pojawia się w trakcie logowania oraz jeden VLAN. Dodatkowo, jeżeli doszło do zmiany w konfiguracji, to zostanie ona trwale zapisana.

[msleczek@vm0-net projekt_A]$ ansible-playbook configure_switches.yml

PLAY [catalyst] ****************************************************************************

TASK [Create ACL] **************************************************************************
changed: [10.8.100.65]

TASK [Harden VTY lines] ********************************************************************
changed: [10.8.100.65]

TASK [Save Configuration] ******************************************************************
changed: [10.8.100.65]

PLAY [nexus] *******************************************************************************

TASK [Configure hostname and domain name] **************************************************
changed: [10.8.100.66]

TASK [Configure DNS Servers] ***************************************************************
changed: [10.8.100.66]

TASK [Banner MOTD] *************************************************************************
changed: [10.8.100.66]

TASK [VLAN creation] ***********************************************************************
changed: [10.8.100.66]

TASK [Save Configuration] ******************************************************************
changed: [10.8.100.66]

PLAY RECAP *********************************************************************************
10.8.100.65 : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.8.100.66 : ok=5 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

[msleczek@vm0-net projekt_A]$


Urządzeniami sieciowymi można również zarządzać za pomocą poleceń doraźnych (łac. ad-hoc). Dotyczy to zarówno wprowadzania zmian, jak i zbierania pewnych danych. To pierwsze może być przydatne przy zmianie hasła na dużej ilości urządzeń, a to drugie przy inwentaryzacji czy weryfikacji stanu pewnych elementów lub usług urządzenia. 

Do zbierania faktów z urządzeń sieciowych służą oddzielne wyspecjalizowane moduły. Dla urządzeń z systemem IOS lub IOS-XE należy do tego celu stosować moduł "ios_facts", a dla urządzeń z systemem NXOS moduł "nxos_facts".

[msleczek@vm0-net projekt_A]$ ansible -m ios_facts catalyst -a "gather_subset=hardware"
10.8.100.65 | SUCCESS => {
"ansible_facts": {
"ansible_net_api": "cliconf",
"ansible_net_filesystems": [
"flash:"
],
"ansible_net_filesystems_info": {
"flash:": {
"spacefree_kb": 8897616.0,
"spacetotal_kb": 11087104.0
}
},
"ansible_net_gather_network_resources": [],
"ansible_net_gather_subset": [
"default",
"hardware"
],
"ansible_net_hostname": "cat9300",
"ansible_net_image": "flash:packages.conf",
"ansible_net_iostype": "IOS",
"ansible_net_memfree_mb": 1105641.8515625,
"ansible_net_memtotal_mb": 1392644.6328125,
"ansible_net_model": "C9300-48P",
"ansible_net_python_version": "3.6.8",
"ansible_net_serialnum": "FCW2248E156",
"ansible_net_stacked_models": [
"C9300-48P"
],
"ansible_net_stacked_serialnums": [
"FCW2248E156"
],
"ansible_net_system": "ios",
"ansible_net_version": "16.06.05",
"ansible_network_resources": {},
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}
[msleczek@vm0-net projekt_A]$


Poniżej zobaczyć można nasz baner MOTD, jaki ustawiliśmy na przełączniku Cisco Nexus 3172TQ:

[msleczek@vm0-net projekt_A]$ ansible -m nxos_command nexus -a "commands='show banner motd'"
10.8.100.66 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"stdout": [
"W networkers.pl kupisz m.in. produkty:\n- Cisco Systems\n- Red Hat\n- LINBIT\n- Storware\n- VMware\n- Veeam\n- NextCloud\n- Collabora Online\n- ONLYOFFICE\n- SuperMicro\n\nZapraszamy do dzialu PRODUKTY i ROZWIAZANIA."
],
"stdout_lines": [
[
"W networkers.pl kupisz m.in. produkty:",
"- Cisco Systems",
"- Red Hat",
"- LINBIT",
"- Storware",
"- VMware",
"- Veeam",
"- NextCloud",
"- Collabora Online",
"- ONLYOFFICE",
"- SuperMicro",
"",
"Zapraszamy do dzialu PRODUKTY i ROZWIAZANIA."
]
]
}
[msleczek@vm0-net projekt_A]$


Istnieje także możliwość wykorzystania szablonów Jinja2 do generowania konfiguracji, które następnie będą wysyłane do urządzeń sieciowych. Co fajne, to szablony Jinja2 są obsługiwane bezpośrednio przez moduły sieciowe, dzięki czemu nie jest potrzebne stosowanie pośrednio modułu "template".

Na koniec warto dodać, że w ramach Red Hat Automation Hub dostępne jest wiele gotowych roli, pluginów, modułów oraz kolekcji tworzonych przez różnych producentów rozwiązań sieciowych. Przykładem może być bardzo dobrze dopracowany zestaw do automatyzacji Cisco ACI (Application Centric Infrastructure).


Zainteresowanych automatyzacją oraz orkiestracją sieci i jej usług na większą skalę, zachęcamy do zapoznania się z Cisco NSO (Network Services Orchestrator), który umożliwia kooperację z Ansible oraz wprowadzanie wszelkich zmian w sposób atomowy w ramach całej infrastruktury sieciowej.


Narzędzie Ansible dostępne jest w systemie RHEL bez żadnych dodatkowych opłat. Niemniej, Red Hat nie udziela dla niego wsparcia i nie daje dostępu do innych komponentów platformy Red Hat Ansible Automation bez wykupienia stosownych subskrypcji. Zainteresowanych ich zakupem This email address is being protected from spambots. You need JavaScript enabled to view it..


Zapraszamy do kontaktu drogą mailową This email address is being protected from spambots. You need JavaScript enabled to view it. lub telefonicznie +48 797 004 932.