Files
dockernet-inspector/dockernet-inspector.v1.sh

554 lines
17 KiB
Bash

#!/usr/bin/env bash
# Script Name: dockernet-inspector.v1.sh
# Beschreibung: Dockernet Inspector - Verwaltet und inspiziert Docker-Netzwerke
# - Alle Netzwerke auflisten
# - Details zu einem spezifischen Docker-Netzwerk anzeigen
# - Belegte IPs je Netzwerk und netzwerkuebergreifend ausgeben
# - Ungenutzte Netzwerke (ohne Container) anzeigen
# - Netzwerke interaktiv auswaehlen und loeschen
# - Alle ungenutzten, loeschbaren Netzwerke auf einmal entfernen
# Verwendung: bash ./dockernet-inspector.v1.sh [BEFEHL] [OPTIONS]
# Befehle:
# networks - Alle Docker Netzwerke auflisten
# inspect - Details zu einem Netzwerk anzeigen (Standard)
# ips - Belegte IPs in einem Netzwerk
# ips-all - Belegte IPs in allen Netzwerken
# unused-networks - Netzwerke ohne verbundene Container
# delete-networks - Netzwerke interaktiv auswaehlen und loeschen
# delete-unused-networks - Alle ungenutzten, loeschbaren Netzwerke loeschen
# help, -h, --help - Diese Hilfe anzeigen
# Autor: Patrick Asmus
# Web: https://www.cleveradmin.de
# Git-Reposit.: https://git.techniverse.net/scriptos/dockernet-inspector.git
# Version: 1.7.0
# Datum: 08.05.2026
# Modifikation: Sammelloeschung fuer ungenutzte Docker-Netzwerke hinzugefuegt
#####################################################
set -uo pipefail
TABLE_LINE_WIDTH=80
INTERACTIVE_MODE=false
print_rule() {
local char="$1"
printf '%*s\n' "$TABLE_LINE_WIDTH" '' | tr ' ' "$char"
}
print_table_title() {
local title="$1"
echo "$title"
print_rule "="
}
is_protected_network() {
local net="$1"
[[ "$net" == "bridge" || "$net" == "host" || "$net" == "none" || "$net" == "ingress" || "$net" == "docker_gwbridge" ]]
}
# Hilfe-Funktion
show_help() {
cat <<HELP
Dockernet Inspector v1.7.0 - Docker Netzwerk Verwaltungstool
VERWENDUNG:
$0 - Interaktiver Modus (Menü)
$0 networks - Alle Docker Netzwerke auflisten
$0 inspect <netzwerk> - Details zu einem Netzwerk anzeigen
$0 ips <netzwerk> - Belegte IPs in einem Netzwerk anzeigen
$0 ips-all - Belegte IPs in allen Netzwerken anzeigen
$0 unused-networks - Ungenutzte Netzwerke (ohne Container) anzeigen
$0 delete-networks - Netzwerke interaktiv auswaehlen und loeschen
$0 delete-unused-networks - Alle ungenutzten, loeschbaren Netzwerke loeschen
$0 <netzwerk> - Alias fuer 'inspect <netzwerk>'
$0 help - Diese Hilfe anzeigen
BEFEHLE:
networks Listet alle verfuegbaren Docker Netzwerke auf
ips <netzwerk> Zeigt Containername und IPv4-Adresse im angegebenen Netzwerk
ips-all Zeigt alle belegten IPv4-Adressen in allen Netzwerken (aufsteigend)
unused-networks Zeigt alle Docker Netzwerke ohne verbundene Container
delete-networks Netzwerk per Nummer auswaehlen und loeschen;
Docker-Standard- und Systemnetzwerke sind geschuetzt
delete-unused-networks Loescht alle ungenutzten Docker Netzwerke nach Bestaetigung;
bridge, host, none, ingress und docker_gwbridge sind geschuetzt
inspect <netzwerk> Zeigt detaillierte Informationen zu einem Netzwerk:
- Netzwerktyp (Driver)
- Bridge-Interface
- Subnetz und Gateway
- Interface-Status
- Netzwerk-Statistiken (RX/TX bytes und packets)
help Zeigt diese Hilfe an
BEISPIELE:
$0 networks
$0 ips mynetwork
$0 ips-all
$0 unused-networks
$0 delete-networks
$0 delete-unused-networks
$0 inspect mynetwork
$0 mynetwork
$0 -h
HELP
}
# Funktion zum Auflisten aller Docker Netzwerke
list_networks() {
local table_output header
print_table_title "Verfuegbare Docker Netzwerke:"
table_output="$(docker network ls --format "table {{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.Scope}}")" || {
echo "Fehler: Docker Netzwerke konnten nicht abgerufen werden."
exit 2
}
header="$(printf "%s\n" "$table_output" | head -n 1)"
printf "%s\n" "$header"
print_rule "-"
printf "%s\n" "$table_output" | tail -n +2
}
print_dynamic_table() {
local data="$1"
local formatted
local header
if command -v column >/dev/null 2>&1; then
formatted="$(printf "%b" "$data" | column -t -s $'\t')"
else
formatted="$(printf "%b" "$data")"
fi
header="$(printf "%s\n" "$formatted" | head -n 1)"
printf "%s\n" "$header"
print_rule "-"
printf "%s\n" "$formatted" | tail -n +2
}
list_ips_in_network() {
local net="$1"
local rows table_data
rows="$(docker network inspect "$net" --format '{{range $id, $c := .Containers}}{{println $c.Name $c.IPv4Address}}{{end}}' 2>/dev/null || true)"
[[ -z "$rows" ]] && {
echo "Keine Container mit IPv4-Adresse im Netzwerk '$net' gefunden."
exit 0
}
print_table_title "Belegte IP-Adressen im Netzwerk '$net':"
table_data=$'CONTAINER\tIP\n'
while read -r cname ipcidr; do
[[ -z "${cname:-}" || -z "${ipcidr:-}" ]] && continue
table_data+="${cname}"$'\t'"${ipcidr%%/*}"$'\n'
done < <(printf "%s\n" "$rows" | sort -k2,2V)
print_dynamic_table "$table_data"
}
list_ips_all_networks() {
local nets rows table_data
rows=""
nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)"
[[ -z "$nets" ]] && {
echo "Fehler: Docker Netzwerke konnten nicht abgerufen werden."
exit 2
}
while read -r net; do
[[ -z "${net:-}" ]] && continue
while read -r cname ipcidr; do
[[ -z "${cname:-}" || -z "${ipcidr:-}" ]] && continue
rows+="${net}"$'\t'"${cname}"$'\t'"${ipcidr%%/*}"$'\n'
done < <(docker network inspect "$net" --format '{{range $id, $c := .Containers}}{{println $c.Name $c.IPv4Address}}{{end}}' 2>/dev/null || true)
done < <(printf "%s\n" "$nets")
[[ -z "$rows" ]] && {
echo "Keine belegten IPv4-Adressen in Docker Netzwerken gefunden."
exit 0
}
print_table_title "Belegte IP-Adressen in allen Netzwerken (aufsteigend):"
table_data=$'NETZWERK\tCONTAINER\tIP\n'
while IFS=$'\t' read -r net cname ip; do
[[ -z "${net:-}" || -z "${cname:-}" || -z "${ip:-}" ]] && continue
table_data+="${net}"$'\t'"${cname}"$'\t'"${ip}"$'\n'
done < <(printf "%s" "$rows" | sort -t $'\t' -k3,3V -k1,1 -k2,2)
print_dynamic_table "$table_data"
}
list_unused_networks() {
local nets net containers table_data has_unused
nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)"
[[ -z "$nets" ]] && {
echo "Fehler: Docker Netzwerke konnten nicht abgerufen werden."
exit 2
}
table_data=$'NAME\tDRIVER\tSCOPE\n'
has_unused=false
while read -r net; do
[[ -z "${net:-}" ]] && continue
containers="$(docker network inspect "$net" --format '{{len .Containers}}' 2>/dev/null || true)"
if [[ "${containers:-0}" -eq 0 ]]; then
local driver scope
driver="$(docker network inspect "$net" --format '{{.Driver}}' 2>/dev/null || true)"
scope="$(docker network inspect "$net" --format '{{.Scope}}' 2>/dev/null || true)"
table_data+="${net}"$'\t'"${driver}"$'\t'"${scope}"$'\n'
has_unused=true
fi
done < <(printf "%s\n" "$nets")
if ! $has_unused; then
echo "Alle Docker Netzwerke haben mindestens einen verbundenen Container."
return 0
fi
print_table_title "Ungenutzte Docker Netzwerke (keine Container verbunden):"
print_dynamic_table "$table_data"
}
delete_networks() {
local nets
nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)"
if [[ -z "$nets" ]]; then
echo "Fehler: Keine Docker Netzwerke gefunden."
return 1
fi
local -a net_array
mapfile -t net_array <<< "$nets"
echo ""
echo "Verfuegbare Docker Netzwerke:"
echo ""
local i
for ((i=0; i<${#net_array[@]}; i++)); do
local suffix=""
is_protected_network "${net_array[$i]}" && suffix=" (geschuetzt - nicht loeschbar)"
printf " %d. %s%s\n" "$((i+1))" "${net_array[$i]}" "$suffix"
done
echo ""
read -rp "Nummer des Netzwerks eingeben (0 = Abbrechen): " choice
if [[ "$choice" == "0" || -z "$choice" ]]; then
echo "Abgebrochen."
return 0
fi
if ! [[ "$choice" =~ ^[0-9]+$ ]] || ((choice < 1 || choice > ${#net_array[@]})); then
echo "Fehler: Ungueltige Nummer."
return 1
fi
local selected_net="${net_array[$((choice-1))]}"
if is_protected_network "$selected_net"; then
echo "FEHLER: '$selected_net' ist ein geschuetztes Docker-Netzwerk und kann nicht geloescht werden."
return 1
fi
echo ""
read -rp "Netzwerk '$selected_net' wirklich loeschen? [j/N]: " confirm
if [[ "$confirm" != "j" && "$confirm" != "J" ]]; then
echo "Abgebrochen."
return 0
fi
echo ""
if docker network rm "$selected_net" 2>/dev/null; then
echo "OK: Netzwerk '$selected_net' erfolgreich geloescht."
return 0
else
echo "FEHLER: Netzwerk '$selected_net' konnte nicht geloescht werden (wird moeglicherweise noch genutzt)."
return 1
fi
}
delete_unused_networks() {
local nets net containers driver scope
local -a delete_candidates
local -a protected_unused
nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)"
if [[ -z "$nets" ]]; then
echo "Fehler: Keine Docker Netzwerke gefunden."
return 1
fi
delete_candidates=()
protected_unused=()
while read -r net; do
[[ -z "${net:-}" ]] && continue
containers="$(docker network inspect "$net" --format '{{len .Containers}}' 2>/dev/null || true)"
if [[ "${containers:-0}" -eq 0 ]]; then
if is_protected_network "$net"; then
protected_unused+=("$net")
else
delete_candidates+=("$net")
fi
fi
done < <(printf "%s\n" "$nets")
if ((${#delete_candidates[@]} == 0)); then
echo "Keine ungenutzten, loeschbaren Docker Netzwerke gefunden."
if ((${#protected_unused[@]} > 0)); then
echo "Geschuetzte ungenutzte Netzwerke werden uebersprungen: ${protected_unused[*]}"
fi
return 0
fi
print_table_title "Ungenutzte Docker Netzwerke, die geloescht werden koennen:"
local table_data
table_data=$'NAME\tDRIVER\tSCOPE\n'
for net in "${delete_candidates[@]}"; do
driver="$(docker network inspect "$net" --format '{{.Driver}}' 2>/dev/null || true)"
scope="$(docker network inspect "$net" --format '{{.Scope}}' 2>/dev/null || true)"
table_data+="${net}"$'\t'"${driver}"$'\t'"${scope}"$'\n'
done
print_dynamic_table "$table_data"
if ((${#protected_unused[@]} > 0)); then
echo ""
echo "Geschuetzte ungenutzte Netzwerke werden nicht geloescht: ${protected_unused[*]}"
fi
echo ""
read -rp "Alle oben aufgefuehrten ungenutzten Netzwerke wirklich loeschen? [j/N]: " confirm
if [[ "$confirm" != "j" && "$confirm" != "J" ]]; then
echo "Abgebrochen."
return 0
fi
local success_count=0
local fail_count=0
echo ""
for net in "${delete_candidates[@]}"; do
if is_protected_network "$net"; then
echo "SKIP: Netzwerk '$net' ist geschuetzt."
continue
fi
if docker network rm "$net" >/dev/null 2>&1; then
echo "OK: Netzwerk '$net' erfolgreich geloescht."
((success_count++))
else
echo "FEHLER: Netzwerk '$net' konnte nicht geloescht werden (wird moeglicherweise wieder genutzt)."
((fail_count++))
fi
done
echo ""
echo "Ergebnis: $success_count geloescht, $fail_count fehlgeschlagen."
((fail_count == 0))
}
# Interaktive Menü-Funktion
interactive_menu() {
while true; do
echo ""
print_rule "="
echo "Dockernet Inspector - Interaktives Menü"
print_rule "="
echo ""
echo "1. Alle Netzwerke auflisten"
echo "2. Detaillierte Infos zu einem Netzwerk"
echo "3. IPs eines Netzwerks anzeigen"
echo "4. Alle belegten IPs anzeigen"
echo "5. Ungenutzte Netzwerke anzeigen"
echo "6. Netzwerke loeschen"
echo "7. Alle ungenutzten Netzwerke loeschen"
echo "0. Beenden"
echo ""
read -rp "Bitte waehlen Sie eine Option (0-7): " choice
echo ""
case "$choice" in
1)
list_networks
;;
2)
if select_and_inspect_network; then
:
fi
;;
3)
nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)"
if [[ -z "$nets" ]]; then
echo "Fehler: Keine Docker Netzwerke gefunden."
continue
fi
echo "Verfuegbare Netzwerke:"
select net in $nets; do
if [[ -n "$net" ]]; then
list_ips_in_network "$net"
break
else
echo "Ungueltige Auswahl. Bitte versuchen Sie es erneut."
fi
done
;;
4)
list_ips_all_networks
;;
5)
list_unused_networks
;;
6)
delete_networks
;;
7)
delete_unused_networks
;;
0)
echo "Auf Wiedersehen!"
exit 0
;;
*)
echo "FEHLER: Ungueltige Eingabe '$choice'. Bitte geben Sie eine Nummer zwischen 0 und 7 ein."
sleep 1
;;
esac
done
}
# Funktion zur Auswahl und Inspektion eines Netzwerks
select_and_inspect_network() {
local nets net
nets="$(docker network ls --format '{{.Name}}' 2>/dev/null || true)"
if [[ -z "$nets" ]]; then
echo "Fehler: Keine Docker Netzwerke gefunden."
return 1
fi
echo "Verfuegbare Netzwerke:"
select net in $nets; do
if [[ -n "$net" ]]; then
NET="$net"
inspect_network "$NET"
return 0
else
echo "Ungueltige Auswahl. Bitte versuchen Sie es erneut."
fi
done
}
# Separate Funktion fuer Netzwerk-Inspektion
inspect_network() {
local NET="$1"
local DRIVER
DRIVER="$(docker network inspect "$NET" --format '{{.Driver}}' 2>/dev/null || true)"
[[ -z "$DRIVER" ]] && { echo "Fehler: Netzwerk '$NET' nicht gefunden."; return 2; }
echo ""
print_rule "="
echo "Detaillerte Informationen fuer Netzwerk: $NET"
print_rule "="
echo ""
# Sonderbehandlung fuer Docker-Standardnetzwerke
if [[ "$NET" == "none" || "$NET" == "host" || "$NET" == "bridge" ]]; then
cat <<OUT
Docker-Netzwerk : $NET
Driver : $DRIVER
Hinweis : Dies ist ein Docker-Standardnetzwerk und kann nicht geloescht werden.
OUT
echo ""
return 0
fi
# 1) Netzwerk-ID und Bridge-Name
local ID BR_RAW IF SUBNET GATEWAY state rxb txb rxp txp
ID="$(docker network inspect "$NET" --format '{{.Id}}' 2>/dev/null || true)"
BR_RAW="$(docker network inspect "$NET" --format '{{index .Options "com.docker.network.bridge.name"}}' 2>/dev/null || true)"
if [[ -z "$BR_RAW" || "$BR_RAW" == "<no value>" ]]; then
IF="br-${ID:0:12}"
else
IF="$BR_RAW"
fi
# 2) Subnet/Gateway
SUBNET="$(docker network inspect "$NET" --format '{{(index .IPAM.Config 0).Subnet}}' 2>/dev/null || true)"
GATEWAY="$(docker network inspect "$NET" --format '{{(index .IPAM.Config 0).Gateway}}' 2>/dev/null || true)"
# 3) Interface-Status & Stats
state="unknown"; rxb=0; txb=0; rxp=0; txp=0
if ip link show "$IF" &>/dev/null; then
state="$(cat /sys/class/net/"$IF"/operstate 2>/dev/null || echo unknown)"
rxb="$(cat /sys/class/net/"$IF"/statistics/rx_bytes 2>/dev/null || echo 0)"
txb="$(cat /sys/class/net/"$IF"/statistics/tx_bytes 2>/dev/null || echo 0)"
rxp="$(cat /sys/class/net/"$IF"/statistics/rx_packets 2>/dev/null || echo 0)"
txp="$(cat /sys/class/net/"$IF"/statistics/tx_packets 2>/dev/null || echo 0)"
fi
cat <<OUT
Docker-Netzwerk : $NET
Driver : $DRIVER
Bridge-Interface : $IF
Subnet/Gateway : ${SUBNET:--} / ${GATEWAY:--}
OperState : $state
RX bytes/packets : $rxb / $rxp
TX bytes/packets : $txb / $txp
OUT
echo ""
}
# Argument-Verarbeitung
COMMAND="${1:-}"
case "$COMMAND" in
networks)
list_networks
exit 0
;;
ips)
NET="${2:-}"
[[ -z "$NET" ]] && { echo "Fehler: Netzwerkname erforderlich. Verwendung: $0 ips <docker-network-name>"; exit 1; }
list_ips_in_network "$NET"
exit 0
;;
ips-all)
list_ips_all_networks
exit 0
;;
unused-networks)
list_unused_networks
exit 0
;;
delete-networks)
delete_networks
exit $?
;;
delete-unused-networks)
delete_unused_networks
exit $?
;;
help | -h | --help)
show_help
exit 0
;;
inspect)
NET="${2:-}"
[[ -z "$NET" ]] && { echo "Fehler: Netzwerkname erforderlich. Verwendung: $0 inspect <docker-network-name>"; exit 1; }
inspect_network "$NET"
exit 0
;;
"")
# Starte interaktiven Modus, wenn keine Argumente uebergeben wurden
interactive_menu
exit 0
;;
*)
# Kompatibilitaet: Wenn erstes Argument kein bekannter Befehl ist, behandle es als Netzwerkname
NET="$COMMAND"
inspect_network "$NET"
exit 0
;;
esac