ansible学习日记-条件判断(7)

参考:ansible笔记(29):条件判断与错误处理-朱双印博客

一、条件判断when

绝大多数语言中,都使用”if”作为条件判断的关键字,而在ansible中,条件判断的关键字是”when”,我们来看一个简单的示例,如下:

---
- hosts: test70
  remote_user: root
  tasks:
  - debug:
      msg: "System release is centos"
    when: ansible_distribution == "CentOS"

如上例所示,使用when关键字指明条件,条件是ansible_distribution的值是CentOS,细心如你一定已经发现了,ansible_distribution就是facts信息中的一个key,通过ansible_distribution可以获取到目标主机系统的发行版,在之前的文章中,如果我们需要获取到facts中的key的值,都是通过引用变量的方式获取的,即”{{ key }}”,但是,在使用when关键字时,我们并没有为ansible_distribution添加”{{  }}”,没错,在when关键字中引用变量时,变量名不需要加”{{  }}”, 条件满足就执行这个模块。我们可以使用when关键字为任务指定条件,条件成立,则执行任务,条件不成立,则不执行任务

示例2:

---
- hosts: test70
  remote_user: root
  gather_facts: no
  tasks:
  - debug:
      msg: "{{ item }}"
    with_items:
    - 1
    - 2
    - 3
    when: item > 1

上例表示当item的值大于1时,才会调用debug模块输出对应的信息

在上述两个示例中,我们使用了 “==” 和 “>” 两个比较运算符,在ansible中,我们可以使用如下比较运算符。

==  :比较两个对象是否相等,相等为真

!=  :比较两个对象是否不等,不等为真

>   :比较两个值的大小,如果左边的值大于右边的值,则为真

:比较两个值的大小,如果左边的值小于右边的值,则为真

>=  :比较两个值的大小,如果左边的值大于右边的值或左右相等,则为真

<=  :比较两个值的大小,如果左边的值小于右边的值或左右相等,则为真

我们总结的这些运算符其实都是jinja2的运算符,ansible使用jinja2模板引擎,在ansible中也可以直接使用jinja2的这些运算符。

说完了比较运算符,再来说说逻辑运算符,可用的逻辑运算符如下

and  :逻辑与,当左边与右边同时为真,则返回真

or  :逻辑或,当左边与右边有任意一个为真,则返回真

not  :取反,对一个操作体取反

( )  :组合,将一组操作体包装在一起,形成一个较大的操作体

我们来看一些关于逻辑运算符的示例,如下:

---

- hosts: test70

remote_user: root

tasks:

- debug:

msg: "System release is centos7"

when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "7"

其实,当我们需要使用”逻辑与”时,除了使用”and”这种写法,还能够使用另一种”列表”的写法,示例如下(列表中同时成立才执行):

---
- hosts: test70
  remote_user: root
  tasks:
  - debug:
      msg: "System release is centos7"
    when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "7"

当我们调用shell模块运行命令时,通常需要获取到shell模块的返回信息,以便之后的模块能够根据返回信息的值判断之后进行怎样的操作,示例如下,如下示例存在一个问题:

---
- hosts: test70
  remote_user: root
  tasks:
  - name: task1
    shell: "ls /testabc"
    register: returnmsg
  - name: task2
    debug:
      msg: "Command execution successful"
    when: returnmsg.rc == 0
  - name: task3
    debug:
      msg: "Command execution failed"
    when: returnmsg.rc != 0

我们的想法是如果执行不成功,那就debug会输出Command execution failed"。但是ansible特性是执行失败就直接结束了。并不会执行后面的task2和task3

很简单,通过”ignore_errors”关键字即可实现这种效果,”ignore_errors”表示即使当前task执行报错,ansible也会忽略这个错误,继续执行playbook,示例如下:

---
- hosts: test70
  remote_user: root
  tasks:
  - name: task1
    shell: "ls /testabc"
    register: returnmsg
    ignore_errors: true
  - name: task2
    debug:
      msg: "Command execution successful"
    when: returnmsg.rc == 0
  - name: task3
    debug:
      msg: "Command execution failed"
    when: returnmsg.rc != 0

二、条件判断与tests

在linux中,我们可以使用test命令进行一些常用的判断操作,比如,使用test命令判断”/testdir”是否存在,示例如下

# test -e /testdir

# echo $?

0

其实,在ansible中,也有类似的用法,只不过ansible没有使用linux的test命令,而是使用了jinja2的tests,借助tests,我们可以进行一些判断操作,tests会将判断后的布尔值返回,如果条件成立,则返回true,如果条件不成立,tests会返回false,示例如下:

---
- hosts: test70
  remote_user: root
  gather_facts: no
  vars:
    testpath: /testdir
  tasks:
  - debug:
      msg: "file exist"
    when: testpath is exists

如上例所示,我们定义了一个testpath变量,这个变量的值是”/testdir”路径,我通过when判断”/testdir”路径是否存在,没错,就是这么简单,”is exists”中的”exists”就是tests的一种,它与”test -e”命令的作用是相同的,通过”exists”可以判断ansible主机中的对应路径是否存在(注意:是ansible控制主机中的路径,与目标主机没有关系),当对应的路径存在于ansible控制节点时,”is exists”为真,是不是很简单?

“is exists”可以在路径存在时返回真,但是有时,我们想要在路径不存在时返回真,我们该怎么办呢?我们可以使用”is not exists”,”is not exists”表示对应路径不存在时返回真

判断变量的一些tests

defined :判断变量是否已经定义,已经定义则返回真

undefind :判断变量是否已经定义,未定义则返回真

none :判断变量值是否为空,如果变量已经定义,但是变量值为空,则返回真

判断执行结果的一些tests

success 或 succeeded:通过任务的返回信息判断任务的执行状态,任务执行成功则返回真

failure 或 failed:通过任务的返回信息判断任务的执行状态,任务执行失败则返回真

change 或 changed:通过任务的返回信息判断任务的执行状态,任务执行状态为changed则返回真

skip 或 skipped:通过任务的返回信息判断任务的执行状态,当任务没有满足条件,而被跳过执行时,则返回真

示例:
 

---
- hosts: test70
  remote_user: root
  gather_facts: no
  vars:
    doshell: "yes"
  tasks:
  - shell: "cat /testdir/abc"
    when: doshell == "yes"
    register: returnmsg
    ignore_errors: true
  - debug:
      msg: "success"
    when: returnmsg is success
  - debug:
      msg: "failed"
    when: returnmsg is failure
  - debug:
      msg: "changed"
    when: returnmsg is change
  - debug:
      msg: "skip"
    when: returnmsg is skip

判断路径的一些tests

注:如下tests的判断均针对于ansible主机中的路径,与目标主机无关

file : 判断路径是否是一个文件,如果路径是一个文件则返回真

directory :判断路径是否是一个目录,如果路径是一个目录则返回真

link :判断路径是否是一个软链接,如果路径是一个软链接则返回真

mount:判断路径是否是一个挂载点,如果路径是一个挂载点则返回真

exists:判断路径是否存在,如果路径存在则返回真

示例:

---
- hosts: test70
  remote_user: root
  gather_facts: no
  vars:
    testpath1: "/testdir/test"
    testpath2: "/testdir/"
    testpath3: "/testdir/testsoftlink"
    testpath4: "/testdir/testhardlink"
    testpath5: "/boot"
  tasks:
  - debug:
      msg: "file"
    when: testpath1 is file
  - debug:
      msg: "directory"
    when: testpath2 is directory
  - debug:
      msg: "link"
    when: testpath3 is link
  - debug:
      msg: "link"
    when: testpath4 is link
  - debug:
      msg: "mount"
    when: testpath5 is mount
  - debug:
      msg: "exists"
    when: testpath1 is exists

判断字符串的一些tests

lower:判断包含字母的字符串中的字母是否是纯小写,字符串中的字母全部为小写则返回真

upper:判断包含字母的字符串中的字母是否是纯大写,字符串中的字母全部为大写则返回真

示例:

---
- hosts: test70
  remote_user: root
  gather_facts: no
  vars:
    str1: "abc"
    str2: "ABC"
  tasks:
  - debug:
      msg: "This string is all lowercase"
    when: str1 is lower
  - debug:
      msg: "This string is all uppercase"
    when: str2 is upper

判断整除的一些tests

even :判断数值是否是偶数,是偶数则返回真

odd :判断数值是否是奇数,是奇数则返回真

divisibleby(num) :判断是否可以整除指定的数值,如果除以指定的值以后余数为0,则返回真

其他的一些testst

version:可以用于对比两个版本号的大小,或者与指定的版本号进行对比,使用语法为 version(‘版本号’, ‘比较操作符’)

示例:

---
- hosts: test70
  remote_user: root
  vars:
    ver: 7.4.1708
    ver1: 7.4.1707
  tasks:
  - debug:
      msg: "This message can be displayed when the ver is greater than ver1"
    when: ver is version(ver1,">")
  - debug:
      msg: "system version {{ansible_distribution_version}} greater than 7.3"
    when: ansible_distribution_version is version("7.3","gt")

上例中有两个task

第一个task中,当ver的版本号大于ver1时,返回真,条件成立,debug模块输出”This message can be displayed when the ver is greater than ver1″

第二个task中,当facts中的ansible_distribution_version的值大于7.3时,返回真,条件成立,debug模块输出对应信息

细心如你一定发现了,”>”与”gt”都表示”大于”,当使用version时,支持多种风格的比较操作符,你可以根据自己的使用习惯进行选择,version支持的比较操作符如下

大于:>, gt

大于等于:>=, ge

小于:<, lt

小于等于:<=, le

等于: ==, =, eq

不等于:!=, <>, ne

subset:判断一个list是不是另一个list的子集,是另一个list的子集时返回真

superset : 判断一个list是不是另一个list的父集,是另一个list的父集时返回真

注:2.5版本中上述两个tests从issubset和issuperset更名为subset和superset

示例如下

---

- hosts: test70

remote_user: root

gather_facts: no

vars:

a:

- 2

- 5

b: [1,2,3,4,5]

tasks:

- debug:

msg: "A is a subset of B"

when: a is subset(b)

- debug:

msg: "B is the parent set of A"

when: b is superset(a)


string:判断对象是否是一个字符串,是字符串则返回真

---

- hosts: test70

remote_user: root

gather_facts: no

vars:

testvar1: 1

testvar2: "1"

testvar3: a

tasks:

- debug:

msg: "This variable is a string"

when: testvar1 is string

- debug:

msg: "This variable is a string"

when: testvar2 is string

- debug:

msg: "This variable is a string"

when: testvar3 is string

上例playbook中只有testvar2和testvar3会被判断成字符串,testvar1不会


number:判断对象是否是一个数字,是数字则返回真

示例如下

---

- hosts: test70

remote_user: root

gather_facts: no

vars:

testvar1: 1

testvar2: "1"

testvar3: 00.20

tasks:

- debug:

msg: "This variable is number"

when: testvar1 is number

- debug:

msg: "This variable is a number"

when: testvar2 is number

- debug:

msg: "This variable is a number"

when: testvar3 is number

上例playbook中只有testvar1和testvar3会被判断成数字,testvar2不会

三、block

前文中,我们使用”when”关键字对条件进行判断,如果条件成立,则执行对应的任务,但是,细心如你一定已经发现了,当条件成立时,我们只能执行一个任务,如果我们想要在条件成立时,执行三个任务,该怎么办呢?难道我们要在这三个任务的每个任务中都加入相同的条件判断么?这种方法也太麻烦了,显然应该有更好的方法,没错,我们可以借助”block”解决这个小问题。

在ansible中,可以使用”block”关键字将多个任务整合成一个”块”,这个”块”将被当做一个整体,我们可以对这个”块”添加判断条件,当条件成立时,则执行这个块中的所有任务,我们来看一个小示例,如下:

---
- hosts: test70
  remote_user: root
  tasks:
  - debug:
      msg: "task1 not in block"
  - block:
      - debug:
          msg: "task2 in block1"
      - debug:
          msg: "task3 in block1"
    when: 2 > 1

task2与task3在一个block中

block除了整合多个task与when连用外,还可以支持错误处理。rescue

---
- hosts: test70
  remote_user: root
  tasks:
  - block:
      - shell: 'ls /ooo'
    rescue:
      - debug:
          msg: 'I caught an error'

block中有多个任务时,这种优势就比较明显了,我们来看一个小示例,如下(任何一个出错就报错):

---
- hosts: test70
  remote_user: root
  tasks:
  - block:
      - shell: 'ls /opt'
      - shell: 'ls /testdir'
      - shell: 'ls /c'
    rescue:
      - debug:
          msg: 'I caught an error'

你一定已经理解了,我们来扩展一下,上例中只使用到了block与rescue关键字,其实,我们还能够再加入always关键字,加入always关键字以后,无论block中的任务执行成功还是失败,always中的任务都会被执行,示例如下:

---
- hosts: test70
  remote_user: root
  tasks:
  - block:
      - debug:
          msg: 'I execute normally'
      - command: /bin/false
      - debug:
          msg: 'I never execute, due to the above task failing'
    rescue:
      - debug:
          msg: 'I caught an error'
      - command: /bin/false
      - debug:
          msg: 'I also never execute'
    always:
      - debug:
          msg: "This always executes"

如上例所示,block中有多个任务,rescue中也有多个任务,上例中故意执行”/bin/false”命令,模拟任务出错的情况,当block中的’/bin/false’执行后,其后的debug任务将不会被执行,因为’/bin/false’模拟出错,出错后直接执行rescue中的任务,在执行rescue中的任务时,会先输出 ‘I caught an error’,然后又在rescue中使用’/bin/false’模拟出错的情况,出错后之后的debug任务不会被执行,直接执行always中的任务,always中的任务一定会被执行,无论block中的任务是否出错

四、fail模块

在编写shell脚本时,有可能会有这样的需求,当脚本执行到某个阶段时,需要对某个条件进行判断,如果条件成立,则立即终止脚本的运行,在shell脚本中实现这个需求很简单,只需要在条件成立时调用”exit”命令即可终止脚本的运行, 那么在编写playbook时,如果有类似的需求,我们该怎么办呢?

想要在playbook中按照我们的意愿中断剧本的执行,其实也很简单,我们只需要借助一个模块即可完成,这个模块就是”fail”模块。

我们知道,在执行playbook时,如果playbook中的任何一个任务执行失败,playbook都会停止运行,除非这个任务设置了”ignore_errors: true”,在任务没有设置”ignore_errors: yes”的情况下,任务执行失败后,playbook就会自动终止,而fail模块天生就是一个用来”执行失败”的模块,当fail模块执行后,playbook就会认为有任务失败了,从而终止运行,实现我们想要的中断效果,来看一个小示例:

---
- hosts: all
  remote_user: root
  tasks:
  - debug:
      msg: "1"
  - debug:
      msg: "2"
  - fail:
  - debug:
      msg: "3"
  - debug:
      msg: "4"

执行结果:

[root@ansible-master ~]# ansible-playbook if.yml

PLAY [all] **************************************************************************************

TASK [Gathering Facts] **************************************************************************
ok: [192.168.174.145]
ok: [192.168.174.144]

TASK [debug] ************************************************************************************
ok: [192.168.174.144] => {
    "msg": "1"
}
ok: [192.168.174.145] => {
    "msg": "1"
}

TASK [debug] ************************************************************************************
ok: [192.168.174.144] => {
    "msg": "2"
}
ok: [192.168.174.145] => {
    "msg": "2"
}

TASK [fail] *************************************************************************************
fatal: [192.168.174.144]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}
fatal: [192.168.174.145]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}

PLAY RECAP **************************************************************************************
192.168.174.144            : ok=3    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
192.168.174.145            : ok=3    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

[root@ansible-master ~]#

从上图可以看出,当前两个debug模块输出了对应的信息后,playbook报错了,之后的debug模块并未被调用,实现了中断剧本运行的效果,当执行fail模块时,fail模块默认的输出信息为’Failed as requested from task’,我们可以通过fail模块的msg参数自定义报错的信息,示例如下:

---
- hosts: test70
  remote_user: root
  tasks:
  - debug:
      msg: "1"
  - fail:
      msg: "Interrupt running playbook"
  - debug:
      msg: "2"

当然,上述示例只是为了初步介绍fail模块的用法,我们通常并不会毫无理由的想要去中断playbook,通常需要对某些条件进行判断,如果条件满足,则中断剧本,所以,fail模块通常与when结合使用,比如,如果之前模块执行后的标准输出信息中包含字符串’error’,则认为中断剧本的条件成立,就立即调用fail模块,以终断playbook,示例如下:

---
- hosts: test70
  remote_user: root
  tasks:
  - shell: "echo 'This is a string for testing--error'"
    register: return_value
  - fail:
      msg: "Conditions established,Interrupt running playbook"
    when: "'error' in return_value.stdout"
  - debug:
      msg: "I never execute,Because the playbook has stopped"

或者:

when: ' "successful" not in return_value.stdout '
when: " 'successful' not in return_value.stdout "

五、failed_when

看完上述示例,你一定已经明白了怎样在条件满足时终止playbook,其实,还有另一种方法可以实现类似的效果,我们可以借助’failed_when’关键字来完成类似功能,’failed_when’的作用就是,当对应的条件成立时,将对应任务的执行状态设置为失败,这样说可能不是特别容易理解,不如先来看一个小示例,如下:

---
- hosts: test70
  remote_user: root
  tasks:
  - debug:
      msg: "I execute normally"
  - shell: "echo 'This is a string for testing error'"
    register: return_value
    failed_when: ' "error" in return_value.stdout'
  - debug:
      msg: "I never execute,Because the playbook has stopped"

但是需要注意的时,’ failed_when’虽然会将任务的执行状态设置为失败,但是并不代表任务真的失败了,就以上例来说,上例的shell模块的确是完全正常的执行了,只不过在执行之后,’ failed_when’对应的条件成立了,’ failed_when’将shell模块的执行状态设置为失败而已,所以,’ failed_when’并不会影响shell模块的执行过程,只会在条件成立时影响shell模块最终的执行状态,以便停止playbook的运行。

六、changed_when

理解了’ failed_when’关键字以后,顺势理解’changed_when’关键字就容易多了。

‘ failed_when’关键字的作用是在条件成立时,将对应任务的执行状态设置为失败

‘changed_when’关键字的作用是在条件成立时,将对应任务的执行状态设置为changed

示例:

---
- hosts: test70
  remote_user: root
  tasks:
  - debug:
      msg: "test message"
    changed_when: 2 > 1

执行结果:

[root@ansible-master ~]# ansible-playbook if.yml

PLAY [all] **************************************************************************************

TASK [Gathering Facts] **************************************************************************
ok: [192.168.174.144]
ok: [192.168.174.145]

TASK [debug] ************************************************************************************
changed: [192.168.174.144] => {
    "msg": "test message"
}
changed: [192.168.174.145] => {
    "msg": "test message"
}

PLAY RECAP **************************************************************************************
192.168.174.144            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
192.168.174.145            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@ansible-master ~]#

我们知道,debug模块在正常执行的情况下只能是”ok”状态,上例中,我们使用’changed_when’关键字将debug模块的执行后的状态定义为了”changed”

总结过handlers的用法,我们知道,只有任务作出了实际的操作时(执行后状态为changed),才会真正的执行对应的handlers,而在某些时候,如果想要通过任务执行后的返回值将任务的最终执行状态判定为changed,则可以使用’changed_when’关键字,以便条件成立时,可以执行对应的handlers,其实,’changed_when’除了能够在条件成立时将任务的执行状态设置为”changed”,还能让对应的任务永远不能是changed状态,示例如下:

---
- hosts: test70
  remote_user: root
  tasks:
  - shell: "ls /opt"
    changed_when: false

当将’changed_when’直接设置为false时,对应任务的状态将不会被设置为’changed’,如果任务原本的执行状态为’changed’,最终则会被设置为’ok’,所以,上例playbook执行后,shell模块的执行状态最终为’ok’

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值