前言:
filter_plugins是什么? 这个单词拆解下,filter ! 熟悉jinja2模板的人,到知道他是过滤器,可以在模板中以管道的方式用pyhton的代码处理字符串。 ansible模板调用的是jinja2,这个大家都知道 。 这个filter_plugins插件代码,就是为了更好的处理jinja2模板中的字符串和逻辑判断的。 先前,我和沈灿讨论一个模板的问题,实在蛋疼的要命,总是达不到我们要的数据,当时是做一个数据的统计和rabbitmq的配置,有些地方用jinja2模板自身的语法,处理起来实在是费劲, 用了多级{% set %}和各种length 。 其实一开始我就想用jinja2的filter,结果没看到相应的文档说明。 原来还计划着,在纠结下ansible template的代码,把jinja2的过滤器搞进去,要是不成功,自己外置一个api做处理。 看来,ansible那帮人,把能想到的东西都给做了。
这两天又看了下文档,发现讲插件的list里面有个filter_plugins,然后看了下说明,果然是他 !
更多的有关ansible的文章,请移步到 http://xiaorui.cc
ansible支持jinja2中默认的内置过滤器用法的,这些是一部分 ! 具体的每个功能我就不详细说了,大家自己测测就知道用途了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
abs(number)
绝对值
attr(obj, name)
属性
{{ my_variable|default(
'my_variable is not defined'
) }}
如果没有值,可以定义默认的
{%
for
item
in
mydict|dictsort %}
sort
the dict by key,
case
insensitive
{%
for
item
in
mydict|dictsort(
true
) %}
sort
the dict by key,
case
sensitive
{%
for
item
in
mydict|dictsort(
false
,
'value'
) %}
sort
the dict by key,
case
insensitive, sorted
normally and ordered by value.
escape(s)
安全的代码模式
first(
seq
)
第一个
float(value, default=0.0)
浮点型
forceescape(value)
强制html转义
indent(s, width=4, indentfirst=False)
{{ mytext|indent(2,
true
) }}
{{ [1, 2, 3]|
join
(
'|'
) }}
-> 1|2|3
{{ [1, 2, 3]|
join
}}
-> 123
{{
users
|
join
(
', '
, attribute=
'username'
) }}
last(
seq
)
Return the last item of a sequence.
length(object)
Return the number of items of a sequence or mapping.
lower(s)
Convert a value to lowercase.
random(
seq
)
Return a random item from the sequence.
reject()
Filters a sequence of objects by appying a
test
to the object and rejecting the ones with the
test
succeeding.
Example usage:
{{ numbers|reject(
"odd"
) }}
New
in
version 2.7.
rejectattr()
Filters a sequence of objects by appying a
test
to an attribute of an object or the attribute and rejecting the ones with the
test
succeeding.
{{
users
|rejectattr(
"is_active"
) }}
{{
users
|rejectattr(
"email"
,
"none"
) }}
New
in
version 2.7.
replace(s, old, new, count=None)
{{
"Hello World"
|replace(
"Hello"
,
"Goodbye"
) }}
-> Goodbye World
{{
"aaaaargh"
|replace(
"a"
,
"d'oh, "
, 2) }}
-> d
'oh, d'
oh, aaargh
round(value, precision=0, method=
'common'
)
{{ 42.55|round }}
-> 43.0
{{ 42.55|round(1,
'floor'
) }}
-> 42.5
Note that even
if
rounded to 0 precision, a float is returned. If you need a real integer, pipe it through int:
{{ 42.55|round|int }}
-> 43
safe(value)
Mark the value as safe
which
means that
in
an environment with automatic escaping enabled this variable will not be escaped.
select
()
Filters a sequence of objects by appying a
test
to the object and only selecting the ones with the
test
succeeding.
Example usage:
{{ numbers|
select
(
"odd"
) }}
{{ numbers|
select
(
"odd"
) }}
New
in
version 2.7.
selectattr()
Example usage:
{{
users
|selectattr(
"is_active"
) }}
{{
users
|selectattr(
"email"
,
"none"
) }}
{%
for
item
in
iterable|
sort
%}
...
{% endfor %}
It is also possible to
sort
by an attribute (
for
example to
sort
by the
date
of an object) by specifying the attribute parameter:
{%
for
item
in
iterable|
sort
(attribute=
'date'
) %}
...
{% endfor %}
Changed
in
version 2.6: The attribute parameter was added.
string(object)
Make a string unicode
if
it isn’t already. That way a markup string is not converted back to unicode.
upper(s)
Convert a value to uppercase.
urlencode(value)
Escape strings
for
use
in
URLs (uses UTF-8 encoding). It accepts both dictionaries and regular strings as well as pairwise iterables.
wordcount(s)
个数
|
下面是实现自定义的jinja2 filter的代码。 里面已经实现了调用ansible的template的时候,有可能会用到的filter过滤器。
原文: http://rfyiamcool.blog.51cto.com/1030776/1440686
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
#xiaorui.cc
import
base64
import
json
import
os.path
import
yaml
import
types
import
pipes
import
glob
import
re
import
operator as py_operator
from ansible
import
errors
from ansible.utils
import
md5s
from distutils.version
import
LooseVersion, StrictVersion
from random
import
SystemRandom
from jinja2.filters
import
environmentfilter
def to_nice_yaml(*a, **kw):
''
'Make verbose, human readable yaml'
''
return
yaml.safe_dump(*a, indent=4, allow_unicode=True, default_flow_style=False, **kw)
def to_json(a, *args, **kw):
''
' Convert the value to JSON '
''
return
json.dumps(a, *args, **kw)
def to_nice_json(a, *args, **kw):
''
'Make verbose, human readable JSON'
''
return
json.dumps(a, indent=4, sort_keys=True, *args, **kw)
def failed(*a, **kw):
''
' Test if task result yields failed '
''
item = a[0]
if
type
(item) != dict:
raise errors.AnsibleFilterError(
"|failed expects a dictionary"
)
rc = item.get(
'rc'
,0)
failed = item.get(
'failed'
,False)
if
rc != 0 or failed:
return
True
else
:
return
False
def success(*a, **kw):
''
' Test if task result yields success '
''
return
not failed(*a, **kw)
def changed(*a, **kw):
''
' Test if task result yields changed '
''
item = a[0]
if
type
(item) != dict:
raise errors.AnsibleFilterError(
"|changed expects a dictionary"
)
if
not
'changed'
in
item:
changed = False
if
(
'results'
in
item
# some modules return a 'results' key
and
type
(item[
'results'
]) == list
and
type
(item[
'results'
][0]) == dict):
for
result
in
item[
'results'
]:
changed = changed or result.get(
'changed'
, False)
else
:
changed = item.get(
'changed'
, False)
return
changed
def skipped(*a, **kw):
''
' Test if task result yields skipped '
''
item = a[0]
if
type
(item) != dict:
raise errors.AnsibleFilterError(
"|skipped expects a dictionary"
)
skipped = item.get(
'skipped'
, False)
return
skipped
def mandatory(a):
''
' Make a variable mandatory '
''
try:
a
except NameError:
raise errors.AnsibleFilterError(
'Mandatory variable not defined.'
)
else
:
return
a
def bool(a):
''
' return a bool for the arg '
''
if
a is None or
type
(a) == bool:
return
a
if
type
(a)
in
types.StringTypes:
a = a.lower()
if
a
in
[
'yes'
,
'on'
,
'1'
,
'true'
, 1]:
return
True
else
:
return
False
def quote(a):
''
' return its argument quoted for shell usage '
''
return
pipes.quote(a)
def fileglob(pathname):
''
' return list of matched files for glob '
''
return
glob.glob(pathname)
def regex(value=
''
, pattern=
''
, ignorecase=False, match_type=
'search'
):
''
' Expose `re` as a boolean filter using the `search` method by default.
This is likely only useful
for
`search` and `match`
which
already
have their own filters.
''
'
if
ignorecase:
flags = re.I
else
:
flags = 0
_re = re.compile(pattern, flags=flags)
_bool = __builtins__.get(
'bool'
)
return
_bool(getattr(_re, match_type,
'search'
)(value))
def match(value, pattern=
''
, ignorecase=False):
''
' Perform a `re.match` returning a boolean '
''
return
regex(value, pattern, ignorecase,
'match'
)
def search(value, pattern=
''
, ignorecase=False):
''
' Perform a `re.search` returning a boolean '
''
return
regex(value, pattern, ignorecase,
'search'
)
def regex_replace(value=
''
, pattern=
''
, replacement=
''
, ignorecase=False):
''
' Perform a `re.sub` returning a string '
''
if
not isinstance(value, basestring):
value = str(value)
if
ignorecase:
flags = re.I
else
:
flags = 0
_re = re.compile(pattern, flags=flags)
return
_re.sub(replacement, value)
def unique(a):
return
set
(a)
def intersect(a, b):
return
set
(a).intersection(b)
def difference(a, b):
return
set
(a).difference(b)
def symmetric_difference(a, b):
return
set
(a).symmetric_difference(b)
def union(a, b):
return
set
(a).union(b)
def version_compare(value, version, operator=
'eq'
, strict=False):
''
' Perform a version comparison on a value '
''
op_map = {
'=='
:
'eq'
,
'='
:
'eq'
,
'eq'
:
'eq'
,
'<'
:
'lt'
,
'lt'
:
'lt'
,
'<='
:
'le'
,
'le'
:
'le'
,
'>'
:
'gt'
,
'gt'
:
'gt'
,
'>='
:
'ge'
,
'ge'
:
'ge'
,
'!='
:
'ne'
,
'<>'
:
'ne'
,
'ne'
:
'ne'
}
if
strict:
Version = StrictVersion
else
:
Version = LooseVersion
if
operator
in
op_map:
operator = op_map[operator]
else
:
raise errors.AnsibleFilterError(
'Invalid operator type'
)
try:
method = getattr(py_operator, operator)
return
method(Version(str(value)), Version(str(version)))
except Exception, e:
raise errors.AnsibleFilterError(
'Version comparison: %s'
% e)
@environmentfilter
def rand(environment, end, start=None, step=None):
r = SystemRandom()
if
isinstance(end, (int, long)):
if
not start:
start = 0
if
not step:
step = 1
return
r.randrange(start, end, step)
elif
hasattr(end,
'__iter__'
):
if
start or step:
raise errors.AnsibleFilterError(
'start and step can only be used with integer values'
)
return
r.choice(end)
else
:
raise errors.AnsibleFilterError(
'random can only be used on sequences and integers'
)
class FilterModule(object):
''
' Ansible core jinja2 filters '
''
def filters(self):
return
{
# base 64
'b64decode'
: base64.b64decode,
'b64encode'
: base64.b64encode,
# json
'to_json'
: to_json,
'to_nice_json'
: to_nice_json,
'from_json'
: json.loads,
# yaml
'to_yaml'
: yaml.safe_dump,
'to_nice_yaml'
: to_nice_yaml,
'from_yaml'
: yaml.safe_load,
# path
'basename'
: os.path.
basename
,
'dirname'
: os.path.
dirname
,
'expanduser'
: os.path.expanduser,
'realpath'
: os.path.realpath,
'relpath'
: os.path.relpath,
# failure testing
'failed'
: failed,
'success'
: success,
# changed testing
'changed'
: changed,
# skip testing
'skipped'
: skipped,
# variable existence
'mandatory'
: mandatory,
# value as boolean
'bool'
: bool,
# quote string for shell usage
'quote'
: quote,
# md5 hex digest of string
'md5'
: md5s,
# file glob
'fileglob'
: fileglob,
# regex
'match'
: match,
'search'
: search,
'regex'
: regex,
'regex_replace'
: regex_replace,
# list
'unique'
: unique,
'intersect'
: intersect,
'difference'
: difference,
'symmetric_difference'
: symmetric_difference,
'union'
: union,
# version comparison
'version_compare'
: version_compare,
# random numbers
'random'
: rand,
}
|
模板的代码,这里只是做个测试而已。模板里面的变量不是从外面引入的,是我自己设的变量。
原文: http://rfyiamcool.blog.51cto.com/1030776/1440686
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
this is ceshi
{%
set
list1 = [1,2,3,4,5,6,7,8,9,10] %}
{%
for
i
in
list1 %}
{{ i }}
{% endfor %}
{{ list1|to_nice_yaml }}
{%
set
list2 = [
'k_1'
,
'k_2'
,
'k_3'
] %}
to_replace .....
{%
for
i
in
list2 %}
{{ i|to_replace }}
{% endfor %}
|
这里是结果,可以看到咱们搞的那两个过滤器成功执行了。
原文: http://rfyiamcool.blog.51cto.com/1030776/1440686
ansible有个filter_plugins插件利器,在使用jinja2模板渲染数据的时候,就更加的方便了 !!!
本文转自 rfyiamcool 51CTO博客,原文链接:http://blog.51cto.com/rfyiamcool/1440686,如需转载请自行联系原作者