vcluster-setup: Watcher, init script
[ganeti-local] / tools / vcluster-setup.in
1 #!/bin/bash
2 #
3
4 # Copyright (C) 2012 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21 set -e -u -o pipefail
22 shopt -s extglob
23
24 readonly self=$(readlink -f $0)
25 readonly ensure_dirs=@PKGLIBDIR@/ensure-dirs
26 readonly action_shortcuts=( start stop restart status watcher )
27 readonly default_nodecount=5
28 readonly default_instcount=10
29 readonly default_netprefix=192.0.2
30 readonly default_netdev=eth0
31 readonly default_initscript=@SYSCONFDIR@/init.d/ganeti
32 readonly cluster_name=cluster
33
34 # IP address space:
35 # Cluster: .1
36 # Nodes: .10-.99
37 # Instances: .100-.254
38 readonly first_node_ipaddr_octet=10
39 readonly first_inst_ipaddr_octet=100
40
41 readonly max_node_count=$((first_inst_ipaddr_octet - first_node_ipaddr_octet))
42 readonly max_instance_count=$((255 - first_inst_ipaddr_octet))
43
44 usage() {
45   echo "Usage: $0 [-E] [-N] [-c <number>] [-i <number>] [-p <prefix>]"\
46        '[-n <netdev>] [-I <path>] <directory>'
47   echo
48   echo 'Options:'
49   echo "  -c  Number of virtual nodes (defaults to $default_nodecount)"
50   echo "  -i  Number of instances (defaults to $default_instcount)"
51   echo "  -p  IPv4 network prefix (defaults to $default_netprefix)"
52   echo '  -n  Network device for virtual IP addresses (defaults to'\
53        "$default_netdev)"
54   echo "  -I  Path to init script (defaults to $default_initscript)"
55   echo '  -E  Do not modify /etc/hosts'
56   echo '  -N  Do not configure networking'
57 }
58
59 # Variables for options
60 nodecount=$default_nodecount
61 instcount=$default_instcount
62 netprefix=$default_netprefix
63 netdev=$default_netdev
64 initscript=$default_initscript
65 etchosts=1
66 networking=1
67
68 # Parse options
69 while getopts :hENc:p:n:i:I: opt; do
70   case "$opt" in
71     h)
72       usage
73       exit 0
74     ;;
75     c)
76       nodecount="$OPTARG"
77       if [[ "$nodecount" != +([0-9]) ]]; then
78         echo "Invalid node count number: $nodecount" >&2
79         exit 1
80       elif (( nodecount > max_node_count )); then
81         echo "Node count must be $max_node_count or lower" >&2
82         exit 1
83       fi
84     ;;
85     i)
86       instcount="$OPTARG"
87       if [[ "$instcount" != +([0-9]) ]]; then
88         echo "Invalid instance count number: $instcount" >&2
89         exit 1
90       elif (( instcount > max_instance_count )); then
91         echo "Instance count must be $max_instance_count or lower" >&2
92         exit 1
93       fi
94     ;;
95     p)
96       netprefix="$OPTARG"
97       if [[ "$netprefix" != +([0-9]).+([0-9]).+([0-9]) ]]; then
98         echo "Invalid network prefix: $netprefix" >&2
99         exit 1
100       fi
101     ;;
102     n)
103       netdev="$OPTARG"
104       if ! ip link show $netdev >/dev/null; then
105         echo "Invalid network device: $netdev" >&2
106         exit 1
107       fi
108     ;;
109     I)
110       initscript="$OPTARG"
111       if [[ ! -x $initscript ]]; then
112         echo "Init script '$initscript' is not executable" >&2
113         exit 1
114       fi
115       ;;
116     E)
117       etchosts=
118       ;;
119     N)
120       networking=
121       ;;
122     \?)
123       echo "Invalid option: -$OPTARG" >&2
124       usage >&2
125       exit 1
126       ;;
127     :)
128       echo "Option -$OPTARG requires an argument" >&2
129       usage >&2
130       exit 1
131       ;;
132   esac
133 done
134
135 shift $((OPTIND - 1))
136
137 if [[ "$#" != 1 ]]; then
138   usage
139   exit 1
140 fi
141
142 readonly rootdir=$1; shift
143
144 if [[ ! -d "$rootdir" ]]; then
145   echo "Directory '$rootdir' does not exist!" >&2
146   exit 1
147 fi
148
149 if (( $nodecount < 1 )); then
150   echo "Must create at least one node, currently requested $nodecount" >&2
151   exit 1
152 fi
153
154 node_hostname() {
155   local -r number="$1"
156
157   echo "node$((number + 1))"
158 }
159
160 instance_hostname() {
161   local -r number="$1"
162
163   echo "instance$((number + 1))"
164 }
165
166 node_ipaddr() {
167   local -r number="$1"
168
169   echo "$netprefix.$((first_node_ipaddr_octet + number))"
170 }
171
172 instance_ipaddr() {
173   local -r number="$1"
174
175   echo "$netprefix.$((first_inst_ipaddr_octet + number))"
176 }
177
178 setup_node() {
179   local -r number="$1"
180   local -r nodedir=$rootdir/$(node_hostname $number)
181
182   echo "Setting up node '$(node_hostname $number)' ..." >&2
183
184   if [[ ! -d $nodedir ]]; then
185     mkdir $nodedir
186   fi
187
188   mkdir -p \
189     $nodedir@SYSCONFDIR@/default \
190     $nodedir@LOCALSTATEDIR@/lock\
191     $nodedir@LOCALSTATEDIR@/{lib,log,run}/ganeti
192
193   GANETI_HOSTNAME=$(node_hostname $number) \
194   GANETI_ROOTDIR=$nodedir \
195   $ensure_dirs
196
197   local -r daemon_args="-b $(node_ipaddr $number)"
198
199   cat > $nodedir/etc/default/ganeti <<EOF
200 # Default settings for virtual node $i
201 NODED_ARGS='--no-mlock $daemon_args'
202 MASTERD_ARGS=''
203 RAPI_ARGS='$daemon_args'
204 CONFD_ARGS='$daemon_args'
205
206 export GANETI_ROOTDIR='$nodedir'
207 export GANETI_HOSTNAME='$(node_hostname $number)'
208 EOF
209
210   cat > $nodedir/cmd <<EOF
211 #!/bin/bash
212
213 export GANETI_ROOTDIR='$nodedir'
214 export GANETI_HOSTNAME='$(node_hostname $number)'
215
216 bash -c "\$*"
217 EOF
218   chmod +x $nodedir/cmd
219 }
220
221 setup_all_nodes() {
222   for ((i=0; i < nodecount; ++i)); do
223     setup_node $i
224   done
225 }
226
227 setup_etc_hosts() {
228   echo 'Configuring /etc/hosts ...' >&2
229   (
230     set -e -u
231     local -r tmpfile=$(mktemp /etc/hosts.vcluster.XXXXX)
232     trap "rm -f $tmpfile" EXIT
233     {
234       egrep -v "^$netprefix.[[:digit:]]+[[:space:]]" /etc/hosts
235       echo "$netprefix.1 $cluster_name"
236       for ((i=0; i < nodecount; ++i)); do
237         echo "$(node_ipaddr $i) $(node_hostname $i)"
238       done
239       for ((i=0; i < instcount; ++i)); do
240         echo "$(instance_ipaddr $i) $(instance_hostname $i)"
241       done
242     } > $tmpfile && \
243     chmod 0644 $tmpfile && \
244     mv $tmpfile /etc/hosts && \
245     trap - EXIT
246   )
247 }
248
249 setup_network_interfaces() {
250   echo 'Configuring network ...' >&2
251   for ((i=0; i < nodecount; ++i)); do
252     local ipaddr="$(node_ipaddr $i)/32"
253     ip addr del "$ipaddr" dev "$netdev" || :
254     ip addr add "$ipaddr" dev "$netdev"
255   done
256 }
257
258 setup_scripts() {
259   echo 'Configuring helper scripts ...' >&2
260   for action in "${action_shortcuts[@]}"; do
261     {
262       echo '#!/bin/bash'
263       for ((i=0; i < nodecount; ++i)); do
264         local name=$(node_hostname $i)
265         if [[ $action = watcher ]]; then
266           echo "echo 'Running watcher for virtual node \"$name\" ..."
267           echo "$name/cmd ganeti-watcher \"\$@\""
268         else
269           echo "echo 'Action \"$action\" for virtual node \"$name\" ...'"
270           echo "$name/cmd $initscript $action \"\$@\""
271         fi
272       done
273     } > $rootdir/$action-all
274     chmod +x $rootdir/$action-all
275   done
276 }
277
278 show_info() {
279   cat <<EOF
280 Virtual cluster setup is complete.
281
282 Root directory: $rootdir
283 Cluster name: $cluster_name
284 EOF
285
286   echo 'Nodes:' $(for ((i=0; i < nodecount; ++i)); do node_hostname $i; done)
287
288   cat <<EOF
289
290 Initialize cluster:
291   cd $rootdir && node1/cmd gnt-cluster init --no-etc-hosts \\
292     --no-ssh-init --no-lvm-storage --no-drbd-storage $cluster_name
293
294 Change cluster settings:
295   cd $rootdir && node1/cmd gnt-cluster modify \\
296     --enabled-hypervisors=fake --specs-nic-count=min=0 \\
297     --specs-disk-size=min=0 --specs-disk-count=min=0
298
299 Add node:
300   cd $rootdir && node1/cmd gnt-node add --no-ssh-key-check node2
301 EOF
302 }
303
304 setup_all_nodes
305 if [[ -n "$etchosts" ]]; then
306   setup_etc_hosts
307 fi
308 if [[ -n "$networking" ]]; then
309   setup_network_interfaces
310 fi
311 setup_scripts
312 show_info
313
314 exit 0
315
316 # vim: set sw=2 sts=2 et :