Ansible的Jinja test

文章目录

  • 环境
  • 概述
  • 语法
  • 字符串
  • Vault
  • 真假性
  • 版本比较
  • 子集和超集
  • list里的元素
  • list的“所有元素都是true”和“至少一个元素是true”
  • 路径
  • human_readable
  • task结果
  • 类型
  • 参考

环境

  • 管理节点:Ubuntu 22.04
  • 控制节点:CentOS 8
  • Ansible:2.15.6

概述

Jinja test是指计算一个表达式并返回 truefalse

同所有的template一样,test是运行在控制节点上,而不是受控节点上。

语法

is

比如: result is failed

字符串

我们经常需要用正则表达式来匹配字符串。可用如下test:

  • match :从起始位置匹配
  • search :从任意位置匹配
  • regex :类似 search ,可添加 match_type 参数

比如:

---
- hosts: all
  vars:
    var1: "abcdefghijklmn"

  tasks:
    - name: task1
      debug:
        msg: "OK"
      when: var1 is match("abc") # OK

    - name: task2
      debug:
        msg: "OK"
      when: var1 is match ("^abc") # OK

    - name: task3
      debug:
        msg: "OK"
      when: var1 is match ("abc.*mn$") # OK

    - name: task4
      debug:
        msg: "OK"
      when: var1 is match ("mn") # Skip

    - name: task5
      debug:
        msg: "OK"
      when: var1 is search ("mn") # OK

    - name: task6
      debug:
        msg: "OK"
      when: var1 is regex ("mn") # OK

上述task中,只有task4不满足条件。

此外, matchsearchregex 方法都可以添加 ignorecasemultiline 参数。

  • ignorecase :忽略大小写,默认值为False
  • multiline :多行,默认值为False

比如:

---
- hosts: all
  vars:
    var1: "abcdefg\nhijklmn"
  tasks:
    - name: task1
      debug:
        msg: "OK"
      when: var1 is match("ABC") # Skip

    - name: task2
      debug:
        msg: "OK"
      when: var1 is match("ABC", ignorecase=True) # OK

    - name: task3
      debug:
        msg: "OK"
      when: var1 is search("^hi") # Skip

    - name: task4
      debug:
        msg: "OK"
      when: var1 is search("^hi", multiline=True) # OK

本例中, ^hi 匹配了第二行的开头,若 multiline 设置为True,则匹配成功。

Vault

  • vault_encrypted :测试是否为加密的vault值

比如:

---
- hosts: all
  vars:
    var1: "abcdefg"
    var2: !vault |
      $ANSIBLE_VAULT;1.2;AES256;dev
      61323931353866666336306139373937316366366138656131323863373866376666353364373761
      3539633234313836346435323766306164626134376564330a373530313635343535343133316133
      36643666306434616266376434363239346433643238336464643566386135356334303736353136
      6565633133366366360a326566323363363936613664616364623437336130623133343530333739
      3039
  tasks:
    - name: task1
      debug:
        msg: "OK"
      when: var1 is vault_encrypted # Skip

    - name: task2
      debug:
        msg: "OK"
      when: var2 is vault_encrypted # OK

真假性

  • truthy :真
  • falsy :假

此外,二者都可以添加 convert_bool 参数,将“yes”/“no”、“on”/“off”等转换为布尔值。

比如:

---
- hosts: all
  vars:
    var1: "abcdefg"
    var2: ""
    var3: "yes"
    var4: "no"
    var5: "on"
    var6: "off"
  tasks:
    - name: task1
      debug: 
        msg: "OK"
      when: var1 is truthy # OK

    - name: task2
      debug:
        msg: "OK"
      when: var2 is truthy # Skip

    - name: task3
      debug:
        msg: "OK"
      when: var3 is truthy(convert_bool=True) # OK

    - name: task4
      debug:
        msg: "OK"
      when: var4 is truthy(convert_bool=True) # Skip

    - name: task5
      debug:
        msg: "OK"
      when: var5 is truthy(convert_bool=True) # OK

    - name: task6
      debug:
        msg: "OK"
      when: var6 is truthy(convert_bool=True) # Skip

    - name: task7
      debug:
        msg: "OK"
      when: var1 is falsy # Skip

    - name: task8
      debug:
        msg: "OK"
      when: var2 is falsy # OK

    - name: task9
      debug:
        msg: "OK"
      when: var3 is falsy(convert_bool=True) # Skip

    - name: task10
      debug:
        msg: "OK"
      when: var4 is falsy(convert_bool=True) # OK

    - name: task11
      debug:
        msg: "OK"
      when: var5 is falsy(convert_bool=True) # Skip

    - name: task12
      debug:
        msg: "OK"
      when: var6 is falsy(convert_bool=True) # OK

