diff --git a/changelogs/fragments/timedout_test.yml b/changelogs/fragments/timedout_test.yml new file mode 100644 index 00000000000..7784b691da5 --- /dev/null +++ b/changelogs/fragments/timedout_test.yml @@ -0,0 +1,2 @@ +minor_changes: + - timedout test for checking if a task result represents a 'timed out' task. diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index fa09611578f..932a33cfec0 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -676,7 +676,7 @@ class TaskExecutor: return dict(unreachable=True, msg=to_text(e)) except TaskTimeoutError as e: msg = 'The %s action failed to execute in the expected time frame (%d) and was terminated' % (self._task.action, self._task.timeout) - return dict(failed=True, msg=msg, timedout=e.frame) + return dict(failed=True, msg=msg, timedout={'frame': e.frame, 'period': self._task.timeout}) finally: if self._task.timeout: signal.alarm(0) diff --git a/lib/ansible/plugins/test/core.py b/lib/ansible/plugins/test/core.py index a01e0d9950d..ed357a4bbaf 100644 --- a/lib/ansible/plugins/test/core.py +++ b/lib/ansible/plugins/test/core.py @@ -40,6 +40,13 @@ except ImportError: display = Display() +def timedout(result): + ''' Test if task result yields a time out''' + if not isinstance(result, MutableMapping): + raise errors.AnsibleFilterError("The 'timedout' test expects a dictionary") + return result.get('timedout', False) and result['timedout'].get('period', False) + + def failed(result): ''' Test if task result yields failed ''' if not isinstance(result, MutableMapping): @@ -263,6 +270,7 @@ class TestModule(object): 'successful': success, 'reachable': reachable, 'unreachable': unreachable, + 'timedout': timedout, # changed testing 'changed': changed, diff --git a/lib/ansible/plugins/test/timedout.yml b/lib/ansible/plugins/test/timedout.yml new file mode 100644 index 00000000000..735a8161db8 --- /dev/null +++ b/lib/ansible/plugins/test/timedout.yml @@ -0,0 +1,20 @@ +DOCUMENTATION: + name: timedout + author: Ansible Core + version_added: "2.18" + short_description: did the task time out + description: + - Tests if task finished ended due to a time out + options: + _input: + description: registered result from an Ansible task + type: dictionary + required: True +EXAMPLES: | + # test 'status' to know how to respond + {{ taskresults is timedout }} + +RETURN: + _value: + description: A dictionary with 2 keys 'frame' showing the 'frame of code' in which the timeout occurred and 'period' with the time limit that was enforced. + type: dict diff --git a/test/integration/targets/test_core/tasks/main.yml b/test/integration/targets/test_core/tasks/main.yml index 7c4ed65e48b..0e173ac5c0a 100644 --- a/test/integration/targets/test_core/tasks/main.yml +++ b/test/integration/targets/test_core/tasks/main.yml @@ -368,3 +368,18 @@ - "'files/notvault' is not vaulted_file" - "'files/vault1' is vaulted_file" - "'files/vault2' is vaulted_file" + + +- name: test timeout test + tags: + - timeout + block: + - command: sleep 5 + timeout: 3 + register: timed + ignore_errors: true + + - assert: + that: + - timed is timedout + - timed['timedout'].get('period', 0) == 3