#!/bin/bash
# $HeadURL$ $LastChangedRevision$
# Properties
MY_ID=system_hardware_vm_virsh_storage_iscsi
PARENT_ID=system_hardware_vm_virsh_storage
# Defaults for the questions we ask (should only used here)
# Hooks
# Support functions
# Globals
STORAGESERVER_HOSTNAME=macaroni
STORAGESERVER_VOLUME_GROUP=vg0
DISK_NAME=os
DOMAIN_REGISTRATION_DATE="1 May 2010"
IOMODE=blockio
loading main
old_main()
{
local TARGET_HOSTNAME TARGET_ROOT_DISK_SIZE VMSERVER_HOSTNAME IGNORE_STATE_FILE_FLAG LIST_FILTER LIST_FORMAT MAILTO
local LVCREATE_OUTPUT VIRSH_OUTPUT IETADM_OUTPUT NEXT_FREE_TID IETADM_CMDLINE TID IQN LV_NAME LV_PATH IETCONF_RECORD TARGET_ROOT_DISK_SIZE
# Defaults for options
VERBOSELEVEL=2
IGNORE_STATE_FILE_FLAG=false
LIST_FORMAT=human
LIST_FILTER=inprogress
# Process options
while [ "X$1" != X ]; do
case $1 in
# Application specific options
--vmserver-hostname=*) VMSERVER_HOSTNAME=${1#*=} ;;
--target-hostname=*) TARGET_HOSTNAME=${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#*=} ;;
--mailto=*) MAILTO=${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
[ $MODE = get-rootfs-volname ] || validate_vars MODE || return $?
# Guts
case $MODE in
create) create --vmserver-hostname=$VMSERVER_HOSTNAME --target-hostname=$TARGET_HOSTNAME --target-root-disk-size=$TARGET_ROOT_DISK_SIZE --mailto=$MAILTO "$@" || 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 "$@" ;;
list-finished) list_finished "$@" || return $? ;;
purge) purge "$@" || return $? ;;
get-rootfs-volname) get_rootfs_volname --vmserver-hostname=$VMSERVER_HOSTNAME --target-hostname=$TARGET_HOSTNAME --target-root-disk-size=$TARGET_ROOT_DISK_SIZE "$@" || return $? ;;
*) usage ;;
esac
return 0
}
loading create
old_create()
{
local TARGET_HOSTNAME TARGET_ROOT_DISK_SIZE VMSERVER_HOSTNAME MAILTO
# Process options
while [ "X$1" != X ]; do
case $1 in
# Application specific options
--vmserver-hostname=*) VMSERVER_HOSTNAME=${1#*=} ;;
--target-hostname=*) TARGET_HOSTNAME=${1#*=} ;;
--target-root-disk-size=*) TARGET_ROOT_DISK_SIZE=${1#*=} ;;
--mailto=*) MAILTO=${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 VMSERVER_HOSTNAME TARGET_ROOT_DISK_SIZE MAILTO
debug 10 "create: TARGET_HOSTNAME=$TARGET_HOSTNAME, VMSERVER_HOSTNAME=$VMSERVER_HOSTNAME, TARGET_ROOT_DISK_SIZE=$TARGET_ROOT_DISK_SIZE, MAILTO=$MAILTO"
IQN="iqn.$(date -d "$DOMAIN_REGISTRATION_DATE" +%Y-%m).$(echo ${STORAGESERVER_HOSTNAME%%.*}.$DNS_DOMAIN | sed 's/\./\n/g' | tac | paste -d. -s):storage.disk.$TARGET_HOSTNAME.$DISK_NAME"
STORAGESERVER_IPADDR=$(host2ip $STORAGESERVER_HOSTNAME) || return $?
ROOTFS_VOLNAME=/dev/disk/by-path/ip-$STORAGESERVER_IPADDR:3260-iscsi-$IQN-lun-0
LV_NAME=$TARGET_HOSTNAME
LV_PATH=/dev/$STORAGESERVER_VOLUME_GROUP/$LV_NAME
IETCONF_RECORD="Target $IQN\\n\\tLun 0 Path=$LV_PATH,Type=$IOMODE\\n"
# Guts
# Manage state file
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
# Create volume
info "creating storage volume ..."
check_ssh_access $STORAGESERVER_HOSTNAME || return $?
LVCREATE_OUTPUT=$(ssh -n $STORAGESERVER_HOSTNAME lvcreate --size ${TARGET_ROOT_DISK_SIZE}G --name $LV_NAME $STORAGESERVER_VOLUME_GROUP 2>&1) || { error "lvcreate: failed (output was: $LVCREATE_OUTPUT)"; return 1; }
# Set some iSCSI vars
NEXT_FREE_TID=$({ ssh -n $STORAGESERVER_HOSTNAME cat /proc/net/iet/volume | sed -n 's/tid:\([^ ]*\).*/\1/p'; echo 0; } | sort -un | tail -1 | xargs expr 1 +)
debug 10 "create: NEXT_FREE_TID=$NEXT_FREE_TID, LV_PATH=$LV_PATH, IOMODE=$IOMODE, IETCONF_RECORD=[$IETCONF_RECORD]"
# Configure later sharing via iSCSI
info "sharing storage volume via iSCSI ..."
debug 10 "create: adding an entry to the config file to share the volume via iSCSI ..."
echo -en "$IETCONF_RECORD" | ssh $STORAGESERVER_HOSTNAME "cat >> /etc/ietd.conf"
# Share it right now via iSCSI
debug 10 "create: running commands to share the volume via iSCSI right now ..."
# ietadm doesn't seem to provide good return codes so we must inspect output
IETADM_OUTPUT=$(ssh -n $STORAGESERVER_HOSTNAME ietadm --op new --tid=$NEXT_FREE_TID --params Name=$IQN 2>&1)
[ "X$IETADM_OUTPUT" = X ] || { error "ietadm: failed (output was: $IETADM_OUTPUT)"; return 1; }
IETADM_OUTPUT=$(ssh -n $STORAGESERVER_HOSTNAME ietadm --op new --tid=$NEXT_FREE_TID --lun=0 --params Type=$IOMODE,Path=$LV_PATH 2>&1)
[ "X$IETADM_OUTPUT" = X ] || { error "ietadm: failed (output was: $IETADM_OUTPUT)"; return 1; }
# Define storage pool
debug 10 "create: writing a config file for the storage pool ..."
echo -e "\n$TARGET_HOSTNAME\n\n\n/dev/disk/by-path\n\n" | ssh $VMSERVER_HOSTNAME "cat > /tmp/$TARGET_HOSTNAME.xml"
VIRSH_OUTPUT=$(ssh -n $VMSERVER_HOSTNAME virsh pool-define /tmp/$TARGET_HOSTNAME.xml 2>&1)
debug 10 "create: loading the config file for the storage pool ..."
[ "X$VIRSH_OUTPUT" = "XPool $TARGET_HOSTNAME defined from /tmp/$TARGET_HOSTNAME.xml" ] || { error "virsh: failed (1) (output was: $VIRSH_OUTPUT)"; return 1; }
# Autostart the pool
debug 10 "create: marking the storage pool for autostart ..."
VIRSH_OUTPUT=$(ssh -n $VMSERVER_HOSTNAME virsh pool-autostart $TARGET_HOSTNAME 2>&1)
[ "X$VIRSH_OUTPUT" = "XPool $TARGET_HOSTNAME marked as autostarted" ] || { error "virsh: failed (2) (output was: $VIRSH_OUTPUT)"; return 1; }
# Activate the pool
debug 10 "create: activating the storage pool ..."
VIRSH_OUTPUT=$(ssh -n $VMSERVER_HOSTNAME virsh pool-start $TARGET_HOSTNAME 2>&1)
[ "X$VIRSH_OUTPUT" = "XPool $TARGET_HOSTNAME started" ] || { error "virsh: failed (3) (output was: $VIRSH_OUTPUT)"; return 1; }
# Refresh the pool (necessary for iSCSI)
debug 10 "create: refreshing the storage pool ..."
VIRSH_OUTPUT=$(ssh -n $VMSERVER_HOSTNAME virsh pool-refresh $TARGET_HOSTNAME 2>&1)
[ "X$VIRSH_OUTPUT" = "XPool $TARGET_HOSTNAME refreshed" ] || { error "virsh: failed (4) (output was: $VIRSH_OUTPUT)"; return 1; }
return 0
}
loading delete
old_delete()
{
local TARGET_HOSTNAME IGNORE_STATE_FILE_FLAG
local LVCREATE_OUTPUT VIRSH_OUTPUT IETADM_OUTPUT NEXT_FREE_TID IETADM_CMDLINE TID IQN LV_NAME LV_PATH IETCONF_RECORD TARGET_ROOT_DISK_SIZE VMSERVER_HOSTNAME
# Defaults for options
IGNORE_STATE_FILE_FLAG=false
# 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
debug 10 "delete: TARGET_HOSTNAME=$TARGET_HOSTNAME, IGNORE_STATE_FILE_FLAG=$IGNORE_STATE_FILE_FLAG"
IQN="iqn.$(date -d "$DOMAIN_REGISTRATION_DATE" +%Y-%m).$(echo ${STORAGESERVER_HOSTNAME%%.*}.$DNS_DOMAIN | sed 's/\./\n/g' | tac | paste -d. -s):storage.disk.$TARGET_HOSTNAME.$DISK_NAME"
STORAGESERVER_IPADDR=$(host2ip $STORAGESERVER_HOSTNAME) || return $?
ROOTFS_VOLNAME=/dev/disk/by-path/ip-$STORAGESERVER_IPADDR:3260-iscsi-$IQN-lun-0
LV_NAME=$TARGET_HOSTNAME
LV_PATH=/dev/$STORAGESERVER_VOLUME_GROUP/$LV_NAME
IETCONF_RECORD="Target $IQN\\n\\tLun 0 Path=$LV_PATH,Type=$IOMODE\\n"
# Guts
# Manage state file
mkdir -p $VAR_DIR/$PROGNAME
$IGNORE_STATE_FILE_FLAG || [ -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME ] || { warning "$TARGET_HOSTNAME: installation not in progress using this plugin"; return 0; }
# Only delete the storage if not running from the releaser
if [ "X$RELEASER_FLAG" != Xtrue ]; then
for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do
# Stop pool (only if delete is not at request of releaser)
ssh -n $VMSERVER_HOSTNAME "virsh pool-destroy $TARGET_HOSTNAME > /dev/null 2>&1 || true"
# Undefine pool everywhere (vol-editor could do this, but since we did the define, we do
ssh -n $VMSERVER_HOSTNAME "virsh pool-undefine $TARGET_HOSTNAME > /dev/null 2>&1 || true"
done
# Delete iSCSI share (if it exists)
info "unsharing storage volume via iSCSI ..."
TID=$(ssh -n $STORAGESERVER_HOSTNAME "cat /proc/net/iet/volume" | sed -n "s/tid:\(.*\) name:$IQN/\1/p")
debug 10 "delete: TID=$TID, LV_PATH=$LV_PATH, IOMODE=$IOMODE, IETCONF_RECORD=[$IETCONF_RECORD]"
if [ "X$TID" != X ]; then
# Unshare it right now via iSCSI
debug 10 "delete: unsharing right now via iSCSI ..."
IETADM_CMDLINE="ietadm --op delete --tid=$TID"
debug 10 "delete: IETADM_CMDLINE=[$IETADM_CMDLINE]"
ssh -n $STORAGESERVER_HOSTNAME "$IETADM_CMDLINE || true"
# Unconfigure later sharing via iSCSI
debug 10 "delete: unconfiguring later sharing via iSCSI ..."
# Too much stuff to escape, do it locally.
scp -q $STORAGESERVER_HOSTNAME:/etc/ietd.conf /tmp
perl -0777 -pi -e "s@^$IETCONF_RECORD@@ms" /tmp/ietd.conf
scp -q /tmp/ietd.conf $STORAGESERVER_HOSTNAME:/etc
fi
# Delete volume
info "deleting storage volume ..."
ssh -n $STORAGESERVER_HOSTNAME "lvremove -f /dev/$STORAGESERVER_VOLUME_GROUP/$LV_NAME || true"
fi
# Manage state file
rm -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME
return 0
}
loading list
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
{
ssh -n $STORAGESERVER_HOSTNAME "lvs $STORAGESERVER_VOLUME_GROUP"
for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do
ssh -n $VMSERVER_HOSTNAME "virsh pool-list --all"
done
} | grep $TARGET_HOSTNAME || echo "flag file present, resources not"
done
elif [ $LIST_FORMAT = terse -a $LIST_FILTER = completed ]; then
TARGET_HOSTNAMES=$({ ssh -n $STORAGESERVER_HOSTNAME lvs $STORAGESERVER_VOLUME_GROUP | sed -e '1d' | awk '{ print $1 }'; for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do ssh -n $VMSERVER_HOSTNAME "virsh pool-list --all" | sed -e '1,2d' -e '$d' | awk '{ print $1 }'; done; } | sort -u | 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=$({ ssh -n $STORAGESERVER_HOSTNAME lvs $STORAGESERVER_VOLUME_GROUP | sed -e '1d' | awk '{ print $1 }'; for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do ssh -n $VMSERVER_HOSTNAME "virsh pool-list --all" | sed -e '1,2d' -e '$d' | awk '{ print $1 }'; done; } | sort -u | while read TARGET_HOSTNAME; do [ -f $VAR_DIR/$PROGNAME/$TARGET_HOSTNAME ] || echo $TARGET_HOSTNAME; done)
for TARGET_HOSTNAME in $TARGET_HOSTNAMES; do
{
ssh -n $STORAGESERVER_HOSTNAME "lvs $STORAGESERVER_VOLUME_GROUP"
for VMSERVER_HOSTNAME in $KNOWN_VMSERVER_HOSTNAMES; do
ssh -n $VMSERVER_HOSTNAME "virsh pool-list --all"
done
} | grep $TARGET_HOSTNAME || echo "flag file present, resources not"
done
fi
return 0
}
loading purge
old_purge()
{
rm -f $VAR_DIR/$PROGNAME/*
}
loading get_rootfs_volname
old_get_rootfs_volname()
{
local TARGET_HOSTNAME TARGET_ROOT_DISK_SIZE VMSERVER_HOSTNAME
# Process options
while [ "X$1" != X ]; do
case $1 in
# Application specific options
--vmserver-hostname=*) VMSERVER_HOSTNAME=${1#*=} ;;
--target-hostname=*) TARGET_HOSTNAME=${1#*=} ;;
--target-root-disk-size=*) TARGET_ROOT_DISK_SIZE=${1#*=} ;;
# General options
--) shift; break ;;
-*) internal "get_rootfs_volname: $1: invalid option" ;;
*) break ;;
esac
shift
done
# Process arguments
[ $# = 0 ] || internal "get_rootfs_volname: $#: wrong arg count"
# Sanity checks and derivations
validate_vars TARGET_HOSTNAME VMSERVER_HOSTNAME TARGET_ROOT_DISK_SIZE || return $?
debug 10 "get_rootfs_volname: TARGET_HOSTNAME=$TARGET_HOSTNAME, VMSERVER_HOSTNAME=$VMSERVER_HOSTNAME, TARGET_ROOT_DISK_SIZE=$TARGET_ROOT_DISK_SIZE"
IQN="iqn.$(date -d "$DOMAIN_REGISTRATION_DATE" +%Y-%m).$(echo ${STORAGESERVER_HOSTNAME%%.*}.$DNS_DOMAIN | sed 's/\./\n/g' | tac | paste -d. -s):storage.disk.$TARGET_HOSTNAME.$DISK_NAME"
STORAGESERVER_IPADDR=$(host2ip $STORAGESERVER_HOSTNAME) || return $?
ROOTFS_VOLNAME=/dev/disk/by-path/ip-$STORAGESERVER_IPADDR:3260-iscsi-$IQN-lun-0
# Guts
debug 10 "get_rootfs_volname: ROOTFS_VOLNAME=$ROOTFS_VOLNAME"
echo "path=$ROOTFS_VOLNAME"
return 0
}
loading usage
old_usage()
{
local RC
RC=${1:-1}
{
echo "Usage: $PROGNAME [ ] { create | delete | list | get-rootfs-volname }"
} | if [ $RC = 0 ]; then
cat
else
cat >&2
fi
exit $RC
}