#!/bin/bash # # Author: Daniel Kahn Gillmor # Date: 2009-10-08 # License: GPL v3+ #set -x CMD="$1" shift NAME="$1" # FIXME: make this default to the only vg (if only one vg exists), or to vg_$(hostname0) or $(hostname), if those VGs exist VG="${2:-vg_$(hostname)0}" SIZE="${3:-3G}" RAM="${4:-512}" DISK="/dev/mapper/${VG}-${NAME}" if [ -z "$CREATE_USER" ]; then CREATE_USER="yes" fi # for managing udev (we want to make sure that logical volumes get # created with ownership by the controlling user: UDEV_RULES_FILE="/etc/udev/rules.d/92-kvm_creator-%s.rules" # Why choose 92? /usr/share/doc/udev/README.Debian.gz says after 91 # default permissions and ownership are set. [ "$CMD" == "create" ] && mkdir -p /etc/sv/kvm usage() { die "USAGE: kvm-creator create|destroy|demo guestname [volumegroup [disksize [ram] ] ]" } die() { echo "$1" exit_code=1 [ -n "$2" ] && exit_code="$2" exit $exit_code } destroy() { update-service --remove "/etc/sv/kvm/$NAME" rm -rf "/etc/sv/kvm/$NAME" deluser --remove-home "$NAME" lvremove "$VG/$NAME" rm -f "$(udevrulename "$NAME")" } validate() { errors="" # Make sure none of the pieces already exist. [ -z "$NAME" ] && errors=$(printf "%s\n%s" "$errors" "Please pass the name of the virtual server to create") || : [ -z "$VG" ] && errors=$(printf "%s\n%s" "$errors" "Please pass the name of the volume group to use") || : vgs --noheadings --unbuffered -o name | tr -d ' ' | grep -q -F -x "$VG" || errors=$(printf "%s\n%s" "$errors" "Please pass the name of the volume group to use") || : if [ "$CREATE_USER" = "yes" ]; then getent passwd "$NAME" > /dev/null && errors=$(printf "%s\n%s" "$errors" "The username '$NAME' already exists.") || : getent group "$NAME" > /dev/null && errors=$(printf "%s\n%s" "The group '$NAME' already exists.") || : [ -d /home/"$NAME" ] && errors=$(printf "%s\n%s" "The directory '/home/$NAME' already exists.") || : fi [ -d /etc/sv/kvm/"$NAME" ] && errors=$(printf "%s\n%s" "The directory '/etc/sv/kvm/$NAME' already exists.") || : [ -e $DISK ] && errors=$(printf "%s\n%s" "The logical volume $NAME already exists.") || : [ -e "$(udevrulename "$NAME")" ] && errors=$(printf "%s\n%s" "The udev rules file '$(udevrulename "$NAME")' already exists.") || : [ -z "$errors" ] || die "$errors" } udevrule() { VOLUME_GROUP="$1" LOGICAL_VOLUME="$2" GROUP="$3" # this appears to be the way that a udev rule to control the LVM device gets created: printf 'ACTION=="change", SUBSYSTEM=="block", ATTR{dm/name}=="%s-%s", GROUP="%s"\n' "$VOLUME_GROUP" "$LOGICAL_VOLUME" "$GROUP" # unfortunately, kernel 2.6.26-2-amd64 (from debian) does not # appear to export /sys/block/dm-%d/dm/name ; nether does # 2.6.28-16-generic (ubuntu); 2.6.30-2-686 *does* export that # label, so the above line works on squeeze at the moment. # not sure what to do about this for lenny systems. See: # http://bugs.debian.org/450793 } udevrulename() { printf "$UDEV_RULES_FILE" "$1" } create() { set -e validate if [ "$CREATE_USER" = "yes" ]; then adduser "$NAME" --disabled-password --gecos "$NAME KVM user,,," fi USERHOMEDIR=$(getent passwd "$NAME" | cut -f6 -d:) chpst -u "$NAME:$NAME" mkdir -p "$USERHOMEDIR/"{.ssh,vms,"vms/$NAME"} if [ -f /root/.ssh/authorized_keys ]; then # is this really the right thing to do? cp /root/.ssh/authorized_keys "$USERHOMEDIR/.ssh/" fi USERGID="$(getent passwd "$NAME")" USERGID="$(cut -f4 -d: <<<$USERGID)" USERGROUP="$(getent group "$USERGID")" USERGROUP="${USERGROUP%%:*}" udevrule "$VG" "$NAME" "$USERGROUP" > $(udevrulename "$NAME") lvcreate --name "$NAME" --size "$SIZE" $VG mkdir "/etc/sv/kvm/$NAME"{,/log,/env} cat > "/etc/sv/kvm/$NAME/log/run" <&1 exec chpst -e ../env /usr/local/sbin/kvm-manager log EOF cat > "/etc/sv/kvm/$NAME/run" <&1 exec chpst -e ./env /usr/local/sbin/kvm-manager up EOF cat > "/etc/sv/kvm/$NAME/finish" <&1 exec chpst -e ./env /usr/local/sbin/kvm-manager down EOF chmod a+x "/etc/sv/kvm/$NAME/"{run,finish,log/run} echo "$NAME" > "/etc/sv/kvm/$NAME/env/OWNER" echo "$NAME" > "/etc/sv/kvm/$NAME/env/VMNAME" echo "$RAM" > "/etc/sv/kvm/$NAME/env/RAM" echo "$DISK" > "/etc/sv/kvm/$NAME/env/HDA" } demo() { validate for foo in NAME VG SIZE RAM DISK ; do echo "$foo : ${!foo}" done } [ "$CMD" != "create" ] && [ "$CMD" != "destroy" ] && [ "$CMD" != "demo" ] && usage "$CMD"