Repository: ansible-collections/community.vmware

Issue:
vmware_resource_pool: cluster_name should not be required
<!--- 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
when creating a resource pool using module **vmware_resource_pool** it should _not_ be required to specify **cluster** when instead an esxi-host is specified (e.g. `esxi_hostname: foobarhost` ) - this problem was part of this issue here but was not resolved - see: https://github.com/ansible/ansible/issues/38300#issuecomment-428048898

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
vmware_resource_pool

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.9.15
  config file = /home/user/gitrepos/therepo/ansible_roles/ansible.cfg
  configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/user/gitrepos/therepo/autopytoolchain/venvp3/lib/python3.6/site-packages/ansible
  executable location = /home/user/gitrepos/therepo/autopytoolchain/venvp3/bin/ansible
  python version = 3.6.8 (default, Jan 14 2019, 11:02:34) [GCC 8.0.1 20180414 (experimental) [trunk revision 259383]]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
DEFAULT_FORCE_HANDLERS(/home/user/gitrepos/therepo/ansible_roles/ansible.cfg) = True
DEFAULT_STDOUT_CALLBACK(/home/user/gitrepos/therepo/ansible_roles/ansible.cfg) = debug
DEFAULT_TIMEOUT(/home/user/gitrepos/therepo/ansible_roles/ansible.cfg) = 30
HOST_KEY_CHECKING(/home/user/gitrepos/therepo/ansible_roles/ansible.cfg) = False
RETRY_FILES_ENABLED(/home/user/gitrepos/therepo/ansible_roles/ansible.cfg) = False
```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
* ansible host OS: Ubuntu 1804 LTS

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->
* setup 1-n  esxi hosts in a vmware-datacenter
* do _not_ create a vmware-cluster
* try to create a resource_pool via module vmware_resource_pool

<!--- Paste example playbooks or commands between quotes below -->
```yaml

    - name: ensure resourcepool exists
      vmware_resource_pool:
        hostname: "{{ _host.vcenter.hostname }}"
        username: "{{ vmware_vcenter_username }}"
        password: "{{ vmware_vcenter_password }}"
        validate_certs: "{{ _host.vcenter.validate_certs }}"
        datacenter: '{{ _host.vmware.datacenter }}'
        esxi_hostname: "{{ _host.vmware.esxi_hostname }}"
        resource_pool: "{{ _host.vmware.resource_pool }}"
        state: present
      delegate_to: localhost
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
* the specified resource_pool is created on the esxi host if it does not exist 

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
* module errors out saying that cluster is required / esxi_hostname is not supported
<!--- Paste verbatim command output between quotes -->
```paste below
...
Unsupported parameters for (vmware_resource_pool) module: esxi_hostname Supported parameters include: cluster, cpu_expandable_reservations, cpu_limit, cpu_reservation, cpu_shares, datacenter, hostname, mem_expandable_reservations, mem_limit, mem_reservation, mem_shares, password, port, proxy_host, proxy_port, resource_pool, state, username, validate_certs
...
```



Discussion:
@dmelha Thanks for reporting this issue. `resource_pool` is associated with the cluster and not with esxi (correct me if I am wrong). So it makes sense to specify the cluster name.

Also, while creating a new resource pool, we need to specify the parent resource pool under which new resource pool will be created. To get the value of the parent resource pool, cluster is required value.

We can add support `esxi_hostname` in the module, but AFAIK there is no VMware API to know parent cluster from the given ESXi host system. We will need to implement some logic to gather parent cluster when ESXi name is specified.

I hope this makes sense. Thanks.

@sky-joker @goneri @mariolenz What do you think about this?
@Akasurde 

> @dmelha Thanks for reporting this issue. `resource_pool` is associated with the cluster and not with esxi (correct me if I am wrong). So it makes sense to specify the cluster name.

I'm working with multiple vcenters/datacenters/esxi hosts, that are not organized in clusters but having separate resource_pools defined per host. vmware does in no way require a cluster to be defined for a resourcepool to be created on an esxi host.

> 
> Also, while creating a new resource pool, we need to specify the parent resource pool under which new resource pool will be created. To get the value of the parent resource pool, cluster is required value.
> 
> We can add support `esxi_hostname` in the module, but AFAIK there is no VMware API to know parent cluster from the given ESXi host system. We will need to implement some logic to gather parent cluster when ESXi name is specified.
> 

while i don't know the details of the vmware API(s) + pyvmomi (yet), looking at ***govc/govmomi*** maybe helps, as govc can create a resource_pool on an esxi host without specifying any cluster or  parent resource pool - i just need to specify datacenter & esxi host there via `govc pool.create -dc foodc "myesxi/Resources/barpool"`



@dmelha Thanks for providing information. 

I tried - 

```
$ govc pool.create -dc Asia-Datacenter1 "10.65.201.106/Resources/nested"
govc: cannot create resource pool 'nested': parent not found
```

Where 10.65.201.106 is esxi. Do you know what is wrong here?
> @dmelha Thanks for providing information.
> 
> I tried -
> 
> ```
> $ govc pool.create -dc Asia-Datacenter1 "10.65.201.106/Resources/nested"
> govc: cannot create resource pool 'nested': parent not found
> ```
> 
> Where 10.65.201.106 is esxi. Do you know what is wrong here?

@Akasurde I can only guess that the parent folder(?) (maybe of type vim.ComputeResource) named "Resources" is missing on that host!?
On my vmware infra some resourcepools are always  manually created on the hosts via web-vsphere-client. only afterwards I interact via govc +pyvmomi with vcenters & hosts. I guess creating a resourec pool manually could create the folder(?) "Resources" on that esxi host.


@Akasurde 
> `resource_pool` is associated with the cluster and not with esxi

Nope, a [ResourcePool](https://vdc-download.vmware.com/vmwb-repository/dcr-public/b50dcbbf-051d-4204-a3e7-e1b618c1e384/538cf2ec-b34f-4bae-a332-3820ef9e7773/vim.ResourcePool.html) is associated with a [ComputeResource](https://vdc-download.vmware.com/vmwb-repository/dcr-public/b50dcbbf-051d-4204-a3e7-e1b618c1e384/538cf2ec-b34f-4bae-a332-3820ef9e7773/vim.ComputeResource.html). In most cases, I should say this will be a cluster (there might be use cases for stand-alone ESXi hosts but HA and DRS are something I don't want to miss) but it doesn't have to be. A [ClusterComputeResource](https://vdc-download.vmware.com/vmwb-repository/dcr-public/b50dcbbf-051d-4204-a3e7-e1b618c1e384/538cf2ec-b34f-4bae-a332-3820ef9e7773/vim.ClusterComputeResource.html) is just  sub-type of  ComputeResource.

I'll try to have closer look at his.
@dmelha 
I'm sorry, but this feature request (I don't consider it a bug) isn't 100% clear to me.

As far as I understand, you connect to a **vCenter** and want to configure a resource pool on an ESXi host instead of a cluster. You do **not** want to connect to the ESXi host directly and configure a resource pool. Is this correct?
> @dmelha
> I'm sorry, but this feature request (I don't consider it a bug) isn't 100% clear to me.
> 
> As far as I understand, you connect to a **vCenter** and want to configure a resource pool on an ESXi host instead of a cluster. You do **not** want to connect to the ESXi host directly and configure a resource pool. Is this correct?

@mariolenz yes, I connect to a vcenter, and want to create resource pool on a specific esxi host. I do not want to directly connect to the esxi host for resource pool creation. 

sidenote: While I understand this seems like a feature request to you, to me it seems like a bug, as the vsphere api supports this use case, but the resource_pool module inhibits this use case.
> yes, I connect to a vcenter, and want to create resource pool on a specific esxi host. I do not want to directly connect to the esxi host for resource pool creation.

Great! I hope this'll be easier to implement :-)

@sky-joker @Tomorrow9 
As far as I can see, an ESXi host has two representations in vCenter. It's a [HostSystem](https://vdc-download.vmware.com/vmwb-repository/dcr-public/b50dcbbf-051d-4204-a3e7-e1b618c1e384/538cf2ec-b34f-4bae-a332-3820ef9e7773/vim.HostSystem.html) (which is what we're using in our modules to configure firewall rules and similar), but it's also a [ComputeResource](https://vdc-download.vmware.com/vmwb-repository/dcr-public/b50dcbbf-051d-4204-a3e7-e1b618c1e384/538cf2ec-b34f-4bae-a332-3820ef9e7773/vim.ComputeResource.html) (which we would need for this issue).

Do you, by any chance, know how to get the ComputeResource object of an ESXi host? Does it, possibly, have the same name? Unfortunately, I'm away on leave at the moment and don't have access to our environment to check this myself.
For the record:

```python
import ssl
from pyVim import connect
from pyVmomi import vim
context = ssl._create_unverified_context()
si = connect.SmartConnect(host="vcenter.example.com", user="Administrator@vsphere.local", pwd="T0pS3cret!", port=443, sslContext=context)
content = si.content
container = content.viewManager.CreateContainerView(content.rootFolder, [vim.ComputeResource], True)
for managed_object_ref in container.view:
    print(managed_object_ref.name)
```

This finds all clusters (ClusterComputeResource is a sub-type of ComputeResource) and all hosts (ComputeResource) that are **not** part of a cluster. I've removed one host from a cluster (esx1.example.com) and now there's a ComputeResource with this name.
jfyi i used this hacky custom module as a (temporary) workaround for now: https://gist.github.com/dmelha/9b25d757e9f075c045b82dce784b8cd6

Resolving patch:
diff --git a/plugins/modules/vmware_resource_pool.py b/plugins/modules/vmware_resource_pool.py
--- a/plugins/modules/vmware_resource_pool.py
+++ b/plugins/modules/vmware_resource_pool.py
@@ -24,14 +24,21 @@
 options:
     datacenter:
         description:
-            - Name of the datacenter to add the host.
+            - Name of the datacenter.
         required: True
         type: str
     cluster:
         description:
-            - Name of the cluster to add the host.
-        required: True
+            - Name of the cluster to configure the resource pool.
+            - This parameter is required if C(esxi_hostname) is not specified.
+        type: str
+    esxi_hostname:
+        description:
+            - Name of the host to configure the resource pool.
+            - The host must not be member of a cluster.
+            - This parameter is required if C(cluster) is not specified.
         type: str
+        version_added: '1.5.0'
     resource_pool:
         description:
             - Resource pool name to manage.
@@ -190,7 +197,7 @@
 
 from ansible.module_utils._text import to_native
 from ansible_collections.community.vmware.plugins.module_utils.vmware import get_all_objs, vmware_argument_spec, find_datacenter_by_name, \
-    find_cluster_by_name, wait_for_task, PyVmomi
+    find_cluster_by_name, find_object_by_name, wait_for_task, PyVmomi
 from ansible.module_utils.basic import AnsibleModule
 
 
@@ -199,7 +206,6 @@ class VMwareResourcePool(PyVmomi):
     def __init__(self, module):
         super(VMwareResourcePool, self).__init__(module)
         self.datacenter = module.params['datacenter']
-        self.cluster = module.params['cluster']
         self.resource_pool = module.params['resource_pool']
         self.hostname = module.params['hostname']
         self.username = module.params['username']
@@ -217,10 +223,22 @@ def __init__(self, module):
         self.cpu_reservation = module.params['cpu_reservation']
         self.cpu_expandable_reservations = module.params[
             'cpu_expandable_reservations']
-        self.dc_obj = None
-        self.cluster_obj = None
         self.resource_pool_obj = None
 
+        self.dc_obj = find_datacenter_by_name(self.content, self.datacenter)
+        if self.dc_obj is None:
+            self.module.fail_json(msg="Unable to find datacenter with name %s" % self.datacenter)
+
+        if module.params['cluster']:
+            self.compute_resource_obj = find_cluster_by_name(self.content, module.params['cluster'], datacenter=self.dc_obj)
+            if self.compute_resource_obj is None:
+                self.module.fail_json(msg="Unable to find cluster with name %s" % module.params['cluster'])
+
+        if module.params['esxi_hostname']:
+            self.compute_resource_obj = find_object_by_name(self.content, module.params['esxi_hostname'], [vim.ComputeResource], folder=self.dc_obj.hostFolder)
+            if self.compute_resource_obj is None:
+                self.module.fail_json(msg="Unable to find host with name %s" % module.params['esxi_hostname'])
+
     def select_resource_pool(self):
         pool_obj = None
 
@@ -397,17 +415,9 @@ def state_add_rp(self):
         if self.module.check_mode:
             self.module.exit_json(changed=changed)
 
-        self.dc_obj = find_datacenter_by_name(self.content, self.datacenter)
-        if self.dc_obj is None:
-            self.module.fail_json(msg="Unable to find datacenter with name %s" % self.datacenter)
-
-        self.cluster_obj = find_cluster_by_name(self.content, self.cluster, datacenter=self.dc_obj)
-        if self.cluster_obj is None:
-            self.module.fail_json(msg="Unable to find cluster with name %s" % self.cluster)
-
         rp_spec = self.generate_rp_config()
-        rootResourcePool = self.cluster_obj.resourcePool
 
+        rootResourcePool = self.compute_resource_obj.resourcePool
         rootResourcePool.CreateResourcePool(self.resource_pool, rp_spec)
 
         resource_pool_config = self.generate_rp_config_return_value(True)
@@ -424,7 +434,8 @@ def check_rp_state(self):
 def main():
     argument_spec = vmware_argument_spec()
     argument_spec.update(dict(datacenter=dict(required=True, type='str'),
-                              cluster=dict(required=True, type='str'),
+                              cluster=dict(type='str', required=False),
+                              esxi_hostname=dict(type='str', required=False),
                               resource_pool=dict(required=True, type='str'),
                               mem_shares=dict(type='str', default="normal", choices=[
                                               'high', 'custom', 'normal', 'low']),
@@ -447,6 +458,12 @@ def main():
                                ['mem_shares', 'custom', ['mem_allocation_shares']],
                                ['cpu_shares', 'custom', ['cpu_allocation_shares']]
                            ],
+                           required_one_of=[
+                               ['cluster', 'esxi_hostname'],
+                           ],
+                           mutually_exclusive=[
+                               ['cluster', 'esxi_hostname'],
+                           ],
                            supports_check_mode=True)
 
     if not HAS_PYVMOMI:


Test patch: