Ansible用JSON查询过滤ansible_fact挂载点数据未生效问题
问题说明
编写Ansible playbook时需要提取同时满足以下两个条件的挂载点:
- 文件系统类型为ext2、ext3、ext4
- 挂载路径以
whitelist变量中定义的任意元素为前缀
当前实现仅完成了文件系统类型的过滤,白名单前缀匹配规则未生效,返回结果中包含/tmp这类不符合前缀要求的挂载点。
初始playbook代码:
- hosts: all gather_facts: True become: True vars: whitelist: - /boot - /home - /opt - /var - /bin - /usr tasks: - name: extract mount_points set_fact: mount_point: "{{ansible_facts.mounts | selectattr('fstype', 'in', ['ext2', 'ext3', 'ext4']) | map(attribute='mount') | list }}" - debug: var: mount_point vars: query: "[?starts_with(mount, whitelist)].mount"
执行返回结果:
ok: [ansible@controller] => { "mount_point": [ "/", "/boot", "/tmp", "/home", "/var", "/var/opt", "/var/tmp", "/var/log", "/var/log/audit", "/opt", ] }
失效原因
原有逻辑存在两个明显问题:
- 定义的
query变量没有被任何过滤步骤调用,set_fact环节仅做了文件系统类型过滤,根本没有执行前缀匹配逻辑 - JMESPath内置的
starts_with函数仅支持传入单个字符串作为匹配前缀,无法直接接收列表参数实现“匹配列表中任意前缀”的效果,直接传入whitelist列表不会触发预期的遍历匹配逻辑。
正确实现
推荐直接用Ansible原生过滤器完成双条件过滤,不需要拆分多步处理,也避免JMESPath的参数限制:
tasks: - name: 提取符合要求的挂载点 set_fact: mount_point: >- {{ ansible_facts.mounts | selectattr('fstype', 'in', ['ext2', 'ext3', 'ext4']) | map(attribute='mount') | select('match', '^(' ~ whitelist | join('|') ~ ')(/|$)') | list }} - debug: var: mount_point
关键逻辑说明:
- 正则规则
^(/boot|/home|/opt|...)(/|$)做了边界校验,避免出现/匹配到/tmp、/home匹配到/home2这类误判 - 所有过滤逻辑在同一个表达式中完成,不需要额外定义未使用的查询变量
如果不想使用正则,也可以通过循环判断的方式实现过滤:
tasks: - name: 过滤符合文件系统类型要求的挂载点 set_fact: fs_valid_mounts: "{{ ansible_facts.mounts | selectattr('fstype', 'in', ['ext2','ext3','ext4']) | map(attribute='mount') | list }}" - name: 过滤符合白名单前缀要求的挂载点 set_fact: mount_point: "{{ mount_point | default([]) + [item] }}" loop: "{{ fs_valid_mounts }}" when: whitelist | select('match', '^' ~ item ~ '(/|$)') | length > 0
两种写法最终都会过滤掉/、/tmp、/var/tmp这类不符合前缀要求的挂载点,返回符合预期的结果。
内容的提问来源于stack exchange,提问作者medisamm
相关产品推荐
相关产品推荐

