#!/bin/bash # Creates a new virtual machine from the given stage4 tarball DOMNAME="$1" STAGE_TBZ="$2" VMNAME=$(echo "${DOMNAME}" | tr '[:upper:]' '[:lower:]') VM_FQDN="${VMNAME}.$(hostname -d)" if [[ -z $STAGE_TBZ || -z $VMNAME ]]; then echo "Usage: $(basename $0) " >&2 exit 2 fi : ${VG:=vmachines} : ${POOL:=LVM} : ${DOMXML_TMPL:=/etc/libvirt/qemu/_template.xml} : ${MOUNTPOINT:=/mnt/new_vmachine} : ${PORTAGE_PROFILE:="default/linux/amd64/13.0"} : ${VM_TZ:=America/Chicago} quit() { exit "$@" } cleanup() { local mnt echo "Failed! Cleaning up..." sync ; sync ; sync if [ -n "${MOUNTPOINT}" ]; then for d in home usr var run tmp ""; do mnt="${MOUNTPOINT}"/$d mountpoint -q "${mnt}" && umount -l "${mnt}" done fi vgchange -an ${VMNAME} lvchange -an ${VG}/${VMNAME} virsh -c qemu:///system vol-delete ${VMNAME} ${POOL} } while [[ -z ${VM_ROOTPW} ]]; do read -sp 'New VM root password: ' VM_ROOTPW echo read -sp 'Confirm root password: ' confirm_rootpw echo if [[ ${VM_ROOTPW} != ${confirm_rootpw} ]]; then echo "Passwords don't match" >&2 unset VM_ROOTPW fi done set -e PORTDIR=$(portageq envvar PORTDIR) DISTDIR=$(portageq envvar DISTDIR) PKGDIR=$(portageq envvar PKGDIR) if lvdisplay ${VG}/${VMNAME} >/dev/null 2>&1; then echo "ERROR: Logical volume ${VMNAME} already exists in group ${VG}" >&2 exit 1 fi echo "Creating storage volume..." virsh -c qemu:///system vol-create-as ${POOL} ${VMNAME} 2G echo "Creating LVM volumes..." pvcreate /dev/${VG}/${VMNAME} vgcreate ${VMNAME} /dev/${VG}/${VMNAME} lvcreate -nroot -L100M ${VMNAME} lvcreate -nhome -L200M ${VMNAME} lvcreate -nvar -L400M ${VMNAME} lvcreate -nusr -l100%FREE ${VMNAME} trap "quit" INT TERM trap "cleanup" EXIT echo "Creating XFS filesystems..." for fs in root home usr var; do mkfs.xfs -L$fs -q /dev/${VMNAME}/$fs done echo "Mounting filesystems..." [[ ! -d "${MOUNTPOINT}" ]] && mkdir -p "${MOUNTPOINT}" mount /dev/${VMNAME}/root "${MOUNTPOINT}" for fs in home usr var; do mkdir "${MOUNTPOINT}"/$fs mount /dev/${VMNAME}/$fs "${MOUNTPOINT}"/$fs done mkdir "${MOUNTPOINT}"/run mount -t tmpfs tmpfs "${MOUNTPOINT}"/run echo "Extracting stage tarball..." tar -xjpf "${STAGE_TBZ}" -C "${MOUNTPOINT}" echo "Setting hostname..." sed -i "s/localhost/${VMNAME}/" "${MOUNTPOINT}"/etc/conf.d/hostname echo "Setting time zone..." rm -f "${MOUNTPOINT}"/etc/localtime ln -s /usr/share/zoneinfo/${VM_TZ} "${MOUNTPOINT}"/etc/localtime echo ${VM_TZ} > "${MOUNTPOINT}"/etc/timezone echo "Configuring network..." cat < "${MOUNTPOINT}"/etc/conf.d/net config_eth0="dhcp" dhcpcd_eth0="-q" EOF ln -s net.lo "${MOUNTPOINT}"/etc/init.d/net.eth0 ln -s /etc/init.d/net.eth0 "${MOUNTPOINT}"/etc/runlevels/default/ for keytype in dsa rsa ecdsa; do key="${MOUNTPOINT}"/etc/ssh/ssh_host_${keytype}_key echo "Creating ${keytype} SSH host key..." ssh-keygen -t ${keytype} -f "${key}" -N '' || true done echo "Configuring fstab..." cat << EOF > "${MOUNTPOINT}"/etc/fstab /dev/${VMNAME}/root / xfs ro,noatime 0 1 /dev/${VMNAME}/usr /usr xfs ro,noatime 0 2 /dev/${VMNAME}/var /var xfs noatime 0 2 /dev/${VMNAME}/home /home xfs noatime 0 2 tmpfs /tmp tmpfs defaults 0 0 EOF echo "Creating read-only root filesystem compatibility symlinks..." rm -f "${MOUNTPOINT}"/etc/{resolv,ntp}.conf rm -f "${MOUNTPOINT}"/etc/mtab ln -s /proc/self/mounts "${MOUNTPOINT}"/etc/mtab ln -s /run/resolv.conf "${MOUNTPOINT}"/etc/resolv.conf ln -s /run/ntp.conf "${MOUNTPOINT}"/etc/ntp.conf echo "Setting root password..." echo "root:${VM_ROOTPW}" | chroot "${MOUNTPOINT}" chpasswd echo "Enabling serial console..." sed -i 's/#s0/s0/' "${MOUNTPOINT}"/etc/inittab trap - INT TERM EXIT echo "Cleaning up..." umount "${MOUNTPOINT}"/{run,usr,var,home,} vgchange -an ${VMNAME} echo "Creating libvirt domain..." virsh -c qemu:///system define <(m4 -P -DNAME="${DOMNAME}" "${DOMXML_TMPL}") echo "Starting virtual machine..." virsh -c qemu:///system start ${DOMNAME}