VMware: new module: vmware_guest_disk (#36165)

1. Add support for SCSI controller creation
2. Support for SCSI Type
3. Warn about Disk Unit Number 7
4. Allow specifying SCSI controller
5. Allow specifying Disk Unit Number
6. Idempotency
7. Remove disks
8. Do not allow to reduce disk
9. More than 15 disks support
10. No SCSI controller and unit number check
11. Thin support
12. Update documentation and example
13. Multiple Datastore and datastore cluster support
14. Check datatype of disk unit number and SCSI controller
15. Handle disk_size when it is int or float

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
This commit is contained in:
Abhijeet Kasurde 2018-10-16 18:37:20 +05:30 committed by GitHub
parent 2d66695fd7
commit b48526ca65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -0,0 +1,662 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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 = '''
---
module: vmware_guest_disk
short_description: Manage disks related to virtual machine in given vCenter infrastructure
description:
- This module can be used to add, remove and update disks belonging to given virtual machine.
- All parameters and VMware object names are case sensitive.
- This module is destructive in nature, please read documentation carefully before proceeding.
- Be careful while removing disk specified as this may lead to data loss.
version_added: 2.8
author:
- Abhijeet Kasurde (@akasurde) <akasurde@redhat.com>
notes:
- Tested on vSphere 6.0 and 6.5
requirements:
- "python >= 2.6"
- PyVmomi
options:
name:
description:
- Name of the virtual machine.
- This is a required parameter, if parameter C(uuid) is not supplied.
uuid:
description:
- UUID of the instance to gather facts if known, this is VMware's unique identifier.
- This is a required parameter, if parameter C(name) is not supplied.
folder:
description:
- Destination folder, absolute or relative path to find an existing guest.
- This is a required parameter, only if multiple VMs are found with same name.
- The folder should include the datacenter. ESX's datacenter is ha-datacenter
- 'Examples:'
- ' folder: /ha-datacenter/vm'
- ' folder: ha-datacenter/vm'
- ' folder: /datacenter1/vm'
- ' folder: datacenter1/vm'
- ' folder: /datacenter1/vm/folder1'
- ' folder: datacenter1/vm/folder1'
- ' folder: /folder1/datacenter1/vm'
- ' folder: folder1/datacenter1/vm'
- ' folder: /folder1/datacenter1/vm/folder2'
datacenter:
description:
- The datacenter name to which virtual machine belongs to.
required: True
disk:
description:
- A list of disks to add.
- The virtual disk related information is provided using this list.
- All values and parameters are case sensitive.
- 'Valid attributes are:'
- ' - C(size[_tb,_gb,_mb,_kb]) (integer): Disk storage size in specified unit.'
- ' If C(size) specified then unit must be specified. There is no space allowed in between size number and unit.'
- ' Only first occurance in disk element will be considered, even if there are multiple size* parameters available.'
- ' - C(type) (string): Valid values are:'
- ' - C(thin) thin disk'
- ' - C(eagerzeroedthick) eagerzeroedthick disk'
- ' - C(thick) thick disk'
- ' Default: C(thick) thick disk, no eagerzero.'
- ' - C(datastore) (string): Name of datastore or datastore cluster to be used for the disk.'
- ' - C(autoselect_datastore) (bool): Select the less used datastore. Specify only if C(datastore) is not specified.'
- ' - C(scsi_controller) (integer): SCSI controller number. Valid value range from 0 to 3.'
- ' Only 4 SCSI controllers are allowed per VM.'
- ' Care should be taken while specifying C(scsi_controller) is 0 and C(unit_number) as 0 as this disk may contain OS.'
- ' - C(unit_number) (integer): Disk Unit Number. Valid value range from 0 to 15. Only 15 disks are allowed per SCSI Controller.'
- ' - C(scsi_type) (string): Type of SCSI controller. This value is required only for the first occurance of SCSI Controller.'
- ' This value is ignored, if SCSI Controller is already present or C(state) is C(absent).'
- ' Valid values are C(buslogic), C(lsilogic), C(lsilogicsas) and C(paravirtual).'
- ' C(paravirtual) is default value for this parameter.'
- ' - C(state) (string): State of disk. This is either "absent" or "present".'
- ' If C(state) is set to C(absent), disk will be removed permanently from virtual machine configuration and from VMware storage.'
- ' If C(state) is set to C(present), disk will be added if not present at given SCSI Controller and Unit Number.'
- ' If C(state) is set to C(present) and disk exists with different size, disk size is increased.'
- ' Reducing disk size is not allowed.'
default: []
extends_documentation_fragment: vmware.documentation
'''
EXAMPLES = '''
- name: Add disks to virtual machine using UUID
vmware_guest_disk:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
datacenter: "{{ datacenter_name }}"
validate_certs: no
uuid: 421e4592-c069-924d-ce20-7e7533fab926
disk:
- size_mb: 10
type: thin
datastore: datacluster0
state: present
scsi_controller: 1
unit_number: 1
scsi_type: 'paravirtual'
- size_gb: 10
type: eagerzeroedthick
state: present
autoselect_datastore: True
scsi_controller: 2
scsi_type: 'buslogic'
unit_number: 12
- size: 10Gb
type: eagerzeroedthick
state: present
autoselect_datastore: True
scsi_controller: 2
scsi_type: 'buslogic'
unit_number: 1
delegate_to: localhost
register: disk_facts
- name: Remove disks from virtual machine using name
vmware_guest_disk:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
datacenter: "{{ datacenter_name }}"
validate_certs: no
name: VM_225
disk:
- state: absent
scsi_controller: 1
unit_number: 1
delegate_to: localhost
register: disk_facts
'''
RETURN = """
disk_status:
description: metadata about the virtual machine's disks after managing them
returned: always
type: dict
sample: {
"0": {
"backing_datastore": "datastore2",
"backing_disk_mode": "persistent",
"backing_eagerlyscrub": false,
"backing_filename": "[datastore2] VM_225/VM_225.vmdk",
"backing_thinprovisioned": false,
"backing_writethrough": false,
"capacity_in_bytes": 10485760,
"capacity_in_kb": 10240,
"controller_key": 1000,
"key": 2000,
"label": "Hard disk 1",
"summary": "10,240 KB",
"unit_number": 0
},
}
"""
import re
try:
from pyVmomi import vim, vmodl
except ImportError:
pass
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
from ansible.module_utils.vmware import PyVmomi, vmware_argument_spec, wait_for_task, find_obj, get_all_objs
class PyVmomiHelper(PyVmomi):
def __init__(self, module):
super(PyVmomiHelper, self).__init__(module)
self.desired_disks = self.params['disk'] # Match with vmware_guest parameter
self.vm = None
self.scsi_device_type = dict(lsilogic=vim.vm.device.VirtualLsiLogicController,
paravirtual=vim.vm.device.ParaVirtualSCSIController,
buslogic=vim.vm.device.VirtualBusLogicController,
lsilogicsas=vim.vm.device.VirtualLsiLogicSASController)
self.config_spec = vim.vm.ConfigSpec()
self.config_spec.deviceChange = []
def create_scsi_controller(self, scsi_type, scsi_bus_number):
"""
Create SCSI Controller with given SCSI Type and SCSI Bus Number
Args:
scsi_type: Type of SCSI
scsi_bus_number: SCSI Bus number to be assigned
Returns: Virtual device spec for SCSI Controller
"""
scsi_ctl = vim.vm.device.VirtualDeviceSpec()
scsi_ctl.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
scsi_ctl.device = self.scsi_device_type[scsi_type]()
scsi_ctl.device.unitNumber = 3
scsi_ctl.device.busNumber = scsi_bus_number
scsi_ctl.device.hotAddRemove = True
scsi_ctl.device.sharedBus = 'noSharing'
scsi_ctl.device.scsiCtlrUnitNumber = 7
return scsi_ctl
@staticmethod
def create_scsi_disk(scsi_ctl_key, disk_index):
"""
Create Virtual Device Spec for virtual disk
Args:
scsi_ctl_key: Unique SCSI Controller Key
disk_index: Disk unit number at which disk needs to be attached
Returns: Virtual Device Spec for virtual disk
"""
disk_spec = vim.vm.device.VirtualDeviceSpec()
disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
disk_spec.fileOperation = vim.vm.device.VirtualDeviceSpec.FileOperation.create
disk_spec.device = vim.vm.device.VirtualDisk()
disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
disk_spec.device.backing.diskMode = 'persistent'
disk_spec.device.controllerKey = scsi_ctl_key
disk_spec.device.unitNumber = disk_index
return disk_spec
def reconfigure_vm(self, config_spec, device_type):
"""
Reconfigure virtual machine after modifying device spec
Args:
config_spec: Config Spec
device_type: Type of device being modified
Returns: Boolean status 'changed' and actual task result
"""
changed, results = (False, '')
try:
# Perform actual VM reconfiguration
task = self.vm.ReconfigVM_Task(spec=config_spec)
changed, results = wait_for_task(task)
except vim.fault.InvalidDeviceSpec as invalid_device_spec:
self.module.fail_json(msg="Failed to manage %s on given virtual machine due to invalid"
" device spec : %s" % (device_type, to_native(invalid_device_spec.msg)),
details="Please check ESXi server logs for more details.")
except vim.fault.RestrictedVersion as e:
self.module.fail_json(msg="Failed to reconfigure virtual machine due to"
" product versioning restrictions: %s" % to_native(e.msg))
return changed, results
def ensure_disks(self, vm_obj=None):
"""
Manage internal state of virtual machine disks
Args:
vm_obj: Managed object of virtual machine
"""
# Set vm object
self.vm = vm_obj
# Sanitize user input
disk_data = self.sanitize_disk_inputs()
# Create stateful information about SCSI devices
current_scsi_info = dict()
results = dict(changed=False, disk_data=None, disk_changes=dict())
# Deal with SCSI Controller
for device in vm_obj.config.hardware.device:
if isinstance(device, tuple(self.scsi_device_type.values())):
# Found SCSI device
if device.busNumber not in current_scsi_info:
device_bus_number = 1000 + device.busNumber
current_scsi_info[device_bus_number] = dict(disks=dict())
scsi_changed = False
for disk in disk_data:
scsi_controller = disk['scsi_controller'] + 1000
if scsi_controller not in current_scsi_info and disk['state'] == 'present':
scsi_ctl = self.create_scsi_controller(disk['scsi_type'], disk['scsi_controller'])
current_scsi_info[scsi_controller] = dict(disks=dict())
self.config_spec.deviceChange.append(scsi_ctl)
scsi_changed = True
if scsi_changed:
self.reconfigure_vm(self.config_spec, 'SCSI Controller')
self.config_spec = vim.vm.ConfigSpec()
self.config_spec.deviceChange = []
# Deal with Disks
for device in vm_obj.config.hardware.device:
if isinstance(device, vim.vm.device.VirtualDisk):
# Found Virtual Disk device
if device.controllerKey not in current_scsi_info:
current_scsi_info[device.controllerKey] = dict(disks=dict())
current_scsi_info[device.controllerKey]['disks'][device.unitNumber] = device
vm_name = self.vm.name
disk_change_list = []
for disk in disk_data:
disk_change = False
scsi_controller = disk['scsi_controller'] + 1000 # VMware auto assign 1000 + SCSI Controller
if disk['disk_unit_number'] not in current_scsi_info[scsi_controller]['disks'] and disk['state'] == 'present':
# Add new disk
disk_spec = self.create_scsi_disk(scsi_controller, disk['disk_unit_number'])
disk_spec.device.capacityInKB = disk['size']
if disk['disk_type'] == 'thin':
disk_spec.device.backing.thinProvisioned = True
elif disk['disk_type'] == 'eagerzeroedthick':
disk_spec.device.backing.eagerlyScrub = True
disk_spec.device.backing.fileName = "[%s] %s/%s_%s_%s.vmdk" % (disk['datastore'].name,
vm_name, vm_name,
str(scsi_controller),
str(disk['disk_unit_number']))
disk_spec.device.backing.datastore = disk['datastore']
self.config_spec.deviceChange.append(disk_spec)
disk_change = True
current_scsi_info[scsi_controller]['disks'][disk['disk_unit_number']] = disk_spec.device
results['disk_changes'][disk['disk_index']] = "Disk created."
elif disk['disk_unit_number'] in current_scsi_info[scsi_controller]['disks']:
if disk['state'] == 'present':
disk_spec = vim.vm.device.VirtualDeviceSpec()
# set the operation to edit so that it knows to keep other settings
disk_spec.device = current_scsi_info[scsi_controller]['disks'][disk['disk_unit_number']]
# Edit and no resizing allowed
if disk['size'] < disk_spec.device.capacityInKB:
self.module.fail_json(msg="Given disk size at disk index [%s] is smaller than found (%d < %d)."
" Reducing disks is not allowed." % (disk['disk_index'],
disk['size'],
disk_spec.device.capacityInKB))
if disk['size'] != disk_spec.device.capacityInKB:
disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit
disk_spec.device.capacityInKB = disk['size']
self.config_spec.deviceChange.append(disk_spec)
disk_change = True
results['disk_changes'][disk['disk_index']] = "Disk size increased."
else:
results['disk_changes'][disk['disk_index']] = "Disk already exists."
elif disk['state'] == 'absent':
# Disk already exists, deleting
disk_spec = vim.vm.device.VirtualDeviceSpec()
disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.remove
disk_spec.fileOperation = vim.vm.device.VirtualDeviceSpec.FileOperation.destroy
disk_spec.device = current_scsi_info[scsi_controller]['disks'][disk['disk_unit_number']]
self.config_spec.deviceChange.append(disk_spec)
disk_change = True
results['disk_changes'][disk['disk_index']] = "Disk deleted."
if disk_change:
# Adding multiple disks in a single attempt raises weird errors
# So adding single disk at a time.
self.reconfigure_vm(self.config_spec, 'disks')
self.config_spec = vim.vm.ConfigSpec()
self.config_spec.deviceChange = []
disk_change_list.append(disk_change)
if any(disk_change_list):
results['changed'] = True
results['disk_data'] = self.gather_disk_facts(vm_obj=self.vm)
self.module.exit_json(**results)
def sanitize_disk_inputs(self):
"""
Check correctness of disk input provided by user
Returns: A list of dictionary containing disk information
"""
disks_data = list()
if not self.desired_disks:
self.module.exit_json(changed=False, msg="No disks provided for virtual"
" machine '%s' for management." % self.vm.name)
for disk_index, disk in enumerate(self.desired_disks):
# Initialize default value for disk
current_disk = dict(disk_index=disk_index,
state='present',
datastore=None,
autoselect_datastore=True,
disk_unit_number=0,
scsi_controller=0)
# Check state
if 'state' in disk:
if disk['state'] not in ['absent', 'present']:
self.module.fail_json(msg="Invalid state provided '%s' for disk index [%s]."
" State can be either - 'absent', 'present'" % (disk['state'],
disk_index))
else:
current_disk['state'] = disk['state']
if current_disk['state'] == 'present':
# Select datastore or datastore cluster
if 'datastore' in disk:
if 'autoselect_datastore' in disk:
self.module.fail_json(msg="Please specify either 'datastore' "
"or 'autoselect_datastore' for disk index [%s]" % disk_index)
# Check if given value is datastore or datastore cluster
datastore_name = disk['datastore']
datastore_cluster = find_obj(self.content, [vim.StoragePod], datastore_name)
if datastore_cluster:
# If user specified datastore cluster so get recommended datastore
datastore_name = self.get_recommended_datastore(datastore_cluster_obj=datastore_cluster)
# Check if get_recommended_datastore or user specified datastore exists or not
datastore = find_obj(self.content, [vim.Datastore], datastore_name)
if datastore is None:
self.module.fail_json(msg="Failed to find datastore named '%s' "
"in given configuration." % disk['datastore'])
current_disk['datastore'] = datastore
current_disk['autoselect_datastore'] = False
elif 'autoselect_datastore' in disk:
# Find datastore which fits requirement
datastores = get_all_objs(self.content, [vim.Datastore])
if not datastores:
self.module.fail_json(msg="Failed to gather information about"
" available datastores in given datacenter.")
datastore = None
datastore_freespace = 0
for ds in datastores:
if ds.summary.freeSpace > datastore_freespace:
# If datastore field is provided, filter destination datastores
datastore = ds
datastore_freespace = ds.summary.freeSpace
current_disk['datastore'] = datastore
if 'datastore' not in disk and 'autoselect_datastore' not in disk:
self.module.fail_json(msg="Either 'datastore' or 'autoselect_datastore' is"
" required parameter while creating disk for "
"disk index [%s]." % disk_index)
if [x for x in disk.keys() if x.startswith('size_') or x == 'size']:
# size, size_tb, size_gb, size_mb, size_kb
disk_size_parse_failed = False
if 'size' in disk:
size_regex = re.compile(r'(\d+(?:\.\d+)?)([tgmkTGMK][bB])')
disk_size_m = size_regex.match(disk['size'])
if disk_size_m:
expected = disk_size_m.group(1)
unit = disk_size_m.group(2)
else:
disk_size_parse_failed = True
try:
if re.match(r'\d+\.\d+', expected):
# We found float value in string, let's typecast it
expected = float(expected)
else:
# We found int value in string, let's typecast it
expected = int(expected)
except (TypeError, ValueError, NameError):
disk_size_parse_failed = True
else:
# Even multiple size_ parameter provided by user,
# consider first value only
param = [x for x in disk.keys() if x.startswith('size_')][0]
unit = param.split('_')[-1]
disk_size = disk[param]
if isinstance(disk_size, (float, int)):
disk_size = str(disk_size)
try:
if re.match(r'\d+\.\d+', disk_size):
# We found float value in string, let's typecast it
expected = float(disk_size)
else:
# We found int value in string, let's typecast it
expected = int(disk_size)
except (TypeError, ValueError, NameError):
disk_size_parse_failed = True
if disk_size_parse_failed:
# Common failure
self.module.fail_json(msg="Failed to parse disk size for disk index [%s],"
" please review value provided"
" using documentation." % disk_index)
disk_units = dict(tb=3, gb=2, mb=1, kb=0)
unit = unit.lower()
if unit in disk_units:
current_disk['size'] = expected * (1024 ** disk_units[unit])
else:
self.module.fail_json(msg="%s is not a supported unit for disk size for disk index [%s]."
" Supported units are ['%s']." % (unit,
disk_index,
"', '".join(disk_units.keys())))
else:
# No size found but disk, fail
self.module.fail_json(msg="No size, size_kb, size_mb, size_gb or size_tb"
" attribute found into disk index [%s] configuration." % disk_index)
# Check SCSI controller key
if 'scsi_controller' in disk:
try:
temp_disk_controller = int(disk['scsi_controller'])
except ValueError:
self.module.fail_json(msg="Invalid SCSI controller ID '%s' specified"
" at index [%s]" % (disk['scsi_controller'], disk_index))
if temp_disk_controller not in range(0, 4):
# Only 4 SCSI controllers are allowed per VM
self.module.fail_json(msg="Invalid SCSI controller ID specified [%s],"
" please specify value between 0 to 3 only." % temp_disk_controller)
current_disk['scsi_controller'] = temp_disk_controller
else:
self.module.fail_json(msg="Please specify 'scsi_controller' under disk parameter"
" at index [%s], which is required while creating disk." % disk_index)
# Check for disk unit number
if 'unit_number' in disk:
try:
temp_disk_unit_number = int(disk['unit_number'])
except ValueError:
self.module.fail_json(msg="Invalid Disk unit number ID '%s'"
" specified at index [%s]" % (disk['unit_number'], disk_index))
if temp_disk_unit_number not in range(0, 16):
self.module.fail_json(msg="Invalid Disk unit number ID specified for disk [%s] at index [%s],"
" please specify value between 0 to 15"
" only (excluding 7)." % (temp_disk_unit_number, disk_index))
if temp_disk_unit_number == 7:
self.module.fail_json(msg="Invalid Disk unit number ID specified for disk at index [%s],"
" please specify value other than 7 as it is reserved"
"for SCSI Controller" % disk_index)
current_disk['disk_unit_number'] = temp_disk_unit_number
else:
self.module.fail_json(msg="Please specify 'unit_number' under disk parameter"
" at index [%s], which is required while creating disk." % disk_index)
# Type of Disk
disk_type = disk.get('type', 'thick').lower()
if disk_type not in ['thin', 'thick', 'eagerzeroedthick']:
self.module.fail_json(msg="Invalid 'disk_type' specified for disk index [%s]. Please specify"
" 'disk_type' value from ['thin', 'thick', 'eagerzeroedthick']." % disk_index)
current_disk['disk_type'] = disk_type
# SCSI Controller Type
scsi_contrl_type = disk.get('scsi_type', 'paravirtual').lower()
if scsi_contrl_type not in self.scsi_device_type.keys():
self.module.fail_json(msg="Invalid 'scsi_type' specified for disk index [%s]. Please specify"
" 'scsi_type' value from ['%s']" % (disk_index,
"', '".join(self.scsi_device_type.keys())))
current_disk['scsi_type'] = scsi_contrl_type
disks_data.append(current_disk)
return disks_data
def get_recommended_datastore(self, datastore_cluster_obj):
"""
Return Storage DRS recommended datastore from datastore cluster
Args:
datastore_cluster_obj: datastore cluster managed object
Returns: Name of recommended datastore from the given datastore cluster,
Returns None if no datastore recommendation found.
"""
# Check if Datastore Cluster provided by user is SDRS ready
sdrs_status = datastore_cluster_obj.podStorageDrsEntry.storageDrsConfig.podConfig.enabled
if sdrs_status:
# We can get storage recommendation only if SDRS is enabled on given datastorage cluster
pod_sel_spec = vim.storageDrs.PodSelectionSpec()
pod_sel_spec.storagePod = datastore_cluster_obj
storage_spec = vim.storageDrs.StoragePlacementSpec()
storage_spec.podSelectionSpec = pod_sel_spec
storage_spec.type = 'create'
try:
rec = self.content.storageResourceManager.RecommendDatastores(storageSpec=storage_spec)
rec_action = rec.recommendations[0].action[0]
return rec_action.destination.name
except Exception as e:
# There is some error so we fall back to general workflow
pass
datastore = None
datastore_freespace = 0
for ds in datastore_cluster_obj.childEntity:
if ds.summary.freeSpace > datastore_freespace:
# If datastore field is provided, filter destination datastores
datastore = ds
datastore_freespace = ds.summary.freeSpace
if datastore:
return datastore.name
return None
@staticmethod
def gather_disk_facts(vm_obj):
"""
Gather facts about VM's disks
Args:
vm_obj: Managed object of virtual machine
Returns: A list of dict containing disks information
"""
disks_facts = dict()
if vm_obj is None:
return disks_facts
disk_index = 0
for disk in vm_obj.config.hardware.device:
if isinstance(disk, vim.vm.device.VirtualDisk):
disks_facts[disk_index] = dict(
key=disk.key,
label=disk.deviceInfo.label,
summary=disk.deviceInfo.summary,
backing_filename=disk.backing.fileName,
backing_datastore=disk.backing.datastore.name,
backing_disk_mode=disk.backing.diskMode,
backing_writethrough=disk.backing.writeThrough,
backing_thinprovisioned=disk.backing.thinProvisioned,
backing_eagerlyscrub=bool(disk.backing.eagerlyScrub),
controller_key=disk.controllerKey,
unit_number=disk.unitNumber,
capacity_in_kb=disk.capacityInKB,
capacity_in_bytes=disk.capacityInBytes,
)
disk_index += 1
return disks_facts
def main():
argument_spec = vmware_argument_spec()
argument_spec.update(
name=dict(type='str'),
uuid=dict(type='str'),
folder=dict(type='str'),
datacenter=dict(type='str', required=True),
disk=dict(type=list, default=[]),
)
module = AnsibleModule(argument_spec=argument_spec,
required_one_of=[['name', 'uuid']])
if module.params['folder']:
# FindByInventoryPath() does not require an absolute path
# so we should leave the input folder path unmodified
module.params['folder'] = module.params['folder'].rstrip('/')
pyv = PyVmomiHelper(module)
# Check if the VM exists before continuing
vm = pyv.get_vm()
if not vm:
# We unable to find the virtual machine user specified
# Bail out
module.fail_json(msg="Unable to manage disks for non-existing"
" virtual machine '%s'." % (module.params.get('uuid') or module.params.get('name')))
# VM exists
try:
pyv.ensure_disks(vm_obj=vm)
except Exception as exc:
module.fail_json(msg="Failed to manage disks for virtual machine"
" '%s' with exception : %s" % (vm.name,
to_native(exc)))
if __name__ == '__main__':
main()