feat: 新增第三方软件市场、离线守卫与UPS服务管理,优化NVIDIA vGPU策略

- 新增 Modules 第三方软件市场,支持元信息识别与执行前风险确认
- 新增离线模式守卫,网络不可达时展示诊断信息并要求用户确认
- 移除 NVIDIA vGPU 自动配置流程,改为全屏警告并引导至文档
- 新增 NVIDIA 驱动说明文档,解释为何不建议自动化安装
- 温度监控新增 UPS 服务管理,不启用时自动停止 apcupsd 服务
- 重构文档站主页,新增开发计划页面与时间线组件
- 更新版本至 6.8.0
This commit is contained in:
Maple
2026-03-19 00:44:27 +08:00
parent 0f2b4d0792
commit 7d61f1ebeb
14 changed files with 1242 additions and 264 deletions

Binary file not shown.

136
Modules/install-zsh.sh Normal file
View File

@@ -0,0 +1,136 @@
#!/bin/bash
## name:ZSH 安装脚本 v3.0
## author:Maple
## version:3.0
## github:#
# --- 颜色与符号 ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
NC='\033[0m'
CHECK="✔"
echo -e "${PURPLE}-----------------------------------------"
echo -e " ZSH Installer v3.0 | Made With Maple"
echo -e "-----------------------------------------${NC}"
# 1. 环境预检
[ ! -f /etc/debian_version ] && echo -e "${RED}错误: 仅支持 Debian 系系统${NC}" && exit 1
SUDO=$(command -v sudo)
# 2. 网络诊断与镜像选择
echo -e "\n${BLUE}[1/8] 正在诊断 GitHub 连通性... 请稍等...${NC}"
GITHUB_URL="https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/README.md"
MIRROR_PREFIX=""
if ! curl -I --connect-timeout 5 "$GITHUB_URL" > /dev/null 2>&1; then
echo -e "${YELLOW}检测到直连困难,已启用镜像加速模式。${NC}"
MIRROR_PREFIX="https://ghfast.top/" # 默认使用稳定性较好的镜像
fi
# 3. 安装核心依赖
echo -e "\n${BLUE}[2/8] 安装基础组件...${NC}"
$SUDO apt update && $SUDO apt install -y zsh git curl wget fontconfig locales
$SUDO locale-gen en_US.UTF-8
# 4. 字体安装逻辑 (针对桌面用户优化)
install_fonts_logic() {
echo -e "\n${BLUE}[3/8] 字体增强配置${NC}"
# 检测桌面环境
IS_DESKTOP=false
if [ -n "$DISPLAY" ] || [ -n "$WAYLAND_DISPLAY" ]; then
IS_DESKTOP=true
fi
if [ "$IS_DESKTOP" = true ] || [[ "$THEME" == *"powerlevel10k"* ]] || [[ "$THEME" == "agnoster" ]]; then
echo -e "${YELLOW}您选择了高级主题或处于桌面环境,建议安装 Nerd Fonts 以避免乱码。${NC}"
read -p "是否安装 JetBrainsMono Nerd Font? (y/n) [y]: " confirm_font
if [[ "$confirm_font" != "n" ]]; then
FONT_DIR="$HOME/.local/share/fonts"
mkdir -p "$FONT_DIR"
# 定义字体下载地址 (使用镜像)
FONT_URL="${MIRROR_PREFIX}https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip"
echo -e "${YELLOW}正在下载字体包 (约 30MB),请稍候...${NC}"
wget -q --show-progress -O /tmp/jb_font.zip "$FONT_URL"
if [ $? -eq 0 ]; then
$SUDO apt install -y unzip > /dev/null
unzip -o /tmp/jb_font.zip -d "$FONT_DIR" > /dev/null
fc-cache -fv > /dev/null
echo -e "${GREEN}${CHECK} 字体已安装至 $FONT_DIR 并刷新缓存。${NC}"
echo -e "${BLUE}请记得在终端设置中手动将字体改为 'JetBrainsMono Nerd Font'。${NC}"
else
echo -e "${RED}字体下载失败,跳过此步骤。${NC}"
fi
rm -f /tmp/jb_font.zip
fi
fi
if [ "$IS_DESKTOP" = false ]; then
echo -e "${YELLOW}非桌面环境,跳过字体安装。${NC}"
fi
}
# 5. 安装 Oh My Zsh
echo -e "\n${BLUE}[4/8] 安装 Oh My Zsh 框架...${NC}"
if [ -d "$HOME/.oh-my-zsh" ]; then
echo -e "${YELLOW}已检测到 Oh My Zsh跳过克隆。${NC}"
else
curl -fsSL "${MIRROR_PREFIX}https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh" > install_omz.sh
sed -i "s|https://github.com/|${MIRROR_PREFIX}https://github.com/|g" install_omz.sh
sh install_omz.sh --unattended --keep-zshrc
rm install_omz.sh
fi
# 6. 主题选择
echo -e "\n${BLUE}[5/8] 配置视觉主题${NC}"
echo "1) powerlevel10k (现代全能, 强烈推荐)"
echo "2) robbyrussell (默认, 极简)"
echo "3) agnoster (经典美观, 需字体支持)"
echo "4) ys (信息全面)"
read -p "选择主题编号 [默认 1]: " t_c
case $t_c in
2) THEME="robbyrussell" ;;
3) THEME="agnoster" ;;
4) THEME="ys" ;;
*) THEME="powerlevel10k/powerlevel10k"
git clone --depth=1 "${MIRROR_PREFIX}https://github.com/romkatv/powerlevel10k.git" ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k ;;
esac
# 运行字体安装逻辑
install_fonts_logic
# 7. 插件安装
echo -e "\n${BLUE}[6/8] 正在处理 Oh My Zsh 插件...${NC}"
ZSH_CUSTOM=${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}
echo -e "${YELLOW}提示: 以下插件将被启用:${NC}"
echo -e "如需修改,请编辑 ~/.zshrc 中的 plugins 行。"
PLUGINS_LIST="git zsh-autosuggestions zsh-syntax-highlighting sudo extract colored-man-pages"
# 克隆三方插件
echo -e "${YELLOW}提示: 正在安装 zsh-autosuggestions 和 zsh-syntax-highlighting 插件...${NC}"
git clone "${MIRROR_PREFIX}https://github.com/zsh-users/zsh-autosuggestions" "$ZSH_CUSTOM/plugins/zsh-autosuggestions" --quiet
git clone "${MIRROR_PREFIX}https://github.com/zsh-users/zsh-syntax-highlighting" "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" --quiet
# 8. 写入配置
echo -e "\n${BLUE}[7/8] 同步 .zshrc 配置...${NC}"
[ -f ~/.zshrc ] && cp ~/.zshrc ~/.zshrc.bak
sed -i "s/^ZSH_THEME=.*/ZSH_THEME=\"$THEME\"/" ~/.zshrc
sed -i "s/^plugins=(.*)/plugins=($PLUGINS_LIST)/" ~/.zshrc
# 9. 结束
echo -e "\n${BLUE}[8/8] 权限设置${NC}"
read -p "是否设为默认 Shell? (y/n) [y]: " confirm_chsh
if [[ "$confirm_chsh" != "n" ]]; then
$SUDO chsh -s $(which zsh) $(whoami)
fi
echo -e "\n${GREEN}✨ 配置完成!${NC}"
echo -e "1. 输入 ${YELLOW}zsh${NC} 立即体验。"
echo -e "2. 如果是桌面终端,请务必在设置中修改字体为 ${BLUE}JetBrainsMono Nerd Font${NC}"

View File

@@ -11,7 +11,7 @@
# 版本信息
CURRENT_VERSION="6.7.0"
CURRENT_VERSION="6.8.0"
VERSION_FILE_URL="https://raw.githubusercontent.com/Mapleawaa/PVE-Tools-9/main/VERSION"
UPDATE_FILE_URL="https://raw.githubusercontent.com/Mapleawaa/PVE-Tools-9/main/UPDATE"
PVE_VERSION_DETECTED=""
@@ -76,10 +76,17 @@ CF_TRACE_URL="https://www.cloudflare.com/cdn-cgi/trace"
GITHUB_MIRROR_PREFIX="https://ghfast.top/"
USE_MIRROR_FOR_UPDATE=0
USER_COUNTRY_CODE=""
NETWORK_MODE="auto"
IS_OFFLINE_MODE=0
# 快速虚拟机下载脚本配置
FASTPVE_INSTALLER_URL="https://raw.githubusercontent.com/kspeeder/fastpve/main/fastpve-install.sh"
FASTPVE_PROJECT_URL="https://github.com/kspeeder/fastpve"
THIRD_PARTY_MODULES_TREE_API_MAIN_URL="https://api.github.com/repos/Mapleawaa/PVE-Tools-9/git/trees/main?recursive=1"
THIRD_PARTY_MODULES_TREE_API_MASTER_URL="https://api.github.com/repos/Mapleawaa/PVE-Tools-9/git/trees/master?recursive=1"
THIRD_PARTY_MODULES_RAW_BASE_URL="https://raw.githubusercontent.com/Mapleawaa/PVE-Tools-9/main/Modules"
NVIDIA_ASSETS_BASE_URL="https://raw.githubusercontent.com/Mapleawaa/PVE-Tools-9/main/Modules/NVIDIA"
NVIDIA_VGPU_UNLOCK_SO_URL="${NVIDIA_ASSETS_BASE_URL}/libvgpu_unlock_rs.so"
# 日志函数
log_info() {
@@ -453,6 +460,85 @@ detect_network_region() {
return 0
}
network_show_diagnostics() {
echo "${UI_DIVIDER}"
echo -e "${CYAN}当前网络诊断信息:${NC}"
echo -e "${CYAN}IPv4 地址:${NC}"
ip -4 -o addr show scope global 2>/dev/null | awk '{print " "$2": "$4}' || true
echo -e "${CYAN}默认路由:${NC}"
ip route 2>/dev/null | sed -n '1,3p' | sed 's/^/ /' || true
echo -e "${CYAN}DNS 配置:${NC}"
grep -E '^\s*nameserver\s+' /etc/resolv.conf 2>/dev/null | sed 's/^/ /' || true
echo "${UI_DIVIDER}"
}
network_can_access_internet() {
local test_url="$VERSION_FILE_URL"
if command -v curl >/dev/null 2>&1; then
curl -fsSL --connect-timeout 5 --max-time 8 "$test_url" >/dev/null 2>&1
return $?
fi
if command -v wget >/dev/null 2>&1; then
wget -q --timeout=8 -O - "$test_url" >/dev/null 2>&1
return $?
fi
return 1
}
network_offline_guard() {
IS_OFFLINE_MODE=0
if [[ "$NETWORK_MODE" == "offline" ]]; then
IS_OFFLINE_MODE=1
log_warn "已配置为离线模式:将跳过在线更新检查与在线资源拉取。"
return 0
fi
if network_can_access_internet; then
log_success "网络连通性检测通过。"
return 0
fi
IS_OFFLINE_MODE=1
log_warn "检测到当前主机无法访问互联网,在线资源可能不可用。"
network_show_diagnostics
echo -e "${YELLOW}请先确认是否为本机网络问题网关、DNS、NAT、防火墙再继续。${NC}"
echo -e "${YELLOW}如果你确定当前环境需要离线使用,可继续,但涉及在线下载/更新的功能会失败。${NC}"
read -p "输入 'offline' 继续离线模式,其他任意键退出排查网络: " offline_confirm
if [[ "$offline_confirm" != "offline" ]]; then
log_info "已取消执行,请先修复网络后重试。"
exit 0
fi
return 0
}
disable_ups_service() {
if ! command -v systemctl >/dev/null 2>&1; then
log_warn "系统不支持 systemctl无法自动管理 UPS 服务"
return 1
fi
if ! systemctl list-unit-files 2>/dev/null | grep -q '^apcupsd\.service'; then
log_info "未检测到 apcupsd.service跳过 UPS 服务管理"
return 0
fi
systemctl stop apcupsd >/dev/null 2>&1 || true
systemctl disable apcupsd >/dev/null 2>&1 || true
log_success "已执行 UPS 服务关闭: systemctl stop/disable apcupsd"
return 0
}
enable_ups_service() {
if ! command -v systemctl >/dev/null 2>&1; then
return 1
fi
if ! systemctl list-unit-files 2>/dev/null | grep -q '^apcupsd\.service'; then
return 1
fi
systemctl enable apcupsd >/dev/null 2>&1 || true
systemctl start apcupsd >/dev/null 2>&1 || true
return 0
}
# 显示横幅
show_banner() {
clear
@@ -2786,9 +2872,15 @@ cpu_add() {
if [[ $REPLY =~ ^[Yy]$ ]]; then
enable_ups=true
log_success "已选择启用UPS监控"
if enable_ups_service; then
log_info "已启用并启动 apcupsd 服务"
else
log_warn "未能自动启用 apcupsd 服务UPS 信息可能为空"
fi
else
enable_ups=false
log_info "已选择跳过UPS监控"
disable_ups_service
fi
# 生成系统变量 (参考 PVE 8 脚本的改进实现)
@@ -3363,7 +3455,7 @@ EOF
log_info "修改页面高度"
# 统计添加了几条内容2个基础项 + NVME + SATA + UPS
if [ "$has_ups" = true ]; then
if [ "$enable_ups" = true ]; then
addRs=$((2 + nvi + sdi + 1))
ups_info="+ 1 个UPS"
else
@@ -4643,6 +4735,230 @@ fastpve_quick_download_menu() {
return $run_status
}
third_party_market_menu() {
local -a download_cmd
if command -v curl &> /dev/null; then
download_cmd=(curl -fsSL --connect-timeout 10 --max-time 60 -o)
elif command -v wget &> /dev/null; then
download_cmd=(wget -q -O)
else
log_error "未检测到 curl 或 wget无法访问第三方软件市场"
return 1
fi
local tmp_index
if ! tmp_index=$(mktemp /tmp/pve-third-party-index.XXXXXX.json); then
log_error "无法创建临时文件,第三方软件市场启动失败"
return 1
fi
local api_main_url="$THIRD_PARTY_MODULES_TREE_API_MAIN_URL"
local api_master_url="$THIRD_PARTY_MODULES_TREE_API_MASTER_URL"
local index_ok=0
log_info "正在通过 GitHub API 拉取第三方软件列表..."
if command -v curl &> /dev/null; then
if curl -fsSL --connect-timeout 10 --max-time 60 \
-H "Accept: application/vnd.github+json" \
-H "User-Agent: pve-tools" \
-o "$tmp_index" "$api_main_url"; then
index_ok=1
else
log_warn "main 分支列表拉取失败,尝试使用 master 分支..."
: > "$tmp_index"
if curl -fsSL --connect-timeout 10 --max-time 60 \
-H "Accept: application/vnd.github+json" \
-H "User-Agent: pve-tools" \
-o "$tmp_index" "$api_master_url"; then
index_ok=1
fi
fi
else
if wget -q --timeout=60 \
--header="Accept: application/vnd.github+json" \
--user-agent="pve-tools" \
-O "$tmp_index" "$api_main_url"; then
index_ok=1
else
log_warn "main 分支列表拉取失败,尝试使用 master 分支..."
: > "$tmp_index"
if wget -q --timeout=60 \
--header="Accept: application/vnd.github+json" \
--user-agent="pve-tools" \
-O "$tmp_index" "$api_master_url"; then
index_ok=1
fi
fi
fi
if [[ $index_ok -ne 1 ]]; then
log_error "第三方软件列表拉取失败,请稍后重试"
rm -f "$tmp_index"
return 1
fi
local -a module_files
while IFS= read -r module_name; do
[[ -z "$module_name" ]] && continue
module_files+=("$module_name")
done < <(grep -oE '"path":[[:space:]]*"Modules/[^"]+\.sh"' "$tmp_index" | sed -E 's#.*"path":[[:space:]]*"Modules/([^"]+)".*#\1#')
rm -f "$tmp_index"
if [[ ${#module_files[@]} -eq 0 ]]; then
log_warn "未在 Modules 目录发现可用的 .sh 第三方脚本"
return 1
fi
local -a valid_files valid_names valid_authors valid_versions valid_githubs
for module_file in "${module_files[@]}"; do
local module_url="${THIRD_PARTY_MODULES_RAW_BASE_URL}/${module_file}"
local module_mirror_url="${GITHUB_MIRROR_PREFIX}${module_url}"
local module_preferred_url="$module_url"
local module_fallback_url="$module_mirror_url"
if [[ $USE_MIRROR_FOR_UPDATE -eq 1 ]]; then
module_preferred_url="$module_mirror_url"
module_fallback_url="$module_url"
fi
local tmp_module
if ! tmp_module=$(mktemp /tmp/pve-third-party-meta.XXXXXX.sh); then
continue
fi
if ! "${download_cmd[@]}" "$tmp_module" "$module_preferred_url"; then
: > "$tmp_module"
if ! "${download_cmd[@]}" "$tmp_module" "$module_fallback_url"; then
rm -f "$tmp_module"
continue
fi
fi
local meta_block
meta_block=$(sed -n '2,5p' "$tmp_module")
rm -f "$tmp_module"
local script_name script_author script_version script_github
script_name=$(echo "$meta_block" | grep -m1 '^## name:' | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
script_author=$(echo "$meta_block" | grep -m1 '^## author:' | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
script_version=$(echo "$meta_block" | grep -m1 '^## version:' | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
script_github=$(echo "$meta_block" | grep -m1 '^## github:' | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [[ -z "$script_name" || -z "$script_author" || -z "$script_version" || -z "$script_github" ]]; then
continue
fi
valid_files+=("$module_file")
valid_names+=("$script_name")
valid_authors+=("$script_author")
valid_versions+=("$script_version")
valid_githubs+=("$script_github")
done
if [[ ${#valid_files[@]} -eq 0 ]]; then
log_warn "已发现 .sh 文件但没有符合元信息规范第2-5行的脚本"
return 1
fi
while true; do
clear
show_menu_header "第三方软件市场 (Modules)"
echo " 数据源: $THIRD_PARTY_MODULES_RAW_BASE_URL"
echo " 共发现 ${#valid_files[@]} 个符合规范的脚本"
echo "${UI_DIVIDER}"
local idx=1
while [[ $idx -le ${#valid_files[@]} ]]; do
local arr_idx=$((idx - 1))
echo -e " ${CYAN}${idx}.${NC} ${valid_names[$arr_idx]}"
echo " 作者: ${valid_authors[$arr_idx]} | 版本: ${valid_versions[$arr_idx]}"
echo " 脚本: ${valid_files[$arr_idx]}"
echo " 仓库: ${valid_githubs[$arr_idx]}"
((idx++))
done
echo "${UI_DIVIDER}"
show_menu_option "0" "返回上级菜单"
show_menu_footer
local choice
read -p "请选择要运行的脚本 [0-${#valid_files[@]}]: " choice
if [[ "$choice" == "0" ]]; then
return 0
fi
if [[ ! "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#valid_files[@]} )); then
log_error "无效选择"
pause_function
continue
fi
local selected_idx=$((choice - 1))
local selected_file="${valid_files[$selected_idx]}"
local selected_name="${valid_names[$selected_idx]}"
local selected_author="${valid_authors[$selected_idx]}"
local selected_version="${valid_versions[$selected_idx]}"
local selected_url="${THIRD_PARTY_MODULES_RAW_BASE_URL}/${selected_file}"
local selected_mirror_url="${GITHUB_MIRROR_PREFIX}${selected_url}"
local selected_preferred_url="$selected_url"
local selected_fallback_url="$selected_mirror_url"
local selected_preferred_label="GitHub"
local selected_fallback_label="加速镜像"
if [[ $USE_MIRROR_FOR_UPDATE -eq 1 ]]; then
selected_preferred_url="$selected_mirror_url"
selected_fallback_url="$selected_url"
selected_preferred_label="加速镜像"
selected_fallback_label="GitHub"
fi
echo
echo -e "${RED}⚠️ 第三方脚本风险提示:${NC}"
echo " 名称: $selected_name"
echo " 作者: $selected_author"
echo " 版本: $selected_version"
echo " 来源: $selected_url"
echo " 本工具仅负责下载和执行,请确认你已审计脚本内容并接受风险。"
read -p "输入 'run' 确认执行,其他任意键取消: " confirm_run
if [[ "$confirm_run" != "run" ]]; then
log_info "已取消执行 $selected_name"
pause_function
continue
fi
local tmp_script
if ! tmp_script=$(mktemp /tmp/pve-third-party-run.XXXXXX.sh); then
log_error "无法创建临时脚本文件"
pause_function
continue
fi
log_info "使用 $selected_preferred_label 下载脚本 ($selected_file)..."
if ! "${download_cmd[@]}" "$tmp_script" "$selected_preferred_url"; then
log_warn "$selected_preferred_label 下载失败,尝试改用 $selected_fallback_label..."
: > "$tmp_script"
if ! "${download_cmd[@]}" "$tmp_script" "$selected_fallback_url"; then
log_error "脚本下载失败: $selected_file"
rm -f "$tmp_script"
pause_function
continue
fi
fi
chmod +x "$tmp_script"
echo "${UI_BORDER}"
sh "$tmp_script"
local run_status=$?
echo "${UI_BORDER}"
rm -f "$tmp_script"
if [[ $run_status -eq 0 ]]; then
log_success "$selected_name 执行完成"
else
log_error "$selected_name 执行失败 (退出码: $run_status)"
fi
pause_function
done
}
#---------FastPVE 虚拟机快速下载-----------
# 社区第三方工具集合提示
@@ -5179,7 +5495,7 @@ menu_gpu_passthrough() {
show_menu_header "直通与显卡"
show_menu_option "1" "Intel 核显虚拟化管理 (SR-IOV/GVT-g)"
show_menu_option "2" "Intel 核显直通配置 (修改版 QEMU)"
show_menu_option "3" "NVIDIA 显卡直通/虚拟化 (开发中)"
show_menu_option "3" "NVIDIA 显卡直通/虚拟化"
show_menu_option "4" "硬件直通一键配置 (IOMMU)"
show_menu_option "5" "磁盘/控制器直通 (RDM/PCIe/NVMe)"
show_menu_option "0" "返回主菜单"
@@ -5607,18 +5923,20 @@ menu_vm_container() {
clear
show_menu_header "虚拟机与容器"
show_menu_option "1" "${CYAN}FastPVE${NC} - 虚拟机快速下载"
show_menu_option "2" "${CYAN}Community Scripts${NC} - 第三方工具集"
show_menu_option "3" "虚拟机/容器定时开关机"
show_menu_option "4" "IMG 镜像导入(转 QCOW2/RAW"
show_menu_option "2" "第三方软件市场 (Modules)"
show_menu_option "3" "${CYAN}Community Scripts${NC} - 第三方工具集"
show_menu_option "4" "虚拟机/容器定时开关机"
show_menu_option "5" "IMG 镜像导入(转 QCOW2/RAW"
echo "$UI_DIVIDER"
show_menu_option "0" "返回主菜单"
show_menu_footer
read -p "请选择操作 [0-4]: " choice
read -p "请选择操作 [0-5]: " choice
case $choice in
1) fastpve_quick_download_menu ;;
2) third_party_tools_menu ;;
3) manage_vm_schedule ;;
4) img_convert_import_menu ;;
2) third_party_market_menu ;;
3) third_party_tools_menu ;;
4) manage_vm_schedule ;;
5) img_convert_import_menu ;;
0) return ;;
*) log_error "无效选择" ;;
esac
@@ -5904,11 +6222,12 @@ temp_monitoring_menu() {
show_menu_option "1" "配置温度监控 ${CYAN}(CPU/硬盘温度显示)${NC}"
show_menu_option "2" "${RED}移除温度监控${NC} (移除温度监控功能)"
show_menu_option "3" "自定义温度监控选项 ${MAGENTA}(高级)${NC}"
show_menu_option "4" "UPS 服务管理 ${CYAN}(apcupsd)${NC}"
echo "${UI_DIVIDER}"
show_menu_option "0" "返回上级菜单"
show_menu_footer
echo
read -p "请选择 [0-3]: " temp_choice
read -p "请选择 [0-4]: " temp_choice
echo
case $temp_choice in
@@ -5921,6 +6240,26 @@ temp_monitoring_menu() {
3)
custom_temp_monitoring
;;
4)
if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files 2>/dev/null | grep -q '^apcupsd\.service'; then
local active_state enabled_state
active_state=$(systemctl is-active apcupsd 2>/dev/null || echo unknown)
enabled_state=$(systemctl is-enabled apcupsd 2>/dev/null || echo unknown)
echo "当前 UPS 服务状态: active=${active_state}, enabled=${enabled_state}"
echo "1) 禁用 UPS 服务stop + disable"
echo "2) 启用 UPS 服务enable + start"
echo "0) 返回"
read -p "请选择 [0-2]: " ups_choice
case "$ups_choice" in
1) disable_ups_service ;;
2) enable_ups_service && log_success "已启用 UPS 服务" || log_warn "启用 UPS 服务失败,请手工检查" ;;
0) ;;
*) log_error "无效选择" ;;
esac
else
log_warn "未检测到 apcupsd.service无法管理 UPS 服务"
fi
;;
0)
break
;;
@@ -6152,11 +6491,11 @@ restore_qemu_kvm() {
#英特尔核显直通
intel_gpu_passthrough() {
log_step "开始 Intel 核显直通配置"
echo "注意:此功能基于 lixiaoliu666 的修改版 QEMU 和 ROM"
echo "注意:此功能基于 AICodo 的修改版 QEMU 和 ROM"
echo "详细原理与教程https://pve.u3u.icu/advanced/gpu-passthrough"
echo "适用于需要将 Intel 核显直通给 Windows 虚拟机且遇到代码 43 或黑屏的情况"
echo "支持的 CPU 架构6代(Skylake) 到 14代(Raptor Lake Refresh)"
echo "项目地址https://github.com/lixiaoliu666/intel6-14rom"
echo "项目地址https://github.com/AICodo/intel6-14rom"
echo
log_warn "警告"
log_warn "本功能并非能100%一次成功!"
@@ -6170,7 +6509,7 @@ intel_gpu_passthrough() {
log_tips "如果配置失败,请访问文档站查看详细教程并留言反馈:"
log_tips "🔗 https://pve.u3u.icu/advanced/gpu-passthrough"
echo
log_tips "如需要反馈或者请求更新ROM文件适配你的CPU请前往lixiaoliu666的GitHub仓库开ISSUE反馈不是找我。"
log_tips "如需要反馈或者请求更新ROM文件适配你的CPU请前往AICodo的GitHub仓库开ISSUE反馈不是找我。"
echo
echo "请选择操作:"
@@ -6215,18 +6554,18 @@ intel_gpu_passthrough() {
echo "正在获取最新 release 版本..."
# 尝试获取最新下载链接 (这里为了稳定性暂时写死或使用最新已知的逻辑,实际可爬虫获取)
# 根据用户提供的信息,修改版 QEMU 下载地址: https://github.com/lixiaoliu666/pve-anti-detection/releases
# 根据用户提供的信息,修改版 QEMU 下载地址: https://github.com/AICodo/pve-anti-detection/releases
# 为了简化,我们使用 ghfast.top 加速下载最新的 release
# 注意:这里需要动态获取最新 deb 包链接,或者让用户手动输入链接
# 为方便起见,这里演示自动获取逻辑
local qemu_releases_url="https://api.github.com/repos/lixiaoliu666/pve-anti-detection/releases/latest"
local qemu_releases_url="https://api.github.com/repos/AICodo/pve-anti-detection/releases/latest"
local qemu_deb_url=$(curl -s $qemu_releases_url | grep "browser_download_url.*deb" | cut -d '"' -f 4 | head -n 1)
if [ -z "$qemu_deb_url" ]; then
log_warn "无法自动获取修改版 QEMU 下载链接,尝试使用备用链接或手动下载"
# 备用逻辑:提示用户手动下载
echo "请访问 https://github.com/lixiaoliu666/pve-anti-detection/releases 下载最新 deb 包"
echo "请访问 https://github.com/AICodo/pve-anti-detection/releases 下载最新 deb 包"
echo "然后使用 dpkg -i 安装"
else
# 加速下载
@@ -6284,7 +6623,7 @@ intel_gpu_passthrough() {
done
# 下载 ROM 文件
local rom_releases_url="https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/latest"
local rom_releases_url="https://api.github.com/repos/AICodo/intel6-14rom/releases/latest"
log_info "正在获取 ROM 列表..."
# 获取 release 信息
@@ -6436,9 +6775,10 @@ nvidia_t() {
MENU_TITLE) echo "NVIDIA 显卡管理" ;;
MENU_DESC) echo "请选择功能模块(高风险操作会强制二次确认)" ;;
OPT_PT) echo "显卡直通虚拟机" ;;
OPT_VGPU) echo "vGPU 配置与分配" ;;
OPT_DRV_INFO) echo "驱动信息与监控" ;;
OPT_DRV_SWITCH) echo "驱动切换(开源/闭源)" ;;
OPT_HOST_PREP) echo "宿主机预配置IOMMU/VFIO/黑名单)" ;;
OPT_UNLOCK) echo "部署 vGPU Unlock外部库" ;;
OPT_BACK) echo "返回" ;;
ERR_NO_GPU) echo "未检测到 NVIDIA GPU" ;;
ERR_IOMMU) echo "未检测到 IOMMU 已开启" ;;
@@ -6759,226 +7099,6 @@ nvidia_gpu_passthrough_vm() {
return 0
}
nvidia_vgpu_list_types() {
if [[ ! -d /sys/class/mdev_bus ]]; then
return 1
fi
find /sys/class/mdev_bus -maxdepth 4 -type d -name mdev_supported_types 2>/dev/null | while read -r d; do
find "$d" -maxdepth 1 -mindepth 1 -type d 2>/dev/null
done
}
nvidia_vgpu_show_license() {
local conf="/etc/nvidia/gridd.conf"
if [[ -f "$conf" ]]; then
echo -e "${CYAN}gridd.conf:${NC} $conf"
grep -E '^(ServerAddress|ServerPort|FeatureType|EnableUI)=' "$conf" 2>/dev/null | sed 's/^/ /'
fi
if command -v systemctl >/dev/null 2>&1; then
systemctl is-enabled nvidia-gridd >/dev/null 2>&1 && echo -e "${CYAN}nvidia-gridd:${NC} enabled" || true
systemctl is-active nvidia-gridd >/dev/null 2>&1 && echo -e "${CYAN}nvidia-gridd:${NC} active" || true
fi
if command -v nvidia-smi >/dev/null 2>&1; then
nvidia-smi -q 2>/dev/null | grep -Ei 'License|vGPU' | head -n 30 | sed 's/^/ /' || true
fi
}
nvidia_vgpu_update_license() {
local conf="/etc/nvidia/gridd.conf"
if [[ ! -f "$conf" ]]; then
display_error "未找到 gridd.conf" "请先安装 NVIDIA vGPU 驱动/组件后再配置许可证。"
return 1
fi
local addr port
read -p "许可证服务器地址(例: 1.2.3.4 或 lic.example.com: " addr
read -p "许可证服务器端口 [7070]: " port
port="${port:-7070}"
if [[ -z "$addr" ]]; then
display_error "地址不能为空"
return 1
fi
if [[ ! "$port" =~ ^[0-9]+$ || "$port" -lt 1 || "$port" -gt 65535 ]]; then
display_error "端口不合法: $port"
return 1
fi
if ! confirm_action "更新 vGPU 许可证服务器配置并重启 nvidia-gridd"; then
return 0
fi
backup_file "$conf" >/dev/null 2>&1 || true
if grep -q '^ServerAddress=' "$conf"; then
sed -i "s/^ServerAddress=.*/ServerAddress=${addr}/" "$conf"
else
echo "ServerAddress=${addr}" >> "$conf"
fi
if grep -q '^ServerPort=' "$conf"; then
sed -i "s/^ServerPort=.*/ServerPort=${port}/" "$conf"
else
echo "ServerPort=${port}" >> "$conf"
fi
if command -v systemctl >/dev/null 2>&1; then
systemctl restart nvidia-gridd >/dev/null 2>&1 || true
fi
display_success "许可证配置已更新"
return 0
}
nvidia_vgpu_assign_to_vm() {
log_step "$(nvidia_t OPT_VGPU)"
if ! iommu_is_enabled; then
display_error "$(nvidia_t ERR_IOMMU)" "$(nvidia_t TIP_ENABLE_IOMMU)"
return 1
fi
if [[ ! -d /sys/class/mdev_bus ]]; then
display_error "未检测到 mdev 支持" "请确认内核与硬件支持 mediated device并且已加载相关驱动。"
return 1
fi
local vmid
vmid="$(nvidia_select_vmid)"
local rc=$?
if [[ "$rc" -eq 2 ]]; then
return 0
fi
if [[ -z "$vmid" ]]; then
return 1
fi
local gpu_bdf
gpu_bdf="$(nvidia_select_gpu_bdf)"
rc=$?
if [[ "$rc" -eq 2 ]]; then
return 0
fi
if [[ -z "$gpu_bdf" ]]; then
return 1
fi
local base_sysfs="/sys/bus/pci/devices/${gpu_bdf}/mdev_supported_types"
if [[ ! -d "$base_sysfs" ]]; then
display_error "该 GPU 未提供 mdev_supported_types" "该卡可能不支持 vGPU/mdev或驱动未正确加载。"
return 1
fi
local types
types="$(find "$base_sysfs" -maxdepth 1 -mindepth 1 -type d 2>/dev/null)"
if [[ -z "$types" ]]; then
display_error "未发现可用 vGPU 类型" "请确认 vGPU 驱动已安装,并且该设备支持 vGPU。"
return 1
fi
echo -e "${CYAN}可用 vGPU 类型:${NC}"
echo "$types" | awk -v base="$base_sysfs" '{
type=$0;
n=split(type,a,"/");
id=a[n];
name_file=type"/name";
avail_file=type"/available_instances";
name="";
avail="";
if ((getline l < name_file) > 0) name=l;
close(name_file);
if ((getline k < avail_file) > 0) avail=k;
close(avail_file);
printf " [%d] %s | %s | available=%s\n", NR, id, name, avail
}'
echo -e "${UI_DIVIDER}"
local pick
read -p "$(nvidia_t INPUT_PICK) (0 返回): " pick
pick="${pick:-0}"
if [[ "$pick" == "0" ]]; then
return 0
fi
if [[ ! "$pick" =~ ^[0-9]+$ ]]; then
display_error "序号必须是数字"
return 1
fi
local type_path
type_path="$(echo "$types" | awk -v n="$pick" 'NR==n{print $0}')"
if [[ -z "$type_path" ]]; then
display_error "无效选择"
return 1
fi
local avail
avail="$(cat "${type_path}/available_instances" 2>/dev/null || echo 0)"
if [[ ! "$avail" =~ ^[0-9]+$ || "$avail" -le 0 ]]; then
display_error "该类型无可用实例" "请释放已有 vGPU 实例,或选择其他类型。"
return 1
fi
local uuid
uuid="$(cat /proc/sys/kernel/random/uuid 2>/dev/null || true)"
if [[ -z "$uuid" ]]; then
display_error "无法生成 UUID"
return 1
fi
if ! confirm_action "创建 vGPU 实例并分配给 VM $vmid"; then
return 0
fi
if ! echo "$uuid" > "${type_path}/create" 2>/dev/null; then
display_error "vGPU 实例创建失败" "请检查驱动/权限,并确认该类型可用。"
return 1
fi
local idx
idx="$(qm_find_free_hostpci_index "$vmid" 2>/dev/null)" || {
display_error "已创建 vGPU 实例,但未找到可用 hostpci 插槽" "请手工将 mdev=$uuid 添加到 VM。"
return 1
}
local value="${gpu_bdf},mdev=${uuid}"
if qm_is_q35_machine "$vmid"; then
value="${value},pcie=1"
fi
local conf_path
conf_path="$(get_qm_conf_path "$vmid")"
if [[ -f "$conf_path" ]]; then
backup_file "$conf_path" >/dev/null 2>&1 || true
fi
if ! qm set "$vmid" "-hostpci${idx}" "$value" >/dev/null 2>&1; then
display_error "qm set 写入失败" "请手工添加 hostpci${idx}: ${value}"
return 1
fi
display_success "$(nvidia_t OK_DONE)" "已创建并绑定 mdev=${uuid},如 VM 运行中请重启 VM。"
return 0
}
nvidia_vgpu_menu() {
while true; do
clear
show_menu_header "$(nvidia_t OPT_VGPU)"
show_menu_option "1" "vGPU 类型选择与分配"
show_menu_option "2" "vGPU 许可证状态"
show_menu_option "3" "更新 vGPU 许可证配置"
show_menu_option "0" "$(nvidia_t OPT_BACK)"
show_menu_footer
read -p "$(nvidia_t INPUT_CHOICE) [0-3]: " choice
case "$choice" in
1) nvidia_vgpu_assign_to_vm ;;
2) clear; show_menu_header "$(nvidia_t OPT_VGPU)"; nvidia_vgpu_show_license ;;
3) nvidia_vgpu_update_license ;;
0) return ;;
*) log_error "无效选择" ;;
esac
pause_function
done
}
nvidia_driver_info() {
clear
show_menu_header "$(nvidia_t OPT_DRV_INFO)"
@@ -7185,6 +7305,149 @@ nvidia_driver_switch_menu() {
done
}
nvidia_host_prepare_for_passthrough() {
echo -e "${YELLOW}将执行以下操作:${NC}"
echo " 1) 写入 GRUB IOMMU 参数"
echo " 2) 写入 /etc/modules 的 VFIO 模块配置块"
echo " 3) 写入 /etc/modprobe.d/pve-blacklist.conf 的 NVIDIA 黑名单配置块"
echo " 4) 执行 update-grub 与 update-initramfs"
echo
if ! confirm_action "确认执行宿主机预配置?"; then
return 0
fi
local cpu_vendor
cpu_vendor="$(grep -m1 'vendor_id' /proc/cpuinfo 2>/dev/null | awk '{print $3}')"
if [[ "$cpu_vendor" == "GenuineIntel" ]]; then
grub_add_param "intel_iommu=on"
elif [[ "$cpu_vendor" == "AuthenticAMD" ]]; then
grub_add_param "amd_iommu=on"
else
log_warn "未识别 CPU 厂商,跳过厂商特定 IOMMU 参数"
fi
grub_add_param "iommu=pt"
grub_add_param "pcie_acs_override=downstream,multifunction"
local modules_content
modules_content=$(cat <<'EOF'
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
EOF
)
apply_block "/etc/modules" "NVIDIA_VFIO_MODULES" "$modules_content"
local blacklist_content
blacklist_content=$(cat <<'EOF'
blacklist nouveau
blacklist nvidia
blacklist nvidiafb
options vfio_iommu_type1 allow_unsafe_interrupts=1
EOF
)
apply_block "/etc/modprobe.d/pve-blacklist.conf" "NVIDIA_BLACKLIST" "$blacklist_content"
if command -v update-grub >/dev/null 2>&1; then
update-grub || log_warn "update-grub 执行失败,请手工检查"
elif command -v grub-mkconfig >/dev/null 2>&1; then
grub-mkconfig -o /boot/grub/grub.cfg || log_warn "grub-mkconfig 执行失败,请手工检查"
else
log_warn "未找到 update-grub/grub-mkconfig请手工更新 GRUB"
fi
update-initramfs -u -k all || log_warn "update-initramfs 执行失败,请手工检查"
display_success "宿主机预配置已完成" "建议重启宿主机后再执行直通或 vGPU 操作。"
if confirm_action "是否现在重启宿主机?"; then
reboot
fi
return 0
}
nvidia_setup_vgpu_unlock() {
clear
show_menu_header "vGPU Unlock 高风险提示"
echo -e "${RED} 请先阅读文档后再操作。${NC}"
echo " 本功能会修改 NVIDIA vGPU 服务启动参数并加载外部 .so 文件。"
echo " 驱动/内核/补丁版本不匹配可能导致服务异常、宿主机告警或 VM 无法使用 vGPU。"
echo
echo -e "${CYAN}推荐先阅读 Wiki${NC}"
echo " 对应文章: https://pve.u3u.icu/advanced/nvidia-vgpu-driver-notes"
echo "${UI_DIVIDER}"
read -p "请输入 '确认' 或 'Sure' 继续: " response
response=$(echo "$response" | xargs)
if [[ "$response" != "确认" && "$response" != "Sure" && "${response,,}" != "sure" ]]; then
echo "取消"
return 0
fi
local default_url="$NVIDIA_VGPU_UNLOCK_SO_URL"
local so_url
read -p "请输入 libvgpu_unlock_rs.so 下载地址 [$default_url]: " so_url
so_url="${so_url:-$default_url}"
if [[ -z "$so_url" ]]; then
display_error "下载地址不能为空"
return 1
fi
echo -e "${YELLOW}将创建并写入:${NC}"
echo " /etc/vgpu_unlock/profile_override.toml"
echo " /etc/systemd/system/nvidia-vgpud.service.d/vgpu_unlock.conf"
echo " /etc/systemd/system/nvidia-vgpu-mgr.service.d/vgpu_unlock.conf"
echo " /opt/vgpu_unlock-rs/target/release/libvgpu_unlock_rs.so"
echo
if ! confirm_action "确认部署 vGPU Unlock外部库"; then
return 0
fi
mkdir -p /etc/vgpu_unlock
touch /etc/vgpu_unlock/profile_override.toml
mkdir -p /etc/systemd/system/nvidia-vgpud.service.d
mkdir -p /etc/systemd/system/nvidia-vgpu-mgr.service.d
mkdir -p /opt/vgpu_unlock-rs/target/release
local unlock_conf
unlock_conf=$(cat <<'EOF'
[Service]
Environment=LD_PRELOAD=/opt/vgpu_unlock-rs/target/release/libvgpu_unlock_rs.so
EOF
)
apply_block "/etc/systemd/system/nvidia-vgpud.service.d/vgpu_unlock.conf" "NVIDIA_VGPU_UNLOCK" "$unlock_conf"
apply_block "/etc/systemd/system/nvidia-vgpu-mgr.service.d/vgpu_unlock.conf" "NVIDIA_VGPU_UNLOCK" "$unlock_conf"
local so_out="/opt/vgpu_unlock-rs/target/release/libvgpu_unlock_rs.so"
if command -v curl >/dev/null 2>&1; then
if ! curl -fsSL --connect-timeout 15 --max-time 300 -o "$so_out" "$so_url"; then
display_error "下载失败" "请检查 URL 与网络连接。"
return 1
fi
elif command -v wget >/dev/null 2>&1; then
if ! wget -q -O "$so_out" "$so_url"; then
display_error "下载失败" "请检查 URL 与网络连接。"
return 1
fi
else
display_error "未检测到 curl 或 wget" "无法下载外部库文件。"
return 1
fi
if [[ ! -s "$so_out" ]]; then
display_error "下载结果为空" "请检查 URL 是否可访问。"
return 1
fi
systemctl daemon-reload >/dev/null 2>&1 || true
systemctl restart nvidia-vgpud.service >/dev/null 2>&1 || true
systemctl restart nvidia-vgpu-mgr.service >/dev/null 2>&1 || true
display_success "vGPU Unlock 已部署" "可执行 systemctl status nvidia-vgpud nvidia-vgpu-mgr 验证状态。"
return 0
}
nvidia_gpu_management_menu() {
while true; do
clear
@@ -7192,17 +7455,19 @@ nvidia_gpu_management_menu() {
echo -e "${CYAN}$(nvidia_t MENU_DESC)${NC}"
echo -e "${UI_DIVIDER}"
show_menu_option "1" "$(nvidia_t OPT_PT)"
show_menu_option "2" "$(nvidia_t OPT_VGPU)"
show_menu_option "3" "$(nvidia_t OPT_DRV_INFO)"
show_menu_option "4" "$(nvidia_t OPT_DRV_SWITCH)"
show_menu_option "2" "$(nvidia_t OPT_DRV_INFO)"
show_menu_option "3" "$(nvidia_t OPT_DRV_SWITCH)"
show_menu_option "4" "$(nvidia_t OPT_HOST_PREP)"
show_menu_option "5" "$(nvidia_t OPT_UNLOCK)"
show_menu_option "0" "$(nvidia_t OPT_BACK)"
show_menu_footer
read -p "$(nvidia_t INPUT_CHOICE) [0-4]: " choice
read -p "$(nvidia_t INPUT_CHOICE) [0-5]: " choice
case "$choice" in
1) nvidia_gpu_passthrough_vm ;;
2) nvidia_vgpu_menu ;;
3) nvidia_driver_info_menu ;;
4) nvidia_driver_switch_menu ;;
2) nvidia_driver_info_menu ;;
3) nvidia_driver_switch_menu ;;
4) nvidia_host_prepare_for_passthrough ;;
5) nvidia_setup_vgpu_unlock ;;
0) return ;;
*) log_error "无效选择" ;;
esac
@@ -7216,12 +7481,14 @@ main() {
ensure_legal_acceptance
check_debug_mode "$@"
check_pve_version
# 检查更新
check_update
# 选择镜像源
select_mirror
network_offline_guard
if [[ "$IS_OFFLINE_MODE" -eq 1 ]]; then
log_warn "离线模式下将跳过更新检查与镜像自动策略。"
else
check_update
select_mirror
fi
while true; do

9
UPDATE
View File

@@ -1,4 +1,5 @@
fix: 修复温控概览布局与恢复逻辑,增强磁盘 SMART 展示
- 移除温控功能对节点概览右侧 `minHeight` 的强制拉伸,修复硬盘较多时 CPU / Memory 折线图被异常拉高的问题
- 温控恢复与 Web UI 恢复改为通过 `apt-get install --reinstall pve-manager proxmox-widget-toolkit` 恢复官方文件,避免多次补丁后 `.bak` 文件不再纯净
- 为 CPU / NVMe / SATA 温度增加颜色显示,新增 NVMe `异常断电` 指标,并为 SATA SMART 属性 `174/192` 提供异常断电兜底显示
feat: 第三方市场、离线守卫与 NVIDIA 文档引导增强
- 新增 Modules 第三方软件市场,支持元信息识别与风险确认执行
- NVIDIA 移除 vGPU 自动配置vGPU Unlock 改为先全屏警告并引导 Wiki
- 新增离线模式网络诊断IP/路由/DNS与离线确认流程离线时跳过在线更新
- 温度监控新增 UPS 服务管理;不启用 UPS 显示时自动 stop/disable apcupsd

View File

@@ -1 +1 @@
6.7.0
6.8.0

View File

@@ -25,6 +25,7 @@ export default defineConfig({
{ text: '使用指南', link: '/guide' },
{ text: '高级教程', link: '/advanced/' },
{ text: '更新日志', link: '/update' },
{ text: '开发计划', link: '/todo' },
{ text: 'TOS', link: '/tos' },
{ text: 'ULA', link: '/ula' },
{ text: 'GitHub', link: 'https://github.com/Mapleawaa/PVE-Tools-9' }
@@ -36,6 +37,7 @@ export default defineConfig({
{ text: '简介', link: '/guide' },
{ text: '功能特性', link: '/features' },
{ text: '更新日志', link: '/update' },
{ text: '开发计划', link: '/todo' },
{ text: '服务条款TOS', link: '/tos' },
{ text: '最终用户许可ULA', link: '/ula' },
{ text: '常见问题', link: '/faq' }
@@ -49,7 +51,8 @@ export default defineConfig({
{ text: '核显虚拟化 SR-IOV', link: '/advanced/gpu-virtualization' },
{ text: 'CPU 性能调优', link: '/advanced/cpu-optimization' },
{ text: 'PVE 8 升级 9', link: '/advanced/pve-upgrade' },
{ text: '存储管理与休眠', link: '/advanced/storage-management' }
{ text: '存储管理与休眠', link: '/advanced/storage-management' },
{ text: 'NVIDIA vGPU 驱动说明', link: '/advanced/nvidia-vgpu-driver-notes' }
]
}
],

View File

@@ -0,0 +1,240 @@
<script setup>
import { useData } from 'vitepress'
import todoData from '../../../todo-data.json'
const { frontmatter } = useData()
</script>
<template>
<div v-if="frontmatter.my_features" class="home-content-container">
<div class="features-wrapper">
<div class="features-grid">
<div v-for="feature in frontmatter.my_features" :key="feature.title" class="feature-card">
<div class="feature-icon" :class="feature.iconClass"></div>
<h2 class="feature-title">{{ feature.title }}</h2>
<p class="feature-details">{{ feature.details }}</p>
</div>
</div>
</div>
<div class="timeline-wrapper">
<div class="timeline-box">
<div class="timeline-header">
<h2 class="timeline-box-title">更新时间线</h2>
<a href="/todo" class="timeline-more">更多计划 &gt;</a>
</div>
<div class="timeline-list">
<div v-for="(event, index) in todoData.timeline.slice(0, 5)" :key="index" class="timeline-item">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-date">{{ event.date }}</div>
<div class="timeline-event">{{ event.event }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.home-content-container {
display: flex;
flex-direction: row;
gap: 32px;
max-width: 1152px;
margin: 0 auto;
padding: 48px 24px;
}
.features-wrapper {
flex: 2;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
}
.feature-card {
background-color: var(--vp-c-bg-soft);
padding: 24px;
border-radius: 12px;
border: 1px solid var(--vp-c-bg-soft);
transition: border-color 0.25s, background-color 0.25s;
height: 100%;
}
.feature-card:hover {
border-color: var(--vp-c-brand);
}
/* 这里的图标样式尽量向 VitePress 官方靠拢,并配合 custom.css */
.feature-icon {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
margin-bottom: 20px;
border-radius: 8px;
background-color: var(--vp-c-default-soft);
transition: all 0.25s;
color: var(--custom-icon-color);
}
.feature-card:hover .feature-icon {
color: var(--vp-c-brand);
}
.feature-icon::before {
content: '';
width: 24px;
height: 24px;
background-color: currentColor;
-webkit-mask-size: contain;
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
}
/* 图标映射 - 这里的类名需要在 index.md 中指定 */
.icon-rocket::before {
-webkit-mask-image: url('https://api.iconify.design/mdi:rocket-launch-outline.svg');
mask-image: url('https://api.iconify.design/mdi:rocket-launch-outline.svg');
}
.icon-sparkles::before {
-webkit-mask-image: url('https://api.iconify.design/mdi:sparkles.svg');
mask-image: url('https://api.iconify.design/mdi:sparkles.svg');
}
.icon-harddrive::before {
-webkit-mask-image: url('https://api.iconify.design/mdi:harddisk.svg');
mask-image: url('https://api.iconify.design/mdi:harddisk.svg');
}
.icon-gpu::before {
-webkit-mask-image: url('https://api.iconify.design/mdi:video-card.svg');
mask-image: url('https://api.iconify.design/mdi:video-card.svg');
}
.icon-clock::before {
-webkit-mask-image: url('https://api.iconify.design/mdi:clock-outline.svg');
mask-image: url('https://api.iconify.design/mdi:clock-outline.svg');
}
.icon-more::before {
-webkit-mask-image: url('https://api.iconify.design/mdi:dots-horizontal.svg');
mask-image: url('https://api.iconify.design/mdi:dots-horizontal.svg');
}
.feature-title {
font-size: 1.15rem;
font-weight: 600;
margin-bottom: 8px;
line-height: 1.4;
background: none;
-webkit-text-fill-color: initial;
}
.feature-details {
font-size: 0.9rem;
color: var(--vp-c-text-2);
line-height: 1.6;
}
/* Timeline side */
.timeline-wrapper {
flex: 1;
}
.timeline-box {
background-color: var(--vp-c-bg-soft);
padding: 24px;
border-radius: 12px;
border: 1px solid var(--vp-c-bg-soft);
height: fit-content;
position: sticky;
top: 80px;
}
.timeline-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid var(--vp-c-divider);
}
.timeline-box-title {
font-size: 1.15rem;
font-weight: 600;
margin: 0;
background: none;
-webkit-text-fill-color: initial;
}
.timeline-more {
font-size: 0.8rem;
color: var(--vp-c-brand);
text-decoration: none;
font-weight: 500;
}
.timeline-more:hover {
opacity: 0.8;
}
.timeline-list {
position: relative;
padding-left: 16px;
border-left: 1px solid var(--vp-c-divider);
margin-left: 8px;
}
.timeline-item {
position: relative;
margin-bottom: 20px;
}
.timeline-item:last-child {
margin-bottom: 0;
}
.timeline-dot {
position: absolute;
left: -21px;
top: 6px;
width: 9px;
height: 9px;
background-color: var(--vp-c-brand);
border-radius: 50%;
border: 2px solid var(--vp-c-bg-soft);
}
.timeline-date {
font-size: 0.75rem;
font-weight: 600;
color: var(--vp-c-brand);
margin-bottom: 4px;
}
.timeline-event {
font-size: 0.85rem;
font-weight: 500;
color: var(--vp-c-text-1);
line-height: 1.5;
}
/* Responsive */
@media (max-width: 960px) {
.home-content-container {
flex-direction: column;
padding: 24px;
}
.timeline-box {
position: static;
}
}
</style>

View File

@@ -0,0 +1,155 @@
<script setup>
import todoData from '../../../todo-data.json'
</script>
<template>
<div class="todo-container">
<section class="todo-section">
<h2 class="section-title">即将加入的功能</h2>
<div class="todo-list">
<div v-for="(item, index) in todoData.upcoming_features" :key="index" class="todo-item">
<div class="item-header">
<span class="status-badge" :class="item.status">{{ item.status === 'completed' ? '已完成' : item.status === 'in-progress' ? '开发中' : '计划中' }}</span>
<h3 class="item-title">{{ item.title }}</h3>
</div>
<p class="item-desc">{{ item.description }}</p>
</div>
</div>
</section>
<section class="todo-section">
<h2 class="section-title">未来更新时间线</h2>
<div class="timeline">
<div v-for="(event, index) in todoData.timeline" :key="index" class="timeline-item">
<div class="timeline-dot"></div>
<div class="timeline-content">
<div class="timeline-date">{{ event.date }}</div>
<h3 class="timeline-event">{{ event.event }}</h3>
<p class="timeline-desc">{{ event.description }}</p>
</div>
</div>
</div>
</section>
</div>
</template>
<style scoped>
.todo-container {
margin-top: 2rem;
}
.todo-section {
margin-bottom: 3rem;
}
.section-title {
border-bottom: 1px solid var(--vp-c-divider);
padding-bottom: 0.5rem;
margin-bottom: 1.5rem;
font-weight: 600;
background: none;
-webkit-text-fill-color: initial;
}
.todo-list {
display: grid;
gap: 1rem;
}
.todo-item {
background-color: var(--vp-c-bg-soft);
padding: 1.25rem;
border-radius: 8px;
border: 1px solid var(--vp-c-divider);
transition: border-color 0.25s, background-color 0.25s;
}
.todo-item:hover {
border-color: var(--vp-c-brand);
}
.item-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.5rem;
}
.item-title {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
}
.status-badge {
font-size: 0.75rem;
padding: 0.1rem 0.5rem;
border-radius: 4px;
font-weight: 500;
text-transform: uppercase;
}
.status-badge.pending {
background-color: var(--vp-c-warning-soft);
color: var(--vp-c-warning-1);
}
.status-badge.in-progress {
background-color: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
}
.status-badge.completed {
background-color: var(--vp-c-success-soft);
color: var(--vp-c-success-1);
}
.item-desc {
margin: 0;
font-size: 0.9rem;
color: var(--vp-c-text-2);
line-height: 1.5;
}
.timeline {
position: relative;
padding-left: 2rem;
border-left: 2px solid var(--vp-c-divider);
margin-left: 0.5rem;
}
.timeline-item {
position: relative;
margin-bottom: 2rem;
}
.timeline-dot {
position: absolute;
left: -2.45rem;
top: 0.25rem;
width: 0.75rem;
height: 0.75rem;
background-color: var(--vp-c-brand);
border-radius: 50%;
border: 2px solid var(--vp-c-bg);
}
.timeline-date {
font-size: 0.85rem;
font-weight: 600;
color: var(--vp-c-brand);
margin-bottom: 0.25rem;
}
.timeline-event {
margin: 0 0 0.5rem 0;
font-size: 1.1rem;
font-weight: 600;
}
.timeline-desc {
margin: 0;
font-size: 0.9rem;
color: var(--vp-c-text-2);
}
</style>

View File

@@ -24,6 +24,10 @@
搞懂 local 与 local-lvm 的区别,掌握合并存储与硬盘休眠的精髓。
- [点击阅读教程](./storage-management)
### 6. NVIDIA vGPU 驱动安装说明
解释为何不建议自动化安装驱动,并给出手工安装与版本匹配建议。
- [点击阅读教程](./nvidia-vgpu-driver-notes)
---
> [!CAUTION]

View File

@@ -0,0 +1,72 @@
# NVIDIA vGPU 驱动安装说明:为何不建议脚本自动化安装
本文用于解释:为什么 PVE-Tools 当前不再提供 NVIDIA vGPU 驱动“一键自动安装”,而是建议用户采用手工流程并按版本匹配后再安装。
## 结论先说
- NVIDIA vGPU 驱动对 **内核版本、驱动版本、补丁版本** 高度敏感。
- PVE 8.3+ 到 8.4/8.5 以及可选 6.14 内核场景下,兼容性波动明显。
- 自动化脚本很难覆盖所有机型与版本组合,误装后可能导致宿主机不可用或驱动链路损坏。
- 因此脚本保留环境准备与状态检查能力,但不再默认执行驱动安装器。
## 为什么自动化风险高
### 1) 版本耦合严重,不能“固定一个 run 包”
不同显卡、不同 vGPU 代际、不同 PVE 内核需要不同驱动版本。
把驱动 URL 写死在脚本里,容易出现以下问题:
- DKMS 编译失败
- 安装后模块加载失败
- mdev 类型消失
- `nvidia-smi` 正常但 vGPU 实际不可用
### 2) PVE 新内核会触发历史驱动代码不兼容
在 6.12+(包含 6.14)常见失败特征之一:
```text
nvidia-vgpu-vfio.c: ... error: 'no_llseek' undeclared here
```
这类问题通常需要对驱动源码或补丁做版本化处理,属于“驱动包 + patch + 内核”的组合兼容,不适合盲目自动化。
### 3) 驱动来源与完整性需要用户自行审计
vGPU 驱动及补丁常来自社区资源,不同来源的维护节奏与可信度不同。
在未完成哈希校验与来源审计前,脚本不应替用户直接执行第三方安装器。
## 推荐做法(手工、可回滚)
1. 先确认 BIOS 与 IOMMU/VFIO 基础环境已经就绪。
2. 根据当前内核(`uname -r`)选择匹配的 vGPU 驱动版本。
3. 如需补丁,先手工应用 patch再生成 custom run 安装包。
4. 安装完成后验证:
- `nvidia-smi`
- `mdevctl types`
- `systemctl status nvidia-vgpud nvidia-vgpu-mgr`
5. 每一步都保留日志与回滚点,避免一次性不可逆改动。
## 资源说明
- vGPU 驱动下载网盘:<https://alist.homelabproject.cc/foxipan>
- 资源提供者说明:该资源为第三方提供(佛西博客相关来源),与 PVE-Tools 项目维护方非同一主体。
- 研究教程推荐(适合有经验用户深入折腾):<https://foxi.buduanwang.vip/virtualization/pve/3417.html/>
## 关于 20 系及以上显卡
20 系及以上 NVIDIA 显卡在 vGPU 场景中通常涉及更多版本耦合与补丁细节,方案复杂度明显高于老卡。
本项目不再提供自动化配置流程,建议直接参考上面的研究教程,按你的实际硬件和内核版本逐步验证。
> [!CAUTION]
> 第三方驱动与补丁存在供应链风险。请务必自行校验来源、版本与文件完整性,先在测试环境验证后再用于生产节点。
## 脚本能力边界
PVE-Tools 在 NVIDIA 方向目前聚焦于:
- 宿主机预配置IOMMU/VFIO/黑名单)
- vGPU Unlock 外部库部署
- 驱动状态与诊断能力
驱动安装本身保持人工决策与人工执行,以降低误匹配带来的风险。

View File

@@ -20,24 +20,33 @@ hero:
text: 在 GitHub 查看
link: https://github.com/Mapleawaa/PVE-Tools-9
features:
my_features:
- title: 一键换源
details: 支持中科大、清华等国内镜像源,加速系统更新与软件包下载。
icon: rocket
iconClass: icon-rocket
- title: 完美去弹窗
details: 彻底删除 PVE 登录时的“无有效订阅”提示,告别烦人的弹窗。
icon: sparkles
iconClass: icon-sparkles
- title: 智能硬盘管理
details: 支持硬盘直通、LVM 管理、分区挂载等,操作简单直观。
icon: hard-drive
iconClass: icon-harddrive
- title: 显卡管理
details: 包含 Intel 核显直通、SR-IOV 虚拟化及 NVIDIA vGPU 自动化配置。
iconClass: icon-gpu
- title: 定时开关机
details: 支持 VM 和容器的定时任务管理,基于 cron.d 幂等配置,安全可靠。
icon: clock
iconClass: icon-clock
- title: 更多功能
details: 包含内核管理、硬件直通一键配置、UPS 监控等丰富工具。
icon: dots-horizontal
iconClass: icon-more
---
<script setup>
import HomeFeaturesWithTimeline from './.vitepress/theme/components/HomeFeaturesWithTimeline.vue'
</script>
<HomeFeaturesWithTimeline />
<style>
:root {
--vp-home-hero-name-color: transparent;

66
Web/todo-data.json Normal file
View File

@@ -0,0 +1,66 @@
{
"upcoming_features": [
{
"title": "自动配置备份脚本",
"status": "in-progress",
"description": "支持将 PVE 核心配置及 VM/LXC 配置备份至远程存储或网盘。"
},
{
"title": "增强型硬件监控面板",
"status": "pending",
"description": "提供更详细的 CPU 温度、风扇转速及 NVMe 硬盘健康度监控。"
},
{
"title": "一键部署常用的 LXC 容器镜像",
"status": "pending",
"description": "集成更多如 Docker-compose, Nginx Proxy Manager 等常用应用的快速部署。"
}
],
"timeline": [
{
"date": "2026-03-13",
"event": "v6.8.0 - 第三方市场、离线守卫与 NVIDIA 文档引导增强",
"description": "新增 Modules 第三方软件市场增加vGPU的文档重构文档站主页。"
},
{
"date": "2026-03-09",
"event": "v6.7.0 - 温控与 SMART 增强",
"description": "修复温控概览布局,增强 NVMe/SATA SMART 展示,并重构温控恢复逻辑。"
},
{
"date": "2026-02-22",
"event": "v6.6.0 - 邮件通知与安全防护",
"description": "新增邮件通知引导、IMG 镜像导入、安全防护硬拦截及硬盘直通列表增强。"
},
{
"date": "2026-02-07",
"event": "v6.3.0 - UI 视觉系统进化",
"description": "重构颜色引擎,统一全脚本视觉风格,优化 TUI 交互色彩。"
},
{
"date": "2026-02-06",
"event": "官方文档网站正式上线",
"description": "推出 VitePress 驱动的文档站,包含完整使用指南、功能特性及 FAQ。"
},
{
"date": "2026-01-14",
"event": "v6.0.0 - 核显直通重大更新",
"description": "实现 Intel 6-14 代核显直通自动化,新增救砖工具箱,重构菜单结构。"
},
{
"date": "2025-11-28",
"event": "v5.0.0 - 社区贡献与优化",
"description": "合入多项社区贡献功能,优化更新检查耗时,新增虚拟机快速安装脚本。"
},
{
"date": "2025-10-11",
"event": "v4.0.0 - 架构重构与增强",
"description": "UI 大规模重构,改进内核管理功能,实现交互式温度监控配置系统。"
},
{
"date": "2025-09-01",
"event": "项目正式创建",
"description": "PVE-Tools 1.0 发布,奠定了一键优化与维护脚本的基础。"
}
]
}

14
Web/todo.md Normal file
View File

@@ -0,0 +1,14 @@
---
title: 开发计划与时间线
description: PVE-Tools 项目的未来功能规划和更新时间线。
---
# 项目开发计划 (To-Do List)
这里展示了 PVE-Tools 脚本项目近期计划加入的功能以及未来的更新时间线安排。
<script setup>
import TodoList from './.vitepress/theme/components/TodoList.vue'
</script>
<TodoList />

View File

@@ -3,6 +3,17 @@ title: 更新日志
description: 基于 Commit 记录整理(含正文与文件变更摘要)
---
## 2026年3月19日 - v6.8.0
### 🔄 第三方市场与 NVIDIA 策略调整,补齐离线与 UPS 体验
- **新增第三方软件市场Modules**:脚本可从仓库 `Modules` 自动发现 `.sh` 脚本按头部元信息name/author/version/github展示并执行新增执行前风险确认与下载回退逻辑
- **NVIDIA vGPU 策略改为“文档引导优先”**:移除 vGPU 自动配置流程,保留高风险提醒与人工操作路径;`vGPU Unlock` 入口改为先全屏警告并引导到 Wiki 文章后再继续
- **补充 NVIDIA 专题文档**:新增“为何不建议自动化安装 vGPU 驱动”说明,收录第三方研究教程与资源来源声明,降低误配风险
- **新增离线模式守卫**:启动阶段检测联网状态;若不可达则展示本机 IP/路由/DNS 诊断信息,并要求用户明确确认离线继续,离线时自动跳过在线更新与镜像策略
- **修复 UPS 服务高频告警问题**:温度监控流程加入 `apcupsd` 服务管理;在不启用 UPS 展示时自动执行 `stop + disable`,避免持续刷日志与邮件轰炸
- **文档站结构更新**:导航/侧边栏新增“开发计划”入口与相关页面,完善近期演进信息可见性
---
## 2026年3月9日 - v6.7.0
### 修复温控概览布局并增强 SMART 展示
- **修复节点概览图表被拉高问题**:移除温控功能对右侧 `minHeight` 的重复补丁,解决硬盘数量较多时 CPU Usage、Memory Usage 等折线图被异常拉高的问题