2018-10-04 19:44:50 +00:00
|
|
|
# Copyright: (c) 2018, Matt Davis <mdavis@ansible.com>
|
2017-08-29 21:44:50 +00:00
|
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
|
2016-02-06 08:00:07 +00:00
|
|
|
from __future__ import (absolute_import, division, print_function)
|
|
|
|
__metaclass__ = type
|
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
from datetime import datetime
|
2016-02-06 08:00:07 +00:00
|
|
|
|
2017-11-21 00:01:22 +00:00
|
|
|
from ansible.errors import AnsibleError
|
|
|
|
from ansible.module_utils._text import to_native
|
2018-10-04 19:44:50 +00:00
|
|
|
from ansible.plugins.action import ActionBase
|
2018-08-24 01:12:12 +00:00
|
|
|
from ansible.plugins.action.reboot import ActionModule as RebootActionModule
|
2016-09-07 05:54:17 +00:00
|
|
|
|
2016-02-06 08:00:07 +00:00
|
|
|
try:
|
|
|
|
from __main__ import display
|
|
|
|
except ImportError:
|
|
|
|
from ansible.utils.display import Display
|
|
|
|
display = Display()
|
|
|
|
|
2016-09-07 05:54:17 +00:00
|
|
|
|
2016-02-06 08:00:07 +00:00
|
|
|
class TimedOutException(Exception):
|
|
|
|
pass
|
|
|
|
|
2016-09-07 05:54:17 +00:00
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
class ActionModule(RebootActionModule, ActionBase):
|
2016-02-06 08:00:07 +00:00
|
|
|
TRANSFERS_FILES = False
|
2018-08-30 13:40:36 +00:00
|
|
|
_VALID_ARGS = frozenset((
|
|
|
|
'connect_timeout', 'connect_timeout_sec', 'msg', 'post_reboot_delay', 'post_reboot_delay_sec', 'pre_reboot_delay', 'pre_reboot_delay_sec',
|
|
|
|
'reboot_timeout', 'reboot_timeout_sec', 'shutdown_timeout', 'shutdown_timeout_sec', 'test_command',
|
|
|
|
))
|
2016-02-06 08:00:07 +00:00
|
|
|
|
2018-10-04 19:44:50 +00:00
|
|
|
DEFAULT_BOOT_TIME_COMMAND = "(Get-WmiObject -ClassName Win32_OperatingSystem).LastBootUpTime"
|
2017-08-30 02:08:12 +00:00
|
|
|
DEFAULT_CONNECT_TIMEOUT = 5
|
|
|
|
DEFAULT_PRE_REBOOT_DELAY = 2
|
2018-08-24 01:12:12 +00:00
|
|
|
DEFAULT_SHUTDOWN_COMMAND_ARGS = '/r /t %d /c "%s"'
|
|
|
|
DEFAULT_SUDOABLE = False
|
2016-09-07 05:54:17 +00:00
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
DEPRECATED_ARGS = {
|
|
|
|
'shutdown_timeout': '2.5',
|
|
|
|
'shutdown_timeout_sec': '2.5',
|
|
|
|
}
|
2016-02-06 08:00:07 +00:00
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
def construct_command(self):
|
|
|
|
shutdown_command = self.DEFAULT_SHUTDOWN_COMMAND
|
|
|
|
pre_reboot_delay = int(self._task.args.get('pre_reboot_delay', self._task.args.get('pre_reboot_delay_sec', self.DEFAULT_PRE_REBOOT_DELAY)))
|
|
|
|
msg = self._task.args.get('msg', self.DEFAULT_REBOOT_MESSAGE)
|
|
|
|
shutdown_command_args = self.DEFAULT_SHUTDOWN_COMMAND_ARGS % (pre_reboot_delay, msg)
|
2016-02-06 08:00:07 +00:00
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
reboot_command = '%s %s' % (shutdown_command, shutdown_command_args)
|
|
|
|
return reboot_command
|
2017-08-29 21:44:50 +00:00
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
def perform_reboot(self):
|
|
|
|
display.debug("Rebooting server")
|
2017-08-29 21:44:50 +00:00
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
remote_command = self.construct_command()
|
|
|
|
reboot_result = self._low_level_execute_command(remote_command, sudoable=self.DEFAULT_SUDOABLE)
|
2018-08-28 21:25:37 +00:00
|
|
|
result = {}
|
|
|
|
result['start'] = datetime.utcnow()
|
2017-11-21 00:01:22 +00:00
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
pre_reboot_delay = int(self._task.args.get('pre_reboot_delay', self._task.args.get('pre_reboot_delay_sec', self.DEFAULT_PRE_REBOOT_DELAY)))
|
2016-02-06 08:00:07 +00:00
|
|
|
|
2017-01-10 10:56:38 +00:00
|
|
|
# Test for "A system shutdown has already been scheduled. (1190)" and handle it gracefully
|
2018-08-28 21:25:37 +00:00
|
|
|
stdout = reboot_result['stdout']
|
|
|
|
stderr = reboot_result['stderr']
|
|
|
|
if reboot_result['rc'] == 1190 or (reboot_result['rc'] != 0 and "(1190)" in reboot_result['stderr']):
|
2017-08-29 21:44:50 +00:00
|
|
|
display.warning('A scheduled reboot was pre-empted by Ansible.')
|
2017-01-10 10:56:38 +00:00
|
|
|
|
|
|
|
# Try to abort (this may fail if it was already aborted)
|
2018-08-24 01:12:12 +00:00
|
|
|
result1 = self._low_level_execute_command('shutdown /a', sudoable=self.DEFAULT_SUDOABLE)
|
2017-01-10 10:56:38 +00:00
|
|
|
|
|
|
|
# Initiate reboot again
|
2018-08-28 21:25:37 +00:00
|
|
|
result2 = self._low_level_execute_command('shutdown /r /t %d' % pre_reboot_delay, sudoable=self.DEFAULT_SUDOABLE)
|
2017-01-10 10:56:38 +00:00
|
|
|
|
2018-08-28 21:25:37 +00:00
|
|
|
reboot_result['rc'] = result2['rc']
|
|
|
|
stdout += result1['stdout'] + result2['stdout']
|
|
|
|
stderr += result1['stderr'] + result2['stderr']
|
2018-08-24 01:12:12 +00:00
|
|
|
|
|
|
|
if reboot_result['rc'] != 0:
|
2016-02-06 08:00:07 +00:00
|
|
|
result['failed'] = True
|
|
|
|
result['rebooted'] = False
|
2018-08-28 21:25:37 +00:00
|
|
|
result['msg'] = "Shutdown command failed, error was: %s %s" % (to_native(stdout.strip()), to_native(stderr.strip()))
|
2016-02-06 08:00:07 +00:00
|
|
|
return result
|
|
|
|
|
2018-08-24 01:12:12 +00:00
|
|
|
result['failed'] = False
|
|
|
|
|
2017-11-21 00:01:22 +00:00
|
|
|
# Get the original connection_timeout option var so it can be reset after
|
2016-02-06 08:00:07 +00:00
|
|
|
try:
|
2018-08-24 01:12:12 +00:00
|
|
|
self._original_connection_timeout = self._connection.get_option('connection_timeout')
|
2017-11-21 00:01:22 +00:00
|
|
|
except AnsibleError:
|
2018-08-24 01:12:12 +00:00
|
|
|
display.debug("%s: connect_timeout connection option has not been set" % self._task.action)
|
2017-08-29 21:44:50 +00:00
|
|
|
|
2016-02-06 08:00:07 +00:00
|
|
|
return result
|