#!/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() {
    cat << EOM
Usage:	vm-new [-f] [-b BRIDGE] release arch prefix1 prefix2

 -f		Don't prompt for confirmation
 -b BRIDGE	Use interface BRIDGE for bridged networking
 -d DISTRO	debian|ubuntu (default: ubuntu)
 release	dapper|hardy|intrepid|jaunty|karmic|...
 arch		i386|amd64|...
 prefix		prefix for the machine (eg, 'sec' or 'clean')

Eg, 'vm-new jaunty i386 clean' will create a new vm called 'clean-jaunty-i386'
EOM
}

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
}

use_virtdisk() {
    rel="$1"
    virtdisk_rel="jaunty"

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

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

force=
bridge=
distro="ubuntu"
while getopts "hfsb:d:" opt
do
    case "$opt" in
        f) force="yes";;
        b) bridge="$OPTARG";;
        d) distro="$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 $distro ; then
    echo "Unsupported release '$release' for '$distro'"
    exit 1
fi
shift

flavor="generic"
arch="$1"
ubuntu_args=""
debian_args=""
if [ "$distro" = "ubuntu" ]; then
    vm_extra_packages="$vm_extra_packages ubuntu-standard"
    ubuntu_args="--components=main,restricted,universe,multiverse"

    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
else
    flavor=""
    debian_args=""
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
if [ -n "$flavor" ]; then
    flavor="--kernel-flavour=$flavor"
fi

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

    vmname="${prefix}-${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: ${distro}/${release}"

    if [ "$force" != "yes" ]; then
        echo -n "Continue (y/N)? "
        read ans
        if [ "$ans" != "y" ] && [ "$ans" != "Y" ]; then
            echo "Skipping '$prefix'"
            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" != "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 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'
chroot \$1 sh -c 'echo xserver-xorg xserver-xorg/autodetect_keyboard string false | debconf-set-selections'
chroot \$1 sh -c 'echo xserver-xorg xserver-xorg/config/inputdevice/keyboard/model string $vm_xkbmodel | debconf-set-selections'
chroot \$1 sh -c 'echo xserver-xorg xserver-xorg/config/inputdevice/keyboard/layout string $vm_xkblayout | debconf-set-selections'
chroot \$1 sh -c 'echo xserver-xorg xserver-xorg/config/inputdevice/keyboard/variant string $vm_xkbvariant | debconf-set-selections'
chroot \$1 sh -c 'echo xserver-xorg xserver-xorg/config/inputdevice/keyboard/options string $vm_xkboptions | debconf-set-selections'
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

    mirror=""
    if [ -n "$vm_mirror" ]; then
        mirror="--mirror=$vm_mirror"
    fi

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

    args="kvm $distro --suite=$release --dest=${vm_path}/$vmname --hostname=$vmname ${mirror} --rootsize=${vm_root_size} --swapsize=${vm_swap_size} $flavor $ubuntu_args $debian_args --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
    if use_virtdisk $release ; then
        disk_name="vda"
        disk_bus="virtio"
    else
        disk_name="hda"
        disk_bus="ide"
    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
    sed -i "s#<target dev='hda' bus='ide'/>#<target dev='$disk_name' bus='$disk_bus'/>#" $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 [ -n "$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.

You may also be interested in using snapshots. To do so, please use:
$ vm-use-snapshots "$vmname"
EOM
fi

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