本章主要介绍在playbook中如何使用jinja2模板
- 神马是jinja2模板
- 在jinja2模板文件中写if判断语句
- 在jina2模板文件中写for循环语句
可以使用copy模块把本地的一个文件拷贝到远端机器,下面再次复习一下。
本章实验都在/home/lduan/demo4下操作,先把demo4目录创建出来并把ansible.cfg和hosts拷贝进去,命令如下。
[blab@node01 ~]$ mkdir demo4
[blab@node01 ~]$ cp ansible.cfg hosts demo4/
[blab@node01 ~]$ cd demo4/
[blab@node01 demo4]$
练习1:用copy拷贝一个文件到db主机组。
有一个文件aa.txt,内容如下。
[blab@node01 demo4]$ cat aa.txt
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: {{ansible_default_ipv4.address}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[blab@node01 demo4]$
这个文件中包含一个 fact变量 ansible_default_ipv4.address。
写一个 playbook,内容如下。
[blab@node01 demo4]$ cat 1.yml
---
- hosts: db
tasks:
- name: 拷贝一个文件到远端主机
copy: src=aa.txt dest=/opt/aa.txt
[blab@node01 demo4]$
运行此playbook,命令如下。
[blab@node01 demo4]$ ansible-playbook 1.yml
PLAY [db] **********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [node03]
ok: [node02]
TASK [拷贝一个文件到远端主机] *************************************************************
changed: [node03]
changed: [node02]
PLAY RECAP *********************************************************************
node02 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node03 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[blab@node01 demo4]$
现在已经把本地的aa.,txt拷贝到node02和node03的/opt目录中了。下面查看这两台主机上 /opt/aa.txt的内容
[blab@node01 demo4]$ ansible db -m shell -a "cat /opt/aa.txt"
node03 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: {{ansible_default_ipv4.address}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
node02 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: {{ansible_default_ipv4.address}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[blab@node01 demo4]$
可以看到,当用copy拷贝一个文件到远端机器时,如果这个文件中有变量,拷贝过去的文件的变量并不会变成具体的值。
如果希望文件拷贝过去之后,文件中的变量变成具体的值,那么就不能用copy模块,而是要使用template模块了
练习2:修改1.yml的内容如下
[blab@node01 demo4]$ cat 1.yml
---
- hosts: db
tasks:
- name: 拷贝一个文件到远端主机
template: src=aa.txt dest=/opt/aa.txt
[blab@node01 demo4]$
与刚才相比,只是把copy换成了template。template模块的用法与copy模块一致,所以这里选项并没有变。运行此 playbook,命令如下。
[blab@node01 demo4]$ ansible-playbook 1.yml
PLAY [db] **********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [node03]
ok: [node02]
TASK [拷贝一个文件到远端主机] *************************************************************
changed: [node03]
changed: [node02]
PLAY RECAP *********************************************************************
node02 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node03 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[blab@node01 demo4]$
再次查看两台主机上/opt/aa.txt的内容
[blab@node01 demo4]$ ansible db -m shell -a "cat /opt/aa.txt"
node03 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: 192.168.182.210
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
node02 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: 192.168.182.193
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[blab@node01 demo4]$ cat 1.yml
---
- hosts: db
tasks:
- name: 拷贝一个文件到远端主机
template: src=aa.txt dest=/opt/aa.txt
[blab@node01 demo4]$
可以看到,通过template拷贝含有变量的文件时,拷贝到远端机器之后,文件中的变量会变成具体的值
这个通过template拷贝的,含有变量的文件我们称为jinja2模板,jinja2模板文件的后缀一般使用 j2 ,这不是必须的,但是建议使用j2作为后缀
所以,需要修改aa.txt 的文件为aa.j2
[blab@node01 demo4]$ mv aa.txt aa.j2
同时修改1.yml中对应的内容
[blab@node01 demo4]$ cat 1.yml
---
- hosts: db
tasks:
- name: 拷贝一个文件到远端主机
template: src=aa.j2 dest=/opt/aa.j2 //修改后缀名
[blab@node01 demo4]$
这里如果jinja2模板文件没有写路径,例如,例子中 src=aa.j2的aa.j2没有写路径,则优先到当前目录的templates 中找aa.j2,如果没有,则到当前目录中找aa.j2.
练习3:验证
[blab@node01 demo4]$ mkdir templates
在templates目录中创建aa.j2
[blab@node01 demo4]$ cat template/aa.j2
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的主机名是: {{ansible_fqdn}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[blab@node01 demo4]$
这样我们就有两个aa.j2了,还有一个是当前目录下的aa.j2
[blab@node01 demo4]$ cat aa.j2
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的 IP 地址是: {{ansible_default_ipv4.address}}
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[blab@node01 demo4]$
再次运行此playbook
[blab@node01 demo4]$ ansible-playbook 1.yml
PLAY [db] **********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [node03]
ok: [node02]
TASK [拷贝一个文件到远端主机] *************************************************************
changed: [node03]
changed: [node02]
PLAY RECAP *********************************************************************
node02 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node03 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[blab@node01 demo4]$
查看两台主机上/opt/aa.txt 的内容
[blab@node01 demo4]$ ansible db -m shell -a "cat /opt/aa.j2"
node03 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的主机名是: node03
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
node02 | CHANGED | rc=0 >>
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
| 我的主机名是: node02
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
[blab@node01 demo4]$
这里可以看到显示的主机名,所以是 templates目录中的aa.j2生效了。
1.if判断
在jinja2模板文件中,我们也是可以使用if判断语句的,语法格式如下。
{% if 判断1 %}
内容1
{% elif 判断2 %}
内容2
...多个elif...
{% else %}
内容3
{% endif %}
注意:
- "%"两边有没有空格都可以,不过所有的"%"前后空格要保持一致,即要有都有,要没有都没有。
2. if和elif中的内容如果太长了,可以另起一行写。
如果判断1成立,则打印内容1,后面的条件不再判断,直接跳转到endif后面的内容;如果判断1不成立,则执行elif后面的判断2,如果成立则打印内容2,后面的条件不再判断,直接跳转到endif后面的内容。以此类推,如果所有的f和elif都不成立,则打印else中的内容。 - elif和 else不是必需的。
练习:写一个 jinja2模板文件
[blab@node01 demo4]$ cat templates/bb.j2
1111
{% if ansible_fqdn=="node02" %}
{{ansible_fqdn}}
{% else %}
aaaa
{% endif %}
3333
[blab@node01 demo4]$
这里jinja2模板所生成的文件一共会产生3行内容,第一行的1111和第三行的3333是必打印出来的,第二行的内容具体是什么要看情况。如果在node02上执行则显示主机名,如果在其他机器上执行则显示aaaa。
写一个playbook
[blab@node01 demo4]$ cat 2.yml
---
- hosts: db
tasks:
- name: 拷贝一个文件到远端
template: src=bb.j2 dest=/opt/bb.conf
[blab@node01 demo4]$
这里是把templates/bb.j2拷贝到两台机器的/opt中并命名为bb.conf。运行此playbook,命令如下。
[blab@node01 demo4]$ ansible-playbook 2.yml
PLAY [db] **********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [node03]
ok: [node02]
TASK [拷贝一个文件到远端] ***************************************************************
changed: [node03]
changed: [node02]
PLAY RECAP *********************************************************************
node02 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node03 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[blab@node01 demo4]$
查看两台机器上/opt/bb.conf的内容
[blab@node01 demo4]$ ansible db -m shell -a "cat /opt/bb.conf"
node03 | CHANGED | rc=0 >>
1111
aaaa
3333
node02 | CHANGED | rc=0 >>
1111
node02
3333
[blab@node01 demo4]$
可以看到,node02的/opt/bb.conf的第二行显示的是主机名,node03的/opt/bb.conf的第二行显示的是 aaaa。
在if和 elif后面是可以写多个判断的,用or或and作为连接符,语法如下:
- 判断1 or 判断11:判断1和判断11只要有一个成立就算成立,只有全部不成立才算不成立。
- 判断1 and 判断11:判断1和判断11只有全部成立才算成立,只要有一个不成立就算不成立
查看下面的 jinja2模板文件
[blab@node01 demo4]$ cat templates/cc.j2
1111
{% if ansible_fqdn=="node02"
and
ansible_distribution_major_version=="7" %}
{{ansible_fqdn}}
{% else %}
aaaa
{% endif %}
3333
[blab@node01 demo4]$
这里jinja2模板会打印3行内容,第一行和第三行的内容是固定的,为1111和3333。第二行的内容是什么,要看是否满足条件,这里判断被管理主机名为server2.rhce.cc及系统主版本号为7,二者都要满足,第二行才会显示主机名,否则显示 aaaa。需要注意的是,这里if 判断语句太长,特意写成了3行也是没问题的。
写一个playbook
[blab@node01 demo4]$ cat 3.yml
---
- hosts: db
tasks:
- name: 我要拷贝一个文件过去
template: src=cc.j2 dest=/opt/cc.conf
[blab@node01 demo4]$
运行此playbook。
[blab@node01 demo4]$ ansible-playbook 3.yml
PLAY [db] **********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [node03]
ok: [node02]
TASK [我要拷贝一个文件过去] **************************************************************
changed: [node03]
changed: [node02]
PLAY RECAP *********************************************************************
node02 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node03 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[blab@node01 demo4]$
查看两台机器上/opt/cc.conf的内容
[blab@node01 demo4]$ ansible db -m shell -a "cat /opt/cc.conf"
node03 | CHANGED | rc=0 >>
1111
aaaa
3333
node02 | CHANGED | rc=0 >>
1111
aaaa
3333
[blab@node01 demo4]$
2.for循环
一个列表中有多个元素,如果需要依次对列表中的每个元素操作,则可以使用for循环来实现,for 循环的语法如下。
{% for i in 列表名 %}
{{i}}
{% endfor %}
这里首先把列表中的第一个元素赋值给i,执行中间的操作;然后把第二个元素赋值给i执行中间的操作,以此类推,直到把最后一个元素赋值给i。看下面的例子。
[blab@node01 demo4]$ cat templates/dd.conf.j2
{% set list1=['aa','bb','cc'] %}
1111
{% for i in list1 %}
{{i}}
{% endfor %}
5555
[blab@node01 demo4]$
这里手动在jinja2模板中定义了一个列表(注意定义列表的方式)list1,里面有3个元素,分别为aa、bb、cc。然后对这个列表的内容进行循环。
这里jinja2模板生成的文件有5行内容,第1行和第5行的内容是固定的,为1111和5555。 第2~4行是循环列表list1 中的值,为aa、bb、cc.
写一个 playbook
[blab@node01 demo4]$ cat 4.yml
---
- hosts: db
tasks:
- name: 拷贝一个文件到远端主机
template: src=dd.conf.j2 dest=/opt/dd.conf
[blab@node01 demo4]$
运行此playbook
[blab@node01 demo4]$ ansible-playbook 4.yml
PLAY [db] **********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [node03]
ok: [node02]
TASK [拷贝一个文件到远端主机] *************************************************************
changed: [node03]
changed: [node02]
PLAY RECAP *********************************************************************
node02 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node03 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[blab@node01 demo4]$
查看db组 上/opt/dd.conf的内容
[blab@node01 demo4]$ ansible db -m shell -a "cat /opt/dd.conf"
node03 | CHANGED | rc=0 >>
1111
aa
bb
cc
5555
node02 | CHANGED | rc=0 >>
1111
aa
bb
cc
5555
[blab@node01 demo4]$
除了jinja2模板中手动定义的列表,一般情况下,我们会在playbook中定义列表,然后对列表中的元素进行循环
练习:写一个变量文件users_list.txt,里面包含一个名称为users的列表
[blab@node01 demo4]$ cat users_list.txt
users:
- uname: tom
age: 20
sex: man
- uname: tom
age: 19
sex: man
- uname: bob
age: 22
sex: man
- uname: mary
age: 23
sex: woman
- uname: wangwu
age: 24
sex: man
[blab@node01 demo4]$
在 templates目录下写一个ee.j2,里面写一个 for 语句循环users列表
[blab@node01 demo4]$ cat templates/ee.j2
现在 公司中所有员工姓名是:
{% for i in users %}
{{i.uname}}
{% endfor %}
[blab@node01 demo4]$
循环每个元素时,只打印元素中的uname变量。写一个名称为5.yaml的playbook,加载变量文件 users list.txt,
[blab@node01 demo4]$ cat 5.yml
---
- hosts: node02
vars_files:
- users_list.txt
tasks:
- name: 拷贝一个文件到远端
template: src=ee.j2 dest=/opt/ee.conf
[blab@node01 demo4]$
这里通过 template模块把ee.j2拷贝到被管理主机的/opt 中并命名为ee.conf。查看node02上/opt/ee.conf的内容
[blab@node01 demo4]$ ansible-playbook 5.yml
PLAY [node02] ******************************************************************
TASK [Gathering Facts] *********************************************************
ok: [node02]
TASK [拷贝一个文件到远端] ***************************************************************
changed: [node02]
PLAY RECAP *********************************************************************
node02 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[blab@node01 demo4]$
查看被管理主机的/opt/ee.conf,里面包括users列表中所有的用户名。
[blab@node01 demo4]$ ansible node02 -m shell -a "cat /opt/ee.conf"
node02 | CHANGED | rc=0 >>
现在 公司中所有员工姓名是:
tom
tom
bob
mary
wangwu
[blab@node01 demo4]$