From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 3342 invoked by alias); 6 Sep 2016 19:28:24 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 39195 Received: (qmail 10976 invoked from network); 6 Sep 2016 19:28:24 -0000 X-Qmail-Scanner-Diagnostics: from mail-wm0-f53.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(74.125.82.53):SA:0(-0.0/5.0):. Processed in 0.694831 secs); 06 Sep 2016 19:28:24 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-0.0 required=5.0 tests=SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.1 X-Envelope-From: myllynen@redhat.com X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | Received-SPF: pass (ns1.primenet.com.au: SPF record at _netblocks.google.com designates 74.125.82.53 as permitted sender) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:reply-to:to:from:subject:organization:message-id :date:user-agent:mime-version:content-transfer-encoding; bh=bQDhEAMjL0KbemicaMG3IWL2NFvxtzLcnXpVUWduQws=; b=kiTGraCP1AVxU7RZh+STQxyHHOL5pZw/H0TtOleT3Ce7MSVn0WSyGbrubZXz8IOIEp OTcWTyXGwWvdxZNYx3s1n/rOTK8NattwOERw85pEdBOXRa7JL3xZYpDdrIhIMhmMVSax ScJ+bscL33UcZhrt4nFE0kt4jk7jZA9Gu1zh3cMftudSbgxeIcx1SJYc6D+6i1S+8ld+ lI3UCfSofWKeT6m7/XCKPwgHeLuYpXLNLPLHX9o6QRBZCR8Q+ko4E/uBOv9Q11on5GFo 60LBrF6lxFZajrRLP1uJujPV2GgKOS+fvkq3k/vGX2Ie8qs2HtzPGJZGj/dOuwtXeK9l bF8A== X-Gm-Message-State: AE9vXwOLd8WLb+xu2JbmURThUaB4gl/6vhH6qQxRxC8slqle9XEprjUIZoRcJgZfgxM6bPTU X-Received: by 10.194.112.233 with SMTP id it9mr12446675wjb.176.1473165545324; Tue, 06 Sep 2016 05:39:05 -0700 (PDT) Reply-To: Marko Myllynen To: zsh workers From: Marko Myllynen Subject: Zsh OpenStack completions Organization: Red Hat Message-ID: <9361ff20-f0f2-9acd-e43e-4b1e2e203531@redhat.com> Date: Tue, 6 Sep 2016 15:39:02 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.3.0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Hi, Below is a patch to add completions for several OpenStack related command line clients, including the new common client, openstack(1). There are three categories of clients: 1) newer clients which provide per-command sub-commands and options - output is a Bash function, the relevant part being something 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) old skool clients which provide the "bash-completion" command - output is something like (doesn't separate client/command opts): --tenant_id floatingip-delete bgp-peer-delete --default-prefixlen net-create [...] - command options are available with "$client help $command" 3) an oddball, swift, slightly different than 2) We cache things pretty aggressively as something like "neutron help" takes ~0.75s on my laptop (compared to 0.002 of "ls --help") and openstack(1) even goes over the network for the list of completions. The code is client/command agnostic, the only special case is the "help" command which is available with clients in the categories 1) and 2) and is very helpful for most users. I suspect I'll get a response suggesting to use something else that _values in if/else - I couldn't see more elegant way to achieve the current behavior but if you have any ideas, please provide a quick example. (Also, searching for (sub)commands from the associative array doesn't look very nice but I'm not sure how to do that better while still making sure that e.g. net-list and net-list-on-dhcp-agent are not being confused - perhaps I missed a flag to aid in this.) There's one case which I'm not sure there's a perfect solution without hard-coding / special-casing lots of commands/options: some commands of some clients accept some options more than once - for example, for "openstack project create" the "--property" option could be repeated (to provide several key=value pairs). So if trying to stay generic, we'd need to accept several options in all cases (which is mostly incorrect) or accept options only once and force the user to type the option if wanted more than once. I've chosen the latter as it would seem that in most cases there's some typing needed anyway for repetitive options (like key=value pairs, DNS servers, IP addresses, etc), so while not perfect this doesn't strike me too cumbersome. Due to sheer volume of different commands and options I haven't tested all the possible combinations but I've tested each listed client and at least few commands / options and haven't see any issue. The clients included are based on the list mentioned in the links and available on RH OSP 9 (Mitaka). I'm aware that the list might not be complete compared to what is available so if you have a chance to verify any additional clients, please consider amending the list of clients. I'll revisit the list later this year once RH OSP 10 (Newton) is out. Here's a quick screenshot: % cinder - --bypass-url --os-project-domain-id --os-username --debug --os-project-domain-name --os-volume-api-version --endpoint-type --os-project-id --profile --insecure --os-project-name --retries --os-auth-strategy --os-region-name --service-name --os-auth-system --os-tenant-id --service-type --os-auth-url --os-tenant-name --timeout --os-cacert --os-token --version --os-cert --os-url --volume-service-name --os-endpoint-type --os-user-domain-id -d --os-key --os-user-domain-name --os-password --os-user-id % nova zsh: do you wish to see all 209 possibilities (105 lines)? n % neutron ne net-create net-gateway-disconnect net-list net-delete net-gateway-list net-list-on-dhcp-agent net-external-list net-gateway-show net-show net-gateway-connect net-gateway-update net-update net-gateway-create net-ip-availability-list net-gateway-delete net-ip-availability-show % neutron net-create --admin-state-down --provider:physical_network --availability-zone-hint --provider:segmentation_id --column --qos-policy --dns-domain --request-format --format --shared --help --tenant-id --max-width --vlan-transparent --noindent -c --prefix -f --provider:network_type -h % aodh he % aodh help al alarm alarm-history % aodh help alarm create delete list show update --- Completion/Unix/Command/_openstack | 136 +++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 Completion/Unix/Command/_openstack diff --git a/Completion/Unix/Command/_openstack b/Completion/Unix/Command/_openstack new file mode 100644 index 0000000..1b82bfa --- /dev/null +++ b/Completion/Unix/Command/_openstack @@ -0,0 +1,136 @@ +#compdef openstack aodh barbican ceilometer cinder cloudkitty designate glance gnocchi heat ironic keystone magnum manila mistral monasca murano neutron nova sahara senlin swift trove + +# 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 + +clnts_compl_new=( aodh barbican designate gnocchi openstack ) +clnts_compl_old=( ceilometer cinder cloudkitty glance heat ironic keystone magnum manila mistral monasca murano neutron nova sahara senlin trove ) +clnts_swift_like=( swift ) + +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 + typeset -gA _cache_openstack_clnt_cmds_subcmds + typeset -gA _cache_openstack_clnt_cmds_subcmd_opts +fi + +local -a conn_opts +local opt arg word +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 + 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 + +if [[ -n ${clnts_compl_new[(r)$service]} ]]; then + if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then + _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-} + _cache_openstack_clnt_cmds[$service]=${${${${_cache_openstack_clnt_outputs[$service]}/* cmds=\'}/\'*}/complete} + fi + local cmd subcmd + for word in ${words:1}; do + [[ $_cache_openstack_clnt_cmds[$service] != ${${${_cache_openstack_clnt_cmds[$service]#$word }% $word}/ $word } ]] && cmd=$word && break + done + if [[ -n $cmd && -z $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]]; then + local t=cmds_${cmd//-/_} + _cache_openstack_clnt_cmds_subcmds[$service$cmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*} + fi + if [[ -n $cmd ]]; then + for word in ${words:2}; do + [[ $_cache_openstack_clnt_cmds_subcmds[$service$cmd] != ${${${_cache_openstack_clnt_cmds_subcmds[$service$cmd]#$word }% $word}/ $word } ]] && subcmd=$word && break + done + if [[ -n $subcmd && -z $_cache_openstack_clnt_cmds_subcmd_opts[$service$cmd$subcmd] ]]; then + local t=cmds_${cmd//-/_}_${subcmd//-/_} + _cache_openstack_clnt_cmds_subcmd_opts[$service$cmd$subcmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*} + fi + fi + if [[ $cmd == help ]]; then + if [[ $words[CURRENT-1] == $cmd && $words[CURRENT] != -* ]]; then + [[ -n $_cache_openstack_clnt_cmds[$service] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0 + elif [[ $words[CURRENT-2] == $cmd && $words[CURRENT-1] != -* && $words[CURRENT] != -* ]]; then + local cmd=$words[CURRENT-1] + local t=cmds_${cmd//-/_} + [[ -z $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _cache_openstack_clnt_cmds_subcmds[$service$cmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*} + [[ -n $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmds[$service$cmd]} && ret=0 + else + _values -w option help && ret=0 + fi + elif [[ -z $cmd && $words[CURRENT] == -* ]]; then + _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0 + elif [[ -z $cmd ]]; then + if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then + _message "missing authentication options" + else + _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0 + fi + elif [[ -z $subcmd ]]; then + [[ -n $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmds[$service$cmd]} && ret=0 + else + [[ -n $_cache_openstack_clnt_cmds_subcmd_opts[$service$cmd$subcmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmd_opts[$service$cmd$subcmd]//\:/\\\:} && ret=0 + fi + +elif [[ -n ${clnts_compl_old[(r)$service]} ]]; then + if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then + _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 + for word in ${words:1}; do + [[ $_cache_openstack_clnt_cmds[$service] != ${${${_cache_openstack_clnt_cmds[$service]#$word }% $word}/ $word } ]] && cmd=$word && break + done + # Mostly no options for help, prevent consecutive calls with help here + if [[ -n $cmd && $cmd != help && -z $_cache_openstack_clnt_cmds_opts[$service] ]]; then + _cache_openstack_clnt_cmds_opts[$service$cmd]=${${${(M)${${${${=${(f)"$($service help $cmd 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.} + fi + 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 + elif [[ -z $cmd && $words[CURRENT] == -* ]]; then + _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0 + elif [[ -z $cmd ]]; then + _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0 + else + [[ -n $_cache_openstack_clnt_cmds_opts[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_opts[$service$cmd]//\:/\\\:} && ret=0 + fi + +elif [[ -n ${clnts_swift_like[(r)$service]} ]]; then + if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then + _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 + for word in ${words:1}; do + [[ $_cache_openstack_clnt_cmds[$service] != ${${${_cache_openstack_clnt_cmds[$service]#$word }% $word}/ $word } ]] && cmd=$word && break + done + if [[ -n $cmd && -z $_cache_openstack_clnt_cmds_opts[$service] ]]; then + _cache_openstack_clnt_cmds_opts[$service$cmd]=${${${(M)${${${${=${(f)"$($service $cmd --help 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.} + fi + if [[ -z $cmd && $words[CURRENT] == -* ]]; then + _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0 + elif [[ -z $cmd ]]; then + _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0 + else + [[ -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 Thanks, -- Marko Myllynen