#!/bin/bash -e
#
# bondingadmin-setup - Perform Bondingadmin install/update tasks
#

export PATH="/bin:/sbin:/usr/bin:/usr/sbin"

BONDINGADMIN_VERSION="6.8"
BONDINGADMIN_CONF=/etc/bondingadmin/bondingadmin.conf
BONDINGADMIN_VERSION_CACHE='/var/lib/bondingadmin/version'
INFLUX_EPOCH=1      # increment by 1 on change to influxdb config
INFLUX_EPOCH_CACHE='/var/lib/bondingadmin/influx-epoch'
BONDINGADMIN_LOCAL_REPOS='/var/lib/bondingadmin/repos/'


function service_start_time() {
    start_timestamp=$(systemctl show bondingadmin | grep ExecMainStartTimestamp= | cut -d '=' -f 2)
    if [ -n "$start_timestamp" ] ; then
        date -d "$start_timestamp" +%s
    else
        echo 0
    fi
}

# Indicates if a version cache is present and if either the new version differs from the cached version or the service file has been updated.
# This will always be false on the first execution, but may become true on subsequent executions if either condition is met.
function _is_upgrade() {
    if [ -f $BONDINGADMIN_VERSION_CACHE ] ; then
        if [ $(cat $BONDINGADMIN_VERSION_CACHE) !=  $BONDINGADMIN_VERSION ] ; then
            return 0
        fi
    fi

    install_time=$(stat -c %Z /usr/lib/systemd/system/bondingadmin.service)

    if [[ $install_time > $(service_start_time) ]] ; then
        return 0
    fi

    return 1
}

if _is_upgrade ; then
    IS_UPGRADE=true
else
    IS_UPGRADE=
fi

function is_upgrade() {
    test ! -z $IS_UPGRADE
}

# Indicates that the version cache is missing, meaning this is the first time the script is being executed.
# This condition will only be true once, during the initial run of the script.
function _is_new_install() {
    test ! -f $BONDINGADMIN_VERSION_CACHE
}

if _is_new_install ; then
    IS_NEW_INSTALL=true
else
    IS_NEW_INSTALL=
fi

function is_new_install() {
    test ! -z $IS_NEW_INSTALL
}


function get_last_version() {
    if [ -f $BONDINGADMIN_VERSION_CACHE ] ; then
        cat $BONDINGADMIN_VERSION_CACHE
    fi
}


function test_postgresql_state() {
    version=$1
    desired_state=$2

    state=$(systemctl show postgresql@${version}-main.service | grep 'SubState=' | cut -d '=' -f 2)

    test "$state" = "$desired_state"
}


function wait_for_postgresql_state() {
    version=$1
    desired_state=$2

    echo "Waiting for postgresql ${version} to be ${desired_state}."
    while ! test_postgresql_state $version $desired_state ; do
        sleep 1
    done
}


function postgresql_upgrade() {
    # Check for 11 cluster and upgrade to 13
    pg_lsclusters | tail -n+2 | grep -qe '^11' && {
        pg_dropcluster --stop 13 main
        wait_for_postgresql_state 13 dead
        systemctl stop postgresql
        wait_for_postgresql_state 11 dead
        pg_upgradecluster -v 13 11 main
        pg_dropcluster 11 main
        systemctl start postgresql
        wait_for_postgresql_state 13 running
    } ||:
}


function get_last_influx_epoch() {
    if [ -f $INFLUX_EPOCH_CACHE ] ; then
        cat $INFLUX_EPOCH_CACHE
    fi
}


function influx_config_updated() {
    cache=$(get_last_influx_epoch)

    if [[ $cache < $INFLUX_EPOCH ]] || [ -z $cache ] ; then
        return 0
    fi

    return 1
}


function api_token_exists() {
    token=$(ba get multapplied_api_token)

    if [ -z $token ] ; then
        return 1
    fi

    return 0
}

function get_random() {
    len=$1
    test -z $len && len=128
    pwgen -1 -s $len
}


#
# Main
#

# Set hostname
#
if [ ! -f $BONDINGADMIN_CONF ] ; then
    cat >&2 <<EOF
The bondingadmin config file is not present at $BONDINGADMIN_CONF. This can
happen if the package installation failed.

Please contact support.
EOF
    exit 1
fi

hostname=$(echo $(grep mgmt_server_url /etc/bondingadmin/bondingadmin.conf | head -n 1 | cut -d '=' -f 2))
if [ -z "$hostname" ] ; then
    echo "The mgmt_server_url option is empty in $BONDINGADMIN_CONF" >&2
    exit 1
fi
if [ "$(hostname -f)" != "$hostname" ] ; then
    echo "Updating hostname to $hostname"
    hostnamectl set-hostname $hostname
fi


# Ensure the locale is en_US.UTF-8
#
echo "Checking for valid locale"
if ! grep -q 'LANG=.*\.UTF-8' /etc/default/locale; then
    echo 'Setting default locale to en_US.UTF-8...'
    sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
    locale-gen
    update-locale LANG="en_US.UTF-8" LANGUAGE="en_US:en"
    echo 'You MUST run locale and ensure your current settings are a UTF-8 locale, and if not, restart your session.'
    echo 'Once your locale is confirmed to be UTF-8, run bondingadmin-setup again.'
    exit 1
fi


# Remove legacy files
#
rm -rf /etc/bondingadmin/iso.d
rm -rf /var/lib/bondingadmin/preseed*


# Set up postfix
#
if [ ! -f /etc/postfix/main.cf ] ; then
    cat <<EOF | debconf-set-selections
postfix postfix/mailname string $hostname
postfix postfix/main_mailer_type string 'Internet Site'
EOF
    DEBIAN_FRONTEND=noninteractive apt-get install -f -y postfix
fi


# Create CA if it does not exist
#
if [ ! -f /var/lib/bondingadmin/ca/index ] ; then
    make-bondingadmin-ca
fi
yes y | make-server-cert
make-bondingadmin-openvpn


# Create salt node key files if necessary
#
test ! -f /etc/bondingadmin/salt-config/states/node/root_unauthorized_keys && touch /etc/bondingadmin/salt-config/states/node/root_unauthorized_keys
test ! -f /etc/bondingadmin/salt-config/states/node/root_authorized_keys && touch /etc/bondingadmin/salt-config/states/node/root_authorized_keys

# Remove the local repo if it exists
if [ -d $BONDINGADMIN_LOCAL_REPOS ] ; then
    rm -rf $BONDINGADMIN_LOCAL_REPOS
fi

# Sysctl
#
/sbin/sysctl -p /etc/sysctl.d/firewall.conf ||:


# Tempfile config
#
/bin/systemd-tmpfiles --create


# Services
#
BONDINGADMIN_ENABLE_SERVICES="\
    aggfail.service \
    bondingadmin-collectd \
    bondingadmin-flowcollector-dnat.service \
    bondingadmin-nftables.service \
    bondingadmin-nftables-manager.path \
    bondingadmin-salt-access.service \
    bondingadmin-salt-master.service \
    bondingadmin-salt-minion.service \
    bondingadmin-uwsgi.service \
    bondingadmin.service \
    errorreporter.service \
    homestead.service \
    huey.service \
    influxmux.service \
    mgmtvpn.service \
    mgmtvpn-event-handler.service \
    nodeupdates.service \
    sync-base-isos.timer \
    bondingadmin-cleanup-config-updates.timer \
    bondingadmin-backup-remote.timer \
    bondingadmin-update-crl.timer \
    bondingadmin-push-backup.timer \
    bondingadmin-remove-old-primary-backups.timer \
    bondingadmin-update-outage-stats.timer \
    bondingadmin-salt-state-apply.timer \
"
BONDINGADMIN_DISABLE_SERVICES="\
    collectd.service \
    salt-master.service \
    uwsgi.service \
"
BONDINGADMIN_STOP_ON_UPGRADE_SERVICES="\
    bondingadmin.service \
    bondingadmin-fetch-latest-bonding-version.timer \
    bondingadmin-salt-master.service \
    sync-base-isos.timer \
"
BONDINGADMIN_RESTART_ON_UPGRADE_SERVICES="\
    bondingadmin-nftables.service \
    nginx.service \
"
BONDINGADMIN_TIMERS="\
    bondingadmin-update-crl.timer \
    bondingadmin-push-backup.timer \
    bondingadmin-backup-remote.timer \
    bondingadmin-salt-state-apply.timer \
    bondingadmin-update-outage-stats.timer \
    bondingadmin-cleanup-config-updates.timer \
    bondingadmin-remove-old-primary-backups.timer \
    bondingadmin-fetch-latest-bonding-version.timer
"

systemctl daemon-reload
systemctl disable --now $BONDINGADMIN_DISABLE_SERVICES ||:
systemctl enable $BONDINGADMIN_ENABLE_SERVICES

if is_upgrade ; then
    if ! is_new_install ; then
        echo "Stopping bondingadmin services"
        systemctl stop $BONDINGADMIN_STOP_ON_UPGRADE_SERVICES
    fi
fi

# Create secrets
#
if [ ! -f "/etc/bondingadmin/django-secret-key" ]; then
    echo $(get_random) > /etc/bondingadmin/django-secret-key
fi

# Influx can only have 56 character passwords (as per bcrypt), so limit
# the get_random function to that.
#

if [ ! -f "/etc/bondingadmin/influx-root-pw" ]; then
    echo $(get_random 56) > /etc/bondingadmin/influx-root-pw
fi

# LEGACY-REMOVE-WHEN: databases are separated by space
if [ ! -f "/etc/bondingadmin/influx-graph-pw" ]; then
    echo $(get_random 56) > /etc/bondingadmin/influx-graph-pw
fi

# Create the password for the collectd postgresql user
#
if [ ! -f "/etc/bondingadmin/postgres-collectd-pw" ]; then
    echo $(get_random 63) > /etc/bondingadmin/postgres-collectd-pw
fi

if [ ! -f "/etc/bondingadmin/local-www-key" ]; then
    echo $(get_random) > /etc/bondingadmin/local-www-key
fi


# Collectd
#
COLLECTD_PASS=$(cat /etc/bondingadmin/postgres-collectd-pw)
COLLECTD_POSTGRES_TEMPLATE="/usr/share/bondingadmin/default/postgresql.conf.in"
COLLECTD_CONF_DIR="/etc/bondingadmin/collectd/collectd.conf.d"
COLLECTD_CONF_FILE="$COLLECTD_CONF_DIR/postgresql.conf"
install -d $COLLECTD_CONF_DIR
sed s/{{PASSWORD}}/$COLLECTD_PASS/ "$COLLECTD_POSTGRES_TEMPLATE" > "$COLLECTD_CONF_FILE"


# Database
#
if is_upgrade ; then
    last_version=$(get_last_version)
    new_version=$(ba bondingadmin_version)
    if [ "$last_version" != "$new_version" ] ; then
        echo "Check database upgrade"
        postgresql_upgrade

        # Fix missing flowcollection migration record if necessary
        # Remove this once 6.8 is fully deployed
        cat << EOF | su - postgres -c "psql bondingadmin"
            INSERT INTO django_migrations (app, name, applied)
            SELECT 'flowcollection', '0005_change_space_fk_on_delete_to_protect', now()
                WHERE NOT EXISTS (
                    SELECT 1
                    FROM django_migrations
                    WHERE name = '0005_change_space_fk_on_delete_to_protect'
                )
                AND EXISTS (
                    SELECT 1
                    FROM django_migrations
                    WHERE name = '0006_remove_previous_nfprobe_maxflows_max_value'
                );
EOF
    fi
fi

if is_new_install || is_upgrade ; then

    echo "Performing database migrations"
    /usr/share/bondingadmin/initdb.sh

    echo "Initializing needed database objects"
    ba initialize_objects

    echo "Saving persistent object configurations"
    ba update_configs

