#!/bin/bash

run_user=usgmtr

if [[ "$USER" != "$run_user" ]]; then
    echo "Please run as user $run_user"
    exit 0
fi

LOG_DIR=/var/log/usgmtr
UMDB=usgmtr
VPGBIN=/opt/vmware/vpostgres/current/bin
XFER_DIR=/tmp/migrateum_`date "+%Y%m%d_%H%M%S"`
UMSETENV=/opt/vmware/cloudusagemetering/scripts/umsetenv.sh
SSH_KEY=/tmp/usgmtr.rsa
SSH_PUBKEY=/tmp/usgmtr.rsa.pub
SSH_CONFIG=/tmp/ssh_config
LOCK_FILE=/tmp/um-migration.lock
USER=usgmtr
USER_GROUP=users
SUPERUSER=postgres
ACTION=migration

#DEFAULT_RETENTION=30
VERSION=3.6
DB_VERSION_350=15
DB_VERSION_360=16

UM_VERSION_SHORT_350=350
UM_VERSION_SHORT_360=360

log_echo () {
    timestamp=`date`
    if [ "x$LOG_FILE" != "x" ]; then
	echo "$timestamp $*" >> $LOG_FILE
    fi
    if [ $verbose -eq 1 ]; then
	echo $*
    fi
}

version () {

    version=`rpm -q --queryformat "%{VERSION}-%{RELEASE}\n" -f $0`
    if [ $? -ne 0 ]; then
	version=$VERSION
    fi
    echo $VERSION
    exit 0;
}

banner () {
    cat <<EOF

Usage Meter Migration Utility Version 3.6.1.0

This utility supports the following migration paths:
    Usage Meter 3.5.0.0 to Usage Meter 3.6.1.0
    Usage Meter 3.6.0.0 to Usage Meter 3.6.1.0
    Usage Meter 3.6.0.1 to Usage Meter 3.6.1.0

EOF
}

usage () {

    cat <<EOF
Usage: `basename $0` -h|--help | -d|--debug  IP
Options:
    -h|--help                Print this message and exit
    -d|--debug               sets debug mode on (turns set -x on)
Argument:
    IP                       Remote Usage Meter IP (mandatory, last parameter)

EOF
    exit $1
}

cleanup () {
    log_echo "Cleaning up ..."
    if [[ "x$SSH_CONFIG" != "x" && -f $SSH_CONFIG ]]; then
        rm -f $SSH_CONFIG
    fi
    if [[ "x$SSH_KEY" != "x" && -f $SSH_KEY ]]; then
        rm -f $SSH_KEY
    fi
    if [[ "x$SSH_PUBKEY" != "x" && -f $SSH_PUBKEY ]]; then
        rm -f $SSH_PUBKEY
    fi
    if [[ "x$LOCK_FILE" != "x" && -f $LOCK_FILE ]]; then
        rm -f $LOCK_FILE
    fi
}

exiting () {
	cleanup
	exit $1
}

init_logging () {

    log_echo "Initializing logging ..."
    datetime=`date "+%Y%m%d_%H%M%S"`
    LOG_FILE=$LOG_DIR/migrateum-`date "+%Y%m%d_%H%M%S"`.log
    touch $LOG_FILE       || exit $ERROR_LOG_FILE
    chown $USER $LOG_FILE || exit $ERROR_LOG_FILE
    echo "Log file: $LOG_FILE"
    log_echo "Setting lock file $LOCK_FILE"
    if [[ -f $LOCK_FILE ]]; then
	    log_echo "A migration process is running, exiting ..."
		exit $ERROR_LOCK_FILE
	else
	    touch $LOCK_FILE
		log_echo "Sleeping 5 seconds ..."
		sleep 5
	fi
}

init_ssh () {

    set +e
    ssh -o StrictHostKeyChecking=no -o LogLevel=QUIET -o PreferredAuthentications=publickey 0 true
    set -e
    ssh-keygen -q -R $HOST -f $HOME/.ssh/known_hosts > /dev/null 2>&1
    ssh="ssh -F $SSH_CONFIG $HOST"
    if [[ -f $SSH_CONFIG ]]; then
        set +e
        $ssh true
        if [ $? -eq 0 ]; then
            set -e
            return
        fi
    fi
    log_echo "Initializing ssh connection ..."
    rm          -f $SSH_KEY
    ssh-keygen  -f $SSH_KEY -N '' -q
    set +e
    ssh-copy-id -i $SSH_KEY $USER\@$HOST -o StrictHostKeyChecking=no >> $LOG_FILE 2>&1
    if [ $? -ne 0 ]; then
        log_echo "SSH Connection to $USER@$HOST could not be established, exiting ..."
	exiting $ERROR_SSH_SETUP
    fi
    set -e
    echo "
StrictHostKeyChecking    no
GSSAPIAuthentication	 no
PreferredAuthentications publickey
User                     $USER
IdentityFile             $SSH_KEY
LogLevel                 QUIET
"> $SSH_CONFIG
    $ssh true || exit $ERROR_SSH_SETUP
}

get_version () {

    remote=$1
    if [ "x$remote" == "x" ]; then
        version_str=`grep -oPm1 "(?<=<fullVersion>)[^<]+" /opt/vmware/etc/appliance-manifest.xml |cut -d" " -f1 |cut -b 1-7`
        version=`echo $version_str | tr -d . | cut -b 1-4`
	    version_short=`echo $version | cut -b 1-3`
        remote=local
    else
        version_str=`$ssh grep -oPm1 "'(?<=<fullVersion>)[^<]+'" /opt/vmware/etc/appliance-manifest.xml |cut -d" " -f1 |cut -b 1-7`
        version=`echo $version_str | tr -d . | cut -b 1-4`
		version_short=`echo $version | cut -b 1-3`
        remote=remote
    fi
    if [ "x$version" == "x" ]; then
	    log_echo "Failed to read $remote version"
	    exit $ERROR_READ_VERSION
    fi
    if [ "x$remote" == "xremote" ]; then
	    remote_version=$version
        remote_version_str=$version_str
        remote_version_short=$version_short
    else
	    local_version=$version
        local_version_str=$version_str
        local_version_short=$version_short
    fi
}

check_db_version () {

    remote=$1
    sql_file=/tmp/x.sql
    echo 'select version from "SchemaInfo";' > $sql_file
    if [ "x$remote" == "x" ]; then
        db_version=`$VPGBIN/psql -U $USER -t < $sql_file |tr -d " "`
        version=$local_version_short
        environment=local
    else
        db_version=`$ssh $VPGBIN/psql -U $USER -t < $sql_file |tr -d " "`
        version=$remote_version_short
        environment=remote
    fi

    if [[ !($db_version -eq $DB_VERSION_360 && $version -eq UM_VERSION_SHORT_360  || \
	    $db_version -eq $DB_VERSION_350 && $version -eq UM_VERSION_SHORT_350)  ]]; then
        log_echo "Inconsistent $environment versions: product is $version, DB is $db_version, aborting ..."
        exiting $ERROR_COMPARED_VERSIONS
    else
        echo  "Detected $environment usage-meter version $version_str and db version $db_version are consistent." >> $LOG_FILE
        echo  "Detected local usage-meter version $local_version_str." >> $LOG_FILE
    fi
}

check_versions () {

    log_echo "Detecting product version ..."
    get_version
    get_version remote

    if [[ !($remote_version_short -eq UM_VERSION_SHORT_350 || \
            $remote_version_short -eq UM_VERSION_SHORT_360) ]]; then
      str="The remote product version $remote_version_str is not supported, aborting ..."
      log_echo $str
      echo $str >> $LOG_FILE
      exiting $ERROR_COMPARED_VERSIONS;
    fi

    check_db_version remote

    if [ $local_version -lt $remote_version ]; then
	    log_echo "The remote version $remote_version_str is more recent than the local version $local_version_str, aborting ..."
	    exiting $ERROR_COMPARED_VERSIONS;
    fi
    if [ $local_version == $remote_version ]; then
	    ACTION=cloning
    else
        ACTION=migration
    fi
}

validate_time_zone () {

    log_echo "Validating time zone ..."
    tz_command='date "+%::z"'
    remote=`eval $ssh $tz_command`
    local=`eval $tz_command`
    if [ "$remote" != "$local" ]; then
	    log_echo "The remote host $remote_host time zone $remote is different from "
	    log_echo "the local host $local_host time zone $local"
	    log_echo ""
	    exiting $ERROR_TIMEZONE
    else
	    echo "Remote host $remote_host time zone $remote, local host $local_host time zone $local" >> $LOG_FILE
    fi
}

validate_disk_space () {

    log_echo "Validating disk space ..."
    postgres_partition=/var/vmware
    df_command="df -k $postgres_partition |grep / | awk '{print \$2}'"
    remote=`eval $ssh $df_command`
    local=`eval $df_command`
    if [ $remote -gt $local ]; then
	    log_echo "Warning!"
	    log_echo "The disk space $remote KB on the remote host $remote_host is larger than"
	    log_echo "the disk space $local KB on the local host $local_host"
	    log_echo ""
	    exiting $ERROR_DISK_SPACE
    else
	    echo "Remote host $remote_host disk space: $remote KB, local host $local_host disk space: $local KB" >> $LOG_FILE
    fi
}

check_sql_values () {

    log_echo "Validating remote Provider data before $ACTION ..."
    fields=(company contact email phone partnerId contractNum siteId)
    found_zero=0
    sql_file=/tmp/x.sql
    echo "select count(*) from \"Provider\";" > $sql_file
    val=`$ssh $VPGBIN/psql -U $USER -t < $sql_file |tr -d " "`
    if [ $val -eq 0 ]; then
        log_echo "The remote database is empty, aborting ..."
        exiting $ERROR_SQL_VALUES
    fi
    set -e
    sql_error="The remote Usage Meter at $remote_host Provider page has empty fields. Please login to https://${remote_host}:8443/um/manage, go to Provider page, then set a valid value to the following fields before continuing:\n"
    for field in ${fields[@]}
    do
	echo "select length(\"$field\") from \"Provider\";" > $sql_file
	val=`$ssh $VPGBIN/psql -U $USER -t < $sql_file |tr -d " "`
	if [ $val -lt 1 ]; then
        sql_error="$sql_error\t$field\n"
	    found_zero=1
	fi
    done
    rm -f $sql_file
    if [ $found_zero -eq 1 ]; then
	    tabs 4
	    log_echo -e $sql_error
	    exiting $ERROR_SQL_VALUES
    fi
}

confirm_upgrade () {

    while true; do
        log_echo " "
        log_echo "The remote Usage Meter $remote_version_str at $HOST"
        log_echo "will be migrated to $local_version_str on this local host $local_host"
	log_echo "There will be no changes made by the migration process to the remote Usage Meter"
	log_echo "But the local Usage Meter will be overwritten"
	log_echo ""
        read -p "Please confirm (yes/no)? " yn
        case $yn in
            yes ) break;;
            no ) rm -f $SSH_CONFIG; rm -f $SSH_KEY; exit 0;;
            * ) log_echo "Please answer yes or no.";;
        esac
    done
}

stop_tomcat () {

    sudo service tomcat stop >>$LOG_FILE 2>&1

    if [ $? -ne 0 ]; then
	    log_echo "Failed to stop tomcat, aborting ..."
	    exiting $ERROR_STOP_TOMCAT
    fi
}

prepare_backup () {

    keystore=$1
    rm -rf $XFER_DIR
    mkdir -p $XFER_DIR
    if [ ! -d $XFER_DIR ]; then
	    log_echo "Failed to prepare backup dir $XFER_DIR"
	    exiting $ERROR_BACKUP_DIR
    fi
    echo "Backup dir is set to $XFER_DIR" >> $LOG_FILE
    if [ "x$keystore" != "x" ]; then
	    backup_keystore=$encryption_dir/defaultks-$datetime
	    mv -v $encryption_dir/defaultks $backup_keystore >>$LOG_FILE 2>&1
	if [ $? -ne 0 ]; then
	    log_echo "Failed to back up keystore to $backup_keystore"
	    exiting $ERROR_BACKUP_KEYSTORE
	fi
	    echo "keystore is backed up to $backup_keystore" >> $LOG_FILE
    fi
}

