mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-02-03 20:03:25 +01:00
Refactor: IP-Tag (#10558)
This commit is contained in:
committed by
GitHub
parent
0e2bbc2733
commit
3a35cc8282
@@ -57,7 +57,7 @@ spinner() {
|
||||
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
||||
local spin_i=0
|
||||
local interval=0.1
|
||||
|
||||
|
||||
trap 'exit 0' TERM INT
|
||||
printf "\e[?25l" 2>/dev/null
|
||||
|
||||
@@ -107,7 +107,6 @@ migrate_config() {
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Update existing installation
|
||||
update_installation() {
|
||||
msg_info "Updating IP-Tag Scripts"
|
||||
@@ -179,7 +178,7 @@ EOF
|
||||
systemctl daemon-reload &>/dev/null
|
||||
systemctl enable -q --now iptag.service &>/dev/null
|
||||
msg_ok "Updated IP-Tag Scripts"
|
||||
|
||||
|
||||
# Show configuration information after update
|
||||
show_post_install_info
|
||||
}
|
||||
@@ -187,7 +186,7 @@ EOF
|
||||
# Install only command without service
|
||||
install_command_only() {
|
||||
msg_info "Installing IP-Tag Command Only"
|
||||
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
if [[ ! -d "/opt/iptag" ]]; then
|
||||
mkdir -p /opt/iptag
|
||||
@@ -207,15 +206,15 @@ install_command_only() {
|
||||
echo -e "\n${YW}Configuration file already exists.${CL}"
|
||||
read -p "Do you want to reconfigure tag format? (y/n): " reconfigure
|
||||
case $reconfigure in
|
||||
[Yy]*)
|
||||
interactive_config_setup_command
|
||||
msg_info "Updating Configuration"
|
||||
generate_config >/opt/iptag/iptag.conf
|
||||
msg_ok "Updated configuration file"
|
||||
;;
|
||||
*)
|
||||
msg_ok "Keeping existing configuration file"
|
||||
;;
|
||||
[Yy]*)
|
||||
interactive_config_setup_command
|
||||
msg_info "Updating Configuration"
|
||||
generate_config >/opt/iptag/iptag.conf
|
||||
msg_ok "Updated configuration file"
|
||||
;;
|
||||
*)
|
||||
msg_ok "Keeping existing configuration file"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -240,7 +239,7 @@ exec "$SCRIPT_FILE"
|
||||
EOF
|
||||
chmod +x /usr/local/bin/iptag-run
|
||||
msg_ok "Created iptag-run command"
|
||||
|
||||
|
||||
msg_ok "IP-Tag Command installed successfully! Use 'iptag-run' to run manually."
|
||||
}
|
||||
|
||||
@@ -248,14 +247,14 @@ EOF
|
||||
show_post_install_info() {
|
||||
stop_spinner
|
||||
echo -e "\n${YW}=== Next Steps ===${CL}"
|
||||
|
||||
|
||||
# Show usage information
|
||||
if command -v iptag-run >/dev/null 2>&1; then
|
||||
echo -e "${YW}Run IP tagging manually: ${GN}iptag-run${CL}"
|
||||
echo -e "${YW}Add to cron for scheduled execution if needed${CL}"
|
||||
echo -e ""
|
||||
fi
|
||||
|
||||
|
||||
echo -e "${RD}IMPORTANT: Configure your network subnets!${CL}"
|
||||
echo -e ""
|
||||
echo -e "${YW}Configuration file: ${GN}/opt/iptag/iptag.conf${CL}"
|
||||
@@ -274,37 +273,37 @@ show_post_install_info() {
|
||||
# Interactive configuration setup for command-only (TAG_FORMAT only)
|
||||
interactive_config_setup_command() {
|
||||
echo -e "\n${YW}=== Configuration Setup ===${CL}"
|
||||
|
||||
|
||||
# TAG_FORMAT configuration
|
||||
echo -e "\n${YW}Select tag format:${CL}"
|
||||
echo -e "${GN}1)${CL} last_two_octets - Show last two octets (e.g., 0.100) [Default]"
|
||||
echo -e "${GN}2)${CL} last_octet - Show only last octet (e.g., 100)"
|
||||
echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)"
|
||||
|
||||
|
||||
while true; do
|
||||
read -p "Enter your choice (1-3) [1]: " tag_choice
|
||||
case ${tag_choice:-1} in
|
||||
1)
|
||||
TAG_FORMAT="last_two_octets"
|
||||
echo -e "${GN}✓ Selected: last_two_octets${CL}"
|
||||
break
|
||||
;;
|
||||
2)
|
||||
TAG_FORMAT="last_octet"
|
||||
echo -e "${GN}✓ Selected: last_octet${CL}"
|
||||
break
|
||||
;;
|
||||
3)
|
||||
TAG_FORMAT="full"
|
||||
echo -e "${GN}✓ Selected: full${CL}"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo -e "${RD}Please enter 1, 2, or 3.${CL}"
|
||||
;;
|
||||
1)
|
||||
TAG_FORMAT="last_two_octets"
|
||||
echo -e "${GN}✓ Selected: last_two_octets${CL}"
|
||||
break
|
||||
;;
|
||||
2)
|
||||
TAG_FORMAT="last_octet"
|
||||
echo -e "${GN}✓ Selected: last_octet${CL}"
|
||||
break
|
||||
;;
|
||||
3)
|
||||
TAG_FORMAT="full"
|
||||
echo -e "${GN}✓ Selected: full${CL}"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo -e "${RD}Please enter 1, 2, or 3.${CL}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
# Set default LOOP_INTERVAL for command mode
|
||||
LOOP_INTERVAL=300
|
||||
}
|
||||
@@ -312,46 +311,46 @@ interactive_config_setup_command() {
|
||||
# Interactive configuration setup for service (TAG_FORMAT + LOOP_INTERVAL)
|
||||
interactive_config_setup() {
|
||||
echo -e "\n${YW}=== Configuration Setup ===${CL}"
|
||||
|
||||
|
||||
# TAG_FORMAT configuration
|
||||
echo -e "\n${YW}Select tag format:${CL}"
|
||||
echo -e "${GN}1)${CL} last_two_octets - Show last two octets (e.g., 0.100) [Default]"
|
||||
echo -e "${GN}2)${CL} last_octet - Show only last octet (e.g., 100)"
|
||||
echo -e "${GN}3)${CL} full - Show full IP address (e.g., 192.168.0.100)"
|
||||
|
||||
|
||||
while true; do
|
||||
read -p "Enter your choice (1-3) [1]: " tag_choice
|
||||
case ${tag_choice:-1} in
|
||||
1)
|
||||
TAG_FORMAT="last_two_octets"
|
||||
echo -e "${GN}✓ Selected: last_two_octets${CL}"
|
||||
break
|
||||
;;
|
||||
2)
|
||||
TAG_FORMAT="last_octet"
|
||||
echo -e "${GN}✓ Selected: last_octet${CL}"
|
||||
break
|
||||
;;
|
||||
3)
|
||||
TAG_FORMAT="full"
|
||||
echo -e "${GN}✓ Selected: full${CL}"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo -e "${RD}Please enter 1, 2, or 3.${CL}"
|
||||
;;
|
||||
1)
|
||||
TAG_FORMAT="last_two_octets"
|
||||
echo -e "${GN}✓ Selected: last_two_octets${CL}"
|
||||
break
|
||||
;;
|
||||
2)
|
||||
TAG_FORMAT="last_octet"
|
||||
echo -e "${GN}✓ Selected: last_octet${CL}"
|
||||
break
|
||||
;;
|
||||
3)
|
||||
TAG_FORMAT="full"
|
||||
echo -e "${GN}✓ Selected: full${CL}"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo -e "${RD}Please enter 1, 2, or 3.${CL}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
# LOOP_INTERVAL configuration
|
||||
echo -e "\n${YW}Set check interval (in seconds):${CL}"
|
||||
echo -e "${YW}Default: 300 seconds (5 minutes)${CL}"
|
||||
echo -e "${YW}Recommended range: 300-3600 seconds${CL}"
|
||||
|
||||
|
||||
while true; do
|
||||
read -p "Enter interval in seconds [300]: " interval_input
|
||||
interval_input=${interval_input:-300}
|
||||
|
||||
|
||||
if [[ $interval_input =~ ^[0-9]+$ ]] && [ $interval_input -ge 300 ] && [ $interval_input -le 7200 ]; then
|
||||
LOOP_INTERVAL=$interval_input
|
||||
echo -e "${GN}✓ Selected: ${LOOP_INTERVAL} seconds${CL}"
|
||||
@@ -646,21 +645,40 @@ get_vm_ips() {
|
||||
echo "$unique_ips"
|
||||
}
|
||||
|
||||
# Cache for configs to avoid repeated reads
|
||||
declare -A CONFIG_CACHE
|
||||
declare -A IP_CACHE
|
||||
|
||||
# Update tags for container or VM
|
||||
update_tags() {
|
||||
local type="$1" vmid="$2"
|
||||
local current_ips_full
|
||||
local current_tags_raw=""
|
||||
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
current_ips_full=$(get_lxc_ips "${vmid}")
|
||||
while IFS= read -r line; do
|
||||
[[ "$line" == tags:* ]] && current_tags_raw="${line#tags: }" && break
|
||||
done < <(pct config "$vmid" 2>/dev/null)
|
||||
# Get IPs with caching
|
||||
local cache_key="${type}_${vmid}"
|
||||
if [[ -n "${IP_CACHE[$cache_key]:-}" ]]; then
|
||||
current_ips_full="${IP_CACHE[$cache_key]}"
|
||||
debug_log "$type $vmid: using cached IPs"
|
||||
else
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
current_ips_full=$(get_lxc_ips "${vmid}")
|
||||
else
|
||||
current_ips_full=$(get_vm_ips "${vmid}")
|
||||
fi
|
||||
IP_CACHE[$cache_key]="$current_ips_full"
|
||||
fi
|
||||
|
||||
# Get current tags (optimized file reading)
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
local config_file="/etc/pve/lxc/${vmid}.conf"
|
||||
if [[ -f "$config_file" ]]; then
|
||||
current_tags_raw=$(grep "^tags:" "$config_file" 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//')
|
||||
fi
|
||||
else
|
||||
current_ips_full=$(get_vm_ips "${vmid}")
|
||||
local vm_config="/etc/pve/qemu-server/${vmid}.conf"
|
||||
if [[ -f "$vm_config" ]]; then
|
||||
local current_tags_raw=$(grep "^tags:" "$vm_config" 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//')
|
||||
current_tags_raw=$(grep "^tags:" "$vm_config" 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//')
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -818,11 +836,15 @@ update_tags() {
|
||||
update_all_tags() {
|
||||
local type="$1" vmids count=0
|
||||
|
||||
# Get list of all containers/VMs
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
vmids=($(pct list 2>/dev/null | grep -v VMID | awk '{print $1}'))
|
||||
else
|
||||
local all_vm_configs=($(ls /etc/pve/qemu-server/*.conf 2>/dev/null | sed 's/.*\/\([0-9]*\)\.conf/\1/' | sort -n))
|
||||
vmids=("${all_vm_configs[@]}")
|
||||
# More efficient: direct file listing instead of ls+sed
|
||||
vmids=()
|
||||
for conf in /etc/pve/qemu-server/*.conf 2>/dev/null; do
|
||||
[[ -f "$conf" ]] && vmids+=("${conf##*/}" | sed 's/\.conf$//')
|
||||
done
|
||||
fi
|
||||
|
||||
count=${#vmids[@]}
|
||||
@@ -830,14 +852,16 @@ update_all_tags() {
|
||||
|
||||
# Display processing header with color
|
||||
if [[ "$type" == "lxc" ]]; then
|
||||
log_info "Processing ${WHITE}${count}${NC} LXC container(s) sequentially"
|
||||
log_info "Processing ${WHITE}${count}${NC} LXC container(s)"
|
||||
else
|
||||
log_info "Processing ${WHITE}${count}${NC} virtual machine(s) sequentially"
|
||||
log_info "Processing ${WHITE}${count}${NC} virtual machine(s)"
|
||||
fi
|
||||
|
||||
# Process each VM/LXC container sequentially
|
||||
# Process each VM/LXC container
|
||||
local processed=0
|
||||
for vmid in "${vmids[@]}"; do
|
||||
update_tags "$type" "$vmid"
|
||||
((processed++))
|
||||
done
|
||||
|
||||
# Add completion message
|
||||
@@ -848,32 +872,25 @@ update_all_tags() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if status changed
|
||||
check_status_changed() {
|
||||
local type="$1" current
|
||||
case "$type" in
|
||||
"lxc") current=$(pct list 2>/dev/null | grep -v VMID) ;;
|
||||
"vm") current=$(ls -la /etc/pve/qemu-server/*.conf 2>/dev/null) ;;
|
||||
"fw") current=$(ip link show type bridge 2>/dev/null) ;;
|
||||
esac
|
||||
local last_var="last_${type}_status"
|
||||
[[ "${!last_var}" == "$current" ]] && return 1
|
||||
eval "$last_var='$current'"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Main check function
|
||||
check() {
|
||||
local current_time=$(date +%s)
|
||||
local start_time=$(date +%s)
|
||||
|
||||
# Simple periodic check - always update both LXC and VM every loop
|
||||
log_info "Starting periodic check"
|
||||
|
||||
# Clear caches before each run
|
||||
CONFIG_CACHE=()
|
||||
IP_CACHE=()
|
||||
|
||||
# Update LXC containers
|
||||
update_all_tags "lxc"
|
||||
|
||||
# Update VMs
|
||||
update_all_tags "vm"
|
||||
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - start_time))
|
||||
log_success "Check completed in ${WHITE}${duration}${NC} seconds"
|
||||
}
|
||||
|
||||
# Main loop
|
||||
@@ -917,40 +934,54 @@ get_lxc_ips() {
|
||||
|
||||
local ips=""
|
||||
|
||||
# Method 1: Check Proxmox config for static IP
|
||||
# Method 1: Check Proxmox config for ALL static IPs (multiple interfaces)
|
||||
local pve_lxc_config="/etc/pve/lxc/${vmid}.conf"
|
||||
if [[ -f "$pve_lxc_config" ]]; then
|
||||
local static_ip=$(grep -E "^net[0-9]+:" "$pve_lxc_config" 2>/dev/null | grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d'=' -f2 | head -1)
|
||||
if [[ -n "$static_ip" && "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
debug_log "lxc $vmid: found static IP $static_ip in config"
|
||||
ips="$static_ip"
|
||||
local static_ips=$(grep -E "^net[0-9]+:" "$pve_lxc_config" 2>/dev/null | grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d'=' -f2)
|
||||
if [[ -n "$static_ips" ]]; then
|
||||
while IFS= read -r ip; do
|
||||
if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
debug_log "lxc $vmid: found static IP $ip in config"
|
||||
ips="${ips}${ips:+ }${ip}"
|
||||
fi
|
||||
done <<< "$static_ips"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Method 2: ARP table lookup if no static IP
|
||||
# Method 2: ARP table lookup for ALL MAC addresses if no static IPs found
|
||||
if [[ -z "$ips" && -f "$pve_lxc_config" ]]; then
|
||||
local mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | head -1 | cut -d'=' -f2)
|
||||
if [[ -n "$mac_addr" ]]; then
|
||||
local bridge_name=$(grep -Eo 'bridge=[^,]+' "$pve_lxc_config" | head -1 | cut -d'=' -f2)
|
||||
local arp_ip=$(ip neighbor show | grep "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
||||
if [[ -n "$arp_ip" && "$arp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
debug_log "lxc $vmid: found IP $arp_ip via ARP table"
|
||||
ips="$arp_ip"
|
||||
fi
|
||||
local mac_addrs=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | cut -d'=' -f2)
|
||||
if [[ -n "$mac_addrs" ]]; then
|
||||
while IFS= read -r mac_addr; do
|
||||
[[ -z "$mac_addr" ]] && continue
|
||||
local arp_ip=$(ip neighbor show | grep -i "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
||||
if [[ -n "$arp_ip" && "$arp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
debug_log "lxc $vmid: found IP $arp_ip via ARP table for MAC $mac_addr"
|
||||
ips="${ips}${ips:+ }${arp_ip}"
|
||||
fi
|
||||
done <<< "$mac_addrs"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Method 3: Direct container command if ARP failed
|
||||
# Method 3: Direct container command to get ALL IPs if previous methods failed
|
||||
if [[ -z "$ips" ]]; then
|
||||
local container_ip=$(timeout 5s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1)
|
||||
if [[ -n "$container_ip" ]] && is_valid_ipv4 "$container_ip"; then
|
||||
debug_log "lxc $vmid: found IP $container_ip via pct exec"
|
||||
ips="$container_ip"
|
||||
local container_ips=$(timeout 5s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE 'inet ([0-9]{1,3}\.){3}[0-9]{1,3}' | awk '{print $2}' | grep -v '127.0.0.1')
|
||||
if [[ -n "$container_ips" ]]; then
|
||||
while IFS= read -r ip; do
|
||||
if is_valid_ipv4 "$ip"; then
|
||||
debug_log "lxc $vmid: found IP $ip via pct exec"
|
||||
ips="${ips}${ips:+ }${ip}"
|
||||
fi
|
||||
done <<< "$container_ips"
|
||||
fi
|
||||
fi
|
||||
|
||||
debug_log "lxc $vmid: final IPs: '$ips'"
|
||||
echo "$ips"
|
||||
# Remove duplicates and clean up
|
||||
local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ')
|
||||
unique_ips="${unique_ips% }"
|
||||
|
||||
debug_log "lxc $vmid: final IPs: '$unique_ips'"
|
||||
echo "$unique_ips"
|
||||
}
|
||||
|
||||
main
|
||||
@@ -967,28 +998,28 @@ echo -e "${RD}4)${CL} Cancel"
|
||||
while true; do
|
||||
read -p "Enter your choice (1-4): " choice
|
||||
case $choice in
|
||||
1)
|
||||
INSTALL_MODE="service"
|
||||
echo -e "${GN}✓ Selected: Service installation${CL}"
|
||||
break
|
||||
;;
|
||||
2)
|
||||
INSTALL_MODE="command"
|
||||
echo -e "${GN}✓ Selected: Command-only installation${CL}"
|
||||
break
|
||||
;;
|
||||
3)
|
||||
echo -e "${GN}✓ Selected: Update installation${CL}"
|
||||
update_installation
|
||||
exit 0
|
||||
;;
|
||||
4)
|
||||
msg_error "Action cancelled."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
msg_error "Please enter 1, 2, 3, or 4."
|
||||
;;
|
||||
1)
|
||||
INSTALL_MODE="service"
|
||||
echo -e "${GN}✓ Selected: Service installation${CL}"
|
||||
break
|
||||
;;
|
||||
2)
|
||||
INSTALL_MODE="command"
|
||||
echo -e "${GN}✓ Selected: Command-only installation${CL}"
|
||||
break
|
||||
;;
|
||||
3)
|
||||
echo -e "${GN}✓ Selected: Update installation${CL}"
|
||||
update_installation
|
||||
exit 0
|
||||
;;
|
||||
4)
|
||||
msg_error "Action cancelled."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
msg_error "Please enter 1, 2, 3, or 4."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -1043,15 +1074,15 @@ if [[ "$INSTALL_MODE" == "service" ]]; then
|
||||
echo -e "\n${YW}Configuration file already exists.${CL}"
|
||||
read -p "Do you want to reconfigure tag format and loop interval? (y/n): " reconfigure
|
||||
case $reconfigure in
|
||||
[Yy]*)
|
||||
interactive_config_setup
|
||||
msg_info "Updating Configuration"
|
||||
generate_config >/opt/iptag/iptag.conf
|
||||
msg_ok "Updated configuration file"
|
||||
;;
|
||||
*)
|
||||
msg_ok "Keeping existing configuration file"
|
||||
;;
|
||||
[Yy]*)
|
||||
interactive_config_setup
|
||||
msg_info "Updating Configuration"
|
||||
generate_config >/opt/iptag/iptag.conf
|
||||
msg_ok "Updated configuration file"
|
||||
;;
|
||||
*)
|
||||
msg_ok "Keeping existing configuration file"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@@ -1083,21 +1114,21 @@ exec "$SCRIPT_FILE"
|
||||
EOF
|
||||
chmod +x /usr/local/bin/iptag-run
|
||||
msg_ok "Created iptag-run command"
|
||||
|
||||
|
||||
echo -e "\n${GN}${APP} service installation completed successfully! ${CL}"
|
||||
echo -e "${YW}The service is now running automatically.${CL}"
|
||||
echo -e "${YW}You can also run it manually with: ${GN}iptag-run${CL}\n"
|
||||
|
||||
|
||||
# Show configuration information
|
||||
show_post_install_info
|
||||
|
||||
|
||||
elif [[ "$INSTALL_MODE" == "command" ]]; then
|
||||
# Command-only installation
|
||||
install_command_only
|
||||
|
||||
|
||||
stop_spinner
|
||||
echo -e "\n${GN}${APP} command installation completed successfully! ${CL}"
|
||||
|
||||
|
||||
# Show configuration information
|
||||
show_post_install_info
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user