# hms_load_and_verify.sh
Version=1.0.10

#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE
#------------------------------------------------------------------------------

#==============================================================================
# HMS Library Functions.

#------------------------------------------------------------------------------
# Function: load_helix_topology
#
# Input:
# $1 - Helix Topology file path
# $2 - Helix Topology file required format version.
#
# The following global arrays are updated:
#
# * InstanceUserPorts associative array
# * InstanceMasterHost associative array
# * InstanceServerPort associative array
# * InstanceBrokerPort associative array
# * InstanceManaged associative array
# * InstanceDesc associative array
#
# Instance definition:
# Form: INSTANCE|Name|UserPorts|MasterHost|ServerPort|BrokerPort|Managed|Desc
# Example:
# INSTANCE|fgs|perforce:1666|helix-01|1999|1666|Friendly Greeting Systems
#
# Return:
# 0 - Instances loaded, syntax verified.
# 1 - Errors detected.
#------------------------------------------------------------------------------
function load_helix_topology {
   vvmsg "CALL: load_helix_topology($*)"

   declare cfgFile=$1
   declare reqVersion=$2
   declare cfgVersion=
   declare instance=
   declare instanceUserPorts=
   declare instanceMasterHost=
   declare instanceServerPort=
   declare instanceBrokerPort=
   declare instanceManaged=
   declare instanceDesc=
   declare instanceComponents=
   declare componentName=
   declare componentType=
   declare componentMasterHost=
   declare componentURL=
   declare componentBackupHost=
   declare componentManaged=
   declare componentDesc=
   declare failoverPathName=
   declare failoverFQName=
   declare failoverType=
   declare failoverMasterHost=
   declare failoverBackupHost=
   declare failoverInstanceList=
   declare failoverActive=
   declare line=
   declare -a errors
   declare -i errorCount=0
   declare -i i
   declare -i lineCount=0

   msg "Loading instances from [$cfgFile]."

   if [[ ! -r "$cfgFile" ]]; then
      errmsg "Cannot read Helix Topology file [$cfgFile]."
      return 1
   fi

   # The first action is artifically hard-coded to START_IMPORT.

   while read line; do
      lineCount+=1
      case $line in
         (\#*|NAME*) continue;;
         (GLOBAL_OPTIONS*)
            GlobalOptions=$(echo $line|cut -d '=' -f 2)
         ;;

         (VERSION*)
            cfgVersion=$(echo $line | cut -d '=' -f 2)
            if [[ "$cfgVersion" > "$reqVersion" ]]; then
               msg "Verified:  Helix Topology file version [$cfgVersion] meets minimum requirement."
            else
               errors[$errorCount]="Helix Topology file version [$cfgVersion] is too old.  Must be at least ${reqVersion}.0."
               errorCount+=1
            fi
         ;;

         (INSTANCE*)
            # INSTANCE|Name|UserPorts|MasterHost|ServerPort|BrokerPort|Managed|Desc
            instance="$(echo $line | cut -d '|' -f 2)"
            instanceUserPorts="$(echo $line | cut -d '|' -f 3)"
            instanceMasterHost="$(echo $line | cut -d '|' -f 4)"
            instanceServerPort="$(echo $line | cut -d '|' -f 5)"
            instanceBrokerPort="$(echo $line | cut -d '|' -f 6)"
            instanceManaged="$(echo $line | cut -d '|' -f 7)"
            instanceDesc="$(echo $line | cut -d '|' -f 8)"
            vvmsg "DBG: Instance=$instance"
            InstanceUserPorts[$instance]=$instanceUserPorts
            InstanceMasterHost[$instance]=$instanceMasterHost
            InstanceServerPort[$instance]=$instanceServerPort
            InstanceBrokerPort[$instance]=$instanceBrokerPort
            InstanceManaged[$instance]=$instanceManaged
            InstanceDesc[$instance]=$instanceDesc
            vvmsg "DBG: InstanceName=$instance"
            vvmsg "DBG: InstanceUserPorts[$instance]=${InstanceUserPorts[$instance]}"
            vvmsg "DBG: InstanceMasterHost[$instance]=${InstanceMasterHost[$instance]}"
            vvmsg "DBG: InstanceServerPort[$instance]=${InstanceServerPort[$instance]}"
            vvmsg "DBG: InstanceBrokerPort[$instance]=${InstanceBrokerPort[$instance]}"
            vvmsg "DBG: InstanceManaged[$instance]=${InstanceManaged[$instance]}"
            vvmsg "DBG: InstanceDesc[$instance]=${InstanceDesc[$instance]}"
         ;;

         (COMPONENT*)
            # COMPONENT|Instance|Type|Name|MasterHost|BackupHost|Managed|URL|Desc
            instance="$(echo $line | cut -d '|' -f 2)"
            componentType="$(echo $line | cut -d '|' -f 3)"
            componentName="$(echo $line | cut -d '|' -f 4)"
            componentMasterHost="$(echo $line | cut -d '|' -f 5)"
            componentBackupHost="$(echo $line | cut -d '|' -f 6)"
            componentManaged="$(echo $line | cut -d '|' -f 7)"
            componentURL="$(echo $line | cut -d '|' -f 8)"
            componentDesc="$(echo $line | cut -d '|' -f 9)"

            componentFQName="$instance:$componentName"
            vvmsg "DBG: componentFQName=$componentFQName"
            vvmsg "DBG: componentType=$componentType"
            vvmsg "DBG: componentMasterHost=$componentMasterHost"
            vvmsg "DBG: componentBackupHost=$componentBackupHost"
            vvmsg "DBG: componentManaged=$componentManaged"
            vvmsg "DBG: componentURL=$componentURL"
            vvmsg "DBG: componentDesc=$componentDesc"

            # Maintain the InstanceComponents associative array (indexed by instance name)
            # as a space-separated list of components.
            componentList="${InstanceComponents[$instance]:-Unset}"

            if [[ $componentList == Unset ]]; then
               InstanceComponents[$instance]=$componentName
            else
               InstanceComponents[$instance]="$componentList $componentName"
            fi

            ComponentType[$componentFQName]=$componentType
            ComponentMasterHost[$componentFQName]=$componentMasterHost
            ComponentURL[$componentFQName]=$componentURL
            ComponentBackupHost[$componentFQName]=$componentBackupHost
            ComponentManaged[$componentFQName]=$componentManaged
            ComponentDesc[$componentFQName]=$componentDesc
         ;;

         (FAILOVER*)
            # FAILOVER|Name|Type|MasterHost|BackupHost|InstanceList|Active|Desc

            failoverPathName="$(echo $line | cut -d '|' -f 2)"
            failoverType="$(echo $line | cut -d '|' -f 3)"
            failoverMasterHost="$(echo $line | cut -d '|' -f 4)"
            failoverBackupHost="$(echo $line | cut -d '|' -f 5)"
            failoverInstanceList="$(echo $line | cut -d '|' -f 6)"
            failoverActive="$(echo $line | cut -d '|' -f 7)"
            failoverDesc="$(echo $line | cut -d '|' -f 8)"

            for instance in $(echo $failoverInstanceList|tr ',' ' '); do
               vvmsg "DBG: I=$instance"
               # Maintain the InstanceComponents associative array (indexed by instance name)
               # as a space-separated list of failover options.
               failoverOptionList="${InstanceFailoverOptions[$instance]:-Unset}"
               if [[ $failoverOptionList == Unset ]]; then
                  InstanceFailoverOptions[$instance]=$failoverPathName
               else
                  InstanceFailoverOptions[$instance]="$failoverOptionList $failoverPathName"
               fi

               failoverFQName="$instance:$failoverPathName"

               FailoverType[$failoverFQName]=$failoverType
               FailoverMasterHost[$failoverFQName]=$failoverMasterHost
               FailoverBackupHost[$failoverFQName]=$failoverBackupHost
               FailoverInstanceList[$failoverFQName]=$failoverInstanceList
               FailoverActive[$failoverFQName]=$failoverActive
               FailoverDesc[$failoverFQName]=$failoverDesc
            done
         ;;
      esac
   done < $cfgFile

   if [[ $errorCount -eq 0 ]]; then
      msg "\nVerified:  All data passed sanity checks."
   else
      msg "\nThe following problems were identified:"
      i=0; while [[ $i -lt $errorCount ]]; do
         errmsg "${errors[$i]}"
         i+=1
      done
   fi

   if [[ $errorCount -eq 0 ]]; then
      return 0
   else
      return 1
   fi
}

#------------------------------------------------------------------------------
function show_helix_topology {
   vvmsg "CALL: show_helix_topology($*)"
   declare -i i

   msg "${H}\nLoaded ${#InstanceManaged[*]} instances:\n"

   printf "%-3s %-16s %-40s %-4s %-5s %-2s %-40s %-s\n" "#" "Instance" "User Ports" "P4D" "P4B" "On" "User P4PORTs" "Description"
   printf "%-3s %-16s %-40s %-4s %-5s %-2s %-40s %-s\n" "---" "---------------" "---------------------------------------" "----" "-----" "--" "----------------------------------------" "------------------------------------------------"
   i=1; for instance in ${!InstanceManaged[*]}; do
      printf "%2d. %-16s %-40s %-4s %-5s %2d %-40s %-s\n" "$i" "$instance" "${InstanceUserPorts[$instance]}" "${InstanceServerPort[$instance]}" "${InstanceBrokerPort[$instance]}" "${InstanceManaged[$instance]}" "${InstanceUserPorts[$instance]}" "${InstanceDesc[$instance]}"
      i+=1
   done

   for instance in ${!InstanceManaged[*]}; do
      msg "${H}\nFailover options for Instance $instance:\n"

      printf "%-3s %-9s %-5s %-16s %-16s %-36s %-2s %-s\n" "#" "Path Name" "Type" "Master Host" "Backup Host" "Instance List" "On" "Description"
      printf "%-3s %-9s %-5s %-16s %-16s %-36s %-2s %-s\n" "---" "---------" "-----" "----------------" "----------------" "------------------------------------" "--" "----------------------------------------"
      i=1; for f in ${!FailoverActive[*]};  do
         [[ $f == "$instance:"* ]] || continue
         vvmsg "DBG: F=$f"
         printf "%2d. %-9s %-5s %-16s %-16s %-36s %2d %-s\n" "$i" "${f#*:}" "${FailoverType[$f]}" "${FailoverMasterHost[$f]}" "${FailoverBackupHost[$f]}" "${FailoverInstanceList[$f]}" "${FailoverActive[$f]}" "${FailoverDesc[$f]}"
         i+=1
      done
   done

   for instance in ${!InstanceManaged[*]}; do
      msg "${H}\nComponents for Instance $instance:\n"

      printf "%-3s %-19s %-9s %-24s %-24s %-2s %-36s %-s\n" "#" "Name" "Type" "Master Host" "Backup Host" "M" "Port/URL" "Description"
      printf "%-3s %-19s %-9s %-24s %-24s %-2s %-36s %-s\n" "---" "-------------------" "---------" "------------------------" "------------------------" "--" "------------------------------------" "------------------------------"
      i=1; for f in ${!ComponentManaged[*]};  do
         [[ $f == "$instance:"* ]] || continue
         vvmsg "DBG: F=$f"
         printf "%2d. %-19s %-9s %-24s %-24s %2d %-36s %-s\n" "$i" "${f#*:}" "${ComponentType[$f]}" "${ComponentMasterHost[$f]}" "${ComponentBackupHost[$f]}" "${ComponentManaged[$f]}" "${ComponentURL[$f]}" "${ComponentDesc[$f]}"
         i+=1
      done
   done
}
