Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagebash
#!/bin/bash
# -----------------------------------------------------------------------------------------------------------------------------
# Script: castor-change-log-level.sh
# -----------------------------------------------------------------------------------------------------------------------------
# Description:
# This script changes the log level for the Castor cluster or node(s) using the Swarm API.
# The script supports changing the log level for the entire cluster or individual node(s).
# The script can run in a detachable session using 'screen' or 'tmux'.
# -----------------------------------------------------------------------------------------------------------------------------
# Written by Milton Suen (milton.suen@datacore.com) Oct 31, 2024
# Revision History:
# v1.0.0 - Update to support running the script in a detachable session using screen or tmux.
# v1.1.0 - 2025-02-20 Add support node(s) level log level change.
# v1.2.0 - 2025-02-26 SUPSCR-208:
#        - Enforced proper credential formatting: credentials must be in the username:password format.
#        - Fixed help message shows script name without hard code it.
# v1.2.1 - 2025-02-26 SUPSCR-209: Auto detect CSN or SCS to adjust castor.log file path.
# v1.2.12 - 2025-02-2627 Address the issue of the script not display correct when the castor.log file is rotated.
# v1.2.23 - 2025-02-26 SUPSCR-209: Enforced proper credential formatting: credentials must be in the username:password format27 Address the issue of log level not display correct within detach session.
# -----------------------------------------------------------------------------------------------------------------------------
# Current Version: 1.2.23
# -----------------------------------------------------------------------------------------------------------------------------
# KB: https://perifery.atlassian.net/wiki/spaces/public/pages/3872161835/Setting+and+Managing+Swarm+Log+Levels+with+script
# -----------------------------------------------------------------------------------------------------------------------------

# Define colors
RED='\033[0;31m'
BOLD_RED='\033[1;31m'
GREEN='\033[0;32m'
BOLD_GREEN='\033[1;32m'
UNDERLINE_BOLD_GREEN='\033[4;32m'
YELLOW='\033[0;33m'
BOLD_YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD_BLUE='\033[1;34m'
MAGENTA='\033[0;35m'
BOLD_MAGENTA='\033[1;35m'
CYAN='\033[0;36m'
BOLD_CYAN='\033[1;36m'
RESET='\033[0m' # Reset color to default

SCRIPT_NAME=$(basename "$0")

# Function to display usage information
usage() {
    echo "Usage: ./castor-change-log-level10.sh$SCRIPT_NAME -d swarm_ip -p admin:password [-i new_log_level | -L new_node_log_level] [-t duration_in_seconds] [-D]"
    echo "  -d, --swarm_ip           IP address of the Swarm API endpoint. Supports single or multiple IPs separated by \",\", \";\" or \" \"."
    echo "                           If multiple IPs are provided, the script will update the log level for all nodes."
    echo "                           (Alternatively, set the SCSP_HOST environment variable to the Swarm IP.)"
    echo "  -p, --credentials        Credentials in the format admin:password"
    echo "  -i, --log.level          New cluster log level to set (5, 10, 15, 20, 30, 40, 50, chatter, debug, announce, info, error, critical, default)"
    echo "  -L, --node.log.level     New node log level to set (0, 5, 10, 15, 20, 30, 40, 50, chatter, debug, announce, info, error, critical, default)"
    echo "                           **Either -i or -L must be specified, but not both.**"
    echo "  -t, --time               (Optional) Duration in seconds to keep the new log level (must be greater than 0)"
    echo "  -D, --detach             (Optional) Detach the script from the current terminal and run in a detachable session using screen or tmux"
    exit 1
}

# Default options
detachable=false
debug=false
output_log="scriptcastor-change-log-level_output.log"						# Log file for capturing detachable session output
log_level_type="cluster"									# Default log level type
default_log_level=30											# Default log level
log_file="/var/log/datacore/castor.log"		        # Default log file location
SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
JQLOCATION=$SCRIPTDIR/jq

# Global associative array for log levels
declare -A log_levels=(
    [5]="chatter"
    [10]="debug"
    [15]="audit"
    [20]="info"
    [30]="warning"
    [40]="error"
    [45]="defect"
    [50]="critical"
    [60]="announce"
    [30]="default"
)

# Global associative array for log level names
declare -A log_level_names=(
    ["chatter"]=5
    ["debug"]=10
    ["audit"]=15
    ["info"]=20
    ["warning"]=30
    ["error"]=40
    ["defect"]=45
    ["critical"]=50
    ["announce"]=60
    ["default"]=30
)

# Function to displayget debugthe messagescurrent if debug mode is enabled
debug_msgtimestamp
timestamp() {
    if $debug; date +"%Y-%m-%d_%H:%M:%S.%4N"
}

# Function to display debug messages if debug mode is enabled
debug_msg() {
    if $debug; then
        local caller_line=${BASH_LINENO[0]:-unknown}  # Fallback to "unknown" if empty
        echo -e "$(timestamp) [DEBUG] $1" >> "$output_log(Line $caller_line) $*"
    fi
}

# Function to check if either 'screen' or 'tmux' is installed
check_screen_or_tmux() {
    if ! command -v screen &>/dev/null && ! command -v tmux &>/dev/null; then
        echo "Error: Neither 'screen' nor 'tmux' is installed. Cannot run in detachable mode."
        detachable=false  # Disable detachable session
        debug_msg "detachable=${YELLOW}false${RESET}"
    fi
}

# Function to format file size
format_size() {
    local size=$1
    if (( size >= 1073741824 )); then
        echo "$(awk "BEGIN {printf \"%.1fGB\", $size/1073741824}")"
    elif (( size >= 1048576 )); then
        echo "$(awk "BEGIN {printf \"%.1fMB\", $size/1048576}")"
    elif (( size >= 1024 )); then
        echo "$(awk "BEGIN {printf \"%.1fKB\", $size/1024}")"
    else
        echo "${size}B"
    fi
}

# Function to format duration
format_duration() {
    local duration=$1
    local hours=$((duration / 3600))
    local minutes=$(( (duration % 3600) / 60 ))
    local seconds=$((duration % 60))
    printf "%02d:%02d:%02d" $hours $minutes $seconds
}

# Function to check if jq is available and set up JSON parsing method
check_jq() {
    if [[ -x "/usr/local/bin/jq" ]]; then
        echo "/usr/local/bin/jq"
    elif [[ -x "$(pwd)/jq" ]]; then
        echo "$(pwd)/jq"
    elif command -v jq &>/dev/null; then
        echo "jq"
    else
        echo "grep"
    fi
}

if [[ -f "$JQLOCATION" ]]; then
    jq_or_grep=$($JQLOCATION
else
    jq_or_grep=$(check_jq)
fi
debug_msg "jq_or_grep: $jq_or_grep"
#jq_or_grep=$(check_jq)

# Function to determine the log file path
determine_log_file() {
    if [[ -f "/var/log/datacore/castor.log" ]]; then
        echo "/var/log/datacore/castor.log"
    elif [[ -f "/var/log/caringo/castor.log" ]]; then
        echo "/var/log/caringo/castor.log"
    else
        echo "Error: Log file not found in /var/log/datacore/castor.log or /var/log/caringo/castor.log"
        exit 1
    fi
}

log_file=$(determine_log_file)

# ParseFunction inputto argumentscheck whileif [[ "$#" -gt 0 ]]; docredentials are valid
check_credentials() {
    local case CREDENTIALS="$1"
in         -d|--swarm_ip) swarm_iplocal SWARM_IP="$2";
    debug_msg "Set swarm_ip to $swarm_ip"; shift 2 ;;
    Credentials: $CREDENTIALS"
    debug_msg "Swarm IP: $SWARM_IP"

  -p|--credentials)  # API endpoint for validating user credentials
    local credentials="$2VALIDATE_URL="http://${SWARM_IP}:91/api/validateUser"

    # Make the API request
  if ! [[ "$credentials" =~ ^[^:]+:[^:]+$ ]]; then
       RESPONSE=$(curl -s -u "$CREDENTIALS" -X GET "$VALIDATE_URL" -H 'Content-Type: application/json')
    debug_msg "Validate User Response: $RESPONSE"
echo
-e ""   # Check if the response contains "isValid": true
    if  echo -e "Error: Credentials must be in the format ${BOLD_GREEN}username:password${RESET}"
                exit 1
"$RESPONSE" | "$jq_or_grep" -e '.isValid == true' > /dev/null 2>&1; then
        return 0  fi# Success
    elif echo "$RESPONSE" | "$jq_or_grep" -e '.isValid if [[ "$credentials" =~ [^a-zA-Z0-9:] ]]== false' > /dev/null 2>&1; then
        debug_msg "Authentication failed for user '${CREDENTIALS%%:*}'"
  echo -e ""
         return 1  # Failure
    else
       echo -edebug_msg "WarningError: PasswordUnable containsto specialvalidate characterscredentials. Please check encloseyour theinputs."
credentials with single quotes (${BOLD_YELLOW}'${BOLD_GREEN}username:password${RESET}${BOLD_YELLOW}'${RESET})."    return 1  # Failure
    fi
}

# exitParse 1input arguments
while [[ "$#" -gt 0 ]]; do
    ficase $1 in
        -d|--swarm_ip) swarm_ip="$2"; debug_msg "Set credentialsswarm_ip to $swarm_ip"; shift 2 ;;
        -ip|--log.level)credentials)
            credentials="$2"
            if ! [[ -n "$new_log_level" "$credentials" =~ ^[^:]+:[^:]+$ ]]; then
                echo -e "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together.""
                echo -e "Error: Credentials must be in the format ${BOLD_GREEN}username:password${RESET}"
                exit usage1
            fi
            if [[ ${log_level_names[$2]}"$credentials" =~ [^a-zA-Z0-9:] ]]; then
                new_log_level=${log_level_names[$2]}
 echo -e ""
          elif [[ ${log_levels[$2]} ]]; then  echo -e "Warning: Password contains special characters. Please enclose the credentials with single   new_log_level=$2quotes (${BOLD_YELLOW}'${BOLD_GREEN}username:password${RESET}${BOLD_YELLOW}'${RESET})."
            else    exit 1
           echo "Invalidfi
log level: $2"          debug_msg "Set credentials"; shift 2 ;;
 exit 1      -i|--log.level)
      fi      if [[      new_log_level_name=${log_levels[-n "$new_log_level" ]]}; then
           log_level_type="cluster"     echo "Error: Options -i (cluster log leve)  default_log_level=30
      and -L (node log level) cannot be used together."
     debug_msg "Set new_log_level to $new_log_level ($new_log_level_name)"        usage
    shift 2       fi
     ;;       if  -L|--node.log.level)[[ ${log_level_names[$2]} ]]; then
             if [[ -n "$new_ new_log_level=${log_level" ]]; then_names[$2]}
            elif [[ ${log_levels[$2]} ]]; then
   echo "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together."             new_log_level=$2
            else
       usage         echo "Invalid log level: $2"
fi             if [[ ${log_level_names[$2]} ]]; then exit 1
            fi
            new_log_level_name=${log_levels[$new_log_level_names[$2]}
 
          elif [[ ${log_levels[$2]} || $2 -eq 0 ]]; then_level_type="cluster"
                      newdefault_log_level=$230
            debug_msg "Set new_log_level  newto $new_log_level ($new_log_level_name="default)"
            elseshift 2
            ;;
  echo "Invalid log level: $2"  -L|--node.log.level)
            if [[ exit 1
 -n "$new_log_level" ]]; then
          fi      echo "Error: Options -i (cluster log leve) and -L (node log_level_type="node"
   level) cannot be used together."
         default_log_level=0       usage
     debug_msg "Set new_log_level to $new_log_level ($new_log_level_name)"  fi
          shift 2 if [[ ${log_level_names[$2]} ]]; then
       ;;         -t|--time)new_log_level=${log_level_names[$2]}
            ifelif [[ -n "$2" && "$2" != -* && "$2" -gt ${log_levels[$2]} || $2 -eq 0 ]]; then
                durationnew_log_level="$2"
                debug_msg "Set duration to $duration"new_log_level_name="default"
            else
        shift 2       echo "Invalid log level: $2"
 else               exit 1
echo "Error: Duration must be a number greater than 0."   fi
             exit 1new_log_level_name=${log_levels[$new_log_level]}
            log_level_type="node"
 fi           default_log_level=0
 ;;         -D|--detach) detachable=true; debug_msg "Set detachablenew_log_level to true"; shift ;;
$new_log_level ($new_log_level_name)"
       --debug) debug=true; debug_msg "Set debug toshift true";2
shift ;;         *) usage ;;
     esac done  debug_msg "Set log_level_type to ${YELLOW}$log_level_type${RESET}"

# Set default values if not provided-t|--time)
            if [[ -z "$new_log_level"n "$2" && "$2" != -* && "$2" -gt 0 ]]; then
     if [[ "$log_level_type" == "cluster" ]]; then           duration="$2"
           new_log_level=$default_log_level     elsedebug_msg "Set duration to $duration"
    new_log_level=0     fi     new_log_level_name="default"  shift 2
 new_log_level_name=${log_levels[$new_log_level]}     debug_msg "Set default new_log_level to $new_log_level ($new_log_level_name)"
fi

# Check if 'screen' or 'tmux' is installed
check_screen_or_tmux

# If swarm_ip is not provided, try using SCSP_HOST environment variable
if [[ -z "$swarm_ip" ]]; then else
                echo "Error: Duration must be a number greater than 0."
      if [[ -n "$SCSP_HOST" ]]; then     exit 1
  swarm_ip="$SCSP_HOST"         debug "Usingfi
Swarm IP from SCSP_HOST: $swarm_ip"     else   ;;
     echo "Error: swarm_ip not provided and SCSP_HOST is not set."
  -D|--detach) detachable=true; debug_msg "Set detachable to true"; shift ;;
      usage  --debug) debug=true; debug_msg "Set fidebug fito true # Check if required arguments are provided
if [[ -z "$credentials" || -z "$new_log_level" ]]; thenwith ${YELLOW}--debug${RESET}"; shift ;;
        *) usage ;;
    esac
usagedone
fi
debug_msg # Split the swarm_ip into an array of IP addresses if it contains delimiters
IFS=';, ' read -r -a ip_array <<< "$swarm_ip"

# Retrieve cluster name and handle JSON parsing using the first IP address
debug_msg "Retrieving the cluster name from Swarm API using IP: ${ip_array[0]}"
if [[ "$jq_or_grep" == "grep" ]]; then
    clusterName=$(curl --user "$credentials" -sS ""Set log_level_type to ${YELLOW}$log_level_type${RESET}"

# Set default values if not provided
if [[ -z "$new_log_level" ]]; then
    if [[ "$log_level_type" == "cluster" ]]; then
        new_log_level=$default_log_level
    else
        new_log_level=0
    fi
    new_log_level_name="default"
    new_log_level_name=${log_levels[$new_log_level]}
    debug_msg "Set default new_log_level to $new_log_level ($new_log_level_name)"
fi

# Check if 'screen' or 'tmux' is installed
check_screen_or_tmux

# If swarm_ip is not provided, try using SCSP_HOST environment variable
if [[ -z "$swarm_ip" ]]; then
    if [[ -n "$SCSP_HOST" ]]; then
        swarm_ip="$SCSP_HOST"
        debug "Using Swarm IP from SCSP_HOST: $swarm_ip"
    else
        echo "Error: swarm_ip not provided and SCSP_HOST is not set."
        usage
    fi
fi

# Check if required arguments are provided
if [[ -z "$credentials" || -z "$new_log_level" ]]; then
    usage
fi

# Split the swarm_ip into an array of IP addresses if it contains delimiters
IFS=';, ' read -r -a ip_array <<< "$swarm_ip"

# Validate credentials before proceeding
debug_msg "Validating credentials..."
if ! check_credentials "$credentials" "${ip_array[0]}"; then
    echo -e ""
    echo -e "${YELLOW}Warning${RESET}: ${RED}Invalid credentials${RESET}. Please check your username and password in the format ${GREEN}username:password${RESET}."
    echo -e ""
    usage
    exit 1
fi

# Retrieve cluster name and handle JSON parsing using the first IP address
debug_msg "Retrieving the cluster name from Swarm API using IP: ${ip_array[0]}"
if [[ "$jq_or_grep" == "grep" ]]; then
    clusterName=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters" | grep -oP '"name":\s*"\K[^"]+')
else
    clusterName=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters" | "$jq_or_grep" -r '._embedded.clusters[0].name')
fi

if [[ -z "$clusterName" ]]; then
    echo "Failed to retrieve the cluster name. Please check your inputs."
    exit 1
fi
debug_msg "Cluster Name: $clusterName"

# Main logic function to run the script tasks
main_script() {
    local swarm_ip="$1"
    local credentials="$2"
    local new_log_level="$3"
    local new_log_level_name="$4"
    local duration="$5"
    local log_level_type="$6"
    #local log_file="/var/log/datacore/castor.log"
    if [[ -z "$log_file" ]]; then
        log_file=$(determine_log_file)
    fi
    local initial_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)
    local current_log_level
    local clusterName="$7"
    local jq_or_grep="$8"
    local detachable="$9"
    if [[ "$detachable" ]]; then
        local debug="${10}"
        eval "$(echo "${11}" | sed 's/declare -A/declare -A/')"
        eval "$(echo "${12}" | sed 's/declare -A/declare -A/')"
    fi
    debug_msg "**********************************************************"
    debug_msg "local variables"
    debug_msg "Swarm IP: $swarm_ip"
    debug_msg "Credentials: $credentials"
    debug_msg "New Log Level: $new_log_level"
    debug_msg "New log Level Name: $new_log_level_name"
    debug_msg "Duration: $duration"
    debug_msg "Log Level Type: $log_level_type"
    debug_msg "Log File: $log_file"
    debug_msg "Initial Log File Size: $initial_size"
    debug_msg "Current Log Level: $current_log_level"
    debug_msg "Cluster Name: $clusterName"
    debug_msg "jq_or_grep: $jq_or_grep"
    debug_msg "Detach: $detachable"
    debug_msg "Debug: $debug"
    debug_msg "**********************************************************"

    # Split the swarm_ip into an array of IP addresses
    IFS=';, ' read -r -a ip_array <<< "$swarm_ip"
    debug_msg "IP Array: ${ip_array[*]}"

    # Display initial information
    if [[ "$log_level_type" == "cluster" ]]; then
        echo -e "Swarm IP: ${GREEN}${ip_array[0]}${RESET}"
    else
        echo -e "Swarm IPs: ${GREEN}${ip_array[*]}${RESET}"
    fi
    # echo -e "Swarm IP: ${GREEN}$swarm_ip${RESET}"
    echo -e "Credentials: ${GREEN}[hidden for security]${RESET}"
    echo -e "Cluster Name: ${GREEN}$clusterName${RESET}"

    debug_msg "Starting main_script function..."
    debug_msg "Log level type: $log_level_type"

    # Store the original log levels
    declare -A original_log_levels
    declare -A original_log_level_names

    if [[ $log_level_type == "cluster" ]]; then
        debug_msg "Setting cluster log level to $new_log_level ($new_log_level_name)"
        # Retrieve current log level
        if [[ "$jq_or_grep" == "grep" ]]; then
            current_log_level=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" | grep -oP '"namevalue":\s*"\K[^"0-9]+')
        else
       clusterName     current_log_level=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" | "$jq_or_grep" -r '._embedded.clusters[0].name')
fi

if [[ -z "$clusterName" ]]; then
    echo "Failed to retrieve the cluster name. Please check your inputs."
    exit 1
fi
debug_msg "Cluster Name: $clusterName"

# Main logic function to run the script tasks
main_script() {
    local swarm_ip="$1value')
        fi
        current_log_level_name=${log_levels[$current_log_level]}
        debug_msg "Current cluster log level: ${BOLD_GREEN}$current_log_level${RESET}"
        debug_msg "Current log level name: ${BOLD_GREEN}$current_log_level_name${RESET}"
        echo -e ""
        echo -e "New cluster log level: ${BOLD_GREEN}$new_log_level_name${RESET}"
    local credentials="$2"   echo  local new_log_level="$3"
    local new-e "Current cluster log level is ${GREEN}$current_log_level_name="$4".${RESET}"

      local duration="$5" # Skip update if localnew log_level_type="$6"
    # local log_file="/var/log/datacore/castor.log"level matches the current level
        if local initial_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)[[ "$current_log_level" -eq "$new_log_level" ]]; then
      local current_log_level     localecho clusterName="$7"
       local jq_or_grep="$8"    echo local-e debug="$9"Cluster log level is  debug_msg "**********************************************************"already set to ${BOLD_GREEN}$new_log_level_name${RESET}. No changes made."
         debug_msg "local variables" return
   debug_msg "Swarm IP: $swarm_ip"  fi

 debug_msg "Credentials: $credentials"     debug_msg "New Log Level: $new_log_level ($new_log_level_name)"
    debug_msg "Duration: $duration"# Update the cluster log level using PUT
        debug_msg "Log Level Type: $logUpdating cluster log level to $new_log_level_typename"
        debug_msg "LogCluster FileName: $log_file$clusterName"
     debug_msg "Initial Log File Size: $initial_size"
    debug_msg "Current Log Level: $current_log_level"
    debug_msg "Cluster Name: $clusterName"response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \
           debug_msg "jq_or_grep: $jq_or_grep"http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \
     debug_msg "Debug: $debug"     debug_msg "**********************************************************"-d "{\"value\": $new_log_level}")
     # Split the swarm_ip into an array of IP addressesdebug_msg "Response: $response"

        if  IFS=';, ' read -r -a ip_array <<< "$swarm_ip"[[ "$jq_or_grep" == "grep" ]]; then
            debug_msg "IP Array: ${ip_array[*]}"updated_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
        else
 #  Display initial information     if [[ "$log_level_type" == "cluster" ]]; then updated_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')
        fi
   echo  -e "Swarm IP: ${GREEN}${ip_array[0]}${RESET}"
    else debug_msg "Updated cluster log level: $updated_log_level"

        echoif -e[[ "Swarm IPs: ${GREEN}${ip_array[*]}${RESET}"$updated_log_level" -eq "$new_log_level" ]]; then
      fi     # echo -e "Swarm IP:Log level changed successfully from ${GREEN}$current_log_level${RESET} → ${BOLD_GREEN}$swarm$new_log_ip$level${RESET}."
    echo -e "Credentials: ${GREEN}[hidden for security]${RESET}"    else
            echo -e "Cluster NameFailed to update log level. Response: ${GREENRED}$clusterName$$response${RESET}"
     debug_msg "Starting main_script function..."     debug_msg "Log level type: $log_level_type"exit 1
     # Store the original log levelsfi

   declare -A original_log_levels   # Countdown declareand -Arevert original_log_level_names level
        if [[ $log_level_type == "cluster"  -n "$duration" && "$duration" -gt 0 ]]; then
            echo debug_msg-e "Setting clusterKeeping log level toat ${YELLOW}$new_log_level_name${RESET} ($new_log_level_name)"for ${YELLOW}$duration${RESET} second(s)..."
             # Retrieve current log levelecho -e ""
            for ((i=duration; i>0; i--)); do
        if [[ "$jq_or_grep" == "grep" ]]; then  printf -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60   current_log_level=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" | grep -oP '"value":\s*\K[0-9]+'))) $((i%60))
                echo else-ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r"
         current_log_level=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" | "$jq_or_grep" -r '.value') sleep 1
        fi    done
    current_log_level_name=${log_levels[$current_log_level]}        echo debug_msg-e "Current cluster\n\nTime's up! Reverting log level: back to ${BOLD_GREEN}$current_log_level${RESET}..."

       debug_msg "Current     # Revert the log level: ${BOLD_GREEN}$current_log_level_name${RESET}" back to the original value
            response=$(curl --user "$credentials"  echo-sS -X PUT -eH "Content-Type: application/json" \
        echo    -e "New cluster log level"http: //${BOLD_GREEN}$new_log_level_name${RESET}"ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \
          echo -e "Current cluster log level is-d ${GREEN}"{\"value\": $current_log_level_name.${RESET}")
          # Skip update if new level matches the current leveldebug_msg "Response: $response"

            if [[ "$current$jq_logor_levelgrep" -eq== "$new_log_levelgrep" ]]; then
            echo ""   reverted_log_level=$(echo "$response"         echo| grep -eoP "Cluster log level is already set to ${BOLD_GREEN}$new_log_level_name${RESET}. No changes made."'"value":\s*\K[0-9]+')
            else
            return    reverted_log_level=$(echo "$response" | "$jq_or_grep"  fi-r '.value')
         # Update the clusterfi
log level using PUT         debug_msg "UpdatingReverted cluster log level: to $new$reverted_log_level_name"
 "

            final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)
            debug_msg "Cluster NameInitial log file size: $clusterName$initial_size"
 
      response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \debug_msg "Final log file size: $final_size"
            "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \
 size_diff=$(( final_size - initial_size ))
          -d "{\"value\": $new_log_level}")
  size_diff_formatted=$(format_size "$size_diff")
      debug_msg "Response: $response"    duration_formatted=$(format_duration "$duration")
	    if [[ "$jq_or_grep" == "grep" ]](( size_diff < 0 )); then
		echo -e "castor.log file was rotated."
	    else
 updated_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')      echo -e  else
            updated_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')
        "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level${RESET}. Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after $duration_formatted."
	    fi

       debug_msg "Updated cluster log level: $updated_log_level"

        if [[ "$updated$reverted_log_level" -eq "$new$current_log_level" ]]; then
                echo -e "Log level changedreverted successfully from ${GREEN}$current_log_level${RESET} →back to ${BOLD_GREEN}$new$current_log_level${RESET}."
            else
                echo -e "Failed to update log level. Response: ${RED}$response${RESET}"
            exit 1
 revert log level. Response: ${RED}$response${RESET}"
      fi          #exit Countdown1
and revert log level         iffi
[[ -n "$duration" && "$duration" -gt 0 ]]; thenelse
            echo -e "KeepingLog loglevel levelchange at ${YELLOW}$new_log_level${RESET} for ${YELLOW}$duration${RESET} second(s)..."is permanent until manually modified."
        fi
    elif  echo -e[[ "$log_level_type" == "node" ]]; then
         # First for ((i=duration; i>0; i--)); do
loop: Change the node log level
        for ip in "${ip_array[@]}"; do
  printf -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60))
     # Retrieve current node log level
           echo -nedebug_msg "Countdown: ${YELLOW}$countdown${RESET} remaining...\r"
    Retrieving current node log level for IP: $ip"
           sleep 1debug_msg "curl -s -u \"$credentials\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel\""
       done             echo -e "\n\nTime's up! Reverting log level back to ${GREEN}$current_log_level${RESET}..."

current_log_level=$(curl -s -u "$credentials" "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel" | $jq_or_grep -r '.value')
           # Revert thedebug_msg "Current log level backfor toIP the original value
      $ip: $current_log_level"
     response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ original_log_levels["$ip"]=$current_log_level
            debug_msg "Original log level for IP "http$ip:// ${iporiginal_log_arraylevels[0"$ip"]}:91/api/storage/clusters/$clusterName/settings/log.level"
\            if     -d "{\"value\": [[ "$current_log_level}") == "0" ]];  then
       debug_msg "Response: $response"       current_log_level_name="default"
      if [[ "$jq_or_grep" == "grep" ]]; thenelse
                revertedcurrent_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')_name=${log_levels[$current_log_level]}
            fi
             elseoriginal_log_level_names["$ip"]=$current_log_level_name
            debug_msg "Original log level name: reverted${original_log_level=$(echo "$response_names["$ip"]}"
