重构:整理项目结构,删除冗余文件并调整目录组织

This commit is contained in:
勿忘心安
2025-12-29 11:25:40 +00:00
parent 183181034c
commit 1820db2850
40 changed files with 1135 additions and 829 deletions

116
Shell/debian_zfs.sh Normal file
View File

@@ -0,0 +1,116 @@
#!/bin/bash
echo "========================================"
echo " ZFS 编译安装脚本 (Debian)"
echo " LXDAPI by Github-xkatld"
echo "========================================"
echo
LOG_DIR="/var/log/zfs_build_logs"
mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/zfs_build_$(date +%Y%m%d_%H%M%S).log"
exec &> >(tee -a "$LOG_FILE")
set -e
set -u
DEBIAN_VER=$(cat /etc/debian_version | cut -d. -f1)
ARCH=$(uname -m)
case "$DEBIAN_VER" in
11)
DEBIAN_NAME="Debian 11 (Bullseye)"
ZFS_VER="2.2.9"
;;
12)
DEBIAN_NAME="Debian 12 (Bookworm)"
ZFS_VER="2.3.5"
;;
13|trixie)
DEBIAN_NAME="Debian 13 (Trixie)"
ZFS_VER="2.3.5"
;;
*)
echo "错误: 不支持的 Debian 版本: $DEBIAN_VER"
exit 1
;;
esac
echo ">>> 本次操作的完整日志将保存在: ${LOG_FILE}"
echo ""
echo ">>> [1/5] 开始在 ${DEBIAN_NAME} ${ARCH} 上编译安装 OpenZFS 版本 ${ZFS_VER}"
echo ">>> 内核版本: $(uname -r)"
echo ""
echo ">>> [2/5] 正在安装编译依赖包..."
apt-get update
apt-get install -y \
build-essential \
autoconf \
automake \
libtool \
pkg-config \
linux-headers-$(uname -r) \
libtirpc-dev \
libblkid-dev \
uuid-dev \
zlib1g-dev \
libattr1-dev \
libacl1-dev \
libudev-dev \
libssl-dev \
libelf-dev \
python3 \
python3-dev \
python3-setuptools \
python3-cffi \
libffi-dev
echo ">>> 依赖包安装完成。"
echo ""
echo ">>> [3/5] 正在下载 OpenZFS v${ZFS_VER} 源码..."
WORKDIR=$(mktemp -d)
cd "${WORKDIR}"
curl -sL "https://github.com/openzfs/zfs/releases/download/zfs-${ZFS_VER}/zfs-${ZFS_VER}.tar.gz" -o "zfs-${ZFS_VER}.tar.gz"
tar -xzf "zfs-${ZFS_VER}.tar.gz"
cd "zfs-${ZFS_VER}"
echo ">>> 源码下载并解压至 ${PWD}"
echo ""
echo ">>> [4/5] 正在配置、编译、安装和注册 ZFS... (这可能需要较长时间)"
./autogen.sh
./configure
make -j$(nproc)
make install
depmod -a
ldconfig
modprobe zfs
echo ">>> ZFS 编译、安装和模块加载完成。"
echo ""
echo ">>> [5/5] 正在清理临时文件..."
cd /
rm -rf "${WORKDIR}"
echo ""
echo "=============================================================================="
echo " ZFS v${ZFS_VER} 已成功安装并加载!"
echo ""
echo " 您现在可以使用 'zpool' 和 'zfs' 命令了。"
echo " 运行 'zpool status' 来检查ZFS模块是否正常工作。"
echo "=============================================================================="
zpool status
exit 0

202
Shell/image_import.sh Normal file
View File

