Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
da76837
fix(import): reapply triggers from schema on import
JeGoi Jun 30, 2026
a7163f3
test(venom): verify DB triggers survive restore in backup_db_and_restore
JeGoi Jun 30, 2026
650a45e
fix(backup): export only triggers, not routines or events
JeGoi Jul 6, 2026
dd52277
fix(import): reapply DB triggers on every import and honor --restore-…
JeGoi Jul 6, 2026
9b0000c
feat(import): replace --db-restore with non-interactive --restore-as-is
JeGoi Jul 6, 2026
a60075c
docs(import): document import.sh options
JeGoi Jul 6, 2026
e5c183b
test(venom): cover --restore-as-is db/conf/full restores
JeGoi Jul 6, 2026
be45534
test(venom): restart PacketFence after import so API is reachable
JeGoi Jul 6, 2026
75c9320
feat(import): adjust restored cluster.conf for the local node
JeGoi Jul 6, 2026
f244116
docs(import): correct restored-files list and cluster.conf handling
JeGoi Jul 6, 2026
923a2ab
feat(import): keep cluster.conf empty by default, add --update-cluste…
JeGoi Jul 6, 2026
d68a7af
docs(import): document cluster.conf default and --update-cluster-conf
JeGoi Jul 6, 2026
fc4aa36
Potential fix for pull request finding
JeGoi Jul 7, 2026
b9f676a
Potential fix for pull request finding
JeGoi Jul 7, 2026
5abaca1
Potential fix for pull request finding
JeGoi Jul 7, 2026
71b3081
fix(import): export excluded tables' structure so trigger targets exi…
JeGoi Jul 7, 2026
beb58a3
fix(import): recreate excluded tables from schema for old backups
JeGoi Jul 7, 2026
e3ce7fb
test(venom): verify all schema tables and excluded table present afte…
JeGoi Jul 7, 2026
6dbdda7
Potential fix for pull request finding
satkunas Jul 7, 2026
0167d0a
Potential fix for pull request finding
satkunas Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions addons/full-import/export.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,17 @@ if echo "$last_db_dump" | grep '\.sql.gz$' >/dev/null; then
mariadb_args="$mariadb_args -p$mariadb_root_pass"
fi

db_name=$(/usr/local/pf/bin/get_pf_conf database db)
db_name=${db_name:-pf}

echo "Database dump uses mysqldump. Exporting the grants from the database. Enter the MariaDB root password if prompted to"
mysql $mariadb_args --skip-column-names -A -e"SELECT CONCAT('SHOW GRANTS FOR ''',user,'''@''',host,''';') FROM mysql.user WHERE user<>'' AND user<>'PUBLIC'" | mysql $mariadb_args --skip-column-names -A | sed 's/$/;/g' > grants.sql

# pf-user dump omits triggers (no TRIGGER privilege); dump them as root.
# Routines are skipped: the base pf dump already carries them.
echo "Exporting the triggers from the database"
mysqldump $mariadb_args --single-transaction --no-create-info --no-data --skip-add-drop-table \
--triggers "$db_name" > triggers.sql
fi

main_splitter
Expand Down
95 changes: 47 additions & 48 deletions addons/full-import/import.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ import_db() {
if echo "$db_dump" | grep '\.sql$' >/dev/null; then
echo "The database dump uses mysqldump"
#TODO /tmp/grants.sql should be included in the export
import_mysqldump grants.sql $db_dump usr/local/pf/conf/pf.conf $do_db_restore
import_mysqldump grants.sql $db_dump usr/local/pf/conf/pf.conf
elif echo "$db_dump" | grep '\.xbstream$' >/dev/null; then
echo "The database uses mariabackup"
# permit to remove mariabackup if everything goes well
Expand All @@ -96,7 +96,10 @@ import_db() {
exit 1
fi

if [ "$do_db_restore" -eq 0 ] ; then
if [ "$do_restore_as_is" -eq 1 ]; then
main_splitter
echo "--restore-as-is: same version, skipping database upgrade"
else
handle_devel_upgrade `egrep -o '[0-9]+\.[0-9]+\.[0-9]+$' /usr/local/pf/conf/pf-release | egrep -o '^[0-9]+\.[0-9]+'`

#TODO: check the version of the export, we want to support only 10.3.0 and above
Expand All @@ -105,9 +108,6 @@ import_db() {
main_splitter
db_name=`get_db_name usr/local/pf/conf/pf.conf`
upgrade_database $db_name
else
main_splitter
echo "Only database restoration has been selected. No upgrade on database will be done"
fi
}

Expand All @@ -122,10 +122,14 @@ import_config() {
restore_profile_templates

main_splitter
upgrade_imported_configuration
if [ "$do_restore_as_is" -eq 1 ]; then
echo "--restore-as-is: same version, skipping configuration upgrade"
else
upgrade_imported_configuration
fi

main_splitter
if [ "$do_adjust_config" -eq 1 ]; then
if [ "$do_adjust_db_conf" -eq 1 ]; then
echo "Performing adjustments on the configuration"
adjust_configuration
else
Expand All @@ -139,42 +143,31 @@ import_config() {


finalize_import() {
if [ "$do_db_restore" -eq 0 ] ; then
main_splitter
echo "Finalizing import"
main_splitter
echo "Finalizing import"

sub_splitter
echo "Applying fixpermissions"
/usr/local/pf/bin/pfcmd fixpermissions
sub_splitter
echo "Applying fixpermissions"
/usr/local/pf/bin/pfcmd fixpermissions

sub_splitter
echo "Restarting packetfence-redis-cache"
systemctl restart packetfence-redis-cache
sub_splitter
echo "Restarting packetfence-redis-cache"
systemctl restart packetfence-redis-cache

sub_splitter
echo "Restarting packetfence-config"
systemctl restart packetfence-config
sub_splitter
echo "Restarting packetfence-config"
systemctl restart packetfence-config

sub_splitter
echo "Reloading configuration"
configreload
sub_splitter
echo "Reloading configuration"
configreload

main_splitter
echo "Completed import of the database and the configuration! Complete any necessary adjustments and restart PacketFence"
main_splitter
echo "Completed import of the database and the configuration! Complete any necessary adjustments and restart PacketFence"
Comment thread
JeGoi marked this conversation as resolved.
Outdated

# Done with everything, time to cleanup!
systemctl cat monit > /dev/null 2>&1 && systemctl enable monit

# Done with everything, time to cleanup!
systemctl cat monit > /dev/null 2>&1 && systemctl enable monit
else
main_splitter
echo "Finalizing db restoration"

sub_splitter
echo "Restarting service packetfence-httpd.admin_dispatcher"
systemctl restart packetfence-httpd.admin_dispatcher
echo "Restarting packetfence-haproxy-admin service"
systemctl start packetfence-haproxy-admin
echo "Completed import of the database! Complete any necessary adjustments and restart PacketFence"
fi
popd > /dev/null
}

Expand All @@ -188,9 +181,9 @@ Options:
-f,--file Import a PacketFence export (mandatory)
-h,--help Display this help
--db Import only database from PacketFence export
--db-restore Restore only database from PacketFence export without upgrade process
--conf Import only configuration from PacketFence export
--skip-adjust-conf Don't run adjustments on configuration (only use it if you know what you are doing)
--skip-adjust-db-conf Config import only: keep the backup's DB host/port instead of forcing localhost
--restore-as-is Reapply the backup exactly: same version (no upgrade), keep its DB host/port, no prompts

EOF
}
Expand All @@ -200,16 +193,16 @@ EOF
#############################################################################
do_full_import=1
do_db_import=0
do_db_restore=0
do_config_import=0
do_adjust_config=1
do_adjust_db_conf=1
do_restore_as_is=0
mariabackup_installed=false
EXPORT_FILE=${EXPORT_FILE:-}

# Parse option
# TEMP=$(getopt -o f:h --long file:,help,db,conf \
# -n "$0" -- "$@") || (echo "getopt failed." && exit 1)
TEMP=$(getopt -o f:h --long file:,help,db,db-restore,conf,skip-adjust-conf \
TEMP=$(getopt -o f:h --long file:,help,db,conf,skip-adjust-db-conf,restore-as-is \
-n "$0" -- "$@") || (echo "getopt failed." && exit 1)

# Note the quotes around `$TEMP': they are essential!
Expand All @@ -226,16 +219,16 @@ while true ; do
help ; exit 0 ; shift
;;
--db)
do_db_restore=0 ; do_db_import=1 ; do_full_import=0 ; shift
;;
--db-restore)
do_db_restore=1 ; do_db_import=1 ; do_full_import=0 ; shift
do_db_import=1 ; do_full_import=0 ; shift
;;
--conf)
do_config_import=1 ; do_full_import=0 ; shift
;;
--skip-adjust-conf)
do_adjust_config=0 ; shift
--skip-adjust-db-conf)
do_adjust_db_conf=0 ; shift
;;
--restore-as-is)
do_restore_as_is=1 ; shift
;;
--)
shift ; break
Expand All @@ -257,6 +250,12 @@ if [ ! -f "$EXPORT_FILE" ]; then
exit 1
fi

# --restore-as-is reapplies the backup verbatim, so it also keeps the backup's
# DB host/port (skips adjust_configuration) like --skip-adjust-db-conf does.
if [ "$do_restore_as_is" -eq 1 ]; then
do_adjust_db_conf=0
fi

#############################################################################
### Import process
#############################################################################
Expand Down
5 changes: 5 additions & 0 deletions addons/functions/configuration.functions
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ function handle_network_change() {
handle_interface_exists $interface
check_code $?
else
# --restore-as-is: don't prompt to remap/delete; leave the interface untouched.
if [ "${do_restore_as_is:-0}" = 1 ]; then
echo "--restore-as-is: leaving $interface untouched in pf.conf"
continue
fi
stop_it=""
while [ -z "$stop_it" ]; do
sub_splitter
Expand Down
36 changes: 31 additions & 5 deletions addons/functions/database.functions
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ function import_mysqldump() {

mariadb_args="--defaults-file=`pwd`/setup_mycnf --socket=/var/lib/mysql/mysql.sock"
# we need a grant dump generated via: mysql ${MYSQL_CONN} --skip-column-names -A -e"SELECT CONCAT('SHOW GRANTS FOR ''',user,'''@''',host,''';') FROM mysql.user WHERE user<>''" | mysql ${MYSQL_CONN} --skip-column-names -A | sed 's/$/;/g' > grants.sql
if ! test_db_connection_no_creds; then
# --restore-as-is: never prompt; rely on socket auth (fails later with a clear error if unavailable).
if ! test_db_connection_no_creds && [ "${do_restore_as_is:-0}" != 1 ]; then
echo -n "Please enter the root password for MariaDB:"
read -s mariadb_root_pass
echo
Expand All @@ -48,7 +49,7 @@ function import_mysqldump() {
mysql ${mariadb_args} -e "drop database if exists $db_name; create database $db_name"

sub_splitter
# We reimport the schema so that we have the functions and triggers if the dump doesn't contain the triggers
# Detect a trigger-less dump (a pf-user mysqldump omits triggers); reapplied after import below.
if ! egrep "CREATE.*TRIGGER.*(AFTER|BEFORE)" $dump_file > /dev/null; then
echo "Dump file was made without triggers and procedures"
Comment thread
Copilot marked this conversation as resolved.
Outdated

Expand All @@ -66,15 +67,35 @@ function import_mysqldump() {
sed -i 's/DELETE IGNORE FROM `action`;//g' $dump_file
sed -i 's/DELETE IGNORE FROM `activation`;//g' $dump_file

dump_without_triggers=1
else
echo "Dump file includes triggers and procedures"
dump_without_triggers=0
Comment thread
Copilot marked this conversation as resolved.
fi

sub_splitter
echo "Importing $dump_file into $db_name"
mysql ${mariadb_args} $db_name < $dump_file
check_code $?

# pf dump omits triggers: reapply from triggers.sql (new exports) or the matching schema
# (old exports). Idempotent, so it is safe before upgrade_database patches changed triggers.
if [ "$dump_without_triggers" = 1 ]; then
sub_splitter
if [ -f triggers.sql ]; then
echo "Applying triggers from export (triggers.sql)"
mysql ${mariadb_args} $db_name < triggers.sql
check_code $?
else
schema_version=`echo "$pf_version_in_export" | egrep -o '^[0-9]+\.[0-9]+'`
schema_file="/usr/local/pf/db/pf-schema-$schema_version.sql"
[ -f "$schema_file" ] || schema_file="/usr/local/pf/db/pf-schema-X.Y.sql"
echo "triggers.sql not found in export, reapplying triggers from $schema_file"
awk '/^DELIMITER \//{b=$0 ORS;f=1;t=0;next} f&&/^DELIMITER ;/{b=b $0 ORS;if(t)printf "%s",b;f=0;next} f{b=b $0 ORS;if(/CREATE TRIGGER/)t=1}' "$schema_file" | mysql ${mariadb_args} $db_name
check_code $?
fi
fi

sub_splitter
echo "Importing grants"
mysql ${mariadb_args} < $grants_file
Expand Down Expand Up @@ -124,9 +145,14 @@ function import_mariabackup() {
check_code $?

sub_splitter
echo "Performing upgrade of MariaDB. Enter the MariaDB root password if prompted to"
echo "NOTE: The root password you must enter is the one of the server of which you're reimporting the data, not this server's MariaDB root password"
mysql_upgrade -p
# --restore-as-is: same version, no MariaDB engine upgrade.
if [ "${do_restore_as_is:-0}" = 1 ]; then
echo "--restore-as-is: same version, skipping MariaDB upgrade"
else
echo "Performing upgrade of MariaDB. Enter the MariaDB root password if prompted to"
echo "NOTE: The root password you must enter is the one of the server of which you're reimporting the data, not this server's MariaDB root password"
mysql_upgrade -p
fi

popd
}
Expand Down
5 changes: 5 additions & 0 deletions addons/functions/helpers.functions
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ function check_code() {

function prompt() {
msg="$1"
# --restore-as-is: don't ask, keep things as they are (answer "no").
if [ "${do_restore_as_is:-0}" = 1 ]; then
echo "$msg (y/n): n [--restore-as-is]"
return 1
fi
answer=""
while [ "$answer" != "y" ] && [ "$answer" != "n" ]; do
echo -n "$msg (y/n): "
Expand Down
17 changes: 17 additions & 0 deletions docs/installation/export_import_mechanism.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,23 @@ If any issues are experienced during import, run it again.

If all goes well, restart services using <<PacketFence_Upgrade_Guide.asciidoc#_restart_packetfence_services,following instructions>>.

===== Import options

Without any option, `import.sh` performs a full import (database and configuration)
and upgrades them to the running version when the export comes from an older one.

`-f, --file <archive>`:: Path to the export archive to import (mandatory).
`--db`:: Import only the database.
`--conf`:: Import only the configuration.
`--restore-as-is`:: Restore the export exactly as it was backed up. Intended for
restoring onto the *same* PacketFence version: no database, MariaDB or
configuration upgrade is performed, the backup's database host and port are kept,
and no question is asked (fully non-interactive).
`--skip-adjust-db-conf`:: On a configuration import, keep the backup's database
host and port instead of rewriting them to `localhost`. Useful on a cross-version
import that must preserve a non-local database host. (`--restore-as-is` already
implies this.)

===== Additional steps to build or rebuild a cluster

To build or rebuild a cluster, follow instructions in <<PacketFence_Clustering_Guide.asciidoc#_cluster_setup,Cluster setup section>>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@ testcases:
"Content-Type": "application/json"
assertions:
- result.statuscode ShouldEqual 201

# Seed a marker in a restored config location so later tests can prove the config is restored.
- name: create_config_restore_marker
steps:
- type: exec
script: echo 'venom-config-restore-marker' > {{.backup_db_and_restore.conf_marker}}
assertions:
- result.code ShouldEqual 0
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ testcases:
- type: exec
script: /usr/local/pf/addons/exportable-backup.sh

# The export must be self-contained: schema + data (db dump), triggers/routines
# and grants are each packaged into the archive alongside each other.
- name: export_archive_contains_all_objects
steps:
- type: exec
script: |
tgz=$(find {{.backup_db_and_restore.backup_dir}} -name "packetfence-exportable-backup-*.tgz" -newermt "-10 minute" | head -n 1)
[ -n "$tgz" ] || { echo "no exportable backup archive found"; exit 1; }
echo "archive=$tgz"
list=$(tar -tzf "$tgz")
echo "$list"
echo "$list" | grep -q '^grants.sql$'
echo "$list" | grep -q '^triggers.sql$'
db=$(echo "$list" | grep -E '^packetfence-db-dump-.*\.sql\.gz$' | head -n 1)
[ -n "$db" ]
tmp=$(mktemp -d)
tar -xzf "$tgz" -C "$tmp" "$db" triggers.sql
zcat "$tmp/$db" | grep -q 'CREATE TABLE' # schema
zcat "$tmp/$db" | grep -q 'INSERT INTO' # data
grep -qiE 'CREATE .*TRIGGER' "$tmp/triggers.sql" # triggers
rm -rf "$tmp"
assertions:
- result.code ShouldEqual 0

- name: delete_user_in_db
steps:
- type: http
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ testcases:
- name: import_previous_exportation
steps:
- type: exec
script: '/usr/local/pf/addons/full-import/import.sh --db-restore -f {{.get_backup_name.result.systemout}}'
script: '/usr/local/pf/addons/full-import/import.sh --db --restore-as-is -f {{.get_backup_name.result.systemout}}'

- name: sleep_for_iptables_to_restart
# import.sh isolates packetfence-base; restart PacketFence like an operator would
# so the webadmin API is reachable for the following testcases.
- name: restart_packetfence
steps:
- type: pfcmd_run_command
command: 'service pf start'
- type: exec
script: sleep 10
script: sleep 30
Loading
Loading