Archiv der Kategorie: Nerdig

Eine sammlung aller nerdiger Sachen.

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.

Basis-DOS für virtuelle Maschinen

Wer DOS noch von früher kennt, weiß, wie wichtig es ist, möglichst viel der Verfügbaren 640 KiB zu frei zu haben. Wer DOS nicht von früher kennt: Egal, wie viel RAM ein Computer unter DOS hat, es bleibt stets zusätzlich ein Limit von 640 KiB für die maximale Größe eines Programms. Dieser Wert ist maximal, zusätzliche Treiber oder Systemprogramme benötigen zusätzlichen Speicher.

640K ought to be enough for anybody.

Hat Bill Gates wohl nie gesagt,
er ist damit in guter Gesellschaft von
Albert Einstein und Oscar Wilde ;)

Heutzutage wird DOS hauptsächlich in virtuellen Maschinen betrieben. Der Speichermanager EMM386, der Blöcke im oberen Speicher (UMB) für Treiber verfügbar macht, findet dort nicht mehr automatisch die korrekten Speicherbereiche.

EMM386 unterstützt zwei Modi, EMS und XMS. Wir behandeln hier nur XMS, da EMS nur für wirklich alte Software verwendet wird. Der notwendige Parameter für ist NOEMS. Der Parameter RAM kann benutzt werden, um den Speicherbereich, der für UMBs benutzt werden soll. Ohne weitere Spezifizierung wird der gesamte Bereich von 640 KiB an benutzt. Zusätzlich können über den Parameter I=xxxx-yyyy bestimmte Bereiche angewiesen werden. Dies ist notwendig, um auf modernen Computern mit Virtual Box die UMBs verfügbar zu machen. Folgende Bereiche stehen zur Verfügung:

Bereich Verwendung Benutzbar?
A000-AFFF VGA Videospeicher. Nicht verfügbar.
B000-B7FF Videospeicher für Monocromdisplays (MDA). Sicher, die VM hat ein Farbdisplay.
B800-BFFF Videospeicher für Farbdisplays (CGA). Nicht Verfügbar.
C000-C7FF Video ROM. Nicht benutzbar.
C800-EFFF Verschiedene ROMs. Hard drive Rom von C800 bis CFFF, andere BIOS-Daten (Extended BIOS). Sollte verfügbar sein.
F000-FFFF BIOS ROM. Nicht verfügbar.

Diese Liste gibt uns zwei Zusammenhängende Bereiche von Hauptspeicher, einmal ein kleiner Bereich (32 KiB von B000 bis B7FF) und einmal (160 KiB von C800 bis EFFF); zusammen 192 KiB. Diese beiden Bereiche können beim Laden in den oberen speicher über DEVICEHIGH oder LH mit /L:1 bzw. /L:2 angesprochen werden. Die kleinen Treiber sollten in den vorderen Bereich geladen werden, die großen in den hinteren. Wenn man optimieren möchte, es handelt sich hierbei um eine Instanz des Bin Packings Problems (Achtung! NP-Schwer ;)).

Eine CONFIG.SYS für den Anfang könnte also so aussehen:

Basiskonfiguration CONFIG.SYS

Basiskonfiguration CONFIG.SYS

DEVICE=C:\DOS\HIMEM.SYS /TESTMEM:OFF
DEVICE=C:\DOS\EMM386.EXE RAM NOEMS /I=E000-EFFF /I=B000-B7FF /I=C800-CFFF /I=D000-DFFF
DOS=HIGH,UMB
DEVICEHIGH /L:1=C:\DOS\SETVER.EXE
COUNTRY=049,850,C:\DOS\COUNTRY.SYS
DEVICEHIGH /L:1=C:\DOS\DISPLAY.SYS CON=(EGA,,1)
FILES=30

Die zugehörige Basisversion der AUTOEXEC.BAT ist dann:

Basiskonfiguration AUTOEXEC.BAT

Basiskonfiguration AUTOEXEC.BAT

@ECHO OFF
PROMPT $p$g
LH /L:2 C:\DOS\SMARTDRV.EXE /X
PATH C:\DOS
SET TEMP=C:\DOS
MODE CON CODEPAGE PREPARE=((850) C:\DOS\EGA.CPI)
MODE CON CODEPAGE SELECT=850
LH /L:2 KEYB GR,,C:\DOS\KEYBOARD.SYS
LH /L:2 DOSKEY.COM

Auf dieser Konfiguration aufbauend, kann dann das System weiter aufgesetzt werden. Es gibt noch reichlich freien Speicher im oberen Speicherbereich und ein Großteil der 640 KiB sind verfügbar.

DOS Memory-Übersicht

Verfügbarer Speicher und Treiber im hohen Speicherbereich im Basissystem.

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

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.

Alkohol aus den USA liefern

Amerikaner haben ein Problem mit Drogen. Soweit bekannt. Das gilt jedoch auch für Alkohol, also verschicken sie das Zeug nicht nach Europa (too near to the dragons!?). Es gibt Services, die Pakete weiterleiten. Auch mit Alkohol, wie ich bereits berichtet habe. Hier nun der Follow Up, wie man mit der Weiterleitung nun an den guten Alk kommt.

Shopsuche

Nun sollte der leichtere Teil kommen. Einfach einen Vodka 360 Double Donut bestellen, bisschen warten und genießen.

Ich so: „Yeah, eine Adresse in den USA, dann bestelle ich da gleich mal ne Flasche Vodka hin“. Die Wirklichkeit: „Yeah, schade. Wir liefern gar nicht nach Florida! Und wir wissen, dass an der Adresse ein Weiterleitungsservice sitzt. Du bist bestimmt so ein Dreckseuropäer und wir wollen unseren Booze für uns behalten! Har, har har!

Also, suchen bis ein Shop auftaucht, der dahin liefert. Am einfachsten ist ein Shop, der in Florida liegt. Die verschicken auf jeden Fall nach Florida. Beim Suchen muss man aber aufpassen, viele Shops haben zwar ne Webseite, aber man muss das Zeug selbst abholen. Und die wollen den Kapitalismus perfektioniert haben :O ? Dafür gibts auch nette Services wie eine Übersicht zahlloser Alkoholshops, die man nur der Reihe durchgehen muss. Letztendlich hab ich ihn bei ABC Fine Wine & Spirits gefunden und bestellt. Der Laden ist so zuvorkommend, dass er einen Link verdient.

Bezahlen

Dass die Sache mit den Kreditkarten in deren Herkunftsland gar nicht so einfach ist, habe ich bei der Anmeldung bei MyUS.com schon festgestellt. Bei ABC Fine Wine & Spirits wird ein bisher unerreichtes Level erreicht.

Selbst die Informationen der Issuing Bank und Issuing Bank Phone Number haben nicht gereicht, man braucht ein weiteres Qualitätsmerkmal, AVS. Dabei wird überprüft, ob die Adresse der Kreditkarte der Rechnungsadresse bei der Bestellung übereinstimmt. Was natürlich naheliegenderweise Probleme mit dem ß erzeugt. Ein tatsächlich viel größeres Problem ist, dass in Deutschland ausgegebene Kreditkarten normalerweise keine Adressinformationen enthalten :facepalm:

Aber auch für dieses Problem gibt es eine Lösung, die Internet sei Dank, leicht umzusetzen ist. Die in Malta ansässige Firma EntroPay bietet virtuelle Kreditkarten an, die zwar kein AVS unterstützen, aber trotzdem nutzbar sind. Man muss nur als Rechnungsadresse die Adresse der Bank angeben. Die virtuelle Kreditkarte muss mit einem Mindestbetrag aufgeladen werden, das heißt die Differenz ist dann im Zweifel weg ;)

Liefern

So, Bestellung abgeschickt, nun heißt es erstmal befriedigt zurücklehnen und warten. Nach ein paar Tagen kommt eine Benachrichtigung von MyUS, dass eine Lieferung angekommen ist und auf „Compliance“ geprüft wird. Man kann da z. B. zur Unterstützung den Link zum Shop schicken, dann geht das angeblich schneller. Nach der überstandenen Überprüfung darf man sich einloggen, eine Zieladresse angeben und nach der Bezahlung gehts direkt los. Nun dauert es etwas länger, muss ja erst der Atlantik überquert werden.

Abschluss

Nach ein paar Tagen kommt das Paket mit dem Vodka an die eigene Haustür und es heißt genießen. Und noch viel später kommt die Rechnung vom Zoll.

Und ein kleines Fazit: Der Aufwand lohnt sich wirklich nur, wenn man etwas unbedingt haben möchte. Dafür ist nachdem die Accounts einmal angelegt sind, beim zweiten mal alles etwas leichter. Und zumindestens beim Alkohol muss man zusätzliche Kosten tragen. Ein zusätzlicher Versand, in meinem Fall etwa 10€ und den Zoll bzw. die Alkoholsteuer. Insgesamt habe ich also für eine 13$-Flasche zirka 30€ bezahlt. Wobei, je teurer der Artikel, desto weniger fällt es auf.

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

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.

Von Videokodierung und Bitraten

Ich bin nun in den „Genuss“ gekommen einige Filme zu kompriemieren und als DVD zu brennen. Bei einigen der Filme gab es verschiedene Probleme…

Typische Programme wie DVD-Flick oder DeVeDe machen das mit wenigen klicks. DeVeDe nutzt jedoch den verfügbaren Platz weniger als zur Hälfte aus und die Version von ffmpeg DVD-Flick kam mit dem (neuen) Inputformat nicht klar.

So habe ich habe kurzerhand beschlossen, selbst die passende Ziel-Bitrate zu berechnen. Eigentlich eine einfache Aufgabe, die Größe einer DVD ist fest, es sollte eine Tonspur enthalten sein und die Filmdaten nach möglichkeit den ganzen Rest ausfüllen.

Überraschenderweise war jedoch die endgültige Version größer als die DVD und auch größer als die Video- und die Audiodaten zusammen. Das konnte ich natürlich leicht durch ein erneutes Kodieren der Videodaten korrigieren. Den Grund für den zusätzlichen Speicherverbrauch konnte ich jedoch nicht rausfinden.

Ein Blick in den Sourcecode von DVD-Flick 2 (ein Fork des nicht weiter verfolgten Ursprungsprojektes) zeigt jedoch, dass willkürlich 4% zusätzlicher Platz für das Muxen eingeplant wird. Ist zwar in der aktuellen Version auskommentiert, funktioniert aber in der Praxis gut. Damit kommen wohl auch die sehr genauen Videogrößen von DVD-Flick zustande, mit denen etwa 20-30 MiB auf dem Medium freibleiben.

  ' Room for muxing overhead is 4% (which is a lot for a 4.3 Gb DVD)
  discSize = 0.96 * discSize

Die Zielbitrate (inklusive wählbarem Mux-Overhead) kann mit folgendem Skript leicht bestimmt werden:

Bei Unterschieden (wie DVD+R und DVD-R) wird die kleine Größe gewählt.
Angenommen, codierung von 5.1 Sound als AAC mit 448 kbit/s
Zusätzlich eingeplanter Platz für das Muxen.
Falls die Abspielgeschwindigkeit von Quell- und Zielmedium unterschiedlich ist, z. B. 23,976 bei BluRay-Quellen und 25 bei PAL DVDs
Aktualisiert die unten angegebenen Werte basierend auf den Eingaben.

Berechnet unter der Anname von Tonspuren kodiert als AC3 mit 448 Kb/s:

Benötigter Platz für Tonspuren: 555,23 MiB
Verfügbarer Platz für Video: 3748,09 MiB
Maximale Bitrate für Video: 5906,72 Kb/s

Unberücksichtigt bleibt, dass für DVDs die maximale Bitrate 9000 ist.

Geschrieben von Kap. Zuletzt geändert am 19. November 2016.