@@ -0,0 +1,202 @@
#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'
LXC="/snap/bin/lxc"
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
err() { echo -e "${RED}[ERROR]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
reading() {
read -rp "$(echo -e "${GREEN}[INPUT]${NC} $1")" "$2"
}
detect_arch() {
sys_arch=$(uname -m)
case $sys_arch in
x86_64)
ARCH="amd64"
;;
aarch64|arm64)
ARCH="arm64"
;;
*)
err "不支持的架构: $sys_arch"
exit 1
;;
esac
ok "系统架构: $ARCH"
}
IMAGES_BASE_URL="https://github.com/xkatld/lxdapi-web-server/releases/download/image"
declare -a IMAGE_LIST=(
"almalinux-8"
"almalinux-9"
"alpine-320"
"alpine-321"
"alpine-322"
"archlinux-latest"
"centos-9-Stream"
"debian-11"
"debian-12"
"debian-13"
"fedora-42"
"fedora-43"
"opensuse-156"
"opensuse-tumbleweed"
"rockylinux-8"
"rockylinux-9"
"ubuntu-2204"
"ubuntu-2404"
)
download_and_import() {
local image_name="$1"
local image_type="$2"
local image_url="${IMAGES_BASE_URL}/${image_name}-${ARCH}-${image_type}.tar.gz"
info "下载: ${image_name}-${ARCH}-${image_type}.tar.gz"
local temp_file=$(mktemp)
if wget -q --show-progress -O "$temp_file" "$image_url" 2>&1; then
info "导入到 LXD..."
local alias="${image_name}-${image_type}"
if $LXC image import "$temp_file" --alias "$alias" 2>/dev/null; then
ok "成功导入: $alias"
else
warn "导入失败: $alias"
fi
rm -f "$temp_file"
else
warn "下载失败: ${image_name}-${ARCH}-${image_type}"
rm -f "$temp_file"
fi
}
show_image_list() {
echo
echo "============================================================================================================"
echo " 1) almalinux-8 2) almalinux-9 3) alpine-320 4) alpine-321 5) alpine-322"
echo " 6) archlinux-latest 7) centos-9-Stream 8) debian-11 9) debian-12 10) debian-13"
echo "11) fedora-42 12) fedora-43 13) opensuse-156 14) opensuse-tumbleweed"
echo "15) rockylinux-8 16) rockylinux-9 17) ubuntu-2204 18) ubuntu-2404"
echo "============================================================================================================"
echo
}
menu_import() {
echo
info "=== 导入镜像 ==="
show_image_list
reading "输入编号,多个用逗号分隔,或 all 全部导入 [8,9,17,18]: " image_choices
image_choices=${image_choices:-"8,9,17,18"}
while true; do
reading "选择镜像类型 lxc/kvm [lxc]: " image_type
image_type=${image_type:-lxc}
if [[ "$image_type" =~ ^(lxc|kvm)$ ]]; then
break
else
warn "请输入 lxc 或 kvm"
fi
done
if [[ "$image_type" == "kvm" && "$ARCH" == "arm64" ]]; then
warn "KVM 镜像不支持 arm64 架构"
return
fi
if [[ "$image_choices" == "all" ]]; then
selected_images=("${IMAGE_LIST[@]}")
else
IFS=',' read -ra choices <<< "$image_choices"
selected_images=()
for choice in "${choices[@]}"; do
choice=$(echo "$choice" | xargs)
idx=$((choice - 1))
if [[ $idx -ge 0 && $idx -lt ${#IMAGE_LIST[@]} ]]; then
selected_images+=("${IMAGE_LIST[$idx]}")
fi
done
fi
if [[ ${#selected_images[@]} -eq 0 ]]; then
warn "未选择任何镜像"
return
fi
ok "已选择 ${#selected_images[@]} 个镜像 (${image_type})"
echo
current=0
for img in "${selected_images[@]}"; do
((current++))
echo "[$current/${#selected_images[@]}]"
download_and_import "$img" "$image_type"
echo
done
}
menu_list() {
echo
info "=== 已有镜像 ==="
$LXC image list
}
menu_delete() {
echo
info "=== 删除镜像 ==="
$LXC image list
echo
reading "输入要删除的镜像别名或指纹: " image_id
if [ -z "$image_id" ]; then
return
fi
warn "确认删除镜像 $image_id"
reading "确认?(y/n) [n]: " confirm
if [[ "$confirm" =~ ^[yY]$ ]]; then
if $LXC image delete "$image_id"; then
ok "镜像已删除"
else
err "删除失败"
fi
else
info "已取消"
fi
}
main_menu() {
while true; do
echo
echo "================================"
echo " LXD 镜像管理脚本"
echo " LXDAPI by Github-xkatld"
echo "================================"
echo "1. 导入镜像"
echo "2. 查看已有镜像"
echo "3. 删除镜像"
echo "0. 退出"
echo "================================"
reading "请选择 [0-3]: " choice
case "$choice" in
1) menu_import ;;
2) menu_list ;;
3) menu_delete ;;
0) ok "退出"; exit 0 ;;
*) warn "无效选择" ;;
esac
done
}
detect_arch
main_menu

327
Shell/lxd_install.sh Normal file
View File

@@ -0,0 +1,327 @@
#!/bin/bash
cd /root >/dev/null 2>&1
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
REGEX=("debian|astra" "ubuntu")
RELEASE=("Debian" "Ubuntu")
CMD=("$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" "$(lsb_release -sd 2>/dev/null)")
SYS="${CMD[0]}"
[[ -n $SYS ]] || exit 1
for ((int = 0; int < ${#REGEX[@]}; int++)); do
if [[ $(echo "$SYS" | tr '[:upper:]' '[:lower:]') =~ ${REGEX[int]} ]]; then
SYSTEM="${RELEASE[int]}"
[[ -n $SYSTEM ]] && break
fi
done
if [[ "$SYSTEM" != "Debian" && "$SYSTEM" != "Ubuntu" ]]; then
echo -e "${RED}[ERR]${NC} 此脚本仅支持 Debian 和 Ubuntu 系统"
exit 1
fi
if [[ "$SYSTEM" == "Debian" ]]; then
OS_VERSION=$(cat /etc/debian_version | cut -d. -f1)
elif [[ "$SYSTEM" == "Ubuntu" ]]; then
OS_VERSION=$(grep VERSION_ID /etc/os-release | cut -d'"' -f2 | cut -d. -f1)
fi
RECOMMENDED=false
if [[ "$SYSTEM" == "Debian" && ("$OS_VERSION" == "12" || "$OS_VERSION" == "13") ]]; then
RECOMMENDED=true
elif [[ "$SYSTEM" == "Ubuntu" && ("$OS_VERSION" == "24" || "$OS_VERSION" == "25") ]]; then
RECOMMENDED=true
fi
if [[ "$RECOMMENDED" != "true" ]]; then
echo -e "${YELLOW}[WARN]${NC} 当前系统: $SYSTEM $OS_VERSION"
echo -e "${YELLOW}[WARN]${NC} 推荐使用: Debian 12/13 或 Ubuntu 24/25"
read -rp "$(echo -e "${YELLOW}是否继续安装?(y/n) [n]${NC}")" confirm_install
confirm_install=${confirm_install:-n}
if [[ ! "$confirm_install" =~ ^[yY]$ ]]; then
echo -e "${RED}[ERR]${NC} 安装已取消"
exit 1
fi
fi
log() { echo -e "$1"; }
ok() { log "${GREEN}[OK]${NC} $1"; }
info() { log "${BLUE}[INFO]${NC} $1"; }
warn() { log "${YELLOW}[WARN]${NC} $1"; }
err() { log "${RED}[ERR]${NC} $1"; exit 1; }
reading() { read -rp "$(echo -e "${GREEN}$1${NC}")" "$2"; }
install_package() {
package_name=$1
if dpkg -l 2>/dev/null | grep -q "^ii.*$package_name"; then
ok "$package_name 已安装"
else
apt-get install -y $package_name >/dev/null 2>&1
if [ $? -ne 0 ]; then
apt-get install -y $package_name --fix-missing >/dev/null 2>&1
fi
if dpkg -l 2>/dev/null | grep -q "^ii.*$package_name"; then
ok "$package_name 已安装"
else
warn "$package_name 安装失败"
fi
fi
}
get_available_space() {
local available_space
available_space=$(df -BG / | awk 'NR==2 {gsub("G","",$4); print $4}')
echo "$available_space"
}
install_lxd() {
lxd_snap=$(dpkg -l | awk '/^[hi]i/{print $2}' | grep -ow snap)
lxd_snapd=$(dpkg -l | awk '/^[hi]i/{print $2}' | grep -ow snapd)
if [[ "$lxd_snap" =~ ^snap.* ]] && [[ "$lxd_snapd" =~ ^snapd.* ]]; then
ok "snap 已安装"
else
info "开始安装 snap..."
apt-get update >/dev/null 2>&1
install_package snapd
fi
snap_core=$(snap list core 2>/dev/null)
snap_lxd=$(snap list lxd 2>/dev/null)
if [[ "$snap_core" =~ core.* ]] && [[ "$snap_lxd" =~ lxd.* ]]; then
ok "LXD 已安装"
lxd_lxc_detect=$(lxc list 2>/dev/null)
if [[ "$lxd_lxc_detect" =~ "snap-update-ns failed with code1".* ]]; then
systemctl restart apparmor
snap restart lxd
else
ok "环境检测无问题"
fi
else
info "开始安装 LXD..."
snap install lxd --channel=latest/stable 2>/dev/null
if [[ $? -ne 0 ]]; then
snap remove lxd 2>/dev/null
snap install core 2>/dev/null
snap install lxd --channel=latest/stable 2>/dev/null
fi
snap alias lxd.lxc lxc 2>/dev/null
snap alias lxd.lxd lxd 2>/dev/null
if [ ! -f /etc/profile.d/snap.sh ]; then
echo 'export PATH=$PATH:/snap/bin' > /etc/profile.d/snap.sh
fi
export PATH=$PATH:/snap/bin
if ! command -v lxc >/dev/null 2>&1; then
err 'lxc 路径有问题,请检查 snap alias'
fi
ok "LXD 安装完成"
fi
if dpkg -l lxcfs 2>/dev/null | grep -q "^ii"; then
warn "检测到 deb 版 lxcfs正在移除..."
systemctl stop lxcfs 2>/dev/null || true
systemctl disable lxcfs 2>/dev/null || true
apt-get remove -y lxcfs >/dev/null 2>&1
ok "deb 版 lxcfs 已移除"
fi
lxd_version=$(lxd --version 2>/dev/null)
info "LXD 版本: $lxd_version"
if [[ ! "$lxd_version" =~ ^6\. ]]; then
warn "当前 LXD 版本 $lxd_version 不兼容,推荐使用 6.x 版本"
reading "是否继续?(y/n) [y]" version_confirm
version_confirm=${version_confirm:-y}
if [[ ! "$version_confirm" =~ ^[yY]$ ]]; then
err "已取消安装"
fi
else
ok "LXD 版本兼容"
fi
info "配置 LXD..."
snap set lxd lxcfs.flags="-l" 2>/dev/null
snap set lxd daemon.debug=false 2>/dev/null
snap restart lxd 2>/dev/null
sleep 3
ok "LXD 已配置"
}
init_lxd_network() {
if ! /snap/bin/lxc network show lxdbr0 &>/dev/null; then
info "创建默认网络 lxdbr0..."
/snap/bin/lxc network create lxdbr0
ok "网络 lxdbr0 创建成功"
else
ok "网络 lxdbr0 已存在"
fi
if ! /snap/bin/lxc profile device show default 2>/dev/null | grep -q "eth0"; then
info "配置 default profile 网络设备..."
/snap/bin/lxc profile device add default eth0 nic network=lxdbr0 name=eth0
ok "网络设备已添加到 default profile"
fi
}
setup_storage() {
info "配置存储池..."
if /snap/bin/lxc storage show default &>/dev/null; then
ok "存储池 default 已存在"
/snap/bin/lxc storage list
return 0
fi
available_space=$(get_available_space)
info "当前可用磁盘空间: ${available_space}GB"
while true; do
reading "请选择存储后端 zfs/btrfs/lvm [zfs]" storage_driver
storage_driver=${storage_driver:-zfs}
if [[ "$storage_driver" =~ ^(zfs|btrfs|lvm)$ ]]; then
break
else
warn "请输入 zfs、btrfs 或 lvm"
fi
done
case "$storage_driver" in
zfs)
if ! command -v zpool &>/dev/null; then
info "安装 ZFS..."
if [[ "$SYSTEM" == "Ubuntu" ]]; then
install_package zfsutils-linux
else
bash <(curl -sL https://raw.githubusercontent.com/xkatld/lxdapi-web-server/refs/heads/v2.1.0-vpsm.link/Shell/debian_zfs.sh)
fi
fi
info "配置 LXD 使用系统 ZFS..."
snap set lxd zfs.external=true
snap restart lxd
sleep 3
;;
btrfs)
install_package btrfs-progs
;;
lvm)
install_package lvm2
;;
esac
reading "请输入存储池大小(GB) [${available_space}]" pool_size
pool_size=${pool_size:-$available_space}
info "创建 default 存储池 (${storage_driver}, ${pool_size}GB)..."
/snap/bin/lxc storage create default ${storage_driver} size=${pool_size}GB
if [ $? -eq 0 ]; then
ok "存储池 default 创建成功"
if ! /snap/bin/lxc profile device show default 2>/dev/null | grep -q "root"; then
/snap/bin/lxc profile device add default root disk path=/ pool=default
ok "存储池已添加到 default profile"
fi
else
err "存储池创建失败"
fi
}
main() {
echo
echo "========================================"
echo " LXD 安装脚本"
echo " by Github-xkatld"
echo "========================================"
echo
echo "======== 步骤 1/5: 检测系统 ========"
info "系统: $SYSTEM $OS_VERSION"
if [[ "$RECOMMENDED" == "true" ]]; then
ok "系统版本符合推荐"
else
warn "建议使用 Debian 12/13 或 Ubuntu 24/25"
fi
if [[ "$SYSTEM" == "Debian" ]]; then
echo
warn "Debian 使用 ZFS 存储需要编译安装,耗时较长"
warn "如需使用 ZFS推荐使用 Ubuntu 系统"
reading "是否继续使用 Debian(y/n) [y]" debian_confirm
debian_confirm=${debian_confirm:-y}
if [[ ! "$debian_confirm" =~ ^[yY]$ ]]; then
info "已取消安装"
exit 0
fi
fi
echo
echo "======== 步骤 2/5: 安装 LXD ========"
reading "是否安装 LXD(y/n) [y]" step2_confirm
step2_confirm=${step2_confirm:-y}
if [[ "$step2_confirm" =~ ^[yY]$ ]]; then
install_lxd
ok "LXD 安装完成"
else
info "已跳过 LXD 安装"
fi
echo
echo "======== 步骤 3/5: 网络配置 ========"
reading "是否配置网络?(y/n) [y]" step3_confirm
step3_confirm=${step3_confirm:-y}
if [[ "$step3_confirm" =~ ^[yY]$ ]]; then
init_lxd_network
reading "是否开启 IPv4 分配分配NAT和独立IP需要开启 (y/n) [y]" ipv4_dhcp
ipv4_dhcp=${ipv4_dhcp:-y}
if [[ ! "$ipv4_dhcp" =~ ^[yY]$ ]]; then
/snap/bin/lxc network set lxdbr0 ipv4.dhcp false
ok "IPv4 分配已关闭"
else
ok "IPv4 分配已开启"
fi
reading "是否开启 IPv6 分配分配NAT和独立IP需要开启 (y/n) [y]" ipv6_dhcp
ipv6_dhcp=${ipv6_dhcp:-y}
if [[ ! "$ipv6_dhcp" =~ ^[yY]$ ]]; then
/snap/bin/lxc network set lxdbr0 ipv6.dhcp false
/snap/bin/lxc network set lxdbr0 ipv6.address none
ok "IPv6 分配已关闭"
else
ok "IPv6 分配已开启"
fi
ok "网络配置完成"
else
info "已跳过网络配置"
fi
echo
echo "======== 步骤 4/5: 存储配置 ========"
info "配置 default 存储池,首次安装推荐配置"
reading "是否配置存储池?(y/n) [y]" step4_confirm
step4_confirm=${step4_confirm:-y}
if [[ "$step4_confirm" =~ ^[yY]$ ]]; then
setup_storage
ok "存储配置完成"
else
info "已跳过存储配置"
fi
echo
echo "======== 步骤 5/5: 完成 ========"
echo
echo "========================================"
echo " LXD 安装完成"
echo "========================================"
echo
info "LXD 版本: $(lxd --version 2>/dev/null)"
echo
info "===== 网络配置 ====="
lxc network list 2>/dev/null || warn "无法获取网络列表"
echo
info "===== 存储配置 ====="
lxc storage list 2>/dev/null || warn "无法获取存储列表"
}
main

510
Shell/lxdapi_install.sh Normal file
View File

@@ -0,0 +1,510 @@
#!/bin/bash
cd /root >/dev/null 2>&1
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
REGEX=("debian|astra" "ubuntu")
RELEASE=("Debian" "Ubuntu")
CMD=("$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" "$(lsb_release -sd 2>/dev/null)")
SYS="${CMD[0]}"
[[ -n $SYS ]] || exit 1
for ((int = 0; int < ${#REGEX[@]}; int++)); do
if [[ $(echo "$SYS" | tr '[:upper:]' '[:lower:]') =~ ${REGEX[int]} ]]; then
SYSTEM="${RELEASE[int]}"
[[ -n $SYSTEM ]] && break
fi
done
if [[ "$SYSTEM" != "Debian" && "$SYSTEM" != "Ubuntu" ]]; then
echo -e "${RED}[ERR]${NC} 此脚本仅支持 Debian 和 Ubuntu 系统"
exit 1
fi
log() { echo -e "$1"; }
ok() { log "${GREEN}[OK]${NC} $1"; }
info() { log "${BLUE}[INFO]${NC} $1"; }
warn() { log "${YELLOW}[WARN]${NC} $1"; }
err() { log "${RED}[ERR]${NC} $1"; exit 1; }
reading() { read -rp "$(echo -e "${GREEN}$1${NC}")" "$2"; }
install_package() {
package_name=$1
if dpkg -l 2>/dev/null | grep -q "^ii.*$package_name"; then
ok "$package_name 已安装"
else
apt-get install -y $package_name >/dev/null 2>&1
if [ $? -ne 0 ]; then
apt-get install -y $package_name --fix-missing >/dev/null 2>&1
fi
if dpkg -l 2>/dev/null | grep -q "^ii.*$package_name"; then
ok "$package_name 已安装"
else
warn "$package_name 安装失败"
fi
fi
}
install_base_packages() {
info "更新软件包列表..."
apt-get update >/dev/null 2>&1
apt-get autoremove -y >/dev/null 2>&1
info "安装基础软件包..."
DEBIAN_FRONTEND=noninteractive apt-get install -y unzip e2fsprogs bc nftables fdisk parted iptables-persistent nginx >/dev/null 2>&1
ok "软件包安装完成"
systemctl enable nftables >/dev/null 2>&1
systemctl start nftables >/dev/null 2>&1
ok "nftables 已启动"
if command -v lxc &>/dev/null && lxc network show lxdbr0 &>/dev/null; then
lxc network set lxdbr0 ipv4.nat true 2>/dev/null
lxc network set lxdbr0 ipv6.nat true 2>/dev/null
ok "LXD NAT 规则已重建"
fi
systemctl enable nginx >/dev/null 2>&1
systemctl start nginx >/dev/null 2>&1
ok "nginx 已启动"
}
deploy_lxdapi() {
info "检测系统架构..."
sys_arch=$(uname -m)
case $sys_arch in
x86_64)
arch="amd64"
ok "检测到架构: x86_64"
;;
aarch64|arm64)
arch="arm64"
ok "检测到架构: $sys_arch"
;;
*)
err "不支持的架构: $sys_arch"
;;
esac
while true; do
reading "请选择下载源 github/gitee [github]" download_source
download_source=${download_source:-github}
if [[ "$download_source" =~ ^(github|gitee)$ ]]; then
break
else
warn "请输入 github 或 gitee"
fi
done
if [[ "$download_source" == "github" ]]; then
latest_tag=$(curl -s https://api.github.com/repos/xkatld/lxdapi-web-server/releases/latest | grep '"tag_name"' | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p')
base_url="https://github.com/xkatld/lxdapi-web-server/releases/download"
else
latest_tag=$(curl -s https://gitee.com/api/v5/repos/xkatld/lxdapi-web-server/releases/latest | grep '"tag_name"' | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p')
base_url="https://gitee.com/xkatld/lxdapi-web-server/releases/download"
fi
if [ -z "$latest_tag" ]; then
err "无法获取最新版本信息"
fi
info "最新版本: $latest_tag"
reading "请输入安装版本 [$latest_tag]" install_version
install_version=${install_version:-$latest_tag}
ok "安装版本: $install_version"
download_url="${base_url}/${install_version}/lxdapi-linux-${arch}.tar.gz"
info "下载 lxdapi..."
info "下载地址: $download_url"
temp_file=$(mktemp)
if wget -q --show-progress -O "$temp_file" "$download_url" 2>&1; then
ok "下载完成"
else
rm -f "$temp_file"
err "下载失败"
fi
info "解压到 /opt/lxdapi..."
mkdir -p /opt/lxdapi
tar -xzf "$temp_file" -C /opt/lxdapi --strip-components=1
rm -f "$temp_file"
}
configure_lxdapi() {
info "配置 lxdapi..."
config_file="/opt/lxdapi/configs/config.yaml"
if [ ! -f "$config_file" ]; then
err "配置文件不存在: $config_file"
fi
reading "请输入服务端口 [8443]" server_port
server_port=${server_port:-8443}
reading "请输入API密钥 [随机生成]" api_hash
if [ -z "$api_hash" ]; then
api_hash=$(openssl rand -hex 16)
ok "API密钥已生成: $api_hash"
fi
reading "请输入管理员用户名 [admin]" admin_user
admin_user=${admin_user:-admin}
reading "请输入管理员密码 [随机生成]" admin_pass
if [ -z "$admin_pass" ]; then
admin_pass=$(openssl rand -hex 8)
ok "管理员密码已生成: $admin_pass"
fi
session_secret=$(openssl rand -hex 16)
reading "请输入流量采集间隔秒数 [20]" traffic_interval
traffic_interval=${traffic_interval:-20}
reading "请输入流量批量更新数量 [10]" traffic_batch_size
traffic_batch_size=${traffic_batch_size:-10}
reading "请输入任务自动清理天数 [7]" auto_cleanup_days
auto_cleanup_days=${auto_cleanup_days:-7}
while true; do
reading "请选择任务队列后端 memory/redis [memory]" task_backend
task_backend=${task_backend:-memory}
if [[ "$task_backend" =~ ^(memory|redis)$ ]]; then
break
else
warn "请输入 memory 或 redis"
fi
done
if [[ "$task_backend" == "redis" ]]; then
while true; do
reading "使用本地安装还是远程配置local/remote [local]" redis_location
redis_location=${redis_location:-local}
if [[ "$redis_location" =~ ^(local|remote)$ ]]; then
break
else
warn "请输入 local 或 remote"
fi
done
if [[ "$redis_location" == "local" ]]; then
info "安装 Redis..."
apt-get install -y redis-server >/dev/null 2>&1
systemctl start redis-server
systemctl enable redis-server
redis_host="localhost"
redis_port="6379"
redis_password=""
redis_db="0"
ok "Redis 已安装"
else
reading "请输入 Redis 主机地址:" redis_host
reading "请输入 Redis 端口 [6379]" redis_port
redis_port=${redis_port:-6379}
reading "请输入 Redis 密码 [留空表示无密码]" redis_password
reading "请输入 Redis 数据库编号 [0]" redis_db
redis_db=${redis_db:-0}
fi
else
redis_host="localhost"
redis_port="6379"
redis_password=""
redis_db="0"
fi
while true; do
reading "请选择数据库类型 sqlite/mysql/postgres [sqlite]" db_type
db_type=${db_type:-sqlite}
if [[ "$db_type" =~ ^(sqlite|mysql|postgres)$ ]]; then
break
else
warn "请输入 sqlite、mysql 或 postgres"
fi
done
if [[ "$db_type" == "mysql" ]]; then
while true; do
reading "使用本地安装还是远程配置local/remote [local]" mysql_location
mysql_location=${mysql_location:-local}
if [[ "$mysql_location" =~ ^(local|remote)$ ]]; then
break
else
warn "请输入 local 或 remote"
fi
done
if [[ "$mysql_location" == "local" ]]; then
info "安装 MariaDB..."
apt-get install -y mariadb-server >/dev/null 2>&1
systemctl start mariadb
systemctl enable mariadb
mysql_host="localhost"
mysql_port="3306"
mysql_user="lxdapi"
mysql_password=$(openssl rand -hex 8)
mysql_database="lxdapi"
info "创建数据库和用户..."
mysql -u root << EOF
CREATE DATABASE IF NOT EXISTS ${mysql_database};
CREATE USER IF NOT EXISTS '${mysql_user}'@'localhost' IDENTIFIED BY '${mysql_password}';
GRANT ALL PRIVILEGES ON ${mysql_database}.* TO '${mysql_user}'@'localhost';
FLUSH PRIVILEGES;
EOF
ok "MariaDB 数据库已创建"
ok "用户: $mysql_user"
ok "密码: $mysql_password"
else
reading "请输入 MySQL 主机地址:" mysql_host
reading "请输入 MySQL 端口 [3306]" mysql_port
mysql_port=${mysql_port:-3306}
reading "请输入 MySQL 用户名:" mysql_user
reading "请输入 MySQL 密码:" mysql_password
reading "请输入 MySQL 数据库名:" mysql_database
fi
elif [[ "$db_type" == "postgres" ]]; then
while true; do
reading "使用本地安装还是远程配置local/remote [local]" postgres_location
postgres_location=${postgres_location:-local}
if [[ "$postgres_location" =~ ^(local|remote)$ ]]; then
break
else
warn "请输入 local 或 remote"
fi
done
if [[ "$postgres_location" == "local" ]]; then
info "安装 PostgreSQL..."
apt-get install -y postgresql >/dev/null 2>&1
systemctl start postgresql
systemctl enable postgresql
postgres_host="localhost"
postgres_port="5432"
postgres_user="lxdapi"
postgres_password=$(openssl rand -hex 8)
postgres_database="lxdapi"
postgres_sslmode="disable"
info "创建数据库和用户..."
sudo -u postgres psql << EOF
CREATE DATABASE ${postgres_database};
CREATE USER ${postgres_user} WITH PASSWORD '${postgres_password}';
GRANT ALL PRIVILEGES ON DATABASE ${postgres_database} TO ${postgres_user};
EOF
ok "PostgreSQL 数据库已创建"
ok "用户: $postgres_user"
ok "密码: $postgres_password"
else
reading "请输入 PostgreSQL 主机地址:" postgres_host
reading "请输入 PostgreSQL 端口 [5432]" postgres_port
postgres_port=${postgres_port:-5432}
reading "请输入 PostgreSQL 用户名:" postgres_user
reading "请输入 PostgreSQL 密码:" postgres_password
reading "请输入 PostgreSQL 数据库名:" postgres_database
reading "请输入 PostgreSQL SSL模式 [disable]" postgres_sslmode
postgres_sslmode=${postgres_sslmode:-disable}
fi
fi
info "写入配置文件..."
sed -i "s|__SERVER_PORT__|$server_port|g" "$config_file"
sed -i "s|__API_HASH__|$api_hash|g" "$config_file"
sed -i "s|__ADMIN_USER__|$admin_user|g" "$config_file"
sed -i "s|__ADMIN_PASS__|$admin_pass|g" "$config_file"
sed -i "s|__SESSION_SECRET__|$session_secret|g" "$config_file"
sed -i "s|__TRAFFIC_INTERVAL__|$traffic_interval|g" "$config_file"
sed -i "s|__TRAFFIC_BATCH_SIZE__|$traffic_batch_size|g" "$config_file"
sed -i "s|__AUTO_CLEANUP_DAYS__|$auto_cleanup_days|g" "$config_file"
sed -i "s|__TASK_BACKEND__|$task_backend|g" "$config_file"
sed -i "s|__REDIS_HOST__|$redis_host|g" "$config_file"
sed -i "s|__REDIS_PORT__|$redis_port|g" "$config_file"
sed -i "s|__REDIS_PASSWORD__|$redis_password|g" "$config_file"
sed -i "s|__REDIS_DB__|$redis_db|g" "$config_file"
sed -i "s|__DB_TYPE__|$db_type|g" "$config_file"
if [[ "$db_type" == "mysql" ]]; then
sed -i "s|__MYSQL_HOST__|$mysql_host|g" "$config_file"
sed -i "s|__MYSQL_PORT__|$mysql_port|g" "$config_file"
sed -i "s|__MYSQL_USER__|$mysql_user|g" "$config_file"
sed -i "s|__MYSQL_PASSWORD__|$mysql_password|g" "$config_file"
sed -i "s|__MYSQL_DATABASE__|$mysql_database|g" "$config_file"
else
sed -i "s|__MYSQL_HOST__|localhost|g" "$config_file"
sed -i "s|__MYSQL_PORT__|3306|g" "$config_file"
sed -i "s|__MYSQL_USER__|root|g" "$config_file"
sed -i "s|__MYSQL_PASSWORD__||g" "$config_file"
sed -i "s|__MYSQL_DATABASE__|lxdapi|g" "$config_file"
fi
if [[ "$db_type" == "postgres" ]]; then
sed -i "s|__POSTGRES_HOST__|$postgres_host|g" "$config_file"
sed -i "s|__POSTGRES_PORT__|$postgres_port|g" "$config_file"
sed -i "s|__POSTGRES_USER__|$postgres_user|g" "$config_file"
sed -i "s|__POSTGRES_PASSWORD__|$postgres_password|g" "$config_file"
sed -i "s|__POSTGRES_DATABASE__|$postgres_database|g" "$config_file"
sed -i "s|__POSTGRES_SSLMODE__|$postgres_sslmode|g" "$config_file"
else
sed -i "s|__POSTGRES_HOST__|localhost|g" "$config_file"
sed -i "s|__POSTGRES_PORT__|5432|g" "$config_file"
sed -i "s|__POSTGRES_USER__|postgres|g" "$config_file"
sed -i "s|__POSTGRES_PASSWORD__||g" "$config_file"
sed -i "s|__POSTGRES_DATABASE__|lxdapi|g" "$config_file"
sed -i "s|__POSTGRES_SSLMODE__|disable|g" "$config_file"
fi
ok "配置文件已更新"
}
setup_lxdapi_service() {
info "配置 lxdapi 系统服务..."
config_file="/opt/lxdapi/configs/config.yaml"
if [ ! -f "$config_file" ]; then
err "配置文件不存在: $config_file"
fi
if grep -q "__SERVER_PORT__" "$config_file"; then
err "配置文件未完成配置"
fi
sys_arch=$(uname -m)
case $sys_arch in
x86_64)
exec_bin="/opt/lxdapi/lxdapi-amd64"
;;
aarch64|arm64)
exec_bin="/opt/lxdapi/lxdapi-arm64"
;;
*)
err "不支持的架构: $sys_arch"
;;
esac
service_file="/etc/systemd/system/lxdapi.service"
cat > "$service_file" << EOF
[Unit]
Description=LXD API Server
After=network.target lxd.service
Wants=lxd.service
[Service]
Type=simple
User=root
WorkingDirectory=/opt/lxdapi
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
ExecStart=$exec_bin
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
ok "服务文件已创建: $service_file"
systemctl daemon-reload
systemctl enable lxdapi
systemctl start lxdapi
info "等待服务启动..."
for i in {1..10}; do
printf "\r[%-10s] %d/10s" "$(printf '#%.0s' $(seq 1 $i))" "$i"
sleep 1
done
echo
if systemctl is-active --quiet lxdapi; then
ok "lxdapi 服务已启动"
else
warn "lxdapi 服务启动失败"
journalctl -u lxdapi -n 20 --no-pager
fi
}
main() {
echo
echo "========================================"
echo " LXDAPI 安装脚本"
echo " by Github-xkatld"
echo "========================================"
echo
echo "======== 步骤 1/5: 基础软件包安装 ========"
reading "是否安装基础软件包?(y/n) [y]" step1_confirm
step1_confirm=${step1_confirm:-y}
if [[ "$step1_confirm" =~ ^[yY]$ ]]; then
install_base_packages
ok "基础软件包安装完成"
else
info "已跳过基础软件包安装"
fi
echo
echo "======== 步骤 2/5: 下载 ========"
reading "是否下载 lxdapi(y/n) [y]" step2_confirm
step2_confirm=${step2_confirm:-y}
if [[ "$step2_confirm" =~ ^[yY]$ ]]; then
deploy_lxdapi
ok "下载完成"
else
info "已跳过下载"
fi
echo
echo "======== 步骤 3/5: 配置 ========"
reading "是否配置 lxdapi(y/n) [y]" step3_confirm
step3_confirm=${step3_confirm:-y}
if [[ "$step3_confirm" =~ ^[yY]$ ]]; then
configure_lxdapi
ok "配置完成"
else
info "已跳过配置"
fi
echo
echo "======== 步骤 4/5: 启动服务 ========"
reading "是否启动 lxdapi 服务?(y/n) [y]" step4_confirm
step4_confirm=${step4_confirm:-y}
if [[ "$step4_confirm" =~ ^[yY]$ ]]; then
setup_lxdapi_service
ok "服务已启动"
else
info "已跳过服务启动"
fi
echo
echo "======== 步骤 5/5: 完成 ========"
echo
echo "========================================"
echo " LXDAPI 安装完成"
echo "========================================"
echo
info "服务端口: $server_port"
info "API密钥: $api_hash"
info "管理员用户: $admin_user"
info "管理员密码: $admin_pass"
info "数据库类型: $db_type"
info "任务队列: $task_backend"
info "流量采集间隔: ${traffic_interval}s"
echo
systemctl status lxdapi --no-pager | head -5
}
main

112
Shell/lxdapi_tool.sh Normal file
View File

@@ -0,0 +1,112 @@
#!/bin/bash
INSTALL_DIR="/opt/lxdapi"
SERVICE_NAME="lxdapi"
SCRIPT_PATH="/usr/local/bin/lxdapi"
GREEN='\033[0;32m'
NC='\033[0m'
if [ "$(realpath "$0")" != "$SCRIPT_PATH" ]; then
cp "$0" "$SCRIPT_PATH"
chmod +x "$SCRIPT_PATH"
echo "lxdapi 命令已安装"
echo "用法: lxdapi 或 lxdapi {start|stop|restart|status|config|machine-id}"
exit 0
fi
wait_progress() {
echo "等待服务响应..."
for i in {1..10}; do
printf "\r[%-10s] %d/10s" "$(printf '#%.0s' $(seq 1 $i))" "$i"
sleep 1
done
echo
}
do_start() {
systemctl start $SERVICE_NAME
echo "lxdapi 已启动"
wait_progress
systemctl status $SERVICE_NAME --no-pager | head -5
}
do_stop() {
systemctl stop $SERVICE_NAME
echo "lxdapi 已停止"
sleep 1
systemctl status $SERVICE_NAME --no-pager | head -5
}
do_restart() {
systemctl restart $SERVICE_NAME
echo "lxdapi 已重启"
wait_progress
systemctl status $SERVICE_NAME --no-pager | head -5
}
do_status() {
systemctl status $SERVICE_NAME --no-pager | head -5
echo
echo "===== 最近日志 ====="
journalctl -u $SERVICE_NAME -n 10 --no-pager
}
do_config() {
CONFIG_FILE="$INSTALL_DIR/configs/config.yaml"
PORT=$(grep "port:" "$CONFIG_FILE" | head -1 | awk '{print $2}')
API_KEY=$(grep "api_key:" "$CONFIG_FILE" | awk -F'"' '{print $2}')
echo "端口: $PORT"
echo "API密钥: $API_KEY"
}
do_machine_id() {
ARCH=$(uname -m)
case $ARCH in
x86_64) ARCH="amd64" ;;
aarch64|arm64) ARCH="arm64" ;;
esac
$INSTALL_DIR/lxdapi-$ARCH --machine-id
}
show_menu() {
echo
echo "================================"
echo " LXDAPI 管理工具"
echo "================================"
echo "1. 启动"
echo "2. 停止"
echo "3. 重启"
echo "4. 状态"
echo "5. 配置"
echo "6. 机器码"
echo "0. 退出"
echo "================================"
read -rp "$(echo -e "${GREEN}请选择 [0-6]: ${NC}")" choice
case "$choice" in
1) do_start ;;
2) do_stop ;;
3) do_restart ;;
4) do_status ;;
5) do_config ;;
6) do_machine_id ;;
0) exit 0 ;;
*) echo "无效选择" ;;
esac
show_menu
}
case "$1" in
start) do_start ;;
stop) do_stop ;;
restart) do_restart ;;
status) do_status ;;
config) do_config ;;
machine-id) do_machine_id ;;
"") show_menu ;;
*)
echo "用法: lxdapi 或 lxdapi {start|stop|restart|status|config|machine-id}"
exit 1
;;
esac

237
Shell/lxdapi_update.sh Normal file
View File

@@ -0,0 +1,237 @@
#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "$1"; }
ok() { log "${GREEN}[OK]${NC} $1"; }
info() { log "${BLUE}[INFO]${NC} $1"; }
warn() { log "${YELLOW}[WARN]${NC} $1"; }
err() { log "${RED}[ERR]${NC} $1"; exit 1; }
INSTALL_DIR="/opt/lxdapi"
CONFIG_FILE="$INSTALL_DIR/configs/config.yaml"
BACKUP_DIR="$INSTALL_DIR/backup"
SERVICE_NAME="lxdapi"
check_environment() {
info "检测运行环境..."
if [ ! -d "$INSTALL_DIR" ]; then
err "未检测到 lxdapi 安装目录: $INSTALL_DIR"
fi
ok "环境检测通过"
}
detect_arch() {
info "检测系统架构..."
sys_arch=$(uname -m)
case $sys_arch in
x86_64)
ARCH="amd64"
ok "检测到架构: x86_64"
;;
aarch64|arm64)
ARCH="arm64"
ok "检测到架构: $sys_arch"
;;
*)
err "不支持的架构: $sys_arch"
;;
esac
}
get_current_version() {
if [ -f "$INSTALL_DIR/lxdapi-$ARCH" ]; then
CURRENT_VERSION=$(stat -c %y "$INSTALL_DIR/lxdapi-$ARCH" 2>/dev/null | cut -d' ' -f1)
else
CURRENT_VERSION="未安装"
fi
info "当前版本: $CURRENT_VERSION"
}
get_latest_version() {
info "获取最新版本..."
LATEST_VERSION=$(curl -s https://api.github.com/repos/xkatld/lxdapi-web-server/releases/latest | grep '"tag_name"' | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p')
if [ -z "$LATEST_VERSION" ]; then
err "无法获取最新版本信息,请检查网络连接"
fi
info "最新版本: $LATEST_VERSION"
read -rp "$(echo -e "${GREEN}请输入更新版本 [$LATEST_VERSION]: ${NC}")" UPDATE_VERSION
UPDATE_VERSION=${UPDATE_VERSION:-$LATEST_VERSION}
ok "更新版本: $UPDATE_VERSION"
}
stop_service() {
info "停止 $SERVICE_NAME 服务..."
if systemctl is-active --quiet $SERVICE_NAME; then
systemctl stop $SERVICE_NAME
sleep 2
if systemctl is-active --quiet $SERVICE_NAME; then
err "无法停止 $SERVICE_NAME 服务"
fi
ok "服务已停止"
else
warn "服务未运行"
fi
}
backup_files() {
info "备份当前文件..."
BACKUP_TIME=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DIR/$BACKUP_TIME"
mkdir -p "$BACKUP_PATH"
cp "$CONFIG_FILE" "$BACKUP_PATH/config.yaml"
ok "配置文件已备份到: $BACKUP_PATH/config.yaml"
if [ -f "$INSTALL_DIR/lxdapi-$ARCH" ]; then
cp "$INSTALL_DIR/lxdapi-$ARCH" "$BACKUP_PATH/lxdapi-$ARCH"
ok "二进制文件已备份"
fi
if [ -d "$INSTALL_DIR/certs" ]; then
cp -r "$INSTALL_DIR/certs" "$BACKUP_PATH/certs"
ok "证书文件已备份"
fi
if [ -f "$INSTALL_DIR/lxdapi.db" ]; then
cp "$INSTALL_DIR/lxdapi.db" "$BACKUP_PATH/lxdapi.db"
ok "数据库文件已备份"
fi
}
download_latest() {
info "下载版本 $UPDATE_VERSION..."
DOWNLOAD_URL="https://github.com/xkatld/lxdapi-web-server/releases/download/${UPDATE_VERSION}/lxdapi-linux-${ARCH}.tar.gz"
info "下载地址: $DOWNLOAD_URL"
TEMP_FILE=$(mktemp)
TEMP_DIR=$(mktemp -d)
if wget -q --show-progress -O "$TEMP_FILE" "$DOWNLOAD_URL" 2>&1; then
ok "下载完成"
else
rm -f "$TEMP_FILE"
rm -rf "$TEMP_DIR"
err "下载失败"
fi
info "解压文件..."
tar -xzf "$TEMP_FILE" -C "$TEMP_DIR" --strip-components=1
if [ ! -f "$TEMP_DIR/lxdapi-$ARCH" ]; then
rm -f "$TEMP_FILE"
rm -rf "$TEMP_DIR"
err "解压后未找到可执行文件"
fi
ok "解压完成"
info "更新文件..."
cp "$TEMP_DIR/lxdapi-$ARCH" "$INSTALL_DIR/lxdapi-$ARCH"
chmod +x "$INSTALL_DIR/lxdapi-$ARCH"
rm -f "$TEMP_FILE"
rm -rf "$TEMP_DIR"
ok "文件更新完成"
}
start_service() {
info "启动 $SERVICE_NAME 服务..."
systemctl daemon-reload
systemctl start $SERVICE_NAME
info "等待服务启动..."
for i in {1..10}; do
printf "\r[%-10s] %d/10s" "$(printf '#%.0s' $(seq 1 $i))" "$i"
sleep 1
done
echo
if systemctl is-active --quiet $SERVICE_NAME; then
ok "服务已启动"
else
warn "服务启动失败,查看日志:"
journalctl -u $SERVICE_NAME -n 10 --no-pager
err "请检查配置或手动启动服务"
fi
}
show_result() {
echo
echo "========================================"
echo " lxdapi 更新完成"
echo "========================================"
echo
info "更新前版本: $CURRENT_VERSION"
info "更新后版本: $UPDATE_VERSION"
info "备份目录: $BACKUP_PATH"
echo
info "===== 服务状态 ====="
systemctl status $SERVICE_NAME --no-pager | head -10
}
rollback() {
warn "执行回滚..."
if [ -z "$BACKUP_PATH" ] || [ ! -d "$BACKUP_PATH" ]; then
err "无法回滚:备份目录不存在"
fi
if [ -f "$BACKUP_PATH/lxdapi-$ARCH" ]; then
cp "$BACKUP_PATH/lxdapi-$ARCH" "$INSTALL_DIR/lxdapi-$ARCH"
chmod +x "$INSTALL_DIR/lxdapi-$ARCH"
ok "已恢复二进制文件"
fi
systemctl start $SERVICE_NAME
ok "回滚完成"
}
main() {
echo
echo "========================================"
echo " LXDAPI 更新脚本"
echo " by Github-xkatld"
echo "========================================"
echo
check_environment
detect_arch
get_current_version
get_latest_version
echo
read -rp "$(echo -e "${GREEN}确认更新? (y/n) [y]: ${NC}")" confirm
confirm=${confirm:-y}
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
info "已取消更新"
exit 0
fi
echo
stop_service
backup_files
if download_latest; then
start_service
show_result
else
rollback
fi
}
main "$@"

333
Shell/storage_pool.sh Normal file
View File

@@ -0,0 +1,333 @@
#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
err() { echo -e "${RED}[ERROR]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
reading() {
read -rp "$(echo -e "${GREEN}[INPUT]${NC} $1")" "$2"
}
detect_system() {
if [ -f /etc/os-release ]; then
. /etc/os-release
SYSTEM="$ID"
else
SYSTEM="unknown"
fi
}
check_zfs() {
command -v zfs &>/dev/null && command -v zpool &>/dev/null
}
install_zfs() {
if check_zfs; then
ok "ZFS 已安装"
info "配置 LXD 使用系统 ZFS..."
snap set lxd zfs.external=true
snap restart lxd
sleep 3
return 0
fi
detect_system
if [[ "$SYSTEM" == "debian" ]]; then
warn "Debian 系统需要编译安装 ZFS预计耗时 10-30 分钟"
reading "是否继续?(y/n) [n]: " confirm
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
return 1
fi
info "开始编译安装 ZFS..."
bash <(curl -sL https://raw.githubusercontent.com/xkatld/lxdapi-web-server/refs/heads/v2.1.0-vpsm.link/Shell/debian_zfs.sh) || return 1
else
info "安装 ZFS..."
apt-get update -qq && apt-get install -y zfsutils-linux -qq
fi
info "配置 LXD 使用系统 ZFS..."
snap set lxd zfs.external=true
snap restart lxd
sleep 3
ok "ZFS 安装完成"
return 0
}
check_root() {
if [ "$EUID" -ne 0 ]; then
err "请使用 root 用户运行此脚本"
exit 1
fi
}
check_lxd() {
if ! command -v lxc &>/dev/null; then
err "未检测到 LXD请先安装 LXD"
exit 1
fi
}
get_available_space() {
df -BG / | awk 'NR==2 {gsub("G","",$4); print $4}'
}
get_available_pool_name() {
local i=1
while lxc storage show "pool${i}" &>/dev/null; do
((i++))
done
echo "pool${i}"
}
install_package() {
local pkg="$1"
if ! dpkg -l | grep -q "^ii $pkg "; then
info "安装 $pkg..."
apt-get update -qq && apt-get install -y "$pkg" -qq
fi
}
list_disks() {
info "可用块设备 (>10GB)"
echo
lsblk -d -n -o NAME,SIZE,TYPE | while read name size type; do
if [[ "$type" == "disk" ]]; then
size_num=$(echo "$size" | sed 's/[^0-9.]//g')
size_unit=$(echo "$size" | sed 's/[0-9.]//g')
case "$size_unit" in
T) size_gb=$(echo "$size_num * 1024" | bc 2>/dev/null || echo "1000") ;;
G) size_gb=$size_num ;;
*) size_gb=0 ;;
esac
if (( $(echo "$size_gb > 10" | bc -l 2>/dev/null || echo 0) )); then
echo " /dev/$name ($size)"
fi
fi
done
echo
}
create_native_auto() {
local backend="$1"
local pool_name="$2"
local size_gb="$3"
case "$backend" in
zfs) install_zfs || return 1 ;;
btrfs) install_package btrfs-progs ;;
lvm) install_package lvm2 ;;
esac
ok "创建 $backend 存储池..."
if lxc storage create "$pool_name" "$backend" size="${size_gb}GiB"; then
ok "$backend 存储池 $pool_name 创建成功"
return 0
else
err "创建失败"
return 1
fi
}
create_zfs_disk() {
local device="$1"
local pool_name="$2"
local zpool_name="${pool_name}_zpool"
[ ! -b "$device" ] && { err "设备 $device 不存在"; return 1; }
install_zfs || return 1
ok "创建 ZFS 池..."
zpool create -f "$zpool_name" "$device" || { err "创建 ZFS 池失败"; return 1; }
ok "创建 LXD 存储池..."
if lxc storage create "$pool_name" zfs source="$zpool_name"; then
ok "ZFS 存储池 $pool_name 创建成功"
return 0
else
zpool destroy "$zpool_name"
return 1
fi
}
create_btrfs_disk() {
local device="$1"
local pool_name="$2"
[ ! -b "$device" ] && { err "设备 $device 不存在"; return 1; }
install_package btrfs-progs
ok "格式化为 Btrfs..."
mkfs.btrfs -f "$device" || { err "格式化失败"; return 1; }
if lxc storage create "$pool_name" btrfs source="$device"; then
ok "Btrfs 存储池 $pool_name 创建成功"
return 0
fi
return 1
}
create_lvm_disk() {
local device="$1"
local pool_name="$2"
[ ! -b "$device" ] && { err "设备 $device 不存在"; return 1; }
install_package lvm2
if lxc storage create "$pool_name" lvm source="$device"; then
ok "LVM 存储池 $pool_name 创建成功"
return 0
fi
return 1
}
create_dir_pool() {
local dir_path="$1"
local pool_name="$2"
mkdir -p "$dir_path"
if lxc storage create "$pool_name" dir source="$dir_path"; then
ok "目录存储池 $pool_name 创建成功"
return 0
fi
return 1
}
delete_storage_pool() {
local pool_name="$1"
lxc storage show "$pool_name" &>/dev/null || { err "存储池 $pool_name 不存在"; return 1; }
if lxc storage delete "$pool_name"; then
ok "存储池 $pool_name 已删除"
return 0
fi
return 1
}
menu_native() {
echo
info "=== Loop 设备 (自动创建) ==="
echo "1. ZFS"
echo "2. Btrfs"
echo "3. LVM"
echo "4. Dir (目录)"
echo "0. 返回"
echo
reading "请选择 [0-4]: " choice
local default_pool=$(get_available_pool_name)
local default_size=$(get_available_space)
case "$choice" in
1|2|3)
info "当前可用磁盘空间: ${default_size}GB"
reading "存储池名称 [$default_pool]: " pool_name
pool_name=${pool_name:-$default_pool}
reading "存储大小 GB [$default_size]: " size_gb
size_gb=${size_gb:-$default_size}
case "$choice" in
1) create_native_auto "zfs" "$pool_name" "$size_gb" ;;
2) create_native_auto "btrfs" "$pool_name" "$size_gb" ;;
3) create_native_auto "lvm" "$pool_name" "$size_gb" ;;
esac
;;
4)
reading "存储池名称 [$default_pool]: " pool_name
pool_name=${pool_name:-$default_pool}
reading "目录路径 [/opt/lxd-dir]: " dir_path
dir_path=${dir_path:-/opt/lxd-dir}
create_dir_pool "$dir_path" "$pool_name"
;;
0) return ;;
*) warn "无效选择" ;;
esac
}
menu_disk() {
echo
info "=== 块设备 (磁盘/分区) ==="
list_disks
echo "1. ZFS"
echo "2. Btrfs"
echo "3. LVM"
echo "0. 返回"
echo
reading "请选择 [0-3]: " choice
local default_pool=$(get_available_pool_name)
case "$choice" in
1|2|3)
reading "存储池名称 [$default_pool]: " pool_name
pool_name=${pool_name:-$default_pool}
reading "设备路径: " device
[ -z "$device" ] && { warn "设备路径不能为空"; return; }
warn "将使用 $device 创建存储池,数据将被清除!"
reading "确认继续?(y/n) [n]: " confirm
[[ ! "$confirm" =~ ^[yY]$ ]] && { info "已取消"; return; }
case "$choice" in
1) create_zfs_disk "$device" "$pool_name" ;;
2) create_btrfs_disk "$device" "$pool_name" ;;
3) create_lvm_disk "$device" "$pool_name" ;;
esac
;;
0) return ;;
*) warn "无效选择" ;;
esac
}
menu_delete() {
echo
info "=== 删除存储池 ==="
lxc storage list
echo
reading "输入要删除的存储池名称: " pool_name
[ -z "$pool_name" ] && return
warn "确认删除存储池 $pool_name"
reading "确认?(y/n) [n]: " confirm
[[ "$confirm" =~ ^[yY]$ ]] && delete_storage_pool "$pool_name" || info "已取消"
}
main_menu() {
while true; do
echo
echo "================================"
echo " LXD 存储池管理脚本"
echo " LXDAPI by Github-xkatld"
echo "================================"
echo "1. Loop 设备 (自动创建)"
echo "2. 块设备 (磁盘/分区)"
echo "3. 查看存储池"
echo "4. 删除存储池"
echo "0. 退出"
echo "================================"
reading "请选择 [0-4]: " choice
case "$choice" in
1) menu_native ;;
2) menu_disk ;;
3) echo; lxc storage list ;;
4) menu_delete ;;
0) ok "退出"; exit 0 ;;
*) warn "无效选择" ;;
esac
done
}
check_root
check_lxd
install_package bc
main_menu