Repository: ansible-collections/community.general

Issue:
Fix omapi_host on python3
<!--- 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
omapi_host needs to use byte strings on python3 otherwise getting "TypeError: a bytes-like object is required, not 'str'" errors

Migrated from [#67559](https://github.com/ansible/ansible/issues/67559)

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
omapi_host module

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
# ansible --version
ansible 2.9.4
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.6.8 (default, Oct 11 2019, 15:04:54) [GCC 8.3.1 20190507 (Red Hat 8.3.1-4)]
```
##### STEPS TO REPRODUCE
Create or remote a DHCP reservation using omapi_host module

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
Completed without errors

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes -->
```paste below
"module_stdout": "Traceback (most recent call last):\r
  File \"/root/.ansible/tmp/ansible-tmp-1582083593.0190487-137096835301943/AnsiballZ_omapi_host.py\", line 102, in <module>\r
    _ansiballz_main()\r
  File \"/root/.ansible/tmp/ansible-tmp-1582083593.0190487-137096835301943/AnsiballZ_omapi_host.py\", line 94, in _ansiballz_main\r
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r
  File \"/root/.ansible/tmp/ansible-tmp-1582083593.0190487-137096835301943/AnsiballZ_omapi_host.py\", line 40, in invoke_module\r
    runpy.run_module(mod_name='ansible.modules.net_tools.omapi_host', init_globals=None, run_name='__main__', alter_sys=True)\r
  File \"/usr/lib64/python3.6/runpy.py\", line 205, in run_module\r
    return _run_module_code(code, init_globals, run_name, mod_spec)\r
  File \"/usr/lib64/python3.6/runpy.py\", line 96, in _run_module_code\r
    mod_name, mod_spec, pkg_name, script_name)\r
  File \"/usr/lib64/python3.6/runpy.py\", line 85, in _run_code\r
    exec(code, run_globals)\r
  File \"/tmp/ansible_omapi_host_payload_4n9lnp9o/ansible_omapi_host_payload.zip/ansible/modules/net_tools/omapi_host.py\", line 315, in <module>\r
  File \"/tmp/ansible_omapi_host_payload_4n9lnp9o/ansible_omapi_host_payload.zip/ansible/modules/net_tools/omapi_host.py\", line 304, in main\r
  File \"/tmp/ansible_omapi_host_payload_4n9lnp9o/ansible_omapi_host_payload.zip/ansible/modules/net_tools/omapi_host.py\", line 154, in __init__\r
  File \"/tmp/ansible_omapi_host_payload_4n9lnp9o/ansible_omapi_host_payload.zip/ansible/modules/net_tools/omapi_host.py\", line 159, in connect\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 980, in __init__\r
    self.initialize_authenticator(newauth)\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 1064, in initialize_authenticator\r
    response = self.query_server(msg)\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 1053, in query_server\r
    self.send_message(message)\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 1044, in send_message\r
    self.protocol.send_message(message, sign)\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 944, in send_message\r
    message.sign(self.authenticators[self.defauth])\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 466, in sign\r
    self.signature = authenticator.sign(self.as_string(forsigning=True))\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 457, in as_string\r
    self.serialize(ret, forsigning)\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 443, in serialize\r
    outbuffer.add_bindict(self.obj)\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 212, in add_bindict\r
    self.add_net16string(key).add_net32string(value)\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 183, in add_net32string\r
    return self.add_net32int(len(string)).add(string)\r
  File \"/root/.local/lib/python3.6/site-packages/pypureomapi.py\", line 145, in add\r
    self.buff.write(data)\r
TypeError: a bytes-like object is required, not 'str'\r"

```



Discussion:
Files identified in the description:
* [`plugins/modules/net_tools/omapi_host.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/net_tools/omapi_host.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 @nerzhul
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/net_tools/omapi_host.py b/plugins/modules/net_tools/omapi_host.py
--- a/plugins/modules/net_tools/omapi_host.py
+++ b/plugins/modules/net_tools/omapi_host.py
@@ -150,7 +150,7 @@ def __init__(self, module):
 
     def connect(self):
         try:
-            self.omapi = Omapi(self.module.params['host'], self.module.params['port'], self.module.params['key_name'],
+            self.omapi = Omapi(self.module.params['host'], self.module.params['port'], to_bytes(self.module.params['key_name']),
                                self.module.params['key'])
         except binascii.Error:
             self.module.fail_json(msg="Unable to open OMAPI connection. 'key' is not a valid base64 key.")
@@ -173,13 +173,13 @@ def get_host(self, macaddr):
     def unpack_facts(obj):
         result = dict(obj)
         if 'hardware-address' in result:
-            result['hardware-address'] = unpack_mac(result['hardware-address'])
+            result['hardware-address'] = to_native(unpack_mac(result[to_bytes('hardware-address')]))
 
         if 'ip-address' in result:
-            result['ip-address'] = unpack_ip(result['ip-address'])
+            result['ip-address'] = to_native(unpack_ip(result[to_bytes('ip-address')]))
 
         if 'hardware-type' in result:
-            result['hardware-type'] = struct.unpack("!I", result['hardware-type'])
+            result['hardware-type'] = struct.unpack("!I", result[to_bytes('hardware-type')])
 
         return result
 
@@ -192,11 +192,11 @@ def setup_host(self):
         # If host was not found using macaddr, add create message
         if host_response is None:
             msg = OmapiMessage.open(to_bytes('host', errors='surrogate_or_strict'))
-            msg.message.append(('create', struct.pack('!I', 1)))
-            msg.message.append(('exclusive', struct.pack('!I', 1)))
-            msg.obj.append(('hardware-address', pack_mac(self.module.params['macaddr'])))
-            msg.obj.append(('hardware-type', struct.pack('!I', 1)))
-            msg.obj.append(('name', self.module.params['hostname']))
+            msg.message.append((to_bytes('create'), struct.pack('!I', 1)))
+            msg.message.append((to_bytes('exclusive'), struct.pack('!I', 1)))
+            msg.obj.append((to_bytes('hardware-address'), pack_mac(self.module.params['macaddr'])))
+            msg.obj.append((to_bytes('hardware-type'), struct.pack('!I', 1)))
+            msg.obj.append((to_bytes('name'), to_bytes(self.module.params['hostname'])))
             if self.module.params['ip'] is not None:
                 msg.obj.append((to_bytes("ip-address", errors='surrogate_or_strict'), pack_ip(self.module.params['ip'])))
 
@@ -212,7 +212,7 @@ def setup_host(self):
                 self.module.fail_json(msg="Invalid statements found: %s" % to_native(e))
 
             if len(stmt_join) > 0:
-                msg.obj.append(('statements', stmt_join))
+                msg.obj.append((to_bytes('statements'), to_bytes(stmt_join)))
 
             try:
                 response = self.omapi.query_server(msg)


Test patch: