diff --git a/lib/ansible/modules/cloud/vmware/vmware_guest_boot_manager.py b/lib/ansible/modules/cloud/vmware/vmware_guest_boot_manager.py new file mode 100644 index 0000000000..daac3c8b83 --- /dev/null +++ b/lib/ansible/modules/cloud/vmware/vmware_guest_boot_manager.py @@ -0,0 +1,335 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2018, Ansible Project +# Copyright: (c) 2018, Abhijeet Kasurde +# +# 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_boot_manager +short_description: Manage boot options for the given virtual machine +description: + - This module can be used to manage boot options for the given virtual machine. +version_added: 2.7 +author: + - Abhijeet Kasurde (@Akasurde) +notes: + - Tested on vSphere 6.5 +requirements: + - "python >= 2.6" + - PyVmomi +options: + name: + description: + - Name of the VM to work with. + - This is required if C(uuid) parameter is not supplied. + uuid: + description: + - UUID of the instance to manage if known, this is VMware's BIOS UUID. + - This is required if C(name) parameter is not supplied. + boot_order: + description: + - List of the boot devices. + default: [] + name_match: + description: + - If multiple virtual machines matching the name, use the first or last found. + default: 'first' + choices: ['first', 'last'] + boot_delay: + description: + - Delay in milliseconds before starting the boot sequence. + default: 0 + enter_bios_setup: + description: + - If set to C(True), the virtual machine automatically enters BIOS setup the next time it boots. + - The virtual machine resets this flag, so that the machine boots proceeds normally. + type: 'bool' + default: False + boot_retry_enabled: + description: + - If set to C(True), the virtual machine that fails to boot, will try to boot again after C(boot_retry_delay) is expired. + - If set to C(False), the virtual machine waits indefinitely for user intervention. + type: 'bool' + default: False + boot_retry_delay: + description: + - Specify the time in milliseconds between virtual machine boot failure and subsequent attempt to boot again. + - If set, will automatically set C(boot_retry_enabled) to C(True) as this parameter is required. + default: 0 + boot_firmware: + description: + - Choose which firmware should be used to boot the virtual machine. + choices: ["bios", "efi"] +extends_documentation_fragment: vmware.documentation +''' + +EXAMPLES = r''' +- name: Change virtual machine's boot order and related parameters + vmware_guest_boot_manager: + hostname: "{{ vcenter_server }}" + username: "{{ vcenter_user }}" + password: "{{ vcenter_pass }}" + name: testvm + boot_delay: 2000 + enter_bios_setup: True + boot_retry_enabled: True + boot_retry_delay: 22300 + boot_firmware: bios + boot_order: + - floppy + - cdrom + - ethernet + - disk + register: vm_boot_order +''' + +RETURN = r""" +vm_boot_status: + description: metadata about boot order of virtual machine + returned: always + type: dict + sample: { + "current_boot_order": [ + "floppy", + "disk", + "ethernet", + "cdrom" + ], + "current_boot_delay": 2000, + "current_boot_retry_delay": 22300, + "current_boot_retry_enabled": true, + "current_enter_bios_setup": true, + "current_boot_firmware": "bios", + "previous_boot_delay": 10, + "previous_boot_retry_delay": 10000, + "previous_boot_retry_enabled": true, + "previous_enter_bios_setup": false, + "previous_boot_firmware": "bios", + "previous_boot_order": [ + "ethernet", + "cdrom", + "floppy", + "disk" + ], + } +""" + + +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, find_vm_by_id, wait_for_task, TaskError + +try: + from pyVmomi import vim +except ImportError: + pass + + +class VmBootManager(PyVmomi): + def __init__(self, module): + super(VmBootManager, self).__init__(module) + self.name = self.params['name'] + self.uuid = self.params['uuid'] + self.vm = None + + def _get_vm(self): + vms = [] + + if self.uuid: + vm_obj = find_vm_by_id(self.content, vm_id=self.uuid, vm_id_type="uuid") + if vm_obj is None: + self.module.fail_json(msg="Failed to find the virtual machine with UUID : %s" % self.uuid) + vms = [vm_obj] + + elif self.name: + objects = self.get_managed_objects_properties(vim_type=vim.VirtualMachine, properties=['name']) + for temp_vm_object in objects: + if temp_vm_object.obj.name == self.name: + vms.append(temp_vm_object.obj) + + if vms: + if self.params.get('name_match') == 'first': + self.vm = vms[0] + elif self.params.get('name_match') == 'last': + self.vm = vms[-1] + else: + self.module.fail_json(msg="Failed to find virtual machine using %s" % (self.name or self.uuid)) + + @staticmethod + def humanize_boot_order(boot_order): + results = [] + for device in boot_order: + if isinstance(device, vim.vm.BootOptions.BootableCdromDevice): + results.append('cdrom') + elif isinstance(device, vim.vm.BootOptions.BootableDiskDevice): + results.append('disk') + elif isinstance(device, vim.vm.BootOptions.BootableEthernetDevice): + results.append('ethernet') + elif isinstance(device, vim.vm.BootOptions.BootableFloppyDevice): + results.append('floppy') + return results + + def ensure(self): + self._get_vm() + + valid_device_strings = ['cdrom', 'disk', 'ethernet', 'floppy'] + + boot_order_list = [] + for device_order in self.params.get('boot_order'): + if device_order not in valid_device_strings: + self.module.fail_json(msg="Invalid device found [%s], please specify device from ['%s']" % (device_order, + "', '".join(valid_device_strings))) + if device_order == 'cdrom': + first_cdrom = [device for device in self.vm.config.hardware.device if isinstance(device, vim.vm.device.VirtualCdrom)] + if first_cdrom: + boot_order_list.append(vim.vm.BootOptions.BootableCdromDevice()) + elif device_order == 'disk': + first_hdd = [device for device in self.vm.config.hardware.device if isinstance(device, vim.vm.device.VirtualDisk)] + if first_hdd: + boot_order_list.append(vim.vm.BootOptions.BootableDiskDevice(deviceKey=first_hdd[0].key)) + elif device_order == 'ethernet': + first_ether = [device for device in self.vm.config.hardware.device if isinstance(device, vim.vm.device.VirtualEthernetCard)] + if first_ether: + boot_order_list.append(vim.vm.BootOptions.BootableEthernetDevice(deviceKey=first_ether[0].key)) + elif device_order == 'floppy': + first_floppy = [device for device in self.vm.config.hardware.device if isinstance(device, vim.vm.device.VirtualFloppy)] + if first_floppy: + boot_order_list.append(vim.vm.BootOptions.BootableFloppyDevice()) + + change_needed = False + kwargs = dict() + if len(boot_order_list) != len(self.vm.config.bootOptions.bootOrder): + kwargs.update({'bootOrder': boot_order_list}) + change_needed = True + else: + for i in range(0, len(boot_order_list)): + boot_device_type = type(boot_order_list[i]) + vm_boot_device_type = type(self.vm.config.bootOptions.bootOrder[i]) + if boot_device_type != vm_boot_device_type: + kwargs.update({'bootOrder': boot_order_list}) + change_needed = True + + if self.vm.config.bootOptions.bootDelay != self.params.get('boot_delay'): + kwargs.update({'bootDelay': self.params.get('boot_delay')}) + change_needed = True + + if self.vm.config.bootOptions.enterBIOSSetup != self.params.get('enter_bios_setup'): + kwargs.update({'enterBIOSSetup': self.params.get('enter_bios_setup')}) + change_needed = True + + if self.vm.config.bootOptions.bootRetryEnabled != self.params.get('boot_retry_enabled'): + kwargs.update({'bootRetryEnabled': self.params.get('boot_retry_enabled')}) + change_needed = True + + if self.vm.config.bootOptions.bootRetryDelay != self.params.get('boot_retry_delay'): + if not self.vm.config.bootOptions.bootRetryEnabled: + kwargs.update({'bootRetryEnabled': True}) + kwargs.update({'bootRetryDelay': self.params.get('boot_retry_delay')}) + change_needed = True + + boot_firmware_required = False + if self.vm.config.firmware != self.params.get('boot_firmware'): + change_needed = True + boot_firmware_required = True + + changed = False + results = dict( + previous_boot_order=self.humanize_boot_order(self.vm.config.bootOptions.bootOrder), + previous_boot_delay=self.vm.config.bootOptions.bootDelay, + previous_enter_bios_setup=self.vm.config.bootOptions.enterBIOSSetup, + previous_boot_retry_enabled=self.vm.config.bootOptions.bootRetryEnabled, + previous_boot_retry_delay=self.vm.config.bootOptions.bootRetryDelay, + previous_boot_firmware=self.vm.config.firmware, + current_boot_order=[], + ) + + if change_needed: + vm_conf = vim.vm.ConfigSpec() + vm_conf.bootOptions = vim.vm.BootOptions(**kwargs) + if boot_firmware_required: + vm_conf.firmware = self.params.get('boot_firmware') + task = self.vm.ReconfigVM_Task(vm_conf) + + try: + changed, result = wait_for_task(task) + except TaskError as e: + self.module.fail_json(msg="Failed to perform reconfigure virtual" + " machine %s for boot order due to: %s" % (self.name or self.uuid, + to_native(e))) + + results.update( + { + 'current_boot_order': self.humanize_boot_order(self.vm.config.bootOptions.bootOrder), + 'current_boot_delay': self.vm.config.bootOptions.bootDelay, + 'current_enter_bios_setup': self.vm.config.bootOptions.enterBIOSSetup, + 'current_boot_retry_enabled': self.vm.config.bootOptions.bootRetryEnabled, + 'current_boot_retry_delay': self.vm.config.bootOptions.bootRetryDelay, + 'current_boot_firmware': self.vm.config.firmware, + } + ) + + self.module.exit_json(changed=changed, vm_boot_status=results) + + +def main(): + argument_spec = vmware_argument_spec() + argument_spec.update( + name=dict(type='str'), + uuid=dict(type='str'), + boot_order=dict( + type='list', + default=[], + ), + name_match=dict( + choices=['first', 'last'], + default='first' + ), + boot_delay=dict( + type='int', + default=0, + ), + enter_bios_setup=dict( + type='bool', + default=False, + ), + boot_retry_enabled=dict( + type='bool', + default=False, + ), + boot_retry_delay=dict( + type='int', + default=0, + ), + boot_firmware=dict( + type='str', + choices=['efi', 'bios'], + ) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=[ + ['name', 'uuid'] + ], + mutually_exclusive=[ + ['name', 'uuid'] + ], + ) + + pyv = VmBootManager(module) + pyv.ensure() + +if __name__ == '__main__': + main()