ИТ Блог. Администрирование серверов на основе Linux (Ubuntu, Debian, CentOS, openSUSE)

Ansible. Шаблоны Jinja2

Начало работы с Ansible

В предыдущей статье по принятию решений в Ansible вы узнали, как вносить простые изменения в файлы с помощью блоков blockinfile или встроенных модулей Ansible.

В этой статье вы узнаете, как использовать механизм шаблонов Jinja2 для выполнения более сложных и динамических изменений файлов.

Вы узнаете, как получить доступ к переменным и фактам в шаблонах Jinja2. Кроме того, вы узнаете, как использовать условные операторы и структуры циклов в Jinja2.

 

Доступ к переменным в Jinja2

Ansible будет искать файлы шаблонов jinja2 в каталоге вашего проекта или в каталоге с именем templates в каталоге вашего проекта.

Давайте создадим каталог шаблонов, чтобы все было чище и организовано:

[andreyex@control plays]$ mkdir templates
[andreyex@control plays]$ cd templates/

 

Теперь создайте свой первый шаблон Jinja2 с именем index.j2 :

[andreyex@control templates]$ cat index.j2 
A message from {{ inventory_hostname }}
{{ webserver_message }}

 

Обратите внимание, что имена файлов шаблонов Jinja2 должны заканчиваться расширением .j2.

Inventory_hostname еще один встроенный Ansible ( так называемый специальной или волшебный). Webserver_message является переменной, что вы будете определять в вашем PlayBook.

Теперь вернитесь на шаг назад в каталог вашего проекта и создайте следующий файл check-apache.yml :

[andreyex@control plays]$ cat check-apache.yml 
---
- name: Check if Apache is Working
  hosts: webservers
  vars:
    webserver_message: "I am running to the finish line."
  tasks:
    - name: Start httpd
      service:
        name: httpd
        state: started

    - name: Create index.html using Jinja2
      template:
        src: index.j2
        dest: /var/www/html/index.html

 

Обратите внимание, что пакет httpd уже был установлен в предыдущем руководстве.

В этом руководстве вы сначала убедитесь, что Apache запущен в первой задаче Start httpd. Затем используйте модуль шаблона во второй задаче Create index.html с помощью процесса Jinja2to и перенесите созданный вами файл шаблона index.j2 Jinja2 в место назначения /var/www/html/index.html.

Идите вперед и запустите playbook:

[andreyex@control plays]$ ansible-playbook check-apache.yml 

PLAY [Check if Apache is Working] **********************************************

TASK [Gathering Facts] *********************************************************
ok: [node3]
ok: [node2]

TASK [Start httpd] *************************************************************
ok: [node2]
ok: [node3]

TASK [Create index.html using Jinja2] ******************************************
changed: [node3]
changed: [node2]

PLAY RECAP *********************************************************************
node2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0

 

Пока все выглядит хорошо; давайте запустим быструю специальную команду Ansible, чтобы проверить содержимое index.html на узлах веб-серверов:

[andreyex@control plays]$ ansible webservers -m command -a "cat /var/www/html/index.html"
node3 | CHANGED | rc=0 >>
A message from node3
I am running to the finish line.
node2 | CHANGED | rc=0 >>
A message from node2
I am running to the finish line.

 

Удивительно! Обратите внимание, как Jinja2 смог получить значения встроенной переменной inventory_hostname и переменной webserver_message в вашем playbook.

Вы также можете использовать команду curl, чтобы узнать , получили ли вы ответ от обоих веб-серверов:

[andreyex@control plays]$ curl node2.andreyexru.local
A message from node2
I am running to the finish line.
[andreyex@control plays]$ curl node3.andreyexru.local
A message from node3
I am running to the finish line.

Доступ к фактам в Jinja2

Вы можете получить доступ к фактам в шаблонах Jinja2 так же, как вы получаете доступ к фактам из своей книги.

Для демонстрации перейдите в каталог шаблонов и создайте файл info.j2 Jinja2 со следующим содержимым:

[andreyex@control templates]$ cat info.j2 
Server Information Summary
--------------------------

hostname={{ ansible_facts['hostname'] }}
fqdn={{ ansible_facts['fqdn'] }}
ipaddr={{ ansible_facts['default_ipv4']['address'] }}
distro={{ ansible_facts['distribution'] }}
distro_version={{ ansible_facts['distribution_version'] }}
nameservers={{ ansible_facts['dns']['nameservers'] }}
totalmem={{ ansible_facts['memtotal_mb'] }}
freemem={{ ansible_facts['memfree_mb'] }}

 

Обратите внимание, что info.j2 имеет доступ к восьми различным фактам. Теперь вернитесь в каталог своего проекта и создайте следующий playbook server-info.yml :

[andreyex@control plays]$ cat server-info.yml 
---
- name: Server Information Summary
  hosts: all
  tasks:
   - name: Create server-info.txt using Jinja2
     template:
       src: info.j2
       dest: /tmp/server-info.txt

 

Обратите внимание, что вы создаете /tmp/server-info.txt на всех хостах на основе файла шаблона info.j2. Идите вперед и запустите playbook:

[andreyex@control plays]$ ansible-playbook server-info.yml 

PLAY [Server Information Summary] *******************************************

TASK [Gathering Facts] **********************************
ok: [node4]
ok: [node1]
ok: [node3]
ok: [node2]

TASK [Create server-info.txt using Jinja2] ********
changed: [node4]
changed: [node1]
changed: [node3]
changed: [node2]

PLAY RECAP *************************
node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node2                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node4                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0

 

Все хорошо! Теперь давайте запустим быструю специальную команду, чтобы проверить содержимое файла /tmp/server-info.txt на одном из узлов:

[andreyex@control plays]$ ansible node1 -m command -a "cat /tmp/server-info.txt"
node1 | CHANGED | rc=0 >>
Server Information Summary
--------------------------

hostname=node1
fqdn=node1.andreyexru.local
ipaddr=10.0.0.5
distro=CentOS
distro_version=8.2
nameservers=['168.63.129.16']
totalmem=1896
freemem=1087

 

Как видите, Jinja2 смог получить доступ и обработать все факты.

 

Условные операторы в Jinja2

Вы можете использовать условный оператор if в Jinja2 для тестирования различных условий и сравнения переменных. Это позволяет определить последовательность выполнения шаблона файла в соответствии с условиями тестирования.

Для демонстрации перейдите в каталог шаблонов и создайте следующий шаблон selinux.j2 :

[andreyex@control templates]$ cat selinux.j2 
{% set selinux_status = ansible_facts['selinux']['status'] %}

{% if selinux_status == "enabled" %}
	"SELINUX IS ENABLED"
{% elif selinux_status == "disabled" %}
	"SELINUX IS DISABLED"
{% else %}
	"SELINUX IS NOT AVAILABLE"
{% endif %}

 

Первый оператор в шаблоне создает новую переменную, для которой selinux_status установлено значение ansible_facts[‘selinux’][‘status’].

Затем вы можете использовать selinux_status в вашем, если условия испытания, чтобы определить, является ли SELinux включен, отключен или не установлен. В каждом из трех разных случаев вы отображаете сообщение, отражающее статус Selinux.

Обратите внимание, как если утверждение в jinja2 мимике Python, заявление if; только не забывайте использовать {% endif %}.

Теперь вернитесь в каталог своего проекта и создайте следующую playbook в selinux-status.yml :

[andreyex@control plays]$ cat selinux-status.yml 
---
- name: Check SELinux Status
  hosts: all
  tasks:
    - name: Display SELinux Status
      debug:
        msg: "{{ ansible_facts['selinux']['status'] }}"

    - name: Create selinux.out using Jinja2
      template:
        src: selinux.j2
        dest: /tmp/selinux.out

 

Идите вперед и запустите playbook:

[andreyex@control plays]$ ansible-playbook selinux-status.yml 

PLAY [Check SELinux Status] ****************************************************

TASK [Gathering Facts] *********************************************************
ok: [node4]
ok: [node2]
ok: [node3]
ok: [node1]

TASK [Display SELinux Status] **************************************************
ok: [node1] => {
    "msg": "enabled"
}
ok: [node2] => {
    "msg": "disabled"
}
ok: [node3] => {
    "msg": "enabled"
}
ok: [node4] => {
    "msg": "Missing selinux Python library"
}

TASK [Create selinux.out using Jinja2] *****************************************
changed: [node4]
changed: [node1]
changed: [node3]
changed: [node2]

