Repository: ansible/ansible

Issue:
vmware_guest: vim.vm.device.VirtualDeviceSpec.FileOperation' object is not callable
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report


##### COMPONENT NAME
vmware_guest

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
2.5
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste 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).

-->

##### OS / ENVIRONMENT
<!---
Mention 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.
-->

##### SUMMARY
```
TASK [create the vm] *****************************************************************
task path: /home/jtanner/workspace/issues/AP-29043/site.yml:6
Using module file /home/jtanner/workspace/issues/AP-29043/ansible.ansible/lib/ansible/modules/cloud/vmware/vmware_guest.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: jtanner
<127.0.0.1> EXEC /bin/sh -c 'echo ~ && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/jtanner/.ansible/tmp/ansible-tmp-1506279364.69-200335176959623 `" && echo ansible-tmp-1506279364.69-200335176959623="` echo /home/jtanner/.ansible/tmp/ansible-tmp-1506279364.69-200335176959623 `" ) && sleep 0'
<127.0.0.1> PUT /tmp/tmp2gU6Z1 TO /home/jtanner/.ansible/tmp/ansible-tmp-1506279364.69-200335176959623/vmware_guest.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/jtanner/.ansible/tmp/ansible-tmp-1506279364.69-200335176959623/ /home/jtanner/.ansible/tmp/ansible-tmp-1506279364.69-200335176959623/vmware_guest.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python /home/jtanner/.ansible/tmp/ansible-tmp-1506279364.69-200335176959623/vmware_guest.py; rm -rf "/home/jtanner/.ansible/tmp/ansible-tmp-1506279364.69-200335176959623/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_2nqnjY/ansible_module_vmware_guest.py", line 1504, in <module>
    main()
  File "/tmp/ansible_2nqnjY/ansible_module_vmware_guest.py", line 1498, in main
    module.fail_json(**result)
  File "/tmp/ansible_2nqnjY/ansible_modlib.zip/ansible/module_utils/basic.py", line 2278, in fail_json
  File "/tmp/ansible_2nqnjY/ansible_modlib.zip/ansible/module_utils/basic.py", line 2256, in _return_formatted
  File "/tmp/ansible_2nqnjY/ansible_modlib.zip/ansible/module_utils/basic.py", line 509, in remove_values
  File "/tmp/ansible_2nqnjY/ansible_modlib.zip/ansible/module_utils/basic.py", line 448, in _remove_values_conditions
TypeError: 'vim.vm.device.VirtualDeviceSpec.FileOperation' object is not callable

fatal: [localhost]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_2nqnjY/ansible_module_vmware_guest.py\", line 1504, in <module>\n    main()\n  File \"/tmp/ansible_2nqnjY/ansible_module_vmware_guest.py\", line 1498, in main\n    module.fail_json(**result)\n  File \"/tmp/ansible_2nqnjY/ansible_modlib.zip/ansible/module_utils/basic.py\", line 2278, in fail_json\n  File \"/tmp/ansible_2nqnjY/ansible_modlib.zip/ansible/module_utils/basic.py\", line 2256, in _return_formatted\n  File \"/tmp/ansible_2nqnjY/ansible_modlib.zip/ansible/module_utils/basic.py\", line 509, in remove_values\n  File \"/tmp/ansible_2nqnjY/ansible_modlib.zip/ansible/module_utils/basic.py\", line 448, in _remove_values_conditions\nTypeError: 'vim.vm.device.VirtualDeviceSpec.FileOperation' object is not callable\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE",
    "rc": 0
}
```

##### STEPS TO REPRODUCE
```yaml
- hosts: localhost
  connection: local
  gather_facts: False
  tasks:

  - name: create the vm
    vmware_guest:
      hostname: 10.0.0.152
      username: administrator@vsphere.local
      password: VMware1234!
      validate_certs: no

      name: lxyomat
      datacenter: N_SW100_01
      #cluster: CL-N-SW100-02(Linux)
      cluster: CL-N-SW100-07(Citrix II)
      folder: /Linux/Test
      guest_id: centos64guest

      state: present
      disk:
        - size_gb: 1
          type: thin
          datastore: datastore1
      hardware:
        memory_mb: 512
        num_cpus: 1
```


Discussion:

Resolving patch:
diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py
--- a/lib/ansible/module_utils/vmware.py
+++ b/lib/ansible/module_utils/vmware.py
@@ -31,6 +31,7 @@
 except ImportError:
     HAS_PYVMOMI = False
 
+from ansible.module_utils._text import to_text
 from ansible.module_utils.urls import fetch_url
 from ansible.module_utils.six import integer_types, iteritems, string_types
 
@@ -593,22 +594,14 @@ def serialize_spec(clonespec):
         xt = type(xo)
         if xo is None:
             data[x] = None
-        elif issubclass(xt, list):
-            data[x] = []
-            for xe in xo:
-                data[x].append(serialize_spec(xe))
-        elif issubclass(xt, string_types + integer_types + (float, bool)):
-            data[x] = xo
-        elif issubclass(xt, dict):
-            data[x] = {}
-            for k, v in xo.items():
-                data[x][k] = serialize_spec(v)
         elif isinstance(xo, vim.vm.ConfigSpec):
             data[x] = serialize_spec(xo)
         elif isinstance(xo, vim.vm.RelocateSpec):
             data[x] = serialize_spec(xo)
         elif isinstance(xo, vim.vm.device.VirtualDisk):
             data[x] = serialize_spec(xo)
+        elif isinstance(xo, vim.vm.device.VirtualDeviceSpec.FileOperation):
+            data[x] = serialize_spec(xo)
         elif isinstance(xo, vim.Description):
             data[x] = {
                 'dynamicProperty': serialize_spec(xo.dynamicProperty),
@@ -617,9 +610,22 @@ def serialize_spec(clonespec):
                 'summary': serialize_spec(xo.summary),
             }
         elif hasattr(xo, 'name'):
-            data[x] = str(xo) + ':' + xo.name
+            data[x] = to_text(xo) + ':' + to_text(xo.name)
         elif isinstance(xo, vim.vm.ProfileSpec):
             pass
+        elif issubclass(xt, list):
+            data[x] = []
+            for xe in xo:
+                data[x].append(serialize_spec(xe))
+        elif issubclass(xt, string_types + integer_types + (float, bool)):
+            data[x] = to_text(xt)
+        elif issubclass(xt, bool):
+            data[x] = xo
+        elif issubclass(xt, dict):
+            data[to_text(x)] = {}
+            for k, v in xo.items():
+                k = to_text(k)
+                data[x][k] = serialize_spec(v)
         else:
             data[x] = str(xt)
 


Test patch: