We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Tower settings fails with stacktrace - expected to exit gracefully
##### ISSUE TYPE
 - Bug Report

##### SUMMARY
Running awx.awx.settings module throws `AttributeError: 'NoneType' object has no attribute 'startswith'`

##### ENVIRONMENT
* AWX version: 11.2.0
* AWX install method: setup.sh
* Ansible version:  2.9.7
* Operating System: Tower on RHEL, Ansible on Fedora

##### STEPS TO REPRODUCE

```
---
# tasks file for ansible_tower_genie_settings- name: Update Ansible Tower Settings
  awx.awx.tower_settings:
    name:                "{{ tower_setting_item.name | default(omit) }}"
    value:               "{{ tower_setting_item.value | default(omit) }}"
    tower_config_file:   "{{ tower_config_file | default(omit) }}"
    tower_host:          "{{ tower_hostname | default(omit) }}"
    tower_password:      "{{ tower_password | default(omit) }}"
    tower_username:      "{{ tower_username | default(omit) }}"
    validate_certs:      "{{ validate_certs | default('false') }}"
  loop: "{{ tower_settings }}"
  loop_control:
    loop_var: tower_setting_item
...

```
Data:
```
---
tower_settings:
  - name: AWX_TASK_ENV
    setting: {'GIT_SSL_NO_VERIFY': 'True'}
```

##### EXPECTED RESULTS

Error should be more helpful, gracefully handled vs flat out traceback if possible.

##### ACTUAL RESULTS

```
The full traceback is:
Traceback (most recent call last):
  File "/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py", line 102, in <module>
    _ansiballz_main()
  File "/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py", line 94, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py", line 40, in invoke_module
    runpy.run_module(mod_name='ansible_collections.awx.awx.plugins.modules.tower_settings', init_globals=None, run_name='__main__', alter_sys=True)
  File "/usr/lib64/python3.8/runpy.py", line 206, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "/usr/lib64/python3.8/runpy.py", line 96, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/lib64/python3.8/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py", line 184, in <module>
  File "/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py", line 136, in main
  File "/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py", line 95, in coerce_type
AttributeError: 'NoneType' object has no attribute 'startswith'
failed: [localhost] (item={'name': 'AWX_TASK_ENV', 'setting': {'GIT_SSL_NO_VERIFY': 'True'}}) => {
    "ansible_loop_var": "tower_setting_item",
    "changed": false,
    "module_stderr": "Traceback (most recent call last):\n  File \"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\", line 102, in <module>\n    _ansiballz_main()\n  File \"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\", line 94, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\", line 40, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.awx.awx.plugins.modules.tower_settings', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib64/python3.8/runpy.py\", line 206, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib64/python3.8/runpy.py\", line 96, in _run_module_code\n    _run_code(code, mod_globals, init_globals,\n  File \"/usr/lib64/python3.8/runpy.py\", line 86, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\", line 184, in <module>\n  File \"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\", line 136, in main\n  File \"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\", line 95, in coerce_type\nAttributeError: 'NoneType' object has no attribute 'startswith'\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1,
    "tower_setting_item": {
        "name": "AWX_TASK_ENV",
        "setting": {
            "GIT_SSL_NO_VERIFY": "True"
        }
    }
}
```

##### ADDITIONAL INFORMATION