PLAY RECAP *********************************************************************
node1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    node4                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0

 

Из вывода playbook; вы можете видеть, что SELinux включен как на node1, так и на node3. Мы отключили SELinux на node2 перед запуском playbook, а на node4 не установлен SELinux, потому что Ubuntu использует AppArmor вместо SELinux.

Наконец, вы можете запустить следующую специальную команду для проверки содержимого selinux.out на всех управляемых хостах:

[andreyex@control plays]$ ansible all -m command -a "cat /tmp/selinux.out"
node4 | CHANGED | rc=0 >>

	"SELINUX IS NOT AVAILABLE"
 
node2 | CHANGED | rc=0 >>

	"SELINUX IS DISABLED"
 
node3 | CHANGED | rc=0 >>

	"SELINUX IS ENABLED"
 
node1 | CHANGED | rc=0 >>

	"SELINUX IS ENABLED"

Цикл в Jinja2

Вы можете использовать оператор for в Jinja2 для перебора элементов в списке, диапазоне и т. д. Например, следующий цикл for будет перебирать числа в диапазоне (1,11) и, следовательно, будет отображать числа от 1 до 10:

{% for i in range(1,11) %}
	Number {{ i }}
{% endfor %}

 

Обратите внимание, как цикл for в Jinja2 имитирует синтаксис цикла for Python; снова не забудьте завершить цикл с помощью {% endfor %}.

Теперь давайте создадим полный пример, демонстрирующий возможности циклов for в Jinja2. Перейдите в каталог шаблонов и создайте следующий файл шаблона hosts.j2:

[andreyex@control templates]$ cat hosts.j2 
{% for host in groups['all'] %}
{{ hostvars[host].ansible_facts.default_ipv4.address }}  {{ hostvars[host].ansible_facts.fqdn }}  {{ hostvars[host].ansible_facts.hostname }}
{% endfor %}

 

Обратите внимание, что здесь вы использовали новую встроенную специальную (магическую) переменную hostvars, которая в основном представляет собой словарь, содержащий все хосты в инвентаре и назначенные им переменные.

Вы перебрали все хосты в своем инвентаре, а затем для каждого хоста; вы отобразили значение трех переменных:

  1. {{hostvars [host] .ansible_facts.default_ipv4.address}}
  2. {{hostvars [host] .ansible_facts.fqdn}}
  3. {{hostvars [host] .ansible_facts.hostname}}

 

Также обратите внимание, что вы должны включить эти три переменные в одну строку рядом, чтобы соответствовать формату файла /etc/hosts.

Теперь вернитесь в каталог ваших проектов и создайте следующий playbook local-dns.yml:

[andreyex@control plays]$ cat local-dns.yml 
---
- name: Dynamically Update /etc/hosts File
  hosts: all
  tasks:
    - name: Update /etc/hosts using Jinja2
      template:
        src: hosts.j2
        dest: /etc/hosts

 

Тогда продолжайте и запустите playbook:

[andreyex@control plays]$ ansible-playbook local-dns.yml 

PLAY [Dynamically Update /etc/hosts File] *********************************************

TASK [Gathering Facts] ***************************
ok: [node4]
ok: [node2]
ok: [node1]
ok: [node3]

TASK [Update /etc/hosts using Jinja2] ***********************************************
changed: [node4]
changed: [node3]
changed: [node1]
changed: [node2]

PLAY RECAP **********************
node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node2                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node4                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0

 

Пока все выглядит хорошо; теперь выполните следующую специальную команду, чтобы убедиться, что файл /etc/hosts правильно обновлен на узле node1:

[andreyex@control plays]$ ansible node1 -m command -a "cat /etc/hosts"
node1 | CHANGED | rc=0 >>
10.0.0.5  node1.andreyexru.local  node1
10.0.0.6  node2.andreyexru.local  node2
10.0.0.7  node3.andreyexru.local  node3
10.0.0.8  node4.andreyexru.local  node4

 

Отлично! Выглядит правильно отформатированным, как вы и ожидали.

Надеюсь, теперь вы осознали всю мощь шаблонов Jinja2 в Ansible. Следите за обновлениями, и вы увидите следующее руководство, в котором вы научитесь защищать конфиденциальную информацию и файлы с помощью Ansible Vault.

Exit mobile version