Ansible: Polecenia ad-hoc i moduły
 

Wysyłanie doraźnych poleceń (ang. ad-hoc commands) jest bardzo proste, a jednocześnie otwiera wiele bardzo użytecznych możliwości. Dzięki nim, wygodnie możemy wydawać pojedyncze polecenia zarówno na jednym, jak i wielu węzłach. Wydawanie takich poleceń umożliwia m.in. odpytanie o różne stany, diagnozę, weryfikację zgodności, zgrywanie konfiguracji, kopiowanie plików, zrestartowanie usługi lub całego węzła, zarządzanie pakietami i kontami użytkowników, aktualizację oprogramowania, a nawet okresową zmianę hasła.

Do wydawania doraźnych poleceń (ang. ad-hoc commands) służy poznane wcześniej polecenie "ansible".

$ ansible [pattern] -m [module] -a "[module options]"
$ ansible [pattern] --module-name [module] --args "[module options]"
  • [pattern] - wzorzec dopasowania do inwentarzu, który został dokładnie opisany w poprzednim artykule.
  • [module] - nazwa użytego modułu.
  • [module options] - opcje i argumenty dla użytego modułu.

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


Nasz plik konfiguracyjny Ansible zakłada, że do wszystkich hostów łączymy się z użyciem użytkownika "ansible" bez podawania hasła. Wymaga to wcześniejszego wygenerowania pary kluczy na zarządzającym hoście dla usługi SSH oraz wysłania klucza publicznego do odpowiedniego miejsca na koncie użytkownika "ansible" zarządzanych węzłów. Po zalogowaniu się eskalowane są uprawnienia do użytkownika "root" przy użyciu "sudo" bez podawania żadnego hasła. Do tego celu wymagana jest odpowiednia konfiguracja usługi "sudo" na zarządzanych węzłach. Aby skupić się tylko na działaniu Ansible, elementy te pomijamy.

[msleczek@vm0-net projekt_A]$ cat ansible.cfg 
[defaults]
inventory = ./inventory
remote_user = ansible
ask_pass = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
[msleczek@vm0-net projekt_A]$


Nasz plik inwentarza jest bardzo prosty. Znajdują się w nim 4 adresy IPv4 serwerów z systemem RHEL8.

[msleczek@vm0-net projekt_A]$ cat inventory
10.8.232.121
10.8.232.122
10.8.232.123
10.8.232.124
[msleczek@vm0-net projekt_A]$


Aby zweryfikować możliwość prawidłowego zarządzania tymi hostami można użyć modułu "ping". Sprawdza on, czy da się do nich zalogować i ew. eskalować uprawnienia, a także weryfikuje dostępność Python-a. W zasadzie, to wymaga on aby węzeł zdalny miał go zainstalowanego. Jeżeli wszystko przebiegnie prawidłowo, to w odpowiedzi domyślnie zwraca "pong". Moduł ten nie generuje żadnych pakietów ICMP.

[msleczek@vm0-net projekt_A]$ ansible all -m ping
10.8.232.122 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
10.8.232.123 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
10.8.232.121 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
10.8.232.124 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
[msleczek@vm0-net projekt_A]$


Do wykonania określonych zadań na hostach Ansible używa modułów. Kiedy korzystamy z poleceń doraźnych (ang. ad-hoc commands) i nie podamy nazwy modułu, to domyślnie zostanie wykorzystany moduł "command". Wykonuje on plik binarny na zdalnym węźle. Na jego działanie nie mają wpływu żadne zmienne środowiskowe powłoki (ang. shell). Nie korzysta on w ogóle z powłoki zdalnego systemu. Wykonuje tylko wskazany plik binarny z zadanymi argumentami i zwraca nam sformatowany po odpowiedniej interpretacji wynik jego wykonania. Przykłady jawnego i niejawnego wykorzystania modułu "command" widać poniżej:

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -m command -a "df -h"
10.8.232.123 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
devtmpfs 440M 0 440M 0% /dev
tmpfs 456M 0 456M 0% /dev/shm
tmpfs 456M 6.2M 450M 2% /run
tmpfs 456M 0 456M 0% /sys/fs/cgroup
/dev/mapper/rhel_vm3--net-root 21G 1.8G 20G 9% /
/dev/sda1 1014M 185M 830M 19% /boot
tmpfs 92M 0 92M 0% /run/user/1000
tmpfs 92M 0 92M 0% /run/user/1001
[msleczek@vm0-net projekt_A]$ ansible all -a "uptime"
10.8.232.121 | CHANGED | rc=0 >>
15:02:29 up 1 day, 20:38, 2 users, load average: 0.07, 0.02, 0.00
10.8.232.123 | CHANGED | rc=0 >>
15:02:14 up 1 day, 19:58, 2 users, load average: 0.34, 0.08, 0.03
10.8.232.122 | CHANGED | rc=0 >>
15:03:08 up 1 day, 20:15, 2 users, load average: 0.15, 0.03, 0.01
10.8.232.124 | CHANGED | rc=0 >>
15:01:25 up 1 day, 19:18, 2 users, load average: 0.00, 0.00, 0.00
[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -a "free -h"
10.8.232.123 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 911Mi 212Mi 249Mi 6.0Mi 450Mi 547Mi
Swap: 2.0Gi 0.0Ki 2.0Gi
[msleczek@vm0-net projekt_A]$


Kiedy po zalogowaniu się do systemu za pomocą SSH wpisujemy polecenia, wszystko jest najpierw interpretowane przez powłokę. To jest powodem dla którego, kiedy chcemy przekazać jako argument polecenia, któryś ze specjalnych znaków powłoki, jak m.in. "<", ">", "|", ";" czy "&", to musimy go poprzedzać znakiem backslash "\" lub użyć odpowiednich komentarzy. Moduł "command" nie korzysta z powłoki, stąd każdy ze specjalnych znaków powłoki zostanie przekazany wprost do polecenia jako argument. Zatem nie jest możliwe korzystanie z operatorów i potoków, jakie udostępnia powłoka. Widać to w przykładzie poniżej, gdzie zarówno "|", jak i "grep" oraz "sda1" zostały przekazane jako argumenty do polecenia "df". Kiedy podamy dodatkowe argumenty dla polecenia "df", to pokazuje ono informacje o systemach plików, na których znajdują się pliki będące zadanymi argumentami polecenia.

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -m command -a "df -h | grep sda1"
10.8.232.123 | FAILED | rc=1 >>
df: '|': No such file or directory
df: grep: No such file or directory
df: sda1: No such file or directorynon-zero return code
[msleczek@vm0-net projekt_A]$ df -h /boot /root
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 1014M 185M 830M 19% /boot
/dev/mapper/rhel_vm0--net-root 29G 2.0G 28G 7% /
[msleczek@vm0-net projekt_A]$

Aby lepiej i szybciej orientować się w tym co się wydarzyło oraz odpowiednio ukierunkować naszą uwagę, Ansible używa różnych kolorów w zwracanych wynikach. Są to m.in.:

  • "nie udało się wykonać zadania",
  • "udało się wykonać zadanie"
  • "nie trzeba było nic robić".

Moduł "command" również nie ma dostępu do żadnych zmiennych środowiskowych powłoki użytkownika, jak dla przykładu "$HOSTNAME", stąd jego wykonanie nie może na nich bazować. Ma to zaletę, którą jest brak negatywnego wpływy tych zmiennych i ustawień środowiskowych na wydawane polecenia. To jest też powodem, dla którego jest najbezpieczniejszym i zalecanym sposobem wydawania doraźnych poleceń.

W wielu miejscach bez żadnej weryfikacji piszą o tym, że dla modułu "command" nie będzie dostępna zmienna środowiskowa "$HOME". Akurat niektóre zmienne środowiskowe, jak "$HOME", "$SHELL" czy "$PATH", są ustawiane przez program "login" w systemie GNU/Linux i dostępne dla procesu "sshd" w trakcie nawiązywania sesji SSH. Jest do nich dostęp jeszcze zanim wykonamy jakiekolwiek polecenie czy aktywowana zostanie powłoka. Stąd moduł "command" ma do nich dostęp. Natomiast inne zmienne aktywowane są dopiero po aktywowaniu powłoki, jak dla przykładu "$HOSTNAME" i stąd nie są one dostępne dla modułu "command".

Tam gdzie potrzebne nam potoki, operatory lub zmienne środowiskowe powłoki, powinniśmy używać modułu "shell". Moduł ten domyślnie stosuje powłokę "/bin/sh", ale da się to zmienić. Jednocześnie warto pamiętać o tym, że wynik działania tego modułu może zależeć od wartości zmiennych środowiskowych powłoki. Zatem osoba modyfikująca pliki środowiskowe powłoki może celowo lub niecelowo przyczyni się do czegoś, co nie powinno się zdarzyć.

Dlatego do poleceń doraźnych zalecamy korzystanie z modułu "command" wszędzie tam, gdzie to możliwe.

Należy pamiętać o tym, że powłoka może być aktywowana na różne sposoby, a to ma wpływ na to w jaki sposób ustawiane są zmienne środowiskowe. Jeżeli w trakcie logowania dochodzi do aktywowania interaktywnej powłoki lub robimy to ręcznie wydając polecenie "bash --login" lub "bash -l", to wykonywany jest najpierw plik "/etc/profile", a potem pierwszy dostępny plik z listy o kolejności: "~/.bash_profile", "~/.bash_login" i "~/.profile". Dla powłoki interaktywnej, nie będącej powłoką logowania, jest wykonywany tylko plik "~/.bashrc". Natomiast jest jeszcze powłoka nieinteraktywna ("bash -c"), do której uzyskuje dostęp Ansible poprzez moduł "shell". Wykonuje ona przy starcie plik wskazany zmienną "$BASH_ENV". Zmienna ta jest domyślnie pusta, stąd dużo osób jest zdziwione, kiedy karze modułowi "shell" używać powłoki bash, a ona nie widzi zmiennych zdefiniowanych w swoich plikach środowiskowych.

Moduły "command" i "shell" wymagają posiadania Python-a na zarządzanym węźle. Nie zawsze jest to możliwe, chociaż większość systemów serwerowych posiada go już zaraz po instalacji systemu bazowego. Podobnie się to ma do rozsądnych urządzeń sieciowych. Niemniej, na pewno natrafimy na jakieś starsze urządzenia lub systemy, gdzie Python nie będzie dostępny, a jednocześnie dobrze byłoby nimi zarządzać tym samym narzędziem. Jest to możliwe dzięki modułowi "raw", który omija cały podsystem modułów Ansible. Wydaje on zadane polecenia wprost po nawiązaniu połączenia SSH, a następnie odsyła do nas wynik. Nie wykonuje żadnej próby interpretacji tego wyniku, w tym sprawdzenia błędów. Dostaniemy z powrotem cokolwiek zostanie wyrzucone na wyjściu STDERR i STDOUT.

Użycie modułu "shell" i "raw" dla naszego systemu dało niżej podobny wynik. W obu przypadkach zadane polecenie zostało wykonane w powłoce "/bin/sh". Więcej różnic powinno dać się zaobserwować w innych przykładach.

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -m shell -a "df -h | grep sda1"
10.8.232.123 | CHANGED | rc=0 >>
/dev/sda1 1014M 185M 830M 19% /boot
[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -m raw -a "df -h | grep sda1"
10.8.232.123 | CHANGED | rc=0 >>
/dev/sda1 1014M 185M 830M 19% /boot
Shared connection to 10.8.232.123 closed.

[msleczek@vm0-net projekt_A]$


Niektóre moduły Ansible wymagają podania dodatkowych argumentów, a niektóre nie. Jeżeli nie jesteśmy pewni tego w jaki sposób należy posługiwać się danym modułem, najlepiej posłużyć się poleceniem "ansible-doc". Przykład informacji jakie udostępnia dla zadanego modułu widać poniżej.

[msleczek@vm0-net projekt_A]$ ansible-doc ping
> PING (/usr/lib/python3.6/site-packages/ansible/modules/system/ping.py)

A trivial test module, this module always returns `pong' on successful contact.
It does not make sense in playbooks, but it is
useful from `/usr/bin/ansible'
to verify the ability to login and that a usable Python is configured. This
is NOT ICMP ping, this is
just a trivial test module that requires Python
on the remote-node. For Windows targets, use the [win_ping] module instead.
For
Network targets, use the [net_ping] module instead.

* This module is maintained by The Ansible Core Team
OPTIONS (= is mandatory):

- data
Data to return for the `ping' return value.
If this parameter is set to `crash', the module will cause an exception.
[Default: pong]
type: str


SEE ALSO:
* Module net_ping
The official documentation on the net_ping module.
https://docs.ansible.com/ansible/2.9/modules/net_ping_module.html
* Module win_ping
The official documentation on the win_ping module.
https://docs.ansible.com/ansible/2.9/modules/win_ping_module.html


AUTHOR: Ansible Core Team, Michael DeHaan
METADATA:
status:
- stableinterface
supported_by: core

EXAMPLES:

# Test we can logon to 'webservers' and execute python with json lib.
# ansible webservers -m ping

# Example from an Ansible Playbook
- ping:

# Induce an exception to see what happens
- ping:
data: crash


RETURN VALUES:

ping:
description: value provided with the data parameter
returned: success
type: str
sample: pong

[msleczek@vm0-net projekt_A]$


Zgodnie z "ansible-doc" dla modułu "ping", domyślną wartość "pong" da się zmienić za pomocą argumentu "data".

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.124 --module-name ping
10.8.232.124 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
[msleczek@vm0-net projekt_A]$ ansible 10.8.232.124 --module-name ping --args data=JESTEM
10.8.232.124 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "JESTEM"
}
[msleczek@vm0-net projekt_A]$

 

Powyżej używamy rozszerzonych argumentów, gdzie "-a" jest równoważne z "--args", a "-m" z "--module-name".

Listę obsługiwanych przez nasz system modułów i wtyczek do Ansible można sprawdzić za pomocą:

$ ansible-doc -l


Lista ta jest dość długa, ale można ją przeszukiwać, jak zostało to pokazane poniżej. Na liście widoczne są też własnoręcznie dodane moduły. Poniżej widać zrobiony przez nas moduł "cisco_webex", który umożliwia wysłanie wiadomości m.in. do pokoju w Cisco Webex Teams.

[msleczek@vm0-net projekt_A]$ ansible-doc -l | grep webex
cisco_webex Send a webexmsg to a Cisco Webex Teams Room or Individual
[msleczek@vm0-net projekt_A]$


Istnieje też możliwość wygenerowania przykładowego skrawka konfiguracja dla danego modułu, który posłużyć nam może jako wstęp do dalszej konfiguracji. Służy do tego polecenie:

$ ansible-doc -s <moduł>


Tutaj widzimy przykład dostępnych opcji dla naszego modułu "cisco_webex": 

[msleczek@vm0-net projekt_A]$ ansible-doc -s cisco_webex
- name: Send a webexmsg to a Cisco Webex Teams Room or Individual.
cisco_webex:
personal_token: # (required) Personal access token required to validate the Webex API.
recipient_id: # (required) The unique identifier associated with `recipient_type'.
recipient_type: # (required) Messages can be sent to a room or individual (by ID or E-Mail).
webexmsg: # (required) The webexmsg you would like to send.
webexmsg_type: # Specifies how you would like the webexmsg formatted.
[msleczek@vm0-net projekt_A]$

 

Zadania Ansible mogą być wykonywać doraźnie lub cyklicznie według ustalonego harmonogramu. Dzięki możliwości wysyłania wiadomości do Cisco Webex Teams możemy szybko powiadomić odpowiedni zespół na temat wszelkich niepowodzeń lub wykrytych niezgodności.


Zajmiemy się teraz kilkoma użytecznymi opcjami polecenia "ansible". W tym celu cofniemy się o jeden poziom w strukturze drzewa katalogowego, tak aby przestały działać nasze domyślne wartości ustawień z pliku "ansible.cfg".

[msleczek@vm0-net projekt_A]$ ansible --version | grep "config file"
config file = /home/msleczek/projekt_A/ansible.cfg
[msleczek@vm0-net projekt_A]$ cd ..
[msleczek@vm0-net ~]$ ansible --version | grep "config file"
config file = /home/msleczek/.ansible.cfg
[msleczek@vm0-net ~]$ ansible 10.8.232.124 -a whoami -i projekt_A/inventory
10.8.232.124 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: This email address is being protected from spambots. You need JavaScript enabled to view it.: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
"unreachable": true
}
[msleczek@vm0-net ~]$

 

Powyżej widać błąd, gdyż domyślnie Ansible stara się użyć w trakcie logowania do zdalnego hosta nazwy bieżącego użytkownika. Wcześniej w tym celu korzystaliśmy z użytkownika "ansible". Możemy go wskazać ręcznie za pomocą opcji "-u" lub "--user", co będzie miało nadrzędny priorytet.

[msleczek@vm0-net ~]$ ansible 10.8.232.124 -a whoami -i projekt_A/inventory -u ansible
10.8.232.124 | CHANGED | rc=0 >>
ansible
[msleczek@vm0-net ~]$ ansible 10.8.232.124 -a whoami -i projekt_A/inventory -u ansible -b
10.8.232.124 | CHANGED | rc=0 >>
root
[msleczek@vm0-net ~]$ ansible 10.8.232.124 -a whoami -i projekt_A/inventory --user ansible --become
10.8.232.124 | CHANGED | rc=0 >>
root
[msleczek@vm0-net ~]$


Po wskazaniu prawidłowego użytkownika widać, że nie dostajemy błędu i polecenie "whoami" wskazuje na "ansible". Kiedy dodamy opcję "-b" lub "--become", to polecenie "whoami" zostanie wydane dopiero po eskalacji uprawnień. W wyniku tego, w dwóch kolejnych przykładach powyżej, zwraca użytkownika "root".

W niektórych przypadkach bardziej czytelne lub użyteczne może być umieszczenie całego wyniku w jednym wierszu. Dokonać tego można za pomocą opcji "-o" lub "--one-line".

[msleczek@vm0-net projekt_A]$ ansible all -a "cat /etc/redhat-release"
10.8.232.122 | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.124 | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.123 | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.121 | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.2 (Ootpa)
[msleczek@vm0-net projekt_A]$ ansible all -a "cat /etc/redhat-release" --one-line
10.8.232.121 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.122 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.123 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.124 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
[msleczek@vm0-net projekt_A]$ ansible all -a "cat /etc/redhat-release" -o
10.8.232.121 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.124 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.123 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.122 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
[msleczek@vm0-net projekt_A]$

 


O ile stosowanie modułów "command", "shell" i "raw" jest łatwe i wygodne, to tam gdzie się da powinniśmy ich unikać. Docelowo powinniśmy stosować dedykowane i wyspecjalizowane do określonych zadań moduły, których celem jest doprowadzenie urządzeń, systemów, plików, aplikacji bądź usług do żądanego stanu, który opisywany jest w sposób deklaratywny (ang. declarative language). Oznacza to, że tylko określamy stan jaki chcemy by dany obiekt przyjął, bez opisywania w szczegółach w jaki sposób ma zostać to zrealizowane. Doprowadzeniem go do określonego stanu zajmują się już wyspecjalizowane moduły. W zależności od tego w jakim stanie początkowym jest dany obiekt, użyty moduł może wykonać na nim więcej lub mniej różnych operacji, czy nawet nie wykonać żadnej, jeżeli nie jest to potrzebne. Podejście takie charakteryzuje się idempotentnością (ang. idempotent), czyli możliwość wielokrotnego uruchomienia tego samego modułu bez zmiany stanu końcowego.

