Riorganizzazione struttura repository: separazione servizi e script
This commit is contained in:
14
configs/keepalived_pi1.conf
Normal file
14
configs/keepalived_pi1.conf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
vrrp_instance VI_1 {
|
||||||
|
state MASTER
|
||||||
|
interface eth0
|
||||||
|
virtual_router_id 51
|
||||||
|
priority 101 # 101 = Priorità più alta (Master)
|
||||||
|
advert_int 1
|
||||||
|
authentication {
|
||||||
|
auth_type PASS
|
||||||
|
auth_pass @Dedelove1
|
||||||
|
}
|
||||||
|
virtual_ipaddress {
|
||||||
|
192.168.128.85 # Il nostro VIP
|
||||||
|
}
|
||||||
|
}
|
||||||
14
configs/keepalived_pi2.conf
Normal file
14
configs/keepalived_pi2.conf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
vrrp_instance VI_1 {
|
||||||
|
state BACKUP
|
||||||
|
interface eth0
|
||||||
|
virtual_router_id 51
|
||||||
|
priority 100 # 100 = Priorità più bassa (Backup)
|
||||||
|
advert_int 1
|
||||||
|
authentication {
|
||||||
|
auth_type PASS
|
||||||
|
auth_pass @Dedelove1
|
||||||
|
}
|
||||||
|
virtual_ipaddress {
|
||||||
|
192.168.128.85
|
||||||
|
}
|
||||||
|
}
|
||||||
57
scripts/pi1-master/dhcp-alert.sh
Normal file
57
scripts/pi1-master/dhcp-alert.sh
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# --- CONFIGURAZIONE TELEGRAM ---
|
||||||
|
# 👇👇 INSERISCI I TUOI DATI VERI QUI 👇👇
|
||||||
|
BOT_TOKEN="8155587974:AAF9OekvBpixtk8ZH6KoIc0L8edbhdXt7A4"
|
||||||
|
CHAT_ID="64463169"
|
||||||
|
# 👆👆 FINE MODIFICHE 👆👆
|
||||||
|
|
||||||
|
# --- FILE DI STATO ---
|
||||||
|
KNOWN_MACS_FILE="/etc/pihole/known_macs.txt"
|
||||||
|
|
||||||
|
# Dati ricevuti da dnsmasq
|
||||||
|
ACTION=$1
|
||||||
|
MAC=$2
|
||||||
|
IP=$3
|
||||||
|
HOSTNAME=$4
|
||||||
|
|
||||||
|
# Se l'hostname è vuoto, mettiamo un placeholder
|
||||||
|
if [ -z "$HOSTNAME" ]; then HOSTNAME="[Nessun Hostname]"; fi
|
||||||
|
|
||||||
|
# Ci interessano solo le azioni di assegnazione IP ('add' o 'old')
|
||||||
|
if [[ "$ACTION" == "add" || "$ACTION" == "old" ]]; then
|
||||||
|
|
||||||
|
# Assicura che il file dei MAC esista e sia scrivibile
|
||||||
|
if [ ! -f "$KNOWN_MACS_FILE" ]; then
|
||||||
|
touch "$KNOWN_MACS_FILE" && chmod 666 "$KNOWN_MACS_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cerca il MAC address nel file dei conosciuti
|
||||||
|
if ! grep -q "$MAC" "$KNOWN_MACS_FILE"; then
|
||||||
|
# === NUOVO DISPOSITIVO RILEVATO! ===
|
||||||
|
|
||||||
|
# --- NOVITÀ: Lookup Vendor ---
|
||||||
|
# Usiamo un'API esterna gratuita per trovare il produttore.
|
||||||
|
# Impostiamo un timeout stretto (3s connessione, 5s totale) per non bloccare il DHCP se l'API è lenta.
|
||||||
|
VENDOR=$(curl -s --connect-timeout 3 --max-time 5 "https://api.macvendors.com/$MAC")
|
||||||
|
|
||||||
|
# Se l'API fallisce, è vuota o restituisce errore, mettiamo un default.
|
||||||
|
# (Nota: alcune API restituiscono "Not Found" come testo puro se non trovano il vendor)
|
||||||
|
if [ -z "$VENDOR" ] || [[ "${VENDOR,,}" == *"not found"* ]] || [[ "${VENDOR,,}" == *"error"* ]]; then
|
||||||
|
VENDOR="[Sconosciuto / API Timeout]"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. Invia notifica Telegram (silenziosa)
|
||||||
|
# Aggiunta la riga 🏭 **Vendor:**
|
||||||
|
MSG="🚨 **NUOVO DISPOSITIVO IN RETE!**%0A%0A💻 **Host:** $HOSTNAME%0A📟 **MAC:** \`$MAC\`%0A🏭 **Vendor:** $VENDOR%0A🌐 **IP:** $IP"
|
||||||
|
curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" -d chat_id="$CHAT_ID" -d text="$MSG" -d parse_mode="Markdown" > /dev/null 2>&1
|
||||||
|
|
||||||
|
# 2. Aggiungi il MAC alla lista dei conosciuti
|
||||||
|
# Salviamo anche il vendor nel file di testo per comodità futura
|
||||||
|
{
|
||||||
|
flock -x 200
|
||||||
|
echo "$MAC # Aggiunto il $(date) - Vendor: $VENDOR - Host: $HOSTNAME" >> "$KNOWN_MACS_FILE"
|
||||||
|
} 200>"$KNOWN_MACS_FILE.lock"
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
||||||
57
scripts/pi1-master/sync-npm-full.sh
Executable file
57
scripts/pi1-master/sync-npm-full.sh
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# --- CONFIGURAZIONE ---
|
||||||
|
REMOTE_IP="192.168.128.81"
|
||||||
|
REMOTE_USER="daniely"
|
||||||
|
# Cartella che contiene 'data' e 'letsencrypt'
|
||||||
|
SOURCE_BASE="/home/daniely/docker/npm"
|
||||||
|
TEMP_FILE="/tmp/npm_full_clone.tar.gz"
|
||||||
|
|
||||||
|
echo "[$(date)] Avvio clonazione totale NPM (Master -> Backup)..."
|
||||||
|
|
||||||
|
# 1. Crea un archivio compresso di TUTTO (Database SQL + Certificati)
|
||||||
|
# Usiamo sudo per leggere i file di root senza permessi negati
|
||||||
|
# Escludiamo i log per risparmiare spazio
|
||||||
|
sudo tar --exclude='*.log' -czf "$TEMP_FILE" -C "$SOURCE_BASE" data letsencrypt
|
||||||
|
|
||||||
|
# 2. Assegna l'archivio all'utente corrente (per poterlo spedire via SCP)
|
||||||
|
sudo chown $USER:$USER "$TEMP_FILE"
|
||||||
|
|
||||||
|
# 3. Spedisce l'archivio al Pi-2
|
||||||
|
echo "Invio archivio al Pi-2 ($REMOTE_IP)..."
|
||||||
|
scp -o ConnectTimeout=10 "$TEMP_FILE" "$REMOTE_USER@$REMOTE_IP:/tmp/"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Trasferimento riuscito. Applicazione sul Backup..."
|
||||||
|
|
||||||
|
# 4. Comanda al Pi-2 di:
|
||||||
|
# a) Arrestare NPM (per sbloccare il database)
|
||||||
|
# b) Scompattare sovrascrivendo tutto
|
||||||
|
# c) Ripristinare i permessi di root
|
||||||
|
# d) Riavviare NPM
|
||||||
|
ssh "$REMOTE_USER@$REMOTE_IP" "
|
||||||
|
echo ' ...Stop NPM...';
|
||||||
|
docker stop npm;
|
||||||
|
|
||||||
|
echo ' ...Estrazione dati...';
|
||||||
|
# Pulisce la cartella di destinazione prima di estrarre per evitare residui
|
||||||
|
sudo rm -rf /home/daniely/docker/npm/data/*
|
||||||
|
sudo rm -rf /home/daniely/docker/npm/letsencrypt/*
|
||||||
|
sudo tar -xzf /tmp/npm_full_clone.tar.gz -C /home/daniely/docker/npm/;
|
||||||
|
|
||||||
|
echo ' ...Fix Permessi...';
|
||||||
|
sudo chown -R root:root /home/daniely/docker/npm/;
|
||||||
|
|
||||||
|
echo ' ...Start NPM...';
|
||||||
|
docker start npm;
|
||||||
|
|
||||||
|
# Pulizia remota
|
||||||
|
rm /tmp/npm_full_clone.tar.gz
|
||||||
|
"
|
||||||
|
echo "[$(date)] Sincronizzazione Completata con Successo."
|
||||||
|
else
|
||||||
|
echo "[$(date)] ERRORE CRITICO: Trasferimento fallito."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Pulizia locale
|
||||||
|
rm "$TEMP_FILE"
|
||||||
47
scripts/pi1-master/watchdog_pi2.sh
Executable file
47
scripts/pi1-master/watchdog_pi2.sh
Executable file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
# SENTINELLA DI BACKUP (Gira su Pi-1 Master)
|
||||||
|
# Controlla solo che il Pi-2 sia vivo.
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
# --- CONFIGURAZIONE ---
|
||||||
|
# 👇👇 INSERISCI I TUOI DATI VERI QUI 👇👇
|
||||||
|
BOT_TOKEN="8155587974:AAF9OekvBpixtk8ZH6KoIc0L8edbhdXt7A4"
|
||||||
|
CHAT_ID="64463169"
|
||||||
|
# 👆👆 FINE MODIFICHE 👆👆
|
||||||
|
|
||||||
|
# Il bersaglio da controllare (Pi-2 Backup)
|
||||||
|
TARGET_IP="192.168.128.81"
|
||||||
|
TARGET_NAME="🍓 Pi-2 (Backup & Monitor)"
|
||||||
|
|
||||||
|
# File di stato specifico per questo controllo
|
||||||
|
STATE_FILE="/tmp/pi2_watchdog.state"
|
||||||
|
|
||||||
|
# Funzione per inviare messaggi Telegram con timeout breve
|
||||||
|
send_telegram() {
|
||||||
|
MSG="$1"
|
||||||
|
curl -s --max-time 5 -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" -d chat_id="$CHAT_ID" -d text="$MSG" -d parse_mode="Markdown" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Inizializza stato se non esiste
|
||||||
|
if [ ! -f "$STATE_FILE" ]; then echo "UP" > "$STATE_FILE"; fi
|
||||||
|
LAST_STATE=$(cat "$STATE_FILE")
|
||||||
|
|
||||||
|
# --- IL PING ---
|
||||||
|
# 3 tentativi, 2 secondi di timeout l'uno.
|
||||||
|
# Basta che uno risponda per considerare il bersaglio UP.
|
||||||
|
if ping -c 3 -W 2 "$TARGET_IP" > /dev/null 2>&1; then
|
||||||
|
# === È ONLINE ===
|
||||||
|
if [ "$LAST_STATE" == "DOWN" ]; then
|
||||||
|
send_telegram "✅ **RISOLTO: $TARGET_NAME è tornato ONLINE!**%0AIl monitoraggio di rete è di nuovo attivo."
|
||||||
|
echo "UP" > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# === È OFFLINE ===
|
||||||
|
if [ "$LAST_STATE" == "UP" ]; then
|
||||||
|
# Usiamo un'icona diversa per distinguerlo subito
|
||||||
|
send_telegram "🛡️ **ALLARME SICUREZZA: $TARGET_NAME è OFFLINE!**%0A%0AAttenzione: Il sistema di monitoraggio principale (Super Watchdog) non sta funzionando."
|
||||||
|
echo "DOWN" > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
125
scripts/pi2-backup/daily_report.py
Normal file
125
scripts/pi2-backup/daily_report.py
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
import json
|
||||||
|
|
||||||
|
# --- CONFIGURAZIONE ---
|
||||||
|
BOT_TOKEN = os.environ.get('BOT_TOKEN')
|
||||||
|
CHAT_ID = os.environ.get('ALLOWED_USER_ID')
|
||||||
|
DB_PATH = "/data/speedtest.sqlite"
|
||||||
|
|
||||||
|
# SOGLIE DI ALLARME (Mbps)
|
||||||
|
WARN_DOWN = 400
|
||||||
|
WARN_UP = 100
|
||||||
|
|
||||||
|
def send_telegram_message(message):
|
||||||
|
if not message: return
|
||||||
|
|
||||||
|
url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
|
||||||
|
payload = {
|
||||||
|
"chat_id": CHAT_ID,
|
||||||
|
"text": message,
|
||||||
|
"parse_mode": "Markdown"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = urllib.parse.urlencode(payload).encode('utf-8')
|
||||||
|
req = urllib.request.Request(url, data=data)
|
||||||
|
with urllib.request.urlopen(req) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
print("✅ Report inviato.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Errore invio Telegram: {e}")
|
||||||
|
|
||||||
|
def generate_report():
|
||||||
|
if not os.path.exists(DB_PATH):
|
||||||
|
print(f"❌ Database non trovato: {DB_PATH}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
# Calcola l'orario UTC di 24h fa (perché il DB è in UTC)
|
||||||
|
yesterday = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=24)
|
||||||
|
|
||||||
|
query = "SELECT download, upload, ping, created_at FROM results WHERE created_at > ? AND status = 'completed' ORDER BY created_at ASC"
|
||||||
|
cursor.execute(query, (yesterday,))
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Errore DB: {str(e)}")
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
|
if 'conn' in locals(): conn.close()
|
||||||
|
|
||||||
|
if not rows:
|
||||||
|
print("ℹ️ Nessun test trovato.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Variabili per statistiche
|
||||||
|
total_down = 0
|
||||||
|
total_up = 0
|
||||||
|
count = 0
|
||||||
|
issues = 0
|
||||||
|
|
||||||
|
# Intestazione Messaggio
|
||||||
|
# Usiamo l'ora locale del sistema per l'intestazione
|
||||||
|
now_local = datetime.datetime.now()
|
||||||
|
msg = f"📊 **REPORT VELOCITÀ 24H**\n📅 {now_local.strftime('%d/%m/%Y')}\n\n"
|
||||||
|
|
||||||
|
msg += "```text\n"
|
||||||
|
msg += "ORA | DOWN | UP \n"
|
||||||
|
msg += "------+------+-----\n"
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
# Conversione Bit -> Mbps
|
||||||
|
d_val = (int(row[0]) * 8) / 1000000
|
||||||
|
u_val = (int(row[1]) * 8) / 1000000
|
||||||
|
|
||||||
|
total_down += d_val
|
||||||
|
total_up += u_val
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
marker = ""
|
||||||
|
if d_val < WARN_DOWN or u_val < WARN_UP:
|
||||||
|
issues += 1
|
||||||
|
marker = "!"
|
||||||
|
|
||||||
|
# Parsing Data e CONVERSIONE TIMEZONE
|
||||||
|
try:
|
||||||
|
d_str = row[3].split(".")[0].replace("T", " ")
|
||||||
|
# 1. Creiamo l'oggetto datetime e gli diciamo "Tu sei UTC"
|
||||||
|
dt_utc = datetime.datetime.strptime(d_str, '%Y-%m-%d %H:%M:%S').replace(tzinfo=datetime.timezone.utc)
|
||||||
|
|
||||||
|
# 2. Convertiamo nell'orario locale del sistema (definito da TZ nel docker-compose)
|
||||||
|
dt_local = dt_utc.astimezone()
|
||||||
|
|
||||||
|
time_str = dt_local.strftime('%H:%M')
|
||||||
|
except:
|
||||||
|
time_str = "--:--"
|
||||||
|
|
||||||
|
# Formattazione tabella
|
||||||
|
row_str = f"{time_str} | {d_val:>4.0f} | {u_val:>3.0f} {marker}"
|
||||||
|
msg += f"{row_str}\n"
|
||||||
|
|
||||||
|
msg += "```\n"
|
||||||
|
|
||||||
|
if count > 0:
|
||||||
|
avg_d = total_down / count
|
||||||
|
avg_u = total_up / count
|
||||||
|
|
||||||
|
icon_d = "✅" if avg_d >= WARN_DOWN else "⚠️"
|
||||||
|
icon_u = "✅" if avg_u >= WARN_UP else "⚠️"
|
||||||
|
|
||||||
|
msg += f"📈 **MEDIA:**\n⬇️ {icon_d} `{avg_d:.0f}` Mbps\n⬆️ {icon_u} `{avg_u:.0f}` Mbps"
|
||||||
|
|
||||||
|
if issues > 0:
|
||||||
|
msg += f"\n\n⚠️ **{issues}** test sotto soglia (!)"
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
report = generate_report()
|
||||||
|
if report:
|
||||||
|
send_telegram_message(report)
|
||||||
56
scripts/pi2-backup/dhcp-watchdog.sh
Executable file
56
scripts/pi2-backup/dhcp-watchdog.sh
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# --- CONFIGURAZIONE ---
|
||||||
|
MASTER_IP="192.168.128.80"
|
||||||
|
CONFIG_FILE="/etc/pihole/pihole.toml"
|
||||||
|
LOG_FILE="/var/log/dhcp-watchdog.log"
|
||||||
|
|
||||||
|
# Funzione per il logging
|
||||||
|
log_message() {
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
|
||||||
|
echo "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Funzione ROBUSTA v2 per leggere lo stato
|
||||||
|
# Usa i campi ($1, $2) per ignorare gli spazi di indentazione
|
||||||
|
is_dhcp_active() {
|
||||||
|
val=$(awk '/^\[dhcp\]/{flag=1} flag && $1=="active" && $2=="=" {print $3; exit}' "$CONFIG_FILE")
|
||||||
|
echo "$val"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Funzione per ATTIVARE (sed robusto)
|
||||||
|
enable_dhcp() {
|
||||||
|
# Cerca nel blocco [dhcp] e sostituisce mantenendo l'indentazione (\1)
|
||||||
|
sudo sed -i '/^\[dhcp\]/,/^\[/ s/^\(\s*\)active\s*=\s*false/\1active = true/' "$CONFIG_FILE"
|
||||||
|
sudo systemctl restart pihole-FTL
|
||||||
|
}
|
||||||
|
|
||||||
|
# Funzione per DISATTIVARE (sed robusto)
|
||||||
|
disable_dhcp() {
|
||||||
|
sudo sed -i '/^\[dhcp\]/,/^\[/ s/^\(\s*\)active\s*=\s*true/\1active = false/' "$CONFIG_FILE"
|
||||||
|
sudo systemctl restart pihole-FTL
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- LOGICA DI CONTROLLO ---
|
||||||
|
|
||||||
|
if ping -c 3 -W 5 "$MASTER_IP" &> /dev/null; then
|
||||||
|
# MASTER VIVO
|
||||||
|
CURRENT_STATUS=$(is_dhcp_active)
|
||||||
|
if [ "$CURRENT_STATUS" == "true" ]; then
|
||||||
|
log_message "RECOVERY: Master tornato online. Disattivo DHCP Backup."
|
||||||
|
disable_dhcp
|
||||||
|
log_message "STATO: DHCP disattivato."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# MASTER FORSE MORTO
|
||||||
|
sleep 30
|
||||||
|
if ! ping -c 3 -W 5 "$MASTER_IP" &> /dev/null; then
|
||||||
|
# CONFERMATO MORTO
|
||||||
|
CURRENT_STATUS=$(is_dhcp_active)
|
||||||
|
if [ "$CURRENT_STATUS" == "false" ]; then
|
||||||
|
log_message "EMERGENZA: Master irraggiungibile. Attivo DHCP Backup."
|
||||||
|
enable_dhcp
|
||||||
|
log_message "STATO: DHCP ATTIVO."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user