mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/Mapleawaa/PVE-Tools-9.git
synced 2026-09-20 08:03:35 +08:00
feat: 新增第三方软件市场、离线守卫与UPS服务管理,优化NVIDIA vGPU策略
- 新增 Modules 第三方软件市场,支持元信息识别与执行前风险确认 - 新增离线模式守卫,网络不可达时展示诊断信息并要求用户确认 - 移除 NVIDIA vGPU 自动配置流程,改为全屏警告并引导至文档 - 新增 NVIDIA 驱动说明文档,解释为何不建议自动化安装 - 温度监控新增 UPS 服务管理,不启用时自动停止 apcupsd 服务 - 重构文档站主页,新增开发计划页面与时间线组件 - 更新版本至 6.8.0
This commit is contained in:
BIN
Modules/VGPU/libvgpu_unlock_rs_20230207_44d5bb3.so
Normal file
BIN
Modules/VGPU/libvgpu_unlock_rs_20230207_44d5bb3.so
Normal file
Binary file not shown.
136
Modules/install-zsh.sh
Normal file
136
Modules/install-zsh.sh
Normal 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}。"
|
||||
771
PVE-Tools.sh
771
PVE-Tools.sh
@@ -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
9
UPDATE
@@ -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
|
||||
|
||||
@@ -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' }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
240
Web/.vitepress/theme/components/HomeFeaturesWithTimeline.vue
Normal file
240
Web/.vitepress/theme/components/HomeFeaturesWithTimeline.vue
Normal 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">更多计划 ></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>
|
||||
155
Web/.vitepress/theme/components/TodoList.vue
Normal file
155
Web/.vitepress/theme/components/TodoList.vue
Normal 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>
|
||||
@@ -24,6 +24,10 @@
|
||||
搞懂 local 与 local-lvm 的区别,掌握合并存储与硬盘休眠的精髓。
|
||||
- [点击阅读教程](./storage-management)
|
||||
|
||||
### 6. NVIDIA vGPU 驱动安装说明
|
||||
解释为何不建议自动化安装驱动,并给出手工安装与版本匹配建议。
|
||||
- [点击阅读教程](./nvidia-vgpu-driver-notes)
|
||||
|
||||
---
|
||||
|
||||
> [!CAUTION]
|
||||
|
||||
72
Web/advanced/nvidia-vgpu-driver-notes.md
Normal file
72
Web/advanced/nvidia-vgpu-driver-notes.md
Normal 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 外部库部署
|
||||
- 驱动状态与诊断能力
|
||||
|
||||
驱动安装本身保持人工决策与人工执行,以降低误匹配带来的风险。
|
||||
21
Web/index.md
21
Web/index.md
@@ -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
66
Web/todo-data.json
Normal 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
14
Web/todo.md
Normal 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 />
|
||||
@@ -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 等折线图被异常拉高的问题
|
||||
|
||||
Reference in New Issue
Block a user