-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvadas_completion.sh
More file actions
executable file
·329 lines (294 loc) · 7.02 KB
/
Copy pathvadas_completion.sh
File metadata and controls
executable file
·329 lines (294 loc) · 7.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#!/bin/bash
#
# Copyright (c) 2025-2026 George Sapkin
#
# SPDX-License-Identifier: GPL-2.0-only
readonly VADAS_CONFIG_DIR="${VADAS_CONFIG_DIR:-${HOME}/.config/vadas}"
readonly VADAS_IMAGE_DIR="${VADAS_IMAGE_DIR:-${VADAS_CONFIG_DIR}/images}"
readonly REMOTE_USER='root'
function _comp_cp() {
local start_idx=2
if [ "${COMP_WORDS[2]}" == '-r' ]; then
start_idx=3
fi
if [ ${COMP_CWORD} -eq 2 ] && [[ "${cur}" == -* || -z "${cur}" ]]; then
COMPREPLY+=( $(compgen -W -r -- "${cur}") )
fi
if [[ ${COMP_CWORD} -ge ${start_idx} ]]; then
local cur="${COMP_WORDS[COMP_CWORD]}"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
# SCENARIO 1: Word-split has occurred at the colon
if [[ "$prev" == ":" ]]; then
local vm_name="${COMP_WORDS[COMP_CWORD-2]}"
_comp_remote_path "$vm_name" "$cur" 'split'
return 0
fi
# SCENARIO 2: Colon is present in current word (no split happened)
if [[ "$cur" == *:* ]]; then
local vm_name="${cur%%:*}"
local path="${cur#*:}"
_comp_remote_path "$vm_name" "$path" 'full'
return 0
fi
# SCENARIO 3: Local file OR Start of VM name
# Mix filenames mode (for local files) and raw mode (for VM names
# with colons)
local vm_matches=()
if [[ "$cur" != */* && "$cur" != .* && "$cur" != ~* ]]; then
if command -v virsh >/dev/null 2>&1; then
local vms
vms=$(_get_vm_list --state-running)
for vm in $vms; do
if [[ "$vm" == "${cur}"* ]]; then
vm_matches+=("${vm}:")
fi
done
fi
fi
if [ ${#vm_matches[@]} -gt 0 ]; then
# If we have VM matches, we cannot use 'compopt -o filenames'
# because it will escape the colon in 'vm:'.
# We must manually list local files and append / to directories.
compopt +o filenames
compopt -o nospace
# Add VMs
COMPREPLY=( "${vm_matches[@]}" )
# Add Local files manually
local files
files=$(compgen -f -- "$cur")
for f in $files; do
if [[ -d "$f" ]]; then f="$f/"; fi
COMPREPLY+=("$f")
done
else
# No VM matches? Safe to use standard filename completion
compopt -o filenames
COMPREPLY=( $(compgen -f -- "${cur}") )
fi
return 0
fi
}
function _comp_remote_path() {
local vm_name="$1"
local path="$2"
local mode="$3"
local ip
ip=$(_get_vm_ip "$vm_name")
if [ -z "$ip" ]; then
return 1
fi
local search_path="$path"
if [[ "$path" == \~* ]]; then
local remote_home
remote_home=$(ssh -q \
-o BatchMode=yes \
-o ConnectTimeout=2 \
-o LogLevel=ERROR \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
"${REMOTE_USER}@${ip}" 'echo $HOME'
)
if [ -z "$remote_home" ]; then
return 1
fi
# Replace ~ with the absolute path
search_path="${remote_home}${path#\~}"
fi
# Escape double quotes in the path for safe use in ssh command
local safe_path="${search_path//\"/\\\"}"
local remote_pattern="\"${safe_path}\"*"
local remotes=()
while IFS= read -r line; do
[[ -n "$line" ]] && remotes+=("$line")
done < <(ssh -q \
-o BatchMode=yes \
-o ConnectTimeout=2 \
-o LogLevel=ERROR \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
"${REMOTE_USER}@${ip}" "ls -1dF ${remote_pattern} 2>/dev/null"
)
local choices=()
for r in "${remotes[@]}"; do
local clean_r="${r%[*@|=]}"
local item="${clean_r}"
if [ "$mode" == 'full' ]; then
choices+=("${vm_name}:${item}")
else
choices+=("${item}")
fi
done
if [ ${#choices[@]} -eq 0 ]; then
return 0
fi
compopt -o nospace
compopt +o filenames
COMPREPLY=( "${choices[@]}" )
# If single file match (not dir), allow space
if [ ${#COMPREPLY[@]} -eq 1 ] && [[ "${COMPREPLY[0]}" != */ ]]; then
compopt +o nospace
fi
return 0
}
function _comp_vms() {
local virsh_flags=()
local extra_opts=()
for arg in "$@"; do
if [[ "$arg" == --state-* || "$arg" == "--all" ]]; then
virsh_flags+=("$arg")
else
extra_opts+=("$arg")
fi
done
local vm_ids
vm_ids=$(_get_vm_list "${virsh_flags[@]}")
COMPREPLY=( $(compgen -W "${vm_ids} ${extra_opts[*]}" -- "${cur}") )
}
function _get_vm_list() {
local state_flags=("$@")
# virsh list can't handle multiple states when shutoff is specified
local vms
for state in "${state_flags[@]}"; do
vms+=$(virsh list "$state" --name 2>/dev/null)
vms+=' '
done
for vm in $vms; do
if virsh metadata "$vm" --uri urn:vadas 2>/dev/null |
grep -q 'tags'
then
echo "$vm"
fi
done
}
function _get_vm_ip() {
local vm_name="$1"
virsh metadata "$vm_name" --uri urn:vadas 2>/dev/null |
xmllint --xpath "string(//*[local-name()='lanip'])" -
}
function _vadas_sh_completion() {
local cur prev opts vm_ids
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
case "${COMP_WORDS[1]}" in
cp)
_comp_cp
return 0
;;
esac
opts='
clean
configure
connect
cp
create
env
exec
images
kill
list
pause
ps
remove
rm
resume
show
start
stop
suspend
'
local clean_opts='all cache images temp'
local configure_opts='vm'
local create_opts='network pool vm'
local help_opts='--help -h'
local list_opts='images vm'
local ps_opts='--all'
local remove_opts='all network pool vm'
local show_opts='ip'
local stop_opts='--force'
case "${prev}" in
clean)
COMPREPLY=( $(compgen -W "${clean_opts} ${help_opts}" -- "${cur}") )
return 0
;;
configure)
COMPREPLY=( $(compgen -W "${configure_opts} ${help_opts}" -- "${cur}") )
return 0
;;
connect|exec)
_comp_vms --state-running "${help_opts}"
return 0
;;
create)
COMPREPLY=( $(compgen -W "${create_opts} ${help_opts}" -- "${cur}") )
return 0
;;
list)
COMPREPLY=( $(compgen -W "${list_opts} ${help_opts}" -- "${cur}") )
return 0
;;
remove|rm)
COMPREPLY=( $(compgen -W "${remove_opts} ${help_opts}" -- "${cur}") )
return 0
;;
show)
COMPREPLY=( $(compgen -W "${show_opts} ${help_opts}" -- "${cur}") )
return 0
;;
vm)
local parent="${COMP_WORDS[COMP_CWORD-2]}"
case "${parent}" in
configure) _comp_vms --state-running ;;
create) COMPREPLY=( $(compgen -W "--from-local ${help_opts}" -- "${cur}") ) ;;
*) _comp_vms --all ;;
esac
return 0
;;
ip)
local parent="${COMP_WORDS[COMP_CWORD-2]}"
if [ "${parent}" == 'show' ]; then
_comp_vms --all
fi
return 0
;;
pause|suspend)
_comp_vms --state-running "${help_opts}"
return 0
;;
ps)
COMPREPLY=( $(compgen -W "${ps_opts}" -- "${cur}") )
return 0
;;
resume)
_comp_vms --state-paused "${help_opts}"
return 0
;;
start)
_comp_vms --state-paused --state-shutoff "${help_opts}"
return 0
;;
stop|kill)
_comp_vms --state-paused --state-running "${stop_opts} ${help_opts}"
return 0
;;
--force)
local parent="${COMP_WORDS[COMP_CWORD-2]}"
if [[ "${parent}" == 'stop' || "${parent}" == 'kill' ]]; then
_comp_vms --state-running "${help_opts}"
fi
return 0
;;
--from-local)
compopt -o filenames
COMPREPLY=( $(compgen -f -- "${cur}") )
return 0
;;
*)
if [[ ${COMP_CWORD} -eq 1 ]]; then
COMPREPLY=( $(compgen -W "${opts} ${help_opts}" -- "${cur}") )
fi
return 0
;;
esac
}
complete -F _vadas_sh_completion vadas