-
-
Save asmartin/c03bdeeff6854a06bfb779cf6d2289bb to your computer and use it in GitHub Desktop.
| # This program is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation, either version 3 of the License, or | |
| # (at your option) any later version. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| # Documentation: http://blog.cliffano.com/2014/04/06/human-readable-ansible-playbook-log-output-using-callback-plugin/ | |
| # Improved and made compatible with Ansible v2 | |
| # Original Source: https://gist.github.com/cliffano/9868180 | |
| # First Modified Version: https://gist.github.com/dmsimard/cd706de198c85a8255f6 | |
| # This Version: https://gist.github.com/asmartin/c03bdeeff6854a06bfb779cf6d2289bb | |
| # | |
| # Changelog: | |
| # 2017-03-28 - [asmartin] added support for no_log | |
| from __future__ import (absolute_import, division, print_function) | |
| __metaclass__ = type | |
| from ansible.plugins.callback import CallbackBase | |
| try: | |
| import simplejson as json | |
| except ImportError: | |
| import json | |
| # Fields to reformat output for | |
| FIELDS = ['cmd', 'command', 'start', 'end', 'delta', 'msg', 'stdout', | |
| 'stderr', 'results'] | |
| NO_LOG_FIELDS = ['cmd', 'command', 'msg', 'stdout', 'stderr', 'results'] | |
| class CallbackModule(CallbackBase): | |
| def __init__(self, *args, **kwargs): | |
| super(CallbackModule, self).__init__(*args, **kwargs) | |
| self.task = None | |
| self.play = None | |
| def human_log(self, data): | |
| if type(data) == dict: | |
| for field in FIELDS: | |
| if field in data.keys() and data[field]: | |
| if field in NO_LOG_FIELDS and getattr(self.task, 'no_log', False): | |
| print("\n{0}: <output hidden via no_log>".format(field)) | |
| else: | |
| output = self._format_output(data[field]) | |
| print("\n{0}: {1}".format(field, output.replace("\\n","\n"))) | |
| def _format_output(self, output): | |
| # Strip unicode | |
| if type(output) == unicode: | |
| output = output.encode('ascii', 'replace') | |
| # If output is a dict | |
| if type(output) == dict: | |
| return json.dumps(output, indent=2) | |
| # If output is a list of dicts | |
| if type(output) == list and type(output[0]) == dict: | |
| # This gets a little complicated because it potentially means | |
| # nested results, usually because of with_items. | |
| real_output = list() | |
| for index, item in enumerate(output): | |
| copy = item | |
| if type(item) == dict: | |
| for field in FIELDS: | |
| if field in item.keys(): | |
| copy[field] = self._format_output(item[field]) | |
| real_output.append(copy) | |
| return json.dumps(output, indent=2) | |
| # If output is a list of strings | |
| if type(output) == list and type(output[0]) != dict: | |
| # Strip newline characters | |
| real_output = list() | |
| for item in output: | |
| if "\n" in item: | |
| for string in item.split("\n"): | |
| real_output.append(string) | |
| else: | |
| real_output.append(item) | |
| # Reformat lists with line breaks only if the total length is | |
| # >75 chars | |
| if len("".join(real_output)) > 75: | |
| return "\n" + "\n".join(real_output) | |
| else: | |
| return " ".join(real_output) | |
| # Otherwise it's a string, just return it | |
| return output | |
| def on_any(self, *args, **kwargs): | |
| pass | |
| def runner_on_failed(self, host, res, ignore_errors=False): | |
| self.human_log(res) | |
| def runner_on_ok(self, host, res): | |
| self.human_log(res) | |
| def runner_on_error(self, host, msg): | |
| pass | |
| def runner_on_skipped(self, host, item=None): | |
| pass | |
| def runner_on_unreachable(self, host, res): | |
| self.human_log(res) | |
| def runner_on_no_hosts(self): | |
| pass | |
| def runner_on_async_poll(self, host, res, jid, clock): | |
| self.human_log(res) | |
| def runner_on_async_ok(self, host, res, jid): | |
| self.human_log(res) | |
| def runner_on_async_failed(self, host, res, jid): | |
| self.human_log(res) | |
| def playbook_on_start(self): | |
| pass | |
| def playbook_on_notify(self, host, handler): | |
| pass | |
| def playbook_on_no_hosts_matched(self): | |
| pass | |
| def playbook_on_no_hosts_remaining(self): | |
| pass | |
| def playbook_on_task_start(self, name, is_conditional): | |
| pass | |
| def playbook_on_vars_prompt(self, varname, private=True, prompt=None, | |
| encrypt=None, confirm=False, salt_size=None, | |
| salt=None, default=None): | |
| pass | |
| def playbook_on_setup(self): | |
| pass | |
| def playbook_on_import_for_host(self, host, imported_file): | |
| pass | |
| def playbook_on_not_import_for_host(self, host, missing_file): | |
| pass | |
| def playbook_on_play_start(self, pattern): | |
| pass | |
| def playbook_on_stats(self, stats): | |
| pass | |
| def v2_playbook_on_play_start(self, play): | |
| self.play = play | |
| def v2_playbook_on_task_start(self, task, is_conditional): | |
| self.task = task |
I was getting this error:
[WARNING]: Failure using method (v2_runner_on_ok) in callback plugin (</home/X/callback_plugins/human_log.CallbackModule object at 0x7f3b1bbb6c50>): 'ascii' codec can't encode character u'\u25cf' in position 0: ordinal not in range(128)
The problem seems to be if type(output) == unicode is insufficient. Ansible wraps the unicode in a "ansible.vars.unsafe_proxy.AnsibleUnsafeText" class.
After the __future__ import I added from ansible.vars.unsafe_proxy import AnsibleUnsafeText
and changed the conditional to if type(output) == unicode or type(output) == AnsibleUnsafeText:
forked this and implemented an updated fix as @mark-wagner demonstrated in previous comment here. Worked for me w/o any issue @Krishna1408.
https://gist.github.com/shanedroid/beca4902c6a1af1bd23b3c92f118f6ed
also worth noting that this callback plugin may no longer needed as the debug plugin can be used by default now, I have just switched to using that instead:
ansible/ansible#27078 (comment)
https://github.com/ansible/ansible/blob/v2.4.3.0-1/lib/ansible/plugins/callback/debug.py
https://gist.github.com/cliffano/9868180#gistcomment-1915662
Hi,
Thanks a lot for updating the code. I am getting below warning and logs are not being shown in human readable format:
defined
can you please help with it ?
Best Regards,
Kris