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.
330 lines
9.1 KiB
330 lines
9.1 KiB
#!/bin/bash
|
|
# Purpose: *.patchtt functionality for ngcpcfg config/templates
|
|
################################################################################
|
|
|
|
set -e
|
|
set -u
|
|
|
|
# support testsuite
|
|
FUNCTIONS="${FUNCTIONS:-/usr/share/ngcp-ngcpcfg/functions/}"
|
|
HELPER="${HELPER:-/usr/share/ngcp-ngcpcfg/helper/}"
|
|
SCRIPTS="${SCRIPTS:-/usr/share/ngcp-ngcpcfg/scripts/}"
|
|
|
|
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
|
|
|
|
cd "${NGCPCTL_MAIN}"
|
|
|
|
## functions {{{
|
|
patch_help() {
|
|
export TIME_FORMAT=''
|
|
log_info "'ngcpcfg patch' walks through all templates searching for '*.patchtt.tt2' files"
|
|
log_info "and generates '*.customtt.tt2' files based on the original template."
|
|
log_info "The option '--from-customtt' simplifies migration from customtt to patchtt."
|
|
log_info ""
|
|
log_info "Sample:"
|
|
log_info " ngcpcfg patch [--help | --from-customtt [<customtt file(s)>] | <patchtt file(s)>]"
|
|
log_info ""
|
|
log_info "Run 'man ngcpcfg' for more information."
|
|
}
|
|
|
|
patch_search() {
|
|
local name="$1"
|
|
shift # remove the first 'patchtt/customtt'
|
|
local files=("$@")
|
|
local fileslist
|
|
fileslist=$(mktemp -t ngcpcfg-patch-search.XXXXXXXXXX)
|
|
|
|
local regexp=""
|
|
local hosts=()
|
|
if [ -n "${HOST_FILE:-}" ]; then
|
|
hosts+=("${HOST_FILE}")
|
|
fi
|
|
if [ -n "${PAIR_FILE:-}" ]; then
|
|
hosts+=("${PAIR_FILE}")
|
|
fi
|
|
if [ -n "${NODE_FILE:-}" ]; then
|
|
hosts+=("${NODE_FILE}")
|
|
fi
|
|
for val in "${hosts[@]}"; do
|
|
regexp="${regexp}|\\${val}"
|
|
done
|
|
local awk_regexp=".*${name}\.tt2$"
|
|
if [ -n "${regexp}" ]; then
|
|
awk_regexp=".*${name}\.tt2(${regexp#|})?$"
|
|
fi
|
|
log_debug "Searching for ${name} files (requested '${files[*]}') awk_regexp:${awk_regexp}"
|
|
|
|
for dir in ${CONFIG_POOL} ; do
|
|
[ -n "${dir}" ] || log_error "${dir} doesn't exist"
|
|
log_debug "Iterate over all files in ${TEMPLATE_POOL_BASE%/}/${dir#/}"
|
|
while read -r file ; do
|
|
if [ "${#files[@]}" = "0" ] ; then
|
|
log_debug "NO arguments provided via cmdline"
|
|
log_debug "Found ${name} '${file}'"
|
|
echo "${file}" >> "${fileslist}"
|
|
else
|
|
# arguments (file list/pattern) provided via cmdline
|
|
for arg in "${files[@]}"; do
|
|
if echo "${file}" | grep -q -- "${arg}" ; then
|
|
log_debug "Processing ${name} '${file}' as requested"
|
|
echo "${file}" >> "${fileslist}"
|
|
fi
|
|
done
|
|
fi
|
|
done < <(find "${TEMPLATE_POOL_BASE%/}/${dir#/}" -regextype awk -iregex "${awk_regexp}")
|
|
done
|
|
|
|
# output patch list, make sure we provide the file names just once
|
|
sort -u "${fileslist}"
|
|
|
|
if [ -n "${DEBUG:-}" ] ; then
|
|
# send to stderr since stdout is used from outside
|
|
log_debug "Not removing temporary fileslist files since we are in debug mode:" >&2
|
|
log_debug " fileslist = ${fileslist}" >&2
|
|
else
|
|
rm -f "${fileslist}"
|
|
fi
|
|
}
|
|
|
|
patch_validate_patch() {
|
|
local patch="$1"
|
|
log_debug "Validating patch: '${patch}'"
|
|
|
|
if [ ! -f "${patch}" ] ; then
|
|
log_error "Missing patch file '${patch}'"
|
|
bad_files+=("${patch}")
|
|
return 1
|
|
fi
|
|
|
|
local template="${patch%%.patchtt*}.tt2"
|
|
|
|
if [ -f "${template}" ] ; then
|
|
log_debug "Found template for the patch: '${template}'"
|
|
else
|
|
log_error "Missing template '${template}'"
|
|
log_error " for patch '${patch}'"
|
|
bad_files+=("${patch}")
|
|
return 1
|
|
fi
|
|
|
|
local customtt="${patch//.patchtt/.customtt}"
|
|
if [ -f "${customtt}" ] ; then
|
|
log_debug "Overwriting customtt '${customtt}'"
|
|
else
|
|
log_debug "Not found customtt for the patch: '${customtt}'"
|
|
fi
|
|
}
|
|
|
|
patch_apply() {
|
|
local patch="$1"
|
|
local apply="${2:-false}"
|
|
local template="${patch%%.patchtt*}.tt2"
|
|
local customtt="${patch//.patchtt/.customtt}"
|
|
local patch_output
|
|
patch_output=$(mktemp -t ngcpcfg-patch-out.XXXXXXXXXX)
|
|
|
|
local patch_opts=()
|
|
patch_opts+=("--input=${patch}")
|
|
patch_opts+=("--prefix=/dev/null") # do not produce .orig backup files
|
|
patch_opts+=("--reject-file=-") # do not produce .rej file
|
|
patch_opts+=(-F0 -N) # disable fuzzy logic to be as safe as possible
|
|
if "${apply}" ; then
|
|
patch_opts+=("--output=${customtt}")
|
|
else
|
|
patch_opts+=(--dry-run)
|
|
patch_opts+=("--output=/dev/null")
|
|
fi
|
|
|
|
log_debug "Generating customtt '${customtt}' from '${patch}' (apply=${apply})"
|
|
|
|
log_debug "Executing: patch ${patch_opts[*]} ${template}"
|
|
if patch "${patch_opts[@]}" "${template}" >"${patch_output}" 2>&1 ; then
|
|
if "${apply}" ; then
|
|
log_info "Successfully created '${customtt}'"
|
|
else
|
|
log_debug "Patch '${patch}' can be applied"
|
|
fi
|
|
good_files+=("${patch}")
|
|
else
|
|
log_error "The patch '${patch}' cannot be applied:"
|
|
cat "${patch_output}" >&2
|
|
bad_files+=("${patch}")
|
|
fi
|
|
|
|
if [[ -f "${template}" && -f "${customtt}" ]] ; then
|
|
chmod --reference="${template}" "${customtt}"
|
|
chown --reference="${template}" "${customtt}"
|
|
fi
|
|
rm -f "${patch_output}"
|
|
}
|
|
|
|
patch_validate_customtt() {
|
|
local file="$1"
|
|
log_debug "Validating customtt: '${file}'"
|
|
|
|
if [ ! -f "${file}" ] ; then
|
|
log_error "Missing customtt file '${file}'"
|
|
bad_files+=("${file}")
|
|
return 1
|
|
fi
|
|
|
|
local template="${file%%.customtt*}.tt2"
|
|
|
|
if [ -f "${template}" ] ; then
|
|
log_debug "Found template for the customtt: '${template}'"
|
|
else
|
|
log_error "Missing template for customtt '${customtt}'"
|
|
bad_files+=("${customtt}")
|
|
return 1
|
|
fi
|
|
|
|
local patchtt="${file//.customtt/.patchtt}"
|
|
if [ -f "${patchtt}" ] ; then
|
|
log_debug "Overwriting patchtt '${patchtt}'"
|
|
else
|
|
log_debug "Not found patchtt for the customtt: '${patchtt}'"
|
|
fi
|
|
}
|
|
|
|
patch_import_customtt() {
|
|
local customtt_file="$1"
|
|
local apply="${2:-false}"
|
|
local template="${customtt_file%%.customtt*}.tt2"
|
|
local patchtt="${customtt_file//.customtt/.patchtt}"
|
|
|
|
local tmp_patchtt
|
|
tmp_patchtt=$(mktemp -t ngcpcfg-patch-import.XXXXXXXXXX)
|
|
local diff_output
|
|
diff_output=$(mktemp -t ngcpcfg-patch-diff.XXXXXXXXXX)
|
|
|
|
|
|
# diff exit status is 0 if inputs are the same, 1 if different, 2 if trouble.
|
|
log_debug "Generating temporary patchtt '${tmp_patchtt}' from template '${template}' and customtt '${customtt_file}'"
|
|
case "$(diff -u "${template}" "${customtt_file}" > "${tmp_patchtt}" 2>"${diff_output}" && echo $? || echo $?)" in
|
|
0)
|
|
log_error "No difference between customtt '${customtt_file}' and template '${template}' (patchtt is not necessary here, remove or change customtt file)"
|
|
bad_files+=("${customtt_file}")
|
|
;;
|
|
1)
|
|
log_debug "Successfully processed diff for customtt '${customtt_file}'"
|
|
good_files+=("${customtt_file}")
|
|
;;
|
|
2)
|
|
log_error "Diff failed between template '${template}' and customtt file '${customtt_file}':"
|
|
cat "${diff_output}" >&2
|
|
bad_files+=("${customtt_file}")
|
|
;;
|
|
*)
|
|
log_error "We should not be here. Aborting (better safe then sorry)."
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if "${apply}" ; then
|
|
log_info "Creating patchtt file '${patchtt}'"
|
|
log_debug "Removing first 2 lines (filename + date) from 'diff -u' output (due to time changes on '--from-customtt')"
|
|
tail -n +3 "${tmp_patchtt}" > "${patchtt}"
|
|
fi
|
|
|
|
rm -f "${diff_output}"
|
|
}
|
|
|
|
patch_import() {
|
|
mapfile -t files < <(patch_search "customtt" "$@")
|
|
|
|
log_debug "Validating customtt files"
|
|
for customtt in "${files[@]}" ; do
|
|
log_info "Validating customtt '${customtt}'"
|
|
if patch_validate_customtt "${customtt}" ; then
|
|
patch_import_customtt "${customtt}" "false"
|
|
fi
|
|
done
|
|
|
|
if [ "${#bad_files[@]}" != "0" ] ; then
|
|
log_debug "Aborted here due to failed patch validation above"
|
|
return
|
|
fi
|
|
|
|
log_debug "Validating patchtt files from customtt"
|
|
for customtt in "${files[@]}" ; do
|
|
patch_import_customtt "${customtt}" "true"
|
|
done
|
|
}
|
|
|
|
patch_patch() {
|
|
mapfile -t files < <(patch_search "patchtt" "$@")
|
|
|
|
for patch in "${files[@]}" ; do
|
|
log_info "Validating patch '${patch}'"
|
|
if patch_validate_patch "${patch}" ; then
|
|
patch_apply "${patch}" "false"
|
|
fi
|
|
done
|
|
|
|
if [ "${#bad_files[@]}" != "0" ] ; then
|
|
log_debug "Aborted here due to failed patch validation above"
|
|
return
|
|
fi
|
|
|
|
for patch in "${files[@]}" ; do
|
|
log_info "Applying patch '${patch}'"
|
|
patch_apply "${patch}" "true"
|
|
done
|
|
}
|
|
|
|
patch_footer() {
|
|
local label="$1"
|
|
|
|
if [ "${#bad_files[@]}" = "0" ] ; then
|
|
if [ "${#good_files[@]}" = "0" ] ; then
|
|
log_info "No ${label} files found, nothing to patch."
|
|
else
|
|
log_info "Requested ${label} operation has finished successfully."
|
|
fi
|
|
else
|
|
log_error "Some operations above finished with an error for the file(s):"
|
|
mapfile -t bad_files_unique < <(echo "${bad_files[@]}" | tr ' ' '\n' | sort -u)
|
|
printf '\t%s\n' "${bad_files_unique[@]}"
|
|
RC=1
|
|
fi
|
|
}
|
|
## }}}
|
|
|
|
declare -a bad_files=()
|
|
declare -a good_files=()
|
|
|
|
patch_type=patchtt
|
|
|
|
while [ -n "${1:-}" ] ; do
|
|
case "${1:-}" in
|
|
--help)
|
|
patch_help
|
|
exit 0
|
|
;;
|
|
--from-customtt)
|
|
patch_type=customtt
|
|
shift
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ "${patch_type}" = 'patchtt' ]; then
|
|
patch_patch "$@"
|
|
else
|
|
patch_import "$@"
|
|
fi
|
|
patch_footer "${patch_type}"
|
|
|
|
exit "${RC:-0}"
|
|
|
|
## END OF FILE #################################################################
|