Skip to content

Instantly share code, notes, and snippets.

@fljdin
Last active April 12, 2023 07:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fljdin/0c76a5430ce59e59cfeaed3b473a84d8 to your computer and use it in GitHub Desktop.
Save fljdin/0c76a5430ce59e59cfeaed3b473a84d8 to your computer and use it in GitHub Desktop.
Outil de mise en synchro et synchro régulière basé sur le logshipping Oracle Database. La réplication impose que les bases sources et cibles aient le mode ARCHIVELOG actifs et que la FRA soit un système de fichier standard (EXT4, ACFS, etc.)

repctl

Outil de mise en synchro et synchro régulière basé sur le logshipping Oracle Database.

La réplication impose que les bases sources et cibles aient le mode ARCHIVELOG actifs et que la FRA soit un système de fichier standard (EXT4, ACFS, etc.)

Configuration

Le fichier /etc/repctl.conf doit être alimenté comme dans l'exemple suivant :

# Sample configuration
[general]
databases    = ORCL # MANDATORY: list of actives database configurations
orahome      = /app/oracle/product/x
orabase      = /app/oracle
fra_dest     = /fra
primary_node = runbdd001
standby_node = runbdd002
primary_cnx  = {{ primary_node }}:1521/{{ db_name }}
standby_cnx  = {{ standby_node }}:1521/{{ db_name }}

[ORCL] # configuration name: name of databases listed above
db_name          = ORCL # MANDATORY: database name
primary_node     = runbdd001
primary_orabase  = /app/oracle

standby_node     = runbdd002
standby_orabase  = /app/oracle

password        = Unsecured # MANDATORY: sysdba account

Exécution

  • Aide
Usage : repctl
Provide helpers for Oracle logshipping configuration and management

Commands :
  clone start stop restart open activate recover status list

Options
   -d    : database configuration (from /usr/local/bin/repctl.conf.sample)
   -v    : verbose mode (optional)
   -i    : inline nagios mode (optional)
   -w    : warning threshold (optional)
   -c    : critical threshold (optional)
  • Synchroniser la base standby
repctl recover -d TEST -v
  • Connaître l'état de la synchronisation
repctl status -d TEST
# ----    Get sequence lag for: TEST
# [OK]    Primary sequence is: 36
# [OK]    Standby sequence is: 36
# [OK]    Lag: 0
  • Synchroniser les répertoires

Dans le cas d'une configuration cluster, partager les clés ssh

for node in server-from server-to ; do ssh $node hostname --fqdn ; done
repctl rsync -d TEST
#!/bin/bash
## Variables
VERSION=20181025.1620
CURDIR=$(dirname $0)
SCRIPT=$(basename $0 .sh)
WARN=10
CRIT=30
ARCH_RETENTION=1
## Functions
upper() { echo "$1" | tr '[:lower:]' '[:upper:]' ; }
lower() { echo "$1" | tr '[:upper:]' '[:lower:]' ; }
help() {
cat <<EOF
Usage : $SCRIPT
Provide helpers for Oracle logshipping configuration and management
Commands :
$actions
Options
-f : configuration file (default $configfile)
-d : database configuration
-o : archive retention in days (default: 1)
-v : verbose mode (optional)
-i : inline nagios mode (optional)
-w : warning threshold (optional)
-c : critical threshold (optional)
EOF
exit 0
}
# arg1: section name
# arg2: variable name
# arg3: general variable name
ini() {
var=$(sed -n -e "/^\[$1\]/I,/^\[/ p" $configfile | grep -i ^$2[^a-zA-Z] | cut -d= -f2 | cut -d# -f1)
test -z "$var" && test -n "$3" && var=$(ini general $3)
test -z "$var" && test "$1" != "general" && var=$(ini general $2)
test "$1" == "general" && echo $var | xargs && exit
# recursive template variables like {{ db_name }}
IFS=$'\n'
for m in $(echo $var | grep -Poi '{{(\s?[a-z_]*\s?)}}') ; do
s=$(echo $m | grep -Poi '[a-z_]*')
var=$(echo $var | sed "s/$m/$(ini $1 $s)/g")
done
echo $var | xargs
}
# arg1: message
# arg2: return value (null, 0:ok 1:warn 2:crit 3:unknown)
# arg3: exit or null
trace() {
case "$2" in
0) printf "\033[1;32m[OK]\033[0m\t%s\n" "$1" ;;
1) printf "\033[1;33m[WARN]\t%s\033[0m\n" "$1" ;;
2) printf "\033[1;31m[KO]\t%s\033[0m\n" "$1" ;;
3) printf "\033[1;31m[UNKN]\t%s\033[0m\n" "$1" ;;
*) printf "\033[1;34m----\t%s\033[0m\n" "$1" ;;
esac
if [[ -n "$3" ]] && [[ "$3" = "exit" ]] ; then
exit 1
fi
}
# arg1: connexion string
# arg2: password
# arg3: statement
sql() {
cnx="$1" ; pwd="$2" ; shift 2
result=$($SQLPLUS -L -S /nolog <<OEF
SET HEAD OFF;
CONNECT sys/$pwd@//$cnx as sysdba;
$(echo "$@" | sed 's/;/;\n/g' | sed 's/--BLANK/\n/g')
OEF
)
if [[ ! $result =~ 'TNS-' ]] && \
[[ ! $result =~ 'ORA-' ]] && \
[[ ! $result =~ 'SP2-' ]] ;
then
echo "$result"
else
(>&2 echo "---- SQL: $@")
(>&2 echo "$result")
return 1
fi
}
# arg1: target connection string
# arg2: auxiliary connection string or 0
# arg2: password
# arg3: statement
rman() {
target="$1" ; auxiliary="$2" ; pwd="$3" ; shift 3
result=$($RMAN <<OEF
CONNECT TARGET sys/$pwd@//$target;
$(test ! "$auxiliary" = "0" && echo CONNECT AUXILIARY sys/$pwd@//$auxiliary;)
$(echo "$@" | sed 's/;/;\n/g')
OEF
)
if [[ ! $result =~ 'TNS-' ]] && \
[[ ! $result =~ 'ORA-' ]] && \
[[ ! $result =~ 'SP2-' ]] ;
then
echo "$result"
else
(>&2 echo "---- SQL: $@")
(>&2 echo "$result")
return 1
fi
}
# arg1: database config name
lag() {
trace "Get sequence lag for: $1"
prim_seq=$(sql $(ini $1 primary_cnx) $(ini $1 password) \
$'select max(sequence#) from v$archived_log;')
trace "Primary sequence is: $(echo $prim_seq)" "$(test -n "$prim_seq" && echo 0 || echo 1)"
stdb_seq=$(sql $(ini $1 standby_cnx) $(ini $1 password) \
$'select max(sequence#) from v$archived_log where applied=\'YES\';')
trace "Standby sequence is: $(echo $stdb_seq)" "$(test -n "$stdb_seq" && echo 0 || echo 1)"
if [[ $prim_seq -ge $stdb_seq ]] ; then
diff_seq=$(expr ${prim_seq:-0} - ${stdb_seq:-0})
trace "Lag: $diff_seq" "$(test $diff_seq -ge $CRIT && echo 2 || { test $diff_seq -ge $WARN && echo 1 || echo 0; })"
else
trace "Standby database has been activated" 1 "exit"
fi
}
# arg1: database config name
inline_lag() {
prim_seq=$(sql $(ini $1 primary_cnx) $(ini $1 password) \
$'select min(seq) from (select max(sequence#) seq from gv$archived_log group by inst_id) a;')
stdb_seq=$(sql $(ini $1 standby_cnx) $(ini $1 password) \
$'select max(sequence#) from v$archived_log where applied=\'YES\';')
if [[ $prim_seq -ge $stdb_seq ]] ; then
diff_seq=$(expr ${prim_seq:-0} - ${stdb_seq:-0})
echo "$1: $diff_seq"
else
echo "Standby database has been activated"
fi
}
# arg1: database config name
load() {
ORACLE_HOME=$(ini ${1:-general} orahome)
ORACLE_BASE=$(ini ${1:-general} orabase)
test -d $ORACLE_HOME && export ORACLE_HOME
test -d $ORACLE_BASE && export ORACLE_BASE
export RMAN=${ORACLE_HOME}/bin/rman
export SQLPLUS=${ORACLE_HOME}/bin/sqlplus
export NLS_DATE_FORMAT="YYYY/MM/DD HH24:MI:SS"
}
# arg1: database config name
configured?() {
if [[ ! "$(ini general databases)" =~ "$1" ]] || [[ -z "$1" ]] ; then
echo "Database '$1' is not configured"
exit 1
fi
load "$1"
}
# arg1: database config name
ssh?() {
for node in $(ini $1 primary_node) $(ini $1 standby_node); do
ssh -q $node exit
if [[ $? -gt 0 ]] ; then
echo "SSH connectivity failed on $node with $(whoami) user"
exit 1
fi
done
}
do_list() {
echo "Available databases configuration :"
for cfg in $(ini general databases) ; do
cat <<EOF
$cfg:
- primary = $(ini $cfg primary_cnx)
- standby = $(ini $cfg standby_cnx)
EOF
done
}
# arg1: database config name (optional)
do_status() {
if [[ -z "$1" ]] ; then
for cfg in $(ini general databases) ; do
load "$cfg"
${INLINE}lag $cfg
done
else
configured? "$1"
${INLINE}lag "$1"
fi
}
# arg1: database config name (optional)
do_recover() {
if [[ -z "$1" ]] ; then
for cfg in $(ini general databases) ; do
recover "$cfg"
done
else
recover "$1"
fi
}
# arg1: database config name
recover() {
configured? "$1"
rman $(ini $1 standby_cnx) 0 $(ini $1 password) \
$'CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;' \
$'RECOVER DATABASE;' \
$'CROSSCHECK ARCHIVELOG ALL;' \
$'DELETE NOPROMPT ARCHIVELOG ALL COMPLETED BEFORE \'SYSDATE-'${ARCH_RETENTION}$'\';'
}
# arg1: database config name (optional)
do_rsync() {
if [[ -z "$1" ]] ; then
for cfg in $(ini general databases) ; do
rsync_arch "$cfg"
done
else
rsync_arch "$1"
fi
}
# arg1: database config name
rsync_arch() {
from=$(ini "$1" primary_nodes)
if [[ -n "$from" ]] ; then
# first available
for node in $from ; do
from=$(ssh $node hostname --fqdn 2>&1)
test $? -eq 0 && break
done
else
# no cluster configuration
from=$(ini "$1" primary_node)
fi
remote_fra="$(ini "$1" primary_fra_dest fra_dest)/$1/archivelog"
local_fra="$(ini "$1" standby_fra_dest fra_dest)/$1/archivelog"
result=$(rsync -az --stats "$from:$remote_fra/" "$local_fra")
n=$(echo "$result" | grep "files transferred" | cut -d: -f2)
trace "Transfer $(echo $n) file(s) from $from into $local_fra" "$(echo $?)"
}
# arg1: database config name
do_open() {
configured? "$1"
sql $(ini $1 standby_cnx) $(ini $1 password) \
$'RECOVER AUTOMATIC STANDBY DATABASE;--BLANK' \
$'ALTER DATABASE OPEN READ ONLY;'
}
# arg1: database config name
do_activate() {
configured? "$1"
sql $(ini $1 standby_cnx) $(ini $1 password) \
$'ALTER DATABASE RECOVER MANAGED STANDBY DATABASE FINISH;' \
$'ALTER DATABASE ACTIVATE STANDBY DATABASE;' \
$'ALTER DATABASE OPEN;'
rman $(ini $1 standby_cnx) 0 $(ini $1 password) \
$'CONFIGURE ARCHIVELOG DELETION POLICY CLEAR;'
}
configfile=$CURDIR/repctl.conf.sample
test -f $CURDIR/repctl.conf && configfile=$CURDIR/repctl.conf
actions="activate list open recover rsync status"
if [[ ! $actions =~ "$1" ]] || [[ -z "$1" ]] ; then
help
fi
ACTION="$1"
shift
while getopts ":d:w:c:f:iv" opt; do
case $opt in
d) DBCONF=$OPTARG ;;
c) CRIT=$OPTARG ;;
i) INLINE=inline_ ;;
f) configfile=$OPTARG ;;
v) VERBOSE=0 ;;
w) WARN=$OPTARG ;;
:) echo "Option -$OPTARG requires an argument." ; exit 1;;
*) echo "Invalid option -$OPTARG" ; exit 1;;
esac
done
test ! $VERBOSE && exec 2> /dev/null
supported="11.2 12.2"
# call action logic
do_$ACTION $DBCONF
@tthiebaut
Copy link

