Customise Ansible plugin callback for garcon

Customises Ansible plugin callback for garcon, which includes
* renaming callback
* changing callback description
* adding custom messages to garcon
* changing warnings print destination to stdout
* stripping down not used functionality

Also adds build rules to put callback plugin file to standard callback
plugins folder as a part of cros-garcon package.

BUG=chromium:1021641
TEST=bazel build //cros-debs:debs --host_force_python=py2 & installed
cros-garcon deb on DUT

Change-Id: Ie5a0b9f7a95d248bf820a0f240122860a4b86689
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/containers/cros-container-guest-tools/+/1992391
Reviewed-by: Stephen Barber <smbarber@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Olya Kalitova <okalitova@chromium.org>
diff --git a/cros-garcon/BUILD b/cros-garcon/BUILD
index e56e8b0..bff1a36 100644
--- a/cros-garcon/BUILD
+++ b/cros-garcon/BUILD
@@ -80,9 +80,18 @@
 )
 
 pkg_tar(
+    name = "cros-garcon-ansible-integration",
+    srcs = ["third_party/garcon.py"],
+    mode = "0644",
+    package_dir = "/usr/share/ansible/plugins/callback",
+    strip_prefix = "/cros-garcon/third_party",
+)
+
+pkg_tar(
     name = "debian-data",
     extension = "tar.gz",
     deps = [
+        ":cros-garcon-ansible-integration",
         ":cros-garcon-config",
         ":cros-garcon-desktop",
         ":cros-garcon-dpkg-config",
@@ -111,6 +120,6 @@
     postinst = "postinst",
     prerm = "prerm",
     section = "misc",
-    version = "0.21",
+    version = "0.22",
     visibility = ["//cros-debs:__pkg__"],
 )
diff --git a/cros-garcon/third_party/README.chromium b/cros-garcon/third_party/README.chromium
index b66ef87..f60018d 100644
--- a/cros-garcon/third_party/README.chromium
+++ b/cros-garcon/third_party/README.chromium
@@ -11,4 +11,6 @@
 Files for garcon integration with Ansible that are put into Crostini default
 container so that ansible-playbook spawned from garcon is integrated with
 garcon, for example, produces custom parsable output.
-Local Modifications: none
+Local Modifications: Callback plugin is renamed and description is changed to
+match plugin purpose. Added custom messages parsed by garcon, stripped down,
+changed warnings print destination to stdout.
diff --git a/cros-garcon/third_party/garcon.py b/cros-garcon/third_party/garcon.py
new file mode 100644
index 0000000..f295138
--- /dev/null
+++ b/cros-garcon/third_party/garcon.py
@@ -0,0 +1,106 @@
+# (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)
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+DOCUMENTATION = '''
+    callback: garcon
+    type: stdout
+    short_description: Ansible screen output for cros-garcon package
+    version_added: historical
+    description:
+        - This is the output callback used by the ansible-playbook command
+        - triggered by garcon.
+'''
+
+from ansible.plugins.callback import CallbackBase
+from ansible import constants as C
+
+
+class CallbackModule(CallbackBase):
+    '''
+    Callback plugin which is used by ansible-playbook command triggered by
+    cros-garcon. Prints custom messages to garcon alongside minimal callback
+    plugin messages when new callback events are received.
+    '''
+
+    CALLBACK_VERSION = 1.0
+    CALLBACK_TYPE = 'stdout'
+    CALLBACK_NAME = 'garcon'
+
+    MESSAGE_TO_GARCON_IDENTIFIER = "MESSAGE TO GARCON: "
+    TASK_FAILED_MESSAGE = "TASK_FAILED"
+    TASK_OK_MESSAGE = "TASK_OK"
+    TASK_SKIPPED_MESSAGE = "TASK_SKIPPED"
+    TASK_UNREACHABLE_MESSAGE = "TASK_UNREACHABLE"
+
+    def _command_generic_msg(self, message_to_garcon, host, result, caption):
+        ''' output the result of a command run '''
+
+        buf = self.MESSAGE_TO_GARCON_IDENTIFIER + message_to_garcon + "\n"
+
+        buf += "%s | %s | rc=%s >>\n" % (host, caption, result.get('rc', -1))
+        buf += result.get('stdout', '')
+        buf += result.get('stderr', '')
+        buf += result.get('msg', '')
+
+        return buf + "\n"
+
+    # Redefines warnings handling of a base class so that they are printed to
+    # stdout instead of stderr.
+    def _handle_warnings(self, res):
+        ''' display warnings, if enabled and any exist in the result '''
+        if C.ACTION_WARNINGS:
+            if 'warnings' in res and res['warnings']:
+                for warning in res['warnings']:
+                    self._display.display(warning)
+                del res['warnings']
+            if 'deprecations' in res and res['deprecations']:
+                for warning in res['deprecations']:
+                    self._display.display(**warning)
+                del res['deprecations']
+
+    def v2_runner_on_failed(self, result, ignore_errors=False):
+        self._handle_exception(result._result)
+        self._handle_warnings(result._result)
+
+        self._display.display(
+            self._command_generic_msg(self.TASK_FAILED_MESSAGE,
+                                      result._host.get_name(), result._result,
+                                      "FAILED"))
+
+    def v2_runner_on_ok(self, result):
+        self._clean_results(result._result, result._task.action)
+
+        self._handle_warnings(result._result)
+
+        if result._result.get('changed', False):
+            state = 'CHANGED'
+        else:
+            state = 'SUCCESS'
+
+        self._display.display(
+            self._command_generic_msg(self.TASK_OK_MESSAGE,
+                                      result._host.get_name(), result._result,
+                                      state))
+
+    def v2_runner_on_skipped(self, result):
+        self._display.display(self.MESSAGE_TO_GARCON_IDENTIFIER +
+                              self.TASK_SKIPPED_MESSAGE)
+
+        self._display.display("%s | SKIPPED" % (result._host.get_name()))
+
+    def v2_runner_on_unreachable(self, result):
+        self._display.display(self.MESSAGE_TO_GARCON_IDENTIFIER +
+                              self.TASK_UNREACHABLE_MESSAGE)
+
+        self._display.display("%s | UNREACHABLE! => %s" %
+                              (result._host.get_name(),
+                               self._dump_results(result._result, indent=4)))
+
+    def v2_on_file_diff(self, result):
+        if 'diff' in result._result and result._result['diff']:
+            self._display.display(self._get_diff(result._result['diff']))
diff --git a/cros-garcon/third_party/minimal.py b/cros-garcon/third_party/minimal.py
deleted file mode 100644
index 8d3aef5..0000000
--- a/cros-garcon/third_party/minimal.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# (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)
-
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-DOCUMENTATION = '''
-    callback: minimal
-    type: stdout
-    short_description: minimal Ansible screen output
-    version_added: historical
-    description:
-        - This is the default output callback used by the ansible command (ad-hoc)
-'''
-
-from ansible.plugins.callback import CallbackBase
-from ansible import constants as C
-
-
-class CallbackModule(CallbackBase):
-
-    '''
-    This is the default callback interface, which simply prints messages
-    to stdout when new callback events are received.
-    '''
-
-    CALLBACK_VERSION = 2.0
-    CALLBACK_TYPE = 'stdout'
-    CALLBACK_NAME = 'minimal'
-
-    def _command_generic_msg(self, host, result, caption):
-        ''' output the result of a command run '''
-
-        buf = "%s | %s | rc=%s >>\n" % (host, caption, result.get('rc', -1))
-        buf += result.get('stdout', '')
-        buf += result.get('stderr', '')
-        buf += result.get('msg', '')
-
-        return buf + "\n"
-
-    def v2_runner_on_failed(self, result, ignore_errors=False):
-
-        self._handle_exception(result._result)
-        self._handle_warnings(result._result)
-
-        if result._task.action in C.MODULE_NO_JSON and 'module_stderr' not in result._result:
-            self._display.display(self._command_generic_msg(result._host.get_name(), result._result, "FAILED"), color=C.COLOR_ERROR)
-        else:
-            self._display.display("%s | FAILED! => %s" % (result._host.get_name(), self._dump_results(result._result, indent=4)), color=C.COLOR_ERROR)
-
-    def v2_runner_on_ok(self, result):
-        self._clean_results(result._result, result._task.action)
-
-        self._handle_warnings(result._result)
-
-        if result._result.get('changed', False):
-            color = C.COLOR_CHANGED
-            state = 'CHANGED'
-        else:
-            color = C.COLOR_OK
-            state = 'SUCCESS'
-
-        if result._task.action in C.MODULE_NO_JSON and 'ansible_job_id' not in result._result:
-            self._display.display(self._command_generic_msg(result._host.get_name(), result._result, state), color=color)
-        else:
-            self._display.display("%s | %s => %s" % (result._host.get_name(), state, self._dump_results(result._result, indent=4)), color=color)
-
-    def v2_runner_on_skipped(self, result):
-        self._display.display("%s | SKIPPED" % (result._host.get_name()), color=C.COLOR_SKIP)
-
-    def v2_runner_on_unreachable(self, result):
-        self._display.display("%s | UNREACHABLE! => %s" % (result._host.get_name(), self._dump_results(result._result, indent=4)), color=C.COLOR_UNREACHABLE)
-
-    def v2_on_file_diff(self, result):
-        if 'diff' in result._result and result._result['diff']:
-            self._display.display(self._get_diff(result._result['diff']))