在使用 Ansible v2 使用非 Python 自定义模块时需要注意的事项
这是关于Alpha版本的过时信息。在Ansible 2.0.0正式版本中,已经对旧式模块进行了兼容处理。
但是,对于今后创建的自定义模块,请继续推荐使用new、non_native_want_json样式,所以该文章将保留。
因为 Ansible 2.0.0 的 alpha 版已经发布,所以我想开始认真地使用它,但是当我尝试运行我用 bash 脚本编写的自定义模块时发现它无法工作了,所以记下了解决方法的备忘录。
自行制作的模块:鹦鹉
在这里,我们将以一个叫做”parrot”的自定义模块作为例子,展开下面的对给定参数进行模仿回复的讨论。(顺便说一句,如果参数中包含空格,则不能正确运行)
#!/bin/bash
# 引数ファイルが与えられなかったらエラー
if [ -z $1 ]; then
echo "{\"failed\": true, \"msg\": \"no arguments provided.\"}"
exit 1
fi
result="{\"changed\": false"
# 引数をkey, valueに分解してresultに突っ込む
for kv in $(cat $1); do
echo $kv
escaped=$(echo $kv | sed -e 's/"/\\\"/g')
key=${escaped%%=*}
value=${escaped#*=}
if [ $key ]; then
result+=", \"$key\": \"$value\""
fi
done
result+="}"
echo $result
exit 0
现象
在1.9.2版本中能运行。
当在Ansible 1.9.2中运行这个parrot模块时,输入的参数将作为执行结果直接输出。
$ ansible localhost -m parrot -a 'foo=bar hoge=fuga'
localhost | success >> {
"changed": false,
"foo": "bar",
"hoge": "fuga"
}
在2.0.0版本中无法运行。
然而,即使在Ansible 2.0.0版本中完全相同的情况下进行执也会被指责为没有提供参数!
ansible localhost -m parrot -a 'hoge=fuga foo=bar'
localhost | FAILED! => {
"changed": false,
"failed": true,
"msg": "no arguments provided."
}
错误的原因 de
当我加上-vvv并执行该命令进行确认之后,我开始想知道到底发生了什么。
# 1.9.2の場合
<localhost> EXEC ['/bin/sh', '-c', u'LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /bin/bash /tmp/ansible-tmp-1441005841.0-58446508593101/parrot /tmp/ansible-tmp-1441005841.0-58446508593101/arguments; rm -rf /tmp/ansible-tmp-1441005841.0-58446508593101/ >/dev/null 2>&1']
# 2.0.0の場合
localhost EXEC LANG=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /bin/bash /tmp/ansible-tmp-1441005817.19-202560708428255/parrot; rm -rf "/tmp/ansible-tmp-1441005817.19-202560708428255/" >/dev/null 2>&1
如果仔细观察上述执行结果,可以看到在1.9.2版本中,将arguments文件作为第一个参数传递给parrot,而在2.0.0版本中,parrot没有传递任何参数来执行。由于parrot在内部通过传递的路径参数读取文件并获取ansible参数,所以如果arguments文件不存在,自然会引发错误。
在v2版本中不能使用通过文件传递参数!
为什么在v2中没有传递arguments文件?
实际上,Ansible v1模块存在着三种不同的样式,每个样式都以不同的形式传递参数信息。
Ansible模块的三种风格
-
- old
1.4未満で使われていた形式
モジュールへの引数はスペース区切りのargumentsファイルで渡される
e.g. hoge=fuga foo=bar
スペース入りの値などの扱いが困難
parrotモジュールはこの形式
non_native_want_json
oldと同様に引数はargumentsファイル経由で渡される
oldと違って、argumentsファイルの中身はJSON
e.g. {“foo”: “bar”, “hoge”: “fuga”}
モジュール内にWANT_JSONと書かれている場合にこのスタイルになる。
new
1.4以降の組み込みモジュールで使われている形式
変数系の情報は下で説明する仕様でモジュールファイル内に展開される
引数用の外部ファイルは使われない。
モジュール内に#<>と書かれている場合にこのスタイルになる。
根据查看的资料来看,v2版本似乎无法通过arguments文件传递参数了。关于具体的向后兼容政策还未确认,但是通过文件传递参数一旦被删除,很可能在未来的v2版本中不会重新实现。
在v1版本中,模块内的字符串展开规范。
“<>”: モジュールを実行するAnsibleのバージョン文字列
“<>”: モジュールに与えられた名前なし引数文字列
“<>”: モジュールに与えられた名前付き引数のpythonコード用JSON文字列(reprを使っている)
<>: SELinux情報
在v2版本中,模块内的字符串展开规范发生了变化。
另外,在v2版本中,展开规范也有一些变更。
-
- 追加された展開パターン
<>
PowerShell用の名前付き引数のJSON文字列
“<>”との違いは、引用符が含まれないだけ
“<>”はpythonコード用だが、こちらは単純にダンプされたJSON
削除された展開パターン
“<>”
そもそもv2ではcommand系などの一部の組み込みモジュール以外では、名前なし引数の利用が許可されていません
<>
将自制的模块适配到新的方式中
为了使parrot在v2版本中可用,唯一的选择似乎是在一个文件中接收参数,并修改模块以实现这一功能。
如果在模块内使用字符串插值,只要是在1.4版本或之后的版本中,无论是v1还是v2都可以使用,所以对于v1/v2之间的兼容性,似乎不需要太过担心。
我说的是谎言。
由于”<>”在python中直接使用repr,因此对于类似于{“foo”: “bar\”baz”}这样的JSON,会被转义为'{“foo”: “bar\\”baz”}’。
因此,似乎需要使用<>来支持v2,而对于v1,则需要使用non_native_want_json,这样做似乎有点不舒服,因为这对于非Windows的模块来说是多余的。
在2015/09/11时的最新devel分支中,也可以使用<>来代替。
被修改过的鹦鹉 le
所以,修正后的鹦鹉看起来是这样的。
#!/bin/bash
# WANT_JSON
ANSIBLE_VERSION="<<ANSIBLE_VERSION>>"
# jqがなかったらエラー
if ! type jq >/dev/null 2>&1; then
echo "{\"failed\": true, \"msg\": \"jq not found.\"}"
exit 1
fi
# v1の場合は$1に引数ファイルのパスが入る
if [[ $ANSIBLE_VERSION == 1.* ]]; then
MODULE_COMPLEX_ARGS=$(cat $1)
# v2の場合はINCLUDE_ANSIBLE_MODULE_JSON_ARGSを使う
else
MODULE_COMPLEX_ARGS=$(cat << 'EOF'
<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>
EOF
)
fi
echo $MODULE_COMPLEX_ARGS | jq '.changed=false'
exit 0
我很喜欢使用jq来处理JSON操作。对我来说,除了在CoreOS操作时可能会想要使用bash写Ansible模块之外,幸运的是CoreOS已经安装了jq,这使我感到很便利。
作为要点
WANT_JSONコメントでnon_native_want_jsonスタイルを適用
ANSIBLE_VERSIONの値によって、引数の取得方法を場合分け
大概是在这个时候说的吧。 shì shuō de ba.)
总结
这次我们以bash脚本为例尝试了自制模块的v2兼容方法,但这种方法可以直接应用于除了具备内置辅助程序的Python和PowerShell之外的语言/环境中创建模块的情况。
即使在迁移到v2的时机尚早的情况下,我认为使用”WANT_JSON”注释以获取参数作为JSON的方法,在能够使用JSON解析器的语言/环境中,都是应该引入的。
请参考
以下是关于不同模块样式行为的内容概要:https://gist.github.com/mildred/9ce28b926b567548037d
补充(2015/09/11)
之前由于<>不起作用,所以我添加了一个名为<>的替代器,然后它被接受了。
https://github.com/ansible/ansible/pull/12267