目录
简介
when关键字
1. when的基本使用
2. 比较运算符
3. 逻辑运算符
4. 判断变量
5. 判断执行结果
6. 判断路径
7. 判断字符串
8. 判断整除
9. 其他
9.1 version
9.2 subset
9.3 superset
9.4 in
9.5 string 和 number
条件判断与block
1. block
2. rescue
3. always
条件判断与错误处理
1. fail模块
2. failed_when
3. changed_when
在循环语句中使用条件语句
在写playbook时,有时候playbook的结果依赖于变量,fact或者前一个任务执行的结果,或者有的时候我们会基于上一个task执行返回的结果而决定如何执行后续的task,这个时候我们就需要用到条件语句,而在ansible-playbook中条件语句的关键字就是when。
条件语句在Ansible中使用场景:
· 在目标主机上定义一个硬限制,比如目标主机的最小内存必须达到多少,再能执行该task;
· 捕获一个命令的输出,根据命令的输出结果的不同来触发不同的task;
· 根据不同目标主机的facts,以定义不同的task;
· 根据目标主机的cpu的大小,以调优相关应用性能;
· 用于判断某个服务的配置文件是否发生变更,以确定是否重启服务。
在ansible中,使用条件判断的关键字就是when。如在安装包的时候,需要指定主机的操作系统类型,或者是当操作系统的硬盘满了之后,需要清空文件等,可以使用when语句来做判断。when关键字后面跟着的是python的表达式,在表达式中你能够使用任何的变量或者fact,当表达式的返回结果为false,便会跳过本次任务。
示例:判断系统类型,如果是centos这使用yum安装http,如果是ubunt就使用apt安装apache2。
- hosts: test
gather_facts: yes
tasks:
- name: install apache2 with os is ubuntu
apt:
name: apache2
state: present
update_cache: yes
enabled: yes
when: ansible_distribution == 'Ubuntu'
- name: install http with os is centos
yum:
name: httpd
state: present
when: ansible_distribution == 'CentOS'
- name: Unexpected os family
debug:
msg: "OS Family {{ ansible_distribution }} is not supported"
when: not (ansible_distribution == 'CentOS' or ansible_distribution == 'Ubuntu')
· == :比较两个对象是否相等,相等则返回真。可用于比较字符串和数字,两个变量也可以比较。
· != : 比较两个对象是否不等,不等则为真。
· > :比较两个对象的大小,左边的值大于右边的值,则为真。
· < :比较两个对象的大小,左边的值小于右边的值,则为真。
· >= :比较两个对象的大小,左边的值大于等于右边的值,则为真。
· <= :比较两个对象的大小,左边的值小于等于右边的值,则为真。
基础示例:
· when ansible_machine == "x86"
· when max_memory >= 1024
在ansibe中,除了比较运算符,还支持逻辑运算符:
· and :逻辑与,当左边和右边两个表达式同时为真,则返回真。
· or :逻辑或,当左边和右边 两个表达式任意一个为真,则返回真。
· not :逻辑否,对表达式取反。
· ():当一组表达式组合在一起,形成一个更大的表达式,组合内的所有表达式都是逻辑与的关系。
示例:
# 逻辑或
· when: ansible_distribution == "RedHat" or ansible_distribution == "Fedora"
# 逻辑与
· when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0‐327.el7.x86_64"
· when:
‐ ansible_distribution_version == "7.5"
‐ ansible_kernel == "3.10.0‐327.el7.x86_64"
# 组合
when: =>
( ansible_distribution == "RedHat" and ansible_distribution_major_version == "7" )
or
( ansible_distribution == "Fedora" and ansible_distribution_major_version == "28")
· is exist :判断路径是否存在,存在则为真。
· is not exist :判断路径是否存在,不存在则为真。
· defined:判断变量是否已定义,已定义则返回真。
· undefined:判断变量是否未定义,未定义则返回真。
· none:判断变量的值是否为空,如果变量已定义且值为空,则返回真。
示例1: 判断路径是否存在。
- hosts: test
gather_facts: no
vars:
testpath1: /client
testpath2: /route
tasks:
- name: 判断路径是否存在
debug:
msg: 'file is exist'
when: testpath1 is exists
- name: 判断路径是否存在
debug:
msg: 'file is not exist'
when: testpath2 is not exists
示例2: 判断变量是否被定义。
‐ hosts: test
gather_facts: no
vars:
testvar: "test"
testvar1:
tasks:
‐ debug:
msg: "testvar is defined"
when: testvar is defined
‐ debug:
msg: "testvar2 is undefined"
when: testvar2 is undefined
‐ debug:
msg: "testvar1 is none"
when: testvar1 is none
· sucess或succeeded:通过任务执行结果返回的信息判断任务的执行状态,任务执行成功则返回true
· failure或failed:任务执行失败则返回true
· change或changed:任务执行状态为changed则返回true
· skip或skipped:任务被跳过则返回true
- hosts: test
gather_facts: no
vars:
doshell: true
tasks:
- name: 执行shell语句
shell: cat /testdir/aaa
register: result
ignore_errors: true
when: doshell
- name: debug message1
debug:
msg: 'success'
when: result is success
- name: debug message2
debug:
msg: 'failed'
when: result is failed
- name: debug message3
debug:
msg: 'changed'
when: result is changed
- name: debug message4
debug:
msg: 'skip'
when: result is skip
· file : 判断指定路径是否为一个文件,是则为真;
· directory :判断指定路径是否为一个目录,是则为真;
· link :判断指定路径是否为一个软连接,是则为真;
· mount:判断指定路径是否为一个挂载点,是则为真;
· exists: 判断指定路径是否存在,存在则为真
注意: 关于路径的所有判断均是判断主控端上的路径,而非被控端上的路径。
- hosts: test
gather_facts: no
vars:
path1: "/root/ansible_test/ansible_2/hosts"
path2: "/root/ansible_test/ansible_2"
path3: "/usr/lib64/libedit.so.0"
path4: "/home"
path5: "/var/www123"
tasks:
- name: 判断指定路径是否为文件
debug:
msg: 'this path is file'
when: path1 is file
- name: 判断指定路径是否为目录
debug:
msg: 'this path is directory'
when: path2 is directory
- name: 判断指定路径是否为软连接
debug:
msg: 'this path is links'
when: path3 is link
- name: 判断指定路径是否为挂载点
debug:
msg: 'this path is mount'
when: path4 is mount
- name: 判断指定路径是否存在
debug:
msg: 'this path is exists'
when: path2 is exists
- name: 判断指定路径是否不存在
debug:
msg: 'this path is exists'
when: path5 is not exists
· lower :判断字符串中的所有字母是否为小写,是则为真;
· upper :判断字符串中的所有字母是否都是大写,是则为真。
注意: 字母键可以夹杂数字,判断的时候只会判断字母
- hosts: test
gather_facts: no
vars:
str1: "a12b43c"
str2: "J32DK09UV"
tasks:
- name: debug lower
debug:
msg: 'str1 is all lowercase'
when: str1 is lower
- name: debug upper
debug:
msg: "str2 is all uppercase"
when: str2 is upper
· even :判断数值是否为偶数,是则为真;
· odd :判断数值 是否为奇数,是则为真;
· divisibleby(num) :判断是否可以整除指定的数值,是则为真。
‐ hosts: test
gather_facts: no
vars:
num1: 6
num2: 8
num3: 15
tasks:
‐ debug:
msg: "num1 is an even number"
when: num1 is even
‐ debug:
msg: "num2 is an odd number"
when: num2 is odd
‐ debug:
msg: "num3 can be divided exactly by"
when: num3 is divisibleby(3)
可用于对比两个版本号的大小,或者与指定的版本号进行对比,使用语法为version("版本号","比 较操作符")
‐ hosts: test
vars:
ver1: 1.2
ver2: 1.3
tasks:
‐ debug:
msg: "ver1 is greater than ver2"
when: ver1 is version(ver2,">")
‐ debug:
msg: "system version {{ ansible_distribution_version }} greater than 7.3"
when: ansible_distribution_version is version("7.3","gt")
version中使用的比较运算符:
· 大于: >, gt
· 大于等于: >=, ge
· 小于:<, lt
· 等于: =, ==, eq
· 不等于: !=, <>, ne
subset 判断一个list是不是另一个list的子集
- hosts: test
gather_facts: no
vars:
list1: [1,2,3,4]
list2: [2,4]
tasks:
- name: debug massage
debug:
msg: 'list2 in list1'
when: list2 is subset(list1)
superset 判断一个list是不是另一个list的父集。
- hosts: test
gather_facts: no
vars:
list1: [1,2,3,4]
list2: [2,4]
tasks:
- name: debug massage
debug:
msg: 'list1 is list2 parent'
when: list1 is superset(list2)
in 判断一个字符串是否存在于另一个字符串中,也可用于判断某个特定的值是否存在于列表中 。
‐ hosts: test
vars:
supported_distros:
‐ RedHat
‐ CentOS
tasks:
‐ debug:
msg: "{{ ansible_distribution }} in supported_distros"
when: ansible_distribution in supported_distros
· string 判断对象是否为一个字符串,是则为真。
· number 判断对象是否为一个数字,是则为真。
‐ hosts: test
gather_facts: no
vars:
var1: 1
var2: "1"
var3: a
tasks:
‐ debug:
msg: "var1 is a number"
when: var1 is number
‐ debug:
msg: "var2 is a string"
when: var2 is string
‐ debug:
msg: "var3 is a string"
when: var3 is string
我们在前面使用when做条件判断时,如果条件成立则执行对应的任务。但这就面临一个问题,当我 们要使用同一个条件判断执行多个任务的时候,就意味着我们要在某一个任务下面都写一下when语 句,而且判断条件完全一样。这种方式不仅麻烦而且显得low。
Ansible提供了一种更好的方式来解决 这个问题,即block。 在ansible中,使用block将多个任务进行组合,当作一个整体。我们可以对这一个整体做条件判断, 当条件成立时,则执行块中的所有任务:
‐ hosts: test
tasks:
‐ debug:
msg: "task1 not in block"
‐ block:
‐ debug:
msg: "task2 in block1"
‐ debug:
msg: "task3 in block1"
when: 2 > 1
示例1: ubuntu上配置dns服务
‐ hosts: test
tasks:
‐ name: set /etc/resolv.conf
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
owner: root
group: root
mode: 0644
‐ block:
‐ name: ensure /etc/resolvconf/resolv.conf.d/base file for ubuntu 16.04
template:
src: resolv.conf.j2
dest: /etc/resolvconf/resolv.conf.d/base
‐ name: config dns for ubuntu 16.04
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version == "16"
使用block注意事项:
1. 可以为block定义name(ansible 2.3增加的特性)
2. 可以直接对block使用when,但不能直接对block使用loop
block除了能和when一起使用之外,还能作错误处理。这个时候就需要用到rescue关键字:
‐ hosts: test
tasks:
‐ block:
‐ shell: 'ls /testdir'
rescue:
‐ debug:
msg: '/testdir is not exists'
在上面的例子中,当block中的任务执行失败时,则运行rescue中的任务。如果block中的任务正常执 行,则rescue的任务就不会被执行。如果block中有多个任务,则任何一个任务执行失败,都会执行 rescue。block中可以定义多个任务,同样rescue当中也可以定义多个任务。
当block执行失败时,rescue中的任务才会被执行;而无论block执行成功还是失败,always中的任 务都会被执行:
‐ hosts: test
tasks:
‐ block:
‐ shell: 'ls /testdir'
rescue:
‐ debug:
msg: '/testdir is not exists'
always:
‐ debug:
msg: 'This task always executes'
在上面讲block的使用方法的时候,我们说block除了可以将多个任务组合到一起,还有错误处理的功 能。接下来我们继续说一说错误处理。
在shell中,可能会有这样的需求:当脚本执行至某个阶段时,需要对某个条件进行判断,如果条件成 立,则立即终止脚本的运行。在shell中,可以直接调用"exit"即可执行退出。事实上,在playbook中 也有类似的模块可以做这件事。即fail模块。
fail模块用于终止当前playbook的执行,通常与条件语句组合使用,当满足条件时,终止当前play的 运行。
选项只有一个:
· msg:终止前打印出信息
# 使用fail模块中断playbook输出
‐ hosts: test
tasks:
‐ shell: echo "Just a test‐‐error"
register: result
‐ fail:
msg: "Conditions established,Interrupt running playbook"
when: "'error' in result.stdout"
‐ debug:
msg: "Inever execute,Because the playbook has stopped"
事实上,当fail和when组合使用的时候,还有一个更简单的写法,即 failed_when ,当满足某个条件 时,ansible主动触发失败。
# 如果在command_result存在错误输出,且错误输出中,包含了`FAILED`字串,即返回失败状态:
‐ name: this command prints FAILED when it fails
command: /usr/bin/example‐command ‐x ‐y ‐z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
也可以直接通过 fail 模块和 when 条件语句,写成如下:
‐ name: this command prints FAILED when it fails
command: /usr/bin/example‐command ‐x ‐y ‐z
register: command_result
ignore_errors: True
‐ name: fail the play if the previous command did not succeed
fail: msg="the command failed"
when: " command_result.stderr and 'FAILED' in command_result.stderr"
ansible一旦执行返回失败,后续操作就会中止,所以failed_when通常可以用于满足某种条件时 主动中止playbook运行的一种方式。
ansible默认处理错误的机制是遇到错误就停止执行。但有些时候,有些错误是计划之中的。我 们希望忽略这些错误,以让playbook继续往下执行。这个时候就可以使用 ignore_errors 忽略 错误,从而让playbook继续往下执行。
当我们控制一些远程主机执行某些任务时,当任务在远程主机上成功执行,状态发生更改时,会返回 changed状态响应,状态未发生更改时,会返回OK状态响应,当任务被跳过时,会返回skipped状态 响应。我们可以通过 changed_when 来手动更改 changed 响应状态。示例如下:
- hosts: test
gather_facts: no
tasks:
- name: command
shell: ls -l /home
register: result
changed_when: result.rc == 2 #只有该条task执行以后,result.rc的值为2时,才
会返回changed状态(此时实际上result.rc == 0)
示例:只有当系统为ubuntu的时候才创建相应的用户
[root@clinet ansible_2]# cat vars_file/file1.yml
users:
- name: bob
shell: /sbin/nologin
home: /home/bob
state: present
- name: alice
home: /tmp/alice
state: present
- name: tom
shell: /bin/bash
state: present
[root@clinet ansible_2]# cat yum_file/with_x/with_x.yml
- hosts: mdb
gather_facts: no
vars_files:
- /root/ansible_test/ansible_2/vars_file/file1.yml
tasks:
- name: create user group
group:
name: "{{ item['name'] }}"
state: "{{ item['state'] }}"
with_items: "{{ users }}"
when: : ansible_distribution == "Ubuntu"
- name: create user with_items
user:
name: "{{ item['name'] }}"
shell: "{{ item['shell'] |default(omit) }}"
home: "{{ item['home'] |default(omit) }}"
state: "{{ item['state'] }}"
with_items: "{{ users }}"
when: : ansible_distribution == "Ubuntu"
[root@clinet ansible_2]#