#!/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 mariadb start || true . /etc/default/ngcp-roles . /etc/default/ngcp-db declare -r SIPWISE_EXTRA_CNF="/etc/mysql/sipwise_extra.cnf" if [ ! -f "${SIPWISE_EXTRA_CNF}" ]; then echo "Error: missing DB credentials file '${SIPWISE_EXTRA_CNF}'." >&2 exit 1 fi declare -a MYSQL_OPTS=() MYSQL_OPTS+=("--defaults-extra-file=${SIPWISE_EXTRA_CNF}") # check connection with sipwise user if ! mysql "${MYSQL_OPTS[@]}" -e 'SELECT 1;' &>/dev/null ; then echo "Error: can't connect to local MariaDB port 3306 using '${SIPWISE_EXTRA_CNF}'" >&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 "${MYSQL_OPTS[@]}" -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 "${MYSQL_OPTS[@]}" -e 'use ngcp;' 2>/dev/null ; then mysql "${MYSQL_OPTS[@]}" < /usr/share/ngcp-db-schema/db_scripts/init/0005_create_ngcp.up fi if ! mysql "${MYSQL_OPTS[@]}" -e 'use ngcp;' 2>/dev/null ; then echo 'Error: ngcp database does not exist.' >&2 exit 1 fi if ! mysql "${MYSQL_OPTS[@]}" 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 "${MYSQL_OPTS[@]}" < "$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 "${MYSQL_OPTS[@]}" 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 # check connection to MariaDB on port 3308 using socket to bypass auth as root has invalid password if ! mysql -S /run/mysqld/mysqld2.sock -e 'SELECT 1;' &>/dev/null ; then echo "Error: can't connect to local MariaDB:3308 using socket '/run/mysqld/mysqld2.sock'" >&2 exit 1 fi # Reset 'root' password temporary to allow ngcp-sync-db replicate all credentials from db01 if ! mysql -S /run/mysqld/mysqld2.sock -e "SET PASSWORD FOR root@localhost = '';" 2>/dev/null ; then echo "Error: Failed to temporary reset password for user 'root' on MariaDB:3308" >&2 exit 1 fi if [ -x "$SYNC_DB" ] ; then PRX_SYNC_DBS="ngcp billing carrier kamailio provisioning prosody mysql" PRX_IGNORE_TABLES="kamailio.voicemail_spool provisioning.autoprov_firmwares_data" EXTRA_SYNC_DB_OPTS="" if [ -n "$AUTOMATED_INSTALL_MODE" ] ; then EXTRA_SYNC_DB_OPTS="--local-user=root --set-local-grants" fi # determine the preferred db node a or b if the default db01 is the constants MASTER_NODE_PREF="" if [ "$CENTRAL_DBHOST" == "db01" ] ; then NODE_SIDE="a" if [[ "$nodename" == *b ]] ; then NODE_SIDE="b" fi MASTER_NODE_PREF="db01${NODE_SIDE}" fi if [ -n "$MASTER_NODE_PREF" ] ; then EXTRA_SYNC_DB_OPTS="$EXTRA_SYNC_DB_OPTS --master-host $MASTER_NODE_PREF" fi # shellcheck disable=SC2086 if /usr/sbin/ngcp-sync-db --verbose --force --use-central-db --repl-mode "master-slave" --databases "${PRX_SYNC_DBS}" --ignore-tables "${PRX_IGNORE_TABLES}" --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 #################################################################