Dienstag, 30. Juli 2019

Definitionen in FHEM komplett umziehen

Ich habe mehrere System auf denen ich etwas teste und manchmal schleicht sich nach langer Zeit quasi "produktiver Betrieb" ein. Dann möchte ich gern die getestete Definition und alles was dazu gehört: Log, Logdateien, Plots usw. in das produktive System übernehmen. Da steht also die Aufgabe neben der eigentlichen Definition auch noch ein paar Dateien zu übernehmen. Ich habe mir dazu folgendes Vorgehen ausgedacht:
  1. Alle Dateien auf das Zielsystem kopieren oder das Quellsystem mounten
  2. Die wirklich benötigten Dateien an die richtige Stellen, mit den richtigen Berechtigungen, anhand der Definitionen in den FHEM Ordner kopieren.
  3. Die Definitionen vom Quell- auf das Zielsystem kopieren.
Dabei soll so viel wie möglich "automatisch" laufen. Damit ich wenig installieren und konfigurieren muss, hole ich alle potentiellen Dateien mit scp einfach pauschal lokal in ein temporäres Verzeichnis. Hat man große log Dateien - kann man auch einschränken.
host1='Hostname Instanz1'                        #Quell FHEM Instanz
tDir=fhem                                        #relativer Pfad der im lokalen Pfad (HomeDir des Users) angelegt und verwendet wird
mkdir -p $tDir/www
scp -r pi@$host1:/opt/fhem/log/ $tDir/           #kopiert den Remote Pfad log/ nach fhem/log/  
scp -r pi@$host1:/opt/fhem/www/gplot/ $tDir/www/ #kopiert den Remote Pfad www/gplot/ nach fhem/www/gplot/
Jetzt werden die wirklich notwendigen Dateien anhand der Definitionen ermittelt und lokal an die richtige Stelle mit den richtigen Berechtigungen kopiert. Ich mache das deshalb über FHEM.
Zu allen Schritten brauche ich meinen HTTP Client
wget -O fhemcl.sh https://raw.githubusercontent.com/heinz-otto/fhemcl/master/fhemcl.sh

Um die Einzeiler etwas übersichtlicher zu halten, definiere ich noch ein paar Variablen zu Beginn, $host1 und $tDir wurden im Befehlsblock oben schon definiert. In den Einzeilern wird einfach davon ausgegangen, dass der lokale Zugriff nur die Port Nummer 8083 erfordert.
inst1=$host1:8083                #URL Remote Instanz [http://[<user:password@>][<hostName>:]]<portNummer> 
rDir=$(pwd)/$tDir/               #absoluter Pfad für die (kopierten) Quelldateien
gDir='./www/gplot/'              #Pfad in dem die gplot Datei in FHEM liegen
devSpec='FILTER=NAME=.*Sensor.*' #wird in den Befehlen um TYPE= ergänzt
Man kann selbstverständlich die Quelldateien remote mit mount einbinden (z.B. nfs). Dann enthält $rDir den gemounteten Pfad.

Achtung: Die Einzeiler enthalten keine Fehlerbehandlung! Es kann also zu unvorhergesehenen Auswirkungen kommen!
Datensicherung!
Die Kopiervorgänge werden eventuell nicht oder falsch ausgeführt, können aber gleichnamige Dateien überschreiben! Die Änderungen in der cfg werden nicht automatisch gesichert, ein restart von FHEM stellt den alten Zustand wieder her.
Die devSpec sollte man im Zweifel immer testen!
bash fhemcl.sh $inst1 "list TYPE=.*:$devSpec"
Man sollte auch immer zunächst die Einzeiler ohne den letzten Ausführungsteil zur Probe laufen lassen!

Einzelne Schritte

Zunächst als "Fingerübung" die Grundlagen um aus bestimmten Definitionen, die "Sensor" im Namen haben, die Dateien zu kopieren.
  • der list Befehl erzeugt eine Tabelle aus Name und dem entsprechenden INTERNAL, von dieser wird nur die zweite Spalte benötigt,
    • für die log Datei wird mit sed alle Platzhalter %Y,%m usw. durch * ersetzt. Damit werden alle vorhandenen Log Dateien erfasst. 
    • für die gplot Datei wird mit sed noch $gDir vorangestellt und ".gplot" angehängt, 
  • der zweite sed Befehl ersetzt ./ durch den absoluten Pfad $rDir
    • Der Ausdruck ${rDir////\\/} erzeugt dabei aus dem normalen Pfadnamen einen "escaped" Pfadnamen: ersetzt also / durch \/.
  • Der tr Befehl am Ende macht aus der Zeilen- eine Leerzeichen getrennte Liste für den cp Befehl.

Die FileLog (log) Dateien

datei=$(bash fhemcl.sh $inst1 "list TYPE=FileLog:$devSpec logfile"|awk '{print $2}'|sed "s/%./*/g"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' ')
bash fhemcl.sh 8083 "\"cp -n $datei ./log/\""

Die SVG (gplot) Dateien

datei=$(bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec GPLOTFILE"|awk '{print $2}'|sed "s/^/${gDir////\\/}/;s/$/.gplot/"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' ')
bash fhemcl.sh 8083 "{qx(cp -n $datei ./www/gplot/)}"
Ich verwende bewusst zwei unterschiedliche Kopierbefehle. Die erste Variante blockiert das lokale FHEM nicht, allerdings hat man keine Rückmeldung wann der Kopiervorgang beendet ist. Die zweite Variante blockiert FHEM, was bei den kleinen gplot Dateien aber nicht stören sollte.

Definition

Jetzt brauchen nur noch die Definitionen auf das lokale Zielsystem kopiert werden.
bash fhemcl.sh $inst1 "list -r TYPE=FileLog:$devSpec"|bash fhemcl.sh 8083
bash fhemcl.sh $inst1 "list -r TYPE=SVG:$devSpec"|bash fhemcl.sh 8083

Verbundene Definitionen

Der list Befehl ist sehr vielseitig! Neben Gruppen von Definitionen kann man auch einzelne Geräte und deren komplette verbundenen Definitionen umziehen.
Achtung: "-R" funktioniert so richtig nur mit Device Namen ohne regExp, "-R" ist laut Doku auch "unvollkommen"!
So wird die SVG zusammen mit der zugehörigen FileLog Instanz übertragen.
bash fhemcl.sh $inst1 "list -R SVG_FileLog_SensorWG_1"|bash fhemcl.sh 8083

Abhängigkeit

Die SVG Definition und deren Funktion ist von der zugehörigen FileLog Definition abhängig. Also kann man von der SVG Definition alles notwendige ableiten. Die log Dateien anhand des SVG kopieren:
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec LOGDEVICE"|sed 's/^.*\s/list /;s/$/ logfile/'|bash fhemcl.sh $inst1|awk '{print $2}'
Der Vorgang ist zweistufig, erst wird das LOGDEVICE aus der SVG Definition gelesen, daraus wieder ein list Befehl erzeugt und damit das log File direkt aus der Definition ausgelesen.
Nach dem obigen Schema kann man daraus die Dateiliste erstellen.
|sed "s/%./*/g"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' '
Anstatt über eine Variable ($datei) kann man die Pipe auch fortführen.
|echo "\"cp -n $(cat -) ./log/\""|bash fhemcl.sh 8083
Der komplette Einzeiler ist wirklich lang.
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec LOGDEVICE"|sed 's/^.*\s/list /;s/$/ logfile/'|bash fhemcl.sh $inst1|awk '{print $2}'|sed "s/%./*/g"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' '|echo "\"cp -n $(cat -) ./log/\""|bash fhemcl.sh 8083

Um den list -R Befehl mit einer devSpec zu verwenden, muss man diese quasi erst auflösen.
Achtung: Wenn die devSpec nur eine Definition trifft, schlägt dieser einfache Einzeiler fehl!
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec"|sed 's/^/list -R /'|bash fhemcl.sh $inst1|bash fhemcl.sh 8083

Finale

Damit hat man 3 Schritte ausgehend von einer devSpec:
  1. gplot Dateien kopieren
  2. log Dateien kopieren
  3. SVG Definitionen und die zugehörigen FileLog Definitionen übertragen.

bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec GPLOTFILE"|awk '{print $2}'|sed "s/^/${gDir////\\/}/;s/$/.gplot/"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' '|echo "\"cp -n $(cat -) ./www/gplot/\""|bash fhemcl.sh 8083
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec LOGDEVICE"|sed 's/^.*\s/list /;s/$/ logfile/'|bash fhemcl.sh $inst1|awk '{print $2}'|sed "s/%./*/g"|sed "s/.\//${rDir////\\/}/"|tr '\n' ' '|echo "\"cp -n $(cat -) ./log/\""|bash fhemcl.sh 8083
bash fhemcl.sh $inst1 "list TYPE=SVG:$devSpec"|sed 's/^/list -R /'|bash fhemcl.sh $inst1|bash fhemcl.sh 8083


Donnerstag, 25. Juli 2019

rootfs auf ein anderes Laufwerk verschieben

Der Startparameter root= legt für den Linuxkernel fest wo das root FileSystem zu finden ist.
In der dann dort gelesenen /etc/fstab wird festgelegt wohin welches Laufwerk eingehängt/gemounted wird. Das gilt auch für das rootfs.
Anhand vom SingeBoardComputer Odroid HC1 will ich zeigen wie man das rootfs vom Originalstandort: 2. Partition der SD Card auf eine über Sata angeschlossene Disc (oder irgendein anders Laufwerk) verschieben kann.

Beschreibungen dazu habe ich einige gefunden, ich habe versucht sie zu verstehen und für mich so simpel wie möglich umzusetzen. Prinzipiell wird das in der Art für jeden SBC funktionieren.

Der Systemstart ist dann wie folgt: Bootsystem von SD Card lesen, rootfs auf der zweiten Disc über Sata oder USB starten.
Es gibt auch SBCs die direkt (ohne Umweg über SD Card) von einer Sata/Usb Disc booten können!
Hinweis:
Mittlerweile können einige Hardwareversionen des Raspberry Pi auch direkt von USB booten.
Dazu habe ich auch einen neuen Artikel gemacht.
Nachtrag 2022: Ich bin beim Odroid auf Armbian umgestiegen. Bei Armbian empfehle ich diesen Vorgang einfacher mit sudo armbian-config / System ... oder mit dem Script sudo nand-sata-install durchführen. Siehe dazu auch die Ergänzung am Ende des Artikels.

Donnerstag, 18. Juli 2019

Infos zur Installation von Modulen und Paketen


  • Was ist auf meinem System in der Vergangenheit installiert worden?
  • Ist ein bestimmtes Modul oder Paket schon installiert?
  • Welches Debian Paket brauch ich für ein bestimmtes Perl Modul?


Ich habe etwas herum experimentiert und dabei sind ein paar Einzeiler für die Beantwortung dieser Fragen entstanden.

Historie

Die erste Frage kann man relativ leicht beantworten, das apt System loggt nämlich alle apt-get Vorgänge sogar mit der eingegeben Kommandozeile mit.
cat /var/log/apt/history.log|grep Commandline
Besser zuordnen lässt sich das noch in der Art.
cat /var/log/apt/history.log|grep -A1 Start-Date
Beide Zeilen ergeben keine Ausgabe? Das gesuchte ist nicht dabei? Die Installation liegt schon zu lange zurück! Die Logs werden mit Log Rotate archiviert. Auch dieses Archive kann man komplett, zeitlich richtig sortiert, anzeigen/durchsuchen.
zcat $(ls -tr /var/log/apt/history.log.*.gz)|grep -A1 Start-Date

Man hat Perl Module per CPAN installiert?
Wenn man das debian Paket perl-doc installiert hat (oder noch installiert) kann man mit diesem Mammut-Einzeiler eine List der mit cpan installierten Module ausgeben (Quelle):
for M in `perldoc -t perllocal|grep Module |sed -e 's/^.*" //'`; do V=`perldoc -t perllocal|awk "/$M/{y=1;next}y" |grep VERSION |head -n 1`; printf "%30s %s\n" "$M" "$V"; done |sort

Installierte Modul und Pakete finden und listen

Wie man ermitteln kann ob ein bestimmtes Perl Modul oder debian Paket schon installiert ist habe ich hier schon mal gezeigt. Die beiden folgende Zeile liefern jeweils ein Liste aller installierter Pakete, entweder zeilenweise oder eine Zeile mit Leerzeichen getrennt. Der wichtige Schlüssel in dieser Zeile ist der String "^ii", mit diesem oder vielleicht einfacher einem weiteren grep 'suchstring' kann man die Rückgabe wesentlich beeinflussen. Beide Listentypen eignen sich für die weitere Verarbeitung.
dpkg -l |grep ^ii| awk '{ print $2 }'
dpkg -l |grep ^ii| awk '{ print $2 }'|tr -d "\r"|tr "\n" " "
Will man wissen ob ein bestimmtes Perl Modul installiert ist, kann man das einfach herausfinden. Man kann aber auch herausfinden ob und durch welches debian Paket es installiert wurde. Die zweite Version sucht nur in Paketen die -perl im Namen tragen und läuft damit wesentlich schneller.
s='Device::SerialPort'
perl -M$s -e '' 2>/dev/null &&echo "Modul $s ist vorhanden"
for i in $(dpkg -l |grep ^ii| awk '{ print $2 }'|tr -d "\r"|tr "\n" " ");do if dpkg -L $i|grep $s &> /dev/null;then echo $i enthält $s;fi;done
for i in $(dpkg -l |grep ^ii|grep '\-perl'| awk '{ print $2 }'|tr -d "\r"|tr "\n" " ");do if dpkg -L $i|grep $s &> /dev/null;then echo $i enthält $s;fi;done

Debian Pakete anhand von Perl Modulnamen finden

Es gibt gute Gründe die Installation von Perl Modulen über debian Pakete der Installation über cpan zu bevorzugen. Für mich zählt die Updatefähigkeit und die Geschwindigkeit. Man kann im Internet nach den Paketen suchen, man kann aber auch einfach die Verfügbarkeit für das lokale System testen. Dazu braucht man das Tool apt-file, welches man zuerst installieren muss. Der letzte Befehl lädt den Infocache lokal und dauert etwas.
sudo apt-get update
sudo apt-get install apt-file
sudo apt-file update
Für die folgende Suche, nutze ich den Umstand, dass in der im Paket enthaltenen Liste der Dateien auch die Doku in der Form "/usr/share/man/man3/IO::File.3perl.gz" enthalten ist. Ich ergänze also den Modulstring um "/" + "." und suche exakt danach. Da die Suche ziemlich zeitaufwendig ist, kann man auch nach mehreren Modulen in einem Vorgang suchen lassen. Der search Befehl akzeptiert mehrere Modulnamen durch Zeilenumbruch getrennt. In meinem Beispiel sind die Modulnamen in einem String durch Leerzeichen getrennt, die Trennung wird in Zeilenumbruch geändert, jede Zeile ergänzt und über stdin an den search Befehl von apt-file übergeben. Die Option -l bewirkt das nur die gefunden Pakete gelistet werden, werden mehrere der abgefragten Modulnamen in einem Paket gefunden, wird es nur einmal gelistet!
s="IO::File Digest::MD5"
echo $s|tr " " "\n"|sed 's/$/./;s/^/\//'|apt-file search -l -f -

Die Config analysieren

Mit Hilfe des Installermoduls, kann man auch eine bestehende Configdatei untersuchen und fehlende Pakete nachinstallieren. 
Voraussetzung: das neue FHEM System.
Mit einem Zusatzmodul, einer Definition sowi ein paar Zeilen Scriptcode kann man so testen ob alle Vorrausetzungen installiert sind bzw. die fehlenden Pakete installieren.
Danach backup zurückspielen und fertig. 
# System mit Suchmaschinen und Hilfsscript aufrüsten
sudo apt install apt-file libperl-prereqscanner-notquitelite-perl
sudo apt-file update
wget -O fhemcl.sh https://raw.githubusercontent.com/heinz-otto/fhemcl/master/fhemcl.sh
chmod +x fhemcl.sh

# Definition zum Testen erstellen
cat <<EOF | ./fhemcl.sh 8083
attr initialUsbCheck disable 1
define installer installer
attr installer installerMode developer
save
EOF

# Zu prüfende config Datei holen
scp pi@raspib3:/opt/fhem/fhem.cfg .
ref='/home/pi/fhem.cfg'

# Abfrage starten
s=$(./fhemcl.sh 8083 "get installer checkPrereqs $ref"|grep -oE 'installPerl.*&fwcsrf'|grep -oE '\s[a-z,A-Z,:]+\s')
packages=$(echo $s|tr " " "\n"|sed 's/$/./;s/^/\//'|apt-file search -l -f -)
echo 'debian Pakete fehlen:'
echo $packages
echo 'sudo apt install $packages' 

Gibt es noch mehr?
Oh ja, nodejs Pakete oder auch python.
Mit npm kann man so die "global" installierten Pakete listen lassen:
npm list -g --depth 0
Python ist ja Bestandteil des debian System, verwendet man zusätzliche Pakete hat man meist pip (python-pip) installiert. Python gibt es auch in der Version 3 (python3) dafür gibt es natürlich separat pip3 (python3-pip). Mit der Option -l verringert sich die Liste auf das, was man selbst installiert hat.
pip list -l
Und man kann auch schauen ob sich was neues findet.
pip list -l --outdated

Dienstag, 9. Juli 2019

Neues Linux System - Nacharbeit

Generell ist es relativ einfach eine bestehende FHEM Installation auf ein neues Betriebssystem/System zu setzen:
  1. Backup FHEM
  2. Kopie der aktuellen Backupdatei -> Server | USB Stick | per scp lokal
  3. sudo halt
  4. neue SD Card mit aktuellem Image (ich behalte immer die alte SD erstmal in Reserve)
  5. setupBasic, setupFhem, testen
  6. Backupdatei verfügbar machen -> Server | USB Stick mounten | per scp - lokal -> nach /home/pi
  7. stop fhem
  8. restore des Backups
  9. start fhem

Sicherung / Backup

Bei mir ist beim obigen Plan mindestens die Anbindung an Dateiserver und die ssh Verbindung auf der Strecke geblieben. Schritt 1 und 8 muss man dafür noch etwas ausbauen.

Sicherung ssh Umgebung

Die notwendigen Dateien befinden sich in diesen Verzeichnissen:
  • /etc/ssh für den ssh Host / Server
  • /opt/fhem/.ssh für den User fhem
  • /home/pi/.ssh für den User pi. 
Ich werde zwei zusätzliche Dateien in den Backup Ordner von FHEM erstellen. Dies könnte man auch von FHEM von Zeit zu Zeit machen lassen. Neben der Datei für eine Historie mit Zeitstempel erzeuge ich noch eine ohne Zeitstempel. Damit muss man bei der Wiederherstellung nicht lang suchen.
Man kann dieses als Script auch unter FHEM ausführen, da aber Systemdateien gesichert werden, muss tar als sudo ausgeführt werden. Damit das funktioniert, muss die zuständige Datei um /bin/tar ergänzt werden.
datei=/opt/fhem/backup/ssh$(date +%Y%m%d_%H%M%S).tar.gz
sudo tar -czf $datei /etc/ssh /opt/fhem/.ssh /home/pi/.ssh
sudo cp $datei /opt/fhem/backup/ssh.tar.gz
Falls der User fhem gar kein ssh verwendet kommt ein Fehler, dann muss man die Befehle abwandeln. Da die Dateien nicht voluminös sind, kann man auf zip verzichten. Damit kann das tar Archiv leichter mit der Option -rf ergänzt werden.
sudo tar -cf $datei /etc/ssh /home/pi/.ssh
cp $datei /opt/fhem/backup/ssh.tar
Der folgende Zweizeiler wirft zwar Fehler, sichert aber die ssh Dateien für alle existierenden User.
datei=ssh$(date +%Y%m%d_%H%M%S).tar
sudo tar rf $datei /etc/ssh $(awk -F: '$3>998{print $6"/.ssh"}' /etc/passwd)
Will man einfach in FHEM die ssh Key für user fhem sichern kann man das auch dort machen: 
"datei=./backup/sshFhem$(date +%Y%m%d_%H%M%S).tar;; tar -cf $datei ./.ssh"
Und auch später wiederherstellen:
"datei=./backup/sshFhem*.tar;; tar -xf $datei ./.ssh"

Sicherung Dateiserver Umgebung

datei=/opt/fhem/backup/fsconf$(date +%Y%m%d_%H%M%S).tar.gz
sudo tar -czf $datei /etc/fstab /usr/.*credentials /etc/davfs2/secrets
cp $datei /opt/fhem/backup/fsconf.tar.gz
Hat man Scripte o.ä. zusätzlich in anderen Pfaden, kann man die natürlich einfach in dem tar Befehl ergänzen und gleich mit sichern oder eine weitere Datei nach gleichem Schema erzeugen.

Finale Sicherung des alten Systems

Damit man den finalen Status im FHEM System behält, muss man FHEM beenden und dann ein backup durchführen!
systemctl stop fhem
datei=/opt/fhem/backup/FHEM-$(date +%Y%m%d_%H%M%S).tar.gz
tar -czf $datei -C /opt/fhem --exclude ./backup ./
Wenn das Backup zu Ende ist, startet man beispielsweise das Script /opt/fhem/backupFhem.sh und kopiert alle aktuellen Dateien damit auf den Sicherungsserver.
Am Ende des Artikels habe ich noch den Hinweis auf Alternativen zum Server.

Wiederherstellung

Nachdem das neue System grundlegend installiert und getestet ist (Ergänzung Schritt 5. optional: setupDavfs) wird die Sicherung zurückgespielt.
Die Verbindung zu meinem Windows Server geht relativ simpel. Ohne Angabe des Usernamens wird der momentane User (root) verwendet. Ohne Angabe des Passwortes wird es abgefragt:
sudo su
mount -t cifs //ServerName/Freigabe /mnt
mount -t cifs -o username=UserName //ServerName/Freigabe /mnt
mount -t cifs -o username=UserName,password=Passwort //ServerName/Freigabe /mnt
Ist das Laufwerk verbunden, kann auch ohne lokale Kopie sofort mit der Wiederherstellung begonnen werden.
datei=/mnt/fhem/$(hostname)/backup/FHEM-$(date +%Y%m%d_%H%M%S).tar.gz
tar -xzf $datei -C /opt/fhem
Querverweis: Falls die Module im alten System nicht gut dokumentiert waren, hilft eventuell dieser Beitrag.

Wiederherstellung ssh

Bei meinen Systemen wurde immer nur der ecsda Key verwendet. Ich möchte nicht die neue ssh Server Gesamtkonfiguration zerstören (da ändert sich immer mal was, ich habe nicht untersucht was genau) sondern nur das wiederherstellen was gebraucht wird. Unter der Annahme: es gibt nur eine aktuelle Datei im Verzeichnis, wird damit der ecdsa Key in /etc/ssh wieder hergestellt:
datei=/mnt/fhem/$(hostname)/backup/ssh.tar.gz
tar -xzf $datei -C / --wildcards etc/ssh/ssh_host_ecdsa_*
Um die User .ssh Verzeichnisse wieder herzustellen, sollte dieser Befehl gut sein
tar -xzf $datei -C / home/pi/.ssh/ opt/fhem/.ssh/
Damit steht die ssh Umgebung wieder.

Wiederherstellung Verbindungen zum Dateiserver

Die für die Verbindung verantwortlichen Scripte sollten im /opt/fhem Verzeichnis liegen und sind damit schon Teile der Wiederherstellung. Die Datei /etc/fstab und die jeweiligen Credential Dateien liegen aber unter Umständen "verstreut" im System und sind jetzt entweder nicht vorhanden oder neu gemacht.
Zunächst die credential Datei(en) in /usr/
datei=/mnt/fhem/$(hostname)/backup/fsconf.tar.gz
tar -xzf $datei -C / --wildcards usr/.*credentials
Die davfs2/secrets will ich nur ergänzen, davfs muss schon installiert sein!
tar -xzf $datei etc/davfs2/secrets -O|grep magentacloud >> /etc/davfs2/secrets
Ich möchte gern die fstab nicht überbügeln sondern die neue Datei einfach um meine Einträge ergänzen. Existieren z.B. mehrere Mountpoints in /media/ - kann das in einer Zeile erledigt werden:
tar -xzf $datei etc/fstab -O|grep /media/ >> /etc/fstab
Jetzt müssen noch die Mountpoint erzeugt werden:
mkdir $(tar -xzf $datei etc/fstab -O|grep -o '/media/[a-Z,0-9]*'|tr '\n' ' ')

Jetzt kann man das Sicherungslaufwerk wieder trennen.
umount /mnt
Die Verbindung zu davfs2 erfordert auch noch die Gruppenmitgliedschaft in der Gruppe davfs2
usermod -aG davfs2 fhem
Die Gruppenmitgliedschaft greift erst nach erneutem Login! Also mindestens FHEM neu starten!

Alternativen zum Sicherungsserver

USB Stick
Man könnte einen USB Stick verwenden, der Raspberry Pi 3 verkraftet das Stecken im laufenden Betrieb. Bei allen anderen Typen und "zur Vorsicht" ist aber vom Stecken eines USB Sticks im laufenden Betrieb abzuraten. Zuerst schauen welches Device der Stick geworden ist und dann einfach temporär mounten:
lsblk
mount /dev/sda1 /mnt

SCP - lokale Kopie
Mit scp ist es recht simpel sich eine Kopie der Dateien lokal zum holen und diese später zurück zu spielen. Geht auch unter Windows 10 ab 1809 einfach so!
scp pi@hostname:dateiname .
scp dateiname pi@hostname:
Die Restore Befehle muss man natürlich etwas abwandeln, entweder die Variable $datei wird einfach auf die lokale Datei Homedir von pi gesetzt, oder im tar Befehl die Datei direkt angeben.

Installation nodejs, pm2 und pip

Die Installation von nodejs hatte ich ja hier schon erläutert.
Zusätzlich brauche ich "global" noch den Nodejs Prozessmanager pm2.
sudo su
curl -sL https://deb.nodesource.com/setup_10.x | bash -
apt-get install nodejs
npm install pm2 -g
Trotz der Sicherung des gesamten Ordners installiere ich Landroid neu. Wie so oft gibt es mittlerweile neue Versionen.

Python Pakete werden mit Hilfe von pip installiert. Beispiel: die fhem API
apt-get install python-pip
pip install fhem


ToDo
Andere Applikationen unterhalb von /opt sichern

codebox

Freitag, 14. Juni 2019

Rockrobo - root und gut?

Es ist einige Zeit vergangen und ich habe im Mai 2021 den Artikel leicht überarbeitet.

Der Staubsauger ist in der Cloud und mein Typ Xiaomi Mi Robot Vacuum (rockrobo.vacuum.v1) wird auch noch von höchster Instanz (CCC) genau analysiert!
Was bedeutet es einen Staubsauger mit Root Zugriff zu haben?!
Markus hat gesagt: Es ist nur der ssh Zugriff - und irgendwann geht es vielleicht nicht mehr.
WLAN Zugang ändern und danach den Token neu auslesen - ist mit dem normalen Gerät ein Graus, aber mit ssh Zugang ganz einfach!?
Tobias hat mir mit seinem Beitrag die Hemmschwelle genommen - also los und Test!
Wie so oft: Meine Beschreibung wird auch irgendwann nicht mehr aktuell sein. 
Die Grundlage steht hier https://github.com/dgiese/dustcloud

Stand Mai 2021 - gibt es einige weitere Entwicklungen und als aktuelle Quelle kann dieser Link dienen:
Das mirobo Tool gibt es auch für Windows, ich habe ursprünglich die Linux Version verwendet.
Ich habe auch Valetudo getestet und in diesem Artikel beschrieben.

Mittwoch, 8. Mai 2019

Worx Landroid m500

Ich habe mir nach kurzer Überlegung und Klärung der Unterstützung in FHEM einen Mähroboter zugelegt. Nachdem ich die Installation und Einbindung in FHEM geschafft habe, will ich die mal hier festhalten. Als Grundlage gibt es einen kurzen Wikibeitrag. Dort sind zumindest mal die wichtigsten Links abgelegt. Ich habe die Installation etwas nach meinem Geschmack modifiziert, die Grundlage bildet aber diese Anleitung https://github.com/axelmohnen/fhem-landroid-s
Man sollte sich immer erst auf dieser Seite davon überzeugen, ob es Änderungen gibt!

Anmerkung: Im Herbst 2019 gab es mehrere Änderungen, sowohl im iobroker Modul als auch Modul LandroidSrv.js. Stand April 2020 habe ich die Beschreibung der Neuinstallation angepasst.

Die Voraussetzungen:
  • Cloudzugang eingerichtet und Mäher registriert. App funktioniert!
  • Installation Node.js, dieses Setup habe ich schon in einem extra Artikel beschrieben.

Setup Schritt für Schritt

Man sollte die Zeilen Schritt für Schritt ausführen und nicht einfach den Block als Script übernehmen.
sudo apt-get install libjson-pp-perl
sudo mkdir -p /opt/landroid/fhem-landroid-s
sudo chown -R pi /opt/landroid
sudo chmod -R 777 /opt/landroid
cd /opt/landroid/fhem-landroid-s
# Ich verzichte auf git und lade die beiden Datei einfach herunter
wget -O LandroidConf.json https://raw.githubusercontent.com/axelmohnen/fhem-landroid-s/master/LandroidConf.json
wget -O LandroidSrv.js https://raw.githubusercontent.com/axelmohnen/fhem-landroid-s/master/LandroidSrv.js
# bei der folgenden Installation gibt es ein paar Warnmeldungen, offenbar ohne Auswirkung
sudo npm install iobroker.worx
sudo npm install pm2 -g
# Nur in der alten Version: Ich korrigiere einen aktuellen kleinen Schönheitsfehler fürs Logging
# sed -i -e 's/self.mqtt)/self.mqtt_endpoint)/' node_modules/iobroker.landroid-s/lib/mqttCloud.js
# Jetzt den Cloudzugang eintragen und mit ctrl+o speichern und mit ctrl+x schließen
nano LandroidConf.json
Wenn man alles richtig gemacht hat, kann man jetzt schon mal auf zwei Arten testen:
node /opt/landroid/fhem-landroid-s/LandroidSrv.js mower1
# im Browser kann man sich den aktuellen Datensatz anzeigen lassen
http://ip-adresse-landroidsrv:8001/getMessage
Die Logausgaben sind selbsterklärend. Mit ctrl+c kann man den Testlauf abbrechen.

Automatischer Start

Auch mit der Node Prozess Steuerung kann man die Funktion testen
pm2 start /opt/landroid/fhem-landroid-s/LandroidSrv.js -f -- mower1
pm2 monit                           # zeigt Status
pm2 list                            # zeigt Prozesse
pm2 logs LandroidSrv [--lines 1000] # zeigt das Log
Sieht alles gut aus, kann man jetzt den Autostart aktivieren.
Diese Einrichtung war mir zunächst etwas schleierhaft. Aber - pm2 redet mit einem, und man muss genau lesen was er sagt.
Durch Eingabe von pm2 startup erhält man nach einer Analyse des Systems eine Zeile die man selbst noch einmal kopieren und in der Eingabezeile ausführen muss.
pm2 startup
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u pi --hp /home/pi
Hat man die Zeile ausgeführt muss man die pm2 Konfiguration noch speichern.
pm2 save
Nach einem Neustart des Servers kann man sich davon überzeugen ob alles geklappt hat.
Achtung!
Ich habe mich mehrfach aus der AWS Cloud ausgesperrt! Offenbar ist es mir unbewusst gelungen, den LandroidSrv mehrfach zu starten und dies auch in pm2 zu verankern. Hast Du nicht gesehen, lief er 1x unter User pi und 2x unter User root. Wie ich das geschafft habe und was da genau abgeht weiß ich noch nicht. Mit ps
ps -aux|grep pm2
kann man schauen wie oft der Prozess läuft. Weiter unten habe ich geschrieben wie man alles wieder löschen kann.
pm2 legt für den angemeldeten User jeweils Prozesse an! Also nicht einmal als sudo und einmal als pi einrichten (hatte ich offenbar gemacht).

Setup FHEM

Zunächst brauchen wir das (derzeit nicht offizielle) Modul. Das laden wir direkt von GitHub an Ort und Stelle. Das notwendige Perl Modul haben wir oben als ersten Schritt schon installiert. Der Code ist entweder Zeilenweise für die FHEM Kommandozeile oder komplett für die Raw Definition.
"wget -nv -O FHEM/74_LANDROID.pm https://raw.githubusercontent.com/axelmohnen/fhem-landroid-s/master/74_LANDROID.pm"
reload 74_LANDROID.pm
define robbi LANDROID localhost
attr robbi event-on-change-reading .*
define FileLog_robbi FileLog ./log/robbi-%Y.log robbi
attr FileLog_robbi logtype text
Die ersten Reading sollten nach 180 sec erscheinen.

Noch Dies und Das

Zunächst die Installation aus der Readme vom GitHub in kurzer Zusammenfassung
sudo apt-get install git
cd /opt
sudo mkdir landroid
sudo chown pi landroid
sudo chmod 777 landroid
cd landroid
git clone https://github.com/axelmohnen/fhem-landroid-s.git
cd fhem-landroid-s
sudo npm install iobroker.worx
# bei der Installation gibt es ein paar Warnungen und eine
# npm WARN saveError ENOENT: no such file or directory, open '/opt/landroid/fhem-landroid-s/package.json'
# npm notice created a lockfile as package-lock.json. You should commit this file.
# Jetzt account eintragen
nano LandroidConf.json
#Teststart
node LandroidSrv.js mower1

Fehler beim Start

Wenn man sich im Argument vertippt hat (mover statt mower) kommt dieser Fehler
/opt/landroid/fhem-landroid-s/node_modules/iobroker.landroid-s/lib/mqttCloud.js:24
    this.email = adapter.config.email;
                                ^

TypeError: Cannot read property 'email' of undefined
    at new mqttCloud (/opt/landroid/fhem-landroid-s/node_modules/iobroker.landroid-s/lib/mqttCloud.js:24:33)
    at main (/opt/landroid/fhem-landroid-s/LandroidSrv.js:420:14)
    at Object. (/opt/landroid/fhem-landroid-s/LandroidSrv.js:458:3)
    at Module._compile (internal/modules/cjs/loader.js:701:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:754:12)
    at startup (internal/bootstrap/node.js:283:19)
Ich hatte einen hartnäckigen Fehler, weil mich die Amazon Cloud offenbar gesperrt hatte (ich hatte durch einen Fehler eine Art Attacke gefahren). Abhilfe war: DSL neu verbinden.
undefined:1
<html>
^

SyntaxError: Unexpected token < in JSON at position 0
    at JSON.parse (<anonymous>)
    at IncomingMessage.<anonymous> (/opt/landroid/fhem-landroid-s/node_modules/iobroker.landroid-s/lib/mqttCloud.js:275:21)
    at IncomingMessage.emit (events.js:194:15)
    at endReadableNT (_stream_readable.js:1125:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)

Alles löschen

Der Abschnitt ist ein bisschen unsortiert. So einfach war ein Update bzw. Entfernen der Software nicht. Der iobroker.landroid-s / iobroker.worx wird unterhalb des aktuellen Pfades bei der Installation unter node_modules abgelegt.
Will man nur den Versuch mit pm2 beenden.
pm2 stop /opt/landroid/fhem-landroid-s/LandroidSrv.js
pm2 delete /opt/landroid/fhem-landroid-s/LandroidSrv.js
Wenn man alles wieder loswerden will:
pm2 unstartup
#alte Version
sudo npm uninstall iobroker.landroid-s
#neue Version
sudo npm uninstall iobroker.worx
#Nur Alte Version löschen
sudo rm -R node_modules/
sudo rm package-lock.json
#Alles löschen
cd /opt
sudo rm -R landroid/
#Auch nodejs löschen
sudo pm2 kill
sudo npm remove pm2 -g
sudo apt-get remove nodejs

Dienstag, 7. Mai 2019

Windows von FHEM über ssh steuern

Windows OpenSSH Server installieren

Als ersten Schritt sollte man zunächst unabhängig von der Windows Version prüfen ob ein entsprechender Dienst schon läuft oder existiert:
Get-Service -Name *ssh*

Windows 10 + Server 2019

In den aktuellen Windows Versionen (ab dem Jahr 2019) ist der OpenSSH Server enthalten und kann einfach installiert werden.
Get-WindowsCapability -Online -Name OpenSSH.Server* | Add-WindowsCapability -Online
Get-Service -Name sshd | Set-Service -StartupType Automatic
Get-Service -Name sshd | Start-Service

Windows Server 2016, 2012 R2 und 2012

In früheren Windows Versionen kann der OpenSSH Server nur über ein extra download/Setup installiert werden.
Als Voraussetzung für die Installation muss die Ausführung von Powershellscripten erlaubt sein.
Set-Executionpolicy Remotesigned
Ich habe für die Installation ein komplettes Script auf GitHub  zur Verfügung gestellt.
Man kann es einfach herunterladen und starten.
Ergänzung 2021:  TLS1 kann nicht mehr verwendet werden, es kommt unter Umständen zur Fehlermeldung. Hier habe ich Abhilfe gefunden und eine zusätzliche Zeile eingebaut. Eventuell schafft ein .net Update Abhilfe.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]'Tls11,Tls12'
invoke-webrequest https://raw.githubusercontent.com/heinz-otto/Powershell/master/InstallLatestSshd64.ps1 -OutFile InstallLatestSshd64.ps1
.\InstallLatestSshd64.ps1
Das Script überprüft die Windows Server Version und installiert ein fehlendes
Windows Management Framework 5.1 (dann ist zwischendurch ein Reboot erforderlich). Es wird die aktuellste sshd Version heruntergeladen und die Anmeldung für die Administratoren Gruppe freigeschaltet.
Um bei dieser Installationsart anschließend die Programme wie ssh oder ssh-keygen einfach verwenden zu können, kann der Pfad erweitert werden. Hier die Powershell und die cmd Variante für die aktuelle Sitzung:
$Env:Path += "C:\Program Files\OpenSSH-Win64\;"
set PATH=C:\Program Files\OpenSSH-Win64\;%PATH%
Die Verwendung von Parametern in der Powershell kann fehlerhaft sein. Im Zweifel die CMD verwenden!
Das CMD Fenster unter Windows Server 2012 liefert für ssh nicht das richtige Terminal (conpty), eine interaktive Arbeit ist damit nicht wirklich möglich. Scripte können aber abgearbeitet werden.

Anmeldung am Windows OpenSSH Server

Man kann sich wie gewohnt von jedem System (Windows 10 Version 1803 hat den ssh Client aktiv) mit ssh user@host am SSH Server anmelden!
Der ssh Server akzeptiert nur den "Kurznamen". Windows kürzt den Anmeldenamen ein (z.B. bei einem Onlinekonto).
Man kann den Namen leicht in der Powershell Konsole ermitteln:
whoami

SSH Zugang mit Public Key einrichten

Achtung: Auch in Windows darf die Datei %USERPROFILE%\.ssh\authorized_keys nicht für jedermann zugreifbar sein. Nur DER Benutzer (Eigentümer), Administratoren und System darf Zugriff haben, sonst wird der Public Key abgelehnt!
Bitte den Hinweis am Ende des Artikels für die Gruppe der Administratoren beachten!
Bitte alle Schritte (Befehlszeilen) einzeln kopieren und ausführen!

Problem: Bei Windows als SSH Server versagt leider das Tool ssh-copy-id. Hier drei Szenarien für die Einrichtung des ssh Zuganges mit public key zu einem Windows Server.

Voraussetzung: Benutzer hat schon einen RSA Key. Wenn nicht muss er vorher erzeugt werden!
(ssh-keygen -f ~/.ssh/id_rsa -P "" -t rsa oder ssh-keygen -f %USERPROFILE%\.ssh/id_rsa -P "" -t rsa)

Linux Shell
ziel=user@host
pkey=$(cat ~/.ssh/id_rsa.pub)
ssh $ziel "findstr /c:\"$pkey\" .ssh\authorized_keys ||mkdir .ssh ||echo $pkey >>.ssh\authorized_keys"
Windows CMD
set ziel=user@host
set /p pkey=< %userprofile%\.ssh\id_rsa.pub
ssh %ziel% "findstr /c:""%pkey%"" .ssh\authorized_keys ||mkdir .ssh ||echo %pkey% >>.ssh\authorized_keys"
Windows Powershell
$ziel='user@host'
$pkey = $(type $env:userprofile\.ssh\id_rsa.pub)
ssh $ziel "findstr /c:`"`"$pkey`"`" .ssh\authorized_keys ||mkdir .ssh ||echo $pkey >>.ssh\authorized_keys"

Hinweis: Die Befehle nach ssh werden in der CMD von Windows abgearbeitet! Der Public Key wird direkt durch Variablen "Auflösung" mitgegeben. Der Trenner || ist eine bedingte Abarbeitung der folgenden Befehle. 

Ersteinrichtung
Alle Schritte in Kurzform für den Public Key Zugang für den User fhem zu einem Windows SSH-Remotesystem habe ich hier ausführlich gezeigt.

Nur mit Public Key
Man kann den Zugang sehr sicher machen und nur public key zulassen. Dazu muss man die Passwort Authentisierung abschalten indem in der Datei C:\ProgramData\ssh\sshd_config diese Zeile eingefügt wird:
PasswordAuthentication no
Hinweis: Die Config Datei enthält alle Optionen mit ihren Standardwerten als Kommentarzeilen. Jede Änderung in der Datei muss durch einen restart des sshd Services aktiviert werden.

Powershellscript von FHEM aus starten

Bei der Ausführung von Befehlen vom Linux System aus muss man beachten, dass der Backslash verdoppelt (geschützt) werden muss. Ansonsten kann man wie gewohnt Befehle absetzen und sogar Powershell Scripts mit Argumenten ausführen.
"ssh user\@host 'powershell C:\\Tools\\Scripts\\TestPsFhem.ps1' 'http://host:8083' 'DummyName'"
Oder auch mittels powershell cmdlets direkt starten
"ssh user\@host powershell -command stop-vm vmName"
{qx 'ssh user@host powershell -command get-service' }

Innerhalb von Powershell kann man auch mit einem Powershellscript über HTTP auf FHEM zugreifen.
Powershellbeispiel:
Fragt einen Dienst ab und schreibt Status und Displayname nach FHEM zurück in einen Dummy
<#
.SYNOPSIS
    This Script is for testing the FHEM Client for HTTP
.DESCRIPTION
    Give FHEM Server url and Dummy 
.NOTES
    Will give a State and Displayname of Service back
#>
#region Params
param(
    [Parameter(Mandatory=$true,Position=0,HelpMessage="-first 'Portnumber or URL'")]
    [String]$first,
    [Parameter(ValueFromPipeline=$true,ValueFromRemainingArguments=$true)]
    [String[]]$sec
)
#endregion 
$serv="wuauserv"

if (!(Test-Path .\fhemcl.ps1)) {
    Write-Output "fhemcl.ps1 fehlt"
    invoke-webrequest "https://raw.githubusercontent.com/heinz-otto/fhemcl/master/fhemcl.ps1" -OutFile "fhemcl.ps1"
}

$state=get-service $serv
.\fhemcl.ps1 $first "set $sec $($state.status)"
.\fhemcl.ps1 $first "setreading $sec Displayname $($state.DisplayName)"


Eigenheiten für Administratoren in einigen Windows Versionen?

Symptom:
Ein Mitglied der Gruppe administrators kann offenbar keine Anmeldung mit Public Key machen. Obwohl alles eingerichtet ist, wird das Passwort abgefragt. 
Das trifft zu für Windows Server Version 2012 und Windows 10 ab Version 2004.
Bei Windows Server 2012 R2, 2016 und 2019 war es bisher nicht so.

Neue Erkenntnis: Ist eventuell so gewollt und teilweise fehlerhaft implementiert: Link.

Eine Lösung:
Ich habe mir die Sache näher angeschaut und ich habe bei einigen Installationen Unterschiede in der aktiven sshd Konfigurationsdatei C:\ProgramData\ssh\sshd_config gefunden.
Kommentiert man dort am Ende der Datei den folgenden Eintrag mit einem # aus,
Match Group administrators
       AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
funktioniert auch die Anmeldung für Mitglieder der Administratoren. Der Dienst ssd (OpenSSH SSH Server) muss dafür neu gestartet werden!
Hintergrund:
Es gibt zwei Erklärungen für diese Verhalten
  1. Es werden unterschiedliche Inhalte der Datei ausgeliefert - mit und ohne den beiden oben gezeigten Zeilen am Dateiende. 
  2. Der sshd vergleicht dabei die Klarnamen der Gruppenbezeichnung. Ich installiere gern für den Server EN Systeme und gebe Ihnen eine DE mui.
    • Ein 2012 EN System mit DE mui hat administrators - > match!
    • Alle späteren EN Systeme (2012 R2, 2016) mit DE mui haben Administratoren -> kein match!
Der folgende Code kommentiert die verantwortlichen Zeilen in der config aus. In meinem Setupscript für die älteren Serversysteme ist dieser Code bereits enthalten.
# Patch sshd config to allow administrators Group public Key logon
$Quelle="${Env:ProgramData}\ssh\sshd_config"
write-output "patch the sshd config on $Quelle"
Stop-Service -Name sshd
$Inhalt = Get-Content $Quelle
#search 2 lines contains administrators and insert commment sign
$Inhalt|foreach {if ($_ -match "administrators") {$Inhalt[$_.readcount-1]=$_.Insert(0,"#")}}
set-Content $Quelle $Inhalt
Start-Service -Name sshd
Alternative Lösung:
Die Schlüssel der entsprechenden User wirklich in die Datei c:\ProgramData\ssh\administrators_authorized_keys eintragen. Wie hier beschrieben, müssen dazu noch weitere Änderungen (Rechte an Datei und Inhalt C:\ProgramData\ssh\sshd_config) durchgeführt werden.

Den OpenSSH Server wieder loswerden

Stop-Service -Name sshd 
pushd
cd "${Env:ProgramFiles}\OpenSSH-Win64\" 
.\uninstall-sshd.ps1
popd
rm -recurse "${Env:ProgramFiles}\OpenSSH-Win64"
rm -recurse "${Env:ProgramData}\ssh"
Remove-NetFirewallRule -Name sshd

Zum Nachlesen

Ziel des Artikels war es, kurz und knapp alle Schritte zusammenzufassen.
Ausführlicher kann man in folgenden Artikeln nachlesen:
https://heinz-otto.blogspot.com/2018/06/windows-hat-jetzt-ssh.html
https://heinz-otto.blogspot.com/2017/01/per-ssh-remote-befehle-direkt-ausfuhren.html
https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration