diff options
| author | Martin Schwenke <martin@meltin.net> | 2008-08-15 15:34:22 +1000 |
|---|---|---|
| committer | Martin Schwenke <martin@meltin.net> | 2008-08-15 15:34:22 +1000 |
| commit | f334e8aa5dc18af9ce7d94a2e35f68fe583fc0ca (patch) | |
| tree | 821bd15dc4d521b7fa82cfe26500333ac9ed6173 | |
| parent | 9cadd75c0a109c3c283bd2144f0305c11f00c5e2 (diff) | |
Configuration generalised so that all options can be set via
environment variables, in configuration files or on the command-line.
Changed format of config.default to use defconf rather than
assignment, and include documentation for usage message. All
configuration variables now have an equivalent command-line option.
For example, $NBD_DEVICE can be set on the command-line using the
--nbd-device option. Options specified on the command-line override
options set in any other way. New configuration option $WITH_TSM_NODE
(defaults to 1). Removed -n option - this is implicitly replaced by
--with-tsm-node=0. The -c option can now be specified multiple times
and interspersed with setting individual options - order is important.
New option --dump displays the current configuration settings and then
exits. It can be inserted anywhere on the command-line and takes
immediate effect, so it should be at the end of the command-line to
display all specified settings. Configuration settings can also be
set in autocluster's environment, where they override the settings in
config.default (but not those specified on the command-line, including
via -c). Error trapping moved to after usage processing, so an
invalid command-line option causes the desired usage message to
appear.
Signed-off-by: Martin Schwenke <martin@meltin.net>
| -rwxr-xr-x | autocluster | 83 | ||||
| -rw-r--r-- | config.default | 157 | ||||
| -rw-r--r-- | functions | 160 |
3 files changed, 319 insertions, 81 deletions
diff --git a/autocluster b/autocluster index b2e228b..6adccfc 100755 --- a/autocluster +++ b/autocluster @@ -24,15 +24,6 @@ else installdir="`dirname \"$autocluster\"`" fi -config="config" - -# catch errors -set -e -set -E -trap 'es=$?; - echo ERROR: failed in function \"${FUNCNAME}\" at line ${LINENO} of ${BASH_SOURCE[0]} with code $es; - exit $es' ERR - #################### # show program usage usage () @@ -41,9 +32,15 @@ usage () Usage: autocluster [OPTION] ... <COMMAND> options: -c <file> specify config file (default is "config") - --firstip N override FIRSTIP from config - -n don't create a TSM node -x enable script debugging + --dump dump config settings and exit + + configuration options: +EOF + + usage_config_options + + cat >&2 <<EOF commands: create base @@ -182,13 +179,13 @@ create_cluster() { create_node "$CLUSTER" $i done - if [ $withtsmnode -eq 1 ]; then + if [ $WITH_TSM_NODE -eq 1 ]; then echo "Creating TSM server node" create_tsm "$CLUSTER" fi echo "# autocluster $CLUSTER" > hosts.$CLUSTER - [ $withtsmnode -eq 1 ] && { + [ $WITH_TSM_NODE -eq 1 ] && { echo "$IPBASE.0.$FIRSTIP ${CLUSTER}tsm.$LOWDOMAIN ${CLUSTER}tsm" >> hosts.$CLUSTER } for i in `seq 1 $NUMNODES`; do @@ -327,35 +324,65 @@ test_proxy() { ###################################################################### +. "$installdir/functions" +. "$installdir/config.default" ############################ # parse command line options -temp=$(getopt -n "$prog" -o "c:xhn" -l help,firstip: -- "$@") - +long_opts=$(getopt_config_options) +getopt_output=$(getopt -n autocluster -o "c:xh" -l help,dump -l "$long_opts" -- "$@") [ $? != 0 ] && usage -eval set -- "$temp" +use_default_config=true -withtsmnode=1 +# We 2 passes of the options. The first time we just handle usageor the first pass +eval set -- "$getopt_output" +while true ; do + case "$1" in + -c) shift 2 ; use_default_config=false ;; + --) shift ; break ;; + --dump|-x) shift ;; + -h|--help) usage ;; # Usage should be shown here for real defaults. + --*) shift 2 ;; # Assume other long opts are valid and take an arg. + *) usage ;; # shouldn't happen, so this is reasonable. + esac +done + +config="./config" +$use_default_config && [ -r "$config" ] && . "$config" + +eval set -- "$getopt_output" while true ; do case "$1" in - -c) config="$2" ; shift; shift ;; - --firstip) firstip="$2"; shift; shift; ;; - -n) withtsmnode=0; shift; ;; + -c) . "`dirname $2`/$2" ; shift 2 ; conf_done=true ;; -x) set -x; shift ;; + --dump) dump_config ;; --) shift ; break ;; - -h|--help|*) usage ;; # Shouldn't happen, so this is reasonable. + -h|--help) usage ;; # Redundant. + --*) + # Putting --opt1|opt2|... into a variable and having case + # match against it as a pattern doesn't work. The | is + # part of shell syntax, so we need to do this. Look away + # now to stop your eyes from bleeding! :-) + x=",${long_opts}" # Now each option is surrounded by , and : + if [ "$x" != "${x#*,${1#--}:}" ] ; then + # Our option, $1, surrounded by , and : was in $x, so is legal. + setconf_longopt "$1" "$2"; shift 2 + else + usage + fi + ;; + *) usage ;; # shouldn't happen, so this is reasonable. esac done -. "$installdir/config.default" -. "`dirname $config`/$config" -. "$installdir/functions" - -[ -n "$firstip" ] && { - FIRSTIP="$firstip" -} +# catch errors +set -e +set -E +trap 'es=$?; + echo ERROR: failed in function \"${FUNCNAME}\" at line ${LINENO} of ${BASH_SOURCE[0]} with code $es; + exit $es' ERR LOWDOMAIN=`echo $DOMAIN | tr A-Z a-z` diff --git a/config.default b/config.default index 06d7118..105388b 100644 --- a/config.default +++ b/config.default @@ -2,83 +2,107 @@ # please override with your own options in your own config file # where virtual machines are stored on this host -VIRTBASE=/virtual +defconf VIRTBASE /virtual \ + "<path>" "virtual machine directory for this host" # where to find virsh -VIRSH="virsh -c qemu:///system" +defconf VIRSH "virsh -c qemu:///system" \ + "<cmd>" "how to invoke virsh" # the name of the base RHEL install image that the nodes will be based on # a kvm image called $VIRTBASE/$BASENAME.img will be created # that will form the basis file for the images for each of the nodes -BASENAME="SoFS-1.5-base" +defconf BASENAME "SoFS-1.5-base" \ + "<file>" "filename for cluster base image" # the install server where we will get SoFS packages from -INSTALL_SERVER="http://9.155.61.11/mediasets" +defconf INSTALL_SERVER "http://9.155.61.11/mediasets" \ + "<url>" "URL of install server" # what timezone to put the nodes in # leave this empty to base the timezone on the zone that # this host is in -TIMEZONE= +defconf TIMEZONE "" \ + "<tz>" "timezone for each node" # what keyboard type to setup -KEYBOARD="us" +defconf KEYBOARD "us" \ + "<kbd>" "keyboard layout for each node" # the base ISO to install from -ISO="/virtual/ISO/RHEL5.2-Server-20080430.0-x86_64-DVD.iso" +defconf ISO "/virtual/ISO/RHEL5.2-Server-20080430.0-x86_64-DVD.iso" \ + "<file>" "location of ISO image for base image creation" # which template kickstart file to use. There are separate templates # for each version of SoFS -KICKSTART="$installdir/templates/kickstart-1.5.cfg" +defconf KICKSTART "$installdir/templates/kickstart-1.5.cfg" \ + "<file>" "choice of kickstart file" # the yum repositories to use. Choose the one appropriate for the # version of SoFS you are installing -YUM_TEMPLATE="$installdir/templates/SoFS-1.5.repo" - +defconf YUM_TEMPLATE "$installdir/templates/SoFS-1.5.repo" \ + "<file>" "location of template for yum repositories" # any extra packages to install. List one on each line. To force a package # not to be installed, list it with a leading - -EXTRA_PACKAGES=' +defconf EXTRA_PACKAGES ' emacs -' +' \ + "<list>" "extra packages for kickstart to install" # RHEL install key -INSTALLKEY="--skip" +defconf INSTALLKEY "--skip" \ + "<key>" "RHEL install key" # the kvm binary to use - should be a very recent version # I am using a git snapshot from http://kvm.qumranet.com/kvmwiki/Code -KVM="/usr/local/bin/qemu-system-x86_64" +defconf KVM "/usr/local/bin/qemu-system-x86_64" \ + "<file>" "location of KVM executable" # memory for each node -MEM=250000 +defconf MEM 250000 \ + "<n>" "memory allocated for each node" # memory for the node that will run the SoFS GUI - used if it is # greater than $MEM -GUIMEM=700000 +defconf GUIMEM 700000 \ + "<n>" "memory allocated for the management node" # a directory on the host which will be mounted via NFS onto the # nodes as /root/SOFS, giving a nice common area independent of GPFS # This is useful for compiles, RPMs, devel scripts etc # you need to add this to your /etc/exports and run exportfs -av yourself # on the host -NFSSHARE="10.0.0.1:/home/SOFS" +defconf NFSSHARE "10.0.0.1:/home/SOFS" \ + "<mnt>" "NFS share to mount on each node" # windows domain name the nodes will be part of -DOMAIN="VSOFS1.COM" +defconf DOMAIN "VSOFS1.COM" \ + "<dom>" "Windows(TM) domain name for each node" # windows workgroup name the nodes will be part of -WORKGROUP="VSOFS1" +defconf WORKGROUP "VSOFS1" \ + "<grp>" "Windows(TM) workgroup for node" # DNS name server. Usually set this to the # kvm host, then setup DNS on the kvm host to direct # queries for the windows domain name to the w2003 server -NAMESERVER="10.0.0.1" +defconf NAMESERVER "10.0.0.1" \ + "<ip>" "DNS server for each node" # any extra domains to add to the search list -DNSSEARCH="$DOMAIN" +defconf DNSSEARCH "$DOMAIN" \ + "<dom>" "extra domains for DNS search list" # set the first two octets of the IPs we will use # the 3rd and 4th octets are controlled by the node setup scripts -IPBASE="10.0" +defconf IPBASE "10.0" \ + "<n>.<n>" "first 2 octets of IP for each node" + +# should we create a TSM node? If this is not 1 then no TSM node is +# created and all other TSM options are ignored. +defconf WITH_TSM_NODE 1 \ + "<0|1>" "1 if a TSM node should be created" # the nodes will get IPs starting at this number # the TSM server will get $FIRSTIP, then the first node will get @@ -89,7 +113,8 @@ IPBASE="10.0" # 1st node 10.0.0.21 # 2nd node 10.0.0.22 # etc etc -FIRSTIP="20" +defconf FIRSTIP "20" \ + "<n>" "final octet for the 1st IP of the cluster" # a caching web proxy that can get to the install server from the # nodes. If you don't have one on the local network then look in @@ -98,88 +123,116 @@ FIRSTIP="20" # several G of cache You can choose to have no web proxy at all, in # which case set it to the empty string, and hope you have a fast # network connection to the install server -WEBPROXY="http://10.0.0.1:3128/" +defconf WEBPROXY "http://10.0.0.1:3128/" \ + "<url>" "URL of a caching web proxy" # IP gateway (the IP of the kvm host for the clients) -GATEWAY="10.0.0.1" +defconf GATEWAY "10.0.0.1" \ + "<ip>" "IP gateway for cluster hosts, usually KVM host" # how many nodes to build # this doesn't include the TSM server -NUMNODES=4 +defconf NUMNODES 4 \ + "<n>" "number of nodes to build, not including TSM server" # how much disk space to use on each node # note that it will only use what is actually occupied, # so start this larger than you think you'll need -DISKSIZE="20G" +defconf DISKSIZE "20G" \ + "<n>G" "maximum disk size for each node" # size of root partition in megabytes -ROOTSIZE=15000 +defconf ROOTSIZE 15000 \ + "<n>" "size of root partition in MB" # size of swap partition in megabytes -SWAPSIZE=2000 +defconf SWAPSIZE 2000 \ + "<n>" "size of swap partition in MB" # the size of the 3 GPFS shared disks -SHAREDDISKSIZE="10G" +defconf SHAREDDISKSIZE "10G" \ + "<n>G" "size of the 3 GPFS shared disks" # the size of the TSM storage disk -TSMDISKSIZE="50G" +defconf TSMDISKSIZE "50G" \ + "<n>G" "size of the TSM storage disk" # what network adapter to use -NICMODEL="e1000" +defconf NICMODEL "e1000" \ + "<module>" "choice of KVM network adapter" # where we will log serial consoles to -KVMLOG="/var/log/kvm" +defconf KVMLOG "/var/log/kvm" \ + "<dir>" "directory for serial logs" # initial root password -ROOTPASSWORD="password" +defconf ROOTPASSWORD "password" \ + "<string>" "initial root password for each node" # install language - make it the same as the installers by default -LANGUAGE="${LANG:-en_US.UTF-8}" +defconf LANGUAGE "${LANG:-en_US.UTF-8}" \ + "<locale>" "locale for installer to use" # name of the system in CIFS protocol -CIFSNAME="Samba01" +defconf CIFSNAME "Samba01" \ + "<name>" "name of the system in CIFS protocol" # the name that the nodes will use to talk to the TSM server -TSMNAME="SOFS01" +defconf TSMNAME "SOFS01" \ + "<name>" "name used by nodes to talk to TSM server" # how big should the TSM database be -TSM_DB_SIZE=100 +defconf TSM_DB_SIZE 100 \ + "<n>" "size of TSM database" # how big should the TSM space management pool be -TSM_SPACE_MGMT_SIZE=1024 +defconf TSM_SPACE_MGMT_SIZE 1024 \ + "<n>" "size of TSM space management pool" # how big should the TSM backup pool be -TSM_BACKUP_POOL_SIZE=100 +defconf TSM_BACKUP_POOL_SIZE 100 \ + "<n>" "size of TSM backup pool" # how big should the TSM archive pool be -TSM_ACRHIVE_POOL_SIZE=100 +defconf TSM_ACRHIVE_POOL_SIZE 100 \ + "<n>" "size of TSM archive pool" # the min size of the java heap for the SoFS GUI -JAVA_MIN_SIZE="200M" +defconf JAVA_MIN_SIZE "200M" \ + "<n>M" "minimum size of Java heap for SoFS GUI" # the max size of the java heap for the SoFS GUI -JAVA_MAX_SIZE="400M" +defconf JAVA_MAX_SIZE "400M" \ + "<n>M" "maximum size of Java heap for SoFS GUI" # how many virtual CPUs per node? -NUMCPUS=2 +defconf NUMCPUS 2 \ + "<n>" "number of virtual CPUs per node" # libvirt template to use for nodes -NODE_TEMPLATE="$installdir/templates/node.xml" +defconf NODE_TEMPLATE "$installdir/templates/node.xml" \ + "<file>" "libvirt template for nodes" # libvirt template to use for TSM server -TSM_TEMPLATE="$installdir/templates/tsmserver.xml" +defconf TSM_TEMPLATE "$installdir/templates/tsmserver.xml" \ + "<file>" "libvirt template for TSM server" # libvirt template to use for initial install -INSTALL_TEMPLATE="$installdir/templates/install.xml" +defconf INSTALL_TEMPLATE "$installdir/templates/install.xml" \ + "<file>" "libvirt template for initial install" # libvirt template to use for boot_base.sh -BOOT_TEMPLATE="$installdir/templates/bootbase.xml" +defconf BOOT_TEMPLATE "$installdir/templates/bootbase.xml" \ + "<file>" "libvirt template for \"bootbase\" command" # where to get the base templates from -BASE_TEMPLATES="$installdir/base" +defconf BASE_TEMPLATES "$installdir/base" \ + "<dir>" "directory containing base templates" # nbd device to use -NBD_DEVICE="/dev/nbd0" +defconf NBD_DEVICE "/dev/nbd0" \ + "<dev>" "NBD device node to use" -# the format to use the for qemu base images -BASE_FORMAT="qcow2" +# the format to use for qemu base images +defconf BASE_FORMAT "qcow2" \ + "<fmt>" "format to use for the qemu base images" @@ -1,4 +1,4 @@ -# common functions for autocluster +# common functions for autocluster (-*- shell-script -*-) # create a MAC address based on a hash of the cluster name # plus the adapter and node number @@ -198,3 +198,161 @@ check_command() { fi } +# Set a variable if it isn't already set. This allows environment +# variables to override default config settings. +defconf() { + local v="$1" + local e="$2" + + [ "${!v+x}" ] || eval "$v=\"$e\"" +} + +# Print the list of config variables defined in config.default. +get_config_options() {( # sub-shell for local declaration of defconf() + local options= + defconf() { options="$options $1" ; } + . "$installdir/config.default" + echo $options +)} + +# Produce a list of long options, suitable for use with getopt, that +# represent the config variables defined in config.default. +getopt_config_options() { + local x=$(get_config_options | tr 'A-Z_' 'a-z-') + echo "${x// /:,}:" +} + +# Unconditionally set the config variable associated with the given +# long option. +setconf_longopt() { + local longopt="$1" + local e="$2" + + local v=$(echo "${longopt#--}" | tr 'a-z-' 'A-Z_') + # unset so defconf will set it + eval "unset $v" + defconf "$v" "$e" +} + +# Dump all of the current config variables. +dump_config() { + local o + for o in $(get_config_options) ; do + echo "${o}=\"${!o}\"" + done + exit 0 +} + +# Print text assuming it starts after other text in $startcol and +# needs to wrap before $fillcol. Subsequent lines start at $startcol. +# Long "words" will extend past $fillcol. +fill_text() { + local startcol="$1" + local fillcol="$2" + local text="$3" + + local width=$(($fillcol - $startcol)) + [ $width -lt 0 ] && width=$((78 - $startcol)) + + local out="" + + local padding=$(printf "\n%${startcol}s" " ") + + while [ -n "$text" ] ; do + local orig="$text" + + # If we already have output then arrange padding on the next line. + [ -n "$out" ] && out="${out}${padding}" + + # Break the text at $width. + out="${out}${text:0:${width}}" + text="${text:${width}}" + + # If we have left over text then the line break may be ugly, + # so let's check and try to break it on a space. + if [ -n "$text" ] ; then + if [ "${text:0:1}" != " " -a "${text: -1:1}" != " " ] ; then + # We didn't break on a space. Arrange for the + # beginning of the broken "word" to appear on the next + # line but not if it will make us loop infinitely. + if [ "${orig}" != "${out##* }${text}" ] ; then + text="${out##* }${text}" + out="${out% *}" + else + # Hmmm, doing that would make us loop, so add the + # rest of the word from the remainder of the text + # to this line and let it extend past $fillcol. + out="${out}${text%% *}" + if [ "${text# *}" != "$text" ] ; then + # Remember the text after the next space for next time. + text="${text# *}" + else + # No text after next space. + text="" + fi + fi + else + # We broke on a space. If it will be at the beginning + # of the next line then remove it. + text="${text# }" + fi + fi + done + + echo "$out" +} + +# Display usage text, trying these approaches in order. +# 1. See if it all fits on one line before $fillcol. +# 2. See if splitting before the default value and indenting it +# to $startcol means that nothing passes $fillcol. +# 3. Treat the message and default value as a string and just us fill_text() +# to format it. +usage_display_text() { + local startcol="$1" + local fillcol="$2" + local desc="$3" + local default="$4" + + local width=$(($fillcol - $startcol)) + + # To avoid clutter, only quote values that need it. + #local q= + #[ -z "$default" -o "$(echo $default)" = "${default// /}" ] || q="\"" + default="(default \"$default\")" + + if [ $((${#desc} + 1 + ${#default})) -le $width ] ; then + echo "${desc} ${default}" + else + local padding=$(printf "%${startcol}s" " ") + + if [ ${#desc} -lt $width -a ${#default} -lt $width ] ; then + echo "$desc" + echo "${padding}${default}" + else + fill_text $startcol $fillcol "${desc} ${default}" + fi + fi +} + +# Display usage information for long config options. +usage_config_options(){( # sub-shell for local declaration of defconf() + local def_fillcol=78 + local fillcol=$def_fillcol + local rows=$(stty size | sed -e 's@.* @@') + [ -n "$rows" ] && fillcol=$(($rows - 2)) + + local startcol=33 + + # We need to have at least one column... negative is also a problem. + [ $fillcol -le $startcol ] && fillcol=$def_fillcol + + defconf() { + local local longopt=$(echo "$1" | tr 'A-Z_' 'a-z-') + + printf " --%-25s " "${longopt}=${3}" >&2 + + usage_display_text $startcol $fillcol "$4" "$2" >&2 + } + . "$installdir/config.default" +)} |
