ansible playbook 一个非常强大的功能就是允许我们自定义filter_plugins,这个filter_plugin是什么呢?
就是我们一般看到的
{{item|max}}
其实,后边的这个 max就是一个函数,我们可以定义自己的函数,例如:
|-- filter_plugins
| |-- zhiming_filter.py
| `-- zhiming_filter.pyc
`-- main.yml
目录结构就是我们在我们的playbook下创建filter_plugins文件夹,然后写一个python文件,里边定义一些方法:
例如我的:
#!/usr/bin/env python
# coding=utf-8
'''
Created on Apr 13, 2016
@author: zhizhang
'''
import re
class FilterModule(object):
@staticmethod
def get_ebs_id(data):
patten = re.compile(r'xiaoming-[a-zA-Z0-9]{8}')
if patten.search(data):
return patten.search(data).group()
return None
def filters(self):
''' returns a mapping of filters to methods '''
return {
"get_ebs_id": self.get_ebs_id,
}
这个函数的功能事,例如我们有一串字符串
xiaoming-s7d7f7s7-we32sdfa-asdfasdf
我们只想取到xiaoming-s7d7f7s7
我们直接在playbook中使用我的方法就可以了,例如
– debug: msg=” the id infof is {{abc|get_ebs_id}}”
然后就可以了
例子,ilter_plugins插件实现jinja2模板filter过滤器
ansible支持jinja2中默认的内置过滤器用法的,这些是一部分 ! 具体的每个功能我就不详细说了,大家自己测测就知道用途了。
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过滤器。
#111cn.net
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,
}
模板的代码,这里只是做个测试而已。模板里面的变量不是从外面引入的,是我自己设的变量。
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 %}