...
Set and Revert Log Levels: Temporarily change the log level and revert after a specified duration.
Flexible JSON Parsing: Uses
jq
for JSON parsing if available; defaults tojq
otherwise.Detachable Session Execution: Runs in a detachable session using
screen
ortmux
, allowing the script to continue running independently of the terminal session. If bothscreen
andtmux
are installed, the script will prioritizescreen
first.Supports Cluster and Node Log Levels: Allows setting log level at both the cluster and individual node levels.
Log File Auto-Detection: Automatically detects and adjust the
castor.log
file path forCSN
andSCS
environments.Log Size Monitoring: Reports the log size generated during the temporary log level change.
Countdown Display: Shows a countdown for the specified duration.
...
Code Block |
---|
./castor-change-log-level.sh -d 192.168.8.84 -p admin:datacore -i 20 -t 600 |
Example 2: Set node(s) log level to debug (10) and keep it for 10 minutes
Code Block |
---|
./castor-change-log-level.sh -d 192.168.8.84,192.168.8.86 -p admin:datacore -i debug -t 600 |
Example 3: Run in
...
detachable mode
Code Block |
---|
./castor-change-log-level.sh -d 192.168.8.84 -p admin:datacore -i 20 -t 30 -D |
...
The at
command can used to schedule the script to run at a later time. This is useful when you need to start collecting debug logs at a specific hour.
Example 4: Secure password prompt if password are not provided
Code Block |
---|
./castor-change-log-level.sh -p admin -d 192.168.8.84 -i 10 -t 300 |
(You will be prompted to enter the password securely.)
Code Block |
---|
./castor-change-log-level.sh -p admin -d 192.168.8.84 -i 10 -t 300
Enter password for user admin: |
Example 5: Schedule the script to run at 3:00 AM on 03/02/2025 and collect debug logs of node 192.168.8.84
and 192.168.8.89
for 1 hour
Ensure the at service is installed, enabled and running
Code Block dnf -y install epel-release dnf -y install at atd systemctl enable --now at
Schedule the script execution using
at
:Code Block echo "/root/dist/castor-change-log-level.sh -i -p admin:datacore -d 192.168.8.84 -L '192.168.8.84,192.168.8.89' 10 -t 3600" | at 03:00 AM 03/02/2025
...
To verify scheduled job:
Code Block atq 7 Sun Mar 02 03:00:00 2025 a root
To remove a scheduled job (replace
JOB_ID
with the actual job number fromatq
output):Code Block atrm 7
Behavior
Log Level Change: Sets the log level to the specified value. If the current log level matches the requested level, the script skips the update.
Countdown: During the specified duration, the script displays a countdown every second.
Revert Log Level: After the countdown, the log level reverts to the initial value.
Log Size Report: Provides approximately log size generated during the temporary log level change.
Output Messages
...
Message
...
Description
...
Swarm IP:
...
Displays the specified Swarm IP address.
...
Cluster Name:
...
Displays the cluster name retrieved from the Swarm API.
...
New log level:
...
Shows the new log level requested.
...
Current log level:
...
Displays the current log level.
...
Updating log level to X...
...
Indicates the beginning of the log level update process.
...
Reattaching to a Detached Session
For more details on using screen and tmux, refer to their official documentation:
If you accidentally close your terminal (e.g. putty, iterm2, etc) while the script is running in a detached session, you can reattach using the following commands:
For screen
users:
List active
screen
session:Code Block screen -ls
Reattach to the session:
Code Block screen -r <session_name>
For tmux
users:
List active
tmux
sessions:Code Block tmux ls
Reattach to the session:
Code Block tmux a -t <session_name>
If the session has ended or was not found, you may need to restart the script manually.
Behavior
Log Level Change: Sets the log level to the specified value. If the current log level matches the requested level, the script skips the update.
Countdown: During the specified duration, the script displays a countdown every second.
Revert Log Level: After the countdown, the log level reverts to the initial value.
Log Size Report: Provides approximately log size generated during the temporary log level change.
Output Messages
Message | Description |
---|---|
| Displays the specified Swarm IP address. |
| Displays the cluster name retrieved from the Swarm API. |
| Shows the new log level requested. |
| Displays the current log level. |
| Indicates the beginning of the log level update process. |
| Confirms that the log level was successfully updated. |
| Shows the temporary period for which the new log level is retained, with a countdown. |
| Indicates that the temporary period has ended and the script is reverting the log level. |
| Provides information on the amount of logging activity generated during the temporary log level. |
...
Code Block | ||
---|---|---|
| ||
#!/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.2 - 2025-02-27 Address the issue of the script not display correct when the castor.log file is rotated. # v1.2.3 - 2025-02-27 Address the issue of log level not display correct within detach session. # v1.2.4 - 2025-02-27 Bug fix: The default node-level log level is 0 (unset), which differs from the cluster-level default of 30. # v1.2.5 - 2025-02-28 SUPSCR-208: # - Credentials validation and password prompt enhancements. # - screen or tmux required with detachable mode, script will stopped with '-D' option if neither is installed. # - Fixed an issue where users were prompted to enter password multiple times issue. # - Passwords are now hidden in debug output messages. # v1.2.6 - 2025-02-28 SUPSCR-208: # - Disabled credential display on the screen. # - Bug fix: Resolved the "debug: command not found" error when removing the -d [IP address] option. # v1.3.0 - 2025-02-28 minorMinor bug fix and enhancement. # v1.3.1 - 2025-03-01 SUPSCR-208: # - Fixed an issue where credentials enclosed in single quotes (') were not processed correctly. # v1.3.2 - 2025-03-02 minorMinor bug fix and enhancement. # v1.3.3 - 2025-03-02 Fixed tmux command to long issue. # ----------------------------------------------------------------------------------------------------------------------------- # Current Version: 1.3.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() {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 -e "" echo -e "${BOLD_GREEN}Usage:${RESET} ./$SCRIPT_NAME -d swarm_ip -p admin:password [-i new_log_level | -L new_node_log_level] [-t duration_in_seconds] [-D | --detach]" echo -e " -d, --swarm_ip IP address of the Swarm API endpoint. Supports single or multiple IPs separated by \",\", \";\" or \" \"." echo -e " If multiple IPs are provided, the script will update the log level for all nodes." echo -e " (Alternatively, set the SCSP_HOST environment variable to the Swarm IP.)" echo -e " -p, --credentials Credentials in the format admin:password" echo -e " -i, --log.level Set cluster log level to set (5, 10, 15, 20, 30, 40, 50)" echo -e " Aliases: chatter, debug, announce, info, error, critical, default." echo -e " -L, --node.log.level Set node log level to set (0, 5, 10, 15, 20, 30, 40, 50)" echo -e " Aliases: chatter, debug, announce, info, error, critical, default." echo -e " **Either -i or -L must be specified, but not both.**" echo -e "" -t, --time echo -e "${BOLD_GREEN}Usage:${RESET} ./$SCRIPT_NAME -d swarm_ip -p admin:password [-i new_log_level | -L new_node_log_level] [-t duration_in_seconds] [-D | --detach] (Optional) Duration in seconds to keep the new log level (must be greater than 0)" echo -e " -dD, --swarm_ipdetach IP address(Optional) ofDetach the script Swarmfrom APIthe endpoint.current Supportsterminal singleand orrun multiplein IPsa separateddetachable by \",\", \";\"session using screen or \tmux" \"." echo -e "" exit 1 } # Default options detachable=false debug=false output_log="castor-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 If multiple IPs are provided, the script will update the log level for all nodes." echo -e " level log_file="/var/log/datacore/castor.log" # Default log file location SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) JQLOCATION=$SCRIPTDIR/jq MAX_RETRIES=3 (Alternatively, set the SCSP_HOST environment variable to the Swarm IP.)" # echoMaximum -enumber "of password -p, --credentials retries attempts username="admin" Credentials in the format admin:password" echo -e " -i, --log.level Set cluster log level to set# (5, 10, 15, 20, 30, 40, 50)"Default username password="" echo -e " Aliases: chatter, debug, announce, info,# error,Default critical,password default." # Global associative array echofor -elog "levels declare -L, --node.log.levelA log_levels=( [5]="chatter" Set node log level to set (0, 5, 10, 15, 20, 30, 40, 50) [10]="debug" [15]="audit" [20]="info" echo -e "[30]="warning" [40]="error" [45]="defect" [50]="critical" [60]="announce" ) # Global associative array for log Aliases:level chatter,names debug, announce, info, error, critical, default." echo -e "declare -A log_level_names=( ["chatter"]=5 ["debug"]=10 ["audit"]=15 ["info"]=20 ["warning"]=30 ["error"]=40 ["defect"]=45 **Either -i or -L must be specified, but not both.**" echo -e " -t, --time ["critical"]=50 ["announce"]=60 ) # Separate default values for cluster and node levels declare -A default_log_levels=( ["cluster"]=30 (Optional["node"]=0 ) Duration # inSeparate secondsdefault tovalues keepfor thecluster newand lognode level (mustnames be greater than 0)" declare -A default_log_level_names=( echo -e ["cluster"]="default" -D, --detach ["node"]="default" ) # Function to get the current timestamp timestamp(Optional) Detach{ the script fromdate the current terminal and run in a detachable session using screen or tmux" echo -e "" exit 1 } # Default options detachable=false debug=false output_log="castor-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" -u +"%Y-%m-%dT%H:%M:%S.%3NZ" } # Function to display debug messages if debug mode is enabled debug_msg() { if $debug; then local caller_func=${FUNCNAME[1]:-main} # Fallback to "unknown" if empty # Default log file location SCRIPTDIR=$( cd "$( dirname "local caller_line=${BASH_SOURCELINENO[0]}" )" && pwd ) JQLOCATION=$SCRIPTDIR/jq MAX_RETRIES=3 :-unknown} # Fallback to "unknown" if empty echo -e "$(timestamp) [DEBUG] ($caller_func:$caller_line) $*" fi } # Function to check if either 'screen' or 'tmux' is # Maximum number of password retries attempts username="admin" installed check_screen_or_tmux() { if ! command -v screen &>/dev/null && ! command -v tmux &>/dev/null; then echo -e "" echo # Default username password=""-e "---------------------------------------------------------------------------------------" echo -e " ${YELLOW}Warning${RESET}: Neither '${BOLD_GREEN}screen${RESET}' nor '${BOLD_GREEN}tmux${RESET}' is installed. Cannot run in detachable mode." echo -e " Please install either # Default password # 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" ) # 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 ) # Separate default values for cluster and node levels declare -A default_log_levels=( ["cluster"]=30 ["node"]=0 ) # Separate default values for cluster and node level names declare -A default_log_level_names=( ["cluster"]="default" ["node"]="default" ) # Function to get the current timestamp timestamp() { date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"'${BOLD_GREEN}screen${RESET}' or '${BOLD_GREEN}tmux${RESET}' to run the script in a detachable session." echo -e "---------------------------------------------------------------------------------------" detachable=false # Disable detachable session usage 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 display debug messages if debug mode is enabled debug_msg() { format duration format_duration() { local duration=$1 local hours=$((duration / 3600)) local if $debug; then minutes=$(( (duration % 3600) / 60 )) local caller_func=${FUNCNAME[1]:-main} # Fallback to "unknown" if empty 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() { for jq_path in "/usr/local caller_line=${BASH_LINENO[0]:-unknown}/bin/jq" "$(pwd)/jq"; do [[ -x # Fallback to "unknown" if empty"$jq_path" ]] && echo "$jq_path" && return done echocommand -e "$(timestamp) [DEBUG] ($caller_func:$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 -e "" echo -e "---------------------------------------------------------------------------------------" echo -e " ${YELLOW}Warning${RESET}: Neither '${BOLD_GREEN}screen${RESET}' nor '${BOLD_GREEN}tmux${RESET}' is installed. Cannot run in detachable mode."v jq &>/dev/null && echo "jq" || echo "grep" } 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() { for log in "/var/log/datacore/castor.log" "/var/log/caringo/castor.log"; do if [[ -f "$log" ]]; then echo "$log" return fi echo -e " done Please installecho either-e '"${BOLD_GREEN}screen${RESET}' or '${BOLD_GREEN}tmux${RESET}' to run the script in a detachable session." echo -e "---------------------------------------------------------------------------------------" echo -e "RED}Error: Log file not found.${RESET}" exit 1 } log_file=$(determine_log_file) # Function to check if credentials are valid check_credentials() { local CREDENTIALS="$1" local SWARM_IP="$2" debug_msg "Credentials: [hidden detachable=falsefor security]" # Disable detachable sessiondebug_msg "Swarm IP: $SWARM_IP" if# [[ "$detachable" == false ]]; thenAPI endpoint for validating user credentials local VALIDATE_URL="http://${SWARM_IP}:91/api/validateUser" debug_msg "detachable=${YELLOW}false${RESET}Validate URL: $VALIDATE_URL" # Make the API request debug_msg "StoppingValidating theuser scriptcredentials..." debug_msg "curl -s -u \"********\" -X GET exit 1 \"$VALIDATE_URL\" -H 'Content-Type: application/json'" fi 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 ))RESPONSE=$(curl -s -u "$CREDENTIALS" -X GET "$VALIDATE_URL" -H 'Content-Type: application/json') debug_msg "Validate User Response: $RESPONSE" # Check if the response contains "isValid": true if echo "$RESPONSE" | "$jq_or_grep" -e '.isValid == true' > /dev/null 2>&1; then echodebug_msg "$(awk "BEGIN {printf \"%.1fMB\", $size/1048576}")"Authentication successful for user '${CREDENTIALS%%:*}'" elif (( size >= 1024return ));0 then # Success elif echo "$(awk "BEGIN {printf \"%.1fKB\", $size/1024}")" $RESPONSE" | "$jq_or_grep" -e '.isValid == false' > /dev/null 2>&1; then else debug_msg "Authentication failed for echouser "'${sizeCREDENTIALS%%:*}B'" fi } # Functionreturn to1 format duration format_duration() {# Failure localelse duration=$1 local hours=$((duration / 3600)) debug_msg "Error: Unable local minutes=$(( (duration % 3600) / 60 ))to validate credentials. Please check your inputs." local seconds=$((duration % 60))return 1 # Failure printf "%02d:%02d:%02d" $hours $minutes $secondsfi } # Function to check if jq is available and set up JSON parsing method check_jq() { print credentials - hide password print_credentials() { local CREDENTIALS="$1" local USERNAME="${CREDENTIALS%%:*}" for jq_path in "/usr/local/bin/jq" "$(pwd)/jq"; do [[ -x "$jq_path" ]] && echo "$jq_path" && return doneecho -e "${GREEN}$USERNAME${RESET}":"${GREEN}********${RESET}" } # Parse input arguments while [[ "$#" -gt 0 ]]; do case $1 in command -v jq &>/dev/null && echo "jq" || echo "grep" } 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() { for log in "/var/log/datacore/castor.log" "/var/log/caringo/castor.log"; do-d|--swarm_ip) swarm_ip="$2"; debug_msg "Set swarm_ip to $swarm_ip"; shift 2 ;; -p|--credentials) credentials="$2" shift 2 ;; -i|--log.level) if [[ -fn "$log$new_log_level" ]]; then echo "$logError: Options -i (cluster log leve) and -L (node log level) cannot be used together." return usage fi done echo -e "${RED}Error: Log file not found.${RESET}" log_level_type="cluster" exit 1 }if [[ ${log_file=$(determine_log_file) # Function to check if credentials are valid check_credentials() { local CREDENTIALS="$1"level_names[$2]} ]]; then local SWARMnew_log_IP="$2"level=${log_level_names[$2]} debug_msg "Credentials: [hidden for security]" elif debug_msg "Swarm IP: $SWARM_IP" [[ "$2" == "default" ]]; then # API endpoint for validating user credentials local VALIDATEnew_log_URLlevel="http://${SWARM_IP}:91/api/validateUser"default_log_levels[$log_level_type]} debug_msg "Validate URL: $VALIDATE_URL new_log_level_name="default" # Make the API request elif [[ debug_msg "Validating user credentials..."${log_levels[$2]} ]]; then debug_msg "curl -s -u \"********\" -X GET \"$VALIDATE_URL\" -H 'Content-Type: application/json'" new_log_level=$2 RESPONSE=$(curl -s -u "$CREDENTIALS" -X GET "$VALIDATE_URL" -H 'Content-Type: application/json') else debug_msg "Validate User Response: $RESPONSE" # Check ifecho the"Invalid responselog containslevel: $2"isValid": true if echo "$RESPONSE" | "$jq_or_grep" -e '.isValid == true' > /dev/null 2>&1; thenexit 1 debug_msg "Authenticationfi successful for user '${CREDENTIALS%%:*}'" return 0 # Successnew_log_level_name=${log_levels[$new_log_level]} elif echo "$RESPONSE" | "$jqdefault_or_grep" -e '.isValid == false' > /dev/null 2>&1; then log_level=30 debug_msg "Authentication failed for user '${CREDENTIALS%%:*}'Set new_log_level to $new_log_level ($new_log_level_name)" return 1 # Failureshift 2 else debug_msg "Error: Unable to validate credentials. Please check your inputs.";; -L|--node.log.level) return 1 # Failureif [[ -n "$new_log_level" ]]; fithen } # Function to print credentials - hide password print_credentials() { localecho CREDENTIALS="$1" local USERNAME="${CREDENTIALS%%:*}" echo -e "${GREEN}$USERNAME${RESET}":"${GREEN}********${RESET}" } # Parse input arguments while [[ "$#" -gt 0 ]]; do case $1 in"Error: Options -i (cluster log leve) and -L (node log level) cannot be used together." usage fi -d|--swarm_ip) swarm_ip="$2"; debug_msg "Set swarm_ip to $swarm_ip"; shift 2 ;;log_level_type="node" if [[ ${log_level_names[$2]} -p|--credentials)]]; then credentials="$2" new_log_level=${log_level_names[$2]} shift 2 ;; elif [[ "$2" == "default" ]]; -i|--log.level)then if [[ -n "$new_log_level" ]]; then new_log_level=${default_log_levels[$log_level_type]} echo "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together." new_log_level_name="default" elif [[ ${log_levels[$2]} || $2 -eq 0 ]]; then usage new_log_level=$2 fi new_log_level_typename="clusterdefault" if [[ ${log_level_names[$2]} ]]; thenelse echo "Invalid new_log_level=${log_level_names[$2]} level: $2" exit 1 elif [[ "$2" == "default" ]]; then fi new_log_level_name=${default_log_levels[$log$new_log_level_type]} newdefault_log_level_name="default"0 elif [[ ${log_levels[$2]} ]]; then debug_msg "Set new_log_level to $new_log_level ($new_log_level_name)" shift 2 new_log_level=$2 ;; else -t|--time) echo "Invalidif log level: [[ -n "$2" && "$2" != -* && "$2" -gt 0 ]]; then exit 1 duration="$2" fi new_log_level_name=${log_levels[$new_log_level]} debug_msg "Set duration to $duration" default_log_level=30 shift 2 debug_msg "Set new_log_level toelse $new_log_level ($new_log_level_name)" shift 2 echo -e "" ;; echo -e -L|--node.log.level) if [[ -n "$new_log_level" ]]; then"---------------------------------------------------------------------------------------" echo -e echo "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together ${RED}Error${RESET}: Duration must be a number greater than ${BOLD_GREEN}0${RESET}." usageecho -e " Please specify the duration using the fi log_level_type="node"${BOLD_GREEN}-t${RESET} ${GREEN}<seconds>${RESET} or ${BOLD_GREEN}--time${RESET} ${GREEN}<seconds>${RESET} option." if [[ ${log_level_names[$2]} ]]; thenecho -e "---------------------------------------------------------------------------------------" new_log_level=${log_level_names[$2]}usage eliffi [[ "$2" == "default" ]]; then ;; new_log_level=${default_log_levels[$log_level_type]} -D|--detach) detachable=true; debug_msg "Set detachable to true"; shift ;; --debug) debug=true; new_log_level_name="default" debug_msg "Set debug to true with ${YELLOW}--debug${RESET}"; shift ;; elif [[ ${log_levels[$2]} || $2 -eq 0 ]]; then new_log_level=$2 *) usage ;; esac done debug_msg "Set log_level_type to ${YELLOW}$log_level_type${RESET}" # Check if 'screen' or 'tmux' is installed if [[ "$detachable" == true ]]; then if [[ new_log_level_name="default" -z "$duration" ]]; then echo -e else"" echo -e "---------------------------------------------------------------------------------------" echo -e "Invalid log level: $2" ${RED}Error${RESET}: Duration must be specified when running in detachable mode." exitecho 1-e " Please specify the duration using the ${BOLD_GREEN}-t${RESET} fi or ${BOLD_GREEN}--time${RESET} option." echo new_log_level_name=${log_levels[$new_log_level]}-e "---------------------------------------------------------------------------------------" usage default_log_level=0 fi debug_msg "Set new_log_level to $new_log_level ($new_log_level_name)" shift 2 ;; -t|--time) check_screen_or_tmux fi # If swarm_ip is not provided, try using SCSP_HOST environment variable if [[ -z "$swarm_ip" ]]; then if [[ -n "$2" && "$2" != -* && "$2" -gt 0$SCSP_HOST" ]]; then swarm_ip="$SCSP_HOST" debug_msg duration="$2"Using Swarm IP from SCSP_HOST: $swarm_ip" else debug_msgecho "Set duration to $duration" Error: swarm_ip not provided and SCSP_HOST is not set." usage shift 2 fi fi # Check if required arguments are provided if else [[ -z "$credentials" || -z "$new_log_level" ]]; then usage fi # Split the swarm_ip into an echoarray "Error:of DurationIP mustaddresses beif ait numbercontains greater than 0." exit 1 fi ;; -D|--detach) detachable=true; debug_msg "Set detachable to true"; shift ;; --debug) debug=true;delimiters IFS=';, ' read -r -a ip_array <<< "$swarm_ip" # Validate credentials before proceeding debug_msg "Validating credentials..." #if [[ "$credentials" =~ ^[^:]+$ ]]; then if [[ -n "$credentials" ]]; then CREDENTIALS="$credentials" ATTEMPT=$MAX_RETRIES USERNAME="" PASSWORD="" debug_msg "CREDENTIALS: $(print_credentials "$CREDENTIALS")" debug_msg "Set debug to true with ${YELLOW}--debug${RESET}"; shift ;;ATTEMPT: $ATTEMPT" # Check if *) usage ;; esac donecredentials contain a colon (username:password format) debug_msg "Set log_level_type to ${YELLOW}$log_level_type${RESET}" # Check if 'screen' or 'tmux' is installedChecking for colon in credentials..." if [[ "$detachable$CREDENTIALS" == true*":"* ]]; then check_screen_or_tmux fi # If swarmdebug_ipmsg is"Credentials notcontain provided,colon" try using SCSP_HOST environment variable if [[ -z "$swarm_ip" ]]; thenUSERNAME="${CREDENTIALS%%:*}" if [[ -n "$SCSP_HOST" ]]; then PASSWORD="${CREDENTIALS#*:}" swarm_ip="$SCSP_HOST" # Validate credentials debug_msg "Using Swarm IP from SCSP_HOST: $swarm_ipcheck_credentials "$CREDENTIALS" "${ip_array[0]}" else if [[ $? -ne 0 ]]; echothen "Error: swarm_ip not provided and SCSP_HOST is not set." debug_msg "Invalid credentials. Please check usageyour username and password." fi fi # Check if required arguments are provided if [[echo -ze "$credentials" || -z "$new_log_level" ]]; then echo 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 [[ "$credentials" =~ ^[^:]+$ ]]; then if [[ -n "$credentials" ]]; then CREDENTIALS="$credentials" ATTEMPT=$MAX_RETRIES USERNAME="" PASSWORD="" debug_msg "CREDENTIALS: $(print_credentials "$CREDENTIALS")"-e "${RED}Error${RESET}: Invalid credentials. Please check your username and password." echo -e "" exit 1 fi debug_msg "Validate credentials" credentials=$CREDENTIALS else debug_msg "ATTEMPT: $ATTEMPT" Credentials do not contain password" # Check if credentials contain a colon (username:password format)debug_msg "Username: $(print_credentials "$CREDENTIALS")" debug_msg "Checking for colon in credentials... USERNAME="$CREDENTIALS" if [[ "$CREDENTIALS" == *":"* ]]; then # Prompt for password debug_msg "Credentialswhile contain[[ colon"$ATTEMPTS -lt $MAX_RETRIES ]]; do USERNAME="${CREDENTIALS%%:*}" echo PASSWORD="${CREDENTIALS#*:}" # Validate credentials read -sp "Enter password for check_credentials "$CREDENTIALS" "${ip_array[0]}" user $USERNAME: " PASSWORD if [[ $? -ne 0 ]]; thenecho "" debug_msgecho ""Invalid credentials. Please check your username and password." if [[ -z "$PASSWORD" ]]; then echo -e "" echo -e "${RED}Error${RESET}: InvalidPassword credentials.cannot Pleasebe checkempty." your username and password." echo -e ""((ATTEMPTS++)) exit 1 continue fi fi debug_msg "Validate credentials" credentials=$CREDENTIALS CREDENTIALS="$USERNAME:$PASSWORD" else debug_msg "Credentials do not contain password"CREDENTIALS: $(print_credentials "$CREDENTIALS"))" debug_msg "Username: $(print check_credentials "$CREDENTIALS")" "${ip_array[0]}" if [[ USERNAME="$CREDENTIALS" $? -eq 0 ]]; then # Prompt for password while [[ $ATTEMPTS -lt $MAX_RETRIES ]]; dodebug_msg "CREDENTIALS: $(print_credentials "$CREDENTIALS"))" echodebug_msg "Valid credentials" read -sp "Enter passwordcredentials=$CREDENTIALS for user $USERNAME: " PASSWORD break echo "" else echo "" if [[echo -ze "$PASSWORD" ]]; then echo -e "${RED}Error${RESET}: Password cannot be empty Invalid credentials. Please check your username and password." fi ((ATTEMPTS++)) done if [[ $ATTEMPTS -ge $MAX_RETRIES ]]; then echo -e "" continue echo -e "${RED}Error${RESET}: Maximum number fiof password attempts reached. Exiting script." CREDENTIALS="$USERNAME:$PASSWORD" exit 1 debug_msg "CREDENTIALS: $(print_credentials "$CREDENTIALS"))" fi fi else if ! check_credentials "$CREDENTIALS$credentials" "${ip_array[0]}"; then echo -e if [[ $? -eq 0 ]]; then"" echo -e "${YELLOW}Warning${RESET}: ${RED}Invalid credentials${RESET}." echo -e "" exit debug_msg "CREDENTIALS: $(print_credentials "$CREDENTIALS"))" 1 fi fi # Retrieve cluster name and handle JSON parsing using the first IP address debug_msg "Valid credentials" Retrieving the cluster name from Swarm API using IP: ${ip_array[0]}" if [[ "$jq_or_grep" == "grep" ]]; then clusterName=$(curl --user "$credentials" -sS credentials=$CREDENTIALS "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" break -r '._embedded.clusters[0].name') fi if [[ -z "$clusterName" ]]; then elseecho "Failed to retrieve the cluster name. Please check your inputs." exit 1 echo -efi debug_msg ""Cluster Name: $clusterName" # Main logic function to run the script tasks echo -e "${RED}Error${RESET}: Invalid credentials. Please check your username and password.main_script() { local swarm_ip="$1" local credentials="$2" local finew_log_level="$3" local new_log_level_name="$4" local duration="$5" ((ATTEMPTS++)) local log_level_type="$6" done #local log_file="/var/log/datacore/castor.log" if [[ $ATTEMPTS -gez $MAX"$log_RETRIESfile" ]]; then log_file=$(determine_log_file) echo fi local initial_size=$(stat -ec%s "$log_file" 2>/dev/null || echo 0) local current_log_level echo -elocal "${RED}Error${RESET}: Maximum number of password attempts reached. Exiting script.clusterName="$7" local jq_or_grep="$8" local detachable="$9" if [[ exit 1 "$detachable" ]]; then fi fi elselocal debug="${10}" if ! check_credentialseval "$credentials"$(echo "${ip_array[0]11}"; then| sed echo -e ""'s/declare -A/declare -A/')" eval "$(echo -e "${YELLOW}Warning${RESET}: ${RED}Invalid credentials${RESET}.12}" | sed 's/declare -A/declare -A/')" fi echo -e "local default_log_levels="$13" local default_log_levels_name="$14" exit 1 fi fi # Retrieve cluster name and handle JSON parsing using the first IP address debug_msg "**********************************************************" debug_msg "local variables" debug_msg "RetrievingLog the cluster name from Swarm API using IPLevel Type: ${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" GREEN}$log_level_type${RESET}" debug_msg "Default Log Level: ${GREEN}$default_log_level${RESET}" debug_msg "Swarm IP: $swarm_ip" debug_msg "Credentials: $(print_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 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 [[ "$detachable$log_level_type" == "cluster" ]]; then echo local-e debug="${10Swarm IP: ${GREEN}${ip_array[0]}${RESET}" else eval "$(echo "${11 echo -e "Swarm IPs: ${GREEN}${ip_array[*]}${RESET}" | sed 's/declare -A/declare -A/')" fi # echo -e "Swarm evalIP: "$(echo "${12}" | sed 's/declare -A/declare -A/')" fi local default_log_levels="$13${GREEN}$swarm_ip${RESET}" debug_msg "Credentials: ${GREEN}[hidden for security]${RESET}" echo -e "Cluster Name: ${GREEN}$clusterName${RESET}" local default_log_levels_name="$14"debug_msg "Starting main_script function..." debug_msg "**********************************************************" debug_msg "local variables" debug_msg "Log Level Type: ${GREEN}$log_level_type${RESET}"Log level type: $log_level_type" # Store the original log levels declare -A original_log_levels declare -A original_log_level_names default_log_level=${default_log_levels[$log_level_type]} default_log_level_name=${default_log_level_names[$log_level_type]} debug_msg "Default Loglog Levellevel: ${GREEN}$default_log_level${RESET}level" debug_msg "Swarm IP: $swarm_ip" debug_msg "Credentials: $(print_credentials "$credentials")"Default log level name: $default_log_level_name" if [[ $log_level_type == "cluster" ]]; then debug_msg "New Log Level: Setting cluster log level to $new_log_level ($new_log_level_name)" debug_msg "New log# LevelRetrieve Name:current $new_log_ level_name" debug_msg "Duration: $duration" if debug_msg "Log File: $log_file" debug_msg "Initial Log File Size: $initial_size"[[ "$jq_or_grep" == "grep" ]]; then debug_msg "Current Log Level: $current_log_level" debug_msg "Cluster Name: $clusterName"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]+') debug_msg "jq_or_grep: $jq_or_grep" else 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 current_log_level=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" | "$jq_or_grep" -r '.value') fi current_log_level_name=${log_levels[$current_log_level]} if [[ $current_log_level == 30 ]]; then current_log_level_name="default" fi if [[ $new_log_level == 30 ]]; then echo -e "Swarm IPs: ${GREEN}${ip_array[*]}${RESET} new_log_level_name="default" fi fi # echo -e "Swarm IP: ${GREEN}$swarm_ip${RESET}" debug_msg "Credentials: ${GREEN}[hidden for security]${RESET}" echo -e "Cluster NameCurrent cluster log level: ${BOLD_GREEN}$clusterName$$current_log_level${RESET}" debug_msg "Starting main_script function..."" debug_msg "LogCurrent log level typename: $log${BOLD_GREEN}$current_log_level_typename${RESET}" # Store theecho original-e log"" levels declare -A original_log_levels echo -e "New cluster declarelog -A originallevel: ${BOLD_GREEN}$new_log_level_namesname${RESET} (${BOLD_GREEN}$new_log_level${RESET})" default_log_level=${default_log_levels[$log_level_type]} default_log_level_name=${default_log_level_names[$log_level_type]}echo -e "Current cluster log level is ${BOLD_GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET})." debug_msg "Default log level: $default_log_level" debug_msg "Default log level name: $default_log_level_name" # Skip update if new level matches the current level if [[ $log"$current_log_level_type == "cluster" -eq "$new_log_level" ]]; then debug_msg "Setting cluster echo "" echo -e "Cluster log level is already set to ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level_name)"level${RESET}). No changes made." return # Retrieve current log level fi if [[ "$jq_or_grep" == "grep" ]]; then # Update the cluster log level using PUT 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]+') debug_msg "Updating cluster log level to $new_log_level_name" debug_msg "Cluster Name: $clusterName" else debug_msg "Credentials: $(print_credentials "$credentials")" current_log_level=$(debug_msg "curl --user \"$(print_credentials "$credentials")\" -sS -X PUT -H \"Content-Type: application/json\" \"http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level\" |-d "$jq_or_grep" -r '.value') fi current_log_level_name=${log_levels[$current\"{\\\"value\\\": $new_log_level]}\"" if [[ $current_log_level == 30 ]]; then current_log_level_name="default" firesponse=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ if [[ $new_log_level == 30 ]]; then "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \ -d new"{\"value\": $new_log_level_name="default"}") fi debug_msg "Response: $response" debug_msg "Current cluster logif level: ${BOLD_GREEN}$current_log_level${RESET}" [[ "$jq_or_grep" == "grep" ]]; then debug_msg "Current log level name: ${BOLD_GREEN}$current updated_log_level_name${RESET}=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+') echo -e "" else echo -e "New cluster log level: ${BOLD_GREEN}$new updated_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})" echo -e "Current cluster log level is ${BOLD_GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET})."=$(echo "$response" | "$jq_or_grep" -r '.value') fi # Skip update if new level matches the current leveldebug_msg "Updated cluster log level: $updated_log_level" if [[ "$current$updated_log_level" -eq "$new_log_level" ]]; then echo "" echo -e "Cluster log level is already set to -e "${GREEN}$(timestamp)${RESET} Log level changed successfully from ${BOLD_GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET}) -> ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})." else echo -e "${GREEN}$(timestamp)${RESET} Failed to update log level. Response: ${RED}$response${RESET})." No changes made." exit 1 return fi # UpdateCountdown theand clusterrevert log level using PUT if [[ debug_msg "Updating cluster-n "$duration" && "$duration" -gt 0 ]]; then echo -e "Keeping log level toat ${YELLOW}$new_log_level_name" debug_msg "Cluster Name: $clusterName"name${RESET} (${YELLOW}$new_log_level${RESET}) for ${YELLOW}$duration${RESET} seconds (${YELLOW}$(format_duration $duration)${RESET}) ..." debug_msg "Credentials: $(print_credentials "$credentials") echo -e "" debug_msg "curl --user \"$(print_credentials "$credentials")\" -sS -X PUT -H \"Content-Type: application/json\" \"http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level\" -d \"{\\\"value\\\": $new_log_level}\"" for ((i=duration; i>0; i--)); do response=$(curl --user "$credentials" -sSprintf -X PUT -H "Content-Type: application/json" \ v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60)) "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \ echo -dne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r"value\": $new_log_level}") debug_msg "Response: $response" sleep 1 if [[ "$jq_or_grep" == "grep" ]]; then done updated_log_level=$(echo "$response" | grepecho -oPe '"value":\s*\K[0-9]+') else\n\nTime's up! Reverting log level back to ${GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET}) ..." # Revert the updated_log_ level=$(echo "$response" | "$jq_or_grep" -r '.value') back to the original value fi # debug_msgecho -e "UpdatedLevel clusterlog logrevert level: $updated_log_levelon $(timestamp)" if [[ "$updated_log_level" -eq "$new_log_level" ]]; then response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ echo -e "${GREEN}$(timestamp)${RESET} Log level changed successfully from ${BOLD_GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET}) -> ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})." "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \ else echo -e-d "${GREEN}$(timestamp)${RESET} Failed to update log level. Response: ${RED}$response${RESET}"{\"value\": $current_log_level}") exit 1 debug_msg "Response: $response" fi if [[ "$jq_or_grep" == # Countdown and revert log level"grep" ]]; then if [[ -n "$duration" && "$duration" -gt 0 ]]; then reverted_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+') else echo -e "Keeping log level at ${YELLOW}$newreverted_log_level_name${RESET} (${YELLOW}$new_log_level${RESET}) for ${YELLOW}$duration${RESET} seconds (${YELLOW}$(format_duration $duration)${RESET}) ..." =$(echo "$response" | "$jq_or_grep" -r '.value') fi echo -e "debug_msg "Reverted cluster log level: $reverted_log_level" for ((i=duration; i>0; i--)); do final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0) debug_msg printf"Initial -vlog countdownfile "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60)) size: $initial_size" debug_msg "Final log file size: $final_size" echo -ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r" size_diff=$(( final_size - initial_size )) sleep 1size_diff_formatted=$(format_size "$size_diff") done duration_formatted=$(format_duration "$duration") if (( size_diff < 0 )); then echo -e "\n\nTime's up! Reverting castor.log levelfile back to ${GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET}) ..." was rotated." else # Revert the log level back to the originalecho value-e "" # echo -e "Level log revert on $(timestamp)" response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET}). Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after ${YELLOW}$duration${RESET} seconds (${YELLOW}$duration_formatted${RESET})." echo "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \-e "" fi if [[ "$reverted_log_level" -deq "{\"value\": $current_log_level}") ]]; then debug_msg "Response: $response"echo -e "${GREEN}$(timestamp)${RESET} Log level reverted successfully back to if [[ "$jq_or_grep" == "grep" ]]; then ${BOLD_GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET})." reverted_log_level=$(echo "$response" | grep -oPe '"value":\s*\K[0-9]+') else echo reverted_log_level=$(echo "$response" | "$jq_or_grep" -r '.value') -e "${GREEN}$(timestamp)${RESET} Failed to revert log level. Response: ${RED}$response${RESET}" fi echo debug_msg "Reverted cluster log level: $reverted_log_level"-e "" exit 1 final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0)fi else debug_msg "Initial log file size: $initial_size" echo -e "${GREEN}$(timestamp)${RESET} Log level change is permanent until debug_msgmanually modified."Final log file size: $final_size" fi elif [[ size_diff=$(( final_size - initial_size )) "$log_level_type" == "node" ]]; then # First size_diff_formatted=$(format_size "$size_diff") loop: Change the node log level local durationsame_log_formatted=$(format_duration "$duration") level=false if (( size_difffor <ip 0 )); then echo -e "castor.log file was rotated." in "${ip_array[@]}"; do else # Retrieve current node log level echo -e "" debug_msg "Retrieving current node log level for IP: $ip" echo -e "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET}). Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after ${YELLOW}$duration${RESET} seconds (${YELLOW}$duration_formatted${RESET})." debug_msg "curl -s -u \"$(print_credentials "$credentials")\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel\"" echocurrent_log_level=$(curl -s -eu "" $credentials" "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel" | $jq_or_grep -r '.value') fi if [[debug_msg "$reverted_Current log_level" -eq " level for IP $ip: $current_log_level" ]]; then original_log_levels["$ip"]=$current_log_level echo -e "${GREEN}$(timestamp)${RESET} Log level reverted successfully back to ${BOLD_GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET}). debug_msg "Original log level for IP $ip: ${original_log_levels["$ip"]}" echoif -e[[ "$current_log_level" == "0" ]]; then else echo -e "${GREEN}$(timestamp)${RESET} Failed to revert log level. Response: ${RED}$response${RESET}"current_log_level_name="default" else echo -e "" current_log_level_name=${log_levels[$current_log_level]} exit 1 fi else original_log_level_names["$ip"]=$current_log_level_name echo -e "${GREEN}$(timestamp)${RESET} Log level change is permanent until manually modified.debug_msg "Original log level name: ${original_log_level_names["$ip"]}" fi if elif [[ "$log$new_log_level_type" == "node0" ]]; then # First loop: Change the node new_log level_level_name="default" else local same_log_level=false for ip in " new_log_level_name=${iplog_array[@levels[$new_log_level]}"; do # Retrieve current node log levelfi debug_msg "Retrieving currentCurrent node log level for IP $ip: $ip $current_log_level ($current_log_level_name)" debug_msgecho "curl" -s -u \"$(print_credentials "$credentials")\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel\"" echo -e "New node log currentlevel: ${BOLD_GREEN}$new_log_level=$(curl -s -u "$credentials" "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel" | $jq_or_grep -r '.value')_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})" echo debug_msg-e "Current node log level for IP $ip: ${BOLD_GREEN}$ip${RESET} is ${BOLD_GREEN}$current_log_level" original_log_levels["$ip"]=_name${RESET} (${BOLD_GREEN}$current_log_levellevel${RESET})." debug_msg "Original log# Skip update if new level formatches IPthe $ip: ${original_log_levels["$ip"]}"current level if [[ "$current_log_level" ==-eq "0$new_log_level" ]]; then currentsame_log_level_name="default=true echo "" else echo -e "Node log level for IP ${BOLD_GREEN}$ip${RESET} is already set currentto ${BOLD_GREEN}$new_log_level_name=name${RESET} (${log_levels[$currentBOLD_GREEN}$new_log_level]}level${RESET}). No changes made." fi continue original_log_level_names["$ip"]=$current_log_level_name else debug_msg "Original log level name: ${originalsame_log_level_names["$ip"]}"=false fi if [[ "$new$same_log_level" == "0"false ]]; then new_log_level_name="default" # Update the node log level using elsePUT new_log_level_name=${log_levels[debug_msg "Updating node log level to $new_log_level]}_name for IP $ip..." fi debug_msg "curl --user \"$(print_credentials "$credentials")\" debug_msg "Current log level for IP $ip: $current_log_level ($current_log_level_name)-sS -X PUT -H \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level\"" echo "" echo -e "New node log level: ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})" response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ echo -e "Current node log level for IP ${BOLD_GREEN}$ip${RESET} is ${BOLD_GREEN}$current "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET})."") # Skip update if new level matches the current leveldebug_msg "Response: $response" if [[ "$current$jq_logor_levelgrep" -eq== "$new_log_levelgrep" ]]; then same updated_log_level=true $(echo "$response" | grep -oP '"value":\s*\K[0-9]+') echo "" else echo -e "Node log level for IP ${BOLD_GREEN}$ip${RESET} is already set to ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET}). No changes made." updated_log_level=$(echo "$response" | "$jq_or_grep" -r '.value') continue elsefi same_log_level=false debug_msg "Updated log level for IP $ip: $updated_log_level" fi if [[ "$same$updated_log_level" == false-eq "$new_log_level" ]]; then # Update the node echo -e "${GREEN}$(timestamp)${RESET} Node log level using PUT for IP ${BOLD_GREEN}$ip${RESET} changed successfully from ${BOLD_GREEN}$current_log_level_name${RESET} (${GREEN}$current_log_level${RESET}) -> ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})." debug_msg "Updating node log level to $new_log_level_name for IP $ip..." else debug_msg "curlecho --usere \"$(print_credentials "$credentials")\" -sS -X PUT -H \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level\""{GREEN}$(timestamp)${RESET} Failed to update node log level for IP ${BOLD_GREEN}$ip${RESET}. Response: ${RED}$response${RESET}" response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ exit 1 fi "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level") fi done debug_msg "Response: $response" # Second loop: Countdown if duration is provided if [[ "$jq$same_orlog_greplevel" == "grep"false ]]; then\ if [[ -n "$duration" && "$duration" -gt updated_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')0 ]]; then echo -e "" else echo -e "Keeping node(s) log level at updated${YELLOW}$new_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')_name${RESET} (${YELLOW}$new_log_level${RESET}) for ${YELLOW}$duration${RESET} seconds (${YELLOW}$(format_duration $duration)${RESET}) ..." fiecho -e "" debug_msg "Updated log level for IP $ip: $updated_log_level"for ((i=duration; i>0; i--)); do if [[ "$updated_log_level" -eq "$new_log_level" ]]; then printf -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60)) echo -ene "Countdown: ${GREEN}$(timestamp)$YELLOW}$countdown${RESET} Node log level for IP ${BOLD_GREEN}$ip${RESET} changed successfully from ${BOLD_GREEN}$current_log_level_name${RESET} (${GREEN}$current_log_level${RESET}) -> ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})." remaining...\r" sleep 1 else done echo -e "${GREEN}$(timestamp)${RESET} Failed to update \n\nTime's up! Reverting node log level back forto IP ${BOLD_GREEN}$ip${RESET}. Response: ${RED}$response${RESET}original levels..." # Third loop: exitRevert 1the node log level fi for ip in "${ip_array[@]}"; do fi done current_log_level=${original_log_levels["$ip"]} # Second loop: Countdown if duration is provided if [[ "$same current_log_level_name=${original_log_level_names["$ip"]} == false ]]; then\ if [[ -n "$duration" && debug_msg "$duration"Reverting -gtnode 0log ]];level thenback to $current_log_level_name for IP $ip..." echo -e "" debug_msg "curl --user \"$(print_credentials "$credentials")\" -sS -X PUT echo -e "Keeping node(s) log level at ${YELLOW}$new_log_level_name${RESET} (${YELLOW}$new_log_level${RESET}) for ${YELLOW}$duration${RESET} seconds (${YELLOW}$(format_duration $duration)${RESET}) ...-H \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level\"" echo -e "" # echo -e "Node level log revert on $(timestamp) for IP $ip" for ((i=duration; i>0; i--)); do response=$(curl --user "$credentials" -sS printf-X PUT -v countdownH "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60))Content-Type: application/json" \ echo -ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r" http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level") sleep 1 debug_msg "Response: $response" done if [[ "$jq_or_grep" echo -e "\n\nTime's up! Reverting node log level back to original levels..."== "grep" ]]; then # Third loop: Revert the node reverted_log _level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+') for ip in "${ip_array[@]}"; do else current_log_level=${original_log_levels["$ip"]} currentreverted_log_level_name=${original_log_level_names["$ip"]} (echo "$response" | "$jq_or_grep" -r '.value') debug_msg "Reverting node log level back to $current_log_level_name for IP $ip..." fi debug_msg "curl --user \"$(print_credentials "$credentials")\" -sS -X PUT -H \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level\"" final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0) # echo -e "Node level log revert on size_diff=$((timestamp) forfinal_size IP $ip" - initial_size )) response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ debug_msg "Initial log file size: $initial_size" debug_msg "Final log file "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level")size: $final_size" debug_msg "Response: $response" size_diff_formatted=$(format_size "$size_diff") if [[ "$jq_or_grep" == "grep" ]]; then duration_formatted=$(format_duration "$duration") if reverted[[ "$reverted_log_level=$(echo "$response" | grep -oPeq '"value":\s*\K[0-9]+') $current_log_level" ]]; then else echo -e "${GREEN}$(timestamp)${RESET} Node log level for IP ${BOLD_GREEN}$ip${RESET} reverted successfully back revertedto ${BOLD_GREEN}${current_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')_name}${RESET} (${BOLD_GREEN}$current_log_level${RESET})." fielse final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0) size_diff=$(( final_size - initial_size )) debug_msg "Initial log file size: $initial_size"-e "${GREEN}$(timestamp)${RESET} Failed to revert node log level for IP ${BOLD_GREEN}$ip${RESET}. Response: ${RED}$response${RESET}" debug_msg "Finalexit log1 file size: $final_size" fi size_diff_formatted=$(format_size "$size_diff") done duration_formatted=$(format_duration "$duration") # Combine the log output for multiple IP addresses into a single summary if [[ "$reverted_log_level" -eq "$current_log_level" ]]; then if (( size_diff < 0 )); then echo -e "${GREEN}$(timestamp)${RESET} Node castor.log levelfile for IP ${BOLD_GREEN}$ip${RESET} reverted successfully back to ${BOLD_GREEN}${current_log_level_name}${RESET} (${BOLD_GREEN}$current_log_level${RESET})." was rotated." else else echo -e "" echo -e "Approximate ${BOLD_GREEN}$(timestamp)$$size_diff_formatted${RESET} Failednew logs towere revertgenerated nodeat log level for IP ${BOLD_GREEN}$ip$$new_log_level_name${RESET}. Response: (${RED}$response$BOLD_GREEN}$new_log_level${RESET}") for IP ${ip_array[*]}. Current castor.log size is exit 1 fi ${BOLD_GREEN}$(format_size "$final_size")${RESET} after ${YELLOW}$duration${RESET} seconds (${YELLOW}$duration_formatted${RESET})." done echo -e "" # Combine the log output for multiple IP addresses into a singlefi summary if (( size_diff < 0 )); thenelse echo -e "castor.log file was rotated." ${GREEN}$(timestamp)${RESET} Log level change is permanent until manually modified." else fi fi fi } echo -e# ""Run in detachable or directly if $detachable; then # Pass the main_script function to the screen session echoand -e "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generated at log level ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET}) for IP ${ip_array[*]}. Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after ${YELLOW}$duration${RESET} seconds (${YELLOW}$duration_formatted${RESET})." echo -e "" fi else echo -e "${GREEN}$(timestamp)${RESET} Log level change is permanent until manually modified." fi fi fi } # Run in detachable or directly if $detachable; then # Pass the main_script function to the screen session and store the output in a file debug_msg "**********************************************************store the output in a file debug_msg "**********************************************************" | tee -a "$output_log" debug_msg "Detach mode - Parameters passed to main_script:" | tee -a "$output_log" debug_msg " Swarm IP: $swarm_ip" | tee -a "$output_log" debug_msg " Credentials: $(print_credentials "$credentials")" | tee -a "$output_log" debug_msg " New Log Level: $new_log_level" | tee -a "$output_log" debug_msg " New log Level Name: $new_log_level_name" | tee -a "$output_log" debug_msg " Duration: $duration" | tee -a "$output_log" debug_msg " Log Level Type: $log_level_type" | tee -a "$output_log" debug_msg " Log File: $log_file" | tee -a "$output_log" debug_msg " Initial Log File Size: $initial_size" | tee -a "$output_log" debug_msg "Detach mode - ParametersCurrent passedLog to main_script:Level: $current_log_level" | tee -a "$output_log" debug_msg " SwarmCluster IPName: $swarm_ip$clusterName" | tee -a "$output_log" debug_msg " Credentialsjq_or_grep: $(print_credentials "$credentials")" $jq_or_grep" | tee -a "$output_log" debug_msg " New Log LevelDetach: $new_log_level$detachable" | tee -a "$output_log" debug_msg " Debug: $debug" | New log Level Name: $new_log_level_nametee -a "$output_log" debug_msg "**********************************************************" | tee -a "$output_log" debug_msg "# Convert associative arrays to Duration:strings $duration"and |pass teethem -a "$output_log" to the screen session debug_msg " Log Level Type: $log_level_type" | tee -a "$output_log"log_levels_string=$(declare -p log_levels) debug_msg " Log File: $log_file" | tee -a "$output_log"log_level_names_string=$(declare -p log_level_names) debug_msg " Initial Log File Size: $initial_sizelog_levels_string: $log_levels_string" | tee -a "$output_log" debug_msg " Current Log Level: $current_log_levellog_level_names_string: $log_level_names_string" | tee -a "$output_log" debug_msg " Cluster Name: $clusterName if command -v screen &>/dev/null; then echo -e "Running in ${YELLOW}screen${RESET} detachable mode..." | tee -a "$output_log" debug_msg " screen -dmS jqcastor_orlog_grep: $jq_or_grep" | tee -a "$output_log" script bash -c "$(declare -f main_script timestamp debug_msg " Detach: $detachable" | tee -a "$output_log" debug_msg " Debug: $debug" | tee -a "$output_log" debug_msg "**********************************************************" | tee -a "$output_log" # Convert associative arrays to strings and pass them toformat_size format_duration check_jq determine_log_file print_credentials); 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}\" \"${default_log_levels}\" \"${default_log_levels_name}\" | tee \"$output_log\"" screen -r castor_log_script # Wait for the screen session to complete and then log_levels_string=$(declare -p log_levels) log_level_names_string=$(declare -p log_level_names) debug_msg "log_levels_string: $log_levels_string" | tee -a "$output_log" debug_msg "log_level_names_string: $log_level_names_string" | tee -a "$output_log" ifdisplay the output log sleep 1 while screen -list | grep -q "castor_log_script"; do sleep 1 done elif command -v screentmux &>/dev/null; then echo -e "Running in ${YELLOW}screen$tmux${RESET} detachable mode..." | tee -a "$output_log"" > "$output_log" # Truncate log file to remove old entries # Create a temp script file temp_script="$SCRIPTDIR/castor_log_script.sh" # Write the script to a file to avoid printing function definitions cat screen<<EOF -dmS> castor"$temp_log_script" #!/bin/bash -c "$(declare -f main_script timestamp debug_msg format_size format_duration check_jq determine_log_file print_credentials); 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}\" \"${default_log_levels}\" \"${default_log_levels_name}\" | tee \-a "$output_log\" rm -f "$temp_script" screen -r castor_log_script # WaitRemove for the screenscript sessionfile totmux complete and then display the output logkill-session -t castor_log_script EOF # Ensure sleepthe 1script is executable while screen -list | grep -q "castor_logchmod +x "$temp_script"; do # Start tmux session sleepand 1execute the script inside done eliftmux commandnew-session -vd tmux &>/dev/null; then -s castor_log_script "bash $temp_script" echo -e "Running# in ${YELLOW}tmux${RESET} detachable mode..." | tee -a "$output_log"Attach session only if it's still running while tmux newhas-session -dt -s castor_log_script "$(declare -f main_script timestamp debug_msg format_size format_duration check_jq determine_log_file print_credentials); 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}\" \"${default_log_levels}\" \"${default_log_levels_name}\" | tee \"$output_log\"" 2>/dev/null; do tmux attach-session -t castor_log_script done # if tmux has-session -t castor_log_script 2>/dev/null; then # tmux attach-session -t castor_log_script # fi else echo "Error: Neither screen nor tmux available. Run without --detachable." exit 1 fi 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: $(print_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 |
...