#!/bin/sh
#
# Copyright (C) 2007-2009 Canonical, Ltd.
# Author: Jamie Strandboge <jamie@canonical.com>
#         Kees Cook <kees@canonical.com>
# License: GPLv3
#
# TODO:
#  proper kqemu/qemu support
#  use first login script for vm-builder

set -e

ustconf="$HOME/.uqt-vm-tools.conf"
if [ -s "$ustconf" ]; then
    . "$ustconf"
else
    echo "Could not find '$ustconf'"
    exit 1
fi

. $UQT_VM_TOOLS/libvm.sh
abort_if_root

which vmbuilder >/dev/null || {
    echo "Could not find vmbuilder. Aborting"
    echo "Perhaps try: apt-get install python-vm-builder (should be 0.9 or newer)"
    exit 1
}

help() {
    echo "Usage: vm-new [-f] [-b BRIDGE] release arch hostname1 hostname2"
    echo "  release     dapper|feisty|gutsy|hardy|..."
    echo "  arch        i386|amd64|..."
    echo "  prefix      prefix for the machine (eg, 'sec' or 'clean')"
    echo ""
    echo "Eg, 'vm-new jaunty i386 clean' will create a new vm called 'clean-jaunty-i386'"
}

use_virtnet() {
    rel="$1"

    # hardy is the first release to support virto networking
    #virtnet_rel="hardy"

    # due to bug #331128, only intrepid and higher is supported
    virtnet_rel="intrepid"

    tmprel=`echo "${rel}\n${virtnet_rel}" | sort | tail -1`
    if [ "$rel" = "$tmprel" ]; then
        return 0
    fi
    return 1
}

check_release() {
    rel="$1"
    # this is highly python-vm-builder dependant :(
    if [ ! -z "$rel" ] && [ -s "/usr/share/pyshared/VMBuilder/plugins/ubuntu/${rel}.py" ]; then
        return 0
    fi
    return 1
}

force=
bridge=
while getopts "hfb:" opt
do
    case "$opt" in
        f) force=yes;;
        b) bridge="$OPTARG";;
        ?|h) help
           exit 1
           ;;
    esac
done
shift $(($OPTIND - 1))

if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
    help
    exit 1
fi

release="$1"
if ! check_release $release ; then
    echo "Unsupported release '$release'"
    exit 1
fi
shift

flavor="generic"
arch="$1"
if [ "$arch" = "i386" ]; then
    if [ "$release" = "dapper" ] || [ "$release" = "feisty" ] || [ "$release" = "gutsy" ] || [ "$release" = "hardy" ]; then
        # use 386 flavor for all but edgy and intrepid (and after)
        flavor="386"
    fi
elif [ "$arch" = "amd64" ]; then
    if [ "$release" = "dapper" ] || [ "$release" = "edgy" ] || [ "$release" = "feisty" ]; then
        flavor="amd64-$flavor"
    fi
fi
shift

if [ ! -z "$vm_flavor" ]; then
    flavor="$vm_flavor"
    if [ "$flavor" = "generic" ]; then
        if [ "$release" = "dapper" ] || [ "$release" = "edgy" ] || [ "$release" = "feisty" ]; then
            if [ "$arch" = "i386" ]; then
                flavor="686"
            elif [ "$arch" = "amd64" ]; then
                flavor="amd64-generic"
            fi
        fi
    fi
fi

hname=
error=
created=
for hname in "$@"; do
    echo ""
    if [ ! -d "${vm_path}" ]; then
        echo "${vm_path} not a directory. Skipping '$hname'" >&2
        continue
    fi

    vmname="${hname}-${release}-${arch}"
    if virsh --connect ${vm_connect} dumpxml $vmname >/dev/null 2>&1 ; then
        echo "Virtual machine '$vmname' already exists. Skipping" >&2
        continue
    fi

    if [ -e "${vm_path}/${vmname}" ]; then
        echo "'${vm_path}/${vmname}' already exists. Skipping" >&2
        continue
    fi

    if [ -z "$vm_locale" ]; then
        vm_locale="en_US.UTF-8"
    fi

    if echo "$vm_locale" | egrep -q 'UTF-8'; then
        vm_locale_iso=`echo "$vm_locale" | cut -d '.' -f 1`
    else
        vm_locale_iso=""
    fi

    echo "Creating VM(s) with:"
    echo "Memory: ${vm_memory} (Required: desktop >= 384, server/alternate >= 256)"
    echo "Root size: ${vm_root_size}"
    echo "Swap size: ${vm_swap_size}"
    echo "Kernel flavor: ${flavor}"
    echo "Release: ${release}"

    if [ "$force" != "yes" ]; then
        echo -n "Continue (y/N)? "
        read ans
        if [ "$ans" != "y" ] && [ "$ans" != "Y" ]; then
            echo "Skipping '$hname'"
            continue
        fi
    fi

    post_script="/postinstall.sh"
    xsession_script="/etc/X11/Xsession.d/90vm-tools"
    gui_first_login_script=".vm-tools-gui-first-login"
    script=`mktemp -t vm-new-setup-XXXXXX`
    cat > $script << EOM
#!/bin/sh -e
chroot \$1 mkdir /home/$USER/.ssh 2>/dev/null || true
chroot \$1 cp /root/.ssh/authorized_keys /home/$USER/.ssh || true
chroot \$1 chown -R $USER:$USER /home/$USER/.ssh

# create $xsession_script script, which simply executes $gui_first_login_script
# $gui_first_login_script will do gui setup and remove itself
chroot \$1 sh -c 'mkdir -p $(dirname $xsession_script)'
chroot \$1 sh -c 'echo "if [ -r \"\\\$HOME/$gui_first_login_script\" ]; then . \"\\\$HOME/$gui_first_login_script\"; fi" > $xsession_script'
chroot \$1 sh -c 'echo "# This script is sourced by $xsession_script" > /home/$USER/$gui_first_login_script'
chroot \$1 sh -c 'echo "if [ -x "/usr/bin/gconftool-2" ]; then" >> /home/$USER/$gui_first_login_script'
chroot \$1 sh -c 'echo "    gconftool-2 --set --type=string /apps/gnome-screensaver/mode blank-only" >> /home/$USER/$gui_first_login_script'
chroot \$1 sh -c 'echo "    rm -f /home/$USER/$gui_first_login_script" >> /home/$USER/$gui_first_login_script'
chroot \$1 sh -c 'echo "fi" >> /home/$USER/$gui_first_login_script'
chroot \$1 chmod 644 /home/$USER/$gui_first_login_script

chroot \$1 locale-gen en_US en_US.UTF-8
if [ "$vm_locale_iso" != "en_US.UTF-8" ]; then
    chroot \$1 locale-gen $vm_locale $vm_locale_iso
fi
chroot \$1 dpkg-reconfigure locales

chroot \$1 sh -c 'echo "#!/bin/sh -e" > $post_script'
chroot \$1 sh -c 'echo "" >> $post_script'
chroot \$1 sh -c 'echo "apt-get -f install" >> $post_script'
chroot \$1 sh -c 'echo "apt-get update" >> $post_script'
chroot \$1 sh -c 'echo "apt-get -y dist-upgrade" >> $post_script'
chroot \$1 sh -c 'echo "apt-get -y install acpid openssh-server ubuntu-standard avahi-daemon $vm_extra_packages" >> $post_script'

chroot \$1 sh -c 'echo "apt-get clean" >> $post_script'
chroot \$1 sh -c 'echo "rm -f $post_script.run /etc/cron.daily/find.notslocate /etc/cron.daily/slocate /etc/cron.daily/mlocate /etc/network/if-up.d/postinstall 2>/dev/null" >> $post_script'
EOM

    # cirrus defaults to 800x600 on Jaunty unless update the Monitor section
    # Using a shell script to write a shell script via a shell gets ridiculous
    # when trying to get the quoting right. There *has* to be a better way to
    # do this...
    if [ "$release" = "jaunty" ] || [ "$release" = "karmic" ]; then
        cat >> $script << EOM