版本比较

  • version :版本比较

可指定如下比较:

  • < lt :小于
  • <= le :小于等于
  • > gt :大于
  • >= ge :大于等于
  • == = eq :等于
  • != <> ne :不等于

比如:

---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ ansible_facts['distribution_version'] }}" # 8.1

    - name: task2
      debug:
        msg: "OK"
      when: ansible_facts['distribution_version'] is version('8.1', '>') # Skip

    - name: task3
      debug:
        msg: "OK"
      when: ansible_facts['distribution_version'] is version('8.1', '>=') # OK

version 方法还可以添加第三个参数,表示比较类型。

  • strict :True/False,是否严格比较
  • version_type
    • loose
    • strict
    • semver / semantic
    • pep440

注意: strictversion_type 互斥,最多只能指定一个。

此处我没仔细研究,等需要用的时候再说吧。

子集和超集

  • subset :子集
  • superset :超集

比如:

---
- hosts: all
  vars:
    var1: [1, 2, 3, 4, 5]
    var2: [2, 3, 4]
  tasks:
    - name: task1
      debug:
        msg: "OK"
      when: var2 is subset(var1) # OK

    - name: task2
      debug:
        msg: "OK"
      when: var1 is subset(var2) # Skip

    - name: task3
      debug:
        msg: "OK"
      when: var2 is superset(var1) # Skip

    - name: task4
      debug:
        msg: "OK"
      when: var1 is superset(var2) # OK

    - name: task5
      debug:
        msg: "OK"
      when: var1 is subset(var1) # OK

    - name: task6
      debug:
        msg: "OK"
      when: var1 is superset(var1) # OK

list里的元素

  • contains :是否包含特定的元素,若包含则返回元素

比如:

---
- hosts: all
  vars:
    var1:
      - name: Tom
        age: 20
        sport:
          - football
          - basketball
      - name: Jerry
        age: 18
        sport:
          - swim
          - tennis
          - football
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 | selectattr('name', 'contains', 'Tom') }}" # 第一个元素

    - name: task2
      debug:
        msg: "{{ var1 | selectattr('sport', 'contains', 'football') }}" # 第一个和第二个元素

    - name: task3
      debug:
        msg: "{{ var1 | selectattr('sport', 'contains', 'football') | first }}" # 第一个元素

    - name: task4
      debug:
        msg: "{{ (var1 | selectattr('sport', 'contains', 'football') | first).age }}" # 第一个元素的age属性值

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": [
        {
            "age": 20,
            "name": "Tom",
            "sport": [
                "football",
                "basketball"
            ]
        }
    ]
}

TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": [
        {
            "age": 20,
            "name": "Tom",
            "sport": [
                "football",
                "basketball"
            ]
        },
        {
            "age": 18,
            "name": "Jerry",
            "sport": [
                "swim",
                "tennis",
                "football"
            ]
        }
    ]
}

TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": {
        "age": 20,
        "name": "Tom",
        "sport": [
            "football",
            "basketball"
        ]
    }
}

TASK [task4] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": "20"
}

本例中,是使用 selectattr 方法,通过元素的属性值来匹配元素。 contains 可作用于如下filter:

  • select :选择元素
  • reject :排除元素
  • selectattr :根据属性值选择元素
  • rejectattr :根据属性值排除元素

比如:

---
- hosts: all
  vars:
    var1: ["aaa", "bbb",  "ccc"]

  tasks:
    - name: task1
      debug:
        msg: "{{ var1 | select('contains', 'aaa') }}"

    - name: task2
      debug:
        msg: "{{ var1 | select('contains', 'Tom') }}"

    - name: task3
      debug:
        msg: "{{ var1 | reject('contains', 'aaa') }}"

    - name: task4
      debug:
        msg: "{{ var1 | reject('contains', 'Tom') }}"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": [
        "aaa"
    ]
}

TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": []
}

TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": [
        "bbb",
        "ccc"
    ]
}

TASK [task4] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": [
        "aaa",
        "bbb",
        "ccc"
    ]
}

注意: select 等方法还可以有很多其它有趣的参数,比如:

---
- hosts: all
  vars:
    var1: [0, 1, 2, 3, 4, 4, 3, 2, 1]
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 | select('odd') }}" # 1, 3, 3, 1

    - name: task2
      debug:
        msg: "{{ var1 | select('even') }}" # 0, 2, 4, 4, 2

    - name: task3
      debug:
        msg: "{{ var1 | select('lessthan', 3) }}" # 0, 1, 2, 2, 1

    - name: task4
      debug:
        msg: "{{ var1 | select('equalto', 1) }}" # 1, 1

select 等方法的返回值也可以当做布尔值,比如:

---
- hosts: all
  vars:
    var1: ["aaa", "bbb",  "ccc"]

  tasks:
    - name: task1
      debug:
        msg: "OK"
      when: var1 | select('contains', 'aaa') # OK

    - name: task2
      debug:
        msg: "OK"
      when: var1 | select('contains', 'Tom') # Skip

    - name: task3
      debug:
        msg: "OK"
      when: var1 | reject('contains', 'aaa') # OK

    - name: task4
      debug:
        msg: "OK"
      when: var1 | reject('contains', 'Tom') # OK

list的“所有元素都是true”和“至少一个元素是true”

  • all :所有元素都是True
  • any :至少一个元素是True

比如:

---
- hosts: all
  vars:
    var1: [True, "{{ 1==1 }}", "!{{ 1==2 }}"]
    var2: [True, False]
    var3: [False, False, False]
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 is all }}" # true

    - name: task2
      debug:
        msg: "{{ var1 is any }}" # true

    - name: task3
      debug:
        msg: "{{ var2 is all }}" # false

    - name: task4
      debug:
        msg: "{{ var2 is any }}" # true

    - name: task5
      debug:
        msg: "{{ var3 is all }}" # false

    - name: task6
      debug:
        msg: "{{ var3 is any }}" # false

路径

  • directory :是否为目录
  • file :是否为文件
  • link :是否为链接
  • exists : 路径是否存在
  • abs :是否为绝对路径
  • same_file :是否为相同路径的文件
  • mount :是否为mount

比如:

---
- hosts: all
  vars:
    var1: "/usr/bin/ps"
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 is directory }}" # false

    - name: task2
      debug:
        msg: "{{ var1 is file }}" # true

    - name: task3
      debug:
        msg: "{{ var1 is exists }}" # true

    - name: task4
      debug:
        msg: "{{ var1 is abs }}" # true

human_readable

注:这貌似是转换,并不是test。

  • human_readable :数值转换为可读格式
  • human_to_bytes :可读格式转换为数值

比如:

---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ 1 | human_readable }}" # 1.00 Bytes

    - name: task2
      debug:
        msg: "{{ 1 | human_readable(isbits = True) }}" # 1.00 bits

    - name: task3
      debug:
        msg: "{{ 1024 | human_readable }}" # 1.00 KB

    - name: task4
      debug:
        msg: "{{ (1024 * 1024) | human_readable }}" # 1.00 MB

    - name: task5
      debug:
        msg: "{{ (1024 * 1024 * 1024) | human_readable }}" # 1.00 GB

    - name: task5
      debug:
        msg: "{{ (1024 * 1024 * 1024) | human_readable(unit='M') }}" # 1024.00 MB
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{'1'|human_to_bytes}}" # 1

    - name: task2
      debug:
        msg: "{{'1KB'|human_to_bytes}}" # 1024

    - name: task3
      debug:
        msg: "{{'1MB'|human_to_bytes}}" # 1048576

    - name: task4
      debug:
        msg: "{{'1GB'|human_to_bytes}}" # 1073741824

    - name: task5
      debug:
        msg: "{{'1Kb'|human_to_bytes(isbits=True)}}" # 1024

task结果

  • successsucceeded :成功
  • failed :失败
  • changed :变化
  • skipped :略过

比如:

---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "OK"
      register: result1

    - name: task2
      debug:
        msg: "{{ result1 is success }}, {{ result1 is failed }}, {{ result1 is changed }}, {{ result1 is skipped }}"
        # True, False, False, False

    - name: task3
      debug:
        msg: "OK"
      when: 1 > 2
      register: result2

    - name: task4
      debug:
        msg: "{{ result2 is success }}, {{ result2 is failed }}, {{ result2 is changed }}, {{ result2 is skipped }}"
        # True, False, False, True

    - name: task5
      debug:
        msg: "{{ var1 }}"
      ignore_errors: true
      register: result3

    - name: task6
      debug:
        msg: "{{ result3 is success }}, {{ result3 is failed }}, {{ result3 is changed }}, {{ result3 is skipped }}"
        # False, True, False, False

    - name: task7
      shell: "echo OK"
      register: result4

    - name: task8
      debug:
        msg: "{{ result4 is success }}, {{ result4 is failed }}, {{ result4 is changed }}, {{ result4 is skipped }}"
        # True, False, True, False

类型

你可能想通过 type_debug filter获取类型,然后跟字符串比较,比如:

---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ (1 | type_debug) == 'int' }}" # true

但是还有更好的方法,就是使用test来做类型比较。

  • string :字符串
  • iterable :可迭代
  • sequence :顺序
  • mapping :映射(dictionary)

比如:

---
- hosts: all
  vars:
    var1: "aaa"
    var2: 123
    var3: ["aaa", "bbb", "ccc"]
    var4: {name: Tom, age: 20}
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 is string }}, {{ var1 is iterable }}, {{ var1 is sequence }}, {{ var1 is mapping }}" # True, True, True, False

    - name: task2
      debug:
        msg: "{{ var2 is string }}, {{ var2 is iterable }}, {{ var2 is sequence }}, {{ var2 is mapping }}" # False, False, False, False

    - name: task3
      debug:
        msg: "{{ var3 is string }}, {{ var3 is iterable }}, {{ var3 is sequence }}, {{ var3 is mapping }}" # False, True, True, False

    - name: task4
      debug:
        msg: "{{ var4 is string }}, {{ var4 is iterable }}, {{ var4 is sequence }}, {{ var4 is mapping }}" # True, True, False

数值类型:

  • number :数值
  • integer :整数
  • float :浮点数

比如:

---
- hosts: all
  vars:
    var1: 123
    var2: 45.6
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 is number }}, {{ var1 is integer }}, {{ var1 is float }}"
        # True, True, False

    - name: task2
      debug:
        msg: "{{ var2 is number }}, {{ var2 is integer }}, {{ var2 is float }}"
        # True, False, True

类型转换,比如:

---
- hosts: all
  vars:
    var1: "123"
    var2: 123
    var3: 45.6
  tasks:
    - name: task1
      debug:
        msg: "{{ (var1 | int) is integer }}" # true

    - name: task2
      debug:
        msg: "{{ (var1 | float) is float }}" # true

    - name: task3
      debug:
        msg: "{{ (var2 | float) is float }}" # true

    - name: task4
      debug:
        msg: "{{ (var3 | int) is integer }}" # true

    - name: task5
      debug:
        msg: "{{ (var2 | string) is string }}" # true

    - name: task6
      debug:
        msg: "{{ (var3 | string) is string }}" # true

注:把 45.6 转换为 int 类型,结果是45

布尔类型:

  • yes
  • true
  • True
  • TRUE
  • no
  • No
  • NO
  • false
  • False
  • FALSE

比如:

---
- hosts: all
  tasks:
    - name: task1
      loop:
      - yes
      - Yes
      - YES
      - true
      - True
      - TRUE
      - no
      - No
      - NO
      - false
      - False
      - FALSE
      debug:
        msg: "{{ item is boolean }}"

本例中,所有item都是布尔类型。

注意:要么全部小写,要么全部大写,要么首字母大写,后面都小写。其它写法比如 FaLSE 不是布尔类型。

注:官网上说 yes 是唯一大小写敏感的:

Note also that yes is the only case-sensitive variant of these values.

但我试了一下,貌似跟其它几个值也没什么不同。

注:在我的测试中发现, yesno 赋值给变量时,变量会被当做 bool 类型,但是literal的 yesno 并不是 bool 类型:

---
- hosts: all
  vars:
    var1: yes
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 | type_debug }}" # bool

    - name: task2
      debug:
        msg: "{{ var1 is boolean }}" # true

    - name: task3
      debug:
        msg: "{{ no is boolean }}" # false

    - name: task4
      debug:
        msg: "{{ no | type_debug }}" # AnsibleUndefined

    - name: task5
      debug:
        msg: "{{ true is boolean }}" # true

参考

  • https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tests.html
  • https://jinja.palletsprojects.com/en/latest/templates/#jinja-filters.select

你可能感兴趣的:(Ansible,ansible,jinja)