diff options
author | softwarefactory-project-zuul[bot] <33884098+softwarefactory-project-zuul[bot]@users.noreply.github.com> | 2020-09-29 16:59:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-29 16:59:52 +0200 |
commit | f39015156bc7720d042df85de04a0db11241c711 (patch) | |
tree | 44d15f44cd372ac078c22963ab684d6750514c50 | |
parent | Merge pull request #8254 from RULCSoft/fix-typos (diff) | |
parent | Update integration tests (diff) | |
download | awx-f39015156bc7720d042df85de04a0db11241c711.tar.xz awx-f39015156bc7720d042df85de04a0db11241c711.zip |
Merge pull request #8228 from john-westcott-iv/tower_ad_hoc_module
Adding ad hoc command modules
Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
9 files changed, 824 insertions, 2 deletions
diff --git a/awx_collection/plugins/modules/tower_ad_hoc_command.py b/awx_collection/plugins/modules/tower_ad_hoc_command.py new file mode 100644 index 0000000000..d952f954cf --- /dev/null +++ b/awx_collection/plugins/modules/tower_ad_hoc_command.py @@ -0,0 +1,187 @@ +#!/usr/bin/python +# coding: utf-8 -*- + + +# (c) 2020, John Westcott IV <john.westcott.iv@redhat.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: tower_ad_hoc_command +author: "John Westcott IV (@john-westcott-iv)" +version_added: "4.0" +short_description: create, update, or destroy Ansible Tower ad hoc commands. +description: + - Create, update, or destroy Ansible Tower ad hoc commands. See + U(https://www.ansible.com/tower) for an overview. +options: + job_type: + description: + - Job_type to use for the ad hoc command. + type: str + choices: [ 'run', 'check' ] + inventory: + description: + - Inventory to use for the ad hoc command. + required: True + type: str + limit: + description: + - Limit to use for the ad hoc command. + type: str + credential: + description: + - Credential to use for ad hoc command. + required: True + type: str + module_name: + description: + - The Ansible module to execute. + required: True + type: str + module_args: + description: + - The arguments to pass to the module. + type: str + default: "" + forks: + description: + - The number of forks to use for this ad hoc execution. + type: int + verbosity: + description: + - Verbosity level for this ad hoc command run + type: int + choices: [ 0, 1, 2, 3, 4, 5 ] + extra_vars: + description: + - Extra variables to use for the ad hoc command.. + type: dict + become_enabled: + description: + - If the become flag should be set. + type: bool + diff_mode: + description: + - Show the changes made by Ansible tasks where supported + type: bool + wait: + description: + - Wait for the command to complete. + default: False + type: bool + interval: + description: + - The interval to request an update from Tower. + default: 1 + type: float + timeout: + description: + - If waiting for the command to complete this will abort after this + amount of seconds + type: int +extends_documentation_fragment: awx.awx.auth +''' + +EXAMPLES = ''' +''' + +RETURN = ''' +id: + description: id of the newly launched command + returned: success + type: int + sample: 86 +status: + description: status of newly launched command + returned: success + type: str + sample: pending +''' + +from ..module_utils.tower_api import TowerAPIModule + + +def main(): + # Any additional arguments that are not fields of the item can be added here + argument_spec = dict( + job_type=dict(choices=['run', 'check']), + inventory=dict(required=True), + limit=dict(), + credential=dict(required=True), + module_name=dict(required=True), + module_args=dict(default=""), + forks=dict(type='int'), + verbosity=dict(type='int', choices=['0', '1', '2', '3', '4', '5']), + extra_vars=dict(type='dict'), + become_enabled=dict(type='bool'), + diff_mode=dict(type='bool'), + wait=dict(default=False, type='bool'), + interval=dict(default=1.0, type='float'), + timeout=dict(default=None, type='int'), + ) + + # Create a module for ourselves + module = TowerAPIModule(argument_spec=argument_spec) + + # Extract our parameters + inventory = module.params.get('inventory') + credential = module.params.get('credential') + module_name = module.params.get('module_name') + module_args = module.params.get('module_args') + + wait = module.params.get('wait') + interval = module.params.get('interval') + timeout = module.params.get('timeout') + + # Create a datastructure to pass into our command launch + post_data = { + 'module_name': module_name, + 'module_args': module_args, + } + for arg in ['job_type', 'limit', 'forks', 'verbosity', 'extra_vars', 'become_enabled', 'diff_mode']: + if module.params.get(arg): + post_data[arg] = module.params.get(arg) + + # Attempt to look up the related items the user specified (these will fail the module if not found) + post_data['inventory'] = module.resolve_name_to_id('inventories', inventory) + post_data['credential'] = module.resolve_name_to_id('credentials', credential) + + # Launch the ad hoc command + results = module.post_endpoint('ad_hoc_commands', **{'data': post_data}) + + if results['status_code'] != 201: + module.fail_json(msg="Failed to launch command, see response for details", **{'response': results}) + + if not wait: + module.exit_json(**{ + 'changed': True, + 'id': results['json']['id'], + 'status': results['json']['status'], + }) + + # Invoke wait function + results = module.wait_on_url( + url=results['json']['url'], + object_name=module_name, + object_type='Ad Hoc Command', + timeout=timeout, interval=interval + ) + + module.exit_json(**{ + 'changed': True, + 'id': results['json']['id'], + 'status': results['json']['status'], + }) + + +if __name__ == '__main__': + main() diff --git a/awx_collection/plugins/modules/tower_ad_hoc_command_cancel.py b/awx_collection/plugins/modules/tower_ad_hoc_command_cancel.py new file mode 100644 index 0000000000..4e88b22f7a --- /dev/null +++ b/awx_collection/plugins/modules/tower_ad_hoc_command_cancel.py @@ -0,0 +1,130 @@ +#!/usr/bin/python +# coding: utf-8 -*- + +# (c) 2017, Wayne Witzel III <wayne@riotousliving.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: tower_ad_hoc_command_cancel +author: "John Westcott IV (@john-westcott-iv)" +short_description: Cancel an Ansible Tower Ad Hoc Command. +description: + - Cancel Ansible Tower ad hoc command. See + U(https://www.ansible.com/tower) for an overview. +options: + command_id: + description: + - ID of the command to cancel + required: True + type: int + fail_if_not_running: + description: + - Fail loudly if the I(command_id) can not be canceled + default: False + type: bool + interval: + description: + - The interval in seconds, to request an update from Tower. + required: False + default: 1 + type: float + timeout: + description: + - Maximum time in seconds to wait for a job to finish. + - Not specifying means the task will wait until Tower cancels the command. + type: int +extends_documentation_fragment: awx.awx.auth +''' + +EXAMPLES = ''' +- name: Cancel command + tower_ad_hoc_command_cancel: + command_id: command.id +''' + +RETURN = ''' +id: + description: command id requesting to cancel + returned: success + type: int + sample: 94 +''' + + +import time + +from ..module_utils.tower_api import TowerAPIModule + + +def main(): + # Any additional arguments that are not fields of the item can be added here + argument_spec = dict( + command_id=dict(type='int', required=True), + fail_if_not_running=dict(type='bool', default=False), + interval=dict(type='float', default=1.0), + timeout=dict(type='int', default=0), + ) + + # Create a module for ourselves + module = TowerAPIModule(argument_spec=argument_spec) + + # Extract our parameters + command_id = module.params.get('command_id') + fail_if_not_running = module.params.get('fail_if_not_running') + interval = module.params.get('interval') + timeout = module.params.get('timeout') + + # Attempt to look up the command based on the provided name + command = module.get_one('ad_hoc_commands', **{ + 'data': { + 'id': command_id, + } + }) + + if command is None: + module.fail_json(msg="Unable to find command with id {0}".format(command_id)) + + cancel_page = module.get_endpoint(command['related']['cancel']) + if 'json' not in cancel_page or 'can_cancel' not in cancel_page['json']: + module.fail_json(msg="Failed to cancel command, got unexpected response from tower", **{'response': cancel_page}) + + if not cancel_page['json']['can_cancel']: + if fail_if_not_running: + module.fail_json(msg="Ad Hoc Command is not running") + else: + module.exit_json(**{'changed': False}) + + results = module.post_endpoint(command['related']['cancel'], **{'data': {}}) + + if results['status_code'] != 202: + module.fail_json(msg="Failed to cancel command, see response for details", **{'response': results}) + + result = module.get_endpoint(command['related']['cancel']) + start = time.time() + while result['json']['can_cancel']: + # If we are past our time out fail with a message + if timeout and timeout < time.time() - start: + # Account for Legacy messages + module.json_output['msg'] = 'Monitoring of ad hoc command aborted due to timeout' + module.fail_json(**module.json_output) + + # Put the process to sleep for our interval + time.sleep(interval) + + result = module.get_endpoint(command['related']['cancel']) + + module.exit_json(**{'changed': True}) + + +if __name__ == '__main__': + main() diff --git a/awx_collection/plugins/modules/tower_ad_hoc_command_wait.py b/awx_collection/plugins/modules/tower_ad_hoc_command_wait.py new file mode 100644 index 0000000000..7d1cc19418 --- /dev/null +++ b/awx_collection/plugins/modules/tower_ad_hoc_command_wait.py @@ -0,0 +1,127 @@ +#!/usr/bin/python +# coding: utf-8 -*- + +# (c) 2017, Wayne Witzel III <wayne@riotousliving.com> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: tower_ad_hoc_command_wait +author: "John Westcott IV (@john-westcott-iv)" +short_description: Wait for Ansible Tower Ad Hoc Command to finish. +description: + - Wait for Ansible Tower ad hoc command to finish and report success or failure. See + U(https://www.ansible.com/tower) for an overview. +options: + command_id: + description: + - ID of the ad hoc command to monitor. + required: True + type: int + interval: + description: + - The interval in sections, to request an update from Tower. + required: False + default: 1 + type: float + timeout: + description: + - Maximum time in seconds to wait for a ad hoc command to finish. + type: int +extends_documentation_fragment: awx.awx.auth +''' + +EXAMPLES = ''' +- name: Launch an ad hoc command + tower_ad_hoc_command: + inventory: "Demo Inventory" + credential: "Demo Credential" + wait: false + register: command + +- name: Wait for ad joc command max 120s + tower_ad_hoc_command_wait: + command_id: "{{ command.id }}" + timeout: 120 +''' + +RETURN = ''' +id: + description: Ad hoc command id that is being waited on + returned: success + type: int + sample: 99 +elapsed: + description: total time in seconds the command took to run + returned: success + type: float + sample: 10.879 +started: + description: timestamp of when the command started running + returned: success + type: str + sample: "2017-03-01T17:03:53.200234Z" +finished: + description: timestamp of when the command finished running + returned: success + type: str + sample: "2017-03-01T17:04:04.078782Z" +status: + description: current status of command + returned: success + type: str + sample: successful +''' + + +from ..module_utils.tower_api import TowerAPIModule + + +def main(): + # Any additional arguments that are not fields of the item can be added here + argument_spec = dict( + command_id=dict(type='int', required=True), + timeout=dict(type='int'), + interval=dict(type='float', default=1), + ) + + # Create a module for ourselves + module = TowerAPIModule(argument_spec=argument_spec) + + # Extract our parameters + command_id = module.params.get('command_id') + timeout = module.params.get('timeout') + interval = module.params.get('interval') + + # Attempt to look up command based on the provided id + command = module.get_one('ad_hoc_commands', **{ + 'data': { + 'id': command_id, + } + }) + + if command is None: + module.fail_json(msg='Unable to wait on ad hoc command {0}; that ID does not exist in Tower.'.format(command_id)) + + # Invoke wait function + module.wait_on_url( + url=command['url'], + object_name=command_id, + object_type='ad hoc command', + timeout=timeout, interval=interval + ) + + module.exit_json(**module.json_output) + + +if __name__ == '__main__': + main() diff --git a/awx_collection/test/awx/test_ad_hoc_wait.py b/awx_collection/test/awx/test_ad_hoc_wait.py new file mode 100644 index 0000000000..976e3d0e80 --- /dev/null +++ b/awx_collection/test/awx/test_ad_hoc_wait.py @@ -0,0 +1,55 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest +from django.utils.timezone import now + +from awx.main.models.ad_hoc_commands import AdHocCommand + + +@pytest.mark.django_db +def test_ad_hoc_command_wait_successful(run_module, admin_user): + command = AdHocCommand.objects.create(status='successful', started=now(), finished=now()) + result = run_module('tower_ad_hoc_command_wait', dict( + command_id=command.id + ), admin_user) + result.pop('invocation', None) + assert result.pop('finished', '')[:10] == str(command.finished)[:10] + assert result.pop('started', '')[:10] == str(command.started)[:10] + assert result == { + "status": "successful", + "changed": False, + "elapsed": str(command.elapsed), + "id": command.id + } + + +@pytest.mark.django_db +def test_ad_hoc_command_wait_failed(run_module, admin_user): + command = AdHocCommand.objects.create(status='failed', started=now(), finished=now()) + result = run_module('tower_ad_hoc_command_wait', dict( + command_id=command.id + ), admin_user) + result.pop('invocation', None) + assert result.pop('finished', '')[:10] == str(command.finished)[:10] + assert result.pop('started', '')[:10] == str(command.started)[:10] + assert result == { + "status": "failed", + "failed": True, + "changed": False, + "elapsed": str(command.elapsed), + "id": command.id, + "msg": "The ad hoc command - 1, failed" + } + + +@pytest.mark.django_db +def test_ad_hoc_command_wait_not_found(run_module, admin_user): + result = run_module('tower_ad_hoc_command_wait', dict( + command_id=42 + ), admin_user) + result.pop('invocation', None) + assert result == { + "failed": True, + "msg": "Unable to wait on ad hoc command 42; that ID does not exist in Tower." + } diff --git a/awx_collection/test/awx/test_completeness.py b/awx_collection/test/awx/test_completeness.py index bf8e4f835a..3626c42239 100644 --- a/awx_collection/test/awx/test_completeness.py +++ b/awx_collection/test/awx/test_completeness.py @@ -25,7 +25,7 @@ no_module_for_endpoint = [] no_endpoint_for_module = [ 'tower_import', 'tower_meta', 'tower_export', 'tower_inventory_source_update', 'tower_job_launch', 'tower_job_wait', 'tower_job_list', 'tower_license', 'tower_ping', 'tower_receive', 'tower_send', 'tower_workflow_launch', - 'tower_job_cancel', 'tower_workflow_template', + 'tower_job_cancel', 'tower_workflow_template', 'tower_ad_hoc_command_wait', 'tower_ad_hoc_command_cancel', ] # Global module parameters we can ignore @@ -48,13 +48,15 @@ no_api_parameter_ok = { 'tower_workflow_job_template_node': ['organization'], # Survey is how we handle associations 'tower_workflow_job_template': ['survey'], + # ad hoc commands support interval and timeout since its more like tower_job_launc + 'tower_ad_hoc_command': ['interval', 'timeout', 'wait'], } # When this tool was created we were not feature complete. Adding something in here indicates a module # that needs to be developed. If the module is found on the file system it will auto-detect that the # work is being done and will bypass this check. At some point this module should be removed from this list. needs_development = [ - 'tower_ad_hoc_command', 'tower_inventory_script', 'tower_workflow_approval' + 'tower_inventory_script', 'tower_workflow_approval' ] needs_param_development = { 'tower_host': ['instance_id'], diff --git a/awx_collection/tests/integration/targets/tower_ad_hoc_command/tasks/main.yml b/awx_collection/tests/integration/targets/tower_ad_hoc_command/tasks/main.yml new file mode 100644 index 0000000000..1c45ea6b1e --- /dev/null +++ b/awx_collection/tests/integration/targets/tower_ad_hoc_command/tasks/main.yml @@ -0,0 +1,81 @@ +--- +- name: Generate a random string for test + set_fact: + test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + when: test_id is not defined + +- name: Generate names + set_fact: + inv_name: "AWX-Collection-tests-tower_tower_ad_hoc_command-inventory-{{ test_id }}" + ssh_cred_name: "AWX-Collection-tests-tower_tower_ad_hoc_command-ssh-cred-{{ test_id }}" + org_name: "AWX-Collection-tests-tower_tower_ad_hoc_command-org-{{ test_id }}" + +- name: Create a New Organization + tower_organization: + name: "{{ org_name }}" + +- name: Create an Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: "{{ org_name }}" + state: present + +- name: Add localhost to the Inventory + tower_host: + name: localhost + inventory: "{{ inv_name }}" + variables: + ansible_connection: local + +- name: Create a Credential + tower_credential: + name: "{{ ssh_cred_name }}" + organization: "{{ org_name }}" + credential_type: 'Machine' + state: present + +- name: Launch an Ad Hoc Command waiting for it to finish + tower_ad_hoc_command: + inventory: "{{ inv_name }}" + credential: "{{ ssh_cred_name }}" + module_name: "command" + module_args: "echo I <3 Ansible" + wait: true + register: result + +- assert: + that: + - "result is changed" + - "result.status == 'successful'" + +- name: Check module fails with correct msg + tower_ad_hoc_command: + inventory: "{{ inv_name }}" + credential: "{{ ssh_cred_name }}" + module_name: "Does not exist" + register: result + ignore_errors: true + +- assert: + that: + - "result is failed" + - "result is not changed" + - "'Does not exist' in result.response['json']['module_name'][0]" + +- name: Delete the Credential + tower_credential: + name: "{{ ssh_cred_name }}" + organization: "{{ org_name }}" + credential_type: 'Machine' + state: absent + +- name: Delete the Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: "{{ org_name }}" + state: absent + +- name: Remove the Organization + tower_organization: + name: "{{ org_name }}" + state: absent diff --git a/awx_collection/tests/integration/targets/tower_ad_hoc_command_cancel/tasks/main.yml b/awx_collection/tests/integration/targets/tower_ad_hoc_command_cancel/tasks/main.yml new file mode 100644 index 0000000000..20f11124fe --- /dev/null +++ b/awx_collection/tests/integration/targets/tower_ad_hoc_command_cancel/tasks/main.yml @@ -0,0 +1,108 @@ +--- +- name: Generate a random string for test + set_fact: + test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + when: test_id is not defined + +- name: Generate names + set_fact: + inv_name: "AWX-Collection-tests-tower_tower_ad_hoc_command_cancel-inventory-{{ test_id }}" + ssh_cred_name: "AWX-Collection-tests-tower_tower_ad_hoc_command_cancel-ssh-cred-{{ test_id }}" + org_name: "AWX-Collection-tests-tower_tower_ad_hoc_command_cancel-org-{{ test_id }}" + +- name: Create a New Organization + tower_organization: + name: "{{ org_name }}" + +- name: Create an Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: "{{ org_name }}" + state: present + +- name: Add localhost to the Inventory + tower_host: + name: localhost + inventory: "{{ inv_name }}" + variables: + ansible_connection: local + +- name: Create a Credential + tower_credential: + name: "{{ ssh_cred_name }}" + organization: "{{ org_name }}" + credential_type: 'Machine' + state: present + +- name: Launch an Ad Hoc Command + tower_ad_hoc_command: + inventory: "{{ inv_name }}" + credential: "{{ ssh_cred_name }}" + module_name: "command" + module_args: "sleep 100" + register: command + +- assert: + that: + - "command is changed" + +- name: Timeout waiting for the command to cancel + tower_ad_hoc_command_cancel: + command_id: "{{ command.id }}" + timeout: -1 + register: results + ignore_errors: true + +- assert: + that: + - results is failed + - "results['msg'] == 'Monitoring of ad hoc command aborted due to timeout'" + +- name: Cancel the command with hard error if it's not running + tower_ad_hoc_command_cancel: + command_id: "{{ command.id }}" + fail_if_not_running: true + register: results + +- assert: + that: + - results is changed + +- name: Cancel an already canceled command (assert failure) + tower_ad_hoc_command_cancel: + command_id: "{{ command.id }}" + fail_if_not_running: true + register: results + ignore_errors: true + +- assert: + that: + - results is failed + +- name: Check module fails with correct msg + tower_ad_hoc_command_cancel: + command_id: 9999999999 + register: result + ignore_errors: true + +- assert: + that: + - "result.msg == 'Unable to find command with id 9999999999'" + +- name: Delete the Credential + tower_credential: + name: "{{ ssh_cred_name }}" + organization: "{{ org_name }}" + credential_type: 'Machine' + state: absent + +- name: Delete the Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: "{{ org_name }}" + state: absent + +- name: Remove the Organization + tower_organization: + name: "{{ org_name }}" + state: absent diff --git a/awx_collection/tests/integration/targets/tower_ad_hoc_command_wait/tasks/main.yml b/awx_collection/tests/integration/targets/tower_ad_hoc_command_wait/tasks/main.yml new file mode 100644 index 0000000000..2f30de529d --- /dev/null +++ b/awx_collection/tests/integration/targets/tower_ad_hoc_command_wait/tasks/main.yml @@ -0,0 +1,131 @@ +--- +- name: Generate a random string for test + set_fact: + test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + when: test_id is not defined + +- name: Generate names + set_fact: + inv_name: "AWX-Collection-tests-tower_ad_hoc_command_wait-inventory-{{ test_id }}" + ssh_cred_name: "AWX-Collection-tests-tower_ad_hoc_command_wait-ssh-cred-{{ test_id }}" + org_name: "AWX-Collection-tests-tower_ad_hoc_command_wait-org-{{ test_id }}" + +- name: Create a New Organization + tower_organization: + name: "{{ org_name }}" + +- name: Create an Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: "{{ org_name }}" + state: present + +- name: Add localhost to the Inventory + tower_host: + name: localhost + inventory: "{{ inv_name }}" + variables: + ansible_connection: local + +- name: Create a Credential + tower_credential: + name: "{{ ssh_cred_name }}" + organization: "{{ org_name }}" + credential_type: 'Machine' + state: present + +- name: Check module fails with correct msg + tower_ad_hoc_command_wait: + command_id: "99999999" + register: result + ignore_errors: true + +- assert: + that: + - result is failed + - "result.msg == 'Unable to wait on ad hoc command 99999999; that ID does not exist in Tower.'" + +- name: Launch command module with sleep 10 + tower_ad_hoc_command: + inventory: "{{ inv_name }}" + credential: "{{ ssh_cred_name }}" + module_name: "command" + module_args: "sleep 5" + register: command + +- assert: + that: + - command is changed + +- name: Wait for the Job to finish + tower_ad_hoc_command_wait: + command_id: "{{ command.id }}" + register: wait_results + +# Make sure it worked and that we have some data in our results +- assert: + that: + - wait_results is successful + - "'elapsed' in wait_results" + - "'id' in wait_results" + +- name: Launch a long running command + tower_ad_hoc_command: + inventory: "{{ inv_name }}" + credential: "{{ ssh_cred_name }}" + module_name: "command" + module_args: "sleep 10000" + register: command + +- assert: + that: + - command is changed + +- name: Timeout waiting for the command to complete + tower_ad_hoc_command_wait: + command_id: "{{ command.id }}" + timeout: 1 + ignore_errors: true + register: wait_results + +# Make sure that we failed and that we have some data in our results +- assert: + that: + - "'Monitoring aborted due to timeout' or 'Timeout waiting for command to finish.' in wait_results.msg" + - "'id' in wait_results" + +- name: Async cancel the long-running command + tower_ad_hoc_command_cancel: + command_id: "{{ command.id }}" + async: 3600 + poll: 0 + +- name: Wait for the command to exit on cancel + tower_ad_hoc_command_wait: + command_id: "{{ command.id }}" + register: wait_results + ignore_errors: true + +- assert: + that: + - wait_results is failed + - 'wait_results.status == "canceled"' + - "wait_results.msg == 'The ad hoc command - {{ command.id }}, failed'" + +- name: Delete the Credential + tower_credential: + name: "{{ ssh_cred_name }}" + organization: "{{ org_name }}" + credential_type: 'Machine' + state: absent + +- name: Delete the Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: "{{ org_name }}" + state: absent + +- name: Remove the Organization + tower_organization: + name: "{{ org_name }}" + state: absent diff --git a/awx_collection/tools/vars/resolution.yml b/awx_collection/tools/vars/resolution.yml index fd6ec096cb..2beda0ff23 100644 --- a/awx_collection/tools/vars/resolution.yml +++ b/awx_collection/tools/vars/resolution.yml @@ -4,3 +4,4 @@ name_to_id_endpoint_resolution: project: projects inventory: inventories organization: organizations + credential: credentials |