| "$jq_or_grep" -r '.value')         if [[ "$new_log_level" ==  fi
"0" ]]; then
           debug_msg "Reverted cluster log level: $revertednew_log_level_name="default"
            else
final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)             size_diff=$(( final_size - initial_size ))new_log_level_name=${log_levels[$new_log_level]}
             size_diff_formatted=$(format_size "$size_diff")fi
              duration_formatted=$(format_duration "$duration")
	    if (( size_diff < 0 )); then
		echo -e "castor.log file was rotated."
	    else
   debug_msg "Current log level for IP $ip: $current_log_level ($current_log_level_name)"
            echo ""
            echo -e "ApproximateNew node log level: ${BOLD_GREEN}$size$new_log_difflevel_formatted$name${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level${RESET}. Current castor.log size"
            echo -e "Current node log level for IP $ip is ${BOLD_GREEN}$(format_size "$final_size")$current_log_level_name.${RESET} after $duration_formatted."
	    fi
"

            # Skip update if new level matches the current level
            if [[ "$reverted$current_log_level" -eq "$current$new_log_level" ]]; then
                echo ""
                echo -e "LogNode log level reverted successfully backfor IP $ip is already set to ${BOLD_GREEN}$current$new_log_level_level$name${RESET}. No changes made."
                continue
   else         fi

           echo -e# "FailedUpdate tothe revertnode log level. Response: ${RED}$response${RESET}" using PUT
            debug_msg "Updating node log level to  exit 1$new_log_level_name for IP $ip..."
            debug_msg fi"curl --user \"$credentials\" -sS -X PUT -H \"Content-Type:  elseapplication/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level\""
            echo "Log level change is permanent until manually modified."response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \
         fi     elif [[ "$log_level_type" == "node" ]]; then "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level")
             # First loopdebug_msg "Response: Change$response"
