summaryrefslogtreecommitdiffstats
path: root/ansible_collections/awx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
commit7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch)
treeefb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/awx
parentReleasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff)
downloadansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.tar.xz
ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.zip
Merging upstream version 9.4.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/awx')
-rw-r--r--ansible_collections/awx/awx/FILES.json1140
-rw-r--r--ansible_collections/awx/awx/MANIFEST.json6
-rw-r--r--ansible_collections/awx/awx/README.md3
-rw-r--r--ansible_collections/awx/awx/meta/runtime.yml1
-rw-r--r--ansible_collections/awx/awx/plugins/doc_fragments/auth.py5
-rw-r--r--ansible_collections/awx/awx/plugins/doc_fragments/auth_plugin.py8
-rw-r--r--ansible_collections/awx/awx/plugins/lookup/schedule_rruleset.py2
-rw-r--r--ansible_collections/awx/awx/plugins/module_utils/controller_api.py193
-rw-r--r--ansible_collections/awx/awx/plugins/module_utils/tower_legacy.py119
-rw-r--r--ansible_collections/awx/awx/plugins/modules/ad_hoc_command.py23
-rw-r--r--ansible_collections/awx/awx/plugins/modules/application.py8
-rw-r--r--ansible_collections/awx/awx/plugins/modules/bulk_host_create.py2
-rw-r--r--ansible_collections/awx/awx/plugins/modules/bulk_host_delete.py65
-rw-r--r--ansible_collections/awx/awx/plugins/modules/bulk_job_launch.py5
-rw-r--r--ansible_collections/awx/awx/plugins/modules/credential.py29
-rw-r--r--ansible_collections/awx/awx/plugins/modules/credential_input_source.py10
-rw-r--r--ansible_collections/awx/awx/plugins/modules/credential_type.py6
-rw-r--r--ansible_collections/awx/awx/plugins/modules/execution_environment.py13
-rw-r--r--ansible_collections/awx/awx/plugins/modules/export.py34
-rw-r--r--ansible_collections/awx/awx/plugins/modules/group.py20
-rw-r--r--ansible_collections/awx/awx/plugins/modules/host.py8
-rw-r--r--ansible_collections/awx/awx/plugins/modules/instance.py51
-rw-r--r--ansible_collections/awx/awx/plugins/modules/instance_group.py10
-rw-r--r--ansible_collections/awx/awx/plugins/modules/inventory.py56
-rw-r--r--ansible_collections/awx/awx/plugins/modules/inventory_source.py19
-rw-r--r--ansible_collections/awx/awx/plugins/modules/inventory_source_update.py2
-rw-r--r--ansible_collections/awx/awx/plugins/modules/job_launch.py14
-rw-r--r--ansible_collections/awx/awx/plugins/modules/job_template.py28
-rw-r--r--ansible_collections/awx/awx/plugins/modules/label.py8
-rw-r--r--ansible_collections/awx/awx/plugins/modules/license.py7
-rw-r--r--ansible_collections/awx/awx/plugins/modules/notification_template.py7
-rw-r--r--ansible_collections/awx/awx/plugins/modules/organization.py12
-rw-r--r--ansible_collections/awx/awx/plugins/modules/project.py16
-rw-r--r--ansible_collections/awx/awx/plugins/modules/project_update.py5
-rw-r--r--ansible_collections/awx/awx/plugins/modules/role.py116
-rw-r--r--ansible_collections/awx/awx/plugins/modules/schedule.py81
-rw-r--r--ansible_collections/awx/awx/plugins/modules/settings.py2
-rw-r--r--ansible_collections/awx/awx/plugins/modules/team.py8
-rw-r--r--ansible_collections/awx/awx/plugins/modules/token.py2
-rw-r--r--ansible_collections/awx/awx/plugins/modules/user.py8
-rw-r--r--ansible_collections/awx/awx/plugins/modules/workflow_job_template.py74
-rw-r--r--ansible_collections/awx/awx/plugins/modules/workflow_job_template_node.py22
-rw-r--r--ansible_collections/awx/awx/plugins/modules/workflow_launch.py18
-rw-r--r--ansible_collections/awx/awx/test/awx/conftest.py110
-rw-r--r--ansible_collections/awx/awx/test/awx/test_bulk.py33
-rw-r--r--ansible_collections/awx/awx/test/awx/test_completeness.py9
-rw-r--r--ansible_collections/awx/awx/test/awx/test_credential.py17
-rw-r--r--ansible_collections/awx/awx/test/awx/test_export.py150
-rw-r--r--ansible_collections/awx/awx/test/awx/test_instance.py46
-rw-r--r--ansible_collections/awx/awx/test/awx/test_job_template.py40
-rw-r--r--ansible_collections/awx/awx/test/awx/test_module_utils.py15
-rw-r--r--ansible_collections/awx/awx/test/awx/test_notification_template.py2
-rw-r--r--ansible_collections/awx/awx/test/awx/test_organization.py63
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command/tasks/main.yml48
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_cancel/tasks/main.yml27
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_wait/tasks/main.yml6
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/application/tasks/main.yml38
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/bulk_host_create/tasks/main.yml (renamed from ansible_collections/awx/awx/tests/integration/targets/bulk_host_create/main.yml)0
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/bulk_host_delete/tasks/main.yml80
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/bulk_job_launch/tasks/main.yml (renamed from ansible_collections/awx/awx/tests/integration/targets/bulk_job_launch/main.yml)2
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/credential/tasks/main.yml103
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/credential_input_source/tasks/main.yml45
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/credential_type/tasks/main.yml49
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/demo_data/tasks/main.yml1
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/execution_environment/tasks/main.yml42
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/group/tasks/main.yml110
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/host/tasks/main.yml67
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/instance/tasks/main.yml88
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/instance_group/tasks/main.yml37
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/inventory/tasks/main.yml40
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/inventory_source/tasks/main.yml67
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/inventory_source_update/tasks/main.yml3
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/job_cancel/tasks/main.yml9
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/job_launch/tasks/main.yml11
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/job_list/tasks/main.yml4
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/job_template/tasks/main.yml78
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/job_wait/tasks/main.yml327
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/label/tasks/main.yml23
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/lookup_api_plugin/tasks/main.yml5
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/lookup_rruleset/tasks/main.yml18
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/module_utils/tasks/main.yml8
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/module_utils/tasks/test_named_reference.yml73
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/notification_template/tasks/main.yml86
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/organization/tasks/main.yml34
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/project/tasks/main.yml90
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/project_manual/tasks/create_project_dir.yml56
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/project_manual/tasks/main.yml38
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/project_update/tasks/main.yml6
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/role/tasks/main.yml69
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/schedule/tasks/main.yml51
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/schedule_rrule/tasks/main.yml2
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/settings/tasks/main.yml38
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/team/tasks/main.yml40
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/token/tasks/main.yml7
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/user/tasks/main.yml48
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/workflow_approval/tasks/main.yml4
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/workflow_job_template/tasks/main.yml115
-rw-r--r--ansible_collections/awx/awx/tests/integration/targets/workflow_launch/tasks/main.yml2
-rw-r--r--ansible_collections/awx/awx/tests/sanity/ignore-2.15.txt2
-rw-r--r--ansible_collections/awx/awx/tests/sanity/ignore-2.16.txt3
100 files changed, 3386 insertions, 1465 deletions
diff --git a/ansible_collections/awx/awx/FILES.json b/ansible_collections/awx/awx/FILES.json
index 9a0b1d45c..9fba1d572 100644
--- a/ansible_collections/awx/awx/FILES.json
+++ b/ansible_collections/awx/awx/FILES.json
@@ -8,647 +8,703 @@
"format": 1
},
{
- "name": "bindep.txt",
+ "name": "README.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "7205dda85d2cd5501b3344e9f18e4acd09583056aab5e8a05554ba29a3b8fad8",
+ "chksum_sha256": "41997bded547ba6a168ee41fd5379a44f20dcd155979067564b79d14af6e3fd3",
"format": 1
},
{
- "name": "COPYING",
+ "name": "meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "meta/runtime.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "7c50cd9b85e2b7eebaea2b5618b402862b01d5a66befff8e41401ef3f14e471a",
+ "chksum_sha256": "6c63b75e8e4c9b744914c111d3ff8b54f0e973b78ce444815acfb4aa254a9ad6",
"format": 1
},
{
- "name": "images",
+ "name": "bindep.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7205dda85d2cd5501b3344e9f18e4acd09583056aab5e8a05554ba29a3b8fad8",
+ "format": 1
+ },
+ {
+ "name": "plugins",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "images/completeness_test_output.png",
+ "name": "plugins/doc_fragments",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/auth_plugin.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "6367684c4b5edd3e1e8fdcb9270d68ca54040d5d17108734f3d3a2b9df5878ba",
+ "chksum_sha256": "f1c61a9880edca852582f58c635c32264b0d1b3218ebdeb9db9d23081267c070",
"format": 1
},
{
- "name": "README.md",
+ "name": "plugins/doc_fragments/auth.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "490a000bf64790d90206607e3dd74c77e7ae940e58346a319a590053afe72149",
+ "chksum_sha256": "83f0ecfb936c89bd2b589eff8710617080f539e943f38bf57b45b4eb753564e8",
"format": 1
},
{
- "name": "TESTING.md",
+ "name": "plugins/doc_fragments/auth_legacy.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "4691e79c8038d8e985610fb613cd2f4799d4740b0a6ca1b72d3266528088a272",
+ "chksum_sha256": "c2f10b81ecb89088c7c295430d4a71de26e3700b26e8344cdc7950908a738fd3",
"format": 1
},
{
- "name": "tests",
+ "name": "plugins/inventory",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/inventory/controller.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "29e7cd36a2b18ee616e31cbbec6a6e103f3f18ebe13f2bb87167b915163ca4bf",
"format": 1
},
{
- "name": "tests/integration/targets",
+ "name": "plugins/modules",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/instance_group",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/job_cancel.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d64f698909919b05c9c47a65f24c861c3cabe33c039944f6120d49a2ac7d40da",
"format": 1
},
{
- "name": "tests/integration/targets/instance_group/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/ad_hoc_command_cancel.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3338e10af9ccd0e4178b8e1ec1e7064b00ab90e64665f846a2123f10d9d151f4",
"format": 1
},
{
- "name": "tests/integration/targets/instance_group/tasks/main.yml",
+ "name": "plugins/modules/workflow_approval.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "2498af3df7c31531b3d029afea14945fc211c6cc8a535a855330fb699e7a7d32",
+ "chksum_sha256": "533e52fc20ca99e935154f5ba3ec30c2055f42d1c51c3bc2cf1570af7f951c33",
"format": 1
},
{
- "name": "tests/integration/targets/bulk_host_create",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/job_list.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2ea8024bfc9612c005745a13a508c40d320b4c204bf18fcd495f72789d9adb40",
"format": 1
},
{
- "name": "tests/integration/targets/bulk_host_create/main.yml",
+ "name": "plugins/modules/bulk_host_delete.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "39be6c097c03152ec1873732d8e09e34639464acad4f9d42ef3e13b6f87d47d1",
+ "chksum_sha256": "546fb1eb2104db87c1d608144590e38d361af2ba734caa62fc61586e49a124ad",
"format": 1
},
{
- "name": "tests/integration/targets/ad_hoc_command_cancel",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/inventory_source_update.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e0b79d76d7d9f817f709a8dacbcb2a105a214c33e63449decaec65adebac6d74",
"format": 1
},
{
- "name": "tests/integration/targets/ad_hoc_command_cancel/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/workflow_launch.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3aff0ecdf76b65df3e17f7c8a34eddae8a1bc4d35c304619f7c2054927584d35",
"format": 1
},
{
- "name": "tests/integration/targets/ad_hoc_command_cancel/tasks/main.yml",
+ "name": "plugins/modules/controller_meta.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3f698a655089b977ee89dde6532823b4e496b190a0203b52e75e0a19b0321e3f",
+ "chksum_sha256": "c66ebbe3a0eab6a9d28d517824ebf8478afdf14981c6c931f08592503c243cdd",
"format": 1
},
{
- "name": "tests/integration/targets/ad_hoc_command",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/credential.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e6d52d95b7e59a41b5d3715c3b55857c1b70baf7a0e8e44e9cc66db67b87685f",
"format": 1
},
{
- "name": "tests/integration/targets/ad_hoc_command/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/team.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e53a544c12fc2de705bce163e3babf6d95fae6d088f1415a61debeb07d60f991",
"format": 1
},
{
- "name": "tests/integration/targets/ad_hoc_command/tasks/main.yml",
+ "name": "plugins/modules/host.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e77cbce44dc257461c1edb3602690be28a7d8bf5e11384c9f5f401b6e1cb3149",
+ "chksum_sha256": "b973c5f3790dc0c084e97a6f6b0c87209632f1ff348bc36466cc13392e774c79",
"format": 1
},
{
- "name": "tests/integration/targets/credential_type",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/job_launch.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1c26e876232d5658537b9d041879f93e024e52c901551b5e6ad89354d6023d71",
"format": 1
},
{
- "name": "tests/integration/targets/credential_type/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/credential_input_source.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "78ce109c0cc51aa8d66f0146ac9f448559b186cc2b155ee2a8ed96cebe65909d",
"format": 1
},
{
- "name": "tests/integration/targets/credential_type/tasks/main.yml",
+ "name": "plugins/modules/instance_group.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "b354b1b26b90216a8460fc9d478422c825f87d0dc1d59074acdc4650e8a0fb34",
+ "chksum_sha256": "14c7f97d44a266a9d73d8ef1fc85f8c15ecbab611ff8133026ef943e211ba7c2",
"format": 1
},
{
- "name": "tests/integration/targets/project",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/inventory_source.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "279efe4103630b4961baeb468c43f9d75c657c0beb36ece73982d31080931403",
"format": 1
},
{
- "name": "tests/integration/targets/project/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/application.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e1b39c7cd3d608245b7d419547d6582fca5143bf8f72ff39fa89cb67d4ee8ac0",
"format": 1
},
{
- "name": "tests/integration/targets/project/tasks/main.yml",
+ "name": "plugins/modules/role.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "6267e4d55fe0c8ac80f174bd90deb37ba3dea9825eef2ec9ce28f6863cfed562",
+ "chksum_sha256": "31aebaae562881a3a9ecea61f5d26747559cbed69dcf45fbe21d9a65412ea56c",
"format": 1
},
{
- "name": "tests/integration/targets/bulk_job_launch",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/token.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cc35a007403827d4994793d9bb4a5d573d9b532c605feee6d97b119037ead203",
"format": 1
},
{
- "name": "tests/integration/targets/bulk_job_launch/main.yml",
+ "name": "plugins/modules/export.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "76a5b81caaa30a7dc5fd68048b925551a83ef2cee8a0b4c15d02f0857fbbe812",
+ "chksum_sha256": "b62aa153c8a819461f3bcbd27a4de731e477dffc4d05def8829687901b71aec9",
"format": 1
},
{
- "name": "tests/integration/targets/inventory",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/import.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a7a03186251ef644ba03c49e7e23a799f8046abddb9ea20fff68dd09fe759680",
"format": 1
},
{
- "name": "tests/integration/targets/inventory/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/credential_type.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1867c68c3b43c0f27a8e87dcbb66b22fdfc1cd659cc1747a6686573f6a7d6be9",
"format": 1
},
{
- "name": "tests/integration/targets/inventory/tasks/main.yml",
+ "name": "plugins/modules/workflow_job_template_node.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "40d4e282aabbabbf5aff3ea6f966d3782852aa3204f33d65e216ede5e0fd66fe",
+ "chksum_sha256": "023e170fb1db59ec4d4acecee21befc2aa03199110368d4bd0682ad4adf84092",
"format": 1
},
{
- "name": "tests/integration/targets/project_manual",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/job_wait.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7e7459abf351f6c172401eec4ba579dc8566f8a55fd022cc8eec9fa5a3399067",
"format": 1
},
{
- "name": "tests/integration/targets/project_manual/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/execution_environment.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e00a63a1ddccfef4ad20725799ba67dbdfd8b9c720d9f04a92f05683bf7dc4e1",
"format": 1
},
{
- "name": "tests/integration/targets/project_manual/tasks/main.yml",
+ "name": "plugins/modules/instance.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a1773c1365a408448e4facc5efdcc1b1e9f1f4a05e2eed59197dac00e9fa5105",
+ "chksum_sha256": "fb4467c11809837fee04ed77ddcf154414070a0b0458742e8d6dcb3be045a9f3",
"format": 1
},
{
- "name": "tests/integration/targets/project_manual/tasks/create_project_dir.yml",
+ "name": "plugins/modules/organization.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "562d2c4b88bbb2a3aa9ac76dbcb59e3cdf490e58f88c9971ff7e8b40bd4b3aca",
+ "chksum_sha256": "0c46808d096cba86747d9e9d69da660d44341c885fb966e4ffad8499b7d8d055",
"format": 1
},
{
- "name": "tests/integration/targets/workflow_approval",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/license.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf54b3a3c82fb705cb36a81cdab5e3cc25c8c8a798f3f43d22927c3164e97e69",
"format": 1
},
{
- "name": "tests/integration/targets/workflow_approval/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/group.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7dc98af3cbdfc5ea0cf4cffe0c86c846837dfebe15c16939ed863b730cb05578",
"format": 1
},
{
- "name": "tests/integration/targets/workflow_approval/tasks/main.yml",
+ "name": "plugins/modules/subscriptions.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "d9891056c8e9ce1b3a2e78eebbc470979e4790bffafc3d5e4d22e90c7da4948d",
+ "chksum_sha256": "f497ab9ada8f89650422bf85deef386e32b774dfff9e1de07b387fba32d890a8",
"format": 1
},
{
- "name": "tests/integration/targets/label",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/workflow_node_wait.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5b19778b005fbaa3e0a3abc645a6d6452bc0ad52e89fe04141d051f6ddafbb73",
"format": 1
},
{
- "name": "tests/integration/targets/label/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/label.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f3bde75b41fd4c92037f759ae00e9ebd76f27c91ab54857f167715db1930b0a8",
"format": 1
},
{
- "name": "tests/integration/targets/label/tasks/main.yml",
+ "name": "plugins/modules/workflow_job_template.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a54774f0ce66904eb1ab7c1389add53c60672d2b2d29b670034712f59e99d27d",
+ "chksum_sha256": "18fa1ae60ba7c409cd3baf67215ebf3b8d680e10a1402ca66f362166ab668cb4",
"format": 1
},
{
- "name": "tests/integration/targets/host",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/bulk_host_create.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9398fec791845d1ddb304cd344d308127f89ce6ed5a8341f2c6047b4e2d22ed4",
"format": 1
},
{
- "name": "tests/integration/targets/host/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/project.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d65ade1efc240c94eadc37d984cfec8094c655ed4d162243d170e947bad4b425",
"format": 1
},
{
- "name": "tests/integration/targets/host/tasks/main.yml",
+ "name": "plugins/modules/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/settings.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9490898a74c074d553d8ac98dbc1d59bebc1d2a1f1899f28fd3165125ddfd44a",
+ "chksum_sha256": "c62170baca6d9ecf0be7a68d148daa75569e87a6ddffb92a39f3bc4ea08e9e0a",
"format": 1
},
{
- "name": "tests/integration/targets/inventory_source",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/project_update.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "952bedbca07dba7de277849b45eb258f51420ed8e814fa35acd47dc5e5f8f82f",
"format": 1
},
{
- "name": "tests/integration/targets/inventory_source/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/ad_hoc_command_wait.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "67bc716ec04dfc77cb751cda6013ee54fa0cd3ed3afabc5ba0d146cc9712c996",
"format": 1
},
{
- "name": "tests/integration/targets/inventory_source/tasks/main.yml",
+ "name": "plugins/modules/ad_hoc_command.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "55ef33f725875c00e6e31a928aff85257aacf5689c7f64a0924963c44b35c5af",
+ "chksum_sha256": "e0bc12df3832ad4c12eb7977c3dd69bee9eb0afdbd88755c06f01c2c42146869",
"format": 1
},
{
- "name": "tests/integration/targets/job_cancel",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/schedule.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "517c76a73dae1fadc7c47c0c8544ae00c6f88daff7a3e72c67aa9501265177ac",
"format": 1
},
{
- "name": "tests/integration/targets/job_cancel/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/inventory.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1a430ae0b6371f884c14a9022552846f5174100ef02601a6ece649cb45a9e7e9",
"format": 1
},
{
- "name": "tests/integration/targets/job_cancel/tasks/main.yml",
+ "name": "plugins/modules/user.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "72e23ca4c467f6b23f23577597ad891613ec780c25fc00bb73bd3cd438783b2a",
+ "chksum_sha256": "cd3cc005d12434b3cac4238b3dfc0d2976f53f0c2e8c0dafc02953ad1393e377",
"format": 1
},
{
- "name": "tests/integration/targets/schedule",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/notification_template.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "638dbb75383b11b1227b2120424ee4f9f37861747c9aa5fca6df0665fe97fc86",
"format": 1
},
{
- "name": "tests/integration/targets/schedule/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/modules/job_template.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "77a9774f8baae9a417aa3f7170f4866307ea3e7f29fbe93a00df98f33e8c99b0",
"format": 1
},
{
- "name": "tests/integration/targets/schedule/tasks/main.yml",
+ "name": "plugins/modules/bulk_job_launch.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "617b00f1a2de9e876ce63a471be371ac0e1622e8ab45062e9de8f5ea9ad8d4b2",
+ "chksum_sha256": "f19617e62f57b0a81fb9ebbf209583fbd9c74d3e2b85ca0102b2fd1b2bd77b81",
"format": 1
},
{
- "name": "tests/integration/targets/workflow_job_template",
+ "name": "plugins/module_utils",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/workflow_job_template/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/module_utils/awxkit.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8b2398e4e7893f203b26f6c85d510cc4c41a79c53e1937710807233e62e35f58",
"format": 1
},
{
- "name": "tests/integration/targets/workflow_job_template/tasks/main.yml",
+ "name": "plugins/module_utils/controller_api.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "2d872a591fbf23b975af6eacf660266ab8e0fb883af6b2bd33c058714b530b46",
+ "chksum_sha256": "424dfd5dbc14adedb59d26cfa67bbc46e1c5197859c473c9cdb288bb2ef26db9",
"format": 1
},
{
- "name": "tests/integration/targets/job_launch",
+ "name": "plugins/lookup",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/job_launch/tasks",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/lookup/controller_api.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e79f19c9dee4fa0c3a88126a630fa6163249c332d73a44370f64836e22d4b27",
"format": 1
},
{
- "name": "tests/integration/targets/job_launch/tasks/main.yml",
+ "name": "plugins/lookup/schedule_rruleset.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f4c564c9b92788fec1a7bdbe7ca53b2106ac7fee649912c64e366575ed3eb72a",
+ "chksum_sha256": "0141249f5cbe0651f96d3ba0a627a4d7e7376bd0e5b2b29e63ef44f9c243feb3",
"format": 1
},
{
- "name": "tests/integration/targets/instance",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "plugins/lookup/schedule_rrule.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c3ec7b8f134eca3a9f04156213b584792fc4e3397e3b9f82b5044e9ec662c7a2",
"format": 1
},
{
- "name": "tests/integration/targets/instance/tasks",
+ "name": "COPYING",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7c50cd9b85e2b7eebaea2b5618b402862b01d5a66befff8e41401ef3f14e471a",
+ "format": 1
+ },
+ {
+ "name": "tests",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/instance/tasks/main.yml",
+ "name": "tests/config.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e9588dae873c72034ee98d463c80cb48c5a236b7e4d182786f3dee240ed89456",
+ "chksum_sha256": "4cb8bf065737689916cda6a2856fcfb8bc27f49224a4b2c2fde842e3b0e76fbb",
"format": 1
},
{
- "name": "tests/integration/targets/lookup_api_plugin",
+ "name": "tests/sanity",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/lookup_api_plugin/tasks",
+ "name": "tests/sanity/ignore-2.15.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f6dce33e05558d94ecc8ebacc8a5011e9defc1b197fcc13c4335868b6d6c4952",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.14.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "05b621f6ff40c091ab1c07947c43d817ed37af7acfc0f8bef7b1453eb03b3aa7",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.16.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f6dce33e05558d94ecc8ebacc8a5011e9defc1b197fcc13c4335868b6d6c4952",
+ "format": 1
+ },
+ {
+ "name": "tests/integration",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/lookup_api_plugin/tasks/main.yml",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "e47eaa102b38a0fc93f5a0fa3bf478a9f8b5fffce02737ff7099ae3dee1958ea",
+ "name": "tests/integration/targets",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/lookup_rruleset",
+ "name": "tests/integration/targets/job_list",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/lookup_rruleset/tasks",
+ "name": "tests/integration/targets/job_list/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/lookup_rruleset/tasks/main.yml",
+ "name": "tests/integration/targets/job_list/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "508564b7524b5f807dafd028ee5af7433bb5aace1e1396a3e1afd843f3b8f274",
+ "chksum_sha256": "668f25abe2486218893b6137f5b765301229f649ed3a779a6756496c14f42595",
"format": 1
},
{
- "name": "tests/integration/targets/project_update",
+ "name": "tests/integration/targets/credential_type",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/project_update/tasks",
+ "name": "tests/integration/targets/credential_type/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/project_update/tasks/main.yml",
+ "name": "tests/integration/targets/credential_type/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "cfe9600b0f76c887c924cc051dbd978559f58ca45ad88c760fc44a1a4f1c5a08",
+ "chksum_sha256": "6d5026a4ca77513f73aa620ea14b74e0248c0dde7b5d75b318970154d7f4bf56",
"format": 1
},
{
- "name": "tests/integration/targets/job_list",
+ "name": "tests/integration/targets/job_wait",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/job_list/tasks",
+ "name": "tests/integration/targets/job_wait/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/job_list/tasks/main.yml",
+ "name": "tests/integration/targets/job_wait/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "0f71862d199385973c479a2ce6a2c9eb060c80cfca19b82026a19ec60308f1b3",
+ "chksum_sha256": "76cf3f8c1cfac81eaaf0f5b76494fc3b7605a15eeb73f6fc83efbbe28a4073b3",
"format": 1
},
{
- "name": "tests/integration/targets/demo_data",
+ "name": "tests/integration/targets/workflow_approval",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/demo_data/tasks",
+ "name": "tests/integration/targets/workflow_approval/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/demo_data/tasks/main.yml",
+ "name": "tests/integration/targets/workflow_approval/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "617098185b6890b6c01d85830deb32bda385ca2499ea0c6f5f8bf44f1bedae28",
+ "chksum_sha256": "2ccc6f9f0aafef620896c56a32e1dd07f2f83f61c6b8c4d4bbaa6a220495b91d",
"format": 1
},
{
- "name": "tests/integration/targets/job_template",
+ "name": "tests/integration/targets/application",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/job_template/tasks",
+ "name": "tests/integration/targets/application/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/job_template/tasks/main.yml",
+ "name": "tests/integration/targets/application/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "121573eb6c0556945387c8c8eb533dda8da637c3470c6b53a4cdd7d85f1b58d6",
+ "chksum_sha256": "42675b283f15715cccd1bba3f3d6be8d9d84e1bcf7c1e06e33a281ab4eb5c64f",
"format": 1
},
{
- "name": "tests/integration/targets/workflow_launch",
+ "name": "tests/integration/targets/instance_group",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/workflow_launch/tasks",
+ "name": "tests/integration/targets/instance_group/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/workflow_launch/tasks/main.yml",
+ "name": "tests/integration/targets/instance_group/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "dd5c0022ff17e2eebb52303e1f5132eff0cdf35e737f122b0573f928cdd7ad03",
+ "chksum_sha256": "5d38320dd4563f17fffd12b510a91dd26e40bfd5f408f75f96b930a38fcfd135",
"format": 1
},
{
- "name": "tests/integration/targets/team",
+ "name": "tests/integration/targets/settings",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/team/tasks",
+ "name": "tests/integration/targets/settings/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/team/tasks/main.yml",
+ "name": "tests/integration/targets/settings/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ea5b32cc64a4b1553bc5b254b09ef61187911e6364bb7a4d3c3159233182bdc7",
+ "chksum_sha256": "93262aa7be1aec73bcd5e36b1fadd663bed21eab8863f797dd17f20de80db7ba",
"format": 1
},
{
- "name": "tests/integration/targets/notification_template",
+ "name": "tests/integration/targets/project",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/notification_template/tasks",
+ "name": "tests/integration/targets/project/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/notification_template/tasks/main.yml",
+ "name": "tests/integration/targets/project/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "fe7f0d86e9bde61f8b708757f0b0627f5ace6b7cbdaf7103f8b247289ae5a295",
+ "chksum_sha256": "c2f8af4ca361cc63c465d7b0895ef2ad7a3741f36e30133b5c33696ba2cf7474",
"format": 1
},
{
- "name": "tests/integration/targets/application",
+ "name": "tests/integration/targets/inventory_source",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/application/tasks",
+ "name": "tests/integration/targets/inventory_source/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/application/tasks/main.yml",
+ "name": "tests/integration/targets/inventory_source/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "d95ffd0fd9a79fd5c9bd95841d6e88f0ad9a8d2f4376a1d66a3432a48cc8e445",
+ "chksum_sha256": "7e581c4634196ff64cee14278c55b2292534605006e5a20edadcfe17e778caa9",
"format": 1
},
{
@@ -669,98 +725,91 @@
"name": "tests/integration/targets/group/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "7444fbbb58dc4c93953187b843cbdc3198427571a1fd49f36bc8d71d23b70479",
+ "chksum_sha256": "fe499cf6b248289df031a4d32328dbe45184f4fe7d92423522b1705715010904",
"format": 1
},
{
- "name": "tests/integration/targets/schedule_rrule",
+ "name": "tests/integration/targets/demo_data",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/schedule_rrule/tasks",
+ "name": "tests/integration/targets/demo_data/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/schedule_rrule/tasks/main.yml",
+ "name": "tests/integration/targets/demo_data/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "2d3b202620a305fcf477836f22ce7e52a195b0f43b3c854ffec5763d6411b26b",
+ "chksum_sha256": "619e28fefe26c4854b15b8c739693b1da85f69b1cf9792c84f40db544dda5f8b",
"format": 1
},
{
- "name": "tests/integration/targets/user",
+ "name": "tests/integration/targets/credential",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/user/tasks",
+ "name": "tests/integration/targets/credential/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/user/tasks/main.yml",
+ "name": "tests/integration/targets/credential/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "88f742f66ccac2fed93222a997e02129c43e9dd863ce8b9a2fd8e07dd6973916",
+ "chksum_sha256": "113f6f4c35885fe2a1ebca33523c9a6284a36ae470963e41a12909e444e0281d",
"format": 1
},
{
- "name": "tests/integration/targets/settings",
+ "name": "tests/integration/targets/schedule",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/settings/tasks",
+ "name": "tests/integration/targets/schedule/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/settings/tasks/main.yml",
+ "name": "tests/integration/targets/schedule/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "1d02d2e1a163b170fa15d54b37ec7da22509d45f4cc194583ec1a1c5d5682b16",
+ "chksum_sha256": "7419baf00681606be0a250140ace5f7d07804f8b650ecb5f0937147064e307f0",
"format": 1
},
{
- "name": "tests/integration/targets/import",
+ "name": "tests/integration/targets/organization",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/import/tasks",
+ "name": "tests/integration/targets/organization/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/import/tasks/main.yml",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "59c0ace95e680d9874fe15c76889c1b4beb38d2d3c66a11499581b0f328ec25a",
- "format": 1
- },
- {
- "name": "tests/integration/targets/import/aliases",
+ "name": "tests/integration/targets/organization/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "52e1315ef042495cdf2b0ce22d8ba47f726dce15b968e301a795be1f69045f20",
+ "chksum_sha256": "6e18cbff8118f617a9b594f630739356922030c2d802a0eb92df0ddbe4f1e1fe",
"format": 1
},
{
@@ -781,140 +830,140 @@
"name": "tests/integration/targets/inventory_source_update/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c9da57401129b24c4c6b2a54acd923a5e8f82a884ad94b23dbf1cf4dfad847cb",
+ "chksum_sha256": "06a737371470d7a996e2eb092d820fe536dac73f3abcec56acf002da62fe05d9",
"format": 1
},
{
- "name": "tests/integration/targets/job_wait",
+ "name": "tests/integration/targets/lookup_api_plugin",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/job_wait/tasks",
+ "name": "tests/integration/targets/lookup_api_plugin/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/job_wait/tasks/main.yml",
+ "name": "tests/integration/targets/lookup_api_plugin/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "d7dd7fb1b9b81268a3d35a9df424fc977df5463bdef0c00b34bda6fab98682c9",
+ "chksum_sha256": "d2206729f900ec7f71e77edf782eff6e68160ccb0124fb2bf22f0f371abb9271",
"format": 1
},
{
- "name": "tests/integration/targets/role",
+ "name": "tests/integration/targets/import",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/role/tasks",
+ "name": "tests/integration/targets/import/aliases",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "52e1315ef042495cdf2b0ce22d8ba47f726dce15b968e301a795be1f69045f20",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/import/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/role/tasks/main.yml",
+ "name": "tests/integration/targets/import/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "b5f1f2624634486869b5e587c44fb44d0849b926364bd14766a8921cbcfe3674",
+ "chksum_sha256": "59c0ace95e680d9874fe15c76889c1b4beb38d2d3c66a11499581b0f328ec25a",
"format": 1
},
{
- "name": "tests/integration/targets/organization",
+ "name": "tests/integration/targets/job_cancel",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/organization/tasks",
+ "name": "tests/integration/targets/job_cancel/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/organization/tasks/main.yml",
+ "name": "tests/integration/targets/job_cancel/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "73853c4fd1dde833e599df55d8636e3cabac2e1139eac0785c75a9b7b6fac00f",
+ "chksum_sha256": "0c8b9d511b1ce96b7d5a359b1043ffc18004c5d3c23052b82423512b46ecd7b4",
"format": 1
},
{
- "name": "tests/integration/targets/credential",
+ "name": "tests/integration/targets/ad_hoc_command",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/credential/tasks",
+ "name": "tests/integration/targets/ad_hoc_command/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/credential/tasks/main.yml",
+ "name": "tests/integration/targets/ad_hoc_command/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "df1406f279fb76a35c21f2e178b20f6268f30b15305200d7bff4f70d051d3284",
+ "chksum_sha256": "87ec6c33a66b6dd969c6ed08693a02a51fc43c21b3980a81a5edd29d644050aa",
"format": 1
},
{
- "name": "tests/integration/targets/export",
+ "name": "tests/integration/targets/inventory",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/export/tasks",
+ "name": "tests/integration/targets/inventory/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/export/tasks/main.yml",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "e66e796b995b7c9ae612e00b393ccd75d9747d2d94ea3fbbaf90832e5b3e9e3f",
- "format": 1
- },
- {
- "name": "tests/integration/targets/export/aliases",
+ "name": "tests/integration/targets/inventory/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "52e1315ef042495cdf2b0ce22d8ba47f726dce15b968e301a795be1f69045f20",
+ "chksum_sha256": "21f6d6fa5e76c8578949fd204edef4cdb77b5bff310b681bcd75ac1873a67f18",
"format": 1
},
{
- "name": "tests/integration/targets/token",
+ "name": "tests/integration/targets/role",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/token/tasks",
+ "name": "tests/integration/targets/role/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/token/tasks/main.yml",
+ "name": "tests/integration/targets/role/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "606e16dcd72ab4a0a6c26aedf8830e1de844266e7fa54254c93ed7e307c950d7",
+ "chksum_sha256": "24e699037b51baceceb9f18e1e43464608766127f5eeb4df1fed5991a35aea7f",
"format": 1
},
{
@@ -935,28 +984,28 @@
"name": "tests/integration/targets/execution_environment/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f51f07de38999eff7fbacbd72929a622afc5b9fb01f2acd44cae288978948c64",
+ "chksum_sha256": "950122a04ad3b7406fa9a140e02ab190a0f7f53dfec791fdcb1fb2781ebae54d",
"format": 1
},
{
- "name": "tests/integration/targets/credential_input_source",
+ "name": "tests/integration/targets/lookup_rruleset",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/credential_input_source/tasks",
+ "name": "tests/integration/targets/lookup_rruleset/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/integration/targets/credential_input_source/tasks/main.yml",
+ "name": "tests/integration/targets/lookup_rruleset/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ccc6e4527f9019c28f32cdb7b223d1a4445f2f505ef448e8a2e255c8981bd927",
+ "chksum_sha256": "30edd075f50be0fbf9c57c9d7db10ae7086749d78817e8a24b680301ba339cb5",
"format": 1
},
{
@@ -977,679 +1026,686 @@
"name": "tests/integration/targets/ad_hoc_command_wait/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ce9d9c82599c3673f4a0d7da0b7af4437bb32689b5ff607266f0c875a7b7f2b7",
+ "chksum_sha256": "1e5ced853c1d15d2db163bd5dbb56b1ca419363327dc32bd6a1448560e194c2f",
"format": 1
},
{
- "name": "tests/config.yml",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "4cb8bf065737689916cda6a2856fcfb8bc27f49224a4b2c2fde842e3b0e76fbb",
+ "name": "tests/integration/targets/job_template",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "tests/sanity",
+ "name": "tests/integration/targets/job_template/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/sanity/ignore-2.15.txt",
+ "name": "tests/integration/targets/job_template/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "05b621f6ff40c091ab1c07947c43d817ed37af7acfc0f8bef7b1453eb03b3aa7",
+ "chksum_sha256": "b1232f2cc28b0ff4ff742c2c5847a649d3d9fcc716d3611a938b277088a0e30b",
"format": 1
},
{
- "name": "tests/sanity/ignore-2.14.txt",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "05b621f6ff40c091ab1c07947c43d817ed37af7acfc0f8bef7b1453eb03b3aa7",
+ "name": "tests/integration/targets/token",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "meta",
+ "name": "tests/integration/targets/token/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "meta/runtime.yml",
+ "name": "tests/integration/targets/token/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "8f31c7db3b2274e381a8e6c9dff564f47815ab49ec28b472ed1ec678a331ca06",
+ "chksum_sha256": "e7245f5039915d720baba505b4ab74421c7db138bb45b50917942f3eb05799af",
"format": 1
},
{
- "name": "test",
+ "name": "tests/integration/targets/export",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "tests/integration/targets/export/aliases",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "52e1315ef042495cdf2b0ce22d8ba47f726dce15b968e301a795be1f69045f20",
"format": 1
},
{
- "name": "test/awx/test_token.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "118145cdd5f6a03df7a7a608d5f9e510236b2a54f9bcd456f4294ba69f0f4fad",
+ "name": "tests/integration/targets/export/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_notification_template.py",
+ "name": "tests/integration/targets/export/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f40d5b65fbc78d12570f37799c8e240cfb90d9948421d3db82af6427fd14854f",
+ "chksum_sha256": "e66e796b995b7c9ae612e00b393ccd75d9747d2d94ea3fbbaf90832e5b3e9e3f",
"format": 1
},
{
- "name": "test/awx/test_ad_hoc_wait.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "daed2a74d3f64fd0300255050dc8c732158db401323f44da66ccb4bf84b59633",
+ "name": "tests/integration/targets/host",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_instance_group.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "9ce22bf5e6baa63ab096c9377478f8a3af33624def33e52753342e435924e573",
+ "name": "tests/integration/targets/host/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_application.py",
+ "name": "tests/integration/targets/host/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a106d5fbffbe1eaec36d8247979ca637ee733a29abf94d955c48be8d2fd16842",
+ "chksum_sha256": "bc0849abd5d11fe3fa3039ed0ae93d4e812d915e66c47488f91cc4309b2d7d77",
"format": 1
},
{
- "name": "test/awx/test_organization.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "90dacaf268600864f01bdfdb0eb34f0225a605320b5af73754cbc229610e5d24",
+ "name": "tests/integration/targets/schedule_rrule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_completeness.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "9bdbafb6fc36ac375a5674bebe7a603285ff98b891f66e41412518bd6cb4f72a",
+ "name": "tests/integration/targets/schedule_rrule/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_user.py",
+ "name": "tests/integration/targets/schedule_rrule/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f9520b058e16e4e4800d3a5f70cd28650a365fa357afa1d41a8c63bf3354027e",
+ "chksum_sha256": "c8732015804e039989986f1ebfc82da8d426c7d8ab21a7602b101ed16d4c33e8",
"format": 1
},
{
- "name": "test/awx/test_credential_type.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "1fe388a0c19f08006c7718766d5faa79540dd3b14547ced43b5a237a2c2fd877",
+ "name": "tests/integration/targets/label",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_module_utils.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "da19747889f28cba3f49836ef64363a010c6cb78650456183efba297d71f0def",
+ "name": "tests/integration/targets/label/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_bulk.py",
+ "name": "tests/integration/targets/label/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "194490a94b5d7e3444115a0ead711061cce2ac737ecbcdb786c3d673e919f837",
+ "chksum_sha256": "19b0b776638ca5722ddc8fc85fc90fba2639abecbd5dc5bbc6d7ee3c53b5ff16",
"format": 1
},
{
- "name": "test/awx/test_job_template.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "75528184cbc1e92aafc05360990d0280cf64f3bb7049120090ef25a3feb114ac",
+ "name": "tests/integration/targets/credential_input_source",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_group.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "1ecf188e82d4c848de64c8f7fd7af2d4adb6887c6a448771ff51bb43c4fa8128",
+ "name": "tests/integration/targets/credential_input_source/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_role.py",
+ "name": "tests/integration/targets/credential_input_source/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "8f0340f77fd1465cf6e267b301e44ae86c5238b05aa89bd7fff145726a83ebb4",
+ "chksum_sha256": "83dae6f438515cce80bf4b25d24d472de42feafe46b9f8faacaa7cdf18c1eb16",
"format": 1
},
{
- "name": "test/awx/test_inventory.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "70eac0cf78806e37406137fcfb97e5a249fd6b091b1f18e812278573049a4111",
+ "name": "tests/integration/targets/project_update",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_project.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "fbb93e524df51b788f12746ffb52bf5105f67b3ae6b89403bed51ed1f2da9c12",
+ "name": "tests/integration/targets/project_update/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_team.py",
+ "name": "tests/integration/targets/project_update/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "cbbdbdb3be0b0d80dcfcf337ed0095774cf73ef0e937d3e8dc5abab21739db5d",
+ "chksum_sha256": "6fd0d5b30de4d13f2d2e5fb7497c0fbbc13fa92a31fc43d4a21b19d3f53249ce",
"format": 1
},
{
- "name": "test/awx/test_label.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "cd957d0b0cab6dd51539baf3fb27b659b91a8e57b20aae4c5cce7eaec9cec494",
+ "name": "tests/integration/targets/module_utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_workflow_job_template_node.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "0806356bfd91b28153baa63ca8cbf8f7da1125dd5150e38e73aa37c65e236f6b",
+ "name": "tests/integration/targets/module_utils/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/conftest.py",
+ "name": "tests/integration/targets/module_utils/tasks/test_named_reference.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "09e6eaaa58debfcd70ebd6fd92baafd2fea879b59c60f4cfa8c866ae04977a4d",
+ "chksum_sha256": "0e1babbc9e57e06629675f6d2b0d4fe891880b177a572d355391753e670c5156",
"format": 1
},
{
- "name": "test/awx/test_inventory_source.py",
+ "name": "tests/integration/targets/module_utils/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a14f69db1bf6cec594e64c2963b415560b78eac1f9cbe7d4c09586b494e11bde",
+ "chksum_sha256": "2fb7f33e9695e68ec87d2b9bc52ad2196c2d542bccb7d5b185ebc8e54a8cc237",
"format": 1
},
{
- "name": "test/awx/test_credential_input_source.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "9637a418c0b0e59261ec0d1c206ff2d3574a41a8a169068bbf74588e3a4214b2",
+ "name": "tests/integration/targets/user",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_workflow_job_template.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "16c8ebc74606940f6ee1f51a191f22b497c176a46e770e886bbf94bdf0c25842",
+ "name": "tests/integration/targets/user/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_schedule.py",
+ "name": "tests/integration/targets/user/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "dadfd1c19c4c828dd84128ca484b837c6a904a09e92bcee12cb7cda408562c81",
+ "chksum_sha256": "76733e6cbaba31ccf3510bea4ec1166c93195d5ef385035bb773fba854db3659",
"format": 1
},
{
- "name": "test/awx/test_credential.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "1f8348d6f37932997c7971beb8c5f92cf649523e3d3c5d5e859846460d7d1e8d",
+ "name": "tests/integration/targets/notification_template",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_settings.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "436c13933936e7b80dd26c61ea1dbf492c13974f2922f1543c4fe6e6b0fab0dd",
+ "name": "tests/integration/targets/notification_template/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "test/awx/test_job.py",
+ "name": "tests/integration/targets/notification_template/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "76ba45e14438425f7511d196613928d64253e1912a45b71ea842b1cb2c3ca335",
+ "chksum_sha256": "9d4d3cef57b6e950d9af710bb519050823ef67fd6aa1c6b60c6f2fc33387dc67",
"format": 1
},
{
- "name": "plugins",
+ "name": "tests/integration/targets/ad_hoc_command_cancel",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/module_utils",
+ "name": "tests/integration/targets/ad_hoc_command_cancel/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/module_utils/tower_legacy.py",
+ "name": "tests/integration/targets/ad_hoc_command_cancel/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "442535992f6564ac689645ff6e880848762eafc0d93a3255cbe5bedec5eefd58",
+ "chksum_sha256": "1d276bf3b4ada55707a1e8a00284ab15a5c8f9a6bbe0a49f7d03160cda21d3c1",
"format": 1
},
{
- "name": "plugins/module_utils/controller_api.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "999dfa9d68668fa30823eb837ad3703d2be624413b3078441a040468b89dd16f",
+ "name": "tests/integration/targets/instance",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/module_utils/awxkit.py",
+ "name": "tests/integration/targets/instance/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/instance/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "8b2398e4e7893f203b26f6c85d510cc4c41a79c53e1937710807233e62e35f58",
+ "chksum_sha256": "ccd821e64446cc63715163fa16014f10e76c66d58be119bfe1dc1a3b11637c97",
"format": 1
},
{
- "name": "plugins/doc_fragments",
+ "name": "tests/integration/targets/team",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/doc_fragments/auth.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "08510309125b9276dca6553a3c77436c0a225c250eea33d54be356a68a06a5f3",
+ "name": "tests/integration/targets/team/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/doc_fragments/auth_plugin.py",
+ "name": "tests/integration/targets/team/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "80afe672d9386df036747cda82e54091e9717cdecfeab47b8567502b2ac3fbd1",
+ "chksum_sha256": "f59433918d9495fd5a3b296a0a70963f539571e742e3c790e5d88912eec8e2ef",
"format": 1
},
{
- "name": "plugins/doc_fragments/auth_legacy.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "c2f10b81ecb89088c7c295430d4a71de26e3700b26e8344cdc7950908a738fd3",
+ "name": "tests/integration/targets/workflow_job_template",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/inventory",
+ "name": "tests/integration/targets/workflow_job_template/tasks",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/inventory/controller.py",
+ "name": "tests/integration/targets/workflow_job_template/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "29e7cd36a2b18ee616e31cbbec6a6e103f3f18ebe13f2bb87167b915163ca4bf",
+ "chksum_sha256": "872f3cd4d0d7656ddf7f9239c1139351af6341e5e40e0ac2981ed152fff26a5b",
"format": 1
},
{
- "name": "plugins/modules",
+ "name": "tests/integration/targets/job_launch",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/project_update.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "2b06aacf9e51faa8b51fe770d3663e4e6e6d9e382769edf6883cd04414d3cd8c",
+ "name": "tests/integration/targets/job_launch/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/project.py",
+ "name": "tests/integration/targets/job_launch/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c93ddb644de67226bd22d27d5490613c447b80fcf561927445af157e8c796674",
+ "chksum_sha256": "fbb658e8212fcb120200db84212f6b469fff700c1fd233b486ec82a816572323",
"format": 1
},
{
- "name": "plugins/modules/workflow_job_template.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "c8a3498557011c1fa118006166aabb13a9ddba4eaca5a50d5469214b49d8153b",
+ "name": "tests/integration/targets/bulk_host_delete",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/credential_input_source.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "d90dd76b3b2a42ceaf423d05755c4c61bc565370f7905aecf9a516172761b60b",
+ "name": "tests/integration/targets/bulk_host_delete/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/credential.py",
+ "name": "tests/integration/targets/bulk_host_delete/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "6a1882a9893a096ae4f0a89028e4447719daa33051eaf54919e88d07a3df8ef5",
+ "chksum_sha256": "2a55e10b05593a8ad6f5e2325c3d51f76b5ef040a81990dc3c7a4adaeebcf545",
"format": 1
},
{
- "name": "plugins/modules/job_wait.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "7e7459abf351f6c172401eec4ba579dc8566f8a55fd022cc8eec9fa5a3399067",
+ "name": "tests/integration/targets/bulk_host_create",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/ad_hoc_command.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "4dcafb33a0487b4200a6abcf3283dd55335de9102a2740c93e24b0e9e7ef224d",
+ "name": "tests/integration/targets/bulk_host_create/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/credential_type.py",
+ "name": "tests/integration/targets/bulk_host_create/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c56a3cf4eddc284b0c83e55a3f58d19a9d315a68abf513d232c3fe5b81ec85f3",
+ "chksum_sha256": "39be6c097c03152ec1873732d8e09e34639464acad4f9d42ef3e13b6f87d47d1",
"format": 1
},
{
- "name": "plugins/modules/workflow_launch.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "91eabbcdfed14efb72a6b02db83cd4f92c811a77e55119e9b0fefb6453eee953",
+ "name": "tests/integration/targets/bulk_job_launch",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/workflow_node_wait.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "5b19778b005fbaa3e0a3abc645a6d6452bc0ad52e89fe04141d051f6ddafbb73",
+ "name": "tests/integration/targets/bulk_job_launch/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/ad_hoc_command_wait.py",
+ "name": "tests/integration/targets/bulk_job_launch/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "67bc716ec04dfc77cb751cda6013ee54fa0cd3ed3afabc5ba0d146cc9712c996",
+ "chksum_sha256": "bbb7480d55d9d2261e22d0c55de3a0bada691608a6542385b234633f0493be75",
"format": 1
},
{
- "name": "plugins/modules/subscriptions.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "f497ab9ada8f89650422bf85deef386e32b774dfff9e1de07b387fba32d890a8",
+ "name": "tests/integration/targets/workflow_launch",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/role.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "26f6dfe334c409b0ead538ff1c9a1c20c88d673db374fabdd5b3cfaeeb30e70e",
+ "name": "tests/integration/targets/workflow_launch/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/inventory_source.py",
+ "name": "tests/integration/targets/workflow_launch/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "b939811acd5dd6100b7436633fd3d537d4c44c9006bf29bfa51574db744c0838",
+ "chksum_sha256": "4ceb05ce9ac229472ee12b492754aeacba13b8060a8e973467aba8a9d3d793b6",
"format": 1
},
{
- "name": "plugins/modules/application.py",
+ "name": "requirements.txt",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a932d66c23f578fc62a733ac466f5732d0ed2d2192252b108de21c4da219880c",
+ "chksum_sha256": "2eb11923e1347afc5075a7871e206a8f15a68471c90012f7386e9db0875e70bf",
"format": 1
},
{
- "name": "plugins/modules/user.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "5bca255128e1f376a15622a9dfbf6a469c23f6d9528a5df6e318a503402214e6",
+ "name": "images",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/host.py",
+ "name": "images/completeness_test_output.png",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e42e558d5f91555f6f4f32b186d32d2920aad6126b164448c6258a1ee9f847ef",
+ "chksum_sha256": "6367684c4b5edd3e1e8fdcb9270d68ca54040d5d17108734f3d3a2b9df5878ba",
"format": 1
},
{
- "name": "plugins/modules/job_template.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "8111cffd44f026c9997ab2315ea3a2fd984754caa5953a89ed6da9d9a257bcd1",
+ "name": "test",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/organization.py",
- "ftype": "file",
- "chksum_type": "sha256",
- "chksum_sha256": "020576aef74ec4574dbe35eab8323fcffa7bd93d08a092310949e7bcec0eb196",
+ "name": "test/awx",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
- "name": "plugins/modules/job_cancel.py",
+ "name": "test/awx/test_ad_hoc_wait.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "d64f698909919b05c9c47a65f24c861c3cabe33c039944f6120d49a2ac7d40da",
+ "chksum_sha256": "daed2a74d3f64fd0300255050dc8c732158db401323f44da66ccb4bf84b59633",
"format": 1
},
{
- "name": "plugins/modules/job_list.py",
+ "name": "test/awx/test_notification_template.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "2ea8024bfc9612c005745a13a508c40d320b4c204bf18fcd495f72789d9adb40",
+ "chksum_sha256": "3e9aee01e9d7d3c3662b6204422d907b78323440ebdc836b69f933336ec36b0f",
"format": 1
},
{
- "name": "plugins/modules/__init__.py",
+ "name": "test/awx/test_label.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "chksum_sha256": "cd957d0b0cab6dd51539baf3fb27b659b91a8e57b20aae4c5cce7eaec9cec494",
"format": 1
},
{
- "name": "plugins/modules/bulk_job_launch.py",
+ "name": "test/awx/test_export.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "30167cd8d30e054fbc327308b5a9d407729c568a70bb834b8ba565969f6bc4f4",
+ "chksum_sha256": "c24a35265af8ff90f6456d39d0cc84cc9ce765d9fc3d45a4a8ac945e2538ff6b",
"format": 1
},
{
- "name": "plugins/modules/ad_hoc_command_cancel.py",
+ "name": "test/awx/test_module_utils.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3338e10af9ccd0e4178b8e1ec1e7064b00ab90e64665f846a2123f10d9d151f4",
+ "chksum_sha256": "a5118e383f1370175dc7900ec3abae2ee53bd77ecf8853ca333ffcbf625b216f",
"format": 1
},
{
- "name": "plugins/modules/token.py",
+ "name": "test/awx/test_completeness.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "df79756cfc32e63b15e46c6bed1502c780db0257f54fcecf617960285c0f3286",
+ "chksum_sha256": "56bc8d4297a9c7c4fdb712fd838803cc9233023288f5555f3e3d229117604eee",
"format": 1
},
{
- "name": "plugins/modules/instance.py",
+ "name": "test/awx/test_user.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ae8cbc633720f99c822f3cd5fe459605b241f0db6152fb4293f238864c0b7513",
+ "chksum_sha256": "f9520b058e16e4e4800d3a5f70cd28650a365fa357afa1d41a8c63bf3354027e",
"format": 1
},
{
- "name": "plugins/modules/bulk_host_create.py",
+ "name": "test/awx/test_inventory_source.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ddd2c181c2e32a25a801afbe206514edd938cb372aa17a5f265f3f7eaba78996",
+ "chksum_sha256": "a14f69db1bf6cec594e64c2963b415560b78eac1f9cbe7d4c09586b494e11bde",
"format": 1
},
{
- "name": "plugins/modules/job_launch.py",
+ "name": "test/awx/test_group.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "adb57a4499f754bce74741f5a15ab5d00bf318199796180a627549c6699693e2",
+ "chksum_sha256": "1ecf188e82d4c848de64c8f7fd7af2d4adb6887c6a448771ff51bb43c4fa8128",
"format": 1
},
{
- "name": "plugins/modules/execution_environment.py",
+ "name": "test/awx/test_project.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "219ffa0fca58cbfa76c1dba8dba9d3060b1e6816c84a201241645b90b59a75c0",
+ "chksum_sha256": "fbb93e524df51b788f12746ffb52bf5105f67b3ae6b89403bed51ed1f2da9c12",
"format": 1
},
{
- "name": "plugins/modules/workflow_job_template_node.py",
+ "name": "test/awx/test_job.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "37f6e42c3ba5ab5c6df1c9c7d336e0377346cad811aee6a9ed6004f29770adb8",
+ "chksum_sha256": "76ba45e14438425f7511d196613928d64253e1912a45b71ea842b1cb2c3ca335",
"format": 1
},
{
- "name": "plugins/modules/group.py",
+ "name": "test/awx/test_job_template.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5aaa56a12e55ed92aba4a591ef493d013df47dcd29371664837f5405ff52631f",
+ "chksum_sha256": "2f0e924d79cd0b2bccbe4664e27ec07cb96a274d289229d80be09687f099171e",
"format": 1
},
{
- "name": "plugins/modules/label.py",
+ "name": "test/awx/test_bulk.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "65e6ecc50888ebfae7498ba414325a715363676520af41f65aa8a0cecc19ea9d",
+ "chksum_sha256": "a10cc6ca47f1cd560e5adff57f88b4628b6ff3ec20874a751ae57d7474cfb7d5",
"format": 1
},
{
- "name": "plugins/modules/inventory_source_update.py",
+ "name": "test/awx/test_role.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "8a942beea88f174b16ee262e2332e26a1de2744d88fccfe79aa7b11a11fbf9dc",
+ "chksum_sha256": "8f0340f77fd1465cf6e267b301e44ae86c5238b05aa89bd7fff145726a83ebb4",
"format": 1
},
{
- "name": "plugins/modules/inventory.py",
+ "name": "test/awx/test_token.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "54620e4006a83c9641baefdbe4a8b953ee124dc1654a3ccae487461db2b853b4",
+ "chksum_sha256": "118145cdd5f6a03df7a7a608d5f9e510236b2a54f9bcd456f4294ba69f0f4fad",
"format": 1
},
{
- "name": "plugins/modules/notification_template.py",
+ "name": "test/awx/test_workflow_job_template_node.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "473f8d494ba4356c93b76ccc3b25e95ea5afd6f413ee30d244070e2e7ffd66bd",
+ "chksum_sha256": "0806356bfd91b28153baa63ca8cbf8f7da1125dd5150e38e73aa37c65e236f6b",
"format": 1
},
{
- "name": "plugins/modules/license.py",
+ "name": "test/awx/test_instance.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "d285b03abaf448db184ec0304d95206115e7d3f0cf28adba009c0c84084f5f52",
+ "chksum_sha256": "2b60fecf79c63341b0eece0d9941a655dfeac89b3565e78f5bb39c5ec77b91af",
"format": 1
},
{
- "name": "plugins/modules/controller_meta.py",
+ "name": "test/awx/test_credential.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c66ebbe3a0eab6a9d28d517824ebf8478afdf14981c6c931f08592503c243cdd",
+ "chksum_sha256": "c1bfcf99f4de13b3cc4a76c7f33fa3a51fde175afff5730b4743695cbeb69643",
"format": 1
},
{
- "name": "plugins/modules/workflow_approval.py",
+ "name": "test/awx/test_credential_type.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "533e52fc20ca99e935154f5ba3ec30c2055f42d1c51c3bc2cf1570af7f951c33",
+ "chksum_sha256": "1fe388a0c19f08006c7718766d5faa79540dd3b14547ced43b5a237a2c2fd877",
"format": 1
},
{
- "name": "plugins/modules/import.py",
+ "name": "test/awx/test_credential_input_source.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a7a03186251ef644ba03c49e7e23a799f8046abddb9ea20fff68dd09fe759680",
+ "chksum_sha256": "9637a418c0b0e59261ec0d1c206ff2d3574a41a8a169068bbf74588e3a4214b2",
"format": 1
},
{
- "name": "plugins/modules/team.py",
+ "name": "test/awx/test_instance_group.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "d2ace1a41e1456f7d187ad7a1d3becdddd27cd945dcee863a048add0dbfac9f6",
+ "chksum_sha256": "9ce22bf5e6baa63ab096c9377478f8a3af33624def33e52753342e435924e573",
"format": 1
},
{
- "name": "plugins/modules/export.py",
+ "name": "test/awx/test_organization.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "478b9c1a9808f40a284d733e9dd9739767bdfa5ddf6c14720bc8f325a5433195",
+ "chksum_sha256": "091d48906e4bf5ffaceead8f49281e05184cefce1546f387ce29a232d6694ec9",
"format": 1
},
{
- "name": "plugins/modules/instance_group.py",
+ "name": "test/awx/test_workflow_job_template.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "62bfe82b93ddeafcc72bf9c8a7a12fec6df00bf42d7b5e2c55de17053de276da",
+ "chksum_sha256": "16c8ebc74606940f6ee1f51a191f22b497c176a46e770e886bbf94bdf0c25842",
"format": 1
},
{
- "name": "plugins/modules/schedule.py",
+ "name": "test/awx/conftest.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "42f1c845cb65fc43b5ccc1a08d98ea4cc4b4d0aefbba3c88a454e3497a711e19",
+ "chksum_sha256": "8eb2d805f88e99f1c63aeb256ef1ac720831191d17a81b5b3bb2f9c06b29544e",
"format": 1
},
{
- "name": "plugins/modules/settings.py",
+ "name": "test/awx/test_settings.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "6a382df72aa10c2d5402a2a66b430c789b0b3588c3f1dca226f9ad09b01c9bdb",
+ "chksum_sha256": "436c13933936e7b80dd26c61ea1dbf492c13974f2922f1543c4fe6e6b0fab0dd",
"format": 1
},
{
- "name": "plugins/lookup",
- "ftype": "dir",
- "chksum_type": null,
- "chksum_sha256": null,
+ "name": "test/awx/test_team.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cbbdbdb3be0b0d80dcfcf337ed0095774cf73ef0e937d3e8dc5abab21739db5d",
"format": 1
},
{
- "name": "plugins/lookup/schedule_rruleset.py",
+ "name": "test/awx/test_application.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "be96c1d747f985ea531835277bd751af13e109f326413a45fd5c5cc1093c7414",
+ "chksum_sha256": "a106d5fbffbe1eaec36d8247979ca637ee733a29abf94d955c48be8d2fd16842",
"format": 1
},
{
- "name": "plugins/lookup/schedule_rrule.py",
+ "name": "test/awx/test_schedule.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c3ec7b8f134eca3a9f04156213b584792fc4e3397e3b9f82b5044e9ec662c7a2",
+ "chksum_sha256": "dadfd1c19c4c828dd84128ca484b837c6a904a09e92bcee12cb7cda408562c81",
"format": 1
},
{
- "name": "plugins/lookup/controller_api.py",
+ "name": "test/awx/test_inventory.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5e79f19c9dee4fa0c3a88126a630fa6163249c332d73a44370f64836e22d4b27",
+ "chksum_sha256": "70eac0cf78806e37406137fcfb97e5a249fd6b091b1f18e812278573049a4111",
"format": 1
},
{
- "name": "requirements.txt",
+ "name": "TESTING.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "2eb11923e1347afc5075a7871e206a8f15a68471c90012f7386e9db0875e70bf",
+ "chksum_sha256": "4691e79c8038d8e985610fb613cd2f4799d4740b0a6ca1b72d3266528088a272",
"format": 1
}
],
diff --git a/ansible_collections/awx/awx/MANIFEST.json b/ansible_collections/awx/awx/MANIFEST.json
index 31be1f1d8..9480679a6 100644
--- a/ansible_collections/awx/awx/MANIFEST.json
+++ b/ansible_collections/awx/awx/MANIFEST.json
@@ -2,7 +2,7 @@
"collection_info": {
"namespace": "awx",
"name": "awx",
- "version": "21.14.0",
+ "version": "23.9.0",
"authors": [
"AWX Project Contributors <awx-project@googlegroups.com>"
],
@@ -16,7 +16,7 @@
],
"description": "Ansible content that interacts with the AWX or Automation Platform Controller API.",
"license": [
- "GPL-3.0-only"
+ "GPL-3.0-or-later"
],
"license_file": null,
"dependencies": {},
@@ -29,7 +29,7 @@
"name": "FILES.json",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f4ba3e6ff1189b8f123c73022f3b612c1bcc2f3cc136f5509c722df28a709869",
+ "chksum_sha256": "b47d01dc90de9b4af46e366ac75ac9d533d4abfcc93a81b6956b098d2989f587",
"format": 1
},
"format": 1
diff --git a/ansible_collections/awx/awx/README.md b/ansible_collections/awx/awx/README.md
index ea9e85e11..c3b6f9d52 100644
--- a/ansible_collections/awx/awx/README.md
+++ b/ansible_collections/awx/awx/README.md
@@ -69,6 +69,7 @@ Notable releases of the `awx.awx` collection:
- 11.0.0 has no non-deprecated modules that depend on the deprecated `tower-cli` [PyPI](https://pypi.org/project/ansible-tower-cli/).
- 19.2.1 large renaming purged "tower" names (like options and module names), adding redirects for old names
- 21.11.0 "tower" modules deprecated and symlinks removed.
+ - X.X.X added support of named URLs to all modules. Anywhere that previously accepted name or id can also support named URLs
- 0.0.1-devel is the version you should see if installing from source, which is intended for development and expected to be unstable.
The following notes are changes that may require changes to playbooks:
@@ -112,7 +113,7 @@ Ansible source, set up a dedicated virtual environment:
```
mkvirtualenv my_new_venv
-# may need to replace psycopg2 with psycopg2-binary in requirements/requirements.txt
+# may need to replace psycopg3 with psycopg3-binary in requirements/requirements.txt
pip install -r requirements/requirements.txt -r requirements/requirements_dev.txt -r requirements/requirements_git.txt
make clean-api
pip install -e <path to your Ansible>
diff --git a/ansible_collections/awx/awx/meta/runtime.yml b/ansible_collections/awx/awx/meta/runtime.yml
index 3e50a52e6..18fa4b592 100644
--- a/ansible_collections/awx/awx/meta/runtime.yml
+++ b/ansible_collections/awx/awx/meta/runtime.yml
@@ -8,6 +8,7 @@ action_groups:
- application
- bulk_job_launch
- bulk_host_create
+ - bulk_host_delete
- controller_meta
- credential_input_source
- credential
diff --git a/ansible_collections/awx/awx/plugins/doc_fragments/auth.py b/ansible_collections/awx/awx/plugins/doc_fragments/auth.py
index 3cab718a7..763fe94dc 100644
--- a/ansible_collections/awx/awx/plugins/doc_fragments/auth.py
+++ b/ansible_collections/awx/awx/plugins/doc_fragments/auth.py
@@ -50,6 +50,11 @@ options:
- If value not set, will try environment variable C(CONTROLLER_VERIFY_SSL) and then config files
type: bool
aliases: [ tower_verify_ssl ]
+ request_timeout:
+ description:
+ - Specify the timeout Ansible should use in requests to the controller host.
+ - Defaults to 10s, but this is handled by the shared module_utils code
+ type: float
controller_config_file:
description:
- Path to the controller config file.
diff --git a/ansible_collections/awx/awx/plugins/doc_fragments/auth_plugin.py b/ansible_collections/awx/awx/plugins/doc_fragments/auth_plugin.py
index 5a3a12b0e..b46eaf6bb 100644
--- a/ansible_collections/awx/awx/plugins/doc_fragments/auth_plugin.py
+++ b/ansible_collections/awx/awx/plugins/doc_fragments/auth_plugin.py
@@ -68,6 +68,14 @@ options:
why: Collection name change
alternatives: 'CONTROLLER_VERIFY_SSL'
aliases: [ validate_certs ]
+ request_timeout:
+ description:
+ - Specify the timeout Ansible should use in requests to the controller host.
+ - Defaults to 10 seconds
+ - This will not work with the export or import modules.
+ type: float
+ env:
+ - name: CONTROLLER_REQUEST_TIMEOUT
notes:
- If no I(config_file) is provided we will attempt to use the tower-cli library
diff --git a/ansible_collections/awx/awx/plugins/lookup/schedule_rruleset.py b/ansible_collections/awx/awx/plugins/lookup/schedule_rruleset.py
index b45d861db..07e990800 100644
--- a/ansible_collections/awx/awx/plugins/lookup/schedule_rruleset.py
+++ b/ansible_collections/awx/awx/plugins/lookup/schedule_rruleset.py
@@ -214,7 +214,7 @@ class LookupModule(LookupBase):
if not isinstance(rule[field_name], list):
rule[field_name] = rule[field_name].split(',')
for value in rule[field_name]:
- value = value.strip()
+ value = value.strip().lower()
if value not in valid_list:
raise AnsibleError('In rule {0} {1} must only contain values in {2}'.format(rule_number, field_name, ', '.join(valid_list.keys())))
return_values.append(valid_list[value])
diff --git a/ansible_collections/awx/awx/plugins/module_utils/controller_api.py b/ansible_collections/awx/awx/plugins/module_utils/controller_api.py
index abf9273d7..8bc3955ce 100644
--- a/ansible_collections/awx/awx/plugins/module_utils/controller_api.py
+++ b/ansible_collections/awx/awx/plugins/module_utils/controller_api.py
@@ -10,14 +10,14 @@ from ansible.module_utils.six import raise_from, string_types
from ansible.module_utils.six.moves import StringIO
from ansible.module_utils.six.moves.urllib.error import HTTPError
from ansible.module_utils.six.moves.http_cookiejar import CookieJar
-from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode
+from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode, quote
from ansible.module_utils.six.moves.configparser import ConfigParser, NoOptionError
from socket import getaddrinfo, IPPROTO_TCP
import time
import re
from json import loads, dumps
from os.path import isfile, expanduser, split, join, exists, isdir
-from os import access, R_OK, getcwd
+from os import access, R_OK, getcwd, environ
try:
@@ -47,47 +47,31 @@ class ItemNotDefined(Exception):
class ControllerModule(AnsibleModule):
url = None
AUTH_ARGSPEC = dict(
- controller_host=dict(
- required=False,
- aliases=['tower_host'],
- fallback=(env_fallback, ['CONTROLLER_HOST', 'TOWER_HOST'])),
- controller_username=dict(
- required=False,
- aliases=['tower_username'],
- fallback=(env_fallback, ['CONTROLLER_USERNAME', 'TOWER_USERNAME'])),
- controller_password=dict(
- no_log=True,
- aliases=['tower_password'],
- required=False,
- fallback=(env_fallback, ['CONTROLLER_PASSWORD', 'TOWER_PASSWORD'])),
- validate_certs=dict(
- type='bool',
- aliases=['tower_verify_ssl'],
- required=False,
- fallback=(env_fallback, ['CONTROLLER_VERIFY_SSL', 'TOWER_VERIFY_SSL'])),
+ controller_host=dict(required=False, aliases=['tower_host'], fallback=(env_fallback, ['CONTROLLER_HOST', 'TOWER_HOST'])),
+ controller_username=dict(required=False, aliases=['tower_username'], fallback=(env_fallback, ['CONTROLLER_USERNAME', 'TOWER_USERNAME'])),
+ controller_password=dict(no_log=True, aliases=['tower_password'], required=False, fallback=(env_fallback, ['CONTROLLER_PASSWORD', 'TOWER_PASSWORD'])),
+ validate_certs=dict(type='bool', aliases=['tower_verify_ssl'], required=False, fallback=(env_fallback, ['CONTROLLER_VERIFY_SSL', 'TOWER_VERIFY_SSL'])),
+ request_timeout=dict(type='float', required=False, fallback=(env_fallback, ['CONTROLLER_REQUEST_TIMEOUT'])),
controller_oauthtoken=dict(
- type='raw',
- no_log=True,
- aliases=['tower_oauthtoken'],
- required=False,
- fallback=(env_fallback, ['CONTROLLER_OAUTH_TOKEN', 'TOWER_OAUTH_TOKEN'])),
- controller_config_file=dict(
- type='path',
- aliases=['tower_config_file'],
- required=False,
- default=None),
+ type='raw', no_log=True, aliases=['tower_oauthtoken'], required=False, fallback=(env_fallback, ['CONTROLLER_OAUTH_TOKEN', 'TOWER_OAUTH_TOKEN'])
+ ),
+ controller_config_file=dict(type='path', aliases=['tower_config_file'], required=False, default=None),
)
+ # Associations of these types are ordered and have special consideration in the modified associations function
+ ordered_associations = ['instance_groups', 'galaxy_credentials']
short_params = {
'host': 'controller_host',
'username': 'controller_username',
'password': 'controller_password',
'verify_ssl': 'validate_certs',
+ 'request_timeout': 'request_timeout',
'oauth_token': 'controller_oauthtoken',
}
host = '127.0.0.1'
username = None
password = None
verify_ssl = True
+ request_timeout = 10
oauth_token = None
oauth_token_id = None
authenticated = False
@@ -152,9 +136,11 @@ class ControllerModule(AnsibleModule):
self.url.hostname.replace(char, "")
# Try to resolve the hostname
try:
- addrinfolist = getaddrinfo(self.url.hostname, self.url.port, proto=IPPROTO_TCP)
- for family, kind, proto, canonical, sockaddr in addrinfolist:
- sockaddr[0]
+ proxy_env_var_name = "{0}_proxy".format(self.url.scheme)
+ if not environ.get(proxy_env_var_name) and not environ.get(proxy_env_var_name.upper()):
+ addrinfolist = getaddrinfo(self.url.hostname, self.url.port, proto=IPPROTO_TCP)
+ for family, kind, proto, canonical, sockaddr in addrinfolist:
+ sockaddr[0]
except Exception as e:
self.fail_json(msg="Unable to resolve controller_host ({1}): {0}".format(self.url.hostname, e))
@@ -305,7 +291,7 @@ class ControllerModule(AnsibleModule):
class ControllerAPIModule(ControllerModule):
# TODO: Move the collection version check into controller_module.py
# This gets set by the make process so whatever is in here is irrelevant
- _COLLECTION_VERSION = "21.14.0"
+ _COLLECTION_VERSION = "23.9.0"
_COLLECTION_TYPE = "awx"
# This maps the collections type (awx/tower) to the values returned by the API
# Those values can be found in awx/api/generics.py line 204
@@ -320,10 +306,8 @@ class ControllerAPIModule(ControllerModule):
def __init__(self, argument_spec, direct_params=None, error_callback=None, warn_callback=None, **kwargs):
kwargs['supports_check_mode'] = True
- super().__init__(
- argument_spec=argument_spec, direct_params=direct_params, error_callback=error_callback, warn_callback=warn_callback, **kwargs
- )
- self.session = Request(cookies=CookieJar(), validate_certs=self.verify_ssl)
+ super().__init__(argument_spec=argument_spec, direct_params=direct_params, error_callback=error_callback, warn_callback=warn_callback, **kwargs)
+ self.session = Request(cookies=CookieJar(), timeout=self.request_timeout, validate_certs=self.verify_ssl)
if 'update_secrets' in self.params:
self.update_secrets = self.params.pop('update_secrets')
@@ -331,11 +315,6 @@ class ControllerAPIModule(ControllerModule):
self.update_secrets = True
@staticmethod
- def param_to_endpoint(name):
- exceptions = {'inventory': 'inventories', 'target_team': 'teams', 'workflow': 'workflow_job_templates'}
- return exceptions.get(name, '{0}s'.format(name))
-
- @staticmethod
def get_name_field_from_endpoint(endpoint):
return ControllerAPIModule.IDENTITY_FIELDS.get(endpoint, 'name')
@@ -405,31 +384,53 @@ class ControllerAPIModule(ControllerModule):
response['json']['next'] = next_page
return response
- def get_one(self, endpoint, name_or_id=None, allow_none=True, **kwargs):
+ def get_one(self, endpoint, name_or_id=None, allow_none=True, check_exists=False, **kwargs):
new_kwargs = kwargs.copy()
- if name_or_id:
- name_field = self.get_name_field_from_endpoint(endpoint)
- new_data = kwargs.get('data', {}).copy()
- if name_field in new_data:
- self.fail_json(msg="You can't specify the field {0} in your search data if using the name_or_id field".format(name_field))
+ response = None
- try:
- new_data['or__id'] = int(name_or_id)
- new_data['or__{0}'.format(name_field)] = name_or_id
- except ValueError:
- # If we get a value error, then we didn't have an integer so we can just pass and fall down to the fail
- new_data[name_field] = name_or_id
- new_kwargs['data'] = new_data
-
- response = self.get_endpoint(endpoint, **new_kwargs)
- if response['status_code'] != 200:
- fail_msg = "Got a {0} response when trying to get one from {1}".format(response['status_code'], endpoint)
- if 'detail' in response.get('json', {}):
- fail_msg += ', detail: {0}'.format(response['json']['detail'])
- self.fail_json(msg=fail_msg)
-
- if 'count' not in response['json'] or 'results' not in response['json']:
- self.fail_json(msg="The endpoint did not provide count and results")
+ # A named URL is pretty unique so if we have a ++ in the name then lets start by looking for that
+ # This also needs to go first because if there was data passed in kwargs and we do the next lookup first there may be results
+ if name_or_id is not None and '++' in name_or_id:
+ # Maybe someone gave us a named URL so lets see if we get anything from that.
+ url_quoted_name = quote(name_or_id, safe="+")
+ named_endpoint = '{0}/{1}/'.format(endpoint, url_quoted_name)
+ named_response = self.get_endpoint(named_endpoint)
+
+ if named_response['status_code'] == 200 and 'json' in named_response:
+ # We found a named item but we expect to deal with a list view so mock that up
+ response = {
+ 'json': {
+ 'count': 1,
+ 'results': [named_response['json']],
+ }
+ }
+
+ # Since we didn't have a named URL, lets try and find it with a general search
+ if response is None:
+ if name_or_id:
+ name_field = self.get_name_field_from_endpoint(endpoint)
+ new_data = kwargs.get('data', {}).copy()
+ if name_field in new_data:
+ self.fail_json(msg="You can't specify the field {0} in your search data if using the name_or_id field".format(name_field))
+
+ try:
+ new_data['or__id'] = int(name_or_id)
+ new_data['or__{0}'.format(name_field)] = name_or_id
+ except ValueError:
+ # If we get a value error, then we didn't have an integer so we can just pass and fall down to the fail
+ new_data[name_field] = name_or_id
+ new_kwargs['data'] = new_data
+
+ response = self.get_endpoint(endpoint, **new_kwargs)
+
+ if response['status_code'] != 200:
+ fail_msg = "Got a {0} response when trying to get one from {1}".format(response['status_code'], endpoint)
+ if 'detail' in response.get('json', {}):
+ fail_msg += ', detail: {0}'.format(response['json']['detail'])
+ self.fail_json(msg=fail_msg)
+
+ if 'count' not in response['json'] or 'results' not in response['json']:
+ self.fail_json(msg="The endpoint did not provide count and results")
if response['json']['count'] == 0:
if allow_none:
@@ -446,6 +447,10 @@ class ControllerAPIModule(ControllerModule):
# Or we weren't running with a or search and just got back too many to begin with.
self.fail_wanted_one(response, endpoint, new_kwargs.get('data'))
+ if check_exists:
+ self.json_output['id'] = response['json']['results'][0]['id']
+ self.exit_json(**self.json_output)
+
return response['json']['results'][0]
def fail_wanted_one(self, response, endpoint, query_params):
@@ -453,7 +458,8 @@ class ControllerAPIModule(ControllerModule):
if len(sample['json']['results']) > 1:
sample['json']['results'] = sample['json']['results'][:2] + ['...more results snipped...']
url = self.build_url(endpoint, query_params)
- display_endpoint = url.geturl()[len(self.host):] # truncate to not include the base URL
+ host_length = len(self.host)
+ display_endpoint = url.geturl()[host_length:] # truncate to not include the base URL
self.fail_json(
msg="Request to {0} returned {1} items, expected 1".format(display_endpoint, response['json']['count']),
query=query_params,
@@ -497,7 +503,14 @@ class ControllerAPIModule(ControllerModule):
data = dumps(kwargs.get('data', {}))
try:
- response = self.session.open(method, url.geturl(), headers=headers, validate_certs=self.verify_ssl, follow_redirects=True, data=data)
+ response = self.session.open(
+ method, url.geturl(),
+ headers=headers,
+ timeout=self.request_timeout,
+ validate_certs=self.verify_ssl,
+ follow_redirects=True,
+ data=data
+ )
except (SSLValidationError) as ssl_err:
self.fail_json(msg="Could not establish a secure connection to your host ({1}): {0}.".format(url.netloc, ssl_err))
except (ConnectionError) as con_err:
@@ -551,13 +564,7 @@ class ControllerAPIModule(ControllerModule):
controller_version = response.info().getheader('X-API-Product-Version', None)
parsed_collection_version = Version(self._COLLECTION_VERSION).version
- if not controller_version:
- self.warn(
- "You are using the {0} version of this collection but connecting to a controller that did not return a version".format(
- self._COLLECTION_VERSION
- )
- )
- else:
+ if controller_version:
parsed_controller_version = Version(controller_version).version
if controller_type == 'AWX':
collection_compare_ver = parsed_collection_version[0]
@@ -615,6 +622,7 @@ class ControllerAPIModule(ControllerModule):
'POST',
api_token_url,
validate_certs=self.verify_ssl,
+ timeout=self.request_timeout,
follow_redirects=True,
force_basic_auth=True,
url_username=self.username,
@@ -700,17 +708,26 @@ class ControllerAPIModule(ControllerModule):
response = self.get_all_endpoint(association_endpoint)
existing_associated_ids = [association['id'] for association in response['json']['results']]
- # Disassociate anything that is in existing_associated_ids but not in new_association_list
- ids_to_remove = list(set(existing_associated_ids) - set(new_association_list))
- for an_id in ids_to_remove:
+ # Some associations can be ordered (like galaxy credentials)
+ if association_endpoint.strip('/').split('/')[-1] in self.ordered_associations:
+ if existing_associated_ids == new_association_list:
+ return # If the current associations EXACTLY match the desired associations then we can return
+ removal_list = existing_associated_ids # because of ordering, we have to remove everything
+ addition_list = new_association_list # re-add everything back in-order
+ else:
+ if set(existing_associated_ids) == set(new_association_list):
+ return
+ removal_list = set(existing_associated_ids) - set(new_association_list)
+ addition_list = set(new_association_list) - set(existing_associated_ids)
+
+ for an_id in removal_list:
response = self.post_endpoint(association_endpoint, **{'data': {'id': int(an_id), 'disassociate': True}})
if response['status_code'] == 204:
self.json_output['changed'] = True
else:
self.fail_json(msg="Failed to disassociate item {0}".format(response['json'].get('detail', response['json'])))
- # Associate anything that is in new_association_list but not in `association`
- for an_id in list(set(new_association_list) - set(existing_associated_ids)):
+ for an_id in addition_list:
response = self.post_endpoint(association_endpoint, **{'data': {'id': int(an_id)}})
if response['status_code'] == 204:
self.json_output['changed'] = True
@@ -963,6 +980,15 @@ class ControllerAPIModule(ControllerModule):
def create_or_update_if_needed(
self, existing_item, new_item, endpoint=None, item_type='unknown', on_create=None, on_update=None, auto_exit=True, associations=None
):
+ # Remove boolean values of certain specific types
+ # this is needed so that boolean fields will not get a false value when not provided
+ for key in list(new_item.keys()):
+ if key in self.argument_spec:
+ param_spec = self.argument_spec[key]
+ if 'type' in param_spec and param_spec['type'] == 'bool':
+ if new_item[key] is None:
+ new_item.pop(key)
+
if existing_item:
return self.update_if_needed(existing_item, new_item, on_update=on_update, auto_exit=auto_exit, associations=associations)
else:
@@ -975,17 +1001,14 @@ class ControllerAPIModule(ControllerModule):
# Attempt to delete our current token from /api/v2/tokens/
# Post to the tokens endpoint with baisc auth to try and get a token
endpoint = self.url_prefix.rstrip('/') + '/api/v2/tokens/{0}/'.format(self.oauth_token_id)
- api_token_url = (
- self.url._replace(
- path=endpoint, query=None # in error cases, fail_json exists before exception handling
- )
- ).geturl()
+ api_token_url = (self.url._replace(path=endpoint, query=None)).geturl() # in error cases, fail_json exists before exception handling
try:
self.session.open(
'DELETE',
api_token_url,
validate_certs=self.verify_ssl,
+ timeout=self.request_timeout,
follow_redirects=True,
force_basic_auth=True,
url_username=self.username,
diff --git a/ansible_collections/awx/awx/plugins/module_utils/tower_legacy.py b/ansible_collections/awx/awx/plugins/module_utils/tower_legacy.py
deleted file mode 100644
index 84205c0f8..000000000
--- a/ansible_collections/awx/awx/plugins/module_utils/tower_legacy.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# This code is part of Ansible, but is an independent component.
-# This particular file snippet, and this file snippet only, is BSD licensed.
-# Modules you write using this snippet, which is embedded dynamically by Ansible
-# still belong to the author of the module, and may assign their own license
-# to the complete work.
-#
-# Copyright (c), Wayne Witzel III <wayne@riotousliving.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without modification,
-# are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-import os
-import traceback
-
-TOWER_CLI_IMP_ERR = None
-try:
- import tower_cli.utils.exceptions as exc
- from tower_cli.utils import parser
- from tower_cli.api import client
-
- HAS_TOWER_CLI = True
-except ImportError:
- TOWER_CLI_IMP_ERR = traceback.format_exc()
- HAS_TOWER_CLI = False
-
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-
-
-def tower_auth_config(module):
- """
- `tower_auth_config` attempts to load the tower-cli.cfg file
- specified from the `tower_config_file` parameter. If found,
- if returns the contents of the file as a dictionary, else
- it will attempt to fetch values from the module params and
- only pass those values that have been set.
- """
- config_file = module.params.pop('tower_config_file', None)
- if config_file:
- if not os.path.exists(config_file):
- module.fail_json(msg='file not found: %s' % config_file)
- if os.path.isdir(config_file):
- module.fail_json(msg='directory can not be used as config file: %s' % config_file)
-
- with open(config_file, 'r') as f:
- return parser.string_to_dict(f.read())
- else:
- auth_config = {}
- host = module.params.pop('tower_host', None)
- if host:
- auth_config['host'] = host
- username = module.params.pop('tower_username', None)
- if username:
- auth_config['username'] = username
- password = module.params.pop('tower_password', None)
- if password:
- auth_config['password'] = password
- module.params.pop('tower_verify_ssl', None) # pop alias if used
- verify_ssl = module.params.pop('validate_certs', None)
- if verify_ssl is not None:
- auth_config['verify_ssl'] = verify_ssl
- return auth_config
-
-
-def tower_check_mode(module):
- '''Execute check mode logic for Ansible Tower modules'''
- if module.check_mode:
- try:
- result = client.get('/ping').json()
- module.exit_json(changed=True, tower_version='{0}'.format(result['version']))
- except (exc.ServerError, exc.ConnectionError, exc.BadRequest) as excinfo:
- module.fail_json(changed=False, msg='Failed check mode: {0}'.format(excinfo))
-
-
-class TowerLegacyModule(AnsibleModule):
- def __init__(self, argument_spec, **kwargs):
- args = dict(
- tower_host=dict(),
- tower_username=dict(),
- tower_password=dict(no_log=True),
- validate_certs=dict(type='bool', aliases=['tower_verify_ssl']),
- tower_config_file=dict(type='path'),
- )
- args.update(argument_spec)
-
- kwargs.setdefault('mutually_exclusive', [])
- kwargs['mutually_exclusive'].extend(
- (
- ('tower_config_file', 'tower_host'),
- ('tower_config_file', 'tower_username'),
- ('tower_config_file', 'tower_password'),
- ('tower_config_file', 'validate_certs'),
- )
- )
-
- super().__init__(argument_spec=args, **kwargs)
-
- if not HAS_TOWER_CLI:
- self.fail_json(msg=missing_required_lib('ansible-tower-cli'), exception=TOWER_CLI_IMP_ERR)
diff --git a/ansible_collections/awx/awx/plugins/modules/ad_hoc_command.py b/ansible_collections/awx/awx/plugins/modules/ad_hoc_command.py
index bfe22eb89..5864d392a 100644
--- a/ansible_collections/awx/awx/plugins/modules/ad_hoc_command.py
+++ b/ansible_collections/awx/awx/plugins/modules/ad_hoc_command.py
@@ -29,12 +29,12 @@ options:
choices: [ 'run', 'check' ]
execution_environment:
description:
- - Execution Environment to use for the ad hoc command.
+ - Execution Environment name, ID, or named URL to use for the ad hoc command.
required: False
type: str
inventory:
description:
- - Inventory to use for the ad hoc command.
+ - Inventory name, ID, or named URL to use for the ad hoc command.
required: True
type: str
limit:
@@ -43,7 +43,7 @@ options:
type: str
credential:
description:
- - Credential to use for ad hoc command.
+ - Credential name, ID, or named URL to use for ad hoc command.
required: True
type: str
module_name:
@@ -95,6 +95,13 @@ extends_documentation_fragment: awx.awx.auth
'''
EXAMPLES = '''
+- name: Launch an Ad Hoc Command waiting for it to finish
+ ad_hoc_command:
+ inventory: Demo Inventory
+ credential: Demo Credential
+ module_name: command
+ module_args: echo I <3 Ansible
+ wait: true
'''
RETURN = '''
@@ -111,6 +118,7 @@ status:
'''
from ..module_utils.controller_api import ControllerAPIModule
+import json
def main():
@@ -145,6 +153,7 @@ def main():
wait = module.params.get('wait')
interval = module.params.get('interval')
timeout = module.params.get('timeout')
+ execution_environment = module.params.get('execution_environment')
# Create a datastructure to pass into our command launch
post_data = {
@@ -153,11 +162,17 @@ def main():
}
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)
+ # extra_var can receive a dict or a string, if a dict covert it to a string
+ if arg == 'extra_vars' and type(module.params.get(arg)) is not str:
+ post_data[arg] = json.dumps(module.params.get(arg))
+ else:
+ 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)
+ if execution_environment:
+ post_data['execution_environment'] = module.resolve_name_to_id('execution_environments', execution_environment)
# Launch the ad hoc command
results = module.post_endpoint('ad_hoc_commands', **{'data': post_data})
diff --git a/ansible_collections/awx/awx/plugins/modules/application.py b/ansible_collections/awx/awx/plugins/modules/application.py
index e35e99dfe..7a31522a0 100644
--- a/ansible_collections/awx/awx/plugins/modules/application.py
+++ b/ansible_collections/awx/awx/plugins/modules/application.py
@@ -48,7 +48,7 @@ options:
required: False
organization:
description:
- - Name of organization for application.
+ - Name, ID, or named URL of organization for application.
type: str
required: True
redirect_uris:
@@ -60,7 +60,7 @@ options:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
skip_authorization:
description:
@@ -106,7 +106,7 @@ def main():
client_type=dict(choices=['public', 'confidential']),
organization=dict(required=True),
redirect_uris=dict(type="list", elements='str'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
skip_authorization=dict(type='bool'),
)
@@ -127,7 +127,7 @@ def main():
org_id = module.resolve_name_to_id('organizations', organization)
# Attempt to look up application based on the provided name and org ID
- application = module.get_one('applications', name_or_id=name, **{'data': {'organization': org_id}})
+ application = module.get_one('applications', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'organization': org_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
diff --git a/ansible_collections/awx/awx/plugins/modules/bulk_host_create.py b/ansible_collections/awx/awx/plugins/modules/bulk_host_create.py
index c2cb6992d..dcd32bf44 100644
--- a/ansible_collections/awx/awx/plugins/modules/bulk_host_create.py
+++ b/ansible_collections/awx/awx/plugins/modules/bulk_host_create.py
@@ -48,7 +48,7 @@ options:
type: str
inventory:
description:
- - Inventory name or ID the hosts should be made a member of.
+ - Inventory name, ID, or named URL the hosts should be made a member of.
required: True
type: str
extends_documentation_fragment: awx.awx.auth
diff --git a/ansible_collections/awx/awx/plugins/modules/bulk_host_delete.py b/ansible_collections/awx/awx/plugins/modules/bulk_host_delete.py
new file mode 100644
index 000000000..12468e602
--- /dev/null
+++ b/ansible_collections/awx/awx/plugins/modules/bulk_host_delete.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+# coding: utf-8 -*-
+
+# 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: bulk_host_delete
+author: "Avi Layani (@Avilir)"
+short_description: Bulk host delete in Automation Platform Controller
+description:
+ - Single-request bulk host deletion in Automation Platform Controller.
+ - Provides a way to delete many hosts at once from inventories in Controller.
+options:
+ hosts:
+ description:
+ - List of hosts id's to delete from inventory.
+ required: True
+ type: list
+ elements: int
+extends_documentation_fragment: awx.awx.auth
+'''
+
+EXAMPLES = '''
+- name: Bulk host delete
+ bulk_host_delete:
+ hosts:
+ - 1
+ - 2
+'''
+
+from ..module_utils.controller_api import ControllerAPIModule
+
+
+def main():
+ # Any additional arguments that are not fields of the item can be added here
+ argument_spec = dict(
+ hosts=dict(required=True, type='list', elements='int'),
+ )
+
+ # Create a module for ourselves
+ module = ControllerAPIModule(argument_spec=argument_spec)
+
+ # Extract our parameters
+ hosts = module.params.get('hosts')
+
+ # Delete the hosts
+ result = module.post_endpoint("bulk/host_delete", data={"hosts": hosts})
+
+ if result['status_code'] != 201:
+ module.fail_json(msg="Failed to delete hosts, see response for details", response=result)
+
+ module.json_output['changed'] = True
+
+ module.exit_json(**module.json_output)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/awx/awx/plugins/modules/bulk_job_launch.py b/ansible_collections/awx/awx/plugins/modules/bulk_job_launch.py
index 8aa5fca25..00fa338dd 100644
--- a/ansible_collections/awx/awx/plugins/modules/bulk_job_launch.py
+++ b/ansible_collections/awx/awx/plugins/modules/bulk_job_launch.py
@@ -128,7 +128,7 @@ options:
type: str
inventory:
description:
- - Inventory name or ID to use for the jobs ran within the bulk job, only used if prompt for inventory is set.
+ - Inventory name, ID, or named URL to use for the jobs ran within the bulk job, only used if prompt for inventory is set.
type: str
scm_branch:
description:
@@ -186,6 +186,9 @@ EXAMPLES = '''
food: carrot
color: orange
limit: bar
+ credentials:
+ - "My Credential"
+ - "suplementary cred"
extra_vars: # these override / extend extra_data at the job level
food: grape
animal: owl
diff --git a/ansible_collections/awx/awx/plugins/modules/credential.py b/ansible_collections/awx/awx/plugins/modules/credential.py
index 4e7f02e55..b5fbfe9a4 100644
--- a/ansible_collections/awx/awx/plugins/modules/credential.py
+++ b/ansible_collections/awx/awx/plugins/modules/credential.py
@@ -45,7 +45,8 @@ options:
type: str
organization:
description:
- - Organization that should own the credential.
+ - Organization name, ID, or named URL that should own the credential.
+ - This parameter is mutually exclusive with C(team) and C(user).
type: str
credential_type:
description:
@@ -57,6 +58,7 @@ options:
Insights, Machine, Microsoft Azure Key Vault, Microsoft Azure Resource Manager, Network, OpenShift or Kubernetes API
Bearer Token, OpenStack, Red Hat Ansible Automation Platform, Red Hat Satellite 6, Red Hat Virtualization, Source Control,
Thycotic DevOps Secrets Vault, Thycotic Secret Server, Vault, VMware vCenter, or a custom credential type
+ required: True
type: str
inputs:
description:
@@ -87,21 +89,23 @@ options:
update_secrets:
description:
- C(true) will always update encrypted values.
- - C(false) will only updated encrypted values if a change is absolutely known to be needed.
+ - C(false) will only update encrypted values if a change is absolutely known to be needed.
type: bool
default: true
user:
description:
- - User that should own this credential.
+ - User name, ID, or named URL that should own this credential.
+ - This parameter is mutually exclusive with C(organization) and C(team).
type: str
team:
description:
- - Team that should own this credential.
+ - Team name, ID, or named URL that should own this credential.
+ - This parameter is mutually exclusive with C(organization) and C(user).
type: str
state:
description:
- - Desired state of the resource.
- choices: ["present", "absent"]
+ - Desired state of the resource. C(exists) will not modify the resource if it is present.
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
@@ -211,16 +215,21 @@ def main():
copy_from=dict(),
description=dict(),
organization=dict(),
- credential_type=dict(),
+ credential_type=dict(required=True),
inputs=dict(type='dict', no_log=True),
update_secrets=dict(type='bool', default=True, no_log=False),
user=dict(),
team=dict(),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
+ mutually_exclusive = [("organization", "user", "team")]
+
# Create a module for ourselves
- module = ControllerAPIModule(argument_spec=argument_spec)
+ module = ControllerAPIModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=mutually_exclusive
+ )
# Extract our parameters
name = module.params.get('name')
@@ -247,7 +256,7 @@ def main():
if organization:
lookup_data['organization'] = org_id
- credential = module.get_one('credentials', name_or_id=name, **{'data': lookup_data})
+ credential = module.get_one('credentials', name_or_id=name, check_exists=(state == 'exists'), **{'data': lookup_data})
# Attempt to look up credential to copy based on the provided name
if copy_from:
diff --git a/ansible_collections/awx/awx/plugins/modules/credential_input_source.py b/ansible_collections/awx/awx/plugins/modules/credential_input_source.py
index c7ace4f72..e40e398a1 100644
--- a/ansible_collections/awx/awx/plugins/modules/credential_input_source.py
+++ b/ansible_collections/awx/awx/plugins/modules/credential_input_source.py
@@ -38,17 +38,17 @@ options:
type: dict
target_credential:
description:
- - The credential which will have its input defined by this source
+ - The credential name, ID, or named URL which will have its input defined by this source
required: true
type: str
source_credential:
description:
- - The credential which is the source of the credential lookup
+ - The credential name, ID, or named URL which is the source of the credential lookup
type: str
state:
description:
- Desired state of the resource.
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
@@ -80,7 +80,7 @@ def main():
target_credential=dict(required=True),
source_credential=dict(),
metadata=dict(type="dict"),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -101,7 +101,7 @@ def main():
'target_credential': target_credential_id,
'input_field_name': input_field_name,
}
- credential_input_source = module.get_one('credential_input_sources', **{'data': lookup_data})
+ credential_input_source = module.get_one('credential_input_sources', check_exists=(state == 'exists'), **{'data': lookup_data})
if state == 'absent':
module.delete_if_needed(credential_input_source)
diff --git a/ansible_collections/awx/awx/plugins/modules/credential_type.py b/ansible_collections/awx/awx/plugins/modules/credential_type.py
index f6b56d0ea..9407eaac3 100644
--- a/ansible_collections/awx/awx/plugins/modules/credential_type.py
+++ b/ansible_collections/awx/awx/plugins/modules/credential_type.py
@@ -59,7 +59,7 @@ options:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
extends_documentation_fragment: awx.awx.auth
'''
@@ -98,7 +98,7 @@ def main():
kind=dict(choices=list(KIND_CHOICES.keys())),
inputs=dict(type='dict'),
injectors=dict(type='dict'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -124,7 +124,7 @@ def main():
credential_type_params['injectors'] = module.params.get('injectors')
# Attempt to look up credential_type based on the provided name
- credential_type = module.get_one('credential_types', name_or_id=name)
+ credential_type = module.get_one('credential_types', name_or_id=name, check_exists=(state == 'exists'))
if state == 'absent':
# If the state was absent we can let the module delete it if needed, the module will handle exiting from this
diff --git a/ansible_collections/awx/awx/plugins/modules/execution_environment.py b/ansible_collections/awx/awx/plugins/modules/execution_environment.py
index 552c80572..38c5ecb51 100644
--- a/ansible_collections/awx/awx/plugins/modules/execution_environment.py
+++ b/ansible_collections/awx/awx/plugins/modules/execution_environment.py
@@ -33,7 +33,6 @@ options:
image:
description:
- The fully qualified url of the container image.
- required: True
type: str
description:
description:
@@ -41,16 +40,16 @@ options:
type: str
organization:
description:
- - The organization the execution environment belongs to.
+ - The organization name, ID, or named URL that the execution environment belongs to.
type: str
credential:
description:
- - Name of the credential to use for the execution environment.
+ - Name, ID, or named URL of the credential to use for the execution environment.
type: str
state:
description:
- Desired state of the resource.
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
pull:
@@ -79,11 +78,11 @@ def main():
argument_spec = dict(
name=dict(required=True),
new_name=dict(),
- image=dict(required=True),
+ image=dict(),
description=dict(),
organization=dict(),
credential=dict(),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
# NOTE: Default for pull differs from API (which is blank by default)
pull=dict(choices=['always', 'missing', 'never'], default='missing'),
)
@@ -99,7 +98,7 @@ def main():
state = module.params.get('state')
pull = module.params.get('pull')
- existing_item = module.get_one('execution_environments', name_or_id=name)
+ existing_item = module.get_one('execution_environments', name_or_id=name, check_exists=(state == 'exists'))
if state == 'absent':
module.delete_if_needed(existing_item)
diff --git a/ansible_collections/awx/awx/plugins/modules/export.py b/ansible_collections/awx/awx/plugins/modules/export.py
index d04e99605..1080b4889 100644
--- a/ansible_collections/awx/awx/plugins/modules/export.py
+++ b/ansible_collections/awx/awx/plugins/modules/export.py
@@ -28,72 +28,72 @@ options:
default: 'False'
organizations:
description:
- - organization names to export
+ - organization names, IDs, or named URLs to export
type: list
elements: str
users:
description:
- - user names to export
+ - user names, IDs, or named URLs to export
type: list
elements: str
teams:
description:
- - team names to export
+ - team names, IDs, or named URLs to export
type: list
elements: str
credential_types:
description:
- - credential type names to export
+ - credential type names, IDs, or named URLs to export
type: list
elements: str
credentials:
description:
- - credential names to export
+ - credential names, IDs, or named URLs to export
type: list
elements: str
execution_environments:
description:
- - execution environment names to export
+ - execution environment names, IDs, or named URLs to export
type: list
elements: str
notification_templates:
description:
- - notification template names to export
+ - notification template names, IDs, or named URLs to export
type: list
elements: str
inventory_sources:
description:
- - inventory soruces to export
+ - inventory source name, ID, or named URLs to export
type: list
elements: str
inventory:
description:
- - inventory names to export
+ - inventory names, IDs, or named URLs to export
type: list
elements: str
projects:
description:
- - project names to export
+ - project names, IDs, or named URLs to export
type: list
elements: str
job_templates:
description:
- - job template names to export
+ - job template names, IDs, or named URLs to export
type: list
elements: str
workflow_job_templates:
description:
- - workflow names to export
+ - workflow names, IDs, or named URLs to export
type: list
elements: str
applications:
description:
- - OAuth2 application names to export
+ - OAuth2 application names, IDs, or named URLs to export
type: list
elements: str
schedules:
description:
- - schedule names to export
+ - schedule names, IDs, or named URLs to export
type: list
elements: str
requirements:
@@ -115,7 +115,7 @@ EXAMPLES = '''
- name: Export a job template named "My Template" and all Credentials
export:
job_templates: "My Template"
- credential: 'all'
+ credentials: 'all'
- name: Export a list of inventories
export:
@@ -154,12 +154,12 @@ def main():
# The exporter code currently works like the following:
# Empty string == all assets of that type
- # Non-Empty string = just one asset of that type (by name or ID)
+ # Non-Empty string = just a list of assets of that type (by name, ID, or named URL)
# Asset type not present or None = skip asset type (unless everything is None, then export all)
# Here we are going to setup a dict of values to export
export_args = {}
for resource in EXPORTABLE_RESOURCES:
- if module.params.get('all') or module.params.get(resource) == 'all':
+ if module.params.get('all') or module.params.get(resource) == ['all']:
# If we are exporting everything or we got the keyword "all" we pass in an empty string for this asset type
export_args[resource] = ''
else:
diff --git a/ansible_collections/awx/awx/plugins/modules/group.py b/ansible_collections/awx/awx/plugins/modules/group.py
index c91bf164d..32b7e4104 100644
--- a/ansible_collections/awx/awx/plugins/modules/group.py
+++ b/ansible_collections/awx/awx/plugins/modules/group.py
@@ -32,7 +32,7 @@ options:
type: str
inventory:
description:
- - Inventory the group should be made a member of.
+ - Inventory name, ID, or named URL that the group should be made a member of.
required: True
type: str
variables:
@@ -41,12 +41,12 @@ options:
type: dict
hosts:
description:
- - List of hosts that should be put in this group.
+ - List of host names, IDs, or named URLs that should be put in this group.
type: list
elements: str
children:
description:
- - List of groups that should be nested inside in this group.
+ - List of groups names, IDs, or named URLs that should be nested inside in this group.
type: list
elements: str
aliases:
@@ -67,7 +67,7 @@ options:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
new_name:
description:
@@ -115,7 +115,7 @@ def main():
children=dict(type='list', elements='str', aliases=['groups']),
preserve_existing_hosts=dict(type='bool', default=False),
preserve_existing_children=dict(type='bool', default=False, aliases=['preserve_existing_groups']),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -135,7 +135,7 @@ def main():
inventory_id = module.resolve_name_to_id('inventories', inventory)
# Attempt to look up the object based on the provided name and inventory ID
- group = module.get_one('groups', name_or_id=name, **{'data': {'inventory': inventory_id}})
+ group = module.get_one('groups', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'inventory': inventory_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
@@ -170,9 +170,11 @@ def main():
id_list.append(sub_obj['id'])
# Preserve existing objects
if (preserve_existing_hosts and relationship == 'hosts') or (preserve_existing_children and relationship == 'children'):
- preserve_existing_check = module.get_all_endpoint(group['related'][relationship])
- for sub_obj in preserve_existing_check['json']['results']:
- id_list.append(sub_obj['id'])
+ if group:
+ preserve_existing_check = module.get_all_endpoint(group['related'][relationship])
+ for sub_obj in preserve_existing_check['json']['results']:
+ if 'id' in sub_obj:
+ id_list.append(sub_obj['id'])
if id_list:
association_fields[relationship] = id_list
diff --git a/ansible_collections/awx/awx/plugins/modules/host.py b/ansible_collections/awx/awx/plugins/modules/host.py
index 21d063f39..9b8760b88 100644
--- a/ansible_collections/awx/awx/plugins/modules/host.py
+++ b/ansible_collections/awx/awx/plugins/modules/host.py
@@ -36,7 +36,7 @@ options:
type: str
inventory:
description:
- - Inventory the host should be made a member of.
+ - Inventory name, ID, or named URL the host should be made a member of.
required: True
type: str
enabled:
@@ -50,7 +50,7 @@ options:
state:
description:
- Desired state of the resource.
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
extends_documentation_fragment: awx.awx.auth
@@ -83,7 +83,7 @@ def main():
inventory=dict(required=True),
enabled=dict(type='bool'),
variables=dict(type='dict'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -102,7 +102,7 @@ def main():
inventory_id = module.resolve_name_to_id('inventories', inventory)
# Attempt to look up host based on the provided name and inventory ID
- host = module.get_one('hosts', name_or_id=name, **{'data': {'inventory': inventory_id}})
+ host = module.get_one('hosts', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'inventory': inventory_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
diff --git a/ansible_collections/awx/awx/plugins/modules/instance.py b/ansible_collections/awx/awx/plugins/modules/instance.py
index 049dd976d..af7360687 100644
--- a/ansible_collections/awx/awx/plugins/modules/instance.py
+++ b/ansible_collections/awx/awx/plugins/modules/instance.py
@@ -47,6 +47,7 @@ options:
- Role that this node plays in the mesh.
choices:
- execution
+ - hop
required: False
type: str
node_state:
@@ -62,6 +63,20 @@ options:
- Port that Receptor will listen for incoming connections on.
required: False
type: int
+ peers:
+ description:
+ - List of peers to connect outbound to. Only configurable for hop and execution nodes.
+ - To remove all current peers, set value to an empty list, [].
+ - Each item is an ID or address of a receptor address.
+ - If item is address, it must be unique across all receptor addresses.
+ required: False
+ type: list
+ elements: str
+ peers_from_control_nodes:
+ description:
+ - If enabled, control plane nodes will automatically peer to this node.
+ required: False
+ type: bool
extends_documentation_fragment: awx.awx.auth
'''
@@ -70,12 +85,24 @@ EXAMPLES = '''
awx.awx.instance:
hostname: my-instance.prod.example.com
capacity_adjustment: 0.4
- listener_port: 31337
- name: Deprovision the instance
awx.awx.instance:
hostname: my-instance.prod.example.com
node_state: deprovisioning
+
+- name: Create execution node
+ awx.awx.instance:
+ hostname: execution.example.com
+ node_type: execution
+ peers:
+ - 12
+ - route.to.hop.example.com
+
+- name: Remove peers
+ awx.awx.instance:
+ hostname: execution.example.com
+ peers:
'''
from ..module_utils.controller_api import ControllerAPIModule
@@ -88,9 +115,11 @@ def main():
capacity_adjustment=dict(type='float'),
enabled=dict(type='bool'),
managed_by_policy=dict(type='bool'),
- node_type=dict(type='str', choices=['execution']),
+ node_type=dict(type='str', choices=['execution', 'hop']),
node_state=dict(type='str', choices=['deprovisioning', 'installed']),
listener_port=dict(type='int'),
+ peers=dict(required=False, type='list', elements='str'),
+ peers_from_control_nodes=dict(required=False, type='bool'),
)
# Create a module for ourselves
@@ -104,10 +133,22 @@ def main():
node_type = module.params.get('node_type')
node_state = module.params.get('node_state')
listener_port = module.params.get('listener_port')
-
+ peers = module.params.get('peers')
+ peers_from_control_nodes = module.params.get('peers_from_control_nodes')
# Attempt to look up an existing item based on the provided data
existing_item = module.get_one('instances', name_or_id=hostname)
+ # peer item can be an id or address
+ # if address, get the id
+ peers_ids = []
+ if peers:
+ for p in peers:
+ if not p.isdigit():
+ p_id = module.get_one('receptor_addresses', allow_none=False, data={'address': p})
+ peers_ids.append(p_id['id'])
+ else:
+ peers_ids.append(p)
+
# Create the data that gets sent for create and update
new_fields = {'hostname': hostname}
if capacity_adjustment is not None:
@@ -122,6 +163,10 @@ def main():
new_fields['node_state'] = node_state
if listener_port is not None:
new_fields['listener_port'] = listener_port
+ if peers is not None:
+ new_fields['peers'] = peers_ids
+ if peers_from_control_nodes is not None:
+ new_fields['peers_from_control_nodes'] = peers_from_control_nodes
module.create_or_update_if_needed(
existing_item,
diff --git a/ansible_collections/awx/awx/plugins/modules/instance_group.py b/ansible_collections/awx/awx/plugins/modules/instance_group.py
index dc993f8b5..32e6b192d 100644
--- a/ansible_collections/awx/awx/plugins/modules/instance_group.py
+++ b/ansible_collections/awx/awx/plugins/modules/instance_group.py
@@ -33,7 +33,7 @@ options:
type: str
credential:
description:
- - Credential to authenticate with Kubernetes or OpenShift. Must be of type "OpenShift or Kubernetes API Bearer Token".
+ - Credential name, ID, or named URL to authenticate with Kubernetes or OpenShift. Must be of type "OpenShift or Kubernetes API Bearer Token".
required: False
type: str
is_container_group:
@@ -74,14 +74,14 @@ options:
type: str
instances:
description:
- - The instances associated with this instance_group
+ - The instance names, IDs, or named URLs associated with this instance_group
required: False
type: list
elements: str
state:
description:
- Desired state of the resource.
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
extends_documentation_fragment: awx.awx.auth
@@ -107,7 +107,7 @@ def main():
policy_instance_list=dict(type='list', elements='str'),
pod_spec_override=dict(),
instances=dict(required=False, type="list", elements='str'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -128,7 +128,7 @@ def main():
state = module.params.get('state')
# Attempt to look up an existing item based on the provided data
- existing_item = module.get_one('instance_groups', name_or_id=name)
+ existing_item = module.get_one('instance_groups', name_or_id=name, check_exists=(state == 'exists'))
if state == 'absent':
# If the state was absent we can let the module delete it if needed, the module will handle exiting from this
diff --git a/ansible_collections/awx/awx/plugins/modules/inventory.py b/ansible_collections/awx/awx/plugins/modules/inventory.py
index 8e739b221..67ebe93f0 100644
--- a/ansible_collections/awx/awx/plugins/modules/inventory.py
+++ b/ansible_collections/awx/awx/plugins/modules/inventory.py
@@ -44,7 +44,7 @@ options:
type: str
organization:
description:
- - Organization the inventory belongs to.
+ - Organization name, ID, or named URL the inventory belongs to.
required: True
type: str
variables:
@@ -54,7 +54,7 @@ options:
kind:
description:
- The kind field. Cannot be modified after created.
- choices: ["", "smart"]
+ choices: ["", "smart", "constructed"]
type: str
host_filter:
description:
@@ -62,7 +62,12 @@ options:
type: str
instance_groups:
description:
- - list of Instance Groups for this Organization to run on.
+ - list of Instance Group names, IDs, or named URLs for this Organization to run on.
+ type: list
+ elements: str
+ input_inventories:
+ description:
+ - List of Inventory names, IDs, or named URLs to use as input for Constructed Inventory.
type: list
elements: str
prevent_instance_group_fallback:
@@ -73,7 +78,7 @@ options:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
extends_documentation_fragment: awx.awx.auth
'''
@@ -95,6 +100,35 @@ EXAMPLES = '''
description: "Our Foo Cloud Servers"
organization: Foo
state: present
+
+# You can create and modify constructed inventories by creating an inventory
+# of kind "constructed" and then editing the automatically generated inventory
+# source for that inventory.
+- name: Add constructed inventory with two existing input inventories
+ inventory:
+ name: My Constructed Inventory
+ organization: Default
+ kind: constructed
+ input_inventories:
+ - "West Datacenter"
+ - "East Datacenter"
+
+- name: Edit the constructed inventory source
+ inventory_source:
+ # The constructed inventory source will always be in the format:
+ # "Auto-created source for: <constructed inventory name>"
+ name: "Auto-created source for: My Constructed Inventory"
+ inventory: My Constructed Inventory
+ limit: host3,host4,host6
+ source_vars:
+ plugin: constructed
+ strict: true
+ use_vars_plugins: true
+ groups:
+ shutdown: resolved_state == "shutdown"
+ shutdown_in_product_dev: resolved_state == "shutdown" and account_alias == "product_dev"
+ compose:
+ resolved_state: state | default("running")
'''
@@ -111,11 +145,12 @@ def main():
description=dict(),
organization=dict(required=True),
variables=dict(type='dict'),
- kind=dict(choices=['', 'smart']),
+ kind=dict(choices=['', 'smart', 'constructed']),
host_filter=dict(),
instance_groups=dict(type="list", elements='str'),
prevent_instance_group_fallback=dict(type='bool'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
+ input_inventories=dict(type='list', elements='str'),
)
# Create a module for ourselves
@@ -137,7 +172,7 @@ def main():
org_id = module.resolve_name_to_id('organizations', organization)
# Attempt to look up inventory based on the provided name and org ID
- inventory = module.get_one('inventories', name_or_id=name, **{'data': {'organization': org_id}})
+ inventory = module.get_one('inventories', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'organization': org_id}})
# Attempt to look up credential to copy based on the provided name
if copy_from:
@@ -181,6 +216,13 @@ def main():
if inventory and inventory['kind'] == '' and inventory_fields['kind'] == 'smart':
module.fail_json(msg='You cannot turn a regular inventory into a "smart" inventory.')
+ if kind == 'constructed':
+ input_inventory_names = module.params.get('input_inventories')
+ if input_inventory_names is not None:
+ association_fields['input_inventories'] = []
+ for item in input_inventory_names:
+ association_fields['input_inventories'].append(module.resolve_name_to_id('inventories', item))
+
# If the state was present and we can let the module build or update the existing inventory, this will return on its own
module.create_or_update_if_needed(
inventory,
diff --git a/ansible_collections/awx/awx/plugins/modules/inventory_source.py b/ansible_collections/awx/awx/plugins/modules/inventory_source.py
index 1e6939df3..5f6c1781b 100644
--- a/ansible_collections/awx/awx/plugins/modules/inventory_source.py
+++ b/ansible_collections/awx/awx/plugins/modules/inventory_source.py
@@ -36,7 +36,7 @@ options:
type: str
inventory:
description:
- - Inventory the group should be made a member of.
+ - Inventory name, ID, or named URL the group should be made a member of.
required: True
type: str
source:
@@ -64,13 +64,17 @@ options:
description:
- If specified, AWX will only import hosts that match this regular expression.
type: str
+ limit:
+ description:
+ - Enter host, group or pattern match
+ type: str
credential:
description:
- - Credential to use for the source.
+ - Credential name, ID, or named URL to use for the source.
type: str
execution_environment:
description:
- - Execution Environment to use for the source.
+ - Execution Environment name, ID, or named URL to use for the source.
type: str
custom_virtualenv:
description:
@@ -103,7 +107,7 @@ options:
type: int
source_project:
description:
- - Project to use as source with scm option
+ - Project name, ID, or named URL to use as source with scm option
type: str
scm_branch:
description:
@@ -114,7 +118,7 @@ options:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
notification_templates_started:
description:
@@ -172,6 +176,7 @@ def main():
enabled_var=dict(),
enabled_value=dict(),
host_filter=dict(),
+ limit=dict(),
credential=dict(),
execution_environment=dict(),
custom_virtualenv=dict(),
@@ -187,7 +192,7 @@ def main():
notification_templates_started=dict(type="list", elements='str'),
notification_templates_success=dict(type="list", elements='str'),
notification_templates_error=dict(type="list", elements='str'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -214,6 +219,7 @@ def main():
inventory_source_object = module.get_one(
'inventory_sources',
name_or_id=name,
+ check_exists=(state == 'exists'),
**{
'data': {
'inventory': inventory_object['id'],
@@ -279,6 +285,7 @@ def main():
'enabled_value',
'host_filter',
'scm_branch',
+ 'limit',
)
# Layer in all remaining optional information
diff --git a/ansible_collections/awx/awx/plugins/modules/inventory_source_update.py b/ansible_collections/awx/awx/plugins/modules/inventory_source_update.py
index 5bd6cdfef..6e90e1ccf 100644
--- a/ansible_collections/awx/awx/plugins/modules/inventory_source_update.py
+++ b/ansible_collections/awx/awx/plugins/modules/inventory_source_update.py
@@ -35,7 +35,7 @@ options:
type: str
organization:
description:
- - Name of the inventory source's inventory's organization.
+ - Name, ID, or named URL of the inventory source's inventory's organization.
type: str
wait:
description:
diff --git a/ansible_collections/awx/awx/plugins/modules/job_launch.py b/ansible_collections/awx/awx/plugins/modules/job_launch.py
index 9a76f3a8b..19500b73a 100644
--- a/ansible_collections/awx/awx/plugins/modules/job_launch.py
+++ b/ansible_collections/awx/awx/plugins/modules/job_launch.py
@@ -34,17 +34,17 @@ options:
type: str
inventory:
description:
- - Inventory to use for the job, only used if prompt for inventory is set.
+ - Inventory name, ID, or named URL to use for the job, only used if prompt for inventory is set.
type: str
organization:
description:
- - Organization the job template exists in.
+ - Organization name, ID, or named URL the job template exists in.
- Used to help lookup the object, cannot be modified using this module.
- If not provided, will lookup by name only, which does not work with duplicates.
type: str
credentials:
description:
- - Credential to use for job, only used if prompt for credential is set.
+ - Credential names, IDs, or named URLs to use for job, only used if prompt for credential is set.
type: list
aliases: ['credential']
elements: str
@@ -88,7 +88,7 @@ options:
type: dict
execution_environment:
description:
- - Execution environment to use for the job, only used if prompt for execution environment is set.
+ - Execution environment name, ID, or named URL to use for the job, only used if prompt for execution environment is set.
type: str
forks:
description:
@@ -96,7 +96,7 @@ options:
type: int
instance_groups:
description:
- - Instance groups to use for the job, only used if prompt for instance groups is set.
+ - Instance group names, IDs, or named URLs to use for the job, only used if prompt for instance groups is set.
type: list
elements: str
job_slice_count:
@@ -151,7 +151,9 @@ EXAMPLES = '''
job_launch:
job_template: "My Job Template"
inventory: "My Inventory"
- credential: "My Credential"
+ credentials:
+ - "My Credential"
+ - "suplementary cred"
register: job
- name: Wait for job max 120s
job_wait:
diff --git a/ansible_collections/awx/awx/plugins/modules/job_template.py b/ansible_collections/awx/awx/plugins/modules/job_template.py
index 4508bc18d..81fe49512 100644
--- a/ansible_collections/awx/awx/plugins/modules/job_template.py
+++ b/ansible_collections/awx/awx/plugins/modules/job_template.py
@@ -49,11 +49,11 @@ options:
type: str
inventory:
description:
- - Name of the inventory to use for the job template.
+ - Name, ID, or named URL of the inventory to use for the job template.
type: str
organization:
description:
- - Organization the job template exists in.
+ - Organization name, ID, or named URL the job template exists in.
- Used to help lookup the object, cannot be modified using this module.
- The Organization is inferred from the associated project
- If not provided, will lookup by name only, which does not work with duplicates.
@@ -61,7 +61,7 @@ options:
type: str
project:
description:
- - Name of the project to use for the job template.
+ - Name, ID, or named URL of the project to use for the job template.
type: str
playbook:
description:
@@ -69,22 +69,22 @@ options:
type: str
credential:
description:
- - Name of the credential to use for the job template.
+ - Name, ID, or named URL of the credential to use for the job template.
- Deprecated, use 'credentials'.
type: str
credentials:
description:
- - List of credentials to use for the job template.
+ - List of credential names, IDs, or named URLs to use for the job template.
type: list
elements: str
vault_credential:
description:
- - Name of the vault credential to use for the job template.
+ - Name, ID, or named URL of the vault credential to use for the job template.
- Deprecated, use 'credentials'.
type: str
execution_environment:
description:
- - Execution Environment to use for the job template.
+ - Execution Environment name, ID, or named URL to use for the job template.
type: str
custom_virtualenv:
description:
@@ -94,7 +94,7 @@ options:
type: str
instance_groups:
description:
- - list of Instance Groups for this Organization to run on.
+ - list of Instance Group names, IDs, or named URLs for this Organization to run on.
type: list
elements: str
forks:
@@ -108,7 +108,7 @@ options:
verbosity:
description:
- Control the output level Ansible produces as the playbook runs. 0 - Normal, 1 - Verbose, 2 - More Verbose, 3 - Debug, 4 - Connection Debug.
- choices: [0, 1, 2, 3, 4]
+ choices: [0, 1, 2, 3, 4, 5]
type: int
extra_vars:
description:
@@ -264,7 +264,6 @@ options:
description:
- Maximum time in seconds to wait for a job to finish (server-side).
type: int
- default: 0
job_slice_count:
description:
- The number of jobs to slice into at runtime. Will cause the Job Template to launch a workflow if value is greater than 1.
@@ -295,7 +294,7 @@ options:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
notification_templates_started:
description:
@@ -337,6 +336,7 @@ EXAMPLES = '''
playbook: "ping.yml"
credentials:
- "Local"
+ - "2nd credential"
state: "present"
controller_config_file: "~/tower_cli.cfg"
survey_enabled: yes
@@ -404,7 +404,7 @@ def main():
instance_groups=dict(type="list", elements='str'),
forks=dict(type='int'),
limit=dict(),
- verbosity=dict(type='int', choices=[0, 1, 2, 3, 4]),
+ verbosity=dict(type='int', choices=[0, 1, 2, 3, 4, 5]),
extra_vars=dict(type='dict'),
job_tags=dict(),
force_handlers=dict(type='bool', aliases=['force_handlers_enabled']),
@@ -443,7 +443,7 @@ def main():
notification_templates_success=dict(type="list", elements='str'),
notification_templates_error=dict(type="list", elements='str'),
prevent_instance_group_fallback=dict(type="bool"),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -483,7 +483,7 @@ def main():
new_fields['execution_environment'] = module.resolve_name_to_id('execution_environments', ee)
# Attempt to look up an existing item based on the provided data
- existing_item = module.get_one('job_templates', name_or_id=name, **{'data': search_fields})
+ existing_item = module.get_one('job_templates', name_or_id=name, check_exists=(state == 'exists'), **{'data': search_fields})
# Attempt to look up credential to copy based on the provided name
if copy_from:
diff --git a/ansible_collections/awx/awx/plugins/modules/label.py b/ansible_collections/awx/awx/plugins/modules/label.py
index b17d58bee..230f9f470 100644
--- a/ansible_collections/awx/awx/plugins/modules/label.py
+++ b/ansible_collections/awx/awx/plugins/modules/label.py
@@ -34,14 +34,14 @@ options:
type: str
organization:
description:
- - Organization this label belongs to.
+ - Organization name, ID, or named URL this label belongs to.
required: True
type: str
state:
description:
- Desired state of the resource.
default: "present"
- choices: ["present"]
+ choices: ["present", "exists"]
type: str
extends_documentation_fragment: awx.awx.auth
'''
@@ -62,7 +62,7 @@ def main():
name=dict(required=True),
new_name=dict(),
organization=dict(required=True),
- state=dict(choices=['present'], default='present'),
+ state=dict(choices=['present', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -72,6 +72,7 @@ def main():
name = module.params.get('name')
new_name = module.params.get("new_name")
organization = module.params.get('organization')
+ state = module.params.get("state")
# Attempt to look up the related items the user specified (these will fail the module if not found)
organization_id = None
@@ -82,6 +83,7 @@ def main():
existing_item = module.get_one(
'labels',
name_or_id=name,
+ check_exists=(state == 'exists'),
**{
'data': {
'organization': organization_id,
diff --git a/ansible_collections/awx/awx/plugins/modules/license.py b/ansible_collections/awx/awx/plugins/modules/license.py
index ed9b93727..06f02a821 100644
--- a/ansible_collections/awx/awx/plugins/modules/license.py
+++ b/ansible_collections/awx/awx/plugins/modules/license.py
@@ -52,7 +52,12 @@ EXAMPLES = '''
license:
manifest: "/tmp/my_manifest.zip"
-- name: Attach to a pool
+- name: Use the subscriptions module to fetch subscriptions from Red Hat or Red Hat Satellite
+ subscriptions:
+ username: "my_satellite_username"
+ password: "my_satellite_password"
+
+- name: Attach to a pool (requires fetching subscriptions at least once before)
license:
pool_id: 123456
diff --git a/ansible_collections/awx/awx/plugins/modules/notification_template.py b/ansible_collections/awx/awx/plugins/modules/notification_template.py
index 6da77c659..bb1df60d3 100644
--- a/ansible_collections/awx/awx/plugins/modules/notification_template.py
+++ b/ansible_collections/awx/awx/plugins/modules/notification_template.py
@@ -44,7 +44,7 @@ options:
type: str
organization:
description:
- - The organization the notification belongs to.
+ - The organization name, ID, or named URL the notification belongs to.
type: str
notification_type:
description:
@@ -97,7 +97,7 @@ options:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
extends_documentation_fragment: awx.awx.auth
'''
@@ -222,7 +222,7 @@ def main():
notification_type=dict(choices=['email', 'grafana', 'irc', 'mattermost', 'pagerduty', 'rocketchat', 'slack', 'twilio', 'webhook']),
notification_configuration=dict(type='dict'),
messages=dict(type='dict'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -248,6 +248,7 @@ def main():
existing_item = module.get_one(
'notification_templates',
name_or_id=name,
+ check_exists=(state == 'exists'),
**{
'data': {
'organization': organization_id,
diff --git a/ansible_collections/awx/awx/plugins/modules/organization.py b/ansible_collections/awx/awx/plugins/modules/organization.py
index de78eb228..6fc406b92 100644
--- a/ansible_collections/awx/awx/plugins/modules/organization.py
+++ b/ansible_collections/awx/awx/plugins/modules/organization.py
@@ -36,7 +36,7 @@ options:
type: str
default_environment:
description:
- - Default Execution Environment to use for jobs owned by the Organization.
+ - Default Execution Environment name, ID, or named URL to use for jobs owned by the Organization.
type: str
custom_virtualenv:
description:
@@ -52,11 +52,11 @@ options:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
instance_groups:
description:
- - list of Instance Groups for this Organization to run on.
+ - list of Instance Group names, IDs, or named URLs for this Organization to run on.
type: list
elements: str
notification_templates_started:
@@ -81,7 +81,7 @@ options:
elements: str
galaxy_credentials:
description:
- - list of Ansible Galaxy credentials to associate to the organization
+ - list of Ansible Galaxy credential names, IDs, or named URLs to associate to the organization
type: list
elements: str
extends_documentation_fragment: awx.awx.auth
@@ -130,7 +130,7 @@ def main():
notification_templates_error=dict(type="list", elements='str'),
notification_templates_approvals=dict(type="list", elements='str'),
galaxy_credentials=dict(type="list", elements='str'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -146,7 +146,7 @@ def main():
state = module.params.get('state')
# Attempt to look up organization based on the provided name
- organization = module.get_one('organizations', name_or_id=name)
+ organization = module.get_one('organizations', name_or_id=name, check_exists=(state == 'exists'))
if state == 'absent':
# If the state was absent we can let the module delete it if needed, the module will handle exiting from this
diff --git a/ansible_collections/awx/awx/plugins/modules/project.py b/ansible_collections/awx/awx/plugins/modules/project.py
index 877340a11..2621e57ab 100644
--- a/ansible_collections/awx/awx/plugins/modules/project.py
+++ b/ansible_collections/awx/awx/plugins/modules/project.py
@@ -65,7 +65,7 @@ options:
type: str
credential:
description:
- - Name of the credential to use with this SCM resource.
+ - Name, ID, or named URL of the credential to use with this SCM resource.
type: str
aliases:
- scm_credential
@@ -83,7 +83,7 @@ options:
type: bool
scm_update_on_launch:
description:
- - Before an update to the local repository before launching a job with this project.
+ - Perform an update to the local repository before launching a job with this project.
type: bool
scm_update_cache_timeout:
description:
@@ -106,7 +106,7 @@ options:
- job_timeout
default_environment:
description:
- - Default Execution Environment to use for jobs relating to the project.
+ - Default Execution Environment name, ID, or named URL to use for jobs relating to the project.
type: str
custom_virtualenv:
description:
@@ -116,13 +116,13 @@ options:
type: str
organization:
description:
- - Name of organization for project.
+ - Name, ID, or named URL of organization for the project.
type: str
state:
description:
- Desired state of the resource.
default: "present"
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
type: str
wait:
description:
@@ -162,7 +162,7 @@ options:
type: float
signature_validation_credential:
description:
- - Name of the credential to use for signature validation.
+ - Name, ID, or named URL of the credential to use for signature validation.
- If signature validation credential is provided, signature validation will be enabled.
type: str
@@ -272,7 +272,7 @@ def main():
notification_templates_started=dict(type="list", elements='str'),
notification_templates_success=dict(type="list", elements='str'),
notification_templates_error=dict(type="list", elements='str'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
wait=dict(type='bool', default=True),
update_project=dict(default=False, type='bool'),
interval=dict(default=2.0, type='float'),
@@ -313,7 +313,7 @@ def main():
lookup_data['organization'] = org_id
# Attempt to look up project based on the provided name and org ID
- project = module.get_one('projects', name_or_id=name, data=lookup_data)
+ project = module.get_one('projects', name_or_id=name, check_exists=(state == 'exists'), data=lookup_data)
# Attempt to look up credential to copy based on the provided name
if copy_from:
diff --git a/ansible_collections/awx/awx/plugins/modules/project_update.py b/ansible_collections/awx/awx/plugins/modules/project_update.py
index 6cbcd39b6..d6d712f8f 100644
--- a/ansible_collections/awx/awx/plugins/modules/project_update.py
+++ b/ansible_collections/awx/awx/plugins/modules/project_update.py
@@ -27,7 +27,7 @@ options:
- project
organization:
description:
- - Organization the project exists in.
+ - Organization name, ID, or named URL the project exists in.
- Used to help lookup the object, cannot be modified using this module.
- If not provided, will lookup by name only, which does not work with duplicates.
type: str
@@ -116,8 +116,7 @@ def main():
if result['status_code'] == 405:
module.fail_json(
- msg="Unable to trigger a project update because the project scm_type ({0}) does not support it.".format(project['scm_type']),
- response=result
+ msg="Unable to trigger a project update because the project scm_type ({0}) does not support it.".format(project['scm_type']), response=result
)
elif result['status_code'] != 202:
module.fail_json(msg="Failed to update project, see response for details", response=result)
diff --git a/ansible_collections/awx/awx/plugins/modules/role.py b/ansible_collections/awx/awx/plugins/modules/role.py
index 51ed56439..40463746b 100644
--- a/ansible_collections/awx/awx/plugins/modules/role.py
+++ b/ansible_collections/awx/awx/plugins/modules/role.py
@@ -23,12 +23,24 @@ description:
options:
user:
description:
- - User that receives the permissions specified by the role.
+ - User name, ID, or named URL that receives the permissions specified by the role.
+ - Deprecated, use 'users'.
type: str
+ users:
+ description:
+ - User names, IDs, or named URLs that receive the permissions specified by the role.
+ type: list
+ elements: str
team:
description:
- - Team that receives the permissions specified by the role.
+ - Team name, ID, or named URL that receives the permissions specified by the role.
+ - Deprecated, use 'teams'.
type: str
+ teams:
+ description:
+ - Team names, IDs, or named URLs that receive the permissions specified by the role.
+ type: list
+ elements: str
role:
description:
- The role type to grant/revoke.
@@ -38,82 +50,87 @@ options:
type: str
target_team:
description:
- - Team that the role acts on.
+ - Team name, ID, or named URL that the role acts on.
- For example, make someone a member or an admin of a team.
- Members of a team implicitly receive the permissions that the team has.
- Deprecated, use 'target_teams'.
type: str
target_teams:
description:
- - Team that the role acts on.
+ - Team names, IDs, or named URLs that the role acts on.
- For example, make someone a member or an admin of a team.
- Members of a team implicitly receive the permissions that the team has.
type: list
elements: str
inventory:
description:
- - Inventory the role acts on.
+ - Inventory name, ID, or named URL the role acts on.
- Deprecated, use 'inventories'.
type: str
inventories:
description:
- - Inventory the role acts on.
+ - Inventory names, IDs, or named URLs the role acts on.
type: list
elements: str
job_template:
description:
- - The job template the role acts on.
+ - The job template name, ID, or named URL the role acts on.
- Deprecated, use 'job_templates'.
type: str
job_templates:
description:
- - The job template the role acts on.
+ - The job template names, IDs, or named URLs the role acts on.
type: list
elements: str
workflow:
description:
- - The workflow job template the role acts on.
+ - The workflow job template name, ID, or named URL the role acts on.
- Deprecated, use 'workflows'.
type: str
workflows:
description:
- - The workflow job template the role acts on.
+ - The workflow job template names, IDs, or named URLs the role acts on.
type: list
elements: str
credential:
description:
- - Credential the role acts on.
+ - Credential name, ID, or named URL the role acts on.
- Deprecated, use 'credentials'.
type: str
credentials:
description:
- - Credential the role acts on.
+ - Credential names, IDs, or named URLs the role acts on.
type: list
elements: str
organization:
description:
- - Organization the role acts on.
+ - Organization name, ID, or named URL the role acts on.
- Deprecated, use 'organizations'.
type: str
organizations:
description:
- - Organization the role acts on.
+ - Organization names, IDs, or named URLs the role acts on.
type: list
elements: str
lookup_organization:
description:
- - Organization the inventories, job templates, projects, or workflows the items exists in.
+ - Organization name, ID, or named URL the inventories, job templates, projects, or workflows the items exists in.
- Used to help lookup the object, for organization roles see organization.
- If not provided, will lookup by name only, which does not work with duplicates.
type: str
project:
description:
- - Project the role acts on.
+ - Project name, ID, or named URL the role acts on.
- Deprecated, use 'projects'.
type: str
projects:
description:
- - Project the role acts on.
+ - Project names, IDs, or named URLs the role acts on.
+ type: list
+ elements: str
+ instance_groups:
+ description:
+ - Instance Group names, IDs, or named URLs the role acts on.
type: list
elements: str
state:
@@ -156,7 +173,9 @@ def main():
argument_spec = dict(
user=dict(),
+ users=dict(type='list', elements='str'),
team=dict(),
+ teams=dict(type='list', elements='str'),
role=dict(
choices=[
"admin",
@@ -193,6 +212,7 @@ def main():
lookup_organization=dict(),
project=dict(),
projects=dict(type='list', elements='str'),
+ instance_groups=dict(type='list', elements='str'),
state=dict(choices=['present', 'absent'], default='present'),
)
@@ -213,9 +233,10 @@ def main():
'projects': 'project',
'target_teams': 'target_team',
'workflows': 'workflow',
+ 'users': 'user',
+ 'teams': 'team',
+ 'instance_groups': 'instance_group',
}
- # Singular parameters
- resource_param_keys = ('user', 'team', 'lookup_organization')
resources = {}
for resource_group, old_name in resource_list_param_keys.items():
@@ -223,14 +244,13 @@ def main():
resources.setdefault(resource_group, []).extend(module.params.get(resource_group))
if module.params.get(old_name) is not None:
resources.setdefault(resource_group, []).append(module.params.get(old_name))
- for resource_group in resource_param_keys:
- if module.params.get(resource_group) is not None:
- resources[resource_group] = module.params.get(resource_group)
- # Change workflows and target_teams key to its endpoint name.
+ if module.params.get('lookup_organization') is not None:
+ resources['lookup_organization'] = module.params.get('lookup_organization')
+ if module.params.get('instance_groups') is not None:
+ resources['instance_groups'] = module.params.get('instance_groups')
+ # Change workflows to its endpoint name.
if 'workflows' in resources:
resources['workflow_job_templates'] = resources.pop('workflows')
- if 'target_teams' in resources:
- resources['teams'] = resources.pop('target_teams')
# Set lookup data to use
lookup_data = {}
@@ -242,43 +262,36 @@ def main():
# separate actors from resources
actor_data = {}
missing_items = []
- for key in ('user', 'team'):
- if key in resources:
- if key == 'user':
- lookup_data_populated = {}
- else:
- lookup_data_populated = lookup_data
- # Attempt to look up project based on the provided name or ID and lookup data
- data = module.get_one('{0}s'.format(key), name_or_id=resources[key], data=lookup_data_populated)
- if data is None:
- module.fail_json(
- msg='Unable to find {0} with name: {1}'.format(key, resources[key]), changed=False
- )
- else:
- actor_data[key] = module.get_one('{0}s'.format(key), name_or_id=resources[key], data=lookup_data_populated)
- resources.pop(key)
# Lookup Resources
resource_data = {}
for key, value in resources.items():
for resource in value:
- # Attempt to look up project based on the provided name or ID and lookup data
- if key in resources:
- if key == 'organizations':
- lookup_data_populated = {}
- else:
- lookup_data_populated = lookup_data
- data = module.get_one(key, name_or_id=resource, data=lookup_data_populated)
+ # Attempt to look up project based on the provided name, ID, or named URL and lookup data
+ lookup_key = key
+ if key == 'organizations' or key == 'users':
+ lookup_data_populated = {}
+ else:
+ lookup_data_populated = lookup_data
+ if key == 'target_teams':
+ lookup_key = 'teams'
+ data = module.get_one(lookup_key, name_or_id=resource, data=lookup_data_populated)
if data is None:
missing_items.append(resource)
else:
- resource_data.setdefault(key, []).append(data)
+ if key == 'users' or key == 'teams':
+ actor_data.setdefault(key, []).append(data)
+ elif key == 'target_teams':
+ resource_data.setdefault('teams', []).append(data)
+ else:
+ resource_data.setdefault(key, []).append(data)
if len(missing_items) > 0:
module.fail_json(
msg='There were {0} missing items, missing items: {1}'.format(len(missing_items), missing_items), changed=False
)
+
# build association agenda
associations = {}
- for actor_type, actor in actor_data.items():
+ for actor_type, actors in actor_data.items():
for key, value in resource_data.items():
for resource in value:
resource_roles = resource['summary_fields']['object_roles']
@@ -288,9 +301,10 @@ def main():
msg='Resource {0} has no role {1}, available roles: {2}'.format(resource['url'], role_field, available_roles), changed=False
)
role_data = resource_roles[role_field]
- endpoint = '/roles/{0}/{1}/'.format(role_data['id'], module.param_to_endpoint(actor_type))
+ endpoint = '/roles/{0}/{1}/'.format(role_data['id'], actor_type)
associations.setdefault(endpoint, [])
- associations[endpoint].append(actor['id'])
+ for actor in actors:
+ associations[endpoint].append(actor['id'])
# perform associations
for association_endpoint, new_association_list in associations.items():
diff --git a/ansible_collections/awx/awx/plugins/modules/schedule.py b/ansible_collections/awx/awx/plugins/modules/schedule.py
index 5bf821025..4a6583629 100644
--- a/ansible_collections/awx/awx/plugins/modules/schedule.py
+++ b/ansible_collections/awx/awx/plugins/modules/schedule.py
@@ -44,7 +44,7 @@ options:
type: str
execution_environment:
description:
- - Execution Environment applied as a prompt, assuming jot template prompts for execution environment
+ - Execution Environment name, ID, or named URL applied as a prompt, assuming job template prompts for execution environment
type: str
extra_data:
description:
@@ -57,12 +57,12 @@ options:
type: int
instance_groups:
description:
- - List of Instance Groups applied as a prompt, assuming job template prompts for instance groups
+ - List of Instance Group names, IDs, or named URLs applied as a prompt, assuming job template prompts for instance groups
type: list
elements: str
inventory:
description:
- - Inventory applied as a prompt, assuming job template prompts for inventory
+ - Inventory name, ID, or named URL applied as a prompt, assuming job template prompts for inventory
required: False
type: str
job_slice_count:
@@ -76,7 +76,7 @@ options:
elements: str
credentials:
description:
- - List of credentials applied as a prompt, assuming job template prompts for credentials
+ - List of credential names, IDs, or named URLs applied as a prompt, assuming job template prompts for credentials
type: list
elements: str
scm_branch:
@@ -130,12 +130,12 @@ options:
- 5
unified_job_template:
description:
- - Name of unified job template to schedule. Used to look up an already existing schedule.
+ - Name, ID, or named URL of unified job template to schedule. Used to look up an already existing schedule.
required: False
type: str
organization:
description:
- - The organization the unified job template exists in.
+ - The organization name, ID, or named URL the unified job template exists in.
- Used for looking up the unified job template, not a direct model field.
type: str
enabled:
@@ -146,7 +146,7 @@ options:
state:
description:
- Desired state of the resource.
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
extends_documentation_fragment: awx.awx.auth
@@ -220,7 +220,7 @@ def main():
unified_job_template=dict(),
organization=dict(),
enabled=dict(type='bool'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -265,8 +265,33 @@ def main():
search_fields['name'] = unified_job_template
unified_job_template_id = module.get_one('unified_job_templates', **{'data': search_fields})['id']
sched_search_fields['unified_job_template'] = unified_job_template_id
+
# Attempt to look up an existing item based on the provided data
- existing_item = module.get_one('schedules', name_or_id=name, **{'data': sched_search_fields})
+ existing_item = module.get_one('schedules', name_or_id=name, check_exists=(state == 'exists'), **{'data': sched_search_fields})
+
+ 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(existing_item)
+
+ # We need to clear out the name from the search fields so we can use name_or_id in the following searches
+ if 'name' in search_fields:
+ del search_fields['name']
+
+ # Create the data that gets sent for create and update
+ new_fields = {}
+ if execution_environment is not None:
+ if execution_environment == '':
+ new_fields['execution_environment'] = ''
+ else:
+ ee = module.get_one('execution_environments', name_or_id=execution_environment, **{'data': search_fields})
+ if ee is None:
+ ee2 = module.get_one('execution_environments', name_or_id=execution_environment)
+ if ee2 is None or ee2['organization'] is not None:
+ module.fail_json(msg='could not find execution_environment entry with name {0}'.format(execution_environment))
+ else:
+ new_fields['execution_environment'] = ee2['id']
+ else:
+ new_fields['execution_environment'] = ee['id']
association_fields = {}
@@ -275,9 +300,9 @@ def main():
for item in credentials:
association_fields['credentials'].append(module.resolve_name_to_id('credentials', item))
- # We need to clear out the name from the search fields so we can use name_or_id in the following searches
- if 'name' in search_fields:
- del search_fields['name']
+ # We need to clear out the organization from the search fields the searches for labels and instance_groups doesnt support it and won't be needed anymore
+ if 'organization' in search_fields:
+ del search_fields['organization']
if labels is not None:
association_fields['labels'] = []
@@ -297,8 +322,6 @@ def main():
else:
association_fields['instance_groups'].append(instance_group_id['id'])
- # Create the data that gets sent for create and update
- new_fields = {}
if rrule is not None:
new_fields['rrule'] = rrule
new_fields['name'] = new_name if new_name else (module.get_item_name(existing_item) if existing_item else name)
@@ -333,28 +356,14 @@ def main():
if timeout is not None:
new_fields['timeout'] = timeout
- if execution_environment is not None:
- if execution_environment == '':
- new_fields['execution_environment'] = ''
- else:
- ee = module.get_one('execution_environments', name_or_id=execution_environment, **{'data': search_fields})
- if ee is None:
- module.fail_json(msg='could not find execution_environment entry with name {0}'.format(execution_environment))
- else:
- new_fields['execution_environment'] = ee['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(existing_item)
- elif state == 'present':
- # If the state was present and we can let the module build or update the existing item, this will return on its own
- module.create_or_update_if_needed(
- existing_item,
- new_fields,
- endpoint='schedules',
- item_type='schedule',
- associations=association_fields,
- )
+ # If the state was present and we can let the module build or update the existing item, this will return on its own
+ module.create_or_update_if_needed(
+ existing_item,
+ new_fields,
+ endpoint='schedules',
+ item_type='schedule',
+ associations=association_fields,
+ )
if __name__ == '__main__':
diff --git a/ansible_collections/awx/awx/plugins/modules/settings.py b/ansible_collections/awx/awx/plugins/modules/settings.py
index 56c0b94e5..c911f77fc 100644
--- a/ansible_collections/awx/awx/plugins/modules/settings.py
+++ b/ansible_collections/awx/awx/plugins/modules/settings.py
@@ -89,7 +89,7 @@ def coerce_type(module, value):
if not HAS_YAML:
module.fail_json(msg="yaml is not installed, try 'pip install pyyaml'")
return yaml.safe_load(value)
- elif value.lower in ('true', 'false', 't', 'f'):
+ elif value.lower() in ('true', 'false', 't', 'f'):
return {'t': True, 'f': False}[value[0].lower()]
try:
return int(value)
diff --git a/ansible_collections/awx/awx/plugins/modules/team.py b/ansible_collections/awx/awx/plugins/modules/team.py
index 5482b4c85..6507e8ac0 100644
--- a/ansible_collections/awx/awx/plugins/modules/team.py
+++ b/ansible_collections/awx/awx/plugins/modules/team.py
@@ -36,13 +36,13 @@ options:
type: str
organization:
description:
- - Organization the team should be made a member of.
+ - Organization name, ID, or named URL the team should be made a member of.
required: True
type: str
state:
description:
- Desired state of the resource.
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
extends_documentation_fragment: awx.awx.auth
@@ -69,7 +69,7 @@ def main():
new_name=dict(),
description=dict(),
organization=dict(required=True),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -86,7 +86,7 @@ def main():
org_id = module.resolve_name_to_id('organizations', organization)
# Attempt to look up team based on the provided name and org ID
- team = module.get_one('teams', name_or_id=name, **{'data': {'organization': org_id}})
+ team = module.get_one('teams', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'organization': org_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
diff --git a/ansible_collections/awx/awx/plugins/modules/token.py b/ansible_collections/awx/awx/plugins/modules/token.py
index c9ed84f67..b6e13cd02 100644
--- a/ansible_collections/awx/awx/plugins/modules/token.py
+++ b/ansible_collections/awx/awx/plugins/modules/token.py
@@ -37,7 +37,7 @@ options:
type: str
application:
description:
- - The application tied to this token.
+ - The application name, ID, or named URL tied to this token.
required: False
type: str
scope:
diff --git a/ansible_collections/awx/awx/plugins/modules/user.py b/ansible_collections/awx/awx/plugins/modules/user.py
index 49a6f216a..be1bb61de 100644
--- a/ansible_collections/awx/awx/plugins/modules/user.py
+++ b/ansible_collections/awx/awx/plugins/modules/user.py
@@ -44,7 +44,7 @@ options:
type: str
organization:
description:
- - The user will be created as a member of that organization (needed for organization admins to create new organization users).
+ - The user will be created as a member of that organization name, ID, or named URL (needed for organization admins to create new organization users).
type: str
is_superuser:
description:
@@ -69,7 +69,7 @@ options:
state:
description:
- Desired state of the resource.
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
extends_documentation_fragment: awx.awx.auth
@@ -137,7 +137,7 @@ def main():
password=dict(no_log=True),
update_secrets=dict(type='bool', default=True, no_log=False),
organization=dict(),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -158,7 +158,7 @@ def main():
# Attempt to look up the related items the user specified (these will fail the module if not found)
# Attempt to look up an existing item based on the provided data
- existing_item = module.get_one('users', name_or_id=username)
+ existing_item = module.get_one('users', name_or_id=username, check_exists=(state == 'exists'))
if state == 'absent':
# If the state was absent we can let the module delete it if needed, the module will handle exiting from this
diff --git a/ansible_collections/awx/awx/plugins/modules/workflow_job_template.py b/ansible_collections/awx/awx/plugins/modules/workflow_job_template.py
index 19954877b..f5ac994aa 100644
--- a/ansible_collections/awx/awx/plugins/modules/workflow_job_template.py
+++ b/ansible_collections/awx/awx/plugins/modules/workflow_job_template.py
@@ -58,7 +58,7 @@ options:
- ask_tags
organization:
description:
- - Organization the workflow job template exists in.
+ - Organization name, ID, or named URL the workflow job template exists in.
- Used to help lookup the object, cannot be modified using this module.
- If not provided, will lookup by name only, which does not work with duplicates.
type: str
@@ -72,7 +72,7 @@ options:
type: bool
inventory:
description:
- - Inventory applied as a prompt, assuming job template prompts for inventory
+ - Name, ID, or named URL of inventory applied as a prompt, assuming job template prompts for inventory
type: str
limit:
description:
@@ -144,6 +144,7 @@ options:
choices:
- present
- absent
+ - exists
default: "present"
type: str
notification_templates_started:
@@ -461,7 +462,9 @@ EXAMPLES = '''
failure_nodes:
- identifier: node201
always_nodes: []
- credentials: []
+ credentials:
+ - local_cred
+ - suplementary cred
- identifier: node201
unified_job_template:
organization:
@@ -514,68 +517,63 @@ EXAMPLES = '''
workflow_nodes:
- identifier: node101
unified_job_template:
- name: example-project
+ name: example-inventory
inventory:
organization:
name: Default
type: inventory_source
related:
- success_nodes: []
failure_nodes:
- identifier: node201
- always_nodes: []
- credentials: []
- - identifier: node201
- unified_job_template:
- organization:
- name: Default
- name: job template 1
- type: job_template
- credentials: []
- related:
- success_nodes:
- - identifier: node301
- failure_nodes: []
- always_nodes: []
- credentials: []
- - identifier: node202
+ - identifier: node102
unified_job_template:
organization:
name: Default
name: example-project
type: project
related:
- success_nodes: []
- failure_nodes: []
- always_nodes: []
- credentials: []
- - identifier: node301
- all_parents_must_converge: false
+ success_nodes:
+ - identifier: node201
+ - identifier: node201
unified_job_template:
organization:
name: Default
- name: job template 2
+ name: example-job template
type: job_template
- execution_environment:
- name: My EE
inventory:
- name: Test inventory
+ name: Demo Inventory
organization:
name: Default
related:
+ success_nodes:
+ - identifier: node401
+ failure_nodes:
+ - identifier: node301
+ always_nodes: []
credentials:
- name: cyberark
organization:
name: Default
instance_groups:
- name: SunCavanaugh Cloud
- - name: default
labels:
- name: Custom Label
- - name: Another Custom Label
organization:
name: Default
- register: result
+ - all_parents_must_converge: false
+ identifier: node301
+ unified_job_template:
+ description: Approval node for example
+ timeout: 900
+ type: workflow_approval
+ name: Approval Node for Demo
+ related:
+ success_nodes:
+ - identifier: node401
+ - identifier: node401
+ unified_job_template:
+ name: Cleanup Activity Stream
+ type: system_job_template
'''
@@ -665,8 +663,7 @@ def create_workflow_nodes(module, response, workflow_nodes, workflow_id):
inv_lookup_data = {}
if 'organization' in workflow_node['inventory']:
inv_lookup_data['organization'] = module.resolve_name_to_id('organizations', workflow_node['inventory']['organization']['name'])
- workflow_node_fields['inventory'] = module.get_one(
- 'inventories', name_or_id=workflow_node['inventory']['name'], data=inv_lookup_data)['id']
+ workflow_node_fields['inventory'] = module.get_one('inventories', name_or_id=workflow_node['inventory']['name'], data=inv_lookup_data)['id']
else:
workflow_node_fields['inventory'] = module.get_one('inventories', name_or_id=workflow_node['inventory'])['id']
@@ -841,7 +838,7 @@ def main():
notification_templates_approvals=dict(type="list", elements='str'),
workflow_nodes=dict(type='list', elements='dict', aliases=['schema']),
destroy_current_nodes=dict(type='bool', default=False, aliases=['destroy_current_schema']),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
# Create a module for ourselves
@@ -869,7 +866,7 @@ def main():
search_fields['organization'] = new_fields['organization'] = organization_id
# Attempt to look up an existing item based on the provided data
- existing_item = module.get_one('workflow_job_templates', name_or_id=name, **{'data': search_fields})
+ existing_item = module.get_one('workflow_job_templates', name_or_id=name, check_exists=(state == 'exists'), **{'data': search_fields})
# Attempt to look up credential to copy based on the provided name
if copy_from:
@@ -989,6 +986,7 @@ def main():
# Destroy current nodes if selected.
if destroy_current_nodes:
destroy_workflow_nodes(module, response, workflow_job_template_id)
+ module.json_output['changed'] = True
# Work thorugh and lookup value for schema fields
if workflow_nodes:
diff --git a/ansible_collections/awx/awx/plugins/modules/workflow_job_template_node.py b/ansible_collections/awx/awx/plugins/modules/workflow_job_template_node.py
index a59cd33f9..71ca1c140 100644
--- a/ansible_collections/awx/awx/plugins/modules/workflow_job_template_node.py
+++ b/ansible_collections/awx/awx/plugins/modules/workflow_job_template_node.py
@@ -31,7 +31,7 @@ options:
type: dict
inventory:
description:
- - Inventory applied as a prompt, if job template prompts for inventory
+ - Name, ID, or named URL of the Inventory applied as a prompt, if job template prompts for inventory
type: str
scm_branch:
description:
@@ -73,7 +73,7 @@ options:
- '5'
workflow_job_template:
description:
- - The workflow job template the node exists in.
+ - The workflow job template name, ID, or named URL the node exists in.
- Used for looking up the node, cannot be modified after creation.
required: True
type: str
@@ -81,7 +81,7 @@ options:
- workflow
organization:
description:
- - The organization of the workflow job template the node exists in.
+ - The organization name, ID, or named URL of the workflow job template the node exists in.
- Used for looking up the workflow, not a direct model field.
type: str
unified_job_template:
@@ -93,7 +93,7 @@ options:
type: str
lookup_organization:
description:
- - Organization the inventories, job template, project, inventory source the unified_job_template exists in.
+ - Organization name, ID, or named URL the inventories, job template, project, inventory source the unified_job_template exists in.
- If not provided, will lookup by name only, which does not work with duplicates.
type: str
approval_node:
@@ -145,14 +145,14 @@ options:
elements: str
credentials:
description:
- - Credentials to be applied to job as launch-time prompts.
- - List of credential names.
+ - Credential names, IDs, or named URLs to be applied to job as launch-time prompts.
+ - List of credential names, IDs, or named URLs.
- Uniqueness is not handled rigorously.
type: list
elements: str
execution_environment:
description:
- - Execution Environment applied as a prompt, assuming jot template prompts for execution environment
+ - Execution Environment name, ID, or named URL applied as a prompt, assuming job template prompts for execution environment
type: str
forks:
description:
@@ -160,7 +160,7 @@ options:
type: int
instance_groups:
description:
- - List of Instance Groups applied as a prompt, assuming job template prompts for instance groups
+ - List of Instance Group names, IDs, or named URLs applied as a prompt, assuming job template prompts for instance groups
type: list
elements: str
job_slice_count:
@@ -179,7 +179,7 @@ options:
state:
description:
- Desired state of the resource.
- choices: ["present", "absent"]
+ choices: ["present", "absent", "exists"]
default: "present"
type: str
extends_documentation_fragment: awx.awx.auth
@@ -285,7 +285,7 @@ def main():
job_slice_count=dict(type='int'),
labels=dict(type='list', elements='str'),
timeout=dict(type='int'),
- state=dict(choices=['present', 'absent'], default='present'),
+ state=dict(choices=['present', 'absent', 'exists'], default='present'),
)
mutually_exclusive = [("unified_job_template", "approval_node")]
required_if = [
@@ -327,7 +327,7 @@ def main():
search_fields['workflow_job_template'] = new_fields['workflow_job_template'] = workflow_job_template_id
# Attempt to look up an existing item based on the provided data
- existing_item = module.get_one('workflow_job_template_nodes', **{'data': search_fields})
+ existing_item = module.get_one('workflow_job_template_nodes', check_exists=(state == 'exists'), **{'data': search_fields})
if state == 'absent':
# If the state was absent we can let the module delete it if needed, the module will handle exiting from this
diff --git a/ansible_collections/awx/awx/plugins/modules/workflow_launch.py b/ansible_collections/awx/awx/plugins/modules/workflow_launch.py
index 1613e4fa8..c06841098 100644
--- a/ansible_collections/awx/awx/plugins/modules/workflow_launch.py
+++ b/ansible_collections/awx/awx/plugins/modules/workflow_launch.py
@@ -27,13 +27,13 @@ options:
- workflow_template
organization:
description:
- - Organization the workflow job template exists in.
+ - Organization name, ID, or named URL the workflow job template exists in.
- Used to help lookup the object, cannot be modified using this module.
- If not provided, will lookup by name only, which does not work with duplicates.
type: str
inventory:
description:
- - Inventory to use for the job ran with this workflow, only used if prompt for inventory is set.
+ - Inventory name, ID, or named URL to use for the job ran with this workflow, only used if prompt for inventory is set.
type: str
limit:
description:
@@ -91,7 +91,6 @@ EXAMPLES = '''
'''
from ..module_utils.controller_api import ControllerAPIModule
-import json
def main():
@@ -116,15 +115,18 @@ def main():
name = module.params.get('name')
organization = module.params.get('organization')
inventory = module.params.get('inventory')
- optional_args['limit'] = module.params.get('limit')
wait = module.params.get('wait')
interval = module.params.get('interval')
timeout = module.params.get('timeout')
- # Special treatment of extra_vars parameter
- extra_vars = module.params.get('extra_vars')
- if extra_vars is not None:
- optional_args['extra_vars'] = json.dumps(extra_vars)
+ for field_name in (
+ 'limit',
+ 'extra_vars',
+ 'scm_branch',
+ ):
+ field_val = module.params.get(field_name)
+ if field_val is not None:
+ optional_args[field_name] = field_val
# Create a datastructure to pass into our job launch
post_data = {}
diff --git a/ansible_collections/awx/awx/test/awx/conftest.py b/ansible_collections/awx/awx/test/awx/conftest.py
index 626f85936..5bf288ba6 100644
--- a/ansible_collections/awx/awx/test/awx/conftest.py
+++ b/ansible_collections/awx/awx/test/awx/conftest.py
@@ -18,28 +18,53 @@ import pytest
from ansible.module_utils.six import raise_from
from awx.main.tests.functional.conftest import _request
-from awx.main.models import Organization, Project, Inventory, JobTemplate, Credential, CredentialType, ExecutionEnvironment, UnifiedJob
+from awx.main.tests.functional.conftest import credentialtype_scm, credentialtype_ssh # noqa: F401; pylint: disable=unused-variable
+from awx.main.models import (
+ Organization,
+ Project,
+ Inventory,
+ JobTemplate,
+ Credential,
+ CredentialType,
+ ExecutionEnvironment,
+ UnifiedJob,
+ WorkflowJobTemplate,
+ NotificationTemplate,
+ Schedule,
+)
from django.db import transaction
-try:
- import tower_cli # noqa
- HAS_TOWER_CLI = True
-except ImportError:
- HAS_TOWER_CLI = False
+HAS_TOWER_CLI = False
+HAS_AWX_KIT = False
+logger = logging.getLogger('awx.main.tests')
-try:
- # Because awxkit will be a directory at the root of this makefile and we are using python3, import awxkit will work even if its not installed.
- # However, awxkit will not contain api whih causes a stack failure down on line 170 when we try to mock it.
- # So here we are importing awxkit.api to prevent that. Then you only get an error on tests for awxkit functionality.
- import awxkit.api # noqa
- HAS_AWX_KIT = True
-except ImportError:
- HAS_AWX_KIT = False
+@pytest.fixture(autouse=True)
+def awxkit_path_set(monkeypatch):
+ """Monkey patch sys.path, insert awxkit source code so that
+ the package does not need to be installed.
+ """
+ base_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, 'awxkit'))
+ monkeypatch.syspath_prepend(base_folder)
-logger = logging.getLogger('awx.main.tests')
+
+@pytest.fixture(autouse=True)
+def import_awxkit():
+ global HAS_TOWER_CLI
+ global HAS_AWX_KIT
+ try:
+ import tower_cli # noqa
+ HAS_TOWER_CLI = True
+ except ImportError:
+ HAS_TOWER_CLI = False
+
+ try:
+ import awxkit # noqa
+ HAS_AWX_KIT = True
+ except ImportError:
+ HAS_AWX_KIT = False
def sanitize_dict(din):
@@ -123,7 +148,7 @@ def run_module(request, collection_import):
sanitize_dict(py_data)
resp._content = bytes(json.dumps(django_response.data), encoding='utf8')
resp.status_code = django_response.status_code
- resp.headers = {'X-API-Product-Name': 'AWX', 'X-API-Product-Version': '0.0.1-devel'}
+ resp.headers = dict(django_response.headers)
if request.config.getoption('verbose') > 0:
logger.info('%s %s by %s, code:%s', method, '/api/' + url.split('/api/')[1], request_user.username, resp.status_code)
@@ -156,10 +181,8 @@ def run_module(request, collection_import):
resource_class = resource_module.ControllerAWXKitModule
elif getattr(resource_module, 'ControllerAPIModule', None):
resource_class = resource_module.ControllerAPIModule
- elif getattr(resource_module, 'TowerLegacyModule', None):
- resource_class = resource_module.TowerLegacyModule
else:
- raise RuntimeError("The module has neither a TowerLegacyModule, ControllerAWXKitModule or a ControllerAPIModule")
+ raise RuntimeError("The module has neither a ControllerAWXKitModule or a ControllerAPIModule")
with mock.patch.object(resource_class, '_load_params', new=mock_load_params):
# Call the test utility (like a mock server) instead of issuing HTTP requests
@@ -236,10 +259,8 @@ def job_template(project, inventory):
@pytest.fixture
-def machine_credential(organization):
- ssh_type = CredentialType.defaults['ssh']()
- ssh_type.save()
- return Credential.objects.create(credential_type=ssh_type, name='machine-cred', inputs={'username': 'test_user', 'password': 'pas4word'})
+def machine_credential(credentialtype_ssh, organization): # noqa: F811
+ return Credential.objects.create(credential_type=credentialtype_ssh, name='machine-cred', inputs={'username': 'test_user', 'password': 'pas4word'})
@pytest.fixture
@@ -253,9 +274,7 @@ def vault_credential(organization):
def kube_credential():
ct = CredentialType.defaults['kubernetes_bearer_token']()
ct.save()
- return Credential.objects.create(
- credential_type=ct, name='kube-cred', inputs={'host': 'my.cluster', 'bearer_token': 'my-token', 'verify_ssl': False}
- )
+ return Credential.objects.create(credential_type=ct, name='kube-cred', inputs={'host': 'my.cluster', 'bearer_token': 'my-token', 'verify_ssl': False})
@pytest.fixture
@@ -288,3 +307,42 @@ def mock_has_unpartitioned_events():
# We mock this out to circumvent the migration query.
with mock.patch.object(UnifiedJob, 'has_unpartitioned_events', new=False) as _fixture:
yield _fixture
+
+
+@pytest.fixture
+def workflow_job_template(organization, inventory):
+ return WorkflowJobTemplate.objects.create(name='test-workflow_job_template', organization=organization, inventory=inventory)
+
+
+@pytest.fixture
+def notification_template(organization):
+ return NotificationTemplate.objects.create(
+ name='test-notification_template',
+ organization=organization,
+ notification_type="webhook",
+ notification_configuration=dict(
+ url="http://localhost",
+ username="",
+ password="",
+ headers={
+ "Test": "Header",
+ },
+ ),
+ )
+
+
+@pytest.fixture
+def scm_credential(credentialtype_scm, organization): # noqa: F811
+ return Credential.objects.create(
+ credential_type=credentialtype_scm, name='scm-cred', inputs={'username': 'optimus', 'password': 'prime'}, organization=organization
+ )
+
+
+@pytest.fixture
+def rrule():
+ return 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1'
+
+
+@pytest.fixture
+def schedule(job_template, rrule):
+ return Schedule.objects.create(unified_job_template=job_template, name='test-sched', rrule=rrule)
diff --git a/ansible_collections/awx/awx/test/awx/test_bulk.py b/ansible_collections/awx/awx/test/awx/test_bulk.py
index 6ba97e900..da45621dd 100644
--- a/ansible_collections/awx/awx/test/awx/test_bulk.py
+++ b/ansible_collections/awx/awx/test/awx/test_bulk.py
@@ -10,7 +10,7 @@ from awx.main.models import WorkflowJob
@pytest.mark.django_db
def test_bulk_job_launch(run_module, admin_user, job_template):
jobs = [dict(unified_job_template=job_template.id)]
- run_module(
+ result = run_module(
'bulk_job_launch',
{
'name': "foo-bulk-job",
@@ -21,6 +21,8 @@ def test_bulk_job_launch(run_module, admin_user, job_template):
},
admin_user,
)
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result.get('changed'), result
bulk_job = WorkflowJob.objects.get(name="foo-bulk-job")
assert bulk_job.extra_vars == '{"animal": "owl"}'
@@ -30,7 +32,7 @@ def test_bulk_job_launch(run_module, admin_user, job_template):
@pytest.mark.django_db
def test_bulk_host_create(run_module, admin_user, inventory):
hosts = [dict(name="127.0.0.1"), dict(name="foo.dns.org")]
- run_module(
+ result = run_module(
'bulk_host_create',
{
'inventory': inventory.name,
@@ -38,6 +40,33 @@ def test_bulk_host_create(run_module, admin_user, inventory):
},
admin_user,
)
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result.get('changed'), result
resp_hosts = inventory.hosts.all().values_list('name', flat=True)
for h in hosts:
assert h['name'] in resp_hosts
+
+
+@pytest.mark.django_db
+def test_bulk_host_delete(run_module, admin_user, inventory):
+ hosts = [dict(name="127.0.0.1"), dict(name="foo.dns.org")]
+ result = run_module(
+ 'bulk_host_create',
+ {
+ 'inventory': inventory.name,
+ 'hosts': hosts,
+ },
+ admin_user,
+ )
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result.get('changed'), result
+ resp_hosts_ids = list(inventory.hosts.all().values_list('id', flat=True))
+ result = run_module(
+ 'bulk_host_delete',
+ {
+ 'hosts': resp_hosts_ids,
+ },
+ admin_user,
+ )
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result.get('changed'), result
diff --git a/ansible_collections/awx/awx/test/awx/test_completeness.py b/ansible_collections/awx/awx/test/awx/test_completeness.py
index ef3d70727..42d013df6 100644
--- a/ansible_collections/awx/awx/test/awx/test_completeness.py
+++ b/ansible_collections/awx/awx/test/awx/test_completeness.py
@@ -20,7 +20,9 @@ read_only_endpoints_with_modules = ['settings', 'role', 'project_update', 'workf
# If a module should not be created for an endpoint and the endpoint is not read-only add it here
# THINK HARD ABOUT DOING THIS
-no_module_for_endpoint = []
+no_module_for_endpoint = [
+ 'constructed_inventory', # This is a view for inventory with kind=constructed
+]
# Some modules work on the related fields of an endpoint. These modules will not have an auto-associated endpoint
no_endpoint_for_module = [
@@ -48,10 +50,11 @@ no_endpoint_for_module = [
extra_endpoints = {
'bulk_job_launch': '/api/v2/bulk/job_launch/',
'bulk_host_create': '/api/v2/bulk/host_create/',
+ 'bulk_host_delete': '/api/v2/bulk/host_delete/',
}
# Global module parameters we can ignore
-ignore_parameters = ['state', 'new_name', 'update_secrets', 'copy_from']
+ignore_parameters = ['state', 'new_name', 'update_secrets', 'copy_from', 'is_internal']
# Some modules take additional parameters that do not appear in the API
# Add the module name as the key with the value being the list of params to ignore
@@ -245,7 +248,7 @@ def test_completeness(collection_import, request, admin_user, job_template, exec
singular_endpoint = '{0}'.format(endpoint)
if singular_endpoint.endswith('ies'):
singular_endpoint = singular_endpoint[:-3]
- if singular_endpoint != 'settings' and singular_endpoint.endswith('s'):
+ elif singular_endpoint != 'settings' and singular_endpoint.endswith('s'):
singular_endpoint = singular_endpoint[:-1]
module_name = '{0}'.format(singular_endpoint)
diff --git a/ansible_collections/awx/awx/test/awx/test_credential.py b/ansible_collections/awx/awx/test/awx/test_credential.py
index f12594bee..c67953197 100644
--- a/ansible_collections/awx/awx/test/awx/test_credential.py
+++ b/ansible_collections/awx/awx/test/awx/test_credential.py
@@ -132,3 +132,20 @@ def test_secret_field_write_twice(run_module, organization, admin_user, cred_typ
else:
assert result.get('changed') is False, result
assert Credential.objects.get(id=result['id']).get_input('token') == val1
+
+
+@pytest.mark.django_db
+@pytest.mark.parametrize('state', ('present', 'absent', 'exists'))
+def test_credential_state(run_module, organization, admin_user, cred_type, state):
+ result = run_module(
+ 'credential',
+ dict(
+ name='Galaxy Token for Steve',
+ organization=organization.name,
+ credential_type=cred_type.name,
+ inputs={'token': '7rEZK38DJl58A7RxA6EC7lLvUHbBQ1'},
+ state=state,
+ ),
+ admin_user,
+ )
+ assert not result.get('failed', False), result.get('msg', result)
diff --git a/ansible_collections/awx/awx/test/awx/test_export.py b/ansible_collections/awx/awx/test/awx/test_export.py
new file mode 100644
index 000000000..70c8466ec
--- /dev/null
+++ b/ansible_collections/awx/awx/test/awx/test_export.py
@@ -0,0 +1,150 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import pytest
+
+from awx.main.models.execution_environments import ExecutionEnvironment
+from awx.main.models.jobs import JobTemplate
+
+from awx.main.tests.functional.conftest import user, system_auditor # noqa: F401; pylint: disable=unused-import
+
+
+ASSETS = set([
+ "users",
+ "organizations",
+ "teams",
+ "credential_types",
+ "credentials",
+ "notification_templates",
+ "projects",
+ "inventory",
+ "inventory_sources",
+ "job_templates",
+ "workflow_job_templates",
+ "execution_environments",
+ "applications",
+ "schedules",
+])
+
+
+@pytest.fixture
+def job_template(project, inventory, organization, machine_credential):
+ jt = JobTemplate.objects.create(name='test-jt', project=project, inventory=inventory, organization=organization, playbook='helloworld.yml')
+ jt.credentials.add(machine_credential)
+ jt.save()
+ return jt
+
+
+@pytest.fixture
+def execution_environment(organization):
+ return ExecutionEnvironment.objects.create(name="test-ee", description="test-ee", managed=False, organization=organization)
+
+
+def find_by(result, name, key, value):
+ for c in result[name]:
+ if c[key] == value:
+ return c
+ values = [c.get(key, None) for c in result[name]]
+ raise ValueError(f"Failed to find assets['{name}'][{key}] = '{value}' valid values are {values}")
+
+
+@pytest.mark.django_db
+def test_export(run_module, admin_user):
+ """
+ There should be nothing to export EXCEPT the admin user.
+ """
+ result = run_module('export', dict(all=True), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assets = result['assets']
+
+ assert set(result['assets'].keys()) == ASSETS
+
+ u = find_by(assets, 'users', 'username', 'admin')
+ assert u['is_superuser'] is True
+
+ all_assets_except_users = {k: v for k, v in assets.items() if k != 'users'}
+
+ for k, v in all_assets_except_users.items():
+ assert v == [], f"Expected resource {k} to be empty. Instead it is {v}"
+
+
+@pytest.mark.django_db
+def test_export_simple(
+ run_module,
+ organization,
+ project,
+ inventory,
+ job_template,
+ scm_credential,
+ machine_credential,
+ workflow_job_template,
+ execution_environment,
+ notification_template,
+ rrule,
+ schedule,
+ admin_user,
+):
+ """
+ TODO: Ensure there aren't _more_ results in each resource than we expect
+ """
+ result = run_module('export', dict(all=True), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assets = result['assets']
+
+ u = find_by(assets, 'users', 'username', 'admin')
+ assert u['is_superuser'] is True
+
+ find_by(assets, 'organizations', 'name', 'Default')
+
+ r = find_by(assets, 'credentials', 'name', 'scm-cred')
+ assert r['credential_type']['kind'] == 'scm'
+ assert r['credential_type']['name'] == 'Source Control'
+
+ r = find_by(assets, 'credentials', 'name', 'machine-cred')
+ assert r['credential_type']['kind'] == 'ssh'
+ assert r['credential_type']['name'] == 'Machine'
+
+ r = find_by(assets, 'job_templates', 'name', 'test-jt')
+ assert r['natural_key']['organization']['name'] == 'Default'
+ assert r['inventory']['name'] == 'test-inv'
+ assert r['project']['name'] == 'test-proj'
+
+ find_by(r['related'], 'credentials', 'name', 'machine-cred')
+
+ r = find_by(assets, 'inventory', 'name', 'test-inv')
+ assert r['organization']['name'] == 'Default'
+
+ r = find_by(assets, 'projects', 'name', 'test-proj')
+ assert r['organization']['name'] == 'Default'
+
+ r = find_by(assets, 'workflow_job_templates', 'name', 'test-workflow_job_template')
+ assert r['natural_key']['organization']['name'] == 'Default'
+ assert r['inventory']['name'] == 'test-inv'
+
+ r = find_by(assets, 'execution_environments', 'name', 'test-ee')
+ assert r['organization']['name'] == 'Default'
+
+ r = find_by(assets, 'schedules', 'name', 'test-sched')
+ assert r['rrule'] == rrule
+
+ r = find_by(assets, 'notification_templates', 'name', 'test-notification_template')
+ assert r['organization']['name'] == 'Default'
+ assert r['notification_configuration']['url'] == 'http://localhost'
+
+
+@pytest.mark.django_db
+def test_export_system_auditor(run_module, organization, system_auditor): # noqa: F811
+ """
+ This test illustrates that export of resources can now happen
+ when ran as non-root user (i.e. system auditor). The OPTIONS
+ endpoint does NOT return POST for a system auditor, but now we
+ make a best-effort to parse the description string, which will
+ often have the fields.
+ """
+ result = run_module('export', dict(all=True), system_auditor)
+ assert not result.get('failed', False), result.get('msg', result)
+ assert 'msg' not in result
+ assert 'assets' in result
+
+ find_by(result['assets'], 'organizations', 'name', 'Default')
diff --git a/ansible_collections/awx/awx/test/awx/test_instance.py b/ansible_collections/awx/awx/test/awx/test_instance.py
new file mode 100644
index 000000000..06d1fe94b
--- /dev/null
+++ b/ansible_collections/awx/awx/test/awx/test_instance.py
@@ -0,0 +1,46 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import pytest
+
+from awx.main.models import Instance
+from django.test.utils import override_settings
+
+
+@pytest.mark.django_db
+def test_peers_adding_and_removing(run_module, admin_user):
+ with override_settings(IS_K8S=True):
+ result = run_module(
+ 'instance',
+ {'hostname': 'hopnode', 'node_type': 'hop', 'node_state': 'installed', 'listener_port': 6789},
+ admin_user,
+ )
+ assert result['changed']
+
+ hop_node = Instance.objects.get(pk=result.get('id'))
+
+ assert hop_node.node_type == 'hop'
+
+ address = hop_node.receptor_addresses.get(pk=result.get('id'))
+ assert address.port == 6789
+
+ result = run_module(
+ 'instance',
+ {'hostname': 'executionnode', 'node_type': 'execution', 'node_state': 'installed', 'peers': ['hopnode']},
+ admin_user,
+ )
+ assert result['changed']
+
+ execution_node = Instance.objects.get(pk=result.get('id'))
+
+ assert set(execution_node.peers.all()) == {address}
+
+ result = run_module(
+ 'instance',
+ {'hostname': 'executionnode', 'node_type': 'execution', 'node_state': 'installed', 'peers': []},
+ admin_user,
+ )
+
+ assert result['changed']
+ assert set(execution_node.peers.all()) == set()
diff --git a/ansible_collections/awx/awx/test/awx/test_job_template.py b/ansible_collections/awx/awx/test/awx/test_job_template.py
index e785a63a3..5ee114359 100644
--- a/ansible_collections/awx/awx/test/awx/test_job_template.py
+++ b/ansible_collections/awx/awx/test/awx/test_job_template.py
@@ -2,9 +2,11 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
+import random
+
import pytest
-from awx.main.models import ActivityStream, JobTemplate, Job, NotificationTemplate
+from awx.main.models import ActivityStream, JobTemplate, Job, NotificationTemplate, Label
@pytest.mark.django_db
@@ -244,6 +246,42 @@ def test_job_template_with_survey_encrypted_default(run_module, admin_user, proj
@pytest.mark.django_db
+def test_associate_changed_status(run_module, admin_user, organization, project):
+ # create JT and labels
+ jt = JobTemplate.objects.create(name='foo', project=project, playbook='helloworld.yml')
+ labels = [Label.objects.create(name=f'foo{i}', organization=organization) for i in range(10)]
+
+ # sanity: no-op without labels involved
+ result = run_module('job_template', dict(name=jt.name, playbook='helloworld.yml'), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result['changed'] is False
+
+ # first time adding labels, this should make the label list equal to what was specified
+ result = run_module('job_template', dict(name=jt.name, playbook='helloworld.yml', labels=[l.name for l in labels]), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result['changed']
+ assert set(l.id for l in jt.labels.all()) == set(l.id for l in labels)
+
+ # shuffling the labels should not result in any change
+ random.shuffle(labels)
+ result = run_module('job_template', dict(name=jt.name, playbook='helloworld.yml', labels=[l.name for l in labels]), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result['changed'] is False
+
+ # not specifying labels should not change labels
+ result = run_module('job_template', dict(name=jt.name, playbook='helloworld.yml'), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result['changed'] is False
+
+ # should be able to remove only some labels
+ fewer_labels = labels[:7]
+ result = run_module('job_template', dict(name=jt.name, playbook='helloworld.yml', labels=[l.name for l in fewer_labels]), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assert result['changed']
+ assert set(l.id for l in jt.labels.all()) == set(l.id for l in fewer_labels)
+
+
+@pytest.mark.django_db
def test_associate_only_on_success(run_module, admin_user, organization, project):
jt = JobTemplate.objects.create(
name='foo',
diff --git a/ansible_collections/awx/awx/test/awx/test_module_utils.py b/ansible_collections/awx/awx/test/awx/test_module_utils.py
index 088b5368a..cbdc172a3 100644
--- a/ansible_collections/awx/awx/test/awx/test_module_utils.py
+++ b/ansible_collections/awx/awx/test/awx/test_module_utils.py
@@ -76,21 +76,6 @@ def test_version_warning(collection_import, silence_warning):
)
-def test_no_version_warning(collection_import, silence_warning):
- ControllerAPIModule = collection_import('plugins.module_utils.controller_api').ControllerAPIModule
- cli_data = {'ANSIBLE_MODULE_ARGS': {}}
- testargs = ['module_file2.py', json.dumps(cli_data)]
- with mock.patch.object(sys, 'argv', testargs):
- with mock.patch('ansible.module_utils.urls.Request.open', new=mock_no_ping_response):
- my_module = ControllerAPIModule(argument_spec=dict())
- my_module._COLLECTION_VERSION = "2.0.0"
- my_module._COLLECTION_TYPE = "awx"
- my_module.get_endpoint('ping')
- silence_warning.assert_called_once_with(
- 'You are using the {0} version of this collection but connecting to a controller that did not return a version'.format(my_module._COLLECTION_VERSION)
- )
-
-
def test_version_warning_strictness_awx(collection_import, silence_warning):
ControllerAPIModule = collection_import('plugins.module_utils.controller_api').ControllerAPIModule
cli_data = {'ANSIBLE_MODULE_ARGS': {}}
diff --git a/ansible_collections/awx/awx/test/awx/test_notification_template.py b/ansible_collections/awx/awx/test/awx/test_notification_template.py
index 8bd2647b0..346ac4105 100644
--- a/ansible_collections/awx/awx/test/awx/test_notification_template.py
+++ b/ansible_collections/awx/awx/test/awx/test_notification_template.py
@@ -155,4 +155,4 @@ def test_build_notification_message_undefined(run_module, admin_user, organizati
nt = NotificationTemplate.objects.get(id=result['id'])
body = job.build_notification_message(nt, 'running')
- assert '{"started_by": "My Placeholder"}' in body[1]
+ assert 'The template rendering return a blank body' in body[1]
diff --git a/ansible_collections/awx/awx/test/awx/test_organization.py b/ansible_collections/awx/awx/test/awx/test_organization.py
index 08d35ed6f..e6b3cc5e2 100644
--- a/ansible_collections/awx/awx/test/awx/test_organization.py
+++ b/ansible_collections/awx/awx/test/awx/test_organization.py
@@ -3,8 +3,9 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
import pytest
+import random
-from awx.main.models import Organization
+from awx.main.models import Organization, Credential, CredentialType
@pytest.mark.django_db
@@ -30,3 +31,63 @@ def test_create_organization(run_module, admin_user):
assert result == {"name": "foo", "changed": True, "id": org.id, "invocation": {"module_args": module_args}}
assert org.description == 'barfoo'
+
+
+@pytest.mark.django_db
+def test_galaxy_credential_order(run_module, admin_user):
+ org = Organization.objects.create(name='foo')
+ cred_type = CredentialType.defaults['galaxy_api_token']()
+ cred_type.save()
+
+ cred_ids = []
+ for number in range(1, 10):
+ new_cred = Credential.objects.create(name=f"Galaxy Credential {number}", credential_type=cred_type, organization=org, inputs={'url': 'www.redhat.com'})
+ cred_ids.append(new_cred.id)
+
+ random.shuffle(cred_ids)
+
+ module_args = {
+ 'name': 'foo',
+ 'state': 'present',
+ 'controller_host': None,
+ 'controller_username': None,
+ 'controller_password': None,
+ 'validate_certs': None,
+ 'controller_oauthtoken': None,
+ 'controller_config_file': None,
+ 'galaxy_credentials': cred_ids,
+ }
+
+ result = run_module('organization', module_args, admin_user)
+ print(result)
+ assert result['changed'] is True
+
+ cred_order_in_org = []
+ for a_cred in org.galaxy_credentials.all():
+ cred_order_in_org.append(a_cred.id)
+
+ assert cred_order_in_org == cred_ids
+
+ # Shuffle them up and try again to make sure a new order is honored
+ random.shuffle(cred_ids)
+
+ module_args = {
+ 'name': 'foo',
+ 'state': 'present',
+ 'controller_host': None,
+ 'controller_username': None,
+ 'controller_password': None,
+ 'validate_certs': None,
+ 'controller_oauthtoken': None,
+ 'controller_config_file': None,
+ 'galaxy_credentials': cred_ids,
+ }
+
+ result = run_module('organization', module_args, admin_user)
+ assert result['changed'] is True
+
+ cred_order_in_org = []
+ for a_cred in org.galaxy_credentials.all():
+ cred_order_in_org.append(a_cred.id)
+
+ assert cred_order_in_org == cred_ids
diff --git a/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command/tasks/main.yml
index 316315df3..3180045e8 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command/tasks/main.yml
@@ -9,6 +9,7 @@
inv_name: "AWX-Collection-tests-ad_hoc_command-inventory-{{ test_id }}"
ssh_cred_name: "AWX-Collection-tests-ad_hoc_command-ssh-cred-{{ test_id }}"
org_name: "AWX-Collection-tests-ad_hoc_command-org-{{ test_id }}"
+ ee_name: "AWX-Collection-tests-ad_hoc_command-ee-{{ test_id }}"
- name: Create a New Organization
organization:
@@ -34,6 +35,16 @@
credential_type: 'Machine'
state: present
+- name: Create an Execution Environment
+ execution_environment:
+ name: "{{ ee_name }}"
+ organization: "{{ org_name }}"
+ description: "EE for Testing"
+ image: quay.io/ansible/awx-ee
+ pull: always
+ state: present
+ register: result_ee
+
- name: Launch an Ad Hoc Command waiting for it to finish
ad_hoc_command:
inventory: "{{ inv_name }}"
@@ -61,6 +72,36 @@
- "result is changed"
- "result.status == 'successful'"
+- name: Launch an Ad Hoc Command with extra_vars
+ ad_hoc_command:
+ inventory: "Demo Inventory"
+ credential: "{{ ssh_cred_name }}"
+ module_name: "ping"
+ extra_vars:
+ var1: "test var"
+ wait: true
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+ - "result.status == 'successful'"
+
+- name: Launch an Ad Hoc Command with Execution Environment specified
+ ad_hoc_command:
+ inventory: "Demo Inventory"
+ credential: "{{ ssh_cred_name }}"
+ execution_environment: "{{ ee_name }}"
+ module_name: "ping"
+ wait: true
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+ - "result.status == 'successful'"
+ - "lookup('awx.awx.controller_api', 'ad_hoc_commands/' ~ result.id)['execution_environment'] == result_ee.id"
+
- name: Check module fails with correct msg
ad_hoc_command:
inventory: "{{ inv_name }}"
@@ -75,6 +116,13 @@
- "result is not changed"
- "'Does not exist' in result.response['json']['module_name'][0]"
+- name: Delete the Execution Environment
+ execution_environment:
+ name: "{{ ee_name }}"
+ organization: "{{ org_name }}"
+ image: quay.io/ansible/awx-ee
+ state: absent
+
- name: Delete the Credential
credential:
name: "{{ ssh_cred_name }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_cancel/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_cancel/tasks/main.yml
index f7ffe9bc9..55c1803db 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_cancel/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_cancel/tasks/main.yml
@@ -46,27 +46,24 @@
that:
- "command is changed"
-- name: Timeout waiting for the command to cancel
+- name: Cancel the command
ad_hoc_command_cancel:
command_id: "{{ command.id }}"
- timeout: -1
+ request_timeout: 60
register: results
- ignore_errors: true
- assert:
that:
- - results is failed
- - "results['msg'] == 'Monitoring of ad hoc command aborted due to timeout'"
-
-- block:
- - name: "Wait for up to a minute until the job enters the can_cancel: False state"
- debug:
- msg: "The job can_cancel status has transitioned into False, we can proceed with testing"
- until: not job_status
- retries: 6
- delay: 10
- vars:
- job_status: "{{ lookup('awx.awx.controller_api', 'ad_hoc_commands/'+ command.id | string +'/cancel')['can_cancel'] }}"
+ - results is changed
+
+- name: "Wait for up to a minute until the job enters the can_cancel: False state"
+ debug:
+ msg: "The job can_cancel status has transitioned into False, we can proceed with testing"
+ until: not job_status
+ retries: 6
+ delay: 10
+ vars:
+ job_status: "{{ lookup('awx.awx.controller_api', 'ad_hoc_commands/'+ command.id | string +'/cancel')['can_cancel'] }}"
- name: Cancel the command with hard error if it's not running
ad_hoc_command_cancel:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_wait/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_wait/tasks/main.yml
index 941774874..61bfd42fc 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_wait/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/ad_hoc_command_wait/tasks/main.yml
@@ -108,9 +108,9 @@
- assert:
that:
- - wait_results is failed
- - 'wait_results.status == "canceled"'
- - "wait_results.msg == 'The ad hoc command - {{ command.id }}, failed'"
+ - 'wait_results.status in ["successful", "canceled"]'
+ fail_msg: "Ad hoc command stdout: {{ lookup('awx.awx.controller_api', 'ad_hoc_commands/' + command.id | string + '/stdout/?format=json') }}"
+ success_msg: "Ad hoc command finished with status {{ wait_results.status }}"
- name: Delete the Credential
credential:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/application/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/application/tasks/main.yml
index ba76763a4..864d4d0ac 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/application/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/application/tasks/main.yml
@@ -2,6 +2,7 @@
- name: Generate a test id
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
- name: Generate names
set_fact:
@@ -23,6 +24,43 @@
that:
- "result is changed"
+ - name: Run an application with exists
+ application:
+ name: "{{ app1_name }}"
+ authorization_grant_type: "password"
+ client_type: "public"
+ organization: "Default"
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is not changed"
+
+ - name: Delete our application
+ application:
+ name: "{{ app1_name }}"
+ organization: "Default"
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
+ - name: Run an application with exists
+ application:
+ name: "{{ app1_name }}"
+ authorization_grant_type: "password"
+ client_type: "public"
+ organization: "Default"
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
- name: Delete our application
application:
name: "{{ app1_name }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/bulk_host_create/main.yml b/ansible_collections/awx/awx/tests/integration/targets/bulk_host_create/tasks/main.yml
index 892154265..892154265 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/bulk_host_create/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/bulk_host_create/tasks/main.yml
diff --git a/ansible_collections/awx/awx/tests/integration/targets/bulk_host_delete/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/bulk_host_delete/tasks/main.yml
new file mode 100644
index 000000000..5f38efe7c
--- /dev/null
+++ b/ansible_collections/awx/awx/tests/integration/targets/bulk_host_delete/tasks/main.yml
@@ -0,0 +1,80 @@
+---
+- 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 a unique name"
+ set_fact:
+ bulk_inv_name: "AWX-Collection-tests-bulk_host_create-{{ test_id }}"
+
+- name: "Get our collection package"
+ controller_meta:
+ register: "controller_meta"
+
+- name: "Generate the name of our plugin"
+ set_fact:
+ plugin_name: "{{ controller_meta.prefix }}.controller_api"
+
+- name: "Create an inventory"
+ inventory:
+ name: "{{ bulk_inv_name }}"
+ organization: "Default"
+ state: "present"
+ register: "inventory_result"
+
+- name: "Bulk Host Create"
+ bulk_host_create:
+ hosts:
+ - name: "123.456.789.123"
+ description: "myhost1"
+ variables:
+ food: "carrot"
+ color: "orange"
+ - name: "example.dns.gg"
+ description: "myhost2"
+ enabled: "false"
+ inventory: "{{ bulk_inv_name }}"
+ register: "result"
+
+- assert:
+ that:
+ - "result is not failed"
+
+- name: "Get our collection package"
+ controller_meta:
+ register: "controller_meta"
+
+- name: "Generate the name of our plugin"
+ set_fact:
+ plugin_name: "{{ controller_meta.prefix }}.controller_api"
+
+- name: "Setting the inventory hosts endpoint"
+ set_fact:
+ endpoint: "inventories/{{ inventory_result.id }}/hosts/"
+
+- name: "Get hosts information from inventory"
+ set_fact:
+ hosts_created: "{{ query(plugin_name, endpoint, return_objects=True) }}"
+ host_id_list: []
+
+- name: "Extract host IDs from hosts information"
+ set_fact:
+ host_id_list: "{{ host_id_list + [item.id] }}"
+ loop: "{{ hosts_created }}"
+
+- name: "Bulk Host Delete"
+ bulk_host_delete:
+ hosts: "{{ host_id_list }}"
+ register: "result"
+
+- assert:
+ that:
+ - "result is not failed"
+
+# cleanup
+- name: "Delete inventory"
+ inventory:
+ name: "{{ bulk_inv_name }}"
+ organization: "Default"
+ state: "absent"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/bulk_job_launch/main.yml b/ansible_collections/awx/awx/tests/integration/targets/bulk_job_launch/tasks/main.yml
index cd152a8ee..f4a107ecf 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/bulk_job_launch/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/bulk_job_launch/tasks/main.yml
@@ -60,7 +60,7 @@
- result['job_info']['skip_tags'] == "skipbaz"
- result['job_info']['limit'] == "localhost"
- result['job_info']['job_tags'] == "Hello World"
- - result['job_info']['inventory'] == {{ inventory_id }}
+ - result['job_info']['inventory'] == inventory_id | int
- "result['job_info']['extra_vars'] == '{\"animal\": \"bear\", \"food\": \"carrot\"}'"
# cleanup
diff --git a/ansible_collections/awx/awx/tests/integration/targets/credential/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/credential/tasks/main.yml
index 57b2168f2..34dd058d9 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/credential/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/credential/tasks/main.yml
@@ -47,6 +47,55 @@
that:
- "result is changed"
+- name: Create an Org-specific credential with an ID with exists
+ credential:
+ name: "{{ ssh_cred_name1 }}"
+ organization: Default
+ credential_type: Machine
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - "result is not changed"
+
+- name: Delete an Org-specific credential with an ID
+ credential:
+ name: "{{ ssh_cred_name1 }}"
+ organization: Default
+ credential_type: Machine
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Delete a credential without credential_type
+ credential:
+ name: "{{ ssh_cred_name1 }}"
+ organization: Default
+ state: absent
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - "result is failed"
+
+
+- name: Create an Org-specific credential with an ID with exists
+ credential:
+ name: "{{ ssh_cred_name1 }}"
+ organization: Default
+ credential_type: Machine
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
- name: Delete a Org-specific credential
credential:
name: "{{ ssh_cred_name1 }}"
@@ -178,6 +227,60 @@
that:
- result is changed
+- name: Delete an SSH credential
+ credential:
+ name: "{{ ssh_cred_name2 }}"
+ organization: Default
+ state: absent
+ credential_type: Machine
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Ensure existence of SSH credential
+ credential:
+ name: "{{ ssh_cred_name2 }}"
+ organization: Default
+ state: exists
+ credential_type: Machine
+ description: An example SSH awx.awx.credential
+ inputs:
+ username: joe
+ password: secret
+ become_method: sudo
+ become_username: superuser
+ become_password: supersecret
+ ssh_key_data: "{{ ssh_key_data }}"
+ ssh_key_unlock: "passphrase"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Ensure existence of SSH credential, not updating any inputs
+ credential:
+ name: "{{ ssh_cred_name2 }}"
+ organization: Default
+ state: exists
+ credential_type: Machine
+ description: An example SSH awx.awx.credential
+ inputs:
+ username: joe
+ password: no-update-secret
+ become_method: sudo
+ become_username: some-other-superuser
+ become_password: some-other-secret
+ ssh_key_data: "{{ ssh_key_data }}"
+ ssh_key_unlock: "another-pass-phrase"
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
- name: Create an invalid SSH credential (passphrase required)
credential:
name: SSH Credential
diff --git a/ansible_collections/awx/awx/tests/integration/targets/credential_input_source/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/credential_input_source/tasks/main.yml
index e9066a0f2..1d56d5e4b 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/credential_input_source/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/credential_input_source/tasks/main.yml
@@ -54,6 +54,51 @@
that:
- "result is changed"
+ - name: Add credential Input Source with exists
+ credential_input_source:
+ input_field_name: password
+ target_credential: "{{ target_cred_result.id }}"
+ source_credential: "{{ src_cred_result.id }}"
+ metadata:
+ object_query: "Safe=MY_SAFE;Object=AWX-user"
+ object_query_format: "Exact"
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is not changed"
+
+ - name: Delete credential Input Source
+ credential_input_source:
+ input_field_name: password
+ target_credential: "{{ target_cred_result.id }}"
+ source_credential: "{{ src_cred_result.id }}"
+ metadata:
+ object_query: "Safe=MY_SAFE;Object=AWX-user"
+ object_query_format: "Exact"
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
+ - name: Add credential Input Source with exists
+ credential_input_source:
+ input_field_name: password
+ target_credential: "{{ target_cred_result.id }}"
+ source_credential: "{{ src_cred_result.id }}"
+ metadata:
+ object_query: "Safe=MY_SAFE;Object=AWX-user"
+ object_query_format: "Exact"
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
- name: Add Second credential Lookup
credential:
description: Credential for Testing Source Change
diff --git a/ansible_collections/awx/awx/tests/integration/targets/credential_type/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/credential_type/tasks/main.yml
index e3b75f765..11bd6f904 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/credential_type/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/credential_type/tasks/main.yml
@@ -1,7 +1,12 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- cred_type_name: "AWX-Collection-tests-credential_type-cred-type-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ cred_type_name: "AWX-Collection-tests-credential_type-cred-type-{{ test_id }}"
- block:
- name: Add Tower credential type
@@ -17,6 +22,48 @@
that:
- "result is changed"
+ - name: Add Tower credential type with exists
+ credential_type:
+ description: Credential type for Test
+ name: "{{ cred_type_name }}"
+ kind: cloud
+ inputs: {"fields": [{"type": "string", "id": "username", "label": "Username"}, {"secret": true, "type": "string", "id": "password", "label": "Password"}], "required": ["username", "password"]}
+ injectors: {"extra_vars": {"test": "foo"}}
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is not changed"
+
+ - name: Delete the credential type
+ credential_type:
+ description: Credential type for Test
+ name: "{{ cred_type_name }}"
+ kind: cloud
+ inputs: {"fields": [{"type": "string", "id": "username", "label": "Username"}, {"secret": true, "type": "string", "id": "password", "label": "Password"}], "required": ["username", "password"]}
+ injectors: {"extra_vars": {"test": "foo"}}
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
+ - name: Add Tower credential type with exists
+ credential_type:
+ description: Credential type for Test
+ name: "{{ cred_type_name }}"
+ kind: cloud
+ inputs: {"fields": [{"type": "string", "id": "username", "label": "Username"}, {"secret": true, "type": "string", "id": "password", "label": "Password"}], "required": ["username", "password"]}
+ injectors: {"extra_vars": {"test": "foo"}}
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
- name: Rename Tower credential type
credential_type:
name: "{{ cred_type_name }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/demo_data/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/demo_data/tasks/main.yml
index db152671b..3052a0ebf 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/demo_data/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/demo_data/tasks/main.yml
@@ -33,6 +33,7 @@
name: "localhost"
inventory: "Demo Inventory"
state: present
+ enabled: true
variables:
ansible_connection: local
register: result
diff --git a/ansible_collections/awx/awx/tests/integration/targets/execution_environment/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/execution_environment/tasks/main.yml
index 0cb2fd90b..f1758bd1b 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/execution_environment/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/execution_environment/tasks/main.yml
@@ -22,6 +22,48 @@
that:
- "result is changed"
+ - name: Add an EE with exists
+ execution_environment:
+ name: "{{ ee_name }}"
+ description: "EE for Testing"
+ image: quay.io/ansible/awx-ee
+ pull: always
+ organization: Default
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is not changed"
+
+ - name: Delete an EE
+ execution_environment:
+ name: "{{ ee_name }}"
+ description: "EE for Testing"
+ image: quay.io/ansible/awx-ee
+ pull: always
+ organization: Default
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
+ - name: Add an EE with exists
+ execution_environment:
+ name: "{{ ee_name }}"
+ description: "EE for Testing"
+ image: quay.io/ansible/awx-ee
+ pull: always
+ organization: Default
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
- name: Associate the Test EE with Default Org (this should fail)
execution_environment:
name: "{{ ee_name }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/group/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/group/tasks/main.yml
index ac58826b1..c9a492911 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/group/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/group/tasks/main.yml
@@ -1,25 +1,54 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- group_name1: "AWX-Collection-tests-group-group-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- group_name2: "AWX-Collection-tests-group-group-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- group_name3: "AWX-Collection-tests-group-group-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- inv_name: "AWX-Collection-tests-group-inv-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- host_name1: "AWX-Collection-tests-group-host-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- host_name2: "AWX-Collection-tests-group-host-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- host_name3: "AWX-Collection-tests-group-host-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ group_name1: "AWX-Collection-tests-group-group1-{{ test_id }}"
+ group_name2: "AWX-Collection-tests-group-group2-{{ test_id }}"
+ group_name3: "AWX-Collection-tests-group-group3-{{ test_id }}"
+ group_name4: "AWX-Collection-tests-group-group4-{{ test_id }}"
+ inv_name: "AWX-Collection-tests-group-inv-{{ test_id }}"
+ host_name1: "AWX-Collection-tests-group-host1-{{ test_id }}"
+ host_name2: "AWX-Collection-tests-group-host2-{{ test_id }}"
+ host_name3: "AWX-Collection-tests-group-host3-{{ test_id }}"
+ host_name4: "AWX-Collection-tests-group-host4-{{ test_id }}"
- name: Create an Inventory
inventory:
name: "{{ inv_name }}"
organization: Default
state: present
- register: result
+ register: inv_result
+
+- name: Create a Host
+ host:
+ name: "{{ host_name4 }}"
+ inventory: "{{ inv_name }}"
+ state: present
+ register: host_result
+
+- name: Add Host to Group
+ group:
+ name: "{{ group_name1 }}"
+ inventory: "{{ inv_name }}"
+ hosts:
+ - "{{ host_name4 }}"
+ preserve_existing_hosts: true
+ register: group_result
+
+- assert:
+ that:
+ - inv_result is changed
+ - host_result is changed
+ - group_result is changed
-- name: Create a Group
+- name: Create Group 1
group:
name: "{{ group_name1 }}"
- inventory: "{{ result.id }}"
+ inventory: "{{ inv_result.id }}"
state: present
variables:
foo: bar
@@ -29,7 +58,46 @@
that:
- "result is changed"
-- name: Create a Group
+- name: Create Group 1 with exists
+ group:
+ name: "{{ group_name1 }}"
+ inventory: "{{ inv_name }}"
+ state: exists
+ variables:
+ foo: bar
+ register: result
+
+- assert:
+ that:
+ - "result is not changed"
+
+- name: Delete Group 1
+ group:
+ name: "{{ group_name1 }}"
+ inventory: "{{ inv_name }}"
+ state: absent
+ variables:
+ foo: bar
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Create Group 1 with exists
+ group:
+ name: "{{ group_name1 }}"
+ inventory: "{{ inv_name }}"
+ state: exists
+ variables:
+ foo: bar
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Create Group 2
group:
name: "{{ group_name2 }}"
inventory: "{{ inv_name }}"
@@ -42,7 +110,7 @@
that:
- "result is changed"
-- name: Create a Group
+- name: Create Group 3
group:
name: "{{ group_name3 }}"
inventory: "{{ inv_name }}"
@@ -64,7 +132,7 @@
- "{{ host_name2 }}"
- "{{ host_name3 }}"
-- name: Create a Group with hosts and sub group
+- name: Create Group 1 with hosts and sub group of Group 2
group:
name: "{{ group_name1 }}"
inventory: "{{ inv_name }}"
@@ -78,7 +146,7 @@
foo: bar
register: result
-- name: Create a Group with hosts and sub group
+- name: Create Group 1 with hosts and sub group
group:
name: "{{ group_name1 }}"
inventory: "{{ inv_name }}"
@@ -99,9 +167,9 @@
that:
- group1_host_count == "3"
-- name: Delete a Group
+- name: Delete Group 3
group:
- name: "{{ group_name1 }}"
+ name: "{{ group_name3 }}"
inventory: "{{ inv_name }}"
state: absent
register: result
@@ -110,9 +178,10 @@
that:
- "result is changed"
-- name: Delete a Group
+# If we delete group 1 first it will delete group 2 and 3
+- name: Delete Group 1
group:
- name: "{{ group_name2 }}"
+ name: "{{ group_name1 }}"
inventory: "{{ inv_name }}"
state: absent
register: result
@@ -121,13 +190,14 @@
that:
- "result is changed"
-- name: Delete a Group
+- name: Delete Group 2
group:
- name: "{{ group_name3 }}"
+ name: "{{ group_name2 }}"
inventory: "{{ inv_name }}"
state: absent
register: result
+# In this case, group 2 was last a child of group1 so deleting group1 deleted group2
- assert:
that:
- "result is not changed"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/host/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/host/tasks/main.yml
index a0321b09c..2b5d66ff8 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/host/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/host/tasks/main.yml
@@ -1,8 +1,13 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- host_name: "AWX-Collection-tests-host-host-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- inv_name: "AWX-Collection-tests-host-inv-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ host_name: "AWX-Collection-tests-host-host-{{ test_id }}"
+ inv_name: "AWX-Collection-tests-host-inv-{{ test_id }}"
- name: Create an Inventory
inventory:
@@ -24,6 +29,64 @@
that:
- "result is changed"
+- name: Create a Host with exists
+ host:
+ name: "{{ host_name }}"
+ inventory: "{{ inv_name }}"
+ state: exists
+ variables:
+ foo: bar
+ register: result
+
+- assert:
+ that:
+ - "result is not changed"
+
+- name: Modify the host as a no-op
+ host:
+ name: "{{ host_name }}"
+ inventory: "{{ inv_name }}"
+ register: result
+
+- assert:
+ that:
+ - "result is not changed"
+
+- name: Delete a Host
+ host:
+ name: "{{ host_name }}"
+ inventory: "{{ inv_name }}"
+ state: absent
+ variables:
+ foo: bar
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Create a Host with exists
+ host:
+ name: "{{ host_name }}"
+ inventory: "{{ inv_name }}"
+ state: exists
+ variables:
+ foo: bar
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Use lookup to check that host was enabled
+ ansible.builtin.set_fact:
+ host_enabled_test: "lookup('awx.awx.controller_api', 'hosts/{{result.id}}/').enabled"
+
+- name: Newly created host should have API default value for enabled
+ assert:
+ that:
+ - host_enabled_test
+
- name: Delete a Host
host:
name: "{{ result.id }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/instance/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/instance/tasks/main.yml
index e312c5fac..045567b50 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/instance/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/instance/tasks/main.yml
@@ -1,14 +1,25 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate hostnames
set_fact:
- hostname1: "AWX-Collection-tests-instance1.{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}.example.com"
- hostname2: "AWX-Collection-tests-instance2.{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}.example.com"
- hostname3: "AWX-Collection-tests-instance3.{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}.example.com"
+ hostname1: "AWX-Collection-tests-instance1.{{ test_id }}.example.com"
+ hostname2: "AWX-Collection-tests-instance2.{{ test_id }}.example.com"
+ hostname3: "AWX-Collection-tests-instance3.{{ test_id }}.example.com"
register: facts
-- name: Show hostnames
- debug:
- var: facts
+- name: Get the k8s setting
+ set_fact:
+ IS_K8S: "{{ controller_settings['IS_K8S'] | default(False) }}"
+ vars:
+ controller_settings: "{{ lookup('awx.awx.controller_api', 'settings/all') }}"
+
+- debug:
+ msg: "Skipping instance test since this is instance is not running on a K8s platform"
+ when: not IS_K8S
- block:
- name: Create an instance
@@ -31,7 +42,6 @@
node_type: execution
node_state: installed
capacity_adjustment: 0.4
- listener_port: 31337
register: result
- assert:
@@ -57,3 +67,67 @@
- "{{ hostname1 }}"
- "{{ hostname2 }}"
- "{{ hostname3 }}"
+
+ when: IS_K8S
+
+- block:
+ - name: Create hop node 1
+ awx.awx.instance:
+ hostname: "{{ hostname1 }}"
+ node_type: hop
+ node_state: installed
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Create hop node 2
+ awx.awx.instance:
+ hostname: "{{ hostname2 }}"
+ node_type: hop
+ node_state: installed
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Create execution node
+ awx.awx.instance:
+ hostname: "{{ hostname3 }}"
+ node_type: execution
+ node_state: installed
+ peers:
+ - "{{ hostname1 }}"
+ - "{{ hostname2 }}"
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Remove execution node peers
+ awx.awx.instance:
+ hostname: "{{ hostname3 }}"
+ node_type: execution
+ node_state: installed
+ peers: []
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ always:
+ - name: Deprovision the instances
+ awx.awx.instance:
+ hostname: "{{ item }}"
+ node_state: deprovisioning
+ with_items:
+ - "{{ hostname1 }}"
+ - "{{ hostname2 }}"
+ - "{{ hostname3 }}"
+
+
+ when: IS_K8S
diff --git a/ansible_collections/awx/awx/tests/integration/targets/instance_group/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/instance_group/tasks/main.yml
index 701137f28..1ef9d2da3 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/instance_group/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/instance_group/tasks/main.yml
@@ -2,6 +2,7 @@
- name: Generate test id
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
- name: Generate names
set_fact:
@@ -37,6 +38,42 @@
that:
- "result is changed"
+ - name: Create an Instance Group with exists
+ instance_group:
+ name: "{{ group_name1 }}"
+ policy_instance_percentage: 34
+ policy_instance_minimum: 12
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is not changed"
+
+ - name: Delete an Instance Group
+ instance_group:
+ name: "{{ group_name1 }}"
+ policy_instance_percentage: 34
+ policy_instance_minimum: 12
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
+ - name: Create an Instance Group with exists
+ instance_group:
+ name: "{{ group_name1 }}"
+ policy_instance_percentage: 34
+ policy_instance_minimum: 12
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
- name: Update an Instance Group
instance_group:
name: "{{ result.id }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/inventory/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/inventory/tasks/main.yml
index abbe4f659..dbaf7dbce 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/inventory/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/inventory/tasks/main.yml
@@ -2,6 +2,7 @@
- name: Generate a test ID
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
- name: Generate names
set_fact:
@@ -50,6 +51,45 @@
that:
- "result is changed"
+ - name: Create an Inventory with exists
+ inventory:
+ name: "{{ inv_name1 }}"
+ organization: Default
+ instance_groups:
+ - "{{ group_name1 }}"
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is not changed"
+
+ - name: Delete an Inventory
+ inventory:
+ name: "{{ inv_name1 }}"
+ organization: Default
+ instance_groups:
+ - "{{ group_name1 }}"
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
+ - name: Create an Inventory with exists
+ inventory:
+ name: "{{ inv_name1 }}"
+ organization: Default
+ instance_groups:
+ - "{{ group_name1 }}"
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
- name: Test Inventory module idempotency
inventory:
name: "{{ result.id }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/inventory_source/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/inventory_source/tasks/main.yml
index d905d03a9..a0442eeaf 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/inventory_source/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/inventory_source/tasks/main.yml
@@ -1,9 +1,14 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- openstack_cred: "AWX-Collection-tests-inventory_source-cred-openstack-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- openstack_inv: "AWX-Collection-tests-inventory_source-inv-openstack-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- openstack_inv_source: "AWX-Collection-tests-inventory_source-inv-source-openstack-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ openstack_cred: "AWX-Collection-tests-inventory_source-cred-openstack-{{ test_id }}"
+ openstack_inv: "AWX-Collection-tests-inventory_source-inv-openstack-{{ test_id }}"
+ openstack_inv_source: "AWX-Collection-tests-inventory_source-inv-source-openstack-{{ test_id }}"
- name: Add a credential
credential:
@@ -25,7 +30,60 @@
organization: Default
name: "{{ openstack_inv }}"
-- name: Create a source inventory
+- name: Create an source inventory
+ inventory_source:
+ name: "{{ openstack_inv_source }}"
+ description: Source for Test inventory
+ inventory: "{{ openstack_inv }}"
+ credential: "{{ credential_result.id }}"
+ overwrite: true
+ update_on_launch: true
+ source_vars:
+ private: false
+ source: openstack
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Create an source inventory with exists
+ inventory_source:
+ name: "{{ openstack_inv_source }}"
+ description: Source for Test inventory
+ inventory: "{{ openstack_inv }}"
+ credential: "{{ credential_result.id }}"
+ overwrite: true
+ update_on_launch: true
+ source_vars:
+ private: false
+ source: openstack
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - "result is not changed"
+
+- name: Delete an source inventory
+ inventory_source:
+ name: "{{ openstack_inv_source }}"
+ description: Source for Test inventory
+ inventory: "{{ openstack_inv }}"
+ credential: "{{ credential_result.id }}"
+ overwrite: true
+ update_on_launch: true
+ source_vars:
+ private: false
+ source: openstack
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Create an source inventory with exists
inventory_source:
name: "{{ openstack_inv_source }}"
description: Source for Test inventory
@@ -36,6 +94,7 @@
source_vars:
private: false
source: openstack
+ state: exists
register: result
- assert:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/inventory_source_update/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/inventory_source_update/tasks/main.yml
index bc9182bb6..43d57c906 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/inventory_source_update/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/inventory_source_update/tasks/main.yml
@@ -2,6 +2,7 @@
- name: Generate a test ID
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
- name: Generate names
set_fact:
@@ -25,7 +26,7 @@
name: "{{ project_name }}"
organization: "{{ org_name }}"
scm_type: git
- scm_url: https://github.com/ansible/test-playbooks
+ scm_url: https://github.com/ansible/ansible-tower-samples
wait: true
- name: Create a git project with same name, different org
diff --git a/ansible_collections/awx/awx/tests/integration/targets/job_cancel/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/job_cancel/tasks/main.yml
index 254ea89ce..deaab76f0 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/job_cancel/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/job_cancel/tasks/main.yml
@@ -11,6 +11,7 @@
- name: Cancel the job
job_cancel:
job_id: "{{ job.id }}"
+ request_timeout: 60
register: results
- assert:
@@ -23,10 +24,10 @@
fail_if_not_running: true
register: results
ignore_errors: true
-
-- assert:
- that:
- - results is failed
+ # This test can be flaky, so we retry it a few times
+ until: results is failed and results.msg == 'Job is not running'
+ retries: 6
+ delay: 5
- name: Check module fails with correct msg
job_cancel:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/job_launch/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/job_launch/tasks/main.yml
index 843c5c96f..23e43cf42 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/job_launch/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/job_launch/tasks/main.yml
@@ -1,9 +1,14 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- jt_name1: "AWX-Collection-tests-job_launch-jt1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- jt_name2: "AWX-Collection-tests-job_launch-jt2-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- proj_name: "AWX-Collection-tests-job_launch-project-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ jt_name1: "AWX-Collection-tests-job_launch-jt1-{{ test_id }}"
+ jt_name2: "AWX-Collection-tests-job_launch-jt2-{{ test_id }}"
+ proj_name: "AWX-Collection-tests-job_launch-project-{{ test_id }}"
- name: Launch a Job Template
job_launch:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/job_list/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/job_list/tasks/main.yml
index 04495bfcb..b9e602cb4 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/job_list/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/job_list/tasks/main.yml
@@ -16,7 +16,7 @@
- assert:
that:
- - "{{ matching_jobs.count }} == 1"
+ - matching_jobs.count == 1
- name: List failed jobs (which don't exist)
job_list:
@@ -26,7 +26,7 @@
- assert:
that:
- - "{{ successful_jobs.count }} == 0"
+ - successful_jobs.count == 0
- name: Get ALL result pages!
job_list:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/job_template/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/job_template/tasks/main.yml
index 951fe27f9..b8e513009 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/job_template/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/job_template/tasks/main.yml
@@ -2,6 +2,7 @@
- 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 random string for project
set_fact:
@@ -9,6 +10,7 @@
cred1: "AWX-Collection-tests-job_template-cred1-{{ test_id }}"
cred2: "AWX-Collection-tests-job_template-cred2-{{ test_id }}"
cred3: "AWX-Collection-tests-job_template-cred3-{{ test_id }}"
+ inv1: "AWX-Collection-tests-job_template-inv-{{ test_id }}"
proj1: "AWX-Collection-tests-job_template-proj-{{ test_id }}"
jt1: "AWX-Collection-tests-job_template-jt1-{{ test_id }}"
jt2: "AWX-Collection-tests-job_template-jt2-{{ test_id }}"
@@ -24,6 +26,11 @@
- Ansible Galaxy
register: result
+- name: Create an inventory
+ inventory:
+ name: "{{ inv1 }}"
+ organization: "{{ org_name }}"
+
- name: Create a Demo Project
project:
name: "{{ proj1 }}"
@@ -103,7 +110,7 @@
job_template:
name: "{{ jt1 }}"
project: "{{ proj1 }}"
- inventory: Demo Inventory
+ inventory: "{{ inv1 }}"
playbook: hello_world.yml
credentials:
- "{{ cred1 }}"
@@ -118,6 +125,63 @@
that:
- "jt1_result is changed"
+- name: Create Job Template 1 with exists
+ job_template:
+ name: "{{ jt1 }}"
+ project: "{{ proj1 }}"
+ inventory: "{{ inv1 }}"
+ playbook: hello_world.yml
+ credentials:
+ - "{{ cred1 }}"
+ - "{{ cred2 }}"
+ instance_groups:
+ - "{{ group_name1 }}"
+ job_type: run
+ state: exists
+ register: jt1_result
+
+- assert:
+ that:
+ - "jt1_result is not changed"
+
+- name: Delete Job Template 1
+ job_template:
+ name: "{{ jt1 }}"
+ project: "{{ proj1 }}"
+ inventory: "{{ inv1 }}"
+ playbook: hello_world.yml
+ credentials:
+ - "{{ cred1 }}"
+ - "{{ cred2 }}"
+ instance_groups:
+ - "{{ group_name1 }}"
+ job_type: run
+ state: absent
+ register: jt1_result
+
+- assert:
+ that:
+ - "jt1_result is changed"
+
+- name: Create Job Template 1 with exists
+ job_template:
+ name: "{{ jt1 }}"
+ project: "{{ proj1 }}"
+ inventory: "{{ inv1 }}"
+ playbook: hello_world.yml
+ credentials:
+ - "{{ cred1 }}"
+ - "{{ cred2 }}"
+ instance_groups:
+ - "{{ group_name1 }}"
+ job_type: run
+ state: exists
+ register: jt1_result
+
+- assert:
+ that:
+ - "jt1_result is changed"
+
- name: Add a credential to this JT
job_template:
name: "{{ jt1 }}"
@@ -217,7 +281,7 @@
name: "{{ jt2 }}"
organization: Default
project: "{{ proj1 }}"
- inventory: Demo Inventory
+ inventory: "{{ inv1 }}"
playbook: hello_world.yml
credential: "{{ cred3 }}"
job_type: run
@@ -235,7 +299,7 @@
name: "{{ jt2 }}"
organization: Default
project: "{{ proj1 }}"
- inventory: Demo Inventory
+ inventory: "{{ inv1 }}"
playbook: hello_world.yml
credential: "{{ cred3 }}"
job_type: run
@@ -383,7 +447,7 @@
job_template:
name: "{{ jt2 }}"
project: "{{ proj1 }}"
- inventory: Demo Inventory
+ inventory: "{{ inv1 }}"
playbook: hello_world.yml
credential: "{{ cred3 }}"
job_type: run
@@ -443,6 +507,12 @@
organization: Default
state: absent
+- name: Delete an inventory
+ inventory:
+ name: "{{ inv1 }}"
+ organization: "{{ org_name }}"
+ state: absent
+
- name: "Remove the organization"
organization:
name: "{{ org_name }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/job_wait/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/job_wait/tasks/main.yml
index 0aac7f314..cc38e171d 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/job_wait/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/job_wait/tasks/main.yml
@@ -1,171 +1,164 @@
---
-- name: Generate random string for template and project
- set_fact:
- jt_name: "AWX-Collection-tests-job_wait-long_running-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- proj_name: "AWX-Collection-tests-job_wait-long_running-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
-
-- name: Assure that the demo project exists
- project:
- name: "{{ proj_name }}"
- scm_type: 'git'
- scm_url: 'https://github.com/ansible/test-playbooks.git'
- scm_update_on_launch: true
- organization: Default
-
-- name: Create a job template
- job_template:
- name: "{{ jt_name }}"
- playbook: "sleep.yml"
- job_type: run
- project: "{{ proj_name }}"
- inventory: "Demo Inventory"
- extra_vars:
- sleep_interval: 300
-
-- name: Validate that interval superceeds min/max
- job_wait:
- min_interval: 10
- max_interval: 20
- interval: 12
- job_id: "99999999"
- register: result
- ignore_errors: true
-
-- assert:
- that:
- - "result.msg =='Unable to wait on job 99999999; that ID does not exist.' or
- 'min and max interval have been depricated, please use interval instead, interval will be set to 12'"
-
-- name: Check module fails with correct msg
- job_wait:
- job_id: "99999999"
- register: result
- ignore_errors: true
-
-- assert:
- that:
- - result is failed
- - "result.msg =='Unable to wait, no job_id 99999999 found: The requested object could not be found.' or
- 'Unable to wait on job 99999999; that ID does not exist.'"
-
-- name: Launch Demo Job Template (take happy path)
- job_launch:
- job_template: "Demo Job Template"
- register: job
-
-- assert:
- that:
- - job is changed
-
-- name: Wait for the Job to finish
- job_wait:
- job_id: "{{ job.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 job
- job_launch:
- job_template: "{{ jt_name }}"
- register: job
-
-- assert:
- that:
- - job is changed
-
-- name: Timeout waiting for the job to complete
- job_wait:
- job_id: "{{ job.id }}"
- timeout: 5
- ignore_errors: true
- register: wait_results
-
-# Make sure that we failed and that we have some data in our results
-- assert:
- that:
- - "wait_results.msg == 'Monitoring aborted due to timeout' or 'Timeout waiting for job to finish.'"
- - "'id' in wait_results"
-
-- name: Async cancel the long running job
- job_cancel:
- job_id: "{{ job.id }}"
- async: 3600
- poll: 0
-
-- name: Wait for the job to exit on cancel
- job_wait:
- job_id: "{{ job.id }}"
- register: wait_results
- ignore_errors: true
-
-- assert:
- that:
- - wait_results is failed
- - 'wait_results.status == "canceled"'
- - "wait_results.msg == 'Job with id {{ job.id }} failed' or 'Job with id={{ job.id }} failed, error: Job failed.'"
-
-- name: Delete the job template
- job_template:
- name: "{{ jt_name }}"
- playbook: "sleep.yml"
- job_type: run
- project: "{{ proj_name }}"
- inventory: "Demo Inventory"
- state: absent
-
-- name: Delete the project
- project:
- name: "{{ proj_name }}"
- organization: Default
- state: absent
-
-# workflow wait test
-- name: Generate a random string for test
+- name: Generate a test ID
set_fact:
- test_id1: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- when: test_id1 is not defined
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
-- name: Generate names
+- name: Generate random string for template and project
set_fact:
- wfjt_name2: "AWX-Collection-tests-workflow_launch--wfjt1-{{ test_id1 }}"
-
-- name: Create our workflow
- workflow_job_template:
- name: "{{ wfjt_name2 }}"
- state: present
-
-- name: Add a node
- workflow_job_template_node:
- workflow_job_template: "{{ wfjt_name2 }}"
- unified_job_template: "Demo Job Template"
- identifier: leaf
- register: new_node
-
-- name: Kick off a workflow
- workflow_launch:
- workflow_template: "{{ wfjt_name2 }}"
- ignore_errors: true
- register: workflow
-
-- name: Wait for the Workflow Job to finish
- job_wait:
- job_id: "{{ workflow.job_info.id }}"
- job_type: "workflow_jobs"
- register: wait_workflow_results
-
-# Make sure it worked and that we have some data in our results
-- assert:
- that:
- - wait_workflow_results is successful
- - "'elapsed' in wait_workflow_results"
- - "'id' in wait_workflow_results"
-
-- name: Clean up test workflow
- workflow_job_template:
- name: "{{ wfjt_name2 }}"
- state: absent
+ jt_name: "AWX-Collection-tests-job_wait-long_running-{{ test_id }}"
+ proj_name: "AWX-Collection-tests-job_wait-long_running-{{ test_id }}"
+
+- block:
+ - name: Create a project
+ project:
+ name: "{{ proj_name }}"
+ scm_type: 'git'
+ scm_url: 'https://github.com/ansible/test-playbooks.git'
+ scm_update_on_launch: true
+ organization: Default
+
+ - name: Create a job template
+ job_template:
+ name: "{{ jt_name }}"
+ playbook: "sleep.yml"
+ job_type: run
+ project: "{{ proj_name }}"
+ inventory: "Demo Inventory"
+ extra_vars:
+ sleep_interval: 600
+
+ - name: Check module fails with correct msg
+ job_wait:
+ job_id: "99999999"
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg =='Unable to wait, no job_id 99999999 found: The requested object could not be found.' or
+ 'Unable to wait on job 99999999; that ID does not exist.'"
+
+ - name: Launch Demo Job Template (take happy path)
+ job_launch:
+ job_template: "Demo Job Template"
+ register: job
+
+ - assert:
+ that:
+ - job is changed
+
+ - name: Wait for the Job to finish
+ job_wait:
+ job_id: "{{ job.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 job
+ job_launch:
+ job_template: "{{ jt_name }}"
+ register: job
+
+ - assert:
+ that:
+ - job is changed
+
+ - name: Timeout waiting for the job to complete
+ job_wait:
+ job_id: "{{ job.id }}"
+ timeout: 5
+ ignore_errors: true
+ register: wait_results
+
+ # Make sure that we failed and that we have some data in our results
+ - assert:
+ that:
+ - "wait_results.msg == 'Monitoring aborted due to timeout' or 'Timeout waiting for job to finish.'"
+ - "'id' in wait_results"
+
+ - name: Async cancel the long running job
+ job_cancel:
+ job_id: "{{ job.id }}"
+ async: 3600
+ poll: 0
+
+ - name: Wait for the job to exit on cancel
+ job_wait:
+ job_id: "{{ job.id }}"
+ register: wait_results
+ ignore_errors: true
+
+ - assert:
+ that:
+ - wait_results is failed
+ - 'wait_results.status == "canceled"'
+ - "'Job with id ~ job.id failed' or 'Job with id= ~ job.id failed, error: Job failed.' is in wait_results.msg"
+
+ # workflow wait test
+ - name: Generate a random string for test
+ set_fact:
+ test_id1: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id1 is not defined
+
+ - name: Generate names
+ set_fact:
+ wfjt_name2: "AWX-Collection-tests-workflow_launch--wfjt1-{{ test_id1 }}"
+
+ - name: Create our workflow
+ workflow_job_template:
+ name: "{{ wfjt_name2 }}"
+ state: present
+
+ - name: Add a node
+ workflow_job_template_node:
+ workflow_job_template: "{{ wfjt_name2 }}"
+ unified_job_template: "Demo Job Template"
+ identifier: leaf
+ register: new_node
+
+ - name: Kick off a workflow
+ workflow_launch:
+ workflow_template: "{{ wfjt_name2 }}"
+ ignore_errors: true
+ register: workflow
+
+ - name: Wait for the Workflow Job to finish
+ job_wait:
+ job_id: "{{ workflow.job_info.id }}"
+ job_type: "workflow_jobs"
+ register: wait_workflow_results
+
+ # Make sure it worked and that we have some data in our results
+ - assert:
+ that:
+ - wait_workflow_results is successful
+ - "'elapsed' in wait_workflow_results"
+ - "'id' in wait_workflow_results"
+
+ always:
+ - name: Clean up test workflow
+ workflow_job_template:
+ name: "{{ wfjt_name2 }}"
+ state: absent
+
+ - name: Delete the job template
+ job_template:
+ name: "{{ jt_name }}"
+ playbook: "sleep.yml"
+ job_type: run
+ project: "{{ proj_name }}"
+ inventory: "Demo Inventory"
+ state: absent
+
+ - name: Delete the project
+ project:
+ name: "{{ proj_name }}"
+ organization: Default
+ state: absent
diff --git a/ansible_collections/awx/awx/tests/integration/targets/label/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/label/tasks/main.yml
index 0ac077f8c..a98594d5c 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/label/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/label/tasks/main.yml
@@ -1,13 +1,34 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- label_name: "AWX-Collection-tests-label-label-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ label_name: "AWX-Collection-tests-label-label-{{ test_id }}"
- name: Create a Label
label:
name: "{{ label_name }}"
organization: Default
state: present
+ register: results
+
+- assert:
+ that:
+ - "results is changed"
+
+- name: Create a Label with exists
+ label:
+ name: "{{ label_name }}"
+ organization: Default
+ state: exists
+ register: results
+
+- assert:
+ that:
+ - "results is not changed"
- name: Check module fails with correct msg
label:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/lookup_api_plugin/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/lookup_api_plugin/tasks/main.yml
index 5abed9dcd..6fac5a0bc 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/lookup_api_plugin/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/lookup_api_plugin/tasks/main.yml
@@ -101,6 +101,9 @@
set_fact:
users: "{{ query(plugin_name, 'users', query_params={ 'username__endswith': test_id, 'page_size': 2 }, return_ids=True ) }}"
+ - debug:
+ msg: "{{ users }}"
+
- name: Assert that user list has 2 ids only and that they are strings, not ints
assert:
that:
@@ -129,7 +132,7 @@
- name: Get the ID of the first user created and verify that it is correct
assert:
- that: "{{ query(plugin_name, 'users', query_params={ 'username' : user_creation_results['results'][0]['item'] }, return_ids=True)[0] }} == {{ user_creation_results['results'][0]['id'] }}"
+ that: "query(plugin_name, 'users', query_params={ 'username' : user_creation_results['results'][0]['item'] }, return_ids=True)[0] == user_creation_results['results'][0]['id'] | string"
- name: Try to get an ID of someone who does not exist
set_fact:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/lookup_rruleset/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/lookup_rruleset/tasks/main.yml
index 7f9f0271b..28ad1da96 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/lookup_rruleset/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/lookup_rruleset/tasks/main.yml
@@ -11,7 +11,7 @@
- name: Call ruleset with no rules
set_fact:
- complex_rule: "{{ query(ruleset_plugin_name, '2022-04-30 10:30:45') }}"
+ complex_rule: "{{ query(ruleset_plugin_name | string, '2022-04-30 10:30:45') }}"
ignore_errors: True
register: results
@@ -356,3 +356,19 @@
that:
- results is success
- "'DTSTART;TZID=UTC:20220430T103045 RRULE:FREQ=MONTHLY;BYMONTHDAY=12,13,14,15,16,17,18;BYDAY=SA;INTERVAL=1' == complex_rule"
+
+- name: mondays, Tuesdays, and WEDNESDAY with case-insensitivity
+ set_fact:
+ complex_rule: "{{ query(ruleset_plugin_name, '2022-04-30 10:30:45', rules=rrules, timezone='UTC' ) }}"
+ ignore_errors: True
+ register: results
+ vars:
+ rrules:
+ - frequency: 'day'
+ interval: 1
+ byweekday: monday, Tuesday, WEDNESDAY
+
+- assert:
+ that:
+ - results is success
+ - "'DTSTART;TZID=UTC:20220430T103045 RRULE:FREQ=DAILY;BYDAY=MO,TU,WE;INTERVAL=1' == complex_rule"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/module_utils/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/module_utils/tasks/main.yml
new file mode 100644
index 000000000..2c0908b48
--- /dev/null
+++ b/ansible_collections/awx/awx/tests/integration/targets/module_utils/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- 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
+
+- include_tasks:
+ file: test_named_reference.yml
diff --git a/ansible_collections/awx/awx/tests/integration/targets/module_utils/tasks/test_named_reference.yml b/ansible_collections/awx/awx/tests/integration/targets/module_utils/tasks/test_named_reference.yml
new file mode 100644
index 000000000..3ae913a57
--- /dev/null
+++ b/ansible_collections/awx/awx/tests/integration/targets/module_utils/tasks/test_named_reference.yml
@@ -0,0 +1,73 @@
+---
+- block:
+ - name: generate random string for project
+ set_fact:
+ org_name: "AWX-Collection-tests-organization-org-{{ test_id }}"
+ cred: "AWX-Collection-tests-job_template-cred-{{ test_id }}"
+ inv: "AWX-Collection-tests-job_template-inv-{{ test_id }}"
+ proj: "AWX-Collection-tests-job_template-proj-{{ test_id }}"
+ jt: "AWX-Collection-tests-job_template-jt-{{ test_id }}"
+
+ - name: "Create a new organization"
+ organization:
+ name: "{{ org_name }}"
+ galaxy_credentials:
+ - Ansible Galaxy
+
+ - name: Create an inventory
+ inventory:
+ name: "{{ inv }}"
+ organization: "{{ org_name }}"
+
+ - name: Create a Demo Project
+ project:
+ name: "{{ proj }}"
+ organization: "{{ org_name }}"
+ state: present
+ scm_type: git
+ scm_url: https://github.com/ansible/ansible-tower-samples.git
+
+ - name: Create Credential
+ credential:
+ name: "{{ cred }}"
+ organization: "{{ org_name }}"
+ credential_type: Machine
+
+ - name: Create Job Template
+ job_template:
+ name: "{{ jt }}"
+ project: "{{ proj }}++{{ org_name }}"
+ inventory: "{{ inv }}++{{ org_name }}"
+ playbook: hello_world.yml
+ credentials:
+ - "{{ cred }}++Machine+ssh++"
+ job_type: run
+ state: present
+
+ always:
+ - name: Delete the Job Template
+ job_template:
+ name: "{{ jt }}"
+ state: absent
+
+ - name: Delete the Demo Project
+ project:
+ name: "{{ proj }}++{{ org_name }}"
+ state: absent
+
+ - name: Delete Credential
+ credential:
+ name: "{{ cred }}++Machine+ssh++{{ org_name }}"
+ credential_type: Machine
+ state: absent
+
+ - name: Delete the inventory
+ inventory:
+ name: "{{ inv }}++{{ org_name }}"
+ organization: "{{ org_name }}"
+ state: absent
+
+ - name: Remove the organization
+ organization:
+ name: "{{ org_name }}"
+ state: absent
diff --git a/ansible_collections/awx/awx/tests/integration/targets/notification_template/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/notification_template/tasks/main.yml
index 278845696..483024e2f 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/notification_template/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/notification_template/tasks/main.yml
@@ -1,12 +1,17 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- slack_not: "AWX-Collection-tests-notification_template-slack-not-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- webhook_not: "AWX-Collection-tests-notification_template-wehbook-not-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- email_not: "AWX-Collection-tests-notification_template-email-not-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- twillo_not: "AWX-Collection-tests-notification_template-twillo-not-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- pd_not: "AWX-Collection-tests-notification_template-pd-not-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- irc_not: "AWX-Collection-tests-notification_template-irc-not-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ slack_not: "AWX-Collection-tests-notification_template-slack-not-{{ test_id }}"
+ webhook_not: "AWX-Collection-tests-notification_template-wehbook-not-{{ test_id }}"
+ email_not: "AWX-Collection-tests-notification_template-email-not-{{ test_id }}"
+ twillo_not: "AWX-Collection-tests-notification_template-twillo-not-{{ test_id }}"
+ pd_not: "AWX-Collection-tests-notification_template-pd-not-{{ test_id }}"
+ irc_not: "AWX-Collection-tests-notification_template-irc-not-{{ test_id }}"
- name: Create Slack notification with custom messages
notification_template:
@@ -31,6 +36,75 @@
that:
- result is changed
+- name: Create Slack notification with custom messages with exists
+ notification_template:
+ name: "{{ slack_not }}"
+ organization: Default
+ notification_type: slack
+ notification_configuration:
+ token: a_token
+ channels:
+ - general
+ messages:
+ started:
+ message: "{{ '{{' }} job_friendly_name {{' }}' }} {{ '{{' }} job.id {{' }}' }} started"
+ success:
+ message: "{{ '{{' }} job_friendly_name {{ '}}' }} completed in {{ '{{' }} job.elapsed {{ '}}' }} seconds"
+ error:
+ message: "{{ '{{' }} job_friendly_name {{ '}}' }} FAILED! Please look at {{ '{{' }} job.url {{ '}}' }}"
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+
+- name: Delete Slack notification with custom messages
+ notification_template:
+ name: "{{ slack_not }}"
+ organization: Default
+ notification_type: slack
+ notification_configuration:
+ token: a_token
+ channels:
+ - general
+ messages:
+ started:
+ message: "{{ '{{' }} job_friendly_name {{' }}' }} {{ '{{' }} job.id {{' }}' }} started"
+ success:
+ message: "{{ '{{' }} job_friendly_name {{ '}}' }} completed in {{ '{{' }} job.elapsed {{ '}}' }} seconds"
+ error:
+ message: "{{ '{{' }} job_friendly_name {{ '}}' }} FAILED! Please look at {{ '{{' }} job.url {{ '}}' }}"
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: Create Slack notification with custom messages with exists
+ notification_template:
+ name: "{{ slack_not }}"
+ organization: Default
+ notification_type: slack
+ notification_configuration:
+ token: a_token
+ channels:
+ - general
+ messages:
+ started:
+ message: "{{ '{{' }} job_friendly_name {{' }}' }} {{ '{{' }} job.id {{' }}' }} started"
+ success:
+ message: "{{ '{{' }} job_friendly_name {{ '}}' }} completed in {{ '{{' }} job.elapsed {{ '}}' }} seconds"
+ error:
+ message: "{{ '{{' }} job_friendly_name {{ '}}' }} FAILED! Please look at {{ '{{' }} job.url {{ '}}' }}"
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
- name: Delete Slack notification
notification_template:
name: "{{ slack_not }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/organization/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/organization/tasks/main.yml
index fcf34e472..57af83c47 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/organization/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/organization/tasks/main.yml
@@ -2,6 +2,7 @@
- name: Generate a test ID
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
- name: Generate an org name
set_fact:
@@ -24,6 +25,39 @@
- assert:
that: "result is changed"
+- name: "Create a new organization with exists"
+ organization:
+ name: "{{ org_name }}"
+ galaxy_credentials:
+ - Ansible Galaxy
+ state: exists
+ register: result
+
+- assert:
+ that: "result is not changed"
+
+- name: "Delete a new organization"
+ organization:
+ name: "{{ org_name }}"
+ galaxy_credentials:
+ - Ansible Galaxy
+ state: absent
+ register: result
+
+- assert:
+ that: "result is changed"
+
+- name: "Create a new organization with exists"
+ organization:
+ name: "{{ org_name }}"
+ galaxy_credentials:
+ - Ansible Galaxy
+ state: exists
+ register: result
+
+- assert:
+ that: "result is changed"
+
- name: "Make sure making the same org is not a change"
organization:
name: "{{ org_name }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/project/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/project/tasks/main.yml
index 3a8889ee2..a5a059beb 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/project/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/project/tasks/main.yml
@@ -1,13 +1,18 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- project_name1: "AWX-Collection-tests-project-project1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- project_name2: "AWX-Collection-tests-project-project2-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- project_name3: "AWX-Collection-tests-project-project3-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- jt1: "AWX-Collection-tests-project-jt1-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- scm_cred_name: "AWX-Collection-tests-project-scm-cred-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- org_name: "AWX-Collection-tests-project-org-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- cred_name: "AWX-Collection-tests-project-cred-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ project_name1: "AWX-Collection-tests-project-project1-{{ test_id }}"
+ project_name2: "AWX-Collection-tests-project-project2-{{ test_id }}"
+ project_name3: "AWX-Collection-tests-project-project3-{{ test_id }}"
+ jt1: "AWX-Collection-tests-project-jt1-{{ test_id }}"
+ scm_cred_name: "AWX-Collection-tests-project-scm-cred-{{ test_id }}"
+ org_name: "AWX-Collection-tests-project-org-{{ test_id }}"
+ cred_name: "AWX-Collection-tests-project-cred-{{ test_id }}"
- block:
- name: Create an SCM Credential
@@ -26,8 +31,67 @@
name: "{{ project_name1 }}"
organization: Default
scm_type: git
- scm_url: https://github.com/ansible/test-playbooks
+ scm_url: https://github.com/ansible/ansible-tower-samples
+ wait: true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Create a git project without credentials and wait with exists
+ project:
+ name: "{{ project_name1 }}"
+ organization: Default
+ scm_type: git
+ scm_url: https://github.com/ansible/ansible-tower-samples
+ wait: true
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Create a git project and wait with short request timeout.
+ project:
+ name: "{{ project_name1 }}"
+ organization: Default
+ scm_type: git
+ scm_url: https://github.com/ansible/ansible-tower-samples
+ wait: true
+ state: exists
+ request_timeout: .001
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - "'timed out' in result.msg"
+
+ - name: Delete a git project without credentials and wait
+ project:
+ name: "{{ project_name1 }}"
+ organization: Default
+ scm_type: git
+ scm_url: https://github.com/ansible/ansible-tower-samples
+ wait: true
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Create a git project without credentials and wait with exists
+ project:
+ name: "{{ project_name1 }}"
+ organization: Default
+ scm_type: git
+ scm_url: https://github.com/ansible/ansible-tower-samples
wait: true
+ state: exists
register: result
- assert:
@@ -39,7 +103,7 @@
name: "{{ project_name1 }}"
organization: Default
scm_type: git
- scm_url: https://github.com/ansible/test-playbooks
+ scm_url: https://github.com/ansible/ansible-tower-samples
wait: false
register: result
ignore_errors: true
@@ -73,7 +137,7 @@
name: "{{ project_name2 }}"
organization: "{{ org_name }}"
scm_type: git
- scm_url: https://github.com/ansible/test-playbooks
+ scm_url: https://github.com/ansible/ansible-tower-samples
scm_credential: "{{ cred_name }}"
check_mode: true
@@ -98,7 +162,7 @@
name: "{{ project_name2 }}"
organization: Non_Existing_Org
scm_type: git
- scm_url: https://github.com/ansible/test-playbooks
+ scm_url: https://github.com/ansible/ansible-tower-samples
scm_credential: "{{ cred_name }}"
register: result
ignore_errors: true
@@ -115,7 +179,7 @@
name: "{{ project_name2 }}"
organization: "{{ org_name }}"
scm_type: git
- scm_url: https://github.com/ansible/test-playbooks
+ scm_url: https://github.com/ansible/ansible-tower-samples
scm_credential: Non_Existing_Credential
register: result
ignore_errors: true
@@ -127,7 +191,7 @@
- "'Non_Existing_Credential' in result.msg"
- "result.total_results == 0"
- - name: Create a git project without credentials without waiting
+ - name: Create a git project using a branch and allowing branch override
project:
name: "{{ project_name3 }}"
organization: Default
diff --git a/ansible_collections/awx/awx/tests/integration/targets/project_manual/tasks/create_project_dir.yml b/ansible_collections/awx/awx/tests/integration/targets/project_manual/tasks/create_project_dir.yml
deleted file mode 100644
index 9fb960725..000000000
--- a/ansible_collections/awx/awx/tests/integration/targets/project_manual/tasks/create_project_dir.yml
+++ /dev/null
@@ -1,56 +0,0 @@
----
-- name: Load the UI settings
- set_fact:
- project_base_dir: "{{ controller_settings.project_base_dir }}"
- vars:
- controller_settings: "{{ lookup('awx.awx.controller_api', 'config/') }}"
-
-- inventory:
- name: localhost
- organization: Default
-
-- host:
- name: localhost
- inventory: localhost
- variables:
- ansible_connection: local
-
-- name: Create an unused SSH / Machine credential
- credential:
- name: dummy
- credential_type: Machine
- inputs:
- ssh_key_data: |
- -----BEGIN EC PRIVATE KEY-----
- MHcCAQEEIIUl6R1xgzR6siIUArz4XBPtGZ09aetma2eWf1v3uYymoAoGCCqGSM49
- AwEHoUQDQgAENJNjgeZDAh/+BY860s0yqrLDprXJflY0GvHIr7lX3ieCtrzOMCVU
- QWzw35pc5tvuP34SSi0ZE1E+7cVMDDOF3w==
- -----END EC PRIVATE KEY-----
- organization: Default
-
-- block:
- - name: Add a path to a setting
- settings:
- name: AWX_ISOLATION_SHOW_PATHS
- value: "[{{ project_base_dir }}]"
-
- - name: Create a directory for manual project
- ad_hoc_command:
- credential: dummy
- inventory: localhost
- job_type: run
- module_args: "mkdir -p {{ project_base_dir }}/{{ project_dir_name }}"
- module_name: command
- wait: true
-
- always:
- - name: Delete path from setting
- settings:
- name: AWX_ISOLATION_SHOW_PATHS
- value: []
-
- - name: Delete dummy credential
- credential:
- name: dummy
- credential_type: Machine
- state: absent
diff --git a/ansible_collections/awx/awx/tests/integration/targets/project_manual/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/project_manual/tasks/main.yml
deleted file mode 100644
index 3cd328b4f..000000000
--- a/ansible_collections/awx/awx/tests/integration/targets/project_manual/tasks/main.yml
+++ /dev/null
@@ -1,38 +0,0 @@
----
-- name: Generate random string for project
- set_fact:
- rand_string: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
-
-- name: Generate manual project name
- set_fact:
- project_name: "Manual_Project_{{ rand_string }}"
-
-- name: Generate manual project dir name
- set_fact:
- project_dir_name: "proj_{{ rand_string }}"
-
-- name: Create a project directory for manual project
- import_tasks: create_project_dir.yml
-
-- name: Create a manual project
- project:
- name: "{{ project_name }}"
- organization: Default
- scm_type: manual
- local_path: "{{ project_dir_name }}"
- register: result
-
-- assert:
- that:
- - "result is changed"
-
-- name: Delete a manual project
- project:
- name: "{{ project_name }}"
- organization: Default
- state: absent
- register: result
-
-- assert:
- that:
- - "result is changed"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/project_update/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/project_update/tasks/main.yml
index 4b08e685f..451fe4ff6 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/project_update/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/project_update/tasks/main.yml
@@ -13,7 +13,7 @@
name: "{{ project_name1 }}"
organization: Default
scm_type: git
- scm_url: https://github.com/ansible/test-playbooks
+ scm_url: https://github.com/ansible/ansible-tower-samples
wait: false
register: project_create_result
@@ -61,6 +61,10 @@
organization: Default
state: absent
register: result
+ until: result is changed # wait for the project update to settle
+ retries: 6
+ delay: 5
+
- assert:
that:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/role/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/role/tasks/main.yml
index a94f53413..dd499b1d2 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/role/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/role/tasks/main.yml
@@ -2,6 +2,7 @@
- name: Generate a test id
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
- name: Generate names
set_fact:
@@ -10,6 +11,8 @@
jt1: "AWX-Collection-tests-role-jt1-{{ test_id }}"
jt2: "AWX-Collection-tests-role-jt2-{{ test_id }}"
wfjt_name: "AWX-Collection-tests-role-project-wfjt-{{ test_id }}"
+ team_name: "AWX-Collection-tests-team-team-{{ test_id }}"
+ team2_name: "AWX-Collection-tests-team-team-{{ test_id }}2"
- block:
- name: Create a User
@@ -26,6 +29,32 @@
that:
- "result is changed"
+ - name: Create a 2nd User
+ user:
+ first_name: Joe
+ last_name: User
+ username: "{{ username }}2"
+ password: "{{ 65535 | random | to_uuid }}"
+ email: joe@example.org
+ state: present
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
+ - name: Create teams
+ team:
+ name: "{{ item }}"
+ organization: Default
+ register: result
+ loop:
+ - "{{ team_name }}"
+ - "{{ team2_name }}"
+ - assert:
+ that:
+ - "result is changed"
+
- name: Create a project
project:
name: "{{ project_name }}"
@@ -54,9 +83,14 @@
that:
- "result is changed"
- - name: Add Joe to the update role of the default Project with lookup Organization
+ - name: Add Joe and teams to the update role of the default Project with lookup Organization
role:
user: "{{ username }}"
+ users:
+ - "{{ username }}2"
+ teams:
+ - "{{ team_name }}"
+ - "{{ team2_name }}"
role: update
lookup_organization: Default
project: "Demo Project"
@@ -73,6 +107,11 @@
- name: Add Joe to the new project by ID
role:
user: "{{ username }}"
+ users:
+ - "{{ username }}2"
+ teams:
+ - "{{ team_name }}"
+ - "{{ team2_name }}"
role: update
project: "{{ project_info['id'] }}"
state: "{{ item }}"
@@ -88,6 +127,8 @@
- name: Add Joe as execution admin to Default Org.
role:
user: "{{ username }}"
+ users:
+ - "{{ username }}2"
role: execution_environment_admin
organizations: Default
state: "{{ item }}"
@@ -109,6 +150,8 @@
- name: Add Joe to workflow execute role
role:
user: "{{ username }}"
+ users:
+ - "{{ username }}2"
role: execute
workflow: test-role-workflow
job_templates:
@@ -124,6 +167,8 @@
- name: Add Joe to nonexistant job template execute role
role:
user: "{{ username }}"
+ users:
+ - "{{ username }}2"
role: execute
workflow: test-role-workflow
job_templates:
@@ -140,6 +185,8 @@
- name: Add Joe to workflow execute role, no-op
role:
user: "{{ username }}"
+ users:
+ - "{{ username }}2"
role: execute
workflow: test-role-workflow
state: present
@@ -151,7 +198,8 @@
- name: Add Joe to workflow approve role
role:
- user: "{{ username }}"
+ users:
+ - "{{ username }}2"
role: approval
workflow: test-role-workflow
state: present
@@ -169,6 +217,23 @@
state: absent
register: result
+ - name: Delete a 2nd User
+ user:
+ username: "{{ username }}2"
+ email: joe@example.org
+ state: absent
+ register: result
+
+ - name: Delete teams
+ team:
+ name: "{{ item }}"
+ organization: Default
+ state: absent
+ register: result
+ loop:
+ - "{{ team_name }}"
+ - "{{ team2_name }}"
+
- name: Delete job templates
job_template:
name: "{{ item }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/schedule/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/schedule/tasks/main.yml
index 73343faf9..c61785d3b 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/schedule/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/schedule/tasks/main.yml
@@ -2,6 +2,7 @@
- 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 random string for schedule
set_fact:
@@ -35,7 +36,7 @@
- assert:
that:
- result is failed
- - "'Unable to create schedule {{ sched1 }}' in result.msg"
+ - "'Unable to create schedule '~ sched1 in result.msg"
- name: Create with options that the JT does not support
schedule:
@@ -61,7 +62,7 @@
- assert:
that:
- result is failed
- - "'Unable to create schedule {{ sched1 }}' in result.msg"
+ - "'Unable to create schedule '~ sched1 in result.msg"
- name: Build a real schedule
schedule:
@@ -75,6 +76,51 @@
that:
- result is changed
+ - name: Use lookup to check that schedules was enabled
+ ansible.builtin.set_fact:
+ schedules_enabled_test: "lookup('awx.awx.controller_api', 'schedules/{{result.id}}/').enabled"
+
+ - name: Newly created schedules should have API default value for enabled
+ assert:
+ that:
+ - schedules_enabled_test
+
+ - name: Build a real schedule with exists
+ schedule:
+ name: "{{ sched1 }}"
+ state: exists
+ unified_job_template: "Demo Job Template"
+ rrule: "DTSTART:20191219T130551Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1"
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Delete a real schedule
+ schedule:
+ name: "{{ sched1 }}"
+ state: absent
+ unified_job_template: "Demo Job Template"
+ rrule: "DTSTART:20191219T130551Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1"
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Build a real schedule with exists
+ schedule:
+ name: "{{ sched1 }}"
+ state: exists
+ unified_job_template: "Demo Job Template"
+ rrule: "DTSTART:20191219T130551Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1"
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
- name: Rebuild the same schedule
schedule:
name: "{{ sched1 }}"
@@ -188,6 +234,7 @@
schedule:
name: "{{ sched2 }}"
state: present
+ organization: Default
unified_job_template: "{{ jt1 }}"
rrule: "DTSTART:20191219T130551Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1"
description: "This hopefully will work"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/schedule_rrule/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/schedule_rrule/tasks/main.yml
index bf416b813..af2d95300 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/schedule_rrule/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/schedule_rrule/tasks/main.yml
@@ -9,7 +9,7 @@
- name: Test too many params (failure from validation of terms)
debug:
- msg: "{{ query(plugin_name, 'none', 'weekly', start_date='2020-4-16 03:45:07') }}"
+ msg: "{{ query(plugin_name | string, 'none', 'weekly', start_date='2020-4-16 03:45:07') }}"
ignore_errors: true
register: result
diff --git a/ansible_collections/awx/awx/tests/integration/targets/settings/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/settings/tasks/main.yml
index 65ae45c63..5feb9ee80 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/settings/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/settings/tasks/main.yml
@@ -1,4 +1,42 @@
---
+- name: Initialize starting project vvv setting to false
+ awx.awx.settings:
+ name: "PROJECT_UPDATE_VVV"
+ value: false
+
+- name: Change project vvv setting to true
+ awx.awx.settings:
+ name: "PROJECT_UPDATE_VVV"
+ value: true
+ register: result
+
+- name: Changing setting to true should have changed the value
+ assert:
+ that:
+ - "result is changed"
+
+- name: Change project vvv setting to true
+ awx.awx.settings:
+ name: "PROJECT_UPDATE_VVV"
+ value: true
+ register: result
+
+- name: Changing setting to true again should not change the value
+ assert:
+ that:
+ - "result is not changed"
+
+- name: Change project vvv setting back to false
+ awx.awx.settings:
+ name: "PROJECT_UPDATE_VVV"
+ value: false
+ register: result
+
+- name: Changing setting back to false should have changed the value
+ assert:
+ that:
+ - "result is changed"
+
- name: Set the value of AWX_ISOLATION_SHOW_PATHS to a baseline
settings:
name: AWX_ISOLATION_SHOW_PATHS
diff --git a/ansible_collections/awx/awx/tests/integration/targets/team/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/team/tasks/main.yml
index f220d9194..52f02b067 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/team/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/team/tasks/main.yml
@@ -1,7 +1,12 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- team_name: "AWX-Collection-tests-team-team-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ team_name: "AWX-Collection-tests-team-team-{{ test_id }}"
- name: Attempt to add a team to a non-existant Organization
team:
@@ -29,6 +34,39 @@
that:
- "result is changed"
+- name: Create a team with exists
+ team:
+ name: "{{ team_name }}"
+ organization: Default
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - "result is not changed"
+
+- name: Delete a team
+ team:
+ name: "{{ team_name }}"
+ organization: Default
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Create a team with exists
+ team:
+ name: "{{ team_name }}"
+ organization: Default
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
- name: Delete a team
team:
name: "{{ team_name }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/token/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/token/tasks/main.yml
index f13bc6bc6..9cd4972a9 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/token/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/token/tasks/main.yml
@@ -1,7 +1,12 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- token_description: "AWX-Collection-tests-token-description-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ token_description: "AWX-Collection-tests-token-description-{{ test_id }}"
- name: Try to use a token as a dict which is missing the token parameter
job_list:
diff --git a/ansible_collections/awx/awx/tests/integration/targets/user/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/user/tasks/main.yml
index 1d5cc5de5..a3fae666b 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/user/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/user/tasks/main.yml
@@ -1,7 +1,12 @@
---
+- name: Generate a test ID
+ set_fact:
+ test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
+
- name: Generate names
set_fact:
- username: "AWX-Collection-tests-user-user-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ username: "AWX-Collection-tests-user-user-{{ test_id }}"
- name: Create a User
user:
@@ -15,6 +20,42 @@
that:
- "result is changed"
+- name: Create a User with exists
+ user:
+ username: "{{ username }}"
+ first_name: Joe
+ password: "{{ 65535 | random | to_uuid }}"
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - "result is not changed"
+
+- name: Delete a User
+ user:
+ username: "{{ username }}"
+ first_name: Joe
+ password: "{{ 65535 | random | to_uuid }}"
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
+- name: Create a User with exists
+ user:
+ username: "{{ username }}"
+ first_name: Joe
+ password: "{{ 65535 | random | to_uuid }}"
+ state: exists
+ register: result
+
+- assert:
+ that:
+ - "result is changed"
+
- name: Change a User by ID
user:
username: "{{ result.id }}"
@@ -131,10 +172,6 @@
'Can not verify ssl with non-https protocol' in result.exception"
- block:
- - name: Generate a test ID
- set_fact:
- test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
-
- name: Generate an org name
set_fact:
org_name: "AWX-Collection-tests-organization-org-{{ test_id }}"
@@ -183,6 +220,7 @@
user:
controller_username: "{{ username }}-orgadmin"
controller_password: "{{ username }}-orgadmin"
+ controller_oauthtoken: false # Hack for CI where we use oauth in config file
username: "{{ username }}"
first_name: Joe
password: "{{ 65535 | random | to_uuid }}"
diff --git a/ansible_collections/awx/awx/tests/integration/targets/workflow_approval/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/workflow_approval/tasks/main.yml
index eaf1b3bf8..52fb7585c 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/workflow_approval/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/workflow_approval/tasks/main.yml
@@ -2,13 +2,15 @@
- name: Generate a random string for names
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- test_prefix: AWX-Collection-tests-workflow_approval
+ when: test_id is not defined
- name: Generate random names for test objects
set_fact:
org_name: "{{ test_prefix }}-org-{{ test_id }}"
approval_node_name: "{{ test_prefix }}-node-{{ test_id }}"
wfjt_name: "{{ test_prefix }}-wfjt-{{ test_id }}"
+ vars:
+ test_prefix: AWX-Collection-tests-workflow_approval
- block:
- name: Create a new organization for test isolation
diff --git a/ansible_collections/awx/awx/tests/integration/targets/workflow_job_template/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/workflow_job_template/tasks/main.yml
index e5f3366cd..f051e47ad 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/workflow_job_template/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/workflow_job_template/tasks/main.yml
@@ -2,6 +2,7 @@
- name: Generate a random string for names
set_fact:
test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ when: test_id is not defined
- name: Generate random names for test objects
set_fact:
@@ -17,8 +18,8 @@
webhook_wfjt_name: "AWX-Collection-tests-workflow_job_template-webhook-wfjt-{{ test_id }}"
email_not: "AWX-Collection-tests-job_template-email-not-{{ test_id }}"
webhook_notification: "AWX-Collection-tests-notification_template-wehbook-not-{{ test_id }}"
- project_inv: "AWX-Collection-tests-inventory_source-inv-project-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
- project_inv_source: "AWX-Collection-tests-inventory_source-inv-source-project-{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}"
+ project_inv: "AWX-Collection-tests-inventory_source-inv-project-{{ test_id }}"
+ project_inv_source: "AWX-Collection-tests-inventory_source-inv-source-project-{{ test_id }}"
github_webhook_credential_name: "AWX-Collection-tests-credential-webhook-{{ test_id }}_github"
ee1: "AWX-Collection-tests-workflow_job_template-ee1-{{ test_id }}"
label1: "AWX-Collection-tests-workflow_job_template-l1-{{ test_id }}"
@@ -168,6 +169,9 @@
name: "{{ jt1_name }}"
project: "{{ demo_project_name }}"
inventory: Demo Inventory
+ ask_inventory_on_launch: true
+ ask_credential_on_launch: true
+ ask_labels_on_launch: true
playbook: hello_world.yml
job_type: run
state: present
@@ -253,6 +257,65 @@
that:
- "result is changed"
+ - name: Create a workflow job template with exists
+ workflow_job_template:
+ name: "{{ wfjt_name }}"
+ organization: Default
+ inventory: Demo Inventory
+ extra_vars: {'foo': 'bar', 'another-foo': {'barz': 'bar2'}}
+ labels:
+ - "{{ lab1 }}"
+ ask_inventory_on_launch: true
+ ask_scm_branch_on_launch: true
+ ask_limit_on_launch: true
+ ask_tags_on_launch: true
+ ask_variables_on_launch: true
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is not changed"
+
+ - name: Delete a workflow job template
+ workflow_job_template:
+ name: "{{ wfjt_name }}"
+ organization: Default
+ inventory: Demo Inventory
+ extra_vars: {'foo': 'bar', 'another-foo': {'barz': 'bar2'}}
+ labels:
+ - "{{ lab1 }}"
+ ask_inventory_on_launch: true
+ ask_scm_branch_on_launch: true
+ ask_limit_on_launch: true
+ ask_tags_on_launch: true
+ ask_variables_on_launch: true
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
+ - name: Create a workflow job template with exists
+ workflow_job_template:
+ name: "{{ wfjt_name }}"
+ organization: Default
+ inventory: Demo Inventory
+ extra_vars: {'foo': 'bar', 'another-foo': {'barz': 'bar2'}}
+ # We don't try with the label here because after we delete the first WFJT the label is delete with it because it has no references
+ ask_inventory_on_launch: true
+ ask_scm_branch_on_launch: true
+ ask_limit_on_launch: true
+ ask_tags_on_launch: true
+ ask_variables_on_launch: true
+ state: exists
+ register: result
+
+ - assert:
+ that:
+ - "result is changed"
+
- name: Create a workflow job template with bad label
workflow_job_template:
name: "{{ wfjt_name }}"
@@ -650,7 +713,7 @@
name: "{{ wfjt_name }}"
inventory: Demo Inventory
extra_vars: {'foo': 'bar', 'another-foo': {'barz': 'bar2'}}
- schema:
+ workflow_nodes:
- identifier: node101
unified_job_template:
name: "{{ project_inv_source_result.id }}"
@@ -661,30 +724,52 @@
related:
failure_nodes:
- identifier: node201
+ - identifier: node102
+ unified_job_template:
+ organization:
+ name: "{{ org_name }}"
+ name: "{{ demo_project_name_2 }}"
+ type: project
+ related:
+ success_nodes:
+ - identifier: node201
- identifier: node201
unified_job_template:
organization:
name: Default
name: "{{ jt1_name }}"
type: job_template
- credentials: []
+ inventory:
+ name: Demo Inventory
+ organization:
+ name: Default
related:
success_nodes:
+ - identifier: node401
+ failure_nodes:
- identifier: node301
- - identifier: node202
- unified_job_template:
- organization:
- name: "{{ org_name }}"
- name: "{{ project_inv_source }}"
- type: project
+ always_nodes: []
+ credentials:
+ - name: "{{ scm_cred_name }}"
+ organization:
+ name: Default
+ instance_groups:
+ - name: "{{ ig1 }}"
+ labels:
+ - name: "{{ lab1 }}"
+ organization:
+ name: "{{ org_name }}"
- all_parents_must_converge: false
identifier: node301
unified_job_template:
- organization:
- name: Default
- name: "{{ jt2_name }}"
- type: job_template
- - identifier: Cleanup Job
+ description: Approval node for example
+ timeout: 900
+ type: workflow_approval
+ name: "{{ approval_node_name }}"
+ related:
+ success_nodes:
+ - identifier: node401
+ - identifier: node401
unified_job_template:
name: Cleanup Activity Stream
type: system_job_template
diff --git a/ansible_collections/awx/awx/tests/integration/targets/workflow_launch/tasks/main.yml b/ansible_collections/awx/awx/tests/integration/targets/workflow_launch/tasks/main.yml
index a328b8380..ce41c6bb8 100644
--- a/ansible_collections/awx/awx/tests/integration/targets/workflow_launch/tasks/main.yml
+++ b/ansible_collections/awx/awx/tests/integration/targets/workflow_launch/tasks/main.yml
@@ -57,7 +57,7 @@
- assert:
that:
- result is failed
- - "'Monitoring of Workflow Job - {{ wfjt_name1 }} aborted due to timeout' in result.msg"
+ - "'Monitoring of Workflow Job - '~ wfjt_name1 ~ ' aborted due to timeout' in result.msg"
- name: Kick off a workflow and wait for it
workflow_launch:
diff --git a/ansible_collections/awx/awx/tests/sanity/ignore-2.15.txt b/ansible_collections/awx/awx/tests/sanity/ignore-2.15.txt
index 19512ea0c..b502cada1 100644
--- a/ansible_collections/awx/awx/tests/sanity/ignore-2.15.txt
+++ b/ansible_collections/awx/awx/tests/sanity/ignore-2.15.txt
@@ -1 +1,3 @@
plugins/modules/export.py validate-modules:nonexistent-parameter-documented # needs awxkit to construct argspec
+plugins/modules/import.py pylint:unused-import # Simply used as a feature flag conditional
+test/awx/conftest.py pylint:unused-import # Used to make sure we are importing the right awxkit, see comment in conftest.py near imports
diff --git a/ansible_collections/awx/awx/tests/sanity/ignore-2.16.txt b/ansible_collections/awx/awx/tests/sanity/ignore-2.16.txt
new file mode 100644
index 000000000..b502cada1
--- /dev/null
+++ b/ansible_collections/awx/awx/tests/sanity/ignore-2.16.txt
@@ -0,0 +1,3 @@
+plugins/modules/export.py validate-modules:nonexistent-parameter-documented # needs awxkit to construct argspec
+plugins/modules/import.py pylint:unused-import # Simply used as a feature flag conditional
+test/awx/conftest.py pylint:unused-import # Used to make sure we are importing the right awxkit, see comment in conftest.py near imports