mirror of https://github.com/sipwise/ngcpcfg.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
360 lines
9.7 KiB
360 lines
9.7 KiB
#!/bin/bash
|
|
# Purpose: detect modified files in config tree and execute
|
|
# any defined service modifications
|
|
################################################################################
|
|
|
|
set -e
|
|
set -E
|
|
set -u
|
|
|
|
# support testsuite
|
|
|
|
FUNCTIONS="${FUNCTIONS:-/usr/share/ngcp-ngcpcfg/functions/}"
|
|
OUTPUT_DIRECTORY="${OUTPUT_DIRECTORY:-}"
|
|
|
|
CLEANUP_FILES=()
|
|
INSTANCES=()
|
|
|
|
# load modules
|
|
|
|
if [[ ! -r "${FUNCTIONS}"/main ]]; then
|
|
printf "Error: %s/main could not be read. Exiting.\n" "${FUNCTIONS}">&2
|
|
exit 1
|
|
fi
|
|
|
|
# shellcheck disable=SC1090
|
|
. "${FUNCTIONS}"/main
|
|
|
|
# functions
|
|
|
|
instances_info() {
|
|
local target
|
|
local dest
|
|
local instance
|
|
local RUNNER="nice -n 19 ionice -c 3"
|
|
|
|
log_debug "${RUNNER} ${HELPER}/instances-info"
|
|
while IFS=":" read -r -a info; do
|
|
target=${info[0]}
|
|
dest=${info[1]}
|
|
instance=${info[2]}
|
|
INSTANCES+=("${target}:${instance}:${dest}")
|
|
done< <(${RUNNER} "${HELPER}/instances-info")
|
|
}
|
|
|
|
exec_wrapper() {
|
|
local service_file=$1
|
|
local instance_name="${2:-}"
|
|
local msg=""
|
|
if [ -n "${instance_name}" ]; then
|
|
msg="[${instance_name}]"
|
|
fi
|
|
|
|
if ${DRYRUN} ; then
|
|
log_info "TEST MODE: Would execute action for ${service_file}${msg}"
|
|
return 0
|
|
fi
|
|
log_info "Executing action for ${service_file}${msg}"
|
|
if [[ -x "${service_file}" ]]; then
|
|
log_debug "${service_file}${msg}"
|
|
if ! INSTANCE_NAME="${instance_name}" "${service_file}" ; then
|
|
log_warn "INSTANCE_NAME=${instance_name} ${service_file} returned with error code, continuing anyway."
|
|
fi
|
|
elif [[ -r "${service_file}" ]]; then
|
|
log_debug "INSTANCE_NAME=${instance_name} bash ${service_file}"
|
|
if ! INSTANCE_NAME="${instance_name}" bash "${service_file}" ; then
|
|
log_warn "INSTANCE_NAME=${instance_name} ${service_file} returned with error code, continuing anyway."
|
|
fi
|
|
else
|
|
log_error "Error: ${service_file} could not be read."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# get rid of "./" and "//" in file names
|
|
normalize_files() {
|
|
NORMALIZED_FILES="$(mktemp -t ngcpcfg-services-norm.XXXXXXXXXX)"
|
|
log_debug "NORMALIZED_FILES = ${NORMALIZED_FILES}"
|
|
|
|
CLEANUP_FILES+=("${NORMALIZED_FILES}")
|
|
|
|
while read -r line ; do
|
|
# shellcheck disable=SC2001
|
|
echo "${line}" | sed -e 's_\./_/_g ; s_//_/_g' >> "${NORMALIZED_FILES}"
|
|
done < "${TMPFILE}"
|
|
}
|
|
|
|
# restart sysctl services before the rest (see TT#58703)
|
|
# restart monit services before the rest (see MT#9971)
|
|
# restart HA just after monit (see MT#17163)
|
|
# restart other services in alphabetical order
|
|
sort_service_list() {
|
|
SORTED_LIST="$(mktemp -t ngcpcfg-services-sorted.XXXXXXXXXX)"
|
|
log_debug "SORTED_LIST = ${SORTED_LIST}"
|
|
|
|
CLEANUP_FILES+=("${SORTED_LIST}")
|
|
|
|
for dir in sysctl.d monit corosync pacemaker ha.d; do
|
|
grep "${SERVICES_POOL_BASE}"/etc/${dir}/'.*services' \
|
|
"${NORMALIZED_FILES}" >> "${SORTED_LIST}" || true
|
|
done
|
|
|
|
# Sort loads the entire contents before outputting, and we are appending
|
|
# instead of truncating, so this should be safe.
|
|
# shellcheck disable=SC2094
|
|
sort "${NORMALIZED_FILES}" "${SORTED_LIST}" | uniq -u \
|
|
>> "${SORTED_LIST}" || true
|
|
}
|
|
|
|
services_start_queue()
|
|
{
|
|
log_info "Clearing out enqueued services actions"
|
|
ngcp-service queue-clear
|
|
log_info "Starting enqueued services actions mode"
|
|
ngcp-service queue-start
|
|
}
|
|
|
|
execute() {
|
|
local line
|
|
local info
|
|
|
|
while read -r line ; do
|
|
while IFS=':' read -ra info; do
|
|
exec_wrapper "${info[0]}" "${info[1]:-}"
|
|
done <<< "${line}"
|
|
done < "${SORTED_LIST}"
|
|
}
|
|
|
|
services_flush_enqueued()
|
|
{
|
|
log_info "Executing enqueued services actions"
|
|
ngcp-service --queue=default,policy-rc.d queue-show
|
|
ngcp-service --queue=default,policy-rc.d queue-run
|
|
}
|
|
|
|
services_sync_state() {
|
|
log_info "Synchronizing current with expected services state"
|
|
ngcp-service sync-state
|
|
}
|
|
|
|
systemd_daemon_reload_preset() {
|
|
if [[ -d "/run/systemd/system" ]]; then
|
|
log_info "Reloading systemd daemon and preset all services"
|
|
|
|
log_debug "systemd needs daemon-reload so the unit files loaded are in sync"
|
|
log_debug "Running: systemctl daemon-reload 2>&1 || true"
|
|
systemctl daemon-reload 2>&1 || true
|
|
|
|
log_debug "Removing any broken systemd service symlink"
|
|
find -L /etc/systemd/system -type l \
|
|
| xargs -r rm
|
|
|
|
log_debug "systemd needs preset-all to enable/disable services (to start them on boot)"
|
|
log_debug "Running: rm -rf /etc/systemd/system/*.wants/ || true"
|
|
rm -rf /etc/systemd/system/*.wants/ || true
|
|
log_debug "Running: systemctl preset-all 2>&1 || true"
|
|
systemctl preset-all 2>&1 || true
|
|
fi
|
|
}
|
|
|
|
is_absolute_path() {
|
|
local dir="$1"
|
|
|
|
if [[ ! "${dir}" =~ ^/ ]]; then
|
|
log_error "${dir} is not an absolute path"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
is_non_git_folder() {
|
|
local dir="$1"
|
|
|
|
if [[ ! -d "${dir}/.git" ]]; then
|
|
log_info "${dir} has no support of .services"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
generate_list_to_process() {
|
|
local dir="$1"
|
|
|
|
if ${FORCE_ALL_SERVICES} ; then
|
|
log_debug "calling function find_all_services()"
|
|
find_all_services "${dir}"
|
|
else
|
|
log_debug "calling function find_all_changed_services()"
|
|
find_all_changed_services "${dir}"
|
|
fi
|
|
}
|
|
|
|
find_instance() {
|
|
local service_file
|
|
local info
|
|
local target
|
|
|
|
# get rid of "./" and "//"
|
|
# shellcheck disable=SC2001
|
|
service_file=$(echo "$1" | sed -e 's_\./_/_g ; s_//_/_g')
|
|
|
|
for instance in "${INSTANCES[@]}"; do
|
|
IFS=":" read -ra info <<< "${instance}"
|
|
# get rid of "./" and "//"
|
|
target=$(echo "${TEMPLATE_POOL_BASE}${info[0]}" | sed -e 's_\./_/_g ; s_//_/_g')
|
|
if [[ "${service_file}" =~ ^${target} ]] ; then
|
|
log_debug "[${info[1]}] Storing ${file} in '${TMPFILE}'"
|
|
echo "${file}:${info[1]}" >> "${TMPFILE}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
find_all_services() {
|
|
local dir="$1"
|
|
|
|
while read -r file ; do
|
|
if [[ ! -r "${file}" ]]; then
|
|
log_warn "Cannot read file '${file}'"
|
|
fi
|
|
log_debug "Storing ${file} in '${TMPFILE}'"
|
|
echo "${file}" >> "${TMPFILE}"
|
|
find_instance "${file}"
|
|
done < <(find "${SERVICES_POOL_BASE}/${dir}" -name '*.services' | sort -u)
|
|
}
|
|
|
|
find_changed_instance_services() {
|
|
local file
|
|
local dir
|
|
local info
|
|
local dest
|
|
local target
|
|
local target_file
|
|
|
|
# get rid of "./" and "//"
|
|
# shellcheck disable=SC2001
|
|
file=$(echo "$1"| sed -e 's_\./_/_g ; s_//_/_g')
|
|
# shellcheck disable=SC2001
|
|
dir=$(echo "$2"| sed -e 's_\./_/_g ; s_//_/_g')
|
|
|
|
for instance in "${INSTANCES[@]}"; do
|
|
IFS=":" read -ra info <<< "${instance}"
|
|
target=${info[0]#${dir}/}
|
|
dest=${info[2]#${dir}/}
|
|
if [[ "${file}" =~ ^${dest} ]] ; then
|
|
target_file=${file/${dest}/${target}}
|
|
if [[ -r "${SERVICES_POOL_BASE}/${dir}/${target_file}".services ]]; then
|
|
log_debug "[${info[1]}] Storing ${SERVICES_POOL_BASE}/${dir}/${target_file}.services in '${TMPFILE}'"
|
|
echo "${SERVICES_POOL_BASE}/${dir}/${target_file}.services:${info[1]}"
|
|
elif [[ -r "${SERVICES_POOL_BASE}/${dir}/$(dirname "${target_file}")"/ngcpcfg.services ]]; then
|
|
log_debug "[${info[1]}] Storing ${SERVICES_POOL_BASE}/${dir}/$(dirname "${target_file}")/ngcpcfg.services in '${TMPFILE}'"
|
|
echo "${SERVICES_POOL_BASE}/${dir}/$(dirname "${target_file}")/ngcpcfg.services:${info[1]}"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
find_all_changed_services() {
|
|
local dir="$1"
|
|
|
|
log_debug "${FUNCNAME[0]}(): Working in ${OUTPUT_DIRECTORY}${dir}"
|
|
pushd "${OUTPUT_DIRECTORY}${dir}" >/dev/null
|
|
|
|
for file in $(git status -uall --porcelain | sed 's/^...//') ; do
|
|
if ! [[ -r "${file}" ]]; then
|
|
continue
|
|
elif [[ -r "${SERVICES_POOL_BASE}/${dir}/${file}".services ]]; then
|
|
log_debug "Storing ${SERVICES_POOL_BASE}/${dir}/${file}.services in '${TMPFILE}'"
|
|
echo "${SERVICES_POOL_BASE}/${dir}/${file}".services
|
|
elif [[ -r "${SERVICES_POOL_BASE}/${dir}/$(dirname "${file}")"/ngcpcfg.services ]]; then
|
|
log_debug "Storing ${SERVICES_POOL_BASE}/${dir}/$(dirname "$file")/ngcpcfg.services in '${TMPFILE}'"
|
|
echo "${SERVICES_POOL_BASE}/${dir}/$(dirname "$file")/ngcpcfg.services"
|
|
else
|
|
find_changed_instance_services "${file}" "${dir}"
|
|
fi
|
|
done | sort -u >> "${TMPFILE}"
|
|
|
|
popd >/dev/null
|
|
}
|
|
|
|
# main script
|
|
|
|
RUNNING_FILE="${RUN_DIR}/ngcpcfg-services.running"
|
|
DRYRUN='false'
|
|
FORCE_ALL_SERVICES='false'
|
|
|
|
while [ -n "${1:-}" ]; do
|
|
case "$1" in
|
|
test|--dry-run)
|
|
DRYRUN='true'
|
|
shift
|
|
;;
|
|
--force-all-services)
|
|
FORCE_ALL_SERVICES='true'
|
|
shift
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
log_debug "DRYRUN = ${DRYRUN}"
|
|
log_debug "FORCE_ALL_SERVICES = ${FORCE_ALL_SERVICES}"
|
|
|
|
if ! ${DRYRUN}; then
|
|
log_debug "creating current execution filesystem trail"
|
|
trap 'rm -f "${RUNNING_FILE}"' EXIT ERR
|
|
echo $$ >"${RUNNING_FILE}"
|
|
fi
|
|
|
|
log_debug "systemd_daemon_reload_preset function"
|
|
systemd_daemon_reload_preset
|
|
|
|
TMPFILE="$(mktemp -t ngcpcfg-services-tmp.XXXXXXXXXX)"
|
|
log_debug "TMPFILE = ${TMPFILE}"
|
|
log_debug "OUTPUT_DIRECTORY=${OUTPUT_DIRECTORY}"
|
|
|
|
CLEANUP_FILES+=("${TMPFILE}")
|
|
|
|
|
|
if [ -n "${TEMPLATE_INSTANCES:-}" ] && [ -f "${TEMPLATE_INSTANCES}" ] ; then
|
|
instances_info
|
|
fi
|
|
|
|
for dir in ${CONFIG_POOL} ; do
|
|
is_absolute_path "${OUTPUT_DIRECTORY}${dir}" || continue
|
|
is_non_git_folder "${OUTPUT_DIRECTORY}${dir}" || continue
|
|
|
|
generate_list_to_process "${dir}"
|
|
done
|
|
|
|
if [[ -s "${TMPFILE}" ]]; then
|
|
log_debug "normalize_files function"
|
|
normalize_files
|
|
|
|
log_debug "sort_service_list function"
|
|
sort_service_list
|
|
|
|
services_start_queue
|
|
|
|
log_debug "execute function"
|
|
execute
|
|
else
|
|
log_debug "No services file(s) reported - no explicit service state changed."
|
|
fi
|
|
|
|
# Always flush the queues, to handle actions from package upgrades.
|
|
services_flush_enqueued
|
|
|
|
log_debug "services_sync_state function"
|
|
services_sync_state
|
|
|
|
if [[ -n "${DEBUG:-}" ]]; then
|
|
log_debug "Not removing temporary files"
|
|
else
|
|
rm -f "${CLEANUP_FILES[@]}"
|
|
fi
|
|
## END OF FILE #################################################################
|