Script de restore d'une instance Oracle
#!/bin/bash
# script : restoreInst.sh
# Objet : Restauration DR d’une CDB + PDB avec limite de recover
# sur la dernière séquence d’archivelog réellement disponible.
# Auteur : Josselin
# Mise à jour : 2026-03-04 (version corrigée sans RENAME de redo logs)
set -euo pipefail
# ---------- Usage ----------
usage() {
echo ""
echo "Usage : $0 <CDBCIBLE> <CDBSOURCE> <RestoreDirectory> <RESTOREPDB> <NEWPDB> [THREAD]"
echo ""
echo "Exemple :"
echo " $0 SOURCECDB TGTCDB /appli/oracle/backup/INSTANCE/20260104 PDB1PROD PDB1TEST 1"
echo ""
echo "Paramètres :"
echo " CDBCIBLE Nom de la base cible "
echo " CDBSOURCE Nom de la base source "
echo " RestoreDirectory Répertoire contenant les backups RMAN (.bkp)"
echo " RESTOREPDB Nom de la PDB à restaurer "
echo " NEWPDB Nouveau nom de la PDB "
echo " THREAD (optionnel, défaut=1) Thread d’archivelogs à récupérer"
echo ""
exit 1
}
# ---------- Vérification des arguments ----------
if [[ $# -lt 5 || $# -gt 6 ]]; then
echo "[ERROR] Nombre de paramètres incorrect."
usage
fi
# ---------- Paramètres ----------
CDBCIBLE="$1"
CDBSOURCE="$2"
RestoreDirectory="$3"
RESTOREPDB="$4"
NEWPDB="$5"
THREAD="${6:-1}" # défaut THREAD=1
LOG_DIR="/appli/home/oracle/dbascripts/logs"
mkdir -p "${LOG_DIR}"
TS="$(date +%Y%m%d_%H%M%S)"
LOG_FILE="${LOG_DIR}/dr_restore_${CDBCIBLE}_${RESTOREPDB}_${TS}.log"
# ---------- Trap & logging ----------
cleanup() { [[ -n "${TMP_LS:-}" && -f "$TMP_LS" ]] && rm -f "$TMP_LS" || true; }
trap cleanup EXIT
exec > >(tee -a "${LOG_FILE}") 2>&1
echo "============================================================"
echo "[INFO] DR Restore ${CDBCIBLE} / PDB ${RESTOREPDB} -> ${NEWPDB}"
echo "[INFO] Host : $(hostname -f)"
echo "[INFO] Started : $(date)"
echo "============================================================"
# ---------- Environnement Oracle (DB cible) ----------
export ORACLE_SID="${CDBCIBLE}"
export ORAENV_ASK=NO
if command -v oraenv >/dev/null 2>&1; then
. oraenv >/dev/null 2>&1
elif [[ -x /usr/local/bin/oraenv ]]; then
. /usr/local/bin/oraenv >/dev/null 2>&1
else
echo "[ERROR] oraenv introuvable dans le PATH."
exit 10
fi
echo "[INFO] ORACLE_SID=${ORACLE_SID}, ORACLE_HOME=${ORACLE_HOME}"
echo "[INFO] CDBSOURCE=${CDBSOURCE}, RestoreDirectory=${RestoreDirectory}, THREAD=${THREAD}"
echo "[INFO] PDB à restaurer : ${RESTOREPDB} → Nouveau nom : ${NEWPDB}"
# ---------- Création PFILE minimal (OMF activé) ----------
echo "[INFO] Création du pfile pour ${CDBCIBLE}"
mkdir -p "/appli/oracle/admin/${CDBCIBLE}/pfile/"
cat > "/appli/oracle/admin/${CDBCIBLE}/pfile/init${CDBCIBLE}.ora" <<EOF
audit_file_dest='/appli/oracle/diag/audit'
db_name='${CDBSOURCE}'
db_unique_name='${CDBCIBLE}'
memory_target=8G
control_files='+DATA_DG/${CDBCIBLE}/controlfile/control01.ctl'
compatible='19.0.0'
enable_pluggable_database=TRUE
_disk_sector_size_override=TRUE
db_files=400
db_recovery_file_dest='+FRA_DG'
db_recovery_file_dest_size=1500G
log_file_name_convert='DATA_DG','+REDO_DG'
db_create_online_log_dest_1='+REDO_DG'
EOF
# ---------- Startup NOMOUNT ----------
echo "[INFO] Startup NOMOUNT"
sqlplus -s / as sysdba <<EOF
set echo off feedback on
shutdown abort;
startup nomount pfile='/appli/oracle/admin/${CDBCIBLE}/pfile/init${CDBCIBLE}.ora';
exit
EOF
# ---------- Restauration du controlfile ----------
CTL_FILE=$(ls "${RestoreDirectory}/${CDBSOURCE}_ctl_"*.bkp 2>/dev/null | head -1 || true)
echo "[INFO] Restauration du controlfile"
if [[ -n "${CTL_FILE}" ]]; then
echo "[INFO] Controlfile trouvé : ${CTL_FILE}"
rman target / <<RMAN
restore controlfile from '${CTL_FILE}';
alter database mount;
RMAN
else
echo "[INFO] Aucun controlfile explicite trouvé. Tentative via AUTOBACKUP."
rman target / <<'RMAN'
restore controlfile from autobackup;
alter database mount;
RMAN
fi
# ---------- Catalog des backups ----------
echo "[INFO] Catalog des backup pieces dans ${RestoreDirectory}"
rman target / <<RMAN
run {
allocate channel c1 type disk;
catalog start with '${RestoreDirectory}/' noprompt;
release channel c1;
}
RMAN
# ---------- Extraction de la dernière séquence disponible ----------
TMP_LS=$(mktemp)
echo "[INFO] Extraction de la dernière séquence via LIST BACKUP OF ARCHIVELOG ALL (THREAD=${THREAD})"
rman target / <<'RMAN' > "${TMP_LS}"
list backup of archivelog all;
RMAN
# On cible les lignes du tableau : "Thrd Seq ...", on filtre le thread et on prend la plus grande séquence
LAST_SEQ=$(awk -v T="${THREAD}" '
/^[[:space:]]*[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+/ {
# colonnes: Thrd Seq LowSCN...
if ($1 == T) print $2
}
' "${TMP_LS}" | sort -n | tail -1)
if [[ -z "${LAST_SEQ}" ]]; then
echo "[ERROR] Impossible de déterminer la dernière séquence d’archivelog depuis les backups."
echo " Vérifie que les backup pieces d’archivelogs sont bien catalogués dans ${RestoreDirectory}."
exit 2
fi
TARGET_SEQ=$((LAST_SEQ - 1))
echo "[INFO] LAST_SEQ=${LAST_SEQ} => TARGET_SEQ (LAST_SEQ-1)=${TARGET_SEQ}"
# ---------- Préparation répertoire ASM pour REDO (oraenv -> +ASM) ----------
echo "[INFO] Préparation ASM : +REDO_DG/${CDBCIBLE}/ONLINELOG"
SAVE_ORACLE_SID="${ORACLE_SID}"
export ORACLE_SID="+ASM"
. oraenv >/dev/null 2>&1 || . /usr/local/bin/oraenv >/dev/null 2>&1
# Vérification accès ASM
if ! asmcmd ls / >/dev/null 2>&1; then
echo "[ERROR] ASM non accessible (ORACLE_SID=+ASM ? permissions ?)."
exit 11
fi
# Création des répertoires si absents
asmcmd ls +REDO_DG/${CDBCIBLE} >/dev/null 2>&1 || asmcmd mkdir +REDO_DG/${CDBCIBLE}
asmcmd ls +REDO_DG/${CDBCIBLE}/ONLINELOG >/dev/null 2>&1 || asmcmd mkdir +REDO_DG/${CDBCIBLE}/ONLINELOG
echo "[INFO] Répertoire prêt : +REDO_DG/${CDBCIBLE}/ONLINELOG"
# Retour environnement DB cible
export ORACLE_SID="${SAVE_ORACLE_SID}"
. oraenv >/dev/null 2>&1 || . /usr/local/bin/oraenv >/dev/null 2>&1
# ---------- RESTORE / RECOVER avec SET UNTIL (AVANT RESTORE) ----------
echo "[INFO] Lancement RESTORE/RECOVER avec SET UNTIL SEQUENCE ${TARGET_SEQ} THREAD ${THREAD}"
rman target / <<EOF
run {
allocate channel primary1 type disk;
allocate channel primary2 type disk;
allocate channel primary3 type disk;
allocate channel primary4 type disk;
crosscheck backup;
# Point de récupération limité
SET UNTIL SEQUENCE ${TARGET_SEQ} THREAD ${THREAD};
# OMF + redirection PDB à +DATA_DG (si besoin spécifique pour la PDB)
SET NEWNAME FOR PLUGGABLE DATABASE ${RESTOREPDB} TO '+DATA_DG/${CDBCIBLE}/%U';
# Restaure ROOT + SEED + PDB ciblée
restore database root;
restore database "PDB$SEED";
restore database ${RESTOREPDB};
# Place les datafiles restaurés en OMF
switch datafile all;
# Recover jusqu’à la séquence cible
recover database;
release channel primary1;
release channel primary2;
release channel primary3;
release channel primary4;
}
EOF
# ---------- OPEN RESETLOGS + création des redo logs + renommage PDB ----------
echo "[INFO] OPEN RESETLOGS et post-actions"
sqlplus -s / as sysdba <<EOF
set echo on timing on
alter database open resetlogs;
-- Crée des online redo logs NEUFS dans le répertoire ASM cible
-- Ajuste SIZE / GROUPS selon tes standards
ALTER DATABASE ADD LOGFILE GROUP 1 ('+REDO_DG','REDO_DG') SIZE 2048M;
ALTER DATABASE ADD LOGFILE GROUP 2 ('+REDO_DG','REDO_DG') SIZE 2048M;
ALTER DATABASE ADD LOGFILE GROUP 3 ('+REDO_DG','REDO_DG') SIZE 2048M;
-- 2-3 switches pour initialiser les nouveaux fichiers
alter system switch logfile;
alter system switch logfile;
alter system checkpoint;
-- Ouvre/renomme la PDB
alter pluggable database ${RESTOREPDB} open restricted;
alter session set container=${RESTOREPDB};
alter pluggable database rename global_name to ${NEWPDB};
alter pluggable database close immediate;
alter pluggable database open;
-- Crée un SPFILE à partir du PFILE utilisé au démarrage
create spfile from pfile='/appli/oracle/admin/${CDBCIBLE}/pfile/init${CDBCIBLE}.ora';
-- Redémarrage avec SPFILE pour pérenniser les paramètres
shutdown immediate;
startup;
-- (Optionnel) Post-refresh applicatif
-- alter session set container=${NEWPDB};
-- EXEC sys_pck_db_post_refresh.start_refresh;
-- COMMIT;
exit
EOF
echo "============================================================"
echo "[SUCCESS] Restauration ${CDBCIBLE}/${RESTOREPDB} -> ${NEWPDB} terminée"
echo "[INFO] Point de recover : UNTIL SEQUENCE ${TARGET_SEQ} THREAD ${THREAD}"
echo "[INFO] Log : ${LOG_FILE}"
echo "============================================================"