community.general/lib/ansible/modules/cloud/vmware/vmware_guest_disk.py

663 lines
31 KiB
Python
Raw Normal View History

#!/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
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()