Repository: ansible/ansible
Pull request: Fix for "AttributeError: 'module' object has no attribute 'cursors'" (#49191)
##### SUMMARY
<!--- Describe the change below, including rationale and design decisions -->
Fixes #49191.
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- Bugfix Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
mysql_db

##### ADDITIONAL INFORMATION
<!--- Include additional information to help people understand the change here -->
<!--- A step-by-step reproduction of the problem is helpful if there is no related issue -->
MySQLdb doesn't import the cursors module for its own purposes so it has to be imported in MySQL module utilities before it can be used in dependent modules like the proxysql module family.


Problem statement:
Module ProxySQL - Backend_servers error
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
Using the proxysql_backend_servers module, I get an error

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
lib/ansible/modules/database/proxysql/proxysql_backend_servers.py

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.7.1
  config file = /Users/user/.ansible.cfg
  configured module search path = [u'/Users/user/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /Library/Python/2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.10 (default, Aug 17 2018, 17:41:52) [GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.0.42)]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
DEFAULT_ASK_SUDO_PASS(/Users/user/.ansible.cfg) = True
DEFAULT_HOST_LIST(/Users/user/.ansible.cfg) = [u'/Users/user/git/ansible/inventories/production/hosts']
DEFAULT_REMOTE_USER(/Users/user/.ansible.cfg) = user
```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
MacOS 10.14.1 (18B75)

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
Use the proxysql_backend_servers in a task
<!--- Paste example playbooks or commands between quotes below -->
```yaml
# Add or remove backend servers
- name: Add backend server
  proxysql_backend_servers:
    login_user: "{{ proxysql_admin_user }}"
    login_password: "{{ proxysql_admin_password }}"
    hostname: "{{ item.address }}"
    port: "{{item.port}}"
    hostgroup_id: "{{ item.hostgroup }}"
    state: present
    load_to_runtime: "{{ load_to_runtime }}"
    save_to_disk: "{{ save_to_disk }}"
  with_items: "{{ proxysql_mysql_servers }}"
  when:
    - proxysql_mysql_servers is defined
    - proxysql_mysql_servers_remove is undefined
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
The backend servers are added to proxysql

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
The module errors out
<!--- Paste verbatim command output between quotes -->
```paste below
failed: [host] (item={u'port': 3306, u'hostgroup': 2, u'address': u'1.2.3.4'}) => {"changed": false, "item": {"address": "1.2.3.4", "hostgroup": 2, "port": 3306}, "module_stderr": "Shared connection to 1.2.3.3 closed.\r\n", "module_stdout": "\r\nTraceback (most recent call last):\r\n  File \"/home/user/.ansible/tmp/ansible-tmp-1543327459.08-40750051886508/AnsiballZ_proxysql_backend_servers.py\", line 113, in <module>\r\n    _ansiballz_main()\r\n  File \"/home/user/.ansible/tmp/ansible-tmp-1543327459.08-40750051886508/AnsiballZ_proxysql_backend_servers.py\", line 105, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/home/user/.ansible/tmp/ansible-tmp-1543327459.08-40750051886508/AnsiballZ_proxysql_backend_servers.py\", line 48, in invoke_module\r\n    imp.load_module('__main__', mod, module, MOD_DESC)\r\n  File \"/tmp/ansible_proxysql_backend_servers_payload_yTxxSk/__main__.py\", line 504, in <module>\r\n  File \"/tmp/ansible_proxysql_backend_servers_payload_yTxxSk/__main__.py\", line 450, in main\r\nAttributeError: 'module' object has no attribute 'cursors'\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
```

Hints from issue discussion:
Hi @matt-horwood-mayden, thank you for submitting this issue!

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: thank_submitter --->
Files identified in the description:
* [lib/ansible/modules/database/proxysql/proxysql_backend_servers.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/database/proxysql/proxysql_backend_servers.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @bmildren
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I believe this is an issue with the shared mysql module util (https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/mysql.py) rather the proxysql modules. This has been fixed with this PR: ansible/ansible#47809 (you can apply the patch to the version of ansible you're using by applying the generated patch file: https://patch-diff.githubusercontent.com/raw/ansible/ansible/pull/47809.patch)
cc @Xyon @bmalynovytch @michaelcoburn @oneiroi @tolland
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Commit message: Adding changelog fragment for issue #49191 and the following PR.

Patch to review:
diff --git a/changelogs/fragments/49191-module_utils_mysql-unexpected-keyword-argument-cursorclass.yml b/changelogs/fragments/49191-module_utils_mysql-unexpected-keyword-argument-cursorclass.yml
new file mode 100644
index 00000000000000..01139d4f39d9a3
--- /dev/null
+++ b/changelogs/fragments/49191-module_utils_mysql-unexpected-keyword-argument-cursorclass.yml
@@ -0,0 +1,2 @@
+bugfixes:
+- mysql - MySQLdb doesn't import the cursors module for its own purposes so it has to be imported in MySQL module utilities before it can be used in dependent modules like the proxysql module family.
diff --git a/lib/ansible/module_utils/mysql.py b/lib/ansible/module_utils/mysql.py
index 8237efc10d61bf..4b2b6932342524 100644
--- a/lib/ansible/module_utils/mysql.py
+++ b/lib/ansible/module_utils/mysql.py
@@ -34,7 +34,7 @@
     _mysql_cursor_param = 'cursor'
 except ImportError:
     try:
-        import MySQLdb as mysql_driver
+        import MySQLdb as mysql_driver, MySQLdb.cursors
         _mysql_cursor_param = 'cursorclass'
     except ImportError:
         mysql_driver = None


Code review comments:
diff_hunk: @@ -34,7 +34,7 @@
     _mysql_cursor_param = 'cursor'
 except ImportError:
     try:
-        import MySQLdb as mysql_driver
+        import MySQLdb as mysql_driver, MySQLdb.cursors
path: lib/ansible/module_utils/mysql.py
text: @user1:
The sanity checks fail on this. Can you put it into a new line?
```suggestion
        import MySQLdb as mysql_driver
        import MySQLdb.cursors
```