有时候我们写 playbook 的时候发现写了很多的 task 都重复引用某个模块,比如一次想同步10个文件,如果按照以前写 playbook 的思路需要写10个 task,这样写的话发现 playbook 会显得很臃肿。可以用 loops 方式去编写 playbook 减少重复使用某个模块。
Ansible playbook 循环有以下几种:
1、标准 Loops
2、嵌套 Loops
3、散列 Loops
4、文件匹配 Loops
5、随机选择 Loops
6、条件判断 Loops
7、register loops
下面分别来介绍每种 Loops 使用方法
一、标准 Loops
标准 Loops 是我们编写 playbook 过程中使用最的一种,它能直接编写 task 的次数,比如使用 yum 安装多个软件包:
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: yum install package
yum: name={{ item }} state=latest
with_items:
- tree
- openssh-clients
- vim
with_items 的值是 python list 数据结构,可以理解每个 task 会循环读取 list 里面的值,然后 key 的名称是 item,当然 list 里面也支持 python 字典。通过下面形式来自定义 yum 包版本:
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: yum install package
yum: name={{ item.name }} state={{ item.state }}
with_items:
- {name: "tree", state: "latest"}
- {name: "openssh-clients", state: "present"}
- {name: "vim", state: "latest" }
- {name: "mysql", state: "present" }
二、嵌套 Loops
嵌套 Loops 也是 playbook 中比较常见一种循环,也主要一对多或者多对多的合并。如下,我需要在客户端创建目录 /tmp/{file1,file2,file3}/{data1,data2,date3}
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: display user info
shell: mkdir -p /tmp/{{ item[0] }}/{{ item[1] }}
with_nested:
- ['file1','file2','file3']
- ['data1','data2','data3']
查看创建的目录:
三、散列 Loops
散列 loops 相对标准 loops 就是支持更丰富的数据结构,比如标准 loops 的最外层数据必须是 python 的 list 数据类型,而散列 loops 直接支持 YAML 格式的数据变量。
with_dict 是接收一个 python (经过yaml.load) 的格式的变量,字典 调取值得时候通过 item.key item.value.VARIABLE 进行调取。
下面通过 MySQL 变量来做散列循环输出信息
---
- hosts: 10.1.0.51
gather_facts: False
vars:
MYSQL_INFO:
10.1.0.51:
MY_DIR: /usr/local/mysql
MY_DATA_DIR: /usr/local/mysql/data
MY_VER: 5.6.27
MY_HOST: 10.1.0.51
MY_FILE: /etc/my.cnf
MY_USER: root
MY_PASS: pass
10.1.0.52:
MY_DIR: /usr/local/mysql
MY_DATA_DIR: /usr/local/mysql/data
MY_VER: 5.6.27
MY_HOST: 10.1.0.52
MY_FILE: /etc/my/cnf
MY_USER: root
MY_PASS: pass
tasks:
- name: display mysql info
debug: msg="HOST:{{ item.key }},MY_DIR:{{ item.value.MY_DIR }}, MY_DATA_DIR:{{ item.value.MY_DATA_DIR}}, MY_VER:{{ item.value.MY_VER }}, MY_HOST:{{ item.value.MY_HOST }}, MY_FILE:{{ item.value.MY_FILE }}, MY_USER:{{ item.value.MY_USER }}, MY_PASS:{{ item.value.MY_PASS }} ."
with_dict: MYSQL_INFO
四、文件匹配 Loops
文件匹配 Loops 是编写 playbook 的时候需要针对文件进行操作中最常用的一种循环,比如需要针对一个目录下指定格式的文件进行处理,这个时候直接引用 with_fileglob 循环去匹配需要处理的文件即可。
with_fileglob 这个会匹配 /tmp 目录下所有以 yaml 结尾的文件:
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: find *.yaml file
debug: msg="files ---> {{ item }} ."
with_fileglob:
- /tmp/*.yaml
五、随机选择 Loops
with_random_choice 就是传入的 list 中随机选择一个值,与使用 python random 实现原理一样。以下 playbook 主要是随机取一个值进行打印:
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: display random value
debug: msg=" random value is a ---> {{ item }} ."
with_random_choice:
- "Ansible-1 : 10.1.0.51"
- "Ansible-2 : 10.1.0.52"
- "Ansible-3 : 10.1.0.53"
六、条件判断 Loops
有时候执行一个 task 之后,我们需要检测这个 task 的结果是否达到了预期效果,如果没有达到该状态时,就需要退出整个 playbook 执行,这个时候就需要对某个结果一直循环检测了。
下面 playbook 匹配 register 中的值是否包含 roo1,如果有则返回,如果无,则持续5次判断,每次间隔2秒,失败退出。
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: find value root
shell: "cat /etc/passwd"
register: user_info
until: user_info.stdout.startswith("roo1")
retries: 5
delay: 2
七、register loops
register 是用于 task 直接互相传递数据的,一般我们会把 register 用在单一的 task 中进行变量临时存储,其实 register 还可以同时接受多个 task 结果当作变量临时存储。
下面 playbook 将多个值传递给 register,需要使用 jinja2 的 for 循环才能把所有的结果显示出来
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: debug loops
shell: "{{ item }}"
with_items:
- hostname
- uname
register: system_info
- name: display system_info
debug: msg="{% for i in system_info.results%} {{ i.stdout }} {% endfor %}"