Deployment Architecture

CLI command completion? Yes, and here's how. (For bash 4.0 and higher.)

Splunk Employee
Splunk Employee

Not technically a question, but pretty sure will be helpful to many. If not helpful to you, please don't upvote.


As we know, Splunk CLI commands are in the form splunk <verb> <object>: splunk add index, splunk disable deploy-server, ...

To get TAB completion of the <verb> and the <object>, just source the bash script listed below (say we named the script cli-comand-completion.sh), like so:


$ . cli-command-completion.sh

Operates exactly like bash-native command/filename completion. Type
splunk di, and hit TAB TAB; you see the possible completions:

$ splunk di
diag disable dispatch display

Let's say you're interested in splunk disable, so you add sa TAB, and
bash completes splunk disable for you. Another TAB TAB, and you see
what things (i.e., objects) you may splunk disable:

$ splunk disable
ad listen web-ssl
app maintenance-mode winhostmon
boot-start monitornohandle winnetmon
deploy-client perfmon winprintmon
eventlog registry wmi
index webserver

Let's further say you're interested in splunk disable index, so you add i TAB, and bash completes splunk disable index for you.


If you want to see all the possible verbs, just type splunk SPACE,
and hit TAB TAB:


$ splunk
add ftr restartss
anonymize ftw _rest_xml_dump
apply httpport rolling-restart
btool install rtsearch
bucket-maint _internal search
clean juststopit set
clean-dispatch list show
clone-prep-clear-config livetail soapport
cmd logout spool
commands migrate start
createssl _normalizepath startnoss
diag offline status
disable pooling stop
dispatch _port_check train
display _RAW_envvars validate
edit rebuild verifyconfig
enable recover version
envvars reload -version
find remove --version
fsck restart _web_bootstart


A few notes about this script:

  • covers Splunk 6.0
  • will work fine with Splunk 4.x and 5.x, except that it will mistakenly show some extra, invalid objects for a few verbs; these objects are invalid in older Spunk releases, but valid in Splunk 6.0
  • will be packaged with future (after 6.x) releases of Splunk; it will be found in SPLUNK_HOME/bin/ directory; further, the SPLUNK_HOME/bin/setSplunkEnv util will run this script, and confirm success with the message   Tab-completion of "splunk <verb> <object>" is available.
  • you can save this script anywhere you like on the host of your Splunk installation; but if you save it in SPLUNK_HOME/bin/, it'll work without you having set the SPLUNK_HOME env var

And here's the script code:


# Vainstein K 12aug2013
­
­
# # # Check a few prereqs.
[ `basename $SHELL` != 'bash' ] && echo 'Sorry, this is only for bash' >&2 && return 11
[ ${BASH_VERSINFO[0]} -lt 4 ] && echo 'Sorry, only works with bash 4.0 or higher' >&2 && return 12
[ `type -t complete` != 'builtin' ] && echo 'Sorry, need a bash that supports programmable command completion' >&2 && return 13
­
­
die () {
echo "(exit=$?) $@" >&2 && exit 42
}
­
­
ifSourced () { # do NOT exit(1) from this function!
local readonly tempfile=`pwd`/tmp--cli-completion--$$
rm -f $tempfile
­
$BASH ${BASH_ARGV[0]} --populateTempfile $tempfile
[ $? -eq 0 ] || return
[ -e $tempfile ] || return
. $tempfile
rm -f $tempfile
­
# # # Associate the completion function with the splunk binary.
local readonly completionFunction=fSplunkComplete
complete -r splunk 2>/dev/null
complete -F $completionFunction splunk
­
# You can view the completion function anytime via: $ type fSplunkComplete
}
­
­
ifInvoked () { # all error checking happens in this function
local readonly debug=false
local readonly tempfile=$1
$debug && echo "Told that tempfile=$tempfile"
­
# # # If anything goes wrong, at least we don't pollute cwd with our tempfile.
$debug || trap "rm -f $tempfile" SIGINT SIGQUIT SIGTERM SIGABRT SIGPIPE
touch $tempfile || die "Cannot touch tempfile=$tempfile"
­
# # # Decide where SPLUNK_HOME is.
if [ "$(dirname $(pwd))" == 'bin' ]; then
local readonly splunkHome=$(dirname $(dirname $(pwd)))
elif [ -n "$SPLUNK_HOME" ]; then
local readonly splunkHome=$SPLUNK_HOME
else
die 'Cannot figure out where SPLUNK_HOME is'
fi
$debug && echo "Decided SPLUNK_HOME=$splunkHome"
­
# # # Check that splunk (the binary) exists.
local readonly splunkBinary=$splunkHome/bin/splunk
[ -e $splunkBinary -a -x $splunkBinary ] || die "Cannot find expected binary=$splunkBinary"
­
# # # Find the file with object->{verb1,verb2,...} map.
local readonly splunkrcCmdsXml=$splunkHome/etc/system/static/splunkrc_cmds.xml
[ -e $splunkrcCmdsXml ] || die "Cannot find expected file $splunkrcCmdsXml"
$debug && echo "Shall read verb-obj info from: $splunkrcCmdsXml"
­
# # # Parse the map file, and generate our internal verb->{objA,objB,...} map.
local -A verb_to_objects
local line object verb objectsForThisVerb lineNumber=0
local inItem=false
local readonly regex_depr='\<depr\>'
local readonly regex_verb='\<verb\>'
local readonly regex_synonym='\<synonym\>'
while read line; do
lineNumber=$((lineNumber+1))
­
if $inItem; then
if [[ $line =~ '</item>' ]]; then
$debug && echo "Exited item tag at line=$lineNumber; this was obj=$object"
inItem=false
object=''
elif [[ $line =~ '<cmd name' && ! $line =~ $regex_depr && ! $line =~ $regex_synonym ]]; then
[ -z "$object" ] && die "BUG: No object within item tag. (At line $lineNumber of $splunkrcCmdsXml)"
verb=${line#*\"} # remove shortest match of .*" from the front
verb=${verb%%\"*} # remove longest match of ".* from the back
[ "$verb" == '_internal' ] && continue # Why the... eh, moving on.
objectsForThisVerb=${verb_to_objects[$verb]}
objectsForThisVerb="$objectsForThisVerb $object"
verb_to_objects[$verb]=$objectsForThisVerb
$debug && echo "Mapped object=$object to verb=$verb at line=$lineNumber; now objectsForThisVerb='$objectsForThisVerb'"
fi
­
else # ! inItem
if [[ $line =~ '<item obj' && ! $line =~ $regex_depr && ! $line =~ $regex_verb && ! $line =~ $regex_synonym ]]; then
inItem=true
object=${line#*\"} # remove shortest match of .*" from the front
object=${object%%\"*} # remove longest match of ".* from the back
$debug && echo "Entered item tag at line=$lineNumber, parsed object=$object"
[ "$object" == 'on' ] && inItem=false # Do not expose Amrit's puerile jest.
[ "$object" == 'help' ] && inItem=false # Although 'help' is a verb, splunkrc_cmds.xml constructs it as an object; ugh. We'll deal with the objects (topics) of 'splunk help' separately, below.
fi
fi
­
done < $splunkrcCmdsXml
$debug && echo "Processed $lineNumber lines. Map keys: ${!verb_to_objects[*]}, values: ${verb_to_objects[@]}"
­
# # # Oh wait, '<verb> deploy-server' aren't in splunkrc_cmds.xml; thanks, Jojy!!!!!
for verb in reload enable disable display; do
objectsForThisVerb=${verb_to_objects[$verb]}
objectsForThisVerb="$objectsForThisVerb deploy-server"
verb_to_objects[$verb]=$objectsForThisVerb
done
­
# # # Find the file with topics understood by 'splunk help <topic>' command, and extract list of topics.
local readonly literalsPy=$splunkHome/lib/python2.7/site-packages/splunk/clilib/literals.py
[ -e $literalsPy ] || die "Cannot find expected file $literalsPy"
$debug && echo "Shall read help topics list from: $literalsPy"
local readonly helpTopics=$(sed '/^addHelp/! d; s/^addHelp//; s/,.*$//; s/[^a-zA-Z_-]/ /g; s/^[ ]*//; s/[ ].*$//; /^$/ d' $literalsPy | sort | uniq)
$debug && echo "Parsed help topics list as: $helpTopics"
­
#######################################################
# # # Write the completion function to tempfile: BEGIN.
local readonly completionFunction=fSplunkComplete
echo -e 'function '$completionFunction' () {' >> $tempfile
echo -e '\tlocal wordCur=${COMP_WORDS[COMP_CWORD]}' >> $tempfile
echo -e '\tlocal wordPrev=${COMP_WORDS[COMP_CWORD-1]}' >> $tempfile
echo -e '\tcase $wordPrev in' >> $tempfile
­
# # # What can follow 'splunk' itself? Verbs used in main.c to key the 'cmd_handlers' array; and verbs from splunkrc_cmds.xml; and 'help'.
local readonly keys__cmd_handlers='ftr start startnoss stop restart restartss status rebuild train fsck clean-dispatch validate verifyconfig anonymize find clean createssl juststopit migrate --version -version version httpport soapport spool ftw envvars _RAW_envvars _port_check cmd _rest_xml_dump search dispatch rtsearch livetail _normalizepath _internal logout btool pooling _web_bootstart offline clone-prep-clear-config diag'
local allVerbs="${!verb_to_objects[*]}"
echo -e '\t\tsplunk)\n\t\t\tCOMPREPLY=( $(compgen -W "'$keys__cmd_handlers $allVerbs' help" -- $wordCur) ) ;;' >> $tempfile
­
# # # What can follow 'splunk _internal'? see cmd_internal() from main.c
local readonly actions_internal='http mgmt https pre-flight-checks check-db call rpc rpc-auth soap-call soap-call-auth prefixcount totalcount check-xml-files first-time-run import-flat-files make-splunkweb-certs-and-var-run-merged'
echo -e '\t\t_internal)\n\t\t\tCOMPREPLY=( $(compgen -W "'$actions_internal'" -- $wordCur) ) ;;' >> $tempfile
­
# # # Options to 'splunk clean' are in CLI::clean() of src/main/Clean.cpp; to 'splunk fsck', in usageBanner of src/main/Fsck.cpp; to 'splunk migrate', in CLI::migrate() of src/main/Migration.cpp
echo -e '\t\tclean)\n\t\t\tCOMPREPLY=( $(compgen -W "all eventdata globaldata userdata inputdata locks deployment-artifacts" -- $wordCur) ) ;;' >> $tempfile
echo -e '\t\tfsck)\n\t\t\tCOMPREPLY=( $(compgen -W "scan repair clear-bloomfilter make-searchable" -- $wordCur) ) ;;' >> $tempfile
echo -e '\t\tmigrate)\n\t\t\tCOMPREPLY=( $(compgen -W "input-records to-modular-inputs rename-cluster-app" -- $wordCur) ) ;;' >> $tempfile
­
# # # List the help topics.
echo -e '\t\thelp)\n\t\t\tCOMPREPLY=( $(compgen -W "'$helpTopics'" -- $wordCur) ) ;;' >> $tempfile
­
# # # What can follow 'splunk cmd'? any executable in SPLUNK_HOME/bin/
echo -e '\t\tcmd)\n\t\t\tCOMPREPLY=( $(compgen -o default -o filenames -G "'$splunkHome'/bin/*" -- $wordCur) ) ;;' >> $tempfile
­
# # # Finally, let each verb be completed by its objects.
for verb in $allVerbs; do
echo -e '\t\t'$verb')\n\t\t\tCOMPREPLY=( $(compgen -W "'${verb_to_objects[$verb]}'" -- $wordCur) ) ;;' >> $tempfile
done
­
# # # And if we've run out of suggestions, revert to bash's default completion behavior: filename completion.
echo -e '\t\t*)\n\t\t\tCOMPREPLY=( $(compgen -f -- $wordCur) ) ;;' >> $tempfile
­
echo -e '\tesac' >> $tempfile
echo -e '}' >> $tempfile
$debug && cp $tempfile $tempfile~bak
# # # Write the completion function to tempfile: DONE.
######################################################
­
# # # Sanity check: source the tempfile, make sure that the function we wrote can be parsed and loaded by the shell.
unset $completionFunction
. $tempfile
[ "`type -t $completionFunction`" == 'function' ] || die 'BUG: generated completion function cannot be parsed by bash'
}
­
­
if [ $SHLVL -eq 1 ]; then
[ $# -ge 1 ] && echo "Ignoring supplied arguments: $@" >&2
ifSourced
elif [ $SHLVL -eq 2 ]; then
if [ $# -eq 2 ] && [ $1 == '--populateTempfile' ]; then
ifInvoked $2
else
echo -e "This script must be sourced, like so:\n\n\t\033[1m. $0\033[0m\n"
fi
else
: # user is running screen(1) or something of the sort.
fi
­
­
# # # Clean up.
unset die ifSourced ifInvoked

1 Solution

Splunk Employee
Splunk Employee

hi andreas,

Sorry about that! Fixed for good, and posted the tool I wrote to make that happen.

Thank you for reporting the problem.

V

View solution in original post

Splunk Employee
Splunk Employee

hi andreas,

Sorry about that! Fixed for good, and posted the tool I wrote to make that happen.

Thank you for reporting the problem.

V

View solution in original post

Explorer

There are some strange code parts, involving "splunk disable deploy-serverX" - maybe a accidental automatic substition. Could you please check and correct this?!

0 Karma