#!/bin/bash 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 keystone="keystone --token $SERVICE_TOKEN \ --endpoint $SERVICE_ENDPOINT" } unset_admin_token() { unalias keystone } #### 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 ${SCREEN_LOGDIR} ]]; then echo $msg >> "${SCREEN_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_column_num() { local name=$1 shift $@ | awk -F'|' "NR == 2 { for (i=2; i&2 echo $user_id else echo "Creating $username user..." >&2 get_id keystone user-create --name=$username \ --pass="$SERVICE_PASSWORD" \ --tenant_id $SERVICE_TENANT \ --email=$username@example.com fi } add_role() { local user_id=$1 local tenant=$2 local role_id=$3 local username=$4 # The keystone argument format changed between essex and folsom # so we use the fact that the folsom keystone version has a new # option "user-role-list" to detect we're on that newer version # This also allows us to detect when the user already has the # requested role_id, preventing an error on folsom user_roles=$(keystone user-role-list \ --user_id $user_id\ --tenant_id $tenant 2>/dev/null) die_if_not_set $LINENO user_roles "Fail to get user_roles for tenant($tenant) and user_id($user_id)" 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 keystone user-role-add --tenant_id $tenant \ --user_id $user_id \ --role_id $role_id else # Essex keystone user-role-add --tenant_id $tenant \ --user $user_id \ --role $role_id fi } create_role() { local role_name=$1 role_id=$(get_data 2 $role_name 1 keystone role-list) if [ -n "$role_id" ] then echo "Role $role_name already exists : $role_id" >&2 else keystone role-create --name $role_name fi } get_endpoint() { local service_type=$1 unset_admin_token keystone endpoint-get --service $service_type set_admin_token } delete_endpoint() { local service_type=$1 case $service_type in volume) urlsuffix='\\\\$\\\\(tenant_id)s';; orchestration) urlsuffix='%[(]tenant_id[)]s';; # cloudformation has no hash suffix *) urlsuffix='' esac local url=$(get_data 1 "${service_type}[.]publicURL" 2 \ get_endpoint $service_type 2>/dev/null | \ sed -r "s/[a-f0-9]{32}/$urlsuffix/") if [ -n "$url" ]; then local endpoints=$(get_data 3 $url 1 keystone endpoint-list) for endpoint in $endpoints; do echo "Removing $service_type endpoint ${endpoint}..." >&2 keystone endpoint-delete "$endpoint" >&2 done if [ -z "$endpoints" ]; then false; fi else 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 keystone service-list) for service in $service_ids; do local service_name=$(get_data 1 $service 2 keystone service-list) echo "Removing $service_name:$service_type service..." >&2 keystone service-delete $service >&2 done } get_service() { local service_name=$1 local service_type=$2 local description="$3" delete_service $service_type get_id keystone service-create --name=$service_name \ --type=$service_type \ --description="$description" } add_endpoint() { local service_id=$1 local url="$2" keystone endpoint-create --region RegionOne --service_id $service_id \ --publicurl "$url" --adminurl "$url" --internalurl "$url" >&2 } keystone_setup() { # Make sure we can use keystone command without OS_SERVICE_TOKEN and OS_SERVICE_ENDPOINT # credential, because we need to use keystone endpoint-get command below, and the # keystone endpoint-get command can not run correctly # using OS_SERVICE_TOKEN and OS_SERVICE_ENDPOINT credential. unset OS_SERVICE_TOKEN unset OS_SERVICE_ENDPOINT TENANT_ID=$(get_data 1 tenant_id 2 keystone token-get) die_if_not_set $LINENO TENANT_ID "Fail to get TENANT_ID by 'token-get' " set_admin_token ADMIN_ROLE=$(get_data 2 admin 1 keystone role-list) die_if_not_set $LINENO ADMIN_ROLE "Fail to get ADMIN_ROLE by 'keystone role-list' " SERVICE_TENANT=$(get_data 2 service 1 keystone tenant-list) die_if_not_set $LINENO SERVICE_TENANT "Fail to get service tenant 'keystone tenant-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 "Endpoints will be registered as localhost, but this usually won't work." echo "Set SERVICE_HOST to a publically accessible hostname/IP instead." 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