420 lines
14 KiB
Bash
Executable File
420 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# List all private IP address assignments in Proxmox
|
|
# Works both via API (remote) and direct commands (on Proxmox host)
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Try to load environment if available
|
|
if [[ -f "$SCRIPT_DIR/load-env.sh" ]]; then
|
|
source "$SCRIPT_DIR/load-env.sh"
|
|
load_env_file 2>/dev/null || true
|
|
fi
|
|
|
|
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
|
|
PROXMOX_PORT="${PROXMOX_PORT:-8006}"
|
|
|
|
# Check if running on Proxmox host
|
|
ON_PROXMOX_HOST=false
|
|
if command -v pct >/dev/null 2>&1 && command -v qm >/dev/null 2>&1; then
|
|
ON_PROXMOX_HOST=true
|
|
fi
|
|
|
|
# Function to check if IP is private
|
|
is_private_ip() {
|
|
local ip="$1"
|
|
# Private IP ranges:
|
|
# 10.0.0.0/8
|
|
# 172.16.0.0/12
|
|
# 192.168.0.0/16
|
|
if [[ "$ip" =~ ^10\. ]] || \
|
|
[[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]] || \
|
|
[[ "$ip" =~ ^192\.168\. ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Function to get LXC container IP from config
|
|
get_lxc_config_ip() {
|
|
local vmid="$1"
|
|
local config_file="/etc/pve/lxc/${vmid}.conf"
|
|
|
|
if [[ -f "$config_file" ]]; then
|
|
# Extract IP from net0: or net1: lines
|
|
local ip=$(grep -E "^net[0-9]+:" "$config_file" 2>/dev/null | \
|
|
grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | \
|
|
cut -d'=' -f2 | head -1)
|
|
if [[ -n "$ip" ]]; then
|
|
# Remove CIDR notation if present
|
|
echo "${ip%/*}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to get LXC container actual IP (if running)
|
|
get_lxc_actual_ip() {
|
|
local vmid="$1"
|
|
|
|
if ! pct status "$vmid" 2>/dev/null | grep -q "status: running"; then
|
|
return
|
|
fi
|
|
|
|
# Try to get IP from container
|
|
local ip=$(timeout 3s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | \
|
|
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]+' | \
|
|
grep -v '127.0.0.1' | head -1 | cut -d'/' -f1)
|
|
|
|
if [[ -n "$ip" ]]; then
|
|
echo "$ip"
|
|
fi
|
|
}
|
|
|
|
# Function to get VM IP from config (cloud-init or static)
|
|
get_vm_config_ip() {
|
|
local vmid="$1"
|
|
local config_file="/etc/pve/qemu-server/${vmid}.conf"
|
|
|
|
if [[ -f "$config_file" ]]; then
|
|
# Check for cloud-init IP configuration
|
|
local ip=$(grep -E "^ipconfig[0-9]+:" "$config_file" 2>/dev/null | \
|
|
grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | \
|
|
cut -d'=' -f2 | head -1)
|
|
if [[ -n "$ip" ]]; then
|
|
echo "${ip%/*}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to get VM actual IP via guest agent
|
|
get_vm_actual_ip() {
|
|
local vmid="$1"
|
|
|
|
if ! qm status "$vmid" 2>/dev/null | grep -q "status: running"; then
|
|
return
|
|
fi
|
|
|
|
# Try guest agent
|
|
local ip=$(timeout 5s qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | \
|
|
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | \
|
|
grep -v "127.0.0.1" | head -1)
|
|
|
|
if [[ -n "$ip" ]]; then
|
|
echo "$ip"
|
|
fi
|
|
}
|
|
|
|
# Function to get VM IP via ARP table
|
|
get_vm_arp_ip() {
|
|
local vmid="$1"
|
|
local config_file="/etc/pve/qemu-server/${vmid}.conf"
|
|
|
|
if [[ ! -f "$config_file" ]]; then
|
|
return
|
|
fi
|
|
|
|
# Get MAC address from config
|
|
local mac=$(grep -E "^net[0-9]+:" "$config_file" 2>/dev/null | \
|
|
grep -oE "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}" | head -1)
|
|
|
|
if [[ -n "$mac" ]]; then
|
|
local ip=$(ip neighbor show 2>/dev/null | grep -i "$mac" | \
|
|
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
|
|
if [[ -n "$ip" ]]; then
|
|
echo "$ip"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to extract IP from config content
|
|
extract_ip_from_config() {
|
|
local config_content="$1"
|
|
local type="$2" # "lxc" or "qemu"
|
|
|
|
if [[ "$type" == "lxc" ]]; then
|
|
# Extract IP from net0: or net1: lines for LXC
|
|
echo "$config_content" | grep -E "^net[0-9]+:" | \
|
|
grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | \
|
|
cut -d'=' -f2 | head -1 | sed 's|/.*||'
|
|
else
|
|
# Extract IP from ipconfig0: or ipconfig1: lines for VMs
|
|
echo "$config_content" | grep -E "^ipconfig[0-9]+:" | \
|
|
grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | \
|
|
cut -d'=' -f2 | head -1 | sed 's|/.*||'
|
|
fi
|
|
}
|
|
|
|
# Function to get config IP via API
|
|
get_config_ip_api() {
|
|
local node="$1"
|
|
local vmid="$2"
|
|
local type="$3" # "qemu" or "lxc"
|
|
|
|
local config_response=$(curl -k -s -m 5 \
|
|
-H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \
|
|
"https://${PROXMOX_HOST}:${PROXMOX_PORT}/api2/json/nodes/${node}/${type}/${vmid}/config" 2>/dev/null)
|
|
|
|
if [[ -z "$config_response" ]]; then
|
|
return
|
|
fi
|
|
|
|
# Extract IP from JSON config
|
|
if [[ "$type" == "lxc" ]]; then
|
|
# For LXC: extract from net0, net1, etc. fields
|
|
echo "$config_response" | python3 -c "
|
|
import sys, json
|
|
try:
|
|
data = json.load(sys.stdin).get('data', {})
|
|
# Check net0, net1, net2, etc.
|
|
for i in range(10):
|
|
net_key = f'net{i}'
|
|
if net_key in data:
|
|
net_value = data[net_key]
|
|
if 'ip=' in net_value:
|
|
# Extract IP from ip=192.168.11.100/24 format
|
|
import re
|
|
match = re.search(r'ip=([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})', net_value)
|
|
if match:
|
|
print(match.group(1))
|
|
break
|
|
except:
|
|
pass
|
|
" 2>/dev/null
|
|
else
|
|
# For VM: extract from ipconfig0, ipconfig1, etc. fields
|
|
echo "$config_response" | python3 -c "
|
|
import sys, json
|
|
try:
|
|
data = json.load(sys.stdin).get('data', {})
|
|
# Check ipconfig0, ipconfig1, etc.
|
|
for i in range(10):
|
|
ipconfig_key = f'ipconfig{i}'
|
|
if ipconfig_key in data:
|
|
ipconfig_value = data[ipconfig_key]
|
|
if 'ip=' in ipconfig_value:
|
|
# Extract IP from ip=192.168.11.100/24 format
|
|
import re
|
|
match = re.search(r'ip=([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})', ipconfig_value)
|
|
if match:
|
|
print(match.group(1))
|
|
break
|
|
except:
|
|
pass
|
|
" 2>/dev/null
|
|
fi
|
|
}
|
|
|
|
# Function to list IPs via API
|
|
list_ips_via_api() {
|
|
local node="$1"
|
|
|
|
echo "Fetching data from Proxmox API..."
|
|
echo ""
|
|
|
|
# Get all VMs
|
|
local vms_response=$(curl -k -s -m 10 \
|
|
-H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \
|
|
"https://${PROXMOX_HOST}:${PROXMOX_PORT}/api2/json/nodes/${node}/qemu" 2>/dev/null)
|
|
|
|
# Get all LXC containers
|
|
local lxc_response=$(curl -k -s -m 10 \
|
|
-H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \
|
|
"https://${PROXMOX_HOST}:${PROXMOX_PORT}/api2/json/nodes/${node}/lxc" 2>/dev/null)
|
|
|
|
echo "VMID Type Name Status Config IP Actual IP"
|
|
echo "--------------------------------------------------------------------------------"
|
|
|
|
# Process VMs - store in temp file to avoid subshell issues
|
|
local temp_file=$(mktemp)
|
|
echo "$vms_response" | python3 -c "
|
|
import sys, json
|
|
try:
|
|
data = json.load(sys.stdin)['data']
|
|
for vm in sorted(data, key=lambda x: x['vmid']):
|
|
vmid = vm['vmid']
|
|
name = vm.get('name', 'N/A')
|
|
status = vm.get('status', 'unknown')
|
|
print(f'{vmid}|{name}|{status}')
|
|
except:
|
|
pass
|
|
" 2>/dev/null > "$temp_file"
|
|
|
|
while IFS='|' read -r vmid name status; do
|
|
[[ -z "$vmid" ]] && continue
|
|
config_ip=$(get_config_ip_api "$node" "$vmid" "qemu")
|
|
if [[ -n "$config_ip" ]] && is_private_ip "$config_ip"; then
|
|
printf "%-7s %-6s %-23s %-10s %-15s %-15s\n" \
|
|
"$vmid" "VM" "${name:0:23}" "$status" "$config_ip" "N/A (remote)"
|
|
elif [[ -z "$config_ip" ]]; then
|
|
# Show even without config IP if it's a known VM
|
|
printf "%-7s %-6s %-23s %-10s %-15s %-15s\n" \
|
|
"$vmid" "VM" "${name:0:23}" "$status" "N/A" "N/A (remote)"
|
|
fi
|
|
done < "$temp_file"
|
|
rm -f "$temp_file"
|
|
|
|
# Process LXC containers
|
|
echo "$lxc_response" | python3 -c "
|
|
import sys, json
|
|
try:
|
|
data = json.load(sys.stdin)['data']
|
|
for lxc in sorted(data, key=lambda x: x['vmid']):
|
|
vmid = lxc['vmid']
|
|
name = lxc.get('name', 'N/A')
|
|
status = lxc.get('status', 'unknown')
|
|
print(f'{vmid}|{name}|{status}')
|
|
except:
|
|
pass
|
|
" 2>/dev/null > "$temp_file"
|
|
|
|
while IFS='|' read -r vmid name status; do
|
|
[[ -z "$vmid" ]] && continue
|
|
config_ip=$(get_config_ip_api "$node" "$vmid" "lxc")
|
|
if [[ -n "$config_ip" ]] && is_private_ip "$config_ip"; then
|
|
printf "%-7s %-6s %-23s %-10s %-15s %-15s\n" \
|
|
"$vmid" "LXC" "${name:0:23}" "$status" "$config_ip" "N/A (remote)"
|
|
elif [[ -z "$config_ip" ]]; then
|
|
# Show even without config IP if it's a known container
|
|
printf "%-7s %-6s %-23s %-10s %-15s %-15s\n" \
|
|
"$vmid" "LXC" "${name:0:23}" "$status" "N/A" "N/A (remote)"
|
|
fi
|
|
done < "$temp_file"
|
|
rm -f "$temp_file"
|
|
|
|
echo ""
|
|
echo "Note: Actual IP addresses (runtime) require running on Proxmox host."
|
|
echo " Config IP shows static IP configuration from Proxmox config files."
|
|
}
|
|
|
|
# Function to list IPs directly (on Proxmox host)
|
|
list_ips_direct() {
|
|
echo "Listing private IP address assignments on Proxmox host..."
|
|
echo ""
|
|
|
|
printf "%-7s %-6s %-23s %-10s %-15s %-15s\n" "VMID" "Type" "Name" "Status" "Config IP" "Actual IP"
|
|
echo "--------------------------------------------------------------------------------"
|
|
|
|
# List all LXC containers
|
|
if command -v pct >/dev/null 2>&1; then
|
|
while IFS= read -r line; do
|
|
[[ -z "$line" ]] && continue
|
|
|
|
local vmid=$(echo "$line" | awk '{print $1}')
|
|
local status=$(echo "$line" | awk '{print $2}')
|
|
|
|
# Get container name
|
|
local name="N/A"
|
|
local config_file="/etc/pve/lxc/${vmid}.conf"
|
|
if [[ -f "$config_file" ]]; then
|
|
name=$(grep "^hostname:" "$config_file" 2>/dev/null | cut -d' ' -f2 || echo "N/A")
|
|
fi
|
|
|
|
# Get IPs
|
|
local config_ip=$(get_lxc_config_ip "$vmid")
|
|
local actual_ip=""
|
|
|
|
if [[ "$status" == "running" ]]; then
|
|
actual_ip=$(get_lxc_actual_ip "$vmid")
|
|
fi
|
|
|
|
# Only show private IPs
|
|
local display_config_ip=""
|
|
local display_actual_ip=""
|
|
|
|
if [[ -n "$config_ip" ]] && is_private_ip "$config_ip"; then
|
|
display_config_ip="$config_ip"
|
|
fi
|
|
|
|
if [[ -n "$actual_ip" ]] && is_private_ip "$actual_ip"; then
|
|
display_actual_ip="$actual_ip"
|
|
fi
|
|
|
|
# Only print if we have at least one private IP
|
|
if [[ -n "$display_config_ip" ]] || [[ -n "$display_actual_ip" ]]; then
|
|
printf "%-7s %-6s %-23s %-10s %-15s %-15s\n" \
|
|
"$vmid" "LXC" "${name:0:23}" "$status" \
|
|
"${display_config_ip:-N/A}" "${display_actual_ip:-N/A}"
|
|
fi
|
|
done < <(pct list 2>/dev/null | tail -n +2)
|
|
fi
|
|
|
|
# List all VMs
|
|
if command -v qm >/dev/null 2>&1; then
|
|
while IFS= read -r line; do
|
|
[[ -z "$line" ]] && continue
|
|
|
|
local vmid=$(echo "$line" | awk '{print $1}')
|
|
local status=$(echo "$line" | awk '{print $2}')
|
|
|
|
# Get VM name
|
|
local name="N/A"
|
|
local config_file="/etc/pve/qemu-server/${vmid}.conf"
|
|
if [[ -f "$config_file" ]]; then
|
|
name=$(grep "^name:" "$config_file" 2>/dev/null | cut -d' ' -f2 || echo "N/A")
|
|
fi
|
|
|
|
# Get IPs
|
|
local config_ip=$(get_vm_config_ip "$vmid")
|
|
local actual_ip=""
|
|
|
|
if [[ "$status" == "running" ]]; then
|
|
actual_ip=$(get_vm_actual_ip "$vmid")
|
|
# Fallback to ARP if guest agent fails
|
|
if [[ -z "$actual_ip" ]]; then
|
|
actual_ip=$(get_vm_arp_ip "$vmid")
|
|
fi
|
|
fi
|
|
|
|
# Only show private IPs
|
|
local display_config_ip=""
|
|
local display_actual_ip=""
|
|
|
|
if [[ -n "$config_ip" ]] && is_private_ip "$config_ip"; then
|
|
display_config_ip="$config_ip"
|
|
fi
|
|
|
|
if [[ -n "$actual_ip" ]] && is_private_ip "$actual_ip"; then
|
|
display_actual_ip="$actual_ip"
|
|
fi
|
|
|
|
# Only print if we have at least one private IP
|
|
if [[ -n "$display_config_ip" ]] || [[ -n "$display_actual_ip" ]]; then
|
|
printf "%-7s %-6s %-23s %-10s %-15s %-15s\n" \
|
|
"$vmid" "VM" "${name:0:23}" "$status" \
|
|
"${display_config_ip:-N/A}" "${display_actual_ip:-N/A}"
|
|
fi
|
|
done < <(qm list 2>/dev/null | tail -n +2)
|
|
fi
|
|
}
|
|
|
|
# Main execution
|
|
if [[ "$ON_PROXMOX_HOST" == "true" ]]; then
|
|
list_ips_direct
|
|
else
|
|
# Try API method
|
|
if [[ -n "${PROXMOX_USER:-}" ]] && [[ -n "${PROXMOX_TOKEN_NAME:-}" ]] && [[ -n "${PROXMOX_TOKEN_VALUE:-}" ]]; then
|
|
# Get first node
|
|
nodes_response=$(curl -k -s -m 10 \
|
|
-H "Authorization: PVEAPIToken=${PROXMOX_USER}!${PROXMOX_TOKEN_NAME}=${PROXMOX_TOKEN_VALUE}" \
|
|
"https://${PROXMOX_HOST}:${PROXMOX_PORT}/api2/json/nodes" 2>/dev/null)
|
|
|
|
first_node=$(echo "$nodes_response" | python3 -c "import sys, json; print(json.load(sys.stdin)['data'][0]['node'])" 2>/dev/null)
|
|
|
|
if [[ -n "$first_node" ]]; then
|
|
list_ips_via_api "$first_node"
|
|
else
|
|
echo "Error: Could not connect to Proxmox API or get node list"
|
|
echo "Please run this script on the Proxmox host for full IP information"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Error: Not on Proxmox host and API credentials not configured"
|
|
echo "Please either:"
|
|
echo " 1. Run this script on the Proxmox host, or"
|
|
echo " 2. Configure PROXMOX_USER, PROXMOX_TOKEN_NAME, and PROXMOX_TOKEN_VALUE"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|