openstack-helm-infra/postgresql/templates/bin/_restore_postgresql.sh.tpl
Parsons, Cliff (cp769u) 233197fc0b Add capabilitity to backup only a single database
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
2020-08-18 18:30:31 +00:00

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[@]}