fi


# Laywire
#
laywire-management-setup

# Salt
#
if is_upgrade ; then
    echo "Updating salt pillars"
    ba update_salt_pillars

    echo "Configuring bondingadmin-salt-master"
    /usr/share/bondingadmin/initsalt.sh
fi


# Check for major version upgrade
#
if is_upgrade ; then
    last_version=$(get_last_version)
    new_version=$(ba bondingadmin_version)
    if [ "$last_version" != "$new_version" ] ; then
        # Major version upgrade. Ensure the update message is seen by users
        echo "Major upgrade"
        echo "Resetting update message"
        ba reset_seen_update_message

        # Remember that this version was installed
        echo "Caching bondingadmin version"
        ba bondingadmin_version > $BONDINGADMIN_VERSION_CACHE
    fi
fi


# Nginx
#
rm -f /etc/nginx/sites-enabled/default
ln -sf /etc/nginx/sites-available/bondingadmin /etc/nginx/sites-enabled/bondingadmin

# Create the default influx proxy conf if missing
if [ ! -f "/etc/bondingadmin/influxdb_proxy.conf" ]; then
    echo "proxy_pass http://127.0.0.1:8086/;" > /etc/bondingadmin/influxdb_proxy.conf
fi

# Set the download srv if not set
if [ ! -f /etc/bondingadmin/nginx/download.conf ]; then
    echo 'set $download download.multapplied.net;' > /etc/bondingadmin/nginx/download.conf
fi

# Set the Nginx resolver address if not set
if [ ! -f /etc/bondingadmin/nginx/resolver.conf ]; then
    echo 'resolver 1.1.1.1;' > /etc/bondingadmin/nginx/resolver.conf
fi


# Services restart and get latest bonding version
#
if is_upgrade ; then
    echo "Starting bondingadmin services"
    systemctl restart $BONDINGADMIN_STOP_ON_UPGRADE_SERVICES $BONDINGADMIN_RESTART_ON_UPGRADE_SERVICES

    echo "Fetch bonding version"
    if [ -n "$BONDING_RELEASE" ] ; then
        ba set repository_release $BONDING_RELEASE
    fi
    ba update_bonding_version
    systemctl start bondingadmin-fetch-latest-bonding-version.service
fi

echo "Starting Timers"
systemctl enable --now $BONDINGADMIN_TIMERS

# InfluxDB
#
mkdir -p /var/log/influxdb
chown -R influxdb:influxdb /var/log/influxdb

# Write a sensible influxdb configuration file (combines our settings with the default settings)
/usr/bin/influxd config -config /etc/bondingadmin/influxdb/bondingadmin.conf > /etc/influxdb/bondingadmin.conf

if influx_config_updated ; then
    echo "Setting up influxdb"
    /usr/lib/bondingadmin/influxconfig

    echo "Caching influxdb epoch"
    echo $INFLUX_EPOCH > $INFLUX_EPOCH_CACHE
fi


# Julius
#
if api_token_exists ; then
    # Sync pricing labels from julius
    echo "Syncing pricing labels"
    ba sync_pricing_labels || echo "Cannot sync pricing labels."
    if is_new_install; then
        echo -e "Setting up API user for Julius"
        ba setup_julius_api_user
        echo -e "Setting up OAuth"
        ba fetch_oauth_client_authentication
    fi
    # Clear ISOs for old spaces and regenerate new ones for valid spaces
    echo "Regenerating the bonding ISO images"
    ba regenerate_isos || echo "Cannot regenerate bonding ISO images."

    # Check and send ssh public key used for backups
    ba sendmgmtpublickey || echo "Cannot send management public key."
else
    echo "To finish setup, please set the API token (ba set multapplied_api_token) and try again."
fi


# Backups
#
if is_new_install ; then
    echo -e "Setting up backups"
    backup-bondingadmin
    ba remotebackup
    ba remotebackupstatus
fi
