arr-stack: add auto IP allocation with ping check
Third IP entry mode "auto": user supplies a starting IP, a range (10.0.0.50-99), or comma-separated ranges; orchestrator walks the candidates with `ping -c 1 -W 1` and grabs the first N that don't respond. Skips the gateway and any IPs already assigned this run. Confirms the picks via yes/no whiptail before locking them in. Adds iputils-ping to ensure_dependencies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -278,19 +278,120 @@ compute_ordered_slugs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pick_ip_mode_and_ips() {
|
pick_ip_mode_and_ips() {
|
||||||
|
while true; do
|
||||||
local mode
|
local mode
|
||||||
mode=$(whiptail --backtitle "$BACKTITLE" \
|
mode=$(whiptail --backtitle "$BACKTITLE" \
|
||||||
--title "IP Entry Mode" \
|
--title "IP Entry Mode" \
|
||||||
--menu "How would you like to enter IP addresses?" 14 70 2 \
|
--menu "How would you like to enter IP addresses?" 15 75 3 \
|
||||||
"list" "Enter all IPs at once (space- or comma-separated)" \
|
"list" "Enter all IPs at once (space- or comma-separated)" \
|
||||||
"one_by_one" "Prompt per container" \
|
"one_by_one" "Prompt per container" \
|
||||||
|
"auto" "Auto-pick free IPs from a starting IP or range(s)" \
|
||||||
3>&1 1>&2 2>&3) || cancelled "IP entry mode pick"
|
3>&1 1>&2 2>&3) || cancelled "IP entry mode pick"
|
||||||
|
|
||||||
if [[ "$mode" == "list" ]]; then
|
case "$mode" in
|
||||||
_collect_ips_list_mode
|
list) _collect_ips_list_mode; return ;;
|
||||||
else
|
one_by_one) _collect_ips_one_by_one; return ;;
|
||||||
_collect_ips_one_by_one
|
auto) _collect_ips_auto && return ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_parse_ip_ranges() {
|
||||||
|
local expr=$1
|
||||||
|
local -a segments
|
||||||
|
IFS=',' read -ra segments <<<"$expr"
|
||||||
|
local seg prefix start end i
|
||||||
|
for seg in "${segments[@]}"; do
|
||||||
|
seg="${seg// /}"
|
||||||
|
[[ -z "$seg" ]] && continue
|
||||||
|
if [[ "$seg" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)-([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)$ ]]; then
|
||||||
|
if [[ "${BASH_REMATCH[1]}" != "${BASH_REMATCH[3]}" ]]; then
|
||||||
|
echo "ERR: cross-subnet range not supported: $seg" >&2; return 1
|
||||||
fi
|
fi
|
||||||
|
prefix=${BASH_REMATCH[1]}; start=${BASH_REMATCH[2]}; end=${BASH_REMATCH[4]}
|
||||||
|
elif [[ "$seg" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)-([0-9]+)$ ]]; then
|
||||||
|
prefix=${BASH_REMATCH[1]}; start=${BASH_REMATCH[2]}; end=${BASH_REMATCH[3]}
|
||||||
|
elif [[ "$seg" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)$ ]]; then
|
||||||
|
prefix=${BASH_REMATCH[1]}; start=${BASH_REMATCH[2]}; end=254
|
||||||
|
else
|
||||||
|
echo "ERR: invalid segment: $seg" >&2; return 1
|
||||||
|
fi
|
||||||
|
if (( start > end || start < 0 || end > 255 )); then
|
||||||
|
echo "ERR: out-of-range octet: $seg" >&2; return 1
|
||||||
|
fi
|
||||||
|
for ((i=start; i<=end; i++)); do
|
||||||
|
echo "${prefix}${i}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_ip_is_free() {
|
||||||
|
local ip=$1
|
||||||
|
[[ "$ip" == "$var_gateway" ]] && return 1
|
||||||
|
if ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_collect_ips_auto() {
|
||||||
|
local expected_n=${#ORDERED_SLUGS[@]}
|
||||||
|
local hint="Examples:"$'\n'" 10.0.0.50 (start, scans upward to .254)"$'\n'" 10.0.0.50-99 (single range)"$'\n'" 10.0.0.50-60,10.0.0.80-99 (multiple ranges)"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
local expr
|
||||||
|
expr=$(whiptail --backtitle "$BACKTITLE" \
|
||||||
|
--title "Auto IP allocation" \
|
||||||
|
--inputbox "Need ${expected_n} free IPs. Enter a starting IP or range expression:"$'\n\n'"${hint}" \
|
||||||
|
16 78 "" 3>&1 1>&2 2>&3) || return 1
|
||||||
|
|
||||||
|
local -a parsed=()
|
||||||
|
while IFS= read -r ip; do
|
||||||
|
[[ -n "$ip" ]] && parsed+=("$ip")
|
||||||
|
done < <(_parse_ip_ranges "$expr" 2>/dev/null)
|
||||||
|
|
||||||
|
if (( ${#parsed[@]} == 0 )); then
|
||||||
|
whiptail --backtitle "$BACKTITLE" --title "Invalid" \
|
||||||
|
--msgbox "Could not parse any IPs from: ${expr}"$'\n\n'"Try a starting IP, a range like 10.0.0.50-99, or comma-separated ranges." 12 70
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
msg_info "Pinging ${#parsed[@]} candidate(s) for ${expected_n} free IP(s)..."
|
||||||
|
local -a found=()
|
||||||
|
local ip already used
|
||||||
|
for ip in "${parsed[@]}"; do
|
||||||
|
(( ${#found[@]} >= expected_n )) && break
|
||||||
|
already=0
|
||||||
|
for used in "${IP_BY_SLUG[@]}"; do
|
||||||
|
[[ "$used" == "$ip" ]] && { already=1; break; }
|
||||||
|
done
|
||||||
|
(( already )) && continue
|
||||||
|
if _ip_is_free "$ip"; then
|
||||||
|
found+=("$ip")
|
||||||
|
echo " free: ${ip}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#found[@]} < expected_n )); then
|
||||||
|
whiptail --backtitle "$BACKTITLE" --title "Not enough free IPs" \
|
||||||
|
--msgbox "Found ${#found[@]}/${expected_n} free IPs in the range. Widen the range and try again." 10 70
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
local lines="" i
|
||||||
|
for i in "${!ORDERED_SLUGS[@]}"; do
|
||||||
|
lines+=" $(printf '%-12s -> %s' "${ORDERED_SLUGS[$i]}" "${found[$i]}")"$'\n'
|
||||||
|
done
|
||||||
|
if ! whiptail --backtitle "$BACKTITLE" --title "Confirm auto-assigned IPs" \
|
||||||
|
--yesno "Free IPs found:"$'\n\n'"${lines}"$'\n'"Use these?" 22 70; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in "${!ORDERED_SLUGS[@]}"; do
|
||||||
|
IP_BY_SLUG[${ORDERED_SLUGS[$i]}]=${found[$i]}
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
_collect_ips_list_mode() {
|
_collect_ips_list_mode() {
|
||||||
@@ -860,7 +961,7 @@ main() {
|
|||||||
header_info
|
header_info
|
||||||
check_root
|
check_root
|
||||||
check_pve_tools
|
check_pve_tools
|
||||||
ensure_dependencies curl whiptail jq
|
ensure_dependencies curl whiptail jq iputils-ping
|
||||||
seed_catalog
|
seed_catalog
|
||||||
pick_storage
|
pick_storage
|
||||||
pick_network_defaults
|
pick_network_defaults
|
||||||
|
|||||||
Reference in New Issue
Block a user