_lustre_cmds() { local cmd="$1" local sub="$2" # "--list-command" prints commands in columns and truncates long ones "$cmd" "$sub" --non-existent-option | sed -e 1d -e '$d' -e 's/"//g' -e /=/d -e /exit/d -e /quit/d } _lustre_long_opts() { local cmd="$1" local sub="$2" local subsub="$3" # strip off usage message decoration and leave long opts if [[ -n "$subsub" ]]; then "$cmd" "$sub" help "$subsub" |& grep -owE -- '--[-a-zA-Z0]*' else "$cmd" help "$sub" |& sed -e 1d | grep -owE -- '--[-a-zA-Z0]*' fi # several commands take the same options as setstripe, except --delete case "$sub$subsub" in migrate|mirrorcreate|mirrorextend) _lustre_long_opts "$cmd" setstripe | grep -v -- --delete esac } _lustre_short_opts() { local cmd="$1" local sub="$2" local subsub="$3" # strip off usage message decoration and leave short opts if [[ -n "$subsub" ]]; then "$cmd" "$sub" help "$subsub" |& grep -owE -- '-[-a-zA-Z0]' else "$cmd" help "$sub" |& grep -owE -- '-[-a-zA-Z0]' fi # several commands take the same options as setstripe, except -d case "$sub$subsub" in migrate|mirrorextend|mirrorextend) _lustre_short_opts "$cmd" setstripe | grep -v -- -d esac } _lustre_comp_flags() { local cmd=$1 local flags flags=$("$cmd" help find |& tr "<>[],}" " " | grep -- '--component-flags {' | cut -d\{ -f2) if [[ -z "$flags" ]]; then local version=($("$cmd" --version)) case "${version[1]}" in 2.13*) flags="init stale prefer offline nosync extension";; 2.12*|2.11*) flags="init stale prefer offline nosync";; *) flags="init";; esac fi echo $flags } _lustre_mountpoints() { findmnt --list -t lustre -n -o TARGET } _lustre_mount_fsnames() { local mountpoint # FIXME: will fail if newlines in $mountpoint, why would anyone do that? _lustre_mountpoints | while read mountpoint; do lfs getname -n "$mountpoint" 2> /dev/null done } _lustre_devices() { lctl device_list | awk '{ print $4 }' } _lustre_fsnames() { local mountpoint="${1:-'.'}" local fsname=$(lfs getname -n "$mountpoint" 2>/dev/null) [[ -n "$fsname" ]] && echo "$fsname" || _lustre_mount_fsnames } _lustre_layouts() { "$cmd" help find |& tr "[]," " " | grep -- --layout | sed "s/.*-L //" } _lustre_mdts() { lfs mdts $1 | grep _UUID | sed -e "s/[0-9]*: //" -e "s/_UUID.*//" } _lustre_osts() { lfs osts $1 | grep _UUID | sed -e "s/[0-9]*: //" -e "s/_UUID.*//" } _lustre_pools() { if [[ -d "$1" ]]; then "$cmd" pool_list $1 2> /dev/null | grep -v "[Pp]ools from" | cut -d. -f2 return 0 fi for fsname in $(_lustre_fsnames $1); do "$cmd" pool_list $fsname 2> /dev/null | grep -v "[Pp]ools from" done } _lfs() { local cur prev words cword local mountpoint cmd sub find_opts COMPREPLY=() # allow different versions of bash_completion to work if declare -F _init_completion > /dev/null; then # this provides more functionality, but is only in v2.x _init_completion || return else # this is compatible with both v1.3 and v2.x _get_comp_words_by_ref cur prev words cword fi cmd="${words[0]}" sub="${words[1]}" [[ "$sub" == "mirror" || "$sub" == "pcc" ]] && subsub="${words[2]}" if [[ "$cword" == "1" || "$prev" == "help" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_cmds "$cmd")' -- "$cur")) return 0 fi case "$cur" in --*) COMPREPLY+=($(compgen -W '$(_lustre_long_opts "$cmd" "$sub" "$subsub")' -- "$cur")) return 0 ;; -*) # lfs find allows "-longopt" for compatibility with find(1) [[ "$sub" == "find" ]] && find_opts=$(_lustre_long_opts "$cmd" find) COMPREPLY+=($(compgen -W '$(_lustre_short_opts "$cmd" "$sub" "$subsub") ${find_opts//--/-}' -- "$cur")) return 0 ;; esac case "$sub" in check) [[ -n "$cur" ]] && return 0 COMPREPLY+=($(compgen -W '$("$cmd" help check |& grep usage | sed -e "s/[<>|]/ /g" \ -e "s/.*check //")' -- "$cur")) return 0 ;; df) mapfile -t COMPREPLY < <( _lustre_mountpoints | grep -- "^$cur" | sed 's/ /\\ /g' ) return 0 ;; find) [[ -d "${words[2]}" ]] && mountpoint="${words[2]}" case "${prev/--/-}" in -component-flags|-comp-flags) # FIXME: this should allow a comma-separated list COMPREPLY+=($(compgen -W '$(_lustre_comp_flags)' -- "$cur")) return 0 ;; -g|-group) COMPREPLY+=($(compgen -g -- "$cur")) return 0 ;; -L|-layout) COMPREPLY+=($(compgen -W '$(_lustre_layouts)' -- "$cur")) return 0 ;; -m|-mdt) # FIXME: this should allow a comma-separated list COMPREPLY+=($(compgen -W '$(_lustre_mdts "$mountpoint")' -- "$cur")) return 0 ;; -O|-ost) # FIXME: this should allow a comma-separated list COMPREPLY+=($(compgen -W '$(_lustre_osts "$mountpoint")' -- "$cur")) return 0 ;; -pool) COMPREPLY+=($(compgen -W '$(_lustre_pools "$mountpoint")' -- "$cur")) return 0 ;; -t|-type) COMPREPLY+=($(compgen -W 'b c d f l p s' -- "$cur")) return 0 ;; -u|-user) COMPREPLY+=($(compgen -u -- "$cur")) return 0 ;; esac if [ -z "$mountpoint" ]; then mapfile -t COMPREPLY < <( _lustre_mountpoints | grep -- "^$cur" | sed -e 's/ /\\ /g' ) return 0 fi ;; mirror) if [[ "$prev" == "$sub" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_cmds "$cmd" "$sub")' -- "$cur")) return 0 fi ;; pcc) if [[ "$prev" == "$sub" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_cmds "$cmd" "$sub")' -- "$cur")) return 0 fi ;; pool_list) COMPREPLY+=($(compgen -W '$(_lustre_fsnames _lustre_pools)' -- "$cur")) return 0 ;; setstripe) case "$prev" in --component-flags|--comp-flags) # only subset allowed, easier to list than exclude for now # COMPREPLY+=($(compgen -W '$(_lustre_comp_flags)' -- "$cur")) # FIXME: this should allow a comma-separated list COMPREPLY+=($(compgen -W 'nosync prefer' -- "$cur")) return 0 ;; -p|--pool) COMPREPLY+=($(compgen -W '$(_lustre_pools)' -- "$cur")) return 0 ;; esac ;; esac _filedir return 0 } && complete -F _lfs lfs _lctl() { local cur prev words cword COMPREPLY=() # allow different versions of bash_completion to work if declare -F _init_completion > /dev/null; then # this provides more functionality, but is only in v2.x _init_completion || return else # this is compatible with both v1.3 and v2.x _get_comp_words_by_ref cur prev words cword fi cmd="${words[0]}" sub="${words[1]}" [[ "$sub" == "--device" && $cword -ge 4 ]] && sub="${words[3]}" if [[ "$cword" == "1" || "$prev" == "help" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_cmds "$cmd")' -- "$cur")) return 0 fi case "$cur" in --*) COMPREPLY+=($(compgen -W '$(_lustre_long_opts "$cmd" "$sub")' -- "$cur")) return 0 ;; -*) COMPREPLY+=($(compgen -W '$(_lustre_short_opts "$cmd" "$sub")' -- "$cur")) return 0 ;; esac case "$sub" in --device) if [[ "$cword" == "2" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_devices)' -- "$cur")) elif [[ "$cword" == "3" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_cmds "$cmd")' -- "$cur")) fi return 0 ;; get_param|list_param|set_param) local filter="s/=$//" [[ "$sub" == "set_param" ]] && filter="/[^=/]$/d" mapfile -t COMPREPLY < <( "$cmd" list_param -F "${cur#[\"\']}*" 2>/dev/null | sed -e "$filter" -e 's#/$#.#' \ -e "s#^${cur//\*/[^.]*}#$cur#" ) compopt -o nospace return 0 ;; pcc) if [[ "$prev" == "$sub" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_cmds "$cmd" "$sub")' -- "$cur")) return 0 fi ;; pool_list) if [[ "$cword" == "2" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_mountpoints _lustre_fsnames _lustre_pools)' -- "$cur")) return 0 fi ;; pool_destroy) if [[ "$cword" == "2" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_pools)' -- "$cur")) return 0 fi return 0 ;; pool_add|pool_remove) if [[ "$cword" == "2" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_pools)' -- "$cur")) return 0 elif [[ "$cword" == "3" ]]; then COMPREPLY+=($(compgen -W '$(_lustre_osts)' -- "$cur")) return 0 fi return 0 ;; esac } && complete -F _lctl lctl