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