Skip to content

Commit ea638d3

Browse files
committed
fixes, improvement
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent 7e3f813 commit ea638d3

1 file changed

Lines changed: 172 additions & 19 deletions

File tree

scripts/util/cloudstack-collect-logs.sh

Lines changed: 172 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ set -o pipefail
2222
DATE=""
2323
SSH_USER="${SSH_USER:-root}"
2424
REQUESTED_HOSTS=()
25-
SSH_OPTS="${SSH_OPTS:--o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no}"
25+
SSH_OPTS="${SSH_OPTS:--o BatchMode=yes -o ConnectTimeout=10}"
26+
SSH_KEY="${SSH_KEY:-}"
27+
SSH_PASSWORD="${SSH_PASSWORD:-}"
28+
HOST_PASSWORDS_FILE=""
29+
WORKDIR_BASE=""
30+
31+
declare -A HOST_PASSWORDS=()
2632

2733
MANAGEMENT_LOG_DIR="/var/log/cloudstack/management"
2834
MANAGEMENT_LOG="management-server.log"
@@ -40,18 +46,25 @@ kvm_hosts=()
4046
usage() {
4147
cat <<EOF
4248
Usage:
43-
$0 [--date YYYY-MM-DD] [--hosts HOST1,HOST2,...]
49+
$0 [--date YYYY-MM-DD] [--hosts HOST1,HOST2,...] [--workdir DIR]
50+
[--ssh-key FILE | --ssh-password PASSWORD | --host-passwords FILE]
4451
4552
Examples:
4653
$0
4754
$0 --date 2026-08-25
4855
$0 --hosts 10.0.0.21
4956
$0 --hosts 10.0.0.21,10.0.0.22,kvm03.example.com
5057
$0 --date 2026-08-25 --hosts 10.0.0.21,10.0.0.22
58+
$0 --workdir /data/cloudstack-logs
59+
$0 --ssh-key /root/.ssh/id_rsa
60+
$0 --ssh-password 'secret'
61+
$0 --host-passwords /root/host-passwords.txt
5162
5263
Environment variables:
5364
SSH_USER=root
5465
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=10"
66+
SSH_KEY
67+
SSH_PASSWORD
5568
5669
Behavior:
5770
KVM host:
@@ -62,6 +75,24 @@ Behavior:
6275
If CloudMonkey is unavailable or API discovery fails, falls back to
6376
the CloudStack database.
6477
78+
Logs are pulled from other management servers and KVM hosts over
79+
SSH as \$SSH_USER, authenticating with (in order of precedence):
80+
1. A per-host password from --host-passwords, if the host appears
81+
in that file.
82+
2. A single password from --ssh-password or \$SSH_PASSWORD, applied
83+
to every remote host.
84+
3. A single private key from --ssh-key or \$SSH_KEY, applied to
85+
every remote host.
86+
4. Whatever SSH would otherwise use (agent, default identity
87+
files), if none of the above are given.
88+
89+
Password-based authentication requires the "sshpass" utility.
90+
91+
--host-passwords FILE:
92+
FILE contains one "host=password" pair per line (blank lines and
93+
lines starting with # are ignored). host must match the IP or
94+
hostname used to reach that server.
95+
6596
Without --date:
6697
Collects the current active log files.
6798
@@ -74,6 +105,14 @@ Behavior:
74105
75106
On a KVM host, --hosts is ignored because only the local agent log is
76107
collected.
108+
109+
Without --workdir:
110+
Collected logs and the resulting archive are placed in a directory
111+
under \${TMPDIR:-/tmp}.
112+
113+
With --workdir:
114+
Collected logs and the resulting archive are placed in the specified
115+
directory instead, which must already exist.
77116
EOF
78117
}
79118

@@ -104,6 +143,26 @@ while [[ $# -gt 0 ]]; do
104143
[[ -n "$host" ]] && REQUESTED_HOSTS+=("$host")
105144
done
106145

146+
shift 2
147+
;;
148+
--workdir)
149+
[[ $# -ge 2 ]] || die "--workdir requires a directory path"
150+
WORKDIR_BASE="$2"
151+
shift 2
152+
;;
153+
--ssh-key)
154+
[[ $# -ge 2 ]] || die "--ssh-key requires a file path"
155+
SSH_KEY="$2"
156+
shift 2
157+
;;
158+
--ssh-password)
159+
[[ $# -ge 2 ]] || die "--ssh-password requires a password"
160+
SSH_PASSWORD="$2"
161+
shift 2
162+
;;
163+
--host-passwords)
164+
[[ $# -ge 2 ]] || die "--host-passwords requires a file path"
165+
HOST_PASSWORDS_FILE="$2"
107166
shift 2
108167
;;
109168
-h|--help)
@@ -125,7 +184,29 @@ else
125184
SUFFIX="$(date '+%Y%m%d-%H%M%S')"
126185
fi
127186

128-
WORKDIR="cloudstack-logs-${SUFFIX}"
187+
WORKDIR_BASE="${WORKDIR_BASE:-${TMPDIR:-/tmp}}"
188+
[[ -d "$WORKDIR_BASE" ]] || die "Working directory does not exist: $WORKDIR_BASE"
189+
190+
if [[ -n "$SSH_KEY" ]]; then
191+
[[ -f "$SSH_KEY" ]] || die "SSH key not found: $SSH_KEY"
192+
fi
193+
194+
if [[ -n "$HOST_PASSWORDS_FILE" ]]; then
195+
[[ -r "$HOST_PASSWORDS_FILE" ]] || die "Cannot read host password file: $HOST_PASSWORDS_FILE"
196+
197+
while IFS='=' read -r host password; do
198+
host="$(printf '%s' "$host" | xargs)"
199+
[[ -z "$host" || "$host" == \#* ]] && continue
200+
HOST_PASSWORDS["$host"]="$password"
201+
done < "$HOST_PASSWORDS_FILE"
202+
fi
203+
204+
if [[ -n "$SSH_PASSWORD" || ${#HOST_PASSWORDS[@]} -gt 0 ]]; then
205+
command -v sshpass >/dev/null 2>&1 ||
206+
die "sshpass is required for password-based SSH authentication"
207+
fi
208+
209+
WORKDIR="${WORKDIR_BASE%/}/cloudstack-logs-${SUFFIX}"
129210
ARCHIVE="${WORKDIR}.tar.gz"
130211

131212
rm -rf "$WORKDIR"
@@ -203,19 +284,51 @@ collect_local_log() {
203284
fi
204285
}
205286

287+
host_password() {
288+
local host="$1"
289+
290+
if [[ -n "${HOST_PASSWORDS[$host]:-}" ]]; then
291+
printf '%s' "${HOST_PASSWORDS[$host]}"
292+
else
293+
printf '%s' "$SSH_PASSWORD"
294+
fi
295+
}
296+
206297
collect_remote_log() {
207298
local host="$1"
208299
local log_dir="$2"
209300
local log_name="$3"
210301
local output="$4"
302+
local password
303+
local passfile=""
304+
local ssh_cmd=(ssh)
305+
local ssh_opts="$SSH_OPTS"
306+
local rc
307+
308+
password="$(host_password "$host")"
309+
310+
if [[ -n "$password" ]]; then
311+
passfile="$(mktemp)" || return 1
312+
chmod 600 "$passfile"
313+
printf '%s' "$password" > "$passfile"
314+
ssh_cmd=(sshpass -f "$passfile" ssh)
315+
# BatchMode disables password/keyboard-interactive auth outright; an
316+
# earlier -o wins over a later one for the same key, so this has to
317+
# come before $SSH_OPTS to override any BatchMode=yes already in it.
318+
ssh_opts="-o BatchMode=no $ssh_opts"
319+
elif [[ -n "$SSH_KEY" ]]; then
320+
ssh_cmd=(ssh -i "$SSH_KEY")
321+
fi
211322

212323
if [[ -z "$DATE" ]]; then
213-
ssh ${SSH_OPTS} "${SSH_USER}@${host}" \
324+
"${ssh_cmd[@]}" ${ssh_opts} "${SSH_USER}@${host}" \
214325
"cat '${log_dir}/${log_name}'" > "$output"
215-
return
326+
rc=$?
327+
[[ -n "$passfile" ]] && rm -f "$passfile"
328+
return $rc
216329
fi
217330

218-
ssh ${SSH_OPTS} "${SSH_USER}@${host}" \
331+
"${ssh_cmd[@]}" ${ssh_opts} "${SSH_USER}@${host}" \
219332
bash -s -- "$log_dir" "$log_name" "$DATE" > "$output" <<'REMOTE'
220333
LOG_DIR="$1"
221334
LOG_NAME="$2"
@@ -247,6 +360,9 @@ awk -v requested_date="$REQUESTED_DATE" '
247360
}
248361
'
249362
REMOTE
363+
rc=$?
364+
[[ -n "$passfile" ]] && rm -f "$passfile"
365+
return $rc
250366
}
251367

252368
# --- CloudMonkey discovery ---
@@ -333,11 +449,26 @@ discover_with_cloudmonkey() {
333449
get_property() {
334450
local property="$1"
335451

336-
awk -F= -v property="$property" '
337-
$1 == property {
338-
sub(/^[^=]*=/, "")
339-
print
340-
exit
452+
awk -v property="$property" '
453+
{
454+
line = $0
455+
sub(/^[ \t]+/, "", line)
456+
sub(/[ \t]+$/, "", line)
457+
458+
if (line == "" || line ~ /^[#!]/) next
459+
460+
eq = index(line, "=")
461+
if (eq == 0) next
462+
463+
key = substr(line, 1, eq - 1)
464+
sub(/[ \t]+$/, "", key)
465+
466+
if (key == property) {
467+
value = substr(line, eq + 1)
468+
sub(/^[ \t]+/, "", value)
469+
print value
470+
exit
471+
}
341472
}
342473
' "$DB_PROPERTIES"
343474
}
@@ -349,6 +480,8 @@ query_cloudstack_db() {
349480
local db_name
350481
local db_user
351482
local db_password
483+
local defaults_file
484+
local rc
352485

353486
[[ -r "$DB_PROPERTIES" ]] || return 1
354487
command -v mysql >/dev/null 2>&1 || return 1
@@ -364,14 +497,27 @@ query_cloudstack_db() {
364497
db_port="${db_port:-3306}"
365498
db_name="${db_name:-cloud}"
366499

367-
MYSQL_PWD="$db_password" mysql \
500+
defaults_file="$(mktemp)" || return 1
501+
chmod 600 "$defaults_file"
502+
503+
{
504+
printf '[client]\n'
505+
printf 'host=%s\n' "$db_host"
506+
printf 'port=%s\n' "$db_port"
507+
printf 'user=%s\n' "$db_user"
508+
printf 'password=%s\n' "$db_password"
509+
} > "$defaults_file"
510+
511+
mysql \
512+
--defaults-extra-file="$defaults_file" \
368513
--batch \
369514
--skip-column-names \
370-
-h "$db_host" \
371-
-P "$db_port" \
372-
-u "$db_user" \
373515
"$db_name" \
374516
-e "$query"
517+
rc=$?
518+
519+
rm -f "$defaults_file"
520+
return $rc
375521
}
376522

377523
discover_with_database() {
@@ -445,10 +591,13 @@ collect_from_kvm_host() {
445591

446592
mkdir -p "$WORKDIR/kvm/$host_name"
447593

448-
collect_local_log \
594+
if ! collect_local_log \
449595
"$AGENT_LOG_DIR" \
450596
"$AGENT_LOG" \
451-
"$WORKDIR/kvm/$host_name/$AGENT_LOG"
597+
"$WORKDIR/kvm/$host_name/$AGENT_LOG"; then
598+
log "WARNING: Failed to collect local agent log"
599+
rm -f "$WORKDIR/kvm/$host_name/$AGENT_LOG"
600+
fi
452601
}
453602

454603
collect_from_management_server() {
@@ -478,6 +627,7 @@ collect_from_management_server() {
478627
"$MANAGEMENT_LOG" \
479628
"$WORKDIR/management/$host/$MANAGEMENT_LOG"; then
480629
log "WARNING: Failed to collect local management log"
630+
rm -f "$WORKDIR/management/$host/$MANAGEMENT_LOG"
481631
fi
482632
elif ! collect_remote_log \
483633
"$host" \
@@ -536,8 +686,11 @@ Requested KVM hosts: ${REQUESTED_HOSTS[*]:-all}
536686
EOF
537687

538688
log "Creating archive $ARCHIVE"
539-
tar -czf "$ARCHIVE" "$WORKDIR"
540-
rm -rf "$WORKDIR"
689+
if tar -czf "$ARCHIVE" -C "$WORKDIR_BASE" "$(basename "$WORKDIR")"; then
690+
rm -rf "$WORKDIR"
691+
else
692+
die "Failed to create archive $ARCHIVE; collected logs remain in $WORKDIR"
693+
fi
541694

542695
echo
543696
echo "CloudStack logs collected:"

0 commit comments

Comments
 (0)