Ansible循环与判断实战指南
Ansible 循环与判断使用指南
循环
1. with_items 迭代列表
当需要在 Linux 中依次安装多个软件包时,可使用 with_items
进行迭代。
示例:安装 httpd、samba、samba-client 软件包
---
- name: install packageshosts: node1tasks:- name: yum_repo1yum_repository:file: servername: baseosdescription: rhel8baseurl: file:///mnt/BaseOSenabled: yesgpgcheck: no- name: yum_repo2yum_repository:file: servername: appstreamdescription: appstreambaseurl: file:///mnt/AppStreamenabled: yesgpgcheck: no- name: mount cdrommount:src: /dev/cdrompath: /mntfstype: iso9660state: mounted- name: install pksyum:name: "{{ item }}"state: presentwith_items:- httpd- samba- samba-client
2. with_dict 迭代字典
item.key
对应字典的键,item.value
对应字典的值。
示例:迭代字典并输出键值对
---
- name: testhosts: node1tasks:- name: debugdebug:msg: "{{ item.key }} & {{ item.value }}"with_dict:address: 1netmask: 2gateway: 3
3. with_fileglob 迭代文件
适用于拷贝多个文件到受控主机的场景。
示例:拷贝 /tmp/ 目录下的 .sh 和 .py 文件
---
- name: testhosts: node1tasks:- name: cp filecopy:src: "{{ item }}"dest: /tmp/with_fileglob:- /tmp/*.sh- /tmp/*.py
4. with_lines 迭代行
with_lines
可将命令的输出结果按行迭代。
示例:查找 /etc/ansible 目录下的所有 .yml 文件并拷贝
---
- name: testhosts: node1tasks:- name: cp filecopy:src: "{{ item }}"dest: /tmp/with_lines:- find /etc/ansible -name "*.yml"
5. with_nested 嵌套迭代
用于多重循环场景,例如组合多个列表中的元素。
示例:嵌套迭代输出组合值
---
- name: testhosts: node1tasks:- name: debugdebug:msg: "{{ item[0] }} & {{ item[1] }}"with_nested:- [a, b]- [1, 2, 3]
6. with_sequence 序列迭代
生成数字序列,支持指定起始值、结束值和步长。
示例:生成从 1 到 5 的序列
---
- name: testhosts: node1tasks:- name: debugdebug:msg: "{{ item }}"with_sequence:start=1end=5stride=1
注意:在较新版本的 Ansible 中,建议使用
loop
配合range
过滤器替代with_sequence
。
7. with_random_choice 随机选择
从列表中随机选择一个值。
示例:随机输出列表中的一个值
---
- name: testhosts: node1tasks:- name: debugdebug:msg: "{{ item }}"with_random_choice:- 1- 2- a- b- c
Loop 循环(现代写法)
现在更推荐使用 loop
替代传统的 with_*
,通常结合过滤器使用。
过滤器(Filters)
字符串过滤器
---
- name: testhosts: node1vars:testvar: "abc123ABC 666"testvar1: " abc "tasks:- name: debug1debug:msg: "{{ testvar | upper }}" # 转换为大写- name: debug2debug:msg: "{{ testvar | lower }}" # 转换为小写- name: debug3debug:msg: "{{ testvar1 | trim }}" # 去除首尾空格- name: debug4debug:msg: "{{ testvar | length }}" # 计算字符串长度
创建用户并使用哈希密码
使用加密算法对字符串进行hash加密
创建一个用户ydh,并且设置密码为redhat,密码采用SHA512哈希格式
---
- name: create userhosts: node1tasks:- name: create ydhuser:name: ydhpassword: "{{ 'redhat' | password_hash('sha512') }}"
补充字符串过滤器示例
---
- name: 过滤器hosts: serveravars:testvar: "abc123ABC 666"testvar1: " abc "testvar2: "123456789"testvar3: "1a2b,@#$%^&"tasks:- name: 将字符串转换成纯大写debug:#将字符串转换成纯大写msg: "{{ testvar | upper }}"- name: 将字符串转换成纯小写debug:#将字符串转换成纯小写msg: "{{ testvar | lower }}"- name: 将字符串首字母大写,之后的所有字母纯小写debug:#将字符串首字母大写,之后的所有字母纯小写msg: "{{ testvar | capitalize }}" - name: 返回字符串的第一个字符debug:#返回字符串的第一个字符msg: "{{ testvar | first }}" - name: 返回字符串的最后一个字符 debug:#返回字符串的最后一个字符msg: "{{ testvar | last }}"- name: 将字符串开头和结尾的空格去除debug:#将字符串开头和结尾的空格去除msg: "{{ testvar1 | trim }}"- name: 将字符串放在中间,并且设置字符串的长度为30,字符串两边用空格补齐30位长debug:#将字符串放在中间,并且设置字符串的长度为30,字符串两边用空格补齐30位长msg: "{{ testvar1 | center(width=30) }}"- name: 返回字符串长度,length与count等效,可以写为countdebug:#返回字符串长度,length与count等效,可以写为countmsg: "{{ testvar2 | length }}"- name: 将字符串转换成列表,每个字符作为一个元素debug:#将字符串转换成列表,每个字符作为一个元素msg: "{{ testvar3 | list }}"- name: 将字符串转换成列表,每个字符作为一个元素,并且随机打乱顺序debug:#将字符串转换成列表,每个字符作为一个元素,并且随机打乱顺序#shuffle的字面意思为洗牌msg: "{{ testvar3 | shuffle }}"
数字操作过滤器
# 将字符串转换为整数
msg: "{{ '8' | int }}"msg: "{{ 8+('8' | int) }}" #将对应的值转换成int类型#ansible中,字符串和整形不能直接计算,比如{{ 8+'8' }}会报错#所以,我们可以把一个值为数字的字符串转换成整形后再做计算# 转换失败时返回默认值
msg: "{{ 'a' | int(default=6) }}"
#将对应的值转换成int类型,如果无法转换,默认返回0
#使用int(default=6)或者int(6)时,如果无法转换则返回指定值6# 转换为浮点数
msg: "{{ '8' | float }}" #将对应的值转换成浮点型,如果无法转换,默认返回'0.0'msg: "{{ 'a' | float(8.88) }}" #当对应的值无法被转换成浮点型时,则返回指定值’8.88‘# 获取绝对值
msg: "{{ testvar4 | abs }}" #获取对应数值的绝对值# 四舍五入
msg: "{{ 12.5 | round }}" # 四舍五入
msg: "{{ 3.1415926 | round(5) }}" #取小数点后五位 # 生成随机数
msg: "{{ 100 | random }}" #从0到100中随机返回一个随机数
msg: "{{ 10 | random(start=5) }}" #从5到10中随机返回一个随机数
msg: "{{ 15 | random(start=5, step=3) }}"
#从5到15中随机返回一个随机数,步长为3
#步长为3的意思是返回的随机数只有可能是5、8、11、14中的一个
msg: "{{ 15 | random(step=5) }}" #从0到15中随机返回一个随机数,这个随机数是5的倍数
文件与目录类过滤器
# 使用不同算法计算哈希值
msg: "{{ '123456' | hash('sha1') }}" #使用sha1算法对字符串进行哈希
msg: "{{ '123456' | hash('md5') }}" #使用md5算法对字符串进行哈希
msg: "{{ '123456' | checksum }}" #获取到字符串的校验和,与md5哈希值一致
msg: "{{ '123456' | password_hash('sha256') }}" #使用sha256算法对字符串进行哈希,哈希过程中会生成随机"盐",以便无法直接对比出原值
msg: "{{ '123456' | password_hash('sha256', 'mysalt') }}" #使用sha256算法对字符串进行哈希,并使用指定的字符串作为"盐"
msg: "{{ '123123' | password_hash('sha512') }}" #使使用sha512算法对字符串进行哈希,哈希过程中会生成随机"盐",以便无法直接对比出原值
msg: "{{ '123123' | password_hash('sha512','ebzL.U5cjaHe55KK') }}" #使用sha512算法对字符串进行哈希,并使用指定的字符串作为"盐"
Ansible 条件判断(When)
基本判断运算符
==`, `!=`, `>`, `<`, `>=`, `<=`, `and`, `or`, `not`, `is`, `in
每次执行完一个任务,不管成功与失败,都会将执行的结果进行注册,可以使用这个注册的变量来when
变量判断 Tests
defined
:变量已定义返回真undefined
:变量未定义返回真none
:变量值为空返回真
示例:
---
- name: testhosts: node1vars:aa: 11cc:tasks:- name: create debug1debug:msg: awhen: aa is defined- name: create debug2debug:msg: abwhen: bb is undefined- name: create debug3debug:msg: abcwhen: cc is none
任务执行结果判断 Tests
success
/successed
:通过任务的返回信息判断执行状态,任务执行成功返回真failure
/failed
:通过任务的返回信息判断执行状态,任务执行失败返回真change
/changed
:通过任务的返回信息判断执行状态,任务状态为 changed 返回真skip
/skipped
:通过任务的返回信息判断执行状态,任务被跳过返回真
路径判断 Tests
注意:这些判断针对的是 Ansible 控制机上的路径,不是目标主机。
file
:判断路径是否是一个文件directory
:判断路径是否是一个目录link
:判断路径是否是一个软连接mount
:判断路径是否是一个挂载点exists
:判断路径是否存在
字符串判断 Tests
lower
:判断包含字母的字符串中的字母是否纯小写upper
:判断包含字母的字符串中的字母是否纯大写
其他 Tests
string
:判断对象是否是一个字符串number
:判断对象是否一个数字
错误处理与流程控制
block/rescue/always
用于异常处理:先执行 block
,失败则执行 rescue
,最后执行 always
。
无论是block还是rescue执行失败还是成功,在最后都执行always
例题:
创建一个名为/etc/ansible/lv.yml 的playbook,它将在所有受管节点上运行以执行下列任务:
创建符合以下要求的逻辑卷:
逻辑卷创建在research卷组中
逻辑卷名称为data
逻辑卷大小为1500MiB
使用ext4文件系统格式化逻辑卷
如果无法创建请求的逻辑卷大小,应显示错误消息
Could not create logical volume of that size,并且应改为使用大小 800MiB。
如果卷组research 不存在 ,应显示错误消息
Volume group does not exist。
不要以任何方式挂载逻辑卷
前提:在node1、node2上添加一块硬盘,然后新建卷组
Node1的卷组大小为2G 卷组名为research
Node2的卷组大小为1G 卷组名为research
示例:创建逻辑卷
---
- name: create vg for node1hosts: node1tasks:- name: create partitionparted:device: /dev/vdbnumber: 1part_type: primarypart_start: 10MiBpart_end: 2058MiBstate: present- name: create vg researchlvg:vg: researchpvs: /dev/vdb1- name: create vg for node2hosts: node2tasks:- name: create partition for node2parted:device: /dev/vdbnumber: 1part_type: primarypart_start: 10MiBpart_end: 1034MiBstate: present- name: create vg research for node2lvg:vg: researchpvs: /dev/vdb1
新建lv.yml,满足题目需求
---
- name: create lvmhosts: node1,node2tasks:- name: create lvblock:- name: create lvm 1500Mlvol:vg: researchlv: datasize: 1500Mrescue:- name: output fail messagedebug:msg: Could not create logical volume of that size- name: create lvm 800Mlvol:vg: researchlv: datasize: 800Malways:- name: format lvmfilesystem:fstype: ext4dev: /dev/research/datawhen: "'research' in ansible_facts.lvm.vgs"#也可以用when: "'research' in ansible_lvm.vgs"- name: serach not existsdebug:msg: Volume group does not existwhen: "'research' not in ansible_facts.lvm.vgs"#也可以用when: "'research' not in ansible_lvm.vgs"
fail 模块
用于在满足条件时中断 playbook 执行。
但我们一般是不会无故中断,除非在满足条件的情况下可以中断,经常和when一起用
示例:
---
- name: testhosts: node1tasks:- name: shellshell:cmd: echo 'this is a string for testing--error'register: return_value- name: failfail:msg: Conditions established, Interrupt running playbookwhen: "'error' in return_value.stdout"- name: debugdebug:msg: I never execute, because the playbook has stopped
failed_when
---
- name: testhosts: node1tasks:- name: debugdebug:msg: I execute normally- name: shellshell:cmd: echo 'this is a string testing--error'register: return_valuefailed_when: "'error' in return_value.stdout"- name: debug2debug:msg: chenyu
直接指定任务失败的条件。
ignore_errors
---
- name: testhosts: node1tasks:- name: debug1debug:msg: "{{ansible_fqdn}}"- name: debug2debug:msg: "{{ansible_ip}}"ignore_errors: yes- name: create filefile:path: /tmp/abcstate: touch
忽略任务错误继续执行。
changed_when
---
- name: testhosts: node1tasks:- name: debug1debug:msg: "{{ansible_fqdn}}"changed_when: true#或者可以让任务执行状态显示失败
---
- name: testhosts: node1tasks:- name: shellshell:cmd: ls /tmpchanged_when: false