diff --git a/client/lib/dump-postgresql.sh b/client/lib/dump-postgresql.sh deleted file mode 100644 index cfe4a80..0000000 --- a/client/lib/dump-postgresql.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC2034,SC2317,SC2155 - -####################################################################### -# Dump a single file of all PostgreSQL databases -# -# Arguments: -####################################################################### -dump_postgresql_global() { - local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-global" - local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") - rm -rf "${dump_dir}" "${errors_dir}" - mkdir -p "${dump_dir}" "${errors_dir}" - # No need to change recursively, the top directory is enough - chmod 700 "${dump_dir}" "${errors_dir}" - - ## example with pg_dumpall and with compression - local error_file="${errors_dir}/pg_dumpall.err" - local dump_file="${dump_dir}/pg_dumpall.sql.gz" - log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" - - (sudo -u postgres pg_dumpall) 2> "${error_file}" | gzip --best > "${dump_file}" - - local last_rc=$? - # shellcheck disable=SC2086 - if [ ${last_rc} -ne 0 ]; then - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dumpall to ${dump_file} returned an error ${last_rc}" "${error_file}" - GLOBAL_RC=${E_DUMPFAILED} - else - rm -f "${error_file}" - fi - - log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" - - ## example with pg_dumpall and without compression - ## WARNING: you need space in ~postgres - # local error_file="${errors_dir}/pg_dumpall.err" - # local dump_file="${dump_dir}/pg_dumpall.sql" - # log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" - # - # (su - postgres -c "pg_dumpall > ~/pg.dump.bak") 2> "${error_file}" - # mv ~postgres/pg.dump.bak "${dump_file}" - # - # log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" -} - -####################################################################### -# Dump a compressed file per database -# -# Arguments: -####################################################################### -dump_postgresql_per_base() { - local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-per-base" - local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") - rm -rf "${dump_dir}" "${errors_dir}" - mkdir -p "${dump_dir}" "${errors_dir}" - # No need to change recursively, the top directory is enough - chmod 700 "${dump_dir}" "${errors_dir}" - - ( - # shellcheck disable=SC2164 - cd /var/lib/postgresql - databases=$(sudo -u postgres psql -U postgres -lt | awk -F\| '{print $1}' | grep -v "template.*") - for database in ${databases} ; do - local error_file="${errors_dir}/${database}.err" - local dump_file="${dump_dir}/${database}.sql.gz" - log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" - - (sudo -u postgres /usr/bin/pg_dump --create -U postgres -d "${database}") 2> "${error_file}" | gzip --best > "${dump_file}" - - local last_rc=$? - # shellcheck disable=SC2086 - if [ ${last_rc} -ne 0 ]; then - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}" - GLOBAL_RC=${E_DUMPFAILED} - else - rm -f "${error_file}" - fi - log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" - done - ) -} - -####################################################################### -# Dump a compressed file per database -# -# Arguments: -# -# TODO: add arguments to include/exclude tables -####################################################################### -dump_postgresql_filtered() { - local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-filtered" - local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") - rm -rf "${dump_dir}" "${errors_dir}" - mkdir -p "${dump_dir}" "${errors_dir}" - # No need to change recursively, the top directory is enough - chmod 700 "${dump_dir}" "${errors_dir}" - - local error_file="${errors_dir}/pg-backup.err" - local dump_file="${dump_dir}/pg-backup.tar" - log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" - - ## example with all tables from MYBASE excepts TABLE1 and TABLE2 - # pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -t 'TABLE1' -t 'TABLE2' MYBASE 2> "${error_file}" - - ## example with only TABLE1 and TABLE2 from MYBASE - # pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -T 'TABLE1' -T 'TABLE2' MYBASE 2> "${error_file}" - - local last_rc=$? - # shellcheck disable=SC2086 - if [ ${last_rc} -ne 0 ]; then - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}" - GLOBAL_RC=${E_DUMPFAILED} - else - rm -f "${error_file}" - fi - log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" -} diff --git a/client/lib/dump/elasticsearch.sh b/client/lib/dump/elasticsearch.sh new file mode 100644 index 0000000..1e5475b --- /dev/null +++ b/client/lib/dump/elasticsearch.sh @@ -0,0 +1,301 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034,SC2317,SC2155 + +####################################################################### +# Snapshot Elasticsearch data +# +# Arguments: +# --protocol= (default: http) +# --cacert=[String] (default: ) +# path to the CA certificate to use when using https +# --host=[String] (default: localhost) +# --port=[Integer] (default: 9200) +# --user=[String] (default: ) +# --password=[String] (default: ) +# --repository=[String] (default: snaprepo) +# --snapshot=[String] (default: snapshot.daily) +####################################################################### +dump_elasticsearch() { + local option_protocol="http" + local option_cacert="" + local option_host="localhost" + local option_port="9200" + local option_user="" + local option_password="" + local option_repository="snaprepo" + local option_snapshot="snapshot.daily" + local option_others="" + + # Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a + while :; do + case ${1:-''} in + --protocol) + # protocol options, with value separated by space + if [ -n "$2" ]; then + option_protocol="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument." + exit 1 + fi + ;; + --protocol=?*) + # protocol options, with value separated by = + option_protocol="${1#*=}" + ;; + --protocol=) + # protocol options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument." + exit 1 + ;; + --cacert) + # cacert options, with value separated by space + if [ -n "$2" ]; then + option_cacert="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--cacert' requires a non-empty option argument." + exit 1 + fi + ;; + --cacert=?*) + # cacert options, with value separated by = + option_cacert="${1#*=}" + ;; + --cacert=) + # cacert options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--cacert' requires a non-empty option argument." + exit 1 + ;; + --host) + # host options, with value separated by space + if [ -n "$2" ]; then + option_host="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument." + exit 1 + fi + ;; + --host=?*) + # host options, with value separated by = + option_host="${1#*=}" + ;; + --host=) + # host options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument." + exit 1 + ;; + --port) + # port options, with value separated by space + if [ -n "$2" ]; then + option_port="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument." + exit 1 + fi + ;; + --port=?*) + # port options, with value separated by = + option_port="${1#*=}" + ;; + --port=) + # port options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument." + exit 1 + ;; + --user) + # user options, with value separated by space + if [ -n "$2" ]; then + option_user="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument." + exit 1 + fi + ;; + --user=?*) + # user options, with value separated by = + option_user="${1#*=}" + ;; + --user=) + # user options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument." + exit 1 + ;; + --password) + # password options, with value separated by space + if [ -n "$2" ]; then + option_password="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument." + exit 1 + fi + ;; + --password=?*) + # password options, with value separated by = + option_password="${1#*=}" + ;; + --password=) + # password options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument." + exit 1 + ;; + --repository) + # repository options, with value separated by space + if [ -n "$2" ]; then + option_repository="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument." + exit 1 + fi + ;; + --repository=?*) + # repository options, with value separated by = + option_repository="${1#*=}" + ;; + --repository=) + # repository options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument." + exit 1 + ;; + --snapshot) + # snapshot options, with value separated by space + if [ -n "$2" ]; then + option_snapshot="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument." + exit 1 + fi + ;; + --snapshot=?*) + # snapshot options, with value separated by = + option_snapshot="${1#*=}" + ;; + --snapshot=) + # snapshot options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument." + exit 1 + ;; + --) + # End of all options. + shift + option_others=${*} + break + ;; + -?*|[[:alnum:]]*) + # ignore unknown options + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)" + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift + done + + # Use the default Elasticsearch CA certificate when using HTTPS, if not specified directly + local default_cacert="/etc/elasticsearch/certs/http_ca.crt" + if [ "${option_protocol}" = "https" ] && [ -z "${option_cacert}" ] && [ -f "${default_cacert}" ]; then + option_cacert="${default_cacert}" + fi + + local errors_dir="${ERRORS_DIR}/elasticsearch-${option_repository}-${option_snapshot}" + rm -rf "${errors_dir}" + mkdir -p "${errors_dir}" + # No need to change recursively, the top directory is enough + chmod 700 "${errors_dir}" + + log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${option_snapshot}" + + ## Take a snapshot as a backup. + ## Warning: You need to have a path.repo configured. + ## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes + + local base_url="${option_protocol}://${option_host}:${option_port}" + local repository_url="${base_url}/_snapshot/${option_repository}" + local snapshot_url="${repository_url}/${option_snapshot}" + + # Verify snapshot repository + + local error_file="${errors_dir}/verify.err" + + declare -a connect_options + connect_options=() + if [ -n "${option_cacert}" ]; then + connect_options+=(--cacert "${option_cacert}") + fi + if [ -n "${option_user}" ] || [ -n "${option_password}" ]; then + local connect_options+=("--user ${option_user}:${option_password}") + fi + if [ -n "${option_others}" ]; then + # word splitting is deliberate here + # shellcheck disable=SC2206 + connect_options+=(${option_others}) + fi + # Add the http return code at the end of the output + connect_options+=(--write-out '%{http_code}\n') + connect_options+=(--silent) + + declare -a dump_options + dump_options=() + dump_options+=(--request POST) + + dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${repository_url}/_verify?pretty" + log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" + ${dump_cmd} > "${error_file}" + + # test if the last line of the log file is "200" + tail -n 1 "${error_file}" | grep --quiet "^200$" "${error_file}" + + local last_rc=$? + # shellcheck disable=SC2086 + if [ ${last_rc} -ne 0 ]; then + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: repository verification returned an error ${last_rc}" "${error_file}" + GLOBAL_RC=${E_DUMPFAILED} + else + rm -f "${error_file}" + + # Delete snapshot + + declare -a dump_options + dump_options=() + dump_options+=(--request DELETE) + + dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${snapshot_url}" + log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" + ${dump_cmd} > /dev/null + + # Create snapshot + + local error_file="${errors_dir}/create.err" + + declare -a dump_options + dump_options=() + dump_options+=(--request PUT) + + dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${snapshot_url}?wait_for_completion=true" + log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" + ${dump_cmd} > "${error_file}" + + # test if the last line of the log file is "200" + tail -n 1 "${error_file}" | grep --quiet "^200$" "${error_file}" + + local last_rc=$? + # shellcheck disable=SC2086 + if [ ${last_rc} -ne 0 ]; then + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: curl returned an error ${last_rc}" "${error_file}" + GLOBAL_RC=${E_DUMPFAILED} + else + rm -f "${error_file}" + fi + fi + + log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${option_snapshot}" +} diff --git a/client/lib/dump-misc.sh b/client/lib/dump/misc.sh similarity index 50% rename from client/lib/dump-misc.sh rename to client/lib/dump/misc.sh index f060b73..c7ea1fa 100644 --- a/client/lib/dump-misc.sh +++ b/client/lib/dump/misc.sh @@ -102,9 +102,9 @@ dump_redis() { log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}" # Copy the Redis database - dump_cmd="cp -a ${instance}/dump.rdb ${dump_dir}/dump.rdb 2> ${error_file}" + dump_cmd="cp -a ${instance}/dump.rdb ${dump_dir}/dump.rdb" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + ${dump_cmd} 2> "${error_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -141,20 +141,46 @@ dump_redis() { # using a custom authentication, instead of /etc/mysql/debian.cnf # # Arguments: +# --port=[String] (default: ) # --user=[String] (default: ) # --password=[String] (default: ) +# --dump-label=[String] (default: "default") +# used as suffix of the dump dir to differenciate multiple instances +# Other options after -- are passed as-is to mongodump # # don't forget to create use with read-only access # > use admin # > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } ) ####################################################################### dump_mongodb() { + local option_port="" local option_user="" local option_password="" + local option_dump_label="" + local option_others="" # Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a while :; do case ${1:-''} in + --port) + # port options, with value separated by space + if [ -n "$2" ]; then + option_port="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument." + exit 1 + fi + ;; + --port=?*) + # port options, with value separated by = + option_port="${1#*=}" + ;; + --port=) + # port options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument." + exit 1 + ;; --user) # user options, with value separated by space if [ -n "$2" ]; then @@ -193,9 +219,29 @@ dump_mongodb() { log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument." exit 1 ;; + --dump-label) + # dump-label options, with value separated by space + if [ -n "$2" ]; then + option_dump_label="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument." + exit 1 + fi + ;; + --dump-label=?*) + # dump-label options, with value separated by = + option_dump_label="${1#*=}" + ;; + --dump-label=) + # dump-label options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument." + exit 1 + ;; --) # End of all options. shift + option_others=${*} break ;; -?*|[[:alnum:]]*) @@ -211,7 +257,15 @@ dump_mongodb() { shift done - local dump_dir="${LOCAL_BACKUP_DIR}/mongodump" + if [ -z "${option_dump_label}" ]; then + if [ -n "${option_port}" ]; then + option_dump_label="${option_port}" + else + option_dump_label="default" + fi + fi + + local dump_dir="${LOCAL_BACKUP_DIR}/mongodb-${option_dump_label}" local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") rm -rf "${dump_dir}" "${errors_dir}" mkdir -p "${dump_dir}" "${errors_dir}" @@ -221,15 +275,27 @@ dump_mongodb() { local error_file="${errors_dir}.err" log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}" - declare -a options - options=() - options+=(--username="${option_user}") - options+=(--password="${option_password}") - options+=(--out="${dump_dir}/") + declare -a dump_options + dump_options=() + if [ -n "${option_port}" ]; then + dump_options+=(--port="${option_port}") + fi + if [ -n "${option_user}" ]; then + dump_options+=(--username="${option_user}") + fi + if [ -n "${option_password}" ]; then + dump_options+=(--password="${option_password}") + fi + dump_options+=(--out="${dump_dir}/") + if [ -n "${option_others}" ]; then + # word splitting is deliberate here + # shellcheck disable=SC2206 + dump_options+=(${option_others}) + fi - dump_cmd=" mongodump ${options[*]} 2> ${error_file} > /dev/null" - log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + dump_cmd="mongodump ${dump_options[*]}" + log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd} > /dev/null" + ${dump_cmd} 2> "${error_file}" > /dev/null local last_rc=$? # shellcheck disable=SC2086 @@ -243,12 +309,12 @@ dump_mongodb() { } ####################################################################### -# Dump MegaCLI configuration +# Dump RAID configuration # # Arguments: ####################################################################### -dump_megacli_config() { - local dump_dir="${LOCAL_BACKUP_DIR}/megacli" +dump_raid_config() { + local dump_dir="${LOCAL_BACKUP_DIR}/raid" local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") rm -rf "${dump_dir}" "${errors_dir}" mkdir -p "${dump_dir}" "${errors_dir}" @@ -260,9 +326,9 @@ dump_megacli_config() { local dump_file="${dump_dir}/megacli.err" log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" - dump_cmd="megacli -CfgSave -f ${dump_file} -a0 2> ${error_file}" + dump_cmd="megacli -CfgSave -f ${dump_file} -a0" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + ${dump_cmd} 2> "${error_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -273,15 +339,31 @@ dump_megacli_config() { rm -f "${error_file}" fi log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" + elif command -v perccli > /dev/null; then + local error_file="${errors_dir}/perccli.cfg" + local dump_file="${dump_dir}/perccli.err" + # log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" + + # TODO: find out what the correct command is + + # dump_cmd="perccli XXXX" + # log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" + # ${dump_cmd} 2> ${error_file} + + # local last_rc=$? + # # shellcheck disable=SC2086 + # if [ ${last_rc} -ne 0 ]; then + # log_error "LOCAL_TASKS - ${FUNCNAME[0]}: perccli to ${dump_file} returned an error ${last_rc}" "${error_file}" + # GLOBAL_RC=${E_DUMPFAILED} + # else + # rm -f "${error_file}" + # fi + # log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" else - log "LOCAL_TASKS - ${FUNCNAME[0]}: 'megacli' not found, unable to dump RAID configuration" + log "LOCAL_TASKS - ${FUNCNAME[0]}: 'megacli' and 'perccli' not found, unable to dump RAID configuration" fi } - - - - ####################################################################### # Save some traceroute/mtr results # @@ -427,9 +509,9 @@ dump_rabbitmq() { log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" - dump_cmd="rabbitmqadmin export ${dump_file} 2> ${error_file}" + dump_cmd="rabbitmqadmin export ${dump_file}" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + ${dump_cmd} 2> "${error_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -475,432 +557,3 @@ dump_facl() { log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}" } - -####################################################################### -# Snapshot Elasticsearch data (single-node cluster) -# -# Arguments: -# --protocol=[String] (default: http) -# --host=[String] (default: localhost) -# --port=[Integer] (default: 9200) -# --user=[String] (default: ) -# --password=[String] (default: ) -# --repository=[String] (default: snaprepo) -# --snapshot=[String] (default: snapshot.daily) -####################################################################### -dump_elasticsearch_snapshot_singlenode() { - local option_protocol="http" - local option_host="localhost" - local option_port="9200" - local option_user="" - local option_password="" - local option_repository="snaprepo" - local option_snapshot="snapshot.daily" - - # Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a - while :; do - case ${1:-''} in - --protocol) - # protocol options, with value separated by space - if [ -n "$2" ]; then - option_protocol="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument." - exit 1 - fi - ;; - --protocol=?*) - # protocol options, with value separated by = - option_protocol="${1#*=}" - ;; - --protocol=) - # protocol options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument." - exit 1 - ;; - --host) - # host options, with value separated by space - if [ -n "$2" ]; then - option_host="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument." - exit 1 - fi - ;; - --host=?*) - # host options, with value separated by = - option_host="${1#*=}" - ;; - --host=) - # host options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument." - exit 1 - ;; - --port) - # port options, with value separated by space - if [ -n "$2" ]; then - option_port="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument." - exit 1 - fi - ;; - --port=?*) - # port options, with value separated by = - option_port="${1#*=}" - ;; - --port=) - # port options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument." - exit 1 - ;; - --user) - # user options, with value separated by space - if [ -n "$2" ]; then - option_user="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument." - exit 1 - fi - ;; - --user=?*) - # user options, with value separated by = - option_user="${1#*=}" - ;; - --user=) - # user options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument." - exit 1 - ;; - --password) - # password options, with value separated by space - if [ -n "$2" ]; then - option_password="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument." - exit 1 - fi - ;; - --password=?*) - # password options, with value separated by = - option_password="${1#*=}" - ;; - --password=) - # password options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument." - exit 1 - ;; - --repository) - # repository options, with value separated by space - if [ -n "$2" ]; then - option_repository="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument." - exit 1 - fi - ;; - --repository=?*) - # repository options, with value separated by = - option_repository="${1#*=}" - ;; - --repository=) - # repository options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument." - exit 1 - ;; - --snapshot) - # snapshot options, with value separated by space - if [ -n "$2" ]; then - option_snapshot="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument." - exit 1 - fi - ;; - --snapshot=?*) - # snapshot options, with value separated by = - option_snapshot="${1#*=}" - ;; - --snapshot=) - # snapshot options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument." - exit 1 - ;; - --) - # End of all options. - shift - break - ;; - -?*|[[:alnum:]]*) - # ignore unknown options - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)" - ;; - *) - # Default case: If no more options then break out of the loop. - break - ;; - esac - - shift - done - - log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${option_snapshot}" - - ## Take a snapshot as a backup. - ## Warning: You need to have a path.repo configured. - ## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes - - local base_url="${option_protocol}://${option_host}:${option_port}" - local snapshot_url="${base_url}/_snapshot/${option_repository}/${option_snapshot}" - - if [ -n "${option_user}" ] || [ -n "${option_password}" ]; then - local option_auth="--user ${option_user}:${option_password}" - else - local option_auth="" - fi - - curl -s -XDELETE "${option_auth}" "${snapshot_url}" >> "${LOGFILE}" - curl -s -XPUT "${option_auth}" "${snapshot_url}?wait_for_completion=true" >> "${LOGFILE}" - - # Clustered version here - # It basically the same thing except that you need to check that NFS is mounted - # if ss | grep ':nfs' | grep -q 'ip\.add\.res\.s1' && ss | grep ':nfs' | grep -q 'ip\.add\.res\.s2' - # then - # curl -s -XDELETE "${option_auth}" "${snapshot_url}" >> "${LOGFILE}" - # curl -s -XPUT "${option_auth}" "${snapshot_url}?wait_for_completion=true" >> "${LOGFILE}" - # else - # echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.' - # fi - - log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${option_snapshot}" -} - -####################################################################### -# Snapshot Elasticsearch data (multi-node cluster) -# -# Arguments: -# --protocol=[String] (default: http) -# --host=[String] (default: localhost) -# --port=[Integer] (default: 9200) -# --user=[String] (default: ) -# --password=[String] (default: ) -# --repository=[String] (default: snaprepo) -# --snapshot=[String] (default: snapshot.daily) -# --nfs-server=[IP|HOST] (default: ) -####################################################################### -dump_elasticsearch_snapshot_multinode() { - local option_protocol="http" - local option_host="localhost" - local option_port="9200" - local option_user="" - local option_password="" - local option_repository="snaprepo" - local option_snapshot="snapshot.daily" - local option_nfs_server="" - - # Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a - while :; do - case ${1:-''} in - --protocol) - # protocol options, with value separated by space - if [ -n "$2" ]; then - option_protocol="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument." - exit 1 - fi - ;; - --protocol=?*) - # protocol options, with value separated by = - option_protocol="${1#*=}" - ;; - --protocol=) - # protocol options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument." - exit 1 - ;; - --host) - # host options, with value separated by space - if [ -n "$2" ]; then - option_host="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument." - exit 1 - fi - ;; - --host=?*) - # host options, with value separated by = - option_host="${1#*=}" - ;; - --host=) - # host options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument." - exit 1 - ;; - --port) - # port options, with value separated by space - if [ -n "$2" ]; then - option_port="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument." - exit 1 - fi - ;; - --port=?*) - # port options, with value separated by = - option_port="${1#*=}" - ;; - --port=) - # port options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument." - exit 1 - ;; - --user) - # user options, with value separated by space - if [ -n "$2" ]; then - option_user="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument." - exit 1 - fi - ;; - --user=?*) - # user options, with value separated by = - option_user="${1#*=}" - ;; - --user=) - # user options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument." - exit 1 - ;; - --password) - # password options, with value separated by space - if [ -n "$2" ]; then - option_password="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument." - exit 1 - fi - ;; - --password=?*) - # password options, with value separated by = - option_password="${1#*=}" - ;; - --password=) - # password options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument." - exit 1 - ;; - --repository) - # repository options, with value separated by space - if [ -n "$2" ]; then - option_repository="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument." - exit 1 - fi - ;; - --repository=?*) - # repository options, with value separated by = - option_repository="${1#*=}" - ;; - --repository=) - # repository options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument." - exit 1 - ;; - --snapshot) - # snapshot options, with value separated by space - if [ -n "$2" ]; then - option_snapshot="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument." - exit 1 - fi - ;; - --snapshot=?*) - # snapshot options, with value separated by = - option_snapshot="${1#*=}" - ;; - --snapshot=) - # snapshot options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument." - exit 1 - ;; - --nfs-server) - # nfs-server options, with value separated by space - if [ -n "$2" ]; then - option_nfs_server="${2}" - shift - else - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--nfs-server' requires a non-empty option argument." - exit 1 - fi - ;; - --nfs-server=?*) - # nfs-server options, with value separated by = - option_nfs_server="${1#*=}" - ;; - --nfs-server=) - # nfs-server options, without value - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--nfs-server' requires a non-empty option argument." - exit 1 - ;; - --) - # End of all options. - shift - break - ;; - -?*|[[:alnum:]]*) - # ignore unknown options - log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)" - ;; - *) - # Default case: If no more options then break out of the loop. - break - ;; - esac - - shift - done - - log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${option_snapshot}" - - ## Take a snapshot as a backup. - ## Warning: You need to have a path.repo configured. - ## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes - - local base_url="${option_protocol}://${option_host}:${option_port}" - local snapshot_url="${base_url}/_snapshot/${option_repository}/${option_snapshot}" - - if [ -n "${option_user}" ] || [ -n "${option_password}" ]; then - local option_auth="--user ${option_user}:${option_password}" - else - local option_auth="" - fi - - # Clustered version here - # It basically the same thing except that you need to check that NFS is mounted - if ss | grep ':nfs' | grep -q -F "${option_nfs_server}"; then - curl -s -XDELETE "${option_auth}" "${snapshot_url}" >> "${LOGFILE}" - curl -s -XPUT "${option_auth}" "${snapshot_url}?wait_for_completion=true" >> "${LOGFILE}" - else - echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.' - fi - - log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${option_snapshot}" -} diff --git a/client/lib/dump-mysql.sh b/client/lib/dump/mysql.sh similarity index 98% rename from client/lib/dump-mysql.sh rename to client/lib/dump/mysql.sh index 1f7a153..0d288b4 100644 --- a/client/lib/dump-mysql.sh +++ b/client/lib/dump/mysql.sh @@ -254,9 +254,9 @@ dump_mysql_summary() { options=() options+=(--sleep=0) - dump_cmd="pt-mysql-summary ${options[*]} -- ${connect_options[*]} > ${dump_file}" + dump_cmd="pt-mysql-summary ${options[*]} -- ${connect_options[*]}" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} 2> "${error_file}" + ${dump_cmd} 2> "${error_file}" > "${dump_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -470,9 +470,9 @@ dump_mysql_grants() { options+=(--flush) options+=(--no-header) - dump_cmd="pt-show-grants ${options[*]} > ${dump_file}" + dump_cmd="pt-show-grants ${options[*]}" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} 2> "${error_file}" + ${dump_cmd} 2> "${error_file}" > "${dump_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -806,9 +806,12 @@ dump_mysql_global() { dump_options+=(${option_others}) fi - dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file}| ${compress_cmd} > ${dump_file}" + ## WARNING : logging and executing the command must be separate + ## because otherwise Bash would interpret | and > as strings and not syntax. + + dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file} | ${compress_cmd} > ${dump_file}" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | ${compress_cmd} > "${dump_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -838,9 +841,9 @@ dump_mysql_global() { dump_options+=(${option_others}) fi - dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file} > ${dump_file}" + dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + ${dump_cmd} 2> "${error_file}" > "${dump_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -1165,9 +1168,11 @@ dump_mysql_per_base() { dump_options+=(${option_others}) fi - dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file} | ${compress_cmd} > ${dump_file}" - log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + ## WARNING : logging and executing the command must be separate + ## because otherwise Bash would interpret | and > as strings and not syntax. + + log "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump ${connect_options[*]} ${dump_options[*]} | ${compress_cmd} > ${dump_file}" + mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | ${compress_cmd} > "${dump_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -1197,9 +1202,9 @@ dump_mysql_per_base() { dump_options+=(${option_others}) fi - dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file} > ${dump_file}" + dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + ${dump_cmd} 2> "${error_file}" > "${dump_file}" local last_rc=$? # shellcheck disable=SC2086 @@ -1529,9 +1534,9 @@ dump_mysql_tabs() { fi dump_options+=("${database}") - dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file}" + dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}" log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" - ${dump_cmd} + ${dump_cmd} 2> "${error_file}" local last_rc=$? # shellcheck disable=SC2086 diff --git a/client/lib/dump/postgresql.sh b/client/lib/dump/postgresql.sh new file mode 100644 index 0000000..302942c --- /dev/null +++ b/client/lib/dump/postgresql.sh @@ -0,0 +1,343 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034,SC2317,SC2155 + +####################################################################### +# Dump a single file of all PostgreSQL databases +# +# Arguments: +# --dump-label=[String] (default: "default") +# used as suffix of the dump dir to differenciate multiple instances +# --compress= (default: "gzip") +# Other options after -- are passed as-is to pg_dump +####################################################################### +dump_postgresql_global() { + local option_dump_label="" + local option_compress="" + local option_others="" + + # Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a + while :; do + case ${1:-''} in + --dump-label) + # dump-label options, with value separated by space + if [ -n "$2" ]; then + option_dump_label="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument." + exit 1 + fi + ;; + --dump-label=?*) + # dump-label options, with value separated by = + option_dump_label="${1#*=}" + ;; + --dump-label=) + # dump-label options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument." + exit 1 + ;; + --compress) + # compress options, with value separated by space + if [ -n "$2" ]; then + option_compress="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument." + exit 1 + fi + ;; + --compress=?*) + # compress options, with value separated by = + option_compress="${1#*=}" + ;; + --compress=) + # compress options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument." + exit 1 + ;; + --) + # End of all options. + shift + option_others=${*} + break + ;; + -?*|[[:alnum:]]*) + # ignore unknown options + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)" + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift + done + + case "${option_compress}" in + none) + compress_cmd="cat" + dump_ext="" + ;; + bzip2|bz|bz2) + compress_cmd="bzip2 --best" + dump_ext=".bz" + ;; + xz) + compress_cmd="xz --best" + dump_ext=".xz" + ;; + pigz) + compress_cmd="pigz --best" + dump_ext=".gz" + ;; + gz|gzip|*) + compress_cmd="gzip --best" + dump_ext=".gz" + ;; + esac + + if [ -z "${option_dump_label}" ]; then + if [ -n "${option_defaults_group_suffix}" ]; then + option_dump_label="${option_defaults_group_suffix}" + elif [ -n "${option_port}" ]; then + option_dump_label="${option_port}" + elif [ -n "${option_socket}" ]; then + option_dump_label=$(path_to_str "${option_socket}") + else + option_dump_label="default" + fi + fi + + local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-${option_dump_label}-global" + local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") + rm -rf "${dump_dir}" "${errors_dir}" + mkdir -p "${dump_dir}" "${errors_dir}" + # No need to change recursively, the top directory is enough + chmod 700 "${dump_dir}" "${errors_dir}" + + ## example with pg_dumpall and with compression + local error_file="${errors_dir}/pg_dumpall.err" + local dump_file="${dump_dir}/pg_dumpall.sql${dump_ext}" + log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" + + declare -a dump_options + dump_options=() + if [ -n "${option_others}" ]; then + # word splitting is deliberate here + # shellcheck disable=SC2206 + dump_options+=(${option_others}) + fi + + dump_cmd="(sudo -u postgres pg_dumpall ${dump_options[*]}) 2> ${error_file} | ${compress_cmd} > ${dump_file}" + log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" + ${dump_cmd} + + local last_rc=$? + # shellcheck disable=SC2086 + if [ ${last_rc} -ne 0 ]; then + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dumpall to ${dump_file} returned an error ${last_rc}" "${error_file}" + GLOBAL_RC=${E_DUMPFAILED} + else + rm -f "${error_file}" + fi + + log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" + + ## example with pg_dumpall and without compression + ## WARNING: you need space in ~postgres + # local error_file="${errors_dir}/pg_dumpall.err" + # local dump_file="${dump_dir}/pg_dumpall.sql" + # log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" + # + # (su - postgres -c "pg_dumpall > ~/pg.dump.bak") 2> "${error_file}" + # mv ~postgres/pg.dump.bak "${dump_file}" + # + # log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" +} + +####################################################################### +# Dump a compressed file per database +# +# Arguments: +####################################################################### +dump_postgresql_per_base() { + local option_dump_label="" + local option_compress="" + local option_others="" + + # Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a + while :; do + case ${1:-''} in + --dump-label) + # dump-label options, with value separated by space + if [ -n "$2" ]; then + option_dump_label="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument." + exit 1 + fi + ;; + --dump-label=?*) + # dump-label options, with value separated by = + option_dump_label="${1#*=}" + ;; + --dump-label=) + # dump-label options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument." + exit 1 + ;; + --compress) + # compress options, with value separated by space + if [ -n "$2" ]; then + option_compress="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument." + exit 1 + fi + ;; + --compress=?*) + # compress options, with value separated by = + option_compress="${1#*=}" + ;; + --compress=) + # compress options, without value + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument." + exit 1 + ;; + --) + # End of all options. + shift + option_others=${*} + break + ;; + -?*|[[:alnum:]]*) + # ignore unknown options + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)" + ;; + *) + # Default case: If no more options then break out of the loop. + break + ;; + esac + + shift + done + + case "${option_compress}" in + none) + compress_cmd="cat" + dump_ext="" + ;; + bzip2|bz|bz2) + compress_cmd="bzip2 --best" + dump_ext=".bz" + ;; + xz) + compress_cmd="xz --best" + dump_ext=".xz" + ;; + pigz) + compress_cmd="pigz --best" + dump_ext=".gz" + ;; + gz|gzip|*) + compress_cmd="gzip --best" + dump_ext=".gz" + ;; + esac + + if [ -z "${option_dump_label}" ]; then + if [ -n "${option_defaults_group_suffix}" ]; then + option_dump_label="${option_defaults_group_suffix}" + elif [ -n "${option_port}" ]; then + option_dump_label="${option_port}" + elif [ -n "${option_socket}" ]; then + option_dump_label=$(path_to_str "${option_socket}") + else + option_dump_label="default" + fi + fi + + local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-${option_dump_label}-per-base" + local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") + rm -rf "${dump_dir}" "${errors_dir}" + mkdir -p "${dump_dir}" "${errors_dir}" + # No need to change recursively, the top directory is enough + chmod 700 "${dump_dir}" "${errors_dir}" + + ( + # shellcheck disable=SC2164 + cd /var/lib/postgresql + databases=$(sudo -u postgres psql -U postgres -lt | awk -F \| '{print $1}' | grep -v "template.*") + for database in ${databases} ; do + local error_file="${errors_dir}/${database}.err" + local dump_file="${dump_dir}/${database}.sql${dump_ext}" + log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" + + declare -a dump_options + dump_options=() + dump_options+=(--create) + dump_options+=(-U postgres) + dump_options+=(-d "${database}") + if [ -n "${option_others}" ]; then + # word splitting is deliberate here + # shellcheck disable=SC2206 + dump_options+=(${option_others}) + fi + + dump_cmd="(sudo -u postgres /usr/bin/pg_dump ${dump_options[*]}) 2> ${error_file} | ${compress_cmd} > ${dump_file}" + log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}" + ${dump_cmd} + + local last_rc=$? + # shellcheck disable=SC2086 + if [ ${last_rc} -ne 0 ]; then + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}" + GLOBAL_RC=${E_DUMPFAILED} + else + rm -f "${error_file}" + fi + log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" + done + ) +} + +####################################################################### +# Dump a compressed file per database +# +# Arguments: +# +# TODO: add arguments to include/exclude tables +####################################################################### +dump_postgresql_filtered() { + local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-filtered" + local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") + rm -rf "${dump_dir}" "${errors_dir}" + mkdir -p "${dump_dir}" "${errors_dir}" + # No need to change recursively, the top directory is enough + chmod 700 "${dump_dir}" "${errors_dir}" + + local error_file="${errors_dir}/pg-backup.err" + local dump_file="${dump_dir}/pg-backup.tar" + log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" + + ## example with all tables from MYBASE excepts TABLE1 and TABLE2 + # pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -t 'TABLE1' -t 'TABLE2' MYBASE 2> "${error_file}" + + ## example with only TABLE1 and TABLE2 from MYBASE + # pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -T 'TABLE1' -T 'TABLE2' MYBASE 2> "${error_file}" + + local last_rc=$? + # shellcheck disable=SC2086 + if [ ${last_rc} -ne 0 ]; then + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}" + GLOBAL_RC=${E_DUMPFAILED} + else + rm -f "${error_file}" + fi + log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" +} diff --git a/client/lib/main.sh b/client/lib/main.sh index e1d1ecc..021fa9d 100644 --- a/client/lib/main.sh +++ b/client/lib/main.sh @@ -18,9 +18,7 @@ if [[ "${TRACE-0}" == "1" ]]; then fi source "${LIBDIR}/utilities.sh" -source "${LIBDIR}/dump-mysql.sh" -source "${LIBDIR}/dump-postgresql.sh" -source "${LIBDIR}/dump-misc.sh" +source "${LIBDIR}"/dump/*.sh # Called from main, it is wrapping the local_tasks function defined in the real script local_tasks_wrapper() {