diff --git a/src/main.sh b/src/main.sh index ab392eb..6fc4f45 100644 --- a/src/main.sh +++ b/src/main.sh @@ -277,24 +277,98 @@ parse_int() { 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 剩余空间 #--------------------------------------- check_lvm_space() { - # 获取根设备所在的 VG - ROOT_VG=$(lvs -no vg_name "$ROOT_DEVICE" 2>/dev/null | xargs) || true + # 解析 VG 和 LV 路径 + if ! resolve_lvm_names "$ROOT_DEVICE"; then + die "无法获取根逻辑卷所在的卷组" + fi if [[ -z "$ROOT_VG" ]]; then die "无法获取根逻辑卷所在的卷组" fi info "根目录所在卷组 (VG): $ROOT_VG" + info "逻辑卷路径: $ROOT_LV_PATH" # 获取 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" # 检查 VG 是否有剩余空间(以 MB 为单位判断) 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") info "卷组可用空间: ${VG_FREE_NUM}MB" @@ -303,9 +377,9 @@ check_lvm_space() { info "卷组可用空间不足,将检查是否需要扩展物理卷 (PV)..." 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_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 die "卷组 $ROOT_VG 没有足够的可用空间进行扩容(需要至少 100MB)" fi @@ -320,7 +394,7 @@ check_lvm_space() { expand_pv_if_needed() { # 获取 VG 对应的 PV 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 die "无法获取卷组 $ROOT_VG 的物理卷" fi @@ -456,8 +530,8 @@ do_expand() { do_expand_lvm() { info "========== 开始 LVM 模式扩容 ==========" - # 获取 LV 路径 - local lv_path="$ROOT_DEVICE" + # 获取 LV 路径(使用解析后的路径) + local lv_path="${ROOT_LV_PATH:-$ROOT_DEVICE}" info "目标逻辑卷: $lv_path" if [[ "$DRY_RUN" == false ]]; then