zoukankan      html  css  js  c++  java
  • 011.Ansible条件语句

    一 简介

    在有的时候play的结果依赖于变量、fact或者是前一个任务的执行结果,或者有的时候,我们会基于上一个task执行返回的结果而决定如何执行后续的task。这个时候就需要用到条件判断。

    条件语句在Ansible中的使用场景:

    • 在目标主机上定义了一个硬限制,比如目标主机的最小内存必须达到多少,才能执行该task
    • 捕获一个命令的输出,根据命令输出结果的不同以触发不同的task
    • 根据不同目标主机的facts,以定义不同的task
    • 根据目标机的cpu的大小,以调优相关应用性能
    • 用于判断某个服务的配置文件是否发生变更,以确定是否需要重启服务

    二 when关键字

    2.1 when基本使用

    在ansible中,使用条件判断的关键字就是when。

    如在安装包的时候,需要指定主机的操作系统类型,或者是当操作系统的硬盘满了之后,需要清空文件等,可以使用when语句来做判断 。when关键字后面跟着的是python的表达式,在表达式中你能够使用任何的变量或者fact,当表达式的结果返回的是false,便会跳过本次的任务

    基本用法

    [root@node1 ansible]# vi when_ex.yml

    - name: Install vim
      hosts: all
      tasks:
        - name: Install VIM via yum
          yum: 
            name: vim-enhanced 
            state: installed
          when: ansible_os_family =="RedHat"
          
        - name: Install VIM via apt
          apt: 
            name: vim 
            state: installed
          when: ansible_os_family =="Debian"
          
        - name: Unexpected OS family
          debug: msg="OS Family {{ ansible_os_family }} is not supported"
          failed_when: not ansible_os_family =="RedHat" or ansible_os_family =="Debian"

    结果

    比较运算符

    在上面的示例当中,我们使用了"=="的比较运算符,在ansible中,还支持如下比较运算符:

    ==:比较两个对象是否相等,相等则返回真。可用于比较字符串和数字
    !=:比较两个对象是否不等,不等则为真。
    >:比较两个对象的大小,左边的值大于右边的值,则为真
    <:比较两个对象的大小,左边的值小于右边的值,则为真
    >=:比较两个对象的大小,左边的值大于等于右边的值,则为真
    <=:比较两个对象的大小,左边的值小于等于右边的值,则为真

    逻辑运算符

    在Ansible中,除了比较运算符,还支持逻辑运算符:

    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")

    完整的例子:

    # 判断register注册变量的返回结果
    - name: restart httpd if postfix is running
      hosts: test
      tasks:
        - name: get postfix server status
          command: /usr/bin/systemctl is-active postfix
          ignore_errors: yes
          register: result
          
        - name: restart apache httpd based on postfix status
          service:
            name: httpd
            state: restarted
          when: result.rc == 0

    2.2 条件判断与tests

    在shell当中,我们可使用test命令来进行一些常用的判断操作,如下:

    # 判断/test文件是否存在
    test -e /test
    
    # 判断/testdir是否存在且为一个目录
    test -d /testdir

    事实上,在ansible中也有类似的用法,只不过ansible没有使用linux的test命令,而是jinja2模板的tests。

    下面是一个简单示例:

    # 通过条件语句判断testpath的路径是否存在
    - hosts: test
      vars:
        testpath: /testdir
      tasks:
        - debug:
            msg: "file exist"
          when: testpath is exists

    上面的示例中,我们使用了is exists用于路径存在时返回真,也可以使用is not exists用于路径不存在时返回真。也可以在整个条件表达式的前面使用not以取反:

    - hosts: test
      vars:
        testpath: /testdir1
      tasks:
        - debug:
            msg: "file not exist"
          when: not testpath is exists

    在ansible中,除了能够使用exists这种tests之外,还有一些别的tests

    2.3 判断变量

    • defined:判断变量是否已定义,已定义则返回真
    • undefined:判断变量是否未定义,未定义则返回真
    • none:判断变量的值是否为空,如果变量已定义且值为空,则返回真
    - hosts: demo2.example.com
      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

    执行结果

    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "testvar is defined"
    }
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "testvar2 is undefined"
    }
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "testvar1 is none"
    }

    2.4 判断执行结果

    • sucess或succeeded:通过任务执行结果返回的信息判断任务的执行状态,任务执行成功则返回true
    • failure或failed:任务执行失败则返回true
    • change或changed:任务执行状态为changed则返回true
    • skip或skipped:任务被跳过则返回true

    查看result结果

    [root@node1 ansible]# vim when_ex.yml

    - hosts: demo2.example.com
      gather_facts: no
      vars:
        doshell: true
      tasks:
        - shell: 'cat /testdir/aaa'
          when: doshell
          register: result
          ignore_errors: true
        - debug:
            msg: "{{ result }}"

    [root@node1 ansible]# ansible-playbook when_ex.yml

    PLAY [demo2.example.com] **************************************************************************************************************************
    
    TASK [shell] **************************************************************************************************************************************
    fatal: [demo2.example.com]: FAILED! => {"changed": true, "cmd": "cat /testdir/aaa", "delta": "0:00:00.005672", "end": "2020-05-02 05:47:40.484154", "msg": "non-zero return code", "rc": 1, "start": "2020-05-02 05:47:40.478482", "stderr": "cat: /testdir/aaa: No such file or directory", "stderr_lines": ["cat: /testdir/aaa: No such file or directory"], "stdout": "", "stdout_lines": []}
    ...ignoring
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": {
            "changed": true, 
            "cmd": "cat /testdir/aaa", 
            "delta": "0:00:00.005672", 
            "end": "2020-05-02 05:47:40.484154", 
            "failed": true, 
            "msg": "non-zero return code", 
            "rc": 1, 
            "start": "2020-05-02 05:47:40.478482", 
            "stderr": "cat: /testdir/aaa: No such file or directory", 
            "stderr_lines": [
                "cat: /testdir/aaa: No such file or directory"
            ], 
            "stdout": "", 
            "stdout_lines": []
        }
    }

    示例

    - hosts: demo2.example.com
      gather_facts: no
      vars:
        doshell: true
      tasks:
        - shell: 'cat /testdir/aaa'
          when: doshell
          register: result
          ignore_errors: true
        - debug:
            msg: "{{ result }}"
        - debug:
            msg: "success"
          when: result is success
          
        - debug:
            msg: "failed"
          when: result is failure
          
        - debug:
            msg: "changed"
          when: result is change
          
        - debug:
            msg: "skip"
          when: result is skip

    执行

    LAY [demo2.example.com] **************************************************************************************************************************
    
    TASK [shell] **************************************************************************************************************************************
    fatal: [demo2.example.com]: FAILED! => {"changed": true, "cmd": "cat /testdir/aaa", "delta": "0:00:00.005698", "end": "2020-05-02 05:50:55.314509", "msg": "non-zero return code", "rc": 1, "start": "2020-05-02 05:50:55.308811", "stderr": "cat: /testdir/aaa: No such file or directory", "stderr_lines": ["cat: /testdir/aaa: No such file or directory"], "stdout": "", "stdout_lines": []}
    ...ignoring
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": {
            "changed": true, 
            "cmd": "cat /testdir/aaa", 
            "delta": "0:00:00.005698", 
            "end": "2020-05-02 05:50:55.314509", 
            "failed": true, 
            "msg": "non-zero return code", 
            "rc": 1, 
            "start": "2020-05-02 05:50:55.308811", 
            "stderr": "cat: /testdir/aaa: No such file or directory", 
            "stderr_lines": [
                "cat: /testdir/aaa: No such file or directory"
            ], 
            "stdout": "", 
            "stdout_lines": []
        }
    }
    
    TASK [debug] **************************************************************************************************************************************
    skipping: [demo2.example.com]
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "failed"
    }
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "changed"
    }

    2.5 基于注册变量的过滤器

    正常情况下,当某个task执行失败的时候,ansible会中止运行。此时我们可以通过ignore_errors来捕获异常以让task继续往下执行。然后调用debug模块打印出出错时的内容,拿来错误结果后,主动失败。

    - name: Run myprog
      command: /opt/myprog
      register: result
      ignore_errors: True
      
    - debug: 
        var: result
    
    - debug: 
        msg: "Stop running the playbook if myprog failed"
        failed_when: result|failed

    任务返回值过滤器:

    • failed: 如果注册变量的值是任务failed则返回True
    • changed: 如果注册变量的值是任务changed则返回True
    • success:如果注册变量的值是任务succeeded则返回True
    • skipped:如果注册变量的值是任务skipped则返回True

    2.6 判断路径

    • file:判断指定路径是否为一个文件,是则为真
    • directory:判断指定路径是否为一个目录,是则为真
    • link:判断指定路径是否为一个软链接,是则为真
    • mount:判断指定路径是否为一个挂载点,是则为真
    • exists:判断指定路径是否存在,存在则为真
    - hosts: demo2.example.com
      gather_facts: no
      vars:
        testpath1: "/testdir/test"
        testpath2: "/testdir"
      tasks:
        - debug:
            msg: "file"
          when: testpath1 is file
        - debug:
            msg: "directory"
          when: testpath2 is directory

    2.7 判断字符串

    • lower:判断字符串中的所有字母是否都是小写,是则为真
    • upper:判断字符串中的所有字母是否都是大写,是则为真
    - hosts: demo2.example.com
      gather_facts: no
      vars: 
        str1: "abc"
        str2: "ABC"
      tasks:
        - debug:
            msg: "str1 is all lowercase"
          when: str1 is lower
        - debug:
            msg: "str2 is all uppercase"
          when: str2 is upper

    执行结果

    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "str1 is all lowercase"
    }
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "str2 is all uppercase"
    }

    2.8 判断整除

    • even:判断数值是否为偶数,是则为真
    • odd:判断数值是否为奇数,是则为真
    • divisibleby(num):判断是否可以整除指定的数值,是则为真
    - hosts: demo2.example.com
      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)

    执行结果

    PLAY [demo2.example.com] **************************************************************************************************************************
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "num1 is an even number"
    }
    
    TASK [debug] **************************************************************************************************************************************
    skipping: [demo2.example.com]
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "num3 can be divided exactly by"
    }

    2.9 其他tests

    version

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

    - hosts: demo2.example.com
      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
    • 小于等于: <=, le
    • 等于: =, ==, eq
    • 不等于: !=, <>, ne

    执行结果

    TASK [debug] **************************************************************************************************************************************
    skipping: [demo2.example.com]
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "system version 7.7 greater than 7.3"
    }

    in
    判断一个字符串是否存在于另一个字符串中,也可用于判断某个特定的值是否存在于列表中

    - hosts: demo2.example.com
      vars:
        supported_distros:
          - RedHat
          - CentOS
      tasks:
        - debug:
            msg: "{{ ansible_distribution }} in supported_distros"
          when: ansible_distribution in supported_distros

    执行结果

    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": "CentOS in supported_distros"
    }

    2.10 条件判断与block

    block

    我们在前面使用when做条件判断时,如果条件成立则执行对应的任务。但这就面临一个问题,当我们要使用同一个条件判断执行多个任务的时候,就意味着我们要在某一个任务下面都写一下when语句,而且判断条件完全一样。这种方式不仅麻烦而且显得low。Ansible提供了一种更好的方式来解决这个问题,即block。

    在ansible中,使用block将多个任务进行组合,当作一个整体。我们可以对这一个整体做条件判断,当条件成立时,则执行块中的所有任务:

    - 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

    rescue

    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当中也可以定义多个任务。

    always

    当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'

    2.11 条件判断与错误处理

    在上面讲block的使用方法的时候,我们说block除了可以将多个任务组合到一起,还有错误处理的功能。接下来我们继续说一说错误处理。

    fail模块

    在shell中,可能会有这样的需求:当脚本执行至某个阶段时,需要对某个条件进行判断,如果条件成立,则立即终止脚本的运行。在shell中,可以直接调用"exit"即可执行退出。事实上,在playbook中也有类似的模块可以做这件事。即fail模块。

    fail模块用于终止当前playbook的执行,通常与条件语句组合使用,当满足条件时,终止当前play的运行。

    选项只有一个:

    • msg:终止前打印出信息
    - hosts: demo2.example.com
      tasks:
        - shell: echo "Just a test--error" 
          register: result
        - debug:
            msg: "{{ result }}"
        - fail:
            msg: "Conditions established,Interrupt running playbook"
          when: "'error' in result.stdout"
        - debug:
            msg: "Inever execute,Because the playbook has stopped"

    执行输出

    TASK [shell] **************************************************************************************************************************************
    changed: [demo2.example.com]
    
    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": {
            "changed": true, 
            "cmd": "echo "Just a test--error"", 
            "delta": "0:00:00.004205", 
            "end": "2020-05-02 06:51:25.377763", 
            "failed": false, 
            "rc": 0, 
            "start": "2020-05-02 06:51:25.373558", 
            "stderr": "", 
            "stderr_lines": [], 
            "stdout": "Just a test--error", 
            "stdout_lines": [
                "Just a test--error"
            ]
        }
    }
    
    TASK [fail] ***************************************************************************************************************************************
    fatal: [demo2.example.com]: FAILED! => {"changed": false, "msg": "Conditions established,Interrupt running playbook"}

    failed_when

    事实上,当fail和when组合使用的时候,还有一个更简单的写法,即failed_when,当满足某个条件时,ansible主动触发失败

    - hosts: demo2.example.com
      tasks:
        - shell: echo "Just a test--error" 
          register: result
        - debug:
            msg: "{{ result }}"
        - debug:
            msg: "Conditions established,Interrupt running playbook"
          failed_when: "'error' in result.stdout"
        - debug:
            msg: "Inever execute,Because the playbook has stopped"

    执行输出

    TASK [debug] **************************************************************************************************************************************
    ok: [demo2.example.com] => {
        "msg": {
            "changed": true, 
            "cmd": "echo "Just a test--error"", 
            "delta": "0:00:00.005333", 
            "end": "2020-05-02 06:55:35.930654", 
            "failed": false, 
            "rc": 0, 
            "start": "2020-05-02 06:55:35.925321", 
            "stderr": "", 
            "stderr_lines": [], 
            "stdout": "Just a test--error", 
            "stdout_lines": [
                "Just a test--error"
            ]
        }
    }
    
    TASK [debug] **************************************************************************************************************************************
    fatal: [demo2.example.com]: FAILED! => {
        "msg": "Conditions established,Interrupt running playbook"
    }

    changed_when

    当我们控制一些远程主机执行某些任务时,当任务在远程主机上成功执行,状态发生更改时,会返回changed状态响应,状态未发生更改时,会返回OK状态响应,当任务被跳过时,会返回skipped状态响应。我们可以通过changed_when来手动更改changed响应状态。


    博主声明:本文的内容来源主要来自誉天教育晏威老师,由本人实验完成操作验证,需要的博友请联系誉天教育(http://www.yutianedu.com/),获得官方同意或者晏老师(https://www.cnblogs.com/breezey/)本人同意即可转载,谢谢!

  • 相关阅读:
    webSQL 实现即时通讯
    微信开发(微信公众号)
    回顾 git 常用命令
    面向对象的 javascript
    关于HTML、js加密、混淆、源码保护、代码安全,防止解压直接看源码
    svn 的使用
    css新单位 vw , vh
    图片转成base64, base64转成图片
    EL表达式
    jsp页面获取集合的长度
  • 原文地址:https://www.cnblogs.com/zyxnhr/p/12818126.html
Copyright © 2011-2022 走看看