mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/Mapleawaa/PVE-Tools-9.git
synced 2026-09-20 08:03:35 +08:00
feat: release v11.0.0 Liino — menu framework, risk guards, CI gates
Unify all menus on lib/menu.sh (run_menu), harden GPU/GRUB writes with two-tier confirms and markers, fix remote/self-update to Releases assets, and close real CI quality gates with SHA256SUMS. Bump to 11.0.0 / Liino.
This commit is contained in:
96
.github/CLAUDE.md
vendored
96
.github/CLAUDE.md
vendored
@@ -4,54 +4,52 @@
|
||||
|
||||
## 模块职责
|
||||
|
||||
管理项目的持续集成/持续部署流水线和社区 Issue 模板。三条工作流覆盖 PR 验证、正式发布和测试版发布。
|
||||
管理项目的持续集成/持续部署流水线和社区 Issue 模板。三条工作流覆盖 PR 验证、正式发布和测试版发布。发布物为 `dist/PVE-Tools.sh` 单文件 + `SHA256SUMS.txt`(不使用 shc 编译二进制)。
|
||||
|
||||
## 入口与启动
|
||||
|
||||
| 项目 | 说明 |
|
||||
|---|---|
|
||||
| 触发方式 | GitHub Actions,由 push/pull_request 事件触发 |
|
||||
| 触发方式 | GitHub Actions,由 push(tag)/pull_request 事件触发 |
|
||||
| 运行环境 | `ubuntu-latest` |
|
||||
| 权限 | `contents: write`(Release 工作流需要) |
|
||||
| 权限 | `contents: write`(两个 Release 工作流需要) |
|
||||
|
||||
## 工作流清单
|
||||
|
||||
### pr-validation.yml -- PR 验证工作流
|
||||
|
||||
**触发条件**: PR 到 `main` 或 `beta` 分支
|
||||
|
||||
| 检查 | 说明 |
|
||||
|---|---|
|
||||
| Shellcheck(入口) | `shellcheck -f gcc PVE-Tools.sh`,error/warning 均失败 |
|
||||
| Shellcheck(全源码) | `find lib src/modules -name '*.sh' \| xargs shellcheck --severity=error`,error 级卡死 |
|
||||
| 语法检查 | `bash -n` 入口/dev.sh,`bash build.sh` 后 `bash -n dist` |
|
||||
| Shellcheck(产物) | dist 的 error/warning 均失败 |
|
||||
| 构建一致性 | 源码与 dist 的函数集合双向 diff,不一致即失败 |
|
||||
| 版本一致性 | `CURRENT_VERSION`(lib/config.sh)== `VERSION` 文件 |
|
||||
| UPDATE 新鲜度 | `UPDATE` 首行必须包含当前版本号(防更新日志脱节) |
|
||||
| 安全扫描 | dist 中禁 `eval`、未加引号变量开头的 `rm -rf`、`source` 语句,命中即失败 |
|
||||
|
||||
### release.yml -- 正式发布工作流
|
||||
|
||||
**触发条件**: 推送版本标签 (`v*.*.*`, `*.*.*`, `v*.*.*-stable`, `*.*.*-stable`)
|
||||
**触发条件**: 推送 `v*.*.*` / `*.*.*` / `*-stable` 标签;**排除** `-beta*` / `-alpha*` / `-rc*`(预发布标签只归 beta-release.yml,避免双流水线竞争同一 tag)
|
||||
|
||||
**步骤**:
|
||||
1. `actions/checkout@v4`(fetch-depth: 0 以获取完整历史)
|
||||
2. 从 tag 提取版本号
|
||||
3. **`bash build.sh`** -- 将 lib/ + src/modules/ 组装为 `dist/PVE-Tools.sh`
|
||||
4. 安装 `shc`(via neurobin/ppa)
|
||||
5. `shc -f dist/PVE-Tools.sh -o pve-tools` -- 编译为二进制
|
||||
6. 生成 release notes(基于 git 提交历史)
|
||||
7. `softprops/action-gh-release@v2` 创建 GitHub Release,上传 `pve-tools` + `dist/PVE-Tools.sh`
|
||||
1. checkout(fetch-depth: 0)-> 从 tag 提取版本号
|
||||
2. `bash build.sh` 构建 dist
|
||||
3. **发布前校验**(打 tag 直发不绕过质量闸门):`bash -n dist`、`shellcheck --severity=error dist`、函数集合一致性 diff、版本三方一致(tag == config == VERSION)
|
||||
4. 生成 `SHA256SUMS.txt`
|
||||
5. 基于 git 提交历史生成 release notes
|
||||
6. `softprops/action-gh-release@v2` 创建 Release,上传 `dist/PVE-Tools.sh` + `dist/SHA256SUMS.txt`
|
||||
|
||||
**构建产物**:
|
||||
- `pve-tools` -- 编译后的二进制文件
|
||||
- `dist/PVE-Tools.sh` -- 拼接后的单文件脚本
|
||||
**注意**: 远程安装链路依赖 `releases/latest/download/PVE-Tools.sh`,正式发版后新用户方能获取最新版。
|
||||
|
||||
### beta-release.yml -- 测试版发布工作流
|
||||
|
||||
**触发条件**: 推送 beta/alpha 标签
|
||||
**触发条件**: 推送 `-beta*` / `-alpha*` 标签
|
||||
|
||||
功能与 release.yml 类似,但标记为 prerelease。
|
||||
|
||||
### pr-validation.yml -- PR 验证工作流
|
||||
|
||||
**触发条件**: PR 合并到 `main` 或 `beta` 分支
|
||||
|
||||
**检查项**:
|
||||
|
||||
| 检查 | 命令 | 说明 |
|
||||
|---|---|---|
|
||||
| Shellcheck | `shellcheck -f gcc PVE-Tools.sh` | 静态分析,有 error/warning 则失败 |
|
||||
| 语法检查 | `bash -n PVE-Tools.sh`、`bash -n dev.sh`、`bash -n dist/PVE-Tools.sh` | 先运行 `bash build.sh` 构建再检查 |
|
||||
| 构建验证 | `bash build.sh` | 验证构建不报错 |
|
||||
| 版本一致性 | 比较 `lib/config.sh` 中的 `CURRENT_VERSION` 与 `VERSION` 文件 | 不一致则失败 |
|
||||
| 安全扫描 | grep 检测 `eval`/`source` 使用 | 发现则告警 |
|
||||
与 release.yml 类似但 `prerelease: true`;同样执行构建质量校验(语法/shellcheck/函数一致性),版本号断言放宽(beta 标签允许与 config 版本不同步)。
|
||||
|
||||
## Issue 模板
|
||||
|
||||
@@ -71,42 +69,29 @@
|
||||
|
||||
## 关键依赖与配置
|
||||
|
||||
- **GitHub Actions**: 免费额度,`ubuntu-latest` runner
|
||||
- **shc**: 来自 `ppa:neurobin/ppa`,用于 Bash 编译
|
||||
- **softprops/action-gh-release@v2**: 第三方 GitHub Action,用于创建 Release
|
||||
- **shellcheck**: Ubuntu 自带或通过 apt 安装
|
||||
|
||||
## 测试与质量
|
||||
|
||||
CI/CD 本身即为项目的测试与质量保障体系:
|
||||
- 每次 PR 自动执行静态分析、语法检查、版本一致性校验、安全扫描
|
||||
- Release 前自动构建并验证构建产物
|
||||
- **GitHub Actions**: `ubuntu-latest` runner(自带 shellcheck)
|
||||
- **softprops/action-gh-release@v2**: 创建 Release 与上传资产
|
||||
|
||||
## 常见问题 (FAQ)
|
||||
|
||||
**Q: 为什么 Release 要用 shc 编译?**
|
||||
shc 将 Bash 脚本编译为二进制文件,提供基础的源码保护,同时便于分发。构建产物 `dist/PVE-Tools.sh` 同时发布以保持 `bash <(curl ...)` 兼容性。
|
||||
**Q: 发版时需要同步哪些版本号?**
|
||||
三处:`lib/config.sh` 的 `CURRENT_VERSION`、`VERSION` 文件、`UPDATE` 首行条目,且正式发布 tag 必须与之一致——任何一处不同步都会被 CI 拦下。
|
||||
|
||||
**Q: PR 验证中 build.sh 会失败怎么办?**
|
||||
检查 lib/ 和 src/modules/ 中的文件是否存在、语法是否正确。`bash -n` 仅检查语法不执行代码。
|
||||
**Q: PR 验证中 build.sh 失败怎么办?**
|
||||
检查 lib/ 与 src/modules/ 文件是否齐全、语法是否正确;新增 lib 文件需同步 build.sh / dev.sh / PVE-Tools.sh 三处加载列表。
|
||||
|
||||
**Q: 如何添加新的 Issue 模板?**
|
||||
在 `ISSUE_TEMPLATE/` 目录添加 `.md` 文件并更新 `config.yml` 即可,GitHub 会自动识别。
|
||||
在 `ISSUE_TEMPLATE/` 目录添加 `.md` 文件并更新 `config.yml`。
|
||||
|
||||
## 相关文件清单
|
||||
|
||||
```
|
||||
.github/
|
||||
workflows/
|
||||
release.yml # 正式发布工作流
|
||||
beta-release.yml # 测试版发布工作流
|
||||
pr-validation.yml # PR 验证工作流
|
||||
ISSUE_TEMPLATE/
|
||||
fast-bugs-report.md # 快速 Bug 报告模板
|
||||
feature-request.md # 功能请求模板
|
||||
plugin-submit.md # 插件提交模板
|
||||
report-bugs.md # 详细 Bug 报告模板
|
||||
config.yml # Issue 模板配置
|
||||
release.yml # 正式发布(含发布前校验与 SHA256SUMS)
|
||||
beta-release.yml # 测试版发布(含构建校验)
|
||||
pr-validation.yml # PR 验证(8 项检查)
|
||||
ISSUE_TEMPLATE/ # Issue 模板 5 件
|
||||
FUNDING.yml # 赞助配置
|
||||
```
|
||||
|
||||
@@ -114,4 +99,5 @@ shc 将 Bash 脚本编译为二进制文件,提供基础的源码保护,同
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-07-08 | 初始化 .github 模块 CLAUDE.md。PR 验证已适配模块化(build.sh/build -n dist)。Release 已添加 build.sh 步骤。 |
|
||||
| 2026-07-26 | 按现实重写:移除 shc 描述;补充发布前校验/SHA256SUMS/UPDATE 新鲜度/安全扫描细节;记录正式与预发布标签互斥规则 |
|
||||
| 2026-07-08 | 初始化 .github 模块 CLAUDE.md |
|
||||
|
||||
14
.github/workflows/beta-release.yml
vendored
14
.github/workflows/beta-release.yml
vendored
@@ -29,6 +29,19 @@ jobs:
|
||||
- name: Build single script from modules
|
||||
run: bash build.sh
|
||||
|
||||
- name: Validate build output before release
|
||||
run: |
|
||||
# beta 同样不跳过构建质量校验(版本号断言放宽,beta 标签允许与 config 不同步)
|
||||
bash -n dist/PVE-Tools.sh
|
||||
shellcheck --severity=error -f gcc dist/PVE-Tools.sh
|
||||
diff <(grep -rhoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' lib src/modules --include='*.sh' | sort -u) \
|
||||
<(grep -hoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' dist/PVE-Tools.sh | sort -u) \
|
||||
|| { echo "构建产物与源码函数集合不一致,中止发布"; exit 1; }
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
cd dist && sha256sum PVE-Tools.sh > SHA256SUMS.txt && cat SHA256SUMS.txt
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
@@ -47,6 +60,7 @@ jobs:
|
||||
```
|
||||
files: |
|
||||
dist/PVE-Tools.sh
|
||||
dist/SHA256SUMS.txt
|
||||
draft: false
|
||||
prerelease: true
|
||||
env:
|
||||
|
||||
44
.github/workflows/pr-validation.yml
vendored
44
.github/workflows/pr-validation.yml
vendored
@@ -18,6 +18,11 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Shellcheck (lib and modules, error level)
|
||||
run: |
|
||||
# 全量源码 error 级静态检查(入口与构建产物维持 error+warning 严格档)
|
||||
find lib src/modules -name '*.sh' -print0 | xargs -0 shellcheck --severity=error -f gcc
|
||||
|
||||
- name: Shell syntax check
|
||||
run: |
|
||||
bash -n PVE-Tools.sh
|
||||
@@ -34,6 +39,13 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build output consistency check
|
||||
run: |
|
||||
# 断言 lib/ 与 src/modules/ 中定义的每个函数都进入了构建产物(双向一致)
|
||||
diff <(grep -rhoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' lib src/modules --include='*.sh' | sort -u) \
|
||||
<(grep -hoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' dist/PVE-Tools.sh | sort -u) \
|
||||
|| { echo "构建产物与源码的函数集合不一致,见上方 diff(< 仅源码有 / > 仅产物有)"; exit 1; }
|
||||
|
||||
- name: Version consistency check
|
||||
run: |
|
||||
SCRIPT_VERSION=$(grep "CURRENT_VERSION=" lib/config.sh | cut -d'"' -f2)
|
||||
@@ -43,11 +55,33 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: UPDATE changelog freshness check
|
||||
run: |
|
||||
# UPDATE 是 check_update 展示给线上用户的更新日志,首行必须包含当前版本号,防止再次脱节
|
||||
VERSION_FILE_VERSION=$(cat VERSION)
|
||||
if ! head -1 UPDATE | grep -qF "$VERSION_FILE_VERSION"; then
|
||||
echo "UPDATE 首行未包含当前版本 $VERSION_FILE_VERSION —— 更新日志已脱节,请同步 UPDATE 文件"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Security scan
|
||||
run: |
|
||||
# 检查是否有潜在的安全问题
|
||||
if grep -q "eval\|source" PVE-Tools.sh; then
|
||||
echo "Potential security issues found: eval or source commands detected"
|
||||
else
|
||||
echo "No security issues detected"
|
||||
# 对全量构建产物扫描真实危险模式,命中即失败
|
||||
# (旧版对入口 grep eval/source 永远命中且从不失败,属于空转检查)
|
||||
status=0
|
||||
if grep -nE '(^|[^a-zA-Z_.])eval([^a-zA-Z_]|$)' dist/PVE-Tools.sh; then
|
||||
echo "检测到 eval 使用:本项目约定禁止 eval,请改写后再合并"
|
||||
status=1
|
||||
fi
|
||||
if grep -nE 'rm -rf +\$' dist/PVE-Tools.sh; then
|
||||
echo "检测到未加引号且以变量开头的 rm -rf 路径:变量为空或含空格时行为不可控,请加引号与前置校验"
|
||||
status=1
|
||||
fi
|
||||
if grep -nE '^[[:space:]]*(source|\.)[[:space:]]' dist/PVE-Tools.sh; then
|
||||
echo "构建产物中不应存在 source 语句:dist 是自包含单文件,出现 source 说明拼接进了开发态加载代码"
|
||||
status=1
|
||||
fi
|
||||
if [ "$status" -eq 0 ]; then
|
||||
echo "Security scan passed"
|
||||
fi
|
||||
exit $status
|
||||
36
.github/workflows/release.yml
vendored
36
.github/workflows/release.yml
vendored
@@ -6,6 +6,14 @@ on:
|
||||
- '*.*.*'
|
||||
- 'v*.*.*-stable'
|
||||
- '*.*.*-stable'
|
||||
# 排除预发布标签,否则 v*.*.* 会同时命中 beta/alpha/rc,
|
||||
# 导致预发布被当成正式版发布并与 beta-release.yml 竞争同一 tag
|
||||
- '!v*.*.*-beta*'
|
||||
- '!*.*.*-beta*'
|
||||
- '!v*.*.*-alpha*'
|
||||
- '!*.*.*-alpha*'
|
||||
- '!v*.*.*-rc*'
|
||||
- '!*.*.*-rc*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -29,6 +37,33 @@ jobs:
|
||||
- name: Build single script from modules
|
||||
run: bash build.sh
|
||||
|
||||
- name: Validate build output before release
|
||||
run: |
|
||||
# 发版前置校验:打 tag 直发不再绕过 PR 的质量闸门
|
||||
bash -n dist/PVE-Tools.sh
|
||||
shellcheck --severity=error -f gcc dist/PVE-Tools.sh
|
||||
# 函数集合一致性(源码 vs 产物 双向)
|
||||
diff <(grep -rhoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' lib src/modules --include='*.sh' | sort -u) \
|
||||
<(grep -hoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\)' dist/PVE-Tools.sh | sort -u) \
|
||||
|| { echo "构建产物与源码函数集合不一致,中止发布"; exit 1; }
|
||||
# 版本三方一致:tag == lib/config.sh == VERSION,防止打错标签发出版本混乱的 Release
|
||||
SCRIPT_VERSION=$(grep "CURRENT_VERSION=" lib/config.sh | cut -d'"' -f2)
|
||||
VERSION_FILE_VERSION=$(cat VERSION)
|
||||
TAG_VERSION="${{ steps.get_version.outputs.VERSION }}"
|
||||
TAG_VERSION="${TAG_VERSION%-stable}"
|
||||
if [ "$SCRIPT_VERSION" != "$VERSION_FILE_VERSION" ]; then
|
||||
echo "版本不一致: config($SCRIPT_VERSION) != VERSION($VERSION_FILE_VERSION),中止发布"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$TAG_VERSION" != "$SCRIPT_VERSION" ]; then
|
||||
echo "标签版本($TAG_VERSION)与脚本版本($SCRIPT_VERSION)不一致,中止发布"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
cd dist && sha256sum PVE-Tools.sh > SHA256SUMS.txt && cat SHA256SUMS.txt
|
||||
|
||||
- name: Generate release notes
|
||||
id: release_notes
|
||||
run: |
|
||||
@@ -66,6 +101,7 @@ jobs:
|
||||
```
|
||||
files: |
|
||||
dist/PVE-Tools.sh
|
||||
dist/SHA256SUMS.txt
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
|
||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
# Ignore dependencies
|
||||
node_modules/
|
||||
**/node_modules/
|
||||
bun.lockb
|
||||
|
||||
# Ignore build outputs
|
||||
dist/
|
||||
@@ -9,10 +8,6 @@ dist/
|
||||
build/
|
||||
out/
|
||||
.temp/
|
||||
.vitepress/dist/
|
||||
.vitepress/cache/
|
||||
**/.vitepress/dist/
|
||||
**/.vitepress/cache/
|
||||
|
||||
# Ignore system files
|
||||
.DS_Store
|
||||
@@ -37,12 +32,3 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# BMad Method V6
|
||||
_bmad/
|
||||
_bmad-output/
|
||||
|
||||
test.txt
|
||||
|
||||
# ignore docs
|
||||
Docs/
|
||||
|
||||
40
AGENTS.md
40
AGENTS.md
@@ -2,41 +2,39 @@
|
||||
|
||||
## Project Structure & Module Organization
|
||||
|
||||
`PVE-Tools.sh` is the main Bash entry point for Proxmox VE 9.x operations. `Tools/` contains standalone maintenance scripts for host setup, LXC cleanup, monitoring, kernel handling, and backups. `Modules/` contains plugin-market modules and bundled binary assets such as `Modules/VGPU/*.so`; do not edit binary files unless the change is intentional and documented. `Web/` is the VitePress documentation site, with pages in Markdown and static assets under `Web/public/` and `Web/assets/`. `Docs/` stores supplemental guides, `images/` stores README and site screenshots, and `.github/` contains issue templates and CI/release workflows.
|
||||
- `PVE-Tools.sh` — bootstrap entry (~330 lines). Local mode sources `lib/` + `src/modules/`; remote mode (`bash <(curl ...)`) downloads the prebuilt single file from GitHub Releases (`releases/latest/download/PVE-Tools.sh`).
|
||||
- `lib/` — infrastructure layer, loaded in strict order: `config.sh` (globals) → `core.sh` (logging/UI/backup/GRUB helpers) → `menu.sh` (menu framework) → `network.sh` (region detection, mirror selection) → `runtime.sh` (guards + `main()`).
|
||||
- `src/modules/` — 10 numbered feature modules matching main-menu items 1-10; each has `init.sh` (menu entry) plus feature files.
|
||||
- `build.sh` concatenates lib + modules into `dist/PVE-Tools.sh` (gitignored; built by CI). `dev.sh` sources everything for local development.
|
||||
- `Tools/` — standalone community maintenance scripts (not part of the build). `Modules/` — plugin-market assets incl. `Modules/VGPU/*.so` (binary asset referenced by the vGPU unlock download URL — do not edit or remove). `Docs/` — supplementary docs incl. the modularization design doc. `images/` — README screenshots. `.github/` — issue templates and CI workflows.
|
||||
- The former `Web/` VitePress documentation site has been removed from this repository.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
Run Bash checks from the repository root:
|
||||
|
||||
```bash
|
||||
bash -n PVE-Tools.sh
|
||||
shellcheck -f gcc PVE-Tools.sh
|
||||
bash dev.sh # run from source
|
||||
bash build.sh # build dist/PVE-Tools.sh
|
||||
bash -n PVE-Tools.sh && bash -n dist/PVE-Tools.sh # syntax check
|
||||
shellcheck -f gcc PVE-Tools.sh dist/PVE-Tools.sh # strict lint (entry + artifact)
|
||||
find lib src/modules -name '*.sh' -print0 | xargs -0 shellcheck --severity=error -f gcc
|
||||
```
|
||||
|
||||
For the documentation site:
|
||||
|
||||
```bash
|
||||
cd Web
|
||||
bun install
|
||||
bun run dev
|
||||
bun run build
|
||||
bun run preview
|
||||
```
|
||||
|
||||
`bun run build` copies `VERSION`, `UPDATE`, and `PVE-Tools.sh` into the generated site output.
|
||||
CI (`pr-validation.yml`) additionally asserts: source-vs-dist function-set consistency, `CURRENT_VERSION` ↔ `VERSION` match, `UPDATE` first line contains the current version, and a security scan (no `eval`, no unquoted `rm -rf $var`, no `source` in dist). Release workflows re-run these gates before publishing and attach `SHA256SUMS.txt`.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
|
||||
Bash scripts use `#!/bin/bash`, 4-space indentation, and `snake_case` function names. Use `UPPER_SNAKE` for constants and global configuration, and `lower_case` for local variables. Prefer existing helpers in `PVE-Tools.sh`, especially logging, confirmation, backup, UI, and idempotent configuration functions. Web components should follow VitePress/Vue 3 conventions with `<script setup>` and scoped CSS where applicable.
|
||||
`#!/bin/bash`, 4-space indent, `snake_case` functions, `UPPER_SNAKE` globals, `lower_case` locals. Every file starts with the SPDX (`GPL-3.0-only`) + Copyright header. Prefer the shared helpers over ad-hoc code: `log_info/warn/error/step/success/tips`, `display_error`/`display_success`, `backup_file` (backs up into `/var/backups/pve-tools/`), `apply_block`/`remove_block` (marker-fenced config blocks: `# PVE-TOOLS BEGIN/END <MARKER>`), `grub_add_param`/`grub_remove_param`/`grub_has_param` (idempotent, key-exact GRUB cmdline management).
|
||||
|
||||
## Testing Guidelines
|
||||
## Menus & Interaction
|
||||
|
||||
There is no full automated end-to-end test suite. At minimum, run `bash -n PVE-Tools.sh` and `shellcheck -f gcc PVE-Tools.sh` for script changes. Keep `CURRENT_VERSION` in `PVE-Tools.sh` synchronized with `VERSION` when releasing. High-risk workflows must be manually verified on an appropriate Proxmox VE 9.x environment with backups and a rollback plan.
|
||||
All menus go through the framework in `lib/menu.sh`: `run_menu <title> <render_fn> <dispatch_fn> <range>` — never hand-write `while true` menu loops. Render functions print options only (`show_menu_option`); dispatch functions `case "$1"`, `return 1` for unknown input, `return 0` at the end. `0` is always "back" and is appended by the framework. For input use `prompt_value` (default-value annotation, empty = cancel), `prompt_yes_no`, `prompt_pick_from_list` (returns 2 on cancel — same convention as the `vm_select_*` pickers).
|
||||
|
||||
Confirmations are two-tier: `confirm_action` (light, type `yes`) for reversible config writes; `confirm_high_risk_action` (heavy, type a specific confirm word) for anything touching GRUB/VFIO, apt sources, fstab, LVM, qm disk slots, firewall, SSH, or cron.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
Follow the existing Conventional Commit style: `feat:`, `fix(scope):`, `docs:`, and `chore:`. Keep commits focused and describe operational risk when touching networking, firewall, storage, GRUB, passthrough, or VM lifecycle logic. Pull requests should include a clear summary, linked issue when relevant, validation commands, screenshots for documentation or UI changes, and notes about manual PVE testing.
|
||||
Conventional Commits (`feat:`, `fix(scope):`, `docs:`, `chore:`). When bumping the version keep three places in sync: `CURRENT_VERSION` in `lib/config.sh`, the `VERSION` file, and the top entry of `UPDATE` (CI enforces all three). Describe operational risk when touching networking, firewall, storage, GRUB, passthrough, or VM lifecycle logic.
|
||||
|
||||
## Security & Configuration Tips
|
||||
|
||||
Avoid introducing `eval` or unreviewed `source` usage. Back up host configuration before modifying production-facing behavior. Treat network, firewall, storage, kernel, and passthrough changes as high risk.
|
||||
No `eval`; no unreviewed `source`. Back up host configuration via `backup_file` before writes, and wrap system-config writes in marker blocks (`apply_block`) so they can be removed cleanly and detected by `gpu_detect_active_stacks`-style probes. Treat network, firewall, storage, kernel, and passthrough changes as high risk and verify manually on a Proxmox VE 9.x box with backups and a rollback plan.
|
||||
|
||||
153
CLAUDE.md
153
CLAUDE.md
@@ -8,21 +8,21 @@ PVE Tools Pro 是一个面向 Proxmox VE 9.x 的交互式 Bash 运维工具集
|
||||
|
||||
## 架构总览
|
||||
|
||||
项目已完成模块化重构(v9.0.0),从单一 430KB 脚本拆分为基础设施层(lib/)与功能模块层(src/modules/),通过 build.sh 组装为单文件发布,用户侧零感知。
|
||||
项目自 v9.0.0 完成模块化重构:单一 14000+ 行脚本拆分为基础设施层(lib/)与功能模块层(src/modules/),通过 build.sh 组装为单文件发布,用户侧零感知。交互层统一收口到 `lib/menu.sh` 菜单框架。
|
||||
|
||||
- **入口层**: `PVE-Tools.sh`(169 行)-- 本地开发时 source lib/ + src/modules/;远程 curl 运行时自动下载 dist/ 构建产物或逐个下载源码模块。
|
||||
- **基础设施层**: `lib/` -- 全局变量(config.sh)、日志/UI/备份/GRUB(core.sh)、网络检测/镜像选择(network.sh)、运行时守卫(runtime.sh)。
|
||||
- **入口层**: `PVE-Tools.sh`(约 330 行)-- 本地开发时按固定顺序 source lib/ 与 src/modules/(各模块 init.sh 显式优先);远程 curl 运行时从 GitHub Releases(`releases/latest/download/PVE-Tools.sh`)下载构建产物执行,带重试/限速/校验/原子落盘守护。
|
||||
- **基础设施层**: `lib/` -- 全局变量(config.sh)、日志/UI/确认/备份/GRUB(core.sh)、菜单交互框架(menu.sh)、网络检测/镜像选择(network.sh)、运行时守卫与主循环(runtime.sh)。
|
||||
- **功能模块层**: `src/modules/` -- 10 个子目录对应主菜单 1-10 项,每个子目录内按功能拆分文件(init.sh 为菜单入口)。
|
||||
- **构建系统**: `build.sh` 按顺序拼接 lib/*.sh + src/modules/**/*.sh 为 `dist/PVE-Tools.sh`;`dev.sh` 直接 source 全部源码供开发调试。
|
||||
- **辅助工具集**: `Tools/` 集成来自 tteck 社区的 13 个系统维护脚本。
|
||||
- **插件市场**: `Modules/` 提供第三方脚本的自动发现与执行框架。
|
||||
- **CI/CD**: `.github/workflows/` 提供 release、beta-release、pr-validation 三条流水线。
|
||||
- **构建系统**: `build.sh` 按顺序拼接 lib/*.sh + src/modules/**/*.sh 为 `dist/PVE-Tools.sh`(gitignore,CI 构建);`dev.sh` 直接 source 全部源码供开发调试。
|
||||
- **辅助工具集**: `Tools/` 集成来自 tteck 社区的 13 个独立维护脚本(不参与构建,供用户手动执行)。
|
||||
- **插件市场**: `Modules/` 提供第三方脚本与二进制资产;`Modules/VGPU/*.so` 是 vGPU Unlock 功能的下载资产,不可删改。
|
||||
- **CI/CD**: `.github/workflows/` 提供 release、beta-release、pr-validation 三条流水线(详见 [.github/CLAUDE.md](./.github/CLAUDE.md))。
|
||||
|
||||
## 模块结构图
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
ROOT["PVE Tools Pro (根)"] --> MAIN["PVE-Tools.sh<br/>入口脚本 169行"]
|
||||
ROOT["PVE Tools Pro (根)"] --> MAIN["PVE-Tools.sh<br/>入口脚本 ~330行"]
|
||||
ROOT --> LIB["lib/<br/>基础设施层"]
|
||||
ROOT --> SRC["src/modules/<br/>功能模块层"]
|
||||
ROOT --> TOOLS["Tools/"]
|
||||
@@ -32,9 +32,10 @@ graph TD
|
||||
ROOT --> BUILD["build.sh + dev.sh<br/>构建与开发入口"]
|
||||
|
||||
LIB --> LIBC["config.sh<br/>全局变量/镜像/URL"]
|
||||
LIB --> LIBCORE["core.sh<br/>颜色/日志/UI/备份/GRUB"]
|
||||
LIB --> LIBCORE["core.sh<br/>颜色/日志/确认/备份/GRUB"]
|
||||
LIB --> LIBMENU["menu.sh<br/>统一菜单交互框架"]
|
||||
LIB --> LIBNET["network.sh<br/>网络检测/镜像选择"]
|
||||
LIB --> LIBRUN["runtime.sh<br/>root守卫/调试/PVE检测"]
|
||||
LIB --> LIBRUN["runtime.sh<br/>守卫/主菜单/main()"]
|
||||
|
||||
SRC --> M01["01-optimization<br/>日常优化与通知"]
|
||||
SRC --> M02["02-sources<br/>软件源与系统升级"]
|
||||
@@ -47,14 +48,6 @@ graph TD
|
||||
SRC --> M09["09-security<br/>安全中心"]
|
||||
SRC --> M10["10-third-party<br/>第三方工具"]
|
||||
|
||||
TOOLS --> T1["系统配置: 5个脚本"]
|
||||
TOOLS --> T2["容器管理: 3个脚本"]
|
||||
TOOLS --> T3["系统维护: 4个脚本"]
|
||||
TOOLS --> T4["监控: 1个脚本"]
|
||||
|
||||
MODS --> P1["install-zsh.sh"]
|
||||
MODS --> P2["VGPU/*.so"]
|
||||
|
||||
GHA --> W1["workflows/<br/>release/beta/PR"]
|
||||
|
||||
click ROOT "./CLAUDE.md" "返回根文档"
|
||||
@@ -69,12 +62,12 @@ graph TD
|
||||
|
||||
| 模块路径 | 语言 | 职责 | 入口文件 | 文档 |
|
||||
|---|---|---|---|---|
|
||||
| `/` (根) | Bash | 入口脚本,本地/远程模块加载 | `PVE-Tools.sh` (169行) | `README.md` |
|
||||
| `lib/` | Bash | 基础设施层:全局变量、日志、UI、网络、运行时 | `config.sh`, `core.sh`, `network.sh`, `runtime.sh` | [lib/CLAUDE.md](./lib/CLAUDE.md) |
|
||||
| `/` (根) | Bash | 入口脚本:本地 source / 远程下载 Releases 单文件 | `PVE-Tools.sh` (~330行) | `README.md` |
|
||||
| `lib/` | Bash | 基础设施层:全局变量、日志、UI、菜单框架、网络、运行时 | `config.sh`, `core.sh`, `menu.sh`, `network.sh`, `runtime.sh` | [lib/CLAUDE.md](./lib/CLAUDE.md) |
|
||||
| `src/modules/` | Bash | 功能模块层:10 个子模块,对应主菜单 1-10 | 各 `*/init.sh` | [src/CLAUDE.md](./src/CLAUDE.md) |
|
||||
| `Tools/` | Bash | 第三方系统维护脚本集(13个) | 各 `.sh` 文件 | [Tools/CLAUDE.md](./Tools/CLAUDE.md) |
|
||||
| `Modules/` | Bash/二进制 | 插件市场与模块 | `install-zsh.sh`, `VGPU/*.so` | [Modules/CLAUDE.md](./Modules/CLAUDE.md) |
|
||||
| `Docs/` | Markdown | 补充文档与重构计划 | `future-guide.md`, `重构计划-PVE-Tools模块化拆分.md` | -- |
|
||||
| `Tools/` | Bash | 第三方系统维护脚本集(13个,不参与构建) | 各 `.sh` 文件 | [Tools/CLAUDE.md](./Tools/CLAUDE.md) |
|
||||
| `Modules/` | Bash/二进制 | 插件市场与分发资产(VGPU .so 为下载资产) | `install-zsh.sh`, `VGPU/*.so` | [Modules/CLAUDE.md](./Modules/CLAUDE.md) |
|
||||
| `Docs/` | Markdown | 补充文档与《重构计划》设计稿 | `future-guide.md`, `重构计划-PVE-Tools模块化拆分.md` | -- |
|
||||
| `.github/` | YAML | CI/CD 工作流与 Issue 模板 | `workflows/*.yml` | [.github/CLAUDE.md](./.github/CLAUDE.md) |
|
||||
|
||||
## 技术栈
|
||||
@@ -85,8 +78,7 @@ graph TD
|
||||
| 主脚本语言 | GNU Bash | 模块化源码;通过 build.sh 组装为单文件分发 |
|
||||
| 构建系统 | bash + find + sort | `build.sh` 顺序拼接 lib/ -> src/modules/ -> dist/PVE-Tools.sh |
|
||||
| 开发模式 | bash dev.sh | 直接 source 全部源文件,无需构建 |
|
||||
| CI/CD | GitHub Actions | release / beta-release / PR validation |
|
||||
| 编译工具 | shc | 将 dist/PVE-Tools.sh 编译为二进制(仅 release 流程) |
|
||||
| CI/CD | GitHub Actions | release / beta-release / PR validation;发布物为 dist 单文件 + SHA256SUMS |
|
||||
| 许可证 | GPL-3.0 | 详见 `LICENSE` |
|
||||
|
||||
## 运行与开发
|
||||
@@ -114,41 +106,84 @@ bash dev.sh
|
||||
bash build.sh
|
||||
|
||||
# 验证构建产物
|
||||
bash dist/PVE-Tools.sh
|
||||
bash -n dist/PVE-Tools.sh
|
||||
|
||||
# 静态检查
|
||||
bash -n PVE-Tools.sh
|
||||
bash -n dist/PVE-Tools.sh
|
||||
shellcheck -f gcc PVE-Tools.sh
|
||||
shellcheck -f gcc dist/PVE-Tools.sh
|
||||
find lib src/modules -name '*.sh' -print0 | xargs -0 shellcheck --severity=error -f gcc
|
||||
```
|
||||
|
||||
### 构建原理
|
||||
### 构建与加载顺序(事实描述)
|
||||
|
||||
`build.sh` 按以下固定顺序拼接:
|
||||
1. `lib/config.sh` -- 全局变量定义(必须最先加载)
|
||||
2. `lib/core.sh` -- 日志/UI/备份/GRUB(依赖 config.sh 中的变量)
|
||||
3. `lib/network.sh` -- 网络检测/镜像选择
|
||||
4. `lib/runtime.sh` -- 运行时守卫/main()函数
|
||||
5. `src/modules/**/*.sh` -- 按路径名排序(`sort -z`),确保 init.sh 先于同目录其他文件加载
|
||||
1. `lib/config.sh` -> `lib/core.sh` -> `lib/menu.sh` -> `lib/network.sh` -> `lib/runtime.sh`(硬编码列表)
|
||||
2. `src/modules/**/*.sh` 按路径名排序(`find | sort -z`),**不对 init.sh 做特殊处理**
|
||||
|
||||
### CI/CD 流水线
|
||||
三个入口的模块加载策略并不相同:`build.sh` 与 `dev.sh` 为纯字典序;`PVE-Tools.sh` 本地模式显式先 source 各目录的 init.sh 再加载其余文件。这一差异不影响运行——**bash 中函数定义顺序与调用无关**,所有模块文件只定义函数,真正的执行入口是文件末尾追加的 `main "$@"`。因此不要写"顶层立即执行且依赖其他模块函数"的代码。
|
||||
|
||||
- **PR 合并到 main/beta**: 触发 shellcheck、Bash 语法检查、版本一致性校验、安全扫描。
|
||||
- **推送版本标签 (v*.*.*)**: 触发 Release 工作流,先执行 `bash build.sh` 构建,再用 shc 编译二进制,自动生成 GitHub Release。
|
||||
- **推送 beta/alpha 标签**: 触发 Beta Release 工作流。
|
||||
## 交互层规范(lib/menu.sh 框架)
|
||||
|
||||
所有菜单必须使用统一框架,禁止手写 `while true` 菜单循环:
|
||||
|
||||
```bash
|
||||
foo_menu() {
|
||||
run_menu "标题" foo_menu_render foo_menu_dispatch "0-N"
|
||||
}
|
||||
foo_menu_render() { # 只打印功能选项与说明行,不打印返回项/页脚/读取提示
|
||||
show_menu_option "1" "..."
|
||||
}
|
||||
foo_menu_dispatch() { # case 处理选项;未识别 return 1;末尾必须 return 0
|
||||
case "$1" in
|
||||
1) do_thing ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
框架职责:清屏、标题、统一"0 返回"项(文案按嵌套深度自动生成:一级"返回主菜单"/更深"返回上级菜单")、带色读取提示、EOF 与 Ctrl+C 守卫(视为返回上级,杜绝死循环)、无效输入报错、pause 节奏(动作后暂停一次;进出子菜单与返回不暂停,由 `MENU_SKIP_PAUSE` 传递)。
|
||||
|
||||
输入原语:`prompt_value <变量名> <提示语> [默认值] [校验函数]`(默认值统一 `[默认值]` 尾括号标注;无默认时空回车=取消 return 2)、`prompt_yes_no <提示语> [默认]`、`prompt_pick_from_list <变量名> <提示> <数组名>`(0=返回,return 2,与 `vm_select_*` 选择器约定一致)、`show_report`(长输出接分页器)。
|
||||
|
||||
### 确认体系(两档)
|
||||
|
||||
- **轻档 `confirm_action "<动作描述>"`**(输入 yes):可逆的配置写入,如写 cron、apt 源重写、hdparm 参数、一般服务操作。
|
||||
- **重档 `confirm_high_risk_action <动作> <风险> <影响> <建议> <确认词>`**(输入指定确认词):以下操作**必须**使用重档 --
|
||||
写 GRUB/VFIO 配置、驱动黑名单+initramfs、安装第三方 deb/内核、apt-mark hold、LVM 删除/缩容、qm 磁盘槽位删除、Ceph 卸载、防火墙规则清空、SSH 加固、批量删除类操作。
|
||||
- 确认词风格:大写短语,如 `IOMMU-ON` / `IOMMU-OFF` / `NVIDIA-HOST` / `AMD-HOST` / `QEMU-MOD` / `DETACH` / `DESTROY-CEPH` / `APPLY-NET` / `CONFIRM`。
|
||||
- 风险横幅(`vm_show_data_risk_banner` / `host_network_show_risk_banner`)每会话仅完整展示一次。
|
||||
|
||||
### 系统配置写入规范
|
||||
|
||||
- 修改系统文件前 `backup_file`(备份至 `/var/backups/pve-tools/`)。
|
||||
- 成块写入一律走 `apply_block <file> <MARKER> <content>` / `remove_block`(`# PVE-TOOLS BEGIN/END <MARKER>` 围栏,自动备份、幂等、可清理)。GPU 直通各方案的 marker 登记与互斥检测见 `gpu_detect_active_stacks`(`src/modules/04-gpu-passthrough/iommu.sh`)。
|
||||
- GRUB 内核参数只通过 `grub_add_param` / `grub_remove_param` / `grub_has_param`(数组化按 key 精确增删查)。
|
||||
- 默认内核切换优先 `proxmox-boot-tool kernel pin`(GRUB 与 systemd-boot 均适用)。
|
||||
|
||||
## 术语表(文案统一约定)
|
||||
|
||||
| 类别 | 统一用法 | 避免 |
|
||||
|---|---|---|
|
||||
| 虚拟机 | 首次出现"虚拟机 (VM)",后文可用 VM | 混用"虚机/客户机" |
|
||||
| 动词 | 创建 / 删除 / 修改 / 查看 | 新增/添加/移除/展示 混用 |
|
||||
| 子菜单命名 | "XX 管理"(常驻功能)、"XX 向导"(一次性流程)、"XX 工具箱"(杂项集合) | 面板/助手/中心 随意混用 |
|
||||
| 颜色语义 | RED=危险/不可逆、YELLOW=警告/提示、GREEN=成功、CYAN(PRIMARY)=交互提示与补充说明、MAGENTA=强调 | 用颜色做纯装饰 |
|
||||
| 菜单提示 | `请选择操作 [0-N]: `(由框架统一输出) | 手写各种变体 |
|
||||
| 默认值标注 | `提示语 [默认值]: `(由 prompt_value 统一输出) | `(默认: x)` 等变体 |
|
||||
|
||||
## 测试策略
|
||||
|
||||
| 类型 | 方式 | 说明 |
|
||||
|---|---|---|
|
||||
| 语法检查 | `bash -n PVE-Tools.sh`、`bash -n dist/PVE-Tools.sh` | CI 中强制通过 |
|
||||
| 静态分析 | `shellcheck -f gcc PVE-Tools.sh` | CI 中强制通过,需额外关注多文件 source 模式 |
|
||||
| 版本一致性 | 比较脚本内 `CURRENT_VERSION` 与 `VERSION` 文件 | CI 中强制通过 |
|
||||
| 安全扫描 | 检测 `eval`/`source` 使用 | CI 中告警 |
|
||||
| 语法检查 | `bash -n`(入口/dev/dist 及全部源文件) | CI 强制 |
|
||||
| 静态分析 | shellcheck:入口与 dist 为 error+warning 档;lib/ 与 src/ 全量 error 档 | CI 强制 |
|
||||
| 构建一致性 | 源码与 dist 的函数集合双向 diff | CI 强制(PR 与发布前均执行) |
|
||||
| 版本一致性 | `CURRENT_VERSION` == `VERSION` == 发布 tag;UPDATE 首行含当前版本 | CI 强制 |
|
||||
| 安全扫描 | dist 中禁 `eval`、未引号变量 `rm -rf`、`source` 语句 | CI 强制 |
|
||||
| 功能测试 | 手动在 PVE 9.x 环境验证 | 无自动化 E2E 测试 |
|
||||
|
||||
**注意**: 本项目目前没有自动化单元测试或集成测试。所有功能验证依赖人工在真实或模拟的 PVE 9.x 环境中测试。模块化后,建议在每次 PR 时同时验证 `bash dev.sh` 和 `bash build.sh && bash dist/PVE-Tools.sh` 的行为一致性。
|
||||
**注意**: 本项目没有自动化单元测试。功能验证依赖人工在真实或模拟的 PVE 9.x 环境中测试。建议每次 PR 同时验证 `bash dev.sh` 与 `bash build.sh && bash -n dist/PVE-Tools.sh`。
|
||||
|
||||
## 编码规范
|
||||
|
||||
@@ -157,30 +192,28 @@ shellcheck -f gcc dist/PVE-Tools.sh
|
||||
- Shebang: `#!/bin/bash`
|
||||
- 版权声明: 每个文件头部包含 `# SPDX-License-Identifier: GPL-3.0-only` 和 `# Copyright (C) 2026 Ciriu Networks`
|
||||
- 缩进: 4 空格
|
||||
- 函数命名: `snake_case`(如 `vm_validate_new_vmid`、`host_network_get_bridges`)
|
||||
- 函数命名: `snake_case`(如 `vm_validate_new_vmid`、`host_network_get_bridges`);菜单配套函数 `<菜单名>_render` / `<菜单名>_dispatch`
|
||||
- 变量命名: `UPPER_SNAKE`(全局配置常量)、`lower_case`(局部变量)
|
||||
- 颜色: 通过 `setup_colors()` 统一管理 ANSI 颜色变量,兼容 `NO_COLOR` 环境变量
|
||||
- 日志: 使用统一日志函数 `log_info`、`log_warn`、`log_error`、`log_step`、`log_success`、`log_tips`
|
||||
- UI: 使用 `UI_BORDER`、`UI_DIVIDER`、`UI_HEADER`、`UI_FOOTER` 统一边框风格
|
||||
- 风险控制: 高风险写入操作必须使用 `confirm_high_risk_action()` 要求输入确认词
|
||||
- 配置备份: 修改系统配置文件前调用 `backup_file()` 自动备份到 `/var/backups/pve-tools/`
|
||||
- 幂等性: GRUB 参数等配置通过专用幂等管理函数修改,支持增删查
|
||||
- 日志文件: 所有操作记录到 `/var/log/pve-tools.log`
|
||||
- 模块加载: `lib/` 按 config -> core -> network -> runtime 固定顺序加载;`src/modules/` 按文件名排序
|
||||
- 颜色: 通过 `setup_colors()` 统一管理,遵循 no-color.org 规范(设置 `NO_COLOR` 即禁色)
|
||||
- 日志: `log_info`、`log_warn`、`log_error`、`log_step`、`log_success`、`log_tips`;所有操作记录到 `/var/log/pve-tools.log`
|
||||
- UI: `UI_BORDER`、`UI_DIVIDER`、`UI_HEADER`、`UI_FOOTER` 统一边框;菜单一律走 `run_menu` 框架
|
||||
- 风险控制: 按上文"确认体系(两档)"执行
|
||||
- 禁止: `eval`、未审 `source`、手写菜单循环、绕过 `qm set` 直接追加写 VM conf、无 marker 的系统配置成块写入
|
||||
|
||||
## AI 使用指引
|
||||
|
||||
- **模块理解**: 优先阅读各模块的 `CLAUDE.md` 而非直接扫描源代码。
|
||||
- **入口分析**: `PVE-Tools.sh` 仅 169 行,可完整阅读。核心逻辑为 `pve_tools_entry_source_tree()` 和 `pve_tools_entry_prepare_remote_tree()`。
|
||||
- **基础设施分析**: `lib/core.sh` 包含所有日志/UI/备份/GRUB 函数(约 500 行);`lib/runtime.sh` 包含 `main()` 函数(约 235 行)。
|
||||
- **功能模块分析**: `src/modules/` 下每个子目录的 `init.sh` 为菜单入口函数,其他文件为具体功能实现。
|
||||
- **忽略的构建产物**: `dist/`、`node_modules/`、`.vitepress/dist/`、`.vitepress/cache/` 均被 `.gitignore` 忽略且不参与分析。
|
||||
- **二进制文件**: `Modules/VGPU/*.so` 只记录路径,不读取内容。
|
||||
- **已移除模块**: `Web/` 目录及其 VitePress 文档站已于本次重构中移除。
|
||||
- **入口分析**: `PVE-Tools.sh` 约 330 行。核心为下载器函数族 `pve_tools_entry_download_file()` / `_download_with_curl()` / `_download_with_wget()` / `_validate_script()` 与末段的本地/远程分流 if 块。
|
||||
- **基础设施分析**: `lib/core.sh`(~505 行)日志/确认/备份/块写入/GRUB;`lib/menu.sh`(~186 行)菜单框架;`lib/runtime.sh`(~232 行)守卫与 `main()`。
|
||||
- **功能模块分析**: 各子目录 `init.sh` 为菜单入口(run_menu + render + dispatch),其他文件为具体功能实现。
|
||||
- **忽略的构建产物**: `dist/`、`node_modules/` 被 `.gitignore` 忽略且不参与分析。
|
||||
- **二进制文件**: `Modules/VGPU/*.so` 只记录路径,不读取内容;它是 vGPU Unlock 的下载资产,勿删。
|
||||
- **已移除**: `Web/` VitePress 文档站已移除;发布流程不使用 shc 编译二进制。
|
||||
|
||||
## 变更记录 (Changelog)
|
||||
|
||||
| 日期 | 变更 | 来源 |
|
||||
|---|---|---|
|
||||
| 2026-07-08 | 重大更新:项目模块化重构完成。430KB 单文件拆分为 lib/ (4文件) + src/modules/ (10子模块, ~60文件)。新增 build.sh/dev.sh 构建系统。Web/ 目录已移除。更新全部 CLAUDE.md 文档体系。 | claude-init 架构师(自适应版) |
|
||||
| 2026-04-28 | 初始化 CLAUDE.md 体系(根 + Web + Tools + Modules) | claude-init 架构师(自适应版) |
|
||||
| 2026-07-26 | 交互层框架化:新增 lib/menu.sh,全部菜单迁移 run_menu;确认体系两档成文;GPU 直通 marker 统一与互斥检测;GRUB 参数函数数组化;文档按现实重写(修正入口行数/远程模式/shc 等失真);仓库卫生清理;CI 护栏补全 | 交互层收口整理 |
|
||||
| 2026-07-08 | 项目模块化重构完成:单文件拆分为 lib/ + src/modules/(10 子模块)。新增 build.sh/dev.sh。Web/ 目录移除。 | claude-init 架构师 |
|
||||
| 2026-04-28 | 初始化 CLAUDE.md 体系 | claude-init 架构师 |
|
||||
|
||||
1361
Docs/重构计划-PVE-Tools模块化拆分.md
Normal file
1361
Docs/重构计划-PVE-Tools模块化拆分.md
Normal file
File diff suppressed because it is too large
Load Diff
11
PVE-Tools.sh
11
PVE-Tools.sh
@@ -11,15 +11,17 @@
|
||||
|
||||
# 模块化入口:本地开发 source 源码;远程 curl 运行时下载 dist 单文件执行。
|
||||
|
||||
PVE_TOOLS_REMOTE_BASE="${PVE_TOOLS_REMOTE_BASE:-https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main}"
|
||||
# 远程模式从 GitHub Release 资产下载构建产物(单文件完整版)。
|
||||
# 仓库 main 分支不跟踪 dist/,raw.githubusercontent.com 上没有 dist/PVE-Tools.sh,不要改回 raw 路径。
|
||||
PVE_TOOLS_RELEASE_BASE_URL="${PVE_TOOLS_RELEASE_BASE_URL:-https://github.com/PVE-Tools/PVE-Tools-9/releases}"
|
||||
PVE_TOOLS_REMOTE_MIRROR_PREFIX="${PVE_TOOLS_REMOTE_MIRROR_PREFIX:-https://ghfast.top/}"
|
||||
PVE_TOOLS_REMOTE_DIST_URL="${PVE_TOOLS_REMOTE_DIST_URL:-$PVE_TOOLS_REMOTE_BASE/dist/PVE-Tools.sh}"
|
||||
PVE_TOOLS_REMOTE_DIST_URL="${PVE_TOOLS_REMOTE_DIST_URL:-$PVE_TOOLS_RELEASE_BASE_URL/latest/download/PVE-Tools.sh}"
|
||||
PVE_TOOLS_CONNECT_TIMEOUT="${PVE_TOOLS_CONNECT_TIMEOUT:-10}"
|
||||
PVE_TOOLS_DOWNLOAD_TIMEOUT="${PVE_TOOLS_DOWNLOAD_TIMEOUT:-120}"
|
||||
PVE_TOOLS_DOWNLOAD_RETRIES="${PVE_TOOLS_DOWNLOAD_RETRIES:-2}"
|
||||
|
||||
PVE_TOOLS_RELEASE_PAGE_URL="https://github.com/PVE-Tools/PVE-Tools-9/releases/tag/v10.2.0"
|
||||
PVE_TOOLS_RELEASE_ASSET_URL="https://github.com/PVE-Tools/PVE-Tools-9/releases/download/v10.2.0/PVE-Tools.sh"
|
||||
PVE_TOOLS_RELEASE_PAGE_URL="$PVE_TOOLS_RELEASE_BASE_URL/latest"
|
||||
PVE_TOOLS_RELEASE_ASSET_URL="$PVE_TOOLS_RELEASE_BASE_URL/latest/download/PVE-Tools.sh"
|
||||
PVE_TOOLS_ENTRY_LAST_ERROR=""
|
||||
|
||||
pve_tools_entry_normalize_positive_integer() {
|
||||
@@ -285,6 +287,7 @@ if [[ -f "$SCRIPT_DIR/lib/config.sh" && -d "$SCRIPT_DIR/src/modules" ]]; then
|
||||
for lib_file in \
|
||||
"$SCRIPT_DIR/lib/config.sh" \
|
||||
"$SCRIPT_DIR/lib/core.sh" \
|
||||
"$SCRIPT_DIR/lib/menu.sh" \
|
||||
"$SCRIPT_DIR/lib/network.sh" \
|
||||
"$SCRIPT_DIR/lib/runtime.sh"; do
|
||||
if [[ ! -f "$lib_file" ]]; then
|
||||
|
||||
@@ -14,7 +14,7 @@ Proxmox VE 9.x 向けのワンストップ運用スクリプトです。VM ラ
|
||||
> 本项目は完全無料のオープンソースであり、個人の情熱だけで維持されています。Issue を立てる前や質問する前に、ドキュメント、画面上の注意書き、既存の Issue を必ずお読みください。
|
||||
> 再現手順やログなどの有効な情報がないフィードバックは直接クローズされます。オープンソースはあなたの召使いではありません。敬意は相互のものです。
|
||||
>
|
||||
> **`main` ブランチ(Shell 版)は v8.8.8 を最後に更新を終了します。今後の開発は Go 版([beta-go](https://github.com/PVE-Tools/PVE-Tools-9/tree/beta-go) ブランチ)に移行します。Go 版は引き続き無料でオープンソースとして提供されます。**
|
||||
> **お知らせ(2026-07):Shell 版はメンテナンスを再開しました。v8.8.8 での更新終了と Go 版への移行計画は撤回されています。経緯は[こちら](https://pve.u3u.icu/blog/why-go-version-is-not-updating/)をご覧ください。**
|
||||
|
||||
## 概要
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
[官网 / Docs](https://pve.u3u.icu) | [更新日志](https://pve.u3u.icu/update) | [FAQ](https://pve.u3u.icu/faq) | [English](./README_EN.md) | [日本語](./REAMDE-JP.md)
|
||||
[官网 / Docs](https://pve.u3u.icu) | [更新日志](https://pve.u3u.icu/update) | [FAQ](https://pve.u3u.icu/faq) | [English](./README_EN.md) | [日本語](./README-JP.md)
|
||||
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
[](https://www.gnu.org/software/bash/)
|
||||
@@ -112,7 +112,7 @@ PVE Tools Pro 是一个面向 Proxmox VE 9.x 的交互式 Bash 工具。
|
||||
## 其它语言
|
||||
|
||||
- English: [README_EN.md](./README_EN.md)
|
||||
- 日本語: [REAMDE-JP.md](./REAMDE-JP.md)
|
||||
- 日本語: [README-JP.md](./README-JP.md)
|
||||
|
||||
## 免责声明
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
An all-in-one operations script for Proxmox VE 9.x, covering VM lifecycle workflows, host networking / firewall / IPv6, GPU / PCI passthrough, day-to-day maintenance, and third-party integrations.
|
||||
|
||||
[Docs](https://pve.u3u.icu) | [Changelog](https://pve.u3u.icu/update) | [FAQ](https://pve.u3u.icu/faq) | [中文](./README.md) | [日本語](./REAMDE-JP.md)
|
||||
[Docs](https://pve.u3u.icu) | [Changelog](https://pve.u3u.icu/update) | [FAQ](https://pve.u3u.icu/faq) | [中文](./README.md) | [日本語](./README-JP.md)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@ An all-in-one operations script for Proxmox VE 9.x, covering VM lifecycle workfl
|
||||
> This project is free and open source, maintained solely by personal passion. Before opening an issue or asking a question, please make sure you have read the documentation, on-screen notices, and existing issues thoroughly.
|
||||
> Feedback without reproduction steps, logs, or other useful information will be closed directly. Open source does not make anyone your servant. Respect is a two-way street.
|
||||
>
|
||||
> **The `main` branch (Shell version) will stop receiving updates after v8.8.8. Future development will move to the Go rewrite ([beta-go](https://github.com/PVE-Tools/PVE-Tools-9/tree/beta-go) branch), which will remain free and open source.**
|
||||
> **Update (2026-07): the Shell version has resumed active maintenance. The earlier plan to freeze `main` after v8.8.8 and migrate to a Go rewrite has been withdrawn — see [why the Go version stalled](https://pve.u3u.icu/blog/why-go-version-is-not-updating/).**
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -65,7 +65,7 @@ If you need one-on-one remote support, emergency recovery, passthrough troublesh
|
||||
## Other Languages
|
||||
|
||||
- 中文: [README.md](./README.md)
|
||||
- 日本語: [REAMDE-JP.md](./REAMDE-JP.md)
|
||||
- 日本語: [README-JP.md](./README-JP.md)
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
||||
38
UPDATE
38
UPDATE
@@ -1,13 +1,29 @@
|
||||
v8.8.8
|
||||
- 换源功能新增 debian-security 镜像源支持(中科大、清华源自动配置安全更新镜像)
|
||||
- 主菜单新增"尝鲜 Go 版本"入口,为后续 Go 语言重写版本预留体验通道
|
||||
- 系统优化菜单调整:将"一键优化"提前至第二项,并在菜单顶部添加 SSH 环境使用提示
|
||||
- 腾讯云源提示文案优化,明确说明 PVE / CT / Ceph 沿用官方源的原因
|
||||
- 版本号同步提升到 8.8.8,代号更新为 Coluccis
|
||||
- Shell将无限期停止维护,正式版将使用Go重构。查看详细:https://github.com/PVE-Tools/PVE-Tools-9/tree/beta-go
|
||||
v11.0.0
|
||||
- 交互层框架化:新增 lib/menu.sh,全部菜单迁移 run_menu,统一 EOF/Ctrl+C 守卫与 pause 节奏
|
||||
- 确认体系两档成文:轻档 confirm_action / 重档 confirm_high_risk_action(指定确认词)
|
||||
- GPU 直通加固:marker 统一写入、多方案互斥检测、IOMMU 开/关强制确认;去掉写死 PCI ID 与默认核显黑名单
|
||||
- GRUB 参数数组化精确增删,新增 grub_has_param;默认内核优先 proxmox-boot-tool kernel pin
|
||||
- 远程安装与自更新改走 GitHub Releases 完整单文件;修正 vGPU so 真实路径
|
||||
- CI 护栏补全:源码 shellcheck、构建函数一致性、UPDATE 新鲜度、dist 安全扫描、发版前校验与 SHA256SUMS
|
||||
- 文档与仓库卫生:按现实重写 CLAUDE/AGENTS;修正 README-JP 文件名;清理历史截图与无关大文件
|
||||
- Build 代号更新为 Liino
|
||||
|
||||
v7.7.0
|
||||
- 修复阿里云换源时 Ceph 镜像指向不存在地址(404)的 BUG,改为使用官方 Ceph 源
|
||||
- 优化换源功能的日志提示,明确告知用户哪些源使用了镜像站、哪些沿用官方源
|
||||
- 版本号同步提升到 7.7.0
|
||||
v10.2.2
|
||||
- IT87 传感器驱动安装加固:仓库引用锁定为 PGP 签名提交 SHA,防止上游变更引入未审计代码
|
||||
- IT87 安装流程补齐 backup_file 备份、mktemp 临时目录、DKMS 版本格式兼容与 read -r 修正
|
||||
|
||||
v10.2.1
|
||||
- 仓库不再跟踪 dist/ 构建产物,发布文件统一由 CI 构建并上传至 GitHub Releases
|
||||
- 远程引导下载增强:重试/超时/限速守护、GitHub 与加速源自动切换、下载校验与原子落盘
|
||||
|
||||
v10.1.0
|
||||
- 新增 CVE 漏洞修补中心(Januscape / Dirty Frag / Copy Fail 本地提权漏洞缓解)
|
||||
- 远程引导简化为下载单文件构建产物;本地模式显式优先加载各模块 init.sh
|
||||
- 版本比较剥离预发布后缀,beta 版本不再被误判为可更新的新版
|
||||
|
||||
v9.0.0
|
||||
- 项目模块化重构:14000+ 行单文件拆分为 lib/ 基础设施层 + src/modules/ 10 个功能模块
|
||||
- 新增 build.sh 构建系统与 dev.sh 开发入口;发布仍为单文件,用户侧零感知
|
||||
- Shell 版本恢复持续维护,此前"停更转 Go"的计划已撤回
|
||||
|
||||
更早期版本历史请见 GitHub Releases 与 git log。
|
||||
|
||||
2
build.sh
2
build.sh
@@ -19,7 +19,7 @@ mkdir -p "$(dirname "$OUTPUT")"
|
||||
echo "# Copyright (C) 2026 Ciriu Networks"
|
||||
echo
|
||||
|
||||
for lib_name in config.sh core.sh network.sh runtime.sh; do
|
||||
for lib_name in config.sh core.sh menu.sh network.sh runtime.sh; do
|
||||
lib_file="$ROOT/lib/$lib_name"
|
||||
if [[ ! -f "$lib_file" ]]; then
|
||||
echo "Missing lib file: $lib_file" >&2
|
||||
|
||||
1
dev.sh
1
dev.sh
@@ -7,6 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
for lib_file in \
|
||||
"$SCRIPT_DIR/lib/config.sh" \
|
||||
"$SCRIPT_DIR/lib/core.sh" \
|
||||
"$SCRIPT_DIR/lib/menu.sh" \
|
||||
"$SCRIPT_DIR/lib/network.sh" \
|
||||
"$SCRIPT_DIR/lib/runtime.sh"; do
|
||||
# shellcheck source=/dev/null
|
||||
|
||||
BIN
home-full.png
BIN
home-full.png
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
106
lib/CLAUDE.md
106
lib/CLAUDE.md
@@ -4,116 +4,126 @@
|
||||
|
||||
## 模块职责
|
||||
|
||||
提供 PVE Tools Pro 全部功能模块所需的基础设施函数与全局变量,零业务逻辑。按固定顺序加载:config.sh -> core.sh -> network.sh -> runtime.sh。
|
||||
提供 PVE Tools Pro 全部功能模块所需的基础设施函数与全局变量,零业务逻辑。按固定顺序加载:config.sh -> core.sh -> menu.sh -> network.sh -> runtime.sh。
|
||||
|
||||
## 入口与启动
|
||||
|
||||
| 项目 | 说明 |
|
||||
|---|---|
|
||||
| 加载方式 | 由 `PVE-Tools.sh`(生产)或 `dev.sh`(开发)按固定顺序 source |
|
||||
| 加载顺序 | `config.sh` -> `core.sh` -> `network.sh` -> `runtime.sh`(严格) |
|
||||
| 加载方式 | 由 `PVE-Tools.sh`(本地模式)、`dev.sh`(开发)source,或由 `build.sh` 拼接进 dist |
|
||||
| 加载顺序 | `config.sh` -> `core.sh` -> `menu.sh` -> `network.sh` -> `runtime.sh`(三处入口硬编码同一列表) |
|
||||
| 对外暴露 | 全部函数和全局变量对后续加载的 `src/modules/` 可见 |
|
||||
|
||||
## 文件清单
|
||||
|
||||
### config.sh -- 全局变量与常量(约 254 行)
|
||||
### config.sh -- 全局变量与常量(约 259 行,无函数)
|
||||
|
||||
| 类别 | 变量 | 说明 |
|
||||
|---|---|---|
|
||||
| 版本信息 | `CURRENT_VERSION`, `BUILD_NICKNAME`, `VERSION_FILE_URL`, `UPDATE_FILE_URL`, `PVE_TOOLS_SCRIPT_URL` | 版本号、更新 URL |
|
||||
| 镜像源注册表 | `MIRROR_NAMES[]`, `MIRROR_IDS[]`, `MIRROR_DEBIAN_URIS[]`, `MIRROR_SECURITY_URIS[]`, `MIRROR_PVE_URIS[]`, `MIRROR_CEPH_URIS[]`, `MIRROR_CT_URIS[]` | 20+ 个镜像源(官方/阿里云/腾讯云/清华/中科大等) |
|
||||
| 镜像选择状态 | `MIRROR_SELECTED_DEBIAN`, `MIRROR_SELECTED_SECURITY`, `MIRROR_SELECTED_PVE`, `MIRROR_SELECTED_CEPH`, `MIRROR_SELECTED_CT` | 当前选中的镜像索引 |
|
||||
| 版本信息 | `CURRENT_VERSION`, `BUILD_NICKNAME`, `VERSION_FILE_URL`, `UPDATE_FILE_URL`, `PVE_TOOLS_SCRIPT_URL` | 版本号与更新 URL(脚本更新指向 GitHub Releases latest) |
|
||||
| 镜像源注册表 | `MIRROR_NAMES[]`, `MIRROR_IDS[]`, `MIRROR_DEBIAN_URIS[]`, `MIRROR_SECURITY_URIS[]`, `MIRROR_PVE_URIS[]`, `MIRROR_CEPH_URIS[]`, `MIRROR_CT_URIS[]` | 24 个镜像源并行数组 |
|
||||
| 镜像选择状态 | `MIRROR_SELECTED_*` | 当前选中的镜像索引(-1 未选) |
|
||||
| 网络检测 | `CF_TRACE_URL`, `GITHUB_MIRROR_PREFIX`, `USER_COUNTRY_CODE`, `NETWORK_MODE`, `IS_OFFLINE_MODE`, `USE_MIRROR_FOR_UPDATE` | Cloudflare 地区检测、镜像加速 |
|
||||
| URL 常量 | `FASTPVE_INSTALLER_URL`, `COOLERCONTROL_*_URL`, `NVIDIA_*_URL`, `THIRD_PARTY_*_URL` | 各功能的外部资源地址 |
|
||||
| 路径常量 | `VM_CONFIG_EXPORT_DIR`, `VM_BACKUP_CRON_FILE`, `HOST_NETWORK_*_FILE`, `PVE_CLUSTER_FIREWALL_FILE`, `PVE_KVM_ROM_DIR` | 运行时文件路径 |
|
||||
| PVE 版本 | `PVE_VERSION_DETECTED`, `PVE_MAJOR_VERSION` | 运行时检测结果 |
|
||||
| 安全 | `RISK_ACK_BYPASS`, `DEBUG_MODE`, `LEGAL_VERSION` | 风险控制标志 |
|
||||
|
||||
### core.sh -- 核心工具函数(约 507 行)
|
||||
### core.sh -- 核心工具函数(约 505 行)
|
||||
|
||||
| 函数 | 说明 |
|
||||
|---|---|
|
||||
| `setup_colors()` | 颜色系统初始化,支持 `NO_COLOR` 环境变量 |
|
||||
| `setup_colors()` | 颜色系统初始化,遵循 no-color.org(设置 `NO_COLOR` 即禁色) |
|
||||
| `log_info()`, `log_warn()`, `log_error()`, `log_step()`, `log_success()`, `log_tips()` | 统一日志(带时间戳,写入 `/var/log/pve-tools.log`) |
|
||||
| `display_error()`, `display_success()` | 带提示的增增强错误/成功反馈 |
|
||||
| `confirm_action()` | 通用确认提示(输入 'yes') |
|
||||
| `confirm_high_risk_action()` | 高风险确认(需输入指定确认词如 `CONFIRM`) |
|
||||
| `vm_show_data_risk_banner()` | VM 数据风险横幅 |
|
||||
| `display_error()`, `display_success()` | 增强错误/成功反馈(均不内置 pause,由菜单框架统一节奏) |
|
||||
| `confirm_action()` | 轻档确认(输入 'yes'),用于可逆配置写入 |
|
||||
| `confirm_high_risk_action()` | 重档确认(输入指定确认词),用于 GRUB/LVM/磁盘槽位等高危操作 |
|
||||
| `vm_show_data_risk_banner()` | VM 数据风险横幅(每会话仅完整展示一次) |
|
||||
| `ensure_legal_acceptance()` | 许可条款首次接受检查 |
|
||||
| `backup_file()` | 配置文件备份到 `/var/backups/pve-tools/` |
|
||||
| `backup_file()` | 配置文件备份到 `/var/backups/pve-tools/`,可回填备份路径 |
|
||||
| `pve_tools_download_url()`, `pve_tools_download_file()` | curl/wget 封装的下载工具 |
|
||||
| `pve_tools_choose_update_urls()`, `pve_tools_version_gt()` | 更新 URL 选择与版本比较 |
|
||||
| `apply_block()`, `remove_block()` | 标记配置块写入/删除(`# PVE-TOOLS BEGIN/END`) |
|
||||
| `grub_add_param()`, `grub_remove_param()` | GRUB 内核参数幂等管理 |
|
||||
| `show_progress()`, `update_progress()`, `show_status()`, `show_progress_bar()` | 进度指示与状态反馈 |
|
||||
| `pve_tools_choose_update_urls()`, `pve_tools_version_gt()` | 更新 URL 选择与版本比较(剥离预发布后缀) |
|
||||
| `apply_block()`, `remove_block()` | marker 配置块写入/删除(`# PVE-TOOLS BEGIN/END <MARKER>`,写前自动备份、幂等) |
|
||||
| `grub_has_param()`, `grub_add_param()`, `grub_remove_param()` | GRUB 内核参数查/增/删:数组化按 key 精确匹配,兼容 `key` 与 `key=value` 传参 |
|
||||
| `show_status()` | 状态行反馈 |
|
||||
| `pause_function()` | "按任意键继续..." |
|
||||
| `show_menu_header()`, `show_menu_footer()`, `show_menu_option()` | 统一菜单 UI |
|
||||
| `show_menu_header()`, `show_menu_footer()`, `show_menu_option()` | 菜单 UI 三件套(被 menu.sh 框架复用) |
|
||||
|
||||
### network.sh -- 网络基础设施(约 218 行,仅头部已读)
|
||||
### menu.sh -- 统一菜单交互框架(约 186 行)
|
||||
|
||||
全项目菜单一致性的唯一事实来源,使用规范见根 CLAUDE.md「交互层规范」。
|
||||
|
||||
| 函数 | 说明 |
|
||||
|---|---|
|
||||
| `run_menu <标题> <渲染函数> <分发函数> <范围>` | 统一菜单循环:清屏/标题/渲染/统一 0 返回项/读取/EOF 与 Ctrl+C 守卫/分发/pause 节奏 |
|
||||
| `menu_prompt <变量名> <范围>` | 统一读取;EOF/中断视为选 0(返回上级),杜绝死循环刷屏 |
|
||||
| `prompt_value <变量名> <提示> [默认值] [校验函数]` | 单值输入:默认值统一 `[默认值]` 标注;无默认时空回车=取消(return 2) |
|
||||
| `prompt_yes_no <提示> [默认]` | 是/否询问(EOF 一律按 no 保守处理) |
|
||||
| `prompt_pick_from_list <变量名> <提示> <数组名>` | 编号列表选择器:0=返回(return 2),与 `vm_select_*` 约定一致 |
|
||||
| `show_report` | 长输出分页展示(tty + less 时走分页器,否则 cat) |
|
||||
| 全局状态 | `MENU_DEPTH`(嵌套深度,决定返回文案)、`MENU_SKIP_PAUSE`(子菜单返回时跳过父层 pause) |
|
||||
|
||||
### network.sh -- 网络基础设施(约 444 行)
|
||||
|
||||
| 函数 | 说明 |
|
||||
|---|---|
|
||||
| `detect_network_region()` | Cloudflare Trace 检测用户地区(CN 自动启用镜像) |
|
||||
| `fetch_session_tip()` | 一言 API 获取每日提示 |
|
||||
| `network_show_diagnostics()` | 网络诊断(IP/路由/DNS) |
|
||||
| `network_can_access_internet()`, `network_offline_guard()` | 连通性检测与离线模式 |
|
||||
| `disable_ups_service()`, `enable_ups_service()`, `show_ups_diagnostics()` | NUT UPS 服务管理 |
|
||||
| `mirror_*()` 系列函数 | 镜像源选择系统(URI 查询、选择、重置、汇总、推荐提示) |
|
||||
| `select_mirror()` | 镜像统一选择入口 |
|
||||
| `fetch_session_tip()` | 一言 API 获取会话提示 |
|
||||
| `network_show_diagnostics()`, `network_can_access_internet()`, `network_offline_guard()` | 网络诊断/连通性/离线守卫 |
|
||||
| `disable_ups_service()`, `enable_ups_service()`, `show_ups_diagnostics()` | NUT UPS 服务管理(菜单入口在 01 模块顶级项) |
|
||||
| `show_banner()` | 启动横幅显示 |
|
||||
| `mirror_*()` 系列 | 镜像注册表查询/选择状态管理 |
|
||||
| `select_mirror()`, `select_mirror_unified()`, `select_mirror_per_source()`, `select_mirror_for_source()` | 镜像选择交互("单选即退"选择器语义,未套 run_menu 属有意设计) |
|
||||
|
||||
### runtime.sh -- 运行时守卫与主入口(约 235 行)
|
||||
### runtime.sh -- 运行时守卫与主入口(约 232 行)
|
||||
|
||||
| 函数 | 说明 |
|
||||
|---|---|
|
||||
| `check_root()` | root 权限检查 |
|
||||
| `check_debug_mode()` | 解析 `--i-know-what-i-do` 和 `--debug` 参数 |
|
||||
| `check_packages()` | 依赖包检查(sudo, curl) |
|
||||
| `check_pve_version()` | PVE 版本检测(非 PVE9 环境拦截高风险操作) |
|
||||
| `block_non_pve9_destructive()` | 非 PVE9 环境拦截一键优化/换源/升级等破坏性操作 |
|
||||
| `check_pve_version()` | PVE 版本检测(非 PVE9 环境需确认风险,拦截高危自动化) |
|
||||
| `block_non_pve9_destructive()` | 非 PVE9 环境拦截换源/升级等破坏性操作 |
|
||||
| `show_menu()` | 主菜单(10 个功能入口 + 一言 Tips) |
|
||||
| `main()` | **脚本主入口**:权限检查 -> 许可确认 -> 调试模式 -> PVE 版本 -> 网络检测 -> 一言 -> 更新检查 -> 主循环 |
|
||||
| `main()` | **脚本主入口**:trap INT(Ctrl+C 中断当前输入回上级而非杀进程)-> 权限/许可/调试/PVE 版本/网络检查 -> 更新检查 -> 主循环(read 带 EOF 守卫优雅退出;子菜单返回不重复 pause) |
|
||||
|
||||
## 关键依赖与配置
|
||||
|
||||
- **运行环境**: Proxmox VE 9.x (Debian 13 Trixie),root 权限
|
||||
- **外部依赖**: `curl` 或 `wget`(网络操作)、`sudo`(权限提升)
|
||||
- **加载顺序严格**: `config.sh` 必须先于 `core.sh` 加载(全局变量依赖);`runtime.sh` 最后加载(包含 `main()` 函数)
|
||||
- **全局变量**: 所有以大写字母开头的变量为全局变量,`src/modules/` 中所有脚本均可直接访问
|
||||
|
||||
## 数据模型
|
||||
|
||||
本模块不涉及持久化数据。全局变量在脚本运行期间存在于内存中。`/var/lib/pve-tools/legal_acceptance_*` 文件用于记录许可条款接受状态。
|
||||
- **外部依赖**: `curl` 或 `wget`(网络操作)
|
||||
- **加载顺序严格**: `config.sh` 定义全局变量必须最先;`runtime.sh` 含 `main()` 必须最后;`menu.sh` 在 `core.sh` 之后(复用其 UI 三件套与 pause)
|
||||
- **全局变量**: 大写变量为全局,`src/modules/` 中所有脚本可直接访问
|
||||
|
||||
## 测试与质量
|
||||
|
||||
- **语法检查**: `bash -n` 对每个 `lib/*.sh` 文件
|
||||
- **静态分析**: `shellcheck` 覆盖(需配合 `--source-path` 处理跨文件引用)
|
||||
- **CI 覆盖**: 所有 lib 文件在 `pr-validation.yml` 的 shellcheck 范围内
|
||||
- **语法检查**: `bash -n` 对每个 `lib/*.sh`
|
||||
- **静态分析**: CI 对 lib/ 全量执行 `shellcheck --severity=error`
|
||||
- **行为验证**: 菜单框架的 EOF/返回节奏可用管道按键序列冒烟(见根 CLAUDE.md 测试策略)
|
||||
|
||||
## 常见问题 (FAQ)
|
||||
|
||||
**Q: 为什么加载顺序很重要?**
|
||||
`config.sh` 定义全局变量,`core.sh` 的 `setup_colors()` 和日志函数依赖颜色变量,`network.sh` 的镜像选择函数依赖 `core.sh` 中的 UI 函数,`runtime.sh` 的 `main()` 调用 `network.sh` 和 `core.sh` 中的函数。
|
||||
`config.sh` 定义全局变量;`core.sh` 的颜色/日志/UI 被后续所有文件使用;`menu.sh` 依赖 core 的 UI 三件套;`runtime.sh` 的 `main()` 调用前面所有层。函数定义顺序本身不影响 bash 运行,但变量初始化(如 `setup_colors` 在 source 时执行)有先后依赖。
|
||||
|
||||
**Q: 可以在 src/modules/ 中重新加载 lib 文件吗?**
|
||||
不需要。`PVE-Tools.sh` 和 `dev.sh` 在加载任何模块之前已经 source 了所有 lib 文件,所有函数和变量对后续加载的模块全局可见。
|
||||
不需要。所有入口在加载任何模块之前已经 source 了全部 lib 文件。
|
||||
|
||||
**Q: config.sh 中新增镜像源如何操作?**
|
||||
在每个并行数组末尾追加对应元素即可。`MIRROR_NAMES`、`MIRROR_IDS`、`MIRROR_*_URIS` 数组的索引一一对应。
|
||||
在每个并行数组末尾追加对应元素即可,`MIRROR_*` 数组索引一一对应。
|
||||
|
||||
## 相关文件清单
|
||||
|
||||
```
|
||||
lib/
|
||||
config.sh # 全局变量与常量(254行)
|
||||
core.sh # 核心工具函数(507行)
|
||||
network.sh # 网络检测与镜像选择(~218行+镜像函数)
|
||||
runtime.sh # 运行时守卫与 main()(235行)
|
||||
config.sh # 全局变量与常量(~259 行)
|
||||
core.sh # 核心工具函数(~505 行)
|
||||
menu.sh # 统一菜单交互框架(~186 行)
|
||||
network.sh # 网络检测与镜像选择(~444 行)
|
||||
runtime.sh # 运行时守卫与 main()(~232 行)
|
||||
```
|
||||
|
||||
## 变更记录 (Changelog)
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-07-26 | 新增 menu.sh 菜单框架;display_error 去内置 pause;grub 参数函数数组化并新增 grub_has_param;main() 加 trap INT 与 EOF 守卫;移除死代码 check_packages/show_progress/update_progress/show_progress_bar;行数与函数索引按现实修正 |
|
||||
| 2026-07-08 | 初始化 lib 模块 CLAUDE.md(模块化重构新增模块) |
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
# 版本信息
|
||||
CURRENT_VERSION="10.2.2"
|
||||
BUILD_NICKNAME="Evanescia"
|
||||
CURRENT_VERSION="11.0.0"
|
||||
BUILD_NICKNAME="Liino"
|
||||
VERSION_FILE_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/VERSION"
|
||||
UPDATE_FILE_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/UPDATE"
|
||||
PVE_TOOLS_SCRIPT_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/PVE-Tools.sh"
|
||||
# 自更新必须下载 Release 资产(构建产物单文件完整版)。
|
||||
# raw main 上的 PVE-Tools.sh 只是 300 行引导入口,不含 CURRENT_VERSION,用它自更新永远过不了校验。
|
||||
PVE_TOOLS_SCRIPT_URL="https://github.com/PVE-Tools/PVE-Tools-9/releases/latest/download/PVE-Tools.sh"
|
||||
PVE_VERSION_DETECTED=""
|
||||
PVE_MAJOR_VERSION=""
|
||||
RISK_ACK_BYPASS=false
|
||||
@@ -245,8 +247,9 @@ COOLERCONTROL_DEB_SETUP_URL="https://dl.cloudsmith.io/public/coolercontrol/coole
|
||||
IT87_REPO_URL="https://github.com/shauno8/it87.git"
|
||||
IT87_REPO_REF="81ae4a610ea763fa7f8cd195e712c41f33207264" # 已验证的 PGP 签名提交
|
||||
IT87_DKMS_NAME="it87"
|
||||
NVIDIA_ASSETS_BASE_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/Modules/NVIDIA"
|
||||
NVIDIA_VGPU_UNLOCK_SO_URL="${NVIDIA_ASSETS_BASE_URL}/libvgpu_unlock_rs.so"
|
||||
# 仓库中实际跟踪的路径是 Modules/VGPU/(带版本号文件名),Modules/NVIDIA/ 不存在
|
||||
NVIDIA_ASSETS_BASE_URL="https://raw.githubusercontent.com/PVE-Tools/PVE-Tools-9/main/Modules/VGPU"
|
||||
NVIDIA_VGPU_UNLOCK_SO_URL="${NVIDIA_ASSETS_BASE_URL}/libvgpu_unlock_rs_20230207_44d5bb3.so"
|
||||
VM_CONFIG_EXPORT_DIR="/var/lib/pve-tools/vm-config-exports"
|
||||
VM_BACKUP_CRON_FILE="/etc/cron.d/pve-tools-vm-backup"
|
||||
VM_DEFAULT_CLOUDINIT_BRIDGE="vmbr0"
|
||||
|
||||
112
lib/core.sh
112
lib/core.sh
@@ -3,7 +3,8 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
setup_colors() {
|
||||
if [[ -t 1 && -z "${NO_COLOR}" ]]; then
|
||||
# NO_COLOR 遵循 no-color.org 规范:只要设置了该变量(即使为空值)即禁用颜色
|
||||
if [[ -t 1 && -z "${NO_COLOR+x}" ]]; then
|
||||
# 使用 printf 确保变量包含真实的转义字符,提高不同 shell 间的兼容性
|
||||
RED=$(printf '\033[0;31m')
|
||||
GREEN=$(printf '\033[0;32m')
|
||||
@@ -22,7 +23,7 @@ setup_colors() {
|
||||
H1=$(printf '\033[1;36m')
|
||||
H2=$(printf '\033[1;37m')
|
||||
else
|
||||
RED='' GREEN='' YELLOW='' BLUE='' CYAN='' MAGENTA='' WHITE='' ORANGE='' NC=''
|
||||
RED='' GREEN='' YELLOW='' BLUE='' PINK='' CYAN='' MAGENTA='' WHITE='' ORANGE='' NC=''
|
||||
PRIMARY='' H1='' H2=''
|
||||
fi
|
||||
|
||||
@@ -73,7 +74,6 @@ display_error() {
|
||||
|
||||
log_error "$error_msg"
|
||||
echo -e "${YELLOW}提示: $suggestion${NC}"
|
||||
pause_function
|
||||
}
|
||||
|
||||
# Enhanced success feedback
|
||||
@@ -125,6 +125,11 @@ confirm_high_risk_action() {
|
||||
return 1
|
||||
}
|
||||
vm_show_data_risk_banner() {
|
||||
# 每会话仅完整展示一次,避免每次重绘菜单都整屏刷同一段风险横幅
|
||||
if [[ "${VM_RISK_BANNER_SHOWN:-0}" -eq 1 ]]; then
|
||||
return 0
|
||||
fi
|
||||
VM_RISK_BANNER_SHOWN=1
|
||||
echo -e "${RED}${UI_DIVIDER}${NC}"
|
||||
echo -e "${RED}以下操作将直接改写 VM 配置、磁盘、快照、克隆、恢复或迁移状态。${NC}"
|
||||
echo -e "${RED}本软件会执行您要求它做的事情,不会替您判断对错。${NC}"
|
||||
@@ -329,6 +334,22 @@ remove_block() {
|
||||
|
||||
# ============ GRUB 参数幂等管理函数 ============
|
||||
|
||||
# 检查 GRUB_CMDLINE_LINUX_DEFAULT 是否已含指定参数(按 key 精确匹配,不受注释/其他行干扰)
|
||||
# 用法: grub_has_param "intel_iommu=on" 或 grub_has_param "intel_iommu"
|
||||
grub_has_param() {
|
||||
local param_key="${1%%=*}"
|
||||
local current_line current_params
|
||||
current_line=$(grep '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub 2>/dev/null) || return 1
|
||||
current_params=$(echo "$current_line" | sed 's/^GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"$/\1/')
|
||||
local -a items=()
|
||||
read -r -a items <<< "$current_params"
|
||||
local item
|
||||
for item in "${items[@]}"; do
|
||||
[[ "${item%%=*}" == "$param_key" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# 添加 GRUB 参数(幂等操作,不会重复添加)
|
||||
# 用法: grub_add_param "intel_iommu=on"
|
||||
grub_add_param() {
|
||||
@@ -353,16 +374,20 @@ grub_add_param() {
|
||||
# 提取引号内的参数
|
||||
local current_params=$(echo "$current_line" | sed 's/^GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"$/\1/')
|
||||
|
||||
# 检查参数是否已存在(支持 key=value 和 key 两种格式)
|
||||
local param_key=$(echo "$param" | cut -d'=' -f1)
|
||||
|
||||
if echo "$current_params" | grep -qw "$param_key"; then
|
||||
# 参数已存在,先删除旧值
|
||||
current_params=$(echo "$current_params" | sed "s/\b${param_key}[^ ]*\b//g")
|
||||
# 数组化精确增删:按空格切分后逐项比对 key('=' 前部分),
|
||||
# 避免旧正则 \b$key[^ ]* 在 key 含 '.' 等元字符时的前缀误伤/误删
|
||||
local param_key="${param%%=*}"
|
||||
local -a items=() kept=()
|
||||
read -r -a items <<< "$current_params"
|
||||
local item
|
||||
for item in "${items[@]}"; do
|
||||
if [[ "${item%%=*}" != "$param_key" ]]; then
|
||||
kept+=("$item")
|
||||
fi
|
||||
done
|
||||
kept+=("$param")
|
||||
|
||||
# 添加新参数(去除多余空格)
|
||||
local new_params=$(echo "$current_params $param" | sed 's/ */ /g' | sed 's/^ //;s/ $//')
|
||||
local new_params="${kept[*]}"
|
||||
|
||||
# 写回配置文件
|
||||
sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"$new_params\"|" /etc/default/grub
|
||||
@@ -371,7 +396,7 @@ grub_add_param() {
|
||||
}
|
||||
|
||||
# 删除 GRUB 参数(精确删除,不影响其他参数)
|
||||
# 用法: grub_remove_param "intel_iommu=on"
|
||||
# 用法: grub_remove_param "intel_iommu=on" 或 grub_remove_param "intel_iommu"
|
||||
grub_remove_param() {
|
||||
local param="$1"
|
||||
|
||||
@@ -394,9 +419,19 @@ grub_remove_param() {
|
||||
# 提取引号内的参数
|
||||
local current_params=$(echo "$current_line" | sed 's/^GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"$/\1/')
|
||||
|
||||
# 删除指定参数(支持精确匹配和前缀匹配)
|
||||
local param_key=$(echo "$param" | cut -d'=' -f1)
|
||||
local new_params=$(echo "$current_params" | sed "s/\b${param_key}[^ ]*\b//g" | sed 's/ */ /g' | sed 's/^ //;s/ $//')
|
||||
# 数组化精确删除:按 key 匹配(兼容传入 "key" 与 "key=value" 两种形式)
|
||||
local param_key="${param%%=*}"
|
||||
local -a items=() kept=()
|
||||
read -r -a items <<< "$current_params"
|
||||
local item
|
||||
for item in "${items[@]}"; do
|
||||
if [[ "${item%%=*}" == "$param_key" ]]; then
|
||||
continue
|
||||
fi
|
||||
kept+=("$item")
|
||||
done
|
||||
|
||||
local new_params="${kept[*]}"
|
||||
|
||||
# 写回配置文件
|
||||
sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"$new_params\"|" /etc/default/grub
|
||||
@@ -406,33 +441,6 @@ grub_remove_param() {
|
||||
|
||||
# ============ GRUB 参数幂等管理函数结束 ============
|
||||
|
||||
# 进度指示函数
|
||||
show_progress() {
|
||||
local message="$1"
|
||||
local spinner="|/-\\"
|
||||
local i=0
|
||||
# Print initial message
|
||||
echo -ne "${CYAN}[ ]${NC} $message\033[0K\r"
|
||||
|
||||
# Update the spinner position in the box
|
||||
while true; do
|
||||
i=$(( (i + 1) % 4 ))
|
||||
echo -ne "\b\b\b\b\b${CYAN}[${spinner:$i:1}]${NC}\033[0K\r"
|
||||
sleep 0.1
|
||||
done &
|
||||
# Store the background job ID to be killed later
|
||||
SPINNER_PID=$!
|
||||
}
|
||||
update_progress() {
|
||||
local message="$1"
|
||||
# Kill the spinner if running
|
||||
if [[ -n "$SPINNER_PID" ]]; then
|
||||
kill $SPINNER_PID 2>/dev/null
|
||||
fi
|
||||
echo -ne "${GREEN}[ OK ]${NC} $message\033[0K\r"
|
||||
echo
|
||||
}
|
||||
|
||||
# Enhanced visual feedback function
|
||||
show_status() {
|
||||
local status="$1"
|
||||
@@ -461,26 +469,6 @@ show_status() {
|
||||
esac
|
||||
}
|
||||
|
||||
# Progress bar function
|
||||
show_progress_bar() {
|
||||
local current="$1"
|
||||
local total="$2"
|
||||
local message="$3"
|
||||
local width=40
|
||||
local percentage=$(( current * 100 / total ))
|
||||
local filled=$(( width * current / total ))
|
||||
|
||||
printf "${CYAN}[${NC}"
|
||||
for ((i=0; i<filled; i++)); do
|
||||
printf "█"
|
||||
done
|
||||
for ((i=filled; i<width; i++)); do
|
||||
printf " "
|
||||
done
|
||||
printf "${CYAN}]${NC} ${percentage}%% $message\r"
|
||||
}
|
||||
|
||||
# 通过 Cloudflare Trace 检测地区,决定是否启用镜像源
|
||||
pause_function() {
|
||||
echo -n "按任意键继续... "
|
||||
read -n 1 -s input
|
||||
|
||||
186
lib/menu.sh
Normal file
186
lib/menu.sh
Normal file
@@ -0,0 +1,186 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
# ============ 统一菜单交互框架 ============
|
||||
#
|
||||
# 约定(全项目菜单一致性的唯一事实来源):
|
||||
# - 0 恒为"返回上级";仅主菜单的 0 表示退出脚本(由 runtime.sh main() 处理)。
|
||||
# - 菜单循环统一由 run_menu 驱动:清屏 -> 标题 -> 渲染选项 -> 统一返回项 ->
|
||||
# 读取(含 EOF / Ctrl+C 守卫)-> 分发 -> pause 节奏控制。
|
||||
# - 渲染函数:只打印功能选项(show_menu_option),不打印返回项/页脚/读取提示。
|
||||
# - 分发函数:case "$1" 处理选项;无法识别的输入 return 1(由框架统一报错),
|
||||
# 其余分支执行完后 return 0(动作本身的成败由动作内部反馈,不影响菜单流转)。
|
||||
# - 选 0 返回、或从子菜单退回父菜单时,不再触发"按任意键继续"。
|
||||
|
||||
# 当前菜单嵌套深度:主循环为 0,模块顶级菜单为 1,再往下递增
|
||||
MENU_DEPTH=0
|
||||
# 置 1 时跳过本轮分发后的 pause(run_menu 退出时自动置位,通知父层菜单)
|
||||
MENU_SKIP_PAUSE=0
|
||||
|
||||
# 统一读取菜单选择:menu_prompt <变量名> <范围串如 "0-5">
|
||||
# EOF(Ctrl+D/输入流关闭)或输入被 Ctrl+C 中断时视为选择 0(返回上级),杜绝死循环刷屏
|
||||
menu_prompt() {
|
||||
local __var_name="$1"
|
||||
local range="$2"
|
||||
local __input=""
|
||||
|
||||
echo
|
||||
echo -ne " ${PRIMARY}请选择操作 [${range}]: ${NC}"
|
||||
if ! read -r __input; then
|
||||
echo
|
||||
__input="0"
|
||||
fi
|
||||
echo
|
||||
printf -v "$__var_name" '%s' "$__input"
|
||||
}
|
||||
|
||||
# 统一菜单循环:run_menu <标题> <渲染函数> <分发函数> <范围串>
|
||||
# 返回项文案按嵌套深度自动生成:模块顶级菜单为"返回主菜单",更深层为"返回上级菜单"
|
||||
run_menu() {
|
||||
local title="$1"
|
||||
local render_fn="$2"
|
||||
local dispatch_fn="$3"
|
||||
local range="$4"
|
||||
local choice back_label
|
||||
|
||||
MENU_DEPTH=$((MENU_DEPTH + 1))
|
||||
if [[ "$MENU_DEPTH" -le 1 ]]; then
|
||||
back_label="返回主菜单"
|
||||
else
|
||||
back_label="返回上级菜单"
|
||||
fi
|
||||
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "$title"
|
||||
"$render_fn"
|
||||
echo "${UI_DIVIDER}"
|
||||
show_menu_option "0" "$back_label"
|
||||
show_menu_footer
|
||||
menu_prompt choice "$range"
|
||||
|
||||
if [[ "$choice" == "0" ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
MENU_SKIP_PAUSE=0
|
||||
if ! "$dispatch_fn" "$choice"; then
|
||||
log_error "无效选择: ${choice:-<空>}"
|
||||
log_warn "请输入 [${range}] 范围内的编号"
|
||||
fi
|
||||
if [[ "$MENU_SKIP_PAUSE" -eq 0 ]]; then
|
||||
echo
|
||||
pause_function
|
||||
fi
|
||||
done
|
||||
|
||||
MENU_DEPTH=$((MENU_DEPTH - 1))
|
||||
MENU_SKIP_PAUSE=1
|
||||
return 0
|
||||
}
|
||||
|
||||
# 统一单值输入:prompt_value <变量名> <提示语> [默认值] [校验函数]
|
||||
# 有默认值:提示为 "提示语 [默认值]: ",空回车取默认值
|
||||
# 无默认值:提示为 "提示语 (回车取消): ",空回车视为取消
|
||||
# 校验函数(可选):接收输入值,返回非 0 表示无效(原因由校验函数自行打印),将重新输入
|
||||
# 返回: 0=值已写入变量 2=用户取消或输入流关闭
|
||||
prompt_value() {
|
||||
local __var_name="$1"
|
||||
local label="$2"
|
||||
local default_value="${3:-}"
|
||||
local validator="${4:-}"
|
||||
local __input
|
||||
|
||||
while true; do
|
||||
if [[ -n "$default_value" ]]; then
|
||||
echo -ne " ${PRIMARY}${label} [${default_value}]: ${NC}"
|
||||
else
|
||||
echo -ne " ${PRIMARY}${label} (回车取消): ${NC}"
|
||||
fi
|
||||
if ! read -r __input; then
|
||||
echo
|
||||
log_info "输入已取消"
|
||||
return 2
|
||||
fi
|
||||
__input="${__input:-$default_value}"
|
||||
if [[ -z "$__input" ]]; then
|
||||
log_info "未输入内容,已取消"
|
||||
return 2
|
||||
fi
|
||||
if [[ -n "$validator" ]] && ! "$validator" "$__input"; then
|
||||
continue
|
||||
fi
|
||||
printf -v "$__var_name" '%s' "$__input"
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
# 统一是/否询问:prompt_yes_no <提示语> [默认 yes|no]
|
||||
# 返回: 0=yes 1=no(EOF/输入流关闭一律按 no 处理,保守优先)
|
||||
# 注意:这是流程中的普通选择题;涉及风险的确认请用 confirm_action / confirm_high_risk_action
|
||||
prompt_yes_no() {
|
||||
local label="$1"
|
||||
local default_answer="${2:-no}"
|
||||
local __input
|
||||
|
||||
echo -ne " ${PRIMARY}${label} (yes/no) [${default_answer}]: ${NC}"
|
||||
if ! read -r __input; then
|
||||
echo
|
||||
return 1
|
||||
fi
|
||||
__input="${__input:-$default_answer}"
|
||||
case "$__input" in
|
||||
yes|YES|y|Y) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 统一列表选择器:prompt_pick_from_list <变量名> <提示语> <数组名>
|
||||
# 打印 1..N 编号列表与 0=返回;选中项内容写入变量
|
||||
# 返回: 0=已选中 2=用户选 0 返回/输入流关闭(与既有 vm_select_* 选择器约定一致)
|
||||
prompt_pick_from_list() {
|
||||
local __var_name="$1"
|
||||
local label="$2"
|
||||
local -n __items_ref="$3"
|
||||
local __count="${#__items_ref[@]}"
|
||||
local __idx __input
|
||||
|
||||
if [[ "$__count" -eq 0 ]]; then
|
||||
log_warn "没有可选项"
|
||||
return 2
|
||||
fi
|
||||
|
||||
echo -e " ${H2}${label}${NC}"
|
||||
for __idx in "${!__items_ref[@]}"; do
|
||||
show_menu_option "$((__idx + 1))" "${__items_ref[$__idx]}"
|
||||
done
|
||||
show_menu_option "0" "返回"
|
||||
|
||||
while true; do
|
||||
echo -ne " ${PRIMARY}请选择序号 [0-${__count}]: ${NC}"
|
||||
if ! read -r __input; then
|
||||
echo
|
||||
return 2
|
||||
fi
|
||||
__input="${__input:-0}"
|
||||
if [[ "$__input" == "0" ]]; then
|
||||
return 2
|
||||
fi
|
||||
if [[ "$__input" =~ ^[0-9]+$ ]] && [[ "$__input" -ge 1 && "$__input" -le "$__count" ]]; then
|
||||
printf -v "$__var_name" '%s' "${__items_ref[$((__input - 1))]}"
|
||||
return 0
|
||||
fi
|
||||
log_warn "无效序号: ${__input},请重新输入"
|
||||
done
|
||||
}
|
||||
|
||||
# 长输出分页展示:命令 | show_report
|
||||
# 交互终端且有 less 时走分页器(上下翻页,q 退出),否则原样输出
|
||||
show_report() {
|
||||
if [[ -t 1 ]] && command -v less >/dev/null 2>&1; then
|
||||
less -R
|
||||
else
|
||||
cat
|
||||
fi
|
||||
}
|
||||
@@ -345,7 +345,7 @@ select_mirror_for_source() {
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
|
||||
read -p "请选择 $label [0-${#candidates[@]}]: " pick
|
||||
read -p "请选择操作 [0-${#candidates[@]}]: " pick
|
||||
pick="${pick:-0}"
|
||||
[[ "$pick" == "0" ]] && return 1
|
||||
if [[ ! "$pick" =~ ^[0-9]+$ ]] || (( pick < 1 || pick > ${#candidates[@]} )); then
|
||||
@@ -385,7 +385,7 @@ select_mirror_unified() {
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
|
||||
read -p "请选择统一镜像 [0-${#candidates[@]}]: " pick
|
||||
read -p "请选择操作 [0-${#candidates[@]}]: " pick
|
||||
pick="${pick:-0}"
|
||||
[[ "$pick" == "0" ]] && return 1
|
||||
if [[ ! "$pick" =~ ^[0-9]+$ ]] || (( pick < 1 || pick > ${#candidates[@]} )); then
|
||||
@@ -431,7 +431,7 @@ select_mirror() {
|
||||
show_menu_footer
|
||||
|
||||
local choice
|
||||
read -p "请选择 [1-3]: " choice
|
||||
read -p "请选择操作 [1-3]: " choice
|
||||
case "$choice" in
|
||||
1) select_mirror_unified && return 0 ;;
|
||||
2) select_mirror_per_source && return 0 ;;
|
||||
|
||||
@@ -39,22 +39,6 @@ check_debug_mode() {
|
||||
DEBUG_MODE=false
|
||||
}
|
||||
|
||||
# 检查是否安装依赖软件包
|
||||
check_packages() {
|
||||
# 程序依赖的软件包: `sudo` `curl`
|
||||
local packages=("sudo" "curl")
|
||||
for pkg in "${packages[@]}"; do
|
||||
if ! command -v "$pkg" &> /dev/null; then
|
||||
log_error "哎呀!需要安装 $pkg 软件包才能运行哦"
|
||||
echo "请使用以下命令安装:apt install -y $pkg"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# 检查 PVE 版本
|
||||
check_pve_version() {
|
||||
# 如果在调试模式下,跳过 PVE 版本检测
|
||||
@@ -151,8 +135,8 @@ show_menu() {
|
||||
show_menu_option "6" "宿主机网络与防火墙 ${CYAN}( 网桥 / VLAN / Bond / 规则 )${NC}"
|
||||
show_menu_option "7" "存储与磁盘维护 ${CYAN}( 路径 / 挂载 / 清理 / Ceph )${NC}"
|
||||
show_menu_option "8" "诊断工具与项目信息 ${CYAN}( 系统信息 / 救砖 / 脚本管理 )${NC}"
|
||||
show_menu_option "9" "安全中心 ${CYAN}( 风险检查 / SSH加固 )${NC}"
|
||||
show_menu_option "10" "第三方工具 ${CYAN}( CoolerControl / Modules / 社区脚本 )${NC}"
|
||||
show_menu_option "9" "安全中心 ${CYAN}( 风险检查 / SSH加固 / CVE修补 )${NC}"
|
||||
show_menu_option "10" "第三方工具 ${CYAN}( CoolerControl / IT87驱动 / 社区脚本 )${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "${RED}退出脚本${NC}"
|
||||
show_menu_footer
|
||||
@@ -165,6 +149,9 @@ show_menu() {
|
||||
}
|
||||
# 应急救砖工具箱菜单
|
||||
main() {
|
||||
# Ctrl+C 不再整体杀死脚本:中断当前输入后回到菜单流转,连续按可逐层返回
|
||||
trap 'echo' INT
|
||||
|
||||
check_root
|
||||
ensure_legal_acceptance
|
||||
check_debug_mode "$@"
|
||||
@@ -185,9 +172,15 @@ main() {
|
||||
while true; do
|
||||
|
||||
show_menu
|
||||
read -r choice
|
||||
if ! read -r choice; then
|
||||
# EOF/输入流关闭:优雅退出,避免无效选择的无限刷屏
|
||||
echo
|
||||
echo "输入已结束,退出脚本。感谢使用,谢谢喵"
|
||||
exit 0
|
||||
fi
|
||||
echo
|
||||
|
||||
MENU_SKIP_PAUSE=0
|
||||
case $choice in
|
||||
1)
|
||||
menu_optimization
|
||||
@@ -230,7 +223,10 @@ main() {
|
||||
;;
|
||||
esac
|
||||
|
||||
# 从子菜单正常返回时(MENU_SKIP_PAUSE=1)直接重绘主菜单,不再多按一次键
|
||||
if [[ "${MENU_SKIP_PAUSE:-0}" -eq 0 ]]; then
|
||||
echo
|
||||
pause_function
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
25686
proxmoxlib.js
25686
proxmoxlib.js
File diff suppressed because one or more lines are too long
691
release ROM
691
release ROM
@@ -1,691 +0,0 @@
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/226870174",
|
||||
"assets_url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/226870174/assets",
|
||||
"upload_url": "https://uploads.github.com/repos/lixiaoliu666/intel6-14rom/releases/226870174/assets{?name,label}",
|
||||
"html_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/tag/v2.0-20250622-100999",
|
||||
"id": 226870174,
|
||||
"author": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"node_id": "RE_kwDONZWCFM4NhcOe",
|
||||
"tag_name": "v2.0-20250622-100999",
|
||||
"target_commitish": "839cc6f71daa31a64e86d9f19ade100bc7cb1cd7",
|
||||
"name": "v2.0 build-20250622-100999",
|
||||
"draft": false,
|
||||
"immutable": false,
|
||||
"prerelease": false,
|
||||
"created_at": "2025-06-22T02:05:22Z",
|
||||
"updated_at": "2025-06-22T02:09:33Z",
|
||||
"published_at": "2025-06-22T02:09:26Z",
|
||||
"assets": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130624",
|
||||
"id": 266130624,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTA",
|
||||
"name": "11-J6412-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 143360,
|
||||
"digest": "sha256:259355031d3ad00d5100a376381bde76b3f64399a7b6df0d9f2a3539859e3036",
|
||||
"download_count": 33,
|
||||
"created_at": "2025-06-22T02:09:26Z",
|
||||
"updated_at": "2025-06-22T02:09:27Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/11-J6412-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130626",
|
||||
"id": 266130626,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTC",
|
||||
"name": "11-n5095-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 144896,
|
||||
"digest": "sha256:b16781d77b1f34b2cda032eb0aa6790776ec1519cd503ba23b7b547deb999682",
|
||||
"download_count": 51,
|
||||
"created_at": "2025-06-22T02:09:27Z",
|
||||
"updated_at": "2025-06-22T02:09:27Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/11-n5095-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130632",
|
||||
"id": 266130632,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTI",
|
||||
"name": "12-1240p-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 182784,
|
||||
"digest": "sha256:1a46e0b55bc241607a93652c3d8de48fc7ff805a00ee906b8bd8d7062449bbd3",
|
||||
"download_count": 65,
|
||||
"created_at": "2025-06-22T02:09:27Z",
|
||||
"updated_at": "2025-06-22T02:09:27Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/12-1240p-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130633",
|
||||
"id": 266130633,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTJ",
|
||||
"name": "12-n100-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 221184,
|
||||
"digest": "sha256:54a04b0e56d35b23515751893d8e94d4336eb5ba29d1011e84bf288d3f71769d",
|
||||
"download_count": 974,
|
||||
"created_at": "2025-06-22T02:09:27Z",
|
||||
"updated_at": "2025-06-22T02:09:28Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/12-n100-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130635",
|
||||
"id": 266130635,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTL",
|
||||
"name": "6-14-qemu10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 2014720,
|
||||
"digest": "sha256:2ac4874197d3d2d242e739d9587baffbdd44d238c280ef15cfc8425a3c552a8a",
|
||||
"download_count": 1893,
|
||||
"created_at": "2025-06-22T02:09:28Z",
|
||||
"updated_at": "2025-06-22T02:09:28Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/6-14-qemu10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130644",
|
||||
"id": 266130644,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTU",
|
||||
"name": "edk2-autoGenPatch.patch",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 627,
|
||||
"digest": "sha256:63e1dabd376772cea4b33900762b50ad7f0d51dd4e770da1f76b49a508ff7790",
|
||||
"download_count": 36,
|
||||
"created_at": "2025-06-22T02:09:29Z",
|
||||
"updated_at": "2025-06-22T02:09:29Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/edk2-autoGenPatch.patch"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130646",
|
||||
"id": 266130646,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTW",
|
||||
"name": "j4125-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 120320,
|
||||
"digest": "sha256:7f556ecf4b4a4d86c55a3ca0c0c4dd402ab4f0328f62eef0c190d75a479b7c4f",
|
||||
"download_count": 70,
|
||||
"created_at": "2025-06-22T02:09:29Z",
|
||||
"updated_at": "2025-06-22T02:09:29Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/j4125-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130638",
|
||||
"id": 266130638,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTO",
|
||||
"name": "N2930-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 96256,
|
||||
"digest": "sha256:87e542ee9b20fd49d620e6ce61ad455f2d57347d2fe05eea16ba0b26d4cc2ab1",
|
||||
"download_count": 25,
|
||||
"created_at": "2025-06-22T02:09:28Z",
|
||||
"updated_at": "2025-06-22T02:09:28Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/N2930-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130640",
|
||||
"id": 266130640,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTQ",
|
||||
"name": "N3350-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 109568,
|
||||
"digest": "sha256:cf7956cb369b193275bd4741279ef12e1fb41ed46620c30ee084961b49d1b784",
|
||||
"download_count": 28,
|
||||
"created_at": "2025-06-22T02:09:29Z",
|
||||
"updated_at": "2025-06-22T02:09:29Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/N3350-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130650",
|
||||
"id": 266130650,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTa",
|
||||
"name": "nb-11-11700h-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 190976,
|
||||
"digest": "sha256:de42ff52d3de24c9cb07be84bbe19f464d45b3a44a1a77040cad97653d22ca6a",
|
||||
"download_count": 23,
|
||||
"created_at": "2025-06-22T02:09:30Z",
|
||||
"updated_at": "2025-06-22T02:09:30Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/nb-11-11700h-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130651",
|
||||
"id": 266130651,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTb",
|
||||
"name": "nb-11-1185G7E-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 192000,
|
||||
"digest": "sha256:30d250c1de6cab611a59339bc4259a534e6c7d1802b72f063de09cee3374a288",
|
||||
"download_count": 21,
|
||||
"created_at": "2025-06-22T02:09:30Z",
|
||||
"updated_at": "2025-06-22T02:09:30Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/nb-11-1185G7E-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130653",
|
||||
"id": 266130653,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTd",
|
||||
"name": "nb-12-12700h-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 224768,
|
||||
"digest": "sha256:559f5412be52f6cb7dcb3fe9021c6700ee84c6881aa5421c2b39994479e548d7",
|
||||
"download_count": 41,
|
||||
"created_at": "2025-06-22T02:09:30Z",
|
||||
"updated_at": "2025-06-22T02:09:31Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/nb-12-12700h-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130655",
|
||||
"id": 266130655,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTf",
|
||||
"name": "nb-13-13700h-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 188416,
|
||||
"digest": "sha256:dcc6e1a6e0137a5a4179f7298810f6b97fd85c2b912e19d9905b0f152f0325fd",
|
||||
"download_count": 47,
|
||||
"created_at": "2025-06-22T02:09:31Z",
|
||||
"updated_at": "2025-06-22T02:09:31Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/nb-13-13700h-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130658",
|
||||
"id": 266130658,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTi",
|
||||
"name": "pc-10-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 104960,
|
||||
"digest": "sha256:852c5b7c268c56aebdfc0c55dfcb6115f7a5c51f4fa2ddfb045a383d327c81ed",
|
||||
"download_count": 39,
|
||||
"created_at": "2025-06-22T02:09:31Z",
|
||||
"updated_at": "2025-06-22T02:09:31Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/pc-10-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130666",
|
||||
"id": 266130666,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTq",
|
||||
"name": "pc-11-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 183296,
|
||||
"digest": "sha256:34e8af911f2dcdfd75b945fd16f32f1557bd5f2ee10693b5c35a6560dc3761a0",
|
||||
"download_count": 34,
|
||||
"created_at": "2025-06-22T02:09:32Z",
|
||||
"updated_at": "2025-06-22T02:09:32Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/pc-11-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130668",
|
||||
"id": 266130668,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTs",
|
||||
"name": "pc-12-13-14-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 193536,
|
||||
"digest": "sha256:b73c83330a80a18e1780e64e964f1e7b477904316f5362667c648737f40da92f",
|
||||
"download_count": 164,
|
||||
"created_at": "2025-06-22T02:09:32Z",
|
||||
"updated_at": "2025-06-22T02:09:32Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/pc-12-13-14-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130670",
|
||||
"id": 266130670,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTu",
|
||||
"name": "pc-4-5-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 77312,
|
||||
"digest": "sha256:e705307a443c2bd8fe308d445e6b66aa16b6229a266daddbf5f14454413325ee",
|
||||
"download_count": 35,
|
||||
"created_at": "2025-06-22T02:09:32Z",
|
||||
"updated_at": "2025-06-22T02:09:32Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/pc-4-5-q10.rom"
|
||||
},
|
||||
{
|
||||
"url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/releases/assets/266130672",
|
||||
"id": 266130672,
|
||||
"node_id": "RA_kwDONZWCFM4P3NTw",
|
||||
"name": "pc-6-7-8-9-q10.rom",
|
||||
"label": "",
|
||||
"uploader": {
|
||||
"login": "github-actions[bot]",
|
||||
"id": 41898282,
|
||||
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"content_type": "application/octet-stream",
|
||||
"state": "uploaded",
|
||||
"size": 101888,
|
||||
"digest": "sha256:84b15dfd1ca7ad731c041efa87a5ae5a42fb260bf3e114f2e69c60e94ef308c3",
|
||||
"download_count": 119,
|
||||
"created_at": "2025-06-22T02:09:32Z",
|
||||
"updated_at": "2025-06-22T02:09:33Z",
|
||||
"browser_download_url": "https://github.com/lixiaoliu666/intel6-14rom/releases/download/v2.0-20250622-100999/pc-6-7-8-9-q10.rom"
|
||||
}
|
||||
],
|
||||
"tarball_url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/tarball/v2.0-20250622-100999",
|
||||
"zipball_url": "https://api.github.com/repos/lixiaoliu666/intel6-14rom/zipball/v2.0-20250622-100999",
|
||||
"body": "build 6-14 all roms 只适用于qemu10及以上版本(不支持qemu7 8 9)ovmf+i440fx 10核显直通请看本项目说明,ovmf+q35 10请阅读此教程 https://www.bilibili.com/read/cv41702099"
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
| 项目 | 说明 |
|
||||
|---|---|
|
||||
| 加载方式 | `find src/modules -name '*.sh' -print0 | sort -z` 递归加载 |
|
||||
| 加载顺序 | 按文件路径名排序(`sort -z`),同一子目录内 `init.sh` 通常先加载 |
|
||||
| 加载顺序 | build.sh/dev.sh 按文件路径名排序(`sort -z`,无 init.sh 特判);PVE-Tools.sh 本地模式显式先 source 各目录 init.sh。bash 函数定义顺序不影响调用(模块文件只定义函数) |
|
||||
| 构建时 | `build.sh` 按同样顺序拼接到 `dist/PVE-Tools.sh` |
|
||||
|
||||
## 子模块概览
|
||||
@@ -26,8 +26,8 @@
|
||||
| 6 | `06-networking/` | 宿主机网络与防火墙 | 10 | **高** |
|
||||
| 7 | `07-storage-disk/` | 存储与磁盘维护 | 6 | 中等 |
|
||||
| 8 | `08-tools-about/` | 诊断工具与项目信息 | 3 | 低 |
|
||||
| 9 | `09-security/` | 安全中心 | 3 | 中等 |
|
||||
| 10 | `10-third-party/` | 第三方工具 | 4 | 低 |
|
||||
| 9 | `09-security/` | 安全中心 | 4 | 中等 |
|
||||
| 10 | `10-third-party/` | 第三方工具 | 5 | 低 |
|
||||
|
||||
---
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
| 文件 | 核心函数 | 功能 |
|
||||
|---|---|---|
|
||||
| `init.sh` | `menu_optimization()` | 二级菜单:弹窗/优化/温度/电源/邮件 |
|
||||
| `init.sh` | `menu_optimization()` | 二级菜单:弹窗/优化/温度/电源/邮件/UPS 诊断 |
|
||||
| `popup.sh` | `remove_subscription_popup()`, `restore_proxmoxlib()`, `reinstall_pve_webui_packages()` | 删除/恢复订阅弹窗 |
|
||||
| `tune.sh` | `quick_setup()` | 一键优化(换源+删弹窗+更新) |
|
||||
| `cpupower.sh` | `cpupower()`, `cpu_add()`, `cpu_del()`, `show_grub_config()`, `backup_grub_with_note()`, `list_grub_backups()`, `restore_grub_backup()` | CPU 电源模式 + GRUB 备份管理 |
|
||||
@@ -85,11 +85,11 @@
|
||||
| 文件 | 核心函数 | 功能 |
|
||||
|---|---|---|
|
||||
| `init.sh` | `menu_gpu_passthrough()`, `menu_disk_controller_passthrough()` | 主菜单 + 磁盘/控制器子菜单 |
|
||||
| `iommu.sh` | `iommu_is_enabled()`, `enable_pass()`, `disable_pass()`, `hw_passth()`, `list_storage_controllers()` | IOMMU 基础设施与硬件直通一键配置 |
|
||||
| `iommu.sh` | `iommu_is_enabled()`, `gpu_detect_active_stacks()`, `enable_pass()`, `disable_pass()`, `hw_passth()`, `list_storage_controllers()` | IOMMU 基础设施、直通方案互斥检测与一键配置 |
|
||||
| `intel-sriov.sh` | `igpu_sriov_setup()` | Intel 核显 SR-IOV 虚拟化 |
|
||||
| `intel-gvtg.sh` | `igpu_gvtg_setup()` | Intel 核显 GVT-g 虚拟化 |
|
||||
| `intel-legacy.sh` | `intel_gpu_passthrough()`, `restore_qemu_kvm()` | Intel 核显直通(修改版 QEMU) |
|
||||
| `igpu-shared.sh` | `igpu_management_menu()`, `igpu_verify()`, `igpu_remove()`, `restore_igpu_config()` | iGPU 共享管理(被 intel-sriov/gvtg 复用) |
|
||||
| `igpu-shared.sh` | `igpu_management_menu()`, `igpu_verify()`, `igpu_remove()` | iGPU 共享管理(igpu_remove 为唯一清理路径,被 intel-sriov/gvtg 复用) |
|
||||
| `nvidia.sh` | `nvidia_gpu_management_menu()`, `nvidia_gpu_passthrough_vm()`, `nvidia_driver_switch_menu()`, `nvidia_setup_vgpu_unlock()` | NVIDIA 显卡直通/驱动管理/vGPU |
|
||||
| `amd-dgpu.sh` | `amd_gpu_management_menu()`, `amd_gpu_passthrough_vm()`, `amd_host_prepare_for_passthrough()` | AMD 独显直通 |
|
||||
| `amd-igpu.sh` | `amd_igpu_management_menu()`, `amd_igpu_passthrough_vm()`, `amd_igpu_check_romfile()` | AMD 核显直通(需 ROM/vBIOS) |
|
||||
@@ -110,13 +110,14 @@
|
||||
|
||||
| 文件 | 核心函数 | 功能 |
|
||||
|---|---|---|
|
||||
| `init.sh` | `menu_vm_container()`, `vm_advanced_operations_menu()` | 主菜单 + 高级运维子菜单 |
|
||||
| `init.sh` | `menu_vm_container()`, `vm_advanced_operations_menu()` | 主菜单(快照/备份为高频直达项)+ 高级运维子菜单 |
|
||||
| `fastpve.sh` | `fastpve_quick_download_menu()` | FastPVE 快速下载 VM |
|
||||
| `schedule.sh` | `manage_vm_schedule()` | VM 定时开关机 |
|
||||
| `img-import.sh` | `img_convert_import_menu()`, `img_convert_and_import_to_vm()` | IMG 镜像导入(转 QCOW2/RAW) |
|
||||
| `storage-helper.sh` | `vm_select_storage_by_content()`, `pve_storage_content_path()`, `vm_list_storages_by_content()` | 存储辅助函数(被多文件复用) |
|
||||
| `backup.sh` | `vm_backup_restore_menu()`, `vm_backup_create()`, `vm_schedule_backup_menu()` | VM 备份与定时备份 |
|
||||
| `restore.sh` | `vm_restore_from_backup()`, `vm_config_io_menu()`, `vm_export_config()`, `vm_import_config()` | VM 恢复与配置导入导出 |
|
||||
| `backup.sh` | `vm_backup_create()`, `vm_schedule_backup_menu()` | VM 备份与定时备份 |
|
||||
| `restore.sh` | `vm_backup_restore_menu()`, `vm_restore_from_backup()` | VM 备份恢复菜单与恢复流程 |
|
||||
| `config-io.sh` | `vm_config_io_menu()`, `vm_export_config()`, `vm_import_config()` | VM 配置导入导出 |
|
||||
| `clone.sh` | `vm_clone_vm()`, `vm_convert_to_template()` | VM 克隆与模板转换 |
|
||||
| `cloudinit.sh` | `vm_template_cloudinit_menu()`, `vm_cloudinit_configure()`, `vm_cloud_image_to_template()` | Cloud-Init 配置与云镜像模板 |
|
||||
| `snapshot.sh` | `vm_snapshot_menu()`, `vm_create_snapshot()`, `vm_rollback_snapshot()` | 快照管理(创建/删除/回滚) |
|
||||
@@ -191,6 +192,7 @@
|
||||
| `init.sh` | `security_center_menu()` | 二级菜单 |
|
||||
| `audit.sh` | `security_risk_check()`, `security_list_public_listeners()` | 安全风险检查(只读报告) |
|
||||
| `ssh-hardening.sh` | `security_ssh_hardening()`, `security_install_fail2ban_if_needed()` | SSH 一键加固(端口/密钥/fail2ban) |
|
||||
| `cve-fix.sh` | `security_cve_menu()`, `security_cve_batch_mitigate()`, `security_cve_lpe_kernel_fix()` | CVE 漏洞修补中心(Januscape/DirtyFrag/CopyFail) |
|
||||
|
||||
**依赖**: 仅依赖 `lib/`。
|
||||
|
||||
@@ -206,6 +208,7 @@
|
||||
| `marketplace.sh` | `third_party_market_menu()` | 第三方软件市场(Modules 插件) |
|
||||
| `coolercontrol.sh` | `coolercontrol_manager_menu()`, `coolercontrol_install()`, `coolercontrol_uninstall()` | CoolerControl 风扇控制管理 |
|
||||
| `community.sh` | `third_party_community_scripts_info()` | 社区脚本集合信息 |
|
||||
| `it87-driver.sh` | `it87_manager_menu()`, `it87_force_id_menu()` | IT87/IT87XX 传感器驱动管理(DKMS,PGP 签名提交锁定) |
|
||||
|
||||
**依赖**: 仅依赖 `lib/`。
|
||||
|
||||
@@ -223,7 +226,7 @@
|
||||
## 测试与质量
|
||||
|
||||
- **语法检查**: `bash -n` 对每个 `src/modules/**/*.sh` 文件
|
||||
- **静态分析**: `shellcheck` 覆盖所有模块文件(CI 中强制通过)
|
||||
- **静态分析**: CI 对全部模块文件执行 `shellcheck --severity=error`(error 级强制通过)
|
||||
- **构建验证**: `bash build.sh && bash -n dist/PVE-Tools.sh` 验证拼接正确性
|
||||
- **功能测试**: 人工在 PVE 9.x 环境验证 `bash dev.sh`
|
||||
|
||||
@@ -231,7 +234,7 @@
|
||||
|
||||
**Q: 新增功能模块如何操作?**
|
||||
1. 在 `src/modules/` 下创建新目录(如 `11-new-feature/`)
|
||||
2. 编写 `init.sh`(菜单入口函数)和功能文件
|
||||
2. 编写 `init.sh`:菜单入口用 `run_menu` 框架(`<菜单名>` + `_render` + `_dispatch` 三函数,规范见根 CLAUDE.md「交互层规范」),功能实现放独立文件
|
||||
3. 在 `lib/runtime.sh` 的 `show_menu()` 和 `main()` 的 case 中添加对应选项
|
||||
4. `build.sh` 会自动扫描并包含新文件
|
||||
|
||||
@@ -239,7 +242,7 @@
|
||||
`init.sh` 仅包含菜单入口函数,功能实现放在独立文件中。这确保了单个文件不至于过大,也便于独立维护和测试各功能模块。
|
||||
|
||||
**Q: 跨文件函数调用如何处理 source 顺序?**
|
||||
不需要显式 source。所有模块文件在入口处一次性全部 source,且 `sort -z` 保证 `init.sh` 优先加载,因此同模块内的交叉引用天然可用。
|
||||
不需要显式 source。所有模块文件在入口处一次性全部加载;bash 中函数定义顺序与调用无关(模块文件只定义函数,执行入口是末尾的 `main "$@"`),因此交叉引用天然可用。不要编写"顶层立即执行且依赖其他模块函数"的代码。
|
||||
|
||||
## 相关文件清单
|
||||
|
||||
@@ -261,4 +264,5 @@ src/modules/
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-07-26 | 全部菜单迁移至 lib/menu.sh 框架;快照/备份提升为 05 直达项;UPS 诊断提升为 01 顶级项;CVE 菜单编号连续化;删除 igpu 死代码;文件数与函数索引按现实修正 |
|
||||
| 2026-07-08 | 初始化 src/modules 模块 CLAUDE.md(模块化重构新增模块) |
|
||||
|
||||
@@ -77,6 +77,10 @@ cpupower_add() {
|
||||
echo "查看当前CPU模式"
|
||||
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||
|
||||
if ! confirm_action "写入 @reboot 计划任务(开机自动应用 CPU 模式 ${GOVERNOR})"; then
|
||||
log_info "已跳过开机任务写入,本次修改仅当前开机周期生效"
|
||||
return 0
|
||||
fi
|
||||
echo "正在添加开机任务"
|
||||
NEW_CRONTAB_COMMAND="sleep 10 && echo "${GOVERNOR}" | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor >/dev/null #CPU Power Mode"
|
||||
EXISTING_CRONTAB=$(crontab -l 2>/dev/null)
|
||||
|
||||
@@ -3,31 +3,30 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
menu_optimization() {
|
||||
while true; do
|
||||
clear
|
||||
echo "功能 1/2 请在外部SSH环境下使用该功能!否则会导致PVE WebUi重启导致Shell断开连接修改失效!"
|
||||
echo "不要犟!查看如何连接到PVE SSH教程:https://pve.u3u.icu/advanced/how-to-connect-ssh.html"
|
||||
show_menu_header "系统优化"
|
||||
run_menu "系统优化" menu_optimization_render menu_optimization_dispatch "0-6"
|
||||
}
|
||||
|
||||
menu_optimization_render() {
|
||||
echo -e " ${YELLOW}功能 1/2 请在外部 SSH 环境下使用!否则 PVE WebUI 重启会导致 Shell 断连、修改失效。${NC}"
|
||||
echo -e " ${YELLOW}SSH 连接教程: https://pve.u3u.icu/advanced/how-to-connect-ssh.html${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "1" "删除订阅弹窗"
|
||||
show_menu_option "2" "${MAGENTA}一键优化 (换源+删弹窗+更新)${NC}"
|
||||
show_menu_option "3" "温度监控管理 ${CYAN}(CPU/硬盘监控设置)${NC}"
|
||||
show_menu_option "4" "CPU 电源模式配置"
|
||||
show_menu_option "5" "配置邮件通知 ${CYAN}(SMTP/Postfix)${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-6]: " choice
|
||||
case $choice in
|
||||
show_menu_option "6" "UPS 电源诊断 ${CYAN}(NUT / upsc)${NC}"
|
||||
}
|
||||
|
||||
menu_optimization_dispatch() {
|
||||
case "$1" in
|
||||
1) remove_subscription_popup ;;
|
||||
2) quick_setup ;;
|
||||
3) temp_monitoring_menu ;;
|
||||
4) cpupower ;;
|
||||
5) pve_mail_notification_setup ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
6) show_ups_diagnostics ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 二级菜单:软件源与更新
|
||||
|
||||
@@ -3,40 +3,21 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
temp_monitoring_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "温度监控管理"
|
||||
run_menu "温度监控管理" temp_monitoring_menu_render temp_monitoring_menu_dispatch "0-2"
|
||||
}
|
||||
|
||||
temp_monitoring_menu_render() {
|
||||
show_menu_option "1" "配置温度监控 ${CYAN}(CPU/硬盘温度显示)${NC}"
|
||||
show_menu_option "2" "${RED}移除温度监控${NC} (移除温度监控功能)"
|
||||
show_menu_option "3" "UPS 状态诊断 ${CYAN}(NUT / upsc)${NC}"
|
||||
echo "${UI_DIVIDER}"
|
||||
show_menu_option "0" "返回上级菜单"
|
||||
show_menu_footer
|
||||
echo
|
||||
read -p "请选择 [0-3]: " temp_choice
|
||||
echo
|
||||
}
|
||||
|
||||
case $temp_choice in
|
||||
1)
|
||||
cpu_add
|
||||
;;
|
||||
2)
|
||||
cpu_del
|
||||
;;
|
||||
3)
|
||||
show_ups_diagnostics
|
||||
;;
|
||||
0)
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "无效选择,请重新输入"
|
||||
;;
|
||||
temp_monitoring_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) cpu_add ;;
|
||||
2) cpu_del ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
|
||||
echo
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 自定义温度监控配置
|
||||
|
||||
@@ -3,25 +3,21 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
menu_sources_updates() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "软件源与更新"
|
||||
run_menu "软件源与更新" menu_sources_updates_render menu_sources_updates_dispatch "0-3"
|
||||
}
|
||||
|
||||
menu_sources_updates_render() {
|
||||
show_menu_option "1" "更换软件源"
|
||||
show_menu_option "2" "更新系统软件包"
|
||||
show_menu_option "3" "${YELLOW}PVE 8.x 升级到 PVE 9.x${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-3]: " choice
|
||||
case $choice in
|
||||
}
|
||||
|
||||
menu_sources_updates_dispatch() {
|
||||
case "$1" in
|
||||
1) change_sources ;;
|
||||
2) update_system ;;
|
||||
3) pve8_to_pve9_upgrade ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 二级菜单:启动与内核
|
||||
|
||||
@@ -29,6 +29,10 @@ change_sources() {
|
||||
log_info " Ceph: $ceph_mirror"
|
||||
log_info " CT 模板: $ct_mirror"
|
||||
|
||||
if ! confirm_action "按以上配置重写 APT 源文件(debian/ceph/pve-no-subscription 等)"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 1. 更换 Debian 软件源 (DEB822 格式)
|
||||
log_info "正在配置 Debian 镜像源..."
|
||||
backup_file "/etc/apt/sources.list.d/debian.sources"
|
||||
|
||||
@@ -3,48 +3,48 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
menu_boot_kernel() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "启动与内核"
|
||||
run_menu "启动与内核" menu_boot_kernel_render menu_boot_kernel_dispatch "0-2"
|
||||
}
|
||||
|
||||
menu_boot_kernel_render() {
|
||||
show_menu_option "1" "内核管理 ${CYAN}(内核切换/更新/清理)${NC}"
|
||||
show_menu_option "2" "查看/备份 GRUB 配置"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-2]: " choice
|
||||
case $choice in
|
||||
}
|
||||
|
||||
menu_boot_kernel_dispatch() {
|
||||
case "$1" in
|
||||
1) kernel_management_menu ;;
|
||||
2)
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "GRUB 配置管理"
|
||||
2) grub_config_menu ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
# GRUB 配置管理子菜单(查看/备份/恢复,底层函数在 01-optimization/cpupower.sh)
|
||||
grub_config_menu() {
|
||||
run_menu "GRUB 配置管理" grub_config_menu_render grub_config_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
grub_config_menu_render() {
|
||||
show_menu_option "1" "查看当前 GRUB 配置"
|
||||
show_menu_option "2" "备份 GRUB 配置"
|
||||
show_menu_option "3" "查看备份列表"
|
||||
show_menu_option "4" "恢复 GRUB 备份"
|
||||
show_menu_option "0" "返回上级菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-4]: " grub_choice
|
||||
case $grub_choice in
|
||||
1) show_grub_config; pause_function ;;
|
||||
2)
|
||||
echo "请输入备份备注:"
|
||||
read -p "> " note
|
||||
backup_grub_with_note "${note:-手动备份}"
|
||||
pause_function
|
||||
;;
|
||||
3) list_grub_backups; pause_function ;;
|
||||
4) restore_grub_backup ;;
|
||||
0) break ;;
|
||||
*) log_error "无效选择" ;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
}
|
||||
|
||||
# 二级菜单:直通与显卡
|
||||
grub_config_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) show_grub_config ;;
|
||||
2) grub_backup_with_note_prompt ;;
|
||||
3) list_grub_backups ;;
|
||||
4) restore_grub_backup ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
grub_backup_with_note_prompt() {
|
||||
local note=""
|
||||
prompt_value note "请输入备份备注" "手动备份" || return 0
|
||||
backup_grub_with_note "$note"
|
||||
}
|
||||
|
||||
@@ -236,8 +236,18 @@ set_default_kernel() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 使用 grub-set-default 设置默认内核
|
||||
if command -v grub-set-default &> /dev/null; then
|
||||
# 首选 proxmox-boot-tool kernel pin:PVE 官方机制,GRUB 与 systemd-boot(UEFI/ZFS)环境均适用
|
||||
if command -v proxmox-boot-tool >/dev/null 2>&1; then
|
||||
if proxmox-boot-tool kernel pin "$kernel_version"; then
|
||||
log_success "默认启动内核已通过 proxmox-boot-tool 固定 (pin)"
|
||||
log_tips "如需恢复自动选择最新内核,可执行: proxmox-boot-tool kernel unpin"
|
||||
return 0
|
||||
fi
|
||||
log_warn "proxmox-boot-tool kernel pin 执行失败,尝试 GRUB 备用方法"
|
||||
fi
|
||||
|
||||
# 备用:grub-set-default(仅适用于直接由 GRUB 引导且存在 grub.cfg 的环境)
|
||||
if command -v grub-set-default &> /dev/null && [[ -f /boot/grub/grub.cfg ]]; then
|
||||
# 查找内核在 GRUB 菜单中的位置
|
||||
local menu_entry=$(grep -n "$kernel_version" /boot/grub/grub.cfg | head -1 | cut -d: -f1)
|
||||
if [[ -n "$menu_entry" ]]; then
|
||||
@@ -317,51 +327,50 @@ remove_old_kernels() {
|
||||
|
||||
# 内核管理主菜单
|
||||
kernel_management_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "内核管理菜单"
|
||||
run_menu "内核管理菜单" kernel_management_menu_render kernel_management_menu_dispatch "0-6"
|
||||
}
|
||||
|
||||
kernel_management_menu_render() {
|
||||
show_menu_option "1" "显示当前内核信息"
|
||||
show_menu_option "2" "查看可用内核列表"
|
||||
show_menu_option "3" "安装新内核"
|
||||
show_menu_option "4" "设置默认启动内核"
|
||||
show_menu_option "5" "${RED}清理旧内核${NC}"
|
||||
show_menu_option "6" "${YELLOW}重启系统应用新内核${NC}"
|
||||
echo "${UI_DIVIDER}"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
read -p "请选择操作 [0-6]: " choice
|
||||
kernel_management_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) check_kernel_version ;;
|
||||
2) get_available_kernels ;;
|
||||
3) install_kernel_prompt ;;
|
||||
4) set_default_kernel_prompt ;;
|
||||
5) remove_old_kernels ;;
|
||||
6) kernel_reboot_prompt ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
check_kernel_version
|
||||
;;
|
||||
2)
|
||||
get_available_kernels
|
||||
;;
|
||||
3)
|
||||
# 交互收集内核标识后安装
|
||||
install_kernel_prompt() {
|
||||
local kernel_ver=""
|
||||
echo "请输入要安装的内核版本:"
|
||||
echo " - 完整包名格式 (推荐): 如 proxmox-kernel-6.14.8-2-pve"
|
||||
echo " - 简化版本格式: 如 6.8.8-1 (将自动补全为 proxmox-kernel-6.8.8-1-pve)"
|
||||
read -p "请输入内核标识: " kernel_ver
|
||||
if [[ -n "$kernel_ver" ]]; then
|
||||
prompt_value kernel_ver "请输入内核标识" || return 0
|
||||
install_kernel "$kernel_ver"
|
||||
else
|
||||
log_error "请输入有效的内核版本"
|
||||
fi
|
||||
;;
|
||||
4)
|
||||
read -p "请输入要设置为默认的内核版本 (例如: 6.8.8-1-pve): " kernel_ver
|
||||
if [[ -n "$kernel_ver" ]]; then
|
||||
}
|
||||
|
||||
# 交互收集内核版本后设为默认启动项
|
||||
set_default_kernel_prompt() {
|
||||
local kernel_ver=""
|
||||
prompt_value kernel_ver "请输入要设置为默认的内核版本 (例如: 6.8.8-1-pve)" || return 0
|
||||
set_default_kernel "$kernel_ver"
|
||||
else
|
||||
log_error "请输入有效的内核版本"
|
||||
fi
|
||||
;;
|
||||
5)
|
||||
remove_old_kernels
|
||||
;;
|
||||
6)
|
||||
}
|
||||
|
||||
# 高风险确认后重启宿主机
|
||||
kernel_reboot_prompt() {
|
||||
if confirm_high_risk_action \
|
||||
"重启宿主机" \
|
||||
"将立即重启当前 Proxmox VE 宿主机,所有运行中的 VM/CT 将被中断。" \
|
||||
@@ -375,18 +384,6 @@ kernel_management_menu() {
|
||||
else
|
||||
log_info "取消重启"
|
||||
fi
|
||||
;;
|
||||
0)
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "无效的选择,请重新输入"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo
|
||||
pause_function
|
||||
done
|
||||
}
|
||||
|
||||
# 内核同步更新(自动检测并更新到最新稳定版)
|
||||
|
||||
@@ -89,6 +89,8 @@ amd_host_prepare_for_passthrough() {
|
||||
fi
|
||||
echo
|
||||
|
||||
gpu_warn_active_stacks
|
||||
|
||||
if ! confirm_high_risk_action "为 AMD GPU 直通写入宿主机预配置" "会修改 GRUB、VFIO 模块和 AMD 显卡黑名单配置。" "错误配置可能导致宿主机本地输出消失、GPU 无法用于宿主机图形界面,甚至在重启后需要控制台修复。" "请确认已准备带外管理或物理控制台,并已理解回滚方式。" "AMD-HOST"; then
|
||||
return 0
|
||||
fi
|
||||
@@ -278,22 +280,19 @@ amd_gpu_passthrough_vm() {
|
||||
return 0
|
||||
}
|
||||
amd_gpu_management_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "AMD 独显直通"
|
||||
run_menu "AMD 独显直通" amd_gpu_management_menu_render amd_gpu_management_menu_dispatch "0-2"
|
||||
}
|
||||
amd_gpu_management_menu_render() {
|
||||
echo -e "${CYAN}提示:如宿主机仍在使用 amdgpu / radeon,占用中的 AMD 独显通常无法直接直通。${NC}"
|
||||
echo -e "${UI_DIVIDER}"
|
||||
show_menu_option "1" "AMD 显卡直通虚拟机"
|
||||
show_menu_option "2" "AMD 宿主机预配置 ( IOMMU / VFIO / 黑名单 )"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-2]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
amd_gpu_management_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) amd_gpu_passthrough_vm ;;
|
||||
2) amd_host_prepare_for_passthrough ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -248,28 +248,25 @@ amd_igpu_passthrough_vm() {
|
||||
return 0
|
||||
}
|
||||
amd_igpu_management_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "AMD 核显直通"
|
||||
run_menu "AMD 核显直通" amd_igpu_management_menu_render amd_igpu_management_menu_dispatch "0-4"
|
||||
}
|
||||
amd_igpu_management_menu_render() {
|
||||
echo -e "${RED}注意:AMD 核显直通通常需要用户自备 ROM / vBIOS 文件,本脚本不负责提取。${NC}"
|
||||
echo -e "${UI_DIVIDER}"
|
||||
show_menu_option "1" "配置 AMD 核显直通"
|
||||
show_menu_option "2" "检查 ROM / vBIOS 文件"
|
||||
show_menu_option "3" "查看 AMD 核显直通说明"
|
||||
show_menu_option "4" "AMD 宿主机预配置 ( IOMMU / VFIO / 黑名单 )"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
amd_igpu_management_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) amd_igpu_passthrough_vm ;;
|
||||
2) amd_igpu_check_romfile ;;
|
||||
3) amd_igpu_show_guidance ;;
|
||||
4) amd_host_prepare_for_passthrough ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 主程序
|
||||
|
||||
@@ -2,83 +2,6 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
igpu_management_menu_simple() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "Intel 核显虚拟化管理"
|
||||
show_menu_option "1" "Intel 11-15代 SR-IOV 配置 (DKMS)"
|
||||
show_menu_option "2" "Intel 6-10代 GVT-g 配置 (传统模式)"
|
||||
show_menu_option "3" "验证核显虚拟化状态"
|
||||
show_menu_option "4" "清理核显虚拟化配置 (恢复默认)"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case $choice in
|
||||
1) igpu_sriov_setup ;;
|
||||
2) igpu_gvtg_setup ;;
|
||||
3) igpu_verify ;;
|
||||
4) restore_igpu_config ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
}
|
||||
|
||||
# Intel 11-15代 SR-IOV 核显虚拟化配置
|
||||
restore_igpu_config() {
|
||||
log_step "开始清理核显虚拟化配置 (恢复默认)"
|
||||
echo -e " 此操作将执行以下步骤:"
|
||||
echo -e " 1. 移除 ${CYAN}GRUB${NC} 中的核显相关参数"
|
||||
echo -e " 2. 从 ${CYAN}/etc/modules${NC} 移除核显相关模块"
|
||||
echo -e " 3. 更新 ${CYAN}GRUB${NC} 和 ${CYAN}initramfs${NC}"
|
||||
echo -e " 适用于因配置核显虚拟化导致系统异常或想要重置配置的情况。"
|
||||
echo
|
||||
|
||||
if ! confirm_action "是否继续执行清理操作?"; then
|
||||
return
|
||||
fi
|
||||
|
||||
# 1. 恢复 GRUB 配置
|
||||
log_info "正在清理 GRUB 参数..."
|
||||
if [[ -f "/etc/default/grub" ]]; then
|
||||
grub_remove_param "intel_iommu"
|
||||
grub_remove_param "iommu"
|
||||
grub_remove_param "i915.enable_gvt"
|
||||
grub_remove_param "i915.enable_guc"
|
||||
grub_remove_param "i915.max_vfs"
|
||||
|
||||
log_success "GRUB 参数清理完成"
|
||||
else
|
||||
log_error "未找到 /etc/default/grub 文件"
|
||||
fi
|
||||
|
||||
# 2. 恢复 /etc/modules
|
||||
log_info "正在清理 /etc/modules..."
|
||||
if [[ -f "/etc/modules" ]]; then
|
||||
backup_file "/etc/modules"
|
||||
sed -i '/vfio/d' /etc/modules
|
||||
sed -i '/vfio_iommu_type1/d' /etc/modules
|
||||
sed -i '/vfio_pci/d' /etc/modules
|
||||
sed -i '/vfio_virqfd/d' /etc/modules
|
||||
sed -i '/kvmgt/d' /etc/modules
|
||||
log_success "/etc/modules 清理完成"
|
||||
fi
|
||||
|
||||
# 3. 更新系统配置
|
||||
log_info "正在更新 GRUB..."
|
||||
update-grub
|
||||
|
||||
log_info "正在更新 initramfs..."
|
||||
update-initramfs -u -k all
|
||||
|
||||
log_success "清理完成!核显虚拟化配置已重置。"
|
||||
if confirm_action "是否现在重启系统?"; then
|
||||
reboot
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证核显虚拟化状态
|
||||
igpu_verify() {
|
||||
echo
|
||||
@@ -176,8 +99,6 @@ igpu_verify() {
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " 检查完成"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
pause_function
|
||||
}
|
||||
|
||||
# 移除核显虚拟化配置
|
||||
@@ -191,6 +112,7 @@ igpu_remove() {
|
||||
echo " • 恢复 GRUB 配置为默认值"
|
||||
echo " • 清理 /etc/modules 中的 VFIO 和 kvmgt 模块"
|
||||
echo " • 删除 /etc/sysfs.conf 中的 VFs 配置"
|
||||
echo " • 清理本工具写入的 i915 相关驱动黑名单(如有)"
|
||||
echo " • 卸载 i915-sriov-dkms 驱动(如已安装)"
|
||||
echo
|
||||
echo -e " 注意:此操作不会自动重启系统"
|
||||
@@ -216,10 +138,12 @@ igpu_remove() {
|
||||
update-grub
|
||||
echo -e " ✓ GRUB 配置已恢复"
|
||||
|
||||
# 清理 /etc/modules
|
||||
# 清理 /etc/modules(先移除 marker 配置块,再清理旧版本裸行)
|
||||
echo "清理内核模块配置..."
|
||||
backup_file "/etc/modules"
|
||||
|
||||
remove_block "/etc/modules" "INTEL_SRIOV_MODULES"
|
||||
remove_block "/etc/modules" "INTEL_GVTG_MODULES"
|
||||
sed -i '/^vfio$/d; /^vfio_iommu_type1$/d; /^vfio_pci$/d; /^vfio_virqfd$/d; /^kvmgt$/d' /etc/modules
|
||||
echo -e " ✓ 内核模块配置已清理"
|
||||
|
||||
@@ -231,6 +155,14 @@ igpu_remove() {
|
||||
echo -e " ✓ sysfs 配置已清理"
|
||||
fi
|
||||
|
||||
# 清理本工具写入的 i915 相关驱动黑名单(如有),避免恢复后核显仍被屏蔽
|
||||
for f in /etc/modprobe.d/blacklist.conf /etc/modprobe.d/pve-blacklist.conf; do
|
||||
if [ -f "$f" ]; then
|
||||
remove_block "$f" "HARDWARE_PASSTHROUGH"
|
||||
remove_block "$f" "INTEL_LEGACY_BLACKLIST"
|
||||
fi
|
||||
done
|
||||
|
||||
# 卸载 i915-sriov-dkms
|
||||
echo "检查 i915-sriov-dkms 驱动..."
|
||||
if dpkg -l | grep -q i915-sriov-dkms; then
|
||||
@@ -259,11 +191,19 @@ igpu_remove() {
|
||||
fi
|
||||
}
|
||||
|
||||
# GRUB 备份(输入备注后调用 backup_grub_with_note)
|
||||
igpu_grub_backup_prompt() {
|
||||
local backup_note=""
|
||||
prompt_value backup_note "请输入备份备注(例如:手动备份_测试)" "手动备份" || return 0
|
||||
backup_grub_with_note "$backup_note"
|
||||
}
|
||||
|
||||
# 核显高级功能菜单
|
||||
igpu_management_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "核显虚拟化高级功能"
|
||||
run_menu "核显虚拟化高级功能" igpu_management_menu_render igpu_management_menu_dispatch "0-8"
|
||||
}
|
||||
|
||||
igpu_management_menu_render() {
|
||||
echo -e " ${RED}【危险警告】${NC} 核显虚拟化属于高危操作"
|
||||
echo -e " 配置错误可能导致系统无法启动,请务必提前备份 GRUB 配置"
|
||||
echo "${UI_DIVIDER}"
|
||||
@@ -286,54 +226,21 @@ igpu_management_menu() {
|
||||
echo -e " ${CYAN}路径:${NC} /etc/pvetools9/backup/grub/"
|
||||
show_menu_option "7" "查看 GRUB 备份列表"
|
||||
show_menu_option "8" "恢复 GRUB 配置"
|
||||
echo "${UI_DIVIDER}"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
echo
|
||||
read -p "请选择操作 [0-8]: " choice
|
||||
}
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
igpu_sriov_setup
|
||||
;;
|
||||
2)
|
||||
igpu_gvtg_setup
|
||||
;;
|
||||
3)
|
||||
igpu_verify
|
||||
;;
|
||||
4)
|
||||
igpu_remove
|
||||
;;
|
||||
5)
|
||||
show_grub_config
|
||||
pause_function
|
||||
;;
|
||||
6)
|
||||
echo
|
||||
echo "请输入备份备注(例如:手动备份_测试):"
|
||||
read -p "> " backup_note
|
||||
backup_note=${backup_note:-"手动备份"}
|
||||
backup_grub_with_note "$backup_note"
|
||||
pause_function
|
||||
;;
|
||||
7)
|
||||
list_grub_backups
|
||||
pause_function
|
||||
;;
|
||||
8)
|
||||
restore_grub_backup
|
||||
;;
|
||||
0)
|
||||
echo "返回主菜单"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo -e "无效的选择,请输入 0-8"
|
||||
pause_function
|
||||
;;
|
||||
igpu_management_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) igpu_sriov_setup ;;
|
||||
2) igpu_gvtg_setup ;;
|
||||
3) igpu_verify ;;
|
||||
4) igpu_remove ;;
|
||||
5) show_grub_config ;;
|
||||
6) igpu_grub_backup_prompt ;;
|
||||
7) list_grub_backups ;;
|
||||
8) restore_grub_backup ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
done
|
||||
return 0
|
||||
}
|
||||
#--------------核显虚拟化管理----------------
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
menu_gpu_passthrough() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "直通与显卡"
|
||||
run_menu "直通与显卡" menu_gpu_passthrough_render menu_gpu_passthrough_dispatch "0-7"
|
||||
}
|
||||
|
||||
menu_gpu_passthrough_render() {
|
||||
show_menu_option "1" "Intel 核显虚拟化管理 (SR-IOV/GVT-g)"
|
||||
show_menu_option "2" "Intel 核显直通配置 (修改版 QEMU)"
|
||||
show_menu_option "3" "NVIDIA 显卡直通/虚拟化"
|
||||
@@ -13,10 +14,10 @@ menu_gpu_passthrough() {
|
||||
show_menu_option "5" "AMD 核显直通 (需自备 ROM / vBIOS)"
|
||||
show_menu_option "6" "硬件直通一键配置 (IOMMU)"
|
||||
show_menu_option "7" "磁盘/控制器直通 (RDM/PCIe/NVMe)"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-7]: " choice
|
||||
case $choice in
|
||||
}
|
||||
|
||||
menu_gpu_passthrough_dispatch() {
|
||||
case "$1" in
|
||||
1) igpu_management_menu ;;
|
||||
2) intel_gpu_passthrough ;;
|
||||
3) nvidia_gpu_management_menu ;;
|
||||
@@ -24,39 +25,31 @@ menu_gpu_passthrough() {
|
||||
5) amd_igpu_management_menu ;;
|
||||
6) hw_passth ;;
|
||||
7) menu_disk_controller_passthrough ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 虚拟机/容器定时开关机管理
|
||||
menu_disk_controller_passthrough() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "磁盘/控制器直通"
|
||||
run_menu "磁盘/控制器直通" menu_disk_controller_passthrough_render menu_disk_controller_passthrough_dispatch "0-5"
|
||||
}
|
||||
|
||||
menu_disk_controller_passthrough_render() {
|
||||
show_menu_option "1" "RDM(裸磁盘映射)- 单个磁盘直通"
|
||||
show_menu_option "2" "RDM 取消直通(--delete)"
|
||||
show_menu_option "3" "磁盘控制器直通(PCIe)"
|
||||
show_menu_option "4" "NVMe 直通(含 MSI-X 重定位)"
|
||||
show_menu_option "5" "引导配置辅助(UEFI/Legacy)"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-6]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
menu_disk_controller_passthrough_dispatch() {
|
||||
case "$1" in
|
||||
1) rdm_single_disk_attach ;;
|
||||
2) rdm_single_disk_detach ;;
|
||||
3) storage_controller_passthrough ;;
|
||||
4) nvme_passthrough ;;
|
||||
5) boot_config_assistant ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============ RDM(裸磁盘映射)单盘直通 ============
|
||||
|
||||
# 获取 VM 配置文件路径(不保证一定存在,需调用方自行判断)
|
||||
|
||||
@@ -53,6 +53,8 @@ igpu_gvtg_setup() {
|
||||
echo -e " ${CYAN}提示 3:${NC} 准备好通过 SSH 或物理访问恢复系统"
|
||||
echo
|
||||
|
||||
gpu_warn_active_stacks
|
||||
|
||||
# 询问是否要备份
|
||||
if confirm_action "是否先备份当前 GRUB 配置(强烈推荐)"; then
|
||||
echo
|
||||
@@ -104,19 +106,22 @@ igpu_gvtg_setup() {
|
||||
echo "清理可能存在的 i915 及音视频相关黑名单..."
|
||||
for f in /etc/modprobe.d/blacklist.conf /etc/modprobe.d/pve-blacklist.conf; do
|
||||
if [ -f "$f" ]; then
|
||||
remove_block "$f" "HARDWARE_PASSTHROUGH"
|
||||
remove_block "$f" "INTEL_LEGACY_BLACKLIST"
|
||||
sed -i '/blacklist i915/d' "$f"
|
||||
sed -i '/blacklist snd_hda_intel/d' "$f"
|
||||
sed -i '/blacklist snd_hda_codec_hdmi/d' "$f"
|
||||
fi
|
||||
done
|
||||
|
||||
# 添加 VFIO 和 kvmgt 模块
|
||||
for module in vfio vfio_iommu_type1 vfio_pci vfio_virqfd kvmgt; do
|
||||
if ! grep -q "^$module$" /etc/modules; then
|
||||
echo "$module" >> /etc/modules
|
||||
echo "已添加模块: $module"
|
||||
fi
|
||||
done
|
||||
# 添加 VFIO 和 kvmgt 模块(marker 配置块写入,自动备份并幂等;同时清掉 SR-IOV 的模块块与旧版裸行)
|
||||
remove_block "/etc/modules" "INTEL_SRIOV_MODULES"
|
||||
sed -i -E '/^(vfio|vfio_iommu_type1|vfio_pci|vfio_virqfd|kvmgt)[[:space:]]*$/d' /etc/modules
|
||||
apply_block "/etc/modules" "INTEL_GVTG_MODULES" "vfio
|
||||
vfio_iommu_type1
|
||||
vfio_pci
|
||||
vfio_virqfd
|
||||
kvmgt"
|
||||
|
||||
echo -e "✓ 内核模块配置完成"
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ restore_qemu_kvm() {
|
||||
# 3. 清理黑名单 (可选)
|
||||
if confirm_action "是否同时清理 Intel 核显相关的驱动黑名单?"; then
|
||||
log_info "正在清理黑名单配置..."
|
||||
remove_block "/etc/modprobe.d/pve-blacklist.conf" "INTEL_LEGACY_BLACKLIST"
|
||||
sed -i '/blacklist i915/d' /etc/modprobe.d/pve-blacklist.conf
|
||||
sed -i '/blacklist snd_hda_intel/d' /etc/modprobe.d/pve-blacklist.conf
|
||||
sed -i '/blacklist snd_hda_codec_hdmi/d' /etc/modprobe.d/pve-blacklist.conf
|
||||
@@ -93,12 +94,23 @@ intel_gpu_passthrough() {
|
||||
;;
|
||||
esac
|
||||
|
||||
# 1. 配置黑名单
|
||||
gpu_warn_active_stacks
|
||||
|
||||
if ! confirm_high_risk_action \
|
||||
"安装第三方修改版 QEMU 并配置核显直通" \
|
||||
"将屏蔽宿主机 i915 驱动(本地控制台会黑屏),并安装来自 AICodo 仓库的非官方 pve-qemu-kvm" \
|
||||
"第三方 deb 包无官方签名校验,且会被 apt-mark hold 锁定版本、无法收到官方安全更新" \
|
||||
"请确认已有可用的 SSH/Web 管理通道;出问题可用本菜单的救砖模式恢复官方 QEMU" \
|
||||
"QEMU-MOD"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 1. 配置黑名单(marker 配置块写入,apply_block 自动备份并幂等)
|
||||
log_step "配置驱动黑名单 (屏蔽宿主机占用核显)"
|
||||
if ! grep -q "blacklist i915" /etc/modprobe.d/pve-blacklist.conf; then
|
||||
echo "blacklist i915" >> /etc/modprobe.d/pve-blacklist.conf
|
||||
echo "blacklist snd_hda_intel" >> /etc/modprobe.d/pve-blacklist.conf
|
||||
echo "blacklist snd_hda_codec_hdmi" >> /etc/modprobe.d/pve-blacklist.conf
|
||||
apply_block "/etc/modprobe.d/pve-blacklist.conf" "INTEL_LEGACY_BLACKLIST" "blacklist i915
|
||||
blacklist snd_hda_intel
|
||||
blacklist snd_hda_codec_hdmi"
|
||||
log_success "已添加黑名单配置"
|
||||
|
||||
log_info "正在更新 initramfs..."
|
||||
@@ -118,7 +130,8 @@ intel_gpu_passthrough() {
|
||||
# 为方便起见,这里演示自动获取逻辑
|
||||
|
||||
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)
|
||||
local qemu_deb_url
|
||||
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 下载链接,尝试使用备用链接或手动下载"
|
||||
@@ -128,12 +141,29 @@ intel_gpu_passthrough() {
|
||||
else
|
||||
# 加速下载
|
||||
local fast_qemu_url="https://ghfast.top/${qemu_deb_url}"
|
||||
local qemu_deb_file
|
||||
local qemu_deb_file qemu_deb_sha256
|
||||
qemu_deb_file="$(mktemp --suffix=.deb)"
|
||||
log_info "正在下载: $fast_qemu_url"
|
||||
wget -O "$qemu_deb_file" "$fast_qemu_url"
|
||||
if ! wget -O "$qemu_deb_file" "$fast_qemu_url"; then
|
||||
log_warn "加速源下载失败,改用 GitHub 原始地址重试..."
|
||||
if ! wget -O "$qemu_deb_file" "$qemu_deb_url"; then
|
||||
rm -f "$qemu_deb_file"
|
||||
log_error "下载失败,已中止配置(未修改 QEMU)"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -s "$qemu_deb_file" ]]; then
|
||||
rm -f "$qemu_deb_file"
|
||||
log_error "下载结果为空,已中止配置(未修改 QEMU)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 上游无官方签名可验,至少展示摘要与来源,供用户与 release 页核对及事后追溯
|
||||
qemu_deb_sha256="$(sha256sum "$qemu_deb_file" | awk '{print $1}')"
|
||||
log_info "deb 包 SHA256: ${qemu_deb_sha256}"
|
||||
log_info "下载来源: ${qemu_deb_url}"
|
||||
|
||||
if [[ -s "$qemu_deb_file" ]]; then
|
||||
log_info "正在安装修改版 QEMU..."
|
||||
if dpkg -i "$qemu_deb_file"; then
|
||||
rm -f "$qemu_deb_file"
|
||||
@@ -141,16 +171,12 @@ intel_gpu_passthrough() {
|
||||
|
||||
# 阻止更新
|
||||
apt-mark hold pve-qemu-kvm
|
||||
log_info "已锁定 pve-qemu-kvm 防止自动更新"
|
||||
log_info "已锁定 pve-qemu-kvm 防止自动更新(救砖模式可解除)"
|
||||
else
|
||||
rm -f "$qemu_deb_file"
|
||||
log_error "安装修改版 QEMU 失败,请检查 deb 包完整性"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
rm -f "$qemu_deb_file"
|
||||
log_error "下载失败"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. 下载 ROM 文件
|
||||
@@ -307,25 +333,29 @@ intel_gpu_passthrough() {
|
||||
args_line="$args_line -set device.hostpci1.bus=pcie.0 -set device.hostpci1.addr=0x03.0"
|
||||
fi
|
||||
|
||||
# 写入 args (保留用户已有的 args 参数)
|
||||
# 通过 qm set 写入:直接追加/就地 sed 会绕过 pmxcfs 加锁,
|
||||
# 且在带快照的 conf 上会把配置行写进快照段导致配置损坏
|
||||
if grep -q '^args:' "/etc/pve/qemu-server/$vmid.conf"; then
|
||||
log_warn "检测到 VM $vmid 已有 args 配置,将被新 args 覆盖"
|
||||
log_warn "原 args 内容: $(grep '^args:' "/etc/pve/qemu-server/$vmid.conf")"
|
||||
sed -i '/^args:/d' "/etc/pve/qemu-server/$vmid.conf"
|
||||
fi
|
||||
echo "args: $args_line" >> "/etc/pve/qemu-server/$vmid.conf"
|
||||
if ! qm set "$vmid" --args "$args_line"; then
|
||||
log_error "写入 args 失败,虚拟机配置未完成"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 写入 hostpci0 (核显)
|
||||
# 先删除旧的 hostpci0
|
||||
sed -i '/^hostpci0:/d' "/etc/pve/qemu-server/$vmid.conf"
|
||||
# 格式: hostpci0: 0000:00:02.0,romfile=xxx.rom
|
||||
# 注意:这里 PCI ID 使用 lspci 获取到的真实 ID,通常是 0000:00:02.0
|
||||
echo "hostpci0: $igpu_pci,romfile=$rom_filename" >> "/etc/pve/qemu-server/$vmid.conf"
|
||||
# 写入 hostpci0 (核显),格式: 0000:00:02.0,romfile=xxx.rom
|
||||
if ! qm set "$vmid" --hostpci0 "$igpu_pci,romfile=$rom_filename"; then
|
||||
log_error "写入 hostpci0 失败,虚拟机配置未完成"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 写入 hostpci1 (声卡)
|
||||
if [ -n "$audio_pci" ]; then
|
||||
sed -i '/^hostpci1:/d' "/etc/pve/qemu-server/$vmid.conf"
|
||||
echo "hostpci1: $audio_pci" >> "/etc/pve/qemu-server/$vmid.conf"
|
||||
if ! qm set "$vmid" --hostpci1 "$audio_pci"; then
|
||||
log_error "写入 hostpci1 失败,请手动检查虚拟机配置"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_success "虚拟机 $vmid 配置完成"
|
||||
|
||||
@@ -59,6 +59,8 @@ igpu_sriov_setup() {
|
||||
echo -e " ${CYAN}提示 3:${NC} 准备好通过 SSH 或物理访问恢复系统"
|
||||
echo
|
||||
|
||||
gpu_warn_active_stacks
|
||||
|
||||
# 询问是否要备份
|
||||
if confirm_action "是否先备份当前 GRUB 配置(强烈推荐)"; then
|
||||
echo
|
||||
@@ -132,22 +134,21 @@ igpu_sriov_setup() {
|
||||
echo "清理可能存在的 i915 及音视频相关黑名单..."
|
||||
for f in /etc/modprobe.d/blacklist.conf /etc/modprobe.d/pve-blacklist.conf; do
|
||||
if [ -f "$f" ]; then
|
||||
remove_block "$f" "HARDWARE_PASSTHROUGH"
|
||||
remove_block "$f" "INTEL_LEGACY_BLACKLIST"
|
||||
sed -i '/blacklist i915/d' "$f"
|
||||
sed -i '/blacklist snd_hda_intel/d' "$f"
|
||||
sed -i '/blacklist snd_hda_codec_hdmi/d' "$f"
|
||||
fi
|
||||
done
|
||||
|
||||
# 添加 VFIO 模块(如果未添加)
|
||||
for module in vfio vfio_iommu_type1 vfio_pci vfio_virqfd; do
|
||||
if ! grep -q "^$module$" /etc/modules; then
|
||||
echo "$module" >> /etc/modules
|
||||
echo "已添加模块: $module"
|
||||
fi
|
||||
done
|
||||
|
||||
# 移除 kvmgt 模块(如果有 GVT-g 配置)
|
||||
sed -i '/^kvmgt$/d' /etc/modules
|
||||
# 添加 VFIO 模块(marker 配置块写入,自动备份并幂等;同时清掉 GVT-g 的模块块、kvmgt 与旧版裸行)
|
||||
remove_block "/etc/modules" "INTEL_GVTG_MODULES"
|
||||
sed -i -E '/^(vfio|vfio_iommu_type1|vfio_pci|vfio_virqfd|kvmgt)[[:space:]]*$/d' /etc/modules
|
||||
apply_block "/etc/modules" "INTEL_SRIOV_MODULES" "vfio
|
||||
vfio_iommu_type1
|
||||
vfio_pci
|
||||
vfio_virqfd"
|
||||
|
||||
echo -e "✓ 内核模块配置完成"
|
||||
|
||||
|
||||
@@ -18,115 +18,178 @@ iommu_is_enabled() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# 从 udev 路径中解析 PCI BDF(格式:0000:00:00.0)
|
||||
# 检测本机已存在的直通/虚拟化配置来源(只读),每行输出一个已配置方案名
|
||||
gpu_detect_active_stacks() {
|
||||
local grub_cmdline=""
|
||||
if [[ -f /etc/default/grub ]]; then
|
||||
grub_cmdline="$(grep '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
grep -qs "PVE-TOOLS BEGIN IOMMU_BASE_MODULES" /etc/modules && echo "一键硬件直通基础配置 (IOMMU)"
|
||||
grep -qs "PVE-TOOLS BEGIN NVIDIA_VFIO_MODULES" /etc/modules && echo "NVIDIA 宿主机直通预配置"
|
||||
grep -qs "PVE-TOOLS BEGIN AMD_VFIO_MODULES" /etc/modules && echo "AMD 宿主机直通预配置"
|
||||
grep -qs "PVE-TOOLS BEGIN INTEL_SRIOV_MODULES" /etc/modules && echo "Intel 核显 SR-IOV"
|
||||
grep -qs "PVE-TOOLS BEGIN INTEL_GVTG_MODULES" /etc/modules && echo "Intel 核显 GVT-g"
|
||||
grep -qs "PVE-TOOLS BEGIN INTEL_LEGACY_BLACKLIST" /etc/modprobe.d/pve-blacklist.conf && echo "Intel 核显直通黑名单 (修改版 QEMU 方案)"
|
||||
grep -qs "PVE-TOOLS BEGIN HARDWARE_PASSTHROUGH" /etc/modprobe.d/blacklist.conf && echo "一键直通可选驱动屏蔽 (i915/snd_hda)"
|
||||
|
||||
# 兼容旧版本裸写入(无 marker):按 GRUB 特征参数兜底识别
|
||||
if [[ "$grub_cmdline" == *"i915.enable_gvt"* ]] && ! grep -qs "PVE-TOOLS BEGIN INTEL_GVTG_MODULES" /etc/modules; then
|
||||
echo "Intel 核显 GVT-g (旧版本写入的 GRUB 参数)"
|
||||
fi
|
||||
if [[ "$grub_cmdline" == *"i915.max_vfs"* ]] && ! grep -qs "PVE-TOOLS BEGIN INTEL_SRIOV_MODULES" /etc/modules; then
|
||||
echo "Intel 核显 SR-IOV (旧版本写入的 GRUB 参数)"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# 在进入任一直通方案配置前提示已存在的其他方案,避免多方案叠加冲突
|
||||
gpu_warn_active_stacks() {
|
||||
local found
|
||||
found="$(gpu_detect_active_stacks)"
|
||||
if [[ -n "$found" ]]; then
|
||||
echo
|
||||
log_warn "检测到本机已存在以下直通/虚拟化配置:"
|
||||
while IFS= read -r line; do
|
||||
echo -e " ${YELLOW}- ${line}${NC}"
|
||||
done <<< "$found"
|
||||
log_warn "多套方案同时修改 GRUB/内核模块可能互相冲突;如需切换方案,建议先用对应菜单清理旧配置。"
|
||||
echo
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
enable_pass() {
|
||||
local grub_changed blacklist_content
|
||||
echo
|
||||
log_step "开启硬件直通..."
|
||||
if [ `dmesg | grep -e DMAR -e IOMMU|wc -l` = 0 ];then
|
||||
if ! dmesg 2>/dev/null | grep -q -e DMAR -e IOMMU; then
|
||||
log_error "您的硬件不支持直通!不如检查一下主板的BIOS设置?"
|
||||
pause_function
|
||||
return
|
||||
fi
|
||||
if [ `cat /proc/cpuinfo|grep Intel|wc -l` = 0 ];then
|
||||
iommu="amd_iommu=on"
|
||||
else
|
||||
if grep -q Intel /proc/cpuinfo; then
|
||||
iommu="intel_iommu=on"
|
||||
else
|
||||
iommu="amd_iommu=on"
|
||||
fi
|
||||
if ! grep -qw "$(echo "$iommu" | cut -d'=' -f1)" /etc/default/grub; then
|
||||
|
||||
gpu_warn_active_stacks
|
||||
|
||||
if ! confirm_high_risk_action \
|
||||
"开启 IOMMU 硬件直通基础配置" \
|
||||
"会修改 GRUB 内核参数并向 /etc/modules 写入 vfio 模块,需重启生效" \
|
||||
"配置错误可能导致宿主机启动异常;与已有显卡直通方案叠加时请确认参数不冲突" \
|
||||
"脚本会自动备份 /etc/default/grub 与 /etc/modules,可通过 GRUB 备份恢复功能回滚" \
|
||||
"IOMMU-ON"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
grub_changed=0
|
||||
if ! grub_has_param "$iommu"; then
|
||||
if grub_add_param "$iommu"; then
|
||||
update-grub
|
||||
grub_changed=1
|
||||
else
|
||||
log_error "GRUB 参数添加失败,无法继续配置硬件直通"
|
||||
return 1
|
||||
fi
|
||||
if [ `grep "vfio" /etc/modules|wc -l` = 0 ];then
|
||||
cat <<-EOF >> /etc/modules
|
||||
vfio
|
||||
else
|
||||
log_info "GRUB 中已存在 IOMMU 参数,跳过内核参数修改。"
|
||||
fi
|
||||
|
||||
# GRUB 已配置过时也要补齐 vfio 模块,避免"以为配了其实没配"
|
||||
# marker 配置块写入(apply_block 内部自动备份并保证幂等)
|
||||
if ! grep -q "vfio" /etc/modules 2>/dev/null; then
|
||||
apply_block "/etc/modules" "IOMMU_BASE_MODULES" "vfio
|
||||
vfio_iommu_type1
|
||||
vfio_pci
|
||||
vfio_virqfd
|
||||
kvmgt
|
||||
EOF
|
||||
kvmgt"
|
||||
log_success "已写入 vfio 内核模块到 /etc/modules"
|
||||
else
|
||||
log_info "/etc/modules 中已存在 vfio 模块,跳过。"
|
||||
fi
|
||||
|
||||
# 使用安全的配置块管理
|
||||
# 屏蔽核显/声卡驱动仅适用于"把核显直通给虚拟机"的场景;
|
||||
# 宿主机还需要本地显示输出时开启会导致控制台黑屏,因此默认不开启。
|
||||
# 设备级 vfio-pci ids 绑定由各显卡专用直通菜单按实际硬件写入,此处不再写死。
|
||||
echo -e "${YELLOW}是否同时屏蔽宿主机核显与核显音频驱动 (i915/snd_hda_*)?${NC}"
|
||||
echo -e "${RED}仅在准备把核显直通给虚拟机时才需要;开启后宿主机本地屏幕将黑屏,只能通过 SSH/Web 管理。${NC}"
|
||||
if confirm_action "屏蔽 i915 与核显音频驱动(一般不需要)"; then
|
||||
blacklist_content="blacklist snd_hda_intel
|
||||
blacklist snd_hda_codec_hdmi
|
||||
blacklist i915"
|
||||
apply_block "/etc/modprobe.d/blacklist.conf" "HARDWARE_PASSTHROUGH" "$blacklist_content"
|
||||
else
|
||||
log_info "已跳过驱动屏蔽。如需针对具体显卡配置,请使用对应显卡的直通菜单。"
|
||||
fi
|
||||
|
||||
# 使用安全的配置块管理
|
||||
vfio_content="options vfio-pci ids=8086:3185"
|
||||
apply_block "/etc/modprobe.d/vfio.conf" "HARDWARE_PASSTHROUGH" "$vfio_content"
|
||||
|
||||
if [[ "$grub_changed" -eq 1 ]]; then
|
||||
update-grub
|
||||
fi
|
||||
log_success "开启设置后需要重启系统,请准备就绪后重启宿主机"
|
||||
log_tips "重启后才可以应用对内核引导的修改哦!命令是 reboot"
|
||||
else
|
||||
log_warn "您已经配置过!"
|
||||
fi
|
||||
}
|
||||
|
||||
# 关闭硬件直通
|
||||
disable_pass() {
|
||||
echo
|
||||
log_step "关闭硬件直通..."
|
||||
if [ `dmesg | grep -e DMAR -e IOMMU|wc -l` = 0 ];then
|
||||
if ! dmesg 2>/dev/null | grep -q -e DMAR -e IOMMU; then
|
||||
log_error "您的硬件不支持直通!"
|
||||
log_tips "不如检查一下主板的BIOS设置?"
|
||||
pause_function
|
||||
return
|
||||
fi
|
||||
if [ `cat /proc/cpuinfo|grep Intel|wc -l` = 0 ];then
|
||||
iommu="amd_iommu=on"
|
||||
else
|
||||
if grep -q Intel /proc/cpuinfo; then
|
||||
iommu="intel_iommu=on"
|
||||
fi
|
||||
if [ `grep $iommu /etc/default/grub|wc -l` = 0 ];then
|
||||
log_warn "您还没有配置过该项"
|
||||
else
|
||||
iommu="amd_iommu=on"
|
||||
fi
|
||||
if ! grub_has_param "$iommu"; then
|
||||
log_warn "您还没有配置过该项"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! confirm_high_risk_action \
|
||||
"关闭 IOMMU 硬件直通基础配置" \
|
||||
"会移除 GRUB 中的 IOMMU 参数并删除 /etc/modules 中所有 vfio 行" \
|
||||
"依赖 IOMMU 的其他直通方案 (核显 SR-IOV / NVIDIA / AMD / 磁盘控制器直通) 将在重启后全部失效" \
|
||||
"如仍有虚拟机挂载直通设备,请先在对应菜单解除直通再关闭本配置" \
|
||||
"IOMMU-OFF"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
grub_remove_param "$iommu"
|
||||
backup_file "/etc/modules"
|
||||
sed -i '/vfio/d' /etc/modules
|
||||
# 使用安全的配置块删除,而不是直接删除整个文件
|
||||
# 先移除本工具的 marker 配置块,再精确清理历史版本写入的裸模块行(含 kvmgt,不误删其它行)
|
||||
remove_block "/etc/modules" "IOMMU_BASE_MODULES"
|
||||
sed -i -E '/^(vfio|vfio_iommu_type1|vfio_pci|vfio_virqfd|kvmgt)[[:space:]]*$/d' /etc/modules
|
||||
# 使用安全的配置块删除,而不是直接删除整个文件(vfio.conf 为历史版本可能写入的位置)
|
||||
remove_block "/etc/modprobe.d/blacklist.conf" "HARDWARE_PASSTHROUGH"
|
||||
remove_block "/etc/modprobe.d/vfio.conf" "HARDWARE_PASSTHROUGH"
|
||||
update-grub
|
||||
log_success "关闭设置后需要重启系统,请准备就绪后重启宿主机。"
|
||||
log_tips "重启后才可以应用对内核引导的修改哦!命令是 reboot"
|
||||
fi
|
||||
}
|
||||
|
||||
# 硬件直通菜单
|
||||
hw_passth() {
|
||||
while :; do
|
||||
clear
|
||||
show_menu_header "配置硬件直通"
|
||||
run_menu "配置硬件直通" hw_passth_render hw_passth_dispatch "0-2"
|
||||
}
|
||||
|
||||
hw_passth_render() {
|
||||
show_menu_option "1" "开启硬件直通"
|
||||
show_menu_option "2" "关闭硬件直通"
|
||||
echo "${UI_DIVIDER}"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择: [ ]" -n 1 hwmenuid
|
||||
echo # New line after input
|
||||
hwmenuid=${hwmenuid:-0}
|
||||
case "${hwmenuid}" in
|
||||
1)
|
||||
enable_pass
|
||||
pause_function
|
||||
;;
|
||||
2)
|
||||
disable_pass
|
||||
pause_function
|
||||
;;
|
||||
0)
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "无效选项!"
|
||||
pause_function
|
||||
;;
|
||||
}
|
||||
|
||||
hw_passth_dispatch() {
|
||||
case "$1" in
|
||||
1) enable_pass ;;
|
||||
2) disable_pass ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
done
|
||||
return 0
|
||||
}
|
||||
#--------------磁盘/控制器直通----------------
|
||||
|
||||
|
||||
@@ -377,22 +377,19 @@ nvidia_driver_export_report() {
|
||||
return 0
|
||||
}
|
||||
nvidia_driver_info_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "$(nvidia_t OPT_DRV_INFO)"
|
||||
run_menu "$(nvidia_t OPT_DRV_INFO)" nvidia_driver_info_menu_render nvidia_driver_info_menu_dispatch "0-2"
|
||||
}
|
||||
nvidia_driver_info_menu_render() {
|
||||
show_menu_option "1" "查看驱动与监控面板"
|
||||
show_menu_option "2" "导出驱动诊断报告"
|
||||
show_menu_option "0" "$(nvidia_t OPT_BACK)"
|
||||
show_menu_footer
|
||||
read -p "$(nvidia_t INPUT_CHOICE) [0-2]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
nvidia_driver_info_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) nvidia_driver_info ;;
|
||||
2) nvidia_driver_export_report ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
nvidia_apt_has_pkg() {
|
||||
local pkg="$1"
|
||||
@@ -495,26 +492,23 @@ nvidia_driver_rollback() {
|
||||
return 0
|
||||
}
|
||||
nvidia_driver_switch_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "$(nvidia_t OPT_DRV_SWITCH)"
|
||||
run_menu "$(nvidia_t OPT_DRV_SWITCH)" nvidia_driver_switch_menu_render nvidia_driver_switch_menu_dispatch "0-3"
|
||||
}
|
||||
nvidia_driver_switch_menu_render() {
|
||||
echo -e "${YELLOW}$(nvidia_t WARN_HIGH_RISK)${NC}"
|
||||
echo -e "${UI_DIVIDER}"
|
||||
show_menu_option "1" "切换到闭源驱动(官方 NVIDIA)"
|
||||
show_menu_option "2" "切换到开源驱动(nouveau)"
|
||||
show_menu_option "3" "回滚最近一次备份"
|
||||
show_menu_option "0" "$(nvidia_t OPT_BACK)"
|
||||
show_menu_footer
|
||||
read -p "$(nvidia_t INPUT_CHOICE) [0-3]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
nvidia_driver_switch_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) nvidia_driver_switch_to_proprietary ;;
|
||||
2) nvidia_driver_switch_to_open ;;
|
||||
3) nvidia_driver_rollback ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
nvidia_host_prepare_for_passthrough() {
|
||||
echo -e "${YELLOW}将执行以下操作:${NC}"
|
||||
@@ -524,7 +518,14 @@ nvidia_host_prepare_for_passthrough() {
|
||||
echo " 4) 执行 update-grub 与 update-initramfs"
|
||||
echo
|
||||
|
||||
if ! confirm_action "确认执行宿主机预配置?"; then
|
||||
gpu_warn_active_stacks
|
||||
|
||||
if ! confirm_high_risk_action \
|
||||
"NVIDIA 宿主机直通预配置" \
|
||||
"将修改 GRUB 内核参数、/etc/modules 与显卡驱动黑名单,并重建 initramfs。" \
|
||||
"配置错误可能导致宿主机本地显示输出丢失或无法进入图形环境。" \
|
||||
"操作前请确认已有带外管理(IPMI/iKVM)或 SSH 通道,必要时先备份 /etc/default/grub。" \
|
||||
"NVIDIA-HOST"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -587,10 +588,7 @@ nvidia_setup_vgpu_unlock() {
|
||||
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 "取消"
|
||||
if ! confirm_action "已阅读文档并了解 vGPU Unlock 风险,继续部署流程"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -658,9 +656,9 @@ EOF
|
||||
return 0
|
||||
}
|
||||
nvidia_gpu_management_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "$(nvidia_t MENU_TITLE)"
|
||||
run_menu "$(nvidia_t MENU_TITLE)" nvidia_gpu_management_menu_render nvidia_gpu_management_menu_dispatch "0-5"
|
||||
}
|
||||
nvidia_gpu_management_menu_render() {
|
||||
echo -e "${CYAN}$(nvidia_t MENU_DESC)${NC}"
|
||||
echo -e "${UI_DIVIDER}"
|
||||
show_menu_option "1" "$(nvidia_t OPT_PT)"
|
||||
@@ -668,18 +666,15 @@ nvidia_gpu_management_menu() {
|
||||
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-5]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
nvidia_gpu_management_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) nvidia_gpu_passthrough_vm ;;
|
||||
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 "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -272,7 +272,12 @@ rdm_single_disk_detach() {
|
||||
backup_file "$conf_path" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if ! confirm_action "从 VM $vmid 删除磁盘插槽(--delete $slot)"; then
|
||||
if ! confirm_high_risk_action \
|
||||
"从 VM $vmid 删除磁盘插槽 $slot" \
|
||||
"上方列表包含该 VM 的全部磁盘插槽(不仅是直通盘),删错会移除系统盘/数据盘的引用。" \
|
||||
"qm set --delete 只解除引用不删除数据,但 VM 可能因缺盘无法启动。" \
|
||||
"请再次核对插槽内容确为要取消直通的磁盘:$line" \
|
||||
"DETACH"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
@@ -195,9 +195,10 @@ vm_schedule_remove_backup_job() {
|
||||
display_success "定时备份任务已删除" "$marker"
|
||||
}
|
||||
vm_schedule_backup_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "VM 定时备份"
|
||||
run_menu "VM 定时备份" vm_schedule_backup_menu_render vm_schedule_backup_menu_dispatch "0-2"
|
||||
}
|
||||
|
||||
vm_schedule_backup_menu_render() {
|
||||
echo -e "${YELLOW}当前任务:${NC}"
|
||||
if [[ -f "$VM_BACKUP_CRON_FILE" ]]; then
|
||||
grep -E '^[^#]' "$VM_BACKUP_CRON_FILE" 2>/dev/null | sed 's/^/ /' || true
|
||||
@@ -207,17 +208,13 @@ vm_schedule_backup_menu() {
|
||||
echo -e "${UI_DIVIDER}"
|
||||
show_menu_option "1" "新增定时备份任务"
|
||||
show_menu_option "2" "删除定时备份任务"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-2]: " choice
|
||||
case "$choice" in
|
||||
vm_schedule_backup_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) vm_schedule_add_backup_job ;;
|
||||
2) vm_schedule_remove_backup_job ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -188,9 +188,10 @@ vm_cloud_image_to_template() {
|
||||
display_success "云镜像模板准备完成" "VMID: $vmid"
|
||||
}
|
||||
vm_template_cloudinit_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "模板 / 克隆 / Cloud-Init"
|
||||
run_menu "模板 / 克隆 / Cloud-Init" vm_template_cloudinit_menu_render vm_template_cloudinit_menu_dispatch "0-6"
|
||||
}
|
||||
|
||||
vm_template_cloudinit_menu_render() {
|
||||
vm_show_data_risk_banner
|
||||
show_menu_option "1" "列出所有模板"
|
||||
show_menu_option "2" "将现有 VM 转换为模板"
|
||||
@@ -198,21 +199,17 @@ vm_template_cloudinit_menu() {
|
||||
show_menu_option "4" "链接克隆模板"
|
||||
show_menu_option "5" "导入云镜像并生成模板"
|
||||
show_menu_option "6" "配置 Cloud-Init 参数"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-6]: " choice
|
||||
case "$choice" in
|
||||
vm_template_cloudinit_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) vm_show_template_records ;;
|
||||
2) vm_convert_to_template ;;
|
||||
3) vm_clone_vm full ;;
|
||||
4) vm_clone_vm linked ;;
|
||||
5) vm_cloud_image_to_template ;;
|
||||
6) vm_cloudinit_configure ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -18,23 +18,29 @@ vm_select_export_file() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
local count manual_idx
|
||||
count="$(echo "$files" | awk 'NF{c++} END{print c+0}')"
|
||||
manual_idx=$((count + 1))
|
||||
|
||||
{
|
||||
echo -e "${CYAN}已发现 VM 配置导出文件:${NC}"
|
||||
echo "$files" | awk -F'|' '{printf " [%d] %-10s %-16s %s\n", NR, $2, $3, $1}'
|
||||
printf ' [%d] 手动输入配置文件路径\n' "$manual_idx"
|
||||
echo -e "${UI_DIVIDER}"
|
||||
} >&2
|
||||
|
||||
local pick line path
|
||||
read -p "请选择配置文件序号 (0 手动输入): " pick
|
||||
read -p "请选择配置文件序号 (0 返回): " pick
|
||||
pick="${pick:-0}"
|
||||
if [[ "$pick" == "0" ]]; then
|
||||
[[ "$pick" == "0" ]] && return 2
|
||||
[[ "$pick" =~ ^[0-9]+$ ]] || return 1
|
||||
if [[ "$pick" -eq "$manual_idx" ]]; then
|
||||
local manual
|
||||
read -p "请输入配置文件完整路径: " manual
|
||||
[[ -n "$manual" && -f "$manual" ]] || return 1
|
||||
echo "$manual"
|
||||
return 0
|
||||
fi
|
||||
[[ "$pick" =~ ^[0-9]+$ ]] || return 1
|
||||
line="$(echo "$files" | awk -F'|' -v n="$pick" 'NR==n{print $0}')"
|
||||
path="$(echo "$line" | awk -F'|' '{print $1}')"
|
||||
[[ -n "$path" && -f "$path" ]] || return 1
|
||||
@@ -191,23 +197,20 @@ vm_import_config() {
|
||||
display_success "VM 配置导入完成" "新 VMID: $new_vmid"
|
||||
}
|
||||
vm_config_io_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "VM 配置导入/导出"
|
||||
run_menu "VM 配置导入/导出" vm_config_io_menu_render vm_config_io_menu_dispatch "0-2"
|
||||
}
|
||||
|
||||
vm_config_io_menu_render() {
|
||||
vm_show_data_risk_banner
|
||||
show_menu_option "1" "导出 VM 配置"
|
||||
show_menu_option "2" "导入 VM 配置"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-2]: " choice
|
||||
case "$choice" in
|
||||
vm_config_io_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) vm_export_config ;;
|
||||
2) vm_import_config ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -132,27 +132,24 @@ vm_move_disk() {
|
||||
display_success "磁盘迁移完成" "$slot -> $target_store"
|
||||
}
|
||||
vm_disk_management_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "虚拟机磁盘管理"
|
||||
run_menu "虚拟机磁盘管理" vm_disk_management_menu_render vm_disk_management_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
vm_disk_management_menu_render() {
|
||||
vm_show_data_risk_banner
|
||||
show_menu_option "1" "磁盘扩容"
|
||||
show_menu_option "2" "添加磁盘"
|
||||
show_menu_option "3" "移除磁盘"
|
||||
show_menu_option "4" "迁移磁盘到其他存储"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
vm_disk_management_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) vm_resize_disk ;;
|
||||
2) vm_add_disk ;;
|
||||
3) vm_remove_disk ;;
|
||||
4) vm_move_disk ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -459,9 +459,10 @@ garbage_cleanup_scan_report() {
|
||||
echo " 疑似孤立磁盘请使用菜单中的只读扫描单独查看。"
|
||||
}
|
||||
garbage_cleanup_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "垃圾清理"
|
||||
run_menu "垃圾清理" garbage_cleanup_menu_render garbage_cleanup_menu_dispatch "0-5"
|
||||
}
|
||||
|
||||
garbage_cleanup_menu_render() {
|
||||
show_menu_option "1" "一键扫描报告 ${CYAN}(只读)${NC}"
|
||||
show_menu_option "2" "清理缓存、日志与本工具旧文件"
|
||||
show_menu_option "3" "清理过期/无主 vzdump 备份"
|
||||
@@ -469,20 +470,16 @@ garbage_cleanup_menu() {
|
||||
show_menu_option "5" "扫描疑似孤立磁盘 ${YELLOW}(只读,不删除)${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
echo -e "${YELLOW}说明:${NC} 清理前会列出候选项;备份和快照删除均需要高风险确认。"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-5]: " choice
|
||||
case "$choice" in
|
||||
garbage_cleanup_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) garbage_cleanup_scan_report ;;
|
||||
2) garbage_cleanup_basic ;;
|
||||
3) garbage_cleanup_prune_backups ;;
|
||||
4) garbage_cleanup_prune_snapshots ;;
|
||||
5) garbage_cleanup_orphan_disk_report ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,60 +3,54 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
vm_advanced_operations_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "虚拟机高级运维工具箱"
|
||||
vm_show_data_risk_banner
|
||||
show_menu_option "1" "VM 备份与恢复"
|
||||
show_menu_option "2" "VM 配置导入/导出"
|
||||
show_menu_option "3" "模板 / 克隆 / Cloud-Init"
|
||||
show_menu_option "4" "虚拟机磁盘管理"
|
||||
show_menu_option "5" "快照管理"
|
||||
show_menu_option "6" "启动顺序与网络管理"
|
||||
show_menu_option "7" "集群内迁移 VM"
|
||||
echo -e "${RED}警告:涉及备份恢复、磁盘、快照、模板与迁移时,必须先确认备份可用,再核对 VMID / 槽位 / 目标存储。${NC}"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-7]: " choice
|
||||
case "$choice" in
|
||||
1) vm_backup_restore_menu ;;
|
||||
2) vm_config_io_menu ;;
|
||||
3) vm_template_cloudinit_menu ;;
|
||||
4) vm_disk_management_menu ;;
|
||||
5) vm_snapshot_menu ;;
|
||||
6) vm_startup_network_menu ;;
|
||||
7) vm_cluster_migrate ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
run_menu "虚拟机高级运维工具箱" vm_advanced_operations_render vm_advanced_operations_dispatch "0-5"
|
||||
}
|
||||
# 二级菜单:虚拟机与容器
|
||||
|
||||
vm_advanced_operations_render() {
|
||||
vm_show_data_risk_banner
|
||||
show_menu_option "1" "VM 配置导入/导出"
|
||||
show_menu_option "2" "模板 / 克隆 / Cloud-Init"
|
||||
show_menu_option "3" "虚拟机磁盘管理"
|
||||
show_menu_option "4" "启动顺序与网络管理"
|
||||
show_menu_option "5" "集群内迁移 VM"
|
||||
echo -e " ${RED}警告:涉及磁盘、模板与迁移时,必须先确认备份可用,再核对 VMID / 槽位 / 目标存储。${NC}"
|
||||
}
|
||||
|
||||
vm_advanced_operations_dispatch() {
|
||||
case "$1" in
|
||||
1) vm_config_io_menu ;;
|
||||
2) vm_template_cloudinit_menu ;;
|
||||
3) vm_disk_management_menu ;;
|
||||
4) vm_startup_network_menu ;;
|
||||
5) vm_cluster_migrate ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
# 二级菜单:虚拟机与容器(快照/备份为高频操作,直达本级菜单,不再埋在工具箱内)
|
||||
menu_vm_container() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "虚拟机与容器"
|
||||
run_menu "虚拟机与容器" menu_vm_container_render menu_vm_container_dispatch "0-6"
|
||||
}
|
||||
|
||||
menu_vm_container_render() {
|
||||
show_menu_option "1" "${CYAN}FastPVE${NC} - 虚拟机快速下载"
|
||||
show_menu_option "2" "虚拟机/容器定时开关机"
|
||||
show_menu_option "3" "IMG 镜像导入(转 QCOW2/RAW)"
|
||||
show_menu_option "4" "虚拟机高级运维工具箱"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case $choice in
|
||||
show_menu_option "4" "快照管理"
|
||||
show_menu_option "5" "VM 备份与恢复"
|
||||
show_menu_option "6" "虚拟机高级运维工具箱"
|
||||
}
|
||||
|
||||
menu_vm_container_dispatch() {
|
||||
case "$1" in
|
||||
1) fastpve_quick_download_menu ;;
|
||||
2) manage_vm_schedule ;;
|
||||
3) img_convert_import_menu ;;
|
||||
4) vm_advanced_operations_menu ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
4) vm_snapshot_menu ;;
|
||||
5) vm_backup_restore_menu ;;
|
||||
6) vm_advanced_operations_menu ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============ 宿主机网络 / 防火墙 / IPv6 / 诊断工具箱 ============
|
||||
|
||||
@@ -112,27 +112,24 @@ vm_modify_network() {
|
||||
display_success "网卡参数已更新" "$slot = $updated"
|
||||
}
|
||||
vm_startup_network_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "启动顺序与网络管理"
|
||||
run_menu "启动顺序与网络管理" vm_startup_network_menu_render vm_startup_network_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
vm_startup_network_menu_render() {
|
||||
vm_show_data_risk_banner
|
||||
show_menu_option "1" "设置开机自启 / 启动顺序 / 启动延迟"
|
||||
show_menu_option "2" "添加网卡"
|
||||
show_menu_option "3" "移除网卡"
|
||||
show_menu_option "4" "修改 bridge / VLAN"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
vm_startup_network_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) vm_configure_startup_policy ;;
|
||||
2) vm_add_network ;;
|
||||
3) vm_remove_network ;;
|
||||
4) vm_modify_network ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -13,23 +13,29 @@ vm_select_backup_archive() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
local count manual_idx
|
||||
count="$(echo "$archives" | awk 'NF{c++} END{print c+0}')"
|
||||
manual_idx=$((count + 1))
|
||||
|
||||
{
|
||||
echo -e "${CYAN}已发现备份文件:${NC}"
|
||||
echo "$archives" | awk -F'|' '{printf " [%d] %-10s %-16s %s\n", NR, $2, $3, $1}'
|
||||
printf ' [%d] 手动输入备份文件路径\n' "$manual_idx"
|
||||
echo -e "${UI_DIVIDER}"
|
||||
} >&2
|
||||
|
||||
local pick line path
|
||||
read -p "请选择备份序号 (0 手动输入): " pick
|
||||
read -p "请选择备份序号 (0 返回): " pick
|
||||
pick="${pick:-0}"
|
||||
if [[ "$pick" == "0" ]]; then
|
||||
[[ "$pick" == "0" ]] && return 2
|
||||
[[ "$pick" =~ ^[0-9]+$ ]] || return 1
|
||||
if [[ "$pick" -eq "$manual_idx" ]]; then
|
||||
local manual
|
||||
read -p "请输入备份文件完整路径: " manual
|
||||
[[ -n "$manual" && -f "$manual" ]] || return 1
|
||||
echo "$manual"
|
||||
return 0
|
||||
fi
|
||||
[[ "$pick" =~ ^[0-9]+$ ]] || return 1
|
||||
line="$(echo "$archives" | awk -F'|' -v n="$pick" 'NR==n{print $0}')"
|
||||
path="$(echo "$line" | awk -F'|' '{print $1}')"
|
||||
[[ -n "$path" && -f "$path" ]] || return 1
|
||||
@@ -91,27 +97,24 @@ vm_restore_from_backup() {
|
||||
display_success "恢复完成" "新 VMID: $new_vmid"
|
||||
}
|
||||
vm_backup_restore_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "VM 备份与恢复"
|
||||
run_menu "VM 备份与恢复" vm_backup_restore_menu_render vm_backup_restore_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
vm_backup_restore_menu_render() {
|
||||
vm_show_data_risk_banner
|
||||
show_menu_option "1" "创建 VM 备份(vzdump)"
|
||||
show_menu_option "2" "从备份恢复为新 VM"
|
||||
show_menu_option "3" "定时备份任务管理"
|
||||
show_menu_option "4" "备份文件跨机恢复引导"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
vm_backup_restore_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) vm_backup_create ;;
|
||||
2) vm_restore_from_backup ;;
|
||||
3) vm_schedule_backup_menu ;;
|
||||
4) vm_backup_transfer_guide ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ manage_vm_schedule() {
|
||||
fi
|
||||
|
||||
if [[ -n "$cron_content" ]]; then
|
||||
if ! confirm_action "将定时开关机任务写入 /etc/cron.d/pve-tools-schedule(以 root 身份定时执行)"; then
|
||||
log_info "已取消写入"
|
||||
continue
|
||||
fi
|
||||
apply_block "/etc/cron.d/pve-tools-schedule" "SCHEDULE_$target_id" "$(echo -e "$cron_content")"
|
||||
log_success "ID $target_id 的定时任务已更新"
|
||||
systemctl restart cron 2>/dev/null || service cron restart 2>/dev/null
|
||||
|
||||
@@ -123,27 +123,24 @@ vm_rollback_snapshot() {
|
||||
display_success "快照回滚完成" "$snapshot_name"
|
||||
}
|
||||
vm_snapshot_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "快照管理"
|
||||
run_menu "快照管理" vm_snapshot_menu_render vm_snapshot_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
vm_snapshot_menu_render() {
|
||||
vm_show_data_risk_banner
|
||||
show_menu_option "1" "创建快照(支持批量)"
|
||||
show_menu_option "2" "列出 VM 快照"
|
||||
show_menu_option "3" "删除快照"
|
||||
show_menu_option "4" "回滚到快照"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
vm_snapshot_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) vm_create_snapshot ;;
|
||||
2) vm_list_snapshots ;;
|
||||
3) vm_delete_snapshot ;;
|
||||
4) vm_rollback_snapshot ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -104,9 +104,10 @@ host_network_delete_bond() {
|
||||
rm -f "$tmp"
|
||||
}
|
||||
host_network_bond_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "Bond 管理"
|
||||
run_menu "Bond 管理" host_network_bond_menu_render host_network_bond_menu_dispatch "0-3"
|
||||
}
|
||||
|
||||
host_network_bond_menu_render() {
|
||||
host_network_show_risk_banner
|
||||
echo -e "${CYAN}当前 Bond:${NC}"
|
||||
if host_network_get_configured_bonds | awk 'NF{print " - "$0}'; then :; fi
|
||||
@@ -114,16 +115,14 @@ host_network_bond_menu() {
|
||||
show_menu_option "1" "列出 Bond"
|
||||
show_menu_option "2" "创建 Bond"
|
||||
show_menu_option "3" "删除 Bond"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-3]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
host_network_bond_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) host_network_show_current_overview ;;
|
||||
2) host_network_create_bond ;;
|
||||
3) host_network_delete_bond ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -101,9 +101,10 @@ host_network_delete_bridge() {
|
||||
rm -f "$tmp"
|
||||
}
|
||||
host_network_bridge_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "桥接管理"
|
||||
run_menu "桥接管理" host_network_bridge_menu_render host_network_bridge_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
host_network_bridge_menu_render() {
|
||||
host_network_show_risk_banner
|
||||
echo -e "${CYAN}当前 bridge:${NC}"
|
||||
if host_network_get_configured_bridges | awk 'NF{print " - "$0}'; then :; fi
|
||||
@@ -112,17 +113,15 @@ host_network_bridge_menu() {
|
||||
show_menu_option "2" "创建桥接"
|
||||
show_menu_option "3" "删除桥接"
|
||||
show_menu_option "4" "MAC 地址绑定管理(防网卡顺序变化)"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
host_network_bridge_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) host_network_show_current_overview ;;
|
||||
2) host_network_create_bridge ;;
|
||||
3) host_network_delete_bridge ;;
|
||||
4) host_network_mac_binding_menu ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -104,28 +104,27 @@ netdiag_quick_stack_check() {
|
||||
echo "$UI_DIVIDER"
|
||||
}
|
||||
netdiag_toolbox_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "网络诊断工具箱"
|
||||
run_menu "网络诊断工具箱" netdiag_toolbox_menu_render netdiag_toolbox_menu_dispatch "0-6"
|
||||
}
|
||||
|
||||
netdiag_toolbox_menu_render() {
|
||||
show_menu_option "1" "网络摘要与监听端口"
|
||||
show_menu_option "2" "traceroute"
|
||||
show_menu_option "3" "mtr"
|
||||
show_menu_option "4" "nmap"
|
||||
show_menu_option "5" "tcpdump"
|
||||
show_menu_option "6" "端口连通性检查(宿主机 / VM / 自定义)"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-6]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
netdiag_toolbox_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) netdiag_quick_stack_check ;;
|
||||
2) netdiag_run_traceroute ;;
|
||||
3) netdiag_run_mtr ;;
|
||||
4) netdiag_run_nmap ;;
|
||||
5) netdiag_run_tcpdump ;;
|
||||
6) netdiag_check_port_connectivity ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -335,46 +335,48 @@ host_firewall_toggle_enable() {
|
||||
log_warn "PVE 客体防火墙还依赖对应网卡开启 firewall=1;如未开启,请同步检查网卡配置。"
|
||||
fi
|
||||
}
|
||||
host_firewall_toggle_node() {
|
||||
local node_name rc
|
||||
node_name="$(host_firewall_select_node_name)"
|
||||
rc=$?
|
||||
[[ "$rc" -eq 2 ]] && return 0
|
||||
[[ -n "$node_name" ]] && host_firewall_toggle_enable node "$node_name" "节点 $node_name"
|
||||
}
|
||||
host_firewall_toggle_vm() {
|
||||
local vmid rc
|
||||
vmid="$(host_firewall_select_guest vm)"
|
||||
rc=$?
|
||||
[[ "$rc" -eq 2 ]] && return 0
|
||||
[[ -n "$vmid" ]] && host_firewall_toggle_enable vm "$vmid" "VM $vmid"
|
||||
}
|
||||
host_firewall_toggle_ct() {
|
||||
local ctid rc
|
||||
ctid="$(host_firewall_select_guest ct)"
|
||||
rc=$?
|
||||
[[ "$rc" -eq 2 ]] && return 0
|
||||
[[ -n "$ctid" ]] && host_firewall_toggle_enable ct "$ctid" "CT $ctid"
|
||||
}
|
||||
host_firewall_toggle_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "PVE 防火墙开关"
|
||||
run_menu "PVE 防火墙开关" host_firewall_toggle_menu_render host_firewall_toggle_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
host_firewall_toggle_menu_render() {
|
||||
host_network_show_risk_banner
|
||||
show_menu_option "1" "数据中心级别开关"
|
||||
show_menu_option "2" "节点级别开关"
|
||||
show_menu_option "3" "VM 级别开关"
|
||||
show_menu_option "4" "CT 级别开关"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
host_firewall_toggle_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) host_firewall_toggle_enable datacenter cluster "数据中心" ;;
|
||||
2)
|
||||
local node_name rc
|
||||
node_name="$(host_firewall_select_node_name)"
|
||||
rc=$?
|
||||
[[ "$rc" -eq 2 ]] && continue
|
||||
[[ -n "$node_name" ]] && host_firewall_toggle_enable node "$node_name" "节点 $node_name"
|
||||
;;
|
||||
3)
|
||||
local vmid rc
|
||||
vmid="$(host_firewall_select_guest vm)"
|
||||
rc=$?
|
||||
[[ "$rc" -eq 2 ]] && continue
|
||||
[[ -n "$vmid" ]] && host_firewall_toggle_enable vm "$vmid" "VM $vmid"
|
||||
;;
|
||||
4)
|
||||
local ctid rc
|
||||
ctid="$(host_firewall_select_guest ct)"
|
||||
rc=$?
|
||||
[[ "$rc" -eq 2 ]] && continue
|
||||
[[ -n "$ctid" ]] && host_firewall_toggle_enable ct "$ctid" "CT $ctid"
|
||||
;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
2) host_firewall_toggle_node ;;
|
||||
3) host_firewall_toggle_vm ;;
|
||||
4) host_firewall_toggle_ct ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
host_firewall_list_security_groups() {
|
||||
clear
|
||||
@@ -613,9 +615,10 @@ host_firewall_import_ruleset() {
|
||||
display_success "规则集已导入" "$path"
|
||||
}
|
||||
host_firewall_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "PVE 防火墙管理"
|
||||
run_menu "PVE 防火墙管理" host_firewall_menu_render host_firewall_menu_dispatch "0-7"
|
||||
}
|
||||
|
||||
host_firewall_menu_render() {
|
||||
host_network_show_risk_banner
|
||||
show_menu_option "1" "数据中心 / 节点 / VM / CT 防火墙开关"
|
||||
show_menu_option "2" "查看目标规则集"
|
||||
@@ -624,10 +627,10 @@ host_firewall_menu() {
|
||||
show_menu_option "5" "删除安全组规则"
|
||||
show_menu_option "6" "导出规则集(JSON / CLI)"
|
||||
show_menu_option "7" "导入规则集(JSON / CLI)"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-7]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
host_firewall_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) host_firewall_toggle_menu ;;
|
||||
2) host_firewall_show_target_rules ;;
|
||||
3) host_firewall_list_security_groups ;;
|
||||
@@ -635,9 +638,7 @@ host_firewall_menu() {
|
||||
5) host_firewall_delete_security_group_rule ;;
|
||||
6) host_firewall_export_ruleset ;;
|
||||
7) host_firewall_import_ruleset ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
menu_host_networking() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "宿主机网络配置向导"
|
||||
run_menu "宿主机网络配置向导" menu_host_networking_render menu_host_networking_dispatch "0-8"
|
||||
}
|
||||
|
||||
menu_host_networking_render() {
|
||||
host_network_show_risk_banner
|
||||
show_menu_option "1" "列出当前网卡与桥接(vmbr0~N)"
|
||||
show_menu_option "2" "桥接管理(创建 / 删除)"
|
||||
@@ -15,11 +16,11 @@ menu_host_networking() {
|
||||
show_menu_option "6" "PVE 防火墙管理"
|
||||
show_menu_option "7" "IPv6 助手"
|
||||
show_menu_option "8" "网络诊断工具箱"
|
||||
echo -e "${RED}警告:应用宿主机网络修改时,建议在控制台或带外管理环境中执行,避免误断 SSH / WebUI。${NC}"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-8]: " choice
|
||||
case "$choice" in
|
||||
echo -e " ${RED}警告:应用宿主机网络修改时,建议在控制台或带外管理环境中执行,避免误断 SSH / WebUI。${NC}"
|
||||
}
|
||||
|
||||
menu_host_networking_dispatch() {
|
||||
case "$1" in
|
||||
1) host_network_show_current_overview ;;
|
||||
2) host_network_bridge_menu ;;
|
||||
3) host_network_configure_interface_addressing ;;
|
||||
@@ -28,11 +29,7 @@ menu_host_networking() {
|
||||
6) host_firewall_menu ;;
|
||||
7) ipv6_helper_menu ;;
|
||||
8) netdiag_toolbox_menu ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 二级菜单:存储与硬盘
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
host_network_show_risk_banner() {
|
||||
# 每会话仅完整展示一次,避免每次重绘菜单都整屏刷同一段风险横幅
|
||||
if [[ "${HOST_NET_RISK_BANNER_SHOWN:-0}" -eq 1 ]]; then
|
||||
return 0
|
||||
fi
|
||||
HOST_NET_RISK_BANNER_SHOWN=1
|
||||
echo -e "${RED}${UI_DIVIDER}${NC}"
|
||||
echo -e "${RED}高风险提示:以下功能会直接改写宿主机网络、防火墙和 IPv6 行为。${NC}"
|
||||
echo -e "${YELLOW}请仅在控制台或带外管理可用、已确认维护窗口、已准备回滚方案时继续。${NC}"
|
||||
|
||||
@@ -111,27 +111,26 @@ ipv6_helper_test_connectivity() {
|
||||
echo "$UI_DIVIDER"
|
||||
}
|
||||
ipv6_helper_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "IPv6 助手"
|
||||
run_menu "IPv6 助手" ipv6_helper_menu_render ipv6_helper_menu_dispatch "0-5"
|
||||
}
|
||||
|
||||
ipv6_helper_menu_render() {
|
||||
host_network_show_risk_banner
|
||||
show_menu_option "1" "检测宿主机 IPv6 就绪度"
|
||||
show_menu_option "2" "检测 VM IPv6 就绪度(Guest Agent)"
|
||||
show_menu_option "3" "一键配置桥接 IPv6 透传 / SLAAC"
|
||||
show_menu_option "4" "一键配置桥接 NAT6"
|
||||
show_menu_option "5" "测试 IPv6 连通性"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-5]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
ipv6_helper_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) ipv6_helper_detect_host_readiness ;;
|
||||
2) ipv6_helper_detect_vm_readiness ;;
|
||||
3) ipv6_helper_configure_passthrough ;;
|
||||
4) ipv6_helper_configure_nat6 ;;
|
||||
5) ipv6_helper_test_connectivity ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -479,9 +479,10 @@ host_network_delete_mac_binding() {
|
||||
display_success "MAC 地址固定命名已删除: ${mac} → ${link_name}" "如需恢复,请重新创建对应 .link 文件。"
|
||||
}
|
||||
host_network_mac_binding_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "MAC 地址绑定管理"
|
||||
run_menu "MAC 地址绑定管理" host_network_mac_binding_menu_render host_network_mac_binding_menu_dispatch "0-2"
|
||||
}
|
||||
|
||||
host_network_mac_binding_menu_render() {
|
||||
echo -e "${YELLOW}说明:通过 MAC 地址而不是接口名来固定网卡命名,避免重启或硬件变更后网卡顺序变化导致网络配置失效。${NC}"
|
||||
echo -e "${YELLOW}绑定后会在 /etc/systemd/network/ 中生成 .link 规则;若系统残留旧式 udev 规则,脚本会尝试同步清理。${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
@@ -489,15 +490,13 @@ host_network_mac_binding_menu() {
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "1" "创建 MAC 地址绑定"
|
||||
show_menu_option "2" "删除 MAC 地址绑定"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-2]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
host_network_mac_binding_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) host_network_create_mac_binding ;;
|
||||
2) host_network_delete_mac_binding ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -92,9 +92,10 @@ host_network_delete_vlan() {
|
||||
rm -f "$tmp"
|
||||
}
|
||||
host_network_vlan_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "VLAN 子接口管理"
|
||||
run_menu "VLAN 子接口管理" host_network_vlan_menu_render host_network_vlan_menu_dispatch "0-3"
|
||||
}
|
||||
|
||||
host_network_vlan_menu_render() {
|
||||
host_network_show_risk_banner
|
||||
echo -e "${CYAN}当前 VLAN 子接口:${NC}"
|
||||
if host_network_get_configured_vlans | awk 'NF{print " - "$0}'; then :; fi
|
||||
@@ -102,16 +103,14 @@ host_network_vlan_menu() {
|
||||
show_menu_option "1" "列出 VLAN 子接口"
|
||||
show_menu_option "2" "创建 VLAN 子接口"
|
||||
show_menu_option "3" "删除 VLAN 子接口"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-3]: " choice
|
||||
case "$choice" in
|
||||
}
|
||||
|
||||
host_network_vlan_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) host_network_show_current_overview ;;
|
||||
2) host_network_create_vlan ;;
|
||||
3) host_network_delete_vlan ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ pve9_ceph() {
|
||||
[[ -e /etc/apt/sources.list.d/ceph.sources ]] && mv /etc/apt/sources.list.d/ceph.sources /etc/apt/backup/ceph.sources.bak
|
||||
[[ -e /etc/apt/sources.list.d/ceph.list ]] && mv /etc/apt/sources.list.d/ceph.list /etc/apt/backup/ceph.list.bak
|
||||
|
||||
[[ -e /usr/share/perl5/PVE/CLI/pveceph.pm ]] && cp -rf /usr/share/perl5/PVE/CLI/pveceph.pm /etc/apt/backup/pveceph.pm.bak
|
||||
# 仅首次备份原始文件,避免第二次运行时用已被 sed 修改过的版本覆盖掉好备份
|
||||
[[ -e /usr/share/perl5/PVE/CLI/pveceph.pm && ! -e /etc/apt/backup/pveceph.pm.bak ]] && cp -a /usr/share/perl5/PVE/CLI/pveceph.pm /etc/apt/backup/pveceph.pm.bak
|
||||
sed -i 's|http://download.proxmox.com|https://mirrors.tuna.tsinghua.edu.cn/proxmox|g' /usr/share/perl5/PVE/CLI/pveceph.pm
|
||||
|
||||
cat > /etc/apt/sources.list.d/ceph.list <<-EOF
|
||||
@@ -65,7 +66,8 @@ pve8_ceph() {
|
||||
[[ -e /etc/apt/sources.list.d/ceph.sources ]] && mv /etc/apt/sources.list.d/ceph.sources /etc/apt/backup/ceph.sources.bak
|
||||
[[ -e /etc/apt/sources.list.d/ceph.list ]] && mv /etc/apt/sources.list.d/ceph.list /etc/apt/backup/ceph.list.bak
|
||||
|
||||
[[ -e /usr/share/perl5/PVE/CLI/pveceph.pm ]] && cp -rf /usr/share/perl5/PVE/CLI/pveceph.pm /etc/apt/backup/pveceph.pm.bak
|
||||
# 仅首次备份原始文件,避免第二次运行时用已被 sed 修改过的版本覆盖掉好备份
|
||||
[[ -e /usr/share/perl5/PVE/CLI/pveceph.pm && ! -e /etc/apt/backup/pveceph.pm.bak ]] && cp -a /usr/share/perl5/PVE/CLI/pveceph.pm /etc/apt/backup/pveceph.pm.bak
|
||||
sed -i 's|http://download.proxmox.com|https://mirrors.tuna.tsinghua.edu.cn/proxmox|g' /usr/share/perl5/PVE/CLI/pveceph.pm
|
||||
|
||||
cat > /etc/apt/sources.list.d/ceph.list <<-EOF
|
||||
@@ -78,22 +80,53 @@ EOF
|
||||
#---------PVE7/8添加ceph-quincy源-----------
|
||||
#---------PVE一键卸载ceph-----------
|
||||
remove_ceph() {
|
||||
log_warn "会卸载ceph,并删除所有ceph相关文件!"
|
||||
local unit osd_dirs
|
||||
|
||||
systemctl stop ceph-mon.target && systemctl stop ceph-mgr.target && systemctl stop ceph-mds.target && systemctl stop ceph-osd.target
|
||||
if ! command -v pveceph >/dev/null 2>&1 && [[ ! -d /var/lib/ceph && ! -e /etc/pve/ceph.conf ]]; then
|
||||
log_info "未检测到 Ceph 安装,无需卸载。"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -e "${RED}即将完全卸载 Ceph:停止全部 Ceph 服务、purge 软件包,并删除以下数据目录:${NC}"
|
||||
echo " /var/lib/ceph (含全部 mon/mgr/mds/osd 数据)"
|
||||
echo " /etc/ceph、/etc/pve/ceph.conf、/etc/pve/priv/ceph.*、/var/log/ceph"
|
||||
|
||||
osd_dirs="$(ls -A /var/lib/ceph/osd 2>/dev/null)"
|
||||
if [[ -n "$osd_dirs" ]] || pgrep -x ceph-osd >/dev/null 2>&1; then
|
||||
echo -e "${RED}警告:检测到本机存在 OSD(数据盘)!卸载会销毁其上的所有存储数据,且不可恢复。${NC}"
|
||||
fi
|
||||
|
||||
if ! confirm_high_risk_action \
|
||||
"完全卸载本机 Ceph 并删除全部相关数据" \
|
||||
"所有 mon/mgr/mds/osd 数据目录会被永久删除,OSD 上的存储数据不可恢复" \
|
||||
"使用该 Ceph 存储的 VM/CT 磁盘将全部丢失;集群其他节点的 Ceph 状态也会受影响" \
|
||||
"请确认已迁移或备份 Ceph 上的所有数据,且该节点已按官方流程移出 Ceph 集群" \
|
||||
"DESTROY-CEPH"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
for unit in ceph-mon.target ceph-mgr.target ceph-mds.target ceph-osd.target; do
|
||||
systemctl stop "$unit" 2>/dev/null || log_warn "停止 $unit 失败或该服务不存在,继续。"
|
||||
done
|
||||
rm -rf /etc/systemd/system/ceph*
|
||||
|
||||
killall -9 ceph-mon ceph-mgr ceph-mds ceph-osd
|
||||
rm -rf /var/lib/ceph/mon/* && rm -rf /var/lib/ceph/mgr/* && rm -rf /var/lib/ceph/mds/* && rm -rf /var/lib/ceph/osd/*
|
||||
killall -9 ceph-mon ceph-mgr ceph-mds ceph-osd 2>/dev/null
|
||||
rm -rf /var/lib/ceph/mon/* /var/lib/ceph/mgr/* /var/lib/ceph/mds/* /var/lib/ceph/osd/*
|
||||
|
||||
pveceph purge
|
||||
if command -v pveceph >/dev/null 2>&1; then
|
||||
pveceph purge || log_warn "pveceph purge 未完全成功,继续清理残留文件。"
|
||||
fi
|
||||
|
||||
apt purge -y ceph-mon ceph-osd ceph-mgr ceph-mds
|
||||
apt purge -y ceph-base ceph-mgr-modules-core
|
||||
if ! apt purge -y ceph-mon ceph-osd ceph-mgr ceph-mds; then
|
||||
log_warn "部分 Ceph 核心包卸载失败,请稍后手动检查 dpkg 状态。"
|
||||
fi
|
||||
apt purge -y ceph-base ceph-mgr-modules-core || log_warn "ceph-base/ceph-mgr-modules-core 卸载失败或未安装。"
|
||||
|
||||
rm -rf /etc/ceph && rm -rf /etc/pve/ceph.conf && rm -rf /etc/pve/priv/ceph.* && rm -rf /var/log/ceph && rm -rf /etc/pve/ceph && rm -rf /var/lib/ceph
|
||||
rm -rf /etc/ceph /etc/pve/ceph.conf /etc/pve/priv/ceph.* /var/log/ceph /etc/pve/ceph /var/lib/ceph
|
||||
|
||||
mkdir -p /etc/apt/backup
|
||||
[[ -e /etc/apt/sources.list.d/ceph.sources ]] && mv /etc/apt/sources.list.d/ceph.sources /etc/apt/backup/ceph.sources.bak
|
||||
[[ -e /etc/apt/sources.list.d/ceph.list ]] && mv /etc/apt/sources.list.d/ceph.list /etc/apt/backup/ceph.list.bak
|
||||
|
||||
log_success "已成功卸载ceph."
|
||||
}
|
||||
@@ -103,41 +136,23 @@ remove_ceph() {
|
||||
# 小工具配置
|
||||
# FastPVE - PVE 虚拟机快速下载
|
||||
ceph_management_menu() {
|
||||
while true; do
|
||||
clear
|
||||
run_menu "Ceph管理" ceph_management_menu_render ceph_management_menu_dispatch "0-3"
|
||||
}
|
||||
|
||||
show_menu_header "Ceph管理"
|
||||
ceph_management_menu_render() {
|
||||
show_menu_option "1" "添加 ${CYAN}ceph-squid${NC} 源 (PVE8/9专用)"
|
||||
show_menu_option "2" "添加 ${CYAN}ceph-quincy${NC} 源 (PVE7/8专用)"
|
||||
show_menu_option "3" "${RED}卸载 Ceph${NC} (完全移除Ceph)"
|
||||
echo "${UI_DIVIDER}"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
echo
|
||||
read -p "请选择 [0-3]: " ceph_choice
|
||||
echo
|
||||
}
|
||||
|
||||
case $ceph_choice in
|
||||
1)
|
||||
pve9_ceph
|
||||
;;
|
||||
2)
|
||||
pve8_ceph
|
||||
;;
|
||||
3)
|
||||
remove_ceph
|
||||
;;
|
||||
0)
|
||||
break
|
||||
;;
|
||||
*)
|
||||
log_error "无效选择,请重新输入"
|
||||
;;
|
||||
ceph_management_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) pve9_ceph ;;
|
||||
2) pve8_ceph ;;
|
||||
3) remove_ceph ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
|
||||
echo
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 救砖:恢复官方 pve-qemu-kvm
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
menu_storage_disk() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "存储与硬盘"
|
||||
run_menu "存储与硬盘" menu_storage_disk_render menu_storage_disk_dispatch "0-7"
|
||||
}
|
||||
|
||||
menu_storage_disk_render() {
|
||||
show_menu_option "1" "存储位置查询面板 ${CYAN}(ISO/备份/SCP路径)${NC}"
|
||||
show_menu_option "2" "磁盘挂载向导 ${CYAN}(复用已有 ext4/xfs 分区)${NC}"
|
||||
show_menu_option "3" "合并 ${CYAN}local${NC} 与 ${CYAN}local-lvm${NC}"
|
||||
@@ -13,37 +14,39 @@ menu_storage_disk() {
|
||||
show_menu_option "5" "硬盘休眠配置 ${CYAN}(hdparm)${NC}"
|
||||
show_menu_option "6" "垃圾清理 ${CYAN}(缓存/备份/快照扫描)${NC}"
|
||||
show_menu_option "7" "${RED}删除 Swap 分区${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-7]: " choice
|
||||
case $choice in
|
||||
}
|
||||
|
||||
menu_storage_disk_dispatch() {
|
||||
case "$1" in
|
||||
1) pve_storage_location_panel ;;
|
||||
2) pve_storage_mount_wizard ;;
|
||||
3) merge_local_storage ;;
|
||||
4) ceph_management_menu ;;
|
||||
5)
|
||||
lsblk -o NAME,MODEL,TYPE,SIZE,MOUNTPOINT | grep disk
|
||||
read -p "请输入要配置休眠的硬盘盘符 (如 sdb, 不含/dev/): " disk_name
|
||||
if [ -b "/dev/$disk_name" ]; then
|
||||
read -p "请输入休眠时间 (1-255, 120=10分钟, 240=20分钟, 0=禁用): " sleep_val
|
||||
if [[ "$sleep_val" =~ ^[0-9]+$ ]]; then
|
||||
hdparm -S "$sleep_val" "/dev/$disk_name"
|
||||
log_success "配置已应用到 /dev/$disk_name"
|
||||
else
|
||||
log_error "无效的时间值"
|
||||
fi
|
||||
else
|
||||
log_error "未找到磁盘 /dev/$disk_name"
|
||||
fi
|
||||
;;
|
||||
5) storage_hdparm_sleep_config ;;
|
||||
6) garbage_cleanup_menu ;;
|
||||
7) remove_swap ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 二级菜单:工具与关于
|
||||
# 硬盘休眠配置(hdparm -S 写入磁盘休眠参数)
|
||||
storage_hdparm_sleep_config() {
|
||||
lsblk -o NAME,MODEL,TYPE,SIZE,MOUNTPOINT | grep disk
|
||||
local disk_name="" sleep_val=""
|
||||
prompt_value disk_name "请输入要配置休眠的硬盘盘符 (如 sdb, 不含/dev/)" || return 0
|
||||
if [[ ! -b "/dev/$disk_name" ]]; then
|
||||
display_error "未找到磁盘 /dev/$disk_name"
|
||||
return 1
|
||||
fi
|
||||
prompt_value sleep_val "请输入休眠时间 (1-255, 120=10分钟, 240=20分钟, 0=禁用)" || return 0
|
||||
if [[ ! "$sleep_val" =~ ^[0-9]+$ ]] || (( sleep_val > 255 )); then
|
||||
display_error "无效的时间值: $sleep_val" "请输入 0-255 之间的数字"
|
||||
return 1
|
||||
fi
|
||||
if ! confirm_action "执行 hdparm -S $sleep_val /dev/$disk_name(向磁盘写入休眠参数)"; then
|
||||
return 0
|
||||
fi
|
||||
hdparm -S "$sleep_val" "/dev/$disk_name"
|
||||
log_success "配置已应用到 /dev/$disk_name"
|
||||
}
|
||||
|
||||
@@ -2,34 +2,72 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
# 根文件系统扩容:按实际文件系统类型分派,不写死 ext4(swap.sh 的 remove_swap 也复用)
|
||||
storage_grow_root_fs() {
|
||||
local device="$1"
|
||||
local fstype
|
||||
fstype="$(findmnt -no FSTYPE / 2>/dev/null)"
|
||||
case "$fstype" in
|
||||
ext2|ext3|ext4)
|
||||
resize2fs "$device"
|
||||
;;
|
||||
xfs)
|
||||
xfs_growfs /
|
||||
;;
|
||||
*)
|
||||
log_error "不支持自动扩容的根文件系统类型: ${fstype:-未知},请手动扩容 $device"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
merge_local_storage() {
|
||||
local thin_lvs
|
||||
log_step "准备合并存储空间,让小硬盘发挥最大价值"
|
||||
log_warn "重要提醒:此操作会删除 local-lvm,请确保重要数据已备份!"
|
||||
|
||||
# 检查 local-lvm 是否存在(先检查再确认,避免对无操作场景要求确认词)
|
||||
if ! lvdisplay /dev/pve/data &> /dev/null; then
|
||||
log_warn "没有找到 local-lvm 分区,可能已经合并过了"
|
||||
return
|
||||
fi
|
||||
|
||||
# thin pool 上仍有 VM 磁盘时禁止删除
|
||||
thin_lvs="$(lvs --noheadings -o lv_name -S 'pool_lv=data' pve 2>/dev/null | xargs)"
|
||||
if [[ -n "$thin_lvs" ]]; then
|
||||
log_error "local-lvm (pve/data) 上仍存在以下逻辑卷,禁止删除:"
|
||||
echo " $thin_lvs"
|
||||
log_tips "请先在 Web UI 迁移或删除这些 VM 磁盘后再执行合并。"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! confirm_high_risk_action \
|
||||
"合并 local-lvm 到 local 存储" \
|
||||
"将删除 /dev/pve/data 逻辑卷,所有 LVM-thin 上的 VM 磁盘和数据将被永久销毁。" \
|
||||
"执行 lvremove、lvextend、resize2fs,不可逆。仅在 root 为 ext 文件系统时有效。" \
|
||||
"执行 lvremove、lvextend、文件系统扩容,不可逆。" \
|
||||
"请确保已将 local-lvm 上的所有 VM 磁盘迁移或备份。" \
|
||||
"CONFIRM"; then
|
||||
log_info "明智的选择!操作已取消"
|
||||
return
|
||||
fi
|
||||
|
||||
# 检查 local-lvm 是否存在
|
||||
if ! lvdisplay /dev/pve/data &> /dev/null; then
|
||||
log_warn "没有找到 local-lvm 分区,可能已经合并过了"
|
||||
return
|
||||
log_info "正在删除 local-lvm 分区..."
|
||||
if ! lvremove -f /dev/pve/data; then
|
||||
log_error "删除 pve/data 失败,操作中止(未做任何扩容)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "正在删除 local-lvm 分区..."
|
||||
lvremove -f /dev/pve/data
|
||||
|
||||
log_info "正在扩容 local 分区..."
|
||||
lvextend -l +100%FREE /dev/pve/root
|
||||
if ! lvextend -l +100%FREE /dev/pve/root; then
|
||||
log_error "扩容 pve/root 失败,请手动执行: lvextend -l +100%FREE /dev/pve/root"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "正在扩展文件系统..."
|
||||
resize2fs /dev/pve/root
|
||||
if ! storage_grow_root_fs /dev/pve/root; then
|
||||
log_error "LV 已扩容但文件系统扩展未完成,请按上方提示手动处理"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "存储合并完成!现在空间更充裕了"
|
||||
log_warn "温馨提示:请在 Web UI 中删除 local-lvm 存储配置,并编辑 local 存储勾选所有内容类型"
|
||||
|
||||
@@ -3,40 +3,58 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
remove_swap() {
|
||||
local swap_dev
|
||||
log_step "准备释放 Swap 空间给系统使用"
|
||||
log_warn "注意:删除 Swap 后请确保内存充足!"
|
||||
|
||||
# 检查 swap 是否存在(先检查再确认,避免对无操作场景要求确认词)
|
||||
if ! lvdisplay /dev/pve/swap &> /dev/null; then
|
||||
log_warn "没有找到 swap 分区,可能已经删除过了"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! confirm_high_risk_action \
|
||||
"删除 Swap 分区并扩展 root 文件系统" \
|
||||
"删除 /dev/pve/swap 逻辑卷,可能导致内存不足场景下系统不稳定。" \
|
||||
"将执行 swapoff、lvremove、lvextend、resize2fs,不可逆。" \
|
||||
"将执行 swapoff、lvremove、lvextend、文件系统扩容,不可逆。" \
|
||||
"请确保内存充足(建议 >= 8GB),并已备份重要数据。" \
|
||||
"CONFIRM"; then
|
||||
log_info "好的,操作已取消"
|
||||
return
|
||||
fi
|
||||
|
||||
# 检查 swap 是否存在
|
||||
if ! lvdisplay /dev/pve/swap &> /dev/null; then
|
||||
log_warn "没有找到 swap 分区,可能已经删除过了"
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "正在关闭 Swap..."
|
||||
swapoff /dev/mapper/pve-swap
|
||||
swap_dev="$(readlink -f /dev/mapper/pve-swap 2>/dev/null)"
|
||||
if [[ -n "$swap_dev" ]] && grep -q "^${swap_dev} " /proc/swaps; then
|
||||
if ! swapoff /dev/mapper/pve-swap; then
|
||||
log_error "关闭 Swap 失败(可能内存不足以容纳换出页),操作中止,未做任何修改"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_info "Swap 已处于关闭状态,跳过 swapoff"
|
||||
fi
|
||||
|
||||
log_info "正在修改启动配置..."
|
||||
backup_file "/etc/fstab"
|
||||
sed -i 's|^/dev/pve/swap|# /dev/pve/swap|g' /etc/fstab
|
||||
|
||||
log_info "正在删除 swap 分区..."
|
||||
lvremove -f /dev/pve/swap
|
||||
if ! lvremove -f /dev/pve/swap; then
|
||||
log_error "删除 swap 逻辑卷失败;Swap 已关闭且 fstab 已注释,可稍后重试或手动处理"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "正在扩展系统分区..."
|
||||
lvextend -l +100%FREE /dev/mapper/pve-root
|
||||
if ! lvextend -l +100%FREE /dev/mapper/pve-root; then
|
||||
log_error "扩容 pve-root 失败,请手动执行: lvextend -l +100%FREE /dev/mapper/pve-root"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "正在扩展文件系统..."
|
||||
resize2fs /dev/mapper/pve-root
|
||||
if ! storage_grow_root_fs /dev/mapper/pve-root; then
|
||||
log_error "LV 已扩容但文件系统扩展未完成,请按上方提示手动处理"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Swap 删除完成!系统空间更宽裕了"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,19 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
menu_tools_about() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "工具与关于"
|
||||
run_menu "工具与关于" menu_tools_about_render menu_tools_about_dispatch "0-5"
|
||||
}
|
||||
|
||||
menu_tools_about_render() {
|
||||
show_menu_option "1" "系统信息概览"
|
||||
show_menu_option "2" "应急救砖工具箱"
|
||||
show_menu_option "3" "本地脚本快捷更新"
|
||||
show_menu_option "4" "${RED}本地脚本快捷卸载${NC}"
|
||||
show_menu_option "5" "给作者点个 Star 吧"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-5]: " choice
|
||||
case $choice in
|
||||
}
|
||||
|
||||
menu_tools_about_dispatch() {
|
||||
case "$1" in
|
||||
1) show_system_info ;;
|
||||
2) show_menu_rescue ;;
|
||||
3) pve_tools_local_update ;;
|
||||
@@ -23,11 +24,9 @@ menu_tools_about() {
|
||||
echo -e "${YELLOW}项目地址:https://github.com/PVE-Tools/PVE-Tools-9${NC}"
|
||||
echo -e "${GREEN}您的支持是我更新的最大动力,谢谢喵~${NC}"
|
||||
;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# NOTE: show_menu_rescue() intentionally calls restore_proxmoxlib() (defined in
|
||||
@@ -36,34 +35,41 @@ menu_tools_about() {
|
||||
# dependency for the rescue/repair menu. Since all modules are sourced
|
||||
# globally, these symbols are always available. Moving to lib/ is the ideal
|
||||
# long-term approach but deferred.
|
||||
# 一键配置
|
||||
show_menu_rescue() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "应急救砖工具箱"
|
||||
echo -e "${RED}警告:本工具箱用于修复因误操作导致的系统问题,请谨慎使用!${NC}"
|
||||
run_menu "应急救砖工具箱" show_menu_rescue_render show_menu_rescue_dispatch "0-3"
|
||||
}
|
||||
|
||||
show_menu_rescue_render() {
|
||||
echo -e " ${RED}警告:本工具箱用于修复因误操作导致的系统问题,请谨慎使用!${NC}"
|
||||
echo
|
||||
show_menu_option "1" "恢复官方 Web UI 文件 (重装 pve-manager / proxmox-widget-toolkit)"
|
||||
show_menu_option "2" "恢复官方 pve-qemu-kvm (修复修改版 QEMU 问题)"
|
||||
show_menu_option "3" "清理驱动黑名单 (i915/snd_hda_intel)"
|
||||
show_menu_option "0" "返回主菜单"
|
||||
show_menu_footer
|
||||
read -p "请选择操作 [0-3]: " choice
|
||||
case $choice in
|
||||
}
|
||||
|
||||
show_menu_rescue_dispatch() {
|
||||
case "$1" in
|
||||
1) restore_proxmoxlib ;;
|
||||
2) restore_qemu_kvm ;;
|
||||
3)
|
||||
if confirm_high_risk_action \
|
||||
3) rescue_clear_driver_blacklist ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
rescue_clear_driver_blacklist() {
|
||||
if ! confirm_high_risk_action \
|
||||
"清理驱动黑名单" \
|
||||
"将修改 /etc/modprobe.d/pve-blacklist.conf 并运行 update-initramfs -u -k all。" \
|
||||
"错误还原可能导致显卡/声卡驱动冲突或系统无法启动。" \
|
||||
"请确认你需要移除黑名单中的显卡和声卡驱动限制。" \
|
||||
"CONFIRM"; then
|
||||
return 0
|
||||
fi
|
||||
log_info "正在清理黑名单配置..."
|
||||
if ! backup_file "/etc/modprobe.d/pve-blacklist.conf"; then
|
||||
log_error "无法备份 /etc/modprobe.d/pve-blacklist.conf,操作中止"
|
||||
pause_function
|
||||
break
|
||||
display_error "无法备份 /etc/modprobe.d/pve-blacklist.conf,操作中止"
|
||||
return 1
|
||||
fi
|
||||
sed -i '/blacklist i915/d' /etc/modprobe.d/pve-blacklist.conf
|
||||
sed -i '/blacklist snd_hda_intel/d' /etc/modprobe.d/pve-blacklist.conf
|
||||
@@ -71,13 +77,4 @@ show_menu_rescue() {
|
||||
log_info "正在更新 initramfs..."
|
||||
update-initramfs -u -k all
|
||||
log_success "黑名单清理完成,请重启系统"
|
||||
fi
|
||||
;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
}
|
||||
|
||||
# 二级菜单:系统优化
|
||||
|
||||
@@ -108,7 +108,7 @@ check_update() {
|
||||
pve_tools_local_update() {
|
||||
local current_script="${BASH_SOURCE[0]}"
|
||||
local resolved_script backup_dir backup_path tmp_script update_urls prefer_mirror version_url update_url script_url
|
||||
local remote_content remote_version detailed_changelog fallback_script_url
|
||||
local remote_content remote_version detailed_changelog fallback_script_url downloaded_version
|
||||
|
||||
if [[ -z "$current_script" || ! -f "$current_script" ]]; then
|
||||
display_error "无法定位当前脚本文件" "请使用本地文件方式运行脚本后再执行更新。"
|
||||
@@ -218,6 +218,12 @@ pve_tools_local_update() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# VERSION 文件跟随 main 分支,Release 资产可能尚未同步发版,两者不一致时提示但不阻断
|
||||
downloaded_version="$(grep -m1 '^CURRENT_VERSION=' "$tmp_script" | cut -d'"' -f2)"
|
||||
if [[ -n "$downloaded_version" && "$downloaded_version" != "$remote_version" ]]; then
|
||||
log_warn "下载脚本版本为 ${downloaded_version},与远程 VERSION (${remote_version}) 不一致,可能 Release 尚未同步发布。"
|
||||
fi
|
||||
|
||||
if ! cp -a "$tmp_script" "$resolved_script"; then
|
||||
cp -a "$backup_path" "$resolved_script" >/dev/null 2>&1 || true
|
||||
rm -f "$tmp_script"
|
||||
|
||||
@@ -673,10 +673,10 @@ security_cve_batch_mitigate() {
|
||||
# ============================================================
|
||||
|
||||
security_cve_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "CVE 漏洞修补中心"
|
||||
run_menu "CVE 漏洞修补中心" security_cve_menu_render security_cve_menu_dispatch "0-5"
|
||||
}
|
||||
|
||||
security_cve_menu_render() {
|
||||
echo -e "${CYAN}已收录漏洞:${NC}"
|
||||
echo " Januscape (CVE-2026-53359) KVM Guest-to-Host Escape"
|
||||
echo " Dirty Frag (CVE-2026-43284/43500) Page-Cache Write LPE"
|
||||
@@ -686,90 +686,78 @@ security_cve_menu() {
|
||||
show_menu_option "2" "Dirty Frag ${RED}(本地提权 — 检测/修补/恢复)${NC}"
|
||||
show_menu_option "3" "Copy Fail ${YELLOW}(本地提权 — 检测/修补/恢复)${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "8" "一键批量修补 ${RED}(同时应用全部缓解措施)${NC}"
|
||||
show_menu_option "9" "升级内核 ${GREEN}(永久修复所有 LPE 漏洞)${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回安全中心"
|
||||
show_menu_footer
|
||||
show_menu_option "4" "一键批量修补 ${RED}(同时应用全部缓解措施)${NC}"
|
||||
show_menu_option "5" "升级内核 ${GREEN}(永久修复所有 LPE 漏洞)${NC}"
|
||||
}
|
||||
|
||||
local choice
|
||||
read -rp "请选择操作 [0-9]: " choice
|
||||
case "$choice" in
|
||||
security_cve_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) security_cve_januscape_menu ;;
|
||||
2) security_cve_dirtyfrag_menu ;;
|
||||
3) security_cve_copyfail_menu ;;
|
||||
8) security_cve_batch_mitigate ;;
|
||||
9) security_cve_lpe_kernel_fix ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
4) security_cve_batch_mitigate ;;
|
||||
5) security_cve_lpe_kernel_fix ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Januscape 子菜单
|
||||
security_cve_januscape_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "Januscape (CVE-2026-53359) — KVM Guest-to-Host Escape"
|
||||
run_menu "Januscape (CVE-2026-53359) — KVM Guest-to-Host Escape" security_cve_januscape_menu_render security_cve_januscape_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
security_cve_januscape_menu_render() {
|
||||
echo " 影响: 2010-08 ~ 2026-06-19 | 修复: 81ccda30b4e8"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "1" "漏洞检测 ${CYAN}(嵌套虚拟化 + 内核版本)${NC}"
|
||||
show_menu_option "2" "临时修补 ${YELLOW}(禁用嵌套虚拟化,重启生效)${NC}"
|
||||
show_menu_option "3" "升级内核修补 ${GREEN}(安装已修复内核,推荐)${NC}"
|
||||
show_menu_option "4" "恢复嵌套虚拟化 ${RED}(确认内核已修复后使用)${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -rp "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
security_cve_januscape_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) security_cve_januscape_detect ;;
|
||||
2) security_cve_januscape_mitigate ;;
|
||||
3) security_cve_januscape_kernel_fix ;;
|
||||
4) security_cve_januscape_restore ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Dirty Frag 子菜单
|
||||
security_cve_dirtyfrag_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "Dirty Frag (CVE-2026-43284 + CVE-2026-43500) — LPE"
|
||||
run_menu "Dirty Frag (CVE-2026-43284 + CVE-2026-43500) — LPE" security_cve_dirtyfrag_menu_render security_cve_dirtyfrag_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
security_cve_dirtyfrag_menu_render() {
|
||||
echo " 影响: 2017-01 ~ 2026-05 | 修复: f4c50a4034e6 / aa54b1d27fe0"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "1" "漏洞检测 ${CYAN}(esp4/esp6/rxrpc 模块)${NC}"
|
||||
show_menu_option "2" "临时修补 ${YELLOW}(禁用 esp4/esp6/rxrpc,立即生效)${NC}"
|
||||
show_menu_option "3" "升级内核修补 ${GREEN}(安装已修复内核,推荐)${NC}"
|
||||
show_menu_option "4" "恢复模块黑名单 ${RED}(确认内核已修复后使用)${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -rp "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
security_cve_dirtyfrag_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) security_cve_dirtyfrag_detect ;;
|
||||
2) security_cve_dirtyfrag_mitigate ;;
|
||||
3) security_cve_lpe_kernel_fix ;;
|
||||
4) security_cve_lpe_restore ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Copy Fail 子菜单
|
||||
security_cve_copyfail_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "Copy Fail — Page-Cache Write LPE"
|
||||
run_menu "Copy Fail — Page-Cache Write LPE" security_cve_copyfail_menu_render security_cve_copyfail_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
security_cve_copyfail_menu_render() {
|
||||
echo " Dirty Pipe/Dirty Frag 同类漏洞 | 参考: https://copy.fail"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "1" "漏洞检测 ${CYAN}(algif_aead 模块)${NC}"
|
||||
@@ -778,20 +766,15 @@ security_cve_copyfail_menu() {
|
||||
show_menu_option "4" "恢复模块黑名单 ${RED}(确认内核已修复后使用)${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
echo -e "${RED}注意:Copy Fail 缓解可被 Dirty Frag 绕过,建议同时修补!${NC}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -rp "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
security_cve_copyfail_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) security_cve_copyfail_detect ;;
|
||||
2) security_cve_copyfail_mitigate ;;
|
||||
3) security_cve_lpe_kernel_fix ;;
|
||||
4) security_cve_lpe_restore ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -3,26 +3,21 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
security_center_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "安全中心"
|
||||
run_menu "安全中心" security_center_menu_render security_center_menu_dispatch "0-3"
|
||||
}
|
||||
|
||||
security_center_menu_render() {
|
||||
show_menu_option "1" "安全风险检查 ${CYAN}(只读报告)${NC}"
|
||||
show_menu_option "2" "SSH 一键加固 ${CYAN}(端口/密钥/fail2ban)${NC}"
|
||||
show_menu_option "3" "CVE 漏洞修补 ${RED}(Januscape/内核漏洞)${NC}"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-3]: " choice
|
||||
case "$choice" in
|
||||
security_center_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) security_risk_check ;;
|
||||
2) security_ssh_hardening ;;
|
||||
3) security_cve_menu ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# 温度监控管理菜单
|
||||
|
||||
21
src/modules/10-third-party/coolercontrol.sh
vendored
21
src/modules/10-third-party/coolercontrol.sh
vendored
@@ -208,9 +208,10 @@ coolercontrol_uninstall() {
|
||||
fi
|
||||
}
|
||||
coolercontrol_manager_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "Cooler Control 管理器"
|
||||
run_menu "Cooler Control 管理器" coolercontrol_manager_menu_render coolercontrol_manager_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
coolercontrol_manager_menu_render() {
|
||||
echo -e "当前状态 : $(coolercontrol_detect_status)"
|
||||
echo -e "当前版本 : $(coolercontrol_detect_version)"
|
||||
echo "$UI_DIVIDER"
|
||||
@@ -223,19 +224,15 @@ coolercontrol_manager_menu() {
|
||||
echo "$UI_DIVIDER"
|
||||
echo "项目官网:$COOLERCONTROL_PROJECT_URL"
|
||||
echo "开源协议:GNU General Public License v3.0 or later."
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
coolercontrol_manager_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) coolercontrol_install ;;
|
||||
2) coolercontrol_update ;;
|
||||
3) coolercontrol_print_manual_install ;;
|
||||
4) coolercontrol_uninstall ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
23
src/modules/10-third-party/init.sh
vendored
23
src/modules/10-third-party/init.sh
vendored
@@ -3,28 +3,23 @@
|
||||
# Copyright (C) 2026 Ciriu Networks
|
||||
|
||||
third_party_tools_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "第三方工具"
|
||||
run_menu "第三方工具" third_party_tools_menu_render third_party_tools_menu_dispatch "0-4"
|
||||
}
|
||||
|
||||
third_party_tools_menu_render() {
|
||||
show_menu_option "1" "第三方软件市场 ${CYAN}(Modules)${NC}"
|
||||
show_menu_option "2" "CoolerControl ${CYAN}(更好的管理风扇控制工具)${NC}"
|
||||
show_menu_option "3" "Community Scripts ${CYAN}(社区脚本集合)${NC}"
|
||||
show_menu_option "4" "IT87 Driver ${CYAN}(ITE 芯片风扇/传感器驱动)${NC}"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -p "请选择操作 [0-4]: " choice
|
||||
case "$choice" in
|
||||
third_party_tools_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) third_party_market_menu ;;
|
||||
2) coolercontrol_manager_menu ;;
|
||||
3) third_party_community_scripts_info ;;
|
||||
4) it87_manager_menu ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# PVE8 to PVE9 升级功能
|
||||
|
||||
21
src/modules/10-third-party/it87-driver.sh
vendored
21
src/modules/10-third-party/it87-driver.sh
vendored
@@ -624,10 +624,10 @@ it87_uninstall() {
|
||||
# ---------- 主菜单 ----------
|
||||
|
||||
it87_manager_menu() {
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "IT87 Driver 管理器"
|
||||
run_menu "IT87 Driver 管理器" it87_manager_menu_render it87_manager_menu_dispatch "0-7"
|
||||
}
|
||||
|
||||
it87_manager_menu_render() {
|
||||
local status version chip
|
||||
status=$(it87_detect_status)
|
||||
version=$(it87_detect_version)
|
||||
@@ -654,13 +654,10 @@ it87_manager_menu() {
|
||||
echo " 安装后需要重启或 modprobe it87 加载驱动。"
|
||||
echo " 传感器数据通过 sensors 命令查看。"
|
||||
echo " 项目仓库: ${IT87_REPO_URL:-https://github.com/shauno8/it87.git}"
|
||||
echo "$UI_DIVIDER"
|
||||
show_menu_option "0" "返回"
|
||||
show_menu_footer
|
||||
}
|
||||
|
||||
local choice
|
||||
read -r -p "请选择操作 [0-7]: " choice
|
||||
case "$choice" in
|
||||
it87_manager_menu_dispatch() {
|
||||
case "$1" in
|
||||
1) it87_install_dkms ;;
|
||||
2) it87_install_direct ;;
|
||||
3) it87_force_id_menu ;;
|
||||
@@ -668,9 +665,7 @@ it87_manager_menu() {
|
||||
5) it87_disable_autostart ;;
|
||||
6) it87_show_sensors ;;
|
||||
7) it87_uninstall ;;
|
||||
0) return ;;
|
||||
*) log_error "无效选择" ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
pause_function
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
41
src/modules/10-third-party/marketplace.sh
vendored
41
src/modules/10-third-party/marketplace.sh
vendored
@@ -128,9 +128,11 @@ third_party_market_menu() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
while true; do
|
||||
clear
|
||||
show_menu_header "第三方软件市场 (Modules)"
|
||||
run_menu "第三方软件市场 (Modules)" third_party_market_menu_render third_party_market_menu_dispatch "0-${#valid_files[@]}"
|
||||
}
|
||||
|
||||
# 渲染脚本列表(valid_* 数组与 download_cmd 来自 third_party_market_menu 的调用栈局部变量)
|
||||
third_party_market_menu_render() {
|
||||
echo " 数据源: $THIRD_PARTY_MODULES_RAW_BASE_URL"
|
||||
echo " 共发现 ${#valid_files[@]} 个符合规范的脚本"
|
||||
echo "${UI_DIVIDER}"
|
||||
@@ -143,22 +145,20 @@ third_party_market_menu() {
|
||||
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
|
||||
third_party_market_menu_dispatch() {
|
||||
local choice="$1"
|
||||
if [[ ! "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#valid_files[@]} )); then
|
||||
log_error "无效选择"
|
||||
pause_function
|
||||
continue
|
||||
return 1
|
||||
fi
|
||||
third_party_market_run_script "$((choice - 1))"
|
||||
return 0
|
||||
}
|
||||
|
||||
local selected_idx=$((choice - 1))
|
||||
# 确认、下载并执行选中的第三方脚本(入参为 valid_* 数组下标)
|
||||
third_party_market_run_script() {
|
||||
local selected_idx="$1"
|
||||
local selected_file="${valid_files[$selected_idx]}"
|
||||
local selected_name="${valid_names[$selected_idx]}"
|
||||
local selected_author="${valid_authors[$selected_idx]}"
|
||||
@@ -195,15 +195,13 @@ third_party_market_menu() {
|
||||
"建议先前往脚本仓库审计源码,备份关键配置,并保留控制台访问。" \
|
||||
"RUN"; then
|
||||
log_info "已取消执行 $selected_name"
|
||||
pause_function
|
||||
continue
|
||||
return 0
|
||||
fi
|
||||
|
||||
local tmp_script
|
||||
if ! tmp_script=$(mktemp /tmp/pve-third-party-run.XXXXXX.sh); then
|
||||
log_error "无法创建临时脚本文件"
|
||||
pause_function
|
||||
continue
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "使用 $selected_preferred_label 下载脚本 ($selected_file)..."
|
||||
@@ -213,8 +211,7 @@ third_party_market_menu() {
|
||||
if ! "${download_cmd[@]}" "$tmp_script" "$selected_fallback_url"; then
|
||||
log_error "脚本下载失败: $selected_file"
|
||||
rm -f "$tmp_script"
|
||||
pause_function
|
||||
continue
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -230,8 +227,6 @@ third_party_market_menu() {
|
||||
else
|
||||
log_error "$selected_name 执行失败 (退出码: $run_status)"
|
||||
fi
|
||||
pause_function
|
||||
done
|
||||
}
|
||||
#---------FastPVE 虚拟机快速下载-----------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user