この投稿はAnsible 2 Advent Calendar 2019(通称裏アドベントカレンダー?)の13日めの記事です。
1週間全部俺の6回目です!
今回はシェルスクリプトでAnsibleモジュールを作ってみようと思います。
シェルスクリプト
どうやるの?
Ansibleは標準だとPythonでモジュールが作成されていますが、他の言語で作成することができます。
例えばAnsible Advent Calendar 2019の2日目の記事はGoでモジュールを作る例でした。
こちらの記事で述べられている通り処理結果はJSONで返すことと、第一引数でパラメーターが記載されているファイルパスが渡されるので、それを読み込んでパースが出来れば何の言語でも大丈夫です。
パラメーターファイルの中身
1 2 |
name=test state=present _ansible_check_mode=False _ansible_no_log=False _ansible_debug=False _ansible_diff=False _ansible_verbosity=0 _ansible_version=2.9.2 _ansible_module_name=dir _ansible_syslog_facility=LOG_USER _ansible_selinux_special_fs='['"'"'fuse'"'"', '"'"'nfs'"'"', '"'"'vboxsf'"'"', '"'"'ramfs'"'"', '"'"'9p'"'"', '"'"'vfat'"'"']' _ansible_string_conversion_action=warn _ansible_socket=None _ansible_shell_executable=/bin/sh _ansible_keep_remote_files=False _ansible_tmpdir=/root/.ansible/tmp/ansible-tmp-1576075509.363012-157708934303795/ _ansible_remote_tmp='~/.ansible/tmp' |
今回はディレクトリを操作するモジュールをシェルスクリプトで作ってみました。
ディレクトリを操作するモジュール
パラメーターは以下のものが使用できます。
パラメーター | 説明 |
---|---|
name | ディレクトリ名 |
path | ディレクトリを作成するパス |
state | present or absent |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
#!/bin/bash # # This code is ansible module example using bash. function exit_json() { if [ -n "$2" ] ; then json_structure=" { \"changed\": $1, $2 } " else json_structure=" { \"changed\": $1 } " fi echo $json_structure exit 0 } function fail_json() { json_structure=" { \"failed\": "true", \"msg\": \"$1\" } " echo $json_structure exit 1 } function main() { # arg parse arg_flag=0 for arg in $(cat $1) ; do echo $arg | grep -E ',|\[|\]' > /dev/null 2>&1 if [ $? -eq 0 ] ; then arg2="$arg2$arg" arg_flag=1 continue fi if [ $arg_flag -eq 0 ] ; then key=`echo $arg | cut -d '=' -f 1` value=`echo $arg | cut -d '=' -f 2` declare "module_arg_$key=$value" arg_flag=0 else key=`echo $arg2 | cut -d '=' -f 1` value=`echo $arg2 | cut -d '=' -f 2` declare "module_arg_$key=$value" arg_flag=0 fi done # parameters name=$module_arg_name path=$module_arg_path state=$module_arg_state # When state is present if [ $state == "present" -a -n "$name" -a -n "$path" ] ; then if [ -d $path ] ; then if [ -d "$path/$name" ] ; then exit_json "false" else result=`mkdir "$path/$name" 2>&1` if [ -n "$result" ] ; then fail_json "`echo -n $result`" else exit_json "true" "\"directory_path\": \"$path/$name\"" fi fi else fail_json "Error: not found $module_arg_path" fi fi # When state is absent if [ $state == "absent" -a -n "$name" -a -n "$path" ] ; then if [ -d "$path/$name" ] ; then rm -rf "$path/$name" 2>&1 > /dev/null if [ -d "$path/$name" ] ; then fail_json "Error: failed delete folder $path/$name" fi exit_json "true" "\"directory_path\": \"$path/$name\"" else exit_json "false" fi fi fail_json "Required parameters include: name, path, state" } main $1 |
上記スクリプトを作成します。
1 2 3 4 |
(venv) [root@localhost shell]# mkdir library/ (venv) [root@localhost shell]# vi library/dir.sh (スクリプトを貼り付ける) |
Playbook
作成
Playbookを作成します。
1 2 |
(venv) [root@localhost shell]# vi main.yml |
1 2 3 4 5 6 7 8 9 10 |
--- - name: Directory operation playbook hosts: localhost gather_facts: no tasks: - dir: name: hoge path: /opt state: present |
実行
Playbookを実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
(venv) [root@localhost shell]# ansible-playbook main.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [Directory operation playbook] ******************************************************************************************************************************************* TASK [dir] ******************************************************************************************************************************************************************** changed: [localhost] PLAY RECAP ******************************************************************************************************************************************************************** localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 (venv) [root@localhost shell]# ls /opt/ hoge |
ディレクトリが作成されていることが確認できます。
もう一度実行します。
1 2 3 4 5 6 7 8 9 10 11 12 |
(venv) [root@localhost shell]# ansible-playbook main.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [Directory operation playbook] ******************************************************************************************************************************************* TASK [dir] ******************************************************************************************************************************************************************** ok: [localhost] PLAY RECAP ******************************************************************************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
冪統制で既にディレクトリが存在しているため changed
は発生しません。
ちなみに、registerを使った場合は以下のように directory_path
に作成されたディレクトリのパスが表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
(venv) [root@localhost shell]# ansible-playbook main.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [Directory operation playbook] ******************************************************************************************************************************************* TASK [dir] ******************************************************************************************************************************************************************** changed: [localhost] TASK [debug] ****************************************************************************************************************************************************************** ok: [localhost] => { "result": { "changed": true, "directory_path": "/opt/hoge", "failed": false } } PLAY RECAP ******************************************************************************************************************************************************************** localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
次に state
を absent
に変更して削除してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
(venv) [root@localhost shell]# ansible-playbook main.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [Directory operation playbook] ******************************************************************************************************************************************* TASK [dir] ******************************************************************************************************************************************************************** changed: [localhost] PLAY RECAP ******************************************************************************************************************************************************************** localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 (venv) [root@localhost shell]# ls /opt/ (venv) [root@localhost shell]# |
ディレクトリが削除されていることが確認できます。
もう一度実行します。
1 2 3 4 5 6 7 8 9 10 11 12 |
(venv) [root@localhost shell]# ansible-playbook main.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [Directory operation playbook] ******************************************************************************************************************************************* TASK [dir] ******************************************************************************************************************************************************************** ok: [localhost] PLAY RECAP ******************************************************************************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
こちらも冪統制で既にディレクトリが削除されているので changed
が発生しません。
オプションが足りない場合は以下のようにエラーが発生します。
1 2 3 4 5 6 7 8 9 10 11 12 |
(venv) [root@localhost shell]# ansible-playbook main.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [Directory operation playbook] ******************************************************************************************************************************************* TASK [dir] ******************************************************************************************************************************************************************** fatal: [localhost]: FAILED! => {"changed": false, "msg": "Required parameters include: name, path, state"} PLAY RECAP ******************************************************************************************************************************************************************** localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 |
最後に
今回シェルスクリプトでAnsibleモジュールを作ってみましたが正直ツライw
ただ、冪統制の処理を書くことと戻りをJSONで返すだけで色々な言語で作れちゃうので便利ですね 🙂