Repository: ansible-collections/community.vmware

Issue:
Provision VM on a prefered ESXi Host with `vmware_guest`
##### SUMMARY
According to the [documentation](https://docs.ansible.com/ansible/latest/modules/vmware_guest_module.html#parameter-esxi_hostname), `esxi_hostname` attribute define on which ESXi host the virtual machine will run. But actually it doesn't behave like that.

As far as I use the `vmware_guest` against the vcenter I din't found a way to define a prefered ESXi host where my VM have to run on.

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
- plugins/modules/vmware_guest.py 

##### ANSIBLE VERSION
```
ansible 2.9.12
  config file = /home/xenlo/Projects/463/etc/ansible.cfg
  configured module search path = ['/home/xenlo/Projects/463/library']
  ansible python module location = /home/xenlo/.virtualenvs/463/lib/python3.8/site-packages/ansible
  executable location = /home/xenlo/.virtualenvs/463/bin/ansible
  python version = 3.8.5 (default, Aug 12 2020, 00:00:00) [GCC 10.2.1 20200723 (Red Hat 10.2.1-1)]
```

##### CONFIGURATION
```
ANSIBLE_SSH_ARGS(/home/xenlo/Projects/463/etc/ansible.cfg) = -C -o ControlMaster=auto -o ControlPersist=60s -o ServerAliveInterval=60
CACHE_PLUGIN(/home/xenlo/Projects/463/etc/ansible.cfg) = jsonfile
CACHE_PLUGIN_CONNECTION(/home/xenlo/Projects/463/etc/ansible.cfg) = $HOME/.ansible/facts/
CACHE_PLUGIN_TIMEOUT(/home/xenlo/Projects/463/etc/ansible.cfg) = 3600
DEFAULT_ACTION_PLUGIN_PATH(env: ANSIBLE_ACTION_PLUGINS) = ['/home/xenlo/.virtualenvs/463/lib/python3.8/site-packages/ara/plugins/action']
DEFAULT_CALLBACK_PLUGIN_PATH(env: ANSIBLE_CALLBACK_PLUGINS) = ['/home/xenlo/.virtualenvs/463/lib/python3.8/site-packages/ara/plugins/callback']
DEFAULT_CALLBACK_WHITELIST(/home/xenlo/Projects/463/etc/ansible.cfg) = ['profile_tasks']
DEFAULT_FILTER_PLUGIN_PATH(env: ANSIBLE_FILTER_PLUGINS) = ['/home/xenlo/Projects/463/plugins/filter']
DEFAULT_GATHERING(/home/xenlo/Projects/463/etc/ansible.cfg) = smart
DEFAULT_LOG_PATH(/home/xenlo/Projects/463/etc/ansible.cfg) = /home/xenlo/.ansible/log/ansible.log
DEFAULT_MODULE_PATH(env: ANSIBLE_LIBRARY) = ['/home/xenlo/Projects/463/library']
DEFAULT_ROLES_PATH(env: ANSIBLE_ROLES_PATH) = ['/home/xenlo/Projects/463/roles.galaxy', '/home/xenlo/Projects/463/roles']
DEFAULT_STDOUT_CALLBACK(/home/xenlo/Projects/463/etc/ansible.cfg) = yaml
GALAXY_ROLE_SKELETON(env: ANSIBLE_GALAXY_ROLE_SKELETON) = /home/xenlo/Projects/463/etc/skel/default
HOST_KEY_CHECKING(/home/xenlo/Projects/463/etc/ansible.cfg) = False
```

##### OS / ENVIRONMENT
- target: vsphere 6.7.0


##### STEPS TO REPRODUCE
A simple loop task which create VMs with a prefered host as `esxi_hostname` attribut.

Here is an sample:
```yaml
---
# Deploy some VMS

- name: Provisions VM hosts
  hosts: vcenter
  gather_facts: no

  vars:
    domain_name: "my_domain.net"
    vcenter_sso_pass: "{{ ansible_ssh_pass }}"
    datacenter_name: "my_little_DC"
    datastore_name: "my_vmfs_01"

    vm_list:
      - name: little-vm-1-01
        pref_node: node01
      - name: little-vm-1-02
        pref_node: node01
      - name: little-vm-1-03
        pref_node: node01
      - name: little-vm-2-02
        pref_node: node02
      - name: little-vm-2-01
        pref_node: node02
      - name: little-vm-3-01
        pref_node: node03
      - name: little-vm-3-02
        pref_node: node03
      - name: little-vm-3-03
        pref_node: node03
      - name: little-vm-3-04
        pref_node: node03
      - name: little-vm-3-05
        pref_node: node03

  tasks:
    - name: Provision VM
      vmware_guest:
        hostname: "{{ ansible_host }}"
        esxi_hostname: "{{ vm.pref_node }}.{{ domain_name }}"
        username: "Administrator@{{ domain_name }}"
        password: "{{ vcenter_sso_pass }}"
        validate_certs: no
        name: "{{ vm.name }}.{{ domain_name }}"
        state: poweredoff
        folder: "/"
        datacenter: "{{ datacenter_name }}"
        guest_id: "debian9_64Guest"
        hardware:
          num_cpus: 1
          memory_mb: 1024
        disk:
          - size_gb: 2
            type: thin
            datastore: "{{ datastore_name }}"
      loop: "{{ vm_list }}"
      loop_control:
        loop_var: vm
      delegate_to: localhost
```

##### EXPECTED RESULTS
The VMs are provisioned on there prefered node.

##### ACTUAL RESULTS
The VMs are created but on random ESXi hosts.



Discussion:
Files identified in the description:
* [`plugins/modules/vmware_guest.py`](https://github.com/ansible-collections/vmware/blob/main/plugins/modules/vmware_guest.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 @Tomorrow9 @goneri @lparkes @nerzhul @pdellaert @pgbidkar @warthog9
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I had some investigations with @goneri already. 

He created a PR #357 to test some changes (having the set of `self.relospec.host` independent if we use template or not).

And from my side I tried to force some `RelocateVM_Task(…)` after the `CreateVM_Task(…)` but without any success. 

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
@@ -3157,10 +3157,14 @@ def deploy_vm(self):
                                                         snapshotDirectory=None,
                                                         suspendDirectory=None,
                                                         vmPathName="[" + datastore_name + "]")
+                esx_host = None
+                # Only select specific host when ESXi hostname is provided
+                if self.params['esxi_hostname']:
+                    esx_host = self.select_host()
 
                 clone_method = 'CreateVM_Task'
                 try:
-                    task = destfolder.CreateVM_Task(config=self.configspec, pool=resource_pool)
+                    task = destfolder.CreateVM_Task(config=self.configspec, pool=resource_pool, host=esx_host)
                 except vmodl.fault.InvalidRequest as e:
                     self.module.fail_json(msg="Failed to create virtual machine due to invalid configuration "
                                               "parameter %s" % to_native(e.msg))


Test patch: