Method: Unix::Exec#reboot

Defined in:
lib/beaker/host/unix/exec.rb

#reboot(wait_time = 10, max_connection_tries = 9, uptime_retries = 18) ⇒ Object

Reboots the host, comparing uptime values to verify success Will throw an exception RebootFailure if it fails

Parameters:

  • (defaults to: 10)

    How long to wait after sending the reboot command before attempting to check in on the host

  • (defaults to: 9)

    How many times to retry connecting to host after reboot. Note that there is an fibbonacci backoff when attempting retries so the time spent waiting on this can grow quickly.

  • (defaults to: 18)

    How many times to check to see if the value of the uptime has reset.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/beaker/host/unix/exec.rb', line 14

def reboot(wait_time = 10, max_connection_tries = 9, uptime_retries = 18)
  require 'time'

  attempts = 0

  # Some systems don't support 'last -F reboot' but it has second granularity
  boot_time_cmd = 'last -F reboot || who -b'

  # Try to match all of the common formats for 'last' and 'who'
  current_year = Time.now.strftime("%Y")
  boot_time_regex = Regexp.new(%{((?:#{(Date::ABBR_DAYNAMES + Date::ABBR_MONTHNAMES).compact.join('|')}|#{current_year}).+?(\\d+:\\d+)+?(?::(\\d+).+?#{current_year})?)})

  original_boot_time_str = nil
  original_boot_time_line = nil
  begin
    attempts += 1
    # Number of seconds to sleep before rebooting.
    reboot_sleep = 1

    original_boot_time_str = exec(Beaker::Command.new(boot_time_cmd), { :max_connection_tries => max_connection_tries, :silent => true }).stdout
    original_boot_time_line = original_boot_time_str.lines.grep(/boot/).first

    raise Beaker::Host::RebootWarning, "Could not find system boot time using '#{boot_time_cmd}': '#{original_boot_time_str}'" unless original_boot_time_line

    original_boot_time_matches = original_boot_time_line.scan(boot_time_regex).last

    raise Beaker::Host::RebootWarning, "Found no valid times in '#{original_boot_time_line}'" unless original_boot_time_matches

    original_boot_time = Time.parse(original_boot_time_matches.first)

    reboot_sleep = (61 - Time.now.strftime("%S").to_i) unless original_boot_time_matches.last

    @logger.notify("Sleeping #{reboot_sleep} seconds before rebooting")

    sleep(reboot_sleep)

    exec(Beaker::Command.new('/bin/systemctl reboot -i || reboot || /sbin/shutdown -r now'), :expect_connection_failure => true)
  rescue ArgumentError => e
    raise Beaker::Host::RebootFailure, "Unable to parse time: #{e.message}"
  rescue Beaker::Host::RebootWarning => e
    raise if attempts > uptime_retries

    @logger.warn(e.message)
    @logger.warn("Retrying #{uptime_retries - attempts} more times.")
    retry
  rescue StandardError => e
    raise if attempts > uptime_retries

    @logger.warn("Unexpected Exception: #{e.message}")
    @logger.warn("Retrying #{uptime_retries - attempts} more times.")
    @logger.warn(e.backtrace[0, 3].join("\n"))
    @logger.debug(e.backtrace.join("\n"))
    retry
  end

  attempts = 0
  begin
    attempts += 1

    # give the host a little time to shutdown
    @logger.debug("Waiting #{wait_time} for host to shut down.")
    sleep wait_time

    # Accept all exit codes because this may fail due to the parallel nature of systemd
    current_boot_time_str = exec(Beaker::Command.new(boot_time_cmd), { :max_connection_tries => max_connection_tries, :silent => true, :accept_all_exit_codes => true }).stdout
    current_boot_time_line = current_boot_time_str.lines.grep(/boot/).first

    raise Beaker::Host::RebootWarning, "Could not find system boot time using '#{boot_time_cmd}': '#{current_boot_time_str}'" unless current_boot_time_line

    current_boot_time_matches = current_boot_time_line.scan(boot_time_regex).last

    raise Beaker::Host::RebootWarning, "Found no valid times in '#{current_boot_time_line}'" unless current_boot_time_matches

    current_boot_time = Time.parse(current_boot_time_matches.first)

    @logger.debug("Original Boot Time: #{original_boot_time}")
    @logger.debug("Current Boot Time: #{current_boot_time}")

    # If this is *exactly* the same then there is really no good way to detect a reboot
    raise Beaker::Host::RebootFailure, "Boot time did not reset. Reboot appears to have failed." if current_boot_time == original_boot_time
  rescue ArgumentError => e
    raise Beaker::Host::RebootFailure, "Unable to parse time: #{e.message}"
  rescue Beaker::Host::RebootFailure => e
    raise
  rescue Beaker::Host::RebootWarning => e
    raise if attempts > uptime_retries

    @logger.warn(e.message)
    @logger.warn("Retrying #{uptime_retries - attempts} more times.")
    retry
  rescue StandardError => e
    raise if attempts > uptime_retries

    @logger.warn("Unexpected Exception: #{e.message}")
    @logger.warn("Retrying #{uptime_retries - attempts} more times.")
    @logger.warn(e.backtrace[0, 3].join("\n"))
    @logger.debug(e.backtrace.join("\n"))
    retry
  end
end