...
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 togrep
jq
otherwise.Detachable Session Execution: Runs in a detachable session using
screen
ortmux
, allowing the script to continue running independently of the terminal session.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.
Requirements
jq (optional): Used for parsing JSON responses; falls back to
grep
if unavailable.screen or tmux (optional): Required for background execution.
Permissions: Ensure sufficient permissions to execute on the DataCore Swarm server and access required files.
...
Code Block |
---|
./castor-change-log-level.sh -d <node_ip> -p <admin:password> [-i <new new_log_level | -L new_node_log_level>level] [-t <duration_in_seconds>] [-D --detach] [-v] |
Parameter | Description | |||
---|---|---|---|---|
| IP address of the Swarm API endpoint (or set | |||
| Admin credentials in the format | |||
| New log level to set (values: | |||
| Duration in seconds to keep the new log level (optional). |
| Runs the script in a detachable session using | New node log level to set (values: |
| Duration in seconds to keep the new log level (optional). | |||
| Runs the script in a detachable session using |
Instruction for Use
Example 1: Set cluster log level to 20
and keep it for 10
...
minutes
Code Block |
---|
./castor-change-log-level.sh -d 192.168.8.84 -p admin:datacore -i 20 -t 10600 |
Example 2: Set node(s) log level to debug (10) and keep it for 10 secondsminutes
Code Block |
---|
./castor-change-log-level.sh -d 192.168.8.84,192.168.8.86 -p admin:datacore -i debug -t 10600 |
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 --detach -vD |
Running the Script at a Specific Time
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: Schedule the script to run at 3:00 AM and collect debug logs for 1 hour
Ensure the at service is installed, enabled and running
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.)
Example 5: Schedule the script to run at 3:00 AM on 03/02/2025 and collect debug logs for 1 hour
Ensure the at service is installed, enabled and running
Code Block 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 10 -t 3600" | at 03:00 AM 203/1602/2025
This schedules the script to run at 3:00 AM on Feb 16 2025 and collects log at debug level for 1 hour.
To verify scheduled job:
Code Block atq 7 Sun FebMar 1602 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
...
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.Debug Mode: When
-v
is specified, debug messages display the script's internal operations.
Output Messages
Message | Description | ||
---|---|---|---|
| Displays the specified Swarm IP address. |
| Credentials are masked for security. |
| 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 |
---|
[root@scs dist]# ./castor-change-log-level.sh -p admin:datacore -i 10 -t 300 Swarm IP: 192.168.1.84 Credentials: [hidden for security] Cluster Name: gatewayadmindomainmsuen-scs1.suen.work New log level: 10debug Current log level is 30. Updating log level to 10...default. 2025-03-02T05:15:49.901Z Log level changed successfully from 30 → 10. Keeping log level at 10debug for 300 second (s00:05:00) ... Countdown: 00:00:01 remaining... Time's up! Reverting log level back to 30... Approximate 6988.4MB9MB new logs were generated at log level 10. Current castor.log size is 371.3MB after 300 seconds (00:05:00). 2025-03-02T05:20:50.483Z Log level reverted successfully back to 30. [root@scs dist]# |
...
Missing Parameters: Missing parameters prompt a usage message.
Invalid Log Levels: If an unsupported log level is specified, the script will prompt the user to enter a valid value.
Invalid Duration: If a non-numeric duration is provided, you’re prompted to enter a valid duration in seconds.
Connection Issues: If unable to connect to the Swarm API, check the IP, credentials, and network access.
...
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---------------------------------------------------------------------------------------------------------------------- # Current Version: 1.2.3 # -------------------------------------------------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 Minor 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 Minor bug fix and enhancement. # v1.3.3 - 2025-03-02 Fixed tmux command to long issue. # ---------------------------------------------------------------------------- # KB: https://perifery.atlassian.net/wiki/spaces/public/pages/3872161835/Setting+and+Managing+Swarm+Log+Levels+with+script # ------------------------------------------------- # Current Version: 1.3.3 # --------------------------------------------------------------------------------------------- # 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: ./$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 " ------------------------------------ # 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 -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 (Optional) Duration in seconds to keep the new log level (must be greater than 0)" echo -e " -D, --detach (Optional) Detach the script from 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" # Default log file location SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) JQLOCATION=$SCRIPTDIR/jq MAX_RETRIES=3 # Maximum number of password retries attempts username="admin" # Default username password="" # 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" } # 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 local caller_line=${BASH_LINENO[0]:-unknown} # Fallback to "unknown" if empty echo -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." echo -e " Please install either '${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 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() { for jq_path in "/usr/local/bin/jq" "$(pwd)/jq"; do [[ -x "$jq_path" ]] && echo "$jq_path" && return done 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 if [[ -f "$log" ]]; then echo "$log" return fi done 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 for security]" debug_msg "Swarm IP: $SWARM_IP" # API endpoint for validating user credentials local VALIDATE_URL="http://${SWARM_IP}:91/api/validateUser" debug_msg "Validate URL: $VALIDATE_URL" # Make the API request debug_msg "Validating user credentials..." debug_msg "curl -s -u \"********\" -X GET \"$VALIDATE_URL\" -H 'Content-Type: application/json'" 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 debug_msg "Authentication successful for user '${CREDENTIALS%%:*}'" return 0 # Success elif echo "$RESPONSE" | "$jq_or_grep" -e '.isValid == false' > /dev/null 2>&1; then debug_msg "Authentication failed for user '${CREDENTIALS%%:*}'" return 1 # Failure else debug_msg "Error: Unable to validate credentials. Please check your inputs." return 1 # Failure fi } # Function to print credentials - hide password print_credentials() { local CREDENTIALS="$1" local USERNAME="${CREDENTIALS%%:*}" echo -e "${GREEN}$USERNAME${RESET}":"${GREEN}********${RESET}" } # Parse input arguments while [[ "$#" -gt 0 ]]; do case $1 in -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 [[ -n "$new_log_level" ]]; then echo "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together." usage fi log_level_type="cluster" if [[ ${log_level_names[$2]} ]]; then new_log_level=${log_level_names[$2]} elif [[ "$2" == "default" ]]; then new_log_level=${default_log_levels[$log_level_type]} new_log_level_name="default" elif [[ ${log_levels[$2]} ]]; then new_log_level=$2 else echo "Invalid log level: $2" exit 1 fi new_log_level_name=${log_levels[$new_log_level]} default_log_level=30 debug_msg "Set new_log_level to $new_log_level ($new_log_level_name)" shift 2 ;; -L|--node.log.level) if [[ -n "$new_log_level" ]]; then echo "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together." usage fi log_level_type="node" if [[ ${log_level_names[$2]} ]]; then new_log_level=${log_level_names[$2]} elif [[ "$2" == "default" ]]; then new_log_level=${default_log_levels[$log_level_type]} new_log_level_name="default" elif [[ ${log_levels[$2]} || $2 -eq 0 ]]; then new_log_level=$2 new_log_level_name="default" else If multiple IPs are provided, the script will updateecho the"Invalid log level for all nodes.": $2" echo " exit 1 fi (Alternatively, set the SCSP_HOST environment variable to the Swarm IP.)" new_log_level_name=${log_levels[$new_log_level]} echo " -p, --credentialsdefault_log_level=0 Credentials in the format admin:password" echo " -i, --log.leveldebug_msg "Set new_log_level to $new_log_level ($new_log_level_name)" shift 2 New cluster log level to set (5, 10, 15, 20, 30,;; 40, 50, chatter, debug, announce, info, error, critical, default)" -t|--time) echo " -L, --node.log.level if New[[ node log level to set (0, 5, 10, 15, 20, 30, 40, 50, chatter, debug, announce, info, error, critical, default)"-n "$2" && "$2" != -* && "$2" -gt 0 ]]; then echo " duration="$2" debug_msg "Set duration to $duration" **Either -i or -L must be specified, but not both.**" echoshift "2 -t, --time else (Optional) Duration in seconds to keep the new log level (must be greaterecho than-e 0)"" echo " -D, --detach echo -e (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="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" # 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 get the current timestamp timestamp() { date +"%Y-%m-%d_%H:%M:%S.%4N" } # Function to display debug messages if debug mode is enabled debug_msg() { if $debug; then"---------------------------------------------------------------------------------------" echo -e " ${RED}Error${RESET}: Duration must be a number greater than ${BOLD_GREEN}0${RESET}." echo -e " Please specify the duration using the ${BOLD_GREEN}-t${RESET} ${GREEN}<seconds>${RESET} or ${BOLD_GREEN}--time${RESET} ${GREEN}<seconds>${RESET} option." echo -e "---------------------------------------------------------------------------------------" usage fi ;; -D|--detach) detachable=true; debug_msg "Set detachable to true"; shift ;; --debug) debug=true; debug_msg "Set debug to true with ${YELLOW}--debug${RESET}"; shift ;; *) 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 [[ -z "$duration" ]]; then echo -e "" echo local caller_line=${BASH_LINENO[0]:-unknown} # Fallback to "unknown" if empty-e "---------------------------------------------------------------------------------------" echo -e "$(timestamp) [DEBUG] (Line $caller_line) $*" fi } # Function to check if either 'screen' or 'tmux' is installed check_screen_or_tmux() { ${RED}Error${RESET}: Duration must be specified when running in detachable mode." if ! commandecho -v screen &>/dev/null && ! command -v tmux &>/dev/null; then echo "Error: Neither 'screen' nor 'tmux' is installed. Cannot run in detachable modee " Please specify the duration using the ${BOLD_GREEN}-t${RESET} or ${BOLD_GREEN}--time${RESET} option." detachable=false # Disable detachable sessionecho -e "---------------------------------------------------------------------------------------" usage debug_msg "detachable=${YELLOW}false${RESET}"fi check_screen_or_tmux fi } # Function to format file size format_size() { local size=$1 if (( size >= 1073741824 ))If swarm_ip is not provided, try using SCSP_HOST environment variable if [[ -z "$swarm_ip" ]]; then if [[ -n echo "$(awk "BEGIN {printf \"%.1fGB\", $size/1073741824}")""$SCSP_HOST" ]]; then elif (( size >= 1048576 )); then swarm_ip="$SCSP_HOST" echo "$(awk "BEGIN {printf \"%.1fMB\", $size/1048576}")" elif (( size >= 1024 )); then debug_msg "Using Swarm IP from SCSP_HOST: $swarm_ip" else echo "$(awk "BEGIN {printf \"%.1fKB\", $size/1024}")" elseError: swarm_ip not provided and SCSP_HOST is not set." echo "${size}B"usage fi }fi # FunctionCheck toif formatrequired 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() { 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 [[ "$credentials" =~ ^[^:]+$ ]]; then if [[ -xn "/usr/local/bin/jq$credentials" ]]; then CREDENTIALS="$credentials" echo "/usr/local/bin/jq" ATTEMPT=$MAX_RETRIES elif [[ -x "$(pwd)/jq" ]]; thenUSERNAME="" PASSWORD="" echodebug_msg "CREDENTIALS: $(pwd)/jqprint_credentials "$CREDENTIALS")" elif command -v jq &>/dev/null; thendebug_msg "ATTEMPT: $ATTEMPT" # Check if credentials contain a echo "jq" colon (username:password format) else debug_msg "Checking for colon echo "grep" in credentials..." fi } if [[ -f "$JQLOCATION"[ "$CREDENTIALS" == *":"* ]]; then jq_or_grep=$JQLOCATION else debug_msg "Credentials contain colon" 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() {USERNAME="${CREDENTIALS%%:*}" PASSWORD="${CREDENTIALS#*:}" if [[ -f "/var/log/datacore/castor.log" ]]; then# Validate credentials echo "/var/log/datacore/castor.logcheck_credentials "$CREDENTIALS" "${ip_array[0]}" elif if [[ $? -f "/var/log/caringo/castor.log"ne 0 ]]; then echo "/var/log/caringo/castor.log" debug_msg "Invalid elsecredentials. Please check your username and password." echo "Error: Log file not found in /var/log/datacore/castor.log or /var/log/caringo/castor.log" echo -e "" exit 1 fi }echo log_file=$(determine_log_file) # Function to check if credentials are valid check_credentials() { local CREDENTIALS="$1"-e "${RED}Error${RESET}: Invalid credentials. Please check your username and password." local SWARM_IP="$2" echo debug_msg-e ""Credentials: $CREDENTIALS" debug_msg "Swarm IP: $SWARM_IP" exit 1 # API endpoint for validating user credentials fi local VALIDATE_URL="http://${SWARM_IP}:91/api/validateUser" debug_msg "Validate #credentials" Make the API request RESPONSEcredentials=$(curl$CREDENTIALS -s -u "$CREDENTIALS" -X GETelse "$VALIDATE_URL" -H 'Content-Type: application/json') debug_msg "ValidateCredentials Userdo Response: $RESPONSE" # Check if the response contains "isValid": truenot contain password" if echodebug_msg "$RESPONSE" | "$jq_or_grep" -e '.isValid == true' > /dev/null 2>&1; then Username: $(print_credentials "$CREDENTIALS")" USERNAME="$CREDENTIALS" return 0 # SuccessPrompt for password elif echo "$RESPONSE" | "$jq_or_grep" -e '.isValid == false' > /dev/null 2>&1; thenwhile [[ $ATTEMPTS -lt $MAX_RETRIES ]]; do debug_msg "Authentication failed for user '${CREDENTIALS%%:*}'" echo "" return 1 # Failure read -sp "Enter elsepassword for user $USERNAME: " PASSWORD debug_msg "Error: Unable to validate credentials. Please check your inputs." echo "" return 1 echo #"" Failure fi } # Parse input arguments whileif [[ -z "$#$PASSWORD" -gt 0 ]]; then do case $1 in -d|--swarm_ip) swarm_ip="$2"; debug_msg "Set swarm_ip to $swarm_ip"; shift 2 ;;echo -e "${RED}Error${RESET}: Password cannot be empty." -p|--credentials) ((ATTEMPTS++)) credentials="$2" ifcontinue ! [[ "$credentials" =~ ^[^:]+:[^:]+$ ]]; then fi echo -e CREDENTIALS="$USERNAME:$PASSWORD" debug_msg "CREDENTIALS: $(print_credentials "$CREDENTIALS"))" echo -e "Error: Credentials must be in the format check_credentials "$CREDENTIALS" "${BOLD_GREEN}username:password${RESETip_array[0]}" if [[ $? -eq exit0 1]]; then fi debug_msg "CREDENTIALS: $(print_credentials "$CREDENTIALS"))" if [[ "$credentials" =~ [^a-zA-Z0-9:] ]]; then debug_msg "Valid credentials" echo -e "" credentials=$CREDENTIALS echo -e "Warning: Password contains special characters. Please enclosebreak the credentials with single quotes (${BOLD_YELLOW}'${BOLD_GREEN}username:password${RESET}${BOLD_YELLOW}'${RESET})." else exit 1 echo -e "" fi echo debug_msg "Set credentials"; shift 2 ;;-e "${RED}Error${RESET}: Invalid credentials. Please check your username and password." -i|--log.level) fi if [[ -n "$new_log_level" ]]; then ((ATTEMPTS++)) done echo "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together." if [[ $ATTEMPTS -ge $MAX_RETRIES ]]; then echo -e "" usage echo -e "${RED}Error${RESET}: Maximum number of password attempts reached. Exiting script." fi exit 1 if [[ ${log_level_names[$2]} ]]; then fi fi else if ! new_log_level=${log_level_names[$2]}check_credentials "$credentials" "${ip_array[0]}"; then echo -e "" elif [[ ${log_levels[$2]} ]]; then echo -e "${YELLOW}Warning${RESET}: ${RED}Invalid credentials${RESET}." new_log_level=$2 echo -e "" exit 1 else fi fi # Retrieve cluster name and handle JSON parsing using the first echoIP "Invalid log level: $2" exit 1 fi new_log_level_name=${log_levels[$new_log_level]} log_level_type="cluster" default_log_level=30 debug_msg "Set new_log_level to $new_log_level ($new_log_level_name)" shift 2 ;; -L|--node.log.level) if [[ -n "$new_log_level" ]]; then echo "Error: Options -i (cluster log leve) and -L (node log level) cannot be used together." usage fi 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 [[ ${log_level_names[$2]}"$detachable" ]]; then new_log_level=${log_level_names[$2]}local debug="${10}" eval elif [[ ${log_levels[$2]} || $2 -eq 0 ]]; then "$(echo "${11}" | sed 's/declare -A/declare -A/')" eval "$(echo "${12}" | sed 's/declare new_log_level=$2-A/declare -A/')" fi local default_log_levels="$13" local newdefault_log_levellevels_name="default$14" debug_msg "**********************************************************" else debug_msg "local variables" echodebug_msg "InvalidLog logLevel levelType: $2${GREEN}$log_level_type${RESET}" debug_msg "Default Log Level: ${GREEN}$default_log_level${RESET}" debug_msg "Swarm exit 1IP: $swarm_ip" debug_msg "Credentials: $(print_credentials "$credentials")" debug_msg fi"New Log Level: $new_log_level" debug_msg "New log Level Name: new$new_log_level_name=${log_levels[$new_log_level]}" debug_msg "Duration: $duration" debug_msg "Log File: $log_file" log_level_type="node" debug_msg "Initial Log File Size: $initial_size" debug_msg "Current Log Level: default$current_log_level=0" debug_msg "Cluster Name: $clusterName" debug_msg "Set newjq_logor_level to $new_log_level ($new_log_level_name)grep: $jq_or_grep" debug_msg "Detach: $detachable" shift 2debug_msg "Debug: $debug" debug_msg "**********************************************************" ;; # Split the swarm_ip into an -t|--time) array of IP addresses IFS=';, ' read -r -a ip_array if [[ -n "$2" && "$2" != -* && "$2" -gt 0 ]]; then<<< "$swarm_ip" debug_msg "IP Array: ${ip_array[*]}" # Display initial information if [[ "$log_level_type" == duration="$2cluster" ]]; then echo -e "Swarm debug_msg "Set duration to $duration" IP: ${GREEN}${ip_array[0]}${RESET}" else echo -e shift"Swarm 2 IPs: ${GREEN}${ip_array[*]}${RESET}" fi # echo else-e "Swarm IP: ${GREEN}$swarm_ip${RESET}" debug_msg "Credentials: ${GREEN}[hidden for security]${RESET}" echo "Error: Duration must be a number greater than 0." -e "Cluster Name: ${GREEN}$clusterName${RESET}" debug_msg "Starting main_script function..." debug_msg "Log level type: $log_level_type" exit# 1Store the original log levels declare -A original_log_levels fi 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]} -D|--detach) detachable=true; debug_msg "SetDefault detachablelog to true"; shift ;; --debug) debug=true;level: $default_log_level" debug_msg "SetDefault debuglog tolevel true with ${YELLOW}--debug${RESET}"; shift ;;name: $default_log_level_name" if [[ $log_level_type == *) usage ;;"cluster" ]]; then esac done debug_msg "SetSetting cluster log_ level_type to ${YELLOW}$log$new_log_level ($new_log_level_type${RESET}name)" # Set default values if not provided if# [[Retrieve -zcurrent "$new_log_ level" ]]; then if [[ "$log$jq_levelor_typegrep" == "clustergrep" ]]; then new current_log_level=$default_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]+') else else new_log_level=0 fi newcurrent_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=$(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 [[ -n "$SCSP_HOST" $current_log_level == 30 ]]; then swarm_ipcurrent_log_level_name="$SCSP_HOSTdefault" debugfi "Using Swarm IP from SCSP_HOST: $swarm_ip" if else [[ $new_log_level == 30 ]]; then echo "Error: swarm_ip not provided and SCSP_HOST is not set." new_log_level_name="default" usage fi fi # Check if required arguments are provided if [[ -z "$credentials" || -z "$new_log_level" ]]; then debug_msg "Current cluster log level: ${BOLD_GREEN}$current_log_level${RESET}" usage fidebug_msg "Current #log Splitlevel 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]}"; thenname: ${BOLD_GREEN}$current_log_level_name${RESET}" echo -e "" echo -e "New cluster log level: ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})" echo -e ""Current cluster log level is echo -e "${YELLOW}Warning$BOLD_GREEN}$current_log_level_name${RESET}: (${RED}Invalid credentials$BOLD_GREEN}$current_log_level${RESET}). Please check your username and password in the format ${GREEN}username:password${RESET}." echo -e "" usage" # Skip update if new level matches the current level if [[ "$current_log_level" -eq "$new_log_level" ]]; then exit 1 fi #echo Retrieve"" cluster name and handle JSON parsing using the first IP address debug_msg "Retrievingecho the-e cluster"Cluster namelog fromlevel Swarmis APIalready usingset IP:to ${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 fiBOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET}). No changes made." return fi # Update the cluster log level using PUT debug_msg "Updating cluster log level to $new_log_level_name" debug_msg "Cluster Name: $clusterName" # Main logic function to run the script tasks main_script() {debug_msg "Credentials: $(print_credentials "$credentials")" local swarm_ip="$1" debug_msg "curl local credentials="$2" local new_log_level="$3" local new_log_level_name="$4" local duration="$5" local log_level_type="$6--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}\"" #local log_file="/var/log/datacore/castor.log" if [[ -z "$log_file" ]]; then log_file=$(determine_log_file)response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ fi local initial_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0) local current_log_level"http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \ -d "{\"value\": $new_log_level}") local clusterName="$7" debug_msg local jq_or_grep="$8"Response: $response" local detachable="$9" if [[ "$jq_or_grep" == "$detachablegrep" ]]; then local debug="${10}" eval "updated_log_level=$(echo "${11}$response" | sed 's/declaregrep -A/declare -A/')" eval "$(echo "${12}" | sed 's/declare -A/declare -A/')"oP '"value":\s*\K[0-9]+') fi else debug_msg "**********************************************************" debug_msg "local variables" debug_msg "Swarm IP: $swarm_ip" debug_msg "Credentials: $credentials"updated_log_level=$(echo "$response" | "$jq_or_grep" -r '.value') fi debug_msg "New Log Level: $new_log_level" debug_msg "NewUpdated cluster log Level Namelevel: $new$updated_log_level_name" debug_msg "Duration: $duration" if debug_msg "Log Level Type: $log_level_type" debug_msg "Log File: $log_file"[[ "$updated_log_level" -eq "$new_log_level" ]]; then debug_msg "Initial Logecho File Size: $initial_size" debug_msg "Current Log Level: $current_log_level-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})." debug_msg "Cluster Name: $clusterName" else debug_msg "jq_or_grep: $jq_or_grep" debug_msg "Detach: $detachable"echo 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-e "${GREEN}$(timestamp)${RESET} Failed to update log level. Response: ${RED}$response${RESET}" exit 1 fi # Countdown and revert log level if [[ -n "$log_level_type$duration" ==&& "cluster$duration" -gt 0 ]]; then echo -e "Keeping log level at echo -e "Swarm IP: ${GREEN}${ip_array[0]}${RESET}${YELLOW}$new_log_level_name${RESET} (${YELLOW}$new_log_level${RESET}) for ${YELLOW}$duration${RESET} seconds (${YELLOW}$(format_duration $duration)${RESET}) ..." else echo -e "Swarm" IPs: ${GREEN}${ip_array[*]}${RESET}" fi # echo -e "Swarm IP: ${GREEN}$swarm_ip${RESET}"for ((i=duration; i>0; i--)); do echo -e "Credentials: ${GREEN}[hidden for security]${RESET}" echoprintf -ev countdown "Cluster Name: ${GREEN}$clusterName${RESET}" debug_msg "Starting main_script function..."%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60)) debug_msg "Log level type: $log_level_type" echo # Store the original log levels-ne "Countdown: ${YELLOW}$countdown${RESET} remaining...\r" declare -A original_log_levels declare -A original_log_level_names sleep 1 if [[ $log_level_type == "cluster" ]]; then done debug_msg "Setting cluster log level to $new_log_level ($new_log_level_name)" # Retrieve current log level echo -e "\n\nTime's up! Reverting log level back to ${GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET}) ..." if [[ "$jq_or_grep" == "grep" ]]; then # Revert the log level back to the original value 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]+') # echo -e "Level log revert on else$(timestamp)" current_log_levelresponse=$(curl --user "$credentials" -sS "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" | "$jq_or_grep" -r '.value') -X PUT -H "Content-Type: application/json" \ fi current_log_level_name="http://${logip_levels[$current_log_level]} debug_msg "Current cluster log level: ${BOLD_GREEN}$current_log_level${RESET}" array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \ debug_msg "Current log level name: ${BOLD_GREEN} -d "{\"value\": $current_log_level_name${RESET}") echo -edebug_msg "Response: $response" echo -e "New cluster logif level: ${BOLD_GREEN}$new_log_level_name${RESET}"[[ "$jq_or_grep" == "grep" ]]; then echo -e "Current cluster log level is ${GREEN}$currentreverted_log_level_name.${RESET}"=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+') # Skip update ifelse new level matches the current level if [[ "$currentreverted_log_level=$(echo "$response" -eq| "$new$jq_logor_levelgrep" ]]; then-r '.value') echofi "" debug_msg echo -e "Cluster"Reverted cluster log level is already set to ${BOLD_GREEN}$new_log_level_name${RESET}. No changes made.: $reverted_log_level" return final_size=$(stat -c%s "$log_file" 2>/dev/null fi # Update the cluster log level using PUT|| echo 0) debug_msg "UpdatingInitial cluster log levelfile tosize: $new_log_level_name"$initial_size" debug_msg "Cluster NameFinal log file size: $clusterName$final_size" response size_diff=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \( final_size - initial_size )) size_diff_formatted=$(format_size "$size_diff") "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \ duration_formatted=$(format_duration "$duration") if (( size_diff < 0 )); then echo -de "{\"value\": $new_log_level}") castor.log file was rotated." else debug_msg "Response: $response" ifecho [[-e "$jq_or_grep" == "grep" ]]; then echo -e updated_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+') else updated_log_level=$(echo "$response" | "$jq_or_grep" -r '.value') fi"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 debug_msg-e ""Updated cluster log level: $updated_log_level" fi if [[ "$updated$reverted_log_level" -eq "$new$current_log_level" ]]; then echo -e "${GREEN}$(timestamp)${RESET} Log level changedreverted successfully fromback to ${BOLD_GREEN}$current_log_level_level$name${RESET} → (${BOLD_GREEN}$new$current_log_level${RESET})." echo -e "" else echo -e "${GREEN}$(timestamp)${RESET} Failed to updaterevert log level. Response: ${RED}$response${RESET}" echo -e "" exit 1 fi # Countdown and revert log levelelse echo -e "${GREEN}$(timestamp)${RESET} Log level change is permanent until manually modified." fi ifelif [[ -n "$duration" && "$duration" -gt 0 "$log_level_type" == "node" ]]; then echo# -e "Keeping log level at ${YELLOW}$new_log_level_name${RESET} for ${YELLOW}$duration${RESET} second(s)..."First loop: Change the node log level local same_log_level=false echo -e "" for ip in "${ip_array[@]}"; do for ((i=duration; i>0; i--)); do # Retrieve current node log level printf -v countdowndebug_msg "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60)) Retrieving current node log level for IP: $ip" debug_msg "curl echo-s -neu "Countdown: ${YELLOW}$countdown${RESET} remaining...\r\"$(print_credentials "$credentials")\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel\"" current_log_level=$(curl -s -u sleep 1 "$credentials" "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel" | $jq_or_grep -r '.value') done debug_msg "Current log level for IP $ip: $current_log_level" echo -e "\n\nTime's up! Reverting log level back to ${GREEN} original_log_levels["$ip"]=$current_log_level${RESET}..."level debug_msg # Revert the "Original log level backfor toIP the$ip: ${original value_log_levels["$ip"]}" if [[ response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \"$current_log_level" == "0" ]]; then current_log_level_name="default" "http://${ip_array[0]}:91/api/storage/clusters/$clusterName/settings/log.level" \ else -d "{\"value\": current_log_level_name=${log_levels[$current_log_level]}") debug_msg "Response: $response"fi if [[ "$jq_or_grep" == "grep" ]]; thenoriginal_log_level_names["$ip"]=$current_log_level_name debug_msg "Original log level name: reverted${original_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+')_names["$ip"]}" if [[ "$new_log_level" == "0" ]]; then else revertednew_log_level_name=$(echo "$responsedefault" | "$jq_or_grep" -r '.value') else fi debug_msg "Reverted cluster log level: $revertednew_log_level_name=${log_levels[$new_log_level"]} fi final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0) debug_msg "InitialCurrent log level for fileIP size$ip: $initial_size" debug_msg "Final log file size: $final_size"$current_log_level ($current_log_level_name)" echo "" size_diff=$(( final_size - initial_size )) echo -e "New node log size_diff_formatted=$(format_size "$size_diff")level: ${BOLD_GREEN}$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})" duration_formatted=$(format_duration "$duration") if (( size_diff < 0 )); then echo -e "castor.log file was rotated." else echo -e "Current node log level for IP ${BOLD_GREEN}$ip${RESET} is ${BOLD_GREEN}$current_log_level_name${RESET} (${BOLD_GREEN}$current_log_level${RESET})." # Skip update if new level matches the current level echo -e "Approximate ${BOLD_GREEN}$size_diff_formatted${RESET} new logs were generatedif at[[ "$current_log_level" level ${BOLD_GREEN}-eq "$new_log_level${RESET}. Current castor.log size is ${BOLD_GREEN}$(format_size "$final_size")${RESET} after $duration_formatted." filevel" ]]; then same_log_level=true if [[ "$reverted_log_level" -eq "$current_log_level" ]]; then echo "" echo -e "LogNode log level reverted successfully backfor IP ${BOLD_GREEN}$ip${RESET} is already set to ${BOLD_GREEN}$current$new_log_level_name${RESET} (${BOLD_GREEN}$new_log_level${RESET})." No changes made." else continue echo -e "Failed toelse revert log level. Response: ${RED}$response${RESET}" exit 1same_log_level=false fi else if [[ "$same_log_level" == false echo "Log level change is permanent until manually modified."]]; then fi # Update the elifnode [[log "$log_level_type" == "node" ]]; thenlevel using PUT # First loop: Change thedebug_msg "Updating node log level to $new_log_level_name for IP $ip..." for ip in "${ip_array[@]}"; do debug_msg "curl --user # Retrieve current node log level\"$(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\"" debug_msg "Retrieving current node log level for IP: $ip" response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ debug_msg "curl -s -u \"$credentials\" \ "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel\"?value=$new_log_level") debug_msg "Response: $response" current_log_level=$(curl -s -u "$credentials" "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel" | $jq_or_grep -r '.value') if [[ "$jq_or_grep" == "grep" ]]; then debug_msg "Current log level for IP $ip: $currentupdated_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+') original_log_levels["$ip"]=$current_log_level else debug_msg "Original log level for IP $ip: ${originalupdated_log_levels["$ip"]}" level=$(echo "$response" | "$jq_or_grep" -r '.value') if [[ "$current_log_level" == "0" ]]; then fi current_log_level_name="default" debug_msg "Updated log level for IP $ip: $updated_log_level" else if [[ current"$updated_log_level_name=${log_levels[$current" -eq "$new_log_level" ]}]; then fi echo -e "${GREEN}$(timestamp)${RESET} Node original_log_level_names["$ip"]=$current_log_level_name debug_msg "Original log level name: ${original_log_level_names["$ip"]}" if [[ "$new_log_level" == "0" ]]; thenlog 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})." else new_log_level_name="default" else echo -e "${GREEN}$(timestamp)${RESET} Failed to update node log level for IP ${BOLD_GREEN}$ip${RESET}. new_log_level_name=${log_levels[$new_log_level]}Response: ${RED}$response${RESET}" fi exit 1 debug_msg "Current log level for IP $ip: $current_log_level ($current_log_level_name)" fi echo "" fi echodone -e "New node log level: ${BOLD_GREEN}$new_log_level_name${RESET}" # Second loop: Countdown if duration is provided echo -e "Current node log level for IPif $ip is ${GREEN}$current[[ "$same_log_level_name.${RESET}" == false ]]; then\ # Skip update if new[[ level matches the current level if [[ "$current_log_level" -eq "$new_log_level"-n "$duration" && "$duration" -gt 0 ]]; then echo -e "" echo -e "NodeKeeping node(s) log level for IP $ip is already set to ${BOLD_GREENat ${YELLOW}$new_log_level_name${RESET}. No changes made." continue fi # Update the node log level using PUT (${YELLOW}$new_log_level${RESET}) for ${YELLOW}$duration${RESET} seconds (${YELLOW}$(format_duration $duration)${RESET}) ..." echo debug_msg-e "Updating" node log level to $new_log_level_name for IP $ip..." for ((i=duration; i>0; i--)); do 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\"" printf -v countdown "%02d:%02d:%02d" response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \$((i/3600)) $(( (i%3600) / 60 )) $((i%60)) echo -ne "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$new_log_level")Countdown: ${YELLOW}$countdown${RESET} remaining...\r" 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]+') \n\nTime's up! Reverting node log level back to original levels..." else # Third loop: Revert the updated_node log_level=$(echo "$response" | "$jq_or_grep" -r '.value') level for ip in "${ip_array[@]}"; do fi debug_msg "Updated log level for IP $ip: $updatedcurrent_log_level=${original_log_levels["$ip"]} if [[ "$updatedcurrent_log_level" -eq "$new_name=${original_log_level_names["$ip" ]]; then ]} echo -e "Node log level for IP $ip changed successfully from ${GREEN}$current_log_level_name${RESET} → ${BOLD_GREEN}$new_debug_msg "Reverting node log level back to $current_log_level_name${RESET}." name for IP $ip..." else debug_msg echo -e "Failed to update node log level for IP $ip. Response: ${RED}$response${RESET}"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\"" exit 1 # echo -e "Node level log revert on $(timestamp) for fiIP $ip" done # Second loop: Countdown if duration is provided if [[ -n "$duration" && "$duration" -gt 0 ]]; then response=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \ echo -e "" echo -e "Keeping node(s) log level at ${YELLOW}$new_log_level_name${RESET} for ${YELLOW}$duration${RESET} second(s)..." "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level") echo -e "" debug_msg "Response: $response" for ((i=duration; i>0; i--)); do if [[ "$jq_or_grep" == "grep" ]]; then printf -v countdown "%02d:%02d:%02d" $((i/3600)) $(( (i%3600) / 60 )) $((i%60)) reverted_log_level=$(echo "$response" | echogrep -neoP '"value"Countdown: ${YELLOW}$countdown${RESET} remaining...\r":\s*\K[0-9]+') sleep 1 else done reverted_log_level=$(echo "$response" -e "\n\nTime's up! Reverting node log level back to original levels..."| "$jq_or_grep" -r '.value') fi # Third loop: Revert the node log level for ip in "${ip_array[@]}"; dofinal_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0) currentsize_log_leveldiff=${original_log_levels["$ip"]}(( final_size - initial_size )) currentdebug_msg "Initial log_level_name=${original_log_level_names["$ip"]} file size: $initial_size" debug_msg "Reverting nodeFinal log levelfile back to $current_log_level_name for IP $ip...size: $final_size" debug_msg "curl --user \"$credentials\" -sS -X PUT -H \"Content-Type: application/json\" \"http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value=$current_log_level\"" size_diff_formatted=$(format_size "$size_diff") response duration_formatted=$(curl --user "$credentials" -sS -X PUT -H "Content-Type: application/json" \format_duration "$duration") if [[ "http://$ip:91/api/storage/nodes/_self/settings/log.nodeLogLevel?value="$reverted_log_level" -eq "$current_log_level") ]]; then debug_msg "Response: $response" echo -e "${GREEN}$(timestamp)${RESET} Node log level for if [[ "$jq_or_grep" == "grep" ]]; then IP ${BOLD_GREEN}$ip${RESET} reverted successfully back to ${BOLD_GREEN}${current_log_level_name}${RESET} (${BOLD_GREEN}$current_log_level${RESET})." reverted_log_level=$(echo "$response" | grep -oP '"value":\s*\K[0-9]+') else else echo -e "${GREEN}$(timestamp)${RESET} Failed to revert node log level for IP ${BOLD_GREEN}$ip${RESET}. reverted_log_level=$(echo "$response" | "$jq_or_grep" -r '.value')Response: ${RED}$response${RESET}" fi exit 1 final_size=$(stat -c%s "$log_file" 2>/dev/null || echo 0) fi size_diff=$(( final_size - initial_size )) done # debug_msg "InitialCombine the log fileoutput size: $initial_size" for multiple IP addresses into a single summary debug_msg "Final logif file(( size: $final_size" _diff < 0 )); then size_diff_formatted=$(format_size "$size_diff") echo -e "castor.log file was rotated." duration_formatted=$(format_duration "$duration")else if [[ "$reverted_log_level" echo -eqe "$current_log_level" ]]; then echo -e "NodeApproximate ${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 reverted successfully back to${ip_array[*]}. Current castor.log size is ${BOLD_GREEN}${current_log_level_name}${RESET}." (format_size "$final_size")${RESET} after ${YELLOW}$duration${RESET} seconds (${YELLOW}$duration_formatted${RESET})." else echo -e "Failed" to revert node log level for IP $ip. Response: ${RED}$response${RESET}" fi else exit 1 echo -e "${GREEN}$(timestamp)${RESET} Log level change fiis permanent until manually modified." done fi fi # Combine the log outputfi for} multiple IP# addressesRun intoin adetachable single summary if (( size_diff < 0 ))or directly if $detachable; then echo -e "castor.log file was# rotated."Pass the main_script function to elsethe screen session and store the output in a file debug_msg "**********************************************************" | echotee -ea "$output_log" debug_msg "Detach mode - Parameters passed to main_script:" | tee -a "$output_log" 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." fi else echo "Log level change is permanent until manually modified." 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 filedebug_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: $credentials$jq_or_grep" | tee -a "$output_log" debug_msg " New Log LevelDetach: $new_log_level$detachable" | tee -a "$output_log" debug_msg " New log Level NameDebug: $new_log_level_name$debug" | tee -a "$output_log" debug_msg " Duration: $duration**********************************************************" | tee -a "$output_log" debug_msg " Log Level Type: $log_level_type" | tee -a "$output_log"# Convert associative arrays to strings and pass them to the screen session log_levels_string=$(declare -p log_levels) debug_msg "log_level_names_string=$(declare -p log_level_names) Log Filedebug_msg "log_levels_string: $log_levels_filestring" | tee -a "$output_log" debug_msg " Initial Log File Size: $initial_sizelog_level_names_string: $log_level_names_string" | tee -a "$output_log" debug_msg "if command Current Log Level: $current_log_level" | tee -a "$output_log"-v screen &>/dev/null; then debug_msg " echo -e "Running Cluster Name: $clusterNamein ${YELLOW}screen${RESET} detachable mode..." | tee -a "$output_log" debug_msg " screen -dmS jqcastor_orlog_grep: $jq_or_grep" | tee -a "$output_log" debug_msg " Detach: $detachable" | tee -a "$output_log" debug_msg " Debug: $debug" | tee -a "$output_log" debug_msg "**********************************************************" | tee -a "$output_log"script 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 \"$output_log\"" # Convert associative arrays to strings and pass them toscreen -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)display the output log sleep 1 while screen -list | grep -q "castor_log_script"; do debug_msg "log_levels_string: $log_levels_string" | tee -a "$output_log" sleep 1 debug_msg "log_level_names_string: $log_level_names_string" | tee -a "$output_log" done ifelif command -v screentmux &>/dev/null; then echo -e "Running in ${YELLOW}screen$tmux${RESET} detachable mode..." |> tee -a "$output_log" # Truncate log file to remove old #entries if [[ "$debug" ]]; then # Create a temp #script file echo -e "Debug mode enabled. Check the output log for debug messages. temp_script="$SCRIPTDIR/castor_log_script.sh" # Write the script to sleepa 5file to avoid printing function definitions # fi cat <<EOF screen -dmS indexer> "$temp_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}\" | tee \"$output_log\"" screen -r indexer_script # Wait for the screen session to complete and then display the output log sleep 1 while screen -list | grep -q "indexer_script"; do_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" sleep 1 # Remove the script file tmux donekill-session -t castor_log_script EOF elif command -v tmux &>/dev/null; then # Ensure the script is executable echo -e "Running in ${YELLOW}tmux${RESET} detachable mode..." | teechmod -a+x "$output$temp_logscript" # ifStart [[ "$debug" ]]; then tmux session and execute the script inside # tmux new-session echo-d -es castor_log_script "Debug mode enabled. Check the output log for debug messages." #bash $temp_script" # Attach session only if it's still running sleep 5 while tmux has-session -t castor_log_script 2>/dev/null; do # fi tmux newattach-session -d -s indexert castor_log_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\" \"${log_levels_string}\" \"${log_level_names_string}\" | tee \"$output_log\"" done # if tmux has-session -t castor_log_script 2>/dev/null; then # tmux attach-session -t indexercastor_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 |
...