Jiri Podivin 9163d8bcbc Moving callbacks to validations-libs
Callback plugins were transferred from validations-common
repository to validations-libs.

Necessary adjustments were made to the module structure,
requierements, as well as installation and documentation generator config.

Associated tests were moved as well, with removal of superfluous inheritance
and imports included.

Demonstration http server module for communication with `http_json`
callback was moved with directory structure preserved.

Signed-off-by: Jiri Podivin <jpodivin@redhat.com>
Change-Id: I31768375430a2f29da71aae8f3db3882c373ced5
2022-03-10 08:59:40 +00:00

204 lines
7.7 KiB
Python

# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pprint
from ansible import constants as C
from ansible.plugins.callback import CallbackBase
FAILURE_TEMPLATE = """\
Task '{}' failed:
Host: {}
Message: {}
"""
WARNING_TEMPLATE = """\
Task '{}' succeeded, but had some warnings:
Host: {}
Warnings: {}
"""
DEBUG_TEMPLATE = """\
Task: Debug
Host: {}
{}
"""
def indent(text):
'''Indent the given text by four spaces.'''
return ''.join(' {}\n'.format(line) for line in text.splitlines())
# TODO(shadower): test with async settings
class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'validation_output'
def __init__(self, display=None):
super(CallbackModule, self).__init__(display)
def print_failure_message(self, host_name, task_name, results,
abridged_result):
'''Print a human-readable error info from Ansible result dictionary.'''
def is_script(results):
return ('rc' in results and 'invocation' in results
and 'script' in results._task_fields['action']
and '_raw_params' in results._task_fields['args'])
display_full_results = False
if 'rc' in results and 'cmd' in results:
command = results['cmd']
# The command can be either a list or a string.
# Concat if it's a list:
if type(command) == list:
command = " ".join(results['cmd'])
message = "Command `{}` exited with code: {}".format(
command, results['rc'])
# There may be an optional message attached to the command.
# Display it:
if 'msg' in results:
message = message + ": " + results['msg']
elif is_script(results):
script_name = results['invocation']['module_args']['_raw_params']
message = "Script `{}` exited with code: {}".format(
script_name, results['rc'])
elif 'msg' in results:
message = results['msg']
else:
message = "Unknown error"
display_full_results = True
self._display.display(
FAILURE_TEMPLATE.format(task_name, host_name, message),
color=C.COLOR_ERROR)
stdout = results.get('module_stdout', results.get('stdout', ''))
if stdout:
print('stdout:')
self._display.display(indent(stdout), color=C.COLOR_ERROR)
stderr = results.get('module_stderr', results.get('stderr', ''))
if stderr:
print('stderr:')
self._display.display(indent(stderr), color=C.COLOR_ERROR)
if display_full_results:
print(
"Could not get an error message. Here is the Ansible output:")
pprint.pprint(abridged_result, indent=4)
warnings = results.get('warnings', [])
if warnings:
print("Warnings:")
for warning in warnings:
self._display.display("* %s " % warning, color=C.COLOR_WARN)
print("")
def v2_playbook_on_play_start(self, play):
pass # No need to notify that a play started
def v2_playbook_on_task_start(self, task, is_conditional):
pass # No need to notify that a task started
def v2_runner_on_ok(self, result, **kwargs):
host_name = result._host
task_name = result._task.get_name()
task_fields = result._task_fields
results = result._result # A dict of the module name etc.
self._dump_results(results)
warnings = results.get('warnings', [])
# Print only tasks that produced some warnings:
if warnings:
for warning in warnings:
warn_msg = "{}\n".format(warning)
self._display.display(WARNING_TEMPLATE.format(task_name,
host_name,
warn_msg),
color=C.COLOR_WARN)
if 'debug' in task_fields['action']:
output = ""
if 'var' in task_fields['args']:
variable = task_fields['args']['var']
value = results[variable]
output = "{}: {}".format(variable, str(value))
elif 'msg' in task_fields['args']:
output = "Message: {}".format(
task_fields['args']['msg'])
self._display.display(DEBUG_TEMPLATE.format(host_name, output),
color=C.COLOR_OK)
def v2_runner_on_failed(self, result, **kwargs):
host_name = result._host
task_name = result._task.get_name()
result_dict = result._result # A dict of the module name etc.
abridged_result = self._dump_results(result_dict)
if 'results' in result_dict:
# The task is a list of items under `results`
for item in result_dict['results']:
if item.get('failed', False):
self.print_failure_message(host_name, task_name,
item, item)
else:
# The task is a "normal" module invocation
self.print_failure_message(host_name, task_name, result_dict,
abridged_result)
def v2_runner_on_skipped(self, result, **kwargs):
pass # No need to print skipped tasks
def v2_runner_on_unreachable(self, result, **kwargs):
host_name = result._host
task_name = result._task.get_name()
results = {'msg': 'The host is unreachable.'}
self.print_failure_message(host_name, task_name, results, results)
def v2_playbook_on_stats(self, stats):
def failed(host):
_failures = stats.summarize(host).get('failures', 0) > 0
_unreachable = stats.summarize(host).get('unreachable', 0) > 0
return (_failures or _unreachable)
hosts = sorted(stats.processed.keys())
failed_hosts = [host for host in hosts if failed(host)]
if hosts:
if failed_hosts:
if len(failed_hosts) == len(hosts):
print("Failure! The validation failed for all hosts:")
for failed_host in failed_hosts:
self._display.display("* %s" % failed_host,
color=C.COLOR_ERROR)
else:
print("Failure! The validation failed for hosts:")
for failed_host in failed_hosts:
self._display.display("* %s" % failed_host,
color=C.COLOR_ERROR)
print("and passed for hosts:")
for host in [h for h in hosts if h not in failed_hosts]:
self._display.display("* %s" % host,
color=C.COLOR_OK)
else:
print("Success! The validation passed for all hosts:")
for host in hosts:
self._display.display("* %s" % host,
color=C.COLOR_OK)
else:
print("Warning! The validation did not run on any host.")