Bonjour Florent,

Très bon script.
Je me suis permis de le récupérer et d'y travailler un peu pour effectuer les changements suivant:

  • J'ai viré les couleurs dans la fonction trace, car je l'utilisais en crontab, avec une redirection dans un fichier de log. Pas très lisible les couleurs dans ce cas ;-)

  • J'ai viré la variable db_name pour plutôt utiliser les alias réseaux de la primaire et de la standby qui ne sont pas les mêmes:
    -primary_base et standby_base

  • J'ai supprimé le rsync dans la FRA de la standby, car je suis tombé sur un problème. je transfère dans un répertoire temporaire, et je fais un
    catalog start dans RMAN: $'CATALOG START WITH '\'$(ini $1 standby_dest)\'' NOPROMPT;' \ :
    Avec une très très grosse activité (25G d'archivelogues produit en 1h), rsync créé des fichiers temporaires .mon_fichier.xxx pendant le
    transfère qui sont référencés par la standby et donc Oracle cherché a l'appliquer sur la standby, sauf que le fichier .mon_fichier.xxx a été
    renommé en mon fichier après le transfert rsync. Et donc, la standby est restée bloquée sur l'archivelogues .mon_fichier.xxxx qui n'existait plus.

  • J'ai ajouté l'utilisation d'un flag pour éviter d'avoir le script qui tourne plusieurs fois en // si jamais la première exécution est trop longue.

dans ce que j’aimerai faire:
Au niveau des logs, je cherche un moyen d'inclure des traces dans un fichier, avec par exemple l'utilisation d'un paramètre -l pour log (écriture des traces ds un fichier) via une redirection ( | tee mon_fichier.log ou 1>mon_fichier 2>&1), mais je n'ai pas encore d'idée sans faire une usine à gaz. Si tu as des idées, je suis preneur.

Si tu veux une copie de ce que j'ai fait, pas de souci, dis-le-moi.

Cordialement,
Thierry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment