Repository: ansible-collections/community.vmware

Issue:
community.vmware.vmware_portgroup_info Failed if vCenter modify PortGroup
<!--- 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
<!--- Explain the problem briefly below -->
When a portgroup is created from a vCenter and configuration of this portgroup is modified like the vlan ID
The Ansible module failed

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible [core 2.13.5]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.8/dist-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0]
  jinja version = 3.1.2
  libyaml = True
```

##### COLLECTION VERSION
<!--- Paste verbatim output from "ansible-galaxy collection list <namespace>.<collection>"  between the quotes
for example: ansible-galaxy collection list community.general
-->
```paste below
# /root/.ansible/collections/ansible_collections
Collection       Version
---------------- -------
community.vmware 3.1.0
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
HOST_KEY_CHECKING(/etc/ansible/ansible.cfg) = False
```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
vCenter 7.0U3
ESXi 7.0U3

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
Create portgroup from a vCenter and modify the configuration of this portgroup like the vlan ID from the vCenter

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: Gather portgroup info about ESXi Host system
  community.vmware.vmware_portgroup_info:
    hostname: "vcenter"
    username: "username"
    password: "password"
    esxi_hostname: "esxi"
    policies: true
  delegate_to: localhost
```

If you modify the Portgroup directly from the ESXi its work
<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
Its return the information of the Porgroup

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes -->
```
TASK [ HIDEN Gather portgroup info about ESXi Host system] ***************************************************************************************************************************************************************************************************
task path: HIDEN/tasks/main.yml:37
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root
<localhost> EXEC /bin/sh -c 'echo ~root && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081 `" && echo ansible-tmp-1668593533.403802-7031-78492973480081="` echo /root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081 `" ) && sleep 0'
Using module file /root/.ansible/collections/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py
<localhost> PUT /root/.ansible/tmp/ansible-local-7025mskd63mg/tmpi9m0a6t8 TO /root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py
<localhost> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/ /root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py", line 107, in <module>
    _ansiballz_main()
  File "/root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py", line 99, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py", line 47, in invoke_module
    runpy.run_module(mod_name='ansible_collections.community.vmware.plugins.modules.vmware_portgroup_info', init_globals=dict(_module_fqn='ansible_collections.community.vmware.plugins.modules.vmware_portgroup_info', _modlib_path=modlib_path),
  File "/usr/lib/python3.8/runpy.py", line 207, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "/usr/lib/python3.8/runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/tmp/ansible_community.vmware.vmware_portgroup_info_payload_pgprffwr/ansible_community.vmware.vmware_portgroup_info_payload.zip/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py", line 217, in <module>
  File "/tmp/ansible_community.vmware.vmware_portgroup_info_payload_pgprffwr/ansible_community.vmware.vmware_portgroup_info_payload.zip/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py", line 213, in main
  File "/tmp/ansible_community.vmware.vmware_portgroup_info_payload_pgprffwr/ansible_community.vmware.vmware_portgroup_info_payload.zip/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py", line 190, in gather_host_portgroup_info
  File "/tmp/ansible_community.vmware.vmware_portgroup_info_payload_pgprffwr/ansible_community.vmware.vmware_portgroup_info_payload.zip/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py", line 168, in normalize_pg_info
AttributeError: 'NoneType' object has no attribute 'checkBeacon'
fatal: [esxi -> localhost]: FAILED! => {
    "changed": false,
    "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py\", line 107, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py\", line 99, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1668593533.403802-7031-78492973480081/AnsiballZ_vmware_portgroup_info.py\", line 47, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.community.vmware.plugins.modules.vmware_portgroup_info', init_globals=dict(_module_fqn='ansible_collections.community.vmware.plugins.modules.vmware_portgroup_info', _modlib_path=modlib_path),\n  File \"/usr/lib/python3.8/runpy.py\", line 207, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib/python3.8/runpy.py\", line 97, in _run_module_code\n    _run_code(code, mod_globals, init_globals,\n  File \"/usr/lib/python3.8/runpy.py\", line 87, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_community.vmware.vmware_portgroup_info_payload_pgprffwr/ansible_community.vmware.vmware_portgroup_info_payload.zip/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py\", line 217, in <module>\n  File \"/tmp/ansible_community.vmware.vmware_portgroup_info_payload_pgprffwr/ansible_community.vmware.vmware_portgroup_info_payload.zip/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py\", line 213, in main\n  File \"/tmp/ansible_community.vmware.vmware_portgroup_info_payload_pgprffwr/ansible_community.vmware.vmware_portgroup_info_payload.zip/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py\", line 190, in gather_host_portgroup_info\n  File \"/tmp/ansible_community.vmware.vmware_portgroup_info_payload_pgprffwr/ansible_community.vmware.vmware_portgroup_info_payload.zip/ansible_collections/community/vmware/plugins/modules/vmware_portgroup_info.py\", line 168, in normalize_pg_info\nAttributeError: 'NoneType' object has no attribute 'checkBeacon'\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}
```


Discussion:
Files identified in the description:
None

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 --->
That's weird. A newly created portgroup doesn't have `spec.policy.nicTeaming` set, it's `None`. So this `if` branch isn't executed:

https://github.com/ansible-collections/community.vmware/blob/fe41bdced99a9bc0fb6588f52cafbebd00b7f175/plugins/modules/vmware_portgroup_info.py#L145-L146

But when I update then portgroup, it's set (it's a `pyVmomi.VmomiSupport.vim.host.NetworkPolicy.NicTeamingPolicy`), so this branch _is_ executed. But `failureCriteria` is empty / `None`.

I think the logic is broken here:

https://github.com/ansible-collections/community.vmware/blob/fe41bdced99a9bc0fb6588f52cafbebd00b7f175/plugins/modules/vmware_portgroup_info.py#L165-L171

Since `spec.policy.nicTeaming.failureCriteria` is `None`, the `else` branch is executed. And there, the code tries to access `spec.policy.nicTeaming.failureCriteria.checkBeacon` which is `None.checkBeacon`.

Result: `AttributeError: 'NoneType' object has no attribute 'checkBeacon'`

Resolving patch:
diff --git a/plugins/modules/vmware_portgroup_info.py b/plugins/modules/vmware_portgroup_info.py
--- a/plugins/modules/vmware_portgroup_info.py
+++ b/plugins/modules/vmware_portgroup_info.py
@@ -162,7 +162,7 @@ def normalize_pg_info(portgroup_obj, policy_info):
                 else:
                     pg_info_dict['failover_active'] = spec.policy.nicTeaming.nicOrder.activeNic
                     pg_info_dict['failover_standby'] = spec.policy.nicTeaming.nicOrder.standbyNic
-                if spec.policy.nicTeaming.failureCriteria and spec.policy.nicTeaming.failureCriteria.checkBeacon is None:
+                if spec.policy.nicTeaming.failureCriteria is None:
                     pg_info_dict['failure_detection'] = "No override"
                 else:
                     if spec.policy.nicTeaming.failureCriteria.checkBeacon:


Test patch: