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