实施任务控制
编写循环和条件任务
利用循环迭代任务
通过利用循环,我们无需编写多个使用同一模块的任务。例如,他们不必编写五个任务来确保存在五个用户,而是只需编写一个任务来对含有五个用户的列表迭代,从而确保它们都存在。
Ansible支持使用loop关键字对一组项目迭代任务。可以配置循环以利用列表中的各个项目、列表中各个文件的内容、生成的数字序列或更为复杂的结构来重复任务。
简单循环
简单循环对一组项目迭代任务。loop关键字添加到任务中,将应对其迭代任务的项目列表取为值。循环变量item保存每个迭代过程中使用的值。
创建用户不用循环
[root@control book]# cat debug/loop.yml---
- name: look
hosts: server1
tasks:
- name: cat loop
user:
name: tom
uid: 1201
state: present
[root@server1 ~]# id tom
uid=1201(tom) gid=1201(tom) groups=1201(tom)
用循环创建用户
[root@control book]# cat debug/loop.yml
---
- name: look
hosts: server1
tasks:
- name: cat loop
user:
name: '{{ item }}'
state: present
loop:
- time1
- time2
- time3
[root@control book]# ansible-playbook debug/loop.yml
PLAY [look] ********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.56.130]
TASK [cat loop] ***************************************************************
changed: [192.168.56.130] => (item=time1)
changed: [192.168.56.130] => (item=time2)
changed: [192.168.56.130] => (item=time3)
PLAY RECAP *********************************************************************
192.168.56.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
//查看结果
[root@server1 ~]# id time1
uid=1202(time1) gid=1202(time1) groups=1202(time1)
[root@server1 ~]# id time2
uid=1203(time2) gid=1203(time2) groups=1203(time2)
[root@server1 ~]# id time3
uid=1204(time3) gid=1204(time3) groups=1204(time3)
在循环里使用变量
---
- name: look
hosts: server1
vars_files: //局外变量模块
- /opt/book/debug/users.yml //变量文件地址
tasks:
- name: cat loop
user: //创建用户模块
name: '{{ item }}' //loop循环关键字
state: present
loop: "{{ users }}" //引用变量
//创建变量文件
[root@control book]# cat debug/users.yml
users:
- time1
- time2
- time3
- time4
//运行
[root@control book]# ansible-playbook debug/loop.yml
PLAY [look] ********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.56.130]
TASK [cat loop] ***************************************************************
changed: [192.168.56.130] => (item=time1)
changed: [192.168.56.130] => (item=time2)
changed: [192.168.56.130] => (item=time3)
changed: [192.168.56.130] => (item=time4)
PLAY RECAP *********************************************************************
192.168.56.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
//查看结果
[root@server1 ~]# id time1
uid=1001(time1) gid=1001(time1) groups=1001(time1)
[root@server1 ~]# id time2
uid=1002(time2) gid=1002(time2) groups=1002(time2)
[root@server1 ~]# id time3
uid=1003(time3) gid=1003(time3) groups=1003(time3)
[root@server1 ~]# id time4
uid=1004(time4) gid=1004(time4) groups=1004(time4)
循环散列或字典表
loop列表不需要是简单值列表。在以下示例中,列表中的每个项实际上是散列或字典。示例中的每个散列或字典具有两个键,即name和groups,当前item循环变量中每个键的值可以分别通过item.name和item.groups变量来检索。
//编写变量文件
[root@control book]# cat debug/users.yml
users:
- name: time1
uid: 2000
- name: time2
uid: 2001
- name: time3
uid: 2002
[root@control book]# cat debug/loop.yml
---
- name: look
hosts: server1
vars_files:
- /opt/book/debug/users.yml
tasks:
- name: cat loop
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
state: present
loop: "{{ users }}"
//执行
[root@control book]# ansible-playbook debug/loop.yml
PLAY [look] ********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.56.130]
TASK [cat loop] ***************************************************************
changed: [192.168.56.130] => (item={'name': 'time1', 'uid': 2000})
changed: [192.168.56.130] => (item={'name': 'time2', 'uid': 2001})
changed: [192.168.56.130] => (item={'name': 'time3', 'uid': 2002})
PLAY RECAP *********************************************************************
192.168.56.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
//结果
[root@server1 ~]# id time1
uid=2000(time1) gid=2000(time1) groups=2000(time1)
[root@server1 ~]# id time2
uid=2001(time2) gid=2001(time2) groups=2001(time2)
[root@server1 ~]# id time3
uid=2002(time3) gid=2002(time3) groups=2002(time3)
将Register变量与Loop一起使用
register关键字也可以捕获循环任务的输出。以下代码片段显示了循环任务中register变量的结构:
[root@control book]# cat debug/loop.yml ---
- name: look
hosts: server1
tasks:
- name: cat loop
command: "echo hello {{ item }} ,your name?"
loop: //循环
- lisi
- wangwu
- xiaohong
register: result //注册变量
- debug: //收集事实模块
var: result //输出指定事实
[root@control book]# ansible-playbook debug/loop.yml
PLAY [look] ********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.56.130]
TASK [cat loop] ***************************************************************
changed: [192.168.56.130] => (item=lisi)
changed: [192.168.56.130] => (item=wangwu)
changed: [192.168.56.130] => (item=xiaohong)
TASK [debug] *******************************************************************
ok: [192.168.56.130] => {
"result": {
"changed": true,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": true,
"cmd": [
"echo",
"hello",
"lisi",
",your",
"name?"
],
"delta": "0:00:00.002498",
"end": "2021-07-25 12:47:50.353290",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo hello lisi ,your name?",
"_uses_shell": false,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "lisi",
"rc": 0,
"start": "2021-07-25 12:47:50.350792",
"stderr": "",
"stderr_lines": [],
"stdout": "hello lisi ,your name?",
"stdout_lines": [
"hello lisi ,your name?"
]
},
{
"ansible_loop_var": "item",
"changed": true,
"cmd": [
"echo",
"hello",
"wangwu",
",your",
"name?"
],
"delta": "0:00:00.001697",
"end": "2021-07-25 12:47:50.551735",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo hello wangwu ,your name?",
"_uses_shell": false,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "wangwu",
"rc": 0,
"start": "2021-07-25 12:47:50.550038",
"stderr": "",
"stderr_lines": [],
"stdout": "hello wangwu ,your name?",
"stdout_lines": [
"hello wangwu ,your name?"
]
},
{
"ansible_loop_var": "item",
"changed": true,
"cmd": [
"echo",
"hello",
"xiaohong",
",your",
"name?"
],
"delta": "0:00:00.001758",
"end": "2021-07-25 12:47:50.751189",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo hello xiaohong ,your name?",
"_uses_shell": false,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "xiaohong",
"rc": 0,
"start": "2021-07-25 12:47:50.749431",
"stderr": "",
"stderr_lines": [],
"stdout": "hello xiaohong ,your name?",
"stdout_lines": [
"hello xiaohong ,your name?"
]
}
]
}
}
有条件地运行任务
Ansible可使用conditionals在符合特定条件时执行任务或play。例如,可以利用一个条件在Ansible安装或配置服务前确定受管主机上的可用内存。
我们可以利用条件来区分不同的受管主机,并根据它们所符合的条件来分配功能角色。Playbook变量、注册的变量和Ansible事实都可通过条件来进行测试。可以使用比较字符串、数字数据和布尔值的运算符。
以下场景说明了在Ansible中使用条件的情况:
- 可以在变量中定义硬限制(如min_memory)并将它与受管主机上的可用内存进行比较。
- Ansible可以捕获并评估命令的输出,以确定某一任务在执行进一步操作前是否已经完成。例如,如果某一程序失败,则将路过批处理。
- 可以利用Ansible事实来确定受管主机网络配置,并决定要发送的模板文件(如,网络绑定或中继)。
- 可以评估CPU的数量,来确定如何正确调节某一Web服务器。
- 将注册的变量与预定义的变量进行比较,以确定服务是否已更改。例如,测试服务配置文件的MD5检验以和查看服务是否已更改。
条件语法
when语句用于有条件地运行任务。它取要测试的条件为值。如果条件满足,则运行任务。如果条件不满足,则跳过任务。
可以测试的一个最简单条件是某一布尔变量是True还是False。以下示例中的when语句导致任务仅在run_my_task为True时运行:
[root@control book]# cat debug/us.yml
---
- name: look
hosts: server1
vars_files:
- /opt/book/debug/users.yml
vars:
power: False //布尔值 否
tasks:
- name: cat where
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
state: present
loop: "{{ users }}"
when: power
[root@control book]# ansible-playbook debug/us.yml
PLAY [look] ********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.56.130]
TASK [cat where] **************************************************************
skipping: [192.168.56.130] => (item={'name': 'time1', 'uid': 2000})
skipping: [192.168.56.130] => (item={'name': 'time2', 'uid': 2001})
skipping: [192.168.56.130] => (item={'name': 'time3', 'uid': 2002})
//略过
PLAY RECAP *********************************************************************
192.168.56.130 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
[root@control debug]# cat users.yml
users:
- name: time1
uid: 2000
- name: time2
uid: 2001
- name: time3
uid: 2002
[root@control book]# cat debug/us.yml
---
- name: look
hosts: server1
vars_files:
- /opt/book/debug/users.yml
tasks:
- name: cat where
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
state: present
loop: "{{ users }}"
when: '"{{ item.name }}" == "time1"' //只允许time1执行
//运行结果
[root@control book]# ansible-playbook debug/us.yml
PLAY [look] ********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [192.168.56.130]
TASK [cat where] **************************************************************
[WARNING]: conditional statements should not include jinja2 templating
delimiters such as {{ }} or {% %}. Found: "{{ item.name }}" == "time1"
changed: [192.168.56.130] => (item={'name': 'time1', 'uid': 2000}) //通过
skipping: [192.168.56.130] => (item={'name': 'time2', 'uid': 2001})
skipping: [192.168.56.130] => (item={'name': 'time3', 'uid': 2002})
PLAY RECAP *********************************************************************
192.168.56.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@server1 ~]# id time1
uid=2000(time1) gid=2000(time1) groups=2000(time1)
以下示例测试my_service变量是否具有值。若有值,则将my_service的值用作要安装的软件包的名称。如果未定义my_service变量,则跳过任务且不显示错误。
[root@control book]# cat debug/us.yml
---
- name: look
hosts: server1
vars_files:
- /opt/book/debug/users.yml
tasks:
- name: cat where
user:
name: "{{ users.name }}"
state: present
when: user is defined //只执行变量存在的值
下表显示了在处理条件时可使用的一些运算:
示例条件
操作 | 示例 |
---|---|
等于(值为字符串) | ansible_machine == “x86_64” |
等于(值为数字) | max_memory == 512 |
小于 | min_memory < 128 |
大于 | min_memory > 256 |
小于等于 | min_memory <= 256 |
大于等于 | min_memory >= 512 |
不等于 | min_memory != 512 |
变量存在 | min_memory is defined |
变量不存在 | min_memory is not defined |
布尔变量是True。1、True或yes的求值为True | memory_available |
布尔变量是False。0、False或no的求值为False | not memory_available |
第一个变量的值存在,作为第二个变量的列表中的值 | ansible_distribution in supported_distros |