ethers hook: reduce the probability of data loss
[ganeti-local] / doc / examples / hooks / ethers
1 #!/bin/bash
2
3 # Copyright (C) 2009 Google Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19
20 # This is an example ganeti hook that writes the instance mac addresses in the
21 # node's /etc/ether file. It will pic up the first nic connected to the
22 # TARGET_BRIDGE bridge, and write it down with the syntax "MAC  INSTANCE_NAME".
23
24 # The hook will also send a HUP signal the daemon whose PID is in
25 # DAEMON_PID_FILE, so that it can load the new /etc/ethers file and use it.
26 # This has been tested in conjunction with dnsmasq's dhcp implementation.
27
28 # It will also remove any other occurrences for the same instance in the
29 # aformentioned file. This hook supports the "instance-add", "instance-modify"
30 # "instance-remove", and "instance-mirror-replace" ganeti post hook paths. To
31 # install it add a symlink from those hooks' directories to where this file is
32 # installed (with a mode which permits execution).
33
34 # TARGET_BRIDGE: We'll only add the first nic which gets connected to this
35 # bridge to /etc/ethers.
36 TARGET_BRIDGE="br0"
37 DAEMON_PID_FILE="/var/run/dnsmasq.pid"
38 LOCKFILE="/var/lock/ganeti_ethers.lock"
39 LOCKTIMEOUT=10
40 LOCKSLEEP=2
41
42 hooks_path=$GANETI_HOOKS_PATH
43 [ -n "$hooks_path" ] || exit 1
44 instance=$GANETI_INSTANCE_NAME
45 [ -n "$instance" ] || exit 1
46 nic_count=$GANETI_INSTANCE_NIC_COUNT
47
48 acquire_lockfile() {
49   NOW=$(date +%s)
50   TIMEOUT=$(($NOW + $LOCKTIMEOUT))
51   while ! ( set -o noclobber; echo "$$" > $LOCKFILE) 2> /dev/null ; do
52     NOW=$(date +%s)
53     if [ $NOW -ge $TIMEOUT ]; then
54       echo "Cannot acquire lockfile for ethers update, giving up"
55       exit 1
56     fi
57     echo "Cannot acquire lockfile for ethers update, waiting"
58     sleep $LOCKSLEEP
59   done
60   trap "rm -f $LOCKFILE" EXIT
61 }
62
63 update_ethers_from_new() {
64   chmod 644 /etc/ethers.new
65   mv /etc/ethers.new /etc/ethers
66   [ -f "$DAEMON_PID_FILE" ] && kill -HUP $(< $DAEMON_PID_FILE)
67 }
68
69 if [ "$hooks_path" = "instance-add" -o \
70      "$hooks_path" = "instance-modify" -o \
71      "$hooks_path" = "instance-mirror-replace" ]
72 then
73   for i in $(seq 0 $((nic_count - 1)) ); do
74     bridge_var="GANETI_INSTANCE_NIC${i}_BRIDGE"
75     bridge=${!bridge_var}
76     if [ -n "$bridge" -a "$bridge" = "$TARGET_BRIDGE" ]; then
77       mac_var="GANETI_INSTANCE_NIC${i}_MAC"
78       mac=${!mac_var}
79       acquire_lockfile
80       cat /etc/ethers | awk -- "! /^([[:xdigit:]:]*)[[:blank:]]+$instance\>/;
81       END {print \"$mac\t$instance\"}" > /etc/ethers.new
82       update_ethers_from_new
83       break
84     fi
85   done
86 fi
87 if [ "$hooks_path" = "instance-remove" -o \
88        \( "$hooks_path" = "instance-modify" -a "$nic_count" -eq 0 \) ]; then
89   acquire_lockfile
90   cat /etc/ethers | awk -- "! /^([[:xdigit:]:]*)[[:blank:]]+$instance\>/" \
91     > /etc/ethers.new
92   update_ethers_from_new
93 fi
94