newfeature: can now add a new node, but only builtin types

newfeature: can now delete nodes
This commit is contained in:
Rob Pearce 2021-12-31 16:32:08 +11:00
parent 0f8036657b
commit d618ea2b1a
1 changed files with 304 additions and 183 deletions

239
gnscli.sh
View File

@ -1,9 +1,8 @@
#!/bin/bash
# add node
# issue with json post data
# add appliance
# clean up action output
# rename project
# delete node
# node stop givesjq error
@ -770,20 +769,31 @@ function runcurlget() { # location api_endpoint(ovname)
return $rv
}
function runcurldatapost() { # location api_endpoint "curl data"
function runcurldatapost() { # location api_endpoint endpoint_uuid "curl data"
local thisapi thisurl curlres rv
local loc api_endpoint obname ovmethod
local endpoint_uuid
local data=""
loc="$1"
api_endpoint="$2"
data="$3"
endpoint_uuid="$3"
data="$4"
thisapi=$(get $loc api)
[[ -z $thisapi ]] && echo "cant find api for '$loc'" && return 1
#thisauthdom=$(get $loc authdomain)
debug -f "data is $data"
thisurl="$thisapi/$api_endpoint"
thisurl=${thisurl/\/v2\/appliances/\/v2\/projects\/_CURPROJECT_\/appliances} # special case
thisurl=${thisurl/_CURPROJECT_/$curprojid}
if [[ -n $endpoint_uuid ]]; then
thisurl="$thisurl/$endpoint_uuid"
fi
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"
debug -f "curl -XPOST -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d \"$data\" $thisurl"
set -x
@ -800,8 +810,9 @@ set +x
function runcurlaction() { # location api_endpoint(ovname) obname ovmethod ["curl data"]
local thisapi thisurl curlres rv
#local thisauthdom u p thisauthstr
local loc api_endpoint obname ovmethod
local loc api_endpoint obname ovmethou
local data=""
local curlmethod="POST"
#local data="<action/>"
loc="$1"
api_endpoint="$2"
@ -822,8 +833,15 @@ debug -f "data is $data"
#unset u
#unset p
debug -f "curl -XPOST -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d \"$data\" $thisurl"
curlres=$(curl -XPOST -sk --header 'Content-type: application/xml' --header 'Accept: application/json' -d "$data" $thisurl)
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)
rv=$?
[[ $rv -ne 0 ]] && error "curl to $thisurl failed"
@ -876,6 +894,7 @@ function getgnsmethod() {
start) echo "start";;
stop) echo "stop";;
add) echo "";;
del) echo "";;
#migrate) echo "migrate";;
*)
echo
@ -923,8 +942,13 @@ function runaction_loc() { # runaction_loc syd|etc api_endpoint action_name ob_u
opts="$*"
opts_arr=( $opts )
debug -f "extrainfo : $extrainfo"
debug -f "remaining opts: $opts"
debug -f "arglist - loc: $loc"
debug -f "arglist - epidx: $epidx"
debug -f "arglist - actionname: $actionname"
debug -f "arglist - ob: $ob"
debug -f "arglist - obname: $obname"
debug -f "arglist - extrainfo : $extrainfo"
debug -f "arglist: remaining opts: $opts"
if [[ $actionname == "add" ]]; then
curldata=$(makejson "$extrainfo")
fi
@ -963,21 +987,35 @@ debug -f "remaining opts: $opts"
return 1
fi
debug -f "ABOUT TO CALL CURL FUNCTION...."
debug -f "location: ${loc}"
debug -f "api endpoint: ${api_endpoint}"
debug -f "jq obj: ${jq_obj}"
debug -f "actionname: ${actionname} (gns method: '$method')"
debug -f "obname: ${ob}"
debug -f "ob: ${ob}"
debug -f "options: ${opts}"
debug -f "curldata: ${curldata}"
if [[ $action == "add" ]]; then
curlres=$(runcurldatapost $loc $api_endpoint "$curldata" )
# note: "ob" is empty when adding a builtin node like VPCS
curlres=$(runcurldatapost "$loc" "$api_endpoint" "$ob" "$curldata" )
crv=$?
else
curlres=$(runcurlaction $loc $api_endpoint $ob $method "$curldata" )
curlres=$(runcurlaction "$loc" "$api_endpoint" "$ob" "$method" "$curldata" )
crv=$?
fi
if [[ $curlres =~ $CURLERRORSTRINGS ]]; then
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
crv=99
fi
if [[ $crv -ne 0 ]]; then
rv=1
fi
@ -1144,7 +1182,7 @@ function getbasefilter() { # epidx [regexp_filter]
bf="${bf}] ), (.[] | $refilter | [\"_LOCATION_\""
first=1
for x in ${fields[@]}; do
thisdefault="defaultvalue"
thisdefault="not_found"
[[ $x == ".status" ]] && thisdefault="project_closed"
x=${x//^/ }
#bf="${bf}, try(${x}) catch(\"xx\")"
@ -1290,12 +1328,16 @@ debug "opts is $opts"
else
pids=""
while read -r line ; do
debug -f "processing line: [$line]"
loc=$(echo "$line" | cut -d, -f1)
ob=$(echo "$line" | cut -d, -f2)
obuuid=$(echo "$line" | cut -d, -f3)
extrainfo=$(echo "$line" | cut -d, -f4-)
debug "extrainfo is: $extrainfo"
runaction_loc $loc $epidx $actionname $obuuid $ob "$extrainfo" $opts > "$TMPDIR/run,$loc,$ob" &
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" &
pids="$pids $!"
done <<< "$targetlist"
@ -1390,6 +1432,7 @@ function validate_action() { # nodes/vms/etv actionname
local what acion epidx
what="$1"
action="$2"
debug "validating action '$action' on obtype '$what'"
epidx=$(getepidx $what)
[[ $? -ne 0 ]] && return 1
[[ ${ep_validactions[$epidx]} != *\ $action\ * ]] && return 1
@ -1697,6 +1740,14 @@ function listcommands() {
done
}
function listcommandaliases() {
local x format
format=" %-12s|%-24s|%-36s${PLAIN}\n"
printf "${UNDERLINE}${BOLD}$format" "Alias" "Command" "Default Args"
for x in ${!cmdalias_src[@]}; do
printf "$format" "${cmdalias_src[$x]}" "${cmdalias_dst[$x]}" "${cmdalias_defaultarg[$x]}"
done
}
function generate_random_string() {
local segmentlen=5 x str="" thissegment
@ -1752,11 +1803,20 @@ function expand_cmdalias() { # expand_cmdalias "cmd" newcmd_var newargs_var "ar
return 1
}
function getnodetypeforadd() {
local nt
nt=$(echo "$1" | tr 'A-Z' 'a-z' | tr ' ' '_')
echo "$nt"
}
function processcmd() {
local cmd arg newarg rv newlocs x err admin idx opts pipe gotargs
local whattolist actionname="" actionfilter=""
local showerror=0 showerroropt=""
local epidx endpoint newname newtype newtype_uuid
local epidx endpoint addendpoint newname newnodetype newnodetype_uuid
local newnodex=0 newnodey=0
local BUILTINMODELS="Cloud|VPCS|NAT|Frame Relay switch|Ethernet hub|Ethernet switch"
cmd=$1
shift
arg="$*"
@ -1846,23 +1906,35 @@ debug "post replacedargs is [$replacedargs]"
endpoint=${arg_array[0]} && unset 'arg_array[0]'
whattolist=${endpoint}
actionname=${arg_array[1]} && unset 'arg_array[1]'
GLOBALACTION="$actionname"
if [[ $actionname == "add" ]]; then
newname=${arg_array[2]}
if [[ -z $newname ]]; then
error "Name of new $whattolist not provided."
return 1
fi
# ie. look for models named 'IOS'
# oooo ...if we find one, check its template_type
# if it's qemu then use template_id to figure out appliance id???
# otherwise just use template_type
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)"
addendpoint="node"
#whattolist=""
#newnodetype=$(getnodetypeforadd $newnodetype)
#newnodetype_uuid=""
whattolist="model"
newtype=".*${arg_array[3]}.*"
actionfilter="-f${newtype}"
actionfilter="-f.*${newnodetype}.*"
else
debug "adding an appliance ($newnodetype)"
addendpoint="appliance"
# we will lookup the template ID of "nodetype" later...
whattolist="model"
actionfilter="-f.*${newnodetype}.*"
fi
else
# start/stop/etc
if [[ ${#arg_array[@]} -ge 1 ]]; then
@ -1912,6 +1984,9 @@ debug "post replacedargs is [$replacedargs]"
q)
DONE=1
;;
la)
listcommandaliases
;;
help)
listcommands ADMIN
;;
@ -1937,7 +2012,6 @@ debug "post replacedargs is [$replacedargs]"
;;
action)
# TODO: ooremove any output format opts
validate_action ${endpoint} $actionname
if [[ $? -ne 0 ]]; then
error "'$actionname' is not a valid action for ${endpoint}s"
@ -1950,6 +2024,7 @@ debug "post replacedargs is [$replacedargs]"
return 1
fi
if [[ -n $whattolist ]]; then
# 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"
@ -1969,7 +2044,7 @@ debug "post replacedargs is [$replacedargs]"
else
local ndcs nobs data confirm=0
ok
debug "tmpfile contents: [$(cat $TMPFILE)]"
debug "tmpfile contents: [$(cat $TMPFILE)]"
echo
data=$(cat "$TMPFILE" | egrep -v "^$")
@ -1996,8 +2071,8 @@ debug "tmpfile contents: [$(cat $TMPFILE)]"
if [[ $nobs -gt 1 ]]; then
local o n
info "Matched multiple ${whattolist}s:"
newtype=""
while [[ -z $newtype ]]; do
newnodetype=""
while [[ -z $newnodetype ]]; do
echo
n=1
while read -r o; do
@ -2013,8 +2088,8 @@ debug "tmpfile contents: [$(cat $TMPFILE)]"
if [[ $retstr -le 0 || $retstr -ge $n ]]; then
error "Invalid selection"
else
newtype="${o_arr[$retstr]}"
newtype_uuid="${ou_arr[$retstr]}"
newnodetype="${o_arr[$retstr]}"
newnodetype_uuid="${ou_arr[$retstr]}"
fi
else
local matched=0 x allmatches="" this thisuuid
@ -2023,8 +2098,8 @@ debug "tmpfile contents: [$(cat $TMPFILE)]"
thisuuid="${ou_arr[$x]}"
shopt -s nocasematch
if [[ ${this} =~ $retstr ]]; then
newtype="$this"
newtype_uuid="$thisuuid"
newnodetype="$this"
newnodetype_uuid="$thisuuid"
allmatches="$allmatches [$this]"
matched=$((matched + 1))
fi
@ -2032,26 +2107,26 @@ debug "tmpfile contents: [$(cat $TMPFILE)]"
done
if [[ $matched -eq 0 ]]; then
error "'$retstr' doesn't match any choice"
newtype=""
newnodetype=""
elif [[ $matched -gt 1 ]]; then
error "'$retstr' matched multiple choices: $allmatches"
newtype=""
newnodetype=""
fi
fi
fi
done
else
newtype="$allobs"
newtype_uuid="$alluuids"
newnodetype="$allobs"
newnodetype_uuid="$alluuids"
fi
if [[ -z $newtype ]]; then
if [[ -z $newnodetype ]]; then
confirm=0
else
notify_nodots "Adding a new ${BOLD}$newtype${PLAIN}${PURPLE} named $BOLD$newname${PLAIN}"
notify_nodots "Adding a new ${BOLD}$newnodetype${PLAIN}${PURPLE} named $BOLD$newname${PLAIN}"
confirm=1
fi
else
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}\"}"
@ -2068,18 +2143,47 @@ debug "tmpfile contents: [$(cat $TMPFILE)]"
[[ $entered_string == $confirmcode ]] && confirm=1 || confirm=0
fi
fi
fi # end if data query got results
else
# we dont need to do a data query
newnodetype_uuid=""
confirm=1
fi
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
actiontargets="$curlocs,$newname,$newtype_uuid"
# ooo
#actiontargets="${actiontargets},'{\"name\": \"$newname\", \"node_type\": \"$newtype\", \"compute_id\": \"local\"}'"
actiontargets="${actiontargets},name:$newname^node_type:$newtype^compute_id:local"
# extrainfo=curl post data in the form of key1:val1^key2:val2...
if [[ $addendpoint == "node" ]]; 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=""
actiontargets="$curlocs,$newname,$newnodetype_uuid"
elif [[ $addendpoint == "appliance" ]]; then
# name will be autogenerated from the appliance name.
postdata="x:$newnodex^y:$newnodey"
actiontargets="$curlocs,$newname,$newnodetype_uuid"
else
error "Don't know how to add node with endpoint of '$endpoint'"
return 1
fi
actiontargets="${actiontargets},$postdata"
# replace endpoint with the correct one based on whether
# we are adding a gns3 builtin node or an appliance
endpoint="${addendpoint}"
else
actiontargets=$(echo "$data")
fi
@ -2096,6 +2200,7 @@ debug "tmpfile contents: [$(cat $TMPFILE)]"
rm -f "$TMPFILE"
debug "about to call runaction with:"
debug " endpoint = $endpoint"
debug " obtype = $endpoint"
debug " actionname = $actionname"
debug " actiontargets = $actiontargets"
@ -2109,8 +2214,7 @@ debug " outputfile = ${TMPFILE}"
echo
info $(printf "action submission time: %s seconds" "$lastqsecs")
fi
fi
fi
fi # end if confirm == 1
rm -f "$TMPFILE"
;;
exit)
@ -2161,6 +2265,7 @@ function checkfor() { # checkfor name [pkg_name]
local what pkgname
local hw os ok=0 confirm=0
local alt="" rv globvar=""
local realpath=""
what=$1
pkg_name=${2:-$what}
@ -2169,9 +2274,6 @@ function checkfor() { # checkfor name [pkg_name]
if [[ $hw == "aarch64" ]]; then # Android Termux
alias which="command -v"
fi
which $what >/dev/null 2>&1
rv=$?
if [[ $rv -ne 0 ]]; then
if [[ $what == "gdate" ]]; then
alt=date
globvar=GDATE
@ -2179,19 +2281,26 @@ function checkfor() { # checkfor name [pkg_name]
alt=sed
globvar=GSED
fi
realpath=$(which $what 2>&1)
rv=$?
if [[ $rv -ne 0 ]]; then
if [[ -n $alt ]]; then
$alt --version 2>/dev/null | grep -q GNU 2>/dev/null
if [[ $? -eq 0 ]]; then
local path
altpath=$(which $alt 2>/dev/null)
what=${alt}
[[ -n $globvar ]] && eval "$globvar=\"$altpath\""
realpath=$(which $alt 2>/dev/null)
rv=0
fi
fi
fi
if [[ $rv -eq 0 ]]; then
if [[ -n $globvar ]]; then
debug "found $what at $realpath - have set \$$globvar"
eval "$globvar=\"$realpath\""
else
debug "found $what at $realpath - no globvar"
fi
ok=1
else
error "$what binary not found in \$PATH."
@ -2221,7 +2330,9 @@ function checkfor() { # checkfor name [pkg_name]
fi
fi
which $what >/dev/null 2>&1
return $?
rv=$?
[[ $rv -eq 0 && -n $globvar ]] && eval "$globvar=\"$realpath\""
return $rv
}
function killtmux() {
@ -2308,6 +2419,8 @@ LINK="$BLUE$UNDERLINE"
UNDERLINE_PRINTED=$(echo -en "$UNDERLINE")
UNDERLINE_LENGTH=${#UNDERLINE_PRINTED}
CURLERRORSTRINGS="(^40.:|error|status.*403)"
# 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\}
@ -2339,7 +2452,7 @@ addendpoint nodes projects/_CURPROJECT_/nodes node node_id name
addepalias nodes n
addeptitles nodes "Node " "Model_UUID" "Status " "Console_Port"
addepfields nodes ".name" ".template_id" ".status" ".console"
addepactions nodes start stop connect add
addepactions nodes start stop connect add del
addendpoint links projects/_CURPROJECT_/links link link_id link_id
addepalias links l
@ -2351,6 +2464,11 @@ addendpoint models templates model template_id name
addeptitles models "Name" "Category" "UUID"
addepfields models ".name" ".category" ".template_id"
addendpoint appliances appliances appliance template_id name
addepalias appliances a
addeptitles appliances "Name" "Category" "Ports"
addepfields appliances ".name" ".category" ".qemu.adapters"
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[*]}]"
@ -2383,10 +2501,13 @@ addcmdalias "stop" "action node stop" ""
addcmdalias "connect" "action node connect" ""
addcmdalias "c" "action node connect" ""
addcmdalias "add" "action node add" ""
addcmdalias "rm" "action node del" ""
addcmdalias "del" "action node del" ""
addcmd help "List regular commands" 0 "?" "h"
addcmd exit "Exit from gnscli" 0 quit
addcmd -a help "List admin commands" 0 "?" "h"
addcmd -a la "List command aliases" 0
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