Ansible 过滤器可以返回未定义吗?

问题描述 投票:0回答:2

我正在编写一个 Ansible 过滤器。有没有可能这个过滤器返回

Undefined
,所以结果输出是
Undefined

我的目标是在以下情况下可以将过滤器与

default
select
结合使用:

my_variable: "{{ my_input | myfilter | default(something) }}"

或:

my_list: "{{ list_of_things | map('myfilter') | select | list }}"

例如:

- vars:
    area: 9
    side: "{{ area | my_sqrt | default(0) }}"
  debug:
    var: side

或:

- vars:
    numbers: [16, 9, -4]
    roots: "{{ numbers | map('my_sqrt') | select | list }}"
  debug:
    var: roots

其中

map('my_sqrt')
应输出
[4, 3, Undefined]
select
应将其转换为
[4, 3]

我尝试将

my_sqrt
定义为:

import math
from jinja2.runtime import Undefined

def my_sqrt(input):
    if input < 0:
        return Undefined
    else:
        return math.sqrt(input)

class FilterModule(object):
    def filters(self):
        return { 'my_sqrt': my_sqrt }

但是,当我执行上面的剧本时,它似乎返回字符串

<class 'jinja2.runtime.Undefined'>

进一步的测试证明了这一点:

('never' if 0) | default('the_default')
确实返回了
the_default

然而,

-4 | my_sqrt | default('the_default')
遗憾地返回了字符串
"<class 'jinja2.runtime.Undefined'>"

我都尝试过

jinja2.runtime.Undefined
ansible.template.AnsibleUndefined

我主要想知道 Ansible/Jinja2 过滤器是否可以返回

Undefined

如果这是不可能的,我有兴趣听到替代方法。我实际上正在编写的是一个过滤器,它接受一个对象和一个选项列表(可能的匹配),并将返回最佳选项(最佳匹配)。如果没有好的选择,它应该返回

Undefined
,之后由剧本来处理这种情况。作为基本要求,它应该适用于
default
select

ansible jinja2 ansible-filter
2个回答
3
投票

您可以将

filter
定义为返回
None
而不是
Undefined
。然后,您可以将输出通过管道传输到
select
default
过滤器,但需要注意。

None
将被
default
过滤器解释为布尔值。因此,您必须将值
true
作为
default
过滤器的第二个参数传递,才能使其按预期工作。

使用此代码尝试一下:

过滤器定义:

#!/bin/bash

import math
import typing as t

V = t.TypeVar("V")

def my_sqrt(value: V) -> t.Union[V , None]:
    if value < 0:
        return None
    else:
        return math.sqrt(value)

class FilterModule(object):
    def filters(self):
        return {
            'my_sqrt': my_sqrt
        }

剧本示例:

- hosts: localhost
  gather_facts: no
  tasks:
    - name: Test the my_sqrt filter
      debug:
        msg: "{{ item | my_sqrt | default(0, true) }}"
      loop:
        - 4
        - -1

    - name: Select
      debug:
        msg: "{{ [4, 3, -4] | map('my_sqrt') | select | list }}"

您会看到两个

tasks
都按预期工作:


PLAY [localhost] ***************************************************************
Friday 14 May 2021  09:27:41 -0300 (0:00:00.019)       0:00:00.019 ************ 

TASK [Test the my_sqrt filter] *************************************************
ok: [localhost] => (item=4) => 
  msg: '2.0'
ok: [localhost] => (item=-1) => 
  msg: '0'
Friday 14 May 2021  09:27:41 -0300 (0:00:00.042)       0:00:00.062 ************ 

TASK [Select] ******************************************************************
ok: [localhost] => 
  msg:
  - 2.0
  - 1.7320508075688772

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

Friday 14 May 2021  09:27:41 -0300 (0:00:00.044)       0:00:00.106 ************ 
=============================================================================== 
Select ------------------------------------------------------------------ 0.04s
Test the my_sqrt filter ------------------------------------------------- 0.04s

0
投票

这在默认设置下(非 Jinja 原生)似乎是不可能的,因为 Ansible 非常努力将返回值粉碎为字符串类型。 (至少我做不到,除了以感觉脆弱的方式对 Ansible 核心进行猴子修补。)

另一方面,如果您在包装器 shell 脚本中设置

ANSIBLE_JINJA2_NATIVE=1
;或者有一个
ansible.cfg
文件,内容如下:

[defaults] jinja_native = true
(我谦虚地建议你养成习惯,至少在新项目中);然后从过滤器或查找插件返回“真实”未定义值是小菜一碟:

from jinja2.runtime import Undefined class LookupModule(LookupBase): def run(self, terms, variables=None, **kwargs): return [Undefined("Don't look up")]
然后,

| default

is defined
等将按照您的预期工作,并且您可以自定义错误消息(通过 
Undefined
 构造函数参数,如上所示),Ansible 将在未定义的值传播到顶级。

© www.soinside.com 2019 - 2024. All rights reserved.