blob: 4961b9004d7feb66c0c88184902bac89004b0b15 [file] [log] [blame]
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Reboot the slave machine, unless it is run in a development
envirenment (TESTING_MASTER environment variable is defined).
The reboot is also controlled by reboot_on_step_timeout flag in the
master config.
"""
import os
import subprocess
import sys
import time
def Log(message):
"""Log a message.
Use the Buildbot/Twisted log facility if it's imported. Otherwise
assume we are running in a terminal from a command line.
"""
log_mod = sys.modules.get('twisted.python.log')
if log_mod:
log_mod.msg(message)
else:
print message
def IssueReboot():
"""Issue reboot command according to platform type."""
if sys.platform.startswith('win'):
subprocess.call(['shutdown', '-r', '-f', '-t', '1'])
elif sys.platform in ('darwin', 'posix', 'linux2'):
subprocess.call(['sudo', 'shutdown', '-r', 'now'])
else:
raise NotImplementedError('Implement IssueReboot function '
'for %s' % sys.platform)
def SigTerm(*args):
"""Receive a SIGTERM and do nothing."""
Log('SigTerm: Received SIGTERM, doing nothing.')
def UpdateSignals():
"""Override the twisted SIGTERM handler with our own.
Ensure that the signal module is available and do nothing if it is not.
"""
try:
import signal
except ImportError:
Log('UpdateSignals: Warning: signal module unavailable -- '
'not installing signal handlers.')
return
# Twisted installs a SIGTERM signal handler which tries to shut the system
# down. Use our own handler instead.
Log('UpdateSignals: installed new SIGTERM handler')
signal.signal(signal.SIGTERM, SigTerm)
def Sleep(desired_sleep):
"""Sleep for |desired_sleep| seconds.
time.sleep() can return in less time than desired if the process receives
a signal. We expect that to happen when the shutdown we run causes the system
to send a TERM signal to us. When that happens, we need to ensure we go
back to sleep for the remainder of the time that's left."""
actual_sleep = 0
while True:
sleep_length = desired_sleep - actual_sleep
start_time = int(time.time())
Log('Sleep: Sleeping for %s seconds' % sleep_length)
time.sleep(sleep_length)
this_sleep = int(time.time()) - start_time
Log('Sleep: Actually slept for %s seconds' % actual_sleep)
if this_sleep < 0:
Log('Sleep: Error, this_sleep was %d (less than zero)' % actual_sleep)
break
actual_sleep += this_sleep
if actual_sleep >= desired_sleep:
Log('Sleep: Finished sleeping, returning' % actual_sleep)
break
Log('Sleep: Awoke too early, sleeping again')
def ReallyReboot():
"""Repeatedly try to reboot the system.
In IssueReboot, use subprocess.call() instead of Popen() to ensure that
run_slave.py doesn't exit at all when it calls Reboot(). This ensures that
run_slave.py won't exit and trigger any cleanup routines by whatever
launched run_slave.py.
Since our strategy depends on Reboot() never returning, raise an exception
if that should occur to make it clear in logs that an error condition is
occurring somewhere.
"""
Log('Reboot: Starting system reboot cycle')
UpdateSignals()
i = 0
try:
while True:
Log('Reboot: Reboot cycle %d' % i)
IssueReboot()
Sleep(60)
i += 1
except:
Log('Reboot: failed to issue a reboot: %s' % str(sys.exc_info()[0]))
raise
def Reboot():
"""Reboot the buildbot slave machine.
This behavior is controlled by the reboot_on_step_timeout flag in
the active master configuration.
"""
# This envrionment is defined only when testing the slave on a dev machine.
is_testing = 'TESTING_MASTER' in os.environ
should_reboot = False
try:
import config_bootstrap
master = getattr(config_bootstrap.Master, 'active_master', None)
should_reboot = getattr(master, 'reboot_on_step_timeout', True)
Log('Reboot: reboot_on_step_timeout = %r (from master_site_config: %r)'
% (should_reboot, master))
except: # pylint: disable=W0702
Log('Reboot: failed to read master config: %s' % str(sys.exc_info()[0]))
return
if should_reboot:
if not is_testing:
Log('Reboot: Issuing Reboot...')
ReallyReboot()
else:
Log('Reboot: Testing mode enabled, skipping the actual reboot')