From 0bc2e6795dbd2e80d6fb3920806a14ee95801d4b Mon Sep 17 00:00:00 2001 From: Christian Kotte Date: Thu, 11 Oct 2018 12:22:30 +0200 Subject: [PATCH] VMware: Gather extended datastore facts (#46546) * Add datacenter_name alias * Add check mode support * Add extended datastore information --- .../cloud/vmware/vmware_datastore_facts.py | 143 ++++++++++++++---- .../vmware_datastore_facts/tasks/main.yml | 42 ++++- 2 files changed, 151 insertions(+), 34 deletions(-) diff --git a/lib/ansible/modules/cloud/vmware/vmware_datastore_facts.py b/lib/ansible/modules/cloud/vmware/vmware_datastore_facts.py index 5f86f8723c1..1fd9ccbcdf2 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_datastore_facts.py +++ b/lib/ansible/modules/cloud/vmware/vmware_datastore_facts.py @@ -40,12 +40,27 @@ options: - Datacenter to search for datastores. - This parameter is required, if C(cluster) is not supplied. required: False + aliases: ['datacenter_name'] cluster: description: - Cluster to search for datastores. - If set, facts of datastores belonging this clusters will be returned. - This parameter is required, if C(datacenter) is not supplied. required: False + gather_nfs_mount_info: + description: + - Gather mount information of NFS datastores. + - Disabled per default because this slows down the execution if you have a lot of datastores. + type: bool + default: false + version_added: 2.8 + gather_vmfs_mount_info: + description: + - Gather mount information of VMFS datastores. + - Disabled per default because this slows down the execution if you have a lot of datastores. + type: bool + default: false + version_added: 2.8 extends_documentation_fragment: vmware.documentation ''' @@ -55,7 +70,7 @@ EXAMPLES = ''' hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' - datacenter: '{{ datacenter_name }}' + datacenter_name: '{{ datacenter_name }}' validate_certs: no delegate_to: localhost register: facts @@ -65,7 +80,7 @@ EXAMPLES = ''' hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' - datacenter: '{{ datacenter_name }}' + datacenter_name: '{{ datacenter_name }}' name: datastore1 delegate_to: localhost register: facts @@ -88,7 +103,25 @@ datastores: "provisioned": 12289211488, "type": "VMFS", "uncommitted": 9246243936, - "url": "ds:///vmfs/volumes/5a69b18a-c03cd88c-36ae-5254001249ce/" + "url": "ds:///vmfs/volumes/5a69b18a-c03cd88c-36ae-5254001249ce/", + "vmfs_blockSize": 1024, + "vmfs_uuid": "5a69b18a-c03cd88c-36ae-5254001249ce", + "vmfs_version": "6.81" + }, + { + "accessible": true, + "capacity": 5497558138880, + "datastore_cluster": "datacluster0", + "freeSpace": 4279000641536, + "maintenanceMode": "normal", + "multipleHostAccess": true, + "name": "datastore3", + "nfs_path": "/vol/datastore3", + "nfs_server": "nfs_server1", + "provisioned": 1708109410304, + "type": "NFS", + "uncommitted": 489551912960, + "url": "ds:///vmfs/volumes/420b3e73-67070776/" }, ] """ @@ -103,6 +136,70 @@ from ansible.module_utils.vmware import (PyVmomi, vmware_argument_spec, get_all_ find_cluster_by_name, get_parent_datacenter) +class VMwareHostDatastore(PyVmomi): + """ This class populates the datastore list """ + def __init__(self, module): + super(VMwareHostDatastore, self).__init__(module) + self.gather_nfs_mount_info = self.module.params['gather_nfs_mount_info'] + self.gather_vmfs_mount_info = self.module.params['gather_vmfs_mount_info'] + + def check_datastore_host(self, esxi_host, datastore): + """ Get all datastores of specified ESXi host """ + esxi = self.find_hostsystem_by_name(esxi_host) + if esxi is None: + self.module.fail_json(msg="Failed to find ESXi hostname %s " % esxi_host) + storage_system = esxi.configManager.storageSystem + host_file_sys_vol_mount_info = storage_system.fileSystemVolumeInfo.mountInfo + for host_mount_info in host_file_sys_vol_mount_info: + if host_mount_info.volume.name == datastore: + return host_mount_info + return None + + def build_datastore_list(self, datastore_list): + """ Build list with datastores """ + datastores = list() + for datastore in datastore_list: + summary = datastore.summary + datastore_summary = dict() + datastore_summary['accessible'] = summary.accessible + datastore_summary['capacity'] = summary.capacity + datastore_summary['name'] = summary.name + datastore_summary['freeSpace'] = summary.freeSpace + datastore_summary['maintenanceMode'] = summary.maintenanceMode + datastore_summary['multipleHostAccess'] = summary.multipleHostAccess + datastore_summary['type'] = summary.type + if self.gather_nfs_mount_info or self.gather_vmfs_mount_info: + if self.gather_nfs_mount_info and summary.type.startswith("NFS"): + # get mount info from the first ESXi host attached to this NFS datastore + host_mount_info = self.check_datastore_host(summary.datastore.host[0].key.name, summary.name) + datastore_summary['nfs_server'] = host_mount_info.volume.remoteHost + datastore_summary['nfs_path'] = host_mount_info.volume.remotePath + if self.gather_vmfs_mount_info and summary.type == "VMFS": + # get mount info from the first ESXi host attached to this VMFS datastore + host_mount_info = self.check_datastore_host(summary.datastore.host[0].key.name, summary.name) + datastore_summary['vmfs_blockSize'] = host_mount_info.volume.blockSize + datastore_summary['vmfs_version'] = host_mount_info.volume.version + datastore_summary['vmfs_uuid'] = host_mount_info.volume.uuid + # vcsim does not return uncommitted + if not summary.uncommitted: + summary.uncommitted = 0 + datastore_summary['uncommitted'] = summary.uncommitted + datastore_summary['url'] = summary.url + # Calculated values + datastore_summary['provisioned'] = summary.capacity - summary.freeSpace + summary.uncommitted + datastore_summary['datastore_cluster'] = 'N/A' + if isinstance(datastore.parent, vim.StoragePod): + datastore_summary['datastore_cluster'] = datastore.parent.name + + if self.module.params['name']: + if datastore_summary['name'] == self.module.params['name']: + datastores.extend([datastore_summary]) + else: + datastores.extend([datastore_summary]) + + return datastores + + class PyVmomiCache(object): """ This class caches references to objects which are requested multiples times but not modified """ def __init__(self, content, dc_name=None): @@ -129,15 +226,18 @@ class PyVmomiCache(object): class PyVmomiHelper(PyVmomi): + """ This class gets datastores """ def __init__(self, module): super(PyVmomiHelper, self).__init__(module) self.cache = PyVmomiCache(self.content, dc_name=self.params['datacenter']) def lookup_datastore(self): + """ Get datastore(s) per ESXi host or vCenter server """ datastores = self.cache.get_all_objs(self.content, [vim.Datastore], confine_to_datacenter=True) return datastores def lookup_datastore_by_cluster(self): + """ Get datastore(s) per cluster """ cluster = find_cluster_by_name(self.content, self.params['cluster']) if not cluster: self.module.fail_json(msg='Failed to find cluster "%(cluster)s"' % self.params) @@ -146,16 +246,20 @@ class PyVmomiHelper(PyVmomi): def main(): + """ Main """ argument_spec = vmware_argument_spec() argument_spec.update( name=dict(type='str'), - datacenter=dict(type='str'), - cluster=dict(type='str') + datacenter=dict(type='str', aliases=['datacenter_name']), + cluster=dict(type='str'), + gather_nfs_mount_info=dict(type='bool', default=False), + gather_vmfs_mount_info=dict(type='bool', default=False) ) module = AnsibleModule(argument_spec=argument_spec, required_one_of=[ ['cluster', 'datacenter'], ], + supports_check_mode=True ) result = dict(changed=False) @@ -166,33 +270,8 @@ def main(): else: dxs = pyv.lookup_datastore() - datastores = list() - for ds in dxs: - summary = ds.summary - dds = dict() - dds['accessible'] = summary.accessible - dds['capacity'] = summary.capacity - dds['name'] = summary.name - dds['freeSpace'] = summary.freeSpace - dds['maintenanceMode'] = summary.maintenanceMode - dds['multipleHostAccess'] = summary.multipleHostAccess - dds['type'] = summary.type - # vcsim does not return uncommitted - if not summary.uncommitted: - summary.uncommitted = 0 - dds['uncommitted'] = summary.uncommitted - dds['url'] = summary.url - # Calculated values - dds['provisioned'] = summary.capacity - summary.freeSpace + summary.uncommitted - dds['datastore_cluster'] = 'N/A' - if isinstance(ds.parent, vim.StoragePod): - dds['datastore_cluster'] = ds.parent.name - - if module.params['name']: - if dds['name'] == module.params['name']: - datastores.extend([dds]) - else: - datastores.extend([dds]) + vmware_host_datastore = VMwareHostDatastore(module) + datastores = vmware_host_datastore.build_datastore_list(dxs) result['datastores'] = datastores diff --git a/test/integration/targets/vmware_datastore_facts/tasks/main.yml b/test/integration/targets/vmware_datastore_facts/tasks/main.yml index 7f2eb1a2a15..dab779f9507 100644 --- a/test/integration/targets/vmware_datastore_facts/tasks/main.yml +++ b/test/integration/targets/vmware_datastore_facts/tasks/main.yml @@ -38,7 +38,7 @@ - set_fact: cl1: "{{ clusters['json'][0] }}" - + - name: get a list of Datacenters from vcsim uri: url: http://{{ vcsim }}:5000/govc_find?filter=DC @@ -54,7 +54,7 @@ - set_fact: ds1: "{{ datastores['json'][0] }}" - + # Testcase 0001: Get a full list of datastores in a datacenter - name: get list of facts about datastores vmware_datastore_facts: @@ -109,3 +109,41 @@ that: - "datastore_facts_0003['datastores'][0]['name'] == ds1 | basename" - "datastore_facts_0003['datastores'][0]['capacity'] is defined" + +- name: get list of extended facts about one datastore + vmware_datastore_facts: + validate_certs: False + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + datacenter: "{{ dc1 | basename }}" + name: "{{ ds1 | basename }}" + gather_nfs_mount_info: True + gather_vmfs_mount_info: True + register: datastore_facts_0004 + +- debug: + msg: "{{ datastore_facts_0004 }}" + +- assert: + that: + - "datastore_facts_0004['datastores'][0]['name'] == ds1 | basename" + - "datastore_facts_0004['datastores'][0]['capacity'] is defined" + +- name: get list of facts about one datastore in check mode + vmware_datastore_facts: + validate_certs: False + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + datacenter: "{{ dc1 | basename }}" + name: "{{ ds1 | basename }}" + register: datastore_facts_0005 + +- debug: + msg: "{{ datastore_facts_0005 }}" + +- assert: + that: + - "datastore_facts_0005['datastores'][0]['name'] == ds1 | basename" + - "datastore_facts_0005['datastores'][0]['capacity'] is defined"