#!/bin/bash

# Lazy zone creator with resource set for Lysator.
#
# Peter Bortas 2008, originally created for Lysator
#
#TODO: Copy ssh host keys from storage
#NOTE: Assumes the existence of /root as on modern OpenSolaris

#default limits
MAX_LWPS=750000
MAX_CPUS=`psrinfo -v|grep "Status of .*processor" | wc -l | sed 's/ //g'`
MAX_MEM=`prtconf -v | grep "Memory size" | awk '{print $3}'`
#FIXME: Eliminate the sed step...
MAX_SWAP=`swap -lk | grep K | awk '{print $4}'|sed 's/K//'|awk '{swap += $1} END {print swap/1024}'`
CPU_SHARES=10

ZONEDIR=/zones


parse_args() {
 while [ ! c"$1" = "c" ] ; do
  case "$1" in
  '-h'|'--help')
  	sed -e "s/\\.B/`tput 'bold' 2>/dev/null`/g" -e "s/B\\./`tput 'sgr0' 2>/dev/null`/g" << EOF
.Bzone creationB.

   .BArguments:B.

      .B--helpB.:                  This information.
      .B--versionB.:               Displays client version.

      .B-z, --zonenameB.:          Name of zone.
      .B-d, --descriptionB.:       Description of zone.
      .B--zonedirB.:               Zone directory, if not $ZONEDIR.
      .B--physicalB.:              Network interface, if other than default.
      .B--ipB.:                    IP, if the zone does not exist in DNS.  
      .B--delay-bootB.             If set to "enter" will wait for enter before
                                   booting the zone. If set to a number will 
                                   delay boot that many seconds.
                                     This gives you a chance to hook up the 
                                   console before booting the zone.

      Zone limits (can be changed later but will require reboot)
      .B--max-cpusB.:              Nr CPUs limit        (default: $MAX_CPUS)
      .B--max-lwpsB.:              OS threads limit     (default: $MAX_LWPS)
      .B--max-memB.:               Memory limit in MiB  (default: $MAX_MEM)
      .B--max-swapB.:              Swap limit in MiB    (default: $MAX_SWAP)
      .B--cpu-sharesB.:            CPU shares           (default: $CPU_SHARES)

EOF
    	tput 'rmso' 2>/dev/null
	exit 0
  #emacs sh-mode kludge: '
  ;;
  '-v'|'--version')
	echo '$Id: 1.2 zino Exp $'
	exit 0
  ;;
  '-z='*|'--zonename='*)
	ZONE="`echo $1|sed -e 's/.*=//'`"
  ;;
  '-z'|'--zonename')
	shift
	ZONE="$1"
  ;;
  '-d='*|'--description='*)
	DESCRIPTION="`echo $1|sed -e 's/.*=//'`"
  ;;
  '-d'|'--description')
	shift
	DESCRIPTION="$1"
  ;;
  '--zonedir='*)
	ZONEDIR="`echo $1|sed -e 's/.*=//'`"
  ;;
  '--zonedir')
	shift
	ZONEDIR="$1"
  ;;
  '--physical')
	shift
	PHYSICAL="$1"
  ;;
  '--ip')
	shift
	IP="$1"
  ;;
  '--delay-boot')
	shift
	DELAY="$1"
  ;;
  '--max-lwps='*)
	MAX_LWPS="`echo $1|sed -e 's/.*=//'`"
  ;;
  '--max-lwps')
	shift
	MAX_LWPS="$1"
  ;;
  '--max-cpus='*)
	MAX_CPUS="`echo $1|sed -e 's/.*=//'`"
  ;;
  '--max-cpus')
	shift
	MAX_CPUS="$1"
  ;;
  '--max-mem='*)
	MAX_MEM="`echo $1|sed -e 's/.*=//'`"
  ;;
  '--max-mem')
	shift
	MAX_MEM="$1"
  ;;
  '--max-swap='*)
	MAX_SWAP="`echo $1|sed -e 's/.*=//'`"
  ;;
  '--max-swap')
	shift
	MAX_SWAP="$1"
  ;;
  '--cpu-shares='*)
	CPU_SHARES="`echo $1|sed -e 's/.*=//'`"
  ;;
  '--cpu-shares')
	shift
	CPU_SHARES="$1"
  ;;
  *)
	echo "Unsupported argument: $1" >&2
	echo "try --help" >&2
	exit 1
  esac
  shift
 done
}

parse_args "$@"

if [ x"$ZONE" = x -o x"$DESCRIPTION" = x ]; then
    echo "FATAL: You must supply a zone name and description. Try --help" >&2
    exit 1
fi

if [ x"$IP" = x ]; then
    if host $ZONE >/dev/null 2>&1; then
	IP=`host $ZONE | head -1 | awk '{print $4}'`
    else
	echo "FATAL: $ZONE is not in DNS"
	exit 1
    fi
else
    echo "WARNING: Using manually set IP. Please use DNS whenever possible."
fi

export ZONE ZONEDIR DESCRIPTION IP

#Enable CPU pools
pooladm -e

pool_path="`mount|grep "$ZONEDIR on"|cut -f3 -d' '`"
if [ x"$pool_path" = x ]; then
    echo "FATAL: Unable to determine ZFS pool for $ZONEDIR" >&2
    exit 1
fi
zfs list | grep $pool_path/homes >/dev/null 2>&1 || 
    zfs create $pool_path/homes
zfs list | grep $pool_path/homes/$ZONE >/dev/null 2>&1 || 
    zfs create $pool_path/homes/$ZONE

if [ x"$PHYSICAL" = x ]; then
    interface=`netstat -nr -f af:inet|grep UG|awk '{print $6}'`
else
    interface="$PHYSICAL"
fi
if [ x"$interface" = x ]; then
    echo "FATAL: Unable to determine interface. Use --physical" 1>&2
    exit 1
fi
defaultgw=`netstat -nr -f af:inet|grep UG|awk '{print $2}'`
hexmask=`ifconfig ${interface}|grep netmask|awk '{print $4}'`
decmask=`printf "%d.%d.%d.%d\n" $(echo "$hexmask" | sed 's/../0x& /g')`
mask=`perl -e '$_=sprintf("%b", 0x'${hexmask}'); printf("%d", tr/1/X/)'`
password=`grep '^root:' /etc/shadow | awk -F: '{print $2}'`

#NOTE: 'create -F' might be called for in some instances.
echo " create
 set zonepath=$ZONEDIR/$ZONE
 set autoboot=true
 set scheduling-class=FSS
 add net
    set address=${IP}/${mask}
    set physical=${interface}
    end
 add capped-cpu
    set ncpus=${MAX_CPUS}.0
    end
 add capped-memory
    set physical=${MAX_MEM}m
    set swap=${MAX_SWAP}m
    end
 add rctl
    set name=zone.max-lwps
    add value (priv=privileged,limit=${MAX_LWPS},action=deny)
    end
 add rctl
    set name=zone.cpu-shares
    add value (priv=privileged,limit=${CPU_SHARES},action=none)
    end
 add attr
    set name=comment
    set type=string
    set value="'"'"zone $ZONE, $DESCRIPTION"'"'"
    end
 add fs
    set dir=/home
    set special=${ZONEDIR}/homes/${ZONE}
    set type=lofs
    add options rw
    end
 verify
 commit
" > /root/zonesetup.$$ || exit $?

domain=`domainname`
if [ x"$domainname" = x ]; then
    domainname=`grep \^domain /etc/resolv.conf|awk '{print $2}'`
fi
if [ x"$domainname" = x ]; then
    echo "FATAL: Unable to determine domain name." >&2
    exit 1
fi
search=`grep \^search /etc/resolv.conf|cut -f 2- -d' '`
if [ x"$search" = x ]; then
    search=$domainname
fi
nameserver=`awk '/^nameserver/ {ns=ns ", " $2} END {print ns}' < /etc/resolv.conf|sed 's/^, //'`

zonecfg -z $ZONE < /root/zonesetup.$$ || exit $?
zoneadm -z $ZONE verify || exit $?
zoneadm -z $ZONE install || exit $?
# OpenSolaris requires a "ready" to mount filesystems
zoneadm -z $ZONE ready || exit $?

#if [ ! -d $ZONEDIR/$ZONE/root/etc ]; then
#    mkdir $ZONEDIR/$ZONE/root/etc
#fi
#Get rid of most of the questions
#FIXME: removed "protocol_ipv6=yes" from network_interface
#FIXME: removed "keyboard=Swedish"
echo "system_locale=C
terminal=vt100
network_interface=primary {
    hostname=${ZONE}
    ip_address=${IP}
    netmask=${decmask}
    default_route=${defaultgw}
}
timeserver=localhost
security_policy=NONE
name_service=DNS {
    domain_name=${domainname}
    name_server=${nameserver}
    search=${search}
}
timezone=Europe/Stockholm
nfs4_domain=${domainname}
root_password=${password}
" > $ZONEDIR/$ZONE/root/etc/sysidcfg || exit $?

echo "initial config $ZONEDIR/$ZONE/root/etc/sysidcfg:"
cat $ZONEDIR/$ZONE/root/etc/sysidcfg
echo

#Get rid of NFSv4 question (This should not be needed since v10 8/07)
#echo NFS4_DOMAIN=lysator.liu.se > $ZONEDIR/$ZONE/root/etc/.NFS4inst_state.domain

if [ x"$DELAY" != x ]; then
    if [ x"$DELAY" = xenter ]; then
        echo "Press enter to boot the new zone. Start the console (zlogin -C) first."
	read dummy
    else
	echo "Will boot the new zone in ${DELAY}s. Start the console (zlogin -C) now."
	sleep $DELAY || (echo "Illegal delay value ${DELAY}. 60s forced sleep."; sleep 60)
    fi
fi

zoneadm -z $ZONE boot
zoneadm list -cv

rm /root/zonesetup.$$

echo "The root password is the same as for the global zone."
