Repository: ansible/ansible

Issue:
vmware_host_config_manager long integer error
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
<!--- Explain the problem briefly -->
Changing advanced options that are flagged in vCenter as (long integer) fails. 
##### ISSUE TYPE
 - Bug Report
Changing advanced options that are flagged in vCenter as (long integer) fails. Changing advanced options that are flagged in vCenter as (integer/string) succeed.
Security.AccountUnlockTime (integer) - Success
UserVars.DcuiTimeOut (long integer) - Failure

msg: 'Failed to update option/s as one or more OptionValue contains an invalid value: A specified parameter was not correct:  '
##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
vmware_host_config_manager
##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.2
python version 2.7.13
RHEL 6 airgapped
```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).-->
airgap
##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->
airgap
##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->
run playbook
<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: ESXi Advanced Settings
  delegate_to: localhost
  VMware_host_config_manager:
  hostname: vcenter
  username: vcenter admin
  password: ansible_password
  validate_certs: no
  esxi_hostname: '{{ inventory_hostname }}'
  options:
     'Security.AccountUnlockTime': 900   # works (integer)
     'UserVars.DcuiTimeOut': 600             # fails (long integer)
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Value change
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
error
<!--- Paste verbatim command output between quotes below -->
```
msg: 'Failed to update option/s as one or more OptionValue contains an invalid value: A specified parameter was not correct:  '
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/vmware/vmware_host_config_manager.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/vmware/vmware_host_config_manager.py)

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 --->
cc @Akasurde @dav1x @warthog9
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@LastCaress77 Thanks for reporting this issue. Would you be interested in testing this patch - https://github.com/ansible/ansible/compare/devel...Akasurde:i40180 and let me know if it works for you ? 

needs_info
Seems to be strongly related to or a duplicate of #40180,  this issue contains some information/conditions that #40180 does not.
@Renlor Yes, both are somewhat related. Once @LastCaress77 confirms, we will close one of them as duplicate.
Both issues are the same. After review of your fix it also appears that won’t fully solve the issue either. The problem here is VMware has cast types randomly but you can see the type if you edit an advanced setting. We have some code we are testing that we hope to get committed back today for a full fix for this issue.

Resolving patch:
diff --git a/lib/ansible/modules/cloud/vmware/vmware_host_config_manager.py b/lib/ansible/modules/cloud/vmware/vmware_host_config_manager.py
--- 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 @@
     - 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 @@
 '''
 
 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 @@ def __init__(self, module):
         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)


Test patch: