Repository: ansible-collections/community.general

Issue:
community.general.imc_rest: Module fails with Python 3
### Summary

The imc_rest fails to send a request to the Cisco IMC API because of [line 321](https://github.com/ansible-collections/community.general/blob/7b02adc57ede6db92743e4e58a9c8040293e3faa/plugins/modules/remote_management/imc/imc_rest.py#L321)

It uses `itertools.izip_longest` which in Python 3 was changed to `itertools.zip_longest`, which returns an iterator just like the izip_longest in Python 2. If this line is changed to `return [merge(alpha, beta) for (alpha, beta) in itertools.zip_longest(one, two)]` the module will no longer fail.

Error message when executing `Ansible playbook: An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: module 'itertools' has no attribute 'izip_longest'`

Tested with Ansible 2.12.2 and Python 3.9.7


### Issue Type

Bug Report

### Component Name

community.general.imc_rest

### Ansible Version

```console (paste below)
$ ansible --version

ansible [core 2.12.2]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/opt/ansible/dev/modules', '/opt/ansible/lib/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /opt/ansible/dev/collections:/opt/ansible/collections:/opt/ansible/vsphere
  executable location = /usr/bin/ansible
  python version = 3.9.7 (default, Nov 24 2021, 21:15:59) [GCC 10.3.1 20211027]
  jinja version = 2.11.3
  libyaml = False
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general

# /usr/lib/python3.9/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 4.4.0  

```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed

CALLBACKS_ENABLED(/etc/ansible/ansible.cfg) = ['timer', 'profile_tasks', 'profile_roles']
COLLECTIONS_PATHS(/etc/ansible/ansible.cfg) = ['/opt/ansible/dev/collections', '/opt/ansible/collections', '/opt/ansible/vsphere']
DEFAULT_FILTER_PLUGIN_PATH(/etc/ansible/ansible.cfg) = ['/opt/ansible/dev/plugins/filter', '/opt/ansible/lib/filter_plugins']
DEFAULT_FORKS(/etc/ansible/ansible.cfg) = 8
DEFAULT_HASH_BEHAVIOUR(/etc/ansible/ansible.cfg) = merge
DEFAULT_JINJA2_NATIVE(/etc/ansible/ansible.cfg) = True
DEFAULT_LOCAL_TMP(/etc/ansible/ansible.cfg) = /opt/ansible/.tmp/ansible-local-7185whknkirc
DEFAULT_LOOKUP_PLUGIN_PATH(/etc/ansible/ansible.cfg) = ['/opt/ansible/dev/plugins/lookup', '/opt/ansible/lib/plugins/lookup']
DEFAULT_MODULE_PATH(/etc/ansible/ansible.cfg) = ['/opt/ansible/dev/modules', '/opt/ansible/lib/modules']
DEFAULT_MODULE_UTILS_PATH(/etc/ansible/ansible.cfg) = ['/opt/ansible/dev/module_utils', '/opt/ansible/lib/module_utils']
DEFAULT_STDOUT_CALLBACK(/etc/ansible/ansible.cfg) = unixy
HOST_KEY_CHECKING(/etc/ansible/ansible.cfg) = False
INVENTORY_ENABLED(/etc/ansible/ansible.cfg) = ['script']
INVENTORY_UNPARSED_IS_FAILED(/etc/ansible/ansible.cfg) = True
RETRY_FILES_ENABLED(/etc/ansible/ansible.cfg) = False
SHOW_CUSTOM_STATS(/etc/ansible/ansible.cfg) = True
TRANSFORM_INVALID_GROUP_CHARS(/etc/ansible/ansible.cfg) = always

```


### OS / Environment

Alpine Linux v3.15
In a Docker container

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)

- name: Enable PXE boot and power-cycle server
  community.general.imc_rest:
    hostname: '{{ imc_hostname }}'
    username: '{{ imc_username }}'
    password: '{{ imc_password }}'
    validate_certs: no
    content: |
      <!-- Configure PXE boot -->
      <configConfMo><inConfig>
        <lsbootLan dn="sys/rack-unit-1/boot-policy/lan-read-only" access="read-only" order="1" prot="pxe" type="lan"/>
      </inConfig></configConfMo>
      <!-- Power cycle server -->
      <configConfMo><inConfig>
        <computeRackUnit dn="sys/rack-unit-1" adminPower="cycle-immediate"/>
      </inConfig></configConfMo>
  delegate_to: localhost

```

### Expected Results

I expected the request to be sent to the Cisco IMC API, which in turn would enable PXE booting on the server.

### Actual Results

```console (paste below)

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AttributeError: module 'itertools' has no attribute 'izip_longest'
fatal: [ cisco_hostfqdn.example -> localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1644920082.7706497-7156-167138396534203/AnsiballZ_imc_rest.py\", line 107, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1644920082.7706497-7156-167138396534203/AnsiballZ_imc_rest.py\", line 99, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1644920082.7706497-7156-167138396534203/AnsiballZ_imc_rest.py\", line 47, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.community.general.plugins.modules.imc_rest', init_globals=dict(_module_fqn='ansible_collections.community.general.plugins.modules.imc_rest', _modlib_path=modlib_path),\n  File \"/usr/lib/python3.9/runpy.py\", line 210, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib/python3.9/runpy.py\", line 97, in _run_module_code\n    _run_code(code, mod_globals, init_globals,\n  File \"/usr/lib/python3.9/runpy.py\", line 87, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_community.general.imc_rest_payload_0_dki2fb/ansible_community.general.imc_rest_payload.zip/ansible_collections/community/general/plugins/modules/imc_rest.py\", line 434, in <module>\n  File \"/tmp/ansible_community.general.imc_rest_payload_0_dki2fb/ansible_community.general.imc_rest_payload.zip/ansible_collections/community/general/plugins/modules/imc_rest.py\", line 416, in main\n  File \"/tmp/ansible_community.general.imc_rest_payload_0_dki2fb/ansible_community.general.imc_rest_payload.zip/ansible_collections/community/general/plugins/modules/imc_rest.py\", line 317, in merge\n  File \"/tmp/ansible_community.general.imc_rest_payload_0_dki2fb/ansible_community.general.imc_rest_payload.zip/ansible_collections/community/general/plugins/modules/imc_rest.py\", line 317, in <genexpr>\n  File \"/tmp/ansible_community.general.imc_rest_payload_0_dki2fb/ansible_community.general.imc_rest_payload.zip/ansible_collections/community/general/plugins/modules/imc_rest.py\", line 317, in merge\n  File \"/tmp/ansible_community.general.imc_rest_payload_0_dki2fb/ansible_community.general.imc_rest_payload.zip/ansible_collections/community/general/plugins/modules/imc_rest.py\", line 317, in <genexpr>\n  File \"/tmp/ansible_community.general.imc_rest_payload_0_dki2fb/ansible_community.general.imc_rest_payload.zip/ansible_collections/community/general/plugins/modules/imc_rest.py\", line 321, in merge\nAttributeError: module 'itertools' has no attribute 'izip_longest'\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}


```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/remote_management/imc/imc_rest.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/remote_management/imc/imc_rest.py)

If these files are incorrect, 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/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @dagwieers
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
`itertools.zip_longest` is available in python 3 but unfortunately in python 2 there's only `itertools.izip_longest`. So simply changing it to `itertools.zip_longest` would break it for python 2
It's probably better to use `from ansible.module_utils.six.moves import zip_longest`, that works with both Python 2 and 3.

Resolving patch:
diff --git a/plugins/modules/remote_management/imc/imc_rest.py b/plugins/modules/remote_management/imc/imc_rest.py
--- a/plugins/modules/remote_management/imc/imc_rest.py
+++ b/plugins/modules/remote_management/imc/imc_rest.py
@@ -261,7 +261,6 @@
 '''
 
 import datetime
-import itertools
 import os
 import traceback
 from functools import partial
@@ -283,6 +282,7 @@
     HAS_XMLJSON_COBRA = False
 
 from ansible.module_utils.basic import AnsibleModule, missing_required_lib
+from ansible.module_utils.six.moves import zip_longest
 from ansible.module_utils.urls import fetch_url
 
 
@@ -318,7 +318,7 @@ def merge(one, two):
         return copy
 
     elif isinstance(one, list) and isinstance(two, list):
-        return [merge(alpha, beta) for (alpha, beta) in itertools.izip_longest(one, two)]
+        return [merge(alpha, beta) for (alpha, beta) in zip_longest(one, two)]
 
     return one if two is None else two
 


Test patch: