Repository: ansible/ansible

Issue:
win_reboot fails when a reboot is already ongoing
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Feature Idea

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
win_reboot

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
v2.3

##### SUMMARY
<!--- Explain the problem briefly -->
When automating things on Windows (due to bug #19966) I discovered that when a system is on course of being rebooted, the `win_reboot` task will fail with a message like **"System is already rebooting."**. (I will post the verbatim message when I hit this one again !)

But in this unique case, win_reboot could simply wait until the system is rebooted, rather than consider it a failed task.


Discussion:
Interesting- haven't seen that before. Is that failure output from the `shutdown` command, or something coming back from the WinRM layer? I hate doing crap like parsing error strings to determine behavior, especially since that message is quite possibly localized (though we could probably force InvariantCulture to get the English version)- if it is what's coming back from shutdown, hopefully there's something else we could check.

I've hassled some Microsoft folks about a related issue where WinRM becomes operational during post-reboot update application, but is then followed by another (unmanaged) reboot (causing win_reboot to return successfully, then the machine goes down again). That obviously wreaks havoc with things- I'm sure there's *something* we could check to see that we're in that situation and not return from win_reboot until the system is stable again, but they have yet to tell me what it might be.
I don't know, but it's easy to find out. Just do a win_reboot in async with a long delay.
I would expect this to trigger this.

It happened to me because pause was not working (#19966) and a previous reboot was ongoing because it takes more time after installing/configuring AD. The **wait_for_connection** action plugin described in #19998 will be my solution to prevent this in the future. No need to pause, or check tcp ports unreliably.
Can't do an async reboot (since it's already an action, async can only wrap a module, not an action). If I do it after a long-delayed `shutdown` command, I get "A system shutdown has already been scheduled. (1190)" - is that what you saw?

Just Googling around, it doesn't seem like there's a documented place to look for a pending shutdown- if that is indeed the error you saw, probably the best thing we could do would be to do a `shutdown /a`,  ignore exit code 1116, then issue the normal shutdown command.
Or I suppose we could just do the normal shutdown, watch for 1190, and only do the `/a` as an error recovery...
@nitzmahone Yes, that is the exact error. I sometimes experienced the problem after a domain-join with `/Reboot`. Followed by a win_reboot. That obviously was a bug on my part ;-) But it did cause win_reboot to fail from time to time.

I think your last suggestion makes the most sense. It will override the existing shutdown, so I don't know if some people may want a different behaviour. But at the very least we should send a warning by the module to the end-user that an existing shutdown operation was already on-going and we pre-empted it.

Let me do the necessary and create a PR for this.

Resolving patch:
diff --git a/lib/ansible/modules/windows/win_reboot.py b/lib/ansible/modules/windows/win_reboot.py
--- a/lib/ansible/modules/windows/win_reboot.py
+++ b/lib/ansible/modules/windows/win_reboot.py
@@ -57,21 +57,23 @@
     description:
     - Message to display to users
     default: Reboot initiated by Ansible
+notes:
+- If a shutdown was already scheduled on the system, C(win_reboot) will abort the scheduled shutdown and enforce its own shutdown.
 author:
     - Matt Davis (@nitzmahone)
 '''
 
 EXAMPLES='''
-# unconditionally reboot the machine with all defaults
+# Unconditionally reboot the machine with all defaults
 - win_reboot:
 
-# apply updates and reboot if necessary
+# Apply updates and reboot if necessary
 - win_updates:
   register: update_result
 - win_reboot:
   when: update_result.reboot_required
 
-# reboot a slow machine that might have lots of updates to apply
+# Reboot a slow machine that might have lots of updates to apply
 - win_reboot:
     shutdown_timeout_sec: 3600
     reboot_timeout_sec: 3600
diff --git a/lib/ansible/plugins/action/win_reboot.py b/lib/ansible/plugins/action/win_reboot.py
--- a/lib/ansible/plugins/action/win_reboot.py
+++ b/lib/ansible/plugins/action/win_reboot.py
@@ -83,9 +83,21 @@ def run(self, tmp=None, task_vars=None):
 
         result = super(ActionModule, self).run(tmp, task_vars)
 
-        # initiate reboot
+        # 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'] = ( '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')
+
+            # Initiate reboot again
+            (rc, stdout2, stderr2) = self._connection.exec_command('shutdown /r /t %d' % pre_reboot_delay_sec)
+            stdout += stdout1 + stdout2
+            stderr += stderr1 + stderr2
+
         if rc != 0:
             result['failed'] = True
             result['rebooted'] = False


Test patch: