diff --git a/manage-lxc-vm-notes.sh b/manage-lxc-vm-notes.sh index d37a7cc..0459416 100755 --- a/manage-lxc-vm-notes.sh +++ b/manage-lxc-vm-notes.sh @@ -2,6 +2,90 @@ set -e +SUMMARY_LINES=() + +check_lxc_tailscale_issue() { + local vmid="$1" + pct exec "$vmid" -- sh -c ' + if ! command -v tailscale >/dev/null 2>&1; then + echo tailscale-not-installed + exit 0 + fi + if command -v systemctl >/dev/null 2>&1; then + systemctl is-active --quiet tailscaled || echo tailscale-down + elif command -v rc-service >/dev/null 2>&1 || [ -e /etc/init.d/tailscaled ]; then + rc-service tailscaled status >/dev/null 2>&1 || echo tailscale-down + fi + ' 2>/dev/null | tr -d '\r' | head -n1 +} + +check_lxc_docker_issue() { + local vmid="$1" + pct exec "$vmid" -- sh -c ' + DAEMON_JSON="/etc/docker/daemon.json" + if [ ! -f "$DAEMON_JSON" ]; then + echo docker-missing:daemon.json + exit 0 + fi + if ! command -v jq >/dev/null 2>&1; then + echo docker-check-skipped-no-jq + exit 0 + fi + missing="" + # metrics-addr + metrics=$(jq -r '."metrics-addr" // empty' "$DAEMON_JSON") + [ "$metrics" = "0.0.0.0:9023" ] || missing="${missing},metrics-addr" + # experimental + exp=$(jq -r '.experimental // false' "$DAEMON_JSON") + [ "$exp" = "true" ] || missing="${missing},experimental" + # hosts + has_unix=$(jq -r '.hosts // [] | index("unix:///var/run/docker.sock") | type' "$DAEMON_JSON" 2>/dev/null || echo null) + [ "$has_unix" = "number" ] || missing="${missing},hosts-unix" + has_tcp=$(jq -r '.hosts // [] | index("tcp://0.0.0.0:2375") | type' "$DAEMON_JSON" 2>/dev/null || echo null) + [ "$has_tcp" = "number" ] || missing="${missing},hosts-tcp" + if [ -z "$missing" ]; then + echo OK + else + echo docker-missing:${missing#,} + fi + ' 2>/dev/null | tr -d '\r' | head -n1 +} + +collect_lxc_summary() { + local vmid="$1" + local name ip issues="" + name=$(pct exec "$vmid" -- sh -c "hostname -s 2>/dev/null || cat /etc/hostname 2>/dev/null || echo lxc-$vmid" 2>/dev/null | head -n1 | tr -d '\r') + ip=$(pct exec "$vmid" -- sh -c "(hostname -I 2>/dev/null || ip -4 -o addr show | awk '{print \\\$4}' | cut -d/ -f1) | tr ' ' '\n' | grep '^192\\.168\\.1\\.' | tr '\n' ' ' | sed 's/ *$//'" 2>/dev/null || true) + # Tailcale check + ts_issue=$(check_lxc_tailscale_issue "$vmid") + if [ -n "$ts_issue" ]; then + issues="${issues}${ts_issue}" + fi + # Docker config check + dk_issue=$(check_lxc_docker_issue "$vmid") + if [ -n "$dk_issue" ] && [ "$dk_issue" != "OK" ]; then + issues="${issues}${issues:+;}${dk_issue}" + fi + if [ -n "$issues" ]; then + SUMMARY_LINES+=("LXC $vmid | ${name:-lxc-$vmid} | ${ip:-no-ip-in-range} | ${issues}") + else + SUMMARY_LINES+=("LXC $vmid | ${name:-lxc-$vmid} | ${ip:-no-ip-in-range}") + fi +} + +collect_vm_summary() { + local vmid="$1" + local name ip + name=$(qm config "$vmid" 2>/dev/null | awk '/^name:/{ $1=""; sub(/^ /,"",$0); print; exit }') + # Try modern guest cmd first + ip=$(qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | awk -F '"' '/"ip-address"/ {print $4}' | grep '^192\\.168\\.1\\.' | tr '\n' ' ' | sed 's/ *$//' || true) + # Fallback to older agent command + if [ -z "$ip" ]; then + ip=$(qm agent "$vmid" network-get-interfaces 2>/dev/null | awk -F '"' '/"ip-address"/ {print $4}' | grep '^192\\.168\\.1\\.' | tr '\n' ' ' | sed 's/ *$//' || true) + fi + SUMMARY_LINES+=("VM $vmid | ${name:-vm-$vmid} | ${ip:-no-ip-in-range/agent-off}") +} + update_notes() { local vmid="$1" local type="$2" @@ -153,6 +237,39 @@ OVR fi } + wait_for_docker_ready() { + echo "[INFO] Esperando a que Docker esté operativo..." + READY=0 + for i in $(seq 1 30); do + if docker info >/dev/null 2>&1; then + READY=1; break + fi + sleep 1 + done + if [ $READY -eq 1 ]; then + echo "[INFO] Docker respondió a 'docker info'." + else + echo "[WARN] Docker no respondió tras el tiempo de espera." + fi + } + + wait_for_endpoints() { + echo "[INFO] Esperando endpoints 0.0.0.0:2375 y 0.0.0.0:9023..." + EP=0 + for i in $(seq 1 30); do + API_OK=0; MET_OK=0 + if ss -lnt 2>/dev/null | grep -q ":2375 "; then API_OK=1; fi + if ss -lnt 2>/dev/null | grep -q ":9023 "; then MET_OK=1; fi + if [ $API_OK -eq 1 ] && [ $MET_OK -eq 1 ]; then EP=1; break; fi + sleep 1 + done + if [ $EP -eq 1 ]; then + echo "[INFO] Endpoints confirmados." + else + echo "[WARN] Endpoints no confirmados tras el tiempo de espera." + fi + } + check_listeners() { echo "[INFO] Comprobando puertos en escucha para Docker..." if command -v ss >/dev/null 2>&1; then @@ -176,6 +293,8 @@ OVR apply_systemd_override_if_needed if [ ${JSON_CHANGED:-0} -eq 1 ] || [ ${OVERRIDE_APPLIED:-0} -eq 1 ]; then restart_docker + wait_for_docker_ready + wait_for_endpoints else echo "[INFO] Configuración de Docker ya presente; no se requiere reinicio." fi @@ -217,11 +336,17 @@ echo "[INFO] Iniciando gestión automática de LXC..." # Iterar sobre LXC activos for vmid in $(pct list | awk 'NR>1 {print $1}'); do update_notes "$vmid" "LXC" + collect_lxc_summary "$vmid" done -# VMs (sin integración por ahora) +# VMs: intentar obtener IPs vía QEMU Guest Agent (si disponible) for vmid in $(qm list | awk 'NR>1 && $2 == "running" {print $1}'); do - echo "[INFO] Saltando VM $vmid (se requiere integración SSH para gestionar desde el host)" + collect_vm_summary "$vmid" +done + +echo "[INFO] Resumen final (nombre e IP 192.168.1.*):" +for line in "${SUMMARY_LINES[@]}"; do + echo "$line" done echo "[INFO] Script completado."