#!/bin/bash # iff executed under VERBOSE=yes provide more information verbose() { if [[ "$VERBOSE" == "yes" ]] ; then echo "$*" fi } usage() { echo -e "Usage: $0 [OPTION]..." 1>&2 echo -e "\t-f: Force update, even if running on active node" 1>&2 } FORCE_ACTIVE_NODE=false RUN_SYNC_DB=false SYNC_DB="/usr/sbin/ngcp-sync-db" NOT_REPLICATED_REV_APPLIED=false while getopts "fh" opt; do case "${opt}" in f) FORCE_ACTIVE_NODE=true ;; h) usage; exit 0; ;; *) usage; exit 1; ;; esac done shift $((OPTIND-1)) nodename=$(/usr/sbin/ngcp-nodename 2>/dev/null || echo "unknown") if [ "${nodename}" = "unknown" ] ; then echo "Error: nodename could not be identified." >&2 exit 1 fi service mysql start || true . /etc/mysql/sipwise.cnf . /etc/default/ngcp-roles . /etc/default/ngcp-db if [ -z "${SIPWISE_DB_PASSWORD}" ] ; then echo 'Error: SIPWISE_DB_PASSWORD is unset (using /etc/mysql/sipwise.cnf).' >&2 exit 1 fi # check connection with sipwise user if ! mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" -e 'SELECT 1;' 1>/dev/null 2>/dev/null ; then echo 'Error: cant connect to local MySQL with sipwise user' >&2 exit 1 fi # support automated installation if [ -n "$AUTOMATED_INSTALL_MODE" ] ; then echo "Running in automated installation mode, ignoring check for empty db_schema." else if [ "$(mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" -e 'select * from ngcp.db_schema;' 2>/dev/null)" = "" ] ; then echo "=================================================================" echo "Warning: the db_schema table of the ngcp database is empty." echo "Are you sure you want to proceed with applying db-schema changes?" echo "This will DROP and then re-initialize your existing database." printf "Please type 'agree' to really continue: " unset AGREE read AGREE if [ "$AGREE" != "agree" ]; then echo "Exiting as requested." >&2 exit 1 fi fi fi if ! mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" -e 'use ngcp;' 2>/dev/null ; then mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" < /usr/share/ngcp-db-schema/db_scripts/init/0005_create_ngcp.up fi if ! mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" -e 'use ngcp;' 2>/dev/null ; then echo 'Error: ngcp database does not exist.' >&2 exit 1 fi if ! mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" ngcp -e 'describe db_schema' >/dev/null; then echo 'Error: db_schema table does not exit.' >&2 exit 1 fi if [ -z "$SKIP_SYNC_DB" ] && [ "$NGCP_TYPE" = "carrier" ] && [ "$NGCP_IS_PROXY" = "yes" ] ; then if ! $FORCE_ACTIVE_NODE && [ ! -x "$SYNC_DB" ] ; then echo "Error: ${SYNC_DB} is required for carrier proxy (or lacking execute permissions)." >&2 exit 1 fi RUN_SYNC_DB=true fi running_on_active_node() { if [ "$nodename" = "spce" ] ; then return 1 else if /usr/sbin/ngcp-check-active -q ; then if $FORCE_ACTIVE_NODE; then echo "Force update on active node as requested." else echo "This seems to be the active node, nothing to do." fi return 0 else echo "This seems to be the inactive node, applying revisions." return 1 fi fi } if running_on_active_node ; then ACTIVE_NODE=true else ACTIVE_NODE=false fi apply_revision() { [ -n "$1" ] || return 1 rev="$1" revname="$(basename "$rev")" printf "Applying revision script %s: " "$rev" if mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" < "$rev" ; then echo "done" else echo "failed. :(" >&2 echo "Please resolve the problem and run ngcp-update-db-schema again." >&2 exit 1 fi if mysql -usipwise -p"${SIPWISE_DB_PASSWORD}" ngcp -e "insert into db_schema values (0, '${revname}', '${nodename}', CURRENT_TIMESTAMP);" ; then verbose "Marked revision $rev as applied." else echo "Error while executing DB commands using revision $rev for node $nodename" >&2 exit 1 fi } # execute the rev script iff there's no entry for *any* host yet apply_revs() { [ -n "$1" ] || return 1 revs="$1" # generate list of rev scripts that are node specific, used inside # missing_revision's ngcp-check-rev-applied ... --node $nodename for rev in $revs ; do revision_file="$(find /usr/share/ngcp-db-schema/ -name "$rev")" case "$revision_file" in *_not_replicated.up) node_revs="$node_revs $rev" ;; esac done # shellcheck disable=SC2086 for missing_revision in \ $( { ngcp-check-rev-applied --schema db_schema --revision $revs | awk '/^No match for revision/ {print $5}' ; ngcp-check-rev-applied --schema db_schema --revision $node_revs --node "$nodename" | awk '/^No match for revision/ {print $5}' ; } | sort -n -u ) ; do revision_file="$(find /usr/share/ngcp-db-schema/ -name "$missing_revision")" # execute the rev script iff there's no entry for the *current* host yet case "$revision_file" in *_not_replicated.up) if ngcp-check-rev-applied --schema db_schema --revision "$missing_revision" --node "$nodename" | grep -q 'already executed' ; then continue fi ;; esac if [ -r "$revision_file" ] ; then apply_revision "$revision_file" if [[ $revision_file == *_not_replicated.up ]] ; then NOT_REPLICATED_REV_APPLIED=true fi else echo "Warning: missing revision $missing_revision identified but could not find according db-schema file." fi done } revision_wrapper() { [ -n "$1" ] || return 1 local revlist for rev in "$@" ; do if ! [ -r "$rev" ] ; then echo "Error: $rev can not be read." >&2 exit 1 fi cd "$(dirname "$rev")" || exit 1 # would fail if a script references a file (like language_strings.txt) in its CWD revname="$(basename "$rev")" case "$revname" in # the scripts that should be executed on *all* hosts, no matter whether # they are active or inactive, since the script's content doesn't get replicated # NOTE: the actual logic is inside the apply_revs to avoid *_not_replicated.up # scripts being executed independent from the other ones *_not_replicated.up) revlist="$revlist $(basename "$rev")" ;; *) if $ACTIVE_NODE ; then if $FORCE_ACTIVE_NODE; then revlist="$revlist $(basename "$rev")" else verbose "Replication script ${revname} noted, nothing to do on active node" fi else revlist="$revlist $(basename "$rev")" fi ;; esac done apply_revs "$revlist" } # make sure we get sorted 10XXX after 9XXX cd /usr/share/ngcp-db-schema/db_scripts/base/ # shellcheck disable=SC2035 disable=SC2046 revision_wrapper $(printf '%s\0' *.up | sort -z -n | while IFS= read -r -d "" file; do \ echo "/usr/share/ngcp-db-schema/db_scripts/base/$file"; done) cd /usr/share/ngcp-db-schema/db_scripts/diff/ # shellcheck disable=SC2035 disable=SC2046 revision_wrapper $(printf '%s\0' *.up | sort -z -n | while IFS= read -r -d "" file; do \ echo "/usr/share/ngcp-db-schema/db_scripts/diff/$file"; done) # for carrier prx* hosts sync master-slave replication for not_replicated statements (if any) if $RUN_SYNC_DB && ($NOT_REPLICATED_REV_APPLIED || [ -n "$AUTOMATED_INSTALL_MODE" ]); then if [ -x "$SYNC_DB" ] ; then PRX_SYNC_DBS="ngcp billing carrier kamailio provisioning prosody mysql" EXTRA_SYNC_DB_OPTS="" if [ -n "$AUTOMATED_INSTALL_MODE" ] ; then EXTRA_SYNC_DB_OPTS="--local-user=root --set-local-grants" fi # shellcheck disable=SC2086 if /usr/sbin/ngcp-sync-db --verbose --force --use-central-db --repl-mode "master-slave" --init-replication --databases "${PRX_SYNC_DBS}" --ssh-tunnel=33125 ${EXTRA_SYNC_DB_OPTS} --local-host=${LOCAL_DBHOST} --local-port=${LOCAL_DBPORT}; then echo "Syncing 'not_replicated' statements from the central node." else echo "Error: While syncing 'not_replicated' statements from the central node, please run ngcp-sync-db-wrapper manually to fix them." >&2 exit 1 fi else echo "Warning: Skipping sync of 'not_replicated' statements from the central node as ${SYNC_DB} is missing on the current node." >&2 fi fi ## END OF FILE #################################################################