<!-- Include any links to sosreport, database dumps, screenshots or other
information. -->


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `awx_collection/plugins/modules/tower_settings.py`
Content:
```
1 #!/usr/bin/python
2 # coding: utf-8 -*-
3 
4 # (c) 2018, Nikhil Jain <nikjain@redhat.com>
5 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 
7 from __future__ import absolute_import, division, print_function
8 __metaclass__ = type
9 
10 
11 ANSIBLE_METADATA = {'metadata_version': '1.1',
12                     'status': ['preview'],
13                     'supported_by': 'community'}
14 
15 
16 DOCUMENTATION = '''
17 ---
18 module: tower_settings
19 author: "Nikhil Jain (@jainnikhil30)"
20 short_description: Modify Ansible Tower settings.
21 description:
22     - Modify Ansible Tower settings. See
23       U(https://www.ansible.com/tower) for an overview.
24 options:
25     name:
26       description:
27         - Name of setting to modify
28       type: str
29     value:
30       description:
31         - Value to be modified for given setting.
32         - If given a non-string type, will make best effort to cast it to type API expects.
33         - For better control over types, use the C(settings) param instead.
34       type: str
35     settings:
36       description:
37         - A data structure to be sent into the settings endpoint
38       type: dict
39 requirements:
40   - pyyaml
41 extends_documentation_fragment: awx.awx.auth
42 '''
43 
44 EXAMPLES = '''
45 - name: Set the value of AWX_PROOT_BASE_PATH
46   tower_settings:
47     name: AWX_PROOT_BASE_PATH
48     value: "/tmp"
49   register: testing_settings
50 
51 - name: Set the value of AWX_PROOT_SHOW_PATHS
52   tower_settings:
53     name: "AWX_PROOT_SHOW_PATHS"
54     value: "'/var/lib/awx/projects/', '/tmp'"
55   register: testing_settings
56 
57 - name: Set the LDAP Auth Bind Password
58   tower_settings:
59     name: "AUTH_LDAP_BIND_PASSWORD"
60     value: "Password"
61   no_log: true
62 
63 - name: Set all the LDAP Auth Bind Params
64   tower_settings:
65     settings:
66       AUTH_LDAP_BIND_PASSWORD: "password"
67       AUTH_LDAP_USER_ATTR_MAP:
68         email: "mail"
69         first_name: "givenName"
70         last_name: "surname"
71 '''
72 
73 from ..module_utils.tower_api import TowerModule
74 
75 try:
76     import yaml
77     HAS_YAML = True
78 except ImportError:
79     HAS_YAML = False
80 
81 
82 def coerce_type(module, value):
83     yaml_ish = bool((
84         value.startswith('{') and value.endswith('}')
85     ) or (
86         value.startswith('[') and value.endswith(']'))
87     )
88     if yaml_ish:
89         if not HAS_YAML:
90             module.fail_json(msg="yaml is not installed, try 'pip install pyyaml'")
91         return yaml.safe_load(value)
92     elif value.lower in ('true', 'false', 't', 'f'):
93         return {'t': True, 'f': False}[value[0].lower()]
94     try:
95         return int(value)
96     except ValueError:
97         pass
98     return value
99 
100 
101 def main():
102     # Any additional arguments that are not fields of the item can be added here
103     argument_spec = dict(
104         name=dict(),
105         value=dict(),
106         settings=dict(type='dict'),
107     )
108 
109     # Create a module for ourselves
110     module = TowerModule(
111         argument_spec=argument_spec,
112         required_one_of=[['name', 'settings']],
113         mutually_exclusive=[['name', 'settings']],
114         required_if=[['name', 'present', ['value']]]
115     )
116 
117     # Extract our parameters
118     name = module.params.get('name')
119     value = module.params.get('value')
120     new_settings = module.params.get('settings')
121 
122     # If we were given a name/value pair we will just make settings out of that and proceed normally
123     if new_settings is None:
124         new_value = coerce_type(module, value)
125 
126         new_settings = {name: new_value}
127 
128     # Load the existing settings
129     existing_settings = module.get_endpoint('settings/all')['json']
130 
131     # Begin a json response
132     json_response = {'changed': False, 'old_values': {}}
133 
134     # Check any of the settings to see if anything needs to be updated
135     needs_update = False
136     for a_setting in new_settings:
137         if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]:
138             # At least one thing is different so we need to patch
139             needs_update = True
140             json_response['old_values'][a_setting] = existing_settings[a_setting]
141 
142     # If nothing needs an update we can simply exit with the response (as not changed)
143     if not needs_update:
144         module.exit_json(**json_response)
145 
146     # Make the call to update the settings
147     response = module.patch_endpoint('settings/all', **{'data': new_settings})
148 
149     if response['status_code'] == 200:
150         # Set the changed response to True
151         json_response['changed'] = True
152 
153         # To deal with the old style values we need to return 'value' in the response
154         new_values = {}
155         for a_setting in new_settings:
156             new_values[a_setting] = response['json'][a_setting]
157 
158         # If we were using a name we will just add a value of a string, otherwise we will return an array in values
159         if name is not None:
160             json_response['value'] = new_values[name]
161         else:
162             json_response['values'] = new_values
163 
164         module.exit_json(**json_response)
165     elif 'json' in response and '__all__' in response['json']:
166         module.fail_json(msg=response['json']['__all__'])
167     else:
168         module.fail_json(**{'msg': "Unable to update settings, see response", 'response': response})
169 
170 
171 if __name__ == '__main__':
172     main()
173
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/awx_collection/plugins/modules/tower_settings.py b/awx_collection/plugins/modules/tower_settings.py
--- a/awx_collection/plugins/modules/tower_settings.py
+++ b/awx_collection/plugins/modules/tower_settings.py
@@ -82,6 +82,10 @@
 
 
 def coerce_type(module, value):
+    # If our value is already None we can just return directly
+    if value is None:
+        return value
+
     yaml_ish = bool((
         value.startswith('{') and value.endswith('}')
     ) or (

{"golden_diff": "diff --git a/awx_collection/plugins/modules/tower_settings.py b/awx_collection/plugins/modules/tower_settings.py\n--- a/awx_collection/plugins/modules/tower_settings.py\n+++ b/awx_collection/plugins/modules/tower_settings.py\n@@ -82,6 +82,10 @@\n \n \n def coerce_type(module, value):\n+    # If our value is already None we can just return directly\n+    if value is None:\n+        return value\n+\n     yaml_ish = bool((\n         value.startswith('{') and value.endswith('}')\n     ) or (\n", "issue": "Tower settings fails with stacktrace - expected to exit gracefully\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### SUMMARY\r\nRunning awx.awx.settings module throws `AttributeError: 'NoneType' object has no attribute 'startswith'`\r\n\r\n##### ENVIRONMENT\r\n* AWX version: 11.2.0\r\n* AWX install method: setup.sh\r\n* Ansible version:  2.9.7\r\n* Operating System: Tower on RHEL, Ansible on Fedora\r\n\r\n##### STEPS TO REPRODUCE\r\n\r\n```\r\n---\r\n# tasks file for ansible_tower_genie_settings- name: Update Ansible Tower Settings\r\n  awx.awx.tower_settings:\r\n    name:                \"{{ tower_setting_item.name | default(omit) }}\"\r\n    value:               \"{{ tower_setting_item.value | default(omit) }}\"\r\n    tower_config_file:   \"{{ tower_config_file | default(omit) }}\"\r\n    tower_host:          \"{{ tower_hostname | default(omit) }}\"\r\n    tower_password:      \"{{ tower_password | default(omit) }}\"\r\n    tower_username:      \"{{ tower_username | default(omit) }}\"\r\n    validate_certs:      \"{{ validate_certs | default('false') }}\"\r\n  loop: \"{{ tower_settings }}\"\r\n  loop_control:\r\n    loop_var: tower_setting_item\r\n...\r\n\r\n```\r\nData:\r\n```\r\n---\r\ntower_settings:\r\n  - name: AWX_TASK_ENV\r\n    setting: {'GIT_SSL_NO_VERIFY': 'True'}\r\n```\r\n\r\n##### EXPECTED RESULTS\r\n\r\nError should be more helpful, gracefully handled vs flat out traceback if possible.\r\n\r\n##### ACTUAL RESULTS\r\n\r\n```\r\nThe full traceback is:\r\nTraceback (most recent call last):\r\n  File \"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\", line 102, in <module>\r\n    _ansiballz_main()\r\n  File \"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\", line 94, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\", line 40, in invoke_module\r\n    runpy.run_module(mod_name='ansible_collections.awx.awx.plugins.modules.tower_settings', init_globals=None, run_name='__main__', alter_sys=True)\r\n  File \"/usr/lib64/python3.8/runpy.py\", line 206, in run_module\r\n    return _run_module_code(code, init_globals, run_name, mod_spec)\r\n  File \"/usr/lib64/python3.8/runpy.py\", line 96, in _run_module_code\r\n    _run_code(code, mod_globals, init_globals,\r\n  File \"/usr/lib64/python3.8/runpy.py\", line 86, in _run_code\r\n    exec(code, run_globals)\r\n  File \"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\", line 184, in <module>\r\n  File \"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\", line 136, in main\r\n  File \"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\", line 95, in coerce_type\r\nAttributeError: 'NoneType' object has no attribute 'startswith'\r\nfailed: [localhost] (item={'name': 'AWX_TASK_ENV', 'setting': {'GIT_SSL_NO_VERIFY': 'True'}}) => {\r\n    \"ansible_loop_var\": \"tower_setting_item\",\r\n    \"changed\": false,\r\n    \"module_stderr\": \"Traceback (most recent call last):\\n  File \\\"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\\\", line 102, in <module>\\n    _ansiballz_main()\\n  File \\\"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\\\", line 94, in _ansiballz_main\\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\\n  File \\\"/home/kkulkarni/.ansible/tmp/ansible-tmp-1591366383.968238-878504-224766098821440/AnsiballZ_tower_settings.py\\\", line 40, in invoke_module\\n    runpy.run_module(mod_name='ansible_collections.awx.awx.plugins.modules.tower_settings', init_globals=None, run_name='__main__', alter_sys=True)\\n  File \\\"/usr/lib64/python3.8/runpy.py\\\", line 206, in run_module\\n    return _run_module_code(code, init_globals, run_name, mod_spec)\\n  File \\\"/usr/lib64/python3.8/runpy.py\\\", line 96, in _run_module_code\\n    _run_code(code, mod_globals, init_globals,\\n  File \\\"/usr/lib64/python3.8/runpy.py\\\", line 86, in _run_code\\n    exec(code, run_globals)\\n  File \\\"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\\\", line 184, in <module>\\n  File \\\"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\\\", line 136, in main\\n  File \\\"/tmp/ansible_awx.awx.tower_settings_payload_x13vlw6v/ansible_awx.awx.tower_settings_payload.zip/ansible_collections/awx/awx/plugins/modules/tower_settings.py\\\", line 95, in coerce_type\\nAttributeError: 'NoneType' object has no attribute 'startswith'\\n\",\r\n    \"module_stdout\": \"\",\r\n    \"msg\": \"MODULE FAILURE\\nSee stdout/stderr for the exact error\",\r\n    \"rc\": 1,\r\n    \"tower_setting_item\": {\r\n        \"name\": \"AWX_TASK_ENV\",\r\n        \"setting\": {\r\n            \"GIT_SSL_NO_VERIFY\": \"True\"\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n##### ADDITIONAL INFORMATION\r\n\r\n<!-- Include any links to sosreport, database dumps, screenshots or other\r\ninformation. -->\r\n\n", "before_files": [{"content": "#!/usr/bin/python\n# coding: utf-8 -*-\n\n# (c) 2018, Nikhil Jain <nikjain@redhat.com>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\n\nANSIBLE_METADATA = {'metadata_version': '1.1',\n                    'status': ['preview'],\n                    'supported_by': 'community'}\n\n\nDOCUMENTATION = '''\n---\nmodule: tower_settings\nauthor: \"Nikhil Jain (@jainnikhil30)\"\nshort_description: Modify Ansible Tower settings.\ndescription:\n    - Modify Ansible Tower settings. See\n      U(https://www.ansible.com/tower) for an overview.\noptions:\n    name:\n      description:\n        - Name of setting to modify\n      type: str\n    value:\n      description:\n        - Value to be modified for given setting.\n        - If given a non-string type, will make best effort to cast it to type API expects.\n        - For better control over types, use the C(settings) param instead.\n      type: str\n    settings:\n      description:\n        - A data structure to be sent into the settings endpoint\n      type: dict\nrequirements:\n  - pyyaml\nextends_documentation_fragment: awx.awx.auth\n'''\n\nEXAMPLES = '''\n- name: Set the value of AWX_PROOT_BASE_PATH\n  tower_settings:\n    name: AWX_PROOT_BASE_PATH\n    value: \"/tmp\"\n  register: testing_settings\n\n- name: Set the value of AWX_PROOT_SHOW_PATHS\n  tower_settings:\n    name: \"AWX_PROOT_SHOW_PATHS\"\n    value: \"'/var/lib/awx/projects/', '/tmp'\"\n  register: testing_settings\n\n- name: Set the LDAP Auth Bind Password\n  tower_settings:\n    name: \"AUTH_LDAP_BIND_PASSWORD\"\n    value: \"Password\"\n  no_log: true\n\n- name: Set all the LDAP Auth Bind Params\n  tower_settings:\n    settings:\n      AUTH_LDAP_BIND_PASSWORD: \"password\"\n      AUTH_LDAP_USER_ATTR_MAP:\n        email: \"mail\"\n        first_name: \"givenName\"\n        last_name: \"surname\"\n'''\n\nfrom ..module_utils.tower_api import TowerModule\n\ntry:\n    import yaml\n    HAS_YAML = True\nexcept ImportError:\n    HAS_YAML = False\n\n\ndef coerce_type(module, value):\n    yaml_ish = bool((\n        value.startswith('{') and value.endswith('}')\n    ) or (\n        value.startswith('[') and value.endswith(']'))\n    )\n    if yaml_ish:\n        if not HAS_YAML:\n            module.fail_json(msg=\"yaml is not installed, try 'pip install pyyaml'\")\n        return yaml.safe_load(value)\n    elif value.lower in ('true', 'false', 't', 'f'):\n        return {'t': True, 'f': False}[value[0].lower()]\n    try:\n        return int(value)\n    except ValueError:\n        pass\n    return value\n\n\ndef main():\n    # Any additional arguments that are not fields of the item can be added here\n    argument_spec = dict(\n        name=dict(),\n        value=dict(),\n        settings=dict(type='dict'),\n    )\n\n    # Create a module for ourselves\n    module = TowerModule(\n        argument_spec=argument_spec,\n        required_one_of=[['name', 'settings']],\n        mutually_exclusive=[['name', 'settings']],\n        required_if=[['name', 'present', ['value']]]\n    )\n\n    # Extract our parameters\n    name = module.params.get('name')\n    value = module.params.get('value')\n    new_settings = module.params.get('settings')\n\n    # If we were given a name/value pair we will just make settings out of that and proceed normally\n    if new_settings is None:\n        new_value = coerce_type(module, value)\n\n        new_settings = {name: new_value}\n\n    # Load the existing settings\n    existing_settings = module.get_endpoint('settings/all')['json']\n\n    # Begin a json response\n    json_response = {'changed': False, 'old_values': {}}\n\n    # Check any of the settings to see if anything needs to be updated\n    needs_update = False\n    for a_setting in new_settings:\n        if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]:\n            # At least one thing is different so we need to patch\n            needs_update = True\n            json_response['old_values'][a_setting] = existing_settings[a_setting]\n\n    # If nothing needs an update we can simply exit with the response (as not changed)\n    if not needs_update:\n        module.exit_json(**json_response)\n\n    # Make the call to update the settings\n    response = module.patch_endpoint('settings/all', **{'data': new_settings})\n\n    if response['status_code'] == 200:\n        # Set the changed response to True\n        json_response['changed'] = True\n\n        # To deal with the old style values we need to return 'value' in the response\n        new_values = {}\n        for a_setting in new_settings:\n            new_values[a_setting] = response['json'][a_setting]\n\n        # If we were using a name we will just add a value of a string, otherwise we will return an array in values\n        if name is not None:\n            json_response['value'] = new_values[name]\n        else:\n            json_response['values'] = new_values\n\n        module.exit_json(**json_response)\n    elif 'json' in response and '__all__' in response['json']:\n        module.fail_json(msg=response['json']['__all__'])\n    else:\n        module.fail_json(**{'msg': \"Unable to update settings, see response\", 'response': response})\n\n\nif __name__ == '__main__':\n    main()\n", "path": "awx_collection/plugins/modules/tower_settings.py"}], "after_files": [{"content": "#!/usr/bin/python\n# coding: utf-8 -*-\n\n# (c) 2018, Nikhil Jain <nikjain@redhat.com>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\n\nANSIBLE_METADATA = {'metadata_version': '1.1',\n                    'status': ['preview'],\n                    'supported_by': 'community'}\n\n\nDOCUMENTATION = '''\n---\nmodule: tower_settings\nauthor: \"Nikhil Jain (@jainnikhil30)\"\nversion_added: \"2.7\"\nshort_description: Modify Ansible Tower settings.\ndescription:\n    - Modify Ansible Tower settings. See\n      U(https://www.ansible.com/tower) for an overview.\noptions:\n    name:\n      description:\n        - Name of setting to modify\n      type: str\n    value:\n      description:\n        - Value to be modified for given setting.\n        - If given a non-string type, will make best effort to cast it to type API expects.\n        - For better control over types, use the C(settings) param instead.\n      type: str\n    settings:\n      description:\n        - A data structure to be sent into the settings endpoint\n      type: dict\n      version_added: \"3.7\"\nrequirements:\n  - pyyaml\nextends_documentation_fragment: awx.awx.auth\n'''\n\nEXAMPLES = '''\n- name: Set the value of AWX_PROOT_BASE_PATH\n  tower_settings:\n    name: AWX_PROOT_BASE_PATH\n    value: \"/tmp\"\n  register: testing_settings\n\n- name: Set the value of AWX_PROOT_SHOW_PATHS\n  tower_settings:\n    name: \"AWX_PROOT_SHOW_PATHS\"\n    value: \"'/var/lib/awx/projects/', '/tmp'\"\n  register: testing_settings\n\n- name: Set the LDAP Auth Bind Password\n  tower_settings:\n    name: \"AUTH_LDAP_BIND_PASSWORD\"\n    value: \"Password\"\n  no_log: true\n\n- name: Set all the LDAP Auth Bind Params\n  tower_settings:\n    settings:\n      AUTH_LDAP_BIND_PASSWORD: \"password\"\n      AUTH_LDAP_USER_ATTR_MAP:\n        email: \"mail\"\n        first_name: \"givenName\"\n        last_name: \"surname\"\n'''\n\nfrom ..module_utils.tower_api import TowerModule\n\ntry:\n    import yaml\n    HAS_YAML = True\nexcept ImportError:\n    HAS_YAML = False\n\n\ndef coerce_type(module, value):\n    # If our value is already None we can just return directly\n    if value is None:\n        return value\n\n    yaml_ish = bool((\n        value.startswith('{') and value.endswith('}')\n    ) or (\n        value.startswith('[') and value.endswith(']'))\n    )\n    if yaml_ish:\n        if not HAS_YAML:\n            module.fail_json(msg=\"yaml is not installed, try 'pip install pyyaml'\")\n        return yaml.safe_load(value)\n    elif value.lower in ('true', 'false', 't', 'f'):\n        return {'t': True, 'f': False}[value[0].lower()]\n    try:\n        return int(value)\n    except ValueError:\n        pass\n    return value\n\n\ndef main():\n    # Any additional arguments that are not fields of the item can be added here\n    argument_spec = dict(\n        name=dict(),\n        value=dict(),\n        settings=dict(type='dict'),\n    )\n\n    # Create a module for ourselves\n    module = TowerModule(\n        argument_spec=argument_spec,\n        required_one_of=[['name', 'settings']],\n        mutually_exclusive=[['name', 'settings']],\n        required_if=[['name', 'present', ['value']]]\n    )\n\n    # Extract our parameters\n    name = module.params.get('name')\n    value = module.params.get('value')\n    new_settings = module.params.get('settings')\n\n    # If we were given a name/value pair we will just make settings out of that and proceed normally\n    if new_settings is None:\n        new_value = coerce_type(module, value)\n\n        new_settings = {name: new_value}\n\n    # Load the existing settings\n    existing_settings = module.get_endpoint('settings/all')['json']\n\n    # Begin a json response\n    json_response = {'changed': False, 'old_values': {}}\n\n    # Check any of the settings to see if anything needs to be updated\n    needs_update = False\n    for a_setting in new_settings:\n        if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]:\n            # At least one thing is different so we need to patch\n            needs_update = True\n            json_response['old_values'][a_setting] = existing_settings[a_setting]\n\n    # If nothing needs an update we can simply exit with the response (as not changed)\n    if not needs_update:\n        module.exit_json(**json_response)\n\n    # Make the call to update the settings\n    response = module.patch_endpoint('settings/all', **{'data': new_settings})\n\n    if response['status_code'] == 200:\n        # Set the changed response to True\n        json_response['changed'] = True\n\n        # To deal with the old style values we need to return 'value' in the response\n        new_values = {}\n        for a_setting in new_settings:\n            new_values[a_setting] = response['json'][a_setting]\n\n        # If we were using a name we will just add a value of a string, otherwise we will return an array in values\n        if name is not None:\n            json_response['value'] = new_values[name]\n        else:\n            json_response['values'] = new_values\n\n        module.exit_json(**json_response)\n    elif 'json' in response and '__all__' in response['json']:\n        module.fail_json(msg=response['json']['__all__'])\n    else:\n        module.fail_json(**{'msg': \"Unable to update settings, see response\", 'response': response})\n\n\nif __name__ == '__main__':\n    main()\n", "path": "awx_collection/plugins/modules/tower_settings.py"}]}