Playbook中jinja2模板的使用

发布时间:2023年12月25日

本章主要介绍playbook中如何使用jinja2模板

  • 什么是jinja2模板
  • 在jinja2模板文件中写if判断语句
  • 在jinja2模板文件在写for循环语句
  • handlers的使用

可以使用copy模块把本地的一个文件拷贝到远端机器,下面再次复习一下

本章实验都在demo4文件夹下操作,跟之前一样把ansible.cf和hosts文件拷贝进去

[jin@rhel801 ~]$ cd demo4/
[jin@rhel801 demo4]$ ls
ansible.cfg  hosts
[jin@rhel801 demo4]$

练习1:用copy拷贝一个文件到db主机组

有一个文件aa.txt,内容如下

[jin@rhel801 demo4]$ cat aa.txt 
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: {{ansible_default_ipv4.address}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[jin@rhel801 demo4]$

这个文件中包含一个 fact变量 ansible_default_ipv4.address

写一个 playbook,内容如下

[jin@rhel801 demo4]$ cat 1.yml 
---
- hosts: db
  tasks:
  - name: 拷贝一个文件到远端主机
    copy: src=aa.txt dest=/opt/aa.txt
[jin@rhel801 demo4]$

运行此playbook

[jin@rhel801 demo4]$ ansible-playbook 1.yml 

PLAY [db] **********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [rhel802]
ok: [rhel803]

TASK [拷贝一个文件到远端主机] *************************************************************
ok: [rhel802]
changed: [rhel803]

PLAY RECAP *********************************************************************
rhel802                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
rhel803                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[jin@rhel801 demo4]$

现在已经把本地的aa.,txt拷贝到rhel802和 rhel803的/opt目录中了。下面查看这两台主机 上 /opt/aa.txt的内容

[jin@rhel801 demo4]$ ansible db -m shell -a "cat /opt/aa.txt"
rhel802 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: {{ansible_default_ipv4.address}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
rhel803 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: {{ansible_default_ipv4.address}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[jin@rhel801 demo4]$ 

可以看到,当用copy拷贝一个文件到远端机器时,如果这个文件中有变量,拷贝过去的文件中的变量并不会变成具体的值

如果希望文件考别过去之后,文件中的变量变成具体的值,那么就不能使用copy模块,而是要使用template模块了

练习2:修改1.yml的内容如下

[jin@rhel801 demo4]$ cat 1.yml 
---
- hosts: db
  tasks:
  - name: 拷贝一个文件到远端主机
    template: src=aa.txt dest=/opt/aa.txt
[jin@rhel801 demo4]$

与刚才相比,只是把copy换成了template。template模块的用法与copy模块一致,所以这里选项并没有变。运行此 playbook

[jin@rhel801 demo4]$ ansible-playbook 1.yml 

PLAY [db] **********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [rhel802]
ok: [rhel803]

TASK [拷贝一个文件到远端主机] *************************************************************
changed: [rhel802]
changed: [rhel803]

PLAY RECAP *********************************************************************
rhel802                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
rhel803                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[jin@rhel801 demo4]$ 

再次查看两台主机上/opt/aa.txt的内容

[jin@rhel801 demo4]$ ansible db -m shell -a "cat /opt/aa.txt"
rhel802 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: 192.168.161.17
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
rhel803 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: 192.168.161.18
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[jin@rhel801 demo4]$

可以看到,通过template拷贝含有变量的文件时,拷贝到远端机器之后,文件中的变量 会变成具体的值

这个通过template拷贝的、含有变量的文件我们称为jinja2模板,jinja2模板文件的后缀 一般使用j2,这不是必需的,但是建议使用j2作为后缀

所以,需要修改aa.txt的文件为aa.j2

[jin@rhel801 demo4]$ mv aa.txt aa.j2

同时修改1.yml中对应的内容

[jin@rhel801 demo4]$ cat 1.yml 
---
- hosts: db
  tasks:
  - name: 拷贝一个文件到远端主机
    template: src=aa.j2 dest=/opt/aa.txt
[jin@rhel801 demo4]$

这里如果jinja2模板文件没有写路径,例如,例子中 src=aa.j2的aa.j2没有写路径,则优先到当前目录的templates 中找aa.j2,如果没有,则到当前目录中找aa.j2

练习3:验证,命令如下

[jin@rhel801 demo4]$ mkdir templates

在templates目录中创建aa.j2,内容如下

[jin@rhel801 templates]$ cat aa.j2 
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的主机名是: {{ansible_fqdn}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+

[jin@rhel801 templates]$

这样我们就有两个aa.j2了,还有一个是当前目录下的aa.j2

再次运行此playbook

[jin@rhel801 demo4]$ ansible-playbook 1.yml

查看两台主机上/opt/aa.txt 的内容

[jin@rhel801 demo4]$ ansible db -m shell -a "cat /opt/aa.txt"
rhel803 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的主机名是: rhel803
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
rhel802 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的主机名是: rhel802
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[jin@rhel801 demo4]$

里可以看到显示的主机名,所以是 templates目录中的aa.j2生效了

1.1 if判断

在jinja2模板文件中,我们也是可以使用if判断语句的,语法格式如下

1 {% if 判断1 %}
2 内容1
3 {% elif 判断2 %}
4 内容2
5 ...多个elif...
6 {% else %}
7 内容3
8 {% endif %}

注意

(1)“%”两边有没有空格都可以,不过所有的“%”前后空格要保持一致,即要有都有,要没有都没有

(2)if和elif中的内容如果太长了,可以另起一行写

如果判断1成立,则打印内容1,后面的条件不再判断,直接跳转到endif后面的内容;如果判断1不成立,则执行elif后面的判断2,如果成立则打印内容2,后面的条件不再判断,直接跳转到endif后面的内容。以此类推,如果所有的f和elif都不成立,则打印else中的内容

(3)elif和 else不是必需的

练习:写一个 jinja2模板文件,内容如下

[jin@rhel801 templates]$ cat bb.j2 
1111
{% if ansible_fqdn=="rhel802" %}
  {{ansible_fqdn}}
{% else %}
  aaaa
{% endif %}
  3333
[jin@rhel801 templates]$

这里jinja2模板所生成的文件一共会产生3行内容,第一行的1111和第三行的3333是必打印出来的,第二行的内容具体是什么要看情况。如果在server2上执行则显示主机名,如果在其他机器上执行则显示aaaa

写一个playbook

[jin@rhel801 demo4]$ cat 2.yml 
---
- hosts: db
  tasks:
  - name: 拷贝一个文件过去
    template: src=bb.j2 dest=/opt/bb.conf
[jin@rhel801 demo4]$

这里是把templates/bb.j2拷贝到两台机器的/opt中并命名为bb.conf,运行此 playbook

[jin@rhel801 demo4]$ ansible-playbook 2.yml 

PLAY [db] **********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [rhel803]
ok: [rhel802]

TASK [拷贝一个文件过去] ****************************************************************
changed: [rhel802]
changed: [rhel803]

PLAY RECAP *********************************************************************
rhel802                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
rhel803                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[jin@rhel801 demo4]$

查看两台机器上/opt/bb.conf的内容

[jin@rhel801 demo4]$ ansible db -m shell -a "cat /opt/bb.conf"
rhel802 | CHANGED | rc=0 >>
1111
  rhel802
  3333
rhel803 | CHANGED | rc=0 >>
1111
  aaaa
  3333
[jin@rhel801 demo4]$

可以看到,rhel802的/opt/bb.conf的第二行显示的是主机名,rhel803的/opt/bb.conf 的第二行显示的是 aaaa

在if和elif后面是可以写多个判断的,用or或and作为连接符,语法如下

判断1 or 判断11:判断1和判断11只要有一个成立就算成立,只有全部不成立才不算成立

判断1 and 判断11:判断1和判断11只有全部成立才算成立,只要有一个不成立就算不成立

查看下面的jinja2模板文件

[jin@rhel801 templates]$ cat cc.j2 
1111
{% if ansible_fqdn=="rhel802" and ansible_distribution_major_version=="7" %}
 {{ansible_fqdn}}
{% else %}
 aaaa
{% endif %}
 3333
[jin@rhel801 templates]$ 

这里jinja2模板会打印3行内容,第一行和第三行的内容是固定的,为1111和3333。第二行的内容是什么,要看是否满足条件,这里判断被管理主机名为rhel802及系统主版本号为7,二者都要满足,第二行才会显示主机名,否则显示 aaaa。需要注意的是,这里if判断语句太长,特意写成了3行也是没问题的

写一个playbook

[jin@rhel801 demo4]$ cat 3.yml 
---
- hosts: db
  tasks: 
  - name: 我要拷贝一个文件过去
    template: src=cc.j2 dest=/opt/cc.conf
[jin@rhel801 demo4]$

运行此playbook

[jin@rhel801 demo4]$ ansible-playbook 3.yml 

PLAY [db] **********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [rhel802]
ok: [rhel803]

TASK [我要拷贝一个文件过去] **************************************************************
changed: [rhel802]
changed: [rhel803]

PLAY RECAP *********************************************************************
rhel802                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
rhel803                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[jin@rhel801 demo4]$

查看两台机器上/opt/cc.conf的内容

[jin@rhel801 demo4]$ ansible db -m shell -a "cat /opt/cc.conf"
rhel802 | CHANGED | rc=0 >>
1111
  aaaa
  3333
rhel803 | CHANGED | rc=0 >>
1111
  aaaa
  3333
[jin@rhel801 demo4]$

1.2 for循环

一个列表中有多个元素,如果需要依次对列表中的每个元素操作,则可以使用for循环来实现,for 循环的语法如下

1 {% for i in 列表名 %}
2 {{i}}
3 {% endfor %}

这里首先把列表中的第一个元素赋值给i,执行中间的操作;然后把第二个元素赋值给i执行中间的操作,以此类推,直到把最后一个元素赋值给i。看下面的例子

[jin@rhel801 templates]$ cat dd.conf.j2 
{% set list1=['aa','bb','cc'] %}
 1111
{% for i in list1 %}
 {{i}}
{% endfor %}
 5555
[jin@rhel801 templates]$

这里手动在jinja2模板中定义了一个列表(注意定义列表的方式)list1,里面有3个元素,分别为aa、bb、cc。然后对这个列表的内容进行循环

这里jinja2模板生成的文件有5行内容,第1行和第5行的内容是固定的,为1111和5555。 第2~4行是循环列表list1 中的值,为aa、bb、cc

写一个 playbook,内容如下

[jin@rhel801 demo4]$ cat 4.yml 
---
- hosts: rhel802
  tasks:
  - name: 拷贝一个文件到远端主机
    template: src=dd.conf.j2 dest=/opt/dd.conf
[jin@rhel801 demo4]$

运行此playbook,内容如下

[jin@rhel801 demo4]$ ansible-playbook 4.yml 

PLAY [rhel802] *****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [rhel802]

TASK [拷贝一个文件到远端主机] *************************************************************
changed: [rhel802]

PLAY RECAP *********************************************************************
rhel802                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[jin@rhel801 demo4]$

查看rhel802 上/opt/dd.conf的内容

[jin@rhel801 demo4]$ ansible rhel802 -m shell -a "cat /opt/dd.conf"
rhel802 | CHANGED | rc=0 >>
 1111
 aa
 bb
 cc
 5555
[jin@rhel801 demo4]$

除了jinja2模板中手动定义的列表,一般情况下,我们会在playbook中定义列表,然后对列表中的元素进行循环

练习:写一个变量文件users_list.txt,里面包含一个名称为users的列表

[jin@rhel801 demo4]$ cat users_list.txt 
users:
- uname: tom
  age: 20
  sex: man

- uname: bob
  age: 21
  sex: man

- uname: mary
  age: 22
  sex: woman

- uname: wangwu
  age: 23
  sex: man

[jin@rhel801 demo4]$

在 templates目录下写一个ee.j2,里面写一个 for 语句循环users列表

[jin@rhel801 templates]$ cat ee.j2 
现在公司中所有员工姓名是:
{% for i in users %}
 {{i.uname}}
{% endfor %}

[jin@rhel801 templates]$

循环每个元素时,只打印元素中的uname变量。写一个名称为5.yaml的playbook,加载变量文件 users list.txt

[jin@rhel801 demo4]$ cat 5.yml 
---
- hosts: rhel802
  vars_files:
  ‐ users_list.txt
  tasks:
  - name: 拷贝一个文件到远端主机
    template: src=ee.j2 dest=/opt/ee.conf
[jin@rhel801 demo4]$

这里通过 template模块把ee.j2拷贝到被管理主机的/opt 中并命名为ee.conf。查看 rhel802上/opt/ee.conf的内容

[jin@rhel801 demo4]$ ansible-playbook 5.yml 

PLAY [rhel802] *****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [rhel802]

TASK [拷贝一个文件到远端主机] *************************************************************
changed: [rhel802]

PLAY RECAP *********************************************************************
rhel802                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[jin@rhel801 demo4]$

查看被管理主机的/opt/ee.conf,里面包括users列表中所有的用户名

[jin@rhel801 demo4]$ ansible rhel802 -m shell -a "cat /opt/ee.conf"
rhel802 | CHANGED | rc=0 >>
现在公司中所有员工姓名是:
 tom
 bob
 mary
 wangwu
[jin@rhel801 demo4]$

3.3 handlers

前面讲了模板的使用,但是后期我们可能需要修改模板的内容,然后重新拷贝到各个机器,此时需要重启httpd服务才会生效,先看下面的例子

先获取 httpd.conf的配置文件,获取的httpd.conf中没有任何空白行和注释行

[jin@rhel801 ~]$ egrep -v '#|^#' /etc/httpd/conf/httpd.conf > httpd.conf.j2

修改此httpd.conf.j2的第三行,把原来Listen后面的端口80换成{{myport}},让 httpd.confj2引用myport变量,内容如下

[jin@rhel801 demo4]$ head -4 httpd.conf.j2 

ServerRoot "/etc/httpd"

Listen {{myport}}
[jin@rhel801 demo4]$

为了不让例子变得太复杂,先关闭?SELinux("/etc/selinux/config")

写一个名称为hand-1.yml的playbook

[jin@rhel801 demo4]$ cat hand-1.yml 
---
- hosts: rhel802
  vars:
    myport: 80
  tasks: 
  - name: task1安装httpd
    yum: name=httpd state=installed
  - name: task2拷贝配置文件
    template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
  - name: task3启动httpd服务
    service: name=httpd state=started
[jin@rhel801 demo4]$

第一个task用于安装 htpd,第二个task用于把模板httpd.conf.j2拷贝到被管理机器,第三个 task用于启动httpd服务,第一次是可以正常运行的

下面修改myport的值为80

[jin@rhel801 demo4]$ ansible-playbook hand-1.yml 

PLAY [rhel802] *****************************************************************

TASK [Gathering Facts] *********************************************************
ok: [rhel802]

TASK [task1安装httpd] ************************************************************
ok: [rhel802]

TASK [task2拷贝配置文件] *************************************************************
changed: [rhel802]

TASK [task3启动httpd服务] **********************************************************
changed: [rhel802]

PLAY RECAP *********************************************************************
rhel802                    : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[jin@rhel801 demo4]$

启动成功?

文章来源:https://blog.csdn.net/m0_73823239/article/details/135192463
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。