Ansible的filter

环境

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

filter

使用filter可以对数据做操作,比如把JSON数据转换为YAML数据,从URL中解析出hostname,提取字符串的SHA1哈希值,做数学运算,等等。可以使用Ansible特有的filter,也可以使用jinja2移植的标准filter。

因为templating发生在Ansible的控制节点,而不是目标节点,所以filter运行在控制节点,并在本地转换数据。

处理未定义变量

提供缺省值

创建文件 test1.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "Hello {{ var1 }}"

运行结果如下:

powershell 复制代码
➜  temp1105_2 ansible-playbook test1.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

TASK [task1] ***************************************************************************************
fatal: [192.168.1.55]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'var1' is undefined\n\nThe error appears to be in '/root/temp/temp1105_2/test1.yml': line 4, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n  tasks:\n    - name: task1\n      ^ here\n"}

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

这是因为变量 var1 未定义。

为避免出错,可以使用jinja2的 default filter:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "Hello {{ var1 | default('ABC') }}, {{var2 | default(\"XYZ\")}}, {{ var3 | default(123) }}"

其中:

  • var1 default值是单引号引起来的字符串
  • var2 default值是双引号引起来的字符串,所以用 \ 来转义
  • var3 default值是数值,不需要用引号

运行结果如下:

powershell 复制代码
➜  temp1105_2 ansible-playbook test1.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": "Hello ABC, XYZ, 123"
}

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

注:对于role,也可以在其 defaults/main.yml 文件里给该role的变量定义缺省值。

如果原值evaluate为false或者空字符串,则使用default时,需要将其第二个参数设置为true(默认值是false):

yaml 复制代码
......
        msg: "aaa {{ '' | default('empty string', true) }} bbb"
......

运行结果如下:

powershell 复制代码
......
    "msg": "aaa empty string bbb"
......

注:如果不加第二个参数,则不会替换为缺省值。

变量为空值

创建文件 test2.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "aaa {{ item.var1 }} bbb {{ item.var2 | default(omit) }}"
      loop:
        - var1: "Tom"
          var2: 20
        - var1: "Jerry"

其中,item1指定了 var1var2 ,而item2只指定了 var1

运行结果如下:

powershell 复制代码
➜  temp1105_2 ansible-playbook test2.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item={'var1': 'Tom', 'var2': 20}) => {
    "msg": "aaa Tom bbb 20"
}
ok: [192.168.1.55] => (item={'var1': 'Jerry'}) => {
    "msg": "aaa Jerry bbb __omit_place_holder__ae2509bf44427378895b86e1e65511fbaaf58727"
}

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

可见,指定了 default(omit) ,则变量默认值为空(若打印其值,则输出一个占位符)。

注:对于链式filter,官网上说,要用 "{``{ foo | default(None) | <some_filter> or omit }}" 的形式,比如:

yaml 复制代码
msg: " {{ var1 | default(None) | lower or omit }}"

运行结果如下:

powershell 复制代码
    "msg": " none"

但我试了一下,如果不这么做,比如:

yaml 复制代码
msg: " {{ var1 | default(omit) | lower }}"

也不报错,运行结果如下:

powershell 复制代码
    "msg": " __omit_place_holder__2212ea218f1e07f0070e5184cf11ebf71e3e5e9e"

变量必须有值

如果配置了 DEFAULT_UNDEFINED_VAR_BEHAVIORfalse ,则变量允许为空值。此时,如果想要强制变量不得为空值,则可以使用 mandatory filter,比如:

yaml 复制代码
{{ var1 | mandatory }}

根据不同值true/false/null做判断

注: ternary三元 的意思。

创建文件 test6.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ (var1 == 'a') | ternary('111', '222') }}"
      vars:
        - var1: 'a'  

本例中,由于 var1 == 'a' 是true,所以会打印 111

powershell 复制代码
➜  temp1105_2 ansible-playbook test6.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

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

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

创建文件 test7.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 | ternary('111', '222', omit) }}"
      vars:
        - var1: 'abc'
    - name: task2
      debug:
        msg: "{{ var2 | ternary('111', '222', omit) }}"
      vars:
        - var2: false
    - name: task3
      debug:
        msg: "{{ var3 | ternary('111', '222', omit) }}"
      vars:
        - var3: null

运行结果如下:

powershell 复制代码
➜  temp1105_2 ansible-playbook test7.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

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

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

TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": "Hello world!"
}

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
  • var1 有值且不等于 false ,表示true
  • var2 其值为 false (注意没有引号,否则是字符串),表示false
  • var3 其值为 null ,注意打印出了 Hello world!

管理数据类型

检测数据类型

创建文件 test8.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ 'abc' | type_debug }}, {{ 123 | type_debug }}, {{ 0.5 | type_debug }}, {{ true | type_debug }}"

运行结果如下:

powershell 复制代码
➜  temp1105_2 ansible-playbook test8.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": "str, int, float, bool"
}

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0    

可见:

  • 'abc' 类型为 str
  • 123 类型为 int
  • 0.5 类型为 float
  • true 类型为 bool

强制类型转换

创建文件 test9.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ var1 | int | type_debug }}, {{ var2 | float | type_debug }}, {{ var3 |bool | type_debug }}, {{ var4 | string | type_debug }}"
      vars:
        - var1: '123'
        - var2: '0.5'
        - var3: 'true'
        - var4: 456

运行结果如下:

powershell 复制代码
➜  temp1105_2 ansible-playbook test9.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": "int, float, bool, str"
}

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

JSON和YAML

  • to_json :转换为JSON格式
  • to_nice_json :转换为可读的JSON格式(加上换行,缩进等)
  • to_yaml :转换为YAML格式
  • to_nice_yaml :转换为可读的YAML格式(加上换行,缩进等)

创建文件 test12.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      template:
        src: /tmp/src1
        dest: /tmp/dest1
    - name: task2
      template:
        src: /tmp/src2
        dest: /tmp/dest2
    - name: task3
      template:
        src: /tmp/src3
        dest: /tmp/dest3
    - name: task4
      template:
        src: /tmp/src4
        dest: /tmp/dest4

/tmp/src1 如下:

yaml 复制代码
{{ ansible_facts['default_ipv4'] | to_json }}

/tmp/src2 如下:

yaml 复制代码
{{ ansible_facts['default_ipv4'] | to_nice_json }}

/tmp/src3 如下:

yaml 复制代码
{{ ansible_facts['default_ipv4'] | to_yaml }}

/tmp/src4 如下:

yaml 复制代码
{{ ansible_facts['default_ipv4'] | to_nice_yaml }}

运行结果如下:

powershell 复制代码
➜  temp1105_2 ansible-playbook test12.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

TASK [task1] ***************************************************************************************
changed: [192.168.1.55]

TASK [task2] ***************************************************************************************
changed: [192.168.1.55]

TASK [task3] ***************************************************************************************
changed: [192.168.1.55]

TASK [task4] ***************************************************************************************
changed: [192.168.1.55]

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

运行结束后,到目标机器上查看。

/tmp/dest1 如下:

javascript 复制代码
{"gateway": "192.168.1.1", "interface": "ens33", "address": "192.168.1.55", "broadcast": "192.168.1.255", "netmask": "255.255.255.0", "network": "192.168.1.0", "macaddress": "00:0c:29:12:a6:b7", "mtu": 1500, "type": "ether", "alias": "ens33"}

/tmp/dest2 如下:

javascript 复制代码
{
    "address": "192.168.1.55",
    "alias": "ens33",
    "broadcast": "192.168.1.255",
    "gateway": "192.168.1.1",
    "interface": "ens33",
    "macaddress": "00:0c:29:12:a6:b7",
    "mtu": 1500,
    "netmask": "255.255.255.0",
    "network": "192.168.1.0",
    "type": "ether"
}

/tmp/dest3 如下:

yaml 复制代码
{address: 192.168.1.55, alias: ens33, broadcast: 192.168.1.255, gateway: 192.168.1.1,
  interface: ens33, macaddress: '00:0c:29:12:a6:b7', mtu: 1500, netmask: 255.255.255.0,
  network: 192.168.1.0, type: ether}

/tmp/dest4 如下:

yaml 复制代码
address: 192.168.1.55
alias: ens33
broadcast: 192.168.1.255
gateway: 192.168.1.1
interface: ens33
macaddress: 00:0c:29:12:a6:b7
mtu: 1500
netmask: 255.255.255.0
network: 192.168.1.0
type: ether

注: to_yamlto_nice_yaml ,默认每行包含80个字符(当超过80字符后,遇到空格就会换行),可以指定 width 选项,比如:

yaml 复制代码
{{ ansible_facts['default_ipv4'] | to_yaml(width=500) }}

结果如下:

yaml 复制代码
{address: 192.168.1.55, alias: ens33, broadcast: 192.168.1.255, gateway: 192.168.1.1, interface: ens33, macaddress: '00:0c:29:12:a6:b7', mtu: 1500, netmask: 255.255.255.0, network: 192.168.1.0, type: ether}

可以指定 indent 选项(默认值貌似是4),比如:

yaml 复制代码
{{ ansible_facts['default_ipv4'] | to_nice_json(indent=8) }}

结果如下:

javascript 复制代码
{
        "address": "192.168.1.55",
        "alias": "ens33",
        "broadcast": "192.168.1.255",
        "gateway": "192.168.1.1",
        "interface": "ens33",
        "macaddress": "00:0c:29:12:a6:b7",
        "mtu": 1500,
        "netmask": "255.255.255.0",
        "network": "192.168.1.0",
        "type": "ether"
}

可见缩进值变为8了。

  • from_json :读取已知的JSON数据
  • from_yaml :读取已知的YAML数据

在目标机器上创建 json1.json 如下:

javascript 复制代码
{
  "Name": "Tom",
  "Age": 20,
  "Sports":
    ["Football", "Swimming"]
}

在目标机器上创建 yaml1.yml 如下:

yaml 复制代码
Name: Tom
Age: 20
Sports:
  - Football
  - Swimming

创建文件 test14.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      shell: cat /tmp/json1.json
      register: result1
    - name: task2
      debug:
        msg: "{{ result1.stdout | type_debug }}"
    - name: task3
      debug:
        msg: "{{ result1.stdout | from_json | type_debug }}"
    - name: task4
      shell: cat /tmp/yaml1.yml
      register: result2
    - name: task5
      debug:
        msg: "{{ result2.stdout | type_debug }}"
    - name: task6
      debug:
        msg: "{{ result2.stdout | from_yaml | type_debug }}"

运行结果如下:

powershell 复制代码
➜  temp1105_2 ansible-playbook test14.yml

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

TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]

TASK [task1] ***************************************************************************************
changed: [192.168.1.55]

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

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

TASK [task4] ***************************************************************************************
changed: [192.168.1.55]

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

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

PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=7    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

可见,如果不加 from_jsonfrom_yaml ,则读取的是字符串( AnsibleUnsafeText ),加上from_jsonfrom_yaml ,则读取为 dict

随机数

创建文件 test15.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ 60 | random }}"

运行结果如下:

powershell 复制代码
    "msg": "4"

多次运行,结果也不同。

注意:取值范围是0到60,左闭右开。

如果想指定起始值,可以用 start 选项:

yaml 复制代码
        msg: "{{ 60 | random(start=55) }}"

则取值范围是55到60,左闭右开。

如果要求取值是特定值的整倍数,可以用 step 选项:

yaml 复制代码
        msg: "{{ 60 | random(step=20) }}"

则取值只能是0或20或40。

如果想在多个目标机器上各自产生不同的随机数,但要求每个机器上的随机数多次运行结果不变(幂等性),则可以添加 seed 选项,使用该种子来产生随机数,注意seed的值不要变:

yaml 复制代码
        msg: "{{ 60 | random(seed=inventory_hostname) }}"
powershell 复制代码
    "msg": "50"

多次运行,产生的随机数保持不变。

数学运算

  • log
  • pow
  • root
  • abs
  • round

创建文件 test16.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "{{ 100 | log(10) }}, {{ 2 | pow(10) }}, {{ 216 | root(3) }}, {{ -1.5 | abs }}, {{ 12.34 | round }}"

运行结果如下:

powershell 复制代码
    "msg": "2.0, 1024.0, 5.999999999999999, 1.5, 12.0"

注释

