備忘
課題
以下の様なリストにおいて、各dictのキー部分を文字列操作したい。
例えば以下のキーを小文字化する例を考える。
- KEY1: Val1
KEY2: Val2
KEY3: Val3
- KEY1: Val4
KEY2: Val5
KEY3: val6
↓
- key1: Val1
key2: Val2
key3: Val3
- key1: Val4
key2: Val5
key3: Val6
ソリューション例
キーと値を一旦分離し、keyに対してフィルターなりで変更を加え、zipフィルターで再び合わせる。
サンプルplaybook
---
- hosts: localhost
gather_facts: false
vars:
foo:
- KEY1: Val1
KEY2: Val2
KEY3: Val3
- KEY1: Val4
KEY2: Val5
KEY3: Val6
tasks:
- loop: "{{ foo }}"
debug:
msg: "{{ dict(item.keys()|map('lower')|zip(item.values())) }}"
register: r
- debug:
var: r.results|map(attribute="msg")|list
- loopにて、listの各要素に対する処理に分解
- item.keys()
- dictのkey部分のlistを作成
- 例えば["KEY1", "KEY2", "KEY3"]
- item|dict2items|map(attribute='key')|listと同義
- item.values()
- dictのvalue部分のlistを作成
- 例えば["Val1", "Val2", "Val3"]
- item|dict2items|map(attribute='value')|listと同義
- |map('lower')
- listの各要素にlowerフィルターを適用
- 例えば["key1", "key2", "key3"]を作成
- 内部で指定するフィルターに引数が必要な場合、map('フィルター名', '第一引数', '第二引数',,)などとして渡す事が可能
- 要素が文字列ならば、regex_replace フィルターを使用することで、map('regex_replace', '^(.*)$', 'https://\1')などとして、各文字列に対する正規表現での編集も可能
- dict(xxxx|zip(yyyy))により、list xxxxとlist yyyyからdictを再作成
- |map(attribute="msg")|list
- debugのloop結果から出力内容"msg"の内容でlistを再作成
実行結果
$ ansible-playbook -i localhost, site.yml
PLAY [localhost] *************************************************************************************************
TASK [debug] *****************************************************************************************************
ok: [localhost] => (item={'KEY1': 'Val1', 'KEY2': 'Val2', 'KEY3': 'Val3'}) => {
"msg": {
"key1": "Val1",
"key2": "Val2",
"key3": "Val3"
}
}
ok: [localhost] => (item={'KEY1': 'Val4', 'KEY2': 'Val5', 'KEY3': 'val6'}) => {
"msg": {
"key1": "Val4",
"key2": "Val5",
"key3": "Val6"
}
}
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"r.results|map(attribute=\"msg\")|list": [
{
"key1": "Val1",
"key2": "Val2",
"key3": "Val3"
},
{
"key1": "Val4",
"key2": "Val5",
"key3": "Val6"
}
]
}
PLAY RECAP *******************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
参考
Ansible Tips: dictionary や list_of_dictionaries 構造に対する値の参照とループ操作
https://qiita.com/hiroyuki_onodera/items/b42f728c2270a7bccd97
雑感
使える武器がフィルターだったりpythonの関数だったり。。
別解1 loop時に作った解をまとめる
- set_fact:
list0: []
- loop: "{{ foo }}"
set_fact:
list0: "{{ list0 + [ dict(item.keys()|map('lower')|zip(item.values())) ] }}"
- debug:
var: list0
TASK [set_fact] *****************************************************************************************************
ok: [localhost]
TASK [set_fact] *****************************************************************************************************
ok: [localhost] => (item={'KEY1': 'Val1', 'KEY2': 'Val2', 'KEY3': 'Val3'})
ok: [localhost] => (item={'KEY1': 'Val4', 'KEY2': 'Val5', 'KEY3': 'Val6'})
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"list0": [
{
"key1": "Val1",
"key2": "Val2",
"key3": "Val3"
},
{
"key1": "Val4",
"key2": "Val5",
"key3": "Val6"
}
]
}
別解2 jinja2のforを使用
可読性は最も高そう
- loop: "{{ foo }}"
debug:
msg: "{{ bar|from_yaml }}"
vars:
bar: >-
{
{% for k,v in item.items() %}
'{{k|lower}}': '{{v}}',
{% endfor %}
}
register: r
- debug:
var: r.results|map(attribute="msg")|list
- task varsにて{% %}によるforループを回して文字列のYAMLを作成、モジュール属性側でfrom_yamlフィルターを適用してYAMLとして認識
- from_yamlフィルターの為の{{ }}内にforの為の{% %}を含めることができなかった為
- forループは以下でも可
- bar: "{ {% for i in foo|dict2items %} '{{i.key|lower}}': '{{i.value}}', {% endfor %} }"
TASK [debug] ********************************************************************************************************
ok: [localhost] => (item={'KEY1': 'Val1', 'KEY2': 'Val2', 'KEY3': 'Val3'}) => {
"msg": {
"key1": "Val1",
"key2": "Val2",
"key3": "Val3"
}
}
ok: [localhost] => (item={'KEY1': 'Val4', 'KEY2': 'Val5', 'KEY3': 'Val6'}) => {
"msg": {
"key1": "Val4",
"key2": "Val5",
"key3": "Val6"
}
}
TASK [debug] ********************************************************************************************************
ok: [localhost] => {
"r.results|map(attribute=\"msg\")|list": [
{
"key1": "Val1",
"key2": "Val2",
"key3": "Val3"
},
{
"key1": "Val4",
"key2": "Val5",
"key3": "Val6"
}
]
}