Restoring Planning Analytics
A Planning Analytics administrator can use the Planning Analytics administration console to restore the instance from an online backup.
Before you begin
- Verify that the workstation where you run the installation is set up as a client workstation and includes the Red Hat® OpenShift® CLI.
- Make sure that you are using the external version of the TM1 service. Export and import jobs for Planning Analytics support only the internal version of TM1.
To determine if your TM1 service is external or internal, run the following command:
oc get PAServiceInstance ${SERVICE_NAME} -n zen -o jsonpath='{.spec.tm1_internal_type}'If this command displays false as the output, your TM1 service is external.
Procedure
- Make sure that you are logged in to the Red Hat
OpenShift cluster with the
occommand line tool. - Create the restore-pa.sh script to restore the instance.
The restore-pa.sh script is a file with the following contents:
#!/usr/bin/env bash # # Licensed Materials - Property of IBM # # IBM Cognos Products: pa # # (C) Copyright IBM Corp. 2016 # # US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # ----------------------------------------------------------------------------- # PA_KUBE_SCRIPT_DIR="$( cd "$(dirname "$0")" || exit ; pwd -P )" PA_KUBE_TEMP_DIR=${PA_KUBE_SCRIPT_DIR}/.temp set +e KUBERNETES_CMD=kubectl OPENSSL_CMD=openssl UNZIP_CMD=unzip PA_KUBE_NAMESPACE=${2:-zen} EXTENSION='' PA_KUBE_BACKUP_LOCATION="" PA_KUBE_DECRYPT_BACKUP="false" PA_KUBE_COUCHDB_POD="" PA_KUBE_MONGO_POD="" PA_KUBE_MYSQL_POD="" PA_KUBE_REDIS_POD="" PODS=( atlas async-service bss cdn gateway glass neo-idviz neo-provision pa-content pa-predict palm-service paw-ui-api plan-service prism-app prism-platform prism-proxy share-app share-platform share-proxy tm1proxy user-admin wa-proxy ) REPLICAS=() # # General messages # MSG_LOWERCASE_NO="n" MSG_LOWERCASE_YES="y" MSG_UPPERCASE_NO="N" MSG_UPPERCASE_YES="Y" # # Restore messages # MSG_BACKUP_NAMESPACE_PROMPT="Enter the name of the namespace where Planning Analytics is deployed (default: '${1}'): " MSG_PAW_LOCAL_BACKUP_INVALID_VERSION="No 'mongo.${1}' file found. The PAW Local backup must be created by a newer version." MSG_RESTORE_CONFIGURATION="Restoring configuration" MSG_RESTORE_COUCHDB="Restoring couchdb" MSG_RESTORE="Restoring ${1}" MSG_RESTORE_FROM_PAW_LOCAL_LINUX="Restoring from PAW Local Linux backup" MSG_RESTORE_FROM_PAW_LOCAL_WINDOWS="Restoring from PAW Local Windows backup" MSG_RESTORE_MONGO="Restoring mongo" MSG_RESTORE_MYSQL="Restoring mysql" MSG_RESTORE_PASSWORD="Decryption password: " MSG_RESTORE_REDIS="Restoring redis" MSG_RESTORE_USAGE="Usage: restore.sh <backup directory or zip file> <namespace>" MSG_START_SERVICE="Starting service ${1}" MSG_START_SERVICES="Starting services" MSG_STOP_SERVICE="Stopping service ${1}" MSG_STOP_SERVICES="Stopping services" MSG_WAIT_FOR_SERVICE_TO_START="Waiting for service ${1} to start" MSG_WAIT_FOR_SERVICE_TO_STOP="Waiting for service ${1} to stop" function _exit { echo "$@" exit 1 } function _getParameterValue { local msg='' local value='' msg=$( echo "${1}" "${2}" ) read -er -p "${msg}" value value=${value//[[:space:]]} if [ "${value}" == "" ]; then value="${2}" fi PA_KUBE_CONFIGURATION_VALUE="${value}" } function _getPod { local idx=0 local maxIdx=0 local pod='' local pods=() local result='' local container='' pod="${1}${PAW_INSTANCE_ID}" container="${2}" if ! read -r -a pods <<< "$( ${KUBERNETES_CMD} get pods -o=jsonpath='{range .items[*]}{.metadata.name}{.name}{" "}{end}' -l name="${pod}" -n "${PA_KUBE_NAMESPACE}" 2>&1 )"; then _exit "${pods[@]}" fi if [ "${#pods[@]}" != "1" ]; then _exit "${pods[@]}" fi maxIdx=${MAX_TIME:-30} while [ "${idx}" -lt "${maxIdx}" ]; do result=$( ${KUBERNETES_CMD} exec "${pods[0]}" -c "${container}" -n "${PA_KUBE_NAMESPACE}" -- /bin/bash -c 'ls >& /dev/null' 2>&1 ) if [[ "${result}" != *"container not found"* ]]; then break fi sleep 1 (( idx=idx+1 )) done if [ "${idx}" -eq "${maxIdx}" ]; then _exit _exit "$( echo $MSG_CONTAINER_NOT_FOUND "${container}" )" fi echo "${pods[0]}" } function _getPods { PA_KUBE_COUCHDB_POD=$( _getPod 'couchdb-data1' 'couchdb1' ) PA_KUBE_MONGO_POD=$( _getPod 'mongo-data1' 'mongo1' ) PA_KUBE_MYSQL_POD=$( _getPod 'mysql-server' 'mysql-server' ) PA_KUBE_REDIS_POD=$( _getPod 'redis-data1' 'redis' ) } function _getReplicas { local pods='' local replicas=() for pod in "${PODS[@]}"; do pod="${pod}${PAW_INSTANCE_ID}" replica=$( ${KUBERNETES_CMD} get deployment "${pod}" --output='jsonpath={.spec.replicas}' -n "${PA_KUBE_NAMESPACE}" ) if [ "${replica}" == "0" ]; then replica="1" fi REPLICAS+=("${replica}") done } function _log() { local msg='' local line='' local result='' if [ "${1:0:4}" == "$MSG_" ]; then msg=$( echo "$@" ) else msg=$* fi if [ "$msg" != "" ]; then OLDIFS=${IFS} IFS=$'\n' case "${PA_LOG_MODE}" in console) for line in ${msg}; do echo "$( date +"%Y-%m-%d_:%H:%M:%S" ): ${line}" done ;; file) if [ ! -d "${PA_KUBE_LOG_DIR}" ]; then if ! result=$( mkdir -p "${PA_KUBE_LOG_DIR}" ); then _exit "${result}" fi fi for line in ${msg}; do echo "$( date +"${PA_KUBE_DATE_FORMAT}" ): ${line}" >> "${PA_KUBE_LOG_DIR}/${PA_KUBE_LOG_FILE}" done ;; esac IFS=${OLDIFS} fi } function _onExit { if [ -d "${PA_KUBE_TEMP_DIR}" ]; then rm -rf "${PA_KUBE_TEMP_DIR}" fi } # # _prepareRestore # # ${1} - restore directory or zip file # function _prepareRestore { local result='' if ! result=$( mkdir -p "${PA_KUBE_TEMP_DIR}" ); then _exit "${result}" fi _log "${result}" if [[ "${1}" == *".zip" ]]; then if ! result=$( ${UNZIP_CMD} -d "${PA_KUBE_TEMP_DIR}" -j "${1}" ); then _exit "${result}" fi else if ! result=$( cp -r "${1}/." "${PA_KUBE_TEMP_DIR}" ); then _exit "${result}" fi fi if [ "${PA_KUBE_DECRYPT_BACKUP}" == "true" ]; then _getParameterValue $MSG_RESTORE_PASSWORD "" for file in "${PA_KUBE_TEMP_DIR}"/*; do if ! result=$( ${OPENSSL_CMD} enc -d -base64 -in "${file}" -out "${file}.base64" -pass pass:"${PA_KUBE_CONFIGURATION_VALUE}"); then _exit "${result}" fi mv "${file}.base64" "${file}" done fi EXTENSION="tgz" if [ -f "${PA_KUBE_TEMP_DIR}/bss.tgz" ]; then echo $MSG_RESTORE_FROM_PAW_LOCAL_LINUX fi if [ -f "${PA_KUBE_TEMP_DIR}/bss.zip" ]; then EXTENSION="zip" echo $MSG_RESTORE_FROM_PAW_LOCAL_WINDOWS fi if [ ! -f "${PA_KUBE_TEMP_DIR}/mysql.${EXTENSION}" ]; then _exit "$( echo $MSG_PAW_LOCAL_BACKUP_INVALID_VERSION "${EXTENSION}" )" fi _getPods _getReplicas _stopServices } function _processArgs { if [ "${#@}" != "2" ]; then echo $MSG_RESTORE_USAGE exit 0 fi PA_KUBE_BACKUP_LOCATION="${1}" PA_KUBE_NAMESPACE="${2}" } function _restoreCouchDb { local pods=() local result='' echo $MSG_RESTORE_COUCHDB if ! read -r -a pods <<< "$( ${KUBERNETES_CMD} get pods -o=jsonpath='{range .items[*]}{.metadata.name}{.name}{" "}{end}' -l name=couchdb-data1 -n "${PA_KUBE_NAMESPACE}" 2>&1 )"; then _exit "${pods[@]}" fi if [ "${#pods[@]}" != "1" ]; then _exit "${pods[@]}" fi if ! result=$( ${KUBERNETES_CMD} cp "${PA_KUBE_TEMP_DIR}/couchdb.${EXTENSION}" "${PA_KUBE_NAMESPACE}/${pods[0]}:/tmp/couchdb.${EXTENSION}" -c couchdb-data1 2>&1 ); then _exit "${result}" fi if ! result=$( ${KUBERNETES_CMD} exec "${pods[0]}" -c couchdb-data1 -n "${PA_KUBE_NAMESPACE}" -- /bin/bash -c '/tools/restore.sh' 2>&1 ); then _exit "${result}" fi } function _restoreMongo { local pods=() local result='' echo $MSG_RESTORE_MONGO if ! read -r -a pods <<< "$( ${KUBERNETES_CMD} get pods -o=jsonpath='{range .items[*]}{.metadata.name}{.name}{" "}{end}' -l name=mongo-data1 -n "${PA_KUBE_NAMESPACE}" 2>&1 )"; then _exit "${pods[@]}" fi if [ "${#pods[@]}" != "1" ]; then _exit "${pods[@]}" fi if ! result=$( ${KUBERNETES_CMD} cp "${PA_KUBE_TEMP_DIR}/mongo.${EXTENSION}" "${PA_KUBE_NAMESPACE}/${pods[0]}:/tmp/mongo.${EXTENSION}" -c mongo-data1 2>&1 ); then _exit "${result}" fi if ! result=$( ${KUBERNETES_CMD} exec "${pods[0]}" -c mongo-data1 -n "${PA_KUBE_NAMESPACE}" -- /bin/bash -c '/tools/restore.sh' 2>&1 ); then _exit "${result}" fi _stopService mongo-data1 _startService mongo-data1 1 } function _restoreMySQL { local pods=() local result='' echo $MSG_RESTORE_MYSQL if ! read -r -a pods <<< "$( ${KUBERNETES_CMD} get pods -o=jsonpath='{range .items[*]}{.metadata.name}{.name}{" "}{end}' -l name=mysql-server -n "${PA_KUBE_NAMESPACE}" 2>&1 )"; then _exit "${pods[@]}" fi if ! result=$( ${KUBERNETES_CMD} cp "${PA_KUBE_TEMP_DIR}/mysql.${EXTENSION}" "${PA_KUBE_NAMESPACE}/${pods[0]}:/tmp/mysql.${EXTENSION}" -c mysql-server 2>&1 ); then _exit "${result}" fi if [ "${PA_KUBE_EXPLICIT_USERIDS}" == "true" ]; then if ! result=$( ${KUBERNETES_CMD} exec "${pods[0]}" -c mysql-server -n "${PA_KUBE_NAMESPACE}" -- /bin/bash -c "chown mysql:mysql /tmp/mysql.${EXTENSION}" 2>&1 ); then _exit "${result}" fi fi if ! result=$( ${KUBERNETES_CMD} exec "${pods[0]}" -c mysql-server -n "${PA_KUBE_NAMESPACE}" -- /bin/bash -c 'mysqladmin -u root --password=root flush-hosts;/tools/restore.sh' 2>&1 ); then _exit "${result}" fi } function _restoreRedis { local pods=() local result='' echo $MSG_RESTORE_REDIS _stopService redis-data2 _stopService redis-data3 _waitForRedisMaster if ! read -r -a pods <<< "$( ${KUBERNETES_CMD} get pods -o=jsonpath='{range .items[*]}{.metadata.name}{.name}{" "}{end}' -l name=redis-data1 -n "${PA_KUBE_NAMESPACE}" 2>&1 )"; then _exit "${pods[@]}" fi if [ "${#pods[@]}" != "1" ]; then _exit "${pods[@]}" fi if ! result=$( ${KUBERNETES_CMD} cp "${PA_KUBE_TEMP_DIR}/redis.${EXTENSION}" "${PA_KUBE_NAMESPACE}/${pods[0]}:/tmp/redis.${EXTENSION}" -c redis-data1 2>&1 ); then _exit "${result}" fi if ! result=$( ${KUBERNETES_CMD} exec "${pods[0]}" -c redis-data1 -n "${PA_KUBE_NAMESPACE}" -- /bin/bash -c '/tools/restore.sh' 2>&1 ); then _exit "${result}" fi _stopService redis-data1 _startService redis-data1 1 _startService redis-data2 1 _startService redis-data3 1 } function _startServices { local idx='' local pod='' idx=0 for pod in "${PODS[@]}"; do pod="${pod}${PAW_INSTANCE_ID}" echo $MSG_START_SERVICE "${pod}" if ! result=$( ${KUBERNETES_CMD} scale --replicas="${REPLICAS[${idx}]}" deployment/"${pod}" -n "${PA_KUBE_NAMESPACE}" 2>&1 ); then _exit "${result}" fi (( idx++ )) done for pod in "${PODS[@]}"; do _waitForServiceToStart "${pod}" done } # # _startService # # ${1} - service name # ${2} - number of instances # function _startService { local pod='' local result='' pod="${1}${PAW_INSTANCE_ID}" echo $MSG_START_SERVICE "${pod}" if ! result=$( ${KUBERNETES_CMD} scale --replicas="${2}" deployment/"${pod}" -n "${PA_KUBE_NAMESPACE}" 2>&1 ); then _exit "${result}" fi result=$( ${KUBERNETES_CMD} get pods --selector=name="${pod}" --output=jsonpath='{.items..status.phase}' -n "${PA_KUBE_NAMESPACE}" 2>&1 ) while [[ "${result}" != *"Running"* ]]; do sleep 1 result=$( ${KUBERNETES_CMD} get pods --selector=name="${pod}" --output=jsonpath='{.items..status.phase}' -n "${PA_KUBE_NAMESPACE}" 2>&1 ) done } function _stopServices { local pod='' for pod in "${PODS[@]}"; do pod="${pod}${PAW_INSTANCE_ID}" echo $MSG_STOP_SERVICE "${pod}" if ! result=$( ${KUBERNETES_CMD} scale --replicas=0 deployment/"${pod}" -n "${PA_KUBE_NAMESPACE}" 2>&1 ); then _exit "${result}" fi done for pod in "${PODS[@]}"; do pod="${pod}${PAW_INSTANCE_ID}" _waitForServiceToStop "${pod}" done } # # _stopService # # ${1} - service name # function _stopService { local pod='' local result='' pod="${1}${PAW_INSTANCE_ID}" echo $MSG_STOP_SERVICE "${pod}" if ! result=$( ${KUBERNETES_CMD} scale --replicas=0 deployment/"${pod}" -n "${PA_KUBE_NAMESPACE}" 2>&1 ); then _exit "${result}" fi result=$( ${KUBERNETES_CMD} get pods --selector=name="${pod}" --output=jsonpath='{.items..status.phase}' -n "${PA_KUBE_NAMESPACE}" 2>&1 ) while [[ "${result}" != "" ]]; do sleep 1 result=$( ${KUBERNETES_CMD} get pods --selector=name="${pod}" --output=jsonpath='{.items..status.phase}' -n "${PA_KUBE_NAMESPACE}" 2>&1 ) done } function _waitForRedisMaster { local idx=0 local maxIdx=0 local pod='' local pods=() local result='' local container='' pod="redis-data1${PAW_INSTANCE_ID}" container="redis-data1" if ! read -r -a pods <<< "$( ${KUBERNETES_CMD} get pods -o=jsonpath='{range .items[*]}{.metadata.name}{.name}{" "}{end}' -l name=${pod} -n "${PA_KUBE_NAMESPACE}" 2>&1 )"; then _exit "${pods[@]}" fi if [ "${#pods[@]}" != "1" ]; then _exit "${pods[@]}" fi maxIdx=${MAX_TIME:-30} while [ "${idx}" -lt "${maxIdx}" ]; do if result=$( ${KUBERNETES_CMD} exec "${pods[0]}" -c ${container} -n "${PA_KUBE_NAMESPACE}" -- /bin/bash -c '/tools/is-master.sh' 2>&1 ); then break fi sleep 1 (( idx=idx+1 )) done if [ "${idx}" -eq "${maxIdx}" ]; then _exit _exit "$( echo $MSG_REDIS_MASTER_ERROR "${maxIdx}" )" fi } # # _waitForServiceToStart # # ${1} - service name # function _waitForServiceToStart { local pod='' local result='' pod="${1}${PAW_INSTANCE_ID}" echo $MSG_WAIT_FOR_SERVICE_TO_START "${pod}" result=$( ${KUBERNETES_CMD} get pods --selector=name="${pod}" --output=jsonpath='{.items..status.phase}' -n "${PA_KUBE_NAMESPACE}" 2>&1 ) while [[ "${result}" != *"Running"* ]]; do sleep 1 result=$( ${KUBERNETES_CMD} get pods --selector=name="${pod}" --output=jsonpath='{.items..status.phase}' -n "${PA_KUBE_NAMESPACE}" 2>&1 ) done } # # _waitForServiceToStop # # ${1} - service name # function _waitForServiceToStop { local pod='' local result='' pod="${1}${PAW_INSTANCE_ID}" echo $MSG_WAIT_FOR_SERVICE_TO_STOP "${1}" result=$( ${KUBERNETES_CMD} get pods --selector=name="${pod}" --output=jsonpath='{.items..status.phase}' -n "${PA_KUBE_NAMESPACE}" 2>&1 ) while [[ "${result}" != "" ]]; do sleep 1 result=$( ${KUBERNETES_CMD} get pods --selector=name="${pod}" --output=jsonpath='{.items..status.phase}' -n "${PA_KUBE_NAMESPACE}" 2>&1 ) done } # # main # trap _onExit EXIT _processArgs "${@}" if [ -z "${PA_KUBE_BACKUP_LOCATION}" ]; then echo $MSG_RESTORE_USAGE exit 1 fi if [ ! -e "${PA_KUBE_BACKUP_LOCATION}" ]; then echo $MSG_RESTORE_USAGE exit 1 fi echo $MSG_RESTORE "${PA_KUBE_BACKUP_LOCATION}" echo "Restoring to namespace $PA_KUBE_NAMESPACE" _prepareRestore "${PA_KUBE_BACKUP_LOCATION}" _restoreRedis _restoreCouchDb _restoreMongo _restoreMySQL _startServices - Run the restore-pa.sh script with the directory that contains the
backup as the first argument and the namespace where Planning Analytics is running as the second argument.
./restore-pa.sh <backup-directory> <pa_namespace>For example,
./restore-pa.sh ./backup-2022-01-01 zen - Wait for the Planning Analytics pods to start again.