Just realized that I missed the confirmation email from Peter, So, here is his signed-off-by,
Signed-off-by: Peter Amidon <pe...@picnicpark.org> On Mon, Feb 23, 2015 at 8:45 AM, Alex Wang <al...@nicira.com> wrote: > Waiting for a sign-off from Peter, > > But I'd like to send it out as RFC first~ > > Thanks, > Alex Wang, > > On Mon, Feb 23, 2015 at 8:48 AM, Alex Wang <al...@nicira.com> wrote: > >> From: Peter Amidon <pe...@picnicpark.org> >> >> This patch adds bash command-line completion script for ovs-vsctl. >> Therein, codes are added to ovs-vsctl to allow it to print the >> options and command arguments. The ovs-vsctl-bashcomp.bash will >> parse the vsctl command and complete on the user input. >> >> The completion script can do the following:: >> >> - display available completion and complete on user input for >> global/local options, command, and argument. >> >> - query database and expand keywords like 'table/record/column/key' >> to available completions. >> >> - deal with argument relations like 'one and more', 'zero or one'. >> >> - complete multiple ovs-vsctl commands cascaded via '--'. >> >> To use the script, either copy it inside /etc/bash_completion.d/ >> or manually run it via . ovs-vsctl-bashcomp.bash. >> >> Signed-off-by: Alex Wang <al...@nicira.com> >> --- >> AUTHORS | 1 + >> utilities/automake.mk | 1 + >> utilities/ovs-vsctl-bashcomp.bash | 778 >> +++++++++++++++++++++++++++++++++++++ >> utilities/ovs-vsctl.c | 287 +++++++++++--- >> 4 files changed, 1018 insertions(+), 49 deletions(-) >> create mode 100755 utilities/ovs-vsctl-bashcomp.bash >> >> diff --git a/AUTHORS b/AUTHORS >> index c85ecd4..75fe3a5 100644 >> --- a/AUTHORS >> +++ b/AUTHORS >> @@ -300,6 +300,7 @@ Pankaj Thakkar thak...@nicira.com >> Pasi Kärkkäinen pa...@iki.fi >> Paulo Cravero pcrav...@as2594.net >> Pawan Shukla shuk...@vmware.com >> +Peter Amidon pe...@picnicpark.org >> Peter Balland pe...@nicira.com >> Peter Phaal peter.ph...@inmon.com >> Prabina Pattnaik prabina.pattn...@nechclst.in >> diff --git a/utilities/automake.mk b/utilities/automake.mk >> index 09d6702..5aae782 100644 >> --- a/utilities/automake.mk >> +++ b/utilities/automake.mk >> @@ -30,6 +30,7 @@ docs += utilities/ovs-command-bashcomp.INSTALL.md >> EXTRA_DIST += \ >> utilities/ovs-check-dead-ifs.in \ >> utilities/ovs-appctl-bashcomp.bash \ >> + utilities/ovs-vsctl-bashcomp.bash \ >> utilities/ovs-command-bashcomp.INSTALL.md \ >> utilities/ovs-ctl.in \ >> utilities/ovs-dev.py \ >> diff --git a/utilities/ovs-vsctl-bashcomp.bash >> b/utilities/ovs-vsctl-bashcomp.bash >> new file mode 100755 >> index 0000000..ba4429f >> --- /dev/null >> +++ b/utilities/ovs-vsctl-bashcomp.bash >> @@ -0,0 +1,778 @@ >> +SAVE_IFS=$IFS >> +IFS=" >> +" >> +_OVSDB_SERVER_LOCATION="" >> + >> +# Run ovs-vsctl and make sure that ovs-vsctl is always called with >> +# the correct --db argument. >> +_ovs_vsctl () { >> + local _db >> + >> + if [ -n "$_OVSDB_SERVER_LOCATION" ]; then >> + _db="--db=$_OVSDB_SERVER_LOCATION" >> + fi >> + ovs-vsctl ${_db} "$@" >> +} >> + >> +# ovs-vsctl --commands outputs in this format: >> +# >> +# main = <localopts>,<name>,<options> >> +# localopts = ([<localopt>] )* >> +# localopt = --[^]]* >> +# name = [^,]* >> +# arguments = ((!argument|?argument|*argument|+argument) )* >> +# argument = ([^ ]*|argument\|argument) >> +# >> +# The [] characters in local options are just delimiters. The >> +# argument prefixes mean: >> +# !argument :: The argument is required >> +# ?argument :: The argument is optional >> +# *argument :: The argument may appear any number (0 or more) times >> +# +argument :: The argument may appear one or more times >> +# A bar (|) character in an argument means thing before bar OR thing >> +# after bar; for example, del-port can take a port or an interface. >> + >> +_OVS_VSCTL_COMMANDS="$(_ovs_vsctl --commands)" >> + >> +# This doesn't complete on short arguments, so it filters them out. >> +_OVS_VSCTL_OPTIONS="$(_ovs_vsctl --options | awk '/^--/ { print $0 }' \ >> + | sed -e 's/\(.*\)=ARG/\1=/')" >> +IFS=$SAVE_IFS >> + >> +declare -A _OVS_VSCTL_PARSED_ARGS >> +declare -A _OVS_VSCTL_NEW_RECORDS >> + >> +# This is a convenience function to make sure that user input is >> +# looked at as a fixed string when being compared to something. $1 is >> +# the input; this behaves like 'grep "^$1"' but deals with regex >> +# metacharacters in $1. >> +_ovs_vsctl_check_startswith_string () { >> + awk 'index($0, thearg)==1' thearg="$1" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on global options. >> +_ovs_vsctl_bashcomp_globalopt () { >> + local options result >> + >> + options="" >> + result=$(printf "%s\n" "${_OVS_VSCTL_OPTIONS}" \ >> + | _ovs_vsctl_check_startswith_string "${1%=*}") >> + if [[ $result =~ "=" ]]; then >> + options="NOSPACE" >> + fi >> + printf -- "${options}\nEO\n${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on local options. >> +_ovs_vsctl_bashcomp_localopt () { >> + local options result possible_opts >> + >> + possible_opts=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" | cut -f1 >> -d',') >> + # This finds all options that could go together with the >> + # already-seen ones >> + for prefix_arg in $1; do >> + possible_opts=$(printf "%s\n" "$possible_opts" \ >> + | grep -- "\[${prefix_arg%%=*}=\?\]") >> + done >> + result=$(printf "%s\n" "${possible_opts}" \ >> + | tr ' ' '\n' | tr -s '\n' | sort | uniq) >> + # This removes the already-seen options from the list so that >> + # users aren't completed for the same option twice. >> + for prefix_arg in $1; do >> + result=$(printf "%s\n" "${result}" \ >> + | grep -v -- "\[${prefix_arg%%=*}=\?\]") >> + done >> + result=$(printf "%s\n" "${result}" | sed -ne 's/\[\(.*\)\]/\1/p' \ >> + | _ovs_vsctl_check_startswith_string "$2") >> + if [[ $result =~ "=" ]]; then >> + options="NOSPACE" >> + fi >> + printf -- "${options}\nEO\n${result}" >> +} >> + >> +# $1 = given local options. >> +# $2 = word to complete on. >> +# Complete on command that could contain the given local options. >> +_ovs_vsctl_bashcomp_command () { >> + local result possible_cmds >> + >> + possible_cmds=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}") >> + for prefix_arg in $1; do >> + possible_cmds=$(printf "%s\n" "$possible_cmds" \ >> + | grep -- "\[$prefix_arg=\?\]") >> + done >> + result=$(printf "%s\n" "${possible_cmds}" \ >> + | cut -f2 -d',' \ >> + | _ovs_vsctl_check_startswith_string "$2") >> + printf -- "${result}" >> +} >> + >> +# $1 = completion result to check. >> +# Return 0 if the completion result is non-empty, otherwise return 1. >> +_ovs_vsctl_detect_nonzero_completions () { >> + local tmp newarg >> + >> + newarg=${1#*EO} >> + readarray tmp <<< "$newarg" >> + if [ "${#tmp[@]}" -eq 1 ] && [ "${#newarg}" -eq 0 ]; then >> + return 1 >> + fi >> + return 0 >> +} >> + >> +# $1 = argument format to expand. >> +# Expand '+ARGUMENT' in argument format to '!ARGUMENT *ARGUMENT'. >> +_ovs_vsctl_expand_command () { >> + result=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" \ >> + | grep -- ",$1," | cut -f3 -d',' | tr ' ' '\n' \ >> + | awk '/\+.*/ { name=substr($0,2); >> + print "!"name; print "*"name; next; } >> + 1') >> + printf -- "${result}\n!--" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on table. >> +_ovs_vsctl_complete_table () { >> + local result >> + >> + result=$(ovsdb-client --no-heading list-tables >> $_OVSDB_SERVER_LOCATION Open_vSwitch \ >> + | _ovs_vsctl_check_startswith_string "$1") >> + printf -- "EO\n%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on record. Provide both the name and uuid. >> +_ovs_vsctl_complete_record () { >> + local table uuids names new_record >> + >> + table="${_OVS_VSCTL_PARSED_ARGS[TABLE]}" >> + new_record="${_OVS_VSCTL_NEW_RECORDS[${table^^}]}" >> + # Tables should always have an _uuid column >> + uuids=$(_ovs_vsctl --no-heading -f table -d bare --columns=_uuid \ >> + list $table | _ovs_vsctl_check_startswith_string >> "$1") >> + # Names don't always exist, silently ignore if the name column is >> + # unavailable. >> + names=$(_ovs_vsctl --no-heading -f table -d bare \ >> + --columns=name list $table \ >> + 2>/dev/null \ >> + | _ovs_vsctl_check_startswith_string "$1") >> + printf -- "EO\n%s\n%s\n%s\n" "${uuids}" "${names}" "${new_record}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on bridge. >> +_ovs_vsctl_complete_bridge () { >> + local result >> + >> + result=$(_ovs_vsctl list-br | _ovs_vsctl_check_startswith_string >> "$1") >> + printf -- "EO\n%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on port. If a bridge has already been specified, >> +# just complete for that bridge. >> +_ovs_vsctl_complete_port () { >> + local ports result >> + >> + if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then >> + ports=$(_ovs_vsctl list-ports >> "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}") >> + else >> + local all_ports >> + all_ports=$(_ovs_vsctl --format=table \ >> + --no-headings \ >> + --columns=name \ >> + list Port) >> + ports=$(printf "$all_ports" | tr -d '" ' | sort -u) >> + fi >> + result=$(_ovs_vsctl_check_startswith_string "$1" <<< "$ports") >> + printf -- "EO\n%s\n" "${result}" >> +} >> + >> +# $1: Atom to complete (as usual) >> +# $2: Table to complete the key in >> +# $3: Column to find keys in >> +# $4: Prefix for each completion >> +# Complete on key based on given table and column info. >> +_ovs_vsctl_complete_key_given_table_column () { >> + local keys >> + >> + keys=$(_ovs_vsctl --no-heading --columns="$3" list \ >> + "$2" \ >> + | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1 \ >> + | xargs printf "$4%s\n" | _ovs_vsctl_check_startswith_string >> "$1") >> + result="${keys}" >> + printf -- "%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on key. >> +__complete_key () { >> + # KEY is used in both br-set-external-id/br-get-external id (in >> + # which case it is implicitly a key in the external-id column) and >> + # in remove, where it is a table key. This checks to see if table >> + # is set (the remove scenario), and then decides what to do. >> + local result >> + >> + if [ -n "${_OVS_VSCTL_PARSED_ARGS[TABLE]}" ]; then >> + local column=$(tr -d '\n' <<< >> ${_OVS_VSCTL_PARSED_ARGS["COLUMN"]}) >> + result=$(_ovs_vsctl_complete_key_given_table_column \ >> + "$1" \ >> + ${_OVS_VSCTL_PARSED_ARGS["TABLE"]} \ >> + $column \ >> + "") >> + else >> + result=$(_ovs_vsctl br-get-external-id \ >> + ${_OVS_VSCTL_PARSED_ARGS["BRIDGE"]} \ >> + | cut -d'=' -f1 | _ovs_vsctl_check_startswith_string >> "$1") >> + fi >> + printf -- "%s" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on key. >> +_ovs_vsctl_complete_key () { >> + # KEY is used in both br-set-external-id/br-get-external id (in >> + # which case it is implicitly a key in the external-id column) and >> + # in remove, where it is a table key. This checks to see if table >> + # is set (the remove scenario), and then decides what to do. >> + local result >> + >> + result="$(__complete_key $1)" >> + # If result is empty, just use user input as result. >> + if [ -z "$result" ]; then >> + result=$1 >> + fi >> + printf -- "EO\n%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on value. >> +_ovs_vsctl_complete_value () { >> + local result >> + >> + # Just use user input as result. >> + if [ -z "$result" ]; then >> + result=$1 >> + fi >> + printf -- "EO\n%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on key=value. >> +_ovs_vsctl_complete_key_value () { >> + local orig_completions new_completions >> + >> + orig_completions=$(__complete_key "$1") >> + for completion in ${orig_completions#*EO}; do >> + new_completions="${new_completions} ${completion}=" >> + done >> + # If 'new_completions' is empty, just use user input as result. >> + if [ -z "$new_completions" ]; then >> + new_completions=$1 >> + fi >> + printf -- "NOSPACE\nEO\n%s" "${new_completions}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on column. >> +_ovs_vsctl_complete_column () { >> + local columns result >> + >> + columns=$(ovsdb-client --no-headings list-columns >> $_OVSDB_SERVER_LOCATION \ >> + Open_vSwitch ${_OVS_VSCTL_PARSED_ARGS["TABLE"]}) >> + result=$(printf "%s\n" "${columns}" \ >> + | tr -d ':' | cut -d' ' -f1 \ >> + | _ovs_vsctl_check_startswith_string "$1" | sort | uniq) >> + printf -- "EO\n%s\n" "${result}" >> +} >> + >> +# Extract all system interfaces. >> +_ovs_vsctl_get_sys_intf () { >> + local result >> + >> + case "$(uname -o)" in >> + *Linux*) >> + result=$(ip -o link 2>/dev/null | cut -d':' -f2 \ >> + | sed -e 's/^ \(.*\)/\1/') >> + ;; >> + *) >> + result=$(ifconfig -a -s 2>/dev/null | cut -f1 -d' ' | tail >> -n +2) >> + ;; >> + esac >> + printf "%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on system interface. >> +_ovs_vsctl_complete_sysiface () { >> + local result >> + >> + result=$(_ovs_vsctl_get_sys_intf | >> _ovs_vsctl_check_startswith_string "$1") >> + printf -- "EO\n%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on interface. If a bridge has already been specified, >> +# just complete for that bridge. >> +_ovs_vsctl_complete_iface () { >> + local result >> + >> + if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then >> + result=$(_ovs_vsctl list-ifaces >> "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}") >> + else >> + for bridge in $(_ovs_vsctl list-br); do >> + local ifaces >> + >> + ifaces=$(_ovs_vsctl list-ifaces "${bridge}") >> + result="${result} ${ifaces}" >> + done >> + fi >> + printf "EO\n%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on COLUMN?:KEY=VALUE. >> +_ovs_vsctl_complete_column_optkey_value () { >> + local result column key value completion >> + >> + column=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -d':' -f1) >> + key=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -s -d':' -f2) >> + # The tr -d '\n' <<< makes sure that there are no leading or >> + # trailing accidental newlines. >> + table=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["TABLE"]}) >> + # This might also be called after add-port or add-bond; in those >> + # cases, the table should implicitly be assumed to be "Port". >> + # This is done by checking if a NEW- parameter has been >> + # encountered and, if it has, using that type without the NEW- as >> + # the table. >> + if [ -z "$table" ]; then >> + if [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-PORT"]} ] \ >> + || [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-BOND-PORT"]} ]; then >> + table="Port" >> + fi >> + fi >> + if [ -z "$key" ]; then >> + local columns=$(ovsdb-client --no-headings list-columns \ >> + $_OVSDB_SERVER_LOCATION Open_vSwitch $table) >> + >> + result=$(printf "%s\n" "${columns}" \ >> + | awk '/key.*value/ { print $1":"; next } >> + { print $1; next }' \ >> + | _ovs_vsctl_check_startswith_string "$1" | sort | uniq) >> + fi >> + if [[ $1 =~ ":" ]]; then >> + result=$(_ovs_vsctl_complete_key_given_table_column \ >> + "$key" "$table" "$column" "$column:") >> + fi >> + # If result is empty, just use user input as result. >> + if [ -z "$result" ]; then >> + result=$1 >> + fi >> + printf -- "NOSPACE\nEO\n%s\n" "${result}" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on filename. >> +_ovs_vsctl_complete_filename () { >> + local result >> + >> + result=$(compgen -o filenames -A file "$1") >> + printf -- "EO\n%s\n" "${result}" >> +} >> + >> +_ovs_vsctl_complete_bridge_fail_mode () { >> + printf -- "EO\nstandalone\nsecure" >> +} >> + >> +# $1 = word to complete on. >> +# Complete on target. >> +_ovs_vsctl_complete_target () { >> + local result >> + >> + if [[ "$1" =~ ^p?u ]]; then >> + local protocol pathname expansion_base result >> + >> + protocol=$(cut -d':' -f1 <<< "$1") >> + pathname=$(cut -s -d':' -f2 <<< "$1") >> + expansion_base=$(compgen -W "unix punix" "$protocol") >> + expansion_base="$expansion_base:" >> + result=$(compgen -o filenames -A file \ >> + -P $expansion_base "${pathname}") >> + printf -- "NOSPACE\nEO\n%s\n" "${result}" >> + else >> + printf -- "NOSPACE\nEO\nssl:\ntcp:\nunix:\npssl:\nptcp:\npunix:" >> + fi >> +} >> + >> +# Extract PS1 prompt. >> +_ovs_vsctl_get_PS1 () { >> + >> + # Original inspiration from >> + # >> http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2, >> + # but changed quite a lot to make it more robust. >> + >> + # Make sure the PS1 used doesn't include any of the special >> + # strings used to identify the prompt >> + myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End >> prompt/' <<< "$PS1")" >> + # Export the current environment in case the prompt uses any >> + vars="$(env | cut -d'=' -f1)" >> + for var in $vars; do export $var; done >> + funcs="$(declare -F | cut -d' ' -f3)" >> + for func in $funcs; do export -f $func; done >> + # Get the prompt >> + v="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# >> Begin prompt\n# End prompt')" >> + v="${v##*# Begin prompt}" >> + printf -- "$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin >> prompt/Begin prompt/; s/\\End prompt/End prompt/')" >> + >> +} >> + >> +# Request a new value from user. Nothing to complete on. >> +_ovs_vsctl_complete_new () { >> + local two_word_type message result >> + >> + if [ ! "$1" = "--" ]; then >> + two_word_type="${2/-/ }" >> + message="\nEnter a >> ${two_word_type,,}:\n$(_ovs_vsctl_get_PS1)$COMP_LINE" >> + if [ -n "$1" ]; then >> + result="$1" >> + fi >> + printf -- "NOCOMP\nBM%sEM\nEO\n%s\n" "${message}" "${result}" >> + fi >> +} >> + >> +_ovs_vsctl_complete_dashdash () { >> + printf -- "EO\n%s\n" "--" >> +} >> + >> + >> +# These functions are given two arguments: >> +# >> +# $1 is the word being completed >> +# >> +# $2 is the type of completion --- only currently useful for the >> +# NEW-* functions. >> +# >> +# Note that the NEW-* functions actually are ``completed''; currently >> +# the completions are just used to save the fact that they have >> +# appeared for later use (i.e. implicit table calculation). >> +# >> +# The output is of the form <options>EO<completions>, where EO stands >> +# for end options. Currently available options are: >> +# - NOSPACE: Do not add a space at the end of each completion >> +# - NOCOMP: Do not complete, but store the output of the completion >> +# func in _OVS_VSCTL_PARSED_ARGS for later usage. >> +# - BM<message>EM: Print the <message> >> +declare -A _OVS_VSCTL_ARG_COMPLETION_FUNCS=( >> + ["TABLE"]=_ovs_vsctl_complete_table >> + ["RECORD"]=_ovs_vsctl_complete_record >> + ["BRIDGE"]=_ovs_vsctl_complete_bridge >> + ["PARENT"]=_ovs_vsctl_complete_bridge >> + ["PORT"]=_ovs_vsctl_complete_port >> + ["KEY"]=_ovs_vsctl_complete_key >> + ["VALUE"]=_ovs_vsctl_complete_value >> + ["ARG"]=_ovs_vsctl_complete_value >> + ["IFACE"]=_ovs_vsctl_complete_iface >> + ["SYSIFACE"]=_ovs_vsctl_complete_sysiface >> + ["COLUMN"]=_ovs_vsctl_complete_column >> + ["COLUMN?:KEY"]=_ovs_vsctl_complete_column_optkey_value >> + ["COLUMN?:KEY=VALUE"]=_ovs_vsctl_complete_column_optkey_value >> + ["KEY=VALUE"]=_ovs_vsctl_complete_key_value >> + ["?KEY=VALUE"]=_ovs_vsctl_complete_key_value >> + ["PRIVATE-KEY"]=_ovs_vsctl_complete_filename >> + ["CERTIFICATE"]=_ovs_vsctl_complete_filename >> + ["CA-CERT"]=_ovs_vsctl_complete_filename >> + ["MODE"]=_ovs_vsctl_complete_bridge_fail_mode >> + ["TARGET"]=_ovs_vsctl_complete_target >> + ["NEW-BRIDGE"]=_ovs_vsctl_complete_new >> + ["NEW-PORT"]=_ovs_vsctl_complete_new >> + ["NEW-BOND-PORT"]=_ovs_vsctl_complete_new >> + ["NEW-VLAN"]=_ovs_vsctl_complete_new >> + ["--"]=_ovs_vsctl_complete_dashdash >> +) >> + >> +# $1: Argument type, may include vertical bars to mean OR >> +# $2: Beginning of completion >> +# >> +# Note that this checks for existance in >> +# _OVS_VSCTL_ARG_COMPLETION_FUNCS; if the argument type ($1) is not >> +# there it will fail gracefully. >> +_ovs_vsctl_possible_completions_of_argument () { >> + local possible_types completions tmp >> + >> + completions="EO" >> + >> + possible_types=$(printf "%s\n" "$1" | tr '|' '\n') >> + for type in $possible_types; do >> + if [ ${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} ]; then >> + tmp=$(${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} \ >> + "$2" "${type^^}") >> + tmp_noEO="${tmp#*EO}" >> + tmp_EO="${tmp%%EO*}" >> + completions=$(printf "%s%s\n%s" "${tmp_EO}" \ >> + "${completions}" "${tmp_noEO}") >> + fi >> + done >> + printf "%s\n" "${completions}" >> +} >> + >> +# $1 = List of argument types >> +# $2 = current pointer into said list >> +# $3 = word to complete on >> +# Outputs list of possible completions >> +# The return value is the index in the cmd_args($1) list that should >> +# next be matched, if only one of them did, or 254 if there are no >> +# matches, so it doesn't know what comes next. >> +_ovs_vsctl_complete_argument() { >> + local cmd_args arg expansion index >> + >> + new=$(printf "%s\n" "$1" | grep -- '.\+') >> + readarray -t cmd_args <<< "$new"; >> + arg=${cmd_args[$2]} >> + case ${arg:0:1} in >> + !) >> + expansion=$(_ovs_vsctl_possible_completions_of_argument \ >> + "${arg:1}" $3) >> + index=$(($2+1)) >> + ;; >> + \?|\*) >> + local tmp1 tmp2 arg2_index tmp2_noEO tmp2_EO >> + tmp1=$(_ovs_vsctl_possible_completions_of_argument >> "${arg:1}" $3) >> + tmp2=$(_ovs_vsctl_complete_argument "$1" "$(($2+1))" "$3") >> + arg2_index=$? >> + if _ovs_vsctl_detect_nonzero_completions "$tmp1" \ >> + && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then >> + if [ "${arg:0:1}" = "*" ]; then >> + index=$2; >> + else >> + index=$(($2+1)); >> + fi >> + fi >> + if _ovs_vsctl_detect_nonzero_completions "$tmp1" \ >> + && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then >> + if [ "${arg:0:1}" = "*" ]; then >> + index=$2; >> + else >> + index=$(($2+1)); >> + fi >> + fi >> + if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \ >> + && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then >> + index=$arg2_index >> + fi >> + if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \ >> + && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then >> + index=254 >> + fi >> + # Don't allow secondary completions to inhibit primary >> + # completions: >> + if [[ $tmp2 =~ ^([^E]|E[^O])*NOCOMP ]]; then >> + tmp2="" >> + fi >> + tmp2_noEO="${tmp2#*EO}" >> + tmp2_EO="${tmp2%%EO*}" >> + expansion=$(printf "%s%s\n%s" "${tmp2_EO}" \ >> + "${tmp1}" "${tmp2_noEO}") >> + ;; >> + esac >> + printf "%s\n" "$expansion" >> + return $index >> +} >> + >> +_ovs_vsctl_detect_nospace () { >> + if [[ $1 =~ ^([^E]|E[^O])*NOSPACE ]]; then >> + _OVS_VSCTL_COMP_NOSPACE=true >> + fi >> +} >> + >> +_ovs_vsctl_process_messages () { >> + local message >> + >> + message="${1#*BM}" >> + message="${message%%EM*}" >> + if [ "$test" = "true" ]; then >> + printf -- "--- BEGIN MESSAGE" >> + fi >> + printf "${message}" >> + if [ "$test" = "true" ]; then >> + printf -- "--- END MESSAGE" >> + fi >> +} >> + >> +# The general strategy here is that the same functions that decide >> +# completions can also capture the necessary context for later >> +# completions. This means that there is no distinction between the >> +# processing for words that are not the current word and words that >> +# are the current word. >> +# >> +# Parsing up until the command word happens starts with everything >> +# valid; as the syntax order of ovs-vsctl is fairly strict, when types >> +# of words that preclude other words from happending can turn them >> +# off; this is controlled by valid_globals, valid_opts, and >> +# valid_commands. given_opts is used to narrow down which commands >> +# are valid based on the previously given options. >> +# >> +# After the command has been detected, the parsing becomes more >> +# complicated. The cmd_pos variable is set to 0 when the command is >> +# detected; it is used as a pointer into an array of the argument >> +# types for that given command. The argument types are stored in both >> +# cmd_args and raw_cmd as the main loop uses properties of arrays to >> +# detect certain conditions, but arrays cannot be passed to functions. >> +# To be able to deal with optional or repeatable arguments, the exit >> +# status of the function _ovs_vsctl_complete_argument represents where >> +# it has determined that the next argument will be. >> +_ovs_vsctl_bashcomp () { >> + local cur valid_globals cmd_args raw_cmd cmd_pos valid_globals >> valid_opts >> + local test="false" >> + >> + # Prepare the COMP_* variables based on input. >> + if [ "$1" = "test" ]; then >> + test="true" >> + export COMP_LINE="ovs-vsctl $2" >> + tmp="ovs-vsctl"$'\n'"$(tr ' ' '\n' <<< "${COMP_LINE}x")" >> + tmp="${tmp%x}" >> + readarray -t COMP_WORDS \ >> + <<< "$tmp" >> + export COMP_WORDS >> + export COMP_CWORD="$((${#COMP_WORDS[@]}-1))" >> + export PS1="> " >> + # This is used to make the PS1-extraction code not emit extra >> + # escape sequences; it seems like bash assumes that unknown >> + # terminal names are dumb which means this should work even in >> + # the unlikely occurence of the terminal "dumb" not existing. >> + export TERM="dumb" >> + fi >> + >> + # Extract the conf.db path. >> + db=$(sed -n 's/.*--db=\([^ ]*\).*/\1/p' <<< "$COMP_LINE") >> + if [ -n "$db" ]; then >> + _OVSDB_SERVER_LOCATION="$db" >> + fi >> + >> + # If having trouble accessing the database, return. >> + if ! _ovs_vsctl get-manager 2>/dev/null; then >> + return 1; >> + fi >> + >> + _OVS_VSCTL_PARSED_ARGS=() >> + _OVS_VSCTL_NEW_RECORDS=() >> + cmd_pos=-1 >> + cur=${COMP_WORDS[COMP_CWORD]} >> + valid_globals=true >> + valid_opts=true >> + valid_commands=true >> + given_opts="" >> + index=1 >> + export COMP_WORDBREAKS=" " >> + for word in "${COMP_WORDS[@]:1:${COMP_CWORD}} "; do >> + _OVS_VSCTL_COMP_NOSPACE=false >> + local completion >> + completion="" >> + if [ $cmd_pos -gt -1 ]; then >> + local tmp tmp_noop arg possible_newindex >> + tmp=$(_ovs_vsctl_complete_argument "$raw_cmd" "$cmd_pos" >> "$word") >> + possible_newindex=$? >> + # Check for nospace. >> + _ovs_vsctl_detect_nospace $tmp >> + # Remove all options. >> + tmp_noop="${tmp#*EO}" >> + >> + # Allow commands to specify that they should not be >> + # completed >> + if ! [[ $tmp =~ ^([^E]|E[^O])*NOCOMP ]]; then >> + # Directly assignment, since 'completion' is guaranteed >> to >> + # to be empty. >> + completion="$tmp_noop" >> + # If intermediate completion is empty, it means that the >> current >> + # argument is invalid. And we should not continue. >> + if [ $index -lt $COMP_CWORD ] \ >> + && (! _ovs_vsctl_detect_nonzero_completions >> "$completion"); then >> + _ovs_vsctl_process_messages "BM\nCannot complete >> \'${COMP_WORDS[$index]}\' at index >> ${index}:\n$(_ovs_vsctl_get_PS1)${COMP_LINE}EM\nEO\n" >> + return 1 >> + fi >> + else >> + # Only allow messages when there is no completion >> + # printout and when on the current word. >> + if [ $index -eq $COMP_CWORD ]; then >> + _ovs_vsctl_process_messages "${tmp}" >> + fi >> + # Append the new record to _OVS_VSCTL_NEW_RECORDS. >> + >> _OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]="${_OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]} >> $tmp_noop" >> + fi >> + if [[ $cmd_pos -lt ${#cmd_args} ]]; then >> + _OVS_VSCTL_PARSED_ARGS["${cmd_args[$cmd_pos]:1}"]=$word >> + fi >> + if [ $possible_newindex -lt 254 ]; then >> + cmd_pos=$possible_newindex >> + fi >> + fi >> + >> + if [ $valid_globals == true ]; then >> + tmp=$(_ovs_vsctl_bashcomp_globalopt $word) >> + _ovs_vsctl_detect_nospace $tmp >> + completion="${completion} ${tmp#*EO}" >> + fi >> + if [ $valid_opts == true ]; then >> + tmp=$(_ovs_vsctl_bashcomp_localopt "$given_opts" $word) >> + _ovs_vsctl_detect_nospace $tmp >> + completion="${completion} ${tmp#*EO}" >> + if [ $index -lt $COMP_CWORD ] \ >> + && _ovs_vsctl_detect_nonzero_completions "$tmp"; then >> + valid_globals=false >> + given_opts="${given_opts} ${word}" >> + fi >> + fi >> + if [ $valid_commands = true ]; then >> + tmp=$(_ovs_vsctl_bashcomp_command "$given_opts" $word) >> + _ovs_vsctl_detect_nospace $tmp >> + completion="${completion} ${tmp#*EO}" >> + if [ $index -lt $COMP_CWORD ] \ >> + && _ovs_vsctl_detect_nonzero_completions "$tmp"; then >> + valid_globals=false >> + valid_opts=false >> + valid_commands=false >> + cmd_pos=0 >> + raw_cmd=$(_ovs_vsctl_expand_command "$word") >> + readarray -t cmd_args <<< "$raw_cmd" >> + fi >> + fi >> + if [ "$word" = "--" ] && [ $index -lt $COMP_CWORD ]; then >> + # Empty the parsed args array. >> + _OVS_VSCTL_PARSED_AGS=() >> + cmd_pos=-1 >> + # No longer allow global options after '--'. >> + valid_globals=false >> + valid_opts=true >> + valid_commands=true >> + given_opts="" >> + fi >> + completion="$(sort -u <<< "$(tr ' ' '\n' <<< ${completion})")" >> + if [ $index -eq $COMP_CWORD ]; then >> + if [ "$test" = "true" ]; then >> + if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then >> + printf "%s" "$completion" | sed -e '/^$/d' >> + else >> + printf "%s" "$completion" | sed -e '/^$/d; s/$/ /g' >> + fi >> + printf "\n" >> + else >> + if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then >> + compopt -o nospace >> + COMPREPLY=( $(compgen -W "${completion}" -- $word) ) >> + else >> + compopt +o nospace >> + COMPREPLY=( $(compgen -W "${completion}" -- $word) ) >> + fi >> + fi >> + fi >> + index=$(($index+1)) >> + done >> +} >> + >> +if [ "$1" = "test" ]; then >> + _ovs_vsctl_bashcomp "$@" >> +else >> + complete -F _ovs_vsctl_bashcomp ovs-vsctl >> +fi >> diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c >> index 8b64245..c39c2ba 100644 >> --- a/utilities/ovs-vsctl.c >> +++ b/utilities/ovs-vsctl.c >> @@ -34,6 +34,7 @@ >> #include "fatal-signal.h" >> #include "hash.h" >> #include "json.h" >> +#include "list.h" >> #include "ovsdb-data.h" >> #include "ovsdb-idl.h" >> #include "poll-loop.h" >> @@ -63,6 +64,11 @@ struct vsctl_command_syntax { >> int min_args; /* Min number of arguments following >> name. */ >> int max_args; /* Max number of arguments following >> name. */ >> >> + /* Names that roughly describe the arguments that the command >> + * uses. These should be similar to the names displayed in the >> + * man page or in the help output. */ >> + const char *arguments; >> + >> /* If nonnull, calls ovsdb_idl_add_column() or ovsdb_idl_add_table() >> for >> * each column or table in ctx->idl that it uses. */ >> void (*prerequisites)(struct vsctl_context *ctx); >> @@ -85,6 +91,7 @@ struct vsctl_command_syntax { >> /* A comma-separated list of supported options, e.g. "--a,--b", or >> the >> * empty string if the command does not support any options. */ >> const char *options; >> + >> enum { RO, RW } mode; /* Does this command modify the >> database? */ >> }; >> >> @@ -141,6 +148,8 @@ OVS_NO_RETURN static void vsctl_exit(int status); >> OVS_NO_RETURN static void vsctl_fatal(const char *, ...) >> OVS_PRINTF_FORMAT(1, 2); >> static char *default_db(void); >> OVS_NO_RETURN static void usage(void); >> +OVS_NO_RETURN static void print_vsctl_commands(void); >> +OVS_NO_RETURN static void print_vsctl_options(const struct option >> *options); >> static void parse_options(int argc, char *argv[], struct shash >> *local_options); >> static bool might_write_to_db(char **argv); >> >> @@ -292,6 +301,8 @@ parse_options(int argc, char *argv[], struct shash >> *local_options) >> OPT_PEER_CA_CERT, >> OPT_LOCAL, >> OPT_RETRY, >> + OPT_COMMANDS, >> + OPT_OPTIONS, >> VLOG_OPTION_ENUMS, >> TABLE_OPTION_ENUMS >> }; >> @@ -304,6 +315,8 @@ parse_options(int argc, char *argv[], struct shash >> *local_options) >> {"timeout", required_argument, NULL, 't'}, >> {"retry", no_argument, NULL, OPT_RETRY}, >> {"help", no_argument, NULL, 'h'}, >> + {"commands", no_argument, NULL, OPT_COMMANDS}, >> + {"options", no_argument, NULL, OPT_OPTIONS}, >> {"version", no_argument, NULL, 'V'}, >> VLOG_LONG_OPTIONS, >> TABLE_LONG_OPTIONS, >> @@ -418,6 +431,12 @@ parse_options(int argc, char *argv[], struct shash >> *local_options) >> case 'h': >> usage(); >> >> + case OPT_COMMANDS: >> + print_vsctl_commands(); >> + >> + case OPT_OPTIONS: >> + print_vsctl_options(global_long_options); >> + >> case 'V': >> ovs_print_version(0, 0); >> printf("DB Schema %s\n", ovsrec_get_db_version()); >> @@ -729,6 +748,153 @@ Other options:\n\ >> exit(EXIT_SUCCESS); >> } >> >> +/* Converts the command arguments into format that can be parsed by >> + * bash completion script. >> + * >> + * Therein, arguments will be attached with following prefixes: >> + * >> + * !argument :: The argument is required >> + * ?argument :: The argument is optional >> + * *argument :: The argument may appear any number (0 or more) times >> + * +argument :: The argument may appear one or more times >> + * >> + */ >> +static void >> +print_command_arguments(const struct vsctl_command_syntax *command) >> +{ >> + /* >> + * The argument string is parsed in reverse. We use a stack >> 'oew_stack' >> + * to keep track of nested optionals. Whenever a ']' is >> encountered, we >> + * push an element to 'oew_stack'. The 'optional_ends_word' is set >> if >> + * the ']' is not nested. Subsequently, we pop an entry everytime >> '[' is >> + * met. >> + * >> + * We use 'whole_word_is_optional' value to decide whether or not a >> ! or >> + * + should be added on encountering a space: if the optional >> surronds >> + * the whole word then it shouldn't be, but if it is only a part of >> the >> + * word (i.e. [key=]value), it should be. >> + */ >> + struct oew_stack_element { >> + bool optional_ends_word; >> + struct ovs_list node; >> + }; >> + >> + const char *arguments = command->arguments; >> + struct ovs_list oew_stack; >> + int length = strlen(arguments); >> + char *simple_args = xzalloc(2 * length * sizeof(char)); >> + /* One char has already been written: \0 */ >> + int chars_written = 1; >> + int in_repeated = 0; >> + bool whole_word_is_optional = false; >> + int i; >> + >> + list_init(&oew_stack); >> + >> + for (i = 1; i <= length; i++) { >> + int simple_index = 2 * length - chars_written - 1; >> + char current = arguments[length-i]; >> + struct oew_stack_element *elem; >> + int oew; >> + >> + switch(current) { >> + case ']': >> + elem = xmalloc(sizeof *elem); >> + if (i == 1 >> + || arguments[length-i+1] == ' ' >> + || arguments[length-i+1] == '.') { >> + elem->optional_ends_word = true; >> + } else { >> + elem->optional_ends_word = false; >> + } >> + list_push_back(&oew_stack, &elem->node); >> + break; >> + case '[': >> + elem = CONTAINER_OF(list_pop_back(&oew_stack), >> + struct oew_stack_element, >> + node); >> + oew = elem->optional_ends_word; >> + free(elem); >> + /* Checks if the whole word is optional, and sets the >> + * 'whole_word_is_optional' accordingly. */ >> + if ((i == length || arguments[length-i-1] == ' ') >> + && oew) { >> + simple_args[simple_index] = in_repeated ? '*' : '?'; >> + whole_word_is_optional = oew; >> + } else { >> + simple_args[simple_index] = '?'; >> + whole_word_is_optional = 0; >> + } >> + chars_written++; >> + break; >> + case ' ': >> + if (!whole_word_is_optional) { >> + simple_args[simple_index] = in_repeated ? '+' : '!'; >> + chars_written++; >> + } >> + simple_args[2 * length - ++chars_written] = ' '; >> + in_repeated = 0; >> + whole_word_is_optional = false; >> + break; >> + case '.': >> + in_repeated = 1; >> + break; >> + default: >> + simple_args[simple_index] = current; >> + chars_written++; >> + } >> + } >> + if (arguments[0] != '[' && chars_written > 1) { >> + simple_args[2 * length - ++chars_written] = in_repeated ? '+' : >> '!'; >> + } >> + printf("%s", simple_args + 2 * length - chars_written); >> +} >> + >> + >> +static void >> +print_vsctl_commands(void) >> +{ >> + const struct vsctl_command_syntax *p; >> + >> + for (p = get_all_commands(); p->name; p++) { >> + char *options = xstrdup(p->options); >> + char *options_begin = options; >> + char *item; >> + >> + for (item = strsep(&options, ","); >> + item != NULL; >> + item = strsep(&options, ",")) { >> + if (strlen(item) > 0) { >> + printf("[%s] ", item); >> + } >> + } >> + printf(",%s,", p->name); >> + print_command_arguments(p); >> + printf("\n"); >> + >> + free(options_begin); >> + } >> + >> + exit(EXIT_SUCCESS); >> +} >> + >> + >> +static void >> +print_vsctl_options(const struct option *options) >> +{ >> + for (; options->name; options++) { >> + const struct option *o = options; >> + >> + printf("--%s%s\n", o->name, o->has_arg ? "=ARG" : ""); >> + if (o->flag == NULL && o->val > 0 && o->val <= UCHAR_MAX) { >> + printf("-%c%s\n", o->val, o->has_arg ? " ARG" : ""); >> + } >> + } >> + >> + exit(EXIT_SUCCESS); >> +} >> + >> + >> static char * >> default_db(void) >> { >> @@ -3545,6 +3711,7 @@ cmd_remove(struct vsctl_context *ctx) >> rm_type.n_max = UINT_MAX; >> error = ovsdb_datum_from_string(&rm, &rm_type, >> ctx->argv[i], ctx->symtab); >> + >> if (error) { >> if (ovsdb_type_is_map(&rm_type)) { >> rm_type.value.type = OVSDB_TYPE_VOID; >> @@ -4263,75 +4430,97 @@ try_again: >> >> static const struct vsctl_command_syntax all_commands[] = { >> /* Open vSwitch commands. */ >> - {"init", 0, 0, NULL, cmd_init, NULL, "", RW}, >> - {"show", 0, 0, pre_cmd_show, cmd_show, NULL, "", RO}, >> + {"init", 0, 0, "", NULL, cmd_init, NULL, "", RW}, >> + {"show", 0, 0, "", pre_cmd_show, cmd_show, NULL, "", RO}, >> >> /* Bridge commands. */ >> - {"add-br", 1, 3, pre_get_info, cmd_add_br, NULL, "--may-exist", RW}, >> - {"del-br", 1, 1, pre_get_info, cmd_del_br, NULL, "--if-exists", RW}, >> - {"list-br", 0, 0, pre_get_info, cmd_list_br, NULL, "--real,--fake", >> RO}, >> - {"br-exists", 1, 1, pre_get_info, cmd_br_exists, NULL, "", RO}, >> - {"br-to-vlan", 1, 1, pre_get_info, cmd_br_to_vlan, NULL, "", RO}, >> - {"br-to-parent", 1, 1, pre_get_info, cmd_br_to_parent, NULL, "", RO}, >> - {"br-set-external-id", 2, 3, pre_cmd_br_set_external_id, >> - cmd_br_set_external_id, NULL, "", RW}, >> - {"br-get-external-id", 1, 2, pre_cmd_br_get_external_id, >> + {"add-br", 1, 3, "NEW-BRIDGE [PARENT] [NEW-VLAN]", pre_get_info, >> + cmd_add_br, NULL, "--may-exist", RW}, >> + {"del-br", 1, 1, "BRIDGE", pre_get_info, cmd_del_br, >> + NULL, "--if-exists", RW}, >> + {"list-br", 0, 0, "", pre_get_info, cmd_list_br, NULL, >> "--real,--fake", >> + RO}, >> + {"br-exists", 1, 1, "BRIDGE", pre_get_info, cmd_br_exists, NULL, "", >> RO}, >> + {"br-to-vlan", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_vlan, NULL, >> "", >> + RO}, >> + {"br-to-parent", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_parent, >> NULL, >> + "", RO}, >> + {"br-set-external-id", 2, 3, "BRIDGE KEY [VALUE]", >> + pre_cmd_br_set_external_id, cmd_br_set_external_id, NULL, "", RW}, >> + {"br-get-external-id", 1, 2, "BRIDGE [KEY]", >> pre_cmd_br_get_external_id, >> cmd_br_get_external_id, NULL, "", RO}, >> >> /* Port commands. */ >> - {"list-ports", 1, 1, pre_get_info, cmd_list_ports, NULL, "", RO}, >> - {"add-port", 2, INT_MAX, pre_get_info, cmd_add_port, NULL, >> "--may-exist", >> - RW}, >> - {"add-bond", 4, INT_MAX, pre_get_info, cmd_add_bond, NULL, >> - "--may-exist,--fake-iface", RW}, >> - {"del-port", 1, 2, pre_get_info, cmd_del_port, NULL, >> + {"list-ports", 1, 1, "BRIDGE", pre_get_info, cmd_list_ports, NULL, >> "", >> + RO}, >> + {"add-port", 2, INT_MAX, "BRIDGE NEW-PORT [COLUMN[:KEY]=VALUE]...", >> + pre_get_info, cmd_add_port, NULL, "--may-exist", RW}, >> + {"add-bond", 4, INT_MAX, >> + "BRIDGE NEW-BOND-PORT SYSIFACE... [COLUMN[:KEY]=VALUE]...", >> pre_get_info, >> + cmd_add_bond, NULL, "--may-exist,--fake-iface", RW}, >> + {"del-port", 1, 2, "[BRIDGE] PORT|IFACE", pre_get_info, >> cmd_del_port, NULL, >> "--if-exists,--with-iface", RW}, >> - {"port-to-br", 1, 1, pre_get_info, cmd_port_to_br, NULL, "", RO}, >> + {"port-to-br", 1, 1, "PORT", pre_get_info, cmd_port_to_br, NULL, "", >> RO}, >> >> /* Interface commands. */ >> - {"list-ifaces", 1, 1, pre_get_info, cmd_list_ifaces, NULL, "", RO}, >> - {"iface-to-br", 1, 1, pre_get_info, cmd_iface_to_br, NULL, "", RO}, >> + {"list-ifaces", 1, 1, "BRIDGE", pre_get_info, cmd_list_ifaces, NULL, >> "", >> + RO}, >> + {"iface-to-br", 1, 1, "IFACE", pre_get_info, cmd_iface_to_br, NULL, >> "", >> + RO}, >> >> /* Controller commands. */ >> - {"get-controller", 1, 1, pre_controller, cmd_get_controller, NULL, >> "", RO}, >> - {"del-controller", 1, 1, pre_controller, cmd_del_controller, NULL, >> "", RW}, >> - {"set-controller", 1, INT_MAX, pre_controller, cmd_set_controller, >> NULL, >> + {"get-controller", 1, 1, "BRIDGE", pre_controller, >> cmd_get_controller, >> + NULL, "", RO}, >> + {"del-controller", 1, 1, "BRIDGE", pre_controller, >> cmd_del_controller, >> + NULL, "", RW}, >> + {"set-controller", 1, INT_MAX, "BRIDGE TARGET...", pre_controller, >> + cmd_set_controller, NULL, "", RW}, >> + {"get-fail-mode", 1, 1, "BRIDGE", pre_get_info, cmd_get_fail_mode, >> NULL, >> + "", RO}, >> + {"del-fail-mode", 1, 1, "BRIDGE", pre_get_info, cmd_del_fail_mode, >> NULL, >> "", RW}, >> - {"get-fail-mode", 1, 1, pre_get_info, cmd_get_fail_mode, NULL, "", >> RO}, >> - {"del-fail-mode", 1, 1, pre_get_info, cmd_del_fail_mode, NULL, "", >> RW}, >> - {"set-fail-mode", 2, 2, pre_get_info, cmd_set_fail_mode, NULL, "", >> RW}, >> + {"set-fail-mode", 2, 2, "BRIDGE MODE", pre_get_info, >> cmd_set_fail_mode, >> + NULL, "", RW}, >> >> /* Manager commands. */ >> - {"get-manager", 0, 0, pre_manager, cmd_get_manager, NULL, "", RO}, >> - {"del-manager", 0, 0, pre_manager, cmd_del_manager, NULL, "", RW}, >> - {"set-manager", 1, INT_MAX, pre_manager, cmd_set_manager, NULL, "", >> RW}, >> + {"get-manager", 0, 0, "", pre_manager, cmd_get_manager, NULL, "", >> RO}, >> + {"del-manager", 0, 0, "", pre_manager, cmd_del_manager, NULL, "", >> RW}, >> + {"set-manager", 1, INT_MAX, "TARGET...", pre_manager, >> cmd_set_manager, >> + NULL, "", RW}, >> >> /* SSL commands. */ >> - {"get-ssl", 0, 0, pre_cmd_get_ssl, cmd_get_ssl, NULL, "", RO}, >> - {"del-ssl", 0, 0, pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW}, >> - {"set-ssl", 3, 3, pre_cmd_set_ssl, cmd_set_ssl, NULL, "--bootstrap", >> RW}, >> + {"get-ssl", 0, 0, "", pre_cmd_get_ssl, cmd_get_ssl, NULL, "", RO}, >> + {"del-ssl", 0, 0, "", pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW}, >> + {"set-ssl", 3, 3, "PRIVATE-KEY CERTIFICATE CA-CERT", pre_cmd_set_ssl, >> + cmd_set_ssl, NULL, "--bootstrap", RW}, >> >> /* Switch commands. */ >> - {"emer-reset", 0, 0, pre_cmd_emer_reset, cmd_emer_reset, NULL, "", >> RW}, >> + {"emer-reset", 0, 0, "", pre_cmd_emer_reset, cmd_emer_reset, NULL, >> "", RW}, >> >> /* Database commands. */ >> - {"comment", 0, INT_MAX, NULL, NULL, NULL, "", RO}, >> - {"get", 2, INT_MAX, pre_cmd_get, cmd_get, NULL, "--if-exists,--id=", >> RO}, >> - {"list", 1, INT_MAX, pre_cmd_list, cmd_list, NULL, >> + {"comment", 0, INT_MAX, "[ARG]...", NULL, NULL, NULL, "", RO}, >> + {"get", 2, INT_MAX, "TABLE RECORD [COLUMN[:KEY]]...",pre_cmd_get, >> cmd_get, >> + NULL, "--if-exists,--id=", RO}, >> + {"list", 1, INT_MAX, "TABLE [RECORD]...", pre_cmd_list, cmd_list, >> NULL, >> "--if-exists,--columns=", RO}, >> - {"find", 1, INT_MAX, pre_cmd_find, cmd_find, NULL, "--columns=", RO}, >> - {"set", 3, INT_MAX, pre_cmd_set, cmd_set, NULL, "--if-exists", RW}, >> - {"add", 4, INT_MAX, pre_cmd_add, cmd_add, NULL, "--if-exists", RW}, >> - {"remove", 4, INT_MAX, pre_cmd_remove, cmd_remove, NULL, >> "--if-exists", >> - RW}, >> - {"clear", 3, INT_MAX, pre_cmd_clear, cmd_clear, NULL, "--if-exists", >> RW}, >> - {"create", 2, INT_MAX, pre_create, cmd_create, post_create, "--id=", >> RW}, >> - {"destroy", 1, INT_MAX, pre_cmd_destroy, cmd_destroy, NULL, >> - "--if-exists,--all", RW}, >> - {"wait-until", 2, INT_MAX, pre_cmd_wait_until, cmd_wait_until, NULL, >> "", >> - RO}, >> - >> - {NULL, 0, 0, NULL, NULL, NULL, NULL, RO}, >> + {"find", 1, INT_MAX, "TABLE [COLUMN[:KEY]=VALUE]...", pre_cmd_find, >> + cmd_find, NULL, "--columns=", RO}, >> + {"set", 3, INT_MAX, "TABLE RECORD COLUMN[:KEY]=VALUE...", >> pre_cmd_set, >> + cmd_set, NULL, "--if-exists", RW}, >> + {"add", 4, INT_MAX, "TABLE RECORD COLUMN [KEY=]VALUE...", >> pre_cmd_add, >> + cmd_add, NULL, "--if-exists", RW}, >> + {"remove", 4, INT_MAX, "TABLE RECORD COLUMN KEY|VALUE|KEY=VALUE...", >> + pre_cmd_remove, cmd_remove, NULL, "--if-exists", RW}, >> + {"clear", 3, INT_MAX, "TABLE RECORD COLUMN...", pre_cmd_clear, >> cmd_clear, >> + NULL, "--if-exists", RW}, >> + {"create", 2, INT_MAX, "TABLE COLUMN[:KEY]=VALUE...", pre_create, >> + cmd_create, post_create, "--id=", RW}, >> + {"destroy", 1, INT_MAX, "TABLE [RECORD]...", pre_cmd_destroy, >> cmd_destroy, >> + NULL, "--if-exists,--all", RW}, >> + {"wait-until", 2, INT_MAX, "TABLE RECORD [COLUMN[:KEY]=VALUE]...", >> + pre_cmd_wait_until, cmd_wait_until, NULL, "", RO}, >> + >> + {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO}, >> }; >> >> static const struct vsctl_command_syntax *get_all_commands(void) >> -- >> 1.7.9.5 >> >> > _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev