Repository: ansible/ansible

Issue:
IRC module not working properly with Python3 and SSL enabled
##### SUMMARY
irc module fails
"unable to send to IRC: a bytes-like object is required, not 'str'

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
module IRC
/usr/lib/python3.6/site-packages/ansible/modules/notification/irc.py

##### ANSIBLE VERSION
```
# ansible-3 --version
ansible-3 2.6.0
  config file = /home/brq/mzink/Projects/RH/rhgs/ansible/ansible.cfg
  configured module search path = ['/home/brq/mzink/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible-3
  python version = 3.6.5 (default, Mar 29 2018, 18:20:46) [GCC 8.0.1 20180317 (Red Hat 8.0.1-0.19)]
```

##### CONFIGURATION
```
ANSIBLE_PIPELINING(./ansible.cfg) = True
ANSIBLE_SSH_ARGS(./ansible.cfg) = -o ControlPersist=30m -q
ANSIBLE_SSH_CONTROL_PATH(./ansible.cfg) = ~/.ssh/ansible-mux-%%r@%%h:%%p
DEFAULT_FORKS(./ansible.cfg) = 24
DEFAULT_REMOTE_USER(./ansible.cfg) = root
DEFAULT_SCP_IF_SSH(./ansible.cfg) = True
DEFAULT_TIMEOUT(./ansible.cfg) = 30
DISPLAY_SKIPPED_HOSTS(./ansible.cfg) = False
HOST_KEY_CHECKING(./ansible.cfg) = False
INVENTORY_IGNORE_EXTS(./ansible.cfg) = ['.yaml', 'data', 'volumes', 'nodes']
RETRY_FILES_ENABLED(./ansible.cfg) = False
RETRY_FILES_SAVE_PATH(./ansible.cfg) = /home/brq/mzink/.ansible/retry-files
```

##### OS / ENVIRONMENT
```
> uname -a
Linux windmill 4.17.3-200.fc28.x86_64 #1 SMP Tue Jun 26 14:17:07 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
> cat /etc/fedora-release 
Fedora release 28 (Twenty Eight)

```

##### STEPS TO REPRODUCE
```json
    "invocation": {
        "module_args": {
            "channel": "#chan",
            "color": "teal",
            "key": null,
            "msg": "some msg goes here",
            "nick": "ansibot",
            "nick_to": null,
            "part": false,
            "passwd": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "port": 6697,
            "server": "irc.server.com",
            "style": "none",
            "timeout": 30,
            "topic": null,
            "use_ssl": true
        }
    }
```

##### EXPECTED RESULTS
Working module

##### ACTUAL RESULTS
```
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_rg5fkxk0/ansible_module_irc.py", line 287, in main
    send_msg(msg, server, port, channel, nick_to, key, topic, nick, color, passwd, timeout, use_ssl, part, style)
  File "/tmp/ansible_rg5fkxk0/ansible_module_irc.py", line 189, in send_msg
    irc.send('PASS %s\r\n' % passwd)
  File "/usr/lib64/python3.6/ssl.py", line 897, in send
    return self._sslobj.write(data)
  File "/usr/lib64/python3.6/ssl.py", line 598, in write
    return self._sslobj.write(data)
TypeError: a bytes-like object is required, not 'str'
```

Works fine with python 2.7.


Discussion:
Files identified in the description:
* [lib/ansible/modules/notification/irc.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/notification/irc.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 @jpmens @sivel
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/notification/irc.py b/lib/ansible/modules/notification/irc.py
--- a/lib/ansible/modules/notification/irc.py
+++ b/lib/ansible/modules/notification/irc.py
@@ -133,8 +133,8 @@
 import time
 import traceback
 
+from ansible.module_utils._text import to_native, to_bytes
 from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
 
 
 def send_msg(msg, server='localhost', port='6667', channel=None, nick_to=None, key=None, topic=None,
@@ -170,13 +170,13 @@ def send_msg(msg, server='localhost', port='6667', channel=None, nick_to=None, k
 
     try:
         styletext = stylechoices[style]
-    except:
+    except Exception:
         styletext = ""
 
     try:
         colornumber = colornumbers[color]
         colortext = "\x03" + colornumber
-    except:
+    except Exception:
         colortext = ""
 
     message = styletext + colortext + msg
@@ -185,14 +185,15 @@ def send_msg(msg, server='localhost', port='6667', channel=None, nick_to=None, k
     if use_ssl:
         irc = ssl.wrap_socket(irc)
     irc.connect((server, int(port)))
+
     if passwd:
-        irc.send('PASS %s\r\n' % passwd)
-    irc.send('NICK %s\r\n' % nick)
-    irc.send('USER %s %s %s :ansible IRC\r\n' % (nick, nick, nick))
+        irc.send(to_bytes('PASS %s\r\n' % passwd))
+    irc.send(to_bytes('NICK %s\r\n' % nick))
+    irc.send(to_bytes('USER %s %s %s :ansible IRC\r\n' % (nick, nick, nick)))
     motd = ''
     start = time.time()
     while 1:
-        motd += irc.recv(1024)
+        motd += to_native(irc.recv(1024))
         # The server might send back a shorter nick than we specified (due to NICKLEN),
         #  so grab that and use it from now on (assuming we find the 00[1-4] response).
         match = re.search(r'^:\S+ 00[1-4] (?P<nick>\S+) :', motd, flags=re.M)
@@ -204,14 +205,14 @@ def send_msg(msg, server='localhost', port='6667', channel=None, nick_to=None, k
         time.sleep(0.5)
 
     if key:
-        irc.send('JOIN %s %s\r\n' % (channel, key))
+        irc.send(to_bytes('JOIN %s %s\r\n' % (channel, key)))
     else:
-        irc.send('JOIN %s\r\n' % channel)
+        irc.send(to_bytes('JOIN %s\r\n' % channel))
 
     join = ''
     start = time.time()
     while 1:
-        join += irc.recv(1024)
+        join += to_native(irc.recv(1024))
         if re.search(r'^:\S+ 366 %s %s :' % (nick, channel), join, flags=re.M):
             break
         elif time.time() - start > timeout:
@@ -219,18 +220,18 @@ def send_msg(msg, server='localhost', port='6667', channel=None, nick_to=None, k
         time.sleep(0.5)
 
     if topic is not None:
-        irc.send('TOPIC %s :%s\r\n' % (channel, topic))
+        irc.send(to_bytes('TOPIC %s :%s\r\n' % (channel, topic)))
         time.sleep(1)
 
     if nick_to:
         for nick in nick_to:
-            irc.send('PRIVMSG %s :%s\r\n' % (nick, message))
+            irc.send(to_bytes('PRIVMSG %s :%s\r\n' % (nick, message)))
     if channel:
-        irc.send('PRIVMSG %s :%s\r\n' % (channel, message))
+        irc.send(to_bytes('PRIVMSG %s :%s\r\n' % (channel, message)))
     time.sleep(1)
     if part:
-        irc.send('PART %s\r\n' % channel)
-        irc.send('QUIT\r\n')
+        irc.send(to_bytes('PART %s\r\n' % channel))
+        irc.send(to_bytes('QUIT\r\n'))
         time.sleep(1)
     irc.close()
 


Test patch: