Archiv der Kategorie: IT-Kram

Pypicloud als Azure App Service

Pypicloud ist für kleine Entwicklerteams oder auch als privater Python-Pakethoster eine interessante alternative, wenn da nur der Overhead nicht wäre. Er kann jedoch leicht in Azure in der PaaS-Umgebung App Service aufgesetzt werden.

Azure App Service

Azure App Service ist eine verwaltete Umgebung zum Aufsetzen von Webanwendungen. Es gibt ihn in verschiedenen Varianten, und Pypicloud benötigt eine Python-Umgebung. Diese wird in Azure als Linux-Container unterstützt.

In diesem Artikel wird mit einfachen Mitteln Pypicloud als App Service eingerichtet. Über Grundlagen wissen von Azure und Azure CLI hinaus wird keine weitere Erfahrung benötigt, insbesondere wird kein Docker-Container konfiguriert.

Voraussetzungen

Alle Befehle müssen von der Kommandozeile ausgeführt werden und können der Reihe nach wie im Artikel eingetippt werden. Alle einzutippenden Befehle beginnen mit $.

Sämtliche Befehle werden aus einem Arbeitsverzeichnis ausgeführt. Der komplette Inhalt dieses Verzeichnisses wird nach Azure hochgeladen.

Als Arbeitsverzeichnis wird ~/pypicloud-test genutzt.

$ mkdir ~/pypicloud-test
$ cd ~/pypicloud-test

Azure

Azure CLI muss installiert werden.

Das gezeigte Beispiel verwendet den kostenlosen Tarif für Webanwendungen (F1). Es ist zumindestens ein Probeabbonement für Azure notwendig.

Python

Im folgenden wird davon ausgegangen, dass entweder das System-Python oder ein pyenv genutzt wird. Pthon 3.7.x muss installiert sein. Alternativ kann die Konfigurationsdatei natürlich auch in einem venv erstellt werden und in das Arbeitsverzeichnis kopiert werden.

Pypicloud

Die Pypicloud-Dokumentation beschreibt als Einführung wie Pypicloud sehr schnell lokal gestartet werden kann. Es wird dafür eine virtuelle Python-Umgebung genutzt. Für das Hosting in Azure wird das nicht benötigt, sondern nur die erstellte Konfigurationsdatei.

Das folgende Beispiel zeigt, wie eine Konfiguration für Produktions-Einsatz erstellt werden kann:

$ pyenv shell 3.7.3
$ pip install pypicloud==1.0.15
$ ppc-make-config -p pypicloud-prod.ini
[1] s3
[2] gcs
[3] filesystem
Where do you want to store your packages? 3
Admin username? pypicloud-admin
Password:
Password:
Config file written to 'pypicloud-prod.ini'

Azure App Service

Um eine Python-Webanwendung in App Service laufen zu lassen, müssen sämtliche notwendigen Python-Pakete als requirements.txt gespeichert werden. Die Pakete werden dann im App Service mit pip installiert.

Für Pypicloud wird nur ein Paket benötigt, alle Abghängigkeiten werden von pip automatisch aufgelöst. Die requirements.txt, muss nur eine Zeile enthalten und Pypicloud und die Versionsnummer spezifizieren:

pypicloud==1.0.15

Starten der Webanwendung mit Azure

Die Webanwendung kann so bereits gestartet werden. Im Folgenden wird keine Resourcen-Gruppe eingestellt, weitere Beispiele und Konfigurationsmöglichkeiten stehen zum Beispiel in der Azure-Dokumentation.

$ az webapp up --sku F1 -n pypicloud-nerdpause -l westeurope
webapp pypicloud-nerdpause doesn't exist
Creating Resource group 'Nerdpause_rg_Linux_westeurope' ...
Resource group creation complete
Creating AppServicePlan 'Nerdpause_asp_Linux_westeurope_0' ...
Creating webapp 'pypicloud-nerdpause' ...
Configuring default logging for the app, if not already enabled
Creating zip with contents of dir /home/nerdpause/pypicloud ...
Getting scm site credentials for zip deployment
Starting zip deployment. This operation can take a while to complete ...
Deployment endpoint responded with status code 202
You can launch the app at http://pypicloud-nerdpause.azurewebsites.net
{
  "URL": "http://pypicloud-nerdpause.azurewebsites.net",
  "appserviceplan": "Nerdpause_asp_Linux_westeurope_0",
  "location": "westeurope",
  "name": "pypicloud-nerdpause",
  "os": "Linux",
  "resourcegroup": "Nerdpause_rg_Linux_westeurope",
  "runtime_version": "python|3.7",
  "runtime_version_detected": "-",
  "sku": "FREE",
  "src_path": "//home//nerdpause//pypicloud"
}

Die Ausgabe bestätigt, dass der Server unter http://pypicloud-nerdpause.azurewebsites.net erreichbar ist. Ein kurzer Test zeigt jedoch, dass zwar eine Webanwendung existiert, aber nur die von Azure bereitgestellte Standardseite. Pypicloud ist jedoch nicht gestartet worden.

Default App Service landing page
Eine von Azure bereitgestellte Startseite wird angezeigt

Aktivieren von Pypicloud

Dies liegt daran, dass Azure beim Start der Webanwendung standardmäßig versucht, bestimmte Webfrontends (zur Zeit Flask und Django) zu erkennen und zu starten, Pypicloud basiert jedoch auf pyramid. In einem Blog-Post wird zwar bestätigt, dass auf pyramid basierende Webapps unterstützt werden, die angegebene Quelle zeigt jedoch nur, dass Gunicorn verwendet wird um Webanwendungen zu starten.

Zum Glück lassen sich pyramid-Anwendungen leicht mit Gunicorn starten. Für die oben angegebene Pypicloud-Konfiguration zum Beispiel so:

$ gunicorn --paste pypicloud-prod.ini

Nun muss nur noch den Azure App Service konfiguriert werden um einen benutzerdefinierten Startbefehl zu verwenden. Dazu muss die der Azure Web Service entsprechend konfiguriert werden:

$ az webapp config set --name pypicloud-nerdpause --resource-group Nerdpause_rg_Linux_westeurope  --startup-file 'gunicorn --bind=0.0.0.0 --timeout 600  --paste pypicloud-prod.ini'
{
  ...
  "appCommandLine": "gunicorn --bind=0.0.0.0 --timeout 600  --paste pypicloud-prod.ini",
  ...
}

Nach kurzer Zeit kann die Webseite neu geladen werden. Es dauert ein bisschen, bis die aktualisierte Konfiguration deployt ist. Leider ist das Ergebnis nicht wie gewünscht, sondern eine Fehlermeldung wird angezeigt.

Fehlerausgabe des gestarteten Services
Es liegt ein Fehler beim Start des Webservices vor

Cache-Datenbank

Die Ursache des Problems ist der Speicher: Das Rootverzeichnis der Webanwendung ist ein gemountetes Azure Files-Verzeichnis. SQLite kann keine Locks erstellen, so dass der Starten des App Service fehlschlägt (sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked ). Glücklicherweise bestätigt die Pypicloud-Dokumentation, dass die Cache-Datenbank nicht persistiert werden muss:

The cache does not have to be backed up because it is only a local cache of data that is permanently stored in the storage backend

https://pypicloud.readthedocs.io/en/latest/topics/cache.html#cache

Es gibt nun zwei Lösungsmöglichkeiten: Die Datenbank kann einfach an einem anderen Ort gespeichert werden, zum Beispiel im Temp-Verzeichnis. Der Pfad wird als Parameer db.url im Format für SQLAlchemy festgeleg. Dazu muss pypicloud-prod.ini einfach wie folgt angepasst werden:

db.url = sqlite:////tmp/db.sqlite

Alternativ kann die Datenbank im Journal-Modus WAL erstellt werden. Diese kann lokal erstellt und dann zum Webserver geschickt werden:

$ apt install sqlite # optional
$ sqlite3 db.sqlite 'pragma journal__mode=wal;'
$ az webapp up

Das Ergebnis ist aber immer noch nicht wie erwartet. Die Fehlermeldung verschwindet zwar, es wird jedoch stattdessen eine leere Seite angezeigt. Dies kann mehrere Ursachen haben, z.B. falls Scripte verboten sind.

Leere Webseite
Azure Web Service hat keinen Fehler mehr, aber Pypicloud ist noch immer nicht verfügbar

Sichere Verbindungen

Eine genauere Betrachtung der ausgelieferten Seite zeigt jedoch, dass der Quellcode und alle Dateien verfügbar sind. Das Problem ist, das standardmäßig alle Links das http-Protokol nutzen, Azure App Service jedoch https nutzt. Um das zu korrigieren muss pypicloud-prod.ini angepasst werden:

[app:main]
filter-with = proxy-prefix
 
[filter:proxy-prefix]
use = egg:PasteDeploy#prefix
scheme = https

Nun ist der Server bereit :)

Startseite der Pypicloud Webanwendung
Pypicloud ist bereit

Aber Achtung: Der Service ist ohne weitere Konfiguration weltweit verfügbar und jeder kann darauf zugreifen

Weitere Schritte

Das gezeigte Beispiel ist natürlich erst der Anfang um einen (semi-)professionellen Pypicloud-Server aufzusetzen. Es bieten sich weitere Schritte an.

Daten Persistieren

Der freie Account hat nicht allzu viel Speicher, und die Persistenz ist nur gegeben, solange der Service nicht gestoppt wird (az webapp delete). In diesem Fall sind alle Daten gelöscht.

Die Lösung dazu wäre, die Daten auf einem dafür angelegten Storage-Account zu persistieren.

Automatische Deployments

Anstatt manuell mit Azure CLI kann der Service automatisch aufgesetzt werden z.B. mit Terraform.

Geschrieben von Kap. Zuletzt geändert am 17. Mai 2020.

Ein Switch für EntertainTV mit IGMPv3

Aktuelle Netzwerkkonfiguration mit altem Switch

Die bisherige Einrichtung der Medien-Zentrale im Wohnzimmer

Zum Anschluss zahlreicher internetfähiger Geräte steht schön im Schrank versteckt ein kleiner, alter Switch. Der Betreibt unter anderem magentanes IPTV der Marke EntertainTV. Das nutzt zum Verteilen der Daten Multicast und das Verfahren IGMPv3. Bei der Einrichtung können zahllose Probleme auftreten; das Internet ist voll davon. Um es überhaupt erstmal zum Laufen zu bekommen, kann es notwendig sein in die Tiefen der Routerkonfiguration abzusteigen und am VLAN-Tagging rumzuspielen. Wenn das Fernsehen dann läuft, wie es soll können weitere Probleme auftreten.

Multicast ist die richtige Technologie für IPTV. Wie der Name andeutet, verbreitet Multicast Datenpakete an viele viele Empfänger. Damit wird ermöglicht, dass viele Nutzer sich einen Datenstrom teilen und nicht das ganze Netz (in diesem Fall der Telekom) überlasten. Im Heimnetz kann das jedoch Probleme verursachen. Der Datenstrom soll hier ja nur an ein bestimmtes Gerät gehen (den IPTV-Receiver). Alte Hardware, die das moderne Protokoll nicht kennt, kann keine Zuordnung an ein spezielles Gerät vornehmen. Daher wird der Datenstrom einfach überall hin gesendet.

Das Verfahren funktioniert in dem Sinne, dass der Receiver das TV-Signal empfängt und man damit Fernsehen kann. Als unschöner Nebeneffekt empfangen jedoch alle anderen Geräte im Netz ebenfalls die Daten. Was einige über Kabel angeschlossene Geräte, wie ein PC, das gut abkönnen, ist die Lage bei WLAN leider anders. Die Vielen überflüssigen Datenpakete verstopfen das Funktnetz und sorgen dafür, dass WLAN nicht funktioniert. Zur Lösung gibt es zahllose Beiträge in diversen Blogs und Foren, zum Beispiel bei Dennis Hobmaier für einen Netgear GS108Ev2 beschreibt oder bei Stübi, der ebenfalls die Netgear-Konfiguration beschreibt.

Antiker Switch Frontansicht

Leicht vergilbt und angestaubt: Der antike Noname-Switch. Bisher hat er immer gute Dienste geleistet.

Antiker Switch Hinten

Von hinten ein normaler 8-Port-Switch, nicht viel anders als aktuelle Geräte.

Genau das Problem habe ich gehabt. Die Geräte im Wohnzimmer hängen alle an einem Switch, dessen Alter ich nicht mehr genau bestimmen kann. Ich schätze mal, dass er so 20 Jahre auf dem Buckel hat. Und damit nichts mit IGMPv3 anfangen kann. An dieser Stelle eine Warnung: auch moderne Switches müssen das nicht unterstützen! Hinter dem Switch ist ein WLAN-Access-Point. Und in der Konsequenz, kann man nicht gleichzeitig Fernsehen und das WLAN nutzen.

Konfiguration

Ich habe mich für den TP-Link TL-SG2008 entschieden. Mit TP-Link-Hardware habe ich bisher immer gute Erfahrungen gemacht, laut Einträgen im Telekomhilft-Forum funktioniert er zusammen mit EntertainTV und ansonsten scheint er von der Technik ganz gut zu sein, und das zu einem vernünftigen Preis. Auch wenn er nicht mehr so günstig ist, wie der Noname-Switch, den ich damals für 30 DM kaufen konnte. (DM, das war das Ding vor dem Euro…)

Man kann direkt loslegen mit dem TV-Konsum, aber um das Problem mit dem überlasteten WLAN zu beheben ist etwas Konfiguration notwendig. Im wesentlichen muss nur IGMP-Snooping aktiviert werden, das sich im Menü hinter dem Punkt Multicast verbirgt. Es genügt leider nicht, einfach einen Haken zu setzen. Glücklicherweise informiert die erste Seite direkt, dass drei Konfigurationen durchgeführt werden müssen: Global Config, was die erste Snooping Config-Seite ist, sowie Port Config und VLAN Config, die folgenden Konfigurationsseiten.

Standardmäßig ist IGMP Snooping deaktiviert. Es muss zunächst auf Enable gesetzt werden.
TP-Link TL-SG2008 Konfiguration IGMP Snooping

Globale IGMP-Snooping-Konfiguration

Weiter geht es mit der Portkonfiguration. Hier muss das Snooping für die einzelnen Ports aktiviert werden. Ich habe es für alle aktiviert, ob es Nachteile gibt, wenn man zu viele auswählt, weiß ich nicht. Es könnte aber einen Einfluss auf die Leistungsfähigkeit haben.
TP-Link TL-SG2008 Konfiguration IGMPv3 Ports

Portkonfiguration

Zuletzt muss das VLAN konfiguriert werden. Das hier ist nicht zu verwechseln mit der generellen VLAN-Konfiguration. Es muss vermittels Create ein neues VLAN erstellt werden. Dieses VLAN wird die Multicast-Daten empfangen. Das heißt, hier dürfen nur die Ports aktiviert werden, an denen der eingehende Datenstrom vom Router anliegt und der Mediareceiver angeschlossen ist. Bei mir sind das die Ports 1 und 2. Die abschließende Konfiguration sieht wie im Bild aus, die aktiven Router Ports für das VLAN sind 1-2.
TP-Link TL-SG2008 Konfiguration IGMP VLAN

VLAN-Konfiguration

Die Konfiguration ist nun fertig, Auf den Ports 1 und 2 werden die Multicast-Daten verteilt, und Geräte ohne IGMPv3-Support, wie der WLAN-Router können an den Ports 3-6 angeschlossen werden.

Weite Konfigurationen können nun natürlich im Anschluss vorgenommen werden. Zum Beispiel der Datenvorrang. Man kann da unterschiedlich argumentieren. Ich denke ja, das Fernsehen sollte gegenüber anderen Daten nachrangig behandelt werden, aber Dennis Hobmaier kommt zu einem anderen Fazit, „Schließlich sollte TV Vorrang gegenüber Daten-Downloads haben“.

Zuletzt sieht die Medienzentrale nun auch schöner und weniger vergilbt aus. Aber das sieht man im verschlossenen Schrank ja eher nicht.

Die neue Media-Zentrale

Die aktualisierte Media-Zentrale mit dem neuen IGMPv3-Fähigen Switch und dem alten (nicht fähigen) Access-Point. Je ein HDMI- und Ethernet-Kabel erlauben, weitere Geräte schnell anzuschließen.

Geschrieben von Kap. Zuletzt geändert am 18. Dezember 2018.

Ubuntu Upgrade von Yakkety Yak zu Artful nach Zesty end of life

Zum aktuellen Zeitpunkt kommt es bim Upgrade von Ubuntu 16.10 Yakkety Yak zu Problemen, da die nächste Version 17.05 Zesty bereits nicht mehr unterstützt wird. Ein kleines Bisschen Nachhilfe verhilft den Installationsscripten zur Fähigkeit, trotzdem upzudaten.

Ursprünglich habe ich das als Frage bei Askubuntu gestellt. Wurde aber als angebliches duplicate geschlossen. Naja, als ob mich wirklich interessieren würde, wie und wann ein Release auf den Servern verschoben wird… :O Auf jeden Fall habe ich das hier mal aufgeschrieben.

Vorbemerkungen

Die Installationsdateien für unterstützte Versionen von Ubuntu liegen auf http://archive.ubuntu.com/ubuntu, sobald die Versionen nicht mehr unterstützt werden, sind sie unter http://old-releases.ubuntu.com/ubuntu zu finden.

Um den Ubuntus Updatemanager zum Aktualisieren zu bewegen, müssen die Pfade „nur“ manuell abgeglichen werden.

Download der Installationsskripte

Erster Schritt der Installation ist der Download des Updaters für das nächste Release. Das Skript unter /usr/lib/python3/dist-packages/DistUpgrade/DistUpgradeFetcherCore.py lädt (versucht es zumindest) eine gepackte Datei mit der Software für das Upgrade für die entsprechende Version und die Zugehörige Signatur herunter. Sobald das erfolgt ist, wird die Korrektheit überprüft, die Datei entpackt und das Setup-Programm gestartet.

Man kann einerseits den DEFAULT_MIRROR auf http://old-releases.ubuntu.com/ubuntu ändern und das Skript findet nun die neue Datei, verifiziert sie und entpackt in ein temporäres Verzeichnis. Falls man manuell verifizieren mag oder auf die Verifikation verzichtet, kann man die Datei auch direkt hier herunter laden:

http://old-releases.ubuntu.com/ubuntu/dists/zesty/main/dist-upgrader-all/current/zesty.tar.gz

Ausführen des Updates

Ob man nun das Setup vom Startskript mit angepassten Pfaden starten lässt, oder manuell herunterlädt und startet, die Pfade zu den Zesty-Paketquellen sind falsch. Das Setup modifiziert die Quellpfade in /etc/apt/sources.lst so dass sie auf die nächste Version zeigen, das heißt in unserem Fall von yakkety zu zesty. Da Zesty nun jedoch schon end of life ist, müssen die Paketquellen über den angepassten Releasenamen hinaus auch noch auf die alten Pfade zeigen.

Die Aktualisierung der Quellen wird in DistUpdateController.py durchgeführt. Die Methode rewriteSourcesList (beginnend etwa in Zeile 520) ändert diese Pfade anhand einer langen Fallunterscheidung. Die Standardpaketquellen werden in der Bedingung validMirror or thirdPartyMirror (etwa in zeile 690) überprüft und angepasst. Ein Eintrag aus der Datei sources.lst ist in der Variable entry gespeichert, im Feld entry.uri wird der Pfad zu den Paketquellen angegeben und in entry.dist die Aktuelle Ubuntu Releaseversion.

Um die Aktualisierung durchführen zu können, müssen die Pfade auf old-releases gesetzt werden. Dabei muss sowohl der normale Archivpfad, aber auch der Pfad zu Security-Paketen angepasst werden. Um sicher zu gehen habe ich auch den DE mirror angepasst:

                if entry.uri == "http://archive.ubuntu.com/ubuntu" and entry.dist.startswith("yakkety"):
                    entry.uri = "http://old-releases.ubuntu.com/ubuntu"
                if entry.uri == "http://de.archive.ubuntu.com/ubuntu/" and entry.dist.startswith("yakkety"):
                    entry.uri = "http://old-releases.ubuntu.com/ubuntu/"
                if entry.uri == "http://security.ubuntu.com/ubuntu" and entry.dist.startswith("yakkety"):
                    entry.uri = "http://old-releases.ubuntu.com/ubuntu"

Das Update starten

Endlich kann nun das Update mit

sudo ./zesty --mode=server --frontend=DistUpgradeViewText

gestartet werden.

Geschrieben von Kap. Zuletzt geändert am 30. Januar 2018.

UMS als systemd Service

Der Universal Media Server ist praktisch um Musik oder Filme und Serien zum Beispiel über eine Playstation abspielen zu können. Besonders wenn man einen kleinen Server laufen hat. In dem Fall soll der UMS natürlich beim Systemstart ebenfalls starten. Leider hat der UMS manchmal aber auch seine macken, insbesondere leidet er an gelegentlichen Exceptions und lässt den Speicher voll laufen.

Installation als Service

Neuere Systeme, z. B. CentOS 7 oder die aktuelle LTS Ubuntu-Variante Yakkety Yak nutzen systemd anstelle von init.d zum Starten von Systemdiensten. Es gibt einige Ressentiments dagegen und viele Diskussionen. Es spielen sogar irgendwie Nazis rein :D

[…] he’s your typical German that’s into efficiency and control, and that’s the philosophy the software is built around. Doing everything as efficiently as possible with whatever is available. This doesn’t mesh too well with the (arguably very American) Linux philosophy of modularity and freedom of choice. [1]

Gemeint ist hier wohl Entwickler Lennart Poettering und sein Beitrag zur Klärung. Ein weiter deutscher Entwickler ist Kay Sievers, der durch seine Art scheinbar Stress mit Linus Torvalds hat, der mal wieder einen seiner epischen Rants abgelassen hat

Wie dem auch sei, man kann den UMS auch mit systemd starten.

Zur Konfiguration des Media Servers als Service wird in der Datei

/etc/systemd/system/ums.service

die Konfiguration hinterlegt. UMS kommt bereits mit einem Startscript namens UMS.sh, was als Startkommando ExecStart hinterlegt wird. Wie es sich gehört, sollte der Media Server auch mit seinen eigenen Benutzerrechten laufen, in diesem Fall als Nutzer ums.

Im bereich [Install] wird angegeben, wann der Service gestartet werden soll, in diesem Fall also beim Systemstart vom Ziel multi-user.

[Unit]
Description=Universal Media Server
 
[Service]
Type=simple
User=ums
Group=ums
ExecStart=/opt/UMS/UMS.sh
 
[Install]
WantedBy=multi-user.target

Nun muss der Service noch aktiviert werden.

systemctl enable ums.service
systemctl start ums.service

Neustart des Service

Eigentlich könnte es das schon gewesen sein. Leider stürzt der Server manchmal ab oder belegt zu viele Resourcen. Eine Möglichkeit das zu umgehen ist ein regelmäßiger Neustart.

Das kann man mit systemd auch automatisieren, es ist ein cron ähnlicher Scheduler enthalten (siehe feature creep ;) ) Die Umsetzung ist aber etwas umständlich. Zunächst benötigt man einen eigenen Service

/etc/systemd/system/umsRestart.service

dessen Aufgabe darin besteht, den gerade definierten UMS Service neu zu starten. Er soll danach direkt beendet werden, und nicht wieterlaufen. Es ist daher eigentlich kein Service im eigentlichen Sinne. Dazu wird der Type als oneshot definiert. Um einen systemd-Service neu zu starten kann der Befehl try-restart genutzt werden.

[Unit]
Description=Universal Media Server Restart Service
 
[Service]
Type=oneshot
ExecStart=/bin/systemctl try-restart ums.service

Nun fehlt noch der Timer. Dazu wird ein dritter systemd-Service benötigt, der diesmal ein anderer Typ ist und daher in der Datei

/etc/systemd/system/umsRestart.timer

mit der Endung .timer spezifiziert wird. Die genaue Uhrzeit kann als Parameter OnCalendar im 24-Stunden-Format angegeben werden, zum Beispiel nachts wenn normalerweise nichts läuft. Die verschiedenen Timer werden vom Target timer verwaltet.

[Unit]
Description=Universal Media Server Restart Timer
 
[Timer]
OnCalendar=4:45
Persistent=true
 
[Install]
WantedBy=timer.target

Damit nun alles läuft, muss die neuen Services noch gestartet werden:

systemctl daemon-reload
systemctl start umsRestart.service

Testen

Eine Liste der aktuell laufenden Timer kann mit

systemctl list-timers

angezeigt werden. Wenn alles funktioniert hat ist der UMS Restart Service in der Liste enthalten:

NEXT                         LEFT          LAST                         PASSED               UNIT                
Di 2017-04-11 00:04:01 CEST  42min left    Mo 2017-04-10 13:07:02 CEST  10h ago              apt-daily.timer     
Di 2017-04-11 02:21:04 CEST  2h 59min left Mo 2017-04-10 19:32:02 CEST  3h 49min ago         snapd.refresh.timer 
Di 2017-04-11 04:45:00 CEST  5h 23min left Mo 2017-04-10 04:45:02 CEST  18h ago              umsRestart.timer    
Di 2017-04-11 22:33:35 CEST  23h left      Mo 2017-04-10 22:33:35 CEST  47min ago            systemd-tmpfiles-clean.timer

4 timers listed.

Wichtig ist, dass der Universal Media Service tatsächlich läuft. Ansonsten kann er nämlich nicht neu gestartet werden. Das kann man testen, indem der Restart-Service direkt ausgeführt wird. Es erscheint dann eine zugegebenermaßen nicht sehr verständliche Fehlermeldung:

$ systemctl status umsRestart.service
● umsRestart.service - Universal Media Server Restart Service
   Loaded: loaded (/etc/systemd/system/umsRestart.service; static; vendor preset: enabled)
   Active: failed (Result: exit-code) since Mo 2017-04-10 04:45:02 CEST; 18h ago
 Main PID: 28192 (code=exited, status=203/EXEC)
 
Apr 10 04:45:02 k systemd[1]: Starting Universal Media Server Restart Service...
Apr 10 04:45:02 k systemd[1]: umsRestart.service: Main process exited, code=exited, status=203/EXEC
Apr 10 04:45:02 k systemd[1]: Failed to start Universal Media Server Restart Service.
Apr 10 04:45:02 k systemd[1]: umsRestart.service: Unit entered failed state.
Apr 10 04:45:02 k systemd[1]: umsRestart.service: Failed with result 'exit-code'.
Geschrieben von Kap. Zuletzt geändert am 22. April 2017.

ProLiant MicroServer Gen 8 ruhig stellen

Mein kleiner Miniserver der Reihe ProLiant von HP tut bisher was er soll, hat mich jedoch in letzter Zeit durch schrille Töne etwas gestört. Er steht auch direkt auf dem Schreibtisch, da er ja auch sehr ansehnlich ist :)

Nachdem eine Reinigung der üblichen Verdächtigen (vulgo: des Prozessorlüfters) keine Verbesserung ergab, musste ich feststellen, dass das Netzteil in einer unangenehmen Tonlage Geräusche von sich gab. Zum Glück ist es möglich das Standardnetzteil durch ein lüfterloses Modell auszutauschen. Für Leute, die schonmal einen Rechner mit dem Schraubenzieher traktiert haben, ist es ganz einfach. Alle anderen gehen so vor, wie im Folgenden beschrieben.

Was braucht man?

Um das Netzteil im MicroServer Gen 8 auszutauschen braucht man nicht einfach ein „Silent Gen 8 Power Supply“ sondern mehrere einzelne Teile:

Lüfterloses Netzteil:

Die wichtigste Komponente ist selbstverständlich die leise Stromversorgung, zum Beispiel das PicoPSU-160-XT 160W ATX Netzteil. Das kleine Netzteil passt direkt auf den ATX-Stecker der Hauptplatine. Das Netzteil kann 160 Watt mit kurzzeitigen Spitzen bis zu 200 Watt liefern.
Das PicoPSU 160-XT Netzteil für den MicroServer Gen 8 passt in den ATX-Stecker und hat einige SATA- und Molex-Anschlüsse.

Das PicoPSU 160-XT Netzteil für den MicroServer Gen 8 passt in den ATX-Stecker und hat einige SATA- und Molex-Anschlüsse.

Adapter:

Wie jedes Netzteil braucht auch die kleine Variante Strom, im Unterschied zu handelsüblichen Steckern passt jedoch nicht direkt ein Schukostecker an das PicoPSU. Eine Möglichkeit für die Stromversorgung ist ein Standard-AC/DC Adapter. Der Adapter ist natürlich viel Größer als das alte Netzteil und wird wie ein Laptop-Netzadapter hinter dem Server auf dem Boden liegen (oder sonstwo versteckt.)
Adapter für den PicoPSU 160-XT

Adapter für den PicoPSU 160-XT

Anschlusskabel:

Das PicoPSU hat für den MicroServer Gen 8 einen Anschluss zu wenig. Einige Geräte sind über einen Berg-Stecker (ein herkömmlicher, eigentlich aus der Mode gekommener Disketten-Anschluss) verbunden. Um diesen Anschluss weiterhin zu ermöglichen. Das ist die Gelegenheit den lange vergessenen Molex-Y-Adapter (für alte Stromanschlüsse von Festplatten) aus der alten Kiste mit Kabeln zu suchen. Zumindestens, wenn man so eine Kiste im Keller stehen hat. Ansonsten gibts den auch noch zu kaufen.
Standard Molex-Y-Adapter mit Berg-Anschluss.

Standard Molex-Y-Adapter mit Berg-Anschluss.

Ausbau des alten Netzteils

Bevor die Arbeiten beginnen sollte natürlich das Stromkabel vom alten Netzteil abgezogen werden. Dann kann der ATX-Pfostenstecker von der Hauptplatine abgezogen werden. Das Netzteil des Gen 8 hat noch einige weitere Anschlüsse. Die Festplatten sind über einen Molex-Adapter angeschlossen, der sich unter dem optischen Laufwerk verbirgt. Nachdem das Laufwerk gelöst und rausgeschoben worden ist, wird der Anschluss unter dem Gehäuse sichtbar. Es ist nicht notwendig, das CD-Laufwerk komplett auszubauen. Man kommt nicht direkt an den Molex-Stecker heran, sondern muss durch zwei kleine Öffnungen hindurch. Ich habe einen Schraubenzieher und den Inbusschlüssel, der sich vorne hinter der Klappe befindet, genommen um den Stecker auseinander zu ziehen.

Der zum Netzteil gehörende Stecker muss durch einen Adapter ersetzt werden.

Der zum Netzteil gehörende Stecker muss durch einen Adapter ersetzt werden.

Unter dem optischen Laufwerk befindet sich eine Molex-Steckverbindung, die gelöst werden muss.

Unter dem optischen Laufwerk befindet sich eine Molex-Steckverbindung, die gelöst werden muss.

Ein weiteres Anschlusskabel mit Berg-Stecker verläuft direkt oben auf dem Gehäuse. Das Gegenstück zum Stecker ist ziemlich fest verbaut, es lässt sich aber gut lösen wenn ein spitzer Gegenstand zum Anheben der Klemme benutzt wird.

Der Berg-Stecker, der das optische Laufwerk mit Strom versorgt.

Der Berg-Stecker, der das optische Laufwerk mit Strom versorgt.

Zum Lösen des Steckers muss etwas spitzes unter die Klemme geschoben werden.

Zum Lösen des Steckers muss etwas spitzes unter die Klemme geschoben werden.

Das Netzteil selbst ist innen und außen mit jeweils drei Schrauben fixiert, die mit dem Inbusschlüssel an der Vorderseite gelöst werden können. Anschließend kann das alte Netzteil leicht herausgenommen werden.

Drei Schrauben müssen innen entfernt werden um die Halterung zu lösen.

Drei Schrauben müssen innen entfernt werden um die Halterung zu lösen.

Drei Schrauben müssen auf der Rückseite entfernt werden um das Netzteil vom Gehäuse zu lösen.

Drei Schrauben müssen auf der Rückseite entfernt werden um das Netzteil vom Gehäuse zu lösen.

Einbau des neuen Netzteils

Das PicoPSU besteht im wesentlichen nur aus dem Pfostenstecker und kann einfach auf das Motherboard aufgesteckt werden. Das Y-Kabel wird zwischen den Molex-Anschluss des PicoPSU und den unter dem Gehäuse liegenden schwer zu erreichenden Anschluss der Festplatten geklemmt. Hier ist wieder etwas Fingerspitzengefühl oder der geschickte Einsatz eines Schraubenziehers als fester Gegenpol hilfreich.

Das PicoPSU ist sehr klein und sitzt direkt auf dem ATX-Connector der Hauptplatine.

Das PicoPSU ist sehr klein und sitzt direkt auf dem ATX-Connector der Hauptplatine.

Der Y-Adapter wird zwischen das PicoPSU und den Anschluss unter dem optischen Laufwerk eingefügt.

Der Y-Adapter wird zwischen das PicoPSU und den Anschluss unter dem optischen Laufwerk eingefügt.

Der Berg-Connector des Y-Adapters wird nach oben gezogen und einfach in das entsprechende Gegenstück eingesteckt. Zuletzt muss noch das 4-polige Anschlusskabel des Netzteils nach hinten an die Stelle des alten Netzteils gelegt werden, so dass der Netzadapter angeschlossen werden kann. Es bleibt noch ein Loch hinten in der Rückwand des Servers, der als Staubschutz noch mit einem geeigneten Stück Altmetall verschlossen werden kann. Man denke nur an oben erwähnte Kiste ;)

Der Berg-Anschluss des Y-Kabels wird mit dem oben liegenden Gegenstück verbunden.

Der Berg-Anschluss des Y-Kabels wird mit dem oben liegenden Gegenstück verbunden.

Das vierpolige Anschlusskabel für den Stromadapter wird nach hinten gezogen und durch die Lücke des alten Netzteils nach außen geleitet.

Das vierpolige Anschlusskabel für den Stromadapter wird nach hinten gezogen und durch die Lücke des alten Netzteils nach außen geleitet.

Das war’s schon, und jetzt viel Spaß mit eurem neuen, leisen Server :)

Geschrieben von Kap. Zuletzt geändert am 10. März 2017.

Mehrere Rückgabewerte eines HTTP-Requests

Manchmal ist es notwendig, dass ein Webservice mehrere Ergebnisse nacheinander zurückliefert. Zum Beispiel, wenn ein Request abgesetzt wird, der zunächst validiert werden muss, bevor tatsächlich eine Prozessierung beginnt und die Validierung selbst eine längere Zeit dauert. Dann soll der Server nach Möglichkeit den Request direkt beenden und die Validierung im Hintergrund ablaufen. Andernfalls ist die Webseite nicht responsiv, was den Nutzer verunsichern könnte und keine gute User Experience ist. In einem Spring Boot basierten Webservice kann das zum Beispiel mit asynchroner Ausführung und long polling zusammen mit einem Deferred Result erreicht werden, wie im folgenden beschrieben.

Das Szenario

Im folgenden soll ein kleiner Webservice in Spring Boot implementiert werden, der eine Anfrage auslöst, die zwei Stadien durchläuft. Direkt bei der Anfrage wird in einen Zustand INITIALIZING gewechselt, bis nach einigen Sekunden der Zustand PHASE1 erreicht wird. Den Abschluss markiert der Zustand DONE. Die zugehörige Webseite soll jeweils den aktuellen Status anzeigen und bei einem Statuswechsel automatisch aktualisiert werden.

Eine Beispielimplementation des Webservers habe ich auf GitHub hochgeladen. Es kann nach dem Auschecken mit mvn spring-boot:run gestartet werden, der Webservice ist unter http://localhost:8080 erreichbar.

Beispiel-Service für asynchrone Ausführung und Long-Polling

Beispiel-Service für asynchrone Ausführung und Long-Polling

Bei einem Klick auf die Schaltfläche geht es los.

Die serverseitige Implementierung

Zu Beginn wird ein POST-Request über an /request geschickt. Die Methode soll sofort ein Ergebnis zurückliefern, auch wenn die auszuführende Aktion länger dauert. Dazu wird nicht der tatsächliche Ergebnistyp zurückgeliefert sondern ein DeferredResult. Die Webseite kann daraufhin direkt aktualisiert werden, da die Anfrage schnell bearbeitet wird, das tatsächliche Ergebnis ist im Ergebnis erst vorhanden, sobald setResult aufgerufen wird.

@RequestMapping(name = "/request", method = RequestMethod.POST)
@ResponseBody
public DeferredResult request() {
    // Prepare already for the first state change
    DeferredResult result = service.getStatus();
 
    // Actually let the asynchronous service do something
    service.doSomething();
 
    // Return the deferred result that will be set in the above asynchronous call
    return result;
}

Die Anfrage wurde abgesetzt.

Die Anfrage wurde abgesetzt.

Beim Long Polling kann der Webserver verspätet antworten. Typischerweise wird ein Timeout gesetzt, so dass bei einem Verbindungsabbruch eine neue Anfrage gestartet werden kann. Da unklar ist, wie lange die Antwort tatsächlich dauert, sollte mit dem Verbindungsabbruch gerechnet werden. Die Statusabfrage für das Long Polling wird über den Endpunkt /poll durchgeführt. Das Ergebnis wird vom ausführenden Service geholt und zurückgeliefert. Das zurückgelieferte Object vom Typ DeferredResult wird automatisch so lange zurückgehalten, bis der Service über einen Aufruf von setStatus() ein Ergebnis bereitstellt.

@RequestMapping(name = "/poll", method = RequestMethod.GET)
@ResponseBody
public DeferredResult requestStatus() {
    // Simply forward the request to the service
    return service.getStatus();
}

Asynchrone Ausführung der Anfrage

Damit der ganze Prozess funktioniert, muss die Arbeit des Services (service.doSomething() im Beispiel oben) asynchron ablaufen. Das heißt, nach dem Aufruf von doSomething kann direkt etwas zurückgeliefert werden. Spring Boot ermöglicht durch die Annotation @Async, dass eine Methode eines Services automatisch in einem anderen Thread asynchron ausgeführt wird. Die folgende Methode simuliert, dass der Status zunächst auf INITIALIZING gesetzt wird, anschließend eine längere Zeit gearbeitet wird und zuletzt beendet wird sobald der Status auf DONE steht.

@Async
void doSomething() {
    // Initialize the current status and notify any listener that is already present
    currentStatus = LongRequestStatus.INITIALIZING;
    publishStatus(currentStatus);
 
    // Perform some work until the final state is reached.
    do {
        currentStatus = doNecessaryWork(currentStatus);
        publishStatus(currentStatus);
    } while (currentStatus != LongRequestStatus.DONE);
}

Die Methode wird bei einem POST-Request an /request aufgerufen. Den Status der parallel ausgeführten Methode wird mit publishStatus gesetzt. Falls es in der Zwischenzeit eine eventuell vorhandene Long Polling-Anfrage (mit einem Status als DeferredResult) gab, wird der Status gesetzt.

private synchronized void publishStatus(LongRequestStatus nextToReturn) {
    if (waitingResponse != null) {
        waitingResponse.setResult(nextToReturn.toString());
    }
    waitingResponse = null;
}

Die Anfrage wird gerade Bearbeitet.

Die Anfrage wird gerade Bearbeitet.

Eine Polling-Anfrage ruft ausgehend vom Controller die Methode getStatus auf. Diese erzeugt das DeferredResult waitingResponse, das direkt beantwortet wird, wenn bereits Informationen da sind. Andernfalls wird die Anfrage gespeichert und das Ergebnis gegebenenfalls in obiger Methode publishStatus gesetzt.

public synchronized DeferredResult getStatus() {
    waitingResponse = new DeferredResult<>();
 
    // If the request completed, immediately return the status because it will never change
    if (currentStatus == LongRequestStatus.DONE) {
        waitingResponse.setResult(currentStatus.toString());
    }
 
return waitingResponse;

Die Webseite

Auf der Clientseite wird das Statusupdate mit JavaScript durchgeführt. Bei einem Click auf die Schaltfläche wird zunächst wird die Anfrage ausgeführt und bei Erfolg die Long Polling-Abfrage gestartet.

function request() {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/request", true);
    xhr.onreadystatechange = function() {
        if(xhr.readyState === 4 && xhr.status === 200) {
            // Do something with the response
            console.log(xhr.responseText);
 
            // Start long polling and get the next status
            poll();
        }
    }
    xhr.send();
}

Die Methode poll sendet die Anfrage an den Server um den aktuellen Status abzuholen. Die Anfrage wird solange erneut ausgeführt, bis das Endergebnis DONE zurückgeliefert wird. Als Timeout ist eine Sekunde gesetzt, was relativ kurz ist und dafür sorgt, dass jede Sekunde erneut beim Server nachgefragt wird.

function poll() {
    var xhr = new XMLHttpRequest();
 
    xhr.open("GET", "/poll", true);
 
    xhr.onreadystatechange = function() {
        if(xhr.readyState == 4 && xhr.status == 200) {
            if (xhr.responseText !== "DONE") {
                // Start the next polling request
                setTimeout(poll, 1000);
            } else {
                // Stop polling
            }
        }
    }
    xhr.send();
}

Die Anfrage wurde komplett bearbeitet.

Die Anfrage wurde komplett bearbeitet.

Was fehlt?

Das Grundgerüst für mehrere konsekutive Ergebnisse, die auf eine Anfrage zurückgegeben werden sollen steht damit. Es schließen sich nun einige Möglichkeiten der Erweiterung an.

Da Server normalerweise mehrere Nutzer haben, sollte es natürlich möglich sein, dass mehrere Anfragen parallel ablaufen. Dazu müssen für alle diese Anfragen die Stati gespeichert werden. Der erste Request kann dann ein Token (zum Beispiel eine UUID) zurückgeben, mit der anschließend beim Polling der Status der passenden Anfrage abgefragt werden kann. Gegebenenfalls muss hier natürlich noch mit Authentifizierung gearbeitet werden, so dass jeder Nutzer nur seine eigenen Requests anfragen kann.

Geschrieben von Kap. Zuletzt geändert am 16. Februar 2019.

MySports in Linux

Leider gibt es erstmal keinen Port von MySports auf Linux. Es gibt zwar einen Open-Source-Tool zum Auslesen der Daten, aber zur Benutzung der offiziellen Software bleibt man auf eine Lösung mit VirtualBox (die vermutlich einzige brauchbare Software von Oracle) und Windows angewiesen. Die folgenden Schritte beziehen sich alle auf Ubuntu, sollten jedoch ähnlich auf allen gängigen Distributionen funktionieren.

Um die Uhr über USB anzuschließen und in VirtualBox verfügbar zu machen sind einige Schritte notwendig. Automatisch tut sich da nichts, von wegen universelles Plug & Play und so ;-) Zunächst mal zeigt KDE kein angeschlossenes Gerät beim einstecken an. Es lässt sich aber leicht überprüfen, ob ein gerät eingesteckt ist:

$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 003: ID 1390:7474 TOMTOM B.V. GPS Sport Watch [Runner, Multi-Sport]

Es sieht also schonmal so aus, dass die Uhr vorhanden ist. In VirtualBox kann man in den Eigenschaften einer virtuellen Maschine festlegen, welche der im Host-System vorhandenen Geräte im Gastsystem verfügbar sein sollen. Das Symbol mit dem USB-Stecker und dem grünen + zeigt (eigentlich) die vorhandenen Geräte an. Zunächstmal ist die Liste jedoch leer. Über die Kommandozeile kann man das natürlich auch machen:

$ VBoxManage list usbhost
Host USB Devices:
 
  <no devices>

Diesen Konflikt zwischen den beiden Ausgaben gibt es, weil USB standardmäßig nicht unterstützt wird. Zunächst mal muss das Oracle VM Extension Pack installiert sein, und zwar in der gleichen Version wie VirtualBox. Dies kann man so überprüfen:

$ VBoxManage --version
5.1.2r108956
 
$ VBoxManage list extpacks
Extension Packs: 1
Pack no. 0:   Oracle VM VirtualBox Extension Pack
Version:      5.1.2
Revision:     108956
Edition:      
Description:  USB 2.0 and USB 3.0 Host Controller, Host Webcam, VirtualBox RDP, PXE ROM, Disk Encryption.
VRDE Module:  VBoxVRDP
Usable:       true 
Why unusable:

Die Version (5.1.2) stimmt schonmal überein und USB ist generell verfügbar. Damit die Geräte von VirtualBox erkannt sein, muss der Benutzer jedoch auch Mitglied der Gruppe vboxusers sein:

sudo usermod -a -G vboxusers <useruame>

Damit die Änderungen übernommen werden, muss man sich einmal aus- und wieder einloggen. Nun sind auch die USB-Geräte verfügbar:

$ VBoxManage list usbhost
Host USB Devices:
 
UUID:               05f7f0e1-8d84-4cf7-bd2c-7f11753a02a8
VendorId:           0x1390 (1390)
ProductId:          0x7474 (7474)
Revision:           2.0 (0200)
Port:               1
USB version/speed:  2/Full
Manufacturer:       TomTom
Product:            TomTom GPS Watch
SerialNumber:       HE4045G02754
Address:            sysfs:/sys/devices/pci0000:00/0000:00:1a.7/usb1/1-3/1-3.2//device:/dev/vboxusb/001/004
Current State:      Busy

Bei einem Update der distribution, zum Beispiel von Wily Werewolf auf Xenial Xerus, kann es passieren dass man das Ganze nochmal machen muss oO

Geschrieben von Kap. Zuletzt geändert am 2. August 2016.

Solarisiere dein Leben!

Nein, ich meine keinen neuen digitalen Filter, der die neuesten Instagram-Hipster for Freude jauchzen lässt. Auch nicht die (nicht minder nerdigen, da retro) echte Solarisation, mit der schöne Effekte erzielt werden können.

The Black Sun (1939)

The Black Sun (1939) von Ansel Adams, aus dem Buch Examples: The Making of 40 Photographs. Original in der Sammlung der Yale Art Gallery.

Weitere schöne Beispiele, für den echten Effekt auch in der Serie 1h von Hans-Christian Schink.

Stattdessen geht es um mein aktuelles Lieblings-Farbschema für Programmierung, Solarized von Ethan Schoonover, das vom deutschen KDE-Team passend übersetzt wird ;)

Solarized für Bash

Für die Nutzung in Vim wird pathogen benötigt. Wenn es noch nicht installiert ist, die Schnellanleitung geht wie folgt:

# Verzeichnisse anlegen
mkdir -p ~/.vim/autoload ~/.vim/bundle
# Herunterladen und installieren
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim

Um pathogen nutzen zu können, muss es in der ~/.vimrc aktiviert werden:

" Pathogen
execute pathogen#infect()
syntax on
filetype plugin indent on

Nun können neue vim-Pakete einfach in das Verzeichnis geladen werden. Für Solarized geht das wie folgt:

# Wechseln in das (spätestens soeben erzeugte) Verzeichnis
cd ~/.vim/bundle
# Installieren über git-clone
git clone git://github.com/altercation/vim-colors-solarized.git

Modifikation in der .vimrc:

" Solarized
syntax enable
set background=dark
colorscheme solarized

Solarized für NetBeans
Die benötigten Dateien für NetBeans liegen ebenfalls in einem Git-Repository.

mkdir tmp
cd tmp
curl -sOL https://github.com/fentie/netbeans-colors-solarized/archive/master.zip
unzip master.zip
pushd netbeans-colors-solarized-master/
zip -r ../config-nb.zip config/
popd

Das Farbschema kann nun in NetBeans importiert werden. Dazu muss unter Extras | Optionen die Schaltfläche Import... in der unteren linken Ecke gewählt werden. Nachdem die gerade erstellte Datei config-nb.zip kann importiert werden und NetBeans verlangt von selbst einen Neustart.

Das Profil ist automatisch aktiviert, man kann wechseln unter Extras | Optionen | Schriften und Farben als Profil.

Die heruntergeladenen Dateien und das Verzeichnis kann gelöscht werden.

Netbeans mit Solarized-Farbschema

Netbeans mit Solarized-Farbschema

Geschrieben von Kap. Zuletzt geändert am 10. März 2017.

Ich und mein Computer: Symbolische Links

Es gibt eine ganze Liste von Fehlern, die ein Computer so haben kann. Deichkind beschreiben in ihrem Song Ich und mein Computer einige davon. Die meisten davon waren mit bekannt, und einige wie „Festplatte Voll“ und „Falsche Version, Update nicht verfügbar“ hatte ich natürlich auch schon. Selbstverständlich auch den Refrain „Sanduhr – Sanduhr“.

Auf meinem Linux-Rechner bin ich vor der Sanduhr verschont, dafür gibt es aber ein Problem, an das der Typische Windws-User nicht denkt. (Bei Äpfeln gibts das theoretisch auch, da ja auch ein Unix drunter ist. The More You Know ;) )

The More You Know - Have A Mac? - You Use UNIX!

Nun hatte ich das Problem, dass Wuala auf dem Linux-Rechner ein Verzeichnis nicht synchronisiert. In der Sync-Übersicht (Strg+F9) wurde bei dem Sync-Auftrag zwar ein grünes Häkchen angezeigt, die Größe des Verzeichnisses stand aber bei 0 B, obwohl das Verzeichnis ganz sicher Dateien enthielt. Nach langem Suchen, was wohl die Ursache sein könnte, fand ich heraus dass ~/Data ein symbolischer Link auf ein anderes Verzeichnis war. Wuala scheint symbolische Links nicht zu unterstützen, nach Änderung auf den absoluten Pfad klappte auch das synchronisieren.

:facepalm: ich hatte das gleiche Problem bereits mit Thunderbird, das den Profilordner nicht ordnungsgemäß lädt, wenn er einen symbolischen Link enthält.

In dem Sinne: Programm findet Daten nicht, symbolischer Link benutzt!

Geschrieben von Kap. Zuletzt geändert am 20. November 2013.