ansible基础-Jinja2模版 | 过滤器
Jinja2模版介紹
注:本文demo使用ansible2.7穩定版
在ansible基礎-變量的「8.2 模版使用變量」章節中關于模版與變量也有所提及,有興趣的同學可以去回顧一下。
ansible通過Jinja2模版來實現動態表達式和變量的引用,模版的執行都是在ansible控制端完成的,所以理論上python的jinja2模塊在控制端存在就能滿足需求。
Jinja2模版都可以怎么使用?(分類)
為什么要使用Jinja2模版?(好處)
相比較于python原生的Jinja2模版,ansible擴展了很多過濾器和測試變量,同時也添加了一個新的插件「lookups」。
關于jinja2模版的基礎語法和使用,因為篇幅原因,這里不再擴展。本文著重介紹下過濾器。
數據格式化過濾器
過濾器「to_json」「to_yaml」,將變量轉換為json和yaml格式
{{ some_variable | to_json }} {{ some_variable | to_yaml }}過濾器「to_nice_json」「to_nice_yaml」,將變量轉換為更加友好的json和yaml格式
{{ some_variable | to_nice_json }} {{ some_variable | to_nice_yaml }}也可以自定義縮進的大小
{{ some_variable | to_nice_json(indent=2) }} {{ some_variable | to_nice_yaml(indent=8) }}過濾器「from_json」「from_yaml」,從已經格式化好了的變量讀取數據:
{{ some_variable | from_json }} {{ some_variable | from_yaml }}「from_json」示例,從file.json文件讀取json數據:
tasks:- shell: cat /some/path/to/file.jsonregister: result- set_fact:myvar: "{{ result.stdout | from_json }}"過濾器「from_yaml_all」,用來解析YAML多文檔文件
tasks:- shell: cat /some/path/to/multidoc-file.yamlregister: result- debug:msg: '{{ item }}'loop: '{{ result.stdout | from_yaml_all | list }}'YAML多文檔文件指一個文件中包含多個yaml數據文檔,例如:
--- part_one: one ...--- part_two: two ...變量強制定義過濾器
當我們引用一個未被定義的變量時,ansible默認會報錯,當然我們可以通過更改ansible.cfg配置項的方式關閉這種機制(即設置[defaults]字段下的error_on_undefined_vars = False)。
在關閉這個機制的情況下,如果我們想讓ansible強制檢查某個變量是否定義,可以使用「mandatory」過濾器,寫法如下:
{{ variable | mandatory }}此時,如果變量「variable」被定義了,則引用,否則會報錯:
fatal: [node1]: FAILED! => {"msg": "Mandatory variable 'aaa' not defined."}變量默認值過濾器
「default」過濾器可以為未定義變量設置默認值,類似于roles/defaults/main.yaml里定義的變量,優先級最低(變量優先級參考ansible基礎-變量)。
示例如下:
{{ some_variable | default(5) }}另外,如果我們想將變量參數是false、False和空(None)視為未定義,則必須給defaults過濾器第二參數位置加上「true」:
{{ lookup('env', 'MY_USER') | default('admin', true) }}上面示例中表示:從環境變量中查找「MU_USER」變量,如果變量值為false、False、空(None)、未定義則將其設置為「admin」。否則引用之前被定義的參數。
可刪除參數過濾器
過濾器「omit」:在使用模塊的時候,有些參數的存在與否可以取決于變量是否被定義:
- name: touch files with an optional modefile: dest={{ item.path }} state=touch mode={{ item.mode | default(omit) }}loop:- path: /tmp/foo- path: /tmp/bar- path: /tmp/bazmode: "0444"上面示例表示:變量中如果定義了變量「mode」,file模塊則使用mode參數,否則就不使用。
執行結果為「/tmp/foo」和「/tmp/bar」文件使用默認權限,「/tmp/baz」文件使用「0444」權限。
列表過濾器
過濾器「min」,獲取最小值元素
{{ list1 | min }}過濾器「max」,獲取最大值元素
{{ [3, 4, 2] | max }}過濾器「flatten」,扁平化列表元素
{{ [3, [4, 2] ] | flatten }}轉換結果為:
[3, 4, 2 ]過濾器「flatten」,并且指定級別
{{ [3, [4, [2]] ] | flatten(levels=1) }}只轉換一級的列表元素,結果為:
[3, 4, [2] ]過濾器「unique」,給列表元素去重
{{ list1 | unique }}過濾器「union」,合并兩個列表后去重
{{ list1 | union(list2) }}過濾器「intersect」,取兩個列表相同的元素
{{ list1 | intersect(list2) }}過濾器「difference」,去掉list1中與list2相同的元素,返回list1中剩余的元素
{{ list1 | difference(list2) }}過濾器「symmetric_difference」,去掉list1與list2相同的元素,返回list1和list2剩余元素的集合
{{ list1 | symmetric_difference(list2) }}操作列表過濾器zip和zip_longest
過濾器「zip」,使兩個列表元素遞歸的融合,生成一個「itertools.izip」生成器對象。
通常后面加上「list」過濾器來使用,表示list1[0]元素與list2[0]元素組合,作為新列表的第一個元素;list1[1]元素與list2[1]元素組合,作為新列表的第二個元素 ,以此類推…… 新列表元素個數以list1和list2中元素個數較少者為準。
如果文字描述不懂,看下面示例就懂了:
- name: give me list combo of two listsdebug:msg: "{{ [1,2,3,4,5] | zip(['a','b','c','d','e','f']) | list }}"轉換結果為:
"msg": [[1,"a"],[2,"b"],[3,"c"],[4,"d"],[5,"e"]]過濾器「zip_longest」,與「zip」過濾器合并原理相似,「zip_longest」可以對更多的列表進行操作,且新列表元素個數以被操作列表中元素個數最多者為準,此時就需要指定「fillvalue」參數作為補位填充。示例如下:
{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], [100,200,300],fillvalue='X') | list }}轉換結果為:
?
"msg": [[1,"a",21,100],[2,"b",22,200],[3,"c",23,300],["X","d","X","X"],["X","e","X","X"],["X","f","X","X"]] 轉換結果操作列表過濾器subelements
過濾器「subelements」,操作對象為列表,摘取列表中的一個元素(通常為一個字典),將這個字典元素作為原始列表的新元素,其他元素保持不變。
{{ users | subelements('groups', skip_missing=True) }}上面語句會將:
users:- name: aliceauthorized:- /tmp/alice/onekey.pub- /tmp/alice/twokey.pubgroups:- wheel- docker- name: bobauthorized:- /tmp/bob/id_rsa.pubgroups:- docker轉換為:
-- name: alicegroups:- wheel- dockerauthorized:- /tmp/alice/onekey.pub- /tmp/alice/twokey.pub- wheel -- name: alicegroups:- wheel- dockerauthorized:- /tmp/alice/onekey.pub- /tmp/alice/twokey.pub- docker -- name: bobauthorized:- /tmp/bob/id_rsa.pubgroups:- docker- docker列表與字典互相轉換過濾器
過濾器「dict2items」,將字典變量轉換為列表變量
{{ dict | dict2items }}例如,可以將
tags:Application: paymentEnvironment: dev轉換為:
- key: Applicationvalue: payment - key: Environmentvalue: dev過濾器「items2dict」,將列表變量轉換為字典變量,默認情況下,列表元素必須有「key:」和「 value」。例如:
tags:- key: Applicationvalue: payment- key: Environmentvalue: dev轉換為:
Application: payment Environment: dev當然我們也可以認為指定「key:」「value」的替代參數:
{{ tags | items2dict(key_name='key_spec', value_name='value_spec') }}隨機Mac地址數過濾器
過濾器「random_mac」,在一個MAC地址前綴的基礎上,隨機生成mac地址。
"{{ '52:54:00' | random_mac }}" # => '52:54:00:ef:1c:03'注:如果給出的MAC地址前綴格式有問題,ansible會報錯。
隨機數過濾器
過濾器「random」,用于生成隨機數,操作對象可以是一個列表也可以是一個數字。
從列表里隨機獲取一個數值:
"{{ ['a','b','c'] | random }}" # => 'c'從數字0到60之間獲取一個隨機數:
"{{ 60 | random }} * * * * root /script/from/cron" # => '21 * * * * root /script/from/cron'從數字10到100之間獲取一個隨機數,間隔設置為10:
{{ 101 | random(1, 10) }} # => 31 {{ 101 | random(start=1, step=10) }} # => 51添加「seed」參數可以根據指定變量獲取一個隨機數,用于滿足冪等性需求:
"{{ 60 | random(seed=inventory_hostname) }} * * * * root /script/from/cron"打亂列表順序過濾器
過濾器「shuffle」,用于給一個列表重新排序,每次排序隨機:
{{ ['a','b','c'] | shuffle }} # => ['c','a','b'] {{ ['a','b','c'] | shuffle }} # => ['b','c','a']添加「seed」參數可以根據指定變量獲取一個隨機排序,用于滿足冪等性要求,此時,每次執行playbook獲取到的列表順序是固定的:
{{ ['a','b','c'] | shuffle(seed=inventory_hostname) }} # => ['b','a','c']Json數據查詢過濾器
過濾器「json_query」,用于從json數據變量中摘取出一部分數據:
例如,下面是一個完整的json格式的變量
domain_definition:domain:cluster:- name: "cluster1"- name: "cluster2"server:- name: "server11"cluster: "cluster1"port: "8080"- name: "server12"cluster: "cluster1"port: "8090"- name: "server21"cluster: "cluster2"port: "9080"- name: "server22"cluster: "cluster2"port: "9090"library:- name: "lib1"target: "cluster1"- name: "lib2"從這個變量中摘取出所有的「name」:
- name: "Display all cluster names"debug:var: itemloop: "{{ domain_definition | json_query('domain.cluster[*].name') }}"摘取cluster1的port:
- name: "Display all ports from cluster1"debug:var: itemloop: "{{ domain_definition | json_query(server_name_cluster1_query) }}"vars:server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port"摘取cluster2的name和port:
- name: "Display all server ports and names from cluster1"debug:var: itemloop: "{{ domain_definition | json_query(server_name_cluster1_query) }}"vars:server_name_cluster1_query: "domain.server[?cluster=='cluster2'].{name: name, port: port}"IP地址過濾器
過濾器「ipaddr」,用于測試是否為IP地址格式:
{{ myvar | ipaddr }}「ipaddr」過濾器也可以用于摘取出一個IP地址的指定信息:
例如,從一個CIDR摘取出IP地址信息:
{{ '192.0.2.1/24' | ipaddr('address') }}輸出結果為:
"msg": "192.0.2.1"過濾器「ipv4」「ipv6」,用于檢測ipv4和ipv6協議的IP地址:
{{ myvar | ipv4 }} {{ myvar | ipv6 }}哈希值過濾器
過濾器「hash」,用于獲取字符串的hash值。
獲取字符串的sha1哈希值:
{{ 'test1' | hash('sha1') }}獲取字符串的md5哈希值:
{{ 'test2' | hash('blowfish') }}過濾器「checksum」,用于獲取字符串的checksum:
{{ 'test2' | checksum }}過濾器「password_hash」,用于獲取一個密碼的哈希值。
獲取sha512密碼哈希值,結果隨機:
{{ 'passwordsaresecret' | password_hash('sha512') }}獲取sha256密碼哈希值,并加鹽處理,結果滿足冪等性:
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt') }}根據主機名獲取一個滿足冪等原則的sha256密碼,寫法如下:
{{ 'secretpassword' | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }}一些hash類型也允許提供「rounds」參數:
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=10000) }}注:關于哈希加鹽和rounds請自行Google。
操作字典元素過濾器
過濾器「combine」,用于合并字典數據。
默認情況下,不僅兩個字典會被合并,字典數據也會被后面字典數據覆蓋:
{{ {'a':1, 'b':2} | combine({'b':3, 'c':4}) }}結果為:
{'a':1, 'b':3, 'c':4}如果字典類型是多層全套字典,我們可以添加「resursive=True」參數進行內層字典融合:
{{ {'a':{'foo':1, 'bar':2}, 'b':2} | combine({'a':{'bar':3, 'baz':4}}, recursive=True) }}結果為:
{'a':{'foo':1, 'bar':3, 'baz':4}, 'b':2}多個字典遞歸融合:
{{ a | combine(b, c, d) }}上述示例中,字典「d」會字典「c」進行合并與元素的覆蓋,合并結果與字典「b」進行合并于覆蓋,合并結果再與字典「a」進行合并與覆蓋,返回最后的字典數據。
注:combine過濾器與ansible.cfg的「hash_behaviour」參數無關,即使「hash_behaviour」設置為了「replace」,在「combine」過濾器里依然會使用merge的方式進行融合。
參數提取過濾器
過濾器「map」,根據指定條件提取出列表或字典內的數據。
{{ [0,2] | map('extract', ['x','y','z']) | list }} {{ ['x','y'] | map('extract', {'x': 42, 'y': 31}) | list }}輸出結果為:
['x', 'z'] [42, 31]「map」還可以實現更加復雜的提取工作:
假設我想提取出主機組「nodes」下三個主機的「ansible_architecture」fact變量值,可以這樣寫:
{{ groups['nodes'] | map ('extract',hostvars,['ansible_architecture']) | list }}輸出結果為:
"msg": ["x86_64","x86_64","x86_64"]同理,如果我想查詢三個節點的ip地址放到一個列表內,可以這樣寫:
{{ groups['nodes'] | map ('extract',hostvars,['ansible_default_ipv4','address']) | list }}輸出結果為:
"msg": ["10.211.55.7","10.211.55.9","10.211.55.8"]上面兩個示例用一個比較簡易的表達式表示如下,如果不理解可以當公式記住:
{{ ['a'] | map('extract', b, ['x','y']) | list }}則會獲取到b['a']['x']['y']的參數結果。
注釋過濾器
過濾器「comment」,可以實現注釋字符串的功能,默認為「#」注釋。
{{ "Plain style (default)" | comment }}結果為:
# # Plain style (default) #我們也可以為?C (//...), C block (/*...*/), Erlang (%...) 和XML (<!--...-->)做注釋,分別為:
{{ "C style" | comment('c') }} {{ "C block style" | comment('cblock') }} {{ "Erlang style" | comment('erlang') }} {{ "XML style" | comment('xml') }}使用「decoration」參數可以人為指定注釋符號:
{{ "My Special Case" | comment(decoration="! ") }}輸出結果為:
! ! My Special Case !為了美觀,我們也可以定制格式:
{{ "Custom style" | comment('plain', prefix='#######\n#', postfix='#\n#######\n ###\n #') }}輸出結果為:
####### # # Custom style # ###########「comment」也可以傳遞變量,例如「ansible_managed」變量為:
[defaults] ansible_managed = This file is managed by Ansible.%ntemplate: {file}date: %Y-%m-%d %H:%M:%Suser: {uid}host: {host}通過表達式:
{{ ansible_managed | comment }}轉化為:
# # This file is managed by Ansible. # # template: /home/ansible/env/dev/ansible_managed/roles/role1/templates/test.j2 # date: 2015-09-10 11:02:58 # user: ansible # host: myhost #解析url過濾器
過濾器「urlsplit」,用于分解一個url鏈接,取出我們需要的字段,直接上官網示例:
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('hostname') }} # => 'www.acme.com'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('netloc') }} # => 'user:password@www.acme.com:9000'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('username') }} # => 'user'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('password') }} # => 'password'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('path') }} # => '/dir/index.html'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('port') }} # => '9000'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('scheme') }} # => 'http'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('query') }} # => 'query=term'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('fragment') }} # => 'fragment'{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit }} # => # { # "fragment": "fragment", # "hostname": "www.acme.com", # "netloc": "user:password@www.acme.com:9000", # "password": "password", # "path": "/dir/index.html", # "port": 9000, # "query": "query=term", # "scheme": "http", # "username": "user" # }正則過濾器
過濾器「regex_search」,用于對一個字符串的正則匹配查找
# search for "foo" in "foobar" {{ 'foobar' | regex_search('(foo)') }}# will return empty if it cannot find a match {{ 'ansible' | regex_search('(foobar)') }}# case insensitive search in multiline mode {{ 'foo\nBAR' | regex_search("^bar", multiline=True, ignorecase=True) }}過濾器「regex_findall」,用于對所有事件進行查找:
# Return a list of all IPv4 addresses in the string {{ 'Some DNS servers are 8.8.8.8 and 8.8.4.4' | regex_findall('\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b') }}過濾器「regex_replace」,用于對一個字符串進行文本替換:
# convert "ansible" to "able" {{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }}# convert "foobar" to "bar" {{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}# convert "localhost:80" to "localhost, 80" using named groups {{ 'localhost:80' | regex_replace('^(?P<host>.+):(?P<port>\\d+)$', '\\g<host>, \\g<port>') }}# convert "localhost:80" to "localhost" {{ 'localhost:80' | regex_replace(':80') }}# add "https://" prefix to each item in a list {{ hosts | map('regex_replace', '^(.*)$', 'https://\\1') | list }}過濾器「regex_escape」,用于轉義特殊字符串:
# convert '^f.*o(.*)$' to '\^f\.\*o\(\.\*\)\$' {{ '^f.*o(.*)$' | regex_escape() }}其他過濾器
過濾器「quote」,給字符串添加引號,在shell模塊內使用
- shell: echo {{ string_value | quote }}過濾器「ternary」,根據前面語句的真與假選擇一個字符串
{{ (name == "John") | ternary('Mr','Ms') }}上面示例表示:如果變量「name」的值為「John」則表達式返回字符串「Mr」,否則返回字符串「Ms」。
過濾器「join」,將列表轉換成字符串,可以指定連接符
{{ list | join(" ") }}過濾器「basename」,獲取一個文件的絕對路徑,例如將「foo.txt」轉換為「/etc/asdf/foo.txt」
1{{ path | basename }}過濾器「dirname」,獲取一個文件或目錄的上級目錄
{{ path | dirname }}例如:
「/etc/httpd/conf」將獲取到「/etc/httpd」
「/etc/httpd/conf/」將獲取到「/etc/httpd/conf」
「/etc/httpd/conf/httpd.conf」將獲取到「/etc/httpd/conf」
過濾器「realpath」,獲取一個鏈接文件的真實文件路徑,默認是絕對路徑
{{ path | realpath }}獲取「/etc」的相對路徑
{{ path | relpath('/etc') }}過濾器「splittext」,拆分字符串,將文件的位置提取出來作為一個單獨的元素
# with path == 'nginx.conf' the return would be ('nginx', '.conf') {{ path | splitext }}過濾器「b64decode」「b64encode」,Base64編碼與解碼
{{ encoded | b64decode }} {{ decoded | b64encode }}過濾器「to_uuid」,根據一個字符串生成一個UUID
{{ hostname | to_uuid }}過濾器「map」的另一個用法
# get a comma-separated list of the mount points (e.g. "/,/mnt/stuff") on a host {{ ansible_mounts | map(attribute='mount') | join(',') }}過濾器「to_datetime」,獲取到的是日期對象
# Get total amount of seconds between two dates. Default date format is %Y-%m-%d %H:%M:%S but you can pass your own format {{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).total_seconds() }}# Get remaining seconds after delta has been calculated. NOTE: This does NOT convert years, days, hours, etc to seconds. For that, use total_seconds() {{ (("2016-08-14 20:00:12" | to_datetime) - ("2016-08-14 18:00:00" | to_datetime)).seconds }} # This expression evaluates to "12" and not "132". Delta is 2 hours, 12 seconds# get amount of days between two dates. This returns only number of days and discards remaining hours, minutes, and seconds {{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).days }}格式化時間數據
# Display year-month-day {{ '%Y-%m-%d' | strftime }}# Display hour:min:sec {{ '%H:%M:%S' | strftime }}# Use ansible_date_time.epoch fact {{ '%Y-%m-%d %H:%M:%S' | strftime(ansible_date_time.epoch) }}# Use arbitrary epoch value {{ '%Y-%m-%d' | strftime(0) }} # => 1970-01-01 {{ '%Y-%m-%d' | strftime(1441357287) }} # => 2015-09-04過濾器「type_debug」,用于debug出數據類型
{{ myvar | type_debug }}本節應該掌握的技能
- 掌握Jinja2模版的使用分類和優點
- 熟悉本文提到的過濾器,最少記住大概功能
- 掌握在playbook和模版文件中使用過濾器的方法?
參考鏈接
- https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html
- https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html
?
歡迎大家關注我的公眾號:
轉載于:https://www.cnblogs.com/mauricewei/p/10056379.html
總結
以上是生活随笔為你收集整理的ansible基础-Jinja2模版 | 过滤器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux作业(第四章练习题)
- 下一篇: LAMP和LNMP去除index.php