From 6bd7d71cc1503f806cdccc4554ebfc7d2e3e146a Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 18 Jun 2018 08:27:37 -0600 Subject: [PATCH] Fix vmware host config for all vmware OptionTypes (#41343) * Fix vmware host config for all vmware OptionTypes Setting some options and option types failed with invalid value errors being return from vmware, this resolves all known ways that issue can occur. * Add logic for integer inputs as string For example - "UserVars.ESXiShellInteractiveTimeOut": "20" Fixes #40180 Fixes #41212 --- .../vmware/vmware_host_config_manager.py | 67 +++++++++++++++++-- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/lib/ansible/modules/cloud/vmware/vmware_host_config_manager.py b/lib/ansible/modules/cloud/vmware/vmware_host_config_manager.py index 65d29035fd0..e7c7c5082ee 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_host_config_manager.py +++ b/lib/ansible/modules/cloud/vmware/vmware_host_config_manager.py @@ -40,8 +40,8 @@ options: - If C(cluster_name) is not given, this parameter is required. options: description: - - A dictionary of advance configuration parameter. - - Invalid configuration parameters are ignored. + - A dictionary of advance configuration parameters. + - Invalid options will cause module to error. default: {} extends_documentation_fragment: vmware.documentation ''' @@ -81,13 +81,14 @@ RETURN = r'''# ''' try: - from pyVmomi import vim, vmodl + from pyVmomi import vim, vmodl, VmomiSupport except ImportError: pass from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.vmware import vmware_argument_spec, PyVmomi from ansible.module_utils._text import to_native +from ansible.module_utils.six import integer_types, string_types class VmwareConfigManager(PyVmomi): @@ -98,18 +99,70 @@ class VmwareConfigManager(PyVmomi): self.options = self.params.get('options', dict()) self.hosts = self.get_all_host_objs(cluster_name=cluster_name, esxi_host_name=esxi_host_name) + @staticmethod + def is_integer(value, type_of='int'): + try: + VmomiSupport.vmodlTypes[type_of](value) + return True + except (TypeError, ValueError): + return False + + @staticmethod + def is_boolean(value): + if str(value).lower() in ['true', 'on', 'yes', 'false', 'off', 'no']: + return True + return False + + @staticmethod + def is_truthy(value): + if str(value).lower() in ['true', 'on', 'yes']: + return True + return False + def set_host_configuration_facts(self): changed = False for host in self.hosts: option_manager = host.configManager.advancedOption host_facts = {} for option in option_manager.QueryOptions(): - host_facts[option.key] = option.value + host_facts[option.key] = dict(value=option.value) + + for s_option in option_manager.supportedOption: + host_facts[s_option.key].update( + option_type=s_option.optionType, + ) + change_option_list = [] for option_key, option_value in self.options.items(): - if option_key in host_facts and option_value != host_facts[option_key]: - change_option_list.append(vim.option.OptionValue(key=option_key, value=option_value)) - changed = True + if option_key in host_facts: + # Make sure option_type is defined some values do not have + # it defined and appear to be read only. + if 'option_type' in host_facts[option_key]: + # We handle all supported types here so we can give meaningful errors. + option_type = host_facts[option_key]['option_type'] + if self.is_boolean(option_value) and isinstance(option_type, vim.option.BoolOption): + option_value = self.is_truthy(option_value) + elif (isinstance(option_value, integer_types) or self.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 self.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))) + else: + self.module.fail_json(msg="Cannot change read only option %s to %s." % (option_key, option_value)) + + if option_value != host_facts[option_key]['value']: + change_option_list.append(vim.option.OptionValue(key=option_key, value=option_value)) + changed = True + else: # Don't silently drop unknown options. This prevents typos from falling through the cracks. + self.module.fail_json(msg="Unknown option %s" % option_key) if changed: try: option_manager.UpdateOptions(changedValue=change_option_list)