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