This commit is contained in:
2026-09-06 13:46:23 +08:00
parent 2957ef1f1e
commit 8bce42bc84

View File

@@ -277,24 +277,98 @@ parse_int() {
echo "$val" | awk '{printf "%d", $1+0}' echo "$val" | awk '{printf "%d", $1+0}'
} }
#---------------------------------------
# 从设备路径解析 LVM 的 VG 和 LV 名称
#---------------------------------------
resolve_lvm_names() {
local device="$1"
# 尝试多种方法从设备路径获取 VG 名和 LV 路径
# 输入: /dev/mapper/cl_centos85-root 或 /dev/dm-0 等
# 输出: 设置 ROOT_VG 和 ROOT_LV_PATH 全局变量
# 方法1: 直接用 lvs 查询(某些系统支持 /dev/mapper/ 路径)
ROOT_VG=$(lvs --noheadings -o vg_name "$device" 2>/dev/null | xargs) || true
if [[ -n "$ROOT_VG" ]]; then
ROOT_LV_PATH="$device"
return 0
fi
# 方法2: 使用 dmsetup splitname最可靠
if command -v dmsetup &>/dev/null && [[ "$device" == /dev/mapper/* ]]; then
local dm_name
dm_name=$(basename "$device")
ROOT_VG=$(dmsetup splitname "$dm_name" -o vg_name --noheadings 2>/dev/null | xargs) || true
local lv_name
lv_name=$(dmsetup splitname "$dm_name" -o lv_name --noheadings 2>/dev/null | xargs) || true
if [[ -n "$ROOT_VG" && -n "$lv_name" ]]; then
ROOT_LV_PATH="${ROOT_VG}/${lv_name}"
return 0
fi
fi
# 方法3: 从 /dev/mapper/ 路径手动解析
# 规则: -- 转义为 -,然后第一个 - 分隔 VG 和 LV
if [[ "$device" == /dev/mapper/* ]]; then
local mapper_name
mapper_name=$(basename "$device")
# 先将 -- 替换为占位符,再将 - 替换为 /,最后将占位符还原为 -
local vg_lv
vg_lv=$(echo "$mapper_name" | sed 's/--/\x00/g' | tr '-' '/' | tr '\0' '-')
ROOT_VG=$(echo "$vg_lv" | cut -d'/' -f1)
local lv_name
lv_name=$(echo "$vg_lv" | cut -d'/' -f2)
if [[ -n "$ROOT_VG" && -n "$lv_name" ]]; then
ROOT_LV_PATH="${ROOT_VG}/${lv_name}"
return 0
fi
fi
# 方法4: 遍历所有 LV查找匹配当前挂载设备的
local all_lvs
all_lvs=$(lvs --noheadings -o lv_path,vg_name,lv_name 2>/dev/null) || true
while IFS= read -r line; do
local lv_p lv_vg lv_name
lv_p=$(echo "$line" | awk '{print $1}')
lv_vg=$(echo "$line" | awk '{print $2}')
lv_name=$(echo "$line" | awk '{print $3}')
# 检查此 LV 是否是根设备
if [[ -L "$device" ]]; then
local real_dev
real_dev=$(readlink -f "$device")
local lv_real
lv_real=$(readlink -f "$lv_p" 2>/dev/null) || true
if [[ "$real_dev" == "$lv_real" ]]; then
ROOT_VG="$lv_vg"
ROOT_LV_PATH="${lv_vg}/${lv_name}"
return 0
fi
fi
done <<< "$all_lvs"
return 1
}
#--------------------------------------- #---------------------------------------
# LVM 模式:检查 VG 剩余空间 # LVM 模式:检查 VG 剩余空间
#--------------------------------------- #---------------------------------------
check_lvm_space() { check_lvm_space() {
# 获取根设备所在的 VG # 解析 VG 和 LV 路径
ROOT_VG=$(lvs -no vg_name "$ROOT_DEVICE" 2>/dev/null | xargs) || true if ! resolve_lvm_names "$ROOT_DEVICE"; then
die "无法获取根逻辑卷所在的卷组"
fi
if [[ -z "$ROOT_VG" ]]; then if [[ -z "$ROOT_VG" ]]; then
die "无法获取根逻辑卷所在的卷组" die "无法获取根逻辑卷所在的卷组"
fi fi
info "根目录所在卷组 (VG): $ROOT_VG" info "根目录所在卷组 (VG): $ROOT_VG"
info "逻辑卷路径: $ROOT_LV_PATH"
# 获取 VG 的可用空间(人类可读格式,用于显示) # 获取 VG 的可用空间(人类可读格式,用于显示)
VG_FREE_HUMAN=$(vgs -no vg_free "$ROOT_VG" --units g --nosuffix 2>/dev/null | tr -d '<> ' ) || true VG_FREE_HUMAN=$(vgs --noheadings -o vg_name,vg_free --units g --nosuffix 2>/dev/null | awk -v vg="$ROOT_VG" '$1==vg{print $2}' | tr -d '<> ') || true
info "卷组 $ROOT_VG 可用空间: ${VG_FREE_HUMAN}G" info "卷组 $ROOT_VG 可用空间: ${VG_FREE_HUMAN}G"
# 检查 VG 是否有剩余空间(以 MB 为单位判断) # 检查 VG 是否有剩余空间(以 MB 为单位判断)
local vg_free_raw local vg_free_raw
vg_free_raw=$(vgs -no vg_free "$ROOT_VG" --units m --nosuffix 2>/dev/null) || true vg_free_raw=$(vgs --noheadings -o vg_name,vg_free --units m --nosuffix 2>/dev/null | awk -v vg="$ROOT_VG" '$1==vg{print $2}') || true
VG_FREE_NUM=$(parse_int "$vg_free_raw") VG_FREE_NUM=$(parse_int "$vg_free_raw")
info "卷组可用空间: ${VG_FREE_NUM}MB" info "卷组可用空间: ${VG_FREE_NUM}MB"
@@ -303,9 +377,9 @@ check_lvm_space() {
info "卷组可用空间不足,将检查是否需要扩展物理卷 (PV)..." info "卷组可用空间不足,将检查是否需要扩展物理卷 (PV)..."
expand_pv_if_needed expand_pv_if_needed
# 重新获取可用空间 # 重新获取可用空间
vg_free_raw=$(vgs -no vg_free "$ROOT_VG" --units m --nosuffix 2>/dev/null) || true vg_free_raw=$(vgs --noheadings -o vg_name,vg_free --units m --nosuffix 2>/dev/null | awk -v vg="$ROOT_VG" '$1==vg{print $2}') || true
VG_FREE_NUM=$(parse_int "$vg_free_raw") VG_FREE_NUM=$(parse_int "$vg_free_raw")
VG_FREE_HUMAN=$(vgs -no vg_free "$ROOT_VG" --units g --nosuffix 2>/dev/null | tr -d '<> ') || true VG_FREE_HUMAN=$(vgs --noheadings -o vg_name,vg_free --units g --nosuffix 2>/dev/null | awk -v vg="$ROOT_VG" '$1==vg{print $2}' | tr -d '<> ') || true
if [[ -z "$VG_FREE_NUM" ]] || [[ "$VG_FREE_NUM" -lt 100 ]]; then if [[ -z "$VG_FREE_NUM" ]] || [[ "$VG_FREE_NUM" -lt 100 ]]; then
die "卷组 $ROOT_VG 没有足够的可用空间进行扩容(需要至少 100MB" die "卷组 $ROOT_VG 没有足够的可用空间进行扩容(需要至少 100MB"
fi fi
@@ -320,7 +394,7 @@ check_lvm_space() {
expand_pv_if_needed() { expand_pv_if_needed() {
# 获取 VG 对应的 PV # 获取 VG 对应的 PV
local pv_dev local pv_dev
pv_dev=$(pvs -no pv_name "$ROOT_VG" 2>/dev/null | xargs) || true pv_dev=$(pvs --noheadings -o pv_name,vg_name 2>/dev/null | awk -v vg="$ROOT_VG" '$2==vg{print $1}' | head -1 | xargs) || true
if [[ -z "$pv_dev" ]]; then if [[ -z "$pv_dev" ]]; then
die "无法获取卷组 $ROOT_VG 的物理卷" die "无法获取卷组 $ROOT_VG 的物理卷"
fi fi
@@ -456,8 +530,8 @@ do_expand() {
do_expand_lvm() { do_expand_lvm() {
info "========== 开始 LVM 模式扩容 ==========" info "========== 开始 LVM 模式扩容 =========="
# 获取 LV 路径 # 获取 LV 路径(使用解析后的路径)
local lv_path="$ROOT_DEVICE" local lv_path="${ROOT_LV_PATH:-$ROOT_DEVICE}"
info "目标逻辑卷: $lv_path" info "目标逻辑卷: $lv_path"
if [[ "$DRY_RUN" == false ]]; then if [[ "$DRY_RUN" == false ]]; then