Install ganeti-reboot-cluster
[mirror/dsa-puppet.git] / modules / ganeti2 / files / ganeti-reboot-cluster
1 #!/bin/bash
2
3 set -e
4 set -o pipefail
5 set -u
6
7 usage() {
8   echo "Usage: $0 [-n <node-list>] [-f] [ -M <newmaster> ] [up|down]"
9   echo " -M is for internal use only (used in n>2 clusters if we want to reboot the master first)"
10 }
11 error_usage() {
12   usage >&2
13   exit 1
14 }
15
16 nodelist="node-list"
17 newmaster=""
18 force="0"
19
20 while getopts "fhn:M:" OPTION; do
21   case "$OPTION" in
22     f)
23       force="1"
24       ;;
25     h)
26       usage
27       exit 0
28       ;;
29     M)
30       newmaster="$OPTARG"
31       ;;
32     n)
33       nodelist="$OPTARG"
34       if ! [  -e "$nodelist" ]; then
35         echo >&2 "nodelist $nodelist not found."
36         exit 1
37       fi
38       ;;
39     *)
40       error_usage
41   esac
42 done
43 shift $(($OPTIND - 1))
44
45 direction="${1:-up}"
46 [ "$#" -ge 1 ] && shift
47 case "$direction" in
48   up)   print_list=tac;;
49   down) print_list=cat;;
50   *) error_usage;;
51 esac
52
53 [ "$#" -gt 0 ] && error_usage
54
55 count_instances() {
56   gnt-instance list --no-headers -o status --filter '(pnode == "'"$1"'")' | grep -c -v ADMIN_down
57 }
58 has_instances() {
59   if [ "$(count_instances "$1")" != 0 ]; then
60     return 0
61   else
62     return 1
63   fi
64 }
65
66 reboot_host() {
67   local tgt
68   local max_wait
69   local wait_until
70   local sleep_time
71
72   tgt="$1"
73
74   if has_instances "$tgt"; then
75     echo >&2 "$tgt not empty."
76     exit 1
77   fi
78
79   ssh -n -l root "$tgt" shutdown -r 1 "'reboot requested by $0 on $(hostname -f)'"
80
81   # wait for target to go down:
82   max_wait='300 seconds'
83   wait_until=$(date -d "now +$max_wait" +%s)
84   while ping -c 5 -q "$tgt" > /dev/null; do
85     echo "[$(date)] $tgt is still up (will wait until $(date -d "@$wait_until")."
86     sleep 10
87     if [ "$(date +%s)" -gt "$wait_until" ]; then
88       echo >&2 "Giving up on waiting for $tgt to go down."
89       exit 1
90     fi
91   done
92
93   sleep_time=30
94   echo "[$(date)] $tgt is down.  Pausing for $sleep_time seconds"
95   sleep "$sleep_time"
96
97   max_wait='15 minutes'
98   wait_until=$(date -d "now +$max_wait" +%s)
99   while ! ping -c 5 -q "$tgt" > /dev/null; do
100     echo "[$(date)] $tgt is still down (will wait until $(date -d "@$wait_until")."
101     if [ "$(date +%s)" -gt "$wait_until" ]; then
102       echo >&2 "Giving up on waiting for $tgt to come back."
103       exit 1
104     fi
105     sleep 10
106   done
107
108   sleep_time=30
109   echo "[$(date)] $tgt is up.  Pausing for $sleep_time seconds"
110   sleep "$sleep_time"
111
112   max_wait='15 minutes'
113   wait_until=$(date -d "now +$max_wait" +%s)
114   while ! ssh -n -l root "$tgt" systemctl is-system-running; do
115     echo "[$(date)] $tgt is still booting up (will wait until $(date -d "@$wait_until")."
116     if [ "$(date +%s)" -gt "$wait_until" ]; then
117       echo >&2 "Giving up on waiting for $tgt to come back."
118       exit 1
119     fi
120     sleep 10
121   done
122
123   sleep_time=30
124   echo "[$(date)] $tgt has finished booting.  Pausing for $sleep_time seconds"
125   sleep "$sleep_time"
126 }
127
128 # move down, i.e. from 2 to 1, ..., 14 to 13.
129 moveupdown() {
130   first_tgt="$(${print_list} "$nodelist" | head -n1 | awk '{print $1}')"
131   last_node="$(${print_list} "$nodelist" | tail -n1 | awk '{print $1}')"
132   me=$(hostname -f)
133
134   if has_instances "$first_tgt"; then
135     echo "$first_tgt not empty."
136     exit 1
137   fi
138
139   if [ "$me" != "$last_node" ]; then
140     echo "Making $last_node the new master"
141     ssh -n -l root "$last_node" gnt-cluster master-failover
142     echo "relaunching reboot-cluster on $last_node"
143     tmp="$(ssh -n -l root -t "$last_node" tempfile)"
144     scp "$nodelist" "$last_node:$tmp"
145     ssh -l root -t "$last_node" screen -S reboot-cluster -m sh -c "\"echo Relaunched on $last_node; ganeti-reboot-cluster -f -n '$tmp' -M '$me' '$direction'; echo ganeti-reboot-cluster exited with \$?.; sleep 12h\""
146     echo >&1 "fell through!"
147     exit 1
148   fi
149
150   ${print_list} "$nodelist" | (
151     read tgt dummy
152     while read src dummy; do
153       if has_instances "$tgt"; then
154         echo "$tgt not empty."
155         exit 1
156       fi
157       reboot_host "$tgt"
158
159       if has_instances "$src"; then
160         echo "Migrating from $src to $tgt."
161         if ! gnt-node migrate -f -n "$tgt" "$src"; then
162           echo >&2 "gnt-node migrate exited with an error.  Bailing out."
163           exit 1
164         fi
165       else
166         echo "nothing to migrate from $src to $tgt"
167       fi
168       tgt="$src"
169     done
170
171     if has_instances "$tgt"; then
172       echo "$tgt not empty."
173       exit 1
174     fi
175
176     if ! [ "$tgt" = "$me" ]; then
177       echo >&2 "I was expecting $tgt to be me ($me) here."
178       exit 1
179     fi
180
181     if [ "$newmaster" != "" ]; then
182       echo "Making $newmaster the new master"
183       ssh -n -l root "$newmaster" gnt-cluster master-failover
184     fi
185     shutdown -r 1 "reboot requested by $0"
186     exit
187   )
188 }
189
190 crossmigrate() {
191   me=$(hostname -f)
192   if ! grep -q -F "$me" "$nodelist"; then
193     echo >&2 "my hostname ($me) not found in nodelist"
194     exit 1
195   fi
196   them="$(grep -v -F "$me" "$nodelist")"
197
198   echo "Migrating from $them to $me."
199   if ! gnt-node migrate -f -n "$me" "$them"; then
200     echo >&2 "gnt-node migrate exited with an error.  Bailing out."
201     exit 1
202   fi
203   reboot_host "$them"
204
205   echo "Activating disks.."
206   for instance in $( gnt-instance list -o name --no-headers --filter 'status == "running"' ); do
207     echo " - $instance ..."
208     if ! gnt-instance activate-disks "$instance"; then
209       echo >&2 "gnt-instance activate-disks $instance failed.  Bailing out."
210       exit 1
211     fi
212   done
213
214   echo "Migrating from $me to $them."
215   if ! gnt-node migrate -f -n "$them" "$me"; then
216     echo >&2 "gnt-node migrate exited with an error.  Bailing out."
217     exit 1
218   fi
219
220   at 'now + 30 min' << 'EOF'
221 screen -S hbal -d -m sh -c '
222   echo "Activating disks.."
223   for instance in $( gnt-instance list -o name --no-headers --filter "status == \"running\"" ); do
224     echo " - $instance ..."
225     if ! gnt-instance activate-disks "$instance"; then
226       echo >&2 "Warning: gnt-instance activate-disks $instance failed."
227     fi
228   done
229
230   hbal -L -C -v -X
231   echo "done."
232   sleep 1h
233 '
234 EOF
235   reboot_host "$me"
236 }
237
238 reboot_byrd() {
239   /sbin/shutdown -k 30 < /dev/null
240   sleep 15m
241   gnt-cluster watcher pause 30m
242
243   for i in $(gnt-instance list --no-headers -o name); do
244     gnt-instance shutdown --no-remember --submit $i
245   done
246
247   while pgrep -c '^qemu-|^kvm$' -u root ; do
248     sleep 15;
249     gnt-cluster watcher pause 30m
250   done
251
252   at 'now + 5 min' << EOF
253 sleep 4m;
254 gnt-cluster watcher continue
255 EOF
256
257   /sbin/shutdown -c
258   sleep 5
259   /sbin/shutdown -r 1 </dev/null
260 }
261
262 if [ "${TMUX:-}" = "" ] && [ "${STY:-}" = "" ] ; then
263   echo >&2 "Might want to launch me in a screen or tmux."
264   exit 1
265 fi
266
267 if ! [ "$force" = 1 ]; then
268   echo -n 'really? '
269   read really
270   [ "$really" = "y" ]
271 fi
272
273 ### ensure_nodelist
274 ###################
275 if ! [ -e "$nodelist" ]; then
276   tmp="$(tempfile)"
277   trap "rm -f '$tmp'" EXIT
278   gnt-node list --no-headers -o name > "$tmp"
279   nodelist="$tmp"
280 fi
281
282 lines=$(wc -l < "$nodelist")
283 case "$lines" in
284   0) 
285     echo >&2 "nodelist $nodelist empty."
286     exit 1
287     ;;
288   1)
289     case "$(hostname -f)" in
290       byrd.debian.org)
291         reboot_byrd
292         ;;
293       *)
294         echo >&2 "Only one node."
295         exit 1
296     esac
297     ;;
298   2)
299     crossmigrate
300     ;;
301   *)
302     moveupdown
303     ;;
304 esac
305