the
node log level         for ipif in[[ "${ip_array[@]}"; do
  $jq_or_grep" == "grep" ]]; then
         # Retrieve current node log level             debug_msg "Retrieving current node log level for IP: $ip"updated_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
            debug_msgelse
"curl -s -u \"$credentials\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel\""             currentupdated_log_level=$(curl -s -u "$credentials" "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel" | echo "$response" | "$jq_or_grep" -r '.value')
            fi
  original_log_levels["$ip"]=$current_log_level          debug_msg "Updated log if [[ "$currentlevel for IP $ip: $updated_log_level"
==
"0" ]]; then          if [[ "$updated_log_level"     current-eq "$new_log_level_name="default" ]]; then
          else      echo -e "Node log level for IP $ip changed successfully from current${GREEN}$current_log_level_name=name${RESET} → ${log_levels[$currentBOLD_GREEN}$new_log_level]}_name${RESET}."
            fi
            original_log_level_names["$ip"]=$current_log_level_nameelse
                ifecho [[-e "$new_log_level" == "0" ]]; then
    Failed to update node log level for IP $ip. Response: ${RED}$response${RESET}"
           new_log_level_name="default"     exit 1
      else      fi
        done

new_log_level_name=${log_levels[$new_log_level]}        # Second loop: Countdown if fiduration is provided
        if  debug_msg "Current log level for IP $ip: $current_log_level ($current_log_level_name)"[[ -n "$duration" && "$duration" -gt 0 ]]; then
            echo -e ""
            echo -e "NewKeeping node(s) log level: at ${BOLD_GREENYELLOW}$new_log_level_name${RESET} for ${YELLOW}$duration${RESET} second(s)..."
            echo -e "Current node log level for IP $ip is ${GREEN}$current_log_level_name.${RESET}"

            # Skip update if new level matches the current level""
            for ((i=duration; i>0; i--)); do
                printf -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60))
                echo -ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r"
  if [[ "$current_log_level" -eq "$new_log_level" ]]; then        sleep 1
       echo ""    done
            echo -e "Node\n\nTime's up! Reverting node log level forback IPto $ip is already set to ${BOLD_GREEN}$new_log_level_name${RESET}. No changes made."original levels..."

            # Third loop: Revert the node log level
  continue          for ip  fi
in "${ip_array[@]}"; do
            # Update the node current_log level using PUT_level=${original_log_levels["$ip"]}
              debug_msg "Updating node current_log level to $new_level_name=${original_log_level_name for IP $ip..."names["$ip"]}
            debug_msg "curl --user \"$credentials\" -sS -X PUT -H \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level\""    debug_msg "Reverting node log level back to $current_log_level_name for IP $ip..."
                 response=$(debug_msg "curl --user \"$credentials\" -sS -X PUT -H \"Content-Type: application/json\" \
                "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level")
            debug_msg "Response: $response"

            if [[ "$jq_or_grep" == "grep" ]]; then/settings/log.nodeLogLevel?value=$current_log_level\""
                  updated_log_levelresponse=$(echocurl --user "$response$credentials" | grep-sS -X PUT -oPH '"value":\s*\K[0-9]+')
 Content-Type: application/json" \
          else                 updated_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')
     "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level")
      fi             debug_msg "Updated log level for IP $ip: $updated_log_level"Response: $response"

                if [[ "$updated$jq_logor_levelgrep" -eq== "$new_log_levelgrep" ]]; then
                 echo -e "Node reverted_log _level for IP $ip changed successfully from ${GREEN}$current_log_level_name${RESET} → ${BOLD_GREEN}$new_log_level_name${RESET}."=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
                else
                echo -e "Failed to update node log level for IP $ip. Response: ${RED}$response${RESET}"    reverted_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')
                fi

      exit 1         final_size=$(stat -c%s "$log_file"  fi2>/dev/null || echo 0)
         done       size_diff=$(( final_size - # Second loop: Countdown if duration is providedinitial_size ))
              if [[ -n "$duration" && "$duration" -gt 0 ]]; then debug_msg "Initial log file size: $initial_size"
                echo -e ""
  debug_msg "Final log file size: $final_size"
         echo -e "Keeping node(s) log level at ${YELLOW}$new_log_level_name${RESET} for ${YELLOW}$duration${RESET} second(s)..."size_diff_formatted=$(format_size "$size_diff")
               echo -e duration_formatted=$(format_duration ""
     $duration")

     for ((i=duration; i>0; i--)); do       if [[ "$reverted_log_level" -eq "$current_log_level" ]]; then
   printf -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60))       echo -e "Node log level for IP $ip reverted successfully echoback -ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r"to ${BOLD_GREEN}${current_log_level_name}${RESET}."
                else
     sleep    1           echo -e done"Failed to revert node log level for IP $ip. Response: ${RED}$response${RESET}"
  echo -e "\n\nTime's up! Reverting node log level back to original levels..."       exit 1
     # Third loop: Revert the node log level    fi
        for ip in "${ip_array[@]}"; do done

            # Combine  current_log_level=${original_log_levels["$ip"]}
         the log output for multiple IP addresses into a single summary
	    if ((  current_log_level_name=${original_log_level_names["$ip"]}
          size_diff < 0 )); then
		echo -e "castor.log file was rotated."
	    else
 debug_msg "Reverting node log level back to $current_log_level_name for IP $ip..."     echo -e ""
         debug_msg "curl --user \"$credentials\" -sS -X PUT -Hecho \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level\""
                response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \
  e "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level_name${RESET} for IP ${ip_array[*]}. Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after $duration_formatted."
	    fi
        else
        "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level")    echo "Log level change is permanent until       debug_msg "Response: $response"manually modified."
        fi
    fi
}

# Run ifin [[detachable "$jq_or_grep" == "grep" ]]directly
if $detachable; then
    # Pass the main_script function to the screen session and store the output in a  reverted_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')
     file
    debug_msg "**********************************************************" | tee -a "$output_log"
    debug_msg "Detach mode - Parameters passed elseto main_script:" | tee -a "$output_log"
    debug_msg "     Swarm     reverted_log_level=$(echo "$responseIP: $swarm_ip" | tee -a "$jq$output_or_greplog"
