1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Bugfix to check-mode and async support, and features (#28448)

This PR includes:

- Check-mode support, the module will now report as if it was rebooted,
  without making the actual reboot.
- Async-support, this potentially fixes #23835
- The module now also returns the elapsed time, like the wait_for and wait_for_connection modules.
  This fixes #18108
- Add post_reboot_delay_sec parameter
This commit is contained in:
Dag Wieers 2017-08-29 23:44:50 +02:00 committed by Matt Davis
parent 470044f2c5
commit eed6a724ea
3 changed files with 48 additions and 54 deletions

View file

@ -1,29 +1,12 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# This file is part of Ansible # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# 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 <http://www.gnu.org/licenses/>.
# this is a windows documentation stub. actual code lives in the .ps1
# file of the same name
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'], 'status': ['stableinterface'],
'supported_by': 'core'} 'supported_by': 'core'}
DOCUMENTATION = r''' DOCUMENTATION = r'''
--- ---
module: win_reboot module: win_reboot
@ -36,6 +19,12 @@ options:
description: description:
- Seconds for shutdown to wait before requesting reboot - Seconds for shutdown to wait before requesting reboot
default: 2 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: shutdown_timeout_sec:
description: description:
- Maximum seconds to wait for shutdown to occur - Maximum seconds to wait for shutdown to occur
@ -86,4 +75,9 @@ rebooted:
returned: always returned: always
type: boolean type: boolean
sample: true sample: true
elapsed:
description: The number of seconds that elapsed waiting for the system to be rebooted.
returned: always
type: int
sample: 23
''' '''

View file

@ -22,7 +22,6 @@ __metaclass__ = type
import time import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
from ansible.module_utils.pycompat24 import get_exception
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
try: try:
@ -47,19 +46,19 @@ class ActionModule(ActionBase):
def do_until_success_or_timeout(self, what, timeout, connect_timeout, what_desc, sleep=1): def do_until_success_or_timeout(self, what, timeout, connect_timeout, what_desc, sleep=1):
max_end_time = datetime.utcnow() + timedelta(seconds=timeout) max_end_time = datetime.utcnow() + timedelta(seconds=timeout)
e = None
while datetime.utcnow() < max_end_time: while datetime.utcnow() < max_end_time:
try: try:
what(connect_timeout) what(connect_timeout)
if what_desc: if what_desc:
display.debug("wait_for_connection: %s success" % what_desc) display.debug("wait_for_connection: %s success" % what_desc)
return return
except Exception: except Exception as e:
e = get_exception()
if what_desc: if what_desc:
display.debug("wait_for_connection: %s fail (expected), retrying in %d seconds..." % (what_desc, sleep)) display.debug("wait_for_connection: %s fail (expected), retrying in %d seconds..." % (what_desc, sleep))
time.sleep(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): def run(self, tmp=None, task_vars=None):
if task_vars is None: if task_vars is None:
@ -108,8 +107,7 @@ class ActionModule(ActionBase):
# Use the ping module test to determine end-to-end connectivity # 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) self.do_until_success_or_timeout(ping_module_test, timeout, connect_timeout, what_desc="ping module test success", sleep=sleep)
except TimedOutException: except TimedOutException as e:
e = get_exception()
result['failed'] = True result['failed'] = True
result['msg'] = str(e) result['msg'] = str(e)

View file

@ -1,21 +1,6 @@
# (c) 2016, Matt Davis <mdavis@ansible.com> # (c) 2016, Matt Davis <mdavis@ansible.com>
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# 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 <http://www.gnu.org/licenses/>.
# CI-required python3 boilerplate
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
@ -44,52 +29,60 @@ class ActionModule(ActionBase):
DEFAULT_REBOOT_TIMEOUT_SEC = 600 DEFAULT_REBOOT_TIMEOUT_SEC = 600
DEFAULT_CONNECT_TIMEOUT_SEC = 5 DEFAULT_CONNECT_TIMEOUT_SEC = 5
DEFAULT_PRE_REBOOT_DELAY_SEC = 2 DEFAULT_PRE_REBOOT_DELAY_SEC = 2
DEFAULT_POST_REBOOT_DELAY_SEC = 0
DEFAULT_TEST_COMMAND = 'whoami' DEFAULT_TEST_COMMAND = 'whoami'
DEFAULT_REBOOT_MESSAGE = 'Reboot initiated by Ansible.' DEFAULT_REBOOT_MESSAGE = 'Reboot initiated by Ansible.'
def do_until_success_or_timeout(self, what, timeout_sec, what_desc, fail_sleep_sec=1): 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) max_end_time = datetime.utcnow() + timedelta(seconds=timeout_sec)
e = None
while datetime.utcnow() < max_end_time: while datetime.utcnow() < max_end_time:
try: try:
what() what()
if what_desc: if what_desc:
display.debug("win_reboot: %s success" % what_desc) display.debug("win_reboot: %s success" % what_desc)
return return
except: except Exception as e:
if what_desc: 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) 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): 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: if task_vars is None:
task_vars = dict() 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)) 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)) 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)) 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)) 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)) test_command = str(self._task.args.get('test_command', self.DEFAULT_TEST_COMMAND))
msg = str(self._task.args.get('msg', self.DEFAULT_REBOOT_MESSAGE)) 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 # Initiate reboot
(rc, stdout, stderr) = self._connection.exec_command('shutdown /r /t %d /c "%s"' % (pre_reboot_delay_sec, msg)) (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 # Test for "A system shutdown has already been scheduled. (1190)" and handle it gracefully
if rc == 1190: 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) # Try to abort (this may fail if it was already aborted)
(rc, stdout1, stderr1) = self._connection.exec_command('shutdown /a') (rc, stdout1, stderr1) = self._connection.exec_command('shutdown /a')
@ -114,6 +107,8 @@ class ActionModule(ActionBase):
raise Exception("port is open") raise Exception("port is open")
start = datetime.now()
try: try:
self.do_until_success_or_timeout(raise_if_port_open, shutdown_timeout_sec, what_desc="winrm port down") 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['rebooted'] = True
result['msg'] = toex.message 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 return result