傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。——–王小波
写在前面
- 今天和小伙伴分享一些ansible剧本中数据迭代的笔记
- 博文内容比较简单
- 主要介绍的常见的迭代对比
- 使用过滤器和查找插件在复杂数据结构上实施迭代循环
- 食用方式:了解Ansible基础语法
- 理解不足小伙伴帮忙指正
傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。——–王小波
循环和查找插件
Ansible 在 2.5 中引入了loop
关键字。以前任务迭代通过使用with_
开头并以查找的名称结尾的关键字的方法。与 loop 等效的是 with_list
,设计用于在简单的扁平列表中进行迭代,对于简单的列表来讲,loop 是最佳语法。
以下三种语法具有相同的结果,其中第一个使用的loop
是首选:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| $ cat loop_demo.yaml --- - name: loop Play hosts: servera gather_facts: no vars: - mylist: - li - rui - long tasks: - name: using loop debug: msg={{ item }} loop: "{{ mylist }}" - name: using with_list debug: msg={{ item }} with_list: "{{ mylist }}" - name: using lookup plugin debug: msg={{ item }} loop: "{{ lookup('list',mylist) }}" $
|
运行剧本是一样的效果,这里第三种方式通过,lookup插件的的方式实现的,lookup 插件是 Jinja2 模板引擎的 Ansible 扩展。通过插件使 Ansible 能够使用外部来源的数据,我们这里使用lookup来将一个数据转化为list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| $ ansible-playbook loop_demo.yaml
PLAY [loop Play] *********************************************************************************************
TASK [using loop] ******************************************************************************************** ok: [servera] => (item=li) => { "msg": "li" } ok: [servera] => (item=rui) => { "msg": "rui" } ok: [servera] => (item=long) => { "msg": "long" }
TASK [using with_list] *************************************************************************************** ok: [servera] => (item=li) => { "msg": "li" } ok: [servera] => (item=rui) => { "msg": "rui" } ok: [servera] => (item=long) => { "msg": "long" }
TASK [using lookup plugin] *********************************************************************************** ok: [servera] => (item=li) => { "msg": "li" } ok: [servera] => (item=rui) => { "msg": "rui" } ok: [servera] => (item=long) => { "msg": "long" }
PLAY RECAP *************************************************************************************************** servera : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
|
使用 loop 关键字替代 with_* 样式的循环具有以下优点:
- 无需记住或查找 with_* 样式关键字来满足当前迭代场景。
- 专注于学习 Ansible 中提供的插件和过滤器,适用性比单纯的迭代更广泛。
- 可以通过
ansible-doc -t lookup
命令访问查找插件文档,结合插件文档实现复杂遍历
使用loop关键字和dict插件替换”with_dict”关键字
1 2 3 4 5 6 7 8 9 10 11 12
| --- - hosts: testB remote_user: root gather_facts: no vars: users: alice: female bob: male tasks: - debug: msg: "{{item.key}} is {{item.value}}" loop: "{{ lookup('dict',users) }}"
|
在2.6版本的官网手册中,官方开始推荐使用 loop加filter
的方式来替代”loop加lookup”的方式,
1 2 3 4 5 6 7 8 9 10 11 12
| --- - hosts: testB remote_user: root gather_facts: no vars: users: alice: female bob: male tasks: - debug: msg: "{{item.key}} is {{item.value}}" loop: "{{ users | dict2items }}"
|
loop 方式还提供了 loop_control
属性
可以用于控制循环的行为,添加索引之类,比如,使用loop_control的index_var选项,就能在遍历列表时,将元素对应的索引写入到指定的变量中,除了index_var选项,loop_control还有一些其他的选项可用,此处我们就来总结一下这些选项。
pause选项能够让我们设置每次循环之后的暂停时间,以秒为单位,换句话说就是设置每次循环之间的间隔时间,示例如下
迭代场景示例
在列表的列表上迭代
对于需要多级迭代的嵌套数据,使用传统的循环方式,往往获取不到子数据,即不能实现数据的扁平化处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| --- - name: loop Play hosts: servera gather_facts: no vars: - mylist: - ['l','i'] - rui - long tasks: - name: using loop debug: msg={{ item }} loop: "{{ mylist }}" - name: using with_list debug: msg={{ item }} with_list: "{{ mylist }}" - name: using lookup plugin debug: msg={{ item }} loop: "{{ lookup('list',mylist) }}"
|
上面迭代的数据为一个嵌套的list,使用前面所讲的迭代方式不能对嵌套的子数组进行迭代。
三种不同方式的测试,都不能做的扁平化的迭代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| $ ansible-playbook loop_demo.yaml
PLAY [loop Play] *********************************************************************************************
TASK [using loop] ******************************************************************************************** ok: [servera] => (item=['l', 'i']) => { "msg": [ "l", "i" ] } ok: [servera] => (item=rui) => { "msg": "rui" } ok: [servera] => (item=long) => { "msg": "long" }
TASK [using with_list] *************************************************************************************** ok: [servera] => (item=['l', 'i']) => { "msg": [ "l", "i" ] } ok: [servera] => (item=rui) => { "msg": "rui" } ok: [servera] => (item=long) => { "msg": "long" }
TASK [using lookup plugin] *********************************************************************************** ok: [servera] => (item=['l', 'i']) => { "msg": [ "l", "i" ] } ok: [servera] => (item=rui) => { "msg": "rui" } ok: [servera] => (item=long) => { "msg": "long" }
PLAY RECAP *************************************************************************************************** servera : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
这个时候,我们可以使用 with_items
关键字来迭代复杂的列表,实现列表数据的扁平化处理
1 2 3 4 5 6 7 8 9 10 11 12
| $ cat loop_demos.yaml --- - name: loop Play hosts: servera gather_facts: no tasks: - name: using with_list debug: msg: "{{ item }}" with_items: - [ 1,2,4 ] - [ r,u ]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| $ ansible-playbook loop_demos.yaml
PLAY [loop Play] *********************************************************************************************
TASK [using with_list] *************************************************************************************** ok: [servera] => (item=1) => { "msg": 1 } ok: [servera] => (item=2) => { "msg": 2 } ok: [servera] => (item=4) => { "msg": 4 } ok: [servera] => (item=r) => { "msg": "r" } ok: [servera] => (item=u) => { "msg": "u" }
PLAY RECAP *************************************************************************************************** servera : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
获取序列
有时候希望在剧本里获取一些序列,可以通过 whit_sequence
关键字实现
1 2 3 4 5 6 7 8 9 10 11 12
| $ cat loop_demo_sq.yaml --- - name: liruilong demo hosts: servera gather_facts: n tasks: - name: test whit_sequence debug: msg={{ item }} with_sequence: start=1 end=5 stride=1
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| $ ansible-playbook loop_demo_sq.yaml
PLAY [liruilong demo] ****************************************************************************************
TASK [test whit_sequence] ************************************************************************************ ok: [servera] => (item=1) => { "msg": "1" } ok: [servera] => (item=2) => { "msg": "2" } ok: [servera] => (item=3) => { "msg": "3" } ok: [servera] => (item=4) => { "msg": "4" } ok: [servera] => (item=5) => { "msg": "5" }
PLAY RECAP *************************************************************************************************** servera : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
flatten 代替 with_items
若希望重构旧的剧本里的 with_items
任务以使用 loop
关键字,可使用 flatten 过滤器。
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ cat loop_demos.yaml --- - name: loop Play hosts: servera gather_facts: no tasks: - name: using with_list debug: msg: "{{ item }}" loop: - [ 1,2,4,[3,4,5,[6]] ] - [ r,u ]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| $ ansible-playbook loop_demos.yaml
PLAY [loop Play] ********************************************************************************************* TASK [using with_list] *************************************************************************************** ok: [servera] => (item=[1, 2, 4, [3, 4, 5, [6]]]) => { "msg": [ 1, 2, 4, [ 3, 4, 5, [ 6 ] ] ] } ok: [servera] => (item=['r', 'u']) => { "msg": [ "r", "u" ] }
PLAY RECAP *************************************************************************************************** servera : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 $
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $ cat loop_demos.yaml --- - name: loop Play hosts: servera gather_facts: no tasks: - name: using with_list debug: msg: "{{ item }}" loop: "{{ numList | flatten(levels=3) }}" vars: numList: - [ 1,2,4,[3,4,5,[6]] ] - [ r,u ]
$
|
执行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| $ ansible-playbook loop_demos.yaml
PLAY [loop Play] *********************************************************************************************
TASK [using with_list] *************************************************************************************** ok: [servera] => (item=1) => { "msg": 1 } ok: [servera] => (item=2) => { "msg": 2 } ok: [servera] => (item=4) => { "msg": 4 } ok: [servera] => (item=3) => { "msg": 3 } ok: [servera] => (item=4) => { "msg": 4 } ok: [servera] => (item=5) => { "msg": 5 } ok: [servera] => (item=6) => { "msg": 6 } ok: [servera] => (item=r) => { "msg": "r" } ok: [servera] => (item=u) => { "msg": "u" }
PLAY RECAP *************************************************************************************************** servera : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
迭代嵌套列表
subelements 过滤器从 names
变量数据中创建一个新列表。列表中的每一项本身是一个两元素列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $ cat subelements.yaml --- - name: liruilong demo hosts: servera vars: names: - liruilong: - key: li - liruilong: - key: rui - liruilong: - key: long tasks: - name: loop demo debug: msg={{ item }} loop: "{{ names | subelements('liruilong') }}"
|
执行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| $ ansible-playbook subelements.yaml -b
PLAY [liruilong demo] ****************************************************************************************
TASK [Gathering Facts] *************************************************************************************** ok: [servera]
TASK [loop demo] ********************************************************************************************* ok: [servera] => (item=[{'liruilong': [{'key': 'li'}]}, {'key': 'li'}]) => { "msg": [ { "liruilong": [ { "key": "li" } ] }, { "key": "li" } ] } ok: [servera] => (item=[{'liruilong': [{'key': 'rui'}]}, {'key': 'rui'}]) => { "msg": [ { "liruilong": [ { "key": "rui" } ] }, { "key": "rui" } ] } ok: [servera] => (item=[{'liruilong': [{'key': 'long'}]}, {'key': 'long'}]) => { "msg": [ { "liruilong": [ { "key": "long" } ] }, { "key": "long" } ] }
PLAY RECAP *************************************************************************************************** servera : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
- 第一个元素包含对每个用户的引用。
- 第二个元素包含该用户的 liruilong 字典中的单个条目的引用。
迭代字典
在 Ansible 2.5 之前,必须使用 with_dict 关键字来迭代字典中的键值对。
item 变量具有两个属性:key 和 value。
key 属性包含一个字典键的值,而 value 属性则包含与字典关联的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| $ cat dist_demo.yaml --- - name: 字典 demo hosts: servera vars: users: xiaoming: name: xiaoming xiaoli: name: xiaoli tasks: - name: dist demo debug: msg={{ item.value.name }} loop: "{{ users | dict2items }}" - name: dist demo1 debug: msg={{ item.key }} with_dict : "{{ users }}" $
|
这里使用 dict2item
可以实现相同的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| $ ansible-playbook dist_demo.yaml
PLAY [字典 demo] *************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************** ok: [servera]
TASK [dist demo] *********************************************************************************************************** ok: [servera] => (item={'key': 'xiaoming', 'value': {'name': 'xiaoming'}}) => { "msg": "xiaoming" } ok: [servera] => (item={'key': 'xiaoli', 'value': {'name': 'xiaoli'}}) => { "msg": "xiaoli" }
TASK [dist demo1] ********************************************************************************************************** ok: [servera] => (item={'key': 'xiaoming', 'value': {'name': 'xiaoming'}}) => { "msg": "xiaoming" } ok: [servera] => (item={'key': 'xiaoli', 'value': {'name': 'xiaoli'}}) => { "msg": "xiaoli" }
PLAY RECAP ***************************************************************************************************************** servera : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
|
迭代文件通配模式
通过fileglob
查找插件构建循环,以迭代与所提供的文件通配模式的文件列表:
1 2 3 4 5 6 7
| $ cat file_loop.yaml --- - name: demo hosts: serverc tasks: - name: file demo debug: msg={{lookup('fileglob','~/.bash*')}}
|
执行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $ ansible-playbook file_loop.yaml
PLAY [demo] ****************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************** ok: [serverc]
TASK [file demo] *********************************************************************************************************** ok: [serverc] => { "msg": "/home/student/.bash_logout,/home/student/.bash_profile,/home/student/.bashrc,/home/student/.bash_history" }
PLAY RECAP ***************************************************************************************************************** serverc : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
可以使用 query
代替 lookup
, 以列表的形式展示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| $ sed 's/lookup/query/' file_loop.yaml -i $ ansible-playbook file_loop.yaml
PLAY [demo] ****************************************************************************************************************
TASK [Gathering Facts] ***************************************************************************************************** ok: [serverc]
TASK [file demo] *********************************************************************************************************** ok: [serverc] => { "msg": [ "/home/student/.bash_logout", "/home/student/.bash_profile", "/home/student/.bashrc", "/home/student/.bash_history" ] }
PLAY RECAP ***************************************************************************************************************** serverc : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
|
博文参考
《Red Hat Ansible Engine 2.8 DO447》