VMware: Improve module vmware_host_datastore (#45652)
* Don't execute mount or umount in check mode * The datastore was mounted or unmounted when you execute the playbook in check mode. * Test if running in check mode before mounting or unmounting the datastore. * Add support for NFS v4.1 datastores
This commit is contained in:
parent
23b60d843e
commit
849912c01b
1 changed files with 89 additions and 51 deletions
|
@ -19,14 +19,17 @@ module: vmware_host_datastore
|
||||||
short_description: Manage a datastore on ESXi host
|
short_description: Manage a datastore on ESXi host
|
||||||
description:
|
description:
|
||||||
- This module can be used to mount/umount datastore on ESXi host.
|
- This module can be used to mount/umount datastore on ESXi host.
|
||||||
- This module only support NFS/VMFS type of datastores.
|
- This module only supports NFS (NFS v3 or NFS v4.1) and VMFS datastores.
|
||||||
- For VMFS datastore, available device must already be connected on ESXi host.
|
- For VMFS datastore, available device must already be connected on ESXi host.
|
||||||
- All parameters and VMware object names are case sensitive.
|
- All parameters and VMware object names are case sensitive.
|
||||||
version_added: '2.5'
|
version_added: '2.5'
|
||||||
author:
|
author:
|
||||||
- Ludovic Rivallain (@lrivallain) <ludovic.rivallain@gmail.com>
|
- Ludovic Rivallain (@lrivallain) <ludovic.rivallain@gmail.com>
|
||||||
|
- Christian Kotte (@ckotte) <christian.kotte@gmx.de>
|
||||||
notes:
|
notes:
|
||||||
- Tested on vSphere 6.0 and 6.5
|
- Tested on vSphere 6.0 and 6.5
|
||||||
|
- NFS v4.1 tested on vSphere 6.5
|
||||||
|
- Kerberos authentication with NFS v4.1 isn't implemented
|
||||||
requirements:
|
requirements:
|
||||||
- python >= 2.6
|
- python >= 2.6
|
||||||
- PyVmomi
|
- PyVmomi
|
||||||
|
@ -41,21 +44,22 @@ options:
|
||||||
required: true
|
required: true
|
||||||
datastore_type:
|
datastore_type:
|
||||||
description:
|
description:
|
||||||
- Type of the datastore to configure (nfs/vmfs).
|
- Type of the datastore to configure (nfs/nfs41/vmfs).
|
||||||
required: true
|
required: true
|
||||||
choices: [ 'nfs', 'vmfs' ]
|
choices: [ 'nfs', 'nfs41', 'vmfs' ]
|
||||||
nfs_server:
|
nfs_server:
|
||||||
description:
|
description:
|
||||||
- NFS host serving nfs datastore.
|
- NFS host serving nfs datastore.
|
||||||
- Required if datastore type is set to C(nfs) and state is set to C(present), else unused.
|
- Required if datastore type is set to C(nfs)/C(nfs41) and state is set to C(present), else unused.
|
||||||
|
- Two or more servers can be defined if datastore type is set to C(nfs41)
|
||||||
nfs_path:
|
nfs_path:
|
||||||
description:
|
description:
|
||||||
- Resource path on NFS host.
|
- Resource path on NFS host.
|
||||||
- Required if datastore type is set to C(nfs) and state is set to C(present), else unused.
|
- Required if datastore type is set to C(nfs)/C(nfs41) and state is set to C(present), else unused.
|
||||||
nfs_ro:
|
nfs_ro:
|
||||||
description:
|
description:
|
||||||
- ReadOnly or ReadWrite mount.
|
- ReadOnly or ReadWrite mount.
|
||||||
- Unused if datastore type is not set to C(nfs) and state is not set to C(present).
|
- Unused if datastore type is not set to C(nfs)/C(nfs41) and state is not set to C(present).
|
||||||
default: False
|
default: False
|
||||||
type: bool
|
type: bool
|
||||||
vmfs_device_name:
|
vmfs_device_name:
|
||||||
|
@ -112,6 +116,24 @@ EXAMPLES = r'''
|
||||||
- { 'name': 'NasDS_vol01', 'server': 'nas01', 'path': '/mnt/vol01', 'type': 'nfs'}
|
- { 'name': 'NasDS_vol01', 'server': 'nas01', 'path': '/mnt/vol01', 'type': 'nfs'}
|
||||||
- { 'name': 'NasDS_vol02', 'server': 'nas01', 'path': '/mnt/vol02', 'type': 'nfs'}
|
- { 'name': 'NasDS_vol02', 'server': 'nas01', 'path': '/mnt/vol02', 'type': 'nfs'}
|
||||||
|
|
||||||
|
- name: Mount NFS v4.1 datastores to ESXi
|
||||||
|
vmware_host_datastore:
|
||||||
|
hostname: '{{ vcenter_hostname }}'
|
||||||
|
username: '{{ vcenter_username }}'
|
||||||
|
password: '{{ vcenter_password }}'
|
||||||
|
datacenter_name: '{{ datacenter }}'
|
||||||
|
datastore_name: '{{ item.name }}'
|
||||||
|
datastore_type: '{{ item.type }}'
|
||||||
|
nfs_server: '{{ item.server }}'
|
||||||
|
nfs_path: '{{ item.path }}'
|
||||||
|
nfs_ro: no
|
||||||
|
esxi_hostname: '{{ inventory_hostname }}'
|
||||||
|
state: present
|
||||||
|
delegate_to: localhost
|
||||||
|
with_items:
|
||||||
|
- { 'name': 'NasDS_vol03', 'server': 'nas01,nas02', 'path': '/mnt/vol01', 'type': 'nfs41'}
|
||||||
|
- { 'name': 'NasDS_vol04', 'server': 'nas01,nas02', 'path': '/mnt/vol02', 'type': 'nfs41'}
|
||||||
|
|
||||||
- name: Remove/Umount Datastores from ESXi
|
- name: Remove/Umount Datastores from ESXi
|
||||||
vmware_host_datastore:
|
vmware_host_datastore:
|
||||||
hostname: '{{ vcenter_hostname }}'
|
hostname: '{{ vcenter_hostname }}'
|
||||||
|
@ -189,61 +211,73 @@ class VMwareHostDatastore(PyVmomi):
|
||||||
ds = find_datastore_by_name(self.content, self.datastore_name)
|
ds = find_datastore_by_name(self.content, self.datastore_name)
|
||||||
if not ds:
|
if not ds:
|
||||||
self.module.fail_json(msg="No datastore found with name %s" % self.datastore_name)
|
self.module.fail_json(msg="No datastore found with name %s" % self.datastore_name)
|
||||||
error_message_umount = "Cannot umount datastore %s from host %s" % (self.datastore_name, self.esxi_hostname)
|
if self.module.check_mode is False:
|
||||||
try:
|
error_message_umount = "Cannot umount datastore %s from host %s" % (self.datastore_name, self.esxi_hostname)
|
||||||
self.esxi.configManager.datastoreSystem.RemoveDatastore(ds)
|
try:
|
||||||
except (vim.fault.NotFound, vim.fault.HostConfigFault, vim.fault.ResourceInUse) as fault:
|
self.esxi.configManager.datastoreSystem.RemoveDatastore(ds)
|
||||||
self.module.fail_json(msg="%s: %s" % (error_message_umount, to_native(fault.msg)))
|
except (vim.fault.NotFound, vim.fault.HostConfigFault, vim.fault.ResourceInUse) as fault:
|
||||||
except Exception as e:
|
self.module.fail_json(msg="%s: %s" % (error_message_umount, to_native(fault.msg)))
|
||||||
self.module.fail_json(msg="%s: %s" % (error_message_umount, to_native(e)))
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="%s: %s" % (error_message_umount, to_native(e)))
|
||||||
self.module.exit_json(changed=True, result="Datastore %s on host %s" % (self.datastore_name, self.esxi_hostname))
|
self.module.exit_json(changed=True, result="Datastore %s on host %s" % (self.datastore_name, self.esxi_hostname))
|
||||||
|
|
||||||
def mount_datastore_host(self):
|
def mount_datastore_host(self):
|
||||||
if self.datastore_type == 'nfs':
|
if self.datastore_type == 'nfs' or self.datastore_type == 'nfs41':
|
||||||
self.mount_nfs_datastore_host()
|
self.mount_nfs_datastore_host()
|
||||||
if self.datastore_type == 'vmfs':
|
if self.datastore_type == 'vmfs':
|
||||||
self.mount_vmfs_datastore_host()
|
self.mount_vmfs_datastore_host()
|
||||||
|
|
||||||
def mount_nfs_datastore_host(self):
|
def mount_nfs_datastore_host(self):
|
||||||
mnt_specs = vim.host.NasVolume.Specification()
|
if self.module.check_mode is False:
|
||||||
mnt_specs.remoteHost = self.nfs_server
|
mnt_specs = vim.host.NasVolume.Specification()
|
||||||
mnt_specs.remotePath = self.nfs_path
|
# NFS v3
|
||||||
mnt_specs.localPath = self.datastore_name
|
if self.datastore_type == 'nfs':
|
||||||
if self.nfs_ro:
|
mnt_specs.type = "NFS"
|
||||||
mnt_specs.accessMode = "readOnly"
|
mnt_specs.remoteHost = self.nfs_server
|
||||||
else:
|
# NFS v4.1
|
||||||
mnt_specs.accessMode = "readWrite"
|
if self.datastore_type == 'nfs41':
|
||||||
error_message_mount = "Cannot mount datastore %s on host %s" % (self.datastore_name, self.esxi_hostname)
|
mnt_specs.type = "NFS41"
|
||||||
try:
|
# remoteHost needs to be set to a non-empty string, but the value is not used
|
||||||
ds = self.esxi.configManager.datastoreSystem.CreateNasDatastore(mnt_specs)
|
mnt_specs.remoteHost = "something"
|
||||||
if not ds:
|
mnt_specs.remoteHostNames = [self.nfs_server]
|
||||||
self.module.fail_json(msg=error_message_mount)
|
mnt_specs.remotePath = self.nfs_path
|
||||||
except (vim.fault.NotFound, vim.fault.DuplicateName,
|
mnt_specs.localPath = self.datastore_name
|
||||||
vim.fault.AlreadyExists, vim.fault.HostConfigFault,
|
if self.nfs_ro:
|
||||||
vmodl.fault.InvalidArgument, vim.fault.NoVirtualNic,
|
mnt_specs.accessMode = "readOnly"
|
||||||
vim.fault.NoGateway) as fault:
|
else:
|
||||||
self.module.fail_json(msg="%s: %s" % (error_message_mount, to_native(fault.msg)))
|
mnt_specs.accessMode = "readWrite"
|
||||||
except Exception as e:
|
error_message_mount = "Cannot mount datastore %s on host %s" % (self.datastore_name, self.esxi_hostname)
|
||||||
self.module.fail_json(msg="%s : %s" % (error_message_mount, to_native(e)))
|
try:
|
||||||
|
ds = self.esxi.configManager.datastoreSystem.CreateNasDatastore(mnt_specs)
|
||||||
|
if not ds:
|
||||||
|
self.module.fail_json(msg=error_message_mount)
|
||||||
|
except (vim.fault.NotFound, vim.fault.DuplicateName,
|
||||||
|
vim.fault.AlreadyExists, vim.fault.HostConfigFault,
|
||||||
|
vmodl.fault.InvalidArgument, vim.fault.NoVirtualNic,
|
||||||
|
vim.fault.NoGateway) as fault:
|
||||||
|
self.module.fail_json(msg="%s: %s" % (error_message_mount, to_native(fault.msg)))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="%s : %s" % (error_message_mount, to_native(e)))
|
||||||
self.module.exit_json(changed=True, result="Datastore %s on host %s" % (self.datastore_name, self.esxi_hostname))
|
self.module.exit_json(changed=True, result="Datastore %s on host %s" % (self.datastore_name, self.esxi_hostname))
|
||||||
|
|
||||||
def mount_vmfs_datastore_host(self):
|
def mount_vmfs_datastore_host(self):
|
||||||
ds_path = "/vmfs/devices/disks/" + str(self.vmfs_device_name)
|
if self.module.check_mode is False:
|
||||||
host_ds_system = self.esxi.configManager.datastoreSystem
|
ds_path = "/vmfs/devices/disks/" + str(self.vmfs_device_name)
|
||||||
ds_system = vim.host.DatastoreSystem
|
host_ds_system = self.esxi.configManager.datastoreSystem
|
||||||
error_message_mount = "Cannot mount datastore %s on host %s" % (self.datastore_name, self.esxi_hostname)
|
ds_system = vim.host.DatastoreSystem
|
||||||
try:
|
error_message_mount = "Cannot mount datastore %s on host %s" % (self.datastore_name, self.esxi_hostname)
|
||||||
vmfs_ds_options = ds_system.QueryVmfsDatastoreCreateOptions(host_ds_system,
|
try:
|
||||||
ds_path,
|
vmfs_ds_options = ds_system.QueryVmfsDatastoreCreateOptions(host_ds_system,
|
||||||
self.vmfs_version)
|
ds_path,
|
||||||
vmfs_ds_options[0].spec.vmfs.volumeName = self.datastore_name
|
self.vmfs_version)
|
||||||
ds = ds_system.CreateVmfsDatastore(host_ds_system,
|
vmfs_ds_options[0].spec.vmfs.volumeName = self.datastore_name
|
||||||
vmfs_ds_options[0].spec)
|
ds = ds_system.CreateVmfsDatastore(host_ds_system,
|
||||||
except (vim.fault.NotFound, vim.fault.DuplicateName,
|
vmfs_ds_options[0].spec)
|
||||||
vim.fault.HostConfigFault, vmodl.fault.InvalidArgument) as fault:
|
except (vim.fault.NotFound, vim.fault.DuplicateName,
|
||||||
self.module.fail_json(msg="%s : %s" % (error_message_mount, to_native(fault.msg)))
|
vim.fault.HostConfigFault, vmodl.fault.InvalidArgument) as fault:
|
||||||
except Exception as e:
|
self.module.fail_json(msg="%s : %s" % (error_message_mount, to_native(fault.msg)))
|
||||||
self.module.fail_json(msg="%s : %s" % (error_message_mount, to_native(e)))
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg="%s : %s" % (error_message_mount, to_native(e)))
|
||||||
self.module.exit_json(changed=True, result="Datastore %s on host %s" % (self.datastore_name, self.esxi_hostname))
|
self.module.exit_json(changed=True, result="Datastore %s on host %s" % (self.datastore_name, self.esxi_hostname))
|
||||||
|
|
||||||
|
|
||||||
|
@ -252,7 +286,7 @@ def main():
|
||||||
argument_spec.update(
|
argument_spec.update(
|
||||||
datacenter_name=dict(type='str', required=True),
|
datacenter_name=dict(type='str', required=True),
|
||||||
datastore_name=dict(type='str', required=True),
|
datastore_name=dict(type='str', required=True),
|
||||||
datastore_type=dict(type='str', choices=['nfs', 'vmfs']),
|
datastore_type=dict(type='str', choices=['nfs', 'nfs41', 'vmfs']),
|
||||||
nfs_server=dict(type='str'),
|
nfs_server=dict(type='str'),
|
||||||
nfs_path=dict(type='str'),
|
nfs_path=dict(type='str'),
|
||||||
nfs_ro=dict(type='bool', default=False),
|
nfs_ro=dict(type='bool', default=False),
|
||||||
|
@ -276,6 +310,10 @@ def main():
|
||||||
msg = "Missing nfs_server with datastore_type = nfs"
|
msg = "Missing nfs_server with datastore_type = nfs"
|
||||||
module.fail_json(msg=msg)
|
module.fail_json(msg=msg)
|
||||||
|
|
||||||
|
if module.params['datastore_type'] == 'nfs41' and not module.params['nfs_server']:
|
||||||
|
msg = "Missing nfs_server with datastore_type = nfs41"
|
||||||
|
module.fail_json(msg=msg)
|
||||||
|
|
||||||
if module.params['datastore_type'] == 'vmfs' and not module.params['vmfs_device_name']:
|
if module.params['datastore_type'] == 'vmfs' and not module.params['vmfs_device_name']:
|
||||||
msg = "Missing vmfs_device_name with datastore_type = vmfs"
|
msg = "Missing vmfs_device_name with datastore_type = vmfs"
|
||||||
module.fail_json(msg=msg)
|
module.fail_json(msg=msg)
|
||||||
|
|
Loading…
Reference in a new issue