Oznacza to też to, że możemy bez obaw wielokrotnie wykonywać ten sam wyspecjalizowany moduł na danym obiekcie. Jeżeli jest on już w żądanym stanie, to nic się nie stanie, a jeżeli nie, to zostanie do niego doprowadzony.

Należy pamiętać o tym, że idempotentność jest zagwarantowana tylko jeżeli korzystamy z wyspecjalizowanych i wbudowanych modułów. Jeżeli tworzymy je sami lub korzystamy z innych możliwości, jak dla przykładu wysyłanie poleceń przy użyciu modułów "command", "shell" czy "raw", to o idempotentność musimy zadbać sami.


Ansible obsługuje bardzo dużą ilość wyspecjalizowanych modułów, a liczba ich ciągle rośnie. Ich pełna ilość dostępna jest w dokumentacji Ansible. Red Hat udostępnia też dodatkowe moduły i wtyczki w swoim Automation Hub. My też udostępniamy naszym klientom własne, jak wspomniany moduł "cisco_webex". Tutaj przyjrzyjmy się tylko kilku.

Zdarza się, że aktualizujemy cały plik zawierający listę serwerów NTP, stosowane klucze do uwierzytelnienia lub zmienne środowiskowe. Ansible nadaje się do tego idealnie. Zanim cokolwiek zrobi sprawdza czy plik istnieje i jego zawartość jest taka sama. Jeżeli tak, to nic nie robi. Jeżeli nie, to wykonuje stosowne operacje. Jest nawet w stanie wykonać kopię poprzedniej wersji pliku.

