mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
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_roles
|
||||||
playbooks_variables
|
playbooks_variables
|
||||||
playbooks_filters
|
playbooks_filters
|
||||||
|
playbooks_tests
|
||||||
playbooks_conditionals
|
playbooks_conditionals
|
||||||
playbooks_loops
|
playbooks_loops
|
||||||
playbooks_blocks
|
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
|
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.
|
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.
|
In addition to those, Ansible supplies many more.
|
||||||
|
|
||||||
.. _filters_for_formatting_data:
|
.. _filters_for_formatting_data:
|
||||||
|
@ -43,37 +45,6 @@ for example::
|
||||||
|
|
||||||
- set_fact: myvar="{{ result.stdout | from_json }}"
|
- 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:
|
||||||
|
|
||||||
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) }}
|
{{ 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:
|
.. _random_filter:
|
||||||
|
|
||||||
|
@ -245,10 +192,6 @@ Math
|
||||||
.. versionadded:: 1.9
|
.. versionadded:: 1.9
|
||||||
|
|
||||||
|
|
||||||
To see if something is actually a number::
|
|
||||||
|
|
||||||
{{ myvar | isnan }}
|
|
||||||
|
|
||||||
Get the logarithm (default is e)::
|
Get the logarithm (default is e)::
|
||||||
|
|
||||||
{{ myvar | log }}
|
{{ myvar | log }}
|
||||||
|
@ -539,20 +482,6 @@ doesn't know it is a boolean value::
|
||||||
- debug: msg=test
|
- debug: msg=test
|
||||||
when: some_string_value | bool
|
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
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
To replace text in a string with regex, use the "regex_replace" filter::
|
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 json
|
||||||
import os.path
|
import os.path
|
||||||
import ntpath
|
import ntpath
|
||||||
import types
|
|
||||||
import pipes
|
import pipes
|
||||||
import glob
|
import glob
|
||||||
import re
|
import re
|
||||||
|
@ -34,13 +33,11 @@ import crypt
|
||||||
import hashlib
|
import hashlib
|
||||||
import string
|
import string
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import operator as py_operator
|
|
||||||
from random import SystemRandom, shuffle
|
from random import SystemRandom, shuffle
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from jinja2.filters import environmentfilter
|
from jinja2.filters import environmentfilter
|
||||||
from distutils.version import LooseVersion, StrictVersion
|
|
||||||
from ansible.compat.six import iteritems, string_types
|
from ansible.compat.six import iteritems, string_types
|
||||||
|
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
|
@ -186,32 +183,6 @@ def ternary(value, true_val, false_val):
|
||||||
return 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):
|
def regex_escape(string):
|
||||||
'''Escape all regular expressions special characters from STRING.'''
|
'''Escape all regular expressions special characters from STRING.'''
|
||||||
|
@ -261,7 +232,6 @@ def get_encrypted_password(password, hashtype='sha512', salt=None):
|
||||||
'sha512': '6',
|
'sha512': '6',
|
||||||
}
|
}
|
||||||
|
|
||||||
hastype = hashtype.lower()
|
|
||||||
if hashtype in cryptmethod:
|
if hashtype in cryptmethod:
|
||||||
if salt is None:
|
if salt is None:
|
||||||
r = SystemRandom()
|
r = SystemRandom()
|
||||||
|
@ -461,9 +431,6 @@ class FilterModule(object):
|
||||||
'ternary': ternary,
|
'ternary': ternary,
|
||||||
|
|
||||||
# list
|
# list
|
||||||
# version comparison
|
|
||||||
'version_compare': version_compare,
|
|
||||||
|
|
||||||
# random stuff
|
# random stuff
|
||||||
'random': rand,
|
'random': rand,
|
||||||
'shuffle': randomize_list,
|
'shuffle': randomize_list,
|
||||||
|
|
|
@ -585,7 +585,6 @@ def nthhost(value, query=''):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vsize = ipaddr(v, 'size')
|
|
||||||
nth = int(query)
|
nth = int(query)
|
||||||
if value.size > nth:
|
if value.size > nth:
|
||||||
return value[nth]
|
return value[nth]
|
||||||
|
|
|
@ -70,12 +70,6 @@ def max(a):
|
||||||
_max = __builtins__.get('max')
|
_max = __builtins__.get('max')
|
||||||
return _max(a);
|
return _max(a);
|
||||||
|
|
||||||
def isnotanumber(x):
|
|
||||||
try:
|
|
||||||
return math.isnan(x)
|
|
||||||
except TypeError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def logarithm(x, base=math.e):
|
def logarithm(x, base=math.e):
|
||||||
try:
|
try:
|
||||||
|
@ -136,7 +130,6 @@ class FilterModule(object):
|
||||||
def filters(self):
|
def filters(self):
|
||||||
return {
|
return {
|
||||||
# general math
|
# general math
|
||||||
'isnan': isnotanumber,
|
|
||||||
'min' : min,
|
'min' : min,
|
||||||
'max' : max,
|
'max' : max,
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,9 @@ from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import operator as py_operator
|
||||||
|
from distutils.version import LooseVersion, StrictVersion
|
||||||
|
|
||||||
from ansible import errors
|
from ansible import errors
|
||||||
|
|
||||||
def failed(*a, **kw):
|
def failed(*a, **kw):
|
||||||
|
@ -84,6 +87,33 @@ def search(value, pattern='', ignorecase=False, multiline=False):
|
||||||
''' Perform a `re.search` returning a boolean '''
|
''' Perform a `re.search` returning a boolean '''
|
||||||
return regex(value, pattern, ignorecase, multiline, 'search')
|
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):
|
class TestModule(object):
|
||||||
''' Ansible core jinja2 tests '''
|
''' Ansible core jinja2 tests '''
|
||||||
|
|
||||||
|
@ -108,4 +138,7 @@ class TestModule(object):
|
||||||
'search': search,
|
'search': search,
|
||||||
'regex': regex,
|
'regex': regex,
|
||||||
|
|
||||||
|
# version comparison
|
||||||
|
'version_compare': version_compare,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,20 @@
|
||||||
from __future__ import (absolute_import, division, print_function)
|
from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
def issubset(a, b):
|
def issubset(a, b):
|
||||||
return set(a) <= set(b)
|
return set(a) <= set(b)
|
||||||
|
|
||||||
def issuperset(a, b):
|
def issuperset(a, b):
|
||||||
return set(a) >= set(b)
|
return set(a) >= set(b)
|
||||||
|
|
||||||
|
def isnotanumber(x):
|
||||||
|
try:
|
||||||
|
return math.isnan(x)
|
||||||
|
except TypeError:
|
||||||
|
return False
|
||||||
|
|
||||||
class TestModule:
|
class TestModule:
|
||||||
''' Ansible math jinja2 tests '''
|
''' Ansible math jinja2 tests '''
|
||||||
|
|
||||||
|
@ -32,4 +40,5 @@ class TestModule:
|
||||||
# set theory
|
# set theory
|
||||||
'issubset': issubset,
|
'issubset': issubset,
|
||||||
'issuperset': issuperset,
|
'issuperset': issuperset,
|
||||||
|
'isnan': isnotanumber,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue