Repository: ansible-collections/community.vmware

Issue:
Add module to obtain fcd info (vmware_first_class_disk_info)
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Describe the new feature/improvement briefly below -->
We should have a module to obtain info on fcds (first class disks). As of today it is only possible to create and delete them using the vmware_first_class_disk module. Especially deleting might be difficult if we do not have their names.
I did not find another way to get this information like any kind of info module for volumes or datastores etc.

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
vmware_first_class_disk_info

##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->
The use case is less in creating fcds but rather in doing some housekeeping in your vCenter after a k8s or OpenShift cluster has been deleted and fcds not been cleaned up.

govc can provide the information and achieve the cleanup but I seek a more native way:
```
# govc volume.ls -l
fade77df-8f6c-431f-af10-c80129403d13    pvc-5fdb4698-1bd6-4d82-9dac-c5eff7ff53b7        80.0GB  KUBERNETES      CLUSTER-A
f5c5ddf9-5298-40f3-9faa-3b6eda429b7f    pvc-e54c88a8-e618-4810-9b92-30ef3a36e71b        96.0MB  KUBERNETES      CLUSTER-A
752e14e3-73c9-4439-ae97-38649bd74587    pvc-f26b1e9b-8d12-4aef-86a5-33f21e206860        96.0MB  KUBERNETES      CLUSTER-B
049ea98c-96aa-4b66-b7c3-72bf4fad41d5    pvc-49eb5811-d782-4900-a307-a552ec1889e5        25.0GB  KUBERNETES      CLUSTER-B
0d3d4f65-81d8-4f4d-b311-aa4b54306054    pvc-cf97889a-149d-46a9-bf5b-cffa52492a26        80.0GB  KUBERNETES      CLUSTER-C

List fcds for Cluster-A:
# for i in $(govc volume.ls -l |grep CLUSTER-A | awk '{print $1}'); do echo $i ; govc volume.ls $i ; done
Remove everything for Cluster-A:
# for i in $(govc volume.ls -l |grep CLUSTER-A | awk '{print $1}'); do echo $i ; govc volume.rm $i ; done


Using govc volume.ls -l -json provides even more details. 
```




Discussion:
It is ideologically wrong to do 'state: list' - for this there must be an _info or _fact module. And of course, as always, a maintainer is required for this work.
Valid points. Changed title and rephrased the initial comment.

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
@@ -1874,6 +1874,30 @@ def find_first_class_disk_by_name(self, disk_name, datastore_obj):
 
         return None
 
+    def find_first_class_disks(self, datastore_obj):
+        """
+        Get first-class disks managed object
+        Args:
+            datastore_obj: Managed object of datastore
+
+        Returns: First-class disks managed object if found else None
+
+        """
+
+        disks = []
+
+        if self.is_vcenter():
+            for id in self.content.vStorageObjectManager.ListVStorageObject(datastore_obj):
+                disks.append(self.content.vStorageObjectManager.RetrieveVStorageObject(id, datastore_obj))
+
+        else:
+            for id in self.content.vStorageObjectManager.HostListVStorageObject(datastore_obj):
+                disks.append(self.content.vStorageObjectManager.HostRetrieveVStorageObject(id, datastore_obj))
+
+        if disks == []:
+            return None
+        else:
+            return disks
     #
     # Conversion to JSON
     #
