嗯,同样豆包写的
用于实现EVE-NG中单个、多个节点或整个LAB(LAB内所有快照)的内置快照。
本质是qemu-img snapshot -c命令实现,并且是基于tmp中qcow2做的,所以擦除镜像后快照也没了

#!/bin/bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
WHITE='\033[1;37m'
NC='\033[0m'
# 默认参数
USER_ID="0"
LAB_UUID=""
BASE_LAB_PATH="/opt/unetlab/tmp"
# 日志函数
log_info() {
echo -e "${WHITE}[$(date '+%Y-%m-%d %H:%M')] [信息] $1${NC}"
}
log_warn() {
echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M')] [警告] $1${NC}"
}
log_error() {
echo -e "${RED}[$(date '+%Y-%m-%d %H:%M')] [错误] $1${NC}"
}
log_success() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M')] [成功] $1${NC}"
}
# 显示用法
show_usage() {
echo "用法: $0 -i 用户ID -u 实验UUID"
echo "示例: $0 -i 0 -u 19bf7a61-dfa1-4800-99d3-fc10da453fe4"
echo "参数说明:"
echo " -i 用户ID (admin默认是0)"
echo " -u 实验UUID"
exit 1
}
# 解析命令行参数
parse_arguments() {
while getopts "i:u:h" opt; do
case $opt in
i)
USER_ID="$OPTARG"
;;
u)
LAB_UUID="$OPTARG"
;;
h)
show_usage
;;
\?)
log_error "无效参数: -$OPTARG"
show_usage
;;
esac
done
# 验证必需参数
if [ -z "$USER_ID" ] || [ -z "$LAB_UUID" ]; then
log_error "缺少必需参数"
show_usage
fi
# 更新基础路径
BASE_LAB_PATH="/opt/unetlab/tmp/$USER_ID"
}
# 检查路径是否存在
check_path_exists() {
if [ ! -d "$1" ]; then
return 1
fi
return 0
}
# 验证UUID是否存在
validate_uuid() {
local uuid="$1"
local lab_path="${BASE_LAB_PATH}/${uuid}"
if ! check_path_exists "$lab_path"; then
log_error "实验UUID不存在: $uuid"
return 1
fi
return 0
}
# 查找实验室UNL文件
find_lab_unl_file() {
local target_uuid="$1"
local labs_path="/opt/unetlab/labs"
local found_file=""
# 使用while循环处理find输出,处理特殊字符
while IFS= read -r -d '' unl_file; do
# 检查文件是否可读
if [ ! -r "$unl_file" ]; then
continue
fi
# 使用临时变量存储结果
local lab_line
lab_line=$(grep -m1 '<lab' "$unl_file" 2>/dev/null)
if [ -n "$lab_line" ]; then
local lab_uuid
# 使用awk提取UUID
lab_uuid=$(echo "$lab_line" | awk -F'id="' '{print $2}' | awk -F'"' '{print $1}')
if [ "$lab_uuid" = "$target_uuid" ]; then
found_file="$unl_file"
break
fi
fi
done < <(find "$labs_path" -name "*.unl" -type f -print0 2>/dev/null)
if [ -n "$found_file" ]; then
echo "$found_file"
return 0
else
return 1
fi
}
# 获取实验室名称
get_lab_name() {
local unl_file="$1"
if [ -r "$unl_file" ]; then
grep -m1 '<lab' "$unl_file" 2>/dev/null | sed -n 's/.*name="\([^"]*\).*/\1/p'
fi
}
# 获取节点信息
get_node_info() {
local unl_file="$1"
if [ -r "$unl_file" ]; then
grep '<node' "$unl_file" 2>/dev/null | awk -F' ' '
{
id = name = type = uuid = ""
for(i=1; i<=NF; i++) {
if($i ~ /^id=/) {gsub(/^id="|"$/, "", $i); id = $i}
if($i ~ /^name=/) {gsub(/^name="|"$/, "", $i); name = $i}
if($i ~ /^type=/) {gsub(/^type="|"$/, "", $i); type = $i}
if($i ~ /^uuid=/) {gsub(/^uuid="|"$/, "", $i); uuid = $i}
}
if(id != "" && name != "") {
print id "|" name "|" type "|" uuid
}
}'
fi
}
# 检查节点是否运行中
is_node_running() {
local node_uuid="$1"
if [ -n "$node_uuid" ]; then
if ps aux | grep -v grep | grep -q "$node_uuid"; then
return 0 # 运行中
fi
fi
return 1 # 未运行
}
# 检查节点是否可以执行快照操作
check_snapshot_allowed() {
local uuid="$1"
local node_id="$2"
local operation="$3" # create, restore, delete
# 获取节点UUID
local unl_file=$(find_lab_unl_file "$uuid")
if [ -n "$unl_file" ]; then
local node_info=$(get_node_info "$unl_file")
local node_line=$(echo "$node_info" | grep "^$node_id|")
if [ -n "$node_line" ]; then
local node_uuid=$(echo "$node_line" | cut -d'|' -f4)
if is_node_running "$node_uuid"; then
log_error "节点 $node_id 正在运行中,无法执行快照${operation}操作!请先关闭节点。"
return 1
fi
fi
fi
return 0
}
# 显示实验室和节点信息
show_lab_node_info() {
local uuid="$1"
log_info "正在获取实验室信息..."
# 查找UNL文件
local unl_file=$(find_lab_unl_file "$uuid")
if [ -z "$unl_file" ]; then
log_warn "未找到实验室UNL文件,仅显示基础节点信息"
# 显示实验目录下的节点ID
local lab_path="${BASE_LAB_PATH}/${uuid}"
if [ -d "$lab_path" ]; then
echo "基础节点信息(通过扫描实验目录):"
echo "======================================"
local node_dirs=$(find "$lab_path" -maxdepth 1 -type d -name "[0-9]*" | sort -V)
if [ -n "$node_dirs" ]; then
echo "节点ID:"
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
echo " $node_id"
done
else
echo "未找到任何节点"
fi
echo "======================================"
else
log_error "实验目录不存在: $lab_path"
fi
return 1
fi
local lab_name=$(get_lab_name "$unl_file")
log_info "实验室名称: $lab_name"
log_info "实验室UUID: $uuid"
echo "======================================"
# 获取节点信息
local node_info=$(get_node_info "$unl_file")
if [ -n "$node_info" ]; then
echo "节点列表:"
echo "--------------------------------------"
printf "%-8s %-20s %-12s %-10s\n" "节点ID" "节点名称" "节点类型" "状态"
echo "--------------------------------------"
while IFS='|' read -r node_id node_name node_type node_uuid; do
local status="${GREEN}关机${NC}"
if is_node_running "$node_uuid"; then
status="${RED}运行中${NC}"
fi
printf "%-8s %-20s %-12s %b\n" "$node_id" "$node_name" "$node_type" "$status"
done <<< "$node_info"
else
log_warn "未在UNL文件中找到节点信息"
fi
echo "======================================"
}
# 验证节点ID是否存在
validate_node_id() {
local uuid="$1"
local node_id="$2"
local node_path="${BASE_LAB_PATH}/${uuid}/${node_id}"
if ! check_path_exists "$node_path"; then
log_error "节点ID不存在: $node_id"
return 1
fi
# 检查节点目录中是否有qcow2文件
local qcow2_files=$(find "$node_path" -maxdepth 1 -name "*.qcow2" 2>/dev/null)
if [ -z "$qcow2_files" ]; then
log_error "节点 $node_id 中没有找到qcow2文件"
return 1
fi
return 0
}
# 检查bin文件并警告
check_bin_files() {
local uuid="$1"
local node_id="$2"
local node_path="${BASE_LAB_PATH}/${uuid}/${node_id}"
local bin_files=$(find "$node_path" -maxdepth 1 -name "*.bin" 2>/dev/null)
if [ -n "$bin_files" ]; then
log_warn "节点 $node_id 中发现bin文件,已忽略"
return 1
fi
}
# 获取节点中的所有qcow2文件
get_qcow2_files() {
local uuid="$1"
local node_id="$2"
local node_path="${BASE_LAB_PATH}/${uuid}/${node_id}"
find "$node_path" -maxdepth 1 -name "*.qcow2" 2>/dev/null
}
# 显示快照信息
show_snapshot_info() {
local uuid="$1"
local node_id="$2"
local node_path="${BASE_LAB_PATH}/${uuid}/${node_id}"
local qcow2_files=$(find "$node_path" -maxdepth 1 -name "*.qcow2" 2>/dev/null)
if [ -n "$qcow2_files" ]; then
echo "=== 节点 $node_id 快照信息 ==="
for qcow2_file in $qcow2_files; do
echo "文件: $(basename "$qcow2_file")"
qemu-img snapshot -l "$qcow2_file"
echo
done
fi
}
# 节点快照 - 创建
node_snapshot_create() {
local uuid="$LAB_UUID"
# 显示实验室和节点信息
show_lab_node_info "$uuid"
log_info "[节点快照-节点快照LOG] -> 请输入节点ID(多个则以,间隔)"
read -p "节点ID:" node_ids_input
# 处理节点ID输入,支持逗号分隔
IFS=',' read -ra node_ids <<< "$node_ids_input"
local valid_nodes=()
for node_id in "${node_ids[@]}"; do
node_id=$(echo "$node_id" | tr -d ' ') # 去除空格
if validate_node_id "$uuid" "$node_id"; then
# 检查节点是否运行中
if ! check_snapshot_allowed "$uuid" "$node_id" "创建"; then
continue
fi
check_bin_files "$uuid" "$node_id"
valid_nodes+=("$node_id")
fi
done
if [ ${#valid_nodes[@]} -eq 0 ]; then
log_error "没有有效的节点ID"
return 1
fi
log_info "[节点快照-节点快照LOG] -> 请输入快照名称"
read -p "快照名称:" snapshot_name
if [ -z "$snapshot_name" ]; then
log_error "快照名称不能为空"
return 1
fi
local success_count=0
local results=()
for node_id in "${valid_nodes[@]}"; do
local qcow2_files=$(get_qcow2_files "$uuid" "$node_id")
for qcow2_file in $qcow2_files; do
log_info "为节点 $node_id 创建快照: $qcow2_file"
# 创建快照
if qemu-img snapshot -c "$snapshot_name" "$qcow2_file" >/dev/null 2>&1; then
# 验证快照是否创建成功
if qemu-img snapshot -l "$qcow2_file" | grep -q "$snapshot_name"; then
((success_count++))
results+=("节点ID $node_id 快照 ---> $snapshot_name")
log_success "节点 $node_id 快照创建成功"
else
log_error "节点 $node_id 快照创建验证失败"
fi
else
log_error "节点 $node_id 快照创建失败"
fi
done
done
if [ $success_count -gt 0 ]; then
log_success "快照创建成功"
for result in "${results[@]}"; do
echo -e "${GREEN}$result${NC}"
done
else
log_error "没有成功创建任何快照"
fi
}
# 节点快照 - 查看信息
node_snapshot_list() {
local uuid="$LAB_UUID"
# 显示实验室和节点信息
show_lab_node_info "$uuid"
log_info "[节点快照-快照信息查看LOG] -> 请输入节点ID(多个则以,间隔)"
read -p "节点ID:" node_ids_input
IFS=',' read -ra node_ids <<< "$node_ids_input"
local valid_nodes=()
for node_id in "${node_ids[@]}"; do
node_id=$(echo "$node_id" | tr -d ' ')
if validate_node_id "$uuid" "$node_id"; then
valid_nodes+=("$node_id")
fi
done
if [ ${#valid_nodes[@]} -eq 0 ]; then
log_error "没有有效的节点ID"
return 1
fi
for node_id in "${valid_nodes[@]}"; do
show_snapshot_info "$uuid" "$node_id"
done
}
# 节点快照 - 恢复
node_snapshot_restore() {
local uuid="$LAB_UUID"
# 显示实验室和节点信息
show_lab_node_info "$uuid"
# 显示所有节点的快照信息
local lab_path="${BASE_LAB_PATH}/${uuid}"
local node_dirs=$(find "$lab_path" -maxdepth 1 -type d -name "[0-9]*" 2>/dev/null | sort -V)
if [ -z "$node_dirs" ]; then
log_error "实验中没有找到任何节点"
return 1
fi
echo "=== 实验所有节点快照信息 ==="
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
show_snapshot_info "$uuid" "$node_id"
done
log_info "[节点快照-恢复快照LOG] -> 请输入节点ID(多个则以,间隔)"
read -p "节点ID:" node_ids_input
IFS=',' read -ra node_ids <<< "$node_ids_input"
local valid_nodes=()
for node_id in "${node_ids[@]}"; do
node_id=$(echo "$node_id" | tr -d ' ')
if validate_node_id "$uuid" "$node_id"; then
# 检查节点是否运行中
if ! check_snapshot_allowed "$uuid" "$node_id" "恢复"; then
continue
fi
valid_nodes+=("$node_id")
fi
done
if [ ${#valid_nodes[@]} -eq 0 ]; then
log_error "没有有效的节点ID"
return 1
fi
log_info "[节点快照-恢复快照LOG] -> 请输入快照名称"
read -p "快照名称:" snapshot_name
if [ -z "$snapshot_name" ]; then
log_error "快照名称不能为空"
return 1
fi
local success_count=0
local results=()
for node_id in "${valid_nodes[@]}"; do
local qcow2_files=$(get_qcow2_files "$uuid" "$node_id")
for qcow2_file in $qcow2_files; do
log_info "恢复节点 $node_id 快照: $qcow2_file"
# 检查快照是否存在
if qemu-img snapshot -l "$qcow2_file" | grep -q "$snapshot_name"; then
# 恢复快照
if qemu-img snapshot -a "$snapshot_name" "$qcow2_file" >/dev/null 2>&1; then
((success_count++))
results+=("节点ID $node_id 恢复 ---> $snapshot_name")
log_success "节点 $node_id 快照恢复成功"
else
log_error "节点 $node_id 快照恢复失败"
fi
else
log_error "节点 $node_id 中未找到快照: $snapshot_name"
fi
done
done
if [ $success_count -gt 0 ]; then
log_success "快照恢复成功"
for result in "${results[@]}"; do
echo -e "${GREEN}$result${NC}"
done
else
log_error "没有成功恢复任何快照"
fi
}
# 节点快照 - 删除
node_snapshot_delete() {
local uuid="$LAB_UUID"
# 显示实验室和节点信息
show_lab_node_info "$uuid"
# 显示所有节点的快照信息
local lab_path="${BASE_LAB_PATH}/${uuid}"
local node_dirs=$(find "$lab_path" -maxdepth 1 -type d -name "[0-9]*" 2>/dev/null | sort -V)
if [ -z "$node_dirs" ]; then
log_error "实验中没有找到任何节点"
return 1
fi
echo "=== 实验中所有节点快照信息 ==="
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
show_snapshot_info "$uuid" "$node_id"
done
log_info "[节点快照-删除快照LOG] -> 请输入节点ID(多个则以,间隔)"
read -p "节点ID:" node_ids_input
IFS=',' read -ra node_ids <<< "$node_ids_input"
local valid_nodes=()
for node_id in "${node_ids[@]}"; do
node_id=$(echo "$node_id" | tr -d ' ')
if validate_node_id "$uuid" "$node_id"; then
# 检查节点是否运行中
if ! check_snapshot_allowed "$uuid" "$node_id" "删除"; then
continue
fi
valid_nodes+=("$node_id")
fi
done
if [ ${#valid_nodes[@]} -eq 0 ]; then
log_error "没有有效的节点ID"
return 1
fi
log_info "[节点快照-删除快照LOG] -> 请输入要删除的快照名称"
read -p "快照名称:" snapshot_name
if [ -z "$snapshot_name" ]; then
log_error "快照名称不能为空"
return 1
fi
local success_count=0
local results=()
for node_id in "${valid_nodes[@]}"; do
local qcow2_files=$(get_qcow2_files "$uuid" "$node_id")
for qcow2_file in $qcow2_files; do
log_info "删除节点 $node_id 快照: $qcow2_file"
# 检查快照是否存在
if qemu-img snapshot -l "$qcow2_file" | grep -q "$snapshot_name"; then
# 删除快照
if qemu-img snapshot -d "$snapshot_name" "$qcow2_file" >/dev/null 2>&1; then
((success_count++))
results+=("节点ID $node_id 删除 ---> $snapshot_name")
log_success "节点 $node_id 快照删除成功"
else
log_error "节点 $node_id 快照删除失败"
fi
else
log_error "节点 $node_id 中未找到快照: $snapshot_name"
fi
done
done
if [ $success_count -gt 0 ]; then
log_success "快照删除成功"
for result in "${results[@]}"; do
echo -e "${GREEN}$result${NC}"
done
else
log_error "没有成功删除任何快照"
fi
}
# 实验快照 - 创建
lab_snapshot_create() {
local uuid="$LAB_UUID"
# 显示实验室和节点信息
show_lab_node_info "$uuid"
log_info "[实验快照-实验快照LOG] -> 请输入快照名称"
read -p "快照名称:" snapshot_name
if [ -z "$snapshot_name" ]; then
log_error "快照名称不能为空"
return 1
fi
local lab_path="${BASE_LAB_PATH}/${uuid}"
local success_count=0
local skipped_count=0
local results=()
local skipped_nodes=()
# 获取实验中的所有节点目录
local node_dirs=$(find "$lab_path" -maxdepth 1 -type d -name "[0-9]*" 2>/dev/null | sort -V)
if [ -z "$node_dirs" ]; then
log_error "实验中没有找到任何节点"
return 1
fi
# 获取节点运行状态信息
local unl_file=$(find_lab_unl_file "$uuid")
local node_info=""
if [ -n "$unl_file" ]; then
node_info=$(get_node_info "$unl_file")
fi
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
# 检查节点是否运行中
local node_running=0
if [ -n "$node_info" ]; then
local node_line=$(echo "$node_info" | grep "^$node_id|")
if [ -n "$node_line" ]; then
local node_uuid=$(echo "$node_line" | cut -d'|' -f4)
if is_node_running "$node_uuid"; then
node_running=1
fi
fi
fi
if [ $node_running -eq 1 ]; then
log_warn "节点 $node_id 正在运行中,已跳过"
skipped_nodes+=("$node_id")
((skipped_count++))
continue
fi
# 直接检查节点路径和qcow2文件
local node_path="${BASE_LAB_PATH}/${uuid}/${node_id}"
local qcow2_files=$(find "$node_path" -maxdepth 1 -name "*.qcow2" 2>/dev/null)
if [ -n "$qcow2_files" ]; then
check_bin_files "$uuid" "$node_id"
for qcow2_file in $qcow2_files; do
log_info "为节点 $node_id 创建快照: $qcow2_file"
if qemu-img snapshot -c "$snapshot_name" "$qcow2_file" >/dev/null 2>&1; then
if qemu-img snapshot -l "$qcow2_file" | grep -q "$snapshot_name"; then
((success_count++))
results+=("节点ID $node_id 快照 ---> $snapshot_name")
log_success "节点 $node_id 快照创建成功"
else
log_error "节点 $node_id 快照创建验证失败"
fi
else
log_error "节点 $node_id 快照创建失败"
fi
done
else
log_warn "节点 $node_id 中没有找到qcow2文件,已跳过"
fi
done
# 显示操作结果摘要
echo "=== 操作结果摘要 ==="
if [ ${#skipped_nodes[@]} -gt 0 ]; then
log_warn "跳过的运行中节点ID: ${skipped_nodes[*]}"
fi
if [ $success_count -gt 0 ]; then
log_success "实验快照创建成功"
for result in "${results[@]}"; do
echo -e "${GREEN}$result${NC}"
done
else
if [ $skipped_count -eq ${#node_dirs[@]} ]; then
log_error "所有节点都在运行中,没有执行任何快照操作"
else
log_error "没有成功创建任何快照"
fi
fi
}
# 实验快照 - 查看信息
lab_snapshot_list() {
local uuid="$LAB_UUID"
# 显示实验室和节点信息
show_lab_node_info "$uuid"
local lab_path="${BASE_LAB_PATH}/${uuid}"
local node_dirs=$(find "$lab_path" -maxdepth 1 -type d -name "[0-9]*" 2>/dev/null | sort -V)
if [ -z "$node_dirs" ]; then
log_error "实验中没有找到任何节点"
return 1
fi
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
show_snapshot_info "$uuid" "$node_id"
done
}
# 实验快照 - 恢复
lab_snapshot_restore() {
local uuid="$LAB_UUID"
# 显示实验室和节点信息
show_lab_node_info "$uuid"
# 显示所有节点的快照信息
local lab_path="${BASE_LAB_PATH}/${uuid}"
local node_dirs=$(find "$lab_path" -maxdepth 1 -type d -name "[0-9]*" 2>/dev/null | sort -V)
if [ -z "$node_dirs" ]; then
log_error "实验中没有找到任何节点"
return 1
fi
echo "=== 实验所有节点快照信息 ==="
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
show_snapshot_info "$uuid" "$node_id"
done
log_info "[实验快照-恢复快照LOG] -> 请输入快照名称"
read -p "快照名称:" snapshot_name
if [ -z "$snapshot_name" ]; then
log_error "快照名称不能为空"
return 1
fi
local success_count=0
local skipped_count=0
local results=()
local skipped_nodes=()
# 获取节点运行状态信息
local unl_file=$(find_lab_unl_file "$uuid")
local node_info=""
if [ -n "$unl_file" ]; then
node_info=$(get_node_info "$unl_file")
fi
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
# 检查节点是否运行中
local node_running=0
if [ -n "$node_info" ]; then
local node_line=$(echo "$node_info" | grep "^$node_id|")
if [ -n "$node_line" ]; then
local node_uuid=$(echo "$node_line" | cut -d'|' -f4)
if is_node_running "$node_uuid"; then
node_running=1
fi
fi
fi
if [ $node_running -eq 1 ]; then
log_warn "节点 $node_id 正在运行中,已跳过"
skipped_nodes+=("$node_id")
((skipped_count++))
continue
fi
local node_path="${BASE_LAB_PATH}/${uuid}/${node_id}"
local qcow2_files=$(find "$node_path" -maxdepth 1 -name "*.qcow2" 2>/dev/null)
if [ -n "$qcow2_files" ]; then
for qcow2_file in $qcow2_files; do
log_info "恢复节点 $node_id 快照: $qcow2_file"
if qemu-img snapshot -l "$qcow2_file" | grep -q "$snapshot_name"; then
if qemu-img snapshot -a "$snapshot_name" "$qcow2_file" >/dev/null 2>&1; then
((success_count++))
results+=("节点ID $node_id 恢复 ---> $snapshot_name")
log_success "节点 $node_id 快照恢复成功"
else
log_error "节点 $node_id 快照恢复失败"
fi
else
log_error "节点 $node_id 中未找到快照: $snapshot_name"
fi
done
fi
done
# 显示操作结果摘要
echo "=== 操作结果摘要 ==="
if [ ${#skipped_nodes[@]} -gt 0 ]; then
log_warn "跳过的运行中节点ID: ${skipped_nodes[*]}"
fi
if [ $success_count -gt 0 ]; then
log_success "实验快照恢复成功"
for result in "${results[@]}"; do
echo -e "${GREEN}$result${NC}"
done
else
if [ $skipped_count -eq ${#node_dirs[@]} ]; then
log_error "所有节点都在运行中,没有执行任何快照恢复操作"
else
log_error "没有成功恢复任何快照"
fi
fi
}
# 实验快照 - 删除
lab_snapshot_delete() {
local uuid="$LAB_UUID"
# 显示实验室和节点信息
show_lab_node_info "$uuid"
# 显示所有节点的快照信息
local lab_path="${BASE_LAB_PATH}/${uuid}"
local node_dirs=$(find "$lab_path" -maxdepth 1 -type d -name "[0-9]*" 2>/dev/null | sort -V)
if [ -z "$node_dirs" ]; then
log_error "实验中没有找到任何节点"
return 1
fi
echo "=== 实验所有节点快照信息 ==="
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
show_snapshot_info "$uuid" "$node_id"
done
log_info "[实验快照-删除快照LOG] -> 请输入要删除的快照名称"
read -p "快照名称:" snapshot_name
if [ -z "$snapshot_name" ]; then
log_error "快照名称不能为空"
return 1
fi
local success_count=0
local skipped_count=0
local results=()
local skipped_nodes=()
# 获取节点运行状态信息
local unl_file=$(find_lab_unl_file "$uuid")
local node_info=""
if [ -n "$unl_file" ]; then
node_info=$(get_node_info "$unl_file")
fi
for node_dir in $node_dirs; do
local node_id=$(basename "$node_dir")
# 检查节点是否运行中
local node_running=0
if [ -n "$node_info" ]; then
local node_line=$(echo "$node_info" | grep "^$node_id|")
if [ -n "$node_line" ]; then
local node_uuid=$(echo "$node_line" | cut -d'|' -f4)
if is_node_running "$node_uuid"; then
node_running=1
fi
fi
fi
if [ $node_running -eq 1 ]; then
log_warn "节点 $node_id 正在运行中,已跳过"
skipped_nodes+=("$node_id")
((skipped_count++))
continue
fi
local node_path="${BASE_LAB_PATH}/${uuid}/${node_id}"
local qcow2_files=$(find "$node_path" -maxdepth 1 -name "*.qcow2" 2>/dev/null)
if [ -n "$qcow2_files" ]; then
for qcow2_file in $qcow2_files; do
log_info "删除节点 $node_id 快照: $qcow2_file"
# 检查快照是否存在
if qemu-img snapshot -l "$qcow2_file" | grep -q "$snapshot_name"; then
# 删除快照
if qemu-img snapshot -d "$snapshot_name" "$qcow2_file" >/dev/null 2>&1; then
((success_count++))
results+=("节点ID $node_id 删除 ---> $snapshot_name")
log_success "节点 $node_id 快照删除成功"
else
log_error "节点 $node_id 快照删除失败"
fi
else
log_error "节点 $node_id 中未找到快照: $snapshot_name"
fi
done
fi
done
# 显示操作结果摘要
echo "=== 操作结果摘要 ==="
if [ ${#skipped_nodes[@]} -gt 0 ]; then
log_warn "跳过的运行中节点ID: ${skipped_nodes[*]}"
fi
if [ $success_count -gt 0 ]; then
log_success "实验快照删除成功"
for result in "${results[@]}"; do
echo -e "${GREEN}$result${NC}"
done
else
if [ $skipped_count -eq ${#node_dirs[@]} ]; then
log_error "所有节点都在运行中,没有执行任何快照删除操作"
else
log_error "没有成功删除任何快照"
fi
fi
}
# 节点快照菜单
node_snapshot_menu() {
while true; do
echo "========== 节点快照 =========="
echo "[1] 节点快照"
echo "[2] 快照信息查看"
echo "[3] 恢复快照"
echo "[4] 删除快照"
echo "[0] 返回主菜单"
read -p "选择 [1-4]:" node_choice
case $node_choice in
1)
echo "节点快照......"
node_snapshot_create
;;
2)
echo "快照信息查看......"
node_snapshot_list
;;
3)
echo "恢复快照......"
node_snapshot_restore
;;
4)
echo "删除快照......"
node_snapshot_delete
;;
0)
break
;;
*)
log_error "无效选择"
;;
esac
echo
read -p "按回车键继续..."
echo
done
}
# 实验快照菜单
lab_snapshot_menu() {
while true; do
echo "========== 实验快照 =========="
echo "[1] 实验快照"
echo "[2] 快照信息查看"
echo "[3] 恢复快照"
echo "[4] 删除快照"
echo "[0] 返回主菜单"
read -p "选择 [1-4]:" lab_choice
case $lab_choice in
1)
echo "实验快照......"
lab_snapshot_create
;;
2)
echo "快照信息查看......"
lab_snapshot_list
;;
3)
echo "恢复快照......"
lab_snapshot_restore
;;
4)
echo "删除快照......"
lab_snapshot_delete
;;
0)
break
;;
*)
log_error "无效选择"
;;
esac
echo
read -p "按回车键继续..."
echo
done
}
# 主菜单
main_menu() {
while true; do
echo "========== EVE-NG 快照工具 =========="
echo -e "${RED}警告!请在节点关机下执行,否则可能出问题!!!${NC}"
echo -e "${RED}警告!擦除节点会清空快照!!!${NC}"
echo "https://www.emulatedlab.com/forum.php"
echo "v1.0.0"
echo "==================================="
echo "用户ID: $USER_ID"
echo "实验UUID: $LAB_UUID"
echo "==================================="
echo "[1] 节点快照"
echo "[2] 实验快照"
echo "[0] 退出"
read -p "选择 [1-2]:" main_choice
case $main_choice in
1)
node_snapshot_menu
;;
2)
lab_snapshot_menu
;;
0)
log_info "退出脚本"
echo
echo
exit 0
;;
*)
log_error "无效选择"
;;
esac
done
}
# 入口
if [ $# -eq 0 ]; then
# 如果没有参数,显示提示
show_usage
else
# 解析参数
parse_arguments "$@"
# 检测UUID是否存在
if ! validate_uuid "$LAB_UUID"; then
exit 1
fi
# 进入主菜单
main_menu
fi