From b6d4fa1c967a2a83940bbc8253eb7c3fa42da0e2 Mon Sep 17 00:00:00 2001 From: Jim Gu Date: Wed, 21 Feb 2018 06:44:12 -0500 Subject: [PATCH] Wait for VM state to reach poweredoff when state: shutdownguest (#31669) This change adds the optional wait_for_state_change argument to the vmware_guest, vmware_guest_powerstate module, which allows for module completion to be blocked when using the shutdownguest state until the VM has reached the poweredoff state. Fixes: #28498 Signed-off-by: Jim Gu Signed-off-by: Abhijeet Kasurde --- lib/ansible/module_utils/vmware.py | 18 ++++++++++++++- .../modules/cloud/vmware/vmware_guest.py | 10 ++++++++- .../cloud/vmware/vmware_guest_powerstate.py | 22 ++++++++++++++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py index 43bf028a45..00fd022c47 100644 --- a/lib/ansible/module_utils/vmware.py +++ b/lib/ansible/module_utils/vmware.py @@ -716,7 +716,7 @@ def find_host_by_cluster_datacenter(module, content, datacenter_name, cluster_na return None, cluster -def set_vm_power_state(content, vm, state, force): +def set_vm_power_state(content, vm, state, force, timeout=0): """ Set the power status for a VM determined by the current and requested states. force is forceful @@ -764,6 +764,8 @@ def set_vm_power_state(content, vm, state, force): if vm.guest.toolsRunningStatus == 'guestToolsRunning': if expected_state == 'shutdownguest': task = vm.ShutdownGuest() + if timeout > 0: + result.update(wait_for_poweroff(vm, timeout)) else: task = vm.RebootGuest() # Set result['changed'] immediately because @@ -799,6 +801,20 @@ def set_vm_power_state(content, vm, state, force): return result +def wait_for_poweroff(vm, timeout=300): + result = dict() + interval = 15 + while timeout > 0: + if vm.runtime.powerState.lower() == 'poweredoff': + break + time.sleep(interval) + timeout -= interval + else: + result['failed'] = True + result['msg'] = 'Timeout while waiting for VM power off.' + return result + + class PyVmomi(object): def __init__(self, module): """ diff --git a/lib/ansible/modules/cloud/vmware/vmware_guest.py b/lib/ansible/modules/cloud/vmware/vmware_guest.py index b6bb720451..d691a0b1ac 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_guest.py +++ b/lib/ansible/modules/cloud/vmware/vmware_guest.py @@ -156,6 +156,13 @@ options: - "vmware-tools needs to be installed on given virtual machine in order to work with this parameter." default: 'no' type: bool + state_change_timeout: + description: + - If the C(state) is set to C(shutdownguest), by default the module will return immediately after sending the shutdown signal. + - If this argument is set to a positive integer, the module will instead wait for the VM to reach the poweredoff state. + - The value sets a timeout in seconds for the module to wait for the state change. + default: 0 + version_added: '2.6' snapshot_src: description: - Name of the existing snapshot to use to create a clone of a VM. @@ -1994,6 +2001,7 @@ def main(): esxi_hostname=dict(type='str'), cluster=dict(type='str'), wait_for_ip_address=dict(type='bool', default=False), + state_change_timeout=dict(type='int', default=0), snapshot_src=dict(type='str'), linked_clone=dict(type='bool', default=False), networks=dict(type='list', default=[]), @@ -2054,7 +2062,7 @@ def main(): ) module.exit_json(**result) # set powerstate - tmp_result = set_vm_power_state(pyv.content, vm, module.params['state'], module.params['force']) + tmp_result = set_vm_power_state(pyv.content, vm, module.params['state'], module.params['force'], module.params['state_change_timeout']) if tmp_result['changed']: result["changed"] = True if not tmp_result["failed"]: diff --git a/lib/ansible/modules/cloud/vmware/vmware_guest_powerstate.py b/lib/ansible/modules/cloud/vmware/vmware_guest_powerstate.py index 78ab750a49..e1a377a87e 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_guest_powerstate.py +++ b/lib/ansible/modules/cloud/vmware/vmware_guest_powerstate.py @@ -64,6 +64,13 @@ options: - Date and time in string format at which specificed task needs to be performed. - "The required format for date and time - 'dd/mm/yyyy hh:mm'." - Scheduling task requires vCenter server. A standalone ESXi server does not support this option. + state_change_timeout: + description: + - If the C(state) is set to C(shutdown-guest), by default the module will return immediately after sending the shutdown signal. + - If this argument is set to a positive integer, the module will instead wait for the VM to reach the poweredoff state. + - The value sets a timeout in seconds for the module to wait for the state change. + default: 0 + version_added: '2.6' extends_documentation_fragment: vmware.documentation ''' @@ -92,6 +99,18 @@ EXAMPLES = r''' scheduled_at: "09/01/2018 10:18" delegate_to: localhost register: deploy_at_schedule_datetime + +- name: Wait for the virtual machine to shutdown + vmware_guest_powerstate: + hostname: 192.0.2.44 + username: administrator@vsphere.local + password: vmware + validate_certs: no + name: testvm_2 + state: shutdown-guest + state_change_timeout: 200 + delegate_to: localhost + register: deploy ''' RETURN = r''' # ''' @@ -118,6 +137,7 @@ def main(): folder=dict(type='str', default='/vm'), force=dict(type='bool', default=False), scheduled_at=dict(type='str'), + state_change_timeout=dict(type='int', default=0), ) module = AnsibleModule(argument_spec=argument_spec, @@ -182,7 +202,7 @@ def main(): "given are invalid: %s" % (module.params.get('state'), to_native(e.msg))) else: - result = set_vm_power_state(pyv.content, vm, module.params['state'], module.params['force']) + result = set_vm_power_state(pyv.content, vm, module.params['state'], module.params['force'], module.params['state_change_timeout']) else: module.fail_json(msg="Unable to set power state for non-existing virtual machine : '%s'" % (module.params.get('uuid') or module.params.get('name')))