#compdef openstack aodh barbican ceilometer cinder cloudkitty designate freezer glance gnocchi heat ironic keystone magnum manila mistral monasca murano neutron nova sahara senlin tacker trove vitrage watcher zun # https://wiki.openstack.org/wiki/OpenStackClients # http://docs.openstack.org/user-guide/common/cli-install-openstack-command-line-clients.html local curcontext="$curcontext" state line expl ret=1 local -a clnts_compl_new clnts_compl_old clnts_swift_like # # We support three different client categories: # 1) Clients with new style complete command where output is like: # # cmds='alarm alarm-history capabilities complete help' # cmds_alarm='create delete list show update' # cmds_alarm_history='search show' # cmds_alarm_history_search='-h --help -f --format -c --column --max-width --noindent --quote --query' # # 2) Clients with old style bash-completion command which does # not separate options and commands: # # --tenant_id floatingip-delete bgp-peer-delete --default-prefixlen net-create [...] # # 3) Swift, slightly different from 2) # clnts_compl_new=( aodh barbican designate freezer gnocchi openstack vitrage watcher ) clnts_compl_old=( ceilometer cinder cloudkitty glance heat ironic keystone magnum manila mistral monasca murano neutron nova sahara senlin tacker trove zun ) clnts_swift_like=( swift ) # Python clients take quite some time to start up and some (openstack(1)) # even go over the network for completions so we cache things pretty hard if (( ! $+_cache_openstack_clnt_opts )); then typeset -gA _cache_openstack_clnt_outputs typeset -gA _cache_openstack_clnt_opts typeset -gA _cache_openstack_clnt_cmds typeset -gA _cache_openstack_clnt_cmds_opts fi local -a conn_opts local opt arg word # Only openstack(1) requires parameters to provide completion info if [[ $service == openstack && -n ${words[(r)--os-*]} ]]; then if (( ! $+_cache_openstack_conn_opts )); then _cache_openstack_conn_opts=( ${(M)${=${(f)"$($service help 2>/dev/null)"}}:#--os-*} ) fi # --os-tenant-id --os-tenant-name are deprecated but still widely used for opt in ${=_cache_openstack_conn_opts} --os-tenant-id --os-tenant-name; do arg= for word in ${words:1}; do [[ $word == $opt ]] && arg=$word && break done [[ -n $arg && -n ${arg##-*} ]] && conn_opts=( $conn_opts $opt $arg ) done fi # New style clients if [[ -n ${clnts_compl_new[(r)$service]} ]]; then if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then # Populate caches - clnt_outputs is command raw output used later _cache_openstack_clnt_outputs[$service]=${:-"$($service ${(Q)conn_opts} complete 2>/dev/null)"} _cache_openstack_clnt_opts[$service]=${${${${(M)${${${${=${(f)"$($service help 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.}%--os-} fi # get worlds left of the curser into an array local -a left_words left_words=(${=LBUFFER}) # if curser is directly at a word (no space at the end), # exclude the last word to offer right matches # the last word could be a partial match that is later checked (prefix-needed) local partial="" if [[ "${LBUFFER[-1]}" != " " ]]; then partial=${(@)left_words[-1]} left_words=(${(@)left_words[1,$#left_words-1]}) fi # remove $service left_words=(${left_words:1}) # filter out "help" if [[ $left_words[1] == help ]]; then left_words=(${(@)left_words[2,$#left_words]}) fi # filter out options (-*) left_words=(${left_words//-*}) local -a subcmd_array cmds_array cache_key_array cache_values subcmd_array=() cmds_array=(cmds) cache_key_array=(${service}) cache_values=() local cache_key cmds cache_key="" cmds="" # Check for matches one level at a time # example: "" server create for word in "" ${(@)left_words}; do # first loop second loop third loop subcmd_array=(${(@)subcmd_array} ${word}) # () (server) (server create) cmds_array=(${(@)cmds_array} ${word}) # (cmds) (cmds server) (cmds server create) cmds=${${(j:_:)cmds_array}/-/_} # cmds cmds_openstack cmds_server_create cache_key_array=(${(@)cache_key_array} ${word}) # (openstack) (openstack server) (openstack server create) cache_key=${${(j:_:)cache_key_array}/-/_} # openstack openstack_server openstack_server_create # lookup if current word is in cache_values of last elements if [[ ${cache_values[(wI)${word}]} -gt 0 || $word == "" ]]; then _cache_openstack_clnt_cmds[${cache_key}]=${${${_cache_openstack_clnt_outputs[${service}]}/* ${cmds}=\'}/\'*} fi # set cache_values for next loop cache_values=${_cache_openstack_clnt_cmds[${cache_key}]} done # Populate the command cache if [[ -z $_cache_openstack_clnt_cmds[${cache_key}] ]]; then _message "missing authentication options" else # add global options to completion list if current word start with -* local extra_opts if [[ $words[CURRENT] == -* ]]; then; extra_opts=${_cache_openstack_clnt_opts[$service]} fi { ! zstyle -T ":completion:${curcontext}:options" prefix-needed \ || [[ -n "${partial}" && ${${_cache_openstack_clnt_cmds[${cache_key}]}[(Iw)${partial}*]} -gt 0 || -prefix - ]] } \ && _values -w option ${(u)=_cache_openstack_clnt_cmds[${cache_key}]} ${(u)=extra_opts} \ && ret=0 fi # Old style clients elif [[ -n ${clnts_compl_old[(r)$service]} ]]; then if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then # Populate caches _cache_openstack_clnt_opts[$service]=${${${(M)${${${${=${(f)"$($service help 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.} _cache_openstack_clnt_cmds[$service]=${${(M)${=${(f)"$($service bash-completion 2>/dev/null)"}}:#[A-Za-z]*}/bash-completion} fi local cmd # Determine the command for word in ${words:1}; do local s=${_cache_openstack_clnt_cmds[$service]} [[ $s[(wI)$word] -gt 0 ]] && cmd=$word && break done # Populate command option cache # Mostly no options for help, prevent consecutive calls with help here if [[ -n $cmd && $cmd != help && -z $_cache_openstack_clnt_cmds_opts[$service$cmd] ]]; then _cache_openstack_clnt_cmds_opts[$service$cmd]=${${${(M)${${${${=${(f)"$($service help $cmd 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.} fi # Special treatment for the help command if [[ $cmd == help ]]; then if [[ $words[CURRENT-1] == help && $words[CURRENT] != -* ]]; then _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0 else _values -w option help && ret=0 fi # Client options elif [[ -z $cmd && $words[CURRENT] == -* ]]; then _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0 # Commands elif [[ -z $cmd ]]; then _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0 # Command options else { ! zstyle -T ":completion:${curcontext}:options" prefix-needed || [[ -prefix - ]] } && \ [[ -n $_cache_openstack_clnt_cmds_opts[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_opts[$service$cmd]//\:/\\\:} && ret=0 fi # Swift like clients elif [[ -n ${clnts_swift_like[(r)$service]} ]]; then if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then # Populate caches - clnt_outputs is command raw output used later _cache_openstack_clnt_outputs[$service]=${(f)"$($service --help 2>/dev/null)"} _cache_openstack_clnt_opts[$service]=${${${${(M)${${${${=_cache_openstack_clnt_outputs[$service]}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.}/=*} _cache_openstack_clnt_cmds[$service]=${=${(M)${(M)${(f)_cache_openstack_clnt_outputs[$service]}:# [a-z]*}/ [A-Z]*}} fi local cmd # Determine the command for word in ${words:1}; do local s=${_cache_openstack_clnt_cmds[$service]} [[ $s[(wI)$word] -gt 0 ]] && cmd=$word && break done # Populate command option cache if [[ -n $cmd && -z $_cache_openstack_clnt_cmds_opts[$service$cmd] ]]; then _cache_openstack_clnt_cmds_opts[$service$cmd]=${${${(M)${${${${=${(f)"$($service $cmd --help 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.} fi # Client options if [[ -z $cmd && $words[CURRENT] == -* ]]; then _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0 # Commands elif [[ -z $cmd ]]; then _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0 # Command options else { ! zstyle -T ":completion:${curcontext}:options" prefix-needed || [[ -prefix - ]] } && \ [[ -n $_cache_openstack_clnt_cmds_opts[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_opts[$service$cmd]//\:/\\\:} && ret=0 fi fi return ret