# HG changeset patch # User Zheng Li # Date 1272953903 -3600 # Node ID 7d0fb4d42e659d81f23af7d031df8f4916efac35 # Parent ad5ea8e64ad268ea8a99ca1bda96a962a04e348a Add remote server support for xe's bash completion feature According to the manual: "... When executing commands on a remote Xen Cloud Platform host, tab completion does not normally work. However if you put the server, username, and password in an environment variable called XE_EXTRA_ARGS on the machine from which you are entering the commands, tab completion is enabled. See the section called Basic xe syntax for details. ..." However, when dealing with a large number of machines, checking/updating RC file or environment variable at every single call sounds like a ugly way. This patch adds the remote server (and other config options) support to xe's bash completion functionality. It enables tab completion when xe calls a remote server whose config can be set by command line options in "-o xxx" format or "option=xxx" format or the mix of both. The patch should work in general according to my limited tests, but please bear with my poor bash skills and feel free to improve. A few other minor bugs were also fixed during the process. Signed-off-by: Zheng Li diff -r ad5ea8e64ad2 -r 7d0fb4d42e65 ocaml/xe-cli/bash-completion --- a/ocaml/xe-cli/bash-completion Tue May 04 06:22:08 2010 +0100 +++ b/ocaml/xe-cli/bash-completion Tue May 04 07:18:23 2010 +0100 @@ -6,24 +6,76 @@ _xe() { - local IFS=$'\n,' - local cur prev opts xe IFS + local cur prev opts xe IFS oIFS cIFS ABORT_FLAG INGORE_FLAG SKIP_FLAG SKIP_OPTS SKIP_CONF COMP_OPWORD + + oIFS=$' \t\n' + cIFS=$'\n,' + + # The current script doesn't seem to have taken the compat mode into consideration. Using the standard + # mode instead is possible but will obviously return misleading completions, while forcing to use compat + # mode anyway will return message of unexpected format in most case hence become garbage. Considering the + # rase usage of compat mode for interactive command (mostly as old script), we simply disable it here. + ABORT_FLAG=( '-help' '--help' '--compat') + IGNORE_FLAG=( '--debug' '--debug-on-fail' ) + SKIP_FLAG=( '--nossl' ) + SKIP_OPTS=( '-s' '-p' '-u' '-pw' '-pwf' '-h' ) + ABORT_CONF=( 'compat=' ) + IGNORE_CONF=( 'debug=' 'debugonfail=' ) + SKIP_CONF=( 'server=' 'port=' 'username=' 'password=' 'passwordfile=' 'nossl=' ) + + COMP_OPWORD=-1 + i=1 + skip=0 + while [ $COMP_OPWORD -lt 0 ] && [ $i -lt $COMP_CWORD ]; do + cont=0 + for f in ${ABORT_FLAG[@]}; do if [[ $f == ${COMP_WORDS[i]} ]]; then return 0; fi; done + for f in ${IGNORE_FLAG[@]}; do if [[ $f == ${COMP_WORDS[i]} ]]; then unset COMP_WORDS[$i]; cont=1; let skip=$skip+1; break; fi; done + if [ $cont -gt 0 ]; then let i=$i+$cont; continue; fi + for f in ${SKIP_FLAG[@]}; do if [[ $f == ${COMP_WORDS[i]} ]]; then cont=1; break; fi; done + if [ $cont -gt 0 ]; then let i=$i+$cont; continue; fi + for f in ${SKIP_OPTS[@]}; do if [[ $f == ${COMP_WORDS[i]} ]]; then cont=2; break; fi; done + if [ $cont -gt 0 ]; then let i=$i+$cont; continue; fi + for f in ${ABORT_CONF[@]}; do if [[ $f == ${COMP_WORDS[i]:0:${#f}} ]]; then return 0; fi; done + for f in ${IGNORE_CONF[@]}; do if [[ $f == ${COMP_WORDS[i]:0:${#f}} ]]; then unset COMP_WORDS[$i]; cont=1; let skip=$skip+1; break; fi; done + if [ $cont -gt 0 ]; then let i=$i+$cont; continue; fi + for f in ${SKIP_CONF[@]}; do if [[ $f == ${COMP_WORDS[i]:0:${#f}} ]]; then cont=1; break; fi; done + if [ $cont -gt 0 ]; then let i=$i+$cont; continue; fi + COMP_OPWORD=$i + done + COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" - xe=xe - - if [[ $COMP_CWORD == 1 ]] ; then - opts=`${xe} help --minimal --all 2>/dev/null | sed -e 's/,/\ ,/g' -e 's/$/\ /g'` && COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + + optargs=`echo "${ABORT_FLAG[@]} ${IGNORE_FLAG[@]} ${SKIP_FLAG[@]} ${SKIP_OPTS[@]}" | sed -e 's/\ /\ ,/g'` + eqargs=`echo "${ABORT_CONF[@]} ${IGNORE_CONF[@]} ${SKIP_CONF[@]}" | sed -e 's/\ /,/g'` + opts="$optargs ,$eqargs" + + + if [[ $COMP_OPWORD -lt 0 ]]; then + xe="${COMP_WORDS[@]:0:$COMP_CWORD-$skip}" + else + xe="${COMP_WORDS[@]:0:$COMP_OPWORD-$skip}" + fi + + if [[ $COMP_OPWORD -lt 0 ]]; then + for f in ${SKIP_OPTS[@]}; do if [[ $f == ${COMP_WORDS[COMP_CWORD-1]} ]]; then return 0; fi; done + IFS=$oIFS + cmdargs=`${xe} help --minimal --all 2>/dev/null | sed -e 's/,\|&/\ ,/g'` + IFS=$cIFS + opts="$opts,$cmdargs" + COMPREPLY=( $(compgen -W "${opts}" -X ${cur} -- ${cur}) ) + IFS=$oIFS return 0 fi - -# parameters are passed as param=value + + # parameters are passed as param=value + if echo ${COMP_WORDS[COMP_CWORD]} | grep "=" > /dev/null; then local param value - local IFS="" + IFS="" param=`echo ${COMP_WORDS[COMP_CWORD]} | cut -d= -f1` value=`echo ${COMP_WORDS[COMP_CWORD]} | cut -d= -f2` @@ -31,202 +83,220 @@ case "$param" in filename|file-name|license-file) - IFS=$'\n,' + IFS=$cIFS COMPREPLY=( $(compgen -f ${value}) ) return 0 ;; mode) # for pif-reconfigure-ip - if [ "${COMP_WORDS[1]}" == "pif-reconfigure-ip" ]; then - IFS=$'\n,' + if [ "${COMP_WORDS[COMP_OPWORD]}" == "pif-reconfigure-ip" ]; then + IFS=$cIFS COMPREPLY=( $(compgen -W "dhcp ,static ,none" -- ${value}) ) fi return 0 ;; - uuid) - case "${COMP_WORDS[1]}" in + uuid) + case "${COMP_WORDS[COMP_OPWORD]}" in diagnostic-vm-status) cmd=vm-list;; diagnostic-vdi-status) cmd=vdi-list;; - *) cmd=`echo ${COMP_WORDS[1]} | awk -F- '/^host-cpu-/ || /^host-crashdump-/ { print $1 "-" $2 } $0 !~ /^host-cpu-/ && $0 !~ /^host-crashdump-/ { print $1 }'`-list;; + *) cmd=`echo ${COMP_WORDS[COMP_OPWORD]} | awk -F- '/^host-cpu-/ || /^host-crashdump-/ { print $1 "-" $2 } $0 !~ /^host-cpu-/ && $0 !~ /^host-crashdump-/ { print $1 }'`-list;; esac - IFS=$'\n,' + IFS=$cIFS COMPREPLY=( $(compgen_names "$cmd" uuid "$value") ) return 1 ;; vm) - IFS=$'\n,' - COMPREPLY=( $(compgen_names vm-list name-label "$value") ) + IFS=$cIFS + COMPREPLY=( $(compgen_names vm-list name-label "$value") ) return 0 ;; host) - IFS=$'\n,' + IFS=$cIFS COMPREPLY=( $(compgen_names host-list name-label "$value") ) return 0 ;; params) - val=$(final_comma_separated_param "$value") - class=`echo ${COMP_WORDS[1]} | cut -d- -f1` - params=`${xe} ${class}-list params=all 2>/dev/null| cut -d: -f1 | sed -e s/\(.*\)//g -e s/^\ *//g -e s/\ *$//g` - IFS=$'\n,' + val=$(final_comma_separated_param "$value") + class=`echo ${COMP_WORDS[COMP_OPWORD]} | cut -d- -f1` + IFS=$oIFS + params=`${xe} ${class}-list params=all 2>/dev/null | cut -d: -f1 | sed -e s/\(.*\)//g -e s/^\ *//g -e s/\ *$//g` + IFS=$cIFS COMPREPLY=( $(compgen -W "$params,all" -- "$val" ) ) return 0 ;; template) - IFS=$'\n,' - COMPREPLY=( $(compgen_names template-list name-label "$value") ) + IFS=$cIFS + COMPREPLY=( $(compgen_names template-list name-label "$value") ) return 0 ;; # param name is used by *-param-add param-name) - if echo ${COMP_WORDS[1]} | grep "param-add" > /dev/null; then - class=`echo ${COMP_WORDS[1]} | sed s/-param-add//g` + if echo ${COMP_WORDS[COMP_OPWORD]} | grep "param-add" > /dev/null; then + class=`echo ${COMP_WORDS[COMP_OPWORD]} | sed s/-param-add//g` + IFS=$oIFS paramsset=`${xe} ${class}-list params=all 2>/dev/null | grep "SRW\|MRW" | cut -d\( -f 1 | cut -d: -f1 | sed s/\ *//` - IFS=$'\n,' COMPREPLY=( $(compgen -W "${paramsset}" -- ${value}) ) + IFS=$cIFS + COMPREPLY=( $(compgen -W "${paramsset}" -- ${value}) ) return 0 fi ;; cd-name) - if [[ "${COMP_WORDS[1]}" == "vm-cd-add" || "${COMP_WORDS[1]}" == "vm-cd-insert" ]]; then - IFS=$'\n,' - COMPREPLY=( $(compgen_names cd-list name-label "$value") ) + if [[ "${COMP_WORDS[COMP_OPWORD]}" == "vm-cd-add" || "${COMP_WORDS[COMP_OPWORD]}" == "vm-cd-insert" ]]; then + IFS=$cIFS + COMPREPLY=( $(compgen_names cd-list name-label "$value") ) return 0 - elif [[ "${COMP_WORDS[1]}" == "vm-cd-remove" ]]; then - vm=`for i in ${COMP_WORDS[@]:2}; do echo $i | grep "^vm="; done` + elif [[ "${COMP_WORDS[COMP_OPWORD]}" == "vm-cd-remove" ]]; then + vm=`for i in ${COMP_WORDS[@]:2}; do echo $i | grep "^vm="; done` + IFS=$oIFS local cds=`${xe} vm-cd-list "$vm" --minimal --multiple vbd-params=vdi-name-label vdi-params=none 2>/dev/null | sed -e "s,',$MAGIC_SQUOTE,g" -e "s,\",$MAGIC_DQUOTE,g"` - IFS=$'\n,' - COMPREPLY=( $(compgen_escape "$cds" "$value") ) + IFS=$cIFS + COMPREPLY=( $(compgen_escape "$cds" "$value") ) return 0 fi ;; on) - IFS=$'\n,' - COMPREPLY=( $(compgen_names host-list name-label "$value") ) + IFS=$cIFS + COMPREPLY=( $(compgen_names host-list name-label "$value") ) return 0 ;; key) + IFS=$oIFS local keys=`${xe} log-get-keys --minimal 2>/dev/null` - IFS=$'\n,' - COMPREPLY=( $(compgen_escape "$keys" "$value") ) + IFS=$cIFS + COMPREPLY=( $(compgen_escape "$keys" "$value") ) return 0 ;; level) - IFS=$'\n,' + IFS=$cIFS COMPREPLY=( $(compgen -W "debug ,info ,warning ,error " -- ${value}) ) return 0 ;; sr-name-label) # for vm-install - IFS=$'\n,' + IFS=$cIFS COMPREPLY=( $(compgen_names sr-list name-label "$value") ) return 0 ;; crash-dump-SR | suspend-image-SR | default-SR) - IFS=$'\n,' + IFS=$cIFS COMPREPLY=( $(compgen_names sr-list uuid "$value") ) return 0 ;; type) # for vbd-create/vdi-create/sr-create/sr-probe - IFS=$'\n,' - fst=`echo ${COMP_WORDS[1]} | cut -d- -f1` - + IFS=$cIFS + fst=`echo ${COMP_WORDS[COMP_OPWORD]} | cut -d- -f1` if [[ "${fst}" == "vbd" ]]; then - COMPREPLY=( $(compgen -W "Disk ,CD " -- ${value}) ) - return 0 + COMPREPLY=( $(compgen -W "Disk ,CD " -- ${value}) ) + return 0 elif [[ "${fst}" == "vdi" ]]; then - COMPREPLY=( $(compgen -W "system ,user ,suspend ,crashdump " -- ${value}) ) - return 0 + COMPREPLY=( $(compgen -W "system ,user ,suspend ,crashdump " -- ${value}) ) + return 0 elif [[ "${fst}" == "sr" ]]; then - COMPREPLY=( $(compgen -W "$(xe sm-list params=type --minimal 2>/dev/null | sed 's/,/ ,/g') " -- ${value}) ) - return 0 + IFS=$oIFS + local types=`${xe} sm-list params=type --minimal 2>/dev/null | sed 's/,\|$/ &/g'` + IFS=$cIFS + COMPREPLY=( $(compgen -W "$types" -- ${value}) ) + return 0 fi ;; entries) # for host-get-system-status - val=$(final_comma_separated_param "$value") - master_uuid=$(xe pool-list params=master --minimal 2>/dev/null) - IFS=$'\n' - caps=$($xe host-get-system-status-capabilities uuid="$master_uuid" 2>/dev/null | grep '/dev/null` + caps=`${xe} host-get-system-status-capabilities uuid="$master_uuid" 2>/dev/null | grep '/dev/null` - return 0 - ;; - edition) # for host-apply-edition (licensing) - IFS=$'\n,' - COMPREPLY=( $(compgen -W "free ,enterprise ,platinum " -- ${value}) ) - return 0 - ;; + case "${COMP_WORDS[COMP_OPWORD]}" in + log-set-output) + IFS=$cIFS + COMPREPLY=( $(compgen -W "file:,syslog:,stderr ,nil " -- ${value}) ) + ;; + host-get-system-status) + IFS=$cIFS + COMPREPLY=( $(compgen -W "tar.bz2 ,zip " -- ${value}) ) + ;; + esac + return 0 + ;; + copy-bios-strings-from) # for vm-install + IFS=$oIFS + hosts=`${xe} host-list params=uuid --minimal 2>/dev/null | sed 's/,\|$/\ &/g'` + IFS=$cIFS + COMPREPLY=( $(compgen -W "$hosts" -- ${value}) ) + return 0 + ;; + edition) # for host-apply-edition (licensing) + IFS=$cIFS + COMPREPLY=( $(compgen -W "free ,advanced ,enterprise ,enterprise-xd ,platinum " -- ${value}) ) + return 0 + ;; *) snd=`echo ${param} | cut -d- -f2` fst=`echo ${param} | cut -d- -f1` if [[ "${snd}" == "uuid" ]]; then - if [[ "${fst}" == "snapshot" ]]; then - all="" - else - all="--all" - fi - uuids=`${xe} ${fst}-list ${all} params=uuid --minimal 2>/dev/null` - IFS=$'\n,' - COMPREPLY=( $(compgen_escape "$uuids" "$value") ) - return 0 + if [[ "${fst}" == "snapshot" ]]; then + all="" + else + all="--all" + fi + IFS=$oIFS + uuids=`${xe} ${fst}-list ${all} params=uuid --minimal 2>/dev/null` + IFS=$cIFS + COMPREPLY=( $(compgen_escape "$uuids" "$value") ) + return 0 else - fst=`echo ${COMP_WORDS[1]} | cut -d- -f1` - snd=`echo ${COMP_WORDS[1]} | cut -d- -f2` - if [[ "${snd}" == "list" || "${fst}" == "vm" ]]; then - IFS=$'\n,' - COMPREPLY=( $(compgen_names "${fst}-list" "$param" "$value") ) - return 0 - fi - fi + fst=`echo ${COMP_WORDS[COMP_OPWORD]} | cut -d- -f1` + snd=`echo ${COMP_WORDS[COMP_OPWORD]} | cut -d- -f2` + if [[ "${snd}" == "list" || "${fst}" == "vm" ]]; then + IFS=$cIFS + COMPREPLY=( $(compgen_names "${fst}-list" "$param" "$value") ) + return 0 + fi + fi ;; esac else local param reqd param=${COMP_WORDS[COMP_CWORD]} - vmselectors=`${xe} help ${COMP_WORDS[1]} 2>/dev/null | grep "optional params" | grep ""` - hostselectors=`${xe} help ${COMP_WORDS[1]} 2>/dev/null | grep "optional params" | grep ""` isdeviceconfig=`echo "${param}" | grep "device-config:"` isvcpusparams=`echo "${param}" | grep "VCPUs-params:"` + IFS=$oIFS + vmselectors=`${xe} help ${COMP_WORDS[$COMP_OPWORD]} 2>/dev/null | grep "optional params" | grep ""` + hostselectors=`${xe} help ${COMP_WORDS[$COMP_OPWORD]} 2>/dev/null | grep "optional params" | grep ""` + IFS=$cIFS if [ "${isdeviceconfig}" ]; then - IFS=" " type=$(for i in ${COMP_WORDS[@]:2}; do echo $i | grep "^type="; done | sed -e 's/^type=//' | tr [A-Z] [a-z]) - extraargs=,$(IFS=";"; for i in `xe sm-list type=${type} params=configuration --minimal 2>/dev/null`; do echo device-config:$i | cut -d ':' -f 1-2; done | sed -e 's/ //g' -e 's/$/=/') + IFS=" " + type=$(for i in ${COMP_WORDS[@]:$COMP_OPWORD+1}; do echo $i | grep "^type="; done | sed -e 's/^type=//' | tr [A-Z] [a-z]) + IFS=$oIFS + devparam=`${xe} sm-list type=${type} params=configuration --minimal` + IFS=";" + extraargs=,$(for i in $devparam; do echo device-config:$i | cut -d ':' -f 1-2; done | sed -e 's/ //g' -e 's/$/=/') elif [ "${isvcpusparams}" ]; then + IFS=";" extraargs=,$(for i in weight cap mask; do echo "VCPUs-params:$i="; done) elif [ "${vmselectors}" ]; then - if [ "${param}" ] ; then - extraargs=",vm=,"$(params "vm-list" | sed 's/params=//g') - else - extraargs=",vm=" - fi + if [ "${param}" ] ; then + extraargs=",vm=,"$(params "vm-list" | sed 's/params=//g') + else + extraargs=",vm=" + fi elif [ "${hostselectors}" ]; then - if [ "${param}" ] ; then - extraargs=",host=,"$(params "host-list" | sed 's/params=//g') - else - extraargs=",host=" - fi + if [ "${param}" ] ; then + extraargs=",host=,"$(params "host-list" | sed 's/params=//g') + else + extraargs=",host=" + fi else - extraargs="" + extraargs="" fi - IFS=$'\n,' - COMPREPLY=( $(compgen_params "${COMP_WORDS[1]}" "$extraargs" "$param") ) + IFS=$cIFS + COMPREPLY=( $(compgen_params "${COMP_WORDS[COMP_OPWORD]}" "$extraargs" "$param") ) return 0 fi } @@ -236,22 +306,25 @@ # final_comma_separated_param() { + local IFS if expr "$1" : ".*," >/dev/null then - old_ifs="$IFS" - bits=$(echo "$1" | sed -e 's#^\(.*\),\([^,]*\)$#\1%\2#g') - IFS=% - bits=($bits) - echo "${bits[1]}" - IFS="$old_ifs" + old_ifs="$IFS" + bits=$(echo "$1" | sed -e 's#^\(.*\),\([^,]*\)$#\1%\2#g') + IFS=% + bits=($bits) + echo "${bits[1]}" + IFS="$old_ifs" else - echo "$1" + echo "$1" fi } compgen_names() { - local vals=$("$xe" "$1" --minimal params="$2" 2>/dev/null | sed -e "s,',$MAGIC_SQUOTE,g" -e "s,\",$MAGIC_DQUOTE,g") + local IFS=$oIFS + local vals=`${xe} "$1" --minimal params="$2" 2>/dev/null | sed -e "s,',$MAGIC_SQUOTE,g" -e "s,\",$MAGIC_DQUOTE,g"` + local IFS=$cIFS compgen_escape "$vals" "$3" } @@ -263,11 +336,14 @@ params() { - "$xe" help "$1" 2>/dev/null | grep '^[^:]*params' | cut -d: -f2- | egrep -v "^ $" | cut -c 2- | sed -e 's/,/=,/g' -e 's/$/=/g' -e 's/:=/:/g' -e 's/-=/-/g' -e 's/ //g' + local IFS=$oIFS + ${xe} help "$1" | grep '^[^:]*params' | cut -d: -f2- | egrep -v "^ $" | cut -c 2- | sed -e 's/,/=,/g' -e 's/$/=/g' -e 's/:=/:/g' -e 's/-=/-/g' -e 's/ //g' + local IFS=$cIFS } compgen_params() { + local IFS=$cIFS local v=$(params "$1" | sed -e 's/=//g' -e 's/=//g') compgen -o nospace -W "$v$2" -- "$3" }