Repository: ansible-collections/community.vmware

Issue:
vmware_guest: Fails to add two disks and 1 NIC
##### SUMMARY
`vmware_guest` fails to clone a template and add two disks and one NIC. Worked with vCenter 6.7U3, doesn't work with 7.0U1 any more.

Just two disks works, one disk and one NIC works also.

In vCenter I see:

```
Status: A specified parameter was not correct: deviceChange[2].device.key
Error stack:
  -> Cannot add multiple devices using the same device key.
```

Possibly related: #373 

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
vmware_guest

##### ANSIBLE VERSION
```
ansible 2.10.3
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /bin/ansible
  python version = 3.6.1 (default, Oct 26 2017, 01:54:52) [GCC 6.3.0]
```

##### CONFIGURATION
```
```

##### OS / ENVIRONMENT
vCenter 7.0U1

##### STEPS TO REPRODUCE
```
- name: "deploy: create vm"
  vmware_guest:
    hostname: "{{ vcenter_ip }}"
    port: 443
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ vcenter_datacenter }}"
    cluster: "{{ vcenter_cluster }}"
    folder: "{{ vcenter_folder }}"
    name: "{{ vm_name }}"
    guest_id: "{{ vm_guest_id }}"
    template: "{{ vm_template }}"
    hardware:
      boot_firmware: "efi"
      hotadd_cpu: true
      hotadd_memory: true
      memory_mb: 4
      memory_reservation_lock: true
      nested_virt: true
      num_cpus: 2
      scsi: paravirtual
      virt_based_security: true
    disk:
    - size_gb: 75
      type: thin
      datastore: "{{ datastore_cluster }}"
    - size_gb: 5
      type: thin
      datastore: "{{ datastore_cluster }}"
    networks:
    - name: "{{ port_group }}"
      device_type: vmxnet3
      start_connected: true
    state: poweredoff
    validate_certs: false
```

##### EXPECTED RESULTS
Template gets cloned wirh two disks and one NIC.


##### ACTUAL RESULTS
```
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Failed to create a virtual machine : A specified parameter was not correct: deviceChange[2].device.key"}
```



Discussion:
I've had a closer look at `clonespec.config` used to clone the template:

https://github.com/ansible-collections/community.vmware/blob/0b4813b5a3dec6c3aa7c9a503461711ea5f6d576/plugins/modules/vmware_guest.py#L3098-L3106

with [q](https://pypi.org/project/q/) like this:

```python
for virtualDeviceSpec in clonespec.config.deviceChange:
    q(virtualDeviceSpec.device)
    q(virtualDeviceSpec.device.key)
```

And indeed, there are duplicate device keys:
```
 0.8s deploy_vm:
      (vim.vm.device.VirtualDisk) {
         dynamicType = <... <unset>,
         nativeUnmanagedLinkedClone = false
      }
 0.8s deploy_vm: 'key: 2000'
 0.8s deploy_vm:
      (vim.vm.device.VirtualDisk) {
         dynamicType = <...unset>,
         nativeUnmanagedLinkedClone = <unset>
      }
 0.8s deploy_vm: 'key: 0'
 0.8s deploy_vm:
      (vim.vm.device.VirtualVmxnet3) {
         dynamicType ...= <unset>,
         uptCompatibilityEnabled = <unset>
      }
 0.8s deploy_vm: 'key: 0'
```

First disk has key 2000 (maybe inherited from the template?), both the second disk and the NIC have key 0.

When I force the NIC's key to be something else, like this:

```python
clonespec.config.deviceChange[2].device.key = 42
```

it works.

Interesting, because the documentation clearly states that [this property is not read-only, but the client cannot control its value](https://code.vmware.com/apis/704/vsphere/vim.vm.device.VirtualDevice.html). Sounds like the duplicate keys would be ignored by vCenter, but obviously they aren't (any more).

_edit:_
Btw, it looks like the key really _is_ ignored. The NIC has a key 4000, **not** 42.

Resolving patch:
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
@@ -1174,6 +1174,7 @@ def create_hard_disk(self, disk_ctl, disk_index=None):
         diskspec = vim.vm.device.VirtualDeviceSpec()
         diskspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
         diskspec.device = vim.vm.device.VirtualDisk()
+        diskspec.device.key = -randint(20000, 24999)
         diskspec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
         diskspec.device.controllerKey = disk_ctl.device.key
 
@@ -1224,6 +1225,7 @@ def get_device(self, 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.key = -randint(25000, 29999)
         nic.device.wakeOnLanEnabled = bool(device_infos.get('wake_on_lan', True))
         nic.device.deviceInfo = vim.Description()
         nic.device.deviceInfo.label = device_label


Test patch: