Repository: ansible/ansible-modules-core

Issue:
mysql_db fails when login_port value in unquoted
mysql_db module works well when login_port value is quoted (login_port: "3306"), but it fails when the value is unquoted (login_port: 3306):

```
File "/Users/user/.ansible/tmp/ansible-tmp-1424309772.84-207399355587003/mysql_db", line 131, in db_dump
    cmd += " --host=%s --port=%s" % (pipes.quote(host), pipes.quote(port))
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pipes.py", line 269, in quote
    for c in file:
TypeError: 'int' object is not iterable

FATAL: all hosts have already failed -- aborting
```



Discussion:
@mykola42 Can you provide some more details on how you produced this error. I just tried on latest devel branch and could not reproduce.

```
[root@phy01 tmp]# ansible localhost -m mysql_db -a "name=yarg login_port=3306"
localhost | success >> {
    "changed": true,
    "db": "yarg"
}

[root@phy01 tmp]# ansible localhost -m mysql_db -a "name=yarg login_port=3306"
localhost | success >> {
    "changed": false,
    "db": "yarg"
}

[root@phy01 tmp]# ansible localhost -m mysql_db -a "name=yarg login_port='3306'"
localhost | success >> {
    "changed": false,
    "db": "yarg"
}
```

@Jmainguy I'm actually using mysql_db module in my playbook:

```
---
- name: Test mysql_db module
  hosts: all
  gather_facts: False
  connection: local
  tasks:
  - name: Backup MySQL
    mysql_db:
      login_host: "127.0.0.1"
      login_port: "3306"
      login_user: "root"
      login_password: "root"
      state: "dump"
      name: "test"
      target: "~/Downloads/temp/test.sql"
```

And it works, because login_port value was quoted:

```
$ ansible-playbook -i inventory/mysql_servers backup_mysql.yml

PLAY [Test mysql_db module] *************************************************** 

TASK: [Backup MySQL] ********************************************************** 
changed: [localhost]

PLAY RECAP ******************************************************************** 
localhost                 : ok=1    changed=1    unreachable=0    failed=0   
```

And it fails when I have login_port value unquoted (this is the only one difference):

```
---
- name: Test mysql_db module
  hosts: all
  gather_facts: False
  connection: local
  tasks:
  - name: Backup MySQL
    mysql_db:
      login_host: "127.0.0.1"
      login_port: 3306
      login_user: "root"
      login_password: "root"
      state: "dump"
      name: "test"
      target: "~/Downloads/temp/test.sql"
```

The output is:

```
$ ansible-playbook -i inventory/mysql_servers backup_mysql.yml

PLAY [Test mysql_db module] *************************************************** 

TASK: [Backup MySQL] ********************************************************** 
failed: [localhost] => {"failed": true, "parsed": false}
Traceback (most recent call last):
  File "/Users/mykola/.ansible/tmp/ansible-tmp-1424380664.74-318954798701/mysql_db", line 2088, in <module>
    main()
  File "/Users/mykola/.ansible/tmp/ansible-tmp-1424380664.74-318954798701/mysql_db", line 340, in main
    socket=module.params['login_unix_socket'])
  File "/Users/mykola/.ansible/tmp/ansible-tmp-1424380664.74-318954798701/mysql_db", line 131, in db_dump
    cmd += " --host=%s --port=%s" % (pipes.quote(host), pipes.quote(port))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pipes.py", line 269, in quote
    for c in file:
TypeError: 'int' object is not iterable

failed: [localhost] => {"failed": true, "parsed": false}
Traceback (most recent call last):
  File "/Users/mykola/.ansible/tmp/ansible-tmp-1424380664.74-219871789838659/mysql_db", line 2088, in <module>
    main()
  File "/Users/mykola/.ansible/tmp/ansible-tmp-1424380664.74-219871789838659/mysql_db", line 340, in main
    socket=module.params['login_unix_socket'])
  File "/Users/mykola/.ansible/tmp/ansible-tmp-1424380664.74-219871789838659/mysql_db", line 131, in db_dump
    cmd += " --host=%s --port=%s" % (pipes.quote(host), pipes.quote(port))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pipes.py", line 269, in quote
    for c in file:
TypeError: 'int' object is not iterable

FATAL: all hosts have already failed -- aborting

PLAY RECAP ******************************************************************** 

localhost                 : ok=0    changed=0    unreachable=0    failed=1   
```

My configuration:
Mac OS X 10.10.2
$ ansible --version
ansible 1.8.3
$ python
Python 2.7.6 (default, Sep  9 2014, 15:04:36)


Resolving patch:
diff --git a/database/mysql/mysql_db.py b/database/mysql/mysql_db.py
--- a/database/mysql/mysql_db.py
+++ b/database/mysql/mysql_db.py
@@ -129,7 +129,7 @@ def db_dump(module, host, user, password, db_name, target, port, socket=None):
     if socket is not None:
         cmd += " --socket=%s" % pipes.quote(socket)
     else:
