ntopng/httpdocs/misc/ntopng-utils-manage-updates.in
2022-08-26 09:31:25 +02:00

409 lines
12 KiB
Bash

#!/usr/bin/env bash
# bin paths
LSB_RELEASE="/usr/bin/lsb_release"
APT_GET="/usr/bin/apt-get -qq"
YUM="/usr/bin/yum"
NTOPNG_CONFIG_TOOL="/usr/bin/ntopng-utils-manage-config"
REDIS_CLI="/usr/bin/redis-cli"
PRODUCT="ntopng"
# redis keys
REDIS_CHECK_FOR_UPDATES_KEY="ntopng.updates.check_for_updates"
REDIS_NEW_VERSION_AVAILABLE_KEY="ntopng.updates.new_version"
REDIS_RUN_UPGRADE_KEY="ntopng.updates.run_upgrade"
REDIS_UPDATE_FAILURE_KEY="ntopng.updates.update_failure"
REDIS_AUTO_UPDATES_KEY="ntopng.prefs.is_autoupdates_enabled"
REDIS_IN_PROGRESS_KEY="ntopng.updates.in_progress"
REDIS_PRODUCT_NAME_KEY="ntopng.product_name"
ACTION=""
QUIET=true
LOGFILE_BASE="/var/log/ntopng-updates"
LOGFILE="${LOGFILE_BASE}.log"
RETAIN_NUM_LINES=1000
APT_SOURCE="ntop@STABLE_SUFFIX@.list"
# Required by apt-get on Ubuntu 18 to run in non interactive mode
export DEBIAN_FRONTEND="noninteractive"
# Workaround for third-party installers not using absolute paths
export PATH="$PATH:/sbin:/usr/sbin"
# Read redis connection info
REDIS_INFO=$(${NTOPNG_CONFIG_TOOL} -a print-redis-info)
HOST=$(echo ${REDIS_INFO} | cut -d'=' -f2 | cut -d'@' -f1 | cut -d':' -f1)
PORT=$(echo ${REDIS_INFO} | cut -d'=' -f2 | cut -d'@' -f1 | cut -d':' -f2 -s)
PSWD=$(echo ${REDIS_INFO} | cut -d'=' -f2 | cut -d'@' -f1 | cut -d':' -f3 -s)
DBID=$(echo ${REDIS_INFO} | cut -d'@' -f2 -s)
REDIS_CLI_OPT=""
if [ ! -z "${HOST}" ]; then
REDIS_CLI_OPT="${REDIS_CLI_OPT}-h ${HOST} "
fi
if [ ! -z "${PORT}" ]; then
REDIS_CLI_OPT="${REDIS_CLI_OPT}-p ${PORT} "
fi
if [ ! -z "${PSWD}" ]; then
REDIS_CLI_OPT="${REDIS_CLI_OPT}-a ${PSWD} "
fi
if [ ! -z "${DBID}" ]; then
REDIS_CLI_OPT="${REDIS_CLI_OPT}-n ${DBID} "
fi
# Detect OS
OS="DEBIAN"
if [ -x ${LSB_RELEASE} ]; then
REDHAT=$(${LSB_RELEASE} -i | grep -i "CentOS\|RedHat\|Oracle\|Rocky\|AlmaLinux")
if [ ! -z "${REDHAT}" ]; then
OS="REDHAT"
fi
else
if [ -f /etc/rocky-release ]; then
OS="REDHAT"
elif [ -f /etc/oracle-release ]; then
OS="REDHAT"
elif [ -f /etc/redhat-release ]; then
OS="REDHAT"
fi
fi
if [ "${OS}" == "DEBIAN" ]; then
# Check for nEdge
if dpkg --get-selections | grep -q "^nedge[[:space:]]*install$" >/dev/null; then
PRODUCT="nedge"
fi
fi
# Log init
function logsetup {
if [ ! -z "$1" ]; then
LOGFILE="${LOGFILE_BASE}-$1.log"
fi
TMP=$(tail -n $RETAIN_NUM_LINES $LOGFILE 2>/dev/null) && echo "${TMP}" > $LOGFILE
# Log to file and stdout (trigger crontab events, e.g. mail)
#exec > >(tee -a $LOGFILE)
# Log to file only
exec > $LOGFILE
exec 2>&1
}
# Log
function log {
echo "$(date +"%b %d %T"): $*"
}
# Help
function print_usage() {
echo "${PRODUCT} updates utility"
echo ""
echo "Usage:"
echo "`basename $0` -a check-updates"
echo "`basename $0` -a check-updates-on-demand"
echo "`basename $0` -a handle-upgrade-requests"
echo "`basename $0` -a handle-on-demand-requests"
echo ""
echo "[-a check-updates]"
echo "`basename $0` -a check-updates"
echo "Check for ${PRODUCT} updates."
echo ""
echo "[-a check-updates-on-demand]"
echo "`basename $0` -a check-updates-on-demand"
echo "Check for requests for checking ${PRODUCT} updates."
echo ""
echo "[-a handle-upgrade-requests]"
echo "`basename $0` -a handle-upgrade-requests"
echo "Check for update requests and update ${PRODUCT}."
echo ""
echo "[-a handle-on-demand-requests]"
echo "`basename $0` -a handle-on-demand-requests"
echo "Check for requests from ${PRODUCT} (same as check-updates-on-demand + handle-upgrade-requests)."
echo ""
}
POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-a|--action)
ACTION="$2"
shift
shift
;;
-v|--verbose)
QUIET=false
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters
if [[ ( $ACTION != "check-updates" && $ACTION != "check-updates-on-demand" && $ACTION != "handle-upgrade-requests" && $ACTION != "handle-on-demand-requests" ) ]]; then
print_usage
exit 1
fi
# Uncomment to enable logging (moved to the single functions to reduce verbosity)
#logsetup $ACTION
#log Requested $ACTION
function set_failure() {
STATUS=$1
# Report failure status
${REDIS_CLI} ${REDIS_CLI_OPT} SET ${REDIS_UPDATE_FAILURE_KEY} "${STATUS}" >/dev/null 2>&1
# Cleanup requests
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_CHECK_FOR_UPDATES_KEY} >/dev/null 2>&1
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_RUN_UPGRADE_KEY} >/dev/null 2>&1
}
function stall_check() {
INSTANCES=$(pgrep -fc "$0 -a ${ACTION}")
if [ $INSTANCES -gt 2 ]; then
echo "Another instance is running with the same action"
exit 0
fi
}
function maintenance_check() {
APP_BIN="/usr/bin/${PRODUCT}"
NOW=`date +'%s'`
EXPIRY_EPOCH=`${APP_BIN} --check-maintenance | cut -d' ' -f 1`
if [ "${EXPIRY_EPOCH}" = "Invalid" ]; then
# Missing or expired or invalid license
# Allow updates on missing license file as this does not
# invalidate the license and handle community mode
if [ `${APP_BIN} --check-license | grep Empty | wc -l ` -eq 0 ]; then
# Expired or invalid license
set_failure "no-license"
exit 0
fi
elif echo "${EXPIRY_EPOCH}" | grep -q "^[0-9]\+$"; then
if [ "${EXPIRY_EPOCH}" -gt "0" ] && [ "${EXPIRY_EPOCH}" -lt "${NOW}" ]; then
# Expired maintenance
set_failure "expired-maintenance"
exit 0
fi
else
# Unable to read maintenance
set_failure "no-license"
exit 0
fi
}
function service_enabled_check() {
SERVICE_ENABLED=$(/bin/systemctl is-enabled ${PRODUCT} 2>/dev/null)
if [ ! "${SERVICE_ENABLED}" == "enabled" ]; then
set_failure "service-not-enabled"
exit 0
fi
}
# Check if the user requested a ntopng update, and upgrade it in case
function run_upgrade() {
RESULT=1
# Check if upgrade is in progress (it can take more than 1 min)
IN_PROGRESS=$(${REDIS_CLI} ${REDIS_CLI_OPT} GET ${REDIS_IN_PROGRESS_KEY} 2>/dev/null | grep 1)
if [ "${IN_PROGRESS}" == "1" ]; then
return
fi
# Set "in progress" flag
${REDIS_CLI} ${REDIS_CLI_OPT} SET ${REDIS_IN_PROGRESS_KEY} "1" EX 3600 >/dev/null 2>&1
logsetup $ACTION
log Requested $ACTION
if [ "${OS}" == "DEBIAN" ]; then
# Debian or Ubuntu
# Update repo index
#${APT_GET} update -o Dir::Etc::sourcelist="sources.list.d/${APT_SOURCE}" -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"
# Note: we are updating all the repos as ntopng may depend on old
# packages no longer available in other repos, leading to an upgrade failure.
${APT_GET} update
# Install
#${APT_GET} upgrade --assume-yes --fix-broken --allow-unauthenticated --with-new-pkgs ${PRODUCT}
# Note: using install instead of upgrade to avoid blocking the installation due to 'kept back' packages
${APT_GET} install --assume-yes --fix-broken --allow-unauthenticated ${PRODUCT}
RESULT=$?
# Check if installation is successful (we do not trust $?)
#NTOPNG_VERSION=$(${APT_GET} --just-print upgrade 2>&1 | grep "Inst ${PRODUCT} " | cut -d'(' -f2 | cut -d' ' -f1)
NTOPNG_VERSION=$(${APT_GET} --just-print install ${PRODUCT} 2>&1 | grep "Inst ${PRODUCT} " | cut -d'(' -f2 | cut -d' ' -f1)
if [ -z "${NTOPNG_VERSION}" ]; then
RESULT=0
fi
else
# CentOS
# Install
${YUM} clean all
${YUM} check-update ${PRODUCT}
${YUM} -y install ${PRODUCT}
RESULT=$?
fi
if [ $RESULT -eq 0 ]; then
[ $QUIET = false ] && echo "${PRODUCT} updated successfully"
# Cleanup new version available key
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_NEW_VERSION_AVAILABLE_KEY} >/dev/null 2>&1
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_UPDATE_FAILURE_KEY} >/dev/null 2>&1
# Restart the service if not running (e.g. old prerm was disabling it)
/bin/systemctl -q is-active ${PRODUCT} || /bin/systemctl restart ${PRODUCT}
else
[ $QUIET = false ] && echo "Unable to update"
${REDIS_CLI} ${REDIS_CLI_OPT} SET ${REDIS_UPDATE_FAILURE_KEY} "upgrade-failure" >/dev/null 2>&1
fi
# Cleanup "in progress" flag
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_IN_PROGRESS_KEY} >/dev/null 2>&1
# Cleanup upgrade request key if any
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_RUN_UPGRADE_KEY} >/dev/null 2>&1
}
# Check for new ntopng updates available
function check_updates() {
CRON_UPDATES=$1
NTOPNG_VERSION=""
RESULT=1
logsetup $ACTION
log Requested $ACTION
if [ "${OS}" == "DEBIAN" ]; then
# Debian or Ubuntu
# Update repo index
${APT_GET} update -o Dir::Etc::sourcelist="sources.list.d/${APT_SOURCE}" -o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"
# Check for broken packages
#${APT_GET} --just-print upgrade >/dev/null 2>&1
${APT_GET} --just-print install ${PRODUCT} >/dev/null 2>&1
if [ ! $? -eq 0 ]; then
# Something went wrong, trying to fix it
${APT_GET} --assume-yes --fix-broken install
#${APT_GET} --just-print upgrade >/dev/null 2>&1
${APT_GET} --just-print install ${PRODUCT} >/dev/null 2>&1
fi
if [ ! $? -eq 0 ]; then
# Something went wrong, unable to fix it
${REDIS_CLI} ${REDIS_CLI_OPT} SET ${REDIS_UPDATE_FAILURE_KEY} "update-failure" >/dev/null 2>&1
else
# Check update and get version
#NTOPNG_VERSION=$(${APT_GET} --just-print --assume-yes --with-new-pkgs upgrade 2>&1 | grep "Inst ${PRODUCT} " | cut -d'(' -f2 | cut -d' ' -f1)
# Note: using install instead of upgrade to handle 'kept back' packages
NTOPNG_VERSION=$(${APT_GET} --just-print --assume-yes install ${PRODUCT} 2>&1 | grep "Inst ${PRODUCT} " | cut -d'(' -f2 | cut -d' ' -f1)
RESULT=0
fi
else
# CentOS
# Check update and get version
${YUM} clean all
NTOPNG_VERSION=$(${YUM} check-update ${PRODUCT} | grep ${PRODUCT} | tr -s ' ' | cut -d' ' -f2)
RESULT=0
fi
# If there is an update, set new version on redis, otherwise delete the current version (if any)
if [ ! -z "${NTOPNG_VERSION}" ]; then
${REDIS_CLI} ${REDIS_CLI_OPT} SET ${REDIS_NEW_VERSION_AVAILABLE_KEY} ${NTOPNG_VERSION} EX 86400 >/dev/null 2>&1
# Reset failure reason if any
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_UPDATE_FAILURE_KEY} >/dev/null 2>&1
# Check if this is run by cron (not a manual check)
if [ "${CRON_UPDATES}" = "1" ]; then
# Check if automatic updates are enabled by the user
AUTO_UPDATES=$(${REDIS_CLI} ${REDIS_CLI_OPT} GET ${REDIS_AUTO_UPDATES_KEY} 2>/dev/null | grep 1)
if [ "${AUTO_UPDATES}" == "1" ]; then
run_upgrade
fi
fi
else
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_NEW_VERSION_AVAILABLE_KEY} >/dev/null 2>&1
if [ $RESULT -eq 0 ]; then
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_UPDATE_FAILURE_KEY} >/dev/null 2>&1
fi
fi
}
# Check if the user requested a 'Check for updates' manually
function check_updates_on_demand() {
CHECK_FOR_UPDATES=$(${REDIS_CLI} ${REDIS_CLI_OPT} GET ${REDIS_CHECK_FOR_UPDATES_KEY} 2>/dev/null | grep 1)
if [ "${CHECK_FOR_UPDATES}" == "1" ]; then
stall_check
check_updates 0
${REDIS_CLI} ${REDIS_CLI_OPT} DEL ${REDIS_CHECK_FOR_UPDATES_KEY} >/dev/null 2>&1
fi
}
# Check if the user requested a ntopng update, and upgrade it in case
function handle_upgrade_requests() {
# Check redis for upgrade requests
UPGRADE=$(${REDIS_CLI} ${REDIS_CLI_OPT} GET ${REDIS_RUN_UPGRADE_KEY} 2>/dev/null | grep 1)
if [ "${UPGRADE}" == "1" ]; then
stall_check
run_upgrade
fi
}
# Check if the product is under maintenance
maintenance_check
# Check OEM mode
OEM_MODE=$(${REDIS_CLI} ${REDIS_CLI_OPT} EXISTS ${REDIS_PRODUCT_NAME_KEY} 2>/dev/null | grep 1)
if [ "${OEM_MODE}" == "1" ]; then
# Exit unless there is an OEM source file
APT_SOURCE="ntop-oem.list"
eval $(apt-config shell APT_ETC Dir::Etc)
[ -e "/${APT_ETC}sources.list.d/${APT_SOURCE}" ] || exit 0
fi
if [ $ACTION == "check-updates" ]; then
# Run automatic update check when the ntopng service is enabled only
service_enabled_check
check_updates 1
elif [ $ACTION == "check-updates-on-demand" ]; then
check_updates_on_demand
elif [ $ACTION == "handle-upgrade-requests" ]; then
handle_upgrade_requests
elif [ $ACTION == "handle-on-demand-requests" ]; then
# Same as check-updates-on-demand + handle-upgrade-requests
check_updates_on_demand
handle_upgrade_requests
else
# never reached
echo "Unknown action $ACTION"
exit 1
fi
exit 0