From 72dbd10c2ab8417517bb394a11a6071028e1926b Mon Sep 17 00:00:00 2001 From: beeankha Date: Thu, 27 Aug 2020 17:28:31 -0400 Subject: [PATCH 1/8] Initial commit for new inventory sync module --- .../modules/tower_inventory_source_update.py | 100 ++++++++++++++++++ awx_collection/test/awx/test_completeness.py | 6 +- .../tasks/main.yml | 80 ++++++++++++++ 3 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 awx_collection/plugins/modules/tower_inventory_source_update.py create mode 100644 awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml diff --git a/awx_collection/plugins/modules/tower_inventory_source_update.py b/awx_collection/plugins/modules/tower_inventory_source_update.py new file mode 100644 index 0000000000..818011a0a3 --- /dev/null +++ b/awx_collection/plugins/modules/tower_inventory_source_update.py @@ -0,0 +1,100 @@ +#!/usr/bin/python +# coding: utf-8 -*- + +# (c) 2020, Bianca Henderson +# 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_inventory_source_update +author: "Bianca Henderson (@beeankha)" +short_description: Update inventory source(s). +description: + - Update Ansible Tower inventory source(s). See + U(https://www.ansible.com/tower) for an overview. +options: + inventory: + description: + - Name of the inventory that contains the inventory source(s) to update. + required: True + type: str + inventory_source: + description: + - The name of the inventory source to update. + required: True + type: str +extends_documentation_fragment: awx.awx.auth +''' + +EXAMPLES = ''' +- name: Update a single inventory source + tower_inventory_source_update: + inventory: "My Inventory" + inventory_source: "Example Inventory Source" + +- name: Update all inventory sources + tower_inventory_source_update: + inventory: "My Other Inventory" + inventory_source: "{{ item }}" + loop: "{{ query('awx.awx.tower_api', 'inventory_sources', query_params={ 'inventory': 30 }, return_ids=True ) }}" +''' + +RETURN = ''' +id: + description: id of the inventory update + returned: success + type: int + sample: 86 +status: + description: status of the inventory update + 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( + inventory=dict(required=True), + inventory_source=dict(required=True), + ) + + # Create a module for ourselves + module = TowerAPIModule(argument_spec=argument_spec) + + # Extract our parameters + inventory = module.params.get('inventory') + inventory_source = module.params.get('inventory_source') + + # Attempt to look up the inventory the user specified (these will fail the module if not found) + inventory_object = module.get_one_by_name_or_id('inventories', inventory) + # Return all inventory sources related to the specified inventory + inventory_source_object = module.get_one_by_name_or_id(inventory_object['related']['inventory_sources'], inventory_source) + + # Sync the inventory source(s) + inventory_source_update_results = module.post_endpoint(inventory_source_object['related']['update'], **{'data': {}}) + + if inventory_source_update_results['status_code'] != 202: + module.fail_json(msg="Failed to update inventory source, see response for details", **{'response': inventory_source_update_results}) + + module.exit_json(**{ + 'changed': True, + 'id': inventory_source_update_results['json']['id'], + 'status': inventory_source_update_results['json']['status'], + }) + + +if __name__ == '__main__': + main() diff --git a/awx_collection/test/awx/test_completeness.py b/awx_collection/test/awx/test_completeness.py index 2f1fc80d4d..f656db63b3 100644 --- a/awx_collection/test/awx/test_completeness.py +++ b/awx_collection/test/awx/test_completeness.py @@ -23,9 +23,9 @@ no_module_for_endpoint = [] # Some modules work on the related fields of an endpoint. These modules will not have an auto-associated endpoint no_endpoint_for_module = [ - 'tower_import', 'tower_meta', 'tower_export', '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_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', ] # Global module parameters we can ignore diff --git a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml new file mode 100644 index 0000000000..9f32195936 --- /dev/null +++ b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml @@ -0,0 +1,80 @@ +--- +- name: Generate a test ID + set_fact: + test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + +- name: Generate names + set_fact: + project_name: "AWX-Collection-tests-tower_inventory_source_update-project-{{ test_id }}" + inv_name: "AWX-Collection-tests-tower_inventory_source_update-inv-{{ test_id }}" + inv_source1: "AWX-Collection-tests-tower_inventory_source_update-source1-{{ test_id }}" + inv_source2: "AWX-Collection-tests-tower_inventory_source_update-source2-{{ test_id }}" + + +- block: + - name: Create a git project without credentials + tower_project: + name: "{{ project_name }}" + organization: Default + scm_type: git + scm_url: https://github.com/ansible/test-playbooks + wait: true + + - name: Create an Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: Default + state: present + register: created_inventory + + - name: Create an Inventory Source + tower_inventory_source: + name: "{{ inv_source1 }}" + source: scm + source_project: "{{ project_name }}" + source_path: inventories/inventory.ini + description: Source for Test inventory + inventory: "{{ inv_name }}" + + - name: Create Another Inventory Source + tower_inventory_source: + name: "{{ inv_source2 }}" + source: scm + source_project: "{{ project_name }}" + source_path: inventories/create_10_hosts.ini + description: Source for Test inventory + inventory: "{{ inv_name }}" + + - name: Test Inventory Source Update + tower_inventory_source_update: + inventory: "{{ inv_name }}" + inventory_source: "{{ inv_source1 }}" + register: result + + - assert: + that: + - "result is changed" + + - name: Test Inventory Source Update for All Sources + tower_inventory_source_update: + inventory: "{{ inv_name }}" + inventory_source: "{{ item }}" + loop: "{{ query('awx.awx.tower_api', 'inventory_sources', query_params={ 'inventory': created_inventory.id }, return_ids=True ) }}" + register: result + + - assert: + that: + - "result is changed" + + always: + - name: Delete Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: Default + state: absent + + - name: Delete Project + tower_project: + name: "{{ project_name }}" + organization: Default + state: absent From 65e0ed8c77287bb2b199e40ea56a53a15f944426 Mon Sep 17 00:00:00 2001 From: beeankha Date: Thu, 27 Aug 2020 19:07:18 -0400 Subject: [PATCH 2/8] Fix linter error --- .../tasks/main.yml | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml index 9f32195936..2a5fb76d6b 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml @@ -12,59 +12,59 @@ - block: - - name: Create a git project without credentials - tower_project: - name: "{{ project_name }}" - organization: Default - scm_type: git - scm_url: https://github.com/ansible/test-playbooks - wait: true + - name: Create a git project without credentials + tower_project: + name: "{{ project_name }}" + organization: Default + scm_type: git + scm_url: https://github.com/ansible/test-playbooks + wait: true - - name: Create an Inventory - tower_inventory: - name: "{{ inv_name }}" - organization: Default - state: present - register: created_inventory + - name: Create an Inventory + tower_inventory: + name: "{{ inv_name }}" + organization: Default + state: present + register: created_inventory - - name: Create an Inventory Source - tower_inventory_source: - name: "{{ inv_source1 }}" - source: scm - source_project: "{{ project_name }}" - source_path: inventories/inventory.ini - description: Source for Test inventory - inventory: "{{ inv_name }}" + - name: Create an Inventory Source + tower_inventory_source: + name: "{{ inv_source1 }}" + source: scm + source_project: "{{ project_name }}" + source_path: inventories/inventory.ini + description: Source for Test inventory + inventory: "{{ inv_name }}" - - name: Create Another Inventory Source - tower_inventory_source: - name: "{{ inv_source2 }}" - source: scm - source_project: "{{ project_name }}" - source_path: inventories/create_10_hosts.ini - description: Source for Test inventory - inventory: "{{ inv_name }}" + - name: Create Another Inventory Source + tower_inventory_source: + name: "{{ inv_source2 }}" + source: scm + source_project: "{{ project_name }}" + source_path: inventories/create_10_hosts.ini + description: Source for Test inventory + inventory: "{{ inv_name }}" - - name: Test Inventory Source Update - tower_inventory_source_update: - inventory: "{{ inv_name }}" - inventory_source: "{{ inv_source1 }}" - register: result + - name: Test Inventory Source Update + tower_inventory_source_update: + inventory: "{{ inv_name }}" + inventory_source: "{{ inv_source1 }}" + register: result - - assert: - that: - - "result is changed" + - assert: + that: + - "result is changed" - - name: Test Inventory Source Update for All Sources - tower_inventory_source_update: - inventory: "{{ inv_name }}" - inventory_source: "{{ item }}" - loop: "{{ query('awx.awx.tower_api', 'inventory_sources', query_params={ 'inventory': created_inventory.id }, return_ids=True ) }}" - register: result + - name: Test Inventory Source Update for All Sources + tower_inventory_source_update: + inventory: "{{ inv_name }}" + inventory_source: "{{ item }}" + loop: "{{ query('awx.awx.tower_api', 'inventory_sources', query_params={ 'inventory': created_inventory.id }, return_ids=True ) }}" + register: result - - assert: - that: - - "result is changed" + - assert: + that: + - "result is changed" always: - name: Delete Inventory From 1ce5d7d539af59f50fc38332b533b641ae63f8da Mon Sep 17 00:00:00 2001 From: beeankha Date: Tue, 1 Sep 2020 11:35:37 -0400 Subject: [PATCH 3/8] Add ability to look up inventory sources by org name/ID --- .../plugins/modules/tower_inventory_source.py | 15 ++++++++++ .../modules/tower_inventory_source_update.py | 13 +++++++++ awx_collection/test/awx/test_completeness.py | 3 +- .../tasks/main.yml | 29 +++++++++++++++++-- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/awx_collection/plugins/modules/tower_inventory_source.py b/awx_collection/plugins/modules/tower_inventory_source.py index 4755670291..bf52996211 100644 --- a/awx_collection/plugins/modules/tower_inventory_source.py +++ b/awx_collection/plugins/modules/tower_inventory_source.py @@ -128,6 +128,10 @@ options: - list of notifications to send on error type: list elements: str + organization: + description: + - Name of organization for project. + type: str extends_documentation_fragment: awx.awx.auth ''' @@ -140,6 +144,7 @@ EXAMPLES = ''' credential: previously-created-credential overwrite: True update_on_launch: True + organization: Default source_vars: private: false ''' @@ -168,6 +173,10 @@ def main(): enabled_value=dict(), host_filter=dict(), credential=dict(), + source_regions=dict(), + instance_filters=dict(), + group_by=dict(), + organization=dict(), overwrite=dict(type='bool'), overwrite_vars=dict(type='bool'), custom_virtualenv=dict(), @@ -195,6 +204,12 @@ def main(): source_project = module.params.get('source_project') state = module.params.get('state') + new_fields = {} + organization_id = None + organization = module.params.get('organization') + if organization: + organization_id = module.get_one_by_name_or_id('organizations', organization) + # Attempt to look up inventory source based on the provided name and inventory ID inventory_id = module.resolve_name_to_id('inventories', inventory) inventory_source = module.get_one('inventory_sources', **{ diff --git a/awx_collection/plugins/modules/tower_inventory_source_update.py b/awx_collection/plugins/modules/tower_inventory_source_update.py index 818011a0a3..f38e3b6039 100644 --- a/awx_collection/plugins/modules/tower_inventory_source_update.py +++ b/awx_collection/plugins/modules/tower_inventory_source_update.py @@ -32,6 +32,11 @@ options: - The name of the inventory source to update. required: True type: str + organization: + description: + - Name of organization for project. + type: str + required: False extends_documentation_fragment: awx.awx.auth ''' @@ -40,6 +45,7 @@ EXAMPLES = ''' tower_inventory_source_update: inventory: "My Inventory" inventory_source: "Example Inventory Source" + organization: Default - name: Update all inventory sources tower_inventory_source_update: @@ -69,6 +75,7 @@ def main(): argument_spec = dict( inventory=dict(required=True), inventory_source=dict(required=True), + organization=dict(), ) # Create a module for ourselves @@ -78,6 +85,12 @@ def main(): inventory = module.params.get('inventory') inventory_source = module.params.get('inventory_source') + new_fields = {} + organization_id = None + organization = module.params.get('organization') + if organization: + organization_id = module.get_one_by_name_or_id('organizations', organization) + # Attempt to look up the inventory the user specified (these will fail the module if not found) inventory_object = module.get_one_by_name_or_id('inventories', inventory) # Return all inventory sources related to the specified inventory diff --git a/awx_collection/test/awx/test_completeness.py b/awx_collection/test/awx/test_completeness.py index f656db63b3..ab8c1547f8 100644 --- a/awx_collection/test/awx/test_completeness.py +++ b/awx_collection/test/awx/test_completeness.py @@ -43,7 +43,8 @@ no_api_parameter_ok = { # /survey spec is now how we handle associations # We take an organization here to help with the lookups only 'tower_job_template': ['survey_spec', 'organization'], - # Organization is how we looking job templates + 'tower_inventory_source': ['organization'], + # Organization is how we are looking up job templates 'tower_workflow_job_template_node': ['organization'], # Survey is how we handle associations 'tower_workflow_job_template': ['survey'], diff --git a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml index 2a5fb76d6b..7207541838 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml @@ -9,13 +9,20 @@ inv_name: "AWX-Collection-tests-tower_inventory_source_update-inv-{{ test_id }}" inv_source1: "AWX-Collection-tests-tower_inventory_source_update-source1-{{ test_id }}" inv_source2: "AWX-Collection-tests-tower_inventory_source_update-source2-{{ test_id }}" + org_name: "AWX-Collection-tests-tower_inventory_source_update-org-{{ test_id }}" - block: + + - name: "Create a new organization" + tower_organization: + name: "{{ org_name }}" + register: created_org + - name: Create a git project without credentials tower_project: name: "{{ project_name }}" - organization: Default + organization: "{{ org_name }}" scm_type: git scm_url: https://github.com/ansible/test-playbooks wait: true @@ -23,7 +30,14 @@ - name: Create an Inventory tower_inventory: name: "{{ inv_name }}" - organization: Default + organization: "{{ org_name }}" + state: present + register: created_inventory + + - name: Create another inventory w/ same name + tower_inventory: + name: "{{ inv_name }}" + organization: "{{ org_name }}" state: present register: created_inventory @@ -34,21 +48,24 @@ source_project: "{{ project_name }}" source_path: inventories/inventory.ini description: Source for Test inventory + organization: "{{ created_org.id }}" inventory: "{{ inv_name }}" - - name: Create Another Inventory Source + - name: Create Another Inventory Source (for testing org-based lookup) tower_inventory_source: name: "{{ inv_source2 }}" source: scm source_project: "{{ project_name }}" source_path: inventories/create_10_hosts.ini description: Source for Test inventory + organization: Default inventory: "{{ inv_name }}" - name: Test Inventory Source Update tower_inventory_source_update: inventory: "{{ inv_name }}" inventory_source: "{{ inv_source1 }}" + organization: Default register: result - assert: @@ -59,6 +76,7 @@ tower_inventory_source_update: inventory: "{{ inv_name }}" inventory_source: "{{ item }}" + organization: "{{ created_org.id }}" loop: "{{ query('awx.awx.tower_api', 'inventory_sources', query_params={ 'inventory': created_inventory.id }, return_ids=True ) }}" register: result @@ -78,3 +96,8 @@ name: "{{ project_name }}" organization: Default state: absent + + - name: "Remove the organization" + tower_organization: + name: "{{ org_name }}" + state: absent From b78dea3e4b369d0843e473472b04d1c979c169ce Mon Sep 17 00:00:00 2001 From: beeankha Date: Tue, 1 Sep 2020 16:56:29 -0400 Subject: [PATCH 4/8] Add wait ability to the module --- .../plugins/modules/tower_inventory_source.py | 5 +-- .../modules/tower_inventory_source_update.py | 31 ++++++++++++++++++- .../tasks/main.yml | 1 + 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/awx_collection/plugins/modules/tower_inventory_source.py b/awx_collection/plugins/modules/tower_inventory_source.py index bf52996211..eb316fd9ca 100644 --- a/awx_collection/plugins/modules/tower_inventory_source.py +++ b/awx_collection/plugins/modules/tower_inventory_source.py @@ -130,7 +130,7 @@ options: elements: str organization: description: - - Name of organization for project. + - Name of the inventory source's inventory's organization. type: str extends_documentation_fragment: awx.awx.auth ''' @@ -173,9 +173,6 @@ def main(): enabled_value=dict(), host_filter=dict(), credential=dict(), - source_regions=dict(), - instance_filters=dict(), - group_by=dict(), organization=dict(), overwrite=dict(type='bool'), overwrite_vars=dict(type='bool'), diff --git a/awx_collection/plugins/modules/tower_inventory_source_update.py b/awx_collection/plugins/modules/tower_inventory_source_update.py index f38e3b6039..c8ac301ef3 100644 --- a/awx_collection/plugins/modules/tower_inventory_source_update.py +++ b/awx_collection/plugins/modules/tower_inventory_source_update.py @@ -34,9 +34,25 @@ options: type: str organization: description: - - Name of organization for project. + - Name of the inventory source's inventory's organization. type: str required: False + wait: + description: + - Wait for the job to complete. + default: False + type: bool + interval: + description: + - The interval to request an update from Tower. + required: False + default: 1 + type: float + timeout: + description: + - If waiting for the job to complete this will abort after this + amount of seconds + type: int extends_documentation_fragment: awx.awx.auth ''' @@ -76,6 +92,9 @@ def main(): inventory=dict(required=True), inventory_source=dict(required=True), organization=dict(), + wait=dict(default=False, type='bool'), + interval=dict(default=1.0, type='float'), + timeout=dict(default=None, type='int'), ) # Create a module for ourselves @@ -84,6 +103,9 @@ def main(): # Extract our parameters inventory = module.params.get('inventory') inventory_source = module.params.get('inventory_source') + wait = module.params.get('wait') + interval = module.params.get('interval') + timeout = module.params.get('timeout') new_fields = {} organization_id = None @@ -102,6 +124,13 @@ def main(): if inventory_source_update_results['status_code'] != 202: module.fail_json(msg="Failed to update inventory source, see response for details", **{'response': inventory_source_update_results}) + inventory_source_update_results = module.wait_on_url( + url=inventory_source_update_results['json']['url'], + object_name=inventory_object, + object_type='inventory_update', + timeout=timeout, interval=interval + ) + module.exit_json(**{ 'changed': True, 'id': inventory_source_update_results['json']['id'], diff --git a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml index 7207541838..fd6cd48f2f 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml @@ -77,6 +77,7 @@ inventory: "{{ inv_name }}" inventory_source: "{{ item }}" organization: "{{ created_org.id }}" + wait: true loop: "{{ query('awx.awx.tower_api', 'inventory_sources', query_params={ 'inventory': created_inventory.id }, return_ids=True ) }}" register: result From 7764f1c1a53cf0efc17adb04744125f1f773c6e4 Mon Sep 17 00:00:00 2001 From: beeankha Date: Wed, 2 Sep 2020 22:05:25 -0400 Subject: [PATCH 5/8] Fix inv source org lookup, adjust unit tests (since org is now required for inv source module), update README, edit typos in test-related README --- awx/main/tests/factories/README.md | 4 +- .../plugins/modules/tower_inventory_source.py | 36 +++++++++------ .../modules/tower_inventory_source_update.py | 46 ++++++++++++------- .../test/awx/test_inventory_source.py | 40 ++++++---------- .../tower_inventory_source/tasks/main.yml | 2 + .../tasks/main.yml | 13 +++--- .../template_galaxy/templates/README.md.j2 | 1 + 7 files changed, 75 insertions(+), 67 deletions(-) diff --git a/awx/main/tests/factories/README.md b/awx/main/tests/factories/README.md index c451c02598..916c996cfa 100644 --- a/awx/main/tests/factories/README.md +++ b/awx/main/tests/factories/README.md @@ -52,11 +52,11 @@ patterns -------- `mk` functions are single object fixtures. They should create only a single object with the minimum deps. -They should also accept a `persited` flag, if they must be persisted to work, they raise an error if persisted=False +They should also accept a `persisted` flag, if they must be persisted to work, they raise an error if persisted=False `generate` and `apply` functions are helpers that build up the various parts of a `create` functions objects. These should be useful for more than one create function to use and should explicitly accept all of the values needed -to execute. These functions should also be robust and have very speciifc error reporting about constraints and/or +to execute. These functions should also be robust and have very specific error reporting about constraints and/or bad values. `create` functions compose many of the `mk` and `generate` functions to make different object diff --git a/awx_collection/plugins/modules/tower_inventory_source.py b/awx_collection/plugins/modules/tower_inventory_source.py index eb316fd9ca..7c7c506826 100644 --- a/awx_collection/plugins/modules/tower_inventory_source.py +++ b/awx_collection/plugins/modules/tower_inventory_source.py @@ -131,6 +131,7 @@ options: organization: description: - Name of the inventory source's inventory's organization. + required: True type: str extends_documentation_fragment: awx.awx.auth ''' @@ -173,7 +174,7 @@ def main(): enabled_value=dict(), host_filter=dict(), credential=dict(), - organization=dict(), + organization=dict(required=True), overwrite=dict(type='bool'), overwrite_vars=dict(type='bool'), custom_virtualenv=dict(), @@ -196,29 +197,34 @@ def main(): name = module.params.get('name') new_name = module.params.get('new_name') inventory = module.params.get('inventory') + organization = module.params.get('organization') source_script = module.params.get('source_script') credential = module.params.get('credential') source_project = module.params.get('source_project') state = module.params.get('state') - new_fields = {} - organization_id = None - organization = module.params.get('organization') - if organization: - organization_id = module.get_one_by_name_or_id('organizations', organization) - # Attempt to look up inventory source based on the provided name and inventory ID - inventory_id = module.resolve_name_to_id('inventories', inventory) - inventory_source = module.get_one('inventory_sources', **{ + org_id = module.resolve_name_to_id('organizations', organization) + inventory_object = module.get_one('inventories', **{ + 'data': { + 'name': inventory, + 'organization': org_id, + } + }) + + if not inventory_object: + module.fail_json(msg='The specified inventory was not found.') + + inventory_source_object = module.get_one('inventory_sources', **{ 'data': { 'name': name, - 'inventory': inventory_id, + 'inventory': inventory_object['id'], } }) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(inventory_source) + module.delete_if_needed(inventory_source_object) # Attempt to look up associated field items the user specified. association_fields = {} @@ -244,7 +250,7 @@ def main(): # Create the data that gets sent for create and update inventory_source_fields = { 'name': new_name if new_name else name, - 'inventory': inventory_id, + 'inventory': inventory_object['id'], } # Attempt to look up the related items the user specified (these will fail the module if not found) @@ -273,12 +279,12 @@ def main(): inventory_source_fields['source_vars'] = dumps(inventory_source_fields['source_vars']) # Sanity check on arguments - if state == 'present' and not inventory_source and not inventory_source_fields['source']: + if state == 'present' and not inventory_source_object and not inventory_source_fields['source']: module.fail_json(msg="If creating a new inventory source, the source param must be present") - # If the state was present we can let the module build or update the existing inventory_source, this will return on its own + # If the state was present we can let the module build or update the existing inventory_source_object, this will return on its own module.create_or_update_if_needed( - inventory_source, inventory_source_fields, + inventory_source_object, inventory_source_fields, endpoint='inventory_sources', item_type='inventory source', associations=association_fields ) diff --git a/awx_collection/plugins/modules/tower_inventory_source_update.py b/awx_collection/plugins/modules/tower_inventory_source_update.py index c8ac301ef3..6f9cdf6de1 100644 --- a/awx_collection/plugins/modules/tower_inventory_source_update.py +++ b/awx_collection/plugins/modules/tower_inventory_source_update.py @@ -36,7 +36,7 @@ options: description: - Name of the inventory source's inventory's organization. type: str - required: False + required: True wait: description: - Wait for the job to complete. @@ -91,7 +91,7 @@ def main(): argument_spec = dict( inventory=dict(required=True), inventory_source=dict(required=True), - organization=dict(), + organization=dict(required=True), wait=dict(default=False, type='bool'), interval=dict(default=1.0, type='float'), timeout=dict(default=None, type='int'), @@ -103,20 +103,31 @@ def main(): # Extract our parameters inventory = module.params.get('inventory') inventory_source = module.params.get('inventory_source') + organization = module.params.get('organization') wait = module.params.get('wait') interval = module.params.get('interval') timeout = module.params.get('timeout') - new_fields = {} - organization_id = None - organization = module.params.get('organization') - if organization: - organization_id = module.get_one_by_name_or_id('organizations', organization) + org_id = module.resolve_name_to_id('organizations', organization) + inventory_object = module.get_one('inventories', **{ + 'data': { + 'name': inventory, + 'organization': org_id, + } + }) - # Attempt to look up the inventory the user specified (these will fail the module if not found) - inventory_object = module.get_one_by_name_or_id('inventories', inventory) - # Return all inventory sources related to the specified inventory - inventory_source_object = module.get_one_by_name_or_id(inventory_object['related']['inventory_sources'], inventory_source) + if not inventory_object: + module.fail_json(msg='The specified inventory was not found.') + + inventory_source_object = module.get_one('inventory_sources', **{ + 'data': { + 'name': inventory_source, + 'inventory': inventory_object['id'], + } + }) + + if not inventory_source_object: + module.fail_json(msg='The specified inventory source was not found.') # Sync the inventory source(s) inventory_source_update_results = module.post_endpoint(inventory_source_object['related']['update'], **{'data': {}}) @@ -124,12 +135,13 @@ def main(): if inventory_source_update_results['status_code'] != 202: module.fail_json(msg="Failed to update inventory source, see response for details", **{'response': inventory_source_update_results}) - inventory_source_update_results = module.wait_on_url( - url=inventory_source_update_results['json']['url'], - object_name=inventory_object, - object_type='inventory_update', - timeout=timeout, interval=interval - ) + if wait: + inventory_source_update_results = module.wait_on_url( + url=inventory_source_update_results['json']['url'], + object_name=inventory_object, + object_type='inventory_update', + timeout=timeout, interval=interval + ) module.exit_json(**{ 'changed': True, diff --git a/awx_collection/test/awx/test_inventory_source.py b/awx_collection/test/awx/test_inventory_source.py index b27653fb94..b1b362efad 100644 --- a/awx_collection/test/awx/test_inventory_source.py +++ b/awx_collection/test/awx/test_inventory_source.py @@ -24,11 +24,12 @@ def project(base_inventory): @pytest.mark.django_db -def test_inventory_source_create(run_module, admin_user, base_inventory, project): +def test_inventory_source_create(run_module, admin_user, base_inventory, project, organization): source_path = '/var/lib/awx/example_source_path/' result = run_module('tower_inventory_source', dict( name='foo', inventory=base_inventory.name, + organization='test-org', state='present', source='scm', source_path=source_path, @@ -45,30 +46,6 @@ def test_inventory_source_create(run_module, admin_user, base_inventory, project } -@pytest.mark.django_db -def test_create_inventory_source_implied_org(run_module, admin_user): - org = Organization.objects.create(name='test-org') - inv = Inventory.objects.create(name='test-inv', organization=org) - - # Credential is not required for ec2 source, because of IAM roles - result = run_module('tower_inventory_source', dict( - name='Test Inventory Source', - inventory='test-inv', - source='ec2', - state='present' - ), admin_user) - assert result.pop('changed', None), result - - inv_src = InventorySource.objects.get(name='Test Inventory Source') - assert inv_src.inventory == inv - - result.pop('invocation') - assert result == { - "name": "Test Inventory Source", - "id": inv_src.id, - } - - @pytest.mark.django_db def test_create_inventory_source_multiple_orgs(run_module, admin_user): org = Organization.objects.create(name='test-org') @@ -80,7 +57,8 @@ def test_create_inventory_source_multiple_orgs(run_module, admin_user): result = run_module('tower_inventory_source', dict( name='Test Inventory Source', - inventory=inv2.id, + inventory=inv2.name, + organization='test-org-number-two', source='ec2', state='present' ), admin_user) @@ -104,6 +82,7 @@ def test_create_inventory_source_with_venv(run_module, admin_user, base_inventor result = run_module('tower_inventory_source', dict( name='foo', inventory=base_inventory.name, + organization='test-org', state='present', source='scm', source_project=project.name, @@ -120,7 +99,7 @@ def test_create_inventory_source_with_venv(run_module, admin_user, base_inventor @pytest.mark.django_db -def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker, project): +def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker, project, organization): """If the inventory source is modified, then it should not blank fields unrelated to the params that the user passed. This enforces assumptions about the behavior of the AnsibleModule @@ -140,6 +119,7 @@ def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker, proje name='foo', description='this is the changed description', inventory=base_inventory.name, + organization='test-org', source='scm', # is required, but behavior is arguable state='present', source_project=project.name, @@ -156,6 +136,7 @@ def test_falsy_value(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='falsy-test', inventory=base_inventory.name, + organization='test-org', source='ec2', update_on_launch=True ), admin_user) @@ -168,6 +149,7 @@ def test_falsy_value(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='falsy-test', inventory=base_inventory.name, + organization='test-org', # source='ec2', update_on_launch=False ), admin_user) @@ -203,6 +185,7 @@ def test_missing_required_credential(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='Test Azure Source', inventory=base_inventory.name, + organization='test-org', source='azure_rm', state='present' ), admin_user) @@ -216,6 +199,7 @@ def test_source_project_not_for_cloud(run_module, admin_user, base_inventory, pr result = run_module('tower_inventory_source', dict( name='Test ec2 Inventory Source', inventory=base_inventory.name, + organization='test-org', source='ec2', state='present', source_project=project.name @@ -230,6 +214,7 @@ def test_source_path_not_for_cloud(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='Test ec2 Inventory Source', inventory=base_inventory.name, + organization='test-org', source='ec2', state='present', source_path='where/am/I' @@ -243,6 +228,7 @@ def test_source_path_not_for_cloud(run_module, admin_user, base_inventory): def test_scm_source_needs_project(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='SCM inventory without project', + organization='test-org', inventory=base_inventory.name, state='present', source='scm', diff --git a/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml index fb5f33c3ca..738d432711 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml @@ -31,6 +31,7 @@ credential: "{{ openstack_cred }}" overwrite: true update_on_launch: true + organization: Default source_vars: private: false source: openstack @@ -47,6 +48,7 @@ credential: "Does Not Exit" source_project: "Does Not Exist" source_script: "Does Not Exist" + organization: Default state: absent - assert: diff --git a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml index fd6cd48f2f..01b29a0cf0 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml @@ -32,12 +32,11 @@ name: "{{ inv_name }}" organization: "{{ org_name }}" state: present - register: created_inventory - name: Create another inventory w/ same name tower_inventory: name: "{{ inv_name }}" - organization: "{{ org_name }}" + organization: Default state: present register: created_inventory @@ -64,7 +63,7 @@ - name: Test Inventory Source Update tower_inventory_source_update: inventory: "{{ inv_name }}" - inventory_source: "{{ inv_source1 }}" + inventory_source: "{{ inv_source2 }}" organization: Default register: result @@ -75,10 +74,12 @@ - name: Test Inventory Source Update for All Sources tower_inventory_source_update: inventory: "{{ inv_name }}" - inventory_source: "{{ item }}" - organization: "{{ created_org.id }}" + inventory_source: "{{ item.name }}" + organization: Default wait: true - loop: "{{ query('awx.awx.tower_api', 'inventory_sources', query_params={ 'inventory': created_inventory.id }, return_ids=True ) }}" + loop: "{{ query('awx.awx.tower_api', 'inventory_sources', query_params={ 'inventory': created_inventory.id }, expect_objects=True, return_objects=True) }}" + loop_control: + label: "{{ item.name }}" register: result - assert: diff --git a/awx_collection/tools/roles/template_galaxy/templates/README.md.j2 b/awx_collection/tools/roles/template_galaxy/templates/README.md.j2 index 8a5743d34f..e1de06ec97 100644 --- a/awx_collection/tools/roles/template_galaxy/templates/README.md.j2 +++ b/awx_collection/tools/roles/template_galaxy/templates/README.md.j2 @@ -104,6 +104,7 @@ The following notes are changes that may require changes to playbooks: - The `notification_configuration` parameter of `tower_notification_template` has changed from a string to a dict. Please use the `lookup` plugin to read an existing file into a dict. - `tower_credential` no longer supports passing a file name to ssh_key_data. - The HipChat `notification_type` has been removed and can no longer be created using the `tower_notification_template` module. + - The `tower_inventory_source` module now requires the `organization` parameter in order to specify the inventory source's inventory's organization. {% if collection_package | lower() == "awx" %} ## Running Unit Tests From 4133ec974bc793c8b769542c68b13ff6a2dfed42 Mon Sep 17 00:00:00 2001 From: beeankha Date: Fri, 4 Sep 2020 10:39:25 -0400 Subject: [PATCH 6/8] Follow same pattern as project update module, add task to integration test --- .../modules/tower_inventory_source_update.py | 27 ++++++++++--------- .../tasks/main.yml | 11 ++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/awx_collection/plugins/modules/tower_inventory_source_update.py b/awx_collection/plugins/modules/tower_inventory_source_update.py index 6f9cdf6de1..8579126dd3 100644 --- a/awx_collection/plugins/modules/tower_inventory_source_update.py +++ b/awx_collection/plugins/modules/tower_inventory_source_update.py @@ -135,19 +135,22 @@ def main(): if inventory_source_update_results['status_code'] != 202: module.fail_json(msg="Failed to update inventory source, see response for details", **{'response': inventory_source_update_results}) - if wait: - inventory_source_update_results = module.wait_on_url( - url=inventory_source_update_results['json']['url'], - object_name=inventory_object, - object_type='inventory_update', - timeout=timeout, interval=interval - ) + module.json_output['changed'] = True + module.json_output['id'] = inventory_source_update_results['json']['id'] + module.json_output['status'] = inventory_source_update_results['json']['status'] - module.exit_json(**{ - 'changed': True, - 'id': inventory_source_update_results['json']['id'], - 'status': inventory_source_update_results['json']['status'], - }) + if not wait: + module.exit_json(**module.json_output) + + # Invoke wait function + module.wait_on_url( + url=inventory_source_update_results['json']['url'], + object_name=inventory_object, + object_type='inventory_update', + timeout=timeout, interval=interval + ) + + module.exit_json(**module.json_output) if __name__ == '__main__': diff --git a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml index 01b29a0cf0..b151db90ba 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml @@ -9,6 +9,7 @@ inv_name: "AWX-Collection-tests-tower_inventory_source_update-inv-{{ test_id }}" inv_source1: "AWX-Collection-tests-tower_inventory_source_update-source1-{{ test_id }}" inv_source2: "AWX-Collection-tests-tower_inventory_source_update-source2-{{ test_id }}" + inv_source3: "AWX-Collection-tests-tower_inventory_source_update-source3-{{ test_id }}" org_name: "AWX-Collection-tests-tower_inventory_source_update-org-{{ test_id }}" @@ -60,6 +61,16 @@ organization: Default inventory: "{{ inv_name }}" + - name: Create Yet Another Inventory Source (one more to make lookup plugin find multiple inventory sources) + tower_inventory_source: + name: "{{ inv_source3 }}" + source: scm + source_project: "{{ project_name }}" + source_path: inventories/create_100_hosts.ini + description: Source for Test inventory + organization: Default + inventory: "{{ inv_name }}" + - name: Test Inventory Source Update tower_inventory_source_update: inventory: "{{ inv_name }}" From a0b8f6a25dba1d0bd37ca15b7d482d0150457463 Mon Sep 17 00:00:00 2001 From: beeankha Date: Fri, 4 Sep 2020 18:40:18 -0400 Subject: [PATCH 7/8] Make org an optional parameter for both inv source and inv source update modules --- .../plugins/modules/tower_inventory_source.py | 15 +++----- .../modules/tower_inventory_source_update.py | 14 +++---- .../test/awx/test_inventory_source.py | 37 +++++++++++++------ .../tower_inventory_source/tasks/main.yml | 2 - .../tasks/main.yml | 8 ++-- .../template_galaxy/templates/README.md.j2 | 1 - 6 files changed, 40 insertions(+), 37 deletions(-) diff --git a/awx_collection/plugins/modules/tower_inventory_source.py b/awx_collection/plugins/modules/tower_inventory_source.py index 7c7c506826..0145671933 100644 --- a/awx_collection/plugins/modules/tower_inventory_source.py +++ b/awx_collection/plugins/modules/tower_inventory_source.py @@ -131,7 +131,6 @@ options: organization: description: - Name of the inventory source's inventory's organization. - required: True type: str extends_documentation_fragment: awx.awx.auth ''' @@ -174,7 +173,7 @@ def main(): enabled_value=dict(), host_filter=dict(), credential=dict(), - organization=dict(required=True), + organization=dict(), overwrite=dict(type='bool'), overwrite_vars=dict(type='bool'), custom_virtualenv=dict(), @@ -203,14 +202,10 @@ def main(): source_project = module.params.get('source_project') state = module.params.get('state') - # Attempt to look up inventory source based on the provided name and inventory ID - org_id = module.resolve_name_to_id('organizations', organization) - inventory_object = module.get_one('inventories', **{ - 'data': { - 'name': inventory, - 'organization': org_id, - } - }) + lookup_data = {'name': inventory} + if organization: + lookup_data['organization'] = module.resolve_name_to_id('organizations', organization) + inventory_object = module.get_one('inventories', data=lookup_data) if not inventory_object: module.fail_json(msg='The specified inventory was not found.') diff --git a/awx_collection/plugins/modules/tower_inventory_source_update.py b/awx_collection/plugins/modules/tower_inventory_source_update.py index 8579126dd3..abb55d1941 100644 --- a/awx_collection/plugins/modules/tower_inventory_source_update.py +++ b/awx_collection/plugins/modules/tower_inventory_source_update.py @@ -36,7 +36,6 @@ options: description: - Name of the inventory source's inventory's organization. type: str - required: True wait: description: - Wait for the job to complete. @@ -91,7 +90,7 @@ def main(): argument_spec = dict( inventory=dict(required=True), inventory_source=dict(required=True), - organization=dict(required=True), + organization=dict(), wait=dict(default=False, type='bool'), interval=dict(default=1.0, type='float'), timeout=dict(default=None, type='int'), @@ -108,13 +107,10 @@ def main(): interval = module.params.get('interval') timeout = module.params.get('timeout') - org_id = module.resolve_name_to_id('organizations', organization) - inventory_object = module.get_one('inventories', **{ - 'data': { - 'name': inventory, - 'organization': org_id, - } - }) + lookup_data = {'name': inventory} + if organization: + lookup_data['organization'] = module.resolve_name_to_id('organizations', organization) + inventory_object = module.get_one('inventories', data=lookup_data) if not inventory_object: module.fail_json(msg='The specified inventory was not found.') diff --git a/awx_collection/test/awx/test_inventory_source.py b/awx_collection/test/awx/test_inventory_source.py index b1b362efad..9f52574670 100644 --- a/awx_collection/test/awx/test_inventory_source.py +++ b/awx_collection/test/awx/test_inventory_source.py @@ -24,12 +24,11 @@ def project(base_inventory): @pytest.mark.django_db -def test_inventory_source_create(run_module, admin_user, base_inventory, project, organization): +def test_inventory_source_create(run_module, admin_user, base_inventory, project): source_path = '/var/lib/awx/example_source_path/' result = run_module('tower_inventory_source', dict( name='foo', inventory=base_inventory.name, - organization='test-org', state='present', source='scm', source_path=source_path, @@ -46,6 +45,30 @@ def test_inventory_source_create(run_module, admin_user, base_inventory, project } +@pytest.mark.django_db +def test_create_inventory_source_implied_org(run_module, admin_user): + org = Organization.objects.create(name='test-org') + inv = Inventory.objects.create(name='test-inv', organization=org) + + # Credential is not required for ec2 source, because of IAM roles + result = run_module('tower_inventory_source', dict( + name='Test Inventory Source', + inventory='test-inv', + source='ec2', + state='present' + ), admin_user) + assert result.pop('changed', None), result + + inv_src = InventorySource.objects.get(name='Test Inventory Source') + assert inv_src.inventory == inv + + result.pop('invocation') + assert result == { + "name": "Test Inventory Source", + "id": inv_src.id, + } + + @pytest.mark.django_db def test_create_inventory_source_multiple_orgs(run_module, admin_user): org = Organization.objects.create(name='test-org') @@ -82,7 +105,6 @@ def test_create_inventory_source_with_venv(run_module, admin_user, base_inventor result = run_module('tower_inventory_source', dict( name='foo', inventory=base_inventory.name, - organization='test-org', state='present', source='scm', source_project=project.name, @@ -99,7 +121,7 @@ def test_create_inventory_source_with_venv(run_module, admin_user, base_inventor @pytest.mark.django_db -def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker, project, organization): +def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker, project): """If the inventory source is modified, then it should not blank fields unrelated to the params that the user passed. This enforces assumptions about the behavior of the AnsibleModule @@ -119,7 +141,6 @@ def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker, proje name='foo', description='this is the changed description', inventory=base_inventory.name, - organization='test-org', source='scm', # is required, but behavior is arguable state='present', source_project=project.name, @@ -136,7 +157,6 @@ def test_falsy_value(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='falsy-test', inventory=base_inventory.name, - organization='test-org', source='ec2', update_on_launch=True ), admin_user) @@ -149,7 +169,6 @@ def test_falsy_value(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='falsy-test', inventory=base_inventory.name, - organization='test-org', # source='ec2', update_on_launch=False ), admin_user) @@ -185,7 +204,6 @@ def test_missing_required_credential(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='Test Azure Source', inventory=base_inventory.name, - organization='test-org', source='azure_rm', state='present' ), admin_user) @@ -199,7 +217,6 @@ def test_source_project_not_for_cloud(run_module, admin_user, base_inventory, pr result = run_module('tower_inventory_source', dict( name='Test ec2 Inventory Source', inventory=base_inventory.name, - organization='test-org', source='ec2', state='present', source_project=project.name @@ -214,7 +231,6 @@ def test_source_path_not_for_cloud(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='Test ec2 Inventory Source', inventory=base_inventory.name, - organization='test-org', source='ec2', state='present', source_path='where/am/I' @@ -228,7 +244,6 @@ def test_source_path_not_for_cloud(run_module, admin_user, base_inventory): def test_scm_source_needs_project(run_module, admin_user, base_inventory): result = run_module('tower_inventory_source', dict( name='SCM inventory without project', - organization='test-org', inventory=base_inventory.name, state='present', source='scm', diff --git a/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml index 738d432711..fb5f33c3ca 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml @@ -31,7 +31,6 @@ credential: "{{ openstack_cred }}" overwrite: true update_on_launch: true - organization: Default source_vars: private: false source: openstack @@ -48,7 +47,6 @@ credential: "Does Not Exit" source_project: "Does Not Exist" source_script: "Does Not Exist" - organization: Default state: absent - assert: diff --git a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml index b151db90ba..36a4e4b058 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source_update/tasks/main.yml @@ -34,14 +34,14 @@ organization: "{{ org_name }}" state: present - - name: Create another inventory w/ same name + - name: Create another inventory w/ same name, different org tower_inventory: name: "{{ inv_name }}" organization: Default state: present register: created_inventory - - name: Create an Inventory Source + - name: Create an Inventory Source (specifically connected to the randomly generated org) tower_inventory_source: name: "{{ inv_source1 }}" source: scm @@ -51,7 +51,7 @@ organization: "{{ created_org.id }}" inventory: "{{ inv_name }}" - - name: Create Another Inventory Source (for testing org-based lookup) + - name: Create Another Inventory Source tower_inventory_source: name: "{{ inv_source2 }}" source: scm @@ -61,7 +61,7 @@ organization: Default inventory: "{{ inv_name }}" - - name: Create Yet Another Inventory Source (one more to make lookup plugin find multiple inventory sources) + - name: Create Yet Another Inventory Source (to make lookup plugin find multiple inv sources) tower_inventory_source: name: "{{ inv_source3 }}" source: scm diff --git a/awx_collection/tools/roles/template_galaxy/templates/README.md.j2 b/awx_collection/tools/roles/template_galaxy/templates/README.md.j2 index e1de06ec97..8a5743d34f 100644 --- a/awx_collection/tools/roles/template_galaxy/templates/README.md.j2 +++ b/awx_collection/tools/roles/template_galaxy/templates/README.md.j2 @@ -104,7 +104,6 @@ The following notes are changes that may require changes to playbooks: - The `notification_configuration` parameter of `tower_notification_template` has changed from a string to a dict. Please use the `lookup` plugin to read an existing file into a dict. - `tower_credential` no longer supports passing a file name to ssh_key_data. - The HipChat `notification_type` has been removed and can no longer be created using the `tower_notification_template` module. - - The `tower_inventory_source` module now requires the `organization` parameter in order to specify the inventory source's inventory's organization. {% if collection_package | lower() == "awx" %} ## Running Unit Tests From c7114b2571c898110400a97de66cdb25deac0e8b Mon Sep 17 00:00:00 2001 From: beeankha Date: Mon, 7 Sep 2020 11:43:44 -0400 Subject: [PATCH 8/8] Add specific lookup data to error message --- awx_collection/plugins/modules/tower_inventory_source.py | 2 +- awx_collection/plugins/modules/tower_inventory_source_update.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/awx_collection/plugins/modules/tower_inventory_source.py b/awx_collection/plugins/modules/tower_inventory_source.py index 0145671933..1714773b32 100644 --- a/awx_collection/plugins/modules/tower_inventory_source.py +++ b/awx_collection/plugins/modules/tower_inventory_source.py @@ -208,7 +208,7 @@ def main(): inventory_object = module.get_one('inventories', data=lookup_data) if not inventory_object: - module.fail_json(msg='The specified inventory was not found.') + module.fail_json(msg='The specified inventory, {0}, was not found.'.format(lookup_data)) inventory_source_object = module.get_one('inventory_sources', **{ 'data': { diff --git a/awx_collection/plugins/modules/tower_inventory_source_update.py b/awx_collection/plugins/modules/tower_inventory_source_update.py index abb55d1941..c4cb27be7c 100644 --- a/awx_collection/plugins/modules/tower_inventory_source_update.py +++ b/awx_collection/plugins/modules/tower_inventory_source_update.py @@ -113,7 +113,7 @@ def main(): inventory_object = module.get_one('inventories', data=lookup_data) if not inventory_object: - module.fail_json(msg='The specified inventory was not found.') + module.fail_json(msg='The specified inventory, {0}, was not found.'.format(lookup_data)) inventory_source_object = module.get_one('inventory_sources', **{ 'data': {