liuziyi

liuziyi

分享一个oracle全版本的RAC部署脚本

> #!/bin/bash
> # =============================================================================
> #  oracle_deploy.sh  v2.1
> #  Oracle 一键部署:single(单实例/文件系统)| standalone(单实例+GI+ASM)| rac
> #  版本:11G / 12C(12.1) / 12.2 / 18C / 19C / 21C / 23AI
> # -----------------------------------------------------------------------------
> #  说明:
> #   1. 版本差异集中在 load_profile(),安装主流程与版本解耦;
> #   2. 参数支持任意顺序、-k=v 与 -k v 两种写法,并按模式校验必填项;
> #   3. OS 初始化脚本幂等,单机与 RAC 共用同一份;
> #   4. 响应文件统一由函数生成;
> #   5. 安装等待改为精确匹配+超时,避免死循环。
> # =============================================================================
> set -o pipefail

> SOFT_DIR=${SOFT_DIR:-/soft}
> ORA_BASE=/u01/app/oracle
> GRID_BASE=/u01/app/grid
> INVENTORY=/u01/app/oraInventory
> ASM_DISK_DIR=/dev/oracleasm/disks

> DB_TYPE=""; OP="install"; HA=""; VER=""
> PUBLIC_IP=""; NODE_NAME=""; ROOT_PWD=""; DB_PWD=""; DB_SID=""
> USE_CDB="false"; PDB_NAME=""; DB_CHARSET=""; DB_NCHARSET=""; YUM_SRC=""
> PUB1=""; PUB2=""; VIP1=""; VIP2=""; PRIV1=""; PRIV2=""; SCAN_IP=""
> PUB_IF=""; PRIV_IF=""; OCR_REDU=""; OCR_DISKS=""; DATA_REDU=""; DATA_DISKS=""
> DB_ZIP_OVERRIDE=""; GRID_ZIP_OVERRIDE=""

> ORA_NUMBER=""; ORA_NUMBER_S=""; ORACLE_HOME=""; GRID_HOME=""
> DB_ZIP=""; GRID_ZIP=""; DB_MEDIA_MODE=""; GRID_MEDIA_MODE=""
> AU_SIZE=""; SUPPORTS_CDB=0; CDB_REQUIRED=0
> RSP_DB_SCHEMA=""; RSP_GRID_SCHEMA=""; RSP_DBCA_SCHEMA=""
> MIN_MEM_SINGLE=""; MIN_MEM_RAC=""; IGNORE_FLAG=""; YUM_CMD=""

> PROG=$(basename "$0")
> LOG_FILE=${LOG_FILE:-/tmp/oracle_deploy_$(date +%Y%m%d_%H%M%S).log}
> START_TIME=$(date +%s)

> log(){
>     local msg
>     msg="$(date '+%F %T') $*"
>     printf '%s\n' "$msg"
>     printf '%s\n' "$msg" >>"$LOG_FILE" 2>/dev/null || true
> }
> die(){ log "ERROR: $*"; exit 1; }
> run_as(){ local u=$1; shift; su - "$u" -c "$(printf '%q ' "$@")"; }
> cidr24(){ echo "$1" | awk -F. '{print $1"."$2"."$3".0"}'; }

> cleanup(){
>     local rc=$?
>     if (( rc != 0 )); then
>         log "脚本异常退出 (rc=$rc),日志: $LOG_FILE"
>     else
>         log "脚本执行完成,耗时 $(( $(date +%s) - START_TIME ))s,日志: $LOG_FILE"
>     fi
> }
> trap cleanup EXIT

> # -----------------------------------------------------------------------------
> # 帮助
> # -----------------------------------------------------------------------------
> usage(){
> cat <<EOF
> Usage: ${PROG} -dt=oracle -op=install -ht=<mode> -v=<version> [options]
>        ${PROG} -h | --help | -help | help

>   -ht  single     单实例(文件系统)
>        standalone 单实例 + Grid Infrastructure + ASM(原 ha)
>        rac        两节点 RAC

>   -v   11G | 12C | 12.2 | 18C | 19C | 21C | 23AI
>        11G / 12C 使用解压式安装介质,12.2 及以上使用 image 方式

> 通用参数:
>   -i   <public_ip>        主机 IP
>   -n   <name>             主机名前缀(自动追加 db / db1、db2)
>   -dp  <password>         oracle/grid 系统用户口令,SYS/SYSTEM 等口令
>   -o   <oracle_sid>       数据库 SID
>   -cdb <true|false>       是否 CDB(11G 忽略;21C/23AI 强制 TRUE)
>   -pdbname <name>         PDB 名称(CDB 时默认 pdb)
>   -cs  <charset>          字符集,如 ZHS16GBK / AL32UTF8
>   -ncs <ncharset>         国家字符集,如 AL16UTF16 / UTF8
>   -yum </dev/cdrom|xxx.iso>  本地 yum 源

> RAC / standalone 专用:
>   -rp  <root_password>    节点 root 口令(用于配置互信,RAC 必填)
>   -pu1/-pu2 <ip>          node1/node2 公网 IP
>   -vi1/-vi2 <ip>          node1/node2 VIP(兼容旧写法 -vip1/-vip2)
>   -pr1/-pr2 <ip>          node1/node2 私网 IP
>   -si  <scan_ip>          SCAN IP
>   -puf <ifname> -prf <ifname>  公网/私网网卡名
>   -or  <HIGH|NORMAL|EXTERNAL>  OCR 冗余
>   -od  </dev/sdb,...>     OCR 磁盘
>   -dr  <HIGH|NORMAL|EXTERNAL>  数据盘冗余
>   -dd  </dev/sde,...>     数据磁盘

> 可选覆盖:
>   -dbzip   "a.zip b.zip"  覆盖默认数据库安装介质文件名
>   -gridzip "g.zip"        覆盖默认 Grid 安装介质文件名

> 日志:
>   可通过环境变量 LOG_FILE 覆盖日志路径,默认 /tmp/oracle_deploy_<时间戳>.log

> 示例(19C 单实例):
>   ./${PROG} -dt=oracle -op=install -ht=single -v=19C \\
>     -i=192.168.220.100 -n=orcl -dp=Enmo_1234 -o=orcl \\
>     -cdb=false -pdbname=pdb -cs=ZHS16GBK -ncs=AL16UTF16 -yum=/dev/cdrom

> 示例(19C RAC):
>   ./${PROG} -dt=oracle -op=install -ht=rac -v=19C \\
>     -i=192.168.220.100 -n=enmo -rp=123456 -dp=Enmo_1234 -o=orcl \\
>     -cdb=true -pdbname=pdb -cs=ZHS16GBK -ncs=AL16UTF16 -yum=/dev/cdrom \\
>     -pu1=192.168.220.100 -pu2=192.168.220.102 \\
>     -vi1=192.168.220.103 -vi2=192.168.220.104 \\
>     -pr1=192.168.220.100 -pr2=192.168.220.102 -si=192.168.220.240 \\
>     -puf=eth0 -prf=eth1 -or=EXTERNAL -od=/dev/sdb,/dev/sdc \\
>     -dr=EXTERNAL -dd=/dev/sde,/dev/sdf
> EOF
> }

> # -----------------------------------------------------------------------------
> # 参数解析
> # -----------------------------------------------------------------------------
> is_known_opt(){
>     case $1 in
>         -dt|--db-type|-op|--operation|-ht|--ha-type|-v|--version|-i|--ip|-n|--name|\
>         -rp|-dp|-o|-cdb|-pdbname|-cs|-ncs|-yum|-pu1|-pu2|-vi1|-vi2|-vip1|-vip2|\
>         -pr1|-pr2|-si|-scan|-puf|-prf|-or|-od|-dr|-dd|-dbzip|-gridzip|-h|--help|-help)
>             return 0 ;;
>         *) return 1 ;;
>     esac
> }

> set_param(){
>     local k=$1 v=$2
>     case $k in
>         -dt|--db-type)      DB_TYPE=$v ;;
>         -op|--operation)    OP=$v ;;
>         -ht|--ha-type)      HA=$v ;;
>         -v|--version)       VER=$v ;;
>         -i|--ip)            PUBLIC_IP=$v ;;
>         -n|--name)          NODE_NAME=$v ;;
>         -rp)                ROOT_PWD=$v ;;
>         -dp)                DB_PWD=$v ;;
>         -o)                 DB_SID=$v ;;
>         -cdb)               USE_CDB=$v ;;
>         -pdbname)           PDB_NAME=$v ;;
>         -cs)                DB_CHARSET=$v ;;
>         -ncs)               DB_NCHARSET=$v ;;
>         -yum)               YUM_SRC=$v ;;
>         -pu1)               PUB1=$v ;;
>         -pu2)               PUB2=$v ;;
>         -vi1|-vip1)         VIP1=$v ;;
>         -vi2|-vip2)         VIP2=$v ;;
>         -pr1)               PRIV1=$v ;;
>         -pr2)               PRIV2=$v ;;
>         -si|-scan)          SCAN_IP=$v ;;
>         -puf)               PUB_IF=$v ;;
>         -prf)               PRIV_IF=$v ;;
>         -or)                OCR_REDU=$v ;;
>         -od)                OCR_DISKS=$v ;;
>         -dr)                DATA_REDU=$v ;;
>         -dd)                DATA_DISKS=$v ;;
>         -dbzip)             DB_ZIP_OVERRIDE=$v ;;
>         -gridzip)           GRID_ZIP_OVERRIDE=$v ;;
>         *)                  die "未知参数: $k" ;;
>     esac
> }

> parse_args(){
>     (( $# == 0 )) && { usage; exit 1; }
>     while (( $# )); do
>         case $1 in
>             -h|--help|-help|help) usage; exit 0 ;;
>             --) shift; break ;;
>             -*=*)
>                 set_param "${1%%=*}" "${1#*=}"; shift ;;
>             -*)
>                 (( $# >= 2 )) || die "参数 $1 缺少取值"
>                 is_known_opt "$2" && die "参数 $1 缺少取值(下一个是参数 $2)"
>                 set_param "$1" "$2"; shift 2 ;;
>             *) die "无法识别的参数: $1" ;;
>         esac
>     done
>     (( $# == 0 )) || die "存在无法解析的尾部参数: $*"
> }

> # -----------------------------------------------------------------------------
> # 参数规范化与校验
> # -----------------------------------------------------------------------------
> normalize_inputs(){
>     DB_TYPE=${DB_TYPE,,}; OP=${OP,,}; HA=${HA,,}; VER=${VER^^}

>     [[ -n $DB_TYPE ]] || { usage; die "必须指定 -dt=oracle"; }
>     [[ $DB_TYPE == oracle ]] || die "本版本仅支持 -dt=oracle"
>     [[ $OP == install ]]     || die "当前仅支持 -op=install"

>     case $HA in
>         single)                  ;;
>         standalone|ha)  HA=standalone ;;
>         rac)                     ;;
>         *) die "-ht 仅支持 single / standalone / rac" ;;
>     esac
> }

> load_profile(){
>     case $VER in
>         11G|11.2|11.2.0|112)
>             VER=11G
>             ORA_NUMBER=11.2.0; ORA_NUMBER_S=11.2
>             RSP_DB_SCHEMA=v11_2_0; RSP_GRID_SCHEMA=v11_2_0; RSP_DBCA_SCHEMA=11.2.0
>             DB_MEDIA_MODE=extract; GRID_MEDIA_MODE=extract
>             DB_ZIP="p13390677_112040_Linux-x86-64_1of7.zip p13390677_112040_Linux-x86-64_2of7.zip"
>             GRID_ZIP="p13390677_112040_Linux-x86-64_3of7.zip"
>             AU_SIZE=1; SUPPORTS_CDB=0; CDB_REQUIRED=0
>             MIN_MEM_SINGLE=2048; MIN_MEM_RAC=2560
>             IGNORE_FLAG="-ignoreSysPrereqs -ignorePrereq" ;;
>         12C|12.1|121)
>             VER=12C
>             ORA_NUMBER=12.1.0; ORA_NUMBER_S=12.1
>             RSP_DB_SCHEMA=v12_1_0; RSP_GRID_SCHEMA=v12_1_0; RSP_DBCA_SCHEMA=12.1.0
>             DB_MEDIA_MODE=extract; GRID_MEDIA_MODE=extract
>             DB_ZIP="linuxamd64_12102_database_1of2.zip linuxamd64_12102_database_2of2.zip"
>             GRID_ZIP="linuxamd64_12102_grid_1of2.zip linuxamd64_12102_grid_2of2.zip"
>             AU_SIZE=4; SUPPORTS_CDB=1; CDB_REQUIRED=0
>             MIN_MEM_SINGLE=2048; MIN_MEM_RAC=4096
>             IGNORE_FLAG="-ignoreSysPrereqs -ignorePrereq" ;;
>         12.2|122)
>             VER=12.2
>             ORA_NUMBER=12.2.0; ORA_NUMBER_S=12.2
>             RSP_DB_SCHEMA=v12_2_0; RSP_GRID_SCHEMA=v12_2_0; RSP_DBCA_SCHEMA=12.2.0
>             DB_MEDIA_MODE=image; GRID_MEDIA_MODE=image
>             DB_ZIP=LINUX.X64_122010_db_home.zip; GRID_ZIP=LINUX.X64_122010_grid_home.zip
>             AU_SIZE=4; SUPPORTS_CDB=1; CDB_REQUIRED=0
>             MIN_MEM_SINGLE=2048; MIN_MEM_RAC=4096
>             IGNORE_FLAG="-ignorePrereqFailure" ;;
>         18C|18.0|18)
>             VER=18C
>             ORA_NUMBER=18.0.0; ORA_NUMBER_S=18.0
>             RSP_DB_SCHEMA=v18.0.0; RSP_GRID_SCHEMA=v18.0.0; RSP_DBCA_SCHEMA=18.0.0
>             DB_MEDIA_MODE=image; GRID_MEDIA_MODE=image
>             DB_ZIP=LINUX.X64_180000_db_home.zip; GRID_ZIP=LINUX.X64_180000_grid_home.zip
>             AU_SIZE=4; SUPPORTS_CDB=1; CDB_REQUIRED=0
>             MIN_MEM_SINGLE=2048; MIN_MEM_RAC=8192
>             IGNORE_FLAG="-ignorePrereqFailure" ;;
>         19C|19.0|19)
>             VER=19C
>             ORA_NUMBER=19.3.0; ORA_NUMBER_S=19.0
>             RSP_DB_SCHEMA=v19.0.0; RSP_GRID_SCHEMA=v19.0.0; RSP_DBCA_SCHEMA=19.0.0
>             DB_MEDIA_MODE=image; GRID_MEDIA_MODE=image
>             DB_ZIP=LINUX.X64_193000_db_home.zip; GRID_ZIP=LINUX.X64_193000_grid_home.zip
>             AU_SIZE=4; SUPPORTS_CDB=1; CDB_REQUIRED=0
>             MIN_MEM_SINGLE=2048; MIN_MEM_RAC=8192
>             IGNORE_FLAG="-ignorePrereqFailure" ;;
>         21C|21.0|21)
>             VER=21C
>             ORA_NUMBER=21.3.0; ORA_NUMBER_S=21.0
>             RSP_DB_SCHEMA=v21.0.0; RSP_GRID_SCHEMA=v21.0.0; RSP_DBCA_SCHEMA=21.0.0
>             DB_MEDIA_MODE=image; GRID_MEDIA_MODE=image
>             DB_ZIP=LINUX.X64_213000_db_home.zip; GRID_ZIP=LINUX.X64_213000_grid_home.zip
>             AU_SIZE=4; SUPPORTS_CDB=1; CDB_REQUIRED=1
>             MIN_MEM_SINGLE=2048; MIN_MEM_RAC=8192
>             IGNORE_FLAG="-ignorePrereqFailure" ;;
>         23AI|23C|23.0|23)
>             VER=23AI
>             ORA_NUMBER=23.0.0; ORA_NUMBER_S=23.0
>             RSP_DB_SCHEMA=v23.0.0; RSP_GRID_SCHEMA=v23.0.0; RSP_DBCA_SCHEMA=23.0.0
>             DB_MEDIA_MODE=image; GRID_MEDIA_MODE=image
>             DB_ZIP=LINUX.X64_230000_db_home.zip; GRID_ZIP=LINUX.X64_230000_grid_home.zip
>             AU_SIZE=4; SUPPORTS_CDB=1; CDB_REQUIRED=1
>             MIN_MEM_SINGLE=2048; MIN_MEM_RAC=8192
>             IGNORE_FLAG="-ignorePrereqFailure" ;;
>         *) die "不支持的版本: $VER(支持 11G/12C/12.2/18C/19C/21C/23AI)" ;;
>     esac

>     [[ -n $DB_ZIP_OVERRIDE ]]   && DB_ZIP=$DB_ZIP_OVERRIDE
>     [[ -n $GRID_ZIP_OVERRIDE ]] && GRID_ZIP=$GRID_ZIP_OVERRIDE

>     ORACLE_HOME=${ORA_BASE}/product/${ORA_NUMBER}/dbhome_1
>     GRID_HOME=/u01/app/${ORA_NUMBER}/grid
> }

> require(){ [[ -n $2 ]] || die "缺少必填参数: $1"; }

