mirror of https://github.com/ansible/ansible.git
vmware: Add new module 'vmware_datastore_host' (#35004)
* vmware: Add new module 'vmware_datastore_host' Signed-off-by: Ludovic Rivallain <ludovic.rivallain@gmail.com> Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>pull/35411/head
parent
82e6a91c3f
commit
fe57285f9a
@ -0,0 +1,284 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright (c) 2018 Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
ANSIBLE_METADATA = {
|
||||||
|
'metadata_version': '1.1',
|
||||||
|
'status': ['preview'],
|
||||||
|
'supported_by': 'community'
|
||||||
|
}
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: vmware_host_datastore
|
||||||
|
short_description: Manage a datastore on ESXi host
|
||||||
|
description:
|
||||||
|
- This module can be used to mount/umount datastore on ESXi host.
|
||||||
|
- This module only support NFS/VMFS type of datastores.
|
||||||
|
- For VMFS datastore, available device must already be connected on ESXi host.
|
||||||
|
- All parameters and VMware object names are case sensitive.
|
||||||
|
version_added: '2.5'
|
||||||
|
author:
|
||||||
|
- Ludovic Rivallain <ludovic.rivallain@gmail.com> @lrivallain
|
||||||
|
notes:
|
||||||
|
- Tested on vSphere 6.0 and 6.5
|
||||||
|
requirements:
|
||||||
|
- python >= 2.6
|
||||||
|
- PyVmomi
|
||||||
|
options:
|
||||||
|
datacenter_name:
|
||||||
|
description:
|
||||||
|
- Name of the datacenter to add the datastore.
|
||||||
|
required: true
|
||||||
|
datastore_name:
|
||||||
|
description:
|
||||||
|
- Name of the datastore to add/remove.
|
||||||
|
required: true
|
||||||
|
datastore_type:
|
||||||
|
description:
|
||||||
|
- Type of the datastore to configure (nfs/vmfs).
|
||||||
|
required: true
|
||||||
|
nfs_server:
|
||||||
|
description:
|
||||||
|
- NFS host serving nfs datastore.
|
||||||
|
- Required if datastore type is set to C(nfs) and state is set to C(present), else unused.
|
||||||
|
nfs_path:
|
||||||
|
description:
|
||||||
|
- Resource path on NFS host.
|
||||||
|
- Required if datastore type is set to C(nfs) and state is set to C(present), else unused.
|
||||||
|
nfs_ro:
|
||||||
|
description:
|
||||||
|
- ReadOnly or ReadWrite mount.
|
||||||
|
- Unused if datastore type is not set to C(nfs) and state is not set to C(present).
|
||||||
|
default: False
|
||||||
|
vmfs_device_name:
|
||||||
|
description:
|
||||||
|
- Name of the device to be used as VMFS datastore.
|
||||||
|
- Required for VMFS datastore type and state is set to C(present), else unused.
|
||||||
|
vmfs_version:
|
||||||
|
description:
|
||||||
|
- VMFS version to use for datastore creation.
|
||||||
|
- Unused if datastore type is not set to C(vmfs) and state is not set to C(present).
|
||||||
|
esxi_hostname:
|
||||||
|
description:
|
||||||
|
- ESXi hostname to manage the datastore.
|
||||||
|
required: true
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- "present: Mount datastore on host if datastore is absent else do nothing."
|
||||||
|
- "absent: Umount datastore if datastore is present else do nothing."
|
||||||
|
default: present
|
||||||
|
choices: [ present, absent ]
|
||||||
|
extends_documentation_fragment: vmware.documentation
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = r'''
|
||||||
|
- name: Mount VMFS datastores to ESXi
|
||||||
|
vmware_host_datastore:
|
||||||
|
hostname: '{{ vcenter_hostname }}'
|
||||||
|
username: '{{ vcenter_user }}'
|
||||||
|
password: '{{ vcenter_pass }}'
|
||||||
|
datacenter_name: '{{ datacenter }}'
|
||||||
|
datastore_name: '{{ item.name }}'
|
||||||
|
datastore_type: '{{ item.type }}'
|
||||||
|
vmfs_device_name: 'naa.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
||||||
|
vmfs_version: 6
|
||||||
|
esxi_hostname: '{{ inventory_hostname }}'
|
||||||
|
state: present
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: Mount NFS datastores to ESXi
|
||||||
|
vmware_host_datastore:
|
||||||
|
hostname: '{{ vcenter_hostname }}'
|
||||||
|
username: '{{ vcenter_user }}'
|
||||||
|
password: '{{ vcenter_pass }}'
|
||||||
|
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_vol01', 'server': 'nas01', 'path': '/mnt/vol01', 'type': 'nfs'}
|
||||||
|
- { 'name': 'NasDS_vol02', 'server': 'nas01', 'path': '/mnt/vol02', 'type': 'nfs'}
|
||||||
|
|
||||||
|
- name: Remove/Umount Datastores from ESXi
|
||||||
|
vmware_host_datastore:
|
||||||
|
hostname: '{{ vcenter_hostname }}'
|
||||||
|
username: '{{ vcenter_user }}'
|
||||||
|
password: '{{ vcenter_pass }}'
|
||||||
|
datacenter_name: '{{ datacenter }}'
|
||||||
|
datastore_name: NasDS_vol01
|
||||||
|
esxi_hostname: '{{ inventory_hostname }}'
|
||||||
|
state: absent
|
||||||
|
delegate_to: localhost
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = r'''
|
||||||
|
'''
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pyVmomi import vim, vmodl
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.vmware import vmware_argument_spec, PyVmomi, find_datastore_by_name
|
||||||
|
from ansible.module_utils._text import to_native
|
||||||
|
|
||||||
|
|
||||||
|
class VMwareHostDatastore(PyVmomi):
|
||||||
|
def __init__(self, module):
|
||||||
|
super(VMwareHostDatastore, self).__init__(module)
|
||||||
|
|
||||||
|
self.datacenter_name = module.params['datacenter_name']
|
||||||
|
self.datastore_name = module.params['datastore_name']
|
||||||
|
self.datastore_type = module.params['datastore_type']
|
||||||
|
self.nfs_server = module.params['nfs_server']
|
||||||
|
self.nfs_path = module.params['nfs_path']
|
||||||
|
self.nfs_ro = module.params['nfs_ro']
|
||||||
|
self.vmfs_device_name = module.params['vmfs_device_name']
|
||||||
|
self.vmfs_version = module.params['vmfs_version']
|
||||||
|
self.esxi_hostname = module.params['esxi_hostname']
|
||||||
|
self.state = module.params['state']
|
||||||
|
|
||||||
|
self.esxi = self.find_hostsystem_by_name(self.esxi_hostname)
|
||||||
|
if self.esxi is None:
|
||||||
|
self.module.fail_json(msg="Failed to find ESXi hostname %s " % self.esxi_hostname)
|
||||||
|
|
||||||
|
def process_state(self):
|
||||||
|
ds_states = {
|
||||||
|
'absent': {
|
||||||
|
'present': self.umount_datastore_host,
|
||||||
|
'absent': self.state_exit_unchanged,
|
||||||
|
},
|
||||||
|
'present': {
|
||||||
|
'present': self.state_exit_unchanged,
|
||||||
|
'absent': self.mount_datastore_host,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
ds_states[self.state][self.check_datastore_host_state()]()
|
||||||
|
except (vmodl.RuntimeFault, vmodl.MethodFault) as vmodl_fault:
|
||||||
|
self.module.fail_json(msg=to_native(vmodl_fault.msg))
|
||||||
|
except Exception as e:
|
||||||
|
self.module.fail_json(msg=to_native(e))
|
||||||
|
|
||||||
|
def state_exit_unchanged(self):
|
||||||
|
self.module.exit_json(changed=False)
|
||||||
|
|
||||||
|
def check_datastore_host_state(self):
|
||||||
|
storage_system = self.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 == self.datastore_name:
|
||||||
|
return 'present'
|
||||||
|
return 'absent'
|
||||||
|
|
||||||
|
def umount_datastore_host(self):
|
||||||
|
ds = find_datastore_by_name(self.content, self.datastore_name)
|
||||||
|
if not ds:
|
||||||
|
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)
|
||||||
|
try:
|
||||||
|
self.esxi.configManager.datastoreSystem.RemoveDatastore(ds)
|
||||||
|
except (vim.fault.NotFound, vim.fault.HostConfigFault, vim.fault.ResourceInUse) as fault:
|
||||||
|
self.module.fail_json(msg="%s: %s" % (error_message_umount, to_native(fault.msg)))
|
||||||
|
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))
|
||||||
|
|
||||||
|
def mount_datastore_host(self):
|
||||||
|
if self.datastore_type == 'nfs':
|
||||||
|
self.mount_nfs_datastore_host()
|
||||||
|
if self.datastore_type == 'vmfs':
|
||||||
|
self.mount_vmfs_datastore_host()
|
||||||
|
|
||||||
|
def mount_nfs_datastore_host(self):
|
||||||
|
mnt_specs = vim.host.NasVolume.Specification()
|
||||||
|
mnt_specs.remoteHost = self.nfs_server
|
||||||
|
mnt_specs.remotePath = self.nfs_path
|
||||||
|
mnt_specs.localPath = self.datastore_name
|
||||||
|
if self.nfs_ro:
|
||||||
|
mnt_specs.accessMode = "readOnly"
|
||||||
|
else:
|
||||||
|
mnt_specs.accessMode = "readWrite"
|
||||||
|
error_message_mount = "Cannot mount datastore %s on host %s" % (self.datastore_name, self.esxi_hostname)
|
||||||
|
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))
|
||||||
|
|
||||||
|
def mount_vmfs_datastore_host(self):
|
||||||
|
ds_path = "/vmfs/devices/disks/" + str(self.vmfs_device_name)
|
||||||
|
host_ds_system = self.esxi.configManager.datastoreSystem
|
||||||
|
ds_system = vim.host.DatastoreSystem
|
||||||
|
error_message_mount = "Cannot mount datastore %s on host %s" % (self.datastore_name, self.esxi_hostname)
|
||||||
|
try:
|
||||||
|
vmfs_ds_options = ds_system.QueryVmfsDatastoreCreateOptions(host_ds_system,
|
||||||
|
ds_path,
|
||||||
|
self.vmfs_version)
|
||||||
|
vmfs_ds_options[0].spec.vmfs.volumeName = self.datastore_name
|
||||||
|
ds = ds_system.CreateVmfsDatastore(host_ds_system,
|
||||||
|
vmfs_ds_options[0].spec)
|
||||||
|
except (vim.fault.NotFound, vim.fault.DuplicateName,
|
||||||
|
vim.fault.HostConfigFault, vmodl.fault.InvalidArgument) 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))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
argument_spec = vmware_argument_spec()
|
||||||
|
argument_spec.update(
|
||||||
|
datacenter_name=dict(type='str', required=True),
|
||||||
|
datastore_name=dict(type='str', required=True),
|
||||||
|
datastore_type=dict(type='str', choices=['nfs', 'vmfs']),
|
||||||
|
nfs_server=dict(type='str'),
|
||||||
|
nfs_path=dict(type='str'),
|
||||||
|
nfs_ro=dict(type='bool', default=False),
|
||||||
|
vmfs_device_name=dict(type='str'),
|
||||||
|
vmfs_version=dict(type='int'),
|
||||||
|
esxi_hostname=dict(type='str', required=True),
|
||||||
|
state=dict(type='str', default='present', choices=['absent', 'present'])
|
||||||
|
)
|
||||||
|
|
||||||
|
module = AnsibleModule(
|
||||||
|
argument_spec=argument_spec,
|
||||||
|
supports_check_mode=True,
|
||||||
|
required_together=[
|
||||||
|
['nfs_server', 'nfs_path']
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# more complex required_if
|
||||||
|
if module.params['state'] == 'present':
|
||||||
|
if module.params['datastore_type'] == 'nfs' and not module.params['nfs_server']:
|
||||||
|
msg = "Missing nfs_server with datastore_type = nfs"
|
||||||
|
module.fail_json(msg=msg)
|
||||||
|
|
||||||
|
if module.params['datastore_type'] == 'vmfs' and not module.params['vmfs_device_name']:
|
||||||
|
msg = "Missing vmfs_device_name with datastore_type = vmfs"
|
||||||
|
module.fail_json(msg=msg)
|
||||||
|
|
||||||
|
vmware_host_datastore = VMwareHostDatastore(module)
|
||||||
|
vmware_host_datastore.process_state()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue