blob: 081cc651ecbd170b3f21e52bbe3f2b1568f99146 [file] [log] [blame]
#!/bin/bash
#
# Copyright (C) 2012 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
set -e -u -o pipefail
shopt -s extglob
readonly self=$(readlink -f $0)
readonly ensure_dirs=@PKGLIBDIR@/ensure-dirs
readonly action_shortcuts=( start stop restart status watcher )
readonly default_nodecount=5
readonly default_instcount=10
readonly default_netprefix=192.0.2
readonly default_netdev=eth0
readonly default_initscript=@SYSCONFDIR@/init.d/ganeti
readonly cluster_name=cluster
readonly etc_hosts_filename=/etc/hosts
# IP address space:
# Cluster: .1
# Nodes: .10-.99
# Instances: .100-.254
readonly first_node_ipaddr_octet=10
readonly first_inst_ipaddr_octet=100
readonly max_node_count=$((first_inst_ipaddr_octet - first_node_ipaddr_octet))
readonly max_instance_count=$((255 - first_inst_ipaddr_octet))
usage() {
echo "Usage: $0 [-E] [-N] [-c <number>] [-i <number>] [-p <prefix>]"\
'[-n <netdev>] [-I <path>] <directory>'
echo
echo 'Options:'
echo " -c Number of virtual nodes (defaults to $default_nodecount)"
echo " -i Number of instances (defaults to $default_instcount)"
echo " -p IPv4 network prefix (defaults to $default_netprefix)"
echo ' -n Network device for virtual IP addresses (defaults to'\
"$default_netdev)"
echo " -I Path to init script (defaults to $default_initscript)"
echo " -E Do not modify $etc_hosts_filename"
echo ' -N Do not configure networking'
}
# Variables for options
nodecount=$default_nodecount
instcount=$default_instcount
netprefix=$default_netprefix
netdev=$default_netdev
initscript=$default_initscript
etchosts=1
networking=1
# Parse options
while getopts :hENc:p:n:i:I: opt; do
case "$opt" in
h)
usage
exit 0
;;
c)
nodecount="$OPTARG"
if [[ "$nodecount" != +([0-9]) ]]; then
echo "Invalid node count number: $nodecount" >&2
exit 1
elif (( nodecount > max_node_count )); then
echo "Node count must be $max_node_count or lower" >&2
exit 1
fi
;;
i)
instcount="$OPTARG"
if [[ "$instcount" != +([0-9]) ]]; then
echo "Invalid instance count number: $instcount" >&2
exit 1
elif (( instcount > max_instance_count )); then
echo "Instance count must be $max_instance_count or lower" >&2
exit 1
fi
;;
p)
netprefix="$OPTARG"
if [[ "$netprefix" != +([0-9]).+([0-9]).+([0-9]) ]]; then
echo "Invalid network prefix: $netprefix" >&2
exit 1
fi
;;
n)
netdev="$OPTARG"
if ! ip link show $netdev >/dev/null; then
echo "Invalid network device: $netdev" >&2
exit 1
fi
;;
I)
initscript="$OPTARG"
if [[ ! -x $initscript ]]; then
echo "Init script '$initscript' is not executable" >&2
exit 1
fi
;;
E)
etchosts=
;;
N)
networking=
;;
\?)
echo "Invalid option: -$OPTARG" >&2
usage >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument" >&2
usage >&2
exit 1
;;
esac
done
shift $((OPTIND - 1))
if [[ "$#" != 1 ]]; then
usage
exit 1
fi
readonly rootdir=$1; shift
if [[ ! -d "$rootdir" ]]; then
echo "Directory '$rootdir' does not exist!" >&2
exit 1
fi
if (( $nodecount < 1 )); then
echo "Must create at least one node, currently requested $nodecount" >&2
exit 1
fi
node_hostname() {
local -r number="$1"
echo "node$((number + 1))"
}
instance_hostname() {
local -r number="$1"
echo "instance$((number + 1))"
}
node_ipaddr() {
local -r number="$1"
echo "$netprefix.$((first_node_ipaddr_octet + number))"
}
instance_ipaddr() {
local -r number="$1"
echo "$netprefix.$((first_inst_ipaddr_octet + number))"
}
setup_node() {
local -r number="$1"
local -r nodedir=$rootdir/$(node_hostname $number)
echo "Setting up node '$(node_hostname $number)' ..." >&2
if [[ ! -d $nodedir ]]; then
mkdir $nodedir
fi
mkdir -p \
$nodedir@SYSCONFDIR@/{default,ganeti} \
$nodedir@LOCALSTATEDIR@/lock\
$nodedir@LOCALSTATEDIR@/{lib,log,run}/ganeti
GANETI_HOSTNAME=$(node_hostname $number) \
GANETI_ROOTDIR=$nodedir \
$ensure_dirs
local -r daemon_args="-b $(node_ipaddr $number)"
cat > $nodedir/etc/default/ganeti <<EOF
# Default settings for virtual node $i
NODED_ARGS='--no-mlock $daemon_args'
MASTERD_ARGS=''
RAPI_ARGS='$daemon_args'
CONFD_ARGS='$daemon_args'
LUXID_ARGS=''
export GANETI_ROOTDIR='$nodedir'
export GANETI_HOSTNAME='$(node_hostname $number)'
EOF
cat > $nodedir/cmd <<EOF
#!/bin/bash
export GANETI_ROOTDIR='$nodedir'
export GANETI_HOSTNAME='$(node_hostname $number)'
bash -c "\$*"
EOF
chmod +x $nodedir/cmd
}
setup_all_nodes() {
for ((i=0; i < nodecount; ++i)); do
setup_node $i
done
}
setup_etc_hosts() {
echo "Configuring $etc_hosts_filename ..." >&2
(
set -e -u
local -r tmpfile=$(mktemp $etc_hosts_filename.vcluster.XXXXX)
trap "rm -f $tmpfile" EXIT
{
egrep -v "^$netprefix.[[:digit:]]+[[:space:]]" $etc_hosts_filename
echo "$netprefix.1 $cluster_name"
for ((i=0; i < nodecount; ++i)); do
echo "$(node_ipaddr $i) $(node_hostname $i)"
done
for ((i=0; i < instcount; ++i)); do
echo "$(instance_ipaddr $i) $(instance_hostname $i)"
done
} > $tmpfile && \
chmod 0644 $tmpfile && \
mv $tmpfile $etc_hosts_filename && \
trap - EXIT
)
}
setup_network_interfaces() {
echo 'Configuring network ...' >&2
for ((i=0; i < nodecount; ++i)); do
local ipaddr="$(node_ipaddr $i)/32"
ip addr del "$ipaddr" dev "$netdev" || :
ip addr add "$ipaddr" dev "$netdev"
done
route add -net $netprefix.0 netmask 255.255.255.0 dev $netdev || :
}
setup_scripts() {
echo 'Configuring helper scripts ...' >&2
for action in "${action_shortcuts[@]}"; do
{
echo '#!/bin/bash'
for ((i=0; i < nodecount; ++i)); do
local name=$(node_hostname $i)
if [[ $action = watcher ]]; then
echo "echo 'Running watcher for virtual node \"$name\" ..."
echo "$name/cmd ganeti-watcher \"\$@\""
else
echo "echo 'Action \"$action\" for virtual node \"$name\" ...'"
echo "$name/cmd $initscript $action \"\$@\""
fi
done
} > $rootdir/$action-all
chmod +x $rootdir/$action-all
done
}
show_info() {
cat <<EOF
Virtual cluster setup is complete.
Root directory: $rootdir
Cluster name: $cluster_name
EOF
echo 'Nodes:' $(for ((i=0; i < nodecount; ++i)); do node_hostname $i; done)
cat <<EOF
Initialize cluster:
cd $rootdir && node1/cmd gnt-cluster init --no-etc-hosts \\
--no-ssh-init --master-netdev=lo \\
--enabled-disk-templates=diskless --enabled-hypervisors=fake \\
--ipolicy-bounds-specs=min:disk-size=0,cpu-count=1,disk-count=0,memory-size=1,nic-count=0,spindle-use=0/max:disk-size=1048576,cpu-count=8,disk-count=16,memory-size=32768,nic-count=8,spindle-use=12 \\
$cluster_name
Add node:
cd $rootdir && node1/cmd gnt-node add --no-ssh-key-check node2
EOF
}
setup_all_nodes
if [[ -n "$etchosts" ]]; then
setup_etc_hosts
fi
if [[ -n "$networking" ]]; then
setup_network_interfaces
fi
setup_scripts
show_info
exit 0
# vim: set sw=2 sts=2 et :