#!/bin/bash # $HeadURL$ $LastChangedRevision$ # Properties MY_ID=system_hardware_vm_virsh_engine PARENT_ID=system_hardware_vm_virsh MY_PRIVATE_ATTRIBUTES="hostname status helper" MY_PUBLIC_ATTRIBUTES= MY_ATTRIBUTES="$MY_PRIVATE_ATTRIBUTES $MY_PUBLIC_ATTRIBUTES" MY_HELPER_IDS="$(ls | fgrep -v helper | xargs echo)" # Defaults for the questions we ask (should only used here) # Hooks # Properties # My attributes for MY_ATTRIBUTE in ${MY_ATTRIBUTES^^}; do eval "$MY_ATTRIBUTE="; done # Hooks list_prologue() { return 0 } edit_prologue() { map_an_inherited_attribute_to_my_helpers } create_prologue() { map_an_inherited_attribute_to_my_helpers } delete_prologue() { map_an_inherited_attribute_to_my_helpers } map_an_inherited_attribute_to_my_helpers() { # my properties inherit MY_HELPER_IDS MY_ATTRIBUTES # my attributes inherit ${MY_ATTRIBUTES^^} # those inherited attributes i will use inherit ENGINE # real local # Sanity checks [ "X$ENGINE" = Xxen -o "X$ENGINE" = Xkvm ] || internal "ENGINE: seems not to have been propogated" # Guts MY_HELPER_IDS=$ENGINE return 0 } # Support functions old_main() { local TARGET_HOSTNAME TARGET_HVM_FLAG TARGET_VM_APP VMSERVER_HOSTNAME TARGET_MACADDR TARGET_RAM TARGET_RELEASE TARGET_ARCH VOLEDITOR_PLUGIN TARGET_ROOT_DISK_SIZE IGNORE_STATE_FILE_FLAG LIST_FORMAT LIST_FILTER DEBIAN_URL local HEADLESS_FLAG # Defaults for options VERBOSELEVEL=2 HEADLESS_FLAG=false IGNORE_STATE_FILE_FLAG=false LIST_FORMAT=human LIST_FILTER=inprogress # Process options while [ "X$1" != X ]; do case $1 in # Application specific options --target-hostname=*) TARGET_HOSTNAME=${1#*=} ;; --target-hvm-flag=*) TARGET_HVM_FLAG=${1#*=} ;; --target-vm-app=*) TARGET_VM_APP=${1#*=} ;; --vmserver-hostname=*) VMSERVER_HOSTNAME=${1#*=} ;; --target-macaddr=*) TARGET_MACADDR=${1#*=} ;; --target-release=*) TARGET_RELEASE=${1#*=} ;; --target-arch=*) TARGET_ARCH=${1#*=} ;; --target-ram=*) TARGET_RAM=${1#*=} ;; --headless-flag=*) HEADLESS_FLAG=${1#*=} ;; --voleditor-plugin=*) VOLEDITOR_PLUGIN=${1#*=} ;; --target-root-disk-size=*) TARGET_ROOT_DISK_SIZE=${1#*=} ;; --ignore-state-file-flag=*) IGNORE_STATE_FILE_FLAG=${1#*=} ;; --list-format=*) LIST_FORMAT=${1#*=} ;; --list-filter=*) LIST_FILTER=${1#*=} ;; --debian-url=*) DEBIAN_URL=${1#*=} ;; # General options --help|-h) usage 0 ;; --verbose|-v) VERBOSELEVEL=3 ;; -d) VERBOSELEVEL=$2; shift ;; --debug=*) VERBOSELEVEL=${1#*=} ;; --) shift; break ;; -*) usage ;; *) break ;; esac shift done # Process arguments [ $# -ge 1 ] || usage MODE=$1; shift debug 10 "main: MODE=$MODE" # Sanity checks and derivations validate_vars || return $? # Guts case $MODE in create) create --target-hostname=$TARGET_HOSTNAME --target-hvm-flag=$TARGET_HVM_FLAG --target-vm-app=$TARGET_VM_APP --vmserver-hostname=$VMSERVER_HOSTNAME --target-macaddr=$TARGET_MACADDR --target-ram=$TARGET_RAM --headless-flag=$HEADLESS_FLAG --target-release=$TARGET_RELEASE --target-arch=$TARGET_ARCH --voleditor-plugin=$VOLEDITOR_PLUGIN --target-root-disk-size=$TARGET_ROOT_DISK_SIZE --debian-url=$DEBIAN_URL "$@" || return $? ;; delete) delete --target-hostname=$TARGET_HOSTNAME --ignore-state-file-flag=$IGNORE_STATE_FILE_FLAG "$@" || return $? ;; list) list --list-format=$LIST_FORMAT --list-filter=$LIST_FILTER "$@" ;; purge) purge "$@" || return $? ;; *) usage ;; esac return 0 } old_create() { local TARGET_HOSTNAME TARGET_HVM_FLAG TARGET_VM_APP VMSERVER_HOSTNAME TARGET_MACADDR TARGET_RAM TARGET_RELEASE TARGET_ARCH VOLEDITOR_PLUGIN TARGET_ROOT_DISK_SIZE local HEADLESS_FLAG local LINE DHCPD_OUTPUT VIRTINSTALL_VIRTLEVEL_OPTS VIRTINSTALL_DISK_OPTS VIRTINSTALL_GPU_OPTS VIRSH_OUTPUT local VIRTINSTALL_OUTPUT HEADLESS_FLAG CMDLINE KERNEL_PRESEEDING_ARGS VIRTINSTALL_VIRTAPP_OPTS VMSERVER_RELEASE VIRTINSTALL_OS_OPTS local VIRTINSTALL_NET_OPTS VIRTINSTALL_OTHER_OPTS ROOTFS_VOLNAME DEBIAN_URL # Process options while [ "X$1" != X ]; do case $1 in # Application specific options --target-hostname=*) TARGET_HOSTNAME=${1#*=} ;; --target-hvm-flag=*) TARGET_HVM_FLAG=${1#*=} ;; --target-vm-app=*) TARGET_VM_APP=${1#*=} ;; --vmserver-hostname=*) VMSERVER_HOSTNAME=${1#*=} ;; --target-macaddr=*) TARGET_MACADDR=${1#*=} ;; --target-release=*) TARGET_RELEASE=${1#*=} ;; --target-arch=*) TARGET_ARCH=${1#*=} ;; --target-ram=*) TARGET_RAM=${1#*=} ;; --headless-flag=*) HEADLESS_FLAG=${1#*=} ;; --voleditor-plugin=*) VOLEDITOR_PLUGIN=${1#*=} ;; --target-root-disk-size=*) TARGET_ROOT_DISK_SIZE=${1#*=} ;; --debian-url=*) DEBIAN_URL=${1#*=} ;; # General options --) shift; break ;; -*) internal "create: $1: invalid option" ;; *) break ;; esac shift done # Process arguments [ $# = 0 ] || internal "create: $#: wrong arg count" # Sanity checks and derivations validate_vars TARGET_HOSTNAME TARGET_HVM_FLAG TARGET_VM_APP VMSERVER_HOSTNAME TARGET_MACADDR TARGET_RAM VOLEDITOR_PLUGIN TARGET_ROOT_DISK_SIZE DEBIAN_URL || return $? debug 10 "create: TARGET_HOSTNAME=$TARGET_HOSTNAME, TARGET_HVM_FLAG=$TARGET_HVM_FLAG, TARGET_VM_APP=$TARGET_VM_APP, VMSERVER_HOSTNAME=$VMSERVER_HOSTNAME, TARGET_MACADDR=$TARGET_MACADDR, TARGET_RAM=$TARGET_RAM, HEADLESS_FLAG=$HEADLESS_FLAG, VOLEDITOR_PLUGIN=$VOLEDITOR_PLUGIN, TARGET_ROOT_DISK_SIZE=$TARGET_ROOT_DISK_SIZE, DEBIAN_URL=$DEBIAN_URL" check_ssh_access $VMSERVER_HOSTNAME || return $? validate_vars HEADLESS_FLAG || return $? $HEADLESS_FLAG || ! $TARGET_HVM_FLAG || { [ "X$DISPLAY" != X ] || { error "DISPLAY: not set"; return 1; } xlsfonts > /dev/null 2>&1 || { error "creation of VMs will require DISPLAY to be set and X authentication enabled"; return 1; } ssh -n $VMSERVER_HOSTNAME [ -x /usr/bin/virt-viewer ] || { error "$VMSERVER_HOSTNAME: virt-viewer not found (do you need to run 'apt-get -y install virt-viewer' on $VMSERVER_HOSTNAME?)"; return 1; } } CMDLINE="$MODROOT/etc/local-scripts/$VOLEDITOR_PLUGIN --debug=$VERBOSELEVEL --target-root-disk-size=$TARGET_ROOT_DISK_SIZE --target-hostname=$TARGET_HOSTNAME --vmserver-hostname=$VMSERVER_HOSTNAME get-rootfs-volname" debug 10 "create: calling [$CMDLINE] ..." ROOTFS_VOLNAME=$(eval "$CMDLINE") || { RC=$?; error "vol-editor plugin '$VOLEDITOR_PLUGIN' failed"; return $RC; } get_release_by_inspection --hostname=$VMSERVER_HOSTNAME VMSERVER_RELEASE || return $? if [ $VMSERVER_RELEASE = lenny ] && [ $TARGET_VM_APP = xen ] && $TARGET_HVM_FLAG; then VIRTINSTALL_VIRTAPP_OPTS="--connect=xen" VIRTINSTALL_VIRTLEVEL_OPTS=--hvm VIRTINSTALL_BOOTMEDIA_OPTS="--pxe" VIRTINSTALL_GPU_OPTS="--vnc --vncport=5917" VIRTINSTALL_DISK_OPTS="--disk $ROOTFS_VOLNAME" VIRTINSTALL_NET_OPTS="--network=bridge:br0 --mac=$TARGET_MACADDR" # I did not previously use this setting! So verify ok! VIRTINSTALL_OS_OPTS="--os-type=other" VIRTINSTALL_OTHER_OPTS= elif [ $VMSERVER_RELEASE = lenny ] && [ $TARGET_VM_APP = xen ] && ! $TARGET_HVM_FLAG; then VIRTINSTALL_VIRTAPP_OPTS="--connect=xen" VIRTINSTALL_VIRTLEVEL_OPTS=--paravirt # Beware that these kernel command line arguments are also in preseed-editor (where # they are only used for PMs/HVMs, though the netcfg/dhcp_timeout is really only for # PVs which get to doing an DHCP request so much faster) get_kernel_preseeding_args --target-vm-flag=true --target-hvm-flag=$TARGET_HVM_FLAG --target-release=$TARGET_RELEASE --target-hostname=$TARGET_HOSTNAME KERNEL_PRESEEDING_ARGS || return $? VIRTINSTALL_BOOTMEDIA_OPTS="--location $DEBIAN_URL/dists/$TARGET_RELEASE/main/installer-$TARGET_ARCH/ -x \"$KERNEL_PRESEEDING_ARGS\"" VIRTINSTALL_GPU_OPTS=--nographics VIRTINSTALL_DISK_OPTS="--disk $ROOTFS_VOLNAME" VIRTINSTALL_NET_OPTS="--network=bridge:br0 --mac=$TARGET_MACADDR" # I did not previously use this setting! So verify ok! VIRTINSTALL_OS_OPTS="--os-type=linux --os-variant=generic26" VIRTINSTALL_OTHER_OPTS= elif [ $VMSERVER_RELEASE = lenny ] && [ $TARGET_VM_APP = kvm ] && $TARGET_HVM_FLAG; then [ $MODE != create ] || { error "KVM is unsupported on lenny VM servers simply because it has not been implemented in $PROGNAME (1)"; return 1; } elif [ $VMSERVER_RELEASE = lenny ] && [ $TARGET_VM_APP = kvm ] && ! $TARGET_HVM_FLAG; then [ $MODE != create ] || { error "KVM is unsupported on lenny VM servers simply because it has not been implemented in $PROGNAME (2)"; return 1; } elif [ $VMSERVER_RELEASE != lenny ] && [ $TARGET_VM_APP = xen ] && $TARGET_HVM_FLAG; then error "Xen is unsupported on non-lenny VM servers due to continuing nVidia driver incompatibilities" return 1 elif [ $VMSERVER_RELEASE != lenny ] && [ $TARGET_VM_APP = xen ] && ! $TARGET_HVM_FLAG; then error "Xen is unsupported on non-lenny VM servers due to continuing nVidia driver incompatibilities" return 1 elif [ $VMSERVER_RELEASE != lenny ] && [ $TARGET_VM_APP = kvm ] && $TARGET_HVM_FLAG; then VIRTINSTALL_VIRTAPP_OPTS="--connect=qemu:///system" VIRTINSTALL_VIRTLEVEL_OPTS= VIRTINSTALL_BOOTMEDIA_OPTS="--pxe" VIRTINSTALL_GPU_OPTS="--vnc --vncport=5917" VIRTINSTALL_DISK_OPTS="--disk $ROOTFS_VOLNAME" VIRTINSTALL_NET_OPTS="--network bridge:br0,mac=$TARGET_MACADDR" VIRTINSTALL_OS_OPTS="--os-type=none" VIRTINSTALL_OTHER_OPTS= elif [ $VMSERVER_RELEASE != lenny ] && [ $TARGET_VM_APP = kvm ] && ! $TARGET_HVM_FLAG; then VIRTINSTALL_VIRTAPP_OPTS="--connect=qemu:///system" VIRTINSTALL_VIRTLEVEL_OPTS= # Beware that these kernel command line arguments are also in preseed-editor (where # they are only used for PMs/HVMs, though the netcfg/dhcp_timeout is really only for # PVs which get to doing an DHCP request so much faster) get_kernel_preseeding_args --target-vm-flag=true --target-hvm-flag=$TARGET_HVM_FLAG --target-release=$TARGET_RELEASE --target-hostname=$TARGET_HOSTNAME KERNEL_PRESEEDING_ARGS || return $? # Note kvm needs to be told to use a serial console VIRTINSTALL_BOOTMEDIA_OPTS="--location $DEBIAN_URL/dists/$TARGET_RELEASE/main/installer-$TARGET_ARCH/ -x \"console=ttyS0,115200 $KERNEL_PRESEEDING_ARGS\"" VIRTINSTALL_GPU_OPTS=--nographics VIRTINSTALL_DISK_OPTS="--disk $ROOTFS_VOLNAME,bus=virtio" VIRTINSTALL_NET_OPTS="--network bridge:br0,mac=$TARGET_MACADDR,model=virtio" VIRTINSTALL_OS_OPTS="--os-type=linux --os-variant=generic26" VIRTINSTALL_OTHER_OPTS= else internal "unsupported combination" fi # Guts mkdir -p $VAR_DIR/$PROGNAME [ ! -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME ] || { error "$TARGET_HOSTNAME: installation in progress"; return 1; } touch $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME info "creating VM ..." # We need to distinguish between in-progress repo access and normal repo access. # We do this with a flag file; this will simplify the list() function's task of # distinguishing. mkdir -p $VAR_DIR/$PROGNAME touch $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME CMDLINE="virt-install $VIRTINSTALL_VIRTAPP_OPTS --name=$TARGET_HOSTNAME --ram=$TARGET_RAM $VIRTINSTALL_BOOTMEDIA_OPTS $VIRTINSTALL_VIRTLEVEL_OPTS $VIRTINSTALL_DISK_OPTS $VIRTINSTALL_NET_OPTS $VIRTINSTALL_GPU_OPTS $VIRTINSTALL_OS_OPTS $VIRTINSTALL_OTHER_OPTS --noautoconsole" debug 10 "create: CMDLINE is [ssh -n $VMSERVER_HOSTNAME $CMDLINE]" #debug 10 "create: sleeping 300 seconds to let volume become visible ..."; sleep 300 VIRTINSTALL_OUTPUT=$(ssh -n $VMSERVER_HOSTNAME "$CMDLINE" 2>&1) || { RC=1; error "virt-install: failed, output follows"; error "($VIRTINSTALL_OUTPUT)"; rm -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME; return $RC; } # View console if $HEADLESS_FLAG; then info "console not attached; you can monitor status with 'mdi list'" elif $TARGET_HVM_FLAG; then info "launching console viewer ..." ssh -Yn $VMSERVER_HOSTNAME virt-viewer $TARGET_HOSTNAME else info "attaching to text console ..." ssh -t $VMSERVER_HOSTNAME virsh console $TARGET_HOSTNAME fi return 0 } old_delete() { local TARGET_HOSTNAME IGNORE_STATE_FILE_FLAG # Process options while [ "X$1" != X ]; do case $1 in # Application specific options --target-hostname=*) TARGET_HOSTNAME=${1#*=} ;; --ignore-state-file-flag=*) IGNORE_STATE_FILE_FLAG=${1#*=} ;; # General options --) shift; break ;; -*) internal "delete: $1: invalid option" ;; *) break ;; esac shift done # Process arguments [ $# = 0 ] || internal "delete: $#: wrong arg count" # Sanity checks and derivations validate_vars TARGET_HOSTNAME IGNORE_STATE_FILE_FLAG || return $? debug 10 "delete: TARGET_HOSTNAME=$TARGET_HOSTNAME, IGNORE_STATE_FILE_FLAG=$IGNORE_STATE_FILE_FLAG" # Guts # Manage state file mkdir -p $VAR_DIR/$PROGNAME # Requesting to delete an installation while respecting state files when there *isn't* a state # file is not an error. Well, it might be if mdi was capable of seeing exactly what needed to # be called when CTRL-C gets pressed, but it isn't. So we just make this a warning. $IGNORE_STATE_FILE_FLAG || [ -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME ] || { warning "$TARGET_HOSTNAME: installation not in progress"; return 0; } # Delete VM (only if delete is not at request of releaser) if [ "X$RELEASER_FLAG" != Xtrue ]; then # Delete it everywhere (and allow for that it might not be everywhere) info "deleting VM ..." debug 10 "delete: attempting to destroy and undefine VM on all known VM servers ..." for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do # Destroy it and undefine it ssh -n $VMSERVER_HOSTNAME virsh destroy $TARGET_HOSTNAME > /dev/null 2>&1 || true ssh -n $VMSERVER_HOSTNAME virsh undefine $TARGET_HOSTNAME > /dev/null 2>&1 || true done # Then verify it does not exist anywhere. debug 10 "delete: checking not defined on all known VM servers ..." for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do # Strip leading and trailing spaces as the virsh output sometimes puts a space # on the beginning and sometimes on the end! Doh! VIRSH_OUTPUT=$(ssh -n $VMSERVER_HOSTNAME virsh domstate $TARGET_HOSTNAME 2>&1 | paste -s -d' ' | sed -e 's/^ *//' -e 's/ *$//') || true debug 10 "delete: VIRSH_OUTPUT is [$VIRSH_OUTPUT]" get_release_by_inspection --hostname=$VMSERVER_HOSTNAME VMSERVER_RELEASE || return $? if [ $VMSERVER_RELEASE = squeeze ]; then [ "$VIRSH_OUTPUT" = "error: failed to get domain '$TARGET_HOSTNAME' error: Domain not found: no domain with matching name '$TARGET_HOSTNAME'" ] || { error "virsh: failed (1) (output was: $VIRSH_OUTPUT)"; return 1; } elif [ $VMSERVER_RELEASE = lenny ]; then [ "$VIRSH_OUTPUT" = "libvir: Xen Daemon error : GET operation failed: xend_get: error from xen daemon: error: failed to get domain '$TARGET_HOSTNAME'" ] || { error "virsh: failed (2) (output was: $VIRSH_OUTPUT)"; return 1; } fi done fi # Manage state file rm -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME return 0 } old_purge() { # Guts # To dangerous to remove actual VMs rm -f $VAR_DIR/$PROGNAME/* return 0 } old_list() { local TARGET_HOSTNAME LIST_FORMAT LIST_FILTER local CMDLINE # Defaults for options LIST_FORMAT=human LIST_FILTER=inprogress # Process options while [ "X$1" != X ]; do case $1 in # Application specific options --list-format=*) LIST_FORMAT=${1#*=} ;; --list-filter=*) LIST_FILTER=${1#*=} ;; # General options --) shift; break ;; -*) internal "list: $1: invalid option" ;; *) break ;; esac shift done # Process arguments [ $# = 0 ] || internal "list: $#: wrong arg count" # Sanity checks and derivations validate_vars LIST_FORMAT LIST_FILTER || return $? # Guts # Manage state file mkdir -p $VAR_DIR/$PROGNAME # Do the listing if [ $LIST_FORMAT = terse -a $LIST_FILTER = inprogress ]; then TARGET_HOSTNAMES=$(ls -1 $VAR_DIR/$PROGNAME) for TARGET_HOSTNAME in $TARGET_HOSTNAMES; do echo $TARGET_HOSTNAME done elif [ $LIST_FORMAT = human -a $LIST_FILTER = inprogress ]; then TARGET_HOSTNAMES=$(ls -1 $VAR_DIR/$PROGNAME) for TARGET_HOSTNAME in $TARGET_HOSTNAMES; do for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do ssh -n $VMSERVER_HOSTNAME "virsh list --all" | sed -n "s/^\(....$TARGET_HOSTNAME .*\)/\1 (on $VMSERVER_HOSTNAME)/p" done done elif [ $LIST_FORMAT = terse -a $LIST_FILTER = completed ]; then TARGET_HOSTNAMES=$(for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do ssh -n $VMSERVER_HOSTNAME "virsh list --all" | sed -e '1,2d' -e '$d' | awk '{ print $2 }'; done | while read TARGET_HOSTNAME; do [ -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME ] || echo $TARGET_HOSTNAME; done) for TARGET_HOSTNAME in $TARGET_HOSTNAMES; do echo $TARGET_HOSTNAME done elif [ $LIST_FORMAT = human -a $LIST_FILTER = completed ]; then TARGET_HOSTNAMES=$(for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do ssh -n $VMSERVER_HOSTNAME "virsh list --all" | sed -e '1,2d' -e '$d' | awk '{ print $2 }'; done | while read TARGET_HOSTNAME; do [ -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME ] || echo $TARGET_HOSTNAME; done) for TARGET_HOSTNAME in $TARGET_HOSTNAMES; do for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do ssh -n $VMSERVER_HOSTNAME "virsh list --all" | sed -n "s/^\(....$TARGET_HOSTNAME .*\)/\1 (on $VMSERVER_HOSTNAME)/p" done done fi return 0 } old_usage() { local RC RC=${1:-1} { echo "Usage: $PROGNAME [ ] { create | delete | list }" echo } | if [ $RC = 0 ]; then cat else cat >&2 fi exit $RC }