| #!/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 : |