Repository: ansible-collections/community.vmware

Issue:
Add a new module for retrieving supported GOS list and recommended VM config
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Describe the new feature/improvement briefly below -->
A new module is used for below 2 cases:
1. Get the supported guest OS ID list on ESXi host for the most recent hardware version supported.
2. Get the recommended VM configurations for specified guest ID, e.g., CPU number, memory size, boot disk controller type, firmware, secure boot supported, etc.

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
vmware_vm_config_option

##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
community.vmware.vmware_vm_config_option:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    esxi_hostname: "{{ esxi_hostname }}"
    guest_id: "windows9_64Guest"
```

<!--- HINT: You can also paste gist.github.com links for larger files -->



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 --->

Resolving patch:
diff --git a/plugins/module_utils/vm_device_helper.py b/plugins/module_utils/vm_device_helper.py
--- a/plugins/module_utils/vm_device_helper.py
+++ b/plugins/module_utils/vm_device_helper.py
@@ -35,13 +35,21 @@ def __init__(self, module):
             'lsilogic': vim.vm.device.VirtualLsiLogicController,
             'paravirtual': vim.vm.device.ParaVirtualSCSIController,
             'buslogic': vim.vm.device.VirtualBusLogicController,
-            'lsilogicsas': vim.vm.device.VirtualLsiLogicSASController,
+            'lsilogicsas': vim.vm.device.VirtualLsiLogicSASController
         }
         self.sata_device_type = vim.vm.device.VirtualAHCIController
         self.nvme_device_type = vim.vm.device.VirtualNVMEController
         self.usb_device_type = {
             'usb2': vim.vm.device.VirtualUSBController,
-            'usb3': vim.vm.device.VirtualUSBXHCIController,
+            'usb3': vim.vm.device.VirtualUSBXHCIController
+        }
+        self.nic_device_type = {
+            'pcnet32': vim.vm.device.VirtualPCNet32,
+            'vmxnet2': vim.vm.device.VirtualVmxnet2,
+            'vmxnet3': vim.vm.device.VirtualVmxnet3,
+            'e1000': vim.vm.device.VirtualE1000,
+            'e1000e': vim.vm.device.VirtualE1000e,
+            'sriov': vim.vm.device.VirtualSriovEthernetCard
         }
 
     def create_scsi_controller(self, scsi_type, bus_number, bus_sharing='noSharing'):
@@ -49,7 +57,7 @@ def create_scsi_controller(self, scsi_type, bus_number, bus_sharing='noSharing')
         Create SCSI Controller with given SCSI Type and SCSI Bus Number
         Args:
             scsi_type: Type of SCSI
-            scsi_bus_number: SCSI Bus number to be assigned
+            bus_number: SCSI Bus number to be assigned
             bus_sharing: noSharing, virtualSharing, physicalSharing
 
         Returns: Virtual device spec for SCSI Controller
@@ -271,23 +279,10 @@ def create_hard_disk(self, disk_ctl, disk_index=None):
 
         return diskspec
 
-    def get_device(self, device_type, name):
-        nic_dict = dict(pcnet32=vim.vm.device.VirtualPCNet32(),
-                        vmxnet2=vim.vm.device.VirtualVmxnet2(),
-                        vmxnet3=vim.vm.device.VirtualVmxnet3(),
-                        e1000=vim.vm.device.VirtualE1000(),
-                        e1000e=vim.vm.device.VirtualE1000e(),
-                        sriov=vim.vm.device.VirtualSriovEthernetCard(),
-                        )
-        if device_type in nic_dict:
-            return nic_dict[device_type]
-        else:
-            self.module.fail_json(msg='Invalid device_type "%s"'
-                                      ' for network "%s"' % (device_type, name))
-
     def create_nic(self, device_type, device_label, device_infos):
         nic = vim.vm.device.VirtualDeviceSpec()
-        nic.device = self.get_device(device_type, device_infos['name'])
+        nic_device = self.nic_device_type.get(device_type)
+        nic.device = nic_device()
         nic.device.key = -randint(25000, 29999)
         nic.device.wakeOnLanEnabled = bool(device_infos.get('wake_on_lan', True))
         nic.device.deviceInfo = vim.Description()
diff --git a/plugins/modules/vmware_guest.py b/plugins/modules/vmware_guest.py
--- a/plugins/modules/vmware_guest.py
+++ b/plugins/modules/vmware_guest.py
@@ -1705,12 +1705,10 @@ def get_vm_network_interfaces(self, vm=None):
         if vm is None:
             return device_list
 
-        nw_device_types = (vim.vm.device.VirtualPCNet32, vim.vm.device.VirtualVmxnet2,
-                           vim.vm.device.VirtualVmxnet3, vim.vm.device.VirtualE1000,
-                           vim.vm.device.VirtualE1000e, vim.vm.device.VirtualSriovEthernetCard)
         for device in vm.config.hardware.device:
-            if isinstance(device, nw_device_types):
-                device_list.append(device)
+            for device_type in self.device_helper.nic_device_type.values():
+                if isinstance(device, device_type):
+                    device_list.append(device)
 
         return device_list
 
@@ -1774,12 +1772,10 @@ def sanitize_network_params(self):
                     self.module.fail_json(msg="'ip' is required if 'netmask' is"
                                               " specified under VM network list.")
 
-            validate_device_types = ['pcnet32', 'vmxnet2', 'vmxnet3', 'e1000', 'e1000e', 'sriov']
-            if 'device_type' in network and network['device_type'] not in validate_device_types:
-                self.module.fail_json(msg="Device type specified '%s' is not valid."
-                                          " Please specify correct device"
-                                          " type from ['%s']." % (network['device_type'],
-                                                                  "', '".join(validate_device_types)))
+            if 'device_type' in network and network['device_type'] not in self.device_helper.nic_device_type.keys():
+                self.module.fail_json(msg="Device type specified '%s' is not valid. Please specify correct device type"
+                                          " from ['%s']." % (network['device_type'],
+                                                             "', '".join(self.device_helper.nic_device_type.keys())))
 
             if 'mac' in network and not is_mac(network['mac']):
                 self.module.fail_json(msg="Device MAC address '%s' is invalid."
@@ -1834,11 +1830,11 @@ def configure_network(self, vm_obj):
                     nic.device.deviceInfo.summary = network_name
                     nic_change_detected = True
                 if 'device_type' in network_devices[key]:
-                    device = self.device_helper.get_device(network_devices[key]['device_type'], network_name)
-                    device_class = type(device)
-                    if not isinstance(nic.device, device_class):
-                        self.module.fail_json(msg="Changing the device type is not possible when interface is already present. "
-                                                  "The failing device type is %s" % network_devices[key]['device_type'])
+                    device = self.device_helper.nic_device_type.get(network_devices[key]['device_type'])
+                    if not isinstance(nic.device, device):
+                        self.module.fail_json(msg="Changing the device type is not possible when interface is already"
+                                                  " present. The failing device type is %s"
+                                                  % network_devices[key]['device_type'])
                 # Changing mac address has no effect when editing interface
                 if 'mac' in network_devices[key] and nic.device.macAddress != current_net_devices[key].macAddress:
                     self.module.fail_json(msg="Changing MAC address has not effect when interface is already present. "
diff --git a/plugins/modules/vmware_vm_config_option.py b/plugins/modules/vmware_vm_config_option.py
new file mode 100644
--- /dev/null
+++ b/plugins/modules/vmware_vm_config_option.py
@@ -0,0 +1,302 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2021, Ansible Project
+# Copyright: (c) 2021, VMware, Inc. All Rights Reserved.
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+---
+module: vmware_vm_config_option
+short_description: Return supported guest ID list and VM recommended config option for specific guest OS
+description: >
+   This module is used for getting the hardware versions supported for creation, the guest ID list supported by ESXi
+   host for the most recent virtual hardware supported or specified hardware version, the VM recommended config options
+   for specified guest OS ID.
+author:
+- Diane Wang (@Tomorrow9) <dianew@vmware.com>
+version_added: '1.15.0'
+notes:
+- Tested on vSphere 6.5
+- Tested on vSphere 6.7
+- Known issue on vSphere 7.0 (https://github.com/vmware/pyvmomi/issues/915)
+requirements:
+- python >= 2.6
+- PyVmomi
+- System.View privilege
+options:
+  datacenter:
+    description:
+    - The datacenter name used to get specified cluster or host.
+    - This parameter is case sensitive.
+    default: ha-datacenter
+    type: str
+  cluster_name:
+    description:
+    - Name of the cluster.
+    - If C(esxi_hostname) is not given, this parameter is required.
+    type: str
+  esxi_hostname:
+    description:
+    - ESXi hostname.
+    - Obtain VM configure options on this ESXi host.
+    - If C(cluster_name) is not given, this parameter is required.
+    type: str
+  get_hardware_versions:
+    description:
+    - Return the list of VM hardware versions supported for creation and the default hardware version on the
+      specified entity.
+    type: bool
+    default: false
+  get_guest_os_ids:
+    description:
+    - Return the list of guest OS IDs supported on the specified entity.
+    - If C(hardware_version) is set, will return the corresponding guest OS ID list supported, or will return the
+      guest OS ID list for the default hardware version.
+    type: bool
+    default: false
+  get_config_options:
+    description:
+    - Return the dict of VM recommended config options for guest ID specified by C(guest_id) with hardware version
+      specified by C(hardware_version) or the default hardware version.
+    - When set to True, C(guest_id) must be set.
+    type: bool
+    default: false
+  guest_id:
+    description:
+    - The guest OS ID from the returned list when C(get_guest_os_ids) is set to C(True), e.g., 'rhel8_64Guest'.
+    - This parameter must be set when C(get_config_options) is set to C(True).
+    type: str
+  hardware_version:
+    description:
+    - The hardware version from the returned list when C(get_hardware_versions) is set to C(True), e.g., 'vmx-19'.
+    type: str
+extends_documentation_fragment:
+- community.vmware.vmware.documentation
+
+'''
+
+EXAMPLES = r'''
+- name: Get supported guest ID list on given ESXi host for with default hardware version
+  community.vmware.vmware_vm_config_option:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    esxi_hostname: "{{ esxi_hostname }}"
+    get_guest_os_ids: True
+  delegate_to: localhost
+
+- name: Get VM recommended config option for Windows 10 guest OS on given ESXi host
+  community.vmware.vmware_vm_config_option:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    esxi_hostname: "{{ esxi_hostname }}"
+    get_config_options: True
+    guest_id: "windows9_64Guest"
+  delegate_to: localhost
+'''
+
+RETURN = r'''
+instance:
+    description: metadata about the VM recommended configuration
+    returned: always
+    type: dict
+    sample: None
+'''
+
+HAS_PYVMOMI = False
+try:
+    from pyVmomi import vim, vmodl
+    HAS_PYVMOMI = True
+except ImportError:
+    pass
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils._text import to_native
+from ansible_collections.community.vmware.plugins.module_utils.vmware import find_obj, vmware_argument_spec, PyVmomi
+from ansible_collections.community.vmware.plugins.module_utils.vm_device_helper import PyVmomiDeviceHelper
+
+
+class VmConfigOption(PyVmomi):
+    def __init__(self, module):
+        super(VmConfigOption, self).__init__(module)
+        self.device_helper = PyVmomiDeviceHelper(self.module)
+        self.ctl_device_type = self.device_helper.scsi_device_type.copy()
+        self.ctl_device_type.update({'sata': self.device_helper.sata_device_type,
+                                     'nvme': self.device_helper.nvme_device_type}
+                                    )
+        self.ctl_device_type.update(self.device_helper.usb_device_type)
+        self.ctl_device_type.update(self.device_helper.nic_device_type)
+
+    def get_hardware_versions(self, env_browser):
+        support_create = []
+        default_config = ''
+        try:
+            desc = env_browser.QueryConfigOptionDescriptor()
+        except vmodl.RuntimeFault as runtime_fault:
+            self.module.fail_json(msg=to_native(runtime_fault.msg))
+        except Exception as generic_fault:
+            self.module.fail_json(msg="Failed to obtain VM config option descriptor due to fault: %s" % generic_fault)
+        if desc:
+            for option_desc in desc:
+                if option_desc.createSupported:
+                    support_create = support_create + [option_desc.key]
+                if option_desc.defaultConfigOption:
+                    default_config = option_desc.key
+
+        return support_create, default_config
+
+    def get_config_option_by_spec(self, env_browser, guest_id=None, host=None, key=''):
+        vm_config_option = None
+        if guest_id is None:
+            guest_id = []
+        config_query_spec = vim.EnvironmentBrowser.ConfigOptionQuerySpec(guestId=guest_id, host=host, key=key)
+        try:
+            vm_config_option = env_browser.QueryConfigOptionEx(spec=config_query_spec)
+        except vmodl.RuntimeFault as runtime_fault:
+            self.module.fail_json(msg=to_native(runtime_fault.msg))
+        except Exception as generic_fault:
+            self.module.fail_json(msg="Failed to obtain VM config options due to fault: %s" % generic_fault)
+
+        return vm_config_option
+
+    def get_config_option_recommended(self, guest_os_desc, hwv_version=''):
+        guest_os_option_dict = {}
+        if guest_os_desc and len(guest_os_desc) != 0:
+            default_disk_ctl = default_ethernet = default_cdrom_ctl = default_usb_ctl = ''
+            for name, type in self.ctl_device_type.items():
+                if type == guest_os_desc[0].recommendedDiskController:
+                    default_disk_ctl = name
+                if type == guest_os_desc[0].recommendedEthernetCard:
+                    default_ethernet = name
+                if type == guest_os_desc[0].recommendedCdromController:
+                    default_cdrom_ctl = name
+                if type == guest_os_desc[0].recommendedUSBController:
+                    default_usb_ctl = name
+            guest_os_option_dict = {
+                'Hardware version': hwv_version,
+                'Guest ID': guest_os_desc[0].id,
+                'Guest fullname': guest_os_desc[0].fullName,
+                'Default CPU cores per socket': guest_os_desc[0].numRecommendedCoresPerSocket,
+                'Default CPU socket': guest_os_desc[0].numRecommendedPhysicalSockets,
+                'Default memory in MB': guest_os_desc[0].recommendedMemMB,
+                'Default firmware': guest_os_desc[0].recommendedFirmware,
+                'Default secure boot': guest_os_desc[0].defaultSecureBoot,
+                'Support secure boot': guest_os_desc[0].supportsSecureBoot,
+                'Default disk controller': default_disk_ctl,
+                'Default disk size in MB': guest_os_desc[0].recommendedDiskSizeMB,
+                'Default network adapter': default_ethernet,
+                'Default CDROM controller': default_cdrom_ctl,
+                'Default USB controller': default_usb_ctl
+            }
+
+        return guest_os_option_dict
+
+    def get_guest_id_list(self, guest_os_desc):
+        gos_id_list = []
+        if guest_os_desc:
+            for gos_desc in guest_os_desc.guestOSDescriptor:
+                gos_id_list = gos_id_list + [gos_desc.id]
+
+        return gos_id_list
+
+    def get_config_option_for_guest(self):
+        results = {}
+        guest_id = []
+        host = None
+        datacenter_name = self.params.get('datacenter')
+        cluster_name = self.params.get('cluster_name')
+        esxi_host_name = self.params.get('esxi_hostname')
+        if self.params.get('guest_id'):
+            guest_id = [self.params.get('guest_id')]
+
+        if not self.params.get('get_hardware_versions') and not self.params.get('get_guest_os_ids') \
+                and not self.params.get('get_config_options'):
+            self.module.exit_json(msg="Please set at least one of these parameters 'get_hardware_versions',"
+                                      " 'get_guest_os_ids', 'get_config_options' to True to get the desired info.")
+        if self.params.get('get_config_options') and len(guest_id) == 0:
+            self.module.fail_json(msg="Please set 'guest_id' when 'get_config_options' is set to True,"
+                                      " to get the VM recommended config option for specific guest OS.")
+
+        # Get the datacenter object
+        datacenter = find_obj(self.content, [vim.Datacenter], datacenter_name)
+        if not datacenter:
+            self.module.fail_json(msg='Unable to find datacenter "%s"' % datacenter_name)
+        # Get the cluster object
+        if cluster_name:
+            cluster = find_obj(self.content, [vim.ComputeResource], cluster_name, folder=datacenter)
+            if not cluster:
+                self.module.fail_json(msg='Unable to find cluster "%s"' % cluster_name)
+        # If host is given, get the cluster object using the host
+        elif esxi_host_name:
+            host = find_obj(self.content, [vim.HostSystem], esxi_host_name, folder=datacenter)
+            if not host:
+                self.module.fail_json(msg='Unable to find host "%s"' % esxi_host_name)
+            cluster = host.parent
+        # Define the environment browser object the ComputeResource presents
+        env_browser = cluster.environmentBrowser
+        if env_browser is None:
+            self.module.fail_json(msg="The environmentBrowser of the ComputeResource is None, so can not get the"
+                                      " desired config option info, please check your vSphere environment.")
+        # Get supported hardware versions list
+        support_create_list, default_config = self.get_hardware_versions(env_browser=env_browser)
+        if self.params.get('get_hardware_versions'):
+            results.update({'Supported hardware versions': support_create_list,
+                            'Default hardware version': default_config})
+
+        if self.params.get('get_guest_os_ids') or self.params.get('get_config_options'):
+            # Get supported guest ID list
+            hardware_version = self.params.get('hardware_version', '')
+            if hardware_version and len(support_create_list) != 0 and hardware_version not in support_create_list:
+                self.module.fail_json(msg="Specified hardware version '%s' is not in the supported create list: %s"
+                                          % (hardware_version, support_create_list))
+            vm_config_option_all = self.get_config_option_by_spec(env_browser=env_browser, host=host,
+                                                                  key=hardware_version)
+            supported_gos_list = self.get_guest_id_list(guest_os_desc=vm_config_option_all)
+            if self.params.get('get_guest_os_ids'):
+                info_key = 'Supported guest IDs for %s' % vm_config_option_all.version
+                results.update({info_key: supported_gos_list})
+
+            if self.params.get('get_config_options') and len(guest_id) != 0:
+                if supported_gos_list and guest_id[0] not in supported_gos_list:
+                    self.module.fail_json(msg="Specified guest ID '%s' is not in the supported guest ID list: '%s'"
+                                              % (guest_id[0], supported_gos_list))
+                vm_config_option_guest = self.get_config_option_by_spec(env_browser=env_browser, host=host,
+                                                                        guest_id=guest_id, key=hardware_version)
+                guest_os_options = vm_config_option_guest.guestOSDescriptor
+                guest_os_option_dict = self.get_config_option_recommended(guest_os_desc=guest_os_options,
+                                                                          hwv_version=vm_config_option_guest.version)
+                results.update({'Recommended config options': guest_os_option_dict})
+
+        self.module.exit_json(changed=False, failed=False, instance=results)
+
+
+def main():
+    argument_spec = vmware_argument_spec()
+    argument_spec.update(
+        datacenter=dict(type='str', default='ha-datacenter'),
+        cluster_name=dict(type='str'),
+        esxi_hostname=dict(type='str'),
+        get_hardware_versions=dict(type='bool', default=False),
+        get_guest_os_ids=dict(type='bool', default=False),
+        get_config_options=dict(type='bool', default=False),
+        guest_id=dict(type='str'),
+        hardware_version=dict(type='str'),
+    )
+    module = AnsibleModule(
+        argument_spec=argument_spec,
+        supports_check_mode=True,
+        required_one_of=[
+            ['cluster_name', 'esxi_hostname'],
+        ]
+    )
+    vm_config_option_guest = VmConfigOption(module)
+    vm_config_option_guest.get_config_option_for_guest()
+
+
+if __name__ == "__main__":
+    main()


Test patch:
diff --git a/tests/integration/targets/vmware_vm_config_option/aliases b/tests/integration/targets/vmware_vm_config_option/aliases
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/vmware_vm_config_option/aliases
@@ -0,0 +1,4 @@
+cloud/vcenter
+needs/target/prepare_vmware_tests
+zuul/vmware/vcenter_1esxi
+zuul/vmware/govcsim
diff --git a/tests/integration/targets/vmware_vm_config_option/tasks/main.yml b/tests/integration/targets/vmware_vm_config_option/tasks/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/vmware_vm_config_option/tasks/main.yml
@@ -0,0 +1,117 @@
+# Test code for the vmware_vm_config_option module.
+# Copyright: (c) 2021, VMware, Inc. All Rights Reserved.
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- import_role:
+    name: prepare_vmware_tests
+  vars:
+    setup_attach_host: true
+
+- when: vcsim is not defined
+  block:
+    - name: get list of config option keys from ESXi host
+      community.vmware.vmware_vm_config_option:
+        validate_certs: false
+        hostname: "{{ vcenter_hostname }}"
+        username: "{{ vcenter_username }}"
+        password: "{{ vcenter_password }}"
+        datacenter: "{{ dc1 }}"
+        esxi_hostname: "{{ esxi_hosts[0] }}"
+        get_hardware_versions: true
+      register: config_option_keys_1
+
+    - debug:
+        var: config_option_keys_1
+
+    - name: get list of config option keys from cluster
+      community.vmware.vmware_vm_config_option:
+        validate_certs: false
+        hostname: "{{ vcenter_hostname }}"
+        username: "{{ vcenter_username }}"
+        password: "{{ vcenter_password }}"
+        datacenter: "{{ dc1 }}"
+        cluster_name: "{{ ccr1 }}"
+        get_hardware_versions: true
+      register: config_option_keys_2
+
+    - debug:
+        var: config_option_keys_2
+
+    # Ignore errors due to there is known issue on vSphere 7.0.x
+    # https://github.com/vmware/pyvmomi/issues/915
+    - name: get list of supported guest IDs from ESXi host
+      community.vmware.vmware_vm_config_option:
+        validate_certs: false
+        hostname: "{{ vcenter_hostname }}"
+        username: "{{ vcenter_username }}"
+        password: "{{ vcenter_password }}"
+        datacenter: "{{ dc1 }}"
+        esxi_hostname: "{{ esxi_hosts[0] }}"
+        get_guest_os_ids: true
+      register: guest_ids_1
+      ignore_errors: true
+
+    - debug:
+        var: guest_ids_1
+
+    - name: check guest ID list returned when task not failed
+      assert:
+        that:
+          - "'instance' in guest_ids_1"
+      when:
+        - "'failed' in guest_ids_1"
+        - not guest_ids_1.failed
+
+    - block:
+        # Ignore errors due to there is known issue on vSphere 7.0.x
+        # https://github.com/vmware/pyvmomi/issues/915
+        - name: get list of supported guest IDs from ESXi host with config option key
+          community.vmware.vmware_vm_config_option:
+            validate_certs: false
+            hostname: "{{ vcenter_hostname }}"
+            username: "{{ vcenter_username }}"
+            password: "{{ vcenter_password }}"
+            datacenter: "{{ dc1 }}"
+            esxi_hostname: "{{ esxi_hosts[0] }}"
+            get_guest_os_ids: true
+            hardware_version: "{{ config_option_keys_1['instance']['Supported hardware versions'][0] }}"
+          register: guest_ids_2
+          ignore_errors: true
+
+        - debug:
+            var: guest_ids_2
+        - name: check guest ID list returned when task not failed
+          assert:
+            that:
+              - "'instance' in guest_ids_2"
+          when:
+            - "'failed' in guest_ids_2"
+            - not guest_ids_2.failed
+      when:
+        - "'instance' in config_option_keys_1"
+        - "'Supported hardware versions' in config_option_keys_1['instance']"
+
+    # Ignore errors due to there is known issue on vSphere 7.0.x
+    # https://github.com/vmware/pyvmomi/issues/915
+    - name: get dict of recommended configs for specified guest ID from ESXi host
+      community.vmware.vmware_vm_config_option:
+        validate_certs: false
+        hostname: "{{ vcenter_hostname }}"
+        username: "{{ vcenter_username }}"
+        password: "{{ vcenter_password }}"
+        datacenter: "{{ dc1 }}"
+        esxi_hostname: "{{ esxi_hosts[0] }}"
+        get_config_options: true
+        guest_id: "rhel6_64Guest"
+      register: recommended_configs
+      ignore_errors: true
+
+    - debug:
+        var: recommended_configs
+    - name: check guest ID list returned when task not failed
+      assert:
+        that:
+          - "'instance' in recommended_configs"
+      when:
+        - "'failed' in recommended_configs"
+        - not recommended_configs.failed