1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00

vdo: lvm_import_vdo enhancements

Work also with devices that may have ':' inside their generated
/dev/disk/by-id

Ensure there is no race with systems' auto activation while using
the snapshot for conversion.

Update system's vdoconf.yml after the use of snapshot for conversion.

Skip unnecesary prompt for 'convert' while using snapshot and query only
for final snaphot merge.

Prohibit conversion for a device with the PV header.

Enhance 'trap' protection for more signals.

Improve clean() recovery path.

Replace bash 'test' command with [].

Correct some output message to print $TOOL.

Support also options without '-' in the middle i.e. --nosnapshot.

For shellcheck predefine all variables extracted from vdoconf.yml.
This commit is contained in:
Zdenek Kabelac 2023-09-08 17:19:39 +02:00
parent fa49651301
commit d9cebeaf34

View File

@ -36,6 +36,9 @@ TEMPDIR="${TMPDIR:-/tmp}/$IMPORT_NAME"
_SAVEPATH=$PATH _SAVEPATH=$PATH
PATH="/sbin:/usr/sbin:/bin:/usr/sbin:$PATH" PATH="/sbin:/usr/sbin:/bin:/usr/sbin:$PATH"
# Set of trapped signals
declare -a SIGNALS=("HUP" "INT" "QUIT" "ABRT" "TERM" "EXIT")
# user may override lvm location by setting LVM_BINARY # user may override lvm location by setting LVM_BINARY
LVM=${LVM_BINARY:-lvm} LVM=${LVM_BINARY:-lvm}
VDO=${VDO_BINARY:-vdo} VDO=${VDO_BINARY:-vdo}
@ -52,7 +55,9 @@ DM_DEV_DIR="${DM_DEV_DIR:-/dev}"
DM_UUID_PREFIX="${DM_UUID_PREFIX:-}" DM_UUID_PREFIX="${DM_UUID_PREFIX:-}"
DM_VG_NAME= DM_VG_NAME=
DM_LV_NAME= DM_LV_NAME=
DEFAULT_VDO_CONFIG="/etc/vdoconf.yml" # Default location of vdo's manager config file
VDO_CONFIG=${VDO_CONFIG:-} # can be overridden with --vdo-config VDO_CONFIG=${VDO_CONFIG:-} # can be overridden with --vdo-config
VDO_CONFIG_RESTORE=
VDOCONF= VDOCONF=
test -n "$VDO_CONFIG" && VDOCONF="-f $VDO_CONFIG" test -n "$VDO_CONFIG" && VDOCONF="-f $VDO_CONFIG"
@ -61,16 +66,17 @@ VGNAME=
LVNAME= LVNAME=
DEVMAJOR=0 DEVMAJOR=0
DEVMINOR=0 DEVMINOR=0
PROMPTING="" PROMPTING=
USE_VDO_DM_SNAPSHOT=1 USE_VDO_DM_SNAPSHOT="--yes"
VDO_DM_SNAPSHOT_NAME= VDO_DM_SNAPSHOT_NAME=
VDO_DM_SNAPSHOT_DEVICE= VDO_DM_SNAPSHOT_DEVICE=
VDO_SNAPSHOT_LOOP= VDO_SNAPSHOT_LOOP=
VDO_INCONSISTENT=
DRY=0 DRY=0
VERB="" VERB=
FORCE="" FORCE=
YES="" YES=
ABORT_AFTER_VDO_CONVERT=0 ABORT_AFTER_VDO_CONVERT=0
VDO_ALLOCATION_PARAMS= VDO_ALLOCATION_PARAMS=
@ -78,6 +84,25 @@ VDO_ALLOCATION_PARAMS=
DEFAULT_NAME="vdovg/vdolvol" DEFAULT_NAME="vdovg/vdolvol"
NAME="" NAME=""
# predefine empty
vdo_ackThreads=
vdo_bioRotationInterval=
vdo_bioThreads=
vdo_blockMapCacheSize=
vdo_blockMapPeriod=
vdo_compression=
vdo_cpuThreads=
vdo_deduplication=
vdo_hashZoneThreads=
vdo_indexMemory=
vdo_indexSparse=
vdo_logicalBlockSize=
vdo_logicalThreads=
vdo_maxDiscardSize=
vdo_physicalThreads=
vdo_slabSize=
vdo_writePolicy=
# help message # help message
tool_usage() { tool_usage() {
echo "${TOOL}: Utility to convert VDO volume to VDO LV." echo "${TOOL}: Utility to convert VDO volume to VDO LV."
@ -107,7 +132,11 @@ error() {
for i in "$@" ; do for i in "$@" ; do
echo "$TOOL: $i" >&2 echo "$TOOL: $i" >&2
done done
cleanup 1 return 1
}
warn() {
echo "$TOOL: WARNING: $i" >&2
} }
dry() { dry() {
@ -120,20 +149,31 @@ dry() {
} }
cleanup() { cleanup() {
trap '' 2 RC=$? # Return code + 128 of the last command eg INT=2 + 128 -> 130
test -n "$VDO_DM_SNAPSHOT_NAME" && {
trap '' "${SIGNALS[@]}" # mute trap for all signals to not interrupt cleanup() on any next signal
[ -z "$PROMPTING" ] || echo "No"
[ -e "$VDO_CONFIG_RESTORE" ] && { dry cp -a "$VDO_CONFIG_RESTORE" "${VDO_CONFIG:-"$DEFAULT_VDO_CONFIG"}" || true ; }
if [ -n "$VDO_DM_SNAPSHOT_NAME" ]; then
dry "$LVM" vgchange -an --devices "$VDO_DM_SNAPSHOT_DEVICE" "$VGNAME" &>/dev/null || true
for i in {1..20} ; do for i in {1..20} ; do
test "$("$DMSETUP" info --noheading -co open "$VDO_DM_SNAPSHOT_NAME")" = "0" && break [ "$(dry "$DMSETUP" info --noheading -co open "$VDO_DM_SNAPSHOT_NAME")" = "0" ] && break
sleep .1 sleep .1
done done
"$DMSETUP" remove "$VDO_DM_SNAPSHOT_NAME" || true dry "$DMSETUP" remove "$VDO_DM_SNAPSHOT_NAME" &>/dev/null || true
} fi
test -n "$VDO_SNAPSHOT_LOOP" && { "$LOSETUP" -d "$VDO_SNAPSHOT_LOOP" || true ; }
[ -n "$VDO_SNAPSHOT_LOOP" ] && { dry "$LOSETUP" -d "$VDO_SNAPSHOT_LOOP" || true ; }
[ -z "$VDO_INCONSISTENT" ] || echo "$TOOL: VDO volume import process exited unexpectedly!" >&2
test -z "$PROMPTING" || echo "No"
rm -rf "$TEMPDIR" || true rm -rf "$TEMPDIR" || true
# error exit status for break
exit "${1:-1}" exit "$RC"
} }
# Create snapshot target like for persistent snapshot with 16KiB chunksize # Create snapshot target like for persistent snapshot with 16KiB chunksize
@ -148,7 +188,7 @@ snapshot_create_() {
# TODO: maybe use ramdisk via 'brd' device ?) # TODO: maybe use ramdisk via 'brd' device ?)
"$TRUNCATE" -s 20M "$file" "$TRUNCATE" -s 20M "$file"
VDO_SNAPSHOT_LOOP=$("$LOSETUP" -f --show "$file") VDO_SNAPSHOT_LOOP=$("$LOSETUP" -f --show "$file")
"$DMSETUP" create "$VDO_DM_SNAPSHOT_NAME" -u "${DM_UUID_PREFIX}-${VDO_DM_SNAPSHOT_NAME}-priv" --table "$(snapshot_target_line_ "$1" "$VDO_SNAPSHOT_LOOP")" "$DMSETUP" create "$VDO_DM_SNAPSHOT_NAME" -u "${DM_UUID_PREFIX}${VDO_DM_SNAPSHOT_NAME}-priv" --table "$(snapshot_target_line_ "$1" "$VDO_SNAPSHOT_LOOP")"
VDO_DM_SNAPSHOT_DEVICE="$DM_DEV_DIR/mapper/$VDO_DM_SNAPSHOT_NAME" VDO_DM_SNAPSHOT_DEVICE="$DM_DEV_DIR/mapper/$VDO_DM_SNAPSHOT_NAME"
verbose "Snapshot of VDO device $1 created: $VDO_DM_SNAPSHOT_DEVICE." verbose "Snapshot of VDO device $1 created: $VDO_DM_SNAPSHOT_DEVICE."
} }
@ -164,6 +204,8 @@ snapshot_merge_() {
} }
verbose "Merging converted VDO volume..." verbose "Merging converted VDO volume..."
VDO_INCONSISTENT=1
# Running merging # Running merging
"$DMSETUP" resume "$VDO_DM_SNAPSHOT_NAME" "$DMSETUP" resume "$VDO_DM_SNAPSHOT_NAME"
@ -175,20 +217,24 @@ snapshot_merge_() {
for i in $(seq 1 20) ; do for i in $(seq 1 20) ; do
status=( $("$DMSETUP" status "$VDO_DM_SNAPSHOT_NAME") ) status=( $("$DMSETUP" status "$VDO_DM_SNAPSHOT_NAME") )
# Check if merging is finished # Check if merging is finished
test "${status[3]%/*}" = "${status[4]}" && break [ "${status[3]%/*}" = "${status[4]}" ] && break
# Wait a bit and retry # Wait a bit and retry
sleep .2 sleep .2
done done
test "${status[3]%/*}" = "${status[4]}" || {
if [ "${status[3]%/*}" != "${status[4]}" ]; then
# FIXME: Now what shall we do ??? Help.... # FIXME: Now what shall we do ??? Help....
# Keep snapshot in table for possible analysis... # Keep snapshot in DM table for possible analysis...
VDO_DM_SNAPSHOT_NAME= VDO_DM_SNAPSHOT_NAME=
VDO_SNAPSHOT_LOOP= VDO_SNAPSHOT_LOOP=
echo "Initial snapshot status ${initial_status[*]}" echo "$TOOL: Initial snapshot status ${initial_status[*]}"
echo "Failing merge snapshot status ${status[*]}" echo "$TOOL: Failing merge snapshot status ${status[*]}"
error "ABORTING: Snapshot failed to merge! (Administrator required...)" error "ABORTING: Snapshot failed to merge! (Administrator required...)"
} fi
sync
VDO_INCONSISTENT=
VDO_CONFIG_RESTORE=
"$DMSETUP" remove "$VDO_DM_SNAPSHOT_NAME" || { "$DMSETUP" remove "$VDO_DM_SNAPSHOT_NAME" || {
sleep 1 # sleep and retry once more sleep 1 # sleep and retry once more
"$DMSETUP" remove "$VDO_DM_SNAPSHOT_NAME" || { "$DMSETUP" remove "$VDO_DM_SNAPSHOT_NAME" || {
@ -227,9 +273,9 @@ get_largest_extent_size_() {
for i in 8 16 32 64 128 256 512 1024 2048 4096 ; do for i in 8 16 32 64 128 256 512 1024 2048 4096 ; do
d=$(( $1 / i )) d=$(( $1 / i ))
test $(( d * i )) -eq "$1" || break [ $(( d * i )) -eq "$1" ] || break
d=$(( $2 / i )) d=$(( $2 / i ))
test $(( d * i )) -eq "$2" || break [ $(( d * i )) -eq "$2" ] || break
max=$i max=$i
done done
echo "$max" echo "$max"
@ -244,7 +290,7 @@ detect_lv_() {
DEVICE=${1/#"${DM_DEV_DIR}/"/} DEVICE=${1/#"${DM_DEV_DIR}/"/}
DEVICE=$("$READLINK" $READLINK_E "$DM_DEV_DIR/$DEVICE" || true) DEVICE=$("$READLINK" $READLINK_E "$DM_DEV_DIR/$DEVICE" || true)
test -n "$DEVICE" || error "Readlink cannot access device \"$1\"." [ -n "$DEVICE" ] || error "Readlink cannot access device \"$1\"."
RDEVICE=$DEVICE RDEVICE=$DEVICE
case "$RDEVICE" in case "$RDEVICE" in
# hardcoded /dev since udev does not create these entries elsewhere # hardcoded /dev since udev does not create these entries elsewhere
@ -256,12 +302,12 @@ detect_lv_() {
;; ;;
*) *)
RSTAT=$("$STAT" --format "DEVMAJOR=\$((0x%t)) DEVMINOR=\$((0x%T))" "$RDEVICE" || true) RSTAT=$("$STAT" --format "DEVMAJOR=\$((0x%t)) DEVMINOR=\$((0x%T))" "$RDEVICE" || true)
test -n "$RSTAT" || error "Cannot get major:minor for \"$DEVICE\"." [ -n "$RSTAT" ] || error "Cannot get major:minor for \"$DEVICE\"."
eval "$RSTAT" eval "$RSTAT"
;; ;;
esac esac
test "$DEVMAJOR" != "$(grep device-mapper /proc/devices | cut -f1 -d' ')" && return [ "$DEVMAJOR" != "$(grep device-mapper /proc/devices | cut -f1 -d' ')" ] && return
DEV="$("$DMSETUP" info -c -j "$DEVMAJOR" -m "$DEVMINOR" -o uuid,name --noheadings --nameprefixes --separator ' ')" DEV="$("$DMSETUP" info -c -j "$DEVMAJOR" -m "$DEVMINOR" -o uuid,name --noheadings --nameprefixes --separator ' ')"
case "$DEV" in case "$DEV" in
@ -337,9 +383,10 @@ convert_lv_() {
vg_extent_size=$("$LVM" vgs -o vg_extent_size --units b --nosuffix --noheadings "$VGNAME") vg_extent_size=$("$LVM" vgs -o vg_extent_size --units b --nosuffix --noheadings "$VGNAME")
vg_extent_size=$(( vg_extent_size / 1024 )) vg_extent_size=$(( vg_extent_size / 1024 ))
test "$vg_extent_size" -le "$extent_size" || { [ "$vg_extent_size" -le "$extent_size" ] || {
error "Please vgchange extent_size to at most $extent_size KiB or extend and align virtual size of VDO device on $vg_extent_size KiB before retrying conversion." error "Please vgchange extent_size to at most $extent_size KiB or extend and align virtual size of VDO device on $vg_extent_size KiB before retrying conversion."
} }
verbose "Renaming existing LV to be used as _vdata volume for VDO pool LV." verbose "Renaming existing LV to be used as _vdata volume for VDO pool LV."
dry "$LVM" lvrename $YES $VERB "$VGNAME/$DM_LV_NAME" "$VGNAME/${LVNAME}_vpool" || { dry "$LVM" lvrename $YES $VERB "$VGNAME/$DM_LV_NAME" "$VGNAME/${LVNAME}_vpool" || {
error "Rename of LV \"$VGNAME/$DM_LV_NAME\" failed, while VDO header has been already moved!" error "Rename of LV \"$VGNAME/$DM_LV_NAME\" failed, while VDO header has been already moved!"
@ -367,25 +414,39 @@ convert_non_lv_() {
local output local output
local pvfree local pvfree
if [ "$USE_VDO_DM_SNAPSHOT" = "1" ]; then if [ -n "$USE_VDO_DM_SNAPSHOT" ]; then
dry snapshot_create_ "$DEVICE" dry snapshot_create_ "$DEVICE"
sed "s:$DEVICE:$VDO_DM_SNAPSHOT_DEVICE:" "$TEMPDIR/vdoconf.yml" > "$TEMPDIR/vdo_snap.yml" sed "s|$DEVICE|$VDO_DM_SNAPSHOT_DEVICE|" "$TEMPDIR/vdoconf.yml" > "$TEMPDIR/vdo_snap.yml"
# In case of error in the middle of conversion restore original config file
VDO_CONFIG_RESTORE="$TEMPDIR/vdoconf.yml"
# Let VDO manager operate on snapshot volume # Let VDO manager operate on snapshot volume
VDOCONF="-f $TEMPDIR/vdo_snap.yml" dry cp -a "$TEMPDIR/vdo_snap.yml" "${VDO_CONFIG:-"$DEFAULT_VDO_CONFIG"}"
else
# If error in the following section, report possible problems ahead
VDO_INCONSISTENT=1
fi fi
verbose "Moving VDO header." # In case we operate with snapshot, all lvm2 operation will also run on top of snapshot
local device=${VDO_DM_SNAPSHOT_DEVICE:-$DEVICE}
# Check if there is not already an existing PV header, this would have fail on pvcreate after conversion
"$LVM" pvs --devices "$device" "$device" 2>/dev/null && {
error "Cannot convert volume \"$DEVICE\" with existing PV header."
}
verbose "Moving VDO header on \"$device\"."
output=$(dry "$VDO" convert $VDOCONF $VERB --force --name "$VDONAME" 2>&1) || { output=$(dry "$VDO" convert $VDOCONF $VERB --force --name "$VDONAME" 2>&1) || {
local rc=$?
echo "$output" echo "$output"
error "Failed to convert VDO volume \"$DEVICE\" (exit code $?)." error "Failed to convert VDO volume \"$DEVICE\" (exit code $rc)."
} }
echo "$output" echo "$output"
if [ "$ABORT_AFTER_VDO_CONVERT" != "0" ]; then if [ "$ABORT_AFTER_VDO_CONVERT" != "0" ]; then
verbose "Aborting VDO conversion after moving VDO header, volume is useless!" warn "Aborting VDO conversion after moving VDO header, volume is useless!"
cleanup 0 return 0
fi fi
# Parse result from VDO preparation/conversion tool # Parse result from VDO preparation/conversion tool
@ -410,12 +471,7 @@ convert_non_lv_() {
esac esac
done <<< "$output" done <<< "$output"
# In case we operation with snapshot, all lvm2 operation will also run on top of snapshot dry "$LVM" pvcreate $YES $VERB $FORCE --devices "$device" --dataalignment "$vdo_offset"b "$device"
local devices=${VDO_DM_SNAPSHOT_DEVICE:-$DEVICE}
dry "$LVM" pvcreate $YES --devices "$devices" --dataalignment "$vdo_offset"b "$devices" || {
error "Creation of PV on \"$DEVICE\" failed, while VDO header has been already moved!"
}
# Obtain free space in this new PV # Obtain free space in this new PV
# after 'vdo convert' call there is ~(1-2)M free space at the front of the device # after 'vdo convert' call there is ~(1-2)M free space at the front of the device
@ -432,54 +488,57 @@ convert_non_lv_() {
# To precisely byte-synchronize the size of VDO LV, user can lvresize such VDO LV later. # To precisely byte-synchronize the size of VDO LV, user can lvresize such VDO LV later.
vdo_logicalSizeRounded=$(( ( vdo_logicalSize / extent_size ) * extent_size )) vdo_logicalSizeRounded=$(( ( vdo_logicalSize / extent_size ) * extent_size ))
verbose "Creating VG \"${NAME%/*}\" with extent size $extent_size KiB." verbose "Creating volume group \"$VGNAME\" with the extent size $extent_size KiB."
dry "$LVM" vgcreate $YES $VERB --devices "$devices" -s "${extent_size}k" "$VGNAME" "$devices" || { dry "$LVM" vgcreate $YES $VERB --devices "$device" -s "${extent_size}k" "$VGNAME" "$device"
error "Creation of VG \"$VGNAME\" failed, while VDO header has been already moved!"
}
verbose "Creating VDO pool data LV from all extents in volume group $VGNAME." verbose "Creating VDO pool data LV from all extents in the volume group \"$VGNAME\"."
dry "$LVM" lvcreate -Zn -Wn -an $YES $VERB --devices "$devices" -l100%VG -n "${LVNAME}_vpool" "$VGNAME" "$devices" dry "$LVM" lvcreate -Zn -Wn -an $YES $VERB --devices "$device" -l100%VG -n "${LVNAME}_vpool" "$VGNAME" "$device"
verbose "Converting to VDO pool." verbose "Converting to VDO pool."
dry "$LVM" lvconvert $YES $VERB $FORCE --devices "$devices" --config "$VDO_ALLOCATION_PARAMS" -Zn -V "${vdo_logicalSizeRounded}k" -n "$LVNAME" --type vdo-pool "$VGNAME/${LVNAME}_vpool" dry "$LVM" lvconvert ${USE_VDO_DM_SNAPSHOT:-"$YES"} $VERB $FORCE --devices "$device" --config "$VDO_ALLOCATION_PARAMS" -Zn -V "${vdo_logicalSizeRounded}k" -n "$LVNAME" --type vdo-pool "$VGNAME/${LVNAME}_vpool"
if [ "$vdo_logicalSizeRounded" -lt "$vdo_logicalSize" ]; then if [ "$vdo_logicalSizeRounded" -lt "$vdo_logicalSize" ]; then
# need to extend virtual size to be covering all the converted area # need to extend virtual size to be covering all the converted area
# let lvm2 to round to the proper virtual size of VDO LV # let lvm2 to round to the proper virtual size of VDO LV
dry "$LVM" lvextend $YES $VERB --fs ignore --devices "$devices" -L "$vdo_logicalSize"k "$VGNAME/$LVNAME" dry "$LVM" lvextend $YES $VERB --fs ignore --devices "$device" -L "$vdo_logicalSize"k "$VGNAME/$LVNAME"
fi fi
dry "$LVM" vgchange -an $VERB $FORCE --devices "$devices" "$VGNAME" VDO_INCONSISTENT=
if [ -n "$USE_VDO_DM_SNAPSHOT" ]; then
dry "$LVM" vgchange -an $VERB $FORCE --devices "$device" "$VGNAME"
# Prevent unwanted auto activation when VG is merged
dry "$LVM" vgchange --setautoactivation n $VERB $FORCE --devices "$device" "$VGNAME"
if [ "$USE_VDO_DM_SNAPSHOT" = "1" ]; then
if [ -z "$YES" ]; then if [ -z "$YES" ]; then
PROMPTING=yes PROMPTING=yes
echo "Warning: Do not interrupt merging process once it starts (VDO data may become irrecoverable)!" warn "Do not interrupt merging process once it starts (VDO data may become irrecoverable)!"
echo -n "Do you want to merge converted VDO device \"$DEVICE\" to VDO LV \"$VGNAME/$LVNAME\"? [y|N]: " echo -n "$TOOL: Do you want to merge converted VDO device \"$DEVICE\" to VDO LV \"$VGNAME/$LVNAME\"? [y|N]: "
read -r -n 1 -s ANSWER read -r -n 1 -s ANSWER
case "${ANSWER:0:1}" in case "${ANSWER:0:1}" in
y|Y ) echo "Yes" ;; y|Y ) echo "Yes" ;;
* ) echo "No" ; PROMPTING=""; cleanup 1 ;; * ) echo "No" ; PROMPTING=""; return 1 ;;
esac esac
PROMPTING="" PROMPTING=""
YES="-y" # From now, now prompting YES="-y" # From now, now prompting
fi fi
dry snapshot_merge_ "$DEVICE" dry snapshot_merge_ "$DEVICE"
if [ -e "$TEMPDIR/vdo_snap.yml" ]; then
dry cp "$TEMPDIR/vdo_snap.yml" "$VDO_CONFIG"
elif [ -e "$VDO_CONFIG" ]; then
dry rm -f "$VDO_CONFIG"
fi
verbose "Merging of VDO device finished." verbose "Merging of VDO device finished."
fi fi
output=$(dry "$LVM" pvs "$DEVICE" 2>&1) || { output=$("$LVM" pvs "$DEVICE" 2>&1) || {
if echo "$output" | grep -q "not in devices file" ; then if echo "$output" | grep -q "not in devices file" ; then
verbose "Adding \"$DEVICE\" to devices file." verbose "Adding \"$DEVICE\" to devices file."
dry "$LVM" lvmdevices --adddev "$DEVICE" dry "$LVM" lvmdevices --adddev "$DEVICE"
fi fi
} }
# Restore auto activation for a VG
[ -n "$USE_VDO_DM_SNAPSHOT" ] && dry "$LVM" vgchange --setautoactivation y $VERB $FORCE "$VGNAME"
dry "$LVM" lvchange -ay $VERB $FORCE "$VGNAME/$LVNAME" dry "$LVM" lvchange -ay $VERB $FORCE "$VGNAME/$LVNAME"
} }
@ -500,7 +559,7 @@ convert2lvm_() {
if [ -z "$VGNAME" ] || [ "$VGNAME" = "$LVNAME" ]; then if [ -z "$VGNAME" ] || [ "$VGNAME" = "$LVNAME" ]; then
VGNAME=$DM_VG_NAME VGNAME=$DM_VG_NAME
verbose "Using existing volume group name \"$VGNAME\"." verbose "Using existing volume group name \"$VGNAME\"."
test -n "$LVNAME" || LVNAME=$DM_LV_NAME [ -n "$LVNAME" ] || LVNAME=$DM_LV_NAME
elif [ "$VGNAME" != "$DM_VG_NAME" ]; then elif [ "$VGNAME" != "$DM_VG_NAME" ]; then
error "Volume group name \"$VGNAME\" does not match name \"$DM_VG_NAME\" for VDO device \"$DEVICE\"." error "Volume group name \"$VGNAME\" does not match name \"$DM_VG_NAME\" for VDO device \"$DEVICE\"."
fi fi
@ -510,30 +569,30 @@ convert2lvm_() {
if [ -z "$VGNAME" ] || [ "$VGNAME" = "$LVNAME" ]; then if [ -z "$VGNAME" ] || [ "$VGNAME" = "$LVNAME" ]; then
VGNAME=${DEFAULT_NAME%/*} VGNAME=${DEFAULT_NAME%/*}
# Find largest matching VG name to our 'default' vgname # Find largest matching VG name to our 'default' vgname
LASTVGNAME=$(LC_ALL=C "$LVM" vgs -oname -O-name --noheadings -S name=~"${VGNAME}" | grep -E "${VGNAME}[0-9]? ?" | head -1 || true) LASTVGNAME=$(LC_ALL=C "$LVM" vgs -oname -O-name --noheadings -S name=~"${VGNAME}" | grep -m 1 -E "${VGNAME}[0-9]? ?" || true)
if [ -n "$LASTVGNAME" ]; then if [ -n "$LASTVGNAME" ]; then
LASTVGNAME=${LASTVGNAME#*"${VGNAME}"} LASTVGNAME=${LASTVGNAME#*"${VGNAME}"}
# If the number is becoming too high, try some random number # If the number is becoming too high, try some random number
test "$LASTVGNAME" -gt 99999999 2>/dev/null && LASTVGNAME=$RANDOM [ -n "$LASTVGNAME" ] && [ "$LASTVGNAME" -gt 99999999 ] && LASTVGNAME=$RANDOM
# Generate new unused VG name # Generate new unused VG name
VGNAME="${VGNAME}$(( LASTVGNAME + 1 ))" VGNAME="${VGNAME}$(( LASTVGNAME + 1 ))"
verbose "Selected unused volume group name \"$VGNAME\"." verbose "Selected unused volume group name \"$VGNAME\"."
fi fi
fi fi
# New VG is created, LV name should be always unused. # New VG is created, LV name should be always unused.
test -n "$LVNAME" || LVNAME=${DEFAULT_NAME#*/} [ -n "$LVNAME" ] || LVNAME=${DEFAULT_NAME#*/}
"$LVM" vgs "$VGNAME" >/dev/null 2>&1 && error "Cannot use already existing volume group name \"$VGNAME\"." "$LVM" vgs "$VGNAME" >/dev/null 2>&1 && error "Cannot use already existing volume group name \"$VGNAME\"."
;; ;;
esac esac
verbose "Checked whether device \"$DEVICE\" is already logical volume." verbose "Checked whether device \"$DEVICE\" is already logical volume."
"$MKDIR" -p -m 0000 "$TEMPDIR" || error "Failed to create $TEMPDIR." "$MKDIR" -p -m 0000 "$TEMPDIR" || error "Failed to create \"$TEMPDIR\"."
# TODO: might use directly /etc/vdoconf.yml (avoiding need of 'vdo' manager) # TODO: might use directly /etc/vdoconf.yml (avoiding need of 'vdo' manager)
verbose "Getting YAML VDO configuration." verbose "Getting YAML VDO configuration."
"$VDO" printConfigFile $VDOCONF >"$TEMPDIR/vdoconf.yml" "$VDO" printConfigFile $VDOCONF >"$TEMPDIR/vdoconf.yml"
test -s "$TEMPDIR/vdoconf.yml" || error "Cannot work without VDO configuration" [ -s "$TEMPDIR/vdoconf.yml" ] || error "Cannot work without VDO configuration."
# Check list of devices in VDO configure file for their major:minor # Check list of devices in VDO configure file for their major:minor
# and match with given $DEVICE devmajor:devminor # and match with given $DEVICE devmajor:devminor
@ -542,13 +601,13 @@ convert2lvm_() {
DEV=$("$READLINK" $READLINK_E "$i") || continue DEV=$("$READLINK" $READLINK_E "$i") || continue
RSTAT=$("$STAT" --format "MAJOR=\$((0x%t)) MINOR=\$((0x%T))" "$DEV" 2>/dev/null) || continue RSTAT=$("$STAT" --format "MAJOR=\$((0x%t)) MINOR=\$((0x%T))" "$DEV" 2>/dev/null) || continue
eval "$RSTAT" eval "$RSTAT"
test "$MAJOR" = "$DEVMAJOR" && test "$MINOR" = "$DEVMINOR" && { if [ "$MAJOR" = "$DEVMAJOR" ] && [ "$MINOR" = "$DEVMINOR" ]; then
test -z "$FOUND" || error "VDO configuration contains duplicate entries $FOUND and $i" [ -z "$FOUND" ] || error "VDO configuration contains duplicate entries $FOUND and $i."
FOUND=$i FOUND=$i
} fi
done done
test -n "$FOUND" || error "Can't find matching device in VDO configuration file." [ -n "$FOUND" ] || error "Can't find matching device in VDO configuration file."
verbose "Found matching device $FOUND $MAJOR:$MINOR." verbose "Found matching device $FOUND $MAJOR:$MINOR."
VDONAME=$(awk -v DNAME="$FOUND" '/.*VDOService$/ {VNAME=substr($1, 0, length($1) - 1)} /[[:space:]]*device:/ { if ($2 ~ DNAME) {print VNAME}}' "$TEMPDIR/vdoconf.yml") VDONAME=$(awk -v DNAME="$FOUND" '/.*VDOService$/ {VNAME=substr($1, 0, length($1) - 1)} /[[:space:]]*device:/ { if ($2 ~ DNAME) {print VNAME}}' "$TEMPDIR/vdoconf.yml")
@ -559,7 +618,7 @@ convert2lvm_() {
case "$DM_OPEN" in case "$DM_OPEN" in
Device*) ;; # no devices Device*) ;; # no devices
*) eval "$DM_OPEN" *) eval "$DM_OPEN"
test "${DM_OPEN:-0}" -eq 0 || error "Cannot convert in use VDO volume \"$VDONAME\"!" [ "${DM_OPEN:-0}" -eq 0 ] || error "Cannot convert in use VDO volume \"$VDONAME\"!"
;; ;;
esac esac
@ -601,20 +660,20 @@ EOF
dry "$VDO" stop $VDOCONF --name "$VDONAME" $VERB dry "$VDO" stop $VDOCONF --name "$VDONAME" $VERB
# If user has not provided '--yes', prompt before conversion # If user has not provided '--yes', prompt before conversion
if [ -z "$YES" ] && [ "$USE_VDO_DM_SNAPSHOT" != "1" ]; then if [ -z "$YES" ] && [ -z "$USE_VDO_DM_SNAPSHOT" ]; then
PROMPTING=yes PROMPTING=yes
echo -n "Convert VDO device \"$DEVICE\" to VDO LV \"$VGNAME/$LVNAME\"? [y|N]: " echo -n "$TOOL: Convert VDO device \"$DEVICE\" to VDO LV \"$VGNAME/$LVNAME\"? [y|N]: "
read -r -n 1 -s ANSWER read -r -n 1 -s ANSWER
case "${ANSWER:0:1}" in case "${ANSWER:0:1}" in
y|Y ) echo "Yes" ;; y|Y ) echo "Yes" ;;
* ) echo "No" ; PROMPTING=""; cleanup 1 ;; * ) echo "No" ; PROMPTING=""; return 1 ;;
esac esac
PROMPTING="" PROMPTING=""
YES="-y" # From now, no prompting YES="-y" # From now, no prompting
fi fi
# Make a backup of the existing VDO yaml configuration file # Make a backup of the existing VDO yaml configuration file
test -e "$VDO_CONFIG" && dry cp -a "$VDO_CONFIG" "${VDO_CONFIG}.backup" [ -e "$VDO_CONFIG" ] && dry cp -a "$VDO_CONFIG" "${VDO_CONFIG}.backup"
DEVICE=$FOUND DEVICE=$FOUND
case "$DM_UUID" in case "$DM_UUID" in
@ -627,9 +686,9 @@ EOF
# start point of this script # start point of this script
# - parsing parameters # - parsing parameters
############################# #############################
trap "cleanup 2" 2 trap "cleanup" "${SIGNALS[@]}"
test "$#" -eq 0 && tool_usage [ "$#" -eq 0 ] && tool_usage
while [ "$#" -ne 0 ] while [ "$#" -ne 0 ]
do do
@ -640,19 +699,17 @@ do
"-n"|"--name" ) shift; NAME=$1 ;; "-n"|"--name" ) shift; NAME=$1 ;;
"-v"|"--verbose") VERB="--verbose" ;; "-v"|"--verbose") VERB="--verbose" ;;
"-y"|"--yes" ) YES="-y" ;; "-y"|"--yes" ) YES="-y" ;;
"--abort-after-vdo-convert" ) ABORT_AFTER_VDO_CONVERT=1; USE_VDO_DM_SNAPSHOT=0 ;; # For testing only "--abort-after-vdo-convert"|"--abortaftervdoconvert" ) ABORT_AFTER_VDO_CONVERT=1; USE_VDO_DM_SNAPSHOT= ;; # For testing only
"--dry-run" ) DRY="1" ; VERB="-v" ;; "--dry-run"|"--dryrun" ) DRY="1" ; VERB="-v" ;;
"--no-snapshot" ) USE_VDO_DM_SNAPSHOT=0 ;; "--no-snapshot"|"--nosnapshot" ) USE_VDO_DM_SNAPSHOT= ;;
"--uuid-prefix" ) shift; DM_UUID_PREFIX=$1 ;; # For testing only "--uuid-prefix"|"--uuidprefix" ) shift; DM_UUID_PREFIX=$1 ;; # For testing only
"--vdo-config" ) shift; VDO_CONFIG=$1 ; VDOCONF="-f $VDO_CONFIG" ;; "--vdo-config"|"--vdoconfig" ) shift; VDO_CONFIG=$1 ; VDOCONF="-f $VDO_CONFIG" ;;
"-*") error "Wrong argument \"$1\". (see: $TOOL --help)" ;; -* ) error "Wrong argument \"$1\". (see: $TOOL --help)" ;;
*) DEVICE=$1 ;; # device name does not start with '-' *) DEVICE=$1 ;; # device name does not start with '-'
esac esac
shift shift
done done
test -n "$DEVICE" || error "Device name is not specified. (see: $TOOL --help)" [ -n "$DEVICE" ] || error "Device name is not specified. (see: $TOOL --help)"
convert2lvm_ convert2lvm_
cleanup 0