> validate_params(){
>     require "-i"   "$PUBLIC_IP"
>     require "-n"   "$NODE_NAME"
>     require "-dp"  "$DB_PWD"
>     require "-o"   "$DB_SID"
>     require "-cs"  "$DB_CHARSET"
>     require "-ncs" "$DB_NCHARSET"
>     require "-yum" "$YUM_SRC"

>     [[ $DB_SID    =~ ^[A-Za-z][A-Za-z0-9_#$]{0,7}$ ]] || die "SID 非法: $DB_SID"
>     [[ $NODE_NAME =~ ^[A-Za-z][A-Za-z0-9-]*$       ]] || die "主机名前缀非法: $NODE_NAME"

>     if [[ $HA != single ]]; then
>         require "-or" "$OCR_REDU";  require "-od" "$OCR_DISKS"
>         require "-dr" "$DATA_REDU"; require "-dd" "$DATA_DISKS"
>     fi

>     if [[ $HA == rac ]]; then
>         require "-rp"  "$ROOT_PWD"
>         require "-pu1" "$PUB1";  require "-pu2" "$PUB2"
>         require "-vi1" "$VIP1";  require "-vi2" "$VIP2"
>         require "-pr1" "$PRIV1"; require "-pr2" "$PRIV2"
>         require "-si"  "$SCAN_IP"
>         require "-puf" "$PUB_IF"; require "-prf" "$PRIV_IF"
>         [[ $NODE_NAME == *[0-9] ]] && log "WARN: -n 末尾含数字,RAC 主机名将拼接为 ${NODE_NAME}db1/${NODE_NAME}db2"
>     fi

>     if (( SUPPORTS_CDB == 0 )); then
>         [[ ${USE_CDB,,} == true ]] && log "WARN: $VER 不支持 CDB,已强制 -cdb=false"
>         USE_CDB=FALSE; PDB_NAME=""
>     else
>         case ${USE_CDB,,} in
>             true|yes|1) USE_CDB=TRUE ;;
>             *)          USE_CDB=FALSE ;;
>         esac
>         (( CDB_REQUIRED == 1 )) && USE_CDB=TRUE
>         if [[ $USE_CDB == TRUE ]]; then
>             PDB_NAME=${PDB_NAME:-pdb}
>         else
>             PDB_NAME=""
>         fi
>     fi
> }

> resolve_yum_cmd(){
>     case $YUM_SRC in
>         /dev/cdrom) YUM_CMD="mount /dev/cdrom /mnt" ;;
>         *.iso)      [[ -f $YUM_SRC ]] || die "yum 镜像不存在: $YUM_SRC"
>                     YUM_CMD="mount -o loop $YUM_SRC /mnt" ;;
>         *)          die "-yum 仅支持 /dev/cdrom 或本地 iso 文件" ;;
>     esac
> }

> # -----------------------------------------------------------------------------
> # 预检查
> # -----------------------------------------------------------------------------
> os_major(){
>     if [[ -r /etc/redhat-release ]]; then
>         grep -oE '[0-9]+' /etc/redhat-release | head -1
>     elif [[ -r /etc/os-release ]]; then
>         ( . /etc/os-release; echo "${VERSION_ID%%.*}" )
>     else
>         echo 0
>     fi
> }

> precheck(){
>     log "===== 环境预检查 ====="
>     [[ $EUID -eq 0 ]] || die "请以 root 运行"
>     [[ $NODE_NAME =~ ^localhost ]] && die "hostname 非法: $NODE_NAME"
>     [[ $PUBLIC_IP == 127.0.0.1 ]] && die "IP 非法: $PUBLIC_IP"

>     local major; major=$(os_major)
>     case $major in
>         7|8|9) log "OS major version: $major" ;;
>         *)     die "仅支持 RHEL/OEL 7/8/9,当前: $major" ;;
>     esac
>     [[ $VER == 23AI && $major -lt 8 ]] && log "WARN: 23AI 建议部署在 OL8/OL9 上"

>     local mem_mb; mem_mb=$(awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo)
>     local need=$MIN_MEM_SINGLE
>     [[ $HA == rac ]] && need=$MIN_MEM_RAC
>     (( mem_mb >= need )) || die "内存不足: 需要 ${need}MB,当前 ${mem_mb}MB"

>     if id oracle >/dev/null 2>&1; then die "oracle 用户已存在,疑似已安装"; fi
>     [[ -d $ORA_BASE ]] && die "$ORA_BASE 已存在,疑似已安装"

>     local tz; tz=$(timedatectl 2>/dev/null | awk -F': ' '/Time zone/{print $2}' | awk '{print $1}')
>     [[ -n $tz && $tz != Asia/Shanghai ]] && die "时区为 $tz,请先设置为 Asia/Shanghai"

>     mountpoint -q /mnt 2>/dev/null && umount /mnt >/dev/null 2>&1 || true
>     eval "$YUM_CMD" >/dev/null 2>&1 || die "无法挂载 yum 源: $YUM_CMD"
>     umount /mnt >/dev/null 2>&1 || true

>     local f
>     for f in $DB_ZIP; do
>         [[ -f ${SOFT_DIR}/$f ]] || die "缺少数据库介质: ${SOFT_DIR}/$f"
>     done
>     if [[ $HA != single ]]; then
>         for f in $GRID_ZIP; do
>             [[ -f ${SOFT_DIR}/$f ]] || die "缺少 Grid 介质: ${SOFT_DIR}/$f"
>         done
>     fi
>     log "预检查通过"
> }

> # -----------------------------------------------------------------------------
> # OS 初始化脚本(生成一次,单机/RAC 共用,幂等)
> # -----------------------------------------------------------------------------
> gen_setos(){
>     cat > "${SOFT_DIR}/setos.sh" <<'EOS'
> #!/bin/bash
> # generated by oracle_deploy.sh -- 幂等 OS 初始化
> set -u
> say(){ echo "[setos] $*"; }
> : "${ROLE:=primary}"
> : "${NODE_SUFFIX:=1}"

> # 1. hosts
> if [[ "${HA}" == "rac" ]]; then
>     sed -i '/^# >>> oradb hosts/,/^# <<< oradb hosts/d' /etc/hosts
>     cat >> /etc/hosts <<EOF
> # >>> oradb hosts
> ${PUB1} ${NODE_NAME}db1
> ${PUB2} ${NODE_NAME}db2
> ${VIP1} ${NODE_NAME}db1-vip
> ${VIP2} ${NODE_NAME}db2-vip
> ${PRIV1} ${NODE_NAME}db1-priv
> ${PRIV2} ${NODE_NAME}db2-priv
> ${SCAN_IP} ${NODE_NAME}db-scan
> # <<< oradb hosts
> EOF
> else
>     grep -qE "^${PUBLIC_IP}[[:space:]]+${NODE_NAME}db([[:space:]]|$)" /etc/hosts \
>         || echo -e "${PUBLIC_IP}\t${NODE_NAME}db" >> /etc/hosts
> fi
> if [[ "${HA}" == "rac" ]]; then
>     hostnamectl set-hostname "${NODE_NAME}db${NODE_SUFFIX}"
> else
>     hostnamectl set-hostname "${NODE_NAME}db"
> fi

> # 2. yum
> mountpoint -q /mnt 2>/dev/null && umount /mnt >/dev/null 2>&1 || true
> eval "${YUM_CMD}" >/dev/null 2>&1 || { echo "ERROR: 挂载 yum 源失败: ${YUM_CMD}"; exit 1; }
> mkdir -p /etc/yum.repos.d/bak
> for r in /etc/yum.repos.d/*.repo; do [[ -e "${r}" ]] && mv -f "${r}" /etc/yum.repos.d/bak/ 2>/dev/null; done
> cat > /etc/yum.repos.d/oradb.repo <<EOF
> [oradb]
> name=oradb local
> baseurl=file:///mnt
> enabled=1
> gpgcheck=0
> EOF
> yum clean all >/dev/null 2>&1
> yum makecache >/dev/null 2>&1 || { echo "ERROR: yum 源不可用"; exit 1; }

> OS_MAJOR=$(grep -oE '[0-9]+' /etc/redhat-release 2>/dev/null | head -1)
> if [[ ${OS_MAJOR:-7} -ge 8 ]]; then
>     PKGS="gcc gcc-c++ make binutils elfutils-libelf elfutils-libelf-devel glibc-devel \
> libaio libaio-devel libstdc++ libstdc++-devel ksh sysstat unixODBC unixODBC-devel libnsl \
> libXext libX11 libXau libXi libXtst libXrender xorg-x11-utils device-mapper-multipath \
> unzip zip net-tools lrzsz vim dstat bc tree nfs-utils expect psmisc \
> policycoreutils-python-utils smartmontools"
> else
>     PKGS="gcc gcc-c++ make compat-gcc-34 compat-libstdc++-33 elfutils-libelf \
> elfutils-libelf-devel glibc-headers libaio libaio-devel libgcc libstdc++ libstdc++-devel \
> ksh sysstat unixODBC unixODBC-devel compat-libcap1 libXp libXi libXtst xorg-x11-utils \
> device-mapper-multipath unzip zip smartmontools net-tools lrzsz vim dstat libnsl \
> policycoreutils-python bc tree nfs-utils expect psmisc"
> fi
> for p in ${PKGS}; do
>     rpm -q "${p}" >/dev/null 2>&1 || yum install -y "${p}" >/dev/null 2>&1 \
>         || say "WARN: 软件包未安装: ${p}"
> done
> umount /mnt >/dev/null 2>&1 || true

> # 3. 关闭无关服务 / SELinux
> systemctl stop firewalld      >/dev/null 2>&1 || true
> systemctl disable firewalld   >/dev/null 2>&1 || true
> sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
> setenforce 0 >/dev/null 2>&1 || true
> for s in tuned ktune postfix avahi-daemon avahi-dnsconfd atd bluetooth wpa_supplicant \
> accounts-daemon ModemManager rtkit-daemon rpcbind rngd upower rhsmcertd mcelog colord \
> gdm libstoragemgmt ksmtuned brltty cups irqbalance; do
>     systemctl stop "${s}"    >/dev/null 2>&1 || true
>     systemctl disable "${s}" >/dev/null 2>&1 || true
> done

> # 4. sysctl
> sed -i '/^# >>> oradb oracle sysctl/,/^# <<< oradb oracle sysctl/d' /etc/sysctl.conf
> mem_kb=$(awk '/MemTotal/{print $2}' /proc/meminfo)
> huge_pages=$(awk -v m="${mem_kb}" 'BEGIN{printf "%d", (m*0.75)/2048}')
> (( huge_pages < 256 )) && huge_pages=256
> cat >> /etc/sysctl.conf <<EOF
> # >>> oradb oracle sysctl
> kernel.shmall = ${mem_kb}
> kernel.shmmax = $(( mem_kb * 1024 ))
> kernel.shmmni = 4096
> kernel.sem = 250 32000 100 128
> fs.file-max = 6815744
> fs.aio-max-nr = 1048576
> net.ipv4.ip_local_port_range = 9000 65500
> net.core.rmem_default = 262144
> net.core.rmem_max = 4194304
> net.core.wmem_default = 262144
> net.core.wmem_max = 1048576
> net.ipv4.ipfrag_high_thresh = 4194304
> net.ipv4.ipfrag_low_thresh = 3145728
> vm.min_free_kbytes = 512000
> vm.vfs_cache_pressure = 200
> vm.swappiness = 20
> vm.nr_hugepages = ${huge_pages}
> # <<< oradb oracle sysctl
> EOF
> sysctl -p >/dev/null 2>&1 || sysctl --system >/dev/null 2>&1 || true

> # 5. limits
> sed -i '/^# >>> oradb limits/,/^# <<< oradb limits/d' /etc/security/limits.conf
> {
> echo "# >>> oradb limits"
> for u in oracle grid; do
>     [[ ${u} == grid && ${HA} == single ]] && continue
>     cat <<EOF
> ${u}  soft  nproc   16384
> ${u}  hard  nproc   16384
> ${u}  soft  nofile  10240
> ${u}  hard  nofile  65536
> ${u}  soft  stack   10240
> ${u}  hard  stack   32768
> ${u}  soft  memlock unlimited
> ${u}  hard  memlock unlimited
> ${u}  soft  core    unlimited
> ${u}  hard  core    unlimited
> EOF
> done
> echo "# <<< oradb limits"
> } >> /etc/security/limits.conf
> grep -q 'pam_limits.so' /etc/pam.d/login || echo 'session    required     pam_limits.so' >> /etc/pam.d/login

> # 6. 用户与组
> chattr -i /etc/passwd /etc/group /etc/gshadow >/dev/null 2>&1 || true
> add_group(){
>     local g=$1 gid=$2
>     getent group "$g" >/dev/null && return 0
>     if getent group "$gid" >/dev/null; then groupadd "$g"; else groupadd -g "$gid" "$g"; fi
> }
> add_user(){
>     local u=$1 uid=$2 grps=$3
>     id "$u" >/dev/null 2>&1 && return 0
>     if getent passwd "$uid" >/dev/null; then
>         useradd -g oinstall -G "$grps" -m "$u"
>     else
>         useradd -u "$uid" -g oinstall -G "$grps" -m "$u"
>     fi
> }
> add_group oinstall    54321
> add_group dba         54322
> if [[ "${HA}" != "single" ]]; then
>     add_group oper        54323
>     add_group asmadmin    54324
>     add_group asmoper     54325
>     add_group asmdba      54326
>     add_group backupdba   54327
>     add_group dgdba       54328
>     add_group kmdba       54329
>     add_group racdba      54330
>     add_user oracle 54321 "dba,oper,asmdba,backupdba,dgdba,kmdba,racdba"
>     add_user grid   54322 "dba,asmadmin,asmdba,asmoper"
> else
>     add_user oracle 54321 "dba"
> fi
> echo "${DB_PWD}" | passwd --stdin oracle >/dev/null 2>&1
> [[ "${HA}" != "single" ]] && echo "${DB_PWD}" | passwd --stdin grid >/dev/null 2>&1

> # 7. 关闭透明大页
> chmod +x /etc/rc.d/rc.local >/dev/null 2>&1 || true
> for f in /sys/kernel/mm/transparent_hugepage/enabled /sys/kernel/mm/redhat_transparent_hugepage/enabled; do
>     [[ -f "${f}" ]] && echo never > "${f}" || true
> done
> grep -q 'transparent_hugepage' /etc/rc.d/rc.local 2>/dev/null || cat >> /etc/rc.d/rc.local <<'EOF'
> if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
>     echo never > /sys/kernel/mm/transparent_hugepage/enabled
> fi
> EOF

> # 8. 目录
> mkdir -p /u01/app /soft
> if [[ "${HA}" == "single" ]]; then
>     mkdir -p "${ORA_BASE}" /oradata
>     chown -R oracle:oinstall /u01 /oradata "${SOFT_DIR}"
> else
>     mkdir -p "${GRID_BASE}" "${GRID_HOME}" "${ORA_BASE}"
>     chown -R grid:oinstall /u01
>     chown -R oracle:oinstall "${ORA_BASE}" "${SOFT_DIR}"
> fi
> chmod -R 775 /u01 "${SOFT_DIR}" 2>/dev/null || true

> # 9. 解压安装介质(仅主节点)
> if [[ "${ROLE}" == "primary" ]]; then
>     if [[ "${DB_MEDIA_MODE}" == "image" ]]; then
>         mkdir -p "${ORACLE_HOME}"
>         chown -R oracle:oinstall "${ORACLE_HOME}"
>         for z in ${DB_ZIP}; do
>             su - oracle -c "unzip -oq ${SOFT_DIR}/${z} -d ${ORACLE_HOME}" \
>                 || { echo "ERROR: 解压 ${z} 失败"; exit 1; }
>         done
>     else
>         for z in ${DB_ZIP}; do
>             unzip -oq "${SOFT_DIR}/${z}" -d "${SOFT_DIR}" \
>                 || { echo "ERROR: 解压 ${z} 失败"; exit 1; }
>         done
>         chown -R oracle:oinstall "${SOFT_DIR}/database"
>     fi
>     if [[ "${HA}" != "single" ]]; then
>         if [[ "${GRID_MEDIA_MODE}" == "image" ]]; then
>             mkdir -p "${GRID_HOME}"
>             chown -R grid:oinstall "${GRID_HOME}"
>             for z in ${GRID_ZIP}; do
>                 su - grid -c "unzip -oq ${SOFT_DIR}/${z} -d ${GRID_HOME}" \
>                     || { echo "ERROR: 解压 ${z} 失败"; exit 1; }
>             done
>         else
>             for z in ${GRID_ZIP}; do
>                 unzip -oq "${SOFT_DIR}/${z}" -d "${SOFT_DIR}" \
>                     || { echo "ERROR: 解压 ${z} 失败"; exit 1; }
>             done
>             chown -R grid:oinstall "${SOFT_DIR}/grid"
>         fi
>     fi
> fi

> # 10. 环境变量
> if [[ "${HA}" == "rac" ]]; then
>     ORA_SID="${DB_SID}${NODE_SUFFIX}"
>     ASM_SID="+ASM${NODE_SUFFIX}"
> else
>     ORA_SID="${DB_SID}"
>     ASM_SID="+ASM"
> fi

> sed -i '/^# >>> oradb oracle profile/,/^# <<< oradb oracle profile/d' /home/oracle/.bash_profile 2>/dev/null
> cat >> /home/oracle/.bash_profile <<EOF
> # >>> oradb oracle profile
> export TMP=/tmp
> export TMPDIR=\$TMP
> export ORACLE_SID=${ORA_SID}
> export ORACLE_BASE=${ORA_BASE}
> export ORACLE_HOME=${ORACLE_HOME}
> export ORACLE_TERM=xterm
> export PATH=/usr/sbin:\$PATH
> export PATH=\$ORACLE_HOME/bin:\$PATH
> export LD_LIBRARY_PATH=\$ORACLE_HOME/lib:/lib:/usr/lib
> export CLASSPATH=\$ORACLE_HOME/JRE:\$ORACLE_HOME/jlib:\$ORACLE_HOME/rdbms/jlib
> umask 022
> # <<< oradb oracle profile
> EOF
> chown oracle:oinstall /home/oracle/.bash_profile

> if [[ "${HA}" != "single" ]]; then
>     sed -i '/^# >>> oradb grid profile/,/^# <<< oradb grid profile/d' /home/grid/.bash_profile 2>/dev/null
>     cat >> /home/grid/.bash_profile <<EOF
> # >>> oradb grid profile
> export ORACLE_BASE=${GRID_BASE}
> export ORACLE_HOME=${GRID_HOME}
> export ORACLE_SID=${ASM_SID}
> export PATH=\$ORACLE_HOME/OPatch:\$ORACLE_HOME/bin:\$PATH
> umask 022
> # <<< oradb grid profile
> EOF
>     chown grid:oinstall /home/grid/.bash_profile
> fi

> say "OS 初始化完成 (ROLE=${ROLE}, NODE_SUFFIX=${NODE_SUFFIX})"
> EOS
>     chmod +x "${SOFT_DIR}/setos.sh"
> }

> gen_setos_env(){
>     cat > "${SOFT_DIR}/setos.env" <<EOF
> export SOFT_DIR='${SOFT_DIR}'
> export HA='${HA}'
> export VER='${VER}'
> export ORA_NUMBER='${ORA_NUMBER}'
> export ORA_NUMBER_S='${ORA_NUMBER_S}'
> export NODE_NAME='${NODE_NAME}'
> export PUBLIC_IP='${PUBLIC_IP}'
> export PUB1='${PUB1}'
> export PUB2='${PUB2}'
> export VIP1='${VIP1}'
> export VIP2='${VIP2}'
> export PRIV1='${PRIV1}'
> export PRIV2='${PRIV2}'
> export SCAN_IP='${SCAN_IP}'
> export DB_PWD='${DB_PWD}'
> export DB_SID='${DB_SID}'
> export YUM_CMD='${YUM_CMD}'
> export DB_ZIP='${DB_ZIP}'
> export GRID_ZIP='${GRID_ZIP}'
> export DB_MEDIA_MODE='${DB_MEDIA_MODE}'
> export GRID_MEDIA_MODE='${GRID_MEDIA_MODE}'
> export ORACLE_HOME='${ORACLE_HOME}'
> export GRID_HOME='${GRID_HOME}'
> export ORA_BASE='${ORA_BASE}'
> export GRID_BASE='${GRID_BASE}'
> EOF
>     chmod 600 "${SOFT_DIR}/setos.env"
> }

> run_setos_local(){
>     gen_setos; gen_setos_env
>     ( source "${SOFT_DIR}/setos.env"; export ROLE=primary NODE_SUFFIX=1; bash "${SOFT_DIR}/setos.sh" ) \
>         || die "本机 OS 初始化失败"
> }

> run_setos_remote(){
>     ssh "${NODE_NAME}db2" "mkdir -p ${SOFT_DIR}" || die "无法 ssh 到 ${NODE_NAME}db2,请检查 root 互信"
>     local yum_cmd=$YUM_CMD
>     if [[ $YUM_SRC == *.iso ]]; then
>         scp "$YUM_SRC" "${NODE_NAME}db2:${SOFT_DIR}/" || die "复制 yum 镜像失败"
>         yum_cmd="mount -o loop ${SOFT_DIR}/$(basename "$YUM_SRC") /mnt"
>     fi
>     scp "${SOFT_DIR}/setos.env" "${SOFT_DIR}/setos.sh" "${NODE_NAME}db2:${SOFT_DIR}/" \
>         || die "复制 setos 脚本失败"
>     ssh "${NODE_NAME}db2" "source ${SOFT_DIR}/setos.env; export ROLE=secondary NODE_SUFFIX=2 YUM_CMD='${yum_cmd}'; bash ${SOFT_DIR}/setos.sh" \
>         || die "node2 OS 初始化失败"
> }

> # -----------------------------------------------------------------------------
> # RAC 互信 / ASM 存储
> # -----------------------------------------------------------------------------
> find_ssh_setup_script(){
>     local p
>     for p in "${GRID_HOME}/oui/prov/resources/scripts/sshUserSetup.sh" \
>              "${SOFT_DIR}/grid/oui/prov/resources/scripts/sshUserSetup.sh" \
>              "${SOFT_DIR}/grid/sshsetup/sshUserSetup.sh"; do
>         [[ -f $p ]] && { echo "$p"; return 0; }
>     done
>     return 1
> }

> config_ssh_trust(){
>     local user=$1 pwd script expfile
>     script=$(find_ssh_setup_script) || die "未找到 sshUserSetup.sh,请确认 Grid 介质已解压"
>     case $user in root) pwd=$ROOT_PWD ;; *) pwd=$DB_PWD ;; esac
>     expfile="${SOFT_DIR}/sshsetup_${user}.exp"
>     cat > "$expfile" <<EOF
> set timeout 180
> set pwd "${pwd}"
> spawn ${script} -hosts "${NODE_NAME}db1 ${NODE_NAME}db2" -user ${user} -advanced
> expect {
>     -re "(?i)(yes/no)" { send "yes\r"; exp_continue }
>     -re "(?i)password" { send "\$pwd\r"; exp_continue }
>     eof
> }
> EOF
>     chmod 600 "$expfile"
>     if [[ $user == root ]]; then
>         expect "$expfile" || die "root 互信配置失败"
>     else
>         chown "${user}:oinstall" "$expfile"
>         run_as "$user" "expect $expfile" || die "${user} 互信配置失败"
>     fi
>     log "${user} 互信配置完成"
> }

> setup_multipath(){
>     local disk wwid i
>     {
>         echo 'defaults {'
>         echo '    user_friendly_names yes'
>         echo '    find_multipaths yes'
>         echo '}'
>         echo 'blacklist {'
>         echo '    devnode "^sda"'
>         echo '}'
>         echo 'multipaths {'
>         i=0
>         for disk in ${OCR_DISKS//,/ }; do
>             i=$((i+1))
>             wwid=$(/usr/lib/udev/scsi_id --whitelisted --replace-whitespace --device="$disk" 2>/dev/null) \
>                 || die "无法获取 $disk 的 wwid"
>             cat <<EOF
>     multipath {
>         wwid ${wwid}
>         alias ocr${i}
>         path_grouping_policy failover
>     }
> EOF
>         done
>         i=0
>         for disk in ${DATA_DISKS//,/ }; do
>             i=$((i+1))
>             wwid=$(/usr/lib/udev/scsi_id --whitelisted --replace-whitespace --device="$disk" 2>/dev/null) \
>                 || die "无法获取 $disk 的 wwid"
>             cat <<EOF
>     multipath {
>         wwid ${wwid}
>         alias data${i}
>         path_grouping_policy failover
>     }
> EOF
>         done
>         echo '}'
>     } > /etc/multipath.conf

>     systemctl restart multipathd >/dev/null 2>&1 || true
>     systemctl enable  multipathd >/dev/null 2>&1 || true
>     multipath -ll
> }

> setup_udev(){
>     local d uuid
>     : > /etc/udev/rules.d/99-oracle-asmdevices.rules
>     for d in $(ls /dev/mapper/ 2>/dev/null | grep -E '^(ocr|data)'); do
>         uuid=$(udevadm info --query=property --name="/dev/mapper/$d" 2>/dev/null \
>                 | awk -F= '/^DM_UUID=/{print $2}')
>         [[ -n $uuid ]] || { log "WARN: 无法获取 $d 的 DM_UUID,跳过"; continue; }
>         echo "KERNEL==\"dm-*\",ENV{DM_UUID}==\"${uuid}\",SYMLINK+=\"oracleasm/disks/${d}\",OWNER=\"grid\",GROUP=\"asmadmin\",MODE=\"0660\"" \
>             >> /etc/udev/rules.d/99-oracle-asmdevices.rules
>     done
>     udevadm control --reload-rules
>     udevadm trigger
>     sleep 5
>     ls -ltr "${ASM_DISK_DIR}/"
> }

> push_asm_config_remote(){
>     scp /etc/multipath.conf "${NODE_NAME}db2:/etc/" || die "复制 multipath.conf 失败"
>     scp /etc/udev/rules.d/99-oracle-asmdevices.rules "${NODE_NAME}db2:/etc/udev/rules.d/" \
>         || die "复制 udev 规则失败"
>     ssh "${NODE_NAME}db2" "systemctl restart multipathd; systemctl enable multipathd; udevadm control --reload-rules; udevadm trigger; sleep 5; ls -ltr ${ASM_DISK_DIR}/"
> }

> install_cvuqdisk(){
>     local rpm_path
>     rpm_path=$(ls "${GRID_HOME}"/cv/rpm/cvuqdisk-*.rpm "${SOFT_DIR}"/grid/rpm/cvuqdisk-*.rpm 2>/dev/null | head -1)
>     [[ -n $rpm_path ]] || { log "WARN: 未找到 cvuqdisk rpm,跳过"; return 0; }
>     rpm -q cvuqdisk >/dev/null 2>&1 || rpm -ivh "$rpm_path" || log "WARN: 本机 cvuqdisk 安装失败"
>     scp "$rpm_path" "${NODE_NAME}db2:${SOFT_DIR}/" >/dev/null 2>&1 || true
>     ssh "${NODE_NAME}db2" "rpm -q cvuqdisk >/dev/null 2>&1 || rpm -ivh ${SOFT_DIR}/$(basename "$rpm_path")" \
>         || log "WARN: node2 cvuqdisk 安装失败"
> }

> init_asm_disks(){
>     local d
>     for d in $(ls "${ASM_DISK_DIR}/" 2>/dev/null | grep -E '^(ocr|data|redo|arch)'); do
>         dd if=/dev/zero of="${ASM_DISK_DIR}/${d}" bs=1M count=10 status=none || \
>             log "WARN: 擦除 ${d} 失败"
>     done
> }

> # -----------------------------------------------------------------------------
> # 响应文件
> # -----------------------------------------------------------------------------
> write_netca_rsp(){
>     local ver
>     [[ $ORA_NUMBER == 11.2.0 ]] && ver="11.2" || ver=$ORA_NUMBER
>     rm -f "${SOFT_DIR}/netca.rsp"
>     cat > "${SOFT_DIR}/netca.rsp" <<EOF
> [GENERAL]
> RESPONSEFILE_VERSION="${ver}"
> CREATE_TYPE="CUSTOM"
> [oracle.net.ca]
> INSTALLED_COMPONENTS={"server","net8","javavm"}
> INSTALL_TYPE="typical"
> LISTENER_NUMBER=1
> LISTENER_NAMES={"LISTENER"}
> LISTENER_PROTOCOLS={"TCP;1521"}
> LISTENER_START="LISTENER"
> NAMING_METHODS={"TNSNAMES","ONAMES","HOSTNAME"}
> NSN_NUMBER=1
> NSN_NAMES={"EXTPROC_CONNECTION_DATA"}
> NSN_SERVICE={"PLSExtProc"}
> NSN_PROTOCOLS={"TCP;HOSTNAME;1521"}
> EOF
> }

> write_db_rsp(){
>     local cluster_nodes="" host_name
>     [[ $HA == rac ]] && cluster_nodes="${NODE_NAME}db1,${NODE_NAME}db2"
>     if [[ $HA == rac ]]; then host_name="${NODE_NAME}db1"; else host_name=$(hostname -s); fi
>     rm -f "${SOFT_DIR}/db_install.rsp"
>     if [[ $ORA_NUMBER == 11.2.0 ]]; then
>         cat > "${SOFT_DIR}/db_install.rsp" <<EOF
> oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v11_2_0
> oracle.install.option=INSTALL_DB_SWONLY
> ORACLE_HOSTNAME=${host_name}
> UNIX_GROUP_NAME=oinstall
> INVENTORY_LOCATION=${INVENTORY}
> SELECTED_LANGUAGES=en,zh_CN
> ORACLE_HOME=${ORACLE_HOME}
> ORACLE_BASE=${ORA_BASE}
> oracle.install.db.InstallEdition=EE
> oracle.install.db.EEOptionsSelection=false
> oracle.install.db.DBA_GROUP=dba
> oracle.install.db.OPER_GROUP=oper
> oracle.install.db.CLUSTER_NODES=${cluster_nodes}
> oracle.install.db.isRACOneInstall=false
> SECURITY_UPDATES_VIA_MYORACLESUPPORT=false
> DECLINE_SECURITY_UPDATES=true
> oracle.installer.autoupdates.option=SKIP_UPDATES
> EOF
>     else
>         cat > "${SOFT_DIR}/db_install.rsp" <<EOF
> oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_${RSP_DB_SCHEMA}
> oracle.install.option=INSTALL_DB_SWONLY
> UNIX_GROUP_NAME=oinstall
> INVENTORY_LOCATION=${INVENTORY}
> ORACLE_BASE=${ORA_BASE}
> oracle.install.db.InstallEdition=EE
> oracle.install.db.OSDBA_GROUP=dba
> oracle.install.db.OSOPER_GROUP=oper
> oracle.install.db.OSBACKUPDBA_GROUP=backupdba
> oracle.install.db.OSDGDBA_GROUP=dgdba
> oracle.install.db.OSKMDBA_GROUP=kmdba
> oracle.install.db.OSRACDBA_GROUP=racdba
> oracle.install.db.rootconfig.executeRootScript=false
> oracle.install.db.rootconfig.configMethod=
> oracle.install.db.rootconfig.sudoPath=
> oracle.install.db.rootconfig.sudoUserName=
> oracle.install.db.CLUSTER_NODES=${cluster_nodes}
> EOF
>     fi
>     chown oracle:oinstall "${SOFT_DIR}/db_install.rsp"
>     chmod 660 "${SOFT_DIR}/db_install.rsp"
> }

> write_grid_rsp(){
>     rm -f "${SOFT_DIR}/grid.rsp"
>     if [[ $HA == rac ]]; then write_grid_rsp_rac; else write_grid_rsp_ha; fi
>     chown grid:oinstall "${SOFT_DIR}/grid.rsp"
>     chmod 660 "${SOFT_DIR}/grid.rsp"
> }

> write_grid_rsp_rac(){
>     local ocr_list
>     ocr_list=$(ls "${ASM_DISK_DIR}"/ocr* 2>/dev/null | xargs | sed 's/ /,/g')
>     if [[ $ORA_NUMBER == 11.2.0 ]]; then
>         cat > "${SOFT_DIR}/grid.rsp" <<EOF
> oracle.install.responseFileVersion=/oracle/install/rspfmt_crsinstall_response_schema_${RSP_GRID_SCHEMA}
> ORACLE_HOSTNAME=${NODE_NAME}db1
> INVENTORY_LOCATION=${INVENTORY}
> SELECTED_LANGUAGES=en,zh_CN
> oracle.install.option=CRS_CONFIG
> ORACLE_BASE=${GRID_BASE}
> ORACLE_HOME=${GRID_HOME}
> oracle.install.asm.OSDBA=asmdba
> oracle.install.asm.OSOPER=asmoper
> oracle.install.asm.OSASM=asmadmin
> oracle.install.crs.config.gpnp.scanName=${NODE_NAME}db-scan
> oracle.install.crs.config.gpnp.scanPort=1521
> oracle.install.crs.config.clusterName=${NODE_NAME}db-cluster
> oracle.install.crs.config.gpnp.configureGNS=false
> oracle.install.crs.config.gpnp.gnsSubDomain=
> oracle.install.crs.config.gpnp.gnsVIPAddress=
> oracle.install.crs.config.autoConfigureClusterNodeVIP=false
> oracle.install.crs.config.clusterNodes=${NODE_NAME}db1:${NODE_NAME}db1-vip,${NODE_NAME}db2:${NODE_NAME}db2-vip
> oracle.install.crs.config.networkInterfaceList=${PUB_IF}:$(cidr24 "$PUB1"):1,${PRIV_IF}:$(cidr24 "$PRIV1"):2
> oracle.install.crs.config.storageOption=ASM_STORAGE
> oracle.install.crs.config.sharedFileSystemStorage.diskDriveMapping=
> oracle.install.crs.config.sharedFileSystemStorage.votingDiskLocations=
> oracle.install.crs.config.sharedFileSystemStorage.votingDiskRedundancy=${OCR_REDU}
> oracle.install.crs.config.sharedFileSystemStorage.ocrLocations=
> oracle.install.crs.config.sharedFileSystemStorage.ocrRedundancy=${OCR_REDU}
> oracle.install.crs.config.useIPMI=false
> oracle.install.crs.config.ipmi.bmcUsername=
> oracle.install.crs.config.ipmi.bmcPassword=
> oracle.install.asm.SYSASMPassword=${DB_PWD}
> oracle.install.asm.diskGroup.name=OCR_VOT
> oracle.install.asm.diskGroup.redundancy=${OCR_REDU}
> oracle.install.asm.diskGroup.AUSize=${AU_SIZE}
> oracle.install.asm.diskGroup.disks=${ocr_list}
> oracle.install.asm.diskGroup.diskDiscoveryString=${ASM_DISK_DIR}/*
> oracle.install.asm.monitorPassword=${DB_PWD}
> oracle.install.crs.upgrade.clusterNodes=
> oracle.install.asm.upgradeASM=false
> oracle.installer.autoupdates.option=SKIP_UPDATES
> oracle.installer.autoupdates.downloadUpdatesLoc=
> AUTOUPDATES_MYORACLESUPPORT_USERNAME=
> AUTOUPDATES_MYORACLESUPPORT_PASSWORD=
> PROXY_HOST=
> PROXY_PORT=0
> PROXY_USER=
> PROXY_PWD=
> PROXY_REALM=
> EOF
>     else
>         cat > "${SOFT_DIR}/grid.rsp" <<EOF
> oracle.install.responseFileVersion=/oracle/install/rspfmt_crsinstall_response_schema_${RSP_GRID_SCHEMA}
> INVENTORY_LOCATION=${INVENTORY}
> oracle.install.option=CRS_CONFIG
> ORACLE_BASE=${GRID_BASE}
> oracle.install.asm.OSDBA=asmdba
> oracle.install.asm.OSOPER=asmoper
> oracle.install.asm.OSASM=asmadmin
> oracle.install.crs.config.scanType=LOCAL_SCAN
> oracle.install.crs.config.SCANClientDataFile=
> oracle.install.crs.config.gpnp.scanName=${NODE_NAME}db-scan
> oracle.install.crs.config.gpnp.scanPort=1521
> oracle.install.crs.config.ClusterConfiguration=STANDALONE
> oracle.install.crs.config.configureAsExtendedCluster=false
> oracle.install.crs.config.memberClusterManifestFile=
> oracle.install.crs.config.clusterName=${NODE_NAME}db-cluster
> oracle.install.crs.config.gpnp.configureGNS=false
> oracle.install.crs.config.autoConfigureClusterNodeVIP=false
> oracle.install.crs.config.gpnp.gnsOption=
> oracle.install.crs.config.gpnp.gnsClientDataFile=
> oracle.install.crs.config.gpnp.gnsSubDomain=
> oracle.install.crs.config.gpnp.gnsVIPAddress=
> oracle.install.crs.config.sites=
> oracle.install.crs.config.clusterNodes=${NODE_NAME}db1:${NODE_NAME}db1-vip,${NODE_NAME}db2:${NODE_NAME}db2-vip
> oracle.install.crs.config.networkInterfaceList=${PUB_IF}:$(cidr24 "$PUB1"):1,${PRIV_IF}:$(cidr24 "$PRIV1"):5
> oracle.install.crs.configureGIMR=false
> oracle.install.asm.configureGIMRDataDG=false
> oracle.install.crs.config.storageOption=FLEX_ASM_STORAGE
> oracle.install.crs.config.sharedFileSystemStorage.votingDiskLocations=
> oracle.install.crs.config.sharedFileSystemStorage.ocrLocations=
> oracle.install.crs.config.useIPMI=false
> oracle.install.crs.config.ipmi.bmcUsername=
> oracle.install.crs.config.ipmi.bmcPassword=
> oracle.install.asm.SYSASMPassword=${DB_PWD}
> oracle.install.asm.diskGroup.name=OCR_VOT
> oracle.install.asm.diskGroup.redundancy=${OCR_REDU}
> oracle.install.asm.diskGroup.AUSize=${AU_SIZE}
> oracle.install.asm.diskGroup.FailureGroups=
> oracle.install.asm.diskGroup.disksWithFailureGroupNames=
> oracle.install.asm.diskGroup.disks=${ocr_list}
> oracle.install.asm.diskGroup.quorumFailureGroupNames=
> oracle.install.asm.diskGroup.diskDiscoveryString=${ASM_DISK_DIR}/
> oracle.install.asm.monitorPassword=${DB_PWD}
> oracle.install.asm.gimrDG.name=
> oracle.install.asm.gimrDG.redundancy=
> oracle.install.asm.gimrDG.AUSize=1
> oracle.install.asm.gimrDG.FailureGroups=
> oracle.install.asm.gimrDG.disksWithFailureGroupNames=
> oracle.install.asm.gimrDG.disks=
> oracle.install.asm.gimrDG.quorumFailureGroupNames=
> oracle.install.asm.configureAFD=false
> oracle.install.crs.configureRHPS=false
> EOF
>     fi
> }

> # standalone(Oracle Restart)的 GI 响应文件
> write_grid_rsp_ha(){
>     local ocr_list host="${NODE_NAME}db"
>     ocr_list=$(ls "${ASM_DISK_DIR}"/ocr* 2>/dev/null | xargs | sed 's/ /,/g')
>     if [[ $ORA_NUMBER == 11.2.0 ]]; then
>         cat > "${SOFT_DIR}/grid.rsp" <<EOF
> oracle.install.responseFileVersion=/oracle/install/rspfmt_crsinstall_response_schema_${RSP_GRID_SCHEMA}
> ORACLE_HOSTNAME=${host}
> INVENTORY_LOCATION=${INVENTORY}
> SELECTED_LANGUAGES=en,zh_CN
> oracle.install.option=CRS_CONFIG
> ORACLE_BASE=${GRID_BASE}
> ORACLE_HOME=${GRID_HOME}
> oracle.install.asm.OSDBA=asmdba
> oracle.install.asm.OSOPER=asmoper
> oracle.install.asm.OSASM=asmadmin
> oracle.install.crs.config.gpnp.scanName=${host}
> oracle.install.crs.config.gpnp.scanPort=1521
> oracle.install.crs.config.clusterName=${host}-cluster
> oracle.install.crs.config.gpnp.configureGNS=false
> oracle.install.crs.config.gpnp.gnsSubDomain=
> oracle.install.crs.config.gpnp.gnsVIPAddress=
> oracle.install.crs.config.autoConfigureClusterNodeVIP=false
> oracle.install.crs.config.clusterNodes=${host}
> oracle.install.crs.config.networkInterfaceList=${PUB_IF}:$(cidr24 "$PUBLIC_IP"):1
> oracle.install.crs.config.storageOption=ASM_STORAGE
> oracle.install.crs.config.sharedFileSystemStorage.diskDriveMapping=
> oracle.install.crs.config.sharedFileSystemStorage.votingDiskLocations=
> oracle.install.crs.config.sharedFileSystemStorage.votingDiskRedundancy=${OCR_REDU}
> oracle.install.crs.config.sharedFileSystemStorage.ocrLocations=
> oracle.install.crs.config.sharedFileSystemStorage.ocrRedundancy=${OCR_REDU}
> oracle.install.crs.config.useIPMI=false
> oracle.install.crs.config.ipmi.bmcUsername=
> oracle.install.crs.config.ipmi.bmcPassword=
> oracle.install.asm.SYSASMPassword=${DB_PWD}
> oracle.install.asm.diskGroup.name=OCR_VOT
> oracle.install.asm.diskGroup.redundancy=${OCR_REDU}
> oracle.install.asm.diskGroup.AUSize=${AU_SIZE}
> oracle.install.asm.diskGroup.disks=${ocr_list}
> oracle.install.asm.diskGroup.diskDiscoveryString=${ASM_DISK_DIR}/*
> oracle.install.asm.monitorPassword=${DB_PWD}
> oracle.installer.autoupdates.option=SKIP_UPDATES
> oracle.installer.autoupdates.downloadUpdatesLoc=
> AUTOUPDATES_MYORACLESUPPORT_USERNAME=
> AUTOUPDATES_MYORACLESUPPORT_PASSWORD=
> PROXY_HOST=
> PROXY_PORT=0
> PROXY_USER=
> PROXY_PWD=
> PROXY_REALM=
> EOF
>     else
>         cat > "${SOFT_DIR}/grid.rsp" <<EOF
> oracle.install.responseFileVersion=/oracle/install/rspfmt_crsinstall_response_schema_${RSP_GRID_SCHEMA}
> INVENTORY_LOCATION=${INVENTORY}
> oracle.install.option=CRS_CONFIG
> ORACLE_BASE=${GRID_BASE}
> oracle.install.asm.OSDBA=asmdba
> oracle.install.asm.OSOPER=asmoper
> oracle.install.asm.OSASM=asmadmin
> oracle.install.crs.config.scanType=LOCAL_SCAN
> oracle.install.crs.config.SCANClientDataFile=
> oracle.install.crs.config.gpnp.scanName=${host}
> oracle.install.crs.config.gpnp.scanPort=1521
> oracle.install.crs.config.ClusterConfiguration=STANDALONE
> oracle.install.crs.config.configureAsExtendedCluster=false
> oracle.install.crs.config.memberClusterManifestFile=
> oracle.install.crs.config.clusterName=${host}-cluster
> oracle.install.crs.config.gpnp.configureGNS=false
> oracle.install.crs.config.autoConfigureClusterNodeVIP=false
> oracle.install.crs.config.gpnp.gnsOption=
> oracle.install.crs.config.gpnp.gnsClientDataFile=
> oracle.install.crs.config.gpnp.gnsSubDomain=
> oracle.install.crs.config.gpnp.gnsVIPAddress=
> oracle.install.crs.config.sites=
> oracle.install.crs.config.clusterNodes=${host}
> oracle.install.crs.config.networkInterfaceList=${PUB_IF}:$(cidr24 "$PUBLIC_IP"):1
> oracle.install.crs.configureGIMR=false
> oracle.install.asm.configureGIMRDataDG=false
> oracle.install.crs.config.storageOption=FLEX_ASM_STORAGE
> oracle.install.crs.config.sharedFileSystemStorage.votingDiskLocations=
> oracle.install.crs.config.sharedFileSystemStorage.ocrLocations=
> oracle.install.crs.config.useIPMI=false
> oracle.install.crs.config.ipmi.bmcUsername=
> oracle.install.crs.config.ipmi.bmcPassword=
> oracle.install.asm.SYSASMPassword=${DB_PWD}
> oracle.install.asm.diskGroup.name=OCR_VOT
> oracle.install.asm.diskGroup.redundancy=${OCR_REDU}
> oracle.install.asm.diskGroup.AUSize=${AU_SIZE}
> oracle.install.asm.diskGroup.FailureGroups=
> oracle.install.asm.diskGroup.disksWithFailureGroupNames=
> oracle.install.asm.diskGroup.disks=${ocr_list}
> oracle.install.asm.diskGroup.quorumFailureGroupNames=
> oracle.install.asm.diskGroup.diskDiscoveryString=${ASM_DISK_DIR}/
> oracle.install.asm.monitorPassword=${DB_PWD}
> oracle.install.asm.gimrDG.name=
> oracle.install.asm.gimrDG.redundancy=
> oracle.install.asm.gimrDG.AUSize=1
> oracle.install.asm.gimrDG.FailureGroups=
> oracle.install.asm.gimrDG.disksWithFailureGroupNames=
> oracle.install.asm.gimrDG.disks=
> oracle.install.asm.gimrDG.quorumFailureGroupNames=
> oracle.install.asm.configureAFD=false
> oracle.install.crs.configureRHPS=false
> EOF
>     fi
> }

> # -----------------------------------------------------------------------------
> # 安装执行
> # -----------------------------------------------------------------------------
> install_grid(){
>     local runInstaller
>     if [[ $GRID_MEDIA_MODE == image ]]; then
>         runInstaller=${GRID_HOME}/runInstaller
>     else
>         runInstaller=${SOFT_DIR}/grid/runInstaller
>     fi
>     [[ -x $runInstaller ]] || die "未找到 Grid runInstaller: $runInstaller"
>     log "开始安装 Grid Infrastructure($HA)..."
>     run_as grid "cd ${GRID_HOME} && ${runInstaller} -silent -waitforcompletion -responseFile ${SOFT_DIR}/grid.rsp ${IGNORE_FLAG}" \
>         || die "Grid Infrastructure 安装失败"
>     log "Grid Infrastructure 安装完成"
> }

> run_root_scripts_grid(){
>     log "执行 Grid root 脚本 ..."
>     [[ -f ${INVENTORY}/orainstRoot.sh ]] && bash "${INVENTORY}/orainstRoot.sh"
>     bash "${GRID_HOME}/root.sh" || die "node1 ${GRID_HOME}/root.sh 失败"
>     if [[ $HA == rac ]]; then
>         scp "${GRID_HOME}/root.sh" "${NODE_NAME}db2:${GRID_HOME}/root.sh" >/dev/null 2>&1 \
>             || die "复制 root.sh 到 node2 失败"
>         ssh "${NODE_NAME}db2" "bash ${GRID_HOME}/root.sh" || die "node2 ${GRID_HOME}/root.sh 失败"
>     fi
>     log "Grid root 脚本执行完成"
> }

> create_data_dg(){
>     local data_list
>     data_list=$(ls "${ASM_DISK_DIR}"/data* 2>/dev/null | xargs | sed 's/ /,/g')
>     [[ -n $data_list ]] || { log "WARN: 未发现 data 盘,跳过 DATA 磁盘组创建"; return 0; }
>     log "创建 DATA 磁盘组 ..."
>     run_as grid "${GRID_HOME}/bin/asmca -silent -createDiskGroup \
>         -diskGroupName DATA -diskList '${data_list}' -redundancy ${DATA_REDU} \
>         -au_size ${AU_SIZE} -sysAsmPassword '${DB_PWD}'" \
>         || die "创建 DATA 磁盘组失败"
>     log "DATA 磁盘组创建完成"
> }

> install_db(){
>     local runInstaller
>     if [[ $DB_MEDIA_MODE == image ]]; then
>         runInstaller=${ORACLE_HOME}/runInstaller
>     else
>         runInstaller=${SOFT_DIR}/database/runInstaller
>     fi
>     [[ -x $runInstaller ]] || die "未找到 DB runInstaller: $runInstaller"
>     log "开始安装数据库软件 ..."
>     run_as oracle "cd ${ORACLE_HOME} && ${runInstaller} -silent -waitforcompletion -responseFile ${SOFT_DIR}/db_install.rsp ${IGNORE_FLAG}" \
>         || die "数据库软件安装失败"
>     log "数据库软件安装完成"
> }

> run_root_scripts_db(){
>     log "执行 DB root.sh ..."
>     [[ -f ${INVENTORY}/orainstRoot.sh ]] && bash "${INVENTORY}/orainstRoot.sh"
>     bash "${ORACLE_HOME}/root.sh" || die "${ORACLE_HOME}/root.sh 失败"
>     log "DB root.sh 执行完成"
> }

> config_netca(){
>     case $HA in
>         single)
>             log "配置监听(oracle home)..."
>             run_as oracle "${ORACLE_HOME}/bin/netca -silent -responsefile ${SOFT_DIR}/netca.rsp" \
>                 || die "netca 配置失败" ;;
>         standalone)
>             log "配置监听(grid home)..."
>             run_as grid "${GRID_HOME}/bin/netca -silent -responsefile ${SOFT_DIR}/netca.rsp" \
>                 || die "netca 配置失败" ;;
>         rac)
>             log "RAC 监听由 Grid 管理,跳过 netca" ;;
>     esac
> }

> create_db(){
>     local dbca common cdb_opt storage_opt user run_home
>     if [[ $HA == single ]]; then
>         user=oracle; run_home=$ORACLE_HOME
>         storage_opt="-datafileDestination /oradata -storageType FS"
>     else
>         user=oracle; run_home=$ORACLE_HOME
>         storage_opt="-storageType ASM -diskGroupName DATA -recoveryGroupName DATA"
>     fi
>     if (( SUPPORTS_CDB == 1 )); then
>         if [[ $USE_CDB == TRUE ]]; then
>             cdb_opt="-createAsContainerDatabase true -numberOfPDBs 1 -pdbName ${PDB_NAME} -pdbAdminPassword '${DB_PWD}'"
>         else
>             cdb_opt="-createAsContainerDatabase false"
>         fi
>     else
>         cdb_opt=""
>     fi
>     common="-silent -createDatabase -templateName General_Purpose.dbc \
> -gdbname ${DB_SID} -sid ${DB_SID} \
> -characterSet ${DB_CHARSET} -nationalCharacterSet ${DB_NCHARSET} \
> -sysPassword '${DB_PWD}' -systemPassword '${DB_PWD}' \
> -emConfiguration NONE -sampleSchema false ${storage_opt} ${cdb_opt}"
>     if [[ $HA == rac && $ORA_NUMBER == 11.2.0 ]]; then
>         common="${common} -nodeinfo ${NODE_NAME}db1,${NODE_NAME}db2"
>     fi
>     log "开始创建数据库 ${DB_SID} ..."
>     run_as "$user" "${run_home}/bin/dbca ${common}" || die "dbca 建库失败"
>     log "数据库 ${DB_SID} 创建完成"
> }

> # -----------------------------------------------------------------------------
> # 主流程
> # -----------------------------------------------------------------------------
> main(){
>     parse_args "$@"
>     normalize_inputs
>     load_profile
>     validate_params
>     resolve_yum_cmd
>     precheck

>     log "===== 部署模式: $HA | 版本: $VER | SID: $DB_SID ====="
>     run_setos_local

>     if [[ $HA != single ]]; then
>         if [[ $HA == rac ]]; then
>             run_setos_remote
>             config_ssh_trust root
>             config_ssh_trust oracle
>             config_ssh_trust grid
>         fi
>         setup_multipath
>         setup_udev
>         [[ $HA == rac ]] && push_asm_config_remote
>         install_cvuqdisk
>         init_asm_disks
>         write_grid_rsp
>         install_grid
>         run_root_scripts_grid
>         create_data_dg
>     fi

>     write_netca_rsp
>     write_db_rsp
>     install_db
>     run_root_scripts_db
>     config_netca
>     create_db

>     log "===== 全部部署完成 ====="
> }

> main "$@"

posted on 2026-10-30 17:06  刘子毅  阅读(28)  评论(0)    收藏  举报

导航