Załóżmy, że administrujemy bardzo dużą ilością serwerów i chcemy na nich wszystkich zaktualizować lub ujednolić zmienne środowiskowe. Na wszelki wypadek, chcemy też zrobić kopię poprzedniej wersji pliku z tymi zmiennymi tam, gdzie jego zawartość była różna.

[msleczek@vm0-net projekt_A]$ cat environment 
LANG=en_US.utf-8
LC_ALL=en_US.utf-8

[msleczek@vm0-net projekt_A]$ ansible all -m command -a 'cat /etc/environment' -o
10.8.232.122 | CHANGED | rc=0 | (stdout)
10.8.232.124 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.123 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.121 | CHANGED | rc=0 | (stdout)
[msleczek@vm0-net projekt_A]$ ansible all -m copy -a "src=./environment dest=/etc/environment owner=root group=root mode=0644 backup=yes"
10.8.232.124 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"checksum": "0fa89fe380b8dc91e6bb56679f04a8759d5500d6",
"dest": "/etc/environment",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/etc/environment",
"secontext": "system_u:object_r:etc_t:s0",
"size": 37,
"state": "file",
"uid": 0
}
10.8.232.123 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"checksum": "0fa89fe380b8dc91e6bb56679f04a8759d5500d6",
"dest": "/etc/environment",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"path": "/etc/environment",
"secontext": "system_u:object_r:etc_t:s0",
"size": 37,
"state": "file",
"uid": 0
}
10.8.232.121 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"backup_file": "/etc/environment.36908.2020-05-01@20:48:44~",
"changed": true,
"checksum": "0fa89fe380b8dc91e6bb56679f04a8759d5500d6",
"dest": "/etc/environment",
"gid": 0,
"group": "root",
"md5sum": "f14f07073c6735cc233511404a860097",
"mode": "0644",
"owner": "root",
"secontext": "system_u:object_r:etc_t:s0",
"size": 37,
"src": "/home/ansible/.ansible/tmp/ansible-tmp-1588358960.2073026-15260-256434093911547/source",
"state": "file",
"uid": 0
}
10.8.232.122 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"backup_file": "/etc/environment.13295.2020-05-01@20:49:22~",
"changed": true,
"checksum": "0fa89fe380b8dc91e6bb56679f04a8759d5500d6",
"dest": "/etc/environment",
"gid": 0,
"group": "root",
"md5sum": "f14f07073c6735cc233511404a860097",
"mode": "0644",
"owner": "root",
"secontext": "system_u:object_r:etc_t:s0",
"size": 37,
"src": "/home/ansible/.ansible/tmp/ansible-tmp-1588358960.2120671-15262-147935961544848/source",
"state": "file",
"uid": 0
}
[msleczek@vm0-net projekt_A]$ ansible all -m command -a 'cat /etc/environment' -o
10.8.232.122 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.121 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.123 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.124 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
[msleczek@vm0-net projekt_A]$


Widać powyżej, że na dwóch serwerach Ansible nie musiało nic robić. Na pozostałych dwóch doprowadziło zawartość żądanego pliku do odpowiedniej zawartości.

Zamiast wskazywać plik źródłowy podopcją "src", da się wprost zdefiniować zawartość jaką powinien posiadać plik. Służy do tego celu podopcja "content", której zastosowanie widać niżej.

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m copy -a 'content="\nSerwer obsługiwany przez networkers.pl\n\n" dest=/etc/motd'
10.8.232.121 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"checksum": "0bcc7021129a6c1564e9d26983b7ad52c710ec28",
"dest": "/etc/motd",
"gid": 0,
"group": "root",
"md5sum": "2fda36d59048989bd169842be2c843e1",
"mode": "0644",
"owner": "root",
"secontext": "system_u:object_r:etc_t:s0",
"size": 42,
"src": "/home/ansible/.ansible/tmp/ansible-tmp-1588359294.0033875-15420-226606317748867/source",
"state": "file",
"uid": 0
}
[msleczek@vm0-net projekt_A]$


Poniżej można zobaczyć co po tej zmianie widzi użytkownik logujący się do naszych serwerów:

ms-net:~ msleczek$ ssh -l msleczek 10.8.232.121
This email address is being protected from spambots. You need JavaScript enabled to view it.'s password:

Serwer obsługiwany przez networkers.pl

Last login: Fri May 1 15:37:10 2020 from 10.8.64.128
[msleczek@vm1-net ~]$


Załóżmy teraz, że chcemy utworzyć na wszystkich zarządzanych serwerach katalog o określonych uprawnieniach, gdzie będziemy w przyszłości wrzucali kopie zapasowe wybranych plików:

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m file -a "path=/root/backup state=directory mode=700"
10.8.232.121 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"gid": 0,
"group": "root",
"mode": "0700",
"owner": "root",
"path": "/root/backup",
"secontext": "unconfined_u:object_r:admin_home_t:s0",
"size": 6,
"state": "directory",
"uid": 0
}
[msleczek@vm0-net projekt_A]$


Dużo też zależy od naszej pomysłowości i znajomości tego czym administrujemy. Dla przykładu, tym samym modułem możemy wyłączyć pamiętanie historii wydawanych poleceń w bazie MySQL/MariaDB:

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m file -a "src=/dev/null dest=/root/.mysql_history state=link"
10.8.232.121 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": true,
"dest": "/root/.mysql_history",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"secontext": "unconfined_u:object_r:admin_home_t:s0",
"size": 9,
"src": "/dev/null",
"state": "link",
"uid": 0
}
[msleczek@vm0-net projekt_A]$


Na skutek wykonania dwóch powyższych zadań, na serwerze pojawił się katalog "backup" i dowiązanie symboliczne ".mysql_history" prowadzące do "/dev/null":

[root@vm1-net ~]# ls -ald .mysql_history backup
drwx------. 2 root root 6 May 1 20:58 backup
lrwxrwxrwx. 1 root root 9 May 1 20:59 .mysql_history -> /dev/null
[root@vm1-net ~]#


Na koniec zachęcamy zapoznanie się z modułem "setup", który zbiera i udostępnia bardzo dużą ilość informacji na temat każdego z zarządzanych węzłów. Informacji tych jest tak bardzo dużo, że nie umieszczamy ich w artykule. Za to odsyłamy do sprawdzenia tego samodzielnie. Posłużyć się można do tego poleceniem:

$ ansible all -m setup


Udostępniane przez moduł "setup" informacje można wygodnie filtrować i zawężać za pomocą odpowiednich podopcji. Najlepiej w tym celu zapoznać się z "ansible-doc" dla tego modułu. Poniżej został pokazany przykład filtra, który pozwala sprawdzić rodzinę systemu operacyjnego oraz typ procesora.

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m setup -a filter="ansible_os_family"
10.8.232.121 | SUCCESS => {
"ansible_facts": {
"ansible_os_family": "RedHat",
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}
[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m setup -a filter="ansible_processor"
10.8.232.121 | SUCCESS => {
"ansible_facts": {
"ansible_processor": [
"0",
"GenuineIntel",
"Intel Xeon E312xx (Sandy Bridge)"
],
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false
}
[msleczek@vm0-net projekt_A]$


Polecenia doraźne (łac. ad-hoc) tak samo łatwo wykonuje się na jednym, jak i setkach węzłów.


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.