Skip to content

Commit e52cd7e

Browse files
NokiPolynomialDivision
authored andcommitted
scripts: script to check OpenWRT versions running on accessible hosts
1 parent 94d90ec commit e52cd7e

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed

check-versions.sh

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
#!/bin/bash
2+
# shellcheck disable=SC2016
3+
4+
set -euo pipefail
5+
6+
WORK_DIR="tmp"
7+
OPENWRT_REPO="$WORK_DIR/openwrt"
8+
OUTPUT_FILE="$WORK_DIR/versions-$(date +%Y%m%d-%H%M%S).txt"
9+
SSH_OPTS=(-q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes -o ConnectTimeout=5 -o ServerAliveInterval=1 -o ServerAliveCountMax=3)
10+
11+
LOCATION_FILTER=""
12+
UPDATE_THRESHOLD_DAYS=0
13+
14+
usage() {
15+
echo "Usage: $0 [-l LOCATION] [-d DAYS] [-h]"
16+
echo ""
17+
echo "Options:"
18+
echo " -l LOCATION Filter hosts to a specific location (e.g., w38b)"
19+
echo " -d DAYS Only add hosts to update list if outdated by at least DAYS (default: 0)"
20+
echo " -h Show this help message"
21+
echo ""
22+
echo "Examples:"
23+
echo " $0 Check all hosts"
24+
echo " $0 -l w38b Check only w38b hosts"
25+
echo " $0 -d 7 Only add hosts outdated by 7+ days to update list"
26+
exit 1
27+
}
28+
29+
while getopts "l:d:h" opt; do
30+
case $opt in
31+
l)
32+
LOCATION_FILTER="$OPTARG"
33+
;;
34+
d)
35+
UPDATE_THRESHOLD_DAYS="$OPTARG"
36+
;;
37+
h)
38+
usage
39+
;;
40+
*)
41+
usage
42+
;;
43+
esac
44+
done
45+
46+
get_model_version() {
47+
local model="$1"
48+
local model_file="group_vars/model_${model//-/_}.yml"
49+
yq -r '.openwrt_version' "$model_file" 2>/dev/null || echo ""
50+
}
51+
52+
check_host() {
53+
local hostname="$1"
54+
local model="$2"
55+
local ssh_host="${hostname}.ff"
56+
57+
local version_info
58+
version_info=$(timeout 20 ssh "${SSH_OPTS[@]}" "root@$ssh_host" "cat /etc/openwrt_release" 2>/dev/null) || return 1
59+
60+
local openwrt_version build_rev build_hash expected_version
61+
openwrt_version=$(echo "$version_info" | grep -E "^DISTRIB_RELEASE=" | cut -d"'" -f2)
62+
build_rev=$(echo "$version_info" | grep -E "^DISTRIB_REVISION=" | cut -d"'" -f2 | grep -oE "^r[0-9]+")
63+
build_hash=$(echo "$version_info" | grep -E "^DISTRIB_REVISION=" | cut -d"'" -f2 | grep -oE "[a-f0-9]+$")
64+
expected_version=$(get_model_version "$model")
65+
66+
echo "$hostname|$openwrt_version|$expected_version|$build_rev|$build_hash"
67+
}
68+
69+
clone_or_update_repo() {
70+
if [ -d "$OPENWRT_REPO/.git" ]; then
71+
echo "Updating OpenWRT repository..."
72+
git -C "$OPENWRT_REPO" fetch --quiet origin 'refs/heads/*:refs/remotes/origin/*'
73+
else
74+
echo "Cloning OpenWRT repository..."
75+
mkdir -p "$WORK_DIR"
76+
git clone --quiet git@github.com:openwrt/openwrt.git "$OPENWRT_REPO"
77+
git -C "$OPENWRT_REPO" fetch --quiet origin 'refs/heads/*:refs/remotes/origin/*'
78+
fi
79+
}
80+
81+
init_output() {
82+
mkdir -p "$(dirname "$OUTPUT_FILE")"
83+
: >"$OUTPUT_FILE"
84+
}
85+
86+
log() {
87+
echo "$@" | tee -a "$OUTPUT_FILE"
88+
}
89+
90+
version_to_branch() {
91+
local version="$1"
92+
local major minor
93+
version=$(echo "$version" | sed 's/-SNAPSHOT//' | sed 's/-.*//')
94+
major=$(echo "$version" | cut -d. -f1)
95+
minor=$(echo "$version" | cut -d. -f2)
96+
if [[ "$major" =~ ^[0-9]+$ ]] && [[ "$minor" =~ ^[0-9]+$ ]]; then
97+
echo "openwrt-$major.$minor"
98+
else
99+
echo "main"
100+
fi
101+
}
102+
103+
get_commit_date() {
104+
local repo="$1"
105+
local hash="$2"
106+
git -C "$repo" log -1 --format="%ct" "$hash" 2>/dev/null
107+
}
108+
109+
get_branch_tip() {
110+
local repo="$1"
111+
local branch="$2"
112+
git -C "$repo" rev-parse "origin/$branch" 2>/dev/null || git -C "$repo" rev-parse "$branch" 2>/dev/null
113+
}
114+
115+
time_ago() {
116+
local seconds="$1"
117+
local minutes hours days weeks months years
118+
119+
seconds=${seconds#-}
120+
121+
if [ "$seconds" -eq 0 ]; then
122+
echo "up-to-date"
123+
return
124+
fi
125+
126+
if [ "$seconds" -lt 60 ]; then
127+
echo "${seconds}s ago"
128+
return
129+
fi
130+
131+
minutes=$((seconds / 60))
132+
if [ "$minutes" -lt 60 ]; then
133+
echo "${minutes}m ago"
134+
return
135+
fi
136+
137+
hours=$((minutes / 60))
138+
if [ "$hours" -lt 24 ]; then
139+
echo "${hours}h ago"
140+
return
141+
fi
142+
143+
days=$((hours / 24))
144+
if [ "$days" -lt 30 ]; then
145+
echo "${days}d ago"
146+
return
147+
fi
148+
149+
weeks=$((days / 7))
150+
if [ "$weeks" -lt 12 ]; then
151+
echo "${weeks}w ago"
152+
return
153+
fi
154+
155+
months=$((days / 30))
156+
if [ "$months" -lt 12 ]; then
157+
echo "${months}mo ago"
158+
return
159+
fi
160+
161+
years=$((days / 365))
162+
echo "${years}y ago"
163+
}
164+
165+
check_outdated() {
166+
local repo="$1"
167+
local hash="$2"
168+
local branch="$3"
169+
170+
local local_date tip_hash tip_date
171+
local_date=$(get_commit_date "$repo" "$hash")
172+
tip_hash=$(get_branch_tip "$repo" "$branch")
173+
174+
if [ -z "$local_date" ] || [ -z "$tip_hash" ]; then
175+
echo "?|0"
176+
return
177+
fi
178+
179+
if [ "${tip_hash:0:${#hash}}" = "$hash" ]; then
180+
echo "OK|0"
181+
return
182+
fi
183+
184+
tip_date=$(get_commit_date "$repo" "$tip_hash")
185+
if [ -z "$tip_date" ]; then
186+
echo "?|0"
187+
return
188+
fi
189+
190+
local diff=$((tip_date - local_date))
191+
echo "$(time_ago $diff)|$diff"
192+
}
193+
194+
init_output
195+
196+
echo "Extracting hosts from location files..."
197+
if [ -n "$LOCATION_FILTER" ]; then
198+
mapfile -t HOSTS < <(yq -r '.model as $loc_model | .hosts[] | "\(.hostname)\t\(.model // $loc_model)"' "locations/${LOCATION_FILTER}.yml" 2>/dev/null)
199+
else
200+
mapfile -t HOSTS < <(yq -r '.model as $loc_model | .hosts[] | "\(.hostname)\t\(.model // $loc_model)"' locations/*.yml 2>/dev/null)
201+
fi
202+
203+
log "Checking ${#HOSTS[@]} hosts..."
204+
log ""
205+
206+
results=()
207+
unreachable=()
208+
209+
for entry in "${HOSTS[@]}"; do
210+
hostname="${entry%%$'\t'*}"
211+
model="${entry##*$'\t'}"
212+
if result=$(check_host "$hostname" "$model"); then
213+
results+=("$result")
214+
log "$hostname OK"
215+
else
216+
unreachable+=("$hostname")
217+
log "$hostname unreachable"
218+
fi
219+
done
220+
221+
if [ ${#results[@]} -eq 0 ]; then
222+
echo "No reachable hosts found."
223+
exit 0
224+
fi
225+
226+
log ""
227+
log "Updating OpenWRT repository..."
228+
clone_or_update_repo
229+
230+
declare -A branch_cache
231+
for result in "${results[@]}"; do
232+
ver=$(echo "$result" | cut -d'|' -f2)
233+
if [ -z "${branch_cache[$ver]:-}" ]; then
234+
branch_cache[$ver]=$(version_to_branch "$ver")
235+
fi
236+
done
237+
238+
UPDATE_TMP="$WORK_DIR/update.tmp"
239+
UPDATE_FILE="$WORK_DIR/update.txt"
240+
: >"$UPDATE_TMP"
241+
242+
UPDATE_THRESHOLD_SECONDS=$((UPDATE_THRESHOLD_DAYS * 86400))
243+
244+
log ""
245+
log "========================================"
246+
log ""
247+
248+
log "$(printf '| %-64s | %-14s | %-14s | %-9s | %-9s | %-26s |' 'HOST' 'OPENWRT' 'EXPECTED' 'BUILD_REV' 'BUILD_HASH' 'STATUS')"
249+
log '| :--------------------------------------------------------------- | :------------- | :------------- | --------: | :--------- | :------------------------- |'
250+
251+
printf '%s\n' "${results[@]}" | sort -t'|' -k2,2V -k3,3V -k4,4V -k5,5V -k1,1 | while IFS='|' read -r host ver expected rev hash; do
252+
branch="${branch_cache[$ver]}"
253+
status_info=$(check_outdated "$OPENWRT_REPO" "$hash" "$branch")
254+
status="${status_info%|*}"
255+
age_seconds="${status_info#*|}"
256+
if [ "$ver" != "$expected" ]; then
257+
display_expected="$expected"
258+
else
259+
display_expected=""
260+
fi
261+
if [[ "$expected" == 24.10* ]] || [[ "$expected" == 25.12* ]] || [[ "$expected" == "SNAPSHOT" ]]; then
262+
if [ "$status" != "OK" ] && [ "$age_seconds" -ge "$UPDATE_THRESHOLD_SECONDS" ]; then
263+
echo "$host" >>"$UPDATE_TMP"
264+
fi
265+
fi
266+
log "$(printf '| %-64s | %-14s | %-14s | %9s | %-9s | %-26s |' "$host" "$ver" "$display_expected" "$rev" "$hash" "$status")"
267+
done
268+
269+
sort -u "$UPDATE_TMP" >"$UPDATE_FILE"
270+
rm -f "$UPDATE_TMP"
271+
update_count=$(wc -l <"$UPDATE_FILE")
272+
echo "Update list saved to: $UPDATE_FILE ($update_count hosts)"
273+
274+
log ""
275+
log "========================================"
276+
log ""
277+
log "Summary: ${#results[@]} reachable, ${#unreachable[@]} unreachable"
278+
279+
if [ ${#unreachable[@]} -gt 0 ]; then
280+
log ""
281+
log "Unreachable hosts:"
282+
for h in "${unreachable[@]}"; do
283+
log " - $h"
284+
done
285+
fi
286+
287+
echo ""
288+
echo "Output saved to: $OUTPUT_FILE"

0 commit comments

Comments
 (0)