ansible-collections/community.vmware

Some advanced system settings are missing and non-configurable with the module

zaiguiw opened this issue · 10 comments

SUMMARY

Some advanced system settings are missing from the VMware host configure manager ansible module. For example, when I try to set the value of "/VSAN/IgnoreClusterMemberListUpdates" with this ansible task, I get a fatal error saying "Unsupported option VSAN.IgnoreClusterMemberListUpdates"

  - name: set esxi configuration
    community.vmware.vmware_host_config_manager:
      hostname: '{{ vc }}'
      username: '{{ user }}'
      password: '{{ password }}'
      esxi_hostname: '{{ inventory_hostname }}'
      validate_certs: false
      options:
          'VSAN.IgnoreClusterMemberListUpdates': 0 
    delegate_to: localhost
ISSUE TYPE
  • Bug Report
COMPONENT NAME
ANSIBLE VERSION

COLLECTION VERSION

CONFIGURATION

OS / ENVIRONMENT

~] vmware -lv
VMware ESXi 8.0.2 build-22380479

Here is to show the settings is there and can be set:
] esxcli system settings advanced set -o /VSAN/IgnoreClusterMemberListUpdates -i 1
:
] esxcli system settings advanced list -o /VSAN/IgnoreClusterMemberListUpdates
Path: /VSAN/IgnoreClusterMemberListUpdates
Type: integer
Int Value: 1
Default Int Value: 0
Min Value: 0
Max Value: 1
String Value:
Default String Value:
Valid Characters:
Description: Host will ignore cluster membership list updates
Host Specific: false
Impact: none
~] esxcli system settings advanced set -o /VSAN/IgnoreClusterMemberListUpdates -i 0
~]

STEPS TO REPRODUCE

See the ansible code above.

EXPECTED RESULTS
ACTUAL RESULTS

Please give an example of an error.

Thanks for looking into this. Here you go:

ansible-playbook manage-esxi-config.yml --ask-vault-pass -i esxi-host, -t set

Vault password:

PLAY [all] ************************************

TASK [set esxi configuration] ******************************************************************************************************************************************
fatal: [esxi-host -> localhost]: FAILED! => changed=false
msg: Unsupported option VSAN.IgnoreClusterMemberListUpdates

PLAY RECAP *************************************************************************************************************************************************************
esxi-host : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0

cat manage-esxi-config.yml


  • hosts: all
    gather_facts: no
    vars_files:

    • vars/vault.yml

    tasks:

    • name: Gather info about ESXi Host
      community.vmware.vmware_host_config_info:
      hostname: '{{ vc }}'
      username: '{{ user }}'
      password: '{{ password }}'
      esxi_hostname: '{{ inventory_hostname }}'
      validate_certs: false
      delegate_to: localhost
      register: configs
      tags: get

    • debug:
      msg: "{{ configs }}"

    • name: set esxi configuration
      community.vmware.vmware_host_config_manager:
      hostname: '{{ vc }}'
      username: '{{ user }}'
      password: '{{ password }}'
      esxi_hostname: '{{ inventory_hostname }}'
      validate_certs: false
      options:
      'VSAN.IgnoreClusterMemberListUpdates': 0
      delegate_to: localhost
      tags:

      • set
      • never

Run your playbook with -vvv and get full error log.

The module doesn't have a list of supported advanced settings, it tries to find out what advanced settings are supported:

option_manager = host.configManager.advancedOption
host_facts = {}
for s_option in option_manager.supportedOption:
host_facts[s_option.key] = dict(option_type=s_option.optionType, value=None)
for option in option_manager.QueryOptions():
if option.key in host_facts:
host_facts[option.key].update(
value=option.value,
)
change_option_list = []
for option_key, option_value in self.options.items():
if option_key in host_facts:
# We handle all supported types here so we can give meaningful errors.
option_type = host_facts[option_key]['option_type']
if is_boolean(option_value) and isinstance(option_type, vim.option.BoolOption):
option_value = is_truthy(option_value)
elif (isinstance(option_value, integer_types) or is_integer(option_value))\
and isinstance(option_type, vim.option.IntOption):
option_value = VmomiSupport.vmodlTypes['int'](option_value)
elif (isinstance(option_value, integer_types) or is_integer(option_value, 'long'))\
and isinstance(option_type, vim.option.LongOption):
option_value = VmomiSupport.vmodlTypes['long'](option_value)
elif isinstance(option_value, float) and isinstance(option_type, vim.option.FloatOption):
pass
elif isinstance(option_value, string_types) and isinstance(option_type, (vim.option.StringOption, vim.option.ChoiceOption)):
pass
else:
self.module.fail_json(msg="Provided value is of type %s."
" Option %s expects: %s" % (type(option_value), option_key, type(option_type)))
if option_value != host_facts[option_key]['value']:
change_option_list.append(vim.option.OptionValue(key=option_key, value=option_value))
changed_list.append(option_key)
else: # Don't silently drop unknown options. This prevents typos from falling through the cracks.
self.module.fail_json(msg="Unsupported option %s" % option_key)

I'm not sure if we have a bug in the module or if there's a bug in the vSphere API (that is not specifying VSAN.IgnoreClusterMemberListUpdates as a supported option in OptionManager).

I'm afraid I don't have to test with with 8.0 U2 at the moment :-/

This is weird. VSAN.IgnoreClusterMemberListUpdates is documented (here and in other places) but OptionManager doesn't list it as a supportedOption. At least not in 7.0 U3. I don't have a test environment running 8.0+ ATM where I could have a look.

Maybe a bug in the vSphere API?

My understanding is that this might be a "hidden" option. Hidden options don't show up when listed, but can still be manipulated when targeted.

I don't think it's a good idea to support any "hidden" options, sounds like an accident waiting to happen. 1) The name might change anytime and 2) we can't find out the type (integer, boolean, string...) automatically.

Do you have a support contract with VMware Broadcom that allows you to address this? If it's a supported option their API should tell us. "Hidden" options suck...

I am trying to replicate this issue with pvmomi. Will update once I have some clarification.

pyvmomi works fine for me. The key is that when querying the advanced setting, you'll need to target that particular setting, instead of getting a list and iterate through. That is, instead of using
QueryOptions() and iterate

use
QueryOptions(option_key)

Here is a sample code to demo this:

#!/usr/bin/python3

from pyVim.connect import SmartConnect, Disconnect
from pyVmomi import vim, vmodl, VmomiSupport
import ssl, atexit, time, argparse, getpass

def get_args():
    parser = argparse.ArgumentParser(description='VC inventory search by type. Arguments for talking to vCenter')
    parser.add_argument('-s', '--server', required=True, action='store', help='VC to onnect to')
    parser.add_argument('--host', required=True, action='store', help='hostname of ESXi')
    parser.add_argument('-u', '--user', required=True, action='store', help='User name')
    parser.add_argument('-p', '--password', required=False, action='store', help='user password')
    parser.add_argument('-k', '--key', required=True, action='store', help='Setting key')
    parser.add_argument('-v', '--value', required=True, action='store', help='Setting value')
    args = parser.parse_args()
    return args

def connect():
    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    context.verify_mode = ssl.CERT_NONE

    si = SmartConnect(host=args.server, user=args.user, pwd=args.password, port=443, sslContext=context)
    atexit.register(Disconnect, si)
    content = si.RetrieveContent()
    return content

def get_obj(content, vimtype, name):
    """
    Return an object by name, if name is None the
    first found object is returned
    """
    obj = None
    if name in ['', None]:
        obj = []
    container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True)
    for c in container.view:
        if name:
            if c.name == name:
                obj = c
                break
        else:
            obj.append(c)

    container.Destroy()
    return obj

####
args = get_args()
if not args.password:       
    args.password = getpass.getpass( prompt='Password for %s (%s): ' % (args.user, args.server))

content = connect()
host = get_obj(content, [vim.HostSystem], args.host)
option_manager = host.configManager.advancedOption
option = vim.option.OptionValue(key=args.key, value=VmomiSupport.vmodlTypes['int'](args.value))
option_manager.UpdateOptions(changedValue=[option])
#
print (option_manager.QueryOptions(args.key)[0].value)

and a sample execution:

# ./set-adv-setting.py -s test_vcenter --host test_host -u user -k VSAN.IgnoreClusterMemberListUpdates -v 0
Password for user (test_vcenter): 
0