Files
proxmox/scripts/list-proxmox-ips.sh

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