comment 将内容注释。默认的注释符是 # ,可以指定不同语言来做注释。

创建文件 test17.yml 如下:

yaml 复制代码
---
- hosts: all
  tasks:
    - name: task1
      template:
        src: /tmp/src1
        dest: /tmp/dest1
    - name: task2
      template:
        src: /tmp/src2
        dest: /tmp/dest2
    - name: task3
      template:
        src: /tmp/src3
        dest: /tmp/dest3
    - name: task4
      template:
        src: /tmp/src4
        dest: /tmp/dest4
    - name: task5
      template:
        src: /tmp/src5
        dest: /tmp/dest5

创建文件 /tmp/src1 如下:

yaml 复制代码
{{ 'aaa' | comment }}

创建文件 /tmp/src2 如下:

yaml 复制代码
{{ 'bbb' | comment('c') }}

创建文件 /tmp/src3 如下:

yaml 复制代码
{{ 'ccc' | comment('cblock') }}

创建文件 /tmp/src4 如下:

yaml 复制代码
{{ 'ddd' | comment('erlang') }}

创建文件 /tmp/src5 如下:

yaml 复制代码
{{ 'eee' | comment('xml') }}

运行结束后,到目标机器上查看:

/tmp/dest1 如下:

powershell 复制代码
#
# aaa
#

/tmp/dest2 如下:

c 复制代码
//
// bbb
//

/tmp/dest3 如下:

c 复制代码
/*
 *
 * ccc
 *
 */

/tmp/dest4 如下:

erlang 复制代码
%
% ddd
%

/tmp/dest5 如下:

xml 复制代码
<!--
 -
 - eee
 -
-->

字符串操作

  • quote :给值加上引号
yaml 复制代码
      shell: echo {{ var1 | quote }} > /tmp/a.txt
      vars:
        var1: "a\nb\nc"

运行结束后,目标机器 /tmp/a.txt 如下:

powershell 复制代码
a
b
c

如果没有 quote ,则会报错。

  • join :连接字符串
yaml 复制代码
      debug:
        msg: "{{ ['a', 'b', 'c'] | join(' ') }}"

运行结果如下:

powershell 复制代码
    "msg": "a b c"
  • split :切割字符串
yaml 复制代码
      debug:
        msg: "{{ 'a,b,c' | split(',') }}"

注:需要升级到2.11,我用的是2.10,会报错。

  • b64encode :Base64 encode
yaml 复制代码
      debug:
        msg: "{{ 'abcdefg' | b64encode }}"

运行结果如下:

powershell 复制代码
    "msg": "YWJjZGVmZw=="
  • b64decode :Base64 decode
yaml 复制代码
      debug:
        msg: "{{ 'YWJjZGVmZw==' | b64decode }}"

运行结果如下:

powershell 复制代码
    "msg": "abcdefg"

UUID

  • to_uuid :创建UUID
yaml 复制代码
      debug:
        msg: "{{ 'abcdefg' | to_uuid }}"

运行结果如下:

powershell 复制代码
    "msg": "aeb26d4d-43ec-587e-b3a8-67b6ca88a4df"

参考

  • https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_filters.html
  • https://jinja.palletsprojects.com/en/latest/templates
相关推荐
tyatyatya18 小时前
Ansible自动化配置,从入门到实战
运维·自动化·ansible
lbb 小魔仙2 天前
【Linux】Ansible 自动化运维实战:2000+ 节点配置标准化教程
linux·运维·ansible
扑火的小飞蛾5 天前
【Ansible学习笔记01】 批量执行 shell 命令
笔记·学习·ansible
oMcLin5 天前
如何在 Red Hat Linux 服务器上使用 Ansible 自动化部署并管理多节点 Hadoop 集群?
linux·服务器·ansible
linux修理工8 天前
vagrant ubuntu 22.04 ansible 配置
ubuntu·ansible·vagrant
biubiubiu07069 天前
Ansible自动化
运维·自动化·ansible
秋4279 天前
ansible配置与模块介绍
ansible
秋42710 天前
ansible剧本
linux·服务器·ansible
码农101号11 天前
Ansible - Role介绍 和 使用playbook部署wordPress
android·ansible
2301_8000509912 天前
Ansible
运维·ansible