LoginSignup
7

More than 3 years have passed since last update.

Organization

[Ansible小ネタ] にゃんしぶるを支える技術 [cowsay/callback plugin]

Ansible界隈の人たちとの会話で何気なく「にゃんしぶる」という単語を使ったら、Ansible実行時にネコチャンが出てくるモジュールとか作りそうな流れになってきたので、ひとまずメッセージ出力でにゃんしぶるできないかどうか試してみましたにゃーん。。。

2019-11-04_14h05_26.png

環境

[zaki@manager nyan]$ ansible --version
ansible 2.8.5
  config file = /home/zaki/work/ansible/nyan/ansible.cfg
  configured module search path = [u'/home/zaki/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
[zaki@manager nyan]$
[zaki@manager nyan]$ cat /etc/redhat-release
CentOS Linux release 7.7.1908 (Core)
[zaki@manager nyan]$

なお、本記事の内容は、cowsayの使い方・アレンジ方法と、Ansible Callback Pluginの簡単な作り方という構成になってます。

解説はいいからちょっと試したい

詳細は後述してるけど、/usr/local/bin/cowsayに以下のスクリプトをコピペ。(スクリプトの引数を元に整形してascii artをprintしてるだけです)

/usr/local/bin/cowsay
#!/usr/bin/perl

my $border = "-" x ((length $ARGV[-1]) + 2);
print <<"__EOL__";
 $border
< $ARGV[-1] >
 $border
 \\ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ
__EOL__

そしてchmod 755 /usr/local/bin/cowsayで実行権限を付与して、Ansibleを実行すればOK

元に戻したくなったら、/usr/local/bin/cowsayを削除すればOKです。

cowsay でにゃんしぶる

Ansible実行時に、システムにcowsayコマンドがインストールされている場合、このコマンドを使ってtask名を出力する機能が備わっている。

Ansibleからcowsayを呼び出している部分はこの箇所

自前の cowsay を用意してにゃんしぶる

Ansible実行時にcowsayがインストールされているかどうかは、以下のリストに実行ファイルがあるかどうかを見て判断している。

b_COW_PATHS = (
    b"/usr/bin/cowsay",
    b"/usr/games/cowsay",
    b"/usr/local/bin/cowsay",  # BSD path for cowsay
    b"/opt/local/bin/cowsay",  # MacPorts path for cowsay
)

これらのパスのどこかに、実行可能なcowsayスクリプト等を配置すればOK。
逆に言えば、Ansible実行時にパスが通っている場所(~/local/bin/cowsayとか)にcowsayを置いていても、これらのパス以外であれば反応しない。

なので、yumなどでcowsayをインストールしなくても、/usr/local/bin/cowsayを自分で用意してしまえば、Ansibleでにゃんしぶるが可能。
(※ chmod 755で実行権限を与えるのをお忘れなく)

実装例

例えばPerlスクリプト

/usr/local/bin/cowsay
#!/usr/bin/perl

my $border = "-" x ((length $ARGV[-1]) + 2);
print <<"__EOL__";
 $border
< $ARGV[-1] >
 $border
 \\ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ
__EOL__

オリジナルのcowsayがインストール済みで、それ以外は特に追加設定を行わない場合、Ansibleからはcowsay -W 60 -f default "メッセージ"という形式でcowsayが実行される。

    def banner_cowsay(self, msg, color=None):
        if u": [" in msg:
            msg = msg.replace(u"[", u"")
            if msg.endswith(u"]"):
                msg = msg[:-1]
        runcmd = [self.b_cowsay, b"-W", b"60"]
        if self.noncow:
            thecow = self.noncow
            if thecow == 'random':
                thecow = random.choice(list(self.cows_available))
            runcmd.append(b'-f')
            runcmd.append(to_bytes(thecow))
        runcmd.append(to_bytes(msg))
        cmd = subprocess.Popen(runcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (out, err) = cmd.communicate()
        self.display(u"%s\n" % to_text(out), color=color)

この関数の

        runcmd = [self.b_cowsay, b"-W", b"60"]

[...]

            runcmd.append(b'-f')
            runcmd.append(to_bytes(thecow))

の部分。

なので、自作のcowsayを作る場合は、この呼び出し仕様を踏まえて、引数の一番最後を取り出して出力すれば、にゃんしぶるが実行できる。
(Perlにおいてスクリプトの引数は@ARGV配列で受け取ることができ、配列の末尾を参照するには配列インデックスに-1を指定すれば取り出せる)

実行例

[zaki@manager nyan]$ ansible-playbook playbook.yml 
 [WARNING]: provided hosts list is empty, only localhost is available. Note
that the implicit localhost does not match 'all'

 ------------------
< PLAY [localhost] >
 ------------------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ

 ------------------------
< TASK [Gathering Facts] >
 ------------------------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ

ok: [localhost]
 -------------
< TASK [ping] >
 -------------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ

ok: [localhost]
 ------------
< PLAY RECAP >
 ------------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ

localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[zaki@manager nyan]$

cowsay に自前のテンプレートを追加してにゃんしぶる

cowsay自体はパッケージインストールし、cowsayの出力テンプレートを用意することでもにゃんしぶるできる。
cowsayはデフォルトで牛のAAでメッセージを表示するが、表示形式はいくつかサンプルがあり、オプションで表示を変えることができる。

女子エンジニアはcowsayで女子力をupさせよう - Qiita

cowsay の設定

[zaki@manager nyan]$ cowsay Ansible
 _________
< Ansible >
 ---------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

選択できるテンプレート

[zaki@manager nyan]$ cowsay -l
Cow files in /usr/share/cowsay:
beavis.zen blowfish bong bud-frogs bunny cheese cower default dragon
dragon-and-cow elephant elephant-in-snake eyes flaming-sheep ghostbusters
head-in hellokitty kiss kitty koala kosh luke-koala mech-and-cow meow milk
moofasa moose mutilated ren satanic sheep skeleton small sodomized
stegosaurus stimpy supermilker surgery telebears three-eyes turkey turtle
tux udder vader vader-koala www
[zaki@manager nyan]$ 

tuxを選んでみる

[zaki@manager nyan]$ cowsay -f tux Ansible
 _________
< Ansible >
 ---------
   \
    \
        .--.
       |o_o |
       |:_/ |
      //   \ \
     (|     | )
    /'\_   _/`\
    \___)=(___/

[zaki@manager nyan]$

じゃあこのテンプレートってどこにあるのかというと、リストの1行目Cow files in /usr/share/cowsay:の通り、/usr/share/cowsay以下にある。

[zaki@manager nyan]$ ls -F /usr/share/cowsay/
DragonAndCow.pm  default.cow            luke-koala.cow    stimpy.cow
Example.pm       dragon-and-cow.cow     mech-and-cow.cow  supermilker.cow
Frogs.pm         dragon.cow             meow.cow          surgery.cow
MechAndCow.pm    elephant-in-snake.cow  milk.cow          telebears.cow
Stegosaurus.pm   elephant.cow           moofasa.cow       three-eyes.cow
TextBalloon.pm   eyes.cow               moose.cow         turkey.cow
TuxStab.pm       flaming-sheep.cow      mutilated.cow     turtle.cow
beavis.zen.cow   ghostbusters.cow       ren.cow           tux.cow
blowfish.cow     head-in.cow            satanic.cow       udder.cow
bong.cow         hellokitty.cow         sheep.cow         vader-koala.cow
bud-frogs.cow    kiss.cow               skeleton.cow      vader.cow
bunny.cow        kitty.cow              small.cow         www.cow
cheese.cow       koala.cow              sodomized.cow
cower.cow        kosh.cow               stegosaurus.cow

中身はPerlスクリプトで、$the_cow変数に出力するAAをセットしている。

/usr/share/cowsay/tux.cow
##
## TuX
## (c) pborys@p-soft.silesia.linux.org.pl
##
$the_cow = <<EOC;
   $thoughts
    $thoughts
        .--.
       |o_o |
       |:_/ |
      //   \\ \\
     (|     | )
    /'\\_   _/`\\
    \\___)=(___/

EOC
[zaki@manager nyan]$

ということは、あとはわかるな?

/usr/share/cowsay/cat.cow
##
## A cow wadvertising the World Wide Web, from lim@csua.berkeley.edu
##
$the_cow = <<EOC;
 \\ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ
EOC

ちなみに、日本語(多バイト文字)を含む場合、UTF-8では文字化けするため、UTF-16 LEで保存してある。

[zaki@manager ~]$ cowsay -l
Cow files in /usr/share/cowsay:
beavis.zen blowfish bong bud-frogs bunny cat cheese cower default dragon
dragon-and-cow elephant elephant-in-snake eyes flaming-sheep ghostbusters
head-in hellokitty kiss kitty koala kosh luke-koala mech-and-cow meow milk
moofasa moose mutilated ren satanic sheep skeleton small sodomized
stegosaurus stimpy supermilker surgery telebears three-eyes turkey turtle
tux udder vader vader-koala www

catが追加されている。
catを指定して実行。

[zaki@manager ~]$ cowsay -f cat Ansible
 _________
< Ansible >
 ---------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ
[zaki@manager ~]$

なお、パッケージで配置されるファイルの置き場である/usr/share/cowsayに自前のファイルを紛れさせるのが宗教上の理由等により不可能な場合は、/usr/local/share/cowsay/cat.cowに保存し、実行時に環境変数COWPATHで指定しても動く。(詳細はmanかcowsayのソース)

[zaki@manager ~]$ COWPATH=/usr/local/share/cowsay cowsay -l
Cow files in /usr/local/share/cowsay:
cat
[zaki@manager ~]$ COWPATH=/usr/local/share/cowsay cowsay -f cat aaa
 _____
< aaa >
 -----
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ
[zaki@manager ~]$

複数パス設定

[zaki@manager ~]$ COWPATH=/usr/local/share/cowsay:/usr/share/cowsay cowsay -l
Cow files in /usr/local/share/cowsay:
cat
Cow files in /usr/share/cowsay:
beavis.zen blowfish bong bud-frogs bunny cheese cower default dragon
dragon-and-cow elephant elephant-in-snake eyes flaming-sheep ghostbusters
head-in hellokitty kiss kitty koala kosh luke-koala mech-and-cow meow milk
moofasa moose mutilated ren satanic sheep skeleton small sodomized
stegosaurus stimpy supermilker surgery telebears three-eyes turkey turtle
tux udder vader vader-koala www
[zaki@manager ~]$

Ansible の設定

cowsayで自前のテンプレートでメッセージ出力する準備ができたので、Ansibleから指定のテンプレートを使う設定を行う。
といっても、ansible.cfgに記述するだけ。
(自前のテンプレートが/usr/local/share/cowsayなどデフォルト以外にある場合はCOWPATH=/usr/local/share/cowsayが必要)

ANSIBLE_COW_SELECTION

ansible.cfg
[defaults]
cow_selection = cat

ちなみにここにtuxなどの、cowsay -lで表示されるリストの項目を記述すれば、その内容でAnsibleの出力が行われる。
また、randomにすれば、使用可能なテンプレートから出力のたびにランダムで一つ選ばれる。

実行例

[zaki@manager nyan]$ ansible-playbook playbook.yml
 [WARNING]: provided hosts list is empty, only localhost is available. Note
that the implicit localhost does not match 'all'

 __________________
< PLAY [localhost] >
 ------------------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ

 ________________________
< TASK [Gathering Facts] >
 ------------------------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ

ok: [localhost]
 _____________
< TASK [ping] >
 -------------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ

ok: [localhost]
 ____________
< PLAY RECAP >
 ------------
 \ ∧_∧  
 .ミ,,・_・ミ
ヾ(,_uuノ

localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[zaki@manager nyan]$

cowsay を無効にする

ansible.cfg
[defaults]
nocows = True

あるいはyum erase cowsayで削除する。

callback plugin でにゃんしぶる

ここまでのcowsayを使ったにゃんしぶるだと、play名やtask名を出力しているだけなので、正常時はかわいいネコチャン、エラー時は毛が逆立ったネコチャン、みたいなことはできない。

この辺りをイジる場合は、callback plugin(普段使う時も「デフォルトのエラー見づらいわー、あ、でもyamlのstdout_callback使うと見やすくなるらしいで」ってアレ)を使えば可能。

「にゃんしぶる用のcallback pluginを自作」することになるが、「何も設定しないときに使用されるdefaultcallback plugin」をベースに作れば、表示をイジる程度であれば、コピペで簡単にできる。

Ansibleのcallback pluginを使って突然の死(echo-sd)を表現する - Qiita

callback pluginはAnsible本体と同じくPython製。
CentOS7でepelからパッケージインストールしたansible 2.8の場合は/usr/lib/python2.7/site-packages/ansible/plugins/callback/*.pyにプラグインのソースがある。

にゃんしぶる callback plugin を作る

まずはベースとなるdefault.pyを、プレイブックのあるディレクトリにcallback_pluginsディレクトリを作成し、その下へnyansible.pyという名前でコピーする。

[zaki@manager nyan]$ mkdir callback_plugins/
[zaki@manager nyan]$ cp /usr/lib/python2.7/site-packages/ansible/plugins/callba
ck/default.py callback_plugins/nyansible.py
[zaki@manager nyan]$ ll callback_plugins/
合計 20
-rw-r--r--. 1 zaki zaki 17488 11月  4 12:36 nyansible.py
[zaki@manager nyan]$

defaultをベースにした最も簡単なコピペコード

default callback pluginの、表示に部分のメッセージを変更してあげればOK.
ただし、日本語(多バイトコード)をそのまま書いてしまうと文字化けしてしまうので、対応が必要。

日本語対応

ERROR! Unexpected Exception, this is probably a bug: Non-ASCII character '\xce' in file /home/zaki/work/ansible/nyan/callback_plugins/nyansible.py on line 89, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details (nyansible.py, line 89)

スクリプト内に日本語を使用する場合、スクリプトの1行目(普段のPythonスクリプトでshebang(#!/usr/bin/env python3とか書くアレ)が1行目にある場合は2行目)にエンコードを指定する。
UTF-8であれば

# -*- coding: utf-8 -*-

と書く。

 [WARNING]: Failure using method (v2_runner_on_ok) in callback plugin
(<ansible.plugins.callback.nyansible.CallbackModule object at 0x7fda2cacc410>):
'ascii' codec can't decode byte 0xe0 in position 0: ordinal not in range(128)

また、日本語を使用する各箇所でUnicode指定を表すu" ... "という記述が必要。(okchangedのメッセージ出力はuがないため、付け加える)

self._display.display("fatal: [%s]: FAILED! => %s" ...

self._display.display(u"エラー: [%s]: FAILED! => %s" ...

と書く。

変更点サンプル

[zaki@manager nyan]$ diff -u /usr/lib/python2.7/site-packages/ansible/plugins/c
allback/default.py callback_plugins/nyansible.py
--- /usr/lib/python2.7/site-packages/ansible/plugins/callback/default.py       2019-09-13 06:12:55.000000000 +0900
+++ callback_plugins/nyansible.py       2019-11-04 12:59:50.520091324 +0900
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 # (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
 # (c) 2017 Ansible Project
 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
@@ -48,7 +49,7 @@

     CALLBACK_VERSION = 2.0
     CALLBACK_TYPE = 'stdout'
-    CALLBACK_NAME = 'default'
+    CALLBACK_NAME = 'nyansible'

     def __init__(self):

@@ -86,11 +87,11 @@

         else:
             if delegated_vars:
-                self._display.display("fatal: [%s -> %s]: FAILED! => %s" % (result._host.get_name(), delegated_vars['ansible_host'],
+                self._display.display(u"Σ(;Φ ω Φ): [%s -> %s]: FAILED! => %s" % (result._host.get_name(), delegated_vars['ansible_host'],
                                                                             self._dump_results(result._result)),
                                       color=C.COLOR_ERROR, stderr=self.display_failed_stderr)
             else:
-                self._display.display("fatal: [%s]: FAILED! => %s" % (result._host.get_name(), self._dump_results(result._result)),
+                self._display.display(u"Σ(;Φ ω Φ): [%s]: FAILED! => %s" % (result._host.get_name(), self._dump_results(result._result)),
                                       color=C.COLOR_ERROR, stderr=self.display_failed_stderr)

         if ignore_errors:
@@ -107,9 +108,9 @@
                 self._print_task_banner(result._task)

             if delegated_vars:
-                msg = "changed: [%s -> %s]" % (result._host.get_name(), delegated_vars['ansible_host'])
+                msg = u"ฅ/ᐠ。ᆽ。ᐟ \: [%s -> %s]" % (result._host.get_name(), delegated_vars['ansible_host'])
             else:
-                msg = "changed: [%s]" % result._host.get_name()
+                msg = u"ฅ/ᐠ。ᆽ。ᐟ \: [%s]" % result._host.get_name()
             color = C.COLOR_CHANGED
         else:
             if not self.display_ok_hosts:
@@ -119,9 +120,9 @@
                 self._print_task_banner(result._task)

             if delegated_vars:
-                msg = "ok: [%s -> %s]" % (result._host.get_name(), delegated_vars['ansible_host'])
+                msg = u"ฅ(^・ω・^ฅ): [%s -> %s]" % (result._host.get_name(), delegated_vars['ansible_host'])
             else:
-                msg = "ok: [%s]" % result._host.get_name()
+                msg = u"ฅ(^・ω・^ฅ): [%s]" % result._host.get_name()
             color = C.COLOR_OK

         self._handle_warnings(result._result)
[zaki@manager nyan]$

実行例

playbook.yml
---
- hosts: localhost
  tasks:
    - ping:
    - shell: date
    - yum:
        name: ruby
[zaki@manager nyan]$ ansible-playbook playbook.yml  
 [WARNING]: provided hosts list is empty, only localhost is available. Note
that the implicit localhost does not match 'all'


PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ฅ(^・ω・^ฅ): [localhost]

TASK [ping] ********************************************************************
ฅ(^・ω・^ฅ): [localhost]

TASK [shell] *******************************************************************
ฅ/ᐠ。ᆽ。ᐟ \: [localhost]

TASK [yum] *********************************************************************
Σ(;Φ ω Φ): [localhost]: FAILED! => {"changed": false, "changes": {"installed": ["ruby"]}, "msg": "You need to be root to perform this command.\n", "rc": 1, "results": ["Loaded plugins: fastestmirror\n"]}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

[zaki@manager nyan]$

エラーが見やすいyamlプラグインを継承したにゃんしぶる

defaultプラグインでなく、エラー時のメッセージをyaml形式で出力してくれるyamlプラグインをベースに作ってみる。
yamlプラグインは/usr/lib/python2.7/site-packages/ansible/plugins/ca
llback/yaml.py
にソースがある。ただしこのソースを見ても、okやchanged・fatalを出力しているコードがない。
じゃあどうなっているのかというと、Ansibleのcallback pluginはCallbackModuleクラスを作るようになっていて、yamlプラグインはdefaultプラグインを継承して作られている。
つまり、基本的な処理はすべてdefaultプラグインの処理をそのまま使用し、出力処理のみyaml.dump()を使うような実装になっている。

/usr/lib/python2.7/site-packages/ansible/plugins/callback/yaml.py
        if abridged_result:
            dumped += '\n'
            dumped += to_text(yaml.dump(abridged_result, allow_unicode=True, width=1000, Dumper=AnsibleDumper, default_flow_style=False))

というわけなので、yamlプラグインを継承し、ok/changed/fatalなどを出力する処理をオーバーライドするような実装にしてやればOK

yamlプラグインのクラスを継承したにゃんしぶる用CallbackModuleクラスを作成する

まずは自前の処理を何も実装していない、yamlプラグインをそのまま継承しただけのクラスを作成。
こんな感じ。

名前はnyamlにしました。

callback_plugins/nyaml.py
# -*- coding: utf-8 -*-

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

DOCUMENTATION = '''
    callback: nyaml
    type: stdout
    short_description: nyaml-ized Ansible screen output
'''

from ansible.plugins.callback.yaml import CallbackModule as CallbackModule_yaml

class CallbackModule(CallbackModule_yaml):

    """
    nyansible and nyaml /ᐠ。ꞈ。ᐟ\
    """

    CALLBACK_VERSION = 2.0
    CALLBACK_TYPE = 'stdout'
    CALLBACK_NAME = 'yaml'

※ この時点でansible.cfgstdout_callback = nyamlと書けば(ただのyamlプラグインと同じように)動く。

メッセージ出力処理を実装する

defaultプラグインのソースからメッセージ出力処理の関数を拝借してくる。
対象はv2_runner_on_ok()v2_runner_on_failed()など。
これらの関数をまずはコピペして動かしてみる。

すると、いくつか定義が足りないとwarningが出力される。

TASK [Gathering Facts] ********************************************************* [WARNING]: Failure using method (v2_runner_on_ok) in callback plugin
(<ansible.plugins.callback.nyaml.CallbackModule object at 0x7fc66f796c50>):
global name 'TaskInclude' is not defined


TASK [ping] ********************************************************************
TASK [shell] *******************************************************************
TASK [yum] ********************************************************************* [WARNING]: Failure using method (v2_runner_on_failed) in callback plugin
(<ansible.plugins.callback.nyaml.CallbackModule object at 0x7fc66f796c50>):
global name 'C' is not defined

TaskIncludeCが無いと言われている。

ので、これをdefaultプラグインのソースからさらに拝借する。(基本的にこの作業の繰り返し)
というわけで、全体のソースはこんな感じ。

callback_plugins/nyaml.py
# -*- coding: utf-8 -*-

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

DOCUMENTATION = '''
    callback: nyaml
    type: stdout
    short_description: nyaml-ized Ansible screen output
'''

from ansible.plugins.callback.yaml import CallbackModule as CallbackModule_yaml
from ansible import constants as C
from ansible.playbook.task_include import TaskInclude
from ansible.utils.color import colorize, hostcolor

class CallbackModule(CallbackModule_yaml):

    """
    nyansible and nyaml /ᐠ。ꞈ。ᐟ\
    """

    CALLBACK_VERSION = 2.0
    CALLBACK_TYPE = 'stdout'
    CALLBACK_NAME = 'yaml'

    def v2_runner_on_failed(self, result, ignore_errors=False):

        delegated_vars = result._result.get('_ansible_delegated_vars', None)
        self._clean_results(result._result, result._task.action)

        if self._last_task_banner != result._task._uuid:
            self._print_task_banner(result._task)

        self._handle_exception(result._result, use_stderr=self.display_failed_stderr)
        self._handle_warnings(result._result)

        if result._task.loop and 'results' in result._result:
            self._process_items(result)

        else:
            if delegated_vars:
                self._display.display(u"Σ(;Φ ω Φ): [%s -> %s]: FAILED! => %s" % (result._host.get_name(), delegated_vars['ansible_host'],
                                                                            self._dump_results(result._result)),
                                      color=C.COLOR_ERROR, stderr=self.display_failed_stderr)
            else:
                self._display.display(u"Σ(;Φ ω Φ): [%s]: FAILED! => %s" % (result._host.get_name(), self._dump_results(result._result)),
                                      color=C.COLOR_ERROR, stderr=self.display_failed_stderr)

        if ignore_errors:
            self._display.display("...ignoring", color=C.COLOR_SKIP)

    def v2_runner_on_ok(self, result):

        delegated_vars = result._result.get('_ansible_delegated_vars', None)

        if isinstance(result._task, TaskInclude):
            return
        elif result._result.get('changed', False):
            if self._last_task_banner != result._task._uuid:
                self._print_task_banner(result._task)

            if delegated_vars:
                msg = u"ฅ/ᐠ。ᆽ。ᐟ\: [%s -> %s]" % (result._host.get_name(), delegated_vars['ansible_host'])
            else:
                msg = u"ฅ/ᐠ。ᆽ。ᐟ\: [%s]" % result._host.get_name()
            color = C.COLOR_CHANGED
        else:
            if not self.display_ok_hosts:
                return

            if self._last_task_banner != result._task._uuid:
                self._print_task_banner(result._task)

            if delegated_vars:
                msg = u"ฅ(^・ω・^ฅ): [%s -> %s]" % (result._host.get_name(), delegated_vars['ansible_host'])
            else:
                msg = u"ฅ(^・ω・^ฅ): [%s]" % result._host.get_name()
            color = C.COLOR_OK

        self._handle_warnings(result._result)

        if result._task.loop and 'results' in result._result:
            self._process_items(result)
        else:
            self._clean_results(result._result, result._task.action)

            if (self._display.verbosity > 0 or '_ansible_verbose_always' in result._result) and '_ansible_verbose_override' not in result._result:
                msg += " => %s" % (self._dump_results(result._result),)
            self._display.display(msg, color=color)

nyamlプラグインでにゃんしぶる

このnyamlプラグインを使ってansible-playbookを実行すると

[zaki@manager nyan]$ ansible-playbook playbook.yml  
 [WARNING]: provided hosts list is empty, only localhost is available. Note
that the implicit localhost does not match 'all'


PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ฅ(^・ω・^ฅ): [localhost]

TASK [ping] ********************************************************************
ฅ(^・ω・^ฅ): [localhost]

TASK [shell] *******************************************************************
ฅ/ᐠ。ᆽ。ᐟ\: [localhost]

TASK [yum] *********************************************************************
Σ(;Φ ω Φ): [localhost]: FAILED! => changed=false 
  changes:
    installed:
    - ruby
  msg: |-
    You need to be root to perform this command.
  rc: 1
  results:
  - |-
    Loaded plugins: fastestmirror

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

[zaki@manager nyan]$

参考資料


ちなみに筆者はアイコンからもわかるかと思いますが、うさぎが好きです🐰

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
What you can do with signing up
7