Repository: ansible-collections/community.vmware

Issue:
vmware_deploy_ovf: The virtual machine is not supported on the target datastore
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Explain the problem briefly below -->

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
vmware_deploy_ovf

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.10.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.6.5 (default, Apr  1 2018, 05:46:30) [GCC 7.3.0]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below

```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->


##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
```
(1) one datacenter in VC,
(2) two hosts added in this datacenter, no cluster,
(3) "datastore2" is the datastore of host1, "datastore2 (1)" is the datastore of host2, "test-ds" is the shared datastore of these two hosts,
(4) deploy ovf template in "datastore2" and "test-ds" succeed, deploy ovf template in "datastore2 (1)" failed.
```

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- hosts: localhost
  tasks:
    - name: Deploy VM from ovf template
      vmware_deploy_ovf:
        hostname: "xx.xx.xx.xx"
        username: "Administrator@vsphere.local"
        password: "xxxxxx"
        validate_certs: "False"
        datacenter: "vcDC"
        datastore: "datastore2 (1)"
        networks: "{{ ovf_networks | default({'VM Network': 'VM Network'}) }}"
        ovf: "./openwrt_19.07.2_x86.ova"
        name: test_ovf_deploy
        allow_duplicates: False
        disk_provisioning: "thin"
        power_on: False
        wait_for_ip_address: False
      register: ovf_deploy
    - debug: var=ovf_deploy
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
OVF can be deployed successfully

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes -->
```paste below
PLAY [localhost] *****************************************************************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [localhost]

TASK [Deploy VM from ovf template] ***********************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Failure validating OVF import spec: The virtual machine is not supported on the target datastore."}

PLAY RECAP ***********************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0 
```



Discussion:
Files identified in the description:
* [`plugins/modules/vmware_deploy_ovf.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/vmware_deploy_ovf.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 @goneri @lparkes @pgbidkar @warthog9
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I assume if add "esxi_hostname" to specify the exact ESXi host can resolve this issue in this scenario?
```
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Unsupported parameters for (vmware_deploy_ovf) module: esxi_hostname 

Supported parameters include: allow_duplicates, cluster, datacenter, datastore, deployment_option, disk_provisioning, fail_on_spec_warnings, folder, hostname, inject_ovf_env, name, networks, ovf, password, port, power_on, properties, proxy_host, proxy_port, resource_pool, username, validate_certs, wait, wait_for_ip_address"}
```
Like what defined in `vmware_guest` module:
```
  cluster:
    description:
    - The cluster name where the virtual machine will run.
    - This is a required parameter, if C(esxi_hostname) is not set.
    - C(esxi_hostname) and C(cluster) are mutually exclusive parameters.
    - This parameter is case sensitive.
    type: str
  esxi_hostname:
    description:
    - The ESXi hostname where the virtual machine will run.
    - This is a required parameter, if C(cluster) is not set.
    - C(esxi_hostname) and C(cluster) are mutually exclusive parameters.
    - This parameter is case sensitive.
    type: str
```

Resolving patch:
diff --git a/plugins/module_utils/vmware.py b/plugins/module_utils/vmware.py
--- a/plugins/module_utils/vmware.py
+++ b/plugins/module_utils/vmware.py
@@ -1483,7 +1483,7 @@ def get_recommended_datastore(self, datastore_cluster_obj=None):
         return None
 
     # Resource pool
-    def find_resource_pool_by_name(self, resource_pool_name, folder=None):
+    def find_resource_pool_by_name(self, resource_pool_name='Resources', folder=None):
         """
         Get resource pool managed object by name
         Args:
diff --git a/plugins/modules/vmware_deploy_ovf.py b/plugins/modules/vmware_deploy_ovf.py
--- a/plugins/modules/vmware_deploy_ovf.py
+++ b/plugins/modules/vmware_deploy_ovf.py
@@ -30,6 +30,17 @@
     cluster:
         description:
         - Cluster to deploy to.
+        - This is a required parameter, if C(esxi_hostname) is not set and C(hostname) is set to the vCenter server.
+        - C(esxi_hostname) and C(cluster) are mutually exclusive parameters.
+        - This parameter is case sensitive.
+        type: str
+    esxi_hostname:
+        description:
+        - The ESXi hostname where the virtual machine will run.
+        - This is a required parameter, if C(cluster) is not set and C(hostname) is set to the vCenter server.
+        - C(esxi_hostname) and C(cluster) are mutually exclusive parameters.
+        - This parameter is case sensitive.
+        version_added: '1.9.0'
         type: str
     datastore:
         default: datastore1
@@ -152,6 +163,16 @@
     power_on: no
     ovf: /absolute/path/to/template/mytemplate.ova
   delegate_to: localhost
+
+- community.vmware.vmware_deploy_ovf:
+    hostname: '{{ vcenter_hostname }}'
+    username: '{{ vcenter_username }}'
+    password: '{{ vcenter_password }}'
+    datacenter: Datacenter1
+    esxi_hostname: test-server
+    datastore: test-datastore
+    ovf: /path/to/ubuntu-16.04-amd64.ovf
+  delegate_to: localhost
 '''
 
 
@@ -321,12 +342,31 @@ def __init__(self, module):
         self.entity = None
 
     def get_objects(self):
+        # Get datacenter firstly
         self.datacenter = self.find_datacenter_by_name(self.params['datacenter'])
-        if not self.datacenter:
-            self.module.fail_json(msg='%(datacenter)s could not be located' % self.params)
+        if self.datacenter is None:
+            self.module.fail_json(msg="Datacenter '%(datacenter)s' could not be located" % self.params)
+
+        # Get cluster in datacenter if cluster configured
+        if self.params['cluster']:
+            cluster = self.find_cluster_by_name(self.params['cluster'], datacenter_name=self.datacenter)
+            if cluster is None:
+                self.module.fail_json(msg="Unable to find cluster '%(cluster)s'" % self.params)
+            self.resource_pool = self.find_resource_pool_by_cluster(self.params['resource_pool'], cluster=cluster)
+        # Or get ESXi host in datacenter if ESXi host configured
+        elif self.params['esxi_hostname']:
+            host = self.find_hostsystem_by_name(self.params['esxi_hostname'])
+            if host is None:
+                self.module.fail_json(msg="Unable to find host '%(esxi_hostname)s'" % self.params)
+            self.resource_pool = self.find_resource_pool_by_name(self.params['resource_pool'], folder=host.parent)
+        else:
+            self.resource_pool = self.find_resource_pool_by_name(self.params['resource_pool'])
+
+        if not self.resource_pool:
+            self.module.fail_json(msg="Resource pool '%(resource_pool)s' could not be located" % self.params)
 
         self.datastore = None
-        datastore_cluster_obj = self.find_datastore_cluster_by_name(self.params['datastore'])
+        datastore_cluster_obj = self.find_datastore_cluster_by_name(self.params['datastore'], datacenter=self.datacenter)
         if datastore_cluster_obj:
             datastore = None
             datastore_freespace = 0
@@ -340,21 +380,10 @@ def get_objects(self):
             if datastore:
                 self.datastore = datastore
         else:
-            self.datastore = self.find_datastore_by_name(self.params['datastore'], self.datacenter)
-
-        if not self.datastore:
-            self.module.fail_json(msg='%(datastore)s could not be located' % self.params)
-
-        if self.params['cluster']:
-            cluster = self.find_cluster_by_name(self.params['cluster'], datacenter_name=self.datacenter)
-            if cluster is None:
-                self.module.fail_json(msg="Unable to find cluster '%(cluster)s'" % self.params)
-            self.resource_pool = self.find_resource_pool_by_cluster(self.params['resource_pool'], cluster=cluster)
-        else:
-            self.resource_pool = self.find_resource_pool_by_name(self.params['resource_pool'])
+            self.datastore = self.find_datastore_by_name(self.params['datastore'], datacenter_name=self.datacenter)
 
-        if not self.resource_pool:
-            self.module.fail_json(msg='%(resource_pool)s could not be located' % self.params)
+        if self.datastore is None:
+            self.module.fail_json(msg="Datastore '%(datastore)s' could not be located on specified ESXi host or datacenter" % self.params)
 
         for key, value in self.params['networks'].items():
             network = find_network_by_name(self.content, value, datacenter_name=self.datacenter)
@@ -628,6 +657,9 @@ def main():
         'cluster': {
             'default': None,
         },
+        'esxi_hostname': {
+            'default': None,
+        },
         'deployment_option': {
             'default': None,
         },
@@ -693,6 +725,9 @@ def main():
     module = AnsibleModule(
         argument_spec=argument_spec,
         supports_check_mode=True,
+        mutually_exclusive=[
+            ['cluster', 'esxi_hostname'],
+        ],
     )
 
     deploy_ovf = VMwareDeployOvf(module)


Test patch: