Compare commits
2
Commits
05ce194478
...
4a1cd00e6e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a1cd00e6e | ||
|
|
df2765b16a |
@@ -4,9 +4,17 @@ skoda.conf
|
|||||||
*.conf
|
*.conf
|
||||||
secrets.conf
|
secrets.conf
|
||||||
|
|
||||||
|
# Ausnahme von der Zeile darueber: hier stehen keine Zugangsdaten, sondern
|
||||||
|
# die Rotationsregeln, und die gehoeren zum Betrieb wie die Startskripte.
|
||||||
|
!logrotate.conf
|
||||||
|
|
||||||
# Protokolle. wattpilotshell.log ist allein 329 MB gross, die Logs machen
|
# Protokolle. wattpilotshell.log ist allein 329 MB gross, die Logs machen
|
||||||
# 93 Prozent des Verzeichnisses aus.
|
# 93 Prozent des Verzeichnisses aus.
|
||||||
*.log
|
*.log
|
||||||
|
# Die rotierten Staende dazu - *.log.1, *.log.2.gz und so fort.
|
||||||
|
*.log.*
|
||||||
|
# Merkzettel von logrotate: wann welche Datei zuletzt dran war.
|
||||||
|
logrotate.state
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
@@ -256,6 +256,14 @@ dieselbe Client-Kennung benutzen. Genau davor schützt das Beenden am Anfang.
|
|||||||
Ausgabe landet in `autoActions.log` neben `solarOutput.log`. `SIGTERM` fängt
|
Ausgabe landet in `autoActions.log` neben `solarOutput.log`. `SIGTERM` fängt
|
||||||
der Runner ab und fährt geordnet herunter.
|
der Runner ab und fährt geordnet herunter.
|
||||||
|
|
||||||
|
Nachzulesen ist beides im Dashboard unter **Protokolle** — die Seite liest die
|
||||||
|
Dateien direkt, filterbar nach Text und Level. Klein gehalten werden sie von
|
||||||
|
`logs_rotieren.sh` samt `logrotate.conf` eine Ebene höher: täglich, vierzehn
|
||||||
|
Stände, alles über 20 MB sofort. Weil die Prozesse monatelang durchlaufen,
|
||||||
|
arbeitet die Rotation mit `copytruncate` — deshalb leiten die Startskripte mit
|
||||||
|
`>>` um und nicht mehr mit `&>`. Wer das zurückdreht, bekommt nach der
|
||||||
|
nächsten Rotation eine Logdatei, die vorn aus Nullbytes besteht.
|
||||||
|
|
||||||
`fetch_calendar.py` gehört einmal jährlich in den Cron:
|
`fetch_calendar.py` gehört einmal jährlich in den Cron:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -138,16 +138,50 @@ class Regelwerk:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def signatur_lesen(db):
|
def signatur_lesen(db):
|
||||||
"""
|
"""
|
||||||
Woran der Runner merkt, dass er neu laden muss. `changed` allein
|
Woran der Runner merkt, dass er neu laden muss.
|
||||||
genuegt nicht: eine geloeschte Automatik veraendert den groessten
|
|
||||||
Zeitstempel nicht. Deshalb zaehlen die Zeilen mit - auch die der
|
`changed` allein genuegt nicht: eine geloeschte Automatik veraendert
|
||||||
Geraetetabellen, damit ein Discovery-Lauf ebenfalls durchschlaegt.
|
den groessten Zeitstempel nicht. Die Zeilen zu zaehlen genuegt aber
|
||||||
|
auch nicht - die Weboberflaeche speichert eine Automatik, indem sie
|
||||||
|
deren Bedingungen und Aktionen loescht und gleich wieder einfuegt. Wer
|
||||||
|
nur die Uhrzeit einer Bedingung verstellt, aendert damit weder die
|
||||||
|
Zeilenzahl noch `changed`: ON UPDATE stoesst nur an, wenn sich in
|
||||||
|
automations wirklich eine Spalte aendert, und Name, Stockwerk und
|
||||||
|
Zeitfenster stehen ja noch genauso da. Der Runner lief dann bis zum
|
||||||
|
naechsten Neustart mit der alten Uhrzeit weiter, ohne dass irgendwo
|
||||||
|
etwas schieflief - er wusste es schlicht nicht besser.
|
||||||
|
|
||||||
|
Deshalb geht jetzt der Inhalt mit ein, als Summe der CRC32 je Zeile.
|
||||||
|
Das ist kein Hash mit Sicherheitsanspruch, sondern ein billiger
|
||||||
|
Fingerabdruck - er wird alle paar Sekunden gebildet und darf nichts
|
||||||
|
kosten. Zwei Aenderungen, die sich in der Summe gegenseitig aufheben,
|
||||||
|
sind theoretisch denkbar und praktisch nicht zu erwarten. Die
|
||||||
|
Zeilenzahl steht trotzdem daneben, damit eine geloeschte und eine neu
|
||||||
|
angelegte Zeile nicht zufaellig gleich viel ergeben.
|
||||||
|
|
||||||
|
Neu dabei sind die Aktionsparameter. Sie standen vorher gar nicht
|
||||||
|
drin: eine geaenderte Zielhoehe einer Jalousie schlug also ebenso
|
||||||
|
wenig durch.
|
||||||
|
|
||||||
|
Die Zeilenzahlen der Geraetetabellen bleiben, damit ein
|
||||||
|
Discovery-Lauf weiterhin ein Neuladen ausloest.
|
||||||
"""
|
"""
|
||||||
with db.cursor() as c:
|
with db.cursor() as c:
|
||||||
c.execute("""SELECT (SELECT COUNT(*) FROM automations) AS a,
|
c.execute("""SELECT (SELECT COUNT(*) FROM automations) AS a,
|
||||||
(SELECT UNIX_TIMESTAMP(MAX(changed)) FROM automations) AS t,
|
(SELECT UNIX_TIMESTAMP(MAX(changed)) FROM automations) AS t,
|
||||||
(SELECT COUNT(*) FROM automation_conditions) AS b,
|
(SELECT COUNT(*) FROM automation_conditions) AS b,
|
||||||
|
(SELECT COALESCE(SUM(CRC32(CONCAT_WS(':',
|
||||||
|
id, automation_id, group_no, position,
|
||||||
|
state_id, operator, value))), 0)
|
||||||
|
FROM automation_conditions) AS bs,
|
||||||
(SELECT COUNT(*) FROM automation_actions) AS c,
|
(SELECT COUNT(*) FROM automation_actions) AS c,
|
||||||
|
(SELECT COALESCE(SUM(CRC32(CONCAT_WS(':',
|
||||||
|
id, automation_id, position, command_id))), 0)
|
||||||
|
FROM automation_actions) AS cs,
|
||||||
|
(SELECT COUNT(*) FROM automation_action_params) AS p,
|
||||||
|
(SELECT COALESCE(SUM(CRC32(CONCAT_WS(':',
|
||||||
|
action_id, parameter_id, value))), 0)
|
||||||
|
FROM automation_action_params) AS ps,
|
||||||
(SELECT COUNT(*) FROM actor_states) AS d,
|
(SELECT COUNT(*) FROM actor_states) AS d,
|
||||||
(SELECT COUNT(*) FROM actor_commands) AS e""")
|
(SELECT COUNT(*) FROM actor_commands) AS e""")
|
||||||
return tuple(sorted(c.fetchone().items()))
|
return tuple(sorted(c.fetchone().items()))
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Rotation der Logdateien des SolarManagers.
|
||||||
|
#
|
||||||
|
# Aufgerufen wird das ueber logs_rotieren.sh, einmal taeglich aus dem
|
||||||
|
# Aufgabenplaner. Nicht in /etc/logrotate.d abgelegt: das ist DSM-Gebiet und
|
||||||
|
# waere beim naechsten Systemupdate weg. Hier steht es neben den Prozessen,
|
||||||
|
# zu denen es gehoert, und liegt im selben Repository.
|
||||||
|
#
|
||||||
|
# copytruncate ist der Kern der Sache. Die Prozesse laufen monatelang durch
|
||||||
|
# und halten ihre Logdatei offen; niemand will sie fuer eine Rotation neu
|
||||||
|
# starten. Also wird der Inhalt weggeschrieben und die Datei geleert, statt
|
||||||
|
# sie umzubenennen - der Prozess merkt nichts davon. Voraussetzung ist, dass
|
||||||
|
# die Startskripte mit ">>" umleiten und nicht mit "&>", sonst schreibt der
|
||||||
|
# Prozess nach dem Leeren an seiner alten Stelle weiter und die Datei
|
||||||
|
# bekommt ein Loch aus Nullbytes. Genau deshalb wurde das dort umgestellt.
|
||||||
|
#
|
||||||
|
# Der Preis von copytruncate sind die Zeilen, die zwischen Kopieren und
|
||||||
|
# Leeren hereinkommen - die gehen verloren. Bei einem Betriebslog ist das
|
||||||
|
# der guenstigere Handel.
|
||||||
|
|
||||||
|
/volume1/homes/wagner/SolarManager/solarOutput.log
|
||||||
|
/volume1/homes/wagner/SolarManager/autoActions.log
|
||||||
|
/volume1/homes/wagner/SolarManager/wattpilotshell.log
|
||||||
|
/volume1/homes/wagner/SolarManager/wsMQTTbridge.log
|
||||||
|
/volume1/homes/wagner/SolarManager/wecker.log
|
||||||
|
{
|
||||||
|
daily
|
||||||
|
# Zwei Wochen zurueck. Weiter zurueck hat noch nie jemand gesucht, und
|
||||||
|
# was aelter ist, steht ohnehin als Messwert in der Datenbank.
|
||||||
|
rotate 14
|
||||||
|
# Zusaetzlich zur Tagesgrenze: was ueber 20 MB geht, wird sofort
|
||||||
|
# rotiert. Sonst koennte ein Prozess, der in eine Fehlerschleife
|
||||||
|
# geraet, an einem einzigen Tag das Volume fuellen.
|
||||||
|
maxsize 20M
|
||||||
|
compress
|
||||||
|
# Eine leere Datei zu rotieren bringt nichts ausser vierzehn leeren
|
||||||
|
# Archiven - der Wecker meldet an den meisten Tagen gar nichts.
|
||||||
|
notifempty
|
||||||
|
# Fehlt eine Datei, ist das kein Fehler: nicht jeder Prozess laeuft
|
||||||
|
# auf jedem System.
|
||||||
|
missingok
|
||||||
|
copytruncate
|
||||||
|
}
|
||||||
Executable
+28
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Rotiert die Logdateien des SolarManagers.
|
||||||
|
#
|
||||||
|
# Gehoert einmal taeglich in den Aufgabenplaner (Benutzer wagner genuegt,
|
||||||
|
# root ist nicht noetig - alle Dateien gehoeren wagner). Die Regeln stehen in
|
||||||
|
# logrotate.conf daneben.
|
||||||
|
#
|
||||||
|
# Der Zustand kommt ausdruecklich nicht aus /var/lib/logrotate: dort liegt
|
||||||
|
# der von DSM, und ein zweiter Aufrufer haette sich dort mit dem System in
|
||||||
|
# die Quere gebracht. Die eigene Datei bleibt unter unserer Kontrolle und
|
||||||
|
# ist bei Bedarf einfach zu loeschen - dann faengt die Rotation von vorn an.
|
||||||
|
#
|
||||||
|
# Ein Lauf dauert ein bis zwei Minuten, auch wenn nichts zu tun ist. Das
|
||||||
|
# liegt nicht an uns: Synologys logrotate zaehlt bei jedem Start erst den
|
||||||
|
# Platzverbrauch unter /var/log zusammen und stolpert dabei ueber Ordner,
|
||||||
|
# die wagner nicht lesen darf. Die Meldungen "Permission denied" von /bin/du
|
||||||
|
# sind harmlos und gehoeren dazu.
|
||||||
|
#
|
||||||
|
# ./logs_rotieren.sh normaler Lauf
|
||||||
|
# ./logs_rotieren.sh -f sofort rotieren, auch wenn noch nichts faellig
|
||||||
|
# ./logs_rotieren.sh -d nur zeigen, was passieren wuerde
|
||||||
|
|
||||||
|
BASIS="/volume1/homes/wagner/SolarManager"
|
||||||
|
|
||||||
|
exec /usr/bin/logrotate \
|
||||||
|
--state "$BASIS/logrotate.state" \
|
||||||
|
"$@" \
|
||||||
|
"$BASIS/logrotate.conf"
|
||||||
Regular → Executable
+5
-1
@@ -16,4 +16,8 @@ fi
|
|||||||
|
|
||||||
# Start the script
|
# Start the script
|
||||||
cd "/volume1/homes/wagner/SolarManager/"
|
cd "/volume1/homes/wagner/SolarManager/"
|
||||||
/usr/bin/python3 "$SCRIPT_NAME" &> "$LOG_FILE" &
|
# Angehaengt statt ueberschrieben: logrotate leert diese Datei mit
|
||||||
|
# copytruncate, waehrend der Prozess sie offen haelt. Ohne Anhaengemodus
|
||||||
|
# schriebe er danach an seiner alten Stelle weiter, und die Datei bekaeme ein
|
||||||
|
# Loch aus Nullbytes in der Groesse des bisherigen Inhalts.
|
||||||
|
/usr/bin/python3 "$SCRIPT_NAME" >> "$LOG_FILE" 2>&1 &
|
||||||
Regular → Executable
+7
-1
@@ -46,7 +46,13 @@ beenden() {
|
|||||||
starte() {
|
starte() {
|
||||||
beenden "$1"
|
beenden "$1"
|
||||||
echo "Starte $1, Ausgabe nach $2"
|
echo "Starte $1, Ausgabe nach $2"
|
||||||
"$PYTHON" "$1" &> "$2" &
|
# Angehaengt, nicht ueberschrieben. Zwei Gruende: nach einem Neustart
|
||||||
|
# will man sehen, warum der alte Lauf aufgehoert hat, und logrotate
|
||||||
|
# arbeitet hier mit copytruncate - es leert die Datei, waehrend der
|
||||||
|
# Prozess sie offen haelt. Beim Ueberschreiben stuende dessen
|
||||||
|
# Schreibmarke danach weit hinten, und die Datei bekaeme ein Loch aus
|
||||||
|
# Nullbytes in der Groesse des bisherigen Inhalts.
|
||||||
|
"$PYTHON" "$1" >> "$2" 2>&1 &
|
||||||
}
|
}
|
||||||
|
|
||||||
starte "solarManager.py" "solarOutput.log"
|
starte "solarManager.py" "solarOutput.log"
|
||||||
|
|||||||
Regular → Executable
+7
-1
@@ -42,4 +42,10 @@ fi
|
|||||||
|
|
||||||
# Start the script
|
# Start the script
|
||||||
cd "$BASIS"
|
cd "$BASIS"
|
||||||
/var/services/homes/wagner/.local/bin/wattpilotshell server &> "$LOG_FILE" &
|
# Angehaengt statt ueberschrieben: logrotate leert diese Datei mit
|
||||||
|
# copytruncate, waehrend der Prozess sie offen haelt. Ohne Anhaengemodus
|
||||||
|
# schriebe er danach an seiner alten Stelle weiter, und die Datei bekaeme ein
|
||||||
|
# Loch aus Nullbytes in der Groesse des bisherigen Inhalts. Bei dieser Datei
|
||||||
|
# faellt das besonders ins Gewicht - die wattpilotshell meldet einen
|
||||||
|
# Verbindungsfehler im Sekundentakt und hatte es so auf 329 MB gebracht.
|
||||||
|
/var/services/homes/wagner/.local/bin/wattpilotshell server >> "$LOG_FILE" 2>&1 &
|
||||||
|
|||||||
Regular → Executable
+6
-1
@@ -16,4 +16,9 @@ fi
|
|||||||
|
|
||||||
# Start the script
|
# Start the script
|
||||||
cd "/volume1/homes/wagner/SolarManager/"
|
cd "/volume1/homes/wagner/SolarManager/"
|
||||||
/usr/bin/python3 "$SCRIPT_NAME" &> "$LOG_FILE" &
|
# Angehaengt statt ueberschrieben. Dieses Skript laeuft jede Nacht um drei,
|
||||||
|
# und mit "&>" war die Datei danach jedes Mal leer - was der Wecker am
|
||||||
|
# Vortag gemeldet hat, war morgens nicht mehr nachzulesen. Ausserdem braucht
|
||||||
|
# logrotate den Anhaengemodus: es leert die Datei mit copytruncate, waehrend
|
||||||
|
# der Prozess sie offen haelt.
|
||||||
|
/usr/bin/python3 "$SCRIPT_NAME" >> "$LOG_FILE" 2>&1 &
|
||||||
Reference in New Issue
Block a user