Skip to content

Commit 45a2357

Browse files
martinetdthiell
andauthored
bash completions (cea-hpc#563)
* CLI/Nodeset: add --completion command (cea-hpc#563) Special command for bash completion that lists group sources, groups in current source and nodes from groups passed as argument: cluset --completion [-s source] [groups] Example: cluset --completion @* Part of cea-hpc#563. * tests: add basic tests for cluset --completion (cea-hpc#563) Part of cea-hpc#563. * bash completions for clush and cluset (cea-hpc#563) Provide bash completion scripts for clush and cluset/nodeset to autocomplete group sources, groups and "all" nodes from the default or selected source. Part of cea-hpc#563. * packaging: add bash_completion.d/* (cea-hpc#563) Part of cea-hpc#563. --------- Co-authored-by: Stephane Thiell <[email protected]>
1 parent a11656e commit 45a2357

File tree

7 files changed

+214
-3
lines changed

7 files changed

+214
-3
lines changed

Diff for: MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
include ChangeLog
22
include README.md
33
include COPYING.LGPLv2.1
4+
include bash_completion.d/*
45
include conf/*.conf
56
include conf/*.example
67
include conf/clush.conf.d/README

Diff for: bash_completion.d/cluset

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# cluset bash completion
2+
#
3+
# to install in /usr/share/bash-completion/completions/ or ~/.local/share/bash-completion/completions/
4+
_cluset()
5+
{
6+
# shellcheck disable=SC2034 # set/used by _init_completion
7+
local cur prev words cword split
8+
local word options="" skip=argv0 groupsource="" cleangroup=""
9+
10+
_init_completion -s -n : || return
11+
12+
# stop parsing if there had been any non-option before (or --)
13+
for word in "${words[@]}"; do
14+
case "$skip" in
15+
"") ;;
16+
groupsource)
17+
groupsource="$word"
18+
;& # fallthrough
19+
*)
20+
skip=""
21+
continue
22+
;;
23+
esac
24+
case "$word" in
25+
"") ;;
26+
--) return;;
27+
# no-arg options
28+
--version|-h|--help|-n|--nostdin|-a|--all|-q|--quiet|\
29+
-v|--verbose|-d|--debug) ;;
30+
# get source separately...
31+
--groupsource=*) groupsource="${word#*=}";;
32+
-s|--groupsource) skip=groupsource;;
33+
# assume all the rest as options...
34+
# options with = included in word
35+
--*=*) ;;
36+
-*) skip=any;;
37+
*) return;; # was non-option
38+
esac
39+
done
40+
41+
case "$prev" in
42+
-c|--count|-e|--expand|-f|--fold|\
43+
-x|--exclude|-i|--intersection|-X|--xor)
44+
case "$cur" in
45+
*:*)
46+
groupsource="${cur%%:*}"
47+
groupsource="${groupsource#@}"
48+
;;
49+
*)
50+
if [ -n "$groupsource" ]; then
51+
cleangroup=1
52+
fi
53+
;;
54+
esac
55+
options="$(cluset ${groupsource:+-s "$groupsource"} --completion @*)"
56+
if [ -n "$cleangroup" ]; then
57+
options=${options//@"$groupsource":/@}
58+
fi
59+
;;
60+
-s|--groupsource)
61+
options=$(cluset --groupsources --quiet)
62+
;;
63+
# no-arg options
64+
--version|-h|--help|-l|--list|-L|--list-all|-r|--regroup|\
65+
--list-sources|--groupsources|-d|--debug|-q|--quiet|\
66+
-R|--rangeset|-G|--groupbase|--contiguous) ;;
67+
# any other option: just ignore.
68+
-*)
69+
return;;
70+
esac
71+
# get all options from help text... not 100% accurate but good enough.
72+
[ -n "$options" ] || options="$(cluset --help | grep -oP -- '(?<=[ \t])(-[a-z]|--[^= \t]*)')"
73+
74+
# append space for everything that doesn't end in `:` (likely a groupsource)
75+
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur" | sed -e 's/[^:]$/& /')
76+
# remove the prefix from COMPREPLY if $cur contains colons and
77+
# COMP_WORDBREAKS splits on colons...
78+
__ltrim_colon_completions "$cur"
79+
} && complete -o nospace -F _cluset ${BASH_SOURCE##*/}

Diff for: bash_completion.d/clush

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# clush bash completion
2+
#
3+
# to install in /usr/share/bash-completion/completions/ or ~/.local/share/bash-completion/completions/
4+
_clush()
5+
{
6+
# shellcheck disable=SC2034 # set/used by _init_completion
7+
local cur prev words cword split
8+
local word options="" compopts="" skip=argv0 groupsource="" cleangroup=""
9+
10+
_init_completion -s -n : || return
11+
12+
# stop parsing if there had been any non-option before (or --)
13+
for word in "${words[@]}"; do
14+
case "$skip" in
15+
"") ;;
16+
groupsource)
17+
groupsource="$word"
18+
;& # fallthrough
19+
*)
20+
skip=""
21+
continue
22+
;;
23+
esac
24+
case "$word" in
25+
"") ;;
26+
--) return;;
27+
# no-arg options
28+
--version|-h|--help|-n|--nostdin|-a|--all|-q|--quiet|\
29+
-v|--verbose|-d|--debug) ;;
30+
# get source separately...
31+
--groupsource=*) groupsource="${word#*=}";;
32+
-s|--groupsource) skip=groupsource;;
33+
# assume all the rest as options...
34+
# options with = included in word
35+
--*=*) ;;
36+
-*) skip=any;;
37+
*) return;; # was non-option
38+
esac
39+
done
40+
41+
case "$prev" in
42+
-w|-x|-g|--group|-X)
43+
case "$cur" in
44+
*:*)
45+
groupsource="${cur%%:*}"
46+
groupsource="${groupsource#@}"
47+
;;
48+
*)
49+
if [ -n "$groupsource" ]; then
50+
cleangroup=1
51+
fi
52+
;;
53+
esac
54+
if [ "$prev" = "-w" ]; then
55+
compopts="@*" # include all nodes
56+
fi
57+
options="$(cluset ${groupsource:+-s "$groupsource"} --completion $compopts)"
58+
if [ -n "$cleangroup" ]; then
59+
options=${options//@"$groupsource":/@}
60+
fi
61+
case "$prev" in
62+
-g|--group|-X)
63+
options=${options//@/}
64+
;;
65+
esac
66+
;;
67+
-s|--groupsource)
68+
options=$(cluset --groupsources --quiet)
69+
;;
70+
--color)
71+
options="never always auto"
72+
;;
73+
-R|--worker)
74+
options="ssh exec rsh"
75+
;;
76+
# no-arg options
77+
--version|-h|--help|-n|--nostdin|-a|--all|-q|--quiet|\
78+
-v|--verbose|-d|--debug) ;;
79+
# any other option: just ignore.
80+
-*)
81+
return;;
82+
esac
83+
# get all options from help text... not 100% accurate but good enough.
84+
[ -n "$options" ] || options="$(clush --help | grep -oP -- '(?<=[ \t])(-[a-z]|--[^= \t]*)')"
85+
86+
# append space for everything that doesn't end in `:` (likely a groupsource)
87+
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur" | sed -e 's/[^:]$/& /')
88+
# remove the prefix from COMPREPLY if $cur contains colons and
89+
# COMP_WORDBREAKS splits on colons...
90+
__ltrim_colon_completions "$cur"
91+
} && complete -o nospace -F _clush ${BASH_SOURCE##*/}

Diff for: clustershell.spec.in

+12
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
%define py2 1
2828
%endif
2929

30+
%{!?bash_completions_dir: %global bash_completions_dir %{_datadir}/bash-completion/completions}
31+
3032
%global srcname ClusterShell
3133

3234
Name: clustershell
@@ -149,6 +151,13 @@ install -p -m 0644 doc/extras/vim/syntax/clushconf.vim %{buildroot}/%{vimdatadir
149151
install -p -m 0644 doc/extras/vim/syntax/groupsconf.vim %{buildroot}/%{vimdatadir}/syntax/
150152
%{?suse_version:%fdupes %{buildroot}}
151153

154+
install -d %{buildroot}%{bash_completions_dir}
155+
install -p -m 0644 bash_completion.d/cluset -t %{buildroot}%{bash_completions_dir}
156+
install -p -m 0644 bash_completion.d/clush -t %{buildroot}%{bash_completions_dir}
157+
pushd %{buildroot}%{bash_completions_dir}
158+
ln -s cluset nodeset
159+
popd
160+
152161
%if 0%{?rhel}
153162
%clean
154163
rm -rf %{buildroot}
@@ -228,6 +237,9 @@ rm -rf %{buildroot}
228237
%{vimdatadir}/ftdetect/clustershell.vim
229238
%{vimdatadir}/syntax/clushconf.vim
230239
%{vimdatadir}/syntax/groupsconf.vim
240+
%{bash_completions_dir}/cluset
241+
%{bash_completions_dir}/clush
242+
%{bash_completions_dir}/nodeset
231243

232244
%changelog
233245
* Fri Sep 29 2023 Stephane Thiell <[email protected]> 1.9.2-1

Diff for: lib/ClusterShell/CLI/Nodeset.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def print_source_groups(source, level, xset, opts):
132132

133133
def command_list(options, xset, group_resolver):
134134
"""List command handler (-l/-ll/-lll/-L/-LL/-LLL)."""
135-
list_level = options.list + options.listall
135+
list_level = options.list + options.listall + options.completion
136136
if options.listall:
137137
# useful: sources[0] is always the default or selected source
138138
sources = group_resolver.sources()
@@ -175,7 +175,7 @@ def nodeset():
175175
cmdcount = int(options.count) + int(options.expand) + \
176176
int(options.fold) + int(bool(options.list)) + \
177177
int(bool(options.listall)) + int(options.regroup) + \
178-
int(options.groupsources)
178+
int(options.groupsources) + int(options.completion)
179179
if not cmdcount:
180180
parser.error("No command specified.")
181181
elif cmdcount > 1:
@@ -231,7 +231,9 @@ def nodeset():
231231
# Include all nodes from external node groups support.
232232
xset.update(NodeSet.fromall()) # uses default_source when set
233233

234-
if not args and not options.all and not (options.list or options.listall):
234+
if not args and not options.all and not (options.list or
235+
options.listall or
236+
options.completion):
235237
# No need to specify '-' to read stdin in these cases
236238
process_stdin(xset.update, xset.__class__, autostep)
237239

@@ -264,6 +266,17 @@ def nodeset():
264266
# The list command has a special handling
265267
if options.list > 0 or options.listall > 0:
266268
return command_list(options, xset, group_resolver)
269+
elif options.completion: # --completion is used for bash completion
270+
# list group source prefixes unless source is already specified
271+
if not options.groupsource:
272+
for src in group_resolver.sources():
273+
print("@%s:" % src)
274+
# list groups in group source (similar to list)
275+
command_list(options, None, group_resolver)
276+
# then list nodes from the groups passed as argument, if any
277+
if not xset:
278+
return
279+
options.expand = True
267280

268281
# Interpret special characters (may raise SyntaxError)
269282
separator = eval('\'\'\'%s\'\'\'' % options.separator,

Diff for: lib/ClusterShell/CLI/OptionParser.py

+4
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ def install_nodeset_commands(self):
280280
default=False,
281281
help="list all active group sources (see "
282282
"groups.conf(5))")
283+
# special hidden command for bash completion scripts
284+
optgrp.add_option("--completion", action="store_true",
285+
dest="completion",
286+
default=False, help=optparse.SUPPRESS_HELP)
283287
self.add_option_group(optgrp)
284288

285289
def install_nodeset_operations(self):

Diff for: tests/CLINodesetTest.py

+11
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,17 @@ def test_040_wildcards(self):
741741
self._nodeset_t(["-s", "other", "--autostep=3", "-f", "*!*[033-099/2]"],
742742
None, b"nova[030-032,034-100/2,101-489]\n")
743743

744+
def test_041_completion(self):
745+
"""test nodeset --completion"""
746+
self._nodeset_t(["--completion"], None,
747+
b"@test:\n@other:\n@bar\n@foo\n@moo\n")
748+
self._nodeset_t(["--completion", "node1", "node2"], None,
749+
b"@test:\n@other:\n@bar\n@foo\n@moo\nnode1 node2\n")
750+
self._nodeset_t(["-s", "other", "--completion"], None,
751+
b"@other:baz\n@other:norf\n@other:qux\n")
752+
self._nodeset_t(["-s", "other", "--completion", "node1", "node2"], None,
753+
b"@other:baz\n@other:norf\n@other:qux\nnode1 node2\n")
754+
744755

745756
class CLINodesetGroupResolverTest3(CLINodesetTestBase):
746757
"""Unit test class for testing CLI/Nodeset.py with custom Group Resolver

0 commit comments

Comments
 (0)