3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ansible Tower(AWX)にREST API経由で変数を渡すと使用できない仕様について調べてみた

Last updated at Posted at 2018-07-28

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. テンプレートに変数埋め込み

スクリーンショット 2018-07-28 21.38.13.png

1-3-2. 実行

これを実行すると問題なく {{ message }} 変数が展開され test message が表示されています。

スクリーンショット 2018-07-28 21.39.30.png

1-3-3. REST APIから渡す

埋め込んだ変数を削除してREST APIからextra_varsを渡します。

スクリーンショット 2018-07-28 21.46.11.png

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 になっていることが確認できます。

スクリーンショット 2018-07-28 21.46.57.png

2. 原因

REST API経由からextra_varsを渡すと UnsafeProxy で文字列に変換され戻りで AnsibleUnsafeText になるようです。

/usr/lib/python2.7/site-packages/ansible/utils/unsafe_proxy.py
(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 の部分を変更してみます。

/usr/lib/python2.7/site-packages/ansible/utils/unsafe_proxy.py
(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)

実行すると変数が展開されていることが確認できます。

スクリーンショット 2018-07-28 21.55.05.png

しかし、これは本体のソースをいじって無理やりやっている形なので影響度が不明です。

以下は return obj(左)return AnsibleUnsafeText(obj)(右) の時の UnsafeProxy で実行された結果の差分です。
UnsafeProxyで処理されたobjの名前と型を出力して比較しています。

スクリーンショット 2018-07-28 22.09.59.png

左の場合は 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()

スクリーンショット 2018-07-28 22.20.57.png

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()

スクリーンショット 2018-07-28 22.21.49.png

両方とも message 変数が展開されていることが確認できます。

4. 最後に

Ansible Tower(AWX)のREST APIから変数を渡すというやり方は簡単にできないような感じです。
ちょっと一癖ありますね。。。
辞書やリストを使う前提の変数であれば、文字列で渡せばなんとかなりそうです。
フィルター噛ませたりしてやりくりするとできたりするのだろうか。

5. 参考

to_textのソース
string_typesのソース

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?