From b1b6af4ad58c56ca50cd38aff4410739bd8243b3 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 24 Apr 2020 15:31:20 -0400 Subject: [PATCH 01/12] make sure right endpoint is getting called for notification pagination --- .../lib/components/approvalsDrawer/approvalsDrawer.partial.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/lib/components/approvalsDrawer/approvalsDrawer.partial.html b/awx/ui/client/lib/components/approvalsDrawer/approvalsDrawer.partial.html index fa3f21844a..5c61d06673 100644 --- a/awx/ui/client/lib/components/approvalsDrawer/approvalsDrawer.partial.html +++ b/awx/ui/client/lib/components/approvalsDrawer/approvalsDrawer.partial.html @@ -80,7 +80,7 @@ collection="vm.approvals" dataset="vm.dataset" iterator="template" - base-path="unified_job_templates" + base-path="workflow_approvals" query-set="vm.queryset" hide-view-per-page="true"> From 3b84ff70024f0c15c90d123957791c8f434e9911 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 24 Apr 2020 15:11:33 -0400 Subject: [PATCH 02/12] remove unnecessary nesting translate directive from license partial --- awx/ui/client/src/license/license.partial.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui/client/src/license/license.partial.html b/awx/ui/client/src/license/license.partial.html index 098d1c2fbf..9a4a9a80f7 100644 --- a/awx/ui/client/src/license/license.partial.html +++ b/awx/ui/client/src/license/license.partial.html @@ -116,7 +116,7 @@ -
+
OR
From f6a2d41e4c180aaaadacfeba3e1f6ce26c92e6fe Mon Sep 17 00:00:00 2001 From: Christian Adams Date: Fri, 24 Apr 2020 15:06:32 -0400 Subject: [PATCH 03/12] Remove unnecessary or translation --- awx/ui/po/zh.po | 9 --------- 1 file changed, 9 deletions(-) diff --git a/awx/ui/po/zh.po b/awx/ui/po/zh.po index df59e68331..6e8268e4a6 100644 --- a/awx/ui/po/zh.po +++ b/awx/ui/po/zh.po @@ -60,15 +60,6 @@ msgstr "我同意最终用户许可证协议" msgid "User analytics: This data is used to enhance future releases of the Tower Software and help streamline customer experience and success." msgstr "用户分析:这些数据用于增强未来的 Tower 软件发行版本,并帮助简化客户体验和成功。" -#: client/src/license/license.partial.html:119 -msgid "" -"
\n" -"\t\t\t\t\t\t
OR
\n" -"\t\t\t\t\t\t
" -msgstr "
\n" -"\\t\\t\\t\\t\\t\\t
\n" -"\\t\\t\\t\\t\\t\\t
" - #: client/src/login/loginModal/loginModal.partial.html:26 msgid "" "\n" From ac46013fcc5520b628631797b9d5f4db50174220 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Fri, 24 Apr 2020 14:22:46 -0400 Subject: [PATCH 04/12] fix sliding window per/minute calc * Add tests to ensure correctness --- awx/main/analytics/broadcast_websocket.py | 11 ++- .../analytics/test_broadcast_websocket.py | 69 +++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 awx/main/tests/unit/analytics/test_broadcast_websocket.py diff --git a/awx/main/analytics/broadcast_websocket.py b/awx/main/analytics/broadcast_websocket.py index 449726f3fe..5cfda529eb 100644 --- a/awx/main/analytics/broadcast_websocket.py +++ b/awx/main/analytics/broadcast_websocket.py @@ -44,8 +44,8 @@ class FixedSlidingWindow(): def cleanup(self, now_bucket=None): now_bucket = now_bucket or now_seconds() - if self.start_time + 60 <= now_bucket: - self.start_time = now_bucket + 60 + 1 + if self.start_time + 60 < now_bucket: + self.start_time = now_bucket - 60 # Delete old entries for k in list(self.buckets.keys()): @@ -53,16 +53,15 @@ class FixedSlidingWindow(): del self.buckets[k] def record(self, ts=None): - ts = ts or datetime.datetime.now() - now_bucket = int((ts - datetime.datetime(1970,1,1)).total_seconds()) + now_bucket = ts or dt_to_seconds(datetime.datetime.now()) val = self.buckets.get(now_bucket, 0) self.buckets[now_bucket] = val + 1 self.cleanup(now_bucket) - def render(self): - self.cleanup() + def render(self, ts=None): + self.cleanup(now_bucket=ts) return sum(self.buckets.values()) or 0 diff --git a/awx/main/tests/unit/analytics/test_broadcast_websocket.py b/awx/main/tests/unit/analytics/test_broadcast_websocket.py new file mode 100644 index 0000000000..6edfe51b92 --- /dev/null +++ b/awx/main/tests/unit/analytics/test_broadcast_websocket.py @@ -0,0 +1,69 @@ +import datetime + +from awx.main.analytics.broadcast_websocket import FixedSlidingWindow +from awx.main.analytics.broadcast_websocket import dt_to_seconds + + +class TestFixedSlidingWindow(): + + def ts(self, **kwargs): + e = { + 'year': 1985, + 'month': 1, + 'day': 1, + 'hour': 1, + } + return dt_to_seconds(datetime.datetime(**kwargs, **e)) + + def test_record_same_minute(self): + """ + Legend: + - = record() + ^ = render() + |---| = 1 minute, 60 seconds + + .................... + |------------------------------------------------------------| + ^^^^^^^^^^^^^^^^^^^^ + """ + + fsw = FixedSlidingWindow(self.ts(minute=0, second=0, microsecond=0)) + for i in range(20): + fsw.record(self.ts(minute=0, second=i, microsecond=0)) + assert (i + 1) == fsw.render(self.ts(minute=0, second=i, microsecond=0)) + + + def test_record_same_minute_render_diff_minute(self): + """ + Legend: + - = record() + ^ = render() + |---| = 1 minute, 60 seconds + + .................... + |------------------------------------------------------------| + ^^ ^ + AB C + |------------------------------------------------------------| + ^^^^^^^^^^^^^^^^^^^^^ + DEEEEEEEEEEEEEEEEEEEF + """ + + fsw = FixedSlidingWindow(self.ts(minute=0, second=0, microsecond=0)) + for i in range(20): + fsw.record(self.ts(minute=0, second=i, microsecond=0)) + + assert 20 == fsw.render(self.ts(minute=0, second=19, microsecond=0)), \ + "A. The second of the last record() call" + assert 20 == fsw.render(self.ts(minute=0, second=20, microsecond=0)), \ + "B. The second after the last record() call" + assert 20 == fsw.render(self.ts(minute=0, second=59, microsecond=0)), \ + "C. Last second in the same minute that all record() called in" + assert 20 == fsw.render(self.ts(minute=1, second=0, microsecond=0)), \ + "D. First second of the minute following the minute that all record() calls in" + for i in range(20): + assert 20 - i == fsw.render(self.ts(minute=1, second=i, microsecond=0)), \ + "E. Sliding window where 1 record() should drop from the results each time" + + assert 0 == fsw.render(self.ts(minute=1, second=20, microsecond=0)), \ + "F. First second one minute after all record() calls" From 616e7082b33c267f4227ab965b0e5252b040ba1f Mon Sep 17 00:00:00 2001 From: Jim Ladd Date: Mon, 27 Apr 2020 00:17:46 -0700 Subject: [PATCH 05/12] Add support for satellite6_want_ansible_ssh_host * defaults to IPv6, falls back to IP --- awx/main/models/inventory.py | 9 +++++++++ .../data/inventory/plugins/satellite6/files/foreman.yml | 2 ++ 2 files changed, 11 insertions(+) diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 7c596547e7..c8a54354cf 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -2577,6 +2577,12 @@ class satellite6(PluginFileInjector): def inventory_as_dict(self, inventory_update, private_data_dir): ret = super(satellite6, self).inventory_as_dict(inventory_update, private_data_dir) + want_ansible_ssh_host = False + foreman_opts = inventory_update.source_vars_dict.copy() + for k, v in foreman_opts.items(): + if k == 'satellite6_want_ansible_ssh_host' and isinstance(v, bool): + want_ansible_ssh_host = v + # Compatibility content group_by_hostvar = { "environment": {"prefix": "foreman_environment_", @@ -2603,6 +2609,9 @@ class satellite6(PluginFileInjector): ret['want_facts'] = True ret['want_params'] = True + if want_ansible_ssh_host: + ret['compose'] = {'ansible_ssh_host': "foreman['ip6'] | default(foreman['ip'], true)"} + return ret diff --git a/awx/main/tests/data/inventory/plugins/satellite6/files/foreman.yml b/awx/main/tests/data/inventory/plugins/satellite6/files/foreman.yml index 20d868137a..6d4faee619 100644 --- a/awx/main/tests/data/inventory/plugins/satellite6/files/foreman.yml +++ b/awx/main/tests/data/inventory/plugins/satellite6/files/foreman.yml @@ -1,3 +1,5 @@ +compose: + ansible_ssh_host: foreman['ip6'] | default(foreman['ip'], true) keyed_groups: - key: foreman['environment_name'] | lower | regex_replace(' ', '') | regex_replace('[^A-Za-z0-9\_]', '_') | regex_replace('none', '') prefix: foreman_environment_ From 7f66f084f14617fd7837ae2ee09fadd198b424cf Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Mon, 27 Apr 2020 14:11:23 -0400 Subject: [PATCH 06/12] Restructuring modules so that lookup don't happen if deleting --- .../plugins/modules/tower_credential.py | 17 +++++----- .../plugins/modules/tower_credential_type.py | 6 ++-- awx_collection/plugins/modules/tower_group.py | 18 +++++----- awx_collection/plugins/modules/tower_host.py | 12 +++---- .../plugins/modules/tower_inventory.py | 18 +++++----- .../plugins/modules/tower_inventory_source.py | 12 +++---- .../plugins/modules/tower_job_template.py | 34 +++++++++---------- .../plugins/modules/tower_notification.py | 22 ++++++------ .../plugins/modules/tower_organization.py | 12 +++---- .../plugins/modules/tower_project.py | 14 ++++---- awx_collection/plugins/modules/tower_team.py | 12 +++---- awx_collection/plugins/modules/tower_user.py | 12 +++---- .../modules/tower_workflow_job_template.py | 26 +++++++------- .../tower_workflow_job_template_node.py | 26 +++++++------- .../tower_inventory_source/tasks/main.yml | 12 +++---- .../targets/tower_job_template/tasks/main.yml | 5 +-- .../tasks/main.yml | 4 ++- .../tools/templates/tower_module.j2 | 24 ++++++------- 18 files changed, 143 insertions(+), 143 deletions(-) diff --git a/awx_collection/plugins/modules/tower_credential.py b/awx_collection/plugins/modules/tower_credential.py index 5e0717526b..2fe3ca45c2 100644 --- a/awx_collection/plugins/modules/tower_credential.py +++ b/awx_collection/plugins/modules/tower_credential.py @@ -382,10 +382,15 @@ def main(): 'name': name, 'credential_type': cred_type_id, } + if organization: lookup_data['organization'] = org_id credential = module.get_one('credentials', **{'data': lookup_data}) + 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(credential) + # Create credential input from legacy inputs credential_inputs = {} for legacy_input in OLD_INPUT_NAMES: @@ -415,14 +420,10 @@ def main(): if team: credential_fields['team'] = team_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(credential) - elif state == 'present': - # If the state was present we can let the module build or update the existing group, this will return on its own - module.create_or_update_if_needed( - credential, credential_fields, endpoint='credentials', item_type='credential' - ) + # If the state was present we can let the module build or update the existing group, this will return on its own + module.create_or_update_if_needed( + credential, credential_fields, endpoint='credentials', item_type='credential' + ) if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_credential_type.py b/awx_collection/plugins/modules/tower_credential_type.py index 5c3186bca0..71ecc81526 100644 --- a/awx_collection/plugins/modules/tower_credential_type.py +++ b/awx_collection/plugins/modules/tower_credential_type.py @@ -144,9 +144,9 @@ def main(): 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(credential_type) - elif state == 'present': - # If the state was present and we can let the module build or update the existing credential type, this will return on its own - module.create_or_update_if_needed(credential_type, credential_type_params, endpoint='credential_types', item_type='credential type') + + # If the state was present and we can let the module build or update the existing credential type, this will return on its own + module.create_or_update_if_needed(credential_type, credential_type_params, endpoint='credential_types', item_type='credential type') if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_group.py b/awx_collection/plugins/modules/tower_group.py index 91504d4d9b..ec76ca40ad 100644 --- a/awx_collection/plugins/modules/tower_group.py +++ b/awx_collection/plugins/modules/tower_group.py @@ -123,6 +123,10 @@ def main(): } }) + 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(group) + # Create the data that gets sent for create and update group_fields = { 'name': new_name if new_name else name, @@ -149,15 +153,11 @@ def main(): if id_list: association_fields[relationship] = id_list - 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(group) - elif state == 'present': - # If the state was present we can let the module build or update the existing group, this will return on its own - module.create_or_update_if_needed( - group, group_fields, endpoint='groups', item_type='group', - associations=association_fields - ) + # If the state was present we can let the module build or update the existing group, this will return on its own + module.create_or_update_if_needed( + group, group_fields, endpoint='groups', item_type='group', + associations=association_fields + ) if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_host.py b/awx_collection/plugins/modules/tower_host.py index 9d2698e4e5..27e4f5a048 100644 --- a/awx_collection/plugins/modules/tower_host.py +++ b/awx_collection/plugins/modules/tower_host.py @@ -119,6 +119,10 @@ def main(): } }) + 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(host) + # Create the data that gets sent for create and update host_fields = { 'name': new_name if new_name else name, @@ -130,12 +134,8 @@ def main(): if variables is not None: host_fields['variables'] = json.dumps(variables) - 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(host) - elif state == 'present': - # If the state was present and we can let the module build or update the existing host, this will return on its own - module.create_or_update_if_needed(host, host_fields, endpoint='hosts', item_type='host') + # If the state was present and we can let the module build or update the existing host, this will return on its own + module.create_or_update_if_needed(host, host_fields, endpoint='hosts', item_type='host') if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_inventory.py b/awx_collection/plugins/modules/tower_inventory.py index d17d202c98..151fa07232 100644 --- a/awx_collection/plugins/modules/tower_inventory.py +++ b/awx_collection/plugins/modules/tower_inventory.py @@ -119,6 +119,10 @@ def main(): } }) + if state == 'absent': + # If the state was absent we can let the module delete it if needed, the module will handle exiting from this + module.delete_if_needed(inventory) + # Create the data that gets sent for create and update inventory_fields = { 'name': name, @@ -131,16 +135,12 @@ def main(): if variables is not None: inventory_fields['variables'] = json.dumps(variables) - if state == 'absent': - # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(inventory) - elif state == 'present': - # We need to perform a check to make sure you are not trying to convert a regular inventory into a smart one. - if inventory and inventory['kind'] == '' and inventory_fields['kind'] == 'smart': - module.fail_json(msg='You cannot turn a regular inventory into a "smart" inventory.') + # We need to perform a check to make sure you are not trying to convert a regular inventory into a smart one. + 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 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, inventory_fields, endpoint='inventories', item_type='inventory') + # 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, inventory_fields, endpoint='inventories', item_type='inventory') if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_inventory_source.py b/awx_collection/plugins/modules/tower_inventory_source.py index 0bd848bbec..f959580d01 100644 --- a/awx_collection/plugins/modules/tower_inventory_source.py +++ b/awx_collection/plugins/modules/tower_inventory_source.py @@ -198,6 +198,10 @@ def main(): } }) + if state == 'absent': + # If the state was absent we can let the module delete it if needed, the module will handle exiting from this + module.delete_if_needed(inventory_source) + # Create the data that gets sent for create and update inventory_source_fields = { 'name': new_name if new_name else name, @@ -234,12 +238,8 @@ def main(): if state == 'present' and not inventory_source and not inventory_source_fields['source']: module.fail_json(msg="If creating a new inventory source, the source param must be present") - if state == 'absent': - # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(inventory_source) - elif state == 'present': - # If the state was present we can let the module build or update the existing inventory_source, this will return on its own - module.create_or_update_if_needed(inventory_source, inventory_source_fields, endpoint='inventory_sources', item_type='inventory source') + # If the state was present we can let the module build or update the existing inventory_source, this will return on its own + module.create_or_update_if_needed(inventory_source, inventory_source_fields, endpoint='inventory_sources', item_type='inventory source') if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_job_template.py b/awx_collection/plugins/modules/tower_job_template.py index 2d54ad7ad4..7282c7ec43 100644 --- a/awx_collection/plugins/modules/tower_job_template.py +++ b/awx_collection/plugins/modules/tower_job_template.py @@ -414,6 +414,10 @@ def main(): } }) + 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) + # Create the data that gets sent for create and update new_fields = {} new_fields['name'] = new_name if new_name else name @@ -490,23 +494,19 @@ def main(): module._encrypted_changed_warning('survey_spec', existing_item, warning=True) on_change = update_survey - 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='job_templates', item_type='job_template', - associations={ - 'credentials': credentials_ids, - 'labels': labels_ids, - 'notification_templates_success': notification_success_ids, - 'notification_templates_started': notification_start_ids, - 'notification_templates_error': notification_error_ids - }, - on_create=on_change, on_update=on_change, - ) + # 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='job_templates', item_type='job_template', + associations={ + 'credentials': credentials_ids, + 'labels': labels_ids, + 'notification_templates_success': notification_success_ids, + 'notification_templates_started': notification_start_ids, + 'notification_templates_error': notification_error_ids + }, + on_create=on_change, on_update=on_change, + ) if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_notification.py b/awx_collection/plugins/modules/tower_notification.py index bae4302449..875a912f84 100644 --- a/awx_collection/plugins/modules/tower_notification.py +++ b/awx_collection/plugins/modules/tower_notification.py @@ -416,6 +416,10 @@ def main(): } }) + 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) + # Create notification_configuration from legacy inputs final_notification_configuration = {} for legacy_input in OLD_INPUT_NAMES: @@ -440,17 +444,13 @@ def main(): if messages is not None: new_fields['messages'] = messages - 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='notification_templates', item_type='notification_template', - associations={ - } - ) + # 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='notification_templates', item_type='notification_template', + associations={ + } + ) if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_organization.py b/awx_collection/plugins/modules/tower_organization.py index 2b1de03f73..44bb47c787 100644 --- a/awx_collection/plugins/modules/tower_organization.py +++ b/awx_collection/plugins/modules/tower_organization.py @@ -108,6 +108,10 @@ def main(): } }) + 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(organization) + # Create the data that gets sent for create and update org_fields = {'name': name} if description is not None: @@ -117,12 +121,8 @@ def main(): if max_hosts is not None: org_fields['max_hosts'] = max_hosts - 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(organization) - elif state == 'present': - # If the state was present and we can let the module build or update the existing organization, this will return on its own - module.create_or_update_if_needed(organization, org_fields, endpoint='organizations', item_type='organization') + # If the state was present and we can let the module build or update the existing organization, this will return on its own + module.create_or_update_if_needed(organization, org_fields, endpoint='organizations', item_type='organization') if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_project.py b/awx_collection/plugins/modules/tower_project.py index 2d84b82c0b..d390d86185 100644 --- a/awx_collection/plugins/modules/tower_project.py +++ b/awx_collection/plugins/modules/tower_project.py @@ -236,6 +236,10 @@ def main(): } }) + 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(project) + # Create the data that gets sent for create and update project_fields = { 'name': name, @@ -260,7 +264,7 @@ def main(): if scm_type == '': project_fields['local_path'] = local_path - if state != 'absent' and (scm_update_cache_timeout != 0 and scm_update_on_launch is not True): + if scm_update_cache_timeout != 0 and scm_update_on_launch is not True: module.warn('scm_update_cache_timeout will be ignored since scm_update_on_launch was not set to true') # If we are doing a not manual project, register our on_change method @@ -269,12 +273,8 @@ def main(): if wait and scm_type != '': on_change = wait_for_project_update - 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(project) - elif state == 'present': - # If the state was present and we can let the module build or update the existing project, this will return on its own - module.create_or_update_if_needed(project, project_fields, endpoint='projects', item_type='project', on_create=on_change, on_update=on_change) + # If the state was present and we can let the module build or update the existing project, this will return on its own + module.create_or_update_if_needed(project, project_fields, endpoint='projects', item_type='project', on_create=on_change, on_update=on_change) if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_team.py b/awx_collection/plugins/modules/tower_team.py index c581bebf4d..d7af5a616d 100644 --- a/awx_collection/plugins/modules/tower_team.py +++ b/awx_collection/plugins/modules/tower_team.py @@ -102,6 +102,10 @@ def main(): } }) + 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(team) + # Create the data that gets sent for create and update team_fields = { 'name': new_name if new_name else name, @@ -110,12 +114,8 @@ def main(): if description is not None: team_fields['description'] = description - 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(team) - elif state == 'present': - # If the state was present and we can let the module build or update the existing team, this will return on its own - module.create_or_update_if_needed(team, team_fields, endpoint='teams', item_type='team') + # If the state was present and we can let the module build or update the existing team, this will return on its own + module.create_or_update_if_needed(team, team_fields, endpoint='teams', item_type='team') if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_user.py b/awx_collection/plugins/modules/tower_user.py index eac8d70bed..f2f6b1fc31 100644 --- a/awx_collection/plugins/modules/tower_user.py +++ b/awx_collection/plugins/modules/tower_user.py @@ -147,6 +147,10 @@ def main(): } }) + 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) + # Create the data that gets sent for create and update new_fields = {} if username: @@ -164,12 +168,8 @@ def main(): if password: new_fields['password'] = password - 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='users', item_type='user') + # 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='users', item_type='user') if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_workflow_job_template.py b/awx_collection/plugins/modules/tower_workflow_job_template.py index 198aa208fc..a58b6d40bc 100644 --- a/awx_collection/plugins/modules/tower_workflow_job_template.py +++ b/awx_collection/plugins/modules/tower_workflow_job_template.py @@ -175,6 +175,13 @@ def main(): organization_id = module.resolve_name_to_id('organizations', organization) 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', **{'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 + module.delete_if_needed(existing_item) + inventory = module.params.get('inventory') if inventory: new_fields['inventory'] = module.resolve_name_to_id('inventories', inventory) @@ -183,9 +190,6 @@ def main(): if webhook_credential: new_fields['webhook_credential'] = module.resolve_name_to_id('webhook_credential', webhook_credential) - # Attempt to look up an existing item based on the provided data - existing_item = module.get_one('workflow_job_templates', **{'data': search_fields}) - # Create the data that gets sent for create and update new_fields['name'] = new_name if new_name else name for field_name in ( @@ -213,16 +217,12 @@ def main(): module._encrypted_changed_warning('survey_spec', existing_item, warning=True) on_change = update_survey - 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='workflow_job_templates', item_type='workflow_job_template', - on_create=on_change, on_update=on_change - ) + # 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='workflow_job_templates', item_type='workflow_job_template', + on_create=on_change, on_update=on_change + ) if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/tower_workflow_job_template_node.py b/awx_collection/plugins/modules/tower_workflow_job_template_node.py index 8e271014d1..2e8b74e843 100644 --- a/awx_collection/plugins/modules/tower_workflow_job_template_node.py +++ b/awx_collection/plugins/modules/tower_workflow_job_template_node.py @@ -218,6 +218,13 @@ def main(): workflow_job_template_id = wfjt_data['id'] 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}) + + 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) + unified_job_template = module.params.get('unified_job_template') if unified_job_template: new_fields['unified_job_template'] = module.resolve_name_to_id('unified_job_templates', unified_job_template) @@ -226,9 +233,6 @@ def main(): if inventory: new_fields['inventory'] = module.resolve_name_to_id('inventory', inventory) - # Attempt to look up an existing item based on the provided data - existing_item = module.get_one('workflow_job_template_nodes', **{'data': search_fields}) - # Create the data that gets sent for create and update for field_name in ( 'identifier', 'extra_data', 'scm_branch', 'job_type', 'job_tags', 'skip_tags', @@ -262,16 +266,12 @@ def main(): # In the case of a new object, the utils need to know it is a node new_fields['type'] = 'workflow_job_template_node' - 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='workflow_job_template_nodes', item_type='workflow_job_template_node', - 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='workflow_job_template_nodes', item_type='workflow_job_template_node', + associations=association_fields + ) if __name__ == '__main__': diff --git a/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml b/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml index 9cb82a231b..fb5f33c3ca 100644 --- a/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_inventory_source/tasks/main.yml @@ -40,17 +40,13 @@ that: - "result is changed" -- name: Delete the source inventory +- name: Delete the inventory source with an invalid cred, source_project, sourece_script specified tower_inventory_source: name: "{{ openstack_inv_source }}" - description: Source for Test inventory inventory: "{{ openstack_inv }}" - credential: "{{ openstack_cred }}" - overwrite: true - update_on_launch: true - source_vars: - private: false - source: openstack + credential: "Does Not Exit" + source_project: "Does Not Exist" + source_script: "Does Not Exist" state: absent - assert: diff --git a/awx_collection/tests/integration/targets/tower_job_template/tasks/main.yml b/awx_collection/tests/integration/targets/tower_job_template/tasks/main.yml index bc43166ac3..af70d1afd6 100644 --- a/awx_collection/tests/integration/targets/tower_job_template/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_job_template/tasks/main.yml @@ -147,10 +147,11 @@ - name: Delete Job Template 1 tower_job_template: name: "{{ jt1 }}" - project: "{{ proj1 }}" - inventory: Demo Inventory playbook: hello_world.yml job_type: run + project: "Does Not Exist" + inventory: "Does Not Exist" + webhook_credential: "Does Not Exist" state: absent register: result diff --git a/awx_collection/tests/integration/targets/tower_workflow_job_template/tasks/main.yml b/awx_collection/tests/integration/targets/tower_workflow_job_template/tasks/main.yml index 8a7a180ff9..6840dd515a 100644 --- a/awx_collection/tests/integration/targets/tower_workflow_job_template/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_workflow_job_template/tasks/main.yml @@ -104,9 +104,11 @@ unified_job_template: "{{ jt1_name }}" workflow: "{{ wfjt_name }}" -- name: Delete a workflow job template +- name: Delete a workflow job template with an invalid inventory and webook_credential tower_workflow_job_template: name: "{{ wfjt_name }}" + inventory: "Does Not Exist" + webhook_credential: "Does Not Exist" state: absent register: result diff --git a/awx_collection/tools/templates/tower_module.j2 b/awx_collection/tools/templates/tower_module.j2 index 25d9e0a08a..5d687bfc24 100644 --- a/awx_collection/tools/templates/tower_module.j2 +++ b/awx_collection/tools/templates/tower_module.j2 @@ -188,6 +188,10 @@ def main(): } }) + if state is '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) + # Create the data that gets sent for create and update new_fields = {} {% for option in item['json']['actions']['POST'] %} @@ -203,20 +207,16 @@ def main(): {% endif %} {% endfor %} - if state is '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 is '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='{{ item_type }}', item_type='{{ singular_item_type }}', - associations={ + # 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='{{ item_type }}', item_type='{{ singular_item_type }}', + associations={ {% for association in associations[item_type] | default([]) %} - '{{ association['endpoint'] }}': {{ association['related_item'] }}_ids, + '{{ association['endpoint'] }}': {{ association['related_item'] }}_ids, {% endfor %} - } - ) + } + ) if __name__ == '__main__': From f17ced8f9cd2c8c2a4c1c0a97ac10b2b27f9ccd3 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Mon, 27 Apr 2020 14:25:23 -0400 Subject: [PATCH 07/12] Fixing integration tests for deprecation warnings --- .../plugins/modules/tower_credential.py | 23 +++++++++++-------- .../plugins/modules/tower_notification.py | 6 ++++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/awx_collection/plugins/modules/tower_credential.py b/awx_collection/plugins/modules/tower_credential.py index 2fe3ca45c2..5a260f0ef3 100644 --- a/awx_collection/plugins/modules/tower_credential.py +++ b/awx_collection/plugins/modules/tower_credential.py @@ -364,38 +364,41 @@ def main(): # End backwards compatability state = module.params.get('state') - # Attempt to look up the related items the user specified (these will fail the module if not found) - if organization: - org_id = module.resolve_name_to_id('organizations', organization) - if user: - user_id = module.resolve_name_to_id('users', user) - if team: - team_id = module.resolve_name_to_id('teams', team) - + # Deprication warnings + for legacy_input in OLD_INPUT_NAMES: + if module.params.get(legacy_input) is not None: + module.deprecate(msg='{0} parameter has been deprecated, please use inputs instead'.format(legacy_input), version="3.6") if kind: module.deprecate(msg='The kind parameter has been deprecated, please use credential_type instead', version="3.6") cred_type_id = module.resolve_name_to_id('credential_types', credential_type if credential_type else KIND_CHOICES[kind]) + if organization: + org_id = module.resolve_name_to_id('organizations', organization) # Attempt to look up the object based on the provided name, credential type and optional organization lookup_data = { 'name': name, 'credential_type': cred_type_id, } - if organization: lookup_data['organization'] = org_id + credential = module.get_one('credentials', **{'data': lookup_data}) 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(credential) + # Attempt to look up the related items the user specified (these will fail the module if not found) + if user: + user_id = module.resolve_name_to_id('users', user) + if team: + team_id = module.resolve_name_to_id('teams', team) + # Create credential input from legacy inputs credential_inputs = {} for legacy_input in OLD_INPUT_NAMES: if module.params.get(legacy_input) is not None: - module.deprecate(msg='{0} parameter has been deprecated, please use inputs instead'.format(legacy_input), version="3.6") credential_inputs[legacy_input] = module.params.get(legacy_input) if inputs: credential_inputs.update(inputs) diff --git a/awx_collection/plugins/modules/tower_notification.py b/awx_collection/plugins/modules/tower_notification.py index 875a912f84..52d9ccaa5f 100644 --- a/awx_collection/plugins/modules/tower_notification.py +++ b/awx_collection/plugins/modules/tower_notification.py @@ -403,6 +403,11 @@ def main(): messages = module.params.get('messages') state = module.params.get('state') + # Deprecation warnings + for legacy_input in OLD_INPUT_NAMES: + if module.params.get(legacy_input) is not None: + module.deprecate(msg='{0} parameter has been deprecated, please use notification_configuration instead.'.format(legacy_input), version="3.6") + # Attempt to look up the related items the user specified (these will fail the module if not found) organization_id = None if organization: @@ -424,7 +429,6 @@ def main(): final_notification_configuration = {} for legacy_input in OLD_INPUT_NAMES: if module.params.get(legacy_input) is not None: - module.deprecate(msg='{0} parameter has been deprecated, please use notification_configuration instead.'.format(legacy_input), version="3.6") final_notification_configuration[legacy_input] = module.params.get(legacy_input) # Give anything in notification_configuration prescedence over the individual inputs if notification_configuration is not None: From c07b6285daa5ed1bc77991bb65abcd3f664bac4e Mon Sep 17 00:00:00 2001 From: Christian Adams Date: Mon, 27 Apr 2020 14:33:46 -0400 Subject: [PATCH 08/12] Increase stopwait time for rsyslogd service --- installer/roles/image_build/files/supervisor.conf | 2 +- installer/roles/kubernetes/templates/supervisor.yml.j2 | 2 +- tools/docker-compose/supervisor.conf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/installer/roles/image_build/files/supervisor.conf b/installer/roles/image_build/files/supervisor.conf index 641ebf5f64..73dc7d14c2 100644 --- a/installer/roles/image_build/files/supervisor.conf +++ b/installer/roles/image_build/files/supervisor.conf @@ -50,7 +50,7 @@ stderr_logfile_maxbytes=0 command = rsyslogd -n -i /var/run/awx-rsyslog/rsyslog.pid -f /var/lib/awx/rsyslog/rsyslog.conf autostart = true autorestart = true -stopwaitsecs = 1 +stopwaitsecs = 5 stopsignal=KILL stopasgroup=true killasgroup=true diff --git a/installer/roles/kubernetes/templates/supervisor.yml.j2 b/installer/roles/kubernetes/templates/supervisor.yml.j2 index 8aad78f0fa..99f42d30c1 100644 --- a/installer/roles/kubernetes/templates/supervisor.yml.j2 +++ b/installer/roles/kubernetes/templates/supervisor.yml.j2 @@ -57,7 +57,7 @@ data: command = rsyslogd -n -i /var/run/awx-rsyslog/rsyslog.pid -f /var/lib/awx/rsyslog/rsyslog.conf autostart = true autorestart = true - stopwaitsecs = 1 + stopwaitsecs = 5 stopsignal=KILL stopasgroup=true killasgroup=true diff --git a/tools/docker-compose/supervisor.conf b/tools/docker-compose/supervisor.conf index 09f74cd6e8..b70fed413d 100644 --- a/tools/docker-compose/supervisor.conf +++ b/tools/docker-compose/supervisor.conf @@ -75,7 +75,7 @@ stdout_logfile_maxbytes=0 command = rsyslogd -n -i /var/run/awx-rsyslog/rsyslog.pid -f /var/lib/awx/rsyslog/rsyslog.conf autostart = true autorestart = true -stopwaitsecs = 1 +stopwaitsecs = 5 stopsignal=KILL stopasgroup=true killasgroup=true From 41014e62b7d6c3d59f692a3ff18bf7b2a7e83ff3 Mon Sep 17 00:00:00 2001 From: Alan Rominger Date: Mon, 27 Apr 2020 16:15:11 -0400 Subject: [PATCH 09/12] Avoid applying galaxy settings if no user settings exist (#4262) --- awx/main/tasks.py | 25 +++++++++++++++---------- awx/playbooks/project_update.yml | 8 ++++---- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index dd27a7d849..e8a5bf5a57 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -2074,29 +2074,34 @@ class RunProjectUpdate(BaseTask): if settings.GALAXY_IGNORE_CERTS: env['ANSIBLE_GALAXY_IGNORE'] = True # Set up the public Galaxy server, if enabled + galaxy_configured = False if settings.PUBLIC_GALAXY_ENABLED: - galaxy_servers = [settings.PUBLIC_GALAXY_SERVER] + galaxy_servers = [settings.PUBLIC_GALAXY_SERVER] # static setting else: + galaxy_configured = True galaxy_servers = [] # Set up fallback Galaxy servers, if configured if settings.FALLBACK_GALAXY_SERVERS: + galaxy_configured = True galaxy_servers = settings.FALLBACK_GALAXY_SERVERS + galaxy_servers # Set up the primary Galaxy server, if configured if settings.PRIMARY_GALAXY_URL: + galaxy_configured = True galaxy_servers = [{'id': 'primary_galaxy'}] + galaxy_servers for key in GALAXY_SERVER_FIELDS: value = getattr(settings, 'PRIMARY_GALAXY_{}'.format(key.upper())) if value: galaxy_servers[0][key] = value - for server in galaxy_servers: - for key in GALAXY_SERVER_FIELDS: - if not server.get(key): - continue - env_key = ('ANSIBLE_GALAXY_SERVER_{}_{}'.format(server.get('id', 'unnamed'), key)).upper() - env[env_key] = server[key] - if galaxy_servers: - # now set the precedence of galaxy servers - env['ANSIBLE_GALAXY_SERVER_LIST'] = ','.join([server.get('id', 'unnamed') for server in galaxy_servers]) + if galaxy_configured: + for server in galaxy_servers: + for key in GALAXY_SERVER_FIELDS: + if not server.get(key): + continue + env_key = ('ANSIBLE_GALAXY_SERVER_{}_{}'.format(server.get('id', 'unnamed'), key)).upper() + env[env_key] = server[key] + if galaxy_servers: + # now set the precedence of galaxy servers + env['ANSIBLE_GALAXY_SERVER_LIST'] = ','.join([server.get('id', 'unnamed') for server in galaxy_servers]) return env def _build_scm_url_extra_vars(self, project_update): diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index 9c61c81093..fc791069a9 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -136,9 +136,9 @@ register: doesRequirementsExist - name: fetch galaxy roles from requirements.yml - command: ansible-galaxy install -r requirements.yml -p {{roles_destination|quote}}{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} + command: ansible-galaxy install -r roles/requirements.yml -p {{roles_destination|quote}}{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} args: - chdir: "{{project_path|quote}}/roles" + chdir: "{{project_path|quote}}" register: galaxy_result when: doesRequirementsExist.stat.exists changed_when: "'was installed successfully' in galaxy_result.stdout" @@ -157,9 +157,9 @@ register: doesCollectionRequirementsExist - name: fetch galaxy collections from collections/requirements.yml - command: ansible-galaxy collection install -r requirements.yml -p {{collections_destination|quote}}{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} + command: ansible-galaxy collection install -r collections/requirements.yml -p {{collections_destination|quote}}{{ ' -' + 'v' * ansible_verbosity if ansible_verbosity else '' }} args: - chdir: "{{project_path|quote}}/collections" + chdir: "{{project_path|quote}}" register: galaxy_collection_result when: doesCollectionRequirementsExist.stat.exists changed_when: "'Installing ' in galaxy_collection_result.stdout" From 960aa9df16dda04de64ac461ae8a09fcf42d2eb4 Mon Sep 17 00:00:00 2001 From: chris meyers Date: Tue, 28 Apr 2020 09:12:42 -0400 Subject: [PATCH 10/12] add redis logs to sosreport * rhel8 /var/log/redis/redis.log * rhel7 scl location --- tools/sosreport/tower.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/sosreport/tower.py b/tools/sosreport/tower.py index d0dde20dad..9c4b440e4f 100644 --- a/tools/sosreport/tower.py +++ b/tools/sosreport/tower.py @@ -28,8 +28,10 @@ SOSREPORT_TOWER_DIRS = [ "/var/log/tower", "/var/log/nginx", "/var/log/supervisor", + "/var/log/redis", "/etc/opt/rh/rh-redis5/redis.conf", "/etc/redis.conf", + "/var/opt/rh/rh-redis5/log/redis/redis.log", "/var/log/dist-upgrade", "/var/log/installer", "/var/log/unattended-upgrades", From 6652464e257ded724a12a0abcbc8c410d1f0dd5d Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Tue, 28 Apr 2020 10:52:15 -0400 Subject: [PATCH 11/12] Unset old instance IP when conflicting new instance IP With AWX_AUTO_DEPROVISION_INSTANCES on, instances are registered with an ip address. However, new instances might try to register before old instances are deprivisioned. In this case old IPs can conflict with the new ones. This will check for an ip conflict and unset the IP of conflicting instance (set to None) ansible/awx issue 6750 --- awx/main/managers.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/awx/main/managers.py b/awx/main/managers.py index 9f1537fd6f..2076e7f0b0 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -121,6 +121,17 @@ class InstanceManager(models.Manager): if not hostname: hostname = settings.CLUSTER_HOST_ID with advisory_lock('instance_registration_%s' % hostname): + if settings.AWX_AUTO_DEPROVISION_INSTANCES: + # detect any instances with the same IP address. + # if one exists, set it to None + inst_conflicting_ip = self.filter(ip_address=ip_address).exclude(hostname=hostname) + if inst_conflicting_ip.exists(): + for other_inst in inst_conflicting_ip: + other_hostname = other_inst.hostname + other_inst.ip_address = None + other_inst.save(update_fields=['ip_address']) + logger.warning("IP address {0} conflict detected, ip address unset for host {1}.".format(ip_address, other_hostname)) + instance = self.filter(hostname=hostname) if instance.exists(): instance = instance.get() From 5d24acf613a768e59f3e4c4fcf2c8fd486cce1f2 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Tue, 28 Apr 2020 10:56:23 -0400 Subject: [PATCH 12/12] Allow unsigned certs in logging if cert verification is disabled. --- awx/main/tests/unit/api/test_logger.py | 14 +++++++------- awx/main/utils/external_logging.py | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/awx/main/tests/unit/api/test_logger.py b/awx/main/tests/unit/api/test_logger.py index a28c5d0153..2a0bb9856d 100644 --- a/awx/main/tests/unit/api/test_logger.py +++ b/awx/main/tests/unit/api/test_logger.py @@ -44,7 +44,7 @@ data_loggly = { 'https', '\n'.join([ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="logs-01.loggly.com" serverport="80" usehttps="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/")', # noqa + 'action(type="omhttp" server="logs-01.loggly.com" serverport="80" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/")', # noqa ]) ), ( @@ -77,7 +77,7 @@ data_loggly = { None, '\n'.join([ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk" serverport="443" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk" serverport="443" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa ]) ), ( @@ -88,7 +88,7 @@ data_loggly = { None, '\n'.join([ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk" serverport="80" usehttps="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk" serverport="80" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa ]) ), ( @@ -99,7 +99,7 @@ data_loggly = { None, '\n'.join([ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa ]) ), ( @@ -110,7 +110,7 @@ data_loggly = { None, '\n'.join([ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa ]) ), ( @@ -121,7 +121,7 @@ data_loggly = { 'https', '\n'.join([ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk.org" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk.org" serverport="8088" usehttps="on" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa ]) ), ( @@ -132,7 +132,7 @@ data_loggly = { None, '\n'.join([ 'template(name="awx" type="string" string="%rawmsg-after-pri%")\nmodule(load="omhttp")', - 'action(type="omhttp" server="yoursplunk.org" serverport="8088" usehttps="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa + 'action(type="omhttp" server="yoursplunk.org" serverport="8088" usehttps="off" allowunsignedcerts="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/rsyslog.err" action.resumeInterval="5" restpath="services/collector/event")', # noqa ]) ), ] diff --git a/awx/main/utils/external_logging.py b/awx/main/utils/external_logging.py index 4b3fcda7fb..8444b1cfbb 100644 --- a/awx/main/utils/external_logging.py +++ b/awx/main/utils/external_logging.py @@ -60,6 +60,7 @@ def construct_rsyslog_conf_template(settings=settings): # https://github.com/rsyslog/rsyslog-doc/blob/master/source/configuration/modules/omhttp.rst ssl = 'on' if parsed.scheme == 'https' else 'off' skip_verify = 'off' if settings.LOG_AGGREGATOR_VERIFY_CERT else 'on' + allow_unsigned = 'off' if settings.LOG_AGGREGATOR_VERIFY_CERT else 'on' if not port: port = 443 if parsed.scheme == 'https' else 80 @@ -68,6 +69,7 @@ def construct_rsyslog_conf_template(settings=settings): f'server="{host}"', f'serverport="{port}"', f'usehttps="{ssl}"', + f'allowunsignedcerts="{allow_unsigned}"', f'skipverifyhost="{skip_verify}"', 'action.resumeRetryCount="-1"', 'template="awx"',