chroot \$1 sh -c 'echo "if [ -x /usr/bin/Xorg ]; then" >> $post_script'
chroot \$1 sh -c 'echo "    dpkg-reconfigure -phigh xserver-xorg" >> $post_script'
EOM
        # karmic and higher doesn't need this
        if [ "$release" = "jaunty" ]; then
            cat >> $script << EOM
chroot \$1 sh -c 'echo -n "    sed -i " >> $post_script'
chroot \$1 sh -c "echo -n \\\'s/ >> $post_script"
chroot \$1 sh -c "echo -n '\\(.*\\)Identifier\\(.*\\)Configured Monitor\\(.*\\)/\\' >> $post_script"
chroot \$1 sh -c "echo -n '1Identifier     \' >> $post_script"
chroot \$1 sh -c "echo -n '2Configured Monitor\' >> $post_script"
chroot \$1 sh -c "echo -n '3\\' >> $post_script"
chroot \$1 sh -c "echo -n 'n        HorizSync       30-50\\' >> $post_script"
chroot \$1 sh -c "echo -n 'n        VertRefresh     50-75/g' >> $post_script"
chroot \$1 sh -c "echo -n \\\' /etc/X11/xorg.conf >> $post_script"
chroot \$1 sh -c 'echo "" >> $post_script'
EOM
        fi
        cat >> $script << EOM
chroot \$1 sh -c 'echo "fi" >> $post_script'
EOM
    fi

        cat >> $script << EOM
chroot \$1 sh -c 'echo "dpkg --get-selections > /var/log/vm-new.packages" >> $post_script'
chroot \$1 sh -c 'echo "echo Post Installation Tasks Finished" >> $post_script'

chroot \$1 chmod 755 $post_script
chroot \$1 sh -c 'echo "#!/bin/sh -e" > /etc/network/if-up.d/postinstall'
chroot \$1 sh -c 'echo "if [ \"\\\$IFACE\" != "eth0" ] || [ \"\\\$MODE\" != "start" ]; then exit 0; fi" >> /etc/network/if-up.d/postinstall'
chroot \$1 sh -c 'echo "if [ -x $post_script ] ; then" >> /etc/network/if-up.d/postinstall'
chroot \$1 sh -c 'echo "    mv $post_script $post_script.run" >> /etc/network/if-up.d/postinstall'
chroot \$1 sh -c 'echo "    touch /var/log/vm-new.log" >> /etc/network/if-up.d/postinstall'
chroot \$1 sh -c 'echo "    chmod 600 /var/log/vm-new.log" >> /etc/network/if-up.d/postinstall'
chroot \$1 sh -c 'echo "    export HOME=/root" >> /etc/network/if-up.d/postinstall'
chroot \$1 sh -c 'echo "    $post_script.run >> /var/log/vm-new.log 2>&1 &" >> /etc/network/if-up.d/postinstall'
chroot \$1 sh -c 'echo "fi" >> /etc/network/if-up.d/postinstall'
chroot \$1 chmod 755 /etc/network/if-up.d/postinstall
EOM

    if [ "$release" = "dapper" ]; then
        cat >> $script << EOM
chroot \$1 sed -i "s/#send host-name \"andare.fugue.com\";/send host-name \"$vmname\";/" /etc/dhcp3/dhclient.conf
chroot \$1 pwconv
chroot \$1 grpconv
chroot \$1 sh -c 'echo "LANG=\"$vm_locale\"" >> /etc/environment'
EOM
    else
        cat >> $script << EOM
