ansible中过滤器的介绍以及如何自定义过滤器

  • 一、过滤器介绍
  • 二、常用过滤器介绍
  • 2.1 类型转换
  • 2.2 数学运算
  • 2.3 字典转换为列表
  • 2.4 将字典中的所有key生成一个list
  • 2.5 总结
  • 三、自定义过滤器
  • 四、总结

之前介绍了关于如何通过shell, python, golang等语言实现自定义模块,可以参考这篇文章:

今天主要是介绍下如何实现自定义过滤器来应对各种各样的场景。

一、过滤器介绍

在ansible中由于使用了模板引擎jinja2,因此可以在使用jinja2语法的时候可以使用jinja2自带的一些过滤器工具,以及ansible自带的一些过滤器来处理数据,举一个简单的例子:

# 设置一个变量
- name: Set vars
  set_fact:
    name: tom

# 使用过滤器来处理上面的变量
- name: Upper vars
  set_fact:
    name: "{{ name | upper }}"

# 打印结果
- name: Debug result
  debug:
    msg: "{{ name }}"
    
# 结果应为:TOM

以上就是使用过滤器来修改数据的一个小示例,通过此例其实能很方便的帮我们去处理数据,例如下面这个示例,当我们想去获取主机的磁盘挂载信息(通过ansible_mounts变量获取),然后想获取到挂载目录的大小最大的那个挂载点,这个示例在日常的运维中也许是一个很常见的需求,下面是实现方式:

首先ansible内置了一个变量用来获取主机的挂载信息,我们可以执行一下,来看下输出的挂载信息是什么样的?

# ansible localhost -m setup -a "filter=ansible_mounts"
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_mounts": [
            {
                "block_available": 224897220, 
                "block_size": 4096, 
                "block_total": 262016000, 
                "block_used": 37118780, 
                "device": "/dev/vdb", 
                "fstype": "xfs", 
                "inode_available": 520245464, 
                "inode_total": 524288000, 
                "inode_used": 4042536, 
                "mount": "/data", 
                "options": "rw,seclabel,relatime,attr2,inode64,noquota", 
                "size_available": 921179013120, 
                "size_total": 1073217536000, 
                "uuid": "e2f217b1-e17b-4e90-8a34-015c8585a809"
            }, 
            {
                "block_available": 85589, 
                "block_size": 4096, 
                "block_total": 127145, 
                "block_used": 41556, 
                "device": "/dev/vda1", 
                "fstype": "xfs", 
                "inode_available": 255667, 
                "inode_total": 256000, 
                "inode_used": 333, 
                "mount": "/boot", 
                "options": "rw,seclabel,relatime,attr2,inode64,noquota", 
                "size_available": 350572544, 
                "size_total": 520785920, 
                "uuid": "5c98020e-1b57-4f08-9bd6-e3af97f097ce"
            }, 
            {
                "block_available": 2715012, 
                "block_size": 4096, 
                "block_total": 10480385, 
                "block_used": 7765373, 
                "device": "/dev/vda3", 
                "fstype": "xfs", 
                "inode_available": 20361285, 
                "inode_total": 20971008, 
                "inode_used": 609723, 
                "mount": "/", 
                "options": "rw,seclabel,relatime,attr2,inode64,noquota", 
                "size_available": 11120689152, 
                "size_total": 42927656960, 
                "uuid": "9f8f7093-b959-42dc-aa85-28219a02fac0"
            }
        ]
    }, 
    "changed": false
}

可以看到ansible_mounts这个变量的数据类型是一个列表,列表中的元素是一个字典,每一个字典下主要包含:磁盘挂载点、磁盘大小、磁盘可用大小、磁盘分区类型、磁盘设备名称、以及块大小、inode信息等。对于这个示例,我们只需要关注磁盘挂载点、以及磁盘大小即可。

但是ansible_mounts获取的结果是一个列表,而我们只想获取挂载点的目录大小最大的那个挂载点,那我们该如何实现呢?是不是需要先对ansible_mounts进行排序(按照磁盘大小排序),然后对排序后的结果进行取值即可。在python中对列表中的元素为字典中的值进行排序时可以使用sorted结合lambda,在ansible中可以直接使用sort过滤器实现

# cat demo.yaml
- hosts: localhost
  tasks:
    - name: 对ansible_mounts进行排序
      debug:
        msg: "{{ ansible_mounts | sort(attribute='size_total') }}"

看一下运行结果

# ansible-playbook demo.yaml 

接下来我们来获取这个挂载点,通过使用last过滤器对排序后的数据获取最后一个元素的值

# cat demo.yaml
- hosts: localhost
  tasks:
    - name: 对ansible_mounts进行排序
      debug:
        msg: "{{ ansible_mounts | sort(attribute='size_total') }}"
    - name: 对ansible_mounts进行排序,然后再获取最大的挂载点
      debug:
        msg: "{{ ansible_mounts | sort(attribute='size_total') | last }}"

运行结果

接下来我们可以把这个赋值给一个新的变量,然后来获取挂载点名称

# cat demo.yaml
- hosts: localhost
  tasks:
    - name: 对ansible_mounts进行排序,然后再获取最大的挂载点,最后赋值给新的变量
      set_fact:
        get_max_mount: "{{ ansible_mounts | sort(attribute='size_total') | last }}"
    - name: 获取最大的挂载点
      debug:
        msg: "{{ get_max_mount.mount }}"

运行此playbook,获取结果如下:

以上示例使用了sort, last两个过滤器终于获取到了我们想要的数据,可以看出ansible的过滤器让我们对数据的处理更加的得心应手。

二、常用过滤器介绍

上面的几个示例如果你很有耐心的看完,或跟着实验了一遍,你应该就会对ansible的过滤器有了一个清晰的认识,但ansible和jinja2提供的过滤器太多了,接下来我会介绍几个常用的

2.1 类型转换

顾名思义,就是要将数据类型进行转换,例如

# cat demo.yaml
- hosts: localhost
  vars:
    age: "18"
    json_str: '{"name": "tom", "age": 18}'
  tasks:
    - name: 将字符串类型转换层整型
      debug:
        msg: "{{ age | int }}"
    - name: 将json字符串转为json对象
      debug:
        msg: "{{ json_str | from_json }}"

与之类似的,还有以下一些:

  • bool:将变量转换为布尔值。
  • int:将变量转换为整数。
  • float:将变量转换为浮点数。
  • string:将变量转换为字符串。
  • json_query:将 JSON 字符串转换为 JSON 对象。
  • from_json:将 JSON 字符串转换为 JSON 对象。
  • to_json:将 JSON 对象转换为 JSON 字符串。
  • yaml:将变量转换为 YAML 字符串。

2.2 数学运算

这个我个人用的不是很多,可以参考下

# cat demo.yaml
- hosts: localhost
  tasks:
    - name: 取对数
      debug:
        msg: "{{ 8 | log }}"
    - name: 幂运算
      debug:
        msg: "{{ 8 | pow(5) }}"
    - name: 求根
      debug:
        msg: "{{ 8 | root }}"

2.3 字典转换为列表

# cat demo.yaml
- hosts: localhost
  vars:
    is_dict:
      name: tom
      age: 18
  tasks:
    - name: 将字典转为列表
      debug:
        msg: "{{ is_dict | dict2items }}"

 

转换后的列表的元素会是一个固定格式的字典,之前字典中的k, v会分别作为一个value存储到key和value下

这个我用的比较多,在处理数据时经常用到,上面演示了如何获取最大的挂载点,下面来演示如何获取最大的磁盘设备,需要用到ansible_devices这个内置变量,下面来打印下ansible_devices的内容

# ansible localhost -m setup -a "filter=ansible_devices"

执行结果是一个字典,key为设备名称,value为设备的详细信息,如下:

{
  "vdb": {
    "holders": [], 
    "host": "SCSI storage controller: Red Hat, Inc. Virtio block device", 
    "links": {
      "ids": [
        "virtio-17d4e0d5-fabb-46c6-b"
      ], 
      "labels": [], 
      "masters": [], 
      "uuids": [
        "e2f217b1-e17b-4e90-8a34-015c8585a809"
      ]
    }, 
    "model": null, 
    "partitions": {}, 
    "removable": "0", 
    "rotational": "1", 
    "sas_address": null, 
    "sas_device_handle": null, 
    "scheduler_mode": "mq-deadline", 
    "sectors": "2097152000", 
    "sectorsize": "512", 
    "size": "1000.00 GB", 
    "support_discard": "0", 
    "vendor": "0x1af4", 
    "virtual": 1
  },
  "vda": {
    ...
    ...
    ...
  }
}

这里只截取了其中一个设备,很明显输出的内容是一个字典,那么我们如何对字典中的嵌套字典中的指定健进行排序呢?另外上面的输出信息中,我们可以看到size字段是一个字符串,而sectors也是一个字符串,但是我们可以将sectors转成int再进行对此值进行排序,要完成这些操作,我们需要生成一个新的字典,直接上示例:

# cat demo.yaml
- hosts: localhost
  tasks:
    - name: 生成一个新的字典
      set_fact:
        new_ansible_devices: "{{ new_ansible_devices | default({}) | combine({item.key: item.value.sectors | int})  }}"
      with_dict: "{{ ansible_devices  }}"
    - name: 打印新生成的字典
      debug:
        msg: "{{ new_ansible_devices }}"

运行结果如下:

接下来的操作是不是就比较好处理了,可以按照我们一开始说的获取最大的挂载点的方式来操作

