#!/bin/bash . /usr/src/m2/framework/bash_functions.sh # Script for partition delete # http://trac.kolmisoft.com/trac/ticket/15486 # http://trac.kolmisoft.com/trac/ticket/16003 # Scripts print output stdout # It should be run from cron like this # X X * * * root /path/to/script EXECUTE >> /var/log/m2/m2_partitions_delete.log 2>&1 VERSION="1.1.2" if [[ "`/sbin/pidof -x $(basename $0) -o %PPID`" ]]; then echo "$(date_time) [WARNING] $(basename $0) script is already running with PID `/sbin/pidof -x $(basename $0) -o %PPID`" exit 0 fi email_to="support@kolmisoft.com" DRY_RUN=1 [[ $1 == "EXECUTE" ]] && DRY_RUN=0 # safer this way when by default it is DRY RUN today=$(date +'%Y-%m-%d') digit_regex="^[[:digit:]]+$" partition_regex="^d2[0-9][0-9][0-9][0-1][0-9][0-3][0-9]$" mysql_start_regex="^2[0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9] 00:00:00$" mysql_end_regex="^2[0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9] 23:59:59$" #TODO: check DB value for archiving calls # Now checks system.conf delete_partitions_older_than_days, if not found, default value 90 is used delete_partitions_older_than_days=$(sed 's/ //g' /etc/m2/system.conf | awk -F"=" '/delete_partitions_older_than_days/{print $2}') delete_calls_old_partitions_older_than_days=$(sed 's/ //g' /etc/m2/system.conf | awk -F"=" '/delete_calls_old_partitions_older_than_days/{print $2}') mkdir -p /tmp/partition_managment error_log="/tmp/partition_managment/partitions_delete_mysql_error.log" partitions_calls_to_delete="/tmp/partition_managment/partitions_calls_to_delete$$.txt" partitions_calls_old_to_delete="/tmp/partition_managment/partitions_calls_old_to_delete$$.txt" rm -f $error_log RED='\033[0;31m' GREEN='\033[0;32m' BLUE='\033[0;34m' NC='\033[0m' cleanup() { result=$? rm -f $error_log rm -f $partitions_calls_to_delete rm -f $partitions_calls_old_to_delete echo "$(date_time) [NOTICE] Exiting" exit ${result} } trap cleanup EXIT INT send_email_and_exit() { if ((DRY_RUN == 1)); then echo -e "$(date_time) ${GREEN}DRY_RUN!!!${NC} ${BLUE}Email will NOT be sent.${NC}" exit 1 else send_email exit 1 fi } set_and_check_start_dates() { local date_to_check="$1" if ! date_start_check=$(date -d "$today - $date_to_check days" +'d%Y%m%d' 2>&1); then echo "$(date_time) [ERROR] ${RED}Failed to generate date start check:${NC}" echo "$date_start_check" send_email_and_exit elif [[ ! $date_start_check =~ $partition_regex ]]; then echo -e "$(date_time) [ERROR] Got date in unexpected format: ${RED}$date_start_check${NC}" send_email_and_exit fi } send_email() { retrieve_email_settings retrieve_ip_settings # HERE documents don't like indentation read -r -d '' email_output << EOF Hello, $0 script detected issues while deleting partitions Please check /var/log/m2/m2_partitions_delete.log for more information. Information about sever: External IP: $external_ip Main routing IP: $main_server_ip Client tag: $client_tag IP from GUI config: $gui_config_ip --------------- This email was sent automatically by script $0 EOF if [ "$Email_Login" == "" ] && [ "$Email_Password" == "" ]; then echo "$(date_time) [NOTICE] Sending Email using command below:" echo -e "${BLUE}/usr/local/m2/sendEmail -f \"$Email_from\" -t $email_to -u \"[$external_ip] Failed to delete partition\" -s \"$Email_Smtp_Server:$Email_port\"${NC} -m \"$email_output\" ${BLUE}-o tls='auto'${NC}" sendemail_output=$(/usr/local/m2/sendEmail -f "$Email_from" -t $email_to -u "[$external_ip] Failed to delete partition" -s "$Email_Smtp_Server:$Email_port" -m "$email_output" -o tls='auto' 2>&1) if echo "$sendemail_output" | grep -Fq 'was sent successfully!'; then echo -e "$(date_time) [NOTICE] ${GREEN}Email was sent successfully!${NC}" else echo -e "$(date_time) [WARNING] ${RED}Failed to send Email. sendEmail output below:${NC}" echo "$sendemail_output" fi else echo "$(date_time) [NOTICE] Sending Email using command below:" echo -e "${BLUE}/usr/local/m2/sendEmail -f \"$Email_from\" -xu \"$Email_Login\" -xp \"$Email_Password\" -t $email_to -u \"[$external_ip] Failed to delete partition\" -s \"$Email_Smtp_Server:$Email_port\"${NC} -m \"$email_output\" ${BLUE}-o tls='auto'${NC}" sendemail_output=$(/usr/local/m2/sendEmail -f "$Email_from" -xu "$Email_Login" -xp "$Email_Password" -t $email_to -u "[$external_ip] Failed to delete partition" -s "$Email_Smtp_Server:$Email_port" -m "$email_output" -o tls='auto' 2>&1) if echo "$sendemail_output" | grep -Fq 'was sent successfully!'; then echo -e "$(date_time) [NOTICE] ${GREEN}Email was sent successfully!${NC}" else echo -e "$(date_time) [WARNING] ${RED}Failed to send Email. sendEmail output below:${NC}" echo "$sendemail_output" fi fi } drop_partition() { # $1 - partition date (without d, 20xxyyzz) # $2 - table (calls or calls_old) local partition_date="$1" local table="$2" if [[ $# -ne 2 || ( $2 != "calls" && $2 != "calls_old" ) ]]; then echo -e "$(date_time) [ERROR] Failed to drop partition ${RED}d$partition_date${NC}" echo -e "$(date_time) [ERROR] Incorrect drop_partition() usage" echo -e "$(date_time) Expected drop_partition [partition date] [calls|calls_old] got drop_partition [$1] [$2]" send_email_and_exit fi if ((DRY_RUN == 1)); then echo -e "$(date_time) [${GREEN}DRY_RUN${NC}] Partition ${GREEN}d$partition_date${NC} in table ${GREEN}$table${NC} would have been dropped" return 0 fi if mysql_output=$(MYSQL_PWD="$DB_PASSWORD" /usr/bin/mysql -h "$DB_HOST" -u $DB_USERNAME "$DB_NAME" -e "ALTER TABLE $table DROP PARTITION d$partition_date" 2>&1); then echo -e "$(date_time) [NOTICE] Partition ${GREEN}d$partition_date${NC} dropped successfully" else echo -e "$(date_time) [ERROR] Failed to drop partition ${RED}d$partition_date${NC}" echo "$(date_time) [NOTICE] MySQL error message:" echo "$mysql_output" send_email_and_exit fi } delete_calls_partitions() { local count_calls_old=0 local count_calls=0 local mysql_date_start="" local mysql_date_end="" rm -f $error_log set_and_check_start_dates "$delete_partitions_older_than_days" # We will delete partitions from beginning till date_start_check if ! ruby -e "require 'date';if (Date.today - Date.parse(\"$date_start_check\")).to_i != $delete_partitions_older_than_days; exit 1; end" 2>&1; then echo -e "$(date_time) [ERROR] error checking start date. Expecting today - start_date == delete_calls_old_partitions_older_than_days" send_email_and_exit fi # Calls echo -e "$(date_time) [NOTICE] Will check for ${GREEN}calls${NC} partitions older than ${GREEN}$delete_partitions_older_than_days${NC} days (partitions older than ${GREEN}$date_start_check${NC})" echo "" MYSQL_PWD="$DB_PASSWORD" /usr/bin/mysql -h "$DB_HOST" -u $DB_USERNAME "$DB_NAME" -sNe "SELECT PARTITION_NAME FROM information_schema.partitions WHERE TABLE_SCHEMA='m2' AND TABLE_NAME = 'calls' AND PARTITION_NAME IS NOT NULL AND STRCMP ('$date_start_check', PARTITION_NAME) = 1" 2> $error_log | grep -v future | sed -e 's/^d//' | grep -E '^20[0-9][0-9][01][0-9][0-3][0-9]$' > $partitions_calls_to_delete # Check for mysql errors if [[ -s $error_log ]]; then echo -e "$(date_time) [ERROR] ${RED}MySQL errors while generating calls partitions list:${NC}" cat $error_log send_email_and_exit fi # Check for empty file if [[ ! -s $partitions_calls_to_delete ]]; then echo -e "$(date_time) [NOTICE] No partitions for ${GREEN}calls${NC} older than ${GREEN}$delete_partitions_older_than_days${NC} days have been found" fi while read -r partition_date do mysql_date_start=$(date -d "$partition_date" +'%Y-%m-%d 00:00:00') mysql_date_end=$(date -d "$partition_date" +'%Y-%m-%d 23:59:59') if [[ ! $mysql_date_start =~ $mysql_start_regex ]] || [[ ! $mysql_date_end =~ $mysql_end_regex ]]; then echo -e "$(date_time) [ERROR] Got dates |${RED}$mysql_date_start${NC}| and |${RED}$mysql_date_end${NC}| in unexpected format" echo -e "$(date_time) [ERROR] Expected |${GREEN}20xx-xx-xx 00:00:00${NC}| and |${GREEN}20xx-xx-xx 23:59:59${NC}|" send_email_and_exit fi echo -e "$(date_time) [NOTICE] Counting calls for partition ${GREEN}d$partition_date${NC} [calls] vs [calls_old]" count_calls=$(MYSQL_PWD="$DB_PASSWORD" /usr/bin/mysql -h "$DB_HOST" -u "$DB_USERNAME" "$DB_NAME" -sNe "select count(id) from calls WHERE calldate BETWEEN \"$mysql_date_start\" AND \"$mysql_date_end\" AND disposition='ANSWERED'" 2>> $error_log) count_calls_old=$(MYSQL_PWD="$DB_PASSWORD" /usr/bin/mysql -h "$DB_HOST" -u "$DB_USERNAME" "$DB_NAME" -sNe "select count(id) from calls_old WHERE calldate BETWEEN \"$mysql_date_start\" AND \"$mysql_date_end\" AND disposition='ANSWERED'" 2>> $error_log) if [[ -s $error_log ]]; then echo -e "$(date_time) [ERROR] ${RED}MySQL errors while calculating calls count:${NC}" cat $error_log send_email_and_exit fi if [[ $count_calls =~ $digit_regex ]] && [[ $count_calls_old =~ $digit_regex ]]; then if (( count_calls != count_calls_old )); then echo -e "$(date_time) [WARNING] Cannot delete partition ${GREEN}d$partition_date${NC} as calls amount is different - ${BLUE}calls:${NC} ${RED}$count_calls${NC}, ${BLUE}calls_old${NC}: ${RED}$count_calls_old${NC}" send_email_and_exit else echo -e "$(date_time) [NOTICE] Calls count match for ${GREEN}d$partition_date${NC} - ${BLUE}calls:${NC} ${GREEN}$count_calls${NC}, ${BLUE}calls_old:${NC} ${GREEN}$count_calls_old${NC}"; drop_partition "$partition_date" "calls" resync_partition "$partition_date" fi else echo -e "$(date_time) [ERROR] ${RED}Cannot retrieve calls amount. Check${NC} ${BLUE}$error_log${NC} ${RED}for errors${NC}" send_email_and_exit fi echo "" done < $partitions_calls_to_delete } delete_calls_old_partitions() { local count_calls_old=0 local count_calls=0 local mysql_date_start="" local mysql_date_end="" rm -f $error_log set_and_check_start_dates "$delete_calls_old_partitions_older_than_days" # We will delete partitions from beginning till date_start_check #Double check date_start_check, error here would be devastating, as in calls_old we do not have any additional checks if ! ruby -e "require 'date';if (Date.today - Date.parse(\"$date_start_check\")).to_i != $delete_calls_old_partitions_older_than_days; exit 1; end" 2>&1; then echo -e "$(date_time) [ERROR] error checking start date. Expecting today - start_date == delete_calls_old_partitions_older_than_days" send_email_and_exit fi echo "-------------------------------------------------------------------------------------" echo -e "$(date_time) [NOTICE] Will check for ${GREEN}calls_old${NC} partitions older than ${GREEN}$delete_calls_old_partitions_older_than_days${NC} days (partitions older than ${GREEN}$date_start_check${NC})" echo "" MYSQL_PWD="$DB_PASSWORD" /usr/bin/mysql -h "$DB_HOST" -u $DB_USERNAME "$DB_NAME" -sNe "SELECT PARTITION_NAME FROM information_schema.partitions WHERE TABLE_SCHEMA='m2' AND TABLE_NAME = 'calls_old' AND PARTITION_NAME IS NOT NULL AND STRCMP ('$date_start_check', PARTITION_NAME) = 1" 2> $error_log | grep -v future | sed -e 's/^d//' | grep -E '^20[0-9][0-9][01][0-9][0-3][0-9]$' > $partitions_calls_old_to_delete # Check for mysql errors if [[ -s $error_log ]]; then echo -e "$(date_time) [ERROR] ${RED}MySQL errors while generating calls_old partitions list:${NC}" cat $error_log send_email_and_exit fi # Check for empty file if [[ ! -s $partitions_calls_old_to_delete ]]; then echo -e "$(date_time) [NOTICE] No ${GREEN}calls_old${NC} partitions older than ${GREEN}$delete_calls_old_partitions_older_than_days${NC} days have been found" fi while read -r partition_date do mysql_date_start=$(date -d "$partition_date" +'%Y-%m-%d 00:00:00') mysql_date_end=$(date -d "$partition_date" +'%Y-%m-%d 23:59:59') if [[ ! $mysql_date_start =~ $mysql_start_regex ]] || [[ ! $mysql_date_end =~ $mysql_end_regex ]]; then echo -e "$(date_time) [ERROR] Got dates |${RED}$mysql_date_start${NC}| and |${RED}$mysql_date_end${NC}| in unexpected format" echo -e "$(date_time) [ERROR] Expected |${GREEN}20xx-xx-xx 00:00:00${NC}| and |${GREEN}20xx-xx-xx 23:59:59${NC}|" send_email_and_exit fi # If we deleting calls_old partition, calls partition for same date should be gone long time ago. # So check if this the case as sanity check count_calls=$(MYSQL_PWD="$DB_PASSWORD" /usr/bin/mysql -h "$DB_HOST" -u "$DB_USERNAME" "$DB_NAME" -sNe "select count(id) from calls WHERE calldate BETWEEN \"$mysql_date_start\" AND \"$mysql_date_end\" AND disposition='ANSWERED'" 2>> $error_log) count_calls_old=$(MYSQL_PWD="$DB_PASSWORD" /usr/bin/mysql -h "$DB_HOST" -u "$DB_USERNAME" "$DB_NAME" -sNe "select count(id) from calls_old WHERE calldate BETWEEN \"$mysql_date_start\" AND \"$mysql_date_end\" AND disposition='ANSWERED'" 2>> $error_log) if [[ -s $error_log ]]; then echo -e "$(date_time) [ERROR] ${RED}MySQL errors while calculating calls count:${NC}" cat $error_log send_email_and_exit fi if ((count_calls != 0)); then # If we are running DRY_RUN and partition would have been dropped, do not generate false warning if ((DRY_RUN == 1)) && grep -Fq "$partition_date" $partitions_calls_to_delete; then : else echo -e "$(date_time) [WARNING] There are $count_calls calls in calls table for $partition_date" echo -e "$(date_time) [WARNING] If we deleting [calls_old] partition for $partition_date, by design should be 0 calls in [calls] table for $partition_date" echo -e "$(date_time) [WARNING] Please check manually. Exiting" send_email_and_exit fi fi if ((DRY_RUN == 0)); then echo -e "$(date_time) [NOTICE] preparing to drop ${BLUE}calls_old${NC} partition ${BLUE}d$partition_date${NC} with ${BLUE}$count_calls_old calls ${NC}" else echo -e "$(date_time) [${GREEN}DRY_RUN${NC}] preparing to drop ${BLUE}calls_old${NC} partition ${BLUE}d$partition_date${NC} with ${BLUE}$count_calls_old calls ${NC}" fi drop_partition "$partition_date" "calls_old" echo "" done < $partitions_calls_old_to_delete } resync_partition() { local partition_date="$1" local mysql_date_start="" local mysql_date_end="" mysql_date_start=$(date -d "$partition_date" +'%Y-%m-%d') mysql_date_end=${mysql_date_start} if ((DRY_RUN == 1)); then echo -e "$(date_time) [${GREEN}DRY_RUN${NC}] Date ${GREEN}$mysql_date_start - $mysql_date_end${NC} would have been resynced (calls deleted in ES)" return 0 fi if [[ -e /usr/bin/elasticsearch ]]; then echo -e "$(date_time) [NOTICE] Resyncing ES (deleting calls from ES)${BLUE}elasticsearch resync \"$mysql_date_start\" \"$mysql_date_end\" ${NC}" echo -e "$(date_time) [NOTICE] resync log will be in ${BLUE}/var/log/m2/elasticsearch_resync.log${NC}" elasticsearch resync "$mysql_date_start" "$mysql_date_end" > /dev/null 2>&1 else echo -e "$(date_time) [WARNING] /usr/bin/elasticsearch does not exits, will NOT resync ES for dropped partition" echo -e "$(date_time) [NOTICE] Possible fix: svn update /usr/src/m2; ln -fs /usr/src/m2/elasticsearch/m2_elasticsearch.sh /usr/bin/elasticsearch" fi } ############### main setup ################### set_database_variables if ((DRY_RUN==1)); then echo -e "$(date_time) [NOTICE] ${GREEN}DRY RUN!!!${NC}" fi delete_partitions_older_than_days=$(sed 's/ //g' /etc/m2/system.conf | awk -F"=" '/delete_partitions_older_than_days/{print $2}') delete_calls_old_partitions_older_than_days=$(sed 's/ //g' /etc/m2/system.conf | awk -F"=" '/delete_calls_old_partitions_older_than_days/{print $2}') if [[ -z "$delete_partitions_older_than_days" ]] || [[ ! $delete_partitions_older_than_days =~ $digit_regex ]]; then delete_partitions_older_than_days=0 fi if [[ -z "$delete_calls_old_partitions_older_than_days" ]] || [[ ! $delete_calls_old_partitions_older_than_days =~ $digit_regex ]]; then delete_calls_old_partitions_older_than_days=0 fi echo -e "$(date_time) [NOTICE] $0 settings:" echo -e "${BLUE}VERSION:${NC} ${GREEN}$VERSION${NC}" echo -e "${BLUE}DRY_RUN:${NC} ${GREEN}$DRY_RUN${NC}" echo -e "${BLUE}delete_partitions_older_than_days:${NC} ${GREEN}$delete_partitions_older_than_days${NC}" echo -e "${BLUE}delete_calls_old_partitions_older_than_days:${NC} ${GREEN}$delete_calls_old_partitions_older_than_days${NC}" echo "" if (( delete_calls_old_partitions_older_than_days != 0)); then if (( delete_calls_old_partitions_older_than_days <= delete_partitions_older_than_days )); then echo -e "$(date_time) ${RED}[ERROR] delete_calls_old_partitions_older_than_days [$delete_calls_old_partitions_older_than_days] <= delete_partitions_older_than_days [$delete_partitions_older_than_days]${NC}" send_email_and_exit fi if ((delete_calls_old_partitions_older_than_days < 60)); then echo -e "$(date_time) ${RED}[ERROR] delete_calls_old_partitions_older_than_days set to $delete_calls_old_partitions_older_than_days${NC}" echo -e "$(date_time) [NOTICE] delete_calls_old_partitions_older_than_days should be >= 60. Exiting" send_email_and_exit fi max_calls_old_from_now=$(MYSQL_PWD="$DB_PASSWORD" /usr/bin/mysql -h "$DB_HOST" -u "$DB_USERNAME" "$DB_NAME" -sNe "select IFNULL(datediff(CURDATE(), max(calldate)),99999) from calls_old;") if ((delete_calls_old_partitions_older_than_days <= max_calls_old_from_now)); then echo -e "$(date_time) ${RED}[ERROR] delete_calls_old_partitions_older_than_days set to $delete_calls_old_partitions_older_than_days${NC}" echo -e "$(date_time) ${RED}[ERROR] This will delete ALL calls_old partitions${NC}" echo -e "$(date_time) [NOTICE] Check settings manually. Exiting" send_email_and_exit fi fi if (( delete_calls_old_partitions_older_than_days != 0)); then if (( delete_partitions_older_than_days < 60 )); then echo -e "$(date_time) ${RED}[ERROR] delete_partitions_older_than_days set to $delete_partitions_older_than_days${NC}" echo -e "$(date_time) [NOTICE] delete_partitions_older_than_days should be >= 60. Exiting" send_email_and_exit fi fi ############### main ################### echo -e "$(date_time) [NOTICE] Starting ${BLUE}$0${NC} script:" # Calls if ((delete_partitions_older_than_days != 0)); then delete_calls_partitions else echo -e "$(date_time) [NOTICE] ${GREEN}calls${NC} partitions ${BLUE} will not be checked ${NC}" fi # Calls old if ((delete_calls_old_partitions_older_than_days != 0)); then delete_calls_old_partitions else echo -e "$(date_time) [NOTICE] ${GREEN}calls_old${NC} partitions ${BLUE} will not be checked ${NC}" fi