ntopng/httpdocs/misc/ntopng-utils-manage-config.in

640 lines
18 KiB
Bash
Executable file

#!/usr/bin/env bash
OS="`uname`"
NTOPNG_CONFIG_PATH="/etc/ntopng"
case $OS in
'FreeBSD')
NTOPNG_CONFIG_PATH="/usr/local/etc"
;;
'Linux') ;;
*) ;;
esac
DEFAULT_CONF_FILE="${NTOPNG_CONFIG_PATH}/ntopng.conf"
DEFAULT_DATADIR="/var/lib/ntopng"
OLD_DEFAULT_DATADIR="/var/tmp/ntopng"
if [ -d "${OLD_DEFAULT_DATADIR}" ]; then
DEFAULT_DATADIR="${OLD_DEFAULT_DATADIR}"
fi
REDIS_DUMP_DIR="/var/lib/redis"
REDIS_DUMP_FILENAME="dump.rdb"
REDIS_DUMP_PATH="${REDIS_DUMP_DIR}/${REDIS_DUMP_FILENAME}"
DATADIR="${DEFAULT_DATADIR}"
DATADIR_SET=false
REDIS_INFO=""
ARCHIVE_NAME="conf.tar.gz"
ARCHIVE=""
ARCHIVE_SET=false
STAGING_DIR=""
SAVED_SUFFIX=".ntopngsaved"
ACTION=""
QUIET=true
INTERFACE=""
INCLUDE_REDIS_BACKUP=false
NEDGE="@NEDGE@"
function print_usage() {
echo "ntopng configuration backup/restore utility"
echo ""
echo "Usage:"
echo "`basename $0` -a restore [-d datadir] [-c archive.tar.gz] [-r]"
echo "`basename $0` -a backup [-d datadir] [-c archive.tar.gz] [-r]"
echo "`basename $0` -a check-restore [-d datadir] [-c archive.tar.gz]"
echo "`basename $0` -a install-n2disk-conf [-d datadir] -i interface"
echo "`basename $0` -a install-n2n-conf [-d datadir]"
echo "`basename $0` -a print-datadir"
echo "`basename $0` -a print-redis-info"
echo ""
echo "[-d datadir]"
echo " The path to the ntopng data directory."
echo " When no path is specified, ntopng configuration file ${DEFAULT_CONF_FILE}"
echo " is read in an attempt to determine the data directory. If the data directory cannot"
echo " be determined from the ntopng configuration file, the default ${DEFAULT_DATADIR} is used."
echo " The data directory must exist on the system."
echo ""
echo "[-c archive.tar.gz]"
echo " The path to a compressed archive used to backup or restore ntopng files."
echo " When no path is specified, file ${ARCHIVE_NAME} inside the data directory is used."
echo ""
echo "[-a restore]"
echo "`basename $0` -a restore [-c archive.tar.gz] [-d datadir]"
echo " Restore places the files contained in the archive into the current system."
echo " Resored data directory files will take the ownership (user and group) of exisining the data directory."
echo " The only compressed archives accepted are those created from the web interface or with this script."
echo " Add -r to also restore redis from the backup."
echo ""
echo "[-a backup]"
echo "`basename $0` -a backup [-c archive.tar.gz] [-d datadir] [-r]"
echo " Backup creates a compressed archive using files contained into the current system."
echo " Add -r to include also redis to the backup."
echo ""
echo "[-a check-restore]"
echo "`basename $0` -a check-restore [-c archive.tar.gz] [-d datadir]"
echo " Check restore scans the data directory and looks for a valid compressed archive there."
echo " Returns true when the archive is found and false otherwise."
echo ""
echo "[-a install-n2disk-conf]"
echo "`basename $0` -a install-n2disk-conf [-d datadir] -i interface"
echo " Installs a n2disk configuration file generated by ntopng to /etc/n2disk/ creating a symlink"
echo ""
echo "[-a install-n2n-conf]"
echo "`basename $0` -a install-n2n-conf [-d datadir]"
echo " Installs a n2n edge configuration file generated by ntopng to /etc/n2n/ creating a symlink"
echo ""
echo "[-a print-datadir]"
echo "`basename $0` -a print-datadir"
echo " Print the path of the ntopng data directory."
echo ""
echo "[-a print-redis-info]"
echo "`basename $0` -a print-redis-info"
echo " Print the redis connection details used by ntopng."
echo ""
}
POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-a|--action)
ACTION="$2"
shift
shift
;;
-c|--archive)
ARCHIVE="$2"
ARCHIVE_SET=true
shift
shift
;;
-d|--data-dir)
DATADIR="$2"
DATADIR_SET=true
shift
shift
;;
-i|--interface)
INTERFACE="$2"
shift
shift
;;
-v|--verbose)
QUIET=false
shift
;;
-r|--redis)
INCLUDE_REDIS_BACKUP=true
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
function cleanup() {
if [ "$?" = "0" ]; then
# exited with SUCCESS
if [ $ACTION == "restore" ]; then
rm -rf "${ARCHIVE}"
fi
else
# exited with ERROR
:
fi
rm -rf "${STAGING_DIR}"
}
trap cleanup EXIT
if [[ ( $ACTION != "backup" && $ACTION != "restore" && $ACTION != "check-restore" && $ACTION != "install-n2disk-conf" && $ACTION != "install-n2n-conf" && $ACTION != "print-datadir" && $ACTION != "print-redis-info" ) ]]; then
print_usage
exit 1
fi
if [[ ( $ACTION == "restore" ) && ( ${#POSITIONAL[@]} -gt 0 ) ]]; then
print_usage
exit 1
fi
if [ $ACTION == "check-restore" ]; then
QUIET=true
fi
function prepare_redis_info() {
if [ -f "${DEFAULT_CONF_FILE}" ]; then
# make sure there's at most one among -r and --redis
local REDIS_COUNT="`cat "${DEFAULT_CONF_FILE}" | grep -v "#" | grep "\-r[= ]\|\-\-redis" | wc -l`"
if [ "${REDIS_COUNT}" -eq "1" ]; then
# use awk to split on spaces or = signs and remove " and ' s, and take the last column that should be the path to the data directory
REDIS_INFO=`cat "${DEFAULT_CONF_FILE}" | grep -v "#" | grep "\-r[= ]\|\-\-redis" | awk -F"[= ]" '{print $NF}' | sed s/\"//g | sed s/\'//g`
fi
fi
}
function prepare_cur_data_dir_path() {
if [ "${DATADIR_SET}" = false ] && [ -f "${DEFAULT_CONF_FILE}" ]; then
# make sure there's at most one among -d and --data-dir
local DATADIR_COUNT="`cat "${DEFAULT_CONF_FILE}" | grep -v "#" | grep "\-d[= ]\|\-\-data\-dir" | wc -l`"
if [ "${DATADIR_COUNT}" -gt "1" ]; then
[ $QUIET = false ] && echo "Cannot reliably determine the data dir from ${DEFAULT_CONF_FILE}"
exit 1
elif [ "${DATADIR_COUNT}" -eq "1" ]; then
# use awk to split on spaces or = signs and remove " and ' s, and take the last column that should be the path to the data directory
local CUR_DATADIR=`cat "${DEFAULT_CONF_FILE}" | grep -v "#" | grep "\-d[= ]\|\-\-data\-dir" | awk -F"[= ]" '{print $NF}' | sed s/\"//g | sed s/\'//g`
if [ ! -d "${CUR_DATADIR}" ]; then
[ $QUIET = false ] && "Data dir ${CUR_DATADIR} read from ${DEFAULT_CONF_FILE} is not a directory on the system"
exit 1
fi
# set the data dir to the one extracted from the configuration file
DATADIR="${CUR_DATADIR}"
fi
fi
if [ ! -d "${DATADIR}" ]; then
[ $QUIET = false ] && echo "Data dir ${DATADIR} is not a directory on the system."
exit 1
fi
[ $QUIET = false ] && echo "Using data dir ${DATADIR}"
}
function prepare_tar_file_path() {
if [ "${ARCHIVE_SET}" = false ]; then
ARCHIVE="${DATADIR}/${ARCHIVE_NAME}"
fi
}
function setup_staging_dir() {
# the temp directory used, within $DIR
# omit the -p parameter to create a temporal directory in the default location
STAGING_DIR=`mktemp -d`
# check if tmp dir was created
if [[ ! "$STAGING_DIR" || ! -d "$STAGING_DIR" ]]; then
echo "Could not create temp directory"
exit 1
fi
}
function copy_to_staging_dir() {
local PATHNAME="$1"
local DST="$2"
if [ ! -f "${PATHNAME}" ] && [ ! -d "${PATHNAME}" ]; then
return
fi
if [[ "${PATHNAME}" != /* ]]; then
echo "Skipping ${PATHNAME}. Only absolute paths allowed"
return
fi
# recreate the tree into the staging directory
local STAGED="${STAGING_DIR}/${DST}"
mkdir -p "${STAGED}"
# copy to the staging area
if [ -f "${PATHNAME}" ]; then
cp -Pp "${PATHNAME}" "${STAGED}"
else
cp -RPp "${PATHNAME}/." "${STAGED}"
fi
# minimum permissions to move files (e.g. otherwise sanitize fails)
chmod u+rw "${STAGED}"
# echo "${STAGING_DIR}"
# find ${STAGING_DIR}
}
function manifest_to_staging_dir() {
local DST="${STAGING_DIR}/MANIFEST"
# write a timestamp and the original data directory to a manifest file
echo "Time: $(date)" >> "${DST}"
echo "__datadir: ""${DATADIR}" >> "${DST}"
}
function backup_to_staging_dir() {
copy_to_staging_dir "/etc/ntopng" "__etc_ntopng"
copy_to_staging_dir "${DATADIR}/runtimeprefs.json" "__datadir"
if [ "${NEDGE}" == "0" ]; then
copy_to_staging_dir "/etc/ntopng.license" "__license"
else
copy_to_staging_dir "/etc/nedge.license" "__license"
copy_to_staging_dir "${DATADIR}/system.config" "__datadir"
fi
if [ $INCLUDE_REDIS_BACKUP = true ]; then
NTOPNG_IS_ACTIVE=$(/bin/systemctl is-active ntopng 2>/dev/null)
if [ "${NTOPNG_IS_ACTIVE}" == "active" ]; then
echo "[i] Stopping service"
systemctl stop ntopng
fi
echo "[i] Saving configuration"
LASTSAVE=`/usr/bin/redis-cli LASTSAVE|cut -f2`
/usr/bin/redis-cli BGSAVE
sleep 2
echo "[i] Creating backup"
CURRSAVE=`/usr/bin/redis-cli LASTSAVE|cut -f2`
copy_to_staging_dir ${REDIS_DUMP_PATH} "__redis"
echo "[!] New redis backup saved [time=${CURRSAVE}]"
if [ "${NTOPNG_IS_ACTIVE}" == "active" ]; then
echo "[i] Restarting service"
systemctl restart ntopng
fi
fi
manifest_to_staging_dir
}
function sanitize_staging_dir() {
# Throw away any symlink
find "${STAGING_DIR}" -type l -exec rm -rf {} \;
# Throw away any possible executable file from the staging area
# executables include binaries but also shell scripts
if ! `find "${STAGING_DIR}" -executable -type f -exec rm -rf {} \; 2> /dev/null`; then
# some versions of find don't support -executable and require the
# executable permissions bits to be specified explicitly
find "${STAGING_DIR}" -perm +0111 -type f -exec rm -rf {} \;
fi
# Throw away backup files
find "${STAGING_DIR}" -type f -name "*~" -exec rm -rf {} \;
# Throw away previously backed up ntopng files
find "${STAGING_DIR}" -type f -name "*${SAVED_SUFFIX}" -exec rm -rf {} \;
# Throw away empty files
find "${STAGING_DIR}" -type f -size 0 -exec rm -rf {} \;
# Throw away everything else BUT pure ASCII files
# or PNG images when nEdge
find "${STAGING_DIR}" -type f -name '*.*' -print0 |
while IFS= read -r -d '' FL; do
if ! `file "${FL}" | grep -qc "\(ASCII text\|JSON data\)";`; then
if [ "${NEDGE}" == "1" ]; then
if `file "${FL}" | grep -qc "PNG image data";`; then
continue
fi
fi
if [ $INCLUDE_REDIS_BACKUP = true ]; then
if `file "${FL}" | grep -qc "Redis RDB file";`; then
continue
fi
fi
rm -rf "${FL}"
echo "File ""${FL}"" ignored"
fi
done
# echo "${STAGING_DIR}"
# find ${STAGING_DIR}
}
function staging_dir_to_archive() {
# remove the archive if it exists
rm -rf "${ARCHIVE}"
# compress the staging dir to a compressed archive
# by discaring the current path to the staging dir
tar cfz "${ARCHIVE}" -C "${STAGING_DIR}" .
}
function check_tar_file() {
if [ ! -f "${ARCHIVE}" ]; then
[ $QUIET = false ] && echo "Missing ${ARCHIVE}: unable to restore"
exit 1
fi
# make sure it's a tar archive
if ! `file "${ARCHIVE}" | grep -qc "gzip compressed data";`; then
[ $QUIET = false ] && echo "Unrecognized file format for ${ARCHIVE}: expecting gzip compressed data"
exit 1
fi
}
function check_tar_contents() {
# must contain a manifest file
if ! `tar tf "${ARCHIVE}" ./MANIFEST >/dev/null 2>&1`; then
[ $QUIET = false ] && echo "Compressed archive $1 does not contain MANIFEST"
exit 1
fi
}
function tar_file_to_staging_dir() {
echo "Extracting compressed archive contents to ${STAGING_DIR}"
tar xfz "${ARCHIVE}" -C "${STAGING_DIR}" || exit 1
}
function check_current_system_dirs() {
# DATADIR is empty when system.config and runtimeprefs.json are not
# going to be restored
if [ ! -z "${DATADIR}" ] && [ ! -d "${DATADIR}" ]; then
echo "Directory ${DATADIR} missing from the system, unable to restore."
exit 1
fi
}
function restore_and_save_file() {
local SRC="$1" # full path to the source file
local DST_DIR="$2" # destination directory
local OWNERSHIP="$3" # ownership to execute chown
local PERMISSIONS="$4" # permission to execute chmod (e.g. "660")
if [ ! -z "${SRC}" ] && [ -f "${SRC}" ] && [ ! -z "${DST_DIR}" ] && [ -d "${DST_DIR}" ]; then
local SRC_BASENAME=`basename "${SRC}"`
local DST="${DST_DIR}/${SRC_BASENAME}"
if [ -f "${DST}" ]; then
# make a backup copy of the file before overwriting it
local SAV="${DST}${SAVED_SUFFIX}"
rm -rf "${SAV}" || exit 1
echo "Old file ""${SAV}"" removed"
cp -Rp "${DST}" "${SAV}" || exit 1
echo "File ""${DST}"" saved to ""${SAV}"
fi
# put the recovered file in place
cp -Rp "${SRC}" "${DST}" || exit 1
echo "File ""${DST}"" restored"
# possibly change the ownership
if [ ! -z "${OWNERSHIP}" ]; then
chown "${OWNERSHIP}" "${DST}" || exit 1
echo "File ""${DST}"" owner set to ""${OWNERSHIP}"
fi
# possibly change the permissions
if [ ! -z "${PERMISSIONS}" ]; then
chmod "${PERMISSIONS}" "${DST}" || exit 1
echo "File ""${DST}"" permissions set to ""${PERMISSIONS}"
fi
fi
}
function restore_files() {
# restore /etc/ntopng files
if [ -d "${STAGING_DIR}/__etc_ntopng" ]; then
find "${STAGING_DIR}/__etc_ntopng" -maxdepth 1 -type f -name '*.*' -print0 |
while IFS= read -r -d '' FL; do
restore_and_save_file "${FL}" "/etc/ntopng"
done
fi
# restore redis, if requested
if [ $INCLUDE_REDIS_BACKUP = true ]; then
NTOPNG_IS_ACTIVE=$(/bin/systemctl is-active ntopng 2>/dev/null)
if [ "${NTOPNG_IS_ACTIVE}" == "active" ]; then
echo "[i] Stopping ntopng service"
systemctl stop ntopng
fi
echo "[i] Stopping redis service"
/usr/bin/redis-cli FLUSHALL
systemctl stop redis-server
echo "[i] Restoring configuration"
mv ${REDIS_DUMP_PATH} ${REDIS_DUMP_PATH}.old
restore_and_save_file "${STAGING_DIR}/__redis/${REDIS_DUMP_FILENAME}" "${REDIS_DUMP_DIR}" "redis:redis" "660"
echo "[i] Restarting redis service"
systemctl start redis-server
if [ "${NTOPNG_IS_ACTIVE}" == "active" ]; then
echo "[i] Restarting ntopng service"
systemctl restart ntopng
fi
fi
# restore license
if [ "${NEDGE}" == "0" ] && [ -f "${STAGING_DIR}/__license/ntopng.license" ]; then
restore_and_save_file "${STAGING_DIR}/__license/ntopng.license" "/etc" "root:root"
fi
if [ "${NEDGE}" == "1" ] && [ -f "${STAGING_DIR}/__license/nedge.license" ]; then
restore_and_save_file "${STAGING_DIR}/__license/nedge.license" "/etc" "root:root"
fi
if [ -z "${DATADIR}" ] || [ ! -d "${DATADIR}" ]; then
# nothing to do, the either not existing or not a directory
return
fi
# Get the ownership of the data directory
# extracted files will be chowned to make sure they have the same
# owner and group of the data directory
local USER_GROUP=`ls -ld ${DATADIR} | awk '{print $3":"$4}'`
# will be something like root:root nobody:nogroup
if [ -z "${USER_GROUP}" ]; then
echo "Unable to retrieve user and group of ${DATADIR}"
exit 1
fi
if [ "${NEDGE}" == "1" ] && [ -f "${STAGING_DIR}/__datadir/system.config" ]; then
restore_and_save_file "${STAGING_DIR}/__datadir/system.config" "${DATADIR}" "${USER_GROUP}"
fi
if [ -f "${STAGING_DIR}/__datadir/runtimeprefs.json" ]; then
restore_and_save_file "${STAGING_DIR}/__datadir/runtimeprefs.json" "${DATADIR}" "${USER_GROUP}"
fi
}
function post_restore_actions() {
# in case we are restoring nedge, we touch a file to indicate
# a fresh first start.
if [ "${NEDGE}" == "1" ]; then
touch "${DATADIR}/system.config.reload"
fi
}
function install_n2disk_conf() {
if [ "${INTERFACE}" == "" ]; then
return
fi
local SOURCE_FILE="${DATADIR}/n2disk/n2disk-${INTERFACE}.conf"
if [ ! -f "${SOURCE_FILE}" ]; then
return
fi
local DEST_PATH="/etc/n2disk"
local DEST_FILE="${DEST_PATH}/n2disk-ntopng-${INTERFACE}.conf"
mkdir -p "${DEST_PATH}"
rm -rf "${DEST_FILE}"
ln -sf "${SOURCE_FILE}" "${DEST_FILE}"
}
function install_disk2disk_conf() {
if [ "${INTERFACE}" == "" ]; then
return
fi
local SOURCE_FILE="${DATADIR}/disk2disk/disk2disk-${INTERFACE}.conf"
if [ ! -f "${SOURCE_FILE}" ]; then
return
fi
local DEST_PATH="/etc/disk2disk"
local DEST_FILE="${DEST_PATH}/disk2disk-ntopng-${INTERFACE}.conf"
mkdir -p "${DEST_PATH}"
rm -rf "${DEST_FILE}"
ln -sf "${SOURCE_FILE}" "${DEST_FILE}"
}
function install_n2n_conf() {
local SOURCE_FILE="${DATADIR}/n2n/edge.conf"
if [ ! -f "${SOURCE_FILE}" ]; then
return
fi
local DEST_PATH="/etc/n2n"
mkdir -p "${DEST_PATH}"
if [ "${INTERFACE}" != "" ]; then
DEST_PATH="${DEST_PATH}/edge-${INTERFACE}.conf"
fi
ln -sf "${SOURCE_FILE}" "${DEST_PATH}"
}
prepare_cur_data_dir_path
prepare_tar_file_path
prepare_redis_info
if [ $ACTION == "backup" ]; then
echo "Backing up to ${ARCHIVE} ..."
setup_staging_dir
backup_to_staging_dir
sanitize_staging_dir
staging_dir_to_archive
elif [ $ACTION == "restore" ]; then
echo "Restoring from ${ARCHIVE} ..."
# preliminary checks
check_tar_file
check_tar_contents
# actual restore
setup_staging_dir
tar_file_to_staging_dir
check_current_system_dirs
sanitize_staging_dir
restore_files
post_restore_actions
elif [ $ACTION == "check-restore" ]; then
check_tar_file
check_tar_contents
elif [ $ACTION == "install-n2disk-conf" ]; then
[ $QUIET = false ] && echo "Installing n2disk configuration for ${INTERFACE} ..."
install_n2disk_conf
install_disk2disk_conf
elif [ $ACTION == "install-n2n-conf" ]; then
[ $QUIET = false ] && echo "Installing n2n configuration ..."
install_n2n_conf
elif [ $ACTION == "print-datadir" ]; then
echo "${DATADIR}"
elif [ $ACTION == "print-redis-info" ]; then
echo "${REDIS_INFO}"
else
# never reached
echo "Unknown action $ACTION"
exit 1
fi
exit 0