-        cmd += " --host=%s --port=%s" % (pipes.quote(host), pipes.quote(port))
+        cmd += " --host=%s --port=%i" % (pipes.quote(host), port)
     cmd += " %s" % pipes.quote(db_name)
     if os.path.splitext(target)[-1] == '.gz':
         cmd = cmd + ' | gzip > ' + pipes.quote(target)
@@ -149,7 +149,7 @@ def db_import(module, host, user, password, db_name, target, port, socket=None):
     if socket is not None:
         cmd += " --socket=%s" % pipes.quote(socket)
     else:
-        cmd += " --host=%s --port=%s" % (pipes.quote(host), pipes.quote(port))
+        cmd += " --host=%s --port=%i" % (pipes.quote(host), port)
     cmd += " -D %s" % pipes.quote(db_name)
     if os.path.splitext(target)[-1] == '.gz':
         gunzip_path = module.get_bin_path('gunzip')
@@ -266,7 +266,7 @@ def main():
             login_user=dict(default=None),
             login_password=dict(default=None),
             login_host=dict(default="localhost"),
-            login_port=dict(default="3306"),
+            login_port=dict(default=3306, type='int'),
             login_unix_socket=dict(default=None),
             name=dict(required=True, aliases=['db']),
             encoding=dict(default=""),
@@ -285,6 +285,9 @@ def main():
     state = module.params["state"]
     target = module.params["target"]
     socket = module.params["login_unix_socket"]
+    login_port = module.params["login_port"]
+    if login_port < 0 or login_port > 65535:
+        module.fail_json(msg="login_port must be a valid unix port number (0-65535)")
 
     # make sure the target path is expanded for ~ and $HOME
     if target is not None:
@@ -322,10 +325,10 @@ def main():
             except OSError:
                 module.fail_json(msg="%s, does not exist, unable to connect" % socket)
             db_connection = MySQLdb.connect(host=module.params["login_host"], unix_socket=socket, user=login_user, passwd=login_password, db=connect_to_db)
-        elif module.params["login_port"] != "3306" and module.params["login_host"] == "localhost":
+        elif login_port != 3306 and module.params["login_host"] == "localhost":
             module.fail_json(msg="login_host is required when login_port is defined, login_host cannot be localhost when login_port is defined")
         else:
-            db_connection = MySQLdb.connect(host=module.params["login_host"], port=int(module.params["login_port"]), user=login_user, passwd=login_password, db=connect_to_db)
+            db_connection = MySQLdb.connect(host=module.params["login_host"], port=login_port, user=login_user, passwd=login_password, db=connect_to_db)
         cursor = db_connection.cursor()
     except Exception, e:
         if "Unknown database" in str(e):
@@ -344,7 +347,7 @@ def main():
         elif state == "dump":
             rc, stdout, stderr = db_dump(module, login_host, login_user, 
                                         login_password, db, target, 
-                                        port=module.params['login_port'],
+                                        port=login_port,
                                         socket=module.params['login_unix_socket'])
             if rc != 0:
                 module.fail_json(msg="%s" % stderr)
@@ -353,7 +356,7 @@ def main():
         elif state == "import":
             rc, stdout, stderr = db_import(module, login_host, login_user, 
                                         login_password, db, target, 
-                                        port=module.params['login_port'],
+                                        port=login_port,
                                         socket=module.params['login_unix_socket'])
             if rc != 0:
                 module.fail_json(msg="%s" % stderr)
diff --git a/database/mysql/mysql_user.py b/database/mysql/mysql_user.py
--- a/database/mysql/mysql_user.py
+++ b/database/mysql/mysql_user.py
@@ -424,7 +424,7 @@ def connect(module, login_user, login_password):
     if module.params["login_unix_socket"]:
         db_connection = MySQLdb.connect(host=module.params["login_host"], unix_socket=module.params["login_unix_socket"], user=login_user, passwd=login_password, db="mysql")
     else:
-        db_connection = MySQLdb.connect(host=module.params["login_host"], port=int(module.params["login_port"]), user=login_user, passwd=login_password, db="mysql")
+        db_connection = MySQLdb.connect(host=module.params["login_host"], port=module.params["login_port"], user=login_user, passwd=login_password, db="mysql")
     return db_connection.cursor()
 
 # ===========================================
@@ -437,7 +437,7 @@ def main():
             login_user=dict(default=None),
             login_password=dict(default=None),
             login_host=dict(default="localhost"),
-            login_port=dict(default="3306"),
+            login_port=dict(default=3306, type='int'),
             login_unix_socket=dict(default=None),
             user=dict(required=True, aliases=['name']),
             password=dict(default=None),


Test patch: