Puppet Plan: reboot

Defined in:
plans/init.pp

Overview

Reboots targets and waits for them to be available again.

Parameters:

  • targets (TargetSpec)

    Targets to reboot.

  • message (Optional[String]) (defaults to: undef)

    Message to log with the reboot (for platforms that support it).

  • reboot_delay (Integer[1]) (defaults to: 1)

    How long (in seconds) to wait before rebooting. Defaults to 1.

  • disconnect_wait (Integer[0]) (defaults to: 10)

    How long (in seconds) to wait before checking whether the server has rebooted. Defaults to 10.

  • reconnect_timeout (Integer[0]) (defaults to: 180)

    How long (in seconds) to attempt to reconnect before giving up. Defaults to 180.

  • retry_interval (Integer[0]) (defaults to: 1)

    How long (in seconds) to wait between retries. Defaults to 1.

  • fail_plan_on_errors (Boolean) (defaults to: true)

    Raise an error if any targets do not successfully reboot. Defaults to true.



10
11
12
13
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
114
115
116
117
118
119
120
121
122
123
124
# File 'plans/init.pp', line 10

plan reboot (
  TargetSpec $targets,
  Optional[String] $message = undef,
  Integer[1] $reboot_delay = 1,
  Integer[0] $disconnect_wait = 10,
  Integer[0] $reconnect_timeout = 180,
  Integer[0] $retry_interval = 1,
  Boolean    $fail_plan_on_errors = true,
) {
  $target_objects = get_targets($targets)

  # Short-circuit the plan if the TargetSpec given was empty
  if $target_objects.empty { return ResultSet.new([]) }

  # Get last boot time
  $begin_boot_time_results = without_default_logging() || {
    run_task('reboot::last_boot_time', $target_objects)
  }

  # Reboot; catch errors here because the connection may get cut out from underneath
  run_task('reboot', $targets, timeout => $reboot_delay, message => $message)

  # Use $reboot_delay as wait time, but at least 3s
  $wait = max(3, $reboot_delay)
  ctrl::sleep($wait+$disconnect_wait)

  $start_time = Timestamp()
  # Wait for reboot in a loop
  ## Check if we can connect; if we can retrieve last boot time.
  ## Mark finished for targets with a new last boot time.
  ## If we still have targets check for timeout, sleep if not done.
  $wait_results = without_default_logging() || {
    $reconnect_timeout.reduce({'pending' => $target_objects, 'ok' => []}) |$memo, $_| {
      if ($memo['pending'].empty() or $memo['timed_out']) {
        break()
      }

      $plural = if $memo['pending'].size() > 1 { 's' }
      out::message("Waiting: ${$memo['pending'].size()} target${plural} rebooting")
      $current_boot_time_results = run_task('reboot::last_boot_time', $memo['pending'], _catch_errors => true)

      # Compare boot times
      $failed_results = $current_boot_time_results.filter |$current_boot_time_res| {
        # If this one errored, need to check it again
        if !$current_boot_time_res.ok() {
          true
        }
        else {
          # If this succeeded, then we have a boot time, compare it against the begin_boot_time
          $target_name = $current_boot_time_res.target().name()
          $begin_boot_time_res = $begin_boot_time_results.find($target_name)

          # If the boot times are the same, then we need to check it again
          $current_boot_time_res.value() == $begin_boot_time_res.value()
        }
      }

      # $failed_results is an array of results, turn it into a ResultSet so we can
      # extract the targets from it
      $failed_targets = ResultSet($failed_results).targets()
      $ok_targets = $memo['pending'] - $failed_targets

      # Calculate whether or not timeout has been reached
      $elapsed_time_sec = Integer(Timestamp() - $start_time)
      $timed_out = $elapsed_time_sec >= $reconnect_timeout

      if !$failed_targets.empty() and !$timed_out {
        # sleep for a small time before trying again
        ctrl::sleep($retry_interval)

        # wait for all targets to be available again
        $remaining_time = $reconnect_timeout - $elapsed_time_sec
        wait_until_available($failed_targets, wait_time => $remaining_time, retry_interval => $retry_interval)
      }

      # Build and return the memo for this iteration
      ({
        'pending'   => $failed_targets,
        'ok'        => $memo['ok'] + $ok_targets,
        'timed_out' => $timed_out,
      })
    }
  }

  $err = {
    msg  => 'Target failed to reboot before wait timeout.',
    kind => 'bolt/reboot-timeout',
  }

  $error_set = $wait_results['pending'].map |$target| {
    Result.new($target, {
      _output => 'failed to reboot',
      _error  => $err,
    })
  }
  $ok_set = $wait_results['ok'].map |$target| {
    Result.new($target, {
      _output => 'rebooted',
    })
  }

  $result_set = ResultSet.new($ok_set + $error_set)

  if ($fail_plan_on_errors and !$result_set.ok) {
    fail_plan('One or more targets failed to reboot within the allowed wait time',
      'bolt/reboot-failed', {
        action         => 'plan/reboot',
        result_set     => $result_set,
        failed_targets => $result_set.error_set.targets, # legacy / deprecated
    })
  }
  else {
    return($result_set)
  }
}