2021-12-19 23:31:58 +11:00
#!/bin/bash
2022-01-03 11:26:59 +11:00
2022-01-01 16:46:02 +11:00
# rename node issue when matching multiple
# rename project
2021-12-19 23:31:58 +11:00
# node stop givesjq error
# connect xxx & [opens iterm (or whatever)]
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
2022-01-03 11:26:59 +11:00
[ [ $VERBOSE -eq 1 ] ] && info " Caching UUID data for $loc $epname "
2021-12-19 23:31:58 +11:00
epidx = $( getepidx $epname )
api_endpoint = " ${ ep_apiendpoint [ $epidx ] } "
curlres = $( runcurlget $loc $api_endpoint )
[ [ $? -ne 0 ] ] && error " curl to $thisurl failed " && return 1
2022-01-03 13:35:56 +11:00
if [ [ ${ ep_name [ $epidx ] } = = "links" ] ] ; then
echo " $curlres " | jq -r " try (. | to_entries | .[] | [ .value. ${ ep_idfield [ $epidx ] } , \"l\" + (.key|tostring) ] | @csv) catch empty " | tr -d '"' >> " $thisfile "
else
echo " $curlres " | jq -r " try (.[] | [ . ${ ep_idfield [ $epidx ] } , .name ] | @csv) catch empty " | tr -d '"' >> " $thisfile "
fi
2021-12-19 23:31:58 +11:00
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
2022-01-03 11:26:59 +11:00
addmsgq $( info " Loaded $added UUIDs from cache files for $* " 2>& 1)
2021-12-19 23:31:58 +11:00
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
2022-01-03 11:26:59 +11:00
[ [ $VERBOSE -eq 1 ] ] && addmsgq $( info "Loaded cache files for all servers" 2>& 1)
2021-12-19 23:31:58 +11:00
fi
fi
}
function uuid_callback( ) {
nuuids = 0
loadcachefile $curlocs
2021-12-23 16:28:03 +11:00
uuidend = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
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 = ""
2022-01-01 16:46:02 +11:00
unset recache_pid
2021-12-19 23:31:58 +11:00
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
2021-12-23 16:28:03 +11:00
uuidstart = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
if [ [ -z $recache_pid ] ] ; then
# setup callback for uuid cache reload handling
trap uuid_callback SIGUSR1
2022-01-03 11:26:59 +11:00
[ [ $VERBOSE -eq 1 ] ] && info " Initiating UUID cache refresh for $curlocs ... "
2021-12-19 23:31:58 +11:00
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( ) {
2022-01-03 11:26:59 +11:00
local prefix pstr input varname rv pcol
2021-12-19 23:31:58 +11:00
varname = $1
if [ [ -z $curproj ] ] ; then
2022-01-03 11:26:59 +11:00
prefix = "no_project_selected"
pcol = " $GREY "
2021-12-19 23:31:58 +11:00
else
2022-01-03 11:26:59 +11:00
prefix = " ${ curproj } "
if [ [ ${ proj_isopen [ $curprojidx ] } -eq 1 ] ] ; then
pcol = " $GREEN "
else
pcol = " $GREY "
fi
2021-12-19 23:31:58 +11:00
fi
2022-01-03 11:26:59 +11:00
pstr = "gnscli> "
2021-12-19 23:31:58 +11:00
[ [ ! -z $CACHING ] ] && info " $CACHING "
set -f
2022-01-03 14:43:30 +11:00
builtin read -p " $( echo -en $BOLD ) [ $( echo -en $PLAIN $pcol ) ${ prefix } $( echo -e " $PLAIN $BOLD " ) ] $pstr $( echo -e " ${ PLAIN } " ) " -e -r input
2021-12-19 23:31:58 +11:00
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
}
2022-01-03 13:35:56 +11:00
function harddebug( ) {
echo -e " ${ YELLOW } ${ BOLD } $* ${ PlAIN } " >/dev/stderr
}
2021-12-19 23:31:58 +11:00
function debug( ) {
2021-12-23 16:14:39 +11:00
local force = 0
if [ [ $1 = = "-f" ] ] ; then
shift
force = 1
fi
2021-12-19 23:31:58 +11:00
if [ [ $VERBOSE -eq 1 ] ] ; then
2021-12-23 16:14:39 +11:00
if [ [ $force -eq 1 ] ] ; then
echo -e " ${ PURPLE } ${ BOLD } ${ FUNCNAME [1] } (): ${ PLAIN } ${ PURPLE } $* ${ PLAIN } " >/dev/stderr
else
echo -e " ${ PURPLE } ${ BOLD } ${ FUNCNAME [1] } (): ${ PLAIN } ${ PURPLE } $* ${ PLAIN } " 1>& 2
fi
2021-12-19 23:31:58 +11:00
fi
}
function info( ) {
2022-01-03 11:26:59 +11:00
echo -e " ${ CYAN } ${ BOLD } ${ ITALIC } [ $* ] ${ PLAIN } " 1>& 2
2021-12-19 23:31:58 +11:00
}
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
2022-01-01 16:46:02 +11:00
echo " -g path Specify path to perl Graph::Easy binary"
2021-12-19 23:31:58 +11:00
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 = 0; x<${# uuid_id [@] } ; ++x) ) ; do
if [ [ " ${ uuid_id [ $x ] } " = = " $1 " ] ] ; then
echo " ${ uuid_name [ $x ] } "
return 0
fi
done
echo " $1 "
return 1
}
2022-01-03 13:35:56 +11:00
function get_next_linkid( ) {
local x num max = -1
for ( ( x = 0; x<${# uuid_name [@] } ; ++x) ) ; do
if [ [ " ${ uuid_name [ $x ] } " = = l* ] ] ; then
num = ${ uuid_name [ $x ]/l/ }
[ [ $num -gt $max ] ] && max = $num
fi
done
echo " l $(( max + 1 )) "
}
function name_to_uuid( ) {
local x
for ( ( x = 0; x<${# uuid_name [@] } ; ++x) ) ; do
if [ [ " ${ uuid_name [ $x ] } " = = " $1 " ] ] ; then
echo " ${ uuid_id [ $x ] } "
return 0
fi
done
echo " $1 "
return 1
}
2021-12-19 23:31:58 +11:00
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 "
2022-01-03 11:26:59 +11:00
proj_isopen[ $nprojects ] = ""
2021-12-19 23:31:58 +11:00
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
}
2022-01-02 21:44:09 +11:00
function addepactionusage( ) { # addepactions ep_name action_name "usage text"
local iepdx aname text
epidx = $( getepidx $1 )
if [ [ $? -ne 0 ] ] ; then
error " appepactionusage(): can't find endpoint named ' $1 ' "
return 1
fi
shift
aname = " $1 "
text = " $2 "
actionusage_epidx[ $nactusage ] = " $epidx "
actionusage_name[ $nactusage ] = " $aname "
actionusage_text[ $nactusage ] = " $text "
nactusage = $(( nactusage + 1 ))
}
function getactionusage( ) { # epidx actionname
local x
for x in ${ !actionusage_name[@] } ; do
if [ [ ${ actionusage_epidx [ $x ] } = = $1 && ${ actionusage_name [ $x ] } = = $2 ] ] ; then
echo " ${ actionusage_text [ $x ] } "
return 0
fi
done
return 1
}
2021-12-19 23:31:58 +11:00
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 "
}
2022-01-02 21:44:09 +11:00
function addcmdoption( ) {
2021-12-19 23:31:58 +11:00
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
}
2022-01-02 21:44:09 +11:00
2021-12-19 23:31:58 +11:00
function showcmdhelp( ) {
local real_cname cname idx text ln x format optname_arr optdesc_arr len
2022-01-02 21:15:35 +11:00
local otheraliases
2021-12-19 23:31:58 +11:00
cname = " $1 "
shift
idx = $( getcmdidx $cname )
if [ [ $? -ne 0 ] ] ; then
2022-01-02 21:15:35 +11:00
# is it an alias?
otheraliases = $( listcommandaliases $cname )
if [ [ $? -eq 0 ] ] ; then
echo " ${ cname } is an alias: "
echo " $otheraliases " | sed 's/^/ /'
rv = 0
else
error " Invalid command ' $cname ' "
rv = 1
fi
return $rv
2021-12-19 23:31:58 +11:00
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
2022-01-02 21:15:35 +11:00
echo " Alternative names for ' ${ real_cname } ': ${ cmd_aliases [ $idx ] } "
fi
otheraliases = $( listcommandaliases $real_cname )
if [ [ $? -eq 0 ] ] ; then
echo
echo " Aliases related to ' ${ real_cname } ': "
echo " $otheraliases " | sed 's/^/ /'
2021-12-19 23:31:58 +11:00
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 [@] }
}
2022-01-03 11:26:59 +11:00
function getfield( ) { # endpoint jqfilter fieldname
getdata " $1 " list -q -e -R" $3 " -r -F -f" $2 "
}
2021-12-19 23:31:58 +11:00
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
}
2021-12-31 16:32:08 +11:00
function runcurldatapost( ) { # location api_endpoint endpoint_uuid "curl data"
2021-12-23 16:14:39 +11:00
local thisapi thisurl curlres rv
local loc api_endpoint obname ovmethod
2021-12-31 16:32:08 +11:00
local endpoint_uuid
2022-01-01 10:26:34 +11:00
local curlmethod = "POST"
2021-12-23 16:14:39 +11:00
local data = ""
loc = " $1 "
api_endpoint = " $2 "
2021-12-31 16:32:08 +11:00
endpoint_uuid = " $3 "
data = " $4 "
2021-12-23 16:14:39 +11:00
thisapi = $( get $loc api)
[ [ -z $thisapi ] ] && echo " cant find api for ' $loc ' " && return 1
#thisauthdom=$(get $loc authdomain)
thisurl = " $thisapi / $api_endpoint "
2022-01-01 10:26:34 +11:00
thisurl = ${ thisurl / \/ v2 \/ templates / \/ v2 \/ projects \/ _CURPROJECT_ \/ templates } # special case
2021-12-23 16:14:39 +11:00
thisurl = ${ thisurl /_CURPROJECT_/ $curprojid }
2021-12-31 16:32:08 +11:00
if [ [ -n $endpoint_uuid ] ] ; then
thisurl = " $thisurl / $endpoint_uuid "
fi
2022-01-01 10:26:34 +11:00
if [ [ $GLOBALACTION = = "mv" ] ] ; then
curlmethod = "PUT"
else
curlmethod = "POST"
fi
2021-12-31 16:32:08 +11:00
debug -f " loc is $loc "
debug -f " api_endpoint is $api_endpoint "
debug -f " endpoint_uuid is $endpoint_uuid "
debug -f " data is $data "
debug -f " thisapi is $thisapi "
debug -f " thisurl is $thisurl "
2022-01-01 10:26:34 +11:00
debug -f " curlmethod is $curlmethod "
2021-12-23 16:14:39 +11:00
2022-01-01 10:26:34 +11:00
debug -f " curl -X ${ curlmethod } -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d \" $data \" $thisurl "
curlres = $( curl -X${ curlmethod } -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d " $data " $thisurl )
2021-12-23 16:14:39 +11:00
rv = $?
2022-01-01 10:26:34 +11:00
[ [ $rv -ne 0 ] ] && error " curl ${ curlmethod } to $thisurl failed "
2021-12-23 16:14:39 +11:00
echo " $curlres "
return $rv
}
function runcurlaction( ) { # location api_endpoint(ovname) obname ovmethod ["curl data"]
2021-12-19 23:31:58 +11:00
local thisapi thisurl curlres rv
#local thisauthdom u p thisauthstr
2021-12-31 16:32:08 +11:00
local loc api_endpoint obname ovmethou
2021-12-19 23:31:58 +11:00
local data = ""
2021-12-31 16:32:08 +11:00
local curlmethod = "POST"
2021-12-23 16:14:39 +11:00
#local data="<action/>"
2021-12-19 23:31:58 +11:00
loc = " $1 "
api_endpoint = " $2 "
obname = " $3 "
ovmethod = " $4 "
2021-12-23 16:14:39 +11:00
data = " $5 "
2021-12-19 23:31:58 +11:00
thisapi = $( get $loc api)
[ [ -z $thisapi ] ] && echo " cant find api for ' $loc ' " && return 1
#thisauthdom=$(get $loc authdomain)
2021-12-23 16:14:39 +11:00
debug -f " data is $data "
thisurl = " $thisapi / $api_endpoint / $obname "
[ [ -n $ovmethod ] ] && thisurl = " ${ thisurl } / $ovmethod "
2021-12-19 23:31:58 +11:00
thisurl = ${ thisurl /_CURPROJECT_/ $curprojid }
#u=$(head -1 "$AUTHFILE")
#p=$(tail -1 "$AUTHFILE")
#thisauthstr="$u@$thisauthdom:$p"
#unset u
#unset p
2021-12-31 16:32:08 +11:00
if [ [ $GLOBALACTION = = "del" ] ] ; then
curlmethod = "DELETE"
else
curlmethod = "POST"
fi
debug -f " curl -X $curlmethod -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d \" $data \" $thisurl "
curlres = $( curl -X$curlmethod -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d " $data " $thisurl )
2021-12-19 23:31:58 +11:00
rv = $?
2022-01-01 10:26:34 +11:00
[ [ $rv -ne 0 ] ] && error " curl ${ curlmethod } to $thisurl failed "
2021-12-19 23:31:58 +11:00
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 ] }
2022-01-03 14:43:30 +11:00
[ [ $localdb -eq 1 ] ] && info " 1st is $this "
optval = ${ this ##- $optname } # strip "-x" from the very start
optval = ${ optval ##* - $optname } # strip up to " -x" from the start
[ [ $localdb -eq 1 ] ] && info " 2nd is $optval "
2021-12-19 23:31:58 +11:00
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" ; ;
2021-12-23 16:14:39 +11:00
add) echo "" ; ;
2021-12-31 16:32:08 +11:00
del) echo "" ; ;
2022-01-01 10:26:34 +11:00
mv) echo "" ; ;
2021-12-19 23:31:58 +11:00
#migrate) echo "migrate";;
*)
echo
rv = 1;
; ;
esac
return $rv
}
2021-12-23 16:14:39 +11:00
function makejson( ) { # makejson key1:val1^key2:val2^...
2022-01-01 10:26:34 +11:00
local plaindata tok toks key val tnum = 1 format
2021-12-23 16:14:39 +11:00
plaindata = " $1 "
printf "%s" '{'
IFS = '^' read -ra toks <<< " $plaindata "
for tok in " ${ toks [@] } " ; do
[ [ $tnum -ne 1 ] ] && printf "%s" ","
key = ${ tok % : * }
val = ${ tok #* : }
2022-01-01 10:26:34 +11:00
if [ [ $val = ~ ^[ 0-9] +$ ] ] ; then
format = '"%s":%s'
else
format = '"%s":"%s"'
fi
printf " $format " " $key " " $val "
2021-12-23 16:14:39 +11:00
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]
2021-12-19 23:31:58 +11:00
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
2022-01-01 10:26:34 +11:00
local curldata = "" crv obuuid
2021-12-19 23:31:58 +11:00
rv = 0
loc = " $1 "
epidx = " $2 "
actionname = " $3 "
2022-01-01 10:26:34 +11:00
obuuid = " $4 "
2021-12-19 23:31:58 +11:00
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 )
2021-12-31 16:32:08 +11:00
debug -f " arglist - loc: $loc "
debug -f " arglist - epidx: $epidx "
debug -f " arglist - actionname: $actionname "
2022-01-01 10:26:34 +11:00
debug -f " arglist - obuuid: $obuuid "
2021-12-31 16:32:08 +11:00
debug -f " arglist - obname: $obname "
debug -f " arglist - extrainfo : $extrainfo "
debug -f " arglist: remaining opts: $opts "
2022-01-01 10:26:34 +11:00
if [ [ $actionname = = "add" || $actionname = = "mv" ] ] ; then
2022-01-02 20:58:57 +11:00
if [ [ -n $RAWJSONPOSTDATA ] ] ; then
2022-01-03 13:35:56 +11:00
debug -f "using RAWJSONPOSTDATA for curldata"
2022-01-02 20:58:57 +11:00
curldata = " ${ RAWJSONPOSTDATA } "
else
curldata = $( makejson " $extrainfo " )
fi
2021-12-23 16:14:39 +11:00
fi
2022-01-02 20:58:57 +11:00
RAWJSONPOSTDATA = ""
2021-12-19 23:31:58 +11:00
# 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
2022-01-01 10:26:34 +11:00
error " Can't determine gns API method for command ' $actionname ' "
2021-12-19 23:31:58 +11:00
return 1
fi
2021-12-31 16:32:08 +11:00
debug -f "ABOUT TO CALL CURL FUNCTION...."
2021-12-23 16:14:39 +11:00
debug -f " location: ${ loc } "
debug -f " api endpoint: ${ api_endpoint } "
debug -f " jq obj: ${ jq_obj } "
debug -f " actionname: ${ actionname } (gns method: ' $method ') "
2022-01-01 10:26:34 +11:00
debug -f " obuuid: ${ obuuid } "
2021-12-23 16:14:39 +11:00
debug -f " options: ${ opts } "
debug -f " curldata: ${ curldata } "
2022-01-01 10:26:34 +11:00
if [ [ $action = = "add" || $action = = "mv" ] ] ; then
2022-01-02 20:58:57 +11:00
# note: "obuuid" is empty in some cases, eg:
# adding a builtin node like VPCS
# adding a link
2022-01-01 10:26:34 +11:00
curlres = $( runcurldatapost " $loc " " $api_endpoint " " $obuuid " " $curldata " )
2021-12-23 16:14:39 +11:00
crv = $?
else
2022-01-01 10:26:34 +11:00
curlres = $( runcurlaction " $loc " " $api_endpoint " " $obuuid " " $method " " $curldata " )
2021-12-23 16:14:39 +11:00
crv = $?
fi
2021-12-31 16:32:08 +11:00
if [ [ $curlres = ~ $CURLERRORSTRINGS ] ] ; then
2022-01-03 14:43:30 +11:00
#if jq -e . >/dev/null 2>&1 <<< "$curlres"; then
# error "curl returned json error:"
# echo -en "$RED" >/dev/stderr
# echo "$curlres" | jq . | sed -e 's/^/ /' >/dev/stderr
# echo -e "$PLAIN" >/dev/stderr
#else
# error "curl returned error: [$curlres]"
#fi
2021-12-31 16:32:08 +11:00
crv = 99
fi
2021-12-23 16:14:39 +11:00
if [ [ $crv -ne 0 ] ] ; then
2021-12-19 23:31:58 +11:00
rv = 1
fi
2021-12-23 16:14:39 +11:00
debug -f " curlres is [ $curlres ] "
2021-12-19 23:31:58 +11:00
echo " $curlres "
fi
return $rv
}
2022-01-02 20:58:57 +11:00
# uses globals jqscrjpt, jqfilter, awkscript
function apply_filters( ) { # retvar wantquotes [f][s][a]
local retvar which data wantquotes rawopt qstr
retvar = " $1 "
wantquotes = " ${ 2 :- 0 } "
debug " wantquotes is $wantquotes "
2022-01-03 11:26:59 +11:00
2022-01-02 20:58:57 +11:00
if [ [ $wantquotes -eq 0 ] ] ; then
rawopt = "-r"
qstr = "without quotes (jq -r)"
elif [ [ $wantquotes -eq -1 ] ] ; then
rawopt = "-r"
qstr = "REALLY without quotes (jq -r | sed_remove_quotes)"
else
rawopt = ""
qstr = "with quotes"
fi
[ [ -z $retvar ] ] && return 1
which = " ${ 3 :- sfa } "
eval " data=\"\$ $retvar \" "
2022-01-03 11:26:59 +11:00
2022-01-02 20:58:57 +11:00
debug " applying: $which $qstr "
debug " wantquotes is $wantquotes "
debug " jqfilter is $jqfilter "
debug " jqscript is $jqscript "
debug " awkscript is $awkscript "
2022-01-03 11:26:59 +11:00
debug " rawscript is $rawscript "
2022-01-02 20:58:57 +11:00
if [ [ $which = = *f* && -n $jqfilter ] ] ; then
2022-01-03 11:26:59 +11:00
local thiswq = $wantquotes thisro = $rawopt
# override wantquotes if we're hsing more jq later
if [ [ -n $jqscript || -n $rawscript ] ] ; then
thiswq = 1
thisro = ""
fi
2022-01-02 20:58:57 +11:00
#debug "$retvar pre jqfilter is $data"
2022-01-03 11:26:59 +11:00
data = $( echo " $data " | jq $thisro " $jqfilter " 2>/dev/null)
if [ [ $thiswq -eq -1 ] ] ; then
2022-01-02 20:58:57 +11:00
data = $( echo " $data " | tr -d '"' )
fi
#debug "$retvar post jqfilter is $data"
fi
if [ [ $which = = *s* && -n $jqscript ] ] ; then
debug " jqscript contents: $( cat $jqscript ) "
debug " $retvar pre jqscript is $data "
data = $( echo " $data " | jq $rawopt -f " $jqscript " )
debug " $retvar post jqscript is $data "
fi
2022-01-03 11:26:59 +11:00
if [ [ $which = = *r* && -n $rawscript ] ] ; then
debug " rawscript is: $rawscript "
debug " $retvar pre rawscript is $data "
data = $( echo " $data " | jq $rawopt " $rawscript " )
debug " $retvar post jqscript is $data "
fi
2022-01-02 20:58:57 +11:00
if [ [ $which = = *a* && -n $awkscript ] ] ; then
debug " $retvar pre awkscript is $data "
data = $( echo " $data " | awk -f " $awkscript " )
fi
eval $retvar = \" \$ data\"
return 0
}
2021-12-19 23:31:58 +11:00
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 } "
2022-01-03 11:26:59 +11:00
rawscript = $( getarrayopt opts_arr R)
2021-12-19 23:31:58 +11:00
curlres = $( runcurlget $loc $api_endpoint )
[ [ $? -ne 0 ] ] && error " curl to $loc API failed " && return 1
debug " outmode is $outmode "
2022-01-01 16:46:02 +11:00
debug " jqscript is $jqscript "
debug " jqfilter is $jqfilter "
2022-01-02 20:58:57 +11:00
debug " awkscript is $awkscript "
2022-01-03 11:26:59 +11:00
debug " rawscript is $rawscript "
2021-12-19 23:31:58 +11:00
if [ [ $outmode = = "raw" ] ] ; then
2022-01-02 20:58:57 +11:00
r1 = " $curlres "
2022-01-03 11:26:59 +11:00
# only apply jqfilter unless -R is given
if [ [ -n $rawscript ] ] ; then
jqscript = " $rawscript " apply_filters r1 $QUOTES fr
else
apply_filters r1 $QUOTES f
fi
2022-01-02 20:58:57 +11:00
[ [ -n $r1 ] ] && echo " $r1 " || rv = 2
2021-12-19 23:31:58 +11:00
elif [ [ $outmode = = "verbose" ] ] ; then
2022-01-01 16:46:02 +11:00
r1 = " $curlres "
2022-01-02 20:58:57 +11:00
apply_filters r1 $REALLYNOQUOTES
[ [ -n $r1 ] ] && echo " $r1 " || rv = 2
2021-12-19 23:31:58 +11:00
else # namesonly
r1 = $( echo " $curlres " )
2022-01-02 20:58:57 +11:00
apply_filters r1
2021-12-19 23:31:58 +11:00
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 ,/ " )
else
r1 = $( echo " $r1 " | egrep -v " ^ $" )
fi
fi
debug " final r1 is [ $r1 ] "
[ [ -n $r1 ] ] && echo " $r1 " | sed 's/__END__://' || rv = 2
fi
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 = ".*"
2022-01-03 13:35:56 +11:00
[ [ -n " $filterarg " ] ] && refilter = " select(. ${ ep_defaultfield [ $epidx ] } |test(\"^ $filterarg $\")) " || refilter = "."
#if [[ ${ep_name[$epidx]} == "links" ]]; then
# refilter="${refilter} | to_entries"
#fi
2021-12-19 23:31:58 +11:00
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
2021-12-31 16:32:08 +11:00
thisdefault = "not_found"
2021-12-19 23:31:58 +11:00
[ [ $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"
2021-12-23 16:28:03 +11:00
prof_start = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
prof_last = " $prof_start "
}
function profile_mark( ) {
local now secs
2021-12-23 16:28:03 +11:00
now = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
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
2022-01-01 16:46:02 +11:00
local widths uuidcolumn uuidcolumns thisuuid repl line
2021-12-19 23:31:58 +11:00
local col colidx
profile_start
ndatalines = " $1 "
shift 1
msg = " $* "
2022-01-01 16:46:02 +11:00
table = $( echo -e " $msg " | column -t -s,)
2021-12-19 23:31:58 +11:00
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"
2022-01-01 16:46:02 +11:00
# find all heading columns
widths = $( find_fieldwidths " $table " )
debug " table before repl [\n $table ] "
debug " widths is [ $widths ] "
# At this point, $table is the full table without UUID replacement
2021-12-19 23:31:58 +11:00
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 ))
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 "
2022-01-01 16:46:02 +11:00
2021-12-19 23:31:58 +11:00
profile_mark "replaced uuids with names"
2022-01-01 16:46:02 +11:00
table = " ${ table2 } " ; unset table2 # save RAM
debug " table after repl [\n $table ] "
2022-01-02 20:58:57 +11:00
# re-render table since field lengths may have changed
2022-01-01 16:46:02 +11:00
table = $( flatten_table " $table " )
debug " table in csv is: [ $table ] "
table = $( echo -e " $table " | column -t -s, )
debug " table reformatted is: [ $table ] "
# add underlines
table = $( echo -e " $table " | 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; } }' )
debug " table with underline: [ $table ] "
debug " table lines is $( echo " $table " | awk 'BEGIN { FS=" "; } END { print NR }' ) "
2021-12-19 23:31:58 +11:00
profile_mark "created colourisation sed script"
2022-01-01 16:46:02 +11:00
table2 = $( echo -e " $table " | " $GSED " " $ADD_COLOURS " )
table = " ${ table2 } " ; unset table2 # save RAM
echo -e " $table "
2021-12-19 23:31:58 +11:00
profile_mark "added colours"
}
# Process the first line of CSV contents and try to figure out which
2022-01-01 16:46:02 +11:00
# columns contain an object UUID based on the heading.
2021-12-19 23:31:58 +11:00
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 }'
}
2022-01-01 16:46:02 +11:00
function find_fieldwidths( ) {
#echo "$*" | head -1 | awk 'BEGIN {start=1} { idx=1; while (idx>0) { idx=match(substr($0,start), "[a-zA-Z-]+"); if (idx>0) { start+=(idx+RLENGTH); } if (idx>1) { cols = cols " " start-1} } } END { print cols }'
echo " $* " | head -1 | awk 'BEGIN {start=1} { idx=1; iter=1; while (idx>0) { idx=match(substr($0,start), "[a-zA-Z-]+"); start += RSTART; start += RLENGTH; this=start-laststart; if (this>0 && iter > 1) cols = cols " " this; laststart=start; iter++; } } END { print cols }'
}
function flatten_table( ) {
local awkscript
read -d '' awkscript << 'EOF'
( NR = = 1) {
for ( i = 1; i<= NF; i++) {
pos = index( $0 , $i )
if ( i>1) {
len = pos-lastpos;
wids = wids " " ( pos-lastpos)
}
lastpos = pos;
}
len = length( $0 ) -lastpos+1;
wids = wids " " ( length( $0 ) -lastpos+1) ;
FIELDWIDTHS = wids
}
{
OFS = ","
$1 = $1
for ( i = 1; i<= NF; i++) {
gsub( " + $" ,"" ,$i ) ;
}
print
}
EOF
echo -e " $* " | awk " $awkscript "
}
2022-01-01 10:26:34 +11:00
function json_extract( ) { # json_extract "jsondata" jsonfield1 var1 jsonfield2 var2 ...
local jsondata jsonfield varname val rv = 0
jsondata = " $1 "
shift 1
while [ [ $# -ge 2 ] ] ; do
local thisrv
jsonfield = " $1 "
varname = " $2 "
shift 2
val = $( echo " $jsondata " | jq -r " ${ jsonfield } " 2>/dev/null)
thisrv = $?
[ [ -z $val ] ] && $thisrv = 1
[ [ $thisrv -ne 0 ] ] && val = ""
eval " $varname =\" $val \" "
rv = $(( $rv + $thisrv ))
done
return $rv
}
2021-12-19 23:31:58 +11:00
2022-01-01 10:26:34 +11:00
# populates global JSON_RESULTS
2021-12-19 23:31:58 +11:00
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
2022-01-02 20:58:57 +11:00
# oooo change this
2022-01-03 13:35:56 +11:00
local jqf
2021-12-19 23:31:58 +11:00
local jqf_bad = '[ "_DC_", "_OB_", .fault.detail, .fault.reason, .status ] | @csv'
local objecttype extrainfo
2021-12-23 16:28:03 +11:00
start = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
lastqsecs = ""
lastprocsecs = ""
what = " $1 "
shift
actionname = " $1 "
2022-01-01 10:26:34 +11:00
GLOBALACTION = " $actionname "
2021-12-19 23:31:58 +11:00
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
2022-01-01 10:26:34 +11:00
JSON_RESULTS = ""
2021-12-19 23:31:58 +11:00
if [ [ $foreground -eq 1 ] ] ; then
2022-01-01 10:26:34 +11:00
local thistmpfile
2021-12-19 23:31:58 +11:00
# 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)
2021-12-23 16:14:39 +11:00
extrainfo = " $( echo " $line " | cut -d, -f4-) "
2022-01-01 10:26:34 +11:00
thistmpfile = " $TMPDIR /run, $loc , $ob "
runaction_loc $loc $epidx $actionname $obuuid $ob " $extrainfo " $opts > " $thistmpfile "
jqres = $( cat ${ thistmpfile } | jq . 2>/dev/null)
if [ [ $? -eq 0 ] ] ; then
JSON_RESULTS = " ${ JSON_RESULTS } $jqres "
fi
rm -f $TMPDIR /run,*
2021-12-19 23:31:58 +11:00
else
pids = ""
while read -r line ; do
2021-12-31 16:32:08 +11:00
debug -f " processing line: [ $line ] "
2021-12-19 23:31:58 +11:00
loc = $( echo " $line " | cut -d, -f1)
ob = $( echo " $line " | cut -d, -f2)
obuuid = $( echo " $line " | cut -d, -f3)
2021-12-23 16:14:39 +11:00
extrainfo = $( echo " $line " | cut -d, -f4-)
2021-12-31 16:32:08 +11:00
debug -f " --> loc = $loc "
debug -f " --> ob = $ob "
debug -f " --> obuuid = $obuuid "
debug -f " --> extrainfo = $extrainfo "
runaction_loc " $loc " " $epidx " " $actionname " " $obuuid " " $ob " " $extrainfo " $opts > " $TMPDIR /run, $loc , $ob " &
2021-12-19 23:31:58 +11:00
pids = " $pids $! "
done <<< " $targetlist "
wait $pids
files = ( $TMPDIR /run,* )
unset ACTIONRES_LOC
unset ACTIONRES_MSG
n = 0
errs = 0
good = 0
rv = 0
2022-01-03 13:35:56 +11:00
2021-12-19 23:31:58 +11:00
# Capitalise first letter
objecttype = $( echo ${ what : 0 : 1 } | tr '[a-z]' '[A-Z]' ) ${ what : 1 }
2022-01-03 13:35:56 +11:00
[ [ $objecttype = = "Model" ] ] && objecttype = "Node"
2022-01-02 20:58:57 +11:00
# oooo
2022-01-03 13:35:56 +11:00
# figure out jq based on action
if [ [ $actionname = = "add" && $endpoint = = "model" ] ] ; then
allgoodresults = " Server, ${ objecttype } ,Ports "
jqf = '[ "_DC_", "_OB_", .properties.adapters ] | @csv'
elif [ [ $actionname = = "add" && $endpoint = = "link" ] ] ; then
local id
# hack: local ID won't be generated ubtil we
# recache, but weknow ot will be the last current
# id plus one.
id = $( get_next_linkid)
allgoodresults = " Server, ${ objecttype } ,Predicted_ID "
jqf = " [ \"_DC_\", .link_id, \" ${ id } \" ] | @csv "
elif [ [ $actionname = = "del" ] ] ; then
allgoodresults = ""
jqf = ''
else
allgoodresults = " Server, ${ objecttype } ,JobStatus "
jqf = '[ "_DC_", "_OB_", .status ] | @csv'
fi
2021-12-23 16:14:39 +11:00
allbadresults = ""
2021-12-19 23:31:58 +11:00
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 "
2022-01-01 10:26:34 +11:00
JSON_RESULTS = " ${ JSON_RESULTS } $jqres "
2021-12-19 23:31:58 +11:00
else
ACTIONRES_MSG[ $n ] = $( cat ${ thisfile } )
fi
if [ [ ${ ACTIONRES_MSG [ $n ] } = = *detail*reason* ] ] ; then
goterror = 1
elif [ [ ${ ACTIONRES_MSG [ $n ] } = = *rror* ] ] ; then
goterror = 1
2021-12-23 16:14:39 +11:00
elif [ [ ${ ACTIONRES_MSG [ $n ] } = = *message* ] ] ; then
goterror = 1
2021-12-19 23:31:58 +11:00
else
goterror = 0
fi
if [ [ $goterror -eq 1 ] ] ; then
2021-12-23 16:14:39 +11:00
#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 " ^ $" )
2021-12-19 23:31:58 +11:00
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 } "
2022-01-01 10:26:34 +11:00
good = $(( good + 1 ))
2021-12-19 23:31:58 +11:00
#echo -e "$GREEN" >/dev/stderr
fi
n = $(( n + 1 ))
done
echo
if [ [ $errs -gt 0 ] ] ; then
local fullres_bad
2021-12-23 16:14:39 +11:00
echo -e " ${ RED } $errs x ' ${ BOLD } $actionname ${ PLAIN } ${ RED } ' actions failed: ${ PLAIN } "
echo -e " ${ RED } "
echo " $allbadresults " | sed -e 's/^/ /'
echo -e " ${ PLAIN } "
2021-12-19 23:31:58 +11:00
echo
fi
if [ [ $good -ge 1 ] ] ; then
local fullres
echo -e " ${ GREEN } $good x ' ${ BOLD } $actionname ${ PLAIN } ${ GREEN } ' actions submitted successfully. ${ PLAIN } "
2022-01-03 13:35:56 +11:00
# More than just a heading?
if [ [ $( echo -e " $allgoodresults " | wc -l | bc) -gt 1 ] ] ; then
fullres = $( csv_to_table $(( $good + 1 )) " $allgoodresults " )
echo " $fullres "
fi
2021-12-19 23:31:58 +11:00
fi
if [ [ $good -ge 1 ] ] ; then
rv = 0
else
rv = 1
fi
rm -f $TMPDIR /run,*
fi
2021-12-23 16:28:03 +11:00
end = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
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 "
2021-12-31 16:32:08 +11:00
debug " validating action ' $action ' on obtype ' $what ' "
2021-12-19 23:31:58 +11:00
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
2021-12-23 16:14:39 +11:00
local quiet = 0 ignorecase = 0
2021-12-19 23:31:58 +11:00
2021-12-23 16:28:03 +11:00
start = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
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
2022-01-01 16:46:02 +11:00
elif [ [ $cmd = = "net" ] ] ; then
outmode = verbose # global
2021-12-19 23:31:58 +11:00
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
2021-12-23 16:14:39 +11:00
arraycontains opts_a "-c" && ignorecase = 1
2021-12-19 23:31:58 +11:00
arraycontains opts_a "-n" && usecache = 0 || usecache = 1
arraycontains opts_a "-e" && errordebug = 1
2022-01-02 20:58:57 +11:00
debug " cmd is $cmd "
2021-12-19 23:31:58 +11:00
debug " opts is $opts "
outfile = $( getarrayopt opts_a o)
refilter = $( getarrayopt opts_a f)
2022-01-02 20:58:57 +11:00
# Allow standard globs rather than regexp globs
2021-12-19 23:31:58 +11:00
[ [ $refilter = = "*" ] ] && refilter = ".*"
2021-12-23 16:14:39 +11:00
if [ [ -n $refilter ] ] ; then
2022-01-02 20:58:57 +11:00
obfilter = " select(. ${ ep_defaultfield [ $epidx ] } |test(\"^ $refilter $\" "
2021-12-23 16:14:39 +11:00
if [ [ $ignorecase -eq 1 ] ] ; then
obfilter = " ${ obfilter } ; \"i\")) "
else
obfilter = " ${ obfilter } )) "
fi
else
obfilter = "."
fi
2021-12-19 23:31:58 +11:00
[ [ -n $refilter ] ] && [ [ $quiet -eq 0 ] ] && info " ${ what } filter: ^ $refilter $"
debug " refilter is $refilter "
2022-01-02 20:58:57 +11:00
debug " obfilter is $obfilter "
2021-12-19 23:31:58 +11:00
# 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
2022-01-02 20:58:57 +11:00
elif [ [ $outmode = = "raw" ] ] ; then
[ [ $obfilter != "." ] ] && basefilter = " .[] | $obfilter "
2021-12-19 23:31:58 +11:00
fi
2022-01-01 16:46:02 +11:00
elif [ [ $cmd = = "net" ] ] ; then
jqscript = " $SCRIPTDIR /diagram.jq "
#awkscript="$SCRIPTDIR/detail.awk"
2021-12-19 23:31:58 +11:00
elif [ [ $cmd = = "show" ] ] ; then
if [ [ $outmode = = "verbose" ] ] ; then
error "Verbose mode not supported for show command yet"
return 1
2022-01-02 20:58:57 +11:00
elif [ [ $outmode = = "raw" ] ] ; then
error "Raw mode not supported for show command yet - try 'list' instead"
2021-12-19 23:31:58 +11:00
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 = ""
2022-01-03 11:26:59 +11:00
[ [ $quiet -eq 0 ] ] && notify "Obtaining data from gns API"
2021-12-19 23:31:58 +11:00
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
2022-01-02 20:58:57 +11:00
if [ [ $outmode = = "raw" ] ] ; then
all = $( cat " $TMPDIR " /get.* 2>/dev/null)
else
all = $( cat " $TMPDIR " /get.* 2>/dev/null | tr -d : | egrep -v 'DC|Server' | sed '/^[[:space:]]*$/d' )
fi
2021-12-19 23:31:58 +11:00
if [ [ -z $all ] ] ; then
fail
cat " $TMPDIR " /err.*
rm -f " $TMPDIR " /get.*
rm -f " $TMPDIR " /err.*
return 1
fi
2022-01-03 11:26:59 +11:00
[ [ $quiet -eq 0 ] ] && ok
2021-12-19 23:31:58 +11:00
if [ [ $errordebug -eq 1 ] ] ; then
if [ [ $( ls " $TMPDIR " /err.* | wc -l | bc) -gt 0 ] ] ; then
cat " $TMPDIR " /err.* >/dev/stderr
fi
fi
2022-01-02 20:58:57 +11:00
#debug "combined results: [$all]"
2021-12-19 23:31:58 +11:00
2021-12-23 16:28:03 +11:00
end = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
lastqsecs = $( echo " scale=2; ( $end - $start ) / 1000000; " | bc)
2021-12-23 16:28:03 +11:00
start = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
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
2022-01-01 16:46:02 +11:00
elif [ [ $cmd = = "net" ] ] ; then
local dotscript dev port x repl
local updated_result = ""
# replace uuids with names
fullres = $( cat $TMPDIR /get.*)
while read -r line ; do
if [ [ $line = ~ .*,.* ] ] ; then
dev[ 0] = $( echo " $line " | awk -F, '{ print $1 }' )
port[ 0] = $( echo " $line " | awk -F, '{ print $2 }' )
dev[ 1] = $( echo " $line " | awk -F, '{ print $3 }' )
port[ 1] = $( echo " $line " | awk -F, '{ print $4 }' )
for x in ${ !dev[@] } ; do
repl = " $( uuid_to_name ${ dev [ $x ] } ) "
if [ [ $? -eq 0 ] ] ; then
dev[ $x ] = " ${ repl } "
fi
done
#updated_result="${updated_result} \"${dev[0]}\":\"${port[0]}\" -- \"${dev[1]}\":\"${port[1]}\"\n"
updated_result = " ${ updated_result } \" ${ dev [0] } \" -- \" ${ dev [1] } \"[label=\" ${ dev [0] } : ${ port [0] } \\\n ${ dev [1] } : ${ port [1] } \"]\n "
fi
done <<< " $fullres "
dotscript = " digraph {\n $( echo " $updated_result " ) \n} "
debug " dotscript:\n ${ dotscript } "
if [ [ -n $GRAPHEASY ] ] ; then
updated_result = " ${ UNDERLINE } Network diagram for ${ BOLD } $curproj ${ PLAIN } "
updated_result = " $updated_result \n\n $( echo -e " $dotscript " | $GRAPHEASY --from= dot --as_ascii) "
else
error "graph-easy not installed - try 'sudo cpan Graph::Easy then use -g to specify path."
fi
2021-12-19 23:31:58 +11:00
else
# ie. show
fullres = $( cat $TMPDIR /get.*)
if [ [ $outmode = = "raw" ] ] ; then
echo " $fullres "
else
2022-01-01 16:46:02 +11:00
local updated_result = ""
2021-12-19 23:31:58 +11:00
# Replace UUIDS with names from cache
# and colourise certain words
greenwords = "true up enabled"
redwords = "false down paused uninitialized disabled"
yellowwords = "migrating"
uuidcol = " $PURPLE "
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
2021-12-23 18:45:10 +11:00
updated_result = $( echo " $updated_result " | " $GSED " " $ADD_COLOURS " )
2021-12-19 23:31:58 +11:00
echo -e " $updated_result "
rm -f $TMPDIR /get.*
else
# serial
cp /dev/null " $outfile "
2022-01-03 11:26:59 +11:00
[ [ $quiet -eq 0 ] ] && notify "Obtaining data from gns API into file"
2021-12-19 23:31:58 +11:00
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)
2021-12-23 16:28:03 +11:00
end = $(( $( $GDATE +%s%N) / 1000 ))
2021-12-19 23:31:58 +11:00
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
}
2022-01-02 21:15:35 +11:00
function listcommandaliases( ) { # [filter]
local x format filter output = "" rv = 0
filter = " $1 "
2021-12-31 16:32:08 +11:00
format = " %-12s|%-24s|%-36s ${ PLAIN } \n "
for x in ${ !cmdalias_src[@] } ; do
2022-01-02 21:15:35 +11:00
if [ [ -z $filter || ${ cmdalias_dst [ $x ] } = = *$filter * ] ] ; then
output = " ${ output } $( printf " $format " " ${ cmdalias_src [ $x ] } " " ${ cmdalias_dst [ $x ] } " " ${ cmdalias_defaultarg [ $x ] } " ) \n "
fi
2021-12-31 16:32:08 +11:00
done
2022-01-02 21:15:35 +11:00
if [ [ -n $output ] ] ; then
printf " ${ UNDERLINE } ${ BOLD } $format " "Alias" "Command" "Default Args"
echo -e " $output "
rv = 0
else
echo " No command aliases for ' $filter ' "
rv = 1
fi
return $rv
2021-12-31 16:32:08 +11:00
}
2021-12-19 23:31:58 +11:00
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
}
2021-12-31 16:32:08 +11:00
function getnodetypeforadd( ) {
local nt
nt = $( echo " $1 " | tr 'A-Z' 'a-z' | tr ' ' '_' )
echo " $nt "
}
2022-01-02 20:58:57 +11:00
# populates globals:
2022-01-03 14:43:30 +11:00
# data
2022-01-02 20:58:57 +11:00
# ndcs
# nobs
# dc_ess
# ob_ess
# data
# allobs
# alluids
# o_arr
# ou_arr
2022-01-03 13:35:56 +11:00
function validate_action_obs( ) { # infoname whattolist actionfilter showerroropt multiple_obs_allowed [retvar_selobname] [retvar_selobuuid]
local infoname whattolist actionfilter showerroropt
2022-01-02 20:58:57 +11:00
local selobname = "" selobuuid = ""
local retvar_obname retvar_obuuid multiallowed
2022-01-03 13:35:56 +11:00
infoname = " $1 "
whattolist = " $2 "
actionfilter = " $3 "
showerroropt = " $4 "
multiallowed = " $5 "
retvar_obname = " $6 "
retvar_obuuid = " $7 "
2022-01-02 20:58:57 +11:00
debug " whattolist= $whattolist "
debug " actionfilter= $actionfilter "
debug " showerroropt= $showerroropt "
debug " multiallowed= $multiallowed "
debug " retvar_obname= $retvar_obname "
debug " retvar_obuuid= $retvar_obuuid "
2022-01-03 13:35:56 +11:00
notify " Validating ${ infoname } "
2022-01-03 14:43:30 +11:00
getdata ${ whattolist } list " ${ actionfilter } .* " $showerroropt -c -s -q >" $TMPFILE "
2022-01-02 20:58:57 +11:00
rv = $?
debug " actionfilter= $actionfilter "
if [ [ $rv -ne 0 ] ] ; then
error " Can't find any ${ whattolist } s matching ' ${ BOLD } $actionfilter ' $PLAIN $RED . "
elif [ [ ! -e $TMPFILE ] ] ; then
error " No matching ${ whattolist } s found. "
elif grep -q "no results" $TMPFILE ; then
error " No ${ whattolist } s found matching ' $obname '. "
elif [ [ ! -e $TMPFILE ] ] ; then
error "Results file does not exist."
else
ok
data = $( cat " $TMPFILE " | egrep -v " ^ $" )
ndcs = $( printf %d $( echo " $data " | awk -F, '{ print $1 }' | sort -u | wc -l) )
nobs = $( printf %d $( echo " $data " | awk -F, '{ print $2 }' | sort -u | wc -l) )
[ [ $ndcs -eq 1 ] ] && dc_ess = "" || dc_ess = "s"
[ [ $nobs -eq 1 ] ] && ob_ess = "" || ob_ess = "s"
allobs = $( echo " $data " | awk -F, '{ print $2 }' | sort -u)
alluuids = $( echo " $data " | awk -F, '{ print $3 }' | sort -u)
debug " allobs is $allobs "
debug " alluuids is $alluuids "
o_arr = ( $allobs )
ou_arr = ( $alluuids )
if [ [ $nobs -gt 1 && $multiallowed -eq 0 ] ] ; then
debug " actionfilter= $actionfilter "
notify_nodots " ' ${ BOLD } ${ actionfilter } ${ PLAIN } ${ PURPLE } ' matched multiple ${ whattolist } s "
narrowdown " $whattolist " " $allobs " " $alluuids " selobname selobuuid || rv = 1
else
selobname = " $allobs "
selobuuid = " $alluuids "
fi
fi
[ [ -n $retvar_obname ] ] && eval " $retvar_obname =\" $selobname \" "
[ [ -n $retvar_obuuid ] ] && eval " $retvar_obuuid =\" $selobuuid \" "
rm -f " $TMPFILE "
return $rv
}
function narrowdown( ) { # whattolist "allobs" "all_uuids" retvar_newnodetype retvar_newnodetype_uuid
local whattolist allobs o_arr alluuids ou_arr
local o n rv = 0
local newnodetype = "" newnodetype_uuid = ""
local retvar_newnodetype retvar_newnodetype_uuid
whattolist = " $1 "
allobs = " $2 "
alluuids = " $3 "
retvar_newnodetype = " $4 "
retvar_newnodetype_uuid = " $5 "
o_arr = ( $allobs )
ou_arr = ( $alluuids )
while [ [ -z $newnodetype ] ] ; do
echo
n = 1
while read -r o; do
printf "%3d. %s\n" $n " $o "
n = $(( n + 1 ))
done <<< " $allobs "
echo
getstr ":" "" "Select one (q to abort)"
if [ [ -n $retstr ] ] ; then
if [ [ $retstr = = "q" ] ] ; then
rv = 1
break
elif [ [ $retstr = ~ ^[ 0-9] *$ ] ] ; then
if [ [ $retstr -le 0 || $retstr -ge $n ] ] ; then
error "Invalid selection"
else
newnodetype = " ${ o_arr [ $(( retstr - 1 )) ] } "
newnodetype_uuid = " ${ ou_arr [ $(( retstr - 1 )) ] } "
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
newnodetype = " $this "
newnodetype_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 "
newnodetype = ""
elif [ [ $matched -gt 1 ] ] ; then
error " ' $retstr ' matched multiple choices: $allmatches "
newnodetype = ""
fi
fi
fi
done
eval " $retvar_newnodetype =\" $newnodetype \" "
eval " $retvar_newnodetype_uuid =\" $newnodetype_uuid \" "
return $rv
}
2022-01-03 11:26:59 +11:00
function action_triggers_recache( ) {
local trigger = 0
case " $1 " in
add) trigger = 1; ;
del) trigger = 1; ;
mv) trigger = 1; ;
esac
return $(( 1 - $trigger ))
}
2022-01-03 13:35:56 +11:00
# populates global CLIDS_RES
function convert_link_ids( ) { # endpoint str
local u ep
ep = " $1 "
u = " $2 "
CLIDS_RES = ""
if [ [ $ep = = "link" && ${ u } = = l* ] ] ; then
u = $( name_to_uuid ${ u } )
if [ [ $? -ne 0 ] ] ; then
2022-01-03 14:43:30 +11:00
error " Can't find link ID ${ BOLD } $u ${ PLAIN } ${ RED } - recache may be needed. "
2022-01-03 13:35:56 +11:00
return 1
fi
fi
CLIDS_RES = " $u "
return 0
}
2021-12-19 23:31:58 +11:00
function processcmd( ) {
local cmd arg newarg rv newlocs x err admin idx opts pipe gotargs
local whattolist actionname = "" actionfilter = ""
local showerror = 0 showerroropt = ""
2022-01-02 20:58:57 +11:00
local epidx endpoint origendpoint builtin newname
2021-12-31 16:32:08 +11:00
local newnodex = 0 newnodey = 0
2022-01-02 20:58:57 +11:00
local dev devuuid port portnum adapnum
2021-12-31 16:32:08 +11:00
local BUILTINMODELS = "Cloud|VPCS|NAT|Frame Relay switch|Ethernet hub|Ethernet switch"
2022-01-01 10:26:34 +11:00
local oldname olduuid
2021-12-19 23:31:58 +11:00
cmd = $1
shift
arg = " $* "
shift
2022-01-03 13:35:56 +11:00
RAWJSONPOSTDATA = ""
2021-12-19 23:31:58 +11:00
[ [ -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
2021-12-23 16:14:39 +11:00
endpoint = ${ arg_array [0] } && unset 'arg_array[0]'
whattolist = ${ endpoint }
2022-01-03 13:35:56 +11:00
if [ [ ${# arg_array [@] } -ge 1 ] ] ; then
convert_link_ids $endpoint " ${ arg_array [@] } " || return 1
opts += ( " -f ${ CLIDS_RES } " )
fi
2021-12-19 23:31:58 +11:00
elif [ [ $cmd = = "list" ] ] ; then
2021-12-23 16:14:39 +11:00
endpoint = ${ arg_array [0] } && unset 'arg_array[0]'
whattolist = ${ endpoint }
2022-01-03 13:35:56 +11:00
if [ [ ${# arg_array [@] } -ge 1 ] ] ; then
convert_link_ids $endpoint " ${ arg_array [@] } " || return 1
opts += ( " -f ${ CLIDS_RES } " )
fi
2022-01-01 16:46:02 +11:00
elif [ [ $cmd = = "net" ] ] ; then
whattolist = "links"
opts += "-e"
2021-12-19 23:31:58 +11:00
elif [ [ $cmd = = "action" ] ] ; then
2021-12-23 16:14:39 +11:00
endpoint = ${ arg_array [0] } && unset 'arg_array[0]'
2022-01-01 10:26:34 +11:00
origendpoint = " $endpoint "
2021-12-23 16:14:39 +11:00
whattolist = ${ endpoint }
2021-12-19 23:31:58 +11:00
actionname = ${ arg_array [1] } && unset 'arg_array[1]'
2021-12-23 16:14:39 +11:00
if [ [ $actionname = = "add" ] ] ; then
newname = ${ arg_array [2] }
if [ [ -z $newname ] ] ; then
error " Name of new $whattolist not provided. "
return 1
fi
2022-01-02 20:58:57 +11:00
if [ [ $endpoint = = "node" ] ] ; then
newnodetype = " ${ arg_array [3] } " # eg. VPCS, Cisco IOS, etc
# is it a builtin appliance?
if [ [ $newnodetype = ~ ^( $BUILTINMODELS ) $ ] ] ; then
debug " adding a gns3 built-in node ( $newnodetype ) "
builtin = 1
whattolist = "model"
actionfilter = " -f.* ${ newnodetype } .* "
else
debug " adding a gns3 appliance ( $newnodetype ) "
builtin = 0
# we will lookup the template ID of "nodetype" later...
whattolist = "model"
actionfilter = " -f.* ${ newnodetype } .* "
fi
elif [ [ $endpoint = = "link" ] ] ; then
local x idx failed = 0
idx = 2
for x in 0 1; do
dev[ $x ] = ${ arg_array [ $idx ] } ; idx = $(( idx + 1 ))
port[ $x ] = ${ arg_array [ $idx ] } ; idx = $(( idx + 1 ))
2022-01-03 13:35:56 +11:00
# replace * with .*
2022-01-02 20:58:57 +11:00
port[ $x ] = $( echo " ${ port [ $x ] } " | sed 's#[^\.]\*#\.\*#g' )
if [ [ -z ${ dev [ $x ] } || -z ${ port [ $x ] } ] ] ; then
failed = 1
fi
done
if [ [ $failed -eq 1 ] ] ; then
error "usage: action link add adev aport zdev zport"
return 1
fi
2022-01-03 13:35:56 +11:00
else
error " Don't know how to add a $endpoint yet "
return 1
2021-12-31 16:32:08 +11:00
fi
2022-01-01 10:26:34 +11:00
elif [ [ $actionname = = "mv" ] ] ; then
oldname = ${ arg_array [2] }
newname = ${ arg_array [3] }
actionfilter = " -f.* ${ oldname } .* "
2021-12-19 23:31:58 +11:00
else
2022-01-03 13:35:56 +11:00
# start/stop/del/etc
# oooo does thiss work with multi args?
2021-12-23 16:14:39 +11:00
if [ [ ${# arg_array [@] } -ge 1 ] ] ; then
2022-01-03 13:35:56 +11:00
convert_link_ids $endpoint " ${ arg_array [@] } " || return 1
actionfilter = " -f ${ CLIDS_RES } "
obname = " ${ CLIDS_RES } "
2021-12-23 16:14:39 +11:00
else
error " Name of $whattolist not provided. "
return 1
fi
2021-12-19 23:31:58 +11:00
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)
2022-01-03 11:26:59 +11:00
setproject -q " ${ arg } "
2021-12-19 23:31:58 +11:00
rv = $?
2022-01-03 11:26:59 +11:00
[ [ $rv -eq 0 ] ] && loadcachefile $curlocs || start_recache
2021-12-19 23:31:58 +11:00
; ;
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
2022-01-03 14:43:30 +11:00
local res rv
echo -e " $PLAIN "
res = $( uuid_to_name ${ arg } )
if [ [ $? -eq 0 ] ] ; then
echo -e " ${ CYAN } UUID ${ BOLD } ${ arg } ${ PLAIN } ${ CYAN } -> ${ res } "
else
res = $( name_to_uuid ${ arg } )
if [ [ $? -eq 0 ] ] ; then
echo -e " ${ CYAN } UUID ${ res } -> ${ BOLD } ${ arg } ${ PLAIN } "
else
error " No cache results found for ${ arg } "
fi
fi
2021-12-19 23:31:58 +11:00
fi
; ;
q)
DONE = 1
; ;
2021-12-31 16:32:08 +11:00
la)
2022-01-02 21:15:35 +11:00
listcommandaliases " $arg "
2021-12-31 16:32:08 +11:00
; ;
2021-12-19 23:31:58 +11:00
help )
listcommands ADMIN
; ;
*)
error " Admin command ' $cmd ' not implemented "
rv = 1
; ;
esac
else
case ${ cmd } in
2022-01-01 16:46:02 +11:00
list| show| net)
2021-12-19 23:31:58 +11:00
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
2021-12-23 16:14:39 +11:00
validate_action ${ endpoint } $actionname
2021-12-19 23:31:58 +11:00
if [ [ $? -ne 0 ] ] ; then
2021-12-23 16:14:39 +11:00
error " ' $actionname ' is not a valid action for ${ endpoint } s "
2021-12-19 23:31:58 +11:00
return 1
fi
2021-12-23 16:14:39 +11:00
epidx = $( getepidx $endpoint )
2021-12-19 23:31:58 +11:00
if [ [ $? -ne 0 ] ] ; then
2021-12-23 16:14:39 +11:00
error " ' $endpoint ' is not a valid endpoint "
2021-12-19 23:31:58 +11:00
return 1
fi
2021-12-31 16:32:08 +11:00
if [ [ -n $whattolist ] ] ; then
2022-01-02 20:58:57 +11:00
local ndcs nobs data confirm = 0
2021-12-31 16:32:08 +11:00
# Get a list of objects to operate on
# ie. turn regexp into a list of dcs and obnames first
2022-01-02 20:58:57 +11:00
echo
if [ [ $actionname = = "connect" || $actionname = = "mv" ] ] ; then
2022-01-03 13:35:56 +11:00
validate_action_obs " $whattolist name " ${ whattolist } " $actionfilter " " $showerroropt " $NOMULTI || return $?
2022-01-02 20:58:57 +11:00
if [ [ $nobs -gt 1 ] ] ; then
local allobs = $( echo " $data " | awk -F, '{ print $2 }' | sort -u | tr '\n' ',' )
error " Can't run ' $actionname ' with multiple objects (matched: ${ allobs %, } ) "
return 1
else
confirm = 1
fi
elif [ [ $actionname = = "add" ] ] ; then
if [ [ $endpoint = = "node" ] ] ; then
2022-01-03 13:35:56 +11:00
validate_action_obs "node type" ${ whattolist } " $actionfilter " " $showerroropt " $NOMULTI newnodetype newnodetype_uuid
2021-12-31 16:32:08 +11:00
if [ [ -z $newnodetype ] ] ; then
confirm = 0
else
2022-01-02 20:58:57 +11:00
debug " newnodetype is $newnodetype "
debug " newnodeuuid is $newnodetype_uuid "
2021-12-31 16:32:08 +11:00
notify_nodots " Adding a new ${ BOLD } $newnodetype ${ PLAIN } ${ PURPLE } named $BOLD $newname ${ PLAIN } "
confirm = 1
fi
2022-01-02 20:58:57 +11:00
elif [ [ $endpoint = = "link" ] ] ; then
local x json adaplist portnumlist portlist nports newn newu jqs
local jqs_name jqs_num
for x in 0 1; do
local letter portlower
[ [ $x -eq 0 ] ] && letter = A || letter = Z
debug " about to validate link $x "
2022-01-03 13:35:56 +11:00
validate_action_obs " ${ letter } -end " "node" " ${ dev [ $x ] } " " $showerroropt " $NOMULTI newn newu || return $?
2022-01-02 20:58:57 +11:00
dev[ $x ] = " $newn "
devuuid[ $x ] = " $newu "
debug " ${ letter } -node ${ dev [ $x ] } is OK ( ${ dev [ $x ] } ) "
debug " getting node $x ports "
json = $( getdata nodes list -q -e -r " -f ${ dev [ $x ] } " )
portlower = $( echo ${ port [ $x ] } | tr 'A-Z' 'a-z' )
jqs = " .ports[] | select(.name|ascii_downcase|test(\".* ${ portlower } .*\")) "
jqs_name = " ${ jqs } | .short_name "
jqs_num = " ${ jqs } | .port_number "
jqs_adapnum = " ${ jqs } | .adapter_number "
portlist = $( echo " $json " | jq -r " $jqs_name " )
portnumlist = $( echo " $json " | jq -r " $jqs_num " )
adapnumlist = $( echo " $json " | jq -r " $jqs_adapnum " )
debug " portlist is $portlist "
debug " portnumlist is $portnumlist "
debug " adapnumlist is $adapnumlist "
#debug "raw port data is\n$json"
nports = $( wc -l <<< " $portlist " | bc)
if [ [ -z $portlist || $nports -eq 0 ] ] ; then
error " ${ letter } -node ${ BOLD } ${ dev [ $x ] } $PLAIN $RED has no ports matching ' ${ BOLD } ${ port [ $x ] } $PLAIN $RED '. "
elif [ [ $nports -gt 1 ] ] ; then
error " ${ letter } -node ${ BOLD } ${ dev [ $x ] } $PLAIN $RED has multiple ports matching ' ${ BOLD } ${ port [ $x ] } $PLAIN $RED ': "
echo -e " ${ RED } $portlist ${ PLAIN } " | sed -e 's/^/ /'
else
port[ $x ] = " $portlist "
portnum[ $x ] = " $portnumlist "
adapnum[ $x ] = " $adapnumlist "
debug " ${ letter } -node ${ dev [ $x ] } port ${ port [ $x ] } is OK ( ${ dev [ $x ] } ) "
fi
done
notify_nodots " Adding a link from ${ BOLD } ${ dev [0] } ${ port [0] } ${ PLAIN } ${ PURPLE } to ${ BOLD } ${ dev [1] } ${ port [1] } ${ PLAIN } "
confirm = 1
2021-12-19 23:31:58 +11:00
fi
2022-01-02 20:58:57 +11:00
else # action is not add/connect
2022-01-03 13:35:56 +11:00
validate_action_obs " ${ whattolist } name " ${ whattolist } " $actionfilter " " $showerroropt " $MULTI || return $?
2022-01-03 14:43:30 +11:00
if [ [ $whattolist = = "link" ] ] ; then
local line srv linkuuid d p n newdata = ""
# show link deacription instead of uuid
while read -r line; do
srv = " ${ line %%,* } "
linkuuid = " ${ line ##*, } "
for n in 0 1; do
d[ $n ] = $( getfield links " $linkuuid " " .nodes[ $n ].node_id " | tr -d '"' )
d[ $n ] = $( uuid_to_name " ${ d [ $n ] } " )
p[ $n ] = $( getfield links " $linkuuid " " .nodes[ $n ].label.text " | tr -d '"' )
done
[ [ -n $newdata ] ] && newdata = " ${ newdata } \n "
newdata = " ${ newdata } ${ srv } , ${ d [0] } ${ p [0] } <-> ${ d [1] } ${ p [1] } "
done <<< " $data "
textdata = " $newdata "
else
textdata = " $data "
fi
2022-01-02 20:58:57 +11:00
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 } "
2022-01-03 14:43:30 +11:00
echo -e " $textdata " | awk -F, " BEGIN {lastdc=\"\"} { if (\$1 != lastdc) { print \" ${ YELLOW } - ${ BOLD } \" \$1 \" -> project= ${ curproj } ${ PLAIN } \"; lastdc=\$1; } print \" ${ YELLOW } - \" \$2 \" ${ PLAIN } \"} "
2022-01-02 20:58:57 +11:00
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
2021-12-31 16:32:08 +11:00
else
# we dont need to do a data query
newnodetype_uuid = ""
confirm = 1
fi
2021-12-19 23:31:58 +11:00
2021-12-31 16:32:08 +11:00
if [ [ $confirm -eq 1 ] ] ; then
if [ [ $actionname = = "add" ] ] ; then
local postdata
2022-01-02 20:58:57 +11:00
if [ [ $endpoint = = "node" ] ] ; 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 in the form of key1:val1^key2:val2...
# the endpoint we call depends on whether
# we are adding a gns3 builtin node or an appliance
if [ [ $builtin -eq 1 ] ] ; then
local nodetypeforadd
# gns3 wants the nodetype in the post data and in a
# special format.
nodetypeforadd = $( getnodetypeforadd $newnodetype )
postdata = " name: $newname ^node_type: $nodetypeforadd ^compute_id:local^template_id: $newnodetype_uuid "
# ...and NOT in the URL
newnodetype = ""
newnodetype_uuid = ""
else
# ie. use an appliance template
postdata = " x: $newnodex ^y: $newnodey "
endpoint = "model"
fi
actiontargets = " $curlocs , $newname , $newnodetype_uuid , $postdata "
#actiontargets="${actiontargets},$postdata"
elif [ [ $endpoint = = "link" ] ] ; then
#POST 'http://localhost:3080/v2/projects/bfb83f16-dab4-445a-a572-d7cc1801222e/links' -d '{"nodes": [{"adapter_number": 0, "label": {"text": "Text", "x": 42, "y": 0}, "node_id": "d3601934-e39a-4865-9cf5-3e46e1fe24e2", "port_number": 3}, {"adapter_number": 0, "node_id": "d3601934-e39a-4865-9cf5-3e46e1fe24e2", "port_number": 4}]}'
RAWJSONPOSTDATA = "{\"nodes\": ["
for n in 0 1; do
RAWJSONPOSTDATA = " ${ RAWJSONPOSTDATA } {\"node_id\": \" ${ devuuid [ $n ] } \", \"adapter_number\": ${ adapnum [ $n ] } , \"port_number\": ${ portnum [ $n ] } } "
[ [ $n -eq 0 ] ] && RAWJSONPOSTDATA = " ${ RAWJSONPOSTDATA } , "
done
RAWJSONPOSTDATA = " ${ RAWJSONPOSTDATA } ]} "
actiontargets = " $curlocs ,,, "
2021-12-19 23:31:58 +11:00
fi
2022-01-01 10:26:34 +11:00
elif [ [ $actionname = = "mv" ] ] ; then
oldname = $( echo " $data " | awk -F, '{ print $2 }' | sort -u)
olduuid = $( echo " $data " | awk -F, '{ print $3 }' | sort -u)
2021-12-31 16:32:08 +11:00
2022-01-01 10:26:34 +11:00
actiontargets = " $curlocs , $oldname , $olduuid ,name: $newname "
2021-12-31 16:32:08 +11:00
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 "
2021-12-19 23:31:58 +11:00
debug "about to call runaction with:"
2021-12-31 16:32:08 +11:00
debug " endpoint = $endpoint "
2021-12-23 16:14:39 +11:00
debug " obtype = $endpoint "
2021-12-19 23:31:58 +11:00
debug " actionname = $actionname "
debug " actiontargets = $actiontargets "
debug " opts = ${ opts [@] } "
debug " outputfile = ${ TMPFILE } "
2021-12-31 16:32:08 +11:00
runaction ${ endpoint } $actionname " $actiontargets " ${ opts [@] } >" $TMPFILE "
rv = $?
2022-01-02 20:58:57 +11:00
if [ [ $actionname = = "add" && $origendpoint = = "node" && $rv -eq 0 && $builtin -eq 0 ] ] ; then
2022-01-01 10:26:34 +11:00
local fail = 0
# we now need to rename the newly created object - gns3
# will always generate a name based on the template
debug " json is $JSON_RESULTS "
json_extract " $JSON_RESULTS " .name oldname .node_id olduuid
if [ [ $? -eq 0 ] ] ; then
runaction ${ origendpoint } mv " $curlocs , $oldname , $olduuid ,name: $newname ^ " -F
[ [ $? -ne 0 ] ] && fail = 1
else
fail = 1
fi
if [ [ $fail -eq 1 ] ] ; then
warn " Failed to rename new object to ' $newname ' - please check. "
fi
fi
2021-12-31 16:32:08 +11:00
if [ [ $actionname != "connect" ] ] ; then
ok
[ [ -e $TMPFILE ] ] && cat " $TMPFILE " | $pipe
2022-01-03 13:35:56 +11:00
if [ [ $actionname = = "add" && $origendpoint = = "link" && $rv -eq 0 ] ] ; then
warn " Use ' ${ ITALIC } l l ${ PLAIN } ${ YELLOW } ' to confirm predicted ID "
fi
2021-12-31 16:32:08 +11:00
echo
info $( printf "action submission time: %s seconds" " $lastqsecs " )
2021-12-19 23:31:58 +11:00
fi
2022-01-03 11:26:59 +11:00
2022-01-03 14:43:30 +11:00
if [ [ $rv -eq 0 ] ] ; then
2022-01-03 11:26:59 +11:00
if action_triggers_recache $actionname ; then
start_recache
fi
fi
2021-12-31 16:32:08 +11:00
fi # end if confirm == 1
2021-12-19 23:31:58 +11:00
rm -f " $TMPFILE "
; ;
exit )
DONE = 1
; ;
help )
listcommands NOADMIN
; ;
*)
error " Command ' $cmd ' not implemented "
rv = 1
; ;
esac
fi
return $rv
}
2022-01-03 11:26:59 +11:00
function updateprojstatus( ) { # projidx
local status idx
idx = " $1 "
# get open/close status
status = $( getfield projects " ${ proj_name [ $idx ] } " .status)
[ [ $status = = *opened* ] ] && proj_isopen[ $idx ] = 1 || proj_isopen[ $idx ] = 0
}
function setproject( ) { # setproject [-q] [project_name]
local newproj x err rv idx status quiet = 0
[ [ $1 = = "-q" ] ] && quiet = 1 && shift
2021-12-19 23:31:58 +11:00
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 ] }
2022-01-03 11:26:59 +11:00
curprojidx = ${ idx }
2021-12-19 23:31:58 +11:00
if [ [ $err -eq 0 ] ] ; then
curproj = " ${ newproj } "
2022-01-03 11:26:59 +11:00
[ [ $quiet -eq 0 ] ] && info " Project set to: $curproj ( $curprojid ) "
2021-12-19 23:31:58 +11:00
rv = 0
2022-01-03 11:26:59 +11:00
updateprojstatus ${ idx }
if [ [ ${ proj_isopen [ $curprojidx ] } -ne 1 ] ] ; then
warn " Selected project ${ BOLD } $curproj ${ PLAIN } ${ YELLOW } is closed - use ' ${ BOLD } ${ ITALIC } open ${ PLAIN } ${ YELLOW } ' to open it. "
fi
2021-12-19 23:31:58 +11:00
else
rv = 1
fi
return $rv
}
function checkfor( ) { # checkfor name [pkg_name]
local what pkgname
2021-12-23 16:40:48 +11:00
local hw os ok = 0 confirm = 0
2021-12-23 18:45:10 +11:00
local alt = "" rv globvar = ""
2021-12-31 16:32:08 +11:00
local realpath = ""
2021-12-23 16:40:48 +11:00
2021-12-19 23:31:58 +11:00
what = $1
pkg_name = ${ 2 :- $what }
os = $( uname -s)
2021-12-23 16:40:48 +11:00
hw = $( uname -m)
2021-12-23 18:54:11 +11:00
if [ [ $hw = = "aarch64" ] ] ; then # Android Termux
2021-12-23 16:40:48 +11:00
alias which = "command -v"
fi
2021-12-31 16:32:08 +11:00
if [ [ $what = = "gdate" ] ] ; then
alt = date
globvar = GDATE
elif [ [ $what = = "gsed" ] ] ; then
alt = sed
globvar = GSED
2022-01-01 16:46:02 +11:00
elif [ [ $what = = "graph-easy" ] ] ; then
globvar = GRAPHEASY
2021-12-31 16:32:08 +11:00
fi
realpath = $( which $what 2>& 1)
2021-12-23 16:28:03 +11:00
rv = $?
2021-12-23 18:45:10 +11:00
if [ [ $rv -ne 0 ] ] ; then
if [ [ -n $alt ] ] ; then
$alt --version 2>/dev/null | grep -q GNU 2>/dev/null
if [ [ $? -eq 0 ] ] ; then
2021-12-31 16:32:08 +11:00
realpath = $( which $alt 2>/dev/null)
2021-12-23 18:45:10 +11:00
rv = 0
fi
2021-12-23 16:28:03 +11:00
fi
fi
2021-12-23 18:36:12 +11:00
if [ [ $rv -eq 0 ] ] ; then
2021-12-31 16:32:08 +11:00
if [ [ -n $globvar ] ] ; then
debug " found $what at $realpath - have set \$ $globvar "
eval " $globvar =\" $realpath \" "
else
debug " found $what at $realpath - no globvar "
fi
2021-12-19 23:31:58 +11:00
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
2021-12-23 18:54:11 +11:00
elif [ [ $hw = = "aarch64" ] ] ; then
apt install $pkg_name && ok = 1
else
error " Don't know how to install stuff on OS ' $os ' HW ' $hw ' "
2021-12-19 23:31:58 +11:00
fi
; ;
*)
2021-12-23 18:54:11 +11:00
error " Don't know how to install stuff on OS ' $os ' HW ' $hw ' "
2021-12-19 23:31:58 +11:00
; ;
esac
[ [ $ok -eq 1 ] ] && info " Successfully installed $pkg_name " || error " Failed to install $pkg_name "
fi
fi
which $what >/dev/null 2>& 1
2021-12-31 16:32:08 +11:00
rv = $?
[ [ $rv -eq 0 && -n $globvar ] ] && eval " $globvar =\" $realpath \" "
return $rv
2021-12-19 23:31:58 +11:00
}
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
2022-01-01 16:46:02 +11:00
g)
GRAPHEASY = " ${ OPTARG } "
if [ [ ! -x " $GRAPHEASY " ] ] ; then
error " Provided graph-easy binary not found or not executable ( $GRAPHEASY ) "
exit 1
fi
; ;
2021-12-19 23:31:58 +11:00
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"
2022-01-03 11:26:59 +11:00
MAGENTA = "\033[35m"
2021-12-19 23:31:58 +11:00
CYAN = "\033[36m"
2022-01-03 11:26:59 +11:00
ORANGE = "\033[38;2;255;165;0m"
PURPLE = "\033[38;2;200;0;200m"
#PURPLE="\033[35m"
GREY = "\033[38;2;110;110;110m"
2021-12-19 23:31:58 +11:00
LINK = " $BLUE $UNDERLINE "
UNDERLINE_PRINTED = $( echo -en " $UNDERLINE " )
UNDERLINE_LENGTH = ${# UNDERLINE_PRINTED }
2022-01-02 20:58:57 +11:00
NOMULTI = 0
MULTI = 1
NOQUOTES = 0
QUOTES = 1
REALLYNOQUOTES = -1
2022-01-01 10:26:34 +11:00
CURLERRORSTRINGS = "(^40.:|error|status.*40.)"
2021-12-31 16:32:08 +11:00
2021-12-19 23:31:58 +11:00
# 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
2022-01-02 21:44:09 +11:00
nactusage = 0
2021-12-19 23:31:58 +11:00
# 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"
2022-01-01 10:26:34 +11:00
addepactions nodes start stop connect add del mv
2022-01-02 21:44:09 +11:00
addepactionusage nodes "add" "new_node_name new_node_model"
addepactionusage nodes "del" "node_name"
addepactionusage nodes "mv" "old_node_name new_node_name"
2021-12-19 23:31:58 +11:00
addendpoint links projects/_CURPROJECT_/links link link_id link_id
addepalias links l
2022-01-03 13:35:56 +11:00
addeptitles links "Type" "A-Host_UUID " "A-Port" "Z-Host_UUID " "Z-Port" "ID_UUID"
2022-01-02 20:58:57 +11:00
addepfields links ".link_type" ".nodes[0].node_id" ".nodes[0].label.text" ".nodes[1].node_id" ".nodes[1].label.text" ".link_id"
addepactions links add del
2022-01-02 21:44:09 +11:00
addepactionusage links "add" "a_nodename a_portname b_nodename b_portname"
addepactionusage links "del" "link_uuid"
2021-12-19 23:31:58 +11:00
addendpoint models templates model template_id name
addepalias models m
addeptitles models "Name" "Category" "UUID"
addepfields models ".name" ".category" ".template_id"
2021-12-31 16:32:08 +11:00
addendpoint appliances appliances appliance template_id name
addepalias appliances a
addeptitles appliances "Name" "Category" "Ports"
addepfields appliances ".name" ".category" ".qemu.adapters"
2021-12-19 23:31:58 +11:00
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"
2022-01-01 16:46:02 +11:00
addcmd net "Show network diagram (requires perl Graph::Easy)"
2022-01-02 20:58:57 +11:00
addcmd action "Perform action on given object" 3 act do
2021-12-19 23:31:58 +11:00
declare -a lines
for x in ${ !ep_jqobj[@] } ; do
if [ [ -z ${ ep_validactions [ $x ] } ] ] ; then
lines += ( " ${ ep_jqobj [ $x ] } actions: n/a " )
else
2022-01-02 21:44:09 +11:00
lines += ( " ${ ep_jqobj [ $x ] } actions: " )
for va in ${ ep_validactions [ $x ] } ; do
usage = $( getactionusage $x $va )
lines += ( " $va $usage " )
done
2021-12-19 23:31:58 +11:00
fi
done
2022-01-02 20:58:57 +11:00
addcmdusage action "object_type action_type regexp_filter [extrainfo]" " object_type can be: [ ${ ep_jqobj [*] } ] " "" " ${ lines [@] } "
2021-12-19 23:31:58 +11:00
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" ""
2021-12-23 16:14:39 +11:00
addcmdalias "add" "action node add" ""
2021-12-31 16:32:08 +11:00
addcmdalias "rm" "action node del" ""
addcmdalias "del" "action node del" ""
2022-01-01 10:26:34 +11:00
addcmdalias "mv" "action node mv" ""
2022-01-02 20:58:57 +11:00
addcmdalias "link" "action link add" ""
addcmdalias "unlink" "action link del" ""
addcmdalias "delink" "action link del" ""
2021-12-19 23:31:58 +11:00
addcmd help "List regular commands" 0 "?" "h"
addcmd exit "Exit from gnscli" 0 quit
addcmd -a help "List admin commands" 0 "?" "h"
2021-12-31 16:32:08 +11:00
addcmd -a la "List command aliases" 0
2021-12-19 23:31:58 +11:00
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
2021-12-23 16:28:03 +11:00
GDATE = "gdate"
2021-12-19 23:31:58 +11:00
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
2022-01-03 11:26:59 +11:00
RCARGS = " $RCARGS $f "
2021-12-19 23:31:58 +11:00
done < <( egrep -v " (^#|^ $) " $RCFILE )
fi
2022-01-01 16:46:02 +11:00
VALIDARGS = "g:hiIp:Pv"
2021-12-19 23:31:58 +11:00
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
2021-12-23 18:36:12 +11:00
checkfor gdate coreutils || exit 1
checkfor jq || exit 1
2021-12-23 18:54:11 +11:00
checkfor gsed || exit 1
2021-12-19 23:31:58 +11:00
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"
2022-01-03 11:26:59 +11:00
#oldverbose=$VERBOSE
#VERBOSE=1
2021-12-19 23:31:58 +11:00
errfile = " $TMPDIR " /err
2022-01-03 11:26:59 +11:00
output = $( getdata projects list -q -e -r 2>" $errfile " )
2021-12-19 23:31:58 +11:00
rv = $?
2022-01-02 20:58:57 +11:00
debug " rv is $rv "
2022-01-03 11:26:59 +11:00
#VERBOSE=$oldverbose
2021-12-19 23:31:58 +11:00
if [ [ $rv -eq 0 ] ] ; then
ok
2022-01-02 20:58:57 +11:00
cat " $errfile "
2021-12-19 23:31:58 +11:00
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
2022-01-03 11:26:59 +11:00
setproject -q $DEFPROJECT || exit 1 # defaults to first one
2021-12-19 23:31:58 +11:00
[ [ -e $HISTFILE ] ] && history -r " $HISTFILE "
loadcachefile $curlocs
dumpmsgq
trap cleanup EXIT
2022-01-03 11:26:59 +11:00
echo -e " ${ UNDERLINE } Unofficial GNS3 cli v ${ VER } ${ PLAIN } "
2021-12-19 23:31:58 +11:00
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,
2021-12-23 16:28:03 +11:00
"Locked#" + ( .locked // "n/a" ) ,
"Console port#" + ( ( .console| tostring) // "n/a" ) ,
2021-12-19 23:31:58 +11:00
"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,
2022-01-02 20:58:57 +11:00
"A-Node#" + .nodes[ 0] .node_id,
"A-Port#" + .nodes[ 0] .label.text,
"Z-Node#" + .nodes[ 1] .node_id,
"Z-Port#" + .nodes[ 1] .label.text,
2022-01-01 16:46:02 +11:00
"Capturing#" + ( .capturing| tostring) ,
"Suspended#" + ( .suspend| tostring) ,
2021-12-19 23:31:58 +11:00
"__END__"
#END_INLINE:link.jq
2022-01-01 16:46:02 +11:00
#START_INLINE:diagram.jq
.[ ] | .nodes[ 0] .node_id + "," + .nodes[ 0] .label.text + "," + .nodes[ 1] .node_id + "," + .nodes[ 1] .label.text
#END_INLINE:diagram.jq