-r '.value')   debug_msg "     Credentials: $credentials"  | tee -a "$output_log"
 fi   debug_msg "     New Log Level: $new_log_level" | tee -a "$output_log"
  final_size=$(stat -c%s "$logdebug_filemsg " 2>/dev/null || echo 0) New log Level Name: $new_log_level_name" | tee -a "$output_log"
    debug_msg "  size_diff=$(( final_size - initial_size ))
   Duration: $duration" | tee -a "$output_log"
    debug_msg "     Log Level size_diff_formatted=$(format_size "$size_diff")Type: $log_level_type" | tee -a "$output_log"
    debug_msg "     Log File: $log_file" | tee -a duration_formatted=$(format_duration "$duration")"$output_log"
    debug_msg "     Initial Log File Size: $initial_size" |  if [[tee -a "$reverted$output_log_level" -eq ""
    debug_msg "     Current Log Level: $current_log_level" ]];| thentee -a "$output_log"
    debug_msg "     Cluster Name: $clusterName" |   tee  echo -ea "$output_log"Node
 log level for IPdebug_msg $ip" reverted successfully back to ${BOLD_GREEN}${current_log_level_name}${RESET}."
       jq_or_grep: $jq_or_grep" | tee -a "$output_log"
    debug_msg "   else  Detach: $detachable" | tee -a "$output_log"
    debug_msg "     Debug: $debug" | echotee -ea "Failed$output_log"
to revert node log level for IP $ip. Response: ${RED}$response${RESET}debug_msg "**********************************************************" | tee -a "$output_log"
    
    # Convert associative arrays to strings and pass them to the exitscreen 1session
    log_levels_string=$(declare -p log_levels)
    log_level_names_string=$(declare -p log_level_names)
  fi  debug_msg "log_levels_string: $log_levels_string" | tee -a "$output_log"
    done

    debug_msg "log_level_names_string: $log_level_names_string" | tee -a "$output_log"

    if command #-v Combine the log output for multiple IP addresses into a single summary
	    if (( size_diff < 0 )); then
		echo -e "castor.log file was rotated."
	    else
     screen &>/dev/null; then
        echo -e "Running in ${YELLOW}screen${RESET} detachable mode..." | tee -a "$output_log"
        # if [[ "$debug" ]]; then
        #     echo -e ""Debug mode enabled. Check the output log for debug messages."
       echo -e# "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level_name${RESET} for IP ${ip_array[*]}. Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after $duration_formatted."
	 sleep 5
        # fi
     fi   screen -dmS indexer_script bash -c  else
            echo "Log level change is permanent until manually modified."
  "$(declare -f main_script timestamp debug_msg format_size format_duration check_jq determine_log_file); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" \"$detachable\" \"$debug\" \"${log_levels_string}\" \"${log_level_names_string}\" | tee \"$output_log\""
     fi   screen  fi
}-r indexer_script
 # Run in detachable or directly if $detachable;# thenWait for    # Pass the main_scriptscreen functionsession to thecomplete screenand sessionthen and storedisplay the output in a filelog
    if command -v screen &>/dev/null; then sleep 1
        #while screen -dmSlist indexer_script| bashgrep -cq "$(declare -f main_script format_size format_duration check_jq debug); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" | tee \"$output_log\"""indexer_script"; do
            sleep 1
        done
  screen -dmS indexer_scriptelif bashcommand -c "$(declare -f main_script debug_msg format_size format_duration check_jq); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" \"$debug\" | tee \"$output_log\""v tmux &>/dev/null; then
        echo -e "Running in ${YELLOW}tmux${RESET} detachable mode..." | tee -a "$output_log"
        # if [[ screen -r indexer_script"$debug" ]]; then
    elif command -v tmux &>/dev/null; then #     echo -e "Debug mode enabled. #Check tmuxthe new-session -d -s indexer_script "$(declare -f main_script format_size format_duration check_jq debug); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" | tee \"$output_log\""output log for debug messages."
        #     sleep 5
        # fi
        tmux new-session -d -s indexer_script "$(declare -f main_script timestamp debug_msg format_size format_duration check_jq determine_log_file); main_script \"$swarm_ip\" \"$credentials\" \"$new_log_level\" \"$new_log_level_name\" \"$duration\" \"$log_level_type\" \"$clusterName\" \"$jq_or_grep\" \"$detachable\" \"$debug\" | tee \"$output_log${log_levels_string}\""
        tmux attach-session -t indexer_script
    else
        echo "Error: Neither screen nor tmux available. Run without --detachable."
   \"${log_level_names_string}\" | tee \"$output_log\""
     exit 1  tmux attach-session -t fiindexer_script
     # Wait for the screen session to complete and then display the output log
    sleep 1
    while screen -list | grep -q "indexer_script"; doelse
        echo "Error: Neither screen nor tmux available. Run without --detachable."
         sleepexit 1
    donefi

    echo ""
    cat "$output_log"
else
    # main_script "$swarm_ip" "$credentials" "$new_log_level" "$duration" "$clusterName" "$jq_or_grep" | tee "$output_log"
    debug_msg "**********************************************************"
    debug_msg "Parameters passed to main_script:"
    debug_msg "Swarm IP: $swarm_ip"
    debug_msg "Credentials: $credentials"
    debug_msg "New Log Level: $new_log_level"
    debug_msg "Duration: $duration"
    debug_msg "Cluster Name: $clusterName"
    debug_msg "jq_or_grep: $jq_or_grep"
    debug_msg "**********************************************************"
    debug_msg "Running main_script function..."
    main_script "$swarm_ip" "$credentials" "$new_log_level" "$new_log_level_name" "$duration" "$log_level_type" "$clusterName" "$jq_or_grep" | tee "$output_log"
fi

...