From 7def817482e22a53e02f6e869c7747d070fb3ac9 Mon Sep 17 00:00:00 2001 From: Wang Defa Date: Fri, 26 Dec 2025 14:47:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BB=A3=E7=A0=81=E6=A0=87=E5=87=86?= =?UTF-8?q?=E5=8C=96=E5=92=8C=E6=96=87=E4=BB=B6=E9=87=8D=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 新增功能 - 创建统一的公共函数库 (common/logging.sh, common/error_handler.sh) - 添加功能演示脚本 (common/demo_usage.sh) - 完善的使用文档 (common/README.md) ## 代码重构 - 重构所有脚本使用统一的公共库 - 为所有函数添加完整的文档注释 - 统一代码格式(4空格缩进、严格模式) - 标准化错误处理和日志输出 ## 文件重命名 - gcp/create_ai_project.sh → gcp/create_ai_projects.sh (单复数统一) - gcp/delete_all_project.sh → gcp/delete_all_projects.sh (单复数统一) - linux/install_ohmyzsh.sh → linux/install_oh_my_zsh.sh (专有名词规范) - linux/create_raid0_with_ext4.sh → linux/create_raid0_array.sh (简化命名) - common/example.sh → common/demo_usage.sh (更具描述性) ## 技术改进 - 使用 readonly 声明常量 - 启用 set -euo pipefail 严格模式 - 统一的 ANSI 颜色日志输出 - 完善的命令重试机制 - 栈追踪支持 --- .gitignore | 4 + common/README.md | 245 +++++++++++++++++ common/demo_usage.sh | 179 +++++++++++++ common/error_handler.sh | 300 +++++++++++++++++++++ common/logging.sh | 199 ++++++++++++++ gcp/create_ai_projects.sh | 514 ++++++++++++++++++++++++++++++++++++ gcp/delete_all_projects.sh | 139 ++++++++++ linux/create_raid0_array.sh | 142 ++++++++++ linux/install_oh_my_zsh.sh | 107 ++++++++ linux/repartition_disks.sh | 94 +++++++ oci/create_instance.sh | 389 +++++++++++++++++++++++++++ 11 files changed, 2312 insertions(+) create mode 100644 .gitignore create mode 100644 common/README.md create mode 100755 common/demo_usage.sh create mode 100644 common/error_handler.sh create mode 100644 common/logging.sh create mode 100644 gcp/create_ai_projects.sh create mode 100644 gcp/delete_all_projects.sh create mode 100644 linux/create_raid0_array.sh create mode 100644 linux/install_oh_my_zsh.sh create mode 100644 linux/repartition_disks.sh create mode 100644 oci/create_instance.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe2e168 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Claude Code +.mcp.json +.claude/ +llmdoc/ \ No newline at end of file diff --git a/common/README.md b/common/README.md new file mode 100644 index 0000000..dfe6c87 --- /dev/null +++ b/common/README.md @@ -0,0 +1,245 @@ +# 公共函数库 + +本目录包含可复用的 Bash 函数库,用于标准化所有脚本的日志输出和错误处理。 + +## 📦 包含的库 + +### 1. logging.sh - 日志输出库 + +提供统一的日志输出函数,支持多个日志级别和彩色输出。 + +**功能特性:** +- 5个日志级别:DEBUG, INFO, WARNING, ERROR, SUCCESS +- 彩色控制台输出 +- 可选的时间戳 +- 支持输出到文件 +- 灵活的日志级别控制 + +**使用示例:** + +```bash +#!/bin/bash +source common/logging.sh + +# 基本使用 +log_info "程序启动" +log_success "操作完成" +log_warning "磁盘空间不足" +log_error "连接失败" +log_debug "调试信息" + +# 设置日志级别 +log_set_level "DEBUG" # 显示所有日志 + +# 输出到文件 +log_set_file "/var/log/myapp.log" + +# 禁用时间戳 +log_disable_timestamp +``` + +### 2. error_handler.sh - 错误处理库 + +提供统一的错误处理和检查机制。 + +**功能特性:** +- 命令存在性检查 +- 文件/目录检查 +- 变量非空检查 +- Root 权限检查 +- 命令返回值检查 +- 带重试的命令执行 +- 调用栈跟踪 + +**使用示例:** + +```bash +#!/bin/bash +source common/error_handler.sh + +# 启用严格错误处理 +enable_exit_on_error + +# 检查命令是否存在 +check_command "git" || error_exit "git 未安装" + +# 检查文件 +check_file "/etc/config" || error_exit "配置文件不存在" + +# 检查 root 权限 +check_root || error_exit "需要 root 权限" + +# 执行命令并检查结果 +run_command "安装失败" apt-get install -y package + +# 带重试的命令执行 +retry_command 3 5 "下载失败" curl -O https://example.com/file + +# 检查变量非空 +check_not_empty "$PROJECT_ID" "PROJECT_ID" || error_exit "项目ID不能为空" +``` + +## 🚀 完整示例脚本 + +```bash +#!/bin/bash +# ============================================================================ +# 示例脚本:展示如何使用公共函数库 +# ============================================================================ + +# 获取脚本目录 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# 加载公共库 +source "${SCRIPT_DIR}/../common/logging.sh" +source "${SCRIPT_DIR}/../common/error_handler.sh" + +# 配置日志 +log_set_level "INFO" +log_set_file "/tmp/example.log" + +# 启用错误处理 +enable_exit_on_error + +# +# 主函数 +# +main() { + log_info "脚本开始执行" + + # 检查必要的命令 + check_command "git" || error_exit "请先安装 git" + + # 检查参数 + local project_name="$1" + check_not_empty "$project_name" "project_name" || { + log_error "用法: $0 " + exit 1 + } + + # 执行操作 + log_info "正在处理项目: $project_name" + + # 带重试的网络操作 + retry_command 3 2 "网络请求失败" curl -f "https://example.com/api" + + log_success "脚本执行完成" +} + +# 执行主函数 +main "$@" +``` + +## 📖 最佳实践 + +### 1. 脚本模板 + +每个脚本都应遵循以下模板结构: + +```bash +#!/bin/bash +set -euo pipefail # 启用严格模式 + +# 脚本信息 +# ============================================================================ +# 文件名: script_name.sh +# 描述: 脚本功能描述 +# 作者: 作者名 +# 版本: 1.0.0 +# ============================================================================ + +# 常量定义 +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly SCRIPT_NAME="$(basename "$0")" + +# 加载公共库 +source "${SCRIPT_DIR}/../common/logging.sh" +source "${SCRIPT_DIR}/../common/error_handler.sh" + +# 全局变量 +GLOBAL_VAR="" + +# +# 显示使用帮助 +# +show_usage() { + cat << EOF +使用方法: $SCRIPT_NAME [选项] + +选项: + -h, --help 显示此帮助信息 + -v, --verbose 启用详细输出 + +示例: + $SCRIPT_NAME --verbose +EOF +} + +# +# 主函数 +# +main() { + log_info "脚本开始执行" + + # 你的代码逻辑 + + log_success "脚本执行完成" +} + +# 执行主函数 +main "$@" +``` + +### 2. 错误处理建议 + +- 总是检查命令返回值 +- 使用 `check_*` 函数进行前置条件检查 +- 对网络操作使用 `retry_command` +- 提供有意义的错误消息 + +### 3. 日志使用建议 + +- INFO: 正常流程信息 +- SUCCESS: 操作成功完成 +- WARNING: 需要注意但不影响执行的情况 +- ERROR: 错误但可以继续的情况 +- DEBUG: 调试信息(默认不显示) + +## 🔧 环境变量配置 + +### logging.sh 环境变量 + +```bash +# 设置日志级别 (DEBUG=0, INFO=1, WARNING=2, ERROR=3, SUCCESS=4) +export LOG_CURRENT_LEVEL=1 + +# 设置日志文件路径 +export LOG_FILE_PATH="/var/log/myapp.log" + +# 启用/禁用时间戳 +export LOG_ENABLE_TIMESTAMP=true +``` + +### error_handler.sh 环境变量 + +```bash +# 错误时是否退出 (true/false) +export ERROR_EXIT_ON_FAIL=true + +# 是否显示调用栈 (true/false) +export ERROR_STACK_TRACE=false +``` + +## 📝 维护说明 + +- 版本: 1.0.0 +- 最后更新: 2025-12-26 +- 维护者: Cloud Tools Project + +## 🤝 贡献 + +如需添加新功能或修复 bug,请: +1. 保持向后兼容性 +2. 添加完整的函数文档注释 +3. 提供使用示例 +4. 更新此 README diff --git a/common/demo_usage.sh b/common/demo_usage.sh new file mode 100755 index 0000000..f4256bc --- /dev/null +++ b/common/demo_usage.sh @@ -0,0 +1,179 @@ +#!/bin/bash +# ============================================================================ +# 文件名: demo_usage.sh +# 描述: 公共函数库功能演示和使用示例 +# 作者: Cloud Tools Project +# 版本: 1.0.0 +# ============================================================================ + +set -euo pipefail # 启用严格模式 + +# 获取脚本目录 +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# 加载公共库 +source "${SCRIPT_DIR}/logging.sh" +source "${SCRIPT_DIR}/error_handler.sh" + +# +# 示例 1: 基本日志使用 +# +example_basic_logging() { + log_info "============ 示例 1: 基本日志使用 ============" + + log_debug "这是调试信息(默认不显示)" + log_info "这是普通信息" + log_warning "这是警告信息" + log_error "这是错误信息(但不会退出)" + log_success "这是成功信息" + + echo "" +} + +# +# 示例 2: 日志级别控制 +# +example_log_levels() { + log_info "============ 示例 2: 日志级别控制 ============" + + log_info "当前日志级别: INFO" + log_debug "这条调试信息不会显示" + + log_info "切换到 DEBUG 级别..." + log_set_level "DEBUG" + log_debug "现在调试信息可以显示了!" + + log_info "恢复到 INFO 级别..." + log_set_level "INFO" + + echo "" +} + +# +# 示例 3: 错误检查 +# +example_error_checking() { + log_info "============ 示例 3: 错误检查 ============" + + # 临时禁用自动退出 + disable_exit_on_error + + # 检查命令 + if check_command "bash"; then + log_success "bash 命令存在" + fi + + if ! check_command "nonexistent_command"; then + log_warning "nonexistent_command 不存在(这是预期的)" + fi + + # 检查文件 + if check_file "${SCRIPT_DIR}/logging.sh"; then + log_success "logging.sh 文件存在" + fi + + # 检查变量非空 + local test_var="hello" + if check_not_empty "$test_var" "test_var"; then + log_success "test_var 变量非空" + fi + + # 重新启用自动退出 + enable_exit_on_error + + echo "" +} + +# +# 示例 4: 命令执行和重试 +# +example_command_execution() { + log_info "============ 示例 4: 命令执行 ============" + + # 执行简单命令 + run_command "列出文件失败" ls -la "${SCRIPT_DIR}" + + log_info "执行成功的命令" + + # 临时禁用自动退出来演示重试 + disable_exit_on_error + + log_info "尝试执行一个会失败的命令(演示重试)" + retry_command 3 1 "命令失败" false + + # 重新启用 + enable_exit_on_error + + echo "" +} + +# +# 示例 5: 日志文件输出 +# +example_log_file() { + log_info "============ 示例 5: 日志文件输出 ============" + + local temp_log="/tmp/common_lib_example_$$.log" + + log_info "设置日志输出到文件: $temp_log" + log_set_file "$temp_log" + + log_info "这条消息会同时输出到控制台和文件" + log_success "日志文件功能正常" + + log_info "日志文件内容:" + cat "$temp_log" + + # 清理 + rm -f "$temp_log" + + echo "" +} + +# +# 示例 6: 时间戳控制 +# +example_timestamp() { + log_info "============ 示例 6: 时间戳控制 ============" + + log_info "默认启用时间戳" + + log_info "禁用时间戳..." + log_disable_timestamp + log_info "这条消息没有时间戳" + + log_info "启用时间戳..." + log_enable_timestamp + log_info "时间戳已恢复" + + echo "" +} + +# +# 主函数 +# +main() { + # 配置日志 + log_set_level "INFO" + log_enable_timestamp + + log_success "===================================================" + log_success " 公共函数库使用示例" + log_success "===================================================" + echo "" + + # 执行所有示例 + example_basic_logging + example_log_levels + example_error_checking + example_command_execution + example_log_file + example_timestamp + + log_success "===================================================" + log_success " 所有示例执行完成!" + log_success "===================================================" +} + +# 执行主函数 +main "$@" diff --git a/common/error_handler.sh b/common/error_handler.sh new file mode 100644 index 0000000..a458976 --- /dev/null +++ b/common/error_handler.sh @@ -0,0 +1,300 @@ +#!/bin/bash +# ============================================================================ +# 文件名: error_handler.sh +# 描述: 统一的错误处理机制 +# 作者: Cloud Tools Project +# 版本: 1.0.0 +# 依赖: logging.sh +# 使用方法: source common/error_handler.sh +# ============================================================================ + +# 确保加载日志库 +if ! declare -f log_error >/dev/null 2>&1; then + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + # shellcheck source=./logging.sh + source "${SCRIPT_DIR}/logging.sh" +fi + +# 错误处理配置 +ERROR_EXIT_ON_FAIL=${ERROR_EXIT_ON_FAIL:-true} +ERROR_STACK_TRACE=${ERROR_STACK_TRACE:-false} + +# +# 内部: 获取调用栈信息 +# 返回: 格式化的调用栈字符串 +# +_error_get_stack_trace() { + local frame=0 + local line + local func + local src + + echo "调用栈:" + while caller $frame; do + ((frame++)) + done | while read -r line func src; do + echo " at $func ($src:$line)" + done +} + +# +# 检查命令是否存在 +# 参数: $1 - 命令名称 +# 返回: 0=存在, 1=不存在 +# 示例: check_command "git" || error_exit "git 未安装" +# +check_command() { + local cmd="$1" + + if ! command -v "$cmd" &>/dev/null; then + log_error "命令不存在: $cmd" + return 1 + fi + + return 0 +} + +# +# 检查文件是否存在 +# 参数: $1 - 文件路径 +# 返回: 0=存在, 1=不存在 +# 示例: check_file "/path/to/file" || error_exit "文件不存在" +# +check_file() { + local file="$1" + + if [[ ! -f "$file" ]]; then + log_error "文件不存在: $file" + return 1 + fi + + return 0 +} + +# +# 检查目录是否存在 +# 参数: $1 - 目录路径 +# 返回: 0=存在, 1=不存在 +# 示例: check_directory "/path/to/dir" || error_exit "目录不存在" +# +check_directory() { + local dir="$1" + + if [[ ! -d "$dir" ]]; then + log_error "目录不存在: $dir" + return 1 + fi + + return 0 +} + +# +# 检查变量是否为空 +# 参数: +# $1 - 变量值 +# $2 - 变量名称 (可选,用于错误消息) +# 返回: 0=非空, 1=为空 +# 示例: check_not_empty "$VAR" "VAR" || error_exit "变量不能为空" +# +check_not_empty() { + local value="$1" + local var_name="${2:-变量}" + + if [[ -z "$value" ]]; then + log_error "${var_name} 不能为空" + return 1 + fi + + return 0 +} + +# +# 检查是否以 root 权限运行 +# 返回: 0=是root, 1=不是root +# 示例: check_root || error_exit "此脚本需要 root 权限" +# +check_root() { + if [[ $EUID -ne 0 ]]; then + log_error "此脚本需要 root 权限运行" + return 1 + fi + + return 0 +} + +# +# 检查命令返回值 +# 参数: +# $1 - 命令返回值 ($?) +# $2 - 错误消息 (可选) +# 返回: 传入的返回值 +# 示例: some_command; check_return $? "命令执行失败" +# +check_return() { + local ret=$1 + local msg="${2:-命令执行失败}" + + if [[ $ret -ne 0 ]]; then + log_error "$msg (退出码: $ret)" + + if [[ "$ERROR_STACK_TRACE" == "true" ]]; then + _error_get_stack_trace >&2 + fi + + if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then + exit "$ret" + fi + fi + + return "$ret" +} + +# +# 错误退出 +# 参数: +# $1 - 错误消息 +# $2 - 退出码 (可选,默认: 1) +# 示例: error_exit "配置文件不存在" 2 +# +error_exit() { + local msg="$1" + local exit_code="${2:-1}" + + log_error "$msg" + + if [[ "$ERROR_STACK_TRACE" == "true" ]]; then + _error_get_stack_trace >&2 + fi + + exit "$exit_code" +} + +# +# 执行命令并检查结果 +# 参数: +# $1 - 错误消息前缀 +# $@ - 要执行的命令和参数 +# 返回: 命令的返回值 +# 示例: run_command "安装失败" apt-get install -y package +# +run_command() { + local error_msg="$1" + shift + + log_debug "执行命令: $*" + + if ! "$@"; then + local ret=$? + log_error "${error_msg}: $*" + + if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then + exit "$ret" + fi + + return "$ret" + fi + + return 0 +} + +# +# 带重试的命令执行 +# 参数: +# $1 - 最大重试次数 +# $2 - 重试间隔 (秒) +# $3 - 错误消息前缀 +# $@ - 要执行的命令和参数 +# 返回: 命令的返回值 +# 示例: retry_command 3 5 "连接失败" curl -f https://example.com +# +retry_command() { + local max_retries=$1 + local retry_delay=$2 + local error_msg="$3" + shift 3 + + local attempt=1 + local ret + + while [[ $attempt -le $max_retries ]]; do + log_debug "尝试执行 (第 $attempt/$max_retries 次): $*" + + if "$@"; then + log_debug "命令执行成功" + return 0 + fi + + ret=$? + + if [[ $attempt -lt $max_retries ]]; then + log_warning "${error_msg} (第 $attempt 次失败,将在 ${retry_delay}秒后重试)" + sleep "$retry_delay" + else + log_error "${error_msg} (已重试 $max_retries 次)" + fi + + ((attempt++)) + done + + if [[ "$ERROR_EXIT_ON_FAIL" == "true" ]]; then + exit "$ret" + fi + + return "$ret" +} + +# +# 启用错误时退出 +# 示例: enable_exit_on_error +# +enable_exit_on_error() { + ERROR_EXIT_ON_FAIL=true + set -e + set -o pipefail +} + +# +# 禁用错误时退出 +# 示例: disable_exit_on_error +# +disable_exit_on_error() { + ERROR_EXIT_ON_FAIL=false + set +e + set +o pipefail +} + +# +# 启用调用栈跟踪 +# 示例: enable_stack_trace +# +enable_stack_trace() { + ERROR_STACK_TRACE=true +} + +# +# 禁用调用栈跟踪 +# 示例: disable_stack_trace +# +disable_stack_trace() { + ERROR_STACK_TRACE=false +} + +# 设置 ERR 陷阱(可选) +# trap '_error_handler $? $LINENO' ERR + +# +# 内部: ERR 陷阱处理函数 +# 参数: +# $1 - 错误码 +# $2 - 行号 +# +_error_handler() { + local exit_code=$1 + local line_number=$2 + + log_error "脚本在第 $line_number 行发生错误 (退出码: $exit_code)" + + if [[ "$ERROR_STACK_TRACE" == "true" ]]; then + _error_get_stack_trace >&2 + fi +} diff --git a/common/logging.sh b/common/logging.sh new file mode 100644 index 0000000..f311541 --- /dev/null +++ b/common/logging.sh @@ -0,0 +1,199 @@ +#!/bin/bash +# ============================================================================ +# 文件名: logging.sh +# 描述: 统一的日志输出函数库 +# 作者: Cloud Tools Project +# 版本: 1.0.0 +# 依赖: 无 +# 使用方法: source common/logging.sh +# ============================================================================ + +# 颜色定义 (ANSI 颜色代码) +readonly LOG_COLOR_RED='\033[0;31m' +readonly LOG_COLOR_GREEN='\033[0;32m' +readonly LOG_COLOR_YELLOW='\033[1;33m' +readonly LOG_COLOR_BLUE='\033[0;34m' +readonly LOG_COLOR_MAGENTA='\033[0;35m' +readonly LOG_COLOR_CYAN='\033[0;36m' +readonly LOG_COLOR_RESET='\033[0m' + +# 日志级别定义 +readonly LOG_LEVEL_DEBUG=0 +readonly LOG_LEVEL_INFO=1 +readonly LOG_LEVEL_WARNING=2 +readonly LOG_LEVEL_ERROR=3 +readonly LOG_LEVEL_SUCCESS=4 + +# 当前日志级别 (默认: INFO) +LOG_CURRENT_LEVEL=${LOG_CURRENT_LEVEL:-$LOG_LEVEL_INFO} + +# 日志文件路径 (可选) +LOG_FILE_PATH="${LOG_FILE_PATH:-}" + +# 是否启用时间戳 +LOG_ENABLE_TIMESTAMP=${LOG_ENABLE_TIMESTAMP:-true} + +# +# 获取格式化的时间戳 +# 返回: 格式化的时间字符串 +# +_log_get_timestamp() { + date '+%Y-%m-%d %H:%M:%S' +} + +# +# 内部日志输出函数 +# 参数: +# $1 - 日志级别 (DEBUG|INFO|WARNING|ERROR|SUCCESS) +# $2 - 日志颜色 +# $3 - 日志消息 +# $4 - 输出目标 (stdout/stderr) +# +_log_output() { + local level="$1" + local color="$2" + local message="$3" + local target="${4:-stdout}" + + local timestamp="" + if [[ "$LOG_ENABLE_TIMESTAMP" == "true" ]]; then + timestamp="$(_log_get_timestamp) " + fi + + local log_line="${timestamp}[${level}] ${message}" + local colored_line="${color}${log_line}${LOG_COLOR_RESET}" + + # 输出到控制台 + if [[ "$target" == "stderr" ]]; then + echo -e "$colored_line" >&2 + else + echo -e "$colored_line" + fi + + # 输出到日志文件 (如果配置了) + if [[ -n "$LOG_FILE_PATH" ]]; then + echo "$log_line" >> "$LOG_FILE_PATH" + fi +} + +# +# 调试级别日志 +# 参数: $1 - 日志消息 +# 示例: log_debug "Debugging information" +# +log_debug() { + if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_DEBUG ]]; then + _log_output "DEBUG" "$LOG_COLOR_CYAN" "$1" "stdout" + fi +} + +# +# 信息级别日志 +# 参数: $1 - 日志消息 +# 示例: log_info "Process started" +# +log_info() { + if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_INFO ]]; then + _log_output "INFO" "$LOG_COLOR_BLUE" "$1" "stdout" + fi +} + +# +# 警告级别日志 +# 参数: $1 - 日志消息 +# 示例: log_warning "Disk space low" +# +log_warning() { + if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_WARNING ]]; then + _log_output "WARNING" "$LOG_COLOR_YELLOW" "$1" "stderr" + fi +} + +# +# 错误级别日志 +# 参数: $1 - 日志消息 +# 示例: log_error "Failed to connect to server" +# +log_error() { + if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_ERROR ]]; then + _log_output "ERROR" "$LOG_COLOR_RED" "$1" "stderr" + fi +} + +# +# 成功级别日志 +# 参数: $1 - 日志消息 +# 示例: log_success "Deployment completed" +# +log_success() { + if [[ $LOG_CURRENT_LEVEL -le $LOG_LEVEL_SUCCESS ]]; then + _log_output "SUCCESS" "$LOG_COLOR_GREEN" "$1" "stdout" + fi +} + +# +# 设置日志级别 +# 参数: $1 - 日志级别 (DEBUG|INFO|WARNING|ERROR|SUCCESS) +# 示例: log_set_level "DEBUG" +# +log_set_level() { + local level + level="$(echo "$1" | tr '[:lower:]' '[:upper:]')" + + case "$level" in + DEBUG) LOG_CURRENT_LEVEL=$LOG_LEVEL_DEBUG ;; + INFO) LOG_CURRENT_LEVEL=$LOG_LEVEL_INFO ;; + WARNING) LOG_CURRENT_LEVEL=$LOG_LEVEL_WARNING ;; + ERROR) LOG_CURRENT_LEVEL=$LOG_LEVEL_ERROR ;; + SUCCESS) LOG_CURRENT_LEVEL=$LOG_LEVEL_SUCCESS ;; + *) + log_error "Invalid log level: $1" + return 1 + ;; + esac +} + +# +# 设置日志文件路径 +# 参数: $1 - 日志文件完整路径 +# 示例: log_set_file "/var/log/myapp.log" +# +log_set_file() { + LOG_FILE_PATH="$1" + + # 创建日志文件目录 + local log_dir + log_dir="$(dirname "$LOG_FILE_PATH")" + if [[ ! -d "$log_dir" ]]; then + mkdir -p "$log_dir" 2>/dev/null || { + log_error "无法创建日志目录: $log_dir" + return 1 + } + fi + + # 初始化日志文件 + if [[ ! -f "$LOG_FILE_PATH" ]]; then + touch "$LOG_FILE_PATH" 2>/dev/null || { + log_error "无法创建日志文件: $LOG_FILE_PATH" + return 1 + } + fi + + log_info "日志输出到文件: $LOG_FILE_PATH" +} + +# +# 禁用时间戳 +# 示例: log_disable_timestamp +# +log_disable_timestamp() { + LOG_ENABLE_TIMESTAMP=false +} + +# +# 启用时间戳 +# 示例: log_enable_timestamp +# +log_enable_timestamp() { + LOG_ENABLE_TIMESTAMP=true +} diff --git a/gcp/create_ai_projects.sh b/gcp/create_ai_projects.sh new file mode 100644 index 0000000..a140977 --- /dev/null +++ b/gcp/create_ai_projects.sh @@ -0,0 +1,514 @@ +#!/bin/bash +# ============================================================================ +# 文件名: create_ai_projects.sh +# 描述: 批量创建 Google Cloud Platform AI 项目并配置相关服务 +# 作者: Cloud Tools Project +# 版本: 2.0.0 +# ============================================================================ + +set -euo pipefail # 启用严格模式 + +# 获取脚本目录 +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# 加载公共库 +source "${PROJECT_ROOT}/common/logging.sh" +source "${PROJECT_ROOT}/common/error_handler.sh" + +# 默认配置 +readonly DEFAULT_PROJECT_ID_PREFIX="project" +readonly DEFAULT_START_NUM=1 +readonly DEFAULT_REPEAT_NUM=5 +readonly DEFAULT_MAX_RETRIES=3 +readonly DEFAULT_RETRY_DELAY=5 + +# 创建日志文件 +readonly LOG_FILE="created_projects_$(date +%Y%m%d_%H%M%S).log" +log_set_file "$LOG_FILE" + +# +# 显示脚本使用方法 +# +usage() { + log_info "用法: $0 [选项]" + log_info "选项:" + log_info " -p, --prefix PREFIX 项目ID前缀 (默认: $DEFAULT_PROJECT_ID_PREFIX)" + log_info " -s, --start START 项目编号起始值 (默认: $DEFAULT_START_NUM)" + log_info " -n, --number NUMBER 创建项目的数量 (默认: $DEFAULT_REPEAT_NUM)" + log_info " --max-retries RETRIES 单个命令失败时的最大重试次数 (默认: $DEFAULT_MAX_RETRIES)" + log_info " --retry-delay DELAY 每次重试之间的延迟秒数 (默认: $DEFAULT_RETRY_DELAY)" + log_info " --debug 启用调试模式" + log_info " -h, --help 显示此帮助信息" +} + +# +# 检查必要的命令是否存在 +# +check_command() { + if ! command -v "$1" &> /dev/null; then + log_error "命令 $1 未找到。请确保它已安装并在PATH中。" + exit 1 + fi +} + +# +# 重试执行命令 +# +retry_command() { + local max_attempts=$1 + local delay=$2 + shift 2 + local cmd_to_run=("$@") + local attempt=1 + local exit_code + local temp_output + local temp_error + + while [ $attempt -le "$max_attempts" ]; do + log_info "尝试执行 (第 $attempt/$max_attempts 次): ${cmd_to_run[*]}" + + # 创建临时文件捕获输出和错误 + temp_output=$(mktemp) + temp_error=$(mktemp) + + if "${cmd_to_run[@]}" > "$temp_output" 2> "$temp_error"; then + log_success "命令成功执行: ${cmd_to_run[*]}" + # 如果有输出,显示它 + if [ -s "$temp_output" ]; then + cat "$temp_output" + fi + rm -f "$temp_output" "$temp_error" 2>/dev/null || true + return 0 + else + exit_code=$? + log_warning "命令执行失败 (第 $attempt/$max_attempts 次),退出码: $exit_code" + + # 显示错误信息用于调试 + if [ -s "$temp_error" ]; then + log_error "错误输出: $(cat "$temp_error")" + fi + + if [ $attempt -lt "$max_attempts" ]; then + log_info "将在 $delay 秒后重试..." + sleep "$delay" + fi + fi + rm -f "$temp_output" "$temp_error" 2>/dev/null || true + ((attempt++)) + done + + log_error "命令在 $max_attempts 次尝试后仍然失败: ${cmd_to_run[*]}" + return 1 +} + +# +# 重试并获取命令输出 +# +retry_command_with_output() { + local max_attempts=$1 + local delay=$2 + local output_var=$3 + shift 3 + local cmd_to_run=("$@") + local attempt=1 + local exit_code + local temp_output + local temp_error + + while [ $attempt -le "$max_attempts" ]; do + log_info "尝试执行 (第 $attempt/$max_attempts 次): ${cmd_to_run[*]}" + + temp_output=$(mktemp) + temp_error=$(mktemp) + + if "${cmd_to_run[@]}" > "$temp_output" 2> "$temp_error"; then + log_success "命令成功执行: ${cmd_to_run[*]}" + # 将输出赋值给指定变量 + eval "$output_var=\$(cat '$temp_output')" + rm -f "$temp_output" "$temp_error" 2>/dev/null || true + return 0 + else + exit_code=$? + log_warning "命令执行失败 (第 $attempt/$max_attempts 次),退出码: $exit_code" + + if [ -s "$temp_error" ]; then + log_error "错误输出: $(cat "$temp_error")" + fi + + if [ $attempt -lt "$max_attempts" ]; then + log_info "将在 $delay 秒后重试..." + sleep "$delay" + fi + fi + rm -f "$temp_output" "$temp_error" 2>/dev/null || true + ((attempt++)) + done + + log_error "命令在 $max_attempts 次尝试后仍然失败: ${cmd_to_run[*]}" + return 1 +} + +# +# 创建 GCP 项目 +# +create_project() { + local project_id=$1 + local max_retries=$2 + local retry_delay=$3 + + log_info "创建项目: $project_id" + + # 检查项目是否已存在 + if gcloud projects describe "$project_id" &>/dev/null; then + log_warning "项目 $project_id 已存在,跳过创建" + return 0 + fi + + if retry_command "$max_retries" "$retry_delay" gcloud projects create "$project_id" --name="$project_id"; then + log_success "项目 $project_id 创建成功" + return 0 + else + log_error "创建项目 $project_id 失败 (已重试)" + return 1 + fi +} + +# +# 链接结算账号 +# +link_billing() { + local project_id=$1 + local billing_id=$2 + local max_retries=$3 + local retry_delay=$4 + + log_info "链接结算账号到项目: $project_id" + if retry_command "$max_retries" "$retry_delay" gcloud beta billing projects link "$project_id" --billing-account="$billing_id"; then + log_success "结算账号链接成功" + return 0 + else + log_error "链接结算账号到项目 $project_id 失败 (已重试)" + return 1 + fi +} + +# +# 启用服务 +# +enable_services() { + local project_id=$1 + local max_retries=$2 + local retry_delay=$3 + + log_info "启用 aiplatform.googleapis.com 服务" + if retry_command "$max_retries" "$retry_delay" gcloud services enable aiplatform.googleapis.com --project="$project_id"; then + log_success "服务启用成功" + return 0 + else + log_error "启用 aiplatform.googleapis.com 服务失败 (已重试)" + return 1 + fi +} + +# +# 创建服务账号 +# +create_service_account() { + local project_id=$1 + local max_retries=$2 + local retry_delay=$3 + + log_info "创建服务账号 service-account@$project_id.iam.gserviceaccount.com" + + # 检查服务账号是否已存在 + if gcloud iam service-accounts describe "service-account@$project_id.iam.gserviceaccount.com" --project="$project_id" &>/dev/null; then + log_warning "服务账号已存在,跳过创建" + return 0 + fi + + if retry_command "$max_retries" "$retry_delay" gcloud iam service-accounts create service-account \ + --display-name="AI Platform Service Account" \ + --project="$project_id"; then + log_success "服务账号创建成功" + return 0 + else + log_error "创建服务账号失败 (已重试)" + return 1 + fi +} + +# +# 添加 IAM 策略 +# +add_iam_policy() { + local project_id=$1 + local max_retries=$2 + local retry_delay=$3 + + log_info "授予 aiplatform.serviceAgent 角色" + if retry_command "$max_retries" "$retry_delay" gcloud projects add-iam-policy-binding "$project_id" \ + --member="serviceAccount:service-account@$project_id.iam.gserviceaccount.com" \ + --role="roles/aiplatform.serviceAgent"; then + log_success "IAM 策略授予成功" + return 0 + else + log_error "授予 IAM 策略失败 (已重试)" + return 1 + fi +} + +# +# 创建服务账号密钥 +# +create_service_account_key() { + local project_id=$1 + local max_retries=$2 + local retry_delay=$3 + + log_info "创建服务账号密钥" + + # 检查密钥文件是否已存在 + if [ -f "pass-$project_id.json" ]; then + log_warning "密钥文件 pass-$project_id.json 已存在,跳过创建" + return 0 + fi + + if retry_command "$max_retries" "$retry_delay" gcloud iam service-accounts keys create "pass-$project_id.json" \ + --iam-account="service-account@$project_id.iam.gserviceaccount.com" \ + --project="$project_id"; then + log_success "服务账号密钥创建成功: pass-$project_id.json" + return 0 + else + log_error "创建服务账号密钥失败 (已重试)" + return 1 + fi +} + +# 参数解析和验证 +PROJECT_ID_PREFIX="$DEFAULT_PROJECT_ID_PREFIX" +START_NUM="$DEFAULT_START_NUM" +REPEAT_NUM="$DEFAULT_REPEAT_NUM" +MAX_RETRIES="$DEFAULT_MAX_RETRIES" +RETRY_DELAY="$DEFAULT_RETRY_DELAY" + +# 解析命令行参数 +while [[ $# -gt 0 ]]; do + case $1 in + -p|--prefix) + PROJECT_ID_PREFIX="$2" + shift 2 + ;; + -s|--start) + START_NUM="$2" + shift 2 + ;; + -n|--number) + REPEAT_NUM="$2" + shift 2 + ;; + --max-retries) + MAX_RETRIES="$2" + shift 2 + ;; + --retry-delay) + RETRY_DELAY="$2" + shift 2 + ;; + --debug) + DEBUG_MODE=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_error "未知选项: $1" + usage + exit 1 + ;; + esac +done + +# 检查必要的命令 +check_command gcloud + +# 验证参数 +if ! [[ "$START_NUM" =~ ^[0-9]+$ ]]; then + log_error "起始编号必须是一个正整数" + exit 1 +fi + +if ! [[ "$REPEAT_NUM" =~ ^[0-9]+$ ]] || [ "$REPEAT_NUM" -lt 1 ]; then + log_error "项目数量必须是一个正整数" + exit 1 +fi + +if ! [[ "$MAX_RETRIES" =~ ^[0-9]+$ ]] || [ "$MAX_RETRIES" -lt 0 ]; then + log_error "最大重试次数必须是一个非负整数" + exit 1 +fi + +if ! [[ "$RETRY_DELAY" =~ ^[0-9]+$ ]] || [ "$RETRY_DELAY" -lt 0 ]; then + log_error "重试延迟必须是一个非负整数" + exit 1 +fi + +log_info "################################################################################" +log_info "开始创建 GCP 项目批量任务" +log_info "################################################################################" + +# 调试模式提示 +if [ "$DEBUG_MODE" = true ]; then + log_debug "调试模式已启用" +fi + +# 获取结算账号 +log_info "获取结算账号..." +billing_id="" +if retry_command_with_output "$MAX_RETRIES" "$RETRY_DELAY" billing_id_output gcloud beta billing accounts list --format="value(name.basename())" --filter="open=true"; then + # 提取第一个结算账号ID + billing_id=$(echo "$billing_id_output" | head -n 1 | tr -d '[:space:]') + + if [ -z "$billing_id" ]; then + log_error "未找到有效的活动结算账号。请检查您的结算账户。" + exit 1 + fi + + log_success "结算账号: $billing_id" +else + log_error "获取结算账号失败 (已重试)。请检查您的结算账户。" + exit 1 +fi + +# 计算结束编号 +END_NUM=$((START_NUM + REPEAT_NUM - 1)) + +log_info "配置信息:" +log_info " 项目前缀: $PROJECT_ID_PREFIX" +log_info " 起始编号: $START_NUM" +log_info " 结束编号: $END_NUM" +log_info " 项目总数: $REPEAT_NUM" +log_info " 最大重试: $MAX_RETRIES 次" +log_info " 重试延迟: $RETRY_DELAY 秒" + +# 显示将要创建的项目列表 +log_info "将创建以下项目:" +for i in $(seq "$START_NUM" "$END_NUM"); do + PROJECT_ID="${PROJECT_ID_PREFIX}$(printf "%02d" "$i")" + log_info " - $PROJECT_ID" +done + +# 确认开始创建 +log_warning "准备开始创建项目..." +sleep 1 + +# 统计变量 +successful_projects=0 +failed_projects=0 + +# 创建项目和相关资源 - 主循环 +log_debug "开始主循环,从 $START_NUM 到 $END_NUM" + +for i in $(seq "$START_NUM" "$END_NUM"); do + PROJECT_ID="${PROJECT_ID_PREFIX}$(printf "%02d" "$i")" + current_num=$((i - START_NUM + 1)) + + log_info "-----------------------------------------------------" + log_info "开始处理项目 ($current_num/$REPEAT_NUM): $PROJECT_ID" + log_debug "当前循环变量 i=$i, 项目ID=$PROJECT_ID" + log_info "-----------------------------------------------------" + + # 执行所有步骤 + project_success=true + + log_debug "步骤1: 创建项目" + if ! create_project "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then + log_debug "创建项目失败" + project_success=false + fi + + if [ "$project_success" = true ]; then + log_debug "步骤2: 链接结算账号" + if ! link_billing "$PROJECT_ID" "$billing_id" "$MAX_RETRIES" "$RETRY_DELAY"; then + log_debug "链接结算账号失败" + project_success=false + fi + fi + + if [ "$project_success" = true ]; then + log_debug "步骤3: 启用服务" + if ! enable_services "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then + log_debug "启用服务失败" + project_success=false + fi + fi + + if [ "$project_success" = true ]; then + log_debug "步骤4: 创建服务账号" + if ! create_service_account "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then + log_debug "创建服务账号失败" + project_success=false + fi + fi + + if [ "$project_success" = true ]; then + log_debug "步骤5: 添加IAM策略" + if ! add_iam_policy "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then + log_debug "添加IAM策略失败" + project_success=false + fi + fi + + if [ "$project_success" = true ]; then + log_debug "步骤6: 创建服务账号密钥" + if ! create_service_account_key "$PROJECT_ID" "$MAX_RETRIES" "$RETRY_DELAY"; then + log_debug "创建服务账号密钥失败" + project_success=false + fi + fi + + # 统计结果 + if [ "$project_success" = true ]; then + log_success "项目 $PROJECT_ID 所有操作处理完成" + ((successful_projects++)) + else + log_warning "项目 $PROJECT_ID 处理过程中出现错误,部分操作可能未完成。" + ((failed_projects++)) + fi + + # 添加延迟,避免 API 限制 + log_debug "等待2秒后继续下一个项目..." + sleep 2 + + log_debug "完成项目 $PROJECT_ID 的处理,准备处理下一个项目" +done + +log_debug "主循环结束" + +# 显示结果 +log_info "================== 执行完成 ==================" +log_success "成功创建: $successful_projects 个项目" +log_error "创建失败: $failed_projects 个项目" +log_info "详细日志: $LOG_FILE" +echo "================== 创建完成于: $(date) ==================" >> "$LOG_FILE" + +# 显示成功创建的项目列表 +if [ "$successful_projects" -gt 0 ]; then + log_success "成功创建的项目:" + for i in $(seq "$START_NUM" "$END_NUM"); do + PROJECT_ID="${PROJECT_ID_PREFIX}$(printf "%02d" "$i")" + if [ -f "pass-$PROJECT_ID.json" ]; then + log_info " ✓ $PROJECT_ID (密钥文件: pass-$PROJECT_ID.json)" + fi + done +fi + +if [ "$failed_projects" -gt 0 ]; then + log_error "有 $failed_projects 个项目在处理过程中遇到无法恢复的错误。" + log_info "请检查上面的日志以获取详细信息。" + exit 1 +fi + +log_success "所有项目创建/配置完成。" \ No newline at end of file diff --git a/gcp/delete_all_projects.sh b/gcp/delete_all_projects.sh new file mode 100644 index 0000000..7bfbfa6 --- /dev/null +++ b/gcp/delete_all_projects.sh @@ -0,0 +1,139 @@ +#!/bin/bash +# ============================================================================ +# 文件名: delete_all_projects.sh +# 描述: 删除 GCP 账户下的所有项目(危险操作) +# 作者: Cloud Tools Project +# 版本: 2.0.0 +# 警告: 此操作不可逆,所有项目数据将永久丢失 +# ============================================================================ + +set -euo pipefail + +# 获取脚本目录 +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# 加载公共库 +source "${PROJECT_ROOT}/common/logging.sh" +source "${PROJECT_ROOT}/common/error_handler.sh" + +# 创建日志文件 +readonly LOG_FILE="deleted_projects_$(date +%Y%m%d_%H%M%S).log" +log_set_file "$LOG_FILE" + +# +# 获取并展示所有项目 +# +get_and_display_projects() { + # 获取所有项目 + log_warning "获取项目列表..." + local projects + projects=$(gcloud projects list --format="value(projectId)") + + if [ -z "$projects" ]; then + log_warning "没有找到任何项目" + exit 0 + fi + + # 显示所有项目 + log_warning "以下项目将被删除:" + echo "$projects" | nl + echo + + # 统计项目数量 + local project_count + project_count=$(echo "$projects" | wc -l) + log_warning "总计: $project_count 个项目" + + echo "$projects" +} + +# +# 删除单个项目 +# +delete_project() { + local project_id=$1 + local log_file=$2 + + log_info "正在删除项目: $project_id" + + if gcloud projects delete "$project_id" --quiet 2>&1 | tee -a "$log_file"; then + log_success "成功删除项目: $project_id" + echo "$(date): 成功删除项目 $project_id" >> "$log_file" + return 0 + else + log_error "删除项目 $project_id 失败" + echo "$(date): 删除项目 $project_id 失败" >> "$log_file" + return 1 + fi +} + +# +# 主函数 - 删除所有 GCP 项目 +# +main() { + # 第一次警告和确认 + log_error "################################################################################" + log_error "警告:此脚本将删除您 GCP 账户下的所有项目!" + log_error "这是一个不可逆的操作,所有项目数据将永久丢失!" + log_error "################################################################################" + echo + + # 第一次确认 + read -p "您确定要继续吗?输入 'YES' 继续: " confirm1 + if [ "$confirm1" != "YES" ]; then + log_warning "操作已取消" + exit 0 + fi + + # 获取并展示项目列表 + local projects + projects=$(get_and_display_projects) + + # 统计项目数量 + local project_count + project_count=$(echo "$projects" | wc -l) + + # 第二次确认 + log_error "最后确认:您将要删除 $project_count 个项目!" + read -p "输入 'DELETE ALL' 确认删除所有项目: " confirm2 + if [ "$confirm2" != "DELETE ALL" ]; then + log_warning "操作已取消" + exit 0 + fi + + # 开始删除项目 + log_warning "开始删除项目..." + log_info "删除记录将保存到: $LOG_FILE" + echo "================== 删除开始于: $(date) ==================" >> "$LOG_FILE" + + local deleted_count=0 + local failed_count=0 + + for project_id in $projects; do + if delete_project "$project_id" "$LOG_FILE"; then + ((deleted_count++)) + else + ((failed_count++)) + fi + + # 添加短暂延迟,避免 API 限制 + sleep 2 + done + + # 显示结果 + log_info "================== 执行完成 ===================" + log_success "成功删除: $deleted_count 个项目" + log_error "删除失败: $failed_count 个项目" + log_info "详细日志: $LOG_FILE" + echo "================== 删除完成于: $(date) ==================" >> "$LOG_FILE" + + # 如果有删除失败的项目,返回错误状态 + if [ "$failed_count" -gt 0 ]; then + log_error "有 $failed_count 个项目删除失败。请检查日志。" + exit 1 + fi +} + +# 执行主函数 +main "$@" \ No newline at end of file diff --git a/linux/create_raid0_array.sh b/linux/create_raid0_array.sh new file mode 100644 index 0000000..0b98cb8 --- /dev/null +++ b/linux/create_raid0_array.sh @@ -0,0 +1,142 @@ +#!/bin/bash +# ============================================================================ +# 文件名: create_raid0_array.sh +# 描述: 创建并配置 RAID 0 存储阵列 +# 作者: Cloud Tools Project +# 版本: 2.0.0 +# 警告: 此操作将删除目标磁盘上的所有数据 +# ============================================================================ + +set -euo pipefail + +# 获取脚本目录 +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# 加载公共库 +source "${PROJECT_ROOT}/common/logging.sh" +source "${PROJECT_ROOT}/common/error_handler.sh" + +# 配置 +readonly MOUNT_POINT="/mnt/raid0" + +# 检查 root 权限 +check_root + +# +# 检查并安装依赖工具 +# +# 参数: +# $1 - 工具名称 +# +check_and_install() { + local tool=$1 + if ! command -v "$tool" &> /dev/null; then + log_warning "$tool 未安装,正在尝试安装..." + if command -v apt-get &> /dev/null; then + run_command "安装 $tool" bash -c "apt-get update && apt-get install -y $tool" + elif command -v yum &> /dev/null; then + run_command "安装 $tool" yum install -y "$tool" + else + log_error "无法安装 $tool,请手动安装后重试" + exit 1 + fi + fi +} + +# +# 主函数 - 创建 RAID 0 阵列 +# +main() { + log_info "============ RAID 0 创建程序 ============" + + # 检查依赖 + check_and_install mdadm + check_and_install lsblk + log_success "所有依赖已满足" + + # 获取系统盘 + local system_disk + system_disk=$(mount | grep " / " | cut -d' ' -f1 | sed 's/[0-9]*//g') + log_info "检测到系统盘: ${system_disk}" + + # 获取可用磁盘 + local disks + disks=$(lsblk -dno NAME | sed -e '/^loop/d' -e '/^sr/d' -e "/^${system_disk##*/}$/d" | sed 's/^/\/dev\//') + + if [ -z "$disks" ]; then + log_warning "没有找到除系统盘以外的硬盘" + exit 0 + fi + + # 检查磁盘数量 + local disk_count + disk_count=$(echo "$disks" | wc -l) + if [ "$disk_count" -lt 2 ]; then + log_error "至少需要两个磁盘来创建 RAID 0,只找到 $disk_count 个可用磁盘" + exit 1 + fi + + log_info "找到以下磁盘可用于 RAID 0:" + echo "$disks" + + # 确认操作 + echo + read -p "是否继续创建 RAID 0? 这将删除这些磁盘上的所有数据 (y/n): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log_warning "操作已取消" + exit 1 + fi + + # 删除现有 RAID 配置 + mdadm --stop /dev/md0 2>/dev/null || true + mdadm --zero-superblock $disks 2>/dev/null || true + + # 创建 RAID 0 阵列 + log_info "正在创建 RAID 0 阵列..." + run_command "创建 RAID" mdadm --create /dev/md0 --level=0 --raid-devices="$disk_count" $disks + + # 检查状态 + if mdadm --detail /dev/md0 | grep -q 'State : clean'; then + log_success "RAID 0 阵列创建成功" + mdadm --detail /dev/md0 + else + log_error "RAID 0 阵列创建失败" + exit 1 + fi + + # 更新配置 + log_info "正在更新 mdadm.conf..." + mdadm --detail --scan >> /etc/mdadm/mdadm.conf + + # 创建文件系统 + log_info "正在创建 ext4 文件系统..." + run_command "创建文件系统" mkfs.ext4 -F /dev/md0 + + # 创建挂载点 + mkdir -p "$MOUNT_POINT" + + # 获取 UUID + local raid_uuid + raid_uuid=$(blkid -s UUID -o value /dev/md0) + + # 更新 fstab + log_info "正在更新 /etc/fstab..." + echo "UUID=$raid_uuid $MOUNT_POINT ext4 defaults 0 0" >> /etc/fstab + + # 挂载 + log_info "正在挂载 RAID 阵列..." + if mount -a; then + log_success "RAID 0 阵列已成功创建、格式化并挂载" + log_success "挂载点: $MOUNT_POINT" + else + log_error "挂载 RAID 阵列失败" + exit 1 + fi + + log_warning "注意: 建议重启系统以确保所有配置生效" +} + +# 执行主函数 +main "$@" \ No newline at end of file diff --git a/linux/install_oh_my_zsh.sh b/linux/install_oh_my_zsh.sh new file mode 100644 index 0000000..13dcff0 --- /dev/null +++ b/linux/install_oh_my_zsh.sh @@ -0,0 +1,107 @@ +#!/bin/bash +# ============================================================================ +# 文件名: install_oh_my_zsh.sh +# 描述: 安装和配置 Oh My Zsh 及常用插件 +# 作者: Cloud Tools Project +# 版本: 2.0.0 +# ============================================================================ + +set -euo pipefail + +# 获取脚本目录 +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# 加载公共库 +source "${PROJECT_ROOT}/common/logging.sh" +source "${PROJECT_ROOT}/common/error_handler.sh" + +# 配置路径 +readonly INSTALL_DIR="${HOME}/.oh-my-zsh" +readonly ZSH_CONFIG="${HOME}/.zshrc" +readonly ZSH_CUSTOM="${INSTALL_DIR}/custom" + +# +# 检查并安装缺失的软件包 +# +# 参数: +# $@ - 要检查的软件包列表 +# +check_and_install_packages() { + local packages=("$@") + local missing_packages=() + + for package in "${packages[@]}"; do + if ! check_command "$package"; then + missing_packages+=("$package") + fi + done + + if [[ ${#missing_packages[@]} -gt 0 ]]; then + log_warning "缺少以下软件包: ${missing_packages[*]}" + if command -v apt-get &>/dev/null; then + run_command "安装软件包" apt-get update + run_command "安装软件包" apt-get install -y "${missing_packages[@]}" + else + log_error "只支持 apt-get 包管理器" + exit 1 + fi + fi + log_success "所有软件包已安装" +} + +# +# 安装 Oh My Zsh +# +install_zsh() { + log_info "安装 zsh..." + run_command "更新软件包" sudo apt update + run_command "安装 zsh" sudo apt install zsh -y + sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" +} + +# +# 主函数 +# +main() { + log_info "============ Oh My Zsh 安装程序 ============" + + check_and_install_packages "sudo" "git" "curl" "zsh" + + if [ ! -d "$INSTALL_DIR" ]; then + install_zsh + else + log_success "oh-my-zsh 已安装" + fi + + # 安装插件 + if [ ! -d "$ZSH_CUSTOM/plugins/zsh-autosuggestions" ]; then + log_info "安装 zsh-autosuggestions..." + run_command "克隆插件" git clone https://github.com/zsh-users/zsh-autosuggestions "$ZSH_CUSTOM/plugins/zsh-autosuggestions" + fi + + if [ ! -d "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" ]; then + log_info "安装 zsh-syntax-highlighting..." + run_command "克隆插件" git clone https://github.com/zsh-users/zsh-syntax-highlighting.git "$ZSH_CUSTOM/plugins/zsh-syntax-highlighting" + fi + + log_success "安装完成" + + # 配置 .zshrc + log_info "修改 .zshrc 文件..." + cp "$ZSH_CONFIG" "$ZSH_CONFIG.bak" + sed -i 's/plugins=(git)/plugins=(git zsh-autosuggestions zsh-syntax-highlighting extract)/' "$ZSH_CONFIG" + sed -i 's/ZSH_THEME="robbyrussell"/ZSH_THEME="random"/' "$ZSH_CONFIG" + log_success "配置完成" + + # 设置默认 shell + log_info "设置 zsh 为默认 shell..." + chsh -s /bin/zsh + log_success "设置完成" + + log_info "重启 zsh..." + zsh +} + +# 执行主函数 +main "$@" \ No newline at end of file diff --git a/linux/repartition_disks.sh b/linux/repartition_disks.sh new file mode 100644 index 0000000..4342c54 --- /dev/null +++ b/linux/repartition_disks.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# ============================================================================ +# 文件名: repartition_disks.sh +# 描述: 删除所有现有分区并创建新的 GPT 分区表(除了系统盘) +# 作者: Cloud Tools Project +# 版本: 2.0.0 +# 警告: 此操作将删除目标磁盘上的所有数据 +# ============================================================================ + +set -euo pipefail + +# 获取脚本目录 +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# 加载公共库 +source "${PROJECT_ROOT}/common/logging.sh" +source "${PROJECT_ROOT}/common/error_handler.sh" + +# 检查 root 权限 +check_root + +# +# 检查并安装依赖工具 +# +# 参数: +# $1 - 工具名称 +# +check_and_install() { + local tool=$1 + if ! command -v "$tool" &> /dev/null; then + log_warning "$tool 未安装,正在尝试安装..." + if command -v apt-get &> /dev/null; then + run_command "安装 $tool" bash -c "apt-get update && apt-get install -y $tool" + elif command -v yum &> /dev/null; then + run_command "安装 $tool" yum install -y "$tool" + else + log_error "无法安装 $tool,请手动安装后重试" + exit 1 + fi + fi +} + +# +# 主函数 - 重新分区所有非系统盘 +# +main() { + log_info "============ 磁盘重新分区程序 ============" + + # 检查依赖 + check_and_install lsblk + check_and_install parted + check_and_install wipefs + log_success "所有依赖已满足" + + # 获取系统盘 + local system_disk + system_disk=$(mount | grep " / " | cut -d' ' -f1 | sed 's/[0-9]*//g') + log_info "检测到系统盘: ${system_disk}" + + # 获取可用磁盘 + local disks + disks=$(lsblk -dno NAME | sed -e '/^loop/d' -e '/^sr/d' -e "/^${system_disk##*/}$/d") + + if [ -z "$disks" ]; then + log_warning "没有找到除系统盘以外的硬盘" + exit 0 + fi + + # 处理每个磁盘 + for disk in $disks; do + log_info "正在处理硬盘: /dev/${disk}" + + log_warning "删除 /dev/${disk} 上的所有分区..." + run_command "清除分区签名" wipefs -a "/dev/${disk}" + + log_warning "在 /dev/${disk} 上创建新的 GPT 分区表..." + run_command "创建分区表" parted -s "/dev/${disk}" mklabel gpt + + log_warning "在 /dev/${disk} 上创建新分区..." + run_command "创建分区" parted -s "/dev/${disk}" mkpart primary 0% 100% + + log_info "刷新分区表..." + run_command "刷新分区" partprobe "/dev/${disk}" + + log_success "完成处理 /dev/${disk}" + echo + done + + log_success "所有操作完成" +} + +# 执行主函数 +main "$@" \ No newline at end of file diff --git a/oci/create_instance.sh b/oci/create_instance.sh new file mode 100644 index 0000000..cce90b3 --- /dev/null +++ b/oci/create_instance.sh @@ -0,0 +1,389 @@ +#!/bin/bash +# ============================================================================ +# 文件名: create_instance.sh +# 描述: Oracle Cloud Infrastructure 虚拟机批量部署工具 +# 作者: Cloud Tools Project +# 版本: 2.0.0 +# ============================================================================ + +set -euo pipefail + +# 获取脚本目录 +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +# 加载公共库 +source "${PROJECT_ROOT}/common/logging.sh" +source "${PROJECT_ROOT}/common/error_handler.sh" + +# 默认配置参数 +readonly DEFAULT_NUMBER=1 +readonly DEFAULT_SHAPE="VM.Standard.A1.Flex" +readonly DEFAULT_SHAPE_CONFIG="1+6" +readonly DEFAULT_IMAGE_NAME="Canonical-Ubuntu-20.04-aarch64-2025.05.20-0" +readonly DEFAULT_BOOT_VOLUME_SIZE=0 +readonly DEFAULT_VPU=120 +readonly DEFAULT_DOMAIN=0 + +# +# 检查必要的依赖工具是否安装 +# +check_dependencies() { + local deps=("oci" "jq" "base64" "mkfifo") + local missing=() + + for cmd in "${deps[@]}"; do + if ! check_command "$cmd"; then + missing+=("$cmd") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + log_error "缺少必要依赖: ${missing[*]}" + exit 1 + fi + log_success "运行环境检查通过" +} + +# +# 生成随机安全密码 +# +# 返回: +# 16位随机密码 +# +generate_password() { + LC_ALL=C tr -dc 'A-Za-z0-9!#%&()*+,-.:;<=>?@[]^_~' /dev/null) + + check_not_empty "$vcn_id" "vcn_id" + log_success "VCN 创建成功 (ID: $vcn_id)" + + # 生成 IPv6 CIDR + log_info "正在生成 IPv6 CIDR..." + local ipv6_cidr + ipv6_cidr=$(oci network vcn get --vcn-id "$vcn_id" --query 'data."ipv6-cidr-blocks"[0]' --raw-output) + ipv6_cidr="${ipv6_cidr%00::*}$((RANDOM%90+10))::/64" + log_success "IPv6 CIDR 生成成功: $ipv6_cidr" + + # 创建子网 + log_info "正在创建子网 (DNS: $subnet_dns)" + local subnet_id + subnet_id=$(OCI_CLI_SUPPRESS_JSON_OUTPUT=true oci network subnet create \ + --compartment-id "$ocid" \ + --vcn-id "$vcn_id" \ + --cidr-block "10.0.0.0/16" \ + --ipv6-cidr-block "$ipv6_cidr" \ + --dns-label "$subnet_dns" \ + --query 'data.id' \ + --raw-output 2>/dev/null) + + check_not_empty "$subnet_id" "subnet_id" + log_success "子网创建成功 (ID: $subnet_id)" + + # 配置网关 + log_info "正在配置网络网关..." + local gateway_id + gateway_id=$(oci network internet-gateway create \ + --compartment-id "$ocid" \ + --vcn-id "$vcn_id" \ + --is-enabled true \ + --query 'data.id' \ + --raw-output 2>/dev/null) + + check_return $? "网关创建失败" + log_success "网关创建成功 (ID: $gateway_id)" + + # 配置路由表 + log_info "正在配置路由表..." + local rt_id + rt_id=$(oci network route-table list \ + --compartment-id "$ocid" \ + --vcn-id "$vcn_id" \ + --query 'data[0].id' \ + --raw-output 2>/dev/null) + + check_not_empty "$rt_id" "rt_id" + + oci network route-table update --rt-id "$rt_id" \ + --route-rules "[ + {\"cidrBlock\":\"0.0.0.0/0\",\"networkEntityId\":\"$gateway_id\"}, + {\"cidrBlock\":\"0:0:0:0:0:0:0:0/0\",\"networkEntityId\":\"$gateway_id\"} + ]" --force >/dev/null + + check_return $? "路由配置失败" + log_success "路由表配置成功" + + # 配置安全组 + log_info "正在配置安全组规则..." + local sg_id + sg_id=$(oci network security-list list \ + --compartment-id "$ocid" \ + --vcn-id "$vcn_id" \ + --query 'data[0].id' \ + --raw-output 2>/dev/null) + + oci network security-list update --security-list-id "$sg_id" \ + --ingress-security-rules '[]' --force >/dev/null + check_return $? "安全组清空失败" + + oci network security-list update --security-list-id "$sg_id" \ + --ingress-security-rules "[ + {\"protocol\":\"all\",\"source\":\"0.0.0.0/0\",\"sourceType\":\"CIDR_BLOCK\"}, + {\"protocol\":\"all\",\"source\":\"::/0\",\"sourceType\":\"CIDR_BLOCK\"} + ]" --force >/dev/null + + check_return $? "安全组规则更新失败" + log_success "安全组规则配置成功" + + # 返回子网ID通过 FIFO + echo "$subnet_id" > "$fifo" +} + +# +# 显示帮助信息 +# +show_help() { + cat < 创建实例数量 (默认: $DEFAULT_NUMBER) + --ocid 区域ID (自动获取) + --shape <形状> 实例规格 (默认: $DEFAULT_SHAPE) + --shape_config <配置> 资源配置格式: CPU+内存[+基线] (默认: $DEFAULT_SHAPE_CONFIG) + --subnet_id <子网ID> 指定现有子网ID (自动创建) + --image_name <镜像名> 系统镜像名称 (默认: $DEFAULT_IMAGE_NAME) + --vpu <数值> 启动卷性能单位 (默认: $DEFAULT_VPU) + --boot_volume_size 启动卷大小 (默认: 自动) + --domain <索引> 可用域序号 (默认: $DEFAULT_DOMAIN) + --password <密码> 指定 root 密码 (默认自动生成) + -h, --help 显示本帮助信息 + +示例: + $0 -n 3 --shape VM.Standard.E4.Flex --shape_config 2+16 +EOF +} + +# +# 主函数 - 批量创建 OCI 实例 +# +main() { + log_info "============ OCI 实例批量部署工具 ============" + + # 参数初始化 + local number=$DEFAULT_NUMBER + local shape=$DEFAULT_SHAPE + local shape_config=$DEFAULT_SHAPE_CONFIG + local image_name=$DEFAULT_IMAGE_NAME + local boot_size=$DEFAULT_BOOT_VOLUME_SIZE + local vpu=$DEFAULT_VPU + local domain=$DEFAULT_DOMAIN + local ocid password subnet_id + + # 参数解析 + while [[ $# -gt 0 ]]; do + case "$1" in + -n|--number) number="$2"; shift 2 ;; + --ocid) ocid="$2"; shift 2 ;; + --shape) shape="$2"; shift 2 ;; + --shape_config) shape_config="$2"; shift 2 ;; + --subnet_id) subnet_id="$2"; shift 2 ;; + --image_name) image_name="$2"; shift 2 ;; + --vpu) vpu="$2"; shift 2 ;; + --boot_volume_size) boot_size="$2"; shift 2 ;; + --domain) domain="$2"; shift 2 ;; + --password) password="$2"; shift 2 ;; + -h|--help) show_help; exit 0 ;; + *) log_error "无效参数: $1"; exit 1 ;; + esac + done + + # 环境检查 + check_dependencies + + # 自动生成密码 + if [[ -z "${password:-}" ]]; then + password=$(generate_password) + fi + log_warning "使用密码: $password" + + # OCID 自动获取 + if [[ -z "${ocid:-}" ]]; then + log_info "正在获取 OCID..." + ocid=$(oci iam availability-domain list --query 'data[0]."compartment-id"' --raw-output 2>/dev/null) + check_not_empty "$ocid" "ocid" + fi + log_success "OCID 获取成功: $ocid" + + # 网络配置 + if [[ -z "${subnet_id:-}" ]]; then + log_info "正在配置网络..." + local fifo + fifo=$(mktemp -u) + mkfifo "$fifo" + trap 'rm -f "$fifo"' EXIT + configure_network "$ocid" "$fifo" & + read subnet_id < "$fifo" + log_success "网络配置完成 (子网ID: $subnet_id)" + else + log_info "使用指定的子网ID: $subnet_id" + fi + + # 生成配置 + local shape_cfg + shape_cfg=$(generate_shape_config "$shape_config") + + local image_id + image_id=$(oci compute image list \ + --compartment-id "$ocid" \ + --display-name "$image_name" \ + --query 'data[0].id' \ + --raw-output 2>/dev/null) + check_return $? "镜像查询失败" + + local source_cfg + if [[ $boot_size -eq 0 ]] || [[ $boot_size -lt 50 ]]; then + source_cfg="{\"sourceType\":\"image\",\"imageId\":\"$image_id\",\"bootVolumeVpusPerGB\":$vpu}" + else + source_cfg="{\"sourceType\":\"image\",\"imageId\":\"$image_id\",\"bootVolumeVpusPerGB\":$vpu,\"bootVolumeSizeInGBs\":$boot_size}" + fi + + local metadata="{\"user_data\":\"$(generate_user_data "$password")\"}" + local tags="{\"RootPassword\":\"$password\"}" + + # 获取可用域 + log_info "正在获取可用域列表..." + local domains + domains=$(oci iam availability-domain list \ + --compartment-id "$ocid" \ + --query 'data[*].name' \ + --raw-output 2>/dev/null) + check_return $? "无法获取可用域" + + domains=$(echo "$domains" | jq -c | tr -d '[]"' | tr ',' ' ') + read -ra domains <<< "$domains" + + if [[ ${#domains[@]} -eq 0 ]]; then + log_error "未找到可用域" + exit 1 + fi + + local target_domain="${domains[$domain]}" + log_success "选择可用域: $target_domain" + + # 批量创建实例 + log_info "开始批量创建实例 (数量: $number)..." + local instance_id + + for ((i=1; i<=number; i++)); do + log_info "正在创建第 $i/$number 个实例..." + + instance_id=$(oci compute instance launch \ + --compartment-id "$ocid" \ + --display-name "instance-$(date +%Y%m%d-%H%M%S)" \ + --shape "$shape" \ + --shape-config "$shape_cfg" \ + --subnet-id "$subnet_id" \ + --assign-ipv6-ip true \ + --source-details "$source_cfg" \ + --availability-domain "$target_domain" \ + --metadata "$metadata" \ + --freeform-tags "$tags" \ + --query 'data."id"' \ + --raw-output) + + check_return $? "实例创建失败" + log_success "实例 $i/$number 创建成功 (ID: $instance_id)" + + # 随机延迟避免 API 限制 + sleep $((RANDOM % 5 + 1)) + done + + log_success "所有实例部署完成!" +} + +# 执行主函数 +main "$@" \ No newline at end of file