diff --git a/lib/ansible/modules/windows/win_reboot.py b/lib/ansible/modules/windows/win_reboot.py index af3cbf9291..cb13c11644 100644 --- a/lib/ansible/modules/windows/win_reboot.py +++ b/lib/ansible/modules/windows/win_reboot.py @@ -1,29 +1,12 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - -# this is a windows documentation stub. actual code lives in the .ps1 -# file of the same name +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['stableinterface'], 'supported_by': 'core'} - DOCUMENTATION = r''' --- module: win_reboot @@ -36,6 +19,12 @@ options: description: - Seconds for shutdown to wait before requesting reboot default: 2 + post_reboot_delay_sec: + description: + - Seconds to wait after the reboot was successful and the connection was re-established + - This is useful if you want wait for something to settle despite your connection already working + default: 0 + version_added: '2.4' shutdown_timeout_sec: description: - Maximum seconds to wait for shutdown to occur @@ -86,4 +75,9 @@ rebooted: returned: always type: boolean sample: true +elapsed: + description: The number of seconds that elapsed waiting for the system to be rebooted. + returned: always + type: int + sample: 23 ''' diff --git a/lib/ansible/plugins/action/wait_for_connection.py b/lib/ansible/plugins/action/wait_for_connection.py index f3f856a217..46cfba5b73 100644 --- a/lib/ansible/plugins/action/wait_for_connection.py +++ b/lib/ansible/plugins/action/wait_for_connection.py @@ -22,7 +22,6 @@ __metaclass__ = type import time from datetime import datetime, timedelta -from ansible.module_utils.pycompat24 import get_exception from ansible.plugins.action import ActionBase try: @@ -47,19 +46,19 @@ class ActionModule(ActionBase): def do_until_success_or_timeout(self, what, timeout, connect_timeout, what_desc, sleep=1): max_end_time = datetime.utcnow() + timedelta(seconds=timeout) + e = None while datetime.utcnow() < max_end_time: try: what(connect_timeout) if what_desc: display.debug("wait_for_connection: %s success" % what_desc) return - except Exception: - e = get_exception() + except Exception as e: if what_desc: display.debug("wait_for_connection: %s fail (expected), retrying in %d seconds..." % (what_desc, sleep)) time.sleep(sleep) - raise TimedOutException("timed out waiting for %s: %s" % (what_desc, str(e))) + raise TimedOutException("timed out waiting for %s: %s" % (what_desc, e)) def run(self, tmp=None, task_vars=None): if task_vars is None: @@ -108,8 +107,7 @@ class ActionModule(ActionBase): # Use the ping module test to determine end-to-end connectivity self.do_until_success_or_timeout(ping_module_test, timeout, connect_timeout, what_desc="ping module test success", sleep=sleep) - except TimedOutException: - e = get_exception() + except TimedOutException as e: result['failed'] = True result['msg'] = str(e) diff --git a/lib/ansible/plugins/action/win_reboot.py b/lib/ansible/plugins/action/win_reboot.py index 21ca45b2e6..df46fc832c 100644 --- a/lib/ansible/plugins/action/win_reboot.py +++ b/lib/ansible/plugins/action/win_reboot.py @@ -1,21 +1,6 @@ # (c) 2016, Matt Davis -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# CI-required python3 boilerplate from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -44,52 +29,60 @@ class ActionModule(ActionBase): DEFAULT_REBOOT_TIMEOUT_SEC = 600 DEFAULT_CONNECT_TIMEOUT_SEC = 5 DEFAULT_PRE_REBOOT_DELAY_SEC = 2 + DEFAULT_POST_REBOOT_DELAY_SEC = 0 DEFAULT_TEST_COMMAND = 'whoami' DEFAULT_REBOOT_MESSAGE = 'Reboot initiated by Ansible.' def do_until_success_or_timeout(self, what, timeout_sec, what_desc, fail_sleep_sec=1): max_end_time = datetime.utcnow() + timedelta(seconds=timeout_sec) + e = None while datetime.utcnow() < max_end_time: try: what() if what_desc: display.debug("win_reboot: %s success" % what_desc) return - except: + except Exception as e: if what_desc: - display.debug("win_reboot: %s fail (expected), sleeping before retry..." % what_desc) + display.debug("win_reboot: %s fail (expected), retrying in %d seconds..." % (what_desc, fail_sleep_sec)) time.sleep(fail_sleep_sec) - raise TimedOutException("timed out waiting for %s" % what_desc) + raise TimedOutException("timed out waiting for %s: %s" % (what_desc, e)) def run(self, tmp=None, task_vars=None): + + self._supports_check_mode = True + self._supports_async = True + + if self._play_context.check_mode: + return dict(changed=True, elapsed=0, rebooted=True) + if task_vars is None: task_vars = dict() + result = super(ActionModule, self).run(tmp, task_vars) + + if result.get('skipped', False) or result.get('failed', False): + return result + + winrm_host = self._connection._winrm_host + winrm_port = self._connection._winrm_port + shutdown_timeout_sec = int(self._task.args.get('shutdown_timeout_sec', self.DEFAULT_SHUTDOWN_TIMEOUT_SEC)) reboot_timeout_sec = int(self._task.args.get('reboot_timeout_sec', self.DEFAULT_REBOOT_TIMEOUT_SEC)) connect_timeout_sec = int(self._task.args.get('connect_timeout_sec', self.DEFAULT_CONNECT_TIMEOUT_SEC)) pre_reboot_delay_sec = int(self._task.args.get('pre_reboot_delay_sec', self.DEFAULT_PRE_REBOOT_DELAY_SEC)) + post_reboot_delay_sec = int(self._task.args.get('post_reboot_delay_sec', self.DEFAULT_POST_REBOOT_DELAY_SEC)) test_command = str(self._task.args.get('test_command', self.DEFAULT_TEST_COMMAND)) msg = str(self._task.args.get('msg', self.DEFAULT_REBOOT_MESSAGE)) - if self._play_context.check_mode: - display.vvv("win_reboot: skipping for check_mode") - return dict(skipped=True) - - winrm_host = self._connection._winrm_host - winrm_port = self._connection._winrm_port - - result = super(ActionModule, self).run(tmp, task_vars) - result['warnings'] = [] - # Initiate reboot (rc, stdout, stderr) = self._connection.exec_command('shutdown /r /t %d /c "%s"' % (pre_reboot_delay_sec, msg)) # Test for "A system shutdown has already been scheduled. (1190)" and handle it gracefully if rc == 1190: - result['warnings'].append('A scheduled reboot was pre-empted by Ansible.') + display.warning('A scheduled reboot was pre-empted by Ansible.') # Try to abort (this may fail if it was already aborted) (rc, stdout1, stderr1) = self._connection.exec_command('shutdown /a') @@ -114,6 +107,8 @@ class ActionModule(ActionBase): raise Exception("port is open") + start = datetime.now() + try: self.do_until_success_or_timeout(raise_if_port_open, shutdown_timeout_sec, what_desc="winrm port down") @@ -149,4 +144,11 @@ class ActionModule(ActionBase): result['rebooted'] = True result['msg'] = toex.message + if post_reboot_delay_sec != 0: + display.vvv("win_reboot: waiting an additional %d seconds" % post_reboot_delay_sec) + time.sleep(post_reboot_delay_sec) + + elapsed = datetime.now() - start + result['elapsed'] = elapsed.seconds + return result