# cat demo.yaml
- hosts: localhost
  tasks:
    - name: 生成一个新的字典
      set_fact:
        new_ansible_devices: "{{ new_ansible_devices | default({}) | combine({item.key: item.value.sectors | int})  }}"
      with_dict: "{{ ansible_devices  }}"
    - name: 打印新生成的字典
      debug:
        msg: "{{ new_ansible_devices }}"
    - name: 转成列表,并进行排序
      set_fact:
        sort_disk_devices: "{{ new_ansible_devices | dict2items | sort(attribute='value') }}"
    - name: 打印排序后的结果
      debug:
        msg: "{{ sort_disk_devices  }}"
    - name: 使用last获取最大的设备名称
      set_fact:
        get_max_device: "{{ sort_disk_devices | last }}"
    - name: 打印最大的设备名称
      debug:
        msg: "{{  get_max_device.key }}"

以上示例用到的东西很多,总结如下:

  • 使用到了default来设置一个默认值,这里主要是为了生成一个新的字典
  • 使用到了int来对字符串进行了类型转换
  • 使用到了combine来将k, v 组合成一个字典
  • 使用到了dict2items来将字典转成一个列表
  • 使用到了sort来对列表中的元素进行排序
  • 使用到了last来获取列表中的最后一个元素

2.4 将字典中的所有key生成一个list

这个也很常用,例如我想获取当前节点都有哪些磁盘设备,并且以列表的形式输出出来

# cat demo.yaml
- hosts: localhost
  tasks:
    - name: 输出当前节点中的磁盘设备列表
      debug:
        msg: "{{ ansible_devices.keys() | list  }}"

执行结果为:

除此之外,还可以使用map来实现

# cat demo.yaml
- hosts: localhost     
  tasks:
    - name: 输出当前节点中的磁盘设备列表
      debug:
        msg: "{{ ansible_devices | dict2items | map(attribute='key') | list  }}"

输出的结果是一样的,这里不再赘述

2.5 总结

ansible提供的过滤器太多了,这里结合了实际的一些例子进行了展示,过程中使用到了很多的过滤器,总而言之过滤器就是来帮助我们来处理数据,或者对数据进行二次加工,最终来获取到我们想要的一些数据,或者数据类型。

由于过滤器太多,这里不再一一介绍,建议多看官方文档进行参考

官方参考文档:https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_filters.html#playbooks-filters

三、自定义过滤器

上面介绍了过滤器的使用,但是总有一些情况无法满足我们的需求,我们需要开发自己的一些过滤器,下面我将会演示如何通过python来实现一个自定义的过滤器,来实现对数据的处理

自定义过滤器和自定义模块类似,需要我们提前修改ansible.cfg文件,或者我们在编写playbook时,需要在roles下面对应的目录下创建filter_plugins目录,如下所示:

# 修改配置文件: /etc/ansible/ansible.cfg
[defaults]
filter_plugins     = /opt/workspace/scripts/filter_plugins

或者直接在roles下对应的role创建filter_plugins目录,例如:

roles/demo/filter_plugins

需求:给一个字符串,该字符串可能是以,分割,可能以|, 也可能以:分割,需要自定义过滤器,将该字符串按照分隔符进行拆分,最终生成一个列表

# 过滤器名称:split_everything
from __future__ import absolute_import, division, print_function

__metaclass__ = type


def split_everything(content, sep=","):
    return content.strip(sep).split(sep)


class FilterModule(object):
    def filters(self):
        return {
            "split_everything": split_everything,
        }

使用方法:

# cat demo.yaml
- hosts: localhost     
  vars:
    type_a: "a,b,c,d,e,"
    type_b: "a|b|c|d"
    type_c: "a:b"
    type_d: "d@"
  tasks:
    - name: type_a
      debug:
        msg: "{{ type_a | split_everything(sep=',')  }}"
    - name: type_b
      debug:
        msg: "{{ type_b | split_everything(sep='|')  }}"
    - name: type_c
      debug:
        msg: "{{ type_c | split_everything(sep=':')  }}"
    - name: type_d
      debug:
        msg: "{{ type_d | split_everything(sep='@')  }}"

执行结果

四、总结

本文主要介绍了过滤器的作用,以及常用的过滤器,以磁盘设备、磁盘挂载等实际工作中的例子进行演示,同时还介绍了自定义模块的方法,通过自定义过滤器来满足我们多样化的需求,使我们在自动化中更加得心应手。

另外本篇文章并不是一个教程,而是结合工作中的事例来介绍如何使用过滤器,建议大家还是多阅读官方文档。

官方文档:https://docs.ansible.com/ansible/latest/plugins/filter.html#plugin-list


 

最后,欢迎关注公众号:feelwow

 

posted @ 2023-04-12 23:49  dogfei  阅读(248)  评论(0编辑  收藏  举报