diff --git a/plugins/modules/vmware_first_class_disk_info.py b/plugins/modules/vmware_first_class_disk_info.py
new file mode 100644
--- /dev/null
+++ b/plugins/modules/vmware_first_class_disk_info.py
@@ -0,0 +1,143 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2024, Nina Loser <nina.loser@muenchen.de>
+# Copyright: (c) 2021, Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+---
+module: vmware_first_class_disk_info
+short_description: Gather info about VMware vSphere First Class Disks
+description:
+    - This module can be used to gather information about VMware vSphere First Class Disks.
+author:
+- Nina Loser (@nina2244)
+options:
+    datacenter_name:
+      description: The name of the datacenter.
+      type: str
+    datastore_name:
+      description: Name of datastore or datastore cluster of the disk.
+      required: true
+      type: str
+    disk_name:
+      description: The name of the disk. If not set return all found.
+      type: str
+extends_documentation_fragment: vmware.documentation
+'''
+
+EXAMPLES = r'''
+- name: Gather info of 1GBDisk
+  community.vmware.vmware_first_class_disk_info:
+    hostname: '{{ vcenter_hostname }}'
+    username: '{{ vcenter_username }}'
+    password: '{{ vcenter_password }}'
+    datastore_name: '{{ datastore_name }}'
+    disk_name: '1GBDisk'
+  register: disk_info
+  delegate_to: localhost
+
+- debug:
+    var: disk_info.first_class_disks
+
+- name: Gather info of all first class disks
+  community.vmware.vmware_first_class_disk_info:
+    hostname: '{{ vcenter_hostname }}'
+    username: '{{ vcenter_username }}'
+    password: '{{ vcenter_password }}'
+    datastore_name: '{{ datastore_name }}'
+  register: disk_info
+  delegate_to: localhost
+
+- debug:
+    var: disk_info.first_class_disks
+'''
+
+RETURN = r'''
+first_class_disks:
+  description: list of dictionary of First-class disk and their information
+  returned: success
+  type: list
+  sample: [
+    {
+      "name": "1GBDisk",
+      "datastore_name": "DS0",
+      "size_mb": "1024",
+      "consumption_type": "disk",
+      "descriptor_version": 0,
+      "consumer_ids": []
+    }
+  ]
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.community.vmware.plugins.module_utils.vmware import PyVmomi, vmware_argument_spec
+
+
+class FirstClassDiskInfo(PyVmomi):
+    def __init__(self, module):
+        super(FirstClassDiskInfo, self).__init__(module)
+        self.datacenter_name = self.params['datacenter_name']
+        self.datastore_name = self.params['datastore_name']
+        self.disk_name = self.params['disk_name']
+
+    def gather_first_class_disk_info(self):
+        self.datastore_obj = self.find_datastore_by_name(datastore_name=self.datastore_name, datacenter_name=self.datacenter_name)
+        if not self.datastore_obj:
+            self.module.fail_json(msg='Failed to find datastore %s.' % self.datastore_name)
+
+        if self.disk_name:
+            self.disk_obj = self.find_first_class_disk_by_name(self.disk_name, self.datastore_obj)
+            if not self.disk_obj:
+                self.module.fail_json(msg='Failed to find disk %s.' % self.disk_name)
+
+            self.disks = [self.disk_obj]
+
+        else:
+            self.disks = self.find_first_class_disks(self.datastore_obj)
+            if not self.disks:
+                return []
+
+        disk_infos = list()
+        for disk in self.disks:
+            disk_info = dict(
+                name=disk.config.name,
+                id=disk.config.id.id,
+                datastore_name=disk.config.backing.datastore.name,
+                size_mb=disk.config.capacityInMB,
+                consumption_type=disk.config.consumptionType,
+                descriptor_version=disk.config.descriptorVersion,
+                consumer_ids=list(id.id for id in disk.config.consumerId)
+            )
+            disk_infos.append(disk_info)
+
+        return disk_infos
+
+
+def main():
+    argument_spec = vmware_argument_spec()
+    argument_spec.update(
+        dict(
+            datacenter_name=dict(type='str'),
+            datastore_name=dict(required=True, type='str'),
+            disk_name=dict(type='str')
+        )
+    )
+
+    module = AnsibleModule(argument_spec=argument_spec,
+                           supports_check_mode=True)
+
+    first_class_disk = FirstClassDiskInfo(module)
+
+    _first_class_disks = first_class_disk.gather_first_class_disk_info()
+
+    module.exit_json(changed=False, first_class_disks=_first_class_disks)
+
+
+if __name__ == '__main__':
+    main()


Test patch:
diff --git a/tests/integration/targets/vmware_first_class_disk_info/aliases b/tests/integration/targets/vmware_first_class_disk_info/aliases
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/vmware_first_class_disk_info/aliases
@@ -0,0 +1,3 @@
+cloud/vcenter
+needs/target/prepare_vmware_tests
+zuul/vmware/vcenter_1esxi
diff --git a/tests/integration/targets/vmware_first_class_disk_info/tasks/main.yml b/tests/integration/targets/vmware_first_class_disk_info/tasks/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/vmware_first_class_disk_info/tasks/main.yml
@@ -0,0 +1,96 @@
+# Test code for the vmware_first_class_disk_info module.
+# Copyright: (c) 2021, Mario Lenz <m@riolenz.de>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- import_role:
+    name: prepare_vmware_tests
+  vars:
+    setup_attach_host: true
+    setup_datastore: true
+
+- name: Gather Info if no first-class disk exists
+  vmware_first_class_disk_info: &disk_info
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    datastore_name: "{{ rw_datastore }}"
+    validate_certs: false
+  register: disk_info
+  delegate_to: localhost
+
+- debug:
+    var: disk_info
+
+- assert:
+    that:
+      - "disk_info['first_class_disks'] == []"
+
+- name: Create first-class disk through vCenter
+  vmware_first_class_disk:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    disk_name: "firstclassdisk"
+    size: 1GB
+    datastore_name: "{{ rw_datastore }}"
+    state: present
+    validate_certs: false
+
+- name: Gather Info of the generated first-class disk
+  vmware_first_class_disk_info:
+    <<: *disk_info
+  register: disk_info
+
+- name: assert for first-class disk info
+  assert:
+    that:
+      - "(disk_info['first_class_disks'] | length) == 1"
+      - "disk_info['first_class_disks'][0]['name'] == 'firstclassdisk'"
+      - "disk_info['first_class_disks'][0]['datastore_name'] == rw_datastore"
+      - "disk_info['first_class_disks'][0]['size_mb'] == 1024"
+      - "disk_info['first_class_disks'][0]['consumption_type'] is defined"
+      - "disk_info['first_class_disks'][0]['descriptor_version'] is defined"
+      - "disk_info['first_class_disks'][0]['consumer_ids'] is defined"
+
+- name: Create a second first-class disk through vCenter
+  vmware_first_class_disk:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    disk_name: "secondclassdisk"
+    size: 1GB
+    datastore_name: "{{ rw_datastore }}"
+    state: present
+    validate_certs: false
+
+- name: Gather Info of the two generated first-class disk
+  vmware_first_class_disk_info:
+    <<: *disk_info
+  register: disk_info
+
+- name: assert for first-class disk info
+  assert:
+    that:
+      - "(disk_info['first_class_disks'] | length) == 2"
+
+- name: Gather Info of first-class disk 'firstclassdisk'
+  vmware_first_class_disk_info:
+    hostname: "{{ vcenter_hostname }}"
+    username: "{{ vcenter_username }}"
+    password: "{{ vcenter_password }}"
+    datastore_name: "{{ rw_datastore }}"
+    disk_name: "firstclassdisk"
+    validate_certs: false
+  register: disk_info
+  delegate_to: localhost
+
+- name: assert for first-class disk info
+  assert:
+    that:
+      - "(disk_info['first_class_disks'] | length) == 1"
+      - "disk_info['first_class_disks'][0]['name'] == 'firstclassdisk'"
+      - "disk_info['first_class_disks'][0]['datastore_name'] == rw_datastore"
+      - "disk_info['first_class_disks'][0]['size_mb'] == 1024"
+      - "disk_info['first_class_disks'][0]['consumption_type'] is defined"
+      - "disk_info['first_class_disks'][0]['descriptor_version'] is defined"
+      - "disk_info['first_class_disks'][0]['consumer_ids'] is defined"
\ No newline at end of file