diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2052dab..da4b47a 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -140,3 +140,4 @@ 3.1.0: RD-5392 Kerberos Support 3.1.1: Add missing configure task 3.1.2: Ignore system default for ansible because Cloudify requires python 3. +3.1.3: Extract error messages from hosts if possible. diff --git a/cloudify_ansible/utils.py b/cloudify_ansible/utils.py index bb01009..a86616a 100644 --- a/cloudify_ansible/utils.py +++ b/cloudify_ansible/utils.py @@ -31,6 +31,7 @@ from ansible.playbook import Playbook from cloudify.manager import get_rest_client from cloudify.utils import LocalCommandRunner +from script_runner.tasks import ProcessException from ansible.vars.manager import VariableManager from ansible.parsing.dataloader import DataLoader from cloudify_rest_client.constants import VisibilityState @@ -1016,7 +1017,15 @@ def returns(value): ctx._return_value = None - actual_result = script_func(script_path, ctx, process) + try: + actual_result = script_func(script_path, ctx, process) + except ProcessException as e: + issues = get_issues_from_process_exception(e) + if issues: + raise NonRecoverableError( + 'The following Ansible tasks failed: {}'.format(issues)) + else: + raise e script_result = ctx._return_value if ctx.is_script_exception_defined and isinstance( script_result, ScriptException): @@ -1028,6 +1037,55 @@ def returns(value): return actual_result +def get_message_from_failed_task(task, host): + try: + if task['hosts'][host]['failed']: + message = 'The action {} for host {} failed: {}'.format( + {task['hosts'][host]['action']}, + host, + {task['hosts'][host]['msg']}) + return message + except KeyError: + return + + +def get_issues_from_process_exception(exc): + try: + # We have a big stdout message with JSON result. After META... + result = exc.stdout.split('META: ran handlers')[-1] + loaded_result = json.loads(result) + issues = [] + # Example Result: + # { + # "plays": [ + # { + # "play": {"name": "localhost"}, + # "tasks": [ + # { + # "hosts": { + # "localhost": { + # "action": "debug", + # "changed": false, + # "failed": true, + # "msg": "Invalid options for debug" + # } + # }, + # } + # ] + # } + # ] + # } + for play in loaded_result['plays']: + for task in play['tasks']: + for host in task['hosts']: + message = get_message_from_failed_task(task, host) + if message: + issues.append(message) + return issues + except Exception: + return + + def get_plays(string, node_name): """When play output is returned in JSON, we can parse it and retrieve only the play dictionary. This is a little messy, and it's diff --git a/plugin.yaml b/plugin.yaml index 40de9ae..3989092 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -3,7 +3,7 @@ plugins: ansible: executor: central_deployment_agent package_name: cloudify-ansible-plugin - package_version: '3.1.2' + package_version: '3.1.3' dsl_definitions: diff --git a/plugin_1_4.yaml b/plugin_1_4.yaml index c01282a..5c250fa 100644 --- a/plugin_1_4.yaml +++ b/plugin_1_4.yaml @@ -3,7 +3,7 @@ plugins: ansible: executor: central_deployment_agent package_name: cloudify-ansible-plugin - package_version: '3.1.2' + package_version: '3.1.3' dsl_definitions: diff --git a/v2_plugin.yaml b/v2_plugin.yaml index d8b1a7e..99090c2 100644 --- a/v2_plugin.yaml +++ b/v2_plugin.yaml @@ -3,7 +3,7 @@ plugins: ansible: executor: central_deployment_agent package_name: cloudify-ansible-plugin - package_version: '3.1.2' + package_version: '3.1.3' dsl_definitions: