#!/bin/sh

prefix=/usr/local
# Include utilities. Use installed version if available and
# use build version if it isn't.
if [ -e "${prefix}/share/kea/scripts/admin-utils.sh" ]; then
    . ${prefix}/share/kea/scripts/admin-utils.sh
else
    . /usr/obj/ports/kea-1.6.0/kea-1.6.0/src/bin/admin/admin-utils.sh
fi

# Need a path for temporary files created during upgrade data migration
# Use the state directory in the install path directory if it exists, otherwise
# use the build tree
if [ -e "/var/lib/kea" ]; then
    temp_file_dir="/var/lib/kea"
else
    temp_file_dir="/usr/obj/ports/kea-1.6.0/kea-1.6.0/src/share/database/scripts/cql"
fi

cqlargs=$@

# Ensures the current schema version is 2.0. If not it exits.
check_version() {
    version=$(cql_version $cqlargs)

    if [ "${version}" != "2.0" ]; then
        printf "This script upgrades 2.0 to 3.0. Reported version is %s. Skipping upgrade.\n" "${version}"
        exit 0
    fi
}

# Peforms the schema changes from 2.0 to 3.0
update_schema() {
    cqlsh $cqlargs <<EOF
-- This line starts database upgrade to version 3.0

-- Add a column holding leases for user context.
ALTER TABLE lease4 ADD user_context VARCHAR;
ALTER TABLE lease6 ADD user_context VARCHAR;

-- -----------------------------------------------------
-- Table logs (it is used by forensic logging hook library)
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS logs (
    timeuuid TIMEUUID,  -- creation timeuuid, use dateOf() to get timestamp
    address VARCHAR,    -- address or prefix
    log VARCHAR,        -- the log itself
    PRIMARY KEY ((timeuuid))
);

-- Create search index for logs table
CREATE INDEX IF NOT EXISTS logsindex ON logs (address);

-- Add auth_key for storing keys for DHCPV6 reconfigure.
ALTER TABLE host_reservations ADD auth_key VARCHAR;

-- Cql requires primary keys in the WHERE here.
DELETE FROM schema_version WHERE version=2;
INSERT INTO schema_version (version, minor) VALUES(3, 0);

-- This line concludes database upgrade to version 3.0
EOF

    if [ "$?" -ne 0 ]
    then
        echo Schema udpate FAILED!
        exit 1
    fi
}

# Function to delete temporary migration files
clean_up() {
    # clean up the files
    if [ -e "$export_file" ]
    then
        rm $export_file
    fi

    if [ -e "$update_file" ]
    then
        rm $update_file
    fi
}

# Function to clean up and exit the script gracefully
#
# Called by migrate_host_data()
#
# Parameters:
# status - integer value to pass to sh:exit
# explanation - "quoted" text message to emit to stdout
exit_now() {
    status=$1;shift
    explanation=$1

    clean_up
    if [ "$status" -eq 0 ]
    then
        echo "Data Migration SUCCESS! $explanation"
    else
        echo "Data Migration FAILURE! $explanation"
    fi

    exit $status
}

# Function adds a column to the global, $update_cols if needed
#
# Called by migrate_host_data() to determine if the given column
# value needs to be updated, and if so appends CQL update text
# to a global string accumulator, $update_cols.
#
# The accumlator text is of the form:
#
#  "<column_name>=<column value>{,<column_name>=<column_value>,..}"
#
# Parameters:
# val - current numerical value of the subnet ID column in question
# col - column name of the column in question
#
check_column() {
    local val="$1";shift
    local col="$1"
    local old_id="0"
    local new_id="-1"
    local comma=""

    # If the current value equals the value to be replaced
    # add it to the accumulator
    if [ "$val" = "$old_id" ]
    then
        # If the accumulator isn't empty, we need a comma
        if [ ! -z "$update_cols" ]
        then
            comma=","
        fi

        update_cols="$update_cols$comma $col = $new_id"
    fi
}

# This function converts subnet ID columns in of existing host_reservations
# from 0 to 0xFFFFFFFF (SUBNET_ID_UNUSED). To do this it first creates an
# excerpt from host_reservations containing one row for each reservation
# with the following columns:
#
#  id (primary key)
#  host_ipv4_subnet_id
#  host_ipv6_subnet_id
#  option_subnet_id
#
# The excerpt is captured in a temporary file, the "export" file.
#
# Next, it iterates over the export file emitting a CQL update statement
# for each row that needs at least one of the columns updated. In other
# words, if at least one of the subnet ID columns in a row is 0, an
# update statement for that row will be emitted.  The update statements
# are captured in a second temprory file, the "update" file.
#
# After exhausting the export file, the update file is submitted to
# cqlsh for execution.
#
# No parameters.
migrate_host_data() {
    export_file="$temp_file_dir/cql_export.csv"
    update_file="$temp_file_dir/cql_update.cql"

    clean_up

    # Fetch host_reservation data so we have primary key, plus subnet id values
    echo "Exporting host_reservation data to $export_file ..."
    query="COPY host_reservations \
        (id, host_ipv4_subnet_id, host_ipv6_subnet_id, option_subnet_id)\
        TO '$export_file'"

    cqlsh $cqlargs -e "$query"
    if [ "$?" -ne 0 ]
    then
        exit_now 1 "Cassandra export failed! Could not migrate data!"
    fi

    # Strip the carriage returns that CQL insists on adding.
    if [ -e "$export_file" ]
    then
        cat $export_file | tr -d '\015' > $export_file.2
        mv $export_file.2 $export_file
    else
        # Shouldn't happen but then again we're talking about CQL here
        exit_now 1 "Cassandra export file $export_file is missing?"
    fi

    # Iterate through the exported data, accumulating update statements,
    # one for each reservation that needs updating.  We should have one
    # host per line.
    line_cnt=0;
    update_cnt=0;

    while read -r line
    do
        line_cnt=$((line_cnt + 1));
        update_cols=""
        xIFS="$IFS"
        IFS=$','

        i=1
        # Parse the column values
        for val in $line
        do
            case $i in
            1)
                host_id="$val"
                ;;
            2)
                check_column $val host_ipv4_subnet_id
                ;;
            3)
                check_column $val host_ipv6_subnet_id
                ;;
            4)
                check_column $val option_subnet_id
                ;;
            *)
                # We're going to assume that since any error is fatal
                exit_now 1 "Line# $line_cnt, too many values, wrong or corrupt file"
                ;;
            esac
            i=$((i + 1))
        done

        if [ "$i" -ne 5 ]
        then
            # We're going to assume that since any error is fatal
            exit_now 1 "Line# $line_cnt, too few values, wrong or corrupt file"
        fi

        # If any of the current host's columns need to be replace, append an update for it
        if [ ! -z "$update_cols" ]
        then
            echo "update host_reservations set $update_cols where id = $host_id;" >> $update_file
            update_cnt=$((update_cnt + 1))
        fi

        IFS="$xIFS"
    done <  $export_file

    # If we didn't record any updates, then hey, we're good to go!
    if [ "$update_cnt" -eq 0 ]
    then
        exit_now 0 "Completed successfully: No updates were needed"
    fi

    # We have at least one update in the update file, so submit it # to cqlsh.
    echo "$update_cnt update statements written to $update_file"
    echo "Running the updates..."
    cqlsh $cqlargs -f "$update_file"
    if [ "$?" -ne 0 ]
    then
        exit_now 1 "Cassandra updates failed"
    fi

    exit_now 0 "Updated $update_cnt of $line_cnt records"
}

check_version
update_schema
migrate_host_data
