#!/bin/bash # vi:set ts=3 sw=3: # == Module: Open Telekom Cloud CLI 0.8.x # # Manage OTC via Command Line # # Provides a shell/curl/jq based alternative to the # python-openstackclient tools, exposing a lot o standard OpenStack functions # as well as some custom OTC interfaces. # # === Parameters # # === Variables # # Recognized variables from environment: # # BANDWIDTH # set to default value "25" if unset # VOLUMETYPE # set to default value "SATA" if unset # APILIMIT # Either an integer, limiting the number of API results per call, or "off", removing limits. # If unset, default limits are used (different among API calls), can be overridden by --limit NNN. # MAXGETKB # The maximum size for API (GET) response size that the API gateway allows without cutting it # if off (and thus breaking it for https). otc.sh tries to auto-paginate here ... # # ... and the standard OS_ variables that you also need for the OpenStack python tools # If unset, these are looked for in standard places # ~/.ostackrc.$OTC_TENANT, ~/.ostackrc, ~/novarc, ~/openrc # # === Examples # # Examples # See help ... # # === Authors # # Zsolt Nagy # Kurt Garloff # Christian Kortwich # # === Copyright # # Copyright 2016 - 2017 T-Systems International GmbH # License: CC-BY-SA-4.0 # [ "$1" = -x ] && shift && set -x VERSION=0.8.32 # Get Config #################################################################### warn_too_open() { PERM=$(stat -Lc "%a" "$1") if test "${PERM:2:1}" != "0"; then echo "#Warning: $1 permissions too open ($PERM)" 1>&2 fi } may_read_env_files() { for file in "$@"; do if test -r "$file"; then echo "#Note: Reading environment from $file ..." 1>&2 source "$file" warn_too_open "$file" if test -n "$OS_PASSWORD" -a -n "$OS_USERNAME"; then break; fi fi done } otc_dir="$(dirname "$0")" # Parse otc-tools specific config file (deprecated) if test -r ~/.otc_env.sh; then source ~/.otc_env.sh warn_too_open ~/.otc_env.sh #else # echo "#Note: No ~/.otc_env.sh found, no defaults for ECS creation" 1>&2 fi # Parse standard OpenStack environment setting files if needed if test -z "$OS_PASSWORD" -o -z "$OS_USERNAME"; then may_read_env_files ~/.ostackrc.$OTC_TENANT ~/.ostackrc ~/.novarc ~/novarc fi # Defaults if test -z "$OS_USER_DOMAIN_NAME"; then export OS_USER_DOMAIN_NAME="${OS_USERNAME##* }" fi if test -n "$OS_AUTH_URL"; then REG=${OS_AUTH_URL#*://} REG=${REG#*.} if test -z "$OS_REGION_NAME"; then export OS_REGION_NAME=${REG%%.*} fi #echo "OS_REGION_NAME: $OS_REGION_NAME" #REG=${REG#*.} export OS_CLOUD_ENV=${REG%%.*} fi if test -z "$OS_PROJECT_NAME"; then if test -n "$OS_TENANT_NAME"; then export OS_PROJECT_NAME="$OS_TENANT_NAME" else export OS_PROJECT_NAME="$OS_REGION_NAME" fi fi if test -z "$MAXGETKB"; then export MAXGETKB=251 fi # S3 environment if test -z "$S3_ACCESS_KEY_ID" -a -r ~/.s3rc.$OTC_TENANT; then echo "#Note: Reading S3 environment from ~/.s3rc.$OTC_TENANT ..." 1>&2 source ~/.s3rc.$OTC_TENANT warn_too_open ~/.s3rc.$OTC_TENANT fi if test -z "$S3_ACCESS_KEY_ID" -a -r ~/.s3rc; then echo "#Note: Reading S3 environment from ~/.s3rc ..." 1>&2 source ~/.s3rc warn_too_open ~/.s3rc fi # Alternatively parse CSV as returned by OTC if test -r ~/credentials-$OTC_TENANT.csv; then CRED=credentials-$OTC_TENANT.csv else CRED=credentials.csv fi if test -z "$S3_ACCESS_KEY_ID" -a -r ~/$CRED; then echo -n "#Note: Parsing S3 $CRED ... " 1>&2 LN=$(tail -n1 ~/$CRED | sed 's/"//g') UNM=${LN%%,*} LN=${LN#*,} if test "$UNM" = "$OS_USERNAME"; then echo "succeeded" 1>&2 export S3_ACCESS_KEY_ID="${LN%,*}" export S3_SECRET_ACCESS_KEY="${LN#*,}" else echo "user mismatch \"$UNM\" != \"$OS_USERNAME\"" 1>&2 fi warn_too_open ~/$CRED fi # ENVIRONMENT SETTINGS #################################################################### unset FLAVORSET # Defaults if test -z "$BANDWIDTH"; then BANDWIDTH=25; fi if test -z "$VOLUMETYPE"; then if [[ "$INSTANCE_TYPE" == e1.* ]] || [[ "$INSTANCE_TYPE" == e2.* ]] || [[ "$INSTANCE_TYPE" == hl1.* ]]; then #VOLUMETYPE=co-p1 VOLUMETYPE="uh-l1" else VOLUMETYPE="SATA" fi fi test -n "$S3_HOSTNAME" || export S3_HOSTNAME=obs.otc.t-systems.com if test -n "$OS_AUTH_URL"; then if [[ "$OS_AUTH_URL" = *"/v3" ]]; then export IAM_AUTH_URL="$OS_AUTH_URL/auth/tokens" else export IAM_AUTH_URL="$OS_AUTH_URL/tokens" fi else export IAM_AUTH_URL="https://iam.${OS_REGION_NAME}.otc.t-systems.com/v3/auth/tokens" fi if test -z "$TMPDIR"; then TMPDIR=/dev/shm; fi if test ! -d "$TMPDIR"; then TMPDIR=/tmp; fi PRIMARYDNS=${PRIMARYDNS:-100.125.4.25} SECDNS=${SECDNS:-9.9.9.9} # REST call curl wrappers ########################################################### # Output HTML dumphtml() { echo "$@" | sed 's/<[^>]*>//g' } is_html() { echo "$1" | grep '<[hH][tT][mM][lL]' 2>&1 >/dev/null } is_html_err() { if ! is_html "$1"; then return 1; fi echo "$1" | grep '<[tT][iT][tT][lL][eE]> *[45][012][0-9] [A-Z]' 2>&1 >/dev/null } uriencodenoteq() { local OUT OUT="${*//%/%25}" OUT="${OUT// /%20}" OUT="${OUT//\?/%3F}" OUT="${OUT//&/%26}" OUT="${OUT//\"/%22}" OUT="${OUT//\//%2F}" OUT="${OUT//,/%2C}" OUT="${OUT//;/%3B}" OUT="${OUT//!/%21}" OUT="${OUT//:/%3A}" OUT="${OUT//\#/%23}" echo "$OUT" } uriencode() { OUT="$(uriencodenoteq $*)" OUT="${OUT//=/%3D}" echo "$OUT" } hashtoken() { while read ln; do if ! echo "$ln" | grep '\-Token' >/dev/null 2>&1; then continue; fi echo "$ln" | sed 's@^.*\-Token: MII\([^ ]*\).*$@MII\1@g' | md5sum | awk '{ print $1; }' #if test ${PIPESTATUS[1]} != 0; then echo "ERROR IN SED" 1>&2; exit 2; fi done } # Generic wrapper to facilitate debugging docurl() { local ANS RC TMPHDR # TODO: Alternatively use -w "%{http_code}\n" TMPHDR=`mktemp $TMPDIR/curlhdr.$$.XXXXXXXXXX` #TMPHDR=`mktemp -u $TMPDIR/curlhdr.$$.XXXXXXXXXX` #mkfifo -m 0600 "$TMPHDR" if test -n "$DEBUG"; then TKNDEB=$(echo "$@" | hashtoken) echo "#DEBUG: docurl $INS $@" | sed -e "s/-Token: MII[^ ]*/-Token: MII$TKNDEB/g" -e 's/"password": "[^"]*"/"password": "SECRET"/g' 1>&2 if test "$DEBUG" = "2"; then ANS=`curl $INS -D $TMPHDR "$@"` RC=$? TKNDEB=$(cat $TMPHDR | hashtoken) echo -n "#DEBUG: Header" 1>&2 cat $TMPHDR | sed "s/X-Subject-Token: MII.*\$/X-Subject-Token: MII$TKNDEB/" 1>&2 else ANS=`curl $INS -D $TMPHDR "$@"` RC=$? fi TKNDEB=$(echo "$ANS" | hashtoken) echo "#DEBUG: ($RC) $ANS" | sed "s/X-Subject-Token: MII.*\$/X-Subject-Token: MII$TKNDEB/" 1>&2 #echo "$ANS" else ANS=`curl $INS -D $TMPHDR "$@"` RC=$? #echo "$ANS" fi HDRCODE=$(cat "$TMPHDR" | head -n1 | grep '^HTTP') HDRCODE=$(echo "$HDRCODE" | sed 's/^HTTP\/[0-9.]* \([0-9]*\) .*$/\1/') #echo "$HDRCODE" 1>&2 CODE=$HDRCODE rm $TMPHDR if test $RC != 0; then echo "$ANS" 1>&2; return $RC else # Sometimes function is called with '-i' and the response contains HTTP headers # For a proper response processing get the pure BODY local BODY HDR2 CODE2 JBODY ERROR ECODE MSG HDR=$(echo "$ANS" | head -n1 | grep '^HTTP') if test -n "$HDR"; then # strip off headers on the first empty string. It is still not 100% solution, # but there is no way to access CURLOPT_HEADERFUNCTION from bash # OS X sed seems to be not handling \r \s correctly, so one with printf should be added BODY=$(echo "$ANS" | sed "1,/^\s*$(printf '\r')*$/d") else BODY=$ANS fi HDR2=$(echo "$BODY" | head -n1 | grep '^HTTP') CODE2=$(echo "$HDR2" | sed 's/^HTTP\/[0-9.]* \([0-9]*\) .*$/\1/') if is_html_err "$BODY"; then dumphtml "$BODY" 1>&2; return 9 fi if echo "$BODY" | grep '{' >/dev/null; then JBODY="{${BODY#*\{}"; else JBODY=""; fi ERROR=$(echo "$JBODY" | jq '.error' 2>/dev/null | tr -d '"') if test "$ERROR" != "null" -a -z "$QUIETERR"; then echo "$ERROR" 1>&2; fi CODE=$(echo "$JBODY"| jq '.code' 2>/dev/null) ECODE=$(echo "$JBODY"| jq '.error.error_code' 2>/dev/null) if test "$CODE" == "null"; then CODE=$(echo "$JBODY"| jq '.[] | .code' 2>/dev/null) if test "${CODE:0:4}" == "null" -o "${CODE:0:2}" == "[]"; then CODE=""; fi fi #echo -e "docurl DEBUG: HDR=$HDR\nHDR2=$HDR2\nBODY=$BODY\nERROR=$ERROR\nCODE=$CODE\nECODE=$ECODE" 1>&2 #if test -z "$CODE" -o "$CODE" == "null" && test "$ECODE" != "null"; then CODE="$ECODE"; fi if test "$INDMS" != 1; then MSG=$(echo "$JBODY"| jq '.message' 2>/dev/null) TYP=$(echo "$JBODY"| jq '.type' 2>/dev/null) DET=$(echo "$JBODY"| jq '.detail' 2>/dev/null) if echo "$MSG" | grep "Token need to refresh" >/dev/null 2>&1 && test -z "$INAUTH"; then echo "#Note: $MSG -> Retry" 1>&2 local OLDDC=$DISCARDCACHE DISCARDCACHE=1 if test -n "$PRJ_ID_UNSET"; then unset OS_PROJECT_ID; fi getIAMToken $REQSCOPE || exit $? DISCARDCACHE=$OLDDC declare -a ARGS for a in "$@"; do ARGS[${#ARGS[@]}]=$(echo $a | sed "s/Token: MII[^ ]*/Token: $TOKEN/"); done docurl "${ARGS[@]}" return fi if test -n "$MSG" -a "$MSG" != "null"; then if test -z "$QUIETERR"; then echo "ERROR ${CODE}: $MSG $TYP $DET" | tr -d '"' 1>&2; fi return 9 fi MSG=$(echo "$ANS"| jq '.[] | .message' 2>/dev/null) TYP=$(echo "$ANS"| jq '.[] | .type' 2>/dev/null) DET=$(echo "$ANS"| jq '.[] | .detail' 2>/dev/null) if test -n "$MSG" -a "${MSG:0:4}" != "null" -a "${MSG:0:2}" != "[]"; then echo "ERROR[] ${CODE}: $MSG $TYP $DET" | tr -d '"' 1>&2; return 9; fi if test -n "$ECODE" -a "$ECODE" != "null"; then RC=9; fi fi # PUT/HEAD only return an HTTP header if test -n "$CODE" && test "$CODE" -ge 400; then RC=9; echo "$HDR" | sed 's/^HTTP\/[0-9.]* //' 1>&2; fi if test -n "$CODE2" && test "$CODE2" -ge 400; then RC=9; echo "$HDR2" | sed 's/^HTTP\/[0-9.]* //' 1>&2; fi HDR=$(echo "$ANS" | head -n1 | grep '^HTTP') CODE=$(echo "$HDR" | sed 's/^HTTP\/[0-9.]* \([0-9]*\) .*$/\1/') if test -n "$CODE" && test "$CODE" -ge 400; then echo "${HDR#* }" 1>&2; RC=9; fi fi echo "$ANS" #echo "docurl Return code: $RC" 1>&2 return $RC } curlpost() { docurl -i -sS -H "Content-Type: application/json" -d "$@" } curlhead() { docurl -I -sS -X HEAD -H "Content-Type: application/json" -H "Accept: application/json" "$@" } curlpostauth() { TKN="$1"; shift docurl -sS -X POST \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -H "X-Auth-Token: $TKN" \ -H "X-Language: en-us" \ -d "$@" } curlputauth() { TKN="$1"; shift if test -n "$1"; then docurl -sS -X PUT -H "Content-Type: application/json" -H "Accept: application/json" \ -H "X-Auth-Token: $TKN" -d "$@" else docurl -sS -X PUT -H "Content-Type: application/json" -H "Accept: application/json" \ -H "X-Auth-Token: $TKN" "$@" fi } curlputauthbinfile() { TKN="$1"; shift docurl -sS -X PUT -H "Content-Type: application/octet-stream" \ -H "X-Auth-Token: $TKN" -T "$@" } curlgetauth() { TKN="$1"; shift docurl -sS -X GET -H "Content-Type: application/json" -H "Accept: application/json" \ -H "X-Auth-Token: $TKN" -H "X-Language: en-us" "$@" } curlheadauth() { TKN="$1"; shift docurl -sS -X HEAD --head -H "X-Auth-Token: $TKN" "$@" } curlheadauthparm() { TKN="$1"; shift URL="$1"; shift docurl -sS -X HEAD --head -H "Content-Type: application/json" -H "X-Auth-Token: $TKN" "$@" "$URL" } curlgetauth_pag() { local URL="$2" HASLIM HASPAR LIM TMPF MARKPAR NOANS LASTNO unset HASLIM echo "$URL" | grep -q 'limit=' && HASLIM=1 #echo "$HASLIM $MAXGETKB $RECSZ" 1>&2 if test -n "$HASLIM" -o -z "$MAXGETKB" -o "$MAXGETKB" == "off" -o -z "$RECSZ"; then curlgetauth "$@"; return; fi TKN="$1" unset HASPAR echo "$URL" | grep -q '?' && HASPAR=1 #RECSZ, HDRSZ, ARRNM, IDFIELD LIM=$((($MAXGETKB*1024-$HDRSZ)/$RECSZ)) if test "$HASPAR" == 1; then LIMPAR="&limit=$LIM" else LIMPAR="?limit=$LIM" fi TMPF=$(mktemp $TMPDIR/otc.sh.$$.XXXXXXXX) MARKPAR="" NOANS=0; LASTNO=1 local RC=0 while test $NOANS != $LASTNO -a $(($NOANS%$LIM)) == 0; do LASTNO=$NOANS docurl -sS -X GET -H "Content-Type: application/json" -H "Accept: application/json" \ -H "X-Auth-Token: $TKN" -H "X-Language: en-us" "$URL$LIMPAR$MARKPAR" >>$TMPF # Remember error RV=$? # TODO: Handle 413 => halve page size if test $RC == 0 -a $RV != 0; then RC=$RV; fi # TODO: Actually, look for _links next pointer local ANS=$(cat $TMPF | jq -r ".${ARRNM}[] | .${IDFIELD}") NOANS=$(echo "$ANS" | wc -l) LAST=$(echo "$ANS" | tail -n1 | tr -d '"') MARKPAR="&marker=$LAST" done cat $TMPF rm $TMPF return $RC } curldeleteauth() { TKN="$1"; shift URL="$1"; shift if test -n "$2"; then HDR="$1"; shift #docurl -sS -X DELETE -H "Content-Type: application/json" -H "Accept: application/json" -H "$HDR" -H "X-Auth-Token: $TKN" "$URL" "$@" docurl -sS -X DELETE -H "Accept: application/json" -H "$HDR" -H "X-Auth-Token: $TKN" "$URL" "$@" else #docurl -sS -X DELETE -H "Content-Type: application/json" -H "Accept: application/json" -H "X-Auth-Token: $TKN" "$URL" "$@" docurl -sS -X DELETE -H "Accept: application/json" -H "X-Auth-Token: $TKN" "$URL" "$@" fi } curldeleteauth_language() { TKN="$1"; shift docurl -sS -X DELETE \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -H "X-Language: en-us" \ -H "X-Auth-Token: $TKN" "$@" } curlpatchauth() { TKN="$1"; shift if test -z "$3"; then CTYPE="application/json"; else CTYPE="$3"; fi docurl -sS -X PATCH \ -H "Content-Type: $CTYPE" \ -H "Accept: application/json" \ -H "X-Auth-Token: $TKN" \ -d "$@" } # ARGS: TKN URL PATH OP VALUE [CONTENTTYPE] curldopatch() { TKN="$1"; shift #if test -z "$4"; then OP="remove"; else OP="$3"; VAL="\"value\": \"$4\", "; fi if test "$3" != "remove"; then VAL="\"value\": \"$4\", "; fi if test -z "$5"; then CTYPE="application/json"; else CTYPE="$5"; fi curlpatchauth "$TKN" "[{\"path\": \"$2\", $VAL\"op\": \"$3\"}]" "$1" "$CTYPE" } # ARGS: TKN URL PATH VALUE [CONTENTTYPE] curladdorreplace() { TKN="$1"; shift local VAL if test -z "$4"; then CTYPE="application/json"; else CTYPE="$4"; fi VAL=$(curlgetauth "$TKN" "$1" | jq ".$2"; return ${PIPESTATUS[0]}) echo "#DEBUG: /$2: $VAL -> $3" 1>&2 if test "$VAL" = "null"; then if test -z "$3"; then echo "WARN: Nothing to do, /$2 already non-existent" 1>&2 else curldopatch "$TKN" "$1" "/$2" "add" "$3" "$CTYPE" fi else if test -z "$3"; then curldopatch "$TKN" "$1" "/$2" "remove" "" "$CTYPE" else curldopatch "$TKN" "$1" "/$2" "replace" "$3" "$CTYPE" fi fi } curldeleteauthwithjsonparameter() { # $1: TOKEN # $2: PARAMETER # $3: URI TKN="$1"; shift docurl -sS -X DELETE \ -H "Content-Type: application/json" \ -H "X-Language: en-us" \ -H "X-Auth-Token: $TKN" -d "$@" | jq '.' } unset SUBNETAZ ########################################################################################## #FUNCTIONS ############################################################################### # Helper to remove unnecessary trailing , in JSON (before }) rmvCommaJSON() { # Remove trailing , even w/o '}' CLBR=$2 STR="$1" LF=" " TB=$(echo -e "\t") declare -i LN=${#STR} declare -i CI=$LN while test $CI -gt 0; do let CI-=1 CHR="${STR:$CI:1}" if test "$CHR" == "}"; then CLBR=1; continue; fi if test "$CHR" == "$LF" -o "$CHR" == " " -o "$CHR" == "$TB"; then continue; fi if test $CLBR == 1 -a "$CHR" == ","; then let LN-=1 STR="${STR:0:$CI}${STR:$((CI+1)):$((LN-CI))}" continue fi CLBR=0 done echo "$STR" } # Arguments CATALOGJSON SERVICETYPE getcatendpoint() { local SERVICE_EP=$(echo "$1" | jq "select(.type == \"$2\") | .endpoints[].url" | tr -d '"') if test "$SERVICE_EP" != "null"; then echo "$SERVICE_EP" fi } # Arguments: SERVICESJSON ENDPOINTSJSON SERVICETYPE PROJECTID getendpoint() { local SERVICE_ID=$(echo "$1" | jq ".services[] | select(.type == \"$3\" and .enabled == true) | .id") if test -z "$SERVICE_ID"; then return; fi local SERVICE_EP=$(echo "$2" | jq ".endpoints[] | select(.service_id == $SERVICE_ID and .region == \"$OS_REGION_NAME\") | .url" | tr -d '"' | sed -e "s/\$(tenant_id)s/$4/g") echo "$SERVICE_EP" } # Arguments SERVICEJSON SERVICETYPE getv2endpoint() { local SERVICE_EP=$(echo "$1" | jq ".access.serviceCatalog[] | select(.type == \"$2\") | .endpoints[].publicURL" | tr -d '"') if test "$SERVICE_EP" != "null"; then echo "$SERVICE_EP" fi } # Token caching ... IAMTokenFilename() { # We don't need OS_REGION_NAME, as it's contained in the PROJECT. local FN="${OS_USERNAME% *}" if test "$REQSCOPE" != "unscoped"; then FN="$FN.$OS_USER_DOMAIN_NAME"; fi local PRJ=$OS_PROJECT_ID if test -z "$PRJ"; then PRJ="$OS_PROJECT_NAME"; fi if test "$REQSCOPE" = "project"; then FN="$FN.$PRJ"; fi echo "$HOME/tmp/.otc.cache.$FN" } # Filename readIAMTokenFile() { local TKFN=$1 if ! test -r $TKFN; then return 1; fi if test -n "$DISCARDCACHE" -o -n "$NOCACHE"; then return 1; fi local RESP=$(cat $TKFN) # TODO: Check for expiration in next minutes local now=$(date +"%s") # NOTE: This needs testing for keystonev2 local exp=$(echo "$RESP" | tail -n1 | jq '.token.expires_at' | tr -d '"') if test "$exp" == "null" -o -z "$exp"; then exp=$(echo "$RESP" | tail -n1 | jq '.access.token.expires' | tr -d '"'); fi if test "$exp" == "null"; then return 1; fi exp=$(date -d "${exp%Z}" +"%s") if test -n "$DEBUG"; then echo "Token valid for $(($exp-$now))s" 1>&2; fi TOKEN=`echo "$RESP" | grep "X-Subject-Token:" | cut -d' ' -f 2` if test -z "$TOKEN"; then TOKEN=`echo "$IAMJSON" | jq -r '.access.token.id' | tr -d '"'`; fi if test $(($exp-$now)) -lt 900; then if test $(($exp-$now)) -ge 1; then echo "$TOKEN"; return 42; else return 2; fi fi # TODO: Check Token validity with HEAD /v3/auth/tokens if test -n "$CHECKTOKEN"; then curlheadauthparm $TOKEN "$IAM_AUTH_URL" -H "X-Subject-Token: $TOKEN" || return 3 fi echo "$RESP" } # Filename, Header and Body writeIAMTokenFile() { local TKFN=$1 if ! test -d "$(dirname $TKFN)"; then mkdir "$(dirname $TKFN)"; fi OLDUMASK=$(umask) umask 0177 echo "$2" > $TKFN umask $OLDUMASK } getIAMTokenKeystone() { local TENANT PROJECT USER SCOPE IAM_REQ RESP # Project by ID or by Name if test -n "$OS_PROJECT_ID"; then TENANT="\"tenantId\": \"$OS_PROJECT_ID\"" PROJECT="\"project\": { \"id\": \"$OS_PROJECT_ID\" }" else TENANT="\"tenantName\": \"$OS_PROJECT_NAME\"" PROJECT="\"project\": { \"name\": \"$OS_PROJECT_NAME\" }" fi # USER by ID or by Name if test -n "$OS_USER_ID"; then USER="\"id\": \"$OS_USER_ID\"" else USER="\"name\": \"$OS_USERNAME\"" fi # Token scope: project vs domain if test "$REQSCOPE" == "domain"; then SCOPE="\"scope\": { \"domain\": { \"name\": \"$OS_USER_DOMAIN_NAME\" } }" elif test "$REQSCOPE" == "unscoped"; then SCOPE="" else SCOPE="\"scope\": { $PROJECT }" fi if [[ "$IAM_AUTH_URL" = *"v3/auth/tokens" ]]; then if test -n "$OLDTOKEN" -a -n "$TOKENFROMTOKEN"; then IAM_REQ='{ "auth": { "identity": { "methods": [ "token" ], "token": { "id": "'"$OLDTOKEN"'" } }, '$SCOPE' } }' else IAM_REQ='{ "auth": { "identity": { "methods": [ "password" ], "password": { "user": { '$USER', "password": "'"$OS_PASSWORD"'", "domain": { "name": "'"${OS_USER_DOMAIN_NAME}"'" } } } }, '$SCOPE' } }' fi if test -n "$OS_PROJECT_DOMAIN_NAME"; then IAM_REQ=$(echo "$IAM_REQ" | sed "/\"project\":/i\ \t\t\t\t\"domain\": { \"name\": \"$OS_PROJECT_DOMAIN_NAME\" },") fi else IAM_REQ='{ "auth": { '$TENANT', "passwordCredentials": { "username": "'"$OS_USERNAME"'", "password": "'"$OS_PASSWORD"'" } } } ' fi export INAUTH=1 RESP=$(curlpost "$IAM_REQ" "$IAM_AUTH_URL") RC=$? if test $RC != 0; then echo -e "ERROR: Authentication call failed\n$IAMRESP" 1>&2; exit $RC; fi unset INAUTH echo "$RESP" } # Get a token (and the project ID) TROVE_OVERRIDE=0 IS_OTC=1 getIAMToken() { if test -z "$OS_USERNAME" -o -z "$OS_PASSWORD" -o -z "$IAM_AUTH_URL"; then echo "ERROR: Need to set OS_USERNAME, OS_PASSWORD, OS_AUTH_URL, and OS_PROJECT_NAME environment" 1>&2 echo " Optionally: OS_CACERT, HTTPS_PROXY, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY" 1>&2 exit 1 fi if test -z "$OS_PROJECT_ID"; then PRJ_ID_UNSET=1; fi REQSCOPE=${1:-project} local IAMRESP TKNFN=$(IAMTokenFilename) export BASEURL="${IAM_AUTH_URL/:443\///}" # remove :443 port when present BASEURL=${BASEURL%%/v[23]*} IAMRESP=$(readIAMTokenFile $TKNFN) RC=$? if test $RC != 0; then if test -n "$DEBUG"; then echo "No valid cached token, request from keystone" 1>&2; fi if test $RC = 42; then OLDTOKEN="$IAMRESP"; fi IAMRESP="$(getIAMTokenKeystone)" RC=$? if test $RC != 0; then exit $RC; fi if test -z "$NOCACHE"; then writeIAMTokenFile $TKNFN "$IAMRESP"; fi fi if [[ "$IAM_AUTH_URL" = *"v3/auth/tokens" ]]; then TOKEN=`echo "$IAMRESP" | grep "X-Subject-Token:" | cut -d' ' -f 2` #echo ${TOKEN} | sed -e 's/[0-9]/./g' -e 's/[a-z]/x/g' -e 's/[A-Z]/X/g' if test -z "$OS_PROJECT_ID"; then OS_PROJECT_ID=`echo "$IAMRESP" | tail -n1 | jq -r '.token.project.id'` fi if test -z "$TOKEN" -o -z "$OS_PROJECT_ID"; then echo "ERROR: Failed to authenticate and get token from $IAM_AUTH_URL for user $OS_USERNAME" 1>&2 exit 2 fi if test -z "$OS_USER_DOMAIN_ID"; then OS_USER_DOMAIN_ID=`echo "$IAMRESP" | getUserDomainIdFromIamResponse ` fi if test -z "$OS_USER_DOMAIN_ID"; then echo "ERROR: Failed to determine user domain id from $IAM_AUTH_URL for user $OS_USERNAME" 1>&2 exit 2 fi # Parse IAM RESP catalogue local CATJSON=$(echo "$IAMRESP" | tail -n1 | jq '.token.catalog[]') local ROLEJSON=$(echo "$IAMRESP" | tail -n1 | jq '.token.roles[]') if test -n "$CATJSON" -a "$CATJSON" != "null"; then CINDER_URL=$(getcatendpoint "$CATJSON" volumev2 $OS_PROJECT_ID) NEUTRON_URL=$(getcatendpoint "$CATJSON" network $OS_PROJECT_ID) GLANCE_URL=$(getcatendpoint "$CATJSON" image $OS_PROJECT_ID) DESIGNATE_URL=$(getcatendpoint "$CATJSON" dns $OS_PROJECT_ID) NOVA_URL=$(getcatendpoint "$CATJSON" compute $OS_PROJECT_ID) HEAT_URL=$(getcatendpoint "$CATJSON" orchestration $OS_PROJECT_ID) TROVE_URL=$(getcatendpoint "$CATJSON" database $OS_PROJECT_ID) KEYSTONE_URL=$(getcatendpoint "$CATJSON" identity $OS_PROJECT_ID) CEILOMETER_URL=$(getcatendpoint "$CATJSON" metering $OS_PROJECT_ID) IRONIC_URL=$(getcatendpoint "$CATJSON" baremetal $OS_PROJECT_ID) MANILA_URL=$(getcatendpoint "$CATJSON" sharev2 $OS_PROJECT_ID) KARBOR_URL=$(getcatendpoint "$CATJSON" data-protect $OS_PROJECT_ID) #if test -n "$OUTPUT_CAT"; then echo "$CATJSON" | jq '.'; fi if test -n "$OUTPUT_CAT"; then echo "$CATJSON" | jq '.id+" "+.type+" "+.name+" "+.endpoints[].url+" "+.endpoints[].region+" "+.endpoints[].interface' | tr -d '"' | sort -k2 -u; fi #if test -n "$OUTPUT_CAT"; then echo "$CATJSON" | jq 'def str(s): s|tostring; .id+" "+.type+" "+.name+" "+str(.endpoints[])' | sed 's/\\"url\\"://' | tr -d '"'; fi if test -n "$OUTPUT_ROLES"; then echo "$ROLEJSON" | jq '.id+" "+.name' | tr -d '"'; fi if test -n "$DEL_TOKEN"; then curldeleteauth $TOKEN "$IAM_AUTH_URL"; fi else SERVICES="$(curlgetauth $TOKEN ${IAM_AUTH_URL%auth*}services)" ENDPOINTS="$(curlgetauth $TOKEN ${IAM_AUTH_URL%auth*}endpoints)" #if test "$?" != "0"; then # echo "ERROR: No keystone v3 service catalog" 1>&2 # exit 2 #fi CINDER_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" volumev2 $OS_PROJECT_ID) NEUTRON_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" network $OS_PROJECT_ID) GLANCE_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" image $OS_PROJECT_ID) DESIGNATE_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" dns $OS_PROJECT_ID) NOVA_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" compute $OS_PROJECT_ID) HEAT_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" orchestration $OS_PROJECT_ID) TROVE_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" database $OS_PROJECT_ID) KEYSTONE_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" identity $OS_PROJECT_ID) CEILOMETER_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" metering $OS_PROJECT_ID) IRONIC_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" baremetal $OS_PROJECT_ID) MANILA_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" sharev2 $OS_PROJECT_ID) KARBOR_URL=$(getendpoint "$SERVICES" "$ENDPOINTS" data-protect $OS_PROJECT_ID) fi if test -n "$OUTPUT_DOM"; then echo "$IAMRESP" | tail -n1 | jq '.token.project.domain.id' | tr -d '"'; fi else IS_OTC=0 local IAMJSON=`echo "$IAMRESP" | tail -n1` TOKEN=`echo "$IAMJSON" | jq -r '.access.token.id' | tr -d '"'` if test -z "$OS_PROJECT_ID"; then OS_PROJECT_ID=`echo "$IAMJSON" | tail -n1 | jq -r '.access.token.tenant.id'` fi if test -z "$TOKEN" -o -z "$OS_PROJECT_ID"; then echo "ERROR: Failed to authenticate and get token from $IAM_AUTH_URL for user $OS_USERNAME" 1>&2 exit 2 fi local CATJSON=$(echo "$IAMRESP" | tail -n1 | jq '.access.serviceCatalog[]') local ROLEJSON=$(echo "$IAMRESP" | tail -n1 | jq '.access') CINDER_URL=$(getv2endpoint "$IAMJSON" volumev2 $OS_PROJECT_ID) NEUTRON_URL=$(getv2endpoint "$IAMJSON" network $OS_PROJECT_ID) GLANCE_URL=$(getv2endpoint "$IAMJSON" image $OS_PROJECT_ID) DESIGNATE_URL=$(getv2endpoint "$IAMJSON" dns $OS_PROJECT_ID) NOVA_URL=$(getv2endpoint "$IAMJSON" compute $OS_PROJECT_ID) HEAT_URL=$(getv2endpoint "$IAMJSON" orchestration $OS_PROJECT_ID) TROVE_URL=$(getv2endpoint "$IAMJSON" database $OS_PROJECT_ID) KEYSTONE_URL=$(getv2endpoint "$IAMJSON" identity $OS_PROJECT_ID) CEILOMETER_URL=$(getv2endpoint "$IAMJSON" metering $OS_PROJECT_ID) IRONIC_URL=$(getv2endpoint "$IAMJSON" baremetal $OS_PROJECT_ID) MANILA_URL=$(getv2endpoint "$IAMJSON" sharev2 $OS_PROJECT_ID) KARBOR_URL=$(getv2endpoint "$IAMJSON" data-protect $OS_PROJECT_ID) if test -n "$OUTPUT_CAT"; then echo "$CATJSON" | jq '.endpoints[].id+" "+.type+" "+.name+" "+.endpoints[].publicURL+" "+.endpoints[].region+" public"' | tr -d '"' | sort -k2 -u; fi if test -n "$OUTPUT_ROLES"; then echo "$ROLEJSON" | jq '.metadata.roles[]+" "+.user.roles[].name' | tr -d '"'; fi fi # FIXME: Delete this # For now fall back to hardcoded URLs if test -z "$NOVA_URL" -a "$IS_OTC" = "1" -a "$REQSCOPE" == "project"; then echo "WARN: Using hardcoded endpoints, will be removed" 1>&2 CINDER_URL=${BASEURL/iam/evs}/v2/$OS_PROJECT_ID NEUTRON_URL=${BASEURL/iam/vpc} GLANCE_URL=${BASEURL/iam/ims} DESIGNATE_URL=${BASEURL/iam/dns} NOVA_URL=${BASEURL/iam/ecs}/v2/$OS_PROJECT_ID HEAT_URL=${BASEURL/iam/rts}/v1/$OS_PROJECT_ID TROVE_URL=${BASEURL/iam/rds}/v1.0 IRONIC_URL=${BASEURL/iam/bms} MANILA_URL=${BASEURL/iam/sfs}/v2/$OS_PROJECT_ID KARBOR_URL=${BASEURL/iam/csbs}/v1/$OS_PROJECT_ID fi # DEBUG only: echo "$IAMRESP" | tail -n1 | jq -C . #if test -n "$DEBUG"; then # echo "$IAMRESP" | sed 's/X-Subject-Token: MII.*$/X-Subject-Token: MIIsecretsecret/' 1>&2 #fi if test -z "$KEYSTONE_URL"; then KEYSTONE_URL=$BASEURL/v3; fi if test -z "$CEILOMETER_URL"; then CEILOMETER_URL=${BASEURL/iam/ces}; fi AUTH_URL_ECS="$NOVA_URL/servers" export AUTH_URL_ECS_JOB="${NOVA_URL/v2/v1}/jobs" export AUTH_URL_ECS_DETAIL="$NOVA_URL/servers/detail" AUTH_URL_ECS_CLOUD="${NOVA_URL/v2/v1}/cloudservers" AUTH_URL_ECS_CLOUD_ACTION="$AUTH_URL_ECS_CLOUD/action" AUTH_URL_ECS_CLOUD_DELETE="$AUTH_URL_ECS_CLOUD/delete" AUTH_URL_FLAVORS="$AUTH_URL_ECS_CLOUD/flavors" AUTH_URL_KEYNAMES="$NOVA_URL/os-keypairs" AUTH_URL_VPCS="$NEUTRON_URL/v1/$OS_PROJECT_ID/vpcs" AUTH_URL_ROUTER="$NEUTRON_URL/v2.0/routers" AUTH_URL_PUBLICIPS="$NEUTRON_URL/v1/$OS_PROJECT_ID/publicips" AUTH_URL_SEC_GROUPS="$NEUTRON_URL/v1/$OS_PROJECT_ID/security-groups" #AUTH_URL_SEC_GROUP_RULES="$NEUTRON_URL/v2/$OS_PROJECT_ID/security-group-rules" AUTH_URL_SEC_GROUP_RULES="$NEUTRON_URL/v2.0/security-group-rules" AUTH_URL_SUBNETS="$NEUTRON_URL/v1/$OS_PROJECT_ID/subnets" AUTH_URL_IMAGES="$GLANCE_URL/v2/images" AUTH_URL_IMAGESV1="$GLANCE_URL/v1/cloudimages" AUTH_URL_IMAGESV2="$GLANCE_URL/v2/cloudimages" VBS_URL="${CINDER_URL/evs/vbs}" AUTH_URL_CVOLUMES="$CINDER_URL/cloudvolumes" AUTH_URL_CVOLUMES_DETAILS="$CINDER_URL/cloudvolumes/detail" AUTH_URL_VOLS="$CINDER_URL/volumes" AUTH_URL_CBACKUPS="$VBS_URL/cloudbackups" AUTH_URL_CBACKUPPOLS="$VBS_URL/backuppolicy" AUTH_URL_BACKS="$CINDER_URL/backups" AUTH_URL_SNAPS="$CINDER_URL/snapshots" AUTH_URL_ELB="${NEUTRON_URL/vpc/elb}/v1.0/$OS_PROJECT_ID/elbaas" AUTH_URL_ELB_LB="$AUTH_URL_ELB/loadbalancers" if test -z "$TROVE_URL"; then TROVE_URL=${BASEURL/iam/rds}; TROVE_OVERRIDE=1; fi AUTH_URL_RDS="${TROVE_URL%/v1.0}/rds" AUTH_URL_RDS_DOMAIN="${AUTH_URL_RDS}/v1/$OS_USER_DOMAIN_ID" AUTH_URL_RDS_PROJECT="${AUTH_URL_RDS}/v1/$OS_PROJECT_ID" AUTH_URL_DNS="$DESIGNATE_URL/v2/zones" # FIXME: Use full URLs that point to the service AUTH_URL_AS="${HEAT_URL/rts/as}" AUTH_URL_AS="${AUTH_URL_AS%%/v[12]*}/autoscaling-api/v1/$OS_PROJECT_ID" AUTH_URL_CES="$CEILOMETER_URL" AUTH_URL_CCE="${BASEURL/iam/cce}" AUTH_URL_KMS="${BASEURL/iam/kms}" AUTH_URL_SMN="${BASEURL/iam/smn}" AUTH_URL_CTS="${BASEURL/iam/cts}" AUTH_URL_DMS="${BASEURL/iam/dms}" AUTH_URL_MRS="${BASEURL/iam/mrs}" AUTH_URL_DEH="${BASEURL/iam/deh}" AUTH_URL_ANTIDDOS="${BASEURL/iam/antiddos}" AUTH_URL_DCS="${BASEURL/iam/dcs}/v1.0/$OS_PROJECT_ID" # instances if test -n "$MANILA_URL"; then AUTH_URL_SFS="$MANILA_URL" else AUTH_URL_SFS="${BASEURL/iam/sfs}/v2/$OS_PROJECT_ID" # shares fi # Catalog bug! if echo "$KARBOR_URL" | grep ' ' >/dev/null; then echo "WARN: Wrong CSBS URL: $KARBOR_URL, fixing up ..." 1>&2 KARBOR_URL="${KARBOR_URL/ /}" fi #AUTH_URL_CSBS="${BASEURL/iam/csbs}/v1/$OS_PROJECT_ID" if test -n "$KARBOR_URL"; then AUTH_URL_CSBS="$KARBOR_URL"; else AUTH_URL_CSBS=${BASE_URL/iam/csbs}/v1/$OS_PROJECT_ID; fi AUTH_URL_DWS="${BASEURL/iam/dws}/v1.0/$OS_PROJECT_ID" AUTH_URL_TMS="${BASEURL/iam/tms}/v1.0" AUTH_URL_MAAS="${BASEURL/iam/maas}/v1/$OS_PROJECT_ID" AUTH_URL_NAT="${NEUTRON_URL/vpc/nat}/v2.0" AUTH_URL_DIS="${AUTH_URL_MRS/mrs/dis}/v2/$OS_PROJECT_ID" AUTH_URL_DCAAS="${NEUTRON_URL/vpc/dcaas}/v2.0/dcaas" AUTH_URL_MKT="${BASEURL/iam/marketplace}/v1" } build_data_volumes_json() { local info_str=$1 local DATA_VOLUMES="" disks=(${info_str//,/ }) for disk in "${disks[@]}"; do info=(${disk//:/ }) if test -n "$DATA_VOLUMES"; then DATA_VOLUMES="$DATA_VOLUMES, " fi DATA_VOLUMES="$DATA_VOLUMES{ \"volumetype\": \"${info[0]}\", \"size\": ${info[1]} }" done echo $DATA_VOLUMES } isnum() { echo "$@" | grep '^[0-9]\{1,99\}$' >/dev/null 2>&1 } jsonesc() { if isnum "$@"; then echo "$@" elif test "$@" == "null" -o "$@" == "true" -o "$@" == "false"; then echo "$@" else echo "\"$@\"" fi } keyval2list() { local LIST="" OLDIFS="$IFS" IFS="," for tag in $*; do LIST="$LIST \"${tag/=/.}\"," done IFS="$OLDIFS" echo "${LIST%,}" } keyval2listasterix() { local LIST="" OLDIFS="$IFS" IFS="," for tag in $*; do LIST="$LIST \"${tag/=/*}\"," done IFS="$OLDIFS" echo "${LIST%,}" } keyval2json() { local JSON="" OLDIFS="$IFS" IFS="," for tag in $*; do KEY="${tag%%=*}" VAL="$(jsonesc ${tag#*=})" JSON="$JSON \"$KEY\": $VAL," done IFS="$OLDIFS" echo "${JSON%,}" } keyval2keyvalue() { local JSON="" OLDIFS="$IFS" IFS="," for tag in $*; do KEY="${tag%%=*}" if test "$KEY" != "$tag"; then VAL="$(jsonesc ${tag#*=})" JSON="$JSON { \"key\": \"$KEY\", \"value\": $VAL }," else JSON="$JSON { \"key\": \"$KEY\" }," fi done IFS="$OLDIFS" echo "${JSON%,}" } # Usage ecsHelp() { echo "--- Elastic Cloud Server (VM management) ---" echo "otc ecs list [FILTERS] # list ecs instances (optional key=value filters)" echo " --limit NNN # limit records (works for most list functions)" echo " --marker ID # start with record after marker (UUID) (dito)" echo " --maxgetkb NN # auto-paginate (limiting responses to NN KiB max, def 250)" echo "otc ecs list-detail [ECS] # list ecs instances in full detail (JSON)" echo "otc ecs details [ECS] # list ecs instances in some detail (table)" echo "otc ecs show # show instance " echo "otc ecs show2 # show instance (nova v2.26)" echo "otc ecs console-log # get console output from VM" echo "otc ecs create -n # create ecs instance " echo echo "otc ecs create # create vm example" echo " --count 1 # one instance (default)" echo " --public true # with public ip" echo " --file1 /tmp/A=/tmp/B # inject local file /tmp/B to /tmp/A in VM" echo " --file2 ... # Up to 5 files can be injected this way" echo echo "otc ecs create # create vm (addtl. options)" echo " --instance-type " echo " --instance-name " echo " --image-name " echo " --boot-volume # boot from volume instead of image [only create2]" echo " --subnet-name " echo " --fixed-ip " echo " --nicsubs [:FIX1][,[:FIX2][,...]] # 2ndary NICs " echo " --vpc-name " echo " --security-group-name " echo " --security-group-ids ,," echo " --admin-pass " echo " --key-name " echo " --user-data # don't forget #cloud-config header" echo " --user-data-file # don't forget #cloud-config header" echo " --public " echo " --volumes [,[,..]] # attach volumes as named devices" echo " --bandwidth # defaults to 25" echo " --bandwidth-name # defaults to bandwidth-BW" echo " --disksize " echo " --disktype SATA|SAS|SSD # SATA is default" echo " --tenancy # use 'dedicated' for auto-placement on matching DedicatedHost" echo " --dedicated-host # use ID/Name of preexisting DedicatedHost for direct placement" echo " --scheduler-hints JSON # pass raw JSON scheduler hints to nova (don't use with above two opts)" echo " --datadisks # format: [,[,...]]" echo " # example: SSD:20,SATA:50" echo " --az # determined from subnet by default" echo " --tags KEY=[VAL][,KEY=VAL[,...]]# add key-value pairs as tags" echo " --inherit-tags # tag created rootdisk as well" echo " --autorecovery true/false # set autorecovery" echo " --[no]wait" echo "otc ecs create2 # create vm with v2 API (same params, incomplete)" echo echo "otc ecs update # change VM data (same parms as create)" echo " -r specifies that tags or metadata will remove others" echo "otc ecs reboot-instances # reboot ecs instance " echo " # optionally --soft/--hard/--wait" echo "otc ecs stop-instances # stop ecs instance , dito" echo " # optionally --wait" echo "otc ecs start-instances # start ecs instance " echo " # optionally --wait" echo "otc ecs delete # delete VM" echo " --umount [,..] # umount named volumes before deleting the vm" ##### current issue echo " --rename # prepend DEL_ to ECS names prior to queuing for deletion" echo " --[no]wait # wait for completion (default: no)" echo " --keepEIP # default: delete EIP too" echo " --delVolume # default: delete only system volume, not any volume attached" echo " ... # specify IDs or names" echo "otc ecs job # show status of job " echo "otc ecs limits # display project quotas" echo "otc ecs az-list # list availability zones" echo "otc ecs flavor-list # list available flavors" echo "otc ecs (flp|flavor-pretty) # tersely lists available flavors (aligned, min one-space to separate)" echo "otc ecs flp --spec [pattern] # as above, but lists os_extra_spec, optionally grep-filtered for [pattern]" echo " # [pattern] must be escaped, eg: 'quota\|resource_type' including the ''" echo "otc ecs attach-nic ECSID PORT # attach vNIC to VM: port-spec see below" echo " --port-id PORTID # Specify port-id" echo " --net-id NETID [--fixed-ip IP] # Specify net [and fixed-ip]" echo "otc ecs detach-nic ECSID PORT # detach vNIC from VM" } taskHelp() { echo "--- Task/Job management ---" echo "otc task show # show status of job (same as ecs job)" echo "otc task delete # cancel job (not yet supported)" echo "otc task wait [sec] # wait for job , poll every sec sec (def: 2)" } keypairHelp() { echo "--- SSH Keys ---" echo "otc keypair list # list ssh key pairs" echo "otc keypair show # show ssh key pair" echo "otc keypair create [] # create ssh key pair" echo "otc keypair delete # delete ssh key pair" } evsHelp() { echo "--- Elastic Volume Service (EVS) ---" echo "otc evs list [FILTERS] # list all volumes (only id and name)" echo "otc evs details [FILTERS] # detailed list all volumes (opt. key=value filters)" echo "otc evs show # show details of volume " echo "otc evs tags # show tags of volume " echo "otc evs describe # old iface for vol details, includes tags" echo "otc evs create # create a volume" echo " --volume-name " echo " --disksize " echo " --disktype SATA|SAS|SSD # SATA is default" echo " --az " echo " --multiattach # create shareable volume" echo " --crypt CRYPTKEYID # encryption" echo " --scsi/--vbd # SCSI passthrough or plain VBD attachment" echo " --tags KEY=[VAL][,KEY=VAL[,...]] # add key-value pairs as tags" echo "otc evs update # change volume setting (name, descr, type, ...)" echo "otc evs delete # delete volume" echo echo "otc evs attach ecsid device:volumeid # attach volume at ecs using given device name" echo "otc evs attach --name ecsname device:volume # use names instead of ids" echo "otc evs detach ecsid [device:]volumeid # detach volume-id from ecs" echo "otc evs detach --name ecsname [device:]volume # use names instead of ids" #TODO volume change ... } backupHelp() { echo "--- Elastic Volume Backups ---" echo "otc backup list [FILTERS] # List all backups (opt. key=value filters)" echo "otc backup show BACKUP" echo "otc backup create --name NAME VOL # Create backup from volume" echo "otc backup restore BACKUP VOL # restore backup to volume" echo "otc backup delete BACKUP" echo "otc snapshot list [FILTERS] # list snapshots" echo "otc snapshot show SNAP # details of snapshot snapid" echo "otc snapshot delete SNAP # delete snapshot snapid" echo "otc backuppolicy list # list backup policies" echo "otc backuppolicy show NAME|ID # details of backup policy" echo "otc backuppolicy create [options] NAME # create backup policy" echo " --time HH:mm # option: UTC time to start backup" echo " --freq N # no of days b/w backups" echo " --retain N # no of backups to retain (min 2)" echo " --retain1st Y/N # retain first backup of curr month" echo " --enable/disable # enable/disable (def: enable)" echo " --tags key=val,... # key-value pairs as tags (optional)" echo "otc backuppolicy update NAME|ID # update backup policy (same params as above)" echo "otc backuppolicy delete ID # delete backup policy (by NAME or ID)" echo "otc backuppolicy add ID VOLID [VOLID [...]] # add volumes to policy (dito)" echo "otc backuppolicy remove ID VOLID [VOLID [...]] # remove vols from policy (...)" echo "otc backuppolicy execute ID # trigger backup policy to run once (...)" echo "otc backuppolicy showtasks ID # show jobs triggered by policy (JSON)" echo "otc backuppolicy listtasks ID # show jobs triggered by policy (list)" } vpcHelp() { echo "--- Virtual Private Network (VPC) Router ---" echo "otc vpc list # list all vpc" echo "otc vpc show VPC # display VPC (Router) details" echo "otc vpc delete VPC # delete VPC" echo "otc vpc create # create vpc" echo " --vpc-name " echo " --cidr " echo " --tags KEY=[VAL][,KEY=VAL[,...]] # add key-value pairs as tags" echo "otc vpc listroutes VPC # list VPC routes" echo "otc vpc addroute VPC DEST NHOP # add a route to VPC router with dest and nexthop" echo "otc vpc delroute VPC DEST [NHOP]# delete VPC route" echo "otc vpc en/disable-snat VPC # enable/disable snat" echo "otc vpc limits # list VPC related quota" echo "--- VPC Peering ---" echo "otc vpcpeer list [FILTERS] # List VPC peerings. Optional FILTERS are key=value pairs" echo "otc vpcpeer show NAME|ID # Show details of VPC peering" echo "otc vpcpeer create VPCR VPCA # Create peering req from VPCR to VPCA (IDs or names)" echo " --name # optional name (will get autogen otherwise)" echo " --project-id # project (tenant) ID of VPCA, only required if not us" echo "otc vpcpeer accept ID|NAME # accept peering request" echo "otc vpcpeer reject ID|NAME # refuse peering request" echo "otc vpcpeer update ID|NAME # update peering request" echo " --name # updated name" echo "otc vpcpeer ID|NAME # Delete peering" } subnetHelp() { echo "--- Subnets ---" echo "otc subnet list # list all subnet" echo "otc subnet show # show details for subnet " echo "otc subnet delete # delete subnet " echo " --vpc-name " echo "otc subnet create # create a subnet" echo " --subnet-name " echo " --cidr " echo " --gateway-ip " echo " --primary-dns " echo " --secondary-dns " echo " --availability-zone " echo " --vpc-name " echo " --tags KEY[=VAL][,KEY=VAL[,...]] # add key-value pairs as tags" } eipHelp() { echo "--- Public IPs ---" echo "otc publicip list # list all publicips" echo "otc publicip create # create a publicip" echo " --bandwidth-name " echo " --bandwidth " echo " --address " echo " --pep # create a 198.19.16/20 address" echo "otc publicip update # change name and/or bandwidth (same opts)" echo "otc publicip delete # delete a publicip (EIP)" echo "otc publicip bind # bind a publicip to a port / private IP" echo "otc publicip unbind # unbind a publicip" # TODO: bandwidth echo "otc publicip6 list # list all IPv6 floating ips" echo "otc publicip6 ... # same commands available as for IPv4 (except update)" echo " # currently not well tested (as not yet supported)" echo " # bandwidth control is not available either" } sgHelp() { echo "--- Security Groups ---" echo "otc security-group list # list all sec. group" echo "otc security-group-rules list # list rules of sec. group " echo "otc security-group create # create security group" echo " -g " echo " --vpc-name " echo " --description # optional description" echo "otc security-group delete SGID # delete security group" echo "otc security-group-rules create # create sec. group rule" echo " --security-group-name " echo " --direction " echo " --protocol " echo " --ethertype " echo " --portmin " echo " --portmax " echo " --remotegroup " echo " --remoteip " echo " --description # optional description" } imageHelp() { echo "--- Image Management Service (IMS) ---" echo "otc images list [FILTERS] # list all images (optionally use prop filters)" echo "otc images show # show image details" echo "otc images upload filename # upload image file (OTC-1.1+)" echo "otc images upload bucket:objname # specify image upload src (via s3)" echo "otc images download bucket:objname # export priv image into s3 object" echo "otc images create NAME # create (private) image with name" echo " --disk-format " echo " --min-disk " echo " --min-ram # optional (default 1024)" echo " --os-version # optional (default Other)" echo " --property # optional properties (multiple times possible)" echo " --type --description # optional settings (see below)" echo "otc images create NAME # create image from ECS instance (snapshot)" echo " --image-name " echo " --instance-id " echo " --description # optional description" echo " --type # optional: FusionCompute (ECS, default) or Ironic (BMS)" echo "otc images copy SRCIMAGE NEWIMAGENAME # copy an IMS image within a region" echo " --description # optional" echo " --cmk_id # optional" echo "otc images register NAME FILE # create (private) image with name and s3 file" echo " --property, --min-disk, --os-version and --wait supported" echo "otc images update # change properties, --image-name, --min-*" echo "otc images delete # delete (private) image by ID/name" echo echo "otc images listshare # list projects image id is shared with" echo "otc images showshare # show detailed image sharing status" echo "otc images share # share image id with prj" echo "otc images unshare # stop sharing img id with prj" echo "otc images acceptshare [] # accept image id shared into prj (default to self)" echo "otc images rejectshare [] # reject image id shared into prj" } elbHelp() { echo "--- Classic Elastic Load Balancer (ELBv1) ---" echo "otc elb list # list all classic load balancers" echo "otc elb show # show elb details (eid can also be name)" echo "otc elb create [ [ []]] # create new elb" echo " --vpc-name " echo " --bandwidth # in Mbps" echo " --subnet-name/id # creates internal ELB listening on subnet" echo " --security-group-name/id # for internal ELBs" echo "otc elb delete # Delete ELB with " echo "otc elb listlistener # list listeners of load balancer " echo "otc elb showlistener # show listener detail " echo "otc elb addlistener [ [ []]]" echo " --timeout # timeout in minutes(!) for TCP/UDP" echo " --cookieto # sticky session cookie timeout (min) (roundrobin HTTP/S)" echo " --drain # keep conn after member del in minutes(!) for TCP" echo " --sslcert # SSL cert to use for HTTPS, comma-sep list for SNI" echo " --sslproto # TLSv1.2 or TLSv1.2 TLSv1.1 TLSv1 (only HTTPS)" echo " --sslcipher # Default or Strict or Extended (Ext for v1.2+1.1+1)" #not implemented: modifylistener echo "otc elb dellistener # delete listener by ID/NAME" echo "otc elb listmember " echo "otc elb showmember # show member details by name/IP" echo "otc elb addmember " echo "otc elb delmember " #elb listcheck is missing (!) from API echo "otc elb showcheck # show healtcheck by check ID / listener name" echo "otc elb addcheck []" echo "otc elb delcheck " # echo "otc elb listcert # Show certs for SSL termination" echo "otc elb createcert [opt] CERT PRIV [NAME [DOM]] # SSL certificate creation (PEM files)" echo " --name NAME --description DESC --domain DOM # domain needed for SNI" echo "otc elb updatecert ID/NM [NEWNM]# Update cert name/desc (same opts as above)" echo "otc elb showcert ID/NM # SSL certificate details" echo "otc elb deletecert ID/NM # SSL certificate deletion" echo "--- Unified Load Balancer (ULB aka LBaaSv2) ---" echo "otc ulb list # list all unified load balancers/ELBv2" echo "otc ulb show # show details of " echo "otc ulb details # show more details of " } asHelp() { echo "--- Autoscaling Help (AS) ---" echo "otc asgroup list [FILT] # list all autoscaling groups" echo "otc asgroup show AGRP # show details of autoscaling group AGRP" echo "otc asgroup vms AGRP # show instances of autoscaling group AGRP" echo "otc asgroup log AGRP # get action log of autoscaling group AGRP" echo "otc asconfig list # list all autoscaling configurations" echo "otc asconfig show ACFG # show details of autscaling config ACFG" echo "otc aspolicy list AGRP # list all autoscaling policies for AGRP" echo "otc aspolicy show APOL # show details of autscaling config APOL" echo "otc as limits [AGRP] # show quotas for autoscaling" echo "otc as tags TYPE [RID] # list tags for AS TYPE" } rdsHelp() { echo "--- Relational Database Service (RDS) ---" echo "otc rds list" echo "otc rds listinstances # list database instances" echo "otc rds show [ ...]" echo "otc rds showinstances [ ...] # show database instances details" echo "otc rds apis" echo "otc rds listapis # list API ids" echo "otc rds showapi ... # show API detail information" echo "otc rds showdatastore MySQL|PostgreSQL ... # show datastore ids and metadata" echo "otc rds datastore ... # alias for 'showdatastore'" echo "otc rds showdatastoreparameters ... # show all configuration parameters" echo "otc rds showdatastoreparameter # show a configuration parameter" echo "otc rds listflavors " echo "otc rds flavors # list RDS flavors" echo "otc rds showflavor ... # RDS flavor details" echo "otc rds create [] # create RDS instance, read from" echo " # stdin when no config file is given" echo "otc rds delete # remove RDS instances and backups" echo "otc rds showbackuppolicy ... # show backup policy of database " echo "otc rds listsnapshots # list all backups" echo "otc rds listbackups # alias for 'listsnapshots'" echo "otc rds showerrors # shows db instance errors currently" echo " # limited to last month and MySQL" echo "otc rds showslowstatements # shows db instance errors currently" echo " # id of db instance" echo " select|insert|update|delete # one of the statement type" echo " # top longest stmts to show (1-50)" echo "otc rds showslowqueries ... # alias for 'showslowstatements'" echo "otc rds createsnapshot # create a snapshot from an instance" echo "otc rds createbackup ... # alias for 'createsnapshot'" echo "otc rds deletesnapshot # deletes a snapshot of an instance" echo "otc rds deletebackup ... # alias for 'deletesnapshot'" } dnsHelp() { echo "--- DNS ---" echo "otc domain list # show all zones/domains" echo "otc domain show zid # show details of zone/domain " echo "otc domain delete zid # deleted zone/domain " echo "otc domain create [options] domain [desc [type [mail [ttl]]]]" echo " # create zone for domain (name. or ...in-addr.arpa.)" echo " # desc, public/private, mail, ttl (def: 300s) optional" echo "otc domain addrecord zid name. type ttl val [desc]" echo " # add record to zone for with , " echo " # type could be A, AAAA, MX, CNAME, PTR, TXT, NS" echo " # val is a comma sep list of record values, e.g." echo " # IPADDR, NR NAME, NAME, NAME, STRING, NAME." echo "otc domain showrecord zid rid # show record for zone " echo "otc domain listrecords [zid] # list records for zone " echo "otc domain delrecord zid rid # delete record in zone " echo "otc domain associate zid vpc # connect vpc to private DNS server zid" echo "otc domain dissociate zid vpc # discconnect vpc from private DNS zid" } cceHelp() { echo "--- Cloud Container Engine (CCE) ---" echo "otc cluster list # list container clusters (short)" echo "otc cluster list-detail # list container clusters (detailed)" echo "otc cluster show # show container cluster details of cid" echo "otc cluster delete # delete container cluster cid" echo "otc cluster create [opts] # create new container cluster with name" echo " --type Single/HA # Single Master or HA master (mandatory)" echo " --vpc-name or --vpc-id # VPC (mandatory)" echo " --subnet-name or --subnet-id # SubNet (mandatory)" echo " --az --description # AZ and description (optional)" echo " --security-group-name # Security Group (optional)" echo "otc host list # list container hosts of cluster cid" echo "otc host show # show host hid details (cluster cid)" echo "otc host create [opts] # deploy nr hosts in cluster cid" echo " --disks TYPE:SIZE,TYPE:SIZE,...# disk config (mand), SATA/SAS/SSD:GB" echo " --instance-type # VM flavors to deploy (mandtory)" echo " --key-name # SSH key to inject (mand)" echo " --name