chroot \$1 sh -c 'echo "LANG=\"$vm_locale\"" > /etc/default/locale'
EOM
    fi

    if [ "$vm_setkeyboard" = "true" ]; then
        if [ "$release" = "dapper" ]; then
            cat >> $script << EOM
chroot \$1 sh -c 'echo ubiquity debian-installer/keymap string $vm_keymap | debconf-set-selections'
chroot \$1 sh -c '/usr/sbin/install-keymap $vm_keymap'
EOM
        else
            cat >> $script << EOM
chroot \$1 sed -i "s/XKBMODEL=\".*\"/XKBMODEL=\"$vm_xkbmodel\"/" /etc/default/console-setup
chroot \$1 sed -i "s/XKBLAYOUT=\".*\"/XKBLAYOUT=\"$vm_xkblayout\"/" /etc/default/console-setup
chroot \$1 sed -i "s/XKBVARIANT=\".*\"/XKBVARIANT=\"$vm_xkbvariant\"/" /etc/default/console-setup
chroot \$1 sed -i "s/XKBOPTIONS=\".*\"/XKBOPTIONS=\"$vm_xkboptions\"/" /etc/default/console-setup
EOM
        fi
    fi

    chmod 755 $script

    ssh_key=$vm_ssh_key
    if [ -z "$ssh_key" ]; then
        ssh_key="$HOME/.ssh/id_rsa.pub"
    fi

    security_mirror=""
    if [ -n "$vm_security_mirror" ]; then
        security_mirror="--security-mirror=$vm_security_mirror"
    fi

    args="kvm ubuntu --suite=$release --dest=${vm_path}/$vmname --hostname=$vmname --mirror=${vm_mirror} --rootsize=${vm_root_size} --swapsize=${vm_swap_size} --kernel-flavour=$flavor --components=main,restricted,universe,multiverse --libvirt=qemu:///system --arch=$arch --exec=$script --ssh-key=$ssh_key --user=$USER --mem=${vm_memory} --addpkg=lsb-release $security_mirror --debug"
    echo ""
    echo "Running:"
    echo "vmbuilder $args"
    sudo vmbuilder $args

    rm -f $script

    virsh --connect ${vm_connect} dumpxml $vmname >/dev/null || {
        echo "Problem creating virtual machine '$vmname' (doesn't exist)"
        error="yes"
        continue
    }

    if use_virtnet $release ; then
        interface="virtio"
    else
        interface="e1000"
    fi

    echo "Updating networking interface"
    tmp=`mktemp`
    virsh --connect ${vm_connect} dumpxml $vmname > $tmp 2>/dev/null
    sed -i 's#</interface>#<model type="'$interface'"/>\n</interface>#' $tmp
    if [ -n "$bridge" ]; then
        sed -i "s#<interface type='network'>#<interface type='bridge'>#" $tmp
        sed -i "s#<source network='default'/>#<source bridge='$bridge'/>#" $tmp
    fi
    virsh --connect ${vm_connect} define $tmp 2>/dev/null
    rm -f $tmp

    # Get rid of old ssh keys as they're probably wrong
    ssh-keygen -R $vmname 2>/dev/null
    ssh-keygen -R $vmname. 2>/dev/null
    ssh-keygen -R $vmname.local 2>/dev/null

    echo "Verifying lsb_release and waiting 30 minutes for post-installation to finish..."
    $UQT_VM_TOOLS/vm-cmd -f -s -r -t 1800 $vmname "lsb_release -c | grep $release || echo '*** WARNING *** LSB release is not $release' ; while [ -r /etc/network/if-up.d/postinstall ]; do sleep 5; done" | egrep -v '^Command: ssh'

    created="yes"
done

if [ ! -z "$created" ]; then
    cat << EOM

*** IMPORTANT ***
After logging in for the first time, you may be asked to setup the console and
the command may appear to hang. Just be patient and wait for it to complete,
otherwise you may not be able to boot.
EOM
fi

if [ ! -z "$error" ]; then
    echo "ERRORS FOUND"
    exit 1
fi