copy_file () {

    file=$1
    xfer_dir=$2
    echo "Copying $HOST:$file to $xfer_dir" >> $LOG_FILE
    scp -r -F $SSH_CONFIG $HOST:$file $xfer_dir >>$LOG_FILE 2>&1
    if [ $? -ne 0 ]; then
	    log_echo "Failed to copy file $file to $xfer_dir"
	    exiting $ERROR_COPY_FILE
    fi
    chown $USER:$USER_GROUP $xfer_dir/`basename $file`
    # Remove legacy perms and restrict file to user only.
    chmod go-rwx $xfer_dir/`basename $file`
}

install_certificates () {

    cp -pv $XFER_DIR/defaultks $encryption_dir                 >>$LOG_FILE 2>&1 || exit $ERROR_INSTALL_CERTIFICATE
    cp -pv $XFER_DIR/defaultks $webappClasses                  >>$LOG_FILE 2>&1 || exit $ERROR_INSTALL_CERTIFICATE
    cp -pv $XFER_DIR/certs     $encryption_dir/certs.migrating >>$LOG_FILE 2>&1 || exit $ERROR_INSTALL_CERTIFICATE
}

copy_certificates () {

    log_echo "Copying certificate and keystore ..."
    copy_file $encryption_dir/defaultks $XFER_DIR
    copy_file $encryption_dir/certs     $XFER_DIR
    install_certificates
}

backup_db () {

    backup_db=${UMDB}_$datetime
    echo "Renaming existing database to $backup_db" >> $LOG_FILE
    sql_file=/tmp/x.sql
    echo "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$UMDB'; SELECT pg_sleep(1);" > $sql_file
    sudo -i -u postgres \
	$VPGBIN/psql -t < $sql_file >>$LOG_FILE 2>&1
    sudo -i -u postgres \
	$VPGBIN/psql --echo-all --command="alter database $UMDB rename to $backup_db;" >>$LOG_FILE 2>&1
    sudo -i -u postgres \
	$VPGBIN/createdb -O $USER $UMDB >>$LOG_FILE 2>&1
}

unzip_db () {

    dump_file=$1
    gunzip $dump_file;
}

restore_db () {

    dump_file=$1
    $VPGBIN/psql -U postgres -c "ALTER USER usgmtr WITH SUPERUSER" >>$LOG_FILE 2>&1
    cat $dump_file | $VPGBIN/psql -U $USER $UMDB >>$LOG_FILE 2>&1
    $VPGBIN/psql -U postgres -c "ALTER USER usgmtr WITH NOSUPERUSER" >>$LOG_FILE 2>&1
    $VPGBIN/psql -U $USER $UMDB -c "UPDATE "\"Provider\"" SET "\"portalPassword\""=''" >>$LOG_FILE 2>&1
}

copy_db () {

    log_echo "Copying vPostgres database ..."
    $ssh "$(typeset -f prepare_backup); export XFER_DIR=$XFER_DIR; prepare_backup" >> $LOG_FILE 2>&1

    dump_db_file=$XFER_DIR/umbackup-plain
    dump_db_gz=$dump_db_file.gz
    dump_db="$VPGBIN/pg_dump -U $UMDB --no-owner --format=p $UMDB | gzip >$db_dump_gz"

    echo "Dumping the remote database $UMDB at $HOST" >> $LOG_FILE
    $ssh $dump_db
    echo "Transferring the dump" >> $LOG_FILE
    copy_file $dump_db_gz $XFER_DIR
    backup_db
    unzip_db $dump_db_gz
    restore_db $dump_db_file
}

upgrade_db () {
    log_echo $local_version_short
    if [[ $remote_version_short -eq 350 && $local_version_short -eq 361 ]]; then
		log_echo "Applying database updates 350-360.sql ..."
        cat /opt/vmware/cloudusagemetering/database/migrate/350-360.sql | \
            $VPGBIN/psql -U $USER $UMDB >>$LOG_FILE 2>&1
    fi
}

start_tomcat () {

    sudo service tomcat start >>$LOG_FILE 2>&1

    if [ $? -ne 0 ]; then
	    log_echo "Failed to start tomcat, aborting ..."
	    exiting $ERROR_STOP_TOMCAT
    fi
}

generate_uuid () {
   log_echo "Generating a new Usage Meter instance unique identifier ..."
   $optDir/scripts/generate-um-uuid.sh --forceUpdate >>$LOG_FILE 2>&1
}

copy_localprops() {
    PROPS_DIR=/usr/share/tomcat/webapps/um/WEB-INF/classes/props
    PROPS_FILE=$PROPS_DIR/local.props
    set +e
    $ssh test -f $PROPS_FILE
    if [[ $? -eq 0 ]]; then
        log_echo "Copying remote encryption seed ..."
        copy_file $PROPS_FILE $PROPS_DIR
    elif [[ $remote_version_short -eq 350 ]]; then
        log_echo "Copying local encryption seed ..."
        cp $PROPS_DIR/local.props.3.5 $PROPS_DIR/local.props
    fi
    set -e
}

finish_migration () {
    set +e
    error_count=`grep -c "ERROR:" $LOG_FILE`
    if [ $error_count -ne 0 ]; then
         log_echo "There were errors during $ACTION. Please consult $LOG_FILE for details."
         log_echo "migrateum failed."
         exiting $ERROR_MIGRATION_FAILED
    fi
    Action="$(tr '[:lower:]' '[:upper:]' <<< ${ACTION:0:1})${ACTION:1}"
    log_echo ""
    log_echo "$Action is complete. See $LOG_FILE for details."
    if [ $ACTION == "migration" ]; then
        log_echo "Please refer to the Release Notes for additional information."
	fi
	if [[ $remote_version_short -ne $local_version_short ]]; then
	    log_echo ""
	    log_echo "Please login to the migrated Usage Meter $local_version_str UI on this host, then Edit and Save for each product."
	log_echo "This step is to discover extra certificate and/or validate product connectivity."
    fi
    set -e
}

opts=`getopt --options hd: --longoptions help,debug: -n $0 -- "$@"`

if [ $? -ne 0 ]; then
    banner
    usage
fi

verbose=1
while true; do
    case "$1" in
	-h | --help               ) banner;                     usage ;;
	-V | --version            ) version ;;
	-q | --quiet              ) verbose=0;                  shift ;;
	-d | --debug              ) verbose=1; set -x;          shift ;;
	-f | --config-file        ) DEFAULT_CONFIG=$2;          shift 2 ;;
	-- ) shift; break ;;
	* ) break ;;
    esac
done

ERROR_LOG_FILE=1
ERROR_SSH_SETUP=2
ERROR_READ_VERSIONS=3
ERROR_COMPARED_VERSIONS=4
ERROR_STOP_TOMCAT=5
ERROR_BACKUP_DIR=6
ERROR_BACKUP_KEYSTORE=7
ERROR_COPY_FILE=8
ERROR_INSTALL_CERTIFICATE=9
ERROR_USAGE=10
ERROR_CONFIG_FILE=11
ERROR_SQL_VALUES=12
ERROR_MIGRATION_FAILED=13
ERROR_TIMEZONE=14
ERROR_LOCK_FILE=15

HOST=$1
if [ "x$HOST" == "x" ]; then
    banner
    echo "You have to supply the IP of the remote Usage Meter to migrate"
    echo
    usage $ERROR_USAGE
fi

if [ "x$DEFAULT_CONFIG" != "x" ]; then
    if [[ ! -f $DEFAULT_CONFIG && ! -r $DEFAULT_CONFIG ]]; then
        echo "No readable config file $DEFAULT_CONFIG available, bailing out"
        usage $ERROR_CONFIG_FILE
    fi
    . $DEFAULT_CONFIG
fi

. $UMSETENV
encryption_dir=$optDir/encryption
db_migration_dir=$optDir/database/migrate
db_dump_gz=$XFER_DIR/umbackup-plain.gz

trap exiting INT
stty -echoctl

remote_host=$HOST
local_host=`/sbin/ifconfig -a  | grep inet | grep -v inet6 | grep -v 127.0.0.1 | cut -d":" -f2 | cut -d" " -f1 | head -n 1`

if [[ "$remote_host" == "$local_host" ]]; then
    echo "The remote host $remote_host and the local host $local_host are the same, exiting ..."
    exit 0
fi

banner
init_logging
init_ssh
check_versions
validate_time_zone
validate_disk_space
check_sql_values
confirm_upgrade
stop_tomcat
prepare_backup 1
copy_certificates
copy_db
upgrade_db
generate_uuid
copy_localprops
start_tomcat
cleanup
finish_migration
exit 0
