]> git.proxmox.com Git - mirror_ovs.git/blob - utilities/ovs-vsctl-bashcomp.bash
rhel: Add option to enable AF_XDP on rpm package.
[mirror_ovs.git] / utilities / ovs-vsctl-bashcomp.bash
1 SAVE_IFS=$IFS
2 IFS="
3 "
4 _OVSDB_SERVER_LOCATION=""
5
6 # Run ovs-vsctl and make sure that ovs-vsctl is always called with
7 # the correct --db argument.
8 _ovs_vsctl () {
9 local _db
10
11 if [ -n "$_OVSDB_SERVER_LOCATION" ]; then
12 _db="--db=$_OVSDB_SERVER_LOCATION"
13 fi
14 ovs-vsctl ${_db} "$@"
15 }
16
17 # ovs-vsctl --commands outputs in this format:
18 #
19 # main = <localopts>,<name>,<options>
20 # localopts = ([<localopt>] )*
21 # localopt = --[^]]*
22 # name = [^,]*
23 # arguments = ((!argument|?argument|*argument|+argument) )*
24 # argument = ([^ ]*|argument\|argument)
25 #
26 # The [] characters in local options are just delimiters. The
27 # argument prefixes mean:
28 # !argument :: The argument is required
29 # ?argument :: The argument is optional
30 # *argument :: The argument may appear any number (0 or more) times
31 # +argument :: The argument may appear one or more times
32 # A bar (|) character in an argument means thing before bar OR thing
33 # after bar; for example, del-port can take a port or an interface.
34
35 _OVS_VSCTL_COMMANDS="$(_ovs_vsctl --commands)"
36
37 # This doesn't complete on short arguments, so it filters them out.
38 _OVS_VSCTL_OPTIONS="$(_ovs_vsctl --options | awk '/^--/ { print $0 }' \
39 | sed -e 's/\(.*\)=ARG/\1=/')"
40 IFS=$SAVE_IFS
41
42 declare -A _OVS_VSCTL_PARSED_ARGS
43 declare -A _OVS_VSCTL_NEW_RECORDS
44
45 # This is a convenience function to make sure that user input is
46 # looked at as a fixed string when being compared to something. $1 is
47 # the input; this behaves like 'grep "^$1"' but deals with regex
48 # metacharacters in $1.
49 _ovs_vsctl_check_startswith_string () {
50 awk 'thearg == "" || index($0, thearg)==1' thearg="$1"
51 }
52
53 # $1 = word to complete on.
54 # Complete on global options.
55 _ovs_vsctl_bashcomp_globalopt () {
56 local options result
57
58 options=""
59 result=$(printf "%s\n" "${_OVS_VSCTL_OPTIONS}" \
60 | _ovs_vsctl_check_startswith_string "${1%=*}")
61 if [[ $result =~ "=" ]]; then
62 options="NOSPACE"
63 fi
64 printf -- "${options}\nEO\n${result}"
65 }
66
67 # $1 = word to complete on.
68 # Complete on local options.
69 _ovs_vsctl_bashcomp_localopt () {
70 local options result possible_opts
71
72 possible_opts=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" | cut -f1 -d',')
73 # This finds all options that could go together with the
74 # already-seen ones
75 for prefix_arg in $1; do
76 possible_opts=$(printf "%s\n" "$possible_opts" \
77 | grep -- "\[${prefix_arg%%=*}=\?\]")
78 done
79 result=$(printf "%s\n" "${possible_opts}" \
80 | tr ' ' '\n' | tr -s '\n' | sort | uniq)
81 # This removes the already-seen options from the list so that
82 # users aren't completed for the same option twice.
83 for prefix_arg in $1; do
84 result=$(printf "%s\n" "${result}" \
85 | grep -v -- "\[${prefix_arg%%=*}=\?\]")
86 done
87 result=$(printf "%s\n" "${result}" | sed -ne 's/\[\(.*\)\]/\1/p' \
88 | _ovs_vsctl_check_startswith_string "$2")
89 if [[ $result =~ "=" ]]; then
90 options="NOSPACE"
91 fi
92 printf -- "${options}\nEO\n${result}"
93 }
94
95 # $1 = given local options.
96 # $2 = word to complete on.
97 # Complete on command that could contain the given local options.
98 _ovs_vsctl_bashcomp_command () {
99 local result possible_cmds
100
101 possible_cmds=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}")
102 for prefix_arg in $1; do
103 possible_cmds=$(printf "%s\n" "$possible_cmds" \
104 | grep -- "\[$prefix_arg=\?\]")
105 done
106 result=$(printf "%s\n" "${possible_cmds}" \
107 | cut -f2 -d',' \
108 | _ovs_vsctl_check_startswith_string "$2")
109 printf -- "${result}"
110 }
111
112 # $1 = completion result to check.
113 # Return 0 if the completion result is non-empty, otherwise return 1.
114 _ovs_vsctl_detect_nonzero_completions () {
115 local tmp newarg
116
117 newarg=${1#*EO}
118 readarray tmp <<< "$newarg"
119 if [ "${#tmp[@]}" -eq 1 ] && [ "${#newarg}" -eq 0 ]; then
120 return 1
121 fi
122 return 0
123 }
124
125 # $1 = argument format to expand.
126 # Expand '+ARGUMENT' in argument format to '!ARGUMENT *ARGUMENT'.
127 _ovs_vsctl_expand_command () {
128 result=$(printf "%s\n" "${_OVS_VSCTL_COMMANDS}" \
129 | grep -- ",$1," | cut -f3 -d',' | tr ' ' '\n' \
130 | awk '/\+.*/ { name=substr($0,2);
131 print "!"name; print "*"name; next; }
132 1')
133 printf -- "${result}\n!--"
134 }
135
136 # $1 = word to complete on.
137 # Complete on table.
138 _ovs_vsctl_complete_table () {
139 local result
140
141 result=$(ovsdb-client --no-heading list-tables $_OVSDB_SERVER_LOCATION Open_vSwitch \
142 | _ovs_vsctl_check_startswith_string "$1")
143 printf -- "EO\n%s\n" "${result}"
144 }
145
146 # $1 = word to complete on.
147 # Complete on record. Provide both the name and uuid.
148 _ovs_vsctl_complete_record () {
149 local table uuids names new_record
150
151 table="${_OVS_VSCTL_PARSED_ARGS[TABLE]}"
152 new_record="${_OVS_VSCTL_NEW_RECORDS[${table^^}]}"
153 # Tables should always have an _uuid column
154 uuids=$(_ovs_vsctl --no-heading -f table -d bare --columns=_uuid \
155 list $table | _ovs_vsctl_check_startswith_string "$1")
156 # Names don't always exist, silently ignore if the name column is
157 # unavailable.
158 names=$(_ovs_vsctl --no-heading -f table -d bare \
159 --columns=name list $table \
160 2>/dev/null \
161 | _ovs_vsctl_check_startswith_string "$1")
162 printf -- "EO\n%s\n%s\n%s\n" "${uuids}" "${names}" "${new_record}"
163 }
164
165 # $1 = word to complete on.
166 # Complete on bridge.
167 _ovs_vsctl_complete_bridge () {
168 local result
169
170 result=$(_ovs_vsctl list-br | _ovs_vsctl_check_startswith_string "$1")
171 printf -- "EO\n%s\n" "${result}"
172 }
173
174 # $1 = word to complete on.
175 # Complete on port. If a bridge has already been specified,
176 # just complete for that bridge.
177 _ovs_vsctl_complete_port () {
178 local ports result
179
180 if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then
181 ports=$(_ovs_vsctl list-ports "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}")
182 else
183 local all_ports
184 all_ports=$(_ovs_vsctl --format=table \
185 --no-headings \
186 --columns=name \
187 list Port)
188 ports=$(printf "$all_ports" | tr -d '" ' | sort -u)
189 fi
190 result=$(_ovs_vsctl_check_startswith_string "$1" <<< "$ports")
191 printf -- "EO\n%s\n" "${result}"
192 }
193
194 # $1: Atom to complete (as usual)
195 # $2: Table to complete the key in
196 # $3: Column to find keys in
197 # $4: Prefix for each completion
198 # Complete on key based on given table and column info.
199 _ovs_vsctl_complete_key_given_table_column () {
200 local keys
201
202 keys=$(_ovs_vsctl --no-heading --columns="$3" list \
203 "$2" \
204 | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1 \
205 | xargs printf "$4%s\n" | _ovs_vsctl_check_startswith_string "$4$1")
206 result="${keys}"
207 printf -- "%s\n" "${result}"
208 }
209
210 # $1 = word to complete on.
211 # Complete on key.
212 __complete_key () {
213 # KEY is used in both br-set-external-id/br-get-external id (in
214 # which case it is implicitly a key in the external-id column) and
215 # in remove, where it is a table key. This checks to see if table
216 # is set (the remove scenario), and then decides what to do.
217 local result
218
219 if [ -n "${_OVS_VSCTL_PARSED_ARGS[TABLE]}" ]; then
220 local column=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["COLUMN"]})
221 result=$(_ovs_vsctl_complete_key_given_table_column \
222 "$1" \
223 ${_OVS_VSCTL_PARSED_ARGS["TABLE"]} \
224 $column \
225 "")
226 else
227 result=$(_ovs_vsctl br-get-external-id \
228 ${_OVS_VSCTL_PARSED_ARGS["BRIDGE"]} \
229 | cut -d'=' -f1 | _ovs_vsctl_check_startswith_string "$1")
230 fi
231 printf -- "%s" "${result}"
232 }
233
234 # $1 = word to complete on.
235 # Complete on key.
236 _ovs_vsctl_complete_key () {
237 # KEY is used in both br-set-external-id/br-get-external id (in
238 # which case it is implicitly a key in the external-id column) and
239 # in remove, where it is a table key. This checks to see if table
240 # is set (the remove scenario), and then decides what to do.
241 local result
242
243 result="$(__complete_key $1)"
244 # If result is empty, just use user input as result.
245 if [ -z "$result" ]; then
246 result=$1
247 fi
248 printf -- "EO\n%s\n" "${result}"
249 }
250
251 # $1 = word to complete on.
252 # Complete on value.
253 _ovs_vsctl_complete_value () {
254 local result
255
256 # Just use user input as result.
257 result=$1
258
259 printf -- "EO\n%s\n" "${result}"
260 }
261
262 # $1 = word to complete on.
263 # Complete on key=value.
264 _ovs_vsctl_complete_key_value () {
265 local orig_completions new_completions
266
267 orig_completions=$(__complete_key "$1")
268 for completion in ${orig_completions#*EO}; do
269 new_completions="${new_completions} ${completion}="
270 done
271 # If 'new_completions' is empty, just use user input as result.
272 if [ -z "$new_completions" ]; then
273 new_completions=$1
274 fi
275 printf -- "NOSPACE\nEO\n%s" "${new_completions}"
276 }
277
278 # $1 = word to complete on.
279 # Complete on column.
280 _ovs_vsctl_complete_column () {
281 local columns result
282
283 columns=$(ovsdb-client --no-headings list-columns $_OVSDB_SERVER_LOCATION \
284 Open_vSwitch ${_OVS_VSCTL_PARSED_ARGS["TABLE"]})
285 result=$(printf "%s\n" "${columns}" \
286 | tr -d ':' | cut -d' ' -f1 \
287 | _ovs_vsctl_check_startswith_string "$1" | sort | uniq)
288 printf -- "EO\n%s\n" "${result}"
289 }
290
291 # Extract all system interfaces.
292 _ovs_vsctl_get_sys_intf () {
293 local result
294
295 case "$(uname -o)" in
296 *Linux*)
297 result=$(ip -o link 2>/dev/null | cut -d':' -f2 \
298 | sed -e 's/^ \(.*\)/\1/')
299 ;;
300 *)
301 result=$(ifconfig -a -s 2>/dev/null | cut -f1 -d' ' | tail -n +2)
302 ;;
303 esac
304 printf "%s\n" "${result}"
305 }
306
307 # $1 = word to complete on.
308 # Complete on system interface.
309 _ovs_vsctl_complete_sysiface () {
310 local result
311
312 result=$(_ovs_vsctl_get_sys_intf | _ovs_vsctl_check_startswith_string "$1")
313 printf -- "EO\n%s\n" "${result}"
314 }
315
316 # $1 = word to complete on.
317 # Complete on interface. If a bridge has already been specified,
318 # just complete for that bridge.
319 _ovs_vsctl_complete_iface () {
320 local result
321
322 if [ -n "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}" ]; then
323 result=$(_ovs_vsctl list-ifaces "${_OVS_VSCTL_PARSED_ARGS[BRIDGE]}")
324 else
325 for bridge in $(_ovs_vsctl list-br); do
326 local ifaces
327
328 ifaces=$(_ovs_vsctl list-ifaces "${bridge}")
329 result="${result} ${ifaces}"
330 done
331 fi
332 printf "EO\n%s\n" "${result}"
333 }
334
335 # $1 = word to complete on.
336 # Complete on COLUMN?:KEY=VALUE.
337 _ovs_vsctl_complete_column_optkey_value () {
338 local result column key value completion
339
340 column=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -d':' -f1)
341 key=$(printf "%s\n" "$1" | cut -d '=' -f1 | cut -s -d':' -f2)
342 # The tr -d '\n' <<< makes sure that there are no leading or
343 # trailing accidental newlines.
344 table=$(tr -d '\n' <<< ${_OVS_VSCTL_PARSED_ARGS["TABLE"]})
345 # This might also be called after add-port or add-bond; in those
346 # cases, the table should implicitly be assumed to be "Port".
347 # This is done by checking if a NEW- parameter has been
348 # encountered and, if it has, using that type without the NEW- as
349 # the table.
350 if [ -z "$table" ]; then
351 if [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-PORT"]} ] \
352 || [ -n ${_OVS_VSCTL_PARSED_ARGS["NEW-BOND-PORT"]} ]; then
353 table="Port"
354 fi
355 fi
356 if [ -z "$key" ]; then
357 local columns=$(ovsdb-client --no-headings list-columns \
358 $_OVSDB_SERVER_LOCATION Open_vSwitch $table)
359
360 result=$(printf "%s\n" "${columns}" \
361 | awk '/key.*value/ { print $1":"; next }
362 { print $1; next }' \
363 | _ovs_vsctl_check_startswith_string "$1" | sort | uniq)
364 fi
365 if [[ $1 =~ ":" ]]; then
366 result=$(_ovs_vsctl_complete_key_given_table_column \
367 "$key" "$table" "$column" "$column:")
368 fi
369 # If result is empty, just use user input as result.
370 if [ -z "$result" ]; then
371 result=$1
372 fi
373 printf -- "NOSPACE\nEO\n%s\n" "${result}"
374 }
375
376 # $1 = word to complete on.
377 # Complete on filename.
378 _ovs_vsctl_complete_filename () {
379 local result
380
381 result=$(compgen -o filenames -A file "$1")
382 printf -- "EO\n%s\n" "${result}"
383 }
384
385 _ovs_vsctl_complete_bridge_fail_mode () {
386 printf -- "EO\nstandalone\nsecure"
387 }
388
389 # $1 = word to complete on.
390 # Complete on target.
391 _ovs_vsctl_complete_target () {
392 local result
393
394 if [[ "$1" =~ ^p?u ]]; then
395 local protocol pathname expansion_base result
396
397 protocol=$(cut -d':' -f1 <<< "$1")
398 pathname=$(cut -s -d':' -f2 <<< "$1")
399 expansion_base=$(compgen -W "unix punix" "$protocol")
400 expansion_base="$expansion_base:"
401 result=$(compgen -o filenames -A file \
402 -P $expansion_base "${pathname}")
403 printf -- "NOSPACE\nEO\n%s\n" "${result}"
404 else
405 printf -- "NOSPACE\nEO\nssl:\ntcp:\nunix:\npssl:\nptcp:\npunix:"
406 fi
407 }
408
409 # Extract PS1 prompt.
410 _ovs_vsctl_get_PS1 () {
411 if [ "$test" = "true" ]; then
412 printf -- "> "
413 return;
414 fi
415
416 # Original inspiration from
417 # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2,
418 # but changed quite a lot to make it more robust.
419
420 # Make sure the PS1 used doesn't include any of the special
421 # strings used to identify the prompt
422 myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")"
423 # Export the current environment in case the prompt uses any
424 vars="$(env | cut -d'=' -f1)"
425 for var in $vars; do export $var; done
426 funcs="$(declare -F | cut -d' ' -f3)"
427 for func in $funcs; do export -f $func; done
428 # Get the prompt
429 v="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# Begin prompt\n# End prompt')"
430 v="${v##*# Begin prompt}"
431 printf -- "$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin prompt/Begin prompt/; s/\\End prompt/End prompt/')"
432
433 }
434
435 # Request a new value from user. Nothing to complete on.
436 _ovs_vsctl_complete_new () {
437 local two_word_type message result
438
439 if [ ! "$1" = "--" ]; then
440 two_word_type="${2/-/ }"
441 message="\nEnter a ${two_word_type,,}:\n$(_ovs_vsctl_get_PS1)$COMP_LINE"
442 if [ -n "$1" ]; then
443 result="$1"
444 fi
445 printf -- "NOCOMP\nBM%sEM\nEO\n%s\n" "${message}" "${result}"
446 fi
447 }
448
449 _ovs_vsctl_complete_dashdash () {
450 printf -- "EO\n%s\n" "--"
451 }
452
453
454 # These functions are given two arguments:
455 #
456 # $1 is the word being completed
457 #
458 # $2 is the type of completion --- only currently useful for the
459 # NEW-* functions.
460 #
461 # Note that the NEW-* functions actually are ``completed''; currently
462 # the completions are just used to save the fact that they have
463 # appeared for later use (i.e. implicit table calculation).
464 #
465 # The output is of the form <options>EO<completions>, where EO stands
466 # for end options. Currently available options are:
467 # - NOSPACE: Do not add a space at the end of each completion
468 # - NOCOMP: Do not complete, but store the output of the completion
469 # func in _OVS_VSCTL_PARSED_ARGS for later usage.
470 # - BM<message>EM: Print the <message>
471 declare -A _OVS_VSCTL_ARG_COMPLETION_FUNCS=(
472 ["TABLE"]=_ovs_vsctl_complete_table
473 ["RECORD"]=_ovs_vsctl_complete_record
474 ["BRIDGE"]=_ovs_vsctl_complete_bridge
475 ["PARENT"]=_ovs_vsctl_complete_bridge
476 ["PORT"]=_ovs_vsctl_complete_port
477 ["KEY"]=_ovs_vsctl_complete_key
478 ["VALUE"]=_ovs_vsctl_complete_value
479 ["ARG"]=_ovs_vsctl_complete_value
480 ["IFACE"]=_ovs_vsctl_complete_iface
481 ["SYSIFACE"]=_ovs_vsctl_complete_sysiface
482 ["COLUMN"]=_ovs_vsctl_complete_column
483 ["COLUMN?:KEY"]=_ovs_vsctl_complete_column_optkey_value
484 ["COLUMN?:KEY=VALUE"]=_ovs_vsctl_complete_column_optkey_value
485 ["KEY=VALUE"]=_ovs_vsctl_complete_key_value
486 ["?KEY=VALUE"]=_ovs_vsctl_complete_key_value
487 ["PRIVATE-KEY"]=_ovs_vsctl_complete_filename
488 ["CERTIFICATE"]=_ovs_vsctl_complete_filename
489 ["CA-CERT"]=_ovs_vsctl_complete_filename
490 ["MODE"]=_ovs_vsctl_complete_bridge_fail_mode
491 ["TARGET"]=_ovs_vsctl_complete_target
492 ["NEW-BRIDGE"]=_ovs_vsctl_complete_new
493 ["NEW-PORT"]=_ovs_vsctl_complete_new
494 ["NEW-BOND-PORT"]=_ovs_vsctl_complete_new
495 ["NEW-VLAN"]=_ovs_vsctl_complete_new
496 ["--"]=_ovs_vsctl_complete_dashdash
497 )
498
499 # $1: Argument type, may include vertical bars to mean OR
500 # $2: Beginning of completion
501 #
502 # Note that this checks for existance in
503 # _OVS_VSCTL_ARG_COMPLETION_FUNCS; if the argument type ($1) is not
504 # there it will fail gracefully.
505 _ovs_vsctl_possible_completions_of_argument () {
506 local possible_types completions tmp
507
508 completions="EO"
509
510 possible_types=$(printf "%s\n" "$1" | tr '|' '\n')
511 for type in $possible_types; do
512 if [ ${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} ]; then
513 tmp=$(${_OVS_VSCTL_ARG_COMPLETION_FUNCS["${type^^}"]} \
514 "$2" "${type^^}")
515 tmp_noEO="${tmp#*EO}"
516 tmp_EO="${tmp%%EO*}"
517 completions=$(printf "%s%s\n%s" "${tmp_EO}" \
518 "${completions}" "${tmp_noEO}")
519 fi
520 done
521 printf "%s\n" "${completions}"
522 }
523
524 # $1 = List of argument types
525 # $2 = current pointer into said list
526 # $3 = word to complete on
527 # Outputs list of possible completions
528 # The return value is the index in the cmd_args($1) list that should
529 # next be matched, if only one of them did, or 254 if there are no
530 # matches, so it doesn't know what comes next.
531 _ovs_vsctl_complete_argument() {
532 local cmd_args arg expansion index
533
534 new=$(printf "%s\n" "$1" | grep -- '.\+')
535 readarray -t cmd_args <<< "$new";
536 arg=${cmd_args[$2]}
537 case ${arg:0:1} in
538 !)
539 expansion=$(_ovs_vsctl_possible_completions_of_argument \
540 "${arg:1}" $3)
541 index=$(($2+1))
542 ;;
543 \?|\*)
544 local tmp1 tmp2 arg2_index tmp2_noEO tmp2_EO
545 tmp1=$(_ovs_vsctl_possible_completions_of_argument "${arg:1}" $3)
546 tmp2=$(_ovs_vsctl_complete_argument "$1" "$(($2+1))" "$3")
547 arg2_index=$?
548 if _ovs_vsctl_detect_nonzero_completions "$tmp1" \
549 && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then
550 if [ "${arg:0:1}" = "*" ]; then
551 index=$2;
552 else
553 index=$(($2+1));
554 fi
555 fi
556 if _ovs_vsctl_detect_nonzero_completions "$tmp1" \
557 && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then
558 if [ "${arg:0:1}" = "*" ]; then
559 index=$2;
560 else
561 index=$(($2+1));
562 fi
563 fi
564 if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \
565 && _ovs_vsctl_detect_nonzero_completions "$tmp2"; then
566 index=$arg2_index
567 fi
568 if (! _ovs_vsctl_detect_nonzero_completions "$tmp1") \
569 && (! _ovs_vsctl_detect_nonzero_completions "$tmp2"); then
570 index=254
571 fi
572 # Don't allow secondary completions to inhibit primary
573 # completions:
574 if [[ $tmp2 =~ ^([^E]|E[^O])*NOCOMP ]]; then
575 tmp2=""
576 fi
577 tmp2_noEO="${tmp2#*EO}"
578 tmp2_EO="${tmp2%%EO*}"
579 expansion=$(printf "%s%s\n%s" "${tmp2_EO}" \
580 "${tmp1}" "${tmp2_noEO}")
581 ;;
582 esac
583 printf "%s\n" "$expansion"
584 return $index
585 }
586
587 _ovs_vsctl_detect_nospace () {
588 if [[ $1 =~ ^([^E]|E[^O])*NOSPACE ]]; then
589 _OVS_VSCTL_COMP_NOSPACE=true
590 fi
591 }
592
593 _ovs_vsctl_process_messages () {
594 local message
595
596 message="${1#*BM}"
597 message="${message%%EM*}"
598 if [ "$test" = "true" ]; then
599 printf -- "--- BEGIN MESSAGE"
600 fi
601 printf "${message}"
602 if [ "$test" = "true" ]; then
603 printf -- "--- END MESSAGE"
604 fi
605 }
606
607 # colon, equal sign will mess up the completion output, just
608 # removes the colon-word and equal-word prefix from COMPREPLY items.
609 #
610 # Implementation of this function refers to the __ltrim_colon_completions
611 # function defined in bash_completion module.
612 #
613 # $1: Current argument
614 # $2: $COMP_WORDBREAKS
615 # $3: ${COMPREPLY[@]}
616 _ovs_vsctl_trim_compreply() {
617 local cur comp_wordbreaks
618 local compreply
619
620 cur=$1 && shift
621 comp_wordbreaks=$1 && shift
622 compreply=( $@ )
623
624 if [[ "$cur" == *:* && "$comp_wordbreaks" == *:* ]]; then
625 local colon_word=${cur%${cur##*:}}
626 local i=${#compreply[*]}
627 cur=${cur##*:}
628 while [ $((--i)) -ge 0 ]; do
629 compreply[$i]=${compreply[$i]#"$colon_word"}
630 done
631 fi
632
633 if [[ "$cur" == *=* && "$comp_wordbreaks" == *=* ]]; then
634 local equal_word=${cur%${cur##*=}}
635 local i=${#compreply[*]}
636 while [ $((--i)) -ge 0 ]; do
637 compreply[$i]=${compreply[$i]#"$equal_word"}
638 done
639 fi
640
641 printf "%s " "${compreply[@]}"
642 }
643
644 # The general strategy here is that the same functions that decide
645 # completions can also capture the necessary context for later
646 # completions. This means that there is no distinction between the
647 # processing for words that are not the current word and words that
648 # are the current word.
649 #
650 # Parsing up until the command word happens starts with everything
651 # valid; as the syntax order of ovs-vsctl is fairly strict, when types
652 # of words that preclude other words from happending can turn them
653 # off; this is controlled by valid_globals, valid_opts, and
654 # valid_commands. given_opts is used to narrow down which commands
655 # are valid based on the previously given options.
656 #
657 # After the command has been detected, the parsing becomes more
658 # complicated. The cmd_pos variable is set to 0 when the command is
659 # detected; it is used as a pointer into an array of the argument
660 # types for that given command. The argument types are stored in both
661 # cmd_args and raw_cmd as the main loop uses properties of arrays to
662 # detect certain conditions, but arrays cannot be passed to functions.
663 # To be able to deal with optional or repeatable arguments, the exit
664 # status of the function _ovs_vsctl_complete_argument represents where
665 # it has determined that the next argument will be.
666 _ovs_vsctl_bashcomp () {
667 local words cword valid_globals cmd_args raw_cmd cmd_pos valid_globals valid_opts
668 local test="false"
669
670 # Does not support BASH_VERSION < 4.0
671 if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
672 return 0
673 fi
674
675 # Prepare the COMP_* variables based on input.
676 if [ "$1" = "test" ]; then
677 test="true"
678 export COMP_LINE="ovs-vsctl $2"
679 tmp="ovs-vsctl"$'\n'"$(tr ' ' '\n' <<< "${COMP_LINE}x")"
680 tmp="${tmp%x}"
681 readarray -t COMP_WORDS \
682 <<< "$tmp"
683 export COMP_WORDS
684 export COMP_CWORD="$((${#COMP_WORDS[@]}-1))"
685 else
686 # If not in test mode, reassembles the COMP_WORDS and COMP_CWORD
687 # using just space as word break.
688 _get_comp_words_by_ref -n "\"'><=;|&(:" -w words -i cword
689 COMP_WORDS=( "${words[@]}" )
690 COMP_CWORD=${cword}
691 fi
692
693 # Extract the conf.db path.
694 db=$(sed -n 's/.*--db=\([^ ]*\).*/\1/p' <<< "$COMP_LINE")
695 if [ -n "$db" ]; then
696 _OVSDB_SERVER_LOCATION="$db"
697 fi
698
699 # If having trouble accessing the database, return.
700 if ! _ovs_vsctl get-manager 1>/dev/null 2>/dev/null; then
701 return 1;
702 fi
703
704 _OVS_VSCTL_PARSED_ARGS=()
705 _OVS_VSCTL_NEW_RECORDS=()
706 cmd_pos=-1
707 valid_globals=true
708 valid_opts=true
709 valid_commands=true
710 given_opts=""
711 index=1
712 for word in "${COMP_WORDS[@]:1:${COMP_CWORD}} "; do
713 _OVS_VSCTL_COMP_NOSPACE=false
714 local completion
715 completion=""
716 if [ $cmd_pos -gt -1 ]; then
717 local tmp tmp_noop arg possible_newindex
718 tmp=$(_ovs_vsctl_complete_argument "$raw_cmd" "$cmd_pos" "$word")
719 possible_newindex=$?
720 # Check for nospace.
721 _ovs_vsctl_detect_nospace $tmp
722 # Remove all options.
723 tmp_noop="${tmp#*EO}"
724
725 # Allow commands to specify that they should not be
726 # completed
727 if ! [[ $tmp =~ ^([^E]|E[^O])*NOCOMP ]]; then
728 # Directly assignment, since 'completion' is guaranteed to
729 # to be empty.
730 completion="$tmp_noop"
731 # If intermediate completion is empty, it means that the current
732 # argument is invalid. And we should not continue.
733 if [ $index -lt $COMP_CWORD ] \
734 && (! _ovs_vsctl_detect_nonzero_completions "$completion"); then
735 _ovs_vsctl_process_messages "BM\nCannot complete \'${COMP_WORDS[$index]}\' at index ${index}:\n$(_ovs_vsctl_get_PS1)${COMP_LINE}EM\nEO\n"
736 return 1
737 fi
738 else
739 # Only allow messages when there is no completion
740 # printout and when on the current word.
741 if [ $index -eq $COMP_CWORD ]; then
742 _ovs_vsctl_process_messages "${tmp}"
743 fi
744 # Append the new record to _OVS_VSCTL_NEW_RECORDS.
745 _OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]="${_OVS_VSCTL_NEW_RECORDS["${cmd_args[$cmd_pos]##*-}"]} $tmp_noop"
746 fi
747 if [[ $cmd_pos -lt ${#cmd_args} ]]; then
748 _OVS_VSCTL_PARSED_ARGS["${cmd_args[$cmd_pos]:1}"]=$word
749 fi
750 if [ $possible_newindex -lt 254 ]; then
751 cmd_pos=$possible_newindex
752 fi
753 fi
754
755 if [ $valid_globals == true ]; then
756 tmp=$(_ovs_vsctl_bashcomp_globalopt $word)
757 _ovs_vsctl_detect_nospace $tmp
758 completion="${completion} ${tmp#*EO}"
759 fi
760 if [ $valid_opts == true ]; then
761 tmp=$(_ovs_vsctl_bashcomp_localopt "$given_opts" $word)
762 _ovs_vsctl_detect_nospace $tmp
763 completion="${completion} ${tmp#*EO}"
764 if [ $index -lt $COMP_CWORD ] \
765 && _ovs_vsctl_detect_nonzero_completions "$tmp"; then
766 valid_globals=false
767 given_opts="${given_opts} ${word}"
768 fi
769 fi
770 if [ $valid_commands = true ]; then
771 tmp=$(_ovs_vsctl_bashcomp_command "$given_opts" $word)
772 _ovs_vsctl_detect_nospace $tmp
773 completion="${completion} ${tmp#*EO}"
774 if [ $index -lt $COMP_CWORD ] \
775 && _ovs_vsctl_detect_nonzero_completions "$tmp"; then
776 valid_globals=false
777 valid_opts=false
778 valid_commands=false
779 cmd_pos=0
780 raw_cmd=$(_ovs_vsctl_expand_command "$word")
781 readarray -t cmd_args <<< "$raw_cmd"
782 fi
783 fi
784 if [ "$word" = "--" ] && [ $index -lt $COMP_CWORD ]; then
785 # Empty the parsed args array.
786 _OVS_VSCTL_PARSED_AGS=()
787 cmd_pos=-1
788 # No longer allow global options after '--'.
789 valid_globals=false
790 valid_opts=true
791 valid_commands=true
792 given_opts=""
793 fi
794 completion="$(sort -u <<< "$(tr ' ' '\n' <<< ${completion})")"
795 if [ $index -eq $COMP_CWORD ]; then
796 if [ "$test" = "true" ]; then
797 completion="$(_ovs_vsctl_trim_compreply "$word" ":=" ${completion} | \
798 tr ' ' '\n')"
799 if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then
800 printf "%s" "$completion" | sed -e '/^$/d'
801 else
802 printf "%s" "$completion" | sed -e '/^$/d; s/$/ /g'
803 fi
804 printf "\n"
805 else
806 if [ "${_OVS_VSCTL_COMP_NOSPACE}" = "true" ]; then
807 compopt -o nospace
808 COMPREPLY=( $(compgen -W "${completion}" -- $word) )
809 else
810 compopt +o nospace
811 COMPREPLY=( $(compgen -W "${completion}" -- $word) )
812 fi
813 COMPREPLY=( $(_ovs_vsctl_trim_compreply "$word" \
814 "${COMP_WORDBREAKS}" ${COMPREPLY[@]}) )
815 fi
816 fi
817 index=$(($index+1))
818 done
819 }
820
821 if [ "$1" = "test" ]; then
822 _ovs_vsctl_bashcomp "$@"
823 else
824 complete -F _ovs_vsctl_bashcomp ovs-vsctl
825 fi