J2 test docs (#16646)
* moved tests from filters to actual jinja2 tests also removed some unused declarations and imports * split tests into their own docs removed isnan as existing jinja2's 'number' already covers same added missing docs for several tests * updated as per feedback
This commit is contained in:
parent
245ce9461d
commit
ed7623ecde
8 changed files with 210 additions and 114 deletions
|
@ -22,6 +22,7 @@ It is recommended to look at `Example Playbooks <https://github.com/ansible/ansi
|
|||
playbooks_roles
|
||||
playbooks_variables
|
||||
playbooks_filters
|
||||
playbooks_tests
|
||||
playbooks_conditionals
|
||||
playbooks_loops
|
||||
playbooks_blocks
|
||||
|
|
|
@ -7,6 +7,8 @@ Jinja2 filters
|
|||
Filters in Jinja2 are a way of transforming template expressions from one kind of data into another. Jinja2
|
||||
ships with many of these. See `builtin filters`_ in the official Jinja2 template documentation.
|
||||
|
||||
Take into account that filters always execute on the Ansible controller, **not** on the task target, as they manipulate local data.
|
||||
|
||||
In addition to those, Ansible supplies many more.
|
||||
|
||||
.. _filters_for_formatting_data:
|
||||
|
@ -43,37 +45,6 @@ for example::
|
|||
|
||||
- set_fact: myvar="{{ result.stdout | from_json }}"
|
||||
|
||||
.. _filters_used_with_conditionals:
|
||||
|
||||
Filters Often Used With Conditionals
|
||||
------------------------------------
|
||||
|
||||
The following tasks are illustrative of how filters can be used with conditionals::
|
||||
|
||||
tasks:
|
||||
|
||||
- shell: /usr/bin/foo
|
||||
register: result
|
||||
ignore_errors: True
|
||||
|
||||
- debug: msg="it failed"
|
||||
when: result|failed
|
||||
|
||||
# in most cases you'll want a handler, but if you want to do something right now, this is nice
|
||||
- debug: msg="it changed"
|
||||
when: result|changed
|
||||
|
||||
- debug: msg="it succeeded in Ansible >= 2.1"
|
||||
when: result|succeeded
|
||||
|
||||
- debug: msg="it succeeded"
|
||||
when: result|success
|
||||
|
||||
- debug: msg="it was skipped"
|
||||
when: result|skipped
|
||||
|
||||
.. note:: From 2.1 You can also use success, failure, change, skip so the grammer matches, for those that want to be strict about it.
|
||||
|
||||
.. _forcing_variables_to_be_defined:
|
||||
|
||||
Forcing Variables To Be Defined
|
||||
|
@ -170,30 +141,6 @@ To get the symmetric difference of 2 lists (items exclusive to each list)::
|
|||
|
||||
{{ list1 | symmetric_difference(list2) }}
|
||||
|
||||
.. _version_comparison_filters:
|
||||
|
||||
Version Comparison Filters
|
||||
--------------------------
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
To compare a version number, such as checking if the ``ansible_distribution_version``
|
||||
version is greater than or equal to '12.04', you can use the ``version_compare`` filter.
|
||||
|
||||
The ``version_compare`` filter can also be used to evaluate the ``ansible_distribution_version``::
|
||||
|
||||
{{ ansible_distribution_version | version_compare('12.04', '>=') }}
|
||||
|
||||
If ``ansible_distribution_version`` is greater than or equal to 12, this filter will return True, otherwise it will return False.
|
||||
|
||||
The ``version_compare`` filter accepts the following operators::
|
||||
|
||||
<, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
|
||||
|
||||
This filter also accepts a 3rd parameter, ``strict`` which defines if strict version parsing should
|
||||
be used. The default is ``False``, and if set as ``True`` will use more strict version parsing::
|
||||
|
||||
{{ sample_version_var | version_compare('1.0', operator='lt', strict=True) }}
|
||||
|
||||
.. _random_filter:
|
||||
|
||||
|
@ -245,10 +192,6 @@ Math
|
|||
.. versionadded:: 1.9
|
||||
|
||||
|
||||
To see if something is actually a number::
|
||||
|
||||
{{ myvar | isnan }}
|
||||
|
||||
Get the logarithm (default is e)::
|
||||
|
||||
{{ myvar | log }}
|
||||
|
@ -539,20 +482,6 @@ doesn't know it is a boolean value::
|
|||
- debug: msg=test
|
||||
when: some_string_value | bool
|
||||
|
||||
To match strings against a regex, use the "match" or "search" filter::
|
||||
|
||||
vars:
|
||||
url: "http://example.com/users/foo/resources/bar"
|
||||
|
||||
tasks:
|
||||
- shell: "msg='matched pattern 1'"
|
||||
when: url | match("http://example.com/users/.*/resources/.*")
|
||||
|
||||
- debug: "msg='matched pattern 2'"
|
||||
when: url | search("/users/.*/resources/.*")
|
||||
|
||||
'match' will require a complete match in the string, while 'search' will require a match inside of the string.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
To replace text in a string with regex, use the "regex_replace" filter::
|
||||
|
|
165
docsite/rst/playbooks_tests.rst
Normal file
165
docsite/rst/playbooks_tests.rst
Normal file
|
@ -0,0 +1,165 @@
|
|||
Jinja2 tests
|
||||
============
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
|
||||
Tests in Jinja2 are a way of evaluating template expressions and returning True or False.
|
||||
Jinja2 ships with many of these. See `builtin tests`_ in the official Jinja2 template documentation.
|
||||
Tests are very similar to filters and are used mostly the same way, but they can also be used in list
|
||||
processing filters, like C(map()) and C(select()) to choose items in the list.
|
||||
|
||||
Like filters, tests always execute on the Ansible controller, **not** on the target of a task, as they test local data.
|
||||
|
||||
In addition to those Jinja2 tests, Ansible supplies a few more and users can easily create their own.
|
||||
|
||||
.. _testing_strings:
|
||||
|
||||
Testing strings
|
||||
---------------
|
||||
|
||||
To match strings against a substring or a regex, use the "match" or "search" filter::
|
||||
|
||||
vars:
|
||||
url: "http://example.com/users/foo/resources/bar"
|
||||
|
||||
tasks:
|
||||
- shell: "msg='matched pattern 1'"
|
||||
when: url | match("http://example.com/users/.*/resources/.*")
|
||||
|
||||
- debug: "msg='matched pattern 2'"
|
||||
when: url | search("/users/.*/resources/.*")
|
||||
|
||||
- debug: "msg='matched pattern 3'"
|
||||
when: url | search("/users/")
|
||||
|
||||
'match' requires a complete match in the string, while 'search' only requires matching a subset of the string.
|
||||
|
||||
|
||||
.. _testing_versions:
|
||||
|
||||
Version Comparison
|
||||
------------------
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
To compare a version number, such as checking if the ``ansible_distribution_version``
|
||||
version is greater than or equal to '12.04', you can use the ``version_compare`` filter.
|
||||
|
||||
The ``version_compare`` filter can also be used to evaluate the ``ansible_distribution_version``::
|
||||
|
||||
{{ ansible_distribution_version | version_compare('12.04', '>=') }}
|
||||
|
||||
If ``ansible_distribution_version`` is greater than or equal to 12, this filter returns True, otherwise False.
|
||||
|
||||
The ``version_compare`` filter accepts the following operators::
|
||||
|
||||
<, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
|
||||
|
||||
This test also accepts a 3rd parameter, ``strict`` which defines if strict version parsing should
|
||||
be used. The default is ``False``, but this setting as ``True`` uses more strict version parsing::
|
||||
|
||||
{{ sample_version_var | version_compare('1.0', operator='lt', strict=True) }}
|
||||
|
||||
|
||||
.. _math_tests:
|
||||
|
||||
Group theory tests
|
||||
------------------
|
||||
|
||||
To see if a list includes or is included by another list, you can use 'issubset' and 'issuperset'::
|
||||
|
||||
vars:
|
||||
a: [1,2,3,4,5]
|
||||
b: [2,3]
|
||||
tasks:
|
||||
- debug: msg="A includes B"
|
||||
when: a|issuperset(b)
|
||||
|
||||
- debug: msg="B is included in A"
|
||||
when: b|issubset(a)
|
||||
|
||||
|
||||
.. _path_tests:
|
||||
|
||||
Testing paths
|
||||
-------------
|
||||
|
||||
The following tests can provide information about a path on the controller::
|
||||
|
||||
- debug: msg="path is a directory"
|
||||
when: mypath|isdir
|
||||
|
||||
- debug: msg="path is a file"
|
||||
when: mypath|is_file
|
||||
|
||||
- debug: msg="path is a symlink"
|
||||
when: mypath|is_link
|
||||
|
||||
- debug: msg="path already exists"
|
||||
when: mypath|exists
|
||||
|
||||
- debug: msg="path is {{ (mypath|is_abs)|ternary('absolute','relative')}}"
|
||||
|
||||
- debug: msg="path is the same file as path2"
|
||||
when: mypath|samefile(path2)
|
||||
|
||||
- debug: msg="path is a mount"
|
||||
when: mypath|ismount
|
||||
|
||||
|
||||
.. _test_task_results:
|
||||
|
||||
Task results
|
||||
------------
|
||||
|
||||
The following tasks are illustrative of the tests meant to check the status of tasks::
|
||||
|
||||
tasks:
|
||||
|
||||
- shell: /usr/bin/foo
|
||||
register: result
|
||||
ignore_errors: True
|
||||
|
||||
- debug: msg="it failed"
|
||||
when: result|failed
|
||||
|
||||
# in most cases you'll want a handler, but if you want to do something right now, this is nice
|
||||
- debug: msg="it changed"
|
||||
when: result|changed
|
||||
|
||||
- debug: msg="it succeeded in Ansible >= 2.1"
|
||||
when: result|succeeded
|
||||
|
||||
- debug: msg="it succeeded"
|
||||
when: result|success
|
||||
|
||||
- debug: msg="it was skipped"
|
||||
when: result|skipped
|
||||
|
||||
.. note:: From 2.1, you can also use success, failure, change, and skip so that the grammar matches, for those who need to be strict about it.
|
||||
|
||||
|
||||
|
||||
.. _builtin tests: http://jinja.pocoo.org/docs/templates/#builtin-tests
|
||||
|
||||
.. seealso::
|
||||
|
||||
:doc:`playbooks`
|
||||
An introduction to playbooks
|
||||
:doc:`playbooks_conditionals`
|
||||
Conditional statements in playbooks
|
||||
:doc:`playbooks_variables`
|
||||
All about variables
|
||||
:doc:`playbooks_loops`
|
||||
Looping in playbooks
|
||||
:doc:`playbooks_roles`
|
||||
Playbook organization by roles
|
||||
:doc:`playbooks_best_practices`
|
||||
Best practices in playbooks
|
||||
`User Mailing List <http://groups.google.com/group/ansible-devel>`_
|
||||
Have a question? Stop by the google group!
|
||||
`irc.freenode.net <http://irc.freenode.net>`_
|
||||
#ansible IRC chat channel
|
||||
|
||||
|
|
@ -26,7 +26,6 @@ import itertools
|
|||
import json
|
||||
import os.path
|
||||
import ntpath
|
||||
import types
|
||||
import pipes
|
||||
import glob
|
||||
import re
|
||||
|
@ -34,13 +33,11 @@ import crypt
|
|||
import hashlib
|
||||
import string
|
||||
from functools import partial
|
||||
import operator as py_operator
|
||||
from random import SystemRandom, shuffle
|
||||
import uuid
|
||||
|
||||
import yaml
|
||||
from jinja2.filters import environmentfilter
|
||||
from distutils.version import LooseVersion, StrictVersion
|
||||
from ansible.compat.six import iteritems, string_types
|
||||
|
||||
from ansible import errors
|
||||
|
@ -186,32 +183,6 @@ def ternary(value, true_val, false_val):
|
|||
return false_val
|
||||
|
||||
|
||||
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 as e:
|
||||
raise errors.AnsibleFilterError('Version comparison: %s' % e)
|
||||
|
||||
def regex_escape(string):
|
||||
'''Escape all regular expressions special characters from STRING.'''
|
||||
|
@ -261,7 +232,6 @@ def get_encrypted_password(password, hashtype='sha512', salt=None):
|
|||
'sha512': '6',
|
||||
}
|
||||
|
||||
hastype = hashtype.lower()
|
||||
if hashtype in cryptmethod:
|
||||
if salt is None:
|
||||
r = SystemRandom()
|
||||
|
@ -461,9 +431,6 @@ class FilterModule(object):
|
|||
'ternary': ternary,
|
||||
|
||||
# list
|
||||
# version comparison
|
||||
'version_compare': version_compare,
|
||||
|
||||
# random stuff
|
||||
'random': rand,
|
||||
'shuffle': randomize_list,
|
||||
|
|
|
@ -585,7 +585,6 @@ def nthhost(value, query=''):
|
|||
return False
|
||||
|
||||
try:
|
||||
vsize = ipaddr(v, 'size')
|
||||
nth = int(query)
|
||||
if value.size > nth:
|
||||
return value[nth]
|
||||
|
|
|
@ -70,12 +70,6 @@ def max(a):
|
|||
_max = __builtins__.get('max')
|
||||
return _max(a);
|
||||
|
||||
def isnotanumber(x):
|
||||
try:
|
||||
return math.isnan(x)
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
|
||||
def logarithm(x, base=math.e):
|
||||
try:
|
||||
|
@ -136,7 +130,6 @@ class FilterModule(object):
|
|||
def filters(self):
|
||||
return {
|
||||
# general math
|
||||
'isnan': isnotanumber,
|
||||
'min' : min,
|
||||
'max' : max,
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ from __future__ import (absolute_import, division, print_function)
|
|||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
import operator as py_operator
|
||||
from distutils.version import LooseVersion, StrictVersion
|
||||
|
||||
from ansible import errors
|
||||
|
||||
def failed(*a, **kw):
|
||||
|
@ -84,6 +87,33 @@ def search(value, pattern='', ignorecase=False, multiline=False):
|
|||
''' Perform a `re.search` returning a boolean '''
|
||||
return regex(value, pattern, ignorecase, multiline, 'search')
|
||||
|
||||
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 as e:
|
||||
raise errors.AnsibleFilterError('Version comparison: %s' % e)
|
||||
|
||||
class TestModule(object):
|
||||
''' Ansible core jinja2 tests '''
|
||||
|
||||
|
@ -108,4 +138,7 @@ class TestModule(object):
|
|||
'search': search,
|
||||
'regex': regex,
|
||||
|
||||
# version comparison
|
||||
'version_compare': version_compare,
|
||||
|
||||
}
|
||||
|
|
|
@ -18,12 +18,20 @@
|
|||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import math
|
||||
|
||||
def issubset(a, b):
|
||||
return set(a) <= set(b)
|
||||
|
||||
def issuperset(a, b):
|
||||
return set(a) >= set(b)
|
||||
|
||||
def isnotanumber(x):
|
||||
try:
|
||||
return math.isnan(x)
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
class TestModule:
|
||||
''' Ansible math jinja2 tests '''
|
||||
|
||||
|
@ -32,4 +40,5 @@ class TestModule:
|
|||
# set theory
|
||||
'issubset': issubset,
|
||||
'issuperset': issuperset,
|
||||
'isnan': isnotanumber,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue