2021-07-09 15:44:07 +03:00
#!/bin/bash
#
2023-05-11 21:07:37 +03:00
# Copyright (C) 2021-2023 Red Hat, Inc. All rights reserved.
2021-07-09 15:44:07 +03:00
#
# This file is part of LVM2.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License v.2.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Author: Zdenek Kabelac <zkabelac at redhat.com>
#
2021-08-23 15:02:42 +03:00
# Script for importing VDO volumes to lvm2 managed VDO LVs
2021-07-09 15:44:07 +03:00
#
# Needed utilities:
# lvm, dmsetup,
2021-08-23 15:02:42 +03:00
# vdo,
2023-05-11 21:07:37 +03:00
# grep, awk, sed, blockdev, readlink, stat, mkdir, truncate
2021-07-09 15:44:07 +03:00
#
# Conversion is using 'vdo convert' support from VDO manager to move
# existing VDO header by 2M which makes space to place in PV header
# and VG metadata area, and then create VDOPOOL LV and VDO LV in such VG.
#
set -euE -o pipefail
2021-08-26 17:53:33 +03:00
TOOL = lvm_import_vdo
2023-05-11 21:07:37 +03:00
IMPORT_NAME = " VDO_ ${ TOOL } _ ${ RANDOM } $$ "
test ${# IMPORT_NAME } -lt 100 || error " Random name \" $IMPORT_NAME \" is too long! "
TEMPDIR = " ${ TMPDIR :- /tmp } / $IMPORT_NAME "
2021-07-09 15:44:07 +03:00
_SAVEPATH = $PATH
PATH = " /sbin:/usr/sbin:/bin:/usr/sbin: $PATH "
2023-09-08 18:19:39 +03:00
# Set of trapped signals
declare -a SIGNALS = ( "HUP" "INT" "QUIT" "ABRT" "TERM" "EXIT" )
2021-07-09 15:44:07 +03:00
# user may override lvm location by setting LVM_BINARY
LVM = ${ LVM_BINARY :- lvm }
VDO = ${ VDO_BINARY :- vdo }
BLOCKDEV = "blockdev"
2023-05-11 21:07:37 +03:00
LOSETUP = "losetup"
2021-07-09 15:44:07 +03:00
READLINK = "readlink"
READLINK_E = "-e"
2021-09-01 16:46:04 +03:00
STAT = "stat"
2021-07-09 15:44:07 +03:00
MKDIR = "mkdir"
2023-05-11 21:07:37 +03:00
TRUNCATE = "truncate"
2021-08-31 21:52:26 +03:00
DMSETUP = "dmsetup"
2021-07-09 15:44:07 +03:00
DM_DEV_DIR = " ${ DM_DEV_DIR :- /dev } "
2023-05-11 21:07:37 +03:00
DM_UUID_PREFIX = " ${ DM_UUID_PREFIX :- } "
DM_VG_NAME =
DM_LV_NAME =
2023-09-08 18:19:39 +03:00
DEFAULT_VDO_CONFIG = "/etc/vdoconf.yml" # Default location of vdo's manager config file
2023-06-29 14:05:55 +03:00
VDO_CONFIG = ${ VDO_CONFIG :- } # can be overridden with --vdo-config
2023-09-08 18:19:39 +03:00
VDO_CONFIG_RESTORE =
2023-05-11 21:07:37 +03:00
VDOCONF =
test -n " $VDO_CONFIG " && VDOCONF = " -f $VDO_CONFIG "
DEVICE =
VGNAME =
LVNAME =
2021-08-31 21:52:26 +03:00
DEVMAJOR = 0
DEVMINOR = 0
2023-09-08 18:19:39 +03:00
PROMPTING =
USE_VDO_DM_SNAPSHOT = "--yes"
2023-05-11 21:07:37 +03:00
VDO_DM_SNAPSHOT_NAME =
VDO_DM_SNAPSHOT_DEVICE =
VDO_SNAPSHOT_LOOP =
2023-09-08 18:19:39 +03:00
VDO_INCONSISTENT =
2021-08-31 21:52:26 +03:00
2021-07-09 15:44:07 +03:00
DRY = 0
2023-09-08 18:19:39 +03:00
VERB =
FORCE =
YES =
2023-05-11 21:07:37 +03:00
ABORT_AFTER_VDO_CONVERT = 0
VDO_ALLOCATION_PARAMS =
2021-07-09 15:44:07 +03:00
# default name for converted VG and its VDO LV
2021-09-06 16:06:32 +03:00
DEFAULT_NAME = "vdovg/vdolvol"
NAME = ""
2021-07-09 15:44:07 +03:00
2023-09-08 18:19:39 +03:00
# 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 =
2021-07-09 15:44:07 +03:00
# help message
tool_usage( ) {
echo " ${ TOOL } : Utility to convert VDO volume to VDO LV. "
echo
echo " ${ TOOL } [options] <vdo_device_path> "
echo
echo " Options:"
echo " -f | --force Bypass sanity checks"
echo " -h | --help Show this help message"
echo " -n | --name Specifies VG/LV name for converted VDO volume"
echo " -v | --verbose Be verbose"
echo " -y | --yes Answer \"yes\" at any prompts"
2021-09-17 17:49:17 +03:00
echo " --dry-run Print verbosely commands without running them"
2023-05-11 21:07:37 +03:00
echo " --no-snapshot Do not use snapshot for converted VDO device"
echo " --uuid-prefix Prefix for DM snapshot uuid"
echo " --vdo-config Configuration file for VDO manager"
2021-07-09 15:44:07 +03:00
exit
}
verbose( ) {
test -z " $VERB " || echo " $TOOL : " " $@ "
}
# Support multi-line error messages
error( ) {
for i in " $@ " ; do
echo " $TOOL : $i " >& 2
done
2023-09-08 18:19:39 +03:00
return 1
}
warn( ) {
echo " $TOOL : WARNING: $i " >& 2
2021-07-09 15:44:07 +03:00
}
dry( ) {
if [ " $DRY " -ne 0 ] ; then
verbose "Dry execution" " $@ "
return 0
fi
verbose "Executing" " $@ "
" $@ "
}
cleanup( ) {
2023-09-08 18:19:39 +03:00
RC = $? # Return code + 128 of the last command eg INT=2 + 128 -> 130
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
2023-09-02 22:25:20 +03:00
for i in { 1..20} ; do
2023-09-08 18:19:39 +03:00
[ " $( dry " $DMSETUP " info --noheading -co open " $VDO_DM_SNAPSHOT_NAME " ) " = "0" ] && break
2023-09-02 22:25:20 +03:00
sleep .1
done
2023-09-08 18:19:39 +03:00
dry " $DMSETUP " remove " $VDO_DM_SNAPSHOT_NAME " & >/dev/null || true
fi
[ -n " $VDO_SNAPSHOT_LOOP " ] && { dry " $LOSETUP " -d " $VDO_SNAPSHOT_LOOP " || true ; }
[ -z " $VDO_INCONSISTENT " ] || echo " $TOOL : VDO volume import process exited unexpectedly! " >& 2
2023-05-11 21:07:37 +03:00
rm -rf " $TEMPDIR " || true
2023-09-08 18:19:39 +03:00
exit " $RC "
2021-07-09 15:44:07 +03:00
}
2023-05-11 21:07:37 +03:00
# Create snapshot target like for persistent snapshot with 16KiB chunksize
snapshot_target_line_( ) {
echo " 0 $( " $BLOCKDEV " --getsize " $1 " ) snapshot ${ 3 :- } $1 $2 P 32 "
}
snapshot_create_( ) {
VDO_DM_SNAPSHOT_NAME = " ${ IMPORT_NAME } _snap "
local file = " $TEMPDIR / $VDO_DM_SNAPSHOT_NAME "
# TODO: maybe use ramdisk via 'brd' device ?)
" $TRUNCATE " -s 20M " $file "
VDO_SNAPSHOT_LOOP = $( " $LOSETUP " -f --show " $file " )
2023-09-08 18:19:39 +03:00
" $DMSETUP " create " $VDO_DM_SNAPSHOT_NAME " -u " ${ DM_UUID_PREFIX } ${ VDO_DM_SNAPSHOT_NAME } -priv " --table " $( snapshot_target_line_ " $1 " " $VDO_SNAPSHOT_LOOP " ) "
2023-05-11 21:07:37 +03:00
VDO_DM_SNAPSHOT_DEVICE = " $DM_DEV_DIR /mapper/ $VDO_DM_SNAPSHOT_NAME "
verbose " Snapshot of VDO device $1 created: $VDO_DM_SNAPSHOT_DEVICE . "
}
snapshot_merge_( ) {
local status
2023-06-29 14:05:55 +03:00
local initial_status
2023-05-11 21:07:37 +03:00
initial_status = ( $( " $DMSETUP " status " $VDO_DM_SNAPSHOT_NAME " ) )
" $DMSETUP " reload " $VDO_DM_SNAPSHOT_NAME " --table " $( snapshot_target_line_ " $1 " " $VDO_SNAPSHOT_LOOP " -merge) "
" $DMSETUP " suspend " $VDO_DM_SNAPSHOT_NAME " || {
error "ABORTING: Failed to initialize snapshot merge! Origin volume is unchanged."
}
2023-09-13 14:20:31 +03:00
verbose " Merging converted VDO volume \" $VDO_DM_SNAPSHOT_NAME \". "
2023-09-08 18:19:39 +03:00
VDO_INCONSISTENT = 1
2023-05-11 21:07:37 +03:00
# Running merging
" $DMSETUP " resume " $VDO_DM_SNAPSHOT_NAME "
#du -h "$TEMPDIR/$VDO_DM_SNAPSHOT_NAME"
# Loop for a while, till the snapshot is merged.
2023-06-29 14:05:55 +03:00
# Should be nearly instantaneous.
2023-09-08 18:19:39 +03:00
# FIXME: Recovery when something prevents merging is hard
2023-05-11 21:07:37 +03:00
for i in $( seq 1 20) ; do
status = ( $( " $DMSETUP " status " $VDO_DM_SNAPSHOT_NAME " ) )
# Check if merging is finished
2023-09-08 18:19:39 +03:00
[ " ${ status [3]%/* } " = " ${ status [4] } " ] && break
2023-05-11 21:07:37 +03:00
# Wait a bit and retry
sleep .2
done
2023-09-08 18:19:39 +03:00
if [ " ${ status [3]%/* } " != " ${ status [4] } " ] ; then
2023-05-11 21:07:37 +03:00
# FIXME: Now what shall we do ??? Help....
2023-09-08 18:19:39 +03:00
# Keep snapshot in DM table for possible analysis...
2023-05-11 21:07:37 +03:00
VDO_DM_SNAPSHOT_NAME =
VDO_SNAPSHOT_LOOP =
2023-09-08 18:19:39 +03:00
echo " $TOOL : Initial snapshot status ${ initial_status [*] } "
echo " $TOOL : Failing merge snapshot status ${ status [*] } "
2023-05-11 21:07:37 +03:00
error "ABORTING: Snapshot failed to merge! (Administrator required...)"
2023-09-08 18:19:39 +03:00
fi
VDO_INCONSISTENT =
VDO_CONFIG_RESTORE =
2023-09-13 14:20:31 +03:00
verbose " Converted VDO volume is merged to \" $1 \". "
2023-05-11 21:07:37 +03:00
" $DMSETUP " remove " $VDO_DM_SNAPSHOT_NAME " || {
sleep 1 # sleep and retry once more
" $DMSETUP " remove " $VDO_DM_SNAPSHOT_NAME " || {
error " ABORTING: Cannot remove snapshot $VDO_DM_SNAPSHOT_NAME ! (check volume autoactivation...) "
}
}
2023-09-13 14:20:31 +03:00
2023-05-11 21:07:37 +03:00
VDO_DM_SNAPSHOT_NAME =
" $LOSETUP " -d " $VDO_SNAPSHOT_LOOP "
VDO_SNAPSHOT_LOOP =
}
2021-07-09 15:44:07 +03:00
get_enabled_value_( ) {
case " $1 " in
enabled) echo "1" ; ;
*) echo "0" ; ;
esac
}
get_kb_size_with_unit_( ) {
case " $1 " in
*[ kK] ) echo $(( ${ 1 %[kK] } )) ; ;
*[ mM] ) echo $(( ${ 1 %[mM] } * 1024 )) ; ;
*[ gG] ) echo $(( ${ 1 %[gG] } * 1024 * 1024 )) ; ;
*[ tT] ) echo $(( ${ 1 %[tT] } * 1024 * 1024 * 1024 )) ; ;
*[ pP] ) echo $(( ${ 1 %[pP] } * 1024 * 1024 * 1024 * 1024 )) ; ;
esac
}
# Figure out largest possible extent size usable for VG
# $1 physical size
# $2 logical size
get_largest_extent_size_( ) {
local max = 4
local i
local d
for i in 8 16 32 64 128 256 512 1024 2048 4096 ; do
d = $(( $1 / i ))
2023-09-08 18:19:39 +03:00
[ $(( d * i )) -eq " $1 " ] || break
2021-07-09 15:44:07 +03:00
d = $(( $2 / i ))
2023-09-08 18:19:39 +03:00
[ $(( d * i )) -eq " $2 " ] || break
2021-07-09 15:44:07 +03:00
max = $i
done
echo " $max "
}
# detect LV on the given device
2023-06-29 14:05:55 +03:00
# deference device name if it is symbolic link
2021-07-09 15:44:07 +03:00
detect_lv_( ) {
local DEVICE = $1
local SYSVOLUME
local MAJORMINOR
DEVICE = ${ 1 /# " ${ DM_DEV_DIR } / " / }
2021-09-01 16:46:04 +03:00
DEVICE = $( " $READLINK " $READLINK_E " $DM_DEV_DIR / $DEVICE " || true )
2023-09-08 18:19:39 +03:00
[ -n " $DEVICE " ] || error " Readlink cannot access device \" $1 \". "
2021-07-09 15:44:07 +03:00
RDEVICE = $DEVICE
case " $RDEVICE " in
# hardcoded /dev since udev does not create these entries elsewhere
/dev/dm-[ 0-9] *)
read -r <" /sys/block/ ${ RDEVICE #/dev/ } /dm/name " SYSVOLUME 2>& 1 && DEVICE = " $DM_DEV_DIR /mapper/ $SYSVOLUME "
read -r <" /sys/block/ ${ RDEVICE #/dev/ } /dev " MAJORMINOR 2>& 1 || error " Cannot get major:minor for \" $DEVICE \". "
2021-08-31 21:52:26 +03:00
DEVMAJOR = ${ MAJORMINOR %% : * }
DEVMINOR = ${ MAJORMINOR ##* : }
2021-07-09 15:44:07 +03:00
; ;
*)
2021-09-01 16:46:04 +03:00
RSTAT = $( " $STAT " --format "DEVMAJOR=\$((0x%t)) DEVMINOR=\$((0x%T))" " $RDEVICE " || true )
2023-09-08 18:19:39 +03:00
[ -n " $RSTAT " ] || error " Cannot get major:minor for \" $DEVICE \". "
2021-09-01 16:46:04 +03:00
eval " $RSTAT "
2021-07-09 15:44:07 +03:00
; ;
esac
2023-09-08 18:19:39 +03:00
[ " $DEVMAJOR " != " $( grep device-mapper /proc/devices | cut -f1 -d' ' ) " ] && return
2021-09-06 16:06:32 +03:00
DEV = " $( " $DMSETUP " info -c -j " $DEVMAJOR " -m " $DEVMINOR " -o uuid,name --noheadings --nameprefixes --separator ' ' ) "
2021-08-31 21:52:26 +03:00
case " $DEV " in
Device*) ; ; # no devices
*) eval " $DEV " ; ;
esac
2021-07-09 15:44:07 +03:00
}
# parse yaml config files into 'prefix_yaml_part_names=("value")' strings
parse_yaml_( ) {
local yaml_file = $1
local prefix = $2
local s
local w
local fs
s = '[[:space:]]*'
w = '[a-zA-Z0-9_.-]*'
fs = " $( echo @| tr @ '\034' ) "
(
sed -ne '/^--/s|--||g; s|\"|\\\"|g; s/[[:space:]]*$//g;' \
-e 's/\$/\\\$/g' \
-e "/#.*[\"\']/!s| #.*||g; /^#/s|#.*||g;" \
-e " s|^\( $s \)\( $w \) $s : $s \"\(.*\)\" $s \$|\1 $fs \2 $fs \3|p " \
-e " s|^\( $s \)\( $w \) ${ s } [:-] $s \(.*\) $s \$|\1 $fs \2 $fs \3|p " |
awk -F" $fs " ' {
indent = length( $1 ) /2;
if ( length( $2 ) = = 0) { conj[ indent] = "+" ; } else { conj[ indent] = "" ; }
vname[ indent] = $2 ;
for ( i in vname) { if ( i > indent) { delete vname[ i] } }
if ( length( $3 ) > 0) {
vn = "" ; for ( i = 0; i<indent; i++) { vn = ( vn) ( vname[ i] ) ( "_" ) }
printf( "%s%s%s%s=(\"%s\")\n" , "'" $prefix "'" ,vn, $2 , conj[ indent-1] , $3 ) ;
}
} ' |
sed -e 's/_=/+=/g' |
awk ' BEGIN {
FS = "=" ;
OFS = "="
}
/( -| \. ) .*= / {
gsub( "-|\\." , "_" , $1 )
}
{ print } '
) < " $yaml_file "
}
2023-05-11 21:07:37 +03:00
#
# Convert VDO volume on LV to VDOPool within this VG
#
# This conversion requires the size of VDO virtual volume has to be expressed in the VG's extent size.
# Currently this enforces a user to reduce the VG extent size to the smaller size (up to 4KiB).
#
# TODO: We may eventually relax this condition just like we are doing rounding for convert_non_lv_()
2023-06-29 14:05:55 +03:00
# Let's if there would be any singly user requiring this feature.
2023-05-11 21:07:37 +03:00
# It may allow to better use larger VDO volume size (in TiB ranges).
#
convert_lv_( ) {
local vdo_logicalSize = $1
local extent_size
local pvfree
pvfree = $( " $LVM " lvs -o size --units b --nosuffix --noheadings " $DM_VG_NAME / $DM_LV_NAME " )
pvfree = $(( pvfree / 1024 )) # to KiB
# select largest possible extent size that can exactly express both sizes
extent_size = $( get_largest_extent_size_ " $pvfree " " $vdo_logicalSize " )
# validate existing VG extent_size can express virtual VDO size
vg_extent_size = $( " $LVM " vgs -o vg_extent_size --units b --nosuffix --noheadings " $VGNAME " )
vg_extent_size = $(( vg_extent_size / 1024 ))
2023-09-08 18:19:39 +03:00
[ " $vg_extent_size " -le " $extent_size " ] || {
2023-05-11 21:07:37 +03:00
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. "
}
2023-09-08 18:19:39 +03:00
2023-05-11 21:07:37 +03:00
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 " || {
error " Rename of LV \" $VGNAME / $DM_LV_NAME \" failed, while VDO header has been already moved! "
}
verbose "Converting to VDO pool."
dry " $LVM " lvconvert $YES $VERB $FORCE --config " $VDO_ALLOCATION_PARAMS " -Zn -V " ${ vdo_logicalSize } k " -n " $LVNAME " --type vdo-pool " $VGNAME / ${ LVNAME } _vpool "
verbose "Removing now unused VDO entry from VDO configuration."
dry " $VDO " remove $VDOCONF $VERB --force --name " $VDONAME "
}
#
# Convert VDO volume on a device to VG with VDOPool LV
#
# Convert device with the use of snapshot on top of original VDO volume (can be optionally disabled)
2023-06-29 14:05:55 +03:00
# Once the whole conversion is finished, snapshot is merged (During the short period time of merging
2023-05-11 21:07:37 +03:00
# user must ensure there will be no power-off!)
#
# For best use the latest version of vdoprepareforlvm tool is required.
convert_non_lv_( ) {
local vdo_logicalSize = $1
2023-06-23 19:01:32 +03:00
local vdo_logicalSizeRounded
2023-05-11 21:07:37 +03:00
local extent_size
local output
local pvfree
2023-09-08 18:19:39 +03:00
if [ -n " $USE_VDO_DM_SNAPSHOT " ] ; then
2023-05-11 21:07:37 +03:00
dry snapshot_create_ " $DEVICE "
2023-09-08 18:19:39 +03:00
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 "
2023-05-11 21:07:37 +03:00
# Let VDO manager operate on snapshot volume
2023-09-08 18:19:39 +03:00
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
2023-05-11 21:07:37 +03:00
fi
2023-09-08 18:19:39 +03:00
# 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 \". "
2023-09-02 22:25:20 +03:00
output = $( dry " $VDO " convert $VDOCONF $VERB --force --name " $VDONAME " 2>& 1) || {
2023-09-08 18:19:39 +03:00
local rc = $?
2023-09-02 22:25:20 +03:00
echo " $output "
2023-09-08 18:19:39 +03:00
error " Failed to convert VDO volume \" $DEVICE \" (exit code $rc ). "
2023-09-02 22:25:20 +03:00
}
echo " $output "
2023-05-11 21:07:37 +03:00
2023-09-08 18:19:39 +03:00
if [ " $ABORT_AFTER_VDO_CONVERT " != "0" ] ; then
warn "Aborting VDO conversion after moving VDO header, volume is useless!"
return 0
2023-05-11 21:07:37 +03:00
fi
# Parse result from VDO preparation/conversion tool
2023-06-29 14:05:55 +03:00
# New version of the tool provides output with alignment and offset
2023-05-11 21:07:37 +03:00
local vdo_length = 0
local vdo_aligned = 0
local vdo_offset = 0
local vdo_non_converted = 0
while IFS = read -r line ; do
2023-06-29 14:06:18 +03:00
# trim leading spaces
case " $( echo $line ) " in
2023-05-11 21:07:37 +03:00
"Non converted" *) vdo_non_converted = 1 ; ;
"Length" *) vdo_length = ${ line ##* = } ; ;
"Conversion completed" *)
vdo_aligned = ${ line ##*aligned on }
vdo_aligned = ${ vdo_aligned %%[!0-9]* }
vdo_offset = ${ line ##*offset }
# backward compatibility with report from older version
vdo_offset = ${ vdo_offset ##*by }
vdo_offset = ${ vdo_offset %%[!0-9]* }
; ;
esac
done <<< " $output "
2023-09-08 18:19:39 +03:00
dry " $LVM " pvcreate $YES $VERB $FORCE --devices " $device " --dataalignment " $vdo_offset " b " $device "
2023-05-11 21:07:37 +03:00
# Obtain free space in this new PV
# after 'vdo convert' call there is ~(1-2)M free space at the front of the device
pvfree = $( " $BLOCKDEV " --getsize64 " $DEVICE " )
pvfree = $(( ( pvfree - vdo_offset ) / 1024 )) # to KiB
if [ -n " $vdo_aligned " ] && [ " $vdo_aligned " != "0" ] ; then
extent_size = $(( vdo_aligned / 1024 ))
else
extent_size = $( get_largest_extent_size_ " $pvfree " " $vdo_logicalSize " )
fi
# Round virtual size to the LOWER size expressed in extent units.
# lvm is parsing VDO metadata and can read real full size and use it instead of this smaller value.
# To precisely byte-synchronize the size of VDO LV, user can lvresize such VDO LV later.
2023-06-23 19:01:32 +03:00
vdo_logicalSizeRounded = $(( ( vdo_logicalSize / extent_size ) * extent_size ))
2023-05-11 21:07:37 +03:00
2023-09-08 18:19:39 +03:00
verbose " Creating volume group \" $VGNAME \" with the extent size $extent_size KiB. "
dry " $LVM " vgcreate $YES $VERB --devices " $device " -s " ${ extent_size } k " " $VGNAME " " $device "
2023-05-11 21:07:37 +03:00
2023-09-08 18:19:39 +03:00
verbose " Creating VDO pool data LV from all extents in the volume group \" $VGNAME \". "
dry " $LVM " lvcreate -Zn -Wn -an $YES $VERB --devices " $device " -l100%VG -n " ${ LVNAME } _vpool " " $VGNAME " " $device "
2023-05-11 21:07:37 +03:00
verbose "Converting to VDO pool."
2023-09-08 18:19:39 +03:00
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
2023-06-29 14:05:55 +03:00
# need to extend virtual size to be covering all the converted area
2023-06-23 19:01:32 +03:00
# let lvm2 to round to the proper virtual size of VDO LV
2023-09-13 14:20:31 +03:00
dry " $LVM " lvextend $YES $VERB --devices " $device " -L " $vdo_logicalSize " k " $VGNAME / $LVNAME "
2023-06-23 19:01:32 +03:00
fi
2023-05-11 21:07:37 +03:00
2023-09-08 18:19:39 +03:00
VDO_INCONSISTENT =
2023-09-13 14:20:31 +03:00
[ -z " $USE_VDO_DM_SNAPSHOT " ] && return # no-snapshot case finished
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 [ -z " $YES " ] ; then
PROMPTING = yes
warn "Do not interrupt merging process once it starts (VDO data may become irrecoverable)!"
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
case " ${ ANSWER : 0 : 1 } " in
y| Y ) echo "Yes" ; ;
* ) echo "No" ; PROMPTING = "" ; return 1 ; ;
esac
PROMPTING = ""
YES = "-y" # From now, now prompting
2023-05-11 21:07:37 +03:00
fi
2023-09-13 14:20:31 +03:00
dry snapshot_merge_ " $DEVICE "
# For systems using devicesfile add 'merged' PV into system.devices.
2023-09-14 00:13:27 +03:00
# Bypassing use of --valuesonly to keep compatibility with older lvm.
local usedev = $( " $LVM " lvmconfig --typeconfig full devices/use_devicesfile || true )
2023-09-14 11:01:43 +03:00
[ " ${ usedev #*= } " = "1" ] && dry " $LVM " lvmdevices --adddev " $DEVICE "
2023-09-02 22:25:20 +03:00
2023-09-08 18:19:39 +03:00
# Restore auto activation for a VG
2023-09-13 14:20:31 +03:00
dry " $LVM " vgchange --setautoactivation y $VERB $FORCE " $VGNAME "
2023-09-08 18:19:39 +03:00
2023-05-11 21:07:37 +03:00
dry " $LVM " lvchange -ay $VERB $FORCE " $VGNAME / $LVNAME "
}
# Convert existing VDO volume into lvm2 volume
2021-07-09 15:44:07 +03:00
convert2lvm_( ) {
local VDONAME
local TRVDONAME
2021-08-31 21:52:26 +03:00
local FOUND = ""
local MAJOR = 0
local MINOR = 0
2021-07-09 15:44:07 +03:00
2023-05-11 21:07:37 +03:00
VGNAME = ${ NAME %/* }
LVNAME = ${ NAME #*/ }
2021-07-09 15:44:07 +03:00
DM_UUID = ""
detect_lv_ " $DEVICE "
case " $DM_UUID " in
2021-08-31 21:52:26 +03:00
LVM-*) eval " $( " $DMSETUP " splitname --nameprefixes --noheadings --separator ' ' " $DM_NAME " ) "
2023-09-08 18:19:39 +03:00
if [ -z " $VGNAME " ] || [ " $VGNAME " = " $LVNAME " ] ; then
2021-07-09 15:44:07 +03:00
VGNAME = $DM_VG_NAME
2023-05-11 21:07:37 +03:00
verbose " Using existing volume group name \" $VGNAME \". "
2023-09-08 18:19:39 +03:00
[ -n " $LVNAME " ] || LVNAME = $DM_LV_NAME
2023-05-11 21:07:37 +03:00
elif [ " $VGNAME " != " $DM_VG_NAME " ] ; then
2021-09-06 16:06:32 +03:00
error " Volume group name \" $VGNAME \" does not match name \" $DM_VG_NAME \" for VDO device \" $DEVICE \". "
2021-07-09 15:44:07 +03:00
fi
; ;
2023-05-11 21:07:37 +03:00
*)
2021-09-06 16:06:32 +03:00
# Check if we need to generate unused $VGNANE
2023-09-08 18:19:39 +03:00
if [ -z " $VGNAME " ] || [ " $VGNAME " = " $LVNAME " ] ; then
2021-09-06 16:06:32 +03:00
VGNAME = ${ DEFAULT_NAME %/* }
# Find largest matching VG name to our 'default' vgname
2023-09-08 18:19:39 +03:00
LASTVGNAME = $( LC_ALL = C " $LVM " vgs -oname -O-name --noheadings -S name = ~" ${ VGNAME } " | grep -m 1 -E " ${ VGNAME } [0-9]? ? " || true )
2023-05-11 21:07:37 +03:00
if [ -n " $LASTVGNAME " ] ; then
2023-02-09 18:04:54 +03:00
LASTVGNAME = ${ LASTVGNAME #* " ${ VGNAME } " }
2021-09-06 16:06:32 +03:00
# If the number is becoming too high, try some random number
2023-09-08 18:19:39 +03:00
[ -n " $LASTVGNAME " ] && [ " $LASTVGNAME " -gt 99999999 ] && LASTVGNAME = $RANDOM
2021-09-06 16:06:32 +03:00
# Generate new unused VG name
2023-02-09 18:04:54 +03:00
VGNAME = " ${ VGNAME } $(( LASTVGNAME + 1 )) "
2023-05-11 21:07:37 +03:00
verbose " Selected unused volume group name \" $VGNAME \". "
2021-09-06 16:06:32 +03:00
fi
fi
# New VG is created, LV name should be always unused.
2023-09-08 18:19:39 +03:00
[ -n " $LVNAME " ] || LVNAME = ${ DEFAULT_NAME #*/ }
2021-08-31 21:52:26 +03:00
" $LVM " vgs " $VGNAME " >/dev/null 2>& 1 && error " Cannot use already existing volume group name \" $VGNAME \". "
2021-07-09 15:44:07 +03:00
; ;
esac
2023-05-11 21:07:37 +03:00
verbose " Checked whether device \" $DEVICE \" is already logical volume. "
2021-07-09 15:44:07 +03:00
2023-09-08 18:19:39 +03:00
" $MKDIR " -p -m 0000 " $TEMPDIR " || error " Failed to create \" $TEMPDIR \". "
2021-07-09 15:44:07 +03:00
2023-06-29 14:05:55 +03:00
# TODO: might use directly /etc/vdoconf.yml (avoiding need of 'vdo' manager)
2021-07-09 15:44:07 +03:00
verbose "Getting YAML VDO configuration."
" $VDO " printConfigFile $VDOCONF >" $TEMPDIR /vdoconf.yml "
2023-09-08 18:19:39 +03:00
[ -s " $TEMPDIR /vdoconf.yml " ] || error "Cannot work without VDO configuration."
2021-07-09 15:44:07 +03:00
2021-08-31 21:52:26 +03:00
# Check list of devices in VDO configure file for their major:minor
# and match with given $DEVICE devmajor:devminor
for i in $( awk '/.*device:/ {print $2}' " $TEMPDIR /vdoconf.yml " ) ; do
local DEV
DEV = $( " $READLINK " $READLINK_E " $i " ) || continue
2021-09-01 16:46:04 +03:00
RSTAT = $( " $STAT " --format "MAJOR=\$((0x%t)) MINOR=\$((0x%T))" " $DEV " 2>/dev/null) || continue
eval " $RSTAT "
2023-09-08 18:19:39 +03:00
if [ " $MAJOR " = " $DEVMAJOR " ] && [ " $MINOR " = " $DEVMINOR " ] ; then
[ -z " $FOUND " ] || error " VDO configuration contains duplicate entries $FOUND and $i . "
2021-08-31 21:52:26 +03:00
FOUND = $i
2023-09-08 18:19:39 +03:00
fi
2021-08-31 21:52:26 +03:00
done
2023-09-08 18:19:39 +03:00
[ -n " $FOUND " ] || error "Can't find matching device in VDO configuration file."
2023-05-11 21:07:37 +03:00
verbose " Found matching device $FOUND $MAJOR : $MINOR . "
2021-08-31 21:52:26 +03:00
VDONAME = $( awk -v DNAME = " $FOUND " '/.*VDOService$/ {VNAME=substr($1, 0, length($1) - 1)} /[[:space:]]*device:/ { if ($2 ~ DNAME) {print VNAME}}' " $TEMPDIR /vdoconf.yml " )
2021-07-09 15:44:07 +03:00
TRVDONAME = $( echo " $VDONAME " | tr '-' '_' )
# When VDO volume is 'active', check it's not mounted/being used
2021-08-31 21:52:26 +03:00
DM_OPEN = " $( " $DMSETUP " info -c -o open " $VDONAME " --noheadings --nameprefixes 2>/dev/null || true ) "
case " $DM_OPEN " in
Device*) ; ; # no devices
2021-09-01 16:46:04 +03:00
*) eval " $DM_OPEN "
2023-09-08 18:19:39 +03:00
[ " ${ DM_OPEN :- 0 } " -eq 0 ] || error " Cannot convert in use VDO volume \" $VDONAME \"! "
2021-08-31 21:52:26 +03:00
; ;
esac
2021-07-09 15:44:07 +03:00
#parse_yaml_ "$TEMPDIR/vdoconf.yml" _
eval " $( parse_yaml_ " $TEMPDIR /vdoconf.yml " _ | grep " $TRVDONAME " | sed -e " s/_config_vdos_ $TRVDONAME /vdo/g " ) "
vdo_logicalSize = $( get_kb_size_with_unit_ " $vdo_logicalSize " )
vdo_physicalSize = $( get_kb_size_with_unit_ " $vdo_physicalSize " )
2021-08-31 21:52:26 +03:00
verbose " Converted VDO device has logical/physical size $vdo_logicalSize / $vdo_physicalSize KiB. "
2021-07-09 15:44:07 +03:00
2023-05-11 21:07:37 +03:00
VDO_ALLOCATION_PARAMS = $( cat <<EOF
2021-07-09 15:44:07 +03:00
allocation {
vdo_use_compression = $( get_enabled_value_ " $vdo_compression " )
vdo_use_deduplication = $( get_enabled_value_ " $vdo_deduplication " )
vdo_use_metadata_hints = 1
vdo_minimum_io_size = $vdo_logicalBlockSize
2022-05-31 23:48:38 +03:00
vdo_block_map_cache_size_mb = $(( $( get_kb_size_with_unit_ " $vdo_blockMapCacheSize " ) / 1024 ))
2021-07-09 15:44:07 +03:00
vdo_block_map_period = $vdo_blockMapPeriod
vdo_use_sparse_index = $( get_enabled_value_ " $vdo_indexSparse " )
vdo_index_memory_size_mb = $( awk " BEGIN {print $vdo_indexMemory * 1024} " )
2022-07-11 02:07:24 +03:00
vdo_slab_size_mb = $(( $( get_kb_size_with_unit_ " $vdo_slabSize " ) / 1024 ))
2021-07-09 15:44:07 +03:00
vdo_ack_threads = $vdo_ackThreads
vdo_bio_threads = $vdo_bioThreads
vdo_bio_rotation = $vdo_bioRotationInterval
vdo_cpu_threads = $vdo_cpuThreads
vdo_hash_zone_threads = $vdo_hashZoneThreads
vdo_logical_threads = $vdo_logicalThreads
vdo_physical_threads = $vdo_physicalThreads
vdo_write_policy = $vdo_writePolicy
2021-09-06 15:57:43 +03:00
vdo_max_discard = $(( $( get_kb_size_with_unit_ " $vdo_maxDiscardSize " ) / 4 ))
2021-07-09 15:44:07 +03:00
vdo_pool_header_size = 0
}
EOF
)
2023-05-11 21:07:37 +03:00
verbose " VDO conversion parameters: $VDO_ALLOCATION_PARAMS "
verbose "Stopping VDO volume."
2023-06-29 14:06:18 +03:00
dry " $VDO " stop $VDOCONF --name " $VDONAME " $VERB
2021-07-09 15:44:07 +03:00
2021-09-06 16:06:32 +03:00
# If user has not provided '--yes', prompt before conversion
2023-09-08 18:19:39 +03:00
if [ -z " $YES " ] && [ -z " $USE_VDO_DM_SNAPSHOT " ] ; then
2021-09-06 16:06:32 +03:00
PROMPTING = yes
2023-09-08 18:19:39 +03:00
echo -n " $TOOL : Convert VDO device \" $DEVICE \" to VDO LV \" $VGNAME / $LVNAME \"? [y|N]: "
2023-05-11 21:07:37 +03:00
read -r -n 1 -s ANSWER
2021-09-06 16:06:32 +03:00
case " ${ ANSWER : 0 : 1 } " in
2023-05-11 21:07:37 +03:00
y| Y ) echo "Yes" ; ;
2023-09-08 18:19:39 +03:00
* ) echo "No" ; PROMPTING = "" ; return 1 ; ;
2021-09-06 16:06:32 +03:00
esac
PROMPTING = ""
2023-05-11 21:07:37 +03:00
YES = "-y" # From now, no prompting
2021-09-06 16:06:32 +03:00
fi
2023-05-11 21:07:37 +03:00
# Make a backup of the existing VDO yaml configuration file
2023-09-08 18:19:39 +03:00
[ -e " $VDO_CONFIG " ] && dry cp -a " $VDO_CONFIG " " ${ VDO_CONFIG } .backup "
2021-07-09 15:44:07 +03:00
2023-09-03 16:55:26 +03:00
DEVICE = $FOUND
2023-05-11 21:07:37 +03:00
case " $DM_UUID " in
LVM-*) convert_lv_ " $vdo_logicalSize " ; ;
*) convert_non_lv_ " $vdo_logicalSize " ; ;
esac
2021-07-09 15:44:07 +03:00
}
#############################
# start point of this script
# - parsing parameters
#############################
2023-09-08 18:19:39 +03:00
trap "cleanup" " ${ SIGNALS [@] } "
2021-07-09 15:44:07 +03:00
2023-09-08 18:19:39 +03:00
[ " $# " -eq 0 ] && tool_usage
2021-07-09 15:44:07 +03:00
while [ " $# " -ne 0 ]
do
case " $1 " in
"" ) ; ;
"-f" | "--force" ) FORCE = "-f" ; ;
"-h" | "--help" ) tool_usage ; ;
"-n" | "--name" ) shift; NAME = $1 ; ;
2023-07-17 17:22:29 +03:00
"-v" | "--verbose" ) VERB = "--verbose" ; ;
2021-07-09 15:44:07 +03:00
"-y" | "--yes" ) YES = "-y" ; ;
2023-09-08 18:19:39 +03:00
"--abort-after-vdo-convert" | "--abortaftervdoconvert" ) ABORT_AFTER_VDO_CONVERT = 1; USE_VDO_DM_SNAPSHOT = ; ; # For testing only
"--dry-run" | "--dryrun" ) DRY = "1" ; VERB = "-v" ; ;
"--no-snapshot" | "--nosnapshot" ) USE_VDO_DM_SNAPSHOT = ; ;
"--uuid-prefix" | "--uuidprefix" ) shift; DM_UUID_PREFIX = $1 ; ; # For testing only
"--vdo-config" | "--vdoconfig" ) shift; VDO_CONFIG = $1 ; VDOCONF = " -f $VDO_CONFIG " ; ;
-* ) error " Wrong argument \" $1 \". (see: $TOOL --help) " ; ;
2023-05-11 21:07:37 +03:00
*) DEVICE = $1 ; ; # device name does not start with '-'
2021-07-09 15:44:07 +03:00
esac
shift
done
2023-09-08 18:19:39 +03:00
[ -n " $DEVICE " ] || error " Device name is not specified. (see: $TOOL --help) "
2023-05-11 21:07:37 +03:00
convert2lvm_