Compare commits
14
Commits
prerestart
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4850bc067 | ||
|
|
fd37656586 | ||
|
|
6f4fda8993 | ||
|
|
cce95f2c38 | ||
|
|
90e2df955d | ||
|
|
15c59df140 | ||
|
|
8b741a5728 | ||
|
|
293c3b05c0 | ||
|
|
6ea15021ec | ||
|
|
5e2f3868be | ||
|
|
5709f32fcd | ||
|
|
b1d1c07846 | ||
|
|
15ab66fef2 | ||
|
|
4b12975107 |
+64
-3
@@ -1,10 +1,10 @@
|
||||
#u!/usr/bin/env python3
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
|
||||
from flask_login import LoginManager, login_user, login_required, logout_user, UserMixin, current_user
|
||||
from flask_bcrypt import Bcrypt
|
||||
from cryptography.fernet import Fernet
|
||||
from datetime import datetime
|
||||
import sqlite3, glob, os, re
|
||||
import sqlite3, glob, os, re, subprocess
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = "309cc4d5ce1fe7486ae25cbd232bbdfe6a72539c03f0127d372186dbdc0fc928"
|
||||
@@ -15,6 +15,7 @@ login_manager.login_view = "login"
|
||||
login_manager.init_app(app)
|
||||
|
||||
DB_PATH = "/srv/poe_manager/sqlite.db"
|
||||
POE_SCRIPT = "/usr/local/bin/custom/poe.sh"
|
||||
|
||||
class User(UserMixin):
|
||||
def __init__(self, id_, username, is_admin):
|
||||
@@ -115,7 +116,7 @@ def index():
|
||||
# Geräte aus DB laden
|
||||
conn = sqlite3.connect("sqlite.db")
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT mac, name, is_active FROM devices ORDER BY name ASC")
|
||||
c.execute("SELECT mac, name, rpi_ip, switch_hostname, port, is_active FROM devices ORDER BY name ASC")
|
||||
devices = c.fetchall()
|
||||
|
||||
# Intervall aus DB laden
|
||||
@@ -458,6 +459,66 @@ def get_log():
|
||||
except Exception as e:
|
||||
return f"Fehler beim Lesen des Logs: {e}"
|
||||
|
||||
@app.route("/restart/<mac>", methods=["POST"])
|
||||
@login_required
|
||||
def restart_device(mac):
|
||||
|
||||
conn = sqlite3.connect("sqlite.db")
|
||||
conn.row_factory = sqlite3.Row
|
||||
c = conn.cursor()
|
||||
|
||||
c.execute("""
|
||||
SELECT
|
||||
mac,
|
||||
name,
|
||||
is_active
|
||||
FROM devices
|
||||
WHERE mac=?
|
||||
""", (mac,))
|
||||
|
||||
device = c.fetchone()
|
||||
conn.close()
|
||||
|
||||
if not device:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"message": "Gerät wurde nicht gefunden."
|
||||
}), 404
|
||||
|
||||
if device["is_active"] == 0:
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"message": f"{device['name']} ist deaktiviert."
|
||||
}), 400
|
||||
|
||||
try:
|
||||
|
||||
subprocess.Popen(
|
||||
[
|
||||
"/bin/bash",
|
||||
POE_SCRIPT,
|
||||
"restart",
|
||||
mac
|
||||
],
|
||||
env={
|
||||
"PATH": "/srv/poe_manager/venv/bin:" + os.environ["PATH"]
|
||||
},
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"device": device["name"]
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"message": str(e)
|
||||
}), 500
|
||||
|
||||
@app.route('/logs')
|
||||
@login_required
|
||||
def logs():
|
||||
|
||||
@@ -29,7 +29,16 @@ def generate_ips_list():
|
||||
switch_pass = ""
|
||||
|
||||
port = dev['port'] or ""
|
||||
print(f"{dev['rpi_ip']}:{dev['name']}:{switch_ip}:{dev['switch_hostname'] or 'kein Switch'}:{port}:{switch_user}:{switch_pass}")
|
||||
print(
|
||||
f"{dev['rpi_ip']}|"
|
||||
f"{dev['name']}|"
|
||||
f"{switch_ip}|"
|
||||
f"{dev['switch_hostname'] or 'kein Switch'}|"
|
||||
f"{port}|"
|
||||
f"{switch_user}|"
|
||||
f"{switch_pass}|"
|
||||
f"{dev['mac']}"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_ips_list()
|
||||
|
||||
@@ -10,7 +10,14 @@
|
||||
<div class="row g-3">
|
||||
{% for d in devices %}
|
||||
<div class="col-6 col-md-4 col-lg-3 col-xl-2">
|
||||
<div class="card text-center p-2"
|
||||
<div class="card text-center p-2 device-card"
|
||||
style="cursor:pointer;"
|
||||
data-mac="{{ d[0] }}"
|
||||
data-name="{{ d[1] }}"
|
||||
data-ip="{{ d[2] }}"
|
||||
data-switch="{{ d[3] }}"
|
||||
data-port="{{ d[4] }}"
|
||||
data-active="{{ d[5] }}"
|
||||
{% if last_seen.get(d[0]) %}
|
||||
title="{{ last_seen[d[0]] }}"
|
||||
{% elif status[d[0]] == 'offline' %}
|
||||
@@ -19,11 +26,11 @@
|
||||
<div class="card-header">{{ d[1] }}</div>
|
||||
<div class="card-body">
|
||||
<span class="fw-bold" style="color:
|
||||
{% if d[2] == 0 %}gray
|
||||
{% if d[5] == 0 %}gray
|
||||
{% elif status[d[0]] == 'online' %}green
|
||||
{% else %}red
|
||||
{% endif %};">
|
||||
{% if d[2] == 0 %}
|
||||
{% if d[5] == 0 %}
|
||||
Deaktiviert
|
||||
{% else %}
|
||||
{% if status[d[0]] %}{{ status[d[0]]|capitalize }}{% else %}Unbekannt{% endif %}
|
||||
@@ -39,6 +46,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const intervalMinutes = {{ interval | int }}; // aus DB
|
||||
const intervalMilliseconds = intervalMinutes * 60 * 1000;
|
||||
let lastUpdateTime = Date.now();
|
||||
let selectedCard = null;
|
||||
let selectedMac = null;
|
||||
let selectedName = null;
|
||||
|
||||
const deviceModal = new bootstrap.Modal(
|
||||
document.getElementById("deviceModal")
|
||||
);
|
||||
|
||||
const restartModal = new bootstrap.Modal(
|
||||
document.getElementById("restartModal")
|
||||
);
|
||||
|
||||
function parseLogTimestamp(ts) {
|
||||
const parts = ts.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
||||
@@ -82,6 +100,90 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
.catch(err => console.error("Fehler beim Laden der Logs:", err));
|
||||
}
|
||||
|
||||
document.querySelectorAll(".device-card").forEach(card => {
|
||||
const mac = card.dataset.mac;
|
||||
const restartKey = "restart_" + mac;
|
||||
if (sessionStorage.getItem(restartKey)) {
|
||||
const span = card.querySelector(".fw-bold");
|
||||
if (span.innerText.trim().toLowerCase() === "online") {
|
||||
sessionStorage.removeItem(restartKey);
|
||||
}
|
||||
else {
|
||||
span.innerHTML =
|
||||
'<span class="spinner-border spinner-border-sm me-2"></span>Restarting...';
|
||||
span.style.color = "#ffc107";
|
||||
document.getElementById("deviceStatus").innerHTML =
|
||||
'<span class="spinner-border spinner-border-sm me-2"></span>Restarting...';
|
||||
}
|
||||
}
|
||||
card.addEventListener("click", function () {
|
||||
if (this.dataset.active == "0") {
|
||||
return;
|
||||
}
|
||||
if (sessionStorage.getItem("restart_" + this.dataset.mac)) {
|
||||
return;
|
||||
}
|
||||
selectedCard = this;
|
||||
selectedMac = this.dataset.mac;
|
||||
selectedName = this.dataset.name;
|
||||
document.getElementById("deviceModalTitle").innerText =
|
||||
this.dataset.name;
|
||||
document.getElementById("deviceIp").innerText =
|
||||
this.dataset.ip || "-";
|
||||
document.getElementById("deviceSwitch").innerText =
|
||||
this.dataset.switch || "-";
|
||||
document.getElementById("devicePort").innerText =
|
||||
this.dataset.port || "-";
|
||||
document.getElementById("deviceStatus").innerText =
|
||||
this.querySelector(".fw-bold").innerText;
|
||||
deviceModal.show();
|
||||
});
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById("restartButton")
|
||||
.addEventListener("click", function () {
|
||||
deviceModal.hide();
|
||||
document.getElementById("restartTitle").innerText =
|
||||
selectedName + " neu starten?";
|
||||
restartModal.show();
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById("confirmRestart")
|
||||
.addEventListener("click", function () {
|
||||
const button = this;
|
||||
button.disabled = true;
|
||||
fetch("/restart/" + encodeURIComponent(selectedMac), {
|
||||
method: "POST"
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
button.disabled = false;
|
||||
if (!data.success) {
|
||||
alert(data.message || "Neustart konnte nicht gestartet werden.");
|
||||
return;
|
||||
}
|
||||
restartModal.hide();
|
||||
deviceModal.hide();
|
||||
sessionStorage.setItem(
|
||||
"restart_" + selectedMac,
|
||||
"1"
|
||||
);
|
||||
const span = selectedCard.querySelector(".fw-bold");
|
||||
span.innerHTML =
|
||||
'<span class="spinner-border spinner-border-sm me-2"></span>Restarting...';
|
||||
span.style.color = "#ffc107";
|
||||
})
|
||||
.catch(error => {
|
||||
button.disabled = false;
|
||||
alert(
|
||||
"Fehler beim Starten des Neustarts.\n\n" +
|
||||
error
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Timer alle 1 Sekunde aktualisieren
|
||||
setInterval(updateTimer, 1000);
|
||||
|
||||
@@ -90,4 +192,104 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Geräteinformationen -->
|
||||
<!-- ========================================================= -->
|
||||
|
||||
<div class="modal fade"
|
||||
id="deviceModal"
|
||||
tabindex="-1"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5
|
||||
id="deviceModalTitle"
|
||||
class="modal-title">
|
||||
</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Schließen">
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table table-sm align-middle mb-0">
|
||||
<tr>
|
||||
<th style="width:120px;">IP-Adresse</th>
|
||||
<td id="deviceIp"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Switch</th>
|
||||
<td id="deviceSwitch"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Port</th>
|
||||
<td id="devicePort"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td id="deviceStatus"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
id="restartButton"
|
||||
class="btn btn-warning">
|
||||
🔄 Neustarten
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
data-bs-dismiss="modal">
|
||||
Schließen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ========================================================= -->
|
||||
<!-- Neustart bestätigen -->
|
||||
<!-- ========================================================= -->
|
||||
|
||||
<div class="modal fade"
|
||||
id="restartModal"
|
||||
tabindex="-1"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5
|
||||
id="restartTitle"
|
||||
class="modal-title">
|
||||
</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="mb-2">
|
||||
Der Neustart erfolgt durch einen kurzen
|
||||
PoE-Reset.
|
||||
</p>
|
||||
<p class="mb-0">
|
||||
Der Pi wird für wenige Sekunden
|
||||
vom Netzwerk getrennt.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
data-bs-dismiss="modal">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
id="confirmRestart"
|
||||
class="btn btn-danger">
|
||||
Neustarten
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -85,11 +85,41 @@ expect eof
|
||||
EOF
|
||||
}
|
||||
|
||||
function manual_restart() {
|
||||
local target_mac="$1"
|
||||
python3 /srv/poe_manager/generate_ips.py | while IFS='|' read -r rpi_ip dev_name switch_ip switch_hostname switch_port switch_user switch_pass mac; do
|
||||
if [[ "$mac" != "$target_mac" ]]; then
|
||||
continue
|
||||
fi
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Manueller Neustart von $dev_name gestartet." >> "$LOGFILE"
|
||||
if [ -n "$switch_port" ] && [ "$switch_port" != "None" ]; then
|
||||
disable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE deaktiviert." >> "$LOGFILE"
|
||||
sleep 2
|
||||
enable_poe "$switch_ip" "$switch_port" "$switch_user" "$switch_pass"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name PoE aktiviert." >> "$LOGFILE"
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') Manueller Neustart von $dev_name abgeschlossen." >> "$LOGFILE"
|
||||
else
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name besitzt keinen PoE-Port." >> "$LOGFILE"
|
||||
fi
|
||||
exit 0
|
||||
done
|
||||
}
|
||||
|
||||
if [[ "$1" == "restart" ]]; then
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "MAC-Adresse fehlt."
|
||||
exit 1
|
||||
fi
|
||||
manual_restart "$2"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "" > "$LOGFILE"
|
||||
|
||||
while true; do
|
||||
echo "--------------------------------------------------------------------" >> "$LOGFILE"
|
||||
python3 /srv/poe_manager/generate_ips.py | while IFS=: read -r rpi_ip dev_name switch_ip switch_hostname switch_port switch_user switch_pass; do
|
||||
python3 /srv/poe_manager/generate_ips.py | while IFS='|' read -r rpi_ip dev_name switch_ip switch_hostname switch_port switch_user switch_pass; do
|
||||
if ping -c 1 -W 2 "$rpi_ip" &> /dev/null; then
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $dev_name ist erreichbar!" >> "$LOGFILE"
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user