Restoring Planning Analytics

A Planning Analytics administrator can use the Planning Analytics administration console to restore the instance.

Before you begin

The workstation from which you will run the installation is set up as a client workstation and includes the OpenShift CLI.

Procedure

  1. Ensure you are logged into the OpenShift cluster with the oc command line tool.
  2. Create a new file called restore-pa.sh
  3. Copy and paste the following script into restore-pa.sh:
    #!/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
  4. Execute the script providing the directory that contains the backup as the first argument ./restore-pa.sh <backup-directory> <namespace>. For example: ./restore-pa.sh ./backup-2022-01-01 zen
  5. Wait for the Planning Analytics pods to start up again.