#!/usr/bin/env bash # DESCRIPTION: # creating database dump, copying to additional smb share, copies rotating # and # sending report to email # # DEPENDENCIES: # - privileged rights # - mysqldump # - gzip # - cifs-utils # - Python 3 # - sendmail.py # # PARAMETERS: # 1: "/path/to/file.conf" - path to config file # # FUNCTIONS: # ####################################### # Print message and add to log. # Globals: # logs: /path/to/file.log # Arguments: # 1: message to print and logging ####################################### addtologs() { printf "%s\n" "$(date +'%Y.%m.%d-%H:%M:%S') $1" | tee -a "${logs}" } ####################################### # Exit procedure. # Globals: # time: time of script start # Arguments: # None ####################################### execquite() { addtologs "execution time is $(($(date +%s)-time)) seconds, exit." exit "${1}" } ####################################### # Error exit procedure. # Globals: # None # Arguments: # 1: message to print and logging ####################################### execerror() { addtologs "error: $1" execquite 1 } ####################################### # Parsing config file and creating global vars. # Globals: # conf: /path/to/file.conf # Arguments: # None ####################################### getconfig() { # mysql connection parameters db_host=$(grep 'db_host=' "${conf}" | cut -d= -f2) db_user=$(grep 'db_user=' "${conf}" | cut -d= -f2) db_pass=$(grep 'db_pass=' "${conf}" | cut -d= -f2) db_name=$(grep 'db_name=' "${conf}" | cut -d= -f2) # dump repository parameters dump_root=$(grep 'dump_root=' "${conf}" | cut -d= -f2) dump_name="${db_name}_backup_$(date +%Y_%m_%d_%H%M%S).sql.gz" dump_file="${dump_root}/${dump_name}" dump_save=$(grep 'dump_save=' "${conf}" | cut -d= -f2) # copy smb-repository parameters smb_host=$(grep 'smb_host=' "${conf}" | cut -d= -f2) smb_path=$(grep 'smb_path=' "${conf}" | cut -d= -f2) smb_user=$(grep 'smb_user=' "${conf}" | cut -d= -f2) smb_domn=$(grep 'smb_domn=' "${conf}" | cut -d= -f2) smb_pass=$(grep 'smb_pass=' "${conf}" | cut -d= -f2) copy_root="/mnt/${smb_host}/${smb_path}" copy_file="${copy_root}/${dump_name}" copy_save=$(grep 'copy_save=' "${conf}" | cut -d= -f2) # sendmail parameters smtp_pyth=$(grep 'smtp_pyth=' "${conf}" | cut -d= -f2) smtp_send=$(grep 'smtp_send=' "${conf}" | cut -d= -f2) smtp_host=$(grep 'smtp_host=' "${conf}" | cut -d= -f2) smtp_port=$(grep 'smtp_port=' "${conf}" | cut -d= -f2) smtp_from=$(grep 'smtp_from=' "${conf}" | cut -d= -f2) smtp_pass=$(grep 'smtp_pass=' "${conf}" | cut -d= -f2) smtp_dest=$(grep 'smtp_dest=' "${conf}" | cut -d= -f2) # common parameters logs="${dump_file}.log" } ####################################### # Checking user rights. # Globals: # None # Arguments: # None # return: # 0 - if privileged rights, 1 - if not privileged rights ####################################### checkroot() { if [ "${EUID}" -ne 0 ]; then return 1 # false else return 0 # true fi } ####################################### # Start mysqldump without database blocking (WARNING: InnoDB required). # Globals: # db_user: db auth username # db_pass: db auth password # db_host: db host address # db_name: db name # dump_file: database_dump.sql.gz # dump_root: /path/to/dump/directory # addtolog: log-writer function # Arguments: # None # return: # 0 - dump created, 1 - dump failed ####################################### createdump() { if mysqldump --single-transaction --skip-lock-tables \ --user="${db_user}" --password="${db_pass}" \ -h "${db_host}" "${db_name}" | \ gzip > "${dump_file}"; then addtologs "created ${dump_file}" return 0 # true else addtologs "failed ${dump_file}" return 1 # false fi } ####################################### # Search and delete files, keep last file's amount. # Globals: # addtolog: log-writer function # Arguments: # 1: path for search # 2: name for search (grep's regex) # 3: amount of last files to keep ####################################### # shellcheck disable=SC2010,SC2207 cleanolder(){ file_root=$1 file_name=$2 keep_last=$3 file_list=() file_list=($(ls -tra "${file_root}" | grep "${file_name}")) list_size=${#file_list[@]} list_stop=$((list_size-keep_last)) if [ "${list_stop}" -gt 0 ]; then for ((i=0; i /dev/null 2>&1 addtologs "sent mail with subject '${smtp_subj}' to '${smtp_dest}'" } # # VARIABLES: # conf=$1 time=$(date +%s) newl=$'\n' # # MAIN: # if [ -e "${conf}" ]; then getconfig else logs=/dev/null execerror "Config not found. Usage example: $0 '/path/to/dump.conf'" fi if ! command -v mysqldump &> /dev/null || \ ! command -v gzip &> /dev/null || \ ! command -v "${smtp_send}" &> /dev/null || \ ! command -v "${smtp_pyth}" &> /dev/null || \ ! command -v mount.cifs &> /dev/null; then execerror "Not found dependencies." fi if ! checkroot; then execerror "Restart this as root!" else smtp_subj="[Failed] MySQL backup: '${db_name}' on ${db_host}" db_orig_size_byte=$(mysql --user="${db_user}" --password="${db_pass}" -h "${db_host}" \ -e 'SELECT table_schema AS "Database", (SUM(data_length)+SUM(index_length)) AS "Size" FROM information_schema.TABLES GROUP BY table_schema;' | \ grep "${db_name}" | awk '{print $2}') fs_root_free_byte=$(df -B1 "${dump_root}" | tail -1 | awk '{print $4}') addtologs "${db_orig_size_byte} bytes are in '${db_name}' database." addtologs "${fs_root_free_byte} bytes are free in ${dump_root}." if [ "${db_orig_size_byte}" -gt "${fs_root_free_byte}" ]; then addtologs "%s\n" "Not enough free space" else addtologs "Start backuping MySQL '${db_name}' database.${newl}" if ! createdump; then addtologs "Failed backuping MySQL '${db_name}' database." else smtp_subj="[Warning] MySQL backup: '${db_name}' on ${db_host}" db_copy_size_unit=$(du -h "${dump_file}" | awk '{print $1}') fs_root_free_unit=$(df -h "${dump_root}" | tail -1 | awk '{print $4}') addtologs "created ${dump_file} of ${db_copy_size_unit}." addtologs "${fs_root_free_unit} are free in ${dump_root}." addtologs "Start cleaning. Keep last ${dump_save}.${newl}" cleanolder "${dump_root}" "^${db_name}_backup_.*.sql.gz$" "${dump_save}" cleanolder "${dump_root}" "^${db_name}_backup_.*.sql.gz.log$" "${dump_save}" fs_root_free_unit=$(df -h "${dump_root}" | tail -1 | awk '{print $4}') addtologs "${fs_root_free_unit} are free in ${dump_root}." if mkdir -p "${copy_root}" && \ mount.cifs "//${smb_host}/${smb_path}" "${copy_root}" \ -o "user=${smb_user},domain=${smb_domn},password=${smb_pass},vers=2.0"; then sleep 1 db_copy_size_byte=$(du -B1 "${dump_file}" | awk '{print $1}') fs_copy_free_byte=$(df -B1 "${copy_root}" | tail -1 | awk '{print $4}') addtologs "${db_copy_size_byte} bytes are in ${dump_file}." addtologs "${fs_copy_free_byte} bytes are free in ${copy_root}." if [ "${db_copy_size_byte}" -gt "${fs_copy_free_byte}" ]; then addtologs "%s\n" "Not enough free space" else addtologs "Start copying backup to //${smb_host}/${smb_path}${newl}" if ! cp "${dump_file}" "${copy_file}"; then addtologs "failed ${db_name} dump copy in ${copy_root}" else smtp_subj="[Success] MySQL backup: '${db_name}' on ${db_host}" addtologs "created ${copy_file}" fs_copy_free_unit=$(df -h "${copy_root}" | tail -1 | awk '{print $4}') addtologs "${fs_copy_free_unit} are free in ${copy_root}." sleep 1 addtologs "Start cleaning. Keep last ${copy_save}.${newl}" cleanolder "${copy_root}" "^${db_name}_backup_.*.sql.gz$" "${copy_save}" fs_copy_free_unit=$(df -h "${copy_root}" | tail -1 | awk '{print $4}') addtologs "${fs_copy_free_unit} are free in ${copy_root}." fi fi umount "/mnt/${smb_host}/${smb_path}" && rm -rf "/mnt/${smb_host:?}" fi fi fi startsendmail fi execquite 0