fix bug45
This commit is contained in:
216
system_info.py
216
system_info.py
@@ -3,7 +3,7 @@ import subprocess
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import os # 导入 os 模块
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -82,7 +82,7 @@ class SystemInfoManager:
|
||||
Helper to find device data by its path recursively.
|
||||
Added type check for target_path.
|
||||
"""
|
||||
if not isinstance(target_path, str): # 添加类型检查
|
||||
if not isinstance(target_path, str):
|
||||
logger.warning(f"传入 _find_device_by_path_recursive 的 target_path 不是字符串: {target_path} (类型: {type(target_path)})")
|
||||
return None
|
||||
for dev in dev_list:
|
||||
@@ -94,42 +94,109 @@ class SystemInfoManager:
|
||||
return found
|
||||
return None
|
||||
|
||||
def _find_device_by_maj_min_recursive(self, dev_list, target_maj_min):
|
||||
"""
|
||||
Helper to find device data by its major:minor number recursively.
|
||||
"""
|
||||
if not isinstance(target_maj_min, str):
|
||||
logger.warning(f"传入 _find_device_by_maj_min_recursive 的 target_maj_min 不是字符串: {target_maj_min} (类型: {type(target_maj_min)})")
|
||||
return None
|
||||
for dev in dev_list:
|
||||
if dev.get('maj:min') == target_maj_min:
|
||||
return dev
|
||||
if 'children' in dev:
|
||||
found = self._find_device_by_maj_min_recursive(dev['children'], target_maj_min)
|
||||
if found:
|
||||
return found
|
||||
return None
|
||||
|
||||
def get_mountpoint_for_device(self, device_path):
|
||||
"""
|
||||
根据设备路径获取其挂载点。
|
||||
此方法现在可以正确处理 /dev/md/new_raid 这样的 RAID 阵列别名。
|
||||
Added type check for device_path.
|
||||
此方法现在可以正确处理 /dev/md/new_raid 这样的 RAID 阵列别名,以及 LVM 逻辑卷的符号链接。
|
||||
"""
|
||||
if not isinstance(device_path, str): # 添加类型检查
|
||||
if not isinstance(device_path, str):
|
||||
logger.warning(f"传入 get_mountpoint_for_device 的 device_path 不是字符串: {device_path} (类型: {type(device_path)})")
|
||||
return None
|
||||
|
||||
original_device_path = device_path
|
||||
logger.debug(f"get_mountpoint_for_device: 正在处理原始设备路径: {original_device_path}")
|
||||
|
||||
devices = self.get_block_devices()
|
||||
logger.debug(f"get_mountpoint_for_device: 从 lsblk 获取到 {len(devices)} 个块设备信息。")
|
||||
|
||||
target_maj_min = None
|
||||
current_search_path = original_device_path
|
||||
|
||||
# 1. 解析符号链接以获取实际路径,并尝试获取 maj:min
|
||||
if original_device_path.startswith('/dev/') and os.path.exists(original_device_path):
|
||||
try:
|
||||
# 先尝试解析真实路径,因为 stat 可能对符号链接返回链接本身的 maj:min
|
||||
resolved_path = os.path.realpath(original_device_path)
|
||||
logger.debug(f"get_mountpoint_for_device: 原始设备路径 {original_device_path} 解析为 {resolved_path}")
|
||||
current_search_path = resolved_path # 使用解析后的路径进行 stat 和后续查找
|
||||
|
||||
# 获取解析后路径的 maj:min
|
||||
stat_output, _ = self._run_command(["stat", "-c", "%t:%T", resolved_path], check_output=True)
|
||||
raw_maj_min = stat_output.strip() # e.g., "fc:0"
|
||||
|
||||
# MODIFIED: Convert hex major to decimal
|
||||
if ':' in raw_maj_min:
|
||||
major_hex, minor_dec = raw_maj_min.split(':')
|
||||
try:
|
||||
major_dec = str(int(major_hex, 16)) # Convert hex to decimal string
|
||||
target_maj_min = f"{major_dec}:{minor_dec}" # e.g., "252:0"
|
||||
logger.debug(f"get_mountpoint_for_device: 解析后设备 {resolved_path} 的 maj:min (hex) 为 {raw_maj_min},转换为 decimal 为 {target_maj_min}")
|
||||
except ValueError:
|
||||
logger.warning(f"get_mountpoint_for_device: 无法将 maj:min 的主要部分 '{major_hex}' 从十六进制转换为十进制。")
|
||||
target_maj_min = None # Fallback if conversion fails
|
||||
else:
|
||||
logger.warning(f"get_mountpoint_for_device: stat 输出 '{raw_maj_min}' 格式不符合预期。")
|
||||
target_maj_min = None
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.debug(f"get_mountpoint_for_device: 无法获取 {resolved_path} 的 maj:min (命令失败): {e.stderr.strip()}")
|
||||
except Exception as e:
|
||||
logger.debug(f"get_mountpoint_for_device: 无法获取 {resolved_path} 的 maj:min (未知错误): {e}")
|
||||
elif original_device_path.startswith('/dev/') and not os.path.exists(original_device_path):
|
||||
logger.warning(f"get_mountpoint_for_device: 设备路径 {original_device_path} 不存在,无法获取挂载点。")
|
||||
return None
|
||||
|
||||
# 2. 尝试使用 (可能已解析的) current_search_path 在 lsblk 输出中查找
|
||||
dev_info = self._find_device_by_path_recursive(devices, current_search_path)
|
||||
logger.debug(f"get_mountpoint_for_device: _find_device_by_path_recursive 查找结果 (使用 {current_search_path}): {dev_info}")
|
||||
|
||||
# 1. 尝试直接使用提供的 device_path 在 lsblk 输出中查找
|
||||
dev_info = self._find_device_by_path_recursive(devices, device_path)
|
||||
if dev_info:
|
||||
logger.debug(f"直接从 lsblk 获取到 {device_path} 的挂载点: {dev_info.get('mountpoint')}")
|
||||
return dev_info.get('mountpoint')
|
||||
mountpoint = dev_info.get('mountpoint')
|
||||
logger.debug(f"get_mountpoint_for_device: 直接从 lsblk 获取到 {original_device_path} (解析为 {current_search_path}) 的挂载点: {mountpoint}")
|
||||
return mountpoint
|
||||
|
||||
# 2. 如果直接查找失败,并且是 RAID 阵列(例如 /dev/md/new_raid)
|
||||
if device_path.startswith('/dev/md'): # 此处 device_path 已通过类型检查
|
||||
logger.debug(f"处理 RAID 阵列 {device_path} 以获取挂载点...")
|
||||
# 3. 如果直接路径查找失败,并且我们有 maj:min,尝试通过 maj:min 查找
|
||||
if target_maj_min:
|
||||
logger.debug(f"get_mountpoint_for_device: 直接路径查找失败,尝试通过 maj:min ({target_maj_min}) 查找。")
|
||||
dev_info_by_maj_min = self._find_device_by_maj_min_recursive(devices, target_maj_min)
|
||||
logger.debug(f"get_mountpoint_for_device: 通过 maj:min 查找结果: {dev_info_by_maj_min}")
|
||||
if dev_info_by_maj_min:
|
||||
mountpoint = dev_info_by_maj_min.get('mountpoint')
|
||||
logger.debug(f"get_mountpoint_for_device: 通过 maj:min 找到 {original_device_path} 的挂载点: {mountpoint}")
|
||||
return mountpoint
|
||||
|
||||
# 获取 RAID 阵列的实际内核设备路径(例如 /dev/md127)
|
||||
actual_md_device_path = self._get_actual_md_device_path(device_path)
|
||||
logger.debug(f"RAID 阵列 {device_path} 的实际设备路径: {actual_md_device_path}")
|
||||
# 4. 如果仍然查找失败,并且是 RAID 阵列(例如 /dev/md/new_raid)
|
||||
if original_device_path.startswith('/dev/md'):
|
||||
logger.debug(f"get_mountpoint_for_device: 正在处理 RAID 阵列 {original_device_path} 以获取挂载点...")
|
||||
actual_md_device_path = self._get_actual_md_device_path(original_device_path)
|
||||
logger.debug(f"get_mountpoint_for_device: RAID 阵列 {original_device_path} 的实际设备路径: {actual_md_device_path}")
|
||||
|
||||
if actual_md_device_path:
|
||||
# 现在,使用实际的内核设备路径从 lsblk 中查找挂载点
|
||||
actual_dev_info = self._find_device_by_path_recursive(devices, actual_md_device_path)
|
||||
logger.debug(f"实际设备路径 {actual_md_device_path} 的 lsblk 详情: {actual_dev_info}")
|
||||
logger.debug(f"get_mountpoint_for_device: 实际设备路径 {actual_md_device_path} 的 lsblk 详情: {actual_dev_info}")
|
||||
|
||||
if actual_dev_info:
|
||||
logger.debug(f"在实际设备 {actual_md_device_path} 上找到了挂载点: {actual_dev_info.get('mountpoint')}")
|
||||
return actual_dev_info.get('mountpoint')
|
||||
mountpoint = actual_dev_info.get('mountpoint')
|
||||
logger.debug(f"get_mountpoint_for_device: 在实际设备 {actual_md_device_path} 上找到了挂载点: {mountpoint}")
|
||||
return mountpoint
|
||||
|
||||
logger.debug(f"未能获取到 {device_path} 的挂载点。")
|
||||
logger.debug(f"get_mountpoint_for_device: 未能获取到 {original_device_path} 的挂载点。")
|
||||
return None
|
||||
|
||||
def get_mdadm_arrays(self):
|
||||
@@ -269,9 +336,10 @@ class SystemInfoManager:
|
||||
except Exception as e:
|
||||
logger.error(f"获取LVM卷组信息失败: {e}")
|
||||
|
||||
# Get LVs
|
||||
# Get LVs (MODIFIED: added -o lv_path)
|
||||
try:
|
||||
stdout, _ = self._run_command(["lvs", "--reportformat", "json"])
|
||||
# 明确请求 lv_path,因为默认的 --reportformat json 不包含它
|
||||
stdout, _ = self._run_command(["lvs", "-o", "lv_name,vg_name,lv_uuid,lv_size,lv_attr,origin,snap_percent,lv_path", "--reportformat", "json"])
|
||||
data = json.loads(stdout)
|
||||
if 'report' in data and data['report']:
|
||||
for lv_data in data['report'][0].get('lv', []):
|
||||
@@ -283,7 +351,7 @@ class SystemInfoManager:
|
||||
'lv_attr': lv_data.get('lv_attr'),
|
||||
'origin': lv_data.get('origin'),
|
||||
'snap_percent': lv_data.get('snap_percent'),
|
||||
'lv_path': lv_data.get('lv_path')
|
||||
'lv_path': lv_data.get('lv_path') # 现在应该能正确获取到路径了
|
||||
})
|
||||
except subprocess.CalledProcessError as e:
|
||||
if "No logical volume found" in e.stderr or "No logical volumes found" in e.stdout:
|
||||
@@ -358,7 +426,7 @@ class SystemInfoManager:
|
||||
通过检查 /dev/md/ 目录下的符号链接来获取。
|
||||
Added type check for array_path.
|
||||
"""
|
||||
if not isinstance(array_path, str): # 添加类型检查
|
||||
if not isinstance(array_path, str):
|
||||
logger.warning(f"传入 _get_actual_md_device_path 的 array_path 不是字符串: {array_path} (类型: {type(array_path)})")
|
||||
return None
|
||||
|
||||
@@ -378,63 +446,103 @@ class SystemInfoManager:
|
||||
def get_device_details_by_path(self, device_path):
|
||||
"""
|
||||
根据设备路径获取设备的 UUID 和文件系统类型 (fstype)。
|
||||
此方法现在可以正确处理 /dev/md/new_raid 这样的 RAID 阵列别名。
|
||||
此方法现在可以正确处理 /dev/md/new_raid 这样的 RAID 阵列别名,以及 LVM 逻辑卷的符号链接。
|
||||
Added type check for device_path.
|
||||
:param device_path: 设备的路径,例如 '/dev/sdb1' 或 '/dev/md0' 或 '/dev/md/new_raid'
|
||||
:return: 包含 'uuid' 和 'fstype' 的字典,如果未找到则返回 None。
|
||||
"""
|
||||
if not isinstance(device_path, str): # 添加类型检查
|
||||
if not isinstance(device_path, str):
|
||||
logger.warning(f"传入 get_device_details_by_path 的 device_path 不是字符串: {device_path} (类型: {type(device_path)})")
|
||||
return None
|
||||
|
||||
original_device_path = device_path
|
||||
logger.debug(f"get_device_details_by_path: 正在处理原始设备路径: {original_device_path}")
|
||||
|
||||
devices = self.get_block_devices() # 获取所有块设备信息
|
||||
logger.debug(f"get_device_details_by_path: 从 lsblk 获取到 {len(devices)} 个块设备信息。")
|
||||
|
||||
# 1. 首先,尝试直接使用提供的 device_path 在 lsblk 输出中查找
|
||||
# 对于 /dev/md/new_raid 这样的别名,这一步通常会返回 None
|
||||
lsblk_details = self._find_device_by_path_recursive(devices, device_path)
|
||||
logger.debug(f"lsblk_details for {device_path} (direct lookup): {lsblk_details}")
|
||||
target_maj_min = None
|
||||
current_search_path = original_device_path
|
||||
|
||||
if lsblk_details and lsblk_details.get('fstype'): # 即使没有UUID,有fstype也算找到部分信息
|
||||
# 如果直接找到了 fstype,就返回 lsblk 提供的 UUID 和 fstype
|
||||
# 这里的 UUID 应该是文件系统 UUID
|
||||
logger.debug(f"直接从 lsblk 获取到 {device_path} 的详情: {lsblk_details}")
|
||||
# 1. 解析符号链接以获取实际路径,并尝试获取 maj:min
|
||||
if original_device_path.startswith('/dev/') and os.path.exists(original_device_path):
|
||||
try:
|
||||
resolved_path = os.path.realpath(original_device_path)
|
||||
logger.debug(f"get_device_details_by_path: 原始设备路径 {original_device_path} 解析为 {resolved_path}")
|
||||
current_search_path = resolved_path
|
||||
|
||||
stat_output, _ = self._run_command(["stat", "-c", "%t:%T", resolved_path], check_output=True)
|
||||
raw_maj_min = stat_output.strip() # e.g., "fc:0"
|
||||
|
||||
# MODIFIED: Convert hex major to decimal
|
||||
if ':' in raw_maj_min:
|
||||
major_hex, minor_dec = raw_maj_min.split(':')
|
||||
try:
|
||||
major_dec = str(int(major_hex, 16)) # Convert hex to decimal string
|
||||
target_maj_min = f"{major_dec}:{minor_dec}" # e.g., "252:0"
|
||||
logger.debug(f"get_device_details_by_path: 解析后设备 {resolved_path} 的 maj:min (hex) 为 {raw_maj_min},转换为 decimal 为 {target_maj_min}")
|
||||
except ValueError:
|
||||
logger.warning(f"get_device_details_by_path: 无法将 maj:min 的主要部分 '{major_hex}' 从十六进制转换为十进制。")
|
||||
target_maj_min = None
|
||||
else:
|
||||
logger.warning(f"get_device_details_by_path: stat 输出 '{raw_maj_min}' 格式不符合预期。")
|
||||
target_maj_min = None
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.debug(f"get_device_details_by_path: 无法获取 {resolved_path} 的 maj:min (命令失败): {e.stderr.strip()}")
|
||||
except Exception as e:
|
||||
logger.debug(f"get_device_details_by_path: 无法获取 {resolved_path} 的 maj:min (未知错误): {e}")
|
||||
elif original_device_path.startswith('/dev/') and not os.path.exists(original_device_path):
|
||||
logger.warning(f"get_device_details_by_path: 设备路径 {original_device_path} 不存在,无法获取详情。")
|
||||
return None
|
||||
|
||||
# 2. 首先,尝试使用 (可能已解析的) current_search_path 在 lsblk 输出中查找
|
||||
lsblk_details = self._find_device_by_path_recursive(devices, current_search_path)
|
||||
logger.debug(f"get_device_details_by_path: _find_device_by_path_recursive 查找结果 (使用 {current_search_path}, 直接查找): {lsblk_details}")
|
||||
|
||||
if lsblk_details and lsblk_details.get('fstype'):
|
||||
logger.debug(f"get_device_details_by_path: 直接从 lsblk 获取到 {original_device_path} (解析为 {current_search_path}) 的详情: {lsblk_details}")
|
||||
return {
|
||||
'uuid': lsblk_details.get('uuid'),
|
||||
'fstype': lsblk_details.get('fstype')
|
||||
}
|
||||
|
||||
# 2. 如果直接查找失败,并且是 RAID 阵列(例如 /dev/md/new_raid)
|
||||
if device_path.startswith('/dev/md'): # 此处 device_path 已通过类型检查
|
||||
logger.debug(f"处理 RAID 阵列 {device_path}...")
|
||||
# 3. 如果直接路径查找失败,并且我们有 maj:min,尝试通过 maj:min 查找
|
||||
if target_maj_min:
|
||||
logger.debug(f"get_device_details_by_path: 直接路径查找失败,尝试通过 maj:min ({target_maj_min}) 查找。")
|
||||
dev_info_by_maj_min = self._find_device_by_maj_min_recursive(devices, target_maj_min)
|
||||
logger.debug(f"get_device_details_by_path: 通过 maj:min 查找结果: {dev_info_by_maj_min}")
|
||||
if dev_info_by_maj_min and dev_info_by_maj_min.get('fstype'):
|
||||
logger.debug(f"get_device_details_by_path: 通过 maj:min 找到 {original_device_path} 的文件系统详情: {dev_info_by_maj_min}")
|
||||
return {
|
||||
'uuid': dev_info_by_maj_min.get('uuid'),
|
||||
'fstype': dev_info_by_maj_min.get('fstype')
|
||||
}
|
||||
|
||||
# 获取 RAID 阵列的实际内核设备路径(例如 /dev/md127)
|
||||
actual_md_device_path = self._get_actual_md_device_path(device_path)
|
||||
logger.debug(f"RAID 阵列 {device_path} 的实际设备路径: {actual_md_device_path}")
|
||||
# 4. 如果仍然没有找到,并且是 RAID 阵列(例如 /dev/md/new_raid)
|
||||
if original_device_path.startswith('/dev/md'):
|
||||
logger.debug(f"get_device_details_by_path: 正在处理 RAID 阵列 {original_device_path}...")
|
||||
|
||||
actual_md_device_path = self._get_actual_md_device_path(original_device_path)
|
||||
logger.debug(f"get_device_details_by_path: RAID 阵列 {original_device_path} 的实际设备路径: {actual_md_device_path}")
|
||||
|
||||
if actual_md_device_path:
|
||||
# 现在,使用实际的内核设备路径从 lsblk 中查找 fstype 和 UUID (文件系统 UUID)
|
||||
actual_device_lsblk_details = self._find_device_by_path_recursive(devices, actual_md_device_path)
|
||||
logger.debug(f"实际设备路径 {actual_md_device_path} 的 lsblk 详情: {actual_device_lsblk_details}")
|
||||
logger.debug(f"get_device_details_by_path: 实际设备路径 {actual_md_device_path} 的 lsblk 详情: {actual_device_lsblk_details}")
|
||||
|
||||
if actual_device_lsblk_details and actual_device_lsblk_details.get('fstype'):
|
||||
# 找到了实际 RAID 设备上的文件系统信息
|
||||
# 此时的 UUID 是文件系统 UUID,fstype 是文件系统类型
|
||||
logger.debug(f"在实际设备 {actual_md_device_path} 上找到了文件系统详情: {actual_device_lsblk_details}")
|
||||
logger.debug(f"get_device_details_by_path: 在实际设备 {actual_md_device_path} 上找到了文件系统详情: {actual_device_lsblk_details}")
|
||||
return {
|
||||
'uuid': actual_device_lsblk_details.get('uuid'),
|
||||
'fstype': actual_device_lsblk_details.get('fstype')
|
||||
}
|
||||
else:
|
||||
# RAID 设备存在,但 lsblk 没有报告文件系统 (例如,尚未格式化)
|
||||
# 此时 fstype 为 None。如果需要,我们可以返回 RAID 阵列本身的 UUID,但 fstype 仍为 None
|
||||
logger.warning(f"RAID 阵列 {device_path} (实际设备 {actual_md_device_path}) 未找到文件系统类型。")
|
||||
# 对于 fstab,如果没有 fstype,就无法创建条目。
|
||||
# 此时返回 None,让调用者知道无法写入 fstab。
|
||||
logger.warning(f"get_device_details_by_path: RAID 阵列 {original_device_path} (实际设备 {actual_md_device_path}) 未找到文件系统类型。")
|
||||
return None
|
||||
else:
|
||||
logger.warning(f"无法确定 RAID 阵列 {device_path} 的实际内核设备路径。")
|
||||
return None # 无法解析实际设备路径,也无法获取 fstype
|
||||
logger.warning(f"get_device_details_by_path: 无法确定 RAID 阵列 {original_device_path} 的实际内核设备路径。")
|
||||
return None
|
||||
|
||||
# 3. 如果仍然没有找到,返回 None
|
||||
logger.debug(f"未能获取到 {device_path} 的任何详情。")
|
||||
# 5. 如果仍然没有找到,返回 None
|
||||
logger.debug(f"get_device_details_by_path: 未能获取到 {original_device_path} 的任何详情。")
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user