#!/bin/bash # Creates a new virtual machine from the given stage3 tarball DOMNAME="$1" STAGE_TBZ="$2" VMNAME=$(echo "${DOMNAME}" | tr '[:upper:]' '[:lower:]') VM_FQDN="${VMNAME}.$(hostname -d)" VG=vmachines POOL=LVM DOMXML_TMPL=/etc/libvirt/qemu/_template.xml MOUNTPOINT=/mnt/new_vmachine PUPPETMASTER=$(hostname -f) PORTAGE_PROFILE="default/linux/amd64/13.0" PORTDIR=$(portageq envvar PORTDIR) DISTDIR=$(portageq envvar DISTDIR) PKGDIR=$(portageq envvar PKGDIR) if [[ -z $STAGE_TBZ || -z $VMNAME ]]; then echo "Usage: $(basename $0) " >&2 exit 1 fi set -e if lvdisplay ${VG}/${VMNAME} >/dev/null 2>&1; then echo "ERROR: Logical volume ${VMNAME} already exists in group ${VG}" >&2 exit 2 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} 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 "Installing critical packages..." # Installing packages must be done in a chroot instead of using the $ROOT # Portage environment variable because several ebuilds use enewuser/enewgroup # to create system users, which creates the user on the host system, not the # target system. See Gentoo bug #53269 for details on the problem. mkdir "${MOUNTPOINT}"/usr/portage mount -obind "${PORTDIR}" "${MOUNTPOINT}"/usr/portage mount -obind "${DISTDIR}" "${MOUNTPOINT}"/usr/portage/distfiles mount -obind "${PKGDIR}" "${MOUNTPOINT}"/usr/portage/packages mount -t tmpfs tmpfs "${MOUNTPOINT}"/tmp mount -t proc proc "${MOUNTPOINT}"/proc mount -t sysfs sysfs "${MOUNTPOINT}"/sys cat < "${MOUNTPOINT}"/build-base.sh #!/bin/bash set -e export EMERGE_DEFAULT_OPTS="--binpkg-respect-use=y" export FEATURES="-news" env-update USE=-thin emerge -jkv xfsprogs nfs-utils lvm2 rc-update add lvm boot EOF chmod u+x "${MOUNTPOINT}"/build-base.sh chroot "${MOUNTPOINT}" /build-base.sh rm "${MOUNTPOINT}"/build-base.sh echo "Installing Puppet..." cat < "${MOUNTPOINT}"/build-puppet.sh #!/bin/bash set -e export EMERGE_DEFAULT_OPTS="--binpkg-respect-use=y" export FEATURES="-news" RUBY_TARGETS=ruby18 USE="-gdbm minimal old-output" emerge -jkv puppet rc-update add puppet default EOF chmod u+x "${MOUNTPOINT}"/build-puppet.sh chroot "${MOUNTPOINT}" /build-puppet.sh rm "${MOUNTPOINT}"/build-puppet.sh echo "Configuring Puppet..." cfg_head=$(mktemp) cfg_foot=$(mktemp) awk '/\[agent\]/ {exit} {print}' "${MOUNTPOINT}"/etc/puppet/puppet.conf > ${cfg_head} awk '/\[agent\]/,0' "${MOUNTPOINT}"/etc/puppet/puppet.conf > ${cfg_foot} cat ${cfg_head} > "${MOUNTPOINT}"/etc/puppet/puppet.conf cat <> "${MOUNTPOINT}"/etc/puppet/puppet.conf server = ${PUPPETMASTER} pluginsync = true EOF cat ${cfg_foot} >> "${MOUNTPOINT}"/etc/puppet/puppet.conf rm ${cfg_head} ${cfg_foot} echo "Setting hostname..." sed -i "s/localhost/${VMNAME}/" "${MOUNTPOINT}"/etc/conf.d/hostname echo "Configuring network..." echo 'config_eth0="dhcp"' > "${MOUNTPOINT}"/etc/conf.d/net echo 'dhcpcd_eth0="-q"' >> "${MOUNTPOINT}"/etc/conf.d/net ln -s net.lo "${MOUNTPOINT}"/etc/init.d/net.eth0 ln -s /etc/init.d/net.eth0 "${MOUNTPOINT}"/etc/runlevels/default/ ln -s /etc/init.d/sshd "${MOUNTPOINT}"/etc/runlevels/default/ echo "Configuring fstab..." cat << EOF > "${MOUNTPOINT}"/etc/fstab LABEL=root / xfs noatime 0 1 LABEL=usr /usr xfs noatime 0 2 LABEL=var /var xfs noatime 0 2 LABEL=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/mounts "${MOUNTPOINT}"/etc/mtab ln -s /run/resolv.conf "${MOUNTPOINT}"/etc/resolv.conf ln -s /run/ntp.conf "${MOUNTPOINT}"/etc/ntp.conf echo "Blanking root password..." sed -i 's/^root:.*/root::0:0:::::/' "${MOUNTPOINT}"/etc/shadow echo "Enabling serial console..." sed -i 's/#s0/s0/' "${MOUNTPOINT}"/etc/inittab echo "Cleaning up..." umount "${MOUNTPOINT}"/usr/portage/{packages,distfiles,} rmdir "${MOUNTPOINT}"/usr/portage umount "${MOUNTPOINT}"/{tmp,proc,sys,run,usr,var,home,} vgchange -an ${VMNAME} echo "Creating libvirt domain..." m4 -P -DNAME="${DOMNAME}" "${DOMXML_TMPL}" | virsh -c qemu:///system define /dev/stdin echo "Starting virtual machine..." virsh -c qemu:///system start ${DOMNAME}