mirror of
https://github.com/openvswitch/ovs
synced 2025-09-06 01:05:25 +00:00
ovs-vsctl-bashcomp: Add bash command-line completion for ovs-vsctl.
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 completions and complete on user input for global/local options, sub-commands, and arguments. - 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: Peter Amidon <peter@picnicpark.org> Signed-off-by: Alex Wang <alexw@nicira.com> Acked-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -302,6 +302,7 @@ Pankaj Thakkar thakkar@nicira.com
|
|||||||
Pasi Kärkkäinen pasik@iki.fi
|
Pasi Kärkkäinen pasik@iki.fi
|
||||||
Paulo Cravero pcravero@as2594.net
|
Paulo Cravero pcravero@as2594.net
|
||||||
Pawan Shukla shuklap@vmware.com
|
Pawan Shukla shuklap@vmware.com
|
||||||
|
Peter Amidon peter@picnicpark.org
|
||||||
Peter Balland peter@nicira.com
|
Peter Balland peter@nicira.com
|
||||||
Peter Phaal peter.phaal@inmon.com
|
Peter Phaal peter.phaal@inmon.com
|
||||||
Prabina Pattnaik Prabina.Pattnaik@nechclst.in
|
Prabina Pattnaik Prabina.Pattnaik@nechclst.in
|
||||||
|
@@ -30,6 +30,7 @@ docs += utilities/ovs-command-bashcomp.INSTALL.md
|
|||||||
EXTRA_DIST += \
|
EXTRA_DIST += \
|
||||||
utilities/ovs-check-dead-ifs.in \
|
utilities/ovs-check-dead-ifs.in \
|
||||||
utilities/ovs-appctl-bashcomp.bash \
|
utilities/ovs-appctl-bashcomp.bash \
|
||||||
|
utilities/ovs-vsctl-bashcomp.bash \
|
||||||
utilities/ovs-command-bashcomp.INSTALL.md \
|
utilities/ovs-command-bashcomp.INSTALL.md \
|
||||||
utilities/ovs-ctl.in \
|
utilities/ovs-ctl.in \
|
||||||
utilities/ovs-dev.py \
|
utilities/ovs-dev.py \
|
||||||
|
775
utilities/ovs-vsctl-bashcomp.bash
Executable file
775
utilities/ovs-vsctl-bashcomp.bash
Executable file
@@ -0,0 +1,775 @@
|
|||||||
|
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.
|
||||||
|
result=$1
|
||||||
|
|
||||||
|
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 () {
|
||||||
|
if [ "$test" = "true" ]; then
|
||||||
|
printf -- "> "
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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))"
|
||||||
|
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
|
@@ -63,6 +63,11 @@ struct vsctl_command_syntax {
|
|||||||
int min_args; /* Min number of arguments following name. */
|
int min_args; /* Min number of arguments following name. */
|
||||||
int max_args; /* Max 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
|
/* If nonnull, calls ovsdb_idl_add_column() or ovsdb_idl_add_table() for
|
||||||
* each column or table in ctx->idl that it uses. */
|
* each column or table in ctx->idl that it uses. */
|
||||||
void (*prerequisites)(struct vsctl_context *ctx);
|
void (*prerequisites)(struct vsctl_context *ctx);
|
||||||
@@ -85,6 +90,7 @@ struct vsctl_command_syntax {
|
|||||||
/* A comma-separated list of supported options, e.g. "--a,--b", or the
|
/* A comma-separated list of supported options, e.g. "--a,--b", or the
|
||||||
* empty string if the command does not support any options. */
|
* empty string if the command does not support any options. */
|
||||||
const char *options;
|
const char *options;
|
||||||
|
|
||||||
enum { RO, RW } mode; /* Does this command modify the database? */
|
enum { RO, RW } mode; /* Does this command modify the database? */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -141,6 +147,8 @@ OVS_NO_RETURN static void vsctl_exit(int status);
|
|||||||
OVS_NO_RETURN static void vsctl_fatal(const char *, ...) OVS_PRINTF_FORMAT(1, 2);
|
OVS_NO_RETURN static void vsctl_fatal(const char *, ...) OVS_PRINTF_FORMAT(1, 2);
|
||||||
static char *default_db(void);
|
static char *default_db(void);
|
||||||
OVS_NO_RETURN static void usage(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 void parse_options(int argc, char *argv[], struct shash *local_options);
|
||||||
static bool might_write_to_db(char **argv);
|
static bool might_write_to_db(char **argv);
|
||||||
|
|
||||||
@@ -292,6 +300,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
|
|||||||
OPT_PEER_CA_CERT,
|
OPT_PEER_CA_CERT,
|
||||||
OPT_LOCAL,
|
OPT_LOCAL,
|
||||||
OPT_RETRY,
|
OPT_RETRY,
|
||||||
|
OPT_COMMANDS,
|
||||||
|
OPT_OPTIONS,
|
||||||
VLOG_OPTION_ENUMS,
|
VLOG_OPTION_ENUMS,
|
||||||
TABLE_OPTION_ENUMS
|
TABLE_OPTION_ENUMS
|
||||||
};
|
};
|
||||||
@@ -304,6 +314,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
|
|||||||
{"timeout", required_argument, NULL, 't'},
|
{"timeout", required_argument, NULL, 't'},
|
||||||
{"retry", no_argument, NULL, OPT_RETRY},
|
{"retry", no_argument, NULL, OPT_RETRY},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
|
{"commands", no_argument, NULL, OPT_COMMANDS},
|
||||||
|
{"options", no_argument, NULL, OPT_OPTIONS},
|
||||||
{"version", no_argument, NULL, 'V'},
|
{"version", no_argument, NULL, 'V'},
|
||||||
VLOG_LONG_OPTIONS,
|
VLOG_LONG_OPTIONS,
|
||||||
TABLE_LONG_OPTIONS,
|
TABLE_LONG_OPTIONS,
|
||||||
@@ -418,6 +430,12 @@ parse_options(int argc, char *argv[], struct shash *local_options)
|
|||||||
case 'h':
|
case 'h':
|
||||||
usage();
|
usage();
|
||||||
|
|
||||||
|
case OPT_COMMANDS:
|
||||||
|
print_vsctl_commands();
|
||||||
|
|
||||||
|
case OPT_OPTIONS:
|
||||||
|
print_vsctl_options(global_long_options);
|
||||||
|
|
||||||
case 'V':
|
case 'V':
|
||||||
ovs_print_version(0, 0);
|
ovs_print_version(0, 0);
|
||||||
printf("DB Schema %s\n", ovsrec_get_db_version());
|
printf("DB Schema %s\n", ovsrec_get_db_version());
|
||||||
@@ -734,6 +752,132 @@ Other options:\n\
|
|||||||
exit(EXIT_SUCCESS);
|
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
|
||||||
|
* a bit to 'oew_stack'. The bit is set to 1 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 surrounds 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.
|
||||||
|
*/
|
||||||
|
uint32_t oew_stack = 0;
|
||||||
|
|
||||||
|
const char *arguments = command->arguments;
|
||||||
|
int length = strlen(arguments);
|
||||||
|
if (!length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output buffer, written backward from end. */
|
||||||
|
char *output = xmalloc(2 * length);
|
||||||
|
char *outp = output + 2 * length;
|
||||||
|
*--outp = '\0';
|
||||||
|
|
||||||
|
bool in_repeated = false;
|
||||||
|
bool whole_word_is_optional = false;
|
||||||
|
|
||||||
|
for (const char *inp = arguments + length; inp > arguments; ) {
|
||||||
|
switch (*--inp) {
|
||||||
|
case ']':
|
||||||
|
oew_stack <<= 1;
|
||||||
|
if (inp[1] == '\0' || inp[1] == ' ' || inp[1] == '.') {
|
||||||
|
oew_stack |= 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
/* Checks if the whole word is optional, and sets the
|
||||||
|
* 'whole_word_is_optional' accordingly. */
|
||||||
|
if ((inp == arguments || inp[-1] == ' ') && oew_stack & 1) {
|
||||||
|
*--outp = in_repeated ? '*' : '?';
|
||||||
|
whole_word_is_optional = true;
|
||||||
|
} else {
|
||||||
|
*--outp = '?';
|
||||||
|
whole_word_is_optional = false;
|
||||||
|
}
|
||||||
|
oew_stack >>= 1;
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
if (!whole_word_is_optional) {
|
||||||
|
*--outp = in_repeated ? '+' : '!';
|
||||||
|
}
|
||||||
|
*--outp = ' ';
|
||||||
|
in_repeated = false;
|
||||||
|
whole_word_is_optional = false;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
in_repeated = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*--outp = *inp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arguments[0] != '[' && outp != output + 2 * length - 1) {
|
||||||
|
*--outp = in_repeated ? '+' : '!';
|
||||||
|
}
|
||||||
|
printf("%s", outp);
|
||||||
|
free(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (item[0] != '\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 *
|
static char *
|
||||||
default_db(void)
|
default_db(void)
|
||||||
{
|
{
|
||||||
@@ -3732,6 +3876,7 @@ cmd_remove(struct vsctl_context *ctx)
|
|||||||
rm_type.n_max = UINT_MAX;
|
rm_type.n_max = UINT_MAX;
|
||||||
error = ovsdb_datum_from_string(&rm, &rm_type,
|
error = ovsdb_datum_from_string(&rm, &rm_type,
|
||||||
ctx->argv[i], ctx->symtab);
|
ctx->argv[i], ctx->symtab);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
if (ovsdb_type_is_map(&rm_type)) {
|
if (ovsdb_type_is_map(&rm_type)) {
|
||||||
rm_type.value.type = OVSDB_TYPE_VOID;
|
rm_type.value.type = OVSDB_TYPE_VOID;
|
||||||
@@ -4448,82 +4593,125 @@ try_again:
|
|||||||
free(error);
|
free(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Developers who add new commands to the 'struct vsctl_command_syntax' must
|
||||||
|
* define the 'arguments' member of the struct. The following keywords are
|
||||||
|
* available for composing the argument format:
|
||||||
|
*
|
||||||
|
* TABLE RECORD BRIDGE PARENT PORT
|
||||||
|
* KEY VALUE ARG KEY=VALUE ?KEY=VALUE
|
||||||
|
* IFACE SYSIFACE COLUMN COLUMN?:KEY COLUMN?:KEY=VALUE
|
||||||
|
* MODE CA-CERT CERTIFICATE PRIVATE-KEY
|
||||||
|
* TARGET NEW-* (e.g. NEW-PORT)
|
||||||
|
*
|
||||||
|
* For argument types not listed above, just uses 'ARG' as place holder.
|
||||||
|
*
|
||||||
|
* Encloses the keyword with '[]' if it is optional. Appends '...' to
|
||||||
|
* keyword or enclosed keyword to indicate that the argument can be specified
|
||||||
|
* multiple times.
|
||||||
|
*
|
||||||
|
* */
|
||||||
static const struct vsctl_command_syntax all_commands[] = {
|
static const struct vsctl_command_syntax all_commands[] = {
|
||||||
/* Open vSwitch commands. */
|
/* Open vSwitch commands. */
|
||||||
{"init", 0, 0, NULL, cmd_init, NULL, "", RW},
|
{"init", 0, 0, "", NULL, cmd_init, NULL, "", RW},
|
||||||
{"show", 0, 0, pre_cmd_show, cmd_show, NULL, "", RO},
|
{"show", 0, 0, "", pre_cmd_show, cmd_show, NULL, "", RO},
|
||||||
|
|
||||||
/* Bridge commands. */
|
/* Bridge commands. */
|
||||||
{"add-br", 1, 3, pre_get_info, cmd_add_br, NULL, "--may-exist", RW},
|
{"add-br", 1, 3, "NEW-BRIDGE [PARENT] [NEW-VLAN]", pre_get_info,
|
||||||
{"del-br", 1, 1, pre_get_info, cmd_del_br, NULL, "--if-exists", RW},
|
cmd_add_br, NULL, "--may-exist", RW},
|
||||||
{"list-br", 0, 0, pre_get_info, cmd_list_br, NULL, "--real,--fake", RO},
|
{"del-br", 1, 1, "BRIDGE", pre_get_info, cmd_del_br,
|
||||||
{"br-exists", 1, 1, pre_get_info, cmd_br_exists, NULL, "", RO},
|
NULL, "--if-exists", RW},
|
||||||
{"br-to-vlan", 1, 1, pre_get_info, cmd_br_to_vlan, NULL, "", RO},
|
{"list-br", 0, 0, "", pre_get_info, cmd_list_br, NULL, "--real,--fake",
|
||||||
{"br-to-parent", 1, 1, pre_get_info, cmd_br_to_parent, NULL, "", RO},
|
RO},
|
||||||
{"br-set-external-id", 2, 3, pre_cmd_br_set_external_id,
|
{"br-exists", 1, 1, "BRIDGE", pre_get_info, cmd_br_exists, NULL, "", RO},
|
||||||
cmd_br_set_external_id, NULL, "", RW},
|
{"br-to-vlan", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_vlan, NULL, "",
|
||||||
{"br-get-external-id", 1, 2, pre_cmd_br_get_external_id,
|
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},
|
cmd_br_get_external_id, NULL, "", RO},
|
||||||
|
|
||||||
/* Port commands. */
|
/* Port commands. */
|
||||||
{"list-ports", 1, 1, pre_get_info, cmd_list_ports, NULL, "", RO},
|
{"list-ports", 1, 1, "BRIDGE", pre_get_info, cmd_list_ports, NULL, "",
|
||||||
{"add-port", 2, INT_MAX, pre_get_info, cmd_add_port, NULL, "--may-exist",
|
RO},
|
||||||
RW},
|
{"add-port", 2, INT_MAX, "BRIDGE NEW-PORT [COLUMN[:KEY]=VALUE]...",
|
||||||
{"add-bond", 4, INT_MAX, pre_get_info, cmd_add_bond, NULL,
|
pre_get_info, cmd_add_port, NULL, "--may-exist", RW},
|
||||||
"--may-exist,--fake-iface", RW},
|
{"add-bond", 4, INT_MAX,
|
||||||
{"del-port", 1, 2, pre_get_info, cmd_del_port, NULL,
|
"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},
|
"--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. */
|
/* Interface commands. */
|
||||||
{"list-ifaces", 1, 1, pre_get_info, cmd_list_ifaces, NULL, "", RO},
|
{"list-ifaces", 1, 1, "BRIDGE", pre_get_info, cmd_list_ifaces, NULL, "",
|
||||||
{"iface-to-br", 1, 1, pre_get_info, cmd_iface_to_br, NULL, "", RO},
|
RO},
|
||||||
|
{"iface-to-br", 1, 1, "IFACE", pre_get_info, cmd_iface_to_br, NULL, "",
|
||||||
/* 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,
|
|
||||||
"", 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},
|
|
||||||
|
|
||||||
/* 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},
|
|
||||||
|
|
||||||
/* 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},
|
|
||||||
|
|
||||||
/* Auto Attach commands. */
|
|
||||||
{"add-aa-mapping", 3, 3, pre_get_info, cmd_add_aa_mapping, NULL, "", RW},
|
|
||||||
{"del-aa-mapping", 3, 3, pre_aa_mapping, cmd_del_aa_mapping, NULL, "", RW},
|
|
||||||
{"get-aa-mapping", 1, 1, pre_aa_mapping, cmd_get_aa_mapping, NULL, "", RO},
|
|
||||||
|
|
||||||
/* Switch commands. */
|
|
||||||
{"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,
|
|
||||||
"--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},
|
RO},
|
||||||
|
|
||||||
{NULL, 0, 0, NULL, NULL, NULL, NULL, RO},
|
/* Controller commands. */
|
||||||
|
{"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},
|
||||||
|
{"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, "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, "PRIVATE-KEY CERTIFICATE CA-CERT", pre_cmd_set_ssl,
|
||||||
|
cmd_set_ssl, NULL, "--bootstrap", RW},
|
||||||
|
|
||||||
|
/* Auto Attach commands. */
|
||||||
|
{"add-aa-mapping", 3, 3, "BRIDGE ARG ARG", pre_get_info, cmd_add_aa_mapping,
|
||||||
|
NULL, "", RW},
|
||||||
|
{"del-aa-mapping", 3, 3, "BRIDGE ARG ARG", pre_aa_mapping, cmd_del_aa_mapping,
|
||||||
|
NULL, "", RW},
|
||||||
|
{"get-aa-mapping", 1, 1, "BRIDGE", pre_aa_mapping, cmd_get_aa_mapping,
|
||||||
|
NULL, "", RO},
|
||||||
|
|
||||||
|
/* Switch commands. */
|
||||||
|
{"emer-reset", 0, 0, "", pre_cmd_emer_reset, cmd_emer_reset, NULL, "", RW},
|
||||||
|
|
||||||
|
/* Database commands. */
|
||||||
|
{"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, "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)
|
static const struct vsctl_command_syntax *get_all_commands(void)
|
||||||
|
Reference in New Issue
Block a user