From bb4d15e508b921d510d2b478df9e73a86f471a9f Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Sun, 2 Jan 2022 20:58:57 +1100 Subject: [PATCH] Clean up getdata and json filtering code Can now add/remove links --- gnscli.sh | 614 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 391 insertions(+), 223 deletions(-) diff --git a/gnscli.sh b/gnscli.sh index f28d901..4110174 100755 --- a/gnscli.sh +++ b/gnscli.sh @@ -1,5 +1,6 @@ #!/bin/bash - +# addepactionusage "links" "add" "ahost aport zhost zport' +# better cinfirmation when removing a link # clean up action output # add node issue with menu when type isnt specified @@ -314,7 +315,6 @@ function debug() { if [[ $VERBOSE -eq 1 ]]; then if [[ $force -eq 1 ]]; then echo -e "${PURPLE}${BOLD}${FUNCNAME[1]}(): ${PLAIN}${PURPLE}$*${PLAIN}" >/dev/stderr - echo -e "${PURPLE}${BOLD}${FUNCNAME[1]}(): ${PLAIN}${PURPLE}$*${PLAIN}" >/tmp/a else echo -e "${PURPLE}${BOLD}${FUNCNAME[1]}(): ${PLAIN}${PURPLE}$*${PLAIN}" 1>&2 fi @@ -965,9 +965,15 @@ debug -f "arglist - obname: $obname" debug -f "arglist - extrainfo : $extrainfo" debug -f "arglist: remaining opts: $opts" if [[ $actionname == "add" || $actionname == "mv" ]]; then - curldata=$(makejson "$extrainfo") + if [[ -n $RAWJSONPOSTDATA ]]; then + curldata="${RAWJSONPOSTDATA}" + else + curldata=$(makejson "$extrainfo") + fi fi + RAWJSONPOSTDATA="" + # special case if [[ $actionname == "connect" ]]; then local locidx @@ -1012,7 +1018,9 @@ debug -f "arglist: remaining opts: $opts" debug -f "curldata: ${curldata}" if [[ $action == "add" || $action == "mv" ]]; then - # note: "obuuid" is empty when adding a builtin node like VPCS + # note: "obuuid" is empty in some cases, eg: + # adding a builtin node like VPCS + # adding a link curlres=$(runcurldatapost "$loc" "$api_endpoint" "$obuuid" "$curldata" ) crv=$? else @@ -1040,6 +1048,55 @@ debug -f "arglist: remaining opts: $opts" return $rv } +# 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" + 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\"" + +debug "applying: $which $qstr" +debug " wantquotes is $wantquotes" +debug " jqfilter is $jqfilter" +debug " jqscript is $jqscript" +debug " awkscript is $awkscript" + + if [[ $which == *f* && -n $jqfilter ]]; then + #debug "$retvar pre jqfilter is $data" +echo "$data" >/tmp/a + data=$(echo "$data" | jq $rawopt "$jqfilter" 2>/dev/null) + if [[ $wantquotes -eq -1 ]]; then + 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 + if [[ $which == *a* && -n $awkscript ]]; then + debug "$retvar pre awkscript is $data" + data=$(echo "$data" | awk -f "$awkscript") + fi + eval $retvar=\"\$data\" + return 0 +} + 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 @@ -1065,55 +1122,20 @@ debug "options: ${opts}" debug "outmode is $outmode" debug "jqscript is $jqscript" debug "jqfilter is $jqfilter" - +debug "awkscript is $awkscript" if [[ $outmode == "raw" ]]; then - [[ ! -z $curlres ]] && echo "$curlres" || rv=2 - elif [[ $outmode == "verbose" ]]; then - #if [[ $locnum -eq 1 ]]; then - # # bold+underline the heading, and store in a temp file - # echo "$curlres" | jq -r "$jqfilter" 2>/dev/null | tr -d '"' | awk '{ if (NR == 1) { printf "\033[1m\033[4m" $0 "\033[0m\n" } }' >$HEADINGFILE - #fi r1="$curlres" - if [[ ! -z $jqfilter ]]; then - debug "jqfilter is $jqfilter" - debug "r1 pre jqfilter is $r1" - r1=$(echo "$r1" | jq -r "$jqfilter" 2>/dev/null | tr -d '"') - debug "r1 post jqfilter is $r1" - fi - #if [[ ! -z $regexpfilter ]]; then - # [[ $VERBOSE -eq 1 ]] && debug "r1 pre regexpfilter is $r1" - # r1=$(echo "$r1" | awk -v filter="$regexpfilter" '(NR==1){ print } (NR !=1 && (match($0,filter))) { print }') - #fi - if [[ ! -z $jqscript ]]; then - debug "jqscript is $jqscript" - debug "jqscript contents: $(cat $jqscript)" - debug "r1 pre jqscript is $r1" -echo "$r1" >/tmp/out - r1=$(echo "$r1" | jq -rf "$jqscript") - fi - [[ ! -z $r1 ]] && echo "$r1" || rv=2 + # only apply jqfilter + apply_filters r1 $QUOTES f + [[ -n $r1 ]] && echo "$r1" || rv=2 + elif [[ $outmode == "verbose" ]]; then + r1="$curlres" + apply_filters r1 $REALLYNOQUOTES + [[ -n $r1 ]] && echo "$r1" || rv=2 else # namesonly r1=$(echo "$curlres") - if [[ ! -z $jqfilter ]]; then - debug "jqfilter is $jqfilter" - debug "r1 pre jqfilter is $r1" - r1=$(echo "$r1" | jq -r "$jqfilter" 2>/dev/null) - debug "r1 post jqfilter is $r1" - fi - if [[ ! -z $jqscript ]]; then - debug "jqscript is $jqscript" - debug "r1 pre jqscript is $r1" - r1=$(echo "$r1" | jq -rf "$jqscript") - fi -# if [[ ! -z $regexpfilter ]]; then -# debug "r1 pre regexpfilter is $r1" -# r1=$(echo "$r1" | awk -v filter="$regexpfilter" '(NR==1){ print } (NR !=1 && (match($0,filter))) { print }') -# fi - if [[ ! -z $awkscript ]]; then - debug "r1 pre awkscript is $r1" - r1=$(echo "$r1" | awk -f "$awkscript") - fi + apply_filters r1 debug "semi-final r1 is [$r1]" if [[ $cmd == "list" ]]; then debug "Global cmd is $GLOBALCMD" @@ -1121,10 +1143,8 @@ echo "$r1" >/tmp/out # ie. we end up with each line being: # location,object,object_uuid - #r1=$(echo "$r1" | egrep -v "^$" | sed "s/^/$loc,$thisgrid,/") r1=$(echo "$r1" | egrep -v "^$" | sed "s/^/$loc,/") else - #r1=$(echo "$r1" | egrep -v "^$" | sed "s/^/$(echo -e "$BOLD")\[$thisgrid\] $(echo -e "$PLAIN")/") r1=$(echo "$r1" | egrep -v "^$") fi fi @@ -1132,7 +1152,6 @@ echo "$r1" >/tmp/out [[ -n $r1 ]] && echo "$r1" | sed 's/__END__://' || rv=2 fi - #echo "$curlres" | jq -r "$jqfilter" | sed "s/^/$(echo -e "$BOLD")\[$thisgrid\] $(echo -e "$PLAIN")/" debug "about to exit with code $rv" return $rv } @@ -1195,7 +1214,7 @@ function getbasefilter() { # epidx [regexp_filter] filterarg="$*" [[ $filterarg == "*" ]] && filterarg=".*" - [[ -n "$*" ]] && refilter="select(.name|test(\"^$*$\"))" || refilter="." + [[ -n "$*" ]] && refilter="select(.${ep_defaultfield[$epidx]}|test(\"^$*$\"))" || refilter="." IFS='#' read -r -a titles <<< "${ep_titles[$epidx]}" IFS='#' read -r -a fields <<< "${ep_fields[$epidx]}" @@ -1293,7 +1312,7 @@ debug "repl is $repl" debug "table after repl [\n$table]" - # ooo re-render table since field lengths may have changed + # re-render table since field lengths may have changed table=$(flatten_table "$table") debug "table in csv is: [$table]" @@ -1388,6 +1407,7 @@ function runaction() { # runaction targetlist optio local actionname line all local force=0 foreground=0 f rv files n thisfile jqres goterror local good allgoodresults errs allbadresults +# oooo change this local jqf='[ "_DC_", .job.id, "_OB_", .status ] | @csv' local jqf_bad='[ "_DC_", "_OB_", .fault.detail, .fault.reason, .status ] | @csv' local objecttype extrainfo @@ -1464,6 +1484,8 @@ debug -f "--> extrainfo = $extrainfo" # Capitalise first letter objecttype=$(echo ${what:0:1} | tr '[a-z]' '[A-Z]')${what:1} +# oooo +# change this jq based on thr action allgoodresults="Server,${objecttype},Job Status" allbadresults="" for f in ${!files[@]} ; do @@ -1599,14 +1621,16 @@ function getdata() { # getdata options arraycontains opts_a "-n" && usecache=0 || usecache=1 arraycontains opts_a "-e" && errordebug=1 +debug "cmd is $cmd" debug "opts is $opts" outfile=$(getarrayopt opts_a o) refilter=$(getarrayopt opts_a f) + # Allow standard globs rather than regexp globs [[ $refilter == "*" ]] && refilter=".*" if [[ -n $refilter ]]; then - obfilter="select(.name|test(\"^$refilter$\"" + obfilter="select(.${ep_defaultfield[$epidx]}|test(\"^$refilter$\"" if [[ $ignorecase -eq 1 ]]; then obfilter="${obfilter}; \"i\"))" else @@ -1617,8 +1641,8 @@ debug "opts is $opts" fi [[ -n $refilter ]] && [[ $quiet -eq 0 ]] && info "${what} filter: ^$refilter$" - # Allow standard globs rather than regexp globs debug "refilter is $refilter" +debug "obfilter is $obfilter" # determine jq filter based on object name jqfilter="" @@ -1645,6 +1669,8 @@ debug "(list) basefilter is $basefilter" basefilter="${basefilter} + \",\" + (.console|tostring)" fi fi + elif [[ $outmode == "raw" ]]; then + [[ $obfilter != "." ]] && basefilter=".[] | $obfilter" fi elif [[ $cmd == "net" ]]; then jqscript="$SCRIPTDIR/diagram.jq" @@ -1653,6 +1679,8 @@ debug "(list) basefilter is $basefilter" if [[ $outmode == "verbose" ]]; then error "Verbose mode not supported for show command yet" return 1 + elif [[ $outmode == "raw" ]]; then + error "Raw mode not supported for show command yet - try 'list' instead" else # namesonly #basefilter=".$jq_obj[] | select(.name|test(\"${arg_array[1]}\"))" basefilter=".[] | $obfilter" @@ -1687,7 +1715,11 @@ debug "awkscript is $awkscript" done wait $pids debug all pids finished - all=$(cat "$TMPDIR"/get.* 2>/dev/null | tr -d : | egrep -v 'DC|Server' | sed '/^[[:space:]]*$/d') + 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 if [[ -z $all ]]; then fail cat "$TMPDIR"/err.* @@ -1702,7 +1734,7 @@ debug all pids finished cat "$TMPDIR"/err.* >/dev/stderr fi fi - debug "combined results: [$all]" + #debug "combined results: [$all]" end=$(($($GDATE +%s%N)/1000)) lastqsecs=$(echo "scale=2; ($end - $start) / 1000000;" | bc) @@ -1956,12 +1988,148 @@ function getnodetypeforadd() { } +# populates globals: +# ndcs +# nobs +# dc_ess +# ob_ess +# data +# allobs +# alluids +# o_arr +# ou_arr +function validate_action_obs() { # whattolist actionfilter showerroropt multiple_obs_allowed [retvar_selobname] [retvar_selobuuid] + local whattolist actionfilter showerroropt + local selobname="" selobuuid="" + local retvar_obname retvar_obuuid multiallowed + whattolist="$1" + actionfilter="$2" + showerroropt="$3" + multiallowed="$4" + retvar_obname="$5" + retvar_obuuid="$6" + + debug "whattolist=$whattolist" + debug "actionfilter=$actionfilter" + debug "showerroropt=$showerroropt" + debug "multiallowed=$multiallowed" + debug "retvar_obname=$retvar_obname" + debug "retvar_obuuid=$retvar_obuuid" + + getdata ${whattolist} list "-f${actionfilter}.*" $showerroropt -c -s -q >"$TMPFILE" + 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 +} + function processcmd() { local cmd arg newarg rv newlocs x err admin idx opts pipe gotargs local whattolist actionname="" actionfilter="" local showerror=0 showerroropt="" - local epidx endpoint origendpoint builtin newname newnodetype newnodetype_uuid + local epidx endpoint origendpoint builtin newname local newnodex=0 newnodey=0 + local dev devuuid port portnum adapnum local BUILTINMODELS="Cloud|VPCS|NAT|Frame Relay switch|Ethernet hub|Ethernet switch" local oldname olduuid cmd=$1 @@ -2063,27 +2231,38 @@ debug "post replacedargs is [$replacedargs]" error "Name of new $whattolist not provided." return 1 fi - newnodetype="${arg_array[3]}" # eg. VPCS, Cisco IOS, etc + if [[ $endpoint == "node" ]]; then + newnodetype="${arg_array[3]}" # eg. VPCS, Cisco IOS, etc -debug "jjjjj newname is $newname" -debug "jjjjj newnodetype is $newnodetype" -debug "jjjjj whattolist is $whattolist" - - # is it a builtin appliance? - if [[ $newnodetype =~ ^($BUILTINMODELS)$ ]]; then - debug "adding a gns3 built-in node ($newnodetype)" - builtin=1 - #whattolist="" - #newnodetype=$(getnodetypeforadd $newnodetype) - #newnodetype_uuid="" - 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}.*" + # 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)) + # replace * with .* + 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 fi elif [[ $actionname == "mv" ]]; then oldname=${arg_array[2]} @@ -2179,126 +2358,89 @@ debug "jjjjj whattolist is $whattolist" fi if [[ -n $whattolist ]]; then + local ndcs nobs data confirm=0 # Get a list of objects to operate on # ie. turn regexp into a list of dcs and obnames first - getdata ${whattolist} list $actionfilter $showerroropt -c -s -q >"$TMPFILE" - rv=$? - if [[ $rv -ne 0 ]]; then - error "Query for matching ${whattolist}s failed." - rv=1 - elif [[ ! -e $TMPFILE ]]; then - error "No matching ${whattolist}s found." - rv=1 - elif grep -q "no results" $TMPFILE; then - error "No ${whattolist}s found matching '$obname'." - rv=1 - elif [[ ! -e $TMPFILE ]]; then - error "Results file does not exist." - rv=1 - else - local ndcs nobs data confirm=0 - ok - debug "tmpfile contents: [$(cat $TMPFILE)]" - echo - data=$(cat "$TMPFILE" | egrep -v "^$") - - # how many servers? - ndcs=$( printf %d $(echo "$data" | awk -F, '{ print $1 }' | sort -u | wc -l)) - [[ $ndcs -eq 1 ]] && dc_ess="" || dc_ess="s" - nobs=$( printf %d $(echo "$data" | awk -F, '{ print $2 }' | sort -u | wc -l)) - [[ $nobs -eq 1 ]] && ob_ess="" || ob_ess="s" - - if [[ $actionname == "connect" || $actionname == "mv" ]]; then - 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 - local allobs alluuids o_arr ou_arr - allobs=$(echo "$data" | awk -F, '{ print $2 }' | sort -u) - alluuids=$(echo "$data" | awk -F, '{ print $3 }' | sort -u) - o_arr=($allobs) - ou_arr=($alluuids) - - if [[ $nobs -gt 1 ]]; then - local o n - info "Matched multiple ${whattolist}s:" - newnodetype="" - while [[ -z $newnodetype ]]; do - echo - n=1 - while read -r o; do - printf "%3d. %s\n" $n "${o_arr[$n]}" - n=$((n + 1)) - done <<<"$allobs" - echo - getstr ":" "" "Select one (q to abort)" - if [[ -n $retstr ]]; then - if [[ $retstr == "q" ]]; then - break - elif [[ $retstr =~ ^[0-9]*$ ]]; then - if [[ $retstr -le 0 || $retstr -ge $n ]]; then - error "Invalid selection" - else - newnodetype="${o_arr[$retstr]}" - newnodetype_uuid="${ou_arr[$retstr]}" - fi - else - local matched=0 x allmatches="" this thisuuid - for x in ${!o_arr[@]}; do - this="${o_arr[$x]}" - thisuuid="${ou_arr[$x]}" - shopt -s nocasematch - if [[ ${this} =~ $retstr ]]; then - 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 - else - newnodetype="$allobs" - newnodetype_uuid="$alluuids" - fi + echo + if [[ $actionname == "connect" || $actionname == "mv" ]]; then + validate_action_obs ${whattolist} "$actionfilter" "$showerroropt" $NOMULTI || return $? + 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 + validate_action_obs ${whattolist} "$actionfilter" "$showerroropt" $NOMULTI newnodetype newnodetype_uuid if [[ -z $newnodetype ]]; then confirm=0 else +debug "newnodetype is $newnodetype" +debug "newnodeuuid is $newnodetype_uuid" notify_nodots "Adding a new ${BOLD}$newnodetype${PLAIN}${PURPLE} named $BOLD$newname${PLAIN}" confirm=1 fi - else # action is not add/connect - echo -e "${PURPLE}About to run '${BOLD}$actionname${PLAIN}${PURPLE}' on ${BOLD}${nobs}${PLAIN}${PURPLE} ${whattolist}${ob_ess} on ${BOLD}${ndcs}${PLAIN}${PURPLE} server${dc_ess}:${PLAIN}" - echo "$data" | awk -F, "BEGIN {lastdc=\"\"} { if (\$1 != lastdc) { print \" ${YELLOW}- ${BOLD}\" \$1 \"${PLAIN}\"; lastdc=\$1; } print \" ${YELLOW}- \" \$2 \"${PLAIN}\"}" - - echo - if [[ $ndcs -le 1 ]]; then - getyn n "Really proceed" - [[ $? -eq 0 ]] && confirm=1 || confirm=0 - else - local confirmcode entered_string - confirmcode=$(generate_random_string) - notify_nodots "Confirmation code is: ${BOLD}${confirmcode}${PLAIN}" - getstr ":" "n" "Enter the above code to proceed, anything else will abort" - entered_string="$retstr" - [[ $entered_string == $confirmcode ]] && confirm=1 || confirm=0 - fi + 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" + validate_action_obs "node" "${dev[$x]}" "$showerroropt" $NOMULTI newn newu || return $? + 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 fi - fi # end if data query got results + else # action is not add/connect + validate_action_obs ${whattolist} "$actionfilter" "$showerroropt" $MULTI || return $? + echo -e "${PURPLE}About to run '${BOLD}$actionname${PLAIN}${PURPLE}' on ${BOLD}${nobs}${PLAIN}${PURPLE} ${whattolist}${ob_ess} on ${BOLD}${ndcs}${PLAIN}${PURPLE} server${dc_ess}:${PLAIN}" + echo "$data" | awk -F, "BEGIN {lastdc=\"\"} { if (\$1 != lastdc) { print \" ${YELLOW}- ${BOLD}\" \$1 \"${PLAIN}\"; lastdc=\$1; } print \" ${YELLOW}- \" \$2 \"${PLAIN}\"}" + + echo + if [[ $ndcs -le 1 ]]; then + getyn n "Really proceed" + [[ $? -eq 0 ]] && confirm=1 || confirm=0 + else + local confirmcode entered_string + confirmcode=$(generate_random_string) + notify_nodots "Confirmation code is: ${BOLD}${confirmcode}${PLAIN}" + getstr ":" "n" "Enter the above code to proceed, anything else will abort" + entered_string="$retstr" + [[ $entered_string == $confirmcode ]] && confirm=1 || confirm=0 + fi + fi else # we dont need to do a data query newnodetype_uuid="" @@ -2308,30 +2450,45 @@ debug "jjjjj whattolist is $whattolist" if [[ $confirm -eq 1 ]]; then if [[ $actionname == "add" ]]; then local postdata - # 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... + 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" + # 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}]}" +echo -e "${YELLOW}${BOLD}rawjson is:\n${RAWJSONPOSTDATA}${PLAIN}" >/dev/stderr + + actiontargets="$curlocs,,," fi - actiontargets="$curlocs,$newname,$newnodetype_uuid" - actiontargets="${actiontargets},$postdata" elif [[ $actionname == "mv" ]]; then oldname=$(echo "$data" | awk -F, '{ print $2 }' | sort -u) olduuid=$(echo "$data" | awk -F, '{ print $3 }' | sort -u) @@ -2361,14 +2518,13 @@ debug " opts = ${opts[@]}" debug " outputfile = ${TMPFILE}" runaction ${endpoint} $actionname "$actiontargets" ${opts[@]} >"$TMPFILE" rv=$? - if [[ $actionname == "add" && $rv -eq 0 && $builtin -eq 0 ]]; then + if [[ $actionname == "add" && $origendpoint == "node" && $rv -eq 0 && $builtin -eq 0 ]]; then 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 - info "Now need to rename '$oldname' to '$newname'..." runaction ${origendpoint} mv "$curlocs,$oldname,$olduuid,name:$newname^" -F [[ $? -ne 0 ]] && fail=1 else @@ -2598,6 +2754,13 @@ LINK="$BLUE$UNDERLINE" UNDERLINE_PRINTED=$(echo -en "$UNDERLINE") UNDERLINE_LENGTH=${#UNDERLINE_PRINTED} +NOMULTI=0 +MULTI=1 + +NOQUOTES=0 +QUOTES=1 +REALLYNOQUOTES=-1 + CURLERRORSTRINGS="(^40.:|error|status.*40.)" # Generate sed script to add colours to certain words @@ -2635,8 +2798,9 @@ addendpoint nodes projects/_CURPROJECT_/nodes node node_id name addendpoint links projects/_CURPROJECT_/links link link_id link_id addepalias links l - addeptitles links "Type" "A-Host_UUID " "A-Port" "Z-Host_UUID " "Z-Port" - addepfields links ".link_type" ".nodes[0].node_id" ".nodes[0].label.text" ".nodes[1].node_id" ".nodes[1].label.text" + addeptitles links "Type" "A-Host_UUID " "A-Port" "Z-Host_UUID " "Z-Port" "ID _" + 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 addendpoint models templates model template_id name addepalias models m @@ -2663,7 +2827,7 @@ addcmd show "Show detail of a given element (eg. compute node, VM, etc)" 2 sh addcmd net "Show network diagram (requires perl Graph::Easy)" -addcmd action "Perform action on given object." 3 act do +addcmd action "Perform action on given object" 3 act do declare -a lines for x in ${!ep_jqobj[@]}; do if [[ -z ${ep_validactions[$x]} ]]; then @@ -2672,7 +2836,7 @@ addcmd action "Perform action on given object." 3 act do lines+=("${ep_jqobj[$x]} actions: ${ep_validactions[$x]}") fi done - addcmdusage action "object_type action_type regexp_filter" "object_type can be: [${ep_jqobj[*]}]" "" "${lines[@]}" + addcmdusage action "object_type action_type regexp_filter [extrainfo]" "object_type can be: [${ep_jqobj[*]}]" "" "${lines[@]}" addcmdoption action "-f" "Force - don't ask for confirmation" addcmdalias "open" "action project open" "_CURPROJECT_" @@ -2685,6 +2849,9 @@ addcmdalias "add" "action node add" "" addcmdalias "rm" "action node del" "" addcmdalias "del" "action node del" "" addcmdalias "mv" "action node mv" "" +addcmdalias "link" "action link add" "" +addcmdalias "unlink" "action link del" "" +addcmdalias "delink" "action link del" "" addcmd help "List regular commands" 0 "?" "h" addcmd exit "Exit from gnscli" 0 quit @@ -2776,12 +2943,13 @@ notify "Getting initial projectlist" oldverbose=$VERBOSE VERBOSE=1 errfile="$TMPDIR"/err -output=$(getdata projects show -e -r 2>"$errfile") +output=$(getdata projects list -e -r 2>"$errfile") rv=$? +debug "rv is $rv" VERBOSE=$oldverbose if [[ $rv -eq 0 ]]; then ok -#cat "$errfile" +cat "$errfile" rm -f "$errfile" jqoutput=$(echo "$output" | jq -r '.[] | .project_id + "|" + .name' 2>"$errfile") rv=$? @@ -2915,10 +3083,10 @@ END { "UUID#" + .link_id, "Type#" + .link_type, "Project UUID#" + .project_uuid, -"A-Node#" + .node[0].node_id, -"A-Port#" + .node[0].label.text, -"Z-Node#" + .node[1].node_id, -"Z-Port#" + .node[1].label.text, +"A-Node#" + .nodes[0].node_id, +"A-Port#" + .nodes[0].label.text, +"Z-Node#" + .nodes[1].node_id, +"Z-Port#" + .nodes[1].label.text, "Capturing#" + (.capturing|tostring), "Suspended#" + (.suspend|tostring), "__END__"