官术网_书友最值得收藏!

Understanding Jinja2 filters

As Ansible is written in Python, it inherits an incredibly useful and powerful templating engine called Jinja2. We will look at the concept of templating later in this book, so for now, we will focus on one particular aspect of Jinja2 known as filtering. Jinja2 filters provide an incredibly powerful framework that you can use to manipulate and transform your data. Perhaps you have a string that you need to convert to lowercase, for example—you could apply a Jinja2 filter to achieve this. You can also use it to perform pattern matching, search and replace operations, and much more. There are many hundreds of filters for you to work with and in this section, we hope to empower you with a basic understanding of Jinja2 filters and some practical knowledge about how to apply them, as well as show you where to get more information about them if you wish to explore the subject further. 

It is worth noting that Jinja2 operations are performed on the Ansible control host and only the results of the filter operation are sent to the remote hosts. This is done by design, both for consistency and to reduce the workload on the individual nodes as much as possible. 

Let's explore this through a practical example. Suppose we have a YAML file containing some data that we want to parse. We can quite easily read a file from the machine filesystem and capture the result using the register keyword (register captures the result of the task and stores it in a variable—in the case of running the shell module, it captures all the output from the command that was run).

Our YAML data file might look as follows:

tags:
- key: job
value: developer
- key: language
value: java

Now, we could create a playbook to read this file and register the result, but how can we actually turn it into a variable structure that Ansible can understand and work with? Let's consider the following playbook:

---
- name: Jinja2 filtering demo 1
hosts: localhost

tasks:
- copy:
src: multiple-document-strings.yaml
dest: /tmp/multiple-document-strings.yaml
- shell: cat /tmp/multiple-document-strings.yaml
register: result
- debug:
msg: '{{ item }}'
loop: '{{ result.stdout | from_yaml_all | list }}'

The shell module does not necessarily run from the directory that the playbook is stored in, so we cannot guarantee that it will find our multiple-document-strings.yaml file. The copy module does, however, source the file from the current directory, so it is useful to use it to copy it to a known location (such as /tmp) for the shell module to read the file from. The debug module is then run in a loop module. The loop module is used to iterate over all of the lines of stdout from the shell command, as we are using two Jinja2 filters—from_yaml_all and list.

The from_yaml_all filter parses the source document lines as YAML and then the list filter converts the parsed data into a valid Ansible list. If we run the playbook, we should see Ansible's representation of the data structure from within our original file:

$ ansible-playbook -i localhost, jinja-filtering1.yml

PLAY [Jinja2 filtering demo 1] *************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [copy] ********************************************************************
ok: [localhost]

TASK [shell] *******************************************************************
changed: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => (item={'tags': [{'value': u'developer', 'key': u'job'}, {'value': u'java', 'key': u'language'}]}) => {
"msg": {
"tags": [
{
"key": "job",
"value": "developer"
},
{
"key": "language",
"value": "java"
}
]
}
}

PLAY RECAP *********************************************************************
localhost : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

As you can see, we have generated a list of dictionaries that in themselves contain the key-value pairs.

If this data structure was already stored in our playbook, we could take this one step further and use the items2dict filter to turn the list into true key: value pairs, removing the key and value items from the data structure. For example, consider this second playbook:

---
- name: Jinja2 filtering demo 2
hosts: localhost
vars:
tags:
- key: job
value: developer
- key: language
value: java

tasks:
- debug:
msg: '{{ tags | items2dict }}'

Now, if we run this, we can see that our data is converted into a nice neat set of key: value pairs:

$ ansible-playbook -i localhost, jinja2-filtering2.yml
[WARNING]: Found variable using reserved name: tags

PLAY [Jinja2 filtering demo 2] *************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"job": "developer",
---
"language": "java"
}
}

PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Observe the warning at the top of the playbook. Ansible displays a warning if you attempt to use a reserved name for a variable, as we did here. Normally, you should not create a variable with a reserved name, but the example here demonstrates both how the filter works and how Ansible will attempt to warn you if you do something that might cause problems.

Earlier in this section, we used the shell module to read a file and used register to store the result in a variable. This is perfectly fine, if a little inelegant. Jinja2 contains a series of lookup filters that, among other things, can read the contents of a given file. Let's examine the behavior of this following playbook::

---
- name: Jinja2 filtering demo 3
hosts: localhost
vars:
ping_value: "{{ lookup('file', '/etc/hosts') }}"
  tasks:
- debug:
msg: "ping value is {{ ping_value }}"

When we run this, we can see that Ansible has captured the contents of the /etc/hosts file for us, without us needing to resort to the copy and shell modules as we did earlier:

$ ansible-playbook -i localhost, jinja2-filtering3.yml

PLAY [Jinja2 filtering demo 3] *************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "ping value is 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4\n::1 localhost localhost.localdomain localhost6 localhost6.localdomain6\n\n"
}

PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

There are many other filters that you might be interested in exploring and a full list can be found in the official Jinja2 documentation (https://jinja.palletsprojects.com/en/2.11.x/). The following are a handful of other examples that will give you an idea of the kinds of things that Jinja2 filters can achieve for you, from quoting strings to concatenating lists to obtaining useful path information for a file:

# Add some quotation in the shell
- shell: echo {{ string_value | quote }}

# Concatenate a list into a specific string
{{ list | join("$") }}

# Have the last name of a specific file path
{{ path | basename }}

# Have the directory from a specific path
{{ path | dirname }}

# Have
the directory from a specific windows path
{{ path | win_dirname }}

That concludes our look at Jinja2 filtering. It is a massive topic that deserves a book all to itself, but, as ever, I hope that this practical guide has given you some pointers on how to get started and where to find information.

主站蜘蛛池模板: 诸城市| 绥棱县| 阿瓦提县| 翁牛特旗| 榕江县| 家居| 突泉县| 东台市| 淮北市| 西丰县| 宁德市| 阳朔县| 雷山县| 海原县| 锦屏县| 永嘉县| 汤原县| 遂川县| 石家庄市| 彩票| 墨竹工卡县| 张掖市| 湛江市| 罗定市| 霞浦县| 昌江| 巴马| 兴业县| 泰来县| 平利县| 封丘县| 三门县| 达拉特旗| 赤水市| 乳源| 万载县| 潢川县| 隆昌县| 盱眙县| 贵港市| 平乐县|