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:
Brian Coca 2016-07-12 10:13:00 -04:00 committed by GitHub
parent 245ce9461d
commit ed7623ecde
8 changed files with 210 additions and 114 deletions

View file

@ -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

View file

@ -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::

View 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

View file

@ -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,

View file

@ -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]

View file

@ -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,

View file

@ -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,
}

View file

@ -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,
}