EVOLUTION-MANAGER
Edit File: ntopng-utils-manage-config
#!/bin/bash DEFAULT_CONF_FILE="/etc/ntopng/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 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="" NEDGE="0" function print_usage() { echo "ntopng configuration backup/restore utility" echo "" echo "Usage:" echo "`basename $0` -a restore [-d datadir] [-c archive.tar.gz]" echo "`basename $0` -a backup [-d datadir] [-c archive.tar.gz]" 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 "" echo "[-a backup]" echo "`basename $0` -a backup [-c archive.tar.gz] [-d datadir]" echo " Backup creates a compressed archive using files contained into the current system." 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 ;; *) 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 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 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 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}"" permissions set to ""${OWNERSHIP}" 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 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_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 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