2606 lines
80 KiB
Bash
Executable File
2606 lines
80 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# add node
|
|
# issue with json post data
|
|
# rename project
|
|
# delete node
|
|
|
|
# node stop givesjq error
|
|
|
|
# connect xxx & [opens iterm (or whatever)]
|
|
|
|
# when oepning new project and caching, "open" the project
|
|
# when quitting or changing projects, "close" curproject
|
|
# warn if curprojectis closed
|
|
|
|
|
|
VER=0.1
|
|
|
|
# helper function to turn a directory of scripts into inline
|
|
## defaults at the end of this script
|
|
function scriptstoinline() {
|
|
local outfile files
|
|
echo "About to convert scripts from $SCRIPTDIR to inline format:"
|
|
files=$(ls -A "${SCRIPTDIR}")
|
|
echo "$files" | sed -e 's/^/ - /'
|
|
echo
|
|
outfile=""
|
|
while [[ -z $outfile ]]; do
|
|
getstr ":" "./out" "Enter file to place output in"
|
|
outfile="$retstr"
|
|
if [[ -e $outfile ]]; then
|
|
getyn "n" "'$outfile' already exists. Overwrite"
|
|
[[ $? -ne 0 ]] && outfile=""
|
|
fi
|
|
done
|
|
cp /dev/null $outfile;
|
|
for f in $SCRIPTDIR/*; do
|
|
echo -n "Processing ${f}..."
|
|
echo "#START_INLINE:$(basename $f)" >> $outfile
|
|
cat "$f" >> $outfile
|
|
echo "#END_INLINE:$(basename $f)" >> $outfile
|
|
echo "done"
|
|
done
|
|
echo
|
|
echo "All done. Replace inline code with the contents of: $outfile"
|
|
}
|
|
|
|
function mkscripts() {
|
|
local dir
|
|
dir="$1"
|
|
info "Creating helper scripts in ${dir}"
|
|
cat "$THISSCRIPT" | awk -v dir="$dir" -F: 'BEGIN { curfile=""; } /^#START_INLINE/ { curfile = dir "/" $2; printf(" %s...", curfile) > "/dev/stderr"; } /^#END_INLINE/ { curfile=""; printf("done\n") > "/dev/stderr"; } (curfile != "") { print > curfile; }'
|
|
}
|
|
|
|
function initauth() {
|
|
local u p doscripts=0 files
|
|
[[ ! -e $CONFDIR ]] && mkdir -p "$CONFDIR" && info "Created $CONFDIR"
|
|
[[ ! -e $TMPDIR ]] && mkdir -p "$TMPDIR" && info "Created $CACHEDIR"
|
|
[[ ! -e $CACHEDIR ]] && mkdir -p "$CACHEDIR" && info "Created $TMPDIR"
|
|
[[ ! -e $SCRIPTDIR ]] && mkdir -p "$SCRIPTDIR" && info "Created $SCRIPTDIR"
|
|
[[ ! -e $HISTFILE ]] && cp /dev/null "$HISTFILE" && info "Created $HISTFILE"
|
|
|
|
# if [[ -e $AUTHFILE ]]; then
|
|
# warn "Authentication file $AUTHFILE already exists - skipping creation."
|
|
# else
|
|
# info "Creating authentication file"
|
|
# read -p "Enter gns username: " u
|
|
# read -sp "Enter gns password: " p
|
|
# echo
|
|
# cp /dev/null "${AUTHFILE}"
|
|
# [[ $? -ne 0 ]] && error "Can't create $AUTHFILE" && return 1
|
|
# echo "$u" >"${AUTHFILE}"
|
|
# echo "$p" >>"${AUTHFILE}"
|
|
# chmod 600 "$AUTHFILE"
|
|
# info "Authentication details stored in $AUTHFILE"
|
|
# fi
|
|
[[ ! -e $RCFILE ]] && echo "# each line in this file is used as a command-line argument" > "$RCFILE" && info "Created initial $RCFILE"
|
|
[[ ! -e $SRVFILE ]] && echo "exampleserver:gns3.example.net:3080" > "$SRVFILE" && info "Created initial $SRVFILE - you need to update this!"
|
|
|
|
files=$(ls -A "${SCRIPTDIR}")
|
|
if [[ -n $files ]]; then
|
|
warn "Script directory $SCRIPTDIR already contains files:"
|
|
echo -en "$YELLOW"
|
|
echo "$files" | sed -e 's/^/ - /'
|
|
echo
|
|
echo -en "$PLAIN"
|
|
|
|
getyn n "Overwrite scripts with defaults"
|
|
[[ $? -eq 0 ]] && doscripts=1 || doscripts=0
|
|
else
|
|
doscripts=1
|
|
fi
|
|
if [[ $doscripts -eq 1 ]]; then
|
|
mkscripts "$SCRIPTDIR"
|
|
fi
|
|
echo -e "$BOLD${CYAN}Initialisation of $BOLD$CONFDIR$PLAIN complete.$PLAIN"
|
|
return 0
|
|
}
|
|
|
|
function cache_uuids() { # cache_uuids $$ [locations]
|
|
local loc epidx epname api_endpoint curlres mainpid thisfile
|
|
local where
|
|
|
|
mainpid="$1"
|
|
shift
|
|
if [[ $# -ge 1 ]]; then
|
|
where="$*"
|
|
else
|
|
where="$curlocs"
|
|
fi
|
|
|
|
for loc in $where; do
|
|
thisfile="${CACHEFILEBASE}.${loc}"
|
|
cp /dev/null "$thisfile"
|
|
for epname in ${ep_name[@]}; do
|
|
info "- Caching UUID data for $loc $epname"
|
|
epidx=$(getepidx $epname)
|
|
api_endpoint="${ep_apiendpoint[$epidx]}"
|
|
|
|
curlres=$(runcurlget $loc $api_endpoint)
|
|
[[ $? -ne 0 ]] && error "curl to $thisurl failed" && return 1
|
|
echo "$curlres" | jq -r "try (.[] | [ .${ep_idfield[$epidx]}, .name ] | @csv) catch empty" | tr -d '"' >> "$thisfile"
|
|
done
|
|
done
|
|
|
|
kill -SIGUSR1 $mainpid
|
|
}
|
|
|
|
function loadcachefile() { # loadcachefile [dc1 dc2 ...]
|
|
local f line dc added
|
|
if [[ ! -d "$CACHEDIR" ]]; then
|
|
return 1
|
|
fi
|
|
clear_cache
|
|
|
|
added=0
|
|
if [[ $# -ge 1 ]]; then
|
|
for dc in $*; do
|
|
f=${CACHEFILEBASE}.${dc}
|
|
if [[ -e ${f} ]]; then
|
|
while read -r line ; do
|
|
adduuid "${line%%,*}" "${line##*,}"
|
|
if [[ $? -eq 0 ]]; then
|
|
added=$((added + 1))
|
|
fi
|
|
done < "${f}"
|
|
fi
|
|
done
|
|
addmsgq $(info "Loaded $added UUIDs from cache files for $*" 2>&1)
|
|
else
|
|
ls ${CACHEFILEBASE}.* >/dev/null 2>&1
|
|
if [[ $? -eq 0 ]]; then
|
|
for f in ${CACHEFILEBASE}.*; do
|
|
while read -r line ; do
|
|
adduuid ${line%%,*} ${line##*,}
|
|
done < "$f"
|
|
done
|
|
addmsgq $(info "Loaded cache files for all servers" 2>&1)
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function uuid_callback() {
|
|
nuuids=0
|
|
loadcachefile $curlocs
|
|
uuidend=$(($($GDATE +%s%N)/1000))
|
|
uuidsecs=$(echo "scale=2; ($uuidend - $uuidstart) / 1000000;" | bc)
|
|
|
|
if [[ $nuuids -ge 1 ]]; then
|
|
addmsgq $(info "Cached $nuuids UUID(s) for $curlocs in $uuidsecs seconds." 2>&1)
|
|
else
|
|
addmsgq $(error "No UUIDs found for [$curlocs]" 2>&1)
|
|
fi
|
|
CACHING=""
|
|
trap - SIGUSR1
|
|
}
|
|
|
|
function start_recache_if_needed() {
|
|
local dc f needed toload
|
|
needed=""
|
|
toload=""
|
|
for dc in $curlocs; do
|
|
f=${CACHEFILEBASE}.${dc}
|
|
if [[ -e ${f} ]]; then
|
|
toload="$toload $dc"
|
|
else
|
|
needed="$needed $dc"
|
|
fi
|
|
done
|
|
[[ -n $toload ]] && loadcachefile $toload
|
|
[[ -n $needed ]] && start_recache $needed
|
|
}
|
|
|
|
function start_recache() {
|
|
local rv
|
|
uuidstart=$(($($GDATE +%s%N)/1000))
|
|
if [[ -z $recache_pid ]]; then
|
|
# setup callback for uuid cache reload handling
|
|
trap uuid_callback SIGUSR1
|
|
info "Initiating UUID cache refresh for $curlocs..."
|
|
CACHING="background task: refreshing cache for $curlocs"
|
|
cache_uuids $$ $* &
|
|
recache_pid=$!
|
|
rv=0
|
|
else
|
|
rv=1
|
|
fi
|
|
return $rv
|
|
}
|
|
|
|
# populates global retstr
|
|
function getstr() { # getstr last_prompt_char default_answer "prompt string"
|
|
local lastchar def prompt rv
|
|
lastchar="$1"
|
|
shift
|
|
def="$1"
|
|
shift
|
|
prompt="$*"
|
|
retstr=""
|
|
echo -n -e "${PURPLE}$prompt [$BOLD$def$PLAIN$PURPLE]${lastchar}${PLAIN} "
|
|
read retstr
|
|
echo -e "$PLAIN"
|
|
[[ -z $retstr ]] && retstr="$def"
|
|
}
|
|
|
|
# populates global yn, returns 0 for yes, 1 for no
|
|
function getyn() { # getyn default_answer "prompt string"
|
|
local def prompt rv
|
|
def="$1"
|
|
shift
|
|
prompt="$*"
|
|
yn=""
|
|
while [[ $yn != "y" && $yn != "n" ]]; do
|
|
getstr "?" "$def" "$prompt"
|
|
yn="$retstr"
|
|
done
|
|
if [[ $yn == "y" ]]; then
|
|
rv=0
|
|
else
|
|
rv=1
|
|
fi
|
|
return $rv
|
|
}
|
|
|
|
function arraycontains() { # arraycontains arrayname lookfor wildcard
|
|
local arr lookfor varname wild lookfor_modified
|
|
varname="$1"
|
|
lookfor="$2"
|
|
shift 2
|
|
|
|
eval arr=( '"${'${varname}'[@]}"' )
|
|
if [[ $# -ge 1 ]]; then
|
|
lookfor_modified=" $lookfor"
|
|
else
|
|
lookfor_modified=" $lookfor "
|
|
fi
|
|
[[ " ${arr[@]} " =~ $lookfor_modified ]] && return 0
|
|
return 1
|
|
|
|
}
|
|
|
|
function getcmd() {
|
|
local prefix pstr input varname rv
|
|
varname=$1
|
|
if [[ -z $curproj ]]; then
|
|
prefix="[no_project_selected]"
|
|
else
|
|
prefix="[${curproj}]"
|
|
fi
|
|
#echo "${BOLD}$prefix rosh> ${PLAIN}"
|
|
pstr="$prefix gnscli> "
|
|
|
|
[[ ! -z $CACHING ]] && info "$CACHING"
|
|
set -f
|
|
builtin read -p "$(echo -en $BOLD)${pstr}$(echo -e "$PLAIN") " -e -r input
|
|
set +f
|
|
rv=$?
|
|
eval "$varname='$input'"
|
|
return $rv
|
|
}
|
|
|
|
function listprojects() {
|
|
local x format
|
|
format=" %-24s|%-36s${PLAIN}\n"
|
|
printf "${UNDERLINE}${BOLD}$format" "Project" "UUID"
|
|
for x in ${!proj_name[@]}; do
|
|
printf "$format" "${proj_name[$x]}" "${proj_id[$x]}"
|
|
done
|
|
}
|
|
|
|
function listservers() {
|
|
local x format
|
|
format=" %-12s|%-24s|%-34s${PLAIN}\n"
|
|
printf "${UNDERLINE}${BOLD}$format" "Location" "Host" "API URL"
|
|
for x in ${!loc_name[@]}; do
|
|
printf "$format" "${loc_name[$x]}" "${loc_engine[$x]}" "${loc_api[$x]}"
|
|
done
|
|
}
|
|
|
|
function profile() {
|
|
local fname
|
|
fname=${FUNCNAME[2]}
|
|
[[ -z $fname ]] && fname=${FUNCNAME[1]}
|
|
[[ $PROFILING -eq 1 ]] && echo -e "${YELLOW}${BOLD}${FUNCNAME[1]}(): ${PLAIN}${YELLOW}$*${PLAIN}" 1>&2
|
|
}
|
|
|
|
function debug() {
|
|
local force=0
|
|
if [[ $1 == "-f" ]]; then
|
|
shift
|
|
force=1
|
|
fi
|
|
if [[ $VERBOSE -eq 1 ]]; then
|
|
if [[ $force -eq 1 ]]; then
|
|
echo -e "${PURPLE}${BOLD}${FUNCNAME[1]}(): ${PLAIN}${PURPLE}$*${PLAIN}" >/dev/stderr
|
|
echo -e "${PURPLE}${BOLD}${FUNCNAME[1]}(): ${PLAIN}${PURPLE}$*${PLAIN}" >/tmp/a
|
|
else
|
|
echo -e "${PURPLE}${BOLD}${FUNCNAME[1]}(): ${PLAIN}${PURPLE}$*${PLAIN}" 1>&2
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function info() {
|
|
echo -e "${CYAN}${ITALIC}[$*]${PLAIN}" 1>&2
|
|
}
|
|
|
|
function error() {
|
|
[[ $innotify -eq 1 ]] && fail
|
|
echo -e "${RED}${BOLD}ERROR: ${PLAIN}${RED}$*${PLAIN}" 1>&2
|
|
}
|
|
|
|
function notify() {
|
|
echo -en "${PURPLE}${BOLD}* ${PLAIN}${PURPLE}$*...${PLAIN} " 1>&2
|
|
innotify=1
|
|
}
|
|
|
|
function notify_nodots() {
|
|
echo -e "${PURPLE}${BOLD}* ${PLAIN}${PURPLE}$*${PLAIN} " 1>&2
|
|
}
|
|
|
|
function ok() {
|
|
local msg=${*:-ok}
|
|
[[ $innotify -eq 0 ]] && return 1
|
|
echo -e "$GREEN$msg$PLAIN" 1>&2
|
|
innotify=0
|
|
}
|
|
|
|
function fail() {
|
|
local msg=${*:-failed}
|
|
[[ $innotify -eq 0 ]] && return 1
|
|
echo -e "$RED$msg$PLAIN" 1>&2
|
|
innotify=0
|
|
}
|
|
|
|
function warn() {
|
|
echo -e "${YELLOW}${BOLD}Warning: ${PLAIN}${YELLOW}$*${PLAIN}" 1>&2
|
|
}
|
|
|
|
function usage() {
|
|
echo "usage: $0 OPTIONS command"
|
|
echo
|
|
echo " -h Show this text."
|
|
echo " -i Generate initial auth file."
|
|
echo " -s server Set initial server."
|
|
echo " Valid servers: [${loc_name[@]}]"
|
|
echo " -p project Set initial project."
|
|
echo " -P Profiling mode."
|
|
echo " -v Verbose mode."
|
|
echo
|
|
}
|
|
|
|
function addcmd() {
|
|
local adm
|
|
[[ -z $ncmds ]] && ncmds=0
|
|
|
|
if [[ $1 == "-a" ]]; then
|
|
adm=1
|
|
shift
|
|
else
|
|
adm=0
|
|
fi
|
|
cmd_name[$ncmds]="$1"
|
|
cmd_desc[$ncmds]="$2"
|
|
cmd_adm[$ncmds]="$adm"
|
|
if [[ $3 == ** ]]; then
|
|
cmd_minargs[$ncmds]="${3%%+*}"
|
|
cmd_maxargs[$ncmds]="99"
|
|
cmd_needargs[$ncmds]=">= ${cmd_minargs[$ncmds]}"
|
|
elif [[ $3 == *-* ]]; then
|
|
cmd_minargs[$ncmds]="${3%%-*}"
|
|
cmd_maxargs[$ncmds]="${3##*-}"
|
|
cmd_needargs[$ncmds]="${cmd_minargs[$ncmds]}-${cmd_maxargs[$ncmds]}"
|
|
else
|
|
cmd_minargs[$ncmds]="$3"
|
|
cmd_maxargs[$ncmds]="$3"
|
|
cmd_needargs[$ncmds]="$3"
|
|
fi
|
|
shift 3
|
|
cmd_aliases[$ncmds]=" $* "
|
|
cmd_usage[$ncmds]=""
|
|
cmd_optionname[$ncmds]=""
|
|
cmd_optiondesc[$ncmds]=""
|
|
cmd_maxoptnamelen[$ncmds]=0
|
|
|
|
ncmds=$((ncmds + 1))
|
|
}
|
|
|
|
function addcmdalias() {
|
|
local defarg
|
|
[[ -z $ncmdaliases ]] && ncmdaliases=0
|
|
|
|
cmdalias_src[$ncmdaliases]="$1"
|
|
cmdalias_dst[$ncmdaliases]="$2"
|
|
defarg="$3"
|
|
|
|
cmdalias_defaultarg[$ncmdaliases]="$defarg"
|
|
|
|
ncmdaliases=$((ncmdaliases + 1))
|
|
}
|
|
|
|
function addmsgq() {
|
|
msgq[$nmsgq]="$*"
|
|
nmsgq=$((nmsgq + 1))
|
|
}
|
|
|
|
function dumpmsgq() {
|
|
local x
|
|
for x in ${!msgq[@]}; do
|
|
echo -e "${msgq[$x]}"
|
|
done
|
|
msgq=()
|
|
nmsgq=0
|
|
}
|
|
|
|
function clear_cache() {
|
|
uuid_id=()
|
|
uuid_name=()
|
|
nuuids=0
|
|
}
|
|
|
|
function adduuid() { # uuid name
|
|
# local i
|
|
|
|
# add
|
|
uuid_id[$nuuids]="${1}"
|
|
uuid_name[$nuuids]="${2}"
|
|
nuuids=$((nuuids + 1))
|
|
|
|
# i=$(uuid_to_idx "${1}")
|
|
# if [[ $? -eq 0 ]]; then
|
|
# # update
|
|
# uuid_name[$i]="${2}"
|
|
# return 1
|
|
# else
|
|
# # add
|
|
# uuid_id[$nuuids]="${1}"
|
|
# uuid_name[$nuuids]="${2}"
|
|
# nuuids=$((nuuids + 1))
|
|
# return 0
|
|
# fi
|
|
}
|
|
|
|
function uuid_to_name() {
|
|
local x
|
|
#for x in ${!uuid_id[@]}; do
|
|
# if [[ ${uuid_id[$x]} == $1 ]]; then
|
|
# echo "${uuid_name[$x]}"
|
|
# return 0
|
|
# fi
|
|
#done
|
|
|
|
# faster
|
|
for ((x=0; x<${#uuid_id[@]}; ++x)); do
|
|
if [[ "${uuid_id[$x]}" == "$1" ]]; then
|
|
echo "${uuid_name[$x]}"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
echo "$1"
|
|
return 1
|
|
}
|
|
|
|
function uuid_to_idx() {
|
|
local x
|
|
for x in ${!uuid_id[@]}; do
|
|
if [[ ${uuid_id[$x]} == $1 ]]; then
|
|
echo "${$x}"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function addloc() {
|
|
[[ -z $nlocs ]] && nlocs=0
|
|
loc_name[$nlocs]="$1"
|
|
if [[ $nlocs -eq 0 ]]; then
|
|
curlocs="$1"
|
|
fi
|
|
loc_engine[$nlocs]="$2"
|
|
loc_port[$nlocs]="$3"
|
|
|
|
loc_api[$nlocs]="http://${loc_engine[$nlocs]}:${loc_port[$nlocs]}/v2"
|
|
|
|
nlocs=$((nlocs + 1))
|
|
}
|
|
|
|
function addproject() {
|
|
[[ -z $nprojects ]] && nprojects=0
|
|
proj_id[$nprojects]="$1"
|
|
proj_name[$nprojects]="$2"
|
|
|
|
nprojects=$((nprojects + 1))
|
|
}
|
|
|
|
# Don't use '#'. Use ^ for spaces.
|
|
function addepfields() { # addepfields ep_name field1 field2 field3 ...
|
|
local idx
|
|
idx=$(getepidx $1)
|
|
if [[ $? -ne 0 ]]; then
|
|
error "appepfields(): can't find endpoint named '$1'"
|
|
return 1
|
|
fi
|
|
shift
|
|
while [[ $# -ge 1 ]]; do
|
|
[[ -z ${ep_fields[$idx]} ]] && ep_fields[$idx]="${1}" || ep_fields[$idx]="${ep_fields[$idx]}#${1}"
|
|
shift
|
|
done
|
|
}
|
|
|
|
# Don't use '#'. Use ^ for spaces.
|
|
function addeptitles() { # addeptitles ep_name title1 title2 title3 ... # Don't use '#' or spaces.
|
|
local idx
|
|
idx=$(getepidx $1)
|
|
if [[ $? -ne 0 ]]; then
|
|
error "appeptitles(): can't find endpoint named '$1'"
|
|
return 1
|
|
fi
|
|
shift
|
|
while [[ $# -ge 1 ]]; do
|
|
ep_titles[$idx]="${ep_titles[$idx]}#${1}"
|
|
shift
|
|
done
|
|
}
|
|
|
|
function addendpoint() { # addendpoint ep_name ep_apiendpoint ep_jqobjectname ep_idfieldname [defaultfieldname]
|
|
[[ -z $neps ]] && neps=0
|
|
|
|
ep_name[$neps]="$1"
|
|
ep_apiendpoint[$neps]="$2"
|
|
ep_jqobj[$neps]="$3"
|
|
ep_idfield[$neps]="$4"
|
|
ep_defaultfield[$neps]="${5:-name}"
|
|
ep_alias[$neps]=""
|
|
ep_fields[$neps]=""
|
|
ep_titles[$neps]="Server"
|
|
ep_validactions[$neps]=""
|
|
|
|
neps=$((neps + 1))
|
|
}
|
|
|
|
function addepalias() { # addeptitles ep_name alias1 alias2 alias3 ...
|
|
local idx
|
|
idx=$(getepidx $1)
|
|
if [[ $? -ne 0 ]]; then
|
|
error "appepalias(): can't find endpoint named '$1'"
|
|
return 1
|
|
fi
|
|
shift
|
|
while [[ $# -ge 1 ]]; do
|
|
ep_alias[$idx]="${ep_alias[$idx]} ${1} "
|
|
shift
|
|
done
|
|
}
|
|
|
|
function addepactions() { # addepactions ep_name action_name [action_name2] ...
|
|
local idx
|
|
idx=$(getepidx $1)
|
|
if [[ $? -ne 0 ]]; then
|
|
error "appepaction(): can't find endpoint named '$1'"
|
|
return 1
|
|
fi
|
|
shift
|
|
while [[ $# -ge 1 ]]; do
|
|
ep_validactions[$idx]="${ep_validactions[$idx]} ${1} "
|
|
shift
|
|
done
|
|
}
|
|
|
|
function getcmdidx() { # getcmdidx commandname adminonly(1|0)
|
|
local x adm lookfor match
|
|
lookfor=$1
|
|
adm=$2
|
|
|
|
match=0
|
|
for x in ${!cmd_name[@]}; do
|
|
#echo "check alias '${cmd_aliases[$x]}' against '* $lookfor *'" >/dev/stderr
|
|
if [[ ${cmd_name[$x]} == $lookfor ]]; then
|
|
match=1
|
|
elif [[ ${cmd_aliases[$x]} == *\ $lookfor\ * ]]; then
|
|
match=1
|
|
else
|
|
match=0
|
|
fi
|
|
if [[ $match -eq 1 ]]; then
|
|
if [[ -z $adm || ${cmd_adm[$x]} == $adm ]]; then
|
|
echo "$x"
|
|
return 0
|
|
fi
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function addcmdusage() { # addcmdusage cmd_name "usage goes here" "line 2" "line 3" ...
|
|
local cname idx text
|
|
cname="$1"
|
|
shift
|
|
idx=$(getcmdidx $cname)
|
|
[[ $? -ne 0 ]] && return 1
|
|
text=""
|
|
while [[ $# -ge 1 ]]; do
|
|
[[ -z $text ]] && text="$1" || text="$text\n$1"
|
|
shift 1
|
|
done
|
|
cmd_usage[$idx]="$text"
|
|
}
|
|
|
|
function addcmdoption() { # addcmdusage cmd_name "usage goes here" "line 2" "line 3" ...
|
|
local cname idx text
|
|
cname="$1"
|
|
shift
|
|
idx=$(getcmdidx $cname)
|
|
if [[ $? -ne 0 ]]; then
|
|
error "Tried to addcmdoption() for nonexistant command '$cname'"
|
|
kill $$
|
|
fi
|
|
while [[ $# -ge 1 ]]; do
|
|
[[ -z ${cmd_optionname[$idx]} ]] && cmd_optionname[$idx]="$1" || cmd_optionname[$idx]="${cmd_optionname[$idx]}#$1"
|
|
[[ ${#1} -gt ${cmd_maxoptnamelen[$idx]} ]] && cmd_maxoptnamelen[$idx]=${#1}
|
|
shift 1
|
|
|
|
[[ -z ${cmd_optiondesc[$idx]} ]] && cmd_optiondesc[$idx]="$1" || cmd_optiondesc[$idx]="${cmd_optiondesc[$idx]}#$1"
|
|
shift 1
|
|
done
|
|
}
|
|
|
|
function showcmdhelp() {
|
|
local real_cname cname idx text ln x format optname_arr optdesc_arr len
|
|
cname="$1"
|
|
shift
|
|
idx=$(getcmdidx $cname)
|
|
if [[ $? -ne 0 ]]; then
|
|
error "Invalid command '$cname'"
|
|
return 1
|
|
fi
|
|
real_cname="${cmd_name[$idx]}"
|
|
[[ ${cmd_adm[$idx]} -eq 1 ]] && real_cname="\\${real_cname}"
|
|
|
|
# make array of options
|
|
IFS='#' read -r -a optname_arr <<< "${cmd_optionname[$idx]}"
|
|
IFS='#' read -r -a optdesc_arr <<< "${cmd_optiondesc[$idx]}"
|
|
len=${cmd_maxoptnamelen[$idx]}
|
|
|
|
|
|
echo -n "Usage: ${real_cname} "
|
|
[[ $len -gt 0 ]] && echo -n "[OPTIONS] "
|
|
|
|
# show usage if we have it
|
|
if [[ -n ${cmd_usage[$idx]} ]]; then
|
|
echo -e "${cmd_usage[$idx]}" | awk '(NR == 1) { print }'
|
|
else
|
|
echo
|
|
fi
|
|
echo " ${cmd_desc[$idx]}."
|
|
|
|
if [[ -n ${cmd_usage[$idx]} ]]; then
|
|
echo
|
|
echo -e "${cmd_usage[$idx]}" | awk '(NR > 1) { printf(" %s\n",$0); }'
|
|
fi
|
|
|
|
# show options
|
|
if [[ $len -gt 0 ]]; then
|
|
echo
|
|
format=" %-${len}s %s\n"
|
|
for x in ${!optname_arr[@]}; do
|
|
printf "$format" "${optname_arr[$x]}" "${optdesc_arr[$x]}"
|
|
done
|
|
fi
|
|
|
|
# Show aliases
|
|
if [[ -n ${cmd_aliases[$idx]// /} ]]; then
|
|
echo
|
|
echo "Aliases for '${real_cname}': ${cmd_aliases[$idx]}"
|
|
fi
|
|
echo
|
|
}
|
|
|
|
|
|
function getprojidx() { # getprojidx project_name_or_uuid
|
|
local x
|
|
for x in ${!proj_name[@]}; do
|
|
if [[ ${proj_name[$x]} == $1 || ${proj_id[$x]} == $1 ]]; then
|
|
echo "$x"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function getlocidx() { # getlocidx <syd|mlb|etc>
|
|
local x
|
|
for x in ${!loc_name[@]}; do
|
|
if [[ ${loc_name[$x]} == $1 ]]; then
|
|
echo "$x"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function getlocidx_byregion() { # getlocidx <au|uk|etc>
|
|
local x
|
|
for x in ${!loc_region[@]}; do
|
|
if [[ ${loc_region[$x]} == $1 ]]; then
|
|
echo "$x"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function getalllocs() {
|
|
echo ${loc_name[@]}
|
|
}
|
|
|
|
function get() { # getloc <syd|mlb|etc> <name|grid|api|etc>
|
|
local idx
|
|
idx=$(getlocidx $1)
|
|
if [[ $? -ne 0 ]]; then
|
|
return 1
|
|
fi
|
|
eval "echo \${loc_${2}[$idx]}"
|
|
}
|
|
|
|
function runcurlget() { # location api_endpoint(ovname)
|
|
local thisapi thisurl curlres rv
|
|
local thisauthdom u p thisauthstr
|
|
local loc api_andpoint
|
|
loc="$1"
|
|
api_endpoint="$2"
|
|
thisapi=$(get $loc api)
|
|
|
|
[[ -z $thisapi ]] && echo "cant find api for '$loc'" && return 1
|
|
thisurl="$thisapi/$api_endpoint"
|
|
thisurl=${thisurl/_CURPROJECT_/$curprojid}
|
|
debug "url is $thisurl"
|
|
#thisauthdom=$(get $loc authdomain)
|
|
#u=$(head -1 "$AUTHFILE")
|
|
#p=$(tail -1 "$AUTHFILE")
|
|
#thisauthstr="$u@$thisauthdom:$p"
|
|
#unset u
|
|
#unset p
|
|
debug "curl -sk --header 'Accept: application/json' $thisurl"
|
|
curlres=$(curl -sk --header 'Accept: application/json' $thisurl)
|
|
rv=$?
|
|
[[ $rv -ne 0 ]] && error "curl to $thisurl failed"
|
|
echo "$curlres"
|
|
return $rv
|
|
}
|
|
|
|
function runcurldatapost() { # location api_endpoint "curl data"
|
|
local thisapi thisurl curlres rv
|
|
local loc api_endpoint obname ovmethod
|
|
local data=""
|
|
loc="$1"
|
|
api_endpoint="$2"
|
|
data="$3"
|
|
thisapi=$(get $loc api)
|
|
[[ -z $thisapi ]] && echo "cant find api for '$loc'" && return 1
|
|
#thisauthdom=$(get $loc authdomain)
|
|
|
|
debug -f "data is $data"
|
|
thisurl="$thisapi/$api_endpoint"
|
|
thisurl=${thisurl/_CURPROJECT_/$curprojid}
|
|
|
|
debug -f "curl -XPOST -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d \"$data\" $thisurl"
|
|
set -x
|
|
curlres=$(curl -XPOST -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d "$data" $thisurl)
|
|
set +x
|
|
|
|
rv=$?
|
|
[[ $rv -ne 0 ]] && error "curl POST to $thisurl failed"
|
|
|
|
echo "$curlres"
|
|
return $rv
|
|
}
|
|
|
|
function runcurlaction() { # location api_endpoint(ovname) obname ovmethod ["curl data"]
|
|
local thisapi thisurl curlres rv
|
|
#local thisauthdom u p thisauthstr
|
|
local loc api_endpoint obname ovmethod
|
|
local data=""
|
|
#local data="<action/>"
|
|
loc="$1"
|
|
api_endpoint="$2"
|
|
obname="$3"
|
|
ovmethod="$4"
|
|
data="$5"
|
|
thisapi=$(get $loc api)
|
|
[[ -z $thisapi ]] && echo "cant find api for '$loc'" && return 1
|
|
#thisauthdom=$(get $loc authdomain)
|
|
|
|
debug -f "data is $data"
|
|
thisurl="$thisapi/$api_endpoint/$obname"
|
|
[[ -n $ovmethod ]] && thisurl="${thisurl}/$ovmethod"
|
|
thisurl=${thisurl/_CURPROJECT_/$curprojid}
|
|
#u=$(head -1 "$AUTHFILE")
|
|
#p=$(tail -1 "$AUTHFILE")
|
|
#thisauthstr="$u@$thisauthdom:$p"
|
|
#unset u
|
|
#unset p
|
|
|
|
debug -f "curl -XPOST -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d \"$data\" $thisurl"
|
|
curlres=$(curl -XPOST -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d "$data" $thisurl)
|
|
|
|
rv=$?
|
|
[[ $rv -ne 0 ]] && error "curl to $thisurl failed"
|
|
echo "$curlres"
|
|
return $rv
|
|
}
|
|
|
|
# getarrayopt arrname f
|
|
#
|
|
# ie. if myvar=("aaa" "-z" "-ofirst_test" "123" "-osecond_test"
|
|
#
|
|
# then "getarrayopt myvar o" will output "first_test"
|
|
|
|
function getarrayopt() { # getarrayopt arrname optname
|
|
local arrname optname arrtext optval rv newarr
|
|
local localdb
|
|
localdb=0
|
|
arrname="$1"
|
|
optname="$2"
|
|
[[ $localdb -eq 1 ]] && info "look for -$optname in \$$arrname"
|
|
# fix in case array indices aren't sequential
|
|
arrtext=$(eval "echo \"\${${arrname}[@]}\"")
|
|
newarr=( $arrtext )
|
|
[[ $localdb -eq 1 ]] && info "arrtext is '$arrtext'"
|
|
if arraycontains $arrname "-$optname" WILDCARD; then
|
|
local this
|
|
# get array index of given option
|
|
idx=$(echo "${arrtext}" | awk -v lookfor="-$optname" '{ for (x=1;x<=NF;x++) { if (index($x,lookfor) == 1) print (x-1); } }')
|
|
[[ $localdb -eq 1 ]] && info " found at idx $idx"
|
|
this=${newarr[$idx]}
|
|
optval=${this##*-$optname} # strip "-x" from the start
|
|
optval=${optval%% *} # strip all other options from the end
|
|
[[ $localdb -eq 1 ]] && info " optval is $optval"
|
|
rv=0
|
|
else
|
|
[[ $localdb -eq 1 ]] && info " not found"
|
|
optval=""
|
|
rv=1
|
|
fi
|
|
echo "$optval"
|
|
return $rv
|
|
}
|
|
|
|
# convert action name into gns method name
|
|
function getgnsmethod() {
|
|
local rv=0
|
|
case "$1" in
|
|
open) echo "open";;
|
|
close) echo "close";;
|
|
start) echo "start";;
|
|
stop) echo "stop";;
|
|
add) echo "";;
|
|
#migrate) echo "migrate";;
|
|
*)
|
|
echo
|
|
rv=1;
|
|
;;
|
|
esac
|
|
return $rv
|
|
}
|
|
|
|
function makejson() { # makejson key1:val1^key2:val2^...
|
|
local plaindata tok toks key val tnum=1
|
|
plaindata="$1"
|
|
printf "%s" '{'
|
|
IFS='^' read -ra toks <<< "$plaindata"
|
|
for tok in "${toks[@]}"; do
|
|
[[ $tnum -ne 1 ]] && printf "%s" ","
|
|
key=${tok%:*}
|
|
val=${tok#*:}
|
|
printf '"%s":"%s"' "$key" "$val"
|
|
tnum=$((tnum + 1))
|
|
done
|
|
printf "%s\n" '}'
|
|
}
|
|
|
|
function runaction_loc() { # runaction_loc syd|etc api_endpoint action_name ob_uuid ob_name "extra info" [options]
|
|
local thisgrid thisauthdom thisurl thisauthstr curlres loc u p
|
|
local api_endpoint jq_obj opts opts_arr deffield epidx r1 r2 rv
|
|
local actionname method trv extrainfo
|
|
local curldata="" crv
|
|
rv=0
|
|
|
|
loc="$1"
|
|
epidx="$2"
|
|
actionname="$3"
|
|
ob="$4"
|
|
obname="$5"
|
|
extrainfo="$6"
|
|
|
|
thisgrid=$(get $loc grid)
|
|
api_endpoint="${ep_apiendpoint[$epidx]}"
|
|
jq_obj="${ep_jqobj[$epidx]}"
|
|
deffield="${ep_defaultfield[$epidx]}"
|
|
|
|
shift 6
|
|
opts="$*"
|
|
opts_arr=( $opts )
|
|
|
|
debug -f "extrainfo : $extrainfo"
|
|
debug -f "remaining opts: $opts"
|
|
if [[ $actionname == "add" ]]; then
|
|
curldata=$(makejson "$extrainfo")
|
|
fi
|
|
|
|
# special case
|
|
if [[ $actionname == "connect" ]]; then
|
|
local locidx
|
|
# todo: only a single curloc, not multiple
|
|
locidx=$(getlocidx $curlocs)
|
|
if [[ $? -ne 0 ]]; then
|
|
error "Couldn't determine index of current location '$curlocs'"
|
|
return 1
|
|
fi
|
|
tmux ls 2>&1 | egrep -q "^$curproj:"
|
|
if [[ $? -eq 0 ]]; then
|
|
debug "using existing tmux session with: tmux neww -S -n $obname telnet ${loc_engine[$locidx]} ${extrainfo}"
|
|
# create or attach to window
|
|
tmux neww -S -n $obname telnet ${loc_engine[$locidx]} ${extrainfo}
|
|
trv=$?
|
|
else
|
|
debug "making new tmux session with: tmux new -s gnscli -n $obname -d telnet ${loc_engine[$locidx]} ${extrainfo}"
|
|
# create a new session and window
|
|
tmux new -s $curproj -n $obname -d telnet ${loc_engine[$locidx]} ${extrainfo}
|
|
trv=$?
|
|
fi
|
|
if [[ $trv -eq 0 ]]; then
|
|
tmux a -t $curproj
|
|
else
|
|
error "tmux call failed"
|
|
return 1
|
|
fi
|
|
else
|
|
method=$(getgnsmethod "$actionname")
|
|
if [[ $? -ne 0 ]] ; then
|
|
error "Can't determine gns API method for command '$action'"
|
|
return 1
|
|
fi
|
|
|
|
debug -f "location: ${loc}"
|
|
debug -f "api endpoint: ${api_endpoint}"
|
|
debug -f "jq obj: ${jq_obj}"
|
|
debug -f "actionname: ${actionname} (gns method: '$method')"
|
|
debug -f "obname: ${ob}"
|
|
debug -f "options: ${opts}"
|
|
debug -f "curldata: ${curldata}"
|
|
|
|
if [[ $action == "add" ]]; then
|
|
curlres=$(runcurldatapost $loc $api_endpoint "$curldata" )
|
|
crv=$?
|
|
else
|
|
curlres=$(runcurlaction $loc $api_endpoint $ob $method "$curldata" )
|
|
crv=$?
|
|
fi
|
|
if [[ $crv -ne 0 ]]; then
|
|
rv=1
|
|
fi
|
|
debug -f "curlres is [$curlres]"
|
|
echo "$curlres"
|
|
fi
|
|
return $rv
|
|
}
|
|
|
|
function getdata_loc() { # getdata_loc syd|etc api_endpoint [options]
|
|
local thisgrid thisauthdom thisurl thisauthstr curlres loc u p
|
|
local api_endpoint jq_obj opts opts_arr deffield epidx r1 r2 rv
|
|
rv=0
|
|
|
|
loc="$1"
|
|
epidx="$2"
|
|
|
|
thisgrid=$(get $loc grid)
|
|
api_endpoint="${ep_apiendpoint[$epidx]}"
|
|
jq_obj="${ep_jqobj[$epidx]}"
|
|
deffield="${ep_defaultfield[$epidx]}"
|
|
|
|
shift 2
|
|
opts="$*"
|
|
opts_arr=( $opts )
|
|
|
|
debug "options: ${opts}"
|
|
|
|
curlres=$(runcurlget $loc $api_endpoint)
|
|
[[ $? -ne 0 ]] && error "curl to $loc API failed" && return 1
|
|
|
|
debug "outmode is $outmode"
|
|
|
|
if [[ $outmode == "raw" ]]; then
|
|
[[ ! -z $curlres ]] && echo "$curlres" || rv=2
|
|
elif [[ $outmode == "verbose" ]]; then
|
|
#if [[ $locnum -eq 1 ]]; then
|
|
# # bold+underline the heading, and store in a temp file
|
|
# echo "$curlres" | jq -r "$jqfilter" 2>/dev/null | tr -d '"' | awk '{ if (NR == 1) { printf "\033[1m\033[4m" $0 "\033[0m\n" } }' >$HEADINGFILE
|
|
#fi
|
|
debug "jqfilter is $jqfilter"
|
|
debug "r1 pre jqfilter is $curlres"
|
|
r1=$(echo "$curlres" | jq -r "$jqfilter" 2>/dev/null | tr -d '"')
|
|
debug "r1 post jqfilter is $r1"
|
|
#if [[ ! -z $regexpfilter ]]; then
|
|
# [[ $VERBOSE -eq 1 ]] && debug "r1 pre regexpfilter is $r1"
|
|
# r1=$(echo "$r1" | awk -v filter="$regexpfilter" '(NR==1){ print } (NR !=1 && (match($0,filter))) { print }')
|
|
#fi
|
|
[[ ! -z $r1 ]] && echo "$r1" || rv=2
|
|
else # namesonly
|
|
r1=$(echo "$curlres")
|
|
echo "$r1" >/tmp/out
|
|
if [[ ! -z $jqfilter ]]; then
|
|
debug "jqfilter is $jqfilter"
|
|
debug "r1 pre jqfilter is $r1"
|
|
r1=$(echo "$r1" | jq -r "$jqfilter" 2>/dev/null)
|
|
debug "r1 post jqfilter is $r1"
|
|
fi
|
|
if [[ ! -z $jqscript ]]; then
|
|
debug "jqscript is $jqscript"
|
|
debug "r1 pre jqscript is $r1"
|
|
r1=$(echo "$r1" | jq -rf "$jqscript")
|
|
fi
|
|
# if [[ ! -z $regexpfilter ]]; then
|
|
# debug "r1 pre regexpfilter is $r1"
|
|
# r1=$(echo "$r1" | awk -v filter="$regexpfilter" '(NR==1){ print } (NR !=1 && (match($0,filter))) { print }')
|
|
# fi
|
|
if [[ ! -z $awkscript ]]; then
|
|
debug "r1 pre awkscript is $r1"
|
|
r1=$(echo "$r1" | awk -f "$awkscript")
|
|
fi
|
|
debug "semi-final r1 is [$r1]"
|
|
if [[ $cmd == "list" ]]; then
|
|
debug "Global cmd is $GLOBALCMD"
|
|
if [[ $GLOBALCMD == "action" ]]; then
|
|
# ie. we end up with each line being:
|
|
# location,object,object_uuid
|
|
|
|
#r1=$(echo "$r1" | egrep -v "^$" | sed "s/^/$loc,$thisgrid,/")
|
|
r1=$(echo "$r1" | egrep -v "^$" | sed "s/^/$loc,/")
|
|
else
|
|
#r1=$(echo "$r1" | egrep -v "^$" | sed "s/^/$(echo -e "$BOLD")\[$thisgrid\] $(echo -e "$PLAIN")/")
|
|
r1=$(echo "$r1" | egrep -v "^$")
|
|
fi
|
|
fi
|
|
debug "final r1 is [$r1]"
|
|
[[ -n $r1 ]] && echo "$r1" | sed 's/__END__://' || rv=2
|
|
fi
|
|
|
|
#echo "$curlres" | jq -r "$jqfilter" | sed "s/^/$(echo -e "$BOLD")\[$thisgrid\] $(echo -e "$PLAIN")/"
|
|
debug "about to exit with code $rv"
|
|
return $rv
|
|
}
|
|
|
|
function getlocidx() { # getlocidx locname
|
|
local x lookfor
|
|
lookfor="$1"
|
|
for x in ${!loc_name[@]}; do
|
|
if [[ ${loc_name[$x]} == $lookfor ]]; then
|
|
echo "$x"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function getepidx() { # getepidx epname
|
|
local x lookfor
|
|
lookfor="$1"
|
|
for x in ${!ep_name[@]}; do
|
|
if [[ ${ep_name[$x]} == $lookfor || ${ep_alias[$x]} == *\ $lookfor\ * ]]; then
|
|
echo "$x"
|
|
return 0
|
|
elif [[ ${ep_name[$x]} == ${lookfor}s || ${ep_alias[$x]} == *\ ${lookfor}s\ * ]]; then
|
|
echo "$x"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function getepjqobj() {
|
|
local x
|
|
for x in ${!ep_name[@]}; do
|
|
if [[ ${ep_name[$x]} == $1 || ${ep_alias[$x]} == *\ $1\ * ]]; then
|
|
echo "${ep_jqobj[$x]}"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function getepovname() {
|
|
local x
|
|
for x in ${!ep_name[@]}; do
|
|
if [[ ${ep_name[$x]} == $1 || ${ep_alias[$x]} == *\ $1\ * ]]; then
|
|
echo "${ep_apiendpoint[$x]}"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
function getbasefilter() { # epidx [regexp_filter]
|
|
local bf x first epidx local refilter defaultvalue="unknown" thisdefault
|
|
local filterarg
|
|
epidx="$1"
|
|
shift
|
|
|
|
filterarg="$*"
|
|
[[ $filterarg == "*" ]] && filterarg=".*"
|
|
|
|
[[ -n "$*" ]] && refilter="select(.name|test(\"^$*$\"))" || refilter="."
|
|
IFS='#' read -r -a titles <<< "${ep_titles[$epidx]}"
|
|
IFS='#' read -r -a fields <<< "${ep_fields[$epidx]}"
|
|
|
|
bf=""
|
|
for x in ${titles[@]}; do
|
|
x=${x//^/ }
|
|
[[ -z $bf ]] && bf="([" || bf="$bf, "
|
|
bf="${bf}\"${x}\""
|
|
done
|
|
#bf="${bf}] ), (.$jq_obj[] | $refilter | [\"_LOCATION_\""
|
|
bf="${bf}] ), (.[] | $refilter | [\"_LOCATION_\""
|
|
first=1
|
|
for x in ${fields[@]}; do
|
|
thisdefault="defaultvalue"
|
|
[[ $x == ".status" ]] && thisdefault="project_closed"
|
|
x=${x//^/ }
|
|
#bf="${bf}, try(${x}) catch(\"xx\")"
|
|
bf="${bf}, ${x} // \"${thisdefault}\""
|
|
done
|
|
bf="${bf}]) | @csv"
|
|
echo "${bf}"
|
|
}
|
|
|
|
|
|
function profile_start() {
|
|
profile "start profiling"
|
|
prof_start=$(($($GDATE +%s%N)/1000))
|
|
prof_last="$prof_start"
|
|
}
|
|
|
|
function profile_mark() {
|
|
local now secs
|
|
now=$(($($GDATE +%s%N)/1000))
|
|
secs=$(echo "scale=2; ($now - $prof_last) / 1000000;" | bc)
|
|
profile "[+${secs} secs] $* "
|
|
prof_last="$now"
|
|
}
|
|
|
|
# Output CSV contents as a nicely formatted table.
|
|
# If a heading row is detected, it is bolded and underlines.
|
|
# The last table line is also underlined.
|
|
# A blank line is printed before each change of first column value - this is generally the data centre name.
|
|
function csv_to_table() { # csv_to_table num_data_lines_excluding_heading "csv_string_with_newlines"
|
|
local ndatalines msg
|
|
local table
|
|
local data ln
|
|
local uuidcolumn uuidcolumns thisuuid repl line
|
|
|
|
local col colidx
|
|
|
|
profile_start
|
|
|
|
ndatalines="$1"
|
|
shift 1
|
|
msg="$*"
|
|
# table=$(echo -e "$msg" | column -t -s, | awk -v ndatalines=$ndatalines '{ if (match($0, "DC|TOTAL") == 1) { hdgline=1; } else { hdgline=0 } if (!hdgline && pfw != "" && $1 != pfw) { print " " } if (NR == 1 ) { printf "\033[1m\033[4m" $0 "\033[0m\n" } else if (NR == ndatalines ) { printf "\033[4m" $0 "\033[0m\n"} else if (!hdgline || index($0,"TOTAL") == 1) { print } if (!hdgline) { pfw=$1; } }')
|
|
|
|
table=$(echo -e "$msg" | column -t -s, | awk -v ndatalines=$ndatalines 'BEGIN { max=0 } { if (length($0) > max) max=length($0); } { if (match($0, "Server|TOTAL") == 1) { hdgline=1; } else { hdgline=0 } if (!hdgline && pfw != "" && $1 != pfw) { print " " } if (NR == 1 ) { printf "\033[1m\033[4m" $0 "\033[0m\n" } else if (NR == ndatalines) { print; for (n=0;n<max;n++) printf("-"); printf("\n"); } else if (!hdgline || index($0,"TOTAL") == 1) { print } if (!hdgline) { pfw=$1; } }')
|
|
profile_mark "made initial table"
|
|
|
|
# Find out which columns need a UUID lookup
|
|
if [[ $usecache -eq 1 ]]; then
|
|
uuidcolumns=$(csv_find_uuid_cols "$msg")
|
|
table=${table//_UUID/ }
|
|
else
|
|
uuidcolumns=""
|
|
fi
|
|
profile_mark "found uuid columns"
|
|
|
|
# At this point, $table is the full table table without UUID replacement
|
|
ln=1
|
|
table2=""
|
|
while read -r line ; do
|
|
if [[ $ln -gt 1 ]]; then
|
|
# Replace UUIDS with names from cache
|
|
for uuidcolumn in $uuidcolumns; do
|
|
uuidcolumn=$((uuidcolumn - 1))
|
|
#if [[ $ln -eq $ndatalines ]]; then
|
|
# # Account for underline ANSI code
|
|
# uuidcolumn=$(( $uuidcolumn + $UNDERLINE_LENGTH))
|
|
#fi
|
|
thisuuid=${line:${uuidcolumn}:${UUIDLENGTH}}
|
|
debug "uuidcol $uuidcolumn: thisuuid is [$thisuuid]"
|
|
repl=$( printf "%-${UUIDLENGTH}s" "$(uuid_to_name $thisuuid)")
|
|
debug "repl is $repl"
|
|
[[ -n $repl ]] && line="${line/${thisuuid}/$repl}"
|
|
done
|
|
fi
|
|
table2="${table2}${line}\n"
|
|
ln=$((ln + 1))
|
|
done <<< "$table"
|
|
profile_mark "replaced uuids with names"
|
|
|
|
# save RAM in case of super big data
|
|
unset table
|
|
|
|
profile_mark "created colourisation sed script"
|
|
table2=$(echo "$table2" | gsed "$ADD_COLOURS")
|
|
echo -e "$table2"
|
|
|
|
profile_mark "added colours"
|
|
}
|
|
|
|
# Process the first line of CSV contents and try to figure out which
|
|
# columnds contain an object UUID based on the heading.
|
|
function csv_find_uuid_cols() {
|
|
echo "$*" | column -t -s, | egrep "^(Server)" | head -1 | awk 'BEGIN {start=1} { idx=1; while (idx>0) { idx=match(substr($0,start), "[a-zA-Z-]+_UUID"); if (idx>0) { uuidcols = uuidcols " " start+idx-1; start+=(idx+RLENGTH); } } } END { print uuidcols }'
|
|
}
|
|
|
|
|
|
function runaction() { # runaction <nodes|vms|etc> <actionname> targetlist options
|
|
local targetlist
|
|
local loc ob obuuid what api_endpoint jq_obj
|
|
local start end opts opts_a
|
|
local x epidx
|
|
local actionname line all
|
|
local force=0 foreground=0 f rv files n thisfile jqres goterror
|
|
local good allgoodresults errs allbadresults
|
|
local jqf='[ "_DC_", .job.id, "_OB_", .status ] | @csv'
|
|
local jqf_bad='[ "_DC_", "_OB_", .fault.detail, .fault.reason, .status ] | @csv'
|
|
local objecttype extrainfo
|
|
|
|
start=$(($($GDATE +%s%N)/1000))
|
|
lastqsecs=""
|
|
lastprocsecs=""
|
|
what="$1"
|
|
shift
|
|
actionname="$1"
|
|
shift
|
|
targetlist="$1"
|
|
shift
|
|
opts="$*"
|
|
opts_a=( $* )
|
|
|
|
epidx=$(getepidx $what)
|
|
[[ $? -ne 0 ]] && error "unknown endpoint '$what'" && return 1
|
|
|
|
jq_obj="${ep_jqobj[$epidx]}"
|
|
api_endpoint="${ep_apiendpoint[$epidx]}"
|
|
[[ -z $api_endpoint ]] && error "no endpointname for endpoint '$what'" && return 1
|
|
debug "what = $what"
|
|
debug "actionname = $actionname"
|
|
debug "targetlist = $targetlist"
|
|
debug "opts is $opts"
|
|
|
|
arraycontains opts_a "-f" && force=1 || force=0
|
|
arraycontains opts_a "-F" && foreground=1 || foreground=0
|
|
|
|
if [[ $foreground -eq 1 ]]; then
|
|
# just use one line
|
|
line=$(echo "$targetlist" | head -1)
|
|
loc=$(echo "$line" | cut -d, -f1)
|
|
ob=$(echo "$line" | cut -d, -f2)
|
|
obuuid=$(echo "$line" | cut -d, -f3)
|
|
extrainfo="$(echo "$line" | cut -d, -f4-)"
|
|
runaction_loc $loc $epidx $actionname $obuuid $ob "$extrainfo" $opts > "$TMPDIR/run,$loc,$ob"
|
|
else
|
|
pids=""
|
|
while read -r line ; do
|
|
loc=$(echo "$line" | cut -d, -f1)
|
|
ob=$(echo "$line" | cut -d, -f2)
|
|
obuuid=$(echo "$line" | cut -d, -f3)
|
|
extrainfo=$(echo "$line" | cut -d, -f4-)
|
|
debug "extrainfo is: $extrainfo"
|
|
runaction_loc $loc $epidx $actionname $obuuid $ob "$extrainfo" $opts > "$TMPDIR/run,$loc,$ob" &
|
|
pids="$pids $!"
|
|
done <<< "$targetlist"
|
|
|
|
wait $pids
|
|
files=( $TMPDIR/run,* )
|
|
unset ACTIONRES_LOC
|
|
unset ACTIONRES_MSG
|
|
n=0
|
|
errs=0
|
|
good=0
|
|
rv=0
|
|
|
|
# Capitalise first letter
|
|
objecttype=$(echo ${what:0:1} | tr '[a-z]' '[A-Z]')${what:1}
|
|
allgoodresults="Server,${objecttype},Job Status"
|
|
allbadresults=""
|
|
for f in ${!files[@]} ; do
|
|
local thiscsv
|
|
thisfile="${files[$f]}"
|
|
debug "processing file $thisfile"
|
|
debug "contents: $(cat $thisfile)"
|
|
ACTIONRES_LOC[$n]=$(echo "$thisfile" | cut -d, -f2)
|
|
ACTIONRES_OB[$n]=$(echo "$thisfile" | cut -d, -f3)
|
|
jqres=$(cat ${thisfile} | jq . 2>/dev/null)
|
|
if [[ $? -eq 0 ]]; then
|
|
ACTIONRES_MSG[$n]="$jqres"
|
|
else
|
|
ACTIONRES_MSG[$n]=$(cat ${thisfile})
|
|
fi
|
|
|
|
if [[ ${ACTIONRES_MSG[$n]} == *detail*reason* ]]; then
|
|
goterror=1
|
|
elif [[ ${ACTIONRES_MSG[$n]} == *rror* ]]; then
|
|
goterror=1
|
|
elif [[ ${ACTIONRES_MSG[$n]} == *message* ]]; then
|
|
goterror=1
|
|
else
|
|
goterror=0
|
|
fi
|
|
|
|
if [[ $goterror -eq 1 ]]; then
|
|
#thiscsv_bad=$(echo "${ACTIONRES_MSG[$n]}" | jq -r "${jqf_bad}" | sed -e "s/_DC_/${ACTIONRES_LOC[$n]}/g" | sed -e "s/_OB_/${ACTIONRES_OB[$n]}/" | tr -d '"[]' | egrep -v "^$")
|
|
thiscsv_bad=$(echo "${ACTIONRES_MSG[$n]}" | jq -r . | sed -e "s/_DC_/${ACTIONRES_LOC[$n]}/g" | sed -e "s/_OB_/${ACTIONRES_OB[$n]}/" | egrep -v "^$")
|
|
allbadresults="${allbadresults}\n${thiscsv_bad}"
|
|
|
|
errs=$((errs + 1))
|
|
else
|
|
thiscsv=$(echo "${ACTIONRES_MSG[$n]}" | jq -r "${jqf}" | sed -e "s/_DC_/${ACTIONRES_LOC[$n]}/g" | sed -e "s/_OB_/${ACTIONRES_OB[$n]}/" | tr -d '"' | egrep -v "^$")
|
|
allgoodresults="${allgoodresults}\n${thiscsv}"
|
|
good=$((good + 1))
|
|
|
|
#echo -e "$GREEN" >/dev/stderr
|
|
fi
|
|
n=$((n + 1))
|
|
done
|
|
|
|
echo
|
|
if [[ $errs -gt 0 ]]; then
|
|
local fullres_bad
|
|
echo -e "${RED}$errs x '${BOLD}$actionname${PLAIN}${RED}' actions failed:${PLAIN}"
|
|
echo -e "${RED}"
|
|
echo "$allbadresults" | sed -e 's/^/ /'
|
|
echo -e "${PLAIN}"
|
|
echo
|
|
fi
|
|
|
|
if [[ $good -ge 1 ]]; then
|
|
local fullres
|
|
echo -e "${GREEN}$good x '${BOLD}$actionname${PLAIN}${GREEN}' actions submitted successfully.${PLAIN}"
|
|
|
|
fullres=$(csv_to_table $(($good + 1)) "$allgoodresults")
|
|
|
|
echo "$fullres"
|
|
fi
|
|
|
|
if [[ $good -ge 1 ]]; then
|
|
rv=0
|
|
else
|
|
rv=1
|
|
fi
|
|
|
|
rm -f $TMPDIR/run,*
|
|
fi
|
|
|
|
end=$(($($GDATE +%s%N)/1000))
|
|
lastqsecs=$(echo "scale=2; ($end - $start) / 1000000;" | bc)
|
|
return $rv
|
|
}
|
|
|
|
|
|
function validate_action() { # nodes/vms/etv actionname
|
|
local what acion epidx
|
|
what="$1"
|
|
action="$2"
|
|
epidx=$(getepidx $what)
|
|
[[ $? -ne 0 ]] && return 1
|
|
[[ ${ep_validactions[$epidx]} != *\ $action\ * ]] && return 1
|
|
return 0
|
|
}
|
|
|
|
function getdata() { # getdata <nodes|vms|etc> <cmd> options
|
|
local loc what api_endpoint jq_obj
|
|
local start end opts opts_a parallel
|
|
local idx thisopt outfile datawithheading bf
|
|
local basefilter outmode bitswewant x epidx titles fields
|
|
local heading cols wantcols
|
|
local cmd fullres line everything uuidcols ln
|
|
local usecache refilter
|
|
local w greenwords yellowwords redwords uuidcol
|
|
local errordebug=0
|
|
local quiet=0 ignorecase=0
|
|
|
|
start=$(($($GDATE +%s%N)/1000))
|
|
lastqsecs=""
|
|
lastprocsecs=""
|
|
what="$1"
|
|
shift
|
|
cmd="$1"
|
|
shift
|
|
set -f
|
|
opts="$*"
|
|
opts_a=( $* )
|
|
set +f
|
|
|
|
epidx=$(getepidx $what)
|
|
[[ $? -ne 0 ]] && error "unknown endpoint '$what'" && return 1
|
|
|
|
jq_obj="${ep_jqobj[$epidx]}"
|
|
api_endpoint="${ep_apiendpoint[$epidx]}"
|
|
[[ -z $api_endpoint ]] && error "no endpointname for endpoint '$what'" && return 1
|
|
|
|
#outmode=namesonly # global
|
|
if [[ $cmd == "show" ]]; then
|
|
outmode=namesonly # global
|
|
else
|
|
outmode=verbose # global
|
|
fi
|
|
arraycontains opts_a "-r" && outmode=raw
|
|
arraycontains opts_a "-q" && quiet=1
|
|
arraycontains opts_a "-v" && outmode=verbose
|
|
arraycontains opts_a "-s" && outmode=namesonly
|
|
arraycontains opts_a "-c" && ignorecase=1
|
|
|
|
arraycontains opts_a "-n" && usecache=0 || usecache=1
|
|
arraycontains opts_a "-e" && errordebug=1
|
|
|
|
debug "opts is $opts"
|
|
|
|
outfile=$(getarrayopt opts_a o)
|
|
refilter=$(getarrayopt opts_a f)
|
|
|
|
[[ $refilter == "*" ]] && refilter=".*"
|
|
if [[ -n $refilter ]]; then
|
|
obfilter="select(.name|test(\"^$refilter$\""
|
|
if [[ $ignorecase -eq 1 ]]; then
|
|
obfilter="${obfilter}; \"i\"))"
|
|
else
|
|
obfilter="${obfilter}))"
|
|
fi
|
|
else
|
|
obfilter="."
|
|
fi
|
|
[[ -n $refilter ]] && [[ $quiet -eq 0 ]] && info "${what} filter: ^$refilter$"
|
|
|
|
# Allow standard globs rather than regexp globs
|
|
debug "refilter is $refilter"
|
|
|
|
# determine jq filter based on object name
|
|
jqfilter=""
|
|
jqscript=""
|
|
awkscript=""
|
|
|
|
if [[ $cmd == "list" ]]; then
|
|
if [[ $outmode == "verbose" ]]; then
|
|
if [[ -z ${ep_fields[$epidx]} ]]; then
|
|
error "Verbose mode not supported for the '$api_endpoint' endpoint"
|
|
return 1
|
|
else
|
|
#bitswewant=" [ .name, .address, .status, .spm.status ]"
|
|
basefilter=$(getbasefilter $epidx "$refilter")
|
|
|
|
#basefilter="([\"DC\", \"Node \",\"IP \",\"Status\",\"SPM\"] ), (.$jq_obj[] | [\"_LOCATION_\", .name, .address, .status, .spm.status]) | @csv"
|
|
fi
|
|
elif [[ $outmode == "namesonly" ]]; then
|
|
basefilter=".[] | $obfilter | .${ep_defaultfield[$epidx]}"
|
|
debug "(list) basefilter is $basefilter"
|
|
if [[ $GLOBALCMD == "action" ]]; then
|
|
basefilter="${basefilter} + \",\" + .${ep_idfield[$epidx]}"
|
|
if [[ ${ep_name[$epidx]} == "nodes" ]]; then
|
|
basefilter="${basefilter} + \",\" + (.console|tostring)"
|
|
fi
|
|
fi
|
|
fi
|
|
elif [[ $cmd == "show" ]]; then
|
|
if [[ $outmode == "verbose" ]]; then
|
|
error "Verbose mode not supported for show command yet"
|
|
return 1
|
|
else # namesonly
|
|
#basefilter=".$jq_obj[] | select(.name|test(\"${arg_array[1]}\"))"
|
|
basefilter=".[] | $obfilter"
|
|
#basefilter=".[]"
|
|
jqscript="$SCRIPTDIR/${jq_obj}.jq"
|
|
awkscript="$SCRIPTDIR/detail.awk"
|
|
debug "jqscript is $jqscript"
|
|
debug "awkscript is $awkscript"
|
|
|
|
if [[ ! -z $jqscript && ! -e $jqscript ]]; then
|
|
error "'show' not yet implemented for object type '$jq_obj'"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
|
|
locnum=1
|
|
if [[ -z $outfile ]]; then
|
|
pids=""
|
|
notify "Obtaining data from gns API"
|
|
for loc in $curlocs; do
|
|
[[ ! -z $basefilter ]] && jqfilter=${basefilter/_LOCATION_/${loc}}
|
|
[[ $VERBOSE -eq 1 ]] && debug "jqfilter is $jqfilter"
|
|
[[ $VERBOSE -eq 1 ]] && debug "jqscript is $jqscript"
|
|
[[ $VERBOSE -eq 1 ]] && debug "awkscript is $awkscript"
|
|
#echo "jqfilter is:" >/dev/stderr
|
|
#echo "$jqfilter" >/dev/stderr
|
|
debug " starting getdata $loc $epidx $opts"
|
|
getdata_loc $loc $epidx $opts > "$TMPDIR/get.$loc" 2>"$TMPDIR/err.$loc" &
|
|
pids="$pids $!"
|
|
locnum=$((locnum + 1))
|
|
done
|
|
wait $pids
|
|
debug all pids finished
|
|
all=$(cat "$TMPDIR"/get.* 2>/dev/null | tr -d : | egrep -v 'DC|Server' | sed '/^[[:space:]]*$/d')
|
|
if [[ -z $all ]]; then
|
|
fail
|
|
cat "$TMPDIR"/err.*
|
|
rm -f "$TMPDIR"/get.*
|
|
rm -f "$TMPDIR"/err.*
|
|
return 1
|
|
fi
|
|
ok
|
|
|
|
if [[ $errordebug -eq 1 ]]; then
|
|
if [[ $( ls "$TMPDIR"/err.* | wc -l | bc) -gt 0 ]]; then
|
|
cat "$TMPDIR"/err.* >/dev/stderr
|
|
fi
|
|
fi
|
|
debug "combined results: [$all]"
|
|
|
|
end=$(($($GDATE +%s%N)/1000))
|
|
lastqsecs=$(echo "scale=2; ($end - $start) / 1000000;" | bc)
|
|
start=$(($($GDATE +%s%N)/1000))
|
|
|
|
rescount=$(printf %d $(echo -n "$all" | wc -c))
|
|
if [[ $rescount -eq 0 ]]; then
|
|
echo -e " ${BOLD}${YELLOW}(no results)${PLAIN}"
|
|
echo
|
|
rm -f $TMPDIR/get.*
|
|
rm -f $TMPDIR/err.*
|
|
elif [[ $outmode == "verbose" && $cmd == "list" ]]; then
|
|
# totals
|
|
# get col numbers which we need to total
|
|
heading=$(cat $TMPDIR/get.* | head -1 | tr ',' ' ')
|
|
debug "heading is [$heading]"
|
|
cols=( $heading )
|
|
wantcols=""
|
|
for x in ${!cols[@]}; do
|
|
debug " checking ${cols[$x]}"
|
|
if [[ ${cols[$x]} =~ CPUs|RAM|VMs ]]; then
|
|
wantcols="$wantcols,$((x + 1))"
|
|
fi
|
|
done
|
|
debug "wantcols is [$wantcols]"
|
|
if [[ ! -z $wantcols ]]; then
|
|
wantcols="$wantcols,"
|
|
# generate totals line
|
|
cat $TMPDIR/get.* | column -t -s, | awk -vCOLS="${wantcols}" '{ gsub(" GB", "_GB"); if (index($0,"Server") == 1 && maxf == "") { maxf = NF } else { split(COLS, c, ","); for (x in c) { if (c[x]) { this = $(c[x]); rv=gsub("_GB","",this); if (rv!=0) { isgb[c[x]]=1; } tot[c[x]] += this; } } } gsub("_GB"," GB"); } END { line=""; for (i=1; i<=maxf; i++) { if (i==1) { str="TOTAL:"} else if (index(COLS, "," i ",")) { str = tot[i]; } else { str=" "} line = line sprintf("%s%s,",str, isgb[i] ? " GB" : ""); } printf("%s\n",line); }' > "$TOTFILE"
|
|
ndatalines=$(printf %d $(cat $TMPDIR/get.* | wc -l))
|
|
else
|
|
cp /dev/null "$TOTFILE"
|
|
ndatalines=-1
|
|
fi
|
|
|
|
# print combined data (with only one heading row) and totals
|
|
#everything=$(cat $TMPDIR/get.* "$TOTFILE" | column -t -s,)
|
|
everything=$(cat $TMPDIR/get.* "$TOTFILE")
|
|
|
|
notify "Processing data"
|
|
fullres=$(csv_to_table $ndatalines "$everything")
|
|
ok && echo 1>&2
|
|
|
|
echo "$fullres"
|
|
|
|
debug "fullres is: [$fullres]"
|
|
rm -f $TOTFILE
|
|
else
|
|
# ie. show
|
|
fullres=$(cat $TMPDIR/get.*)
|
|
if [[ $outmode == "raw" ]]; then
|
|
echo "$fullres"
|
|
else
|
|
local updated_result
|
|
# Replace UUIDS with names from cache
|
|
# and colourise certain words
|
|
greenwords="true up enabled"
|
|
redwords="false down paused uninitialized disabled"
|
|
yellowwords="migrating"
|
|
uuidcol="$PURPLE"
|
|
|
|
updated_result=""
|
|
while read -r line ; do
|
|
key=${line%%:*}
|
|
val=${line##*:}
|
|
val=$(echo "$val" | sed -e 's/^.* //')
|
|
|
|
if [[ $key =~ [A-Za-z]+\ UUID ]]; then
|
|
repl="$(uuid_to_name ${val})"
|
|
if [[ $? -eq 0 ]]; then
|
|
line="${line/${val}/$repl} (${uuidcol}${val}${PLAIN})"
|
|
else
|
|
line="${line/$val/${uuidcol}${val}${PLAIN}}"
|
|
fi
|
|
#if [[ -n $repl ]]; then
|
|
# #line=$(echo "${line}" | sed -e 's/[A-Za-z]\+ UUID/UUID/')
|
|
# line="${line/${val}/$repl}"
|
|
#fi
|
|
elif [[ $key =~ UUID ]]; then
|
|
# colourise the entire key
|
|
line="${line/${val}/${uuidcol}${val}$PLAIN}"
|
|
fi
|
|
|
|
updated_result="${updated_result}${line}\n"
|
|
done <<< "$fullres"
|
|
fi
|
|
#cat $TMPDIR/get.*
|
|
fi
|
|
|
|
updated_result=$(echo "$updated_result" | gsed "$ADD_COLOURS")
|
|
echo -e "$updated_result"
|
|
rm -f $TMPDIR/get.*
|
|
else
|
|
|
|
# serial
|
|
cp /dev/null "$outfile"
|
|
|
|
notify "Obtaining data from gns API into file"
|
|
for loc in $curlocs; do
|
|
jqfilter=${basefilter/_LOCATION_/${loc}}
|
|
getdata_loc $loc $epidx $opts >> "$outfile"
|
|
locnum=$((locnum + 1))
|
|
done
|
|
|
|
if [[ $errordebug -eq 1 ]]; then
|
|
if [[ $( ls "$TMPDIR"/err.* | wc -l | bc) -gt 0 ]]; then
|
|
cat "$TMPDIR"/err.* >/dev/stderr
|
|
fi
|
|
fi
|
|
|
|
|
|
ok
|
|
#if [[ -e ${HEADINGFILE} ]]; then
|
|
# datawithheading=$(cat ${HEADINGFILE} ${outfile})
|
|
# echo "${datawithheading}" > "$outfile"
|
|
# rm -f $HEADINGFILE
|
|
#fi
|
|
|
|
|
|
|
|
info $(printf "%d line(s) written to %s" $(cat "$outfile" | wc -l) "$outfile" )
|
|
|
|
|
|
fi
|
|
|
|
#end=$(($(gdate +%s%N)/1000))
|
|
#lastqsecs=$(echo "scale=2; ($end - $start) / 1000000;" | bc)
|
|
end=$(($($GDATE +%s%N)/1000))
|
|
lastprocsecs=$(echo "scale=2; ($end - $start) / 1000000;" | bc)
|
|
|
|
}
|
|
|
|
function listcommands() {
|
|
local x wantadm
|
|
if [[ $1 == "ADMIN" ]]; then
|
|
wantadm=1
|
|
prefix='\'
|
|
else
|
|
wantadm=0
|
|
prefix=""
|
|
fi
|
|
|
|
for x in ${!cmd_name[@]}; do
|
|
if [[ ${cmd_adm[$x]} -eq $wantadm ]]; then
|
|
printf " %-10s %s\n" "${prefix}${cmd_name[$x]}" "${cmd_desc[$x]}"
|
|
if [[ $wantadm -eq 0 && ${cmd_name[$x]} == "help" ]]; then
|
|
# show that there is admin help available
|
|
printf " %-10s %s\n" "\\help" "List admin commands."
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
|
|
function generate_random_string() {
|
|
local segmentlen=5 x str="" thissegment
|
|
for x in {1..5}; do
|
|
thissegment=$(LC_CTYPE=C tr -dc a-z0-9 < /dev/urandom | fold -w ${segmentlen} | head -n 1)
|
|
[[ -n $str ]] && str="${str} "
|
|
str="${str}${thissegment}"
|
|
done
|
|
echo "$str"
|
|
}
|
|
|
|
function expand_cmdalias() { # expand_cmdalias "cmd" newcmd_var newargs_var "args"
|
|
local origcmd newcmd x firstword rest defarg changed=0 args
|
|
local newarg_varname newargs
|
|
local newcmd_varname
|
|
local repl_cmd repl_args
|
|
origcmd="$1"
|
|
shift
|
|
newcmd_varname="$1"
|
|
shift
|
|
newarg_varname="$1"
|
|
shift
|
|
args="$*"
|
|
newargs="$args" # default
|
|
newcmd="$origcmd" # default
|
|
for x in ${!cmdalias_src[@]} ; do
|
|
if [[ ${cmdalias_src[$x]} == $origcmd ]]; then
|
|
debug " matched"
|
|
# Matched
|
|
# cmd is FIRST WORD of _dst. rest goes before args.
|
|
repl_cmd="${cmdalias_dst[$x]%% *}"
|
|
repl_args="${cmdalias_dst[$x]#* }"
|
|
newcmd="$repl_cmd"
|
|
if [[ -n $args ]]; then
|
|
newargs="$repl_args $args"
|
|
else
|
|
debug "using default arg $defarg"
|
|
defarg=${cmdalias_defaultarg[$x]}
|
|
defarg=${defarg/_CURPROJECT_/$curproj}
|
|
debug " default arg is: [$defarg]"
|
|
newargs="$repl_args $defarg"
|
|
fi
|
|
changed=1
|
|
break
|
|
fi
|
|
done
|
|
eval "$newcmd_varname=\"$newcmd\""
|
|
eval "$newarg_varname=\"$newargs\""
|
|
if [[ $changed -eq 1 ]]; then
|
|
debug "expanded [$origcmd] to [$cmd], args is [$newargs]"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
function processcmd() {
|
|
local cmd arg newarg rv newlocs x err admin idx opts pipe gotargs
|
|
local whattolist actionname="" actionfilter=""
|
|
local showerror=0 showerroropt=""
|
|
local epidx endpoint newname newtype newtype_uuid
|
|
cmd=$1
|
|
shift
|
|
arg="$*"
|
|
shift
|
|
|
|
[[ -z ${cmd:0:1} ]] && return 0
|
|
[[ ${cmd:0:1} == "#" ]] && return 0
|
|
|
|
if [[ ${cmd:0:1} == \\ ]]; then
|
|
cmd=${cmd:1}
|
|
admin=1
|
|
else
|
|
admin=0
|
|
fi
|
|
|
|
idx=$(getcmdidx "$cmd" $admin)
|
|
rv=$?
|
|
if [[ $rv -ne 0 ]]; then
|
|
# check aliases
|
|
debug "pre replacedargs is [$args]"
|
|
expand_cmdalias "$cmd" replacedcmd replacedargs "$arg"
|
|
debug "post replacedargs is [$replacedargs]"
|
|
cmd="$replacedcmd"
|
|
arg="$replacedargs"
|
|
idx=$(getcmdidx "$cmd" $admin)
|
|
rv=$?
|
|
fi
|
|
|
|
if [[ $rv -ne 0 ]]; then
|
|
if [[ $admin -eq 1 ]]; then
|
|
error "Invalid admin command '\\$cmd'"
|
|
else
|
|
error "Invalid command '$cmd'"
|
|
fi
|
|
return 1
|
|
fi
|
|
|
|
# replace command aliases with base command name
|
|
cmd=${cmd_name[$idx]}
|
|
GLOBALCMD="$cmd"
|
|
|
|
# strip pipe suffix
|
|
if [[ $arg == *\|* ]]; then
|
|
pipe=${arg#*|}
|
|
arg=${arg%%|*}
|
|
else
|
|
pipe="cat"
|
|
fi
|
|
|
|
# strip options from arguments
|
|
declare -a opts
|
|
newarg=""
|
|
for x in $arg; do
|
|
if [[ ${x:0:1} == - ]]; then
|
|
opts+=($x)
|
|
else
|
|
[[ -z $newarg ]] && newarg="$x" || newarg="$newarg $x"
|
|
fi
|
|
done
|
|
arg="$newarg"
|
|
arg_array=( $arg )
|
|
|
|
if arraycontains opts "-h"; then
|
|
showcmdhelp "$cmd"
|
|
return 0
|
|
fi
|
|
if arraycontains opts "-e"; then
|
|
showerror=1
|
|
showerroropt="-e"
|
|
fi
|
|
|
|
gotargs=$( printf %d $(wc -w <<< "$arg"))
|
|
if [[ $gotargs -lt ${cmd_minargs[$idx]} || $gotargs -gt ${cmd_maxargs[$idx]} ]]; then
|
|
error "${cmd_name[$idx]} requires ${cmd_needargs[$idx]} arguments (got $gotargs)"
|
|
return 1
|
|
fi
|
|
obname="" # global
|
|
if [[ $cmd == "show" ]]; then
|
|
endpoint=${arg_array[0]} && unset 'arg_array[0]'
|
|
whattolist=${endpoint}
|
|
[[ ${#arg_array[@]} -ge 1 ]] && opts+=("-f${arg_array[@]}")
|
|
elif [[ $cmd == "list" ]]; then
|
|
endpoint=${arg_array[0]} && unset 'arg_array[0]'
|
|
whattolist=${endpoint}
|
|
[[ ${#arg_array[@]} -ge 1 ]] && opts+=("-f${arg_array[@]}")
|
|
elif [[ $cmd == "action" ]]; then
|
|
endpoint=${arg_array[0]} && unset 'arg_array[0]'
|
|
whattolist=${endpoint}
|
|
actionname=${arg_array[1]} && unset 'arg_array[1]'
|
|
if [[ $actionname == "add" ]]; then
|
|
newname=${arg_array[2]}
|
|
if [[ -z $newname ]]; then
|
|
error "Name of new $whattolist not provided."
|
|
return 1
|
|
fi
|
|
# ie. look for models named 'IOS'
|
|
# oooo ...if we find one, check its template_type
|
|
# if it's qemu then use template_id to figure out appliance id???
|
|
# otherwise just use template_type
|
|
whattolist="model"
|
|
newtype=".*${arg_array[3]}.*"
|
|
actionfilter="-f${newtype}"
|
|
|
|
|
|
|
|
|
|
else
|
|
# start/stop/etc
|
|
if [[ ${#arg_array[@]} -ge 1 ]]; then
|
|
actionfilter="-f${arg_array[@]}"
|
|
obname="${arg_array[@]}"
|
|
else
|
|
error "Name of $whattolist not provided."
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
rv=1
|
|
if [[ $cmd == "help" && -n $arg ]]; then
|
|
showcmdhelp "$arg"
|
|
rv=$?
|
|
elif [[ $admin -eq 1 ]]; then
|
|
case ${cmd} in
|
|
ls)
|
|
listservers
|
|
rv=$?
|
|
;;
|
|
lp)
|
|
listprojects
|
|
rv=$?
|
|
;;
|
|
p)
|
|
setproject "${arg}"
|
|
rv=$?
|
|
start_recache_if_needed
|
|
;;
|
|
c)
|
|
start_recache
|
|
rv=$?
|
|
[[ $rv -ne 0 ]] && error "UUID re-cache is already in progress (PID $recache_pid)"
|
|
|
|
;;
|
|
sc)
|
|
if [[ -z ${arg} ]]; then
|
|
for x in ${!uuid_id[@]}; do
|
|
echo "UUID: ${uuid_id[$x]} -> ${uuid_name[$x]}"
|
|
done
|
|
else
|
|
echo " UUID ${arg} -> $(uuid_to_name ${arg})"
|
|
fi
|
|
;;
|
|
q)
|
|
DONE=1
|
|
;;
|
|
help)
|
|
listcommands ADMIN
|
|
;;
|
|
*)
|
|
error "Admin command '$cmd' not implemented"
|
|
rv=1
|
|
;;
|
|
esac
|
|
else
|
|
case ${cmd} in
|
|
list|show)
|
|
getdata ${whattolist} $cmd ${opts[@]} >"$TMPFILE"
|
|
rv=$?
|
|
[[ -e $TMPFILE ]] && cat "$TMPFILE" | $pipe
|
|
if [[ $rv -eq 0 ]]; then
|
|
local timestr
|
|
timestr=$(printf "query time: %s seconds" "$lastqsecs")
|
|
[[ -n $lastprocsecs ]] && timestr="${timestr}$(printf ", processing time: %s secs" "$lastprocsecs")"
|
|
echo
|
|
info "$timestr"
|
|
fi
|
|
rm -f "$TMPFILE"
|
|
;;
|
|
action)
|
|
# TODO: ooremove any output format opts
|
|
|
|
validate_action ${endpoint} $actionname
|
|
if [[ $? -ne 0 ]]; then
|
|
error "'$actionname' is not a valid action for ${endpoint}s"
|
|
return 1
|
|
fi
|
|
|
|
epidx=$(getepidx $endpoint)
|
|
if [[ $? -ne 0 ]]; then
|
|
error "'$endpoint' is not a valid endpoint"
|
|
return 1
|
|
fi
|
|
|
|
# Get a list of objects to operate on
|
|
# ie. turn regexp into a list of dcs and obnames first
|
|
getdata ${whattolist} list $actionfilter $showerroropt -c -s -q >"$TMPFILE"
|
|
rv=$?
|
|
if [[ $rv -ne 0 ]]; then
|
|
error "Query for matching ${whattolist}s failed."
|
|
rv=1
|
|
elif [[ ! -e $TMPFILE ]]; then
|
|
error "No matching ${whattolist}s found."
|
|
rv=1
|
|
elif grep -q "no results" $TMPFILE; then
|
|
error "No ${whattolist}s found matching '$obname'."
|
|
rv=1
|
|
elif [[ ! -e $TMPFILE ]]; then
|
|
error "Results file does not exist."
|
|
rv=1
|
|
else
|
|
local ndcs nobs data confirm=0
|
|
ok
|
|
debug "tmpfile contents: [$(cat $TMPFILE)]"
|
|
echo
|
|
data=$(cat "$TMPFILE" | egrep -v "^$")
|
|
|
|
# how many servers?
|
|
ndcs=$( printf %d $(echo "$data" | awk -F, '{ print $1 }' | sort -u | wc -l))
|
|
[[ $ndcs -eq 1 ]] && dc_ess="" || dc_ess="s"
|
|
nobs=$( printf %d $(echo "$data" | awk -F, '{ print $2 }' | sort -u | wc -l))
|
|
[[ $nobs -eq 1 ]] && ob_ess="" || ob_ess="s"
|
|
|
|
if [[ $actionname == "connect" ]]; then
|
|
if [[ $nobs -gt 1 ]]; then
|
|
error "Can't run '$actionname' with multiple objects"
|
|
return 1
|
|
else
|
|
confirm=1
|
|
fi
|
|
elif [[ $actionname == "add" ]]; then
|
|
local allobs alluuids o_arr ou_arr
|
|
allobs=$(echo "$data" | awk -F, '{ print $2 }' | sort -u)
|
|
alluuids=$(echo "$data" | awk -F, '{ print $3 }' | sort -u)
|
|
o_arr=($allobs)
|
|
ou_arr=($alluuids)
|
|
|
|
if [[ $nobs -gt 1 ]]; then
|
|
local o n
|
|
info "Matched multiple ${whattolist}s:"
|
|
newtype=""
|
|
while [[ -z $newtype ]]; do
|
|
echo
|
|
n=1
|
|
while read -r o; do
|
|
printf "%3d. %s\n" $n "${o_arr[$n]}"
|
|
n=$((n + 1))
|
|
done <<<"$allobs"
|
|
echo
|
|
getstr ":" "" "Select one (q to abort)"
|
|
if [[ -n $retstr ]]; then
|
|
if [[ $retstr == "q" ]]; then
|
|
break
|
|
elif [[ $retstr =~ ^[0-9]*$ ]]; then
|
|
if [[ $retstr -le 0 || $retstr -ge $n ]]; then
|
|
error "Invalid selection"
|
|
else
|
|
newtype="${o_arr[$retstr]}"
|
|
newtype_uuid="${ou_arr[$retstr]}"
|
|
fi
|
|
else
|
|
local matched=0 x allmatches="" this thisuuid
|
|
for x in ${!o_arr[@]}; do
|
|
this="${o_arr[$x]}"
|
|
thisuuid="${ou_arr[$x]}"
|
|
shopt -s nocasematch
|
|
if [[ ${this} =~ $retstr ]]; then
|
|
newtype="$this"
|
|
newtype_uuid="$thisuuid"
|
|
allmatches="$allmatches [$this]"
|
|
matched=$((matched + 1))
|
|
fi
|
|
shopt -u nocasematch
|
|
done
|
|
if [[ $matched -eq 0 ]]; then
|
|
error "'$retstr' doesn't match any choice"
|
|
newtype=""
|
|
elif [[ $matched -gt 1 ]]; then
|
|
error "'$retstr' matched multiple choices: $allmatches"
|
|
newtype=""
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
done
|
|
else
|
|
newtype="$allobs"
|
|
newtype_uuid="$alluuids"
|
|
fi
|
|
if [[ -z $newtype ]]; then
|
|
confirm=0
|
|
else
|
|
notify_nodots "Adding a new ${BOLD}$newtype${PLAIN}${PURPLE} named $BOLD$newname${PLAIN}"
|
|
confirm=1
|
|
fi
|
|
else
|
|
echo -e "${PURPLE}About to run '${BOLD}$actionname${PLAIN}${PURPLE}' on ${BOLD}${nobs}${PLAIN}${PURPLE} ${whattolist}${ob_ess} on ${BOLD}${ndcs}${PLAIN}${PURPLE} server${dc_ess}:${PLAIN}"
|
|
echo "$data" | awk -F, "BEGIN {lastdc=\"\"} { if (\$1 != lastdc) { print \" ${YELLOW}- ${BOLD}\" \$1 \"${PLAIN}\"; lastdc=\$1; } print \" ${YELLOW}- \" \$2 \"${PLAIN}\"}"
|
|
|
|
echo
|
|
if [[ $ndcs -le 1 ]]; then
|
|
getyn n "Really proceed"
|
|
[[ $? -eq 0 ]] && confirm=1 || confirm=0
|
|
else
|
|
local confirmcode entered_string
|
|
confirmcode=$(generate_random_string)
|
|
notify_nodots "Confirmation code is: ${BOLD}${confirmcode}${PLAIN}"
|
|
getstr ":" "n" "Enter the above code to proceed, anything else will abort"
|
|
entered_string="$retstr"
|
|
[[ $entered_string == $confirmcode ]] && confirm=1 || confirm=0
|
|
fi
|
|
fi
|
|
|
|
if [[ $confirm -eq 1 ]]; then
|
|
if [[ $actionname == "add" ]]; then
|
|
# We use alternate values for the actiontarget string here:
|
|
# loc=servername (normal)
|
|
# ob=name of new ob to add
|
|
# obuuid=uuid of model
|
|
# extrainfo=curl post data
|
|
actiontargets="$curlocs,$newname,$newtype_uuid"
|
|
# ooo
|
|
#actiontargets="${actiontargets},'{\"name\": \"$newname\", \"node_type\": \"$newtype\", \"compute_id\": \"local\"}'"
|
|
actiontargets="${actiontargets},name:$newname^node_type:$newtype^compute_id:local"
|
|
else
|
|
actiontargets=$(echo "$data")
|
|
fi
|
|
if [[ $actionname == "connect" ]]; then
|
|
local devname sevname srvport
|
|
devname=$(echo "$data" | awk -F, '{ print $2 }')
|
|
srvname=$(echo "$data" | awk -F, '{ print $1 }')
|
|
srvport=$(echo "$data" | awk -F, '{ print $4 }')
|
|
notify_nodots "Connecting to $devname ($srvname:$srvport)"
|
|
opts+=("-F")
|
|
else
|
|
notify "Submitting actions to gns API"
|
|
fi
|
|
rm -f "$TMPFILE"
|
|
|
|
debug "about to call runaction with:"
|
|
debug " obtype = $endpoint"
|
|
debug " actionname = $actionname"
|
|
debug " actiontargets = $actiontargets"
|
|
debug " opts = ${opts[@]}"
|
|
debug " outputfile = ${TMPFILE}"
|
|
runaction ${endpoint} $actionname "$actiontargets" ${opts[@]} >"$TMPFILE"
|
|
rv=$?
|
|
if [[ $actionname != "connect" ]]; then
|
|
ok
|
|
[[ -e $TMPFILE ]] && cat "$TMPFILE" | $pipe
|
|
echo
|
|
info $(printf "action submission time: %s seconds" "$lastqsecs")
|
|
fi
|
|
fi
|
|
fi
|
|
rm -f "$TMPFILE"
|
|
;;
|
|
exit)
|
|
DONE=1
|
|
;;
|
|
help)
|
|
listcommands NOADMIN
|
|
;;
|
|
*)
|
|
error "Command '$cmd' not implemented"
|
|
rv=1
|
|
;;
|
|
esac
|
|
fi
|
|
return $rv
|
|
}
|
|
|
|
function setproject() { # setproject [project_name]
|
|
local newproj x err rv idx
|
|
newproj="${1}"
|
|
err=0
|
|
if [[ -z $newproj ]]; then
|
|
newproj=${proj_name[0]}
|
|
if [[ -z $newproj ]]; then
|
|
error "Could not set default project - none found on server!"
|
|
return 1
|
|
fi
|
|
fi
|
|
idx=$(getprojidx ${newproj})
|
|
if [[ $? -ne 0 ]]; then
|
|
error "invalid project '$newproj'"
|
|
err=1
|
|
fi
|
|
curprojid=${proj_id[$idx]}
|
|
|
|
if [[ $err -eq 0 ]]; then
|
|
curproj="${newproj}"
|
|
info "Project set to: $curproj ($curprojid)"
|
|
rv=0
|
|
else
|
|
rv=1
|
|
fi
|
|
|
|
return $rv
|
|
}
|
|
|
|
function checkfor() { # checkfor name [pkg_name]
|
|
local what pkgname
|
|
local hw os ok=0 confirm=0
|
|
local alt rv
|
|
|
|
what=$1
|
|
pkg_name=${2:-$what}
|
|
os=$(uname -s)
|
|
hw=$(uname -m)
|
|
if [[ $hw == "aarch64" ]]; then # Android Termuz
|
|
alias which="command -v"
|
|
fi
|
|
which $what >/dev/null 2>&1
|
|
rv=$?
|
|
if [[ $rv -ne 0 && $what == "gdate" ]]; then
|
|
date --version 2>/dev/null | grep -q GNU 2>/dev/null
|
|
if [[ $? -eq 0 ]]; then
|
|
GDATE=$(which date 2>/dev/null)
|
|
what=${GDATE}
|
|
rv=0
|
|
fi
|
|
fi
|
|
|
|
if [[ $? -eq 0 ]]; then
|
|
ok=1
|
|
else
|
|
error "$what binary not found in \$PATH."
|
|
getyn n "Try to install it?"
|
|
[[ $? -eq 0 ]] && confirm=1 || confirm=0
|
|
if [[ $confirm -eq 1 ]]; then
|
|
case $os in
|
|
Darwin)
|
|
brew install $pkg_name && ok=1
|
|
;;
|
|
Linux)
|
|
if [[ -f /etc/debian_version ]]; then
|
|
apt-get install $pkg_name && ok=1
|
|
elif [[ -f /etc/redhat_release ]]; then
|
|
apt-get install $pkg_name && ok=1
|
|
fi
|
|
;;
|
|
*)
|
|
echo "Don't know how to install stuff on OS '$os'"
|
|
;;
|
|
esac
|
|
[[ $ok -eq 1 ]] && info "Successfully installed $pkg_name" || error "Failed to install $pkg_name"
|
|
fi
|
|
fi
|
|
which $what >/dev/null 2>&1
|
|
return $?
|
|
}
|
|
|
|
function killtmux() {
|
|
tmux kill-session -t "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
function cleanup() {
|
|
local x
|
|
echo
|
|
notify "Killing remaining tmux sessions"
|
|
for x in ${proj_name[@]}; do
|
|
killtmux "$x"
|
|
done
|
|
ok
|
|
notify "Cleaning up temporary files"
|
|
[[ -n $TMPDIR && -d $TMPDIR ]] && rm -f "$TMPDIR/*"
|
|
ok
|
|
}
|
|
|
|
function processarg() {
|
|
case "$i" in
|
|
h)
|
|
usage;
|
|
exit 1;
|
|
;;
|
|
I)
|
|
scriptstoinline
|
|
exit $?;
|
|
;;
|
|
i)
|
|
initauth;
|
|
exit $?;
|
|
;;
|
|
p)
|
|
DEFPROJECT="${OPTARG}"
|
|
;;
|
|
P)
|
|
PROFILING=1
|
|
;;
|
|
v)
|
|
VERBOSE=1
|
|
info verbose mode
|
|
;;
|
|
*)
|
|
error "invalid argument: $i";
|
|
usage;
|
|
;;
|
|
esac
|
|
}
|
|
|
|
function loadservers() {
|
|
local sname shost sport
|
|
if [[ -e $SRVFILE ]]; then
|
|
notify "Loading servers from $SRVFILE"
|
|
while read -r f ; do
|
|
sname=$(echo "$f" | awk -F: '{ print $1 }')
|
|
shost=$(echo "$f" | awk -F: '{ print $2 }')
|
|
sport=$(echo "$f" | awk -F: '{ print $3 }')
|
|
if [[ -z $sname || -z $shost || -z $sport ]]; then
|
|
fail
|
|
error "Bad line in server file: $f"
|
|
return 1
|
|
fi
|
|
addloc $sname $shost $sport
|
|
|
|
done < <(egrep -v "(^#|^$)" $SRVFILE)
|
|
fi
|
|
ok "Got $nlocs servers"
|
|
return 0
|
|
}
|
|
|
|
BOLD="\033[1m"
|
|
PLAIN="\033[0m"
|
|
ITALIC="\033[3m"
|
|
UNDERLINE="\033[4m"
|
|
RED="\033[31m"
|
|
YELLOW="\033[33m"
|
|
GREEN="\033[32m"
|
|
BLUE="\033[34m"
|
|
PURPLE="\033[35m"
|
|
CYAN="\033[36m"
|
|
LINK="$BLUE$UNDERLINE"
|
|
|
|
UNDERLINE_PRINTED=$(echo -en "$UNDERLINE")
|
|
UNDERLINE_LENGTH=${#UNDERLINE_PRINTED}
|
|
|
|
# Generate sed script to add colours to certain words
|
|
UUIDCHAR="[0-9a-f]"
|
|
UUID_REGEXP=${UUIDCHAR}\{8\}-${UUIDCHAR}\{4\}-${UUIDCHAR}\{4\}-${UUIDCHAR}\{4\}-${UUIDCHAR}\{12\}
|
|
WORDCOLOURS=( "${GREEN}" "${RED}" "${YELLOW}" )
|
|
UUIDCOL="$PURPLE"
|
|
COLOURED_WORDS=( "true\|up\|enabled\|complete\|finished\|started\|opened" "false\|down\|paused\|uninitialized\|disabled\|failed\|stopped\|closed" "migrating\|powering_up\|project_closed\|unknown" )
|
|
ADD_COLOURS=""
|
|
for colidx in ${!WORDCOLOURS[@]}; do
|
|
col=${WORDCOLOURS[$colidx]}
|
|
ADD_COLOURS="${ADD_COLOURS}s/\b\(${COLOURED_WORDS[$colidx]}\)\b/\\${col}\\1\\${PLAIN}/g;"
|
|
done
|
|
ADD_COLOURS="${ADD_COLOURS}s/\(${UUID_REGEXP}\)/${UUIDCOL}\\1${PLAIN}/g"
|
|
|
|
|
|
nlocs=0
|
|
neps=0
|
|
|
|
|
|
|
|
|
|
# addendpoint ep_name ep_apiendpoint ep_jqobjectname idfieldname [defaultfieldname]
|
|
addendpoint projects projects project project_id name
|
|
addepalias projects p
|
|
addeptitles projects "Project " "UUID" "Status "
|
|
addepfields projects ".name" ".project_id" ".status"
|
|
addepactions projects open close
|
|
|
|
addendpoint nodes projects/_CURPROJECT_/nodes node node_id name
|
|
addepalias nodes n
|
|
addeptitles nodes "Node " "Model_UUID" "Status " "Console_Port"
|
|
addepfields nodes ".name" ".template_id" ".status" ".console"
|
|
addepactions nodes start stop connect add
|
|
|
|
addendpoint links projects/_CURPROJECT_/links link link_id link_id
|
|
addepalias links l
|
|
addeptitles links "Type" "A-Host_UUID " "A-Port" "Z-Host_UUID " "Z-Port"
|
|
addepfields links ".link_type" ".nodes[0].node_id" ".nodes[0].label.text" ".nodes[1].node_id" ".nodes[1].label.text"
|
|
|
|
addendpoint models templates model template_id name
|
|
addepalias models m
|
|
addeptitles models "Name" "Category" "UUID"
|
|
addepfields models ".name" ".category" ".template_id"
|
|
|
|
|
|
addcmd list "List elements of a given type (eg. nodes, links, etc)" 1+ ls l
|
|
addcmdusage list "object_type [regexp_filter]" "object_type can be: [${ep_name[*]}]"
|
|
addcmdoption list "-n" "Don't resolve UUIDs to names"
|
|
addcmdoption list "-o filename" "Send output to 'filename'"
|
|
addcmdoption list "-r" "Raw mode - show raw JSON from gns API"
|
|
addcmdoption list "-v" "Verbose mode - show object details"
|
|
addcmd show "Show detail of a given element (eg. compute node, VM, etc)" 2 sh
|
|
addcmdusage show "object_type [regexp_filter]" "object_type can be: [${ep_jqobj[*]}]"
|
|
addcmdoption show "-n" "Don't resolve UUIDs to names"
|
|
addcmdoption show "-o filename" "Send output to 'filename'"
|
|
addcmdoption show "-r" "Raw mode - show raw JSON from gns API"
|
|
|
|
addcmd action "Perform action on given object." 3 act do
|
|
declare -a lines
|
|
for x in ${!ep_jqobj[@]}; do
|
|
if [[ -z ${ep_validactions[$x]} ]]; then
|
|
lines+=("${ep_jqobj[$x]} actions: n/a")
|
|
else
|
|
lines+=("${ep_jqobj[$x]} actions: ${ep_validactions[$x]}")
|
|
fi
|
|
done
|
|
addcmdusage action "object_type action_type regexp_filter" "object_type can be: [${ep_jqobj[*]}]" "" "${lines[@]}"
|
|
addcmdoption action "-f" "Force - don't ask for confirmation"
|
|
|
|
addcmdalias "open" "action project open" "_CURPROJECT_"
|
|
addcmdalias "close" "action project close" "_CURPROJECT_"
|
|
addcmdalias "start" "action node start" ""
|
|
addcmdalias "stop" "action node stop" ""
|
|
addcmdalias "connect" "action node connect" ""
|
|
addcmdalias "c" "action node connect" ""
|
|
addcmdalias "add" "action node add" ""
|
|
|
|
addcmd help "List regular commands" 0 "?" "h"
|
|
addcmd exit "Exit from gnscli" 0 quit
|
|
addcmd -a help "List admin commands" 0 "?" "h"
|
|
addcmd -a p "Select project filter (comma or space separated list)" 1 loc
|
|
addcmdusage p "project_list" "project_list is a comma-separated list made up of [${loc_name[*]}]"
|
|
addcmd -a lp "List GNS3 projects on current server(s)" 0 listprojects
|
|
addcmd -a ls "List GNS3 servers" 0 listservers l
|
|
addcmd -a c "Re-cache UUIDs for current projects" 0 cache
|
|
addcmd -a sc "Show cached name for given UUID (or all UUIDs if none provided)." 0-1 showcache
|
|
addcmdusage sc "[uuid]" "If UUID is not provided, all UUID-to-name mappings are shown."
|
|
addcmd -a q "Exit from gnscli" 0
|
|
|
|
|
|
VERBOSE=0
|
|
GDATE="gdate"
|
|
CONFDIR="$HOME/.gnscli"
|
|
HISTFILE="$CONFDIR/history"
|
|
TMPDIR="$CONFDIR/tmp"
|
|
SCRIPTDIR="$CONFDIR/scripts"
|
|
#HEADINGFILE="$CONFDIR/head.tmp"
|
|
TMPFILE="$TMPDIR/temp.tmp"
|
|
TOTFILE="$TMPDIR/tot.tmp"
|
|
#AUTHFILE="$CONFDIR/auth"
|
|
CACHEDIR="$CONFDIR/uuid_cache"
|
|
CACHEFILEBASE="$CACHEDIR/mappings"
|
|
RCFILE="$CONFDIR/rc"
|
|
SRVFILE="$CONFDIR/servers"
|
|
UUIDLENGTH=36
|
|
CACHING=""
|
|
PROFILING=0
|
|
DEFPROJECT=""
|
|
curproj=""
|
|
|
|
nmsgq=0
|
|
|
|
recache_pid=""
|
|
|
|
THISSCRIPT="$0"
|
|
|
|
|
|
RCARGS=""
|
|
if [[ -e $RCFILE ]]; then
|
|
info "processing $RCFILE"
|
|
while read -r f ; do
|
|
RCARGS="$f $ALLARGS"
|
|
done < <(egrep -v "(^#|^$)" $RCFILE)
|
|
fi
|
|
|
|
|
|
VALIDARGS="hiIp:Pv"
|
|
while getopts "$VALIDARGS" i $RCARGS; do
|
|
processarg "$i"
|
|
done
|
|
OPTIND=0
|
|
while getopts "$VALIDARGS" i ; do
|
|
processarg "$i"
|
|
done
|
|
shift $((OPTIND - 1))
|
|
|
|
|
|
if [[ ! -e $TMPDIR ]]; then
|
|
error "Temporary file dir $TMPDIR doesn't exist. Use -i to create it."
|
|
exit 1
|
|
else
|
|
rm -fr ${TMPDIR}/*.tmp
|
|
rm -fr ${TMPDIR}/get.*
|
|
rm -fr ${TMPDIR}/run.*
|
|
fi
|
|
if [[ ! -d $CONFDIR ]]; then
|
|
error "Configuration directory $CONFDIR doesn't exist. Use -i to create it."
|
|
exit 1
|
|
fi
|
|
|
|
checkfor gdate coreutils jq || exit 1
|
|
|
|
loadservers
|
|
if [[ $nlocs -lt 1 ]]; then
|
|
error "No servers found - check $SRVFILE"
|
|
exit 1
|
|
fi
|
|
alllocs=$(getalllocs)
|
|
|
|
# prepopulate list of projects
|
|
notify "Getting initial projectlist"
|
|
oldverbose=$VERBOSE
|
|
VERBOSE=1
|
|
errfile="$TMPDIR"/err
|
|
output=$(getdata projects show -e -r 2>"$errfile")
|
|
rv=$?
|
|
VERBOSE=$oldverbose
|
|
if [[ $rv -eq 0 ]]; then
|
|
ok
|
|
#cat "$errfile"
|
|
rm -f "$errfile"
|
|
jqoutput=$(echo "$output" | jq -r '.[] | .project_id + "|" + .name' 2>"$errfile")
|
|
rv=$?
|
|
if [[ $rv -ne 0 || -z $jqoutput ]]; then
|
|
error "$rv Got bad data from initial project list query to API"
|
|
echo -e "${RED}${BOLD}Errors:${PLAIN}"
|
|
echo -en "${RED}"
|
|
cat "${errfile}" | sed -e 's/^/ /'
|
|
echo -en "${PLAIN}"
|
|
rm -f "$errfile"
|
|
exit 1
|
|
fi
|
|
rm -f "$errfile"
|
|
|
|
for x in $jqoutput; do
|
|
id=${x%|*}
|
|
name=${x#*|}
|
|
addproject $id $name
|
|
done
|
|
else
|
|
fail
|
|
error "Could not obtain initial project list from API"
|
|
echo -e "${RED}${BOLD}Output:${PLAIN}"
|
|
echo -en "${RED}"
|
|
echo "${output}" | sed -e 's/^/ /'
|
|
echo -en "${PLAIN}"
|
|
echo -e "${RED}${BOLD}Errors:${PLAIN}"
|
|
echo -en "${RED}"
|
|
cat "${errfile}" | sed -e 's/^/ /'
|
|
echo -en "${PLAIN}"
|
|
rm -f "$errfile"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ${#proj_id[@]} -eq 0 ]]; then
|
|
error "No projects found on server!!"
|
|
exit 1
|
|
fi
|
|
|
|
setproject $DEFPROJECT || exit 1 # defaults to first one
|
|
|
|
[[ -e $HISTFILE ]] && history -r "$HISTFILE"
|
|
loadcachefile $curlocs
|
|
dumpmsgq
|
|
|
|
trap cleanup EXIT
|
|
|
|
echo "Unofficial GNS3 cli v${VER}"
|
|
echo -e "${ITALIC}${YELLOW}Note: this script is still in development and may have bugs!${PLAIN}"
|
|
|
|
if [[ $# -gt 0 ]]; then
|
|
processcmd $*
|
|
else
|
|
DONE=0
|
|
while [[ $DONE -ne 1 ]]; do
|
|
lastqsecs=0
|
|
#echo -en "${pstr}"
|
|
getcmd cmd || break
|
|
set -f
|
|
processcmd $cmd
|
|
set +f
|
|
if [[ ! -z $cmd ]]; then
|
|
history -s "$cmd"
|
|
history -w "$HISTFILE"
|
|
fi
|
|
dumpmsgq # show any queued messages
|
|
done
|
|
fi
|
|
|
|
exit 0
|
|
|
|
#START_INLINE:model.jq
|
|
"UUID#" + .template_id,
|
|
"Name#" + .name,
|
|
"Usage#" + .usage,
|
|
"Category#" + .category,
|
|
"RAM#" + .ram + "MB",
|
|
"Action on close#" + .on_close,
|
|
"Firewall Type#" + .firewall_type,
|
|
"Image#" + .hda_disk_image,
|
|
"__END__"
|
|
#END_INLINE:model.jq
|
|
|
|
#START_INLINE:project.jq
|
|
"UUID#" + .project_id,
|
|
"Name#" + .name,
|
|
"Status#" + .status,
|
|
"Filename#" + .filename,
|
|
"Path#" + .path,
|
|
"Auto-Open#" + .auto_open,
|
|
"Auto-Close#" + .auto_close,
|
|
"Auto-Start#" + .auto_start,
|
|
"__END__"
|
|
#END_INLINE:project.jq
|
|
#START_INLINE:detail.awk
|
|
BEGIN {
|
|
FS="#"
|
|
nlines=0
|
|
BOLDBLUE="\033[1m\033[36m"
|
|
PLAIN="\033[0m"
|
|
}
|
|
{
|
|
key[nlines]=$1
|
|
val[nlines++]=$2
|
|
if (length($1)>max) max=length($1);
|
|
}
|
|
|
|
END {
|
|
nvalid=0
|
|
for (i=0;i<nlines;i++) {
|
|
if (length(key[i]) + length(val[i]) >= 1) {
|
|
printf( BOLDBLUE"%" max "s:" PLAIN " %s\n",key[i],val[i]);
|
|
nvalid++;
|
|
}
|
|
}
|
|
if (nvalid >= 1) printf(" \n");
|
|
}
|
|
#END_INLINE:detail.awk
|
|
#START_INLINE:node.jq
|
|
"Hostname#" + .name,
|
|
"UUID#" + .node_id,
|
|
"Model UUID#" + .template_id,
|
|
"Project UUID#" + .project_id,
|
|
"Status#" + .status,
|
|
"Port format#" + .port_name_format,
|
|
"Locked#" + (.locked // "n/a"),
|
|
"Console port#" + ((.console|tostring) // "n/a"),
|
|
"Port count#" + try (.ports[] | length) catch ("unknown"),
|
|
#END_INLINE:node.jq
|
|
#START_INLINE:link.jq
|
|
"UUID#" + .link_id,
|
|
"Type#" + .link_type,
|
|
"Project UUID#" + .project_uuid,
|
|
"A-Node#" + .node[0].node_id,
|
|
"A-Port#" + .node[0].label.text,
|
|
"Z-Node#" + .node[1].node_id,
|
|
"Z-Port#" + .node[1].label.text,
|
|
"Capturing#" + .capturing,
|
|
"Suspended#" + .suspend,
|
|
"__END__"
|
|
#END_INLINE:link.jq
|