Ansible Tower(AWX)にREST APIで変数を渡すと変数が展開されないので仕様について調べて見ました。
1. 変数展開されない事象について
まずは、変数が展開されない事象について確認しています。
1-1. 動作確認方針
extra_varsに変数を格納してテンプレート埋め込みとREST API経由から実行して動作を確認してみます。
1-2. テスト用Playbook
使用するPlaybookは以下のものです。
extra_varsから msg
に {{ message }}
を格納して実行し test message
が表示されれば問題ありません。
---
- name: var output test
hosts: all
gather_facts: no
vars:
message: test message
tasks:
- debug: msg="{{ msg }}"
- debug: msg="{{ msg | type_debug }}"
1-3. テンプレート設定
1-3-1. テンプレートに変数埋め込み
1-3-2. 実行
これを実行すると問題なく {{ message }}
変数が展開され test message
が表示されています。
1-3-3. REST APIから渡す
埋め込んだ変数を削除してREST APIからextra_varsを渡します。
1-3-4. 実行
以下のソースを使って実行します。
# !/usr/bin/env python3
from collections import defaultdict
import json
import requests
def main():
url = "https://192.168.0.236/api/v1/job_templates/7/launch/"
headers = {"Content-Type":"application/json"}
nested_dict = lambda: defaultdict(nested_dict)
data = nested_dict()
data['extra_vars']['msg'] = "{{ message }}"
r = requests.post(url,
headers=headers,
auth=("admin","redhat"),
data=json.dumps(data),
verify=False)
print(r.status_code)
print(json.dumps(json.loads(r.text), indent=2))
if __name__ == "__main__":
main()
REST API経由から実行すると変数は展開されず、そのまま文字列として出力されます。
変数の型も AnsibleunsafeText
になっていることが確認できます。
2. 原因
REST API経由からextra_varsを渡すと UnsafeProxy
で文字列に変換され戻りで AnsibleUnsafeText
になるようです。
(snip)
class UnsafeProxy(object):
def __new__(cls, obj, *args, **kwargs):
# In our usage we should only receive unicode strings.
# This conditional and conversion exists to sanity check the values
# we're given but we may want to take it out for testing and sanitize
# our input instead.
if isinstance(obj, string_types):
obj = to_text(obj, errors='surrogate_or_strict')
return AnsibleUnsafeText(obj)
return obj
(snip)
https://github.com/ansible/ansible/blob/cbb6a7f4e831abd2d56375fb0aa22465e0cc7c0c/lib/ansible/utils/unsafe_proxy.py#L73
https://github.com/ansible/ansible/blob/cbb6a7f4e831abd2d56375fb0aa22465e0cc7c0c/lib/ansible/utils/unsafe_proxy.py#L80
3. 解決方法
3-1. Ansible本体のソースを改修
一先ず動作確認として UnsafeProxy
の部分を変更してみます。
(snip)
class UnsafeProxy(object):
def __new__(cls, obj, *args, **kwargs):
# In our usage we should only receive unicode strings.
# This conditional and conversion exists to sanity check the values
# we're given but we may want to take it out for testing and sanitize
# our input instead.
if isinstance(obj, string_types):
obj = to_text(obj, errors='surrogate_or_strict')
return obj
#return AnsibleUnsafeText(obj)
return obj
(snip)
実行すると変数が展開されていることが確認できます。
しかし、これは本体のソースをいじって無理やりやっている形なので影響度が不明です。
以下は return obj(左)
と return AnsibleUnsafeText(obj)(右)
の時の UnsafeProxy
で実行された結果の差分です。
UnsafeProxyで処理されたobjの名前と型を出力して比較しています。
左の場合は ansible.parsing.yaml.objects.AnsibleUnicode
のclassのためPlaybookを実行すると変数が展開されます。
3-2. dict/listを文字列として渡す
dictを文字列として渡してみます。
# !/usr/bin/env python3
from collections import defaultdict
import json
import requests
def main():
url = "https://192.168.0.236/api/v1/job_templates/7/launch/"
headers = {"Content-Type":"application/json"}
nested_dict = lambda: defaultdict(nested_dict)
data = nested_dict()
data['extra_vars']['msg'] = "{'msg': message }"
r = requests.post(url,
headers=headers,
auth=("admin","redhat"),
data=json.dumps(data),
verify=False)
print(r.status_code)
print(json.dumps(json.loads(r.text), indent=2))
if __name__ == "__main__":
main()
listを文字列として渡してみます。
# !/usr/bin/env python3
from collections import defaultdict
import json
import requests
def main():
url = "https://192.168.0.236/api/v1/job_templates/7/launch/"
headers = {"Content-Type":"application/json"}
nested_dict = lambda: defaultdict(nested_dict)
data = nested_dict()
data['extra_vars']['msg'] = "[ message ]"
r = requests.post(url,
headers=headers,
auth=("admin","redhat"),
data=json.dumps(data),
verify=False)
print(r.status_code)
print(json.dumps(json.loads(r.text), indent=2))
if __name__ == "__main__":
main()
両方とも message
変数が展開されていることが確認できます。
4. 最後に
Ansible Tower(AWX)のREST APIから変数を渡すというやり方は簡単にできないような感じです。
ちょっと一癖ありますね。。。
辞書やリストを使う前提の変数であれば、文字列で渡せばなんとかなりそうです。
フィルター噛ませたりしてやりくりするとできたりするのだろうか。