#!/bin/bash

echo "Warning: This script is deprecated! Please use other tool to setup keystone for heat." >&2

set +e

KEYSTONE_CONF=${KEYSTONE_CONF:-/etc/keystone/keystone.conf}

# Extract some info from Keystone's configuration file
if [[ -r "$KEYSTONE_CONF" ]]; then
    CONFIG_SERVICE_TOKEN=$(sed 's/[[:space:]]//g' $KEYSTONE_CONF | grep ^admin_token= | cut -d'=' -f2)
    CONFIG_ADMIN_PORT=$(sed 's/[[:space:]]//g' $KEYSTONE_CONF | grep ^admin_port= | cut -d'=' -f2)
fi

SERVICE_TOKEN=${OS_SERVICE_TOKEN:-$CONFIG_SERVICE_TOKEN}
SERVICE_ENDPOINT=${OS_SERVICE_ENDPOINT:-http://127.0.0.1:${CONFIG_ADMIN_PORT:-35357}/v2.0}
if [[ -z "$SERVICE_TOKEN" ]]; then
    echo "No service token found." >&2
    echo "Set SERVICE_TOKEN manually from keystone.conf admin_token." >&2
    exit 1
fi

set_admin_token() {
    alias openstack="openstack --os-token $SERVICE_TOKEN \
                             --os-endpoint $SERVICE_ENDPOINT"
}

unset_admin_token() {
    unalias openstack
}

#### utilities functions merged from devstack to check required parameter is not empty
# Prints line number and "message" in error format
# err $LINENO "message"
function err() {
    local exitcode=$?
    errXTRACE=$(set +o | grep xtrace)
    set +o xtrace
    local msg="[ERROR] ${BASH_SOURCE[2]}:$1 $2"
    echo $msg 1>&2;
    if [[ -n ${LOGDIR} ]]; then
        echo $msg >> "${LOGDIR}/error.log"
    fi
    $errXTRACE
    return $exitcode
}
# Prints backtrace info
# filename:lineno:function
function backtrace {
    local level=$1
    local deep=$((${#BASH_SOURCE[@]} - 1))
    echo "[Call Trace]"
    while [ $level -le $deep ]; do
        echo "${BASH_SOURCE[$deep]}:${BASH_LINENO[$deep-1]}:${FUNCNAME[$deep-1]}"
        deep=$((deep - 1))
    done
}


# Prints line number and "message" then exits
# die $LINENO "message"
function die() {
    local exitcode=$?
    set +o xtrace
    local line=$1; shift
    if [ $exitcode == 0 ]; then
        exitcode=1
    fi
    backtrace 2
    err $line "$*"
    exit $exitcode
}


# Checks an environment variable is not set or has length 0 OR if the
# exit code is non-zero and prints "message" and exits
# NOTE: env-var is the variable name without a '$'
# die_if_not_set $LINENO env-var "message"
function die_if_not_set() {
    local exitcode=$?
    FXTRACE=$(set +o | grep xtrace)
    set +o xtrace
    local line=$1; shift
    local evar=$1; shift
    if ! is_set $evar || [ $exitcode != 0 ]; then
        die $line "$*"
    fi
    $FXTRACE
}

# Test if the named environment variable is set and not zero length
# is_set env-var
function is_set() {
    local var=\$"$1"
    eval "[ -n \"$var\" ]" # For ex.: sh -c "[ -n \"$var\" ]" would be better, but several exercises depends on this
}

#######################################

get_data() {
    local match_column=$(($1 + 1))
    local regex="$2"
    local output_column=$(($3 + 1))
    shift 3

    output=$("$@" | \
           awk -F'|' \
               "! /^\+/ && \$${match_column} ~ \"^ *${regex} *\$\" \
                { print \$${output_column} }")

    echo "$output"
}

get_id () {
    get_data 1 id 2 "$@"
}

get_user() {
    local username=$1

    local user_id=$(get_data 2 $username 1 openstack user list)

    if [ -n "$user_id" ]; then
        echo "Found existing $username user" >&2
        echo $user_id >&2
    else
        echo "Creating $username user..." >&2
        get_id openstack user create $username \
                                    --password="$SERVICE_PASSWORD" \
                                    --project $SERVICE_TENANT \
                                    --email=$username@example.com
    fi
}

add_role() {
    local user_id=$1
    local tenant=$2
    local role_id=$3
    local username=$4

    user_roles=$(openstack user role list $user_id\
                          --project $tenant 2>/dev/null)
    if [ $? == 0 ]; then
        # Folsom
        existing_role=$(get_data 1 $role_id 1 echo "$user_roles")
        if [ -n "$existing_role" ]
        then
            echo "User $username already has role $role_id" >&2
            return
        fi
        openstack role add --project $tenant \
                           --user $user_id \
                           $role_id
    fi
}

create_role() {
    local role_name=$1

    role_id=$(get_data 2 $role_name 1 openstack role list)
    if [ -n "$role_id" ]
    then
        echo "Role $role_name already exists : $role_id" >&2
    else
        openstack role create $role_name
    fi
}

get_endpoint() {
    local service_type=$1

    unset_admin_token
    openstack endpoint show $service_type
    set_admin_token
}

delete_endpoint() {
    local service_type=$1

    local endpoints=$(get_data 4 $service_type 1 openstack endpoint list)

    for endpoint in $endpoints; do
        echo "Removing $service_type endpoint ${endpoint}..." >&2
        openstack endpoint delete "$endpoint" >&2
    done

    if [ -z "$endpoints" ]; then false; fi

}

delete_all_endpoints() {
    while delete_endpoint $1; do
        true
    done
}

delete_service() {
    local service_type=$1

    delete_all_endpoints $service_type

    local service_ids=$(get_data 3 $service_type 1 openstack service list)

    for service in $service_ids; do
        local service_name=$(get_data 1 $service 2 openstack service list)
        echo "Removing $service_name:$service_type service..." >&2
        openstack service delete $service >&2
    done
}

get_service() {
    local service_name=$1
    local service_type=$2
    local description="$3"

    delete_service $service_type

    get_id openstack service create --name=$service_name \
                                   --description="$description" \
                                   $service_type
}

add_endpoint() {
    local service_id=$1
    local url="$2"

    openstack endpoint create --region RegionOne --publicurl "$url" \
        --adminurl "$url" --internalurl "$url" $service_id >&2
}

keystone_setup() {

    unset OS_SERVICE_TOKEN
    unset OS_SERVICE_ENDPOINT
    TENANT_ID=$(get_data 1 project_id 2 openstack token issue)
    die_if_not_set $LINENO TENANT_ID "Fail to get TENANT_ID by 'openstack token issue' "

    set_admin_token

    ADMIN_ROLE=$(get_data 2 admin 1 openstack role list)
    die_if_not_set $LINENO ADMIN_ROLE "Fail to get ADMIN_ROLE by 'openstack role list' "
    SERVICE_TENANT=$(get_data 2 service 1 openstack project list)
    die_if_not_set $LINENO SERVICE_TENANT "Fail to get service tenant 'openstack project list' "
    SERVICE_PASSWORD=${SERVICE_PASSWORD:-$OS_PASSWORD}
    SERVICE_HOST=${SERVICE_HOST:-localhost}

    if [[ "$SERVICE_PASSWORD" == "$OS_PASSWORD" ]]; then
        echo "Using the OS_PASSWORD for the SERVICE_PASSWORD." >&2
    fi

    if [[ "$SERVICE_HOST" == "localhost" ]]; then
        echo "Warning: Endpoints will be registered as localhost, but this usually won't work." >&2
        echo "Set SERVICE_HOST to a publicly accessible hostname/IP instead." >&2
    fi

    echo ADMIN_ROLE $ADMIN_ROLE
    echo SERVICE_TENANT $SERVICE_TENANT
    echo SERVICE_PASSWORD $SERVICE_PASSWORD
    echo SERVICE_TOKEN $SERVICE_TOKEN
    echo SERVICE_HOST $SERVICE_HOST

    HEAT_USERNAME="heat"
    HEAT_USERID=$(get_user $HEAT_USERNAME)
    die_if_not_set $LINENO HEAT_USERID "Fail to get user for $HEAT_USERNAME"
    echo HEAT_USERID $HEAT_USERID
    add_role $HEAT_USERID $SERVICE_TENANT $ADMIN_ROLE $HEAT_USERNAME

    # Create a special role which template-defined "stack users" are
    # assigned to in the engine when they are created, this allows them
    # to be more easily differentiated from other users (e.g so we can
    # lock down these implicitly untrusted users via RBAC policy)
    STACK_USER_ROLE="heat_stack_user"
    create_role $STACK_USER_ROLE

    HEAT_CFN_SERVICE=$(get_service heat-cfn cloudformation \
                       "Heat CloudFormation API")
    add_endpoint $HEAT_CFN_SERVICE "http://$SERVICE_HOST:8000/v1"

    HEAT_OS_SERVICE=$(get_service heat orchestration \
                      "Heat API")
    add_endpoint $HEAT_OS_SERVICE "http://$SERVICE_HOST:8004/v1/%(tenant_id)s"
}

if [[ ${BASH_SOURCE[0]} == ${0} ]]; then
    keystone_setup
fi