233197fc0b
This PS adds the capability to Mariadb and Postgresql to backup a single database (as an optional parameter to the backup script). Change-Id: I9bc1eb0173063638b2cf58465c063f602ed20bc1
363 lines
12 KiB
Smarty
Executable File
363 lines
12 KiB
Smarty
Executable File
#!/bin/bash
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
# Capture the user's command line arguments
|
|
ARGS=("$@")
|
|
|
|
# This is needed to get the postgresql admin password
|
|
# Note: xtracing should be off so it doesn't print the pw
|
|
export PGPASSWORD=$(cat /etc/postgresql/admin_user.conf \
|
|
| grep postgres | awk -F: '{print $5}')
|
|
|
|
source /tmp/restore_main.sh
|
|
|
|
# Export the variables needed by the framework
|
|
export DB_NAME="postgres"
|
|
export DB_NAMESPACE=${POSTGRESQL_POD_NAMESPACE}
|
|
export ARCHIVE_DIR=${POSTGRESQL_BACKUP_BASE_DIR}/db/${DB_NAMESPACE}/${DB_NAME}/archive
|
|
|
|
# Define variables needed in this file
|
|
POSTGRESQL_HOST=$(cat /etc/postgresql/admin_user.conf | cut -d: -f 1)
|
|
export PSQL="psql -U $POSTGRESQL_ADMIN_USER -h $POSTGRESQL_HOST"
|
|
export LOG_FILE=/tmp/dbrestore.log
|
|
|
|
# Extract all databases from an archive and put them in the requested
|
|
# file.
|
|
get_databases() {
|
|
TMP_DIR=$1
|
|
DB_FILE=$2
|
|
|
|
SQL_FILE=$TMP_DIR/postgres.$POSTGRESQL_POD_NAMESPACE.*.sql
|
|
if [ -f $SQL_FILE ]; then
|
|
grep 'CREATE DATABASE' $SQL_FILE | awk '{ print $3 }' > $DB_FILE
|
|
else
|
|
# Error, cannot report the databases
|
|
echo "No SQL file found - cannot extract the databases"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Extract all tables of a database from an archive and put them in the requested
|
|
# file.
|
|
get_tables() {
|
|
DATABASE=$1
|
|
TMP_DIR=$2
|
|
TABLE_FILE=$3
|
|
|
|
SQL_FILE=$TMP_DIR/postgres.$POSTGRESQL_POD_NAMESPACE.*.sql
|
|
if [ -f $SQL_FILE ]; then
|
|
cat $SQL_FILE | sed -n /'\\connect '$DATABASE/,/'\\connect'/p | grep "CREATE TABLE" | awk -F'[. ]' '{print $4}' > $TABLE_FILE
|
|
else
|
|
# Error, cannot report the tables
|
|
echo "No SQL file found - cannot extract the tables"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Extract all rows in the given table of a database from an archive and put them in the requested
|
|
# file.
|
|
get_rows() {
|
|
DATABASE=$1
|
|
TABLE=$2
|
|
TMP_DIR=$3
|
|
ROW_FILE=$4
|
|
|
|
SQL_FILE=$TMP_DIR/postgres.$POSTGRESQL_POD_NAMESPACE.*.sql
|
|
if [ -f $SQL_FILE ]; then
|
|
cat $SQL_FILE | sed -n /'\\connect '${DATABASE}/,/'\\connect'/p > /tmp/db.sql
|
|
cat /tmp/db.sql | grep "INSERT INTO public.${TABLE} VALUES" > $ROW_FILE
|
|
rm /tmp/db.sql
|
|
else
|
|
# Error, cannot report the rows
|
|
echo "No SQL file found - cannot extract the rows"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Extract the schema for the given table in the given database belonging to the archive file
|
|
# found in the TMP_DIR.
|
|
get_schema() {
|
|
DATABASE=$1
|
|
TABLE=$2
|
|
TMP_DIR=$3
|
|
SCHEMA_FILE=$4
|
|
|
|
SQL_FILE=$TMP_DIR/postgres.$POSTGRESQL_POD_NAMESPACE.*.sql
|
|
if [ -f $SQL_FILE ]; then
|
|
DB_FILE=$(mktemp -p /tmp)
|
|
cat $SQL_FILE | sed -n /'\\connect '${DATABASE}/,/'\\connect'/p > ${DB_FILE}
|
|
cat ${DB_FILE} | sed -n /'CREATE TABLE public.'${TABLE}' ('/,/'--'/p > ${SCHEMA_FILE}
|
|
cat ${DB_FILE} | sed -n /'CREATE SEQUENCE public.'${TABLE}/,/'--'/p >> ${SCHEMA_FILE}
|
|
cat ${DB_FILE} | sed -n /'ALTER TABLE public.'${TABLE}/,/'--'/p >> ${SCHEMA_FILE}
|
|
cat ${DB_FILE} | sed -n /'ALTER TABLE ONLY public.'${TABLE}/,/'--'/p >> ${SCHEMA_FILE}
|
|
cat ${DB_FILE} | sed -n /'ALTER SEQUENCE public.'${TABLE}/,/'--'/p >> ${SCHEMA_FILE}
|
|
cat ${DB_FILE} | sed -n /'SELECT pg_catalog.*public.'${TABLE}/,/'--'/p >> ${SCHEMA_FILE}
|
|
cat ${DB_FILE} | sed -n /'CREATE INDEX.*public.'${TABLE}' USING'/,/'--'/p >> ${SCHEMA_FILE}
|
|
cat ${DB_FILE} | sed -n /'GRANT.*public.'${TABLE}' TO'/,/'--'/p >> ${SCHEMA_FILE}
|
|
rm -f ${DB_FILE}
|
|
else
|
|
# Error, cannot report the rows
|
|
echo "No SQL file found - cannot extract the schema"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Extract Single Database SQL Dump from pg_dumpall dump file
|
|
extract_single_db_dump() {
|
|
ARCHIVE=$1
|
|
DATABASE=$2
|
|
DIR=$3
|
|
sed -n '/\\connect'" ${DATABASE}/,/PostgreSQL database dump complete/p" ${ARCHIVE} > ${DIR}/${DATABASE}.sql
|
|
}
|
|
|
|
# Re-enable connections to a database
|
|
reenable_connections() {
|
|
SINGLE_DB_NAME=$1
|
|
|
|
# First make sure this is not the main postgres database or either of the
|
|
# two template databases that should not be touched.
|
|
if [[ ${SINGLE_DB_NAME} == "postgres" ||
|
|
${SINGLE_DB_NAME} == "template0" ||
|
|
${SINGLE_DB_NAME} == "template1" ]]; then
|
|
echo "Cannot re-enable connections on an postgres internal db ${SINGLE_DB_NAME}"
|
|
return 1
|
|
fi
|
|
|
|
# Re-enable connections to the DB
|
|
$PSQL -tc "UPDATE pg_database SET datallowconn = 'true' WHERE datname = '${SINGLE_DB_NAME}';" > /dev/null 2>&1
|
|
if [[ "$?" -ne 0 ]]; then
|
|
echo "Could not re-enable connections for database ${SINGLE_DB_NAME}"
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Drop connections from a database
|
|
drop_connections() {
|
|
SINGLE_DB_NAME=$1
|
|
|
|
# First make sure this is not the main postgres database or either of the
|
|
# two template databases that should not be touched.
|
|
if [[ ${SINGLE_DB_NAME} == "postgres" ||
|
|
${SINGLE_DB_NAME} == "template0" ||
|
|
${SINGLE_DB_NAME} == "template1" ]]; then
|
|
echo "Cannot drop connections on an postgres internal db ${SINGLE_DB_NAME}"
|
|
return 1
|
|
fi
|
|
|
|
# First, prevent any new connections from happening on this database.
|
|
$PSQL -tc "UPDATE pg_database SET datallowconn = 'false' WHERE datname = '${SINGLE_DB_NAME}';" > /dev/null 2>&1
|
|
if [[ "$?" -ne 0 ]]; then
|
|
echo "Could not prevent new connections before restoring database ${SINGLE_DB_NAME}."
|
|
return 1
|
|
fi
|
|
|
|
# Next, force disconnection of all clients currently connected to this database.
|
|
$PSQL -tc "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${SINGLE_DB_NAME}';" > /dev/null 2>&1
|
|
if [[ "$?" -ne 0 ]]; then
|
|
echo "Could not drop existing connections before restoring database ${SINGLE_DB_NAME}."
|
|
reenable_connections ${SINGLE_DB_NAME}
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Re-enable connections for all of the databases within Postgresql
|
|
reenable_connections_on_all_dbs() {
|
|
# Get a list of the databases
|
|
DB_LIST=$($PSQL -tc "\l" | grep "| postgres |" | awk '{print $1}')
|
|
|
|
RET=0
|
|
|
|
# Re-enable the connections for each of the databases.
|
|
for DB in $DB_LIST; do
|
|
if [[ ${DB} != "postgres" && ${DB} != "template0" && ${DB} != "template1" ]]; then
|
|
reenable_connections $DB
|
|
if [[ "$?" -ne 0 ]]; then
|
|
RET=1
|
|
fi
|
|
fi
|
|
done
|
|
|
|
return $RET
|
|
}
|
|
|
|
# Drop connections in all of the databases within Postgresql
|
|
drop_connections_on_all_dbs() {
|
|
# Get a list of the databases
|
|
DB_LIST=$($PSQL -tc "\l" | grep "| postgres |" | awk '{print $1}')
|
|
|
|
RET=0
|
|
|
|
# Drop the connections for each of the databases.
|
|
for DB in $DB_LIST; do
|
|
# Make sure this is not the main postgres database or either of the
|
|
# two template databases that should not be touched.
|
|
if [[ ${DB} != "postgres" && ${DB} != "template0" && ${DB} != "template1" ]]; then
|
|
drop_connections $DB
|
|
if [[ "$?" -ne 0 ]]; then
|
|
RET=1
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# If there was a failure to drop any connections, go ahead and re-enable
|
|
# them all to prevent a lock-out condition
|
|
if [[ $RET -ne 0 ]]; then
|
|
reenable_connections_on_all_dbs
|
|
fi
|
|
|
|
return $RET
|
|
}
|
|
|
|
# Restore a single database dump from pg_dumpall sql dumpfile.
|
|
restore_single_db() {
|
|
SINGLE_DB_NAME=$1
|
|
TMP_DIR=$2
|
|
|
|
# Reset the logfile incase there was some older log there
|
|
rm -rf ${LOG_FILE}
|
|
touch ${LOG_FILE}
|
|
|
|
# First make sure this is not the main postgres database or either of the
|
|
# two template databases that should not be touched.
|
|
if [[ ${SINGLE_DB_NAME} == "postgres" ||
|
|
${SINGLE_DB_NAME} == "template0" ||
|
|
${SINGLE_DB_NAME} == "template1" ]]; then
|
|
echo "Cannot restore an postgres internal db ${SINGLE_DB_NAME}"
|
|
return 1
|
|
fi
|
|
|
|
SQL_FILE=$TMP_DIR/postgres.$POSTGRESQL_POD_NAMESPACE.*.sql
|
|
if [ -f $SQL_FILE ]; then
|
|
extract_single_db_dump $SQL_FILE $SINGLE_DB_NAME $TMP_DIR
|
|
if [[ -f $TMP_DIR/$SINGLE_DB_NAME.sql && -s $TMP_DIR/$SINGLE_DB_NAME.sql ]]; then
|
|
# Drop connections first
|
|
drop_connections ${SINGLE_DB_NAME}
|
|
if [[ "$?" -ne 0 ]]; then
|
|
return 1
|
|
fi
|
|
|
|
# Next, drop the database
|
|
$PSQL -tc "DROP DATABASE $SINGLE_DB_NAME;"
|
|
if [[ "$?" -ne 0 ]]; then
|
|
echo "Could not drop the old ${SINGLE_DB_NAME} database before restoring it."
|
|
reenable_connections ${SINGLE_DB_NAME}
|
|
return 1
|
|
fi
|
|
|
|
# Postgresql does not have the concept of creating database if condition.
|
|
# This next command creates the database in case it does not exist.
|
|
$PSQL -tc "SELECT 1 FROM pg_database WHERE datname = '$SINGLE_DB_NAME'" | grep -q 1 || \
|
|
$PSQL -c "CREATE DATABASE $SINGLE_DB_NAME"
|
|
if [[ "$?" -ne 0 ]]; then
|
|
echo "Could not create the single database being restored: ${SINGLE_DB_NAME}."
|
|
reenable_connections ${SINGLE_DB_NAME}
|
|
return 1
|
|
fi
|
|
$PSQL -d $SINGLE_DB_NAME -f ${TMP_DIR}/${SINGLE_DB_NAME}.sql 2>>$LOG_FILE >> $LOG_FILE
|
|
if [[ "$?" -eq 0 ]]; then
|
|
if grep "ERROR:" ${LOG_FILE} > /dev/null 2>&1; then
|
|
cat $LOG_FILE
|
|
echo "Errors occurred during the restore of database ${SINGLE_DB_NAME}"
|
|
reenable_connections ${SINGLE_DB_NAME}
|
|
return 1
|
|
else
|
|
echo "Database restore Successful."
|
|
fi
|
|
else
|
|
# Dump out the log file for debugging
|
|
cat $LOG_FILE
|
|
echo -e "\nDatabase restore Failed."
|
|
reenable_connections ${SINGLE_DB_NAME}
|
|
return 1
|
|
fi
|
|
|
|
# Re-enable connections to the DB
|
|
reenable_connections ${SINGLE_DB_NAME}
|
|
if [[ "$?" -ne 0 ]]; then
|
|
return 1
|
|
fi
|
|
else
|
|
echo "Database dump For $SINGLE_DB_NAME is empty or not available."
|
|
return 1
|
|
fi
|
|
else
|
|
echo "No database file available to restore from."
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Restore all the databases from the pg_dumpall sql file.
|
|
restore_all_dbs() {
|
|
TMP_DIR=$1
|
|
|
|
# Reset the logfile incase there was some older log there
|
|
rm -rf ${LOG_FILE}
|
|
touch ${LOG_FILE}
|
|
|
|
SQL_FILE=$TMP_DIR/postgres.$POSTGRESQL_POD_NAMESPACE.*.sql
|
|
if [ -f $SQL_FILE ]; then
|
|
|
|
# Check the scope of the archive.
|
|
SCOPE=$(echo ${SQL_FILE} | awk -F'.' '{print $(NF-1)}')
|
|
if [[ "${SCOPE}" != "all" ]]; then
|
|
# This is just a single database backup. The user should
|
|
# instead use the single database restore option.
|
|
echo "Cannot use the restore all option for an archive containing only a single database."
|
|
echo "Please use the single database restore option."
|
|
return 1
|
|
fi
|
|
|
|
# First drop all connections on all databases
|
|
drop_connections_on_all_dbs
|
|
if [[ "$?" -ne 0 ]]; then
|
|
return 1
|
|
fi
|
|
|
|
$PSQL postgres -f $SQL_FILE 2>>$LOG_FILE >> $LOG_FILE
|
|
if [[ "$?" -eq 0 ]]; then
|
|
if grep "ERROR:" ${LOG_FILE} > /dev/null 2>&1; then
|
|
cat ${LOG_FILE}
|
|
echo "Errors occurred during the restore of the databases."
|
|
reenable_connections_on_all_dbs
|
|
return 1
|
|
else
|
|
echo "Database Restore Successful."
|
|
fi
|
|
else
|
|
# Dump out the log file for debugging
|
|
cat ${LOG_FILE}
|
|
echo -e "\nDatabase Restore failed."
|
|
reenable_connections_on_all_dbs
|
|
return 1
|
|
fi
|
|
|
|
# Re-enable connections on all databases
|
|
reenable_connections_on_all_dbs
|
|
if [[ "$?" -ne 0 ]]; then
|
|
return 1
|
|
fi
|
|
else
|
|
echo "There is no database file available to restore from."
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# Call the CLI interpreter, providing the archive directory path and the
|
|
# user arguments passed in
|
|
cli_main ${ARGS[@]}
|