Current File : //proc/self/root/usr/local/jetapps/usr/share/rear/layout/prepare/GNU/Linux/110_include_lvm_code.sh |
# Code to recreate an LVM configuration.
# The input to the code creation functions is a file descriptor to one line
# of the layout description.
if ! has_binary lvm; then
return
fi
#
# Refer to usr/share/rear/layout/save/GNU/Linux/220_lvm_layout.sh for the
# corresponding information collected during 'mkrescue'.
#
# Test for features in lvm.
# Versions higher than 2.02.73 need --norestorefile if no UUID/restorefile.
FEATURE_LVM_RESTOREFILE=
lvm_version=$(get_version lvm version)
[ "$lvm_version" ] || BugError "Function get_version could not detect lvm version."
# RHEL 6.0 contains lvm with knowledge of --norestorefile (issue #462)
if version_newer "$lvm_version" 2.02.71 ; then
FEATURE_LVM_RESTOREFILE="y"
fi
# Create a new PV.
create_lvmdev() {
local lvmdev vgrp device uuid junk
# Selects line matching 'lvmdev' (PV) and disk name (column 3),
# cf. https://github.com/rear/rear/pull/1897
# This should be unique.
read lvmdev vgrp device uuid junk < <(awk "\$1 == \"lvmdev\" && \$3 == \"${1#pv:}\" { print }" "$LAYOUT_FILE")
local vg=${vgrp#/dev/}
(
echo "LogPrint \"Creating LVM PV $device\""
### Work around automatic volume group activation leading to active disks
echo "lvm vgchange -a n $vg || true"
local uuidopt=""
local restorefileopt=""
if ! is_true "$MIGRATION_MODE" && test -e "$VAR_DIR/layout/lvm/${vg}.cfg" ; then
# we have a restore file
restorefileopt=" --restorefile $VAR_DIR/layout/lvm/${vg}.cfg"
else
if [ -n "$FEATURE_LVM_RESTOREFILE" ] ; then
restorefileopt=" --norestorefile"
fi
fi
if [ -n "$uuid" ] ; then
uuidopt=" --uuid \"$uuid\""
fi
echo "lvm pvcreate -ff --yes -v$uuidopt$restorefileopt $device >&2"
) >> "$LAYOUT_CODE"
}
# Create a new VG.
create_lvmgrp() {
local lvmgrp vgrp extentsize junk
read lvmgrp vgrp extentsize junk < <(grep "^lvmgrp $1 " "$LAYOUT_FILE")
local vg=${vgrp#/dev/}
# If a volume group name is in one of the following lists, it
# means that the particular condition is valid for this volume
# group. If it is not in the list, it means that the condition is
# not valid for this volume group. To set a condition, add the VG
# name to the list. To unset it, remove it from the list.
# We must represent the conditions using lists, not simple scalar
# boolean variables. The conditions shall propagate information
# from VG creation to LV creation. A scalar does not work well in
# the case of multiple VGs, because the variables are global and
# if there are multiple VGs, their values will leak from one VG to
# another. (The generated diskrestore.sh script does not guarantee
# that the LVs of a given VG are created immediately after their
# VG and before creating another VG, actually, the script first
# creates all VGs and then all LVs.) This logic does not apply to
# the create_volume_group condition, because it is local to the VG
# creation and does not need to be propagated to LV creation. We
# use the same approach for symmetry, though.
# The meanings of conditions corresponding to those lists are:
# create_volume_group - VG needs to be created using the vgcreate command
# create_logical_volumes - LVs in the VG need to be created using the lvcreate command
# create_thin_volumes_only - when the previous condition is true,
# do not create volumes that are not thin volumes.
cat >> "$LAYOUT_CODE" <<EOF
create_volume_group+=( "$vg" )
create_logical_volumes+=( "$vg" )
create_thin_volumes_only=( \$( RmInArray "$vg" "\${create_thin_volumes_only[@]}" ) )
EOF
# If we are not migrating, then try using "vgcfgrestore", but this can
# fail, typically if Thin Pools are used.
#
# In such case, we need to rely on vgcreate/lvcreate commands which is not
# recommended because we are not able to collect all required options yet.
# For example, we do not take the '--stripes' option into account, nor
# '--mirrorlog', etc.
# Also, we likely do not support every layout yet (e.g. 'cachepool').
if ! is_true "$MIGRATION_MODE" ; then
cat >> "$LAYOUT_CODE" <<EOF
LogPrint "Restoring LVM VG '$vg'"
if [ -e "$vgrp" ] ; then
rm -rf "$vgrp"
fi
#
# Restore layout using 'vgcfgrestore', this may fail if there are Thin volumes
#
if lvm vgcfgrestore -f "$VAR_DIR/layout/lvm/${vg}.cfg" $vg >&2 ; then
lvm vgchange --available y $vg >&2
LogPrint "Sleeping 3 seconds to let udev or systemd-udevd create their devices..."
sleep 3 >&2
create_volume_group=( \$( RmInArray "$vg" "\${create_volume_group[@]}" ) )
create_logical_volumes=( \$( RmInArray "$vg" "\${create_logical_volumes[@]}" ) )
#
# It failed ... restore layout using 'vgcfgrestore --force', but then remove Thin volumes, they are broken
#
elif lvm vgcfgrestore --force -f "$VAR_DIR/layout/lvm/${vg}.cfg" $vg >&2 ; then
lvm lvs --noheadings -o lv_name,vg_name,lv_layout | while read lv vg layout ; do
# Consider LVs for our VG only
[ \$vg == "$vg" ] || continue
# Consider Thin Pools only
[[ ,\$layout, == *,thin,* ]] && [[ ,\$layout, == *,pool,* ]] || continue
# Use "--force" twice to bypass any error due to invalid transaction id
lvm lvremove -q -f -f -y $vg/\$lv
done
# Once Thin pools have been removed, we can activate the VG
lvm vgchange --available y $vg >&2
LogPrint "Sleeping 3 seconds to let udev or systemd-udevd create their devices..."
sleep 3 >&2
# All logical volumes have been created, except Thin volumes and pools
create_volume_group=( \$( RmInArray "$vg" "\${create_volume_group[@]}" ) )
create_thin_volumes_only+=( "$vg" )
#
# It failed also ... restore using 'vgcreate/lvcreate' commands
#
else
LogPrint "Could not restore LVM configuration using 'vgcfgrestore'. Using traditional 'vgcreate/lvcreate' commands instead"
fi
EOF
fi
# Cf. https://github.com/rear/rear/pull/1897
local -a devices=($(awk "\$1 == \"lvmdev\" && \$2 == \"$vgrp\" { print \$3 }" "$LAYOUT_FILE"))
cat >> "$LAYOUT_CODE" <<EOF
if IsInArray $vg "\${create_volume_group[@]}" ; then
LogPrint "Creating LVM VG '$vg' (some properties may not be preserved)"
lvm vgremove --force --force --yes $vg >&2 || true
if [ -e "$vgrp" ] ; then
rm -rf "$vgrp"
fi
lvm vgcreate --physicalextentsize ${extentsize}k $vg ${devices[@]} >&2
lvm vgchange --available y $vg >&2
fi
EOF
}
# Create a LV.
create_lvmvol() {
local name vg lv
name=${1#/dev/mapper/}
### split between vg and lv is single dash
### Device mapper doubles dashes in vg and lv
vg=$(sed "s/\([^-]\)-[^-].*/\1/;s/--/-/g" <<< "$name")
lv=$(sed "s/.*[^-]-\([^-]\)/\1/;s/--/-/g" <<< "$name")
# kval: "key:value" pairs, separated by spaces
local lvmvol vgrp lvname size layout kval
read lvmvol vgrp lvname size layout kval < <(grep "^lvmvol /dev/$vg $lv " "$LAYOUT_FILE")
local lvopts=""
# Handle 'key:value' pairs
for kv in $kval ; do
local key=$(awk -F ':' '{ print $1 }' <<< "$kv")
local value=$(awk -F ':' '{ print $2 }' <<< "$kv")
lvopts="${lvopts:+$lvopts }--$key $value"
done
local is_thin=0
local is_raidunknown=0
if [[ ,$layout, == *,thin,* ]] ; then
is_thin=1
if [[ ,$layout, == *,pool,* ]] ; then
# Thin Pool
lvopts="${lvopts:+$lvopts }--type thin-pool -L $size --thinpool $lv"
else
# Thin Volume within Thin Pool
if [[ ,$layout, == *,sparse,* ]] ; then
lvopts="${lvopts:+$lvopts }-V $size -n ${lvname}"
else
BugError "Unsupported Thin LV layout '$layout' for LV '$lv'"
fi
fi
elif [[ ,$layout, == *,linear,* ]] ; then
lvopts="${lvopts:+$lvopts }-L $size -n ${lvname}"
elif [[ ,$layout, == *,mirror,* ]] ; then
lvopts="${lvopts:+$lvopts }--type mirror -L $size -n ${lvname}"
elif [[ ,$layout, == *,striped,* ]] ; then
lvopts="${lvopts:+$lvopts }--type striped -L $size -n ${lvname}"
elif [[ ,$layout, == *,raid,* ]] ; then
local found=0
local lvl
for lvl in raid0 raid1 raid4 raid5 raid6 raid10 ; do
if [[ ,$layout, == *,$lvl,* ]] ; then
lvopts="${lvopts:+$lvopts }--type $lvl"
found=1
break
fi
done
if [ $found -eq 0 ] ; then
if [[ ,$layout, == *,RAID_UNKNOWNTYPE,* ]] ; then
# Compatibility with older LVM versions (e.g. <= 2.02.98)
is_raidunknown=1
# Don't set '--type', so will be created as a linear volume
else
BugError "Unsupported LV layout '$layout' found for LV '$lv'"
fi
fi
lvopts="${lvopts:+$lvopts }-L $size -n ${lvname}"
else
BugError "Unsupported LV layout '$layout' found for LV '$lv'"
fi
local ifline
local warnraidline
if [ $is_thin -eq 0 ] ; then
ifline="if IsInArray $vg \"\${create_logical_volumes[@]}\" && ! IsInArray $vg \"\${create_thin_volumes_only[@]}\" ; then"
else
ifline="if IsInArray $vg \"\${create_logical_volumes[@]}\" ; then"
fi
if [ $is_raidunknown -eq 1 ]; then
warnraidline="LogPrint \"Warning: Don't know how to restore RAID volume '$lv', restoring as linear volume\""
else
warnraidline=""
fi
local fallbacklvopts
# Assume lvcreate had failed because of "Volume group ... has insufficient free space"
# which usually happens when the replacement disk is a bit smaller than the original disk
# so that the following fallback attempt to create a LV could work at least once per VG
# which is sufficient when the replacement disk is a bit smaller than the original disk
# because then the last LV that is created gets shrunk to the remaining space in the VG.
# In the lvopts string replace the exact size option of the form '-L 123456b'
# with an option to use all remaining free space in the VG via '-l 100%FREE'
# so e.g. 'lvcreate -L 123456b -n LV VG' becomes 'lvcreate -l 100%FREE -n LV VG'
fallbacklvopts="$( sed -e 's/-L [0-9b]*/-l 100%FREE/' <<< "$lvopts" )"
cat >> "$LAYOUT_CODE" <<EOF
$ifline
LogPrint "Creating LVM volume '$vg/$lvname' (some properties may not be preserved)"
$warnraidline
if ! lvm lvcreate -y $lvopts $vg ; then
LogPrintError "Failed to create LVM volume '$vg/$lvname' with lvcreate -y $lvopts $vg"
if lvm lvcreate -y $fallbacklvopts $vg ; then
LogPrintError "Created LVM volume '$vg/$lvname' using fallback options lvcreate -y $fallbacklvopts $vg"
else
LogPrintError "Also failed to create LVM volume '$vg/$lvname' with lvcreate -y $fallbacklvopts $vg"
# Explicit 'false' is needed to let the whole 'if then else fi' command exit with non zero exit state
# to let diskrestore.sh abort here as usual when a command fails (diskrestore.sh runs with 'set -e'):
false
fi
fi
fi
EOF
}
# vim: set et ts=4 sw=4: